/*	JVDEST.C  --  Reads the specified input file that presumably
**  contains one or more patches for Roland JV-80 family synthesizers in
**  SYSEX format, extracts any patches found in them, and rewrites them to
**  the specified output file addressed to transfer to the specified
**  destination buffer, which may be any one of the seven Temporary
**  Performance Patch buffers (by specifying 1 - 7 as the first argument)
**  or the Temporary Patch buffer (by specifying an upper or lower case 'P'
**  as the argument.  While the program was intended to be used on single
**  patch files (such as those written by JVEDIT), it can read any file with
**  SYSEX patches in it.  All patches found are transfered to the output file
**  with the addressing adjusted as specified, and any other messages in the
**  file (such as Performance or Drum Kit messages) are not passed on to the
**  output file.
**
**  To compile with Microsoft C/C++ 7.0 issue the following command:
**
**	CL /Za jvdest.c
**
**  For other compilers be sure to compile for __STDC__ if possible (the
**  /Za option to CL enables this).  If you compiler isn't __STDC__ then
**  be sure that reading and writing of binary files requires only the "r"
**  and "w" mode strings to the fopen() call as opposed to "rb" and "wb".
**  Otherwise be sure and make the necessary adjustments to the code where
**  the fopen() call is made.
*/


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>


/*  The following structure is used for each individual SYSEX message
**  found in the file.  It is allocated (with malloc()) and populated by the
**  get_syx_message() function when a Start Of SYSEX byte (0xF0) is found
**  and the get_syx_message() function returns a pointer to the allocated
**  memory.  The buffer pointed to by msg_bytes is also allocated using
**  malloc().  It is the responsability of the programmer to keep track of
**  and free() these areas as required.  The msg_bytes field will be NULL
**  if no message data was found, or if no memory was available.  If the
**  get_syx_message() function returns NULL then no SYSEX message was found
**  in the file, or no memory was available.
*/
struct syx_msg
{
	int size;
	int unit_num;
	char adr[ 4 ];
	char *msg_bytes;
	int errors;
};

/*  Bits for errors field of syx_msg
*/
#define E_NOT_ROLAND	0x01
#define E_BAD_DEVICE	0x02
#define E_WRONG_MODEL	0x04
#define E_BAD_COMMAND	0x08
#define E_BAD_CHECKSUM	0x10
#define E_EOF		0x20
#define E_SHORT_MSG	0x40
#define E_LONG_MSG	0x80


#define MSG_INVALID	0
#define MSG_SYSCOM	1
#define MSG_PERF_COM	2
#define MSG_PERF_PART	3
#define MSG_PATCH_COM	4
#define MSG_PATCH_TONE	5
#define MSG_RHYTHM	6


#ifndef __STDC__

/*  Non-ANSI "prototypes"
*/
FILE *do_open_r();
FILE *do_open_w();
int file_exists();
int warn_exists();
int warn_noopen();

int address_type();
int perf_msg();
int patch_msg();
int rhythm_msg();
int rhythm2_msg();		/*  JV-90/JV-1000 only  */

int display_patch_names();

struct syx_msg *get_syx_message();
int put_syx_message();

int cpymsg();
int get_patches();
int put_patches();

#else

/*  ANSI prototypes
*/
FILE *do_open_r( char *name );
FILE *do_open_w( char *name );
int file_exists( char *name );
int warn_exists( char *name );
int warn_noopen( char *name );

int address_type( struct syx_msg *mp );
int perf_msg( struct syx_msg *mp );
int patch_msg( struct syx_msg *mp );
int rhythm_msg( struct syx_msg *mp );
/*  JV-90/JV-1000 only...  */
int rhythm2_msg( struct syx_msg *mp );

int display_patch_names( void );

struct syx_msg *get_syx_message( FILE *fp );
int put_syx_message( FILE *ofp, struct syx_msg *mp );

int cpymsg( char *dest, struct syx_msg *mp, int nbytes );
int get_patches( FILE *ifp );
int put_patches( FILE *ofp, int destbuf );

#endif


/*  The purpose of these functions is to localize the fopen() call so that
**  changes to the mode string controlled by the __STDC__ ("w" vs "wb" and
**  "r" vs "rb") can be more easily modified if necessary.
*/
#ifndef __STDC__
FILE *
do_open_r( name )
char *name;
#else
FILE *
do_open_r( char *name )
#endif
{
#ifndef __STDC__
	return fopen( name, "r" );
#else
	return fopen( name, "rb" );
#endif
}

#ifndef __STDC__
FILE *
do_open_w( name )
char *name;
#else
FILE *
do_open_w( char *name )
#endif
{
#ifndef __STDC__
	return fopen( name, "w" );
#else
	return fopen( name, "wb" );
#endif
}


#ifndef __STDC__
int
file_exists( name )
char *name;
#else
int
file_exists( char *name )
#endif
{
#ifndef __STDC__
	struct stat sbuf;

	if( stat( name, &sbuf ) == 0 )
#else
	struct _stat sbuf;

	if( _stat( name, &sbuf ) == 0 )
#endif
	{
		return 1;	/*  The file exists.  */
	}
	return 0;	/*  Couldn't stat, assume it doesn't exist.  */
}

#ifndef __STDC__
int
warn_exists( name )
char *name;
#else
int
warn_exists( char *name )
#endif
{
	fprintf( stderr, "\nWarning: %s already exists.\n", name );
	fprintf( stderr, "    Rename the file and run this command again.\n" );
	return 0;
}

#ifndef __STDC__
int
warn_noopen( name )
char *name;
#else
int
warn_noopen( char *name )
#endif
{
	fprintf( stderr, "\nWarning: couldn't open %s for writing!\n", name );
	return 0;
}



#ifndef __STDC__
int
syx_msg_free( mp )
struct syx_msg *mp;
#else
int
syx_msg_free( struct syx_msg *mp )
#endif
{
	if( mp )
	{
		if( mp->msg_bytes )
		{
			free( mp->msg_bytes );
		}
		free( mp );
	}

	return 0;
}


#ifndef __STDC__
int
address_type( mp )
struct syx_msg *mp;
#else
int
address_type( struct syx_msg *mp )
#endif
{
	if( mp->adr[ 3 ] != 0 )
	{
		/*  All acceptable address have adr[ 3 ] == 0.
		*/
		return MSG_INVALID;
	}

	switch( mp->adr[ 0 ] )
	{
	    case 0x00 :
		/*  System common or Temporary buffers.
		*/
		if( mp->adr[ 1 ] == 0x00 )
		{
			if( mp->adr[ 2 ] == 0x00 )
			{
				/*  System Common data  */
				return MSG_SYSCOM;
			}
			if( mp->adr[ 2 ] >= 0x10 && mp->adr[ 2 ] <= 0x1F )
			{
				/*  Temporary Performance data  */
				return perf_msg( mp );
			}
			if( mp->adr[ 2 ] >= 0x20 && mp->adr[ 2 ] <= 0x2B )
			{
				/*  Temporary Performance Patch 1 data  */
				return patch_msg( mp );
			}
			return MSG_INVALID;
		}
		if( mp->adr[ 1 ] >= 0x01 && mp->adr[ 1 ] <= 0x06 )
		{
			/*  Temporary Performance Patch 2-7 data  */
			return patch_msg( mp );
		}
		if( mp->adr[ 1 ] == 0x07 )
		{
			return rhythm_msg( mp );
		}
		if( mp->adr[ 1 ] == 0x08 )
		{
			/*  Temporary Patch data  */
			return patch_msg( mp );
		}
		if( mp->adr[ 1 ] == 0x20 )
		{
			return rhythm2_msg( mp );
		}
		return MSG_INVALID;
	    case 0x01 :		/*  Internal memory message	*/
	    case 0x02 :		/*  Card memory message		*/
		if( mp->adr[ 1 ] >= 0x00  &&  mp->adr[ 1 ] <= 0x0F )
		{
			/*  It is a Performance message  */
			return perf_msg( mp );
		}
		if( mp->adr[ 1 ] == 0x20 )
		{
			return rhythm2_msg( mp );
		}
		if( mp->adr[ 1 ] >= 0x40  &&  mp->adr[ 1 ] <= 0x7E )
		{
			/*  It is a Patch message  */
			return patch_msg( mp );
		}
		if( mp->adr[ 1 ] == 0x7F )
		{
			if( mp->adr[ 2 ] >= 0x20 && mp->adr[ 2 ] <= 0x2B )
			{
				/*  It is a Patch message  */
				return patch_msg( mp );
			}
			/*  Its probably a rhythm setup message  */
			return rhythm_msg( mp );
		}
		return MSG_INVALID;
	    default :
		break;
	}

	return MSG_INVALID;
}

#ifndef __STDC__
int
perf_msg( mp )
struct syx_msg *mp;
#else
int
perf_msg( struct syx_msg *mp )
#endif
{
	if( mp->adr[ 2 ] == 0x10 )
	{
		return MSG_PERF_COM;
	}
	if( mp->adr[ 2 ] >= 0x18 && mp->adr[ 2 ] <= 0x1F )
	{
		return MSG_PERF_PART;
	}

	return MSG_INVALID;
}

#ifndef __STDC__
int
patch_msg( mp )
struct syx_msg *mp;
#else
int
patch_msg( struct syx_msg *mp )
#endif
{
	if( mp->adr[ 2 ] == 0x20 )
	{
		return MSG_PATCH_COM;
	}
	if( mp->adr[ 2 ] >= 0x28 && mp->adr[ 2 ] <= 0x2B )
	{
		return MSG_PATCH_TONE;
	}

	return MSG_INVALID;
}

#ifndef __STDC__
int
rhythm_msg( mp )
struct syx_msg *mp;
#else
int
rhythm_msg( struct syx_msg *mp )
#endif
{
	if( mp->adr[ 2 ] >= 0x40 && mp->adr[ 2 ] <= 0x7C )
	{
		return MSG_RHYTHM;
	}

	return MSG_INVALID;
}

#ifndef __STDC__
int
rhythm2_msg( mp )
struct syx_msg *mp;
#else
int
rhythm2_msg( struct syx_msg *mp )
#endif
{
	if( mp->adr[ 2 ] >= 0x38 && mp->adr[ 2 ] <= 0x46 )
	{
		return MSG_RHYTHM;
	}

	return MSG_INVALID;
}


#define MAX_BUFR 1024

#ifndef __STDC__
struct syx_msg *
get_syx_message( fp )
FILE *fp;
#else
struct syx_msg *
get_syx_message( FILE *fp )
#endif
{
	int nxt_byte, cksum, i;
	struct syx_msg *mp;
	static char bufr[ MAX_BUFR ];

	/*  Look for the start of the SYX message (0xF0 byte).
	**  In theory the first byte should be the SYSEX_START byte
	**  but we will allow a resync in case of trouble, or in case
	**  there are other bytes in the file.  Any non-0xF0 bytes
	**  are discarded at this point.
	*/
	while( (nxt_byte = getc( fp )) != 0xF0 )
	{
		if( nxt_byte == EOF )
		{
			/*  No more SYX messages in the file.
			*/
			return NULL;
		}

		/*  All non-0xF0 bytes are discarded here!  */
	}

	/*  A message is starting, allocate and initialize
	**  a syx_msg structure.
	*/
	mp = (struct syx_msg *) malloc( sizeof( struct syx_msg ) );
	if( mp == NULL )
	{
		/*  No memory for storing message, bail out!
		*/
		return NULL;
	}
	mp->size = -9;	/*  increments as bytes are analyzed  */
	mp->unit_num = 0;
	mp->adr[ 0 ] = 0;
	mp->adr[ 1 ] = 0;
	mp->adr[ 2 ] = 0;
	mp->adr[ 3 ] = 0;
	mp->msg_bytes = NULL;
	mp->errors = 0;

	/*  Begin collecting and analyzing the message.
	**  The first 8 bytes are "header" information.
	*/
	for( i = 0; i < 8; i++ )
	{
		if( (nxt_byte = getc( fp )) == EOF )
		{
			mp->errors |= E_EOF;
			return mp;
		}
		if( nxt_byte == 0xF7 )
		{
			mp->errors |= E_SHORT_MSG;
			return mp;
		}

		switch( mp->size++ )
		{
		    case -9:	/*  The Roland ID byte  */
			if( nxt_byte != 0x41 )
			{
				mp->errors |= E_NOT_ROLAND;
			}
			break;
		    case -8:	/*  The Device ID  */
			if( nxt_byte < 0x10  || nxt_byte > 0x1F )
			{
				mp->errors |= E_BAD_DEVICE;
			}
			/*  Convert to unit number  */
			mp->unit_num = nxt_byte + 1;
			break;
		    case -7:	/*  The Model ID  */
			if( nxt_byte != 0x46 )
			{
				mp->errors |= E_WRONG_MODEL;
			}
			break;
		    case -6:	/*  The Command byte  */
			if( nxt_byte != 0x12 )
			{
				mp->errors |= E_BAD_COMMAND;
			}
			break;
		    case -5:	/*  Address byte 1 (MSB)  */
			mp->adr[ 0 ] = nxt_byte;
			cksum = nxt_byte;
			break;
		    case -4:	/*  Address byte 2        */
			mp->adr[ 1 ] = nxt_byte;
			cksum += nxt_byte;
			break;
		    case -3:	/*  Address byte 3        */
			mp->adr[ 2 ] = nxt_byte;
			cksum += nxt_byte;
			break;
		    case -2:	/*  Address byte 4 (LSB)  */
			mp->adr[ 3 ] = nxt_byte;
			cksum += nxt_byte;
			break;
		    default :
			/*  How did we get here???  */
			printf( "Que passa (unexpected default 1)?\n" );
			break;
		}
	}

	/*  Now collect the actual data bytes into bufr.
	**  Stop at 0xF7 (hopefully), EOF, or mp->size greater than MAX_BUFR.
	**  The last byte before 0xF7 is the checksm byte, so some adjustment
	**  is necessary to mp->size, and the last byte should be compared to
	**  the calculated checksum and not copied into the buffer allocated
	**  for mp->msg_bytes.
	*/
	while( (nxt_byte = getc( fp )) != 0xF7 )
	{
		if( nxt_byte == EOF )
		{
			mp->errors |= E_EOF;
			break;
		}
		if( ++mp->size >= MAX_BUFR )
		{
			mp->errors |= E_LONG_MSG;
			--mp->size;
			break;
		}
		bufr[ mp->size ] = nxt_byte;
	}

	if( mp->size > 0 )
	{
		mp->msg_bytes = (char *) malloc( mp->size );

		for( i = 0; i < mp->size; i++ )
		{
			if( mp->msg_bytes != NULL )
			{
				mp->msg_bytes[ i ] = bufr[ i ];
			}
			cksum += bufr[ i ];
		}
	}

	if( mp->size >= 0 )
	{
		/*  Assume the last byte was the checksum (possibly invalid
		**  if nxt_byte == EOF or mp->size >= MAX_BUFR).  Verify this
		**  value with the calculated checksum.
		*/
		cksum &= 0x7F;
		if( cksum )
		{
			cksum = 0x80 - cksum;
		}
		if( bufr[ mp->size ] != cksum )
		{
			mp->errors |= E_BAD_CHECKSUM;
		}
	}

	return mp;
}


/*	int put_syx_message( FILE *ofp, struct syx_msg *mp )
**
**  This function writes the SYSEX message pointed to by the mp into
**  the file pointed to by the ofp.  The necessary initial bytes 
**  necessary to conform to the format for Roland JV SYSEX messages
**  and the calculated checksum and final End_Of_SYSEX bytes are added
**  to make a complete message.
*/
#ifndef __STDC__
int
put_syx_message( ofp, mp )
FILE *ofp;
struct syx_msg *mp;
#else
int
put_syx_message( FILE *ofp, struct syx_msg *mp )
#endif
{
	int i, cksum, v;

	putc( 0xF0, ofp );		/*  Start of SYSEX	*/
	putc( 0x41, ofp );		/*  Roland ID byte	*/
	putc( mp->unit_num - 1, ofp );	/*  Device ID byte	*/
	putc( 0x46, ofp );		/*  JV model ID		*/
	putc( 0x12, ofp );		/*  Command byte	*/

	v = mp->adr[ 0 ];		/*  Address MSB		*/
	cksum = v;
	putc( v, ofp );
	v = mp->adr[ 1 ];		/*  Address		*/
	cksum += v;
	putc( v, ofp );
	v = mp->adr[ 2 ];		/*  Address		*/
	cksum += v;
	putc( v, ofp );
	v = mp->adr[ 3 ];		/*  Address LSB		*/
	cksum += v;
	putc( v, ofp );

	for( i = 0; i < mp->size; i++ )
	{
		v = mp->msg_bytes[ i ];
		cksum += v;
		putc( v, ofp );		/*  Data	*/
	}

	cksum &= 0x7F;
	if( cksum )
	{
		cksum = 0x80 - cksum;
	}

	putc( cksum, ofp );		/*  Message checksum	*/
	putc( 0xF7, ofp );		/*  End of SYSEX	*/

	return 0;
}


#ifndef __STDC__
int
cpymsg( dest, mp, nbytes )
char *dest;
struct syx_msg *mp;
int nbytes;
#else
int
cpymsg( char *dest, struct syx_msg *mp, int nbytes )
#endif
{
	int i, cnt;
	char *cp;

	if( nbytes >= mp->size )
	{
		if( (nbytes - 2) > mp->size )
		{
			printf( "Unexpectedly small message!" );
			printf( " Got %d, expected %d\n", mp->size, nbytes );
		}
		cnt = mp->size;
	}
	else
	{
		printf( "Message size exceeds expected size!\n" );
		cnt = nbytes;
	}

	cp  = mp->msg_bytes;
	for( i = 0; i < cnt; i++ )
	{
		*dest++ = *cp++;
	}
	while( i++ < nbytes )
	{
		*dest++ = 0;
	}

	return 0;
}


#define SZ_COMMON	 34
#define N_TONES		  4
#define SZ_TONE		117

struct patch
{
	char common[ SZ_COMMON ];
	char tone[ N_TONES ][ SZ_TONE ];
	struct patch *next;
};

struct patch *patchlist = NULL;


/*	add_patch( np )
**
**  Maintain the patch list in alphabetical order according to the
**  12 character patch name field at the start of the common patch data.
*/
int
add_patch( np )
struct patch *np;		/*  New patch	    */
{
	struct patch *cp,	/*  Current patch   */
		     *pp;	/*  Previous patch  */

	if( patchlist == NULL )
	{
		/*  This is the first patch to be put into the list.
		*/
		patchlist = np;
		np->next = NULL;
		return 0;
	}

	pp = NULL;
	for( cp = patchlist; cp != NULL; cp = cp->next )
	{
		if( strncmp( cp->common, np->common, 12 ) > 0 )
		{
			break;
		}
		pp = cp;
	}

	if( pp == NULL )
	{
		/*  This patch goes at the head of the list.
		*/
		np->next = patchlist;
		patchlist = np;
	}
	else
	{
		/*  Insert this patch between pp and cp.
		*/
		np->next = cp;
		pp->next = np;
	}

	return 0;
}


#define S_NEED4		0
#define S_NEED3		1
#define S_NEED2		2
#define S_NEED1		3
#define S_LOOKING	4

int Last_unit_num = 0x10;

#ifndef __STDC__
int
get_patches( FILE *ifp )
#else
int
get_patches( ifp )
FILE *ifp;
#endif
{
	int msg_cnt;
	int state;
	struct syx_msg *mp;
	int npatches = 0;
	struct patch *cp = NULL;


	msg_cnt = 0;
	state = S_LOOKING;
	while( (mp = get_syx_message( ifp )) != NULL )
	{
		++msg_cnt;
		if( mp->errors != 0 )
		{
			printf( "Errors on message %3d -- 0x%02x (skipped)\n",
						msg_cnt, mp->errors );
			syx_msg_free( mp );
			continue;
		}

		/*  Save the unit number for future reference.
		*/
		Last_unit_num = mp->unit_num;

		switch( address_type( mp ) )
		{
		    case MSG_INVALID :
			printf( "Invalid address - " );
			printf( "Message %3d: ", msg_cnt );
			printf( "[%02x %02x %02x %02x]\n", mp->adr[ 0 ],
							   mp->adr[ 1 ],
							   mp->adr[ 2 ],
							   mp->adr[ 3 ]  );
			break;

		    case MSG_PERF_COM :
		    case MSG_PERF_PART :
		    case MSG_RHYTHM :
			/*  Ignore performance and rhythm messages  */
			syx_msg_free( mp );
			break;

		    case MSG_PATCH_COM :
			if( state != S_LOOKING )
			{
				printf( "Unexpected patch common message #" );
				printf( "%d!\n", msg_cnt );
			}
			if( cp == NULL )
			{
				cp = (struct patch *)
					malloc( sizeof( struct patch ) );
				if( cp == NULL )
				{
				    printf( "No more memory for patches!\n" );
				    return npatches;
				}
				cp->next = NULL;
			}
			cpymsg( cp->common, mp, SZ_COMMON );
			state = S_NEED4;
			syx_msg_free( mp );
			break;
		    case MSG_PATCH_TONE :
			if( state < S_NEED4  ||  state > S_NEED1 )
			{
				printf( "Unexpected patch tone message #" );
				printf( "%d!\n", msg_cnt );
			}
			else
			{
				cpymsg( cp->tone[ state ], mp, SZ_TONE );
				++state;
				if( state == S_LOOKING )
				{
					/*  We've got a complete patch
					**  add it to the patch list.
					*/
					add_patch( cp );
					cp = NULL;
					npatches++;
				}

			}
			syx_msg_free( mp );
			break;

		    default :
			printf( "How did I get here???\n" );
			syx_msg_free( mp );
			break;
		}
	}

	return npatches;
}

int
put_pat( FILE *ofp, struct patch *pat, int destbuf, int unit_num )
{
	int i;
	struct syx_msg outmsg;

	outmsg.adr[ 0 ] = 0x00;
	outmsg.adr[ 1 ] = destbuf;
	outmsg.adr[ 3 ] = 0x00;
	outmsg.unit_num = unit_num;

	outmsg.size = SZ_COMMON;
	outmsg.adr[ 2 ] = 0x20;
	outmsg.msg_bytes = pat->common;
	put_syx_message( ofp, &outmsg );

	outmsg.size = SZ_TONE;
	for( i = 0; i < 4; i++ )
	{
		outmsg.adr[ 2 ] = 0x28 + i;
		outmsg.msg_bytes = pat->tone[ i ];
		put_syx_message( ofp, &outmsg );
	}

	return 0;
}


#ifndef __STDC__
int
put_patches( FILE *ofp, int destbuf )
#else
int
put_patches( ofp, destbuf )
FILE *ofp;
int destbuf;
#endif
{
	struct patch *p;
	int i;

	for( p = patchlist; p != NULL; p = p->next )
	{
		put_pat( ofp, p, destbuf, Last_unit_num );
	}
}


#ifndef __STDC__
int
display_patch_names()
#else
int
display_patch_names( void )
#endif
{
	struct patch *p;
	int i;

	for( p = patchlist; p != NULL; p = p->next )
	{
		for( i = 0; i < 12; i++ )
		{
			printf( "%c", p->common[ i ] );
		}
		printf( "\n" );
	}

	return 0;
}



#ifndef __STDC__
int
main( argc, argv )
int argc;
char *argv[];
#else
int
main( int argc, char *argv[] )
#endif
{
	int destbuf, count;
	FILE *ifp, *ofp;

	if( argc != 4 )
	{
		usage();
		return 1;
	}

	/*  safety check  */
	if( strcmp( argv[ 2 ], argv[ 3 ] ) == 0 )
	{
		/*  The same name was specified for input and output.  */
		fprintf( stderr, "%s: Please specify different ", argv[ 0 ] );
		fprintf( stderr, "names for input and output files!\n" );
		return 4;
	}

	/*  Allow the user to specify values from 1 to 7 for the Temporary
	**  Performance Patch buffers 1 to 7, or 'P' (or 'p') for the
	**  Temporary Patch buffer.  After the error checks adjust the
	**  value of destbuf to the value appropriate to use in the
	**  messages' address field.
	*/
	destbuf = -1;
	if( sscanf( argv[ 1 ], "%d", &destbuf ) != 1 )
	{
		if( argv[1][0] == 'P'  ||  argv[1][0] == 'p' )
		{
			/*  Set value one high to be consistent with the
			**  numeric values input by the user.  The value
			**  will be decremented after the error checking.
			*/
			destbuf = 9;
		}
	}

	if( destbuf < 1  ||  destbuf == 8  ||  destbuf > 9 )
	{
		fprintf( stderr, "\njvdest: " );
		fprintf( stderr, "Invalid destination buffer specified!\n" );
		usage();
		return 3;
	}
	destbuf -= 1;


	if( (ifp = do_open_r( argv[ 2 ] )) == NULL )
	{
		fprintf( stderr, "%s: Can't open %s for reading!\n",
							argv[ 0 ], argv[ 2 ] );
		return 4;
	}

	if( (ofp = do_open_w( argv[ 3 ] )) == NULL )
	{
		fprintf( stderr, "%s: Can't open %s for writing!\n",
							argv[ 0 ], argv[ 3 ] );
		return 5;
	}

	count = get_patches( ifp );

	fclose( ifp );

	put_patches( ofp, destbuf );

	fclose( ofp );

	printf( "\nThe following patch%s ", count == 1 ? " was" : "es were" );
	printf( "found in %s:\n", argv[ 2 ] );
	display_patch_names();
	printf( "\n%s", count == 1 ? "It has " : "They have " );
	printf( "been transfered to %s\n", argv[ 3 ] ); 
	printf( "addressed for " );
	if( destbuf < 8 )
	{
		printf( "Temporary Performance Patch buffer " );
		printf( "%d.\n\n", destbuf + 1 );
	}
	else
	{
		printf( "the Temporary Patch buffer.\n\n" );
	}

	return 0;
}

char Usage_message[] = { "\
\n\
USAGE:  jvdest destination currentfile newfile\n\
\n\
The jvdest program locates patches in the \"current\" file and rewrites\n\
them into the \"new\" file with their destination address modified as\n\
specified by the \"destination\" variable.\n\
\n\
Valid \"destination\" values are\n\
\n\
    1 - 7  for temporary performance patch buffers 1 - 7\n\
    P      for the temporary patch buffer\n\
\n\
While the program is intended for use on files with single patches in them\n\
(such as those written by JVEDIT) it will read files with multiple patches\n\
in them (such as a bank of 64 patches) and write a new file with all the\n\
patches addressed for the specified destination.  If the file has any other\n\
messages in it (as a bulk dump of the performance buffers would) the other\n\
messages are discarded, and only the patches appear in the new file.\n\
\n\
" };

/*	int usage( void )
**
**  Issues the Usage message.
*/
#ifndef __STDC__
int
usage()
#else
int
usage( void )
#endif
{
	return fprintf( stderr, Usage_message );
}
