Mercurial > ~darius > hgwebdir.cgi > mikmod
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 +}; +