#include "main.h" #include #define _RIFF 'FFIR' #define _MThd 'dhTM' #define _MTrk 'krTM' #define _RMID 'DIMR' #define _data 'atad' //#define BLAH static cfg_int cfg_loop_ctrl("cfg_loop_ctrl",255),cfg_loop_meta("cfg_loop_meta",255); class CGetInfo { public: MIDI_file * mf; int loop; int nch; int got_notes; int cur_track_start; int c_track,s_track; bool is_blah,f2; int max_ff_track,max_ff_num; CTempoMap *tmap,*ttmap; CSysexMap *smap; void CleanTempo(); int DoTrack(const BYTE* track,size_t size,string& name,int); bool Run(MIDI_file* mf); }; static bool memicmp(char* b1,char* b2,int s) { for(int n = 0; n < s; n++) { if (tolower(b1[n]) != tolower(b2[n])) return 1; } return 0; } static bool is_kar(char* ptr,int siz) //hack { siz -= 7;//strlen("karaoke"); for(int n = 0; n <= siz; n++) { // lameness to just prevent a crash on broken // files no idea what else it'll break though if ((int)(ptr+n) > siz) return 0; if (!memicmp(ptr+n,"karaoke",7)) return 1; } return 0; } extern cfg_int cfg_ff7loopz; BYTE ff7loopstart[12]={0xFF,6,9,'l','o','o','p','S','t','a','r','t'}; BYTE ff7loopend[10]={0xFF,6,7,'l','o','o','p','E','n','d'}; int CGetInfo::DoTrack(const BYTE* track,size_t size,string& name,int cpos) { int res=0,_res=0; size_t n=0; BYTE lc1=0,lastcom=0; bool run=0; int ff_num=0; while(n=sizeof(ff7loopstart) // bounds check && !memcmp(&track[n],ff7loopstart,sizeof(ff7loopstart))) { if (loop==-1) loop=res; } if ((UINT)track[n+1]==(UINT)cfg_loop_meta && loop==-1) loop=res; if (track[n+1]==0x51 && track[n+2]==0x03) //tempo { if (ttmap) ttmap->AddEntry(cpos+res,((DWORD)track[n+3]<<16)+((DWORD)track[n+4]<<8)+((DWORD)track[n+5])); n+=6; } else if (track[n+1]==0x2F && track[n+2]==0x00) { if (ff_num>max_ff_num) { max_ff_num=ff_num; max_ff_track=cur_track_start; } return _res; } else { DWORD _l=0,l1; UINT n1=0; { do { _l=(_l<<7)|(track[n+2+n1++]&0x7F); } while((n+1+n1< size) && track[n+1+n1]&0x80); } if (_l>255) l1=255; else l1=_l; if (track[n+1]<0x10) ff_num++; switch(track[n+1]) { case 6: // if (!cpr || *cpr) break; case 2: if (n + 1 + n1 + l1 >= size) return -1; mf->info.copyright.add_string_n((char*)(track+n+n1+2),l1); mf->info.copyright.add_string("\x0d\x0a"); break; case 5: is_blah=1; break; case 3: case 1: if (is_kar((char*)track+n+n1+2,_l)) is_blah=1; case 4: if (name.length()==0) { name.add_string_n((char*)(track+n+n1+2),l1); } break; } size_t n_increment; if (SizeTAdd(2, n1, &n_increment) != S_OK || SizeTAdd(n_increment, _l, &n_increment) != S_OK || SizeTAdd(n_increment, n, &n) != S_OK) return -1; } } else if ((track[n]&0xF0)==0xF0) { if (track[n]==0xF0) { _res=res; UINT s=ReadSysex(&track[n],size-n); smap->AddEvent(&track[n],s,cpos+res); n+=s; if (s_track==-1) s_track=c_track; else if (s_track!=c_track) s_track=-2; } else //hack... if (track[n]==0xF7) n++; else { #ifdef BLAH char tmp[32] = {0}; wsprintf(tmp,"invalid Fx event at %x",n); MessageBox(0,tmp,0,0); #endif return -1; } } else { lc1=track[n]; // if (lc1 == 0) return -1; if ((lc1&0x80)==0) { if (lastcom==0) return -1; run=1; lc1=lastcom; n--; } else run=0; _res=res; switch(lc1&0xF0) { case 0x80: case 0x90: if (!(got_notes&(1<<(lc1&0xF)))) { nch++; got_notes|=1<<(lc1&0xF); } case 0xB0: if (track[n+1]==cfg_loop_ctrl && loop==-1) loop=res; case 0xA0: case 0xE0: n+=3; lastcom=lc1; break; case 0xC0: case 0xD0: n+=2; lastcom=lc1; break; default: return -1; } } } return _res; } bool GetMidiInfo(MIDI_file* mf) { CGetInfo i; return i.Run(mf); } bool CGetInfo::Run(MIDI_file* _mf) { mf=_mf; nch=0; s_track=-1; is_blah=0; max_ff_track=max_ff_num=0; MIDIHEADER hd = *(MIDIHEADER*)(mf->data+8); tmap=tmap_create(); if (!tmap) return 0; smap=smap_create(); ttmap=0; mf->tmap=tmap;//avoid stupid memleaks mf->smap=smap; loop=-1; tmap->AddEntry(0,500000); DWORD sz = mf->size-14; FixHeader(hd); got_notes=0; nch=0; const BYTE* trax=mf->data+14; const BYTE* ntrak=trax; if (hd.trax>0x100 || hd.fmt>2) return 0; f2=hd.fmt==2; int n,tmp; int size=0; mf->info.traxnames = new string[hd.trax]; for(c_track=0;c_track=(UINT)sz || *((DWORD*)ntrak)!='krTM' || (tmp=rev32(*((DWORD*)ntrak+1)))+ntrak>sz+trax) return 0; cur_track_start=ntrak-mf->data; tmp=DoTrack(ntrak+8,tmp,mf->info.traxnames[c_track],f2 ? size : 0); if (tmp==-1) { /* ntrak[8]=0; ntrak[9]=0xFF; ntrak[10]=0x2F; ntrak[11]=0;*/ #ifdef BLAH { char e[128] = {0}; wsprintf(e,"Bad track #%u",c_track); MessageBox(0,e,ERROR,0); } #endif } else { if (f2) size+=tmp; else if (tmp>size) size=tmp; if (ttmap->pos) { mf->tmap=tmap=tmap_merge(tmap,ttmap); ttmap=0; } } ntrak+=rev32(*((DWORD*)ntrak+1))+8; } if (ttmap) delete ttmap; if (!tmap) return 0; mf->tix=MulDiv(size+50,768,hd.dtx); DWORDLONG res=0; for(n=0;npos-1 && tmap->data[n].posdata[n].tm,tmap->data[n+1].pos-tmap->data[n].pos); } if (tmap->data[n].posdata[n].tm,size-tmap->data[n].pos); mf->len=(DWORD)(res/(hd.dtx*1000)); if (loop!=-1 && looploopstart=loop; } mf->info.channels=nch; mf->info.fmt=hd.fmt; mf->info.ntrax=hd.trax; mf->info.tix=size; if (mf->loopstart) { mf->loopstart_t=mf->loopstart; mf->loopstart=MulDiv(mf->loopstart,768,hd.dtx); mf->loopend=MulDiv(size+15,768,hd.dtx); } else mf->loopstart_t=-1; if (!f2 && smap && s_track==-2) smap->CleanUp(); //todo: optimize this shit... /* mf->tmap=tmap; mf->smap=smap;*/ mf->info.e_type=smap->GetType(); if (is_blah) { mf->kar_track=max_ff_track; } return 1; }