You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
2.7 KiB
138 lines
2.7 KiB
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 |
|
}
|
|
|