/*********************************************************************

    formats/td0_dsk.c

    TD0 disk images

*********************************************************************/
/*
 * Based on Japanese version 29-NOV-1988
 * LZSS coded by Haruhiko OKUMURA
 * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
 * Edited and translated to English by Kenji RIKITAKE
 */

#include <string.h>
#include "flopimg.h"

#define BUFSZ           512     // new input buffer

/* LZSS Parameters */

#define N        4096    /* Size of string buffer */
#define F        60    /* Size of look-ahead buffer */
#define THRESHOLD    2
#define NIL        N    /* End of tree's node  */


/* Huffman coding parameters */

#define N_CHAR      (256 - THRESHOLD + F)
                /* character code (= 0..N_CHAR-1) */
#define T         (N_CHAR * 2 - 1)    /* Size of table */
#define R         (T - 1)            /* root position */
#define MAX_FREQ    0x8000
                    /* update when cumulative frequency */
                    /* reaches to this value */

struct td0dsk_tag
{
	int heads;
	int tracks;
	int sector_size;
	UINT64 track_offsets[84*2]; /* offset within data for each track */
	UINT8 *data;
};

struct tdlzhuf {
	UINT16 r,
				   bufcnt,bufndx,bufpos,  // string buffer
			 // the following to allow block reads from input in next_word()
				   ibufcnt,ibufndx; // input buffer counters
	UINT8  inbuf[BUFSZ];    // input buffer
};


struct td0dsk_t
{
	floppy_image_legacy *floppy_file;
	UINT64 floppy_file_offset;

	struct tdlzhuf tdctl;
	UINT8 text_buf[N + F - 1];
	UINT16 freq[T + 1];    /* cumulative freq table */

/*
 * pointing parent nodes.
 * area [T..(T + N_CHAR - 1)] are pointers for leaves
 */
	INT16 prnt[T + N_CHAR];

	/* pointing children nodes (son[], son[] + 1)*/
	INT16 son[T];

	UINT16 getbuf;
	UINT8 getlen;

	int data_read(UINT8 *buf, UINT16 size);
	int next_word();
	int GetBit();
	int GetByte();
	void StartHuff();
	void reconst();
	void update(int c);
	INT16 DecodeChar();
	INT16 DecodePosition();
	void init_Decode();
	int Decode(UINT8 *buf, int len);
};

static td0dsk_t td0dsk;


static struct td0dsk_tag *get_tag(floppy_image_legacy *floppy)
{
	struct td0dsk_tag *tag;
	tag = (td0dsk_tag *)floppy_tag(floppy);
	return tag;
}



FLOPPY_IDENTIFY( td0_dsk_identify )
{
	UINT8 header[2];

	floppy_image_read(floppy, header, 0, 2);
	if (header[0]=='T' && header[1]=='D') {
		*vote = 100;
	} else if (header[0]=='t' && header[1]=='d') {
		*vote = 100;
	} else {
		*vote = 0;
	}
	return FLOPPY_ERROR_SUCCESS;
}

static int td0_get_heads_per_disk(floppy_image_legacy *floppy)
{
	return get_tag(floppy)->heads;
}

static int td0_get_tracks_per_disk(floppy_image_legacy *floppy)
{
	return get_tag(floppy)->tracks;
}

static UINT64 td0_get_track_offset(floppy_image_legacy *floppy, int head, int track)
{
	return get_tag(floppy)->track_offsets[(track<<1) + head];
}

static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset)
{
	UINT64 offs;
	UINT8 *header;
	UINT8 sectors_per_track;
	int i;

	if ((head < 0) || (head >= get_tag(floppy)->heads) || (track < 0) || (track >= get_tag(floppy)->tracks)
			|| (sector < 0) )
		return FLOPPY_ERROR_SEEKERROR;

	// position on beginning of track data
	offs = td0_get_track_offset(floppy, head, track);

	// read track header
	header = get_tag(floppy)->data + offs - 4;

	// take number of sectors per track
	sectors_per_track = header[0];

	if (!sector_is_index) {
		// when taking ID's return seek error if number is over counter
		if (sector > sectors_per_track) {
			return FLOPPY_ERROR_SEEKERROR;
		}
	}

	// move trought sectors
	for(i=0;i < sector-1;i++) {
		header = get_tag(floppy)->data + offs;
		offs+= 6;
		if ((header[4] & 0x30)==0) {
			offs+= 2;
			offs+= header[6] + (header[7]<<8);
		}
	}
	// read size of sector
	header = get_tag(floppy)->data + offs;
	get_tag(floppy)->sector_size = 1 << (header[3] + 7);

	if (offset)
		*offset = offs;
	return FLOPPY_ERROR_SUCCESS;
}



static floperr_t internal_td0_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen)
{
	UINT64 offset;
	floperr_t err;
	UINT8 *header;
	int size,realsize,i;
	int buff_pos;
	int data_pos;
	UINT8 *data;
	UINT8 *buf;

	buf = (UINT8*)buffer;
	// take sector offset
	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
	if (err)
		return err;

	// read sector header
	header = get_tag(floppy)->data + offset;
	offset+=6;
	// if there is no date just jump out
	if ((header[4] & 0x30)!=0) return FLOPPY_ERROR_SUCCESS;

	offset+=3;
	// take data size
	size = header[6] + (header[7]<<8)-1;
	// take real sector size
	realsize =  1 << (header[3] + 7);

	// read sector data
	data = get_tag(floppy)->data + offset;
	buff_pos = 0;
	data_pos = 0;

	switch(header[8]) {
		case 0:
				// encoding type 0
				//  - plain data
				memcpy(buffer,data,size);
				break;
		case 1:
				// encoding type 1
				//  - 2 bytes size
				//  - 2 bytes of data
				//  data is reapeted specified number of times
				while(buff_pos<realsize) {
					for (i=0;i<data[data_pos]+(data[data_pos+1] << 8);i++) {
						buf[buff_pos] = data[data_pos+2];buff_pos++;
						buf[buff_pos] = data[data_pos+3];buff_pos++;
					}
					data_pos+=4;
				}
				break;
		case 2:
				// encoding type 2
				//  - if first byte is zero next byte represent size of
				//      plain data after it
				//  - if different then zero when multiply by 2 represent
				//      size of data that should be reapeted next byte times
				while(buff_pos<realsize) {
					if (data[data_pos]==0x00) {
						int size_ = data[data_pos+1];
						memcpy(buf+buff_pos,data + data_pos + 2,size_);
						data_pos += 2 + size_;
						buff_pos += size_;
					} else {
						int size_  = 2*data[data_pos];
						int repeat = data[data_pos+1];
						data_pos+=2;

						for (i=0;i<repeat;i++) {
							memcpy(buf + buff_pos,data + data_pos,size_);
							buff_pos += size_;
						}
						data_pos += size_;
					}
				}
				break;
		default:
				return FLOPPY_ERROR_INTERNAL;
	}
	return FLOPPY_ERROR_SUCCESS;
}


static floperr_t td0_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
{
	return internal_td0_read_sector(floppy, head, track, sector, FALSE, buffer, buflen);
}

static floperr_t td0_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
{
	return internal_td0_read_sector(floppy, head, track, sector, TRUE, buffer, buflen);
}

static floperr_t td0_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, UINT32 *sector_length)
{
	floperr_t err;
	err = get_offset(floppy, head, track, sector, FALSE, NULL);
	if (err)
		return err;

	if (sector_length) {
		*sector_length = get_tag(floppy)->sector_size;
	}
	return FLOPPY_ERROR_SUCCESS;
}

static floperr_t td0_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags)
{
	floperr_t retVal;
	UINT64 offset;
	UINT8 *sector_info;

	retVal = get_offset(floppy, head, track, sector_index, FALSE, &offset);
	sector_info = get_tag(floppy)->data + offset;
	if (cylinder)
		*cylinder = sector_info[0];
	if (side)
		*side = sector_info[1];
	if (sector)
		*sector = sector_info[2];
	if (sector_length) {
		*sector_length = 1 << (sector_info[3] + 7);
	}
	if (flags) {
		*flags = 0;
		if (sector_info[4] & 0x02) *flags |= ID_FLAG_CRC_ERROR_IN_DATA_FIELD;
		if (sector_info[4] & 0x04) *flags |= ID_FLAG_DELETED_DATA;
	}

	return retVal;
}

int td0dsk_t::data_read(UINT8 *buf, UINT16 size)
{
	if (floppy_file_offset + size > floppy_image_size(floppy_file) ) {
		size = floppy_image_size(floppy_file) - floppy_file_offset;
	}
	floppy_image_read(floppy_file,buf,floppy_file_offset,size);
	floppy_file_offset += size;
	return size;
}


/*
 * Tables for encoding/decoding upper 6 bits of
 * sliding dictionary pointer
 */

/* decoder table */
static const UINT8 d_code[256] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
    0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
    0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
    0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
    0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
    0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
    0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
    0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
    0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
    0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
    0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
    0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
    0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
    0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
    0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
};

static const UINT8 d_len[256] = {
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};

int td0dsk_t::next_word()
{
    if(tdctl.ibufndx >= tdctl.ibufcnt)
    {
        tdctl.ibufndx = 0;
        tdctl.ibufcnt = data_read(tdctl.inbuf,BUFSZ);
        if(tdctl.ibufcnt <= 0)
            return(-1);
    }
    while (getlen <= 8) { // typically reads a word at a time
        getbuf |= tdctl.inbuf[tdctl.ibufndx++] << (8 - getlen);
        getlen += 8;
    }
    return(0);
}


int td0dsk_t::GetBit()    /* get one bit */
{
    INT16 i;
    if(next_word() < 0)
        return(-1);
    i = getbuf;
    getbuf <<= 1;
    getlen--;
        if(i < 0)
        return(1);
    else
        return(0);
}

int td0dsk_t::GetByte()    /* get a byte */
{
    UINT16 i;
    if(next_word() != 0)
        return(-1);
    i = getbuf;
    getbuf <<= 8;
    getlen -= 8;
    i = i >> 8;
    return((int) i);
}



/* initialize freq tree */

void td0dsk_t::StartHuff()
{
    int i, j;

    for (i = 0; i < N_CHAR; i++) {
        freq[i] = 1;
        son[i] = i + T;
        prnt[i + T] = i;
    }
    i = 0; j = N_CHAR;
    while (j <= R) {
        freq[j] = freq[i] + freq[i + 1];
        son[j] = i;
        prnt[i] = prnt[i + 1] = j;
        i += 2; j++;
    }
    freq[T] = 0xffff;
    prnt[R] = 0;
}


/* reconstruct freq tree */

void td0dsk_t::reconst()
{
    INT16 i, j, k;
    UINT16 f, l;

    /* halven cumulative freq for leaf nodes */
    j = 0;
    for (i = 0; i < T; i++) {
        if (son[i] >= T) {
            freq[j] = (freq[i] + 1) / 2;
            son[j] = son[i];
            j++;
        }
    }
    /* make a tree : first, connect children nodes */
    for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
        k = i + 1;
        f = freq[j] = freq[i] + freq[k];
        for (k = j - 1; f < freq[k]; k--);
        k++;
        l = (j - k) * 2;

        /* movmem() is Turbo-C dependent
           rewritten to memmove() by Kenji */

        /* movmem(&freq[k], &freq[k + 1], l); */
        (void)memmove(&freq[k + 1], &freq[k], l);
        freq[k] = f;
        /* movmem(&son[k], &son[k + 1], l); */
        (void)memmove(&son[k + 1], &son[k], l);
        son[k] = i;
    }
    /* connect parent nodes */
    for (i = 0; i < T; i++) {
        if ((k = son[i]) >= T) {
            prnt[k] = i;
        } else {
            prnt[k] = prnt[k + 1] = i;
        }
    }
}


/* update freq tree */

void td0dsk_t::update(int c)
{
    int i, j, k, l;

    if (freq[R] == MAX_FREQ) {
        reconst();
    }
    c = prnt[c + T];
    do {
        k = ++freq[c];

        /* swap nodes to keep the tree freq-ordered */
        if (k > freq[l = c + 1]) {
            while (k > freq[++l]);
            l--;
            freq[c] = freq[l];
            freq[l] = k;

            i = son[c];
            prnt[i] = l;
            if (i < T) prnt[i + 1] = l;

            j = son[l];
            son[l] = i;

            prnt[j] = c;
            if (j < T) prnt[j + 1] = c;
            son[c] = j;

            c = l;
        }
    } while ((c = prnt[c]) != 0);    /* do it until reaching the root */
}


INT16 td0dsk_t::DecodeChar()
{
    int ret;
    UINT16 c;

    c = son[R];

    /*
     * start searching tree from the root to leaves.
     * choose node #(son[]) if input bit == 0
     * else choose #(son[]+1) (input bit == 1)
     */
    while (c < T) {
        if((ret = GetBit()) < 0)
            return(-1);
        c += (unsigned) ret;
        c = son[c];
    }
    c -= T;
    update(c);
    return c;
}

INT16 td0dsk_t::DecodePosition()
{
    INT16 bit;
    UINT16 i, j, c;

    /* decode upper 6 bits from given table */
    if((bit=GetByte()) < 0)
        return(-1);
    i = (UINT16) bit;
    c = (UINT16)d_code[i] << 6;
    j = d_len[i];

    /* input lower 6 bits directly */
    j -= 2;
    while (j--) {
        if((bit = GetBit()) < 0)
            return(-1);
        i = (i << 1) + bit;
    }
    return(c | (i & 0x3f));
}

/* DeCompression

split out initialization code to init_Decode()

*/

void td0dsk_t::init_Decode()
{
    int i;
    getbuf = 0;
    getlen = 0;
    tdctl.ibufcnt= tdctl.ibufndx = 0; // input buffer is empty
    tdctl.bufcnt = 0;
    StartHuff();
    for (i = 0; i < N - F; i++)
        text_buf[i] = ' ';
    tdctl.r = N - F;
}


int td0dsk_t::Decode(UINT8 *buf, int len)  /* Decoding/Uncompressing */
{
    INT16 c,pos;
    int  count;  // was an unsigned long, seems unnecessary
    for (count = 0; count < len; ) {
            if(tdctl.bufcnt == 0) {
                if((c = DecodeChar()) < 0)
                    return(count); // fatal error
                if (c < 256) {
                    *(buf++) = c;
                    text_buf[tdctl.r++] = c;
                    tdctl.r &= (N - 1);
                    count++;
                }
                else {
                    if((pos = DecodePosition()) < 0)
                           return(count); // fatal error
                    tdctl.bufpos = (tdctl.r - pos - 1) & (N - 1);
                    tdctl.bufcnt = c - 255 + THRESHOLD;
                    tdctl.bufndx = 0;
                }
            }
            else { // still chars from last string
                while( tdctl.bufndx < tdctl.bufcnt && count < len ) {
                    c = text_buf[(tdctl.bufpos + tdctl.bufndx) & (N - 1)];
                    *(buf++) = c;
                    tdctl.bufndx++;
                    text_buf[tdctl.r++] = c;
                    tdctl.r &= (N - 1);
                    count++;
                }
                // reset bufcnt after copy string from text_buf[]
                if(tdctl.bufndx >= tdctl.bufcnt)
                    tdctl.bufndx = tdctl.bufcnt = 0;
            }
    }
    return(count); // count == len, success
}


FLOPPY_CONSTRUCT( td0_dsk_construct )
{
	td0dsk_t *state = &td0dsk;
	struct FloppyCallbacks *callbacks;
	struct td0dsk_tag *tag;
	UINT8 *header;
	int number_of_sectors;
	int position;
	int i;
	int track;

	if(params)
	{
		// create
		return FLOPPY_ERROR_UNSUPPORTED;
	}

	tag = (struct td0dsk_tag *) floppy_create_tag(floppy, sizeof(struct td0dsk_tag));
	if (!tag)
		return FLOPPY_ERROR_OUTOFMEMORY;

	tag->data = (UINT8*)malloc(floppy_image_size(floppy));
	if (tag->data==NULL) {
		return FLOPPY_ERROR_OUTOFMEMORY;
	}
	floppy_image_read(floppy, tag->data, 0, floppy_image_size(floppy));
	header = tag->data;

	if (header[0]=='t') {
		UINT8 obuf[BUFSZ];
		int rd;
		int off = 12;
		int size = 0;
		state->floppy_file = floppy;
		state->init_Decode();
		state->floppy_file_offset = 12;
		do
		{
			if((rd = state->Decode(obuf, BUFSZ)) > 0) size += rd;
		} while(rd == BUFSZ);
		memcpy(obuf,tag->data,12);
		free(tag->data);
		tag->data = (UINT8*)malloc(size+12);
		if (tag->data==NULL) {
			return FLOPPY_ERROR_OUTOFMEMORY;
		}
		memcpy(tag->data,obuf,12);
		state->floppy_file_offset = 12;
		state->init_Decode();
		do
		{
			if((rd = state->Decode(obuf, BUFSZ)) > 0) {
				memcpy(tag->data+off,obuf,rd);
				off += rd;
			}
		}  while(rd == BUFSZ);
	}
	header = tag->data;
	tag->heads   = header[9];
	if (tag->heads > 1) {
		tag->heads  = 2;
	}

	//  header len + comment header + comment len
	position = 12;
	if (header[7] & 0x80) {
		position += 10 + header[14] + (header[15]<<8);
	}
	tag->tracks = 0;
	do {
		// read track header
		header = tag->data + position;
		track = header[1];
		number_of_sectors = header[0];
		if (number_of_sectors!=0xff){
			position+=4;
			tag->track_offsets[(track<<1) + (header[2] & 1)] = position;
			for(i=0;i<number_of_sectors;i++) {
				// read sector header
				header = tag->data + position;
				position+=6;
				// read sector size
				if ((header[4] & 0x30)==0) {
					// if there is sector data
					header = tag->data + position;
					position+=2;
					// skip sector data
					position+= header[0] + (header[1]<<8);
				}
			}
			tag->tracks++;
		}
	} while(number_of_sectors!=0xff);
	tag->tracks++;

	callbacks = floppy_callbacks(floppy);
	callbacks->read_sector = td0_read_sector;
	callbacks->read_indexed_sector = td0_read_indexed_sector;
	callbacks->get_sector_length = td0_get_sector_length;
	callbacks->get_heads_per_disk = td0_get_heads_per_disk;
	callbacks->get_tracks_per_disk = td0_get_tracks_per_disk;
	callbacks->get_indexed_sector_info = td0_get_indexed_sector_info;
	return FLOPPY_ERROR_SUCCESS;
}

FLOPPY_DESTRUCT( td0_dsk_destruct )
{
	struct td0dsk_tag *tag = get_tag(floppy);
	free(tag->data);
	tag->data = NULL;
	return FLOPPY_ERROR_SUCCESS;
}
