/* ms2smus:
 *       Convert Music Studio score to SMUS format
 */

#include "smus.h"
#include "music.h"
#include <libraries/dosextens.h>
#include <workbench/startup.h>


/* For WorkBench startup */
extern struct WBStartup *WBenchMsg;
BOOL fromWB;
struct FileLock *startLock, *newLock;

extern char *NextCmd();
extern GroupContext *OpenWSmus();
extern BOOL CloseWSmus();

static IFFP ifferror = IFF_OKAY;

/*
 * Find a given command in the setup frame
 *  and return a pointer to it, or NULL if not present.
 */
char *
FindCmd(CmdChar)
   char CmdChar;
{
   int i;
   char *CmdPtr = NULL;

   for (i = 0; i < 11; i++)
      if (song[i] == CmdChar)
         CmdPtr = &song[i];
   return(CmdPtr);
} /* FindCmd */


/*
 * Read first frame of song to make IFF SHDR
 */
Setup2Shdr(Context,Song)
   GroupContext *Context;
   char *Song;
{
   static   SScoreHeader   Shdr;
   char  *SongCmd;

   SongCmd = FindCmd(CHGTEMPO);
   if (SongCmd == NULL)
      Shdr.tempo = (DEFAULT_TEMPO << 7);
   else
      Shdr.tempo = (SongCmd[1] & 0xFF) << 7; /* tempo one byte past CHGT */

   SongCmd = FindCmd(CHGVOL);
   if (SongCmd == NULL)
      Shdr.volume = DEFAULT_VOLUME;          /* 0x7F is default intro vol */
   else
      Shdr.volume = SongCmd[2];     /* vol 2 bytes past CHGVOL command */

   Shdr.ctTrack = NumTracks;
   if (Debug) {
      printf("     Tempo = %lx\n",Shdr.tempo);
      printf("     Volume = %d\n",Shdr.volume);
      printf("     Tracks = %d\n",Shdr.ctTrack);
   }
   CkErr(PutCk(Context, ID_SHDR, sizeof(SScoreHeader), (BYTE *) &Shdr) );
} /* Setup2Shdr */

/*
 * Convert MS stitle to IFF NAME ck
 */
Hdr2Name(Context)
   GroupContext *Context;
{
   static char mstitle[33];

   (VOID) strcpy(mstitle,"untitled");
   if (strlen(stitle) > 0)
   {
      (VOID) strncpy(mstitle, stitle, MS_NAME_MAX-1);
   }
   CkErr(PutCk(Context, ID_NAME, strlen(mstitle), (BYTE *) mstitle));
   if (Debug)
      printf("  Name: '%s'\n",mstitle);
} /* Hdr2Name */



#define RI_SIZE (sizeof(RefInstrument) + 9)

/*
 * Make IFF INS1 cks from MS adsrnames, midnames,
 *  midchannel's and midpre's
 */
Hdr2Ins1s(Context)
   GroupContext *Context;
{
   char RefBuff[RI_SIZE];
   RefInstrument *MyRef;
   int RefSize = RI_SIZE;
   int trk = 1;
   int i,j,WhichInst;

   MyRef = (RefInstrument *) RefBuff;  /* 'overlay' MyRef on RefBuff */
   for (i = 1; i <= MAXCOLORS; i++)
      if ((NumNotes[i] > 0) &&
          (Traks[i].TrakData != NULL))    /* if notes on this track */
      {
         WhichInst = i;                /* what about extra tracks? */
         MyRef->registr = trk++ - 1;
         if (FALSE)                    /* never use MIDI info  */
         {
            MyRef->type = INS1_MIDI ;
            MyRef->data1 = midcha[i];
            MyRef->data2 = midpre[i];
         }
         else
         {
            MyRef->type = INS1_Name;
            MyRef->data1 = 0;
            MyRef->data2 = 0;
         }
         for (j=0; j < 10; j++)
            MyRef->name[j] = adsrname[(WhichInst-1)*10+j];
         if (strlen(&adsrname[(WhichInst-1)*10]) > 0)
            RefSize = strlen(&adsrname[(WhichInst-1)*10]);
         RefSize = RefSize + sizeof(RefInstrument) - 1;
         CkErr(PutCk(Context, ID_INS1, RefSize, (BYTE *) MyRef));
         if (Debug)
         {
            printf("INS1: %2d Type:%d",MyRef->registr,MyRef->type);
            if (MyRef->type == INS1_MIDI)
               printf(" MCHNL: %2d MPRE: %2d",MyRef->data1,MyRef->data2);
            printf(" '%s'\n",MyRef->name);
         } /* Debug print */
      } /* if */
} /* Hdr2Ins1s */

/*
 * Set up IFF TRACKs --> set t-sig and key-sig
 */
MakTHdrs()
{
   int   i;
   int   MSTimeSig;
   char  *CmdPtr;
   char  TimeSign;

   for(i = 1; i < MAXCOLORS+1; i++)
      if (Traks[i].TrakData != NULL)             /* if notes on this track */
      {
         Traks[i].TrakData[0] = SID_TimeSig;

         CmdPtr = FindCmd(SETTSIG);    /* set IFF time sig from MS */
         if (CmdPtr == NULL)
            TimeSign = DEFAULT_TIME;   /* default time sig in intro */
         else
            TimeSign = CmdPtr[1];      /* curtsig next byte beyond SETT */

         if (TimeSign <= MAX_MS_TSIG)
            MSTimeSig = TimeSign;
         else
            MSTimeSig = MS_TSIG44;     /* 4/4 time is default */

         Traks[i].TrakData[1] = (char) TimeSigTab[MSTimeSig];
         Traks[i].TrakData[2] = SID_KeySig;

         CmdPtr = FindCmd(CHGKEY);     /* Set IFF key sig from MS key sig */
         if (CmdPtr == NULL)
         {
            Traks[i].TrakData[3] = DEFAULT_KEY;
            if (Debug)
               printf("No key sig found ");
         }
         else
            Traks[i].TrakData[3] = CmdPtr[2] - 1;

         Traks[i].CurrEvent = 2;
      } /* if */
} /* IFF Tracks */

/*
 * Count num. of active colors in MS song
 */
int
CountTracks(SongP)
   char *SongP;
{
   char  *ThisNote;
   int   i;
   int   cnt = 0;

   ThisNote = SongP;
   for (i = 0; i <= MAXCOLORS; i++)    /* NumNotes[0] counts frames */
      NumNotes[i] = 0;

   while ((ThisNote = NextCmd(ThisNote,FALSE)) != NULL)   /* while not end */
      if ((!(*ThisNote & 0x80)) && (!(*ThisNote & 0x10)))
            /* if this is a note (NOT a rest) */
         NumNotes[*ThisNote & 0x0F] += 1;  /* or a frame mark   */

   if (Debug)
      printf("NumNotes: ");
   cnt = 0;
   for (i = 1; i <= MAXCOLORS; i++)         /* skip ct of frames */
   {
      if (Debug)
         printf("%4d",NumNotes[i]);
      if (NumNotes[i] > 0)
         cnt += 1;
   } /* for */
   if (Debug)
      printf("--> %d\n",cnt);
   return(cnt);
} /* CountTracks */

/*
 * After filling tracks in RAM, find out how many
 *  tracks are valid (not just rests or empty)
 */
int
PostCount()
{
   int   i;
   int   TrackCount = 0;

   for (i=1; i <= MAXCOLORS; i++)
      if ((Traks[i].TrakSize > 0) && (NumNotes[i] > 0))
         TrackCount++;
   return(TrackCount);
} /* PostCount */


static char FileNamBuf[MS_NAME_MAX];

main(argc, argv)
   int argc;
   char **argv;
{
   struct WBArg *arg;
   int argno = 0;
   char *DestDir = NULL;
   int  MaxArgs;
   char *filename = FileNamBuf;
   int   Remains;             /* free bytes left in source filename */

   FileNamBuf[0] = NULL;

   fromWB = (argc == 0) ? TRUE : FALSE;

   MaxTracks = 4;       /* Default is maximum number of tracks 4 */
   IFFChords = FALSE;   /* Default is not to use IFF-style chording */
   Debug = FALSE;       /* Default is convert quietly; don't print */

   if (argc > 1)
   {
      argc--;  /* skip command name */
      while (argc-- > 0)
      {
         argno++;                         /* move to next argument */
         if (*argv[argno] == '-')
         {
            argv[argno]++;                /* skip over the dash */
            switch(*argv[argno])          /* check char after dash */
            {
               case 'z':
               case 'Z':
                  Debug = TRUE;
                  break;
               case 'c':
               case 'C':
                  IFFChords = TRUE;      /* Use SMUS-std chords! */
                  break;
               case 't':                  /* -t 4 for InstantMusic */
               case 'T':
                  argno++;                /* skip ahead to # */
                  MaxArgs =  sscanf(argv[argno],"%d",&MaxTracks);
                  if (MaxArgs == 0)       /* if no track num */
                  {
                     argno--;                /* back up one arg */
                     MaxTracks = 0;          /* -t alone means no max */
                  }
                  else
                     argc--;                 /* one less arg due to # */
                  break;
               case 'd':
               case 'D':
                  argno++;                /* skip ahead to dir */
                  DestDir = argv[argno];
                  argc--;                 /* one less arg due to dir */
                  break;
               default:
                  CleanExit("Usage: 'ms2smus filename'\n");
            } /* switch */
         } /* if found a dash */
         else /* arg doesn't start with dash, so... */
         {
            if (FileNamBuf[0] == NULL)                /* 1st piece */
               (VOID) strncpy(FileNamBuf,argv[argno],MS_NAME_MAX-1);
            else
            {                                      /* subsequent ones */
               Remains = MS_NAME_MAX-strlen(FileNamBuf)-1;
               (VOID) strcat(FileNamBuf," ");
               (VOID) strncat(FileNamBuf,argv[argno],Remains);
            }
         } /* else */
      } /* while */ 
   } else
      /* if passed filename via Workbench */
   if ((argc == 0) && (WBenchMsg->sm_NumArgs > 1))
   {
      arg = WBenchMsg->sm_ArgList;
      arg++;
      filename = (char *)arg->wa_Name;
      newLock = (struct FileLock *)arg->wa_Lock;
      startLock = (struct FileLock *)CurrentDir(newLock);
   } else
   if (argc == 1)          /* from CLI but no filename */
      CleanExit("Usage: 'ms2smus filename'\n");
   else                    /* from WB but no filename */
      CleanExit("\nClick ONCE on ms2smus\nSHIFT and DoubleClick on song\n");

   if (filename != NULL)
      ReadMS(filename,DestDir);
   else
      CleanExit("Usage: 'ms2smus filename'\n");
} /* main */

CleanExit(errstr)
   char *errstr;
{
   char c;

   printf("\n %s \n",errstr);
   if (newLock != startLock)     /* return to current directory */
      CurrentDir(startLock);
   if (fromWB)             /* wait so user can read message */
   {
      printf("\nPRESS RETURN TO CLOSE THIS WINDOW\n");
      while ((c=getchar()) != '\n');
   }
   exit();
} /* Clean Exit */


/*
 * Read an MS song file and process it
 */
ReadMS(MSFileName,DestDir)
   char *MSFileName;
   char *DestDir;
{
   LONG  LenRead,LenExpected;
   LONG  MSFile,SMUSFile;
   GroupContext *Context;
   char  *SmusNameP;
   char  *CmdPtr;

   MSFile = (LONG) Open(MSFileName,MODE_OLDFILE);
   if (MSFile == NULL)
      CleanExit("Error! Couldn't open source file!\n");

   LenExpected = MAXMSLEN;
   LenRead = Read(MSFile,mshead,LenExpected);
   if (LenRead > 0)
   {
      if (!IsMSFile(mshead))
      {
         Close(MSFile);
         CleanExit("Error! Source not a Music Studio file.\n");
      }
      SmusNameP = MakeName(MSFileName,DestDir,".SMUS");
      SMUSFile = (LONG) Open(SmusNameP,MODE_NEWFILE);
      if (SMUSFile > 0)
      {
         if ( ((Context = OpenWSmus(SMUSFile)) != NULL) )
         {
            NumTracks = CountTracks(&(song[FIRST_FRAME]));
            AllocTraks();                 /* allocate track buffers */
            MakTHdrs();                   /* init track buffers */

            CmdPtr = FindCmd(CHGKEY);
            if (CmdPtr == NULL)
               SetKeyTab(DEFAULT_KEY);    /* default key from intro */
            else
               SetKeyTab((int) CmdPtr[2]);

            ReadSong(&(song[FIRST_FRAME]));   /* process MS song to tracks */

            NumTracks = PostCount();

            Setup2Shdr(Context,song);     /* write IFF Score Header */
            Hdr2Name(Context);            /* write IFF Name chunk */
            Hdr2Ins1s(Context);           /* write IFF INS1 chunks */

            WriteTraks(Context);          /* complete and write tracks */
            CloseWSmus(Context);
         } else
            CleanExit("Error! Couldn't open destination file\n");
      } else
         CleanExit("Error! Couldn't open dest file.\n");
      Close(SMUSFile);
   } /* if from file opened */
   else
      CleanExit("Error! Couldn't read source file.\n");

   Close(MSFile);
   FreeTraks();
   if (newLock != startLock)  /* if changed current directories */
      CurrentDir(startLock);  /* then change it back */
}  /* ReadMS */


