/*-----------------------------------------------
 Name     Restore
 Autor       Tilo Rossmanith
 Datum       Februar 1989
 Zweck      Stellt geloeschte Files einer
       Diskette wieder her
 Bedienung  Aufruf : 'Restore <DRIVE>'
         DRIVE : entweder df0: oder df1:
 Hinweise   Der Filenamen wird beim Loeschen oft
       unwiederbringlich vernichtet. Deshalb
            wird jeweils die Eingabe eines Namens
       verlangt.
       Assemblierung: cc +L -S restore.c
                      ln restore.o -lc32
 MAXON COMPUTER GmbH 1989
 KICKSTART 1989
-----------------------------------------------*/

/* ------------ Includes --------------------- */

#include "exec/types.h"
#include "exec/memory.h"
#include "exec/io.h"
#include "libraries/dos.h"
#include "exec/devices.h"
#include "devices/trackdisk.h"
#include "functions.h"
#include "stdio.h"

/* ----------Definitionen -------------------- */

#define TD_READ      CMD_READ
#define BLOCKSIZE    TD_SECTOR

#define MAXHEADERS   100

/* ----------- globale Variablen ------------- */

struct Header {
  long  FirstBlock;
  long  ByteSize;
  char  Name[30];
};

long  *BitMap= NULL, *Buffer= NULL;
WORD  *Sequence= NULL, *NextBlock, *Datasize;
WORD  HeaderCount= 0;

struct MsgPort  *DiskPort= NULL;
struct IOExtTD  *DiskRequest;
struct Header   Headers[MAXHEADERS];

/* -------- OpenTrackdisk -------------------- */

long OpenTrackdisk (Drive, DiskPort, DiskRequest)
  long   Drive;
  struct MsgPort  **DiskPort;
  struct IOExtTD  **DiskRequest;
{
  *DiskPort= CreatePort (0,0);
  if (*DiskPort == NULL)
    return (100);

  *DiskRequest= (struct IOExtTD *)
    CreateExtIO (*DiskPort, sizeof (struct IOExtTD));
  if (*DiskRequest == NULL)
  {
    DeletePort (*DiskPort);
    return (101);
  }

  if (OpenDevice (TD_NAME, Drive, *DiskRequest, 0)!= NULL)
  {
    DeleteExtIO (*DiskRequest, sizeof (struct IOExtTD));
    DeletePort (*DiskPort);
    return (102);
  }

  return (0);
}

/* ------------- CloseTrackdisk ------------------ */

void CloseTrackdisk (DiskPort, DiskRequest)
  struct MsgPort  *DiskPort;
  struct IOExtTD  *DiskRequest;
{
  MotorOff (DiskRequest);

  CloseDevice (DiskRequest);
  DeleteExtIO (DiskRequest, sizeof (struct IOExtTD));
  DeletePort (DiskPort);
}

/* --------------- ReadBlock -------------------- */

long ReadBlock (Block, Buffer, DiskRequest)
  long    Block;
  long    *Buffer;
  struct IOExtTD  *DiskRequest;
{
  DiskRequest-> iotd_Req.io_Length= BLOCKSIZE;
  DiskRequest-> iotd_Req.io_Data= (APTR)Buffer;
  DiskRequest-> iotd_Req.io_Command= TD_READ;
  DiskRequest-> iotd_Req.io_Offset= TD_SECTOR * Block;

  return (DoIO (DiskRequest));
}

/* -------------- ReadBitMap ------------------- */

long ReadBitMap (Buffer, DiskRequest)
  long        *Buffer;
  struct IOExtTD  *DiskRequest;
{
  long    *ContBuffer;
  COUNT   Counter;

  ContBuffer= AllocMem (BLOCKSIZE, MEMF_CHIP);
  if (ContBuffer== NULL)
    return (100);

  if (ReadBlock (880, ContBuffer, DiskRequest) != NULL)
    return (101);
  if (ReadBlock (ContBuffer[79], ContBuffer, DiskRequest) != NULL)
    return (102);

  for (Counter= 0; Counter< 56; Counter++)
    Buffer[Counter]= ContBuffer[Counter];

  FreeMem (ContBuffer, BLOCKSIZE);

  return (0);
}

/* ---------------- BlockFree ------------------- */

BOOL BlockFree (Block, BitMap)
  long  Block;
  long  *BitMap;
{
  if (Block < 2)
    return (FALSE);

  Block -= 2;

  if (BitMap[Block/32+1] & 1<< (Block-32*(Block/32)))
    return (TRUE);
  else
    return (FALSE);
}

/* -------------- MotorOff --------------------- */

long MotorOff (DiskRequest)
  struct IOExtTD  *DiskRequest;
{
  DiskRequest-> iotd_Req.io_Length= 0;
  DiskRequest-> iotd_Req.io_Command= TD_MOTOR;

  return (DoIO (DiskRequest));
}

/* --------------- Abort ----------------------- */

void Abort (text)
  char  *text;
{
  if (Sequence) FreeMem (Sequence, 2*3*1760);
  if (BitMap) FreeMem (BitMap, 224);
  if (Buffer) FreeMem (Buffer, BLOCKSIZE);
  if (DiskPort) CloseTrackdisk (DiskPort, DiskRequest);

  if (text)
    printf ("\nRESTORE: %s\n", text);

  exit (333);
}

/* ---------------- _abort --------------------- */
/* _abort wird normalerweise beim Druck von CTRL-C
 * aufgerufen. Wenn man diese Libraryfunktion
 * ueberschreibt, wird ein eigenes Programmende
 * gewaehrleistet. Somit ist es moeglich, das
 * Trackdisk-device zu schliessen und den Speicher
 * wieder freizugeben.
 */

_abort()
{
  Abort("*** Abbruch");
}

/* ---------------- Restore -------------------- */

void Restore (Block)
  long  Block;
{
  long    Bytesize= 0;
  WORD    Next;
  long    Fh= 0;
  char    Name[80];
  UCOUNT  Loop, Counter= 0;

  printf ("File gefunden - Filestruktur wird geprueft - ");

  Next= Block;
  do
  {
    Counter++;
    if (Counter != Sequence[Next])
      goto Corrupt;
    if ( (NextBlock[Next]> 0) && (Datasize[Next]!= 488) )
      goto Corrupt;
    Bytesize+= Datasize[Next];
    Next= NextBlock[Next];
  } while (Next != 0);

  printf ("Ok.\n  Filelaenge: %ld Bytes\n", Bytesize);

  printf ("  alter Filename: ");
  for (Loop= 0; Loop< HeaderCount; Loop++)
  {
    if (Headers[Loop].ByteSize== Bytesize)
    {
      printf ("%s\n", Headers[Loop].Name);
      Loop= HeaderCount+10;
    }
  }
  if (Loop< HeaderCount+10)
    printf ("-unbekannt-\n");

 Enter:
  printf ("  neuer Filename: ");
  gets (Name);
  if (strlen (Name)== 0)
    return;
  Fh= Open (Name, MODE_NEWFILE);
  if (Fh== NULL)
  {
    printf ("  - File laesst sich nicht oeffnen\n");
    goto Enter;
  }

  Next= Block;
  for (Loop= 0; Loop< Counter; Loop++)
  {
    if (ReadBlock (Next, Buffer,DiskRequest)!= NULL)
      Abort ("Lesefehler");
    if (Write (Fh, Buffer+6, Datasize[Next])!= Datasize[Next])
      Abort ("Schreibfehler");
    Next= NextBlock[Next];
  }

  Close (Fh);
  return;

 Corrupt:
  printf ("Fehlerhaft !\n");
}

/* ---------------- main ----------------------- */

main (argc, argv)
  int  argc;
  char  *argv[];
{
  long    Drive;
  COUNT   Loop, Loop2;
  int     Choice;
  char    *Name;
  BYTE    NameSize;

  if (argc != 2)
    Abort ("'Restore <DRIVE>'");

  if (strcmp (argv[1], "df0:")== NULL)
    Drive= 0;
  else
  {
    if (strcmp (argv[1], "df1:")== NULL)
      Drive= 1;
    else
      Abort ("Falsche Laufwerksangabe");
  }

  BitMap= AllocMem (224, MEMF_CHIP);
  Buffer= AllocMem (BLOCKSIZE, MEMF_CHIP);
  Sequence= AllocMem (3*2*1760, MEMF_CLEAR);
  Datasize= Sequence+1760;
  NextBlock= Sequence+2*1760;

  if ((BitMap== NULL)|| (Sequence== NULL)|| (Buffer== NULL))
    Abort ("Nicht genug Speicher");

  if ( OpenTrackdisk (Drive, &DiskPort, &DiskRequest)!= NULL)
    Abort ("Kein Trackdisk-device verfuegbar");
  if ( ReadBitMap (BitMap, DiskRequest)!= NULL)
    Abort ("BitMap nicht gefunden");

  printf ("RESTORE V1.0 - (C)02/89 Tilo Rossmanith\n");
  printf ("Bitte warten, die Blocks werden untersucht...\n");

  for (Loop= 2; Loop< 1760; Loop++)
  {
    if (BlockFree (Loop, BitMap))
    {
      if (ReadBlock (Loop, Buffer,DiskRequest)!= NULL)
        Abort ("Lesefehler");
      if (Buffer[0]== 8)
      {
        if ( (Buffer[3]<=488) && (Buffer[4]<1760) )
        {
          Sequence[Loop]= (WORD)Buffer[2];
          Datasize[Loop]= (WORD)Buffer[3];
          NextBlock[Loop]= (WORD)Buffer[4];
        }
      }
      if ((Buffer[0]== 2) && (Buffer[127]== -3))
      {
        if (HeaderCount< MAXHEADERS)
        {
          Headers[HeaderCount].FirstBlock=
            Buffer[4];
          Headers[HeaderCount].ByteSize=
            Buffer[81];
          Name= &Buffer[108];
          NameSize= *Name;
          Name++;
          for (Loop2= 0; Loop2< NameSize; Loop2++)
          {
             Headers[HeaderCount].Name[Loop2]=
              Name[Loop2];
          }
          Name[NameSize]= 0;
          HeaderCount++;
         }
      }
    }
  }
  printf ("Alle Blocks ueberprueft.\n\n");

  for (Loop= 2; Loop< 1760; Loop++)
  {
    if (Sequence[Loop]== 1)
    {
      Restore (Loop);
    }
  }

  Abort ("*** Fertig");
}
