/* 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 AC1D Packer modules based on the format description
 * written by Sylvain Chipaux (Asle/ReDoX). Format created by
 * Slammer/Anarchy.
 */

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

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


struct ac1d_instrument {
    uint16 size;
    uint8 finetune;
    uint8 volume;
    uint16 loop_start;
    uint16 loop_size;
} PACKED;

struct ac1d_header {
    uint8 len;
    uint8 ignore;		/* Noistracker byte set to 0x7f */
    uint8 magic[2];		/* 0xAC1D */
    uint32 smp_addr;		/* Address of sample data */
    struct ac1d_instrument ins[31];
    uint32 addr[128];		/* Pattern address table */
    uint8 order[128];		/* Pattern order table */
} PACKED;


int ac1d_load (FILE *f)
{
    int i, j, k;
    struct xxm_event *event;
    struct ac1d_header ah;
    uint8 x8, y8;

    LOAD_INIT ();

    fread (&ah, 1, sizeof (struct ac1d_header), f);

    if (ah.magic[0] != 0xac || ah.magic[1] != 0x1d)
	return -1;

    B_ENDIAN32 (ah.smp_addr);
    for (i = 0; i < 128; i++) {
	B_ENDIAN32 (ah.addr[i]);
	if (!ah.addr[i])
	    break;
    }

    xxh->pat = i;
    xxh->trk = xxh->pat * xxh->chn;
    xxh->len = ah.len;

    for (i = 0; i < xxh->len; i++)
	xxo[i] = ah.order[i];

    sprintf (xmp_ctl->type, "AC1D Packer");

    MODULE_INFO ();

    INSTRUMENT_INIT ();

    for (i = 0; i < xxh->ins; i++) {
	B_ENDIAN16 (ah.ins[i].size);
	B_ENDIAN16 (ah.ins[i].loop_start);
	B_ENDIAN16 (ah.ins[i].loop_size);
	xxi[i] = calloc (sizeof (struct xxm_instrument), 1);
	xxs[i].len = 2 * ah.ins[i].size;
	xxs[i].lps = 2 * ah.ins[i].loop_start;
	xxs[i].lpe = xxs[i].lps + 2 * ah.ins[i].loop_size;
	xxs[i].flg = ah.ins[i].loop_size > 1 ? WAVE_LOOPING : 0;
	xxi[i][0].fin = (int8) ah.ins[i].finetune << 4;
	xxi[i][0].vol = ah.ins[i].volume;
	xxi[i][0].pan = 0x80;
	xxi[i][0].sid = i;
	xxih[i].nsm = !!(xxs[i].len);
	xxih[i].rls = 0xfff;

	if (V (1) && xxs[i].len > 2) {
	    report ("[%2X] %04x %04x %04x %c V%02x %+d\n",
		i, xxs[i].len, xxs[i].lps,
		xxs[i].lpe, ah.ins[i].loop_size > 1 ? 'L' : ' ',
		xxi[i][0].vol, (char) xxi[i][0].fin >> 4);
	}
    }

    PATTERN_INIT ();

    /* Load and convert patterns */
    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);
	if (ftell (f) & 1)		/* Patterns are 16-bit aligned */
	    fread (&x8, 1, 1, f);
	fseek (f, 12, SEEK_CUR);	/* Skip track pointers */
	for (j = 0; j < 4; j++) {
	    for (k = 0; k < 64; k++) {
		event = &EVENT (i, j, k);
		fread (&x8, 1, 1, f);
		if (x8 & 0x80) {
		    k += (x8 & 0x7f) - 1;
		    continue;
		}

		fread (&y8, 1, 1, f);
		event->note = x8 & 0x3f;

		if (event->note == 12) {		/* ??? */
		    event->note = 37;
		} else if (event->note == 0x3f) {
		    event->note = 0;
		} else {
		    event->note += 25;
		}

		event->ins = ((x8 & 0x40) >> 2) | MSN (y8);
		if (LSN (y8) == 0x7)
		    continue;

		fread (&x8, 1, 1, f);
		event->fxt = LSN (y8);
		event->fxp = x8;

		if (!event->fxp) {
		    switch (event->fxt) {
		    case 0x05:
			event->fxt = 0x03;
			break;
		    case 0x06:
			event->fxt = 0x04;
			break;
		    case 0x01:
		    case 0x02:
		    case 0x0a:
			event->fxt = 0x00;
		    case 0x0f:
		    }
		}
	    }
	}

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

    xxh->flg |= XXM_FLG_MODRNG;

    /* Load samples */

    if (V (0))
	report ("\nStored samples : %d ", xxh->smp);
    for (i = 0; i < xxh->smp; i++) {
	if (!xxs[i].len)
	    continue;
	xmp_drv_loadpatch (f, xxi[i][0].sid, xmp_ctl->c4rate, 0,
	    &xxs[xxi[i][0].sid], NULL);
	if (V (0))
	    report (".");
    }
    if (V (0))
	report ("\n");

    return 0;
}
