/*      MikMod sound library
   (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
   complete list.

   This library is free software; you can redistribute it and/or modify
   it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */

/*==============================================================================

  $Id$

  MTM module loader

==============================================================================*/

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

#include <string.h>

#include "unimod_priv.h"

/*========== Module structure */

typedef struct MTMHEADER
  {
    UBYTE id[3];		/* MTM file marker */
    UBYTE version;		/* upper major, lower nibble minor version number */
    CHAR songname[20];		/* ASCIIZ songname */
    UWORD numtracks;		/* number of tracks saved */
    UBYTE lastpattern;		/* last pattern number saved */
    UBYTE lastorder;		/* last order number to play (songlength-1) */
    UWORD commentsize;		/* length of comment field */
    UBYTE numsamples;		/* number of samples saved  */
    UBYTE attribute;		/* attribute byte (unused) */
    UBYTE beatspertrack;
    UBYTE numchannels;		/* number of channels used  */
    UBYTE panpos[32];		/* voice pan positions */
  }
MTMHEADER;

typedef struct MTMSAMPLE
  {
    CHAR samplename[22];
    ULONG length;
    ULONG reppos;
    ULONG repend;
    UBYTE finetune;
    UBYTE volume;
    UBYTE attribute;
  }
MTMSAMPLE;

typedef struct MTMNOTE
  {
    UBYTE a, b, c;
  }
MTMNOTE;

/*========== Loader variables */

static MTMHEADER *mh = NULL;
static MTMNOTE *mtmtrk = NULL;
static UWORD pat[32];

static CHAR MTM_Version[] = "MTM";

/*========== Loader code */

static BOOL
MTM_Test (void)
{
  UBYTE id[3];

  if (!_mm_read_UBYTES (id, 3, modreader))
    return 0;
  if (!memcmp (id, "MTM", 3))
    return 1;
  return 0;
}

static BOOL
MTM_Init (void)
{
  if (!(mtmtrk = (MTMNOTE *) _mm_calloc (64, sizeof (MTMNOTE))))
    return 0;
  if (!(mh = (MTMHEADER *) _mm_malloc (sizeof (MTMHEADER))))
    return 0;

  return 1;
}

static void
MTM_Cleanup (void)
{
  _mm_free (mtmtrk);
  _mm_free (mh);
}

static UBYTE *
MTM_Convert (void)
{
  int t;
  UBYTE a, b, inst, note, eff, dat;

  UniReset ();
  for (t = 0; t < 64; t++)
    {
      a = mtmtrk[t].a;
      b = mtmtrk[t].b;
      inst = ((a & 0x3) << 4) | (b >> 4);
      note = a >> 2;
      eff = b & 0xf;
      dat = mtmtrk[t].c;

      if (inst)
	UniInstrument (inst - 1);
      if (note)
	UniNote (note + 2 * OCTAVE);

      /* MTM bug workaround : when the effect is volslide, slide-up *always*
         overrides slide-down. */
      if (eff == 0xa && (dat & 0xf0))
	dat &= 0xf0;

      /* Convert pattern jump from Dec to Hex */
      if (eff == 0xd)
	dat = (((dat & 0xf0) >> 4) * 10) + (dat & 0xf);
      UniPTEffect (eff, dat);
      UniNewline ();
    }
  return UniDup ();
}

static BOOL
MTM_Load (BOOL curious)
{
  int t, u;
  MTMSAMPLE s;
  SAMPLE *q;

  /* try to read module header  */
  _mm_read_UBYTES (mh->id, 3, modreader);
  mh->version = _mm_read_UBYTE (modreader);
  _mm_read_string (mh->songname, 20, modreader);
  mh->numtracks = _mm_read_I_UWORD (modreader);
  mh->lastpattern = _mm_read_UBYTE (modreader);
  mh->lastorder = _mm_read_UBYTE (modreader);
  mh->commentsize = _mm_read_I_UWORD (modreader);
  mh->numsamples = _mm_read_UBYTE (modreader);
  mh->attribute = _mm_read_UBYTE (modreader);
  mh->beatspertrack = _mm_read_UBYTE (modreader);
  mh->numchannels = _mm_read_UBYTE (modreader);
  _mm_read_UBYTES (mh->panpos, 32, modreader);

  if (_mm_eof (modreader))
    {
      _mm_errno = MMERR_LOADING_HEADER;
      return 0;
    }

  /* set module variables */
  of.initspeed = 6;
  of.inittempo = 125;
  of.modtype = strdup (MTM_Version);
  of.numchn = mh->numchannels;
  of.numtrk = mh->numtracks + 1;	/* get number of channels */
  of.songname = DupStr (mh->songname, 20, 1);	/* make a cstr of songname */
  of.numpos = mh->lastorder + 1;	/* copy the songlength */
  of.numpat = mh->lastpattern + 1;
  of.reppos = 0;
  for (t = 0; t < 32; t++)
    of.panning[t] = mh->panpos[t] << 4;
  of.numins = of.numsmp = mh->numsamples;

  if (!AllocSamples ())
    return 0;
  q = of.samples;
  for (t = 0; t < of.numins; t++)
    {
      /* try to read sample info */
      _mm_read_string (s.samplename, 22, modreader);
      s.length = _mm_read_I_ULONG (modreader);
      s.reppos = _mm_read_I_ULONG (modreader);
      s.repend = _mm_read_I_ULONG (modreader);
      s.finetune = _mm_read_UBYTE (modreader);
      s.volume = _mm_read_UBYTE (modreader);
      s.attribute = _mm_read_UBYTE (modreader);

      if (_mm_eof (modreader))
	{
	  _mm_errno = MMERR_LOADING_SAMPLEINFO;
	  return 0;
	}

      q->samplename = DupStr (s.samplename, 22, 1);
      q->seekpos = 0;
      q->speed = finetune[s.finetune];
      q->length = s.length;
      q->loopstart = s.reppos;
      q->loopend = s.repend;
      q->volume = s.volume;
      if ((s.repend - s.reppos) > 2)
	q->flags |= SF_LOOP;

      if (s.attribute & 1)
	{
	  /* If the sample is 16-bits, convert the length and replen
	     byte-values into sample-values */
	  q->flags |= SF_16BITS;
	  q->length >>= 1;
	  q->loopstart >>= 1;
	  q->loopend >>= 1;
	}

      q++;
    }

  if (!AllocPositions (of.numpos))
    return 0;
  for (t = 0; t < of.numpos; t++)
    of.positions[t] = _mm_read_UBYTE (modreader);
  for (; t < 128; t++)
    _mm_read_UBYTE (modreader);
  if (_mm_eof (modreader))
    {
      _mm_errno = MMERR_LOADING_HEADER;
      return 0;
    }

  if (!AllocTracks ())
    return 0;
  if (!AllocPatterns ())
    return 0;

  of.tracks[0] = MTM_Convert ();	/* track 0 is empty */
  for (t = 1; t < of.numtrk; t++)
    {
      int s;

      for (s = 0; s < 64; s++)
	{
	  mtmtrk[s].a = _mm_read_UBYTE (modreader);
	  mtmtrk[s].b = _mm_read_UBYTE (modreader);
	  mtmtrk[s].c = _mm_read_UBYTE (modreader);
	}

      if (_mm_eof (modreader))
	{
	  _mm_errno = MMERR_LOADING_TRACK;
	  return 0;
	}

      if (!(of.tracks[t] = MTM_Convert ()))
	return 0;
    }

  for (t = 0; t < of.numpat; t++)
    {
      _mm_read_I_UWORDS (pat, 32, modreader);
      for (u = 0; u < of.numchn; u++)
	of.patterns[((long) t * of.numchn) + u] = pat[u];
    }

  /* read comment field */
  if (mh->commentsize)
    if (!ReadLinedComment (mh->commentsize / 40, 40))
      return 0;

  return 1;
}

static CHAR *
MTM_LoadTitle (void)
{
  CHAR s[20];

  _mm_fseek (modreader, 4, SEEK_SET);
  if (!_mm_read_UBYTES (s, 20, modreader))
    return NULL;

  return (DupStr (s, 20, 1));
}

/*========== Loader information */

MLOADER load_mtm =
{
  NULL,
  "MTM",
  "MTM (MultiTracker Module editor)",
  MTM_Init,
  MTM_Test,
  MTM_Load,
  MTM_Cleanup,
  MTM_LoadTitle
};

/* ex:set ts=4: */
