/* Extended Module Player
 * Copyright (C) 1996-1999 Claudio Matsuoka and Hipolito Carraro Jr.
 *
 * This file is part of the Extended Module Player and is distributed
 * under the terms of the GNU General Public License. See doc/COPYING
 * for more information.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "load.h"
#include "it.h"
#include "period.h"

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


static uint32 *pp_ins;		/* Pointers to instruments */
static uint32 *pp_smp;		/* Pointers to samples */
static uint32 *pp_pat;		/* Pointers to patterns */
static uint8 arpeggio_val[64];

static uint8 fx[] = {
    /*   */ FX_NONE,		/* A */ FX_S3M_TEMPO,
    /* B */ FX_JUMP,		/* C */ FX_BREAK,
    /* D */ FX_VOLSLIDE,	/* E */ FX_PORTA_DN,
    /* F */ FX_PORTA_UP,	/* G */ FX_TONEPORTA,
    /* H */ FX_VIBRATO,		/* I */ FX_TREMOR,
    /* J */ FX_ARPEGGIO,	/* K */ FX_VIBRA_VSLIDE,
    /* L */ FX_TONE_VSLIDE,	/* M */ FX_TRK_VOL,
    /* N */ FX_TRK_VSLIDE,	/* O */ FX_OFFSET,
    /* P */ FX_NONE,		/* Q */ FX_MULTI_RETRIG,
    /* R */ FX_TREMOLO,		/* S */ FX_XTND,
    /* T */ FX_TEMPO,		/* U */ FX_NONE,
    /* V */ FX_GLOBALVOL,	/* W */ FX_G_VOLSLIDE,
    /* X */ FX_NONE,		/* Y */ FX_NONE,
    /* Z */ FX_FLT_CUTOFF
};

static char *nna[] = { "cut", "cont", "off", "fade" };
static char *dct[] = { "off", "note", "smp", "inst" };
static int dca2nna[] = { 0, 2, 3 };

int itsex_decompress8 (FILE *, void *, int, int);
int itsex_decompress16 (FILE *, void *, int, int);


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

    switch (e->fxt = fx[e->fxt]) {
    case FX_ARPEGGIO:		/* Arpeggio */
	if (e->fxp)
	    arpeggio_val[c] = e->fxp;
	else
	    e->fxp = arpeggio_val[c];
	break;
    case FX_TEMPO:
	if (e->fxp < 0x21)
	    e->fxp = 0x21;
	break;
    case FX_S3M_TEMPO:
	if (e->fxp <= 0x20)
	    e->fxt = FX_TEMPO;
	break;
    case FX_XTND:		/* Extended effect */
	e->fxt = FX_EXTENDED;
	switch (h) {
	case 0x1:		/* Glissando */
	    e->fxp = 0x30 | l;
	    break;
	case 0x2:		/* Finetune */
	    e->fxp = 0x50 | l;
	    break;
	case 0x3:		/* Vibrato wave */
	    e->fxp = 0x40 | l;
	    break;
	case 0x4:		/* Tremolo wave */
	    e->fxp = 0x70 | l;
	    break;
	case 0x5:		/* Panbrello wave -- NOT IMPLEMENTED */
	    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 -- NOT IMPLEMENTED */
	    e->fxt = e->fxp = 0;
	    break;
	case 0xa:		/* Not defined */
	    e->fxt = e->fxp = 0;
	    break;
	case 0xb:		/* Pattern loop */
	    e->fxp = 0x60 | l;
	    break;
	}
	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_NONE:		/* No effect */
	e->fxt = e->fxp = 0;
	break;
    }
}


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

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

    if (b <= 0x40) {
	event->vol = b + 1;
    } else if (b >= 65 && b <= 74) {
	event->f2t = FX_EXTENDED;
	event->f2p = (EX_F_VSLIDE_UP << 4) | (b - 65);
    } else if (b >= 75 && b <= 84) {
	event->f2t = FX_EXTENDED;
	event->f2p = (EX_F_VSLIDE_DN << 4) | (b - 75);
    } else if (b >= 85 && b <= 94) {
	event->f2t = FX_VOLSLIDE_2;
	event->f2p = (b - 85) << 4;
    } else if (b >= 95 && b <= 104) {
	event->f2t = FX_VOLSLIDE_2;
	event->f2p = b - 95;
    } else if (b >= 105 && b <= 114) {
	event->f2t = FX_PORTA_DN;
	event->f2p = (b - 105) << 2;
    } else if (b >= 115 && b <= 124) {
	event->f2t = FX_PORTA_UP;
	event->f2p = (b - 115) << 2;
    } else if (b >= 128 && b <= 192) {
	if (b == 192)
	    b = 191;
	event->f2t = FX_SETPAN;
	event->f2p = (b - 128) << 2;
    } else if (b >= 193 && b <= 202) {
	event->f2t = FX_TONEPORTA;
	event->f2p = 1 << (b - 193);
#if 0
    } else if (b >= 193 && b <= 202) {
	event->f2t = FX_VIBRATO;
	event->f2p = ???;
#endif
    }
}


int it_load (FILE * f)
{
    int r, c, i, j, k, pat_len;
    struct xxm_event *event, dummy, lastevent[L_CHANNELS];
    struct it_file_header ifh;
    struct it_instrument1_header i1h;
    struct it_instrument2_header i2h;
    struct it_sample_header ish;
    struct it_envelope env;
    uint8 b, mask[L_CHANNELS];
    uint16 x16;
    int max_ch, flag;
    int inst_map[120], inst_rmap[96];

    LOAD_INIT ();

    /* Load and convert header */
    fread (&ifh, 1, sizeof (ifh), f);
    if (strncmp ((char *) ifh.magic, "IMPM", 4))
	return -1;
    L_ENDIAN16 (ifh.ordnum);
    L_ENDIAN16 (ifh.insnum);
    L_ENDIAN16 (ifh.smpnum);
    L_ENDIAN16 (ifh.patnum);
    L_ENDIAN16 (ifh.cwt);
    L_ENDIAN16 (ifh.cmwt);
    L_ENDIAN16 (ifh.flags);
    L_ENDIAN16 (ifh.special);
    L_ENDIAN16 (ifh.msglen);
    L_ENDIAN32 (ifh.msgofs);
    strcpy (xmp_ctl->name, (char *) ifh.name);
    xxh->len = ifh.ordnum;
    xxh->ins = ifh.insnum;
    xxh->smp = ifh.smpnum;
    xxh->pat = ifh.patnum;
    if (xxh->ins)		/* Sample mode has no instruments */
	pp_ins = calloc (4, xxh->ins);
    pp_smp = calloc (4, xxh->smp);
    pp_pat = calloc (4, xxh->pat);
    xxh->tpo = ifh.is;
    xxh->bpm = ifh.it;
    xxh->flg = ifh.flags & IT_LINEAR_FREQ ? XXM_FLG_LINEAR : 0;
    xxh->flg |= ifh.flags & IT_USE_INST ? XXM_FLG_INSVOL : 0;
    for (i = 0; i < 64; i++)
	xxc[i].vol = ifh.chvol[i];
    for (i = 0; i < 64; i++) {
	if (ifh.chpan[i] == 100)
	    ifh.chpan[i] = 32;
	if (~ifh.chpan[i] & 0x80)
	    xxh->chn = i + 1;
	else
	    ifh.chvol[i] = 0;
	xxc[i].pan = ifh.flags & IT_STEREO ?
	    ((int) ifh.chpan[i] * 0x80 >> 5) & 0xff : 0x80;
    }
    fread (xxo, 1, xxh->len, f);

    /* S3M skips pattern 0xfe */
    for (i = 0; i < (xxh->len - 1); i++)
	if (xxo[i] == 0xfe) {
	    memcpy (&xxo[i], &xxo[i + 1], xxh->len - i - 1);
	    xxh->len--;
	}
    for (i = 0; i < xxh->ins; i++) {
	fread (&pp_ins[i], 4, 1, f);
	L_ENDIAN32 (pp_ins[i]);
    }
    for (i = 0; i < xxh->smp; i++) {
	fread (&pp_smp[i], 4, 1, f);
	L_ENDIAN32 (pp_smp[i]);
    }
    for (i = 0; i < xxh->pat; i++) {
	fread (&pp_pat[i], 4, 1, f);
	L_ENDIAN32 (pp_pat[i]);
    }

    xmp_ctl->c4rate = C4_NTSC_RATE;
    xmp_ctl->fetch |= XMP_CTL_FINEFX | XMP_CTL_ENVFADE;

    sprintf (xmp_ctl->type, "IMPM %d.%02x (IT)",
	ifh.cmwt >> 8, ifh.cmwt & 0xff);

    switch (ifh.cwt >> 12) {
    case 0:
	sprintf (tracker_name, "Impulse Tracker");
	break;
    default:
	sprintf (tracker_name, "unknown (%d) version",
	    ifh.cwt >> 12);
    }

    sprintf (tracker_name + strlen (tracker_name), " %d.%02x",
	(ifh.cwt & 0x0f00) >> 8, ifh.cwt & 0xff);

    MODULE_INFO ();

    if (V (0))
	report ("Instr/FX mode  : %s/%s",
	    ifh.flags & IT_USE_INST ? ifh.cmwt >= 0x200 ?
	    "new" : "old" : "sample",
	    ifh.flags & IT_OLD_FX ? "old" : "IT");

    if (~ifh.flags & IT_USE_INST)
	xxh->ins = xxh->smp;

    if (V (2) && ifh.special & IT_HAS_MSG) {
	report ("\nMessage length : %d\n| ", ifh.msglen);
	i = ftell (f);
	fseek (f, ifh.msgofs, SEEK_SET);
	for (j = 0; j < ifh.msglen; j++) {
	    fread (&b, 1, 1, f);
	    if (b == '\r')
		b = '\n';
	    if ((b < 32 || b > 127) && b != '\n' && b != '\t')
		b = '.';
	    report ("%c", b);
	    if (b == '\n')
		report ("| ");
	}
	fseek (f, i, SEEK_SET);
    }

    INSTRUMENT_INIT ();

    if (xxh->ins && V (0) && (ifh.flags & IT_USE_INST)) {
	report ("\nInstruments    : %d ", xxh->ins);
	if (V (1)) {
	    if (ifh.cmwt >= 0x200)
		report ("\n     Instrument name            NNA  DCT  DCA  "
		    "Fade  GbV Pan RV Env NSm FC FR");
	    else
		report ("\n     Instrument name            NNA  DNC  "
		    "Fade  VolEnv NSm");
	}
    }

    for (i = 0; i < xxh->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.
	 */

	if ((ifh.flags & IT_USE_INST) && (ifh.cmwt >= 0x200)) {
	    /* New instrument format */
	    fseek (f, pp_ins[i], SEEK_SET);
	    fread (&i2h, 1, sizeof (i2h), f);
	    L_ENDIAN16 (i2h.fadeout);
	    L_ENDIAN16 (i2h.trkvers);
	    L_ENDIAN16 (i2h.mbnk);
	    str_adj ((char *) i2h.name);
	    strncpy ((char *) xxih[i].name, i2h.name, 24);
	    xxih[i].rls = i2h.fadeout << 6;

	    /* Envelopes */

#define BUILD_ENV(X) { \
	    fread (&env, 1, sizeof (env), f); \
	    xxih[i].##X##ei.flg = env.flg & IT_ENV_ON ? XXM_ENV_ON : 0; \
	    xxih[i].##X##ei.flg |= env.flg & IT_ENV_LOOP ? XXM_ENV_LOOP : 0; \
	    xxih[i].##X##ei.flg |= env.flg & IT_ENV_SLOOP ? XXM_ENV_SUS : 0; \
	    xxih[i].##X##ei.npt = env.num; \
	    xxih[i].##X##ei.sus = env.slb; \
	    xxih[i].##X##ei.sue = env.sle; \
	    xxih[i].##X##ei.lps = env.lpb; \
	    xxih[i].##X##ei.lpe = env.lpe; \
	    if (env.num) xx##X##e[i] = calloc (4, env.num); \
	    for (j = 0; j < env.num; j++) { \
		L_ENDIAN16 (env.node[j].x); \
		xx##X##e[i][j * 2] = env.node[j].x; \
		xx##X##e[i][j * 2 + 1] = env.node[j].y; \
	    } \
}

	    BUILD_ENV (a);
	    BUILD_ENV (p);
	    BUILD_ENV (f);
	    
	    if (xxih[i].pei.flg & XXM_ENV_ON)
		for (j = 0; j < xxih[i].pei.npt; j++)
		    xxpe[i][j * 2 + 1] += 32;

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

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

	    for (k = j = 0; j < 96; j++) {
		c = i2h.keys[25 + j * 2] - 1;
		if (c < 0) {
		    xxim[i].ins[j] = 0xff;	/* No sample */
		    xxim[i].xpo[j] = 0;
		    continue;
		}
		if (inst_map[c] == -1) {
		    inst_map[c] = k;
		    inst_rmap[k] = c;
		    k++;
		}
		xxim[i].ins[j] = inst_map[c];
		xxim[i].xpo[j] = i2h.keys[24 + j * 2] - (j + 12);
	    }

	    xxih[i].nsm = k;
	    xxih[i].vol = i2h.gbv >> 1;

	    if (k) {
		xxi[i] = calloc (sizeof (struct xxm_instrument), k);
		for (j = 0; j < k; j++) {
		    xxi[i][j].sid = inst_rmap[j];
		    xxi[i][j].nna = i2h.nna;
		    xxi[i][j].dct = i2h.dct;
		    xxi[i][j].dca = dca2nna[i2h.dca & 0x03];
		    xxi[i][j].pan = i2h.dfp & 0x80 ? 0x80 : i2h.dfp * 4;
		    xxi[i][j].ifc = i2h.ifc;
		    xxi[i][j].ifr = i2h.ifr;
	        }
	    }

	    if (V (1))
		report ("\n[%2X] %-26.26s %-4.4s %-4.4s %-4.4s %4d %4d  %2x "
			"%02x %c%c%c %3d %02x %02x ",
		i, i2h.name,
		i2h.nna < 4 ? nna[i2h.nna] : "none",
		i2h.dct < 4 ? dct[i2h.dct] : "none",
		i2h.dca < 3 ? nna[dca2nna[i2h.dca]] : "none",
		i2h.fadeout,
		i2h.gbv,
		i2h.dfp & 0x80 ? 0x80 : i2h.dfp * 4,
		i2h.rv,
		xxih[i].aei.flg & XXM_ENV_ON ? 'V' : '-',
		xxih[i].pei.flg & XXM_ENV_ON ? 'P' : '-',
		env.flg & 0x01 ? env.flg & 0x80 ? 'F' : 'P' : '-',
		xxih[i].nsm,
		i2h.ifc,
		i2h.ifr
	    );

	    if (V (0))
		report (".");

	} else if (ifh.flags & IT_USE_INST) {
/* Old instrument format */
	    fseek (f, pp_ins[i], SEEK_SET);
	    fread (&i1h, 1, sizeof (i1h), f);
	    L_ENDIAN16 (i1h.rsvd1);
	    L_ENDIAN16 (i1h.fadeout);
	    L_ENDIAN16 (i1h.trkvers);
	    str_adj ((char *) i1h.name);
	    strncpy ((char *) xxih[i].name, i1h.name, 24);
	    xxih[i].rls = i1h.fadeout << 7;

	    xxih[i].aei.flg = i1h.flags & IT_ENV_ON ? XXM_ENV_ON : 0;
	    xxih[i].aei.flg |= i1h.flags & IT_ENV_LOOP ? XXM_ENV_LOOP : 0;
	    xxih[i].aei.flg |= i1h.flags & IT_ENV_SLOOP ? XXM_ENV_SUS : 0;
	    xxih[i].aei.lps = i1h.vls;
	    xxih[i].aei.lpe = i1h.vle;
	    xxih[i].aei.sus = i1h.sls;
	    xxih[i].aei.sue = i1h.sle;

	    for (k = 0; i1h.enode[k * 2] != 0xff; k++);
	    xxae[i] = calloc (4, k);
	    for (xxih[i].aei.npt = k; k--; ) {
		xxae[i][k * 2] = i1h.enode[k * 2];
		xxae[i][k * 2 + 1] = i1h.enode[k * 2 + 1];
	    }
	    
	    /* See how many different instruments we have */
	    for (j = 0; j < 96; j++)
		inst_map[j] = -1;

	    for (k = j = 0; j < 96; j++) {
		c = i1h.keys[25 + j * 2] - 1;
		if (c < 0) {
		    xxim[i].ins[j] = 0xff;	/* No sample */
		    xxim[i].xpo[j] = 0;
		    continue;
		}
		if (inst_map[c] == -1) {
		    inst_map[c] = k;
		    inst_rmap[k] = c;
		    k++;
		}
		xxim[i].ins[j] = inst_map[c];
		xxim[i].xpo[j] = i1h.keys[24 + j * 2] - (j + 12);
	    }

	    xxih[i].nsm = k;
	    xxih[i].vol = i2h.gbv >> 1;

	    if (k) {
		xxi[i] = calloc (sizeof (struct xxm_instrument), k);
		for (j = 0; j < k; j++) {
		    xxi[i][j].sid = inst_rmap[j];
		    xxi[i][j].nna = i1h.nna;
		    xxi[i][j].dct = i1h.dnc ? XXM_DCT_NOTE : XXM_DCT_OFF;
		    xxi[i][j].dca = XXM_DCA_CUT;
		    xxi[i][j].pan = 0x80;
	        }
	    }

	    if (V (1))
		report ("\n[%2X] %-26.26s %-4.4s %-4.4s %4d  "
			"%2d %c%c%c %3d ",
		i, i1h.name,
		i1h.nna < 4 ? nna[i1h.nna] : "none",
		i1h.dnc ? "on" : "off",
		i1h.fadeout,
		xxih[i].aei.npt,
		xxih[i].aei.flg & XXM_ENV_ON ? 'V' : '-',
		xxih[i].aei.flg & XXM_ENV_LOOP ? 'L' : '-',
		xxih[i].aei.flg & XXM_ENV_SUS ? 'S' : '-',
		xxih[i].nsm
	    );

	    if (V (0))
		report (".");
	}
    }

    if (V (0))
	report ("\nStored Samples : %d ", xxh->smp);

    if (V (2) || (~ifh.flags & IT_USE_INST && V (1)))
	report (
"\n     Sample name                Len   LBeg  LEnd  SBeg  SEnd  Fl Vl Gv C5Spd"
	);
    
    for (i = 0; i < xxh->smp; i++) {
	if (~ifh.flags & IT_USE_INST)
	    xxi[i] = calloc (sizeof (struct xxm_instrument), 1);
	fseek (f, pp_smp[i], SEEK_SET);
	fread (&ish, 1, sizeof (ish), f);
	if (strncmp (ish.magic, "IMPS", 4))
	    return -1;
	L_ENDIAN32 (ish.length);
	L_ENDIAN32 (ish.loopbeg);
	L_ENDIAN32 (ish.loopend);
	L_ENDIAN32 (ish.c5spd);
	L_ENDIAN32 (ish.sloopbeg);
	L_ENDIAN32 (ish.sloopend);
	L_ENDIAN32 (ish.sample_ptr);
	
	xxs[i].len = ish.length * (1 + !!(ish.flags & IT_SMP_16BIT));
	xxs[i].lps = ish.loopbeg * (1 + !!(ish.flags & IT_SMP_16BIT));
	xxs[i].lpe = ish.loopend * (1 + !!(ish.flags & IT_SMP_16BIT));
	xxs[i].flg = ish.flags & IT_SMP_16BIT ? WAVE_16_BITS : 0;
	xxs[i].flg |= ish.flags & IT_SMP_LOOP ? WAVE_LOOPING : 0;
	xxs[i].flg |= ish.flags & IT_SMP_BLOOP ? WAVE_BIDIR_LOOP : 0;

	if (~ifh.flags & IT_USE_INST) {
	    /* Create an instrument for each sample */
	    xxi[i][0].vol = ish.vol;
	    xxi[i][0].pan = 0x80;
	    xxi[i][0].sid = i;
	    xxih[i].nsm = !!(xxs[i].len);
	    str_adj ((char *) ish.name);
	    strncpy ((char *) xxih[i].name, ish.name, 24);
	}

	if (V (2) || (~ifh.flags & IT_USE_INST && V (1))) {
	    if (strlen ((char *) ish.name) || xxs[i].len > 1) {
		report (
		    "\n[%2X] %-26.26s %05x%c%05x %05x %05x %05x "
		    "%02x %02x %02x %5d ",
		    i, ish.name,
		    xxs[i].len,
		    ish.flags & IT_SMP_16BIT ? '+' : ' ',
		    xxs[i].lps, xxs[i].lpe,
		    ish.sloopbeg, ish.sloopend,
		    ish.flags,
		    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 < xxh->ins; j++) {
	    for (k = 0; k < xxih[j].nsm; k++) {
		if (xxi[j][k].sid == i) {
		    xxi[j][k].vol = ish.vol;
		    xxi[j][k].gvl = ish.gvl;
		    c2spd_to_note (ish.c5spd, &xxi[j][k].xpo, &xxi[j][k].fin);
		}
	    }
	}

	if (ish.flags & IT_SMP_SAMPLE && xxs[i].len > 1) {
	    fseek (f, ish.sample_ptr, SEEK_SET);

	    /* Handle compressed samples using Timmo Hinrichs' routine */
	    if (ish.flags & IT_SMP_COMP) {
		char *buf;
		buf = calloc (1, xxs[i].len);
		if (ish.flags & IT_SMP_16BIT)
		    itsex_decompress16 (f, buf, xxs[i].len >> 1, 
			ifh.cmwt == 0x0215);
		else
		    itsex_decompress8 (f, buf, xxs[i].len,
			ifh.cmwt == 0x0215);
		xmp_drv_loadpatch (NULL, i, xmp_ctl->c4rate,
		    XMP_SMP_NOLOAD, &xxs[i], buf);
		free (buf);
	    } else
		xmp_drv_loadpatch (f, i, xmp_ctl->c4rate,
		    ish.convert & IT_CVT_SIGNED ?  0 : XMP_SMP_UNS,
		    &xxs[i], NULL);
	    if (V (0))
		report (".");
	}
    }

    if (V (0))
	report ("\nStored Patterns: %d ", xxh->pat);

    xxh->trk = xxh->pat * xxh->chn;
    memset (arpeggio_val, 0, 64);

    PATTERN_INIT ();

    /* Read patterns */
    for (max_ch = i = 0; i < xxh->pat; i++) {
	PATTERN_ALLOC (i);
	r = 0;
	/* If the offset to a pattern is 0, the pattern is empty */
	if (!pp_pat[i]) {
	    xxp[i]->rows = 64;
	    xxt[i * xxh->chn] = calloc (sizeof (struct xxm_track) +
		sizeof (struct xxm_event) * 64, 1);
	    xxt[i * xxh->chn]->rows = 64;
	    for (j = 0; j < xxh->chn; j++)
		xxp[i]->info[j].index = i * xxh->chn;
	    continue;
	}
	fseek (f, pp_pat[i], SEEK_SET);
	fread (&x16, 2, 1, f);
	L_ENDIAN16 (x16);
	pat_len = x16 /* - 4*/;
	fread (&x16, 2, 1, f);
	L_ENDIAN16 (x16);
	xxp[i]->rows = x16;
	TRACK_ALLOC (i);
	memset (mask, 0, L_CHANNELS);
	fread (&x16, 2, 1, f);
	fread (&x16, 2, 1, f);

	while (--pat_len >= 0) {
	    fread (&b, 1, 1, f);
	    if (!b) {
		r++;
		continue;
	    }
	    c = (b - 1) & 63;

	    if (b & 0x80) {
		fread (&mask[c], 1, 1, 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.
	     */
	    event = c >= xxh->chn ? &dummy : &EVENT (i, c, r);
	    if (mask[c] & 0x01) {
		fread (&b, 1, 1, f);
		switch (b) {
		case 0xff:	/* key off */
		    b = 0x61;
		    break;
		case 0xfe:	/* cut */
		    b = 0x62;
		    break;
		case 0xfd:	/* fade */
		    b = 0x63;
		    break;
		default:
		    if (b < 11)
			b = 0;
		    else
			b -= 11;
		}
		if (b > 0x62)
		    b = 0;
		lastevent[c].note = event->note = b;
		pat_len--;
	    }
	    if (mask[c] & 0x02) {
		fread (&b, 1, 1, f);
		lastevent[c].ins = event->ins = b;
		pat_len--;
	    }
	    if (mask[c] & 0x04) {
		fread (&b, 1, 1, f);
		lastevent[c].vol = event->vol = b;
		xlat_volfx (event);
		pat_len--;
	    }
	    if (mask[c] & 0x08) {
		fread (&b, 1, 1, f);
		event->fxt = b;
		fread (&b, 1, 1, f);
		event->fxp = b;
		xlat_fx (c, event);
		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;
	    }
	}
	if (V (0))
	    report (".");

	/* Sweep channels, look for unused tracks */
	for (c = xxh->chn - 1; c >= max_ch; c--) {
	    for (flag = j = 0; j < xxt[xxp[i]->info[c].index]->rows; j++) {
		event = &EVENT (i, c, j);
		if (event->note || event->vol || event->ins || event->fxt ||
		    event->fxp || event->f2t || event->f2p) {
		    flag = 1;
		    break;
		}
	    }
	    if (flag && c > max_ch)
		max_ch = c;
	}
    }

    xxh->chn = max_ch + 1;
    xmp_ctl->fetch |= ifh.flags & IT_USE_INST ? XMP_MODE_IT : XMP_MODE_ST3;
    xmp_ctl->fetch |= XMP_CTL_VIRTUAL;

    if (V (0))
	report ("\n");

    return XMP_OK;
}
