/*

 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"

/**************************************************************************
**************************************************************************/

// Defined in MPLAYER.C, used to convert c4spd to linear XM periods
extern UWORD getlinearperiod(UBYTE note, ULONG fine);
extern ULONG getfrequency(UBYTE flags, ULONG period);


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, with _mm_error set.
{
   int  row=0,flag,ch;

   do
   {  flag = _mm_read_UBYTE(modfp);
      if(flag == EOF)
      {   _mm_error = MLERR_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_error = MLERR_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);        // <- normal 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 down (65-74)
        {   UniVolEffect(VOL_VOLSLIDE,0xf0 + (volpan-65));
        } else if(volpan>=75 && volpan<=84)        // fine volume slide up   (75-84)
        {   UniVolEffect(VOL_VOLSLIDE,0x0f + (volpan-75)<<4);
        } else if(volpan>=85 && volpan<=94)        // volume slide down (85-94)
        {   UniVolEffect(VOL_VOLSLIDE,(volpan-85));
        } else if(volpan>=95 && volpan<=104)       // volume slide up   (95-104)
        {   UniVolEffect(VOL_VOLSLIDE,(volpan-95)<<4);
        } else if(volpan>=105 && volpan<=114)      // pitch slide down (105-114)
        {   UniVolEffect(VOL_PITCHSLIDEDN,(volpan-105));
        } else if(volpan>=115 && volpan<=124)      // pitch slide up (115-124)
        {   UniVolEffect(VOL_PITCHSLIDEUP,(volpan-115)<<4);
        } 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_error = MLERR_LOADING_HEADER;
        return 0;
    }

    // set module variables

    IT_Version[15] = (mh->cwt >> 8) + 0x30;
    IT_Version[17] = ((mh->cwt >> 4)&0xf) + 0x30;
    IT_Version[18] = ((mh->cwt)&0xf) + 0x30;
    of.modtype   = strdup(IT_Version);
    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.volume    = 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(!_mm_read_UBYTES(of.positions,mh->ordnum,modfp))
    {   _mm_error = MLERR_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_error = MLERR_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;
        //of.commenttype = COMMENT_IT;
    }

    if(!(mh->flags & 4)) of.numins = of.numsmp;
    if(!AllocInstruments()) return 0;
    if(!AllocSamples()) return 0;

    if((noteindex=(int *)_mm_malloc(mh->smpnum*sizeof(int)))==NULL) return 0;
        
    d = of.instruments;
    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);

       q->samplename = DupStr(s.sampname,26);

       q->c2spd      = 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 c2spd to XM linear finetune

       if(of.flags&UF_LINEAR)
           q->c2spd = 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;   
       }
                              
       d++; q++;
    }

    // Load instruments if instrument mode flag enabled

    d = of.instruments;
    if(mh->flags & 4)            
    {   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);
            }
 
            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++;                  
        }
    }

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