2010-12-12 15:03:30 +00:00
|
|
|
/* 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.
|
2020-08-09 01:56:15 +00:00
|
|
|
|
2010-12-12 15:03:30 +00:00
|
|
|
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.
|
2020-08-09 01:56:15 +00:00
|
|
|
|
2010-12-12 15:03:30 +00:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*==============================================================================
|
|
|
|
|
2020-08-09 01:56:15 +00:00
|
|
|
$Id$
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
Amiga MED module loader
|
|
|
|
|
|
|
|
==============================================================================*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#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 information */
|
|
|
|
|
|
|
|
typedef struct MEDHEADER {
|
|
|
|
ULONG id;
|
|
|
|
ULONG modlen;
|
|
|
|
ULONG MEDSONGP; /* struct MEDSONG *song; */
|
|
|
|
UWORD psecnum; /* for the player routine, MMD2 only */
|
|
|
|
UWORD pseq; /* " " " " */
|
|
|
|
ULONG MEDBlockPP; /* struct MEDBlock **blockarr; */
|
|
|
|
ULONG reserved1;
|
|
|
|
ULONG MEDINSTHEADERPP; /* struct MEDINSTHEADER **smplarr; */
|
|
|
|
ULONG reserved2;
|
|
|
|
ULONG MEDEXPP; /* struct MEDEXP *expdata; */
|
|
|
|
ULONG reserved3;
|
|
|
|
UWORD pstate; /* some data for the player routine */
|
|
|
|
UWORD pblock;
|
|
|
|
UWORD pline;
|
|
|
|
UWORD pseqnum;
|
|
|
|
SWORD actplayline;
|
|
|
|
UBYTE counter;
|
|
|
|
UBYTE extra_songs; /* number of songs - 1 */
|
|
|
|
} MEDHEADER;
|
|
|
|
|
|
|
|
typedef struct MEDSAMPLE {
|
|
|
|
UWORD rep, replen; /* offs: 0(s), 2(s) */
|
|
|
|
UBYTE midich; /* offs: 4(s) */
|
|
|
|
UBYTE midipreset; /* offs: 5(s) */
|
|
|
|
UBYTE svol; /* offs: 6(s) */
|
|
|
|
SBYTE strans; /* offs: 7(s) */
|
|
|
|
} MEDSAMPLE;
|
|
|
|
|
|
|
|
typedef struct MEDSONG {
|
|
|
|
MEDSAMPLE sample[63]; /* 63 * 8 bytes = 504 bytes */
|
|
|
|
UWORD numblocks; /* offs: 504 */
|
|
|
|
UWORD songlen; /* offs: 506 */
|
|
|
|
UBYTE playseq[256]; /* offs: 508 */
|
|
|
|
UWORD deftempo; /* offs: 764 */
|
|
|
|
SBYTE playtransp; /* offs: 766 */
|
|
|
|
UBYTE flags; /* offs: 767 */
|
|
|
|
UBYTE flags2; /* offs: 768 */
|
|
|
|
UBYTE tempo2; /* offs: 769 */
|
|
|
|
UBYTE trkvol[16]; /* offs: 770 */
|
|
|
|
UBYTE mastervol; /* offs: 786 */
|
|
|
|
UBYTE numsamples; /* offs: 787 */
|
|
|
|
} MEDSONG;
|
|
|
|
|
|
|
|
typedef struct MEDEXP {
|
|
|
|
ULONG nextmod; /* pointer to next module */
|
|
|
|
ULONG exp_smp; /* pointer to MEDINSTEXT array */
|
|
|
|
UWORD s_ext_entries;
|
|
|
|
UWORD s_ext_entrsz;
|
|
|
|
ULONG annotxt; /* pointer to annotation text */
|
|
|
|
ULONG annolen;
|
|
|
|
ULONG iinfo; /* pointer to MEDINSTINFO array */
|
|
|
|
UWORD i_ext_entries;
|
|
|
|
UWORD i_ext_entrsz;
|
|
|
|
ULONG jumpmask;
|
|
|
|
ULONG rgbtable;
|
|
|
|
ULONG channelsplit;
|
|
|
|
ULONG n_info;
|
|
|
|
ULONG songname; /* pointer to songname */
|
|
|
|
ULONG songnamelen;
|
|
|
|
ULONG dumps;
|
|
|
|
ULONG reserved2[7];
|
|
|
|
} MEDEXP;
|
|
|
|
|
|
|
|
typedef struct MMD0NOTE {
|
|
|
|
UBYTE a, b, c;
|
|
|
|
} MMD0NOTE;
|
|
|
|
|
|
|
|
typedef struct MMD1NOTE {
|
|
|
|
UBYTE a, b, c, d;
|
|
|
|
} MMD1NOTE;
|
|
|
|
|
|
|
|
typedef struct MEDINSTHEADER {
|
|
|
|
ULONG length;
|
|
|
|
SWORD type;
|
|
|
|
/* Followed by actual data */
|
|
|
|
} MEDINSTHEADER;
|
|
|
|
|
|
|
|
typedef struct MEDINSTEXT {
|
|
|
|
UBYTE hold;
|
|
|
|
UBYTE decay;
|
|
|
|
UBYTE suppress_midi_off;
|
|
|
|
SBYTE finetune;
|
|
|
|
} MEDINSTEXT;
|
|
|
|
|
|
|
|
typedef struct MEDINSTINFO {
|
|
|
|
UBYTE name[40];
|
|
|
|
} MEDINSTINFO;
|
|
|
|
|
|
|
|
/*========== Loader variables */
|
|
|
|
|
|
|
|
#define MMD0_string 0x4D4D4430
|
|
|
|
#define MMD1_string 0x4D4D4431
|
|
|
|
|
|
|
|
static MEDHEADER *mh = NULL;
|
|
|
|
static MEDSONG *ms = NULL;
|
|
|
|
static MEDEXP *me = NULL;
|
|
|
|
static ULONG *ba = NULL;
|
|
|
|
static MMD0NOTE *mmd0pat = NULL;
|
|
|
|
static MMD1NOTE *mmd1pat = NULL;
|
|
|
|
|
|
|
|
static int decimalvolumes;
|
|
|
|
static int bpmtempos;
|
|
|
|
|
|
|
|
#define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)]
|
|
|
|
#define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)]
|
|
|
|
|
|
|
|
static CHAR MED_Version[] = "OctaMED (MMDx)";
|
|
|
|
|
|
|
|
/*========== Loader code */
|
|
|
|
|
2012-03-04 19:13:43 +00:00
|
|
|
static int MED_Test(void)
|
2010-12-12 15:03:30 +00:00
|
|
|
{
|
|
|
|
UBYTE id[4];
|
|
|
|
|
|
|
|
if (!_mm_read_UBYTES(id, 4, modreader))
|
|
|
|
return 0;
|
|
|
|
if ((!memcmp(id, "MMD0", 4)) || (!memcmp(id, "MMD1", 4)))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-04 19:13:43 +00:00
|
|
|
static int MED_Init(void)
|
2010-12-12 15:03:30 +00:00
|
|
|
{
|
|
|
|
if (!(me = (MEDEXP *)MikMod_malloc(sizeof(MEDEXP))))
|
|
|
|
return 0;
|
|
|
|
if (!(mh = (MEDHEADER *)MikMod_malloc(sizeof(MEDHEADER))))
|
|
|
|
return 0;
|
|
|
|
if (!(ms = (MEDSONG *)MikMod_malloc(sizeof(MEDSONG))))
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-04 19:13:43 +00:00
|
|
|
static void MED_Cleanup(void)
|
2010-12-12 15:03:30 +00:00
|
|
|
{
|
|
|
|
MikMod_free(me);
|
|
|
|
MikMod_free(mh);
|
|
|
|
MikMod_free(ms);
|
|
|
|
MikMod_free(ba);
|
|
|
|
MikMod_free(mmd0pat);
|
|
|
|
MikMod_free(mmd1pat);
|
2020-08-09 01:56:15 +00:00
|
|
|
me = NULL;
|
|
|
|
mh = NULL;
|
|
|
|
ms = NULL;
|
|
|
|
ba = NULL;
|
|
|
|
mmd0pat = NULL;
|
|
|
|
mmd1pat = NULL;
|
2010-12-12 15:03:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void EffectCvt(UBYTE eff, UBYTE dat)
|
|
|
|
{
|
|
|
|
switch (eff) {
|
|
|
|
/* 0x0 0x1 0x2 0x3 0x4 PT effects */
|
|
|
|
case 0x5: /* PT vibrato with speed/depth nibbles swapped */
|
|
|
|
UniPTEffect(0x4, (dat >> 4) | ((dat & 0xf) << 4));
|
|
|
|
break;
|
|
|
|
/* 0x6 0x7 not used */
|
|
|
|
case 0x6:
|
|
|
|
case 0x7:
|
|
|
|
break;
|
|
|
|
case 0x8: /* midi hold/decay */
|
|
|
|
break;
|
|
|
|
case 0x9:
|
|
|
|
if (bpmtempos) {
|
|
|
|
if (!dat)
|
|
|
|
dat = of.initspeed;
|
|
|
|
UniEffect(UNI_S3MEFFECTA, dat);
|
|
|
|
} else {
|
|
|
|
if (dat <= 0x20) {
|
|
|
|
if (!dat)
|
|
|
|
dat = of.initspeed;
|
|
|
|
else
|
|
|
|
dat /= 4;
|
|
|
|
UniPTEffect(0xf, dat);
|
|
|
|
} else
|
|
|
|
UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / (33 * 4));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/* 0xa 0xb PT effects */
|
|
|
|
case 0xc:
|
|
|
|
if (decimalvolumes)
|
|
|
|
dat = (dat >> 4) * 10 + (dat & 0xf);
|
|
|
|
UniPTEffect(0xc, dat);
|
|
|
|
break;
|
|
|
|
case 0xd: /* same as PT volslide */
|
|
|
|
UniPTEffect(0xa, dat);
|
|
|
|
break;
|
|
|
|
case 0xe: /* synth jmp - midi */
|
|
|
|
break;
|
|
|
|
case 0xf:
|
|
|
|
switch (dat) {
|
|
|
|
case 0: /* patternbreak */
|
|
|
|
UniPTEffect(0xd, 0);
|
|
|
|
break;
|
|
|
|
case 0xf1: /* play note twice */
|
|
|
|
UniWriteByte(UNI_MEDEFFECTF1);
|
|
|
|
break;
|
|
|
|
case 0xf2: /* delay note */
|
|
|
|
UniWriteByte(UNI_MEDEFFECTF2);
|
|
|
|
break;
|
|
|
|
case 0xf3: /* play note three times */
|
|
|
|
UniWriteByte(UNI_MEDEFFECTF3);
|
|
|
|
break;
|
|
|
|
case 0xfe: /* stop playing */
|
|
|
|
UniPTEffect(0xb, of.numpat);
|
|
|
|
break;
|
|
|
|
case 0xff: /* note cut */
|
|
|
|
UniPTEffect(0xc, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (dat <= 10)
|
|
|
|
UniPTEffect(0xf, dat);
|
|
|
|
else if (dat <= 240) {
|
|
|
|
if (bpmtempos)
|
|
|
|
UniPTEffect(0xf, (dat < 32) ? 32 : dat);
|
|
|
|
else
|
|
|
|
UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / 33);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: /* all normal PT effects are handled here */
|
|
|
|
UniPTEffect(eff, dat);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static UBYTE *MED_Convert1(int count, int col)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
UBYTE inst, note, eff, dat;
|
|
|
|
MMD1NOTE *n;
|
|
|
|
|
|
|
|
UniReset();
|
|
|
|
for (t = 0; t < count; t++) {
|
|
|
|
n = &d1note(t, col);
|
|
|
|
|
|
|
|
note = n->a & 0x7f;
|
|
|
|
inst = n->b & 0x3f;
|
|
|
|
eff = n->c & 0xf;
|
|
|
|
dat = n->d;
|
|
|
|
|
|
|
|
if (inst)
|
|
|
|
UniInstrument(inst - 1);
|
|
|
|
if (note)
|
|
|
|
UniNote(note + 3 * OCTAVE - 1);
|
|
|
|
EffectCvt(eff, dat);
|
|
|
|
UniNewline();
|
|
|
|
}
|
|
|
|
return UniDup();
|
|
|
|
}
|
|
|
|
|
|
|
|
static UBYTE *MED_Convert0(int count, int col)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
UBYTE a, b, inst, note, eff, dat;
|
|
|
|
MMD0NOTE *n;
|
|
|
|
|
|
|
|
UniReset();
|
|
|
|
for (t = 0; t < count; t++) {
|
|
|
|
n = &d0note(t, col);
|
|
|
|
a = n->a;
|
|
|
|
b = n->b;
|
|
|
|
|
|
|
|
note = a & 0x3f;
|
|
|
|
a >>= 6;
|
|
|
|
a = ((a & 1) << 1) | (a >> 1);
|
|
|
|
inst = (b >> 4) | (a << 4);
|
|
|
|
eff = b & 0xf;
|
|
|
|
dat = n->c;
|
|
|
|
|
|
|
|
if (inst)
|
|
|
|
UniInstrument(inst - 1);
|
|
|
|
if (note)
|
|
|
|
UniNote(note + 3 * OCTAVE - 1);
|
|
|
|
EffectCvt(eff, dat);
|
|
|
|
UniNewline();
|
|
|
|
}
|
|
|
|
return UniDup();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LoadMEDPatterns(void)
|
|
|
|
{
|
|
|
|
int t, row, col;
|
|
|
|
UWORD numtracks, numlines, maxlines = 0, track = 0;
|
|
|
|
MMD0NOTE *mmdp;
|
|
|
|
|
|
|
|
/* first, scan patterns to see how many channels are used */
|
|
|
|
for (t = 0; t < of.numpat; t++) {
|
|
|
|
_mm_fseek(modreader, ba[t], SEEK_SET);
|
|
|
|
numtracks = _mm_read_UBYTE(modreader);
|
|
|
|
numlines = _mm_read_UBYTE(modreader);
|
|
|
|
|
|
|
|
if (numtracks > of.numchn)
|
|
|
|
of.numchn = numtracks;
|
|
|
|
if (numlines > maxlines)
|
|
|
|
maxlines = numlines;
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (numtracks > 64)
|
|
|
|
return 0;
|
2010-12-12 15:03:30 +00:00
|
|
|
}
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (! of.numchn) /* docs say 4, 8, 12 or 16 */
|
|
|
|
return 0;
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
of.numtrk = of.numpat * of.numchn;
|
|
|
|
if (!AllocTracks())
|
|
|
|
return 0;
|
|
|
|
if (!AllocPatterns())
|
|
|
|
return 0;
|
|
|
|
|
2020-08-09 01:56:15 +00:00
|
|
|
if (!(mmd0pat = (MMD0NOTE *)MikMod_calloc(of.numchn * (maxlines + 1), sizeof(MMD0NOTE))))
|
|
|
|
return 0;
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
/* second read: read and convert patterns */
|
|
|
|
for (t = 0; t < of.numpat; t++) {
|
|
|
|
_mm_fseek(modreader, ba[t], SEEK_SET);
|
|
|
|
numtracks = _mm_read_UBYTE(modreader);
|
|
|
|
numlines = _mm_read_UBYTE(modreader);
|
|
|
|
|
|
|
|
of.pattrows[t] = ++numlines;
|
|
|
|
memset(mmdp = mmd0pat, 0, of.numchn * maxlines * sizeof(MMD0NOTE));
|
|
|
|
for (row = numlines; row; row--) {
|
|
|
|
for (col = numtracks; col; col--, mmdp++) {
|
|
|
|
mmdp->a = _mm_read_UBYTE(modreader);
|
|
|
|
mmdp->b = _mm_read_UBYTE(modreader);
|
|
|
|
mmdp->c = _mm_read_UBYTE(modreader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (col = 0; col < of.numchn; col++)
|
|
|
|
of.tracks[track++] = MED_Convert0(numlines, col);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LoadMMD1Patterns(void)
|
|
|
|
{
|
|
|
|
int t, row, col;
|
|
|
|
UWORD numtracks, numlines, maxlines = 0, track = 0;
|
|
|
|
MMD1NOTE *mmdp;
|
|
|
|
|
|
|
|
/* first, scan patterns to see how many channels are used */
|
|
|
|
for (t = 0; t < of.numpat; t++) {
|
|
|
|
_mm_fseek(modreader, ba[t], SEEK_SET);
|
|
|
|
numtracks = _mm_read_M_UWORD(modreader);
|
|
|
|
numlines = _mm_read_M_UWORD(modreader);
|
|
|
|
if (numtracks > of.numchn)
|
|
|
|
of.numchn = numtracks;
|
|
|
|
if (numlines > maxlines)
|
|
|
|
maxlines = numlines;
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (numtracks > 64)
|
|
|
|
return 0;
|
|
|
|
if (numlines >= 3200) /* per docs */
|
|
|
|
return 0;
|
2010-12-12 15:03:30 +00:00
|
|
|
}
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (! of.numchn) /* docs say 4, 8, 12 or 16 */
|
|
|
|
return 0;
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
of.numtrk = of.numpat * of.numchn;
|
|
|
|
if (!AllocTracks())
|
|
|
|
return 0;
|
|
|
|
if (!AllocPatterns())
|
|
|
|
return 0;
|
|
|
|
|
2020-08-09 01:56:15 +00:00
|
|
|
if (!(mmd1pat = (MMD1NOTE *)MikMod_calloc(of.numchn * (maxlines + 1), sizeof(MMD1NOTE))))
|
|
|
|
return 0;
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
/* second read: really read and convert patterns */
|
|
|
|
for (t = 0; t < of.numpat; t++) {
|
|
|
|
_mm_fseek(modreader, ba[t], SEEK_SET);
|
|
|
|
numtracks = _mm_read_M_UWORD(modreader);
|
|
|
|
numlines = _mm_read_M_UWORD(modreader);
|
|
|
|
|
|
|
|
_mm_fseek(modreader, sizeof(ULONG), SEEK_CUR);
|
|
|
|
of.pattrows[t] = ++numlines;
|
|
|
|
memset(mmdp = mmd1pat, 0, of.numchn * maxlines * sizeof(MMD1NOTE));
|
|
|
|
|
|
|
|
for (row = numlines; row; row--) {
|
|
|
|
for (col = numtracks; col; col--, mmdp++) {
|
|
|
|
mmdp->a = _mm_read_UBYTE(modreader);
|
|
|
|
mmdp->b = _mm_read_UBYTE(modreader);
|
|
|
|
mmdp->c = _mm_read_UBYTE(modreader);
|
|
|
|
mmdp->d = _mm_read_UBYTE(modreader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (col = 0; col < of.numchn; col++)
|
|
|
|
of.tracks[track++] = MED_Convert1(numlines, col);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-04 19:13:43 +00:00
|
|
|
static int MED_Load(int curious)
|
2010-12-12 15:03:30 +00:00
|
|
|
{
|
|
|
|
int t;
|
|
|
|
ULONG sa[64];
|
|
|
|
MEDINSTHEADER s;
|
|
|
|
SAMPLE *q;
|
|
|
|
MEDSAMPLE *mss;
|
|
|
|
|
|
|
|
/* try to read module header */
|
|
|
|
mh->id = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->modlen = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->MEDSONGP = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->psecnum = _mm_read_M_UWORD(modreader);
|
|
|
|
mh->pseq = _mm_read_M_UWORD(modreader);
|
|
|
|
mh->MEDBlockPP = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->reserved1 = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->MEDINSTHEADERPP = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->reserved2 = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->MEDEXPP = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->reserved3 = _mm_read_M_ULONG(modreader);
|
|
|
|
mh->pstate = _mm_read_M_UWORD(modreader);
|
|
|
|
mh->pblock = _mm_read_M_UWORD(modreader);
|
|
|
|
mh->pline = _mm_read_M_UWORD(modreader);
|
|
|
|
mh->pseqnum = _mm_read_M_UWORD(modreader);
|
|
|
|
mh->actplayline = _mm_read_M_SWORD(modreader);
|
|
|
|
mh->counter = _mm_read_UBYTE(modreader);
|
|
|
|
mh->extra_songs = _mm_read_UBYTE(modreader);
|
|
|
|
|
|
|
|
/* Seek to MEDSONG struct */
|
|
|
|
_mm_fseek(modreader, mh->MEDSONGP, SEEK_SET);
|
|
|
|
|
|
|
|
/* Load the MED Song Header */
|
|
|
|
mss = ms->sample; /* load the sample data first */
|
|
|
|
for (t = 63; t; t--, mss++) {
|
|
|
|
mss->rep = _mm_read_M_UWORD(modreader);
|
|
|
|
mss->replen = _mm_read_M_UWORD(modreader);
|
|
|
|
mss->midich = _mm_read_UBYTE(modreader);
|
|
|
|
mss->midipreset = _mm_read_UBYTE(modreader);
|
|
|
|
mss->svol = _mm_read_UBYTE(modreader);
|
|
|
|
mss->strans = _mm_read_SBYTE(modreader);
|
|
|
|
}
|
|
|
|
|
|
|
|
ms->numblocks = _mm_read_M_UWORD(modreader);
|
|
|
|
ms->songlen = _mm_read_M_UWORD(modreader);
|
|
|
|
_mm_read_UBYTES(ms->playseq, 256, modreader);
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (ms->numblocks > 255 || ms->songlen > 256) {
|
|
|
|
_mm_errno = MMERR_NOT_A_MODULE;
|
|
|
|
return 0;
|
|
|
|
}
|
2010-12-12 15:03:30 +00:00
|
|
|
ms->deftempo = _mm_read_M_UWORD(modreader);
|
|
|
|
ms->playtransp = _mm_read_SBYTE(modreader);
|
|
|
|
ms->flags = _mm_read_UBYTE(modreader);
|
|
|
|
ms->flags2 = _mm_read_UBYTE(modreader);
|
|
|
|
ms->tempo2 = _mm_read_UBYTE(modreader);
|
|
|
|
_mm_read_UBYTES(ms->trkvol, 16, modreader);
|
|
|
|
ms->mastervol = _mm_read_UBYTE(modreader);
|
|
|
|
ms->numsamples = _mm_read_UBYTE(modreader);
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (ms->numsamples > 64) {
|
|
|
|
_mm_errno = MMERR_NOT_A_MODULE;
|
|
|
|
return 0;
|
|
|
|
}
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
/* check for a bad header */
|
|
|
|
if (_mm_eof(modreader)) {
|
|
|
|
_mm_errno = MMERR_LOADING_HEADER;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* load extension structure */
|
|
|
|
if (mh->MEDEXPP) {
|
|
|
|
_mm_fseek(modreader, mh->MEDEXPP, SEEK_SET);
|
|
|
|
me->nextmod = _mm_read_M_ULONG(modreader);
|
|
|
|
me->exp_smp = _mm_read_M_ULONG(modreader);
|
|
|
|
me->s_ext_entries = _mm_read_M_UWORD(modreader);
|
|
|
|
me->s_ext_entrsz = _mm_read_M_UWORD(modreader);
|
|
|
|
me->annotxt = _mm_read_M_ULONG(modreader);
|
|
|
|
me->annolen = _mm_read_M_ULONG(modreader);
|
|
|
|
me->iinfo = _mm_read_M_ULONG(modreader);
|
|
|
|
me->i_ext_entries = _mm_read_M_UWORD(modreader);
|
|
|
|
me->i_ext_entrsz = _mm_read_M_UWORD(modreader);
|
|
|
|
me->jumpmask = _mm_read_M_ULONG(modreader);
|
|
|
|
me->rgbtable = _mm_read_M_ULONG(modreader);
|
|
|
|
me->channelsplit = _mm_read_M_ULONG(modreader);
|
|
|
|
me->n_info = _mm_read_M_ULONG(modreader);
|
|
|
|
me->songname = _mm_read_M_ULONG(modreader);
|
|
|
|
me->songnamelen = _mm_read_M_ULONG(modreader);
|
|
|
|
me->dumps = _mm_read_M_ULONG(modreader);
|
2020-08-09 01:56:15 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (me->annolen > 0xffff) {
|
|
|
|
_mm_errno = MMERR_NOT_A_MODULE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* truncate insane songnamelen (fail instead??) */
|
|
|
|
if (me->songnamelen > 256)
|
|
|
|
me->songnamelen = 256;
|
2010-12-12 15:03:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* seek to and read the samplepointer array */
|
|
|
|
_mm_fseek(modreader, mh->MEDINSTHEADERPP, SEEK_SET);
|
|
|
|
if (!_mm_read_M_ULONGS(sa, ms->numsamples, modreader)) {
|
|
|
|
_mm_errno = MMERR_LOADING_HEADER;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* alloc and read the blockpointer array */
|
|
|
|
if (!(ba = (ULONG *)MikMod_calloc(ms->numblocks, sizeof(ULONG))))
|
|
|
|
return 0;
|
|
|
|
_mm_fseek(modreader, mh->MEDBlockPP, SEEK_SET);
|
|
|
|
if (!_mm_read_M_ULONGS(ba, ms->numblocks, modreader)) {
|
|
|
|
_mm_errno = MMERR_LOADING_HEADER;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy song positions */
|
|
|
|
if (!AllocPositions(ms->songlen))
|
|
|
|
return 0;
|
2020-08-09 01:56:15 +00:00
|
|
|
for (t = 0; t < ms->songlen; t++) {
|
2010-12-12 15:03:30 +00:00
|
|
|
of.positions[t] = ms->playseq[t];
|
2020-08-09 01:56:15 +00:00
|
|
|
if (of.positions[t]>ms->numblocks) { /* SANITIY CHECK */
|
|
|
|
/* fprintf(stderr,"positions[%d]=%d > numpat=%d\n",t,of.positions[t],ms->numblocks);*/
|
|
|
|
_mm_errno = MMERR_LOADING_HEADER;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2010-12-12 15:03:30 +00:00
|
|
|
|
|
|
|
decimalvolumes = (ms->flags & 0x10) ? 0 : 1;
|
|
|
|
bpmtempos = (ms->flags2 & 0x20) ? 1 : 0;
|
|
|
|
|
|
|
|
if (bpmtempos) {
|
|
|
|
int bpmlen = (ms->flags2 & 0x1f) + 1;
|
|
|
|
of.initspeed = ms->tempo2;
|
|
|
|
of.inittempo = ms->deftempo * bpmlen / 4;
|
|
|
|
|
|
|
|
if (bpmlen != 4) {
|
|
|
|
/* Let's do some math : compute GCD of BPM beat length and speed */
|
|
|
|
int a, b;
|
|
|
|
|
|
|
|
a = bpmlen;
|
|
|
|
b = ms->tempo2;
|
|
|
|
|
|
|
|
if (a > b) {
|
|
|
|
t = b;
|
|
|
|
b = a;
|
|
|
|
a = t;
|
|
|
|
}
|
|
|
|
while ((a != b) && (a)) {
|
|
|
|
t = a;
|
|
|
|
a = b - a;
|
|
|
|
b = t;
|
|
|
|
if (a > b) {
|
|
|
|
t = b;
|
|
|
|
b = a;
|
|
|
|
a = t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
of.initspeed /= b;
|
|
|
|
of.inittempo = ms->deftempo * bpmlen / (4 * b);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
of.initspeed = ms->tempo2;
|
|
|
|
of.inittempo = ms->deftempo ? ((UWORD)ms->deftempo * 125) / 33 : 128;
|
|
|
|
if ((ms->deftempo <= 10) && (ms->deftempo))
|
|
|
|
of.inittempo = (of.inittempo * 33) / 6;
|
|
|
|
of.flags |= UF_HIGHBPM;
|
|
|
|
}
|
|
|
|
MED_Version[12] = mh->id;
|
2020-08-09 01:56:15 +00:00
|
|
|
of.modtype = MikMod_strdup(MED_Version);
|
2010-12-12 15:03:30 +00:00
|
|
|
of.numchn = 0; /* will be counted later */
|
|
|
|
of.numpat = ms->numblocks;
|
|
|
|
of.numpos = ms->songlen;
|
|
|
|
of.numins = ms->numsamples;
|
|
|
|
of.numsmp = of.numins;
|
|
|
|
of.reppos = 0;
|
|
|
|
if ((mh->MEDEXPP) && (me->songname) && (me->songnamelen)) {
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
_mm_fseek(modreader, me->songname, SEEK_SET);
|
2020-08-09 01:56:15 +00:00
|
|
|
name = (char *) MikMod_malloc(me->songnamelen);
|
2010-12-12 15:03:30 +00:00
|
|
|
_mm_read_UBYTES(name, me->songnamelen, modreader);
|
|
|
|
of.songname = DupStr(name, me->songnamelen, 1);
|
|
|
|
MikMod_free(name);
|
|
|
|
} else
|
|
|
|
of.songname = DupStr(NULL, 0, 0);
|
|
|
|
if ((mh->MEDEXPP) && (me->annotxt) && (me->annolen)) {
|
|
|
|
_mm_fseek(modreader, me->annotxt, SEEK_SET);
|
|
|
|
ReadComment(me->annolen);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AllocSamples())
|
|
|
|
return 0;
|
|
|
|
q = of.samples;
|
|
|
|
for (t = 0; t < of.numins; t++) {
|
|
|
|
q->flags = SF_SIGNED;
|
|
|
|
q->volume = 64;
|
|
|
|
if (sa[t]) {
|
|
|
|
_mm_fseek(modreader, sa[t], SEEK_SET);
|
|
|
|
s.length = _mm_read_M_ULONG(modreader);
|
|
|
|
s.type = _mm_read_M_SWORD(modreader);
|
|
|
|
|
|
|
|
if (s.type) {
|
|
|
|
#ifdef MIKMOD_DEBUG
|
|
|
|
fprintf(stderr, "\rNon-sample instruments not supported in MED loader yet\n");
|
|
|
|
#endif
|
|
|
|
if (!curious) {
|
|
|
|
_mm_errno = MMERR_MED_SYNTHSAMPLES;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
s.length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_mm_eof(modreader)) {
|
|
|
|
_mm_errno = MMERR_LOADING_SAMPLEINFO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
q->length = s.length;
|
|
|
|
q->seekpos = _mm_ftell(modreader);
|
|
|
|
q->loopstart = ms->sample[t].rep << 1;
|
|
|
|
q->loopend = q->loopstart + (ms->sample[t].replen << 1);
|
|
|
|
|
|
|
|
if (ms->sample[t].replen > 1)
|
|
|
|
q->flags |= SF_LOOP;
|
|
|
|
|
|
|
|
/* don't load sample if length>='MMD0'...
|
|
|
|
such kluges make libmikmod's code unique !!! */
|
|
|
|
if (q->length >= MMD0_string)
|
|
|
|
q->length = 0;
|
|
|
|
} else
|
|
|
|
q->length = 0;
|
|
|
|
|
|
|
|
if ((mh->MEDEXPP) && (me->exp_smp) &&
|
|
|
|
(t < me->s_ext_entries) && (me->s_ext_entrsz >= 4)) {
|
|
|
|
MEDINSTEXT ie;
|
|
|
|
|
|
|
|
_mm_fseek(modreader, me->exp_smp + t * me->s_ext_entrsz,
|
|
|
|
SEEK_SET);
|
|
|
|
ie.hold = _mm_read_UBYTE(modreader);
|
|
|
|
ie.decay = _mm_read_UBYTE(modreader);
|
|
|
|
ie.suppress_midi_off = _mm_read_UBYTE(modreader);
|
|
|
|
ie.finetune = _mm_read_SBYTE(modreader);
|
|
|
|
|
|
|
|
q->speed = finetune[ie.finetune & 0xf];
|
|
|
|
} else
|
|
|
|
q->speed = 8363;
|
|
|
|
|
|
|
|
if ((mh->MEDEXPP) && (me->iinfo) &&
|
|
|
|
(t < me->i_ext_entries) && (me->i_ext_entrsz >= 40)) {
|
|
|
|
MEDINSTINFO ii;
|
|
|
|
|
|
|
|
_mm_fseek(modreader, me->iinfo + t * me->i_ext_entrsz, SEEK_SET);
|
|
|
|
_mm_read_UBYTES(ii.name, 40, modreader);
|
|
|
|
q->samplename = DupStr((char*)ii.name, 40, 1);
|
|
|
|
} else
|
|
|
|
q->samplename = NULL;
|
|
|
|
|
|
|
|
q++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mh->id == MMD0_string) {
|
|
|
|
if (!LoadMEDPatterns()) {
|
|
|
|
_mm_errno = MMERR_LOADING_PATTERN;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (mh->id == MMD1_string) {
|
|
|
|
if (!LoadMMD1Patterns()) {
|
|
|
|
_mm_errno = MMERR_LOADING_PATTERN;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_mm_errno = MMERR_NOT_A_MODULE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-04 19:13:43 +00:00
|
|
|
static CHAR *MED_LoadTitle(void)
|
2010-12-12 15:03:30 +00:00
|
|
|
{
|
|
|
|
ULONG posit, namelen;
|
|
|
|
CHAR *name, *retvalue = NULL;
|
2020-08-09 01:56:15 +00:00
|
|
|
|
2010-12-12 15:03:30 +00:00
|
|
|
_mm_fseek(modreader, 0x20, SEEK_SET);
|
|
|
|
posit = _mm_read_M_ULONG(modreader);
|
2020-08-09 01:56:15 +00:00
|
|
|
|
2010-12-12 15:03:30 +00:00
|
|
|
if (posit) {
|
|
|
|
_mm_fseek(modreader, posit + 0x2C, SEEK_SET);
|
|
|
|
posit = _mm_read_M_ULONG(modreader);
|
|
|
|
namelen = _mm_read_M_ULONG(modreader);
|
|
|
|
|
|
|
|
_mm_fseek(modreader, posit, SEEK_SET);
|
2020-08-09 01:56:15 +00:00
|
|
|
name = (CHAR*) MikMod_malloc(namelen);
|
2010-12-12 15:03:30 +00:00
|
|
|
_mm_read_UBYTES(name, namelen, modreader);
|
|
|
|
retvalue = DupStr(name, namelen, 1);
|
|
|
|
MikMod_free(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retvalue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*========== Loader information */
|
|
|
|
|
|
|
|
MIKMODAPI MLOADER load_med = {
|
|
|
|
NULL,
|
|
|
|
"MED",
|
|
|
|
"MED (OctaMED)",
|
|
|
|
MED_Init,
|
|
|
|
MED_Test,
|
|
|
|
MED_Load,
|
|
|
|
MED_Cleanup,
|
|
|
|
MED_LoadTitle
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ex:set ts=4: */
|