/*          Laser Disk Player program                *\
**         Copyright Ron M. Battle 1991              **
**            HyperBorea Studio B                    **
**             Manx Aztec C v5.2a                    **
** May be freely distributed for non-commercial use  **
**            *Revision History*                     **
**---------------------------------------------------**
**  DATE     VERSION  COMMENTS                       **
**---------------------------------------------------**
**  02-22-92  1.31    Add SerialUnit                 **
**  02-15-92  1.3     Can run from WorkBench,        **
**                     help key supported,           **
**                     better error reporting,       **
**                     ToolType defaults,            **
**                     Thanx to Larry Shultz and     **
**                     Brian Berg for LDP.info icons **
**  10-13-91  1.21    Dressed up remote with examples**
**                     from Brian Berg               **
**  04-21-91  1.2     Generic Player revisions for   **
**                     Hitachi, Pioneer, Sony        **
**  09-29-90  1.1     Added ARexx interface          **
**                     Thanx to GS & BH for FancyDemo**
**  02-25-90  1.0     Sony players only              **
**---------------------------------------------------**
**  The onscreen controller only uses a subset of    **
**  commands. This synchronous implementation is     **
**  not slick, could have better error checking,     **
**             but it WORKS!!                        **
**  It would be nice to have more control over the   **
**  serial device's buffer. As it stands, the min    **
**  size is 512 bytes! This makes it more            **
**  difficult to keep track of lid openings,etc.     **
**  Since I'm synchronous, I don't check!            **
**  You could get around this by doing a continual   **
**  serial read with parameters for terminator char  **
**  and number of chars expected on the read. You    **
**  would need to modify the main event loop to also **
**  wait for a message from mySerPort, but would be  **
**  better to set up a new message port for serial   **
**  reads! Use SendIO() for asynchronous reads,      **
**  when read done GetMsg(), get any errors from this**
**  msg then SendIO() another read, etc.!            **
**                                                   **
**  REMOTE CONTROLLER COMMANDS:                      **
**                                                   **
**  CL      Clear commands                           **
**  Index   Toggle index display ON/OFF              **
**  <<< >>> Scan                                     **
**  << >>   Fast                                     **
**  <| |>   Slow                                     **
**  | < > | Step                                     **
**  < >     Play                                     **
**  Search  Search  Hitachi&Sony: Srch Frame# Enter  **
**                  Pioneer: Frame# Srch             **
**  Enter   Enter   For Hitachi&Sony players only    **
**  Log     Log     saves note and frame# in         **
**                  ram:LDP.log                      **
**  M+      Memorize current frame number            **
**  MR      Search for memorized frame number        **
**  Still   Still frame                              **
**  Eject   Eject disc                               **
**                                                   **
**  You can also enter numbers into the display and  **
**  press return for a search.                       **
**  If you don't do the commands in the proper       **
**  sequence, you might hang the controller. If you  **
**  don't like that.......CHANGE IT!                 **
**  This controller also takes exclusive control     **
**  of the serial.device. Shared access never worked **
**  properly!                                        **
**                                                   **
\*                                                   */

#include <exec/exec.h>
#include <intuition/intuition.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <workbench/icon.h>
#include <devices/serial.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <functions.h>
#include <rexx/storage.h>
#include "tinystring.h"     /* The C routine index() causes contention */
#include "gadgets.h"
#include "video.h"     /* this header would not pre-compile properly */


/* #define DEBUG 1 */

#define HELP 95                    /* raw keycode for Help button */

/* defaults for serial device */
#define READ_BITS   8
#define WRITE_BITS  8
#define STOP_BITS   1
#define MIN_BAUD    1200
#define MAX_BAUD    9600
#define MIN_UNIT    0
#define MAX_UNIT    31

/* errors */
#define NO_ERR          0
#define READ_EXTIO_ERR  1
#define DEVICE_ERR      2
#define WRITEPORT_ERR   3
#define WRITE_EXTIO_ERR 4
#define INT_ERR         5     /* error opening intuition library  */
#define WIN_ERR         6    /*  error opening window            */
#define REXX_ERR        7   /*   error creating rexx port       */
#define ILIB_ERR        8  /*    error opening icon library    */

char *ErrStr[] = {
                  "NONE!",
                  "Allocating read i/o block! ",
                  "Opening serial device! ",
                  "Creating write port! ",
                  "Allocating write i/o block! ",
                  "Opening Intuition library!",
                  "Opening window!",
                  "Creating rexx port!",
                  "Opening icon library!" };


#define MAXIN         6              /* size of in-buffers */
#define MAXOUT        22            /* size of out-buffer */
#define LDP_1000A     2            /* for  SonyModel     */
#define OTHER         1           /*  means 180,1200,1500, or 2000 models */
#define ASLEEP        0          /*  ldp not ready     */
#define MAX_FRAME     54000     /* highest frame #    */
#define MAX_FRAME_STR "54000"  /*  "       " string  */

#define D7         128     /* bit 7, used for masking */
#define D6          64
#define D5          32
#define D4          16
#define D3           8
#define D2           4
#define D1           2
#define D0           1
#define NOT_READY  (D5 | D3 | D2)     /* if any bits set: NOT READY */

#define REVERSE 0
#define FORWARD 1
#define ADD     1
#define NEW     0
#define DTEXT   0
#define DNUM    1

/* ARexx stuff */
#define HOST_PORT_NAME  "LDP"
struct MsgPort  *rexx_port      = NULL ;
ULONG           RexxSigMask ;   /* signal mask used for WAIT() */

IMPORT struct Gadget EjectGad   ;    /* first gadget in list */
IMPORT struct Gadget NumDisplayGad, TextDisplayGad ;
IMPORT struct Gadget EnterGad   ;  /* will be disabled if pioneer player! */
IMPORT UBYTE         NumBuff[NUMLEN], TextBuff[TEXTLEN] ;

struct MsgPort  *mySerPort = NULL ;
struct IOExtSer *mySerReq  = NULL ;
char   ScratchBuff[MAXOUT] ;
char   inbuff[MAXIN], outbuff[MAXOUT], StatusBuffer[MAXIN] ;
char   *version =  "$VER: LDP by HyperBorea Studio B  V1.31__22.02.92(" ;
char   *CmdTerm = NULL ;         /* command terminator          */
char   *PlayerMode = NULL ;     /* frame mode only for now     */
char   MemAddr[MAXIN]  ;       /* address for remote memorize */
char   CurrentAddr[MAXIN] ;   /* current address of player   */
char   *GadStr[] = { "","","","","","","","","","",
       "Eject","Enter","Search","Still","Clear","Index","Rplay","Fplay",
       "Rstep","Fstep","Rslow","Fslow",
       "Rfast","Ffast","Rscan","Fscan",
       "Memorize","Msearch","Log to ram:*","",""} ;
struct Window        *wp=NULL ;
struct IntuitionBase *IntuitionBase=NULL ;
struct IconBase      *IconBase=NULL ;
ULONG  wakeupmask, UserSigMask ;
BOOL   FscanDOWN = FALSE ;
BOOL   RscanDOWN = FALSE ;
BOOL   SearchPRESSED   = FALSE ;
BOOL   SerDeviceACTIVE = FALSE ;
BOOL   LdpREADY = TRUE  ;
BOOL   IndexON  = TRUE  ;
BOOL   ReversePolish = FALSE ;       /* Pioneer players do arg,verb            */
BOOL   RemoteON = TRUE ;            /* onscreen remote controller             */
BOOL   TimeToExit = FALSE ;        /* used to exit ProcessEvents()           */
USHORT CurrentCmd = 0  ;          /* Current Command                        */
USHORT SonyModel  = OTHER;       /* default                                */
USHORT BaudRate  = 1200  ;      /* Fixed baud rate for SONY  LDP-1000A    */
USHORT PlayerType = sony ;     /* default player                         */
USHORT ReturnBytes       ;    /* number of bytes returned from command  */
USHORT AddrBytes         ;   /* number of bytes returned address query */
USHORT StatBytes         ;  /* number of bytes returned status query  */
USHORT SerialUnit        ; /* serial port unit number                */

char   HelpStr[] = {"\n\
      Laser Disc Player  by Ron M. Battle    HyperBorea Studio B\n\
------------------------------------------------------------------------------\n\
  CLI USAGE: run ldp -bBaud -pPlayerType -rRemoteOn/Off -uSerialUnit#\n\
    example: run ldp -b1200 -pSony       -rOn     -u0   (DEFAULT)\n\
  WB  USAGE: ToolTypes: BAUD  PLAYER  REMOTE  WINDOW  UNIT\n\
    example: BAUD=4800 PLAYER=PIONEER REMOTE=ON WINDOW=CON:20/20/320/70/LDP!\n\
Public Port: LDP   (case sensitive)\n\
       Note: On remote, click in text area, enter note and press LOG,\n\
             current frame# and note will be saved to ram:LDP.log\n\
       Help: Press Help key on keyboard!\n\
 ARexx cmds:\n\
address (returns frame# as 32 bit integer)  baud rate (set baud rate)\n\
clear (clear all commands)  eject (open door)  enter (terminate some commands)\n\
forward howfast (step/slow/fast/scan/normal)   frame (set frame mode)\n\
index toggle (on/off)  init (initialize player)  play start end (play sequence)\n\
quit (shut down program)  ready (returns 1 if player ready)\n\
reverse howfast (step/slow/fast/scan/normal)  search frame# (ASCII digits)\n\
still (still frame)\n\
vplayer type (set player type: sony/hitachi/pioneer/none, returns 32 bit int)\n\n\
\n CLI ARexx example:  rx 'address LDP play 5000 5100'\n\
------------------------------------------------------------------------------\n\n"};

struct NewWindow LdpWindow= {
        10,10, /* window XY origin relative to TopLeft of screen */
        200,162,        /* window width and height */
        0,3,    /* detail and block pens */
        GADGETDOWN+GADGETUP+CLOSEWINDOW+RAWKEY,    /* IDCMP flags */
        WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH+WINDOWSIZING,
        &EjectGad,      /* first gadget in gadget list */
        NULL,   /* custom CHECKMARK imagery */
        (UBYTE *)"Sony LDP",    /* window title */
        NULL,   /* custom screen pointer */
        NULL,   /* custom bitmap */
        144,12,    /* minimum width and height */
        0,0,  /* maximum width and height */
        WBENCHSCREEN    /* destination screen type */
};



void UpdateDisplay(char *dstring, int how, int which)
{
  long              gadpos   ;
  struct Gadget     *gadptr  ;
  struct StringInfo *strgad  ;
  char              *buffptr ;

  if (which == DTEXT) {
    gadptr  = &TextDisplayGad ;
    buffptr = (char *)TextBuff;
  }
  else {
    gadptr  = &NumDisplayGad ;
    buffptr = (char *)NumBuff;
  };

  strgad=(struct StringInfo *)NumDisplayGad.SpecialInfo ;
  gadpos = RemoveGadget(wp,gadptr) ; /* don't change before removing!  */
  if (how == ADD) {                 /* add to existing string         */
    if ((SHORT)strgad->NumChars < (NUMLEN-1)) {
      strcat((char *)buffptr,dstring) ;
    }
    else strcpy((char *)buffptr,dstring) ; /* start over if too many! */
  }
  else {
  strcpy((char *)buffptr,dstring) ;     /* new string              */
  };
  AddGadget(wp,gadptr,gadpos) ;
  RefreshGList(gadptr,wp,NULL,gadpos) ;
}


void SetBaud(USHORT rate)      /* set baud rate and 8N1 */
{
  LONG  error ;

  if (rate>MAX_BAUD || rate<MIN_BAUD) rate=MIN_BAUD ;    /* simple error checking */
  mySerReq->io_Baud     = rate       ;
  mySerReq->io_ReadLen  = READ_BITS  ;
  mySerReq->io_WriteLen = WRITE_BITS ;
  mySerReq->io_StopBits = STOP_BITS  ;

  mySerReq->IOSer.io_Command = SDCMD_SETPARAMS ;
  if ((error = DoIO((struct IORequest *)mySerReq)) != 0L) {
    printf("SetBaud error = %ld\n",error) ;
  };
}

LONG ReadSer(char *data, USHORT length)
 {
   LONG              error = 0L ;
   USHORT            i          ;

   for (i=0; i<MAXIN; inbuff[i]='\0',i++) ;   /* clear out in buffer */

   mySerReq->IOSer.io_Data = (APTR)data ;
   mySerReq->IOSer.io_Length = (ULONG)length ;
   mySerReq->IOSer.io_Command = CMD_READ ;

   if ((error = DoIO((struct IORequest *)mySerReq)) != 0L) {
     printf("ReadSer err# %ld\n",error) ;
   };
   #ifdef DEBUG
     printf("   ReadHex= %X %X %X %X %X \n",
            inbuff[0],inbuff[1],inbuff[2],inbuff[3],inbuff[4]) ;
   #endif
   return (error) ;
 }

LONG WriteSer(char c)
 {
   LONG  error      ;

   outbuff[0] = c   ;
   outbuff[1] = '\0'; /* null terminate just in case! */
   #ifdef DEBUG
     printf("   SendChar= %s\n",outbuff) ;
   #endif

   mySerReq->IOSer.io_Data = (APTR)outbuff ;   /* change to once only!! */
   mySerReq->IOSer.io_Length = 1L   ;
   mySerReq->IOSer.io_Command = CMD_WRITE ;

   if ((error = DoIO((struct IORequest *)mySerReq)) != 0L) {
     printf("WriteSer err# %ld\n",error);
   };
   return (error) ;
 }

LONG SendString(char *buff)
{
   LONG err,len  ;

   len = (long)strlen(buff) ;
   #ifdef DEBUG
     printf("   SendStr=%s\n",buff) ;
   #endif

   mySerReq->IOSer.io_Data = (APTR)buff ;
   mySerReq->IOSer.io_Length = len ;
   mySerReq->IOSer.io_Command = CMD_WRITE ;

   if ((err = DoIO((struct IORequest *)mySerReq)) != 0L) {
     printf("Write to serial port err# %ld\n",err);
   };
   return (err) ;
}


USHORT Ask_Sony_Model(UBYTE byt) /* returns LDP_1000A or OTHER or SLEEPING */
{
  LONG  err, count  ;
  USHORT model, status = FALSE ;

  model  = (byt & D7) ;
  if (model == 0) model = LDP_1000A ;
  status = !(byt & NOT_READY) ;
  if (status == FALSE) model = ASLEEP ;
  return(model) ;
}

void MyCleanup(USHORT err)
{
 if (err) printf("LDP stopped because: ERROR %s\n",ErrStr[err]) ;
 if ( (CheckIO(mySerReq)==FALSE)&&(err != WRITEPORT_ERR) ) {
   AbortIO((struct IORequest *)mySerReq) ; /* abort any pending requests */
   WaitIO((struct IORequest *)mySerReq)  ;
 };
 if (SerDeviceACTIVE) CloseDevice((struct IORequest *)mySerReq) ;
 if (wp != NULL) CloseWindow(wp) ;
 if (IntuitionBase != NULL) CloseLibrary(IntuitionBase) ;
 if (mySerReq != NULL) DeleteExtIO((struct IORequest *)mySerReq);
 if (mySerPort != NULL) DeletePort(mySerPort) ;
 if (rexx_port != NULL) DeletePort(rexx_port) ;
 puts("   Please turn OFF the player when DONE!");
}


/* ARexx routines */

void SetupRexxPort(void)
{
   struct MsgPort *the_port=NULL ;

   Forbid() ;
   if (FindPort(HOST_PORT_NAME)) {
     Permit() ;
     printf("A public port called '%s' already exists!\n",HOST_PORT_NAME) ;
     the_port=NULL ;
   }
   the_port = CreatePort(HOST_PORT_NAME,0L) ;
   Permit() ;

   if (the_port != NULL) {
     RexxSigMask = (1L<<the_port->mp_SigBit) ;  /* setup signal bit */
     rexx_port = the_port ;
   }
   else {
     MyCleanup(REXX_ERR) ;
     exit(FALSE) ;
   }
}

void ReplyRexxMessage(struct RexxMsg *rexxmessage, long result)
{
  rexxmessage->rm_Result1 = result ;
  rexxmessage->rm_Result2 = 0L  ;
  ReplyMsg((struct Message *)rexxmessage) ;
}

void DoSimpleCmd(char *cmd)
{

    strcpy(outbuff,cmd) ;              /* now build output buffer    */
    strcat(outbuff,CmdTerm) ;
    SendString(outbuff) ;            /* send string to serial port */
    ReadSer(inbuff,ReturnBytes);    /* see what player returns    */
}

void ClearSerialBuffer(void)
{
   ULONG   error ;

  mySerReq->IOSer.io_Command = CMD_CLEAR; /* clear out device buffer */
  if ((error = DoIO((struct IORequest *)mySerReq)) != 0L) {
    printf("Clear serial buffer error# %ld\n",error);
  }
}


long GetAddress(char *cmd)     /* return player address as long int */
{
    USHORT x,*intp ;
    int    y       ;
    long   addr=0  ;

    ClearSerialBuffer() ;
    /* now build output buffer */
    strcpy(outbuff,cmd) ;                /* command                    */
    strcat(outbuff,CmdTerm) ;           /* command terminator, if any */
    SendString(outbuff) ;              /* send string to serial port */
    ReadSer(inbuff,AddrBytes) ;       /* see what player returns    */
    if(PlayerType == hitachi) {
      intp = (USHORT *)(inbuff+1) ;    /* skip past echo, hi-lo bytes follow */
      addr = (long)*intp ;            /* convert to long int                */
      y = sprintf(CurrentAddr,"%ld\0",addr);        /* save addr as string */
    }
    else {
      inbuff[5] = '\0' ;                      /* null terminate string      */
      strcpy(CurrentAddr,inbuff);     /* save current address       */
      addr = atol(inbuff) ;      /* convert string to signed long integer */
    };
    return(addr) ;
}

void SetIndex(char *toggle)
{
    int  offset = on  ;    /* default */
    char *cmd ;

    if (toggle != NULL) {
      if(strncmp(toggle,indexstr[off],2L)==0) offset = off ;
    };     /* now set a pointer to the correct index cmd */
    cmd = Player[PlayerType].index[offset] ;
    /* now build output buffer */
    strcpy(outbuff,cmd) ;
    strcat(outbuff,CmdTerm) ;         /* add any cmd terminator     */
    SendString(outbuff) ;             /* send string to serial port */
    ReadSer(inbuff,ReturnBytes);     /* see what player returns    */

}

void Step1KA(int direction)     /* Sony 1000A won't do single command stepping */
{                              /* Actually the 2000 wouldn't either so all    */
                              /* sonys now use this routine!                 */
                             /* has problems when stepping near frame 1-2   */

    if (direction==FORWARD) {      /* forward step       */
      WriteSer('=');        /* send F-STEP        */
      ReadSer(inbuff,1) ;  /* expect ACK         */
      WriteSer('O');      /* send STILL         */
    }
    else {   /* reverse step */
      WriteSer('M');      /* send R-STEP       */
      ReadSer(inbuff,1);
      WriteSer('O');    /* send STILL command, last ReadSer from Go() */
    }
}


void Go(int direction, char *speed) /* tried to be generic except for Step-1KA  */
{
  int    i=0,HowFast=0      ;
  USHORT type=PlayerType,TotSpeeds=NUMSPEEDS-1 ;

  /* now change string into an index */
  while ((i < TotSpeeds) && (strncmp(speed,speedstr[i],3L) !=0)) i++ ;
  HowFast = i ;                            /* if no match, defaults to normal */
  if (direction==FORWARD) strcpy(outbuff,Player[type].forward[HowFast]);
  else strcpy(outbuff,Player[type].reverse[HowFast]);
  strcat(outbuff,CmdTerm) ;  /* add any terminator */
  if ((type==sony)&&(HowFast==step)) Step1KA(direction);
  else SendString(outbuff) ;
  ReadSer(inbuff,ReturnBytes) ;
}


void GoSearch(char *addr)
{
    char  *cmd,*temp,*fmode,*entercmd ;
    long  num ;

    num = atol(addr) ;
    if (num<1) addr="1" ;           /* error checking */
    if (num>54000) addr="54000" ;

    cmd = Player[PlayerType].generic[search] ;       /* get the search cmd   */
    fmode = PlayerMode ;                            /* get current mode cmd */
    entercmd = Player[PlayerType].generic[enter];  /* get enter cmd        */

    if (ReversePolish == TRUE) {      /* pioneer players are like this    */
      strcpy(outbuff,fmode) ;                 /* frame mode first        */
      strcat(outbuff,addr) ;                 /* put address in second   */
      strcat(outbuff,cmd)  ;                /* now add the command     */
      strcat(outbuff,CmdTerm);             /* add any terminator      */
      SendString(outbuff) ;               /* send the whole thing    */
      ReadSer(inbuff,ReturnBytes);       /* expect R and CR         */
    }
    else {
      if (PlayerType == sony) {           /* sony players         */
        strcpy(ScratchBuff,fmode);       /* frame mode first     */
        strcat(ScratchBuff,cmd)  ;      /* put command in next  */
        strcat(ScratchBuff,addr) ;     /* now add the address  */
        strcat(ScratchBuff,entercmd); /* add the enter cmd    */
        strcat(ScratchBuff,CmdTerm) ;/* add terminator       */
        for(temp=ScratchBuff; *temp!='\0'; temp++) {
          WriteSer(*temp) ;             /* send single char     */
          ReadSer(inbuff,ReturnBytes); /* expect ACK each time */
        };
        ReadSer(inbuff,1) ;  /* expect completion code */
      }  /* end if sony */
      else {                                /* hitachi players      */
        strcpy(outbuff,cmd)  ;             /* put command in first */
        strcat(outbuff,fmode) ;           /* frame mode next      */
        strcat(outbuff,cmd)  ;           /* put command in next  */
        strcat(outbuff,addr) ;          /* now add the address  */
        strcat(outbuff,entercmd);      /* add the enter cmd    */
        SendString(outbuff) ;         /* send the whole thing */
        ReadSer(inbuff,2) ;     /* expect enter and job done */
      }  /* else hitachi        */
    }   /* else reverse polish */
}      /* proc                */



BOOL CheckReady(void)
{
    char  *cmd ;
    BOOL  ok=TRUE,myIO ;


    ClearSerialBuffer() ;    /* make sure no garbage left over in buffer */
    cmd = Player[PlayerType].generic[ready]  ;
    /* now build output buffer */
    strcpy(outbuff,cmd) ;         /* command                    */
    strcat(outbuff,CmdTerm) ;    /* cmd terminator, if any     */
    SendString(outbuff) ;       /* send string to serial port */

    mySerReq->IOSer.io_Data = (APTR)inbuff ;
    mySerReq->IOSer.io_Length = (ULONG)StatBytes ;
    mySerReq->IOSer.io_Command = CMD_READ ;

    SendIO((struct IORequest *)mySerReq) ;     /* asynchronous        */
    Delay(4L) ;                               /* 4/60 sec delay      */
    myIO = CheckIO((struct IORequest *)mySerReq) ;   /* see if done */
    if (myIO == FALSE) {
      Delay(600L) ;          /* wait about 10 more seconds */
      myIO = CheckIO((struct IORequest *)mySerReq) ;
      if (myIO == FALSE) {
        AbortIO((struct IORequest *)mySerReq) ;
        SonyModel = ASLEEP ;
        return(FALSE) ;
      }
    } ;
    GetMsg(mySerPort) ;  /* recycle message */

    switch (PlayerType) {
      case sony     : /* Bit7 is zero for LDP-1000A */
                      #ifdef DEBUG
                        printf("   StatusIn= %d\n",*inbuff) ;
                      #endif
                      SonyModel = Ask_Sony_Model(inbuff[0]) ;
                      if (SonyModel == ASLEEP) ok = FALSE   ;
                      break ;

      case hitachi  : ok = TRUE ;   /* no ready status available! */
                      break ;

      case pioneer  : /* buffer contents might be P01,P02,P06,etc. */
                      #ifdef DEBUG
                      printf("   StatusIn= %s\n",inbuff) ;
                      #endif
                   /* if (inbuff[2] < '4') ok = FALSE ;  */
                      ok = TRUE ;  /* Don't look at what is returned! */
                      break ;

      default       : break ;

    } ;
    return (ok) ;
}

void InitPlayer(void)
{
 char  *temp, *MyInit ;

 if (PlayerType != none) {
    SetBaud(BaudRate) ;
    ReversePolish = Player[PlayerType].rp ;      /* as in PIONEER!            */
    CmdTerm = Player[PlayerType].term ;         /* set up command terminator */
    PlayerMode = Player[PlayerType].generic[frame]; /* frame mode default   */
    ReturnBytes = Player[PlayerType].returnbytes;  /* command return bytes */
    StatBytes = Player[PlayerType].statbytes;  /* # status bytes returned */
    AddrBytes = Player[PlayerType].addrbytes; /* # address ''    ''      */
    MyInit    = Player[PlayerType].generic[init] ;

    if (PlayerType==hitachi) DoSimpleCmd(MyInit) ;
    LdpREADY = CheckReady() ;
    if (LdpREADY) {
      if (PlayerType != sony) Go(FORWARD,"normal") ;   /* spin it up           */
      if (PlayerType == pioneer) DoSimpleCmd(MyInit);
      if (PlayerType == sony) {     /* can't send whole string at once */
        for (temp=MyInit; *temp != '\0'; temp++) {
          WriteSer(*temp) ;             /* send single char     */
          ReadSer(inbuff,ReturnBytes); /* expect ACK each time */
        }
      }
      SetIndex("on") ;                    /* set on-screen display       */
      GoSearch("1")  ;                   /* now go to first frame       */
    }
 }
 else LdpREADY = FALSE ;
}

void PlaySequence(char *start,char *end)  /* I got tired of being GENERIC! */
{                                        /* The following is BRUTE FORCE! */

    char    *temp,*entercmd,*searchcmd,*repeatcmd,*playcmd,*mem1 ;
    USHORT  x ;
    long    startnum,endnum ;

    entercmd  = Player[PlayerType].generic[enter]  ;
    searchcmd = Player[PlayerType].generic[search] ;
    repeatcmd = Player[PlayerType].generic[repeat] ;
    mem1      = Player[PlayerType].special1        ; /* hitachi */

    startnum = atol(start) ;
    endnum   = atol(end)   ;
    if (startnum<1) start="1" ;           /* error checking */
    if (startnum>54000) start="27000" ;
    if (endnum<1) end ="1" ;
    if (endnum>54000) end="27000" ;
    startnum = atol(start) ;
    endnum   = atol(end)   ;

    switch (PlayerType) {

      case sony  :
        SendString(PlayerMode) ;
        ReadSer(inbuff,1) ;
        SendString(searchcmd)  ;
        ReadSer(inbuff,1) ;
        for(temp=start; *temp!='\0'; temp++) {
          WriteSer(*temp) ;     /* send single char     */
          ReadSer(inbuff,1);   /* expect ACK each time */
        };
        SendString(entercmd) ;
        ReadSer(inbuff,1) ;
        ReadSer(inbuff,1) ;   /* expect completion code */
        SendString(repeatcmd) ;
        ReadSer(inbuff,1) ;
        for(temp=end; *temp!='\0'; temp++) {
          WriteSer(*temp) ;     /* send single char     */
          ReadSer(inbuff,1);   /* expect ACK each time */
        };
        SendString(entercmd) ;
        ReadSer(inbuff,1) ;
        SendString("1") ;
        ReadSer(inbuff,1) ;
        SendString(entercmd) ;
        ReadSer(inbuff,1) ;
        ReadSer(inbuff,1) ;   /* get completion code */
        break ;

      case pioneer :   /* ok to send up to 20 char in one command, CR terminated */
        if (startnum < endnum) playcmd = Player[pioneer].forward[normal] ;
        else                   playcmd = Player[pioneer].reverse[normal] ;
        strcpy(outbuff,PlayerMode) ;         /* frame mode               */
        strcat(outbuff,start) ;             /* put search address       */
        strcat(outbuff,searchcmd);         /* put SEarch command       */
        strcat(outbuff,CmdTerm)  ;        /*  CR terminator           */
        SendString(outbuff);
        ReadSer(inbuff,ReturnBytes);
        strcpy(outbuff,end);              /* put play to address      */
        strcat(outbuff,"SM");            /* Stop Marker              */
        strcat(outbuff,playcmd) ;       /* put multi-speed command  */
        strcat(outbuff,CmdTerm) ;      /* terminate command (CR)   */
        SendString(outbuff)     ;     /* send commands to player  */
        ReadSer(inbuff,ReturnBytes); /* expect back R and CR     */
        break ;

      case hitachi : /* ok to send as a string */
        strcpy(outbuff,searchcmd) ;         /* search command first      */
        strcat(outbuff,PlayerMode);        /* frame command next        */
        strcat(outbuff,start)     ;       /* now start address         */
        strcat(outbuff,mem1)      ;      /* remember this location    */
        strcat(outbuff,end)       ;     /* now end address           */
        strcat(outbuff,entercmd)  ;    /* terminate with enter      */
        SendString(outbuff)       ;   /* send it out               */
        ReadSer(inbuff,2)         ;  /* expect ENTER & JOB END    */
        break ;                     /* JOB END = SEARCH+80H      */

      default       :
        break ;
    }  /* switch */
}     /* proc   */


long DoCommand(char *cmd, char *arg1, char *arg2)
{
  int  c=0,i   ;
  long result= 0   ;
  char *current= NULL ;  /* pointer to current command */

  /* now check only the 1st 3 letters of command and increment cmd index */
  while ((c < NUMCMDS) && (strncmp(cmd, VideoCmd[c],3) != 0)) c++ ;
  CurrentCmd = c ; /* global variable, use to index into generic commands */
  current = Player[PlayerType].generic[CurrentCmd];
  switch (c) {

    case address    :   if (LdpREADY) {
                          result = GetAddress(current) ;
                        };
                        break ;


    case baud       :  /* set baud rate of AMIGA serial port */
                      /* laser player should MATCH rate!    */
                     /* usual rates are  1200,4800,9600    */
                      if (arg1 != NULL) {
                        BaudRate = (USHORT)atoi(arg1); /* convert str->int */
                        SetBaud(BaudRate)       ;
                      } ;
                      result = (long)BaudRate ;  /* return long integer */
                      break ;

    case clear      :   if (LdpREADY) {
                          ClearSerialBuffer()  ;
                          DoSimpleCmd(current) ;
                        };
                        break ;

    case eject      :   if (LdpREADY) DoSimpleCmd(current) ;
                        break ;

    case enter      :   if (LdpREADY) DoSimpleCmd(current) ;
                        break ;

    case forward    :   if (LdpREADY) Go(FORWARD,arg1) ;
                        break ;

    case frame      :   if (LdpREADY) DoSimpleCmd(current) ;
                        break ;

    case index      :   if (LdpREADY) SetIndex(arg1) ;
                        break ;

    case init       :   InitPlayer() ;
                        result = (long)LdpREADY ;
                        break ;

    case play       :   if (LdpREADY) PlaySequence(arg1,arg2) ;
                        break ;

    case quit       :   TimeToExit = TRUE ;  /* shutdown the program */
                        break ;             /* useful if no remote! */

    case ready      :   result = (long)CheckReady() ;
                        break ;

    case reverse    :   if (LdpREADY) Go(REVERSE,arg1) ;
                        break ;

    case search     :   if (LdpREADY) GoSearch(arg1) ;
                        break ;


    case still      :   if (LdpREADY) DoSimpleCmd(current)  ;
                        break ;

    case vplayer    :  /* select video player type                      */
                      /* echo back or return current player if no arg1 */
                      if(arg1 != NULL) {
                       result = (long)(*arg1); /* echo char if not illegal */
                        switch(*arg1) {  /*  *arg1 is 1st char of string! */
                          case 's' : PlayerType = sony ;
                                     InitPlayer()   ;
                                     break ;
                          case 'p' : PlayerType = pioneer ;
                                     InitPlayer() ;
                                     break ;
                          case 'h' : PlayerType = hitachi ;
                                     InitPlayer() ;
                                     break ;
                          case 'n' : PlayerType = none    ;
                                     LdpREADY = FALSE     ;
                                     break ;
                          default  : result = (long)*PlayerStr[PlayerType] ;
                                     break ; /* return current if illegal */
                        }  /* switch */
                      }   /* if     */
                      else result = (long)*PlayerStr[PlayerType] ;
                      if(wp!=NULL)SetWindowTitles(wp,PlayerStr[PlayerType],NULL);
                      break ;


    default         :   puts("ILLEGAL command!!") ;
                        result = 255L ;
                        break ;

  };
  return(result) ;
}

void execute_command(struct RexxMsg *rexxmessage)
{
  char    *token[4], *temp  ;        /* token[0]=Command, others=arguments   */
  int     err               ;       /* this routine crashes system if total */
  USHORT  x                 ;      /* tokens not 1 greater!                */
  long    result=0          ;     /* eg: cmd arg1 arg2    tokens=3+1      */

  for (x=0; x<4; token[x]=NULL,x++) ;     /* clear out token buffer */
  token[0] = (char *)rexxmessage->rm_Args[0] ;     /* parse ARexx string, */
  token[0] = strtok(token[0],(char *)" ,") ;      /* look for space or , */
  for (x=1; (token[x] = strtok(NULL,(char *)" ,")) != NULL; x++) ;
  for (x=0; x<4; x++) {               /* convert all strings to lower-case */
    strlwr(token[x]) ;
  } ;
  result = DoCommand(token[0],token[1],token[2]) ;
  ReplyRexxMessage(rexxmessage,result) ;
}

/*  Process Gadgets  */

void ProcessGadgetUp(USHORT mygad)
{
    char   *addrcmd=Player[PlayerType].generic[address]  ;
    char   ThisChar[2], *num, PriorStr[TEXTLEN] ;
    LONG   temp      ;
    FILE   *NoteFile ;

    switch (mygad) {

      case Memorize   :    strcpy(PriorStr,(char *)TextBuff); /* save prior info */
                           UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           temp=GetAddress(addrcmd) ;
                           addrcmd=strcpy(MemAddr,CurrentAddr); /* save MemAddr */
                           UpdateDisplay(MemAddr,NEW,DNUM) ;
                           Delay(30L) ;
                           UpdateDisplay(PriorStr,NEW,DTEXT) ;
                           break ;

      case Msearch    :    UpdateDisplay(MemAddr,NEW,DNUM) ;
                           UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           GoSearch(MemAddr) ;
                           break ;

      case Log        :    strcpy(PriorStr,(char *)TextBuff) ;     /* save note */
                           UpdateDisplay(GadStr[mygad],NEW,DTEXT) ; /* logging */
                           temp=GetAddress(addrcmd) ; /* show current address */
                           UpdateDisplay(CurrentAddr,NEW,DNUM) ;
                           NoteFile = fopen("ram:LDP.log","a") ; /* save to file */
                           fprintf(NoteFile,"%s  %s\n",CurrentAddr,PriorStr) ;
                           fclose(NoteFile) ;
                           Delay(30L) ;
                           UpdateDisplay(PriorStr,NEW,DTEXT) ; /* reshow note */
                           break ;


      case Index      :    IndexON ^= TRUE ;                /* toggle on/off */
                           if (IndexON) SetIndex("on");
                           else SetIndex("off");
                           break ;

      case NumDisplay :    if (PlayerType == pioneer) {
                             UpdateDisplay("Search",NEW,DTEXT) ;
                             SearchPRESSED = TRUE ;
                             GoSearch((char *)NumBuff) ;
                           }
                           else {
                             if (SearchPRESSED) {
                               SearchPRESSED = FALSE ;
                               UpdateDisplay(GadStr[Enter],NEW,DTEXT) ;
                               if (PlayerType == hitachi) {
                                 SendString((char *)NumBuff) ;
                               }
                               else {        /* sony players */
                                 for (num=(char *)NumBuff; *num !='\0'; num++) {
                                   WriteSer(*num) ;
                                   ReadSer(inbuff,1) ;
                                 }
                               };
                               SendString(Player[PlayerType].generic[enter]);
                               /* expect back ACK,COMPLETION or ECHO,JOB DONE */
                               ReadSer(inbuff,2) ;
                             }
                             else {
                               UpdateDisplay("Search/Enter",NEW,DTEXT) ;
                               GoSearch((char *)NumBuff) ;
                             }
                           };
                           break ;


      case Search     :    if ((SearchPRESSED==TRUE) && (PlayerType!=pioneer)) {
                             UpdateDisplay("ERROR!",NEW,DTEXT) ;
                             break ;
                           };
                           UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           SearchPRESSED = TRUE ;
                           if (PlayerType != pioneer) UpdateDisplay("",NEW,DNUM);
                           else SendString((char *)NumBuff);
                           DoSimpleCmd(Player[PlayerType].generic[search]);
                           if (PlayerType == hitachi) {
                             DoSimpleCmd(Player[hitachi].generic[frame]) ;
                           };
                           break ;

      case Still      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           DoSimpleCmd(Player[PlayerType].generic[still]) ;
                           Delay(1L) ;
                           temp=GetAddress(addrcmd) ; /* show current address */
                           UpdateDisplay(CurrentAddr,NEW,DNUM) ;
                           break ;


      case Clear      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           UpdateDisplay("",NEW,DNUM)  ;
                           ClearSerialBuffer() ;
                           DoSimpleCmd(Player[PlayerType].generic[clear]) ;
                           SearchPRESSED = FALSE ;
                           break ;

      case Enter      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT);
                           if (SearchPRESSED) {   /* send buff next */
                             SearchPRESSED = FALSE ;
                             if (PlayerType == hitachi) {
                               SendString((char *)NumBuff) ;
                             }
                             else {        /* for sony: send single chars  */
                               for (num=(char *)NumBuff; *num !='\0'; num++) {
                                 WriteSer(*num) ;
                                 ReadSer(inbuff,1) ;
                               }
                             };
                             SendString(Player[PlayerType].generic[enter]);
                             /* expect back ACK,COMPLETION or ECHO,JOB DONE */
                             ReadSer(inbuff,2) ;
                           }
                           else UpdateDisplay("ERROR!",NEW,DTEXT) ;
                           break ;

      case Eject      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           DoSimpleCmd(Player[PlayerType].generic[eject]) ;
                           break ;


      case Fplay      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(FORWARD,"normal") ;
                           break ;

      case Rplay      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(REVERSE,"normal") ;
                           break ;

      case Fslow      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(FORWARD,"slow")  ;
                           break ;

      case Rslow      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(REVERSE,"slow") ;
                           break ;

      case Ffast      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(FORWARD,"fast") ;
                           break ;

      case Rfast      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(REVERSE,"fast") ;
                           break ;

      case Fscan      :    FscanDOWN = FALSE ; /* scan button UP is still frame */
                           DoSimpleCmd(Player[PlayerType].generic[still]) ;
                           Delay(4L) ;
                           temp=GetAddress(addrcmd) ; /* show current address */
                           UpdateDisplay(CurrentAddr,NEW,DNUM) ;
                           break ;

      case Rscan      :    RscanDOWN = FALSE ;
                           DoSimpleCmd(Player[PlayerType].generic[still]) ;
                           Delay(4L) ;
                           temp=GetAddress(addrcmd) ; /* show current address */
                           UpdateDisplay(CurrentAddr,NEW,DNUM) ;
                           break ;

      case Fstep      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(FORWARD,"step");
                           Delay(1L) ;
                           temp=GetAddress(addrcmd) ; /* show current address */
                           UpdateDisplay(CurrentAddr,NEW,DNUM) ;
                           break ;

      case Rstep      :    UpdateDisplay(GadStr[mygad],NEW,DTEXT) ;
                           Go(REVERSE,"step");
                           Delay(1L) ;
                           temp=GetAddress(addrcmd) ; /* show current address */
                           UpdateDisplay(CurrentAddr,NEW,DNUM) ;
                           break ;

      case Zero: case One: case Two: case Three: case Four:
        ThisChar[0] = (mygad+48) ;
        ThisChar[1] = '\0' ;
        UpdateDisplay(ThisChar,ADD,DNUM) ; /* accumulate chars in NumBuff */
        break ;

      case Five: case Six: case Seven: case Eight: case Nine:
        ThisChar[0] = (mygad+48) ;
        ThisChar[1] = '\0' ;
        UpdateDisplay(ThisChar,ADD,DNUM) ;
        break ;


      default         :     break ;

    }    /* switch */
}       /* proc   */


void ProcessGadgetDown(USHORT mygad)
  {
   switch (mygad) {

     case Fscan   :  UpdateDisplay("Fscan   ",NEW,DTEXT) ;
                     FscanDOWN = TRUE ;
                     Go(FORWARD,"scan") ;
                     break ;

     case Rscan   :  UpdateDisplay("Rscan   ",NEW,DTEXT) ;
                     RscanDOWN = TRUE ;
                     Go(REVERSE,"scan") ;
                     break ;

     default      :  break ;

   }  /* switch */
  }  /* proc   */

void ProcessEvents(void)
{
    struct IntuiMessage *eventptr    ;
    struct RexxMsg      *rexxmessage ;
    ULONG               class        ;
    USHORT              code, mygad  ;
    struct Gadget       *GadPtr      ;
    SHORT               y            ;

  while (!TimeToExit)  {

    wakeupmask = Wait(UserSigMask | RexxSigMask) ; /* now wait for events... */

    if (wakeupmask & RexxSigMask) {               /* process Rexx commands */
      while ( (rexxmessage = (struct RexxMsg *)GetMsg(rexx_port)) ) {
        execute_command(rexxmessage)   ;
      }
    } ;

    if (wakeupmask & UserSigMask) {  /* process remote controller commands */
      while ( (eventptr = (struct IntuiMessage *)GetMsg(wp->UserPort)) ) {

        class  = eventptr->Class    ;
        code   = eventptr->Code     ;       /* Save values of interest */
        GadPtr = (struct Gadget *)eventptr->IAddress ;
        y      = eventptr->MouseY   ;

        ReplyMsg( (struct Message *) eventptr ) ; /* Reply QUICKLY! */
        mygad = GadPtr->GadgetID ;

        switch (class)  {

          case GADGETUP    :   if (LdpREADY) {
                                 ProcessGadgetUp(mygad) ;
                               };
                               break ;

          case GADGETDOWN  :   if (LdpREADY) {
                                 ProcessGadgetDown(mygad) ;
                               };
                               break ;

          case CLOSEWINDOW :   TimeToExit = TRUE ;
                               break ;

          case RAWKEY      :   if (code == HELP) printf("%s",HelpStr) ;
                               break ;

          default          :   break ;

        }    /* switch            */
      }     /* while             */
    }      /* if UserSigMask    */
  } ;     /* while timetoexit  */

  while ( (eventptr = (struct IntuiMessage *)GetMsg(wp->UserPort)) ) {
    ReplyMsg( (struct Message *) eventptr ) ;  /* clear out intuition messages */
  };
  while ( (rexxmessage = (struct RexxMsg *)GetMsg(rexx_port)) ) {
    ReplyMsg( (struct Message *) rexxmessage) ; /* clear out arexx messages */
  }
}    /* proc   */


void SetupPorts(void)
{ LONG      i, error,unit=(long)SerialUnit ;
  BYTE      *b, *c ;

  mySerPort = CreatePort("mySerialPort",0L) ;
  if (mySerPort == NULL) {
    MyCleanup(WRITEPORT_ERR) ;
    exit(FALSE) ;
  };
  i = (LONG)sizeof(struct IOExtSer) ;
  mySerReq = (struct IOExtSer *)CreateExtIO(mySerPort,i) ;
  if (mySerReq == NULL) {
    MyCleanup(READ_EXTIO_ERR) ;
    exit(FALSE) ;
  } ;
  mySerReq->io_SerFlags = (SERF_RAD_BOOGIE | SERF_7WIRE);         /* FAST I/O! */
  error = OpenDevice("serial.device",unit,(struct IORequest *)mySerReq,0L) ;
  if (error != 0) {
    MyCleanup(DEVICE_ERR) ;
    exit(FALSE) ;
  }
  else SerDeviceACTIVE = TRUE ;
}


void OpenLibs(void)
{
  IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",0L);
  if (IntuitionBase == NULL) {
    MyCleanup(INT_ERR) ;
    exit(FALSE) ;
  }
}

void InitWindow(void)
{
  LdpWindow.Title = (UBYTE *)PlayerStr[PlayerType] ;
  if (SonyModel == LDP_1000A) LdpWindow.Title = (UBYTE *)"SONY-1KA" ;
  if (LdpREADY == FALSE) {
    strcpy(ScratchBuff,(char *)LdpWindow.Title) ;
    strcat(ScratchBuff," ASLEEP!") ;
    LdpWindow.Title = (UBYTE *)ScratchBuff ;
  } ;
  if ((wp = OpenWindow(&LdpWindow)) == NULL) {
    MyCleanup(WIN_ERR) ;
    exit(FALSE) ;
  } ;
  if (PlayerType == pioneer) OffGadget(&EnterGad,wp,NULL); /* disable Enter */
  if (LdpREADY != TRUE) UpdateDisplay("RESTART!",NEW,DTEXT) ;
  UserSigMask  =  (1L << wp->UserPort->mp_SigBit) ; /* set up signal for events */
}

void SetPlayer(char *type)
{
      switch (*type) {  /* look at first character */
        case 's' : PlayerType = sony ;
                   break ;
        case 'p' : PlayerType = pioneer ;
                   break ;
        case 'h' : PlayerType = hitachi ;
                   break ;
        case 'n' : PlayerType = none    ;
                   break ;
        default  : PlayerType = sony    ;    /* default player is sony */
                   break ;
      }  /* switch */
}

void ParseCmdLine(char *arg[])        /* global variables will be modified */
{
  USHORT i ;
  char   *temp,*baud,*remote,*player,*unit ;

  if (strcmp(arg[1],"?")==0) {      /* give help */
    printf("%s",HelpStr) ;
    arg[1]="-pnone"      ;
  };
  for (i=0; arg[i] != NULL ; i++) {  /* convert to lower-case first */
    for (temp = arg[i]; *(temp) != '\0'; temp++) *temp=tolower(*temp) ;
    if ((baud = strchr(arg[i],'b'))!= NULL) {  /* look for baud rate */
      baud++ ;  /* increment pointer past 'b' */
      BaudRate = (USHORT)atoi(baud) ; /* convert string to integer value */
    } ;
    if ((player = strchr(arg[i],'p'))!= NULL) { /* look for player name */
      player++ ; /* increment pointer past 'p' */
      SetPlayer(player) ;
    };  /* if     */
    if ((remote = strchr(arg[i],'r'))!= NULL) {  /* look for remote toggle*/
      remote++ ; /* increment pointer past 'r' */
      if (strncmp(remote,indexstr[off],2L)==0) RemoteON = FALSE ;
    };
    if ((unit = strchr(arg[i],'u'))!= NULL) {   /* look for serial unit#      */
      unit++ ;                                 /* increment pointer past 'u' */
      SerialUnit = (USHORT)atoi(unit) ;       /* convert str to integer     */
      if (SerialUnit < MIN_UNIT || SerialUnit > MAX_UNIT) SerialUnit = MIN_UNIT ;
    }  /* if       */
  }   /* for loop */
}    /* proc     */

void GetTooltypes(struct WBStartup *msg)
{
 struct WBArg      *arg ;
 struct DiskObject *diskobj ;
 char              **toolarray ; /* pointer to an array of string pointers */
 char              *value      ;

 if ((IconBase = OpenLibrary(ICONNAME,1L)) == NULL) {
   MyCleanup(ILIB_ERR) ;
   exit(FALSE) ;
 };
 arg=msg->sm_ArgList;         /* get argument list from WB startup msg     */
 diskobj = GetDiskObject(arg->wa_Name) ;        /* now get info from icon */
 toolarray = diskobj->do_ToolTypes  ;          /* setup ToolType array   */
 value = FindToolType(toolarray,"PLAYER") ;
 strlwr(value) ;                           /* convert to lower case */
 SetPlayer(value) ;                       /* setup player type     */
 value = FindToolType(toolarray,"BAUD") ;
 BaudRate = (USHORT)atoi(value) ;         /* setup baud rate    */
 if (BaudRate == 0) BaudRate = MIN_BAUD ;
 value = FindToolType(toolarray,"REMOTE") ;
 if (stricmp(value,"OFF")==0) RemoteON = FALSE ;  /* setup onscreen display */
 else RemoteON = TRUE ;
 value = FindToolType(toolarray,"UNIT") ;
 SerialUnit = (USHORT)atoi(value) ;            /* setup serial unit#      */
 if (SerialUnit < MIN_UNIT || SerialUnit > MAX_UNIT) SerialUnit = MIN_UNIT ;

 FreeDiskObject(diskobj);      /* cleanup */
 CloseLibrary(IconBase) ;
}


/*  *******  MAIN  PROGRAM  ******  */

main(int argc, char *argv[])
{

  puts("") ;
  if (argc > 1) ParseCmdLine(argv) ;                       /* cmd line startup  */
  else if (argc == 0) GetTooltypes((struct WBStartup *)argv) ;  /* WB startup  */
  puts("   Default is SONY 1200Baud RemoteON") ;

  OpenLibs() ;
  SetupPorts()  ;
  ClearSerialBuffer() ;
  SetupRexxPort() ;

  puts("   Waiting......") ;
  InitPlayer()  ;
  if (RemoteON) InitWindow() ;                   /* show controller on screen */
  if (!LdpREADY) puts("   Need to RESTART!") ;
  else puts("   Player READY!") ;

  ProcessEvents() ;                            /* Main loop */
  MyCleanup(NO_ERR) ;

}
