/* Extended Module Player
 * Copyright (C) 1996-2018 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "loader.h"

struct mtm_file_header {
	uint8 magic[3];		/* "MTM" */
	uint8 version;		/* MSN=major, LSN=minor */
	uint8 name[20];		/* ASCIIZ Module name */
	uint16 tracks;		/* Number of tracks saved */
	uint8 patterns;		/* Number of patterns saved */
	uint8 modlen;		/* Module length */
	uint16 extralen;	/* Length of the comment field */
	uint8 samples;		/* Number of samples */
	uint8 attr;		/* Always zero */
	uint8 rows;		/* Number rows per track */
	uint8 channels;		/* Number of tracks per pattern */
	uint8 pan[32];		/* Pan positions for each channel */
};

struct mtm_instrument_header {
	uint8 name[22];		/* Instrument name */
	uint32 length;		/* Instrument length in bytes */
	uint32 loop_start;	/* Sample loop start */
	uint32 loopend;		/* Sample loop end */
	uint8 finetune;		/* Finetune */
	uint8 volume;		/* Playback volume */
	uint8 attr;		/* &0x01: 16bit sample */
};

static int mtm_test(HIO_HANDLE *, char *, const int);
static int mtm_load(struct module_data *, HIO_HANDLE *, const int);

extern const struct format_loader libxmp_loader_mtm;
const struct format_loader libxmp_loader_mtm = {
	"Multitracker",
	mtm_test,
	mtm_load
};

static int mtm_test(HIO_HANDLE *f, char *t, const int start)
{
	uint8 buf[4];

	if (hio_read(buf, 1, 4, f) < 4)
		return -1;
	if (memcmp(buf, "MTM", 3))
		return -1;
	if (buf[3] != 0x10)
		return -1;

	libxmp_read_title(f, t, 20);

	return 0;
}

static int mtm_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
	struct xmp_module *mod = &m->mod;
	int i, j;
	struct mtm_file_header mfh;
	struct mtm_instrument_header mih;
	uint8 mt[192];

	LOAD_INIT();

	hio_read(&mfh.magic, 3, 1, f);	/* "MTM" */
	mfh.version = hio_read8(f);	/* MSN=major, LSN=minor */
	hio_read(&mfh.name, 20, 1, f);	/* ASCIIZ Module name */
	mfh.tracks = hio_read16l(f);	/* Number of tracks saved */
	mfh.patterns = hio_read8(f);	/* Number of patterns saved */
	mfh.modlen = hio_read8(f);	/* Module length */
	mfh.extralen = hio_read16l(f);	/* Length of the comment field */

	mfh.samples = hio_read8(f);	/* Number of samples */
	if (mfh.samples > 63) {
		return -1;
	}

	mfh.attr = hio_read8(f);	/* Always zero */

	mfh.rows = hio_read8(f);	/* Number rows per track */
	if (mfh.rows != 64)
		return -1;

	mfh.channels = hio_read8(f);	/* Number of tracks per pattern */
	if (mfh.channels > XMP_MAX_CHANNELS) {
		return -1;
	}

	hio_read(&mfh.pan, 32, 1, f);	/* Pan positions for each channel */

	if (hio_error(f)) {
		return -1;
	}

#if 0
	if (strncmp((char *)mfh.magic, "MTM", 3))
		return -1;
#endif

	mod->trk = mfh.tracks + 1;
	mod->pat = mfh.patterns + 1;
	mod->len = mfh.modlen + 1;
	mod->ins = mfh.samples;
	mod->smp = mod->ins;
	mod->chn = mfh.channels;
	mod->spd = 6;
	mod->bpm = 125;

	strncpy(mod->name, (char *)mfh.name, 20);
	libxmp_set_type(m, "MultiTracker %d.%02d MTM", MSN(mfh.version),
		 LSN(mfh.version));

	MODULE_INFO();

	if (libxmp_init_instrument(m) < 0)
		return -1;

	/* Read and convert instruments */
	for (i = 0; i < mod->ins; i++) {
		struct xmp_instrument *xxi = &mod->xxi[i];
		struct xmp_sample *xxs = &mod->xxs[i];
		struct xmp_subinstrument *sub;

		if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
			return -1;

		sub = &xxi->sub[0];

		hio_read(&mih.name, 22, 1, f);	/* Instrument name */
		mih.length = hio_read32l(f);	/* Instrument length in bytes */

		if (mih.length > MAX_SAMPLE_SIZE)
			return -1;

		mih.loop_start = hio_read32l(f); /* Sample loop start */
		mih.loopend = hio_read32l(f);	/* Sample loop end */
		mih.finetune = hio_read8(f);	/* Finetune */
		mih.volume = hio_read8(f);	/* Playback volume */
		mih.attr = hio_read8(f);	/* &0x01: 16bit sample */

		xxs->len = mih.length;
		xxs->lps = mih.loop_start;
		xxs->lpe = mih.loopend;
		xxs->flg = xxs->lpe ? XMP_SAMPLE_LOOP : 0;	/* 1 == Forward loop */
		if (mfh.attr & 1) {
			xxs->flg |= XMP_SAMPLE_16BIT;
			xxs->len >>= 1;
			xxs->lps >>= 1;
			xxs->lpe >>= 1;
		}

		sub->vol = mih.volume;
		sub->fin = mih.finetune;
		sub->pan = 0x80;
		sub->sid = i;

		libxmp_instrument_name(mod, i, mih.name, 22);

		if (xxs->len > 0)
			mod->xxi[i].nsm = 1;

		D_(D_INFO "[%2X] %-22.22s %04x%c%04x %04x %c V%02x F%+03d\n", i,
		   xxi->name, xxs->len, xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ',
		   xxs->lps, xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
		   sub->vol, sub->fin - 0x80);
	}

	hio_read(mod->xxo, 1, 128, f);

	if (libxmp_init_pattern(mod) < 0)
		return -1;

	D_(D_INFO "Stored tracks: %d", mod->trk - 1);

	for (i = 0; i < mod->trk; i++) {

		if (libxmp_alloc_track(mod, i, mfh.rows) < 0)
			return -1;

		if (i == 0)
			continue;

		if (hio_read(&mt, 3, 64, f) != 64)
			return -1;

		for (j = 0; j < 64; j++) {
			struct xmp_event *e = &mod->xxt[i]->event[j];
			uint8 *d = mt + j * 3;

			if ((e->note = d[0] >> 2)) {
				e->note += 37;
			}
			e->ins = ((d[0] & 0x3) << 4) + MSN(d[1]);
			e->fxt = LSN(d[1]);
			e->fxp = d[2];
			if (e->fxt > FX_SPEED) {
				e->fxt = e->fxp = 0;
			}

			/* Set pan effect translation */
			if (e->fxt == FX_EXTENDED && MSN(e->fxp) == 0x8) {
				e->fxt = FX_SETPAN;
				e->fxp <<= 4;
			}
		}
	}

	/* Read patterns */
	D_(D_INFO "Stored patterns: %d", mod->pat - 1);

	for (i = 0; i < mod->pat; i++) {
		if (libxmp_alloc_pattern(mod, i) < 0)
			return -1;

		mod->xxp[i]->rows = 64;
		for (j = 0; j < 32; j++) {
			int track = hio_read16l(f);

			if (track >= mod->trk) {
				track = 0;
			}

			if (j < mod->chn) {
				mod->xxp[i]->index[j] = track;
			}
		}
	}

	/* Comments */
	hio_seek(f, mfh.extralen, SEEK_CUR);

	/* Read samples */
	D_(D_INFO "Stored samples: %d", mod->smp);

	for (i = 0; i < mod->ins; i++) {
		if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0)
			return -1;
	}

	for (i = 0; i < mod->chn; i++)
		mod->xxc[i].pan = mfh.pan[i] << 4;

	return 0;
}
