#include <exec/memory.h>
#include <exec/io.h>
#include <devices/trackdisk.h>
#define __USE_SYSBASE_H
#include <proto/exec.h>

#include "disk.h"

static struct MsgPort *port;
static struct IOStdReq *ior;
static LONG device_is_open;
static LONG bytes_per_block;
static LONG byte_offset;
static LONG sector_size;
static LONG alloc_flags;

BOOL openDisk(STRPTR name, LONG unit, LONG flags)
{
	struct DriveGeometry geo;

	port = CreateMsgPort();
	ior = CreateIORequest(port, sizeof(struct IOExtTD));
	if (ior) {
		device_is_open = !OpenDevice(name, unit, ior, flags);
		if (device_is_open) {
			ior->io_Command = TD_GETGEOMETRY;
			ior->io_Data    = (APTR)&geo;
			ior->io_Length  = sizeof(struct DriveGeometry);
			if (DoIO(ior) != 0) {
				sector_size = 512;
				alloc_flags = MEMF_CHIP;
			} else {
				sector_size = geo.dg_SectorSize;
				alloc_flags = geo.dg_BufMemType;
			}

			bytes_per_block = sector_size;
			byte_offset     = 0;
			return TRUE;
		}
	}
	closeDisk();
	return FALSE;
}

VOID closeDisk(void)
{
	if (device_is_open) CloseDevice(ior);
	if (ior) DeleteIORequest(ior);
	if (port) DeleteMsgPort(port);
}

LONG setMapping(LONG block_size, LONG first_block)
{
	bytes_per_block = block_size > 0 ? block_size : sector_size;
	byte_offset     = first_block * sector_size;
	return bytes_per_block;
}

BOOL readDisk(LONG blockno, UBYTE *buf, LONG size)
{
	BOOL ok;

	ior->io_Command = CMD_READ;
	ior->io_Offset  = blockno * bytes_per_block + byte_offset;
	ior->io_Length  = size;
	ior->io_Data    = buf;
	DoIO(ior);
	ok = ior->io_Error == 0;

	return ok;
}

UBYTE *getBuffer(LONG size)
{
	UBYTE *buf;

	buf = AllocMem(size + sizeof(LONG), alloc_flags);
	if (!buf)
		return NULL;
	*(LONG *)buf = size;
	return buf + sizeof(LONG);
}

VOID freeBuffer(UBYTE *buf)
{
	LONG size;

	if (buf) {
		buf -= sizeof(LONG);
		size = *(LONG *)buf;
		FreeMem(buf, size);
	}
}
