Sylloge
A C# helper library
code/Audio/Metadata/ID3/V2.cs
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Enumerations Properties Events