#include <string.h>
#include "hsc.h"
#include "debug.h"
CPlayer *ChscPlayer::factory(Copl *newopl)
{
return new ChscPlayer(newopl);
}
bool ChscPlayer::load(const std::string &filename, const CFileProvider &fp)
{
binistream *f = fp.open(filename);
int i;
if (
!f
|| !fp.extension(filename, ".hsc")
|| fp.filesize(f) > (59187 + 1) || fp.filesize(f) < (1587 + 1152) ) {
AdPlug_LogWrite("ChscPlayer::load(\"%s\"): Not a HSC file!\n", filename.c_str());
fp.close(f);
return false;
}
int total_patterns_in_hsc = (fp.filesize(f) - 1587) / 1152;
for(i=0;i<128*12;i++) *((unsigned char *)instr + i) = f->readInt(1);
for (i=0;i<128;i++) { instr[i][2] ^= (instr[i][2] & 0x40) << 1;
instr[i][3] ^= (instr[i][3] & 0x40) << 1;
instr[i][11] >>= 4; }
for(i=0;i<51;i++) { song[i] = f->readInt(1);
if (
((song[i] & 0x7F) > 0x31)
|| ((song[i] & 0x7F) >= total_patterns_in_hsc)
) song[i] = 0xFF;
}
for(i=0;i<50*64*9;i++) *((char *)patterns + i) = f->readInt(1);
fp.close(f);
rewind(0); return true;
}
bool ChscPlayer::update()
{
unsigned char chan,pattnr,note,effect,eff_op,inst,vol,Okt,db;
unsigned short Fnr;
unsigned long pattoff;
del--; if(del)
return !songend;
if(fadein) fadein--;
pattnr = song[songpos];
if(pattnr >= 0xb2) { songend = 1; songpos = 0;
pattnr = song[songpos];
} else
if ((pattnr & 128) && (pattnr <= 0xb1)) { songpos = song[songpos] & 127;
pattpos = 0;
pattnr = song[songpos];
songend = 1;
}
pattoff = pattpos*9;
for (chan=0;chan<9;chan++) { note = patterns[pattnr][pattoff].note;
effect = patterns[pattnr][pattoff].effect;
pattoff++;
if(note & 128) { setinstr(chan,effect);
continue;
}
eff_op = effect & 0x0f;
inst = channel[chan].inst;
if(note)
channel[chan].slide = 0;
switch (effect & 0xf0) { case 0:
switch(eff_op) {
case 1: pattbreak++; break; case 3: fadein = 31; break; case 5: mode6 = 1; break; case 6: mode6 = 0; break; }
break;
case 0x20:
case 0x10: if (effect & 0x10) {
channel[chan].freq += eff_op;
channel[chan].slide += eff_op;
} else {
channel[chan].freq -= eff_op;
channel[chan].slide -= eff_op;
}
if(!note)
setfreq(chan,channel[chan].freq);
break;
case 0x50: break;
case 0x60: opl->write(0xc0 + chan, (instr[channel[chan].inst][8] & 1) + (eff_op << 1));
break;
case 0xa0: vol = eff_op << 2;
opl->write(0x43 + op_table[chan], vol | (instr[channel[chan].inst][2] & ~63));
break;
case 0xb0: vol = eff_op << 2;
if (instr[inst][8] & 1)
opl->write(0x40 + op_table[chan], vol | (instr[channel[chan].inst][3] & ~63));
else
opl->write(0x40 + op_table[chan],vol | (instr[inst][3] & ~63));
break;
case 0xc0: db = eff_op << 2;
opl->write(0x43 + op_table[chan], db | (instr[channel[chan].inst][2] & ~63));
if (instr[inst][8] & 1)
opl->write(0x40 + op_table[chan], db | (instr[channel[chan].inst][3] & ~63));
break;
case 0xd0: pattbreak++; songpos = eff_op; songend = 1; break; case 0xf0: speed = eff_op;
del = ++speed;
break;
}
if(fadein) setvolume(chan,fadein*2,fadein*2);
if(!note) continue;
note--;
if ((note == 0x7f-1) || ((note/12) & ~7)) { adl_freq[chan] &= ~32;
opl->write(0xb0 + chan,adl_freq[chan]);
continue;
}
if(mtkmode) note--;
Okt = ((note/12) & 7) << 2;
Fnr = note_table[(note % 12)] + instr[inst][11] + channel[chan].slide;
channel[chan].freq = Fnr;
if(!mode6 || chan < 6)
adl_freq[chan] = Okt | 32;
else
adl_freq[chan] = Okt; opl->write(0xb0 + chan, 0);
setfreq(chan,Fnr);
if(mode6) {
switch(chan) { case 6: opl->write(0xbd,bd & ~16); bd |= 48; break; case 7: opl->write(0xbd,bd & ~1); bd |= 33; break; case 8: opl->write(0xbd,bd & ~2); bd |= 34; break; }
opl->write(0xbd,bd);
}
}
del = speed; if(pattbreak) { pattpos=0; pattbreak=0;
songpos++;
songpos %= 50;
if(!songpos)
songend = 1;
} else {
pattpos++;
pattpos &= 63; if (!pattpos) {
songpos++;
songpos %= 50;
if(!songpos)
songend = 1;
}
}
return !songend; }
void ChscPlayer::rewind(int subsong)
{
int i;
pattpos = 0; songpos = 0; pattbreak = 0; speed = 2;
del = 1; songend = 0; mode6 = 0; bd = 0; fadein = 0;
opl->init(); opl->write(1,32); opl->write(8,128); opl->write(0xbd,0);
for(i=0;i<9;i++)
setinstr((char) i,(char) i); }
unsigned int ChscPlayer::getpatterns()
{
unsigned char poscnt,pattcnt=0;
for(poscnt=0;poscnt<51 && song[poscnt] != 0xff;poscnt++)
if(song[poscnt] > pattcnt)
pattcnt = song[poscnt];
return (pattcnt+1);
}
unsigned int ChscPlayer::getorders()
{
unsigned char poscnt;
for(poscnt=0;poscnt<51;poscnt++)
if(song[poscnt] == 0xff)
break;
return poscnt;
}
unsigned int ChscPlayer::getinstruments()
{
unsigned char instcnt,instnum=0,i;
bool isinst;
for(instcnt=0;instcnt<128;instcnt++) {
isinst = false;
for(i=0;i<12;i++)
if(instr[instcnt][i])
isinst = true;
if(isinst)
instnum++;
}
return instnum;
}
void ChscPlayer::setfreq(unsigned char chan, unsigned short freq)
{
adl_freq[chan] = (adl_freq[chan] & ~3) | (freq >> 8);
opl->write(0xa0 + chan, freq & 0xff);
opl->write(0xb0 + chan, adl_freq[chan]);
}
void ChscPlayer::setvolume(unsigned char chan, int volc, int volm)
{
unsigned char *ins = instr[channel[chan].inst];
char op = op_table[chan];
opl->write(0x43 + op,volc | (ins[2] & ~63));
if (ins[8] & 1) opl->write(0x40 + op,volm | (ins[3] & ~63));
else
opl->write(0x40 + op, ins[3]); }
void ChscPlayer::setinstr(unsigned char chan, unsigned char insnr)
{
unsigned char *ins = instr[insnr];
char op = op_table[chan];
channel[chan].inst = insnr; opl->write(0xb0 + chan,0);
opl->write(0xc0 + chan, ins[8]);
opl->write(0x23 + op, ins[0]); opl->write(0x20 + op, ins[1]); opl->write(0x63 + op, ins[4]); opl->write(0x60 + op, ins[5]);
opl->write(0x83 + op, ins[6]); opl->write(0x80 + op, ins[7]);
opl->write(0xe3 + op, ins[9]); opl->write(0xe0 + op, ins[10]);
setvolume(chan, ins[2] & 63, ins[3] & 63);
}