Logo Search packages:      
Sourcecode: ia32-libs-sdl version File versions  Download package

load_mod.c

/*    MikMod sound library
      (c) 1998, 1999, 2000 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: load_mod.c,v 1.5 2001/09/11 18:49:18 slouken Exp $

  Generic MOD loader (Protracker, StarTracker, FastTracker, etc)

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

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

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <ctype.h>
#include <string.h>

#include "mikmod_internals.h"

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

typedef struct MSAMPINFO {
      CHAR samplename[23];          /* 22 in module, 23 in memory */
      UWORD length;
      UBYTE finetune;
      UBYTE volume;
      UWORD reppos;
      UWORD replen;
} MSAMPINFO;

typedef struct MODULEHEADER {
      CHAR songname[21];                  /* the songname.. 20 in module, 21 in memory */
      MSAMPINFO samples[31];        /* all sampleinfo */
      UBYTE songlength;             /* number of patterns used */
      UBYTE magic1;                       /* should be 127 */
      UBYTE positions[128];         /* which pattern to play at pos */
      UBYTE magic2[4];              /* string "M.K." or "FLT4" or "FLT8" */
} MODULEHEADER;

typedef struct MODTYPE {
      CHAR id[5];
      UBYTE channels;
      CHAR *name;
} MODTYPE;

typedef struct MODNOTE {
      UBYTE a, b, c, d;
} MODNOTE;

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

#define MODULEHEADERSIZE 0x438

static CHAR protracker[] = "Protracker";
static CHAR startrekker[] = "Startrekker";
static CHAR fasttracker[] = "Fasttracker";
static CHAR oktalyser[] = "Oktalyser";
static CHAR oktalyzer[] = "Oktalyzer";
static CHAR taketracker[] = "TakeTracker";
static CHAR orpheus[] = "Imago Orpheus (MOD format)";

static MODULEHEADER *mh = NULL;
static MODNOTE *patbuf = NULL;
static int modtype = 0;

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

/* given the module ID, determine the number of channels and the tracker
   description ; also alters modtype */
static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
{
      /* Protracker and variants */
      if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {
            *descr = protracker;
            modtype = 0;
            *numchn = 4;
            return 1;
      }
      
      /* Star Tracker */
      if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&
            (isdigit(id[3]))) {
            *descr = startrekker;
            modtype = 1;
            *numchn = id[3] - '0';
            if (*numchn == 4)
                  return 1;
#ifdef MIKMOD_DEBUG
            if (*numchn == 8)
                  fprintf(stderr, "\rFLT8 modules not supported yet\n");
            else
                  fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn);
#endif
            return 0;
      }

      /* Oktalyzer (Amiga) */
      if (!memcmp(id, "OKTA", 4)) {
            *descr = oktalyzer;
            modtype = 1;
            *numchn = 8;
            return 1;
      }

      /* Oktalyser (Atari) */
      if (!memcmp(id, "CD81", 4)) {
            *descr = oktalyser;
            modtype = 1;
            *numchn = 8;
            return 1;
      }

      /* Fasttracker */
      if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {
            *descr = fasttracker;
            modtype = 1;
            *numchn = id[0] - '0';
            return 1;
      }
      /* Fasttracker or Taketracker */
      if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))
            && (isdigit(id[0])) && (isdigit(id[1]))) {
            if (id[3] == 'H') {
                  *descr = fasttracker;
                  modtype = 2;            /* this can also be Imago Orpheus */
            } else {
                  *descr = taketracker;
                  modtype = 1;
            }
            *numchn = (id[0] - '0') * 10 + (id[1] - '0');
            return 1;
      }

      return 0;
}

static BOOL MOD_Test(void)
{
      UBYTE id[4], numchn;
      CHAR *descr;

      _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET);
      if (!_mm_read_UBYTES(id, 4, modreader))
            return 0;

      if (MOD_CheckType(id, &numchn, &descr))
            return 1;

      return 0;
}

static BOOL MOD_Init(void)
{
      if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))
            return 0;
      return 1;
}

static void MOD_Cleanup(void)
{
      _mm_free(mh);
      _mm_free(patbuf);
}

/*
Old (amiga) noteinfo:

_____byte 1_____   byte2_    _____byte 3_____   byte4_
/                \ /      \  /                \ /      \
0000          0000-00000000  0000          0000-00000000

Upper four    12 bits for    Lower four    Effect command.
bits of sam-  note period.   bits of sam-
ple number.                  ple number.

*/

static void ConvertNote(MODNOTE *n)
{
      UBYTE instrument, effect, effdat, note;
      UWORD period;
      UBYTE lastnote = 0;

      /* extract the various information from the 4 bytes that make up a note */
      instrument = (n->a & 0x10) | (n->c >> 4);
      period = (((UWORD)n->a & 0xf) << 8) + n->b;
      effect = n->c & 0xf;
      effdat = n->d;

      /* Convert the period to a note number */
      note = 0;
      if (period) {
            for (note = 0; note < 7 * OCTAVE; note++)
                  if (period >= npertab[note])
                        break;
            if (note == 7 * OCTAVE)
                  note = 0;
            else
                  note++;
      }

      if (instrument) {
            /* if instrument does not exist, note cut */
            if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {
                  UniPTEffect(0xc, 0);
                  if (effect == 0xc)
                        effect = effdat = 0;
            } else {
                  /* Protracker handling */
                  if (!modtype) {
                        /* if we had a note, then change instrument... */
                        if (note)
                              UniInstrument(instrument - 1);
                        /* ...otherwise, only adjust volume... */
                        else {
                              /* ...unless an effect was specified, which forces a new
                                 note to be played */
                              if (effect || effdat) {
                                    UniInstrument(instrument - 1);
                                    note = lastnote;
                              } else
                                    UniPTEffect(0xc,
                                                      mh->samples[instrument -
                                                                        1].volume & 0x7f);
                        }
                  } else {
                        /* Fasttracker handling */
                        UniInstrument(instrument - 1);
                        if (!note)
                              note = lastnote;
                  }
            }
      }
      if (note) {
            UniNote(note + 2 * OCTAVE - 1);
            lastnote = note;
      }

      /* Convert pattern jump from Dec to Hex */
      if (effect == 0xd)
            effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);

      /* Volume slide, up has priority */
      if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))
            effdat &= 0xf0;

      /* Handle ``heavy'' volumes correctly */
      if ((effect == 0xc) && (effdat > 0x40))
            effdat = 0x40;
      
      /* Ignore 100, 200 and 300 (there is no porta memory in mod files) */
      if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)))
            effect = 0;

      UniPTEffect(effect, effdat);
}

static UBYTE *ConvertTrack(MODNOTE *n)
{
      int t;

      UniReset();
      for (t = 0; t < 64; t++) {
            ConvertNote(n);
            UniNewline();
            n += of.numchn;
      }
      return UniDup();
}

/* Loads all patterns of a modfile and converts them into the 3 byte format. */
static BOOL ML_LoadPatterns(void)
{
      int t, s, tracks = 0;

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

      /* Allocate temporary buffer for loading and converting the patterns */
      if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE))))
            return 0;

      for (t = 0; t < of.numpat; t++) {
            /* Load the pattern into the temp buffer and convert it */
            for (s = 0; s < (64U * of.numchn); s++) {
                  patbuf[s].a = _mm_read_UBYTE(modreader);
                  patbuf[s].b = _mm_read_UBYTE(modreader);
                  patbuf[s].c = _mm_read_UBYTE(modreader);
                  patbuf[s].d = _mm_read_UBYTE(modreader);
            }
            for (s = 0; s < of.numchn; s++)
                  if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s)))
                        return 0;
      }
      return 1;
}

static BOOL MOD_Load(BOOL curious)
{
      int t, scan;
      SAMPLE *q;
      MSAMPINFO *s;
      CHAR *descr;

      /* try to read module header */
      _mm_read_string((CHAR *)mh->songname, 20, modreader);
      mh->songname[20] = 0;         /* just in case */

      for (t = 0; t < 31; t++) {
            s = &mh->samples[t];
            _mm_read_string(s->samplename, 22, modreader);
            s->samplename[22] = 0;  /* just in case */
            s->length = _mm_read_M_UWORD(modreader);
            s->finetune = _mm_read_UBYTE(modreader);
            s->volume = _mm_read_UBYTE(modreader);
            s->reppos = _mm_read_M_UWORD(modreader);
            s->replen = _mm_read_M_UWORD(modreader);
      }

      mh->songlength = _mm_read_UBYTE(modreader);
      mh->magic1 = _mm_read_UBYTE(modreader);
      _mm_read_UBYTES(mh->positions, 128, modreader);
      _mm_read_UBYTES(mh->magic2, 4, modreader);

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

      /* set module variables */
      of.initspeed = 6;
      of.inittempo = 125;
      if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) {
            _mm_errno = MMERR_NOT_A_MODULE;
            return 0;
      }
      of.songname = DupStr(mh->songname, 21, 1);
      of.numpos = mh->songlength;
      of.reppos = 0;

      /* Count the number of patterns */
      of.numpat = 0;
      for (t = 0; t < of.numpos; t++)
            if (mh->positions[t] > of.numpat)
                  of.numpat = mh->positions[t];
      /* since some old modules embed extra patterns, we have to check the
         whole list to get the samples' file offsets right - however we can find
         garbage here, so check carefully */
      scan = 1;
      for (t = of.numpos; t < 128; t++)
            if (mh->positions[t] >= 0x80)
                  scan = 0;
      if (scan)
            for (t = of.numpos; t < 128; t++) {
                  if (mh->positions[t] > of.numpat)
                        of.numpat = mh->positions[t];
                  if ((curious) && (mh->positions[t]))
                        of.numpos = t + 1;
            }
      of.numpat++;
      of.numtrk = of.numpat * of.numchn;

      if (!AllocPositions(of.numpos))
            return 0;
      for (t = 0; t < of.numpos; t++)
            of.positions[t] = mh->positions[t];

      /* Finally, init the sampleinfo structures  */
      of.numins = of.numsmp = 31;
      if (!AllocSamples())
            return 0;
      s = mh->samples;
      q = of.samples;
      for (t = 0; t < of.numins; t++) {
            /* convert the samplename */
            q->samplename = DupStr(s->samplename, 23, 1);
            /* init the sampleinfo variables and convert the size pointers */
            q->speed = finetune[s->finetune & 0xf];
            q->volume = s->volume & 0x7f;
            q->loopstart = (ULONG)s->reppos << 1;
            q->loopend = q->loopstart + ((ULONG)s->replen << 1);
            q->length = (ULONG)s->length << 1;
            q->flags = SF_SIGNED;
            /* Imago Orpheus creates MODs with 16 bit samples, check */
            if ((modtype == 2) && (s->volume & 0x80)) {
                  q->flags |= SF_16BITS;
                  descr = orpheus;
            }

            if (s->replen > 2)
                  q->flags |= SF_LOOP;
            /* fix replen if repend > length */
            if (q->loopend > q->length)
                  q->loopend = q->length;

            s++;
            q++;
      }

      of.modtype = strdup(descr);

      if (!ML_LoadPatterns())
            return 0;
      return 1;
}

static CHAR *MOD_LoadTitle(void)
{
      CHAR s[21];

      _mm_fseek(modreader, 0, SEEK_SET);
      if (!_mm_read_UBYTES(s, 20, modreader))
            return NULL;
      s[20] = 0;                          /* just in case */

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

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

MIKMODAPI MLOADER load_mod = {
      NULL,
      "Standard module",
      "MOD (31 instruments)",
      MOD_Init,
      MOD_Test,
      MOD_Load,
      MOD_Cleanup,
      MOD_LoadTitle
};

/* ex:set ts=4: */

Generated by  Doxygen 1.6.0   Back to index