TAP files are used by emulators of ZX Spectrum computer (released in 1982 by Sinclair Research). TAP file stores blocks of data as if they are written to magnetic tape, which was used as primary media for ZX Spectrum. Contents of this file can be viewed as a very simple linear filesystem, storing named files with some basic metainformation prepended as a header.
This page hosts a formal specification of ZX Spectrum tape file 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"
)
/**
* TAP files are used by emulators of ZX Spectrum computer (released in
* 1982 by Sinclair Research). TAP file stores blocks of data as if
* they are written to magnetic tape, which was used as primary media
* for ZX Spectrum. Contents of this file can be viewed as a very
* simple linear filesystem, storing named files with some basic
* metainformation prepended as a header.
* @see <a href="https://sinclair.wiki.zxnet.co.uk/wiki/TAP_format">Source</a>
*/
type ZxSpectrumTap_FlagEnum int
const (
ZxSpectrumTap_FlagEnum__Header ZxSpectrumTap_FlagEnum = 0
ZxSpectrumTap_FlagEnum__Data ZxSpectrumTap_FlagEnum = 255
)
var values_ZxSpectrumTap_FlagEnum = map[ZxSpectrumTap_FlagEnum]struct{}{0: {}, 255: {}}
func (v ZxSpectrumTap_FlagEnum) isDefined() bool {
_, ok := values_ZxSpectrumTap_FlagEnum[v]
return ok
}
type ZxSpectrumTap_HeaderTypeEnum int
const (
ZxSpectrumTap_HeaderTypeEnum__Program ZxSpectrumTap_HeaderTypeEnum = 0
ZxSpectrumTap_HeaderTypeEnum__NumArray ZxSpectrumTap_HeaderTypeEnum = 1
ZxSpectrumTap_HeaderTypeEnum__CharArray ZxSpectrumTap_HeaderTypeEnum = 2
ZxSpectrumTap_HeaderTypeEnum__Bytes ZxSpectrumTap_HeaderTypeEnum = 3
)
var values_ZxSpectrumTap_HeaderTypeEnum = map[ZxSpectrumTap_HeaderTypeEnum]struct{}{0: {}, 1: {}, 2: {}, 3: {}}
func (v ZxSpectrumTap_HeaderTypeEnum) isDefined() bool {
_, ok := values_ZxSpectrumTap_HeaderTypeEnum[v]
return ok
}
type ZxSpectrumTap struct {
Blocks []*ZxSpectrumTap_Block
_io *kaitai.Stream
_root *ZxSpectrumTap
_parent kaitai.Struct
}
func NewZxSpectrumTap() *ZxSpectrumTap {
return &ZxSpectrumTap{
}
}
func (this ZxSpectrumTap) IO_() *kaitai.Stream {
return this._io
}
func (this *ZxSpectrumTap) Read(io *kaitai.Stream, parent kaitai.Struct, root *ZxSpectrumTap) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0;; i++ {
tmp1, err := this._io.EOF()
if err != nil {
return err
}
if tmp1 {
break
}
tmp2 := NewZxSpectrumTap_Block()
err = tmp2.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Blocks = append(this.Blocks, tmp2)
}
return err
}
type ZxSpectrumTap_ArrayParams struct {
Reserved uint8
VarName uint8
Reserved1 []byte
_io *kaitai.Stream
_root *ZxSpectrumTap
_parent *ZxSpectrumTap_Header
}
func NewZxSpectrumTap_ArrayParams() *ZxSpectrumTap_ArrayParams {
return &ZxSpectrumTap_ArrayParams{
}
}
func (this ZxSpectrumTap_ArrayParams) IO_() *kaitai.Stream {
return this._io
}
func (this *ZxSpectrumTap_ArrayParams) Read(io *kaitai.Stream, parent *ZxSpectrumTap_Header, root *ZxSpectrumTap) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp3, err := this._io.ReadU1()
if err != nil {
return err
}
this.Reserved = tmp3
tmp4, err := this._io.ReadU1()
if err != nil {
return err
}
this.VarName = tmp4
tmp5, err := this._io.ReadBytes(int(2))
if err != nil {
return err
}
tmp5 = tmp5
this.Reserved1 = tmp5
if !(bytes.Equal(this.Reserved1, []uint8{0, 128})) {
return kaitai.NewValidationNotEqualError([]uint8{0, 128}, this.Reserved1, this._io, "/types/array_params/seq/2")
}
return err
}
/**
* Variable name (1..26 meaning A$..Z$ +192)
*/
type ZxSpectrumTap_Block struct {
LenBlock uint16
Flag ZxSpectrumTap_FlagEnum
Header *ZxSpectrumTap_Header
Data []byte
HeaderlessData []byte
_io *kaitai.Stream
_root *ZxSpectrumTap
_parent *ZxSpectrumTap
}
func NewZxSpectrumTap_Block() *ZxSpectrumTap_Block {
return &ZxSpectrumTap_Block{
}
}
func (this ZxSpectrumTap_Block) IO_() *kaitai.Stream {
return this._io
}
func (this *ZxSpectrumTap_Block) Read(io *kaitai.Stream, parent *ZxSpectrumTap, root *ZxSpectrumTap) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp6, err := this._io.ReadU2le()
if err != nil {
return err
}
this.LenBlock = uint16(tmp6)
tmp7, err := this._io.ReadU1()
if err != nil {
return err
}
this.Flag = ZxSpectrumTap_FlagEnum(tmp7)
if ( ((this.LenBlock == 19) && (this.Flag == ZxSpectrumTap_FlagEnum__Header)) ) {
tmp8 := NewZxSpectrumTap_Header()
err = tmp8.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Header = tmp8
}
if (this.LenBlock == 19) {
tmp9, err := this._io.ReadBytes(int(this.Header.LenData + 4))
if err != nil {
return err
}
tmp9 = tmp9
this.Data = tmp9
}
if (this.Flag == ZxSpectrumTap_FlagEnum__Data) {
tmp10, err := this._io.ReadBytes(int(this.LenBlock - 1))
if err != nil {
return err
}
tmp10 = tmp10
this.HeaderlessData = tmp10
}
return err
}
type ZxSpectrumTap_BytesParams struct {
StartAddress uint16
Reserved []byte
_io *kaitai.Stream
_root *ZxSpectrumTap
_parent *ZxSpectrumTap_Header
}
func NewZxSpectrumTap_BytesParams() *ZxSpectrumTap_BytesParams {
return &ZxSpectrumTap_BytesParams{
}
}
func (this ZxSpectrumTap_BytesParams) IO_() *kaitai.Stream {
return this._io
}
func (this *ZxSpectrumTap_BytesParams) Read(io *kaitai.Stream, parent *ZxSpectrumTap_Header, root *ZxSpectrumTap) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp11, err := this._io.ReadU2le()
if err != nil {
return err
}
this.StartAddress = uint16(tmp11)
tmp12, err := this._io.ReadBytes(int(2))
if err != nil {
return err
}
tmp12 = tmp12
this.Reserved = tmp12
return err
}
type ZxSpectrumTap_Header struct {
HeaderType ZxSpectrumTap_HeaderTypeEnum
Filename []byte
LenData uint16
Params kaitai.Struct
Checksum uint8
_io *kaitai.Stream
_root *ZxSpectrumTap
_parent *ZxSpectrumTap_Block
}
func NewZxSpectrumTap_Header() *ZxSpectrumTap_Header {
return &ZxSpectrumTap_Header{
}
}
func (this ZxSpectrumTap_Header) IO_() *kaitai.Stream {
return this._io
}
func (this *ZxSpectrumTap_Header) Read(io *kaitai.Stream, parent *ZxSpectrumTap_Block, root *ZxSpectrumTap) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp13, err := this._io.ReadU1()
if err != nil {
return err
}
this.HeaderType = ZxSpectrumTap_HeaderTypeEnum(tmp13)
tmp14, err := this._io.ReadBytes(int(10))
if err != nil {
return err
}
tmp14 = kaitai.BytesStripRight(tmp14, 32)
this.Filename = tmp14
tmp15, err := this._io.ReadU2le()
if err != nil {
return err
}
this.LenData = uint16(tmp15)
switch (this.HeaderType) {
case ZxSpectrumTap_HeaderTypeEnum__Bytes:
tmp16 := NewZxSpectrumTap_BytesParams()
err = tmp16.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Params = tmp16
case ZxSpectrumTap_HeaderTypeEnum__CharArray:
tmp17 := NewZxSpectrumTap_ArrayParams()
err = tmp17.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Params = tmp17
case ZxSpectrumTap_HeaderTypeEnum__NumArray:
tmp18 := NewZxSpectrumTap_ArrayParams()
err = tmp18.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Params = tmp18
case ZxSpectrumTap_HeaderTypeEnum__Program:
tmp19 := NewZxSpectrumTap_ProgramParams()
err = tmp19.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Params = tmp19
}
tmp20, err := this._io.ReadU1()
if err != nil {
return err
}
this.Checksum = tmp20
return err
}
/**
* Bitwise XOR of all bytes including the flag byte
*/
type ZxSpectrumTap_ProgramParams struct {
AutostartLine uint16
LenProgram uint16
_io *kaitai.Stream
_root *ZxSpectrumTap
_parent *ZxSpectrumTap_Header
}
func NewZxSpectrumTap_ProgramParams() *ZxSpectrumTap_ProgramParams {
return &ZxSpectrumTap_ProgramParams{
}
}
func (this ZxSpectrumTap_ProgramParams) IO_() *kaitai.Stream {
return this._io
}
func (this *ZxSpectrumTap_ProgramParams) Read(io *kaitai.Stream, parent *ZxSpectrumTap_Header, root *ZxSpectrumTap) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp21, err := this._io.ReadU2le()
if err != nil {
return err
}
this.AutostartLine = uint16(tmp21)
tmp22, err := this._io.ReadU2le()
if err != nil {
return err
}
this.LenProgram = uint16(tmp22)
return err
}