Resource Interchange File Format (RIFF): Go parsing library

The Resource Interchange File Format (RIFF) is a generic file container format for storing data in tagged chunks. It is primarily used to store multimedia such as sound and video, though it may also be used to store any arbitrary data.

The Microsoft implementation is mostly known through container formats like AVI, ANI and WAV, which use RIFF as their basis.

This page hosts a formal specification of Resource Interchange File Format (RIFF) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse Resource Interchange File Format (RIFF)

riff.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"io"
	"bytes"
)


/**
 * The Resource Interchange File Format (RIFF) is a generic file container format
 * for storing data in tagged chunks. It is primarily used to store multimedia
 * such as sound and video, though it may also be used to store any arbitrary data.
 * 
 * The Microsoft implementation is mostly known through container formats
 * like AVI, ANI and WAV, which use RIFF as their basis.
 * @see <a href="https://www.johnloomis.org/cpe102/asgn/asgn1/riff.html">Source</a>
 */

type Riff_Fourcc int
const (
	Riff_Fourcc__Riff Riff_Fourcc = 1179011410
	Riff_Fourcc__Info Riff_Fourcc = 1330007625
	Riff_Fourcc__List Riff_Fourcc = 1414744396
)
type Riff struct {
	Chunk *Riff_Chunk
	_io *kaitai.Stream
	_root *Riff
	_parent interface{}
	_f_chunkId bool
	chunkId Riff_Fourcc
	_f_isRiffChunk bool
	isRiffChunk bool
	_f_parentChunkData bool
	parentChunkData *Riff_ParentChunkData
	_f_subchunks bool
	subchunks []*Riff_ChunkType
}
func NewRiff() *Riff {
	return &Riff{
	}
}

func (this *Riff) Read(io *kaitai.Stream, parent interface{}, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewRiff_Chunk()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Chunk = tmp1
	return err
}
func (this *Riff) ChunkId() (v Riff_Fourcc, err error) {
	if (this._f_chunkId) {
		return this.chunkId, nil
	}
	this.chunkId = Riff_Fourcc(Riff_Fourcc(this.Chunk.Id))
	this._f_chunkId = true
	return this.chunkId, nil
}
func (this *Riff) IsRiffChunk() (v bool, err error) {
	if (this._f_isRiffChunk) {
		return this.isRiffChunk, nil
	}
	tmp2, err := this.ChunkId()
	if err != nil {
		return false, err
	}
	this.isRiffChunk = bool(tmp2 == Riff_Fourcc__Riff)
	this._f_isRiffChunk = true
	return this.isRiffChunk, nil
}
func (this *Riff) ParentChunkData() (v *Riff_ParentChunkData, err error) {
	if (this._f_parentChunkData) {
		return this.parentChunkData, nil
	}
	tmp3, err := this.IsRiffChunk()
	if err != nil {
		return nil, err
	}
	if (tmp3) {
		thisIo := this.Chunk.DataSlot._io
		_pos, err := thisIo.Pos()
		if err != nil {
			return nil, err
		}
		_, err = thisIo.Seek(int64(0), io.SeekStart)
		if err != nil {
			return nil, err
		}
		tmp4 := NewRiff_ParentChunkData()
		err = tmp4.Read(thisIo, this, this._root)
		if err != nil {
			return nil, err
		}
		this.parentChunkData = tmp4
		_, err = thisIo.Seek(_pos, io.SeekStart)
		if err != nil {
			return nil, err
		}
		this._f_parentChunkData = true
	}
	this._f_parentChunkData = true
	return this.parentChunkData, nil
}
func (this *Riff) Subchunks() (v []*Riff_ChunkType, err error) {
	if (this._f_subchunks) {
		return this.subchunks, nil
	}
	tmp5, err := this.IsRiffChunk()
	if err != nil {
		return nil, err
	}
	if (tmp5) {
		tmp6, err := this.ParentChunkData()
		if err != nil {
			return nil, err
		}
		thisIo := tmp6.SubchunksSlot._io
		_pos, err := thisIo.Pos()
		if err != nil {
			return nil, err
		}
		_, err = thisIo.Seek(int64(0), io.SeekStart)
		if err != nil {
			return nil, err
		}
		for i := 1;; i++ {
			tmp7, err := this._io.EOF()
			if err != nil {
				return nil, err
			}
			if tmp7 {
				break
			}
			tmp8 := NewRiff_ChunkType()
			err = tmp8.Read(thisIo, this, this._root)
			if err != nil {
				return nil, err
			}
			this.subchunks = append(this.subchunks, tmp8)
		}
		_, err = thisIo.Seek(_pos, io.SeekStart)
		if err != nil {
			return nil, err
		}
		this._f_subchunks = true
	}
	this._f_subchunks = true
	return this.subchunks, nil
}
type Riff_ListChunkData struct {
	SaveParentChunkDataOfs []byte
	ParentChunkData *Riff_ParentChunkData
	_io *kaitai.Stream
	_root *Riff
	_parent *Riff_ChunkType
	_f_parentChunkDataOfs bool
	parentChunkDataOfs int
	_f_formType bool
	formType Riff_Fourcc
	_f_formTypeReadable bool
	formTypeReadable string
	_f_subchunks bool
	subchunks []interface{}
}
func NewRiff_ListChunkData() *Riff_ListChunkData {
	return &Riff_ListChunkData{
	}
}

func (this *Riff_ListChunkData) Read(io *kaitai.Stream, parent *Riff_ChunkType, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp9, err := this.ParentChunkDataOfs()
	if err != nil {
		return err
	}
	if (tmp9 < 0) {
		tmp10, err := this._io.ReadBytes(int(0))
		if err != nil {
			return err
		}
		tmp10 = tmp10
		this.SaveParentChunkDataOfs = tmp10
	}
	tmp11 := NewRiff_ParentChunkData()
	err = tmp11.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.ParentChunkData = tmp11
	return err
}
func (this *Riff_ListChunkData) ParentChunkDataOfs() (v int, err error) {
	if (this._f_parentChunkDataOfs) {
		return this.parentChunkDataOfs, nil
	}
	tmp12, err := this._io.Pos()
	if err != nil {
		return 0, err
	}
	this.parentChunkDataOfs = int(tmp12)
	this._f_parentChunkDataOfs = true
	return this.parentChunkDataOfs, nil
}
func (this *Riff_ListChunkData) FormType() (v Riff_Fourcc, err error) {
	if (this._f_formType) {
		return this.formType, nil
	}
	this.formType = Riff_Fourcc(Riff_Fourcc(this.ParentChunkData.FormType))
	this._f_formType = true
	return this.formType, nil
}
func (this *Riff_ListChunkData) FormTypeReadable() (v string, err error) {
	if (this._f_formTypeReadable) {
		return this.formTypeReadable, nil
	}
	_pos, err := this._io.Pos()
	if err != nil {
		return "", err
	}
	tmp13, err := this.ParentChunkDataOfs()
	if err != nil {
		return "", err
	}
	_, err = this._io.Seek(int64(tmp13), io.SeekStart)
	if err != nil {
		return "", err
	}
	tmp14, err := this._io.ReadBytes(int(4))
	if err != nil {
		return "", err
	}
	tmp14 = tmp14
	this.formTypeReadable = string(tmp14)
	_, err = this._io.Seek(_pos, io.SeekStart)
	if err != nil {
		return "", err
	}
	this._f_formTypeReadable = true
	this._f_formTypeReadable = true
	return this.formTypeReadable, nil
}
func (this *Riff_ListChunkData) Subchunks() (v []interface{}, err error) {
	if (this._f_subchunks) {
		return this.subchunks, nil
	}
	thisIo := this.ParentChunkData.SubchunksSlot._io
	_pos, err := thisIo.Pos()
	if err != nil {
		return nil, err
	}
	_, err = thisIo.Seek(int64(0), io.SeekStart)
	if err != nil {
		return nil, err
	}
	for i := 1;; i++ {
		tmp15, err := this._io.EOF()
		if err != nil {
			return nil, err
		}
		if tmp15 {
			break
		}
		tmp16, err := this.FormType()
		if err != nil {
			return nil, err
		}
		switch (tmp16) {
		case Riff_Fourcc__Info:
			tmp17 := NewRiff_InfoSubchunk()
			err = tmp17.Read(thisIo, this, this._root)
			if err != nil {
				return nil, err
			}
			this.subchunks = append(this.subchunks, tmp17)
		default:
			tmp18 := NewRiff_ChunkType()
			err = tmp18.Read(thisIo, this, this._root)
			if err != nil {
				return nil, err
			}
			this.subchunks = append(this.subchunks, tmp18)
		}
	}
	_, err = thisIo.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_subchunks = true
	this._f_subchunks = true
	return this.subchunks, nil
}
type Riff_Chunk struct {
	Id uint32
	Len uint32
	DataSlot *Riff_Chunk_Slot
	PadByte []byte
	_io *kaitai.Stream
	_root *Riff
	_parent interface{}
	_raw_DataSlot []byte
}
func NewRiff_Chunk() *Riff_Chunk {
	return &Riff_Chunk{
	}
}

func (this *Riff_Chunk) Read(io *kaitai.Stream, parent interface{}, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp19, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.Id = uint32(tmp19)
	tmp20, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.Len = uint32(tmp20)
	tmp21, err := this._io.ReadBytes(int(this.Len))
	if err != nil {
		return err
	}
	tmp21 = tmp21
	this._raw_DataSlot = tmp21
	_io__raw_DataSlot := kaitai.NewStream(bytes.NewReader(this._raw_DataSlot))
	tmp22 := NewRiff_Chunk_Slot()
	err = tmp22.Read(_io__raw_DataSlot, this, this._root)
	if err != nil {
		return err
	}
	this.DataSlot = tmp22
	tmp23 := this.Len % 2
	if tmp23 < 0 {
		tmp23 += 2
	}
	tmp24, err := this._io.ReadBytes(int(tmp23))
	if err != nil {
		return err
	}
	tmp24 = tmp24
	this.PadByte = tmp24
	return err
}
type Riff_Chunk_Slot struct {
	_io *kaitai.Stream
	_root *Riff
	_parent *Riff_Chunk
}
func NewRiff_Chunk_Slot() *Riff_Chunk_Slot {
	return &Riff_Chunk_Slot{
	}
}

func (this *Riff_Chunk_Slot) Read(io *kaitai.Stream, parent *Riff_Chunk, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	return err
}
type Riff_ParentChunkData struct {
	FormType uint32
	SubchunksSlot *Riff_ParentChunkData_Slot
	_io *kaitai.Stream
	_root *Riff
	_parent interface{}
	_raw_SubchunksSlot []byte
}
func NewRiff_ParentChunkData() *Riff_ParentChunkData {
	return &Riff_ParentChunkData{
	}
}

func (this *Riff_ParentChunkData) Read(io *kaitai.Stream, parent interface{}, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp25, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.FormType = uint32(tmp25)
	tmp26, err := this._io.ReadBytesFull()
	if err != nil {
		return err
	}
	tmp26 = tmp26
	this._raw_SubchunksSlot = tmp26
	_io__raw_SubchunksSlot := kaitai.NewStream(bytes.NewReader(this._raw_SubchunksSlot))
	tmp27 := NewRiff_ParentChunkData_Slot()
	err = tmp27.Read(_io__raw_SubchunksSlot, this, this._root)
	if err != nil {
		return err
	}
	this.SubchunksSlot = tmp27
	return err
}
type Riff_ParentChunkData_Slot struct {
	_io *kaitai.Stream
	_root *Riff
	_parent *Riff_ParentChunkData
}
func NewRiff_ParentChunkData_Slot() *Riff_ParentChunkData_Slot {
	return &Riff_ParentChunkData_Slot{
	}
}

func (this *Riff_ParentChunkData_Slot) Read(io *kaitai.Stream, parent *Riff_ParentChunkData, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	return err
}

/**
 * All registered subchunks in the INFO chunk are NULL-terminated strings,
 * but the unregistered might not be. By convention, the registered
 * chunk IDs are in uppercase and the unregistered IDs are in lowercase.
 * 
 * If the chunk ID of an INFO subchunk contains a lowercase
 * letter, this chunk is considered as unregistered and thus we can make
 * no assumptions about the type of data.
 */
type Riff_InfoSubchunk struct {
	SaveChunkOfs []byte
	Chunk *Riff_Chunk
	_io *kaitai.Stream
	_root *Riff
	_parent *Riff_ListChunkData
	_f_chunkData bool
	chunkData string
	_f_isUnregisteredTag bool
	isUnregisteredTag bool
	_f_idChars bool
	idChars []byte
	_f_chunkIdReadable bool
	chunkIdReadable string
	_f_chunkOfs bool
	chunkOfs int
}
func NewRiff_InfoSubchunk() *Riff_InfoSubchunk {
	return &Riff_InfoSubchunk{
	}
}

func (this *Riff_InfoSubchunk) Read(io *kaitai.Stream, parent *Riff_ListChunkData, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp28, err := this.ChunkOfs()
	if err != nil {
		return err
	}
	if (tmp28 < 0) {
		tmp29, err := this._io.ReadBytes(int(0))
		if err != nil {
			return err
		}
		tmp29 = tmp29
		this.SaveChunkOfs = tmp29
	}
	tmp30 := NewRiff_Chunk()
	err = tmp30.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Chunk = tmp30
	return err
}
func (this *Riff_InfoSubchunk) ChunkData() (v string, err error) {
	if (this._f_chunkData) {
		return this.chunkData, nil
	}
	thisIo := this.Chunk.DataSlot._io
	_pos, err := thisIo.Pos()
	if err != nil {
		return nil, err
	}
	_, err = thisIo.Seek(int64(0), io.SeekStart)
	if err != nil {
		return nil, err
	}
	tmp31, err := this.IsUnregisteredTag()
	if err != nil {
		return nil, err
	}
	switch (tmp31) {
	case false:
		tmp32, err := thisIo.ReadBytesTerm(0, false, true, true)
		if err != nil {
			return nil, err
		}
		this.chunkData = string(tmp32)
	}
	_, err = thisIo.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_chunkData = true
	this._f_chunkData = true
	return this.chunkData, nil
}

/**
 * Check if chunk_id contains lowercase characters ([a-z], ASCII 97 = a, ASCII 122 = z).
 */
func (this *Riff_InfoSubchunk) IsUnregisteredTag() (v bool, err error) {
	if (this._f_isUnregisteredTag) {
		return this.isUnregisteredTag, nil
	}
	tmp33, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp34, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp35, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp36, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp37, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp38, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp39, err := this.IdChars()
	if err != nil {
		return false, err
	}
	tmp40, err := this.IdChars()
	if err != nil {
		return false, err
	}
	this.isUnregisteredTag = bool( (( ((tmp33[0] >= 97) && (tmp34[0] <= 122)) ) || ( ((tmp35[1] >= 97) && (tmp36[1] <= 122)) ) || ( ((tmp37[2] >= 97) && (tmp38[2] <= 122)) ) || ( ((tmp39[3] >= 97) && (tmp40[3] <= 122)) )) )
	this._f_isUnregisteredTag = true
	return this.isUnregisteredTag, nil
}
func (this *Riff_InfoSubchunk) IdChars() (v []byte, err error) {
	if (this._f_idChars) {
		return this.idChars, nil
	}
	_pos, err := this._io.Pos()
	if err != nil {
		return nil, err
	}
	tmp41, err := this.ChunkOfs()
	if err != nil {
		return nil, err
	}
	_, err = this._io.Seek(int64(tmp41), io.SeekStart)
	if err != nil {
		return nil, err
	}
	tmp42, err := this._io.ReadBytes(int(4))
	if err != nil {
		return nil, err
	}
	tmp42 = tmp42
	this.idChars = tmp42
	_, err = this._io.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_idChars = true
	this._f_idChars = true
	return this.idChars, nil
}
func (this *Riff_InfoSubchunk) ChunkIdReadable() (v string, err error) {
	if (this._f_chunkIdReadable) {
		return this.chunkIdReadable, nil
	}
	tmp43, err := this.IdChars()
	if err != nil {
		return "", err
	}
	this.chunkIdReadable = string(string(tmp43))
	this._f_chunkIdReadable = true
	return this.chunkIdReadable, nil
}
func (this *Riff_InfoSubchunk) ChunkOfs() (v int, err error) {
	if (this._f_chunkOfs) {
		return this.chunkOfs, nil
	}
	tmp44, err := this._io.Pos()
	if err != nil {
		return 0, err
	}
	this.chunkOfs = int(tmp44)
	this._f_chunkOfs = true
	return this.chunkOfs, nil
}
type Riff_ChunkType struct {
	SaveChunkOfs []byte
	Chunk *Riff_Chunk
	_io *kaitai.Stream
	_root *Riff
	_parent interface{}
	_f_chunkOfs bool
	chunkOfs int
	_f_chunkId bool
	chunkId Riff_Fourcc
	_f_chunkIdReadable bool
	chunkIdReadable string
	_f_chunkData bool
	chunkData *Riff_ListChunkData
}
func NewRiff_ChunkType() *Riff_ChunkType {
	return &Riff_ChunkType{
	}
}

func (this *Riff_ChunkType) Read(io *kaitai.Stream, parent interface{}, root *Riff) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp45, err := this.ChunkOfs()
	if err != nil {
		return err
	}
	if (tmp45 < 0) {
		tmp46, err := this._io.ReadBytes(int(0))
		if err != nil {
			return err
		}
		tmp46 = tmp46
		this.SaveChunkOfs = tmp46
	}
	tmp47 := NewRiff_Chunk()
	err = tmp47.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Chunk = tmp47
	return err
}
func (this *Riff_ChunkType) ChunkOfs() (v int, err error) {
	if (this._f_chunkOfs) {
		return this.chunkOfs, nil
	}
	tmp48, err := this._io.Pos()
	if err != nil {
		return 0, err
	}
	this.chunkOfs = int(tmp48)
	this._f_chunkOfs = true
	return this.chunkOfs, nil
}
func (this *Riff_ChunkType) ChunkId() (v Riff_Fourcc, err error) {
	if (this._f_chunkId) {
		return this.chunkId, nil
	}
	this.chunkId = Riff_Fourcc(Riff_Fourcc(this.Chunk.Id))
	this._f_chunkId = true
	return this.chunkId, nil
}
func (this *Riff_ChunkType) ChunkIdReadable() (v string, err error) {
	if (this._f_chunkIdReadable) {
		return this.chunkIdReadable, nil
	}
	_pos, err := this._io.Pos()
	if err != nil {
		return "", err
	}
	tmp49, err := this.ChunkOfs()
	if err != nil {
		return "", err
	}
	_, err = this._io.Seek(int64(tmp49), io.SeekStart)
	if err != nil {
		return "", err
	}
	tmp50, err := this._io.ReadBytes(int(4))
	if err != nil {
		return "", err
	}
	tmp50 = tmp50
	this.chunkIdReadable = string(tmp50)
	_, err = this._io.Seek(_pos, io.SeekStart)
	if err != nil {
		return "", err
	}
	this._f_chunkIdReadable = true
	this._f_chunkIdReadable = true
	return this.chunkIdReadable, nil
}
func (this *Riff_ChunkType) ChunkData() (v *Riff_ListChunkData, err error) {
	if (this._f_chunkData) {
		return this.chunkData, nil
	}
	thisIo := this.Chunk.DataSlot._io
	_pos, err := thisIo.Pos()
	if err != nil {
		return nil, err
	}
	_, err = thisIo.Seek(int64(0), io.SeekStart)
	if err != nil {
		return nil, err
	}
	tmp51, err := this.ChunkId()
	if err != nil {
		return nil, err
	}
	switch (tmp51) {
	case Riff_Fourcc__List:
		tmp52 := NewRiff_ListChunkData()
		err = tmp52.Read(thisIo, this, this._root)
		if err != nil {
			return nil, err
		}
		this.chunkData = tmp52
	}
	_, err = thisIo.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_chunkData = true
	this._f_chunkData = true
	return this.chunkData, nil
}