/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *      (C) Copyright 1990/91 by Peter Vorwerk
 *
 *      Name .....: ATCopy.c
 *      Created ..: Sunday 09-Sep-90 15:12
 *      Revision .: 22
 *
 *      Date            Author          Comment
 *      =========       ========        ====================
 *      29-Apr-91       Peter Vorwerk   Recompiled with 5.0d
 *      05-Feb-91       Peter Vorwerk   Now will find XT's
 *      ---------       -------------   ------- 2.2 -----------
 *      12-Jan-91       Peter Vorwerk   New Intro
 *      12-Jan-91       Peter Vorwerk   Bugfixing for SideCar's
 *      10-Jan-91       Peter Vorwerk   New FileRequester
 *      08-Jan-91       Peter Vorwerk   Bugfixing Conversion of char's
 *      07-Jan-91       Peter Vorwerk   Speed up OPT B option
 *      ---------       -------------   ------- 2.1 ------------
 *      01-Jan-91       Peter Vorwerk   Two versions
 *      29-Dec-90       Peter Vorwerk   Bugfixing (File size)
 *      ---------       -------------   ------- 2.0 ------------
 *      11-Nov-90       Peter Vorwerk   Bugfixing (FileRequester)
 *      29-Oct-90       Peter Vorwerk   PC Namenkonventionen (2.0)
 *      27-Oct-90       Peter Vorwerk   Bugfixing
 *      27-Oct-90       Peter Vorwerk   PC part resident
 *      22-Oct-90       Peter Vorwerk   Bugfixing
 *      22-Oct-90       Peter Vorwerk   Multi buffers (PD <-> SHARE)
 *      19-Oct-90       Peter Vorwerk   8 small (128) buffers
 *      18-Oct-90       Peter Vorwerk   Bugfixing
 *      17-Oct-90       Peter Vorwerk   Double Buffer
 *      15-Oct-90       Peter Vorwerk   Bugfixing
 *      10-Oct-90       Peter Vorwerk   Allocates JanusRAM
 *      21-Sep-90       Peter Vorwerk   Full WB Support
 *      15-Sep-90       Peter Vorwerk   Suports conversion
 *      14-Sep-90       Peter Vorwerk   Bugfixing
 *      13-Sep-90       Peter Vorwerk   Now suports joker
 *      13-Sep-90       Peter Vorwerk   Multi File no Wildcards
 *      10-Sep-90       Peter Vorwerk   Stop at Timeout
 *      10-Sep-90       Peter Vorwerk   Bugs fixed
 *      09-Sep-90       Peter Vorwerk   Eine Datei keine Konversion
 *      09-Sep-90       Peter Vorwerk   Created this file!
 *
 * $Revision Header ********************************************************/
 #define REVISION 22

#define VERSION 2

#include <stdio.h>
#include <exec/types.h>       /* Include-Files laden */
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/libraries.h>
#include <exec/interrupts.h>
#include <devices/trackdisk.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/arpbase.h>
#include <libraries/filehandler.h>
#include <workbench/startup.h>
#include <intuition/intuition.h>
#include <string.h>
#include <janus/janus.h>

#include <KDBase.h>

char *Copy = "ATCopy2.22 (c) 29.04.1991 by Peter Vorwerk";

extern char *scdir(char *);

#ifdef TEST
long cumsec = 0,cummic = 0,sec1,sec2,mic1,mic2;
#endif

#define NUMBUF   24
#define DATA_LEN 129+1+1

/* 129 = 128 + 1 = Bufferlänge plus eins für überlange Blocke */
/* 1 für akktuelle Blocklänge (neu ab V2.1) */
/* 1 für Flag je Block */

#define BLOCK_LEN 129+1

/* 129 = 128 + 1 = Bufferlänge plus eins für überlange Blocke */
/* 1 für akktuelle Blocklänge (neu ab V2.1) */

#define B    'B'
#define NC   'N'
#define CR   'C'
#define NONE '\0'

/* Offset's für Parameter RAM (16K) */
/* Diese Werte gelten sowohl für die PC- als auch für die AT-Karte. */

#define FileFlag0 BASIS
#define FileData0 BASIS + NUMBUF

#define FILENAME   0x10
#define NEWDATA    0x20
#define LASTDATA   0x30
#define CLOSEFILE  0x40
#define EMERGENCY  0x50

#define SLEEP      0x60
/* Wird NOCH nicht benötigt, da das Programm auf dem PC/AT
   noch nicht resident mitläuft. Erst ab V2.0. */

#ifndef MAXTIME
#define MAXTIME 20
#endif

#define AT      1
#define XT      2
#define SideCar 3

struct AlertMessage
{
    SHORT LeftEdge;
    BYTE  TopEdge;
    char  AlertText[50];
    BYTE  Flag;
};

struct Library   *IconBase    = NULL;
struct Library   *JanusBase   = NULL;
struct Library   *KD_FReqBase = NULL;
UBYTE            *Flag        = NULL;
UBYTE            *Data        = NULL;
FILE             *fh          = NULL;
UBYTE            directory[128];
UBYTE            filename[32];
UBYTE            pattern[32];
struct ExtraData extras;

extern struct AlertMessage TimeOut[];

#define TIMEOUT 80L

extern struct AlertMessage NoLib[];

#define NOLIB 80L

char tabelle[128] =
{
    /* 0x80 */ 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
    /* 0x88 */ 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
    /* 0x90 */ 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
    /* 0x98 */ 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
    /* 0xa0 */ 0x7f,0xad,0x9b,0x9c,0x7f,0x9d,0x7f,0x7f,
    /* 0xa8 */ 0x7f,0x7f,0xa6,0xae,0xaa,0x7f,0x7f,0xfe,
    /* 0xb0 */ 0xf8,0xf1,0xfd,0x7f,0x7f,0xe6,0x7f,0xf9,
    /* 0xb8 */ 0x7f,0x7f,0xa7,0xaf,0xac,0xab,0x7f,0xa8,
    /* 0xc0 */ 0x7f,0x7f,0x7f,0x7f,0x8e,0x8f,0x92,0x80,
    /* 0xc8 */ 0x7f,0x90,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
    /* 0xd0 */ 0x7f,0xa5,0x7f,0x7f,0x7f,0x7f,0x99,0x7f,
    /* 0xd8 */ 0x7f,0x7f,0x7f,0x7f,0x9a,0x7f,0x7f,0xe1,
    /* 0xe0 */ 0x85,0xa0,0x83,0x7f,0x84,0x86,0x91,0x87,
    /* 0xe8 */ 0x8a,0x82,0x88,0x89,0x8d,0xa1,0x8c,0x8b,
    /* 0xf0 */ 0xeb,0xa4,0x95,0xa2,0x93,0x7f,0x94,0xf6,
    /* 0xf8 */ 0xed,0x97,0xa3,0x96,0x81,0x7f,0x7f,0x98
};

/* In BASIS wird die BasisAdresse der PC/AT eingetragen. */

APTR BASIS = NULL;

char ATCP[16];

extern struct NewWindow NewWindowStructure1;
extern struct IntuiText IText9;

void Version(void)
{
    SPrintf(ATCP,"ATCopy V%ld.%ld",VERSION,REVISION);
    NewWindowStructure1.Title = (UBYTE *) ATCP;
    IText9.IText = (UBYTE *) ATCP;
}

void CheckForPC(void)
{
/* Ermitteln, ob und wenn ja ab welcher Adresse die PC/AT Karte ansprechbar ist. */

    if ((BASIS = AllocJanusMem(DATA_LEN * NUMBUF,MEMF_PARAMETER | MEM_BYTEACCESS)) == NULL)
    {
       DisplayAlert(RECOVERY_ALERT,(char *) &NoLib,NOLIB);
       exit(20);
    }
}

int CheckPC(unsigned char *ptr)
{
    ptr += 0x7fff7;
/* Modus Register der XT/AT Karte siehe 'Amiga SYSTEM-Handbuch' */

    if (*ptr >= 0xe0 || *ptr == 0)
    {
        return(SideCar);
    }
/* Beim SideCar ist dieses Register mit DIP-Schltern aufgebaut.
   Daher die merkwürdige Abfrage. */
    if (*ptr < 128)
    {
        return(AT);
    }
    return(XT);
}

void WakeUpPC(void)
{
    unsigned char   *ptr,*key,*intr;
    int    i;
    UWORD  *offset;
    long   stsecs,stmics,ensecs,enmics;

/* Auslösen eines (Software-) Interupts auf dem PC/AT um das schlafende Programm
   zu wecken! */

/* Zur Zeit ( noch ? ) nicht so möglich! Es wird entweder das Programm AT.exe
   oder das Programm XT.exe auf dem PC aufgerufen. Dazu werden die Zeichen
   A (X), T und {CR} direkt zum PC gesendet. Simulierte Tastatur. */

/* Die Keyboard Addresse ist als positiver Offset 0x72 in der Janus.library abgelegt. */
/* Achtung! Im 'Amiga SYSTEM-Handbuch wird die feste Addresse 0x7ffff als Keyboard Addresse
   genannt. Bei meinem Amiga 2000 mit AT Karte stimmte dies, jedoch nicht beim SideCar.

   Die Offset Struktur wurde experimentell ( teilweiser Speicherdump vom Programm
   PCWINDOW ) ermittelt. Zum Glück stimmte wenigstens die Adresse des Interrupts,
   so konnte ich im Speicher nach dem Auftreten dieser Kombination suchen und die
   nähere Umgebung disassemblieren. */

    key     = (unsigned char *) JanusBase;
    key    += 0x72;
    offset  = (UWORD *) key;
    ptr     = (unsigned char *) GetJanusStart();
    key     = ptr + 0x7e000;
    key    += *offset;
    intr    = ptr + 0x7fffb; /* Addresse aus 'Amiga SYSTEM-Handbuch' */
    *key    = '\034';    /* {CR} */
    *intr   = '\377';    /* 0xff */
    Delay(1);
    i       = CheckPC(ptr);
    if (i == AT)
    {
        *key = '\036'; /* 'A'  */
    }
    else
    {
        *key = '\055'; /* 'X'  */
    }
    *intr     = '\377';    /* 0xff */
    Delay(1);
    *key      = '\024';    /* 'T'  */
    *intr     = '\377';    /* 0xff */
    Delay(1);
    *key      = '\034';    /* {CR} */
    *intr     = '\377';    /* 0xff */

/* Bei diesen Codes handelt es sich natürlich um die Roh-Codes der PC-Tastatur ! */
/* Siehe Handbücher zu Turbo Pascal o.ä. */

    ptr = (unsigned char *) BASIS;
    *ptr++ = '\011'; /* Länge des Strings "ATCopy2.1" octal ! */
    strcpy((char *) ptr,"ATCopy2.1");
/* Kein Änderung im Protokoll => 2.1 und 2.2 können gemischt werden.
   Aber nicht 2.0 mit 2.x !!! */

/* Die Startaddresse des angeforderten Buffers wird mit dem Text markiert und
   danach vom PC aus gesucht. Siehe PCCOPY.PAS */

    ptr--;
    CurrentTime(&stsecs,&stmics);
    while(*ptr != 0)
    {
        Chk_Abort();
        CurrentTime(&ensecs,&enmics);
/* Wenn der XT/AT nich innerhalb von 20 Sekunden antworted das Programm abbrechen. */
        if (ensecs - stsecs > MAXTIME * 2)
        {
            DisplayAlert(RECOVERY_ALERT,(char *) &TimeOut,TIMEOUT);
            FreeTrackedItem(GetTracker(TRAK_FREQ));
            FreeJanusMem(BASIS,DATA_LEN * NUMBUF);
            exit(20);
        }
    }
    Flag = (UBYTE *) FileFlag0;
    for(i = 0; i < NUMBUF; i++)
    {
        *Flag++ = 0;
    }
/* Alle Buffer sind leer und können benutzt werden. */
}

void _abort(void)
{

#ifdef TEST
    Printf("\nInsgesamt %ld µsec gewarted.\n",cumsec*1000000+cummic);
#endif

#ifdef DEBUG
    Printf("\nLast Flag = <%lx>\n",*Flag);
#endif

    *Flag = EMERGENCY;
    FreeTrackedItem(GetTracker(TRAK_FREQ));
    FreeJanusMem(BASIS,DATA_LEN * NUMBUF);
    if (fh)
        fclose(fh); /* Da hier Programmende kann <fh = NULL;> entfallen. */
    exit(20);
}

void timeout(void)
{
    long stsecs,stmics,ensecs,enmics;

/* Prüfen, ob der Kanal frei ist und ggf. darauf warten. Dauert es
   allerdings länger als 10 Sekunden => Abbruch. */

#ifdef TEST
    CurrentTime(&sec1,&mic1);
#endif

    CurrentTime(&stsecs,&stmics);
    while(*Flag != 0)
    {
        Delay(1);
        Chk_Abort();
        CurrentTime(&ensecs,&enmics);
        if (ensecs - stsecs > MAXTIME)
        {

#ifdef TEST
    CurrentTime(&sec2,&mic2);
    cumsec += sec2-sec1;
    cummic += mic2-mic1;

    Printf("\nInsgesamt %ld µsec gewarted.\n",cumsec*1000000+cummic);
#endif

#ifdef DEBUG
            Printf("\nLast Flag = <%lx>\n",*Flag);
#endif

            *Flag = EMERGENCY;
            DisplayAlert(RECOVERY_ALERT,(char *) &TimeOut,TIMEOUT);
            FreeTrackedItem(GetTracker(TRAK_FREQ));
            FreeJanusMem(BASIS,DATA_LEN * NUMBUF);
            if (fh)
                fclose(fh); /* Da hier Progende kann fh = NULL; entfallen. */
            exit(20);
        }
    }
#ifdef TEST
    CurrentTime(&sec2,&mic2);
    cumsec += sec2-sec1;
    cummic += mic2-mic1;
#endif
}

extern int Konvert(int wert, char opt, int *flag);

extern int transfer(UBYTE * data, char opt);

int copyfile2(char *str, char *PC, char opt)
{
   register int         c;
   static int           last = 0;
   int                  flag2 = FALSE;
   register char        *filename;
   register UBYTE       *data;
   char                 *s,buffer[80],buffer2[60];

   if (*str == '\0')
   {
       return(FALSE);
   }

   if (strlen(PC) > 0)
   {
       s = &PC[strlen(PC)-1];
       if (*s != ':' && *s != '\\')
       {

/* Der PC-Pfad muß mit ':' oder '\\' enden. */
           s++;
           *s++ = '\\';
           *s = '\0';
       }
   }

   filename = BaseName(str);
   s = buffer2;
   do
   {
       *s = *filename;
       if (*s == '\0')
           break;
/* Nicht alle Zeichen sind beim PC für Dateinamen erlaubt. */

       if (*s <  '\041' || *s == '\\' ||
           *s == '['    || *s == ']'  ||
           *s == '|'    || *s >  '\177')
       {
           filename++;
           s--;
           continue;
       }
       if (*s == '.')
       {
           filename = strrchr(filename,'.');
       }
/* Suchen des letzten '.' nur eine Extension ist beim PC/AT möglich. */
       filename++;
   }
   while(*s++ != '\0');
   if (buffer2[0] == '.' || buffer2[0] == '\0')
   {
      return(TRUE);
   }
/* Der Name darf nicht leer "" sein oder nur aus einer Extension bestehen. */
   if ((fh = fopen(str,"r")) == NULL)
   {
       return(FALSE);
   }
/* File läst sich auf der Amigaseite nicht öffnen. => Programm abbrechen. */
   strcpy(buffer,PC);
   strcat(buffer,buffer2);
   timeout();
   data = Data;
   c = strlen(buffer);
   *data++ = c;
   filename = buffer;
   while(*filename != '\0')
   {
       *data++ = *filename++;
   }
/* Dateiname übertragen. Längenbyte gefolgt vom Text ; PASCAL-Format. */
   *Flag = FILENAME;
   if (*Flag != FILENAME && *Flag != 0)
        *Flag = EMERGENCY;
/* Wenn direkt nach dem Schreibzugriff NICHT der Wert im Speicher steht ist
   ein Fehler aufgetreten. Sollte NIE vorkommem. => Programm abbrechen. */
   do
   {
       Flag++;
       Data += BLOCK_LEN;
       last++;
/* Umschalten auf den nächsten Buffer. Ringpuffer */
       if (last == NUMBUF)
       {
           last = 0;
           Flag = (UBYTE *) FileFlag0;
           Data = (UBYTE *) FileData0;
       }
   }
   while(transfer(Data,opt));
   Flag++;
   Data += BLOCK_LEN;
   last++;
   if (last == NUMBUF)
   {
       last = 0;
       Flag = (UBYTE *) FileFlag0;
       Data = (UBYTE *) FileData0;
   }
   fclose(fh);
   fh = NULL;
/* FileHandle löschen damit bei einem Fehler nicht die Datei noch einmal
   geschlossen wird. Der GURU läst grüßen! */
   return(TRUE);
}

extern char Intro(char **pc,char *opt);

int copyfile(char *str, char *PC, char *Option)
{
   int             flag;
   char            buffer[162],opt;
   struct FileList *ext;

   if (PC == NULL || *PC == '\0')
      PC = "\0     default PC-path";

   if (Option == NULL)
      Option = "\0  Option"; /* Hier die gewünschte 'Default' Option eintragen. */

   opt = Intro(&PC,Option);
   if (opt == 255 || opt == -1)
      return(FALSE);
   *Option = opt;
/* Per Requester ausgewählte Option speichern. */
   if (str == NULL || *str == '\0')
   {
/* Wenn kein Dateiname per CLI oder WB übergeben wurde diesen per Requester anfordern. */
       filename[0] = 0;
       extras.oktext     = (UBYTE *) "Copy";
       extras.canceltext = (UBYTE *) "No Way!";
       if (FReq(NULL, (UBYTE *) ATCP,filename,directory,pattern, FR_CANCELTEXT |
                      FR_EXTENDEDSELECT | FR_OKTEXT | FR_AUTOPOSITION | FR_AUTOSIZE |
                      FR_NOINFO | FR_SCREENFONT ,&extras))
       {
           flag = FALSE;
           if (extras.ExtendedList)
           {
/* Wenn Extendet Select, siehe KR_FREQ.DOC, gewählt wurde NUR diese auswerten. */
               ext = extras.ExtendedList;
               for (; ext; ext = ext->next)
               {
                   strcpy(buffer,(char *) directory);
                   strcat(buffer,(char *) ext->FileName);
                   flag = copyfile2(buffer,PC,opt);
               }
               FreeMem(extras.ExtendedList,extras.ExtendedList->private);
               extras.ExtendedList = NULL;
           }
           else
           {
/* Ansonsten den Dateinamen lesen. */
               strcpy(buffer,(char *) directory);
               strcat(buffer,(char *) filename);
               flag = copyfile2(buffer,PC,opt);
           }
       }
       else
       {
/* Keine Auswahl bzw. CAncel gewählt => ENDE! */
           return(FALSE);
       }
   }
   else
   {
/* Dateinamen per CLI oder WB vorgegeben. */
       flag = copyfile2(str,PC,opt);
   }
   return(flag);
}

void main(int argc, char *argv[])
{
    register char     **demo;
    register char     *s;
    int               wbmax,i;
    struct DiskObject *Do;
    struct WBStartup  *Wbs;
    struct Process    *proc = (struct Process *)FindTask(NULL);
    struct FileLock   *origlock;

    Version();

    directory[0] = filename[0] = pattern[0] = 0;

/* Lib's öffnen. */

    if (!(JanusBase = ArpOpenLibrary("janus.library",0L)))
    {
        DisplayAlert(RECOVERY_ALERT,(char *) &NoLib,NOLIB);
        exit(10);
    }

    if (!(KD_FReqBase = ArpOpenLibrary(KLIBNAME,KLIBVERSION)))
    {
        Puts("KD_FREQ.LIBRARY fehlt!\n");
        exit(10);
    }


    CheckForPC(); /* Ermitteln der Basisadresse. */

    WakeUpPC(); /* AT.EXE oder XT.EXE aufrufen. */

    Flag = (UBYTE *) FileFlag0;
    Data = (UBYTE *) FileData0;

    if (argc > 0)
    {
        demo = (char **) argv[1];
        if (demo != NULL)
        {
            if (*demo != NULL)
            {
                while(*demo != NULL)
                {
/* Wenn Jokerzeichen im Argument sind, diese Auswerten mit scdir(). */

                    while(s = scdir(*demo))
                    {
/* S ist aufgelöster Name, der zum Joker paßt. */

                        copyfile(s,argv[2],argv[3]);
                    }
                    demo++;
                }
            }
            else
            {
                do ; while(copyfile(NULL,argv[2],argv[3]));
            }
        }
        else
        {
            do ; while(copyfile(NULL,argv[2],argv[3]));
        }
    }
    else /* WB Aufruf */
    {
        origlock = (struct FileLock *) CurrentDir(DupLock((BPTR) proc->pr_CurrentDir));
        Wbs = (struct WBStartup *) argv;
        if (!(IconBase = ArpOpenLibrary("icon.library",0L)))
        {
            exit(10);
        }
        if (Wbs->sm_ArgList[0].wa_Lock)
        {
            struct FileLock *savelock = (struct FileLock *) CurrentDir(Wbs->sm_ArgList[0].wa_Lock);
            if (Do = (struct DiskObject *) GetDiskObject(Wbs->sm_ArgList[0].wa_Name))
            {
                FreeDiskObject(Do);
            }
            CurrentDir((BPTR) savelock);
        }
        if ((wbmax = Wbs->sm_NumArgs - 1) == 0)
        {
            do ; while(copyfile(NULL,NULL,NULL)); /* Ganz ohne Parameter zB WorkBench */
        }
        else
        {
            for(i = 0; i < wbmax; i++)
            {
                UnLock(CurrentDir(DupLock((BPTR) Wbs->sm_ArgList[i+1].wa_Lock)));
                if (Do = (struct DiskObject *) GetDiskObject(Wbs->sm_ArgList[i+1].wa_Name))
                {
                    FreeDiskObject(Do);
                }
                copyfile(Wbs->sm_ArgList[i+1].wa_Name,NULL,NULL);
            }
        }
        origlock = (struct FileLock *) CurrentDir((BPTR) origlock);
    }
    timeout();
    *Flag = EMERGENCY;
    timeout(); /* Warten bis XT/AT fertig ist. Dann Speicher freigeben! */

#ifdef TEST
    Printf("\nInsgesamt %ld µsec gewarted.\n",cumsec*1000000+cummic);
#endif

    FreeTrackedItem(GetTracker(TRAK_FREQ));
    FreeJanusMem(BASIS,DATA_LEN * NUMBUF);

}
/* Das war's. */
