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;
+}