view 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 source

/*

 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
};