/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * Fri, 26 Jun 1998 17:45:59 +1000  Andrew Leahy <alf@cit.nepean.uws.edu.au>
 * Finally got it working on the DEC Alpha running DEC UNIX! In the pattern
 * reading loop I found I was getting "0" for (p-patbuf) and "0" for
 * xph.datasize, the next if statement (where it tries to read the patbuf)
 * would then cause a seg_fault.
 *
 * Sun Sep 27 12:07:12 EST 1998  Claudio Matsuoka <claudio@pos.inf.ufpr.br>
 * Extended Module 1.02 stores data in a different order, we must handle
 * this accordingly. MAX_SAMP used as a workaround to check the number of
 * samples recognized by the player.
 */

#include "loader.h"
#include "xm.h"

static int xm_test(HIO_HANDLE *, char *, const int);
static int xm_load(struct module_data *, HIO_HANDLE *, const int);

const struct format_loader libxmp_loader_xm = {
	"Fast Tracker II",
	xm_test,
	xm_load
};

static int xm_test(HIO_HANDLE *f, char *t, const int start)
{
	char buf[20];

	if (hio_read(buf, 1, 17, f) < 17)	/* ID text */
		return -1;

	if (memcmp(buf, "Extended Module: ", 17))
		return -1;

	libxmp_read_title(f, t, 20);

	return 0;
}

static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HANDLE *f)
{
	const int headsize = version > 0x0102 ? 9 : 8;
	struct xmp_module *mod = &m->mod;
	struct xm_pattern_header xph;
	struct xmp_event *event;
	uint8 *patbuf, *pat, b;
	int j, r;
	int size;

	xph.length = hio_read32l(f);
	xph.packing = hio_read8(f);
	xph.rows = version > 0x0102 ? hio_read16l(f) : hio_read8(f) + 1;

	/* Sanity check */
	if (xph.rows > 256) {
		goto err;
	}

	xph.datasize = hio_read16l(f);
	hio_seek(f, xph.length - headsize, SEEK_CUR);
	if (hio_error(f)) {
		goto err;
	}

	r = xph.rows;
	if (r == 0) {
		r = 0x100;
	}

	if (libxmp_alloc_pattern_tracks(mod, num, r) < 0) {
		goto err;
	}

	if (xph.datasize == 0) {
		return 0;
	}

	size = xph.datasize;

	pat = patbuf = calloc(1, size);
	if (patbuf == NULL) {
		goto err;
	}

	hio_read(patbuf, 1, size, f);
	for (j = 0; j < (mod->chn * r); j++) {

		/*if ((pat - patbuf) >= xph.datasize)
		   break; */

		event = &EVENT(num, j % mod->chn, j / mod->chn);

		if (--size < 0) {
			goto err2;
		}

		if ((b = *pat++) & XM_EVENT_PACKING) {
			if (b & XM_EVENT_NOTE_FOLLOWS) {
				if (--size < 0)
					goto err2;
				event->note = *pat++;
			}
			if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
				if (--size < 0)
					goto err2;
				event->ins = *pat++;
			}
			if (b & XM_EVENT_VOLUME_FOLLOWS) {
				if (--size < 0)
					goto err2;
				event->vol = *pat++;
			}
			if (b & XM_EVENT_FXTYPE_FOLLOWS) {
				if (--size < 0)
					goto err2;
				event->fxt = *pat++;
			}
			if (b & XM_EVENT_FXPARM_FOLLOWS) {
				if (--size < 0)
					goto err2;
				event->fxp = *pat++;
			}
		} else {
			size -= 4;
			if (size < 0)
				goto err2;
			event->note = b;
			event->ins = *pat++;
			event->vol = *pat++;
			event->fxt = *pat++;
			event->fxp = *pat++;
		}

		/* Sanity check */
		switch (event->fxt) {
		case 18:
		case 19:
		case 22:
		case 23:
		case 24:
		case 26:
		case 28:
		case 30:
		case 31:
		case 32:
			event->fxt = 0;
		}
		if (event->fxt > 34) {
			event->fxt = 0;
		}

		if (event->note == 0x61) {
			/* See OpenMPT keyoff+instr.xm test case */
			if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
				event->note = XMP_KEY_OFF;
			} else {
				event->note =
				    event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
			}
		} else if (event->note > 0) {
			event->note += 12;
		}

		if (event->fxt == 0x0e) {
			if (MSN(event->fxp) == EX_FINETUNE) {
				unsigned char val = (LSN(event->fxp) - 8) & 0xf;
				event->fxp = (EX_FINETUNE << 4) | val;
			}
			switch (event->fxp) {
			case 0x43:
			case 0x73:
				event->fxp--;
				break;
			}
		}

		if (!event->vol) {
			continue;
		}

		/* Volume set */
		if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
			event->vol -= 0x0f;
			continue;
		}

		/* Volume column effects */
		switch (event->vol >> 4) {
		case 0x06:	/* Volume slide down */
			event->f2t = FX_VOLSLIDE_2;
			event->f2p = event->vol - 0x60;
			break;
		case 0x07:	/* Volume slide up */
			event->f2t = FX_VOLSLIDE_2;
			event->f2p = (event->vol - 0x70) << 4;
			break;
		case 0x08:	/* Fine volume slide down */
			event->f2t = FX_EXTENDED;
			event->f2p =
			    (EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
			break;
		case 0x09:	/* Fine volume slide up */
			event->f2t = FX_EXTENDED;
			event->f2p =
			    (EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
			break;
		case 0x0a:	/* Set vibrato speed */
			event->f2t = FX_VIBRATO;
			event->f2p = (event->vol - 0xa0) << 4;
			break;
		case 0x0b:	/* Vibrato */
			event->f2t = FX_VIBRATO;
			event->f2p = event->vol - 0xb0;
			break;
		case 0x0c:	/* Set panning */
			event->f2t = FX_SETPAN;
			event->f2p = (event->vol - 0xc0) << 4;
			break;
		case 0x0d:	/* Pan slide left */
			event->f2t = FX_PANSL_NOMEM;
			event->f2p = (event->vol - 0xd0) << 4;
			break;
		case 0x0e:	/* Pan slide right */
			event->f2t = FX_PANSL_NOMEM;
			event->f2p = event->vol - 0xe0;
			break;
		case 0x0f:	/* Tone portamento */
			event->f2t = FX_TONEPORTA;
			event->f2p = (event->vol - 0xf0) << 4;

			/* From OpenMPT TonePortamentoMemory.xm:
			 * "Another nice bug (...) is the combination of both
			 *  portamento commands (Mx and 3xx) in the same cell:
			 *  The 3xx parameter is ignored completely, and the Mx
			 *  parameter is doubled. (M2 3FF is the same as M4 000)
			 */
			if (event->fxt == FX_TONEPORTA
			    || event->fxt == FX_TONE_VSLIDE) {
				if (event->fxt == FX_TONEPORTA) {
					event->fxt = 0;
				} else {
					event->fxt = FX_VOLSLIDE;
				}
				event->fxp = 0;

				if (event->f2p < 0x80) {
					event->f2p <<= 1;
				} else {
					event->f2p = 0xff;
				}
			}

			/* From OpenMPT porta-offset.xm:
			 * "If there is a portamento command next to an offset
			 *  command, the offset command is ignored completely. In
			 *  particular, the offset parameter is not memorized."
			 */
			if (event->fxt == FX_OFFSET
			    && event->f2t == FX_TONEPORTA) {
				event->fxt = event->fxp = 0;
			}
			break;
		}
		event->vol = 0;
	}
	free(patbuf);

	return 0;

err2:
	free(patbuf);
err:
	return -1;
}

static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
{
	struct xmp_module *mod = &m->mod;
	int i, j;

	mod->pat++;
	if (libxmp_init_pattern(mod) < 0) {
		return -1;
	}

	D_(D_INFO "Stored patterns: %d", mod->pat - 1);

	for (i = 0; i < mod->pat - 1; i++) {
		if (load_xm_pattern(m, i, version, f) < 0) {
			goto err;
		}
	}

	/* Alloc one extra pattern */
	{
		int t = i * mod->chn;

		if (libxmp_alloc_pattern(mod, i) < 0) {
			goto err;
		}

		mod->xxp[i]->rows = 64;

		if (libxmp_alloc_track(mod, t, 64) < 0) {
			goto err;
		}

		for (j = 0; j < mod->chn; j++) {
			mod->xxp[i]->index[j] = t;
		}
	}

	return 0;

err:
	return -1;
}

/* Packed structures size */
#define XM_INST_HEADER_SIZE 33
#define XM_INST_SIZE 208

/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs
 * specify at most 16. See https://github.com/libxmp/libxmp/issues/168
 * for more details. */
#define XM_MAX_SAMPLES_PER_INST 32

static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
{
	struct xmp_module *mod = &m->mod;
	struct xm_instrument_header xih;
	struct xm_instrument xi;
	struct xm_sample_header xsh[XM_MAX_SAMPLES_PER_INST];
	int sample_num = 0;
	long total_sample_size;
	int i, j;
	uint8 buf[208];

	D_(D_INFO "Instruments: %d", mod->ins);

	/* ESTIMATED value! We don't know the actual value at this point */
	mod->smp = MAX_SAMPLES;

	if (libxmp_init_instrument(m) < 0) {
		return -1;
	}

	for (i = 0; i < mod->ins; i++) {
		long instr_pos = hio_tell(f);
		struct xmp_instrument *xxi = &mod->xxi[i];

		/* Modules converted with MOD2XM 1.0 always say we have 31
		 * instruments, but file may end abruptly before that. Also covers
		 * XMLiTE stripped modules and truncated files. This test will not
		 * work if file has trailing garbage.
		 */
		if (hio_read(buf, 33, 1, f) != 1) {
			D_(D_WARN "short read in instrument header data");
			break;
		}

		xih.size = readmem32l(buf);		/* Instrument size */
		memcpy(xih.name, buf + 4, 22);		/* Instrument name */
		xih.type = buf[26];			/* Instrument type (always 0) */
		xih.samples = readmem16l(buf + 27);	/* Number of samples */
		xih.sh_size = readmem32l(buf + 29);	/* Sample header size */

		/* Sanity check */
		if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) {
			D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size);
			return -1;
		}

		libxmp_instrument_name(mod, i, xih.name, 22);

		xxi->nsm = xih.samples;

		D_(D_INFO "instrument:%2X (%s) samples:%2d", i, xxi->name, xxi->nsm);

		if (xxi->nsm == 0) {
			/* Sample size should be in struct xm_instrument according to
			 * the official format description, but FT2 actually puts it in
			 * struct xm_instrument header. There's a tracker or converter
			 * that follow the specs, so we must handle both cases (see
			 * "Braintomb" by Jazztiz/ART).
			 */

			/* Umm, Cyke O'Path <cyker@heatwave.co.uk> sent me a couple of
			 * mods ("Breath of the Wind" and "Broken Dimension") that
			 * reserve the instrument data space after the instrument header
			 * even if the number of instruments is set to 0. In these modules
			 * the instrument header size is marked as 263. The following
			 * generalization should take care of both cases.
			 */

			if (hio_seek(f, (int)xih.size - XM_INST_HEADER_SIZE, SEEK_CUR) < 0) {
				return -1;
			}

			continue;
		}

		if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) {
			return -1;
		}

		if (xih.size < XM_INST_HEADER_SIZE) {
			return -1;
		}

		/* for BoobieSqueezer (see http://boobie.rotfl.at/)
		 * It works pretty much the same way as Impulse Tracker's sample
		 * only mode, where it will strip off the instrument data.
		 */
		if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
			memset(&xi, 0, sizeof(struct xm_instrument));
			hio_seek(f, xih.size - XM_INST_HEADER_SIZE, SEEK_CUR);
		} else {
			uint8 *b = buf;

			if (hio_read(buf, 208, 1, f) != 1) {
				D_(D_CRIT "short read in instrument data");
				return -1;
			}

			memcpy(xi.sample, b, 96);		/* Sample map */
			b += 96;

			for (j = 0; j < 24; j++) {
				xi.v_env[j] = readmem16l(b);	/* Points for volume envelope */
				b += 2;
			}

			for (j = 0; j < 24; j++) {
				xi.p_env[j] = readmem16l(b);	/* Points for pan envelope */
				b += 2;
			}

			xi.v_pts = *b++;		/* Number of volume points */
			xi.p_pts = *b++;		/* Number of pan points */
			xi.v_sus = *b++;		/* Volume sustain point */
			xi.v_start = *b++;		/* Volume loop start point */
			xi.v_end = *b++;		/* Volume loop end point */
			xi.p_sus = *b++;		/* Pan sustain point */
			xi.p_start = *b++;		/* Pan loop start point */
			xi.p_end = *b++;		/* Pan loop end point */
			xi.v_type = *b++;		/* Bit 0:On 1:Sustain 2:Loop */
			xi.p_type = *b++;		/* Bit 0:On 1:Sustain 2:Loop */
			xi.y_wave = *b++;		/* Vibrato waveform */
			xi.y_sweep = *b++;		/* Vibrato sweep */
			xi.y_depth = *b++;		/* Vibrato depth */
			xi.y_rate = *b++;		/* Vibrato rate */
			xi.v_fade = readmem16l(b);	/* Volume fadeout */

			/* Skip reserved space */
			if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + XM_INST_SIZE), SEEK_CUR) < 0) {
				return -1;
			}

			/* Envelope */
			xxi->rls = xi.v_fade << 1;
			xxi->aei.npt = xi.v_pts;
			xxi->aei.sus = xi.v_sus;
			xxi->aei.lps = xi.v_start;
			xxi->aei.lpe = xi.v_end;
			xxi->aei.flg = xi.v_type;
			xxi->pei.npt = xi.p_pts;
			xxi->pei.sus = xi.p_sus;
			xxi->pei.lps = xi.p_start;
			xxi->pei.lpe = xi.p_end;
			xxi->pei.flg = xi.p_type;

			if (xxi->aei.npt <= 0 || xxi->aei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) {
				xxi->aei.flg &= ~XMP_ENVELOPE_ON;
			} else {
				memcpy(xxi->aei.data, xi.v_env, xxi->aei.npt * 4);
			}

			if (xxi->pei.npt <= 0 || xxi->pei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) {
				xxi->pei.flg &= ~XMP_ENVELOPE_ON;
			} else {
				memcpy(xxi->pei.data, xi.p_env, xxi->pei.npt * 4);
			}

			for (j = 12; j < 108; j++) {
				xxi->map[j].ins = xi.sample[j - 12];
				if (xxi->map[j].ins >= xxi->nsm)
					xxi->map[j].ins = -1;
			}
		}

		/* Read subinstrument and sample parameters */

		for (j = 0; j < xxi->nsm; j++, sample_num++) {
			struct xmp_subinstrument *sub = &xxi->sub[j];
			struct xmp_sample *xxs;
			uint8 *b = buf;

			D_(D_INFO "  sample index:%d sample id:%d", j, sample_num);

			if (sample_num >= mod->smp) {
				if (libxmp_realloc_samples(m, mod->smp * 3 / 2) < 0)
					return -1;
			}
			xxs = &mod->xxs[sample_num];

			if (hio_read(buf, 40, 1, f) != 1) {
				D_(D_CRIT "short read in sample data");
				return -1;
			}

			xsh[j].length = readmem32l(b);	/* Sample length */
			b += 4;

			/* Sanity check */
			if (xsh[j].length > MAX_SAMPLE_SIZE) {
				D_(D_CRIT "sanity check: %d: bad sample size", j);
				return -1;
			}

			xsh[j].loop_start = readmem32l(b);	/* Sample loop start */
			b += 4;
			xsh[j].loop_length = readmem32l(b);	/* Sample loop length */
			b += 4;
			xsh[j].volume = *b++;	/* Volume */
			xsh[j].finetune = *b++;	/* Finetune (-128..+127) */
			xsh[j].type = *b++;	/* Flags */
			xsh[j].pan = *b++;	/* Panning (0-255) */
			xsh[j].relnote = *(int8 *) b++;	/* Relative note number */
			xsh[j].reserved = *b++;
			memcpy(xsh[j].name, b, 22);

			sub->vol = xsh[j].volume;
			sub->pan = xsh[j].pan;
			sub->xpo = xsh[j].relnote;
			sub->fin = xsh[j].finetune;
			sub->vwf = xi.y_wave;
			sub->vde = xi.y_depth << 2;
			sub->vra = xi.y_rate;
			sub->vsw = xi.y_sweep;
			sub->sid = sample_num;

			libxmp_copy_adjust(xxs->name, xsh[j].name, 22);

			xxs->len = xsh[j].length;
			xxs->lps = xsh[j].loop_start;
			xxs->lpe = xsh[j].loop_start + xsh[j].loop_length;

			xxs->flg = 0;
			if (xsh[j].type & XM_SAMPLE_16BIT) {
				xxs->flg |= XMP_SAMPLE_16BIT;
				xxs->len >>= 1;
				xxs->lps >>= 1;
				xxs->lpe >>= 1;
			}

			xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0;
			xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0;

			D_(D_INFO "  size:%06x loop start:%06x loop end:%06x %c V%02x F%+04d P%02x R%+03d %s",
			   mod->xxs[sub->sid].len,
			   mod->xxs[sub->sid].lps,
			   mod->xxs[sub->sid].lpe,
			   mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' :
			   mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
			   sub->vol, sub->fin, sub->pan, sub->xpo,
			   mod->xxs[sub->sid].flg & XMP_SAMPLE_16BIT ? " (16 bit)" : "");
		}

		/* Read actual sample data */

		total_sample_size = 0;
		for (j = 0; j < xxi->nsm; j++) {
			struct xmp_subinstrument *sub = &xxi->sub[j];
			int flags;

			flags = SAMPLE_FLAG_DIFF;
#ifndef LIBXMP_CORE_PLAYER
			if (xsh[j].reserved == 0xad) {
				flags = SAMPLE_FLAG_ADPCM;
			}
#endif

			if (version > 0x0103) {
			        D_(D_INFO "  read sample: index:%d sample id:%d", j, sub->sid);
				if (libxmp_load_sample(m, f, flags, &mod->xxs[sub->sid], NULL) < 0) {
					return -1;
				}
				if (flags & SAMPLE_FLAG_ADPCM) {
			                D_(D_INFO "  sample is adpcm");
					total_sample_size += 16 + ((xsh[j].length + 1) >> 1);
				} else {
					total_sample_size += xsh[j].length;
				}
			}
		}

		/* Reposition correctly in case of 16-bit sample having odd in-file length.
		 * See "Lead Lined for '99", reported by Dennis Mulleneers.
		 */
		if (hio_seek(f, instr_pos + xih.size + 40 * xih.samples + total_sample_size, SEEK_SET) < 0) {
			return -1;
		}
	}

	/* Final sample number adjustment */
	if (libxmp_realloc_samples(m, sample_num) < 0) {
		return -1;
	}

	return 0;
}

static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
	struct xmp_module *mod = &m->mod;
	int i, j;
	struct xm_file_header xfh;
	char tracker_name[21];
	int len;
	uint8 buf[80];

	LOAD_INIT();

	if (hio_read(buf, 80, 1, f) != 1) {
		D_(D_CRIT "error reading header");
		return -1;
	}

	memcpy(xfh.id, buf, 17);		/* ID text */
	memcpy(xfh.name, buf + 17, 20);		/* Module name */
	/* */					/* skip 0x1a */
	memcpy(xfh.tracker, buf + 38, 20);	/* Tracker name */
	xfh.version = readmem16l(buf + 58);	/* Version number, minor-major */
	xfh.headersz = readmem32l(buf + 60);	/* Header size */
	xfh.songlen = readmem16l(buf + 64);	/* Song length */
	xfh.restart = readmem16l(buf + 66);	/* Restart position */
	xfh.channels = readmem16l(buf + 68);	/* Number of channels */
	xfh.patterns = readmem16l(buf + 70);	/* Number of patterns */
	xfh.instruments = readmem16l(buf + 72);	/* Number of instruments */
	xfh.flags = readmem16l(buf + 74);	/* 0=Amiga freq table, 1=Linear */
	xfh.tempo = readmem16l(buf + 76);	/* Default tempo */
	xfh.bpm = readmem16l(buf + 78);		/* Default BPM */

	/* Sanity checks */
	if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) {
		D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns, xfh.instruments);
		return -1;
	}

	if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) {
		D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels);
		return -1;
	}

	if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 255) {
		if (memcmp("MED2XM", xfh.tracker, 6)) {
			D_(D_CRIT "Sanity check: %d %d", xfh.tempo, xfh.bpm);
			return -1;
		}
	}

	/* Honor header size -- needed by BoobieSqueezer XMs */
	len = xfh.headersz - 0x14;
	if (len < 0 || len > 256) {
		D_(D_CRIT "Sanity check: %d", len);
		return -1;
	}

	if (hio_read(xfh.order, len, 1, f) != 1) {	/* Pattern order table */
		D_(D_CRIT "error reading orders");
		return -1;
	}

	strncpy(mod->name, (char *)xfh.name, 20);

	mod->len = xfh.songlen;
	mod->chn = xfh.channels;
	mod->pat = xfh.patterns;
	mod->ins = xfh.instruments;
	mod->rst = xfh.restart;
	mod->spd = xfh.tempo;
	mod->bpm = xfh.bpm;
	mod->trk = mod->chn * mod->pat + 1;

	m->c4rate = C4_NTSC_RATE;
	m->period_type = xfh.flags & XM_LINEAR_PERIOD_MODE ?  PERIOD_LINEAR : PERIOD_AMIGA;

	memcpy(mod->xxo, xfh.order, mod->len);
	/*tracker_name[20] = 0;*/
	snprintf(tracker_name, 21, "%-20.20s", xfh.tracker);
	for (i = 20; i >= 0; i--) {
		if (tracker_name[i] == 0x20)
			tracker_name[i] = 0;
		if (tracker_name[i])
			break;
	}

	/* OpenMPT accurately emulates weird FT2 bugs */
	if (!strncmp(tracker_name, "FastTracker v2.00", 17) ||
	    !strncmp(tracker_name, "OpenMPT ", 8)) {
		m->quirk |= QUIRK_FT2BUGS;
	}
#ifndef LIBXMP_CORE_PLAYER
	if (xfh.headersz == 0x0113) {
		strcpy(tracker_name, "unknown tracker");
		m->quirk &= ~QUIRK_FT2BUGS;
	} else if (*tracker_name == 0) {
		strcpy(tracker_name, "Digitrakker");	/* best guess */
		m->quirk &= ~QUIRK_FT2BUGS;
	}

	/* See MMD1 loader for explanation */
	if (!strncmp(tracker_name, "MED2XM by J.Pynnone", 19)) {
		if (mod->bpm <= 10) {
			mod->bpm = 125 * (0x35 - mod->bpm * 2) / 33;
		}
		m->quirk &= ~QUIRK_FT2BUGS;
	}

	if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
		strcpy(tracker_name, "old ModPlug Tracker");
		m->quirk &= ~QUIRK_FT2BUGS;
	}

	libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff);
#else
	libxmp_set_type(m, tracker_name);
#endif

	MODULE_INFO();

	/* Honor header size */
	if (hio_seek(f, start + xfh.headersz + 60, SEEK_SET) < 0) {
		return -1;
	}

	/* XM 1.02/1.03 has a different patterns and instruments order */
	if (xfh.version <= 0x0103) {
		if (load_instruments(m, xfh.version, f) < 0) {
			return -1;
		}
		if (load_patterns(m, xfh.version, f) < 0) {
			return -1;
		}
	} else {
		if (load_patterns(m, xfh.version, f) < 0) {
			return -1;
		}
		if (load_instruments(m, xfh.version, f) < 0) {
			return -1;
		}
	}

	D_(D_INFO "Stored samples: %d", mod->smp);

	/* XM 1.02 stores all samples after the patterns */
	if (xfh.version <= 0x0103) {
		for (i = 0; i < mod->ins; i++) {
			for (j = 0; j < mod->xxi[i].nsm; j++) {
				int sid = mod->xxi[i].sub[j].sid;
				if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, &mod->xxs[sid], NULL) < 0) {
					return -1;
				}
			}
		}
	}

	for (i = 0; i < mod->chn; i++) {
		mod->xxc[i].pan = 0x80;
	}

	m->quirk |= QUIRKS_FT2;
	m->read_event_type = READ_EVENT_FT2;

	return 0;
}
