📄 load_xm.c
字号:
/* 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: load_xm.c,v 1.2 2004/02/06 19:29:03 raph Exp $
Fasttracker (XM) module loader
==============================================================================*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <string.h>
#include "mikmod_internals.h"
#ifdef SUNOS
extern int fprintf(FILE *, const char *, ...);
#endif
/*========== Module structure */
typedef struct XMHEADER {
CHAR id[17]; /* ID text: 'Extended module: ' */
CHAR songname[21]; /* Module name */
CHAR trackername[20]; /* Tracker name */
UWORD version; /* Version number */
ULONG headersize; /* Header size */
UWORD songlength; /* Song length (in patten order table) */
UWORD restart; /* Restart position */
UWORD numchn; /* Number of channels (2,4,6,8,10,...,32) */
UWORD numpat; /* Number of patterns (max 256) */
UWORD numins; /* Number of instruments (max 128) */
UWORD flags;
UWORD tempo; /* Default tempo */
UWORD bpm; /* Default BPM */
UBYTE orders[256]; /* Pattern order table */
} XMHEADER;
typedef struct XMINSTHEADER {
ULONG size; /* Instrument size */
CHAR name[22]; /* Instrument name */
UBYTE type; /* Instrument type (always 0) */
UWORD numsmp; /* Number of samples in instrument */
ULONG ssize;
} XMINSTHEADER;
#define XMENVCNT (12*2)
#define XMNOTECNT (8*OCTAVE)
typedef struct XMPATCHHEADER {
UBYTE what[XMNOTECNT]; /* Sample number for all notes */
UWORD volenv[XMENVCNT]; /* Points for volume envelope */
UWORD panenv[XMENVCNT]; /* Points for panning envelope */
UBYTE volpts; /* Number of volume points */
UBYTE panpts; /* Number of panning points */
UBYTE volsus; /* Volume sustain point */
UBYTE volbeg; /* Volume loop start point */
UBYTE volend; /* Volume loop end point */
UBYTE pansus; /* Panning sustain point */
UBYTE panbeg; /* Panning loop start point */
UBYTE panend; /* Panning loop end point */
UBYTE volflg; /* Volume type: bit 0: On; 1: Sustain; 2: Loop */
UBYTE panflg; /* Panning type: bit 0: On; 1: Sustain; 2: Loop */
UBYTE vibflg; /* Vibrato type */
UBYTE vibsweep; /* Vibrato sweep */
UBYTE vibdepth; /* Vibrato depth */
UBYTE vibrate; /* Vibrato rate */
UWORD volfade; /* Volume fadeout */
} XMPATCHHEADER;
typedef struct XMWAVHEADER {
ULONG length; /* Sample length */
ULONG loopstart; /* Sample loop start */
ULONG looplength; /* Sample loop length */
UBYTE volume; /* Volume */
SBYTE finetune; /* Finetune (signed byte -128..+127) */
UBYTE type; /* Loop type */
UBYTE panning; /* Panning (0-255) */
SBYTE relnote; /* Relative note number (signed byte) */
UBYTE reserved;
CHAR samplename[22]; /* Sample name */
UBYTE vibtype; /* Vibrato type */
UBYTE vibsweep; /* Vibrato sweep */
UBYTE vibdepth; /* Vibrato depth */
UBYTE vibrate; /* Vibrato rate */
} XMWAVHEADER;
typedef struct XMPATHEADER {
ULONG size; /* Pattern header length */
UBYTE packing; /* Packing type (always 0) */
UWORD numrows; /* Number of rows in pattern (1..256) */
SWORD packsize; /* Packed patterndata size */
} XMPATHEADER;
typedef struct XMNOTE {
UBYTE note,ins,vol,eff,dat;
} XMNOTE;
/*========== Loader variables */
static XMNOTE *xmpat=NULL;
static XMHEADER *mh=NULL;
/* increment unit for sample array reallocation */
#define XM_SMPINCR 64
static ULONG *nextwav=NULL;
static XMWAVHEADER *wh=NULL,*s=NULL;
/*========== Loader code */
BOOL XM_Test(void)
{
UBYTE id[38];
if(!_mm_read_UBYTES(id,38,modreader)) return 0;
if(memcmp(id,"Extended Module: ",17)) return 0;
if(id[37]==0x1a) return 1;
return 0;
}
BOOL XM_Init(void)
{
if(!(mh=(XMHEADER *)_mm_malloc(sizeof(XMHEADER)))) return 0;
return 1;
}
void XM_Cleanup(void)
{
_mm_free(mh);
}
static int XM_ReadNote(XMNOTE* n)
{
UBYTE cmp,result=1;
memset(n,0,sizeof(XMNOTE));
cmp=_mm_read_UBYTE(modreader);
if(cmp&0x80) {
if(cmp&1) { result++;n->note = _mm_read_UBYTE(modreader); }
if(cmp&2) { result++;n->ins = _mm_read_UBYTE(modreader); }
if(cmp&4) { result++;n->vol = _mm_read_UBYTE(modreader); }
if(cmp&8) { result++;n->eff = _mm_read_UBYTE(modreader); }
if(cmp&16) { result++;n->dat = _mm_read_UBYTE(modreader); }
} else {
n->note = cmp;
n->ins = _mm_read_UBYTE(modreader);
n->vol = _mm_read_UBYTE(modreader);
n->eff = _mm_read_UBYTE(modreader);
n->dat = _mm_read_UBYTE(modreader);
result += 4;
}
return result;
}
static UBYTE* XM_Convert(XMNOTE* xmtrack,UWORD rows)
{
int t;
UBYTE note,ins,vol,eff,dat;
UniReset();
for(t=0;t<rows;t++) {
note = xmtrack->note;
ins = xmtrack->ins;
vol = xmtrack->vol;
eff = xmtrack->eff;
dat = xmtrack->dat;
if(note) {
if(note>XMNOTECNT)
UniEffect(UNI_KEYFADE,0);
else
UniNote(note-1);
}
if(ins) UniInstrument(ins-1);
switch(vol>>4) {
case 0x6: /* volslide down */
if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol&0xf);
break;
case 0x7: /* volslide up */
if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol<<4);
break;
/* volume-row fine volume slide is compatible with protracker
EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as
opposed to 'take the last sliding value'. */
case 0x8: /* finevol down */
UniPTEffect(0xe,0xb0|(vol&0xf));
break;
case 0x9: /* finevol up */
UniPTEffect(0xe,0xa0|(vol&0xf));
break;
case 0xa: /* set vibrato speed */
UniEffect(UNI_XMEFFECT4,vol<<4);
break;
case 0xb: /* vibrato */
UniEffect(UNI_XMEFFECT4,vol&0xf);
break;
case 0xc: /* set panning */
UniPTEffect(0x8,vol<<4);
break;
case 0xd: /* panning slide left (only slide when data not zero) */
if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol&0xf);
break;
case 0xe: /* panning slide right (only slide when data not zero) */
if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol<<4);
break;
case 0xf: /* tone porta */
UniPTEffect(0x3,vol<<4);
break;
default:
if((vol>=0x10)&&(vol<=0x50))
UniPTEffect(0xc,vol-0x10);
}
switch(eff) {
case 0x4:
UniEffect(UNI_XMEFFECT4,dat);
break;
case 0x6:
UniEffect(UNI_XMEFFECT6,dat);
break;
case 0xa:
UniEffect(UNI_XMEFFECTA,dat);
break;
case 0xe: /* Extended effects */
switch(dat>>4) {
case 0x1: /* XM fine porta up */
UniEffect(UNI_XMEFFECTE1,dat&0xf);
break;
case 0x2: /* XM fine porta down */
UniEffect(UNI_XMEFFECTE2,dat&0xf);
break;
case 0xa: /* XM fine volume up */
UniEffect(UNI_XMEFFECTEA,dat&0xf);
break;
case 0xb: /* XM fine volume down */
UniEffect(UNI_XMEFFECTEB,dat&0xf);
break;
default:
UniPTEffect(eff,dat);
}
break;
case 'G'-55: /* G - set global volume */
UniEffect(UNI_XMEFFECTG,dat>64?128:dat<<1);
break;
case 'H'-55: /* H - global volume slide */
UniEffect(UNI_XMEFFECTH,dat);
break;
case 'K'-55: /* K - keyOff and KeyFade */
UniEffect(UNI_KEYFADE,dat);
break;
case 'L'-55: /* L - set envelope position */
UniEffect(UNI_XMEFFECTL,dat);
break;
case 'P'-55: /* P - panning slide */
UniEffect(UNI_XMEFFECTP,dat);
break;
case 'R'-55: /* R - multi retrig note */
UniEffect(UNI_S3MEFFECTQ,dat);
break;
case 'T'-55: /* T - Tremor */
UniEffect(UNI_S3MEFFECTI,dat);
break;
case 'X'-55:
switch(dat>>4) {
case 1: /* X1 - Extra Fine Porta up */
UniEffect(UNI_XMEFFECTX1,dat&0xf);
break;
case 2: /* X2 - Extra Fine Porta down */
UniEffect(UNI_XMEFFECTX2,dat&0xf);
break;
}
break;
default:
if(eff<=0xf) {
/* the pattern jump destination is written in decimal,
but it seems some poor tracker software writes them
in hexadecimal... (sigh) */
if (eff==0xd)
/* don't change anything if we're sure it's in hexa */
if ((((dat&0xf0)>>4)<=9)&&((dat&0xf)<=9))
/* otherwise, convert from dec to hex */
dat=(((dat&0xf0)>>4)*10)+(dat&0xf);
UniPTEffect(eff,dat);
}
break;
}
UniNewline();
xmtrack++;
}
return UniDup();
}
static BOOL LoadPatterns(BOOL dummypat)
{
int t,u,v,numtrk;
if(!AllocTracks()) return 0;
if(!AllocPatterns()) return 0;
numtrk=0;
for(t=0;t<mh->numpat;t++) {
XMPATHEADER ph;
ph.size =_mm_read_I_ULONG(modreader);
if (ph.size<(mh->version==0x0102?8:9)) {
_mm_errno=MMERR_LOADING_PATTERN;
return 0;
}
ph.packing =_mm_read_UBYTE(modreader);
if(ph.packing) {
_mm_errno=MMERR_LOADING_PATTERN;
return 0;
}
if(mh->version==0x0102)
ph.numrows =_mm_read_UBYTE(modreader)+1;
else
ph.numrows =_mm_read_I_UWORD(modreader);
ph.packsize =_mm_read_I_UWORD(modreader);
ph.size-=(mh->version==0x0102?8:9);
if(ph.size)
_mm_fseek(modreader,ph.size,SEEK_CUR);
of.pattrows[t]=ph.numrows;
if(ph.numrows) {
if(!(xmpat=(XMNOTE*)_mm_calloc(ph.numrows*of.numchn,sizeof(XMNOTE))))
return 0;
/* when packsize is 0, don't try to load a pattern.. it's empty. */
if(ph.packsize)
for(u=0;u<ph.numrows;u++)
for(v=0;v<of.numchn;v++) {
if(!ph.packsize) break;
ph.packsize-=XM_ReadNote(&xmpat[(v*ph.numrows)+u]);
if(ph.packsize<0) {
free(xmpat);xmpat=NULL;
_mm_errno=MMERR_LOADING_PATTERN;
return 0;
}
}
if(ph.packsize) {
_mm_fseek(modreader,ph.packsize,SEEK_CUR);
}
if(_mm_eof(modreader)) {
free(xmpat);xmpat=NULL;
_mm_errno=MMERR_LOADING_PATTERN;
return 0;
}
for(v=0;v<of.numchn;v++)
of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);
free(xmpat);xmpat=NULL;
} else {
for(v=0;v<of.numchn;v++)
of.tracks[numtrk++]=XM_Convert(NULL,ph.numrows);
}
}
if(dummypat) {
of.pattrows[t]=64;
if(!(xmpat=(XMNOTE*)_mm_calloc(64*of.numchn,sizeof(XMNOTE)))) return 0;
for(v=0;v<of.numchn;v++)
of.tracks[numtrk++]=XM_Convert(&xmpat[v*64],64);
free(xmpat);xmpat=NULL;
}
return 1;
}
static void FixEnvelope(ENVPT *cur, int pts)
{
int u, old, tmp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -