/*
 * disk/ruffian.c
 *
 * Custom format as used by Ruffin from Xplosive Software
 *
 * Written in 2022 by Keith Krellwitz
 *
 * RAW TRACK LAYOUT:
 *  u16 0x8914 ::  Sync
 *  u32 0x5554aaaa :: Sig
 *  u16 0xaaaa, 0x92aa, or 0xa4aa :: Disk Identifier
 *  u32 dat[5632/4] :: Data
 *  u16 0x2a54 or 0xaa54 :: Padding
 *  u32 0xaaaa4489 :: Sig/End Sync
 * 
 * There is no checksum of any kind (all undecoded track content is
 * gap filler).
 */

#include <libdisk/util.h>
#include <private/disk.h>

static const uint16_t crcs[];
static const uint16_t disk_ids[] = {0xaaaa, 0x92aa, 0xa4aa};

static const uint16_t get_disk_number(uint16_t id)
{
    unsigned int i;
    for (i = 0; i < ARRAY_SIZE(disk_ids); i++)
        if (disk_ids[i] == id)
            return (uint16_t)i;
    return 0xffff;
}

static void *ruffian_write_raw(
    struct disk *d, unsigned int tracknr, struct stream *s)
{
    struct track_info *ti = &d->di->track[tracknr];

    while (stream_next_bit(s) != -1) {

        uint32_t raw[2], dat[ti->len/4+1];
        uint16_t disknr, padding;
        unsigned int i;
        char *block;

        /* sync */
        if ((uint16_t)s->word != 0x8914)
            continue;
        ti->data_bitoff = s->index_offset_bc - 15;

        /* sig */
        if (stream_next_bits(s, 32) == -1)
            goto fail;
        if (s->word != 0x5554aaaa)
            continue;

        /* disk identifier */
        if (stream_next_bits(s, 16) == -1)
            goto fail;
        if ((uint16_t)s->word != 0xaaaa && (uint16_t)s->word != 0x92aa \
            && (uint16_t)s->word != 0xa4aa)
            continue;
        disknr = get_disk_number((uint16_t)s->word);
    
        /* data */
        stream_start_crc(s);
        for (i = 0; i < ti->len/4; i++) {
            if (stream_next_bytes(s, raw, 8) == -1)
                goto fail;
            mfm_decode_bytes(bc_mfm_even_odd, 4, raw, &dat[i]);
        }

        /* Check stored CRC with the calculated */
        if (s->crc16_ccitt != crcs[disknr*160+tracknr])
            continue;

        /* padding */
        if (stream_next_bits(s, 16) == -1)
            goto fail;
        padding = (uint16_t)s->word;
        dat[ti->len/4] = (disknr << 16) | padding;

        /* padding sync */
        if (stream_next_bits(s, 32) == -1)
            goto fail;
        if (s->word != 0xaaaa4489)
            continue;

        block = memalloc(ti->len+4);
        memcpy(block, dat, ti->len+4);
        set_all_sectors_valid(ti);
        return block;
    }

fail:
    return NULL;
}

static void ruffian_read_raw(
    struct disk *d, unsigned int tracknr, struct tbuf *tbuf)
{
    struct track_info *ti = &d->di->track[tracknr];
    uint32_t *dat = (uint32_t *)ti->dat;
    uint16_t csum;
    unsigned int i;

    /* sync */
    tbuf_bits(tbuf, SPEED_AVG, bc_raw, 16, 0x8914);

    /* sig */
    tbuf_bits(tbuf, SPEED_AVG, bc_raw, 32, 0x5554aaaa);

    /* disk identifier */
    tbuf_bits(tbuf, SPEED_AVG, bc_raw, 16, disk_ids[dat[ti->len/4] >> 16]);

    /* data */
    for (i = csum = 0; i < ti->len/4; i++)
        tbuf_bits(tbuf, SPEED_AVG, bc_mfm_even_odd, 32, be32toh(dat[i]));

    /* padding */
    tbuf_bits(tbuf, SPEED_AVG, bc_raw, 16, (uint16_t)dat[ti->len/4]);

    /* padding sync */
    tbuf_bits(tbuf, SPEED_AVG, bc_raw, 32, 0xaaaa4489);
}

struct track_handler ruffian_handler = {
    .bytes_per_sector = 5632,
    .nr_sectors = 1,
    .write_raw = ruffian_write_raw,
    .read_raw = ruffian_read_raw
};

static const uint16_t crcs[] = {
    /* Disk 1 */
    0x0000, 0x26f0, 0x376b, 0x177a, 0xeed0, 0x0efd, 0x67da, 0xe4c2,
    0x31d4, 0x7326, 0x81ab, 0x456d, 0xf790, 0x5932, 0xc4cd, 0xa9d1,
    0x3ffb, 0x067c, 0x05b1, 0x18a0, 0x38f4, 0x34da, 0x6def, 0xb54e,
    0x93d9, 0x379f, 0x5e24, 0xe94a, 0x3d7a, 0xf70b, 0x2887, 0x179a,
    0x53d1, 0x9e2d, 0x1795, 0xad0b, 0xf973, 0xe88b, 0xb595, 0x3295,
    0x2be1, 0xce64, 0xdc2c, 0x995d, 0x1573, 0x014d, 0xb4f8, 0x6a8d,
    0x7bd6, 0xb8fd, 0xc067, 0x8397, 0x6659, 0xa0a3, 0xf811, 0xc966,
    0xbb87, 0xfcd4, 0x5092, 0xe50e, 0x17bb, 0xf344, 0x40f1, 0x1c0d,
    0xcca8, 0x9c7f, 0xcea7, 0x4d61, 0xe4fc, 0xc576, 0xde11, 0xf7dd,
    0x03e0, 0x42a2, 0x716f, 0x831b, 0x0ab1, 0x9184, 0xc52f, 0x0926,
    0x2950, 0x6659, 0x6e48, 0x4a41, 0x2b2b, 0xd869, 0xc6ef, 0x0739,
    0x9a79, 0x97db, 0x001c, 0x15fc, 0x0d55, 0x7a06, 0x74ed, 0x1ed7,
    0xcd55, 0x190a, 0xab8f, 0x1b4d, 0x35a9, 0x97ae, 0xd368, 0x20db,
    0x2f49, 0xb3b8, 0x3fe8, 0x31eb, 0x4464, 0x44f4, 0x09f8, 0x9371,
    0xe707, 0x682c, 0x6db2, 0x1234, 0xb478, 0xf6d6, 0x5fe2, 0x3955,
    0x54ca, 0xcd9a, 0xbcbe, 0xba09, 0x67a0, 0xd3a7, 0xabe1, 0x0cbd,
    0xcdb6, 0xc317, 0x4c0e, 0xbbc6, 0x076f, 0x7d96, 0xc25a, 0x8d3c,
    0x47c5, 0xcd02, 0xd38d, 0xd89d, 0xf2cd, 0x9bf4, 0x36bd, 0xbfdf,
    0x40c9, 0x89bf, 0x0127, 0x3745, 0xfa49, 0x3d0b, 0xf993, 0xad8d,
    0xfc0f, 0x04a7, 0xbbf0, 0xd2dd, 0x0171, 0xceca, 0x3561, 0xd3ac,
    /* Disk 2 */
    0xf95d, 0x3b28, 0x4147, 0x1a74, 0x7b23, 0x4bc7, 0x3fc2, 0x1b94,
    0x961f, 0x3854, 0xc10d, 0xa77f, 0xa4ed, 0x878d, 0x3fd1, 0x4a87,
    0x4d39, 0xaf46, 0xd9d7, 0x5942, 0x5de1, 0x5ee7, 0xd9ce, 0xffc1,
    0xd702, 0xc989, 0xf76b, 0x4405, 0xa914, 0xbf16, 0x7c6c, 0x3566,
    0x1dde, 0x84ad, 0x21da, 0x34f5, 0xc555, 0x6ed2, 0xfefa, 0x9003,
    0xf15c, 0x1c5a, 0xa16d, 0xb506, 0x5dc0, 0x0417, 0x79b0, 0xf81a,
    0xc525, 0x52a2, 0xbcb0, 0xe63c, 0xbebb, 0x7e33, 0xdb72, 0xf021,
    0x3d10, 0x6b6b, 0x2ce8, 0x15d5, 0xb89a, 0x83f1, 0x516a, 0x05f6,
    0x4336, 0x4206, 0x2570, 0x9468, 0x5a2e, 0x5e11, 0x0a03, 0xf0e9,
    0x6fc5, 0x6ee7, 0xb9e7, 0xa9e8, 0xe83d, 0x23b0, 0x0b8c, 0x6e49,
    0x1cbe, 0xe3d7, 0x60cb, 0xa02b, 0x946e, 0x9e2e, 0x9fdd, 0xc2fc,
    0x89eb, 0xa172, 0xaafe, 0xcdfb, 0x6845, 0x7834, 0x3311, 0xa788,
    0xa788, 0xa788, 0xaf4d, 0x6cad, 0x6274, 0x776d, 0x723c, 0xcb0f,
    0x556e, 0x61eb, 0x0189, 0x82e5, 0xfae3, 0x11cd, 0xc45c, 0x6b41,
    0x96d9, 0x5f13, 0xb34c, 0xe2ad, 0xddfc, 0xf405, 0xd466, 0xc731,
    0x75f5, 0xcb06, 0x3791, 0x9b6f, 0xb3ff, 0x0d09, 0x3b51, 0x0e66,
    0xec0d, 0x365c, 0x4ba2, 0x185f, 0xcc0d, 0xf832, 0x95bf, 0x1cf1,
    0x6d53, 0x6a58, 0x69d9, 0x2357, 0xe89b, 0x2b5a, 0xcb17, 0x3454,
    0x5bbd, 0x70d8, 0x083f, 0xd2c5, 0xfac7, 0xeed5, 0xb82b, 0x1a66,
    0x7539, 0x0312, 0x6db4, 0x9469, 0xc3a8, 0x153c, 0xa788, 0x3d4b,
    /* Disk 3 */
    0xa821, 0xa8a6, 0x5cbd, 0x9a63, 0xab13, 0x0524, 0x5004, 0x2ef5,
    0x25b8, 0x8882, 0x666a, 0xa07b, 0x1a1c, 0x203d, 0xec77, 0x11a3,
    0x3b70, 0xe1b6, 0x0364, 0x426e, 0xe27b, 0x276f, 0x38b4, 0x71e4,
    0x4a9d, 0x2d48, 0xcdf5, 0x278f, 0xd695, 0xaf3e, 0x08e5, 0xc639,
    0xffd9, 0x317f, 0x19e9, 0x82db, 0xea7d, 0xfa14, 0x3d0c, 0x1b7e,
    0x2dda, 0xfcfb, 0xa212, 0xfdcb, 0x1f0a, 0x089b, 0x76f6, 0xae9e,
    0x383a, 0x45e7, 0x1595, 0x5a8e, 0x492b, 0xbb89, 0x6cb1, 0x91e4,
    0x1420, 0x29eb, 0xe3b3, 0x01cc, 0x50b8, 0xe23f, 0x7b20, 0x8517,
    0x4827, 0xaf2e, 0xc0c8, 0x3049, 0xe752, 0x0ba6, 0x7c7d, 0x1f84,
    0xbf59, 0xbac8, 0xe051, 0xbfc0, 0xcbf4, 0xe5d8, 0x8ea1, 0x7522,
    0x5f6c, 0x27d0, 0x04e6, 0xeb91, 0x53a3, 0x2575, 0x7697, 0xa29f,
    0x22dc, 0x3254, 0x6bbf, 0xbdfe, 0xe86e, 0x13eb, 0xa6cd, 0x8ea1,
    0x8ea1, 0x8ea1, 0x8ea1, 0xf5ab, 0x7a33, 0x0864, 0x2c8e, 0xce1a,
    0xd8f6, 0x2153, 0xaf6f, 0xa4db, 0xdf5c, 0x32b6, 0x495c, 0x09b4,
    0xe790, 0x4411, 0x0a09, 0xeb07, 0x3151, 0xed8a, 0x6c21, 0x9b30,
    0x58c5, 0xcc93, 0x1e24, 0x34db, 0x96de, 0x5a7e, 0x4788, 0xbf55,
    0x6d6a, 0xf551, 0xe619, 0x032e, 0xac70, 0x8200, 0x992e, 0xa5a0,
    0x3b18, 0xa386, 0x65d4, 0xe128, 0x135d, 0x1e4e, 0x0b05, 0x335a,
    0xa5ef, 0x9333, 0xee4d, 0xbe4e, 0x3415, 0x7604, 0xbcbb, 0xb759,
    0x7cc6, 0xaf01, 0x98f5, 0x5299, 0x2282, 0xfa4b, 0x0c99, 0xd8a0
};

/*
 * Local variables:
 * mode: C
 * c-file-style: "Linux"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */
