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

/* Liquid Tracker module loader based on the format description written
 * by Nir Oren. Tested with Shell.liq sent by Adi Sapir.
 */

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

#include "period.h"
#include "load.h"
#include "liq.h"

#define NONE 0xff

static uint8 arpeggio_val[64];

static uint8 fx[] = {
    NONE,
    FX_ARPEGGIO,	FX_TEMPO,
    FX_BREAK,		FX_PORTA_DN,
    NONE,		NONE /* fine vibrato */,
    FX_GLOBALVOL,	NONE,
    NONE,		FX_JUMP,
    NONE,		FX_VOLSLIDE,
    FX_EXTENDED,	FX_TONEPORTA,
    FX_OFFSET,		FX_SETPAN,
    NONE,		FX_MULTI_RETRIG,
    FX_S3M_TEMPO,	FX_TREMOLO,
    FX_PORTA_UP,	FX_VIBRATO,
    NONE,		FX_TONE_VSLIDE,
    NONE,		FX_VIBRA_VSLIDE
};


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

#if 0
    e->fxt = e->fxp = 0;
    return;
#endif

    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 < 0x20)
	    e->fxp = 0x20;
	break;
    case FX_S3M_TEMPO:
	if (e->fxp < 0x20)
	    e->fxt = FX_TEMPO;
	break;
    case FX_EXTENDED:			/* Extended effects */
	switch (h) {
	case 0x3:			/* Glissando */
	    e->fxp = l | (EX_GLISS << 4);
	    break;
	case 0x4:			/* Vibrato wave */
	    e->fxp = l | (EX_VIBRATO_WF << 4);
	    break;
	case 0x5:			/* Finetune */
	    e->fxp = l | (EX_FINETUNE << 4);
	    break;
	case 0x6:			/* Pattern loop */
	    e->fxp = l | (EX_PATTERN_LOOP << 4);
	    break;
	case 0x7:			/* Tremolo wave */
	    e->fxp = l | (EX_TREMOLO_WF << 4);
	    break;
	case 0xc:			/* Tremolo wave */
	    e->fxp = l | (EX_CUT << 4);
	    break;
	case 0xd:			/* Tremolo wave */
	    e->fxp = l | (EX_DELAY << 4);
	    break;
	case 0xe:			/* Tremolo wave */
	    e->fxp = l | (EX_PATT_DELAY << 4);
	    break;
	default:			/* Ignore */
	    e->fxt = e->fxp = 0;
	    break;
	}
	break;
    case NONE:				/* No effect */
	e->fxt = e->fxp = 0;
	break;
    }
}


static void decode_event (uint8 x1, struct xxm_event *event, FILE *f)
{
    uint8 x2;

    if (x1 & 0x01) {
        fread (&x2, 1, 1, f);
	if (x2 == 0xfe)
	    event->note = XMP_KEY_OFF;
	else
	    event->note = x2 + 1 + 24;
    }
    if (x1 & 0x02) {
        fread (&x2, 1, 1, f);
	event->ins = x2 + 1;
    }
    if (x1 & 0x04) {
        fread (&x2, 1, 1, f);
	event->vol = x2 + 1;
    }
    if (x1 & 0x08) {
        fread (&x2, 1, 1, f);
	event->fxt = x2 + 1 - 'A';
    }
    if (x1 & 0x10) {
        fread (&x2, 1, 1, f);
	event->fxp = x2;
    }
}

int liq_load (FILE * f)
{
    int i, j, k;
    struct xxm_event *event, *event2, dummy;
    struct liq_header lh;
    struct liq_instrument li;
    struct liq_pattern lp;
    uint8 x1, x2, pmag[4];

    LOAD_INIT ();

    fread (&lh, 1, sizeof (lh), f);
    if (strncmp ((char *) lh.magic, "Liquid Module:", 14))
	return -1;

    L_ENDIAN16 (lh.version);
    L_ENDIAN16 (lh.speed);
    L_ENDIAN16 (lh.bpm);
    L_ENDIAN16 (lh.low);
    L_ENDIAN16 (lh.high);
    L_ENDIAN16 (lh.chn);
    L_ENDIAN32 (lh.flags);
    L_ENDIAN16 (lh.pat);
    L_ENDIAN16 (lh.ins);
    L_ENDIAN16 (lh.len);
    L_ENDIAN16 (lh.hdrsz);

    xxh->tpo = lh.speed;
    xxh->bpm = lh.bpm;
    xxh->chn = lh.chn;
    xxh->pat = lh.pat;
    xxh->ins = xxh->smp = lh.ins;
    xxh->len = lh.len;
    xxh->trk = xxh->chn * xxh->pat;
    xxh->flg = XXM_FLG_INSVOL;

    strncpy (xmp_ctl->name, lh.name, 30);
    strncpy (tracker_name, lh.tracker, 20);
    strncpy (author_name, lh.author, 20);
    sprintf (xmp_ctl->type, "Liquid module %d.%02d",
	lh.version >> 8, lh.version & 0x00ff);

    MODULE_INFO ();

    for (i = 0; i < xxh->chn; i++) {
	fread (&x1, 1, 1, f);
	xxc[i].pan = x1 << 2;
    }

    for (i = 0; i < xxh->chn; i++) {
	fread (&x1, 1, 1, f);
	xxc[i].vol = x1;
    }

    fread (xxo, 1, xxh->len, f);

    /* Skip 1.01 echo pools */
    fseek (f, lh.hdrsz - (0x6d + xxh->chn * 2 + xxh->len), SEEK_CUR);

    PATTERN_INIT ();

    /* Read and convert patterns */

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

printf ("CHN + %d\n", xxh->chn);
    for (i = 0; i < xxh->pat; i++) {
printf ("\n*** PATTERN %d ***\n", i);
	PATTERN_ALLOC (i);
	fread (pmag, 1, 4, f);
	if (pmag[0] == '!')
	    continue;
	fread (&lp, sizeof (struct liq_pattern), 1, f);
	L_ENDIAN16 (lp.rows);
	L_ENDIAN32 (lp.size);
	xxp[i]->rows = lp.rows;
	TRACK_ALLOC (i);

	for (j = 0; j < 64/*xxh->chn*/; j++) {
	    for (k = 0; k < xxp[i]->rows; k++) {
		event = j < xxh->chn ? &EVENT (i, j, k) : &dummy;
		fread (&x1, 1, 1, f);
printf ("Fetch: %02x\n", x1);
		switch (x1) {
		case 0xc0:
		    goto next_pattern;
		case 0xa0:
		    goto next_track;
		case 0xe0:
		    fread (&x1, 1, 1, f);
		    k += x1;
		case 0x80:
		    goto next_row;
		case 0xe1:
		    fread (&x1, 1, 1, f);
		    j += x1;
		    goto next_track;
		}
		if (x1 > 0x80 && x1 < 0xa0) {
		    fread (&x2, 1, 1, f);
printf ("Will copy %d times...\n", x2);
		    decode_event (x1, event, f);
		    xlat_fx (j, event); 
		    for (; x2; x2--, k++) {
printf ("Copy to (%d %d %d)\n", i, j, k);
			event2 = j < xxh->chn ? &EVENT (i, j, k) : &dummy;
			memcpy (event2, event, sizeof (struct xxm_event));
		    }
		    goto next_row;
		}
		if (x1 > 0xa0 && x1 < 0xe0) {
		    if (x1 < 0xc0) {
		        fread (&x2, 1, 1, f);
printf ("Will decode %d times...\n", x2);
			for (; x2; x2--) {
printf ("Decode to (%d %d %d)\n", i, j, k);
			    decode_event (x1, event, f);
			    xlat_fx (j, event); 
			    fread (&x1, 1, 1, f);
			    k++;
			    event = j < xxh->chn ? &EVENT (i, j, k) : &dummy;
			}
		    }
		    decode_event (x1, event, f);
		    xlat_fx (j, event); 
		    goto next_row;
		}
printf ("Unpacked event\n");
		if (++x1)
		    event->note = x1 + 24;
		else if (x1 == 0xff)
		    event->note = XMP_KEY_OFF;
		fread (&x1, 1, 1, f);
		if (++x1)
		    event->ins = x1;
		fread (&x1, 1, 1, f);
		if (++x1)
		    event->vol = x1;
		fread (&x1, 1, 1, f);
		if (++x1)
		    event->fxt = x1 - 'A' - 1;
		fread (&x1, 1, 1, f);
		event->fxp = x1;
		xlat_fx (j, event); 
next_row:
	    }
next_track:
	}
next_pattern:
	if (V (0))
	    report (".");
    }

    /* Read and convert instruments */

    INSTRUMENT_INIT ();

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

    for (i = 0; i < xxh->ins; i++) {
	xxi[i] = calloc (sizeof (struct xxm_instrument), 1);
	fread (&li, 1, sizeof (struct liq_instrument), f);
	L_ENDIAN32 (li.length);
	L_ENDIAN32 (li.loopstart);
	L_ENDIAN32 (li.loopend);
	L_ENDIAN32 (li.c2spd);
	L_ENDIAN16 (li.hdrsz);
	L_ENDIAN16 (li.comp);
	xxih[i].nsm = !!(li.length);
	xxih[i].vol = 0x40;
	xxs[i].len = li.length;
	xxs[i].lps = li.loopstart;
	xxs[i].lpe = li.loopend;

	if (li.flags & 0x01) {
	    xxs[i].flg = WAVE_16_BITS;
	    xxs[i].len <<= 1;
	}

	if (li.loopend > 0)
	    xxs[i].flg = WAVE_LOOPING;

	xxi[i][0].vol = li.vol;
	xxi[i][0].gvl = li.gvl;
	xxi[i][0].pan = li.pan;
	xxi[i][0].sid = i;
	strncpy ((char *) xxih[i].name, li.name, 24);
	str_adj ((char *) li.name);
	if ((V (1)) && (strlen ((char *) li.name) || xxs[i].len)) {
	    report ("\n[%2X] %-30.30s %05x%c%05x %05x %c %02x %02x %5d ",
		i, li.name, xxs[i].len,
		xxs[i].flg & WAVE_16_BITS ? '+' : ' ',
		xxs[i].lps, xxs[i].lpe,
		xxs[i].flg & WAVE_LOOPING ? 'L' : ' ',
		xxi[i][0].vol, xxi[i][0].gvl, li.c2spd);
	}

	c2spd_to_note (li.c2spd, &xxi[i][0].xpo, &xxi[i][0].fin);
	fseek (f, li.hdrsz - 0x90, SEEK_CUR);

	if (!xxs[i].len)
	    continue;
	xmp_drv_loadpatch (f, xxi[i][0].sid, xmp_ctl->c4rate, 0, &xxs[i], NULL);
	if (V (0))
	    report (".");
    }
    if (V (0))
	report ("\n");

    return 0;
}

