/* WriteKickDisk by Ralf Gruner
*/

#define PROGRAM_NAME "WriteKickDisk"
#define VERSION_NUM "1.3"
#define VERSION_DAT "(20.1.2001)"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <devices/trackdisk.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <dos/dos.h>
#include <dos/dosextens.h>

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>

extern struct ExecBase *SysBase;

LONG tderror=-1;
struct MsgPort *diskport=NULL;
struct IOExtTD *diskreq=NULL;
__chip BYTE diskbuffer[TD_SECTOR];
BYTE comparebuffer[TD_SECTOR];
FILE *kickfile=NULL;
int diskerror=0;
struct DriveGeometry *geo=NULL;

const char *pversion="$VER: "PROGRAM_NAME" "VERSION_NUM" "VERSION_DAT;

VOID disk_busy(LONG onflag);

void CloseAndExit(int retval)
{
	if(tderror==0)
	{
		diskreq->iotd_Req.io_Command = CMD_UPDATE;
		DoIO((struct IORequest *)diskreq);
		diskreq->iotd_Req.io_Length = 0;
		diskreq->iotd_Req.io_Command = TD_MOTOR;
		DoIO((struct IORequest *)diskreq);
		disk_busy(FALSE);
		CloseDevice((struct IORequest *)diskreq);
	}
	if(diskreq)DeleteExtIO((struct IORequest *)diskreq);
	if(diskport) DeletePort(diskport);
	if(kickfile) fclose(kickfile);
	if(geo) free(geo);
	exit(retval);
}             

VOID disk_busy(LONG onflag)
{
struct StandardPacket *pk;
struct Process        *tsk;

    tsk=(struct Process *)FindTask(NULL);
    if (pk=calloc(1,sizeof(struct StandardPacket)))
    {
        pk->sp_Msg.mn_Node.ln_Name=(UBYTE *)&(pk->sp_Pkt);

        pk->sp_Pkt.dp_Link=&(pk->sp_Msg);
        pk->sp_Pkt.dp_Port=&(tsk->pr_MsgPort);
        pk->sp_Pkt.dp_Type=ACTION_INHIBIT;
        pk->sp_Pkt.dp_Arg1=(onflag ? -1L : 0L);

        PutMsg(DeviceProc("DF0:"),(struct Message *)pk);
        WaitPort(&(tsk->pr_MsgPort));
        GetMsg(&(tsk->pr_MsgPort));
        free(pk);
    }
}

int WriteSector(SHORT trk, SHORT sec)
{	
	diskreq->iotd_Req.io_Length = TD_SECTOR;
	diskreq->iotd_Req.io_Data = (APTR)diskbuffer;
	diskreq->iotd_Req.io_Command = CMD_WRITE;
	diskreq->iotd_Req.io_Offset = (ULONG) TD_SECTOR * ((trk * NUMSECS) + sec);
	DoIO((struct IORequest *)diskreq);
	if (diskreq->iotd_Req.io_Error)
	{
		printf("Disk error %d on track %ld sector %ld\n",(int)diskreq->iotd_Req.io_Error,trk,sec);
		diskerror=1;
	}
	return((int)diskreq->iotd_Req.io_Error);
}

ReadSector(SHORT trk, SHORT sec)
{
	diskreq->iotd_Req.io_Length = TD_SECTOR;
	diskreq->iotd_Req.io_Data = (APTR)diskbuffer;
	diskreq->iotd_Req.io_Command = CMD_READ;
	diskreq->iotd_Req.io_Offset = TD_SECTOR * ((trk * NUMSECS) + sec);
	DoIO((struct IORequest *)diskreq);
	if (diskreq->iotd_Req.io_Error)
	{
		printf("Disk error %d on track %ld sector %ld\n",(int)diskreq->iotd_Req.io_Error,trk,sec);
		diskerror=1;
	}
	return((int)diskreq->iotd_Req.io_Error);
}

void MakeSector0(BYTE *buffer)
{
	memset(buffer,0,TD_SECTOR);
	buffer[0]= 'K';
	buffer[1]= 'I';
	buffer[2]= 'C';
	buffer[3]= 'K';
}

void main(int argc, char *argv[])
{
	SHORT track,sector;
	unsigned short *version=0;
	int sectorcounter=0, i;

	if(argc<2)
	{
		printf("Missing filename of kickfile.\nUsage: WriteKickDisk <filename>\n");
		exit(5);
	}
	if(argv[1][0]=='?')
	{
		printf("%s by Ralf Gruner.\n",pversion+6);
		exit(0);
	}

	diskport = CreatePort(0,0);
	if(diskport == 0)
		CloseAndExit(15);

	diskreq = (struct IOExtTD*)CreateExtIO(diskport,sizeof(struct IOExtTD));
	if(diskreq == 0)
		CloseAndExit(15);

	tderror=OpenDevice(TD_NAME,0,(struct IORequest *)diskreq,0);
	if(tderror != 0)
	{
		printf("OpenDevice returned %ld error \n",tderror);
		CloseAndExit(15);
	}
	diskreq->iotd_Req.io_Command = TD_CHANGESTATE;
	DoIO((struct IORequest *)diskreq);
	if(diskreq->iotd_Req.io_Actual != 0)
	{
		printf("No disk in df0:\n");
		CloseAndExit(5);
	}
	diskreq->iotd_Req.io_Command = TD_PROTSTATUS;
	DoIO((struct IORequest *)diskreq);
	if(diskreq->iotd_Req.io_Actual != 0)
	{
		printf("Disk is write protected\n");
		CloseAndExit(5);
	}

	if(SysBase->LibNode.lib_Version>=36)
	{
		geo=(struct DriveGeometry *) calloc(sizeof(struct DriveGeometry),1);
		diskreq->iotd_Req.io_Data=geo;
		diskreq->iotd_Req.io_Command = TD_GETGEOMETRY;
		DoIO((struct IORequest *)diskreq);
		if(geo->dg_TrackSectors != 11)
		{
			printf("*** Warning: %lu sectors/track - no double density disk.\n",geo->dg_TrackSectors);
		}
	}

	MakeSector0(diskbuffer);

	if(!(kickfile=fopen(argv[1],"rb")))
	{
		printf("Error while opening %s.\n",argv[1]);
		CloseAndExit(10);
	}
	printf("\n");

	disk_busy(TRUE);

	for(track=0; track<160; track++)
	{
		for(sector=0; sector<11; sector++)
		{
			if(WriteSector(track,sector))
				goto END2;
			sectorcounter++;

			printf("\033[1A\033[K track %2d  sector %2d\n",(int)track,(int)sector);

			if(fread(diskbuffer, 1, TD_SECTOR, kickfile)<TD_SECTOR)
			{
				if(!feof(kickfile))
				{
					printf(" Error while reading %s\n",argv[1]);
					CloseAndExit(15);
				}
				else
				{
					goto END1;
				}
			}
			if(!version)
			{
				version=(unsigned short*)(diskbuffer+12);
				printf("\033[1A\033[KWriting Kickstart version %u.%u to df0:\n\n",*version,*(version+1),kickfile);
			}

			if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
			{
				printf("*** %s: break\n",argv[0]);
				CloseAndExit(15);
			}
		}
	}

END1:
	printf("Verifying...\n\n");
	fseek(kickfile, 0L, SEEK_SET);
	MakeSector0(comparebuffer);

	for(track=0; track<160; track++)
	{
		for(sector=0; sector<11; sector++)
		{
			if(ReadSector(track,sector))
				goto END2;

			printf("\033[1A\033[K track %2d  sector %2d\n",(int)track,(int)sector);

			for(i=0; i<TD_SECTOR; i++)
			{
				if(diskbuffer[i]!=comparebuffer[i])
				{
					printf("Verify error.\n");
					CloseAndExit(15);
				}
			}

			if(fread(comparebuffer, 1, TD_SECTOR, kickfile)<TD_SECTOR)
			{
				if(!feof(kickfile))
				{
					printf(" Error while reading %s\n",argv[1]);
					CloseAndExit(15);
				}
				else
				{
					goto END2;
				}
			}

			if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
			{
				printf("*** %s: break\n",argv[0]);
				CloseAndExit(15);
			}
		}
	}

END2:
	diskreq->iotd_Req.io_Command = CMD_UPDATE;
	DoIO((struct IORequest *)diskreq);
	if (diskreq->iotd_Req.io_Error)
	{
		printf("Disk error %ld on track %ld\n",diskreq->iotd_Req.io_Error,track);
		diskerror=1;
	}
	if(track>=159)
		printf("File %s is too large.\n",argv[1]);
	else
	{
		if(diskerror)
		{
			printf("Bad disk, please format and retry.\n");
			CloseAndExit(15);
		}
		else
			printf("Finished. %d KB written.\n",sectorcounter*TD_SECTOR/1024);
	}
	CloseAndExit(0);
}
