![]() |
Sylloge
A C# helper library
|
00001 using System; 00002 using System.Collections.Generic; 00003 using System.Linq; 00004 using System.Text; 00005 00006 namespace Sylloge.Audio.Metadata.ID3 00007 { 00012 public class V2 00013 { 00014 #region Classes 00015 00021 public class BitFlags 00022 { 00028 public bool IsUnsynchronised { get; set; } 00034 public bool IsExtendedHeader { get; set; } 00039 public bool IsExperimentalIndicator { get; set; } 00044 public bool IsFooterPresent { get; set; } 00045 00050 public BitFlags(byte flag) 00051 { 00052 this.SetValues(flag); 00053 } 00054 00055 public void Clear() 00056 { 00057 this.IsUnsynchronised = false; 00058 this.IsExtendedHeader = false; 00059 this.IsExperimentalIndicator = false; 00060 this.IsFooterPresent = false; 00061 } 00062 00063 public void Set(byte flag) 00064 { 00065 this.SetValues(flag); 00066 } 00067 00068 public byte ToByte() 00069 { 00070 // 6543 00071 // %0abcd000 00072 byte r = (byte)0; 00073 if (this.IsUnsynchronised) { r = Sylloge.Memory.SetBit(r, 6); } 00074 if (this.IsExtendedHeader) { r = Sylloge.Memory.SetBit(r, 5); } 00075 if (this.IsExperimentalIndicator) { r = Sylloge.Memory.SetBit(r, 4); } 00076 if (this.IsFooterPresent) { r = Sylloge.Memory.SetBit(r, 3); } 00077 return r; 00078 } 00079 00080 private void SetValues(byte flag) 00081 { 00083 this.IsUnsynchronised = Sylloge.Memory.IsBitSet(flag, 7); // a 00084 this.IsExtendedHeader = Sylloge.Memory.IsBitSet(flag, 6); // b 00085 this.IsExperimentalIndicator = Sylloge.Memory.IsBitSet(flag, 5); // c 00086 this.IsFooterPresent = Sylloge.Memory.IsBitSet(flag, 4); // d 00087 } 00088 } 00089 00099 public class ExtendedHeader 00100 { 00101 public int FlagCount { get; private set; } 00102 public int Size { get; private set; } 00103 private byte[] m_flags = null; 00104 00116 public ExtendedHeader(byte[] exhdr) 00117 { 00118 if (exhdr == null) { throw new System.ArgumentNullException("exhdr"); } 00119 this.Size = ID3.Helper.FromSynchsafeInt(BitConverter.ToInt32(Sylloge.Memory.SwapByteOrder(exhdr, 0, 4), 0)); 00120 this.FlagCount = (int)exhdr[4]; 00121 int len = this.Size - 5; 00122 this.m_flags = new byte[len]; 00123 System.Buffer.BlockCopy(exhdr, 5, this.m_flags, 0, len); 00124 } 00125 00126 public byte[] ToBytes() 00127 { 00128 byte[] buf = new byte[this.Size]; 00129 byte[] sz = Sylloge.Memory.SwapByteOrder(BitConverter.GetBytes(ID3.Helper.ToSynchsafeInt(this.Size))); 00130 System.Buffer.BlockCopy(sz, 0, buf, 0, 4); 00131 buf[4] = (byte)this.FlagCount; 00132 System.Buffer.BlockCopy(this.m_flags, 0, buf, 5, this.m_flags.Length); 00133 return buf; 00134 } 00135 } 00136 00152 public class TagFooter 00153 { 00154 public const string ID = "3DI"; 00155 public int VersionMajor { get; private set; } 00156 public int VersionMinor { get; private set; } 00157 public BitFlags Flags { get; private set; } 00158 public int Size { get; private set; } 00159 00160 public TagFooter(byte[] ftr) 00161 { 00162 if (ftr == null) { throw new System.ArgumentNullException("ftr"); } 00163 if (ftr.Length != 10) { throw new System.ArgumentOutOfRangeException("ftr", "Data length must be 10 bytes"); } 00164 if (ID3.Helper.ISO.GetString(ftr, 0, 3) == TagFooter.ID) { // Its a valid tag, so continue 00165 // ID3v2 version $04 00 (byte 3-4, len=2) 00166 this.VersionMajor = (int)ftr[3]; 00167 this.VersionMinor = (int)ftr[4]; 00168 if (this.VersionMajor <= 4 && this.VersionMinor < 255) { 00169 // bit:76543210 00170 // ID3v2 flags %abcd0000 00171 this.Flags = new BitFlags(ftr[5]); 00172 /* The ID3v2 tag size is stored as a 32 bit synchsafe integer (section 00173 6.2), making a total of 28 effective bits (representing up to 256MB). 00174 00175 The ID3v2 tag size is the sum of the byte length of the extended 00176 header, the padding and the frames after unsynchronisation. If a 00177 footer is present this equals to ('total size' - 20) bytes, otherwise 00178 ('total size' - 10) bytes. */ 00179 this.Size = ID3.Helper.FromSynchsafeInt(BitConverter.ToInt32(Sylloge.Memory.SwapByteOrder(ftr, 6, 4), 0)); 00180 this.Size -= (this.Flags.IsFooterPresent ? 20 : 10); 00181 // this.Size should always be -20 in this instance since we are in the footer 00182 } 00183 } 00184 } 00185 00186 public byte[] ToBytes() 00187 { 00188 byte[] buf = new byte[10]; 00189 System.Buffer.BlockCopy(ID3.Helper.ISO.GetBytes(TagFooter.ID), 0, buf, 0, 3); 00190 buf[3] = (byte)this.VersionMajor; 00191 buf[4] = (byte)this.VersionMinor; 00192 buf[5] = this.Flags.ToByte(); 00193 byte[] sz = BitConverter.GetBytes(ID3.Helper.ToSynchsafeInt(this.Size)); 00194 System.Buffer.BlockCopy(sz, 0, buf, 6, 4); 00195 return buf; 00196 00197 } 00198 } 00199 00200 #endregion 00201 00202 #region Local Attributes 00203 00204 #region Header/Footer Info 00205 00209 public BitFlags Flags { get; protected set; } 00214 public ExtendedHeader Extended { get; protected set; } 00218 public TagFooter Footer { get; protected set; } 00219 00220 #endregion 00221 00222 #region Frames 00223 00227 public ID3.Frames.TextFrame Artist { get; protected set; } 00231 public ID3.Frames.TextFrame Album { get; protected set; } 00235 public ID3.Frames.TextFrame Genre { get; protected set; } 00239 public ID3.Frames.TextFrame Title { get; protected set; } 00243 public ID3.Frames.TextFrame Year { get; protected set; } 00247 public ID3.Frames.TextFrame AlbumArtist { get; protected set; } 00251 public ID3.Frames.TextFrame Composer { get; protected set; } 00255 public ID3.Frames.TextFrame Copyright { get; protected set; } 00259 public ID3.Frames.TextFrame DiscNumber { get; protected set; } 00263 public ID3.Frames.TextFrame EncodedBy { get; protected set; } 00267 public ID3.Frames.TextFrame OriginalArtist { get; protected set; } 00271 public ID3.Frames.TextFrame Publisher { get; protected set; } 00275 public ID3.Frames.TextFrame TrackNumber { get; protected set; } 00279 public List<ID3.Frames.AttachedPicture> Images { get; protected set; } 00283 public List<ID3.Frames.Comment> Comments { get; protected set; } 00287 public List<ID3.Frames.UserDefinedString> UserDefinedStrings { get; protected set; } 00291 public List<ID3.Frames.UserDefinedURL> UserDefinedUrls { get; protected set; } 00292 00299 private List<ID3.Frame> m_unsupported; 00300 00305 public ID3.Frame[] OtherFrames 00306 { 00307 get 00308 { 00309 return (this.m_unsupported != null ? this.m_unsupported.ToArray() : null); 00310 } 00311 } 00312 00313 #endregion 00314 00315 // these can be multiple values, but are here for simplicities sake 00316 00320 public string Comment 00321 { 00322 get 00323 { 00324 if (this.Comments != null && this.Comments.Count > 0) { return this.Comments[0].Value; } 00325 return string.Empty; 00326 } 00327 } 00331 public ID3.Frames.AttachedPicture Image 00332 { 00333 get { return ((this.Images != null && this.Images.Count > 0) ? this.Images[0] : null); } 00334 } 00338 public string URL 00339 { 00340 get { return ((this.UserDefinedUrls != null && this.UserDefinedUrls.Count > 0) ? this.UserDefinedUrls[0].Value : null); } 00341 } 00345 public string UserText 00346 { 00347 get { return ((this.UserDefinedStrings != null && this.UserDefinedStrings.Count > 0) ? this.UserDefinedStrings[0].Value : null); } 00348 } 00349 00353 public byte[] Data { get; protected set; } 00357 public string File { get; protected set; } 00361 public bool IsValid { get; protected set; } 00365 public System.Version Version { get; protected set; } 00369 public int HeaderLength { get; protected set; } 00370 00371 #endregion 00372 00373 #region Methods 00374 00378 public V2() 00379 { 00380 this.Version = new Version(2, 4, 0); 00381 this.Clear(); 00382 } 00383 00388 public void AddFrame(Sylloge.Audio.Metadata.ID3.Frame frame) 00389 { 00390 // DEV_NOTE: For more information on the tags, visit http://www.id3.org 00391 switch (frame.ID) { 00392 case ID3.Frame.Keys.AttachedPicture: // can be multiple 00393 this.AddImage(Frames.AttachedPicture.Parse(frame)); 00394 break; 00395 case ID3.Frame.Keys.Comment: // can be multiple 00396 this.AddComment(Frames.Comment.Parse(frame)); 00397 break; 00398 case ID3.Frame.Keys.UserDefinedString: // can be multiple 00399 this.AddUserText(Frames.UserDefinedString.Parse(frame)); 00400 break; 00401 case ID3.Frame.Keys.UserDefinedURL: // can be multiple 00402 this.AddURL(Frames.UserDefinedURL.Parse(frame)); 00403 break; 00404 case ID3.Frame.Keys.Album: 00405 this.Album = new Frames.TextFrame(frame); 00406 break; 00407 case ID3.Frame.Keys.Composer: 00408 this.Composer = new Frames.TextFrame(frame); 00409 break; 00410 case ID3.Frame.Keys.Genre: 00411 this.Genre = new Frames.TextFrame(frame); 00412 break; 00413 case ID3.Frame.Keys.CopyrightInfo: 00414 this.Copyright = new Frames.TextFrame(frame); 00415 break; 00416 case ID3.Frame.Keys.EncodedBy: 00417 this.EncodedBy = new Frames.TextFrame(frame); 00418 break; 00419 case ID3.Frame.Keys.TitleOfSong: 00420 this.Title = new Frames.TextFrame(frame); 00421 break; 00422 case ID3.Frame.Keys.OriginalArtist: 00423 this.OriginalArtist = new Frames.TextFrame(frame); 00424 break; 00425 case ID3.Frame.Keys.Artist: 00426 this.Artist = new Frames.TextFrame(frame); 00427 break; 00428 case ID3.Frame.Keys.AlbumArtist: 00429 this.AlbumArtist = new Frames.TextFrame(frame); 00430 break; 00431 case ID3.Frame.Keys.DiscNumber: 00432 this.DiscNumber = new Frames.TextFrame(frame); 00433 break; 00434 case ID3.Frame.Keys.Publisher: 00435 this.Publisher = new Frames.TextFrame(frame); 00436 break; 00437 case ID3.Frame.Keys.Track: 00438 this.TrackNumber = new Frames.TextFrame(frame); 00439 break; 00440 case ID3.Frame.Keys.Year: 00441 this.Year = new Frames.TextFrame(frame); 00442 break; 00443 default: // add tags as needed to incorperate 00444 this.m_unsupported.Add(frame); 00445 break; 00446 } 00447 } 00448 00453 private void AddComment(ID3.Frames.Comment comment) 00454 { 00455 if (comment == null) { throw new System.ArgumentNullException("comment"); } 00456 this.Comments.Add(comment); 00457 } 00458 00463 private void AddComment(string comment) 00464 { 00465 if (comment == null) { throw new System.ArgumentNullException("comment"); } 00466 this.AddComment(new ID3.Frames.Comment(comment)); 00467 } 00468 00473 private void AddImage(ID3.Frames.AttachedPicture img) 00474 { 00475 if (img == null) { throw new System.ArgumentNullException("img"); } 00476 this.Images.Add(img); 00477 } 00478 00483 private void AddImage(System.Drawing.Image img) 00484 { 00485 if (img == null) { throw new System.ArgumentNullException("img"); } 00486 this.Images.Add(new ID3.Frames.AttachedPicture(img)); 00487 } 00488 00493 private void AddURL(ID3.Frames.UserDefinedURL url) 00494 { 00495 if (url == null) { throw new System.ArgumentNullException("url"); } 00496 this.UserDefinedUrls.Add(url); 00497 } 00498 00503 private void AddURL(string url) 00504 { 00505 if (url == null) { throw new System.ArgumentNullException("url"); } 00506 this.UserDefinedUrls.Add(new ID3.Frames.UserDefinedURL(url)); 00507 } 00508 00513 private void AddUserText(ID3.Frames.UserDefinedString txt) 00514 { 00515 if (txt == null) { throw new System.ArgumentNullException("txt"); } 00516 this.UserDefinedStrings.Add(txt); 00517 } 00518 00523 private void AddUserText(string txt) 00524 { 00525 if (txt == null) { throw new System.ArgumentNullException("txt"); } 00526 this.UserDefinedStrings.Add(new ID3.Frames.UserDefinedString(txt)); 00527 } 00528 00532 public void Clear() 00533 { 00534 this.IsValid = false; 00535 if (this.Flags != null) { this.Flags.Clear(); } 00536 this.Flags = new BitFlags((byte)0); 00537 this.Extended = null; 00538 this.Footer = null; 00539 if (this.Images != null) { this.Images.Clear(); this.Images.TrimExcess(); } 00540 if (this.Comments != null) { this.Comments.Clear(); this.Comments.TrimExcess(); } 00541 if (this.UserDefinedStrings != null) { this.UserDefinedStrings.Clear(); this.UserDefinedStrings.TrimExcess(); } 00542 if (this.UserDefinedUrls != null) { this.UserDefinedUrls.Clear(); this.UserDefinedUrls.TrimExcess(); } 00543 if (this.m_unsupported != null) { this.m_unsupported.Clear(); this.m_unsupported.TrimExcess(); } 00544 this.Images = new List<ID3.Frames.AttachedPicture>(); 00545 this.Comments = new List<ID3.Frames.Comment>(); 00546 this.UserDefinedStrings = new List<ID3.Frames.UserDefinedString>(); 00547 this.UserDefinedUrls = new List<ID3.Frames.UserDefinedURL>(); 00548 this.Title = new Frames.TextFrame(Frame.Keys.TitleOfSong); 00549 this.Artist = new Frames.TextFrame(Frame.Keys.Artist); 00550 this.Album = new Frames.TextFrame(Frame.Keys.Album); 00551 this.Year = new Frames.TextFrame(Frame.Keys.Year); 00552 this.Genre = new Frames.TextFrame(Frame.Keys.Genre); 00553 this.Composer = new Frames.TextFrame(Frame.Keys.Composer); 00554 this.Copyright = new Frames.TextFrame(Frame.Keys.CopyrightInfo); 00555 this.EncodedBy = new Frames.TextFrame(Frame.Keys.EncodedBy); 00556 this.OriginalArtist = new Frames.TextFrame(Frame.Keys.OriginalArtist); 00557 this.AlbumArtist = new Frames.TextFrame(Frame.Keys.AlbumArtist); 00558 this.DiscNumber = new Frames.TextFrame(Frame.Keys.DiscNumber); 00559 this.Publisher = new Frames.TextFrame(Frame.Keys.Publisher); 00560 this.TrackNumber = new Frames.TextFrame(Frame.Keys.Track); 00561 this.m_unsupported = new List<ID3.Frame>(); 00562 } 00563 00568 public int CalculateHeaderSize() 00569 { 00570 byte[] d = this.GenerateAllTagBytes(); 00571 return d.Length; 00572 } 00573 00574 public byte[] CalculateDataBytes() 00575 { 00576 int hlen = 10; 00577 if (this.Flags.IsFooterPresent) { hlen = 20; } 00578 byte[] data = this.GenerateAllTagBytes(); 00579 byte[] hdr = new byte[(hlen + data.Length)]; 00580 byte[] sz = Sylloge.Memory.SwapByteOrder(BitConverter.GetBytes(ID3.Helper.ToSynchsafeInt(data.Length)), 0, 4); 00587 System.Buffer.BlockCopy(ID3.Helper.ISO.GetBytes("ID3"), 0, hdr, 0, 3); 00588 hdr[3] = Convert.ToByte(this.Version.Minor); 00589 hdr[4] = Convert.ToByte(this.Version.Build); 00590 hdr[5] = this.Flags.ToByte(); 00591 // copy the size in the header (We don't count the 10 byte header for the size of the tag in bytes) 00592 System.Buffer.BlockCopy(sz, 0, hdr, 6, 4); 00593 // copy the data 00594 System.Buffer.BlockCopy(data, 0, hdr, 10, data.Length); 00595 return hdr; 00596 } 00597 00602 public bool Save() 00603 { 00604 // DEV_NOTE: Be sure that if you added any extra processing in the 00605 // other functions to handle other tag types, if you wish to save 00606 // that data you need to also add the handling here and in GenerateTag(ID) 00607 try { 00608 byte[] hdr = this.CalculateDataBytes(); 00609 // write it the file 00610 System.IO.FileStream fin = new System.IO.FileStream(this.File, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite); 00611 int hlen = this.HeaderLength + (this.Flags.IsFooterPresent ? 20 : 10); 00612 byte[] mp3 = new byte[(fin.Length - hlen)]; 00613 byte[] fdata = new byte[(hdr.Length + mp3.Length)]; 00614 // read the file data (sans ID3v2 tag) 00615 fin.Seek(hlen, System.IO.SeekOrigin.Begin); 00616 fin.Read(mp3, 0, mp3.Length); 00617 // copy ID3v2 tag header w/ 10 byte header into the file data 00618 System.Buffer.BlockCopy(hdr, 0, fdata, 0, hdr.Length); 00619 // copy MP3 data into file data 00620 System.Buffer.BlockCopy(mp3, 0, fdata, hdr.Length, mp3.Length); 00621 fin.Seek(0, System.IO.SeekOrigin.Begin); 00622 fin.SetLength(0); fin.Flush(); 00623 fin.Write(fdata, 0, fdata.Length); 00624 fin.Flush(); fin.Close(); 00625 this.HeaderLength = hdr.Length - 10; // -10 for the tag header 00626 if (this.Flags.IsFooterPresent) { this.HeaderLength -= 10; } 00627 return true; 00628 } catch (Exception ex) { 00629 throw ex; 00630 } 00631 } 00632 00637 public void SetFlags(BitFlags flags) 00638 { 00639 this.Flags = flags; 00640 } 00641 00645 public void SetFile(string file) 00646 { 00647 this.File = file; 00648 } 00649 00653 public void SetYear(DateTime d) 00654 { 00655 this.Year.SetValue(d.ToString("yyyy-MM-ddTHH:mm:ss")); 00656 } 00657 00661 public void SetVersion(System.Version ver) 00662 { 00663 this.Version = ver; 00664 } 00665 00669 public void SetVersion(string ver) 00670 { 00671 this.Version = System.Version.Parse(ver); 00672 } 00673 00678 public override string ToString() 00679 { 00680 return this.Artist.Value + " - " + this.Title.Value; 00681 } 00682 00683 #endregion 00684 00685 #region Private Methods 00686 00691 private byte[] GenerateAllTagBytes() 00692 { 00693 List<byte> data = new List<byte>(); 00694 data.AddRange(this.Artist.GenerateTag()); 00695 data.AddRange(this.Title.GenerateTag()); 00696 data.AddRange(this.Album.GenerateTag()); 00697 data.AddRange(this.DiscNumber.GenerateTag()); 00698 data.AddRange(this.TrackNumber.GenerateTag()); 00699 data.AddRange(this.Year.GenerateTag()); 00700 data.AddRange(this.Genre.GenerateTag()); 00701 data.AddRange(this.GenerateTagBytesFromAllComments()); 00702 data.AddRange(this.Copyright.GenerateTag()); 00703 data.AddRange(this.Composer.GenerateTag()); 00704 data.AddRange(this.OriginalArtist.GenerateTag()); 00705 data.AddRange(this.AlbumArtist.GenerateTag()); 00706 data.AddRange(this.EncodedBy.GenerateTag()); 00707 data.AddRange(this.Publisher.GenerateTag()); 00708 data.AddRange(this.GenerateTagBytesFromAllUDT()); 00709 data.AddRange(this.GenerateTagBytesFromAllUDURL()); 00710 data.AddRange(this.GenerateTagBytesFromUnsupported()); 00711 // DEV_NOTE: Be sure to copy any extra processed data here. Its good to try and keep imgs last in the tag 00712 data.AddRange(this.GenerateTagBytesFromAllImages()); 00713 return data.ToArray(); 00714 } 00715 00720 private byte[] GenerateTagBytesFromAllImages() 00721 { 00722 List<byte> all = new List<byte>(); 00723 foreach (ID3.Frames.AttachedPicture i in this.Images) { 00724 all.AddRange(i.GenerateTag()); 00725 } 00726 return all.ToArray(); 00727 } 00728 00733 private byte[] GenerateTagBytesFromAllComments() 00734 { 00735 List<byte> all = new List<byte>(); 00736 foreach (ID3.Frames.Comment c in this.Comments) { 00737 all.AddRange(c.GenerateTag()); 00738 } 00739 return all.ToArray(); 00740 } 00741 00746 private byte[] GenerateTagBytesFromAllUDT() 00747 { 00748 List<byte> all = new List<byte>(); 00749 foreach (ID3.Frames.UserDefinedString udt in this.UserDefinedStrings) { 00750 all.AddRange(udt.GenerateTag()); 00751 } 00752 return all.ToArray(); 00753 } 00754 00759 private byte[] GenerateTagBytesFromAllUDURL() 00760 { 00761 List<byte> all = new List<byte>(); 00762 foreach (ID3.Frames.UserDefinedURL url in this.UserDefinedUrls) { 00763 all.AddRange(url.GenerateTag()); 00764 } 00765 return all.ToArray(); 00766 } 00767 00772 private byte[] GenerateTagBytesFromUnsupported() 00773 { 00774 List<byte> all = new List<byte>(); 00775 foreach (ID3.Frame f in this.m_unsupported) { 00776 all.AddRange(f.GenerateTag()); 00777 } 00778 return all.ToArray(); 00779 } 00780 00781 #endregion 00782 00783 #region Static Methods 00784 00791 public static Frame[] ReadHeaderInfo(byte[] data, bool unsynchronisation) 00792 { 00793 if (data == null) { throw new System.ArgumentNullException("data"); } 00794 Frame tag = null; 00795 List<Frame> frames = new List<Frame>(); 00796 List<byte> dbuf = new List<byte>(data); 00797 byte[] buf = dbuf.ToArray(); 00798 while (buf != null && buf.Length > 0) { 00799 tag = ID3.Frame.Parse(buf, unsynchronisation); 00800 if (tag != null) { 00801 dbuf.RemoveRange(0, tag.FullFrameLength); 00802 frames.Add(tag); 00803 } else { 00804 if (buf.Length < 4) { break; } 00805 bool is_pad = true; 00806 for (int i = 0; i < 4; i++) { 00807 if (buf[i] > 0) { 00808 is_pad = false; 00809 break; 00810 } 00811 } 00812 if (is_pad) { break; } 00813 dbuf.RemoveRange(0, 4); 00814 } 00815 buf = dbuf.ToArray(); 00816 } 00817 return frames.ToArray(); 00818 } 00819 00820 public static V2 Parse(string file) 00821 { 00822 if (System.IO.File.Exists(file)) { 00823 V2 ret = new V2(); 00824 ret.File = file; 00825 System.IO.FileStream fin = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite); 00826 byte[] hdr_data = new byte[10]; 00827 byte[] buf = null; 00828 fin.Read(hdr_data, 0, 10); 00829 // ID3v2/file identifier "ID3" (byte 0-2, len=3) 00830 if (ID3.Helper.ISO.GetString(hdr_data, 0, 3) == "ID3") { // Its a valid tag, so continue 00831 // ID3v2 version $04 00 (byte 3-4, len=2) 00832 // The first byte of ID3v2 version is its major version, while the second byte is its revision number 00833 // DEV_NOTE, since we're using the .NET Version class, "major" == Version.Minor and "revision" == Version.Build 00834 ret.Version = new Version(2, Convert.ToInt32(hdr_data[3]), Convert.ToInt32(hdr_data[4])); 00835 /* DEV_NOTE: from the ID3 spec - 00836 If software with ID3v2.4.0 and below support should encounter version five or 00837 higher it should simply ignore the whole tag. Version or revision will never be $FF. 00838 00839 the current version supported by Sylloge is 2.4.0, so we shall comply */ 00840 if (ret.Version.Minor <= 4 && ret.Version.Build < 255) { 00841 // bit:76543210 00842 // ID3v2 flags %abcd0000 00843 ret.Flags.Set(hdr_data[5]); 00844 /* The ID3v2 tag size is stored as a 32 bit synchsafe integer (section 00845 6.2), making a total of 28 effective bits (representing up to 256MB). 00846 00847 The ID3v2 tag size is the sum of the byte length of the extended 00848 header, the padding and the frames after unsynchronisation. If a 00849 footer is present this equals to ('total size' - 20) bytes, otherwise 00850 ('total size' - 10) bytes. */ 00851 int tagsz = ID3.Helper.FromSynchsafeInt(BitConverter.ToInt32(Sylloge.Memory.SwapByteOrder(hdr_data, 6, 4), 0)); 00852 tagsz -= (ret.Flags.IsFooterPresent ? 20 : 10); 00853 ret.HeaderLength = tagsz; 00854 if (tagsz > 0) { 00855 if (ret.Flags.IsExtendedHeader) { 00856 byte[] exhdr = new byte[4]; 00857 fin.Read(exhdr, 0, 4); 00858 int exsz = ID3.Helper.FromSynchsafeInt(BitConverter.ToInt32(Sylloge.Memory.SwapByteOrder(exhdr, 0, 4), 0)); 00859 exhdr = new byte[exsz]; 00860 fin.Position -= 4; // go back 4 bytes in the stream to re-read the entire header 00861 fin.Read(exhdr, 0, exsz); 00862 ret.Extended = new ExtendedHeader(exhdr); 00863 } 00864 buf = new byte[tagsz]; 00865 fin.Read(buf, 0, tagsz); 00866 if (ret.Flags.IsFooterPresent) { 00867 byte[] ftr = new byte[10]; 00868 fin.Read(ftr, 0, 10); 00869 ret.Footer = new TagFooter(ftr); 00870 } 00871 ret.IsValid = true; 00872 } 00873 } 00874 } 00875 fin.Close(); 00876 if (ret.IsValid) { 00877 List<Frame> frames = new List<Frame>(V2.ReadHeaderInfo(buf, ret.Flags.IsUnsynchronised)); 00878 foreach (Frame f in frames) { 00879 ret.AddFrame(f); 00880 } 00881 } 00882 return ret; 00883 } 00884 return null; 00885 } 00886 00887 public static bool TryParse(string file, out V2 v2) 00888 { 00889 try { 00890 v2 = V2.Parse(file); 00891 return true; 00892 } 00893 catch (Exception) { v2 = null; } 00894 return false; 00895 } 00896 00897 #endregion 00898 } 00899 }