Mercurial > audlegacy-plugins
diff src/Input/adplug/core/dmo.cxx @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
| author | nenolod |
|---|---|
| date | Mon, 18 Sep 2006 01:11:49 -0700 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Input/adplug/core/dmo.cxx Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,403 @@ +/* + Adplug - Replayer for many OPL2/OPL3 audio file formats. + Copyright (C) 1999 - 2004, 2006 Simon Peter, <dn.tlp@gmx.net>, et al. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru> +*/ +/* + NOTES: + Panning is ignored. + + A WORD ist 16 bits, a DWORD is 32 bits and a BYTE is 8 bits in this context. +*/ + +#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)) + +/* -------- Public Methods -------------------------------- */ + +CPlayer *CdmoLoader::factory(Copl *newopl) +{ + return new CdmoLoader(newopl); +} + +bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) +{ + int i,j; + binistream *f; + + // check header + dmo_unpacker *unpacker = new dmo_unpacker; + unsigned char chkhdr[16]; + + if(!fp.extension(filename, ".dmo")) return false; + f = fp.open(filename); if(!f) return false; + + f->readString((char *)chkhdr, 16); + + if (!unpacker->decrypt(chkhdr, 16)) + { + delete unpacker; + fp.close(f); + return false; + } + + // get file size + long packed_length = fp.filesize(f); + f->seek(0); + + unsigned char *packed_module = new unsigned char [packed_length]; + + // load file + f->readString((char *)packed_module, packed_length); + fp.close(f); + + // decrypt + unpacker->decrypt(packed_module,packed_length); + + long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12); + unsigned char *module = new unsigned char [unpacked_length]; + + // unpack + if (!unpacker->unpack(packed_module+12,module,unpacked_length)) + { + delete unpacker; + delete [] packed_module; + delete [] module; + return false; + } + + delete unpacker; + delete [] packed_module; + + // "TwinTeam" - signed ? + if (memcmp(module,"TwinTeam Module File""\x0D\x0A",22)) + { + delete module; + return false; + } + + // load header + binisstream uf(module, unpacked_length); + uf.setFlag(binio::BigEndian, false); uf.setFlag(binio::FloatIEEE); + + memset(&header,0,sizeof(s3mheader)); + + uf.ignore(22); // ignore DMO header ID string + uf.readString(header.name, 28); + + uf.ignore(2); // _unk_1 + header.ordnum = uf.readInt(2); + header.insnum = uf.readInt(2); + header.patnum = uf.readInt(2); + uf.ignore(2); // _unk_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); // ignore panning settings for all 32 channels + + // load orders + for(i = 0; i < 256; i++) orders[i] = uf.readInt(1); + + orders[header.ordnum] = 0xFF; + + // load pattern lengths + unsigned short my_patlen[100]; + for(i = 0; i < 100; i++) my_patlen[i] = uf.readInt(2); + + // load instruments + 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); + /* + * Originally, riven sets d0b = d0a and ignores 1 byte in the + * stream, but i guess this was a typo, so i read it here. + */ + inst[i].d0b = uf.readInt(1); + } + + // load patterns + 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; + + // note + instrument ? + 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); + } + + // volume ? + if (token & 64) + pattern[i][j][chan].volume = uf.readInt(1); + + // command ? + 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() +{ + /* + All available .DMO modules written by one composer. And because all .DMO + stuff was lost due to hd crash (TwinTeam guys said this), there are + never(?) be another. + */ + return std::string("Benjamin GERARDIN"); +} + +/* -------- Private Methods ------------------------------- */ + +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; + + // leave it that way or amd64 might get it wrong + 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; + + // LZ77 child + while (ipos - ibuf < ilen) + { + code = *ipos++; + + // 00xxxxxx: copy (xxxxxx + 1) bytes + if ((code >> 6) == 0) + { + cx = (code & 0x3F) + 1; + + if(opos + cx >= oend) + return -1; + + for (int i=0;i<cx;i++) + *opos++ = *ipos++; + + continue; + } + + // 01xxxxxx xxxyyyyy: copy (Y + 3) bytes from (X + 1) + 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); + + continue; + } + + // 10xxxxxx xyyyzzzz: copy (Y + 3) bytes from (X + 1); copy Z bytes + 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); + + for (i=0;i<bx;i++) + *opos++ = *ipos++; + + continue; + } + + // 11xxxxxx xxxxxxxy yyyyzzzz: copy (Y + 4) from X; copy Z bytes + 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); + + 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; +}
