#include <proto/exec.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <intuition/intuition.h>
#include <proto/intuition.h>

#include "SynthLib.h"
#include <libraries/locale.h>
#include <proto/locale.h>
#define CATCOMP_BLOCK
#define CATCOMP_NUMBERS
#include "Sy22loc.h"

#define STEPD_CHECK_YAMAHA   0
#define STEPD_G1P            1
#define STEPD_G1P_DATAS      2
#define STEPD_GP_LENGTH      3
#define STEPD_GP_DATAS       4

#define STEPD_WAIT_SYSEX     5
#define STEPD_WAIT_YAMAHA    6

#define STEPD_WAIT100         0
#define STEPD_S1P             1
#define STEPD_SEND_END        2

#define TYPE_VOICE           1
#define TYPE_ALL             2

#define BUFFER_SIZE         40000L

struct LocaleInfo DLi;
struct LocaleBase *LocaleBase;
struct DosBase *DOSBase;
struct ExecBase *SysBase;
struct IntuitionBase *IntuitionBase ;

char Message[256];
static struct DriverDatas DD;
extern struct MinList SY22TypesList;
struct Node SY22TypesNodes[] = {
        &SY22TypesNodes[1], ( struct Node * )&SY22TypesList.mlh_Head, 0, 0, "SY22 Voice",
        ( struct Node * )&SY22TypesList.mlh_Tail, &SY22TypesNodes[0], 0, 0, "SY22 All" };

struct MinList SY22TypesList = {
        ( struct MinNode * )&SY22TypesNodes[0], ( struct MinNode * )NULL, ( struct MinNode * )&SY22TypesNodes[1] };
struct EasyStruct AboutES = {
    sizeof (struct EasyStruct),
    0,
    NULL,
    NULL,
    NULL
 };


struct DriverDatas * InitDriver(ULONG (*GetData)(ULONG Number));
void Close(void);
STRPTR GetString(struct LocaleInfo *li, LONG stringNum);
ULONG Driver(ULONG *StepPtr,struct Exchange *Ex);
STRPTR About(void);

__saveds struct DriverDatas *InitDriver(ULONG (*GetData)(ULONG Number))
 {DOSBase=(struct DosBase *)GetData(GTD_DosBase);
  SysBase=(struct ExecBase *)GetData(GTD_ExecBase);
  LocaleBase=(struct LocaleBase *)GetData(GTD_LocaleBase);
  IntuitionBase=(struct IntuitionBase *)GetData(GTD_IntuitionBase);

  if (DLi.li_LocaleBase=LocaleBase)
    DLi.li_Catalog=OpenCatalog(NULL,"SynthLib/SY22.catalog",OC_BuiltInLanguage,"english",TAG_DONE);

  DD.Flags=0;
  DD.Driver=Driver;
  DD.Close=Close;
  DD.About=About;
  DD.TypeList=&SY22TypesList;

  return(&DD);
 }

__saveds void Close(void)
 {if (LocaleBase)
    CloseCatalog(DLi.li_Catalog);
 }

__saveds STRPTR About()
 {return(GetString(&DLi,MSGD_ABOUT));
 }

__saveds ULONG Driver(ULONG *StepPtr,struct Exchange *Ex)
 {static UBYTE * BufferSY22;
  static StepD;
  static USHORT PacketLength;
  static UBYTE Type;
  static struct DumpNode *Node;
  static UBYTE NPackets;
  static ULONG DatasLength;
  static UBYTE * DatasPtr;
  ULONG ReturnCode=0;

  switch(*StepPtr)
   {
    case STEP_INIT_DRIVER:

      BufferSY22=NULL;

      break;

    case STEP_EXIT_DRIVER:
    case STEP_ABORT:
      if (BufferSY22)
       {BufferSY22=0;
        FreeMem(BufferSY22,BUFFER_SIZE);
       }

    break;

    case STEP_START:
      if (!BufferSY22)
        BufferSY22=AllocMem(BUFFER_SIZE,0L);
      if (BufferSY22)
       {
        BufferSY22[0]=0xF0;
        Ex->DatasPtr=BufferSY22+1;
        Ex->Length=3;
        *StepPtr=STEP_RECIEVE;
        StepD=STEPD_CHECK_YAMAHA;
       }
       else
       {Ex->Message=GetString(&DLi,ERRORD_OUT_OF_MEMORY);
        ReturnCode=1;
        *StepPtr=STEP_WAIT;
       }

      break;

    case STEP_CONTINUE:
      switch(StepD)
       {case STEPD_CHECK_YAMAHA:
          if (Ex->DatasPtr[-3]==0x43 && !(Ex->DatasPtr[-2]&0xF0) && Ex->DatasPtr[-1]==0x7E)
           {StepD=STEPD_G1P;
            Ex->Length=2;
            *StepPtr=STEP_RECIEVE;
           }
           else
            *StepPtr=STEP_WAIT;

          break;

        case STEPD_G1P:
          PacketLength=(((UWORD)Ex->DatasPtr[-2])<<7)+Ex->DatasPtr[-1];

          if (BUFFER_SIZE+(BufferSY22-Ex->DatasPtr)<PacketLength+1)
           {Ex->Message=GetString(&DLi,ERRORD_TOO_MANY_DATAS);
            ReturnCode=1;
            *StepPtr=STEP_WAIT;
           }
           else
           {*StepPtr=STEP_RECIEVE;
            Ex->Length=PacketLength+1;
            StepD=STEPD_G1P_DATAS;
            sprintf(Message,GetString(&DLi,MSGD_RECIEVEPACKET),1);
            Ex->Message=Message;
           }

          break;

        case STEPD_G1P_DATAS:
          Type=0;
          NPackets=1;

          if (!memcmp(&(Ex->DatasPtr[-PacketLength-1]),"PK  2203AE",10))
            Type=TYPE_VOICE;
           else
            if (!memcmp(&(Ex->DatasPtr[-PacketLength-1]),"PK  2203VM",10))
              Type=TYPE_ALL;

          if (Type)
           {
            *StepPtr=STEP_RECIEVE_EOF;
            Ex->Length=2;
            Ex->Datas1=0xF7F7F7F7;
            StepD=STEPD_GP_LENGTH;
           }
           else
           {*StepPtr=STEP_WAIT;
            Ex->Message=GetString(&DLi,ERRORD_UNKNOWN_FORMAT);
            ReturnCode=1;
           }

          break;

        case STEPD_GP_LENGTH:

          if (Ex->DatasPtr[-1]!=0xF7)
           {PacketLength=(((UWORD)Ex->DatasPtr[-2])<<7)+Ex->DatasPtr[-1];

            if (BUFFER_SIZE+(BufferSY22-Ex->DatasPtr)<PacketLength+1)
             {Ex->Message=GetString(&DLi,ERRORD_TOO_MANY_DATAS);
              ReturnCode=1;
              *StepPtr=STEP_WAIT;
             }
             else
             {*StepPtr=STEP_RECIEVE;
              Ex->Length=PacketLength+1;
              StepD=STEPD_GP_DATAS;
              sprintf(Message,GetString(&DLi,MSGD_RECIEVEPACKET),NPackets+1);
              Ex->Message=Message;
             }
           }
           else
           {DatasLength=Ex->DatasPtr-BufferSY22;
            if (Node=AllocMem(sizeof(struct DumpNode),0L))
             {Node->DNode.ln_Type=0;
              Node->DNode.ln_Name=Node->Name;
              Node->Datas=NULL;
              Node->DatasLength=0;
              Node->DumpLength=DatasLength;
              Node->Name[0]='\0';
              Node->Flags=0;
              Ex->Node=Node;
              if (Type==TYPE_VOICE)
               {Ex->Message=GetString(&DLi,MSGD_RECIEVEENDVOICE);
                strcpy(Node->Type,"SY22 Voice");
                memcpy(Node->Name,BufferSY22+19,8);
                Node->Name[8]='\0';
               }
              if (Type==TYPE_ALL)
               {Ex->Message=GetString(&DLi,MSGD_RECIEVEENDALL);
                strcpy(Node->Type,"SY22 All");
               }
              if (Node->Dump=AllocMem(DatasLength,0L))
               {memcpy(Node->Dump,BufferSY22,DatasLength);

                Ex->DatasPtr=(APTR)Node;
                *StepPtr=STEP_RECIEVE_COMPLETE;
               }
               else
               {Ex->Message=GetString(&DLi,ERRORD_OUT_OF_MEMORY);
                ReturnCode=1;
                *StepPtr=STEP_WAIT;
                FreeMem(Node,sizeof(struct DumpNode));
               }
             }
             else
             {Ex->Message=GetString(&DLi,ERRORD_OUT_OF_MEMORY);
              ReturnCode=1;
              *StepPtr=STEP_WAIT;
             }
            FreeMem(BufferSY22,BUFFER_SIZE);
            BufferSY22=0;
           }

          break;

        case STEPD_GP_DATAS:
          NPackets++;

          *StepPtr=STEP_RECIEVE_EOF;
          Ex->Length=2;
          Ex->Datas1=0xF7F7F7F7;
          StepD=STEPD_GP_LENGTH;

          break;
       }
      break;

    case STEP_SEND_ABORT:

    break;


    case STEP_SEND_START:
      Node=(struct DumpNode *)Ex->Node;
      Type=0;
      NPackets=0;

      if (!strcmp(Node->Type,"SY22 Voice"))
       {sprintf(Message,GetString(&DLi,MSGD_SENDVOICE),Node->Name);
        Ex->Message=Message;
        Type=TYPE_VOICE;
       }
       else
        if (!strcmp(Node->Type,"SY22 All"))
         {sprintf(Message,GetString(&DLi,MSGD_SENDALLPACKET),1);
          Ex->Message=Message;
          Type=TYPE_ALL;
         }

      if (Type)
       {DatasPtr=Node->Dump;
        Ex->DatasPtr=DatasPtr;
        PacketLength=(((UWORD)Ex->DatasPtr[4])<<7)+Ex->DatasPtr[5];
        Ex->Length=PacketLength+7;
        DatasPtr+=Ex->Length;
        *StepPtr=STEP_SEND_SEND;
        StepD=STEPD_WAIT100;
       }
       else
       {Ex->Message=GetString(&DLi,ERRORD_UNKNOWN_TYPE);
        ReturnCode=1;
        *StepPtr=STEP_SEND_COMPLETE;
       }

       break;

    case STEP_SEND_CONTINUE:
      switch(StepD)
       {case STEPD_WAIT100:

          NPackets++;

          if (DatasPtr[0]==0xF7)
           {Ex->Length=1;
            Ex->DatasPtr=DatasPtr;
            *StepPtr=STEP_SEND_SEND;
            StepD=STEPD_SEND_END;
           }
           else
           {
            Ex->Datas1=100000;
            *StepPtr=STEP_SEND_DELAY;
            StepD=STEPD_S1P;
           }

          break;

        case STEPD_S1P:
          PacketLength=(((UWORD)DatasPtr[0])<<7)+DatasPtr[1];
          Ex->Length=PacketLength+3;
          Ex->DatasPtr=DatasPtr;
          DatasPtr+=Ex->Length;

          Ex->Message=Message;
          sprintf(Message,GetString(&DLi,MSGD_SENDALLPACKET),NPackets+1);

          *StepPtr=STEP_SEND_SEND;
          StepD=STEPD_WAIT100;

          break;

        case STEPD_SEND_END:
          *StepPtr=STEP_SEND_COMPLETE;
          Ex->Message=Message;
          sprintf(Message,GetString(&DLi,MSGD_SENDEND),Node->Name);

       }
      break;

    case STEP_REQ_START:
      Node=Ex->Node;
      Ex->Node=NULL;
      Type=0;
      NPackets=0;

      if (!strcmp(((struct Node *)Node)->ln_Name,"SY22 Voice"))
       {Ex->DatasPtr="\xF0\x43 \x7EPK  2203AE\xF7";
        Type=TYPE_VOICE;
       }
       else
        if (!strcmp(((struct Node *)Node)->ln_Name,"SY22 All"))
         {Ex->DatasPtr="\xF0\x43 \x7EPK  2203VM\xF7";
          Type=TYPE_ALL;
         }

      if (Type)
       {
        Ex->Length=15;
        *StepPtr=STEP_REQ_SEND;
        StepD=STEPD_WAIT_SYSEX;
       }
       else
       {Ex->Message=GetString(&DLi,ERRORD_UNKNOWN_TYPE);
        ReturnCode=1;
        *StepPtr=STEP_REQ_COMPLETE;
       }

      break;

    case STEP_REQ_ABORT:
      if (BufferSY22)
       {BufferSY22=0;
        FreeMem(BufferSY22,BUFFER_SIZE);
       }

      break;

    case STEP_REQ_CONTINUE:
      switch(StepD)
       {
        case STEPD_WAIT_SYSEX:
          if (!BufferSY22)
            BufferSY22=AllocMem(BUFFER_SIZE,0L);
          if (!BufferSY22)
           {Ex->Message=GetString(&DLi,ERRORD_OUT_OF_MEMORY);
            ReturnCode=1;
            *StepPtr=STEP_REQ_COMPLETE;
           }
           else
           {*StepPtr=STEP_REQ_WAIT;
            StepD=STEPD_WAIT_YAMAHA;
           }

          break;

        case STEPD_WAIT_YAMAHA:
          BufferSY22[0]=0xF0;
          Ex->DatasPtr=BufferSY22+1;
          Ex->Length=3;
          *StepPtr=STEP_REQ_RECIEVE;
          StepD=STEPD_CHECK_YAMAHA;

          break;

        case STEPD_CHECK_YAMAHA:
          if (Ex->DatasPtr[-3]==0x43 && !(Ex->DatasPtr[-2]&0xF0) && Ex->DatasPtr[-1]==0x7E)
           {StepD=STEPD_G1P;
            Ex->Length=2;
            *StepPtr=STEP_REQ_RECIEVE;
           }
           else
           {
            *StepPtr=STEP_REQ_COMPLETE;
            Ex->Message=GetString(&DLi,ERRORD_UNEXPECTED_ANSWER);
            ReturnCode=1;
           }

          break;

        case STEPD_G1P:
          PacketLength=(((UWORD)Ex->DatasPtr[-2])<<7)+Ex->DatasPtr[-1];

          if (BUFFER_SIZE+(BufferSY22-Ex->DatasPtr)<PacketLength+1)
           {
            Ex->Message=GetString(&DLi,ERRORD_TOO_MANY_DATAS);
            ReturnCode=1;
            *StepPtr=STEP_REQ_COMPLETE;
           }
           else
           {*StepPtr=STEP_REQ_RECIEVE;
            Ex->Length=PacketLength+1;
            StepD=STEPD_G1P_DATAS;
            Ex->Message=Message;
            sprintf(Message,GetString(&DLi,MSGD_RECIEVEPACKET),1);
           }

          break;

        case STEPD_G1P_DATAS:
          NPackets=1;

          if (Type==TYPE_VOICE)
            if (memcmp(&(Ex->DatasPtr[-PacketLength-1]),"PK  2203AE",10))
              Type=0;

          if (Type==TYPE_ALL)
            if (memcmp(&(Ex->DatasPtr[-PacketLength-1]),"PK  2203VM",10))
              Type=0;

          if (Type)
           {
            *StepPtr=STEP_REQ_RECIEVE_EOF;
            Ex->Length=2;
            Ex->Datas1=0xF7F7F7F7;
            StepD=STEPD_GP_LENGTH;
           }
           else
           {*StepPtr=STEP_REQ_COMPLETE;
            ReturnCode=1;
            Ex->Message=GetString(&DLi,ERRORD_UNEXPECTED_ANSWER);
           }

          break;

        case STEPD_GP_LENGTH:

          if (Ex->DatasPtr[-1]!=0xF7)
           {PacketLength=(((UWORD)Ex->DatasPtr[-2])<<7)+Ex->DatasPtr[-1];

            if (BUFFER_SIZE+(BufferSY22-Ex->DatasPtr)<PacketLength+1)
             {Ex->Message=GetString(&DLi,ERRORD_TOO_MANY_DATAS);
              ReturnCode=1;
              *StepPtr=STEP_REQ_COMPLETE;
             }
             else
             {*StepPtr=STEP_REQ_RECIEVE;
              Ex->Length=PacketLength+1;
              StepD=STEPD_GP_DATAS;
              Ex->Message=Message;
              sprintf(Message,GetString(&DLi,MSGD_RECIEVEPACKET),NPackets+1);
             }
           }
           else
           {DatasLength=Ex->DatasPtr-BufferSY22;
            if (Node=AllocMem(sizeof(struct DumpNode),0L))
             {Node->DNode.ln_Type=0;
              Node->DNode.ln_Name=Node->Name;
              Node->Datas=NULL;
              Node->DatasLength=0;
              Node->DumpLength=DatasLength;
              Node->Name[0]='\0';
              Node->Flags=0;
              Ex->Node=Node;
              if (Type==TYPE_VOICE)
               {Ex->Message=GetString(&DLi,MSGD_RECIEVEENDVOICE);
                strcpy(Node->Type,"SY22 Voice");
                memcpy(Node->Name,BufferSY22+19,8);
                Node->Name[8]='\0';
               }
              if (Type==TYPE_ALL)
               {Ex->Message=GetString(&DLi,MSGD_RECIEVEENDALL);
                strcpy(Node->Type,"SY22 All");
               }
              if (Node->Dump=AllocMem(DatasLength,0L))
               {memcpy(Node->Dump,BufferSY22,DatasLength);

                Ex->DatasPtr=(APTR)Node;
                *StepPtr=STEP_REQ_COMPLETE;
               }
               else
               {Ex->Message=GetString(&DLi,ERRORD_OUT_OF_MEMORY);
                ReturnCode=1;
                *StepPtr=STEP_REQ_COMPLETE;
                FreeMem(Node,sizeof(struct DumpNode));
               }
             }
             else
             {Ex->Message=GetString(&DLi,ERRORD_OUT_OF_MEMORY);
              ReturnCode=1;
              *StepPtr=STEP_REQ_COMPLETE;
             }
            FreeMem(BufferSY22,BUFFER_SIZE);
            BufferSY22=0;
           }

          break;

        case STEPD_GP_DATAS:
          NPackets++;

          *StepPtr=STEP_REQ_RECIEVE_EOF;
          Ex->Length=2;
          Ex->Datas1=0xF7F7F7F7;
          StepD=STEPD_GP_LENGTH;

          break;
       }
      break;

   }
  return(ReturnCode);
}

STRPTR GetString(struct LocaleInfo *li, LONG stringNum)
{
LONG   *l;
UWORD  *w;
STRPTR  builtIn;

    l = (LONG *)CatCompBlock;

    while (*l != stringNum)
    {
        w = (UWORD *)((ULONG)l + 4);
        l = (LONG *)((ULONG)l + (ULONG)*w + 6);
    }
    builtIn = (STRPTR)((ULONG)l + 6);

    if (LocaleBase)
        return(GetCatalogStr(li->li_Catalog,stringNum,builtIn));
    return(builtIn);
}
