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 "player.h"
#include "effects.h"
#include "period.h"
#include "virtual.h"
#include "mixer.h"
#ifndef LIBXMP_CORE_PLAYER
#include "extras.h"
#endif

#define NOT_IMPLEMENTED
#define HAS_QUIRK(x) (m->quirk & (x))

#define SET_LFO_NOTZERO(lfo, depth, rate) do { \
	if ((depth) != 0) libxmp_lfo_set_depth(lfo, depth); \
	if ((rate) != 0)  libxmp_lfo_set_rate(lfo, rate); \
} while (0)

#define EFFECT_MEMORY__(p, m) do { \
	if ((p) == 0) { (p) = (m); } else { (m) = (p); } \
} while (0)

/* ST3 effect memory is not a bug, but it's a weird implementation and it's
 * unlikely to be supported in anything other than ST3 (or OpenMPT).
 */
#define EFFECT_MEMORY(p, m) do { \
	if (HAS_QUIRK(QUIRK_ST3BUGS)) { \
		EFFECT_MEMORY__((p), xc->vol.memory); \
	} else { \
		EFFECT_MEMORY__((p), (m)); \
	} \
} while (0)

#define EFFECT_MEMORY_SETONLY(p, m) do { \
	EFFECT_MEMORY__((p), (m)); \
	if (HAS_QUIRK(QUIRK_ST3BUGS)) { \
		if ((p) != 0) { xc->vol.memory = (p); } \
	} \
} while (0)

#define EFFECT_MEMORY_S3M(p) do { \
	if (HAS_QUIRK(QUIRK_ST3BUGS)) { \
		EFFECT_MEMORY__((p), xc->vol.memory); \
	} \
} while (0)

static void do_toneporta(struct context_data *ctx,
                                struct channel_data *xc, int note)
{
	struct module_data *m = &ctx->m;
	struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
	int mapped = instrument->map[xc->key].ins;
	struct xmp_subinstrument *sub;

	if (mapped >= instrument->nsm) {
		mapped = 0;
	}

	sub = &instrument->sub[mapped];

	if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) {
		note--;
		xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo +
			instrument->map[xc->key_porta].xpo, xc->finetune,
			xc->per_adj);
	}
	xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
}

void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int chn,
		struct xmp_event *e, int fnum)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct flow_control *f = &p->flow;
	uint8 note, fxp, fxt;
	int h, l;

	/* key_porta is IT only */
	if (m->read_event_type != READ_EVENT_IT) {
		xc->key_porta = xc->key;
	}

	note = e->note;
	if (fnum == 0) {
		fxt = e->fxt;
		fxp = e->fxp;
	} else {
		fxt = e->f2t;
		fxp = e->f2p;
	}

	switch (fxt) {
	case FX_ARPEGGIO:
	      fx_arpeggio:
		if (!HAS_QUIRK(QUIRK_ARPMEM) || fxp != 0) {
			xc->arpeggio.val[0] = 0;
			xc->arpeggio.val[1] = MSN(fxp);
			xc->arpeggio.val[2] = LSN(fxp);
			xc->arpeggio.size = 3;
		}
		break;
	case FX_S3M_ARPEGGIO:
		EFFECT_MEMORY(fxp, xc->arpeggio.memory);
		goto fx_arpeggio;

#ifndef LIBXMP_CORE_PLAYER
	case FX_OKT_ARP3:
		if (fxp != 0) {
			xc->arpeggio.val[0] = -MSN(fxp);
			xc->arpeggio.val[1] = 0;
			xc->arpeggio.val[2] = LSN(fxp);
			xc->arpeggio.size = 3;
		}
		break;
	case FX_OKT_ARP4:
		if (fxp != 0) {
			xc->arpeggio.val[0] = 0;
			xc->arpeggio.val[1] = LSN(fxp);
			xc->arpeggio.val[2] = 0;
			xc->arpeggio.val[3] = -MSN(fxp);
			xc->arpeggio.size = 4;
		}
		break;
	case FX_OKT_ARP5:
		if (fxp != 0) {
			xc->arpeggio.val[0] = LSN(fxp);
			xc->arpeggio.val[1] = LSN(fxp);
			xc->arpeggio.val[2] = 0;
			xc->arpeggio.size = 3;
		}
		break;
#endif
	case FX_PORTA_UP:	/* Portamento up */
		EFFECT_MEMORY(fxp, xc->freq.memory);

		if (HAS_QUIRK(QUIRK_FINEFX)
		    && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
			switch (MSN(fxp)) {
			case 0xf:
				fxp &= 0x0f;
				goto fx_f_porta_up;
			case 0xe:
				fxp &= 0x0f;
				fxp |= 0x10;
				goto fx_xf_porta;
			}
		}

		SET(PITCHBEND);

		if (fxp != 0) {
			xc->freq.slide = -fxp;
			if (HAS_QUIRK(QUIRK_UNISLD))
				xc->porta.memory = fxp;
		} else if (xc->freq.slide > 0) {
			xc->freq.slide *= -1;
		}
		break;
	case FX_PORTA_DN:	/* Portamento down */
		EFFECT_MEMORY(fxp, xc->freq.memory);

		if (HAS_QUIRK(QUIRK_FINEFX)
		    && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
			switch (MSN(fxp)) {
			case 0xf:
				fxp &= 0x0f;
				goto fx_f_porta_dn;
			case 0xe:
				fxp &= 0x0f;
				fxp |= 0x20;
				goto fx_xf_porta;
			}
		}

		SET(PITCHBEND);

		if (fxp != 0) {
			xc->freq.slide = fxp;
			if (HAS_QUIRK(QUIRK_UNISLD))
				xc->porta.memory = fxp;
		} else if (xc->freq.slide < 0) {
			xc->freq.slide *= -1;
		}
		break;
	case FX_TONEPORTA:	/* Tone portamento */
		EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory);

		if (fxp != 0) {
			if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */
				xc->freq.memory = fxp;
			xc->porta.slide = fxp;
		}

		if (HAS_QUIRK(QUIRK_IGSTPOR)) {
			if (note == 0 && xc->porta.dir == 0)
				break;
		}

		if (!IS_VALID_INSTRUMENT(xc->ins))
			break;

		do_toneporta(ctx, xc, note);

		SET(TONEPORTA);
		break;

	case FX_VIBRATO:	/* Vibrato */
		EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);

		SET(VIBRATO);
		SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp));
		break;
	case FX_FINE_VIBRATO:	/* Fine vibrato */
		EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);

		SET(VIBRATO);
		SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp));
		break;

	case FX_TONE_VSLIDE:	/* Toneporta + vol slide */
		if (!IS_VALID_INSTRUMENT(xc->ins))
			break;
		do_toneporta(ctx, xc, note);
		SET(TONEPORTA);
		goto fx_volslide;
	case FX_VIBRA_VSLIDE:	/* Vibrato + vol slide */
		SET(VIBRATO);
		goto fx_volslide;

	case FX_TREMOLO:	/* Tremolo */
		EFFECT_MEMORY(fxp, xc->tremolo.memory);
		SET(TREMOLO);
		SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp));
		break;

	case FX_SETPAN:		/* Set pan */
		if (HAS_QUIRK(QUIRK_PROTRACK)) {
			break;
		}
	    fx_setpan:
		/* From OpenMPT PanOff.xm:
		 * "Another chapter of weird FT2 bugs: Note-Off + Note Delay
		 *  + Volume Column Panning = Panning effect is ignored."
		 */
		if (!HAS_QUIRK(QUIRK_FT2BUGS)		/* If not FT2 */
		    || fnum == 0			/* or not vol column */
		    || e->note != XMP_KEY_OFF		/* or not keyoff */
		    || e->fxt != FX_EXTENDED		/* or not delay */
		    || MSN(e->fxp) != EX_DELAY) {
			xc->pan.val = fxp;
			xc->pan.surround = 0;
		}
		xc->rpv = 0;	/* storlek_20: set pan overrides random pan */
		xc->pan.surround = 0;
		break;
	case FX_OFFSET:		/* Set sample offset */
		EFFECT_MEMORY(fxp, xc->offset.memory);
		SET(OFFSET);
		if (note) {
			xc->offset.val &= xc->offset.val & ~0xffff;
			xc->offset.val |= fxp << 8;
			xc->offset.val2 = fxp << 8;
		}
		if (e->ins) {
			xc->offset.val2 = fxp << 8;
		}
		break;
	case FX_VOLSLIDE:	/* Volume slide */
	      fx_volslide:
		/* S3M file volume slide note:
		 * DFy  Fine volume down by y (...) If y is 0, the command will
		 *      be treated as a volume slide up with a value of f (15).
		 *      If a DFF command is specified, the volume will be slid
		 *      up.
		 */
		if (HAS_QUIRK(QUIRK_FINEFX)) {
			h = MSN(fxp);
			l = LSN(fxp);
			if (l == 0xf && h != 0) {
				xc->vol.memory = fxp;
				fxp >>= 4;
				goto fx_f_vslide_up;
			} else if (h == 0xf && l != 0) {
				xc->vol.memory = fxp;
				fxp &= 0x0f;
				goto fx_f_vslide_dn;
			}
		}

		/* recover memory */
		if (fxp == 0x00) {
			if ((fxp = xc->vol.memory) != 0)
				goto fx_volslide;
		}

		SET(VOL_SLIDE);

		/* Skaven's 2nd reality (S3M) has volslide parameter D7 => pri
		 * down. Other trackers only compute volumes if the other
		 * parameter is 0, Fall from sky.xm has 2C => do nothing.
		 * Also don't assign xc->vol.memory if fxp is 0, see Guild
		 * of Sounds.xm
		 */
		if (fxp) {
			xc->vol.memory = fxp;
			h = MSN(fxp);
			l = LSN(fxp);
			if (fxp) {
				if (HAS_QUIRK(QUIRK_VOLPDN)) {
					xc->vol.slide = l ? -l : h;
				} else {
					xc->vol.slide = h ? h : -l;
				}
			}
		}

		/* Mirko reports that a S3M with D0F effects created with ST321
		 * should process volume slides in all frames like ST300. I
		 * suspect ST3/IT could be handling D0F effects like this.
		 */
		if (HAS_QUIRK(QUIRK_FINEFX)) {
			if (MSN(xc->vol.memory) == 0xf
			    || LSN(xc->vol.memory) == 0xf) {
				SET(FINE_VOLS);
				xc->vol.fslide = xc->vol.slide;
			}
		}
		break;
	case FX_VOLSLIDE_2:	/* Secondary volume slide */
		SET(VOL_SLIDE_2);
		if (fxp) {
			h = MSN(fxp);
			l = LSN(fxp);
			xc->vol.slide2 = h ? h : -l;
		}
		break;
	case FX_JUMP:		/* Order jump */
		p->flow.pbreak = 1;
		p->flow.jump = fxp;
		/* effect B resets effect D in lower channels */
		p->flow.jumpline = 0;
		break;
	case FX_VOLSET:		/* Volume set */
		SET(NEW_VOL);
		xc->volume = fxp;
		if (xc->split) {
			p->xc_data[xc->pair].volume = xc->volume;
		}
		break;
	case FX_BREAK:		/* Pattern break */
		p->flow.pbreak = 1;
		p->flow.jumpline = 10 * MSN(fxp) + LSN(fxp);
		break;
	case FX_EXTENDED:	/* Extended effect */
		EFFECT_MEMORY_S3M(fxp);
		fxt = fxp >> 4;
		fxp &= 0x0f;
		switch (fxt) {
		case EX_FILTER:		/* Amiga led filter */
			if (IS_AMIGA_MOD()) {
				p->filter = !(fxp & 1);
			}
			break;
		case EX_F_PORTA_UP:	/* Fine portamento up */
			EFFECT_MEMORY(fxp, xc->fine_porta.up_memory);
			goto fx_f_porta_up;
		case EX_F_PORTA_DN:	/* Fine portamento down */
			EFFECT_MEMORY(fxp, xc->fine_porta.down_memory);
			goto fx_f_porta_dn;
		case EX_GLISS:		/* Glissando toggle */
			if (fxp) {
				SET_NOTE(NOTE_GLISSANDO);
			} else {
				RESET_NOTE(NOTE_GLISSANDO);
			}
			break;
		case EX_VIBRATO_WF:	/* Set vibrato waveform */
			fxp &= 3;
			libxmp_lfo_set_waveform(&xc->vibrato.lfo, fxp);
			break;
		case EX_FINETUNE:	/* Set finetune */
			if (!HAS_QUIRK(QUIRK_FT2BUGS) || note > 0) {
				xc->finetune = (int8)(fxp << 4);
			}
			break;
		case EX_PATTERN_LOOP:	/* Loop pattern */
			if (fxp == 0) {
				/* mark start of loop */
				f->loop[chn].start = p->row;
				if (HAS_QUIRK(QUIRK_FT2BUGS))
				  p->flow.jumpline = p->row;
			} else {
				/* end of loop */
				if (f->loop[chn].count) {
					if (--f->loop[chn].count) {
						/* **** H:FIXME **** */
						f->loop_chn = ++chn;
					} else {
						if (HAS_QUIRK(QUIRK_S3MLOOP))
							f->loop[chn].start =
							    p->row + 1;
					}
				} else {
					f->loop[chn].count = fxp;
					f->loop_chn = ++chn;
				}
			}
			break;
		case EX_TREMOLO_WF:	/* Set tremolo waveform */
			libxmp_lfo_set_waveform(&xc->tremolo.lfo, fxp & 3);
			break;
		case EX_SETPAN:
			fxp <<= 4;
			goto fx_setpan;
		case EX_RETRIG:		/* Retrig note */
			SET(RETRIG);
			xc->retrig.val = fxp;
			xc->retrig.count = LSN(xc->retrig.val) + 1;
			xc->retrig.type = 0;
			break;
		case EX_F_VSLIDE_UP:	/* Fine volume slide up */
			EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
			goto fx_f_vslide_up;
		case EX_F_VSLIDE_DN:	/* Fine volume slide down */
			EFFECT_MEMORY(fxp, xc->fine_vol.down_memory);
			goto fx_f_vslide_dn;
		case EX_CUT:		/* Cut note */
			SET(RETRIG);
			SET_NOTE(NOTE_CUT);	/* for IT cut-carry */
			xc->retrig.val = fxp + 1;
			xc->retrig.count = xc->retrig.val;
			xc->retrig.type = 0x10;
			break;
		case EX_DELAY:		/* Note delay */
			/* computed at frame loop */
			break;
		case EX_PATT_DELAY:	/* Pattern delay */
			goto fx_patt_delay;
		case EX_INVLOOP:	/* Invert loop / funk repeat */
			xc->invloop.speed = fxp;
			break;
		}
		break;
	case FX_SPEED:		/* Set speed */
		if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK) {
			goto fx_s3m_speed;
		}

		/* speedup.xm needs BPM = 20 */
		if (fxp < 0x20) {
			goto fx_s3m_speed;
		}
		goto fx_s3m_bpm;

	case FX_FINETUNE:
		xc->finetune = (int16) (fxp - 0x80);
		break;

	case FX_F_VSLIDE_UP:	/* Fine volume slide up */
		EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
	    fx_f_vslide_up:
		SET(FINE_VOLS);
		xc->vol.fslide = fxp;
		break;
	case FX_F_VSLIDE_DN:	/* Fine volume slide down */
		EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
	    fx_f_vslide_dn:
		SET(FINE_VOLS);
		xc->vol.fslide = -fxp;
		break;

	case FX_F_PORTA_UP:	/* Fine portamento up */
	    fx_f_porta_up:
		if (fxp) {
			SET(FINE_BEND);
			xc->freq.fslide = -fxp;
		}
		break;
	case FX_F_PORTA_DN:	/* Fine portamento down */
	    fx_f_porta_dn:
		if (fxp) {
			SET(FINE_BEND);
			xc->freq.fslide = fxp;
		}
		break;
	case FX_PATT_DELAY:
	    fx_patt_delay:
		if (m->read_event_type != READ_EVENT_ST3 || !p->flow.delay) {
			p->flow.delay = fxp;
		}
		break;

	case FX_S3M_SPEED:	/* Set S3M speed */
		EFFECT_MEMORY_S3M(fxp);
	    fx_s3m_speed:
		if (fxp) {
			p->speed = fxp;
#ifndef LIBXMP_CORE_PLAYER
			p->st26_speed = 0;
#endif
		}
		break;
	case FX_S3M_BPM:	/* Set S3M BPM */
	    fx_s3m_bpm: {
		/* Lower time factor in MED allows lower BPM values */
		int min_bpm = (int)(0.5 + m->time_factor * XMP_MIN_BPM / 10);
		if (fxp < min_bpm)
			fxp = min_bpm;
		p->bpm = fxp;
		p->frame_time = m->time_factor * m->rrate / p->bpm;
		break;
	}

#ifndef LIBXMP_CORE_DISABLE_IT
	case FX_IT_BPM:		/* Set IT BPM */
		if (MSN(fxp) == 0) {
			SET(TEMPO_SLIDE);
			if (LSN(fxp))	/* T0x - Tempo slide down by x */
				xc->tempo.slide = -LSN(fxp);
			/* T00 - Repeat previous slide */
		} else if (MSN(fxp) == 1) {	/* T1x - Tempo slide up by x */
			SET(TEMPO_SLIDE);
			xc->tempo.slide = LSN(fxp);
		} else {
			if (fxp < XMP_MIN_BPM)
				fxp = XMP_MIN_BPM;
			p->bpm = fxp;
		}
		p->frame_time = m->time_factor * m->rrate / p->bpm;
		break;
	case FX_IT_ROWDELAY:
		if (!f->rowdelay_set) {
			f->rowdelay = fxp;
			f->rowdelay_set = ROWDELAY_ON | ROWDELAY_FIRST_FRAME;
		}
		break;

	/* From the OpenMPT VolColMemory.it test case:
	 * "Volume column commands a, b, c and d (volume slide) share one
	 *  effect memory, but it should not be shared with Dxy in the effect
	 *  column.
	 */
	case FX_VSLIDE_UP_2:	/* Fine volume slide up */
		EFFECT_MEMORY(fxp, xc->vol.memory2);
		SET(VOL_SLIDE_2);
		xc->vol.slide2 = fxp;
		break;
	case FX_VSLIDE_DN_2:	/* Fine volume slide down */
		EFFECT_MEMORY(fxp, xc->vol.memory2);
		SET(VOL_SLIDE_2);
		xc->vol.slide2 = -fxp;
		break;
	case FX_F_VSLIDE_UP_2:	/* Fine volume slide up */
		EFFECT_MEMORY(fxp, xc->vol.memory2);
		SET(FINE_VOLS_2);
		xc->vol.fslide2 = fxp;
		break;
	case FX_F_VSLIDE_DN_2:	/* Fine volume slide down */
		EFFECT_MEMORY(fxp, xc->vol.memory2);
		SET(FINE_VOLS_2);
		xc->vol.fslide2 = -fxp;
		break;
	case FX_IT_BREAK:	/* Pattern break with hex parameter */
		if (!f->loop_chn)
		{
			p->flow.pbreak = 1;
			p->flow.jumpline = fxp;
		}
		break;

#endif

	case FX_GLOBALVOL:	/* Set global volume */
		if (fxp > m->gvolbase) {
			p->gvol = m->gvolbase;
		} else {
			p->gvol = fxp;
		}
		break;
	case FX_GVOL_SLIDE:	/* Global volume slide */
            fx_gvolslide:
		if (fxp) {
			SET(GVOL_SLIDE);
			xc->gvol.memory = fxp;
			h = MSN(fxp);
			l = LSN(fxp);

			if (HAS_QUIRK(QUIRK_FINEFX)) {
				if (l == 0xf && h != 0) {
					xc->gvol.slide = 0;
					xc->gvol.fslide = h;
				} else if (h == 0xf && l != 0) {
					xc->gvol.slide = 0;
					xc->gvol.fslide = -l;
				} else {
					xc->gvol.slide = h ? h : -l;
					xc->gvol.fslide = 0;
				}
			} else {
				xc->gvol.slide = h ? h : -l;
				xc->gvol.fslide = 0;
			}
		} else {
			if ((fxp = xc->gvol.memory) != 0) {
				goto fx_gvolslide;
			}
		}
		break;
	case FX_KEYOFF:		/* Key off */
		xc->keyoff = fxp + 1;
		break;
	case FX_ENVPOS:		/* Set envelope position */
		/* From OpenMPT SetEnvPos.xm:
		 * "When using the Lxx effect, Fasttracker 2 only sets the
		 *  panning envelope position if the volume envelope’s sustain
		 *  flag is set.
		 */
		if (HAS_QUIRK(QUIRK_FT2BUGS)) {
			struct xmp_instrument *instrument;
			instrument = libxmp_get_instrument(ctx, xc->ins);
			if (instrument != NULL) {
				if (instrument->aei.flg & XMP_ENVELOPE_SUS) {
					xc->p_idx = fxp;
				}
			}
		} else {
			xc->p_idx = fxp;
		}
		xc->v_idx = fxp;
		xc->f_idx = fxp;
		break;
	case FX_PANSLIDE:	/* Pan slide (XM) */
		EFFECT_MEMORY(fxp, xc->pan.memory);
		SET(PAN_SLIDE);
		xc->pan.slide = LSN(fxp) - MSN(fxp);
		break;
	case FX_PANSL_NOMEM:	/* Pan slide (XM volume column) */
		SET(PAN_SLIDE);
		xc->pan.slide = LSN(fxp) - MSN(fxp);
		break;

#ifndef LIBXMP_CORE_DISABLE_IT
	case FX_IT_PANSLIDE:	/* Pan slide w/ fine pan (IT) */
		SET(PAN_SLIDE);
		if (fxp) {
			if (MSN(fxp) == 0xf) {
				xc->pan.slide = 0;
				xc->pan.fslide = LSN(fxp);
			} else if (LSN(fxp) == 0xf) {
				xc->pan.slide = 0;
				xc->pan.fslide = -MSN(fxp);
			} else {
				SET(PAN_SLIDE);
				xc->pan.slide = LSN(fxp) - MSN(fxp);
				xc->pan.fslide = 0;
			}
		}
		break;
#endif

	case FX_MULTI_RETRIG:	/* Multi retrig */
		EFFECT_MEMORY_S3M(fxp);
		if (fxp) {
			xc->retrig.val = fxp;
			xc->retrig.type = MSN(xc->retrig.val);
		}
		if (note) {
			xc->retrig.count = LSN(xc->retrig.val) + 1;
		}
		SET(RETRIG);
		break;
	case FX_TREMOR:			/* Tremor */
		EFFECT_MEMORY(fxp, xc->tremor.memory);
		xc->tremor.up = MSN(fxp);
		xc->tremor.down = LSN(fxp);
		if (IS_PLAYER_MODE_FT2()) {
			xc->tremor.count |= 0x80;
		} else {
			if (xc->tremor.up == 0) {
				xc->tremor.up++;
			}
			if (xc->tremor.down == 0) {
				xc->tremor.down++;
			}
		}
		SET(TREMOR);
		break;
	case FX_XF_PORTA:	/* Extra fine portamento */
	      fx_xf_porta:
		SET(FINE_BEND);
		switch (MSN(fxp)) {
		case 1:
			xc->freq.fslide = -0.25 * LSN(fxp);
			break;
		case 2:
			xc->freq.fslide = 0.25 * LSN(fxp);
			break;
		}
		break;
	case FX_SURROUND:
		xc->pan.surround = fxp;
		break;

#ifndef LIBXMP_CORE_DISABLE_IT
	case FX_TRK_VOL:	/* Track volume setting */
		if (fxp <= m->volbase) {
			xc->mastervol = fxp;
		}
		break;
	case FX_TRK_VSLIDE:	/* Track volume slide */
		if (fxp == 0) {
			if ((fxp = xc->trackvol.memory) == 0)
				break;
		}

		if (HAS_QUIRK(QUIRK_FINEFX)) {
			h = MSN(fxp);
			l = LSN(fxp);
			if (h == 0xf && l != 0) {
				xc->trackvol.memory = fxp;
				fxp &= 0x0f;
				goto fx_trk_fvslide;
			} else if (l == 0xf && h != 0) {
				xc->trackvol.memory = fxp;
				fxp &= 0xf0;
				goto fx_trk_fvslide;
			}
		}

		SET(TRK_VSLIDE);
		if (fxp) {
			h = MSN(fxp);
			l = LSN(fxp);

			xc->trackvol.memory = fxp;
			if (HAS_QUIRK(QUIRK_VOLPDN)) {
				xc->trackvol.slide = l ? -l : h;
			} else {
				xc->trackvol.slide = h ? h : -l;
			}
		}

		break;
	case FX_TRK_FVSLIDE:	/* Track fine volume slide */
	      fx_trk_fvslide:
		SET(TRK_FVSLIDE);
		if (fxp) {
			xc->trackvol.fslide = MSN(fxp) - LSN(fxp);
		}
		break;

	case FX_IT_INSTFUNC:
		switch (fxp) {
		case 0:	/* Past note cut */
			libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_CUT);
			break;
		case 1:	/* Past note off */
			libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_OFF);
			break;
		case 2:	/* Past note fade */
			libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_FADE);
			break;
		case 3:	/* Set NNA to note cut */
			libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT);
			break;
		case 4:	/* Set NNA to continue */
			libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CONT);
			break;
		case 5:	/* Set NNA to note off */
			libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_OFF);
			break;
		case 6:	/* Set NNA to note fade */
			libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_FADE);
			break;
		case 7:	/* Turn off volume envelope */
			SET_PER(VENV_PAUSE);
			break;
		case 8:	/* Turn on volume envelope */
			RESET_PER(VENV_PAUSE);
			break;
		case 9:	/* Turn off pan envelope */
			SET_PER(PENV_PAUSE);
			break;
		case 0xa:	/* Turn on pan envelope */
			RESET_PER(PENV_PAUSE);
			break;
		case 0xb:	/* Turn off pitch envelope */
			SET_PER(FENV_PAUSE);
			break;
		case 0xc:	/* Turn on pitch envelope */
			RESET_PER(FENV_PAUSE);
			break;
		}
		break;
	case FX_FLT_CUTOFF:
		if (fxp < 0xfe || xc->filter.resonance > 0) {
			xc->filter.cutoff = fxp;
		}
		break;
	case FX_FLT_RESN:
		xc->filter.resonance = fxp;
		break;
	case FX_PANBRELLO:	/* Panbrello */
		SET(PANBRELLO);
		SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp));
		break;
	case FX_PANBRELLO_WF:	/* Panbrello waveform */
		libxmp_lfo_set_waveform(&xc->panbrello.lfo, fxp & 3);
		break;
	case FX_HIOFFSET:	/* High offset */
		xc->offset.val &= 0xffff;
		xc->offset.val |= fxp << 16;
		break;
#endif

#ifndef LIBXMP_CORE_PLAYER

	/* SFX effects */
	case FX_VOL_ADD:
		if (!IS_VALID_INSTRUMENT(xc->ins)) {
			break;
		}
		SET(NEW_VOL);
		xc->volume = m->mod.xxi[xc->ins].sub[0].vol + fxp;
		if (xc->volume > m->volbase) {
			xc->volume = m->volbase;
		}
		break;
	case FX_VOL_SUB:
		if (!IS_VALID_INSTRUMENT(xc->ins)) {
			break;
		}
		SET(NEW_VOL);
		xc->volume = m->mod.xxi[xc->ins].sub[0].vol - fxp;
		if (xc->volume < 0) {
			xc->volume =0;
		}
		break;
	case FX_PITCH_ADD:
		SET_PER(TONEPORTA);
		xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0)
			+ fxp;
		xc->porta.slide = 2;
		xc->porta.dir = 1;
		break;
	case FX_PITCH_SUB:
		SET_PER(TONEPORTA);
		xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0)
			- fxp;
		xc->porta.slide = 2;
		xc->porta.dir = -1;
		break;

	/* Saga Musix says:
	 *
	 * "When both nibbles of an Fxx command are set, SoundTracker 2.6
	 * applies the both values alternatingly, first the high nibble,
	 * then the low nibble on the next row, then the high nibble again...
	 * If only the high nibble is set, it should act like if only the low
	 * nibble is set (i.e. F30 is the same as F03).
	 */
	case FX_ICE_SPEED:
		if (fxp) {
			if (LSN(fxp)) {
				p->st26_speed = (MSN(fxp) << 8) | LSN(fxp);
			} else {
				p->st26_speed = MSN(fxp);
			}
		}
		break;

	case FX_VOLSLIDE_UP:	/* Vol slide with uint8 arg */
		if (HAS_QUIRK(QUIRK_FINEFX)) {
			h = MSN(fxp);
			l = LSN(fxp);
			if (h == 0xf && l != 0) {
				fxp &= 0x0f;
				goto fx_f_vslide_up;
			}
		}

		if (fxp)
			xc->vol.slide = fxp;
		SET(VOL_SLIDE);
		break;
	case FX_VOLSLIDE_DN:	/* Vol slide with uint8 arg */
		if (HAS_QUIRK(QUIRK_FINEFX)) {
			h = MSN(fxp);
			l = LSN(fxp);
			if (h == 0xf && l != 0) {
				fxp &= 0x0f;
				goto fx_f_vslide_dn;
			}
		}

		if (fxp)
			xc->vol.slide = -fxp;
		SET(VOL_SLIDE);
		break;
	case FX_F_VSLIDE:	/* Fine volume slide */
		SET(FINE_VOLS);
		if (fxp) {
			h = MSN(fxp);
			l = LSN(fxp);
			xc->vol.fslide = h ? h : -l;
		}
		break;
	case FX_NSLIDE_DN:
	case FX_NSLIDE_UP:
	case FX_NSLIDE_R_DN:
	case FX_NSLIDE_R_UP:
		if (fxp != 0) {
			if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) {
				xc->retrig.val = MSN(fxp);
				xc->retrig.count = MSN(fxp) + 1;
				xc->retrig.type = 0;
			}

			if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP)
				xc->noteslide.slide = LSN(fxp);
			else
				xc->noteslide.slide = -LSN(fxp);

			xc->noteslide.count = xc->noteslide.speed = MSN(fxp);
		}
		if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP)
			SET(RETRIG);
		SET(NOTE_SLIDE);
		break;
	case FX_NSLIDE2_DN:
		SET(NOTE_SLIDE);
		xc->noteslide.slide = -fxp;
		xc->noteslide.count = xc->noteslide.speed = 1;
		break;
	case FX_NSLIDE2_UP:
		SET(NOTE_SLIDE);
		xc->noteslide.slide = fxp;
		xc->noteslide.count = xc->noteslide.speed = 1;
		break;
	case FX_F_NSLIDE_DN:
		SET(FINE_NSLIDE);
		xc->noteslide.fslide = -fxp;
		break;
	case FX_F_NSLIDE_UP:
		SET(FINE_NSLIDE);
		xc->noteslide.fslide = fxp;
		break;

	case FX_PER_VIBRATO:	/* Persistent vibrato */
		if (LSN(fxp) != 0) {
			SET_PER(VIBRATO);
		} else {
			RESET_PER(VIBRATO);
		}
		SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp));
		break;
	case FX_PER_PORTA_UP:	/* Persistent portamento up */
		SET_PER(PITCHBEND);
		xc->freq.slide = -fxp;
		if ((xc->freq.memory = fxp) == 0)
			RESET_PER(PITCHBEND);
		break;
	case FX_PER_PORTA_DN:	/* Persistent portamento down */
		SET_PER(PITCHBEND);
		xc->freq.slide = fxp;
		if ((xc->freq.memory = fxp) == 0)
			RESET_PER(PITCHBEND);
		break;
	case FX_PER_TPORTA:	/* Persistent tone portamento */
		if (!IS_VALID_INSTRUMENT(xc->ins))
			break;
		SET_PER(TONEPORTA);
		do_toneporta(ctx, xc, note);
		xc->porta.slide = fxp;
		if (fxp == 0)
			RESET_PER(TONEPORTA);
		break;
	case FX_PER_VSLD_UP:	/* Persistent volslide up */
		SET_PER(VOL_SLIDE);
		xc->vol.slide = fxp;
		if (fxp == 0)
			RESET_PER(VOL_SLIDE);
		break;
	case FX_PER_VSLD_DN:	/* Persistent volslide down */
		SET_PER(VOL_SLIDE);
		xc->vol.slide = -fxp;
		if (fxp == 0)
			RESET_PER(VOL_SLIDE);
		break;
	case FX_VIBRATO2:	/* Deep vibrato (2x) */
		SET(VIBRATO);
		SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 3, MSN(fxp));
		break;
	case FX_SPEED_CP:	/* Set speed and ... */
		if (fxp) {
			p->speed = fxp;
			p->st26_speed = 0;
		}
		/* fall through */
	case FX_PER_CANCEL:	/* Cancel persistent effects */
		xc->per_flags = 0;
		break;

	/* 669 effects */

	case FX_669_PORTA_UP:	/* 669 portamento up */
		SET_PER(PITCHBEND);
		xc->freq.slide = 80 * fxp;
		if ((xc->freq.memory = fxp) == 0)
			RESET_PER(PITCHBEND);
		break;
	case FX_669_PORTA_DN:	/* 669 portamento down */
		SET_PER(PITCHBEND);
		xc->freq.slide = -80 * fxp;
		if ((xc->freq.memory = fxp) == 0)
			RESET_PER(PITCHBEND);
		break;
	case FX_669_TPORTA:	/* 669 tone portamento */
		if (!IS_VALID_INSTRUMENT(xc->ins))
			break;
		SET_PER(TONEPORTA);
		do_toneporta(ctx, xc, note);
		xc->porta.slide = 40 * fxp;
		if (fxp == 0)
			RESET_PER(TONEPORTA);
		break;
	case FX_669_FINETUNE:	/* 669 finetune */
		xc->finetune = 80 * (int8)fxp;
		break;
	case FX_669_VIBRATO:	/* 669 vibrato */
		if (LSN(fxp) != 0) {
			libxmp_lfo_set_waveform(&xc->vibrato.lfo, 669);
			SET_PER(VIBRATO);
		} else {
			RESET_PER(VIBRATO);
		}
		SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1);
		break;
#endif

	default:
#ifndef LIBXMP_CORE_PLAYER
		libxmp_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum);
#endif
		break;
	}
}


code:
#ifndef LIBXMP_EFFECTS_H
#define LIBXMP_EFFECTS_H

/* Protracker effects */
#define FX_ARPEGGIO	0x00
#define FX_PORTA_UP	0x01
#define FX_PORTA_DN	0x02
#define FX_TONEPORTA	0x03
#define FX_VIBRATO	0x04
#define FX_TONE_VSLIDE  0x05
#define FX_VIBRA_VSLIDE	0x06
#define FX_TREMOLO	0x07
#define FX_OFFSET	0x09
#define FX_VOLSLIDE	0x0a
#define FX_JUMP		0x0b
#define FX_VOLSET	0x0c
#define FX_BREAK	0x0d
#define FX_EXTENDED	0x0e
#define FX_SPEED	0x0f

/* Fast tracker effects */
#define FX_SETPAN	0x08

/* Fast Tracker II effects */
#define FX_GLOBALVOL	0x10
#define FX_GVOL_SLIDE	0x11
#define FX_KEYOFF	0x14
#define FX_ENVPOS	0x15
#define FX_PANSLIDE	0x19
#define FX_MULTI_RETRIG	0x1b
#define FX_TREMOR	0x1d
#define FX_XF_PORTA	0x21

/* Protracker extended effects */
#define EX_FILTER	0x00
#define EX_F_PORTA_UP	0x01
#define EX_F_PORTA_DN	0x02
#define EX_GLISS	0x03
#define EX_VIBRATO_WF	0x04
#define EX_FINETUNE	0x05
#define EX_PATTERN_LOOP	0x06
#define EX_TREMOLO_WF	0x07
#define EX_SETPAN	0x08
#define EX_RETRIG	0x09
#define EX_F_VSLIDE_UP	0x0a
#define EX_F_VSLIDE_DN	0x0b
#define EX_CUT		0x0c
#define EX_DELAY	0x0d
#define EX_PATT_DELAY	0x0e
#define EX_INVLOOP	0x0f

#ifndef LIBXMP_CORE_PLAYER
/* Oktalyzer effects */
#define FX_OKT_ARP3	0x70
#define FX_OKT_ARP4	0x71
#define FX_OKT_ARP5	0x72
#define FX_NSLIDE2_DN	0x73
#define FX_NSLIDE2_UP	0x74
#define FX_F_NSLIDE_DN	0x75
#define FX_F_NSLIDE_UP	0x76

/* Persistent effects -- for FNK and FAR */
#define FX_PER_PORTA_DN	0x78
#define FX_PER_PORTA_UP	0x79
#define FX_PER_TPORTA	0x7a
#define FX_PER_VIBRATO	0x7b
#define FX_PER_VSLD_UP	0x7c
#define FX_PER_VSLD_DN	0x7d
#define FX_SPEED_CP	0x7e
#define FX_PER_CANCEL	0x7f

/* 669 frequency based effects */
#define FX_669_PORTA_UP	0x60
#define FX_669_PORTA_DN	0x61
#define FX_669_TPORTA	0x62
#define FX_669_FINETUNE	0x63
#define FX_669_VIBRATO	0x64
#endif

#ifndef LIBXMP_CORE_DISABLE_IT
/* IT effects */
#define FX_TRK_VOL      0x80
#define FX_TRK_VSLIDE   0x81
#define FX_TRK_FVSLIDE  0x82
#define FX_IT_INSTFUNC	0x83
#define FX_FLT_CUTOFF	0x84
#define FX_FLT_RESN	0x85
#define FX_IT_BPM	0x87
#define FX_IT_ROWDELAY	0x88
#define FX_IT_PANSLIDE	0x89
#define FX_PANBRELLO	0x8a
#define FX_PANBRELLO_WF	0x8b
#define FX_HIOFFSET	0x8c
#define FX_IT_BREAK	0x8e	/* like FX_BREAK with hex parameter */
#endif

#ifndef LIBXMP_CORE_PLAYER
/* MED effects */
#define FX_HOLD_DECAY	0x90
#define FX_SETPITCH	0x91
#define FX_VIBRATO2	0x92

/* PTM effects */
#define FX_NSLIDE_DN	0x9c	/* IMF/PTM note slide down */
#define FX_NSLIDE_UP	0x9d	/* IMF/PTM note slide up */
#define FX_NSLIDE_R_UP	0x9e	/* PTM note slide down with retrigger */
#define FX_NSLIDE_R_DN	0x9f	/* PTM note slide up with retrigger */

/* Extra effects */
#define FX_VOLSLIDE_UP	0xa0	/* SFX, MDL */
#define FX_VOLSLIDE_DN	0xa1
#define FX_F_VSLIDE	0xa5	/* IMF/MDL */
#define FX_CHORUS	0xa9	/* IMF */
#define FX_ICE_SPEED	0xa2
#define FX_REVERB	0xaa	/* IMF */
#define FX_MED_HOLD	0xb1	/* MMD hold/decay */
#define FX_MEGAARP	0xb2	/* Smaksak effect 7: MegaArp */
#define FX_VOL_ADD	0xb6	/* SFX change volume up */
#define FX_VOL_SUB	0xb7	/* SFX change volume down */
#define FX_PITCH_ADD	0xb8	/* SFX add steps to current note */
#define FX_PITCH_SUB	0xb9	/* SFX add steps to current note */
#endif

#define FX_SURROUND	0x8d	/* S3M/IT */
#define FX_S3M_SPEED	0xa3	/* S3M */
#define FX_VOLSLIDE_2	0xa4
#define FX_FINETUNE	0xa6
#define FX_S3M_BPM	0xab	/* S3M */
#define FX_FINE_VIBRATO	0xac	/* S3M/PTM/IMF/LIQ */
#define FX_F_VSLIDE_UP	0xad	/* MMD */
#define FX_F_VSLIDE_DN	0xae	/* MMD */
#define FX_F_PORTA_UP	0xaf	/* MMD */
#define FX_F_PORTA_DN	0xb0	/* MMD */
#define FX_PATT_DELAY	0xb3	/* MMD */
#define FX_S3M_ARPEGGIO	0xb4
#define FX_PANSL_NOMEM	0xb5	/* XM volume column */

#define FX_VSLIDE_UP_2	0xc0	/* IT volume column volume slide */
#define FX_VSLIDE_DN_2	0xc1
#define FX_F_VSLIDE_UP_2 0xc2
#define FX_F_VSLIDE_DN_2 0xc3

#endif /* LIBXMP_EFFECTS_H */

repo_name: orochi/nblood

license: mit


code:
/*
 * Based on the public domain version by Olivier Lapicque
 * Rewritten for libxmp by Claudio Matsuoka
 *
 * Copyright (C) 2012 Claudio Matsuoka
 *
 * 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.
 */

#ifndef LIBXMP_CORE_DISABLE_IT
#include <math.h>
#include "xmp.h"
#include "common.h"
#include "mixer.h"

/* LUT for 2 * damping factor */
static const float resonance_table[128] = {
        1.0000000000000000f, 0.9786446094512940f, 0.9577452540397644f, 0.9372922182083130f,
        0.9172759056091309f, 0.8976871371269226f, 0.8785166740417481f, 0.8597555756568909f,
        0.8413951396942139f, 0.8234267830848694f, 0.8058421611785889f, 0.7886331081390381f,
        0.7717915177345276f, 0.7553095817565918f, 0.7391796708106995f, 0.7233941555023193f,
        0.7079457640647888f, 0.6928272843360901f, 0.6780316829681397f, 0.6635520458221436f,
        0.6493816375732422f, 0.6355138421058655f, 0.6219421625137329f, 0.6086603403091431f,
        0.5956621170043945f, 0.5829415321350098f, 0.5704925656318665f, 0.5583094954490662f,
        0.5463865399360657f, 0.5347182154655457f, 0.5232990980148315f, 0.5121238231658936f,
        0.5011872053146362f, 0.4904841780662537f, 0.4800096750259399f, 0.4697588682174683f,
        0.4597269892692566f, 0.4499093294143677f, 0.4403013288974762f, 0.4308985173702240f,
        0.4216965138912201f, 0.4126909971237183f, 0.4038778245449066f, 0.3952528536319733f,
        0.3868120610713959f, 0.3785515129566193f, 0.3704673945903778f, 0.3625559210777283f,
        0.3548133969306946f, 0.3472362160682678f, 0.3398208320140839f, 0.3325638175010681f,
        0.3254617750644684f, 0.3185114264488220f, 0.3117094635963440f, 0.3050527870655060f,
        0.2985382676124573f, 0.2921628654003143f, 0.2859236001968384f, 0.2798175811767578f,
        0.2738419771194458f, 0.2679939568042755f, 0.2622708380222321f, 0.2566699385643005f,
        0.2511886358261108f, 0.2458244115114212f, 0.2405747324228287f, 0.2354371547698975f,
        0.2304092943668366f, 0.2254888117313385f, 0.2206734120845795f, 0.2159608304500580f,
        0.2113489061594009f, 0.2068354636430740f, 0.2024184018373489f, 0.1980956792831421f,
        0.1938652694225311f, 0.1897251904010773f, 0.1856735348701477f, 0.1817083954811096f,
        0.1778279393911362f, 0.1740303486585617f, 0.1703138649463654f, 0.1666767448186874f,
        0.1631172895431519f, 0.1596338599920273f, 0.1562248021364212f, 0.1528885662555695f,
        0.1496235728263855f, 0.1464282870292664f, 0.1433012634515762f, 0.1402409970760346f,
        0.1372461020946503f, 0.1343151479959488f, 0.1314467936754227f, 0.1286396980285645f,
        0.1258925348520279f, 0.1232040524482727f, 0.1205729842185974f, 0.1179980933666229f,
        0.1154781952500343f, 0.1130121126770973f, 0.1105986908078194f, 0.1082368120551109f,
        0.1059253737330437f, 0.1036632955074310f, 0.1014495193958283f, 0.0992830246686935f,
        0.0971627980470657f, 0.0950878411531448f, 0.0930572077631950f, 0.0910699293017387f,
        0.0891250967979431f, 0.0872217938303947f, 0.0853591337800026f, 0.0835362523794174f,
        0.0817523002624512f, 0.0800064504146576f, 0.0782978758215904f, 0.0766257941722870f,
        0.0749894231557846f, 0.0733879879117012f, 0.0718207582831383f, 0.0702869966626167f,
        0.0687859877943993f, 0.0673170387744904f, 0.0658794566988945f, 0.0644725710153580f,
};

#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__)
#define powf pow /* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */
#endif

/*
 * Simple 2-poles resonant filter
 */
#define FREQ_PARAM_MULT (128.0f / (24.0f * 256.0f))
void libxmp_filter_setup(int srate, int cutoff, int res, int *a0, int *b0, int *b1)
{
	float fc, fs = (float)srate;
	float fg, fb0, fb1;
	float r, d, e;

	/* [0-255] => [100Hz-8000Hz] */
	CLAMP(cutoff, 0, 255);
	CLAMP(res, 0, 255);

        fc = 110.0f * powf(2.0f, (float)cutoff * FREQ_PARAM_MULT + 0.25f);
        if (fc > fs / 2.0f) {
                fc = fs / 2.0f;
	}

        r = fs / (2.0 * 3.14159265358979f * fc);
        d = resonance_table[res >> 1] * (r + 1.0) - 1.0;
        e = r * r;

        fg = 1.0 / (1.0 + d + e);
        fb0 = (d + e + e) / (1.0 + d + e);
        fb1 = -e / (1.0 + d + e);

	*a0 = (int)(fg  * (1 << FILTER_SHIFT));
	*b0 = (int)(fb0 * (1 << FILTER_SHIFT));
	*b1 = (int)(fb1 * (1 << FILTER_SHIFT));
}

#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.
 */

#include "format.h"

#ifndef LIBXMP_NO_PROWIZARD
#include "loaders/prowizard/prowiz.h"
#endif

extern const struct format_loader libxmp_loader_xm;
extern const struct format_loader libxmp_loader_mod;
extern const struct format_loader libxmp_loader_it;
extern const struct format_loader libxmp_loader_s3m;
extern const struct format_loader libxmp_loader_mtm;

#ifndef LIBXMP_NO_PROWIZARD
extern const struct pw_format *const pw_formats[];
#endif

extern const struct format_loader *const format_loaders[];
#ifdef EDUKE32_DISABLE
const struct format_loader *const format_loaders[NUM_FORMATS + 2] = {
#else
const struct format_loader *const format_loaders[] = {
#endif // EDUKE32_DISABLE
	&libxmp_loader_xm,
	&libxmp_loader_mod,
	&libxmp_loader_it,
	&libxmp_loader_s3m,
	&libxmp_loader_mtm,
#ifndef LIBXMP_NO_PROWIZARD
	&libxmp_loader_pw,
#endif
	NULL
};

#ifdef EDUKE32_DISABLE
static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL };
#else
static const char *_farray[sizeof(format_loaders)/sizeof(struct format_loader *)] = { NULL };
#endif // EDUKE32_DISABLE

const char *const *format_list(void)
{
	int count, i;

	if (_farray[0] == NULL) {
		for (count = i = 0; format_loaders[i] != NULL; i++) {
#ifndef LIBXMP_NO_PROWIZARD
			if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
				int j;

				for (j = 0; pw_formats[j] != NULL; j++) {
					_farray[count++] = pw_formats[j]->name;
				}
				continue;
			}
#endif
			_farray[count++] = format_loaders[i]->name;
		}

		_farray[count] = NULL;
	}

	return _farray;
}


code:
#ifndef LIBXMP_FORMAT_H
#define LIBXMP_FORMAT_H

#include "common.h"
#include "hio.h"

struct format_loader {
	const char *name;
	int (*const test)(HIO_HANDLE *, char *, const int);
	int (*const loader)(struct module_data *, HIO_HANDLE *, const int);
};

const char *const *format_list(void);

#ifndef LIBXMP_CORE_PLAYER

#define NUM_FORMATS 53
#define NUM_PW_FORMATS 43

#ifndef LIBXMP_NO_PROWIZARD
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
#endif
#endif

#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.
 */

#include <errno.h>
#include "common.h"
#include "hio.h"
#ifdef EDUKE32_DISABLED
#include "callbackio.h"
#endif // EDUKE32_DISABLED
#include "mdataio.h"

#ifdef EDUKE32_DISABLED
static long get_size(FILE *f)
{
	long size, pos;

	pos = ftell(f);
	if (pos >= 0) {
		if (fseek(f, 0, SEEK_END) < 0) {
			return -1;
		}
		size = ftell(f);
		if (fseek(f, pos, SEEK_SET) < 0) {
			return -1;
		}
		return size;
	} else {
		return pos;
	}
}
#endif // EDUKE32_DISABLED

int8 hio_read8s(HIO_HANDLE *h)
{
	int err;
	int8 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read8s(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread8s(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread8s(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint8 hio_read8(HIO_HANDLE *h)
{
	int err;
	uint8 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read8(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread8(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread8(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint16 hio_read16l(HIO_HANDLE *h)
{
	int err;
	uint16 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read16l(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread16l(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread16l(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint16 hio_read16b(HIO_HANDLE *h)
{
	int err;
	uint16 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read16b(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread16b(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread16b(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint32 hio_read24l(HIO_HANDLE *h)
{
	int err;
	uint32 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read24l(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread24l(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread24l(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint32 hio_read24b(HIO_HANDLE *h)
{
	int err;
	uint32 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read24b(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread24b(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread24b(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint32 hio_read32l(HIO_HANDLE *h)
{
	int err;
	uint32 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read32l(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread32l(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread32l(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

uint32 hio_read32b(HIO_HANDLE *h)
{
	int err;
	uint32 ret;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = read32b(h->handle.file, &err);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread32b(h->handle.mem, &err);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread32b(h->handle.cbfile, &err);
		break;
#endif // EDUKE32_DISABLED
	default:
		return 0;
	}

	if (err != 0) {
		h->error = err;
	}
	return ret;
}

size_t hio_read(void *buf, size_t size, size_t num, HIO_HANDLE *h)
{
	size_t ret = 0;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = fread(buf, size, num, h->handle.file);
		if (ret != num) {
			if (ferror(h->handle.file)) {
				h->error = errno;
			} else {
				h->error = feof(h->handle.file) ? EOF : -2;
			}
		}
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mread(buf, size, num, h->handle.mem);
		if (ret != num) {
			h->error = EOF;
		}
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbread(buf, size, num, h->handle.cbfile);
		if (ret != num) {
			h->error = EOF;
		}
		break;
#endif // EDUKE32_DISABLED
	}

	return ret;
}

int hio_seek(HIO_HANDLE *h, long offset, int whence)
{
	int ret = -1;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = fseek(h->handle.file, offset, whence);
		if (ret < 0) {
			h->error = errno;
		}
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mseek(h->handle.mem, offset, whence);
		if (ret < 0) {
			h->error = EINVAL;
		}
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbseek(h->handle.cbfile, offset, whence);
		if (ret < 0) {
			h->error = EINVAL;
		}
		break;
#endif // EDUKE32_DISABLED
	}

	return ret;
}

long hio_tell(HIO_HANDLE *h)
{
	long ret = -1;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = ftell(h->handle.file);
		if (ret < 0) {
			h->error = errno;
		}
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mtell(h->handle.mem);
		if (ret < 0) {
		/* should _not_ happen! */
			h->error = EINVAL;
		}
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbtell(h->handle.cbfile);
		if (ret < 0) {
			h->error = EINVAL;
		}
		break;
#endif // EDUKE32_DISABLED
	}

	return ret;
}

int hio_eof(HIO_HANDLE *h)
{
	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		return feof(h->handle.file);
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		return meof(h->handle.mem);
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		return cbeof(h->handle.cbfile);
#endif // EDUKE32_DISABLED
	}
	return EOF;
}

int hio_error(HIO_HANDLE *h)
{
	int error = h->error;
	h->error = 0;
	return error;
}

#ifdef EDUKE32_DISABLED
HIO_HANDLE *hio_open(const char *path, const char *mode)
{
	HIO_HANDLE *h;

	h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
	if (h == NULL)
		goto err;

	h->type = HIO_HANDLE_TYPE_FILE;
	h->handle.file = fopen(path, mode);
	if (h->handle.file == NULL)
		goto err2;

	h->size = get_size(h->handle.file);
	if (h->size < 0)
		goto err3;

	return h;

    err3:
	fclose(h->handle.file);
    err2:
	free(h);
    err:
	return NULL;
}
#endif // EDUKE32_DISABLED

HIO_HANDLE *hio_open_mem(const void *ptr, long size)
{
	HIO_HANDLE *h;

	if (size <= 0) return NULL;
	h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
	if (h == NULL)
		return NULL;

	h->type = HIO_HANDLE_TYPE_MEMORY;
	h->handle.mem = mopen(ptr, size);
	h->size = size;

	return h;
}

#ifdef EDUKE32_DISABLED
HIO_HANDLE *hio_open_file(FILE *f)
{
	HIO_HANDLE *h;

	h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
	if (h == NULL)
		return NULL;

	h->noclose = 1;
	h->type = HIO_HANDLE_TYPE_FILE;
	h->handle.file = f;
	h->size = get_size(f);
	if (h->size < 0) {
		free(h);
		return NULL;
	}

	return h;
}

HIO_HANDLE *hio_open_file2(FILE *f)
{
	HIO_HANDLE *h = hio_open_file(f);
	if (h != NULL) {
		h->noclose = 0;
	}
	else {
		fclose(f);
	}
	return h;
}

HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
{
	HIO_HANDLE *h;
	CBFILE *f = cbopen(priv, callbacks);
	if (!f)
		return NULL;

	h = (HIO_HANDLE *)calloc(1, sizeof(HIO_HANDLE));
	if (h == NULL) {
		cbclose(f);
		return NULL;
	}

	h->type = HIO_HANDLE_TYPE_CBFILE;
	h->handle.cbfile = f;
	h->size = cbfilelength(f);
	if (h->size < 0) {
		cbclose(f);
		free(h);
		return NULL;
	}
	return h;
}
#endif // EDUKE32_DISABLED

int hio_close(HIO_HANDLE *h)
{
	int ret = -1;

	switch (HIO_HANDLE_TYPE(h)) {
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_FILE:
		ret = (h->noclose)? 0 : fclose(h->handle.file);
		break;
#endif // EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_MEMORY:
		ret = mclose(h->handle.mem);
		break;
#ifdef EDUKE32_DISABLED
	case HIO_HANDLE_TYPE_CBFILE:
		ret = cbclose(h->handle.cbfile);
		break;
#endif // EDUKE32_DISABLED
	}

	free(h);
	return ret;
}

long hio_size(HIO_HANDLE *h)
{
	return h->size;
}


code:
#ifndef XMP_HIO_H
#define XMP_HIO_H

#ifdef EDUKE32_DISABLED
#include "callbackio.h"
#endif // EDUKE32_DISABLED
#include <stddef.h>
#include "memio.h"

#define HIO_HANDLE_TYPE(x) ((x)->type)

#ifdef EDUKE32_DISABLED
enum hio_type {
	HIO_HANDLE_TYPE_FILE,
	HIO_HANDLE_TYPE_MEMORY,
	HIO_HANDLE_TYPE_CBFILE
};
#else
enum hio_type {
	HIO_HANDLE_TYPE_MEMORY = 1,
};
#endif // EDUKE32_DISABLED

typedef struct {
	enum hio_type type;
	long size;
	union {
#ifdef EDUKE32_DISABLED
		FILE *file;
#endif // EDUKE32_DISABLED
		MFILE *mem;
#ifdef EDUKE32_DISABLED
		CBFILE *cbfile;
#endif // EDUKE32_DISABLED
	} handle;
	int error;
	int noclose;
} HIO_HANDLE;

int8	hio_read8s	(HIO_HANDLE *);
uint8	hio_read8	(HIO_HANDLE *);
uint16	hio_read16l	(HIO_HANDLE *);
uint16	hio_read16b	(HIO_HANDLE *);
uint32	hio_read24l	(HIO_HANDLE *);
uint32	hio_read24b	(HIO_HANDLE *);
uint32	hio_read32l	(HIO_HANDLE *);
uint32	hio_read32b	(HIO_HANDLE *);
size_t	hio_read	(void *, size_t, size_t, HIO_HANDLE *);
int	hio_seek	(HIO_HANDLE *, long, int);
long	hio_tell	(HIO_HANDLE *);
int	hio_eof		(HIO_HANDLE *);
int	hio_error	(HIO_HANDLE *);
#ifdef EDUKE32_DISABLED
HIO_HANDLE *hio_open	(const char *, const char *);
#endif // EDUKE32_DISABLED
HIO_HANDLE *hio_open_mem  (const void *, long);
#ifdef EDUKE32_DISABLED
HIO_HANDLE *hio_open_file (FILE *);
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks);
#endif // EDUKE32_DISABLED
int	hio_close	(HIO_HANDLE *);
long	hio_size	(HIO_HANDLE *);

#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.
 */

#ifndef LIBXMP_LOADERS_IT_H
#define LIBXMP_LOADERS_IT_H

#include "loader.h"

/* IT flags */
#define IT_STEREO	0x01
#define IT_VOL_OPT	0x02	/* Not recognized */
#define IT_USE_INST	0x04
#define IT_LINEAR_FREQ	0x08
#define IT_OLD_FX	0x10
#define IT_LINK_GXX	0x20

/* IT special */
#define IT_HAS_MSG	0x01

/* IT instrument flags */
#define IT_INST_SAMPLE	0x01
#define IT_INST_16BIT	0x02
#define IT_INST_STEREO	0x04
#define IT_INST_LOOP	0x10
#define IT_INST_SLOOP	0x20
#define IT_INST_BLOOP	0x40
#define IT_INST_BSLOOP	0x80

/* IT sample flags */
#define IT_SMP_SAMPLE	0x01
#define IT_SMP_16BIT	0x02
#define IT_SMP_STEREO	0x04	/* unsupported */
#define IT_SMP_COMP	0x08	/* unsupported */
#define IT_SMP_LOOP	0x10
#define IT_SMP_SLOOP	0x20
#define IT_SMP_BLOOP	0x40
#define IT_SMP_BSLOOP	0x80

/* IT sample conversion flags */
#define IT_CVT_SIGNED	0x01
#define IT_CVT_BIGEND	0x02	/* 'safe to ignore' according to ittech.txt */
#define IT_CVT_DIFF	0x04	/* Compressed sample flag */
#define IT_CVT_BYTEDIFF	0x08	/* 'safe to ignore' according to ittech.txt */
#define IT_CVT_12BIT	0x10	/* 'safe to ignore' according to ittech.txt */

/* IT envelope flags */
#define IT_ENV_ON	0x01
#define IT_ENV_LOOP	0x02
#define IT_ENV_SLOOP	0x04
#define IT_ENV_CARRY	0x08
#define IT_ENV_FILTER	0x80

struct it_file_header {
	uint32 magic;		/* 'IMPM' */
	uint8 name[26];		/* ASCIIZ Song name */
	uint8 hilite_min;	/* Pattern editor highlight */
	uint8 hilite_maj;	/* Pattern editor highlight */
	uint16 ordnum;		/* Number of orders (must be even) */
	uint16 insnum;		/* Number of instruments */
	uint16 smpnum;		/* Number of samples */
	uint16 patnum;		/* Number of patterns */
	uint16 cwt;		/* Tracker ID and version */
	uint16 cmwt;		/* Format version */
	uint16 flags;		/* Flags */
	uint16 special;		/* More flags */
	uint8 gv;		/* Global volume */
	uint8 mv;		/* Master volume */
	uint8 is;		/* Initial speed */
	uint8 it;		/* Initial tempo */
	uint8 sep;		/* Panning separation */
	uint8 pwd;		/* Pitch wheel depth */
	uint16 msglen;		/* Message length */
	uint32 msgofs;		/* Message offset */
	uint32 rsvd;		/* Reserved */
	uint8 chpan[64];	/* Channel pan settings */
	uint8 chvol[64];	/* Channel volume settings */
};

struct it_instrument1_header {
	uint32 magic;		/* 'IMPI' */
	uint8 dosname[12];	/* DOS filename */
	uint8 zero;		/* Always zero */
	uint8 flags;		/* Instrument flags */
	uint8 vls;		/* Volume loop start */
	uint8 vle;		/* Volume loop end */
	uint8 sls;		/* Sustain loop start */
	uint8 sle;		/* Sustain loop end */
	uint16 rsvd1;		/* Reserved */
	uint16 fadeout;		/* Fadeout (release) */
	uint8 nna;		/* New note action */
	uint8 dnc;		/* Duplicate note check */
	uint16 trkvers;		/* Tracker version */
	uint8 nos;		/* Number of samples */
	uint8 rsvd2;		/* Reserved */
	uint8 name[26];		/* ASCIIZ Instrument name */
	uint8 rsvd3[6];		/* Reserved */
	uint8 keys[240];
	uint8 epoint[200];
	uint8 enode[50];
};

struct it_instrument2_header {
	uint32 magic;		/* 'IMPI' */
	uint8 dosname[12];	/* DOS filename */
	uint8 zero;		/* Always zero */
	uint8 nna;		/* New Note Action */
	uint8 dct;		/* Duplicate Check Type */
	uint8 dca;		/* Duplicate Check Action */
	uint16 fadeout;
	uint8 pps;		/* Pitch-Pan Separation */
	uint8 ppc;		/* Pitch-Pan Center */
	uint8 gbv;		/* Global Volume */
	uint8 dfp;		/* Default pan */
	uint8 rv;		/* Random volume variation */
	uint8 rp;		/* Random pan variation */
	uint16 trkvers;		/* Not used: tracked version */
	uint8 nos;		/* Not used: number of samples */
	uint8 rsvd1;		/* Reserved */
	uint8 name[26];		/* ASCIIZ Instrument name */
	uint8 ifc;		/* Initial filter cutoff */
	uint8 ifr;		/* Initial filter resonance */
	uint8 mch;		/* MIDI channel */
	uint8 mpr;		/* MIDI program */
	uint16 mbnk;		/* MIDI bank */
	uint8 keys[240];
};

struct it_envelope_node {
	int8 y;
	uint16 x;
};

struct it_envelope {
	uint8 flg;		/* Flags */
	uint8 num;		/* Number of node points */
	uint8 lpb;		/* Loop beginning */
	uint8 lpe;		/* Loop end */
	uint8 slb;		/* Sustain loop beginning */
	uint8 sle;		/* Sustain loop end */
	struct it_envelope_node node[25];
	uint8 unused;
};

struct it_sample_header {
	uint32 magic;		/* 'IMPS' */
	uint8 dosname[12];	/* DOS filename */
	uint8 zero;		/* Always zero */
	uint8 gvl;		/* Global volume for instrument */
	uint8 flags;		/* Sample flags */
	uint8 vol;		/* Volume */
	uint8 name[26];		/* ASCIIZ sample name */
	uint8 convert;		/* Sample flags */
	uint8 dfp;		/* Default pan */
	uint32 length;		/* Length */
	uint32 loopbeg;		/* Loop begin */
	uint32 loopend;		/* Loop end */
	uint32 c5spd;		/* C 5 speed */
	uint32 sloopbeg;	/* SusLoop begin */
	uint32 sloopend;	/* SusLoop end */
	uint32 sample_ptr;	/* Sample pointer */
	uint8 vis;		/* Vibrato speed */
	uint8 vid;		/* Vibrato depth */
	uint8 vir;		/* Vibrato rate */
	uint8 vit;		/* Vibrato waveform */
};

int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215);
int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215);

#endif /* LIBXMP_LOADERS_IT_H */


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

#ifndef LIBXMP_CORE_DISABLE_IT

#include "loader.h"
#include "it.h"
#include "period.h"

#define MAGIC_IMPM	MAGIC4('I','M','P','M')
#define MAGIC_IMPI	MAGIC4('I','M','P','I')
#define MAGIC_IMPS	MAGIC4('I','M','P','S')

static int it_test(HIO_HANDLE *, char *, const int);
static int it_load(struct module_data *, HIO_HANDLE *, const int);

extern const struct format_loader libxmp_loader_it;
const struct format_loader libxmp_loader_it = {
	"Impulse Tracker",
	it_test,
	it_load
};

static int it_test(HIO_HANDLE *f, char *t, const int start)
{
	if (hio_read32b(f) != MAGIC_IMPM)
		return -1;

	libxmp_read_title(f, t, 26);

	return 0;
}

#define	FX_NONE	0xff
#define FX_XTND 0xfe
#define L_CHANNELS 64

static const uint8 fx[32] = {
	/*   */ FX_NONE,
	/* A */ FX_S3M_SPEED,
	/* B */ FX_JUMP,
	/* C */ FX_IT_BREAK,
	/* D */ FX_VOLSLIDE,
	/* E */ FX_PORTA_DN,
	/* F */ FX_PORTA_UP,
	/* G */ FX_TONEPORTA,
	/* H */ FX_VIBRATO,
	/* I */ FX_TREMOR,
	/* J */ FX_S3M_ARPEGGIO,
	/* K */ FX_VIBRA_VSLIDE,
	/* L */ FX_TONE_VSLIDE,
	/* M */ FX_TRK_VOL,
	/* N */ FX_TRK_VSLIDE,
	/* O */ FX_OFFSET,
	/* P */ FX_IT_PANSLIDE,
	/* Q */ FX_MULTI_RETRIG,
	/* R */ FX_TREMOLO,
	/* S */ FX_XTND,
	/* T */ FX_IT_BPM,
	/* U */ FX_FINE_VIBRATO,
	/* V */ FX_GLOBALVOL,
	/* W */ FX_GVOL_SLIDE,
	/* X */ FX_SETPAN,
	/* Y */ FX_PANBRELLO,
	/* Z */ FX_FLT_CUTOFF,
	/* ? */ FX_NONE,
	/* ? */ FX_NONE,
	/* ? */ FX_NONE,
	/* ? */ FX_NONE,
	/* ? */ FX_NONE
};

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

	switch (e->fxt = fx[e->fxt]) {
	case FX_XTND:		/* Extended effect */
		e->fxt = FX_EXTENDED;

		if (h == 0 && e->fxp == 0) {
			e->fxp = last_fxp[c];
			h = MSN(e->fxp);
			l = LSN(e->fxp);
		} else {
			last_fxp[c] = e->fxp;
		}

		switch (h) {
		case 0x1:	/* Glissando */
			e->fxp = 0x30 | l;
			break;
		case 0x2:	/* Finetune -- not supported */
			e->fxt = e->fxp = 0;
			break;
		case 0x3:	/* Vibrato wave */
			e->fxp = 0x40 | l;
			break;
		case 0x4:	/* Tremolo wave */
			e->fxp = 0x70 | l;
			break;
		case 0x5:	/* Panbrello wave */
			if (l <= 3) {
				e->fxt = FX_PANBRELLO_WF;
				e->fxp = l;
			} else {
				e->fxt = e->fxp = 0;
			}
			break;
		case 0x6:	/* Pattern delay */
			e->fxp = 0xe0 | l;
			break;
		case 0x7:	/* Instrument functions */
			e->fxt = FX_IT_INSTFUNC;
			e->fxp &= 0x0f;
			break;
		case 0x8:	/* Set pan position */
			e->fxt = FX_SETPAN;
			e->fxp = l << 4;
			break;
		case 0x9:	/* 0x91 = set surround */
			e->fxt = FX_SURROUND;
			e->fxp = l;
			break;
		case 0xa:	/* High offset */
			e->fxt = FX_HIOFFSET;
			e->fxp = l;
			break;
		case 0xb:	/* Pattern loop */
			e->fxp = 0x60 | l;
			break;
		case 0xc:	/* Note cut */
		case 0xd:	/* Note delay */
			if ((e->fxp = l) == 0)
				e->fxp++;  /* SD0 and SC0 become SD1 and SC1 */
			e->fxp |= h << 4;
			break;
		case 0xe:	/* Pattern row delay */
			e->fxt = FX_IT_ROWDELAY;
			e->fxp = l;
			break;
		default:
			e->fxt = e->fxp = 0;
		}
		break;
	case FX_FLT_CUTOFF:
		if (e->fxp > 0x7f && e->fxp < 0x90) {	/* Resonance */
			e->fxt = FX_FLT_RESN;
			e->fxp = (e->fxp - 0x80) * 16;
		} else {	/* Cutoff */
			e->fxp *= 2;
		}
		break;
	case FX_TREMOR:
		if (!new_fx && e->fxp != 0) {
			e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1);
		}
		break;
	case FX_GLOBALVOL:
		if (e->fxp > 0x80) {	/* See storlek test 16 */
			e->fxt = e->fxp = 0;
		}
		break;
	case FX_NONE:		/* No effect */
		e->fxt = e->fxp = 0;
		break;
	}
}

static void xlat_volfx(struct xmp_event *event)
{
	int b;

	b = event->vol;
	event->vol = 0;

	if (b <= 0x40) {
		event->vol = b + 1;
	} else if (b >= 65 && b <= 74) {	/* A */
		event->f2t = FX_F_VSLIDE_UP_2;
		event->f2p = b - 65;
	} else if (b >= 75 && b <= 84) {	/* B */
		event->f2t = FX_F_VSLIDE_DN_2;
		event->f2p = b - 75;
	} else if (b >= 85 && b <= 94) {	/* C */
		event->f2t = FX_VSLIDE_UP_2;
		event->f2p = b - 85;
	} else if (b >= 95 && b <= 104) {	/* D */
		event->f2t = FX_VSLIDE_DN_2;
		event->f2p = b - 95;
	} else if (b >= 105 && b <= 114) {	/* E */
		event->f2t = FX_PORTA_DN;
		event->f2p = (b - 105) << 2;
	} else if (b >= 115 && b <= 124) {	/* F */
		event->f2t = FX_PORTA_UP;
		event->f2p = (b - 115) << 2;
	} else if (b >= 128 && b <= 192) {	/* pan */
		if (b == 192) {
			event->f2p = 0xff;
		} else {
			event->f2p = (b - 128) << 2;
		}
		event->f2t = FX_SETPAN;
	} else if (b >= 193 && b <= 202) {	/* G */
		uint8 val[10] = {
			0x00, 0x01, 0x04, 0x08, 0x10,
			0x20, 0x40, 0x60, 0x80, 0xff
		};
		event->f2t = FX_TONEPORTA;
		event->f2p = val[b - 193];
	} else if (b >= 203 && b <= 212) {	/* H */
		event->f2t = FX_VIBRATO;
		event->f2p = b - 203;
	}
}

static void fix_name(uint8 *s, int l)
{
	int i;

	/* IT names can have 0 at start of data, replace with space */
	for (l--, i = 0; i < l; i++) {
		if (s[i] == 0)
			s[i] = ' ';
	}
	for (i--; i >= 0 && s[i] == ' '; i--) {
		if (s[i] == ' ')
			s[i] = 0;
	}
}

static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
			  HIO_HANDLE *f)
{
	int i;
	uint8 buf[82];

	if (hio_read(buf, 1, 82, f) != 82) {
		return -1;
	}

	env->flg = buf[0];
	env->num = MIN(buf[1], 25); /* Clamp to IT max */

	env->lpb = buf[2];
	env->lpe = buf[3];
	env->slb = buf[4];
	env->sle = buf[5];

	for (i = 0; i < 25; i++) {
		env->node[i].y = buf[6 + i * 3];
		env->node[i].x = readmem16l(buf + 7 + i * 3);
	}

	ei->flg = env->flg & IT_ENV_ON ? XMP_ENVELOPE_ON : 0;

	if (env->flg & IT_ENV_LOOP) {
		ei->flg |= XMP_ENVELOPE_LOOP;
	}

	if (env->flg & IT_ENV_SLOOP) {
		ei->flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP;
	}

	if (env->flg & IT_ENV_CARRY) {
		ei->flg |= XMP_ENVELOPE_CARRY;
	}

	ei->npt = env->num;
	ei->sus = env->slb;
	ei->sue = env->sle;
	ei->lps = env->lpb;
	ei->lpe = env->lpe;

	if (ei->npt > 0 && ei->npt <= 25 /* XMP_MAX_ENV_POINTS */) {
		for (i = 0; i < ei->npt; i++) {
			ei->data[i * 2] = env->node[i].x;
			ei->data[i * 2 + 1] = env->node[i].y;
		}
	} else {
		ei->flg &= ~XMP_ENVELOPE_ON;
	}

	return 0;
}

static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
{
#ifndef LIBXMP_CORE_PLAYER
	char tracker_name[40];
	int sample_mode = ~ifh->flags & IT_USE_INST;

	switch (ifh->cwt >> 8) {
	case 0x00:
		strcpy(tracker_name, "unmo3");
		break;
	case 0x01:
	case 0x02:		/* test from Schism Tracker sources */
		if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0214
		    && ifh->flags == 9 && ifh->special == 0
		    && ifh->hilite_maj == 0 && ifh->hilite_min == 0
		    && ifh->insnum == 0 && ifh->patnum + 1 == ifh->ordnum
		    && ifh->gv == 128 && ifh->mv == 100 && ifh->is == 1
		    && ifh->sep == 128 && ifh->pwd == 0
		    && ifh->msglen == 0 && ifh->msgofs == 0 && ifh->rsvd == 0) {
			strcpy(tracker_name, "OpenSPC conversion");
		} else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0217) {
			strcpy(tracker_name, "ModPlug Tracker 1.16");
			/* ModPlug Tracker files aren't really IMPM 2.00 */
			ifh->cmwt = sample_mode ? 0x100 : 0x214;
		} else if (ifh->cwt == 0x0216) {
			strcpy(tracker_name, "Impulse Tracker 2.14v3");
		} else if (ifh->cwt == 0x0217) {
			strcpy(tracker_name, "Impulse Tracker 2.14v5");
		} else if (ifh->cwt == 0x0214 && !memcmp(&ifh->rsvd, "CHBI", 4)) {
			strcpy(tracker_name, "Chibi Tracker");
		} else {
			snprintf(tracker_name, 40, "Impulse Tracker %d.%02x",
				 (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff);
		}
		break;
	case 0x08:
	case 0x7f:
		if (ifh->cwt == 0x0888) {
			strcpy(tracker_name, "OpenMPT 1.17");
		} else if (ifh->cwt == 0x7fff) {
			strcpy(tracker_name, "munch.py");
		} else {
			snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt);
		}
		break;
	default:
		switch (ifh->cwt >> 12) {
		case 0x1:
			libxmp_schism_tracker_string(tracker_name, 40,
				(ifh->cwt & 0x0fff), ifh->rsvd);
			break;
		case 0x5:
			snprintf(tracker_name, 40, "OpenMPT %d.%02x",
				 (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff);
			if (memcmp(&ifh->rsvd, "OMPT", 4))
				strncat(tracker_name, " (compat.)", 39);
			break;
		case 0x06:
			snprintf(tracker_name, 40, "BeRoTracker %d.%02x",
				 (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff);
			break;
		default:
			snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt);
		}
	}

	libxmp_set_type(m, "%s IT %d.%02x", tracker_name, ifh->cmwt >> 8,
						ifh->cmwt & 0xff);
#else
	libxmp_set_type(m, "Impulse Tracker");
#endif
}

static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
{
	int inst_map[120], inst_rmap[XMP_MAX_KEYS];
	struct it_instrument1_header i1h;
	int c, k, j;
	uint8 buf[64];

	if (hio_read(buf, 1, 64, f) != 64) {
		return -1;
	}

	i1h.magic = readmem32b(buf);
	if (i1h.magic != MAGIC_IMPI) {
		D_(D_CRIT "bad instrument magic");
		return -1;
	}
	memcpy(i1h.dosname, buf + 4, 12);
	i1h.zero = buf[16];
	i1h.flags = buf[17];
	i1h.vls = buf[18];
	i1h.vle = buf[19];
	i1h.sls = buf[20];
	i1h.sle = buf[21];
	i1h.fadeout = readmem16l(buf + 24);
	i1h.nna = buf[26];
	i1h.dnc = buf[27];
	i1h.trkvers = readmem16l(buf + 28);
	i1h.nos = buf[30];

	memcpy(i1h.name, buf + 32, 26);
	fix_name(i1h.name, 26);

	if (hio_read(i1h.keys, 1, 240, f) != 240) {
		return -1;
	}
	if (hio_read(i1h.epoint, 1, 200, f) != 200) {
		return -1;
	}
	if (hio_read(i1h.enode, 1, 50, f) != 50) {
		return -1;
	}

	libxmp_copy_adjust(xxi->name, i1h.name, 25);

	xxi->rls = i1h.fadeout << 7;

	xxi->aei.flg = 0;
	if (i1h.flags & IT_ENV_ON) {
		xxi->aei.flg |= XMP_ENVELOPE_ON;
	}
	if (i1h.flags & IT_ENV_LOOP) {
		xxi->aei.flg |= XMP_ENVELOPE_LOOP;
	}
	if (i1h.flags & IT_ENV_SLOOP) {
		xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP;
	}
	if (i1h.flags & IT_ENV_CARRY) {
		xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_CARRY;
	}
	xxi->aei.lps = i1h.vls;
	xxi->aei.lpe = i1h.vle;
	xxi->aei.sus = i1h.sls;
	xxi->aei.sue = i1h.sle;

	for (k = 0; k < 25 && i1h.enode[k * 2] != 0xff; k++) ;

	/* Sanity check */
	if (k >= 25 || i1h.enode[k * 2] != 0xff) {
		return -1;
	}

	for (xxi->aei.npt = k; k--;) {
		xxi->aei.data[k * 2] = i1h.enode[k * 2];
		xxi->aei.data[k * 2 + 1] = i1h.enode[k * 2 + 1];
	}

	/* See how many different instruments we have */
	for (j = 0; j < 120; j++)
		inst_map[j] = -1;

	for (k = j = 0; j < XMP_MAX_KEYS; j++) {
		c = j < 120 ? i1h.keys[j * 2 + 1] - 1 : -1;
		if (c < 0 || c >= 120) {
			xxi->map[j].ins = 0;
			xxi->map[j].xpo = 0;
			continue;
		}
		if (inst_map[c] == -1) {
			inst_map[c] = k;
			inst_rmap[k] = c;
			k++;
		}
		xxi->map[j].ins = inst_map[c];
		xxi->map[j].xpo = i1h.keys[j * 2] - j;
	}

	xxi->nsm = k;
	xxi->vol = 0x40;

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

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

			sub->sid = inst_rmap[j];
			sub->nna = i1h.nna;
			sub->dct =
			    i1h.dnc ? XMP_INST_DCT_NOTE : XMP_INST_DCT_OFF;
			sub->dca = XMP_INST_DCA_CUT;
			sub->pan = -1;
		}
	}

	D_(D_INFO "[  ] %-26.26s %d %-4.4s %4d %2d %c%c%c %3d",
	   /*i,*/ i1h.name,
	   i1h.nna,
	   i1h.dnc ? "on" : "off",
	   i1h.fadeout,
	   xxi->aei.npt,
	   xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-',
	   xxi->aei.flg & XMP_ENVELOPE_LOOP ? 'L' : '-',
	   xxi->aei.flg & XMP_ENVELOPE_SUS ? 'S' : '-', xxi->nsm);

	return 0;
}

static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
{
	int inst_map[120], inst_rmap[XMP_MAX_KEYS];
	struct it_instrument2_header i2h;
	struct it_envelope env;
	int dca2nna[] = { 0, 2, 3, 3 /* Northern Sky (cj-north.it) has this... */ };
	int c, k, j;
	uint8 buf[64];

	if (hio_read(buf, 1, 64, f) != 64) {
		return -1;
	}

	i2h.magic = readmem32b(buf);
	if (i2h.magic != MAGIC_IMPI) {
		D_(D_CRIT "bad instrument magic");
		return -1;
	}
	memcpy(i2h.dosname, buf + 4, 12);
	i2h.zero = buf[16];
	i2h.nna = buf[17];
	i2h.dct = buf[18];
	i2h.dca = buf[19];

	/* Sanity check */
	if (i2h.dca > 3) {
		/* Northern Sky has an instrument with DCA 3 */
		D_(D_WARN "bad instrument dca: %d", i2h.dca);
		i2h.dca = 0;
	}

	i2h.fadeout = readmem16l(buf + 20);
	i2h.pps = buf[22];
	i2h.ppc = buf[23];
	i2h.gbv = buf[24];
	i2h.dfp = buf[25];
	i2h.rv = buf[26];
	i2h.rp = buf[27];
	i2h.trkvers = readmem16l(buf + 28);
	i2h.nos = buf[30];

	memcpy(i2h.name, buf + 32, 26);
	fix_name(i2h.name, 26);

	i2h.ifc = buf[58];
	i2h.ifr = buf[59];
	i2h.mch = buf[60];
	i2h.mpr = buf[61];
	i2h.mbnk = readmem16l(buf + 62);

	if (hio_read(i2h.keys, 1, 240, f) != 240) {
		D_(D_CRIT "key map read error");
		return -1;
	}

	libxmp_copy_adjust(xxi->name, i2h.name, 25);
	xxi->rls = i2h.fadeout << 6;

	/* Envelopes */

	if (read_envelope(&xxi->aei, &env, f) < 0) {
		return -1;
	}
	if (read_envelope(&xxi->pei, &env, f) < 0) {
		return -1;
	}
	if (read_envelope(&xxi->fei, &env, f) < 0) {
		return -1;
	}

	if (xxi->pei.flg & XMP_ENVELOPE_ON) {
		for (j = 0; j < xxi->pei.npt; j++)
			xxi->pei.data[j * 2 + 1] += 32;
	}

	if (xxi->aei.flg & XMP_ENVELOPE_ON && xxi->aei.npt == 0) {
		xxi->aei.npt = 1;
	}
	if (xxi->pei.flg & XMP_ENVELOPE_ON && xxi->pei.npt == 0) {
		xxi->pei.npt = 1;
	}
	if (xxi->fei.flg & XMP_ENVELOPE_ON && xxi->fei.npt == 0) {
		xxi->fei.npt = 1;
	}

	if (env.flg & IT_ENV_FILTER) {
		xxi->fei.flg |= XMP_ENVELOPE_FLT;
		for (j = 0; j < env.num; j++) {
			xxi->fei.data[j * 2 + 1] += 32;
			xxi->fei.data[j * 2 + 1] *= 4;
		}
	} else {
		/* Pitch envelope is *50 to get fine interpolation */
		for (j = 0; j < env.num; j++)
			xxi->fei.data[j * 2 + 1] *= 50;
	}

	/* See how many different instruments we have */
	for (j = 0; j < 120; j++)
		inst_map[j] = -1;

	for (k = j = 0; j < 120; j++) {
		c = i2h.keys[j * 2 + 1] - 1;
		if (c < 0 || c >= 120) {
			xxi->map[j].ins = 0xff;	/* No sample */
			xxi->map[j].xpo = 0;
			continue;
		}
		if (inst_map[c] == -1) {
			inst_map[c] = k;
			inst_rmap[k] = c;
			k++;
		}
		xxi->map[j].ins = inst_map[c];
		xxi->map[j].xpo = i2h.keys[j * 2] - j;
	}

	xxi->nsm = k;
	xxi->vol = i2h.gbv >> 1;

	if (k) {
		xxi->sub = (struct xmp_subinstrument *)calloc(sizeof(struct xmp_subinstrument), k);
		if (xxi->sub == NULL)
			return -1;

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

			sub->sid = inst_rmap[j];
			sub->nna = i2h.nna;
			sub->dct = i2h.dct;
			sub->dca = dca2nna[i2h.dca];
			sub->pan = i2h.dfp & 0x80 ? -1 : i2h.dfp * 4;
			sub->ifc = i2h.ifc;
			sub->ifr = i2h.ifr;
			sub->rvv = ((int)i2h.rp << 8) | i2h.rv;
		}
	}

	D_(D_INFO "[  ] %-26.26s %d %d %d %4d %4d  %2x "
	   "%02x %c%c%c %3d %02x %02x",
	   /*i,*/ i2h.name,
	   i2h.nna, i2h.dct, i2h.dca,
	   i2h.fadeout,
	   i2h.gbv,
	   i2h.dfp & 0x80 ? 0x80 : i2h.dfp * 4,
	   i2h.rv,
	   xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-',
	   xxi->pei.flg & XMP_ENVELOPE_ON ? 'P' : '-',
	   env.flg & 0x01 ? env.flg & 0x80 ? 'F' : 'P' : '-',
	   xxi->nsm, i2h.ifc, i2h.ifr);

	return 0;
}

static void force_sample_length(struct xmp_sample *xxs, int len)
{
	xxs->len = len;

	if (xxs->lpe > xxs->len)
		xxs->lpe = xxs->len;

	if (xxs->lps >= xxs->len)
		xxs->flg &= ~XMP_SAMPLE_LOOP;
}

static int load_it_sample(struct module_data *m, int i, int start,
			  int sample_mode, HIO_HANDLE *f)
{
	struct it_sample_header ish;
	struct xmp_module *mod = &m->mod;
	struct xmp_sample *xxs, *xsmp;
	int j, k;
	uint8 buf[80];

	if (sample_mode) {
		mod->xxi[i].sub = (struct xmp_subinstrument *)calloc(sizeof(struct xmp_subinstrument), 1);
		if (mod->xxi[i].sub == NULL) {
			return -1;
		}
	}

	if (hio_read(buf, 1, 80, f) != 80) {
		return -1;
	}

	ish.magic = readmem32b(buf);
	/* Changed to continue to allow use-brdg.it and use-funk.it to
	 * load correctly (both IT 2.04)
	 */
	if (ish.magic != MAGIC_IMPS) {
		return 0;
	}

	xxs = &mod->xxs[i];
	xsmp = &m->xsmp[i];

	memcpy(ish.dosname, buf + 4, 12);
	ish.zero = buf[16];
	ish.gvl = buf[17];
	ish.flags = buf[18];
	ish.vol = buf[19];

	memcpy(ish.name, buf + 20, 26);
	fix_name(ish.name, 26);

	ish.convert = buf[46];
	ish.dfp = buf[47];
	ish.length = readmem32l(buf + 48);
	ish.loopbeg = readmem32l(buf + 52);
	ish.loopend = readmem32l(buf + 56);
	ish.c5spd = readmem32l(buf + 60);
	ish.sloopbeg = readmem32l(buf + 64);
	ish.sloopend = readmem32l(buf + 68);
	ish.sample_ptr = readmem32l(buf + 72);
	ish.vis = buf[76];
	ish.vid = buf[77];
	ish.vir = buf[78];
	ish.vit = buf[79];

	if (ish.flags & IT_SMP_16BIT) {
		xxs->flg = XMP_SAMPLE_16BIT;
	}
	xxs->len = ish.length;

	xxs->lps = ish.loopbeg;
	xxs->lpe = ish.loopend;
	xxs->flg |= ish.flags & IT_SMP_LOOP ? XMP_SAMPLE_LOOP : 0;
	xxs->flg |= ish.flags & IT_SMP_BLOOP ? XMP_SAMPLE_LOOP_BIDIR : 0;
	xxs->flg |= ish.flags & IT_SMP_SLOOP ? XMP_SAMPLE_SLOOP : 0;
	xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0;

	if (ish.flags & IT_SMP_SLOOP) {
		memcpy(xsmp, xxs, sizeof (struct xmp_sample));
		xsmp->lps = ish.sloopbeg;
		xsmp->lpe = ish.sloopend;
		xsmp->flg |= XMP_SAMPLE_LOOP;
		xsmp->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
		if (ish.flags & IT_SMP_BSLOOP) {
			xsmp->flg |= XMP_SAMPLE_LOOP_BIDIR;
		}
	}

	if (sample_mode) {
		/* Create an instrument for each sample */
		mod->xxi[i].vol = 64;
		mod->xxi[i].sub[0].vol = ish.vol;
		mod->xxi[i].sub[0].pan = 0x80;
		mod->xxi[i].sub[0].sid = i;
		mod->xxi[i].nsm = !!(xxs->len);
		libxmp_instrument_name(mod, i, ish.name, 25);
	} else {
		libxmp_copy_adjust(xxs->name, ish.name, 25);
	}

	D_(D_INFO "\n[%2X] %-26.26s %05x%c%05x %05x %05x %05x "
	   "%02x%02x %02x%02x %5d ",
	   i, sample_mode ? xxs->name : mod->xxs[i].name,
	   xxs->len,
	   ish.flags & IT_SMP_16BIT ? '+' : ' ',
	   MIN(xxs->lps, 0xfffff), MIN(xxs->lpe, 0xfffff),
	   MIN(ish.sloopbeg, 0xfffff), MIN(ish.sloopend, 0xfffff),
	   ish.flags, ish.convert, ish.vol, ish.gvl, ish.c5spd);

	/* Convert C5SPD to relnote/finetune
	 *
	 * In IT we can have a sample associated with two or more
	 * instruments, but c5spd is a sample attribute -- so we must
	 * scan all xmp instruments to set the correct transposition
	 */

	for (j = 0; j < mod->ins; j++) {
		for (k = 0; k < mod->xxi[j].nsm; k++) {
			struct xmp_subinstrument *sub = &mod->xxi[j].sub[k];
			if (sub->sid == i) {
				sub->vol = ish.vol;
				sub->gvl = ish.gvl;
				sub->vra = ish.vis;	/* sample to sub-instrument vibrato */
				sub->vde = ish.vid << 1;
				sub->vwf = ish.vit;
				sub->vsw = (0xff - ish.vir) >> 1;

				libxmp_c2spd_to_note(ish.c5spd,
					      &mod->xxi[j].sub[k].xpo,
					      &mod->xxi[j].sub[k].fin);

				/* Set sample pan (overrides subinstrument) */
				if (ish.dfp & 0x80) {
					sub->pan = (ish.dfp & 0x7f) * 4;
				} else if (sample_mode) {
					sub->pan = -1;
				}
			}
		}
	}

	if (ish.flags & IT_SMP_SAMPLE && xxs->len > 1) {
		int cvt = 0;

		/* Sanity check - some modules may have invalid sizes on
		 * unused samples so only check this if the sample flag is set. */
		if (xxs->len > MAX_SAMPLE_SIZE) {
			return -1;
		}

		if (0 != hio_seek(f, start + ish.sample_ptr, SEEK_SET))
			return -1;

		if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe)
			xxs->flg &= ~XMP_SAMPLE_LOOP;

		if (~ish.convert & IT_CVT_SIGNED)
			cvt |= SAMPLE_FLAG_UNS;

		/* compressed samples */
		if (ish.flags & IT_SMP_COMP) {
			long min_size, file_len, left;
			void *decbuf;
			int ret;

			/* Sanity check - the lower bound on IT compressed
			 * sample size (in bytes) is a little over 1/8th of the
			 * number of SAMPLES in the sample.
			 */
			file_len = hio_size(f);
			min_size = xxs->len >> 3;
			left = file_len - (long)ish.sample_ptr;
			/* No data to read at all? Just skip it... */
			if (left <= 0)
				return 0;

			if ((file_len > 0) && (left < min_size)) {
				D_(D_WARN "sample %X failed minimum size check "
				   "(len=%d, needs >=%ld bytes, %ld available): "
				   "resizing to %ld",
				   i, xxs->len, min_size, left, left << 3);

				force_sample_length(xxs, left << 3);
				if (ish.flags & IT_SMP_SLOOP)
					force_sample_length(xsmp, left << 3);
			}

			decbuf = calloc(1, xxs->len * 2);
			if (decbuf == NULL)
				return -1;

			if (ish.flags & IT_SMP_16BIT) {
				itsex_decompress16(f, (int16 *)decbuf, xxs->len,
						   ish.convert & IT_CVT_DIFF);

#ifdef WORDS_BIGENDIAN
				/* decompression generates native-endian
				 * samples, but we want little-endian
				 */
				cvt |= SAMPLE_FLAG_BIGEND;
#endif
			} else {
				itsex_decompress8(f, (uint8 *)decbuf, xxs->len,
						  ish.convert & IT_CVT_DIFF);
			}

			if (ish.flags & IT_SMP_SLOOP) {
				long pos = hio_tell(f);
				if (pos < 0) {
					free(decbuf);
					return -1;
				}
				ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD |
							cvt, &m->xsmp[i], decbuf);
				if (ret < 0) {
					free(decbuf);
					return -1;
				}
				hio_seek(f, pos, SEEK_SET);
			}

			ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt,
					  &mod->xxs[i], decbuf);
			if (ret < 0) {
				free(decbuf);
				return -1;
			}

			free(decbuf);
		} else {
			if (ish.flags & IT_SMP_SLOOP) {
				long pos = hio_tell(f);
				if (pos < 0) {
					return -1;
				}
				if (libxmp_load_sample(m, f, cvt, &m->xsmp[i], NULL) < 0)
					return -1;
				hio_seek(f, pos, SEEK_SET);
			}

			if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0)
				return -1;
		}
	}

	return 0;
}

static int load_it_pattern(struct module_data *m, int i, int new_fx,
			   HIO_HANDLE *f)
{
	struct xmp_module *mod = &m->mod;
	struct xmp_event *event, dummy, lastevent[L_CHANNELS];
	uint8 mask[L_CHANNELS];
	uint8 last_fxp[64];

	int r, c, pat_len, num_rows;
	uint8 b;

	r = 0;

	memset(last_fxp, 0, sizeof(last_fxp));
	memset(lastevent, 0, L_CHANNELS * sizeof(struct xmp_event));
	memset(&dummy, 0, sizeof(struct xmp_event));

	pat_len = hio_read16l(f) /* - 4 */ ;
	mod->xxp[i]->rows = num_rows = hio_read16l(f);

	if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) {
		return -1;
	}

	memset(mask, 0, L_CHANNELS);
	hio_read16l(f);
	hio_read16l(f);

	while (r < num_rows && --pat_len >= 0) {
		b = hio_read8(f);
		if (hio_error(f)) {
			return -1;
		}
		if (!b) {
			r++;
			continue;
		}
		c = (b - 1) & 63;

		if (b & 0x80) {
			mask[c] = hio_read8(f);
			pat_len--;
		}
		/*
		 * WARNING: we IGNORE events in disabled channels. Disabled
		 * channels should be muted only, but we don't know the
		 * real number of channels before loading the patterns and
		 * we don't want to set it to 64 channels.
		 */
		if (c >= mod->chn) {
			event = &dummy;
		} else {
			event = &EVENT(i, c, r);
		}

		if (mask[c] & 0x01) {
			b = hio_read8(f);

			/* From ittech.txt:
			 * Note ranges from 0->119 (C-0 -> B-9)
			 * 255 = note off, 254 = notecut
			 * Others = note fade (already programmed into IT's player
			 *                     but not available in the editor)
			 */
			switch (b) {
			case 0xff:	/* key off */
				b = XMP_KEY_OFF;
				break;
			case 0xfe:	/* cut */
				b = XMP_KEY_CUT;
				break;
			default:
				if (b > 119) {	/* fade */
					b = XMP_KEY_FADE;
				} else {
					b++;	/* note */
				}
			}
			lastevent[c].note = event->note = b;
			pat_len--;
		}
		if (mask[c] & 0x02) {
			b = hio_read8(f);
			lastevent[c].ins = event->ins = b;
			pat_len--;
		}
		if (mask[c] & 0x04) {
			b = hio_read8(f);
			lastevent[c].vol = event->vol = b;
			xlat_volfx(event);
			pat_len--;
		}
		if (mask[c] & 0x08) {
			b = hio_read8(f);
			if (b >= ARRAY_SIZE(fx)) {
				D_(D_WARN "invalid effect %#02x", b);
				hio_read8(f);

			} else {
				event->fxt = b;
				event->fxp = hio_read8(f);

				xlat_fx(c, event, last_fxp, new_fx);
				lastevent[c].fxt = event->fxt;
				lastevent[c].fxp = event->fxp;
			}
			pat_len -= 2;
		}
		if (mask[c] & 0x10) {
			event->note = lastevent[c].note;
		}
		if (mask[c] & 0x20) {
			event->ins = lastevent[c].ins;
		}
		if (mask[c] & 0x40) {
			event->vol = lastevent[c].vol;
			xlat_volfx(event);
		}
		if (mask[c] & 0x80) {
			event->fxt = lastevent[c].fxt;
			event->fxp = lastevent[c].fxp;
		}
	}

	return 0;
}

static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
	struct xmp_module *mod = &m->mod;
	int c, i, j;
	struct it_file_header ifh;
	int max_ch;
	uint32 *pp_ins;		/* Pointers to instruments */
	uint32 *pp_smp;		/* Pointers to samples */
	uint32 *pp_pat;		/* Pointers to patterns */
	int new_fx, sample_mode;

	LOAD_INIT();

	/* Load and convert header */
	ifh.magic = hio_read32b(f);
	if (ifh.magic != MAGIC_IMPM) {
		return -1;
	}

	hio_read(ifh.name, 26, 1, f);
	ifh.hilite_min = hio_read8(f);
	ifh.hilite_maj = hio_read8(f);

	ifh.ordnum = hio_read16l(f);
	ifh.insnum = hio_read16l(f);
	ifh.smpnum = hio_read16l(f);
	ifh.patnum = hio_read16l(f);

	ifh.cwt = hio_read16l(f);
	ifh.cmwt = hio_read16l(f);
	ifh.flags = hio_read16l(f);
	ifh.special = hio_read16l(f);

	ifh.gv = hio_read8(f);
	ifh.mv = hio_read8(f);
	ifh.is = hio_read8(f);
	ifh.it = hio_read8(f);
	ifh.sep = hio_read8(f);
	ifh.pwd = hio_read8(f);

	/* Sanity check */
	if (ifh.gv > 0x80 || ifh.mv > 0x80) {
		D_(D_CRIT "invalid gv (%u) or mv (%u)", ifh.gv, ifh.mv);
		goto err;
	}

	ifh.msglen = hio_read16l(f);
	ifh.msgofs = hio_read32l(f);
	ifh.rsvd = hio_read32l(f);

	hio_read(ifh.chpan, 64, 1, f);
	hio_read(ifh.chvol, 64, 1, f);

	if (hio_error(f)) {
		D_(D_CRIT "error reading IT header");
		goto err;
	}

	memcpy(mod->name, ifh.name, sizeof(ifh.name));
	/* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */
	mod->name[sizeof(ifh.name)] = '\0';
	mod->len = ifh.ordnum;
	mod->ins = ifh.insnum;
	mod->smp = ifh.smpnum;
	mod->pat = ifh.patnum;

	/* Sanity check */
	if (mod->ins > 255 || mod->smp > 255 || mod->pat > 255) {
		D_(D_CRIT "invalid ins (%u), smp (%u), or pat (%u)",
		   mod->ins, mod->smp, mod->pat);
		goto err;
	}

	if (mod->ins) {
		pp_ins = (uint32 *)calloc(4, mod->ins);
		if (pp_ins == NULL)
			goto err;
	} else {
		pp_ins = NULL;
	}

	pp_smp = (uint32 *)calloc(4, mod->smp);
	if (pp_smp == NULL)
		goto err2;

	pp_pat = (uint32 *)calloc(4, mod->pat);
	if (pp_pat == NULL)
		goto err3;

	mod->spd = ifh.is;
	mod->bpm = ifh.it;

	sample_mode = ~ifh.flags & IT_USE_INST;

	if (ifh.flags & IT_LINEAR_FREQ) {
		m->period_type = PERIOD_LINEAR;
	}

	for (i = 0; i < 64; i++) {
		struct xmp_channel *xxc = &mod->xxc[i];

		if (ifh.chpan[i] == 100) {	/* Surround -> center */
			xxc->flg |= XMP_CHANNEL_SURROUND;
		}

		if (ifh.chpan[i] & 0x80) {	/* Channel mute */
			xxc->flg |= XMP_CHANNEL_MUTE;
		}

		if (ifh.flags & IT_STEREO) {
			xxc->pan = (int)ifh.chpan[i] * 0x80 >> 5;
			if (xxc->pan > 0xff)
				xxc->pan = 0xff;
		} else {
			xxc->pan = 0x80;
		}

		xxc->vol = ifh.chvol[i];
	}

	if (mod->len <= XMP_MAX_MOD_LENGTH) {
		hio_read(mod->xxo, 1, mod->len, f);
	} else {
		hio_read(mod->xxo, 1, XMP_MAX_MOD_LENGTH, f);
		hio_seek(f, mod->len - XMP_MAX_MOD_LENGTH, SEEK_CUR);
		mod->len = XMP_MAX_MOD_LENGTH;
	}

	new_fx = ifh.flags & IT_OLD_FX ? 0 : 1;

	for (i = 0; i < mod->ins; i++)
		pp_ins[i] = hio_read32l(f);
	for (i = 0; i < mod->smp; i++)
		pp_smp[i] = hio_read32l(f);
	for (i = 0; i < mod->pat; i++)
		pp_pat[i] = hio_read32l(f);

	m->c4rate = C4_NTSC_RATE;

	identify_tracker(m, &ifh);

	MODULE_INFO();

	D_(D_INFO "Instrument/FX mode: %s/%s",
	   sample_mode ? "sample" : ifh.cmwt >= 0x200 ?
	   "new" : "old", ifh.flags & IT_OLD_FX ? "old" : "IT");

	if (sample_mode)
		mod->ins = mod->smp;

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

	/* Alloc extra samples for sustain loop */
	if (mod->smp > 0) {
		m->xsmp = (struct xmp_sample *)calloc(sizeof (struct xmp_sample), mod->smp);
		if (m->xsmp == NULL) {
			goto err4;
		}
	}

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

	for (i = 0; i < mod->ins; i++) {
		/*
		 * IT files can have three different instrument types: 'New'
		 * instruments, 'old' instruments or just samples. We need a
		 * different loader for each of them.
		 */

		struct xmp_instrument *xxi = &mod->xxi[i];

		if (!sample_mode && ifh.cmwt >= 0x200) {
			/* New instrument format */
			if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) {
				goto err4;
			}

			if (load_new_it_instrument(xxi, f) < 0) {
				goto err4;
			}

		} else if (!sample_mode) {
			/* Old instrument format */
			if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) {
				goto err4;
			}

			if (load_old_it_instrument(xxi, f) < 0) {
				goto err4;
			}
		}
	}

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

	for (i = 0; i < mod->smp; i++) {

		if (hio_seek(f, start + pp_smp[i], SEEK_SET) < 0) {
			goto err4;
		}

		if (load_it_sample(m, i, start, sample_mode, f) < 0) {
			goto err4;
		}
	}
	/* Reset any error status set by truncated samples. */
	hio_error(f);

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

	/* Effects in muted channels are processed, so scan patterns first to
	 * see the real number of channels
	 */
	max_ch = 0;
	for (i = 0; i < mod->pat; i++) {
		uint8 mask[L_CHANNELS];
		int pat_len, num_rows, row;

		/* If the offset to a pattern is 0, the pattern is empty */
		if (pp_pat[i] == 0)
			continue;

		hio_seek(f, start + pp_pat[i], SEEK_SET);
		pat_len = hio_read16l(f) /* - 4 */ ;
		num_rows = hio_read16l(f);
		memset(mask, 0, L_CHANNELS);
		hio_read16l(f);
		hio_read16l(f);

		/* Sanity check:
		 * - Impulse Tracker and Schism Tracker allow up to 200 rows.
		 * - ModPlug Tracker 1.16 allows 256 rows.
		 * - OpenMPT allows 1024 rows.
		 */
		if (num_rows > 1024) {
			D_(D_WARN "skipping pattern %d (%d rows)", i, num_rows);
			pp_pat[i] = 0;
			continue;
		}

		row = 0;
		while (row < num_rows && --pat_len >= 0) {
			int b = hio_read8(f);
			if (hio_error(f)) {
				D_(D_CRIT "error scanning pattern %d", i);
				goto err4;
			}
			if (b == 0) {
				row++;
				continue;
			}

			c = (b - 1) & 63;

			if (c > max_ch)
				max_ch = c;

			if (b & 0x80) {
				mask[c] = hio_read8(f);
				pat_len--;
			}

			if (mask[c] & 0x01) {
				hio_read8(f);
				pat_len--;
			}
			if (mask[c] & 0x02) {
				hio_read8(f);
				pat_len--;
			}
			if (mask[c] & 0x04) {
				hio_read8(f);
				pat_len--;
			}
			if (mask[c] & 0x08) {
				hio_read8(f);
				hio_read8(f);
				pat_len -= 2;
			}
		}
	}

	/* Set the number of channels actually used
	 */
	mod->chn = max_ch + 1;
	mod->trk = mod->pat * mod->chn;

	if (libxmp_init_pattern(mod) < 0) {
		goto err4;
	}

	/* Read patterns */
	for (i = 0; i < mod->pat; i++) {

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

		/* If the offset to a pattern is 0, the pattern is empty */
		if (pp_pat[i] == 0) {
			mod->xxp[i]->rows = 64;
			for (j = 0; j < mod->chn; j++) {
				int tnum = i * mod->chn + j;
				if (libxmp_alloc_track(mod, tnum, 64) < 0)
					goto err4;
				mod->xxp[i]->index[j] = tnum;
			}
			continue;
		}

		if (hio_seek(f, start + pp_pat[i], SEEK_SET) < 0) {
			D_(D_CRIT "error seeking to %d", start + pp_pat[i]);
			goto err4;
		}

		if (load_it_pattern(m, i, new_fx, f) < 0) {
			D_(D_CRIT "error loading pattern %d", i);
			goto err4;
		}
	}

	free(pp_pat);
	free(pp_smp);
	free(pp_ins);

	/* Song message */

	if (ifh.special & IT_HAS_MSG) {
		if ((m->comment = (char *)malloc(ifh.msglen)) != NULL) {
			hio_seek(f, start + ifh.msgofs, SEEK_SET);

			D_(D_INFO "Message length : %d", ifh.msglen);

			for (j = 0; j + 1 < ifh.msglen; j++) {
				int b = hio_read8(f);
				if (b == '\r') {
					b = '\n';
				} else if ((b < 32 || b > 127) && b != '\n'
					   && b != '\t') {
					b = '.';
				}
				m->comment[j] = b;
			}

			if (ifh.msglen > 0) {
				m->comment[j] = 0;
			}
		}
	}

	/* Format quirks */

	m->quirk |= QUIRKS_IT | QUIRK_ARPMEM | QUIRK_INSVOL;

	if (ifh.flags & IT_LINK_GXX) {
		m->quirk |= QUIRK_PRENV;
	} else {
		m->quirk |= QUIRK_UNISLD;
	}

	if (new_fx) {
		m->quirk |= QUIRK_VIBHALF | QUIRK_VIBINV;
	} else {
		m->quirk &= ~QUIRK_VIBALL;
		m->quirk |= QUIRK_ITOLDFX;
	}

	if (sample_mode) {
		m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN);
	}

	m->gvolbase = 0x80;
	m->gvol = ifh.gv;
	m->read_event_type = READ_EVENT_IT;

	return 0;

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

#endif /* LIBXMP_CORE_DISABLE_IT */


code:
#ifndef LIBXMP_CORE_DISABLE_IT

/* Public domain IT sample decompressor by Olivier Lapicque */

#include "loader.h"
#include "it.h"

static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n, int *err)
{
	uint32 retval = 0;
	int i = n;
	int bnum = *bitnum;
	uint32 bbuf = *bitbuf;

	if (n > 0 && n <= 32) {
		do {
			if (bnum == 0) {
				if (hio_eof(ibuf)) {
					*err = EOF;
					return 0;
				}
				bbuf = hio_read8(ibuf);
				bnum = 8;
			}
			retval >>= 1;
			retval |= bbuf << 31;
			bbuf >>= 1;
			bnum--;
			i--;
		} while (i != 0);

		i = n;

		*bitnum = bnum;
		*bitbuf = bbuf;
	} else {
		/* Invalid shift value. */
		*err = -2;
		return 0;
	}

	return (retval >> (32 - i));
}

int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
{
	/* uint32 size = 0; */
	uint32 block_count = 0;
	uint32 bitbuf = 0;
	int bitnum = 0;
	uint8 left = 0, temp = 0, temp2 = 0;
	uint32 d, pos;
	int err = 0;

	while (len) {
		if (!block_count) {
			block_count = 0x8000;
			/*size =*/ hio_read16l(src);
			left = 9;
			temp = temp2 = 0;
			bitbuf = bitnum = 0;
		}

		d = block_count;
		if (d > len)
			d = len;

		/* Unpacking */
		pos = 0;
		do {
			uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
			if (err != 0)
				return -1;

			if (left < 7) {
				uint32 i = 1 << (left - 1);
				uint32 j = bits & 0xffff;
				if (i != j)
					goto unpack_byte;
				bits = (read_bits(src, &bitbuf, &bitnum, 3, &err)
								+ 1) & 0xff;
				if (err != 0)
					return -1;

				left = ((uint8)bits < left) ?  (uint8)bits :
						(uint8)((bits + 1) & 0xff);
				goto next;
			}

			if (left < 9) {
				uint16 i = (0xff >> (9 - left)) + 4;
				uint16 j = i - 8;

				if ((bits <= j) || (bits > i))
					goto unpack_byte;

				bits -= j;
				left = ((uint8)(bits & 0xff) < left) ?
						(uint8)(bits & 0xff) :
						(uint8)((bits + 1) & 0xff);
				goto next;
			}

			if (left >= 10)
				goto skip_byte;

			if (bits >= 256) {
				left = (uint8) (bits + 1) & 0xff;
				goto next;
			}

		    unpack_byte:
			if (left < 8) {
				uint8 shift = 8 - left;
				signed char c = (signed char)(bits << shift);
				c >>= shift;
				bits = (uint16) c;
			}
			bits += temp;
			temp = (uint8)bits;
			temp2 += temp;
			dst[pos] = it215 ? temp2 : temp;

		    skip_byte:
			pos++;

		    next:
			/* if (slen <= 0)
				return -1 */;
		} while (pos < d);

		/* Move On */
		block_count -= d;
		len -= d;
		dst += d;
	}

	return 0;
}

int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
{
	/* uint32 size = 0; */
	uint32 block_count = 0;
	uint32 bitbuf = 0;
	int bitnum = 0;
	uint8 left = 0;
	int16 temp = 0, temp2 = 0;
	uint32 d, pos;
	int err = 0;

	while (len) {
		if (!block_count) {
			block_count = 0x4000;
			/*size =*/ hio_read16l(src);
			left = 17;
			temp = temp2 = 0;
			bitbuf = bitnum = 0;
		}

		d = block_count;
		if (d > len)
			d = len;

		/* Unpacking */
		pos = 0;
		do {
			uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
			if (err != 0)
				return -1;

			if (left < 7) {
				uint32 i = 1 << (left - 1);
				uint32 j = bits;

				if (i != j)
					goto unpack_byte;

				bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1;
				if (err != 0)
					return -1;

				left = ((uint8)(bits & 0xff) < left) ?
						(uint8)(bits & 0xff) :
						(uint8)((bits + 1) & 0xff);
				goto next;
			}

			if (left < 17) {
				uint32 i = (0xffff >> (17 - left)) + 8;
				uint32 j = (i - 16) & 0xffff;

				if ((bits <= j) || (bits > (i & 0xffff)))
					goto unpack_byte;

				bits -= j;
				left = ((uint8)(bits & 0xff) < left) ?
						(uint8)(bits & 0xff) :
						(uint8)((bits + 1) & 0xff);
				goto next;
			}

			if (left >= 18)
				goto skip_byte;

			if (bits >= 0x10000) {
				left = (uint8)(bits + 1) & 0xff;
				goto next;
			}

		    unpack_byte:
			if (left < 16) {
				uint8 shift = 16 - left;
				int16 c = (int16)(bits << shift);
				c >>= shift;
				bits = (uint32) c;
			}
			bits += temp;
			temp = (int16)bits;
			temp2 += temp;
			dst[pos] = (it215) ? temp2 : temp;

		    skip_byte:
			pos++;

		    next:
			/* if (slen <= 0)
				return -1 */;
		} while (pos < d);

		/* Move On */
		block_count -= d;
		len -= d;
		dst += d;
		if (len <= 0)
			break;
	}

	return 0;
}

#endif /* LIBXMP_CORE_DISABLE_IT */


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 "lfo.h"

#define WAVEFORM_SIZE 64

static const int sine_wave[WAVEFORM_SIZE] = {
	   0,  24,  49,  74,  97, 120, 141, 161, 180, 197, 212, 224,
	 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197,
	 180, 161, 141, 120,  97,  74,  49,  24,   0, -24, -49, -74,
	 -97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253,
	-255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,
	 -97, -74, -49, -24
};

/* LFO */

static int get_lfo_mod(struct lfo *lfo)
{
	int val;

	if (lfo->rate == 0)
		return 0;

	switch (lfo->type) {
	case 0: /* sine */
		val = sine_wave[lfo->phase];
		break;
	case 1:	/* ramp down */
		val = 255 - (lfo->phase << 3);
		break;
	case 2:	/* square */
		val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : -255;
		break;
	case 3: /* random */
		val = ((rand() & 0x1ff) - 256);
		break;
#ifndef LIBXMP_CORE_PLAYER
	case 669: /* 669 vibrato */
		val = lfo->phase & 1;
		break;
#endif
	default:
		return 0;
	}

	return val * lfo->depth;
}

static int get_lfo_st3(struct lfo *lfo)
{
	if (lfo->rate == 0) {
		return 0;
	}

	/* S3M square */
	if (lfo->type == 2) {
		int val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : 0;
		return val * lfo->depth;
	}

	return get_lfo_mod(lfo);
}

/* From OpenMPT VibratoWaveforms.xm:
 * "Generally the vibrato and tremolo tables are identical to those that
 *  ProTracker uses, but the vibrato’s “ramp down” table is upside down."
 */
static int get_lfo_ft2(struct lfo *lfo)
{
	if (lfo->rate == 0)
		return 0;

	/* FT2 ramp */
	if (lfo->type == 1) {
		int phase = (lfo->phase + (WAVEFORM_SIZE >> 1)) % WAVEFORM_SIZE;
		int val = (phase << 3) - 255;
		return val * lfo->depth;
	}

	return get_lfo_mod(lfo);
}

#ifndef LIBXMP_CORE_DISABLE_IT

static int get_lfo_it(struct lfo *lfo)
{
	if (lfo->rate == 0)
		return 0;

	return get_lfo_st3(lfo);
}

#endif

int libxmp_lfo_get(struct context_data *ctx, struct lfo *lfo, int is_vibrato)
{
	struct module_data *m = &ctx->m;

	switch (m->read_event_type) {
	case READ_EVENT_ST3:
		return get_lfo_st3(lfo);
	case READ_EVENT_FT2:
		if (is_vibrato) {
			return get_lfo_ft2(lfo);
		} else {
			return get_lfo_mod(lfo);
		}
#ifndef LIBXMP_CORE_DISABLE_IT
	case READ_EVENT_IT:
		return get_lfo_it(lfo);
#endif
	default:
		return get_lfo_mod(lfo);
	}
}

void libxmp_lfo_update(struct lfo *lfo)
{
	lfo->phase += lfo->rate;
	lfo->phase %= WAVEFORM_SIZE;
}

void libxmp_lfo_set_phase(struct lfo *lfo, int phase)
{
	lfo->phase = phase;
}

void libxmp_lfo_set_depth(struct lfo *lfo, int depth)
{
	lfo->depth = depth;
}

void libxmp_lfo_set_rate(struct lfo *lfo, int rate)
{
	lfo->rate = rate;
}

void libxmp_lfo_set_waveform(struct lfo *lfo, int type)
{
	lfo->type = type;
}


code:
#ifndef LIBXMP_LFO_H
#define LIBXMP_LFO_H

#include "common.h"

struct lfo {
	int type;
	int rate;
	int depth;
	int phase;
};

int  libxmp_lfo_get(struct context_data *, struct lfo *, int);
void libxmp_lfo_update(struct lfo *);
void libxmp_lfo_set_phase(struct lfo *, int);
void libxmp_lfo_set_depth(struct lfo *, int);
void libxmp_lfo_set_rate(struct lfo *, int);
void libxmp_lfo_set_waveform(struct lfo *, int);

#endif