513 lines
12 KiB
C
513 lines
12 KiB
C
|
/* MikMod sound library
|
||
|
(c) 1998, 1999, 2000, 2001, 2002 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.3 2005/04/07 19:57:38 realtech 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 <stdio.h>
|
||
|
#ifdef HAVE_MEMORY_H
|
||
|
#include <memory.h>
|
||
|
#endif
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "mikmod_internals.h"
|
||
|
|
||
|
#ifdef SUNOS
|
||
|
extern int fprintf(FILE *, const char *, ...);
|
||
|
#endif
|
||
|
|
||
|
/*========== 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, trekker;
|
||
|
|
||
|
/*========== Loader code */
|
||
|
|
||
|
/* given the module ID, determine the number of channels and the tracker
|
||
|
description ; also alters modtype */
|
||
|
static int MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
|
||
|
{
|
||
|
modtype = trekker = 0;
|
||
|
|
||
|
/* 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 = trekker = 1;
|
||
|
*numchn = id[3] - '0';
|
||
|
if (*numchn == 4 || *numchn == 8)
|
||
|
return 1;
|
||
|
#ifdef MIKMOD_DEBUG
|
||
|
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 int 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 int MOD_Init(void)
|
||
|
{
|
||
|
if (!(mh = (MODULEHEADER *)MikMod_malloc(sizeof(MODULEHEADER))))
|
||
|
return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void MOD_Cleanup(void)
|
||
|
{
|
||
|
MikMod_free(mh);
|
||
|
MikMod_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 UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect)
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
/* An isolated 100, 200 or 300 effect should be ignored (no
|
||
|
"standalone" porta memory in mod files). However, a sequence such
|
||
|
as 1XX, 100, 100, 100 is fine. */
|
||
|
if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&
|
||
|
(lasteffect < 0x10) && (effect != lasteffect))
|
||
|
effect = 0;
|
||
|
|
||
|
UniPTEffect(effect, effdat);
|
||
|
if (effect == 8)
|
||
|
of.flags |= UF_PANNING;
|
||
|
|
||
|
return effect;
|
||
|
}
|
||
|
|
||
|
static UBYTE *ConvertTrack(MODNOTE *n, int numchn)
|
||
|
{
|
||
|
int t;
|
||
|
UBYTE lasteffect = 0x10; /* non existant effect */
|
||
|
|
||
|
UniReset();
|
||
|
for (t = 0; t < 64; t++) {
|
||
|
lasteffect = ConvertNote(n,lasteffect);
|
||
|
UniNewline();
|
||
|
n += numchn;
|
||
|
}
|
||
|
return UniDup();
|
||
|
}
|
||
|
|
||
|
/* Loads all patterns of a modfile and converts them into the 3 byte format. */
|
||
|
static int 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 *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE))))
|
||
|
return 0;
|
||
|
|
||
|
if (trekker && of.numchn == 8) {
|
||
|
/* Startrekker module dual pattern */
|
||
|
for (t = 0; t < of.numpat; t++) {
|
||
|
for (s = 0; s < (64U * 4); 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 < 4; s++)
|
||
|
if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))
|
||
|
return 0;
|
||
|
for (s = 0; s < (64U * 4); 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 < 4; s++)
|
||
|
if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))
|
||
|
return 0;
|
||
|
}
|
||
|
} else {
|
||
|
/* Generic module pattern */
|
||
|
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, of.numchn)))
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int MOD_Load(int 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);
|
||
|
|
||
|
/* this fixes mods which declare more than 128 positions.
|
||
|
* eg: beatwave.mod */
|
||
|
if (mh->songlength > 128) { mh->songlength = 128; }
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
if (trekker && of.numchn == 8)
|
||
|
for (t = 0; t < 128; t++)
|
||
|
/* if module pretends to be FLT8, yet the order table
|
||
|
contains odd numbers, chances are it's a lying FLT4... */
|
||
|
if (mh->positions[t] & 1) {
|
||
|
of.numchn = 4;
|
||
|
break;
|
||
|
}
|
||
|
if (trekker && of.numchn == 8)
|
||
|
for (t = 0; t < 128; t++)
|
||
|
mh->positions[t] >>= 1;
|
||
|
|
||
|
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;
|
||
|
|
||
|
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: */
|