#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/trackdisk.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdlib.h>
#include <stdio.h>
#include <proto/exec.h>
#include <proto/dos.h>


/* Lhwarp version 1.03 */
#define MAJOR_VERSION 1
#define MINOR_VERSION 0
#define DECIMAL_VERSION 3


#define NUM_HEADS 2


#define TRACK_SIZE (TD_SECTOR * NUMSECS * NUM_HEADS)


#define SEEK_SET 0


#define WARP_READ 1
#define WARP_WRITE 2


#define IN_BUFFER_SIZE 16384
#define OUT_BUFFER_SIZE 16384


/* Only LZHUF is supported at the moment */
#define COMPTYPE_LHARC 0


/* Note, this has changed! */
#define DATATYPE_NORMAL 0
#define DATATYPE_RAW 1


struct WarpHeader
   {
      UBYTE  MajorVersion;
      UBYTE  MinorVersion;
      UBYTE  DecimalVersion;
      UBYTE  Reserved;
      USHORT StartTrack;
      USHORT EndTrack;
      ULONG  TextLength;
      ULONG  CompressedTextLength;
   };


struct CompHeader101
   {
      UBYTE  CompressionType;
      UBYTE  DataType;
      UBYTE  TrackNumber;
      UBYTE  Unused;
      ULONG  FullLength;
      ULONG  CompressedLength;
   };


struct CompHeader102
   {
      UBYTE  CompressionType;
      UBYTE  DataType;
      UBYTE  TrackNumber;
      UBYTE  Unused;
      ULONG  FullLength;
      ULONG  CompressedLength;
      ULONG  CheckSum;
   };


#define LABEL_LENGTH (16 * NUMSECS * NUM_HEADS)


UBYTE *iotd_SecLabel = NULL;


struct WarpHeader WHeader;
struct IOExtTD *ReadMsg   = NULL;
struct IOExtTD *WriteMsg  = NULL;
struct MsgPort *ReadPort  = NULL;
struct MsgPort *WritePort = NULL;


extern FILE  *infile;
extern FILE  *outfile;
extern UBYTE *FilePosition;
extern UBYTE *EndOfFilePosition;
extern UBYTE *PutFilePosition;


/* LZHUF defines and externs */
#define N		4096	/* Size of string buffer */
#define F		60	/* Size of look-ahead buffer */
#define THRESHOLD	2
#define NIL		N	/* End of tree's node  */

extern unsigned char *text_buf;
extern short         *lson, *rson, *dad;


struct CompHeader101 CHeader101;
struct CompHeader102 CHeader102;
char               FileName[256];
char               TextFileName[256];
UBYTE              Command;
UBYTE              AppendTextFile;
UBYTE              Version101;
BPTR               FHandle;
ULONG              StartTrack;
ULONG              EndTrack;
ULONG              Unit;
APTR               DataBlock = NULL;
APTR               NewBlock = NULL;
APTR               TrackdiskBlock = NULL;


ULONG GetFileLength(register char *FileToOpen);
ULONG DoSum(UBYTE *Block, ULONG Length);
void  __regargs Decode(ULONG Size);


void main(int argc, char **argv)
{
   if (argc != 2 && argc != 4 && argc != 6 && argc != 7)
      {
         Title();
         exit (1);
      }

   if (!stricmp(argv[1], "READ"))
      {
         /* Remove the file if it was already there */
         remove(FileName);

         Command = WARP_READ;
      }
   else if (!stricmp(argv[1], "WRITE"))
      {
         Command = WARP_WRITE;
      }
   else if (!stricmp(argv[1], "HELP"))
      {
         exit(0);
      }
   else
      {
         puts("\nUnknown command\n");
         exit(1);
      }

   if (argc < 6 && Command == WARP_READ)
      {
         Title();
         exit (1);
      }

   if (argc == 7)
      {
         strcpy(TextFileName, argv[6]);
         AppendTextFile = 1;
      }
   else
      {
         AppendTextFile = 0;
      }

   /* Get the unit number */
   Unit       = atoi(argv[2]);

   /* Get the file name */
   strcpy(FileName, argv[3]);
   strupr(FileName);

   /* Append .LHW if necessary */
   if (strcmp(FileName, ".LHW"))
      {
         if (strlen(FileName) > 4)
            {
               if (strcmp(&FileName[strlen(FileName)-4], ".LHW"))
                  {
                     strcat(FileName, ".LHW");
                  }
            }
         else
            {
               strcat(FileName, ".LHW");
            }
      }

   /* Get the start and end tracks */
   if (argc > 5)
      {
         StartTrack = atoi(argv[4]);
         EndTrack   = atoi(argv[5]);
      }

   if (StartTrack > EndTrack)
      {
         puts("Start track is greater than end track\n");
         exit (1);
      }

   Init();

   if (Command == WARP_WRITE)
      {
         WriteData();
      }
   else
      {
         ReadData();
      }
}


Init()
{
   text_buf = NULL;
   lson     = NULL;
   rson     = NULL;
   dad      = NULL;

   if (!(ReadPort = CreatePort(0L, 0L)))
      {
         puts("Couldn't create read port");
         exit (1);
      }

   if (!(WritePort = CreatePort(0L, 0L)))
      {
         puts("Couldn't create write port");
         CleanUp();
         exit (1);
      }

   if (!(ReadMsg = (struct IORequest *) CreateExtIO(ReadPort, (long) sizeof(struct IOExtTD))))
      {
         puts("Couldn't create i/o read request");
         CleanUp();
         exit (1);
      }

   if (!(WriteMsg = (struct IORequest *) CreateExtIO(WritePort, (long) sizeof(struct IOExtTD))))
      {
         puts("Couldn't create i/o write request");
         CleanUp();
         exit (1);
      }

   ReadMsg->iotd_Req.io_Command = ETD_READ;
   WriteMsg->iotd_Req.io_Command = ETD_WRITE;

   if (OpenDevice(TD_NAME, Unit, (struct IORequest *) ReadMsg, 0L))
      {
         puts("Couldn't open trackdisk.device for reading");
         CleanUp();
         exit (1);
      }

   if (OpenDevice(TD_NAME, Unit, (struct IORequest *) WriteMsg, 0L))
      {
         puts("Couldn't open trackdisk.device for writing");
         CleanUp();
         exit (1);
      }

   if (!(DataBlock = AllocMem((ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH, MEMF_PUBLIC)))
      {
         puts("Couldn't allocate trackdisk memory buffer");
         CleanUp();
         exit (1);
      }

   if (!(NewBlock = AllocMem((ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH, MEMF_PUBLIC)))
      {
         puts("Couldn't allocate trackdisk memory buffer");
         CleanUp();
         exit (1);
      }

   if (!(TrackdiskBlock = AllocMem((ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH, MEMF_CHIP)))
      {
         puts("Couldn't allocate trackdisk memory buffer");
         CleanUp();
         exit (1);
      }

   if (!(iotd_SecLabel = AllocMem((ULONG) LABEL_LENGTH, MEMF_CHIP)))
      {
         puts("Couldn't allocate trackdisk extended memory buffer");
         CleanUp();
         exit (1);
      }

   if (!(text_buf = AllocMem((ULONG) (N + F - 1), MEMF_PUBLIC)))
      {
         puts("Out of memory");
         CleanUp();
         exit (1);
      }

   if (!(lson = AllocMem((ULONG) (N + 1) * (ULONG) (sizeof(short)), MEMF_PUBLIC)))
      {
         puts("Out of memory");
         CleanUp();
         exit (1);
      }

   if (!(rson = AllocMem((ULONG) (N + 257) * (ULONG) (sizeof(short)), MEMF_PUBLIC)))
      {
         puts("Out of memory");
         CleanUp();
         exit (1);
      }

   if (!(dad = AllocMem((ULONG) (N + 1) * (ULONG) (sizeof(short)), MEMF_PUBLIC)))
      {
         puts("Out of memory");
         CleanUp();
         exit (1);
      }
}


Archive(USHORT Track, ULONG DataSize)
{
   UBYTE            *OutBuffer;
   UBYTE             CanSetBuffer;
   ULONG             CurrentPosition;
   ULONG             AfterEncodeLength;
   ULONG             TotalEncodeLength;
   BPTR              outfp;

   if (OutBuffer = AllocMem((ULONG) OUT_BUFFER_SIZE, MEMF_PUBLIC))
      {
         CanSetBuffer = 1;
      }
   else
      {
         CanSetBuffer = 0;
      }

   CurrentPosition = GetFileLength(FileName);

   /* Lhwarp to a file; this way, if the output is larger than the input */
   /* we won't crash the machine */
   if (!(outfile = fopen(FileName, "a+b")))
      {
         printf("Couldn't open temporary file for output\n");

         if (CanSetBuffer)
            {
               FreeMem(OutBuffer, (ULONG) OUT_BUFFER_SIZE);
            }

         CleanUp();
         exit (1);
      }

   if (CanSetBuffer)
      {
         setvbuf(outfile, OutBuffer, _IOFBF, (long) OUT_BUFFER_SIZE);
      }

   /* Write out a dummy file compression header */
   fwrite(&CHeader102, sizeof(struct CompHeader102), 1, outfile);

   Encode((ULONG) (DataSize));

   fclose(outfile);

   if (CanSetBuffer)
      {
         FreeMem(OutBuffer, (ULONG) OUT_BUFFER_SIZE);
      }

   AfterEncodeLength = GetFileLength(FileName);
   TotalEncodeLength = AfterEncodeLength - CurrentPosition - sizeof(struct CompHeader102);

   if (!(outfp = (BPTR) Open(FileName, (long) MODE_READWRITE)))
      {
         printf("Couldn't open temporary file for output\n");

         if (CanSetBuffer)
            {
               FreeMem(OutBuffer, (ULONG) OUT_BUFFER_SIZE);
            }

         CleanUp();
         exit (1);
      }

   /* Seek back to the CompHeader section */
   Seek((BPTR) outfp, (long) CurrentPosition, (long) OFFSET_BEGINNING);

   CHeader102.CompressedLength = TotalEncodeLength;
   CHeader102.FullLength       = DataSize;
   CHeader102.TrackNumber      = (UBYTE) Track;
   CHeader102.CompressionType  = COMPTYPE_LHARC;
   CHeader102.DataType         = DATATYPE_NORMAL;

   /* Checksum the block */
   CHeader102.CheckSum = (ULONG) DoSum(NewBlock, (ULONG) CHeader102.FullLength);

   /* Write it to disk */
   Write((BPTR) outfp, &CHeader102, (long) sizeof(struct CompHeader102));

   /* Close the file */
   Close((BPTR) outfp);
}


WriteData()
{
   ULONG  Track;
   UBYTE *TextMemory;
   UBYTE *TextCompMemory;
   UBYTE *ExtendedDataBlock;
   ULONG  CheckSum;
   ULONG  TracksWritten = 0L;

   printf("\nLHWARP %d.%d%d - Amiga disk tracker - Written by Jonathan Forbes @ 1:250/642\n", (UBYTE) MAJOR_VERSION, (UBYTE) MINOR_VERSION, (UBYTE) DECIMAL_VERSION);
   puts("Copyright © Xenomiga Technology, 1990.\n");
   printf("Writing to disk in drive %ld from file %s\n", Unit, FileName);

   /* Set up the write request */
   WriteMsg->iotd_Req.io_Command = ETD_FORMAT;

   if (!(infile = fopen(FileName, "r")))
      {
         printf("Cannot open %s for input\n", FileName);
         CleanUp();
         exit (1);
      }

   /* Read the Warp header */
   fread(&WHeader, sizeof(struct WarpHeader), 1, infile);

   /* What version of Lhwarp was used to compress the disk? */
   if (WHeader.MajorVersion == 1 && WHeader.MinorVersion == 0 && WHeader.DecimalVersion == 1)
      {
         printf("Disk was compressed using Lhwarp 1.01\n\n");
         Version101 = 1;
      }
   else
      {
         printf("Disk was compressed using Lhwarp %d.%d%d\n\n", WHeader.MajorVersion, WHeader.MinorVersion, WHeader.DecimalVersion);
         Version101 = 0;
      }

   if ((WHeader.MajorVersion > MAJOR_VERSION) ||
       (WHeader.MajorVersion == MAJOR_VERSION && WHeader.MinorVersion > MINOR_VERSION))
      {
         printf("Sorry, this file was Lhwarp'd with Lhwarp V%d%d.%d\n",
            WHeader.MajorVersion, WHeader.MinorVersion, WHeader.DecimalVersion);

         printf("You are using Lhwarp V%d%d.%d\n",
            MAJOR_VERSION, MINOR_VERSION, DECIMAL_VERSION);

         CleanUp();
         exit(1);
      }

   if (WHeader.TextLength)
      {
         if (!(TextMemory = AllocMem((ULONG) WHeader.TextLength+1, MEMF_PUBLIC)))
            {
               printf("Insufficient memory (%ld bytes required) to read compressed text\n", (ULONG) WHeader.TextLength+1);
               CleanUp();
               exit(1);
            }

         if (!(TextCompMemory = AllocMem((ULONG) WHeader.CompressedTextLength, MEMF_PUBLIC)))
            {
               printf("Insufficient memory (%ld bytes required) to decompress text\n", (ULONG) WHeader.CompressedTextLength);
               FreeMem(TextMemory, (ULONG) WHeader.TextLength+1);
               CleanUp();
               exit(1);
            }

         fread(TextCompMemory, (ULONG) WHeader.CompressedTextLength, 1, infile);

         FilePosition = (UBYTE *) TextCompMemory;

         /* Arbitrary number; just make it greater than TextLength */
         EndOfFilePosition = (UBYTE *) ( ((UBYTE *) TextCompMemory) + (WHeader.CompressedTextLength));

         PutFilePosition = (UBYTE *) TextMemory;

         Decode((ULONG) WHeader.TextLength);

         /* Make sure the text has a null terminator */
         TextMemory[WHeader.TextLength] = 0;

         puts("\n----\n");
         puts(TextMemory);
         puts("----\n");

         FreeMem(TextMemory, (ULONG) WHeader.TextLength+1);
         FreeMem(TextCompMemory, (ULONG) WHeader.CompressedTextLength);
      }

   /* Start writing tracks */
   for (;;)
      {
         if (Version101)
            {
               if (!(fread(&CHeader101, sizeof(struct CompHeader101), 1, infile)))
                  {
                     break;
                  }
            }
         else
            {
               if (!(fread(&CHeader102, sizeof(struct CompHeader102), 1, infile)))
                  {
                     break;
                  }
            }

         FilePosition = (UBYTE *) DataBlock;
         PutFilePosition = (UBYTE *) NewBlock;

         if (Version101)
            {
               EndOfFilePosition = (UBYTE *) ( ((UBYTE *) DataBlock) + CHeader101.CompressedLength);
               fread(DataBlock, (long) CHeader101.CompressedLength, 1, infile);
               Decode((ULONG) CHeader101.FullLength);

               Track = (ULONG) CHeader101.TrackNumber;
            }
         else
            {
               EndOfFilePosition = (UBYTE *) ( ((UBYTE *) DataBlock) + CHeader102.CompressedLength);
               fread(DataBlock, (long) CHeader102.CompressedLength, 1, infile);
               Decode((ULONG) CHeader102.FullLength);

               CheckSum = (ULONG) DoSum(NewBlock, (ULONG) CHeader102.FullLength);

               if (CheckSum != CHeader102.CheckSum)
                  {
                     printf("Warning: Track %ld fails checksum\n", Track);
                  }

               Track = (ULONG) CHeader102.TrackNumber;
            }

         ExtendedDataBlock = (UBYTE *) ( ((UBYTE *) NewBlock) + ((ULONG) TRACK_SIZE));

         CopyMem(ExtendedDataBlock, (UBYTE *) &iotd_SecLabel[0], (ULONG) LABEL_LENGTH);

         if (!Track)
            {
               puts("Disk contains the following bootblock:\n");
               DisplayBootBlock(NewBlock);
            }

         /* Copy data into CHIP RAM */
         CopyMem(NewBlock, TrackdiskBlock, (ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH);

         WriteMsg->iotd_Req.io_Offset = (long) TRACK_SIZE * Track;
         WriteMsg->iotd_Req.io_Data   = (APTR) TrackdiskBlock;
         WriteMsg->iotd_Req.io_Length = (long) TRACK_SIZE;
         WriteMsg->iotd_SecLabel      = &iotd_SecLabel[0];
         WriteMsg->iotd_Count         = 0xFFFFFFFF;

         printf("Writing track %2ld", (ULONG) (Track));
         fflush(stdout);

         if (DoIO((struct IORequest *) WriteMsg))
            {
               fclose(infile);
               printf("Fatal error writing to disk in unit %ld -terminating\n\n", Unit);
               MotorOff();
               CleanUp();
               exit (1);
            }

         printf("\n[A");
         fflush(stdout);

         TracksWritten++;
      }

   MotorOff();
   WriteMsg->iotd_Req.io_Length  = 0L;
   WriteMsg->iotd_Req.io_Command = CMD_FLUSH;
   DoIO((struct IORequest *) WriteMsg);

   fclose(infile);

   printf("\n\nFinished writing %ld tracks\n\n", TracksWritten);
   printf("Please remove and re-insert destination disk, to use\n\n");

   CleanUp();
}


MotorOff()
{
   WriteMsg->iotd_Req.io_Length  = 0L;
   WriteMsg->iotd_Req.io_Command = TD_MOTOR;
   DoIO((struct IORequest *) WriteMsg);
}


DisplayBootBlock(UBYTE *Data)
{
   char           Line[76];
   UBYTE          i;
   register UBYTE j;
   register UBYTE c;

   /* End of the line to be displayed */
   Line[73] = 10;
   Line[74] = 13;
   Line[75] = 0;

   for (i=0; i<14; i++)
      {
         for (j=0; j<73; j++)
            {
               c = *((UBYTE *) ( ((UBYTE *) Data) + (i * 64) + j));

               if (c < 32 || c > 126)
                  {
                     c = '.';
                  }

               Line[j] = c;
            }

         printf(Line);
      }

   puts("");
}


ReadData()
{
   ULONG  AfterEncodeLength;
   ULONG  TotalEncodeLength;
   ULONG  TextLength;
   ULONG  i;
   ULONG  FileLength;
   ULONG  OriginalLength = 0L;
   BPTR   OutputFile;
   ULONG  Error;
   ULONG  DataSize;
   ULONG  OnlyDataSize;
   FILE  *TextInFile;
   UBYTE *TextMemory;
   UBYTE *ExtendedDataBlock;

   printf("\nLHWARP %d.%d%d - Amiga disk tracker - Written by Jonathan Forbes @ 1:250/642\n", (UBYTE) MAJOR_VERSION, (UBYTE) MINOR_VERSION, (UBYTE) DECIMAL_VERSION);
   puts("Copyright © Xenomiga Technology, 1990.\n");
   printf("Reading from disk in drive %ld to file %s\n", Unit, FileName);

   /* Set up the read request */
   ReadMsg->iotd_Req.io_Command = ETD_READ;
   ReadMsg->iotd_Count          = 0xFFFFFFFF;

   /* If we start at track zero, check the boot block */
   if (!StartTrack)
      {
         ReadMsg->iotd_Req.io_Offset = (long) 0L;
         ReadMsg->iotd_Req.io_Data   = (APTR) TrackdiskBlock;

         /* Only get the first two sectors */
         ReadMsg->iotd_Req.io_Length = (long) 1024L;

         if (Error = DoIO((struct IORequest *) ReadMsg))
            {
               printf("Error %d reading from trackdisk.device\n", Error);
               CleanUp();
               exit (1);
            }

         printf("\nBoot block of floppy disk in drive %d:\n\n", Unit);
         DisplayBootBlock(TrackdiskBlock);
      }

   if (AppendTextFile)
      {
         TextLength = GetFileLength(TextFileName);

         if (!TextLength)
            {
               puts("Text file missing or zero length\n");
               CleanUp();
               exit (1);
            }

         WHeader.TextLength = TextLength;
      }
   else
      {
         WHeader.TextLength = 0L;
      }

   /* Build the Warp header */
   WHeader.StartTrack = StartTrack;
   WHeader.EndTrack   = EndTrack;

   /* Version 1.03 */
   WHeader.MajorVersion = (UBYTE) MAJOR_VERSION;
   WHeader.MinorVersion = (UBYTE) MINOR_VERSION;
   WHeader.DecimalVersion = (UBYTE) DECIMAL_VERSION;

   /* Write out the Warp header */
   if (!(OutputFile = Open(FileName, MODE_NEWFILE)))
      {
         printf("Couldn't open output file\n");
         CleanUp();
         exit (1);
      }

   Write(OutputFile, &WHeader, (long) sizeof(struct WarpHeader));
   Close(OutputFile);

   if (AppendTextFile)
      {
         printf("Compressing text ... ");
         fflush(stdout);

         if (!(TextInFile = fopen(TextFileName, "r")))
            {
               printf("Can't open %s for input\n", TextInFile);
               CleanUp();
               exit(1);
            }

         if (!(TextMemory = AllocMem((ULONG) TextLength, MEMF_PUBLIC)))
            {
               puts("Couldn't allocate text memory buffer");
               CleanUp();
               exit(1);
            }

         fread(TextMemory, (ULONG) TextLength, 1, TextInFile);
         fclose(TextInFile);

         if (!(outfile = fopen(FileName, "a")))
            {
               printf("Can't open %s for append\n", FileName);
               FreeMem(TextMemory, (ULONG) TextLength);
               CleanUp();
               exit(1);
            }

         FilePosition = (UBYTE *) TextMemory;
         EndOfFilePosition = (UBYTE *) ( ((UBYTE *) TextMemory) + (TextLength));

         Encode((ULONG) (TextLength));
         fclose(outfile);

         FreeMem(TextMemory, (ULONG) TextLength);

         AfterEncodeLength = GetFileLength(FileName);
         TotalEncodeLength = AfterEncodeLength - (ULONG) sizeof(struct WarpHeader);

         WHeader.CompressedTextLength = TotalEncodeLength;

         if (!(outfile = fopen(FileName, "r+w")))
            {
               printf("Can't open %s for append\n", FileName);
               CleanUp();
               exit(1);
            }

         fwrite(&WHeader, sizeof(struct WarpHeader), 1, outfile);
         fclose(outfile);

         printf("done\n\n");
      }

   /* Start reading tracks */
   for (i = StartTrack; i <= EndTrack; i++)
      {
         DataSize = (ULONG) TRACK_SIZE + (ULONG) (NUMSECS * NUM_HEADS * 16);
         OnlyDataSize = (ULONG) TRACK_SIZE;
         ExtendedDataBlock = (UBYTE *) ( ((UBYTE *) NewBlock) + ((ULONG) OnlyDataSize));

         ReadMsg->iotd_Req.io_Offset = (long) TRACK_SIZE * i;
         ReadMsg->iotd_Req.io_Data   = (APTR) TrackdiskBlock;
         ReadMsg->iotd_Req.io_Length = (long) TRACK_SIZE;
         ReadMsg->iotd_SecLabel      = &iotd_SecLabel[0];
         ReadMsg->iotd_Count         = 0xFFFFFFFF;

         printf("Reading track %2ld ...            [11D", (ULONG) (i));

         fflush(stdout);

         DoIO((struct IORequest *) ReadMsg);

         printf("compressing");
         fflush(stdout);

         /* Copy data out of CHIP RAM */
         CopyMem(TrackdiskBlock, NewBlock, (ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH);
         CopyMem((UBYTE *) &iotd_SecLabel[0], (UBYTE *) ExtendedDataBlock, (ULONG) NUMSECS * NUM_HEADS * 16);

         FilePosition = (UBYTE *) NewBlock;
         EndOfFilePosition = (UBYTE *) ( ((UBYTE *) NewBlock) + (DataSize));

         Archive(i, DataSize);

         OriginalLength += (DataSize);

         printf("\n[A");
      }

   ReadMsg->iotd_Req.io_Length  = 0;
   ReadMsg->iotd_Req.io_Command = TD_MOTOR;
   DoIO((struct IORequest *) ReadMsg);

   FileLength = GetFileLength(FileName);

   printf("\n\nFinished reading tracks %d to %d (%d total)\n", WHeader.StartTrack, WHeader.EndTrack, WHeader.EndTrack - WHeader.StartTrack + 1);
   printf(" Input file: %ld bytes\n", OriginalLength);
   printf("Output file: %ld bytes\n\n", FileLength);

   CleanUp();
}


CleanUp()
{
   if (ReadMsg)
      {
         CloseDevice((struct IORequest *) ReadMsg);
         DeleteExtIO((struct IORequest *) ReadMsg, (long) sizeof(struct IOExtTD));
      }

   if (WriteMsg)
      {
         CloseDevice((struct IORequest *) WriteMsg);
         DeleteExtIO((struct IORequest *) WriteMsg, (long) sizeof(struct IOExtTD));
      }

   if (ReadPort)
      {
         DeletePort(ReadPort);
      }

   if (WritePort)
      {
         DeletePort(WritePort);
      }

   if (DataBlock)
      {
         FreeMem(DataBlock, (ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH);
      }

   if (NewBlock)
      {
         FreeMem(NewBlock, (ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH);
      }

   if (TrackdiskBlock)
      {
         FreeMem(TrackdiskBlock, (ULONG) TRACK_SIZE + (ULONG) LABEL_LENGTH);
      }

   if (iotd_SecLabel)
      {
         FreeMem(iotd_SecLabel, (ULONG) LABEL_LENGTH);
      }

   if (text_buf)
      {
         FreeMem(text_buf, (ULONG) (N + F - 1));
      }

   if (lson)
      {
         FreeMem(lson, (ULONG) (N + 1) * (ULONG) (sizeof(short)));
      }

   if (rson)
      {
         FreeMem(rson, (ULONG) (N + 257) * (ULONG) (sizeof(short)));
      }

   if (dad)
      {
         FreeMem(dad, (ULONG) (N + 1) * (ULONG) (sizeof(short)));
      }
}


Title()
{
   printf("\nLHWARP %d.%d%d - Amiga disk tracker - Written by Jonathan Forbes @ 1:250/642\n", (UBYTE) MAJOR_VERSION, (UBYTE) MINOR_VERSION, (UBYTE) DECIMAL_VERSION);
   puts("Copyright © Xenomiga Technology, 1990.\n");

   puts("Usage: LHWARP <Read|Write> <Unit> <Filename> <StartTrack> <EndTrack> <TextFile>\n");

   puts("<Unit>       - Drive number (0 for internal, 1 ... 3 for external)");
   puts("<Filename>   - Output|Input filename");
   puts("<StartTrack> - Track number (0 ... 79) [only valid in read mode]");
   puts("<EndTrack>   - Track number (0 ... 79) [only valid in read mode]");
   puts("<TFile>      - Append text in <TextFile> to output file [optional]\n");
}


ULONG GetFileLength(register char *FileToOpen)
{
   register ULONG                 FileLength;
   register ULONG                 lock;
   register struct FileInfoBlock *fileinfo;

   if (access(FileToOpen, 0))
      {
         return (0L);
      }

   fileinfo = (struct FileInfoBlock *) AllocMem(sizeof(struct FileInfoBlock),
               MEMF_CLEAR | MEMF_PUBLIC);

   if (!fileinfo)
      {
         printf("\n\r\n\rUtterly fatal error: Out of memory\n\r\n\r");
         return (NULL);
      }

   lock     = (ULONG) Lock(FileToOpen, ACCESS_READ);

   if (!lock)
      {
         FreeMem(fileinfo, sizeof(struct FileInfoBlock));
         return (NULL);
      }

   Examine(lock, fileinfo);
   FileLength = fileinfo->fib_Size;
   UnLock(lock);
   FreeMem(fileinfo, sizeof(struct FileInfoBlock));
   return (FileLength);
}


/* If the user presses ^C */
int CXBRK()
{
   if (WriteMsg)
      {
         WriteMsg->iotd_Req.io_Length  = 0L;
         WriteMsg->iotd_Req.io_Command = TD_MOTOR;
         DoIO((struct IORequest *) WriteMsg);
      }

   if (ReadMsg)
      {
         ReadMsg->iotd_Req.io_Length  = 0L;
         ReadMsg->iotd_Req.io_Command = TD_MOTOR;
         DoIO((struct IORequest *) ReadMsg);
      }

   CleanUp();
   return (-1);
}


ULONG DoSum(UBYTE *Block, ULONG Length)
{
   register ULONG Sum = 0;
   register ULONG i;

   for (i = 0; i < Length; i++)
      {
         Sum = Sum + Block[i];
      }

   return (Sum);
}
