#include <string.h>
#include <binstr.h>
#include "dmo.h"
#include "debug.h"
#define LOWORD(l) ((l) & 0xffff)
#define HIWORD(l) ((l) >> 16)
#define LOBYTE(w) ((w) & 0xff)
#define HIBYTE(w) ((w) >> 8)
#define ARRAY_AS_DWORD(a, i) \
((a[i + 3] << 24) + (a[i + 2] << 16) + (a[i + 1] << 8) + a[i])
#define ARRAY_AS_WORD(a, i) ((a[i + 1] << 8) + a[i])
#define CHARP_AS_WORD(p) (((*(p + 1)) << 8) + (*p))
CPlayer *CdmoLoader::factory(Copl *newopl)
{
return new CdmoLoader(newopl);
}
bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp)
{
int i,j;
binistream *f;
dmo_unpacker *unpacker = new dmo_unpacker;
unsigned char chkhdr[16];
if(!fp.extension(filename, ".dmo"))
{
delete unpacker;
return false;
}
f = fp.open(filename);
if(!f)
{
delete unpacker;
return false;
}
f->readString((char *)chkhdr, 16);
if (!unpacker->decrypt(chkhdr, 16))
{
delete unpacker;
fp.close(f);
return false;
}
long packed_length = fp.filesize(f);
f->seek(0);
unsigned char *packed_module = new unsigned char [packed_length];
f->readString((char *)packed_module, packed_length);
fp.close(f);
unpacker->decrypt(packed_module,packed_length);
long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12);
unsigned char *module = new unsigned char [unpacked_length];
if (!unpacker->unpack(packed_module+12,module,unpacked_length))
{
delete unpacker;
delete [] packed_module;
delete [] module;
return false;
}
delete unpacker;
delete [] packed_module;
if (memcmp(module,"TwinTeam Module File""\x0D\x0A",22))
{
delete [] module;
return false;
}
binisstream uf(module, unpacked_length);
uf.setFlag(binio::BigEndian, false); uf.setFlag(binio::FloatIEEE);
memset(&header,0,sizeof(s3mheader));
uf.ignore(22); uf.readString(header.name, 28);
uf.ignore(2); header.ordnum = uf.readInt(2);
header.insnum = uf.readInt(2);
header.patnum = uf.readInt(2);
uf.ignore(2); header.is = uf.readInt(2);
header.it = uf.readInt(2);
memset(header.chanset,0xFF,32);
for (i=0;i<9;i++)
header.chanset[i] = 0x10 + i;
uf.ignore(32);
for(i = 0; i < 256; i++) orders[i] = uf.readInt(1);
orders[header.ordnum] = 0xFF;
unsigned short my_patlen[100];
for(i = 0; i < 100; i++) my_patlen[i] = uf.readInt(2);
for (i = 0; i < header.insnum; i++)
{
memset(&inst[i],0,sizeof(s3minst));
uf.readString(inst[i].name, 28);
inst[i].volume = uf.readInt(1);
inst[i].dsk = uf.readInt(1);
inst[i].c2spd = uf.readInt(4);
inst[i].type = uf.readInt(1);
inst[i].d00 = uf.readInt(1);
inst[i].d01 = uf.readInt(1);
inst[i].d02 = uf.readInt(1);
inst[i].d03 = uf.readInt(1);
inst[i].d04 = uf.readInt(1);
inst[i].d05 = uf.readInt(1);
inst[i].d06 = uf.readInt(1);
inst[i].d07 = uf.readInt(1);
inst[i].d08 = uf.readInt(1);
inst[i].d09 = uf.readInt(1);
inst[i].d0a = uf.readInt(1);
inst[i].d0b = uf.readInt(1);
}
for (i = 0; i < header.patnum; i++) {
long cur_pos = uf.pos();
for (j = 0; j < 64; j++) {
while (1) {
unsigned char token = uf.readInt(1);
if (!token)
break;
unsigned char chan = token & 31;
if (token & 32) {
unsigned char bufbyte = uf.readInt(1);
pattern[i][j][chan].note = bufbyte & 15;
pattern[i][j][chan].oct = bufbyte >> 4;
pattern[i][j][chan].instrument = uf.readInt(1);
}
if (token & 64)
pattern[i][j][chan].volume = uf.readInt(1);
if (token & 128) {
pattern[i][j][chan].command = uf.readInt(1);
pattern[i][j][chan].info = uf.readInt(1);
}
}
}
uf.seek(cur_pos + my_patlen[i]);
}
delete [] module;
rewind(0);
return true;
}
std::string CdmoLoader::gettype()
{
return std::string("TwinTeam (packed S3M)");
}
std::string CdmoLoader::getauthor()
{
return std::string("Benjamin GERARDIN");
}
unsigned short CdmoLoader::dmo_unpacker::brand(unsigned short range)
{
unsigned short ax,bx,cx,dx;
ax = LOWORD(bseed);
bx = HIWORD(bseed);
cx = ax;
ax = LOWORD(cx * 0x8405);
dx = HIWORD(cx * 0x8405);
cx <<= 3;
cx = (((HIBYTE(cx) + LOBYTE(cx)) & 0xFF) << 8) + LOBYTE(cx);
dx += cx;
dx += bx;
bx <<= 2;
dx += bx;
dx = (((HIBYTE(dx) + LOBYTE(bx)) & 0xFF) << 8) + LOBYTE(dx);
bx <<= 5;
dx = (((HIBYTE(dx) + LOBYTE(bx)) & 0xFF) << 8) + LOBYTE(dx);
ax += 1;
if (!ax) dx += 1;
bseed = dx;
bseed <<= 16;
bseed += ax;
return HIWORD(HIWORD(LOWORD(bseed) * range) + HIWORD(bseed) * range);
}
bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, long len)
{
unsigned long seed = 0;
int i;
bseed = ARRAY_AS_DWORD(buf, 0);
for (i=0; i < ARRAY_AS_WORD(buf, 4) + 1; i++)
seed += brand(0xffff);
bseed = seed ^ ARRAY_AS_DWORD(buf, 6);
if (ARRAY_AS_WORD(buf, 10) != brand(0xffff))
return false;
for (i=0;i<(len-12);i++)
buf[12+i] ^= brand(0x100);
buf[len - 2] = buf[len - 1] = 0;
return true;
}
short CdmoLoader::dmo_unpacker::unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf)
{
unsigned char code,par1,par2;
unsigned short ax,bx,cx;
unsigned char *ipos = ibuf;
unsigned char *opos = obuf;
while (ipos - ibuf < ilen)
{
code = *ipos++;
if ((code >> 6) == 0)
{
cx = (code & 0x3F) + 1;
if(opos + cx >= oend)
return -1;
for (int i=0;i<cx;i++)
*opos++ = *ipos++;
continue;
}
if ((code >> 6) == 1)
{
par1 = *ipos++;
ax = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1;
cx = (par1 & 0x1F) + 3;
if(opos + cx >= oend)
return -1;
for(int i=0;i<cx;i++)
{
*opos = *(opos - ax);
opos++;
}
continue;
}
if ((code >> 6) == 2)
{
int i;
par1 = *ipos++;
ax = ((code & 0x3F) << 1) + (par1 >> 7) + 1;
cx = ((par1 & 0x70) >> 4) + 3;
bx = par1 & 0x0F;
if(opos + bx + cx >= oend)
return -1;
for(i=0;i<cx;i++)
{
*opos = *(opos - ax);
opos++;
}
for (i=0;i<bx;i++)
*opos++ = *ipos++;
continue;
}
if ((code >> 6) == 3)
{
int i;
par1 = *ipos++;
par2 = *ipos++;
bx = ((code & 0x3F) << 7) + (par1 >> 1);
cx = ((par1 & 0x01) << 4) + (par2 >> 4) + 4;
ax = par2 & 0x0F;
if(opos + ax + cx >= oend)
return -1;
for(i=0;i<cx;i++)
{
*opos = *(opos - bx);
opos++;
}
for (i=0;i<ax;i++)
*opos++ = *ipos++;
continue;
}
}
return opos - obuf;
}
long CdmoLoader::dmo_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf,
unsigned long outputsize)
{
long olen = 0;
unsigned short block_count = CHARP_AS_WORD(ibuf);
ibuf += 2;
unsigned char *block_length = ibuf;
ibuf += 2 * block_count;
oend = obuf + outputsize;
for (int i=0;i<block_count;i++)
{
unsigned short bul = CHARP_AS_WORD(ibuf);
if(unpack_block(ibuf + 2,CHARP_AS_WORD(block_length) - 2,obuf) != bul)
return 0;
obuf += bul;
olen += bul;
ibuf += CHARP_AS_WORD(block_length);
block_length += 2;
}
return olen;
}