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.
// 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
)
var values_Riff_Fourcc = map[Riff_Fourcc]struct{}{1179011410: {}, 1330007625: {}, 1414744396: {}}
func (v Riff_Fourcc) isDefined() bool {
_, ok := values_Riff_Fourcc[v]
return ok
}
type Riff struct {
Chunk *Riff_Chunk
_io *kaitai.Stream
_root *Riff
_parent kaitai.Struct
_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) IO_() *kaitai.Stream {
return this._io
}
func (this *Riff) Read(io *kaitai.Stream, parent kaitai.Struct, 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._f_chunkId = true
this.chunkId = Riff_Fourcc(Riff_Fourcc(this.Chunk.Id))
return this.chunkId, nil
}
func (this *Riff) IsRiffChunk() (v bool, err error) {
if (this._f_isRiffChunk) {
return this.isRiffChunk, nil
}
this._f_isRiffChunk = true
tmp2, err := this.ChunkId()
if err != nil {
return false, err
}
this.isRiffChunk = bool(tmp2 == Riff_Fourcc__Riff)
return this.isRiffChunk, nil
}
func (this *Riff) ParentChunkData() (v *Riff_ParentChunkData, err error) {
if (this._f_parentChunkData) {
return this.parentChunkData, nil
}
this._f_parentChunkData = true
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
}
}
return this.parentChunkData, nil
}
func (this *Riff) Subchunks() (v []*Riff_ChunkType, err error) {
if (this._f_subchunks) {
return this.subchunks, nil
}
this._f_subchunks = true
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 := 0;; 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
}
}
return this.subchunks, nil
}
type Riff_Chunk struct {
Id uint32
Len uint32
DataSlot *Riff_Chunk_Slot
PadByte []byte
_io *kaitai.Stream
_root *Riff
_parent kaitai.Struct
_raw_DataSlot []byte
}
func NewRiff_Chunk() *Riff_Chunk {
return &Riff_Chunk{
}
}
func (this Riff_Chunk) IO_() *kaitai.Stream {
return this._io
}
func (this *Riff_Chunk) Read(io *kaitai.Stream, parent kaitai.Struct, root *Riff) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp9, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Id = uint32(tmp9)
tmp10, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Len = uint32(tmp10)
tmp11, err := this._io.ReadBytes(int(this.Len))
if err != nil {
return err
}
tmp11 = tmp11
this._raw_DataSlot = tmp11
_io__raw_DataSlot := kaitai.NewStream(bytes.NewReader(this._raw_DataSlot))
tmp12 := NewRiff_Chunk_Slot()
err = tmp12.Read(_io__raw_DataSlot, this, this._root)
if err != nil {
return err
}
this.DataSlot = tmp12
tmp13 := this.Len % 2
if tmp13 < 0 {
tmp13 += 2
}
tmp14, err := this._io.ReadBytes(int(tmp13))
if err != nil {
return err
}
tmp14 = tmp14
this.PadByte = tmp14
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) IO_() *kaitai.Stream {
return this._io
}
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_ChunkType struct {
SaveChunkOfs []byte
Chunk *Riff_Chunk
_io *kaitai.Stream
_root *Riff
_parent kaitai.Struct
_f_chunkData bool
chunkData *Riff_ListChunkData
_f_chunkId bool
chunkId Riff_Fourcc
_f_chunkIdReadable bool
chunkIdReadable string
_f_chunkOfs bool
chunkOfs int
}
func NewRiff_ChunkType() *Riff_ChunkType {
return &Riff_ChunkType{
}
}
func (this Riff_ChunkType) IO_() *kaitai.Stream {
return this._io
}
func (this *Riff_ChunkType) Read(io *kaitai.Stream, parent kaitai.Struct, root *Riff) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp15, err := this.ChunkOfs()
if err != nil {
return err
}
if (tmp15 < 0) {
tmp16, err := this._io.ReadBytes(int(0))
if err != nil {
return err
}
tmp16 = tmp16
this.SaveChunkOfs = tmp16
}
tmp17 := NewRiff_Chunk()
err = tmp17.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Chunk = tmp17
return err
}
func (this *Riff_ChunkType) ChunkData() (v *Riff_ListChunkData, err error) {
if (this._f_chunkData) {
return this.chunkData, nil
}
this._f_chunkData = true
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
}
tmp18, err := this.ChunkId()
if err != nil {
return nil, err
}
switch (tmp18) {
case Riff_Fourcc__List:
tmp19 := NewRiff_ListChunkData()
err = tmp19.Read(thisIo, this, this._root)
if err != nil {
return nil, err
}
this.chunkData = tmp19
}
_, err = thisIo.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.chunkData, nil
}
func (this *Riff_ChunkType) ChunkId() (v Riff_Fourcc, err error) {
if (this._f_chunkId) {
return this.chunkId, nil
}
this._f_chunkId = true
this.chunkId = Riff_Fourcc(Riff_Fourcc(this.Chunk.Id))
return this.chunkId, nil
}
func (this *Riff_ChunkType) ChunkIdReadable() (v string, err error) {
if (this._f_chunkIdReadable) {
return this.chunkIdReadable, nil
}
this._f_chunkIdReadable = true
_pos, err := this._io.Pos()
if err != nil {
return "", err
}
tmp20, err := this.ChunkOfs()
if err != nil {
return "", err
}
_, err = this._io.Seek(int64(tmp20), io.SeekStart)
if err != nil {
return "", err
}
tmp21, err := this._io.ReadBytes(int(4))
if err != nil {
return "", err
}
tmp21 = tmp21
this.chunkIdReadable = string(tmp21)
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return "", err
}
return this.chunkIdReadable, nil
}
func (this *Riff_ChunkType) ChunkOfs() (v int, err error) {
if (this._f_chunkOfs) {
return this.chunkOfs, nil
}
this._f_chunkOfs = true
tmp22, err := this._io.Pos()
if err != nil {
return 0, err
}
this.chunkOfs = int(tmp22)
return this.chunkOfs, nil
}
/**
* 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_chunkIdReadable bool
chunkIdReadable string
_f_chunkOfs bool
chunkOfs int
_f_idChars bool
idChars []byte
_f_isUnregisteredTag bool
isUnregisteredTag bool
}
func NewRiff_InfoSubchunk() *Riff_InfoSubchunk {
return &Riff_InfoSubchunk{
}
}
func (this Riff_InfoSubchunk) IO_() *kaitai.Stream {
return this._io
}
func (this *Riff_InfoSubchunk) Read(io *kaitai.Stream, parent *Riff_ListChunkData, root *Riff) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp23, err := this.ChunkOfs()
if err != nil {
return err
}
if (tmp23 < 0) {
tmp24, err := this._io.ReadBytes(int(0))
if err != nil {
return err
}
tmp24 = tmp24
this.SaveChunkOfs = tmp24
}
tmp25 := NewRiff_Chunk()
err = tmp25.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Chunk = tmp25
return err
}
func (this *Riff_InfoSubchunk) ChunkData() (v string, err error) {
if (this._f_chunkData) {
return this.chunkData, nil
}
this._f_chunkData = true
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
}
tmp26, err := this.IsUnregisteredTag()
if err != nil {
return nil, err
}
switch (tmp26) {
case false:
tmp27, err := thisIo.ReadBytesTerm(0, false, true, true)
if err != nil {
return nil, err
}
this.chunkData = string(tmp27)
}
_, err = thisIo.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.chunkData, nil
}
func (this *Riff_InfoSubchunk) ChunkIdReadable() (v string, err error) {
if (this._f_chunkIdReadable) {
return this.chunkIdReadable, nil
}
this._f_chunkIdReadable = true
tmp28, err := this.IdChars()
if err != nil {
return "", err
}
this.chunkIdReadable = string(string(tmp28))
return this.chunkIdReadable, nil
}
func (this *Riff_InfoSubchunk) ChunkOfs() (v int, err error) {
if (this._f_chunkOfs) {
return this.chunkOfs, nil
}
this._f_chunkOfs = true
tmp29, err := this._io.Pos()
if err != nil {
return 0, err
}
this.chunkOfs = int(tmp29)
return this.chunkOfs, nil
}
func (this *Riff_InfoSubchunk) IdChars() (v []byte, err error) {
if (this._f_idChars) {
return this.idChars, nil
}
this._f_idChars = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
tmp30, err := this.ChunkOfs()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(tmp30), io.SeekStart)
if err != nil {
return nil, err
}
tmp31, err := this._io.ReadBytes(int(4))
if err != nil {
return nil, err
}
tmp31 = tmp31
this.idChars = tmp31
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.idChars, 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
}
this._f_isUnregisteredTag = true
tmp32, err := this.IdChars()
if err != nil {
return false, err
}
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
}
this.isUnregisteredTag = bool( (( ((tmp32[0] >= 97) && (tmp33[0] <= 122)) ) || ( ((tmp34[1] >= 97) && (tmp35[1] <= 122)) ) || ( ((tmp36[2] >= 97) && (tmp37[2] <= 122)) ) || ( ((tmp38[3] >= 97) && (tmp39[3] <= 122)) )) )
return this.isUnregisteredTag, nil
}
type Riff_ListChunkData struct {
SaveParentChunkDataOfs []byte
ParentChunkData *Riff_ParentChunkData
_io *kaitai.Stream
_root *Riff
_parent *Riff_ChunkType
_f_formType bool
formType Riff_Fourcc
_f_formTypeReadable bool
formTypeReadable string
_f_parentChunkDataOfs bool
parentChunkDataOfs int
_f_subchunks bool
subchunks []kaitai.Struct
}
func NewRiff_ListChunkData() *Riff_ListChunkData {
return &Riff_ListChunkData{
}
}
func (this Riff_ListChunkData) IO_() *kaitai.Stream {
return this._io
}
func (this *Riff_ListChunkData) Read(io *kaitai.Stream, parent *Riff_ChunkType, root *Riff) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp40, err := this.ParentChunkDataOfs()
if err != nil {
return err
}
if (tmp40 < 0) {
tmp41, err := this._io.ReadBytes(int(0))
if err != nil {
return err
}
tmp41 = tmp41
this.SaveParentChunkDataOfs = tmp41
}
tmp42 := NewRiff_ParentChunkData()
err = tmp42.Read(this._io, this, this._root)
if err != nil {
return err
}
this.ParentChunkData = tmp42
return err
}
func (this *Riff_ListChunkData) FormType() (v Riff_Fourcc, err error) {
if (this._f_formType) {
return this.formType, nil
}
this._f_formType = true
this.formType = Riff_Fourcc(Riff_Fourcc(this.ParentChunkData.FormType))
return this.formType, nil
}
func (this *Riff_ListChunkData) FormTypeReadable() (v string, err error) {
if (this._f_formTypeReadable) {
return this.formTypeReadable, nil
}
this._f_formTypeReadable = true
_pos, err := this._io.Pos()
if err != nil {
return "", err
}
tmp43, err := this.ParentChunkDataOfs()
if err != nil {
return "", err
}
_, err = this._io.Seek(int64(tmp43), io.SeekStart)
if err != nil {
return "", err
}
tmp44, err := this._io.ReadBytes(int(4))
if err != nil {
return "", err
}
tmp44 = tmp44
this.formTypeReadable = string(tmp44)
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return "", err
}
return this.formTypeReadable, nil
}
func (this *Riff_ListChunkData) ParentChunkDataOfs() (v int, err error) {
if (this._f_parentChunkDataOfs) {
return this.parentChunkDataOfs, nil
}
this._f_parentChunkDataOfs = true
tmp45, err := this._io.Pos()
if err != nil {
return 0, err
}
this.parentChunkDataOfs = int(tmp45)
return this.parentChunkDataOfs, nil
}
func (this *Riff_ListChunkData) Subchunks() (v []kaitai.Struct, err error) {
if (this._f_subchunks) {
return this.subchunks, nil
}
this._f_subchunks = true
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 := 0;; i++ {
tmp46, err := this._io.EOF()
if err != nil {
return nil, err
}
if tmp46 {
break
}
tmp47, err := this.FormType()
if err != nil {
return nil, err
}
switch (tmp47) {
case Riff_Fourcc__Info:
tmp48 := NewRiff_InfoSubchunk()
err = tmp48.Read(thisIo, this, this._root)
if err != nil {
return nil, err
}
this.subchunks = append(this.subchunks, tmp48)
default:
tmp49 := NewRiff_ChunkType()
err = tmp49.Read(thisIo, this, this._root)
if err != nil {
return nil, err
}
this.subchunks = append(this.subchunks, tmp49)
}
}
_, err = thisIo.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.subchunks, nil
}
type Riff_ParentChunkData struct {
FormType uint32
SubchunksSlot *Riff_ParentChunkData_Slot
_io *kaitai.Stream
_root *Riff
_parent kaitai.Struct
_raw_SubchunksSlot []byte
}
func NewRiff_ParentChunkData() *Riff_ParentChunkData {
return &Riff_ParentChunkData{
}
}
func (this Riff_ParentChunkData) IO_() *kaitai.Stream {
return this._io
}
func (this *Riff_ParentChunkData) Read(io *kaitai.Stream, parent kaitai.Struct, root *Riff) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp50, err := this._io.ReadU4le()
if err != nil {
return err
}
this.FormType = uint32(tmp50)
tmp51, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp51 = tmp51
this._raw_SubchunksSlot = tmp51
_io__raw_SubchunksSlot := kaitai.NewStream(bytes.NewReader(this._raw_SubchunksSlot))
tmp52 := NewRiff_ParentChunkData_Slot()
err = tmp52.Read(_io__raw_SubchunksSlot, this, this._root)
if err != nil {
return err
}
this.SubchunksSlot = tmp52
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) IO_() *kaitai.Stream {
return this._io
}
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
}