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

/* Loader for Imago Orpheus modules based on the format description
 * written by Lutz Roeder.
 */

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

#include "load.h"
#include "imf.h"
#include "period.h"

#define NONE 0xff
#define FX_IMF_FPORTA_UP 0xfe
#define FX_IMF_FPORTA_DN 0xfd

static uint8 arpeggio_val[32];

/* Effect conversion table */
static uint8 fx[] =
{
    NONE,		FX_S3M_TEMPO,
    FX_TEMPO,		FX_TONEPORTA,
    FX_TONE_VSLIDE,	FX_VIBRATO,
    FX_VIBRA_VSLIDE,	NONE /* fine vibrato */,
    FX_TREMOLO,		FX_ARPEGGIO,
    FX_SETPAN,		FX_PANSLIDE,
    FX_VOLSET,		FX_VOLSLIDE,
    FX_F_VSLIDE,	FX_FINETUNE,
    FX_NSLIDE_UP,	FX_NSLIDE_DN,
    FX_PORTA_UP,	FX_PORTA_DN,
    FX_IMF_FPORTA_UP,	FX_IMF_FPORTA_DN,
    NONE /*FX_FLT_CUTOFF*/,	NONE /*FX_FLT_RESN*/,
    FX_OFFSET,		NONE /* fine offset */,
    FX_KEYOFF,		FX_MULTI_RETRIG,
    FX_TREMOR,		FX_JUMP,
    FX_BREAK,		FX_GLOBALVOL,
    FX_G_VOLSLIDE,	FX_EXTENDED,
    FX_CHORUS,		FX_REVERB
};


/* Effect translation */
static void xlat_fx (int c, uint8 *fxt, uint8 *fxp)
{
    uint8 h = MSN (*fxp), l = LSN (*fxp);

    switch (*fxt = fx[*fxt]) {
    case FX_ARPEGGIO:			/* Arpeggio */
	if (*fxp)
	    arpeggio_val[c] = *fxp;
	else
	    *fxp = arpeggio_val[c];
	break;
    case FX_TEMPO:
	if (*fxp < 0x21)
	    *fxp = 0x21;
	break;
    case FX_S3M_TEMPO:
	if (*fxp <= 0x20)
	    *fxt = FX_TEMPO;
	break;
    case FX_IMF_FPORTA_UP:
	*fxt = FX_PORTA_UP;
	if (*fxp < 0x30)
	    *fxp = LSN (*fxp >> 2) | 0xe0;
	else
	    *fxp = LSN (*fxp >> 4) | 0xf0;
	break;
    case FX_IMF_FPORTA_DN:
	*fxt = FX_PORTA_DN;
	if (*fxp < 0x30)
	    *fxp = LSN (*fxp >> 2) | 0xe0;
	else
	    *fxp = LSN (*fxp >> 4) | 0xf0;
	break;
    case FX_EXTENDED:			/* Extended effects */
	switch (h) {
	case 0x1:			/* Set filter */
	case 0x2:			/* Undefined */
	case 0x4:			/* Undefined */
	case 0x6:			/* Undefined */
	case 0x7:			/* Undefined */
	case 0x9:			/* Undefined */
	case 0xe:			/* Ignore envelope */
	case 0xf:			/* Invert loop */
	    *fxp = *fxt = 0;
	    break;
	case 0x3:			/* Glissando */
	    *fxp = l | (EX_GLISS << 4);
	    break;
	case 0x5:			/* Vibrato waveform */
	    *fxp = l | (EX_VIBRATO_WF << 4);
	    break;
	case 0x8:			/* Tremolo waveform */
	    *fxp = l | (EX_TREMOLO_WF << 4);
	    break;
	case 0xa:			/* Pattern loop */
	    *fxp = l | (EX_PATTERN_LOOP << 4);
	    break;
	case 0xb:			/* Pattern delay */
	    *fxp = l | (EX_PATT_DELAY << 4);
	    break;
	case 0xc:
	    if (l == 0)
		*fxt = *fxp = 0;
	}
	break;
    case NONE:				/* No effect */
	*fxt = *fxp = 0;
	break;
    }
}


int imf_load (FILE *f)
{
    int c, r, i, j;
    struct xxm_event *event = 0, dummy;
    struct imf_header ih;
    struct imf_instrument ii;
    struct imf_sample is;
    int pat_len, smp_num;
    uint8 n, b;
    uint16 x16;

    LOAD_INIT ();

    /* Load and convert header */
    fread (&ih, 1, sizeof (ih), f);
    if (ih.magic[0] != 'I' || ih.magic[1] != 'M' || ih.magic[2] != '1' ||
	ih.magic[3] != '0')
	return -1;

    L_ENDIAN16 (ih.len);
    L_ENDIAN16 (ih.pat);
    L_ENDIAN16 (ih.ins);
    L_ENDIAN16 (ih.flg);

    str_adj ((char *) ih.name);
    strcpy (xmp_ctl->name, (char *) ih.name);

    xxh->len = ih.len;
    xxh->ins = ih.ins;
    xxh->smp = 1024;
    xxh->pat = ih.pat;

    if (ih.flg & 0x01)
	xxh->flg |= XXM_FLG_LINEAR;

    xxh->tpo = ih.tpo;
    xxh->bpm = ih.bpm;

    sprintf (xmp_ctl->type, "IM10 (Imago Orpheus)");

    MODULE_INFO ();

    for (xxh->chn = i = 0; i < 32; i++) {
	if (ih.chn[i].status != 0x00)
	    xxh->chn = i + 1;
	else
	    continue;
	xxc[i].pan = ih.chn[i].pan;
	xxc[i].cho = ih.chn[i].chorus;
	xxc[i].rvb = ih.chn[i].reverb;
	xxc[i].flg |= XXM_CHANNEL_FX;
    }
    xxh->trk = xxh->pat * xxh->chn;

    memcpy (xxo, ih.pos, xxh->len);
    for (i = 0; i < xxh->len; i++)
	if (xxo[i] == 0xff)
	    xxo[i]--;

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

    PATTERN_INIT ();

    /* Read patterns */

    if (V (0))
	report ("Stored patterns: %d ", xxh->pat);

    memset (arpeggio_val, 0, 32);

    for (i = 0; i < xxh->pat; i++) {
	PATTERN_ALLOC (i);

	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);

	r = 0;

	while (--pat_len >= 0) {
	    fread (&b, 1, 1, f);

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

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

	    if (b & IMF_NI_FOLLOW) {
		fread (&n, 1, 1, f);
		switch (n) {
		case 255:
		case 160:	/* ??!? */
		    n = 0x61;
		    break;	/* Key off */
		default:
		    n = 1 + 12 * MSN (n) + LSN (n);
		}

		event->note = n;
		fread (&n, 1, 1, f);
		event->ins = n;
		pat_len -= 2;
	    }
	    if (b & IMF_FX_FOLLOWS) {
		fread (&n, 1, 1, f);
		event->fxt = n;
		fread (&n, 1, 1, f);
		event->fxp = n;
		xlat_fx (c, &event->fxt, &event->fxp);
		pat_len -= 2;
	    }
	    if (b & IMF_F2_FOLLOWS) {
		fread (&n, 1, 1, f);
		event->f2t = n;
		fread (&n, 1, 1, f);
		event->f2p = n;
		xlat_fx (c, &event->f2t, &event->f2p);
		pat_len -= 2;
	    }
	}

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

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

    INSTRUMENT_INIT ();

    /* Read and convert instruments and samples */

    if (V (0))
	report ("Instruments    : %d ", xxh->ins);

    if (V (1))
	report (
"\n     Instrument name                NSm Fade Env Smp# Len   Start End   C2Spd");

    for (smp_num = i = 0; i < xxh->ins; i++) {
	fread (&ii, 1, sizeof (ii), f);
	if (strncmp (ii.magic, "II10", 4))
	    return -2;

	L_ENDIAN16 (ii.fadeout);
	L_ENDIAN16 (ii.nsm);

        if (ii.nsm)
 	    xxi[i] = calloc (sizeof (struct xxm_instrument), ii.nsm);

	xxih[i].nsm = ii.nsm;

	str_adj ((char *) ii.name);
	strncpy ((char *) xxih[i].name, ii.name, 24);

	memcpy (xxim[i].ins, ii.map, 96);

	if (V (1) && (strlen ((char *) ii.name) || ii.nsm))
	    report ("\n[%2X] %-31.31s %2d %4x %c%c%c ",
		i, ii.name, ii.nsm, ii.fadeout,
		ii.env[0].flg & 0x01 ? 'V' : '-',
		'-', '-');

	xxih[i].aei.npt = ii.env[0].npt;
	xxih[i].aei.sus = ii.env[0].sus;
	xxih[i].aei.lps = ii.env[0].lps;
	xxih[i].aei.lpe = ii.env[0].lpe;
	xxih[i].aei.flg = ii.env[0].flg & 0x01 ? XXM_ENV_ON : 0;
	xxih[i].aei.flg |= ii.env[0].flg & 0x02 ? XXM_ENV_SUS : 0;
	xxih[i].aei.flg |= ii.env[0].flg & 0x04 ?  XXM_ENV_LOOP : 0;

	if (xxih[i].aei.npt)
	    xxae[i] = calloc (4, xxih[i].aei.npt);

	for (j = 0; j < xxih[i].aei.npt; j++) {
	    x16 = ii.vol_env[j * 2];
	    L_ENDIAN16 (x16);
	    xxae[i][j * 2] = x16;
	    x16 = ii.vol_env[j * 2 + 1];
	    L_ENDIAN16 (x16);
	    xxae[i][j * 2 + 1] = x16;
	}

	for (j = 0; j < ii.nsm; j++, smp_num++) {
	    fread (&is, 1, sizeof (is), f);
	    L_ENDIAN32 (is.len);
	    L_ENDIAN32 (is.lps);
	    L_ENDIAN32 (is.lpe);
	    L_ENDIAN32 (is.rate);
	    xxi[i][j].sid = smp_num;
	    xxi[i][j].vol = is.vol;
	    xxi[i][j].pan = is.pan;
	    xxs[smp_num].len = is.len;
	    xxs[smp_num].lps = is.lps;
	    xxs[smp_num].lpe = is.lpe;
	    xxs[smp_num].flg = is.flg & 1 ? WAVE_LOOPING : 0;
	    xxs[smp_num].flg |= is.flg & 4 ? WAVE_16_BITS : 0;

	    if (V (1)) {
		if (j)
		    report("\n\t\t\t\t\t\t ");
		report ("[%02x] %05x %05x %05x %5d ",
		    j, is.len, is.lps, is.lpe, is.rate);
	    }
	    c2spd_to_note (is.rate, &xxi[i][j].xpo, &xxi[i][j].fin);

	    if (!xxs[smp_num].len)
		continue;

	    xmp_drv_loadpatch (f, xxi[i][j].sid, xmp_ctl->c4rate, 0,
		&xxs[xxi[i][j].sid], NULL);

	    if (V (0))
		report (".");
	}
    }
    xxh->smp = smp_num;
    xxs = realloc (xxs, sizeof (struct xxm_sample) * xxh->smp);

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

    xmp_ctl->fetch |= XMP_MODE_ST3;

    return 0;
}
