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

/* Zen Packer loader based on the desciption written by Sylvain Chipaux.
 * Tested with ZEN-dif-prty.exe sent by Martin Jeppesen.
 */

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

#include "load.h"


struct zen_ins {
    uint16 finetune;		/* Finetune (*48h) */
    uint16 volume;		/* Volume (0-63) */
    uint16 len;			/* Sample length / 2 */
    uint16 loop_size;		/* Loop size / 2 */
    uint32 addr;		/* Sample address in file */
    uint32 loop_start;		/* Sample loop start in file */
} PACKED;

struct zen_header {
    uint32 pataddr;		/* Address of the pattern table */
    uint8 pat;			/* Number of patterns (-1) */
    uint8 len;			/* Size of the pattern list */
    struct zen_ins ins[31];	/* Instruments */
} PACKED;


int zen_load (FILE *f)
{
    int i, j;
    struct xxm_event *event;
    struct zen_header zh;
    uint8 ev[4];
    uint32 x, *pataddr;
    int smp_size;

    LOAD_INIT ();

    fread (&zh, 1, sizeof (zh), f);

    B_ENDIAN32 (zh.pataddr);
    fseek (f, zh.pataddr + 4 * zh.len, SEEK_SET);
    fread (&x, 1, 4, f);
    B_ENDIAN32 (x);
    if (x != 0xffffffff)
	return -1;

    xxh->ins = 31;
    xxh->smp = xxh->ins;
    xxh->pat = zh.pat + 1;
    xxh->len = zh.len;
    xxh->trk = xxh->pat * xxh->chn;

    for (smp_size = i = 0; i < xxh->ins; i++) {
	B_ENDIAN16 (zh.ins[i].finetune);
	B_ENDIAN16 (zh.ins[i].volume);
	B_ENDIAN16 (zh.ins[i].len);
	B_ENDIAN16 (zh.ins[i].loop_size);
	B_ENDIAN32 (zh.ins[i].addr);
	B_ENDIAN32 (zh.ins[i].loop_start);
	if (zh.ins[i].len > 8)
	    smp_size += 2 * zh.ins[i].len;
    }

    if (abs (zh.pataddr + 4 * zh.len + 4 + smp_size - xmp_ctl->size) > 16)
	return -1;

    pataddr = calloc (4, xxh->len);
    fseek (f, zh.pataddr, SEEK_SET);
    for (i = 0; i < xxh->len; i++) {
	fread (&pataddr[i], 1, 4, f);
	B_ENDIAN32 (pataddr[i]);
    }

    strcpy (xmp_ctl->type, "Zen Packer");
    MODULE_INFO ();

    INSTRUMENT_INIT ();

    if (V (1))
	report ("     Len  LBeg LEnd L Vl Ft\n");

    for (i = 0; i < xxh->ins; i++) {
	xxi[i] = calloc (sizeof (struct xxm_instrument), 1);
	xxih[i].nsm = !!(xxs[i].len = 2 * zh.ins[i].len);
	xxs[i].lps = zh.ins[i].loop_start - zh.ins[i].addr;
	xxs[i].lpe = xxs[i].lps + 2 * zh.ins[i].loop_size;
	xxs[i].flg = zh.ins[i].loop_size > 1 ? WAVE_LOOPING : 0;
	xxi[i][0].vol = zh.ins[i].volume;
	xxi[i][0].fin = ((int16)zh.ins[i].finetune / 0x48) << 4;
	xxi[i][0].pan = 0x80;
	xxi[i][0].sid = i;
	if (V (1) && xxs[i].len > 2)
	    report ("[%2X] %04x %04x %04x %c %02x %+01x\n",
		i, xxs[i].len, xxs[i].lps, xxs[i].lpe,
		xxs[i].flg & WAVE_LOOPING ? 'L' : ' ', xxi[i][0].vol,
		xxi[i][0].fin >> 4);
    }

    fseek (f, 502, SEEK_SET);

    PATTERN_INIT ();

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

    for (i = 0; i < xxh->pat; i++) {
	PATTERN_ALLOC (i);
	xxp[i]->rows = 64;
	TRACK_ALLOC (i);

	x = ftell (f);
	for (j = 0; j < xxh->len; j++)
	    if (pataddr[j] == x)
		xxo[j] = i;

	do {
	    fread (ev, 1, 4, f);
	    event = &EVENT(i, ev[0] % xxh->chn, ev[0] / xxh->chn);
	    if ((event->note = (ev[1] & 0x7e) >> 1))
		event->note += 36;
	    event->ins = ((ev[1] & 0x01) << 4) | (ev[2] >> 4);
	    event->fxt = LSN (ev[2]);
	    event->fxp = ev[3];
	} while (ev[0] != 0xff || ev[1] || ev[2] || ev[3]);

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

    free (pataddr);
    xxh->flg |= XXM_FLG_MODRNG;

    /* Read samples */

    fseek (f, zh.pataddr + 4 * zh.len + 4, SEEK_SET);

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

    for (i = 0; i < xxh->ins; i++) {
	if (xxs[i].len <= 4)
	    continue;
	fseek (f, zh.ins[i].addr, SEEK_SET);
	xmp_drv_loadpatch (f, i, xmp_ctl->c4rate, 0, &xxs[i], NULL);
	if (V (0))
	    report (".");
    }
    if (V (0))
	report ("\n");

    return 0;
}
