|
|
@ -4,20 +4,28 @@ import ( |
|
|
|
"bytes" |
|
|
|
"bytes" |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
"io" |
|
|
|
"io" |
|
|
|
|
|
|
|
"strconv" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/Unbewohnte/id3ed/util" |
|
|
|
"github.com/Unbewohnte/id3ed/util" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type HeaderFlags struct { |
|
|
|
|
|
|
|
Unsynchronisated bool |
|
|
|
|
|
|
|
HasExtendedHeader bool |
|
|
|
|
|
|
|
Experimental bool |
|
|
|
|
|
|
|
FooterPresent bool |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ID3v2.x header structure
|
|
|
|
// ID3v2.x header structure
|
|
|
|
type Header struct { |
|
|
|
type Header struct { |
|
|
|
Identifier string |
|
|
|
Identifier string |
|
|
|
Version string |
|
|
|
Flags HeaderFlags |
|
|
|
Unsynchronisated bool |
|
|
|
Version uint |
|
|
|
Compressed bool |
|
|
|
|
|
|
|
Size int64 // size of the whole tag - 10 header bytes
|
|
|
|
Size int64 // size of the whole tag - 10 header bytes
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Reads and structuralises ID3v2 header
|
|
|
|
// Reads and structuralises ID3v2.3.0 or ID3v2.4.0 header
|
|
|
|
func GetHeader(rs io.ReadSeeker) (*Header, error) { |
|
|
|
func GetHeader(rs io.ReadSeeker) (*Header, error) { |
|
|
|
var header Header |
|
|
|
var header Header |
|
|
|
|
|
|
|
|
|
|
@ -51,23 +59,63 @@ func GetHeader(rs io.ReadSeeker) (*Header, error) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
header.Version = fmt.Sprintf("%d%d", -majorVersion, revisionNumber) |
|
|
|
|
|
|
|
|
|
|
|
version, err := strconv.Atoi(fmt.Sprintf("%d%d", majorVersion, revisionNumber)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
header.Version = uint(version) |
|
|
|
|
|
|
|
|
|
|
|
// flags
|
|
|
|
// flags
|
|
|
|
flags, err := util.Read(rs, 1) |
|
|
|
flags, err := util.Read(rs, 1) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
bits := fmt.Sprintf("%08b", flags) // 1 byte is 8 bits
|
|
|
|
|
|
|
|
if bits[0] == 1 { |
|
|
|
flagBits := fmt.Sprintf("%08b", flags) // 1 byte is 8 bits
|
|
|
|
header.Unsynchronisated = true |
|
|
|
|
|
|
|
} else { |
|
|
|
// v3.0 and v4.0 have different amount of flags
|
|
|
|
header.Unsynchronisated = false |
|
|
|
switch version { |
|
|
|
} |
|
|
|
case 30: |
|
|
|
if bits[1] == 1 { |
|
|
|
if flagBits[0] == 1 { |
|
|
|
header.Compressed = true |
|
|
|
header.Flags.Unsynchronisated = true |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
header.Compressed = false |
|
|
|
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 40: |
|
|
|
|
|
|
|
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
|
|
|
|
// size
|
|
|
|