/* id3v2.pk - ID3v2 implementation for GNU poke */

/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024 Jose E. Marchesi.  */

/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Implemented as specified in http://id3.org/id3v2.3.0  */

type ID3V2_Hdr =
  struct
  {
    uint<8>[3] head == ['I','D','3'];
   uint<8> ver_major : ver_major != 0xff;
   uint<8> ver_revision : ver_revision != 0xff;

    struct uint<8>
    {
      uint<1> unsynchronisation_used;
      uint<1> extended_hdr_present;
      uint<1> experimental_tag;
      uint<5>;
    } flags;

    uint<8>[4] size : (size[0] < 0x80
                    && size[1] < 0x80
                    && size[2] < 0x80
                    && size[3] < 0x80);

    method get_size = uint<28>:
      {
        /* The tag size is encoded with four bytes where the most
           significant bit is set to zero in every byte, making a
           total of 28 bits.  */
        return ((size[0] as uint<7>)
                :::(size[1] as uint<7>)
                :::(size[2] as uint<7>)
                :::(size[3] as uint<7>));
      }
  };

type ID3V2_Ext_Hdr =
  struct
  {
    /* Size of this header, excluding this field.  */
    uint<32> size : size == 10;

    struct uint<16>
    {
      uint<1> crc_present;
      uint<15>;
    } flags;

    uint<32> padding_sz;
    if (flags.crc_present)
      uint<32> crc;
  };

type ID3V2_Frame =
  struct
  {
    uint<8>[4] id : id[0] != 0;

    /* Frame size without frame header.  */
    uint<32> size;

    struct uint<16>
    {
      uint<1> tag_alter_preserv;
      uint<1> file_alter_preserv;
      uint<1> read_only_frame;
      uint<5>;
      uint<1> compressed_frame;
      uint<1> encrypted_frame;
      uint<1> group_member_frame;
      uint<5>;
    } flags;

    union
    {
      /* Frame contains text related data.  */
      union
      {
        struct
        {
          uint<8> id_asciiz_str = 0;
          uint<8>[size - 1] frame_data;
        } fd : size > 1;

        uint<8>[size] frame_data;
      } fd : id[0] == 'T';

      /* Frame contains other data.  */
      uint<8>[size] frame_data;
    } fd;
  };

type ID3V2_Tag =
  struct
  {
    ID3V2_Hdr hdr;
    if (hdr.flags.extended_hdr_present)
      ID3V2_Ext_Hdr ext_hdr;
    ID3V2_Frame[] frames;

    /* Padding.  */
    uint<8>[hdr.get_size - (frames'offset + (frames'size))/#B];
  };
