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

#include <stdio.h>
#include <sys/ultrasound.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>

struct lib_pat_header {
    char  magic[12];
    char  version[10];
    char  filename[60];
    unsigned char  fill[3];
    unsigned char  nr_samples;
};

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;
  };

struct lib_sample_header {
    unsigned short  base_freq;

    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;
};

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;
  };

#define MAXSPV 14

#ifndef GUSPATDIR
#define GUSPATDIR "/dos/ultrasnd/midi"
#endif
#ifndef GUSPATDIR2
#define GUSPATDIR2 "/dos/ultrasnd/midi2"
#endif
#define GUSPATSLDIR GUSPATDIR##"/"
#define GUSPATSLDIR2 GUSPATDIR2##"/"

main(int argc, char **argv)
{
	int             i, j, n, patfd, libfd;
	struct lib_pat_header header;
	struct sample_header sample;
	struct lib_sample_header lib_sample;
	char            buf[100];
	long            offset;
	char		 infile[80], outfile[80];

	if (argc != 2) {
		fprintf(stderr, "usage: pat2gl file.pat\n");
		exit(1);
	}

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

	if ((libfd = creat(outfile, 0666)) == -1) {
		perror (outfile);
		exit (1);
	}

	if ((patfd = open(infile, O_RDONLY, 0)) == -1) {
		strcpy(infile, GUSPATSLDIR);
		strcat(infile, argv[1]);
		if (strcmp(infile + strlen(infile) - 4, ".pat"))
			strcat(infile, ".pat");
		if ((patfd = open(infile, O_RDONLY, 0)) == -1) {
			perror (infile);
			exit (1);
		}
	}

	if (read (patfd, buf, 0xef) != 0xef) {
		fprintf (stderr, "%s: Short file\n", infile);
		exit (1);
	}
	memcpy ((char *) &header, buf, sizeof (header));

	if (strncmp (header.magic, "GF1PATCH110", 12)) {
		fprintf (stderr, "%s: Not a patch file\n", infile);
		exit (1);
	}
	if (strncmp (header.version, "ID#000002", 10)) {
		fprintf (stderr, "%s: Incompatible patch file version\n", infile);
		exit (-1);
	}
	header.nr_samples = *(unsigned short *) &buf[85];

	strcpy(header.filename, infile);
	for (j = 0; j < 12; j++) header.magic[j] = '\0';
	for (j = 0; j < 10; j++) header.version[j] = '\0';
	for (j = 0; j < 3; j++) header.fill[j] = '\0';

	header.version[1] = 64; /* transpose */
	header.version[2] = 8192 >> 8; /* tune voice */
	header.version[3] = 8192 & 0xff; /* tune voice */
	header.version[4] = 64; /* chorus spread */
	header.version[5] = 64; /* echo delay */
	header.version[6] = 64; /* volume */
	header.version[8] = 64; /* echo attenuation */

	if (write(libfd, (char*)&header, sizeof(header)) == -1) {
		close(patfd);
		fprintf (stderr, "couldn't write to lib\n");
		exit (1);
	}
	printf("appended %d byte header\n", sizeof(header));

	offset = 0xef;


    for (i = 0; i < 1 /*header.nr_samples*/; i++) {

	if (lseek (patfd, offset, 0) == -1) {
		perror (infile);
		exit (1);
	}

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

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

	/*
	 * Since some fields of the patch record are not 32bit aligned, we must
	 * handle them specially.
	 */

    sample.low_note = *(long *) &buf[22];
    sample.high_note = *(long *) &buf[26];
    sample.base_note = *(long *) &buf[30];
    sample.detune = *(short *)&buf[34];
    sample.panning = (unsigned char)buf[36];

    lib_sample.base_freq = sample.base_freq;

    memcpy(lib_sample.envelope_rate, &buf[37], 6);
    memcpy(lib_sample.envelope_offset, &buf[43], 6);

    lib_sample.tremolo_sweep = (unsigned char) buf[49];
    lib_sample.tremolo_rate = (unsigned char) buf[50];
    lib_sample.tremolo_depth = (unsigned char) buf[51];

    lib_sample.vibrato_sweep = (unsigned char) buf[52];
    lib_sample.vibrato_rate = (unsigned char) buf[53];
    lib_sample.vibrato_depth = (unsigned char) buf[54];
    lib_sample.modes = (unsigned char) buf[55];
    lib_sample.scale_frequency = *(short *)&buf[56];
    lib_sample.scale_factor = *(unsigned short *)&buf[58];

	offset = offset + 96;

	if (write(libfd, (char*)&lib_sample, sizeof(lib_sample)) == -1) {
		close(patfd);
		exit(1);
	}

	printf("appended %d byte sample of %d\n", sizeof(lib_sample), header.nr_samples);

	offset = offset + sample.len;
    }
/**
    for (i = 0; i < 100; i++) buf[i] = 0;

    for (i = header.nr_samples; i < MAXSPV; i++) {

	if (write(libfd, buf, sizeof(sample)) == -1) {
		close(patfd);
		exit(1);
	}

	printf("appended %d byte blank space\n", sizeof(sample));
    }
**/
	close(patfd);

	exit(0);
}
