/* 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$ Farandole (FAR) module 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 FARHEADER1 { UBYTE id[4]; /* file magic */ CHAR songname[40]; /* songname */ CHAR blah[3]; /* 13,10,26 */ UWORD headerlen; /* remaining length of header in bytes */ UBYTE version; UBYTE onoff[16]; UBYTE edit1[9]; UBYTE speed; UBYTE panning[16]; UBYTE edit2[4]; UWORD stlen; } FARHEADER1; typedef struct FARHEADER2 { UBYTE orders[256]; UBYTE numpat; UBYTE snglen; UBYTE loopto; UWORD patsiz[256]; } FARHEADER2; typedef struct FARSAMPLE { CHAR samplename[32]; ULONG length; UBYTE finetune; UBYTE volume; ULONG reppos; ULONG repend; UBYTE type; UBYTE loop; } FARSAMPLE; typedef struct FARNOTE { UBYTE note,ins,vol,eff; } FARNOTE; /*========== Loader variables */ static CHAR FAR_Version[] = "Farandole"; static FARHEADER1 *mh1 = NULL; static FARHEADER2 *mh2 = NULL; static FARNOTE *pat = NULL; static const unsigned char FARSIG[4+3]={'F','A','R',0xfe,13,10,26}; /*========== Loader code */ static int FAR_Test(void) { UBYTE id[47]; if(!_mm_read_UBYTES(id,47,modreader)) return 0; if((memcmp(id,FARSIG,4))||(memcmp(id+44,FARSIG+4,3))) return 0; return 1; } static int FAR_Init(void) { if(!(mh1 = (FARHEADER1*)MikMod_malloc(sizeof(FARHEADER1)))) return 0; if(!(mh2 = (FARHEADER2*)MikMod_malloc(sizeof(FARHEADER2)))) return 0; if(!(pat = (FARNOTE*)MikMod_malloc(256*16*4*sizeof(FARNOTE)))) return 0; return 1; } static void FAR_Cleanup(void) { MikMod_free(mh1); MikMod_free(mh2); MikMod_free(pat); mh1 = NULL; mh2 = NULL; pat = NULL; } static UBYTE *FAR_ConvertTrack(FARNOTE* n,int rows) { int t,vibdepth=1; UniReset(); for(t=0;tnote) { UniInstrument(n->ins); UniNote(n->note+3*OCTAVE-1); } if (n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2); if (n->eff) switch(n->eff>>4) { case 0x3: /* porta to note */ UniPTEffect(0x3,(n->eff&0xf)<<4); break; case 0x4: /* retrigger */ UniPTEffect(0x0e, 0x90 | (n->eff & 0x0f)); break; case 0x5: /* set vibrato depth */ vibdepth=n->eff&0xf; break; case 0x6: /* vibrato */ UniPTEffect(0x4,((n->eff&0xf)<<4)|vibdepth); break; case 0x7: /* volume slide up */ UniPTEffect(0xa,(n->eff&0xf)<<4); break; case 0x8: /* volume slide down */ UniPTEffect(0xa,n->eff&0xf); break; case 0xb: /* panning */ UniPTEffect(0xe,0x80|(n->eff&0xf)); break; case 0xf: /* set speed */ UniPTEffect(0xf,n->eff&0xf); break; /* others not yet implemented */ default: #ifdef MIKMOD_DEBUG fprintf(stderr,"\rFAR: unsupported effect %02X\n",n->eff); #endif break; } UniNewline(); n+=16; } return UniDup(); } static int FAR_Load(int curious) { int t,u,tracks=0; SAMPLE *q; FARSAMPLE s; FARNOTE *crow; UBYTE smap[8]; (void)curious; /* try to read module header (first part) */ _mm_read_UBYTES(mh1->id,4,modreader); _mm_read_SBYTES(mh1->songname,40,modreader); _mm_read_SBYTES(mh1->blah,3,modreader); mh1->headerlen = _mm_read_I_UWORD (modreader); mh1->version = _mm_read_UBYTE (modreader); _mm_read_UBYTES(mh1->onoff,16,modreader); _mm_read_UBYTES(mh1->edit1,9,modreader); mh1->speed = _mm_read_UBYTE(modreader); _mm_read_UBYTES(mh1->panning,16,modreader); _mm_read_UBYTES(mh1->edit2,4,modreader); mh1->stlen = _mm_read_I_UWORD (modreader); /* init modfile data */ of.modtype = MikMod_strdup(FAR_Version); of.songname = DupStr(mh1->songname,40,1); of.numchn = 16; of.initspeed = mh1->speed; of.inittempo = 80; of.reppos = 0; of.flags |= UF_PANNING; for(t=0;t<16;t++) of.panning[t]=mh1->panning[t]<<4; /* read songtext into comment field */ if(mh1->stlen) if (!ReadLinedComment(mh1->stlen, 132)) return 0; if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } /* try to read module header (second part) */ _mm_read_UBYTES(mh2->orders,256,modreader); mh2->numpat = _mm_read_UBYTE(modreader); mh2->snglen = _mm_read_UBYTE(modreader); mh2->loopto = _mm_read_UBYTE(modreader); _mm_read_I_UWORDS(mh2->patsiz,256,modreader); of.numpos = mh2->snglen; if(!AllocPositions(of.numpos)) return 0; for(t=0;torders[t]==0xff) break; of.positions[t] = mh2->orders[t]; } /* count number of patterns stored in file */ of.numpat = 0; for(t=0;t<256;t++) if(mh2->patsiz[t]) if((t+1)>of.numpat) of.numpat=t+1; of.numtrk = of.numpat*of.numchn; /* seek across eventual new data */ _mm_fseek(modreader,mh1->headerlen-(869+mh1->stlen),SEEK_CUR); /* alloc track and pattern structures */ if(!AllocTracks()) return 0; if(!AllocPatterns()) return 0; for(t=0;tpatsiz[t]) { rows = _mm_read_UBYTE(modreader); tempo = _mm_read_UBYTE(modreader); (void)tempo; /* unused */ crow = pat; /* file often allocates 64 rows even if there are less in pattern */ if (mh2->patsiz[t]<2+(rows*16*4)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } for(u=(mh2->patsiz[t]-2)/4;u;u--,crow++) { crow->note = _mm_read_UBYTE(modreader); crow->ins = _mm_read_UBYTE(modreader); crow->vol = _mm_read_UBYTE(modreader); crow->eff = _mm_read_UBYTE(modreader); } if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } crow=pat; of.pattrows[t] = rows; for(u=16;u;u--,crow++) if(!(of.tracks[tracks++]=FAR_ConvertTrack(crow,rows))) { _mm_errno=MMERR_LOADING_PATTERN; return 0; } } else tracks+=16; } /* read sample map */ if(!_mm_read_UBYTES(smap,8,modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } /* count number of samples used */ of.numins = 0; for(t=0;t<64;t++) if(smap[t>>3]&(1<<(t&7))) of.numins=t+1; of.numsmp = of.numins; /* alloc sample structs */ if(!AllocSamples()) return 0; q = of.samples; for(t=0;tspeed = 8363; q->flags = SF_SIGNED; if(smap[t>>3]&(1<<(t&7))) { _mm_read_SBYTES(s.samplename,32,modreader); s.length = _mm_read_I_ULONG(modreader); s.finetune = _mm_read_UBYTE(modreader); s.volume = _mm_read_UBYTE(modreader); s.reppos = _mm_read_I_ULONG(modreader); s.repend = _mm_read_I_ULONG(modreader); s.type = _mm_read_UBYTE(modreader); s.loop = _mm_read_UBYTE(modreader); q->samplename = DupStr(s.samplename,32,1); q->length = s.length; q->loopstart = s.reppos; q->loopend = s.repend; q->volume = s.volume<<2; if(s.type&1) q->flags|=SF_16BITS; if(s.loop&8) q->flags|=SF_LOOP; q->seekpos = _mm_ftell(modreader); _mm_fseek(modreader,q->length,SEEK_CUR); } else q->samplename = MikMod_strdup(""); q++; } return 1; } static CHAR *FAR_LoadTitle(void) { CHAR s[40]; _mm_fseek(modreader,4,SEEK_SET); if(!_mm_read_UBYTES(s,40,modreader)) return NULL; return (DupStr(s,40,1)); } /*========== Loader information */ MIKMODAPI MLOADER load_far={ NULL, "FAR", "FAR (Farandole Composer)", FAR_Init, FAR_Test, FAR_Load, FAR_Cleanup, FAR_LoadTitle }; /* ex:set ts=4: */