/* main.c - Entry point plus startup and shutdown routines
** Copyright (C) 1997,1998 Karl J. Ots
** 
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
** 
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*----------------*/
/* Main procedure */
/*----------------*/

/* COMPILE_LITE takes precedence over COMPILE_RT */
#ifdef COMPILE_LITE
#  ifdef COMPILE_RT
#    undef COMPILE_RT
#  endif /* COMPILE_RT */
#endif /* COMPILE_LITE */

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>
#ifdef COMPILE_RT
#  include <exec/libraries.h>
#endif /* COMPILE_RT */
#include <devices/trackdisk.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

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

#include "main.h"
#include "args.h"
#include "mem_chunks.h"
#include "td.h"
#include "read_disk.h"
#include "write_disk.h"
#ifndef COMPILE_LITE
#  include "defl_disk.h"
#  include "infl_disk.h"
#endif /* COMPILE_LITE */
#include "util.h"
#include "errors.h"
#include "version.h"


/*---------------------------------*/
/* Global constants and structures */
/*---------------------------------*/

const char breakText[]   = "***Break";

/*----------------------------*/
/* Standatd Input/Ouput/Error */
/*----------------------------*/

BPTR StdIn, StdOut, StdErr;
STRPTR ProgName;

/*
** Set handles for DOS Library StdIO functions, as well as
** find our command name.
*/
void setStdIO (void)
{
  struct Process *Proc = (struct Process *)FindTask (NULL);
  
  StdIn  = Proc->pr_CIS;  /* Quicker than Input() or Output() */
  StdOut = Proc->pr_COS;
  StdErr = Proc->pr_CES;
  if (!StdErr)
  {
    /* Use the file handle from the file number of stdio FILE stderr */
    StdErr = (BPTR) fdtofh (fileno (stderr));
    if (!StdErr) StdErr = StdOut;
  }
}


/*-------------------------------------------*/
/* Global variables and associated functions */
/*-------------------------------------------*/

#ifdef COMPILE_RT
/* z.library base pointer */
struct Library *ZBase;
#endif /* COMPILE_RT */

/* files */
BPTR   ADFile;
STRPTR DOSDev;

/* io */
struct MsgPort *diskPort;
struct IOExtTD *diskReq;
BOOL   TDOpen;


/*
** Initialise all globals to their default values.
*/
void initGlobals (void)
{
#ifdef COMPILE_RT
  ZBase = NULL;
#endif /* COMPILE_RT */
  
  setStdIO();
  
  /* Iniitalise our memory list */
  initMemChunkList();
  
  ADFile = NULL;
  DOSDev = NULL;
  
  diskPort = NULL;
  diskReq = NULL;
  TDOpen = FALSE;
}


/*
** Exit cleanly.
*/
void cleanExit (ULONG rc, LONG rc2)
{
  /* Free all allocated memory */
  deleteMemChunkList();
  
  if (TDOpen)
  {
    /* Flush any buffers that may be unwritten */
    flushTrack (diskReq);
    
    /* Turn off the motor */
    diskReq->iotd_Req.io_Command = TD_MOTOR;
    diskReq->iotd_Req.io_Flags = 0;
    diskReq->iotd_Req.io_Length = 0;
    DoIO ((struct IORequest *)diskReq);
    
    CloseDevice ((struct IORequest *)diskReq);
  }
  
  if (diskReq) DeleteIORequest ((APTR)diskReq);
  if (diskPort) DeleteMsgPort (diskPort);
  
  if (DOSDev) Inhibit (DOSDev,FALSE);
  if (ADFile) Close (ADFile);
  
  /* Turn the cursor on */
  FPuts (StdOut, "\033[1 p"); 
  Flush (StdOut);
  
#ifdef COMPILE_RT
  if (ZBase) CloseLibrary (ZBase);
#endif /* COMPILE_RT */
  
  SetIoErr (rc2);
  exit (rc);
}


/*---------------*/
/* Main function */
/*---------------*/
int main (int argc, char *argv[])
{
  struct ADF_Packet adf_packet = {NULL, NULL, NULL, NULL, NULL, NULL, FALSE};
  struct TA_Args *taArgs;
  
  LONG   diskUnit;
  STRPTR ADFileName, origName;
  ULONG  startTrack, endTrack, cLevel;
  ULONG  fileType = 0;
  
  BOOL   WriteDisk, Verify;
  LONG   ADFOpenMode = MODE_NEWFILE;  /* Default is to write to file */
  BYTE   TDError;
  LONG   DOSError;
  
  
  /* Initialise any global data */
  initGlobals();
  ProgName = FilePart (argv[0]);

  /* Collect arguments */
  taArgs = getArgs ();
  if (!taArgs) outputUsage ();

#ifdef COMPILE_RT
  /* Open z.library */
  ZBase = OpenLibrary ("z.library",0);
  if (!ZBase)
  {
    FPrintf (StdErr, "%s: Couldn't open z.library.\n", ProgName);
    cleanExit (RETURN_FAIL, NULL);
  }
#endif /* COMPILE_RT */

  diskUnit   = Name2Unit (taArgs->Drive);
  ADFileName = taArgs->File;
  startTrack = taArgs->Start;
  endTrack   = taArgs->End;
  WriteDisk  = taArgs->WriteDisk;
  Verify     = taArgs->Verify;
  
#ifndef COMPILE_LITE
  if      (taArgs->ZLib)  fileType = FT_ZLIB;  
  else if (taArgs->GZip)  fileType = FT_GZIP;  
  else if (taArgs->PKZip) 
  {
    if (taArgs->PKZAdd)   fileType = FT_PKZIP_ADD;
    else                  fileType = FT_PKZIP;
  }
  origName = taArgs->OrigName;  
  cLevel = taArgs->Level;
#endif /* COMPILE_LITE */
  
  if (diskUnit < 0)
  {
    FPrintf (StdErr, "%s: Error - Invalid drive.\n",ProgName);
    cleanExit (RETURN_FAIL, NULL);
  }
  if ((startTrack < 0) || (startTrack > endTrack))
  {
    FPrintf (StdErr, "%s: Invalid Start/End track.\n", ProgName);
    cleanExit (RETURN_FAIL, NULL);
  }
  if (endTrack > 159)
    FPrintf (StdErr, "%s: Warning - Extended tracks specified, transfer may fail.\n",ProgName);
  
  /* Inhibit DOS access to the drive we'll be operating on */  
  DOSDev = taArgs->Drive;
  if ( !Inhibit (DOSDev, DOSTRUE) )
  {
    DOSError = IoErr();
    FPrintf (StdErr, "%s: Error - Couldn't inhibit DOS access to %s.\n",
                     ProgName, DOSDev);
    DOSDev = NULL;
    cleanExit (RETURN_FAIL, DOSError);
  }
  
  /* Open the Amiga Disk File */
  if (WriteDisk) ADFOpenMode = MODE_OLDFILE;  /* Reading from file */
  else if (fileType == FT_PKZIP_ADD) ADFOpenMode = MODE_READWRITE;
  ADFile = Open (ADFileName, ADFOpenMode);
  if (!ADFile)
  {
    DOSError = IoErr();
    FPrintf (StdErr, "%s: Couldn't open %s for %s - ", ProgName, ADFileName,
                     (WriteDisk ? "input" : "output"));
    reportDOSError (DOSError);
    cleanExit (RETURN_FAIL, DOSError);
  }
  
  /* Attempt to create a MsgPort */
  diskPort = CreateMsgPort();
  if (!diskPort)
  {
    FPrintf (StdErr, 
             "%s: Error - Couldn't create a Message Port for TrackDisk IO.\n",
             ProgName);
    cleanExit (RETURN_FAIL, NULL);
  };
  
  /* Attempt to create an IORequest */
  diskReq = (struct IOExtTD *)
            CreateIORequest (diskPort, sizeof (struct IOExtTD));
  if (!diskReq)
  {
    FPrintf (StdErr,
             "%s: Error - Couldn't create a Request for TrackDisk IO.\n",
             ProgName);
    cleanExit (RETURN_FAIL, NULL);
  }
  
  /* Attempt to open trackdisk.device */
  TDError = OpenDevice (TD_NAME, diskUnit, (struct IORequest *)diskReq,NULL);
  if (TDError)
  {
    FPrintf (StdErr, "%s: Couldn't open trackdisk.device - ", ProgName);
    reportTDError (TDError);
    cleanExit (RETURN_FAIL, NULL);
  }
  else
    TDOpen = TRUE;
  
  /* The trackdisk.device in now open and ready for use */
  
  /* Turn off the cursor */
  FPuts (StdOut, "\033[0 p"); Flush (StdOut);
  
  /* Load packet */
  adf_packet.diskReq    = diskReq;
  adf_packet.diskUnit   = diskUnit;
  adf_packet.ADFile     = ADFile;
  adf_packet.ADFileName = ADFileName;
  adf_packet.startTrack = startTrack;
  adf_packet.endTrack   = endTrack;
  adf_packet.verify     = Verify;
  
  if (WriteDisk)
  {
    /* We're reading from the file and writing to the disk */
#ifndef COMPILE_LITE
    fileType = getFileType (ADFile);
    
    if ((fileType == FT_ZLIB) || 
        (fileType == FT_GZIP) ||
        (fileType == FT_PKZIP))
      inflDisk (&adf_packet, origName, fileType);
    else
#endif /* COMPILE_LITE */
      writeDisk (&adf_packet);
  }
  else
  {
    /* We're reading from the disk and writing to the file */
#ifndef COMPILE_LITE
    if (fileType)
      deflDisk (&adf_packet, cLevel, origName, fileType);
    else
#endif /* COMPILE_LITE */
      readDisk (&adf_packet);
  }
  
  
  FPuts (StdOut, "\nDone.\n");
  
  /* Let's get outa here! */
  cleanExit (RETURN_OK, NULL);
  
  /* Keep the compiler happy */
  return 0;
}
