/* SYXINS  Inserts SysEx in MIDI file */

#define NULLFUNC 0

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <dos.h>
#include <midifile.h>

char *strcpy(), *strcat();
void exit(), free();

unsigned char syxfname[80], midfname[80];
unsigned int syxf, midf, tmpf;
unsigned long syxlen;
unsigned int syxfmt;
int mf_format;

#define MIDIEX 0
#define BANK    1

int (*mf_getc)() = NULLFUNC;
int (*mf_putc)() = NULLFUNC;
int (*mf_error)() = NULLFUNC;

unsigned long mf_toberead = 0L;

unsigned long bufread(unsigned int handle, unsigned long buflen);

long readvarinum();
long read32bit();
long to32bit();
int  writevarinum(unsigned long num);
void write16bit(unsigned int val);
void write32bit(unsigned long val);

unsigned char far *buff = NULL;
unsigned int bufsegp;
unsigned long allocated;
unsigned char inbyte;


main(int argc, char **argv)
{
       unsigned int max_para;
       unsigned long fpos1, fpos2;
       int bytecount;

       if(argc > 2)
       {
               strcpy(syxfname, argv[2]);
               strcpy(midfname, argv[1]);

               if(strstr(strupr(syxfname), ".C30"))
                       syxfmt = BANK;
               else
               {
                       if(strstr(strupr(syxfname), ".SYX"))
                               syxfmt = MIDIEX;
                       else {
                               printf("Error: .C30 or .SYX extension required for SysEx file\n");
                               exit(1);
                       }
               }

               if(! strstr(strupr(midfname), ".MID"))
                       strcat(midfname, ".MID");

               if((syxf = _open(syxfname, O_BINARY|O_RDONLY)) == -1) {
                       printf("Error opening %s!\n", syxfname);
                       exit(1);
               }

               syxlen = filelength(syxf);
               if(syxfmt == BANK)
                       syxlen -= 4743L;

               if((midf = _open(midfname, O_BINARY|O_RDONLY)) == -1) {
                       printf("Error opening %s!\n", midfname);
                       exit(1);
               }

               tmpf = _creat("SYXINS.TMP",  0);

               initfuncs();

               max_para = allocmem(0xffff, &bufsegp);      /* Allocate all but 1K memory
               max_para -= 16;                                for SysEx buffer  */
               allocated = (unsigned long)max_para * 16L;
               allocmem(max_para, &bufsegp);
               buff = MK_FP(bufsegp, 0);

               readheader();

               if(mf_format == 1)               /* If format = 1 read past 1st track */
                       readtrack();

               readmt();
               mf_toberead = read32bit();
               fpos1 = tell(tmpf);              /* Save position where to insert new
                                                                                                       /* track length value             */
               lseek(tmpf, 4L, SEEK_CUR);       /* Skip over track length for now */
               bytecount = writevarinum(0L);    /* Delta time */
               fileputc(0xf0);
               bytecount += writevarinum(syxlen-1L);
               fpos2 = tell(tmpf);

               mf_toberead += (syxlen + (unsigned long)bytecount);
               lseek(tmpf, fpos1, SEEK_SET);     /* Go back and fill in new tracklen */
               write32bit(mf_toberead);

               lseek(tmpf, fpos2, SEEK_SET);
               if(syxfmt == BANK)
                       lseek(syxf, 4743L, SEEK_SET);    /* Skip header if bank file */
               lseek(syxf, 1L, SEEK_CUR);          /* Skip first byte (0xf0)   */
               bufread(syxf, syxlen - 1L);
               bufwrite(syxlen - 1L);              /* Write SysEx to new file  */

               bufwrite(bufread(midf, 0xffffffff)); /* Append rest of MIDI file */

               _close(syxf);
               _close(midf);
               _close(tmpf);
               remove(midfname);
               rename("SYXINS.TMP", midfname);

               freemem(bufsegp);
               printf("SysEx inserted OK!\n");      /* We're done!  */
       }
       else
       {
               printf("SYXINS v1.0  Inserts SysEx into a standard MIDI file\n");
               printf("usage: syxins <midfname[.mid]>  <syxfname.c30 | syxfname.syx>\n");
       }
}



error(unsigned char *s)
{
       fprintf(stderr,"Error: %s\n",s);
       exit(1);
}


readmt()                /* read through the "MThd" or "MTrk" header string */
{
       bufread(midf, 4L);
       bufwrite(4L);
}


readheader()            /* read a header chunk */
{
       bufread(midf, 4L);
       bufwrite(4L);

       mf_toberead = read32bit();
       write32bit(mf_toberead);

       mf_format = read16bit();                                /* Get format of MIDI file */
       write16bit(mf_format);

       bufread(midf, mf_toberead);
       bufwrite(mf_toberead);
}


readtrack()              /* read a track chunk */
{
       readmt();
       mf_toberead = read32bit();
       write32bit(mf_toberead);
       bufread(midf, mf_toberead);
       bufwrite(mf_toberead);
}


to16bit(int c1, int c2)
{
       return ((c1 & 0xff ) << 8) + (c2 & 0xff);
}

read16bit()
{
       int c1, c2;
       c1 = egetc();
       c2 = egetc();
       return to16bit(c1,c2);
}

long
to32bit(c1,c2,c3,c4)
{
       long value = 0L;

       value = (c1 & 0xff);
       value = (value<<8) + (c2 & 0xff);
       value = (value<<8) + (c3 & 0xff);
       value = (value<<8) + (c4 & 0xff);
       return (value);
}

long
read32bit()
{
       int c1, c2, c3, c4;

       c1 = egetc();
       c2 = egetc();
       c3 = egetc();
       c4 = egetc();
       return to32bit(c1,c2,c3,c4);
}


writevarinum(unsigned long value)
{
       unsigned long buffer;
       int bytecount;

       bytecount = 0;

       buffer = value & 0x7f;
       while((value >>= 7) > 0)
       {
               buffer <<= 8;
               buffer |= 0x80;
               buffer += (value & 0x7f);
       }
       while(1)
       {
               eputc((unsigned)(buffer & 0xff));
               bytecount++;
               if(buffer & 0x80)
                       buffer >>= 8;
               else
                       return bytecount;
       }
}

void
write16bit(unsigned int data)
{
        eputc((unsigned)((data & 0xff00) >> 8));
        eputc((unsigned)(data & 0xff));
}


void
write32bit(unsigned long data)
{
        eputc((unsigned)((data >> 24) & 0xff));
        eputc((unsigned)((data >> 16) & 0xff));
        eputc((unsigned)((data >> 8 ) & 0xff));
        eputc((unsigned)(data & 0xff));
}


filegetc()
{
       if(! _read(midf, &inbyte, 1))
               return EOF;
       else
               return((int)inbyte);
}


fileputc(int outbyte)
{
       unsigned char c;

       c = (unsigned char)outbyte;

       if(_write(tmpf, &c, 1) == -1)
               return EOF;
       else
               return 0;
}


egetc()                 /* read a single character and abort on EOF */
{
       int c = (*mf_getc)();

       if ( c == EOF )
               (*mf_error)("premature EOF");
       mf_toberead--;
       return(c);
}

eputc(c)
unsigned char c;
{
       int return_val;

       if((mf_putc) == NULLFUNC)
       {
               mf_error("mf_putc undefined");
               return(-1);
       }

       return_val = (mf_putc)(c);

       if ( return_val == EOF )
               mf_error("error writing");

       return(return_val);
}


unsigned long bufread(unsigned int handle, unsigned long len)
{
       union REGS regs;
       struct SREGS segregs;
       unsigned int bytes_to_read;
       unsigned long bytes_read;

       segregs.ds = FP_SEG(buff);
       bytes_read = 0L;

       while(1)
       {
               regs.h.ah = 0x3f ;
               regs.x.bx = handle;
               regs.x.dx = 0;

               if(len >= 0x8000L)
                       bytes_to_read = 0x8000;
               else
                       bytes_to_read = (unsigned int)len;

               regs.x.cx = bytes_to_read;
               intdosx(&regs, &regs, &segregs);
               bytes_read += (unsigned long)(regs.x.ax);
               if(regs.x.ax < bytes_to_read)
                       return bytes_read;

               if(bytes_to_read < 0x8000) break;
               len -= 0x8000L;
               if(len == 0L) break;
               segregs.ds += 0x0800;
       }
       return 0L;
}


bufwrite(unsigned long len)
{
       union REGS regs;
       struct SREGS segregs;
       unsigned int bytes_to_write;

       segregs.ds = FP_SEG(buff);

       while(1)
       {
               regs.h.ah = 0x40 ;
               regs.x.bx = tmpf;
               regs.x.dx = 0;
               if(len >= 0x8000L)
                       bytes_to_write = 0x8000;
               else
                       bytes_to_write = (unsigned int)len;
               regs.x.cx = bytes_to_write;
               intdosx(&regs, &regs, &segregs);
               if(bytes_to_write < 0x8000) break;
               len -= 0x8000L;
               if(len == 0L) break;
               segregs.ds += 0x0800;
       }
}


initfuncs()
{
       mf_getc = filegetc;
       mf_putc = fileputc;
       mf_error = error;
}