/* gusvoice.c, adapted (slightly) from gusload.c by H. Savolainen, --gl */

#include <stdio.h>
/*#include <linux/ultrasound.h>*/
/*#include <sys/ioctl.h>*/
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

typedef union {
      unsigned char c[4];
      unsigned long l;
} long_end;
typedef union {
      unsigned char c[2];
      unsigned short s;
} short_end;

typedef struct linfo {
	unsigned char velmin;
	unsigned char velmax;
	unsigned char left_samples;
	unsigned char right_samples;
} linfo;

#pragma pack (1)

struct pat_header
  {
    char            magic[12];
    char            version[10];
    char            description[60];
    unsigned char   instruments;
    unsigned char            voices;
    unsigned char            channels;
    unsigned short  nr_waveforms;
    unsigned short  master_volume;
    unsigned long   data_size;
    char	    sf2magic[7];
    char	    unused1[29];
    short	    instrument_number;
    char	    instrument_name[16];
    int		    instrument_size;
    unsigned char   layer_count;
    unsigned char   velocity_count;
    struct linfo    sf2layer[9];
    char	    unused2[40-37];
    char	    layer_duplicate;
    char	    layer_number;
    int		    layer_size;
    unsigned char   num_samples;
    struct linfo    sf2layer2[10];
  };

#pragma pack (1)

struct sample_header
  {
    char            name[7];
    unsigned char   fractions;
    long            len;
    long            loop_start;
    long            loop_end;
    unsigned short  base_freq;
    long            low_note;
    long            high_note;
    long            base_note;
    short           detune;
    unsigned char   panning;

    unsigned char   envelope_rate[6];
    unsigned char   envelope_offset[6];

    unsigned char   tremolo_sweep;
    unsigned char   tremolo_rate;
    unsigned char   tremolo_depth;

    unsigned char   vibrato_sweep;
    unsigned char   vibrato_rate;
    unsigned char   vibrato_depth;

    char            modes;

    short           scale_frequency;
    unsigned short  scale_factor;
    unsigned short  volume; /* Is this really here?? */

    unsigned char   volume_envelope_delay;
    unsigned char   exclusive_class;
    unsigned char   vibrato_delay;

    unsigned char   mod_envelope_rate[6];
    unsigned char   mod_envelope_offset[6];
    unsigned char   mod_envelope_delay;
    unsigned char   chorus_effect;
    unsigned char   reverb_effect;
    short	    resonance;
    short	    cutoff_frequency;
    unsigned char   modEnvToPitch;
    unsigned char   modEnvToFilterFc;
    unsigned char   modLfoToFilterFc;
    unsigned char   keynumToModEnvHold;
    unsigned char   keynumToModEnvDecay;
    unsigned char   keynumToVolEnvHold;
    unsigned char   keynumToVolEnvDecay;
    unsigned char   true_panning;

    short	    lfo_rate;
    unsigned char   lfo_depth;
    char	    aps_parameter;
  };

static int big_endian = 1;

static long longnum(unsigned char c1, unsigned char c2,
		unsigned char c3, unsigned char c4)
{
    long_end lswitch;
    if (big_endian) {
	lswitch.c[0] = c4;
	lswitch.c[1] = c3;
	lswitch.c[2] = c2;
	lswitch.c[3] = c1;
    } else {
	lswitch.c[0] = c1;
	lswitch.c[1] = c2;
	lswitch.c[2] = c3;
	lswitch.c[3] = c4;
    }
    return(lswitch.l);
}

static void numlong(long number, unsigned char* c)
{
    long_end lswitch;
    lswitch.l = number;
    if (big_endian) {
	c[3] = lswitch.c[0];
	c[2] = lswitch.c[1];
	c[1] = lswitch.c[2];
	c[0] = lswitch.c[3];
    } else {
	c[0] = lswitch.c[0];
	c[1] = lswitch.c[1];
	c[2] = lswitch.c[2];
	c[3] = lswitch.c[3];
    }
}

static short shortnum(unsigned char c1, unsigned char c2)
{
    short_end sswitch;
    if (big_endian) {
	sswitch.c[0] = c2;
	sswitch.c[1] = c1;
    } else {
	sswitch.c[0] = c1;
	sswitch.c[1] = c2;
    }
    return(sswitch.s);
}

static void numshort(short number, unsigned char *c)
{
    short_end sswitch;
    sswitch.s = number;
    if (big_endian) {
	c[1] = sswitch.c[0];
	c[0] = sswitch.c[1];
    } else {
	c[0] = sswitch.c[0];
	c[1] = sswitch.c[1];
    }
}

static void byteorder()
{   long_end hexx;

    hexx.c[0] = 0x12;
    hexx.c[1] = 0x34;
    hexx.c[2] = 0x56;
    hexx.c[3] = 0x78;
    if (hexx.l == 0x78563412) big_endian = 0;
}


#define MAXSPV 14

#ifndef GUSPATDIR
#define GUSPATDIR "/dos/ultrasnd/midi"
#endif
#ifndef GUSPATDIR2
#define GUSPATDIR2 "/usr/local/lib/timidity"
#endif
#define GUSPATSLDIR "/dos/ultrasnd/midi/"
#define GUSPATSLDIR2 "/usr/local/lib/timidity/"

static int opt_verbose = 0;

static void fix_header_read(unsigned char *buf, struct pat_header *header)
{
	header->nr_waveforms = shortnum(buf[85],buf[86]);
	header->master_volume = shortnum(buf[87],buf[88]);
}

static void fix_header_write(unsigned char *buf, struct pat_header *header)
{
	/* memcpy(buf, (char *) header, sizeof( header ));*/
	numshort(header->nr_waveforms, buf + 85);
	numshort(header->master_volume, buf + 87);
}

static void fix_sample_read(unsigned char *buf, struct sample_header *sample)
{
	sample->len = longnum(buf[8],buf[9],buf[10],buf[11]);
	sample->loop_start = longnum(buf[12],buf[13],buf[14],buf[15]);
	sample->loop_end = longnum(buf[16],buf[17],buf[18],buf[19]);
	sample->base_freq = shortnum(buf[20],buf[21]);
	sample->low_note = longnum(buf[22],buf[23],buf[24],buf[25]);
	sample->high_note = longnum(buf[26],buf[27],buf[28],buf[29]);
	sample->base_note = longnum(buf[30],buf[31],buf[32],buf[33]);
	sample->detune = shortnum(buf[34],buf[35]);
	sample->panning = (unsigned char)buf[36];
	memcpy(sample->envelope_rate, &buf[37], 6);
	memcpy(sample->envelope_offset, &buf[43], 6);
	sample->tremolo_sweep = (unsigned char) buf[49];
	sample->tremolo_rate = (unsigned char) buf[50];
	sample->tremolo_depth = (unsigned char) buf[51];
	sample->vibrato_sweep = (unsigned char) buf[52];
	sample->vibrato_rate = (unsigned char) buf[53];
	sample->vibrato_depth = (unsigned char) buf[54];
	sample->modes = (char) buf[55];
	sample->scale_frequency = (short)shortnum(buf[56],buf[57]);
	sample->scale_factor = shortnum(buf[58],buf[59]);
	sample->volume = (int)shortnum(buf[60],buf[61]);
	memcpy(sample->mod_envelope_rate, &buf[65], 6);
	memcpy(sample->mod_envelope_offset, &buf[71], 6);
	sample->resonance = (short)shortnum(buf[79],buf[80]);
	sample->cutoff_frequency = (short)shortnum(buf[81],buf[82]);
	sample->lfo_rate = (short)shortnum(buf[92],buf[93]);
	sample->lfo_depth = (unsigned char) buf[94];
	sample->aps_parameter = (char) buf[95];
}

static void fix_sample_write(unsigned char *buf, struct sample_header *sample)
{
	numlong(sample->len, buf + 8);
	numlong(sample->loop_start, buf + 12);
	numlong(sample->loop_end, buf + 16);
	numshort(sample->base_freq, buf + 20);
	numlong(sample->low_note, buf + 22);
	numlong(sample->high_note, buf + 26);
	numlong(sample->base_note, buf + 30);
	numshort(sample->detune, buf + 34);
	buf[36] = sample->panning;
	memcpy(&buf[37], sample->envelope_rate, 6);
	memcpy(&buf[43], sample->envelope_offset, 6);
	buf[49] = sample->tremolo_sweep;
	buf[50] = sample->tremolo_rate;
	buf[51] = sample->tremolo_depth;
	buf[52] = sample->vibrato_sweep;
	buf[53] = sample->vibrato_rate;
	buf[54] = sample->vibrato_depth;
	buf[55] = sample->modes;
	numshort(sample->scale_frequency, buf + 56);
	numshort(sample->scale_factor, buf + 58);
	numshort(sample->volume, buf + 60);
	memcpy(&buf[65], sample->mod_envelope_rate, 6);
	memcpy(&buf[71], sample->mod_envelope_offset, 6);
	numshort(sample->resonance, buf + 79);
	numshort(sample->cutoff_frequency, buf + 81);
	numshort(sample->lfo_rate, buf + 92);
}

static void nullify_extras ( struct sample_header *sample)
{	int i;

	sample->volume_envelope_delay = 0;
	sample->exclusive_class = 0;
	sample->vibrato_delay = 0;

	for (i = 0; i < 6; i++) sample->mod_envelope_rate[i] = 0;
	for (i = 0; i < 6; i++) sample->mod_envelope_offset[i] = 0;

	sample->mod_envelope_delay = 0;
	sample->chorus_effect = 0;
	sample->reverb_effect = 0;
	sample->resonance = 0;
	sample->cutoff_frequency = 0;
	sample->modEnvToPitch = 0;
	sample->modEnvToFilterFc = 0;
	sample->modLfoToFilterFc = 0;
	sample->keynumToModEnvHold = 0;
	sample->keynumToModEnvDecay = 0;
	sample->keynumToVolEnvHold = 0;
	sample->keynumToVolEnvDecay = 0;
	sample->true_panning = 0;
}

main(int argc, char **argv)
{
	int             i, j, k, l, n, leftfd, rightfd, stereofd;
	struct pat_header header;
	struct pat_header right_header;
	struct sample_header sample;
	unsigned char   buf[1024];
	unsigned char   right_buf[1024];
	unsigned char	*sample_buf;
	long            offset, right_offset;
	char		 leftinfile[80], rightinfile[80], outfile[80];
	int	sf2flag_left = 0, sf2flag_right = 0, vlayer_count, stereo_count;
	int     vlayer, vlayer_list[19][4];

	byteorder();

	if (argc != 3) {
		fprintf(stderr, "usage: patcat left.pat right.pat\n");
		exit(1);
	}

	strcpy(outfile, argv[1]);
	strcpy(leftinfile, argv[1]);
	if (!strcmp(leftinfile + strlen(leftinfile) - 4, ".pat"))
		outfile[ strlen(outfile) - 4 ] = '\0';
	else strcat(leftinfile, ".pat");
	strcpy(rightinfile, argv[2]);
	if (strcmp(rightinfile + strlen(rightinfile) - 4, ".pat"))
		strcat(rightinfile, ".pat");

	strcat(outfile, "-stereo.pat");

	if ((leftfd = open(leftinfile, O_RDONLY, 0)) < 0) {
		fprintf(stderr, "Can't open left input file %s\n", leftinfile);
		return 1;
	}
	if ((rightfd = open(rightinfile, O_RDONLY, 0)) < 0) {
		fprintf(stderr, "Can't open right input file %s\n", rightinfile);
		close(leftfd);
		return 1;
	}



	if (read (leftfd, buf, 239) != 239) {
		fprintf (stderr, "%s: Short file\n", leftinfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}
	if (read (rightfd, right_buf, 239) != 239) {
		fprintf (stderr, "%s: Short file\n", rightinfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}

	memcpy ((char *) &header, buf, sizeof (header));
	memcpy ((char *) &right_header, right_buf, sizeof (right_header));

	fix_header_read(buf, &header);
	fix_header_read(right_buf, &right_header);

	if (strncmp (header.magic, "GF1PATCH110", 12)) {
		fprintf (stderr, "%s: Not a patch file\n", leftinfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}
	if (strncmp (right_header.magic, "GF1PATCH110", 12)) {
		fprintf (stderr, "%s: Not a patch file\n", rightinfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}
	if (strncmp (header.version, "ID#000002", 10)) {
		fprintf (stderr, "%s: Incompatible patch file version\n", leftinfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}
	if (strncmp (right_header.version, "ID#000002", 10)) {
		fprintf (stderr, "%s: Incompatible patch file version\n", rightinfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}

	if (!strncmp (header.sf2magic, "SF2EXT", 7)) {
		sf2flag_left = 1;
	}
	if (!strncmp (right_header.sf2magic, "SF2EXT", 7)) {
		sf2flag_right = 1;
	}

	if (sf2flag_left != sf2flag_right) {
		fprintf (stderr, "Can't combine GUS extended with non-extended\n");
		close(leftfd);
		close(rightfd);
		return 1;
	}
	if (sf2flag_left && header.velocity_count != right_header.velocity_count) {
		fprintf (stderr, "The two GUS extended patches don't have the same number of layers\n");
		close(leftfd);
		close(rightfd);
		return 1;
	}

	memcpy(header.sf2magic, "SF2EXT\0", 7);

	if (sf2flag_left) {
		int velmin, velmax, left_samples, right_samples, vmatch, nilright;
		vlayer_count = header.velocity_count;
		vmatch = nilright = 1;
		for (i = 0; i < vlayer_count; i++) {
			if (i < 9) {
				velmin = header.sf2layer[i].velmin;
				if (velmin != right_header.sf2layer[i].velmin) vmatch = 0;
				velmax = header.sf2layer[i].velmax;
				if (velmax != right_header.sf2layer[i].velmax) vmatch = 0;
				left_samples = header.sf2layer[i].left_samples;
				if (header.sf2layer[i].right_samples) nilright = 0;
				right_samples = right_header.sf2layer[i].left_samples;
				if (right_header.sf2layer[i].right_samples) nilright = 0;
				header.sf2layer[i].right_samples = right_samples;
			}
			else {
				velmin = header.sf2layer2[i-9].velmin;
				if (velmin != right_header.sf2layer2[i-9].velmin) vmatch = 0;
				velmax = header.sf2layer2[i-9].velmax;
				if (velmax != right_header.sf2layer2[i-9].velmax) vmatch = 0;
				left_samples = header.sf2layer2[i-9].left_samples;
				if (header.sf2layer2[i-9].right_samples) nilright = 0;
				right_samples = right_header.sf2layer2[i-9].left_samples;
				if (right_header.sf2layer2[i-9].right_samples) nilright = 0;
				header.sf2layer2[i-9].right_samples = right_samples;
			}
			vlayer_list[i][0] = velmin;
			vlayer_list[i][1] = velmax;
			vlayer_list[i][2] = left_samples;
			vlayer_list[i][3] = right_samples;
		}
		if (!vmatch || !nilright) {
			if (!vmatch)
			fprintf (stderr, "The two GUS extended patches don't have matching layers\n");
			else fprintf (stderr, "The two GUS extended patches must both be mono\n");
			close(leftfd);
			close(rightfd);
			return 1;
		}
	}
	else {
		vlayer_count = header.velocity_count = 1;
		vlayer_list[0][0] = header.sf2layer[0].velmin = 0;
		vlayer_list[0][1] = header.sf2layer[0].velmax = 127;
		vlayer_list[0][2] = header.sf2layer[0].left_samples = header.num_samples;
		vlayer_list[0][3] = header.sf2layer[0].right_samples = right_header.num_samples;
	}

	memcpy (buf, (char *) &header, sizeof (header));
	fix_header_write(buf, &header);

	if ((stereofd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
		fprintf(stderr, "Can't open output file %s\n", outfile);
		close(leftfd);
		close(rightfd);
		return 1;
	}

	if ( write(stereofd, buf, 239) != 239 ) {
		fprintf(stderr, "Write error\n");
		return 1;
	}


	offset = 239;
	right_offset = 239;

  for (vlayer = 0; vlayer < vlayer_count; vlayer++) {

    for (i = 0; i < vlayer_list[vlayer][2]; i++) {

	if (lseek (leftfd, offset, 0) == -1) {
		perror (leftinfile);
		exit (1);
	}

	if (read (leftfd, &buf, sizeof (sample)) != sizeof (sample)) {
		fprintf (stderr, "%s: Short file\n", leftinfile);
		exit (-1);
	}

	memcpy ((char *) &sample, buf, sizeof (sample));

	/*
	 * Since some fields of the patch record are not 32bit aligned, we must
	 * handle them specially.
	 */
	fix_sample_read(buf, &sample);

	/* change ext part of header now */
	if (!sf2flag_left) {
		nullify_extras(&sample);
		sample.true_panning = 0;
	}

	memcpy (buf, (char *) &sample, sizeof (sample));
	fix_sample_write(buf, &sample);

	if ( write(stereofd, buf, 96) != 96 ) {
		fprintf(stderr, "Write error\n");
		return 1;
	}

	offset += 96;

	sample_buf = malloc(sample.len);

	if (read (leftfd, sample_buf, sample.len) != sample.len) {
		fprintf (stderr, "%s: Short file\n", leftinfile);
		return 1;
	}
	if ( write(stereofd, sample_buf, sample.len) != sample.len ) {
		fprintf(stderr, "Write error\n");
		return 1;
	}

	free(sample_buf);

        offset += sample.len;

      } /* keyrange */


    for (i = 0; i < vlayer_list[vlayer][3]; i++) {

	if (lseek (rightfd, right_offset, 0) == -1) {
		perror (rightinfile);
		return 1;
	}

	if (read (rightfd, &buf, sizeof (sample)) != sizeof (sample)) {
		fprintf (stderr, "%s: Short file\n", rightinfile);
		return 1;
	}

	memcpy ((char *) &sample, buf, sizeof (sample));

	/*
	 * Since some fields of the patch record are not 32bit aligned, we must
	 * handle them specially.
	 */
	fix_sample_read(buf, &sample);

	/* change ext part of header now */
	nullify_extras(&sample);
	sample.true_panning = 127;

	memcpy (buf, (char *) &sample, sizeof (sample));
	fix_sample_write(buf, &sample);

	if ( write(stereofd, buf, 96) != 96 ) {
		fprintf(stderr, "Write error\n");
		return 1;
	}

	right_offset += 96;

	sample_buf = malloc(sample.len);

	if (read (rightfd, sample_buf, sample.len) != sample.len) {
		fprintf (stderr, "%s: Short file\n", rightinfile);
		return 1;
	}
	if ( write(stereofd, sample_buf, sample.len) != sample.len ) {
		fprintf(stderr, "Write error\n");
		return 1;
	}

	free(sample_buf);

        right_offset += sample.len;

      } /* keyrange */
  } /* vlayer */

	close(leftfd);
	close(rightfd);
	close(stereofd);


	return 0;
}
