/********************************\
*      >>> DATASETTE 64 <<<      *
*--------------------------------*
*  ©1993 by ARTSOFT Productions  *
*           Holger Schemel       *
*           Sennehof 28          *
*           4800 Bielefeld 12    *
*           Tel.: (0521) 493245  *
*--------------------------------*
*  LAST UPDATE:   27/02/93       *
\********************************/

/************************************************************\
* >>> Programm zum Einlesen von C64-Datasettenprogrammen <<< *
*------------------------------------------------------------*
* Programm konvertiert C64-Programme, die im TURBO TAPE 64 - *
* Format auf Magnetkassetten gespeichert sind. Um diese auf  *
* C64-Emulatoren wie "TheA64Package" oder "The 64 Emulator"  *
* verwenden zu können, wenn zur Konvertierung kein C64, ge-  *
* schweige denn eine Datasette, mehr zur Verfügung steht,    *
* muß das Programm zunächst mit einem Kassettenrecorder bzw. *
* einem Tapedeck sowie einem handelsüblichen Sounddigitizer  *
* mit maximaler Samplefrequenz (über 40 kHz) digitalisiert   *
* werden (z.B. mit Audiomaster). Der Anfang des Samples muß  *
* alsdann maximal vergrößert (Audiomaster: 599 Bytes) werden *
* und der Anfang mit 'Cut' soweit abgeschnitten werden, daß  *
* sowohl das anfängliche Bandrauschen als auch Tonschwankun- *
* gen (Welle liegt kurzzeitig völlig über oder unter der     *
* Mittellinie) vollständig beseitigt sind. Vom Anfang kann   *
* ruhig ein Teil des Synchronisationssignales fehlen. Fängt  *
* nun das Sample mit einer Folge von sieben kleineren und    *
* einer größeren Welle an, kann es abgespeichert und mit dem *
* Programm 'Datasette64' behandelt werden.                   *
*------------------------------------------------------------*
* Aufbau einer TurboTape64-Datei:                            *
* """""""""""""""""""""""""""""""                            *
* 1. Synchonisation:                                         *
*    - 5 mal (246 mal Byte 2)                                *
*    - Countdownbytes 9,8,7,6,5,4,3,2,1                      *
* 2. Vorspann:                                               *
*    - Sekundäradresse 1                                     *
*    - Low- und Highbyte der Startadresse                    *
*    - Low- und Highbyte der Endadresse                      *
*    - Füllbyte (0)                                          *
*    - Dateiname                                             *
*    - Rest wird auf 193 Zeichen mit SPACE aufgefüllt        *
* 3. Synchonisation:                                         *
*    - 2 mal (246 mal Byte 2)                                *
*    - Countdownbytes 9,8,7,6,5,4,3,2,1                      *
*    - Null-Byte                                             *
* 4. Daten                                                   *
* 5. Prüfsumme: EXOR aller Datenbytes (255 mal)              *
*                                                            *
*------------------------------------------------------------*
* Um sich das manuelle Entfernen des einleitenden Bandrau-   *
* schens zu ersparen, empfiehlt es sich, das Bandlaufwerk    *
* einige Umdrehungen vor Beginn der Aufzeichnung zu starten  *
* (wobei sich der Bandlauf stabilisieren kann) und den Digi- *
* talisiervorgang zu beginnen, wenn der Synchronisationston  *
* ertönt. Der Ton bzw. das Signal ist lang genug, um vorne   *
* etwas verkürzt werden zu können.                           *
*------------------------------------------------------------*
* Erhält man die Meldung "Prüfsumme OK!", so wurde die Datei *
* vollständig korrekt gelesen. Die Meldung "Prüfsummenfeh-   *
* ler" kann zwei Gründe haben:                               *
* - Ein oder mehrere Bits wurden falsch erkannt              *
* - Ein oder mehrere Bits fehlen oder sind zuviel            *
* Während der erste Fall nur ein falsches Byte nach sich     *
* zieht, führt der zweite zu einem bitweise verschobenen     *
* Programm, das ab der Fehlerstelle unbrauchbar ist. Meist   *
* gibt das Programm dann Wellenlängenabweichungsfehler aus,  *
* die oftmals von Hand korrigiert werden können, da die Po-  *
* sition des Fehlers in der Sampledatei genannt wird.        *
\************************************************************/

#include <exec/memory.h>
#include <libraries/dos.h>

extern VOID *Open(), *AllocMem();
extern LONG Read(), Seek(), Write();
extern VOID Chk_Abort();
extern WORD Enable_Abort;
UBYTE *Lower2Upper();
UBYTE ScanBit();
UBYTE ScanByte();

#define ENDOFFILE       (ptr>=max)
#define SCANWHILE(cond) while(ptr<max && (cond))

struct DatasetteFileHeader
{
   UBYTE FileAddress2;
   UBYTE startlow;
   UBYTE starthigh;
   UBYTE endlow;
   UBYTE endhigh;
   UBYTE filler;
   UBYTE filename[187];
} DSHeader;

BYTE dbyte,check;
BYTE synctab[100],synccnt,hisync,losync,sync,halfsync,doubsync;
BYTE *ptr,*max,*fptr,*hptr,*FileBuf1=0,*FileBuf2=0;
UWORD *file1=0,*file2=0;
ULONG len1,len2,FileLen1,FileLen2;
LONG i;

main(argc,argv)   /*------------ HAUPTPROGRAMM ------------*/
WORD argc;
BYTE *argv[];
{
   if (argc<3 || *argv[1]=='?')
      CloseAll("Usage: Datasette64 <tape file> <disk file> [<sync>]");

   printf("SEARCHING FOR %s\n",Lower2Upper(argv[1]));
   if (!(file1=Open(argv[1],MODE_OLDFILE)))
      CloseAll("?FILE NOT FOUND ERROR");
   Seek(file1,0L,OFFSET_END);
   FileLen1=Seek(file1,0L,OFFSET_BEGINNING);
   printf("LOADING\n");
   if (!(ptr=FileBuf1=AllocMem(FileLen1,MEMF_PUBLIC)))
      CloseAll("?OUT OF MEMORY ERROR");
   len1=Read(file1,FileBuf1,FileLen1);
   max=FileBuf1+(BYTE *)len1;
   Close(file1); file1=0;
   Chk_Abort();

   if (Equal(ptr,"FORM"))     /* IFF-Datei? */
   {
      SCANWHILE(!Equal(ptr,"BODY")) ptr++;
      if (!ENDOFFILE)
      {
         ptr+=4;
         max=ptr+(BYTE *)(*(ULONG *)ptr)+4L;
         ptr+=4;
      }
      else ptr=FileBuf1;
   }

   SCANWHILE(*ptr>=0) ptr++;  /* Erste vollständige positive */
   SCANWHILE(*ptr< 0) ptr++;  /* Zahlenreihe suchen          */

   synccnt=0;
   SCANWHILE(synccnt<100)     /* Wellenlängen 0/1-Bits ermitteln */
   {
      synctab[synccnt]=0;
      SCANWHILE(*ptr>=0) { synctab[synccnt]++; ptr++; }
      SCANWHILE(*ptr< 0) { synctab[synccnt]++; ptr++; }
      synccnt++;
   }
   hisync=losync=synctab[0];
   for(i=1;i<synccnt;i++)
   {
      if (synctab[i]>hisync) hisync=synctab[i];
      if (synctab[i]<losync) losync=synctab[i];
   }
   sync=(hisync+losync)/2;    /* Vergleichswert: "0"<sync<"1" */

   if (argc==4)
   {
      printf("Ermittelter Sync-Wert: %d\n",sync);
      if (*argv[3]!='?') sync=atoi(argv[3]);
      printf("-->Sync: <%d>>> %d <<<%d>\n",losync,sync,hisync);
   }
   halfsync=sync-2*(sync-losync);
   doubsync=sync+2*(hisync-sync);

   SCANWHILE(!ScanBit());     /* Synchronisationsanfang suchen: */
   ScanBit();                 /* ?-mal Byte 2 = %00000010       */

   SkipSync();                /* Erste Synchronisation überspringen */

   hptr=(BYTE *)&DSHeader;
   for(i=0;i<193;i++) *hptr++=ScanByte();

   printf("-->Header:\n");
   printf("   - Sekundäradresse: %d\n",DSHeader.FileAddress2);
   printf("   - Startadresse LO: %d\n",DSHeader.startlow);
   printf("   - Startadresse HI: %d\n",DSHeader.starthigh);
   printf("   - Endadresse   LO: %d\n",DSHeader.endlow);
   printf("   - Endadresse   HI: %d\n",DSHeader.endhigh);
   dbyte=DSHeader.filename[40];
   DSHeader.filename[40]=0;
   printf("   - Dateiname      : %s\n",DSHeader.filename);
   DSHeader.filename[40]=dbyte;

   SkipSync();                /* Zweite Synchronisation überspringen */
   if (ScanByte()) CloseAll("?LOAD ERROR");

   len2=DSHeader.endhigh*256+DSHeader.endlow;
   len2-=DSHeader.starthigh*256+DSHeader.startlow;
   FileLen2=len2+2L;
   if (!(fptr=FileBuf2=AllocMem(FileLen2,MEMF_PUBLIC)))
      CloseAll("?OUT OF MEMORY ERROR");

   *fptr++=0x01;
   *fptr++=0x08;
   check=0;

   SCANWHILE(len2--) check^=(*fptr++=(BYTE)ScanByte());
   if (ENDOFFILE) CloseAll("?OUT OF DATA ERROR");

   if (check==ScanByte()) printf("-->Prüfsumme OK!\n");
   else                   printf("-->Prüfsummenfehler!\n");

   if (!(file2=Open(argv[2],MODE_NEWFILE)))
      CloseAll("?OPEN ERROR");
   printf("SAVING\n");
   if (Write(file2,FileBuf2,FileLen2)<0)
      CloseAll("?WRITE ERROR");
   Close(file2); file2=0;

   CloseAll(0L);
}

UBYTE *Lower2Upper(str)
UBYTE *str;
{
   static UBYTE up[40];
   register BYTE z;

   for(z=0;*str && z<40;z++,str++) up[z]=toupper(*str);
   return(up);
}

Equal(p,s)
UBYTE *p,*s;
{
   UBYTE *sp=s;
   WORD rc=1;

   while(*s) if(*p++!=*s++) rc=0;
   return(rc);
}

UBYTE ScanBit()
{
   register WORD scnt=0;
   static WORD cnt=0;

   if (cnt++>25) { Chk_Abort(); cnt=0; }
   SCANWHILE(*ptr>=0) { scnt++; ptr++; }
   SCANWHILE(*ptr< 0) { scnt++; ptr++; }
   if (scnt<halfsync || scnt>doubsync)
     printf("-->Wellenlängeninkonsistenz in Byte %ld\n",(LONG)ptr-FileBuf1);

   return(scnt>sync);
}

UBYTE ScanByte()
{
   register UBYTE i,sbyte=0;

   for(i=0;i<8;i++)
   {
      sbyte<<=1;
      sbyte|=ScanBit();
   }
   return(sbyte);
}

SkipSync()
{
   register UBYTE i,sbyte=0;

   SCANWHILE((sbyte=ScanByte())==0x02);   /* Syncbytes überspringen */
   for(i=9;i>0;i--)                       /* Countdown prüfen       */
   {
      if (i<9) sbyte=ScanByte();
      if (sbyte!=i) CloseAll("?LOAD ERROR");
   }
}

void _abort()
{
   CloseAll("*** User Break");
}

CloseAll(txt)
UBYTE *txt;
{
   if (FileBuf1) FreeMem(FileBuf1,FileLen1);
   if (FileBuf2) FreeMem(FileBuf2,FileLen2);
   if (file1) Close(file1);
   if (file2) Close(file2);
   if (txt) printf("%s\n",txt);
   printf("READY.\n");
   exit(txt!=0L);
}

