changeset 396:329a48431102 trunk

[svn] Really add S3M support back after receiving a convincing testcase.
author chainsaw
date Sat, 07 Jan 2006 06:18:41 -0800
parents 0923a41170bb
children 4fa1244ad483
files Plugins/Input/adplug/core/dmo.cpp Plugins/Input/adplug/core/dmo.h Plugins/Input/adplug/core/s3m.cpp Plugins/Input/adplug/core/s3m.h
diffstat 4 files changed, 1063 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/adplug/core/dmo.cpp	Sat Jan 07 06:18:41 2006 -0800
@@ -0,0 +1,391 @@
+/*
+  Adplug - Replayer for many OPL2/OPL3 audio file formats.
+  Copyright (C) 1999 - 2004 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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))
+    {
+      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;
+
+	  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;
+
+	  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;
+
+	  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;
+
+	  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)
+{
+  long olen = 0;
+
+  unsigned short block_count = CHARP_AS_WORD(ibuf);
+
+  ibuf += 2;
+
+  unsigned char *block_length = ibuf;
+
+  ibuf += 2 * block_count;
+
+  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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/adplug/core/dmo.h	Sat Jan 07 06:18:41 2006 -0800
@@ -0,0 +1,48 @@
+/*
+  Adplug - Replayer for many OPL2/OPL3 audio file formats.
+  Copyright (C) 1999 - 2003 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "s3m.h"
+
+class CdmoLoader: public Cs3mPlayer
+{
+ public:
+  static CPlayer *factory(Copl *newopl);
+
+  CdmoLoader(Copl *newopl) : Cs3mPlayer(newopl) { };
+
+  bool	load(const std::string &filename, const CFileProvider &fp);
+
+  std::string	gettype();
+  std::string	getauthor();
+
+ private:
+
+  class dmo_unpacker {
+  public:
+    bool decrypt(unsigned char *buf, long len);
+    long unpack(unsigned char *ibuf, unsigned char *obuf);
+
+  private:
+    unsigned short brand(unsigned short range);
+    short unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf);
+    unsigned long bseed;
+  };
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/adplug/core/s3m.cpp	Sat Jan 07 06:18:41 2006 -0800
@@ -0,0 +1,517 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * s3m.c - S3M Player by Simon Peter <dn.tlp@gmx.net>
+ *
+ * BUGS:
+ * Extra Fine Slides (EEx, FEx) & Fine Vibrato (Uxy) are inaccurate
+ */
+
+#include "s3m.h"
+
+const char Cs3mPlayer::chnresolv[] =	// S3M -> adlib channel conversion
+	{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,-1,-1,-1,-1,-1,-1,-1};
+
+const unsigned short Cs3mPlayer::notetable[12] =		// S3M adlib note table
+			{340,363,385,408,432,458,485,514,544,577,611,647};
+
+const unsigned char Cs3mPlayer::vibratotab[32] =		// vibrato rate table
+			{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
+
+/*** public methods *************************************/
+
+CPlayer *Cs3mPlayer::factory(Copl *newopl)
+{
+  return new Cs3mPlayer(newopl);
+}
+
+Cs3mPlayer::Cs3mPlayer(Copl *newopl): CPlayer(newopl)
+{
+	int			i,j,k;
+
+	memset(pattern,255,sizeof(pattern));
+	memset(orders,255,sizeof(orders));
+
+	for(i=0;i<99;i++)		// setup pattern
+		for(j=0;j<64;j++)
+			for(k=0;k<32;k++) {
+				pattern[i][j][k].instrument = 0;
+				pattern[i][j][k].info = 0;
+			}
+}
+
+bool Cs3mPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+        binistream *f = fp.open(filename); if(!f) return false;
+	unsigned short	insptr[99],pattptr[99];
+	int		i,row;
+	unsigned char	bufval,bufval2;
+	unsigned short	ppatlen;
+	s3mheader	*checkhead;
+	bool		adlibins=false;
+
+	// file validation section
+	checkhead = new s3mheader;
+	load_header(f, checkhead);
+	if((checkhead->kennung != 0x1a) || (checkhead->typ != 16)) {
+		delete checkhead; fp.close(f); return false;
+	} else
+		if(strncmp(checkhead->scrm,"SCRM",4)) {
+			delete checkhead; fp.close(f); return false;
+		} else {	// is an adlib module?
+             	        f->seek(checkhead->ordnum, binio::Add);
+			for(i = 0; i < checkhead->insnum; i++)
+			  insptr[i] = f->readInt(2);
+			for(i=0;i<checkhead->insnum;i++) {
+				f->seek(insptr[i]*16);
+				if(f->readInt(1) >= 2) {
+					adlibins = true;
+					break;
+				}
+			}
+			delete checkhead;
+			if(!adlibins) { fp.close(f); return false; }
+		}
+
+	// load section
+	f->seek(0);	// rewind for load
+	load_header(f, &header);			// read header
+	for(i = 0; i < header.ordnum; i++) orders[i] = f->readInt(1);	// read orders
+	for(i = 0; i < header.insnum; i++) insptr[i] = f->readInt(2);	// instrument parapointers
+	for(i = 0; i < header.patnum; i++) pattptr[i] = f->readInt(2);	// pattern parapointers
+
+	for(i=0;i<header.insnum;i++) {	// load instruments
+		f->seek(insptr[i]*16);
+		inst[i].type = f->readInt(1);
+		f->readString(inst[i].filename, 15);
+		inst[i].d00 = f->readInt(1); inst[i].d01 = f->readInt(1);
+		inst[i].d02 = f->readInt(1); inst[i].d03 = f->readInt(1);
+		inst[i].d04 = f->readInt(1); inst[i].d05 = f->readInt(1);
+		inst[i].d06 = f->readInt(1); inst[i].d07 = f->readInt(1);
+		inst[i].d08 = f->readInt(1); inst[i].d09 = f->readInt(1);
+		inst[i].d0a = f->readInt(1); inst[i].d0b = f->readInt(1);
+		inst[i].volume = f->readInt(1); inst[i].dsk = f->readInt(1);
+		f->ignore(2);
+		inst[i].c2spd = f->readInt(4);
+		f->ignore(12);
+		f->readString(inst[i].name, 28);
+		f->readString(inst[i].scri, 4);
+	}
+
+	for(i=0;i<header.patnum;i++) {	// depack patterns
+		f->seek(pattptr[i]*16);
+		ppatlen = f->readInt(2);
+		unsigned long pattpos = f->pos();
+		for(row=0;(row<64) && (pattpos-pattptr[i]*16<=ppatlen);row++)
+			do {
+            		        bufval = f->readInt(1);
+				if(bufval & 32) {
+				        bufval2 = f->readInt(1);
+					pattern[i][row][bufval & 31].note = bufval2 & 15;
+					pattern[i][row][bufval & 31].oct = (bufval2 & 240) >> 4;
+					pattern[i][row][bufval & 31].instrument = f->readInt(1);
+				}
+				if(bufval & 64)
+				        pattern[i][row][bufval & 31].volume = f->readInt(1);
+				if(bufval & 128) {
+				        pattern[i][row][bufval & 31].command = f->readInt(1);
+					pattern[i][row][bufval & 31].info = f->readInt(1);
+				}
+			} while(bufval);
+	}
+
+	fp.close(f);
+	rewind(0);
+	return true;		// done
+}
+
+bool Cs3mPlayer::update()
+{
+	unsigned char	pattbreak=0,donote;		// remember vars
+	unsigned char	pattnr,chan,row,info;	// cache vars
+	signed char		realchan;
+
+	// effect handling (timer dependant)
+	for(realchan=0; realchan<9; realchan++) {
+		info = channel[realchan].info;	// fill infobyte cache
+		switch(channel[realchan].fx) {
+		case 11:
+		case 12: if(channel[realchan].fx == 11)								// dual command: H00 and Dxy
+					 vibrato(realchan,channel[realchan].dualinfo);
+				 else															// dual command: G00 and Dxy
+					 tone_portamento(realchan,channel[realchan].dualinfo);
+		case 4: if(info <= 0x0f)												// volume slide down
+					if(channel[realchan].vol - info >= 0)
+						channel[realchan].vol -= info;
+					else
+						channel[realchan].vol = 0;
+				if((info & 0x0f) == 0)											// volume slide up
+					if(channel[realchan].vol + (info >> 4) <= 63)
+						channel[realchan].vol += info >> 4;
+					else
+						channel[realchan].vol = 63;
+				setvolume(realchan);
+				break;
+		case 5: if(info == 0xf0 || info <= 0xe0) {								// slide down
+					slide_down(realchan,info);
+					setfreq(realchan);
+				}
+				break;
+		case 6: if(info == 0xf0 || info <= 0xe0) {								// slide up
+					slide_up(realchan,info);
+					setfreq(realchan);
+				}
+				break;
+		case 7: tone_portamento(realchan,channel[realchan].dualinfo); break;	// tone portamento
+		case 8: vibrato(realchan,channel[realchan].dualinfo); break;		// vibrato
+		case 10: channel[realchan].nextfreq = channel[realchan].freq;	// arpeggio
+				 channel[realchan].nextoct = channel[realchan].oct;
+				 switch(channel[realchan].trigger) {
+				 case 0: channel[realchan].freq = notetable[channel[realchan].note]; break;
+				 case 1: if(channel[realchan].note + ((info & 0xf0) >> 4) < 12)
+							 channel[realchan].freq = notetable[channel[realchan].note + ((info & 0xf0) >> 4)];
+						 else {
+							 channel[realchan].freq = notetable[channel[realchan].note + ((info & 0xf0) >> 4) - 12];
+							 channel[realchan].oct++;
+						 }
+						 break;
+				 case 2: if(channel[realchan].note + (info & 0x0f) < 12)
+							 channel[realchan].freq = notetable[channel[realchan].note + (info & 0x0f)];
+						 else {
+							 channel[realchan].freq = notetable[channel[realchan].note + (info & 0x0f) - 12];
+							 channel[realchan].oct++;
+						 }
+						 break;
+				 }
+				 if(channel[realchan].trigger < 2)
+					 channel[realchan].trigger++;
+				 else
+					 channel[realchan].trigger = 0;
+				 setfreq(realchan);
+				 channel[realchan].freq = channel[realchan].nextfreq;
+				 channel[realchan].oct = channel[realchan].nextoct;
+				 break;
+		case 21: vibrato(realchan,(unsigned char) (info / 4)); break;		// fine vibrato
+		}
+	}
+
+	if(del) {		// speed compensation
+		del--;
+		return !songend;
+	}
+
+	// arrangement handling
+	pattnr = orders[ord];
+	if(pattnr == 0xff || ord > header.ordnum) {	// "--" end of song
+		songend = 1;				// set end-flag
+		ord = 0;
+		pattnr = orders[ord];
+		if(pattnr == 0xff)
+			return !songend;
+	}
+	if(pattnr == 0xfe) {		// "++" skip marker
+		ord++; pattnr = orders[ord];
+	}
+
+	// play row
+	row = crow;	// fill row cache
+	for(chan=0;chan<32;chan++) {
+		if(!(header.chanset[chan] & 128))		// resolve S3M -> AdLib channels
+			realchan = chnresolv[header.chanset[chan] & 127];
+		else
+			realchan = -1;		// channel disabled
+		if(realchan != -1) {	// channel playable?
+			// set channel values
+			donote = 0;
+			if(pattern[pattnr][row][chan].note < 14)
+				// tone portamento
+				if(pattern[pattnr][row][chan].command == 7 || pattern[pattnr][row][chan].command == 12) {
+					channel[realchan].nextfreq = notetable[pattern[pattnr][row][chan].note];
+					channel[realchan].nextoct = pattern[pattnr][row][chan].oct;
+				} else {											// normal note
+					channel[realchan].note = pattern[pattnr][row][chan].note;
+					channel[realchan].freq = notetable[pattern[pattnr][row][chan].note];
+					channel[realchan].oct = pattern[pattnr][row][chan].oct;
+					channel[realchan].key = 1;
+					donote = 1;
+				}
+			if(pattern[pattnr][row][chan].note == 14) {	// key off (is 14 here, cause note is only first 4 bits)
+				channel[realchan].key = 0;
+				setfreq(realchan);
+			}
+			if((channel[realchan].fx != 8 && channel[realchan].fx != 11) &&		// vibrato begins
+				(pattern[pattnr][row][chan].command == 8 || pattern[pattnr][row][chan].command == 11)) {
+				channel[realchan].nextfreq = channel[realchan].freq;
+				channel[realchan].nextoct = channel[realchan].oct;
+			}
+			if(pattern[pattnr][row][chan].note >= 14)
+				if((channel[realchan].fx == 8 || channel[realchan].fx == 11) &&	// vibrato ends
+					(pattern[pattnr][row][chan].command != 8 && pattern[pattnr][row][chan].command != 11)) {
+					channel[realchan].freq = channel[realchan].nextfreq;
+					channel[realchan].oct = channel[realchan].nextoct;
+					setfreq(realchan);
+				}
+			if(pattern[pattnr][row][chan].instrument) {	// set instrument
+				channel[realchan].inst = pattern[pattnr][row][chan].instrument - 1;
+				if(inst[channel[realchan].inst].volume < 64)
+					channel[realchan].vol = inst[channel[realchan].inst].volume;
+				else
+					channel[realchan].vol = 63;
+				if(pattern[pattnr][row][chan].command != 7)
+					donote = 1;
+			}
+			if(pattern[pattnr][row][chan].volume != 255)
+				if(pattern[pattnr][row][chan].volume < 64)	// set volume
+					channel[realchan].vol = pattern[pattnr][row][chan].volume;
+				else
+					channel[realchan].vol = 63;
+			channel[realchan].fx = pattern[pattnr][row][chan].command;	// set command
+			if(pattern[pattnr][row][chan].info)			// set infobyte
+				channel[realchan].info = pattern[pattnr][row][chan].info;
+
+			// play note
+			if(donote)
+				playnote(realchan);
+			if(pattern[pattnr][row][chan].volume != 255)	// set volume
+				setvolume(realchan);
+
+			// command handling (row dependant)
+			info = channel[realchan].info;	// fill infobyte cache
+			switch(channel[realchan].fx) {
+			case 1: speed = info; break;							// set speed
+			case 2: if(info <= ord) songend = 1; ord = info; crow = 0; pattbreak = 1; break;	// jump to order
+			case 3: if(!pattbreak) { crow = info; ord++; pattbreak = 1; } break;	// pattern break
+			case 4: if(info > 0xf0)										// fine volume down
+						if(channel[realchan].vol - (info & 0x0f) >= 0)
+							channel[realchan].vol -= info & 0x0f;
+						else
+							channel[realchan].vol = 0;
+					if((info & 0x0f) == 0x0f && info >= 0x1f)			// fine volume up
+						if(channel[realchan].vol + ((info & 0xf0) >> 4) <= 63)
+							channel[realchan].vol += (info & 0xf0) >> 4;
+						else
+							channel[realchan].vol = 63;
+					setvolume(realchan);
+					break;
+			case 5: if(info > 0xf0)	{									// fine slide down
+						slide_down(realchan,(unsigned char) (info & 0x0f));
+						setfreq(realchan);
+					}
+					if(info > 0xe0 && info < 0xf0) {					// extra fine slide down
+						slide_down(realchan,(unsigned char) ((info & 0x0f) / 4));
+						setfreq(realchan);
+					}
+					break;
+			case 6: if(info > 0xf0) {									// fine slide up
+						slide_up(realchan,(unsigned char) (info & 0x0f));
+						setfreq(realchan);
+					}
+					if(info > 0xe0 && info < 0xf0) {					// extra fine slide up
+						slide_up(realchan,(unsigned char) ((info & 0x0f) / 4));
+						setfreq(realchan);
+					}
+					break;
+			case 7:														// tone portamento
+			case 8:	if((channel[realchan].fx == 7 ||				// vibrato (remember info for dual commands)
+					 channel[realchan].fx == 8) && pattern[pattnr][row][chan].info)
+						channel[realchan].dualinfo = info;
+					break;
+			case 10: channel[realchan].trigger = 0; break;			// arpeggio (set trigger)
+			case 19: if(info == 0xb0)									// set loop start
+						loopstart = row;
+					 if(info > 0xb0 && info <= 0xbf)					// pattern loop
+						if(!loopcnt) {
+							loopcnt = info & 0x0f;
+							crow = loopstart;
+							pattbreak = 1;
+						} else
+							if(--loopcnt > 0) {
+								crow = loopstart;
+								pattbreak = 1;
+							}
+					 if((info & 0xf0) == 0xe0)							// patterndelay
+						 del = speed * (info & 0x0f) - 1;
+					 break;
+			case 20: tempo = info; break;							// set tempo
+			}
+		}
+	}
+
+	if(!del)
+		del = speed - 1;// speed compensation
+	if(!pattbreak) {			// next row (only if no manual advance)
+		crow++;
+		if(crow > 63) {
+			crow = 0;
+			ord++;
+			loopstart = 0;
+		}
+	}
+
+	return !songend;		// still playing
+}
+
+void Cs3mPlayer::rewind(int subsong)
+{
+	// set basic variables
+	songend = 0; ord = 0; crow = 0; tempo = header.it;
+	speed = header.is; del = 0; loopstart = 0; loopcnt = 0;
+
+	memset(channel,0,sizeof(channel));
+
+	opl->init();				// reset OPL chip
+	opl->write(1,32);			// Go to ym3812 mode
+}
+
+std::string Cs3mPlayer::gettype()
+{
+	char filever[5];
+
+	switch(header.cwtv) {		// determine version number
+	case 0x1300: strcpy(filever,"3.00"); break;
+	case 0x1301: strcpy(filever,"3.01"); break;
+	case 0x1303: strcpy(filever,"3.03"); break;
+	case 0x1320: strcpy(filever,"3.20"); break;
+	default: strcpy(filever,"3.??");
+	}
+
+	return (std::string("Scream Tracker ") + filever);
+}
+
+float Cs3mPlayer::getrefresh()
+{
+	return (float) (tempo / 2.5);
+}
+
+/*** private methods *************************************/
+
+void Cs3mPlayer::load_header(binistream *f, s3mheader *h)
+{
+  int i;
+
+  f->readString(h->name, 28);
+  h->kennung = f->readInt(1); h->typ = f->readInt(1);
+  f->ignore(2);
+  h->ordnum = f->readInt(2); h->insnum = f->readInt(2);
+  h->patnum = f->readInt(2); h->flags = f->readInt(2);
+  h->cwtv = f->readInt(2); h->ffi = f->readInt(2);
+  f->readString(h->scrm, 4);
+  h->gv = f->readInt(1); h->is = f->readInt(1); h->it = f->readInt(1);
+  h->mv = f->readInt(1); h->uc = f->readInt(1); h->dp = f->readInt(1);
+  f->ignore(8);
+  h->special = f->readInt(2);
+  for(i = 0; i < 32; i++) h->chanset[i] = f->readInt(1);
+}
+
+void Cs3mPlayer::setvolume(unsigned char chan)
+{
+	unsigned char op = op_table[chan], insnr = channel[chan].inst;
+
+	opl->write(0x43 + op,(int)(63-((63-(inst[insnr].d03 & 63))/63.0)*channel[chan].vol) + (inst[insnr].d03 & 192));
+	if(inst[insnr].d0a & 1)
+		opl->write(0x40 + op,(int)(63-((63-(inst[insnr].d02 & 63))/63.0)*channel[chan].vol) + (inst[insnr].d02 & 192));
+}
+
+void Cs3mPlayer::setfreq(unsigned char chan)
+{
+	opl->write(0xa0 + chan, channel[chan].freq & 255);
+	if(channel[chan].key)
+		opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
+	else
+		opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
+}
+
+void Cs3mPlayer::playnote(unsigned char chan)
+{
+	unsigned char op = op_table[chan], insnr = channel[chan].inst;
+
+	opl->write(0xb0 + chan, 0);	// stop old note
+
+	// set instrument data
+	opl->write(0x20 + op, inst[insnr].d00);
+	opl->write(0x23 + op, inst[insnr].d01);
+	opl->write(0x40 + op, inst[insnr].d02);
+	opl->write(0x43 + op, inst[insnr].d03);
+	opl->write(0x60 + op, inst[insnr].d04);
+	opl->write(0x63 + op, inst[insnr].d05);
+	opl->write(0x80 + op, inst[insnr].d06);
+	opl->write(0x83 + op, inst[insnr].d07);
+	opl->write(0xe0 + op, inst[insnr].d08);
+	opl->write(0xe3 + op, inst[insnr].d09);
+	opl->write(0xc0 + chan, inst[insnr].d0a);
+
+	// set frequency & play
+	channel[chan].key = 1;
+	setfreq(chan);
+}
+
+void Cs3mPlayer::slide_down(unsigned char chan, unsigned char amount)
+{
+	if(channel[chan].freq - amount > 340)
+		channel[chan].freq -= amount;
+	else
+		if(channel[chan].oct > 0) {
+			channel[chan].oct--;
+			channel[chan].freq = 684;
+		} else
+			channel[chan].freq = 340;
+}
+
+void Cs3mPlayer::slide_up(unsigned char chan, unsigned char amount)
+{
+	if(channel[chan].freq + amount < 686)
+		channel[chan].freq += amount;
+	else
+		if(channel[chan].oct < 7) {
+			channel[chan].oct++;
+			channel[chan].freq = 341;
+		} else
+			channel[chan].freq = 686;
+}
+
+void Cs3mPlayer::vibrato(unsigned char chan, unsigned char info)
+{
+	unsigned char i,speed,depth;
+
+	speed = info >> 4;
+	depth = (info & 0x0f) / 2;
+
+	for(i=0;i<speed;i++) {
+		channel[chan].trigger++;
+		while(channel[chan].trigger >= 64)
+			channel[chan].trigger -= 64;
+		if(channel[chan].trigger >= 16 && channel[chan].trigger < 48)
+			slide_down(chan,(unsigned char) (vibratotab[channel[chan].trigger - 16] / (16-depth)));
+		if(channel[chan].trigger < 16)
+			slide_up(chan,(unsigned char) (vibratotab[channel[chan].trigger + 16] / (16-depth)));
+		if(channel[chan].trigger >= 48)
+			slide_up(chan,(unsigned char) (vibratotab[channel[chan].trigger - 48] / (16-depth)));
+	}
+	setfreq(chan);
+}
+
+void Cs3mPlayer::tone_portamento(unsigned char chan, unsigned char info)
+{
+	if(channel[chan].freq + (channel[chan].oct << 10) < channel[chan].nextfreq +
+		(channel[chan].nextoct << 10))
+		slide_up(chan,info);
+	if(channel[chan].freq + (channel[chan].oct << 10) > channel[chan].nextfreq +
+		(channel[chan].nextoct << 10))
+		slide_down(chan,info);
+	setfreq(chan);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/adplug/core/s3m.h	Sat Jan 07 06:18:41 2006 -0800
@@ -0,0 +1,107 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * s3m.h - AdLib S3M Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_S3M
+#define H_ADPLUG_S3M
+
+#include "player.h"
+
+class Cs3mPlayer: public CPlayer
+{
+public:
+  static CPlayer *factory(Copl *newopl);
+
+	Cs3mPlayer(Copl *newopl);
+
+	bool load(const std::string &filename, const CFileProvider &fp);
+	bool update();
+	void rewind(int subsong);
+	float getrefresh();
+
+	std::string gettype();
+	std::string gettitle()
+	{ return std::string(header.name); };
+
+	unsigned int getpatterns()
+	{ return header.patnum; };
+	unsigned int getpattern()
+	{ return orders[ord]; };
+	unsigned int getorders()
+	{ return (header.ordnum-1); };
+	unsigned int getorder()
+	{ return ord; };
+	unsigned int getrow()
+	{ return crow; };
+	unsigned int getspeed()
+	{ return speed; };
+	unsigned int getinstruments()
+	{ return header.insnum; };
+	std::string getinstrument(unsigned int n)
+	{ return std::string(inst[n].name); };
+
+protected:
+	struct s3mheader {
+		char name[28];				// song name
+		unsigned char kennung,typ,dummy[2];
+		unsigned short ordnum,insnum,patnum,flags,cwtv,ffi;
+		char scrm[4];
+		unsigned char gv,is,it,mv,uc,dp,dummy2[8];
+		unsigned short special;
+		unsigned char chanset[32];
+	};
+
+	struct s3minst {
+		unsigned char type;
+		char filename[15];
+		unsigned char d00,d01,d02,d03,d04,d05,d06,d07,d08,d09,d0a,d0b,volume,dsk,dummy[2];
+		unsigned long c2spd;
+		char dummy2[12], name[28],scri[4];
+	} inst[99];
+
+	struct {
+		unsigned char note,oct,instrument,volume,command,info;
+	} pattern[99][64][32];
+
+	struct {
+		unsigned short freq,nextfreq;
+		unsigned char oct,vol,inst,fx,info,dualinfo,key,nextoct,trigger,note;
+	} channel[9];
+
+	s3mheader header;
+	unsigned char orders[256];
+	unsigned char crow,ord,speed,tempo,del,songend,loopstart,loopcnt;
+
+private:
+	static const char chnresolv[];
+	static const unsigned short notetable[12];
+	static const unsigned char vibratotab[32];
+
+	void load_header(binistream *f, s3mheader *h);
+	void setvolume(unsigned char chan);
+	void setfreq(unsigned char chan);
+	void playnote(unsigned char chan);
+	void slide_down(unsigned char chan, unsigned char amount);
+	void slide_up(unsigned char chan, unsigned char amount);
+	void vibrato(unsigned char chan, unsigned char info);
+	void tone_portamento(unsigned char chan, unsigned char info);
+};
+
+#endif