#include #include #include #include #include "mloader.h" #include "munitrk.h" typedef struct MMD0 { ULONG id; ULONG modlen; ULONG MMD0songP; // struct MMD0song *song; UWORD psecnum; /* for the player routine, MMD2 only */ UWORD pseq; /* " " " " */ ULONG MMD0BlockPP; // struct MMD0Block **blockarr; ULONG reserved1; ULONG InstrHdrPP; // struct InstrHdr **smplarr; ULONG reserved2; ULONG MMD0expP; // struct MMD0exp *expdata; ULONG reserved3; UWORD pstate; // some data for the player routine */ UWORD pblock; UWORD pline; UWORD pseqnum; WORD actplayline; UBYTE counter; UBYTE extra_songs; /* number of songs - 1 */ } MMD0; /* length = 52 bytes */ typedef struct MMD0sample { UWORD rep,replen; /* offs: 0(s), 2(s) */ UBYTE midich; /* offs: 4(s) */ UBYTE midipreset; /* offs: 5(s) */ UBYTE svol; /* offs: 6(s) */ BYTE strans; /* offs: 7(s) */ } MMD0sample; typedef struct MMD0song { MMD0sample sample[63]; /* 63 * 8 bytes = 504 bytes */ UWORD numblocks; /* offs: 504 */ UWORD songlen; /* offs: 506 */ UBYTE playseq[256]; /* offs: 508 */ UWORD deftempo; /* offs: 764 */ BYTE playtransp; /* offs: 766 */ UBYTE flags; /* offs: 767 */ UBYTE flags2; /* offs: 768 */ UBYTE tempo2; /* offs: 769 */ UBYTE trkvol[16]; /* offs: 770 */ UBYTE mastervol; /* offs: 786 */ UBYTE numsamples; /* offs: 787 */ } MMD0song; /* length = 788 bytes */ typedef struct MMD0NOTE{ UBYTE a,b,c; } MMD0NOTE; typedef struct MMD1NOTE{ UBYTE a,b,c,d; } MMD1NOTE; typedef struct InstrHdr { ULONG length; WORD type; /* Followed by actual data */ } InstrHdr; static MMD0 *mh; static MMD0song *ms; static ULONG *ba; static MMD0NOTE *mmd0pat; static MMD1NOTE *mmd1pat; #define d0note(row,col) mmd0pat[(row*(UWORD)of.numchn)+col] #define d1note(row,col) mmd1pat[(row*(UWORD)of.numchn)+col] char MED_Version[]="MED"; ULONG mlong(ULONG p) { return( ((p&0xff000000)>>24) | ((p&0x00ff0000)>>8) | ((p&0x0000ff00)<<8) | ((p&0x000000ff)<<24) ); } UWORD mword(UWORD p) { #ifdef __BORLANDC__ asm{ mov ax,p xchg al,ah } return _AX; #else return( ((p&0xff00)>>8) | ((p&0x00ff)<<8) ); #endif } #define clong(a) a=mlong(a) #define cword(a) a=mword(a) BOOL MED_Test(void) { char id[4]; rewind(modfp); if(!fread(id,4,1,modfp)) return 0; if(!memcmp(id,"MMD0",4)) return 1; if(!memcmp(id,"MMD1",4)) return 1; return 0; } BOOL MED_Init(void) { mh=NULL; ms=NULL; ba=NULL; // blockarr mmd0pat=NULL; mmd1pat=NULL; if(!(mh=(MMD0 *)MyCalloc(1,sizeof(MMD0)))) return 0; if(!(ms=(MMD0song *)MyCalloc(1,sizeof(MMD0song)))) return 0; return 1; } void MED_Cleanup(void) { if(mh!=NULL) free(mh); if(ms!=NULL) free(ms); if(ba!=NULL) free(ba); if(mmd0pat!=NULL) free(mmd0pat); if(mmd1pat!=NULL) free(mmd1pat); } void MMD02Intel(MMD0 *p) { clong(p->id); clong(p->modlen); clong(p->MMD0songP); // struct MMD0song *song; cword(p->psecnum); // for the player routine, MMD2 only cword(p->pseq); // " " " " clong(p->MMD0BlockPP); // struct MMD0Block **blockarr; clong(p->reserved1); clong(p->InstrHdrPP); // struct InstrHdr **smplarr; clong(p->reserved2); clong(p->MMD0expP); // struct MMD0exp *expdata; clong(p->reserved3); cword(p->pstate); // some data for the player routine cword(p->pblock); cword(p->pline); cword(p->pseqnum); cword(p->actplayline); } void MMD0song2Intel(MMD0song *p) { int t; for(t=0;t<63;t++){ cword(p->sample[t].rep); cword(p->sample[t].replen); } cword(p->numblocks); /* offs: 504 */ cword(p->songlen); /* offs: 506 */ cword(p->deftempo); /* offs: 764 */ } void EffectCvt(UBYTE eff,UBYTE dat) { switch(eff){ // 0x0 0x1 0x2 0x3 0x4 // PT effects case 0x5: // PT vibrato with speed/depth nibbles swapped UniPTEffect(0x4,(dat>>4) | ((dat&0xf)<<4) ); break; case 0x6: // not used case 0x7: // not used case 0x8: // midi hold/decay break; case 0x9: if(dat<=0x20) UniPTEffect(0xf,dat); break; // 0xa 0xb 0xc all PT effects case 0xd: // same as PT volslide UniPTEffect(0xa,dat); break; case 0xe: // synth jmp - midi break; case 0xf: // F00 does patternbreak with med if(dat==0) UniPTEffect(0xd,0); else if(dat<=0xa) UniPTEffect(0xf,dat); else if(dat<0xf1) UniPTEffect(0xf,((UWORD)dat*125)/33); else if(dat==0xff) UniPTEffect(0xc,0); // stop note break; default: // all normal PT effects are handled here :) UniPTEffect(eff,dat); break; } } UBYTE *MED_Convert1(int col) { int t; UBYTE a,b,c,d,inst,note,eff,dat; MMD1NOTE *n; UniReset(); for(t=0;t<64;t++){ n=&d1note(t,col); a=n->a; b=n->b; c=n->c; d=n->d; note=a&0x7f; inst=b&0x3f; eff=c&0xf; dat=d; if(inst!=0){ UniInstrument(inst-1); } if(note!=0){ UniNote(note+23); } EffectCvt(eff,dat); UniNewline(); } return UniDup(); } UBYTE *MED_Convert0(int col) { int t; UBYTE a,b,c,inst,note,eff,dat; MMD0NOTE *n; UniReset(); for(t=0;t<64;t++){ n=&d0note(t,col); a=n->a; b=n->b; c=n->c; note=a&0x3f; a>>=6; a=((a&1)<<1)|(a>>1); inst=(b>>4)|(a<<4); eff=b&0xf; dat=c; if(inst!=0){ UniInstrument(inst-1); } if(note!=0){ UniNote(note+35); } EffectCvt(eff,dat); UniNewline(); } return UniDup(); } BOOL LoadMMD0Patterns(void) { int t,row,col; UWORD numtracks,numlines,maxlines=0,track=0; // first, scan patterns to see how many channels are used for(t=0;tof.numchn) of.numchn=numtracks; if(numlines>maxlines) maxlines=numlines; } of.numtrk=of.numpat*of.numchn; if(!AllocTracks()) return 0; if(!AllocPatterns()) return 0; if(!(mmd0pat=(MMD0NOTE *)MyCalloc(of.numchn*(maxlines+1),sizeof(MMD0NOTE)))) return 0; /* second read: no more mr. nice guy, really read and convert patterns */ for(t=0;tof.numchn) of.numchn=numtracks; if(numlines>maxlines) maxlines=numlines; if(numlines>255){ puts("Can't load patterns > 256 rows"); return 0; } } of.numtrk=of.numpat*of.numchn; if(!AllocTracks()) return 0; if(!AllocPatterns()) return 0; if(!(mmd1pat=MyCalloc(of.numchn*(maxlines+1),sizeof(MMD1NOTE)))) return 0; /* second read: no more mr. nice guy, really read and convert patterns */ for(t=0;tMMD0songP,SEEK_SET); if(!fread(ms,sizeof(MMD0song),1,modfp)){ myerr=ERROR_LOADING_HEADER; return 0; } MMD0song2Intel(ms); // seek to samplepointer array fseek(modfp,mh->InstrHdrPP,SEEK_SET); // read sample array if(fread(sa,sizeof(ULONG),ms->numsamples,modfp)numsamples){ myerr=ERROR_LOADING_HEADER; return 0; } // alloc blockpointer array if(!(ba=MyCalloc(ms->numblocks,sizeof(ULONG)))) return 0; // seek to blockpointer array fseek(modfp,mh->MMD0BlockPP,SEEK_SET); // read blockpointer array if(fread(ba,sizeof(ULONG),ms->numblocks,modfp)numblocks){ myerr=ERROR_LOADING_HEADER; return 0; } // copy song positions for(t=0;tsonglen;t++){ of.positions[t]=ms->playseq[t]; } of.initspeed=6; of.inittempo=((UWORD)ms->deftempo*125)/33; of.modtype=strdup("MED"); of.numchn=0; // will be counted later of.numpat=ms->numblocks; of.numpos=ms->songlen; of.numins=ms->numsamples; if(!AllocInstruments()) return 0; d=of.instruments; for(t=0;tnumsmp=1; if(!AllocSamples(d)) return 0; q=d->samples; fseek(modfp,mlong(sa[t]),SEEK_SET); if(!fread(&s,sizeof(InstrHdr),1,modfp)){ myerr=ERROR_LOADING_SAMPLEINFO; return 0; } d->insname=NULL; q->length=mlong(s.length); q->seekpos=ftell(modfp); q->loopstart=ms->sample[t].rep<<1; q->loopend=q->loopstart+(ms->sample[t].replen<<1); q->flags=SF_SIGNED; q->c2spd=8363; q->volume=64; if(ms->sample[t].replen>1) q->flags|=SF_LOOP; // don't load sample if length>='MMD0' hah.. hah.. very funny.. NOT! if(q->length>=0x4d4d4430) q->length=0; d++; } if(mh->id==0x4D4D4430){ if(!LoadMMD0Patterns()) return 0; } else if(mh->id==0x4D4D4431){ if(!LoadMMD1Patterns()) return 0; } else{ puts("Can't load MMD2 yet"); return 0; } return 1; } LOADER medload={ NULL, "MED", "MED loader v0.1", MED_Init, MED_Test, MED_Load, MED_Cleanup };