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

#define __USE_SYSBASE 42

#include <proto/exec.h>

#include <string.h>

struct DosPacket *WaitDosPacket(void);
void __regargs ReplyDosPacket(struct DosPacket *,LONG,LONG);

void __regargs OpenDH(struct DosPacket *,ULONG *);
void __regargs CloseDH(struct DHHandle *,ULONG *);

LONG __regargs RawRead(struct DHHandle *,UBYTE *,LONG);
LONG __regargs RawWrite(struct DHHandle *,UBYTE *,LONG);

struct ExecBase *SysBase;

struct DHHandle
 {
  struct IOStdReq *rh_IO;
  struct DriveGeometry rh_DG;
  ULONG rh_Len,rh_Pos,rh_Left;
  UBYTE *rh_Buffer,*rh_Ptr;
  BOOL rh_Write;
 };

#define BCPL_MAX_LEN 256
#define MIN __builtin_min

char *Version = "$VER: Device-Handler 1.0 "__AMIGADATE__;

LONG __saveds DeviceHandler(void)

{
 struct Process *MyProc;
 struct DosPacket *Pkt;
 struct DeviceNode *DeviceNode;
 BOOL Done;
 ULONG OpenCnt;

 SysBase=*(struct ExecBase **)4L;
 if ((MyProc=(struct Process *)SysBase->ThisTask)->pr_CLI) return 20L;

 Pkt=WaitDosPacket();
 DeviceNode=(struct DeviceNode *)BADDR(Pkt->dp_Arg3);
 if (SysBase->LibNode.lib_Version<37)
  {
   ReplyDosPacket (Pkt,DOSFALSE,0L);
   return 0L;
  }

 DeviceNode->dn_Task=&MyProc->pr_MsgPort;
 ReplyDosPacket (Pkt,DOSTRUE,Pkt->dp_Res2);

 Done=FALSE;
 OpenCnt=0L;

 while (!Done)
  {
   Pkt=WaitDosPacket();

   switch (Pkt->dp_Type)
    {
     case ACTION_FINDINPUT:
      OpenDH (Pkt,&OpenCnt);
      ReplyDosPacket (Pkt,Pkt->dp_Res1,Pkt->dp_Res2);
      break;
     case ACTION_FINDOUTPUT:
      OpenDH (Pkt,&OpenCnt);
      ReplyDosPacket (Pkt,Pkt->dp_Res1,Pkt->dp_Res2);
      break;
     case ACTION_END:
      CloseDH ((struct DHHandle *)Pkt->dp_Arg1,&OpenCnt);
      ReplyDosPacket (Pkt,DOSTRUE,0L);
      break;
     case ACTION_READ:
      if (!((struct DHHandle *)Pkt->dp_Arg1)->rh_Write)
       {
        LONG Bytes;

        Bytes=RawRead((struct DHHandle *)Pkt->dp_Arg1,(UBYTE *)Pkt->dp_Arg2,Pkt->dp_Arg3);
        if (Bytes<0L) ReplyDosPacket (Pkt,-1L,ERROR_SEEK_ERROR);
        else ReplyDosPacket (Pkt,Bytes,0L);
       }
      else ReplyDosPacket (Pkt,-1L,ERROR_OBJECT_WRONG_TYPE);
      break;
     case ACTION_WRITE:
      if (((struct DHHandle *)Pkt->dp_Arg1)->rh_Write)
       {
        LONG Bytes;

        Bytes=RawWrite((struct DHHandle *)Pkt->dp_Arg1,(UBYTE *)Pkt->dp_Arg2,Pkt->dp_Arg3);
        if (Bytes<0L) ReplyDosPacket (Pkt,-1L,ERROR_SEEK_ERROR);
        else ReplyDosPacket (Pkt,Bytes,0L);
       }
      else ReplyDosPacket (Pkt,-1L,ERROR_OBJECT_WRONG_TYPE);
      break;
     case ACTION_IS_FILESYSTEM:
      ReplyDosPacket (Pkt,DOSFALSE,0L);
      break;
     default:
      ReplyDosPacket (Pkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
    }

   if (OpenCnt==0L)
    {
     Disable();
     if (MyProc->pr_MsgPort.mp_MsgList.lh_Head->ln_Succ==NULL)
      {
       DeviceNode->dn_Task=NULL;
       Done=TRUE;
      }
     Enable();
    }
  }

 return 0L;
}

struct DosPacket *WaitDosPacket(void)

{
 struct Process *MyProc;

 MyProc=(struct Process *)SysBase->ThisTask;
 (void)WaitPort(&MyProc->pr_MsgPort);
 return (struct DosPacket *)GetMsg(&MyProc->pr_MsgPort)->mn_Node.ln_Name;
}

void __regargs ReplyDosPacket(struct DosPacket *Packet,LONG Res1,LONG Res2)

{
 struct MsgPort *ReplyPort;

 ReplyPort=Packet->dp_Port;
 Packet->dp_Port=&((struct Process *)SysBase->ThisTask)->pr_MsgPort;
 Packet->dp_Link->mn_Node.ln_Name=(char *)Packet;
 Packet->dp_Res1=Res1;
 Packet->dp_Res2=Res2;

 PutMsg (ReplyPort,Packet->dp_Link);
}

void __regargs OpenDH(struct DosPacket *Pkt,ULONG *OpenCnt)

{
 struct FileHandle *FH;
 UBYTE *BStr,Name[BCPL_MAX_LEN],*Ptr;
 ULONG Unit;
 struct DHHandle *RH;
 struct MsgPort *IOPort;

 Pkt->dp_Res1=DOSFALSE;
 FH=(struct FileHandle *)BADDR(Pkt->dp_Arg1);
 FH->fh_Port=(struct MsgPort *)FALSE;
 FH->fh_Arg1=0L;

 BStr=(UBYTE *)BADDR(Pkt->dp_Arg3);
 (void)memcpy(Name,&BStr[1],BStr[0]);
 Name[BStr[0]]='\0';

 if (Ptr=strchr(Name,':')) (void)strcpy(Name,Ptr+1L);
 if ((Ptr=strchr(Name,'/'))==NULL)
  {
   Pkt->dp_Res2=ERROR_BAD_STREAM_NAME;
   return;
  }
 *Ptr++='\0';

 if ((Name[0]=='\0')||(*Ptr=='\0'))
  {
   Pkt->dp_Res2=ERROR_BAD_STREAM_NAME;
   return;
  }

 Unit=0L;
 while (*Ptr)
  if ((*Ptr<'0')||(*Ptr>'9'))
   {
    Pkt->dp_Res2=ERROR_BAD_NUMBER;
    return;
   }
  else Unit=10L*Unit+(*Ptr++-'0');

 Pkt->dp_Res2=ERROR_NO_FREE_STORE;
 if (RH=AllocVec(sizeof(struct DHHandle),MEMF_PUBLIC|MEMF_CLEAR))
  {
   if (IOPort=CreateMsgPort())
    {
     if (RH->rh_IO=CreateStdIO(IOPort))
      {
       if (OpenDevice(Name,Unit,(struct IORequest *)RH->rh_IO,0L)==0L)
        {
         RH->rh_IO->io_Command=TD_GETGEOMETRY;
         RH->rh_IO->io_Length=sizeof(struct DriveGeometry);
         RH->rh_IO->io_Data=&RH->rh_DG;
         if (DoIO((struct IORequest *)RH->rh_IO)==0L)
          if (RH->rh_Buffer=AllocMem(RH->rh_DG.dg_SectorSize,RH->rh_DG.dg_BufMemType))
           {
            RH->rh_Len=RH->rh_DG.dg_SectorSize*RH->rh_DG.dg_TotalSectors;
            if (Pkt->dp_Type==ACTION_FINDOUTPUT)
             {
              RH->rh_Write=TRUE;
              RH->rh_Left=RH->rh_DG.dg_SectorSize;
              RH->rh_Ptr=RH->rh_Buffer;
             }
            else RH->rh_Write=FALSE;
            FH->fh_Arg1=(LONG)RH;
            Pkt->dp_Res1=DOSTRUE;
            Pkt->dp_Res2=0L;

            (*OpenCnt)++;
            return;
           }
         CloseDevice ((struct IORequest *)RH->rh_IO);
        }
       else Pkt->dp_Res2=ERROR_OBJECT_NOT_FOUND;

       DeleteStdIO (RH->rh_IO);
      }
     DeleteMsgPort (IOPort);
    }
   FreeVec (RH);
  }
}

void __regargs CloseDH(struct DHHandle *RH,ULONG *OpenCnt)

{
 if (RH->rh_Write)
  {
   if (RH->rh_Left<RH->rh_DG.dg_SectorSize)
    {
     (void)memset(RH->rh_Ptr,'\0',RH->rh_Left);
     RH->rh_IO->io_Command=CMD_WRITE;
     RH->rh_IO->io_Length=RH->rh_DG.dg_SectorSize;
     RH->rh_IO->io_Data=RH->rh_Buffer;
     RH->rh_IO->io_Offset=RH->rh_Pos+RH->rh_Left-RH->rh_DG.dg_SectorSize;
     (void)DoIO((struct IORequest *)RH->rh_IO);
    }
   RH->rh_IO->io_Command=CMD_UPDATE;
   (void)DoIO((struct IORequest *)RH->rh_IO);
  }

 FreeMem (RH->rh_Buffer,RH->rh_DG.dg_SectorSize);

 RH->rh_IO->io_Command=TD_MOTOR;
 RH->rh_IO->io_Length=0L;
 (void)DoIO((struct IORequest *)RH->rh_IO);
 CloseDevice ((struct IORequest *)RH->rh_IO);

 DeleteMsgPort (RH->rh_IO->io_Message.mn_ReplyPort);
 DeleteStdIO (RH->rh_IO);

 FreeVec (RH);

 (*OpenCnt)--;
}

LONG __regargs RawRead(struct DHHandle *RH,UBYTE *Buffer,LONG Bytes)

{
 LONG Total,RDBytes;

 if ((RH->rh_Pos+Bytes)>RH->rh_Len) Bytes=RH->rh_Len-RH->rh_Pos;

 Total=0L;
 while (Bytes)
  {
   if (RH->rh_Left)
    {
     RDBytes=MIN(RH->rh_Left,Bytes);
     (void)memcpy(Buffer,RH->rh_Ptr,RDBytes);

     Buffer+=RDBytes;
     Bytes-=RDBytes;
     Total+=RDBytes;

     RH->rh_Pos+=RDBytes;
     RH->rh_Ptr+=RDBytes;
     RH->rh_Left-=RDBytes;

     if (Bytes==0L) return Total;
    }

   RH->rh_IO->io_Command=CMD_READ;
   RH->rh_IO->io_Length=RH->rh_DG.dg_SectorSize;
   RH->rh_IO->io_Data=RH->rh_Buffer;
   RH->rh_IO->io_Offset=RH->rh_Pos;
   if (DoIO((struct IORequest *)RH->rh_IO)) return -1L;

   RH->rh_Ptr=RH->rh_Buffer;
   RH->rh_Left=RH->rh_IO->io_Actual;
  }
 return Total;
}

LONG __regargs RawWrite(struct DHHandle *RH,UBYTE *Buffer,LONG Bytes)

{
 LONG Total,WRBytes;

 if ((RH->rh_Pos+Bytes)>RH->rh_Len) Bytes=RH->rh_Len-RH->rh_Pos;

 Total=0L;
 while (Bytes)
  {
   WRBytes=MIN(RH->rh_Left,Bytes);
   (void)memcpy(RH->rh_Ptr,Buffer,WRBytes);

   Buffer+=WRBytes;
   Bytes-=WRBytes;
   Total+=WRBytes;

   RH->rh_Pos+=WRBytes;
   RH->rh_Ptr+=WRBytes;
   RH->rh_Left-=WRBytes;

   if (RH->rh_Left==0L)
    {
     RH->rh_IO->io_Command=CMD_WRITE;
     RH->rh_IO->io_Length=RH->rh_DG.dg_SectorSize;
     RH->rh_IO->io_Data=RH->rh_Buffer;
     RH->rh_IO->io_Offset=RH->rh_Pos-RH->rh_DG.dg_SectorSize;
     if (DoIO((struct IORequest *)RH->rh_IO)) return -1L;

     RH->rh_Ptr=RH->rh_Buffer;
     RH->rh_Left=RH->rh_DG.dg_SectorSize;
    }
  }
 return Total;
}
