changeset 4:5d614bcc4287

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:08 +0000
parents 71e20a32bd84
children 42e11dc15457
files docs/bugs.txt docs/future.txt docs/mikcvt.txt docs/mikmod.doc docs/tips.txt playercode/drv_nos.c playercode/drv_wav.c playercode/load_669.c playercode/load_dsm.c playercode/load_far.c playercode/load_it.c playercode/load_m15.c playercode/load_med.c playercode/load_mod.c playercode/load_mtm.c playercode/load_s3m.c playercode/load_stm.c playercode/load_ult.c playercode/load_uni.c playercode/load_wav.c playercode/load_xm.c playercode/mloader.c playercode/stream.c playercode/virtch2.c
diffstat 24 files changed, 7301 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/bugs.txt	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,25 @@
+
+ --> MikMod Sound Libraries Version 3.0
+  -> Known Bugs - August 20st, 1997
+
+
+ If anyone knows of a fix for any of the bugs listed below, please contact
+ me at dracoirs@epix.net and tell me about it.
+
+ - The DSM loader has been changed from MikMod 2.10 to 3.0 and is un-
+   tested.  It may not work.
+ 
+ - 669 loader is far from perfect.  Thanks go out to the complete lack of
+   useful docs for this format.  It seems to play most 669's adequately,
+   however.
+
+ - Since there is no 100% sure detection of UST / Soundtracker the 15-
+   instrument loader may, under very rare circumstances, detect a sound-
+   tracker module as UST and play it incorrectly.
+
+ - MikMod locks Aztec soundcards (SB clones distributed with Packard Bell
+   computers).  MikMod will work, but it hard locks the card and the
+   user must turn off the computer to reset it (Windows can't even do it!)
+
+   This bug may not exist anymore; someone tell me if it does.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/future.txt	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,26 @@
+
+ --> MikMod Sound Libraries Version 3.0
+  -> Changes Planned for the Future - August 20th, 1997
+
+
+ - Optional Interpolated mixing in VIRTCH.C [real-time].
+
+ - Floating point alternative to 64-bit ints in VIRTCH2.C.
+
+ - Support for stereo samples and sound effects.
+
+ - Audio streaming from disk for large raw audio files (WAV, etc).
+
+ - Native Gravis Ultrasound Plug and Play soundcard support.
+
+ - Better AWE32 support (AWE32 does not support software sound-fx
+   mixing).
+
+ - Native AWE64 support [does anyone have GOOD specs on this or the
+   AWE32?]
+
+ - Windows 95 and DirectSound [ugh] Drivers.
+
+ - Things people suggest - send suggestions to dracoirs@epix.net and
+   don't forget to visit the webpage at http://www.epix.net/~dracoirs/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/mikcvt.txt	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,76 @@
+
+ --> MikMod Sound System  v3.0
+  -> UniFormat Conversion Program (MIKCVT.EXE)
+
+
+
+-/- Introduction
+================
+
+ MikMod utilizes its own internal format called UniMod (or UniFormat).
+ This internal format can 100% accurately represent any of the modules
+ that MikMod supports.  The advantage of UniMod is that the programmer
+ can convert all modules of any type to this native MikMod format, and
+ simply include the UniMod loader alone into the program.
+
+ For more information about UniMod, see UNIMOD30.TXT.
+
+ MikMod comes with a program called MIKCVT.EXE, which converts modules
+ to the UniMod format.  MIKCVT will load any module supported by
+ MikMod, optimize and compress the pattern data, and save out a file
+ with the .UNI extension.  MIKCVT also supports wildcards, so that you
+ can easily convert several modules at a time.
+
+
+-/- Distribution
+================
+
+ MIKCVT is a totally free utility, and can be downloaded separately
+ from MikMod.  MIKCVT comes with full source code, and the programmer
+ can feel free to modify this source code in any way for PERSONAL use
+ only (to rearrange the format to prevent module ripping for instance).
+ PLEASE DO NOT REDISTRIBUTE A MODIFIED VERSION OF MIKCVT.  The last
+ thing anyone wants is to have odd non-std versions of UniMod modules
+ floating around.
+
+
+
+-/- Usage
+=========
+
+ MIKCVT has a very simple command line interface, as follows:
+
+   MIKCVT  [-strip] infile [infile] [infile] ...
+
+   MIKCVT  song1.s3m song2.xm
+   MIKCVT  -strip *.s3m
+   MIKCVT  *.*
+
+ The first example shows the general syntax of MIKCVT.  Note that
+ -strip is optional, but if present MUST be the first option on
+ the command line.
+
+ In any instance, the -strip is optional, and if present will tell
+ MIKCVT to remove all text strings from the module.  This includes
+ the title, sample names, instrument names, comments and other forms
+ of text.  This feature is generally intended for use by game and demo
+ developers, who may not require such information in the release pro-
+ duct.
+
+ 
+-/- Miscellaneous
+=================
+
+ There will probably be a future version of MIKCVT that will further
+ optimize modules in -strip mode.
+
+ For information, questions, comments, or various chit-chat, contact
+ Jake Stine / Divine Entertainment at jstine@hotmail.com.
+
+ For updates and other updated information, check out the Divine Ent-
+ ertainment website at http://www.epix.net/~dracoirs/
+
+
+ That's all for today.  Have fun!
+
+
Binary file docs/mikmod.doc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/tips.txt	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,64 @@
+
+ --> MikMod 3.0
+  -> Various Programming Tips and Tricks!
+
+
+ Here are some tips and tricks that I have jotted down as I think of them.
+ The list is short for now, but it will no doubt grow as more are thought
+ up.  Also, check for additional information on the Divine Entertainment
+ website at http://www.epix.net/~dracoirs/
+
+ Ok, so this little text file is pretty short right now.  These things
+ take time to think of.. :-)
+
+
+-/- Compiling / Makefiles
+
+ Watcom:
+
+   The example files that come with MikMod default to 30k stack buffers
+   (option stack=30000 on the wlink command line).  This is a safety pre-
+   caution and by no means a requirement.
+
+   You can usually safely use a 10k stack if you are using an interrupt
+   driven MikMod_Update() and you can safely use a 5k stack if you are
+   using polling (calling MikMod_Update() from a main program loop).
+
+                               ----------
+
+   If you are writing a module player and are NOT calling MikMod_Update()
+   from an interrupt, then you can safely remove /ZU from the following
+   modules in the /MikMod directory:
+
+      MDRIVER.C
+      VIRTCH.C
+
+   Removing /ZU will slightly reduce code size and increase replay speed.
+
+                               ----------
+   
+   If you are using the IDE and want to add PMODE/W to the list of valid
+   targets, follow these steps:
+
+     a) copy PMWBIND.EXE and PMODEW.LNK to your watcom/binw diectory.
+
+     b) Load WSYSTEM.LNK, and inster the contents of PMODEW.LINK into it
+        at the very top.
+
+     c) Load IDEDOS32.CFG.  Insert a line that reads:
+            Target *.exe, dw2e_, "PMODE/W Executable"
+
+     d) Load IDE.CFG.  Around line 840, you will find the following line:
+          VSwitch  0, dr2??, "System:", SYS, " ", ONE, REQ, dos4g
+        Inser this line below it:
+          VSwitch  0, dw2??, "System:", SYS, " ", ONE, REQ, pmodew
+
+     e) This is the tricky part.  There are several lines in IDE.CFG that
+        contain 'dr2'.  Each of these [excluding the special case in step
+        d] must be duplicated, and 'dr2' replaced with 'dw2' (no other
+        changes required).  Do this right and you will have full IDE cap-
+        ability with the pmode/w extender!
+
+   There you go, all set to compile with pmode/w within the Watcom IDE!
+
+                               ----------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/drv_nos.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,160 @@
+/*
+
+Name:
+DRV_NOS.C
+
+Description:
+Mikmod driver for no output on any soundcard, monitor, keyboard, or whatever :)
+
+Portability:
+All systems - All compilers
+
+*/
+
+#include "mikmod.h"
+
+
+static BOOL NS_IsThere(void)
+{
+    return 1;
+}
+
+
+static SWORD NS_SampleLoad(SAMPLOAD *s, int type, FILE *fp)
+{
+    return 0;
+}
+
+
+static void NS_SampleUnload(SWORD h)
+{
+}
+
+
+static ULONG NS_SampleSpace(int type)
+{
+    return 0;
+}
+
+
+static ULONG NS_SampleLength(int type, SAMPLE *s)
+{
+    return s->length;
+}
+
+
+static BOOL NS_Init(void)
+{
+    return 0;
+}
+
+
+static void NS_Exit(void)
+{
+}
+
+
+static BOOL NS_Reset(void)
+{
+    return 0;
+}
+
+
+static BOOL NS_PlayStart(void)
+{
+}
+
+
+static void NS_PlayStop(void)
+{
+}
+
+
+static void NS_Update(void)
+{
+}
+
+
+static BOOL NS_SetNumVoices(void)
+{
+    return 0;
+}
+
+
+static void NS_VoiceSetVolume(UBYTE voice,UWORD vol)
+{
+}
+
+
+static void NS_VoiceSetFrequency(UBYTE voice,ULONG frq)
+{
+}
+
+
+static void NS_VoiceSetPanning(UBYTE voice,ULONG pan)
+{
+}
+
+
+static void NS_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)
+{
+}
+
+
+static void NS_VoiceStop(UBYTE voice)
+{
+}
+
+
+static BOOL NS_VoiceStopped(UBYTE voice)
+{
+   return 0;
+}
+
+
+static void NS_VoiceReleaseSustain(UBYTE voice)
+{
+}
+
+
+static SLONG NS_VoiceGetPosition(UBYTE voice)
+{
+   return 0;
+}
+
+
+static ULONG NS_VoiceRealVolume(UBYTE voice)
+{
+   return 0;
+}
+
+
+
+MDRIVER drv_nos =
+{   NULL,
+    "No Sound",
+    "Nosound Driver v2.0 - (c) Creative Silence",
+    255,255,
+    NS_IsThere,
+    NS_SampleLoad,
+    NS_SampleUnload,
+    NS_SampleSpace,
+    NS_SampleLength,
+    NS_Init,
+    NS_Exit,
+    NS_Reset,
+    NS_SetNumVoices,
+    NS_PlayStart,
+    NS_PlayStop,
+    NS_Update,
+    NS_VoiceSetVolume,
+    NS_VoiceSetFrequency,
+    NS_VoiceSetPanning,
+    NS_VoicePlay,
+    NS_VoiceStop,
+    NS_VoiceStopped,
+    NS_VoiceReleaseSustain,
+    NS_VoiceGetPosition,
+    NS_VoiceRealVolume
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/drv_wav.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,116 @@
+
+#include "mikmod.h"
+
+#ifdef __GNUC__
+#include <sys/types.h>
+#else
+#include <io.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define WAVBUFFERSIZE 65536
+
+static FILE *wavout;
+
+static SBYTE *WAV_DMABUF;
+static ULONG dumpsize;
+
+static BOOL WAV_IsThere(void)
+{
+    return 1;
+}
+
+
+static BOOL WAV_Init(void)
+{
+    if(NULL == (wavout = fopen("music.wav", "wb"))) return 1;
+    if(NULL == (WAV_DMABUF = _mm_malloc(WAVBUFFERSIZE))) return 1;
+
+    md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
+
+    if(VC_Init()) return 1;
+    
+    _mm_write_string("RIFF    WAVEfmt ",wavout);
+    _mm_write_I_ULONG(16,wavout);     // length of this RIFF block crap
+
+    _mm_write_I_UWORD(1, wavout);    // microsoft format type
+    _mm_write_I_UWORD((md_mode & DMODE_STEREO) ? 2 : 1, wavout);
+    _mm_write_I_ULONG(md_mixfreq, wavout);
+    _mm_write_I_ULONG(md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) *
+                      ((md_mode & DMODE_16BITS) ? 2 : 1), wavout);
+
+    _mm_write_I_UWORD(((md_mode & DMODE_16BITS) ? 2 : 1) * 
+                      ((md_mode & DMODE_STEREO) ? 2 : 1), wavout);    // block alignment (8/16 bit)
+
+    _mm_write_I_UWORD((md_mode & DMODE_16BITS) ? 16 : 8,wavout);
+
+    _mm_write_string("data",wavout);
+
+    dumpsize = 0;
+
+    return 0;
+}
+
+
+static void WAV_Exit(void)
+{
+    VC_Exit();
+
+    // write in the actual sizes now
+
+    if(wavout!=NULL)
+    {   _mm_fseek(wavout,4,SEEK_SET);
+        _mm_write_I_ULONG(dumpsize + 32, wavout);
+        _mm_fseek(wavout,40,SEEK_SET);
+        _mm_write_I_ULONG(dumpsize, wavout);
+
+        fclose(wavout);
+
+        if(WAV_DMABUF != NULL) free(WAV_DMABUF);
+    }
+}
+
+
+static void WAV_Update(void)
+{
+    VC_WriteBytes(WAV_DMABUF, WAVBUFFERSIZE);
+    fwrite(WAV_DMABUF, 1, WAVBUFFERSIZE, wavout);
+    dumpsize += WAVBUFFERSIZE;
+}
+
+
+static BOOL WAV_Reset(void)
+{
+    return 0;
+}
+
+
+MDRIVER drv_wav =
+{   NULL,
+    "music.wav file",
+    "WAV [music.wav] file output driver v1.0",
+    0,255,
+    WAV_IsThere,
+    VC_SampleLoad,
+    VC_SampleUnload,
+    VC_SampleSpace,
+    VC_SampleLength,
+    WAV_Init,
+    WAV_Exit,
+    WAV_Reset,
+    VC_SetNumVoices,
+    VC_PlayStart,
+    VC_PlayStop,
+    WAV_Update,
+    VC_VoiceSetVolume,
+    VC_VoiceSetFrequency,
+    VC_VoiceSetPanning,
+    VC_VoicePlay,
+    VC_VoiceStop,
+    VC_VoiceStopped,
+    VC_VoiceReleaseSustain,
+    VC_VoiceGetPosition,
+    NULL
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_669.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,255 @@
+/*
+
+ Name: LOAD_669.C
+
+ Description:
+ Tran's 669 module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+// Raw 669 header struct:
+
+typedef struct S69HEADER
+{   UBYTE  marker[2];
+    CHAR   message[108];
+    UBYTE  nos;
+    UBYTE  nop;
+    UBYTE  looporder;
+    UBYTE  orders[0x80];
+    UBYTE  tempos[0x80];
+    UBYTE  breaks[0x80];
+} S69HEADER;
+
+
+// Raw 669 sampleinfo struct:
+
+typedef struct S69SAMPLE
+{   CHAR   filename[13];
+    SLONG  length;
+    SLONG  loopbeg;
+    SLONG  loopend;
+} S69SAMPLE;
+
+
+// Raw 669 Note struct
+
+typedef struct S69NOTE
+{   UBYTE a,b,c;
+} S69NOTE;
+
+
+static S69NOTE *s69pat = NULL;
+static S69HEADER *mh   = NULL;
+
+static CHAR *S69_Version[] =
+{   "669",
+    "Extended 669"
+};
+
+
+BOOL S69_Test(void)
+{
+    UBYTE id[2];
+
+    if(!_mm_read_UBYTES(id,2,modfp)) return 0;
+    if(!memcmp(id,"if",2) || !memcmp(id,"JN",2))
+    {   _mm_fseek(modfp,108,SEEK_CUR);
+        if(_mm_read_UBYTE(modfp) > 64) return 0;
+        if(_mm_read_UBYTE(modfp) > 128) return 0;
+        if(_mm_read_UBYTE(modfp) > 120) return 0;
+        return 1;
+    }
+    return 0;
+}
+
+
+BOOL S69_Init(void)
+{
+    if(!(s69pat=(S69NOTE *)_mm_malloc(64*8*sizeof(S69NOTE)))) return 0;
+    if(!(mh=(S69HEADER *)_mm_calloc(1,sizeof(S69HEADER)))) return 0;
+    return 1;
+}
+
+
+void S69_Cleanup(void)
+{
+    if(s69pat!=NULL) free(s69pat);
+    if(mh!=NULL) free(mh);
+
+    mh     = NULL;
+    s69pat = NULL;
+}
+
+
+BOOL S69_LoadPatterns(void)
+{
+    int     t,s,q,tracks=0,t2,t3;
+    UBYTE   note,inst,vol,a,b,c, lo;
+    S69NOTE *cur;
+
+    if(!AllocPatterns()) return 0;
+    if(!AllocTracks())   return 0;
+
+    for(t=0; t<of.numpat; t++)
+    {   of.pattrows[t] = mh->breaks[t]+1;
+
+        // Load the pattern into the temp buffer
+        // and convert it into the 3-byte format
+
+        cur = s69pat;
+        for(t2=64; t2; t2--)
+        {   for(t3=8; t3; t3--, cur++)
+            {   cur->a = _mm_read_UBYTE(modfp);
+                cur->b = _mm_read_UBYTE(modfp);
+                cur->c = _mm_read_UBYTE(modfp);
+            }
+        }
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 0;
+        }
+
+        for(s=0; s<8; s++)
+        {   UniReset();
+            UniPTEffect(0xf,75);   // was 78
+            UniPTEffect(0xf,3);
+
+            for(q=0; q<64; q++)
+            {   a = s69pat[(q*8)+s].a;
+                b = s69pat[(q*8)+s].b;
+                c = s69pat[(q*8)+s].c;
+
+                note = a >> 2;
+                inst = ((a & 0x3) << 4) | ((b & 0xf0) >> 4);
+                vol  = b & 0xf;
+
+                if(note < 0x3e)
+                {   UniInstrument(inst);
+                    UniNote(note+24);
+                }
+
+                if(note < 0x3f) UniPTEffect(0xc,vol<<2);
+
+                lo = c & 0xf;
+                switch(c >> 4)
+                {   case 0:
+                        UniPTEffect(0x1,lo);
+                    break;
+
+                    case 1:
+                        UniPTEffect(0x2,lo);
+                    break;
+                   
+                    case 2:
+                        UniPTEffect(0x3,lo);
+                    break;
+
+                    case 4:
+                        UniPTEffect(0x4,lo);
+                    break;
+                }
+                UniNewline();
+            }
+            if(!(of.tracks[tracks++]=UniDup())) return 0;
+        }
+    }
+    return 1;
+}
+
+
+BOOL S69_Load(void)
+{
+    int         t;
+    S69SAMPLE   s;
+    SAMPLE     *q;
+
+    // try to read module header       
+
+    _mm_read_UBYTES(mh->marker,2,modfp);
+    _mm_read_UBYTES((UBYTE *)mh->message,108,modfp);
+    mh->nos = _mm_read_UBYTE(modfp);
+    mh->nop = _mm_read_UBYTE(modfp);
+    mh->looporder = _mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(mh->orders,0x80,modfp);
+    _mm_read_UBYTES(mh->tempos,0x80,modfp);
+    _mm_read_UBYTES(mh->breaks,0x80,modfp);
+
+    // set module variables
+
+    of.initspeed = 6;
+    of.inittempo = 125;
+    of.songname  = DupStr(mh->message,108);
+    of.modtype   = strdup(S69_Version[memcmp(mh->marker,"JN",2)==0]);
+    of.numchn    = 8;
+    of.numpat    = mh->nop;
+    of.numins    = of.numsmp = mh->nos;
+    of.numtrk    = of.numchn*of.numpat;
+    of.flags     = UF_XMPERIODS; // | UF_LINEAR;
+
+    if(!AllocPositions(0x80)) return 0;
+    for(t=0; t<0x80; t++)
+    {   if(mh->orders[t]==0xff) break;
+        of.positions[t] = mh->orders[t];
+    }
+
+    of.numpos = t;
+
+    if(!AllocSamples()) return 0;
+    q = of.samples;
+
+    for(t=0; t<of.numins; t++)
+    {   // try to read sample info
+
+        _mm_read_UBYTES((UBYTE *)s.filename,13,modfp);
+        s.length   = _mm_read_I_SLONG(modfp);
+        s.loopbeg  = _mm_read_I_SLONG(modfp);
+        s.loopend  = _mm_read_I_SLONG(modfp);
+
+        if((s.length < 0) || (s.loopbeg < -1) || (s.loopend < -1))
+        {   _mm_errno = MMERR_LOADING_HEADER;
+            return 0;
+        }
+
+        q->samplename = DupStr(s.filename,13);
+
+        q->seekpos   = 0;
+        q->speed     = 0;
+        q->length    = s.length;
+        q->loopstart = s.loopbeg;
+        q->loopend   = (s.loopend<s.length) ? s.loopend : s.length;
+        q->flags     = (s.loopbeg<s.loopend) ? SF_LOOP : 0;
+        q->volume    = 64;
+
+        q++;
+    }
+
+    if(!S69_LoadPatterns()) return 0;
+
+    return 1;
+}
+
+
+MLOADER load_669 =
+{   NULL,
+    "669",
+    "Portable 669 loader v0.1",
+    S69_Init,
+    S69_Test,
+    S69_Load,
+    S69_Cleanup,
+    NULL
+};
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_dsm.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,314 @@
+/*
+
+ Name: LOAD_DSM.C
+
+ Description:
+ DSIK Internal Format (DSM) module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+typedef struct DSMNOTE
+{   UBYTE note,ins,vol,cmd,inf;
+} DSMNOTE;
+
+
+typedef struct DSMINST
+{   CHAR  filename[13];
+    UWORD flags;
+    UBYTE volume;
+    ULONG length;
+    ULONG loopstart;
+    ULONG loopend;
+    ULONG reserved1;
+    UWORD c2spd;
+    UWORD reserved2;
+    CHAR  samplename[28];
+} DSMINST;
+
+
+typedef struct DSMSONG
+{  CHAR  songname[28];
+   UWORD reserved1;
+   UWORD flags;
+   ULONG reserved2;
+   UWORD numord;
+   UWORD numsmp;
+   UWORD numpat;
+   UWORD numtrk;
+   UBYTE globalvol;
+   UBYTE mastervol;
+   UBYTE speed;
+   UBYTE bpm;
+   UBYTE panpos[16];
+   UBYTE orders[128];
+} DSMSONG;
+
+
+
+static CHAR  *SONGID = "SONG";
+static CHAR  *INSTID = "INST";
+static CHAR  *PATTID = "PATT";
+
+
+static UBYTE blockid[4];
+static ULONG blockln;
+static ULONG blocklp;
+static DSMSONG *mh = NULL;
+static DSMNOTE *dsmbuf = NULL;
+
+static CHAR  DSM_Version[] = "DSIK DSM-format";
+
+
+BOOL DSM_Test(void)
+{
+    UBYTE id[12];
+
+    if(_mm_read_UBYTES((UBYTE *)id,12,modfp)) return 0;
+    if(!memcmp(id,"RIFF",4) && !memcmp(&id[8],"DSMF",4)) return 1;
+    
+    return 0;
+}
+
+
+BOOL DSM_Init(void)
+{
+    if(!(dsmbuf=(DSMNOTE *)_mm_malloc(16*64*sizeof(DSMNOTE)))) return 0;
+    if(!(mh=(DSMSONG *)_mm_calloc(1,sizeof(DSMSONG)))) return 0;
+    return 1;
+}
+
+
+void DSM_Cleanup(void)
+{
+   if(dsmbuf!=NULL) free(dsmbuf);
+   if(mh!=NULL) free(mh);
+
+   dsmbuf = NULL;
+   mh     = NULL;
+}
+
+
+BOOL GetBlockHeader(void)
+{
+   // make sure we're at the right position for reading the
+   // next riff block, no matter how many bytes read
+
+   _mm_fseek(modfp, blocklp+blockln, SEEK_SET);
+   
+   while(1)
+   {   _mm_read_UBYTES(blockid,4,modfp);
+       blockln = _mm_read_I_ULONG(modfp);
+       if(feof(modfp))
+       {   _mm_errno = MMERR_LOADING_HEADER;
+           return 0;
+       }
+       
+       if(memcmp(blockid,SONGID,4) && memcmp(blockid,INSTID,4) && memcmp(blockid,PATTID,4))
+       {   //printf("Skipping unknown block type %4.4s\n",&blockid);
+           _mm_fseek(modfp, blockln, SEEK_CUR);
+       } else break;
+   }
+
+   blocklp = _mm_ftell(modfp);
+   return 1;
+}
+
+
+BOOL DSM_ReadPattern(void)
+{
+    int row=0,flag;
+    DSMNOTE *n;
+
+    // clear pattern data
+    memset(dsmbuf,255,16*64*sizeof(DSMNOTE));
+    _mm_read_UBYTE(modfp);
+    _mm_read_UBYTE(modfp);
+
+    while(row<64)
+    {   flag = _mm_read_UBYTE(modfp);
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 0;
+        }
+
+        if(flag)
+        {   n = &dsmbuf[((flag&0xf)*64)+row];
+            if(flag&0x80) n->note = _mm_read_UBYTE(modfp);
+            if(flag&0x40) n->ins  = _mm_read_UBYTE(modfp);
+            if(flag&0x20) n->vol  = _mm_read_UBYTE(modfp);
+            if(flag&0x10)
+            {   n->cmd = _mm_read_UBYTE(modfp);
+                n->inf = _mm_read_UBYTE(modfp);
+            }
+        } else row++;
+    }
+    return 1;
+}
+
+
+UBYTE *DSM_ConvertTrack(DSMNOTE *tr)
+{
+    int t;
+    UBYTE note,ins,vol,cmd,inf;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {   note = tr[t].note;
+        ins  = tr[t].ins;
+        vol  = tr[t].vol;
+        cmd  = tr[t].cmd;
+        inf  = tr[t].inf;
+
+        if(ins!=0 && ins!=255) UniInstrument(ins-1);
+        if(note!=255) UniNote(note-1);                // <- normal note
+        if(vol<65) UniPTEffect(0xc,vol);
+
+        if(cmd!=255)
+        {   if(cmd==0x8)
+            {   if(inf<=0x80)
+                {   inf = (inf<0x80) ? inf<<1 : 255;
+                    UniPTEffect(cmd,inf);
+                }
+            } else if(cmd==0xb)
+            {   if(inf<=0x7f) UniPTEffect(cmd,inf);
+            } else
+            {   // Convert pattern jump from Dec to Hex
+                if(cmd == 0xd)
+                    inf = (((inf&0xf0)>>4)*10)+(inf&0xf);
+                UniPTEffect(cmd,inf);
+            }
+        }
+        UniNewline();
+     }
+     return UniDup();
+}
+
+
+BOOL DSM_Load(void)
+{
+       int     t;
+       DSMINST s;
+       SAMPLE *q;
+       int    cursmp = 0, curpat = 0, track = 0;
+
+       blocklp = 0;
+       blockln = 12;
+
+       if(!GetBlockHeader()) return 0;
+       if(memcmp(blockid,SONGID,4))
+       {   _mm_errno = MMERR_LOADING_HEADER;
+           return 0;
+       }
+
+       _mm_read_UBYTES(mh->songname,28,modfp);
+       mh->reserved1   = _mm_read_I_UWORD(modfp);
+       mh->flags       = _mm_read_I_UWORD(modfp);
+       mh->reserved2   = _mm_read_I_ULONG(modfp);
+       mh->numord      = _mm_read_I_UWORD(modfp);
+       mh->numsmp      = _mm_read_I_UWORD(modfp);
+       mh->numpat      = _mm_read_I_UWORD(modfp);
+       mh->numtrk      = _mm_read_I_UWORD(modfp);
+       mh->globalvol   = _mm_read_UBYTE(modfp);
+       mh->mastervol   = _mm_read_UBYTE(modfp);
+       mh->speed       = _mm_read_UBYTE(modfp);
+       mh->bpm         = _mm_read_UBYTE(modfp);
+       _mm_read_UBYTES(mh->panpos,16,modfp);
+       _mm_read_UBYTES(mh->orders,128,modfp);
+
+       // set module variables
+       of.initspeed = mh->speed;
+       of.inittempo = mh->bpm;
+       of.modtype   = strdup(DSM_Version);
+       of.numchn    = mh->numtrk;
+       of.numpat    = mh->numpat;
+       of.numtrk    = of.numchn*of.numpat;
+       of.songname  = DupStr(mh->songname,28);    // make a cstr of songname
+
+       for(t=0; t<16; t++)
+          of.panning[t] = mh->panpos[t]<0x80 ? (mh->panpos[t]<<1) : 255;
+
+       if(!AllocPositions(mh->numord)) return 0;
+       of.numpos = 0;
+       for(t=0; t<mh->numord; t++)
+       {   of.positions[of.numpos] = mh->orders[t];
+           if(mh->orders[t]<254) of.numpos++;
+       }
+
+       of.numins = of.numsmp = mh->numsmp;
+
+       if(!AllocSamples()) return 0;
+       if(!AllocTracks()) return 0;
+       if(!AllocPatterns()) return 0;
+
+       while(cursmp<of.numins || curpat<of.numpat)
+       {   if(!GetBlockHeader()) return 0;
+           if(!memcmp(blockid,INSTID,4) && cursmp<of.numins)
+           {   q = &of.samples[cursmp];
+               
+               // try to read sample info
+               _mm_read_UBYTES(s.filename,13,modfp);
+               s.flags     = _mm_read_I_UWORD(modfp);
+               s.volume    = _mm_read_UBYTE(modfp);
+               s.length    = _mm_read_I_ULONG(modfp);
+               s.loopstart = _mm_read_I_ULONG(modfp);
+               s.loopend   = _mm_read_I_ULONG(modfp);
+               s.reserved1 = _mm_read_I_ULONG(modfp);
+               s.c2spd     = _mm_read_I_UWORD(modfp);
+               s.reserved2 = _mm_read_I_UWORD(modfp);
+               _mm_read_UBYTES(s.samplename,28,modfp);
+
+               q->samplename= DupStr(s.samplename,28);
+               q->seekpos   = _mm_ftell(modfp);
+               q->speed     = s.c2spd;
+               q->length    = s.length;
+               q->loopstart = s.loopstart;
+               q->loopend   = s.loopend;
+               q->volume    = s.volume;
+
+               if(s.flags&1) q->flags|=SF_LOOP;
+               if(s.flags&2) q->flags|=SF_SIGNED;
+               cursmp++;
+           } else if(!memcmp(blockid,PATTID,4) && curpat<of.numpat)
+           {   DSM_ReadPattern();
+               for(t=0; t<of.numchn; t++)
+                  if(!(of.tracks[track++] = DSM_ConvertTrack(&dsmbuf[t*64]))) return 0;
+               curpat++;
+           }
+       }
+
+       return 1;
+}
+
+
+CHAR *DSM_LoadTitle(void)
+{
+   CHAR s[28];
+
+   _mm_fseek(modfp,12,SEEK_SET);
+   if(!fread(s,28,1,modfp)) return NULL;
+   
+   return(DupStr(s,28));
+}
+
+
+MLOADER load_dsm =
+{   NULL,
+    "DSM",
+    "Portable DSM loader v0.1",
+    DSM_Init,
+    DSM_Test,
+    DSM_Load,
+    DSM_Cleanup,
+    DSM_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_far.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,288 @@
+/*
+
+ Name: LOAD_FAR.C
+
+ Description:
+ Farandole (FAR) module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+typedef struct FARSAMPLE
+{   CHAR  samplename[32];
+    ULONG length;
+    UBYTE finetune;
+    UBYTE volume;
+    ULONG reppos;
+    ULONG repend;
+    UBYTE type;
+    UBYTE loop;
+} FARSAMPLE;
+
+
+
+typedef struct FARHEADER1
+{   UBYTE id[4];                        // file magic
+    CHAR  songname[40];                 // songname
+    CHAR  blah[3];                      // 13,10,26
+    UWORD headerlen;                    // remaining length of header in bytes
+    UBYTE version;
+    UBYTE onoff[16];
+    UBYTE edit1[9];
+    UBYTE speed;
+    UBYTE panning[16];
+    UBYTE edit2[4];
+    UWORD stlen;
+} FARHEADER1;
+
+
+typedef struct FARHEADER2
+{   UBYTE orders[256];
+    UBYTE numpat;
+    UBYTE snglen;
+    UBYTE loopto;
+    UWORD patsiz[256];
+} FARHEADER2;
+
+
+typedef struct FARNOTE
+{   UBYTE note,ins,vol,eff;
+} FARNOTE;
+
+
+
+static CHAR FAR_Version[] = "Farandole";
+static FARHEADER1 *mh1 = NULL;
+static FARHEADER2 *mh2 = NULL;
+static FARNOTE *pat = NULL;
+
+
+BOOL FAR_Test(void)
+{
+    UBYTE id[4];
+
+    if(!_mm_read_UBYTES(id,4,modfp)) return 0;
+    return(!memcmp(id,"FAR=",4));
+}
+
+
+BOOL FAR_Init(void)
+{
+    if(!(mh1 = (FARHEADER1 *)_mm_malloc(sizeof(FARHEADER1)))) return 0;
+    if(!(mh2 = (FARHEADER2 *)_mm_malloc(sizeof(FARHEADER2)))) return 0;
+    if(!(pat = (FARNOTE *)_mm_malloc(16*256*sizeof(FARNOTE)))) return 0;
+
+    return 1;
+}
+
+
+void FAR_Cleanup(void)
+{
+    if(mh1!=NULL) free(mh1);
+    if(mh2!=NULL) free(mh2);
+    if(pat!=NULL) free(pat);
+
+    mh1 = NULL;
+    mh2 = NULL;
+    pat = NULL;
+}
+
+
+UBYTE *FAR_ConvertTrack(FARNOTE *n,int rows)
+{
+       int t;
+
+       UniReset();
+
+       for(t=0; t<rows; t++)
+       {   if(n->note)
+           {   UniInstrument(n->ins);
+               UniNote(n->note+23+12);
+           }
+
+           if(n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2);
+           switch(n->eff>>4)
+           {   case 0xf:
+                  UniPTEffect(0xf,n->eff&0xf);
+               break;
+
+               // others not yet implemented
+           }
+
+           UniNewline();
+           n+=16;
+       }
+       return UniDup();
+}
+
+
+BOOL FAR_Load(void)
+{
+       int t,u,tracks=0;
+       SAMPLE     *q;
+       FARSAMPLE  s;
+       FARNOTE    *crow;
+       UBYTE      smap[8];
+
+       // try to read module header (first part)
+       _mm_read_UBYTES(mh1->id,4,modfp);
+       _mm_read_SBYTES(mh1->songname,40,modfp);
+       _mm_read_SBYTES(mh1->blah,3,modfp);
+       mh1->headerlen = _mm_read_I_UWORD (modfp);
+       mh1->version   = _mm_read_UBYTE (modfp);
+       _mm_read_UBYTES(mh1->onoff,16,modfp);
+       _mm_read_UBYTES(mh1->edit1,9,modfp);
+       mh1->speed     = _mm_read_UBYTE(modfp);
+       _mm_read_UBYTES(mh1->panning,16,modfp);
+       _mm_read_UBYTES(mh1->edit2,4,modfp);
+       mh1->stlen     = _mm_read_I_UWORD (modfp);
+
+
+       // init modfile data
+
+       of.modtype   = strdup(FAR_Version);
+       of.songname  = DupStr(mh1->songname,40);
+       of.numchn    = 16;
+       of.initspeed = mh1->speed;
+       of.inittempo = 99;
+
+       for(t=0; t<16; t++) of.panning[t] = mh1->panning[t]<<4;
+
+       // read songtext into comment field
+       if(!ReadComment(mh1->stlen)) return 0;
+
+       // try to read module header (second part)
+       _mm_read_UBYTES(mh2->orders,256,modfp);
+       mh2->numpat        = _mm_read_UBYTE(modfp);
+       mh2->snglen        = _mm_read_UBYTE(modfp);
+       mh2->loopto        = _mm_read_UBYTE(modfp);
+       _mm_read_I_UWORDS(mh2->patsiz,256,modfp);
+
+//      of.numpat=mh2->numpat;
+       of.numpos = mh2->snglen;
+       if(!AllocPositions(of.numpos)) return 0;
+       for(t=0; t<of.numpos; t++)
+       {   if(mh2->orders[t]==0xff) break;
+           of.positions[t] = mh2->orders[t];
+       }
+
+       // count number of patterns stored in file
+       of.numpat = 0;
+       for(t=0; t<256; t++)
+          if(mh2->patsiz[t]) if((t+1)>of.numpat) of.numpat=t+1;
+
+       of.numtrk = of.numpat*of.numchn;
+
+       // seek across eventual new data
+       _mm_fseek(modfp,mh1->headerlen-(869+mh1->stlen),SEEK_CUR);
+
+       // alloc track and pattern structures
+       if(!AllocTracks()) return 0;
+       if(!AllocPatterns()) return 0;
+
+       for(t=0; t<of.numpat; t++)
+       {   UBYTE rows=0,tempo;
+
+           memset(pat,0,16*256*sizeof(FARNOTE));
+           if(mh2->patsiz[t])
+           {   rows  = _mm_read_UBYTE(modfp);
+               tempo = _mm_read_UBYTE(modfp);
+
+               crow = pat;
+               for(u=mh2->patsiz[t]-2; u; u--, crow++)
+               {   crow->note = _mm_read_UBYTE(modfp);
+                   crow->ins  = _mm_read_UBYTE(modfp);
+                   crow->vol  = _mm_read_UBYTE(modfp);
+                   crow->eff  = _mm_read_UBYTE(modfp);
+               }
+               
+               if(feof(modfp))
+               {   _mm_errno = MMERR_LOADING_PATTERN;
+                   return 0;
+               }
+
+               of.pattrows[t] = rows+2;
+               crow = pat;
+               for(u=16; u; u--)
+                  if(!(of.tracks[tracks++] = FAR_ConvertTrack(crow,rows+2))) return 0;
+           }
+       }
+
+       // read sample map
+       if(!_mm_read_UBYTES(smap,8,modfp))
+       {   _mm_errno = MMERR_LOADING_HEADER;
+           return 0;
+       }
+
+       // count number of samples used
+       of.numins = 0;
+       for(t=0; t<64; t++)
+          if(smap[t>>3] & (1 << (t&7)))  of.numins++;
+       of.numsmp = of.numins;             
+
+       // alloc sample structs
+       if(!AllocSamples()) return 0;
+       q = of.samples;
+
+       for(t=0; t<64; t++)
+       {  if(smap[t>>3] & (1 << (t&7)))
+           {  _mm_read_SBYTES(s.samplename,32,modfp);
+              s.length   = _mm_read_I_ULONG(modfp);
+              s.finetune = _mm_read_UBYTE(modfp);
+              s.volume   = _mm_read_UBYTE(modfp);
+              s.reppos   = _mm_read_I_ULONG(modfp);
+              s.repend   = _mm_read_I_ULONG(modfp);
+              s.type     = _mm_read_UBYTE(modfp);
+              s.loop     = _mm_read_UBYTE(modfp);
+
+              q->samplename = DupStr(s.samplename,32);
+              q->length     = s.length;
+              q->loopstart  = s.reppos;
+              q->loopend    = s.repend;
+              q->volume     = 64;
+              q->speed      = 8363;
+
+              q->flags=SF_SIGNED;
+              if(s.type&1) q->flags|=SF_16BITS;
+              if(s.loop) q->flags|=SF_LOOP;
+
+              q->seekpos = _mm_ftell(modfp);
+              _mm_fseek(modfp,q->length,SEEK_CUR);
+           }
+           q++;
+       }
+       return 1;
+}
+
+
+CHAR *FAR_LoadTitle(void)
+{
+   CHAR s[40];
+
+   _mm_fseek(modfp,4,SEEK_SET);
+   if(!fread(s,40,1,modfp)) return NULL;
+   
+   return(DupStr(s,40));
+}
+
+
+MLOADER load_far =
+{   NULL,
+    "FAR",
+    "Portable FAR loader v0.1",
+    FAR_Init,
+    FAR_Test,
+    FAR_Load,
+    FAR_Cleanup,
+    FAR_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_it.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,857 @@
+/*
+
+ Name: LOAD_IT.C
+
+ Description:
+ ImpulseTracker (IT) module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ Copyright 1997 by Jake Stine and Divine Entertainment
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+/**************************************************************************
+**************************************************************************/
+
+typedef struct ITNOTE
+{   UBYTE note,ins,volpan,cmd,inf;
+} ITNOTE;
+
+UBYTE *IT_ConvertTrack(ITNOTE *tr,UWORD numrows);
+
+
+// Raw IT header struct:
+
+typedef struct ITHEADER
+{   CHAR  songname[26];
+    UBYTE blank01[2];
+    UWORD ordnum;
+    UWORD insnum;
+    UWORD smpnum;
+    UWORD patnum;
+    UWORD cwt;          // Created with tracker (y.xx = 0x0yxx)
+    UWORD cmwt;         // Compatable with tracker ver > than val.
+    UWORD flags;
+    UWORD special;      // bit 0 set = song message attached
+    UBYTE globvol;
+    UBYTE mixvol;       // mixing volume [ignored]
+    UBYTE initspeed;
+    UBYTE inittempo;
+    UBYTE pansep;       // panning separation between channels
+    UBYTE zerobyte;       
+    UWORD msglength;
+    ULONG msgoffset;
+    UBYTE blank02[4];
+ 
+    UBYTE pantable[64];
+    UBYTE voltable[64];
+} ITHEADER;
+
+
+// Raw IT sampleinfo struct:
+
+typedef struct ITSAMPLE
+{   CHAR  filename[12];
+    UBYTE zerobyte;
+    UBYTE globvol;
+    UBYTE flag;
+    UBYTE volume;
+    UBYTE panning;
+    CHAR  sampname[28];
+    UWORD convert;        // sample conversion flag
+    ULONG length;
+    ULONG loopbeg;
+    ULONG loopend;
+    ULONG c5spd;
+    ULONG susbegin;
+    ULONG susend;
+    ULONG sampoffset;
+    UBYTE vibspeed;
+    UBYTE vibdepth;
+    UBYTE vibrate;
+    UBYTE vibwave;    // 0 = sine; 1 = rampdown; 2 = square; 3 = random (speed ignored)
+
+    UBYTE noteindex;  // for converting c5spd to finetune
+} ITSAMPLE;
+
+
+typedef struct ITINSTHEADER
+{   ULONG size;             // (dword) Instrument size
+    CHAR  filename[12];     // (char) Instrument filename
+    UBYTE zerobyte;         // (byte) Instrument type (always 0)
+    UBYTE volflg;
+    UBYTE volpts;   
+    UBYTE volbeg;           // (byte) Volume loop start (node)
+    UBYTE volend;           // (byte) Volume loop end (node)
+    UBYTE volsusbeg;        // (byte) Volume sustain begin (node)
+    UBYTE volsusend;        // (byte) Volume Sustain end (node)
+    UBYTE panflg;
+    UBYTE panpts;  
+    UBYTE panbeg;           // (byte) channel loop start (node)
+    UBYTE panend;           // (byte) channel loop end (node)
+    UBYTE pansusbeg;        // (byte) cahnnel sustain begin (node)
+    UBYTE pansusend;        // (byte) channel Sustain end (node)
+    UBYTE pitflg;
+    UBYTE pitpts;   
+    UBYTE pitbeg;           // (byte) pitch loop start (node)
+    UBYTE pitend;           // (byte) pitch loop end (node)
+    UBYTE pitsusbeg;        // (byte) pitch sustain begin (node)
+    UBYTE pitsusend;        // (byte) pitch Sustain end (node)
+    UWORD blank;
+    UBYTE globvol;
+    UBYTE chanpan;
+    UWORD fadeout;          // Envelope end / NNA volume fadeout
+    UBYTE dnc;              // Duplicate note check
+    UBYTE dca;              // Duplicate check action
+    UBYTE dct;              // Duplicate check type
+    UBYTE nna;              // New Note Action [0,1,2,3]
+    UWORD trkvers;          // tracker version used to save [in files only]
+    UBYTE ppsep;            // Pitch-pan Separation
+    UBYTE ppcenter;         // Pitch-pan Center
+    UBYTE rvolvar;          // random volume varations
+    UBYTE rpanvar;          // random panning varations
+    UWORD numsmp;           // Number of samples in instrument [in files only]
+    CHAR  name[26];         // Instrument name
+    UBYTE blank01[6];
+    UWORD samptable[120];   // sample for each note [note / samp pairs]
+
+    UBYTE volenv[200];      // volume envelope (IT 1.x stuff)
+    UBYTE oldvoltick[25];   // volume tick position (IT 1.x stuff)
+    UBYTE volnode[25];      // aplitude of volume nodes
+    UWORD voltick[25];      // tick value of volume nodes
+    SBYTE pannode[25];      // panenv - node points
+    UWORD pantick[25];      // tick value of panning nodes
+    SBYTE pitnode[25];      // pitchenv - node points
+    UWORD pittick[25];      // tick value of pitch nodes
+} ITINSTHEADER;                       
+
+
+/**************************************************************************
+**************************************************************************/
+
+extern SBYTE    remap[64];        // for removing empty channels
+extern UBYTE    *poslookup;       // S3M/IT fix - removing blank patterns needs a
+                                  // lookup table to fix position-jump commands
+static ULONG    *paraptr = NULL;  // parapointer array (see IT docs)
+static ITHEADER *mh = NULL;
+static ITNOTE   *itpat = NULL;    // allocate to space for one full pattern
+static UBYTE    *mask = NULL;     // arrays allocated to 64 elements and used for
+static ITNOTE   *last = NULL;     // uncompressing IT's pattern information
+static int       numtrk = 0;
+static int       old_effect;      // if set, use S3M old-effects stuffs
+static int       *noteindex;
+   
+CHAR IT_Version[] = "ImpulseTracker x.xx";
+
+
+BOOL IT_Test(void)
+{
+    UBYTE id[4];
+    
+    if(!_mm_read_UBYTES(id,4,modfp)) return 0;
+    if(!memcmp(id,"IMPM",4)) return 1;
+    return 0;
+}
+
+BOOL IT_Init(void)
+{
+    if((mh=(ITHEADER *)_mm_calloc(1,sizeof(ITHEADER)))==NULL) return 0;
+    if((poslookup=(UBYTE *)_mm_malloc(256*sizeof(UBYTE)))==NULL) return 0;
+    if((itpat=(ITNOTE *)_mm_malloc(200*64*sizeof(ITNOTE)))==NULL) return 0;
+    if((mask=(UBYTE *)_mm_malloc(64*sizeof(UBYTE)))==NULL) return 0;
+    if((last=(ITNOTE *)_mm_malloc(64*sizeof(ITNOTE)))==NULL) return 0;
+    
+    return 1;
+}
+
+void IT_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    if(poslookup!=NULL) free(poslookup);
+    if(itpat!=NULL) free(itpat);
+    if(mask!=NULL) free(mask);
+    if(last!=NULL) free(last);
+    if(paraptr!=NULL) free(paraptr);
+    if(noteindex!=NULL) free(noteindex);
+
+    mh        = NULL;
+    poslookup = NULL;
+    itpat     = NULL;
+    mask      = NULL;
+    last      = NULL;
+    paraptr   = NULL;
+    noteindex = NULL;
+}
+
+
+BOOL IT_GetNumChannels(UWORD patrows)
+
+// Because so many IT files have 64 channels as the set number used, but really
+// only use far less (usually 8 to 12 still), I had to make this function,
+// which determines the number of channels that are actually USED by a pattern.
+//
+// For every channel that's used, it sets the appropriate array entry of the
+// global varialbe 'isused'
+//
+// NOTE: You must first seek to the file location of the pattern before calling
+//       this procedure.
+// Returns 1 on error
+{
+    int  row=0,flag,ch;
+
+    do
+    {   flag = _mm_read_UBYTE(modfp);
+        if(flag == EOF)
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 1;
+        }
+
+        if(flag == 0)
+        {   row++;
+        } else
+        {   ch = (flag-1) & 63;
+            remap[ch] = 0;
+            if(flag & 128) mask[ch] = _mm_read_UBYTE(modfp);
+            if(mask[ch] &   1) _mm_read_UBYTE(modfp);
+            if(mask[ch] &   2) _mm_read_UBYTE(modfp);
+            if(mask[ch] &   4) _mm_read_UBYTE(modfp);
+            if(mask[ch] &   8) { _mm_read_UBYTE(modfp); _mm_read_UBYTE(modfp); }
+        }
+    } while(row < patrows);
+
+    return 0;
+}
+
+
+BOOL IT_ReadPattern(UWORD patrows)
+{
+   int    blah;
+   int    row=0,flag,ch;
+   ITNOTE *itt = itpat, dummy,*n,*l;
+
+   memset(itt,255,patrows*of.numchn*sizeof(ITNOTE));
+
+   do 
+   {   flag = _mm_read_UBYTE(modfp);
+       if(feof(modfp))
+       {   _mm_errno = MMERR_LOADING_PATTERN;
+           return 0;
+       }
+       if(flag == 0)
+       {   itt = &itt[of.numchn];
+           row++;
+       } else
+       {   ch = remap[(flag-1) & 63];
+           if(ch != -1)
+           {   n = &itt[ch];
+               l = &last[ch];
+           } else
+           {   n = l = &dummy;  }
+
+           if(flag & 128) mask[ch] = _mm_read_UBYTE(modfp);
+           if(mask[ch] &   1) if((l->note = n->note   = _mm_read_UBYTE(modfp)) == 255)
+                              {   l->note = n->note   = 253; }
+           if(mask[ch] &   2) l->ins      = n->ins    = _mm_read_UBYTE(modfp);
+           if(mask[ch] &   4) l->volpan   = n->volpan = _mm_read_UBYTE(modfp);
+           if(mask[ch] &   8) { l->cmd    = n->cmd    = _mm_read_UBYTE(modfp);
+                                l->inf    = n->inf    = _mm_read_UBYTE(modfp); }
+           if(mask[ch] &  16) n->note     = l->note;
+           if(mask[ch] &  32) n->ins      = l->ins;
+           if(mask[ch] &  64) n->volpan   = l->volpan;
+           if(mask[ch] & 128) { n->cmd    = l->cmd;
+                                n->inf    = l->inf; }
+        }
+   } while(row < patrows);
+
+   for(blah=0; blah<of.numchn; blah++)
+       of.tracks[numtrk++] = IT_ConvertTrack(&itpat[blah],patrows);
+
+   return 1;
+}
+
+
+void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, BOOL oldeffect);
+
+// table for porta-to-note command within volume/panning column
+static UBYTE portatable[] = "1, 4, 8, 16, 32, 64, 96, 128, 255";
+
+UBYTE *IT_ConvertTrack(ITNOTE *tr, UWORD numrows)
+{
+    int t;
+
+    UBYTE note,ins,volpan;
+
+    UniReset();
+
+    for(t=0; t<numrows; t++)
+    {   note    = tr[t*of.numchn].note;
+        ins     = tr[t*of.numchn].ins;
+        volpan  = tr[t*of.numchn].volpan;
+
+        if(note!=255)
+        {   if(note==253)
+                UniWrite(UNI_KEYOFF);
+            else if(note==254)
+                UniPTEffect(0xc,0);   // <- note cut command
+            else
+                UniNote(note);
+        }
+
+        if((ins != 0) && (ins < 100)) UniInstrument(ins-1);
+
+        // process volume / panning column
+        // volume / panning effects do NOT all share the same memory address
+        // yet.  That requires more work than I care to apply at the moment ;)
+
+        if(volpan<=64)
+        {   UniVolEffect(VOL_VOLUME,volpan);
+        } else if((volpan>=65) && (volpan<=74))        // fine volume slide up (65-74)
+        {   UniVolEffect(VOL_VOLSLIDE,0x0f + ((volpan-65)<<4));
+        } else if((volpan>=75) && (volpan<=84))        // fine volume slide down   (75-84)
+        {   UniVolEffect(VOL_VOLSLIDE,0xf0 + (volpan-75));
+        } else if((volpan>=85) && (volpan<=94))        // volume slide up (85-94)
+        {   UniVolEffect(VOL_VOLSLIDE,((volpan-85)<<4));
+        } else if((volpan>=95) && (volpan<=104))       // volume slide down   (95-104)
+        {   UniVolEffect(VOL_VOLSLIDE,(volpan-95));
+        } else if((volpan>=105) && (volpan<=114))      // pitch slide up (105-114)
+        {   UniVolEffect(VOL_PITCHSLIDEDN,((volpan-105)<<4));
+        } else if((volpan>=115) && (volpan<=124))      // pitch slide down (115-124)
+        {   UniVolEffect(VOL_PITCHSLIDEUP,(volpan-115));
+        } else if((volpan>=128) && (volpan<=192))
+        {   UniVolEffect(VOL_PANNING,((volpan-128) == 64) ? 255 : ((volpan-128) << 2));
+        } else if((volpan>=193) && (volpan<=202))      // portamento to note
+        {   UniVolEffect(VOL_PORTAMENTO,portatable[volpan-193]);
+        } else if((volpan>=203) && (volpan<=212))      // vibrato
+        {   UniVolEffect(VOL_VIBRATO,(volpan-203));
+        }
+
+        S3MIT_ProcessCmd(tr[t*of.numchn].cmd,tr[t*of.numchn].inf,old_effect);
+
+        UniNewline();
+    }
+    return UniDup();
+}
+                                             
+
+int cvt_c5spd_to_finetune(ULONG c5spd, int sampnum)
+{
+    int ctmp=0,tmp,note=1,finetune=0;
+
+    c5spd/=2;
+
+    do
+    {   tmp = getfrequency(of.flags,getlinearperiod(note,0));
+        if(tmp >= c5spd) break;
+        ctmp = tmp;
+        note++;
+    } while(1);
+
+    if(tmp != c5spd)
+    {   if((tmp-c5spd) < (c5spd-ctmp))
+            while(tmp>c5spd) tmp = getfrequency(of.flags,getlinearperiod(note,--finetune));
+        else
+        {   note--;
+            while(ctmp<c5spd) ctmp = getfrequency(of.flags,getlinearperiod(note,++finetune));
+        }
+    }
+
+    noteindex[sampnum] = note-48;
+    return finetune;
+}       
+
+
+BOOL IT_Load(void)
+{
+    int        t,u,lp;
+    INSTRUMENT *d;
+    SAMPLE     *q;
+
+    numtrk = 0;
+
+    // try to read module header
+
+    _mm_read_I_ULONG(modfp);   // kill the 4 byte header
+    _mm_read_string(mh->songname,26,modfp);
+    _mm_read_UBYTES(mh->blank01,2,modfp);
+    mh->ordnum      =_mm_read_I_UWORD(modfp);
+    mh->insnum      =_mm_read_I_UWORD(modfp);
+    mh->smpnum      =_mm_read_I_UWORD(modfp);
+    mh->patnum      =_mm_read_I_UWORD(modfp);
+    mh->cwt         =_mm_read_I_UWORD(modfp);
+    mh->cmwt        =_mm_read_I_UWORD(modfp);
+    mh->flags       =_mm_read_I_UWORD(modfp);
+    mh->special     =_mm_read_I_UWORD(modfp);
+
+    mh->globvol     =_mm_read_UBYTE(modfp);
+    mh->mixvol      =_mm_read_UBYTE(modfp);
+    mh->initspeed   =_mm_read_UBYTE(modfp);
+    mh->inittempo   =_mm_read_UBYTE(modfp);
+    mh->pansep      =_mm_read_UBYTE(modfp);
+    mh->zerobyte    =_mm_read_UBYTE(modfp);
+    mh->msglength   =_mm_read_I_UWORD(modfp);
+    mh->msgoffset   =_mm_read_I_ULONG(modfp);
+    _mm_read_UBYTES(mh->blank02,4,modfp);
+    _mm_read_UBYTES(mh->pantable,64,modfp);
+    _mm_read_UBYTES(mh->voltable,64,modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // set module variables
+
+    of.modtype     = strdup(IT_Version);
+    of.modtype[15] = (mh->cwt >> 8) + 0x30;
+    of.modtype[17] = ((mh->cwt >> 4) & 0xf) + 0x30;
+    of.modtype[18] = ((mh->cwt) & 0xf) + 0x30;
+    of.songname    = DupStr(mh->songname,26);    // make a cstr of songname 
+    of.reppos      = 0;
+    of.numpat      = mh->patnum;
+    of.numins      = mh->insnum;
+    of.numsmp      = mh->smpnum;
+    of.initspeed   = mh->initspeed;
+    of.inittempo   = mh->inittempo;
+    of.initvolume  = mh->globvol;
+
+    old_effect = 0;
+    if(mh->flags & 8) { of.flags |= (UF_XMPERIODS | UF_LINEAR); old_effect |= 2; }
+    if((mh->cwt >= 0x106) && (mh->flags & 16))  old_effect |= 1;
+
+    // set panning positions
+    for(t=0; t<64; t++)
+    {   if(mh->pantable[t] < 64) of.panning[t] = mh->pantable[t] << 2;
+        else if(mh->pantable[t]==64) of.panning[t] = 255;
+        else if(mh->pantable[t]==100) of.panning[t] = PAN_SURROUND;
+    }
+
+    // set channel volumes
+    memcpy(of.chanvol,mh->voltable,64);
+
+    // read the order data
+    if(!AllocPositions(mh->ordnum)) return 0;
+
+    for(t=0; t<mh->ordnum; t++)
+        of.positions[t] = _mm_read_UBYTE(modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+    
+    of.numpos = 0;
+    for(t=0; t<mh->ordnum; t++)
+    {   of.positions[of.numpos] = of.positions[t];
+        poslookup[t] = of.numpos;    // bug fix for FREAKY S3Ms / ITs
+        if(of.positions[t]<254) of.numpos++;
+    }
+
+    if((paraptr=(ULONG *)_mm_malloc((mh->insnum+mh->smpnum+of.numpat)*sizeof(ULONG))) == NULL) return 0;
+
+    // read the instrument, sample, and pattern parapointers
+    _mm_read_I_ULONGS(paraptr,mh->insnum+mh->smpnum+of.numpat,modfp);
+
+    // now is a good time to check if the header was too short :)
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // Check for and load song comment
+    if(mh->special & 1)
+    {   _mm_fseek(modfp,(long)(mh->msgoffset),SEEK_SET);
+        if(!ReadComment(mh->msglength)) return 0;
+    }
+
+    if(!(mh->flags & 4)) of.numins = of.numsmp;
+    if(!AllocSamples()) return 0;
+
+    if((noteindex=(int *)_mm_malloc(mh->smpnum*sizeof(int)))==NULL) return 0;
+        
+    q = of.samples;
+
+    // Load all samples (they're used either way)
+    for(t=0; t<mh->smpnum; t++)
+    {   ITSAMPLE s;
+
+        // seek to sample position
+        _mm_fseek(modfp,(long)(paraptr[mh->insnum+t] + 4),SEEK_SET);
+
+        // and load sample info
+        _mm_read_string(s.filename,12,modfp);
+        s.zerobyte    = _mm_read_UBYTE(modfp);
+        s.globvol     = _mm_read_UBYTE(modfp);
+        s.flag        = _mm_read_UBYTE(modfp);
+        s.volume      = _mm_read_UBYTE(modfp);
+        _mm_read_string(s.sampname,26,modfp);
+        s.convert     = _mm_read_UBYTE(modfp);
+        s.panning     = _mm_read_UBYTE(modfp);
+        s.length      = _mm_read_I_ULONG(modfp);
+        s.loopbeg     = _mm_read_I_ULONG(modfp);
+        s.loopend     = _mm_read_I_ULONG(modfp);
+        s.c5spd       = _mm_read_I_ULONG(modfp);
+        s.susbegin    = _mm_read_I_ULONG(modfp);
+        s.susend      = _mm_read_I_ULONG(modfp);
+        s.sampoffset  = _mm_read_I_ULONG(modfp);
+        s.vibspeed    = _mm_read_UBYTE(modfp);
+        s.vibdepth    = _mm_read_UBYTE(modfp);
+        s.vibrate     = _mm_read_UBYTE(modfp);
+        s.vibwave     = _mm_read_UBYTE(modfp);
+
+ 
+        // Generate an error if c5spd is > 512k, or samplelength > 256 megs
+        //  (nothing would EVER be that high)
+
+        if(feof(modfp) || (s.c5spd > 0x7ffffL) || (s.length > 0xfffffffUL) ||
+          (s.loopbeg > 0xfffffffUL) || (s.loopend > 0xfffffffUL))
+        {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+            return 0;
+        }
+
+        q->samplename = DupStr(s.sampname,26);
+
+        q->speed      = s.c5spd / 2;
+        q->panning    = ((s.panning & 127)==64) ? 255 : (s.panning & 127) << 2;
+        q->length     = s.length;
+        q->loopstart  = s.loopbeg;
+        q->loopend    = s.loopend;
+        q->volume     = s.volume;
+        q->globvol    = s.globvol;
+        q->seekpos    = s.sampoffset;
+
+        // ===================================
+        // Convert speed to XM linear finetune
+
+        if(of.flags & UF_LINEAR)
+            q->speed = cvt_c5spd_to_finetune(s.c5spd, t);
+
+        if(s.panning & 128) q->flags |= SF_OWNPAN;
+
+        if(s.vibrate)
+        {   q->vibflags |= AV_IT;
+            q->vibtype   = s.vibwave;
+            q->vibsweep  = s.vibrate * 2;
+            q->vibdepth  = s.vibdepth;
+            q->vibrate   = s.vibspeed;
+        }
+
+        if(s.flag & 2)  q->flags |= SF_16BITS;
+        if(s.flag & 16) q->flags |= SF_LOOP;
+        if(s.flag & 64) q->flags |= SF_BIDI;
+
+        if(mh->cwt >= 0x200)
+        {   if(s.convert & 1) q->flags |= SF_SIGNED;
+            if(s.convert & 4) q->flags |= SF_DELTA;   
+        }
+                              
+        q++;
+    }
+
+    // Load instruments if instrument mode flag enabled
+
+    if(mh->flags & 4)
+    {   if(!AllocInstruments()) return 0;
+        d = of.instruments;
+        of.flags |= UF_NNA | UF_INST;
+
+        for(t=0; t<mh->insnum; t++)
+        {   ITINSTHEADER ih;
+
+            // seek to instrument position
+            _mm_fseek(modfp,paraptr[t]+4,SEEK_SET);
+
+            // and load instrument info
+            _mm_read_string(ih.filename,12,modfp);
+            ih.zerobyte  = _mm_read_UBYTE(modfp);
+            if(mh->cwt < 0x200) // load IT 1.xx inst header
+            {   ih.volflg    = _mm_read_UBYTE(modfp);
+                ih.volbeg    = _mm_read_UBYTE(modfp);
+                ih.volend    = _mm_read_UBYTE(modfp);
+                ih.volsusbeg = _mm_read_UBYTE(modfp);
+                ih.volsusend = _mm_read_UBYTE(modfp);
+                _mm_read_I_UWORD(modfp);
+                ih.fadeout   = _mm_read_I_UWORD(modfp);
+                ih.nna       = _mm_read_UBYTE(modfp);
+                ih.dnc       = _mm_read_UBYTE(modfp);
+            } else   // Read IT200+ header
+            {   ih.nna       = _mm_read_UBYTE(modfp);
+                ih.dct       = _mm_read_UBYTE(modfp);
+                ih.dca       = _mm_read_UBYTE(modfp);
+                ih.fadeout   = _mm_read_I_UWORD(modfp);
+                ih.ppsep     = _mm_read_UBYTE(modfp);
+                ih.ppcenter  = _mm_read_UBYTE(modfp);
+                ih.globvol   = _mm_read_UBYTE(modfp);
+                ih.chanpan   = _mm_read_UBYTE(modfp);
+                ih.rvolvar   = _mm_read_UBYTE(modfp);
+                ih.rpanvar   = _mm_read_UBYTE(modfp);
+            }
+
+            ih.trkvers   = _mm_read_I_UWORD(modfp);
+            ih.numsmp    = _mm_read_UBYTE(modfp);
+            _mm_read_UBYTE(modfp);
+            _mm_read_string(ih.name,26,modfp);
+            _mm_read_UBYTES(ih.blank01,6,modfp);
+            _mm_read_I_UWORDS(ih.samptable,120,modfp);
+            if(mh->cwt < 0x200)  // load IT 1xx volume envelope
+            {   _mm_read_UBYTES(ih.volenv,200,modfp);
+                for(lp=0; lp<25; lp++)
+                {   ih.oldvoltick[lp] = _mm_read_UBYTE(modfp);
+                    ih.volnode[lp]    = _mm_read_UBYTE(modfp);
+                }
+            } else // load IT 2xx vol & chanpan & pitch envs
+            {   ih.volflg    = _mm_read_UBYTE(modfp);
+                ih.volpts    = _mm_read_UBYTE(modfp);
+                ih.volbeg    = _mm_read_UBYTE(modfp);
+                ih.volend    = _mm_read_UBYTE(modfp);
+                ih.volsusbeg = _mm_read_UBYTE(modfp);
+                ih.volsusend = _mm_read_UBYTE(modfp);
+                for(lp=0; lp<25; lp++)
+                {   ih.volnode[lp] = _mm_read_UBYTE(modfp);
+                    ih.voltick[lp] = _mm_read_I_UWORD(modfp);
+                }
+                _mm_read_UBYTE(modfp);
+
+                ih.panflg    = _mm_read_UBYTE(modfp);
+                ih.panpts    = _mm_read_UBYTE(modfp);
+                ih.panbeg    = _mm_read_UBYTE(modfp);
+                ih.panend    = _mm_read_UBYTE(modfp);
+                ih.pansusbeg = _mm_read_UBYTE(modfp);
+                ih.pansusend = _mm_read_UBYTE(modfp);
+                for(lp=0; lp<25; lp++)
+                {   ih.pannode[lp] = _mm_read_SBYTE(modfp);
+                    ih.pantick[lp] = _mm_read_I_UWORD(modfp);
+                }
+                _mm_read_UBYTE(modfp);
+
+                ih.pitflg    = _mm_read_UBYTE(modfp);
+                ih.pitpts    = _mm_read_UBYTE(modfp);
+                ih.pitbeg    = _mm_read_UBYTE(modfp);
+                ih.pitend    = _mm_read_UBYTE(modfp);
+                ih.pitsusbeg = _mm_read_UBYTE(modfp);
+                ih.pitsusend = _mm_read_UBYTE(modfp);
+                for(lp=0; lp<25; lp++)
+                {   ih.pitnode[lp] = _mm_read_SBYTE(modfp);
+                    ih.pittick[lp] = _mm_read_I_UWORD(modfp);
+                }
+                _mm_read_UBYTE(modfp);
+            }
+ 
+            if(feof(modfp))
+            {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+                return 0;
+            }
+
+            d->volflg |= EF_VOLENV;
+            d->insname = DupStr(ih.name,26);
+            d->nnatype = ih.nna;
+            
+            if(mh->cwt < 0x200)
+            {   d->volfade = ih.fadeout << 6;
+                if(ih.dnc)
+                {   d->dct = DCT_NOTE;
+                    d->dca = DCA_CUT;
+                }
+
+                if(ih.volflg & 1) d->volflg |= EF_ON;
+                if(ih.volflg & 2) d->volflg |= EF_LOOP;
+                if(ih.volflg & 4) d->volflg |= EF_SUSTAIN;      
+               
+                // XM conversion of IT envelope Array
+                
+                d->volbeg    = ih.volbeg;   
+                d->volend    = ih.volend;
+                d->volsusbeg = ih.volsusbeg;
+                d->volsusend = ih.volsusend;
+            
+                if(ih.volflg & 1)
+                {   for(u=0; u<25; u++)
+                        if(ih.oldvoltick[d->volpts] != 0xff)
+                        {   d->volenv[d->volpts].val = (ih.volnode[d->volpts] << 2);
+                            d->volenv[d->volpts].pos = ih.oldvoltick[d->volpts];
+                            d->volpts++;
+                        } else break;
+                }  
+            } else
+            {   d->panning = ((ih.chanpan&127) == 64) ? 255 : (ih.chanpan&127)<<2;
+                if(!(ih.chanpan & 128)) d->flags |= IF_OWNPAN;
+
+                if(!(ih.ppsep & 128))
+                {   d->pitpansep = ih.ppsep << 2;
+                    d->pitpancenter= ih.ppcenter;
+                    d->flags |= IF_PITCHPAN;
+                }
+                d->globvol = ih.globvol >> 1;
+                d->volfade = ih.fadeout << 5;
+                d->dct     = ih.dct;
+                d->dca     = ih.dca;
+
+                if(mh->cwt >= 0x204)
+                {   d->rvolvar = ih.rvolvar;
+                    d->rpanvar = ih.rpanvar;
+                }
+
+                if(ih.volflg & 1) d->volflg |= EF_ON;
+                if(ih.volflg & 2) d->volflg |= EF_LOOP;
+                if(ih.volflg & 4) d->volflg |= EF_SUSTAIN;
+
+                if(ih.panflg & 1) d->panflg |= EF_ON;
+                if(ih.panflg & 2) d->panflg |= EF_LOOP;
+                if(ih.panflg & 4) d->panflg |= EF_SUSTAIN;      
+
+                if(ih.pitflg & 1) d->pitflg |= EF_ON;
+                if(ih.pitflg & 2) d->pitflg |= EF_LOOP;
+                if(ih.pitflg & 4) d->pitflg |= EF_SUSTAIN;      
+
+                d->volpts    = ih.volpts;
+                d->volbeg    = ih.volbeg;   
+                d->volend    = ih.volend;
+                d->volsusbeg = ih.volsusbeg;
+                d->volsusend = ih.volsusend;
+
+                for(u=0; u<ih.volpts; u++)
+                {   d->volenv[u].val = (ih.volnode[u] << 2);
+                    d->volenv[u].pos = ih.voltick[u];
+                }
+
+                d->panpts    = ih.panpts;
+                d->panbeg    = ih.panbeg;
+                d->panend    = ih.panend;
+                d->pansusbeg = ih.pansusbeg;
+                d->pansusend = ih.pansusend;
+ 
+                for(u=0; u<ih.panpts; u++)                                               
+                {   d->panenv[u].val = (ih.pannode[u]+32) << 2;
+                    d->panenv[u].pos = ih.pantick[u];
+                }
+
+                d->pitpts    = ih.pitpts;
+                d->pitbeg    = ih.pitbeg;
+                d->pitend    = ih.pitend;
+                d->pitsusbeg = ih.pitsusbeg;
+                d->pitsusend = ih.pitsusend;
+
+                for(u=0; u<ih.pitpts; u++)
+                {   d->pitenv[u].val = (ih.pitnode[u]+32);
+                    d->pitenv[u].pos = ih.pittick[u];
+                }
+            }
+   
+            if(of.flags & UF_LINEAR)
+            {   for(u=0; u<120; u++)
+                {   d->samplenote[u]   = (ih.samptable[u] & 255);
+                    d->samplenumber[u] = (ih.samptable[u] >> 8) ? ((ih.samptable[u] >> 8) - 1) : 255;
+                    if(d->samplenumber[u]!=255)
+                        d->samplenote[u] += noteindex[d->samplenumber[u]];
+                }
+            } else
+            {   for(u=0; u<120; u++)
+                {   d->samplenote[u]   = (ih.samptable[u] & 255);
+                    d->samplenumber[u] = (ih.samptable[u] >> 8) ? ((ih.samptable[u] >> 8) - 1) : 255;
+                }
+            }
+
+            d++;                  
+        }
+    } else if(of.flags & UF_LINEAR)
+    {   if(!AllocInstruments()) return 0;
+        d = of.instruments;
+        of.flags |= UF_INST;
+
+        for(t=0; t<mh->smpnum; t++, d++)
+        {   for(u=0; u<120; u++)
+                d->samplenote[u] += noteindex[d->samplenumber[u]];
+        }
+    }
+
+
+    // Figure out how many channels this blasted song actually uses (what
+    // ever happened to common courtesy of storing this simple value
+    // somewhere in the damn module, eh!?)
+
+    of.numchn = 0;
+    memset(remap,-1,64*sizeof(UBYTE));
+    
+    for(t=0; t<of.numpat; t++)
+    {   UWORD packlen;
+
+        // seek to pattern position
+        if(paraptr[mh->insnum+mh->smpnum+t] != 0)  // No parapointer = pattern of 64 rows, EMPTY
+        {   _mm_fseek(modfp,(((long)paraptr[mh->insnum+mh->smpnum+t])),SEEK_SET);
+            packlen = _mm_read_I_UWORD(modfp);
+            packlen = _mm_read_I_UWORD(modfp);    // read pattern length (# of rows)
+            _mm_read_I_ULONG(modfp);
+            if(IT_GetNumChannels(packlen)) return 0;
+        }
+    }
+
+    // give each of them a different number
+    for(t=0; t<64; t++)
+    {   if(remap[t]==0)
+        {   remap[t] = of.numchn;
+            of.numchn++;
+        }
+    }
+
+    of.numtrk = of.numpat*of.numchn;
+
+
+    if(!AllocPatterns()) return 0;
+    if(!AllocTracks()) return 0;
+
+    for(t=0; t<of.numpat; t++)
+    {   UWORD packlen;
+
+        // seek to pattern position
+        if(paraptr[mh->insnum+mh->smpnum+t] == 0)  // No parapointer = pattern of 64 rows, EMPTY
+        {  of.pattrows[t] = 64;
+           for(u=0; u<of.numchn; u++)
+           {  int k;
+              UniReset();
+              for(k=0; k<64; k++) UniNewline();
+              of.tracks[numtrk++] = UniDup();
+           }
+        } else
+        {  _mm_fseek(modfp,(((long)paraptr[mh->insnum+mh->smpnum+t])),SEEK_SET);
+           packlen = _mm_read_I_UWORD(modfp);
+           of.pattrows[t] = _mm_read_I_UWORD(modfp);
+           _mm_read_I_ULONG(modfp);
+
+           if(!IT_ReadPattern(of.pattrows[t])) return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+CHAR *IT_LoadTitle(void)
+{
+   CHAR s[26];
+
+   _mm_fseek(modfp,4,SEEK_SET);
+   if(!fread(s,26,1,modfp)) return NULL;
+   
+   return(DupStr(s,26));
+}
+
+
+MLOADER load_it =
+{   NULL,
+    "IT",
+    "Portable IT loader v0.2",
+    IT_Init,
+    IT_Test,
+    IT_Load,
+    IT_Cleanup,
+
+    IT_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_m15.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,410 @@
+/*
+
+ Name: LOAD_M15.C
+
+ Description:
+ 15 instrument MOD loader
+ Also supports Ultimate Sound Tracker (old M15 format)
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+/*************************************************************************
+*************************************************************************/
+
+
+typedef struct MSAMPINFO       // sample header as it appears in a module
+{   CHAR  samplename[22];
+    UWORD length;
+    UBYTE finetune;
+    UBYTE volume;
+    UWORD reppos;
+    UWORD replen;
+} MSAMPINFO;
+
+
+typedef struct MODULEHEADER          // verbatim module header
+{   CHAR       songname[20];         // the songname..
+    MSAMPINFO  samples[15];          // all sampleinfo
+    UBYTE      songlength;           // number of patterns used
+    UBYTE      magic1;               // should be 127
+    UBYTE      positions[128];       // which pattern to play at pos
+} MODULEHEADER;
+
+
+typedef struct MODNOTE
+{   UBYTE a,b,c,d;
+} MODNOTE;
+
+
+/*************************************************************************
+*************************************************************************/
+
+static MODULEHEADER *mh = NULL;       // raw as-is module header
+static MODNOTE *patbuf = NULL;
+static BOOL ust_loader = 0;          // if TRUE, load as a ust module.
+static CHAR nulls[3] = {0,0,0};
+
+static BOOL LoadModuleHeader(MODULEHEADER *mh)
+{
+    int t;
+
+    _mm_read_string(mh->songname,20,modfp);
+
+    for(t=0; t<15; t++)
+    {   MSAMPINFO *s = &mh->samples[t];
+        _mm_read_string(s->samplename,22,modfp);
+        s->length   =_mm_read_M_UWORD(modfp);
+        s->finetune =_mm_read_UBYTE(modfp);
+        s->volume   =_mm_read_UBYTE(modfp);
+        s->reppos   =_mm_read_M_UWORD(modfp);
+        s->replen   =_mm_read_M_UWORD(modfp);
+    }
+
+    mh->songlength  =_mm_read_UBYTE(modfp);
+    mh->magic1      =_mm_read_UBYTE(modfp);               // should be 127
+    _mm_read_UBYTES(mh->positions,128,modfp);
+
+    return(!feof(modfp));
+}
+
+
+static int CheckPatternType(int numpat)
+
+// Checks the patterns in the modfile for UST / 15-inst indications.
+// For example, if an effect 3xx is found, it is assumed that the song
+// is 15-inst.  If a 1xx effect has dat greater than 0x20, it is UST.
+//  Returns:  0 indecisive; 1 = UST; 2 = 15-inst
+
+{
+    int   t;
+    UBYTE eff, dat;
+
+    ust_loader = 1;
+
+    for(t=0; t<numpat*(64U*4); t++)
+    {   // Load the pattern into the temp buffer
+        // and convert it
+
+        _mm_read_UBYTE(modfp);      // read note
+        _mm_read_UBYTE(modfp);      // read inst
+        eff = _mm_read_UBYTE(modfp);
+        dat = _mm_read_UBYTE(modfp);
+        
+        if((eff==3) && (dat!=0) || (eff >= 2))   return 2;
+        if(eff==1)
+        {   if(dat > 0x1f) return 1;
+            if(dat < 0x3)  return 2;
+        }
+        if((eff==2) && (dat > 0x1f)) return 1;
+    }
+
+    return 0;
+}
+
+
+BOOL M15_Test(void)
+{
+    int          t, numpat;
+    MODULEHEADER mh;
+
+    ust_loader = 0;
+
+    if(!LoadModuleHeader(&mh)) return 0;
+    if(mh.magic1>127) return 0;
+
+    for(t=0; t<15; t++)
+    {   // all finetunes should be zero
+        if(mh.samples[t].finetune != 0) return 0;
+
+        // all volumes should be <= 64
+        if(mh.samples[t].volume > 64) return 0;
+
+        // all instrument names should begin with s, st-, or a number
+        if(mh.samples[t].samplename[0] == 's')
+        {   if((memcmp(mh.samples[t].samplename,"st-",3) != 0) &&
+               (memcmp(mh.samples[t].samplename,"ST-",3) != 0) &&
+               (memcmp(mh.samples[t].samplename,nulls,3) != 0))
+                ust_loader = 1;
+        } else if((mh.samples[t].samplename[0] < '0') || (mh.samples[t].samplename[0] > '9'))
+            ust_loader = 1;
+
+        if(mh.samples[t].length > 4999)
+        {   ust_loader = 0;
+            if(mh.samples[t].length > 32768) return 0;
+        }
+
+        if(!ust_loader) return 1;
+
+        if(((mh.samples[t].reppos) + mh.samples[t].replen) > (mh.samples[t].length + 10))
+        {   ust_loader = 1;
+            return 1;
+        }
+        
+    }
+
+    for(numpat=0, t=0; t<mh.songlength; t++)
+    {   if(mh.positions[t] > numpat)
+            numpat = mh.positions[t];
+    }
+
+    numpat++;
+    switch(CheckPatternType(numpat))
+    {   case 0:   // indecisive, so check more clues...
+
+        break;
+
+        case 1:  ust_loader = 1;   break;
+        case 2:  ust_loader = 0;   break;
+    }
+ 
+    return 1;
+}
+
+
+BOOL M15_Init(void)
+{
+    if(!(mh=(MODULEHEADER *)_mm_calloc(1,sizeof(MODULEHEADER)))) return 0;
+    return 1;
+}
+
+
+void M15_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    if(patbuf!=NULL) free(patbuf);
+
+    mh = NULL;
+    patbuf = NULL;
+}
+
+
+/*
+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 M15_ConvertNote(MODNOTE *n)
+{
+    UBYTE instrument,effect,effdat,note;
+    UWORD period;
+
+    // extract the various information from the 4 bytes that
+    // make up a single 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 != 0)
+    {   for(note=0; note<60; note++)
+            if(period >= npertab[note]) break;
+        note++;
+        if(note==61) note = 0;
+    }
+
+    if(instrument!=0) UniInstrument(instrument-1);
+    if(note!=0) UniNote(note+23);
+
+    // Convert pattern jump from Dec to Hex
+    if(effect == 0xd)
+        effdat = (((effdat&0xf0)>>4)*10)+(effdat&0xf);
+
+    if(ust_loader)
+    {   switch(effect)
+        {   case 0:  break;
+            case 1:
+                UniPTEffect(0,effdat);
+            break;
+
+            case 2:  
+                if(effdat&0xf) UniPTEffect(1,effdat&0xf);
+                if(effdat>>2)  UniPTEffect(2,effdat>>2);
+            break;
+
+            case 3:  break;
+
+            default:
+                UniPTEffect(effect,effdat);
+            break;
+        }
+    } else UniPTEffect(effect,effdat);
+}
+
+
+static UBYTE *M15_ConvertTrack(MODNOTE *n)
+{
+    int t;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {   M15_ConvertNote(n);
+        UniNewline();
+        n += 4;
+    }
+    return UniDup();
+}
+
+
+
+static BOOL M15_LoadPatterns(void)
+//  Loads all patterns of a modfile and converts them into the
+//  3 byte format.
+{
+    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*4,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*4); s++)
+        {   patbuf[s].a=_mm_read_UBYTE(modfp);
+            patbuf[s].b=_mm_read_UBYTE(modfp);
+            patbuf[s].c=_mm_read_UBYTE(modfp);
+            patbuf[s].d=_mm_read_UBYTE(modfp);
+        }
+
+        for(s=0; s<4; s++)
+            if(!(of.tracks[tracks++]=M15_ConvertTrack(patbuf+s))) return 0;
+    }
+
+    return 1;
+}
+
+
+BOOL M15_Load(void)
+{
+    int        t;
+    SAMPLE     *q;
+    MSAMPINFO  *s;           // old module sampleinfo
+
+    // try to read module header
+
+    if(!LoadModuleHeader(mh))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+
+    if(ust_loader)
+        of.modtype = strdup("Ultimate Soundtracker");
+    else
+        of.modtype = strdup("Soundtracker");
+
+    // set module variables
+
+    of.initspeed = 6;
+    of.inittempo = 125;
+    of.numchn    = 4;                           // get number of channels
+    of.songname  = DupStr(mh->songname,20);     // make a cstr of songname
+    of.numpos    = mh->songlength;              // copy the songlength
+
+    if(!AllocPositions(of.numpos)) return 0;
+    for(t=0; t<of.numpos; t++)
+        of.positions[t] = mh->positions[t];
+
+
+    // Count the number of patterns
+
+    of.numpat = 0;
+
+    for(t=0; t<of.numpos; t++)
+    {   if(of.positions[t] > of.numpat)
+            of.numpat = of.positions[t];
+    }
+    of.numpat++;
+    of.numtrk = of.numpat*4;
+
+    // Finally, init the sampleinfo structures
+
+    of.numins = of.numsmp = 15;
+    if(!AllocSamples()) return 0;
+
+    s = mh->samples;          // init source pointer
+    q = of.samples;
+    
+    for(t=0; t<of.numins; t++)
+    {   // convert the samplename
+        q->samplename = DupStr(s->samplename,22);
+
+        // init the sampleinfo variables and
+        // convert the size pointers to longword format
+
+        q->speed     = finetune[s->finetune&0xf];
+        q->volume    = s->volume;
+        if(ust_loader)
+            q->loopstart = s->reppos;
+        else
+            q->loopstart = s->reppos<<1;
+        q->loopend   = q->loopstart+(s->replen<<1);
+        q->length    = s->length<<1;
+
+        q->flags = SF_SIGNED | SF_UST_LOOP;
+        if(s->replen>1) q->flags |= SF_LOOP;
+
+        // fix replen if repend>length
+
+        if(q->loopend>q->length) q->loopend = q->length;
+
+        s++;    // point to next source sampleinfo
+        q++;
+    }
+
+    if(!M15_LoadPatterns()) return 0;
+
+    ust_loader = 0;
+    return 1;
+}
+
+
+CHAR *M15_LoadTitle(void)
+{
+   CHAR s[20];
+
+   fseek(modfp,0,SEEK_SET);
+   if(!fread(s,22,1,modfp)) return NULL;
+
+   return(DupStr(s,20));
+}
+
+
+MLOADER load_m15 =
+{   NULL,
+    "15-instrument module",
+    "Portable MOD-15 loader v0.1",
+    M15_Init,
+    M15_Test,
+    M15_Load,
+    M15_Cleanup,
+    M15_LoadTitle
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_med.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,490 @@
+/*
+
+ Name: LOAD_MED.C
+
+ Description:
+ Amiga MED module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+#define MMD0_string 0x4D4D4430
+#define MMD1_string 0x4D4D4431
+
+typedef struct MMD0
+{   ULONG   id;
+    ULONG   modlen;
+    ULONG   MMD0songP;        // struct MMD0song *song;
+    UWORD   psecnum;          // for the player routine, MMD2 only
+    UWORD   pseq;             //  "   "   "   "
+    ULONG   MMD0BlockPP;      // struct MMD0Block **blockarr;
+    ULONG   reserved1;
+    ULONG   InstrHdrPP;       // struct InstrHdr **smplarr;
+    ULONG   reserved2;
+    ULONG   MMD0expP;         // struct MMD0exp *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
+} MMD0;
+
+
+typedef struct MMD0sample
+{   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)
+} MMD0sample;
+
+
+typedef struct MMD0song
+{   MMD0sample 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
+} MMD0song;
+
+
+typedef struct MMD0NOTE
+{  UBYTE a,b,c;
+} MMD0NOTE;
+
+
+typedef struct MMD1NOTE
+{   UBYTE a,b,c,d;
+} MMD1NOTE;
+
+
+typedef struct InstrHdr
+{   ULONG   length;
+    SWORD   type;
+    // Followed by actual data
+} InstrHdr;
+
+
+static MMD0 *mh     = NULL;
+static MMD0song *ms = NULL;
+static ULONG *ba    = NULL;
+static MMD0NOTE *mmd0pat = NULL;
+static MMD1NOTE *mmd1pat = NULL;
+
+#define d0note(row,col) mmd0pat[(row*(UWORD)of.numchn)+col]
+#define d1note(row,col) mmd1pat[(row*(UWORD)of.numchn)+col]
+
+
+static CHAR MED_Version[] = "MED";
+
+
+BOOL MED_Test(void)
+{
+    UBYTE id[4];
+
+    if(!_mm_read_UBYTES(id,4,modfp)) return 0;
+    if(!memcmp(id,"MMD0",4)) return 1;
+    if(!memcmp(id,"MMD1",4)) return 1;
+    return 0;
+}
+
+
+BOOL MED_Init(void)
+{
+    if(!(mh=(MMD0 *)_mm_calloc(1,sizeof(MMD0)))) return 0;
+    if(!(ms=(MMD0song *)_mm_calloc(1,sizeof(MMD0song)))) return 0;
+    return 1;
+}
+
+
+void MED_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    if(ms!=NULL) free(ms);
+    if(ba!=NULL) free(ba);
+    if(mmd0pat!=NULL) free(mmd0pat);
+    if(mmd1pat!=NULL) free(mmd1pat);
+
+    mh = NULL;
+    ms = NULL;
+    ba = NULL;        // blockarr
+    mmd0pat = NULL;
+    mmd1pat = NULL;    
+}
+
+
+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;
+
+        case 0x6:       // not used
+        case 0x7:       // not used
+        case 0x8:       // midi hold/decay
+        break;
+
+        case 0x9:
+           if(dat<=0x20) UniPTEffect(0xf,dat);
+        break;
+
+        // 0xa 0xb 0xc all PT effects
+
+        case 0xd:       // same as PT volslide
+           UniPTEffect(0xa,dat);
+        break;
+
+        case 0xe:       // synth jmp - midi
+        break;
+
+        case 0xf:
+           // F00 does patternbreak with med
+           if(dat==0) UniPTEffect(0xd,0);
+           else if(dat<=0xa) UniPTEffect(0xf,dat);
+           else if(dat<0xf1) UniPTEffect(0xf,((UWORD)dat*125)/33);
+           else if(dat==0xff) UniPTEffect(0xc,0);  // stop note
+        break;
+
+        default:        // all normal PT effects are handled here :)
+           // Convert pattern jump from Dec to Hex
+           if(eff == 0xd)
+               dat = (((dat&0xf0)>>4)*10)+(dat&0xf);
+           UniPTEffect(eff,dat);
+        break;
+    }
+}
+
+
+
+UBYTE *MED_Convert1(int col)
+{
+    int t;
+    UBYTE a,b,c,d,inst,note,eff,dat;
+    MMD1NOTE *n;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {   n = &d1note(t,col);
+        a = n->a;
+        b = n->b;
+        c = n->c;
+        d = n->d;
+
+        note = a&0x7f;
+        inst = b&0x3f;
+        eff  = c&0xf;
+        dat  = d;
+
+        if(inst!=0) UniInstrument(inst-1);
+        if(note!=0) UniNote(note+23);
+
+        EffectCvt(eff,dat);
+        UniNewline();
+    }
+
+    return UniDup();
+}
+
+
+UBYTE *MED_Convert0(int col)
+{
+    int t;
+    UBYTE a,b,c,inst,note,eff,dat;
+    MMD0NOTE *n;
+
+    UniReset();
+    for(t=0;t<64;t++)
+    {   n = &d0note(t,col);
+        a = n->a;
+        b = n->b;
+        c = n->c;
+
+        note = a & 0x3f;
+        a  >>= 6;
+        a    = ((a & 1) << 1) | (a >> 1);
+
+        inst = (b >> 4) | (a << 4);
+        eff  = b & 0xf;
+        dat  = c;
+
+        if(inst!=0) UniInstrument(inst-1);
+        if(note!=0) UniNote(note+35);
+
+        EffectCvt(eff,dat);
+        UniNewline();
+    }
+    return UniDup();
+}
+
+
+BOOL LoadMMD0Patterns(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(modfp,ba[t],SEEK_SET);
+       numtracks = _mm_read_UBYTE(modfp);
+       numlines  = _mm_read_UBYTE(modfp);
+
+       if(numtracks>of.numchn) of.numchn = numtracks;
+       if(numlines>maxlines) maxlines = numlines;
+   }
+
+   of.numtrk = of.numpat*of.numchn;
+   if(!AllocTracks()) return 0;
+   if(!AllocPatterns()) return 0;
+
+   if(!(mmd0pat=(MMD0NOTE*)_mm_calloc(of.numchn*(maxlines+1),sizeof(MMD0NOTE)))) return 0;
+
+   // second read: no more mr. nice guy,
+   // really read and convert patterns
+
+   for(t=0; t<of.numpat; t++)
+   {   _mm_fseek(modfp,ba[t],SEEK_SET);
+       numtracks = _mm_read_UBYTE(modfp);
+       numlines  = _mm_read_UBYTE(modfp);
+
+       of.pattrows[t] = numlines+1;
+       memset(mmdp=mmd0pat,0,of.numchn*maxlines*sizeof(MMD0NOTE));
+       for(row=numlines+1; row; row--)
+       {   for(col=numtracks; col; col--,mmdp++)
+           {   mmdp->a = _mm_read_UBYTE(modfp);
+               mmdp->b = _mm_read_UBYTE(modfp);
+               mmdp->c = _mm_read_UBYTE(modfp);
+           }
+       }
+
+       for(col=0; col<of.numchn; col++)
+       {   of.tracks[track] = MED_Convert0(col);
+           track++;
+       }
+    }
+    return 1;
+}
+
+
+BOOL 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(modfp,ba[t],SEEK_SET);
+       numtracks = _mm_read_M_UWORD(modfp);
+       numlines  = _mm_read_M_UWORD(modfp);
+
+       _mm_fseek(modfp,sizeof(ULONG),SEEK_CUR);
+       if(numtracks>of.numchn) of.numchn = numtracks;
+       if(numlines>maxlines) maxlines = numlines;
+    }
+
+    of.numtrk = of.numpat*of.numchn;
+    if(!AllocTracks()) return 0;
+    if(!AllocPatterns()) return 0;
+
+    if(!(mmd1pat=(MMD1NOTE*)_mm_calloc(of.numchn*(maxlines+1),sizeof(MMD1NOTE)))) return 0;
+
+    // second read: no more mr. nice guy, really read and convert patterns
+    for(t=0; t<of.numpat; t++)
+    {   _mm_fseek(modfp,ba[t],SEEK_SET);
+        numtracks = _mm_read_M_UWORD(modfp);
+        numlines  = _mm_read_M_UWORD(modfp);
+
+        _mm_fseek(modfp,sizeof(ULONG),SEEK_CUR);
+        of.pattrows[t] = numlines;
+        memset(mmdp=mmd1pat,0,of.numchn*maxlines*sizeof(MMD1NOTE));
+
+        for(row=numlines+1; row; row--)
+        {   for(col=numtracks; col; col--,mmdp++)
+            {   mmdp->a = _mm_read_UBYTE(modfp);
+                mmdp->b = _mm_read_UBYTE(modfp);
+                mmdp->c = _mm_read_UBYTE(modfp);
+                mmdp->d = _mm_read_UBYTE(modfp);
+            }
+        }
+
+        for(col=0;col<of.numchn;col++)
+        {   of.tracks[track]=MED_Convert1(col);
+            track++;
+        }
+    }
+    return 1;
+}
+
+
+
+BOOL MED_Load(void)
+{
+    int        t;
+    ULONG      sa[64];
+    InstrHdr   s;
+    SAMPLE     *q;
+    MMD0sample *mss;
+
+    // try to read module header
+
+    mh->id          = _mm_read_M_ULONG(modfp);
+    mh->modlen      = _mm_read_M_ULONG(modfp);
+    mh->MMD0songP   = _mm_read_M_ULONG(modfp);
+    mh->psecnum     = _mm_read_M_UWORD(modfp);
+    mh->pseq        = _mm_read_M_UWORD(modfp);
+    mh->MMD0BlockPP = _mm_read_M_ULONG(modfp);
+    mh->reserved1   = _mm_read_M_ULONG(modfp);
+    mh->InstrHdrPP  = _mm_read_M_ULONG(modfp);
+    mh->reserved2   = _mm_read_M_ULONG(modfp);
+    mh->MMD0expP    = _mm_read_M_ULONG(modfp);
+    mh->reserved3   = _mm_read_M_ULONG(modfp);
+    mh->pstate      = _mm_read_M_UWORD(modfp);
+    mh->pblock      = _mm_read_M_UWORD(modfp);
+    mh->pline       = _mm_read_M_UWORD(modfp);
+    mh->pseqnum     = _mm_read_M_UWORD(modfp);
+    mh->actplayline = _mm_read_M_SWORD(modfp);
+    mh->counter     = _mm_read_UBYTE(modfp);
+    mh->extra_songs = _mm_read_UBYTE(modfp);
+
+    // Seek to MMD0song struct
+    _mm_fseek(modfp,mh->MMD0songP,SEEK_SET);
+
+
+    // Load the MMD0 Song Header
+
+    mss = ms->sample;     // load the sample data first
+    for(t=63; t; t--, mss++)
+    {   mss->rep        = _mm_read_M_UWORD(modfp);
+        mss->replen     = _mm_read_M_UWORD(modfp);
+        mss->midich     = _mm_read_UBYTE(modfp);
+        mss->midipreset = _mm_read_UBYTE(modfp);
+        mss->svol       = _mm_read_UBYTE(modfp);
+        mss->strans     = _mm_read_SBYTE(modfp);
+    }
+
+    ms->numblocks  = _mm_read_M_UWORD(modfp);
+    ms->songlen    = _mm_read_M_UWORD(modfp);
+    _mm_read_UBYTES(ms->playseq,256,modfp);
+    ms->deftempo   = _mm_read_M_UWORD(modfp);
+    ms->playtransp = _mm_read_SBYTE(modfp);
+    ms->flags      = _mm_read_UBYTE(modfp);
+    ms->flags2     = _mm_read_UBYTE(modfp);
+    ms->tempo2     = _mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(ms->trkvol,16,modfp);
+    ms->mastervol  = _mm_read_UBYTE(modfp);
+    ms->numsamples = _mm_read_UBYTE(modfp);
+
+    // check for a bad header
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // seek to and read the samplepointer array
+    _mm_fseek(modfp,mh->InstrHdrPP,SEEK_SET);
+    if(!_mm_read_M_ULONGS(sa,ms->numsamples,modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // alloc and read the blockpointer array
+    if(!(ba=(ULONG *)_mm_calloc(ms->numblocks, sizeof(ULONG)))) return 0;
+    _mm_fseek(modfp,mh->MMD0BlockPP,SEEK_SET);
+    if(!_mm_read_M_ULONGS(ba,ms->numblocks,modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+
+    // copy song positions
+    if(!AllocPositions(ms->songlen)) return 0;
+    for(t=0; t<ms->songlen; t++)
+       of.positions[t] = ms->playseq[t];
+
+    of.initspeed = 6;
+    of.inittempo = ((UWORD)ms->deftempo*125)/33;
+    of.modtype   = strdup(MED_Version);
+    of.numchn    = 0;                         // will be counted later
+    of.numpat    = ms->numblocks;
+    of.numpos    = ms->songlen;
+    of.numins    = ms->numsamples;
+
+    of.numsmp = of.numins;
+    if(!AllocSamples()) return 0;
+    q = of.samples;
+   
+    for(t=0; t<of.numins; t++)
+    {   _mm_fseek(modfp,sa[t],SEEK_SET);
+        s.length = _mm_read_M_ULONG(modfp);
+        s.type   = _mm_read_M_SWORD(modfp);
+       
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+            return 0;
+        }
+
+        q->samplename = NULL;
+        q->length    = s.length;
+        q->seekpos   = _mm_ftell(modfp);
+        q->loopstart = ms->sample[t].rep<<1;
+        q->loopend   = q->loopstart+(ms->sample[t].replen<<1);
+        q->flags     = SF_SIGNED;
+        q->speed     = 8363;
+        q->volume    = 64;
+
+        if(ms->sample[t].replen>1) q->flags|=SF_LOOP;
+
+        // don't load sample if length>='MMD0' hah.. hah.. very funny.. NOT!
+        if(q->length >= MMD0_string) q->length = 0;
+
+        q++;
+    }
+
+
+    if(mh->id==MMD0_string)
+    {   if(!LoadMMD0Patterns()) return 0;
+    } else if(mh->id==MMD1_string)
+    {   if(!LoadMMD1Patterns()) return 0;
+    } else
+    {   _mm_errno = MMERR_NOT_A_MODULE;
+        return 0;
+    }
+   
+    return 1;
+}
+
+
+MLOADER load_med =
+{   NULL,
+    "MED",
+    "MED loader v0.1",
+    MED_Init,
+    MED_Test,
+    MED_Load,
+    MED_Cleanup,
+    NULL
+};
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_mod.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,341 @@
+/*
+
+ Name: LOAD_MOD.C
+
+ Description:
+ Generic MOD loader (Protracker, StarTracker, FastTracker, etc)
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+/*************************************************************************
+*************************************************************************/
+
+
+typedef struct MSAMPINFO                  // sample header as it appears in a module
+{   CHAR  samplename[22];
+    UWORD length;
+    UBYTE finetune;
+    UBYTE volume;
+    UWORD reppos;
+    UWORD replen;
+} MSAMPINFO;
+
+
+typedef struct MODULEHEADER              // verbatim module header
+{   CHAR       songname[20];             // the songname..
+    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;
+
+#define MODULEHEADERSIZE 1084
+
+
+typedef struct MODTYPE              // struct to identify type of module
+{   CHAR    id[5];
+    UBYTE   channels;
+    CHAR    *name;
+} MODTYPE;
+
+
+typedef struct MODNOTE
+{   UBYTE a,b,c,d;
+} MODNOTE;
+
+
+/*************************************************************************
+*************************************************************************/
+
+
+CHAR protracker[]   = "Protracker";
+CHAR startracker[]  = "Startracker";
+CHAR fasttracker[]  = "Fasttracker";
+CHAR ins15tracker[] = "15-instrument";
+CHAR oktalyzer[]    = "Oktalyzer";
+CHAR taketracker[]  = "TakeTracker";
+
+
+MODTYPE modtypes[] =
+{   "M.K.",4,protracker,      // protracker 4 channel
+    "M!K!",4,protracker,      // protracker 4 channel
+    "FLT4",4,startracker,     // startracker 4 channel
+    "2CHN",2,fasttracker,     // fasttracker 2 channel
+    "4CHN",4,fasttracker,     // fasttracker 4 channel
+    "6CHN",6,fasttracker,     // fasttracker 6 channel
+    "8CHN",8,fasttracker,     // fasttracker 8 channel
+    "10CH",10,fasttracker,    // fasttracker 10 channel
+    "12CH",12,fasttracker,    // fasttracker 12 channel
+    "14CH",14,fasttracker,    // fasttracker 14 channel
+    "16CH",16,fasttracker,    // fasttracker 16 channel
+    "18CH",18,fasttracker,    // fasttracker 18 channel
+    "20CH",20,fasttracker,    // fasttracker 20 channel
+    "22CH",22,fasttracker,    // fasttracker 22 channel
+    "24CH",24,fasttracker,    // fasttracker 24 channel
+    "26CH",26,fasttracker,    // fasttracker 26 channel
+    "28CH",28,fasttracker,    // fasttracker 28 channel
+    "30CH",30,fasttracker,    // fasttracker 30 channel
+    "32CH",32,fasttracker,    // fasttracker 32 channel
+    "CD81",8,oktalyzer,       // atari oktalyzer 8 channel
+    "OKTA",8,oktalyzer,       // atari oktalyzer 8 channel
+    "16CN",16,taketracker,    // taketracker 16 channel
+    "32CN",32,taketracker,    // taketracker 32 channel
+    "    ",4,ins15tracker     // 15-instrument 4 channel    
+};
+
+static MODULEHEADER *mh = NULL;        // raw as-is module header
+static MODNOTE *patbuf = NULL;
+static int modtype = 0;
+
+BOOL MOD_Test(void)
+{
+    UBYTE id[4];
+
+    _mm_fseek(modfp,MODULEHEADERSIZE-4,SEEK_SET);
+    if(!fread(id,4,1,modfp)) return 0;
+
+    // find out which ID string
+
+    for(modtype=0; modtype<23; modtype++)
+        if(!memcmp(id,modtypes[modtype].id,4)) return 1;
+
+    return 0;
+}
+
+
+BOOL MOD_Init(void)
+{
+    if(!(mh=(MODULEHEADER *)_mm_calloc(1,sizeof(MODULEHEADER)))) return 0;
+    return 1;
+}
+
+
+void MOD_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    if(patbuf!=NULL) free(patbuf);
+
+    mh = NULL;
+    patbuf = NULL;
+}
+
+
+/*
+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.
+
+*/
+
+
+void ConvertNote(MODNOTE *n)
+{
+    UBYTE instrument,effect,effdat,note;
+    UWORD period;
+
+    // extract the various information from the 4 bytes that
+    // make up a single 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!=0)
+    {   for(note=0; note<60; note++)
+            if(period >= npertab[note]) break;
+        note++;
+        if(note==61) note = 0;
+    }
+
+    if(instrument!=0) UniInstrument(instrument-1);
+    if(note!=0) UniNote(note+23);
+
+    // Convert pattern jump from Dec to Hex
+    if(effect == 0xd)
+        effdat = (((effdat&0xf0)>>4)*10)+(effdat&0xf);
+
+    UniPTEffect(effect,effdat);
+}
+
+
+UBYTE *ConvertTrack(MODNOTE *n)
+{
+    int t;
+
+    UniReset();
+    for(t=0;t<64;t++)
+    {   ConvertNote(n);
+        UniNewline();
+        n+=of.numchn;
+    }
+    return UniDup();
+}
+
+
+BOOL ML_LoadPatterns(void)
+//  Loads all patterns of a modfile and converts them into the
+//  3 byte format.
+{
+    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(modfp);
+            patbuf[s].b = _mm_read_UBYTE(modfp);
+            patbuf[s].c = _mm_read_UBYTE(modfp);
+            patbuf[s].d = _mm_read_UBYTE(modfp);
+        }
+
+        for(s=0; s<of.numchn; s++)
+            if(!(of.tracks[tracks++]=ConvertTrack(patbuf+s))) return 0;
+    }
+
+    return 1;
+}
+
+
+BOOL MOD_Load(void)
+{
+    int       t;
+    SAMPLE    *q;
+    MSAMPINFO *s;           // old module sampleinfo
+
+    // try to read module header
+
+    _mm_read_string((CHAR *)mh->songname,20,modfp);
+
+    for(t=0; t<31; t++)
+    {   s = &mh->samples[t];
+        _mm_read_string(s->samplename,22,modfp);
+        s->length   =_mm_read_M_UWORD(modfp);
+        s->finetune =_mm_read_UBYTE(modfp);
+        s->volume   =_mm_read_UBYTE(modfp);
+        s->reppos   =_mm_read_M_UWORD(modfp);
+        s->replen   =_mm_read_M_UWORD(modfp);
+    }
+
+    mh->songlength  =_mm_read_UBYTE(modfp);
+    mh->magic1      =_mm_read_UBYTE(modfp);
+
+    _mm_read_UBYTES(mh->positions,128,modfp);
+    _mm_read_UBYTES(mh->magic2,4,modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // set module variables
+
+    of.initspeed = 6;
+    of.inittempo = 125;
+    of.numchn    = modtypes[modtype].channels;      // get number of channels
+    of.modtype   = strdup(modtypes[modtype].name);  // get ascii type of mod
+    of.songname  = DupStr(mh->songname,20);         // make a cstr of songname
+    of.numpos    = mh->songlength;                  // copy the songlength
+
+    if(!AllocPositions(of.numpos)) return 0;
+    for(t=0; t<of.numpos; t++)
+        of.positions[t] = mh->positions[t];
+
+    // Count the number of patterns
+
+    of.numpat = 0;
+
+    for(t=0; t<of.numpos; t++)
+    {   if(of.positions[t] > of.numpat)
+            of.numpat = of.positions[t];
+    }
+    of.numpat++;
+    of.numtrk = of.numpat*of.numchn;
+    
+    // Finally, init the sampleinfo structures 
+    of.numins = of.numsmp = 31;
+
+    if(!AllocSamples())     return 0;
+    
+    s = mh->samples;       // init source pointer 
+    q = of.samples;
+    
+    for(t=0; t<of.numins; t++)
+    {   // convert the samplename
+        q->samplename = DupStr(s->samplename, 22);
+
+        // init the sampleinfo variables and
+        // convert the size pointers to longword format
+
+        q->speed     = finetune[s->finetune & 0xf];
+        q->volume    = s->volume;
+        q->loopstart = (ULONG)s->reppos << 1;
+        q->loopend   = q->loopstart + ((ULONG)s->replen << 1);
+        q->length    = (ULONG)s->length << 1;
+
+        q->flags     = SF_SIGNED;
+        if(s->replen > 1) q->flags |= SF_LOOP;
+
+        // fix replen if repend > length
+        if(q->loopend > q->length) q->loopend = q->length;
+
+        s++;    // point to next source sampleinfo
+        q++;
+    }
+
+    if(!ML_LoadPatterns()) return 0;
+    return 1;
+}
+
+
+CHAR *MOD_LoadTitle(void)
+{
+   CHAR s[20];
+
+   _mm_fseek(modfp,0,SEEK_SET);
+   if(!fread(s,20,1,modfp)) return NULL;
+   
+   return(DupStr(s,20));
+}
+
+
+MLOADER load_mod =
+{   NULL,
+    "Standard module",
+    "Portable MOD loader v0.11",
+    MOD_Init,
+    MOD_Test,
+    MOD_Load,
+    MOD_Cleanup,
+    MOD_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_mtm.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,282 @@
+/*
+
+ Name:  LOAD_MTM.C
+
+ Description:
+ MTM module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+/**************************************************************************
+**************************************************************************/
+
+
+typedef struct MTMSAMPLE
+{   CHAR  samplename[22];
+    ULONG length;
+    ULONG reppos;
+    ULONG repend;
+    UBYTE finetune;
+    UBYTE volume;
+    UBYTE attribute;
+} MTMSAMPLE;
+
+
+
+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 MTMNOTE
+{   UBYTE a,b,c;
+} MTMNOTE;
+
+
+/**************************************************************************
+**************************************************************************/
+
+
+static MTMHEADER *mh = NULL;
+static MTMNOTE *mtmtrk = NULL;
+static UWORD pat[32];
+
+char MTM_Version[] = "MTM";
+
+
+
+BOOL MTM_Test(void)
+{
+    UBYTE id[3];
+    if(!_mm_read_UBYTES(id,3,modfp)) return 0;
+    if(!memcmp(id,"MTM",3)) return 1;
+    return 0;
+}
+
+
+BOOL MTM_Init(void)
+{
+    if(!(mtmtrk=(MTMNOTE *)_mm_calloc(64,sizeof(MTMNOTE)))) return 0;
+    if(!(mh=(MTMHEADER *)_mm_calloc(1,sizeof(MTMHEADER)))) return 0;
+
+    return 1;
+}
+
+
+void MTM_Cleanup(void)
+{
+    if(mtmtrk!=NULL) free(mtmtrk);
+    if(mh!=NULL) free(mh);
+
+    mtmtrk = NULL;
+    mh = NULL;
+}
+
+
+UBYTE *MTM_Convert(void)
+{
+    int t;
+    UBYTE a,b,c,inst,note,eff,dat;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {   a = mtmtrk[t].a;
+        b = mtmtrk[t].b;
+        c = mtmtrk[t].c;
+
+        inst = ((a&0x3)<<4)|(b>>4);
+        note = a>>2;
+
+        eff = b&0xf;
+        dat = c;
+
+        if(inst!=0)  UniInstrument(inst-1);
+        if(note!=0)  UniNote(note+24);
+
+        // mtm bug bugfix: when the effect is volslide,
+        // slide-up _always_ overrides slide-dn.
+
+        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();
+}
+
+
+BOOL MTM_Load(void)
+{
+    MTMSAMPLE s;
+    SAMPLE *q;
+
+    int t,u;
+
+    // try to read module header 
+
+    _mm_read_UBYTES(mh->id,3,modfp);
+    mh->version     =_mm_read_UBYTE(modfp);
+    _mm_read_string(mh->songname,20,modfp);
+    mh->numtracks   =_mm_read_I_UWORD(modfp);
+    mh->lastpattern =_mm_read_UBYTE(modfp);
+    mh->lastorder   =_mm_read_UBYTE(modfp);
+    mh->commentsize =_mm_read_I_UWORD(modfp);
+    mh->numsamples  =_mm_read_UBYTE(modfp);
+    mh->attribute   =_mm_read_UBYTE(modfp);
+    mh->beatspertrack=_mm_read_UBYTE(modfp);
+    mh->numchannels =_mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(mh->panpos,32,modfp);
+
+    if(feof(modfp))
+    {   _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); // make a cstr of songname
+    of.numpos    = mh->lastorder+1;         // copy the songlength
+    of.numpat    = mh->lastpattern+1;
+    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,modfp);
+        s.length    =_mm_read_I_ULONG(modfp);
+        s.reppos    =_mm_read_I_ULONG(modfp);
+        s.repend    =_mm_read_I_ULONG(modfp);
+        s.finetune  =_mm_read_UBYTE(modfp);
+        s.volume    =_mm_read_UBYTE(modfp);
+        s.attribute =_mm_read_UBYTE(modfp);
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_SAMPLEINFO; 
+            return 0;
+        }
+
+        q->samplename = DupStr(s.samplename,22);
+        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(modfp);
+    for(; t<128; t++)  _mm_read_UBYTE(modfp);
+
+    if(feof(modfp))
+    {   _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(modfp);
+            mtmtrk[s].b=_mm_read_UBYTE(modfp);
+            mtmtrk[s].c=_mm_read_UBYTE(modfp);
+        }
+
+        if(feof(modfp))
+        {   _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,modfp);
+
+        for(u=0; u<of.numchn; u++)
+            of.patterns[((long)t*of.numchn)+u]=pat[u];
+    }
+
+    // read comment field
+
+    if(!ReadComment(mh->commentsize)) return 0;
+
+    return 1;
+}
+
+
+CHAR *MTM_LoadTitle(void)
+{
+    CHAR s[20];
+
+    _mm_fseek(modfp,4,SEEK_SET);
+    if(!fread(s,20,1,modfp)) return NULL;
+ 
+    return(DupStr(s,20));
+}
+                
+
+MLOADER load_mtm =
+{   NULL,
+    "MTM",
+    "Portable MTM loader v0.1",
+    MTM_Init,
+    MTM_Test,
+    MTM_Load,
+    MTM_Cleanup,
+    MTM_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_s3m.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,466 @@
+/*
+
+ Name: LOAD_S3M.C
+
+ Description:
+ Screamtracker (S3M) module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+/**************************************************************************
+**************************************************************************/
+
+typedef struct S3MNOTE
+{   UBYTE note,ins,vol,cmd,inf;
+} S3MNOTE;
+
+typedef S3MNOTE S3MTRACK[64];
+
+
+// Raw S3M header struct:
+
+typedef struct S3MHEADER
+{   CHAR  songname[28];
+    UBYTE t1a;
+    UBYTE type;
+    UBYTE unused1[2];
+    UWORD ordnum;
+    UWORD insnum;
+    UWORD patnum;
+    UWORD flags;
+    UWORD tracker;
+    UWORD fileformat;
+    CHAR  scrm[4];
+    UBYTE mastervol;
+    UBYTE initspeed;
+    UBYTE inittempo;
+    UBYTE mastermult;
+    UBYTE ultraclick;
+    UBYTE pantable;
+    UBYTE unused2[8];
+    UWORD special;
+    UBYTE channels[32];
+} S3MHEADER;
+
+
+// Raw S3M sampleinfo struct:
+
+typedef struct S3MSAMPLE
+{   UBYTE type;
+    CHAR  filename[12];
+    UBYTE memsegh;
+    UWORD memsegl;
+    ULONG length;
+    ULONG loopbeg;
+    ULONG loopend;
+    UBYTE volume;
+    UBYTE dsk;
+    UBYTE pack;
+    UBYTE flags;
+    ULONG c2spd;
+    UBYTE unused[12];
+    CHAR  sampname[28];
+    CHAR  scrs[4];
+} S3MSAMPLE;
+
+/**************************************************************************
+**************************************************************************/
+
+
+extern UBYTE *poslookup;       // S3M/IT fix - removing blank patterns needs a
+                               // lookup table to fix position-jump commands
+extern SBYTE  remap[64];       // for removing empty channels
+
+static S3MNOTE   *s3mbuf  = NULL; // pointer to a complete S3M pattern
+static S3MHEADER *mh      = NULL;
+static UWORD     *paraptr = NULL; // parapointer array (see S3M docs)
+
+CHAR  S3M_Version[] = "Screamtracker 3.xx";
+
+BOOL S3M_Test(void)
+{
+    UBYTE id[4];
+    
+    _mm_fseek(modfp,0x2c,SEEK_SET);
+    if(!_mm_read_UBYTES(id,4,modfp)) return 0;
+    if(!memcmp(id,"SCRM",4)) return 1;
+    return 0;
+}
+
+BOOL S3M_Init(void)
+{
+    if(!(s3mbuf    = (S3MNOTE *)_mm_malloc(16*64*sizeof(S3MNOTE)))) return 0;
+    if(!(mh        = (S3MHEADER *)_mm_calloc(1,sizeof(S3MHEADER)))) return 0;
+    if(!(poslookup = (UBYTE *)_mm_malloc(sizeof(UBYTE)*128))) return 0;
+
+    return 1;
+}
+
+void S3M_Cleanup(void)
+{
+    if(s3mbuf!=NULL) free(s3mbuf);
+    if(paraptr!=NULL) free(paraptr);
+    if(poslookup!=NULL) free(poslookup);
+    if(mh!=NULL) free(mh);
+
+    paraptr   = NULL;
+    s3mbuf    = NULL;
+    poslookup = NULL;
+    mh        = NULL;
+}
+
+
+BOOL S3M_GetNumChannels(void)
+
+// Because so many s3m files have 16 channels as the set number used, but really
+// only use far less (usually 8 to 12 still), I had to make this function,
+// which determines the number of channels that are actually USED by a pattern.
+//
+// For every channel that's used, it sets the appropriate array entry of the
+// global varialbe 'isused'
+//
+// NOTE: You must first seek to the file location of the pattern before calling
+//       this procedure.
+// Returns 1 on fail.
+
+{
+    int row=0,flag,ch;
+
+    while(row<64)
+    {   flag = _mm_read_UBYTE(modfp);
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 1;
+        }
+
+        if(flag)
+        {   ch = flag&31;
+            if(mh->channels[ch] < 16) remap[ch] = 0;
+            
+            if(flag&32)
+            {   _mm_read_UBYTE(modfp);
+                _mm_read_UBYTE(modfp);
+            }
+
+            if(flag&64)
+                _mm_read_UBYTE(modfp);
+
+            if(flag&128)
+            {   _mm_read_UBYTE(modfp);
+                _mm_read_UBYTE(modfp);
+            }
+        } else row++;
+    }
+
+    return 0;
+}    
+
+
+BOOL S3M_ReadPattern(void)
+{
+    int row=0,flag,ch;
+    S3MNOTE *n;
+    S3MNOTE dummy;
+
+    // clear pattern data
+    memset(s3mbuf,255,16*64*sizeof(S3MNOTE));
+
+    while(row<64)
+    {   flag = _mm_read_UBYTE(modfp);
+
+        if(flag==EOF)
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 0;
+        }
+
+        if(flag)
+        {   ch = remap[flag&31];
+
+            if(ch != -1)
+                n = &s3mbuf[(64U*ch)+row];
+            else
+                n = &dummy;
+
+            if(flag&32)
+            {   n->note = _mm_read_UBYTE(modfp);
+                n->ins  = _mm_read_UBYTE(modfp);
+            }
+
+            if(flag&64)
+                n->vol = _mm_read_UBYTE(modfp);
+
+            if(flag&128)
+            {   n->cmd = _mm_read_UBYTE(modfp);
+                n->inf = _mm_read_UBYTE(modfp);
+            }
+        } else row++;
+    }
+    return 1;
+}
+
+
+void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, BOOL oldeffect);
+
+UBYTE *S3M_ConvertTrack(S3MNOTE *tr)
+{
+    int t;
+
+    UBYTE note,ins,vol;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {
+        note = tr[t].note;
+        ins  = tr[t].ins;
+        vol  = tr[t].vol;
+
+
+        if(ins!=0 && ins!=255) UniInstrument(ins-1);
+        if(note!=255)
+        {   if(note==254) UniPTEffect(0xc,0);             // <- note off command
+            else UniNote(((note>>4)*12)+(note&0xf));      // <- normal note
+        }
+
+        if(vol<255)
+            UniPTEffect(0xc,vol);
+
+        S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf,1);
+        UniNewline();
+    }
+
+    return UniDup();
+}
+
+
+BOOL S3M_Load(void)
+{
+    int    t,u,track = 0;
+    SAMPLE *q;
+    UBYTE  pan[32];
+
+    // try to read module header
+
+    _mm_read_string(mh->songname,28,modfp);
+    mh->t1a         =_mm_read_UBYTE(modfp);
+    mh->type        =_mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(mh->unused1,2,modfp);
+    mh->ordnum      =_mm_read_I_UWORD(modfp);
+    mh->insnum      =_mm_read_I_UWORD(modfp);
+    mh->patnum      =_mm_read_I_UWORD(modfp);
+    mh->flags       =_mm_read_I_UWORD(modfp);
+    mh->tracker     =_mm_read_I_UWORD(modfp);
+    mh->fileformat  =_mm_read_I_UWORD(modfp);
+    _mm_read_string(mh->scrm,4,modfp);
+
+    mh->mastervol   =_mm_read_UBYTE(modfp);
+    mh->initspeed   =_mm_read_UBYTE(modfp);
+    mh->inittempo   =_mm_read_UBYTE(modfp);
+    mh->mastermult  =_mm_read_UBYTE(modfp);
+    mh->ultraclick  =_mm_read_UBYTE(modfp);
+    mh->pantable    =_mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(mh->unused2,8,modfp);
+    mh->special     =_mm_read_I_UWORD(modfp);
+    _mm_read_UBYTES(mh->channels,32,modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // set module variables
+
+    of.modtype     = strdup(S3M_Version);
+    of.modtype[14] = ((mh->tracker >> 8) &0xf) + 0x30;
+    of.modtype[16] = ((mh->tracker >> 4)&0xf) + 0x30;
+    of.modtype[17] = ((mh->tracker)&0xf) + 0x30;
+    of.songname    = DupStr(mh->songname,28);
+    of.numpat      = mh->patnum;
+    of.reppos      = 0;
+    of.numins      = of.numsmp = mh->insnum;
+    of.initspeed   = mh->initspeed;
+    of.inittempo   = mh->inittempo;
+    of.initvolume  = mh->mastervol<<1;
+
+    // read the order data
+    if(!AllocPositions(mh->ordnum)) return 0;
+    for(t=0; t<mh->ordnum; t++)
+        of.positions[t] = _mm_read_UBYTE(modfp);
+
+    of.numpos = 0;
+    for(t=0; t<mh->ordnum; t++)
+    {   of.positions[of.numpos] = of.positions[t];
+        poslookup[t]            = of.numpos;   // bug fix for FREAKY S3Ms
+        if(of.positions[t]<254) of.numpos++;        
+    }
+
+    if((paraptr=(UWORD *)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0;
+
+    // read the instrument+pattern parapointers
+    _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp);
+
+
+    if(mh->pantable==252)
+    {   // read the panning table (ST 3.2 addition.  See below for further
+        // portions of channel panning [past reampper]).
+        _mm_read_UBYTES(pan,32,modfp);
+    }
+
+
+    // now is a good time to check if the header was too short :)
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+
+    // ==============================================
+    // Load those darned Samples!  (no insts in ST3)
+
+    if(!AllocSamples()) return 0;
+
+    q = of.samples;
+
+    for(t=0; t<of.numins; t++)
+    {   S3MSAMPLE s;
+
+        // seek to instrument position
+
+        _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET);
+
+        // and load sample info
+
+        s.type      =_mm_read_UBYTE(modfp);
+        _mm_read_string(s.filename,12,modfp);
+        s.memsegh   =_mm_read_UBYTE(modfp);
+        s.memsegl   =_mm_read_I_UWORD(modfp);
+        s.length    =_mm_read_I_ULONG(modfp);
+        s.loopbeg   =_mm_read_I_ULONG(modfp);
+        s.loopend   =_mm_read_I_ULONG(modfp);
+        s.volume    =_mm_read_UBYTE(modfp);
+        s.dsk       =_mm_read_UBYTE(modfp);
+        s.pack      =_mm_read_UBYTE(modfp);
+        s.flags     =_mm_read_UBYTE(modfp);
+        s.c2spd     =_mm_read_I_ULONG(modfp);
+        _mm_read_UBYTES(s.unused,12,modfp);
+        _mm_read_string(s.sampname,28,modfp);
+        _mm_read_string(s.scrs,4,modfp);
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+            return 0;
+        }
+
+        q->samplename = DupStr(s.sampname,28);
+        q->speed      = s.c2spd;
+        q->length     = s.length;
+        q->loopstart  = s.loopbeg;
+        q->loopend    = s.loopend;
+        q->volume     = s.volume;
+        q->seekpos    = (((long)s.memsegh)<<16|s.memsegl)<<4;
+
+        if(s.flags&1) q->flags |= SF_LOOP;
+        if(s.flags&4) q->flags |= SF_16BITS;
+        if(mh->fileformat==1) q->flags |= SF_SIGNED;
+
+        // DON'T load sample if it doesn't have the SCRS tag
+        if(memcmp(s.scrs,"SCRS",4)!=0) q->length = 0;
+
+        q++;
+    }
+
+    // ====================================
+    // Determine the number of channels actually used.  (what ever happened
+    // to the concept of a single "numchn" variable, eh?!
+
+    of.numchn = 0;
+    memset(remap,-1,32*sizeof(UBYTE));
+
+    for(t=0; t<of.numpat; t++)
+    {   // seek to pattern position ( + 2 skip pattern length )
+        _mm_fseek(modfp,(long)((paraptr[of.numins+t])<<4)+2,SEEK_SET);
+        if(S3M_GetNumChannels()) return 0;
+    }
+    
+    // build the remap array 
+    for(t=0; t<32; t++)
+    {   if(remap[t]==0)
+        {   remap[t] = of.numchn;
+            of.numchn++;
+        }
+    }
+
+    // ============================================================
+    // set panning positions AFTER building remap chart!
+
+    for(t=0; t<32; t++)
+    {   if((mh->channels[t]<16) && (remap[t]!=-1))
+        {   if(mh->channels[t]<8)
+                of.panning[remap[t]] = 0x20;     // 0x30 = std s3m val
+            else
+                of.panning[remap[t]] = 0xd0;     // 0xc0 = std s3m val
+        }
+    }
+
+    if(mh->pantable==252)
+    {   // set panning positions according to panning table (new for st3.2)
+        for(t=0; t<32; t++)
+        {   if((pan[t]&0x20) && (mh->channels[t]<16) && (remap[t]!=-1))
+                of.panning[remap[t]] = (pan[t]&0xf)<<4;
+        }
+    }
+
+
+    // ==============================
+    // Load the pattern info now!
+    
+    of.numtrk = of.numpat*of.numchn;
+    if(!AllocTracks()) return 0;
+    if(!AllocPatterns()) return 0;
+
+    for(t=0; t<of.numpat; t++)
+    {   // seek to pattern position ( + 2 skip pattern length )
+        _mm_fseek(modfp,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET);
+        if(!S3M_ReadPattern()) return 0;
+        for(u=0; u<of.numchn; u++)
+            if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0;
+    }
+
+    return 1;
+}
+
+         
+CHAR *S3M_LoadTitle(void)
+{
+   CHAR s[28];
+
+   _mm_fseek(modfp,0,SEEK_SET);
+   if(!fread(s,28,1,modfp)) return NULL;
+   
+   return(DupStr(s,28));
+}
+
+
+MLOADER load_s3m =
+{   NULL,
+    "S3M",
+    "S3M loader v0.3",
+    S3M_Init,
+    S3M_Test,
+    S3M_Load,
+    S3M_Cleanup,
+
+    S3M_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_stm.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,374 @@
+/*
+
+ Name: LOAD_STM.C
+
+ Description:
+ ScreamTracker 2 (STM) module Loader - Version 1.oOo Release 2 
+ A Coding Nightmare by Rao and Air Richter of HaRDCoDE
+ You can now play all of those wonderful old C.C. Catch STM's!
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author). 
+
+*/
+
+#include <string.h>
+#include <ctype.h>
+#include "mikmod.h"
+
+
+typedef struct STMNOTE
+{   UBYTE note,insvol,volcmd,cmdinf;
+} STMNOTE;
+
+
+// Raw STM sampleinfo struct:
+
+typedef struct STMSAMPLE
+{  CHAR  filename[12];   // Can't have long comments - just filename comments :)
+   UBYTE unused;         // 0x00
+   UBYTE instdisk;       // Instrument disk
+   UWORD reserved;       // ISA in memory when in ST 2
+   UWORD length;         // Sample length
+   UWORD loopbeg;        // Loop start point
+   UWORD loopend;        // Loop end point
+   UBYTE volume;         // Volume
+   UBYTE reserved2;      // More reserved crap
+   UWORD c2spd;          // Good old c2spd
+   UBYTE reserved3[4];   // Yet more of PSi's reserved crap
+   UWORD isa;            // Internal Segment Address ->
+                         //    contrary to the tech specs, this is NOT actually
+                         //    written to the stm file.
+} STMSAMPLE;
+
+// Raw STM header struct:
+
+typedef struct STMHEADER
+{  CHAR  songname[20];
+   CHAR  trackername[8];   // !SCREAM! for ST 2.xx 
+   UBYTE unused;           // 0x1A 
+   UBYTE filetype;         // 1=song, 2=module (only 2 is supported, of course) :) 
+   UBYTE ver_major;        // Like 2 
+   UBYTE ver_minor;        // "ditto" 
+   UBYTE inittempo;        // initspeed= stm inittempo>>4 
+   UBYTE  numpat;          // number of patterns 
+   UBYTE   globalvol;      // <- WoW! a RiGHT TRiANGLE =8*) 
+   UBYTE    reserved[13];  // More of PSi's internal crap 
+   STMSAMPLE sample[31];   // STM sample data
+   UBYTE patorder[128];    // Docs say 64 - actually 128
+} STMHEADER;
+
+
+static STMNOTE *stmbuf = NULL;
+static STMHEADER *mh = NULL;
+
+static CHAR  STM_Version[] = "Screamtracker 2";
+
+
+BOOL STM_Test(void)
+{
+   UBYTE str[9],filetype;
+
+   _mm_fseek(modfp,21,SEEK_SET);
+   _mm_read_UBYTES(str,9,modfp);
+   filetype = _mm_read_UBYTE(modfp);
+   if(!memcmp(str,"!SCREAM!",8) || (filetype!=2)) // STM Module = filetype 2
+      return 0;
+   return 1;
+}
+
+
+
+BOOL STM_Init(void)
+{
+    if(!(mh=(STMHEADER *)_mm_calloc(1,sizeof(STMHEADER)))) return 0;
+    if(!(stmbuf=(STMNOTE *)_mm_calloc(64U*of.numchn,sizeof(STMNOTE)))) return 0;
+
+    return 1;
+}
+
+void STM_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    if(stmbuf!=NULL) free(stmbuf);
+
+    stmbuf = NULL;
+    mh = NULL;
+}
+
+
+
+void STM_ConvertNote(STMNOTE *n)
+{
+    UBYTE note,ins,vol,cmd,inf;
+
+    // extract the various information from the 4 bytes that
+    //  make up a single note
+
+    note = n->note;
+    ins  = n->insvol>>3;
+    vol  = (n->insvol&7)+(n->volcmd>>1);
+    cmd  = n->volcmd&15;
+    inf  = n->cmdinf;
+
+    if(ins!=0 && ins<32)  UniInstrument(ins-1);
+
+  // special values of [SBYTE0] are handled here ->
+  // we have no idea if these strange values will ever be encountered.
+  // but it appears as though stms sound correct.
+  if(note==254 || note==252) UniPTEffect(0xc,0); // <- note off command (???)
+     else
+  // if note < 251, then all three bytes are stored in the file
+  if(note<251) UniNote((((note>>4)+2)*12)+(note&0xf));      // <- normal note and up the octave by two
+
+    if(vol<65)
+        UniPTEffect(0xc,vol);
+
+    if(cmd!=255){
+        switch(cmd){
+
+            case 1:                 // Axx set speed to xx and add 0x1c to fix StoOoPiD STM 2.x
+                UniPTEffect(0xf,inf>>4);
+                break;
+
+            case 2:                 // Bxx position jump
+                UniPTEffect(0xb,inf);
+                break;
+
+            case 3:                 // Cxx patternbreak to row xx
+                UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));
+                break;
+
+            case 4:                 // Dxy volumeslide
+                UniWrite(UNI_S3MEFFECTD);
+                UniWrite(inf);
+                break;
+
+            case 5:                 // Exy toneslide down
+                UniWrite(UNI_S3MEFFECTE);
+                UniWrite(inf);
+                break;
+
+            case 6:                 // Fxy toneslide up
+                UniWrite(UNI_S3MEFFECTF);
+                UniWrite(inf);
+                break;
+
+            case 7:                 // Gxx Tone portamento,speed xx
+                UniPTEffect(0x3,inf);
+                break;
+
+            case 8:                 // Hxy vibrato
+                UniPTEffect(0x4,inf);
+                break;
+
+            case 9:                 // Ixy tremor, ontime x, offtime y
+                UniWrite(UNI_S3MEFFECTI);
+                UniWrite(inf);
+                break;
+
+            case 0xa:               // Jxy arpeggio
+                UniPTEffect(0x0,inf);
+                break;
+
+            case 0xb:               // Kxy Dual command H00 & Dxy
+                UniPTEffect(0x4,0);
+                UniWrite(UNI_S3MEFFECTD);
+                UniWrite(inf);
+                break;
+
+            case 0xc:               // Lxy Dual command G00 & Dxy
+                UniPTEffect(0x3,0);
+                UniWrite(UNI_S3MEFFECTD);
+                UniWrite(inf);
+                break;
+
+         // Support all these above, since ST2 can LOAD these values
+         // but can actually only play up to J - and J is only
+         // half-way implemented in ST2
+
+            case 0x18:      // Xxx amiga command 8xx - What the hell, support panning. :)
+                UniPTEffect(0x8,inf);
+                break;
+        }
+    }
+}
+
+
+UBYTE *STM_ConvertTrack(STMNOTE *n)
+{
+    int t;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {   STM_ConvertNote(n);
+        UniNewline();
+        n+=of.numchn;
+    }
+    return UniDup();
+}
+
+
+BOOL STM_LoadPatterns(void)
+{
+    int t,s,tracks=0;
+
+    if(!AllocPatterns()) return 0;
+    if(!AllocTracks()) return 0;
+
+    // Allocate temporary buffer for loading
+    // and converting the patterns
+
+    for(t=0; t<of.numpat; t++)
+    {   for(s=0; s<(64U*of.numchn); s++)
+        {   stmbuf[s].note   = _mm_read_UBYTE(modfp);
+            stmbuf[s].insvol = _mm_read_UBYTE(modfp);
+            stmbuf[s].volcmd = _mm_read_UBYTE(modfp);
+            stmbuf[s].cmdinf = _mm_read_UBYTE(modfp);
+        }
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 0;
+        }
+
+        for(s=0;s<of.numchn;s++)
+        {   if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+
+BOOL STM_Load(void)
+{
+    int        t; 
+    ULONG      MikMod_ISA; // We MUST generate our own ISA - NOT stored in the stm
+    SAMPLE     *q;
+
+    // try to read stm header
+
+    _mm_read_string(mh->songname,20,modfp);
+    _mm_read_string(mh->trackername,8,modfp);
+    mh->unused      =_mm_read_UBYTE(modfp);
+    mh->filetype    =_mm_read_UBYTE(modfp);
+    mh->ver_major   =_mm_read_UBYTE(modfp);
+    mh->ver_minor   =_mm_read_UBYTE(modfp);
+    mh->inittempo   =_mm_read_UBYTE(modfp);
+    mh->numpat      =_mm_read_UBYTE(modfp);
+    mh->globalvol   =_mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(mh->reserved,13,modfp);
+
+    for(t=0;t<31;t++)
+    {   STMSAMPLE *s = &mh->sample[t];  // STM sample data
+
+        _mm_read_string(s->filename,12,modfp);
+        s->unused   =_mm_read_UBYTE(modfp);
+        s->instdisk =_mm_read_UBYTE(modfp);
+        s->reserved =_mm_read_I_UWORD(modfp);
+        s->length   =_mm_read_I_UWORD(modfp);
+        s->loopbeg  =_mm_read_I_UWORD(modfp);
+        s->loopend  =_mm_read_I_UWORD(modfp);
+        s->volume   =_mm_read_UBYTE(modfp);
+        s->reserved2=_mm_read_UBYTE(modfp);
+        s->c2spd    =_mm_read_I_UWORD(modfp);
+        _mm_read_UBYTES(s->reserved3,4,modfp);
+        s->isa      =_mm_read_I_UWORD(modfp);
+    }
+    _mm_read_UBYTES(mh->patorder,128,modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // set module variables
+
+    of.modtype   = strdup(STM_Version);
+    of.songname  = DupStr(mh->songname,20); // make a cstr of songname
+    of.numpat    = mh->numpat;
+    of.inittempo = 125;                     // mh->inittempo+0x1c;
+    of.initspeed = mh->inittempo>>4;
+    of.numchn    = 4;                       // get number of channels
+
+    t=0;
+    if(!AllocPositions(0x80)) return 0;
+    while(mh->patorder[t]!=99)    // 99 terminates the patorder list
+    {   of.positions[t] = mh->patorder[t];
+        t++;
+    }
+    of.numpos = --t;
+    of.numtrk = of.numpat*of.numchn;
+
+    // Finally, init the sampleinfo structures
+    of.numins = of.numsmp = 31;    // always this
+
+    if(!AllocSamples()) return 0;
+    if(!STM_LoadPatterns()) return 0;
+
+    q = of.samples;
+
+    MikMod_ISA = _mm_ftell(modfp);
+    MikMod_ISA = (MikMod_ISA+15)&0xfffffff0;
+
+    for(t=0; t<of.numins; t++)
+    {   // load sample info
+
+        q->samplename = DupStr(mh->sample[t].filename,12);
+        q->speed      = mh->sample[t].c2spd;
+        q->volume     = mh->sample[t].volume;
+        q->length     = mh->sample[t].length;
+        if (!mh->sample[t].volume || q->length==1 ) q->length = 0; // if vol = 0 or length = 1, then no sample
+        q->loopstart  = mh->sample[t].loopbeg;
+        q->loopend    = mh->sample[t].loopend;
+        q->seekpos    = MikMod_ISA;
+
+        MikMod_ISA+=q->length;
+        MikMod_ISA = (MikMod_ISA+15)&0xfffffff0;
+
+        // Once again, contrary to the STM specs, all the sample data is
+        // actually SIGNED! Sheesh
+
+        q->flags = SF_SIGNED;
+
+        if(mh->sample[t].loopend>0 && mh->sample[t].loopend!=0xffff) q->flags|=SF_LOOP;
+
+        // fix replen if repend>length
+
+        if(q->loopend > q->length) q->loopend = q->length;
+
+        q++;
+    }
+
+    return 1;
+}
+
+
+CHAR *STM_LoadTitle(void)
+{
+    CHAR s[20];
+
+    _mm_fseek(modfp,0,SEEK_SET);
+    if(!fread(s,20,1,modfp)) return NULL;
+
+    return(DupStr(s,20));
+}
+
+
+MLOADER load_stm =
+{   NULL,
+    "STM",
+    "Portable STM Loader - V 1.2",
+    STM_Init,
+    STM_Test,
+    STM_Load,
+    STM_Cleanup,
+    STM_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_ult.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,292 @@
+/*
+
+ Name:  LOAD_ULT.C
+
+ Description:
+ Ultratracker (ULT) module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+#define ULTS_16BITS     4
+#define ULTS_LOOP       8
+#define ULTS_REVERSE    16
+
+
+// Raw ULT header struct:
+
+typedef struct ULTHEADER
+{   CHAR  id[16];
+    CHAR  songtitle[32];
+    UBYTE reserved;
+} ULTHEADER;
+
+
+// Raw ULT sampleinfo struct:
+
+typedef struct ULTSAMPLE
+{   CHAR   samplename[32];
+    CHAR   dosname[12];
+    SLONG  loopstart;
+    SLONG  loopend;
+    SLONG  sizestart;
+    SLONG  sizeend;
+    UBYTE  volume;
+    UBYTE  flags;
+    SWORD  finetune;
+} ULTSAMPLE;
+
+
+typedef struct ULTEVENT
+{   UBYTE note,sample,eff,dat1,dat2;
+} ULTEVENT;
+
+
+CHAR *ULT_Version[]=
+{   "Ultra Tracker V1.3",
+    "Ultra Tracker V1.4",
+    "Ultra Tracker V1.5",
+    "Ultra Tracker V1.6"
+};
+
+
+BOOL ULT_Test(void)
+{
+    CHAR id[16];
+
+    if(!_mm_read_string(id,15,modfp)) return 0;
+    return(!strncmp(id,"MAS_UTrack_V00",14));
+}
+
+
+BOOL ULT_Init(void)
+{
+    return 1;
+}
+
+
+void ULT_Cleanup(void)
+{
+}
+
+ULTEVENT ev;
+
+
+
+int ReadUltEvent(ULTEVENT *event)
+{
+    UBYTE flag,rep=1;
+
+    flag = _mm_read_UBYTE(modfp);
+
+    if(flag==0xfc)
+    {   rep = _mm_read_UBYTE(modfp);
+        event->note =_mm_read_UBYTE(modfp);
+    } else
+        event->note = flag;
+
+    event->sample   =_mm_read_UBYTE(modfp);
+    event->eff      =_mm_read_UBYTE(modfp);
+    event->dat1     =_mm_read_UBYTE(modfp);
+    event->dat2     =_mm_read_UBYTE(modfp);
+
+    return rep;
+}
+
+
+BOOL ULT_Load(void)
+{
+    int        t,u,tracks=0;
+    SAMPLE     *q;
+    ULTSAMPLE  s;
+    ULTHEADER  mh;
+    UBYTE      nos,noc,nop;
+
+    // try to read module header
+
+    _mm_read_string(mh.id,15,modfp);
+    _mm_read_string(mh.songtitle,32,modfp);
+    mh.reserved = _mm_read_UBYTE(modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    if(mh.id[14]<'1' || mh.id[14]>'4')
+    {   _mm_errno = MMERR_NOT_A_MODULE;
+        return 0;
+    }
+
+    of.modtype   = strdup(ULT_Version[mh.id[14]-'1']);
+    of.initspeed = 6;
+    of.inittempo = 125;
+
+    // read songtext
+
+    if(!ReadComment((UWORD)mh.reserved*32)) return 0;
+
+    nos = _mm_read_UBYTE(modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    of.songname = DupStr(mh.songtitle,32);
+    of.numins   = nos;
+
+    if(!AllocSamples()) return 0;
+    
+    q = of.samples;
+
+    for(t=0; t<nos; t++)
+    {   // try to read sample info
+
+        _mm_read_string(s.samplename,32,modfp);
+        _mm_read_string(s.dosname,12,modfp);
+        s.loopstart     =_mm_read_I_ULONG(modfp);
+        s.loopend       =_mm_read_I_ULONG(modfp);
+        s.sizestart     =_mm_read_I_ULONG(modfp);
+        s.sizeend       =_mm_read_I_ULONG(modfp);
+        s.volume        =_mm_read_UBYTE(modfp);
+        s.flags         =_mm_read_UBYTE(modfp);
+        s.finetune      =_mm_read_I_SWORD(modfp);
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+            return 0;
+        }
+
+        q->samplename = DupStr(s.samplename,32);
+        q->speed      = 8363;
+
+        if(mh.id[14]>='4')
+        {   _mm_read_I_UWORD(modfp);       // read 1.6 extra info(??) word
+            q->speed=s.finetune;
+        }
+
+        q->length    = s.sizeend-s.sizestart;
+        q->volume    = s.volume>>2;
+        q->loopstart = s.loopstart;
+        q->loopend   = s.loopend;
+
+        q->flags = SF_SIGNED;
+
+        if(s.flags&ULTS_LOOP)
+            q->flags|=SF_LOOP;
+
+        if(s.flags&ULTS_16BITS)
+        {   q->flags|=SF_16BITS;
+            q->loopstart>>=1;
+            q->loopend>>=1;
+        }
+
+        q++;
+    }
+
+    if(!AllocPositions(256)) return 0;
+    for(t=0; t<256; t++)
+        of.positions[t] = _mm_read_UBYTE(modfp);
+    for(t=0; t<256; t++)
+        if(of.positions[t]==255) break;
+
+    of.numpos = t;
+
+    noc = _mm_read_UBYTE(modfp);
+    nop = _mm_read_UBYTE(modfp);
+
+    of.numchn = noc+1;
+    of.numpat = nop+1;
+    of.numtrk = of.numchn*of.numpat;
+
+    if(!AllocTracks()) return 0;
+    if(!AllocPatterns()) return 0;
+
+    for(u=0; u<of.numchn; u++)
+    {   for(t=0; t<of.numpat; t++)
+            of.patterns[(t*of.numchn)+u]=tracks++;
+    }
+
+    // read pan position table for v1.5 and higher
+
+    if(mh.id[14]>='3')
+        for(t=0; t<of.numchn; t++) of.panning[t]=_mm_read_UBYTE(modfp)<<4;
+
+    for(t=0; t<of.numtrk; t++)
+    {   int rep,s,done;
+
+        UniReset();
+        done=0;
+
+        while(done<64)
+        {   rep=ReadUltEvent(&ev);
+
+            if(feof(modfp))
+            {   _mm_errno = MMERR_LOADING_TRACK;
+                return 0;
+            }
+
+            for(s=0; s<rep; s++)
+            {   UBYTE eff;
+
+                if(ev.sample) UniInstrument(ev.sample-1);
+                if(ev.note)   UniNote(ev.note+23);
+
+                eff = ev.eff>>4;
+
+                //  ULT panning effect fixed by Alexander Kerkhove :
+
+                if(eff==0xc) UniPTEffect(eff,ev.dat2>>2);
+                else if(eff==0xb) UniPTEffect(8,ev.dat2*0xf);
+                else UniPTEffect(eff,ev.dat2);
+
+                eff=ev.eff&0xf;
+
+                if(eff==0xc) UniPTEffect(eff,ev.dat1>>2);
+                else if(eff==0xb) UniPTEffect(8,ev.dat1*0xf);
+                else UniPTEffect(eff,ev.dat1);
+
+                UniNewline();
+                done++;
+            }
+        }
+        if(!(of.tracks[t]=UniDup())) return 0;
+    }
+
+    return 1;
+}
+
+
+CHAR *ULT_LoadTitle(void)
+{
+   CHAR s[32];
+
+   _mm_fseek(modfp,15,SEEK_SET);
+   if(!fread(s,32,1,modfp)) return NULL;
+   
+   return(DupStr(s,32));
+}
+
+
+MLOADER load_ult =
+{   NULL,
+    "ULT",
+    "Portable ULT loader v0.1",
+    ULT_Init,
+    ULT_Test,
+    ULT_Load,
+    ULT_Cleanup,
+    ULT_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_uni.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,229 @@
+/*
+
+Name: LOAD_UNI.C
+
+Description:
+UNIMOD (mikmod's internal format) module loader.
+Currently only supports UniMOD 06 - the internal format for MikMod 3.0.
+
+Portability:
+All systems - all compilers (hopefully)
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+BOOL UNI_Test(void)
+{
+    UBYTE id[4];
+
+    _mm_read_UBYTES(id,4,modfp);
+    if(!memcmp(id, "UN06", 4)) return 1;
+
+    return 0;
+}
+
+
+BOOL UNI_Init(void)
+{
+    return 1;
+}
+
+
+void UNI_Cleanup(void)
+{
+}
+
+
+UBYTE *TrkRead(void)
+{
+    UBYTE  *t;
+    UWORD  len;
+
+    len = _mm_read_M_UWORD(modfp);
+    t   = (UBYTE *)malloc(len);
+    _mm_read_UBYTES(t,len,modfp);
+
+    return t;
+}
+
+
+BOOL UNI_Load(void)
+{
+    int t,v,w;
+    INSTRUMENT *i;
+    SAMPLE *s;
+
+    // UNI format version 3.0 (#6)
+    
+    of.modtype    = strdup("MikMod UniFormat 3.0");
+
+    _mm_fseek(modfp,5,SEEK_SET);    // skip the header
+    
+    of.flags      = _mm_read_M_UWORD(modfp);
+    of.numchn     = _mm_read_UBYTE(modfp);
+    of.numvoices  = _mm_read_UBYTE(modfp);
+    of.numpos     = _mm_read_M_UWORD(modfp);
+    of.numpat     = _mm_read_M_UWORD(modfp);
+    of.numtrk     = _mm_read_M_UWORD(modfp);
+    of.numins     = _mm_read_M_UWORD(modfp);
+    of.numsmp     = _mm_read_M_UWORD(modfp);
+    of.reppos     = _mm_read_M_UWORD(modfp);
+    of.initspeed  = _mm_read_UBYTE(modfp);
+    of.inittempo  = _mm_read_UBYTE(modfp);
+    of.initvolume = _mm_read_UBYTE(modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    of.songname = StringRead(modfp);
+    of.composer = StringRead(modfp);
+    of.comment  = StringRead(modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    if(!AllocSamples())            return 0;
+    if(!AllocTracks())             return 0;
+    if(!AllocPatterns())           return 0;
+    if(!AllocPositions(of.numpos)) return 0;
+
+    _mm_read_UBYTES(of.positions,of.numpos,modfp);
+    _mm_read_M_UWORDS(of.panning,of.numchn,modfp);
+    _mm_read_UBYTES(of.chanvol,of.numchn,modfp);
+
+
+    // Load sample headers
+
+    s = of.samples;
+    for(v=0; v<of.numsmp; v++, s++)
+    {   s->flags      = _mm_read_M_UWORD(modfp);
+        s->speed      = _mm_read_M_ULONG(modfp);
+        s->volume     = _mm_read_UBYTE(modfp);
+        s->panning    = _mm_read_M_UWORD(modfp);
+        s->length     = _mm_read_M_ULONG(modfp);
+        s->loopstart  = _mm_read_M_ULONG(modfp);
+        s->loopend    = _mm_read_M_ULONG(modfp);
+        s->susbegin   = _mm_read_M_ULONG(modfp);
+        s->susend     = _mm_read_M_ULONG(modfp);
+
+        s->globvol    = _mm_read_UBYTE(modfp);
+        s->vibflags   = _mm_read_UBYTE(modfp);
+        s->vibtype    = _mm_read_UBYTE(modfp);
+        s->vibsweep   = _mm_read_UBYTE(modfp);
+        s->vibdepth   = _mm_read_UBYTE(modfp);
+        s->vibrate    = _mm_read_UBYTE(modfp);
+        s->samplename = StringRead(modfp);
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_HEADER;
+            return 0;
+        }
+    }
+
+    // Load instruments
+
+    if(of.flags & UF_INST)
+    {   if(!AllocInstruments()) return 0;
+        i = of.instruments;
+
+        for(v=0; v<of.numins; v++, i++)
+        {   i->flags        = _mm_read_UBYTE(modfp);
+            i->nnatype      = _mm_read_UBYTE(modfp);
+            i->dca          = _mm_read_UBYTE(modfp);
+            i->dct          = _mm_read_UBYTE(modfp);
+            i->globvol      = _mm_read_UBYTE(modfp);
+            i->panning      = _mm_read_M_UWORD(modfp);
+            i->pitpansep    = _mm_read_UBYTE(modfp);
+            i->pitpancenter = _mm_read_UBYTE(modfp);
+            i->rvolvar      = _mm_read_UBYTE(modfp);
+            i->rpanvar      = _mm_read_UBYTE(modfp);
+
+            i->volfade      = _mm_read_M_UWORD(modfp);
+    
+            i->volflg       = _mm_read_UBYTE(modfp);
+            i->volpts       = _mm_read_UBYTE(modfp);
+            i->volsusbeg    = _mm_read_UBYTE(modfp);
+            i->volsusend    = _mm_read_UBYTE(modfp);
+            i->volbeg       = _mm_read_UBYTE(modfp);
+            i->volend       = _mm_read_UBYTE(modfp);
+    
+            for(w=0; w<i->volpts; w++)
+            {   i->volenv[w].pos = _mm_read_M_SWORD(modfp);
+                i->volenv[w].val = _mm_read_M_SWORD(modfp);
+            }
+    
+            i->panflg    = _mm_read_UBYTE(modfp);
+            i->panpts    = _mm_read_UBYTE(modfp);
+            i->pansusbeg = _mm_read_UBYTE(modfp);
+            i->pansusend = _mm_read_UBYTE(modfp);
+            i->panbeg    = _mm_read_UBYTE(modfp);
+            i->panend    = _mm_read_UBYTE(modfp);
+    
+            for(w=0; w<i->panpts; w++)
+            {   i->panenv[w].pos = _mm_read_M_SWORD(modfp);
+                i->panenv[w].val = _mm_read_M_SWORD(modfp);
+            }
+    
+            i->pitflg    = _mm_read_UBYTE(modfp);
+            i->pitpts    = _mm_read_UBYTE(modfp);
+            i->pitsusbeg = _mm_read_UBYTE(modfp);
+            i->pitsusend = _mm_read_UBYTE(modfp);
+            i->pitbeg    = _mm_read_UBYTE(modfp);
+            i->pitend    = _mm_read_UBYTE(modfp);
+    
+            for(w=0; w<i->pitpts; w++)
+            {   i->pitenv[w].pos = _mm_read_M_SWORD(modfp);
+                i->pitenv[w].val = _mm_read_M_SWORD(modfp);
+            }
+    
+            _mm_read_UBYTES(i->samplenumber, 120, modfp);
+            _mm_read_UBYTES(i->samplenote, 120, modfp);
+    
+            i->insname = StringRead(modfp);
+
+            if(feof(modfp))
+            {   _mm_errno = MMERR_LOADING_HEADER;
+                return 0;
+            }
+        }
+    }
+
+    // Read patterns
+
+    _mm_read_M_UWORDS(of.pattrows,of.numpat,modfp);
+    _mm_read_M_UWORDS(of.patterns,of.numpat*of.numchn,modfp);
+
+    // Read tracks
+
+    for(t=0; t<of.numtrk; t++)
+        of.tracks[t] = TrkRead();
+
+    return 1;
+}
+
+
+CHAR *UNI_LoadTitle(void)
+{
+    _mm_fseek(modfp,24,SEEK_SET);
+    return(StringRead(modfp));
+}
+
+
+MLOADER load_uni =
+{   NULL,
+    "UNI",
+    "Portable UniFormat 3.0 Loader",
+    UNI_Init,
+    UNI_Test,
+    UNI_Load,
+    UNI_Cleanup,
+    UNI_LoadTitle
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_wav.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,97 @@
+/*
+
+ Name:  LOAD_WAV.C
+
+ Description:
+ WAV Streaming Audio Loader / Player
+
+ Portability:
+ All compilers -- All systems (hopefully)
+
+*/
+
+#include "mikmod.h"
+#include <string.h>
+
+
+typedef struct
+{   CHAR  rID[4];
+    ULONG rLen;
+    CHAR  wID[4];
+    CHAR  fID[4];
+    ULONG fLen;
+    UWORD wFormatTag;
+    UWORD nChannels;
+    ULONG nSamplesPerSec;
+    ULONG nAvgBytesPerSec;
+    UWORD nBlockAlign;
+    UWORD nFormatSpecific;
+} WAV;
+
+
+BOOL WAV_Load(void)
+{
+    SAMPLE     *si;
+    static WAV  wh;
+    static CHAR dID[4];
+
+    // read wav header
+
+    _mm_read_string(wh.rID,4,stream_fp);
+    wh.rLen            = _mm_read_I_ULONG(stream_fp);
+    _mm_read_string(wh.wID,4,stream_fp);
+    _mm_read_string(wh.fID,4,stream_fp);
+    wh.fLen            = _mm_read_I_ULONG(stream_fp);
+    wh.wFormatTag      = _mm_read_I_UWORD(stream_fp);
+    wh.nChannels       = _mm_read_I_UWORD(stream_fp);
+    wh.nSamplesPerSec  = _mm_read_I_ULONG(stream_fp);
+    wh.nAvgBytesPerSec = _mm_read_I_ULONG(stream_fp);
+    wh.nBlockAlign     = _mm_read_I_UWORD(stream_fp);
+    wh.nFormatSpecific = _mm_read_I_UWORD(stream_fp);
+
+    // check it
+
+    if( feof(stream_fp) ||
+        memcmp(wh.rID,"RIFF",4) ||
+        memcmp(wh.wID,"WAVE",4) ||
+        memcmp(wh.fID,"fmt ",4) )
+    {
+        _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+    // skip other crap
+
+    _mm_fseek(stream_fp,wh.fLen-16,SEEK_CUR);
+    _mm_read_string(dID,4,stream_fp);
+
+    if( memcmp(dID,"data",4) )
+    {   _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+    if(wh.nChannels > 1)
+    {   _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+//  printf("wFormatTag: %x\n",wh.wFormatTag);
+//  printf("blockalign: %x\n",wh.nBlockAlign);
+//  prinff("nFormatSpc: %x\n",wh.nFormatSpecific);
+
+    if((si=(SAMPLE *)_mm_calloc(1,sizeof(SAMPLE)))==NULL) return NULL;
+
+    si->speed  = wh.nSamplesPerSec/wh.nChannels;
+    si->volume = 64;
+
+    si->length = _mm_read_I_ULONG(stream_fp);
+
+    if(wh.nBlockAlign==2)
+    {   si->flags = SF_16BITS | SF_SIGNED;
+        si->length>>=1;
+    }
+
+    return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_xm.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,679 @@
+/*
+
+ Name: LOAD_XM.C
+
+ Description:
+ Fasttracker (XM) module loader
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author).
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+/**************************************************************************
+**************************************************************************/
+
+
+typedef struct XMHEADER
+{   CHAR  id[17];                   // ID text: 'Extended module: '
+    CHAR  songname[21];             // Module name, padded with zeroes and 0x1a at the end
+    CHAR  trackername[20];          // Tracker name
+    UWORD version;                  // (word) Version number, hi-byte major and low-byte minor
+    ULONG headersize;               // Header size
+    UWORD songlength;               // (word) Song length (in patten order table)
+    UWORD restart;                  // (word) Restart position
+    UWORD numchn;                   // (word) Number of channels (2,4,6,8,10,...,32)
+    UWORD numpat;                   // (word) Number of patterns (max 256)
+    UWORD numins;                   // (word) Number of instruments (max 128)
+    UWORD flags;                    // (word) Flags: bit 0: 0 = Amiga frequency table (see below) 1 = Linear frequency table
+    UWORD tempo;                    // (word) Default tempo
+    UWORD bpm;                      // (word) Default BPM
+    UBYTE orders[256];              // (byte) Pattern order table 
+} XMHEADER;
+
+
+typedef struct XMINSTHEADER
+{   ULONG size;                     // (dword) Instrument size
+    CHAR  name[22];                 // (char) Instrument name
+    UBYTE type;                     // (byte) Instrument type (always 0)
+    UWORD numsmp;                   // (word) Number of samples in instrument
+    ULONG ssize;                    //
+} XMINSTHEADER;
+
+
+typedef struct XMPATCHHEADER
+{   UBYTE what[96];         // (byte) Sample number for all notes
+    UWORD volenv[24];       // (byte) Points for volume envelope
+    UWORD panenv[24];       // (byte) Points for panning envelope
+    UBYTE volpts;           // (byte) Number of volume points
+    UBYTE panpts;           // (byte) Number of panning points
+    UBYTE volsus;           // (byte) Volume sustain point
+    UBYTE volbeg;           // (byte) Volume loop start point
+    UBYTE volend;           // (byte) Volume loop end point
+    UBYTE pansus;           // (byte) Panning sustain point
+    UBYTE panbeg;           // (byte) Panning loop start point
+    UBYTE panend;           // (byte) Panning loop end point
+    UBYTE volflg;           // (byte) Volume type: bit 0: On; 1: Sustain; 2: Loop
+    UBYTE panflg;           // (byte) Panning type: bit 0: On; 1: Sustain; 2: Loop
+    UBYTE vibflg;           // (byte) Vibrato type
+    UBYTE vibsweep;         // (byte) Vibrato sweep
+    UBYTE vibdepth;         // (byte) Vibrato depth
+    UBYTE vibrate;          // (byte) Vibrato rate
+    UWORD volfade;          // (word) Volume fadeout
+    UWORD reserved[11];     // (word) Reserved
+} XMPATCHHEADER;
+
+
+typedef struct XMWAVHEADER
+{   ULONG length;           // (dword) Sample length
+    ULONG loopstart;        // (dword) Sample loop start
+    ULONG looplength;       // (dword) Sample loop length
+    UBYTE volume;           // (byte) Volume 
+    SBYTE finetune;         // (byte) Finetune (signed byte -128..+127)
+    UBYTE type;             // (byte) Type: Bit 0-1: 0 = No loop, 1 = Forward loop,
+                            //                       2 = Ping-pong loop;
+                            //                    4: 16-bit sampledata
+    UBYTE panning;          // (byte) Panning (0-255)
+    SBYTE relnote;          // (byte) Relative note number (signed byte)
+    UBYTE reserved;         // (byte) Reserved
+    CHAR  samplename[22];   // (char) Sample name
+
+    UBYTE vibtype;          // (byte) Vibrato type
+    UBYTE vibsweep;         // (byte) Vibrato sweep
+    UBYTE vibdepth;         // (byte) Vibrato depth
+    UBYTE vibrate;          // (byte) Vibrato rate
+} XMWAVHEADER;
+
+
+typedef struct XMPATHEADE
+{   ULONG size;                     // (dword) Pattern header length 
+    UBYTE packing;                  // (byte) Packing type (always 0)
+    UWORD numrows;                  // (word) Number of rows in pattern (1..256)
+    UWORD packsize;                 // (word) Packed patterndata size
+} XMPATHEADER;
+
+typedef struct MTMNOTE
+{    UBYTE a,b,c;
+} MTMNOTE;
+
+
+typedef struct XMNOTE
+{    UBYTE note,ins,vol,eff,dat;
+}XMNOTE;
+
+/**************************************************************************
+**************************************************************************/
+
+static XMNOTE *xmpat = NULL;
+static XMHEADER *mh = NULL;
+
+BOOL XM_Test(void)
+{
+    UBYTE id[17];
+    
+    if(!_mm_read_UBYTES(id,17,modfp)) return 0;
+    if(!memcmp(id,"Extended Module: ",17)) return 1;
+    return 0;
+}
+
+
+BOOL XM_Init(void)
+{
+    if(!(mh=(XMHEADER *)_mm_calloc(1,sizeof(XMHEADER)))) return 0;
+    return 1;
+}
+
+
+void XM_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    mh = NULL;
+}
+
+
+void XM_ReadNote(XMNOTE *n)
+{
+    UBYTE cmp;
+
+    memset(n,0,sizeof(XMNOTE));
+
+    cmp = _mm_read_UBYTE(modfp);
+
+    if(cmp&0x80)
+    {   if(cmp&1)  n->note = _mm_read_UBYTE(modfp);
+        if(cmp&2)  n->ins  = _mm_read_UBYTE(modfp);
+        if(cmp&4)  n->vol  = _mm_read_UBYTE(modfp);
+        if(cmp&8)  n->eff  = _mm_read_UBYTE(modfp);
+        if(cmp&16) n->dat  = _mm_read_UBYTE(modfp);
+    }
+    else
+    {   n->note = cmp;
+        n->ins  = _mm_read_UBYTE(modfp);
+        n->vol  = _mm_read_UBYTE(modfp);
+        n->eff  = _mm_read_UBYTE(modfp);
+        n->dat  = _mm_read_UBYTE(modfp);
+    }
+}
+
+
+UBYTE *XM_Convert(XMNOTE *xmtrack,UWORD rows)
+{
+    int t;
+    UBYTE note,ins,vol,eff,dat;
+
+    UniReset();
+
+    for(t=0; t<rows; t++)
+    {   note = xmtrack->note;
+        ins  = xmtrack->ins;
+        vol  = xmtrack->vol;
+        eff  = xmtrack->eff;
+        dat  = xmtrack->dat;
+
+        if(note!=0)
+        {  if(note==97) 
+           {   UniWrite(UNI_KEYFADE);
+               UniWrite(0);
+           } else
+               UniNote(note-1);
+        }
+
+        if(ins!=0) UniInstrument(ins-1);
+
+        switch(vol>>4)
+        {
+            case 0x6:                   // volslide down
+                if(vol&0xf)
+                {   UniWrite(UNI_XMEFFECTA);
+                    UniWrite(vol&0xf);
+                }
+                break;
+
+            case 0x7:                   // volslide up
+                if(vol&0xf)
+                {   UniWrite(UNI_XMEFFECTA);
+                    UniWrite(vol<<4);
+                }
+                break;
+
+            // volume-row fine volume slide is compatible with protracker
+            // EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as
+            // opposed to 'take the last sliding value'.
+
+            case 0x8:                       // finevol down
+                UniPTEffect(0xe,0xb0 | (vol&0xf));
+                break;
+
+            case 0x9:                       // finevol up
+                UniPTEffect(0xe,0xa0 | (vol&0xf));
+                break;
+
+            case 0xa:                       // set vibrato speed
+                UniPTEffect(0x4,vol<<4);
+                break;
+
+            case 0xb:                       // vibrato
+                UniPTEffect(0x4,vol&0xf);
+                break;
+
+            case 0xc:                       // set panning
+                UniPTEffect(0x8,vol<<4);
+                break;
+
+            case 0xd:                       // panning slide left
+                // only slide when data nibble not zero:
+
+                if(vol&0xf)
+                {   UniWrite(UNI_XMEFFECTP);
+                    UniWrite(vol&0xf);
+                }
+                break;
+
+            case 0xe:                       // panning slide right
+                // only slide when data nibble not zero:
+
+                if(vol&0xf)
+                {   UniWrite(UNI_XMEFFECTP);
+                    UniWrite(vol<<4);
+                }
+             break;
+
+            case 0xf:                       // tone porta
+                UniPTEffect(0x3,vol<<4);
+            break;
+
+            default:
+                if(vol>=0x10 && vol<=0x50)
+                    UniPTEffect(0xc,vol-0x10);
+        }
+
+        switch(eff)
+        {
+            case 0x4:                       // Effect 4: Vibrato
+                UniWrite(UNI_XMEFFECT4);
+                UniWrite(dat);
+            break;
+
+            case 0xa:
+                UniWrite(UNI_XMEFFECTA);
+                UniWrite(dat);
+            break;
+
+            case 0xe:
+                switch(dat>>4)
+                {  case 0x1:      // XM fine porta up
+                      UniWrite(UNI_XMEFFECTE1);
+                      UniWrite(dat&0xf);
+                   break;
+
+                   case 0x2:      // XM fine porta down
+                      UniWrite(UNI_XMEFFECTE2);
+                      UniWrite(dat&0xf);
+                   break;
+
+                   case 0xa:      // XM fine volume up
+                      UniWrite(UNI_XMEFFECTEA);
+                      UniWrite(dat&0xf);
+                   break;
+
+                   case 0xb:      // XM fine volume down
+                      UniWrite(UNI_XMEFFECTEB);
+                      UniWrite(dat&0xf);
+                   break;
+
+                   default:
+                      UniPTEffect(0x0e,dat);
+                }
+            break;
+
+            case 'G'-55:                    // G - set global volume
+                if(dat>64) dat = 64;
+                UniWrite(UNI_XMEFFECTG);
+                UniWrite(dat);
+                break;
+
+            case 'H'-55:                    // H - global volume slide
+                UniWrite(UNI_XMEFFECTH);
+                UniWrite(dat);
+                break;
+
+            case 'K'-55:                    // K - keyOff and KeyFade
+                UniWrite(UNI_KEYFADE);
+                UniWrite(dat);
+                break;
+
+            case 'L'-55:                    // L - set envelope position
+                UniWrite(UNI_XMEFFECTL);
+                UniWrite(dat);
+                break;
+
+            case 'P'-55:                    // P - panning slide
+                UniWrite(UNI_XMEFFECTP);
+                UniWrite(dat);
+                break;
+
+            case 'R'-55:                    // R - multi retrig note
+                UniWrite(UNI_S3MEFFECTQ);
+                UniWrite(dat);
+                break;
+
+            case 'T'-55:                    // T - Tremor !! (== S3M effect I)
+                UniWrite(UNI_S3MEFFECTI);
+                UniWrite(dat);
+                break;
+
+            case 'X'-55:
+                if((dat>>4) == 1)           // X1 - Extra Fine Porta up
+                {  UniWrite(UNI_XMEFFECTX1);
+                   UniWrite(dat & 0xf);
+                } else if((dat>>4) == 2)    // X2 - Extra Fine Porta down
+                {  UniWrite(UNI_XMEFFECTX2);
+                   UniWrite(dat & 0xf);
+                }
+            break;
+
+            default:
+                if(eff <= 0xf)
+                {   // Convert pattern jump from Dec to Hex
+                    if(eff == 0xd)
+                        dat = (((dat&0xf0)>>4)*10)+(dat&0xf);
+                    UniPTEffect(eff,dat);
+                }
+            break;
+        }
+
+        UniNewline();
+        xmtrack++;
+    }
+    return UniDup();
+}
+
+
+
+BOOL XM_Load(void)
+{
+    INSTRUMENT *d;
+    SAMPLE     *q;
+    XMWAVHEADER *wh,*s;
+    int   t,u,v,p,numtrk;
+    long  next;
+    ULONG nextwav[256];
+    BOOL  dummypat=0;
+ 
+    // try to read module header
+
+    _mm_read_string(mh->id,17,modfp);
+    _mm_read_string(mh->songname,21,modfp);
+    _mm_read_string(mh->trackername,20,modfp);
+    mh->version     =_mm_read_I_UWORD(modfp);
+    mh->headersize  =_mm_read_I_ULONG(modfp);
+    mh->songlength  =_mm_read_I_UWORD(modfp);
+    mh->restart     =_mm_read_I_UWORD(modfp);
+    mh->numchn      =_mm_read_I_UWORD(modfp);
+    mh->numpat      =_mm_read_I_UWORD(modfp);
+    mh->numins      =_mm_read_I_UWORD(modfp);
+    mh->flags       =_mm_read_I_UWORD(modfp);
+    mh->tempo       =_mm_read_I_UWORD(modfp);
+    mh->bpm         =_mm_read_I_UWORD(modfp);
+    _mm_read_UBYTES(mh->orders,256,modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // set module variables
+    of.initspeed = mh->tempo;         
+    of.inittempo = mh->bpm;
+    of.modtype   = DupStr(mh->trackername,20);
+    of.numchn    = mh->numchn;
+    of.numpat    = mh->numpat;
+    of.numtrk    = (UWORD)of.numpat*of.numchn;   // get number of channels
+    of.songname  = DupStr(mh->songname,20);      // make a cstr of songname
+    of.numpos    = mh->songlength;               // copy the songlength
+    of.reppos    = mh->restart;
+    of.numins    = mh->numins;
+    of.flags |= UF_XMPERIODS | UF_INST;
+    if(mh->flags&1) of.flags |= UF_LINEAR;
+
+    memset(of.chanvol,64,of.numchn);             // store channel volumes
+
+    if(!AllocPositions(of.numpos+3)) return 0;
+    for(t=0; t<of.numpos; t++)
+        of.positions[t] = mh->orders[t];
+
+/*
+   WHY THIS CODE HERE?? I CAN'T REMEMBER!
+   
+   Well, I do know why, mikmak!  Seems that FT2 doesn't always count blank 
+   patterns AT ALL if they are at the END of the song.  So, we have to check
+   for any patter numbers in the order list greater than the number of pat-
+   terns total.  If one or more is found, we set it equal to the pattern total
+   and make a dummy pattern to accomidate for the discrepency!
+*/
+
+    for(t=0; t<of.numpos; t++)
+    {   if(of.positions[t] > of.numpat)
+        {  of.positions[t] = of.numpat;
+           dummypat = 1;
+        }
+    }      
+
+    if(dummypat) { of.numpat++; of.numtrk+=of.numchn; }
+
+    if(!AllocTracks()) return 0;
+    if(!AllocPatterns()) return 0;
+
+    numtrk = 0;
+    for(t=0; t<mh->numpat; t++)
+    {   XMPATHEADER ph;
+
+        ph.size     =_mm_read_I_ULONG(modfp);
+        ph.packing  =_mm_read_UBYTE(modfp);
+        ph.numrows  =_mm_read_I_UWORD(modfp);
+        ph.packsize =_mm_read_I_UWORD(modfp);
+
+        of.pattrows[t] = ph.numrows;
+
+        //  Gr8.. when packsize is 0, don't try to load a pattern.. it's empty.
+        //  This bug was discovered thanks to Khyron's module..
+
+        if(!(xmpat=(XMNOTE *)_mm_calloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) return 0;
+
+        if(ph.packsize>0)
+        {   for(u=0; u<ph.numrows; u++)
+            {   for(v=0; v<of.numchn; v++)
+                    XM_ReadNote(&xmpat[(v*ph.numrows)+u]);
+            }
+        }
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 0;
+        }
+
+        for(v=0; v<of.numchn; v++)                                           
+           of.tracks[numtrk++] = XM_Convert(&xmpat[v*ph.numrows],ph.numrows);
+
+        free(xmpat);
+    }
+                                                          
+    if(dummypat)
+    {  of.pattrows[t] = 64;
+       if(!(xmpat=(XMNOTE *)_mm_calloc(64*of.numchn,sizeof(XMNOTE)))) return 0;
+       for(v=0; v<of.numchn; v++)
+           of.tracks[numtrk++] = XM_Convert(&xmpat[v*64],64);
+       free(xmpat);
+    }
+
+    if(!AllocInstruments()) return 0;
+    if((wh = (XMWAVHEADER *)_mm_calloc(256,sizeof(XMWAVHEADER))) == NULL) return 0;
+    d = of.instruments;
+    s = wh;
+
+
+    for(t=0; t<of.numins; t++)
+    {   XMINSTHEADER ih;
+        int          headend;
+
+        memset(d->samplenumber,255,120);
+
+        // read instrument header
+
+        headend     = _mm_ftell(modfp);
+        ih.size     = _mm_read_I_ULONG(modfp);
+        headend    += ih.size;
+        _mm_read_string(ih.name, 22, modfp);
+        ih.type     = _mm_read_UBYTE(modfp);
+        ih.numsmp   = _mm_read_I_UWORD(modfp);
+        d->insname  = DupStr(ih.name,22);
+
+        if(ih.size > 29)
+        {   ih.ssize    = _mm_read_I_ULONG(modfp);
+            if(ih.numsmp > 0)
+            {   XMPATCHHEADER pth;
+    
+                _mm_read_UBYTES (pth.what, 96, modfp);
+                _mm_read_I_UWORDS (pth.volenv, 24, modfp);
+                _mm_read_I_UWORDS (pth.panenv, 24, modfp);
+                pth.volpts      =  _mm_read_UBYTE(modfp);
+                pth.panpts      =  _mm_read_UBYTE(modfp);
+                pth.volsus      =  _mm_read_UBYTE(modfp);
+                pth.volbeg      =  _mm_read_UBYTE(modfp);
+                pth.volend      =  _mm_read_UBYTE(modfp);
+                pth.pansus      =  _mm_read_UBYTE(modfp);
+                pth.panbeg      =  _mm_read_UBYTE(modfp);
+                pth.panend      =  _mm_read_UBYTE(modfp);
+                pth.volflg      =  _mm_read_UBYTE(modfp);
+                pth.panflg      =  _mm_read_UBYTE(modfp);
+                pth.vibflg      =  _mm_read_UBYTE(modfp);
+                pth.vibsweep    =  _mm_read_UBYTE(modfp);
+                pth.vibdepth    =  _mm_read_UBYTE(modfp);
+                pth.vibrate     =  _mm_read_UBYTE(modfp);
+                pth.volfade     =  _mm_read_I_UWORD(modfp);
+    
+                // read the remainder of the header
+                for(u=headend-_mm_ftell(modfp); u; u--)  _mm_read_UBYTE(modfp);
+    
+                if(feof(modfp))
+                {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+                    return 0;
+                }
+    
+                for(u=0; u<96; u++)         
+                   d->samplenumber[u] = pth.what[u] + of.numsmp;
+    
+                d->volfade = pth.volfade;
+    
+                memcpy(d->volenv,pth.volenv,24);
+                if(pth.volflg & 1)  d->volflg |= EF_ON;
+                if(pth.volflg & 2)  d->volflg |= EF_SUSTAIN;
+                if(pth.volflg & 4)  d->volflg |= EF_LOOP;
+                d->volsusbeg = d->volsusend = pth.volsus;
+                d->volbeg    = pth.volbeg;
+                d->volend    = pth.volend;
+                d->volpts    = pth.volpts;
+    
+                // scale volume envelope:
+    
+                for(p=0; p<12; p++)
+                    d->volenv[p].val <<= 2;
+    
+                if((d->volflg & EF_ON) && (d->volpts < 2))
+                    d->volflg &= ~EF_ON;
+    
+                memcpy(d->panenv,pth.panenv,24);
+                d->panflg    = pth.panflg;
+                d->pansusbeg = d->pansusend = pth.pansus;
+                d->panbeg    = pth.panbeg;
+                d->panend    = pth.panend;
+                d->panpts    = pth.panpts;
+    
+                // scale panning envelope:
+    
+                for(p=0; p<12; p++)
+                    d->panenv[p].val <<= 2;
+                if((d->panflg & EF_ON) && (d->panpts < 2))
+                    d->panflg &= ~EF_ON;
+    
+                next = 0;
+    
+                //  Samples are stored outside the instrument struct now, so we have
+                //  to load them all into a temp area, count the of.numsmp along the
+                //  way and then do an AllocSamples() and move everything over 
+    
+                for(u=0; u<ih.numsmp; u++,s++)
+                {   s->length       =_mm_read_I_ULONG (modfp);
+                    s->loopstart    =_mm_read_I_ULONG (modfp);
+                    s->looplength   =_mm_read_I_ULONG (modfp);
+                    s->volume       =_mm_read_UBYTE (modfp);
+                    s->finetune     =_mm_read_SBYTE (modfp);
+                    s->type         =_mm_read_UBYTE (modfp);
+                    s->panning      =_mm_read_UBYTE (modfp);
+                    s->relnote      =_mm_read_SBYTE (modfp);
+                    s->vibtype      = pth.vibflg;
+                    s->vibsweep     = pth.vibsweep;
+                    s->vibdepth     = pth.vibdepth*4;
+                    s->vibrate      = pth.vibrate;
+    
+                    s->reserved =_mm_read_UBYTE (modfp);
+                    _mm_read_string(s->samplename, 22, modfp);
+    
+                    nextwav[of.numsmp+u] = next;
+                    next += s->length;
+    
+                    if(feof(modfp))
+                    {   _mm_errno = MMERR_LOADING_SAMPLEINFO;
+                        return 0;
+                    }
+                }
+    
+                for(u=0; u<ih.numsmp; u++) nextwav[of.numsmp++] += _mm_ftell(modfp);
+                _mm_fseek(modfp,next,SEEK_CUR);
+            }
+        }
+
+        d++;
+    }
+
+    if(!AllocSamples()) return 0;
+    q = of.samples;
+    s = wh;
+
+    for(u=0; u<of.numsmp; u++,q++,s++)
+    {   q->samplename   = DupStr(s->samplename,22);
+        q->length       = s->length;
+        q->loopstart    = s->loopstart;
+        q->loopend      = s->loopstart+s->looplength;
+        q->volume       = s->volume;
+        q->speed        = s->finetune+128;
+        q->panning      = s->panning;
+        q->seekpos      = nextwav[u];
+        q->vibtype      = s->vibtype;
+        q->vibsweep     = s->vibsweep;
+        q->vibdepth     = s->vibdepth;
+        q->vibrate      = s->vibrate;
+
+        if(s->type & 0x10)
+        {   q->length    >>= 1;
+            q->loopstart >>= 1;
+            q->loopend   >>= 1;
+        }
+
+        q->flags|=SF_OWNPAN;
+        if(s->type&0x3) q->flags|=SF_LOOP;
+        if(s->type&0x2) q->flags|=SF_BIDI;
+
+        if(s->type&0x10) q->flags|=SF_16BITS;
+        q->flags|=SF_DELTA;
+        q->flags|=SF_SIGNED;
+    }
+
+    d = of.instruments;
+    s = wh;
+    for(u=0; u<of.numins; u++, d++)
+    {  /*for(t=0; t<3; t++)
+          if((s[d->samplenumber[t]].relnote / 12) > )
+          {  s[d->samplenumber[t]].relnote -= 12;
+             of.samples[d->samplenumber[t]].speed <<= 1;
+          }
+       */
+       for(t=0; t<96; t++)
+          d->samplenote[t] = (d->samplenumber[t]==of.numsmp) ? 255 : (t+s[d->samplenumber[t]].relnote);
+    }
+
+    free(wh);
+    return 1;
+}
+
+
+
+CHAR *XM_LoadTitle(void)
+{
+    CHAR s[21];
+
+    _mm_fseek(modfp,17,SEEK_SET);
+    if(!fread(s,21,1,modfp)) return NULL;
+  
+    return(DupStr(s,21));
+}
+
+
+
+MLOADER load_xm =
+{   NULL,
+    "XM",
+    "Portable XM loader v0.5",
+    XM_Init,
+    XM_Test,
+    XM_Load,
+    XM_Cleanup,
+    XM_LoadTitle
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/mloader.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,399 @@
+/*
+
+Name:  MLOADER.C
+
+Description:
+ These routines are used to access the available module loaders
+
+Portability:
+ All systems - all compilers
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+FILE   *modfp;
+UNIMOD of;
+
+static MLOADER *firstloader = NULL;
+
+UWORD finetune[16] =
+{   8363,   8413,   8463,   8529,   8581,   8651,   8723,   8757,
+    7895,   7941,   7985,   8046,   8107,   8169,   8232,   8280
+};
+
+
+void ML_InfoLoader(void)
+{
+    int      t;
+    MLOADER *l;
+
+    // list all registered devicedrivers:
+
+    for(t=1,l=firstloader; l!=NULL; l=l->next, t++)
+        printf("%d. %s\n",t,l->version);
+}
+
+
+void ML_RegisterLoader(MLOADER *ldr)
+{
+    MLOADER *cruise = firstloader;
+
+    if(cruise!=NULL)
+    {   while(cruise->next!=NULL)  cruise = cruise->next;
+        cruise->next = ldr;
+    } else
+        firstloader = ldr; 
+}
+
+
+BOOL ReadComment(UWORD len)
+{
+    //int t;
+
+    if(len)
+    {   if(!(of.comment=(CHAR *)_mm_malloc(len+1))) return 0;
+        fread(of.comment,len,1,modfp);
+        of.comment[len] = 0;
+    }
+    return 1;
+}
+
+
+BOOL AllocPositions(int total)
+{
+    if((of.positions = _mm_calloc(total,sizeof(UWORD))) == NULL) return 0;
+    return 1;
+}
+
+
+BOOL AllocPatterns(void)
+{
+    int s,t,tracks = 0;
+
+    // Allocate track sequencing array
+
+    if(!(of.patterns = (UWORD *)_mm_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;
+    if(!(of.pattrows = (UWORD *)_mm_calloc(of.numpat+1,sizeof(UWORD)))) return 0;
+
+    for(t=0; t<of.numpat+1; t++)
+    {   of.pattrows[t] = 64;
+        for(s=0; s<of.numchn; s++)
+            of.patterns[(t*of.numchn)+s] = tracks++;
+    }
+
+    return 1;
+}
+
+
+BOOL AllocTracks(void)
+{
+    if(!(of.tracks=(UBYTE **)_mm_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;
+    return 1;
+}
+
+
+BOOL AllocInstruments(void)
+{
+    int t,n;
+
+    if((of.instruments=(INSTRUMENT *)_mm_calloc(of.numins,sizeof(INSTRUMENT)))==NULL) return 0;
+
+    for(t=0; t<of.numins; t++)
+    {  for(n=0; n<120; n++)     // Init note / sample lookup table
+       {  of.instruments[t].samplenote[n]   = n;
+          of.instruments[t].samplenumber[n] = t;
+       }   
+       of.instruments[t].globvol = 64;
+    }
+    return 1;
+}
+
+
+BOOL AllocSamples(void)
+{
+    UWORD u;
+
+    if((of.samples = (SAMPLE *)_mm_calloc(of.numsmp,sizeof(SAMPLE)))==NULL) return 0;
+
+    for(u=0; u<of.numsmp; u++)
+    {   of.samples[u].panning = 128;
+        of.samples[u].handle  = -1;
+        of.samples[u].globvol = 64;
+        of.samples[u].volume  = 64;
+    }
+    return 1;
+}
+
+
+BOOL ML_LoadSamples(void)
+{
+    SAMPLE *s;
+    int    u;
+
+    for(u=of.numsmp, s=of.samples; u; u--, s++)
+        if(s->length) SL_RegisterSample(s,MD_MUSIC,modfp);
+
+    return 1;
+}
+
+
+CHAR *DupStr(CHAR *s, UWORD len)
+//  Creates a CSTR out of a character buffer of 'len' bytes, but strips
+//  any terminating non-printing characters like 0, spaces etc.
+{
+    UWORD t;
+    CHAR  *d = NULL;
+
+    // Scan for first printing char in buffer [includes high ascii up to 254]
+    while(len)
+    {   if(s[len-1] > 0x20) break;
+        len--;
+    }
+
+    // When the buffer wasn't completely empty, allocate
+    // a cstring and copy the buffer into that string, except
+    // for any control-chars
+
+#ifdef __GNUC__
+    if(len<16) len = 16;
+#endif
+
+    if((d=(CHAR *)_mm_malloc(len+1)) != NULL)
+    {   for(t=0; t<len; t++) d[t] = (s[t]<32) ? ' ' : s[t];
+        d[t] = 0;
+    }
+
+    return d;
+}
+
+
+static void ML_XFreeSample(SAMPLE *s)
+{
+   if(s->handle>=0)
+      MD_SampleUnLoad(s->handle);
+   if(s->samplename!=NULL) free(s->samplename);
+}
+
+
+static void ML_XFreeInstrument(INSTRUMENT *i)
+{
+    if(i->insname!=NULL) free(i->insname);
+}
+
+
+static void ML_FreeEx(UNIMOD *mf)
+{
+    UWORD t;
+
+    if(mf->songname!=NULL) free(mf->songname);
+    if(mf->composer!=NULL) free(mf->composer);
+    if(mf->comment!=NULL)  free(mf->comment);
+
+    if(mf->modtype!=NULL)   free(mf->modtype);
+    if(mf->positions!=NULL) free(mf->positions);
+    if(mf->patterns!=NULL)  free(mf->patterns);
+    if(mf->pattrows!=NULL)  free(mf->pattrows);
+
+    if(mf->tracks!=NULL)
+    {   for(t=0; t<mf->numtrk; t++)
+            if(mf->tracks[t]!=NULL) free(mf->tracks[t]);
+        free(mf->tracks);
+    }
+
+    if(mf->instruments != NULL)
+    {   for(t=0; t<mf->numins; t++)
+            ML_XFreeInstrument(&mf->instruments[t]);
+        free(mf->instruments);
+    }
+
+    if(mf->samples != NULL)
+    {   for(t=0; t<mf->numsmp; t++)
+            if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);
+        free(mf->samples);
+    }
+
+    memset(mf,0,sizeof(UNIMOD));
+}
+
+
+static UNIMOD *ML_AllocUniMod(void)
+{
+   UNIMOD *mf;
+   
+   if((mf=_mm_calloc(1,sizeof(UNIMOD))) == NULL) return NULL;
+   return mf;
+}
+
+
+/******************************************
+
+    Next are the user-callable functions
+
+******************************************/
+
+
+void MikMod_FreeSong(UNIMOD *mf)
+{
+   if(mf!=NULL)
+   {   Player_Exit(mf);
+       ML_FreeEx(mf);
+   }
+}
+
+
+CHAR *MikMod_LoadSongTitle(CHAR *filename)
+{
+    MLOADER *l;
+    CHAR    *retval;
+    FILE    *fp;
+
+    if((fp = _mm_fopen(filename,"rb"))==NULL) return NULL;
+
+    _mm_errno = 0;
+    _mm_critical = 0;
+    _mm_iobase_setcur(modfp);
+
+    // Try to find a loader that recognizes the module
+
+    for(l=firstloader; l!=NULL; l=l->next)
+    {   _mm_rewind(modfp);
+        if(l->Test()) break;
+    }
+
+    if(l==NULL)
+    {   _mm_errno = MMERR_NOT_A_MODULE;
+        _mm_iobase_revert();
+        if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        return NULL;
+    }
+
+    retval = l->LoadTitle();
+
+    fclose(fp);
+    return(retval);
+}
+
+
+UNIMOD *MikMod_LoadSongFP(FILE *fp, int maxchan)
+
+// Loads a module given a file pointer.
+// File is loaded from the current file seek position.
+
+{
+    int      t;
+    MLOADER *l;
+    BOOL     ok;
+    UNIMOD  *mf;
+
+    modfp = fp;
+    _mm_errno = 0;
+    _mm_critical = 0;
+
+    _mm_iobase_setcur(modfp);
+
+    // Try to find a loader that recognizes the module
+
+    for(l=firstloader; l!=NULL; l=l->next)
+    {   _mm_rewind(modfp);
+        if(l->Test()) break;
+    }
+
+    if(l==NULL)
+    {   _mm_errno = MMERR_NOT_A_MODULE;
+        _mm_iobase_revert();
+        if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        return NULL;
+    }
+
+    // init unitrk routines
+    if(!UniInit())
+    {   if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        return NULL;
+    }
+
+    // load the song using the song's loader variable
+    memset(&of,0,sizeof(UNIMOD));
+    of.initvolume = 128;
+
+    // init panning array
+    for(t=0; t<64; t++) of.panning[t] = ((t+1)&2) ? 255 : 0;
+    for(t=0; t<64; t++) of.chanvol[t] = 64;
+
+    // init module loader and load the header / patterns
+    if(l->Init())
+    {   _mm_rewind(modfp);
+        ok = l->Load();
+    } else ok = 0;
+
+    // free loader and unitrk allocations
+    l->Cleanup();
+    UniCleanup();
+
+    if(!ok)
+    {   ML_FreeEx(&of);
+        _mm_iobase_revert();
+        if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        return NULL;
+    }
+
+    if(!ML_LoadSamples())
+    {   ML_FreeEx(&of);
+        _mm_iobase_revert();
+        if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        return NULL;
+    }
+
+    if((mf=ML_AllocUniMod()) == NULL)
+    {   ML_FreeEx(&of);
+        _mm_iobase_revert();
+        if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        return NULL;
+    }
+
+    // Copy the static UNIMOD contents into the dynamic UNIMOD struct.
+    memcpy(mf,&of,sizeof(UNIMOD));
+
+    _mm_iobase_revert();
+
+    if(maxchan > 0)
+    {   if(!(mf->flags & UF_NNA) && (mf->numchn < maxchan))
+            maxchan = mf->numchn;
+        else if((mf->numvoices!=0) && mf->numvoices < maxchan)
+            maxchan = mf->numvoices;
+
+        if(maxchan < mf->numchn) mf->flags |= UF_NNA;
+
+        if(MikMod_SetNumVoices(maxchan,-1))
+        {   MikMod_FreeSong(mf);
+            return NULL;
+        }
+    }
+
+    return mf;
+}
+
+
+UNIMOD *MikMod_LoadSong(CHAR *filename, int maxchan)
+
+// Open a module via it's filename.  The loader will initialize the specified
+// song-player 'player'.
+
+{
+    FILE   *fp;
+    UNIMOD *mf;
+    
+    if((fp = _mm_fopen(filename,"rb"))==NULL) return NULL;
+    if((mf = MikMod_LoadSongFP(fp, maxchan)) != NULL)
+    {   if(SL_LoadSamples() || Player_Init(mf))
+        {   MikMod_FreeSong(mf);
+            mf = NULL;
+        }
+    }
+
+    fclose(fp);
+    return mf;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/stream.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,134 @@
+/*
+ File: STREAM.C
+
+ Description:
+ Streaming Audio module for MikMod.
+
+ Portability:
+ All compilers - All systems
+
+*/
+
+
+#include "mikmod.h"
+
+
+int   stream_bufsize = 100;  // in 1/100th seconds
+
+static BOOL    is_streaming = 0;
+static MSTREAM *firststream = NULL;
+
+static BOOL active;
+
+FILE  *stream_fp;
+SLONG stream_seekpos;
+SLONG stream_reppos;
+
+void MikMod_RegisterStream(MSTREAM *stream)
+{
+    MSTREAM *cruise = firststream;
+
+    if(cruise!=NULL)
+    {   while(cruise->next!=NULL)  cruise = cruise->next;
+        cruise->next = stream;
+    } else
+        firststream = stream;
+}
+
+
+BOOL Stream_PlayFN(CHAR *filename)
+{
+    return 0;
+}
+
+
+BOOL Stream_PlayFP(FILE *fp)
+{
+    BOOL    ok;
+    MSTREAM *l;
+
+    stream_fp = fp;
+    _mm_errno = 0;
+    _mm_critical = 0;
+
+    _mm_iobase_setcur(stream_fp);
+
+    // Try to find a loader that recognizes the stream
+
+    for(l=firststream; l!=NULL; l=l->next)
+    {   _mm_rewind(stream_fp);
+        if(l->Test()) break;
+    }
+
+    if(l==NULL)
+    {   _mm_errno = MMERR_NOT_A_STREAM;
+        if(_mm_errorhandler!=NULL) _mm_errorhandler();
+        _mm_iobase_revert();
+        return 1;
+    }
+
+    // init stream loader and load the header
+    if(l->Init())
+    {   _mm_rewind(stream_fp);
+        ok = l->Load();
+    }
+
+    // free loader allocations
+    l->Cleanup();
+
+    if(!ok)
+    {   _mm_iobase_revert();
+        return 1;
+    }
+
+    //loadbuf = _mm_malloc(8192);
+
+    _mm_iobase_revert();
+    active = 1;
+    return 0;
+}
+
+
+BOOL Stream_Init(ULONG speed, UWORD flags)
+
+// size = size of buffer in 1/100th seconds
+
+{
+    if(is_streaming) md_driver->StreamExit();
+    if(md_driver->StreamInit(speed,flags)) return 1;
+    is_streaming = 1;
+
+    return 0;
+}
+
+
+void Stream_Exit(void)
+{
+    if(is_streaming) md_driver->StreamExit();
+    is_streaming = 0;
+}
+
+
+static ULONG last = 0;
+
+void Stream_Update(void)
+{
+    ULONG curr;
+    ULONG todo;
+
+    curr = md_driver->StreamGetPosition();
+    if(curr==last) return;
+
+    if(curr>last)
+    {   todo = curr-last;
+        //SL_LoadStream(,vstream.infmt,vstream.flags,todo,stream_fp);
+        last += todo;
+        //if(last>=vstream.size) last = 0;
+    } else
+    {   //todo = vstream.size-last;
+        //SL_LoadStream(&vstream.buffer[last],vstream.infmt,vstream.flags,todo,stream_fp);
+        //SL_LoadStream(vstream.buffer,vstream.infmt,vstream.flags,curr,stream_fp);
+        last = curr;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/virtch2.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,927 @@
+/*
+
+Name:  VIRTCH2.C
+
+Description:
+ All-c sample mixing routines, using a 32 bits mixing buffer, interpolation,
+ and sample smoothing [improves sound quality and removes clicks].
+
+ Future Additions:
+   Low-Pass filter to remove annoying staticy buzz.
+
+
+C Mixer Portability:
+ All Systems -- All compilers.
+
+Assembly Mixer Portability:
+
+ MSDOS:  BC(?)   Watcom(y)   DJGPP(y)
+ Win95:  ?
+ Os2:    ?
+ Linux:  y
+
+ (y) - yes
+ (n) - no (not possible or not useful)
+ (?) - may be possible, but not tested
+
+*/
+
+#include <stddef.h>
+#include <string.h>
+#include "mikmod.h"
+
+
+// REVERBERATION : Larger numbers result in shorter reverb duration.
+//      Longer reverb durations can cause unwanted static and make the
+//      reverb sound more like an echo.
+
+#define REVERBERATION 28000l
+
+// SAMPLING_SHIFT : Specified the shift multiplier which controls by how
+//      much the mixing rate is multiplied while mixing.  Higher values
+//      can improve quality by smoothing the soudn and reducing pops and
+//      clicks.  Note, this is a shift value, so a value of 2 becomes a
+//      mixing-rate multiplier of 4, and a value of 3 = 8, etc.
+
+#define SAMPLING_SHIFT  2
+#define SAMPLING_FACTOR (SLONG)(1<<SAMPLING_SHIFT)
+
+
+// FRACBITS : the number of bits per integer devoted to the fractional
+//      part of the number.  This value HAS to be changed if the compiler
+//      does not support 64 bit integers.  If 32 bit integers are used,
+//      FRACBITS _must_ be 9 or smaller.
+
+#define FRACBITS 28
+#define FRACMASK ((1l<<FRACBITS)-1)
+
+#define TICKLSIZE 4096
+#define TICKWSIZE (TICKLSIZE*2)
+#define TICKBSIZE (TICKWSIZE*2)
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+
+typedef struct
+{   UBYTE  kick;                  // =1 -> sample has to be restarted
+    UBYTE  active;                // =1 -> sample is playing
+    UWORD  flags;                 // 16/8 bits looping/one-shot
+    SWORD  handle;                // identifies the sample
+    ULONG  start;                 // start index
+    ULONG  size;                  // samplesize
+    ULONG  reppos;                // loop start
+    ULONG  repend;                // loop end
+    ULONG  frq;                   // current frequency
+    UWORD  vol;                   // current volume
+    UWORD  pan;                   // current panning position
+    SDOUBLE  current;               // current index in the sample
+    SDOUBLE  increment;             // fixed-point increment value
+} VINFO;
+
+
+static SWORD   **Samples;
+
+static VINFO   *vinf = NULL;
+static VINFO   *vnf;
+static ULONG   samplesthatfit;
+static int     vc_memory, vc_softchn;
+static SDOUBLE idxsize,idxlpos,idxlend;
+static SLONG   TICKLEFT;
+static SLONG   *VC2_TICKBUF = NULL;
+static UWORD   vc_mode;
+static int     bitshift;           // Amplification shift (amount to decrease 32 bit mixing buffer by!)
+static SLONG   lvolsel, rvolsel;   // Volume factor .. range 0-255
+
+
+// Reverb control variables
+// ========================
+
+static int     RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8;
+static ULONG   RVRindex;
+
+// For Mono or Left Channel
+static SLONG  *RVbuf1 = NULL, *RVbuf2 = NULL, *RVbuf3 = NULL,
+              *RVbuf4 = NULL, *RVbuf5 = NULL, *RVbuf6 = NULL,
+              *RVbuf7 = NULL, *RVbuf8 = NULL;
+
+// For Stereo only (Right Channel)
+static SLONG  *RVbuf9 = NULL, *RVbuf10 = NULL, *RVbuf11 = NULL,
+              *RVbuf12 = NULL, *RVbuf13 = NULL, *RVbuf14 = NULL,
+              *RVbuf15 = NULL, *RVbuf16 = NULL;
+
+
+// ==============================================================
+//  16 bit sample mixers!
+
+static SDOUBLE MixStereoNormal(SWORD *srce, SLONG *dest, SDOUBLE index, SDOUBLE increment, ULONG todo)
+{
+    SWORD  sample;
+
+    for(; todo; todo--)
+    {   sample = (index & FRACBITS) ?
+                    (((srce[index >> FRACBITS] * (FRACBITS - (index & FRACBITS))) +
+                    (srce[(index >> FRACBITS)+1] * (index & FRACBITS))) / FRACBITS)
+                 :  srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvolsel * sample;
+        *dest++ += rvolsel * sample;
+    }
+
+    return index;
+}
+
+
+static SDOUBLE MixStereoSurround(SWORD *srce, SLONG *dest, SDOUBLE index, SDOUBLE increment, ULONG todo)
+{
+    SWORD  sample;
+
+    for(dest--; todo; todo--)
+    {   sample = (index & FRACBITS) ?
+                    (((srce[index >> FRACBITS] * (FRACBITS - (index & FRACBITS))) +
+                    (srce[(index >> FRACBITS)+1] * (index & FRACBITS))) / FRACBITS)
+                 :  srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvolsel * sample;
+        *dest++ -= lvolsel * sample;
+    }
+    
+    return index;
+}
+
+
+static SDOUBLE MixMonoNormal(SWORD *srce, SLONG *dest, SDOUBLE index, SDOUBLE increment, SLONG todo)
+{
+    SWORD  sample;
+    
+    for(; todo; todo--)
+    {   sample = (index & FRACBITS) ?
+                    (((srce[index >> FRACBITS] * (FRACBITS - (index & FRACBITS))) +
+                    (srce[(index >> FRACBITS)+1] * (index & FRACBITS))) / FRACBITS)
+                 :  srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvolsel * sample;
+    }
+
+    return index;
+}
+
+
+static void (*Mix32to16)(SWORD *dste, SLONG *srce, SLONG count);
+static void (*Mix32to8)(SBYTE *dste, SLONG *srce, SLONG count);
+static void (*MixReverb)(SLONG *srce, SLONG count);
+
+
+static void MixReverb_Normal(SLONG *srce, SLONG count)
+{
+    SLONG        speedup;
+    int          ReverbPct;
+    unsigned int loc1, loc2, loc3, loc4,
+                 loc5, loc6, loc7, loc8;
+
+    ReverbPct = 63 + (md_reverb*4);
+
+    loc1 = RVRindex % RVc1;
+    loc2 = RVRindex % RVc2;
+    loc3 = RVRindex % RVc3;
+    loc4 = RVRindex % RVc4;
+    loc5 = RVRindex % RVc5;
+    loc6 = RVRindex % RVc6;
+    loc7 = RVRindex % RVc7;
+    loc8 = RVRindex % RVc8;
+
+    for(; count; count--)
+    {
+        // Compute the LEFT CHANNEL echo buffers!
+
+        speedup = *srce >> 3;
+
+        RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128);
+        RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128);
+        RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128);
+        RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128);
+        RVbuf5[loc5] = speedup + ((ReverbPct * RVbuf5[loc5]) / 128);
+        RVbuf6[loc6] = speedup + ((ReverbPct * RVbuf6[loc6]) / 128);
+        RVbuf7[loc7] = speedup + ((ReverbPct * RVbuf7[loc7]) / 128);
+        RVbuf8[loc8] = speedup + ((ReverbPct * RVbuf8[loc8]) / 128);
+
+        // Prepare to compute actual finalized data!
+
+        RVRindex++;
+        loc1 = RVRindex % RVc1;
+        loc2 = RVRindex % RVc2;
+        loc3 = RVRindex % RVc3;
+        loc4 = RVRindex % RVc4;
+        loc5 = RVRindex % RVc5;
+        loc6 = RVRindex % RVc6;
+        loc7 = RVRindex % RVc7;
+        loc8 = RVRindex % RVc8;
+
+        // Left Channel!
+        
+        *srce++ += (RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4] +
+                    RVbuf5[loc5] - RVbuf6[loc6] + RVbuf7[loc7] - RVbuf8[loc8]);
+    }
+}
+
+
+static void MixReverb_Stereo(SLONG *srce, SLONG count)
+{
+    SLONG        speedup;
+    int          ReverbPct;
+    unsigned int loc1, loc2, loc3, loc4,
+                 loc5, loc6, loc7, loc8;
+
+    ReverbPct = 63 + (md_reverb*4);
+
+    loc1 = RVRindex % RVc1;
+    loc2 = RVRindex % RVc2;
+    loc3 = RVRindex % RVc3;
+    loc4 = RVRindex % RVc4;
+    loc5 = RVRindex % RVc5;
+    loc6 = RVRindex % RVc6;
+    loc7 = RVRindex % RVc7;
+    loc8 = RVRindex % RVc8;
+
+    for(; count; count--)
+    {
+        // Compute the LEFT CHANNEL echo buffers!
+
+        speedup = *srce >> 3;
+
+        RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128);
+        RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128);
+        RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128);
+        RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128);
+        RVbuf5[loc5] = speedup + ((ReverbPct * RVbuf5[loc5]) / 128);
+        RVbuf6[loc6] = speedup + ((ReverbPct * RVbuf6[loc6]) / 128);
+        RVbuf7[loc7] = speedup + ((ReverbPct * RVbuf7[loc7]) / 128);
+        RVbuf8[loc8] = speedup + ((ReverbPct * RVbuf8[loc8]) / 128);
+
+        // Compute the RIGHT CHANNEL echo buffers!
+        
+        speedup = srce[1] >> 3;
+
+        RVbuf9[loc1] = speedup + ((ReverbPct * RVbuf9[loc1]) / 128);
+        RVbuf10[loc2] = speedup + ((ReverbPct * RVbuf11[loc2]) / 128);
+        RVbuf11[loc3] = speedup + ((ReverbPct * RVbuf12[loc3]) / 128);
+        RVbuf12[loc4] = speedup + ((ReverbPct * RVbuf12[loc4]) / 128);
+        RVbuf13[loc5] = speedup + ((ReverbPct * RVbuf13[loc5]) / 128);
+        RVbuf14[loc6] = speedup + ((ReverbPct * RVbuf14[loc6]) / 128);
+        RVbuf15[loc7] = speedup + ((ReverbPct * RVbuf15[loc7]) / 128);
+        RVbuf16[loc8] = speedup + ((ReverbPct * RVbuf16[loc8]) / 128);
+
+        // Prepare to compute actual finalized data!
+
+        RVRindex++;
+        loc1 = RVRindex % RVc1;
+        loc2 = RVRindex % RVc2;
+        loc3 = RVRindex % RVc3;
+        loc4 = RVRindex % RVc4;
+        loc5 = RVRindex % RVc5;
+        loc6 = RVRindex % RVc6;
+        loc7 = RVRindex % RVc7;
+        loc8 = RVRindex % RVc8;
+
+        // Left Channel!
+        
+        *srce++ += (RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4] +
+                    RVbuf5[loc5] - RVbuf6[loc6] + RVbuf7[loc7] - RVbuf8[loc8]);
+
+        // Right Channel!
+        
+        *srce++ += (RVbuf9[loc1] - RVbuf10[loc2] + RVbuf11[loc3] - RVbuf12[loc4] +
+                    RVbuf13[loc5] - RVbuf14[loc6] + RVbuf15[loc7] - RVbuf16[loc8]);
+    }
+}
+
+
+static void Mix32To16_Normal(SWORD *dste, SLONG *srce, SLONG count)
+{
+    SLONG    x1, x2, tmpx;
+    int      i;
+    
+    for(count/=SAMPLING_FACTOR; count; count--)
+    {   tmpx = 0;
+
+        for(i=SAMPLING_FACTOR/2; i; i--)
+        {  x1 = *srce++ / bitshift;
+           x2 = *srce++ / bitshift;
+
+           x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1;
+           x2 = (x2 > 32767) ? 32767 : (x2 < -32768) ? -32768 : x2;
+
+           tmpx += x1 + x2;
+        }
+
+        *dste++ = tmpx / SAMPLING_FACTOR;
+    }
+}
+
+
+static void Mix32To16_Stereo(SWORD *dste, SLONG *srce, SLONG count)
+{
+    SLONG   x1, x2, x3, x4, tmpx, tmpy;
+    int     i;
+
+    for(count/=SAMPLING_FACTOR; count; count--)
+    {   tmpx = tmpy = 0;
+
+        for(i=SAMPLING_FACTOR/2; i; i--)
+        {   x1 = *srce++ / bitshift;
+            x2 = *srce++ / bitshift;
+            x3 = *srce++ / bitshift;
+            x4 = *srce++ / bitshift;
+
+            x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1;
+            x2 = (x2 > 32767) ? 32767 : (x2 < -32768) ? -32768 : x2;
+            x3 = (x3 > 32767) ? 32767 : (x3 < -32768) ? -32768 : x3;
+            x4 = (x4 > 32767) ? 32767 : (x4 < -32768) ? -32768 : x4;
+
+            tmpx += x1 + x3;
+            tmpy += x2 + x4;
+        }
+
+        *dste++ = tmpx / SAMPLING_FACTOR;
+        *dste++ = tmpy / SAMPLING_FACTOR;
+    }
+}
+
+
+static void Mix32To8_Normal(SBYTE *dste, SLONG *srce, SLONG count)
+{
+    int    x1, x2;
+    int    i, tmpx;
+    
+    for(count/=SAMPLING_FACTOR; count; count--)
+    {   tmpx = 0;
+
+        for(i=SAMPLING_FACTOR/2; i; i--)
+        {   x1 = *srce++ / bitshift;
+            x2 = *srce++ / bitshift;
+
+            x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1;
+            x2 = (x2 > 127) ? 127 : (x2 < -128) ? -128 : x2;
+
+            tmpx += x1 + x2;
+        }
+
+        *dste++ = (tmpx / SAMPLING_FACTOR) + 128;
+    }
+}
+
+
+static void Mix32To8_Stereo(SBYTE *dste, SLONG *srce, SLONG count)
+{
+    int    x1, x2, x3, x4;
+    int    i, tmpx, tmpy;
+
+    for(count/=SAMPLING_FACTOR; count; count--)
+    {   tmpx = tmpy = 0;
+
+        for(i=SAMPLING_FACTOR/2; i; i--)
+        {   x1 = *srce++ / bitshift;
+            x2 = *srce++ / bitshift;
+            x3 = *srce++ / bitshift;
+            x4 = *srce++ / bitshift;
+    
+            x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1;
+            x2 = (x2 > 127) ? 127 : (x2 < -128) ? -128 : x2;
+            x3 = (x3 > 127) ? 127 : (x3 < -128) ? -128 : x3;
+            x4 = (x4 > 127) ? 127 : (x4 < -128) ? -128 : x4;
+    
+            tmpx += x1 + x3;
+            tmpy += x2 + x4;
+        }
+
+        *dste++ = (tmpx / SAMPLING_FACTOR) + 128;        
+        *dste++ = (tmpy / SAMPLING_FACTOR) + 128;        
+    }
+}
+
+
+static ULONG samples2bytes(ULONG samples)
+{
+    if(vc_mode & DMODE_16BITS) samples <<= 1;
+    if(vc_mode & DMODE_STEREO) samples <<= 1;
+
+    return samples;
+}
+
+
+static ULONG bytes2samples(ULONG bytes)
+{
+    if(vc_mode & DMODE_16BITS) bytes >>= 1;
+    if(vc_mode & DMODE_STEREO) bytes >>= 1;
+
+    return bytes;
+}
+
+
+static void AddChannel(SLONG *ptr, SLONG todo)
+{
+    SDOUBLE  end;
+    SLONG  done;
+    SWORD  *s;
+
+    while(todo > 0)
+    {   // update the 'current' index so the sample loops, or
+        // stops playing if it reached the end of the sample
+
+        if(vnf->flags & SF_REVERSE)
+        {
+            // The sample is playing in reverse
+
+            if((vnf->flags & SF_LOOP) && (vnf->current < idxlpos))
+            {
+                // the sample is looping, and it has
+                // reached the loopstart index
+
+                if(vnf->flags & SF_BIDI)
+                {
+                    // sample is doing bidirectional loops, so 'bounce'
+                    // the current index against the idxlpos
+
+                    vnf->current    = idxlpos + (idxlpos - vnf->current);
+                    vnf->flags     &= ~SF_REVERSE;
+                    vnf->increment  = -vnf->increment;
+                } else
+                    // normal backwards looping, so set the
+                    // current position to loopend index
+
+                   vnf->current = idxlend - (idxlpos-vnf->current);
+            } else
+            {
+                // the sample is not looping, so check
+                // if it reached index 0
+
+                if(vnf->current < 0)
+                {
+                    // playing index reached 0, so stop
+                    // playing this sample
+
+                    vnf->current = 0;
+                    vnf->active  = 0;
+                    break;
+                }
+            }
+        } else
+        {
+            // The sample is playing forward
+
+            if((vnf->flags & SF_LOOP) && (vnf->current > idxlend))
+            {
+                 // the sample is looping, so check if
+                 // it reached the loopend index
+
+                 if(vnf->flags & SF_BIDI)
+                 {
+                     // sample is doing bidirectional loops, so 'bounce'
+                     //  the current index against the idxlend
+
+                     vnf->flags    |= SF_REVERSE;
+                     vnf->increment = -vnf->increment;
+                     vnf->current   = idxlend-(vnf->current-idxlend);
+                 } else
+                     // normal backwards looping, so set the
+                     // current position to loopend index
+
+                     vnf->current = idxlpos + (vnf->current-idxlend);
+            } else
+            {
+                // sample is not looping, so check
+                // if it reached the last position
+
+                if(vnf->current > idxsize)
+                {
+                    // yes, so stop playing this sample
+
+                    vnf->current = 0;
+                    vnf->active  = 0;
+                    break;
+                }
+            }
+        }
+
+        if(!(s=Samples[vnf->handle]))
+        {   vnf->current = 0;
+            vnf->active  = 0;
+            break;
+        }
+
+        end = (vnf->flags & SF_REVERSE) ? 
+                (vnf->flags & SF_LOOP) ? idxlpos : 0 :
+                (vnf->flags & SF_LOOP) ? idxlend : idxsize;
+
+        done = MIN((end - vnf->current) / vnf->increment + 1, todo);
+
+        if(!done)
+        {   vnf->active = 0;
+            break;
+        }
+
+        if(vnf->vol)
+        {   if(vc_mode & DMODE_STEREO)
+                if(vnf->pan == PAN_SURROUND && vc_mode&DMODE_SURROUND)
+                    vnf->current = MixStereoSurround(s,ptr,vnf->current,vnf->increment,done);
+                else
+                    vnf->current = MixStereoNormal(s,ptr,vnf->current,vnf->increment,done);
+            else
+                vnf->current = MixMonoNormal(s,ptr,vnf->current,vnf->increment,done);
+        }
+        todo -= done;
+        ptr  += (vc_mode & DMODE_STEREO) ? (done<<1) : done;
+    }
+}
+
+
+void VC2_WriteSamples(SBYTE *buf, ULONG todo)
+{
+    int    left, portion = 0;
+    SBYTE  *buffer;
+    int    t;
+    int    pan, vol;
+
+    todo *= SAMPLING_FACTOR;
+
+    while(todo)
+    {   if(TICKLEFT==0)
+        {   if(vc_mode & DMODE_SOFT_MUSIC) md_player();
+            TICKLEFT = (md_mixfreq*125l*SAMPLING_FACTOR) / (md_bpm*50l);
+            TICKLEFT &= ~(SAMPLING_FACTOR-1);
+        }
+
+        left = MIN(TICKLEFT, todo);
+        
+        buffer    = buf;
+        TICKLEFT -= left;
+        todo     -= left;
+
+        buf += samples2bytes(left) / SAMPLING_FACTOR;
+
+        while(left)
+        {   portion = MIN(left, samplesthatfit);
+            memset(VC2_TICKBUF, 0, portion << ((vc_mode & DMODE_STEREO) ? 3 : 2));
+
+            for(t=0; t<vc_softchn; t++)
+            {   vnf = &vinf[t];
+
+                if(vnf->kick)
+                {   vnf->current = (SDOUBLE)(vnf->start) << FRACBITS;
+                    vnf->kick    = 0;
+                    vnf->active  = 1;
+                }
+                
+                if(vnf->frq == 0) vnf->active = 0;
+                
+                if(vnf->active)
+                {   vnf->increment = ((SDOUBLE)(vnf->frq) << (FRACBITS-SAMPLING_SHIFT)) / (SDOUBLE)md_mixfreq;
+                    if(vnf->flags & SF_REVERSE) vnf->increment=-vnf->increment;
+
+                    vol = vnf->vol;  pan = vnf->pan;
+
+                    if((vc_mode & DMODE_STEREO) && (pan!=PAN_SURROUND))
+                    {   lvolsel = (vol * (255-pan)) >> 8;
+                        rvolsel = (vol * pan) >> 8;
+                    } else
+                        lvolsel = (vol*256l) / 480;
+
+                    idxsize = (vnf->size)   ? ((SDOUBLE)(vnf->size) << FRACBITS)-1 : 0;
+                    idxlend = (vnf->repend) ? ((SDOUBLE)(vnf->repend) << FRACBITS)-1 : 0;
+                    idxlpos = (SDOUBLE)(vnf->reppos) << FRACBITS;
+                    AddChannel(VC2_TICKBUF, portion);
+                }
+            }
+
+            if(md_reverb) MixReverb(VC2_TICKBUF, portion);
+
+            if(vc_mode & DMODE_16BITS)
+                Mix32to16((SWORD *) buffer, VC2_TICKBUF, portion);
+            else
+                Mix32to8((SBYTE *) buffer, VC2_TICKBUF, portion);
+
+            buffer += samples2bytes(portion) / SAMPLING_FACTOR;
+            left   -= portion;
+        }
+    }
+}
+
+
+void VC2_SilenceBytes(SBYTE *buf, ULONG todo)
+
+//  Fill the buffer with 'todo' bytes of silence (it depends on the mixing
+//  mode how the buffer is filled)
+
+{
+    // clear the buffer to zero (16 bits
+    // signed ) or 0x80 (8 bits unsigned)
+
+    if(vc_mode & DMODE_16BITS)
+        memset(buf,0,todo);
+    else
+        memset(buf,0x80,todo);
+}
+
+
+ULONG VC2_WriteBytes(SBYTE *buf, ULONG todo)
+
+//  Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of
+//  SBYTES actually written to 'buf' (which is rounded to number of samples
+//  that fit into 'todo' bytes).
+
+{
+    if(vc_softchn == 0)
+    {   VC2_SilenceBytes(buf,todo);
+        return todo;
+    }
+
+    todo = bytes2samples(todo);
+    VC2_WriteSamples(buf,todo);
+
+    return samples2bytes(todo);
+}
+
+
+BOOL VC2_PlayStart(void)
+{
+    if(vc_softchn > 0)
+    {   bitshift = vc_softchn + 257;
+        if(!(vc_mode & DMODE_16BITS))
+            bitshift *= 256;
+        if(md_reverb) bitshift++;
+    }
+
+    samplesthatfit = TICKLSIZE;
+    if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1;
+    TICKLEFT = 0;
+
+    /*  Original Reverb Code!
+        The stuff I use avoids floating point (below).
+
+    RVc1 = (SLONG)(500      * md_mixfreq) / 11000;
+    RVc2 = (SLONG)(507.8125 * md_mixfreq) / 11000;
+    RVc3 = (SLONG)(531.25   * md_mixfreq) / 11000;
+    RVc4 = (SLONG)(570.3125 * md_mixfreq) / 11000;
+    RVc5 = (SLONG)(625      * md_mixfreq) / 11000;
+    RVc6 = (SLONG)(695.3125 * md_mixfreq) / 11000;
+    RVc7 = (SLONG)(781.25   * md_mixfreq) / 11000;
+    RVc8 = (SLONG)(882.8125 * md_mixfreq) / 11000;
+    ReverbPct = 99 - (md_reverb*2);
+    */
+
+    RVc1 = (5000L * md_mixfreq) / REVERBERATION;
+    RVc2 = (5078L * md_mixfreq) / REVERBERATION;
+    RVc3 = (5313L * md_mixfreq) / REVERBERATION;
+    RVc4 = (5703L * md_mixfreq) / REVERBERATION;
+    RVc5 = (6250L * md_mixfreq) / REVERBERATION;
+    RVc6 = (6953L * md_mixfreq) / REVERBERATION;
+    RVc7 = (7813L * md_mixfreq) / REVERBERATION;
+    RVc8 = (8828L * md_mixfreq) / REVERBERATION;
+   
+    if((RVbuf1 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf2 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf3 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf4 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf5 = (SLONG *)_mm_calloc((RVc5+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf6 = (SLONG *)_mm_calloc((RVc6+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf7 = (SLONG *)_mm_calloc((RVc7+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf8 = (SLONG *)_mm_calloc((RVc8+1),sizeof(SLONG))) == NULL) return 1;
+    
+    if(vc_mode & DMODE_STEREO)
+    {   if((RVbuf9 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf10 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf11 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf12 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf13 = (SLONG *)_mm_calloc((RVc5+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf14 = (SLONG *)_mm_calloc((RVc6+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf15 = (SLONG *)_mm_calloc((RVc7+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf16 = (SLONG *)_mm_calloc((RVc8+1),sizeof(SLONG))) == NULL) return 1;
+    }
+    
+    RVRindex = 0;
+    return 0;
+}
+
+
+void VC2_PlayStop(void)
+{
+    if(RVbuf1  != NULL) free(RVbuf1);
+    if(RVbuf2  != NULL) free(RVbuf2);
+    if(RVbuf3  != NULL) free(RVbuf3);
+    if(RVbuf4  != NULL) free(RVbuf4);
+    if(RVbuf5  != NULL) free(RVbuf5);
+    if(RVbuf6  != NULL) free(RVbuf6);
+    if(RVbuf7  != NULL) free(RVbuf7);
+    if(RVbuf8  != NULL) free(RVbuf8);
+    if(RVbuf9  != NULL) free(RVbuf9);
+    if(RVbuf10 != NULL) free(RVbuf10);
+    if(RVbuf11 != NULL) free(RVbuf11);
+    if(RVbuf12 != NULL) free(RVbuf12);
+    if(RVbuf13 != NULL) free(RVbuf13);
+    if(RVbuf14 != NULL) free(RVbuf14);
+    if(RVbuf15 != NULL) free(RVbuf15);
+    if(RVbuf16 != NULL) free(RVbuf16);
+
+    RVbuf1  = NULL;  RVbuf2  = NULL;  RVbuf3  = NULL;  RVbuf4  = NULL;
+    RVbuf5  = NULL;  RVbuf6  = NULL;  RVbuf7  = NULL;  RVbuf8  = NULL;
+    RVbuf9  = NULL;  RVbuf10 = NULL;  RVbuf11 = NULL;  RVbuf12 = NULL;
+    RVbuf13 = NULL;  RVbuf14 = NULL;  RVbuf15 = NULL;  RVbuf16 = NULL;
+}
+
+
+BOOL VC2_Init(void)
+{
+    _mm_errno = MMERR_INITIALIZING_MIXER;
+    if((Samples = (SWORD **)calloc(MAXSAMPLEHANDLES, sizeof(SWORD *))) == NULL) return 1;
+    if(VC2_TICKBUF==NULL) if((VC2_TICKBUF=(SLONG *)malloc((TICKLSIZE+32) * sizeof(SLONG))) == NULL) return 1;
+
+    if(md_mode & DMODE_STEREO)
+    {   Mix32to16  = Mix32To16_Stereo;
+        Mix32to8   = Mix32To8_Stereo;
+        MixReverb  = MixReverb_Stereo;
+    } else
+    {   Mix32to16  = Mix32To16_Normal;
+        Mix32to8   = Mix32To8_Normal;
+        MixReverb  = MixReverb_Normal;
+    }
+
+    vc_mode = md_mode;
+
+    _mm_errno = 0;
+    return 0;
+}
+
+
+void VC2_Exit(void)
+
+// Yay, he joys and fruits of C and C++ -
+//                        Deallocation of arrays!
+
+{
+    //if(VC2_TICKBUF!=NULL) free(VC2_TICKBUF);
+    if(vinf!=NULL) free(vinf);
+    if(Samples!=NULL) free(Samples);
+
+//    VC2_TICKBUF     = NULL;
+    vinf            = NULL;
+    Samples         = NULL;
+}
+
+
+BOOL VC2_SetNumVoices(void)
+{
+    int t;
+
+    if((vc_softchn = md_softchn) == 0) return 0;
+
+    if(vinf!=NULL) free(vinf);
+    if((vinf = _mm_calloc(sizeof(VINFO),vc_softchn)) == NULL) return 1;
+    
+    for(t=0; t<vc_softchn; t++)
+    {   vinf[t].frq = 10000;
+        vinf[t].pan = (t & 1) ? 0 : 255;
+    }
+
+    return 0;
+}
+
+
+void VC2_VoiceSetVolume(UBYTE voice, UWORD vol)
+{
+    vinf[voice].vol = vol;
+}
+
+
+void VC2_VoiceSetFrequency(UBYTE voice, ULONG frq)
+{
+    vinf[voice].frq = frq;
+}
+
+
+void VC2_VoiceSetPanning(UBYTE voice, ULONG pan)
+{
+    vinf[voice].pan = pan;
+}
+
+
+void VC2_VoicePlay(UBYTE voice, SWORD handle, ULONG start, ULONG size, ULONG reppos, ULONG repend, UWORD flags)
+{
+    vinf[voice].flags    = flags;
+    vinf[voice].handle   = handle;
+    vinf[voice].start    = start;
+    vinf[voice].size     = size;
+    vinf[voice].reppos   = reppos;
+    vinf[voice].repend   = repend;
+    vinf[voice].kick     = 1;
+}
+
+
+void VC2_VoiceStop(UBYTE voice)
+{
+    vinf[voice].active = 0;
+}  
+
+
+BOOL VC2_VoiceStopped(UBYTE voice)
+{
+    return(vinf[voice].active==0);
+}
+
+
+void VC2_VoiceReleaseSustain(UBYTE voice)
+{
+
+}
+
+
+SLONG VC2_VoiceGetPosition(UBYTE voice)
+{
+    return(vinf[voice].current>>FRACBITS);
+}
+
+
+/**************************************************
+***************************************************
+***************************************************
+**************************************************/
+
+
+void VC2_SampleUnload(SWORD handle)
+{
+    void *sampleadr = Samples[handle];
+
+    free(sampleadr);
+    Samples[handle] = NULL;
+}
+
+
+SWORD VC2_SampleLoad(SAMPLOAD *sload, int type, FILE *fp)
+{
+    SAMPLE *s = sload->sample;
+    int    handle;
+    ULONG  t, length,loopstart,loopend;
+
+    if(type==MD_HARDWARE) return -1;
+
+    // Find empty slot to put sample address in
+    for(handle=0; handle<MAXSAMPLEHANDLES; handle++)
+        if(Samples[handle]==NULL) break;
+
+    if(handle==MAXSAMPLEHANDLES)
+    {   _mm_errno = MMERR_OUT_OF_HANDLES;
+        return -1;
+    }
+
+    length    = s->length;
+    loopstart = s->loopstart;
+    loopend   = s->loopend;
+
+    SL_SampleSigned(sload);
+
+    SL_Sample8to16(sload);
+    if((Samples[handle]=(SWORD *)malloc((length+16)<<1))==NULL)
+    {   _mm_errno = MMERR_SAMPLE_TOO_BIG;
+        return -1;
+    }
+    // read sample into buffer.
+    SL_Load(Samples[handle],sload,length);
+
+    // Unclick samples:
+
+    if(s->flags & SF_LOOP)
+    {   if(s->flags & SF_BIDI)
+            for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][(loopend-t)-1];
+        else
+            for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][t+loopstart];
+    } else
+        for(t=0; t<16; t++) Samples[handle][t+length] = 0;
+
+    return handle;
+}
+
+
+ULONG VC2_SampleSpace(int type)
+{
+    return vc_memory;
+}
+
+
+ULONG VC2_SampleLength(int type, SAMPLE *s)
+{
+    return (s->length * ((s->flags & SF_16BITS) ? 2 : 1)) + 16;
+}
+
+
+/**************************************************
+***************************************************
+***************************************************
+**************************************************/
+