/*************************************************/
/* EasyBackup, Support.c (c)1989 Oliver Enseling */
/*************************************************/

#include <exec/types.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <devices/trackdisk.h>
#include <intuition/intuition.h>
#include <dos.h>
#include <proto/all.h>
#include <devices/audio.h>
#include <intuition/intuition.h>
#include <stdlib.h>
#include <ctype.h>

#define TRACK_SIZE (11 * TD_SECTOR)
#define LABEL_SIZE (11 * 16)
#define MAX_TRACKS 160

#define MAX_SECTORS (11 * MAX_TRACKS)
#define CONFIG_NAME "S:EasyBackup.config"
#define BUFFER_SIZE (1024)             /* 1K File Buffer */

#define DATEIENDEMARKE '£'             /* empirisch sehr wenig
                                        * vorkommendes Zeichen */

#define ID_FILE 0
#define ID_DIR  1
#define ID_ENDDIR 2

struct NewWindow TrackNW =
{20, 20, 200, 50, 0, 1, NULL, SMART_REFRESH, NULL, NULL,
 (UBYTE *) "EasyBackup Disk-Status",
 NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN};
struct Window  *TrackWin = NULL;

struct IntuitionBase *IntuitionBase;

extern int      _OSERR;

BYTE           *TrackBuffer, *LabelBuffer, *FileBuffer;
struct IOExtTD *TrackDiskBlock;

/* MakeDB: Device-Block anlegen und initialisieren */

void           *
MakeDB(len)
    LONG            len;
{
  struct MsgPort *p;
  APTR            req;

  if (!(p = (struct MsgPort *) CreatePort(0L, 0L)))
    return (NULL);
  if (!(req = (APTR) CreateExtIO(p, len))) {
    DeletePort(p);
    return (NULL);
  }
  return (req);
}

/* FreeDB: Device-Block freigeben */

void
FreeDB(req)
    struct IORequest *req;
{
  if (req != 0L) {
    if (req->io_Message.mn_ReplyPort != 0L)
      DeletePort(req->io_Message.mn_ReplyPort);
    DeleteExtIO(req);
  }
}

/* InitDevice: Device öffnen und initialisieren */

BOOL
InitDevice(name, unit, req, fl, len)
    char           *name;
    LONG            unit;
    struct IORequest **req;
    ULONG           fl, len;
{
  if (len != 0L)
    if (!(*req = MakeDB(len)))
      return (FALSE);
  if (OpenDevice(name, unit, *req, fl))
    return (FALSE);
  else
    return (TRUE);
}

/* ExitDevice: Device schließen und Device-Block freigeben */

void
ExitDevice(req)
    struct IORequest *req;
{
  if (req != 0L) {
    if (req->io_Message.mn_ReplyPort != 0L)
      DeletePort(req->io_Message.mn_ReplyPort);
    if (req->io_Device != 0L)
      CloseDevice(req);
    DeleteExtIO(req);
  }
}

/* DeviceCommmand: Device-Kommando ausführen */

void
DeviceCommand(db, com)
    struct IORequest *db;
    UWORD           com;
{
  db->io_Command = com;
  DoIO(db);
}

/* Fehlermeldungen des Trackdisk.device */

#ifdef DEUTSCH
char           *Errors[] =
{
  "Nicht bestimmt.",
  "Kein Sektorkopf.",
  "Fehlerhafte Sektorpräambel.",
  "Fehlerhafte Sektoridentifikation.",
  "Fehlerhafte Sektorkopfsumme.",
  "Fehlerhafte Sektorsumme.",
  "Nicht genügend Sektoren.",
  "Fehlerhafter Sektorkopf.",
  "Diskette schreibgeschützt.",
  "Diskette gewechselt.",
  "Fehler beim Suchen.",
  "Nicht genügend Speicher.",
  "Falsche Laufwerksnummer.",
  "Falscher Laufwerkstyp.",
  "Laufwerk ist schon Betrieb.",
  "Zugriff nach System-Reset."};
#else
char           *Errors[] =
{
  "Not defined",
  "No sector header",
  "Wrong sector preambule",
  "Wrong sector id",
  "Wrong sector head checksum",
  "Wrong sector checksum",
  "Not enough sectors",
  "Wrong sector header",
  "Disk write-protected",
  "Diskette gewechselt.",
  "Search error",
  "Not enough memory",
  "Wrong drive number",
  "Wrong drive type",
  "Drive already in use",
  "Access past system reset"};
#endif

char           *
TrackDiskError(fn)
    LONG            fn;
{
  if ((fn >= 20) && (fn <= 35))
    return (Errors[fn - 20]);
#ifdef DEUTSCH
  else
    return ("Fehlerdiagnose nicht möglich.");
#else
  else
    return ("Unknown error");
#endif
}

/* WaitDisk: auf eine neue Diskette warten */

void
WaitDisk(db)
    struct IOExtTD *db;
{
  do {
    DeviceCommand(db, (UWORD) TD_CHANGESTATE);
  }
  while (db->iotd_Req.io_Actual == 0);
  do {
    DeviceCommand(db, (UWORD) TD_CHANGESTATE);
  }
  while (db->iotd_Req.io_Actual != 0);
}

/* ReadTrack: einen Track lesen */

void
ReadTrack(db, tbuf, lbuf, nr)
    struct IOExtTD *db;
    BYTE           *tbuf;
    BYTE           *lbuf;
    LONG            nr;
{
  FOREVER
  {
    DeviceCommand(db, (UWORD) TD_CHANGENUM);
    db->iotd_Count = db->iotd_Req.io_Actual;
    db->iotd_Req.io_Data = (APTR) tbuf;
    db->iotd_SecLabel = (ULONG) lbuf;
    db->iotd_Req.io_Length = TRACK_SIZE;
    db->iotd_Req.io_Offset = nr * TRACK_SIZE;
    DeviceCommand(db, (UWORD) ETD_READ);
    if (db->iotd_Req.io_Error) {
#ifdef DEUTSCH
      printf("Fehler beim Lesen von Spur #%d\n%s\n",
             nr, TrackDiskError((LONG) db->iotd_Req.io_Error));
#else
      printf("Error reading track Track #%d\n%s\n",
             nr, TrackDiskError((LONG) db->iotd_Req.io_Error));
#endif
      UserRequest();
    } else
      break;
  }
}

/* Alles geöffnete schließen und Programm beenden */

void
CloseAll(err)
    int             err;
{
  if (TrackWin) {
    CloseWindow(TrackWin);
    TrackWin = NULL;
  }
  if (IntuitionBase)
    CloseLibrary(IntuitionBase);
  if ((LONG) TrackDiskBlock->iotd_Req.io_Device != -1) {
    if (!TrackDiskBlock->iotd_Req.io_Error)
      EndBackup();
    ExitDevice(TrackDiskBlock);
  } else
    FreeDB(TrackDiskBlock);
  if (TrackBuffer)
    FreeMem(TrackBuffer, TRACK_SIZE + LABEL_SIZE);
  if (FileBuffer)
    FreeMem(FileBuffer, BUFFER_SIZE);
  exit (err);
}

/* UserRequest: Ja-Nein-Abfrage */

void
UserRequest()
{
  char            c;

#ifdef DEUTSCH
  printf("Abbrechen ? \033[32m(J/N)\033[31m");
#else
  printf("Abort ? \033[32m(Y/N)\033[31m");
#endif
  FOREVER
  {
    c = getchar();
#ifdef DEUTSCH
    if (toupper(c) == 'J') {
      printf("Abbruch !!!\n");
      CloseAll(1);
    }
#else
    if (toupper(c) == 'Y') {
      printf("Aborted\n");
      CloseAll(1);
    }
#endif
    if (toupper(c) == 'N')
      return;
  }
}


int
DoNothing()
{
  return (0);
}

/* Alles benötigte Öffnen */

void
OpenAll(devunit)
    int             devunit;
{
  if (onbreak(DoNothing))
#ifdef DEUTSCH
    printf("Fehler beim Installieren der Unterbrechungsabfangroutine !!!\n"
           "Bei Unterbrechen des Programmes mit Ctrl-C oder Ctrl-D\n"
           "kann Datenverlust entstehen !\n");
#else
    printf("Error installing routine to catch user breaks\n"
    "User breaks by Ctrl-C or Ctrl-D may lead into loss of data\n");
#endif
  if (!InitDevice("trackdisk.device", devunit, &TrackDiskBlock, 0,
                  sizeof(*TrackDiskBlock))) {
#ifdef DEUTSCH
    printf("Trackdisk.device kann nicht geöffnet werden !!!\n"
           "Prüfen sie, ob das Laufwerk vorhanden ist.\n");
#else
    printf("Unable to open trackdisk.device\n"
           "Check disk drive\n");
#endif
    exit(10);
  }
  if (!(TrackBuffer = AllocMem(TRACK_SIZE * 2 + LABEL_SIZE, MEMF_CHIP))) {
#ifdef DEUTSCH
    printf("Nicht genügend CHIP-Speicher !!!\n");
#else
    printf("Not enough CHIP-memory\n");
#endif
    CloseAll(20);
  }
  LabelBuffer = TrackBuffer + 2 * TRACK_SIZE;
  if (!(FileBuffer = AllocMem(BUFFER_SIZE, MEMF_PUBLIC))) {
#ifdef DEUTSCH
    printf("Nicht genügend Speicher für Datenpuffer (1K) !!!\n");
#else
    printf("Not enough memory for data buffer (1K)\n");
#endif
    CloseAll(20);
  }
  if (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0))
    TrackWin = OpenWindow(&TrackNW);
}

/*
 * ConcatPath: einen Dateinamen zusammensetzen p1: Directory oder
 * Device p2: Directory oder Filename
 */

void
ConcatPath(buffer, p1, p2)
    char           *buffer, *p1, *p2;
{
  strcpy(buffer, p1);
  if (strlen(p1) > 0 && strlen(p2) > 0)
    if (buffer[strlen(buffer) - 1] != ':')
      strcat(buffer, "/");
  strcat(buffer, p2);
}

/* FileAbfrage: Abfrage für interaktiven Modus */

BOOL
FileAbfrage()
{
  char            antwort[2];

  do {
#ifdef DEUTSCH
    printf("\033[33mJ\033[0ma/\033[33mN\033[0mein ");
#else
    printf("\033[33mY\033[0mes/\033[33mN\033[0mo ");
#endif
    scanf("%1s", antwort);
  }
#ifdef DEUTSCH
  while (tolower(antwort[0]) != 'j' && tolower(antwort[0]) != 'n');
  return ((BOOL) (tolower(antwort[0]) == 'j'));
#else
  while (tolower(antwort[0]) != 'y' && tolower(antwort[0]) != 'n');
  return ((BOOL) (tolower(antwort[0]) == 'y'));
#endif
}

/*
 * Beep: einen Ton variabler Höhe und Länge ausgeben freq: Frequenz
 * cycles: Anzahl der Delay-Zyklen (1/50-Sekunde)
 */

UBYTE           Channel_Map[] =
{1, 8, 2, 4};
BYTE            WaveForm[] =
{0, 39, 74, 102, 120, 127, 120, 102, 74, 39,
 0, -40, -75, -103, -121, -127, -121, -103, -75, -40};

struct IOAudio *
MakeIOA(rate, vol, repeat, buf, len)
    LONG            rate, vol, repeat;
    UBYTE          *buf;
    LONG            len;
{
  struct IOAudio *ioa;
  struct MsgPort *port;
  char           *PortName;

  PortName = "Beep-Port";

  /* IO-Request holen */
  if (ioa = (struct IOAudio *) AllocMem((LONG) sizeof(struct IOAudio),
                                        MEMF_PUBLIC | MEMF_CLEAR)) {
    ioa->ioa_Request.io_Message.mn_Node.ln_Pri = 10;
    if (!(port = CreatePort(PortName, 0L))) {
      FreeMem(ioa, (LONG) sizeof(struct IOAudio));
      /* War nix */
    } else {
      /* Audio-Kanal holen */
      ioa->ioa_Request.io_Message.mn_ReplyPort = port;
      ioa->ioa_Data = Channel_Map;
      ioa->ioa_Length = (LONG) sizeof(Channel_Map);
      /* Audio Device oeffnen */
      if (OpenDevice(AUDIONAME, 0L, (struct IORequest *) ioa,
                     0L)) {
        /* war nix */
        DeletePort(port);
        FreeMem(ioa, (LONG) sizeof(struct IOAudio));
      } else {
        /* Request initialisieren */
        ioa->ioa_Request.io_Flags = ADIOF_PERVOL;
        ioa->ioa_Request.io_Command = CMD_WRITE;
        ioa->ioa_Period = (SHORT) (3579545L / rate);
        ioa->ioa_Volume = (SHORT) vol;
        ioa->ioa_Length = len;
        ioa->ioa_Data = buf;

        ioa->ioa_Cycles = (SHORT) repeat;
      }
    }
  }
  return (ioa);
}

void
Beep(freq, dauer, vol)
    LONG            freq, dauer, vol;
{
  BYTE           *DataPtr;
  LONG            PlayLen = sizeof(WaveForm);
  LONG            rate;
  struct IOAudio *ioa;

  rate = freq * sizeof(WaveForm);
  if (DataPtr = (BYTE *) AllocMem(sizeof(WaveForm), MEMF_CHIP)) {
    memcpy(DataPtr, WaveForm, sizeof(WaveForm));

    /* IO-Request initialisieren */
    if (ioa = MakeIOA(rate, vol, 0, DataPtr, PlayLen)) {
      /* Starten */
      BeginIO((struct IORequest *) ioa);
      Delay(dauer);
      AbortIO((struct IORequest *) ioa);        /* Halt!!! */
      if (ioa->ioa_Request.io_Device) {
        CloseDevice((struct IORequest *) ioa);
      }
      if (ioa->ioa_Request.io_Message.mn_ReplyPort) {
        DeletePort(ioa->ioa_Request.io_Message.mn_ReplyPort);
      }
      if (ioa) {
        FreeMem(ioa, (LONG) sizeof(struct IOAudio));
      }
    }
    FreeMem(DataPtr, sizeof(WaveForm));
  }
}

/* Ausgabe für die Trackanzeige */

struct IntuiText TrackText =
{2, 0, JAM2, 0, 0, NULL, (UBYTE *) "                     "};

void
PrintTrack(nr)
{
  if (TrackWin) {
#ifdef DEUTSCH
    sprintf(TrackText.IText, "Spur:%3d, Kopf:%2d", nr / 2, nr % 2);
#else
    sprintf(TrackText.IText,"Track:%3d, Head:%2d",nr / 2,nr % 2);
#endif
            PrintIText(TrackWin->RPort, &TrackText, 20, 34);
  }
}

void
PrintDisk(nr)
{
  if (TrackWin) {
#ifdef DEUTSCH
    sprintf(TrackText.IText, "Diskette:%4d", nr);
#else
    sprintf(TrackText.IText, "Disk:%4d", nr);
#endif
    PrintIText(TrackWin->RPort, &TrackText, 20, 18);
  }
}
