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

    formats/oric_dsk.c

    Oric disk images

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

#include <string.h>

#include "imageutl.h"
#include "flopimg.h"
#include "oric_dsk.h"
#include "basicdsk.h"


#define mfm_disk_header_size	0x0100
#define MFM_ID	"MFM_DISK"

#define TRACK_SIZE_MFM 0x1900

struct mfm_disk_sector_info
{
	int id_ptr;
	int data_ptr;
	int sector_size;
	UINT8 ddam;
};

struct oricdsk_tag
{
	int tracks;
	int heads;
	int geometry;
	int tracksize;
	int num_sectors;
	struct mfm_disk_sector_info	sector_data[32];
};


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


static FLOPPY_IDENTIFY(oric_dsk_identify)
{
	UINT8 header[mfm_disk_header_size];

	floppy_image_read(floppy, header, 0, mfm_disk_header_size);
	if ( memcmp( header, MFM_ID, 8 ) ==0) {
		int heads  = pick_integer_le(header, 8, 4);
		int tracks = pick_integer_le(header, 12, 4);

		if (floppy_image_size(floppy)==((tracks*heads*TRACK_SIZE_MFM)+mfm_disk_header_size)) {
			*vote = 100;
		} else {
			*vote = 0;
		}
	} else {
		*vote = 0;
	}
	return FLOPPY_ERROR_SUCCESS;
}
static int oric_get_track_offset(floppy_image_legacy *floppy,int track, int head)
{
	if (get_tag(floppy)->geometry==1) {
		return mfm_disk_header_size + (get_tag(floppy)->tracksize * track) + (head * get_tag(floppy)->tracksize * get_tag(floppy)->tracks);
	} else {
		return mfm_disk_header_size + (get_tag(floppy)->tracksize*((track * get_tag(floppy)->heads)+head));
	}
}

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

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

static void mfm_info_cache_sector_info(floppy_image_legacy *floppy,int track,int head)
{
	UINT8 track_data[TRACK_SIZE_MFM];

	/* initialise these with single density values if single density */
	UINT8 IdMark = 0x0fe;
	UINT8 DataMark = 0x0fb;
	UINT8 DeletedDataMark = 0x0f8;

	UINT8 SectorCount;
	UINT8 SearchCode = 0;
	UINT8 sector_number = 0;
	int ptr = 0;
	int track_offset = oric_get_track_offset(floppy,track,head);
	floppy_image_read(floppy, track_data, track_offset, TRACK_SIZE_MFM);
	SectorCount = 0;

	do
	{
		switch (SearchCode)
		{
			/* searching for id's */
			case 0:
			{
				/* found id mark? */
				if (track_data[ptr] == IdMark)
				{
					sector_number  = track_data[ptr+3]-1;
					/* store pointer to id mark */
					get_tag(floppy)->sector_data[sector_number].id_ptr = ptr + track_offset;
					SectorCount++;

					/* grab N value - used to skip data in data field */
					get_tag(floppy)->sector_data[sector_number].sector_size = (1<< (track_data[ptr+4]+7));

					/* skip past id field and crc */
					ptr+=7;

					/* now looking for data field */
					SearchCode = 1;
				}
				else
				{
					/* update position */
					ptr++;
				}
			}
			break;

			/* searching for data id's */
			case 1:
			{
				/* found data or deleted data? */
				if ((track_data[ptr] == DataMark) || (track_data[ptr] == DeletedDataMark))
				{
					/* yes */
					get_tag(floppy)->sector_data[sector_number].data_ptr = ptr + track_offset + 1;
					get_tag(floppy)->sector_data[sector_number].ddam = (track_data[ptr] == DeletedDataMark) ? ID_FLAG_DELETED_DATA : 0;

					/* skip data field and id */
					ptr += get_tag(floppy)->sector_data[sector_number].sector_size + 3;

					/* now looking for id field */
					SearchCode = 0;
				}
				else
				{
					ptr++;
				}
			}
			break;

			default:
				break;
		}
	}
	while (ptr < TRACK_SIZE_MFM);
	get_tag(floppy)->num_sectors = SectorCount;

}

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

	/* translate the sector to a raw sector */
	if (!sector_is_index)
	{
		sector -= 1;
	}
	mfm_info_cache_sector_info(floppy,track,head);

	/* check to see if we are out of range */
	if ((head < 0) || (head >= get_tag(floppy)->heads) || (track < 0) || (track >= get_tag(floppy)->tracks)
			|| (sector < 0) || (sector >=get_tag(floppy)->num_sectors))
		return FLOPPY_ERROR_SEEKERROR;

	offs = get_tag(floppy)->sector_data[sector].data_ptr;
	if (offset)
		*offset = offs;
	return FLOPPY_ERROR_SUCCESS;
}



static floperr_t internal_oric_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;
	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
	if (err)
		return err;
	floppy_image_read(floppy, buffer, offset, buflen);
	return FLOPPY_ERROR_SUCCESS;
}



static floperr_t internal_oric_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam)
{
	UINT64 offset;
	floperr_t err;

	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
	if (err)
		return err;

	floppy_image_write(floppy, buffer, offset, buflen);
	return FLOPPY_ERROR_SUCCESS;
}



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

static floperr_t oric_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
{
	return internal_oric_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam);
}

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

static floperr_t oric_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
{
	return internal_oric_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam);
}

static floperr_t oric_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_data[sector].sector_size;
	}
	return FLOPPY_ERROR_SUCCESS;
}



static floperr_t oric_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;

	sector_index += 1;

	retVal = oric_get_sector_length(floppy, head, track, sector_index, sector_length);
	if (sector_length!=NULL) {
		*sector_length =  get_tag(floppy)->sector_data[sector_index-1].sector_size;
	}
	if (cylinder)
		*cylinder = track;
	if (side)
		*side = head;
	if (sector)
		*sector = sector_index;
	if (flags)
		*flags = get_tag(floppy)->sector_data[sector_index].ddam;
	return retVal;
}


static FLOPPY_CONSTRUCT(oric_dsk_construct)
{
	struct FloppyCallbacks *callbacks;
	struct oricdsk_tag *tag;
	UINT8 header[mfm_disk_header_size];

	floppy_image_read(floppy, header, 0, mfm_disk_header_size);

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

	tag->heads   = pick_integer_le(header, 8, 4);
	tag->tracks  = pick_integer_le(header, 12, 4);
	tag->geometry = pick_integer_le(header, 16, 4);
	tag->tracksize = TRACK_SIZE_MFM;
	memset(tag->sector_data,0,sizeof(tag->sector_data));

	callbacks = floppy_callbacks(floppy);
	callbacks->read_sector = oric_read_sector;
	callbacks->write_sector = oric_write_sector;
	callbacks->read_indexed_sector = oric_read_indexed_sector;
	callbacks->write_indexed_sector = oric_write_indexed_sector;
	callbacks->get_sector_length = oric_get_sector_length;
	callbacks->get_heads_per_disk = oric_get_heads_per_disk;
	callbacks->get_tracks_per_disk = oric_get_tracks_per_disk;
	callbacks->get_indexed_sector_info = oric_get_indexed_sector_info;
	return FLOPPY_ERROR_SUCCESS;
}
/* ----------------------------------------------------------------------- */

LEGACY_FLOPPY_OPTIONS_START( oric )
	LEGACY_FLOPPY_OPTION( oricmfm, "dsk", "Oric MFM floppy disk image",	oric_dsk_identify, oric_dsk_construct, NULL, NULL)
	LEGACY_FLOPPY_OPTION( oric, "dsk", "Oric disk image", basicdsk_identify_default, basicdsk_construct_default, NULL,
		HEADS([2])
		TRACKS([80])
		SECTORS([9])
		SECTOR_LENGTH([512])
		FIRST_SECTOR_ID([1]))
LEGACY_FLOPPY_OPTIONS_END
