588 lines
9.9 KiB
C++
588 lines
9.9 KiB
C++
#include "main.h"
|
|
|
|
#pragma warning(disable:4200)
|
|
|
|
extern BYTE ff7loopstart[12];
|
|
extern BYTE ff7loopend[10];
|
|
|
|
extern cfg_int cfg_hack_xg_drums, cfg_hack_dls_instruments, cfg_hack_dls_drums, cfg_ff7loopz;
|
|
|
|
typedef union
|
|
{
|
|
BYTE b[4];
|
|
DWORD dw;
|
|
} b_dw;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD pos, tm, sz;
|
|
BYTE le;
|
|
BYTE data[];
|
|
}
|
|
TRACK;
|
|
|
|
DWORD _fastcall rev32(DWORD);
|
|
//WORD _fastcall rev16(WORD);
|
|
|
|
|
|
|
|
int test_drum_kit(DWORD no, IDirectMusicCollection* dls);
|
|
void do_dls_check(DWORD * i, IDirectMusicCollection * dls);
|
|
|
|
|
|
class CCleaner
|
|
{
|
|
public:
|
|
INSTRUMENT_DESC* instr, **instr_ptr;
|
|
BYTE ctab[16][128];
|
|
// UINT dm_vol;
|
|
grow_buf outbuf;
|
|
UINT ntrax, ntrax1;
|
|
UINT maxvol;
|
|
TRACK** in_trax;
|
|
TRACK* out_trax[16];
|
|
DWORD ct;
|
|
UINT tf;
|
|
MIDI_file* mf;
|
|
DWORD vol_set;
|
|
|
|
bool drumfix, insfix;
|
|
b_dw ins[16], ins_set[16];
|
|
|
|
bool f2, tr1, dm, only_ins, ins_no_lsb;
|
|
bool hasnotes[16];
|
|
void check_ins(UINT msb, UINT lsb, UINT patch, UINT note, BOOL drum, UINT ch) //called on note
|
|
{
|
|
if (ins_no_lsb) lsb = 0;
|
|
INSTRUMENT_DESC * d = instr;
|
|
while (d)
|
|
{
|
|
if (d->bank_hi == msb && d->bank_lo == lsb && d->patch == patch && d->drum == drum) break;
|
|
d = d->next;
|
|
}
|
|
if (d)
|
|
{
|
|
d->count++;
|
|
if (d->note_max < note) d->note_max = note;
|
|
if (d->note_min > note) d->note_min = note;
|
|
d->channels |= 1 << ch;
|
|
}
|
|
else
|
|
{
|
|
d = new INSTRUMENT_DESC;
|
|
*instr_ptr = d;
|
|
instr_ptr = &d->next;
|
|
d->next = 0;
|
|
d->note_min = d->note_max = note;
|
|
d->bank_hi = msb;
|
|
d->bank_lo = lsb;
|
|
d->patch = patch;
|
|
d->count = 1;
|
|
d->drum = drum;
|
|
d->user = 0;
|
|
d->channels = 1 << ch;
|
|
}
|
|
}
|
|
void AdvanceTime(TRACK* t);
|
|
void AddEvent(BYTE ev, BYTE* data);
|
|
void WriteTrack(TRACK* t);
|
|
int Run(MIDI_file* mf, DWORD, void ** out_data, int * out_size);
|
|
|
|
void do_shit(UINT n);
|
|
|
|
UINT get_next_time()
|
|
{
|
|
UINT t = -1;
|
|
UINT n;
|
|
for (n = 0;n < ntrax;n++)
|
|
{
|
|
UINT t1 = in_trax[n]->tm;
|
|
if (t1 < t) t = t1;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
BOOL event_ok(BYTE e, BYTE* p)
|
|
{
|
|
BYTE _c = e & 0xF0;
|
|
BYTE ch = e & 0xF;
|
|
if (_c == 0xB0)
|
|
{
|
|
if (cfg_hack_xg_drums && ch == 9 && p[1] == 0 && (p[0] == 0 || p[0] == 0x20))
|
|
return 0;
|
|
|
|
if (p[0] > 127)
|
|
return 0;
|
|
|
|
ctab[ch][p[0]] = p[1];
|
|
|
|
|
|
if (p[0] == 0)
|
|
{
|
|
ins[ch].b[2] = p[1];
|
|
if (insfix) return 0;
|
|
}
|
|
if (p[0] == 0x20)
|
|
{
|
|
ins[ch].b[1] = p[1];
|
|
if (insfix) return 0;
|
|
}
|
|
|
|
if (dm) //keep dm drivers happy...
|
|
{
|
|
if (p[0] >= 0x20 && p[0] < 0x40) //lsb values
|
|
{
|
|
return 0;
|
|
}
|
|
else if (p[0] < 0x20)
|
|
{
|
|
BYTE data[2] = {(BYTE)(p[0] + 0x20),ctab[ch][p[0] + 0x20]};
|
|
AddEvent(e, data);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (_c == 0xC0)
|
|
{
|
|
if (ch == 9)
|
|
{
|
|
if (drumfix && !test_drum_kit(p[0], mf->pDLS)) return 0;
|
|
ins[ch].b[0] = p[0];
|
|
}
|
|
else
|
|
{
|
|
ins[ch].b[0] = p[0];
|
|
if (insfix) return 0;
|
|
}
|
|
}
|
|
else if (_c == 0x90 && p[1])
|
|
{
|
|
if (only_ins)
|
|
check_ins(ins[ch].b[2], ins[ch].b[1], ins[ch].b[0], p[0], ch == 9, ch);
|
|
if (ch != 9 && insfix)
|
|
{
|
|
if (ins_set[ch].dw != ins[ch].dw)
|
|
{
|
|
do_dls_check(&ins[ch].dw, mf->pDLS);
|
|
|
|
|
|
if (ins_set[ch].b[1] != ins[ch].b[1])
|
|
{
|
|
BYTE t[2] = {0x20, ins[ch].b[1]};
|
|
AddEvent(0xB0 | ch, t);
|
|
}
|
|
|
|
if (ins_set[ch].b[2] != ins[ch].b[2])
|
|
{
|
|
BYTE t[2] = {0, ins[ch].b[2]};
|
|
AddEvent(0xB0 | ch, t);
|
|
}
|
|
AddEvent(0xC0 | ch, ins[ch].b);
|
|
|
|
ins_set[ch].dw = ins[ch].dw;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
CCleaner()
|
|
{
|
|
memset(ins, 0, sizeof(ins));
|
|
memset(ins_set, -1, sizeof(ins_set));
|
|
memset(hasnotes, 0, sizeof(hasnotes));
|
|
memset(out_trax, 0, sizeof(out_trax));
|
|
in_trax = 0;
|
|
}
|
|
~CCleaner()
|
|
{
|
|
UINT n;
|
|
if (in_trax)
|
|
{
|
|
for (n = 0;n < ntrax;n++)
|
|
if (in_trax[n]) {free(in_trax[n]);in_trax[n] = 0;}
|
|
free(in_trax);
|
|
}
|
|
for (n = 0;n < 16;n++)
|
|
{
|
|
if (out_trax[n])
|
|
{
|
|
free(out_trax[n]);
|
|
out_trax[n] = 0;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void CCleaner::do_shit(UINT n)
|
|
{
|
|
BYTE ce = 0;
|
|
TRACK* t = in_trax[n];
|
|
if (!t) return ;
|
|
while (t->tm == ct)
|
|
{
|
|
|
|
if (t->pos >= t->sz)
|
|
{
|
|
t->pos = -1;
|
|
t->tm = -1;
|
|
tf++;
|
|
break;
|
|
}
|
|
BYTE c0 = t->data[t->pos];
|
|
if (c0 == 0xFF) //Meta-events
|
|
{
|
|
|
|
if (cfg_ff7loopz
|
|
&& (t->sz - t->pos) >= sizeof(ff7loopend) // bounds check
|
|
&& !memcmp(t->data + t->pos, ff7loopend, sizeof(ff7loopend)))
|
|
{
|
|
// MessageBox(GetActiveWindow(),"blah",0,0);
|
|
// AdvanceTime(t);
|
|
tf = ntrax;
|
|
// return;
|
|
}
|
|
BYTE c1 = t->data[t->pos + 1];
|
|
if (c1 == 0x2F)
|
|
{
|
|
t->pos += 3;
|
|
t->tm = -1;
|
|
tf++;
|
|
}
|
|
{
|
|
t->pos += 2;
|
|
if (t->pos < t->sz)
|
|
{
|
|
|
|
unsigned int _d;
|
|
t->pos += DecodeDelta(t->data + t->pos, &_d, t->sz - t->pos);
|
|
t->pos += _d;
|
|
}
|
|
}
|
|
} else if (c0 == 0xF0)
|
|
{
|
|
t->pos += ReadSysex(&t->data[t->pos], t->sz - t->pos);
|
|
}
|
|
else if (c0 == 0xF7) t->pos++;
|
|
else if ((c0&0xF0) == 0xF0) //WTF?
|
|
{
|
|
t->pos = -1;
|
|
t->tm = -1;
|
|
tf++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (c0&0x80)
|
|
{
|
|
ce = c0;
|
|
t->pos++;
|
|
}
|
|
else ce = t->le;
|
|
|
|
if (event_ok(ce, &t->data[t->pos])) AddEvent(ce, &t->data[t->pos]);
|
|
|
|
if ((ce&0xF0) == 0xC0 || (ce&0xF0) == 0xD0) t->pos++;
|
|
else t->pos += 2;
|
|
t->le = ce;
|
|
}
|
|
|
|
if (t->tm != -1 && t->pos >= t->sz)
|
|
{
|
|
t->pos = -1;
|
|
t->tm = -1;
|
|
tf++;
|
|
break;
|
|
}
|
|
AdvanceTime(t);
|
|
}
|
|
}
|
|
|
|
#define WriteBuf(A,B) outbuf.write(A,B)
|
|
|
|
#pragma pack(push)
|
|
#pragma pack(1)
|
|
typedef struct
|
|
{
|
|
WORD t, n, d;
|
|
}
|
|
MHD;
|
|
typedef struct
|
|
{
|
|
DWORD c, s;
|
|
}
|
|
CHD;
|
|
#pragma pack(pop)
|
|
|
|
|
|
void CCleaner::AdvanceTime(TRACK* t)
|
|
{
|
|
if (t->tm != -1)
|
|
{
|
|
unsigned int d;
|
|
UINT _n = DecodeDelta(t->data + t->pos, &d, t->sz - t->pos);
|
|
if (_n < 4) t->tm += d;
|
|
t->pos += _n;
|
|
}
|
|
}
|
|
|
|
void CCleaner::AddEvent(BYTE ev, BYTE* data)
|
|
{
|
|
if (only_ins) return ;
|
|
BYTE nt = ev & 0xF;
|
|
BYTE ec = ev & 0xF0;
|
|
if (tr1) nt = 0;
|
|
TRACK *t = out_trax[nt];
|
|
|
|
ZeroMemory(ctab, sizeof(ctab));
|
|
|
|
|
|
if (!t)
|
|
{
|
|
t = out_trax[nt] = (TRACK*)malloc(sizeof(TRACK) + 0x1000);
|
|
if (!t) return ;
|
|
ZeroMemory(t, 16);
|
|
t->sz = 0x1000;
|
|
t->tm = 0;
|
|
|
|
}
|
|
else if (t->pos > t->sz - 0x10)
|
|
{
|
|
t->sz *= 2;
|
|
out_trax[nt] = (TRACK*)realloc(t, sizeof(TRACK) + t->sz);
|
|
if (!out_trax[nt])
|
|
{
|
|
free(t);
|
|
return ;
|
|
}
|
|
t = out_trax[nt];
|
|
}
|
|
|
|
if (t->tm < ct)
|
|
{
|
|
t->pos += EncodeDelta(&t->data[t->pos], ct - t->tm);
|
|
t->tm = ct;
|
|
}
|
|
else
|
|
{
|
|
t->data[t->pos++] = 0;
|
|
}
|
|
if (ec == 0x90)
|
|
{
|
|
hasnotes[nt] = 1;
|
|
data[0] &= 0x7F; /* don't allow 8bit note numbers */
|
|
}
|
|
else if (ec == 0x80)
|
|
{
|
|
data[0] &= 0x7F; /* don't allow 8bit note numbers */
|
|
}
|
|
/*if (ev!=t->le) */{t->data[t->pos++] = ev;t->le = ev;}
|
|
t->data[t->pos++] = data[0];
|
|
if (ec != 0xC0 && ec != 0xD0) t->data[t->pos++] = data[1];
|
|
}
|
|
|
|
void CCleaner::WriteTrack(TRACK* t)
|
|
{
|
|
CHD chd;
|
|
chd.c = 'krTM';
|
|
chd.s = rev32(t->pos);
|
|
WriteBuf(&chd, 8);
|
|
WriteBuf(&t->data, t->pos);
|
|
ntrax1++;
|
|
}
|
|
|
|
int DoCleanUp(MIDI_file* mf, DWORD mode, void** out_data, int * out_size)
|
|
{
|
|
CCleaner c;
|
|
c.only_ins = 0;
|
|
return c.Run(mf, mode, out_data, out_size);
|
|
}
|
|
|
|
int CCleaner::Run(MIDI_file* _mf, DWORD _md, void ** out_data, int * out_size)
|
|
{
|
|
f2 = *(WORD*)(_mf->data + 8) == 0x0200;
|
|
maxvol = 90;
|
|
vol_set = 0;
|
|
dm = (_md & CLEAN_DM) ? 1 : 0;
|
|
tr1 = (_md & CLEAN_1TRACK) ? 1 : 0;
|
|
|
|
if (_md&CLEAN_DLS)
|
|
{
|
|
drumfix = dm && cfg_hack_dls_drums;
|
|
insfix = dm && cfg_hack_dls_instruments;
|
|
}
|
|
else
|
|
{
|
|
drumfix = insfix = 0;
|
|
}
|
|
|
|
|
|
|
|
mf = _mf;
|
|
|
|
instr_ptr = &instr;
|
|
instr = 0;
|
|
|
|
UINT n;
|
|
|
|
ct = 0;
|
|
tf = 0;
|
|
ntrax = ntrax1 = 0;
|
|
CHD chd;
|
|
MHD mhd;
|
|
DWORD ptr = 8;
|
|
|
|
|
|
|
|
mhd = *(MHD*)(mf->data + 8);
|
|
|
|
ptr += 6;
|
|
|
|
mhd.t = rev16(mhd.t);
|
|
mhd.n = rev16(mhd.n);
|
|
|
|
if (mhd.t > 2)
|
|
goto fail;
|
|
ntrax = mhd.n;
|
|
n = 0;
|
|
in_trax = (TRACK**)malloc(sizeof(void*) * ntrax);
|
|
for (;n < ntrax && ptr < (UINT)mf->size;n++)
|
|
{
|
|
chd = *(CHD*)(mf->data + ptr);
|
|
ptr += 8;
|
|
if (chd.c != 'krTM' || ptr > (UINT)mf->size)
|
|
{
|
|
ntrax = n;
|
|
break;
|
|
}
|
|
chd.s = rev32(chd.s);
|
|
//if (ptr+chd.s>(UINT)mf->size)
|
|
if (chd.s > ((UINT)mf->size - ptr))
|
|
{
|
|
chd.s = mf->size - ptr;
|
|
}
|
|
//goto fail;
|
|
in_trax[n] = (TRACK*)malloc(16 + chd.s);
|
|
in_trax[n]->sz = chd.s;
|
|
in_trax[n]->tm = 0;
|
|
in_trax[n]->le = 0;
|
|
in_trax[n]->pos = 0;
|
|
memcpy(in_trax[n]->data, mf->data + ptr, chd.s);
|
|
ptr += chd.s;
|
|
AdvanceTime(in_trax[n]);
|
|
}
|
|
if (f2)
|
|
{
|
|
for (n = 0;n < ntrax;n++)
|
|
{
|
|
in_trax[n]->tm = ct;
|
|
while (tf <= n)
|
|
{
|
|
do_shit(n);
|
|
if (in_trax[n]->tm != -1) ct = in_trax[n]->tm;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (tf < ntrax)
|
|
{
|
|
UINT nt = get_next_time(); //ct++;
|
|
if (nt == -1) break;
|
|
ct = nt;
|
|
for (n = 0;n < ntrax && tf < ntrax;n++)
|
|
{
|
|
do_shit(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!only_ins)
|
|
{
|
|
|
|
|
|
mhd.t = 0x0100;
|
|
mhd.n = 0; //rev16(ntrax1);
|
|
chd.c = 'dhTM';
|
|
chd.s = 0x06000000;
|
|
WriteBuf(&chd, 8);
|
|
WriteBuf(&mhd, 6);
|
|
if (!(_md&CLEAN_NOTEMPO) && mf->tmap)
|
|
{
|
|
/* BYTE *tt=mf->tmap->BuildTrack();
|
|
if (tt)
|
|
{
|
|
WriteBuf(tt,rev32(*(DWORD*)(tt+4))+8);
|
|
ntrax1++;
|
|
free(tt);
|
|
}*/
|
|
if (mf->tmap->BuildTrack(outbuf))
|
|
{
|
|
ntrax1++;
|
|
}
|
|
}
|
|
if (!(_md&CLEAN_NOSYSEX) && mf->smap)
|
|
{
|
|
/* BYTE *st=mf->smap->BuildTrack();
|
|
if (st)
|
|
{
|
|
WriteBuf(st,rev32(*(DWORD*)(st+4))+8);
|
|
ntrax1++;
|
|
free(st);
|
|
}*/
|
|
if (mf->smap->BuildTrack(outbuf))
|
|
{
|
|
ntrax1++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (n = 0;n < 16;n++) if (out_trax[n] && hasnotes[n] && out_trax[n]->pos)
|
|
{
|
|
TRACK *t = out_trax[n];
|
|
t->pos += EncodeDelta(t->data + t->pos, ct - t->tm);
|
|
t->data[t->pos++] = 0xFF;
|
|
t->data[t->pos++] = 0x2F;
|
|
t->data[t->pos++] = 0;
|
|
WriteTrack(t);
|
|
}
|
|
{
|
|
WORD t = rev16(ntrax1);
|
|
outbuf.write_ptr(&t, 2, 10);
|
|
}
|
|
if (out_size) *out_size = outbuf.get_size();
|
|
if (out_data) *out_data = outbuf.finish();
|
|
#if 0
|
|
{
|
|
HANDLE f = CreateFile("c:\\dump.mid", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
|
|
DWORD bw = 0;
|
|
WriteFile(f, rv, bs, &bw, 0);
|
|
CloseHandle(f);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
return 1;
|
|
fail:
|
|
// ErrorBox("WARNING: cleaner messed up");
|
|
|
|
return 0;
|
|
|
|
//TO DESTRUCTOR
|
|
|
|
}
|
|
|
|
INSTRUMENT_DESC* GetInstruments(MIDI_file* mf, BOOL do_lsb)
|
|
{
|
|
CCleaner c;
|
|
c.only_ins = 1;
|
|
c.ins_no_lsb = !do_lsb;
|
|
c.Run(mf, 0, 0, 0);
|
|
return c.instr;
|
|
}
|