/*  _______         ____    __         ___    ___
 * \    _  \       \    /  \  /       \   \  /   /       '   '  '
 *  |  | \  \       |  |    ||         |   \/   |         .      .
 *  |  |  |  |      |  |    ||         ||\  /|  |
 *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
 *  |  |  |  |      |  |    ||         ||    |  |         .      .
 *  |  |_/  /        \  \__//          ||    |  |
 * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
 *                                                      /  \
 *                                                     / .  \
 * reads3m.c - Code to read a ScreamTracker 3         / / \  \
 *             module from an open file.             | <  /   \_
 *                                                   |  \/ /\   /
 * By entheh.                                         \_  /  > /
 *                                                      | \ / /
 *                                                      |  ' /
 *                                                       \__/
 */

// IT_STEREO... :o
#include <stdlib.h>
#include <string.h>

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

//#define S3M_BROKEN_OVERLAPPED_SAMPLES

/** WARNING: this is duplicated in itread.c */
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;
}



static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, unsigned char *pack, int cwtv, DUMBFILE *f)
{
	unsigned char type;
	int flags;

	type = dumbfile_getc(f);

	dumbfile_getnc(sample->filename, 12, f);
	sample->filename[12] = 0;

	if (type > 1) {
		/** WARNING: no adlib support */
		dumbfile_skip(f, 3 + 12 + 1 + 1 + 2 + 2 + 2 + 12);
		dumbfile_getnc(sample->name, 28, f);
		sample->name[28] = 0;
		dumbfile_skip(f, 4);
		sample->flags &= ~IT_SAMPLE_EXISTS;
		return -1; // return error so that another plugin could pick that file up
	}

	*offset = dumbfile_getc(f) << 20;
	*offset += dumbfile_igetw(f) << 4;

	sample->length = dumbfile_igetl(f);
	sample->loop_start = dumbfile_igetl(f);
	sample->loop_end = dumbfile_igetl(f);

	sample->default_volume = dumbfile_getc(f);

	dumbfile_skip(f, 1);

	flags = dumbfile_getc(f);

	if (flags < 0 || (flags != 0 && flags != 4))
		/* Sample is packed apparently (or error reading from file). We don't
		 * know how to read packed samples.
		 */
		return -1;

	*pack = flags;

	flags = dumbfile_getc(f);

	sample->C5_speed = dumbfile_igetl(f) << 1;

	/* Skip four unused bytes and three internal variables. */
	dumbfile_skip(f, 4+2+2+4);

	dumbfile_getnc(sample->name, 28, f);
	sample->name[28] = 0;

	if (type == 0 || sample->length <= 0) {
		/* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */
		sample->flags &= ~IT_SAMPLE_EXISTS;
		return dumbfile_error(f);
	}

	if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S'))
		return -1;

	sample->global_volume = 64;

	sample->flags = IT_SAMPLE_EXISTS;
	if (flags & 1) sample->flags |= IT_SAMPLE_LOOP;

	/* The ST3 TECH.DOC is unclear on this, but IMAGO Orpheus is not. Piece of crap. */

	if (flags & 2) {
		sample->flags |= IT_SAMPLE_STEREO;

		if ((cwtv & 0xF000) == 0x2000) {
			sample->length >>= 1;
			sample->loop_start >>= 1;
			sample->loop_end >>= 1;
		}
	}

	if (flags & 4) {
		sample->flags |= IT_SAMPLE_16BIT;

		if ((cwtv & 0xF000) == 0x2000) {
			sample->length >>= 1;
			sample->loop_start >>= 1;
			sample->loop_end >>= 1;
		}
	}

	sample->default_pan = 0; // 0 = don't use, or 160 = centre?

	if (sample->flags & IT_SAMPLE_LOOP) {
		if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
			/*sample->flags &= ~IT_SAMPLE_LOOP;*/
			sample->loop_end = sample->length;
		else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
			sample->flags &= ~IT_SAMPLE_LOOP;
		else
			/* ScreamTracker seems not to save what comes after the loop end
			 * point, but rather to assume it is a duplicate of what comes at
			 * the loop start point. I am not completely sure of this though.
			 * It is easy to evade; simply truncate the sample.
			 */
			sample->length = sample->loop_end;
	}


	//Do we need to set all these?
	sample->vibrato_speed = 0;
	sample->vibrato_depth = 0;
	sample->vibrato_rate = 0;
	sample->vibrato_waveform = IT_VIBRATO_SINE;
	sample->finetune = 0;
	sample->max_resampling_quality = -1;

	return dumbfile_error(f);
}



static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, unsigned char pack, DUMBFILE *f)
{
	long n;

	long datasize = sample->length;
	if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1;

	sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
	if (!sample->data)
		return -1;

	if (pack == 4) {
		if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
			return -1;
	}
	else if (sample->flags & IT_SAMPLE_STEREO) {
		if (sample->flags & IT_SAMPLE_16BIT) {
			for (n = 0; n < datasize; n += 2)
				((short *)sample->data)[n] = dumbfile_igetw(f);
			for (n = 1; n < datasize; n += 2)
				((short *)sample->data)[n] = dumbfile_igetw(f);
		} else {
			for (n = 0; n < datasize; n += 2)
				((signed char *)sample->data)[n] = dumbfile_getc(f);
			for (n = 1; n < datasize; n += 2)
				((signed char *)sample->data)[n] = dumbfile_getc(f);
		}
	} else if (sample->flags & IT_SAMPLE_16BIT)
		for (n = 0; n < sample->length; n++)
			((short *)sample->data)[n] = dumbfile_igetw(f);
	else
		for (n = 0; n < sample->length; n++)
			((signed char *)sample->data)[n] = dumbfile_getc(f);

	if (dumbfile_error(f))
		return -1;

	if (ffi != 1) {
		/* Convert to signed. */
		if (sample->flags & IT_SAMPLE_16BIT)
			for (n = 0; n < datasize; n++)
				((short *)sample->data)[n] ^= 0x8000;
		else
			for (n = 0; n < datasize; n++)
				((signed char *)sample->data)[n] ^= 0x80;
	}

	return 0;
}



static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int maxlen)
{
	int length;
	int buflen = 0;
	int bufpos = 0;

	IT_ENTRY *entry;

	unsigned char channel;

	/* Haha, this is hilarious!
	 *
	 * Well, after some experimentation, it seems that different S3M writers
	 * define the format in different ways. The S3M docs say that the first
	 * two bytes hold the "length of [the] packed pattern", and the packed
	 * pattern data follow. Judging by the contents of ARMANI.S3M, packaged
	 * with ScreamTracker itself, the measure of length _includes_ the two
	 * bytes used to store the length; in other words, we should read
	 * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug
	 * Tracker, excludes these two bytes, so (length) more bytes must be
	 * read.
	 *
	 * Call me crazy, but I just find it insanely funny that the format was
	 * misunderstood in this way :D
	 *
	 * Now we can't just risk reading two extra bytes, because then we
	 * overshoot, and DUMBFILEs don't support backward seeking (for a good
	 * reason). Luckily, there is a way. We can read the data little by
	 * little, and stop when we have 64 rows in memory. Provided we protect
	 * against buffer overflow, this method should work with all sensibly
	 * written S3M files. If you find one for which it does not work, please
	 * let me know at entheh@users.sf.net so I can look at it.
	 */

	/* Discard the length. */
	/* read at most length bytes, in case of retarded crap */
	length = dumbfile_igetw(f);
	
	if (maxlen)
	{
		maxlen -= 2;
		if (length > maxlen) length = maxlen;
	}

	if (dumbfile_error(f) || !length)
		return -1;

	pattern->n_rows = 0;
	pattern->n_entries = 0;

	/* Read in the pattern data, little by little, and work out how many
	 * entries we need room for. Sorry, but this is just so funny...
	 */
	for (;;) {
		unsigned char b = buffer[buflen++] = dumbfile_getc(f);

#if 1
		static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
		channel = b & 31;
		b >>= 5;
		pattern->n_entries++;
		if (b) {
			if (buflen + used[b] >= 65536) return -1;
			if (buflen + used[b] <= length)
				dumbfile_getnc(buffer + buflen, used[b], f);
			else
				memset(buffer + buflen, 0, used[b]);
			buflen += used[b];
		} else {
			/* End of row */
			if (++pattern->n_rows == 64) break;
			if (buflen >= 65536) return -1;
		}
#else
		if (b == 0) {
			/* End of row */
			pattern->n_entries++;
			if (++pattern->n_rows == 64) break;
			if (buflen >= 65536) return -1;
		} else {
			static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
			channel = b & 31;
			b >>= 5;
			if (b) {
				pattern->n_entries++;
				if (buflen + used[b] >= 65536) return -1;
				dumbfile_getnc(buffer + buflen, used[b], f);
				buflen += used[b];
			}
		}
#endif

		/* We have ensured that buflen < 65536 at this point, so it is safe
		 * to iterate and read at least one more byte without checking.
		 * However, now would be a good time to check for errors reading from
		 * the file.
		 */

		if (dumbfile_error(f))
			return -1;

		/* Great. We ran out of data, but there should be data for more rows.
		 * Fill the rest with null data...
		 */
		if (buflen >= length && pattern->n_rows < 64)
		{
			while (pattern->n_rows < 64)
			{
				if (buflen >= 65536) return -1;
				buffer[buflen++] = 0;
				pattern->n_entries++;
				pattern->n_rows++;
			}
			break;
		}
	}

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

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

	entry = pattern->entry;

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

#if 1
		if (!(b & ~31))
#else
		if (b == 0)
#endif
		{
			/* End of row */
			IT_SET_END_ROW(entry);
			entry++;
			continue;
		}

		channel = b & 31;

		if (b & 224) {
			entry->mask = 0;
			entry->channel = channel;

			if (b & 32) {
				unsigned char n = buffer[bufpos++];
				if (n != 255) {
					if (n == 254)
						entry->note = IT_NOTE_CUT;
					else
						entry->note = (n >> 4) * 12 + (n & 15);
					entry->mask |= IT_ENTRY_NOTE;
				}

				entry->instrument = buffer[bufpos++];
				if (entry->instrument)
					entry->mask |= IT_ENTRY_INSTRUMENT;
			}

			if (b & 64) {
				entry->volpan = buffer[bufpos++];
				if (entry->volpan != 255)
					entry->mask |= IT_ENTRY_VOLPAN;
			}

			if (b & 128) {
				entry->effect = buffer[bufpos++];
				entry->effectvalue = buffer[bufpos++];
				// XXX woot
				if (entry->effect && entry->effect < IT_MIDI_MACRO /*!= 255*/) {
					entry->mask |= IT_ENTRY_EFFECT;
					switch (entry->effect) {
					case IT_BREAK_TO_ROW:
						entry->effectvalue -= (entry->effectvalue >> 4) * 6;
						break;

					case IT_SET_CHANNEL_VOLUME:
					case IT_CHANNEL_VOLUME_SLIDE:
					case IT_PANNING_SLIDE:
					case IT_GLOBAL_VOLUME_SLIDE:
					case IT_PANBRELLO:
					case IT_MIDI_MACRO:
						entry->mask &= ~IT_ENTRY_EFFECT;
						break;

					case IT_S:
						switch (entry->effectvalue >> 4) {
						case IT_S_SET_PANBRELLO_WAVEFORM:
						case IT_S_FINE_PATTERN_DELAY:
						case IT_S7:
						case IT_S_SET_SURROUND_SOUND:
						case IT_S_SET_MIDI_MACRO:
							entry->mask &= ~IT_ENTRY_EFFECT;
							break;
						}
						break;
					}
				}
				/** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */
			}

			entry++;
		}
	}

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

	return 0;
}



/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */
/* 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 S3M_COMPONENT_INSTRUMENT 1
#define S3M_COMPONENT_PATTERN    2
#define S3M_COMPONENT_SAMPLE     3

typedef struct S3M_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 */
}
S3M_COMPONENT;



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



static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv)
{
	DUMB_IT_SIGDATA *sigdata;

	int flags, ffi;
	int default_pan_present;

	int master_volume;

	unsigned char sample_pack[256];

	S3M_COMPONENT *component;
	int n_components = 0;

	int n;

	unsigned char *buffer;

	sigdata = malloc(sizeof(*sigdata));
	if (!sigdata) return NULL;

	dumbfile_getnc(sigdata->name, 28, f);
	sigdata->name[28] = 0;

	n = dumbfile_getc(f);

	if (n != 0x1A && n != 0) {
		free(sigdata);
		return NULL;
	}

	if (dumbfile_getc(f) != 16) {
		free(sigdata);
		return NULL;
	}

	dumbfile_skip(f, 2);

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

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

	if (dumbfile_error(f) || sigdata->n_orders <= 0 || 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_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].data = 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;
	}

	flags = dumbfile_igetw(f);

	*cwtv = dumbfile_igetw(f);

	if (*cwtv == 0x1300) {
		/** WARNING: volume slides on every frame */
	}

	ffi = dumbfile_igetw(f);

	/** WARNING: which ones? */
	sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;

	if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) {
		_dumb_it_unload_sigdata(sigdata);
		return NULL;
	}

	sigdata->global_volume = dumbfile_getc(f) << 1;
	if ( !sigdata->global_volume || sigdata->global_volume > 128 ) sigdata->global_volume = 128;
	sigdata->speed = dumbfile_getc(f);
	if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
	sigdata->tempo = dumbfile_getc(f);
	master_volume = dumbfile_getc(f); // 7 bits; +128 for stereo
	//what do we do with master_volume? it's not the same as mixing volume...
	sigdata->mixing_volume = 48;

	if (master_volume & 128) sigdata->flags |= IT_STEREO;

	/* Skip GUS Ultra Click Removal byte. */
	dumbfile_getc(f);

	default_pan_present = dumbfile_getc(f);

	dumbfile_skip(f, 8);

	/* Skip Special Custom Data Pointer. */
	/** WARNING: investigate this? */
	dumbfile_igetw(f);

	sigdata->n_pchannels = 0;
	/* Channel settings for 32 channels, 255=unused, +128=disabled */
	{
		int i;
		for (i = 0; i < 32; i++) {
			int c = dumbfile_getc(f);
			if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */
				if (sigdata->n_pchannels < i + 1) sigdata->n_pchannels = i + 1;
				sigdata->channel_volume[i] = 64;
				sigdata->channel_pan[i] = c & 8 ? 12 : 3;
				/** WARNING: ah, but it should be 7 for mono... */
			} else {
				/** WARNING: this could be improved if we support channel muting... */
				sigdata->channel_volume[i] = 0;
				sigdata->channel_pan[i] = 7;
			}
		}
	}

	/* Orders, byte each, length = sigdata->n_orders (should be even) */
	dumbfile_getnc(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_samples; n++) {
		component[n_components].type = S3M_COMPONENT_SAMPLE;
		component[n_components].n = n;
		component[n_components].offset = dumbfile_igetw(f) << 4;
		component[n_components].sampfirst = -1;
		n_components++;
	}

	for (n = 0; n < sigdata->n_patterns; n++) {
		long offset = dumbfile_igetw(f) << 4;
		if (offset) {
			component[n_components].type = S3M_COMPONENT_PATTERN;
			component[n_components].n = n;
			component[n_components].offset = offset;
			component[n_components].sampfirst = -1;
			n_components++;
		} else {
			/** WARNING: Empty 64-row pattern ... ? (this does happen!) */
			sigdata->pattern[n].n_rows = 64;
			sigdata->pattern[n].n_entries = 0;
		}
	}

	qsort(component, n_components, sizeof(S3M_COMPONENT), &s3m_component_compare);

	/* I found a really dumb S3M file that claimed to contain default pan
	 * data but didn't contain any. Programs would load it by reading part of
	 * the first instrument header, assuming the data to be default pan
	 * positions, and then rereading the instrument module. We cannot do this
	 * without obfuscating the file input model, so we insert an extra check
	 * here that we won't overrun the start of the first component.
	 */
	if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) {
		/* Channel default pan positions */
		int i;
		for (i = 0; i < 32; i++) {
			int c = dumbfile_getc(f);
			if (c & 32)
				sigdata->channel_pan[i] = c & 15;
		}
	}

	{
		int i;
		for (i = 0; i < 32; i++) {
			sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3;
			sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7;
		}
	}

	sigdata->pan_separation = 128;

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

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

	/* Voila, I must deal with a very dumb S3M myself. This file refers to the same file offset twice
	 * for two different patterns. Solution: Eliminate it.
	 */

	for (n = 0; n < n_components; n++) {
		if (component[n].type == S3M_COMPONENT_PATTERN) {
			int m;
			for (m = n + 1; m < n_components; m++) {
				if (component[m].type == S3M_COMPONENT_PATTERN) {
					if (component[n].offset == component[m].offset) {
						int o, pattern;
						pattern = component[m].n;
						n_components--;
						for (o = m; o < n_components; o++) {
							component[o] = component[o + 1];
						}
						for (o = 0; o < sigdata->n_orders; o++) {
							if (sigdata->order[o] == pattern) {
								sigdata->order[o] = component[n].n;
							}
						}
						sigdata->pattern[pattern].n_rows = 64;
						sigdata->pattern[pattern].n_entries = 0;
						m--;
					} else
						break;
				}
			}
		}
	}

	for (n = 0; n < n_components; n++) {
		long offset;
		int m;
#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
		int last;
#endif

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

		switch (component[n].type) {

			case S3M_COMPONENT_PATTERN:
				if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) {
					free(buffer);
					free(component);
					_dumb_it_unload_sigdata(sigdata);
					return NULL;
				}
				break;

			case S3M_COMPONENT_SAMPLE:
                {
                int err = it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f);
				if (err) {
					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;

#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
		last = -1;
#endif

		while (m >= 0) {
			// XXX
#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
			if ( last >= 0 ) {
				if ( dumbfile_pos( f ) > component[m].offset ) {
					IT_SAMPLE * s1 = &sigdata->sample[component[last].n];
					IT_SAMPLE * s2 = &sigdata->sample[component[m].n];
					if ( ( s1->flags | s2->flags ) & ( IT_SAMPLE_16BIT | IT_SAMPLE_STEREO ) ) {
						free(buffer);
						free(component);
						_dumb_it_unload_sigdata(sigdata);
						return NULL;
					}
					if ( component[m].offset >= component[last].offset &&
						component[m].offset + s2->length <= component[last].offset + s1->length ) {
						s2->left = malloc( s2->length );
						if ( ! s2->left ) {
							free(buffer);
							free(component);
							_dumb_it_unload_sigdata(sigdata);
							return NULL;
						}
						memcpy( s2->left, ( const char * ) s1->left + component[m].offset - component[last].offset, s2->length );
						last = -1;
					}
				}
			} else last = 0;

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

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

#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
				last = m;
			}
#endif

			m = component[m].sampnext;
		}
	}

	free(buffer);
	free(component);

	_dumb_it_fix_invalid_orders(sigdata);

	return sigdata;
}

static char hexdigit(int in)
{
	if (in < 10) return in + '0';
	else return in + 'A' - 10;
}

DUH *dumb_read_s3m_quick(DUMBFILE *f)
{
	sigdata_t *sigdata;
	int cwtv;

	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;

	sigdata = it_s3m_load_sigdata(f, &cwtv);

	if (!sigdata)
		return NULL;

	{
		char version[8];
		const char *tag[3][2];
		tag[0][0] = "TITLE";
		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
		tag[1][0] = "FORMAT";
		tag[1][1] = "S3M";
		tag[2][0] = "TRACKERVERSION";
		version[0] = hexdigit((cwtv >> 8) & 15);
		version[1] = '.';
		version[2] = hexdigit((cwtv >> 4) & 15);
		version[3] = hexdigit(cwtv & 15);
		version[4] = 0;
		tag[2][1] = (const char *) &version;
		return make_duh(-1, 3, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
	}
}
