#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>

#ifndef NO_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

#if defined(__FreeBSD__) || defined(__WIN32__) || defined(__bsdi__)
#include <stdlib.h>
#else
#include <malloc.h>
#endif

#include "timidity.h"

/* I guess "rb" should be right for any libc */
#define OPEN_MODE "rb"

void patinfo(const char *patfile);
void string_dump(const char *s, int len);
void safe_puts(const char *s, int len);
uint16 conv_short(uint8 *s);
uint32 conv_long(uint8 *s);
void skip(FILE *fp, size_t len);
void dump_modes(uint8 modes);
int find_note(uint32 freq);
char *note_string(int note);
int32 convert_envelope_rate(uint8 rate);
double convert_envelope_volume(uint8 offset);
double convert_envelope_time(uint8 rate, uint8 o1, uint8 o2);

uint32 freq_table[128]=
{
    8176, 8662, 9177, 9723,
    10301, 10913, 11562, 12250,
    12978, 13750, 14568, 15434,

    16352, 17324, 18354, 19445,
    20602, 21827, 23125, 24500,
    25957, 27500, 29135, 30868,

    32703, 34648, 36708, 38891,
    41203, 43654, 46249, 48999,
    51913, 55000, 58270, 61735,

    65406, 69296, 73416, 77782,
    82407, 87307, 92499, 97999,
    103826, 110000, 116541, 123471,

    130813, 138591, 146832, 155563,
    164814, 174614, 184997, 195998,
    207652, 220000, 233082, 246942,

    261626, 277183, 293665, 311127,
    329628, 349228, 369994, 391995,
    415305, 440000, 466164, 493883,

    523251, 554365, 587330, 622254,
    659255, 698456, 739989, 783991,
    830609, 880000, 932328, 987767,

    1046502, 1108731, 1174659, 1244508,
    1318510, 1396913, 1479978, 1567982,
    1661219, 1760000, 1864655, 1975533,

    2093005, 2217461, 2349318, 2489016,
    2637020, 2793826, 2959955, 3135963,
    3322438, 3520000, 3729310, 3951066,

    4186009, 4434922, 4698636, 4978032,
    5274041, 5587652, 5919911, 6271927,
    6644875, 7040000, 7458620, 7902133,

    8372018, 8869844, 9397273, 9956063,
    10548082, 11175303, 11839822, 12543854
};

/* v=2.^((x/127-1) * 6) */
FLOAT_T vol_table[128] =
{
 0.015625, 0.016145143728351113, 0.016682602624583379, 0.017237953096759438,
 0.017811790741104401, 0.01840473098076444, 0.019017409725829021, 0.019650484055324921,
 0.020304632921913132, 0.020980557880044631, 0.021678983838355849, 0.02240065983711079,
 0.023146359851523596, 0.023916883621822989, 0.024713057510949051, 0.025535735390801884,
 0.026385799557992876, 0.027264161680080529, 0.028171763773305786, 0.029109579212875332,
 0.030078613776876421, 0.031079906724942836, 0.032114531912828696, 0.033183598944085631,
 0.034288254360078256, 0.035429682869614412, 0.036609108619508737, 0.037827796507442342,
 0.039087053538526394, 0.040388230227024875, 0.041732722044739302, 0.043121970917609151,
 0.044557466772132896, 0.046040749133268132, 0.047573408775524545, 0.049157089429020417,
 0.050793489542332405, 0.05248436410402918, 0.054231526524842463, 0.056036850582493913,
 0.057902272431264008, 0.059829792678457581, 0.061821478529993396, 0.063879466007418645,
 0.066005962238725971, 0.068203247825430205, 0.070473679288442961, 0.072819691595368496,
 0.075243800771931268, 0.077748606600335793, 0.080336795407452768, 0.083011142945821612,
 0.085774517370559328, 0.088629882315368294, 0.091580300070941839, 0.094628934869176312,
 0.097779056276712184, 0.10103404270144323, 0.1043973850157546, 0.1078726903003755,
 0.11146368571286204, 0.11517422248485852, 0.11900828005242428, 0.12296997032385605,
 0.12706354208958254, 0.13129338557886089, 0.13566403716816194, 0.14018018424629392,
 0.14484667024148207, 0.14966849981579558, 0.15465084423249356, 0.15979904690204472,
 0.16511862911277009, 0.17061529595225433, 0.17629494242587571, 0.18216365977901747,
 0.18822774202974024, 0.19449369271892172, 0.20096823188510385, 0.20765830327152621,
 0.21457108177307616, 0.22171398113114205, 0.2290946618846218, 0.23672103958561411,
 0.2446012932886038, 0.25274387432224471, 0.26115751535314891, 0.26985123975140174,
 0.27883437126784744, 0.28811654403352405, 0.29770771289197112, 0.30761816407549192,
 0.31785852623682015, 0.32843978184802081, 0.33937327897885317, 0.3506707434672246,
 0.36234429149478936, 0.37440644258117928, 0.38687013301080181, 0.39974872970660535,
 0.41305604456569134, 0.42680634927214656, 0.44101439060298442, 0.45569540624360722,
 0.47086514112975281, 0.48653986433345225, 0.50273638651110641, 0.51947207793239625,
 0.53676488710936021, 0.55463336004561792, 0.57309666012638816, 0.59217458867062556,
 0.61188760616732485, 0.63225685421876243, 0.65330417821421161, 0.67505215075844849,
 0.69752409588017272, 0.72074411404630734, 0.74473710800900605, 0.76952880951308478,
 0.79514580689252357, 0.82161557358563286, 0.84896649759946774, 0.87722791195508854,
 0.90643012614631979, 0.93660445864574493, 0.96778327049280244, 1
};

int main(int argc, char **argv)
{
    int i;

    if(argc == 1)
	patinfo(NULL);
    else
	for(i = 1; i < argc; i++)
	    patinfo(argv[i]);
    return 0;
}

void patinfo(const char *patfile)
{
    FILE *fp;
    uint8 tmp[1024];
    int samples, i, j;

    if(patfile == NULL)
    {
	printf("(stdin):" NLS);
	fp = stdin;
    }
    else
    {
	printf("%s:" NLS, patfile);
	if((fp = fopen(patfile, OPEN_MODE)) == NULL)
	{
	    perror(patfile);
	    return;
	}
    }

    if ((239 != fread(tmp, 1, 239, fp)) ||
	(memcmp(tmp, "GF1PATCH110\0ID#000002", 22) &&
	 memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the
							differences are */
    {
	fprintf(stderr, "    Not an instrument" NLS);
	if(fp != stdin)
	    fclose(fp);
	return;
    }

    if(tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers,
					0 means 1 */
    {
	fprintf(stderr, "    Can't handle patches with %d instruments" NLS,
		tmp[82]);
	if(fp != stdin)
	    fclose(fp);
	return;
    }

    if(tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */
    {
	fprintf(stderr, "    Can't handle instruments with %d layers" NLS,
		tmp[151]);
	if(fp != stdin)
	    fclose(fp);
	return;
    }

    printf("16  Discription: ");
    string_dump((char *)tmp + 22, 60);

    printf("52  Instruments: %d" NLS, tmp[82]);
    printf("53  Voices: %d" NLS, tmp[83]);
    printf("54  Channels: %d" NLS, tmp[84]);
    printf("55  Waveforms: %d" NLS, conv_short(tmp + 85));
    printf("57  MasterVolume: %d" NLS, conv_short(tmp + 87));
    printf("59  DataSize: %ld" NLS, (long)conv_long(tmp + 89));

    /* 93 -> 129 */
    printf("5d ");
    for(i = 93; i < 129; i++)
	printf(" 0x%02x", tmp[i]);
    fputs(NLS, stdout);

    printf("81  InstrumentNumber: %d" NLS, conv_short(tmp + 129));
    printf("83  InstrumentName: ");
    safe_puts((char *)tmp + 131, 16);
    printf("93  InstrumentSize: %ld" NLS,
	   (long)conv_long(tmp + 147));
    printf("97  Layers: %d" NLS, tmp[151]);

    /* 152 -> 192 */
    printf("98 ");
    for(i = 152; i < 192; i++)
	printf(" 0x%02x", tmp[i]);
    fputs(NLS, stdout);

    printf("c0  LayerDuplicate: %d" NLS, tmp[192]);
    printf("c1  Layer: %d" NLS, tmp[193]);
    printf("c2  LayerSize: %ld" NLS, (long)conv_long(tmp + 194));

    samples = tmp[198];
    printf("c6  Samples: %d" NLS, samples);

    printf("99 ");
    for(i = 199; i < 239; i++)
	printf(" 0x%02x", tmp[i]);
    fputs(NLS, stdout);

    for(i = 0; i < samples; i++)
    {
	uint8 fractions;
	int32 tmplong;
	uint16 tmpshort;
	uint8 tmpchar;
	int32 data_length;
	int32 tmpaddr;
	int note;

#define READ_CHAR(thing) \
	if (1 != fread(&tmpchar, 1, 1, fp)) goto fail; \
	thing = tmpchar;
#define READ_SHORT(thing) \
	if (1 != fread(&tmpshort, 2, 1, fp)) goto fail; \
	thing = LE_SHORT(tmpshort);
#define READ_LONG(thing) \
	if (1 != fread(&tmplong, 4, 1, fp)) goto fail; \
	thing = LE_LONG(tmplong);

	printf("    Sample No: %d" NLS, i);

	printf("\t%lx\t", (long)ftell(fp));
	if(fread(tmp, 1, 7, fp) != 7)
	    goto fail;
	printf("WaveName: ");
	safe_puts((char *)tmp, 7);

	printf("\t%lx\t", (long)ftell(fp));
	if(1 != fread(&fractions, 1, 1, fp))
	{
	  fail:
	    fprintf(stderr, "\tError reading sample %d" NLS, i);
	    if(fp != stdin)
		fclose(fp);
	    return;
	}
	printf("Fractions: %d %d" NLS,
	       fractions & 0x0f, (fractions>>4) & 0x0f);

	printf("\t%lx\t", (long)ftell(fp));
	READ_LONG(data_length);
	printf("DataLength: %ld" NLS, (long)data_length);

	printf("\t%lx\t", (long)ftell(fp));
	READ_LONG(tmplong);
	printf("LoopStart: %ld" NLS, (long)tmplong);

	printf("\t%lx\t", (long)ftell(fp));
	READ_LONG(tmplong);
	printf("LoopEnd: %ld" NLS, (long)tmplong);

	printf("\t%lx\t", (long)ftell(fp));
	READ_SHORT(tmpshort);
	printf("SampleRate: %d" NLS, tmpshort);

	printf("\t%lx\t", (long)ftell(fp));
	READ_LONG(tmplong);
	printf("LowFrequency: %ld note ", (long)tmplong);
	note = find_note(tmplong);
	if(tmplong != freq_table[note])
	    putchar('~');
	printf("%d (%s)" NLS, note, note_string(note));

	printf("\t%lx\t", (long)ftell(fp));
	READ_LONG(tmplong);
	printf("HighFrequency: %ld note ", (long)tmplong);
	note = find_note(tmplong);
	if(tmplong != freq_table[note])
	    putchar('~');
	printf("%d (%s)" NLS, note, note_string(note));

	printf("\t%lx\t", (long)ftell(fp));
	READ_LONG(tmplong);
	printf("RootFrequency: %ld note ", (long)tmplong);
	note = find_note(tmplong);
	if(tmplong != freq_table[note])
	    putchar('~');
	printf("%d (%s)" NLS, note, note_string(note));

	printf("\t%lx\t", (long)ftell(fp));
	READ_SHORT(tmpshort);
	printf("Tune: %d" NLS, (int)tmpshort); /* Always 1, not used anymore */

	printf("\t%lx\t", (long)ftell(fp));
	READ_CHAR(tmp[0]);
	printf("Panning: %d", tmp[0]); /* 0..15 */
	if(tmp[0] == 0)
	    printf(" (Left)");
	else if(tmp[0] == 7)
	    printf(" (Center)");
	else if(tmp[0] == 15)
	    printf(" (Right)");
	fputs(NLS, stdout);

	tmpaddr = ftell(fp);
	if (18 != fread(tmp, 1, 18, fp)) goto fail;

	for(j = 0; j < 6; j++)
	{
	    double envt;

	    if(j == 0)
		envt = convert_envelope_time(tmp[0], 0, tmp[6]);
	    else
		envt = convert_envelope_time(tmp[j], tmp[j + 5], tmp[j + 6]);
	    printf("\t%lx\tEnvelopeRate(%s): 0x%02x %ld (%.3e sec)" NLS,
		   (long)(tmpaddr + j),
		   j < 3 ? "on" : "off",
		   tmp[j], (long)convert_envelope_rate(tmp[j]), envt);
	}

	for(; j < 12; j++)
	    printf("\t%lx\tEnvelopeOffset(%s): %d %.1f%%" NLS,
		   (long)(tmpaddr + j),
		   j < 9 ? "on" : "off",
		   tmp[j], convert_envelope_volume(tmp[j]) * 100);

	printf("\t%lx\tTremoloSweep: %d" NLS,
	       (long)tmpaddr + 12, tmp[12]);
	printf("\t%lx\tTremoloRate: %d"  NLS,
	       (long)tmpaddr + 13, tmp[13]);
	printf("\t%lx\tTremoloDepth: %d" NLS,
	       (long)tmpaddr + 14, tmp[14]);
	printf("\t%lx\tVibratoSweep: %d" NLS,
	       (long)tmpaddr + 15, tmp[15]);
	printf("\t%lx\tVibratoRate: %d"  NLS,
	       (long)tmpaddr + 16, tmp[16]);
	printf("\t%lx\tVibratoDepth: %d" NLS,
	       (long)tmpaddr + 17, tmp[17]);

	printf("\t%lx\t", (long)ftell(fp));
	READ_CHAR(tmpchar);
	printf("Mode: 0x%02x (", tmpchar);
	dump_modes(tmpchar);
	printf(")" NLS);

	printf("\t%lx\t", (long)ftell(fp));
	READ_SHORT(tmpshort);
	printf("ScaleFrequency: %d" NLS, (int)tmpshort);

	printf("\t%lx\t", (long)ftell(fp));
	READ_SHORT(tmpshort);
	printf("ScaleFactor: %d" NLS, (int)tmpshort);

	printf("\t%lx\t", (long)ftell(fp));
	for(j = 0; j < 36; j++)
	{
	    int c;
	    if((c = getc(fp)) == EOF)
	    {
		puts("EOF");
		exit(1);
	    }
	    printf("0x%02x ", c);
	}
	fputs(NLS, stdout);

	/* data */
	printf("\t%lx\tData..." NLS, (long)ftell(fp));

	skip(fp, data_length);
    }
    if(fp != stdin)
	fclose(fp);
}

void string_dump(const char *s, int len)
{
    int i;

    for(i = 0; i < len; i++)
	if(s[i] < 32)
	    putchar('.');
	else
	    putchar(s[i]);
    fputs(NLS, stdout);
}

void safe_puts(const char *s, int len)
{
    int i;

    for(i = 0; i < len && s[i]; i++)
	if(s[i] < 32)
	    putchar('.');
	else
	    putchar(s[i]);
    fputs(NLS, stdout);
}

uint16 conv_short(uint8 *s)
{
    uint16 x;
    memcpy(&x, s, 2);
    return LE_SHORT(x);
}

uint32 conv_long(uint8 *s)
{
    uint32 x;
    memcpy(&x, s, 4);
    return LE_LONG(x);
}

void skip(FILE *fp, size_t len)
{
    size_t c;
    char tmp[1024];

    while(len > 0)
    {
	c = len;
	if(c > 1024)
	    c = 1024;
	len -= c;
	if(c != fread(tmp, 1, c, fp))
	    return;
    }
}

void dump_modes(uint8 modes)
{
    int i, flag;
    char *mode_names[8] =
    {
	"16BIT",		/* 1 */
	"UNSIGNED",		/* 2 */
	"LOOPING",		/* 4 */
	"PINGPONG",		/* 8 */
	"REVERSE",		/* 16 */
	"SUSTAIN",		/* 32 */
	"ENVELOPE",		/* 64 */
	"CLAMPED"		/* 128 */
    };

    flag = 0;
    for(i = 0; i < 8; i++)
    {
	if(modes & (1<<i))
	{
	    if(flag)
		putchar('|');
	    fputs(mode_names[i], stdout);
	    flag = 1;
	}
    }
}

int find_note(uint32 freq)
{
    int i, d1, d2;

    if(freq < freq_table[0])
	return 0;

    for(i = 0; i < 127; i++)
    {
	if(freq_table[i] <= freq && freq < freq_table[i + 1])
	    break;
    }

    if(i == 127)
	return 127;

    d1 = (int)((long)freq - (long)freq_table[i]);
    d2 = (int)((long)freq_table[i + 1] - (long)freq);

    if(d1 < d2)
	return i;
    return i + 1;
}

char *note_string(int note)
{
    static char buf[8];
    static const char *note_name[12] =
    {
	"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
    };
    int n, o;

    n = note % 12;
    o = note / 12;
    sprintf(buf, "%s%d", note_name[n], o);
    return buf;
}


int32 convert_envelope_rate(uint8 rate)
{
    int32 r;

    r=3-((rate>>6) & 0x3);
    r*=3;
    r = (int32)(rate & 0x3f) << r;
    return r;
}

double convert_envelope_volume(uint8 offset)
{
    /* This is not too good... Can anyone tell me what these values mean?
       Are they GUS-style "exponential" volumes? And what does that mean? */
    return vol_table[offset >> 1];
}

double convert_envelope_time(uint8 rate, uint8 o1, uint8 o2)
{
    int32 d, r;

    if(o1 > o2)
	d = o1 - o2;
    else
	d = o2 - o1;
    d <<= 22;
    r = convert_envelope_rate(rate) << 9;

    return (double)d / r / 44100.0;
}
