package v2 import ( "bytes" "fmt" "io" "github.com/Unbewohnte/id3ed/util" ) type HeaderFlags struct { Unsynchronisated bool HasExtendedHeader bool Experimental bool FooterPresent bool } // ID3v2.x header structure type Header struct { Identifier string Flags HeaderFlags Version string Size int64 // size of the whole tag - 10 header bytes } // Reads and structuralises ID3v2.3.0 or ID3v2.4.0 header. // Returns a blank header struct if encountered an error func GetHeader(rs io.ReadSeeker) (Header, error) { var header Header rs.Seek(0, io.SeekStart) identifier, err := util.Read(rs, 3) if err != nil { return Header{}, err } // check if ID3v2 is used if !bytes.Equal([]byte(HEADERIDENTIFIER), identifier) { return Header{}, fmt.Errorf("no ID3v2 identifier found") } header.Identifier = string(identifier) // version VersionBytes, err := util.Read(rs, 2) if err != nil { return Header{}, err } majorVersion, err := util.ByteToInt(VersionBytes[0]) if err != nil { return Header{}, err } revisionNumber, err := util.ByteToInt(VersionBytes[1]) if err != nil { return Header{}, err } var version string switch majorVersion { case 2: version = V2_2 case 3: version = V2_3 case 4: version = V2_4 default: return Header{}, fmt.Errorf("ID3v2.%d.%d is not supported", majorVersion, revisionNumber) } header.Version = version // flags flags, err := util.Read(rs, 1) if err != nil { return Header{}, err } flagBits := fmt.Sprintf("%08b", flags) // 1 byte is 8 bits // v3.0 and v4.0 have different amount of flags switch version { case V2_3: if flagBits[0] == 1 { header.Flags.Unsynchronisated = true } else { header.Flags.Unsynchronisated = false } if flagBits[1] == 1 { header.Flags.HasExtendedHeader = true } else { header.Flags.HasExtendedHeader = false } if flagBits[2] == 1 { header.Flags.Experimental = true } else { header.Flags.Experimental = false } // always false, because ID3v2.3.0 does not support footers header.Flags.FooterPresent = false case V2_4: if flagBits[0] == 1 { header.Flags.Unsynchronisated = true } else { header.Flags.Unsynchronisated = false } if flagBits[1] == 1 { header.Flags.HasExtendedHeader = true } else { header.Flags.HasExtendedHeader = false } if flagBits[2] == 1 { header.Flags.Experimental = true } else { header.Flags.Experimental = false } if flagBits[3] == 1 { header.Flags.FooterPresent = true } else { header.Flags.FooterPresent = false } } // size sizeBytes, err := util.Read(rs, 4) if err != nil { return Header{}, err } size, err := util.BytesToIntIgnoreFirstBit(sizeBytes) if err != nil { return Header{}, err } header.Size = size return header, nil }