diff playercode/load_it.c @ 4:5d614bcc4287

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:08 +0000
parents
children
line wrap: on
line diff
--- /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
+};
+