/* MikMod sound library (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file AUTHORS for complete list. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*============================================================================== $Id$ UNIMOD (libmikmod's and APlayer's internal module format) loader ==============================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_MEMORY_H #include #endif #include #include "mikmod_internals.h" #ifdef SUNOS extern int fprintf(FILE *, const char *, ...); #endif /*========== Module structure */ typedef struct UNIHEADER { CHAR id[4]; UBYTE numchn; UWORD numpos; UWORD reppos; UWORD numpat; UWORD numtrk; UWORD numins; UWORD numsmp; UBYTE initspeed; UBYTE inittempo; UBYTE initvolume; UWORD flags; UBYTE numvoices; UWORD bpmlimit; UBYTE positions[256]; UBYTE panning[32]; } UNIHEADER; typedef struct UNISMP05 { UWORD c2spd; UWORD transpose; UBYTE volume; UBYTE panning; ULONG length; ULONG loopstart; ULONG loopend; UWORD flags; CHAR* samplename; UBYTE vibtype; UBYTE vibsweep; UBYTE vibdepth; UBYTE vibrate; } UNISMP05; /*========== Loader variables */ static UWORD universion; static UNIHEADER mh; #define UNI_SMPINCR 64 static UNISMP05 *wh=NULL,*s=NULL; /*========== Loader code */ static char * readstring(void) { char *str=NULL; UWORD len; len=_mm_read_I_UWORD(modreader); if(len) { str=(char *) MikMod_malloc(len+1); _mm_read_UBYTES(str,len,modreader); str[len]=0; } return str; } static int UNI_Test(void) { char id[6]; if(!_mm_read_UBYTES(id,6,modreader)) return 0; /* UNIMod created by MikCvt */ if(!(memcmp(id,"UN0",3))) { if((id[3]>='4')&&(id[3]<='6')) return 1; } /* UNIMod created by APlayer */ if(!(memcmp(id,"APUN\01",5))) { if((id[5]>=1)&&(id[5]<=6)) return 1; } return 0; } static int UNI_Init(void) { return 1; } static void UNI_Cleanup(void) { MikMod_free(wh); wh = s = NULL; } static UBYTE* readtrack(void) { UBYTE *t; UWORD len; int cur=0,chunk; if(universion>=6) len=_mm_read_M_UWORD(modreader); else len=_mm_read_I_UWORD(modreader); if(!len) return NULL; if(!(t=(UBYTE*)MikMod_malloc(len))) return NULL; _mm_read_UBYTES(t,len,modreader); /* Check if the track is correct */ while(1) { chunk=t[cur++]; if(!chunk) break; chunk=(chunk&0x1f)-1; while(chunk>0) { int opcode,oplen; if(cur>=len) { MikMod_free(t); return NULL; } opcode=t[cur]; /* Remap opcodes */ if (universion <= 5) { if (opcode > 29) { MikMod_free(t); return NULL; } switch (opcode) { /* UNI_NOTE .. UNI_S3MEFFECTQ are the same */ case 25: opcode = UNI_S3MEFFECTT; break; case 26: opcode = UNI_XMEFFECTA; break; case 27: opcode = UNI_XMEFFECTG; break; case 28: opcode = UNI_XMEFFECTH; break; case 29: opcode = UNI_XMEFFECTP; break; } } else { /* APlayer < 1.05 does not have XMEFFECT6 */ if (opcode >= UNI_XMEFFECT6 && universion < 0x105) opcode++; /* APlayer < 1.03 does not have ITEFFECTT */ if (opcode >= UNI_ITEFFECTT && universion < 0x103) opcode++; /* APlayer < 1.02 does not have ITEFFECTZ */ if (opcode >= UNI_ITEFFECTZ && universion < 0x102) opcode++; } if((!opcode)||(opcode>=UNI_LAST)) { MikMod_free(t); return NULL; } t[cur]=opcode; oplen=unioperands[opcode]+1; cur+=oplen; chunk-=oplen; } if((chunk<0)||(cur>=len)) { MikMod_free(t); return NULL; } } return t; } static int loadsmp6(void) { int t; SAMPLE *sptr; sptr=of.samples; for(t=0;tflags=0; if(flags&0x0004) sptr->flags|=SF_STEREO; if(flags&0x0002) sptr->flags|=SF_SIGNED; if(flags&0x0001) sptr->flags|=SF_16BITS; /* convert flags */ if(universion>=0x104) { if(flags&0x2000) sptr->flags|=SF_UST_LOOP; if(flags&0x1000) sptr->flags|=SF_OWNPAN; if(flags&0x0800) sptr->flags|=SF_SUSTAIN; if(flags&0x0400) sptr->flags|=SF_REVERSE; if(flags&0x0200) sptr->flags|=SF_BIDI; if(flags&0x0100) sptr->flags|=SF_LOOP; if(flags&0x0020) sptr->flags|=SF_ITPACKED; if(flags&0x0010) sptr->flags|=SF_DELTA; if(flags&0x0008) sptr->flags|=SF_BIG_ENDIAN; } else if(universion>=0x102) { if(flags&0x0800) sptr->flags|=SF_UST_LOOP; if(flags&0x0400) sptr->flags|=SF_OWNPAN; if(flags&0x0200) sptr->flags|=SF_SUSTAIN; if(flags&0x0100) sptr->flags|=SF_REVERSE; if(flags&0x0080) sptr->flags|=SF_BIDI; if(flags&0x0040) sptr->flags|=SF_LOOP; if(flags&0x0020) sptr->flags|=SF_ITPACKED; if(flags&0x0010) sptr->flags|=SF_DELTA; if(flags&0x0008) sptr->flags|=SF_BIG_ENDIAN; } else { if(flags&0x400) sptr->flags|=SF_UST_LOOP; if(flags&0x200) sptr->flags|=SF_OWNPAN; if(flags&0x100) sptr->flags|=SF_REVERSE; if(flags&0x080) sptr->flags|=SF_SUSTAIN; if(flags&0x040) sptr->flags|=SF_BIDI; if(flags&0x020) sptr->flags|=SF_LOOP; if(flags&0x010) sptr->flags|=SF_BIG_ENDIAN; if(flags&0x008) sptr->flags|=SF_DELTA; } sptr->speed = _mm_read_M_ULONG(modreader); sptr->volume = _mm_read_UBYTE(modreader); sptr->panning = _mm_read_M_UWORD(modreader); sptr->length = _mm_read_M_ULONG(modreader); sptr->loopstart = _mm_read_M_ULONG(modreader); sptr->loopend = _mm_read_M_ULONG(modreader); sptr->susbegin = _mm_read_M_ULONG(modreader); sptr->susend = _mm_read_M_ULONG(modreader); sptr->globvol = _mm_read_UBYTE(modreader); sptr->vibflags = _mm_read_UBYTE(modreader); sptr->vibtype = _mm_read_UBYTE(modreader); sptr->vibsweep = _mm_read_UBYTE(modreader); sptr->vibdepth = _mm_read_UBYTE(modreader); sptr->vibrate = _mm_read_UBYTE(modreader); sptr->samplename=readstring(); if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } } return 1; } static int loadinstr6(void) { int t,w; INSTRUMENT *i; i=of.instruments; for(t=0;tflags = _mm_read_UBYTE(modreader); i->nnatype = _mm_read_UBYTE(modreader); i->dca = _mm_read_UBYTE(modreader); i->dct = _mm_read_UBYTE(modreader); i->globvol = _mm_read_UBYTE(modreader); i->panning = _mm_read_M_UWORD(modreader); i->pitpansep = _mm_read_UBYTE(modreader); i->pitpancenter = _mm_read_UBYTE(modreader); i->rvolvar = _mm_read_UBYTE(modreader); i->rpanvar = _mm_read_UBYTE(modreader); i->volfade = _mm_read_M_UWORD(modreader); #if defined __STDC__ || defined _MSC_VER || defined __WATCOMC__ || defined MPW_C #define UNI_LoadEnvelope6(name) \ i-> name##flg=_mm_read_UBYTE(modreader); \ i-> name##pts=_mm_read_UBYTE(modreader); \ i-> name##susbeg=_mm_read_UBYTE(modreader); \ i-> name##susend=_mm_read_UBYTE(modreader); \ i-> name##beg=_mm_read_UBYTE(modreader); \ i-> name##end=_mm_read_UBYTE(modreader); \ for(w=0;w<(universion>=0x100?32:i-> name##pts);w++) { \ i-> name##env[w].pos=_mm_read_M_SWORD(modreader); \ i-> name##env[w].val=_mm_read_M_SWORD(modreader); \ } #else #define UNI_LoadEnvelope6(name) \ i-> name/**/flg=_mm_read_UBYTE(modreader); \ i-> name/**/pts=_mm_read_UBYTE(modreader); \ i-> name/**/susbeg=_mm_read_UBYTE(modreader); \ i-> name/**/susend=_mm_read_UBYTE(modreader); \ i-> name/**/beg=_mm_read_UBYTE(modreader); \ i-> name/**/end=_mm_read_UBYTE(modreader); \ for (w=0;w<(universion>=0x100?32:i-> name/**/pts);w++) { \ i-> name/**/env[w].pos=_mm_read_M_SWORD(modreader); \ i-> name/**/env[w].val=_mm_read_M_SWORD(modreader); \ } #endif UNI_LoadEnvelope6(vol); UNI_LoadEnvelope6(pan); UNI_LoadEnvelope6(pit); #undef UNI_LoadEnvelope6 if(universion>=0x103) _mm_read_M_UWORDS(i->samplenumber,120,modreader); else for(w=0;w<120;w++) i->samplenumber[w]=_mm_read_UBYTE(modreader); _mm_read_UBYTES(i->samplenote,120,modreader); i->insname=readstring(); if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } } return 1; } static int loadinstr5(void) { INSTRUMENT *i; int t; UWORD wavcnt=0; UBYTE vibtype,vibsweep,vibdepth,vibrate; i=of.instruments; for(of.numsmp=t=0;tsamplenumber,0xff,INSTNOTES*sizeof(UWORD)); for(u=0;u<96;u++) i->samplenumber[u]=of.numsmp+_mm_read_UBYTE(modreader); #if defined __STDC__ || defined _MSC_VER || defined __WATCOMC__ || defined MPW_C #define UNI_LoadEnvelope5(name) \ i-> name##flg=_mm_read_UBYTE(modreader); \ i-> name##pts=_mm_read_UBYTE(modreader); \ i-> name##susbeg=_mm_read_UBYTE(modreader); \ i-> name##susend=i-> name##susbeg; \ i-> name##beg=_mm_read_UBYTE(modreader); \ i-> name##end=_mm_read_UBYTE(modreader); \ for(u=0;u<12;u++) { \ i-> name##env[u].pos=_mm_read_I_SWORD(modreader); \ i-> name##env[u].val=_mm_read_I_SWORD(modreader); \ } #else #define UNI_LoadEnvelope5(name) \ i-> name/**/flg=_mm_read_UBYTE(modreader); \ i-> name/**/pts=_mm_read_UBYTE(modreader); \ i-> name/**/susbeg=_mm_read_UBYTE(modreader); \ i-> name/**/susend=i-> name/**/susbeg; \ i-> name/**/beg=_mm_read_UBYTE(modreader); \ i-> name/**/end=_mm_read_UBYTE(modreader); \ for(u=0;u<12;u++) { \ i-> name/**/env[u].pos=_mm_read_I_SWORD(modreader); \ i-> name/**/env[u].val=_mm_read_I_SWORD(modreader); \ } #endif UNI_LoadEnvelope5(vol); UNI_LoadEnvelope5(pan); #undef UNI_LoadEnvelope5 vibtype =_mm_read_UBYTE(modreader); vibsweep =_mm_read_UBYTE(modreader); vibdepth =_mm_read_UBYTE(modreader); vibrate =_mm_read_UBYTE(modreader); i->volfade=_mm_read_I_UWORD(modreader); i->insname=readstring(); for(u=0;uc2spd =_mm_read_I_UWORD(modreader); s->transpose=_mm_read_SBYTE(modreader); s->volume =_mm_read_UBYTE(modreader); s->panning =_mm_read_UBYTE(modreader); s->length =_mm_read_I_ULONG(modreader); s->loopstart=_mm_read_I_ULONG(modreader); s->loopend =_mm_read_I_ULONG(modreader); s->flags =_mm_read_I_UWORD(modreader); s->samplename=readstring(); s->vibtype =vibtype; s->vibsweep=vibsweep; s->vibdepth=vibdepth; s->vibrate =vibrate; if(_mm_eof(modreader)) { MikMod_free(wh);wh=NULL; _mm_errno=MMERR_LOADING_SAMPLEINFO; return 0; } } } /* sanity check */ if(!of.numsmp) { MikMod_free(wh);wh=NULL; _mm_errno=MMERR_LOADING_SAMPLEINFO; return 0; } return 1; } static int loadsmp5(void) { int t,u; SAMPLE *q; INSTRUMENT *d; q=of.samples;s=wh; for(u=0;usamplename=s->samplename; q->length =s->length; q->loopstart=s->loopstart; q->loopend =s->loopend; q->volume =s->volume; q->speed =s->c2spd; q->panning =s->panning; q->vibtype =s->vibtype; q->vibsweep =s->vibsweep; q->vibdepth =s->vibdepth; q->vibrate =s->vibrate; /* convert flags */ q->flags=0; if(s->flags&128) q->flags|=SF_REVERSE; if(s->flags& 64) q->flags|=SF_SUSTAIN; if(s->flags& 32) q->flags|=SF_BIDI; if(s->flags& 16) q->flags|=SF_LOOP; if(s->flags& 8) q->flags|=SF_BIG_ENDIAN; if(s->flags& 4) q->flags|=SF_DELTA; if(s->flags& 2) q->flags|=SF_SIGNED; if(s->flags& 1) q->flags|=SF_16BITS; } d=of.instruments;s=wh; for(u=0;usamplenote[t]=(d->samplenumber[t]>=of.numsmp)? 255:(t+s[d->samplenumber[t]].transpose); MikMod_free(wh);wh=NULL; return 1; } static int UNI_Load(int curious) { int t; char *modtype,*oldtype=NULL; INSTRUMENT *d; SAMPLE *q; (void)curious; /* read module header */ _mm_read_UBYTES(mh.id,4,modreader); if(mh.id[3]!='N') universion=mh.id[3]-'0'; else universion=0x100; if(universion>=6) { if (universion==6) { _mm_skip_BYTE(modreader); } else { universion=_mm_read_M_UWORD(modreader); } mh.flags =_mm_read_M_UWORD(modreader); mh.numchn =_mm_read_UBYTE(modreader); mh.numvoices =_mm_read_UBYTE(modreader); mh.numpos =_mm_read_M_UWORD(modreader); mh.numpat =_mm_read_M_UWORD(modreader); mh.numtrk =_mm_read_M_UWORD(modreader); mh.numins =_mm_read_M_UWORD(modreader); mh.numsmp =_mm_read_M_UWORD(modreader); mh.reppos =_mm_read_M_UWORD(modreader); mh.initspeed =_mm_read_UBYTE(modreader); mh.inittempo =_mm_read_UBYTE(modreader); mh.initvolume=_mm_read_UBYTE(modreader); /* I expect this to show up soon in APlayer 1.06 format */ if (universion >= 0x106) mh.bpmlimit=_mm_read_M_UWORD(modreader); else mh.bpmlimit=32; mh.flags &= UF_XMPERIODS | UF_LINEAR | UF_INST | UF_NNA; mh.flags |= UF_PANNING; } else { mh.numchn =_mm_read_UBYTE(modreader); mh.numpos =_mm_read_I_UWORD(modreader); mh.reppos =(universion==5)?_mm_read_I_UWORD(modreader):0; mh.numpat =_mm_read_I_UWORD(modreader); mh.numtrk =_mm_read_I_UWORD(modreader); mh.numins =_mm_read_I_UWORD(modreader); mh.initspeed =_mm_read_UBYTE(modreader); mh.inittempo =_mm_read_UBYTE(modreader); _mm_read_UBYTES(mh.positions,256,modreader); _mm_read_UBYTES(mh.panning,32,modreader); mh.flags =_mm_read_UBYTE(modreader); mh.bpmlimit =32; mh.flags &= UF_XMPERIODS | UF_LINEAR; mh.flags |= UF_INST | UF_NOWRAP | UF_PANNING; } /* set module parameters */ of.flags =mh.flags; of.numchn =mh.numchn; of.numpos =mh.numpos; of.numpat =mh.numpat; of.numtrk =mh.numtrk; of.numins =mh.numins; of.reppos =mh.reppos; of.initspeed =mh.initspeed; of.inittempo =mh.inittempo; if(mh.bpmlimit) of.bpmlimit=mh.bpmlimit; else /* be bug-compatible with older releases */ of.bpmlimit=32; of.songname=readstring(); if(universion<0x102) oldtype=readstring(); if(oldtype) { size_t len=strlen(oldtype)+20; if(!(modtype=(char*)MikMod_malloc(len))) return 0; #ifdef HAVE_SNPRINTF snprintf(modtype,len,"%s (was %s)",(universion>=0x100)?"APlayer":"MikCvt2",oldtype); #else sprintf(modtype,"%s (was %s)",(universion>=0x100)?"APlayer":"MikCvt2",oldtype); #endif } else { if(!(modtype=(char*)MikMod_malloc(10))) return 0; #ifdef HAVE_SNPRINTF snprintf(modtype,10,"%s",(universion>=0x100)?"APlayer":"MikCvt3"); #else sprintf(modtype,"%s",(universion>=0x100)?"APlayer":"MikCvt3"); #endif } of.modtype=MikMod_strdup(modtype); MikMod_free(modtype);MikMod_free(oldtype); of.comment=readstring(); if(universion>=6) { of.numvoices=mh.numvoices; of.initvolume=mh.initvolume; } if(_mm_eof(modreader)) { _mm_errno=MMERR_LOADING_HEADER; return 0; } /* positions */ if(!AllocPositions(of.numpos)) return 0; if(universion>=6) { if(universion>=0x100) _mm_read_M_UWORDS(of.positions,of.numpos,modreader); else for(t=0;t256)||(mh.numchn>32)) { _mm_errno=MMERR_LOADING_HEADER; return 0; } for(t=0;tof.numpat) { /* SANITIY CHECK */ /* fprintf(stderr,"position[%d]=%d > numpat=%d\n",t,of.positions[t],of.numpat);*/ _mm_errno = MMERR_LOADING_HEADER; return 0; } } /* instruments and samples */ if(universion>=6) { of.numsmp=mh.numsmp; if(!AllocSamples()) return 0; if(!loadsmp6()) return 0; if(of.flags&UF_INST) { if(!AllocInstruments()) return 0; if(!loadinstr6()) return 0; } } else { if(!AllocInstruments()) return 0; if(!loadinstr5()) return 0; if(!AllocSamples()) { MikMod_free(wh);wh=NULL; return 0; } if(!loadsmp5()) return 0; /* check if the original file had no instruments */ if(of.numsmp==of.numins) { for(t=0,d=of.instruments;tvolpts)||(d->panpts)||(d->globvol!=64)) break; for(u=0;u<96;u++) if((d->samplenumber[u]!=t)||(d->samplenote[u]!=u)) break; if(u!=96) break; } if(t==of.numins) { of.flags&=~UF_INST; of.flags&=~UF_NOWRAP; for(t=0,d=of.instruments,q=of.samples;tsamplename=d->insname; d->insname=NULL; } } } } /* patterns */ if(!AllocPatterns()) return 0; if(universion>=6) { _mm_read_M_UWORDS(of.pattrows,of.numpat,modreader); _mm_read_M_UWORDS(of.patterns,of.numpat*of.numchn,modreader); } else { _mm_read_I_UWORDS(of.pattrows,of.numpat,modreader); _mm_read_I_UWORDS(of.patterns,of.numpat*of.numchn,modreader); } /* tracks */ if(!AllocTracks()) return 0; for(t=0;t