This page hosts a formal specification of ID3v2.3 tag for .mp3 files using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
)
/**
* @see <a href="https://id3.org/id3v2.3.0">Source</a>
*/
type Id3v23 struct {
Tag *Id3v23_Tag
_io *kaitai.Stream
_root *Id3v23
_parent kaitai.Struct
}
func NewId3v23() *Id3v23 {
return &Id3v23{
}
}
func (this Id3v23) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23) Read(io *kaitai.Stream, parent kaitai.Struct, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1 := NewId3v23_Tag()
err = tmp1.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Tag = tmp1
return err
}
/**
* @see "Section 3.3. ID3v2 frame overview"
*/
type Id3v23_Frame struct {
Id string
Size uint32
Flags *Id3v23_Frame_Flags
Data []byte
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_Tag
_f_isInvalid bool
isInvalid bool
}
func NewId3v23_Frame() *Id3v23_Frame {
return &Id3v23_Frame{
}
}
func (this Id3v23_Frame) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_Frame) Read(io *kaitai.Stream, parent *Id3v23_Tag, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp2, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp2 = tmp2
this.Id = string(tmp2)
tmp3, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Size = uint32(tmp3)
tmp4 := NewId3v23_Frame_Flags()
err = tmp4.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Flags = tmp4
tmp5, err := this._io.ReadBytes(int(this.Size))
if err != nil {
return err
}
tmp5 = tmp5
this.Data = tmp5
return err
}
func (this *Id3v23_Frame) IsInvalid() (v bool, err error) {
if (this._f_isInvalid) {
return this.isInvalid, nil
}
this._f_isInvalid = true
this.isInvalid = bool(this.Id == "\000\000\000\000")
return this.isInvalid, nil
}
type Id3v23_Frame_Flags struct {
FlagDiscardAlterTag bool
FlagDiscardAlterFile bool
FlagReadOnly bool
Reserved1 uint64
FlagCompressed bool
FlagEncrypted bool
FlagGrouping bool
Reserved2 uint64
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_Frame
}
func NewId3v23_Frame_Flags() *Id3v23_Frame_Flags {
return &Id3v23_Frame_Flags{
}
}
func (this Id3v23_Frame_Flags) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_Frame_Flags) Read(io *kaitai.Stream, parent *Id3v23_Frame, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp6, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagDiscardAlterTag = tmp6 != 0
tmp7, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagDiscardAlterFile = tmp7 != 0
tmp8, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagReadOnly = tmp8 != 0
tmp9, err := this._io.ReadBitsIntBe(5)
if err != nil {
return err
}
this.Reserved1 = tmp9
tmp10, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagCompressed = tmp10 != 0
tmp11, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagEncrypted = tmp11 != 0
tmp12, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagGrouping = tmp12 != 0
tmp13, err := this._io.ReadBitsIntBe(5)
if err != nil {
return err
}
this.Reserved2 = tmp13
return err
}
/**
* ID3v2 fixed header
* @see "Section 3.1. ID3v2 header"
*/
type Id3v23_Header struct {
Magic []byte
VersionMajor uint8
VersionRevision uint8
Flags *Id3v23_Header_Flags
Size *Id3v23_U4beSynchsafe
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_Tag
}
func NewId3v23_Header() *Id3v23_Header {
return &Id3v23_Header{
}
}
func (this Id3v23_Header) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_Header) Read(io *kaitai.Stream, parent *Id3v23_Tag, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp14, err := this._io.ReadBytes(int(3))
if err != nil {
return err
}
tmp14 = tmp14
this.Magic = tmp14
if !(bytes.Equal(this.Magic, []uint8{73, 68, 51})) {
return kaitai.NewValidationNotEqualError([]uint8{73, 68, 51}, this.Magic, this._io, "/types/header/seq/0")
}
tmp15, err := this._io.ReadU1()
if err != nil {
return err
}
this.VersionMajor = tmp15
tmp16, err := this._io.ReadU1()
if err != nil {
return err
}
this.VersionRevision = tmp16
tmp17 := NewId3v23_Header_Flags()
err = tmp17.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Flags = tmp17
tmp18 := NewId3v23_U4beSynchsafe()
err = tmp18.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Size = tmp18
return err
}
type Id3v23_Header_Flags struct {
FlagUnsynchronization bool
FlagHeaderex bool
FlagExperimental bool
Reserved uint64
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_Header
}
func NewId3v23_Header_Flags() *Id3v23_Header_Flags {
return &Id3v23_Header_Flags{
}
}
func (this Id3v23_Header_Flags) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_Header_Flags) Read(io *kaitai.Stream, parent *Id3v23_Header, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp19, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagUnsynchronization = tmp19 != 0
tmp20, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagHeaderex = tmp20 != 0
tmp21, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagExperimental = tmp21 != 0
tmp22, err := this._io.ReadBitsIntBe(5)
if err != nil {
return err
}
this.Reserved = tmp22
return err
}
/**
* ID3v2 extended header
* @see "Section 3.2. ID3v2 extended header"
*/
type Id3v23_HeaderEx struct {
Size uint32
FlagsEx *Id3v23_HeaderEx_FlagsEx
PaddingSize uint32
Crc uint32
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_Tag
}
func NewId3v23_HeaderEx() *Id3v23_HeaderEx {
return &Id3v23_HeaderEx{
}
}
func (this Id3v23_HeaderEx) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_HeaderEx) Read(io *kaitai.Stream, parent *Id3v23_Tag, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp23, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Size = uint32(tmp23)
tmp24 := NewId3v23_HeaderEx_FlagsEx()
err = tmp24.Read(this._io, this, this._root)
if err != nil {
return err
}
this.FlagsEx = tmp24
tmp25, err := this._io.ReadU4be()
if err != nil {
return err
}
this.PaddingSize = uint32(tmp25)
if (this.FlagsEx.FlagCrc) {
tmp26, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Crc = uint32(tmp26)
}
return err
}
type Id3v23_HeaderEx_FlagsEx struct {
FlagCrc bool
Reserved uint64
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_HeaderEx
}
func NewId3v23_HeaderEx_FlagsEx() *Id3v23_HeaderEx_FlagsEx {
return &Id3v23_HeaderEx_FlagsEx{
}
}
func (this Id3v23_HeaderEx_FlagsEx) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_HeaderEx_FlagsEx) Read(io *kaitai.Stream, parent *Id3v23_HeaderEx, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp27, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.FlagCrc = tmp27 != 0
tmp28, err := this._io.ReadBitsIntBe(15)
if err != nil {
return err
}
this.Reserved = tmp28
return err
}
/**
* @see "Section 3. ID3v2 overview"
*/
type Id3v23_Tag struct {
Header *Id3v23_Header
HeaderEx *Id3v23_HeaderEx
Frames []*Id3v23_Frame
Padding []byte
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23
}
func NewId3v23_Tag() *Id3v23_Tag {
return &Id3v23_Tag{
}
}
func (this Id3v23_Tag) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_Tag) Read(io *kaitai.Stream, parent *Id3v23, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp29 := NewId3v23_Header()
err = tmp29.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Header = tmp29
if (this.Header.Flags.FlagHeaderex) {
tmp30 := NewId3v23_HeaderEx()
err = tmp30.Read(this._io, this, this._root)
if err != nil {
return err
}
this.HeaderEx = tmp30
}
for i := 1;; i++ {
tmp31 := NewId3v23_Frame()
err = tmp31.Read(this._io, this, this._root)
if err != nil {
return err
}
_it := tmp31
this.Frames = append(this.Frames, _it)
tmp32, err := this._io.Pos()
if err != nil {
return err
}
tmp33, err := this.Header.Size.Value()
if err != nil {
return err
}
tmp34, err := _it.IsInvalid()
if err != nil {
return err
}
if ((tmp32 + _it.Size > tmp33) || (tmp34)) {
break
}
}
if (this.Header.Flags.FlagHeaderex) {
tmp35, err := this._io.Pos()
if err != nil {
return err
}
tmp36, err := this._io.ReadBytes(int(this.HeaderEx.PaddingSize - tmp35))
if err != nil {
return err
}
tmp36 = tmp36
this.Padding = tmp36
}
return err
}
type Id3v23_U1beSynchsafe struct {
Padding bool
Value uint64
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_U2beSynchsafe
}
func NewId3v23_U1beSynchsafe() *Id3v23_U1beSynchsafe {
return &Id3v23_U1beSynchsafe{
}
}
func (this Id3v23_U1beSynchsafe) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_U1beSynchsafe) Read(io *kaitai.Stream, parent *Id3v23_U2beSynchsafe, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp37, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.Padding = tmp37 != 0
tmp38, err := this._io.ReadBitsIntBe(7)
if err != nil {
return err
}
this.Value = tmp38
return err
}
type Id3v23_U2beSynchsafe struct {
Byte0 *Id3v23_U1beSynchsafe
Byte1 *Id3v23_U1beSynchsafe
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_U4beSynchsafe
_f_value bool
value int
}
func NewId3v23_U2beSynchsafe() *Id3v23_U2beSynchsafe {
return &Id3v23_U2beSynchsafe{
}
}
func (this Id3v23_U2beSynchsafe) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_U2beSynchsafe) Read(io *kaitai.Stream, parent *Id3v23_U4beSynchsafe, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp39 := NewId3v23_U1beSynchsafe()
err = tmp39.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Byte0 = tmp39
tmp40 := NewId3v23_U1beSynchsafe()
err = tmp40.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Byte1 = tmp40
return err
}
func (this *Id3v23_U2beSynchsafe) Value() (v int, err error) {
if (this._f_value) {
return this.value, nil
}
this._f_value = true
this.value = int(this.Byte0.Value << 7 | this.Byte1.Value)
return this.value, nil
}
type Id3v23_U4beSynchsafe struct {
Short0 *Id3v23_U2beSynchsafe
Short1 *Id3v23_U2beSynchsafe
_io *kaitai.Stream
_root *Id3v23
_parent *Id3v23_Header
_f_value bool
value int
}
func NewId3v23_U4beSynchsafe() *Id3v23_U4beSynchsafe {
return &Id3v23_U4beSynchsafe{
}
}
func (this Id3v23_U4beSynchsafe) IO_() *kaitai.Stream {
return this._io
}
func (this *Id3v23_U4beSynchsafe) Read(io *kaitai.Stream, parent *Id3v23_Header, root *Id3v23) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp41 := NewId3v23_U2beSynchsafe()
err = tmp41.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Short0 = tmp41
tmp42 := NewId3v23_U2beSynchsafe()
err = tmp42.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Short1 = tmp42
return err
}
func (this *Id3v23_U4beSynchsafe) Value() (v int, err error) {
if (this._f_value) {
return this.value, nil
}
this._f_value = true
tmp43, err := this.Short0.Value()
if err != nil {
return 0, err
}
tmp44, err := this.Short1.Value()
if err != nil {
return 0, err
}
this.value = int(tmp43 << 14 | tmp44)
return this.value, nil
}