Search code examples
kaitai-struct

kaitai instance value ternary: can't combine output types


I've created a Kaitai Struct .ksy for two very similar Digilent log file formats. The second format (openlogger) is an extension of the first (openscope) with two additional fields in the struct. The scope is basically a single-channel logger; the additional logger fields describe the number of active channels (a u1, max 8) and the channel to sample order map (a u1 x 8).

I'm attempting to harmonise the interface for the two formats by synthesising always-present fields for the num_channels and channel_map; this has worked fine for the num_channels instance. However I can't figure out how to create a suitable value for the channel map, the .ksy below reports an error: /types/body/types/header/instances/channel_order/value: can't combine output types: ArrayType(Int1Type(false)) vs CalcBytesType

I can't figure out how I can represent the if_false part ([0]) as an ArrayType.

Is there a better way to approach this?

meta:
  id: dlog
  file-extension: dlog
seq:
  - id: endianness
    type: u1
    doc: 0 - little endian 1 - big endian
  - id: body
    type: body
types:
  body:
    meta:
      endian:
        switch-on: _root.endianness
        cases:
          0: le
          1: be
    seq:
      - id: header
        type: header
    instances:
      data:
        pos: header.start_of_data
        type: data
    types:
      header:
        seq:
        - id: sample_size
          type: u1
        - id: header_size
          type: u2
        - id: start_of_data
          type: u2
        - id: dlog_format
          type: u2
          enum: dlog_formats
        - id: dlog_version
          type: u4

        - id: voltage_units
          type: u8
        - id: stop_reason
          type: u4
          enum: stop_reasons
          #...

        - id: num_openlogger_channels
          type: u4
          if: dlog_format == dlog_formats::openlogger
          doc: number of channels per sample
        - id: openlogger_channel_map
          type: u1
          repeat: expr
          repeat-expr: 8
          if: dlog_format == dlog_formats::openlogger
          doc: channel order

        instances:
          num_channels:
            value: 'dlog_format == dlog_formats::openlogger ? num_openlogger_channels : 1'
          channel_map:
            value: 'dlog_format == dlog_formats::openlogger ? openlogger_channel_map : [0]'

      data:
        seq:
        - id: samples
          type: sample
          repeat: eos
        types:
          sample:
            seq:
              - id: channel
                type:
                  switch-on: _root.body.header.sample_size
                  cases:
                    1: s1
                    2: s2
                    4: s4
                repeat: expr
                repeat-expr: _root.body.header.num_channels

enums:
  dlog_formats:
    1: openscope
    3: openlogger
  stop_reasons:
    0: normal
    1: forced
    2: error
    3: overflow
    4: unknown

Solution

  • Literal [0] gets parsed as byte array: this is default behavior that people typically depend on, so heuristic array literal parser treats all arrays where values fit the pattern of being 0..255 as byte arrays, not true arrays.

    You can still do a true array literal if you want to by forcing it so with a typecast:

    [0].as<u1[]>
    

    Note that it will likely cause problems with C++98, which lacks one-line initializers for true arrays (std::vector).