/*	(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;

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

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

/* 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,36,	/* 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 */
	36,48,	/* 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[50] = "df1:sysexfiles";
char	def_name[32];
char	pathname[80];

struct FileRequester FR = {
	"SysEx File Requester",
	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};


/************************************************************************/
main()
{
USHORT	GetReqNum();
SHORT	savefile();
struct FileLock *lock;
struct FileHandle *filehandle;		/* Load voice file	*/
struct FileInfoBlock *fib;
struct Gadget *igad;
UBYTE	*msg,
	*buffer;
LONG	len,
	gadgid;
ULONG	class;
USHORT	i,
	j,
	code,
	menunum,
	itemnum,
	subnum,
	chksum;
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));
	if(msg =GetMidiMsg(dest))
		{
		len = MidiMsgLength(msg);
		error = 0;
		if(len>5)
			{
			DrawImage(window->RPort,&cleartext,2L,28L);
			if(errorcheck) /* check file for errors if possible */
				{
				chksum = 0;
				switch(errorcheck)
					{
					case DX:
						if(*(msg+3)==9)
							{
							error = 1;
							for (i = 6; i < 4102; ++i)
								chksum += *(msg+i);
							chksum = 127 & ((~chksum)+1);
							if (chksum == *(msg+4102))
								error = 0;
							}
						else if(*(msg+3)==1)
							{
							error = 1;
							for (i = 6; i < 161; ++i)
								chksum += *(msg+i);
							chksum = 127 & ((~chksum)+1);
							if (chksum == *(msg+161))
								error = 0;
							}
						break;
					case FB:
						if(*(msg+5)==0) /* check sum voice data to validate */
							{
							for(i=0;i<48;i++)
								{
								buffer = i * 131 + 74;
								chksum = 0;
								for (j=2;j<128;++j)
									chksum += *(msg+buffer+j);
								if(*(msg+buffer+130) != (UBYTE)(127 & ((~chksum)+1)))
									{
									error = 1;
									i = 48;
									}
								}
							}
						else if((*(msg+5)==1)||(*(msg+5)==2)) /* check sum config data to validate */
							{
							for(i=9;i<169;i++)
								chksum += *(msg+i);
							if(*(msg+169) != (UBYTE)(127&((~chksum)+1)))
								error = 1;
							}
						else if(*(msg+5)==3) /* check sum all configs */
							{
							for(i=0;i<16;i++)
								{
								buffer = i*169+7;
								for(j=2;j<169;j++)
									chksum += *(msg+buffer+j);
								if(*(msg+buffer+169)!=(UBYTE)(127&((~chksum)+1)))
									error = 1;
								}
							}
						break;
					case TX81Z:
						if(*(msg+3)==4)
							{
							error = 1;
							for (i = 6; i < 4102; ++i)
								chksum += *(msg+i);
							chksum = 127 & ((~chksum)+1);
							if (chksum == *(msg+4102))
								error = 0;
							}
						break;
					}
				}
			if(error==0)
				{
				/* save voicedata to disk	*/
				error = savefile(msg,len);
				FreeMidiMsg(msg);
				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(msg);
				Mess1.IText = dataerror;
				PrintIText(window->RPort,&Mess1,0L,0L);
				}
			}
		else FreeMidiMsg(msg);
		}
	while(message=(struct IntuiMessage *)GetMsg(window->UserPort))
		{
		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  */
		    					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 */
															{
															*(buffer+6) = GetReqNum(3);
															*(buffer+3) = sysexch;
															}
														else if(*(buffer+5)==0x02) /* Config Bank */
															{
															*(buffer+6) = GetReqNum(2);
															*(buffer+3) = sysexch;
															}
														}
													else *(buffer+2) = (*(buffer+2) & 0xf0)|sysexch;
													}
												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 = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* FB-01 */
									MenuItem3.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* TX81Z */
									MenuItem4.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* Other(None) */
									dxreq[2] = 0x20 | sysexch;
									dxreq[3] = 9;
									PutMidiMsg(source,dxreq);
									break;
									break;
								case 1: /* FB-01 */
									errorcheck = FB;
									MenuItem1.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* DX, TX */
									MenuItem2.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP+CHECKED; /* FB-01 */
									MenuItem3.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* TX81Z */
									MenuItem4.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* Other(None) */
									fbreq[3] = sysexch;
									switch(subnum)
										{
										case 0:	/* Voice Bank X */
											i = GetReqNum(1);
											fbreq[5] = 0;
											fbreq[6] = i;
											PutMidiMsg(source,fbreq);
											break;
										case 1: /* Config Bank X */
											i = GetReqNum(2);
											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 = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* FB-01 */
									MenuItem3.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* TX81Z */
									MenuItem4.Flags = CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP; /* Other(None) */
									dxreq[2] = 0x20 | sysexch;
									dxreq[3] = 4;
									PutMidiMsg(source,dxreq);
									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: /* None */
									errorcheck = OTHER;
									break;
								}
							break;
						} /* end of switch(menunum) */
					} /*  end of if(not null) */
				break;
			} /* end of switch (class) */
		} /* end of while (message)*/
	} /* 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;

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	*/

}	/* end of file */

