/*  _______         ____    __         ___    ___
 * \    _  \       \    /  \  /       \   \  /   /       '   '  '
 *  |  | \  \       |  |    ||         |   \/   |         .      .
 *  |  |  |  |      |  |    ||         ||\  /|  |
 *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
 *  |  |  |  |      |  |    ||         ||    |  |         .      .
 *  |  |_/  /        \  \__//          ||    |  |
 * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
 *                                                      /  \
 *                                                     / .  \
 * itread.c - Code to read an Impulse Tracker         / / \  \
 *            module from an open file.              | <  /   \_
 *                                                   |  \/ /\   /
 * Based on the loader from an IT player by Bob.      \_  /  > /
 * Adapted for DUMB by entheh.                          | \ / /
 *                                                      |  ' /
 *                                                       \__/
 */

#include <stdlib.h>
#include <string.h>//might not be necessary later; required for memset

#include "dumb.h"
#include "internal/it.h"



#define INVESTIGATE_OLD_INSTRUMENTS



static int it_seek(DUMBFILE *f, long offset)
{
	long pos = dumbfile_pos(f);

	if (pos > offset)
		return -1;

	if (pos < offset)
		if (dumbfile_skip(f, offset - pos))
			return -1;

	return 0;
}



typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;



static unsigned char *sourcebuf = NULL;
static unsigned char *sourcepos = NULL;
static unsigned char *sourceend;
static int rembits = 0;



static int readblock(DUMBFILE *f)
{
	long size;
	int c;

	size = dumbfile_igetw(f);
	if (size < 0)
		return size;

	sourcebuf = malloc(size);
	if (!sourcebuf)
		return -1;

	c = dumbfile_getnc((char *)sourcebuf, size, f);
	if (c < size) {
		free(sourcebuf);
		sourcebuf = NULL;
		return -1;
	}

	sourcepos = sourcebuf;
	sourceend = sourcebuf + size;
	rembits = 8;
	return 0;
}



static void freeblock(void)
{
	free(sourcebuf);
	sourcebuf = NULL;
}



static int readbits(int bitwidth)
{
	int val = 0;
	int b = 0;

	if (sourcepos >= sourceend) return val;

	while (bitwidth > rembits) {
		val |= *sourcepos++ << b;
		if (sourcepos >= sourceend) return val;
		b += rembits;
		bitwidth -= rembits;
		rembits = 8;
	}

	val |= (*sourcepos & ((1 << bitwidth) - 1)) << b;
	*sourcepos >>= bitwidth;
	rembits -= bitwidth;

	return val;
}



/** WARNING - do we even need to pass `right`? */
/** WARNING - why bother memsetting at all? The whole array is written... */
// if we do memset, dumb_silence() would be neater...
static int decompress8(DUMBFILE *f, sample_t *left, sample_t *right, int len, int cmwt)
{
	int blocklen, blockpos;
	byte bitwidth;
	word val;
	char d1, d2;

	memset(left, 0, len * sizeof(*left));
	if (right) {
		memset(right, 0, len * sizeof(*right));
		len <<= 1;
	}

	while (len > 0) {
		//Read a block of compressed data:
		if (readblock(f))
			return -1;
		//Set up a few variables
		blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes
		blockpos = 0;
		bitwidth = 9;
		d1 = d2 = 0;
		//Start the decompression:
		while (blockpos < blocklen) {
			//Read a value:
			val = (word)readbits(bitwidth);
			//Check for bit width change:

			if (bitwidth < 7) { //Method 1:
				if (val == (1 << (bitwidth - 1))) {
					val = (word)readbits(3) + 1;
					bitwidth = (val < bitwidth) ? val : val + 1;
					continue;
				}
			}
			else if (bitwidth < 9) { //Method 2
				byte border = (0xFF >> (9 - bitwidth)) - 4;

				if (val > border && val <= (border + 8)) {
					val -= border;
					bitwidth = (val < bitwidth) ? val : val + 1;
					continue;
				}
			}
			else if (bitwidth == 9) { //Method 3
				if (val & 0x100) {
					bitwidth = (val + 1) & 0xFF;
					continue;
				}
			}
			else { //Illegal width, abort ?
				freeblock();
				return -1;
			}

			//Expand the value to signed byte:
			{
				char v; //The sample value:
				if (bitwidth < 8) {
					byte shift = 8 - bitwidth;
					v = (val << shift);
					v >>= shift;
				}
				else
					v = (char)val;

				//And integrate the sample value
				//(It always has to end with integration doesn't it ? ;-)
				d1 += v;
				d2 += d1;
			}

			//Store !
			/* Version 2.15 was an unofficial version with hacked compression
			 * code. Yay, better compression :D
			 */
			if (right && (len & 1))
				*right++ = (int)(signed char)(cmwt == 0x215 ? d2 : d1) << 16;
			else
				*left++ = (int)(signed char)(cmwt == 0x215 ? d2 : d1) << 16;
			len--;
			blockpos++;
		}
		freeblock();
	}
	return 0;
}



static int decompress16(DUMBFILE *f, sample_t *left, sample_t *right, int len, int cmwt)
{
	int blocklen, blockpos;
	byte bitwidth;
	long val;
	short d1, d2;

	memset(left, 0, len * sizeof(*left));
	if (right) {
		memset(right, 0, len * sizeof(*right));
		len <<= 1;
	}

	while (len > 0) {
		//Read a block of compressed data:
		if (readblock(f))
			return -1;
		//Set up a few variables
		blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes
		blockpos = 0;
		bitwidth = 17;
		d1 = d2 = 0;
		//Start the decompression:
		while (blockpos < blocklen) {
			val = readbits(bitwidth);
			//Check for bit width change:

			if (bitwidth < 7) { //Method 1:
				if (val == (1 << (bitwidth - 1))) {
					val = readbits(4) + 1;
					bitwidth = (val < bitwidth) ? val : val + 1;
					continue;
				}
			}
			else if (bitwidth < 17) { //Method 2
				word border = (0xFFFF >> (17 - bitwidth)) - 8;

				if (val > border && val <= (border + 16)) {
					val -= border;
					bitwidth = val < bitwidth ? val : val + 1;
					continue;
				}
			}
			else if (bitwidth == 17) { //Method 3
				if (val & 0x10000) {
					bitwidth = (val + 1) & 0xFF;
					continue;
				}
			}
			else { //Illegal width, abort ?
				freeblock();
				return -1;
			}

			//Expand the value to signed byte:
			{
				short v; //The sample value:
				if (bitwidth < 16) {
					byte shift = 16 - bitwidth;
					v = (short)(val << shift);
					v >>= shift;
				}
				else
					v = (short)val;

				//And integrate the sample value
				//(It always has to end with integration doesn't it ? ;-)
				d1 += v;
				d2 += d1;
			}

			//Store !
			/* Version 2.15 was an unofficial version with hacked compression
			 * code. Yay, better compression :D
			 */
			if (right && (len & 1))
				*right++ = (int)(signed short)(cmwt == 0x215 ? d2 : d1) << 8;
			else
				*left++ = (int)(signed short)(cmwt == 0x215 ? d2 : d1) << 8;
			len--;
			blockpos++;
		}
		freeblock();
	}
	return 0;
}



static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f)
{
	int n;

	envelope->flags = dumbfile_getc(f);
	envelope->n_nodes = dumbfile_getc(f);
	envelope->loop_start = dumbfile_getc(f);
	envelope->loop_end = dumbfile_getc(f);
	envelope->sus_loop_start = dumbfile_getc(f);
	envelope->sus_loop_end = dumbfile_getc(f);
	for (n = 0; n < envelope->n_nodes; n++) {
		envelope->node_y[n] = dumbfile_getc(f);
		envelope->node_t[n] = dumbfile_igetw(f);
	}
	dumbfile_skip(f, 75 - envelope->n_nodes * 3 + 1);

	return dumbfile_error(f);
}



static int it_read_old_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f)
{
	int n;

	if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
		return -1;

	/* Skip DOS Filename. */
	dumbfile_skip(f, 13);

	instrument->volume_envelope.flags = dumbfile_getc(f);
	instrument->volume_envelope.loop_start = dumbfile_getc(f);
	instrument->volume_envelope.loop_end = dumbfile_getc(f);
	instrument->volume_envelope.sus_loop_start = dumbfile_getc(f);
	instrument->volume_envelope.sus_loop_end = dumbfile_getc(f);

	/* Skip two unused bytes. */
	dumbfile_skip(f, 2);

	/* In the old instrument format, fadeout ranges from 0 to 64, and is
	 * subtracted at intervals from a value starting at 512. In the new
	 * format, all these values are doubled. Therefore we double when loading
	 * from the old instrument format - that way we don't have to think about
	 * it later.
	 */
	instrument->fadeout = dumbfile_igetw(f) << 1;
	instrument->new_note_action = dumbfile_getc(f);
	instrument->dup_check_type = dumbfile_getc(f);
	instrument->dup_check_action = DCA_NOTE_CUT; // This might be wrong!
	/** WARNING - what is the duplicate check action for old-style instruments? */

	/* Skip Tracker Version and Number of Samples. These are only used in
	 * separate instrument files. Also skip unused bytes and Instrument Name.
	 */
	dumbfile_skip(f, 36);

	instrument->pp_separation = 0;
	instrument->pp_centre = 60;
	instrument->global_volume = 128;
	/** WARNING - should global_volume be 64 or something? */
	instrument->default_pan = 32;
	/** WARNING - should default_pan be 128, meaning don`t use? */
	instrument->random_volume = 0;
	instrument->random_pan = 0;

	for (n = 0; n < 120; n++) {
		instrument->map_note[n] = dumbfile_getc(f);
		instrument->map_sample[n] = dumbfile_getc(f);
	}

	/* Skip "Volume envelope (200 bytes)". */
	// - need to know better what this is for though.
	dumbfile_skip(f, 200);

#if defined(INVESTIGATE_OLD_INSTRUMENTS) && 0
	fprintf(stderr, "Inst %02d Env:", n);
#endif

	for (n = 0; n < 25; n++)
	{
		instrument->volume_envelope.node_t[n] = dumbfile_getc(f);
		instrument->volume_envelope.node_y[n] = dumbfile_getc(f);

#if defined(INVESTIGATE_OLD_INSTRUMENTS) && 0
		fprintf(stderr, " %d,%d",
				instrument->volume_envelope.node_t[n],
				instrument->volume_envelope.node_y[n]);
#endif

		// This loop is unfinished, as we can probably escape from it before
		// the end if we want to. Hence the otherwise useless dumbfile_skip()
		// call below.
	}
	dumbfile_skip(f, 50 - (n << 1));
	instrument->volume_envelope.n_nodes = n;

#if defined(INVESTIGATE_OLD_INSTRUMENTS) && 0
	fprintf(stderr, "\n");
#endif

	if (dumbfile_error(f))
		return -1;

	instrument->filter_cutoff = 127;
	instrument->filter_resonance = 0;

	instrument->pan_envelope.flags = 0;
	instrument->pitch_envelope.flags = 0;

	return 0;
}



static int it_read_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f)
{
	int n;

	if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
		return -1;

	/* Skip DOS Filename. */
	dumbfile_skip(f, 13);

	instrument->new_note_action = dumbfile_getc(f);
	instrument->dup_check_type = dumbfile_getc(f);
	instrument->dup_check_action = dumbfile_getc(f);
	instrument->fadeout = dumbfile_igetw(f);
	instrument->pp_separation = dumbfile_getc(f);
	instrument->pp_centre = dumbfile_getc(f);
	instrument->global_volume = dumbfile_getc(f);
	instrument->default_pan = dumbfile_getc(f);
	instrument->random_volume = dumbfile_getc(f);
	instrument->random_pan = dumbfile_getc(f);

	/* Skip Tracker Version and Number of Samples. These are only used in
	 * separate instrument files. Also skip unused byte and Instrument Name.
	 */
	dumbfile_skip(f, 30);

	instrument->filter_cutoff = dumbfile_getc(f);
	instrument->filter_resonance = dumbfile_getc(f);

	/* Skip MIDI Channel, Program and Bank. */
	dumbfile_skip(f, 4);

	for (n = 0; n < 120; n++) {
		instrument->map_note[n] = dumbfile_getc(f);
		instrument->map_sample[n] = dumbfile_getc(f);
	}

	if (dumbfile_error(f))
		return -1;

	if (it_read_envelope(&instrument->volume_envelope, f)) return -1;
	if (it_read_envelope(&instrument->pan_envelope, f)) return -1;
	if (it_read_envelope(&instrument->pitch_envelope, f)) return -1;

	return 0;
}



static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f)
{
	if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE)
		return -1;

	/* Skip DOS Filename. */
	dumbfile_skip(f, 13);

	sample->global_volume = dumbfile_getc(f);
	sample->flags = dumbfile_getc(f);
	sample->default_volume = dumbfile_getc(f);

	/* Skip Sample Name. */
	dumbfile_skip(f, 26);

	*convert = dumbfile_getc(f);
	sample->default_pan = dumbfile_getc(f);
	sample->length = dumbfile_igetl(f);
	sample->loop_start = dumbfile_igetl(f);
	sample->loop_end = dumbfile_igetl(f);
	sample->C5_speed = dumbfile_igetl(f);
	sample->sus_loop_start = dumbfile_igetl(f);
	sample->sus_loop_end = dumbfile_igetl(f);

#ifdef STEREO_SAMPLES_COUNT_AS_TWO
	if (sample->flags & IT_SAMPLE_STEREO) {
		sample->length >>= 1;
		sample->loop_start >>= 1;
		sample->loop_end >>= 1;
		sample->C5_speed >>= 1;
		sample->sus_loop_start >>= 1;
		sample->sus_loop_end >>= 1;
	}
#endif

	if (sample->flags & IT_SAMPLE_EXISTS) {
		if (sample->length <= 0)
			sample->flags &= ~IT_SAMPLE_EXISTS;
		else {
			if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
				sample->flags &= ~IT_SAMPLE_LOOP;
			else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
				sample->flags &= ~IT_SAMPLE_LOOP;

			if ((unsigned int)sample->sus_loop_end > (unsigned int)sample->length)
				sample->flags &= ~IT_SAMPLE_SUS_LOOP;
			else if ((unsigned int)sample->sus_loop_start >= (unsigned int)sample->sus_loop_end)
				sample->flags &= ~IT_SAMPLE_SUS_LOOP;

			/* We may be able to truncate the sample to save memory. */
			if (sample->flags & IT_SAMPLE_LOOP) {
				if ((sample->flags & IT_SAMPLE_SUS_LOOP) && sample->sus_loop_end >= sample->loop_end)
					sample->length = sample->sus_loop_end;
				else
					sample->length = sample->loop_end;
			}
		}
	}

	*offset = dumbfile_igetl(f);

	sample->vibrato_speed = dumbfile_getc(f);
	sample->vibrato_depth = dumbfile_getc(f);
	sample->vibrato_rate = dumbfile_getc(f);
	sample->vibrato_waveform = dumbfile_getc(f);

	return dumbfile_error(f);
}



static long it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f)
{
	long n;

	sample->left = malloc(sample->length * sizeof(*sample->left));
	if (!sample->left)
		return -1;

	if (sample->flags & IT_SAMPLE_STEREO) {
		sample->right = malloc(sample->length * sizeof(*sample->right));
		if (!sample->right)
			return -1;
	}

	if (sample->flags & 8) {
		/* If the sample is packed, then we must unpack it. */

		/** WARNING - unresolved business here... test with ModPlug? */

		if (sample->flags & IT_SAMPLE_STEREO)
                    return -1;
                
/*
//#ifndef STEREO_SAMPLES_COUNT_AS_TWO
		ASSERT(!(sample->flags & IT_SAMPLE_STEREO));
//#endif
*/
		if (sample->flags & IT_SAMPLE_16BIT)
			decompress16(f, sample->left, sample->right, sample->length, cmwt);
		else
			decompress8(f, sample->left, sample->right, sample->length, cmwt);
	} else if (sample->flags & IT_SAMPLE_STEREO) {
		if (sample->flags & IT_SAMPLE_16BIT) {
			if (convert & 2) {
				for (n = 0; n < sample->length; n++) {
					sample->left[n] = (int)(signed short)dumbfile_mgetw(f) << 8;
					sample->right[n] = (int)(signed short)dumbfile_mgetw(f) << 8;
				}
			} else {
				for (n = 0; n < sample->length; n++) {
					sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
					sample->right[n] = (int)(signed short)dumbfile_igetw(f) << 8;
				}
			}
		} else {
			for (n = 0; n < sample->length; n++) {
				sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
				sample->right[n] = (int)(signed char)dumbfile_getc(f) << 16;
			}
		}
	} else if (sample->flags & IT_SAMPLE_16BIT) {
		if (convert & 2)
			for (n = 0; n < sample->length; n++)
				sample->left[n] = (int)(signed short)dumbfile_mgetw(f) << 8;
		else
			for (n = 0; n < sample->length; n++)
				sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
	} else
		for (n = 0; n < sample->length; n++)
			sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;

	if (dumbfile_error(f))
		return -1;

	if (!(convert & 1)) {
		/* Convert to signed. */
		for (n = 0; n < sample->length; n++)
			sample->left[n] ^= 0xFF800000;

		if (sample->right)
			for (n = 0; n < sample->length; n++)
				sample->right[n] ^= 0xFF800000;
	}

	/* NOT SUPPORTED:
	 *
	 * convert &  4 - Samples stored as delta values
	 * convert & 16 - Samples stored as TX-Wave 12-bit values
	 * convert & 32 - Left/Right/All Stereo prompt
	 */

	return 0;
}



#define DETECT_DUPLICATE_CHANNELS
#ifdef DETECT_DUPLICATE_CHANNELS
#include <stdio.h>
#endif
static int it_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer)
{
	unsigned char cmask[DUMB_IT_N_CHANNELS];
	unsigned char cnote[DUMB_IT_N_CHANNELS];
	unsigned char cinstrument[DUMB_IT_N_CHANNELS];
	unsigned char cvolpan[DUMB_IT_N_CHANNELS];
	unsigned char ceffect[DUMB_IT_N_CHANNELS];
	unsigned char ceffectvalue[DUMB_IT_N_CHANNELS];
#ifdef DETECT_DUPLICATE_CHANNELS
	IT_ENTRY *dupentry[DUMB_IT_N_CHANNELS];
#endif

	int n_entries = 0;
	int buflen;
	int bufpos = 0;

	IT_ENTRY *entry;

	unsigned char channel;
	unsigned char mask;

	memset(cmask, 0, sizeof(cmask));
	memset(cnote, 0, sizeof(cnote));
	memset(cinstrument, 0, sizeof(cinstrument));
	memset(cvolpan, 0, sizeof(cvolpan));
	memset(ceffect, 0, sizeof(ceffect));
	memset(ceffectvalue, 0, sizeof(ceffectvalue));
#ifdef DETECT_DUPLICATE_CHANNELS
	{
		int i;
		for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL;
	}
#endif

	buflen = dumbfile_igetw(f);
	pattern->n_rows = dumbfile_igetw(f);

	/* Skip four unused bytes. */
	dumbfile_skip(f, 4);

	if (dumbfile_error(f))
		return -1;

	/* Read in the pattern data. */
	dumbfile_getnc((char *)buffer, buflen, f);

	if (dumbfile_error(f))
		return -1;

	/* Scan the pattern data, and work out how many entries we need room for. */
	while (bufpos < buflen) {
		unsigned char b = buffer[bufpos++];

		if (b == 0) {
			/* End of row */
			n_entries++;
			continue;
		}

		channel = (b - 1) & 63;

		if (b & 128)
			cmask[channel] = mask = buffer[bufpos++];
		else
			mask = cmask[channel];

		{
			static const unsigned char used[16] = {0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5};
			n_entries += (mask != 0);
			bufpos += used[mask & 15];
		}
	}

	pattern->n_entries = n_entries;

	pattern->entry = malloc(n_entries * sizeof(*pattern->entry));

	if (!pattern->entry)
		return -1;

	bufpos = 0;
	memset(cmask, 0, sizeof(cmask));

	entry = pattern->entry;

	while (bufpos < buflen) {
		unsigned char b = buffer[bufpos++];

		if (b == 0) {
			/* End of row */
			IT_SET_END_ROW(entry);
			entry++;
#ifdef DETECT_DUPLICATE_CHANNELS
			{
				int i;
				for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL;
			}
#endif
			continue;
		}

		channel = (b - 1) & 63;

		if (b & 128)
			cmask[channel] = mask = buffer[bufpos++];
		else
			mask = cmask[channel];

		if (mask) {
			entry->mask = (mask & 15) | (mask >> 4);
			entry->channel = channel;

			if (mask & IT_ENTRY_NOTE)
				cnote[channel] = entry->note = buffer[bufpos++];
			else if (mask & (IT_ENTRY_NOTE << 4))
				entry->note = cnote[channel];

			if (mask & IT_ENTRY_INSTRUMENT)
				cinstrument[channel] = entry->instrument = buffer[bufpos++];
			else if (mask & (IT_ENTRY_INSTRUMENT << 4))
				entry->instrument = cinstrument[channel];

			if (mask & IT_ENTRY_VOLPAN)
				cvolpan[channel] = entry->volpan = buffer[bufpos++];
			else if (mask & (IT_ENTRY_VOLPAN << 4))
				entry->volpan = cvolpan[channel];

			if (mask & IT_ENTRY_EFFECT) {
				ceffect[channel] = entry->effect = buffer[bufpos++];
				ceffectvalue[channel] = entry->effectvalue = buffer[bufpos++];
			} else {
				entry->effect = ceffect[channel];
				entry->effectvalue = ceffectvalue[channel];
			}

#if defined( DETECT_DUPLICATE_CHANNELS) && 0
			if (dupentry[channel]) {
				FILE *f = fopen("dupentry.txt", "a");
				if (!f) abort();
				fprintf(f, "Two events on channel %d:", channel);
				fprintf(f, "  Event #1:");
				if (dupentry[channel]->mask & IT_ENTRY_NOTE      ) fprintf(f, " %03d", dupentry[channel]->note      ); else fprintf(f, " ...");
				if (dupentry[channel]->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", dupentry[channel]->instrument); else fprintf(f, " ...");
				if (dupentry[channel]->mask & IT_ENTRY_VOLPAN    ) fprintf(f, " %03d", dupentry[channel]->volpan    ); else fprintf(f, " ...");
				if (dupentry[channel]->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + dupentry[channel]->effect, dupentry[channel]->effectvalue); else fprintf(f, " ...\n");
				fprintf(f, "  Event #2:");
				if (entry->mask & IT_ENTRY_NOTE      ) fprintf(f, " %03d", entry->note      ); else fprintf(f, " ...");
				if (entry->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", entry->instrument); else fprintf(f, " ...");
				if (entry->mask & IT_ENTRY_VOLPAN    ) fprintf(f, " %03d", entry->volpan    ); else fprintf(f, " ...");
				if (entry->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + entry->effect, entry->effectvalue); else fprintf(f, " ...\n");
				fclose(f);
			}
			dupentry[channel] = entry;
#endif

			entry++;
		}
	}

	ASSERT(entry == pattern->entry + n_entries);

	return 0;
}



/* Currently we assume the sample data are stored after the sample headers in
 * module files. This assumption may be unjustified; let me know if you have
 * trouble.
 */

#define IT_COMPONENT_INSTRUMENT 1
#define IT_COMPONENT_PATTERN    2
#define IT_COMPONENT_SAMPLE     3

typedef struct IT_COMPONENT
{
	unsigned char type;
	unsigned char n;
	long offset;
	short sampfirst; /* component[sampfirst] = first sample data after this */
	short sampnext; /* sampnext is used to create linked lists of sample data */
}
IT_COMPONENT;



static int it_component_compare(const void *e1, const void *e2)
{
	return ((const IT_COMPONENT *)e1)->offset -
	       ((const IT_COMPONENT *)e2)->offset;
}



static sigdata_t *it_load_sigdata(DUMBFILE *f)
{
	DUMB_IT_SIGDATA *sigdata;

	int cwt, cmwt;
	int special;

	IT_COMPONENT *component;
	int n_components = 0;

	unsigned char sample_convert[256];

	int n;

	unsigned char *buffer;

	if (dumbfile_mgetl(f) != IT_SIGNATURE)
		return NULL;

	sigdata = malloc(sizeof(*sigdata));

	if (!sigdata)
		return NULL;

	sigdata->order = NULL;
	sigdata->instrument = NULL;
	sigdata->sample = NULL;
	sigdata->pattern = NULL;
	sigdata->midi = NULL;
	sigdata->checkpoint = NULL;

	/* Skip song name and pattern row highlight info. */
	dumbfile_skip(f, 28);

	sigdata->n_orders = dumbfile_igetw(f);
	sigdata->n_instruments = dumbfile_igetw(f);
	sigdata->n_samples = dumbfile_igetw(f);
	sigdata->n_patterns = dumbfile_igetw(f);

	cwt = dumbfile_igetw(f);
	cmwt = dumbfile_igetw(f);

	sigdata->flags = dumbfile_igetw(f);
	special = dumbfile_igetw(f);

	sigdata->global_volume = dumbfile_getc(f);
	sigdata->mixing_volume = dumbfile_getc(f);
	sigdata->speed = dumbfile_getc(f);
	if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
	sigdata->tempo = dumbfile_getc(f);
	sigdata->pan_separation = dumbfile_getc(f); /** WARNING: use this */

	/* Skip Pitch Wheel Depth, Message Length, Message Offset and Reserved. */
	dumbfile_skip(f, 11);

	dumbfile_getnc((char *)sigdata->channel_pan, DUMB_IT_N_CHANNELS, f);
	dumbfile_getnc((char *)sigdata->channel_volume, DUMB_IT_N_CHANNELS, f);

	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) {
		_dumb_it_unload_sigdata(sigdata);
		return NULL;
	}

	sigdata->order = malloc(sigdata->n_orders);
	if (!sigdata->order) {
		_dumb_it_unload_sigdata(sigdata);
		return NULL;
	}

	if (sigdata->n_instruments) {
		sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
		if (!sigdata->instrument) {
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
		}
	}

	if (sigdata->n_samples) {
		sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
		if (!sigdata->sample) {
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
		}
		for (n = 0; n < sigdata->n_samples; n++)
			sigdata->sample[n].right = sigdata->sample[n].left = NULL;
	}

	if (sigdata->n_patterns) {
		sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
		if (!sigdata->pattern) {
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
		}
		for (n = 0; n < sigdata->n_patterns; n++)
			sigdata->pattern[n].entry = NULL;
	}

	dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f);
	sigdata->restart_position = 0;

	component = malloc(768 * sizeof(*component));
	if (!component) {
		_dumb_it_unload_sigdata(sigdata);
		return NULL;
	}

	for (n = 0; n < sigdata->n_instruments; n++) {
		component[n_components].type = IT_COMPONENT_INSTRUMENT;
		component[n_components].n = n;
		component[n_components].offset = dumbfile_igetl(f);
		component[n_components].sampfirst = -1;
		n_components++;
	}

	for (n = 0; n < sigdata->n_samples; n++) {
		component[n_components].type = IT_COMPONENT_SAMPLE;
		component[n_components].n = n;
		component[n_components].offset = dumbfile_igetl(f);
		component[n_components].sampfirst = -1;
		n_components++;
	}

	for (n = 0; n < sigdata->n_patterns; n++) {
		long offset = dumbfile_igetl(f);
		if (offset) {
			component[n_components].type = IT_COMPONENT_PATTERN;
			component[n_components].n = n;
			component[n_components].offset = offset;
			component[n_components].sampfirst = -1;
			n_components++;
		} else {
			/* Empty 64-row pattern */
			sigdata->pattern[n].n_rows = 64;
			sigdata->pattern[n].n_entries = 0;
		}
	}

	if (dumbfile_error(f)) {
		free(component);
		_dumb_it_unload_sigdata(sigdata);
		return NULL;
	}

	if (!(sigdata->flags & 128) != !(special & 8)) {
#if 0
		fprintf(stderr, "Flags   Bit 7 (\"Request embedded MIDI configuration\"): %s\n", sigdata->flags & 128 ? "=SET=" : "clear");
		fprintf(stderr, "Special Bit 3     (\"MIDI configuration embedded\")    : %s\n", special        &   8 ? "=SET=" : "clear");
		fprintf(stderr, "entheh would like to investigate this IT file.\n");
		fprintf(stderr, "Please contact him! entheh@users.sf.net\n");
#endif
	}

	if (special & 8) {
		/* MIDI configuration is embedded. */
		unsigned char mididata[32];
		int i;
		sigdata->midi = malloc(sizeof(*sigdata->midi));
		if (!sigdata->midi) {
			free(component);
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
			// Should we be happy with this outcome in some situations?
		}
		// What are we skipping?
		i = dumbfile_igetw(f);
		if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) {
			free(component);
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
		}
		/* Read embedded MIDI configuration */
		// What are the first 9 commands for?
		if (dumbfile_skip(f, 32*9)) {
			free(component);
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
		}
		for (i = 0; i < 16; i++) {
			unsigned char len = 0;
			int j, leftdigit = -1;
			if (dumbfile_getnc((char *)mididata, 32, f) < 32) {
				free(component);
				_dumb_it_unload_sigdata(sigdata);
				return NULL;
			}
			sigdata->midi->SFmacroz[i] = 0;
			for (j = 0; j < 32; j++) {
				if (leftdigit >= 0) {
					if (mididata[j] == 0) {
						sigdata->midi->SFmacro[i][len++] = leftdigit;
						break;
					} else if (mididata[j] == ' ')
						sigdata->midi->SFmacro[i][len++] = leftdigit;
					else if (mididata[j] >= '0' && mididata[j] <= '9')
						sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0');
					else if (mididata[j] >= 'A' && mididata[j] <= 'F')
						sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA);
					leftdigit = -1;
				} else if (mididata[j] == 0)
					break;
				else if (mididata[j] == 'z')
					sigdata->midi->SFmacroz[i] |= 1 << len++;
				else if (mididata[j] >= '0' && mididata[j] <= '9')
					leftdigit = mididata[j] - '0';
				else if (mididata[j] >= 'A' && mididata[j] <= 'F')
					leftdigit = mididata[j] - 'A' + 0xA;
			}
			sigdata->midi->SFmacrolen[i] = len;
		}
		for (i = 0; i < 128; i++) {
			unsigned char len = 0;
			int j, leftdigit = -1;
			dumbfile_getnc((char *)mididata, 32, f);
			for (j = 0; j < 32; j++) {
				if (leftdigit >= 0) {
					if (mididata[j] == 0) {
						sigdata->midi->Zmacro[i][len++] = leftdigit;
						break;
					} else if (mididata[j] == ' ')
						sigdata->midi->Zmacro[i][len++] = leftdigit;
					else if (mididata[j] >= '0' && mididata[j] <= '9')
						sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0');
					else if (mididata[j] >= 'A' && mididata[j] <= 'F')
						sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA);
					leftdigit = -1;
				} else if (mididata[j] == 0)
					break;
				else if (mididata[j] >= '0' && mididata[j] <= '9')
					leftdigit = mididata[j] - '0';
				else if (mididata[j] >= 'A' && mididata[j] <= 'F')
					leftdigit = mididata[j] - 'A' + 0xA;
			}
			sigdata->midi->Zmacrolen[i] = len;
		}
	}

	sigdata->flags &= IT_REAL_FLAGS;

	qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);

	buffer = malloc(65536);
	if (!buffer) {
		free(component);
		_dumb_it_unload_sigdata(sigdata);
		return NULL;
	}

	for (n = 0; n < n_components; n++) {
		long offset;
		int m;

		if (it_seek(f, component[n].offset)) {
			free(buffer);
			free(component);
			_dumb_it_unload_sigdata(sigdata);
			return NULL;
		}

		switch (component[n].type) {

			case IT_COMPONENT_INSTRUMENT:
				if (cmwt < 0x200)
					m = it_read_old_instrument(&sigdata->instrument[component[n].n], f);
				else
					m = it_read_instrument(&sigdata->instrument[component[n].n], f);

				if (m) {
					free(buffer);
					free(component);
					_dumb_it_unload_sigdata(sigdata);
					return NULL;
				}
				break;

			case IT_COMPONENT_PATTERN:
				if (it_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) {
					free(buffer);
					free(component);
					_dumb_it_unload_sigdata(sigdata);
					return NULL;
				}
				break;

			case IT_COMPONENT_SAMPLE:
				if (it_read_sample_header(&sigdata->sample[component[n].n], &sample_convert[component[n].n], &offset, f)) {
					free(buffer);
					free(component);
					_dumb_it_unload_sigdata(sigdata);
					return NULL;
				}

				if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
					short *sample;

					for (m = n + 1; m < n_components; m++)
						if (component[m].offset > offset)
							break;
					m--;

					sample = &component[m].sampfirst;

					while (*sample >= 0 && component[*sample].offset <= offset)
						sample = &component[*sample].sampnext;

					component[n].sampnext = *sample;
					*sample = n;

					component[n].offset = offset;
				}
		}

		m = component[n].sampfirst;

		while (m >= 0) {
			if (it_seek(f, component[m].offset)) {
				free(buffer);
				free(component);
				_dumb_it_unload_sigdata(sigdata);
				return NULL;
			}

			if (it_read_sample_data(cmwt, &sigdata->sample[component[m].n], sample_convert[component[m].n], f)) {
				free(buffer);
				free(component);
				_dumb_it_unload_sigdata(sigdata);
				return NULL;
			}

			m = component[m].sampnext;
		}
	}

	free(buffer);
	free(component);

	_dumb_it_fix_invalid_orders(sigdata);

	return sigdata;
}



DUH *dumb_read_it(DUMBFILE *f)
{
	sigdata_t *sigdata;
	long length;

	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;

	sigdata = it_load_sigdata(f);

	if (!sigdata)
		return NULL;

	length = _dumb_it_build_checkpoints(sigdata);

	return make_duh(length, 1, &descptr, &sigdata);
}

