code:
/* 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.
 */

/* S3M packed pattern macros */
#define S3M_EOR		0	/* End of row */
#define S3M_CH_MASK	0x1f	/* Channel */
#define S3M_NI_FOLLOW	0x20	/* Note and instrument follow */
#define S3M_VOL_FOLLOWS	0x40	/* Volume follows */
#define S3M_FX_FOLLOWS	0x80	/* Effect and parameter follow */

/* S3M channel info macros */
#define S3M_CH_ON	0x80	/* Psi says it's bit 8, I'll assume bit 7 */
#define S3M_CH_OFF	0xff
#define S3M_CH_PAN	0x7f	/* Left/Right */

/* S3M channel pan macros */
#define S3M_PAN_SET	0x20
#define S3M_PAN_MASK	0x0f

/* S3M flags */
#define S3M_ST2_VIB	0x01	/* Not recognized */
#define S3M_ST2_TEMPO	0x02	/* Not recognized */
#define S3M_AMIGA_SLIDE	0x04	/* Not recognized */
#define S3M_VOL_OPT	0x08	/* Not recognized */
#define S3M_AMIGA_RANGE	0x10
#define S3M_SB_FILTER	0x20	/* Not recognized */
#define S3M_ST300_VOLS	0x40
#define S3M_CUSTOM_DATA	0x80	/* Not recognized */

/* S3M Adlib instrument types */
#define S3M_INST_SAMPLE	0x01
#define S3M_INST_AMEL	0x02
#define S3M_INST_ABD	0x03
#define S3M_INST_ASNARE	0x04
#define S3M_INST_ATOM	0x05
#define S3M_INST_ACYM	0x06
#define S3M_INST_AHIHAT	0x07

struct s3m_file_header {
	uint8 name[28];		/* Song name */
	uint8 doseof;		/* 0x1a */
	uint8 type;		/* File type */
	uint8 rsvd1[2];		/* Reserved */
	uint16 ordnum;		/* Number of orders (must be even) */
	uint16 insnum;		/* Number of instruments */
	uint16 patnum;		/* Number of patterns */
	uint16 flags;		/* Flags */
	uint16 version;		/* Tracker ID and version */
	uint16 ffi;		/* File format information */
	uint32 magic;		/* 'SCRM' */
	uint8 gv;		/* Global volume */
	uint8 is;		/* Initial speed */
	uint8 it;		/* Initial tempo */
	uint8 mv;		/* Master volume */
	uint8 uc;		/* Ultra click removal */
	uint8 dp;		/* Default pan positions if 0xfc */
	uint8 rsvd2[8];		/* Reserved */
	uint16 special;		/* Ptr to special custom data */
	uint8 chset[32];	/* Channel settings */
};

struct s3m_instrument_header {
	uint8 dosname[13];	/* DOS file name */
	uint16 memseg;		/* Pointer to sample data */
	uint32 length;		/* Length */
	uint32 loopbeg;		/* Loop begin */
	uint32 loopend;		/* Loop end */
	uint8 vol;		/* Volume */
	uint8 rsvd1;		/* Reserved */
	uint8 pack;		/* Packing type (not used) */
	uint8 flags;		/* Loop/stereo/16bit samples flags */
	uint16 c2spd;		/* C 4 speed */
	uint16 rsvd2;		/* Reserved */
	uint8 rsvd3[4];		/* Reserved */
	uint16 int_gp;		/* Internal - GUS pointer */
	uint16 int_512;		/* Internal - SB pointer */
	uint32 int_last;	/* Internal - SB index */
	uint8 name[28];		/* Instrument name */
	uint32 magic;		/* 'SCRS' */
};

#ifndef LIBXMP_CORE_PLAYER
struct s3m_adlib_header {
	uint8 dosname[12];	/* DOS file name */
	uint8 rsvd1[3];		/* 0x00 0x00 0x00 */
	uint8 reg[12];		/* Adlib registers */
	uint8 vol;
	uint8 dsk;
	uint8 rsvd2[2];
	uint16 c2spd;		/* C 4 speed */
	uint16 rsvd3;		/* Reserved */
	uint8 rsvd4[12];	/* Reserved */
	uint8 name[28];		/* Instrument name */
	uint32 magic;		/* 'SCRI' */
};
#endif


code:
/* 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.
 */

/*
 * Tue, 30 Jun 1998 20:23:11 +0200
 * Reported by John v/d Kamp <blade_@dds.nl>:
 * I have this song from Purple Motion called wcharts.s3m, the global
 * volume was set to 0, creating a devide by 0 error in xmp. There should
 * be an extra test if it's 0 or not.
 *
 * Claudio's fix: global volume ignored
 */

/*
 * Sat, 29 Aug 1998 18:50:43 -0500 (CDT)
 * Reported by Joel Jordan <scriber@usa.net>:
 * S3M files support tempos outside the ranges defined by xmp (that is,
 * the MOD/XM tempo ranges).  S3M's can have tempos from 0 to 255 and speeds
 * from 0 to 255 as well, since these are handled with separate effects
 * unlike the MOD format.  This becomes an issue in such songs as Skaven's
 * "Catch that Goblin", which uses speeds above 0x1f.
 *
 * Claudio's fix: FX_S3M_SPEED added. S3M supports speeds from 0 to 255 and
 * tempos from 32 to 255 (S3M speed == xmp tempo, S3M tempo == xmp BPM).
 */

/* Wed, 21 Oct 1998 15:03:44 -0500  Geoff Reedy <vader21@imsa.edu>
 * It appears that xmp has issues loading/playing a specific instrument
 * used in LUCCA.S3M.
 * (Fixed by Hipolito in xmp-2.0.0dev34)
 */

/*
 * From http://code.pui.ch/2007/02/18/turn-demoscene-modules-into-mp3s/
 * The only flaw I noticed [in xmp] is a problem with portamento in Purple
 * Motion's second reality soundtrack (1:06-1:17)
 *
 * Claudio's note: that's a dissonant beating between channels 6 and 7
 * starting at pos12, caused by pitchbending effect F25.
 */

#include "loader.h"
#include "s3m.h"
#include "period.h"

#define MAGIC_SCRM	MAGIC4('S','C','R','M')
#define MAGIC_SCRI	MAGIC4('S','C','R','I')
#define MAGIC_SCRS	MAGIC4('S','C','R','S')

static int s3m_test(HIO_HANDLE *, char *, const int);
static int s3m_load(struct module_data *, HIO_HANDLE *, const int);

extern const struct format_loader libxmp_loader_s3m;
const struct format_loader libxmp_loader_s3m = {
	"Scream Tracker 3",
	s3m_test,
	s3m_load
};

static int s3m_test(HIO_HANDLE *f, char *t, const int start)
{
	hio_seek(f, start + 44, SEEK_SET);
	if (hio_read32b(f) != MAGIC_SCRM)
		return -1;

	hio_seek(f, start + 29, SEEK_SET);
	if (hio_read8(f) != 0x10)
		return -1;

	hio_seek(f, start + 0, SEEK_SET);
	libxmp_read_title(f, t, 28);

	return 0;
}

#define NONE		0xff
#define FX_S3M_EXTENDED	0xfe

/* Effect conversion table */
static const uint8 fx[27] = {
	NONE,
	FX_S3M_SPEED,		/* Axx  Set speed to xx (the default is 06) */
	FX_JUMP,		/* Bxx  Jump to order xx (hexadecimal) */
	FX_BREAK,		/* Cxx  Break pattern to row xx (decimal) */
	FX_VOLSLIDE,		/* Dxy  Volume slide down by y/up by x */
	FX_PORTA_DN,		/* Exx  Slide down by xx */
	FX_PORTA_UP,		/* Fxx  Slide up by xx */
	FX_TONEPORTA,		/* Gxx  Tone portamento with speed xx */
	FX_VIBRATO,		/* Hxy  Vibrato with speed x and depth y */
	FX_TREMOR,		/* Ixy  Tremor with ontime x and offtime y */
	FX_S3M_ARPEGGIO,	/* Jxy  Arpeggio with halfnote additions */
	FX_VIBRA_VSLIDE,	/* Kxy  Dual command: H00 and Dxy */
	FX_TONE_VSLIDE,		/* Lxy  Dual command: G00 and Dxy */
	NONE,
	NONE,
	FX_OFFSET,		/* Oxy  Set sample offset */
	NONE,
	FX_MULTI_RETRIG,	/* Qxy  Retrig (+volumeslide) note */
	FX_TREMOLO,		/* Rxy  Tremolo with speed x and depth y */
	FX_S3M_EXTENDED,	/* Sxx  (misc effects) */
	FX_S3M_BPM,		/* Txx  Tempo = xx (hex) */
	FX_FINE_VIBRATO,	/* Uxx  Fine vibrato */
	FX_GLOBALVOL,		/* Vxx  Set global volume */
	NONE,
	FX_SETPAN,		/* Xxx  Set pan */
	NONE,
	NONE
};

/* Effect translation */
static void xlat_fx(int c, struct xmp_event *e)
{
	uint8 h = MSN(e->fxp), l = LSN(e->fxp);

	if (e->fxt >= ARRAY_SIZE(fx)) {
		D_(D_WARN "invalid effect %02x", e->fxt);
		e->fxt = e->fxp = 0;
		return;
	}

	switch (e->fxt = fx[e->fxt]) {
	case FX_S3M_BPM:
		if (e->fxp < 0x20) {
			e->fxp = e->fxt = 0;
		}
		break;
	case FX_S3M_EXTENDED:	/* Extended effects */
		e->fxt = FX_EXTENDED;
		switch (h) {
		case 0x1:	/* Glissando */
			e->fxp = LSN(e->fxp) | (EX_GLISS << 4);
			break;
		case 0x2:	/* Finetune */
			e->fxp =
			    ((LSN(e->fxp) - 8) & 0x0f) | (EX_FINETUNE << 4);
			break;
		case 0x3:	/* Vibrato wave */
			e->fxp = LSN(e->fxp) | (EX_VIBRATO_WF << 4);
			break;
		case 0x4:	/* Tremolo wave */
			e->fxp = LSN(e->fxp) | (EX_TREMOLO_WF << 4);
			break;
		case 0x5:
		case 0x6:
		case 0x7:
		case 0x9:
		case 0xa:	/* Ignore */
			e->fxt = e->fxp = 0;
			break;
		case 0x8:	/* Set pan */
			e->fxt = FX_SETPAN;
			e->fxp = l << 4;
			break;
		case 0xb:	/* Pattern loop */
			e->fxp = LSN(e->fxp) | (EX_PATTERN_LOOP << 4);
			break;
		case 0xc:
			if (!l)
				e->fxt = e->fxp = 0;
		}
		break;
	case FX_SETPAN:
		/* Saga Musix says: "The X effect in S3M files is not
		 * exclusive to IT and clones. You will find tons of S3Ms made
		 * with ST3 itself using this effect (and relying on an
		 * external player being used). X in S3M also behaves
		 * differently than in IT, which your code does not seem to
		 * handle: X00 - X80 is left... right, XA4 is surround (like
		 * S91 in IT), other values are not supposed to do anything.
		 */
		if (e->fxp == 0xa4) {
			// surround
			e->fxt = FX_SURROUND;
			e->fxp = 1;
		} else {
			int pan = ((int)e->fxp) << 1;
			if (pan > 0xff) {
				pan = 0xff;
			}
			e->fxp = pan;
		}
		break;
	case NONE:		/* No effect */
		e->fxt = e->fxp = 0;
		break;
	}
}

static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
	struct xmp_module *mod = &m->mod;
	int c, r, i;
	struct xmp_event *event = 0, dummy;
	struct s3m_file_header sfh;
	struct s3m_instrument_header sih;
#ifndef LIBXMP_CORE_PLAYER
	struct s3m_adlib_header sah;
	char tracker_name[40];
#endif
	int pat_len;
	uint8 n, b;
	uint16 *pp_ins;			/* Parapointers to instruments */
	uint16 *pp_pat;			/* Parapointers to patterns */
	int ret;
	uint8 buf[96]

	LOAD_INIT();

	if (hio_read(buf, 1, 96, f) != 96) {
		goto err;
	}

	memcpy(sfh.name, buf, 28);		/* Song name */
	sfh.type = buf[30];			/* File type */
	sfh.ordnum = readmem16l(buf + 32);	/* Number of orders (must be even) */
	sfh.insnum = readmem16l(buf + 34);	/* Number of instruments */
	sfh.patnum = readmem16l(buf + 36);	/* Number of patterns */
	sfh.flags = readmem16l(buf + 38);	/* Flags */
	sfh.version = readmem16l(buf + 40);	/* Tracker ID and version */
	sfh.ffi = readmem16l(buf + 42);		/* File format information */

	/* Sanity check */
	if (sfh.ffi != 1 && sfh.ffi != 2) {
		goto err;
	}
	if (sfh.ordnum > 255 || sfh.insnum > 255 || sfh.patnum > 255) {
		goto err;
	}

	sfh.magic = readmem32b(buf + 44);	/* 'SCRM' */
	sfh.gv = buf[48];			/* Global volume */
	sfh.is = buf[49];			/* Initial speed */
	sfh.it = buf[50];			/* Initial tempo */
	sfh.mv = buf[51];			/* Master volume */
	sfh.uc = buf[52];			/* Ultra click removal */
	sfh.dp = buf[53];			/* Default pan positions if 0xfc */
	memcpy(sfh.rsvd2, buf + 54, 8);		/* Reserved */
	sfh.special = readmem16l(buf + 62);	/* Ptr to special custom data */
	memcpy(sfh.chset, buf + 64, 32);	/* Channel settings */

	if (sfh.magic != MAGIC_SCRM) {
		goto err;
	}

	libxmp_copy_adjust(mod->name, sfh.name, 28);

	pp_ins = (uint16 *)calloc(2, sfh.insnum);
	if (pp_ins == NULL) {
		goto err;
	}

	pp_pat = (uint16 *)calloc(2, sfh.patnum);
	if (pp_pat == NULL) {
		goto err2;
	}

	if (sfh.flags & S3M_AMIGA_RANGE) {
		m->period_type = PERIOD_MODRNG;
	}
	if (sfh.flags & S3M_ST300_VOLS) {
		m->quirk |= QUIRK_VSALL;
	}
	/* m->volbase = 4096 / sfh.gv; */
	mod->spd = sfh.is;
	mod->bpm = sfh.it;
	mod->chn = 0;

	for (i = 0; i < 32; i++) {
		if (sfh.chset[i] == S3M_CH_OFF)
			continue;

		mod->chn = i + 1;

		if (sfh.mv & 0x80) {	/* stereo */
			int x = sfh.chset[i] & S3M_CH_PAN;
			mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0;
		} else {
			mod->xxc[i].pan = 0x80;
		}
	}

	if (sfh.ordnum <= XMP_MAX_MOD_LENGTH) {
		mod->len = sfh.ordnum;
		if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) {
			goto err3;
		}
	} else {
		mod->len = XMP_MAX_MOD_LENGTH;
		if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) {
			goto err3;
		}
		if (hio_seek(f, sfh.ordnum - XMP_MAX_MOD_LENGTH, SEEK_CUR) < 0) {
			goto err3;
		}
	}

	/* Don't trust sfh.patnum */
	mod->pat = -1;
	for (i = 0; i < mod->len; ++i) {
		if (mod->xxo[i] < 0xfe && mod->xxo[i] > mod->pat) {
			mod->pat = mod->xxo[i];
		}
	}
	mod->pat++;
	if (mod->pat > sfh.patnum) {
		mod->pat = sfh.patnum;
	}
	if (mod->pat == 0) {
		goto err3;
	}

	mod->trk = mod->pat * mod->chn;
	/* Load and convert header */
	mod->ins = sfh.insnum;
	mod->smp = mod->ins;

	for (i = 0; i < sfh.insnum; i++) {
		pp_ins[i] = hio_read16l(f);
	}

	for (i = 0; i < sfh.patnum; i++) {
		pp_pat[i] = hio_read16l(f);
	}

	/* Default pan positions */

	for (i = 0, sfh.dp -= 0xfc; !sfh.dp /* && n */  && (i < 32); i++) {
		uint8 x = hio_read8(f);
		if (x & S3M_PAN_SET) {
			mod->xxc[i].pan = (x << 4) & 0xff;
		} else {
			mod->xxc[i].pan =
			    sfh.mv % 0x80 ? 0x30 + 0xa0 * (i & 1) : 0x80;
		}
	}

	m->c4rate = C4_NTSC_RATE;

	if (sfh.version == 0x1300) {
		m->quirk |= QUIRK_VSALL;
	}

#ifndef LIBXMP_CORE_PLAYER
	switch (sfh.version >> 12) {
	case 1:
		snprintf(tracker_name, 40, "Scream Tracker %d.%02x",
			 (sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
		m->quirk |= QUIRK_ST3BUGS;
		break;
	case 2:
		snprintf(tracker_name, 40, "Imago Orpheus %d.%02x",
			 (sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
		break;
	case 3:
		if (sfh.version == 0x3216) {
			strcpy(tracker_name, "Impulse Tracker 2.14v3");
		} else if (sfh.version == 0x3217) {
			strcpy(tracker_name, "Impulse Tracker 2.14v5");
		} else {
			snprintf(tracker_name, 40, "Impulse Tracker %d.%02x",
				 (sfh.version & 0x0f00) >> 8,
				 sfh.version & 0xff);
		}
		break;
	case 5:
		snprintf(tracker_name, 40, "OpenMPT %d.%02x",
			 (sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
		m->quirk |= QUIRK_ST3BUGS;
		break;
	case 4:
		if (sfh.version != 0x4100) {
			libxmp_schism_tracker_string(tracker_name, 40,
				(sfh.version & 0x0fff), sfh.rsvd2[0] | (sfh.rsvd2[1] << 8));
			break;
		}
		/* fall through */
	case 6:
		snprintf(tracker_name, 40, "BeRoTracker %d.%02x",
			 (sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
		break;
	default:
		snprintf(tracker_name, 40, "unknown (%04x)", sfh.version);
	}

	libxmp_set_type(m, "%s S3M", tracker_name);
#else
	libxmp_set_type(m, "Scream Tracker 3");
	m->quirk |= QUIRK_ST3BUGS;
#endif

	MODULE_INFO();

	if (libxmp_init_pattern(mod) < 0)
		goto err3;

	/* Read patterns */

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

	for (i = 0; i < mod->pat; i++) {
		if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
			goto err3;

		if (pp_pat[i] == 0)
			continue;

		hio_seek(f, start + pp_pat[i] * 16, SEEK_SET);
		r = 0;
		pat_len = hio_read16l(f) - 2;

		while (pat_len >= 0 && r < mod->xxp[i]->rows) {
			b = hio_read8(f);

			if (hio_error(f)) {
				goto err3;
			}

			if (b == S3M_EOR) {
				r++;
				continue;
			}

			c = b & S3M_CH_MASK;
			event = c >= mod->chn ? &dummy : &EVENT(i, c, r);

			if (b & S3M_NI_FOLLOW) {
				switch (n = hio_read8(f)) {
				case 255:
					n = 0;
					break;	/* Empty note */
				case 254:
					n = XMP_KEY_OFF;
					break;	/* Key off */
				default:
					n = 13 + 12 * MSN(n) + LSN(n);
				}
				event->note = n;
				event->ins = hio_read8(f);
				pat_len -= 2;
			}

			if (b & S3M_VOL_FOLLOWS) {
				event->vol = hio_read8(f) + 1;
				pat_len--;
			}

			if (b & S3M_FX_FOLLOWS) {
				event->fxt = hio_read8(f);
				event->fxp = hio_read8(f);
				xlat_fx(c, event);

				pat_len -= 2;
			}
		}
	}

	D_(D_INFO "Stereo enabled: %s", sfh.mv & 0x80 ? "yes" : "no");
	D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");

	if (libxmp_init_instrument(m) < 0)
		goto err3;

	/* Read and convert instruments and samples */

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

	for (i = 0; i < mod->ins; i++) {
		struct xmp_instrument *xxi = &mod->xxi[i];
		struct xmp_sample *xxs = &mod->xxs[i];
		struct xmp_subinstrument *sub;
		int load_sample_flags;

		xxi->sub = (struct xmp_subinstrument *)calloc(sizeof(struct xmp_subinstrument), 1);
		if (xxi->sub == NULL) {
			goto err3;
		}

		sub = &xxi->sub[0];

		hio_seek(f, start + pp_ins[i] * 16, SEEK_SET);
		sub->pan = 0x80;
		sub->sid = i;

		if (hio_read(buf, 1, 80, f) != 80) {
			goto err3;
		}

		if (buf[0] >= 2) {
#ifndef LIBXMP_CORE_PLAYER
			/* OPL2 FM instrument */

			memcpy(sah.dosname, buf + 1, 12);	/* DOS file name */
			memcpy(sah.reg, buf + 16, 12);		/* Adlib registers */
			sah.vol = buf[28];
			sah.dsk = buf[29];
			sah.c2spd = readmem16l(buf + 32);	/* C4 speed */
			memcpy(sah.name, buf + 48, 28);		/* Instrument name */
			sah.magic = readmem32b(buf + 76);		/* 'SCRI' */

			if (sah.magic != MAGIC_SCRI) {
				D_(D_CRIT "error: FM instrument magic");
				goto err3;
			}
			sah.magic = 0;

			libxmp_instrument_name(mod, i, sah.name, 28);

			xxi->nsm = 1;
			sub->vol = sah.vol;
			libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
			sub->xpo += 12;
			ret =
			    libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs,
					(char *)sah.reg);
			if (ret < 0)
				goto err3;

			D_(D_INFO "[%2X] %-28.28s", i, xxi->name);

			continue;
#else
			goto err3;
#endif
		}

		memcpy(sih.dosname, buf + 1, 13);	/* DOS file name */
		sih.memseg = readmem16l(buf + 14);	/* Pointer to sample data */
		sih.length = readmem32l(buf + 16);	/* Length */

#if 0
		/* ST3 limit */
		if ((sfh.version >> 12) == 1 && sih.length > 64000)
			sih.length = 64000;
#endif

		if (sih.length > MAX_SAMPLE_SIZE) {
			goto err3;
		}

		sih.loopbeg = readmem32l(buf + 20);	/* Loop begin */
		sih.loopend = readmem32l(buf + 24);	/* Loop end */
		sih.vol = buf[28];			/* Volume */
		sih.pack = buf[30];			/* Packing type */
		sih.flags = buf[31];			/* Loop/stereo/16bit flags */
		sih.c2spd = readmem16l(buf + 32);	/* C4 speed */
		memcpy(sih.name, buf + 48, 28);		/* Instrument name */
		sih.magic = readmem32b(buf + 76);	/* 'SCRS' */

		if (buf[0] == 1 && sih.magic != MAGIC_SCRS) {
			D_(D_CRIT "error: instrument magic");
			goto err3;
		}

		xxs->len = sih.length;
		xxi->nsm = sih.length > 0 ? 1 : 0;
		xxs->lps = sih.loopbeg;
		xxs->lpe = sih.loopend;

		xxs->flg = sih.flags & 1 ? XMP_SAMPLE_LOOP : 0;

		if (sih.flags & 4) {
			xxs->flg |= XMP_SAMPLE_16BIT;
		}

		load_sample_flags = (sfh.ffi == 1) ? 0 : SAMPLE_FLAG_UNS;
		if (sih.pack == 4) {
			load_sample_flags = SAMPLE_FLAG_ADPCM;
		}

		sub->vol = sih.vol;
		sih.magic = 0;

		libxmp_instrument_name(mod, i, sih.name, 28);

		D_(D_INFO "[%2X] %-28.28s %04x%c%04x %04x %c V%02x %5d",
		   i, mod->xxi[i].name, mod->xxs[i].len,
		   xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ',
		   xxs->lps, mod->xxs[i].lpe,
		   xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sih.c2spd);

		libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin);

		if (hio_seek(f, start + 16L * sih.memseg, SEEK_SET) < 0) {
			goto err3;
		}

		ret = libxmp_load_sample(m, f, load_sample_flags, xxs, NULL);
		if (ret < 0) {
			goto err3;
		}
	}

	free(pp_pat);
	free(pp_ins);

	m->quirk |= QUIRKS_ST3 | QUIRK_ARPMEM;
	m->read_event_type = READ_EVENT_ST3;

	return 0;

err3:
	free(pp_pat);
err2:
	free(pp_ins);
err:
	return -1;
}

repo_name: orochi/nblood

license: mit


code:
/* 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.
 */

#include "common.h"
#include "loader.h"

#ifndef LIBXMP_CORE_PLAYER

/*
 * From the Audio File Formats (version 2.5)
 * Submitted-by: Guido van Rossum <guido@cwi.nl>
 * Last-modified: 27-Aug-1992
 *
 * The Acorn Archimedes uses a variation on U-LAW with the bit order
 * reversed and the sign bit in bit 0.  Being a 'minority' architecture,
 * Arc owners are quite adept at converting sound/image formats from
 * other machines, and it is unlikely that you'll ever encounter sound in
 * one of the Arc's own formats (there are several).
 */
static const int8 vdic_table[128] = {
	/*   0 */	  0,   0,   0,   0,   0,   0,   0,   0,
	/*   8 */	  0,   0,   0,   0,   0,   0,   0,   0,
	/*  16 */	  0,   0,   0,   0,   0,   0,   0,   0,
	/*  24 */	  1,   1,   1,   1,   1,   1,   1,   1,
	/*  32 */	  1,   1,   1,   1,   2,   2,   2,   2,
	/*  40 */	  2,   2,   2,   2,   3,   3,   3,   3,
	/*  48 */	  3,   3,   4,   4,   4,   4,   5,   5,
	/*  56 */	  5,   5,   6,   6,   6,   6,   7,   7,
	/*  64 */	  7,   8,   8,   9,   9,  10,  10,  11,
	/*  72 */	 11,  12,  12,  13,  13,  14,  14,  15,
	/*  80 */	 15,  16,  17,  18,  19,  20,  21,  22,
	/*  88 */	 23,  24,  25,  26,  27,  28,  29,  30,
	/*  96 */	 31,  33,  34,  36,  38,  40,  42,  44,
	/* 104 */	 46,  48,  50,  52,  54,  56,  58,  60,
	/* 112 */	 62,  65,  68,  72,  77,  80,  84,  91,
	/* 120 */	 95,  98, 103, 109, 114, 120, 126, 127
};

/* Convert 7 bit samples to 8 bit */
static void convert_7bit_to_8bit(uint8 *p, int l)
{
	for (; l--; p++) {
		*p <<= 1;
	}
}

/* Convert Archimedes VIDC samples to linear */
static void convert_vidc_to_linear(uint8 *p, int l)
{
	int i;
	uint8 x;

	for (i = 0; i < l; i++) {
		x = p[i];
		p[i] = vdic_table[x >> 1];
		if (x & 0x01)
			p[i] *= -1;
	}
}

static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len)
{
	char delta = 0;
	uint8 b0, b1;
	int i;

	len = (len + 1) / 2;

	for (i = 0; i < len; i++) {
		b0 = *inp;
		b1 = *inp++ >> 4;
		delta += tab[b0 & 0x0f];
		*outp++ = delta;
		delta += tab[b1 & 0x0f];
		*outp++ = delta;
	}
}

#endif

/* Convert differential to absolute sample data */
static void convert_delta(uint8 *p, int l, int r)
{
	uint16 *w = (uint16 *)p;
	uint16 absval = 0;

	if (r) {
		for (; l--;) {
			absval = *w + absval;
			*w++ = absval;
		}
	} else {
		for (; l--;) {
			absval = *p + absval;
			*p++ = (uint8) absval;
		}
	}
}

/* Convert signed to unsigned sample data */
static void convert_signal(uint8 *p, int l, int r)
{
	uint16 *w = (uint16 *)p;

	if (r) {
		for (; l--; w++)
			*w += 0x8000;
	} else {
		for (; l--; p++)
			*p += (char)0x80;	/* cast needed by MSVC++ */
	}
}

/* Convert little-endian 16 bit samples to big-endian */
static void convert_endian(uint8 *p, int l)
{
	uint8 b;
	int i;

	for (i = 0; i < l; i++) {
		b = p[0];
		p[0] = p[1];
		p[1] = b;
		p += 2;
	}
}

#if 0
/* Downmix stereo samples to mono */
static void convert_stereo_to_mono(uint8 *p, int l, int r)
{
	int16 *b = (int16 *)p;
	int i;

	if (r) {
		l /= 2;
		for (i = 0; i < l; i++)
			b[i] = (b[i * 2] + b[i * 2 + 1]) / 2;
	} else {
		for (i = 0; i < l; i++)
			p[i] = (p[i * 2] + p[i * 2 + 1]) / 2;
	}
}
#endif

static void unroll_loop(struct xmp_sample *xxs)
{
	int8 *s8;
	int16 *s16;
	int start, loop_size;
	int i;

	s16 = (int16 *)xxs->data;
	s8 = (int8 *)xxs->data;

	if (xxs->len > xxs->lpe) {
		start = xxs->lpe;
	} else {
		start = xxs->len;
	}

	loop_size = xxs->lpe - xxs->lps;

	if (xxs->flg & XMP_SAMPLE_16BIT) {
		s16 += start;
		for (i = 0; i < loop_size; i++) {
			*(s16 + i) = *(s16 - i - 1);
		}
	} else {
		s8 += start;
		for (i = 0; i < loop_size; i++) {
			*(s8 + i) = *(s8 - i - 1);
		}
	}
}

int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer)
{
	int bytelen, extralen, unroll_extralen, i;

#ifndef LIBXMP_CORE_PLAYER
	/* Adlib FM patches */
	if (flags & SAMPLE_FLAG_ADLIB) {
		return 0;
	}
#endif

	/* Empty or invalid samples
	 */
	if (xxs->len <= 0) {
		return 0;
	}

	/* Skip sample loading
	 * FIXME: fails for ADPCM samples
	 *
	 * + Sanity check: skip huge samples (likely corrupt module)
	 */
	if (xxs->len > MAX_SAMPLE_SIZE || (m && m->smpctl & XMP_SMPCTL_SKIP)) {
		if (~flags & SAMPLE_FLAG_NOLOAD) {
			/* coverity[check_return] */
			hio_seek(f, xxs->len, SEEK_CUR);
		}
		return 0;
	}

	/* If this sample starts at or after EOF, skip it entirely.
	 */
	if (~flags & SAMPLE_FLAG_NOLOAD) {
		long file_pos, file_len;
		if (!f) {
			return 0;
		}
		file_pos = hio_tell(f);
		file_len = hio_size(f);
		if (file_pos >= file_len) {
			D_(D_WARN "ignoring sample at EOF");
			return 0;
		}
		/* If this sample goes past EOF, truncate it. */
		if (file_pos + xxs->len > file_len && (~flags & SAMPLE_FLAG_ADPCM)) {
			D_(D_WARN "sample would extend %ld bytes past EOF; truncating to %ld",
				file_pos + xxs->len - file_len, file_len - file_pos);
			xxs->len = file_len - file_pos;
		}
	}

	/* Loop parameters sanity check
	 */
	if (xxs->lps < 0) {
		xxs->lps = 0;
	}
	if (xxs->lpe > xxs->len) {
		xxs->lpe = xxs->len;
	}
	if (xxs->lps >= xxs->len || xxs->lps >= xxs->lpe) {
		xxs->lps = xxs->lpe = 0;
		xxs->flg &= ~(XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR);
	}

	/* Patches with samples
	 * Allocate extra sample for interpolation.
	 */
	bytelen = xxs->len;
	extralen = 4;
	unroll_extralen = 0;

	/* Disable birectional loop flag if sample is not looped
	 */
	if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
		if (~xxs->flg & XMP_SAMPLE_LOOP)
			xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
	}
	/* Unroll bidirectional loops
	 */
	if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
		unroll_extralen = (xxs->lpe - xxs->lps) -
				(xxs->len - xxs->lpe);

		if (unroll_extralen < 0) {
			unroll_extralen = 0;
		}
	}

	if (xxs->flg & XMP_SAMPLE_16BIT) {
		bytelen *= 2;
		extralen *= 2;
		unroll_extralen *= 2;
	}

	/* add guard bytes before the buffer for higher order interpolation */
	xxs->data = (unsigned char *)malloc(bytelen + extralen + unroll_extralen + 4);
	if (xxs->data == NULL) {
		goto err;
	}

	*(uint32 *)xxs->data = 0;
	xxs->data += 4;

	if (flags & SAMPLE_FLAG_NOLOAD) {
		memcpy(xxs->data, buffer, bytelen);
	} else
#ifndef LIBXMP_CORE_PLAYER
	if (flags & SAMPLE_FLAG_ADPCM) {
		int x2 = (bytelen + 1) >> 1;
		char table[16];

		if (hio_read(table, 1, 16, f) != 16) {
			goto err2;
		}
		if (hio_read(xxs->data + x2, 1, x2, f) != x2) {
			goto err2;
		}
		adpcm4_decoder((uint8 *)xxs->data + x2,
			       (uint8 *)xxs->data, table, bytelen);
	} else
#endif
	{
		int x = hio_read(xxs->data, 1, bytelen, f);
		if (x != bytelen) {
			D_(D_WARN "short read (%d) in sample load", x - bytelen);
			memset(xxs->data + x, 0, bytelen - x);
		}
	}

#ifndef LIBXMP_CORE_PLAYER
	if (flags & SAMPLE_FLAG_7BIT) {
		convert_7bit_to_8bit(xxs->data, xxs->len);
	}
#endif

	/* Fix endianism if needed */
	if (xxs->flg & XMP_SAMPLE_16BIT) {
#ifdef WORDS_BIGENDIAN
		if (~flags & SAMPLE_FLAG_BIGEND)
			convert_endian(xxs->data, xxs->len);
#else
		if (flags & SAMPLE_FLAG_BIGEND)
			convert_endian(xxs->data, xxs->len);
#endif
	}

	/* Convert delta samples */
	if (flags & SAMPLE_FLAG_DIFF) {
		convert_delta(xxs->data, xxs->len, xxs->flg & XMP_SAMPLE_16BIT);
	} else if (flags & SAMPLE_FLAG_8BDIFF) {
		int len = xxs->len;
		if (xxs->flg & XMP_SAMPLE_16BIT) {
			len *= 2;
		}
		convert_delta(xxs->data, len, 0);
	}

	/* Convert samples to signed */
	if (flags & SAMPLE_FLAG_UNS) {
		convert_signal(xxs->data, xxs->len,
				xxs->flg & XMP_SAMPLE_16BIT);
	}

#if 0
	/* Downmix stereo samples */
	if (flags & SAMPLE_FLAG_STEREO) {
		convert_stereo_to_mono(xxs->data, xxs->len,
					xxs->flg & XMP_SAMPLE_16BIT);
		xxs->len /= 2;
	}
#endif

#ifndef LIBXMP_CORE_PLAYER
	if (flags & SAMPLE_FLAG_VIDC) {
		convert_vidc_to_linear(xxs->data, xxs->len);
	}
#endif

	/* Check for full loop samples */
	if (flags & SAMPLE_FLAG_FULLREP) {
	    if (xxs->lps == 0 && xxs->len > xxs->lpe)
		xxs->flg |= XMP_SAMPLE_LOOP_FULL;
	}

	/* Unroll bidirectional loops */
	if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
		unroll_loop(xxs);
		bytelen += unroll_extralen;
	}

	/* Add extra samples at end */
	if (xxs->flg & XMP_SAMPLE_16BIT) {
		for (i = 0; i < 8; i++) {
			xxs->data[bytelen + i] = xxs->data[bytelen - 2 + i];
		}
	} else {
		for (i = 0; i < 4; i++) {
			xxs->data[bytelen + i] = xxs->data[bytelen - 1 + i];
		}
	}

	/* Add extra samples at start */
	if (xxs->flg & XMP_SAMPLE_16BIT) {
		xxs->data[-2] = xxs->data[0];
		xxs->data[-1] = xxs->data[1];
	} else {
		xxs->data[-1] = xxs->data[0];
	}

	/* Fix sample at loop */
	if (xxs->flg & XMP_SAMPLE_LOOP) {
		int lpe = xxs->lpe;
		int lps = xxs->lps;

		if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
			lpe += lpe - lps;
		}

		if (xxs->flg & XMP_SAMPLE_16BIT) {
			lpe <<= 1;
			lps <<= 1;
			for (i = 0; i < 8; i++) {
				xxs->data[lpe + i] = xxs->data[lps + i];
			}
		} else {
			for (i = 0; i < 4; i++) {
				xxs->data[lpe + i] = xxs->data[lps + i];
			}
		}
	}

	return 0;

#ifndef LIBXMP_CORE_PLAYER
    err2:
	libxmp_free_sample(xxs);
#endif
    err:
	return -1;
}

void libxmp_free_sample(struct xmp_sample *s)
{
    if (s->data) {
	free(s->data - 4);
	s->data = NULL;		/* prevent double free in PCM load error */
    }
}


code:
/* 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.
 */

/*
 * Sun, 31 May 1998 17:50:02 -0600
 * Reported by ToyKeeper <scriven@CS.ColoState.EDU>:
 * For loop-prevention, I know a way to do it which lets most songs play
 * fine once through even if they have backward-jumps. Just keep a small
 * array (256 bytes, or even bits) of flags, each entry determining if a
 * pattern in the song order has been played. If you get to an entry which
 * is already non-zero, skip to the next song (assuming looping is off).
 */

/*
 * Tue, 6 Oct 1998 21:23:17 +0200 (CEST)
 * Reported by John v/d Kamp <blade_@dds.nl>:
 * scan.c was hanging when it jumps to an invalid restart value.
 * (Fixed by hipolito)
 */

#include "common.h"
#include "effects.h"
#include "mixer.h"

#define S3M_END		0xff
#define S3M_SKIP	0xfe

static int scan_module(struct context_data *ctx, int ep, int chain)
{
    struct player_data *p = &ctx->p;
    struct module_data *m = &ctx->m;
    struct xmp_module *mod = &m->mod;
    int parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
    int row, last_row, break_row, row_count, row_count_total;
    int orders_since_last_valid, any_valid;
    int gvl, bpm, speed, base_time, chn;
    int frame_count;
    double time, start_time;
    int loop_chn, loop_num, inside_loop;
    int pdelay = 0;
    int loop_count[XMP_MAX_CHANNELS];
    int loop_row[XMP_MAX_CHANNELS];
    struct xmp_event* event;
    int i, pat;
    int has_marker;
    struct ord_data *info;
#ifndef LIBXMP_CORE_PLAYER
    int st26_speed;
#endif

    if (mod->len == 0)
	return 0;

    for (i = 0; i < mod->len; i++) {
	pat = mod->xxo[i];
	memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
			mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1);
    }

    for (i = 0; i < mod->chn; i++) {
	loop_count[i] = 0;
	loop_row[i] = -1;
    }
    loop_num = 0;
    loop_chn = -1;

    gvl = mod->gvl;
    bpm = mod->bpm;

    speed = mod->spd;
    base_time = m->rrate;
#ifndef LIBXMP_CORE_PLAYER
    st26_speed = 0;
#endif

    has_marker = HAS_QUIRK(QUIRK_MARKER);

    /* By erlk ozlr <erlk.ozlr@gmail.com>
     *
     * xmp doesn't handle really properly the "start" option (-s for the
     * command-line) for DeusEx's .umx files. These .umx files contain
     * several loop "tracks" that never join together. That's how they put
     * multiple musics on each level with a file per level. Each "track"
     * starts at the same order in all files. The problem is that xmp starts
     * decoding the module at order 0 and not at the order specified with
     * the start option. If we have a module that does "0 -> 2 -> 0 -> ...",
     * we cannot play order 1, even with the supposed right option.
     *
     * was: ord2 = ord = -1;
     *
     * CM: Fixed by using different "sequences" for each loop or subsong.
     *     Each sequence has its entry point. Sequences don't overlap.
     */
    ord2 = -1;
    ord = ep - 1;

    gvol_memory = break_row = row_count = row_count_total = frame_count = 0;
    orders_since_last_valid = any_valid = 0;
    start_time = time = 0.0;
    inside_loop = 0;

    while (42) {
	/* Sanity check to prevent getting stuck due to broken patterns. */
	if (orders_since_last_valid > 512) {
	    D_(D_CRIT "orders_since_last_valid = %d @ ord %d; ending scan", orders_since_last_valid, ord);
	    break;
	}
	orders_since_last_valid++;

	if ((uint32)++ord >= mod->len) {
	    if (mod->rst > mod->len || mod->xxo[mod->rst] >= mod->pat) {
		ord = ep;
	    } else {
		if (libxmp_get_sequence(ctx, mod->rst) == chain) {
	            ord = mod->rst;
		} else {
		    ord = ep;
	        }
	    }

	    pat = mod->xxo[ord];
	    if (has_marker && pat == S3M_END) {
		break;
	    }
	}

	pat = mod->xxo[ord];
	info = &m->xxo_info[ord];

	/* Allow more complex order reuse only in main sequence */
	if (ep != 0 && p->sequence_control[ord] != 0xff) {
	    break;
	}
	p->sequence_control[ord] = chain;

	/* All invalid patterns skipped, only S3M_END aborts replay */
	if (pat >= mod->pat) {
	    if (has_marker && pat == S3M_END) {
		ord = mod->len;
	        continue;
	    }
	    continue;
	}

        if (break_row >= mod->xxp[pat]->rows) {
            break_row = 0;
        }

        /* Loops can cross pattern boundaries, so check if we're not looping */
        if (m->scan_cnt[ord][break_row] && !inside_loop) {
            break;
        }

        /* Only update pattern information if we weren't here before. This also
         * means that we don't update pattern information if we're inside a loop,
         * otherwise a loop containing e.g. a global volume fade can make the
         * pattern start with the wrong volume. (fixes xyce-dans_la_rue.xm replay,
         * see https://github.com/libxmp/libxmp/issues/153 for more details).
         */
        if (info->time < 0) {
            info->gvl = gvl;
            info->bpm = bpm;
            info->speed = speed;
            info->time = (int) (time + m->time_factor * frame_count * base_time / bpm);
#ifndef LIBXMP_CORE_PLAYER
            info->st26_speed = st26_speed;
#endif
        }

	if (info->start_row == 0 && ord != 0) {
	    if (ord == ep) {
		start_time = time + m->time_factor * frame_count * base_time / bpm;
	    }

	    info->start_row = break_row;
	}

	last_row = mod->xxp[pat]->rows;
	for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) {
	    /* Prevent crashes caused by large softmixer frames */
	    if (bpm < XMP_MIN_BPM) {
	        bpm = XMP_MIN_BPM;
	    }

	    /* Date: Sat, 8 Sep 2007 04:01:06 +0200
	     * Reported by Zbigniew Luszpinski <zbiggy@o2.pl>
	     * The scan routine falls into infinite looping and doesn't let
	     * xmp play jos-dr4k.xm.
	     * Claudio's workaround: we'll break infinite loops here.
	     *
	     * Date: Oct 27, 2007 8:05 PM
	     * From: Adric Riedel <adric.riedel@gmail.com>
	     * Jesper Kyd: Global Trash 3.mod (the 'Hardwired' theme) only
	     * plays the first 4:41 of what should be a 10 minute piece.
	     * (...) it dies at the end of position 2F
	     */

	    if (row_count_total > 512) { /* was 255, but Global trash goes to 318. */
		D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row);
		goto end_module;
	    }

	    if (!loop_num && m->scan_cnt[ord][row]) {
		row_count--;
		goto end_module;
	    }
	    m->scan_cnt[ord][row]++;
	    orders_since_last_valid = 0;
	    any_valid = 1;

	    /* If the scan count for this row overflows, break.
	     * A scan count of 0 will help break this loop in playback (storlek_11.it).
	     */
	    if (!m->scan_cnt[ord][row]) {
		goto end_module;
	    }

	    pdelay = 0;

	    for (chn = 0; chn < mod->chn; chn++) {
		if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows)
		    continue;

		event = &EVENT(mod->xxo[ord], chn, row);

		f1 = event->fxt;
		p1 = event->fxp;
		f2 = event->f2t;
		p2 = event->f2p;

		if (f1 == FX_GLOBALVOL || f2 == FX_GLOBALVOL) {
		    gvl = (f1 == FX_GLOBALVOL) ? p1 : p2;
		    gvl = gvl > m->gvolbase ? m->gvolbase : gvl < 0 ? 0 : gvl;
		}

		/* Process fine global volume slide */
		if (f1 == FX_GVOL_SLIDE || f2 == FX_GVOL_SLIDE) {
		    int h, l;
		    parm = (f1 == FX_GVOL_SLIDE) ? p1 : p2;

		process_gvol:
                    if (parm) {
			gvol_memory = parm;
                        h = MSN(parm);
                        l = LSN(parm);

		        if (HAS_QUIRK(QUIRK_FINEFX)) {
                            if (l == 0xf && h != 0) {
				gvl += h;
			    } else if (h == 0xf && l != 0) {
				gvl -= l;
			    } else {
		                if (m->quirk & QUIRK_VSALL) {
                                    gvl += (h - l) * speed;
				} else {
                                    gvl += (h - l) * (speed - 1);
				}
			    }
			} else {
		            if (m->quirk & QUIRK_VSALL) {
                                gvl += (h - l) * speed;
			    } else {
                                gvl += (h - l) * (speed - 1);
			    }
			}
		    } else {
                        if ((parm = gvol_memory) != 0)
			    goto process_gvol;
		    }
		}

		if ((f1 == FX_SPEED && p1) || (f2 == FX_SPEED && p2)) {
		    parm = (f1 == FX_SPEED) ? p1 : p2;
		    frame_count += row_count * speed;
		    row_count = 0;
		    if (parm) {
			if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
			    if (parm > 0) {
			        speed = parm;
#ifndef LIBXMP_CORE_PLAYER
			        st26_speed = 0;
#endif
                            }
			} else {
			    time += m->time_factor * frame_count * base_time / bpm;
			    frame_count = 0;
			    bpm = parm;
			}
		    }
		}

#ifndef LIBXMP_CORE_PLAYER
		if (f1 == FX_SPEED_CP) {
		    f1 = FX_S3M_SPEED;
		}
		if (f2 == FX_SPEED_CP) {
		    f2 = FX_S3M_SPEED;
		}

		/* ST2.6 speed processing */

		if (f1 == FX_ICE_SPEED && p1) {
		    if (LSN(p1)) {
		        st26_speed = (MSN(p1) << 8) | LSN(p1);
		    } else {
			st26_speed = MSN(p1);
		    }
		}
#endif

		if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) {
		    parm = (f1 == FX_S3M_SPEED) ? p1 : p2;
		    if (parm > 0) {
		        frame_count += row_count * speed;
		        row_count  = 0;
		        speed = parm;
#ifndef LIBXMP_CORE_PLAYER
		        st26_speed = 0;
#endif
		    }
		}

		if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
		    parm = (f1 == FX_S3M_BPM) ? p1 : p2;
		    if (parm >= 0x20) {
			frame_count += row_count * speed;
			row_count = 0;
			time += m->time_factor * frame_count * base_time / bpm;
			frame_count = 0;
			bpm = parm;
		    }
		}

#ifndef LIBXMP_CORE_DISABLE_IT
		if ((f1 == FX_IT_BPM && p1) || (f2 == FX_IT_BPM && p2)) {
		    parm = (f1 == FX_IT_BPM) ? p1 : p2;
		    frame_count += row_count * speed;
		    row_count = 0;
		    time += m->time_factor * frame_count * base_time / bpm;
		    frame_count = 0;

		    if (MSN(parm) == 0) {
		        time += m->time_factor * base_time / bpm;
			for (i = 1; i < speed; i++) {
			    bpm -= LSN(parm);
			    if (bpm < 0x20)
				bpm = 0x20;
		            time += m->time_factor * base_time / bpm;
			}

			/* remove one row at final bpm */
			time -= m->time_factor * speed * base_time / bpm;

		    } else if (MSN(parm) == 1) {
		        time += m->time_factor * base_time / bpm;
			for (i = 1; i < speed; i++) {
			    bpm += LSN(parm);
			    if (bpm > 0xff)
				bpm = 0xff;
		            time += m->time_factor * base_time / bpm;
			}

			/* remove one row at final bpm */
			time -= m->time_factor * speed * base_time / bpm;

		    } else {
			bpm = parm;
		    }
		}

		if (f1 == FX_IT_ROWDELAY) {
			/* Don't allow the scan count for this row to overflow here. */
			int x = m->scan_cnt[ord][row] + (p1 & 0x0f);
			m->scan_cnt[ord][row] = MIN(x, 255);
			frame_count += (p1 & 0x0f) * speed;
		}

		if (f1 == FX_IT_BREAK) {
		    break_row = p1;
		    last_row = 0;
		}
#endif

		if (f1 == FX_JUMP || f2 == FX_JUMP) {
		    ord2 = (f1 == FX_JUMP) ? p1 : p2;
		    break_row = 0;
		    last_row = 0;

		    /* prevent infinite loop, see OpenMPT PatLoop-Various.xm */
		    inside_loop = 0;
		}

		if (f1 == FX_BREAK || f2 == FX_BREAK) {
		    parm = (f1 == FX_BREAK) ? p1 : p2;
		    break_row = 10 * MSN(parm) + LSN(parm);
		    last_row = 0;
		}

		if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
		    parm = (f1 == FX_EXTENDED) ? p1 : p2;

		    if ((parm >> 4) == EX_PATT_DELAY) {
			if (m->read_event_type != READ_EVENT_ST3 || !pdelay) {
			    pdelay = parm & 0x0f;
			    frame_count += pdelay * speed;
                        }
		    }

		    if ((parm >> 4) == EX_PATTERN_LOOP) {
			if (parm &= 0x0f) {
			    /* Loop end */
			    if (loop_count[chn]) {
				if (--loop_count[chn]) {
				    /* next iteraction */
				    loop_chn = chn;
				} else {
				    /* finish looping */
				    loop_num--;
				    inside_loop = 0;
				    if (m->quirk & QUIRK_S3MLOOP)
					loop_row[chn] = row;
				}
			    } else {
				loop_count[chn] = parm;
				loop_chn = chn;
				loop_num++;
			    }
			} else {
			    /* Loop start */
			    loop_row[chn] = row - 1;
			    inside_loop = 1;
			    if (HAS_QUIRK(QUIRK_FT2BUGS))
				break_row = row;
			}
		    }
		}
	    }

	    if (loop_chn >= 0) {
		row = loop_row[loop_chn];
		loop_chn = -1;
	    }

#ifndef LIBXMP_CORE_PLAYER
	    if (st26_speed) {
	        frame_count += row_count * speed;
	        row_count  = 0;
		if (st26_speed & 0x10000) {
			speed = (st26_speed & 0xff00) >> 8;
		} else {
			speed = st26_speed & 0xff;
		}
		st26_speed ^= 0x10000;
	    }
#endif
	}

	if (break_row && pdelay) {
	    break_row++;
	}

	if (ord2 >= 0) {
	    ord = ord2 - 1;
	    ord2 = -1;
	}

	frame_count += row_count * speed;
	row_count_total = 0;
	row_count = 0;
    }
    row = break_row;

end_module:

    /* Sanity check */
    {
        if (!any_valid) {
	    return -1;
	}
        pat = mod->xxo[ord];
        if (pat >= mod->pat || row >= mod->xxp[pat]->rows) {
            row = 0;
        }
    }

    p->scan[chain].num = m->scan_cnt[ord][row];
    p->scan[chain].row = row;
    p->scan[chain].ord = ord;

    time -= start_time;
    frame_count += row_count * speed;

    return (int) (time + m->time_factor * frame_count * base_time / bpm);
}

int libxmp_get_sequence(struct context_data *ctx, int ord)
{
	struct player_data *p = &ctx->p;
	return p->sequence_control[ord];
}

int libxmp_scan_sequences(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct scan_data *s;
	int i, ep;
	int seq;
	unsigned char temp_ep[XMP_MAX_MOD_LENGTH];

	s = (struct scan_data *)realloc(p->scan, MAX(1, mod->len) * sizeof(struct scan_data));
	if (!s) {
		D_(D_CRIT "failed to allocate scan data");
		return -1;
	}
	p->scan = s;

	/* Initialize order data to prevent overwrite when a position is used
	 * multiple times at different starting points (see janosik.xm).
	 */
	for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
		m->xxo_info[i].time = -1;
	}

	ep = 0;
	memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
	temp_ep[0] = 0;
	p->scan[0].time = scan_module(ctx, ep, 0);
	seq = 1;

	if (p->scan[0].time < 0) {
		D_(D_CRIT "scan was not able to find any valid orders");
		return -1;
	}

	while (1) {
		/* Scan song starting at given entry point */
		/* Check if any patterns left */
		for (i = 0; i < mod->len; i++) {
			if (p->sequence_control[i] == 0xff) {
				break;
			}
		}
		if (i != mod->len && seq < MAX_SEQUENCES) {
			/* New entry point */
			ep = i;
			temp_ep[seq] = ep;
			p->scan[seq].time = scan_module(ctx, ep, seq);
			if (p->scan[seq].time > 0)
				seq++;
		} else {
			break;
		}
	}

	if (seq < mod->len) {
		s = (struct scan_data *)realloc(p->scan, seq * sizeof(struct scan_data));
		if (s != NULL) {
			p->scan = s;
		}
	}
	m->num_sequences = seq;

	/* Now place entry points in the public accessible array */
	for (i = 0; i < m->num_sequences; i++) {
		m->seq_data[i].entry_point = temp_ep[i];
		m->seq_data[i].duration = p->scan[i].time;
	}

	return 0;
}


code:
/* 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.
 */

#include "common.h"
#include "period.h"
#include "player.h"
#include "hio.h"
#include "loader.h"

struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins)
{
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_instrument *xxi;

	if (ins < mod->ins) {
		xxi = &mod->xxi[ins];
	} else if (ins < mod->ins + smix->ins) {
		xxi = &smix->xxi[ins - mod->ins];
	} else {
		xxi = NULL;
	}

	return xxi;
}

struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp)
{
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_sample *xxs;

	if (smp < mod->smp) {
		xxs = &mod->xxs[smp];
	} else if (smp < mod->smp + smix->smp) {
		xxs = &smix->xxs[smp - mod->smp];
	} else {
		xxs = NULL;
	}

	return xxs;
}

int xmp_start_smix(xmp_context opaque, int chn, int smp)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;

	if (ctx->state > XMP_STATE_LOADED) {
		return -XMP_ERROR_STATE;
	}

	smix->xxi = (struct xmp_instrument *)calloc(sizeof (struct xmp_instrument), smp);
	if (smix->xxi == NULL) {
		goto err;
	}
	smix->xxs = (struct xmp_sample *)calloc(sizeof (struct xmp_sample), smp);
	if (smix->xxs == NULL) {
		goto err1;
	}

	smix->chn = chn;
	smix->ins = smix->smp = smp;

	return 0;

    err1:
	free(smix->xxi);
	smix->xxi = NULL;
    err:
	return -XMP_ERROR_INTERNAL;
}

int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int chn)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct player_data *p = &ctx->p;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_event *event;

	if (ctx->state < XMP_STATE_PLAYING) {
		return -XMP_ERROR_STATE;
	}

	if (chn >= smix->chn || ins >= mod->ins) {
		return -XMP_ERROR_INVALID;
	}

	if (note == 0) {
		note = 60;		/* middle C note number */
	}

	event = &p->inject_event[mod->chn + chn];
	memset(event, 0, sizeof (struct xmp_event));
	event->note = note + 1;
	event->ins = ins + 1;
	event->vol = vol + 1;
	event->_flag = 1;

	return 0;
}

int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct player_data *p = &ctx->p;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_event *event;

	if (ctx->state < XMP_STATE_PLAYING) {
		return -XMP_ERROR_STATE;
	}

	if (chn >= smix->chn || ins >= smix->ins) {
		return -XMP_ERROR_INVALID;
	}

	if (note == 0) {
		note = 60;		/* middle C note number */
	}

	event = &p->inject_event[mod->chn + chn];
	memset(event, 0, sizeof (struct xmp_event));
	event->note = note + 1;
	event->ins = mod->ins + ins + 1;
	event->vol = vol + 1;
	event->_flag = 1;

	return 0;
}

int xmp_smix_channel_pan(xmp_context opaque, int chn, int pan)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct player_data *p = &ctx->p;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct channel_data *xc;

	if (chn >= smix->chn || pan < 0 || pan > 255) {
		return -XMP_ERROR_INVALID;
	}

	xc = &p->xc_data[m->mod.chn + chn];
	xc->pan.val = pan;

	return 0;
}

#ifdef EDUKE32_DISABLED
int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_instrument *xxi;
	struct xmp_sample *xxs;
	HIO_HANDLE *h;
	uint32 magic;
	int chn, rate, bits, size;
	int retval = -XMP_ERROR_INTERNAL;

	if (num >= smix->ins) {
		retval = -XMP_ERROR_INVALID;
		goto err;
	}

	xxi = &smix->xxi[num];
	xxs = &smix->xxs[num];

	h = hio_open(path, "rb");
	if (h == NULL) {
		retval = -XMP_ERROR_SYSTEM;
		goto err;
	}

	/* Init instrument */

	xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
	if (xxi->sub == NULL) {
		retval = -XMP_ERROR_SYSTEM;
		goto err1;
	}

	xxi->vol = m->volbase;
	xxi->nsm = 1;
	xxi->sub[0].sid = num;
	xxi->sub[0].vol = xxi->vol;
	xxi->sub[0].pan = 0x80;

	/* Load sample */

	magic = hio_read32b(h);
	if (magic != 0x52494646) {	/* RIFF */
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	if (hio_seek(h, 22, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	chn = hio_read16l(h);
	if (chn != 1) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	rate = hio_read32l(h);
	if (rate == 0) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	if (hio_seek(h, 34, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	bits = hio_read16l(h);
	if (bits == 0) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	if (hio_seek(h, 40, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	size = hio_read32l(h);
	if (size == 0) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	libxmp_c2spd_to_note(rate, &xxi->sub[0].xpo, &xxi->sub[0].fin);

	xxs->len = 8 * size / bits;
	xxs->lps = 0;
	xxs->lpe = 0;
	xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;

	xxs->data = malloc(size + 8);
	if (xxs->data == NULL) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}

	/* ugly hack to make the interpolator happy */
	memset(xxs->data, 0, 4);
	memset(xxs->data + 4 + size, 0, 4);
	xxs->data += 4;

	if (hio_seek(h, 44, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	if (hio_read(xxs->data, 1, size, h) != size) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	hio_close(h);

	return 0;

    err2:
	free(xxi->sub);
	xxi->sub = NULL;
    err1:
	hio_close(h);
    err:
	return retval;
}
#endif // EDUKE32_DISABLED

int xmp_smix_release_sample(xmp_context opaque, int num)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;

	if (num >= smix->ins) {
		return -XMP_ERROR_INVALID;
	}

	libxmp_free_sample(&smix->xxs[num]);
	free(smix->xxi[num].sub);

	smix->xxs[num].data = NULL;
	smix->xxi[num].sub = NULL;

	return 0;
}

void xmp_end_smix(xmp_context opaque)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;
	int i;

	for (i = 0; i < smix->smp; i++) {
		xmp_smix_release_sample(opaque, i);
	}

	free(smix->xxs);
	free(smix->xxi);
	smix->xxs = NULL;
	smix->xxi = NULL;
}


code:
/* 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.
 */

#include <limits.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"

#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif

#define	FREE	-1

/* For virt_pastnote() */
void libxmp_player_set_release(struct context_data *, int);
void libxmp_player_set_fadeout(struct context_data *, int);

/* Get parent channel */
int libxmp_virt_getroot(struct context_data *ctx, int chn)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi;
	int voc;

	voc = p->virt.virt_channel[chn].map;
	if (voc < 0) {
		return -1;
	}

	vi = &p->virt.voice_array[voc];

	return vi->root;
}

void libxmp_virt_resetvoice(struct context_data *ctx, int voc, int mute)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];
#ifdef LIBXMP_PAULA_SIMULATOR
	struct paula_state *paula;
#endif

	if ((uint32)voc >= p->virt.maxvoc) {
		return;
	}

	if (mute) {
		libxmp_mixer_setvol(ctx, voc, 0);
	}

	p->virt.virt_used--;
	p->virt.virt_channel[vi->root].count--;
	p->virt.virt_channel[vi->chn].map = FREE;
#ifdef LIBXMP_PAULA_SIMULATOR
	paula = vi->paula;
#endif
	memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
	vi->paula = paula;
#endif
	vi->chn = vi->root = FREE;
}

/* virt_on (number of tracks) */
int libxmp_virt_on(struct context_data *ctx, int num)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	int i;

	p->virt.num_tracks = num;
	num = libxmp_mixer_numvoices(ctx, -1);

	p->virt.virt_channels = p->virt.num_tracks;

	if (HAS_QUIRK(QUIRK_VIRTUAL)) {
		p->virt.virt_channels += num;
	} else if (num > p->virt.virt_channels) {
		num = p->virt.virt_channels;
	}

	p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num);

	p->virt.voice_array = (struct mixer_voice *)calloc(p->virt.maxvoc,
				sizeof(struct mixer_voice));
	if (p->virt.voice_array == NULL)
		goto err;

	for (i = 0; i < p->virt.maxvoc; i++) {
		p->virt.voice_array[i].chn = FREE;
		p->virt.voice_array[i].root = FREE;
	}

#ifdef LIBXMP_PAULA_SIMULATOR
	/* Initialize Paula simulator */
	if (IS_AMIGA_MOD()) {
		for (i = 0; i < p->virt.maxvoc; i++) {
			p->virt.voice_array[i].paula = calloc(1, sizeof (struct paula_state));
			if (p->virt.voice_array[i].paula == NULL) {
				goto err2;
			}
			libxmp_paula_init(ctx, p->virt.voice_array[i].paula);
		}
	}
#endif

	p->virt.virt_channel = (struct virt_channel *)malloc(p->virt.virt_channels *
				sizeof(struct virt_channel));
	if (p->virt.virt_channel == NULL)
		goto err2;

	for (i = 0; i < p->virt.virt_channels; i++) {
		p->virt.virt_channel[i].map = FREE;
		p->virt.virt_channel[i].count = 0;
	}

	p->virt.virt_used = 0;

	return 0;

      err2:
#ifdef LIBXMP_PAULA_SIMULATOR
	if (IS_AMIGA_MOD()) {
		for (i = 0; i < p->virt.maxvoc; i++) {
			free(p->virt.voice_array[i].paula);
		}
	}
#endif
	free(p->virt.voice_array);
	p->virt.voice_array = NULL;
      err:
	return -1;
}

void libxmp_virt_off(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
#ifdef LIBXMP_PAULA_SIMULATOR
	struct module_data *m = &ctx->m;
	int i;
#endif

#ifdef LIBXMP_PAULA_SIMULATOR
	/* Free Paula simulator state */
	if (IS_AMIGA_MOD()) {
		for (i = 0; i < p->virt.maxvoc; i++) {
			free(p->virt.voice_array[i].paula);
		}
	}
#endif

	p->virt.virt_used = p->virt.maxvoc = 0;
	p->virt.virt_channels = 0;
	p->virt.num_tracks = 0;

	free(p->virt.voice_array);
	free(p->virt.virt_channel);
	p->virt.voice_array = NULL;
	p->virt.virt_channel = NULL;
}

void libxmp_virt_reset(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	int i;

	if (p->virt.virt_channels < 1) {
		return;
	}

	/* CID 129203 (#1 of 1): Useless call (USELESS_CALL)
	 * Call is only useful for its return value, which is ignored.
	 *
	 * libxmp_mixer_numvoices(ctx, p->virt.maxvoc);
	 */

	for (i = 0; i < p->virt.maxvoc; i++) {
		struct mixer_voice *vi = &p->virt.voice_array[i];
#ifdef LIBXMP_PAULA_SIMULATOR
		struct paula_state *paula = vi->paula;
#endif
		memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
		vi->paula = paula;
#endif
		vi->chn = FREE;
		vi->root = FREE;
	}

	for (i = 0; i < p->virt.virt_channels; i++) {
		p->virt.virt_channel[i].map = FREE;
		p->virt.virt_channel[i].count = 0;
	}

	p->virt.virt_used = 0;
}

static int free_voice(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	int i, num, vol;

	/* Find background voice with lowest volume*/
	num = FREE;
	vol = INT_MAX;
	for (i = 0; i < p->virt.maxvoc; i++) {
		struct mixer_voice *vi = &p->virt.voice_array[i];

		if (vi->chn >= p->virt.num_tracks && vi->vol < vol) {
			num = i;
			vol = vi->vol;
		}
	}

	/* Free voice */
	if (num >= 0) {
		p->virt.virt_channel[p->virt.voice_array[num].chn].map = FREE;
		p->virt.virt_channel[p->virt.voice_array[num].root].count--;
		p->virt.virt_used--;
	}

	return num;
}

static int alloc_voice(struct context_data *ctx, int chn)
{
	struct player_data *p = &ctx->p;
	int i;

	/* Find free voice */
	for (i = 0; i < p->virt.maxvoc; i++) {
		if (p->virt.voice_array[i].chn == FREE)
			break;
	}

	/* not found */
	if (i == p->virt.maxvoc) {
		i = free_voice(ctx);
	}

	if (i >= 0) {
		p->virt.virt_channel[chn].count++;
		p->virt.virt_used++;

		p->virt.voice_array[i].chn = chn;
		p->virt.voice_array[i].root = chn;
		p->virt.virt_channel[chn].map = i;
	}

	return i;
}

static int map_virt_channel(struct player_data *p, int chn)
{
	int voc;

	if ((uint32)chn >= p->virt.virt_channels)
		return -1;

	voc = p->virt.virt_channel[chn].map;

	if ((uint32)voc >= p->virt.maxvoc)
		return -1;

	return voc;
}

int libxmp_virt_mapchannel(struct context_data *ctx, int chn)
{
	return map_virt_channel(&ctx->p, chn);
}

void libxmp_virt_resetchannel(struct context_data *ctx, int chn)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi;
#ifdef LIBXMP_PAULA_SIMULATOR
	struct paula_state *paula;
#endif
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0)
		return;

	libxmp_mixer_setvol(ctx, voc, 0);

	p->virt.virt_used--;
	p->virt.virt_channel[p->virt.voice_array[voc].root].count--;
	p->virt.virt_channel[chn].map = FREE;

	vi = &p->virt.voice_array[voc];
#ifdef LIBXMP_PAULA_SIMULATOR
	paula = vi->paula;
#endif
	memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
	vi->paula = paula;
#endif
	vi->chn = vi->root = FREE;
}

void libxmp_virt_setvol(struct context_data *ctx, int chn, int vol)
{
	struct player_data *p = &ctx->p;
	int voc, root;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	root = p->virt.voice_array[voc].root;
	if (root < XMP_MAX_CHANNELS && p->channel_mute[root]) {
		vol = 0;
	}

	libxmp_mixer_setvol(ctx, voc, vol);

	if (vol == 0 && chn >= p->virt.num_tracks) {
		libxmp_virt_resetvoice(ctx, voc, 1);
	}
}

void libxmp_virt_release(struct context_data *ctx, int chn, int rel)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	libxmp_mixer_release(ctx, voc, rel);
}

void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	libxmp_mixer_setpan(ctx, voc, pan);
}

void libxmp_virt_seteffect(struct context_data *ctx, int chn, int type, int val)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	libxmp_mixer_seteffect(ctx, voc, type, val);
}

double libxmp_virt_getvoicepos(struct context_data *ctx, int chn)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return -1;
	}

	return libxmp_mixer_getvoicepos(ctx, voc);
}

#ifndef LIBXMP_CORE_PLAYER

void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi;
	double pos;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	vi = &p->virt.voice_array[voc];
	if (vi->smp == smp) {
		return;
	}

	pos = libxmp_mixer_getvoicepos(ctx, voc);
	libxmp_mixer_setpatch(ctx, voc, smp, 0);
	libxmp_mixer_voicepos(ctx, voc, pos, 0);	/* Restore old position */
}

#endif

#ifndef LIBXMP_CORE_DISABLE_IT

void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	p->virt.voice_array[voc].act = nna;
}

static void check_dct(struct context_data *ctx, int i, int chn, int ins,
			int smp, int note, int nna, int dct, int dca)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[i];
	int voc;

	voc = p->virt.virt_channel[chn].map;

	if (vi->root == chn && vi->ins == ins) {

		if (nna == XMP_INST_NNA_CUT) {
		    libxmp_virt_resetvoice(ctx, i, 1);
		    return;
		}

		vi->act = nna;

		if ((dct == XMP_INST_DCT_INST) ||
		    (dct == XMP_INST_DCT_SMP && vi->smp == smp) ||
		    (dct == XMP_INST_DCT_NOTE && vi->note == note)) {

			if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) {
				vi->act = VIRT_ACTION_OFF;
			} else if (dca) {
				if (i != voc || vi->act) {
					vi->act = dca;
				}
			} else {
				libxmp_virt_resetvoice(ctx, i, 1);
			}
		}
	}
}

#endif

/* For note slides */
void libxmp_virt_setnote(struct context_data *ctx, int chn, int note)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	libxmp_mixer_setnote(ctx, voc, note);
}

int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
		    			int note, int nna, int dct, int dca)
{
	struct player_data *p = &ctx->p;
	int voc, vfree;

	if ((uint32)chn >= p->virt.virt_channels) {
		return -1;
	}

	if (ins < 0) {
		smp = -1;
	}

#ifndef LIBXMP_CORE_DISABLE_IT
	if (dct) {
		int i;

		for (i = 0; i < p->virt.maxvoc; i++) {
			check_dct(ctx, i, chn, ins, smp, note, nna, dct, dca);
		}
	}
#endif

	voc = p->virt.virt_channel[chn].map;

	if (voc > FREE) {
		if (p->virt.voice_array[voc].act) {
			vfree = alloc_voice(ctx, chn);

			if (vfree < 0) {
				return -1;
			}

			for (chn = p->virt.num_tracks;
			     p->virt.virt_channel[chn++].map > FREE;) ;

			p->virt.voice_array[voc].chn = --chn;
			p->virt.virt_channel[chn].map = voc;
			voc = vfree;
		}
	} else {
		voc = alloc_voice(ctx, chn);
		if (voc < 0) {
			return -1;
		}
	}

	if (smp < 0) {
		libxmp_virt_resetvoice(ctx, voc, 1);
		return chn;	/* was -1 */
	}

	libxmp_mixer_setpatch(ctx, voc, smp, 1);
	libxmp_mixer_setnote(ctx, voc, note);
	p->virt.voice_array[voc].ins = ins;
	p->virt.voice_array[voc].act = nna;

	return chn;
}

void libxmp_virt_setperiod(struct context_data *ctx, int chn, double period)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	libxmp_mixer_setperiod(ctx, voc, period);
}

void libxmp_virt_voicepos(struct context_data *ctx, int chn, double pos)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return;
	}

	libxmp_mixer_voicepos(ctx, voc, pos, 1);
}

#ifndef LIBXMP_CORE_DISABLE_IT

void libxmp_virt_pastnote(struct context_data *ctx, int chn, int act)
{
	struct player_data *p = &ctx->p;
	int c, voc;

	for (c = p->virt.num_tracks; c < p->virt.virt_channels; c++) {
		if ((voc = map_virt_channel(p, c)) < 0)
			continue;

		if (p->virt.voice_array[voc].root == chn) {
			switch (act) {
			case VIRT_ACTION_CUT:
				libxmp_virt_resetvoice(ctx, voc, 1);
				break;
			case VIRT_ACTION_OFF:
				libxmp_player_set_release(ctx, c);
				break;
			case VIRT_ACTION_FADE:
				libxmp_player_set_fadeout(ctx, c);
				break;
			}
		}
	}
}

#endif

int libxmp_virt_cstat(struct context_data *ctx, int chn)
{
	struct player_data *p = &ctx->p;
	int voc;

	if ((voc = map_virt_channel(p, chn)) < 0) {
		return VIRT_INVALID;
	}

	if (chn < p->virt.num_tracks) {
		return VIRT_ACTIVE;
	}

	return p->virt.voice_array[voc].act;
}


code:
#ifndef LIBXMP_VIRTUAL_H
#define LIBXMP_VIRTUAL_H

#include "common.h"

#define VIRT_ACTION_CUT		XMP_INST_NNA_CUT
#define VIRT_ACTION_CONT	XMP_INST_NNA_CONT
#define VIRT_ACTION_OFF		XMP_INST_NNA_OFF
#define VIRT_ACTION_FADE	XMP_INST_NNA_FADE

#define VIRT_ACTIVE		0x100
#define VIRT_INVALID		-1

int	libxmp_virt_on		(struct context_data *, int);
void	libxmp_virt_off		(struct context_data *);
int	libxmp_virt_mute	(struct context_data *, int, int);
int	libxmp_virt_setpatch	(struct context_data *, int, int, int, int,
				 int, int, int);
int	libxmp_virt_cvt8bit	(void);
void	libxmp_virt_setnote	(struct context_data *, int, int);
void	libxmp_virt_setsmp	(struct context_data *, int, int);
void	libxmp_virt_setnna	(struct context_data *, int, int);
void	libxmp_virt_pastnote	(struct context_data *, int, int);
void	libxmp_virt_setvol	(struct context_data *, int, int);
void	libxmp_virt_voicepos	(struct context_data *, int, double);
double	libxmp_virt_getvoicepos	(struct context_data *, int);
void	libxmp_virt_setperiod	(struct context_data *, int, double);
void	libxmp_virt_setpan	(struct context_data *, int, int);
void	libxmp_virt_seteffect	(struct context_data *, int, int, int);
int	libxmp_virt_cstat	(struct context_data *, int);
int	libxmp_virt_mapchannel	(struct context_data *, int);
void	libxmp_virt_resetchannel(struct context_data *, int);
void	libxmp_virt_resetvoice	(struct context_data *, int, int);
void	libxmp_virt_reset	(struct context_data *);
void	libxmp_virt_release	(struct context_data *, int, int);
int	libxmp_virt_getroot	(struct context_data *, int);

#endif /* LIBXMP_VIRTUAL_H */


code:
/* _[v]snprintf() from msvcrt.dll might not nul terminate */
/* OpenWatcom-provided versions seem to behave the same... */

#include "common.h"

#if defined(USE_LIBXMP_SNPRINTF)

#undef snprintf
#undef vsnprintf

int libxmp_vsnprintf(char *str, size_t sz, const char *fmt, va_list ap)
{
	int rc = _vsnprintf(str, sz, fmt, ap);
	if (sz != 0) {
		if (rc < 0) rc = (int)sz;
		if ((size_t)rc >= sz) str[sz - 1] = '\0';
	}
	return rc;
}

int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...)
{
	va_list ap;
	int rc;

	va_start (ap, fmt);
	rc = _vsnprintf(str, sz, fmt, ap);
	va_end (ap);

	return rc;
}

#endif


code:
#ifndef LIBXMP_LOADERS_XM_H
#define LIBXMP_LOADERS_XM_H

#define XM_EVENT_PACKING 0x80
#define XM_EVENT_PACK_MASK 0x7f
#define XM_EVENT_NOTE_FOLLOWS 0x01
#define XM_EVENT_INSTRUMENT_FOLLOWS 0x02
#define XM_EVENT_VOLUME_FOLLOWS 0x04
#define XM_EVENT_FXTYPE_FOLLOWS 0x08
#define XM_EVENT_FXPARM_FOLLOWS 0x10
#define XM_LINEAR_FREQ 0x01
#define XM_LOOP_MASK 0x03
#define XM_LOOP_NONE 0
#define XM_LOOP_FORWARD 1
#define XM_LOOP_PINGPONG 2
#define XM_SAMPLE_16BIT 0x10
#define XM_ENVELOPE_ON 0x01
#define XM_ENVELOPE_SUSTAIN 0x02
#define XM_ENVELOPE_LOOP 0x04
#define XM_LINEAR_PERIOD_MODE 0x01

struct xm_file_header {
	uint8 id[17];		/* ID text: "Extended module: " */
	uint8 name[20];		/* Module name, padded with zeroes */
	uint8 doseof;		/* 0x1a */
	uint8 tracker[20];	/* Tracker name */
	uint16 version;		/* Version number, minor-major */
	uint32 headersz;	/* Header size */
	uint16 songlen;		/* Song length (in patten order table) */
	uint16 restart;		/* Restart position */
	uint16 channels;	/* Number of channels (2,4,6,8,10,...,32) */
	uint16 patterns;	/* Number of patterns (max 256) */
	uint16 instruments;	/* Number of instruments (max 128) */
	uint16 flags;		/* bit 0: 0=Amiga freq table, 1=Linear */
	uint16 tempo;		/* Default tempo */
	uint16 bpm;		/* Default BPM */
	uint8 order[256];	/* Pattern order table */
};

struct xm_pattern_header {
	uint32 length;		/* Pattern header length */
	uint8 packing;		/* Packing type (always 0) */
	uint16 rows;		/* Number of rows in pattern (1..256) */
	uint16 datasize;	/* Packed patterndata size */
};

struct xm_instrument_header {
	uint32 size;		/* Instrument size */
	uint8 name[22];		/* Instrument name */
	uint8 type;		/* Instrument type (always 0) */
	uint16 samples;		/* Number of samples in instrument */
	uint32 sh_size;		/* Sample header size */
};

struct xm_instrument {
	uint8 sample[96];	/* Sample number for all notes */
	uint16 v_env[24];	/* Points for volume envelope */
	uint16 p_env[24];	/* Points for panning envelope */
	uint8 v_pts;		/* Number of volume points */
	uint8 p_pts;		/* Number of panning points */
	uint8 v_sus;		/* Volume sustain point */
	uint8 v_start;		/* Volume loop start point */
	uint8 v_end;		/* Volume loop end point */
	uint8 p_sus;		/* Panning sustain point */
	uint8 p_start;		/* Panning loop start point */
	uint8 p_end;		/* Panning loop end point */
	uint8 v_type;		/* Bit 0: On; 1: Sustain; 2: Loop */
	uint8 p_type;		/* Bit 0: On; 1: Sustain; 2: Loop */
	uint8 y_wave;		/* Vibrato waveform */
	uint8 y_sweep;		/* Vibrato sweep */
	uint8 y_depth;		/* Vibrato depth */
	uint8 y_rate;		/* Vibrato rate */
	uint16 v_fade;		/* Volume fadeout */
#if 0
	uint8 reserved[22];	/* Reserved; 2 bytes in specs, 22 in 1.04 */
#endif
};

struct xm_sample_header {
	uint32 length;		/* Sample length */
	uint32 loop_start;	/* Sample loop start */
	uint32 loop_length;	/* Sample loop length */
	uint8 volume;		/* Volume */
	int8 finetune;		/* Finetune (signed byte -128..+127) */
	uint8 type;		/* 0=No loop,1=Fwd loop,2=Ping-pong,16-bit */
	uint8 pan;		/* Panning (0-255) */
	int8 relnote;		/* Relative note number (signed byte) */
	uint8 reserved;		/* Reserved */
	uint8 name[22];		/* Sample_name */
};

struct xm_event {
	uint8 note;		/* Note (0-71, 0 = C-0) */
	uint8 instrument;	/* Instrument (0-128) */
	uint8 volume;		/* Volume column byte */
	uint8 fx_type;		/* Effect type */
	uint8 fx_parm;		/* Effect parameter */
};

#endif


code:
/* 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);

extern const struct format_loader libxmp_loader_xm;
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 = (uint8 *)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;
}