/* dxput.c -- program to send Yamaha exclusive messages from file */

/*****************************************************************************
*	    Change Log
*  Date	    | Change
*-----------+-----------------------------------------------------------------
* 13-Jun-86 | Created Change Log
*  6-Aug-86 | Adapted to Lattice C V3.00
*****************************************************************************/

#include "cext.h"
#include "stdio.h"
#include "midicode.h"
#include "mpu.h"
#include "userio.h"
#include "cmdline.h"

#define buffsize 17000
byte midibuff[buffsize];

extern int miditrace;

int xflag;	/* for debugging */

int debptr;

/****************************************************************************
* Data for command line parsing
****************************************************************************/
#define nswitches 8
private char *switches[nswitches] = 
    { "-help", "-miditrace", "-m", "-trace", "-t", "-block", "-d", "-debug" };

#define noptions 1
private char *options[noptions] = { "-tune" };

/*****************************************************************************
*	Routines local to this module
*****************************************************************************/
private void	instruct();
private boolean isdangerous();
private boolean output();
private	boolean	readbuff();

/****************************************************************************
*				instruct
* Effect: prints instructions for this routine
****************************************************************************/

private void instruct()
{
printf("This program will let you send voice programs to a DX7 or TX816.\n");
printf("You must send a file previously created by the dxget program.\n");
printf("To send DX7 programs, you must connect\n");
printf("	MPU 401 MIDI OUT to DX7 MIDI IN.\n");
printf("For TX816 programs, connect MPU 401 MIDI OUT to TX816 MIDI IN.\n");
printf("For a DX7 voice: push the orange FUNCTION key, then the green 8.\n");
printf("	Push 8 repeatedly until the display says SYS INFO AVAIL\n");
printf("	or SYS INFO UNAVAIL.  If the display says SYS INFO\n");
printf("	UNAVAIL, push YES to change to SYS INFO AVAIL.\n");
printf("	Next, push MEMORY PROTECT/INTERNAL, then NO, and then\n");
printf("	then MEMORY SELECT/INTERNAL. Now, type the name of a file\n");
printf("	created by dxget and then the space bar to send data.\n");
printf("	To store the voice program on the DX7, hold down STORE\n");
printf("	while you push a (green) number.  The current voice at\n");
printf("	this number will be erased and replaced by the new voice.\n");
printf("For ALL DX7 voices: Same as for a single DX7 voice, but all 32\n");
printf("	voice programs on the DX7 will be replaced whey you type\n");
printf("	Y in response to `Are you sure?' on the computer.\n");
printf("For all TX816 voices: To store into one module, push NO until the\n");
printf("	MEMORY PROTECT light goes out.	Make sure other MEMORY\n");
printf("	PROTECT lights are ON.	Type Y in response to 'Are you\n");
printf("	sure?' on the computer.\n");
}

/****************************************************************************
*				isdangerous
* Inputs:
*	byte *msg: pointer message to check
* Outputs:
*	returns true if message can destroy lots of info (block data message)
****************************************************************************/

private boolean isdangerous(msg)
byte *msg;
{
    /* look for 32 voice and 64 performance data msgs */
    return (msg[1] == 0x43 && (msg[3] == 2 || msg[3] == 9));
}

/****************************************************************************
*				main
* Effect: main program -- prompt user for information and put data
****************************************************************************/

void main(argc, argv)
    int argc;
    char *argv[];
{
    FILE *fp;		/* the input file */
    int count;		/* length of data in file */
    byte *msg;		/* message pointer */
    int done = false;	/* flag is true when user is done */
    char filename[100];	/* the output file name */
    char *s;		/* the file name in the command line */

    cl_init(switches, nswitches, options, noptions, argv, argc);
    if (cl_switch("-help")) instruct();
    /* get input file name: */
	filename[0] = NULL;	/* empty string */
	if ((s = cl_arg(1)) != NULL) strcpy(filename, s);


    while (!done) {
	if (askbool("Do you want instructions", true)) instruct();

	fp = fileopen(filename, "dx7", "r", "file name");

	if (!readbuff(fp, midibuff, &count)) {
	    printf("Something is wrong with your data or you typed the\n");
	    printf("wrong file name.  Please try again.\n");
	    exit(1);
	}

	printf("\nReady with your data. Type space when you are ready...\n");
	while (true) {
	    if (kbhit() && getch() == ' ') break;
	}
	musicinit();
	exclusive(true);
	msg = midibuff;
	while (output(&msg, midibuff + count))
	     /* write messages */;
	musicterm();
	fclose(fp);
	printf("DONE\n");

	done = !askbool("Do you want to send another file", false);
	filename[0] = NULL;	/* force prompt for new file name */
    }	
}

/****************************************************************************
*				output
* Inputs:
*	byte **msg: pointer to message to send
*	byte *msg_tail: pointer to byte after last message byte
* Outputs:
*	*msg is set to byte after last byte of the message sent
*	returns true if there is more data to send
* Effect: sends exclusive messages using recorded data
* Assumes: msg_tail > *msg
* Implementation:
****************************************************************************/

private boolean output(msg, msg_tail)
    byte **msg;
    byte *msg_tail;
{
    byte *this_msg = *msg;
    int datalen;
    int cksum;
    int n;		/* counts bytes printed on one line */
    int OK;		/* OK to send message */
/*	printf("*msg %x, this_msg %x, msg_tail %x\n",
		*msg, this_msg, msg_tail);
	printf("%d bytes to go.\n", msg_tail - this_msg);
*/
    excldesc(this_msg);
    this_msg = *msg;
    datalen = (this_msg[4] << 7) + this_msg[5];
/*	printf("datalen %x\n", datalen);*/
    cksum = 0;
    for (n = 6; n < datalen+6; n++) {
	cksum = (cksum + this_msg[n]) & 0x7f;
    }
    if (datalen == 0 || (cksum + this_msg[datalen+6]) & 0x7f != 0) {
	printf("Data in your file is garbled.\n");
	return false;
    }
    if (isdangerous(this_msg)) {
	OK = askbool("Are you sure", false);
    } else OK = true;
    if (OK) midi_exclusive(this_msg);

    do {	/* always skip first byte, */
		/* then terminate after finding MIDI_EOX */
	this_msg++;
    } while (this_msg < msg_tail && *(this_msg-1) != MIDI_EOX) ;
    *msg = this_msg;
    return this_msg < msg_tail;
}

/****************************************************************************
*				readbuff
* Inputs:
*	FILE *fp: file from which to read
* Outputs:
*	byte *buff: where to put the data
*	int *count: count is set to the number of of bytes read
* Effect: 
*	read midi exclusive data from a file into buff
* Assumes:
*	fp is open for reading
****************************************************************************/

private boolean readbuff(fp, buff, count)
    FILE *fp;
    byte *buff;
    int *count; /* result */
{
    int i;
    *count = 0;
    while (fscanf(fp, "%x", &i) > 0) {
	*buff = i;
	buff++;
	(*count)++;
    }
    return *count != 0;
}
