/*	(C) Copyright 1988 Jack Deckard   Helicon Software	*/
/*								*/
/*	SYS-EX Filer Program  Load and Save SYS-EX dumps	*/

#include <exec/types.h>
#include <exec/exec.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <midi/midi.h>
#include <functions.h>
#include <fcntl.h>
#include <libraries/arpbase.h>	/* Standard ARP header file */
#include <arpfunc.h>	/* Predeclared functions */


extern struct NewWindow NewWindowStructure1;
extern struct Menu Menu1;
extern struct Gadget Gadget1;
extern struct IntuiText IText1;
extern struct MenuItem	MenuItem1;
extern struct MenuItem	MenuItem2;
extern struct MenuItem	MenuItem3;
extern struct MenuItem	MenuItem4;
extern struct MenuItem	MenuItem5;
extern struct Border Border3;

#define VERSION 33L	/* 1.2 Library Version	*/

#define OTHER 0
#define DX	1
#define FB	2
#define TX81Z   3
#define MATRX6  10

/* Define all global variables, pointers, structures, etc	*/

struct Window *window;
struct IntuiMessage *message;

void *MidiBase;

UBYTE datasaved[] =	"       Data Saved         ";
UBYTE ioerror[] =	"I/O ERROR  File not saved ";
UBYTE ioerror2[] =	"I/O ERROR  Can't Read File";
UBYTE ioerror3[] =	"I/O ERROR  Can't Open File";
UBYTE ioerror4[] =	"I/O ERROR  File Not Found ";
UBYTE dataerror[] =	"Data Error in SYS-EX File ";
UBYTE loadaborted[] =	"     Load Aborted         ";
UBYTE saveaborted[] =	"     Save Aborted         ";
UBYTE yamtext[] =	"   File ID is Yamaha      ";
UBYTE seqtext[] =	"File ID is Seq Circuits   ";
UBYTE mootext[] =	"    File ID is Moog       ";
UBYTE obetext[] =	"  File ID is Oberheim     ";
UBYTE emutext[] =	"    File ID is E-mu       ";
UBYTE roltext[] =	"   File ID is Roland      ";
UBYTE kortext[] =	"    File ID is Korg       ";
UBYTE castext[] =	"   File ID is Casio       ";
UBYTE unktext[] =	"  File ID is Unknown      ";


struct IntuiText Mess1 = {
	1,0,JAM2,	/* front and back text pens, drawmode and fill byte */
	36,35,	/* XY origin relative to container TopLeft */
	NULL,	/* font pointer or NULL for default */
	datasaved,	/* pointer to text */
	NULL	/* next IntuiText structure */
};

struct IntuiText Mess2 = {
	1,0,JAM2,	/* front and back text pens, drawmode and fill byte */
	60,47,	/* XY origin relative to container TopLeft */
	NULL,	/* font pointer or NULL for default */
	(UBYTE *)"Transmit Complete",	/* pointer to text */
	NULL	/* next IntuiText structure */
};

struct Image cleartext = {
0,0,
260,28,0,
NULL,
0,0,
NULL
};

struct MDest *dest=0;
struct MRoute *routei=0;
struct MRouteInfo routeiinfo = { MMF_SYSEX };     /* receive only sys/ex msg's */

struct MSource *source=0;
struct MRoute *routeo=0;
struct MRouteInfo routeoinfo = { -1, -1 };	/* support all msg's */

char	def_dir[96] = "MIDI_Tools:sysexfiles";
char	def_name[32];
char	pathname[128];

BYTE LFR[] = "Load SysEx File";
BYTE SFR[] = "Save SysEx File";

struct FileRequester FR = {
	LFR,
	def_name,
	def_dir,
	NULL,
	NULL,
	NULL,
	NULL,
};

/************/

UBYTE fbreq[] = {0xf0,0x43,0x75,0x00,0x20,0x00,0x00,0xf7};
UBYTE dxreq[] = {0xf0,0x43,0x20,0x04,0xf7};
UBYTE m6req[] = {0xf0,0x10,0x06,0x04,0x01,0x00,0xf7};


UBYTE	ttxv1[] = "Which Voice Bank (1 - 2)?";
UBYTE	ttxv2[] = "Which Voice Bank (1 - 7)?";
UBYTE	ttxv3[] = "    Which Voice (0 - 99)?";
UBYTE	ttxc1[] = "Which Config Bank (1-16)?";
UBYTE	ttxs1[] = "  Splits Number (0 - 49)?";

struct IntuiText Title = {
	1,0,JAM2,	/* front and back text pens, drawmode and fill byte */
	4,5,	/* XY origin relative to container TopLeft */
	NULL,	/* font pointer or NULL for default */
	ttxv1,	/* pointer to text */
	NULL	/* next IntuiText structure */
};

UBYTE undo[4];
UBYTE SNum[4];

struct StringInfo RSfo = {
	SNum,
	undo,
	0,3,0,0,0,0,0,
	NULL,
	0,
	NULL
};

struct Gadget RGadget = {
	NULL,	/* next gadget */
	218,4,	/* origin XY of hit box relative to window TopLeft */
	24,11,	/* hit box width and height */
	NULL,	/* gadget flags */
	RELVERIFY+LONGINT+ENDGADGET,	/* activation flags */
	STRGADGET+REQGADGET,	/* gadget type flags */
	(APTR)&Border3,	/* gadget border or image to be rendered */
	NULL,	/* alternate imagery for selection */
	NULL,	/* first IntuiText structure */
	NULL,	/* gadget mutual-exclude long word */
	(APTR)&RSfo,	/* SpecialInfo structure */
	1,	/* user-definable data */
	NULL	/* pointer to user-definable data */
};

SHORT BRV1[] = {
	0,0,
	258,0,
	258,60,
	0,60,
	0,0
};
struct Border BRs1 = {
	-2,-1,	/* XY origin relative to container TopLeft */
	1,0,JAM1,	/* front pen, back pen and drawmode */
	5,	/* number of XY vectors */
	BRV1,	/* pointer to XY vectors */
	NULL	/* next border in list */
};

struct Requester Rqstr1 = {
	NULL,
	4,11,256,58,	/* Left, Top, Width, Height */
	0,0,
	&RGadget,	/* first gadget in gadget list */
	&BRs1,		/* Requester Border */
	&Title,		/* window title */
	NULL,		/* Flags */
	0,
	NULL,
	{NULL},
	{NULL},
	NULL,
	{NULL}
};

/************************************************************************/
main()
{
SHORT	GetReqNum();
SHORT	savefile();
struct FileLock *lock;
struct FileHandle *filehandle;		/* Load voice file	*/
struct FileInfoBlock *fib;
struct Gadget *igad;
UBYTE	*midimsg,
		*buffer;
LONG	len,
		gadgid;
ULONG	class;
USHORT	i,
		j,
		code,
		menunum,
		itemnum,
		subnum,
		checksum;
SHORT	keepgoing = 1,
		errorcheck = OTHER;
char	lastchar;
register LONG error;
register UBYTE	id,
		sysexch;

error = 1;
sysexch = 15;

/*  Open libraries. If error, exit immediatly */

if(!(MidiBase = OpenLibrary (MIDINAME,MIDIVERSION)))
	goto clean;

if(!(dest = CreateMDest (NULL,NULL)))
	goto clean;

/* create our source node (private) */
if(!(source = CreateMSource (NULL,NULL)))
	goto clean;

/* create our routes to MidiIn and MidiOut */
if(!(routei = MRouteDest ("MidiIn", dest, &routeiinfo)))
	goto clean;
if(!(routeo = MRouteSource (source,"MidiOut",&routeoinfo)))
	goto clean;


/* Initialize window struct, then call OpenWindow. Exit if error */
if((window = (struct Window *)OpenWindow(&NewWindowStructure1)) == NULL)
	goto clean;

/*  Initialize Menu items, then submit them to Intuition */
SetMenuStrip(window,&Menu1);

/*  Wait for message at IDCMP port  */
while(keepgoing)
	{
	/* wait for window message or midi message */
	Wait((1L<<dest->DestPort->mp_SigBit)|(1L<<window->UserPort->mp_SigBit));
	while((midimsg = GetMidiMsg(dest))!=NULL)
		{
		len = MidiMsgLength(midimsg);
		error = 0;
		if(len>5)
			{
			DrawImage(window->RPort,&cleartext,2L,28L);
			if(errorcheck) /* check file for errors if possible */
				{
				checksum = 0;
				switch(errorcheck)
					{
					case DX:
						if(*(midimsg+3)==9)
							{
							error = 1;
							for (i = 6; i < 4102; ++i)
								checksum += *(midimsg+i);
							checksum = 127 & ((~checksum)+1);
							if (checksum == *(midimsg+4102))
								error = 0;
							}
						else if(*(midimsg+3)==1)
							{
							error = 1;
							for (i = 6; i < 161; ++i)
								checksum += *(midimsg+i);
							checksum = 127 & ((~checksum)+1);
							if (checksum == *(midimsg+161))
								error = 0;
							}
						break;
					case FB:
						if(*(midimsg+5)==0) /* check sum voice data to validate */
							{
							for(i=0;i<48;i++)
								{
								buffer = i * 131 + 74;
								checksum = 0;
								for (j=2;j<128;++j)
									checksum += *(midimsg+buffer+j);
								if(*(midimsg+buffer+130) != (UBYTE)(127 & ((~checksum)+1)))
									{
									error = 1;
									i = 48;
									}
								}
							}
						else if((*(midimsg+5)==1)||(*(midimsg+5)==2)) /* check sum config data to validate */
							{
							for(i=9;i<169;i++)
								checksum += *(midimsg+i);
							if(*(midimsg+169) != (UBYTE)(127&((~checksum)+1)))
								error = 1;
							}
						else if(*(midimsg+5)==3) /* check sum all configs */
							{
							for(i=0;i<16;i++)
								{
								buffer = i*169+7;
								for(j=2;j<169;j++)
									checksum += *(midimsg+buffer+j);
								if(*(midimsg+buffer+169)!=(UBYTE)(127&((~checksum)+1)))
									error = 1;
								}
							}
						break;
					case TX81Z:
						if(*(midimsg+3)==4)
							{
							error = 1;
							for (i = 6; i < 4102; ++i)
								checksum += *(midimsg+i);
							checksum = 127 & ((~checksum)+1);
							if (checksum == *(midimsg+4102))
								error = 0;
							}
						break;
					case MATRX6:
						error = 1;
						if(*(midimsg+3) == 1)
							{
							if(*(midimsg+274)==0xf7)
								error = 0;
							}
						else if(*(midimsg+3)==2)
							{
							for(i=5;i<39;i++);
								checksum +=*(midimsg+i);
							checksum = 127 & checksum;
							if(checksum == *(midimsg+39))
								error = 0;
							}
						else if(*(midimsg+3)==3)
							{
							for(i=5;i<475;i++);
								checksum +=*(midimsg+i);
							checksum = 127 & checksum;
							if(checksum == *(midimsg+475))
								error = 0;
							}
						break;
					}
				}
			if(error==0)
				{
				/* save voicedata to disk	*/
				error = savefile(midimsg,len);
				FreeMidiMsg(midimsg);
				if(error == 1)
					{
					Mess1.IText = datasaved;
					PrintIText(window->RPort,&Mess1,0L,0L);
					}
				else if(error == 0)
					{
					Mess1.IText = ioerror;
					PrintIText(window->RPort,&Mess1,0L,0L);
					}
				else if(error == -1)
					{
					Mess1.IText = saveaborted;
					PrintIText(window->RPort,&Mess1,0L,0L);
					}
				}
			else
				{
				FreeMidiMsg(midimsg);
				Mess1.IText = dataerror;
				PrintIText(window->RPort,&Mess1,0L,0L);
				}
			}
		else FreeMidiMsg(midimsg);
		}
	while((message=(struct IntuiMessage *)GetMsg(window->UserPort))!=NULL)
		{
		class = message->Class;
		code = message->Code;
		igad = (struct Gadget *) message->IAddress; /* Ptr to a gadget */
		ReplyMsg(message);
		DrawImage(window->RPort,&cleartext,2L,28L);
		switch(class)
			{
			case CLOSEWINDOW:  /* User is ready to quit, so
				indicate that execution should terminate with
				next iteration of the loop. */
				keepgoing = 0;
				break;
			case GADGETUP:
				gadgid = igad->GadgetID; /* Get GadgetID */
				switch(gadgid)
					{
					case 0:
						sysexch = (++sysexch) & 15;
						sprintf(IText1.IText,"%2d",(SHORT)sysexch + 1);
						RefreshGList(&Gadget1,window,NULL,1L);
						break;
					}
				break;
			case MENUPICK:
				if(code!=MENUNULL)
					{
					menunum = MENUNUM(code);
					itemnum = ITEMNUM(code);
					subnum = SUBNUM(code);
					switch(menunum)
						{
						case 0: /* Amiga send SYSEX */
							/*  Load the file on disk into array voicefile  */
								FR.fr_Hail = LFR;
		    					if(FileRequest(&FR))
								{
								/* copy path and file name to pathname */
								strcpy(pathname,def_dir);
								if(strlen(pathname))
									{
									lastchar = pathname[strlen(pathname)-1];
									if((lastchar!='/')||(lastchar!=':'))
										strcpy(pathname+strlen(pathname),"/");
									}
								strcpy(pathname+strlen(pathname),def_name);
								if(lock = (struct FileLock *)Lock(pathname,ACCESS_READ))
									{
									fib=(struct FileInfoBlock *)AllocMem((LONG)sizeof(struct FileInfoBlock),MEMF_CLEAR);
									if(Examine(lock,fib))
										{
										len = (LONG)fib->fib_Size;
										FreeMem(fib,(LONG)sizeof(struct FileInfoBlock));
										buffer = (UBYTE *)AllocMem((LONG)len,0L);
										filehandle = (struct FileHandle *)Open(pathname,MODE_OLDFILE);
										if(filehandle)
											{
											if(Read(filehandle,buffer,(LONG)len) != -1)
												{
												Close(filehandle);
												UnLock(lock);
												if(*(buffer+1)==0x43) /* if yamaha file */
													{
													if(*(buffer+2)==0x75) /* if FB-01 file, get bank number to load to */
														{
														if(*(buffer+5)==0x00) /* Voice bank */
															{
															Title.IText = ttxv1;
															*(buffer+6) = GetReqNum();
															*(buffer+3) = sysexch;
															}
														else if((*(buffer+5)==0x01)||(*(buffer+5)==0x02)) /* Config Bank */
															{
															*(buffer+5) = 0x02;
															Title.IText = ttxc1;
															*(buffer+6) = GetReqNum();
															*(buffer+3) = sysexch;
															}
														}
													else *(buffer+2) = (*(buffer+2) & 0xf0)|sysexch;
													}
												else if(*(buffer+1)==0x10) /* Oberheim ID? */
													{
													if(*(buffer+2)==0x06) /* Matrix 6? */
														{
														if(*(buffer+3)==0x01) /* Single Voice? */
															{
															*(buffer+4)=(UBYTE)GetReqNum();
															}
														}
													}
												id = *(buffer+1);
												/* Print file ID */
												switch(id)
													{
													case 0x01:
														Mess1.IText = seqtext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x04:
														Mess1.IText = mootext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x10:
														Mess1.IText = obetext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x18:
														Mess1.IText = emutext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x41:
														Mess1.IText = roltext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x42:
														Mess1.IText = kortext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x43:
														Mess1.IText = yamtext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													case 0x44:
														Mess1.IText = castext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													Default:
														Mess1.IText = unktext;
														PrintIText(window->RPort,&Mess1,0L,0L);
														break;
													}
												/* transmit file */
												PutMidiMsg(source,buffer);
												FreeMem(buffer,len);
												PrintIText(window->RPort,&Mess1,0L,0L);
												PrintIText(window->RPort,&Mess2,0L,0L);
												}
											else /* Can't READ file */ 
												{
												FreeMem(buffer,len);
												Close(filehandle); /* error	*/
												UnLock(lock);
												Mess1.IText = ioerror2;
												PrintIText(window->RPort,&Mess1,0L,0L);
												}
											}
										else /* Can't Open file */ 
											{
											FreeMem(buffer,len);
											UnLock(lock);
											Mess1.IText = ioerror3;
											PrintIText(window->RPort,&Mess1,0L,0L);
											}
										}
									else /* Can't Examine file */
										{
										FreeMem(fib,(LONG)sizeof(struct FileInfoBlock));
										UnLock(lock);
										}
									}
								else /* Can't Lock file */
									{
									Mess1.IText = ioerror4;
									PrintIText(window->RPort,&Mess1,0L,0L);
									}
								}
							else
								{
								Mess1.IText = loadaborted;
								PrintIText(window->RPort,&Mess1,0L,0L);
								}
							break;
						case 1:	/* Dump Request */
							switch(itemnum)
								{
								case 0: /* TX */
									errorcheck = DX;
									MenuItem1.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP+CHECKED; /* DX, TX */
									MenuItem2.Flags = MenuItem3.Flags = MenuItem4.Flags = MenuItem5.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* Other */
									dxreq[2] = 0x20 | sysexch;
									dxreq[3] = 9;
									PutMidiMsg(source,dxreq);
									break;
									break;
								case 1: /* FB-01 */
									errorcheck = FB;
									MenuItem2.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP+CHECKED; /* FB-01 */
									MenuItem1.Flags = MenuItem3.Flags = MenuItem4.Flags = MenuItem5.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* Other */
									fbreq[3] = sysexch;
									switch(subnum)
										{
										case 0:	/* Voice Bank X */
											Title.IText = ttxv2;
											i = GetReqNum();
											fbreq[5] = 0;
											fbreq[6] = i;
											PutMidiMsg(source,fbreq);
											break;
										case 1: /* Config Bank X */
											Title.IText = ttxc1;
											i = GetReqNum();
											fbreq[5] = 2;
											fbreq[6] = i;
											PutMidiMsg(source,fbreq);
											break;
										}
									break;
								case 2:	/* DX27 DX100 */
									errorcheck = DX;
									MenuItem1.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP+CHECKED; /* DX, TX */
									MenuItem2.Flags = MenuItem3.Flags = MenuItem4.Flags = MenuItem5.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* Other */
									dxreq[2] = 0x20 | sysexch;
									dxreq[3] = 4;
									PutMidiMsg(source,dxreq);
									break;
								case 3: /* Matrix 6 */
									errorcheck = MATRX6;
									MenuItem1.Flags = MenuItem2.Flags = MenuItem3.Flags = MenuItem5.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* TX81Z */
									MenuItem4.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP+CHECKED; /* Matrix 6 */
									switch(subnum)
										{
										case 0:	/* Voice file */
											Title.IText = ttxv3;
											m6req[4] = 0x01;
											m6req[5] = (UBYTE)GetReqNum();
											PutMidiMsg(source,m6req);
											break;
										case 1: /* Splits */
											Title.IText = ttxs1;
											m6req[4] = 2;
											m6req[5] = (UBYTE)GetReqNum();
											PutMidiMsg(source,m6req);
											break;
										case 2:
											m6req[4] = 3;
											PutMidiMsg(source,m6req);
											break;
										}
									break;
								}
							break;
						case 2: /* Error Checking and identification */
							switch(itemnum)
								{
								case 0: /* DX type */
									errorcheck = DX;
									break;
								case 1: /* FB type */
									errorcheck = FB;
									break;
								case 2: /* TX81Z */
									errorcheck = TX81Z;
									break;
								case 3: /* Oberheim Matrix 6 */
									errorcheck = MATRX6;
									break;
								case 4: /* None */
									errorcheck = OTHER;
									break;
								}
							break;
						} /* end of switch(menunum) */
					} /*  end of if(menunum not null) */
				break;
			} /* end of switch (class) */
		} /* end of while(intuimessage)*/
	} /* end while (keepgoing) */


/*   Time to quit, so clean up and exit.				*/
clean:

if(window)
	{
	ClearMenuStrip(window);
	CloseWindow(window);
	}
if(routei)
	DeleteMRoute(routei);
if(routeo)
	DeleteMRoute(routeo);
if(dest)
	DeleteMDest(dest);
if(source)
	DeleteMSource(source);
if(MidiBase)
	CloseLibrary(MidiBase);

exit();
}			/* end of main	*/


/************************************************************************/
/*  Create a file on disk using buffered I/O. Open the disk file,	*/
/*  write the array to it, and close the file.				*/

SHORT savefile(buffer,len)
UBYTE *buffer;   /*  pointer to voice file	*/
long len;
{
struct FileHandle *filehandle;		/* Save voice file	*/
char	lastchar;

FR.fr_Hail = SFR;

if(FileRequest(&FR))
	{
	strcpy(pathname,def_dir);
	if(strlen(pathname))
		{
		lastchar = pathname[strlen(pathname)-1];
		if((lastchar!='/')||(lastchar!=':'))
			strcpy(pathname+strlen(pathname),"/");
		}
	strcpy(pathname+strlen(pathname),def_name);
	if(!(filehandle = Open(&pathname,MODE_NEWFILE)))
		return(0);
	if(Write(filehandle,buffer,len) == -1L)
		{
		Close(filehandle);	/* error	*/
		return(0);
		}
	Close(filehandle);
	return(1);
	}
else
	return(-1);	/* aborted save	*/

}


SHORT	GetReqNum()
{
SHORT returncode;
LONG class;


RGadget.LongInt = 0;
RSfo.BufferPos = 0;
RSfo.DispPos = 0;

for(returncode=0;returncode<4;returncode++)
	SNum[returncode] = 0;

Request(&Rqstr1,window);

for(class = 0;class != REQSET;)
	{
	Wait(1L<<window->UserPort->mp_SigBit);
	message=(struct IntuiMessage *)GetMsg(window->UserPort);
	class = message->Class;
	ReplyMsg(message);
	}

ActivateGadget(&RGadget,window,&Rqstr1);

for(;class!=GADGETUP;)
	{
	Wait(1L<<window->UserPort->mp_SigBit);
	message=(struct IntuiMessage *)GetMsg(window->UserPort);
	class = message->Class;
	ReplyMsg(message);
	}
returncode = RSfo.LongInt;

return(returncode);
}
