/* RawDisk-Handler V1.1
   © 1993 Christian Stieber
   stieber@informatik.tu-muenchen.de
   freely distributable */

#ifndef EXEC_ALERTS_H
#include <exec/alerts.h>
#endif

#ifndef DOS_FILEHANDLER_H
#include <dos/filehandler.h>
#endif

#ifndef DEVICES_TRACKDISK_H
#include <devices/trackdisk.h>
#endif

#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>

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

struct MyFileHandle
   {
      long FilePos;
   };

static char Version[]="$VER: RawDisk-Handler 1.1 (13.01.94) © Christian Stieber (stieber@informatik.tu-muenchen.de); freely distributable";

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

long MyDoIO(struct IOExtTD *IORequest)

{
   long Error;

   Error=DoIO(IORequest);
   switch(Error)
      {
         case TDERR_WriteProt:   return ERROR_DISK_WRITE_PROTECTED;
         case TDERR_DiskChanged: return ERROR_NO_DISK;
         case TDERR_NoMem:       return ERROR_NO_FREE_STORE;
         case 0:                 return 0;
         default:                return ABORT_DISK_ERROR;
      }
}

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

void __saveds RawDisk(void)

{
   struct DosLibrary *DOSBase;
   struct DosPacket *Packet;
   struct DeviceNode *DeviceNode;
   ULONG OpenCnt;
   int Quit;
   struct IOExtTD IORequest;
   struct DriveGeometry DriveGeometry;
   UBYTE *Buffer;

   if (!(DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37L)))
      {
         Alert(AT_DeadEnd | AG_OpenLib | AO_DOSLib);
      }
   IORequest.iotd_Req.io_Message.mn_ReplyPort=&((struct Process *)FindTask(NULL))->pr_MsgPort;

   Packet=WaitPkt();
   DeviceNode=BADDR(Packet->dp_Arg3);

   DeviceNode->dn_Task=IORequest.iotd_Req.io_Message.mn_ReplyPort;

   {
      struct FileSysStartupMsg *FileSysStartupMsg;
      struct DosEnvec *DosEnvec;

      FileSysStartupMsg=BADDR(DeviceNode->dn_Startup);
      DosEnvec=BADDR(FileSysStartupMsg->fssm_Environ);
      DriveGeometry.dg_TotalSectors=DosEnvec->de_BlocksPerTrack*DosEnvec->de_Surfaces*(DosEnvec->de_HighCyl-DosEnvec->de_LowCyl+1);
      DriveGeometry.dg_SectorSize=DosEnvec->de_SizeBlock*4;
      if (OpenDevice(((char *)BADDR(FileSysStartupMsg->fssm_Device))+1,FileSysStartupMsg->fssm_Unit,&IORequest,FileSysStartupMsg->fssm_Flags))
         {
            ReplyPkt(Packet,DOSFALSE,ERROR_OBJECT_NOT_FOUND);
            Quit=TRUE;
         }
      else
         {
            ReplyPkt(Packet,DOSTRUE,0);
            Quit=FALSE;
         }
   }

   OpenCnt=0;
   while (!Quit)
      {
         Packet=WaitPkt();
         switch(Packet->dp_Type)
            {
               case ACTION_FINDUPDATE:
               case ACTION_FINDINPUT:
               case ACTION_FINDOUTPUT:      {
                                               struct FileHandle *FileHandle;
                                               struct MyFileHandle *MyFileHandle;

                                               if (!OpenCnt)
                                                  {
                                                     long Error;
                                                     IORequest.iotd_Req.io_Command=TD_GETGEOMETRY;
                                                     IORequest.iotd_Req.io_Data=&DriveGeometry;
                                                     IORequest.iotd_Req.io_Length=sizeof(struct DriveGeometry);
                                                     if (Error=MyDoIO(&IORequest))
                                                        {
                                                           ReplyPkt(Packet,DOSFALSE,Error);
                                                        }
                                                     if (!(Buffer=AllocMem(DriveGeometry.dg_SectorSize,0))) goto OutOfMem;
                                                  }

                                               if (MyFileHandle=AllocMem(sizeof(struct MyFileHandle),0))
                                                  {
                                                     FileHandle=BADDR(Packet->dp_Arg1);
                                                     MyFileHandle->FilePos=0;
                                                     FileHandle->fh_Arg1=(long)MyFileHandle;
                                                     OpenCnt++;
                                                     ReplyPkt(Packet,DOSTRUE,0);
                                                  }
                                               else
                                                  {
OutOfMem:                                            ReplyPkt(Packet,DOSFALSE,ERROR_NO_FREE_STORE);
                                                  }
                                            }
                                            break;

               case ACTION_READ:            {
                                               struct MyFileHandle *MyFileHandle;
                                               long Length;
                                               UBYTE *Destination;
                                               long Error;

                                               MyFileHandle=(struct MyFileHandle *)Packet->dp_Arg1;
                                               Destination=(UBYTE *)Packet->dp_Arg2;
                                               Length=Packet->dp_Arg3;
                                               Error=0;

                                               while (!Error && Length && (MyFileHandle->FilePos!=DriveGeometry.dg_TotalSectors*DriveGeometry.dg_SectorSize))
                                                  {
                                                     int Size;
                                                     int BufferPos;
                                                     BufferPos=MyFileHandle->FilePos % DriveGeometry.dg_SectorSize;
                                                     IORequest.iotd_Req.io_Command=CMD_READ;
                                                     IORequest.iotd_Req.io_Data=Buffer;
                                                     IORequest.iotd_Req.io_Length=DriveGeometry.dg_SectorSize;
                                                     IORequest.iotd_Req.io_Offset=MyFileHandle->FilePos-BufferPos;
                                                     if (!(Error=MyDoIO(&IORequest)))
                                                        {
                                                           Size=DriveGeometry.dg_SectorSize-BufferPos;
                                                           if (Length<Size) Size=Length;
                                                           memcpy(Destination,Buffer+BufferPos,Size);
                                                           Length-=Size;
                                                           Destination+=Size;
                                                           MyFileHandle->FilePos+=Size;
                                                        }
                                                  }
                                               ReplyPkt(Packet,Error ? -1 : Packet->dp_Arg3-Length,Error);
                                            }
                                            break;

               case ACTION_WRITE:           {
                                               struct MyFileHandle *MyFileHandle;
                                               long Length;
                                               UBYTE *Source;
                                               long Error;

                                               MyFileHandle=(struct MyFileHandle *)Packet->dp_Arg1;
                                               Source=(UBYTE *)Packet->dp_Arg2;
                                               Length=Packet->dp_Arg3;
                                               Error=0;

                                               while (Length)
                                                  {
                                                     if (MyFileHandle->FilePos!=DriveGeometry.dg_TotalSectors*DriveGeometry.dg_SectorSize)
                                                        {
                                                           int Size;
                                                           int BufferPos;
                                                           BufferPos=MyFileHandle->FilePos % DriveGeometry.dg_SectorSize;
                                                           if (BufferPos || Length<DriveGeometry.dg_SectorSize)
                                                              {
                                                                 IORequest.iotd_Req.io_Command=CMD_READ;
                                                                 IORequest.iotd_Req.io_Data=Buffer;
                                                                 IORequest.iotd_Req.io_Length=DriveGeometry.dg_SectorSize;
                                                                 IORequest.iotd_Req.io_Offset=MyFileHandle->FilePos-BufferPos;
                                                                 if (Error=MyDoIO(&IORequest))
                                                                    {
                                                                       break;
                                                                    }
                                                              }
                                                           Size=DriveGeometry.dg_SectorSize-BufferPos;
                                                           if (Length<Size) Size=Length;
                                                           memcpy(Buffer+BufferPos,Source,Size);
                                                           IORequest.iotd_Req.io_Command=CMD_WRITE;
                                                           IORequest.iotd_Req.io_Data=Buffer;
                                                           IORequest.iotd_Req.io_Length=DriveGeometry.dg_SectorSize;
                                                           IORequest.iotd_Req.io_Offset=MyFileHandle->FilePos-BufferPos;
                                                           if (Error=MyDoIO(&IORequest))
                                                              {
                                                                 break;
                                                              }
                                                           Length-=Size;
                                                           Source+=Size;
                                                           MyFileHandle->FilePos+=Size;
                                                        }
                                                     else
                                                        {
                                                           if (!(Packet->dp_Arg3-Length))
                                                              {
                                                                 Error=ERROR_DISK_FULL;
                                                              }
                                                           break;
                                                        }
                                                  }
                                               ReplyPkt(Packet,Error ? -1 : Packet->dp_Arg3-Length,Error);
                                            }
                                            break;

               case ACTION_SEEK:            {
                                               struct MyFileHandle *MyFileHandle;
                                               long NewFilePos;

                                               NewFilePos=-1;
                                               MyFileHandle=(struct MyFileHandle *)Packet->dp_Arg1;
                                               switch(Packet->dp_Arg3)
                                                  {
                                                     case OFFSET_BEGINNING: NewFilePos=Packet->dp_Arg2;
                                                                            break;
                                                     case OFFSET_CURRENT:   NewFilePos=MyFileHandle->FilePos+Packet->dp_Arg2;
                                                                            break;
                                                     case OFFSET_END:       NewFilePos=DriveGeometry.dg_TotalSectors*DriveGeometry.dg_SectorSize-Packet->dp_Arg2;
                                                                            break;
                                                  }
                                               if (NewFilePos<0 || NewFilePos>DriveGeometry.dg_TotalSectors*DriveGeometry.dg_SectorSize)
                                                  {
                                                     ReplyPkt(Packet,-1,ERROR_SEEK_ERROR);
                                                  }
                                               else
                                                  {
                                                     long OldPos=MyFileHandle->FilePos;
                                                     MyFileHandle->FilePos=NewFilePos;
                                                     ReplyPkt(Packet,OldPos,0);
                                                  }
                                            }
                                            break;

               case ACTION_END:             {
                                               long Error;

                                               Error=0;
                                               FreeMem((struct MyFileHandle *)Packet->dp_Arg1,sizeof(struct MyFileHandle));
                                               if (!--OpenCnt)
                                                  {
                                                     IORequest.iotd_Req.io_Command=CMD_UPDATE;
                                                     Error=MyDoIO(&IORequest);
                                                     IORequest.iotd_Req.io_Command=TD_MOTOR;
                                                     IORequest.iotd_Req.io_Length=0;
                                                     DoIO(&IORequest);
                                                     FreeMem(Buffer,DriveGeometry.dg_SectorSize);
                                                  }
                                               ReplyPkt(Packet,Error ? DOSTRUE : DOSFALSE,Error);
                                            }
                                            break;

               case ACTION_DIE:             if (!OpenCnt)
                                               {
                                                  Forbid();
                                                  ReplyPkt(Packet,DOSTRUE,0);
                                                  while (!IsMsgPortEmpty(IORequest.iotd_Req.io_Message.mn_ReplyPort))
                                                     {
                                                        ReplyPkt(WaitPkt(),DOSFALSE,ERROR_OBJECT_NOT_FOUND);
                                                     }
                                                  Quit=TRUE;
                                               }
                                            else
                                               {
                                                  ReplyPkt(Packet,DOSFALSE,ERROR_OBJECT_IN_USE);
                                               }
                                            break;

               default:                     {
                                               ReplyPkt(Packet,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
                                            }
                                            break;
            }
      }

   {
      BPTR SegList;
      SegList=DeviceNode->dn_SegList;
      DeviceNode->dn_Task=NULL;
      DeviceNode->dn_SegList=NULL;
      CloseDevice(&IORequest);
      UnLoadSeg(DeviceNode->dn_SegList);
      CloseLibrary((struct Library *)DOSBase);
   }
}
