summaryrefslogtreecommitdiff
path: root/plugins/adplug
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-12-19 15:46:14 +0100
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-12-19 15:46:14 +0100
commit3f060fbd1ded8236ad27988173011ef9e9298a20 (patch)
treec7575a02421c08ad0a83fb21ece5d412d40e43dd /plugins/adplug
parent25f13980b6f30093f80174c8055cc42a47772d0d (diff)
adplug plugin WIP (insert works)
Diffstat (limited to 'plugins/adplug')
-rw-r--r--plugins/adplug/Makefile.am71
-rw-r--r--plugins/adplug/adplug-db.cpp171
-rw-r--r--plugins/adplug/adplug/a2m.cpp483
-rw-r--r--plugins/adplug/adplug/a2m.h83
-rw-r--r--plugins/adplug/adplug/adl.cpp2431
-rw-r--r--plugins/adplug/adplug/adl.h65
-rw-r--r--plugins/adplug/adplug/adlibemu.c600
-rw-r--r--plugins/adplug/adplug/adlibemu.h26
-rw-r--r--plugins/adplug/adplug/adplug.cpp182
-rw-r--r--plugins/adplug/adplug/adplug.h56
-rw-r--r--plugins/adplug/adplug/adtrack.cpp176
-rw-r--r--plugins/adplug/adplug/adtrack.h53
-rw-r--r--plugins/adplug/adplug/amd.cpp193
-rw-r--r--plugins/adplug/adplug/amd.h49
-rw-r--r--plugins/adplug/adplug/analopl.cpp67
-rw-r--r--plugins/adplug/adplug/analopl.h44
-rw-r--r--plugins/adplug/adplug/bam.cpp203
-rw-r--r--plugins/adplug/adplug/bam.h56
-rw-r--r--plugins/adplug/adplug/bmf.cpp597
-rw-r--r--plugins/adplug/adplug/bmf.h91
-rw-r--r--plugins/adplug/adplug/cff.cpp508
-rw-r--r--plugins/adplug/adplug/cff.h103
-rw-r--r--plugins/adplug/adplug/d00.cpp545
-rw-r--r--plugins/adplug/adplug/d00.h109
-rw-r--r--plugins/adplug/adplug/database.cpp426
-rw-r--r--plugins/adplug/adplug/database.h169
-rw-r--r--plugins/adplug/adplug/debug.c57
-rw-r--r--plugins/adplug/adplug/debug.h41
-rw-r--r--plugins/adplug/adplug/dfm.cpp115
-rw-r--r--plugins/adplug/adplug/dfm.h52
-rw-r--r--plugins/adplug/adplug/diskopl.cpp90
-rw-r--r--plugins/adplug/adplug/diskopl.h52
-rw-r--r--plugins/adplug/adplug/dmo.cpp403
-rw-r--r--plugins/adplug/adplug/dmo.h51
-rw-r--r--plugins/adplug/adplug/dro.cpp130
-rw-r--r--plugins/adplug/adplug/dro.h52
-rw-r--r--plugins/adplug/adplug/dtm.cpp317
-rw-r--r--plugins/adplug/adplug/dtm.h69
-rw-r--r--plugins/adplug/adplug/emuopl.cpp147
-rw-r--r--plugins/adplug/adplug/emuopl.h49
-rw-r--r--plugins/adplug/adplug/flash.cpp230
-rw-r--r--plugins/adplug/adplug/flash.h57
-rw-r--r--plugins/adplug/adplug/fmc.cpp224
-rw-r--r--plugins/adplug/adplug/fmc.h91
-rw-r--r--plugins/adplug/adplug/fmopl.c1390
-rw-r--r--plugins/adplug/adplug/fmopl.h174
-rw-r--r--plugins/adplug/adplug/fprovide.cpp76
-rw-r--r--plugins/adplug/adplug/fprovide.h50
-rw-r--r--plugins/adplug/adplug/hsc.cpp317
-rw-r--r--plugins/adplug/adplug/hsc.h75
-rw-r--r--plugins/adplug/adplug/hsp.cpp68
-rw-r--r--plugins/adplug/adplug/hsp.h39
-rw-r--r--plugins/adplug/adplug/hybrid.cpp248
-rw-r--r--plugins/adplug/adplug/hybrid.h80
-rw-r--r--plugins/adplug/adplug/hyp.cpp125
-rw-r--r--plugins/adplug/adplug/hyp.h53
-rw-r--r--plugins/adplug/adplug/imf.cpp196
-rw-r--r--plugins/adplug/adplug/imf.h68
-rw-r--r--plugins/adplug/adplug/kemuopl.h61
-rw-r--r--plugins/adplug/adplug/ksm.cpp336
-rw-r--r--plugins/adplug/adplug/ksm.h62
-rw-r--r--plugins/adplug/adplug/lds.cpp676
-rw-r--r--plugins/adplug/adplug/lds.h91
-rw-r--r--plugins/adplug/adplug/mad.cpp127
-rw-r--r--plugins/adplug/adplug/mad.h48
-rw-r--r--plugins/adplug/adplug/mid.cpp1090
-rw-r--r--plugins/adplug/adplug/mid.h112
-rw-r--r--plugins/adplug/adplug/mididata.h174
-rw-r--r--plugins/adplug/adplug/mkj.cpp164
-rw-r--r--plugins/adplug/adplug/mkj.h50
-rw-r--r--plugins/adplug/adplug/msc.cpp313
-rw-r--r--plugins/adplug/adplug/msc.h87
-rw-r--r--plugins/adplug/adplug/mtk.cpp141
-rw-r--r--plugins/adplug/adplug/mtk.h50
-rw-r--r--plugins/adplug/adplug/opl.h69
-rw-r--r--plugins/adplug/adplug/player.cpp70
-rw-r--r--plugins/adplug/adplug/player.h84
-rw-r--r--plugins/adplug/adplug/players.cpp105
-rw-r--r--plugins/adplug/adplug/players.h60
-rw-r--r--plugins/adplug/adplug/protrack.cpp820
-rw-r--r--plugins/adplug/adplug/protrack.h119
-rw-r--r--plugins/adplug/adplug/psi.cpp177
-rw-r--r--plugins/adplug/adplug/psi.h64
-rw-r--r--plugins/adplug/adplug/rad.cpp125
-rw-r--r--plugins/adplug/adplug/rad.h44
-rw-r--r--plugins/adplug/adplug/rat.cpp293
-rw-r--r--plugins/adplug/adplug/rat.h121
-rw-r--r--plugins/adplug/adplug/raw.cpp104
-rw-r--r--plugins/adplug/adplug/raw.h52
-rw-r--r--plugins/adplug/adplug/realopl.cpp208
-rw-r--r--plugins/adplug/adplug/realopl.h72
-rw-r--r--plugins/adplug/adplug/rix.cpp495
-rw-r--r--plugins/adplug/adplug/rix.h112
-rw-r--r--plugins/adplug/adplug/rol.cpp723
-rw-r--r--plugins/adplug/adplug/rol.h309
-rw-r--r--plugins/adplug/adplug/s3m.cpp536
-rw-r--r--plugins/adplug/adplug/s3m.h107
-rw-r--r--plugins/adplug/adplug/sa2.cpp262
-rw-r--r--plugins/adplug/adplug/sa2.h55
-rw-r--r--plugins/adplug/adplug/silentopl.h29
-rw-r--r--plugins/adplug/adplug/sng.cpp85
-rw-r--r--plugins/adplug/adplug/sng.h64
-rw-r--r--plugins/adplug/adplug/temuopl.cpp75
-rw-r--r--plugins/adplug/adplug/temuopl.h47
-rw-r--r--plugins/adplug/adplug/u6m.cpp934
-rw-r--r--plugins/adplug/adplug/u6m.h168
-rw-r--r--plugins/adplug/adplug/xad.cpp140
-rw-r--r--plugins/adplug/adplug/xad.h97
-rw-r--r--plugins/adplug/adplug/xsm.cpp117
-rw-r--r--plugins/adplug/adplug/xsm.h46
-rw-r--r--plugins/adplug/libbinio/binfile.cpp247
-rw-r--r--plugins/adplug/libbinio/binfile.h110
-rw-r--r--plugins/adplug/libbinio/binio.cpp640
-rw-r--r--plugins/adplug/libbinio/binio.h175
-rw-r--r--plugins/adplug/libbinio/binstr.cpp114
-rw-r--r--plugins/adplug/libbinio/binstr.h66
-rw-r--r--plugins/adplug/libbinio/binwrap.cpp132
-rw-r--r--plugins/adplug/libbinio/binwrap.h92
-rw-r--r--plugins/adplug/plugin.c68
119 files changed, 24957 insertions, 0 deletions
diff --git a/plugins/adplug/Makefile.am b/plugins/adplug/Makefile.am
new file mode 100644
index 00000000..52cbb90d
--- /dev/null
+++ b/plugins/adplug/Makefile.am
@@ -0,0 +1,71 @@
+adlibdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = adplug.la
+adplug_la_SOURCES = adplug-db.cpp\
+ plugin.c\
+ adplug/adplug.cpp\
+ adplug/emuopl.cpp\
+ adplug/fmopl.c\
+ adplug/diskopl.cpp\
+ adplug/debug.c\
+ adplug/debug.h\
+ adplug/fprovide.cpp\
+ adplug/player.cpp\
+ adplug/database.cpp\
+ adplug/hsc.cpp\
+ adplug/sng.cpp\
+ adplug/imf.cpp\
+ adplug/players.cpp\
+ adplug/protrack.cpp\
+ adplug/a2m.cpp\
+ adplug/adtrack.cpp\
+ adplug/amd.cpp\
+ adplug/bam.cpp\
+ adplug/d00.cpp\
+ adplug/dfm.cpp\
+ adplug/hsp.cpp\
+ adplug/ksm.cpp\
+ adplug/mad.cpp\
+ adplug/mid.cpp\
+ adplug/mkj.cpp\
+ adplug/cff.cpp\
+ adplug/dmo.cpp\
+ adplug/s3m.cpp\
+ adplug/dtm.cpp\
+ adplug/fmc.cpp\
+ adplug/mtk.cpp\
+ adplug/rad.cpp\
+ adplug/raw.cpp\
+ adplug/sa2.cpp\
+ adplug/xad.cpp\
+ adplug/flash.cpp\
+ adplug/bmf.cpp\
+ adplug/hybrid.cpp\
+ adplug/hyp.cpp\
+ adplug/psi.cpp\
+ adplug/rat.cpp\
+ adplug/u6m.cpp\
+ adplug/rol.cpp\
+ adplug/mididata.h\
+ adplug/xsm.cpp\
+ adplug/adlibemu.c\
+ adplug/dro.cpp\
+ adplug/lds.cpp\
+ adplug/realopl.cpp\
+ adplug/analopl.cpp\
+ adplug/temuopl.cpp\
+ adplug/msc.cpp\
+ adplug/rix.cpp\
+ adplug/adl.cpp\
+ libbinio/binfile.h\
+ libbinio/binio.h\
+ libbinio/binstr.h\
+ libbinio/binwrap.h\
+ libbinio/binfile.cpp\
+ libbinio/binio.cpp\
+ libbinio/binstr.cpp\
+ libbinio/binwrap.cpp
+
+adplug_la_LDFLAGS = -module
+
+AM_CXXFLAGS = $(CFLAGS) -Dstricmp=strcasecmp -DVERSION=\"2.1\" -I adplug -I libbinio
+AM_CFLAGS = $(CFLAGS) -std=c99
diff --git a/plugins/adplug/adplug-db.cpp b/plugins/adplug/adplug-db.cpp
new file mode 100644
index 00000000..51f25241
--- /dev/null
+++ b/plugins/adplug/adplug-db.cpp
@@ -0,0 +1,171 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "../../deadbeef.h"
+#include "adplug.h"
+#include "emuopl.h"
+#include "silentopl.h"
+
+#define trace(...) { fprintf (stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+extern "C" {
+
+extern DB_decoder_t adplug_plugin;
+static DB_functions_t *deadbeef;
+
+
+const char *adplug_exts[] = { "A2M", "ADL", "AMD", "BAM", "CFF", "CMF", "D00", "DFM", "DMO", "DRO", "DTM", "HSC", "HSP", "IMF", "KSM", "LAA", "LDS", "M", "MAD", "MID", "MKJ", "MSC", "MTK", "RAD", "RAW", "RIX", "ROL", "S3M", "SA2", "SAT", "SCI", "SNG", "SNG", "SNG", "XAD", "XMS", "XSM", NULL };
+
+const char *adplug_filetypes[] = { "A2M", "ADL", "AMD", "BAM", "CFF", "CMF", "D00", "DFM", "DMO", "DRO", "DTM", "HSC", "HSP", "IMF", "KSM", "LAA", "LDS", "M", "MAD", "MID", "MKJ", "MSC", "MTK", "RAD", "RAW", "RIX", "ROL", "S3M", "SA2", "SAT", "SCI", "SNG", "SNG", "SNG", "XAD", "XMS", "XSM", NULL };
+
+int
+adplug_init (DB_playItem_t *it) {
+ // prepare to decode the track
+ // return -1 on failure
+
+ // fill in mandatory plugin fields
+ //adplug_plugin.info.bps = deadbeef->get_output ()->bitspersample ();
+ //adplug_plugin.info.channels = deadbeef->get_output ()->channels ();
+ //adplug_plugin.info.samplerate = decoder->samplerate;
+ //adplug_plugin.info.readpos = 0;
+
+ return 0;
+}
+
+void
+adplug_free (void) {
+ // free everything allocated in _init
+}
+
+int
+adplug_read_int16 (char *bytes, int size) {
+ // try decode `size' bytes
+ // return number of decoded bytes
+ // return 0 on EOF
+
+ //adplug_plugin.info.readpos = (float)currentsample / adplug_plugin.info.samplerate;
+ return size;
+}
+
+int
+adplug_seek_sample (int sample) {
+ // seek to specified sample (frame)
+ // return 0 on success
+ // return -1 on failure
+
+ // update readpos
+ adplug_plugin.info.readpos = (float)sample / adplug_plugin.info.samplerate;
+ return 0;
+}
+
+int
+adplug_seek (float time) {
+ // seek to specified time in seconds
+ // return 0 on success
+ // return -1 on failure
+ return adplug_seek_sample (time * adplug_plugin.info.samplerate);
+ return 0;
+}
+
+static const char *
+adplug_get_extension (const char *fname) {
+ const char *e = fname + strlen (fname);
+ while (*e != '.' && e != fname) {
+ e--;
+ }
+ if (*e == '.') {
+ e++;
+ // now find ext in list
+ }
+ return "adplug-unknown";
+}
+
+DB_playItem_t *
+adplug_insert (DB_playItem_t *after, const char *fname) {
+ // read information from the track
+ // load/process cuesheet if exists
+ // insert track into playlist
+ // return track pointer on success
+ // return NULL on failure
+
+ CSilentopl opl;
+ CPlayers::const_iterator i;
+ CPlayer *p = CAdPlug::factory (fname, &opl, CAdPlug::players);
+ if (!p) {
+ trace ("adplug: failed to open %s\n", fname);
+ return NULL;
+ }
+
+ int subsongs = p->getsubsongs ();
+ for (int i = 0; i < subsongs; i++) {
+ // prepare track for addition
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder = &adplug_plugin;
+ it->fname = strdup (fname);
+ it->filetype = adplug_get_extension (fname);
+ deadbeef->pl_set_item_duration (it, p->songlength (i)/1000.f);
+ // add metainfo
+ if (!p->gettitle().empty()) {
+ deadbeef->pl_add_meta (it, "title", p->gettitle().c_str());
+ }
+ else if (!p->getdesc().empty()) {
+ deadbeef->pl_add_meta (it, "title", p->getdesc().c_str());
+ }
+ else {
+ deadbeef->pl_add_meta (it, "title", NULL);
+ }
+ deadbeef->pl_add_meta (it, "artist", p->getauthor().c_str());
+ // insert
+ after = deadbeef->pl_insert_item (after, it);
+ }
+
+ // free decoder
+ delete p;
+
+ // now the track is ready, insert into playlist
+ return after;
+}
+
+int
+adplug_start (void) {
+ // do one-time plugin initialization here
+ // e.g. starting threads for background processing, subscribing to events, etc
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+
+int
+adplug_stop (void) {
+ // undo everything done in _start here
+ // return 0 on success
+ // return -1 on failure
+ return 0;
+}
+
+DB_plugin_t *
+adplug_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&adplug_plugin);
+}
+
+}
diff --git a/plugins/adplug/adplug/a2m.cpp b/plugins/adplug/adplug/a2m.cpp
new file mode 100644
index 00000000..3fd68d98
--- /dev/null
+++ b/plugins/adplug/adplug/a2m.cpp
@@ -0,0 +1,483 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * a2m.cpp - A2M Loader by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * This loader detects and loads version 1, 4, 5 & 8 files.
+ *
+ * version 1-4 files:
+ * Following commands are ignored: FF1 - FF9, FAx - FEx
+ *
+ * version 5-8 files:
+ * Instrument panning is ignored. Flags byte is ignored.
+ * Following commands are ignored: Gxy, Hxy, Kxy - &xy
+ */
+
+#include <string.h>
+#include "a2m.h"
+
+const unsigned int Ca2mLoader::MAXFREQ = 2000,
+Ca2mLoader::MINCOPY = ADPLUG_A2M_MINCOPY,
+Ca2mLoader::MAXCOPY = ADPLUG_A2M_MAXCOPY,
+Ca2mLoader::COPYRANGES = ADPLUG_A2M_COPYRANGES,
+Ca2mLoader::CODESPERRANGE = ADPLUG_A2M_CODESPERRANGE,
+Ca2mLoader::TERMINATE = 256,
+Ca2mLoader::FIRSTCODE = ADPLUG_A2M_FIRSTCODE,
+Ca2mLoader::MAXCHAR = FIRSTCODE + COPYRANGES * CODESPERRANGE - 1,
+Ca2mLoader::SUCCMAX = MAXCHAR + 1,
+Ca2mLoader::TWICEMAX = ADPLUG_A2M_TWICEMAX,
+Ca2mLoader::ROOT = 1, Ca2mLoader::MAXBUF = 42 * 1024,
+Ca2mLoader::MAXDISTANCE = 21389, Ca2mLoader::MAXSIZE = 21389 + MAXCOPY;
+
+const unsigned short Ca2mLoader::bitvalue[14] =
+ {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192};
+
+const signed short Ca2mLoader::copybits[COPYRANGES] =
+ {4, 6, 8, 10, 12, 14};
+
+const signed short Ca2mLoader::copymin[COPYRANGES] =
+ {0, 16, 80, 336, 1360, 5456};
+
+CPlayer *Ca2mLoader::factory(Copl *newopl)
+{
+ return new Ca2mLoader(newopl);
+}
+
+bool Ca2mLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[10];
+ int i,j,k,t;
+ unsigned int l;
+ unsigned char *org, *orgptr, flags = 0, numpats, version;
+ unsigned long crc, alength;
+ unsigned short len[9], *secdata, *secptr;
+ const unsigned char convfx[16] = {0,1,2,23,24,3,5,4,6,9,17,13,11,19,7,14};
+ const unsigned char convinf1[16] = {0,1,2,6,7,8,9,4,5,3,10,11,12,13,14,15};
+ const unsigned char newconvfx[] = {0,1,2,3,4,5,6,23,24,21,10,11,17,13,7,19,
+ 255,255,22,25,255,15,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,14,255};
+
+ // read header
+ f->readString(id, 10); crc = f->readInt(4);
+ version = f->readInt(1); numpats = f->readInt(1);
+
+ // file validation section
+ if(strncmp(id,"_A2module_",10) || (version != 1 && version != 5 &&
+ version != 4 && version != 8)) {
+ fp.close(f);
+ return false;
+ }
+
+ // load, depack & convert section
+ nop = numpats; length = 128; restartpos = 0;
+ if(version < 5) {
+ for(i=0;i<5;i++) len[i] = f->readInt(2);
+ t = 9;
+ } else { // version >= 5
+ for(i=0;i<9;i++) len[i] = f->readInt(2);
+ t = 18;
+ }
+
+ // block 0
+ secdata = new unsigned short [len[0] / 2];
+ if(version == 1 || version == 5) {
+ for(i=0;i<len[0]/2;i++) secdata[i] = f->readInt(2);
+ org = new unsigned char [MAXBUF]; orgptr = org;
+ sixdepak(secdata,org,len[0]);
+ } else {
+ orgptr = (unsigned char *)secdata;
+ for(i=0;i<len[0];i++) orgptr[i] = f->readInt(1);
+ }
+ memcpy(songname,orgptr,43); orgptr += 43;
+ memcpy(author,orgptr,43); orgptr += 43;
+ memcpy(instname,orgptr,250*33); orgptr += 250*33;
+
+ for(i=0;i<250;i++) { // instruments
+ inst[i].data[0] = *(orgptr+i*13+10);
+ inst[i].data[1] = *(orgptr+i*13);
+ inst[i].data[2] = *(orgptr+i*13+1);
+ inst[i].data[3] = *(orgptr+i*13+4);
+ inst[i].data[4] = *(orgptr+i*13+5);
+ inst[i].data[5] = *(orgptr+i*13+6);
+ inst[i].data[6] = *(orgptr+i*13+7);
+ inst[i].data[7] = *(orgptr+i*13+8);
+ inst[i].data[8] = *(orgptr+i*13+9);
+ inst[i].data[9] = *(orgptr+i*13+2);
+ inst[i].data[10] = *(orgptr+i*13+3);
+
+ if(version < 5)
+ inst[i].misc = *(orgptr+i*13+11);
+ else { // version >= 5 -> OPL3 format
+ int pan = *(orgptr+i*13+11);
+
+ if(pan)
+ inst[i].data[0] |= (pan & 3) << 4; // set pan
+ else
+ inst[i].data[0] |= 48; // enable both speakers
+ }
+
+ inst[i].slide = *(orgptr+i*13+12);
+ }
+
+ orgptr += 250*13;
+ memcpy(order,orgptr,128); orgptr += 128;
+ bpm = *orgptr; orgptr++;
+ initspeed = *orgptr; orgptr++;
+ if(version >= 5) flags = *orgptr;
+ if(version == 1 || version == 5) delete [] org;
+ delete [] secdata;
+
+ // blocks 1-4 or 1-8
+ alength = len[1];
+ for(i = 0; i < (version < 5 ? numpats / 16 : numpats / 8); i++)
+ alength += len[i+2];
+
+ secdata = new unsigned short [alength / 2];
+ if(version == 1 || version == 5) {
+ for(l=0;l<alength/2;l++) secdata[l] = f->readInt(2);
+ org = new unsigned char [MAXBUF * (numpats / (version == 1 ? 16 : 8) + 1)];
+ orgptr = org; secptr = secdata;
+ orgptr += sixdepak(secptr,orgptr,len[1]); secptr += len[1] / 2;
+ if(version == 1) {
+ if(numpats > 16)
+ orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2;
+ if(numpats > 32)
+ orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2;
+ if(numpats > 48)
+ sixdepak(secptr,orgptr,len[4]);
+ } else {
+ if(numpats > 8)
+ orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2;
+ if(numpats > 16)
+ orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2;
+ if(numpats > 24)
+ orgptr += sixdepak(secptr,orgptr,len[4]); secptr += len[4] / 2;
+ if(numpats > 32)
+ orgptr += sixdepak(secptr,orgptr,len[5]); secptr += len[5] / 2;
+ if(numpats > 40)
+ orgptr += sixdepak(secptr,orgptr,len[6]); secptr += len[6] / 2;
+ if(numpats > 48)
+ orgptr += sixdepak(secptr,orgptr,len[7]); secptr += len[7] / 2;
+ if(numpats > 56)
+ sixdepak(secptr,orgptr,len[8]);
+ }
+ delete [] secdata;
+ } else {
+ org = (unsigned char *)secdata;
+ for(l=0;l<alength;l++) org[l] = f->readInt(1);
+ }
+
+ if(version < 5) {
+ for(i=0;i<numpats;i++)
+ for(j=0;j<64;j++)
+ for(k=0;k<9;k++) {
+ struct Tracks *track = &tracks[i * 9 + k][j];
+ unsigned char *o = &org[i*64*t*4+j*t*4+k*4];
+
+ track->note = o[0] == 255 ? 127 : o[0];
+ track->inst = o[1];
+ track->command = convfx[o[2]];
+ track->param2 = o[3] & 0x0f;
+ if(track->command != 14)
+ track->param1 = o[3] >> 4;
+ else {
+ track->param1 = convinf1[o[3] >> 4];
+ if(track->param1 == 15 && !track->param2) { // convert key-off
+ track->command = 8;
+ track->param1 = 0;
+ track->param2 = 0;
+ }
+ }
+ if(track->command == 14) {
+ switch(track->param1) {
+ case 2: // convert define waveform
+ track->command = 25;
+ track->param1 = track->param2;
+ track->param2 = 0xf;
+ break;
+ case 8: // convert volume slide up
+ track->command = 26;
+ track->param1 = track->param2;
+ track->param2 = 0;
+ break;
+ case 9: // convert volume slide down
+ track->command = 26;
+ track->param1 = 0;
+ break;
+ }
+ }
+ }
+ } else { // version >= 5
+ realloc_patterns(64, 64, 18);
+
+ for(i=0;i<numpats;i++)
+ for(j=0;j<18;j++)
+ for(k=0;k<64;k++) {
+ struct Tracks *track = &tracks[i * 18 + j][k];
+ unsigned char *o = &org[i*64*t*4+j*64*4+k*4];
+
+ track->note = o[0] == 255 ? 127 : o[0];
+ track->inst = o[1];
+ track->command = newconvfx[o[2]];
+ track->param1 = o[3] >> 4;
+ track->param2 = o[3] & 0x0f;
+
+ // Convert '&' command
+ if(o[2] == 36)
+ switch(track->param1) {
+ case 0: // pattern delay (frames)
+ track->command = 29;
+ track->param1 = 0;
+ // param2 already set correctly
+ break;
+
+ case 1: // pattern delay (rows)
+ track->command = 14;
+ track->param1 = 8;
+ // param2 already set correctly
+ break;
+ }
+ }
+ }
+
+ init_trackord();
+
+ if(version == 1 || version == 5)
+ delete [] org;
+ else
+ delete [] secdata;
+
+ // Process flags
+ if(version >= 5) {
+ CmodPlayer::flags |= Opl3; // All versions >= 5 are OPL3
+ if(flags & 8) CmodPlayer::flags |= Tremolo; // Tremolo depth
+ if(flags & 16) CmodPlayer::flags |= Vibrato; // Vibrato depth
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+float Ca2mLoader::getrefresh()
+{
+ if(tempo != 18)
+ return (float) (tempo);
+ else
+ return 18.2f;
+}
+
+/*** private methods *************************************/
+
+void Ca2mLoader::inittree()
+{
+ unsigned short i;
+
+ for(i=2;i<=TWICEMAX;i++) {
+ dad[i] = i / 2;
+ freq[i] = 1;
+ }
+
+ for(i=1;i<=MAXCHAR;i++) {
+ leftc[i] = 2 * i;
+ rghtc[i] = 2 * i + 1;
+ }
+}
+
+void Ca2mLoader::updatefreq(unsigned short a,unsigned short b)
+{
+ do {
+ freq[dad[a]] = freq[a] + freq[b];
+ a = dad[a];
+ if(a != ROOT)
+ if(leftc[dad[a]] == a)
+ b = rghtc[dad[a]];
+ else
+ b = leftc[dad[a]];
+ } while(a != ROOT);
+
+ if(freq[ROOT] == MAXFREQ)
+ for(a=1;a<=TWICEMAX;a++)
+ freq[a] >>= 1;
+}
+
+void Ca2mLoader::updatemodel(unsigned short code)
+{
+ unsigned short a=code+SUCCMAX,b,c,code1,code2;
+
+ freq[a]++;
+ if(dad[a] != ROOT) {
+ code1 = dad[a];
+ if(leftc[code1] == a)
+ updatefreq(a,rghtc[code1]);
+ else
+ updatefreq(a,leftc[code1]);
+
+ do {
+ code2 = dad[code1];
+ if(leftc[code2] == code1)
+ b = rghtc[code2];
+ else
+ b = leftc[code2];
+
+ if(freq[a] > freq[b]) {
+ if(leftc[code2] == code1)
+ rghtc[code2] = a;
+ else
+ leftc[code2] = a;
+
+ if(leftc[code1] == a) {
+ leftc[code1] = b;
+ c = rghtc[code1];
+ } else {
+ rghtc[code1] = b;
+ c = leftc[code1];
+ }
+
+ dad[b] = code1;
+ dad[a] = code2;
+ updatefreq(b,c);
+ a = b;
+ }
+
+ a = dad[a];
+ code1 = dad[a];
+ } while(code1 != ROOT);
+ }
+}
+
+unsigned short Ca2mLoader::inputcode(unsigned short bits)
+{
+ unsigned short i,code=0;
+
+ for(i=1;i<=bits;i++) {
+ if(!ibitcount) {
+ if(ibitcount == MAXBUF)
+ ibufcount = 0;
+ ibitbuffer = wdbuf[ibufcount];
+ ibufcount++;
+ ibitcount = 15;
+ } else
+ ibitcount--;
+
+ if(ibitbuffer > 0x7fff)
+ code |= bitvalue[i-1];
+ ibitbuffer <<= 1;
+ }
+
+ return code;
+}
+
+unsigned short Ca2mLoader::uncompress()
+{
+ unsigned short a=1;
+
+ do {
+ if(!ibitcount) {
+ if(ibufcount == MAXBUF)
+ ibufcount = 0;
+ ibitbuffer = wdbuf[ibufcount];
+ ibufcount++;
+ ibitcount = 15;
+ } else
+ ibitcount--;
+
+ if(ibitbuffer > 0x7fff)
+ a = rghtc[a];
+ else
+ a = leftc[a];
+ ibitbuffer <<= 1;
+ } while(a <= MAXCHAR);
+
+ a -= SUCCMAX;
+ updatemodel(a);
+ return a;
+}
+
+void Ca2mLoader::decode()
+{
+ unsigned short i,j,k,t,c,count=0,dist,len,index;
+
+ inittree();
+ c = uncompress();
+
+ while(c != TERMINATE) {
+ if(c < 256) {
+ obuf[obufcount] = (unsigned char)c;
+ obufcount++;
+ if(obufcount == MAXBUF) {
+ output_size = MAXBUF;
+ obufcount = 0;
+ }
+
+ buf[count] = (unsigned char)c;
+ count++;
+ if(count == MAXSIZE)
+ count = 0;
+ } else {
+ t = c - FIRSTCODE;
+ index = t / CODESPERRANGE;
+ len = t + MINCOPY - index * CODESPERRANGE;
+ dist = inputcode(copybits[index]) + len + copymin[index];
+
+ j = count;
+ k = count - dist;
+ if(count < dist)
+ k += MAXSIZE;
+
+ for(i=0;i<=len-1;i++) {
+ obuf[obufcount] = buf[k];
+ obufcount++;
+ if(obufcount == MAXBUF) {
+ output_size = MAXBUF;
+ obufcount = 0;
+ }
+
+ buf[j] = buf[k];
+ j++; k++;
+ if(j == MAXSIZE) j = 0;
+ if(k == MAXSIZE) k = 0;
+ }
+
+ count += len;
+ if(count >= MAXSIZE)
+ count -= MAXSIZE;
+ }
+ c = uncompress();
+ }
+ output_size = obufcount;
+}
+
+unsigned short Ca2mLoader::sixdepak(unsigned short *source, unsigned char *dest,
+ unsigned short size)
+{
+ if((unsigned int)size + 4096 > MAXBUF)
+ return 0;
+
+ buf = new unsigned char [MAXSIZE];
+ input_size = size;
+ ibitcount = 0; ibitbuffer = 0;
+ obufcount = 0; ibufcount = 0;
+ wdbuf = source; obuf = dest;
+
+ decode();
+ delete [] buf;
+ return output_size;
+}
diff --git a/plugins/adplug/adplug/a2m.h b/plugins/adplug/adplug/a2m.h
new file mode 100644
index 00000000..9e032f61
--- /dev/null
+++ b/plugins/adplug/adplug/a2m.h
@@ -0,0 +1,83 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * a2m.h - A2M Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_A2MLOADER
+#define H_ADPLUG_A2MLOADER
+
+#include "protrack.h"
+
+class Ca2mLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ Ca2mLoader(Copl *newopl): CmodPlayer(newopl)
+ { }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("AdLib Tracker 2"); }
+ std::string gettitle()
+ { if(*songname) return std::string(songname,1,*songname); else return std::string(); }
+ std::string getauthor()
+ { if(*author) return std::string(author,1,*author); else return std::string(); }
+ unsigned int getinstruments()
+ { return 250; }
+ std::string getinstrument(unsigned int n)
+ { return std::string(instname[n],1,*instname[n]); }
+
+private:
+
+#define ADPLUG_A2M_COPYRANGES 6
+#define ADPLUG_A2M_FIRSTCODE 257
+#define ADPLUG_A2M_MINCOPY 3
+#define ADPLUG_A2M_MAXCOPY 255
+#define ADPLUG_A2M_CODESPERRANGE (ADPLUG_A2M_MAXCOPY - ADPLUG_A2M_MINCOPY + 1)
+#define ADPLUG_A2M_MAXCHAR (ADPLUG_A2M_FIRSTCODE + ADPLUG_A2M_COPYRANGES * ADPLUG_A2M_CODESPERRANGE - 1)
+#define ADPLUG_A2M_TWICEMAX (2 * ADPLUG_A2M_MAXCHAR + 1)
+
+ static const unsigned int MAXFREQ, MINCOPY, MAXCOPY, COPYRANGES,
+ CODESPERRANGE, TERMINATE, FIRSTCODE, MAXCHAR, SUCCMAX, TWICEMAX, ROOT,
+ MAXBUF, MAXDISTANCE, MAXSIZE;
+
+ static const unsigned short bitvalue[14];
+ static const signed short copybits[ADPLUG_A2M_COPYRANGES],
+ copymin[ADPLUG_A2M_COPYRANGES];
+
+ void inittree();
+ void updatefreq(unsigned short a,unsigned short b);
+ void updatemodel(unsigned short code);
+ unsigned short inputcode(unsigned short bits);
+ unsigned short uncompress();
+ void decode();
+ unsigned short sixdepak(unsigned short *source,unsigned char *dest,unsigned short size);
+
+ char songname[43], author[43], instname[250][33];
+
+ unsigned short ibitcount, ibitbuffer, ibufcount, obufcount, input_size,
+ output_size, leftc[ADPLUG_A2M_MAXCHAR+1], rghtc[ADPLUG_A2M_MAXCHAR+1],
+ dad[ADPLUG_A2M_TWICEMAX+1], freq[ADPLUG_A2M_TWICEMAX+1], *wdbuf;
+ unsigned char *obuf, *buf;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/adl.cpp b/plugins/adplug/adplug/adl.cpp
new file mode 100644
index 00000000..40896fdb
--- /dev/null
+++ b/plugins/adplug/adplug/adl.cpp
@@ -0,0 +1,2431 @@
+/*
+ * adl.cpp - ADL player adaption by Simon Peter <dn.tlp@gmx.net>
+ *
+ * Original ADL player by Torbjorn Andersson and Johannes Schickel
+ * 'lordhoto' <lordhoto at scummvm dot org> of the ScummVM project.
+ */
+
+/* ScummVM - Scumm Interpreter
+ *
+ * This file is licensed under both GPL and LGPL
+ * Copyright (C) 2006 The ScummVM project
+ * Copyright (C) 2006 Torbjorn Andersson and Johannes Schickel
+ *
+ * GPL License
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * LPGL License
+ *
+ * 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
+ *
+ * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/kyra/sound_adlib.cpp $
+ * $Id: adl.cpp,v 1.9 2006/08/16 00:20:45 dynamite Exp $
+ *
+ */
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+
+#include "adl.h"
+#include "debug.h"
+
+#ifdef ADL_DEBUG
+# define warning(...) AdPlug_LogWrite(__VA_ARGS__); \
+AdPlug_LogWrite("\n")
+
+# define debugC(i1, i2, ...) AdPlug_LogWrite(__VA_ARGS__); \
+AdPlug_LogWrite("\n")
+#else
+# define kDebugLevelSound 1
+
+static inline void warning(const char *str, ...)
+{
+}
+
+static inline void debugC(int i1, int i2, const char *str, ...)
+{
+}
+#endif
+
+// #define warning(...)
+// #define debugC(i1, i2, ...)
+
+#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+
+// Basic Adlib Programming:
+// http://www.gamedev.net/reference/articles/article446.asp
+
+#define CALLBACKS_PER_SECOND 72
+
+typedef uint8_t uint8;
+typedef int8_t int8;
+typedef uint16_t uint16;
+typedef int16_t int16;
+typedef uint32_t uint32;
+typedef int32_t int32;
+typedef uint8_t byte;
+
+static inline uint16 READ_LE_UINT16(const void *ptr) {
+ const byte *b = (const byte *)ptr;
+ return (b[1] << 8) + b[0];
+}
+
+static inline uint16 READ_BE_UINT16(const void *ptr) {
+ const byte *b = (const byte *)ptr;
+ return (b[0] << 8) + b[1];
+}
+
+class AdlibDriver {
+public:
+ AdlibDriver(Copl *opl);
+ ~AdlibDriver();
+
+ int callback(int opcode, ...);
+ void callback();
+
+ // AudioStream API
+ // int readBuffer(int16 *buffer, const int numSamples) {
+ // int32 samplesLeft = numSamples;
+ // memset(buffer, 0, sizeof(int16) * numSamples);
+ // while (samplesLeft) {
+ // if (!_samplesTillCallback) {
+ // callback();
+ // _samplesTillCallback = _samplesPerCallback;
+ // _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+ // if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
+ // _samplesTillCallback++;
+ // _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
+ // }
+ // }
+
+ // int32 render = MIN(samplesLeft, _samplesTillCallback);
+ // samplesLeft -= render;
+ // _samplesTillCallback -= render;
+ // YM3812UpdateOne(_adlib, buffer, render);
+ // buffer += render;
+ // }
+ // return numSamples;
+ // }
+
+ bool isStereo() const { return false; }
+ bool endOfData() const { return false; }
+ // int getRate() const { return _mixer->getOutputRate(); }
+
+ struct OpcodeEntry {
+ typedef int (AdlibDriver::*DriverOpcode)(va_list &list);
+ DriverOpcode function;
+ const char *name;
+ };
+
+ void setupOpcodeList();
+ const OpcodeEntry *_opcodeList;
+ int _opcodesEntries;
+
+ int snd_ret0x100(va_list &list);
+ int snd_ret0x1983(va_list &list);
+ int snd_initDriver(va_list &list);
+ int snd_deinitDriver(va_list &list);
+ int snd_setSoundData(va_list &list);
+ int snd_unkOpcode1(va_list &list);
+ int snd_startSong(va_list &list);
+ int snd_unkOpcode2(va_list &list);
+ int snd_unkOpcode3(va_list &list);
+ int snd_readByte(va_list &list);
+ int snd_writeByte(va_list &list);
+ int snd_getSoundTrigger(va_list &list);
+ int snd_unkOpcode4(va_list &list);
+ int snd_dummy(va_list &list);
+ int snd_getNullvar4(va_list &list);
+ int snd_setNullvar3(va_list &list);
+ int snd_setFlag(va_list &list);
+ int snd_clearFlag(va_list &list);
+
+ // These variables have not yet been named, but some of them are partly
+ // known nevertheless:
+ //
+ // unk16 - Sound-related. Possibly some sort of pitch bend.
+ // unk18 - Sound-effect. Used for secondaryEffect1()
+ // unk19 - Sound-effect. Used for secondaryEffect1()
+ // unk20 - Sound-effect. Used for secondaryEffect1()
+ // unk21 - Sound-effect. Used for secondaryEffect1()
+ // unk22 - Sound-effect. Used for secondaryEffect1()
+ // unk29 - Sound-effect. Used for primaryEffect1()
+ // unk30 - Sound-effect. Used for primaryEffect1()
+ // unk31 - Sound-effect. Used for primaryEffect1()
+ // unk32 - Sound-effect. Used for primaryEffect2()
+ // unk33 - Sound-effect. Used for primaryEffect2()
+ // unk34 - Sound-effect. Used for primaryEffect2()
+ // unk35 - Sound-effect. Used for primaryEffect2()
+ // unk36 - Sound-effect. Used for primaryEffect2()
+ // unk37 - Sound-effect. Used for primaryEffect2()
+ // unk38 - Sound-effect. Used for primaryEffect2()
+ // unk39 - Currently unused, except for updateCallback56()
+ // unk40 - Currently unused, except for updateCallback56()
+ // unk41 - Sound-effect. Used for primaryEffect2()
+
+ struct Channel {
+ uint8 opExtraLevel2;
+ uint8 *dataptr;
+ uint8 duration;
+ uint8 repeatCounter;
+ int8 baseOctave;
+ uint8 priority;
+ uint8 dataptrStackPos;
+ uint8 *dataptrStack[4];
+ int8 baseNote;
+ uint8 unk29;
+ uint8 unk31;
+ uint16 unk30;
+ uint16 unk37;
+ uint8 unk33;
+ uint8 unk34;
+ uint8 unk35;
+ uint8 unk36;
+ uint8 unk32;
+ uint8 unk41;
+ uint8 unk38;
+ uint8 opExtraLevel1;
+ uint8 spacing2;
+ uint8 baseFreq;
+ uint8 tempo;
+ uint8 position;
+ uint8 regAx;
+ uint8 regBx;
+ typedef void (AdlibDriver::*Callback)(Channel&);
+ Callback primaryEffect;
+ Callback secondaryEffect;
+ uint8 fractionalSpacing;
+ uint8 opLevel1;
+ uint8 opLevel2;
+ uint8 opExtraLevel3;
+ uint8 twoChan;
+ uint8 unk39;
+ uint8 unk40;
+ uint8 spacing1;
+ uint8 durationRandomness;
+ uint8 unk19;
+ uint8 unk18;
+ int8 unk20;
+ int8 unk21;
+ uint8 unk22;
+ uint16 offset;
+ uint8 tempoReset;
+ uint8 rawNote;
+ int8 unk16;
+ };
+
+ void primaryEffect1(Channel &channel);
+ void primaryEffect2(Channel &channel);
+ void secondaryEffect1(Channel &channel);
+
+ void resetAdlibState();
+ void writeOPL(byte reg, byte val);
+ void initChannel(Channel &channel);
+ void noteOff(Channel &channel);
+ void unkOutput2(uint8 num);
+
+ uint16 getRandomNr();
+ void setupDuration(uint8 duration, Channel &channel);
+
+ void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
+ void setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel);
+ void noteOn(Channel &channel);
+
+ void adjustVolume(Channel &channel);
+
+ uint8 calculateOpLevel1(Channel &channel);
+ uint8 calculateOpLevel2(Channel &channel);
+
+ uint16 checkValue(int16 val) {
+ if (val < 0)
+ val = 0;
+ else if (val > 0x3F)
+ val = 0x3F;
+ return val;
+ }
+
+ // The sound data has at least two lookup tables:
+ //
+ // * One for programs, starting at offset 0.
+ // * One for instruments, starting at offset 500.
+
+ uint8 *getProgram(int progId) {
+ return _soundData + READ_LE_UINT16(_soundData + 2 * progId);
+ }
+
+ uint8 *getInstrument(int instrumentId) {
+ return _soundData + READ_LE_UINT16(_soundData + 500 + 2 * instrumentId);
+ }
+
+ void setupPrograms();
+ void executePrograms();
+
+ struct ParserOpcode {
+ typedef int (AdlibDriver::*POpcode)(uint8 *&dataptr, Channel &channel, uint8 value);
+ POpcode function;
+ const char *name;
+ };
+
+ void setupParserOpcodeTable();
+ const ParserOpcode *_parserOpcodeTable;
+ int _parserOpcodeTableSize;
+
+ int update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_jump(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playRest(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playNote(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_nop1(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_nop2(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value);
+
+ // These variables have not yet been named, but some of them are partly
+ // known nevertheless:
+ //
+ // _unkValue1 - Unknown. Used for updating _unkValue2
+ // _unkValue2 - Unknown. Used for updating _unkValue4
+ // _unkValue3 - Unknown. Used for updating _unkValue2
+ // _unkValue4 - Unknown. Used for updating _unkValue5
+ // _unkValue5 - Unknown. Used for controlling updateCallback24().
+ // _unkValue6 - Unknown. Rhythm section volume?
+ // _unkValue7 - Unknown. Rhythm section volume?
+ // _unkValue8 - Unknown. Rhythm section volume?
+ // _unkValue9 - Unknown. Rhythm section volume?
+ // _unkValue10 - Unknown. Rhythm section volume?
+ // _unkValue11 - Unknown. Rhythm section volume?
+ // _unkValue12 - Unknown. Rhythm section volume?
+ // _unkValue13 - Unknown. Rhythm section volume?
+ // _unkValue14 - Unknown. Rhythm section volume?
+ // _unkValue15 - Unknown. Rhythm section volume?
+ // _unkValue16 - Unknown. Rhythm section volume?
+ // _unkValue17 - Unknown. Rhythm section volume?
+ // _unkValue18 - Unknown. Rhythm section volume?
+ // _unkValue19 - Unknown. Rhythm section volume?
+ // _unkValue20 - Unknown. Rhythm section volume?
+ // _unkTable[] - Probably frequences for the 12-tone scale.
+ // _unkTable2[] - Unknown. Currently only used by updateCallback46()
+ // _unkTable2_1[] - One of the tables in _unkTable2[]
+ // _unkTable2_2[] - One of the tables in _unkTable2[]
+ // _unkTable2_3[] - One of the tables in _unkTable2[]
+
+ int32 _samplesPerCallback;
+ int32 _samplesPerCallbackRemainder;
+ int32 _samplesTillCallback;
+ int32 _samplesTillCallbackRemainder;
+
+ int _lastProcessed;
+ int8 _flagTrigger;
+ int _curChannel;
+ uint8 _soundTrigger;
+ int _soundsPlaying;
+
+ uint16 _rnd;
+
+ uint8 _unkValue1;
+ uint8 _unkValue2;
+ uint8 _unkValue3;
+ uint8 _unkValue4;
+ uint8 _unkValue5;
+ uint8 _unkValue6;
+ uint8 _unkValue7;
+ uint8 _unkValue8;
+ uint8 _unkValue9;
+ uint8 _unkValue10;
+ uint8 _unkValue11;
+ uint8 _unkValue12;
+ uint8 _unkValue13;
+ uint8 _unkValue14;
+ uint8 _unkValue15;
+ uint8 _unkValue16;
+ uint8 _unkValue17;
+ uint8 _unkValue18;
+ uint8 _unkValue19;
+ uint8 _unkValue20;
+
+ int _flags;
+
+ uint8 *_soundData;
+
+ uint8 _soundIdTable[0x10];
+ Channel _channels[10];
+
+ uint8 _vibratoAndAMDepthBits;
+ uint8 _rhythmSectionBits;
+
+ uint8 _curRegOffset;
+ uint8 _tempo;
+
+ const uint8 *_tablePtr1;
+ const uint8 *_tablePtr2;
+
+ static const uint8 _regOffset[];
+ static const uint16 _unkTable[];
+ static const uint8 *_unkTable2[];
+ static const uint8 _unkTable2_1[];
+ static const uint8 _unkTable2_2[];
+ static const uint8 _unkTable2_3[];
+ static const uint8 _unkTables[][32];
+
+ Copl *opl;
+};
+
+AdlibDriver::AdlibDriver(Copl *newopl)
+ : opl(newopl)
+{
+ setupOpcodeList();
+ setupParserOpcodeTable();
+
+ // _mixer = mixer;
+
+ _flags = 0;
+ // _adlib = makeAdlibOPL(getRate());
+ // assert(_adlib);
+
+ memset(_channels, 0, sizeof(_channels));
+ _soundData = 0;
+
+ _vibratoAndAMDepthBits = _curRegOffset = 0;
+
+ _lastProcessed = _flagTrigger = _curChannel = _rhythmSectionBits = 0;
+ _soundsPlaying = 0;
+ _rnd = 0x1234;
+
+ _tempo = 0;
+ _soundTrigger = 0;
+
+ _unkValue3 = 0xFF;
+ _unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0;
+ _unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0;
+ _unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 =
+ _unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0;
+
+ _tablePtr1 = _tablePtr2 = 0;
+
+ // _mixer->setupPremix(this);
+
+ // _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
+ // _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
+ _samplesTillCallback = 0;
+ _samplesTillCallbackRemainder = 0;
+}
+
+AdlibDriver::~AdlibDriver() {
+ // _mixer->setupPremix(0);
+ // OPLDestroy(_adlib);
+ // _adlib = 0;
+}
+
+int AdlibDriver::callback(int opcode, ...) {
+ // lock();
+ if (opcode >= _opcodesEntries || opcode < 0) {
+ warning("AdlibDriver: calling unknown opcode '%d'", opcode);
+ return 0;
+ }
+
+ debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d)", _opcodeList[opcode].name, opcode);
+
+ va_list args;
+ va_start(args, opcode);
+ int returnValue = (this->*(_opcodeList[opcode].function))(args);
+ va_end(args);
+ // unlock();
+ return returnValue;
+}
+
+// Opcodes
+
+int AdlibDriver::snd_ret0x100(va_list &list) {
+ return 0x100;
+}
+
+int AdlibDriver::snd_ret0x1983(va_list &list) {
+ return 0x1983;
+}
+
+int AdlibDriver::snd_initDriver(va_list &list) {
+ _lastProcessed = _soundsPlaying = 0;
+ resetAdlibState();
+ return 0;
+}
+
+int AdlibDriver::snd_deinitDriver(va_list &list) {
+ resetAdlibState();
+ return 0;
+}
+
+int AdlibDriver::snd_setSoundData(va_list &list) {
+ if (_soundData) {
+ delete [] _soundData;
+ _soundData = 0;
+ }
+ _soundData = va_arg(list, uint8*);
+ return 0;
+}
+
+int AdlibDriver::snd_unkOpcode1(va_list &list) {
+ warning("unimplemented snd_unkOpcode1");
+ return 0;
+}
+
+int AdlibDriver::snd_startSong(va_list &list) {
+ int songId = va_arg(list, int);
+ _flags |= 8;
+ _flagTrigger = 1;
+
+ uint8 *ptr = getProgram(songId);
+ uint8 chan = *ptr;
+
+ if ((songId << 1) != 0) {
+ if (chan == 9) {
+ if (_flags & 2)
+ return 0;
+ } else {
+ if (_flags & 1)
+ return 0;
+ }
+ }
+
+ _soundIdTable[_soundsPlaying++] = songId;
+ _soundsPlaying &= 0x0F;
+
+ return 0;
+}
+
+int AdlibDriver::snd_unkOpcode2(va_list &list) {
+ warning("unimplemented snd_unkOpcode2");
+ return 0;
+}
+
+int AdlibDriver::snd_unkOpcode3(va_list &list) {
+ int value = va_arg(list, int);
+ int loop = value;
+ if (value < 0) {
+ value = 0;
+ loop = 9;
+ }
+ loop -= value;
+ ++loop;
+
+ while (loop--) {
+ _curChannel = value;
+ Channel &channel = _channels[_curChannel];
+ channel.priority = 0;
+ channel.dataptr = 0;
+ if (value != 9) {
+ noteOff(channel);
+ }
+ ++value;
+ }
+
+ return 0;
+}
+
+int AdlibDriver::snd_readByte(va_list &list) {
+ int a = va_arg(list, int);
+ int b = va_arg(list, int);
+ uint8 *ptr = getProgram(a) + b;
+ return *ptr;
+}
+
+int AdlibDriver::snd_writeByte(va_list &list) {
+ int a = va_arg(list, int);
+ int b = va_arg(list, int);
+ int c = va_arg(list, int);
+ uint8 *ptr = getProgram(a) + b;
+ uint8 oldValue = *ptr;
+ *ptr = (uint8)c;
+ return oldValue;
+}
+
+int AdlibDriver::snd_getSoundTrigger(va_list &list) {
+ return _soundTrigger;
+}
+
+int AdlibDriver::snd_unkOpcode4(va_list &list) {
+ warning("unimplemented snd_unkOpcode4");
+ return 0;
+}
+
+int AdlibDriver::snd_dummy(va_list &list) {
+ return 0;
+}
+
+int AdlibDriver::snd_getNullvar4(va_list &list) {
+ warning("unimplemented snd_getNullvar4");
+ return 0;
+}
+
+int AdlibDriver::snd_setNullvar3(va_list &list) {
+ warning("unimplemented snd_setNullvar3");
+ return 0;
+}
+
+int AdlibDriver::snd_setFlag(va_list &list) {
+ int oldFlags = _flags;
+ _flags |= va_arg(list, int);
+ return oldFlags;
+}
+
+int AdlibDriver::snd_clearFlag(va_list &list) {
+ int oldFlags = _flags;
+ _flags &= ~(va_arg(list, int));
+ return oldFlags;
+}
+
+// timer callback
+
+void AdlibDriver::callback() {
+ // lock();
+ --_flagTrigger;
+ if (_flagTrigger < 0)
+ _flags &= ~8;
+ setupPrograms();
+ executePrograms();
+
+ uint8 temp = _unkValue3;
+ _unkValue3 += _tempo;
+ if (_unkValue3 < temp) {
+ if (!(--_unkValue2)) {
+ _unkValue2 = _unkValue1;
+ ++_unkValue4;
+ }
+ }
+ // unlock();
+}
+
+void AdlibDriver::setupPrograms() {
+ while (_lastProcessed != _soundsPlaying) {
+ uint8 *ptr = getProgram(_soundIdTable[_lastProcessed]);
+ uint8 chan = *ptr++;
+ uint8 priority = *ptr++;
+
+ // Only start this sound if its priority is higher than the one
+ // already playing.
+
+ Channel &channel = _channels[chan];
+
+ if (priority >= channel.priority) {
+ initChannel(channel);
+ channel.priority = priority;
+ channel.dataptr = ptr;
+ channel.tempo = 0xFF;
+ channel.position = 0xFF;
+ channel.duration = 1;
+ unkOutput2(chan);
+ }
+
+ ++_lastProcessed;
+ _lastProcessed &= 0x0F;
+ }
+}
+
+// A few words on opcode parsing and timing:
+//
+// First of all, We simulate a timer callback 72 times per second. Each timeout
+// we update each channel that has something to play.
+//
+// Each channel has its own individual tempo, which is added to its position.
+// This will frequently cause the position to "wrap around" but that is
+// intentional. In fact, it's the signal to go ahead and do more stuff with
+// that channel.
+//
+// Each channel also has a duration, indicating how much time is left on the
+// its current task. This duration is decreased by one. As long as it still has
+// not reached zero, the only thing that can happen is that the note is turned
+// off depending on manual or automatic note spacing. Once the duration reaches
+// zero, a new set of musical opcodes are executed.
+//
+// An opcode is one byte, followed by a variable number of parameters. Since
+// most opcodes have at least one one-byte parameter, we read that as well. Any
+// opcode that doesn't have that one parameter is responsible for moving the
+// data pointer back again.
+//
+// If the most significant bit of the opcode is 1, it's a function; call it.
+// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do
+// not run the effects callbacks).
+//
+// If the most significant bit of the opcode is 0, it's a note, and the first
+// parameter is its duration. (There are cases where the duration is modified
+// but that's an exception.) The note opcode is assumed to return 1, and is the
+// last opcode unless its duration is zero.
+//
+// Finally, most of the times that the callback is called, it will invoke the
+// effects callbacks. The final opcode in a set can prevent this, if it's a
+// function and it returns anything other than 1.
+
+void AdlibDriver::executePrograms() {
+ // Each channel runs its own program. There are ten channels: One for
+ // each Adlib channel (0-8), plus one "control channel" (9) which is
+ // the one that tells the other channels what to do.
+
+ for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
+ int result = 1;
+
+ if (!_channels[_curChannel].dataptr) {
+ continue;
+ }
+
+ Channel &channel = _channels[_curChannel];
+ _curRegOffset = _regOffset[_curChannel];
+
+ if (channel.tempoReset) {
+ channel.tempo = _tempo;
+ }
+
+ uint8 backup = channel.position;
+ channel.position += channel.tempo;
+ if (channel.position < backup) {
+ if (--channel.duration) {
+ if (channel.duration == channel.spacing2)
+ noteOff(channel);
+ if (channel.duration == channel.spacing1 && _curChannel != 9)
+ noteOff(channel);
+ } else {
+ // An opcode is not allowed to modify its own
+ // data pointer except through the 'dataptr'
+ // parameter. To enforce that, we have to work
+ // on a copy of the data pointer.
+ //
+ // This fixes a subtle music bug where the
+ // wrong music would play when getting the
+ // quill in Kyra 1.
+ uint8 *dataptr = channel.dataptr;
+ while (dataptr) {
+ uint8 opcode = *dataptr++;
+ uint8 param = *dataptr++;
+
+ if (opcode & 0x80) {
+ opcode &= 0x7F;
+ if (opcode >= _parserOpcodeTableSize)
+ opcode = _parserOpcodeTableSize - 1;
+ debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel);
+ result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param);
+ channel.dataptr = dataptr;
+ if (result)
+ break;
+ } else {
+ debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel);
+ setupNote(opcode, channel);
+ noteOn(channel);
+ setupDuration(param, channel);
+ if (param) {
+ channel.dataptr = dataptr;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (result == 1) {
+ if (channel.primaryEffect)
+ (this->*(channel.primaryEffect))(channel);
+ if (channel.secondaryEffect)
+ (this->*(channel.secondaryEffect))(channel);
+ }
+ }
+}
+
+//
+
+void AdlibDriver::resetAdlibState() {
+ debugC(9, kDebugLevelSound, "resetAdlibState()");
+ _rnd = 0x1234;
+
+ // Authorize the control of the waveforms
+ writeOPL(0x01, 0x20);
+
+ // Select FM music mode
+ writeOPL(0x08, 0x00);
+
+ // I would guess the main purpose of this is to turn off the rhythm,
+ // thus allowing us to use 9 melodic voices instead of 6.
+ writeOPL(0xBD, 0x00);
+
+ int loop = 10;
+ while (loop--) {
+ if (loop != 9) {
+ // Silence the channel
+ writeOPL(0x40 + _regOffset[loop], 0x3F);
+ writeOPL(0x43 + _regOffset[loop], 0x3F);
+ }
+ initChannel(_channels[loop]);
+ }
+}
+
+// Old calling style: output0x388(0xABCD)
+// New calling style: writeOPL(0xAB, 0xCD)
+
+void AdlibDriver::writeOPL(byte reg, byte val) {
+ opl->write(reg, val);
+}
+
+void AdlibDriver::initChannel(Channel &channel) {
+ debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels));
+ memset(&channel.dataptr, 0, sizeof(Channel) - ((char*)&channel.dataptr - (char*)&channel));
+
+ channel.tempo = 0xFF;
+ channel.priority = 0;
+ // normally here are nullfuncs but we set 0 for now
+ channel.primaryEffect = 0;
+ channel.secondaryEffect = 0;
+ channel.spacing1 = 1;
+}
+
+void AdlibDriver::noteOff(Channel &channel) {
+ debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels));
+
+ // The control channel has no corresponding Adlib channel
+
+ if (_curChannel >= 9)
+ return;
+
+ // When the rhythm section is enabled, channels 6, 7 and 8 are special.
+
+ if (_rhythmSectionBits && _curChannel >= 6)
+ return;
+
+ // This means the "Key On" bit will always be 0
+ channel.regBx &= 0xDF;
+
+ // Octave / F-Number / Key-On
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+}
+
+void AdlibDriver::unkOutput2(uint8 chan) {
+ debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan);
+
+ // The control channel has no corresponding Adlib channel
+
+ if (chan >= 9)
+ return;
+
+ // I believe this has to do with channels 6, 7, and 8 being special
+ // when Adlib's rhythm section is enabled.
+
+ if (_rhythmSectionBits && chan >= 6)
+ return;
+
+ uint8 offset = _regOffset[chan];
+
+ // The channel is cleared: First the attack/delay rate, then the
+ // sustain level/release rate, and finally the note is turned off.
+
+ writeOPL(0x60 + offset, 0xFF);
+ writeOPL(0x63 + offset, 0xFF);
+
+ writeOPL(0x80 + offset, 0xFF);
+ writeOPL(0x83 + offset, 0xFF);
+
+ writeOPL(0xB0 + chan, 0x00);
+
+ // ...and then the note is turned on again, with whatever value is
+ // still lurking in the A0 + chan register, but everything else -
+ // including the two most significant frequency bit, and the octave -
+ // set to zero.
+ //
+ // This is very strange behaviour, and causes problems with the ancient
+ // FMOPL code we borrowed from AdPlug. I've added a workaround. See
+ // fmopl.cpp for more details.
+ //
+ // More recent versions of the MAME FMOPL don't seem to have this
+ // problem, but cannot currently be used because of licensing and
+ // performance issues.
+ //
+ // Ken Silverman's Adlib emulator (which can be found on his Web page -
+ // http://www.advsys.net/ken - and as part of AdPlug) also seems to be
+ // immune, but is apparently not as feature complete as MAME's.
+
+ writeOPL(0xB0 + chan, 0x20);
+}
+
+// I believe this is a random number generator. It actually does seem to
+// generate an even distribution of almost all numbers from 0 through 65535,
+// though in my tests some numbers were never generated.
+
+uint16 AdlibDriver::getRandomNr() {
+ _rnd += 0x9248;
+ uint16 lowBits = _rnd & 7;
+ _rnd >>= 3;
+ _rnd |= (lowBits << 13);
+ return _rnd;
+}
+
+void AdlibDriver::setupDuration(uint8 duration, Channel &channel) {
+ debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels));
+ if (channel.durationRandomness) {
+ channel.duration = duration + (getRandomNr() & channel.durationRandomness);
+ return;
+ }
+ if (channel.fractionalSpacing) {
+ channel.spacing2 = (duration >> 3) * channel.fractionalSpacing;
+ }
+ channel.duration = duration;
+}
+
+// This function may or may not play the note. It's usually followed by a call
+// to noteOn(), which will always play the current note.
+
+void AdlibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) {
+ debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels));
+
+ channel.rawNote = rawNote;
+
+ int8 note = (rawNote & 0x0F) + channel.baseNote;
+ int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F;
+
+ // There are only twelve notes. If we go outside that, we have to
+ // adjust the note and octave.
+
+ if (note >= 12) {
+ note -= 12;
+ octave++;
+ } else if (note < 0) {
+ note += 12;
+ octave--;
+ }
+
+ // The calculation of frequency looks quite different from the original
+ // disassembly at a first glance, but when you consider that the
+ // largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if
+ // baseFreq is unsigned), freq is still a 10-bit value, just as it
+ // should be to fit in the Ax and Bx registers.
+ //
+ // If it were larger than that, it could have overflowed into the
+ // octave bits, and that could possibly have been used in some sound.
+ // But as it is now, I can't see any way it would happen.
+
+ uint16 freq = _unkTable[note] + channel.baseFreq;
+
+ // When called from callback 41, the behaviour is slightly different:
+ // We adjust the frequency, even when channel.unk16 is 0.
+
+ if (channel.unk16 || flag) {
+ const uint8 *table;
+
+ if (channel.unk16 >= 0) {
+ table = _unkTables[(channel.rawNote & 0x0F) + 2];
+ freq += table[channel.unk16];
+ } else {
+ table = _unkTables[channel.rawNote & 0x0F];
+ freq -= table[-channel.unk16];
+ }
+ }
+
+ channel.regAx = freq & 0xFF;
+ channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03);
+
+ // Keep the note on or off
+ writeOPL(0xA0 + _curChannel, channel.regAx);
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+}
+
+void AdlibDriver::setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel) {
+ debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels));
+ // Amplitude Modulation / Vibrato / Envelope Generator Type /
+ // Keyboard Scaling Rate / Modulator Frequency Multiple
+ writeOPL(0x20 + regOffset, *dataptr++);
+ writeOPL(0x23 + regOffset, *dataptr++);
+
+ uint8 temp = *dataptr++;
+
+ // Feedback / Algorithm
+
+ // It is very likely that _curChannel really does refer to the same
+ // channel as regOffset, but there's only one Cx register per channel.
+
+ writeOPL(0xC0 + _curChannel, temp);
+
+ // The algorithm bit. I don't pretend to understand this fully, but
+ // "If set to 0, operator 1 modulates operator 2. In this case,
+ // operator 2 is the only one producing sound. If set to 1, both
+ // operators produce sound directly. Complex sounds are more easily
+ // created if the algorithm is set to 0."
+
+ channel.twoChan = temp & 1;
+
+ // Waveform Select
+ writeOPL(0xE0 + regOffset, *dataptr++);
+ writeOPL(0xE3 + regOffset, *dataptr++);
+
+ channel.opLevel1 = *dataptr++;
+ channel.opLevel2 = *dataptr++;
+
+ // Level Key Scaling / Total Level
+ writeOPL(0x40 + regOffset, calculateOpLevel1(channel));
+ writeOPL(0x43 + regOffset, calculateOpLevel2(channel));
+
+ // Attack Rate / Decay Rate
+ writeOPL(0x60 + regOffset, *dataptr++);
+ writeOPL(0x63 + regOffset, *dataptr++);
+
+ // Sustain Level / Release Rate
+ writeOPL(0x80 + regOffset, *dataptr++);
+ writeOPL(0x83 + regOffset, *dataptr++);
+}
+
+// Apart from playing the note, this function also updates the variables for
+// primary effect 2.
+
+void AdlibDriver::noteOn(Channel &channel) {
+ debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels));
+
+ // The "note on" bit is set, and the current note is played.
+
+ channel.regBx |= 0x20;
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+
+ int8 shift = 9 - channel.unk33;
+ uint16 temp = channel.regAx | (channel.regBx << 8);
+ channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF;
+ channel.unk38 = channel.unk36;
+}
+
+void AdlibDriver::adjustVolume(Channel &channel) {
+ debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels));
+ // Level Key Scaling / Total Level
+
+ writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel));
+ if (channel.twoChan)
+ writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel));
+}
+
+// This is presumably only used for some sound effects, e.g. Malcolm blowing up
+// the trees in the intro (but not the effect where he "booby-traps" the big
+// tree) and turning Kallak to stone. Related functions and variables:
+//
+// update_setupPrimaryEffect1()
+// - Initialises unk29, unk30 and unk31
+// - unk29 is not further modified
+// - unk30 is not further modified, except by update_removePrimaryEffect1()
+//
+// update_removePrimaryEffect1()
+// - Deinitialises unk30
+//
+// unk29 - determines how often the notes are played
+// unk30 - modifies the frequency
+// unk31 - determines how often the notes are played
+
+void AdlibDriver::primaryEffect1(Channel &channel) {
+ debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel);
+ uint8 temp = channel.unk31;
+ channel.unk31 += channel.unk29;
+ if (channel.unk31 >= temp)
+ return;
+
+ // Initialise unk1 to the current frequency
+ uint16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx;
+
+ // This is presumably to shift the "note on" bit so far to the left
+ // that it won't be affected by any of the calculations below.
+ uint16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C);
+
+ int16 unk3 = (int16)channel.unk30;
+
+ if (unk3 >= 0) {
+ unk1 += unk3;
+ if (unk1 >= 734) {
+ // The new frequency is too high. Shift it down and go
+ // up one octave.
+ unk1 >>= 1;
+ if (!(unk1 & 0x3FF))
+ ++unk1;
+ unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF);
+ unk2 &= 0xFF1C;
+ }
+ } else {
+ unk1 += unk3;
+ if (unk1 < 388) {
+ // The new frequency is too low. Shift it up and go
+ // down one octave.
+ unk1 <<= 1;
+ if (!(unk1 & 0x3FF))
+ --unk1;
+ unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF);
+ unk2 &= 0xFF1C;
+ }
+ }
+
+ // Make sure that the new frequency is still a 10-bit value.
+ unk1 &= 0x3FF;
+
+ writeOPL(0xA0 + _curChannel, unk1 & 0xFF);
+ channel.regAx = unk1 & 0xFF;
+
+ // Shift down the "note on" bit again.
+ uint8 value = unk1 >> 8;
+ value |= (unk2 >> 8) & 0xFF;
+ value |= unk2 & 0xFF;
+
+ writeOPL(0xB0 + _curChannel, value);
+ channel.regBx = value;
+}
+
+// This is presumably only used for some sound effects, e.g. Malcolm entering
+// and leaving Kallak's hut. Related functions and variables:
+//
+// update_setupPrimaryEffect2()
+// - Initialises unk32, unk33, unk34, unk35 and unk36
+// - unk32 is not further modified
+// - unk33 is not further modified
+// - unk34 is a countdown that gets reinitialised to unk35 on zero
+// - unk35 is based on unk34 and not further modified
+// - unk36 is not further modified
+//
+// noteOn()
+// - Plays the current note
+// - Updates unk37 with a new (lower?) frequency
+// - Copies unk36 to unk38. The unk38 variable is a countdown.
+//
+// unk32 - determines how often the notes are played
+// unk33 - modifies the frequency
+// unk34 - countdown, updates frequency on zero
+// unk35 - initialiser for unk34 countdown
+// unk36 - initialiser for unk38 countdown
+// unk37 - frequency
+// unk38 - countdown, begins playing on zero
+// unk41 - determines how often the notes are played
+//
+// Note that unk41 is never initialised. Not that it should matter much, but it
+// is a bit sloppy.
+
+void AdlibDriver::primaryEffect2(Channel &channel) {
+ debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel);
+ if (channel.unk38) {
+ --channel.unk38;
+ return;
+ }
+
+ uint8 temp = channel.unk41;
+ channel.unk41 += channel.unk32;
+ if (channel.unk41 < temp) {
+ uint16 unk1 = channel.unk37;
+ if (!(--channel.unk34)) {
+ unk1 ^= 0xFFFF;
+ ++unk1;
+ channel.unk37 = unk1;
+ channel.unk34 = channel.unk35;
+ }
+
+ uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF;
+ unk2 += unk1;
+
+ channel.regAx = unk2 & 0xFF;
+ channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8);
+
+ // Octave / F-Number / Key-On
+ writeOPL(0xA0 + _curChannel, channel.regAx);
+ writeOPL(0xB0 + _curChannel, channel.regBx);
+ }
+}
+
+// I don't know where this is used. The same operation is performed several
+// times on the current channel, using a chunk of the _soundData[] buffer for
+// parameters. The parameters are used starting at the end of the chunk.
+//
+// Since we use _curRegOffset to specify the final register, it's quite
+// unlikely that this function is ever used to play notes. It's probably only
+// used to modify the sound. Another thing that supports this idea is that it
+// can be combined with any of the effects callbacks above.
+//
+// Related functions and variables:
+//
+// update_setupSecondaryEffect1()
+// - Initialies unk18, unk19, unk20, unk21, unk22 and offset
+// - unk19 is not further modified
+// - unk20 is not further modified
+// - unk22 is not further modified
+// - offset is not further modified
+//
+// unk18 - determines how often the operation is performed
+// unk19 - determines how often the operation is performed
+// unk20 - the start index into the data chunk
+// unk21 - the current index into the data chunk
+// unk22 - the operation to perform
+// offset - the offset to the data chunk
+
+void AdlibDriver::secondaryEffect1(Channel &channel) {
+ debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel);
+ uint8 temp = channel.unk18;
+ channel.unk18 += channel.unk19;
+ if (channel.unk18 < temp) {
+ if (--channel.unk21 < 0) {
+ channel.unk21 = channel.unk20;
+ }
+ writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]);
+ }
+}
+
+uint8 AdlibDriver::calculateOpLevel1(Channel &channel) {
+ int8 value = channel.opLevel1 & 0x3F;
+
+ if (channel.twoChan) {
+ value += channel.opExtraLevel1;
+ value += channel.opExtraLevel2;
+ value += channel.opExtraLevel3;
+ }
+
+ // Preserve the scaling level bits from opLevel1
+
+ return checkValue(value) | (channel.opLevel1 & 0xC0);
+}
+
+uint8 AdlibDriver::calculateOpLevel2(Channel &channel) {
+ int8 value = channel.opLevel2 & 0x3F;
+
+ value += channel.opExtraLevel1;
+ value += channel.opExtraLevel2;
+ value += channel.opExtraLevel3;
+
+ // Preserve the scaling level bits from opLevel2
+
+ return checkValue(value) | (channel.opLevel2 & 0xC0);
+}
+
+// parser opcodes
+
+int AdlibDriver::update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.repeatCounter = value;
+ return 0;
+}
+
+int AdlibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value) {
+ ++dataptr;
+ if (--channel.repeatCounter) {
+ int16 add = READ_LE_UINT16(dataptr - 2);
+ dataptr += add;
+ }
+ return 0;
+}
+
+int AdlibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (value == 0xFF)
+ return 0;
+
+ uint8 *ptr = getProgram(value);
+ uint8 chan = *ptr++;
+ uint8 priority = *ptr++;
+
+ Channel &channel2 = _channels[chan];
+
+ if (priority >= channel2.priority) {
+ _flagTrigger = 1;
+ _flags |= 8;
+ initChannel(channel2);
+ channel2.priority = priority;
+ channel2.dataptr = ptr;
+ channel2.tempo = 0xFF;
+ channel2.position = 0xFF;
+ channel2.duration = 1;
+ unkOutput2(chan);
+ }
+
+ return 0;
+}
+
+int AdlibDriver::update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.spacing1 = value;
+ return 0;
+}
+
+int AdlibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
+ dataptr += add;
+ return 0;
+}
+
+int AdlibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
+ channel.dataptrStack[channel.dataptrStackPos++] = dataptr;
+ dataptr += add;
+ return 0;
+}
+
+int AdlibDriver::update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) {
+ dataptr = channel.dataptrStack[--channel.dataptrStackPos];
+ return 0;
+}
+
+int AdlibDriver::update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.baseOctave = value;
+ return 0;
+}
+
+int AdlibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.priority = 0;
+ if (_curChannel != 9) {
+ noteOff(channel);
+ }
+ dataptr = 0;
+ return 2;
+}
+
+int AdlibDriver::update_playRest(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupDuration(value, channel);
+ noteOff(channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value) {
+ writeOPL(value, *dataptr++);
+ return 0;
+}
+
+int AdlibDriver::update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupNote(value, channel);
+ value = *dataptr++;
+ setupDuration(value, channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.baseNote = value;
+ return 0;
+}
+
+int AdlibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk18 = value;
+ channel.unk19 = value;
+ channel.unk20 = channel.unk21 = *dataptr++;
+ channel.unk22 = *dataptr++;
+ channel.offset = READ_LE_UINT16(dataptr); dataptr += 2;
+ channel.secondaryEffect = &AdlibDriver::secondaryEffect1;
+ return 0;
+}
+
+int AdlibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value) {
+ Channel &channel2 = _channels[value];
+ channel2.duration = 0;
+ channel2.priority = 0;
+ channel2.dataptr = 0;
+ return 0;
+}
+
+int AdlibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 *ptr = getProgram(value);
+ uint8 chan = *ptr;
+
+ if (!_channels[chan].dataptr) {
+ return 0;
+ }
+
+ dataptr -= 2;
+ return 2;
+}
+
+int AdlibDriver::update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupInstrument(_curRegOffset, getInstrument(value), channel);
+ return 0;
+}
+
+int AdlibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk29 = value;
+ channel.unk30 = READ_BE_UINT16(dataptr);
+ dataptr += 2;
+ channel.primaryEffect = &AdlibDriver::primaryEffect1;
+ channel.unk31 = 0xFF;
+ return 0;
+}
+
+int AdlibDriver::update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.primaryEffect = 0;
+ channel.unk30 = 0;
+ return 0;
+}
+
+int AdlibDriver::update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.baseFreq = value;
+ return 0;
+}
+
+int AdlibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk32 = value;
+ channel.unk33 = *dataptr++;
+ uint8 temp = *dataptr++;
+ channel.unk34 = temp + 1;
+ channel.unk35 = temp << 1;
+ channel.unk36 = *dataptr++;
+ channel.primaryEffect = &AdlibDriver::primaryEffect2;
+ return 0;
+}
+
+int AdlibDriver::update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.priority = value;
+ return 0;
+}
+
+int AdlibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value) {
+ value >>= 1;
+ _unkValue1 = _unkValue2 = value;
+ _unkValue3 = 0xFF;
+ _unkValue4 = _unkValue5 = 0;
+ return 0;
+}
+
+int AdlibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (_unkValue5) {
+ if (_unkValue4 & value) {
+ _unkValue5 = 0;
+ return 0;
+ }
+ }
+
+ if (!(value & _unkValue4)) {
+ ++_unkValue5;
+ }
+
+ dataptr -= 2;
+ channel.duration = 1;
+ return 2;
+}
+
+int AdlibDriver::update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.opExtraLevel1 = value;
+ adjustVolume(channel);
+ return 0;
+}
+
+int AdlibDriver::update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupDuration(value, channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_playNote(uint8 *&dataptr, Channel &channel, uint8 value) {
+ setupDuration(value, channel);
+ noteOn(channel);
+ return (value != 0);
+}
+
+int AdlibDriver::update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.fractionalSpacing = value & 7;
+ return 0;
+}
+
+int AdlibDriver::update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ _tempo = value;
+ return 0;
+}
+
+int AdlibDriver::update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.secondaryEffect = 0;
+ return 0;
+}
+
+int AdlibDriver::update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.tempo = value;
+ return 0;
+}
+
+int AdlibDriver::update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.opExtraLevel3 = value;
+ return 0;
+}
+
+int AdlibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+
+ _curChannel = value;
+ Channel &channel2 = _channels[value];
+ channel2.opExtraLevel2 = *dataptr++;
+ adjustVolume(channel2);
+
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+int AdlibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+
+ _curChannel = value;
+ Channel &channel2 = _channels[value];
+ channel2.opExtraLevel2 += *dataptr++;
+ adjustVolume(channel2);
+
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+// Apart from initialising to zero, these two functions are the only ones that
+// modify _vibratoAndAMDepthBits.
+
+int AdlibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (value & 1)
+ _vibratoAndAMDepthBits |= 0x80;
+ else
+ _vibratoAndAMDepthBits &= 0x7F;
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits);
+ return 0;
+}
+
+int AdlibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value) {
+ if (value & 1)
+ _vibratoAndAMDepthBits |= 0x40;
+ else
+ _vibratoAndAMDepthBits &= 0xBF;
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits);
+ return 0;
+}
+
+int AdlibDriver::update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.opExtraLevel1 += value;
+ adjustVolume(channel);
+ return 0;
+}
+
+int AdlibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+
+ _curChannel = value;
+ Channel &channel2 = _channels[value];
+ channel2.duration = channel2.priority = 0;
+ channel2.dataptr = 0;
+ channel2.opExtraLevel2 = 0;
+
+ if (value != 9) {
+ uint8 outValue = _regOffset[value];
+
+ // Feedback strength / Connection type
+ writeOPL(0xC0 + _curChannel, 0x00);
+
+ // Key scaling level / Operator output level
+ writeOPL(0x43 + outValue, 0x3F);
+
+ // Sustain Level / Release Rate
+ writeOPL(0x83 + outValue, 0xFF);
+
+ // Key On / Octave / Frequency
+ writeOPL(0xB0 + _curChannel, 0x00);
+ }
+
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+int AdlibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint16 unk = *dataptr++;
+ unk |= value << 8;
+ unk &= getRandomNr();
+
+ uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx;
+ unk2 += unk;
+ unk2 |= ((channel.regBx & 0x20) << 8);
+
+ // Frequency
+ writeOPL(0xA0 + _curChannel, unk2 & 0xFF);
+
+ // Key On / Octave / Frequency
+ writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8);
+
+ return 0;
+}
+
+int AdlibDriver::update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.primaryEffect = 0;
+ return 0;
+}
+
+int AdlibDriver::updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk16 = value;
+ setupNote(channel.rawNote, channel, true);
+ return 0;
+}
+
+int AdlibDriver::update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ channel.tempo = _tempo;
+ return 0;
+}
+
+int AdlibDriver::update_nop1(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ return 0;
+}
+
+int AdlibDriver::update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.durationRandomness = value;
+ return 0;
+}
+
+int AdlibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int tempo = channel.tempo + (int8)value;
+
+ if (tempo <= 0)
+ tempo = 1;
+ else if (tempo > 255)
+ tempo = 255;
+
+ channel.tempo = tempo;
+ return 0;
+}
+
+int AdlibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 entry = *dataptr++;
+ _tablePtr1 = _unkTable2[entry++];
+ _tablePtr2 = _unkTable2[entry];
+ if (value == 2) {
+ // Frequency
+ writeOPL(0xA0, _tablePtr2[0]);
+ }
+ return 0;
+}
+
+// TODO: This is really the same as update_nop1(), so they should be combined
+// into one single update_nop().
+
+int AdlibDriver::update_nop2(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ return 0;
+}
+
+int AdlibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
+ int channelBackUp = _curChannel;
+ int regOffsetBackUp = _curRegOffset;
+
+ _curChannel = 6;
+ _curRegOffset = _regOffset[6];
+
+ setupInstrument(_curRegOffset, getInstrument(value), channel);
+ _unkValue6 = channel.opLevel2;
+
+ _curChannel = 7;
+ _curRegOffset = _regOffset[7];
+
+ setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel);
+ _unkValue7 = channel.opLevel1;
+ _unkValue8 = channel.opLevel2;
+
+ _curChannel = 8;
+ _curRegOffset = _regOffset[8];
+
+ setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel);
+ _unkValue9 = channel.opLevel1;
+ _unkValue10 = channel.opLevel2;
+
+ // Octave / F-Number / Key-On for channels 6, 7 and 8
+
+ _channels[6].regBx = *dataptr++ & 0x2F;
+ writeOPL(0xB6, _channels[6].regBx);
+ writeOPL(0xA6, *dataptr++);
+
+ _channels[7].regBx = *dataptr++ & 0x2F;
+ writeOPL(0xB7, _channels[7].regBx);
+ writeOPL(0xA7, *dataptr++);
+
+ _channels[8].regBx = *dataptr++ & 0x2F;
+ writeOPL(0xB8, _channels[8].regBx);
+ writeOPL(0xA8, *dataptr++);
+
+ _rhythmSectionBits = 0x20;
+
+ _curRegOffset = regOffsetBackUp;
+ _curChannel = channelBackUp;
+ return 0;
+}
+
+int AdlibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
+ // Any instrument that we want to play, and which was already playing,
+ // is temporarily keyed off. Instruments that were off already, or
+ // which we don't want to play, retain their old on/off status. This is
+ // probably so that the instrument's envelope is played from its
+ // beginning again...
+
+ writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20);
+
+ // ...but since we only set the rhythm instrument bits, and never clear
+ // them (until the entire rhythm section is disabled), I'm not sure how
+ // useful the cleverness above is. We could perhaps simply turn off all
+ // the rhythm instruments instead.
+
+ _rhythmSectionBits |= value;
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits);
+ return 0;
+}
+
+int AdlibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
+ --dataptr;
+ _rhythmSectionBits = 0;
+
+ // All the rhythm bits are cleared. The AM and Vibrato depth bits
+ // remain unchanged.
+
+ writeOPL(0xBD, _vibratoAndAMDepthBits);
+ return 0;
+}
+
+int AdlibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 value2 = *dataptr++;
+
+ if (value & 1) {
+ _unkValue12 = value2;
+
+ // Channel 7, op1: Level Key Scaling / Total Level
+ writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12));
+ }
+
+ if (value & 2) {
+ _unkValue14 = value2;
+
+ // Channel 8, op2: Level Key Scaling / Total Level
+ writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14));
+ }
+
+ if (value & 4) {
+ _unkValue15 = value2;
+
+ // Channel 8, op1: Level Key Scaling / Total Level
+ writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15));
+ }
+
+ if (value & 8) {
+ _unkValue18 = value2;
+
+ // Channel 7, op2: Level Key Scaling / Total Level
+ writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18));
+ }
+
+ if (value & 16) {
+ _unkValue20 = value2;
+
+ // Channel 6, op2: Level Key Scaling / Total Level
+ writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20));
+ }
+
+ return 0;
+}
+
+int AdlibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 value2 = *dataptr++;
+
+ if (value & 1) {
+ _unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12);
+
+ // Channel 7, op1: Level Key Scaling / Total Level
+ writeOPL(0x51, _unkValue11);
+ }
+
+ if (value & 2) {
+ _unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14);
+
+ // Channel 8, op2: Level Key Scaling / Total Level
+ writeOPL(0x55, _unkValue13);
+ }
+
+ if (value & 4) {
+ _unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15);
+
+ // Channel 8, op1: Level Key Scaling / Total Level
+ writeOPL(0x52, _unkValue16);
+ }
+
+ if (value & 8) {
+ _unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18);
+
+ // Channel 7, op2: Level Key Scaling / Total Level
+ writeOPL(0x54, _unkValue17);
+ }
+
+ if (value & 16) {
+ _unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20);
+
+ // Channel 6, op2: Level Key Scaling / Total Level
+ writeOPL(0x53, _unkValue19);
+ }
+
+ return 0;
+}
+
+int AdlibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) {
+ uint8 value2 = *dataptr++;
+
+ if (value & 1) {
+ _unkValue11 = value2;
+
+ // Channel 7, op1: Level Key Scaling / Total Level
+ writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12));
+ }
+
+ if (value & 2) {
+ _unkValue13 = value2;
+
+ // Channel 8, op2: Level Key Scaling / Total Level
+ writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14));
+ }
+
+ if (value & 4) {
+ _unkValue16 = value2;
+
+ // Channel 8, op1: Level Key Scaling / Total Level
+ writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15));
+ }
+
+ if (value & 8) {
+ _unkValue17 = value2;
+
+ // Channel 7, op2: Level Key Scaling / Total Level
+ writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18));
+ }
+
+ if (value & 16) {
+ _unkValue19 = value2;
+
+ // Channel 6, op2: Level Key Scaling / Total Level
+ writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20));
+ }
+
+ return 0;
+}
+
+int AdlibDriver::update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value) {
+ _soundTrigger = value;
+ return 0;
+}
+
+int AdlibDriver::update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.tempoReset = value;
+ return 0;
+}
+
+int AdlibDriver::updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value) {
+ channel.unk39 = value;
+ channel.unk40 = *dataptr++;
+ return 0;
+}
+
+// static res
+
+#define COMMAND(x) { &AdlibDriver::x, #x }
+
+void AdlibDriver::setupOpcodeList() {
+ static const OpcodeEntry opcodeList[] = {
+ COMMAND(snd_ret0x100),
+ COMMAND(snd_ret0x1983),
+ COMMAND(snd_initDriver),
+ COMMAND(snd_deinitDriver),
+ COMMAND(snd_setSoundData),
+ COMMAND(snd_unkOpcode1),
+ COMMAND(snd_startSong),
+ COMMAND(snd_unkOpcode2),
+ COMMAND(snd_unkOpcode3),
+ COMMAND(snd_readByte),
+ COMMAND(snd_writeByte),
+ COMMAND(snd_getSoundTrigger),
+ COMMAND(snd_unkOpcode4),
+ COMMAND(snd_dummy),
+ COMMAND(snd_getNullvar4),
+ COMMAND(snd_setNullvar3),
+ COMMAND(snd_setFlag),
+ COMMAND(snd_clearFlag)
+ };
+
+ _opcodeList = opcodeList;
+ _opcodesEntries = ARRAYSIZE(opcodeList);
+}
+
+void AdlibDriver::setupParserOpcodeTable() {
+ static const ParserOpcode parserOpcodeTable[] = {
+ // 0
+ COMMAND(update_setRepeat),
+ COMMAND(update_checkRepeat),
+ COMMAND(update_setupProgram),
+ COMMAND(update_setNoteSpacing),
+
+ // 4
+ COMMAND(update_jump),
+ COMMAND(update_jumpToSubroutine),
+ COMMAND(update_returnFromSubroutine),
+ COMMAND(update_setBaseOctave),
+
+ // 8
+ COMMAND(update_stopChannel),
+ COMMAND(update_playRest),
+ COMMAND(update_writeAdlib),
+ COMMAND(update_setupNoteAndDuration),
+
+ // 12
+ COMMAND(update_setBaseNote),
+ COMMAND(update_setupSecondaryEffect1),
+ COMMAND(update_stopOtherChannel),
+ COMMAND(update_waitForEndOfProgram),
+
+ // 16
+ COMMAND(update_setupInstrument),
+ COMMAND(update_setupPrimaryEffect1),
+ COMMAND(update_removePrimaryEffect1),
+ COMMAND(update_setBaseFreq),
+
+ // 20
+ COMMAND(update_stopChannel),
+ COMMAND(update_setupPrimaryEffect2),
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+
+ // 24
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+ COMMAND(update_setPriority),
+ COMMAND(update_stopChannel),
+
+ // 28
+ COMMAND(updateCallback23),
+ COMMAND(updateCallback24),
+ COMMAND(update_setExtraLevel1),
+ COMMAND(update_stopChannel),
+
+ // 32
+ COMMAND(update_setupDuration),
+ COMMAND(update_playNote),
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+
+ // 36
+ COMMAND(update_setFractionalNoteSpacing),
+ COMMAND(update_stopChannel),
+ COMMAND(update_setTempo),
+ COMMAND(update_removeSecondaryEffect1),
+
+ // 40
+ COMMAND(update_stopChannel),
+ COMMAND(update_setChannelTempo),
+ COMMAND(update_stopChannel),
+ COMMAND(update_setExtraLevel3),
+
+ // 44
+ COMMAND(update_setExtraLevel2),
+ COMMAND(update_changeExtraLevel2),
+ COMMAND(update_setAMDepth),
+ COMMAND(update_setVibratoDepth),
+
+ // 48
+ COMMAND(update_changeExtraLevel1),
+ COMMAND(update_stopChannel),
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback38),
+
+ // 52
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback39),
+ COMMAND(update_removePrimaryEffect2),
+ COMMAND(update_stopChannel),
+
+ // 56
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback41),
+ COMMAND(update_resetToGlobalTempo),
+ COMMAND(update_nop1),
+
+ // 60
+ COMMAND(update_setDurationRandomness),
+ COMMAND(update_changeChannelTempo),
+ COMMAND(update_stopChannel),
+ COMMAND(updateCallback46),
+
+ // 64
+ COMMAND(update_nop2),
+ COMMAND(update_setupRhythmSection),
+ COMMAND(update_playRhythmSection),
+ COMMAND(update_removeRhythmSection),
+
+ // 68
+ COMMAND(updateCallback51),
+ COMMAND(updateCallback52),
+ COMMAND(updateCallback53),
+ COMMAND(update_setSoundTrigger),
+
+ // 72
+ COMMAND(update_setTempoReset),
+ COMMAND(updateCallback56),
+ COMMAND(update_stopChannel)
+ };
+
+ _parserOpcodeTable = parserOpcodeTable;
+ _parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable);
+}
+#undef COMMAND
+
+// This table holds the register offset for operator 1 for each of the nine
+// channels. To get the register offset for operator 2, simply add 3.
+
+const uint8 AdlibDriver::_regOffset[] = {
+ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11,
+ 0x12
+};
+
+// Given the size of this table, and the range of its values, it's probably the
+// F-Numbers (10 bits) for the notes of the 12-tone scale. However, it does not
+// match the table in the Adlib documentation I've seen.
+
+const uint16 AdlibDriver::_unkTable[] = {
+ 0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9,
+ 0x0207, 0x0225, 0x0246
+};
+
+// These tables are currently only used by updateCallback46(), which only ever
+// uses the first element of one of the sub-tables.
+
+const uint8 *AdlibDriver::_unkTable2[] = {
+ AdlibDriver::_unkTable2_1,
+ AdlibDriver::_unkTable2_2,
+ AdlibDriver::_unkTable2_1,
+ AdlibDriver::_unkTable2_2,
+ AdlibDriver::_unkTable2_3,
+ AdlibDriver::_unkTable2_2
+};
+
+const uint8 AdlibDriver::_unkTable2_1[] = {
+ 0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D,
+ 0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49,
+ 0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45,
+ 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41,
+ 0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D,
+ 0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39,
+ 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35,
+ 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31,
+ 0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D,
+ 0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29,
+ 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25,
+ 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21,
+ 0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D,
+ 0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19,
+ 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15,
+ 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
+ 0x10, 0x10
+};
+
+// no don't ask me WHY this table exsits!
+const uint8 AdlibDriver::_unkTable2_2[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
+};
+
+const uint8 AdlibDriver::_unkTable2_3[] = {
+ 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E,
+ 0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B,
+ 0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39,
+ 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36,
+ 0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33,
+ 0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E,
+ 0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B,
+ 0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29,
+ 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26,
+ 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23,
+ 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21,
+ 0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E,
+ 0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B,
+ 0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19,
+ 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16,
+ 0x16, 0x15
+};
+
+// This table is used to modify the frequency of the notes, depending on the
+// note value and unk16. In theory, we could very well try to access memory
+// outside this table, but in reality that probably won't happen.
+//
+// This could be some sort of pitch bend, but I have yet to see it used for
+// anything so it's hard to say.
+
+const uint8 AdlibDriver::_unkTables[][32] = {
+ // 0
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19,
+ 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 },
+ // 1
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09,
+ 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A,
+ 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 },
+ // 2
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09,
+ 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 },
+ // 3
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 },
+ // 4
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A },
+ // 5
+ { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
+ 0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D },
+ // 6
+ { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
+ 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24,
+ 0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 },
+ // 7
+ { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C,
+ 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18,
+ 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25,
+ 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 },
+ // 8
+ { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A,
+ 0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28,
+ 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 },
+ // 9
+ { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
+ 0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B,
+ 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29,
+ 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 },
+ // 10
+ { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
+ 0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E,
+ 0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D,
+ 0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C },
+ // 11
+ { 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F,
+ 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E,
+ 0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E,
+ 0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F },
+ // 12
+ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10,
+ 0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21,
+ 0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32,
+ 0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 },
+ // 13
+ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11,
+ 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23,
+ 0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35,
+ 0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 }
+};
+
+// #pragma mark -
+
+// At the time of writing, the only known case where Kyra 1 uses sound triggers
+// is in the castle, to cycle between three different songs.
+
+const int CadlPlayer::_kyra1SoundTriggers[] = {
+ 0, 4, 5, 3
+};
+
+const int CadlPlayer::_kyra1NumSoundTriggers = ARRAYSIZE(CadlPlayer::_kyra1SoundTriggers);
+
+CadlPlayer::CadlPlayer(Copl *newopl)
+ : CPlayer(newopl), numsubsongs(0), _trackEntries(), _soundDataPtr(0)
+{
+ memset(_trackEntries, 0, sizeof(_trackEntries));
+ _driver = new AdlibDriver(newopl);
+ assert(_driver);
+
+ _sfxPlayingSound = -1;
+ // _soundFileLoaded = "";
+
+ _soundTriggers = _kyra1SoundTriggers;
+ _numSoundTriggers = _kyra1NumSoundTriggers;
+
+ init();
+}
+
+CadlPlayer::~CadlPlayer() {
+ delete [] _soundDataPtr;
+ delete _driver;
+}
+
+bool CadlPlayer::init() {
+ _driver->callback(2);
+ _driver->callback(16, int(4));
+ return true;
+}
+
+void CadlPlayer::process() {
+ uint8 trigger = _driver->callback(11);
+
+ if (trigger < _numSoundTriggers) {
+ int soundId = _soundTriggers[trigger];
+
+ if (soundId) {
+ playTrack(soundId);
+ }
+ } else {
+ warning("Unknown sound trigger %d", trigger);
+ // TODO: At this point, we really want to clear the trigger...
+ }
+}
+
+// void CadlPlayer::setVolume(int volume) {
+// }
+
+// int CadlPlayer::getVolume() {
+// return 0;
+// }
+
+// void CadlPlayer::loadMusicFile(const char *file) {
+// loadSoundFile(file);
+// }
+
+void CadlPlayer::playTrack(uint8 track) {
+ play(track);
+}
+
+// void CadlPlayer::haltTrack() {
+// unk1();
+// unk2();
+// //_engine->_system->delayMillis(3 * 60);
+// }
+
+void CadlPlayer::playSoundEffect(uint8_t track) {
+ play(track);
+}
+
+void CadlPlayer::play(uint8_t track) {
+ uint8 soundId = _trackEntries[track];
+ if ((int8)soundId == -1 || !_soundDataPtr)
+ return;
+ soundId &= 0xFF;
+ _driver->callback(16, 0);
+ // while ((_driver->callback(16, 0) & 8)) {
+ // We call the system delay and not the game delay to avoid concurrency issues.
+ // _engine->_system->delayMillis(10);
+ // }
+ if (_sfxPlayingSound != -1) {
+ // Restore the sounds's normal values.
+ _driver->callback(10, _sfxPlayingSound, int(1), int(_sfxPriority));
+ _driver->callback(10, _sfxPlayingSound, int(3), int(_sfxFourthByteOfSong));
+ _sfxPlayingSound = -1;
+ }
+
+ int chan = _driver->callback(9, soundId, int(0));
+
+ if (chan != 9) {
+ _sfxPlayingSound = soundId;
+ _sfxPriority = _driver->callback(9, soundId, int(1));
+ _sfxFourthByteOfSong = _driver->callback(9, soundId, int(3));
+
+ // In the cases I've seen, the mysterious fourth byte has been
+ // the parameter for the update_setExtraLevel3() callback.
+ //
+ // The extra level is part of the channels "total level", which
+ // is a six-bit value where larger values means softer volume.
+ //
+ // So what seems to be happening here is that sounds which are
+ // started by this function are given a slightly lower priority
+ // and a slightly higher (i.e. softer) extra level 3 than they
+ // would have if they were started from anywhere else. Strange.
+
+ int newVal = ((((-_sfxFourthByteOfSong) + 63) * 0xFF) >> 8) & 0xFF;
+ newVal = -newVal + 63;
+ _driver->callback(10, soundId, int(3), newVal);
+ newVal = ((_sfxPriority * 0xFF) >> 8) & 0xFF;
+ _driver->callback(10, soundId, int(1), newVal);
+ }
+
+ _driver->callback(6, soundId);
+}
+
+// void CadlPlayer::beginFadeOut() {
+// playSoundEffect(1);
+// }
+
+bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename);
+
+ // file validation section
+ if(!f || !fp.extension(filename, ".adl")) {
+ fp.close(f);
+ return false;
+ }
+
+ // if (_soundFileLoaded == file)
+ // return;
+
+ // if (_soundDataPtr) {
+ // haltTrack();
+ // }
+
+ uint8 *file_data = 0; uint32 file_size = 0;
+
+ // char filename[25];
+ // sprintf(filename, "%s.ADL", file);
+
+ // file_data = _engine->resource()->fileData(filename, &file_size);
+ // if (!file_data) {
+ // warning("Couldn't find music file: '%s'", filename);
+ // return;
+ // }
+
+ unk2();
+ unk1();
+
+ file_size = fp.filesize(f);
+ file_data = new uint8 [file_size];
+ f->readString((char *)file_data, file_size);
+
+ _driver->callback(8, int(-1));
+ _soundDataPtr = 0;
+
+ uint8 *p = file_data;
+ memcpy(_trackEntries, p, 120*sizeof(uint8));
+ p += 120;
+
+ int soundDataSize = file_size - 120;
+
+ _soundDataPtr = new uint8[soundDataSize];
+ assert(_soundDataPtr);
+
+ memcpy(_soundDataPtr, p, soundDataSize*sizeof(uint8));
+
+ delete [] file_data;
+ file_data = p = 0;
+ file_size = 0;
+
+ _driver->callback(4, _soundDataPtr);
+
+ // _soundFileLoaded = file;
+
+ for(int i = 0; i < 200; i++)
+ if(_trackEntries[i] != 0xff)
+ numsubsongs = i + 1;
+
+ fp.close(f);
+ return true;
+}
+
+void CadlPlayer::rewind(int subsong)
+{
+ opl->init();
+ opl->write(1,32);
+ playSoundEffect(subsong);
+ cursubsong = subsong;
+ update();
+}
+
+unsigned int CadlPlayer::getsubsongs()
+{
+ return numsubsongs;
+}
+
+bool CadlPlayer::update()
+{
+ bool songend = true;
+
+// if(_trackEntries[cursubsong] == 0xff)
+// return false;
+
+ _driver->callback();
+
+ for(int i = 0; i < 10; i++)
+ if(_driver->_channels[i].dataptr != NULL)
+ songend = false;
+
+ return !songend;
+}
+
+void CadlPlayer::unk1() {
+ playSoundEffect(0);
+ //_engine->_system->delayMillis(5 * 60);
+}
+
+void CadlPlayer::unk2() {
+ playSoundEffect(0);
+}
+
+CPlayer *CadlPlayer::factory(Copl *newopl)
+{
+ return new CadlPlayer(newopl);
+}
diff --git a/plugins/adplug/adplug/adl.h b/plugins/adplug/adplug/adl.h
new file mode 100644
index 00000000..b0030c43
--- /dev/null
+++ b/plugins/adplug/adplug/adl.h
@@ -0,0 +1,65 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ *
+ * adl.h - ADL player adaption by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_ADLPLAYER
+#define H_ADPLUG_ADLPLAYER
+
+#include <inttypes.h>
+
+#include "player.h"
+
+class AdlibDriver;
+
+class CadlPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CadlPlayer(Copl *newopl);
+ ~CadlPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+
+ // refresh rate is fixed at 72Hz
+ float getrefresh()
+ {
+ return 72.0f;
+ }
+
+ unsigned int getsubsongs();
+ std::string gettype() { return std::string("Westwood ADL"); }
+
+ private:
+ int numsubsongs, cursubsong;
+
+ AdlibDriver *_driver;
+
+ uint8_t _trackEntries[120];
+ uint8_t *_soundDataPtr;
+ int _sfxPlayingSound;
+
+ uint8_t _sfxPriority;
+ uint8_t _sfxFourthByteOfSong;
+
+ int _numSoundTriggers;
+ const int *_soundTriggers;
+
+ static const int _kyra1NumSoundTriggers;
+ static const int _kyra1SoundTriggers[];
+
+ bool init();
+ void process();
+ void playTrack(uint8_t track);
+ void playSoundEffect(uint8_t track);
+ void play(uint8_t track);
+ void unk1();
+ void unk2();
+};
+
+#endif
diff --git a/plugins/adplug/adplug/adlibemu.c b/plugins/adplug/adplug/adlibemu.c
new file mode 100644
index 00000000..c35d6da7
--- /dev/null
+++ b/plugins/adplug/adplug/adlibemu.c
@@ -0,0 +1,600 @@
+/*
+ * ADLIBEMU.C
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ *
+ * 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
+ */
+
+/*
+This file is a digital Adlib emulator for OPL2 and possibly OPL3
+
+Features that could be added in a future version:
+- Amplitude and Frequency Vibrato Bits (not hard, but a big speed hit)
+- Global Keyboard Split Number Bit (need to research this one some more)
+- 2nd Adlib chip for OPL3 (simply need to make my cell array bigger)
+- Advanced connection modes of OPL3 (Just need to add more "docell" cases)
+- L/R Stereo bits of OPL3 (Need adlibgetsample to return stereo)
+
+Features that aren't worth supporting:
+- Anything related to adlib timers&interrupts (Sorry - I always used IRQ0)
+- Composite sine wave mode (CSM) (Supported only on ancient cards)
+
+I'm not sure about a few things in my code:
+- Attack curve. What function is this anyway? I chose to use an order-3
+ polynomial to approximate but this doesn't seem right.
+- Attack/Decay/Release constants - my constants may not be exact
+- What should ADJUSTSPEED be?
+- Haven't verified that Global Keyboard Split Number Bit works yet
+- Some of the drums don't always sound right. It's pretty hard to guess
+ the exact waveform of drums when you look at random data which is
+ slightly randomized due to digital ADC recording.
+- Adlib seems to have a lot more treble than my emulator does. I'm not
+ sure if this is simply unfixable due to the sound blaster's different
+ filtering on FM and digital playback or if it's a serious bug in my
+ code.
+*/
+
+#include <math.h>
+#include <string.h>
+
+#if !defined(max) && !defined(__cplusplus)
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#if !defined(min) && !defined(__cplusplus)
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define PI 3.141592653589793
+#define MAXCELLS 18
+#define WAVPREC 2048
+
+static float AMPSCALE=(8192.0);
+#define FRQSCALE (49716/512.0)
+
+//Constants for Ken's Awe32, on a PII-266 (Ken says: Use these for KSM's!)
+#define MODFACTOR 4.0 //How much of modulator cell goes into carrier
+#define MFBFACTOR 1.0 //How much feedback goes back into modulator
+#define ADJUSTSPEED 0.75 //0<=x<=1 Simulate finite rate of change of state
+
+//Constants for Ken's Awe64G, on a P-133
+//#define MODFACTOR 4.25 //How much of modulator cell goes into carrier
+//#define MFBFACTOR 0.5 //How much feedback goes back into modulator
+//#define ADJUSTSPEED 0.85 //0<=x<=1 Simulate finite rate of change of state
+
+typedef struct
+{
+ float val, t, tinc, vol, sustain, amp, mfb;
+ float a0, a1, a2, a3, decaymul, releasemul;
+ short *waveform;
+ long wavemask;
+ void (*cellfunc)(void *, float);
+ unsigned char flags, dum0, dum1, dum2;
+} celltype;
+
+static long numspeakers, bytespersample;
+static float recipsamp;
+static celltype cell[MAXCELLS];
+static signed short wavtable[WAVPREC*3];
+static float kslmul[4] = {0.0,0.5,0.25,1.0};
+static float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}, nfrqmul[16];
+static unsigned char adlibreg[256], ksl[8][16];
+static unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18};
+static unsigned char odrumstat = 0;
+static unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8};
+
+float lvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on left speaker
+float rvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on right speaker
+long lplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on left speaker
+long rplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on right speaker
+
+long nlvol[9], nrvol[9];
+long nlplc[9], nrplc[9];
+long rend = 0;
+#define FIFOSIZ 256
+static float *rptr[9], *nrptr[9];
+static float rbuf[9][FIFOSIZ*2];
+static float snd[FIFOSIZ*2];
+
+#ifndef USING_ASM
+#define _inline
+#endif
+
+#ifdef USING_ASM
+static _inline void ftol (float f, long *a)
+{
+ _asm
+ {
+ mov eax, a
+ fld f
+ fistp dword ptr [eax]
+ }
+}
+#else
+static void ftol(float f, long *a) {
+ *a=f;
+}
+#endif
+
+#define ctc ((celltype *)c) //A rare attempt to make code easier to read!
+void docell4 (void *c, float modulator) { }
+void docell3 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+void docell2 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+
+ if (*(long *)&ctc->amp <= 0x37800000)
+ {
+ ctc->amp = 0;
+ ctc->cellfunc = docell4;
+ }
+ ctc->amp *= ctc->releasemul;
+
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+void docell1 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+
+ if ((*(long *)&ctc->amp) <= (*(long *)&ctc->sustain))
+ {
+ if (ctc->flags&32)
+ {
+ ctc->amp = ctc->sustain;
+ ctc->cellfunc = docell3;
+ }
+ else
+ ctc->cellfunc = docell2;
+ }
+ else
+ ctc->amp *= ctc->decaymul;
+
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+void docell0 (void *c, float modulator)
+{
+ long i;
+
+ ftol(ctc->t+modulator,&i);
+
+ ctc->amp = ((ctc->a3*ctc->amp + ctc->a2)*ctc->amp + ctc->a1)*ctc->amp + ctc->a0;
+ if ((*(long *)&ctc->amp) > 0x3f800000)
+ {
+ ctc->amp = 1;
+ ctc->cellfunc = docell1;
+ }
+
+ ctc->t += ctc->tinc;
+ ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
+}
+
+
+static long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1};
+static long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1};
+static long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3};
+static float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744};
+static float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608};
+void cellon (long i, long j, celltype *c, unsigned char iscarrier)
+{
+ long frn, oct, toff;
+ float f;
+
+ frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
+ oct = ((((long)adlibreg[i+0xb0])>>2)&7);
+ toff = (oct<<1) + ((frn>>9)&((frn>>8)|(((adlibreg[8]>>6)&1)^1)));
+ if (!(adlibreg[j+0x20]&16)) toff >>= 2;
+
+ f = pow(2.0,(adlibreg[j+0x60]>>4)+(toff>>2)-1)*attackconst[toff&3]*recipsamp;
+ c->a0 = .0377*f; c->a1 = 10.73*f+1; c->a2 = -17.57*f; c->a3 = 7.42*f;
+ f = -7.4493*decrelconst[toff&3]*recipsamp;
+ c->decaymul = pow(2.0,f*pow(2.0,(adlibreg[j+0x60]&15)+(toff>>2)));
+ c->releasemul = pow(2.0,f*pow(2.0,(adlibreg[j+0x80]&15)+(toff>>2)));
+ c->wavemask = wavemask[adlibreg[j+0xe0]&7];
+ c->waveform = &wavtable[waveform[adlibreg[j+0xe0]&7]];
+ if (!(adlibreg[1]&0x20)) c->waveform = &wavtable[WAVPREC];
+ c->t = wavestart[adlibreg[j+0xe0]&7];
+ c->flags = adlibreg[j+0x20];
+ c->cellfunc = docell0;
+ c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
+ c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
+ (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
+ c->sustain = pow(2.0,(float)(adlibreg[j+0x80]>>4) * -.5);
+ if (!iscarrier) c->amp = 0;
+ c->mfb = pow(2.0,((adlibreg[i+0xc0]>>1)&7)+5)*(WAVPREC/2048.0)*MFBFACTOR;
+ if (!(adlibreg[i+0xc0]&14)) c->mfb = 0;
+ c->val = 0;
+}
+
+//This function (and bug fix) written by Chris Moeller
+void cellfreq (signed long i, signed long j, celltype *c)
+{
+ long frn, oct;
+
+ frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
+ oct = ((((long)adlibreg[i+0xb0])>>2)&7);
+
+ c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
+ c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
+ (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
+}
+
+static long initfirstime = 0;
+void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample)
+{
+ long i, j, frn, oct;
+
+ memset((void *)adlibreg,0,sizeof(adlibreg));
+ memset((void *)cell,0,sizeof(celltype)*MAXCELLS);
+ memset((void *)rbuf,0,sizeof(rbuf));
+ rend = 0; odrumstat = 0;
+
+ for(i=0;i<MAXCELLS;i++)
+ {
+ cell[i].cellfunc = docell4;
+ cell[i].amp = 0;
+ cell[i].vol = 0;
+ cell[i].t = 0;
+ cell[i].tinc = 0;
+ cell[i].wavemask = 0;
+ cell[i].waveform = &wavtable[WAVPREC];
+ }
+
+ numspeakers = danumspeakers;
+ bytespersample = dabytespersample;
+
+ recipsamp = 1.0 / (float)dasamplerate;
+ for(i=15;i>=0;i--) nfrqmul[i] = frqmul[i]*recipsamp*FRQSCALE*(WAVPREC/2048.0);
+
+ if (!initfirstime)
+ {
+ initfirstime = 1;
+
+ for(i=0;i<(WAVPREC>>1);i++)
+ {
+ wavtable[i] =
+ wavtable[(i<<1) +WAVPREC] = (signed short)(16384*sin((float)((i<<1) )*PI*2/WAVPREC));
+ wavtable[(i<<1)+1+WAVPREC] = (signed short)(16384*sin((float)((i<<1)+1)*PI*2/WAVPREC));
+ }
+ for(i=0;i<(WAVPREC>>3);i++)
+ {
+ wavtable[i+(WAVPREC<<1)] = wavtable[i+(WAVPREC>>3)]-16384;
+ wavtable[i+((WAVPREC*17)>>3)] = wavtable[i+(WAVPREC>>2)]+16384;
+ }
+
+ //[table in book]*8/3
+ ksl[7][0] = 0; ksl[7][1] = 24; ksl[7][2] = 32; ksl[7][3] = 37;
+ ksl[7][4] = 40; ksl[7][5] = 43; ksl[7][6] = 45; ksl[7][7] = 47;
+ ksl[7][8] = 48; for(i=9;i<16;i++) ksl[7][i] = i+41;
+ for(j=6;j>=0;j--)
+ for(i=0;i<16;i++)
+ {
+ oct = (long)ksl[j+1][i]-8; if (oct < 0) oct = 0;
+ ksl[j][i] = (unsigned char)oct;
+ }
+ }
+ else
+ {
+ for(i=0;i<9;i++)
+ {
+ frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
+ oct = ((((long)adlibreg[i+0xb0])>>2)&7);
+ cell[i].tinc = (float)(frn<<oct)*nfrqmul[adlibreg[modulatorbase[i]+0x20]&15];
+ }
+ }
+}
+
+void adlib0 (long i, long v)
+{
+ unsigned char tmp = adlibreg[i];
+ adlibreg[i] = v;
+
+ if (i == 0xbd)
+ {
+ if ((v&16) > (odrumstat&16)) //BassDrum
+ {
+ cellon(6,16,&cell[6],0);
+ cellon(6,19,&cell[15],1);
+ cell[15].vol *= 2;
+ }
+ if ((v&8) > (odrumstat&8)) //Snare
+ {
+ cellon(16,20,&cell[16],0);
+ cell[16].tinc *= 2*(nfrqmul[adlibreg[17+0x20]&15] / nfrqmul[adlibreg[20+0x20]&15]);
+ if (((adlibreg[20+0xe0]&7) >= 3) && ((adlibreg[20+0xe0]&7) <= 5)) cell[16].vol = 0;
+ cell[16].vol *= 2;
+ }
+ if ((v&4) > (odrumstat&4)) //TomTom
+ {
+ cellon(8,18,&cell[8],0);
+ cell[8].vol *= 2;
+ }
+ if ((v&2) > (odrumstat&2)) //Cymbal
+ {
+ cellon(17,21,&cell[17],0);
+
+ cell[17].wavemask = wavemask[5];
+ cell[17].waveform = &wavtable[waveform[5]];
+ cell[17].tinc *= 16; cell[17].vol *= 2;
+
+ //cell[17].waveform = &wavtable[WAVPREC]; cell[17].wavemask = 0;
+ //if (((adlibreg[21+0xe0]&7) == 0) || ((adlibreg[21+0xe0]&7) == 6))
+ // cell[17].waveform = &wavtable[(WAVPREC*7)>>2];
+ //if (((adlibreg[21+0xe0]&7) == 2) || ((adlibreg[21+0xe0]&7) == 3))
+ // cell[17].waveform = &wavtable[(WAVPREC*5)>>2];
+ }
+ if ((v&1) > (odrumstat&1)) //Hihat
+ {
+ cellon(7,17,&cell[7],0);
+ if (((adlibreg[17+0xe0]&7) == 1) || ((adlibreg[17+0xe0]&7) == 4) ||
+ ((adlibreg[17+0xe0]&7) == 5) || ((adlibreg[17+0xe0]&7) == 7)) cell[7].vol = 0;
+ if ((adlibreg[17+0xe0]&7) == 6) { cell[7].wavemask = 0; cell[7].waveform = &wavtable[(WAVPREC*7)>>2]; }
+ }
+
+ odrumstat = v;
+ }
+ else if (((unsigned)(i-0x40) < (unsigned)22) && ((i&7) < 6))
+ {
+ if ((i&7) < 3) // Modulator
+ cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]]);
+ else // Carrier
+ cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]+9]);
+ }
+ else if ((unsigned)(i-0xa0) < (unsigned)9)
+ {
+ cellfreq(i-0xa0,modulatorbase[i-0xa0],&cell[i-0xa0]);
+ cellfreq(i-0xa0,modulatorbase[i-0xa0]+3,&cell[i-0xa0+9]);
+ }
+ else if ((unsigned)(i-0xb0) < (unsigned)9)
+ {
+ if ((v&32) > (tmp&32))
+ {
+ cellon(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0],0);
+ cellon(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9],1);
+ }
+ else if ((v&32) < (tmp&32))
+ cell[i-0xb0].cellfunc = cell[i-0xb0+9].cellfunc = docell2;
+ cellfreq(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0]);
+ cellfreq(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9]);
+ }
+
+ //outdata(i,v);
+}
+
+#ifdef USING_ASM
+static long fpuasm;
+static float fakeadd = 8388608.0+128.0;
+static _inline void clipit8 (float f, long a)
+{
+ _asm
+ {
+ mov edi, a
+ fld dword ptr f
+ fadd dword ptr fakeadd
+ fstp dword ptr fpuasm
+ mov eax, fpuasm
+ test eax, 0x007fff00
+ jz short skipit
+ shr eax, 16
+ xor eax, -1
+ skipit: mov byte ptr [edi], al
+ }
+}
+
+static _inline void clipit16 (float f, long a)
+{
+ _asm
+ {
+ mov eax, a
+ fld dword ptr f
+ fist word ptr [eax]
+ cmp word ptr [eax], 0x8000
+ jne short skipit2
+ fst dword ptr [fpuasm]
+ cmp fpuasm, 0x80000000
+ sbb word ptr [eax], 0
+ skipit2: fstp st
+ }
+}
+#else
+static void clipit8(float f,unsigned char *a) {
+ f/=256.0;
+ f+=128.0;
+ if (f>254.5) *a=255;
+ else if (f<0.5) *a=0;
+ else *a=f;
+}
+
+static void clipit16(float f,short *a) {
+ if (f>32766.5) *a=32767;
+ else if (f<-32767.5) *a=-32768;
+ else *a=f;
+}
+#endif
+
+void adlibsetvolume(int i) {
+ AMPSCALE=i;
+}
+
+void adlibgetsample (unsigned char *sndptr, long numbytes)
+{
+ long i, j, k=0, ns, endsamples, rptrs, numsamples;
+ celltype *cptr;
+ float f;
+ short *sndptr2=(short *)sndptr;
+
+ numsamples = (numbytes>>(numspeakers+bytespersample-2));
+
+ if (bytespersample == 1) f = AMPSCALE/256.0; else f = AMPSCALE;
+ if (numspeakers == 1)
+ {
+ nlvol[0] = lvol[0]*f;
+ for(i=0;i<9;i++) rptr[i] = &rbuf[0][0];
+ rptrs = 1;
+ }
+ else
+ {
+ rptrs = 0;
+ for(i=0;i<9;i++)
+ {
+ if ((!i) || (lvol[i] != lvol[i-1]) || (rvol[i] != rvol[i-1]) ||
+ (lplc[i] != lplc[i-1]) || (rplc[i] != rplc[i-1]))
+ {
+ nlvol[rptrs] = lvol[i]*f;
+ nrvol[rptrs] = rvol[i]*f;
+ nlplc[rptrs] = rend-min(max(lplc[i],0),FIFOSIZ);
+ nrplc[rptrs] = rend-min(max(rplc[i],0),FIFOSIZ);
+ rptrs++;
+ }
+ rptr[i] = &rbuf[rptrs-1][0];
+ }
+ }
+
+
+ //CPU time used to be somewhat less when emulator was only mono!
+ // Because of no delay fifos!
+
+ for(ns=0;ns<numsamples;ns+=endsamples)
+ {
+ endsamples = min(FIFOSIZ*2-rend,FIFOSIZ);
+ endsamples = min(endsamples,numsamples-ns);
+
+ for(i=0;i<9;i++)
+ nrptr[i] = &rptr[i][rend];
+ for(i=0;i<rptrs;i++)
+ memset((void *)&rbuf[i][rend],0,endsamples*sizeof(float));
+
+ if (adlibreg[0xbd]&0x20)
+ {
+ //BassDrum (j=6)
+ if (cell[15].cellfunc != docell4)
+ {
+ if (adlibreg[0xc6]&1)
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ (cell[15].cellfunc)((void *)&cell[15],0.0);
+ nrptr[6][i] += cell[15].val;
+ }
+ }
+ else
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ (cell[6].cellfunc)((void *)&cell[6],cell[6].val*cell[6].mfb);
+ (cell[15].cellfunc)((void *)&cell[15],cell[6].val*WAVPREC*MODFACTOR);
+ nrptr[6][i] += cell[15].val;
+ }
+ }
+ }
+
+ //Snare/Hihat (j=7), Cymbal/TomTom (j=8)
+ if ((cell[7].cellfunc != docell4) || (cell[8].cellfunc != docell4) || (cell[16].cellfunc != docell4) || (cell[17].cellfunc != docell4))
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ k = k*1664525+1013904223;
+ (cell[16].cellfunc)((void *)&cell[16],k&((WAVPREC>>1)-1)); //Snare
+ (cell[7].cellfunc)((void *)&cell[7],k&(WAVPREC-1)); //Hihat
+ (cell[17].cellfunc)((void *)&cell[17],k&((WAVPREC>>3)-1)); //Cymbal
+ (cell[8].cellfunc)((void *)&cell[8],0.0); //TomTom
+ nrptr[7][i] += cell[7].val + cell[16].val;
+ nrptr[8][i] += cell[8].val + cell[17].val;
+ }
+ }
+ }
+ for(j=9-1;j>=0;j--)
+ {
+ if ((adlibreg[0xbd]&0x20) && (j >= 6) && (j < 9)) continue;
+
+ cptr = &cell[j]; k = j;
+ if (adlibreg[0xc0+k]&1)
+ {
+ if ((cptr[9].cellfunc == docell4) && (cptr->cellfunc == docell4)) continue;
+ for(i=0;i<endsamples;i++)
+ {
+ (cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
+ (cptr->cellfunc)((void *)&cptr[9],0);
+ nrptr[j][i] += cptr[9].val + cptr->val;
+ }
+ }
+ else
+ {
+ if (cptr[9].cellfunc == docell4) continue;
+ for(i=0;i<endsamples;i++)
+ {
+ (cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
+ (cptr[9].cellfunc)((void *)&cptr[9],cptr->val*WAVPREC*MODFACTOR);
+ nrptr[j][i] += cptr[9].val;
+ }
+ }
+ }
+
+ if (numspeakers == 1)
+ {
+ if (bytespersample == 1)
+ {
+ for(i=endsamples-1;i>=0;i--)
+ clipit8(nrptr[0][i]*nlvol[0],sndptr+1);
+ }
+ else
+ {
+ for(i=endsamples-1;i>=0;i--)
+ clipit16(nrptr[0][i]*nlvol[0],sndptr2+i);
+ }
+ }
+ else
+ {
+ memset((void *)snd,0,endsamples*sizeof(float)*2);
+ for(j=0;j<rptrs;j++)
+ {
+ for(i=0;i<endsamples;i++)
+ {
+ snd[(i<<1) ] += rbuf[j][(nlplc[j]+i)&(FIFOSIZ*2-1)]*nlvol[j];
+ snd[(i<<1)+1] += rbuf[j][(nrplc[j]+i)&(FIFOSIZ*2-1)]*nrvol[j];
+ }
+ nlplc[j] += endsamples;
+ nrplc[j] += endsamples;
+ }
+
+ if (bytespersample == 1)
+ {
+ for(i=(endsamples<<1)-1;i>=0;i--)
+ clipit8(snd[i],sndptr+i);
+ }
+ else
+ {
+ for(i=(endsamples<<1)-1;i>=0;i--)
+ clipit16(snd[i],sndptr2+i);
+ }
+ }
+
+ sndptr = sndptr+(numspeakers*endsamples);
+ sndptr2 = sndptr2+(numspeakers*endsamples);
+ rend = ((rend+endsamples)&(FIFOSIZ*2-1));
+ }
+}
diff --git a/plugins/adplug/adplug/adlibemu.h b/plugins/adplug/adplug/adlibemu.h
new file mode 100644
index 00000000..8600d787
--- /dev/null
+++ b/plugins/adplug/adplug/adlibemu.h
@@ -0,0 +1,26 @@
+/*
+ * ADLIBEMU.H
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ *
+ * 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
+ */
+
+void adlibinit(long dasamplerate,long danumspeakers,long dabytespersample);
+void adlib0(long i,long v);
+void adlibgetsample(void *sndptr,long numbytes);
+void adlibsetvolume(int i);
+void randoinsts();
+extern float lvol[9],rvol[9],lplc[9],rplc[9];
diff --git a/plugins/adplug/adplug/adplug.cpp b/plugins/adplug/adplug/adplug.cpp
new file mode 100644
index 00000000..32fea695
--- /dev/null
+++ b/plugins/adplug/adplug/adplug.cpp
@@ -0,0 +1,182 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * adplug.cpp - CAdPlug utility class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string>
+#include <binfile.h>
+#include <string.h>
+
+#include "adplug.h"
+#include "debug.h"
+
+/***** Replayer includes *****/
+
+#include "hsc.h"
+#include "amd.h"
+#include "a2m.h"
+#include "imf.h"
+#include "sng.h"
+#include "adtrack.h"
+#include "bam.h"
+#include "d00.h"
+#include "dfm.h"
+#include "hsp.h"
+#include "ksm.h"
+#include "mad.h"
+#include "mid.h"
+#include "mkj.h"
+#include "cff.h"
+#include "dmo.h"
+#include "s3m.h"
+#include "dtm.h"
+#include "fmc.h"
+#include "mtk.h"
+#include "rad.h"
+#include "raw.h"
+#include "sa2.h"
+#include "bmf.h"
+#include "flash.h"
+#include "hybrid.h"
+#include "hyp.h"
+#include "psi.h"
+#include "rat.h"
+#include "lds.h"
+#include "u6m.h"
+#include "rol.h"
+#include "xsm.h"
+#include "dro.h"
+#include "msc.h"
+#include "rix.h"
+#include "adl.h"
+
+/***** CAdPlug *****/
+
+// List of all players that come with the standard AdPlug distribution
+const CPlayerDesc CAdPlug::allplayers[] = {
+ CPlayerDesc(ChscPlayer::factory, "HSC-Tracker", ".hsc\0"),
+ CPlayerDesc(CsngPlayer::factory, "SNGPlay", ".sng\0"),
+ CPlayerDesc(CimfPlayer::factory, "Apogee IMF", ".imf\0.wlf\0.adlib\0"),
+ CPlayerDesc(Ca2mLoader::factory, "Adlib Tracker 2", ".a2m\0"),
+ CPlayerDesc(CadtrackLoader::factory, "Adlib Tracker", ".sng\0"),
+ CPlayerDesc(CamdLoader::factory, "AMUSIC", ".amd\0"),
+ CPlayerDesc(CbamPlayer::factory, "Bob's Adlib Music", ".bam\0"),
+ CPlayerDesc(Cd00Player::factory, "Packed EdLib", ".d00\0"),
+ CPlayerDesc(CdfmLoader::factory, "Digital-FM", ".dfm\0"),
+ CPlayerDesc(ChspLoader::factory, "HSC Packed", ".hsp\0"),
+ CPlayerDesc(CksmPlayer::factory, "Ken Silverman Music", ".ksm\0"),
+ CPlayerDesc(CmadLoader::factory, "Mlat Adlib Tracker", ".mad\0"),
+ CPlayerDesc(CmidPlayer::factory, "MIDI", ".mid\0.cmf\0.sci\0.laa\0"),
+ CPlayerDesc(CmkjPlayer::factory, "MKJamz", ".mkj\0"),
+ CPlayerDesc(CcffLoader::factory, "Boomtracker", ".cff\0"),
+ CPlayerDesc(CdmoLoader::factory, "TwinTeam", ".dmo\0"),
+ CPlayerDesc(Cs3mPlayer::factory, "Scream Tracker 3", ".s3m\0"),
+ CPlayerDesc(CdtmLoader::factory, "DeFy Adlib Tracker", ".dtm\0"),
+ CPlayerDesc(CfmcLoader::factory, "Faust Music Creator", ".sng\0"),
+ CPlayerDesc(CmtkLoader::factory, "MPU-401 Trakker", ".mtk\0"),
+ CPlayerDesc(CradLoader::factory, "Reality Adlib Tracker", ".rad\0"),
+ CPlayerDesc(CrawPlayer::factory, "RdosPlay RAW", ".raw\0"),
+ CPlayerDesc(Csa2Loader::factory, "Surprise! Adlib Tracker", ".sat\0.sa2\0"),
+ CPlayerDesc(CxadbmfPlayer::factory, "BMF Adlib Tracker", ".xad\0"),
+ CPlayerDesc(CxadflashPlayer::factory, "Flash", ".xad\0"),
+ CPlayerDesc(CxadhybridPlayer::factory, "Hybrid", ".xad\0"),
+ CPlayerDesc(CxadhypPlayer::factory, "Hypnosis", ".xad\0"),
+ CPlayerDesc(CxadpsiPlayer::factory, "PSI", ".xad\0"),
+ CPlayerDesc(CxadratPlayer::factory, "rat", ".xad\0"),
+ CPlayerDesc(CldsPlayer::factory, "LOUDNESS Sound System", ".lds\0"),
+ CPlayerDesc(Cu6mPlayer::factory, "Ultima 6 Music", ".m\0"),
+ CPlayerDesc(CrolPlayer::factory, "Adlib Visual Composer", ".rol\0"),
+ CPlayerDesc(CxsmPlayer::factory, "eXtra Simple Music", ".xsm\0"),
+ CPlayerDesc(CdroPlayer::factory, "DOSBox Raw OPL", ".dro\0"),
+ CPlayerDesc(CmscPlayer::factory, "Adlib MSC Player", ".msc\0"),
+ CPlayerDesc(CrixPlayer::factory, "Softstar RIX OPL Music", ".rix\0"),
+ CPlayerDesc(CadlPlayer::factory, "Westwood ADL", ".adl\0"),
+ CPlayerDesc()
+};
+
+const CPlayers &CAdPlug::init_players(const CPlayerDesc pd[])
+{
+ static CPlayers initplayers;
+ unsigned int i;
+
+ for(i = 0; pd[i].factory; i++)
+ initplayers.push_back(&pd[i]);
+
+ return initplayers;
+}
+
+const CPlayers CAdPlug::players = CAdPlug::init_players(CAdPlug::allplayers);
+CAdPlugDatabase *CAdPlug::database = 0;
+
+CPlayer *CAdPlug::factory(const char *fn, Copl *opl, const CPlayers &pl,
+ const CFileProvider &fp)
+{
+ CPlayer *p;
+ CPlayers::const_iterator i;
+ unsigned int j;
+
+ AdPlug_LogWrite("*** CAdPlug::factory(\"%s\",opl,fp) ***\n", fn);
+
+ // Try a direct hit by file extension
+ for(i = pl.begin(); i != pl.end(); i++)
+ for(j = 0; (*i)->get_extension(j); j++)
+ if(fp.extension(fn, (*i)->get_extension(j))) {
+ AdPlug_LogWrite("Trying direct hit: %s\n", (*i)->filetype.c_str());
+ if((p = (*i)->factory(opl)))
+ if(p->load(fn, fp)) {
+ AdPlug_LogWrite("got it!\n");
+ AdPlug_LogWrite("--- CAdPlug::factory ---\n");
+ return p;
+ } else
+ delete p;
+ }
+
+ // Try all players, one by one
+ for(i = pl.begin(); i != pl.end(); i++) {
+ AdPlug_LogWrite("Trying: %s\n", (*i)->filetype.c_str());
+ if((p = (*i)->factory(opl)))
+ if(p->load(fn, fp)) {
+ AdPlug_LogWrite("got it!\n");
+ AdPlug_LogWrite("--- CAdPlug::factory ---\n");
+ return p;
+ } else
+ delete p;
+ }
+
+ // Unknown file
+ AdPlug_LogWrite("End of list!\n");
+ AdPlug_LogWrite("--- CAdPlug::factory ---\n");
+ return 0;
+}
+
+void CAdPlug::set_database(CAdPlugDatabase *db)
+{
+ database = db;
+}
+
+std::string CAdPlug::get_version()
+{
+ return std::string(VERSION);
+}
+
+void CAdPlug::debug_output(const std::string &filename)
+{
+ AdPlug_LogFile(filename.c_str());
+ AdPlug_LogWrite("CAdPlug::debug_output(\"%s\"): Redirected.\n",filename.c_str());
+}
diff --git a/plugins/adplug/adplug/adplug.h b/plugins/adplug/adplug/adplug.h
new file mode 100644
index 00000000..8b537a5f
--- /dev/null
+++ b/plugins/adplug/adplug/adplug.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ *
+ * adplug.h - AdPlug main header file, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_ADPLUG
+#define H_ADPLUG_ADPLUG
+
+#include <string>
+
+#include "player.h"
+#include "opl.h"
+#include "fprovide.h"
+#include "players.h"
+#include "database.h"
+#include "../../deadbeef.h"
+
+class CAdPlug
+{
+ friend CPlayer::CPlayer(Copl *newopl);
+
+public:
+ static const CPlayers players;
+
+ static CPlayer *factory(const char *fn, Copl *opl,
+ const CPlayers &pl = players,
+ const CFileProvider &fp = CProvider_Filesystem());
+
+ static void set_database(CAdPlugDatabase *db);
+ static std::string get_version();
+ static void debug_output(const std::string &filename);
+
+private:
+ static CAdPlugDatabase *database;
+ static const CPlayerDesc allplayers[];
+
+ static const CPlayers &init_players(const CPlayerDesc pd[]);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/adtrack.cpp b/plugins/adplug/adplug/adtrack.cpp
new file mode 100644
index 00000000..d3f3490a
--- /dev/null
+++ b/plugins/adplug/adplug/adtrack.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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
+ *
+ * adtrack.cpp - Adlib Tracker 1.0 Loader by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * The original Adlib Tracker 1.0 is behaving a little different from the
+ * official spec: The 'octave' integer from the instrument file is stored
+ * "minus 1" from the actual value, underflowing from 0 to 0xffff.
+ *
+ * I also noticed that my player is playing everything transposed a few tones
+ * higher than the original tracker. As far as i can see, my player perfectly
+ * follows the official spec, so it "must" be the tracker that does something
+ * wrong here...
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "adtrack.h"
+#include "debug.h"
+
+/*** Public methods ***/
+
+CPlayer *CadtrackLoader::factory(Copl *newopl)
+{
+ return new CadtrackLoader(newopl);
+}
+
+bool CadtrackLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ binistream *instf;
+ char note[2];
+ unsigned short rwp;
+ unsigned char chp, octave, pnote = 0;
+ int i,j;
+ AdTrackInst myinst;
+
+ // file validation
+ if(!fp.extension(filename, ".sng") || fp.filesize(f) != 36000)
+ { fp.close(f); return false; }
+
+ // check for instruments file
+ std::string instfilename(filename, 0, filename.find_last_of('.'));
+ instfilename += ".ins";
+ AdPlug_LogWrite("CadtrackLoader::load(,\"%s\"): Checking for \"%s\"...\n",
+ filename.c_str(), instfilename.c_str());
+ instf = fp.open(instfilename);
+ if(!instf || fp.filesize(instf) != 468) { fp.close(f); return false; }
+
+ // give CmodPlayer a hint on what we're up to
+ realloc_patterns(1,1000,9); realloc_instruments(9); realloc_order(1);
+ init_trackord(); flags = NoKeyOn;
+ (*order) = 0; length = 1; restartpos = 0; bpm = 120; initspeed = 3;
+
+ // load instruments from instruments file
+ for(i=0;i<9;i++) {
+ for(j=0;j<2;j++) {
+ myinst.op[j].appampmod = instf->readInt(2);
+ myinst.op[j].appvib = instf->readInt(2);
+ myinst.op[j].maintsuslvl = instf->readInt(2);
+ myinst.op[j].keybscale = instf->readInt(2);
+ myinst.op[j].octave = instf->readInt(2);
+ myinst.op[j].freqrisevollvldn = instf->readInt(2);
+ myinst.op[j].softness = instf->readInt(2);
+ myinst.op[j].attack = instf->readInt(2);
+ myinst.op[j].decay = instf->readInt(2);
+ myinst.op[j].release = instf->readInt(2);
+ myinst.op[j].sustain = instf->readInt(2);
+ myinst.op[j].feedback = instf->readInt(2);
+ myinst.op[j].waveform = instf->readInt(2);
+ }
+ convert_instrument(i, &myinst);
+ }
+ fp.close(instf);
+
+ // load file
+ for(rwp=0;rwp<1000;rwp++)
+ for(chp=0;chp<9;chp++) {
+ // read next record
+ f->readString(note, 2); octave = f->readInt(1); f->ignore();
+ switch(*note) {
+ case 'C': if(note[1] == '#') pnote = 2; else pnote = 1; break;
+ case 'D': if(note[1] == '#') pnote = 4; else pnote = 3; break;
+ case 'E': pnote = 5; break;
+ case 'F': if(note[1] == '#') pnote = 7; else pnote = 6; break;
+ case 'G': if(note[1] == '#') pnote = 9; else pnote = 8; break;
+ case 'A': if(note[1] == '#') pnote = 11; else pnote = 10; break;
+ case 'B': pnote = 12; break;
+ case '\0':
+ if(note[1] == '\0')
+ tracks[chp][rwp].note = 127;
+ else {
+ fp.close(f);
+ return false;
+ }
+ break;
+ default: fp.close(f); return false;
+ }
+ if((*note) != '\0') {
+ tracks[chp][rwp].note = pnote + (octave * 12);
+ tracks[chp][rwp].inst = chp + 1;
+ }
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+float CadtrackLoader::getrefresh()
+{
+ return 18.2f;
+}
+
+/*** Private methods ***/
+
+void CadtrackLoader::convert_instrument(unsigned int n, AdTrackInst *i)
+{
+ // Carrier "Amp Mod / Vib / Env Type / KSR / Multiple" register
+ inst[n].data[2] = i->op[Carrier].appampmod ? 1 << 7 : 0;
+ inst[n].data[2] += i->op[Carrier].appvib ? 1 << 6 : 0;
+ inst[n].data[2] += i->op[Carrier].maintsuslvl ? 1 << 5 : 0;
+ inst[n].data[2] += i->op[Carrier].keybscale ? 1 << 4 : 0;
+ inst[n].data[2] += (i->op[Carrier].octave + 1) & 0xffff; // Bug in original tracker
+ // Modulator...
+ inst[n].data[1] = i->op[Modulator].appampmod ? 1 << 7 : 0;
+ inst[n].data[1] += i->op[Modulator].appvib ? 1 << 6 : 0;
+ inst[n].data[1] += i->op[Modulator].maintsuslvl ? 1 << 5 : 0;
+ inst[n].data[1] += i->op[Modulator].keybscale ? 1 << 4 : 0;
+ inst[n].data[1] += (i->op[Modulator].octave + 1) & 0xffff; // Bug in original tracker
+
+ // Carrier "Key Scaling / Level" register
+ inst[n].data[10] = (i->op[Carrier].freqrisevollvldn & 3) << 6;
+ inst[n].data[10] += i->op[Carrier].softness & 63;
+ // Modulator...
+ inst[n].data[9] = (i->op[Modulator].freqrisevollvldn & 3) << 6;
+ inst[n].data[9] += i->op[Modulator].softness & 63;
+
+ // Carrier "Attack / Decay" register
+ inst[n].data[4] = (i->op[Carrier].attack & 0x0f) << 4;
+ inst[n].data[4] += i->op[Carrier].decay & 0x0f;
+ // Modulator...
+ inst[n].data[3] = (i->op[Modulator].attack & 0x0f) << 4;
+ inst[n].data[3] += i->op[Modulator].decay & 0x0f;
+
+ // Carrier "Release / Sustain" register
+ inst[n].data[6] = (i->op[Carrier].release & 0x0f) << 4;
+ inst[n].data[6] += i->op[Carrier].sustain & 0x0f;
+ // Modulator...
+ inst[n].data[5] = (i->op[Modulator].release & 0x0f) << 4;
+ inst[n].data[5] += i->op[Modulator].sustain & 0x0f;
+
+ // Channel "Feedback / Connection" register
+ inst[n].data[0] = (i->op[Carrier].feedback & 7) << 1;
+
+ // Carrier/Modulator "Wave Select" registers
+ inst[n].data[8] = i->op[Carrier].waveform & 3;
+ inst[n].data[7] = i->op[Modulator].waveform & 3;
+}
diff --git a/plugins/adplug/adplug/adtrack.h b/plugins/adplug/adplug/adtrack.h
new file mode 100644
index 00000000..289491ff
--- /dev/null
+++ b/plugins/adplug/adplug/adtrack.h
@@ -0,0 +1,53 @@
+/*
+ * 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
+ *
+ * adtrack.h - Adlib Tracker 1.0 Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CadtrackLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CadtrackLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("Adlib Tracker 1.0"); };
+ unsigned int getinstruments()
+ { return 9; };
+
+private:
+ enum Operators {Carrier = 1, Modulator = 0};
+
+ typedef struct {
+ struct {
+ unsigned short appampmod, appvib, maintsuslvl, keybscale, octave,
+ freqrisevollvldn, softness, attack, decay, release, sustain,
+ feedback, waveform;
+ } op[2];
+ } AdTrackInst;
+
+ void convert_instrument(unsigned int n, AdTrackInst *i);
+};
diff --git a/plugins/adplug/adplug/amd.cpp b/plugins/adplug/adplug/amd.cpp
new file mode 100644
index 00000000..4f844227
--- /dev/null
+++ b/plugins/adplug/adplug/amd.cpp
@@ -0,0 +1,193 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * amd.cpp - AMD Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "amd.h"
+#include "debug.h"
+
+CPlayer *CamdLoader::factory(Copl *newopl)
+{
+ return new CamdLoader(newopl);
+}
+
+bool CamdLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ struct {
+ char id[9];
+ unsigned char version;
+ } header;
+ int i, j, k, t, numtrax, maxi = 0;
+ unsigned char buf, buf2, buf3;
+ const unsigned char convfx[10] = {0,1,2,9,17,11,13,18,3,14};
+ const unsigned char convvol[64] = {
+ 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 0xa, 0xa, 0xb,
+ 0xc, 0xc, 0xd, 0xe, 0xe, 0xf, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21,
+ 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b, 0x2d, 0x2e, 0x30, 0x32, 0x35,
+ 0x37, 0x3a, 0x3c, 0x3f
+ };
+
+ // file validation section
+ if(fp.filesize(f) < 1072) { fp.close(f); return false; }
+ f->seek(1062); f->readString(header.id, 9);
+ header.version = f->readInt(1);
+ if(strncmp(header.id, "<o\xefQU\xeeRoR", 9) &&
+ strncmp(header.id, "MaDoKaN96", 9)) { fp.close(f); return false; }
+
+ // load section
+ memset(inst, 0, sizeof(inst));
+ f->seek(0);
+ f->readString(songname, sizeof(songname));
+ f->readString(author, sizeof(author));
+ for(i = 0; i < 26; i++) {
+ f->readString(instname[i], 23);
+ for(j = 0; j < 11; j++) inst[i].data[j] = f->readInt(1);
+ }
+ length = f->readInt(1); nop = f->readInt(1) + 1;
+ for(i=0;i<128;i++) order[i] = f->readInt(1);
+ f->seek(10, binio::Add);
+ if(header.version == 0x10) { // unpacked module
+ maxi = nop * 9;
+ for(i=0;i<64*9;i++)
+ trackord[i/9][i%9] = i+1;
+ t = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++)
+ for(i=t;i<t+9;i++) {
+ buf = f->readInt(1);
+ tracks[i][j].param2 = (buf&127) % 10;
+ tracks[i][j].param1 = (buf&127) / 10;
+ buf = f->readInt(1);
+ tracks[i][j].inst = buf >> 4;
+ tracks[i][j].command = buf & 0x0f;
+ buf = f->readInt(1);
+ if(buf >> 4) // fix bug in AMD save routine
+ tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
+ else
+ tracks[i][j].note = 0;
+ tracks[i][j].inst += (buf & 1) << 4;
+ }
+ t += 9;
+ }
+ } else { // packed module
+ for(i=0;i<nop;i++)
+ for(j=0;j<9;j++)
+ trackord[i][j] = f->readInt(2) + 1;
+ numtrax = f->readInt(2);
+ for(k=0;k<numtrax;k++) {
+ i = f->readInt(2);
+ if(i > 575) i = 575; // fix corrupted modules
+ maxi = (i + 1 > maxi ? i + 1 : maxi);
+ j = 0;
+ do {
+ buf = f->readInt(1);
+ if(buf & 128) {
+ for(t = j; t < j + (buf & 127) && t < 64; t++) {
+ tracks[i][t].command = 0;
+ tracks[i][t].inst = 0;
+ tracks[i][t].note = 0;
+ tracks[i][t].param1 = 0;
+ tracks[i][t].param2 = 0;
+ }
+ j += buf & 127;
+ continue;
+ }
+ tracks[i][j].param2 = buf % 10;
+ tracks[i][j].param1 = buf / 10;
+ buf = f->readInt(1);
+ tracks[i][j].inst = buf >> 4;
+ tracks[i][j].command = buf & 0x0f;
+ buf = f->readInt(1);
+ if(buf >> 4) // fix bug in AMD save routine
+ tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
+ else
+ tracks[i][j].note = 0;
+ tracks[i][j].inst += (buf & 1) << 4;
+ j++;
+ } while(j<64);
+ }
+ }
+ fp.close(f);
+
+ // convert to protracker replay data
+ bpm = 50; restartpos = 0; flags = Decimal;
+ for(i=0;i<26;i++) { // convert instruments
+ buf = inst[i].data[0];
+ buf2 = inst[i].data[1];
+ inst[i].data[0] = inst[i].data[10];
+ inst[i].data[1] = buf;
+ buf = inst[i].data[2];
+ inst[i].data[2] = inst[i].data[5];
+ buf3 = inst[i].data[3];
+ inst[i].data[3] = buf;
+ buf = inst[i].data[4];
+ inst[i].data[4] = inst[i].data[7];
+ inst[i].data[5] = buf3;
+ buf3 = inst[i].data[6];
+ inst[i].data[6] = inst[i].data[8];
+ inst[i].data[7] = buf;
+ inst[i].data[8] = inst[i].data[9];
+ inst[i].data[9] = buf2;
+ inst[i].data[10] = buf3;
+ for(j=0;j<23;j++) // convert names
+ if(instname[i][j] == '\xff')
+ instname[i][j] = '\x20';
+ }
+ for(i=0;i<maxi;i++) // convert patterns
+ for(j=0;j<64;j++) {
+ tracks[i][j].command = convfx[tracks[i][j].command];
+ // extended command
+ if(tracks[i][j].command == 14) {
+ if(tracks[i][j].param1 == 2) {
+ tracks[i][j].command = 10;
+ tracks[i][j].param1 = tracks[i][j].param2;
+ tracks[i][j].param2 = 0;
+ }
+
+ if(tracks[i][j].param1 == 3) {
+ tracks[i][j].command = 10;
+ tracks[i][j].param1 = 0;
+ }
+ }
+
+ // fix volume
+ if(tracks[i][j].command == 17) {
+ int vol = convvol[tracks[i][j].param1 * 10 + tracks[i][j].param2];
+
+ if(vol > 63) vol = 63;
+ tracks[i][j].param1 = vol / 10;
+ tracks[i][j].param2 = vol % 10;
+ }
+ }
+
+ rewind(0);
+ return true;
+}
+
+float CamdLoader::getrefresh()
+{
+ if(tempo)
+ return (float) (tempo);
+ else
+ return 18.2f;
+}
diff --git a/plugins/adplug/adplug/amd.h b/plugins/adplug/adplug/amd.h
new file mode 100644
index 00000000..daa630b6
--- /dev/null
+++ b/plugins/adplug/adplug/amd.h
@@ -0,0 +1,49 @@
+/*
+ * 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
+ *
+ * amd.h - AMD Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CamdLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CamdLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("AMUSIC Adlib Tracker"); };
+ std::string gettitle()
+ { return std::string(songname,0,24); };
+ std::string getauthor()
+ { return std::string(author,0,24); };
+ unsigned int getinstruments()
+ { return 26; };
+ std::string getinstrument(unsigned int n)
+ { return std::string(instname[n],0,23); };
+
+private:
+ char songname[24],author[24],instname[26][23];
+};
diff --git a/plugins/adplug/adplug/analopl.cpp b/plugins/adplug/adplug/analopl.cpp
new file mode 100644
index 00000000..1a7527df
--- /dev/null
+++ b/plugins/adplug/adplug/analopl.cpp
@@ -0,0 +1,67 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * analopl.cpp - Spectrum analyzing hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "analopl.h"
+
+CAnalopl::CAnalopl(unsigned short initport)
+ : CRealopl(initport)
+{
+ for(int i = 0; i < 9; i++) {
+ keyregs[0][i][0] = 0;
+ keyregs[0][i][1] = 0;
+ keyregs[1][i][0] = 0;
+ keyregs[1][i][1] = 0;
+ }
+}
+
+void CAnalopl::write(int reg, int val)
+{
+ if(nowrite) return;
+
+ if(reg >= 0xb0 && reg <= 0xb8) {
+ if(!keyregs[currChip][reg - 0xb0][0] && (val & 32))
+ keyregs[currChip][reg - 0xb0][1] = 1;
+ else
+ keyregs[currChip][reg - 0xb0][1] = 0;
+ keyregs[currChip][reg - 0xb0][0] = val & 32;
+ }
+
+ CRealopl::write(reg, val);
+}
+
+int CAnalopl::getcarriervol(unsigned int v, unsigned int c)
+{
+ return (hardvols[c][op_table[v]+3][0] & 63);
+}
+
+int CAnalopl::getmodulatorvol(unsigned int v, unsigned int c)
+{
+ return (hardvols[c][op_table[v]][0] & 63);
+}
+
+bool CAnalopl::getkeyon(unsigned int v, unsigned int c)
+{
+ if(keyregs[c][v][1]) {
+ keyregs[c][v][1] = 0;
+ return true;
+ } else
+ return false;
+}
diff --git a/plugins/adplug/adplug/analopl.h b/plugins/adplug/adplug/analopl.h
new file mode 100644
index 00000000..314c4247
--- /dev/null
+++ b/plugins/adplug/adplug/analopl.h
@@ -0,0 +1,44 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * analopl.h - Spectrum analyzing hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_ANALOPL
+#define H_ADPLUG_ANALOPL
+
+#include "realopl.h"
+
+class CAnalopl: public CRealopl
+{
+ public:
+ CAnalopl(unsigned short initport = DFL_ADLIBPORT); // initport = OPL2 hardware baseport
+
+ // get carrier volume of adlib voice v on chip c
+ int getcarriervol(unsigned int v, unsigned int c = 0);
+ // get modulator volume of adlib voice v on chip c
+ int getmodulatorvol(unsigned int v, unsigned int c = 0);
+ bool getkeyon(unsigned int v, unsigned int c = 0);
+
+ void write(int reg, int val);
+
+ protected:
+ unsigned char keyregs[2][9][2]; // shadow key register
+};
+
+#endif
diff --git a/plugins/adplug/adplug/bam.cpp b/plugins/adplug/adplug/bam.cpp
new file mode 100644
index 00000000..ea1aca01
--- /dev/null
+++ b/plugins/adplug/adplug/bam.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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
+ *
+ * bam.cpp - Bob's Adlib Music Player, by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * In my player, the loop counter is stored with the label. This can be
+ * dangerous for some situations (see below), but there shouldn't be any BAM
+ * files triggering this situation.
+ *
+ * From SourceForge Bug #476088:
+ * -----------------------------
+ * Using just one loop counter for each label, my player can't
+ * handle files that loop twice to the same label (if that's at
+ * all possible with BAM). Imagine the following situation:
+ *
+ * ... [*] ---- [<- *] ---- [<- *] ...
+ * ^ ^ ^ ^ ^ ^ ^
+ * | | | | | | |
+ * +---|----+-----|-----+-----|----+--- normal song data
+ * +----------|-----------|-------- label 1
+ * +-----------+-------- loop points to label 1
+ *
+ * both loop points loop to the same label. Storing the loop
+ * count with the label would cause chaos with the counter,
+ * when the player executes the inner jump.
+ * ------------------
+ * Not to worry. my reference implementation of BAM does not
+ * support the multiple loop situation you describe, and
+ * neither do any BAM-creation programs. Then both loops point
+ * to the same label, the inner loop's counter is just allowed
+ * to clobber the outer loop's counter. No stack is neccisary.
+ */
+
+#include <string.h>
+#include "bam.h"
+
+const unsigned short CbamPlayer::freq[] = {172,182,193,205,217,230,243,258,274,
+290,307,326,345,365,387,410,435,460,489,517,547,580,614,651,1369,1389,1411,
+1434,1459,1484,1513,1541,1571,1604,1638,1675,2393,2413,2435,2458,2483,2508,
+2537,2565,2595,2628,2662,2699,3417,3437,3459,3482,3507,3532,3561,3589,3619,
+3652,3686,3723,4441,4461,4483,4506,4531,4556,4585,4613,4643,4676,4710,4747,
+5465,5485,5507,5530,5555,5580,5609,5637,5667,5700,5734,5771,6489,6509,6531,
+6554,6579,6604,6633,6661,6691,6724,6758,6795,7513,7533,7555,7578,7603,7628,
+7657,7685,7715,7748,7782,7819,7858,7898,7942,7988,8037,8089,8143,8191,8191,
+8191,8191,8191,8191,8191,8191,8191,8191,8191,8191};
+
+CPlayer *CbamPlayer::factory(Copl *newopl)
+{
+ return new CbamPlayer(newopl);
+}
+
+bool CbamPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[4];
+ unsigned int i;
+
+ size = fp.filesize(f) - 4; // filesize minus header
+ f->readString(id, 4);
+ if(strncmp(id,"CBMF",4)) { fp.close(f); return false; }
+
+ song = new unsigned char [size];
+ for(i = 0; i < size; i++) song[i] = f->readInt(1);
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CbamPlayer::update()
+{
+ unsigned char cmd,c;
+
+ if(del) {
+ del--;
+ return !songend;
+ }
+
+ if(pos >= size) { // EOF detection
+ pos = 0;
+ songend = true;
+ }
+
+ while(song[pos] < 128) {
+ cmd = song[pos] & 240;
+ c = song[pos] & 15;
+ switch(cmd) {
+ case 0: // stop song
+ pos = 0;
+ songend = true;
+ break;
+ case 16: // start note
+ if(c < 9) {
+ opl->write(0xa0 + c, freq[song[++pos]] & 255);
+ opl->write(0xb0 + c, (freq[song[pos]] >> 8) + 32);
+ } else
+ pos++;
+ pos++;
+ break;
+ case 32: // stop note
+ if(c < 9)
+ opl->write(0xb0 + c, 0);
+ pos++;
+ break;
+ case 48: // define instrument
+ if(c < 9) {
+ opl->write(0x20 + op_table[c],song[pos+1]);
+ opl->write(0x23 + op_table[c],song[pos+2]);
+ opl->write(0x40 + op_table[c],song[pos+3]);
+ opl->write(0x43 + op_table[c],song[pos+4]);
+ opl->write(0x60 + op_table[c],song[pos+5]);
+ opl->write(0x63 + op_table[c],song[pos+6]);
+ opl->write(0x80 + op_table[c],song[pos+7]);
+ opl->write(0x83 + op_table[c],song[pos+8]);
+ opl->write(0xe0 + op_table[c],song[pos+9]);
+ opl->write(0xe3 + op_table[c],song[pos+10]);
+ opl->write(0xc0 + c,song[pos+11]);
+ }
+ pos += 12;
+ break;
+ case 80: // set label
+ label[c].target = ++pos;
+ label[c].defined = true;
+ break;
+ case 96: // jump
+ if(label[c].defined)
+ switch(song[pos+1]) {
+ case 254: // infinite loop
+ if(label[c].defined) {
+ pos = label[c].target;
+ songend = true;
+ break;
+ }
+ // fall through...
+ case 255: // chorus
+ if(!chorus && label[c].defined) {
+ chorus = true;
+ gosub = pos + 2;
+ pos = label[c].target;
+ break;
+ }
+ // fall through...
+ case 0: // end of loop
+ pos += 2;
+ break;
+ default: // finite loop
+ if(!label[c].count) { // loop elapsed
+ label[c].count = 255;
+ pos += 2;
+ break;
+ }
+ if(label[c].count < 255) // loop defined
+ label[c].count--;
+ else // loop undefined
+ label[c].count = song[pos+1] - 1;
+ pos = label[c].target;
+ break;
+ }
+ break;
+ case 112: // end of chorus
+ if(chorus) {
+ pos = gosub;
+ chorus = false;
+ } else
+ pos++;
+ break;
+ default: // reserved command (skip)
+ pos++;
+ break;
+ }
+ }
+ if(song[pos] >= 128) { // wait
+ del = song[pos] - 127;
+ pos++;
+ }
+ return !songend;
+}
+
+void CbamPlayer::rewind(int subsong)
+{
+ int i;
+
+ pos = 0; songend = false; del = 0; gosub = 0; chorus = false;
+ memset(label, 0, sizeof(label)); label[0].defined = true;
+ for(i = 0; i < 16; i++) label[i].count = 255; // 255 = undefined
+ opl->init(); opl->write(1,32);
+}
diff --git a/plugins/adplug/adplug/bam.h b/plugins/adplug/adplug/bam.h
new file mode 100644
index 00000000..53f321b2
--- /dev/null
+++ b/plugins/adplug/adplug/bam.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ *
+ * bam.h - Bob's Adlib Music Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CbamPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CbamPlayer(Copl *newopl)
+ : CPlayer(newopl), song(0)
+ { };
+ ~CbamPlayer()
+ { if(song) delete [] song; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return 25.0f; };
+
+ std::string gettype()
+ { return std::string("Bob's Adlib Music"); };
+
+private:
+ static const unsigned short freq[];
+
+ unsigned char *song, del;
+ unsigned long pos, size, gosub;
+ bool songend, chorus;
+
+ struct {
+ unsigned long target;
+ bool defined;
+ unsigned char count;
+ } label[16];
+};
diff --git a/plugins/adplug/adplug/bmf.cpp b/plugins/adplug/adplug/bmf.cpp
new file mode 100644
index 00000000..df403c86
--- /dev/null
+++ b/plugins/adplug/adplug/bmf.cpp
@@ -0,0 +1,597 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2003, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * [xad] BMF player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : GAMESNET.COM
+ type : GamesNet advertising intro
+ tune : by (?)The Brain [Razor 1911]
+ player : ver.0.9b by Hammer
+
+ file(s) : 2FAST4U.COM
+ type : Ford Knox BBStro
+ tune : by The Brain [Razor 1911]
+ player : ver.1.1 by ?
+ comment : in original player at 9th channel the feedback adlib register is not C8 but C6.
+
+ file(s) : DATURA.COM
+ type : Datura BBStro
+ tune : by The Brain [Razor 1911]
+ player : ver.1.2 by ?
+ comment : inaccurate replaying, because constant outport; in original player it can be 380 or 382.
+*/
+
+#include <string.h>
+#include "bmf.h"
+#include "debug.h"
+
+const unsigned char CxadbmfPlayer::bmf_adlib_registers[117] =
+{
+ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xA0, 0xB0, 0xC0, 0xE0, 0xE3,
+ 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xA1, 0xB1, 0xC1, 0xE1, 0xE4,
+ 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xA2, 0xB2, 0xC2, 0xE2, 0xE5,
+ 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xA3, 0xB3, 0xC3, 0xE8, 0xEB,
+ 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xA4, 0xB4, 0xC4, 0xE9, 0xEC,
+ 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xA5, 0xB5, 0xC5, 0xEA, 0xED,
+ 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xA6, 0xB6, 0xC6, 0xF0, 0xF3,
+ 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xA7, 0xB7, 0xC7, 0xF1, 0xF4,
+ 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xA8, 0xB8, 0xC8, 0xF2, 0xF5
+};
+
+const unsigned short CxadbmfPlayer::bmf_notes[12] =
+{
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287
+};
+
+/* for 1.1 */
+const unsigned short CxadbmfPlayer::bmf_notes_2[12] =
+{
+ 0x159, 0x16D, 0x183, 0x19A, 0x1B2, 0x1CC, 0x1E8, 0x205, 0x223, 0x244, 0x267, 0x28B
+};
+
+const unsigned char CxadbmfPlayer::bmf_default_instrument[13] =
+{
+ 0x01, 0x01, 0x3F, 0x3F, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+CPlayer *CxadbmfPlayer::factory(Copl *newopl)
+{
+ return new CxadbmfPlayer(newopl);
+}
+
+bool CxadbmfPlayer::xadplayer_load()
+{
+ unsigned short ptr = 0;
+ int i;
+
+ if(xad.fmt != BMF)
+ return false;
+
+#ifdef DEBUG
+ AdPlug_LogWrite("\nbmf_load():\n\n");
+#endif
+ if (!strncmp((char *)&tune[0],"BMF1.2",6))
+ {
+ bmf.version = BMF1_2;
+ bmf.timer = 70.0f;
+ }
+ else if (!strncmp((char *)&tune[0],"BMF1.1",6))
+ {
+ bmf.version = BMF1_1;
+ bmf.timer = 60.0f;
+ }
+ else
+ {
+ bmf.version = BMF0_9B;
+ bmf.timer = 18.2f;
+ }
+
+ // copy title & author
+ if (bmf.version > BMF0_9B)
+ {
+ ptr = 6;
+
+ strncpy(bmf.title,(char *)&tune[ptr],36);
+
+ while (tune[ptr]) { ptr++; }
+ ptr++;
+
+ strncpy(bmf.author,(char *)&tune[ptr],36);
+
+ while (tune[ptr]) { ptr++; }
+ ptr++;
+ }
+ else
+ {
+ strncpy(bmf.title,xad.title,36);
+ strncpy(bmf.author,xad.author,36);
+ }
+
+ // speed
+ if (bmf.version > BMF0_9B)
+ bmf.speed = tune[ptr++];
+ else
+ bmf.speed = ((tune[ptr++] << 8) / 3) >> 8; // strange, yeh ?
+
+ // load instruments
+ if (bmf.version > BMF0_9B)
+ {
+ unsigned long iflags = (tune[ptr] << 24) | (tune[ptr+1] << 16) | (tune[ptr+2] << 8) | tune[ptr+3];
+ ptr+=4;
+
+ for(i=0;i<32;i++)
+ if (iflags & (1 << (31-i)))
+ {
+ strcpy(bmf.instruments[i].name, (char *)&tune[ptr]);
+ memcpy(bmf.instruments[i].data, &tune[ptr+11], 13);
+ ptr += 24;
+ }
+ else
+ {
+ bmf.instruments[i].name[0] = 0;
+
+ if (bmf.version == BMF1_1)
+ for(int j=0;j<13;j++)
+ bmf.instruments[i].data[j] = bmf_default_instrument[j];
+ else
+ for(int j=0;j<13;j++)
+ bmf.instruments[i].data[j] = 0;
+ }
+ }
+ else
+ {
+ ptr = 6;
+
+ for(i=0;i<32;i++)
+ {
+ bmf.instruments[i].name[0] = 0;
+ memcpy(bmf.instruments[tune[ptr]].data, &tune[ptr+2],13); // bug no.1 (no instrument-table-end detection)
+ ptr+=15;
+ }
+ }
+
+ // load streams
+ if (bmf.version > BMF0_9B)
+ {
+ unsigned long sflags = (tune[ptr] << 24) | (tune[ptr+1] << 16) | (tune[ptr+2] << 8) | tune[ptr+3];
+ ptr+=4;
+
+ for(i=0;i<9;i++)
+ if (sflags & (1 << (31-i)))
+ ptr+=__bmf_convert_stream(&tune[ptr],i);
+ else
+ bmf.streams[i][0].cmd = 0xFF;
+ }
+ else
+ {
+ for(i=0;i<tune[5];i++)
+ ptr+=__bmf_convert_stream(&tune[ptr],i);
+
+ for(i=tune[5];i<9;i++)
+ bmf.streams[i][0].cmd = 0xFF;
+ }
+
+ return true;
+}
+
+void CxadbmfPlayer::xadplayer_rewind(int subsong)
+{
+ int i,j;
+
+ for(i=0; i<9; i++)
+ {
+ bmf.channel[i].stream_position = 0;
+ bmf.channel[i].delay = 0;
+ bmf.channel[i].loop_position = 0;
+ bmf.channel[i].loop_counter = 0;
+ }
+
+ plr.speed = bmf.speed;
+#ifdef DEBUG
+ AdPlug_LogWrite("speed: %x\n",plr.speed);
+#endif
+
+ bmf.active_streams = 9;
+
+ // OPL initialization
+ if (bmf.version > BMF0_9B)
+ {
+ opl_write(0x01, 0x20);
+
+ /* 1.1 */
+ if (bmf.version == BMF1_1)
+ for(i=0;i<9;i++)
+ for(j=0;j<13;j++)
+ opl_write(bmf_adlib_registers[13*i+j], bmf_default_instrument[j]);
+ /* 1.2 */
+ else if (bmf.version == BMF1_2)
+ for(i=0x20; i<0x100; i++)
+ opl_write(i,0xFF); // very interesting, really!
+ }
+
+ /* ALL */
+
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0xC0);
+}
+
+void CxadbmfPlayer::xadplayer_update()
+{
+ for(int i=0;i<9;i++)
+ if (bmf.channel[i].stream_position != 0xFFFF)
+ if (bmf.channel[i].delay)
+ bmf.channel[i].delay--;
+ else
+ {
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X:\n", i);
+#endif
+ bmf_event event;
+
+ // process so-called cross-events
+ while (true)
+ {
+ memcpy(&event, &bmf.streams[i][bmf.channel[i].stream_position], sizeof(bmf_event));
+#ifdef DEBUG
+ AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X\n",
+ event.note,event.delay,event.volume,event.instrument,
+ event.cmd,event.cmd_data);
+#endif
+
+ if (event.cmd == 0xFF)
+ {
+ bmf.channel[i].stream_position = 0xFFFF;
+ bmf.active_streams--;
+ break;
+ }
+ else if (event.cmd == 0xFE)
+ {
+ bmf.channel[i].loop_position = bmf.channel[i].stream_position+1;
+ bmf.channel[i].loop_counter = event.cmd_data;
+ }
+ else if (event.cmd == 0xFD)
+ {
+ if (bmf.channel[i].loop_counter)
+ {
+ bmf.channel[i].stream_position = bmf.channel[i].loop_position-1;
+ bmf.channel[i].loop_counter--;
+ }
+ }
+ else
+ break;
+
+ bmf.channel[i].stream_position++;
+ } // while (true)
+
+ // process normal event
+ unsigned short pos = bmf.channel[i].stream_position;
+
+ if (pos != 0xFFFF)
+ {
+ bmf.channel[i].delay = bmf.streams[i][pos].delay;
+
+ // command ?
+ if (bmf.streams[i][pos].cmd)
+ {
+ unsigned char cmd = bmf.streams[i][pos].cmd;
+
+ // 0x01: Set Modulator Volume
+ if (cmd == 0x01)
+ {
+ unsigned char reg = bmf_adlib_registers[13*i+2];
+
+ opl_write(reg, (adlib[reg] | 0x3F) - bmf.streams[i][pos].cmd_data);
+ }
+ // 0x10: Set Speed
+ else if (cmd == 0x10)
+ {
+ plr.speed = bmf.streams[i][pos].cmd_data;
+ plr.speed_counter = plr.speed;
+ }
+ } // if (bmf.streams[i][pos].cmd)
+
+ // instrument ?
+ if (bmf.streams[i][pos].instrument)
+ {
+ unsigned char ins = bmf.streams[i][pos].instrument-1;
+
+ if (bmf.version != BMF1_1)
+ opl_write(0xB0+i, adlib[0xB0+i] & 0xDF);
+
+ for(int j=0;j<13;j++)
+ opl_write(bmf_adlib_registers[i*13+j], bmf.instruments[ins].data[j]);
+ } // if (bmf.streams[i][pos].instrument)
+
+ // volume ?
+ if (bmf.streams[i][pos].volume)
+ {
+ unsigned char vol = bmf.streams[i][pos].volume-1;
+ unsigned char reg = bmf_adlib_registers[13*i+3];
+
+ opl_write(reg, (adlib[reg] | 0x3F) - vol);
+ } // if (bmf.streams[i][pos].volume)
+
+ // note ?
+ if (bmf.streams[i][pos].note)
+ {
+ unsigned short note = bmf.streams[i][pos].note;
+ unsigned short freq = 0;
+
+ // mute channel
+ opl_write(0xB0+i, adlib[0xB0+i] & 0xDF);
+
+ // get frequency
+ if (bmf.version == BMF1_1)
+ {
+ if (note <= 0x60)
+ freq = bmf_notes_2[--note % 12];
+ }
+ else
+ {
+ if (note != 0x7F)
+ freq = bmf_notes[--note % 12];
+ }
+
+ // play note
+ if (freq)
+ {
+ opl_write(0xB0+i, (freq >> 8) | ((note / 12) << 2) | 0x20);
+ opl_write(0xA0+i, freq & 0xFF);
+ }
+ } // if (bmf.streams[i][pos].note)
+
+ bmf.channel[i].stream_position++;
+ } // if (pos != 0xFFFF)
+
+ } // if (!bmf.channel[i].delay)
+
+ // is module loop ?
+ if (!bmf.active_streams)
+ {
+ for(int j=0;j<9;j++)
+ bmf.channel[j].stream_position = 0;
+
+ bmf.active_streams = 9;
+
+ plr.looping = 1;
+ }
+}
+
+float CxadbmfPlayer::xadplayer_getrefresh()
+{
+ return bmf.timer;
+}
+
+std::string CxadbmfPlayer::xadplayer_gettype()
+{
+ return std::string("xad: BMF Adlib Tracker");
+}
+
+std::string CxadbmfPlayer::xadplayer_gettitle()
+{
+ return std::string(bmf.title);
+}
+
+std::string CxadbmfPlayer::xadplayer_getauthor()
+{
+ return std::string(bmf.author);
+}
+
+unsigned int CxadbmfPlayer::xadplayer_getinstruments()
+{
+ return 32;
+}
+
+std::string CxadbmfPlayer::xadplayer_getinstrument(unsigned int i)
+{
+ return std::string(bmf.instruments[i].name);
+}
+
+/* -------- Internal Functions ---------------------------- */
+
+int CxadbmfPlayer::__bmf_convert_stream(unsigned char *stream, int channel)
+{
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X (note,delay,volume,instrument,command,command_data):\n",channel);
+ unsigned char *last = stream;
+#endif
+ unsigned char *stream_start = stream;
+
+ int pos = 0;
+
+ while (true)
+ {
+ memset(&bmf.streams[channel][pos], 0, sizeof(bmf_event));
+
+ bool is_cmd = false;
+
+ if (*stream == 0xFE)
+ {
+ // 0xFE -> 0xFF: End of Stream
+ bmf.streams[channel][pos].cmd = 0xFF;
+
+ stream++;
+
+ break;
+ }
+ else if (*stream == 0xFC)
+ {
+ // 0xFC -> 0xFE xx: Save Loop Position
+ bmf.streams[channel][pos].cmd = 0xFE;
+ bmf.streams[channel][pos].cmd_data = (*(stream+1) & ((bmf.version == BMF0_9B) ? 0x7F : 0x3F)) - 1;
+
+ stream+=2;
+ }
+ else if (*stream == 0x7D)
+ {
+ // 0x7D -> 0xFD: Loop Saved Position
+ bmf.streams[channel][pos].cmd = 0xFD;
+
+ stream++;
+ }
+ else
+ {
+ if (*stream & 0x80)
+ {
+ if (*(stream+1) & 0x80)
+ {
+ if (*(stream+1) & 0x40)
+ {
+ // byte0: 1aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+ // byte1: 11bbbbbb = DELAY
+ bmf.streams[channel][pos].delay = *(stream+1) & 0x3F;
+ // byte2: cccccccc = COMMAND
+
+ stream+=2;
+
+ is_cmd = true;
+ }
+ else
+ {
+ // byte0: 1aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+ // byte1: 11bbbbbb = DELAY
+ bmf.streams[channel][pos].delay = *(stream+1) & 0x3F;
+
+ stream+=2;
+ } // if (*(stream+1) & 0x40)
+ }
+ else
+ {
+ // byte0: 1aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+ // byte1: 0bbbbbbb = COMMAND
+
+ stream++;
+
+ is_cmd = true;
+ } // if (*(stream+1) & 0x80)
+ }
+ else
+ {
+ // byte0: 0aaaaaaa = NOTE
+ bmf.streams[channel][pos].note = *stream & 0x7F;
+
+ stream++;
+ } // if (*stream & 0x80)
+ } // if (*stream == 0xFE)
+
+ // is command ?
+ if (is_cmd)
+ {
+
+ /* ALL */
+
+ if ((0x20 <= *stream) && (*stream <= 0x3F))
+ {
+ // 0x20 or higher; 0x3F or lower: Set Instrument
+ bmf.streams[channel][pos].instrument = *stream - 0x20 + 1;
+
+ stream++;
+ }
+ else if (0x40 <= *stream)
+ {
+ // 0x40 or higher: Set Volume
+ bmf.streams[channel][pos].volume = *stream - 0x40 + 1;
+
+ stream++;
+ }
+ else
+ {
+
+ /* 0.9b */
+
+ if (bmf.version == BMF0_9B)
+ if (*stream < 0x20)
+ {
+ // 0x1F or lower: ?
+ stream++;
+ }
+
+ /* 1.2 */
+
+ if (bmf.version == BMF1_2)
+ if (*stream == 0x01)
+ {
+ // 0x01: Set Modulator Volume -> 0x01
+ bmf.streams[channel][pos].cmd = 0x01;
+ bmf.streams[channel][pos].cmd_data = *(stream+1);
+
+ stream+=2;
+ }
+ else if (*stream == 0x02)
+ {
+ // 0x02: ?
+ stream+=2;
+ }
+ else if (*stream == 0x03)
+ {
+ // 0x03: ?
+ stream+=2;
+ }
+ else if (*stream == 0x04)
+ {
+ // 0x04: Set Speed -> 0x10
+ bmf.streams[channel][pos].cmd = 0x10;
+ bmf.streams[channel][pos].cmd_data = *(stream+1);
+
+ stream+=2;
+ }
+ else if (*stream == 0x05)
+ {
+ // 0x05: Set Carrier Volume (port 380)
+ bmf.streams[channel][pos].volume = *(stream+1) + 1;
+
+ stream+=2;
+ }
+ else if (*stream == 0x06)
+ {
+ // 0x06: Set Carrier Volume (port 382)
+ bmf.streams[channel][pos].volume = *(stream+1) + 1;
+
+ stream+=2;
+ } // if (bmf.version == BMF1_2)
+
+ } // if ((0x20 <= *stream) && (*stream <= 0x3F))
+
+ } // if (is_cmd)
+
+#ifdef DEBUG
+ AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X <---- ",
+ bmf.streams[channel][pos].note,
+ bmf.streams[channel][pos].delay,
+ bmf.streams[channel][pos].volume,
+ bmf.streams[channel][pos].instrument,
+ bmf.streams[channel][pos].cmd,
+ bmf.streams[channel][pos].cmd_data
+ );
+ for(int zz=0;zz<(stream-last);zz++)
+ AdPlug_LogWrite("%02X ",last[zz]);
+ AdPlug_LogWrite("\n");
+ last=stream;
+#endif
+ pos++;
+ } // while (true)
+
+ return (stream - stream_start);
+}
diff --git a/plugins/adplug/adplug/bmf.h b/plugins/adplug/adplug/bmf.h
new file mode 100644
index 00000000..12cbdfd3
--- /dev/null
+++ b/plugins/adplug/adplug/bmf.h
@@ -0,0 +1,91 @@
+/*
+ * 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
+ *
+ * [xad] BMF player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadbmfPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadbmfPlayer(Copl *newopl): CxadPlayer(newopl)
+ { };
+ ~CxadbmfPlayer()
+ { };
+
+protected:
+ enum { BMF0_9B, BMF1_1, BMF1_2 };
+ //
+ struct bmf_event
+ {
+ unsigned char note;
+ unsigned char delay;
+ unsigned char volume;
+ unsigned char instrument;
+ unsigned char cmd;
+ unsigned char cmd_data;
+ };
+
+ struct
+ {
+ unsigned char version;
+ char title[36];
+ char author[36];
+ float timer;
+ unsigned char speed;
+
+ struct
+ {
+ char name[11];
+ unsigned char data[13];
+ } instruments[32];
+
+ bmf_event streams[9][1024];
+
+ int active_streams;
+
+ struct
+ {
+ unsigned short stream_position;
+ unsigned char delay;
+ unsigned short loop_position;
+ unsigned char loop_counter;
+ } channel[9];
+ } bmf;
+ //
+ bool xadplayer_load();
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ std::string xadplayer_gettitle();
+ std::string xadplayer_getauthor();
+ std::string xadplayer_getinstrument(unsigned int i);
+ unsigned int xadplayer_getinstruments();
+ //
+private:
+ static const unsigned char bmf_adlib_registers[117];
+ static const unsigned short bmf_notes[12];
+ static const unsigned short bmf_notes_2[12];
+ static const unsigned char bmf_default_instrument[13];
+
+ int __bmf_convert_stream(unsigned char *stream, int channel);
+};
diff --git a/plugins/adplug/adplug/cff.cpp b/plugins/adplug/adplug/cff.cpp
new file mode 100644
index 00000000..37197fd9
--- /dev/null
+++ b/plugins/adplug/adplug/cff.cpp
@@ -0,0 +1,508 @@
+/*
+ AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ cff.cpp - BoomTracker loader by Riven the Mage <riven@ok.ru>
+*/
+/*
+ NOTE: Conversion of slides is not 100% accurate. Original volume slides
+ have effect on carrier volume only. Also, original arpeggio, frequency & volume
+ slides use previous effect data instead of current.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "cff.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CcffLoader::factory(Copl *newopl)
+{
+ return new CcffLoader(newopl);
+}
+
+bool CcffLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_inst[11] = { 2,1,10,9,4,3,6,5,0,8,7 };
+ const unsigned short conv_note[12] = { 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE };
+
+ int i,j,k,t=0;
+
+ // '<CUD-FM-File>' - signed ?
+ f->readString(header.id, 16);
+ header.version = f->readInt(1); header.size = f->readInt(2);
+ header.packed = f->readInt(1); f->readString((char *)header.reserved, 12);
+ if (memcmp(header.id,"<CUD-FM-File>""\x1A\xDE\xE0",16))
+ { fp.close(f); return false; }
+
+ unsigned char *module = new unsigned char [0x10000];
+
+ // packed ?
+ if (header.packed)
+ {
+ cff_unpacker *unpacker = new cff_unpacker;
+
+ unsigned char *packed_module = new unsigned char [header.size + 4];
+
+ memset(packed_module,0,header.size + 4);
+
+ f->readString((char *)packed_module, header.size);
+ fp.close(f);
+
+ if (!unpacker->unpack(packed_module,module))
+ {
+ delete unpacker;
+ delete packed_module;
+ delete module;
+ return false;
+ }
+
+ delete unpacker;
+ delete packed_module;
+
+ if (memcmp(&module[0x5E1],"CUD-FM-File - SEND A POSTCARD -",31))
+ {
+ delete module;
+ return false;
+ }
+ }
+ else
+ {
+ f->readString((char *)module, header.size);
+ fp.close(f);
+ }
+
+ // init CmodPlayer
+ realloc_instruments(47);
+ realloc_order(64);
+ realloc_patterns(36,64,9);
+ init_notetable(conv_note);
+ init_trackord();
+
+ // load instruments
+ for (i=0;i<47;i++)
+ {
+ memcpy(&instruments[i],&module[i*32],sizeof(cff_instrument));
+
+ for (j=0;j<11;j++)
+ inst[i].data[conv_inst[j]] = instruments[i].data[j];
+
+ instruments[i].name[20] = 0;
+ }
+
+ // number of patterns
+ nop = module[0x5E0];
+
+ // load title & author
+ memcpy(song_title,&module[0x614],20);
+ memcpy(song_author,&module[0x600],20);
+
+ // load order
+ memcpy(order,&module[0x628],64);
+
+ // load tracks
+ for (i=0;i<nop;i++)
+ {
+ unsigned char old_event_byte2[9];
+
+ memset(old_event_byte2,0,9);
+
+ for (j=0;j<9;j++)
+ {
+ for (k=0;k<64;k++)
+ {
+ cff_event *event = (cff_event *)&module[0x669 + ((i*64+k)*9+j)*3];
+
+ // convert note
+ if (event->byte0 == 0x6D)
+ tracks[t][k].note = 127;
+ else
+ if (event->byte0)
+ tracks[t][k].note = event->byte0;
+
+ if (event->byte2)
+ old_event_byte2[j] = event->byte2;
+
+ // convert effect
+ switch (event->byte1)
+ {
+ case 'I': // set instrument
+ tracks[t][k].inst = event->byte2 + 1;
+ tracks[t][k].param1 = tracks[t][k].param2 = 0;
+ break;
+
+ case 'H': // set tempo
+ tracks[t][k].command = 7;
+ if (event->byte2 < 16)
+ {
+ tracks[t][k].param1 = 0x07;
+ tracks[t][k].param2 = 0x0D;
+ }
+ break;
+
+ case 'A': // set speed
+ tracks[t][k].command = 19;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'L': // pattern break
+ tracks[t][k].command = 13;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'K': // order jump
+ tracks[t][k].command = 11;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'M': // set vibrato/tremolo
+ tracks[t][k].command = 27;
+ tracks[t][k].param1 = event->byte2 >> 4;
+ tracks[t][k].param2 = event->byte2 & 15;
+ break;
+
+ case 'C': // set modulator volume
+ tracks[t][k].command = 21;
+ tracks[t][k].param1 = (0x3F - event->byte2) >> 4;
+ tracks[t][k].param2 = (0x3F - event->byte2) & 15;
+ break;
+
+ case 'G': // set carrier volume
+ tracks[t][k].command = 22;
+ tracks[t][k].param1 = (0x3F - event->byte2) >> 4;
+ tracks[t][k].param2 = (0x3F - event->byte2) & 15;
+ break;
+
+ case 'B': // set carrier waveform
+ tracks[t][k].command = 25;
+ tracks[t][k].param1 = event->byte2;
+ tracks[t][k].param2 = 0x0F;
+ break;
+
+ case 'E': // fine frequency slide down
+ tracks[t][k].command = 24;
+ tracks[t][k].param1 = old_event_byte2[j] >> 4;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ break;
+
+ case 'F': // fine frequency slide up
+ tracks[t][k].command = 23;
+ tracks[t][k].param1 = old_event_byte2[j] >> 4;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ break;
+
+ case 'D': // fine volume slide
+ tracks[t][k].command = 14;
+ if (old_event_byte2[j] & 15)
+ {
+ // slide down
+ tracks[t][k].param1 = 5;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ }
+ else
+ {
+ // slide up
+ tracks[t][k].param1 = 4;
+ tracks[t][k].param2 = old_event_byte2[j] >> 4;
+ }
+ break;
+
+ case 'J': // arpeggio
+ tracks[t][k].param1 = old_event_byte2[j] >> 4;
+ tracks[t][k].param2 = old_event_byte2[j] & 15;
+ break;
+ }
+ }
+
+ t++;
+ }
+ }
+
+ delete [] module;
+
+ // order loop
+ restartpos = 0;
+
+ // order length
+ for (i=0;i<64;i++)
+ {
+ if (order[i] >= 0x80)
+ {
+ length = i;
+ break;
+ }
+ }
+
+ // default tempo
+ bpm = 0x7D;
+
+ rewind(0);
+
+ return true;
+}
+
+void CcffLoader::rewind(int subsong)
+{
+ CmodPlayer::rewind(subsong);
+
+ // default instruments
+ for (int i=0;i<9;i++)
+ {
+ channel[i].inst = i;
+
+ channel[i].vol1 = 63 - (inst[i].data[10] & 63);
+ channel[i].vol2 = 63 - (inst[i].data[9] & 63);
+ }
+}
+
+std::string CcffLoader::gettype()
+{
+ if (header.packed)
+ return std::string("BoomTracker 4, packed");
+ else
+ return std::string("BoomTracker 4");
+}
+
+std::string CcffLoader::gettitle()
+{
+ return std::string(song_title,20);
+}
+
+std::string CcffLoader::getauthor()
+{
+ return std::string(song_author,20);
+}
+
+std::string CcffLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name);
+}
+
+unsigned int CcffLoader::getinstruments()
+{
+ return 47;
+}
+
+/* -------- Private Methods ------------------------------- */
+
+#ifdef _WIN32
+#pragma warning(disable:4244)
+#pragma warning(disable:4018)
+#endif
+
+/*
+ Lempel-Ziv-Tyr ;-)
+*/
+long CcffLoader::cff_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf)
+{
+ if (memcmp(ibuf,"YsComp""\x07""CUD1997""\x1A\x04",16))
+ return 0;
+
+ input = ibuf + 16;
+ output = obuf;
+
+ output_length = 0;
+
+ heap = (unsigned char *)malloc(0x10000);
+ dictionary = (unsigned char **)malloc(sizeof(unsigned char *)*0x8000);
+
+ memset(heap,0,0x10000);
+ memset(dictionary,0,0x8000);
+
+ cleanup();
+ if(!startup())
+ goto out;
+
+ // LZW
+ while (1)
+ {
+ new_code = get_code();
+
+ // 0x00: end of data
+ if (new_code == 0)
+ break;
+
+ // 0x01: end of block
+ if (new_code == 1)
+ {
+ cleanup();
+ if(!startup())
+ goto out;
+
+ continue;
+ }
+
+ // 0x02: expand code length
+ if (new_code == 2)
+ {
+ code_length++;
+
+ continue;
+ }
+
+ // 0x03: RLE
+ if (new_code == 3)
+ {
+ unsigned char old_code_length = code_length;
+
+ code_length = 2;
+
+ unsigned char repeat_length = get_code() + 1;
+
+ code_length = 4 << get_code();
+
+ unsigned long repeat_counter = get_code();
+
+ if(output_length + repeat_counter * repeat_length > 0x10000) {
+ output_length = 0;
+ goto out;
+ }
+
+ for (unsigned int i=0;i<repeat_counter*repeat_length;i++)
+ output[output_length++] = output[output_length - repeat_length];
+
+ code_length = old_code_length;
+
+ if(!startup())
+ goto out;
+
+ continue;
+ }
+
+ if (new_code >= (0x104 + dictionary_length))
+ {
+ // dictionary <- old.code.string + old.code.char
+ the_string[++the_string[0]] = the_string[1];
+ }
+ else
+ {
+ // dictionary <- old.code.string + new.code.char
+ unsigned char temp_string[256];
+
+ translate_code(new_code,temp_string);
+
+ the_string[++the_string[0]] = temp_string[1];
+ }
+
+ expand_dictionary(the_string);
+
+ // output <- new.code.string
+ translate_code(new_code,the_string);
+
+ if(output_length + the_string[0] > 0x10000) {
+ output_length = 0;
+ goto out;
+ }
+
+ for (int i=0;i<the_string[0];i++)
+ output[output_length++] = the_string[i+1];
+
+ old_code = new_code;
+ }
+
+ out:
+ free(heap);
+ free(dictionary);
+ return output_length;
+}
+
+unsigned long CcffLoader::cff_unpacker::get_code()
+{
+ unsigned long code;
+
+ while (bits_left < code_length)
+ {
+ bits_buffer |= ((*input++) << bits_left);
+ bits_left += 8;
+ }
+
+ code = bits_buffer & ((1 << code_length) - 1);
+
+ bits_buffer >>= code_length;
+ bits_left -= code_length;
+
+ return code;
+}
+
+void CcffLoader::cff_unpacker::translate_code(unsigned long code, unsigned char *string)
+{
+ unsigned char translated_string[256];
+
+ if (code >= 0x104)
+ {
+ memcpy(translated_string,dictionary[code - 0x104],(*(dictionary[code - 0x104])) + 1);
+ }
+ else
+ {
+ translated_string[0] = 1;
+ translated_string[1] = (code - 4) & 0xFF;
+ }
+
+ memcpy(string,translated_string,256);
+}
+
+void CcffLoader::cff_unpacker::cleanup()
+{
+ code_length = 9;
+
+ bits_buffer = 0;
+ bits_left = 0;
+
+ heap_length = 0;
+ dictionary_length = 0;
+}
+
+int CcffLoader::cff_unpacker::startup()
+{
+ old_code = get_code();
+
+ translate_code(old_code,the_string);
+
+ if(output_length + the_string[0] > 0x10000) {
+ output_length = 0;
+ return 0;
+ }
+
+ for (int i=0;i<the_string[0];i++)
+ output[output_length++] = the_string[i+1];
+
+ return 1;
+}
+
+void CcffLoader::cff_unpacker::expand_dictionary(unsigned char *string)
+{
+ if (string[0] >= 0xF0)
+ return;
+
+ memcpy(&heap[heap_length],string,string[0] + 1);
+
+ dictionary[dictionary_length] = &heap[heap_length];
+
+ dictionary_length++;
+
+ heap_length += (string[0] + 1);
+}
+
+#ifdef _WIN32
+#pragma warning(default:4244)
+#pragma warning(default:4018)
+#endif
diff --git a/plugins/adplug/adplug/cff.h b/plugins/adplug/adplug/cff.h
new file mode 100644
index 00000000..04ead8bb
--- /dev/null
+++ b/plugins/adplug/adplug/cff.h
@@ -0,0 +1,103 @@
+/*
+ AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ cff.h - BoomTracker loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CcffLoader: public CmodPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CcffLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ void rewind(int subsong);
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getauthor();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+ private:
+
+ class cff_unpacker
+ {
+ public:
+
+ long unpack(unsigned char *ibuf, unsigned char *obuf);
+
+ private:
+
+ unsigned long get_code();
+ void translate_code(unsigned long code, unsigned char *string);
+
+ void cleanup();
+ int startup();
+
+ void expand_dictionary(unsigned char *string);
+
+ unsigned char *input;
+ unsigned char *output;
+
+ long output_length;
+
+ unsigned char code_length;
+
+ unsigned long bits_buffer;
+ unsigned int bits_left;
+
+ unsigned char *heap;
+ unsigned char **dictionary;
+
+ unsigned int heap_length;
+ unsigned int dictionary_length;
+
+ unsigned long old_code,new_code;
+
+ unsigned char the_string[256];
+ };
+
+ struct cff_header
+ {
+ char id[16];
+ unsigned char version;
+ unsigned short size;
+ unsigned char packed;
+ unsigned char reserved[12];
+ } header;
+
+ struct cff_instrument
+ {
+ unsigned char data[12];
+ char name[21];
+ } instruments[47];
+
+ char song_title[20];
+ char song_author[20];
+
+ struct cff_event
+ {
+ unsigned char byte0;
+ unsigned char byte1;
+ unsigned char byte2;
+ };
+};
diff --git a/plugins/adplug/adplug/d00.cpp b/plugins/adplug/adplug/d00.cpp
new file mode 100644
index 00000000..7aa042d1
--- /dev/null
+++ b/plugins/adplug/adplug/d00.cpp
@@ -0,0 +1,545 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * d00.c - D00 Player by Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * Sorry for the goto's, but the code looks so much nicer now.
+ * I tried it with while loops but it was just a mess. If you
+ * can come up with a nicer solution, just tell me.
+ *
+ * BUGS:
+ * Hard restart SR is sometimes wrong
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "debug.h"
+#include "d00.h"
+
+#define HIBYTE(val) (val >> 8)
+#define LOBYTE(val) (val & 0xff)
+
+static const unsigned short notetable[12] = // D00 note table
+ {340,363,385,408,432,458,485,514,544,577,611,647};
+
+static inline uint16_t LE_WORD(const uint16_t *val)
+{
+ const uint8_t *b = (const uint8_t *)val;
+ return (b[1] << 8) + b[0];
+}
+
+/*** public methods *************************************/
+
+CPlayer *Cd00Player::factory(Copl *newopl)
+{
+ return new Cd00Player(newopl);
+}
+
+bool Cd00Player::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ d00header *checkhead;
+ d00header1 *ch;
+ unsigned long filesize;
+ int i,ver1=0;
+ char *str;
+
+ // file validation section
+ checkhead = new d00header;
+ f->readString((char *)checkhead, sizeof(d00header));
+
+ // Check for version 2-4 header
+ if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type ||
+ !checkhead->subsongs || checkhead->soundcard) {
+ // Check for version 0 or 1 header (and .d00 file extension)
+ delete checkhead;
+ if(!fp.extension(filename, ".d00")) { fp.close(f); return false; }
+ ch = new d00header1;
+ f->seek(0); f->readString((char *)ch, sizeof(d00header1));
+ if(ch->version > 1 || !ch->subsongs)
+ { delete ch; fp.close(f); return false; }
+ delete ch;
+ ver1 = 1;
+ } else
+ delete checkhead;
+
+ AdPlug_LogWrite("Cd00Player::load(f,\"%s\"): %s format D00 file detected!\n",
+ filename.c_str(), ver1 ? "Old" : "New");
+
+ // load section
+ filesize = fp.filesize(f); f->seek(0);
+ filedata = new char [filesize + 1]; // 1 byte is needed for old-style DataInfo block
+ f->readString((char *)filedata, filesize);
+ fp.close(f);
+ if(!ver1) { // version 2 and above
+ header = (struct d00header *)filedata;
+ version = header->version;
+ datainfo = (char *)filedata + LE_WORD(&header->infoptr);
+ inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header->instptr));
+ seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header->seqptr));
+ for(i=31;i>=0;i--) // erase whitespace
+ if(header->songname[i] == ' ')
+ header->songname[i] = '\0';
+ else
+ break;
+ for(i=31;i>=0;i--)
+ if(header->author[i] == ' ')
+ header->author[i] = '\0';
+ else
+ break;
+ } else { // version 1
+ header1 = (struct d00header1 *)filedata;
+ version = header1->version;
+ datainfo = (char *)filedata + LE_WORD(&header1->infoptr);
+ inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header1->instptr));
+ seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header1->seqptr));
+ }
+ switch(version) {
+ case 0:
+ levpuls = 0;
+ spfx = 0;
+ header1->speed = 70; // v0 files default to 70Hz
+ break;
+ case 1:
+ levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header1->lpulptr));
+ spfx = 0;
+ break;
+ case 2:
+ levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header->spfxptr));
+ spfx = 0;
+ break;
+ case 3:
+ spfx = 0;
+ levpuls = 0;
+ break;
+ case 4:
+ spfx = (struct Sspfx *)((char *)filedata + LE_WORD(&header->spfxptr));
+ levpuls = 0;
+ break;
+ }
+ if((str = strstr(datainfo,"\xff\xff")))
+ while((*str == '\xff' || *str == ' ') && str >= datainfo) {
+ *str = '\0'; str--;
+ }
+ else // old-style block
+ memset((char *)filedata+filesize,0,1);
+
+ rewind(0);
+ return true;
+}
+
+bool Cd00Player::update()
+{
+ unsigned char c,cnt,trackend=0,fx,note;
+ unsigned short ord,*patt,buf,fxop,pattpos;
+
+ // effect handling (timer dependant)
+ for(c=0;c<9;c++) {
+ channel[c].slideval += channel[c].slide; setfreq(c); // sliding
+ vibrato(c); // vibrato
+
+ if(channel[c].spfx != 0xffff) { // SpFX
+ if(channel[c].fxdel)
+ channel[c].fxdel--;
+ else {
+ channel[c].spfx = LE_WORD(&spfx[channel[c].spfx].ptr);
+ channel[c].fxdel = spfx[channel[c].spfx].duration;
+ channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff;
+ if(spfx[channel[c].spfx].modlev != 0xff)
+ channel[c].modvol = spfx[channel[c].spfx].modlev;
+ setinst(c);
+ if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency
+ note = spfx[channel[c].spfx].halfnote;
+ else // unlocked frequency
+ note = spfx[channel[c].spfx].halfnote + channel[c].note;
+ channel[c].freq = notetable[note%12] + ((note/12) << 10);
+ setfreq(c);
+ }
+ channel[c].modvol += spfx[channel[c].spfx].modlevadd; channel[c].modvol &= 63;
+ setvolume(c);
+ }
+
+ if(channel[c].levpuls != 0xff) // Levelpuls
+ if(channel[c].frameskip)
+ channel[c].frameskip--;
+ else {
+ channel[c].frameskip = inst[channel[c].inst].timer;
+ if(channel[c].fxdel)
+ channel[c].fxdel--;
+ else {
+ channel[c].levpuls = levpuls[channel[c].levpuls].ptr - 1;
+ channel[c].fxdel = levpuls[channel[c].levpuls].duration;
+ if(levpuls[channel[c].levpuls].level != 0xff)
+ channel[c].modvol = levpuls[channel[c].levpuls].level;
+ }
+ channel[c].modvol += levpuls[channel[c].levpuls].voladd; channel[c].modvol &= 63;
+ setvolume(c);
+ }
+ }
+
+ // song handling
+ for(c=0;c<9;c++)
+ if(version < 3 ? channel[c].del : channel[c].del <= 0x7f) {
+ if(version == 4) // v4: hard restart SR
+ if(channel[c].del == inst[channel[c].inst].timer)
+ if(channel[c].nextnote)
+ opl->write(0x83 + op_table[c], inst[channel[c].inst].sr);
+ if(version < 3)
+ channel[c].del--;
+ else
+ if(channel[c].speed)
+ channel[c].del += channel[c].speed;
+ else {
+ channel[c].seqend = 1;
+ continue;
+ }
+ } else {
+ if(channel[c].speed) {
+ if(version < 3)
+ channel[c].del = channel[c].speed;
+ else {
+ channel[c].del &= 0x7f;
+ channel[c].del += channel[c].speed;
+ }
+ } else {
+ channel[c].seqend = 1;
+ continue;
+ }
+ if(channel[c].rhcnt) { // process pending REST/HOLD events
+ channel[c].rhcnt--;
+ continue;
+ }
+ readorder: // process arrangement (orderlist)
+ ord = LE_WORD(&channel[c].order[channel[c].ordpos]);
+ switch(ord) {
+ case 0xfffe: channel[c].seqend = 1; continue; // end of arrangement stream
+ case 0xffff: // jump to order
+ channel[c].ordpos = LE_WORD(&channel[c].order[channel[c].ordpos + 1]);
+ channel[c].seqend = 1;
+ goto readorder;
+ default:
+ if(ord >= 0x9000) { // set speed
+ channel[c].speed = ord & 0xff;
+ ord = LE_WORD(&channel[c].order[channel[c].ordpos - 1]);
+ channel[c].ordpos++;
+ } else
+ if(ord >= 0x8000) { // transpose track
+ channel[c].transpose = ord & 0xff;
+ if(ord & 0x100)
+ channel[c].transpose = -channel[c].transpose;
+ ord = LE_WORD(&channel[c].order[++channel[c].ordpos]);
+ }
+ patt = (unsigned short *)((char *)filedata + LE_WORD(&seqptr[ord]));
+ break;
+ }
+ channel[c].fxflag = 0;
+ readseq: // process sequence (pattern)
+ if(!version) // v0: always initialize rhcnt
+ channel[c].rhcnt = channel[c].irhcnt;
+ pattpos = LE_WORD(&patt[channel[c].pattpos]);
+ if(pattpos == 0xffff) { // pattern ended?
+ channel[c].pattpos = 0;
+ channel[c].ordpos++;
+ goto readorder;
+ }
+ cnt = HIBYTE(pattpos);
+ note = LOBYTE(pattpos);
+ fx = pattpos >> 12;
+ fxop = pattpos & 0x0fff;
+ channel[c].pattpos++; pattpos = LE_WORD(&patt[channel[c].pattpos]);
+ channel[c].nextnote = LOBYTE(pattpos) & 0x7f;
+ if(version ? cnt < 0x40 : !fx) { // note event
+ switch(note) {
+ case 0: // REST event
+ case 0x80:
+ if(!note || version) {
+ channel[c].key = 0;
+ setfreq(c);
+ }
+ // fall through...
+ case 0x7e: // HOLD event
+ if(version)
+ channel[c].rhcnt = cnt;
+ channel[c].nextnote = 0;
+ break;
+ default: // play note
+ // restart fx
+ if(!(channel[c].fxflag & 1))
+ channel[c].vibdepth = 0;
+ if(!(channel[c].fxflag & 2))
+ channel[c].slideval = channel[c].slide = 0;
+
+ if(version) { // note handling for v1 and above
+ if(note > 0x80) // locked note (no channel transpose)
+ note -= 0x80;
+ else // unlocked note
+ note += channel[c].transpose;
+ channel[c].note = note; // remember note for SpFX
+
+ if(channel[c].ispfx != 0xffff && cnt < 0x20) { // reset SpFX
+ channel[c].spfx = channel[c].ispfx;
+ if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency
+ note = spfx[channel[c].spfx].halfnote;
+ else // unlocked frequency
+ note += spfx[channel[c].spfx].halfnote;
+ channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff;
+ channel[c].fxdel = spfx[channel[c].spfx].duration;
+ if(spfx[channel[c].spfx].modlev != 0xff)
+ channel[c].modvol = spfx[channel[c].spfx].modlev;
+ else
+ channel[c].modvol = inst[channel[c].inst].data[7] & 63;
+ }
+
+ if(channel[c].ilevpuls != 0xff && cnt < 0x20) { // reset LevelPuls
+ channel[c].levpuls = channel[c].ilevpuls;
+ channel[c].fxdel = levpuls[channel[c].levpuls].duration;
+ channel[c].frameskip = inst[channel[c].inst].timer;
+ if(levpuls[channel[c].levpuls].level != 0xff)
+ channel[c].modvol = levpuls[channel[c].levpuls].level;
+ else
+ channel[c].modvol = inst[channel[c].inst].data[7] & 63;
+ }
+
+ channel[c].freq = notetable[note%12] + ((note/12) << 10);
+ if(cnt < 0x20) // normal note
+ playnote(c);
+ else { // tienote
+ setfreq(c);
+ cnt -= 0x20; // make count proper
+ }
+ channel[c].rhcnt = cnt;
+ } else { // note handling for v0
+ if(cnt < 2) // unlocked note
+ note += channel[c].transpose;
+ channel[c].note = note;
+
+ channel[c].freq = notetable[note%12] + ((note/12) << 10);
+ if(cnt == 1) // tienote
+ setfreq(c);
+ else // normal note
+ playnote(c);
+ }
+ break;
+ }
+ continue; // event is complete
+ } else { // effect event
+ switch(fx) {
+ case 6: // Cut/Stop Voice
+ buf = channel[c].inst;
+ channel[c].inst = 0;
+ playnote(c);
+ channel[c].inst = buf;
+ channel[c].rhcnt = fxop;
+ continue; // no note follows this event
+ case 7: // Vibrato
+ channel[c].vibspeed = fxop & 0xff;
+ channel[c].vibdepth = fxop >> 8;
+ channel[c].trigger = fxop >> 9;
+ channel[c].fxflag |= 1;
+ break;
+ case 8: // v0: Duration
+ if(!version)
+ channel[c].irhcnt = fxop;
+ break;
+ case 9: // New Level
+ channel[c].vol = fxop & 63;
+ if(channel[c].vol + channel[c].cvol < 63) // apply channel volume
+ channel[c].vol += channel[c].cvol;
+ else
+ channel[c].vol = 63;
+ setvolume(c);
+ break;
+ case 0xb: // v4: Set SpFX
+ if(version == 4)
+ channel[c].ispfx = fxop;
+ break;
+ case 0xc: // Set Instrument
+ channel[c].ispfx = 0xffff;
+ channel[c].spfx = 0xffff;
+ channel[c].inst = fxop;
+ channel[c].modvol = inst[fxop].data[7] & 63;
+ if(version < 3 && version && inst[fxop].tunelev) // Set LevelPuls
+ channel[c].ilevpuls = inst[fxop].tunelev - 1;
+ else {
+ channel[c].ilevpuls = 0xff;
+ channel[c].levpuls = 0xff;
+ }
+ break;
+ case 0xd: // Slide up
+ channel[c].slide = fxop;
+ channel[c].fxflag |= 2;
+ break;
+ case 0xe: // Slide down
+ channel[c].slide = -fxop;
+ channel[c].fxflag |= 2;
+ break;
+ }
+ goto readseq; // event is incomplete, note follows
+ }
+ }
+
+ for(c=0;c<9;c++)
+ if(channel[c].seqend)
+ trackend++;
+ if(trackend == 9)
+ songend = 1;
+
+ return !songend;
+}
+
+void Cd00Player::rewind(int subsong)
+{
+ struct Stpoin {
+ unsigned short ptr[9];
+ unsigned char volume[9],dummy[5];
+ } *tpoin;
+ int i;
+
+ if(version > 1) { // do nothing if subsong > number of subsongs
+ if(subsong >= header->subsongs)
+ return;
+ } else
+ if(subsong >= header1->subsongs)
+ return;
+
+ memset(channel,0,sizeof(channel));
+ if(version > 1)
+ tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header->tpoin));
+ else
+ tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header1->tpoin));
+ for(i=0;i<9;i++) {
+ if(LE_WORD(&tpoin[subsong].ptr[i])) { // track enabled
+ channel[i].speed = LE_WORD((unsigned short *)
+ ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i])));
+ channel[i].order = (unsigned short *)
+ ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]) + 2);
+ } else { // track disabled
+ channel[i].speed = 0;
+ channel[i].order = 0;
+ }
+ channel[i].ispfx = 0xffff; channel[i].spfx = 0xffff; // no SpFX
+ channel[i].ilevpuls = 0xff; channel[i].levpuls = 0xff; // no LevelPuls
+ channel[i].cvol = tpoin[subsong].volume[i] & 0x7f; // our player may savely ignore bit 7
+ channel[i].vol = channel[i].cvol; // initialize volume
+ }
+ songend = 0;
+ opl->init(); opl->write(1,32); // reset OPL chip
+}
+
+std::string Cd00Player::gettype()
+{
+ char tmpstr[40];
+
+ sprintf(tmpstr,"EdLib packed (version %d)",version > 1 ? header->version : header1->version);
+ return std::string(tmpstr);
+}
+
+float Cd00Player::getrefresh()
+{
+ if(version > 1)
+ return header->speed;
+ else
+ return header1->speed;
+}
+
+unsigned int Cd00Player::getsubsongs()
+{
+ if(version <= 1) // return number of subsongs
+ return header1->subsongs;
+ else
+ return header->subsongs;
+}
+
+/*** private methods *************************************/
+
+void Cd00Player::setvolume(unsigned char chan)
+{
+ unsigned char op = op_table[chan];
+ unsigned short insnr = channel[chan].inst;
+
+ opl->write(0x43 + op,(int)(63-((63-(inst[insnr].data[2] & 63))/63.0)*(63-channel[chan].vol)) +
+ (inst[insnr].data[2] & 192));
+ if(inst[insnr].data[10] & 1)
+ opl->write(0x40 + op,(int)(63-((63-channel[chan].modvol)/63.0)*(63-channel[chan].vol)) +
+ (inst[insnr].data[7] & 192));
+ else
+ opl->write(0x40 + op,channel[chan].modvol + (inst[insnr].data[7] & 192));
+}
+
+void Cd00Player::setfreq(unsigned char chan)
+{
+ unsigned short freq = channel[chan].freq;
+
+ if(version == 4) // v4: apply instrument finetune
+ freq += inst[channel[chan].inst].tunelev;
+
+ freq += channel[chan].slideval;
+ opl->write(0xa0 + chan, freq & 255);
+ if(channel[chan].key)
+ opl->write(0xb0 + chan, ((freq >> 8) & 31) | 32);
+ else
+ opl->write(0xb0 + chan, (freq >> 8) & 31);
+}
+
+void Cd00Player::setinst(unsigned char chan)
+{
+ unsigned char op = op_table[chan];
+ unsigned short insnr = channel[chan].inst;
+
+ // set instrument data
+ opl->write(0x63 + op, inst[insnr].data[0]);
+ opl->write(0x83 + op, inst[insnr].data[1]);
+ opl->write(0x23 + op, inst[insnr].data[3]);
+ opl->write(0xe3 + op, inst[insnr].data[4]);
+ opl->write(0x60 + op, inst[insnr].data[5]);
+ opl->write(0x80 + op, inst[insnr].data[6]);
+ opl->write(0x20 + op, inst[insnr].data[8]);
+ opl->write(0xe0 + op, inst[insnr].data[9]);
+ if(version)
+ opl->write(0xc0 + chan, inst[insnr].data[10]);
+ else
+ opl->write(0xc0 + chan, (inst[insnr].data[10] << 1) + (inst[insnr].tunelev & 1));
+}
+
+void Cd00Player::playnote(unsigned char chan)
+{
+ // set misc vars & play
+ opl->write(0xb0 + chan, 0); // stop old note
+ setinst(chan);
+ channel[chan].key = 1;
+ setfreq(chan);
+ setvolume(chan);
+}
+
+void Cd00Player::vibrato(unsigned char chan)
+{
+ if(!channel[chan].vibdepth)
+ return;
+
+ if(channel[chan].trigger)
+ channel[chan].trigger--;
+ else {
+ channel[chan].trigger = channel[chan].vibdepth;
+ channel[chan].vibspeed = -channel[chan].vibspeed;
+ }
+ channel[chan].freq += channel[chan].vibspeed;
+ setfreq(chan);
+}
diff --git a/plugins/adplug/adplug/d00.h b/plugins/adplug/adplug/d00.h
new file mode 100644
index 00000000..50b0de3c
--- /dev/null
+++ b/plugins/adplug/adplug/d00.h
@@ -0,0 +1,109 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * d00.h - D00 Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_D00
+#define H_D00
+
+#include "player.h"
+
+class Cd00Player: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ Cd00Player(Copl *newopl)
+ : CPlayer(newopl), filedata(0)
+ { };
+ ~Cd00Player()
+ { if(filedata) delete [] filedata; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle()
+ { if(version > 1) return std::string(header->songname); else return std::string(); };
+ std::string getauthor()
+ { if(version > 1) return std::string(header->author); else return std::string(); };
+ std::string getdesc()
+ { if(*datainfo) return std::string(datainfo); else return std::string(); };
+ unsigned int getsubsongs();
+
+ protected:
+#pragma pack(1)
+ struct d00header {
+ char id[6];
+ unsigned char type,version,speed,subsongs,soundcard;
+ char songname[32],author[32],dummy[32];
+ unsigned short tpoin,seqptr,instptr,infoptr,spfxptr,endmark;
+ };
+
+ struct d00header1 {
+ unsigned char version,speed,subsongs;
+ unsigned short tpoin,seqptr,instptr,infoptr,lpulptr,endmark;
+ };
+#pragma pack()
+
+ struct {
+ unsigned short *order,ordpos,pattpos,del,speed,rhcnt,key,freq,inst,
+ spfx,ispfx,irhcnt;
+ signed short transpose,slide,slideval,vibspeed;
+ unsigned char seqend,vol,vibdepth,fxdel,modvol,cvol,levpuls,
+ frameskip,nextnote,note,ilevpuls,trigger,fxflag;
+ } channel[9];
+
+ struct Sinsts {
+ unsigned char data[11],tunelev,timer,sr,dummy[2];
+ } *inst;
+
+ struct Sspfx {
+ unsigned short instnr;
+ signed char halfnote;
+ unsigned char modlev;
+ signed char modlevadd;
+ unsigned char duration;
+ unsigned short ptr;
+ } *spfx;
+
+ struct Slevpuls {
+ unsigned char level;
+ signed char voladd;
+ unsigned char duration,ptr;
+ } *levpuls;
+
+ unsigned char songend,version;
+ char *datainfo;
+ unsigned short *seqptr;
+ d00header *header;
+ d00header1 *header1;
+ char *filedata;
+
+ private:
+ void setvolume(unsigned char chan);
+ void setfreq(unsigned char chan);
+ void setinst(unsigned char chan);
+ void playnote(unsigned char chan);
+ void vibrato(unsigned char chan);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/database.cpp b/plugins/adplug/adplug/database.cpp
new file mode 100644
index 00000000..0a2abdad
--- /dev/null
+++ b/plugins/adplug/adplug/database.cpp
@@ -0,0 +1,426 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (c) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * database.cpp - AdPlug database class
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002, 2003, 2006 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <binio.h>
+#include <binfile.h>
+#include <string.h>
+
+#include "database.h"
+
+#define DB_FILEID_V10 "AdPlug Module Information Database 1.0\x10"
+
+/***** CAdPlugDatabase *****/
+
+const unsigned short CAdPlugDatabase::hash_radix = 0xfff1; // should be prime
+
+CAdPlugDatabase::CAdPlugDatabase()
+ : linear_index(0), linear_logic_length(0), linear_length(0)
+{
+ db_linear = new DB_Bucket * [hash_radix];
+ db_hashed = new DB_Bucket * [hash_radix];
+ memset(db_linear, 0, sizeof(DB_Bucket *) * hash_radix);
+ memset(db_hashed, 0, sizeof(DB_Bucket *) * hash_radix);
+}
+
+CAdPlugDatabase::~CAdPlugDatabase()
+{
+ unsigned long i;
+
+ for(i = 0; i < linear_length; i++)
+ delete db_linear[i];
+
+ delete [] db_linear;
+ delete [] db_hashed;
+}
+
+bool CAdPlugDatabase::load(std::string db_name)
+{
+ binifstream f(db_name);
+ if(f.error()) return false;
+ return load(f);
+}
+
+bool CAdPlugDatabase::load(binistream &f)
+{
+ unsigned int idlen = strlen(DB_FILEID_V10);
+ char *id = new char [idlen];
+ unsigned long length;
+
+ // Open database as little endian with IEEE floats
+ f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
+
+ f.readString(id,idlen);
+ if(memcmp(id,DB_FILEID_V10,idlen)) {
+ delete [] id;
+ return false;
+ }
+ delete [] id;
+ length = f.readInt(4);
+
+ // read records
+ for(unsigned long i = 0; i < length; i++)
+ insert(CRecord::factory(f));
+
+ return true;
+}
+
+bool CAdPlugDatabase::save(std::string db_name)
+{
+ binofstream f(db_name);
+ if(f.error()) return false;
+ return save(f);
+}
+
+bool CAdPlugDatabase::save(binostream &f)
+{
+ unsigned long i;
+
+ // Save database as little endian with IEEE floats
+ f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
+
+ f.writeString(DB_FILEID_V10);
+ f.writeInt(linear_logic_length, 4);
+
+ // write records
+ for(i = 0; i < linear_length; i++)
+ if(!db_linear[i]->deleted)
+ db_linear[i]->record->write(f);
+
+ return true;
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::search(CKey const &key)
+{
+ if(lookup(key)) return get_record(); else return 0;
+}
+
+bool CAdPlugDatabase::lookup(CKey const &key)
+{
+ unsigned long index = make_hash(key);
+ if(!db_hashed[index]) return false;
+
+ // immediate hit ?
+ DB_Bucket *bucket = db_hashed[index];
+
+ if(!bucket->deleted && bucket->record->key == key) {
+ linear_index = bucket->index;
+ return true;
+ }
+
+ // in-chain hit ?
+ bucket = db_hashed[index]->chain;
+
+ while(bucket) {
+ if(!bucket->deleted && bucket->record->key == key) {
+ linear_index = bucket->index;
+ return true;
+ }
+
+ bucket = bucket->chain;
+ }
+
+ return false;
+}
+
+bool CAdPlugDatabase::insert(CRecord *record)
+{
+ long index;
+
+ // sanity checks
+ if(!record) return false; // null-pointer given
+ if(linear_length == hash_radix) return false; // max. db size exceeded
+ if(lookup(record->key)) return false; // record already in db
+
+ // make bucket
+ DB_Bucket *bucket = new DB_Bucket(linear_length, record);
+ if(!bucket) return false;
+
+ // add to linear list
+ db_linear[linear_length] = bucket;
+ linear_logic_length++; linear_length++;
+
+ // add to hashed list
+ index = make_hash(record->key);
+
+ if(!db_hashed[index]) // First entry in hashtable
+ db_hashed[index] = bucket;
+ else { // Add entry in chained list
+ DB_Bucket *chain = db_hashed[index];
+
+ while(chain->chain) chain = chain->chain;
+ chain->chain = bucket;
+ }
+
+ return true;
+}
+
+void CAdPlugDatabase::wipe(CRecord *record)
+{
+ if(!lookup(record->key)) return;
+ wipe();
+}
+
+void CAdPlugDatabase::wipe()
+{
+ if(!linear_length) return;
+
+ DB_Bucket *bucket = db_linear[linear_index];
+
+ if(!bucket->deleted) {
+ delete bucket->record;
+ linear_logic_length--;
+ bucket->deleted = true;
+ }
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::get_record()
+{
+ if(!linear_length) return 0;
+ return db_linear[linear_index]->record;
+}
+
+bool CAdPlugDatabase::go_forward()
+{
+ if(linear_index + 1 < linear_length) {
+ linear_index++;
+ return true;
+ } else
+ return false;
+}
+
+bool CAdPlugDatabase::go_backward()
+{
+ if(!linear_index) return false;
+ linear_index--;
+ return true;
+}
+
+void CAdPlugDatabase::goto_begin()
+{
+ if(linear_length) linear_index = 0;
+}
+
+void CAdPlugDatabase::goto_end()
+{
+ if(linear_length) linear_index = linear_length - 1;
+}
+
+inline unsigned long CAdPlugDatabase::make_hash(CKey const &key)
+{
+ return (key.crc32 + key.crc16) % hash_radix;
+}
+
+/***** CAdPlugDatabase::DB_Bucket *****/
+
+CAdPlugDatabase::DB_Bucket::DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain)
+ : index(nindex), deleted(false), chain(newchain), record(newrecord)
+{
+}
+
+CAdPlugDatabase::DB_Bucket::~DB_Bucket()
+{
+ if(!deleted) delete record;
+}
+
+/***** CAdPlugDatabase::CRecord *****/
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(RecordType type)
+{
+ switch(type) {
+ case Plain: return new CPlainRecord;
+ case SongInfo: return new CInfoRecord;
+ case ClockSpeed: return new CClockRecord;
+ default: return 0;
+ }
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(binistream &in)
+{
+ RecordType type;
+ unsigned long size;
+ CRecord *rec;
+
+ type = (RecordType)in.readInt(1); size = in.readInt(4);
+ rec = factory(type);
+
+ if(rec) {
+ rec->key.crc16 = in.readInt(2); rec->key.crc32 = in.readInt(4);
+ rec->filetype = in.readString('\0'); rec->comment = in.readString('\0');
+ rec->read_own(in);
+ return rec;
+ } else {
+ // skip this record, cause we don't know about it
+ in.seek(size, binio::Add);
+ return 0;
+ }
+}
+
+void CAdPlugDatabase::CRecord::write(binostream &out)
+{
+ out.writeInt(type, 1);
+ out.writeInt(get_size() + filetype.length() + comment.length() + 8, 4);
+ out.writeInt(key.crc16, 2); out.writeInt(key.crc32, 4);
+ out.writeString(filetype); out.writeInt('\0', 1);
+ out.writeString(comment); out.writeInt('\0', 1);
+
+ write_own(out);
+}
+
+bool CAdPlugDatabase::CRecord::user_read(std::istream &in, std::ostream &out)
+{
+ return user_read_own(in, out);
+}
+
+bool CAdPlugDatabase::CRecord::user_write(std::ostream &out)
+{
+ out << "Record type: ";
+ switch(type) {
+ case Plain: out << "Plain"; break;
+ case SongInfo: out << "SongInfo"; break;
+ case ClockSpeed: out << "ClockSpeed"; break;
+ default: out << "*** Unknown ***"; break;
+ }
+ out << std::endl;
+ out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl;
+ out << "File type: " << filetype << std::endl;
+ out << "Comment: " << comment << std::endl;
+
+ return user_write_own(out);
+}
+
+/***** CAdPlugDatabase::CRecord::CKey *****/
+
+CAdPlugDatabase::CKey::CKey(binistream &buf)
+{
+ make(buf);
+}
+
+bool CAdPlugDatabase::CKey::operator==(const CKey &key)
+{
+ return ((crc16 == key.crc16) && (crc32 == key.crc32));
+}
+
+void CAdPlugDatabase::CKey::make(binistream &buf)
+// Key is CRC16:CRC32 pair. CRC16 and CRC32 calculation routines (c) Zhengxi
+{
+ static const unsigned short magic16 = 0xa001;
+ static const unsigned long magic32 = 0xedb88320;
+
+ crc16 = 0; crc32 = ~0;
+
+ while(!buf.eof())
+ {
+ unsigned char byte = buf.readInt(1);
+
+ for (int j=0;j<8;j++)
+ {
+ if ((crc16 ^ byte) & 1)
+ crc16 = (crc16 >> 1) ^ magic16;
+ else
+ crc16 >>= 1;
+
+ if ((crc32 ^ byte) & 1)
+ crc32 = (crc32 >> 1) ^ magic32;
+ else
+ crc32 >>= 1;
+
+ byte >>= 1;
+ }
+ }
+
+ crc16 &= 0xffff;
+ crc32 = ~crc32;
+}
+
+/***** CInfoRecord *****/
+
+CInfoRecord::CInfoRecord()
+{
+ type = SongInfo;
+}
+
+void CInfoRecord::read_own(binistream &in)
+{
+ title = in.readString('\0');
+ author = in.readString('\0');
+}
+
+void CInfoRecord::write_own(binostream &out)
+{
+ out.writeString(title); out.writeInt('\0', 1);
+ out.writeString(author); out.writeInt('\0', 1);
+}
+
+unsigned long CInfoRecord::get_size()
+{
+ return title.length() + author.length() + 2;
+}
+
+bool CInfoRecord::user_read_own(std::istream &in, std::ostream &out)
+{
+ out << "Title: "; in >> title;
+ out << "Author: "; in >> author;
+ return true;
+}
+
+bool CInfoRecord::user_write_own(std::ostream &out)
+{
+ out << "Title: " << title << std::endl;
+ out << "Author: " << author << std::endl;
+ return true;
+}
+
+/***** CClockRecord *****/
+
+CClockRecord::CClockRecord()
+ : clock(0.0f)
+{
+ type = ClockSpeed;
+}
+
+void CClockRecord::read_own(binistream &in)
+{
+ clock = in.readFloat(binio::Single);
+}
+
+void CClockRecord::write_own(binostream &out)
+{
+ out.writeFloat(clock, binio::Single);
+}
+
+unsigned long CClockRecord::get_size()
+{
+ return 4;
+}
+
+bool CClockRecord::user_read_own(std::istream &in, std::ostream &out)
+{
+ out << "Clockspeed: "; in >> clock;
+ return true;
+}
+
+bool CClockRecord::user_write_own(std::ostream &out)
+{
+ out << "Clock speed: " << clock << " Hz" << std::endl;
+ return true;
+}
diff --git a/plugins/adplug/adplug/database.h b/plugins/adplug/adplug/database.h
new file mode 100644
index 00000000..050e8959
--- /dev/null
+++ b/plugins/adplug/adplug/database.h
@@ -0,0 +1,169 @@
+/*
+ * 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
+ *
+ * database.h - AdPlug database class
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_DATABASE
+#define H_ADPLUG_DATABASE
+
+#include <iostream>
+#include <string>
+#include <binio.h>
+
+class CAdPlugDatabase
+{
+public:
+ class CKey
+ {
+ public:
+ unsigned short crc16;
+ unsigned long crc32;
+
+ CKey() {};
+ CKey(binistream &in);
+
+ bool operator==(const CKey &key);
+
+ private:
+ void make(binistream &in);
+ };
+
+ class CRecord
+ {
+ public:
+ typedef enum { Plain, SongInfo, ClockSpeed } RecordType;
+
+ RecordType type;
+ CKey key;
+ std::string filetype, comment;
+
+ static CRecord *factory(RecordType type);
+ static CRecord *factory(binistream &in);
+
+ CRecord() {}
+ virtual ~CRecord() {}
+
+ void write(binostream &out);
+
+ bool user_read(std::istream &in, std::ostream &out);
+ bool user_write(std::ostream &out);
+
+ protected:
+ virtual void read_own(binistream &in) = 0;
+ virtual void write_own(binostream &out) = 0;
+ virtual unsigned long get_size() = 0;
+ virtual bool user_read_own(std::istream &in, std::ostream &out) = 0;
+ virtual bool user_write_own(std::ostream &out) = 0;
+ };
+
+ CAdPlugDatabase();
+ ~CAdPlugDatabase();
+
+ bool load(std::string db_name);
+ bool load(binistream &f);
+ bool save(std::string db_name);
+ bool save(binostream &f);
+
+ bool insert(CRecord *record);
+
+ void wipe(CRecord *record);
+ void wipe();
+
+ CRecord *search(CKey const &key);
+ bool lookup(CKey const &key);
+
+ CRecord *get_record();
+
+ bool go_forward();
+ bool go_backward();
+
+ void goto_begin();
+ void goto_end();
+
+private:
+ static const unsigned short hash_radix;
+
+ class DB_Bucket
+ {
+ public:
+ unsigned long index;
+ bool deleted;
+ DB_Bucket *chain;
+
+ CRecord *record;
+
+ DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain = 0);
+ ~DB_Bucket();
+ };
+
+ DB_Bucket **db_linear;
+ DB_Bucket **db_hashed;
+
+ unsigned long linear_index, linear_logic_length, linear_length;
+
+ unsigned long make_hash(CKey const &key);
+};
+
+class CPlainRecord: public CAdPlugDatabase::CRecord
+{
+public:
+ CPlainRecord() { type = Plain; }
+
+protected:
+ virtual void read_own(binistream &in) {}
+ virtual void write_own(binostream &out) {}
+ virtual unsigned long get_size() { return 0; }
+ virtual bool user_read_own(std::istream &in, std::ostream &out) { return true; }
+ virtual bool user_write_own(std::ostream &out) { return true; }
+};
+
+class CInfoRecord: public CAdPlugDatabase::CRecord
+{
+public:
+ std::string title;
+ std::string author;
+
+ CInfoRecord();
+
+protected:
+ virtual void read_own(binistream &in);
+ virtual void write_own(binostream &out);
+ virtual unsigned long get_size();
+ virtual bool user_read_own(std::istream &in, std::ostream &out);
+ virtual bool user_write_own(std::ostream &out);
+};
+
+class CClockRecord: public CAdPlugDatabase::CRecord
+{
+public:
+ float clock;
+
+ CClockRecord();
+
+protected:
+ virtual void read_own(binistream &in);
+ virtual void write_own(binostream &out);
+ virtual unsigned long get_size();
+ virtual bool user_read_own(std::istream &in, std::ostream &out);
+ virtual bool user_write_own(std::ostream &out);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/debug.c b/plugins/adplug/adplug/debug.c
new file mode 100644
index 00000000..bd6598da
--- /dev/null
+++ b/plugins/adplug/adplug/debug.c
@@ -0,0 +1,57 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 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
+ *
+ * debug.h - AdPlug Debug Logger
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifdef DEBUG
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static FILE *log = NULL;
+
+void AdPlug_LogFile(const char *filename)
+{
+ if(log) fclose(log);
+ log = fopen(filename,"wt");
+}
+
+void AdPlug_LogWrite(const char *fmt, ...)
+{
+ va_list argptr;
+
+ va_start(argptr, fmt);
+
+ if(log) {
+ vfprintf(log, fmt, argptr);
+ fflush(log);
+ } else
+ vfprintf(stderr, fmt, argptr);
+
+ va_end(argptr);
+}
+
+#else
+
+void AdPlug_LogFile(char *filename) { }
+void AdPlug_LogWrite(char *fmt, ...) { }
+
+#endif
diff --git a/plugins/adplug/adplug/debug.h b/plugins/adplug/adplug/debug.h
new file mode 100644
index 00000000..e5832f49
--- /dev/null
+++ b/plugins/adplug/adplug/debug.h
@@ -0,0 +1,41 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 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
+ *
+ * debug.h - AdPlug Debug Logger
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002 Simon Peter <dn.tlp@gmx.net>
+ *
+ * NOTES:
+ * This debug logger is used throughout AdPlug to log debug output to stderr
+ * (the default) or to a user-specified logfile.
+ *
+ * To use it, AdPlug has to be compiled with debug logging support enabled.
+ * This is done by defining the DEBUG macro with every source-file. The
+ * LogFile() function can be used to specify a logfile to write to.
+ */
+
+#ifndef H_DEBUG
+#define H_DEBUG
+
+extern "C"
+{
+ void AdPlug_LogFile(const char *filename);
+ void AdPlug_LogWrite(const char *fmt, ...);
+}
+
+#endif
diff --git a/plugins/adplug/adplug/dfm.cpp b/plugins/adplug/adplug/dfm.cpp
new file mode 100644
index 00000000..4a70a032
--- /dev/null
+++ b/plugins/adplug/adplug/dfm.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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
+ *
+ * dfm.cpp - Digital-FM Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "dfm.h"
+#include "debug.h"
+
+CPlayer *CdfmLoader::factory(Copl *newopl)
+{
+ return new CdfmLoader(newopl);
+}
+
+bool CdfmLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned char npats,n,note,fx,c,r,param;
+ unsigned int i;
+ const unsigned char convfx[8] = {255,255,17,19,23,24,255,13};
+
+ // file validation
+ f->readString(header.id, 4);
+ header.hiver = f->readInt(1); header.lover = f->readInt(1);
+ if(strncmp(header.id,"DFM\x1a",4) || header.hiver > 1)
+ { fp.close(f); return false; }
+
+ // load
+ restartpos = 0; flags = Standard; bpm = 0;
+ init_trackord();
+ f->readString(songinfo, 33);
+ initspeed = f->readInt(1);
+ for(i = 0; i < 32; i++)
+ f->readString(instname[i], 12);
+ for(i = 0; i < 32; i++) {
+ inst[i].data[1] = f->readInt(1);
+ inst[i].data[2] = f->readInt(1);
+ inst[i].data[9] = f->readInt(1);
+ inst[i].data[10] = f->readInt(1);
+ inst[i].data[3] = f->readInt(1);
+ inst[i].data[4] = f->readInt(1);
+ inst[i].data[5] = f->readInt(1);
+ inst[i].data[6] = f->readInt(1);
+ inst[i].data[7] = f->readInt(1);
+ inst[i].data[8] = f->readInt(1);
+ inst[i].data[0] = f->readInt(1);
+ }
+ for(i = 0; i < 128; i++) order[i] = f->readInt(1);
+ for(i = 0; i < 128 && order[i] != 128; i++) ; length = i;
+ npats = f->readInt(1);
+ for(i = 0; i < npats; i++) {
+ n = f->readInt(1);
+ for(r = 0; r < 64; r++)
+ for(c = 0; c < 9; c++) {
+ note = f->readInt(1);
+ if((note & 15) == 15)
+ tracks[n*9+c][r].note = 127; // key off
+ else
+ tracks[n*9+c][r].note = ((note & 127) >> 4) * 12 + (note & 15);
+ if(note & 128) { // additional effect byte
+ fx = f->readInt(1);
+ if(fx >> 5 == 1)
+ tracks[n*9+c][r].inst = (fx & 31) + 1;
+ else {
+ tracks[n*9+c][r].command = convfx[fx >> 5];
+ if(tracks[n*9+c][r].command == 17) { // set volume
+ param = fx & 31;
+ param = 63 - param * 2;
+ tracks[n*9+c][r].param1 = param >> 4;
+ tracks[n*9+c][r].param2 = param & 15;
+ } else {
+ tracks[n*9+c][r].param1 = (fx & 31) >> 4;
+ tracks[n*9+c][r].param2 = fx & 15;
+ }
+ }
+ }
+
+ }
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+std::string CdfmLoader::gettype()
+{
+ char tmpstr[20];
+
+ sprintf(tmpstr,"Digital-FM %d.%d",header.hiver,header.lover);
+ return std::string(tmpstr);
+}
+
+float CdfmLoader::getrefresh()
+{
+ return 125.0f;
+}
diff --git a/plugins/adplug/adplug/dfm.h b/plugins/adplug/adplug/dfm.h
new file mode 100644
index 00000000..4212b2fe
--- /dev/null
+++ b/plugins/adplug/adplug/dfm.h
@@ -0,0 +1,52 @@
+/*
+ * 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
+ *
+ * dfm.h - Digital-FM Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CdfmLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdfmLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype();
+ unsigned int getinstruments()
+ { return 32; };
+ std::string getinstrument(unsigned int n)
+ { if(*instname[n]) return std::string(instname[n],1,*instname[n]); else return std::string(); };
+ std::string getdesc()
+ { return std::string(songinfo,1,*songinfo); };
+
+private:
+ struct {
+ char id[4];
+ unsigned char hiver,lover;
+ } header;
+
+ char songinfo[33];
+ char instname[32][12];
+};
diff --git a/plugins/adplug/adplug/diskopl.cpp b/plugins/adplug/adplug/diskopl.cpp
new file mode 100644
index 00000000..3296c0a1
--- /dev/null
+++ b/plugins/adplug/adplug/diskopl.cpp
@@ -0,0 +1,90 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * diskopl.cpp - Disk Writer OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "diskopl.h"
+
+//static const unsigned short note_table[12] = {363,385,408,432,458,485,514,544,577,611,647,686};
+const unsigned char CDiskopl::op_table[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
+
+CDiskopl::CDiskopl(std::string filename)
+ : old_freq(0.0f), del(1), nowrite(false)
+{
+ unsigned short clock = 0xffff;
+
+ currType = TYPE_OPL3;
+ f = fopen(filename.c_str(),"wb");
+ fwrite("RAWADATA",8,1,f);
+ fwrite(&clock,sizeof(clock),1,f);
+}
+
+CDiskopl::~CDiskopl()
+{
+ fclose(f);
+}
+
+void CDiskopl::update(CPlayer *p)
+{
+ unsigned short clock;
+ unsigned int wait;
+
+ if(p->getrefresh() != old_freq) {
+ old_freq = p->getrefresh();
+ del = wait = (unsigned int)(18.2f / old_freq);
+ clock = (unsigned short)(1192737/(old_freq*(wait+1)));
+ fputc(0,f); fputc(2,f);
+ fwrite(&clock,2,1,f);
+ }
+ if(!nowrite) {
+ fputc(del+1,f);
+ fputc(0,f);
+ }
+}
+
+void CDiskopl::setchip(int n)
+{
+ Copl::setchip(n);
+
+ if(!nowrite) {
+ fputc(currChip + 1, f);
+ fputc(2, f);
+ }
+}
+
+void CDiskopl::write(int reg, int val)
+{
+ if(!nowrite)
+ diskwrite(reg,val);
+}
+
+void CDiskopl::init()
+{
+ for (int i=0;i<9;i++) { // stop instruments
+ diskwrite(0xb0 + i,0); // key off
+ diskwrite(0x80 + op_table[i],0xff); // fastest release
+ }
+ diskwrite(0xbd,0); // clear misc. register
+}
+
+void CDiskopl::diskwrite(int reg, int val)
+{
+ fputc(val,f);
+ fputc(reg,f);
+}
diff --git a/plugins/adplug/adplug/diskopl.h b/plugins/adplug/adplug/diskopl.h
new file mode 100644
index 00000000..929f2906
--- /dev/null
+++ b/plugins/adplug/adplug/diskopl.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * diskopl.h - Disk Writer OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string>
+#include <stdio.h>
+#include "opl.h"
+#include "player.h"
+
+class CDiskopl: public Copl
+{
+ public:
+ CDiskopl(std::string filename);
+ virtual ~CDiskopl();
+
+ void update(CPlayer *p); // write to file
+ void setnowrite(bool nw = true) // set file write status
+ { nowrite = nw; };
+
+ void setchip(int n);
+
+ // template methods
+ void write(int reg, int val);
+ void init();
+
+ private:
+ static const unsigned char op_table[9];
+
+ FILE *f;
+ float old_freq;
+ unsigned char del;
+ bool nowrite; // don't write to file, if true
+
+ void diskwrite(int reg, int val);
+};
diff --git a/plugins/adplug/adplug/dmo.cpp b/plugins/adplug/adplug/dmo.cpp
new file mode 100644
index 00000000..c25293c4
--- /dev/null
+++ b/plugins/adplug/adplug/dmo.cpp
@@ -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., 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,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;
+}
diff --git a/plugins/adplug/adplug/dmo.h b/plugins/adplug/adplug/dmo.h
new file mode 100644
index 00000000..8591cfcc
--- /dev/null
+++ b/plugins/adplug/adplug/dmo.h
@@ -0,0 +1,51 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 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., 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,
+ unsigned long outputsize);
+
+ private:
+ unsigned short brand(unsigned short range);
+ short unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf);
+
+ unsigned long bseed;
+ unsigned char *oend;
+ };
+};
diff --git a/plugins/adplug/adplug/dro.cpp b/plugins/adplug/adplug/dro.cpp
new file mode 100644
index 00000000..eb97c0f8
--- /dev/null
+++ b/plugins/adplug/adplug/dro.cpp
@@ -0,0 +1,130 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * dro.c - DOSBox Raw OPL Player by Sjoerd van der Berg <harekiet@zophar.net>
+ *
+ * upgraded by matthew gambrell <zeromus@zeromus.org>
+ *
+ * NOTES: 3-oct-04: the DRO format is not yet finalized. beware.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "dro.h"
+
+/*** public methods *************************************/
+
+CPlayer *CdroPlayer::factory(Copl *newopl)
+{
+ return new CdroPlayer(newopl);
+}
+
+CdroPlayer::CdroPlayer(Copl *newopl)
+ : CPlayer(newopl), data(0)
+{
+ if(opl->gettype() == Copl::TYPE_OPL2)
+ opl3_mode = 0;
+ else
+ opl3_mode = 1;
+}
+
+bool CdroPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[8];
+ unsigned long i;
+
+ // file validation section
+ f->readString(id, 8);
+ if(strncmp(id,"DBRAWOPL",8)) { fp.close (f); return false; }
+ int version = f->readInt(4); // not very useful just yet
+ if(version != 0x10000) { fp.close(f); return false; }
+
+ // load section
+ mstotal = f->readInt(4); // Total milliseconds in file
+ length = f->readInt(4); // Total data bytes in file
+ f->ignore(1); // Type of opl data this can contain - ignored
+ data = new unsigned char [length];
+ for (i=0;i<length;i++)
+ data[i]=f->readInt(1);
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CdroPlayer::update()
+{
+ if (delay>500) {
+ delay-=500;
+ return true;
+ } else
+ delay=0;
+
+ while (pos < length) {
+ unsigned char cmd = data[pos++];
+ switch(cmd) {
+ case 0:
+ delay = 1 + data[pos++];
+ return true;
+ case 1:
+ delay = 1 + data[pos] + (data[pos+1]<<8);
+ pos+=2;
+ return true;
+ case 2:
+ index = 0;
+ opl->setchip(0);
+ break;
+ case 3:
+ index = 1;
+ opl->setchip(1);
+ break;
+ default:
+ if(cmd==4) cmd = data[pos++]; //data override
+ if(index == 0 || opl3_mode)
+ opl->write(cmd,data[pos++]);
+ break;
+ }
+ }
+
+ return pos<length;
+}
+
+void CdroPlayer::rewind(int subsong)
+{
+ delay=1;
+ pos = index = 0;
+ opl->init();
+
+ //dro assumes all registers are initialized to 0
+ //registers not initialized to 0 will be corrected
+ //in the data stream
+ for(int i=0;i<256;i++)
+ opl->write(i,0);
+
+ opl->setchip(1);
+ for(int i=0;i<256;i++)
+ opl->write(i,0);
+ opl->setchip(0);
+}
+
+float CdroPlayer::getrefresh()
+{
+ if (delay > 500) return 1000 / 500;
+ else return 1000 / (double)delay;
+}
diff --git a/plugins/adplug/adplug/dro.h b/plugins/adplug/adplug/dro.h
new file mode 100644
index 00000000..0f59e7b1
--- /dev/null
+++ b/plugins/adplug/adplug/dro.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * dro.h - DOSBox Raw OPL Player by Sjoerd van der Berg <harekiet@zophar.net>
+ */
+
+#include "player.h"
+
+class CdroPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdroPlayer(Copl *newopl);
+ ~CdroPlayer()
+ {
+ if(data)
+ delete [] data;
+ }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ {
+ return std::string("DOSBox Raw OPL");
+ }
+
+ protected:
+ unsigned char *data;
+ unsigned long pos,length;
+ unsigned long msdone,mstotal;
+ unsigned short delay;
+ unsigned char index, opl3_mode;
+};
diff --git a/plugins/adplug/adplug/dtm.cpp b/plugins/adplug/adplug/dtm.cpp
new file mode 100644
index 00000000..3d022ae8
--- /dev/null
+++ b/plugins/adplug/adplug/dtm.cpp
@@ -0,0 +1,317 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ dtm.cpp - DTM loader by Riven the Mage <riven@ok.ru>
+*/
+/*
+ NOTE: Panning (Ex) effect is ignored.
+*/
+
+#include <string.h>
+#include "dtm.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CdtmLoader::factory(Copl *newopl)
+{
+ return new CdtmLoader(newopl);
+}
+
+bool CdtmLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_inst[11] = { 2,1,10,9,4,3,6,5,0,8,7 };
+ const unsigned short conv_note[12] = { 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE };
+ int i,j,k,t=0;
+
+ // read header
+ f->readString(header.id, 12);
+ header.version = f->readInt(1);
+ f->readString(header.title, 20); f->readString(header.author, 20);
+ header.numpat = f->readInt(1); header.numinst = f->readInt(1);
+
+ // signature exists ? good version ?
+ if(memcmp(header.id,"DeFy DTM ",9) || header.version != 0x10)
+ { fp.close (f); return false; }
+
+ header.numinst++;
+
+ // load description
+ memset(desc,0,80*16);
+
+ char bufstr[80];
+
+ for (i=0;i<16;i++)
+ {
+ // get line length
+ unsigned char bufstr_length = f->readInt(1);
+
+ if(bufstr_length > 80) {
+ fp.close(f);
+ return false;
+ }
+
+ // read line
+ if (bufstr_length)
+ {
+ f->readString(bufstr,bufstr_length);
+
+ for (j=0;j<bufstr_length;j++)
+ if (!bufstr[j])
+ bufstr[j] = 0x20;
+
+ bufstr[bufstr_length] = 0;
+
+ strcat(desc,bufstr);
+ }
+
+ strcat(desc,"\n");
+ }
+
+ // init CmodPlayer
+ realloc_instruments(header.numinst);
+ realloc_order(100);
+ realloc_patterns(header.numpat,64,9);
+ init_notetable(conv_note);
+ init_trackord();
+
+ // load instruments
+ for (i=0;i<header.numinst;i++)
+ {
+ unsigned char name_length = f->readInt(1);
+
+ if (name_length)
+ f->readString(instruments[i].name, name_length);
+
+ instruments[i].name[name_length] = 0;
+
+ for(j = 0; j < 12; j++)
+ instruments[i].data[j] = f->readInt(1);
+
+ for (j=0;j<11;j++)
+ inst[i].data[conv_inst[j]] = instruments[i].data[j];
+ }
+
+ // load order
+ for(i = 0; i < 100; i++) order[i] = f->readInt(1);
+
+ nop = header.numpat;
+
+ unsigned char *pattern = new unsigned char [0x480];
+
+ // load tracks
+ for (i=0;i<nop;i++)
+ {
+ unsigned short packed_length;
+
+ packed_length = f->readInt(2);
+
+ unsigned char *packed_pattern = new unsigned char [packed_length];
+
+ for(j = 0; j < packed_length; j++)
+ packed_pattern[j] = f->readInt(1);
+
+ long unpacked_length = unpack_pattern(packed_pattern,packed_length,pattern,0x480);
+
+ delete [] packed_pattern;
+
+ if (!unpacked_length)
+ {
+ delete pattern;
+ fp.close(f);
+ return false;
+ }
+
+ // convert pattern
+ for (j=0;j<9;j++)
+ {
+ for (k=0;k<64;k++)
+ {
+ dtm_event *event = (dtm_event *)&pattern[(k*9+j)*2];
+
+ // instrument
+ if (event->byte0 == 0x80)
+ {
+ if (event->byte1 <= 0x80)
+ tracks[t][k].inst = event->byte1 + 1;
+ }
+
+ // note + effect
+ else
+ {
+ tracks[t][k].note = event->byte0;
+
+ if ((event->byte0 != 0) && (event->byte0 != 127))
+ tracks[t][k].note++;
+
+ // convert effects
+ switch (event->byte1 >> 4)
+ {
+ case 0x0: // pattern break
+ if ((event->byte1 & 15) == 1)
+ tracks[t][k].command = 13;
+ break;
+
+ case 0x1: // freq. slide up
+ tracks[t][k].command = 28;
+ tracks[t][k].param1 = event->byte1 & 15;
+ break;
+
+ case 0x2: // freq. slide down
+ tracks[t][k].command = 28;
+ tracks[t][k].param2 = event->byte1 & 15;
+ break;
+
+ case 0xA: // set carrier volume
+ case 0xC: // set instrument volume
+ tracks[t][k].command = 22;
+ tracks[t][k].param1 = (0x3F - (event->byte1 & 15)) >> 4;
+ tracks[t][k].param2 = (0x3F - (event->byte1 & 15)) & 15;
+ break;
+
+ case 0xB: // set modulator volume
+ tracks[t][k].command = 21;
+ tracks[t][k].param1 = (0x3F - (event->byte1 & 15)) >> 4;
+ tracks[t][k].param2 = (0x3F - (event->byte1 & 15)) & 15;
+ break;
+
+ case 0xE: // set panning
+ break;
+
+ case 0xF: // set speed
+ tracks[t][k].command = 13;
+ tracks[t][k].param2 = event->byte1 & 15;
+ break;
+ }
+ }
+ }
+
+ t++;
+ }
+ }
+
+ delete [] pattern;
+ fp.close(f);
+
+ // order length
+ for (i=0;i<100;i++)
+ {
+ if (order[i] >= 0x80)
+ {
+ length = i;
+
+ if (order[i] == 0xFF)
+ restartpos = 0;
+ else
+ restartpos = order[i] - 0x80;
+
+ break;
+ }
+ }
+
+ // initial speed
+ initspeed = 2;
+
+ rewind(0);
+
+ return true;
+}
+
+void CdtmLoader::rewind(int subsong)
+{
+ CmodPlayer::rewind(subsong);
+
+ // default instruments
+ for (int i=0;i<9;i++)
+ {
+ channel[i].inst = i;
+
+ channel[i].vol1 = 63 - (inst[i].data[10] & 63);
+ channel[i].vol2 = 63 - (inst[i].data[9] & 63);
+ }
+}
+
+float CdtmLoader::getrefresh()
+{
+ return 18.2f;
+}
+
+std::string CdtmLoader::gettype()
+{
+ return std::string("DeFy Adlib Tracker");
+}
+
+std::string CdtmLoader::gettitle()
+{
+ return std::string(header.title);
+}
+
+std::string CdtmLoader::getauthor()
+{
+ return std::string(header.author);
+}
+
+std::string CdtmLoader::getdesc()
+{
+ return std::string(desc);
+}
+
+std::string CdtmLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name);
+}
+
+unsigned int CdtmLoader::getinstruments()
+{
+ return header.numinst;
+}
+
+/* -------- Private Methods ------------------------------- */
+
+long CdtmLoader::unpack_pattern(unsigned char *ibuf, long ilen, unsigned char *obuf, long olen)
+{
+ unsigned char *input = ibuf;
+ unsigned char *output = obuf;
+
+ long input_length = 0;
+ long output_length = 0;
+
+ unsigned char repeat_byte, repeat_counter;
+
+ // RLE
+ while (input_length < ilen)
+ {
+ repeat_byte = input[input_length++];
+
+ if ((repeat_byte & 0xF0) == 0xD0)
+ {
+ repeat_counter = repeat_byte & 15;
+ repeat_byte = input[input_length++];
+ }
+ else
+ repeat_counter = 1;
+
+ for (int i=0;i<repeat_counter;i++)
+ {
+ if (output_length < olen)
+ output[output_length++] = repeat_byte;
+ }
+ }
+
+ return output_length;
+}
diff --git a/plugins/adplug/adplug/dtm.h b/plugins/adplug/adplug/dtm.h
new file mode 100644
index 00000000..8429bc4f
--- /dev/null
+++ b/plugins/adplug/adplug/dtm.h
@@ -0,0 +1,69 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ dtm.h - DTM loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CdtmLoader: public CmodPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CdtmLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getauthor();
+ std::string getdesc();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+ private:
+
+ struct dtm_header
+ {
+ char id[12];
+ unsigned char version;
+ char title[20];
+ char author[20];
+ unsigned char numpat;
+ unsigned char numinst;
+ } header;
+
+ char desc[80*16];
+
+ struct dtm_instrument
+ {
+ char name[13];
+ unsigned char data[12];
+ } instruments[128];
+
+ struct dtm_event
+ {
+ unsigned char byte0;
+ unsigned char byte1;
+ };
+
+ long unpack_pattern(unsigned char *ibuf, long ilen, unsigned char *obuf, long olen);
+};
diff --git a/plugins/adplug/adplug/emuopl.cpp b/plugins/adplug/adplug/emuopl.cpp
new file mode 100644
index 00000000..0f751e16
--- /dev/null
+++ b/plugins/adplug/adplug/emuopl.cpp
@@ -0,0 +1,147 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * emuopl.cpp - Emulated OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "emuopl.h"
+
+CEmuopl::CEmuopl(int rate, bool bit16, bool usestereo)
+ : use16bit(bit16), stereo(usestereo), mixbufSamples(0)
+{
+ opl[0] = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
+ opl[1] = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
+
+ currType = TYPE_DUAL_OPL2;
+
+ init();
+}
+
+CEmuopl::~CEmuopl()
+{
+ OPLDestroy(opl[0]); OPLDestroy(opl[1]);
+
+ if(mixbufSamples) {
+ delete [] mixbuf0;
+ delete [] mixbuf1;
+ }
+}
+
+void CEmuopl::update(short *buf, int samples)
+{
+ int i;
+
+ //ensure that our mix buffers are adequately sized
+ if(mixbufSamples < samples) {
+ if(mixbufSamples) { delete[] mixbuf0; delete[] mixbuf1; }
+ mixbufSamples = samples;
+
+ //*2 = make room for stereo, if we need it
+ mixbuf0 = new short[samples*2];
+ mixbuf1 = new short[samples*2];
+ }
+
+ //data should be rendered to outbuf
+ //tempbuf should be used as a temporary buffer
+ //if we are supposed to generate 16bit output,
+ //then outbuf may point directly to the actual waveform output "buf"
+ //if we are supposed to generate 8bit output,
+ //then outbuf cannot point to "buf" (because there will not be enough room)
+ //and so it must point to a mixbuf instead--
+ //it will be reduced to 8bit and put in "buf" later
+ short *outbuf;
+ short *tempbuf=mixbuf0;
+ short *tempbuf2=mixbuf1;
+ if(use16bit) outbuf = buf;
+ else outbuf = mixbuf1;
+ //...there is a potentially confusing situation where mixbuf1 can be aliased.
+ //beware. it is a little loony.
+
+ //all of the following rendering code produces 16bit output
+
+ switch(currType) {
+ case TYPE_OPL2:
+ //for opl2 mode:
+ //render chip0 to the output buffer
+ YM3812UpdateOne(opl[0],outbuf,samples);
+
+ //if we are supposed to output stereo,
+ //then we need to dup the mono channel
+ if(stereo)
+ for(i=samples-1;i>=0;i--) {
+ outbuf[i*2] = outbuf[i];
+ outbuf[i*2+1] = outbuf[i];
+ }
+ break;
+
+ case TYPE_OPL3: // unsupported
+ break;
+
+ case TYPE_DUAL_OPL2:
+ //for dual opl2 mode:
+ //render each chip to a different tempbuffer
+ YM3812UpdateOne(opl[0],tempbuf2,samples);
+ YM3812UpdateOne(opl[1],tempbuf,samples);
+
+ //output stereo:
+ //then we need to interleave the two buffers
+ if(stereo){
+ //first, spread tempbuf's samples across left channel
+ //left channel
+ for(i=0;i<samples;i++)
+ outbuf[i*2] = tempbuf2[i];
+ //next, insert the samples from tempbuf2 into right channel
+ for(i=0;i<samples;i++)
+ outbuf[i*2+1] = tempbuf[i];
+ } else
+ //output mono:
+ //then we need to mix the two buffers into buf
+ for(i=0;i<samples;i++)
+ outbuf[i] = (tempbuf[i]>>1) + (tempbuf2[i]>>1);
+ break;
+ }
+
+ //now reduce to 8bit if we need to
+ if(!use16bit)
+ for(i=0;i<(stereo ? samples*2 : samples);i++)
+ ((char *)buf)[i] = (outbuf[i] >> 8) ^ 0x80;
+}
+
+void CEmuopl::write(int reg, int val)
+{
+ switch(currType){
+ case TYPE_OPL2:
+ case TYPE_DUAL_OPL2:
+ OPLWrite(opl[currChip], 0, reg);
+ OPLWrite(opl[currChip], 1, val);
+ break;
+ case TYPE_OPL3: // unsupported
+ break;
+ }
+}
+
+void CEmuopl::init()
+{
+ OPLResetChip(opl[0]); OPLResetChip(opl[1]);
+ currChip = 0;
+}
+
+void CEmuopl::settype(ChipType type)
+{
+ currType = type;
+}
diff --git a/plugins/adplug/adplug/emuopl.h b/plugins/adplug/adplug/emuopl.h
new file mode 100644
index 00000000..7cd2479b
--- /dev/null
+++ b/plugins/adplug/adplug/emuopl.h
@@ -0,0 +1,49 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * emuopl.h - Emulated OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_EMUOPL
+#define H_ADPLUG_EMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "fmopl.h"
+}
+
+class CEmuopl: public Copl
+{
+ public:
+ CEmuopl(int rate, bool bit16, bool usestereo); // rate = sample rate
+ virtual ~CEmuopl();
+
+ void update(short *buf, int samples); // fill buffer
+ void write(int reg, int val);
+
+ void init();
+ void settype(ChipType type);
+
+ private:
+ bool use16bit, stereo;
+ FM_OPL *opl[2]; // OPL2 emulator data
+ short *mixbuf0, *mixbuf1;
+ int mixbufSamples;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/flash.cpp b/plugins/adplug/adplug/flash.cpp
new file mode 100644
index 00000000..a6bf511c
--- /dev/null
+++ b/plugins/adplug/adplug/flash.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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
+ *
+ * [xad] FLASH player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : LA-INTRO.EXE
+ type : Lunatic Asylum BBStro
+ tune : by Rogue [Logic Design]
+ player : by Flash [Logic Design]
+*/
+
+#include "flash.h"
+#include "debug.h"
+
+const unsigned char CxadflashPlayer::flash_adlib_registers[99] =
+{
+ 0x23, 0x20, 0x43, 0x40, 0x63, 0x60, 0x83, 0x80, 0xC0, 0xE3, 0xE0,
+ 0x24, 0x21, 0x44, 0x41, 0x64, 0x61, 0x84, 0x81, 0xC1, 0xE4, 0xE1,
+ 0x25, 0x22, 0x45, 0x42, 0x65, 0x62, 0x85, 0x82, 0xC2, 0xE5, 0xE2,
+ 0x2B, 0x28, 0x4B, 0x48, 0x6B, 0x68, 0x8B, 0x88, 0xC3, 0xEB, 0xE8,
+ 0x2C, 0x29, 0x4C, 0x49, 0x6C, 0x69, 0x8C, 0x89, 0xC4, 0xEC, 0xE9,
+ 0x2D, 0x2A, 0x4D, 0x4A, 0x6D, 0x6A, 0x8D, 0x8A, 0xC5, 0xED, 0xEA,
+ 0x33, 0x30, 0x53, 0x50, 0x73, 0x70, 0x93, 0x90, 0xC6, 0xF3, 0xF0,
+ 0x34, 0x31, 0x54, 0x51, 0x74, 0x71, 0x94, 0x91, 0xC7, 0xF4, 0xF1,
+ 0x35, 0x32, 0x55, 0x52, 0x75, 0x72, 0x95, 0x92, 0xC8, 0xF5, 0xF2
+};
+
+const unsigned short CxadflashPlayer::flash_notes_encoded[268] =
+{
+ 0x000,
+ 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900, 0xA00, 0xB00, 0xC00,
+ 0x101, 0x201, 0x301, 0x401, 0x501, 0x601, 0x701, 0x801, 0x901, 0xA01, 0xB01, 0xC01,
+ 0x102, 0x202, 0x302, 0x402, 0x502, 0x602, 0x702, 0x802, 0x902, 0xA02, 0xB02, 0xC02,
+ 0x103, 0x203, 0x303, 0x403, 0x503, 0x603, 0x703, 0x803, 0x903, 0xA03, 0xB03, 0xC03,
+ 0x104, 0x204, 0x304, 0x404, 0x504, 0x604, 0x704, 0x804, 0x904, 0xA04, 0xB04, 0xC04,
+ 0x105, 0x205, 0x305, 0x405, 0x505, 0x605, 0x705, 0x805, 0x905, 0xA05, 0xB05, 0xC05,
+ 0x106, 0x206, 0x306, 0x406, 0x506, 0x606, 0x706, 0x806, 0x906, 0xA06, 0xB06, 0xC06,
+ 0x107, 0x207, 0x307, 0x407, 0x507, 0x607, 0x707, 0x807, 0x907, 0xA07, 0xB07, 0xC07,
+ 0x108, 0x208, 0x308, 0x408, 0x508, 0x608, 0x708, 0x808, 0x908, 0xA08, 0xB08, 0xC08,
+ 0x109, 0x209, 0x309, 0x409, 0x509, 0x609, 0x709, 0x809, 0x909, 0xA09, 0xB09, 0xC09,
+ 0x10A, 0x20A, 0x30A, 0x40A, 0x50A, 0x60A, 0x70A, 0x80A, 0x90A, 0xA0A, 0xB0A, 0xC0A,
+ 0x10B, 0x20B, 0x30B, 0x40B, 0x50B, 0x60B, 0x70B, 0x80B, 0x90B, 0xA0B, 0xB0B, 0xC0B,
+ 0x10C, 0x20C, 0x30C, 0x40C, 0x50C, 0x60C, 0x70C, 0x80C, 0x90C, 0xA0C, 0xB0C, 0xC0C,
+ 0x10D, 0x20D, 0x30D, 0x40D, 0x50D, 0x60D, 0x70D, 0x80D, 0x90D, 0xA0D, 0xB0D, 0xC0D,
+ 0x10E, 0x20E, 0x30E, 0x40E, 0x50E, 0x60E, 0x70E, 0x80E, 0x90E, 0xA0E, 0xB0E, 0xC0E,
+ 0x10F, 0x20F, 0x30F, 0x40F, 0x50F, 0x60F, 0x70F, 0x80F, 0x90F, 0xA0F, 0xB0F, 0xC0F,
+ 0x110, 0x210, 0x310, 0x410, 0x510, 0x610, 0x710, 0x810, 0x910, 0xA10, 0xB10, 0xC10,
+ 0x111, 0x211, 0x311, 0x411, 0x511, 0x611, 0x711, 0x811, 0x911, 0xA11, 0xB11, 0xC11,
+ 0x112, 0x212, 0x312, 0x412, 0x512, 0x612, 0x712, 0x812, 0x912, 0xA12, 0xB12, 0xC12,
+ 0x113, 0x213, 0x313, 0x413, 0x513, 0x613, 0x713, 0x813, 0x913, 0xA13, 0xB13, 0xC13,
+ 0x114, 0x214, 0x314, 0x414, 0x514, 0x614, 0x714, 0x814, 0x914, 0xA14, 0xB14, 0xC14,
+ 0x115, 0x215, 0x315
+};
+
+const unsigned short CxadflashPlayer::flash_notes[12] =
+{
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287
+};
+
+const unsigned char CxadflashPlayer::flash_default_instrument[8] =
+{
+ 0x00, 0x00, 0x3F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+CPlayer *CxadflashPlayer::factory(Copl *newopl)
+{
+ return new CxadflashPlayer(newopl);
+}
+
+void CxadflashPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ plr.speed = xad.speed;
+
+ flash.order_pos = 0;
+ flash.pattern_pos = 0;
+
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0x00);
+
+ // assign default instrument
+ for(i=0; i<9; i++)
+ {
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+ }
+
+ // assign instruments
+ for(i=0; i<9; i++)
+ for(int j=0; j<11; j++)
+ opl_write(flash_adlib_registers[i*11+j], tune[i*12+j]);
+}
+
+void CxadflashPlayer::xadplayer_update()
+{
+ unsigned short event_pos = (tune[0x600+flash.order_pos]*1152) + \
+ (flash.pattern_pos*18) + \
+ 0x633;
+
+ for (int i=0; i<9; i++)
+ {
+ unsigned short flash_channel_freq = (adlib[0xB0+i] << 8) + adlib[0xA0+i];
+
+ unsigned char event_b0 = tune[event_pos++];
+ unsigned char event_b1 = tune[event_pos++];
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X, event %02X %02X:\n",i+1,event_b0,event_b1);
+#endif
+
+ if (event_b0 == 0x80) // 0.0x80: Set Instrument
+ {
+ for(int j=0; j<11; j++)
+ opl_write(flash_adlib_registers[i*11+j], tune[event_b1*12+j]);
+ }
+ else
+ {
+ if (event_b1 == 0x01)
+ flash.pattern_pos = 0x3F; // 1.0x01: Pattern Break
+
+ unsigned char fx = (event_b1 >> 4);
+ unsigned char fx_p = (event_b1 & 0x0F);
+
+ switch(fx)
+ {
+ case 0x0A: // 1.0xAy: Set Carrier volume
+ opl_write(flash_adlib_registers[11*i+2], fx_p << 2);
+ break;
+ case 0x0B: // 1.0xBy: Set Modulator volume
+ opl_write(flash_adlib_registers[11*i+3], fx_p << 2);
+ break;
+ case 0x0C: // 1.0xCy: Set both operators volume
+ opl_write(flash_adlib_registers[11*i+2], fx_p << 2);
+ opl_write(flash_adlib_registers[11*i+3], fx_p << 2);
+ break;
+// case 0x0E: // 1.0xEy: ? (increase some value)
+ case 0x0F: // 1.0xFy: Set Speed
+ plr.speed = (fx_p + 1);
+ break;
+ }
+
+ if (event_b0)
+ {
+ // mute channel
+ opl_write(0xA0+i, adlib[0xA0+i]);
+ opl_write(0xB0+i, adlib[0xB0+i] & 0xDF);
+
+ // is note ?
+ if (event_b0 != 0x7F)
+ {
+ unsigned short note_encoded = flash_notes_encoded[event_b0];
+ unsigned short freq = flash_notes[(note_encoded >> 8) - 1];
+
+ flash_channel_freq = freq | ((note_encoded & 0xFF) << 10) | 0x2000;
+
+ opl_write(0xA0+i, flash_channel_freq & 0xFF);
+ opl_write(0xB0+i, flash_channel_freq >> 8);
+ }
+ }
+
+ if (fx == 0x01) // 1.0x1y: Fine Frequency Slide Up
+ {
+ flash_channel_freq += (fx_p << 1);
+
+ opl_write(0xA0+i, flash_channel_freq & 0xFF);
+ opl_write(0xB0+i, flash_channel_freq >> 8);
+ }
+ else if (fx == 0x02) // 1.0x2y: Fine Frequency Slide Down
+ {
+ flash_channel_freq -= (fx_p << 1);
+
+ opl_write(0xA0+i, flash_channel_freq & 0xFF);
+ opl_write(0xB0+i, flash_channel_freq >> 8);
+ }
+ }
+ }
+
+ // next row
+ flash.pattern_pos++;
+
+ // end of pattern ?
+ if (flash.pattern_pos >= 0x40)
+ {
+ flash.pattern_pos = 0;
+
+ flash.order_pos++;
+
+ // end of module ?
+ if (tune[0x600+flash.order_pos] == 0xFF)
+ {
+ flash.order_pos = 0;
+
+ plr.looping = 1;
+ }
+ }
+}
+
+float CxadflashPlayer::xadplayer_getrefresh()
+{
+ return 17.5f;
+}
+
+std::string CxadflashPlayer::xadplayer_gettype()
+{
+ return std::string("xad: flash player");
+}
+
+unsigned int CxadflashPlayer::xadplayer_getinstruments()
+{
+ return 32;
+}
diff --git a/plugins/adplug/adplug/flash.h b/plugins/adplug/adplug/flash.h
new file mode 100644
index 00000000..a3471df4
--- /dev/null
+++ b/plugins/adplug/adplug/flash.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+ *
+ * [xad] FLASH player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadflashPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadflashPlayer(Copl *newopl): CxadPlayer(newopl)
+ { };
+
+protected:
+ struct
+ {
+ unsigned char order_pos;
+ unsigned char pattern_pos;
+ } flash;
+ //
+ bool xadplayer_load()
+ {
+ if(xad.fmt == FLASH)
+ return true;
+ else
+ return false;
+ }
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ unsigned int xadplayer_getinstruments();
+
+private:
+ static const unsigned char flash_adlib_registers[99];
+ static const unsigned short flash_notes_encoded[268];
+ static const unsigned short flash_notes[12];
+ static const unsigned char flash_default_instrument[8];
+};
diff --git a/plugins/adplug/adplug/fmc.cpp b/plugins/adplug/adplug/fmc.cpp
new file mode 100644
index 00000000..c6839a61
--- /dev/null
+++ b/plugins/adplug/adplug/fmc.cpp
@@ -0,0 +1,224 @@
+/*
+ Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ Copyright (C) 1999 - 2007 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
+
+ fmc.cpp - FMC Loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include <string.h>
+#include "fmc.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CfmcLoader::factory(Copl *newopl)
+{
+ return new CfmcLoader(newopl);
+}
+
+bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_fx[16] = {0,1,2,3,4,8,255,255,255,255,26,11,12,13,14,15};
+
+ int i,j,k,t=0;
+
+ // read header
+ f->readString(header.id, 4);
+ f->readString(header.title, 21);
+ header.numchan = f->readInt(1);
+
+ // 'FMC!' - signed ?
+ if (strncmp(header.id,"FMC!",4)) { fp.close(f); return false; }
+
+ // init CmodPlayer
+ realloc_instruments(32);
+ realloc_order(256);
+ realloc_patterns(64,64,header.numchan);
+ init_trackord();
+
+ // load order
+ for(i = 0; i < 256; i++) order[i] = f->readInt(1);
+
+ f->ignore(2);
+
+ // load instruments
+ for(i = 0; i < 32; i++) {
+ instruments[i].synthesis = f->readInt(1);
+ instruments[i].feedback = f->readInt(1);
+
+ instruments[i].mod_attack = f->readInt(1);
+ instruments[i].mod_decay = f->readInt(1);
+ instruments[i].mod_sustain = f->readInt(1);
+ instruments[i].mod_release = f->readInt(1);
+ instruments[i].mod_volume = f->readInt(1);
+ instruments[i].mod_ksl = f->readInt(1);
+ instruments[i].mod_freq_multi = f->readInt(1);
+ instruments[i].mod_waveform = f->readInt(1);
+ instruments[i].mod_sustain_sound = f->readInt(1);
+ instruments[i].mod_ksr = f->readInt(1);
+ instruments[i].mod_vibrato = f->readInt(1);
+ instruments[i].mod_tremolo = f->readInt(1);
+
+ instruments[i].car_attack = f->readInt(1);
+ instruments[i].car_decay = f->readInt(1);
+ instruments[i].car_sustain = f->readInt(1);
+ instruments[i].car_release = f->readInt(1);
+ instruments[i].car_volume = f->readInt(1);
+ instruments[i].car_ksl = f->readInt(1);
+ instruments[i].car_freq_multi = f->readInt(1);
+ instruments[i].car_waveform = f->readInt(1);
+ instruments[i].car_sustain_sound = f->readInt(1);
+ instruments[i].car_ksr = f->readInt(1);
+ instruments[i].car_vibrato = f->readInt(1);
+ instruments[i].car_tremolo = f->readInt(1);
+
+ instruments[i].pitch_shift = f->readInt(1);
+
+ f->readString(instruments[i].name, 21);
+ }
+
+ // load tracks
+ for (i=0;i<64;i++)
+ {
+ if(f->ateof()) break;
+
+ for (j=0;j<header.numchan;j++)
+ {
+ for (k=0;k<64;k++)
+ {
+ fmc_event event;
+
+ // read event
+ event.byte0 = f->readInt(1);
+ event.byte1 = f->readInt(1);
+ event.byte2 = f->readInt(1);
+
+ // convert event
+ tracks[t][k].note = event.byte0 & 0x7F;
+ tracks[t][k].inst = ((event.byte0 & 0x80) >> 3) + (event.byte1 >> 4) + 1;
+ tracks[t][k].command = conv_fx[event.byte1 & 0x0F];
+ tracks[t][k].param1 = event.byte2 >> 4;
+ tracks[t][k].param2 = event.byte2 & 0x0F;
+
+ // fix effects
+ if (tracks[t][k].command == 0x0E) // 0x0E (14): Retrig
+ tracks[t][k].param1 = 3;
+ if (tracks[t][k].command == 0x1A) // 0x1A (26): Volume Slide
+ if (tracks[t][k].param1 > tracks[t][k].param2)
+ {
+ tracks[t][k].param1 -= tracks[t][k].param2;
+ tracks[t][k].param2 = 0;
+ }
+ else
+ {
+ tracks[t][k].param2 -= tracks[t][k].param1;
+ tracks[t][k].param1 = 0;
+ }
+ }
+
+ t++;
+ }
+ }
+ fp.close(f);
+
+ // convert instruments
+ for (i=0;i<31;i++)
+ buildinst(i);
+
+ // order length
+ for (i=0;i<256;i++)
+ {
+ if (order[i] >= 0xFE)
+ {
+ length = i;
+ break;
+ }
+ }
+
+ // data for Protracker
+ activechan = (0xffffffff >> (32 - header.numchan)) << (32 - header.numchan);
+ nop = t / header.numchan;
+ restartpos = 0;
+
+ // flags
+ flags = Faust;
+
+ rewind(0);
+
+ return true;
+}
+
+float CfmcLoader::getrefresh()
+{
+ return 50.0f;
+}
+
+std::string CfmcLoader::gettype()
+{
+ return std::string("Faust Music Creator");
+}
+
+std::string CfmcLoader::gettitle()
+{
+ return std::string(header.title);
+}
+
+std::string CfmcLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name);
+}
+
+unsigned int CfmcLoader::getinstruments()
+{
+ return 32;
+}
+
+/* -------- Private Methods ------------------------------- */
+
+void CfmcLoader::buildinst(unsigned char i)
+{
+ inst[i].data[0] = ((instruments[i].synthesis & 1) ^ 1);
+ inst[i].data[0] |= ((instruments[i].feedback & 7) << 1);
+
+ inst[i].data[3] = ((instruments[i].mod_attack & 15) << 4);
+ inst[i].data[3] |= (instruments[i].mod_decay & 15);
+ inst[i].data[5] = ((15 - (instruments[i].mod_sustain & 15)) << 4);
+ inst[i].data[5] |= (instruments[i].mod_release & 15);
+ inst[i].data[9] = (63 - (instruments[i].mod_volume & 63));
+ inst[i].data[9] |= ((instruments[i].mod_ksl & 3) << 6);
+ inst[i].data[1] = (instruments[i].mod_freq_multi & 15);
+ inst[i].data[7] = (instruments[i].mod_waveform & 3);
+ inst[i].data[1] |= ((instruments[i].mod_sustain_sound & 1) << 5);
+ inst[i].data[1] |= ((instruments[i].mod_ksr & 1) << 4);
+ inst[i].data[1] |= ((instruments[i].mod_vibrato & 1) << 6);
+ inst[i].data[1] |= ((instruments[i].mod_tremolo & 1) << 7);
+
+ inst[i].data[4] = ((instruments[i].car_attack & 15) << 4);
+ inst[i].data[4] |= (instruments[i].car_decay & 15);
+ inst[i].data[6] = ((15 - (instruments[i].car_sustain & 15)) << 4);
+ inst[i].data[6] |= (instruments[i].car_release & 15);
+ inst[i].data[10] = (63 - (instruments[i].car_volume & 63));
+ inst[i].data[10] |= ((instruments[i].car_ksl & 3) << 6);
+ inst[i].data[2] = (instruments[i].car_freq_multi & 15);
+ inst[i].data[8] = (instruments[i].car_waveform & 3);
+ inst[i].data[2] |= ((instruments[i].car_sustain_sound & 1) << 5);
+ inst[i].data[2] |= ((instruments[i].car_ksr & 1) << 4);
+ inst[i].data[2] |= ((instruments[i].car_vibrato & 1) << 6);
+ inst[i].data[2] |= ((instruments[i].car_tremolo & 1) << 7);
+
+ inst[i].slide = instruments[i].pitch_shift;
+}
diff --git a/plugins/adplug/adplug/fmc.h b/plugins/adplug/adplug/fmc.h
new file mode 100644
index 00000000..ae7d5c5b
--- /dev/null
+++ b/plugins/adplug/adplug/fmc.h
@@ -0,0 +1,91 @@
+/*
+ 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
+
+ fmc.h - FMC loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CfmcLoader: public CmodPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CfmcLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+ private:
+
+ struct fmc_event
+ {
+ unsigned char byte0;
+ unsigned char byte1;
+ unsigned char byte2;
+ };
+
+ struct fmc_header
+ {
+ char id[4];
+ char title[21];
+ unsigned char numchan;
+ } header;
+
+ struct fmc_instrument
+ {
+ unsigned char synthesis;
+ unsigned char feedback;
+
+ unsigned char mod_attack;
+ unsigned char mod_decay;
+ unsigned char mod_sustain;
+ unsigned char mod_release;
+ unsigned char mod_volume;
+ unsigned char mod_ksl;
+ unsigned char mod_freq_multi;
+ unsigned char mod_waveform;
+ unsigned char mod_sustain_sound;
+ unsigned char mod_ksr;
+ unsigned char mod_vibrato;
+ unsigned char mod_tremolo;
+ unsigned char car_attack;
+ unsigned char car_decay;
+ unsigned char car_sustain;
+ unsigned char car_release;
+ unsigned char car_volume;
+ unsigned char car_ksl;
+ unsigned char car_freq_multi;
+ unsigned char car_waveform;
+ unsigned char car_sustain_sound;
+ unsigned char car_ksr;
+ unsigned char car_vibrato;
+ unsigned char car_tremolo;
+
+ signed char pitch_shift;
+
+ char name[21];
+ } instruments[32];
+
+ void buildinst(unsigned char i);
+};
diff --git a/plugins/adplug/adplug/fmopl.c b/plugins/adplug/adplug/fmopl.c
new file mode 100644
index 00000000..2b0e82b0
--- /dev/null
+++ b/plugins/adplug/adplug/fmopl.c
@@ -0,0 +1,1390 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+**
+** Version 0.37a
+**
+*/
+
+/*
+ preliminary :
+ Problem :
+ note:
+*/
+
+/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL.
+ *
+ * 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
+ */
+
+#define INLINE __inline
+#define HAS_YM3812 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+//#include "driver.h" /* use M.A.M.E. */
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- for debug --------------------- */
+/* #define OPL_OUTPUT_LOG */
+#ifdef OPL_OUTPUT_LOG
+static FILE *opl_dbg_fp = NULL;
+static FM_OPL *opl_dbg_opl[16];
+static int opl_dbg_maxchip,opl_dbg_chip;
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT 4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+
+#define EG_OFF ((2*EG_ENT)<<ENV_BITS) /* OFF */
+#define EG_DED EG_OFF
+#define EG_DST (EG_ENT<<ENV_BITS) /* DECAY START */
+#define EG_AED EG_DST
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
+#define DV (EG_STEP/2)
+static const UINT32 KSL_TABLE[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static INT32 *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static INT32 **SIN_TABLE;
+
+/* LFO table */
+static INT32 *AMS_TABLE;
+static INT32 *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML 2
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32 *ams_table;
+INT32 *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2; /* connect for SLOT 2 */
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+
+//#define LOG_LEVEL LOG_INF
+#define LOG_LEVEL LOG_ERR
+
+//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
+#define LOG(n,x)
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+ if( SLOT->evm > ENV_MOD_RR)
+ {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc&EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+ /* calcrate envelope generator */
+ if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+ {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ)
+ {
+ SLOT->evs = 0;
+ }
+ else
+ {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF+1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm( OPL_CH *CH)
+{
+ INT32 *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = MUL_TABLE[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_typ = (v&0x20)>>5;
+ SLOT->vib = (v&0x40);
+ SLOT->ams = (v&0x80);
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
+
+ if( !(OPL->mode&0x80) )
+ { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ar = v>>4;
+ int dr = v&0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int sl = v>>4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+ UINT32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB)
+ {
+ int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ *CH->connect1 += OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+ UINT32 env_tam,env_sd,env_top,env_hh;
+ int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
+ INT32 tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB)
+ {
+ int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ feedback2 = OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam=OPL_CALC_SLOT(SLOT8_1);
+ env_top=OPL_CALC_SLOT(SLOT8_2);
+ env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+ else SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+ if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+ else SLOT7_2->Cnt += (CH[7].fc*8);
+ if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+ else SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+ else SLOT8_2->Cnt += (CH[8].fc*48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if( env_sd < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+ /* TAM */
+ if( env_tam < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+ /* TOP-CY */
+ if( env_top < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+ /* HH */
+ if( env_hh < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4;i <= 60;i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT<<ENV_BITS);
+ OPL->AR_TABLE[i] = rate / ARRATE;
+ OPL->DR_TABLE[i] = rate / DRRATE;
+ }
+ for (i = 60;i < 76;i++)
+ {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+#if 0
+ for (i = 0;i < 64 ;i++){ /* make for overflow area */
+ LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i,
+ ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
+ ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
+ }
+#endif
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+ return 0;
+ if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+ {
+ free(TL_TABLE);
+ return 0;
+ }
+ if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0;t < EG_ENT-1 ;t++){
+ rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
+/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
+ }
+ /* fill volume off area */
+ for ( t = EG_ENT-1; t < TL_MAX ;t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1];
+ for (s = 1;s <= SIN_ENT/4;s++){
+ pom = sin(2*PI*s/SIN_ENT); /* sin */
+ pom = 20*log10(1/pom); /* decibel */
+ j = pom / EG_STEP; /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j];
+/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+ }
+ for (s = 0;s < SIN_ENT;s++)
+ {
+ SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
+ SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i<EG_ENT; i++)
+ {
+ /* ATTACK curve */
+ pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+ /* make LFO ams table */
+ for (i=0; i<AMS_ENT; i++)
+ {
+ pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+ AMS_TABLE[i] = (1.0/EG_STEP)*pom; /* 1dB */
+ AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i<VIB_ENT; i++)
+ {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = VIB_RATE + (pom*0.07); /* +- 7cent */
+ VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
+ /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
+ }
+ return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+ /* make fnumber -> increment counter table */
+ for( fn=0 ; fn < 1024 ; fn++ )
+ {
+ OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
+ OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
+}
+
+/* ---------- write a OPL registers ---------- */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:controll */
+ switch(r&0x1f)
+ {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ if(!OPL->wavesel)
+ {
+ /* preset compatible mode */
+ int c;
+ for(c=0;c<OPL->max_ch;c++)
+ {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f);
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL,v&0x78);
+ OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+ }
+ }
+ return;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
+ }
+ return;
+ case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+ case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+ v&=0x1f; /* for DELTA-T unit */
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* EG-CTRL */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+#if 0
+ case 0x15: /* DAC data */
+ case 0x16:
+ case 0x17: /* SHIFT */
+ return;
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ return;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ return;
+ case 0x1a: /* PCM data */
+ return;
+#endif
+#endif
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r)
+ {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ UINT8 rkey = OPL->rythm^v;
+ OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
+ OPL->rythm = v&0x3f;
+ if(OPL->rythm&0x20)
+ {
+#if 0
+ usrintf_showmessage("OPL Rythm mode select");
+#endif
+ /* BD key on/off */
+ if(rkey&0x10)
+ {
+ if(v&0x10)
+ {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey&0x08)
+ {
+ if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey&0x04)
+ {
+ if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey&0x02)
+ {
+ if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey&0x01)
+ {
+ if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ int keyon = (v>>5)&1;
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+ if(CH->keyon != keyon)
+ {
+ if( (CH->keyon=keyon) )
+ {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ int blockRv = 7-(block_fnum>>10);
+ int fnum = block_fnum&0x3ff;
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = KSL_TABLE[block_fnum>>6];
+ CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+ CH->kcode = CH->block_fnum>>9;
+ if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v>>1)&7;
+ CH->FB = feedback ? (8+1) - feedback : 0;
+ CH->CON = v&1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ CH = &OPL->P_CH[slot/2];
+ if(OPL->wavesel)
+ {
+ /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !OPLOpenTable() )
+ {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+#if (BUILD_YM3812 || BUILD_YM3526)
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256);
+ }
+#endif
+}
+#endif /* (BUILD_YM3812 || BUILD_YM3526) */
+
+#if BUILD_Y8950
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ /* setup DELTA-T unit */
+ YM_DELTAT_DECODE_PRESET(DELTAT);
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* deltaT ADPCM */
+ if( DELTAT->portstate )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+ /* deltaT START flag */
+ if( !DELTAT->portstate )
+ OPL->status &= 0xfe;
+}
+#endif
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+ /* reset OPerator paramater */
+ for( c = 0 ; c < OPL->max_ch ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF+1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = outd;
+ DELTAT->portshift = 5;
+ DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0);
+ }
+#endif
+}
+
+/* ---------- Create one of vietual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+ int max_ch = 9; /* normaly 9 channels */
+
+ if( OPL_LockTable() ==-1) return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+ /* allocate memory block */
+ ptr = malloc(state_size);
+ if(ptr==NULL) return NULL;
+ /* clear */
+ memset(ptr,0,state_size);
+ OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
+#endif
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+ /* init grobal tables */
+ OPL_initalize(OPL);
+ /* reset chip */
+ OPLResetChip(OPL);
+#ifdef OPL_OUTPUT_LOG
+ if(!opl_dbg_fp)
+ {
+ opl_dbg_fp = fopen("opllog.opl","wb");
+ opl_dbg_maxchip = 0;
+ }
+ if(opl_dbg_fp)
+ {
+ opl_dbg_opl[opl_dbg_maxchip] = OPL;
+ fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip,
+ type,
+ clock&0xff,
+ (clock/0x100)&0xff,
+ (clock/0x10000)&0xff,
+ (clock/0x1000000)&0xff);
+ opl_dbg_maxchip++;
+ }
+#endif
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL)
+{
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ fclose(opl_dbg_fp);
+ opl_dbg_fp = NULL;
+ }
+#endif
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+#if BUILD_Y8950
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
+{
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
+{
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+#endif
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v);
+ }
+#endif
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ { /* status port */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else
+ LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+ }
+ return 0;
+#if 0
+ case 0x0f: /* ADPCM-DATA */
+ return 0;
+#endif
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ return 0;
+ }
+ return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0;ch<9;ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+ return OPL->status>>7;
+}
diff --git a/plugins/adplug/adplug/fmopl.h b/plugins/adplug/adplug/fmopl.h
new file mode 100644
index 00000000..a01ff902
--- /dev/null
+++ b/plugins/adplug/adplug/fmopl.h
@@ -0,0 +1,174 @@
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (HAS_YM3812)
+//#define BUILD_YM3526 (HAS_YM3526)
+//#define BUILD_Y8950 (HAS_Y8950)
+
+/* --- system optimize --- */
+/* select bit size of output : 8 or 16 */
+#define OPL_OUTPUT_BIT 16
+
+/* compiler dependence */
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+#if (OPL_OUTPUT_BIT==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_OUTPUT_BIT==8)
+typedef unsigned char OPLSAMPLE;
+#endif
+
+
+#if BUILD_Y8950
+#include "ymdeltat.h"
+#endif
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
+
+/* !!!!! here is private section , do not access there member direct !!!!! */
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ INT32 TL; /* total level :TL << 8 */
+ INT32 TLL; /* adjusted now TL */
+ UINT8 KSR; /* key scale rate :(shift down bit) */
+ INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
+ INT32 SL; /* sustin level :SL_TALBE[SL] */
+ INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
+ UINT8 ksl; /* keyscale level :(shift down bits) */
+ UINT8 ksr; /* key scale rate :kcode>>KSR */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+ UINT32 Cnt; /* frequency count : */
+ UINT32 Incr; /* frequency step : */
+ /* envelope generator state */
+ UINT8 eg_typ; /* envelope type flag */
+ UINT8 evm; /* envelope phase */
+ INT32 evc; /* envelope counter */
+ INT32 eve; /* envelope counter end point */
+ INT32 evs; /* envelope counter step */
+ INT32 evsa; /* envelope step for AR :AR[ksr] */
+ INT32 evsd; /* envelope step for DR :DR[ksr] */
+ INT32 evsr; /* envelope step for RR :RR[ksr] */
+ /* LFO */
+ UINT8 ams; /* ams flag */
+ UINT8 vib; /* vibrate flag */
+ /* wave selector */
+ INT32 **wavetable;
+}OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ UINT8 CON; /* connection type */
+ UINT8 FB; /* feed back :(shift down bit) */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 *connect2; /* slot2 output pointer */
+ INT32 op1_out[2]; /* slot1 output for selfeedback */
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum : */
+ UINT8 kcode; /* key code : KeyScaleCode */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ UINT8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
+ /* Timer */
+ int T[2]; /* timer counter */
+ UINT8 st[2]; /* timer enable */
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+ /* Rythm sention */
+ UINT8 rythm; /* Rythm mode , key flag */
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+ YM_DELTAT *deltat; /* DELTA-T ADPCM */
+#endif
+ /* Keyboard / I/O interface unit (Y8950) */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ int port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ int keyboard_param;
+ /* time tables */
+ INT32 AR_TABLE[75]; /* atttack rate tables */
+ INT32 DR_TABLE[75]; /* decay rate tables */
+ UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
+ /* LFO */
+ INT32 *ams_table;
+ INT32 *vib_table;
+ INT32 amsCnt;
+ INT32 amsIncr;
+ INT32 vibCnt;
+ INT32 vibIncr;
+ /* wave selector enable flag */
+ UINT8 wavesel;
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
+/* Y8950 port handlers */
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL,int a,int v);
+unsigned char OPLRead(FM_OPL *OPL,int a);
+int OPLTimerOver(FM_OPL *OPL,int c);
+
+/* YM3626/YM3812 local section */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+#endif
diff --git a/plugins/adplug/adplug/fprovide.cpp b/plugins/adplug/adplug/fprovide.cpp
new file mode 100644
index 00000000..cbe94e2f
--- /dev/null
+++ b/plugins/adplug/adplug/fprovide.cpp
@@ -0,0 +1,76 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 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
+ *
+ * fprovide.cpp - File provider class framework, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+#include <binio.h>
+#include <binfile.h>
+
+#include "fprovide.h"
+
+/***** CFileProvider *****/
+
+bool CFileProvider::extension(const std::string &filename,
+ const std::string &extension)
+{
+ const char *fname = filename.c_str(), *ext = extension.c_str();
+
+ if(strlen(fname) < strlen(ext) ||
+ stricmp(fname + strlen(fname) - strlen(ext), ext))
+ return false;
+ else
+ return true;
+}
+
+unsigned long CFileProvider::filesize(binistream *f)
+{
+ unsigned long oldpos = f->pos(), size;
+
+ f->seek(0, binio::End);
+ size = f->pos();
+ f->seek(oldpos, binio::Set);
+
+ return size;
+}
+
+/***** CProvider_Filesystem *****/
+
+binistream *CProvider_Filesystem::open(std::string filename) const
+{
+ binifstream *f = new binifstream(filename);
+
+ if(!f) return 0;
+ if(f->error()) { delete f; return 0; }
+
+ // Open all files as little endian with IEEE floats by default
+ f->setFlag(binio::BigEndian, false); f->setFlag(binio::FloatIEEE);
+
+ return f;
+}
+
+void CProvider_Filesystem::close(binistream *f) const
+{
+ binifstream *ff = (binifstream *)f;
+
+ if(f) {
+ ff->close();
+ delete ff;
+ }
+}
diff --git a/plugins/adplug/adplug/fprovide.h b/plugins/adplug/adplug/fprovide.h
new file mode 100644
index 00000000..2f1a0b62
--- /dev/null
+++ b/plugins/adplug/adplug/fprovide.h
@@ -0,0 +1,50 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * fprovide.h - File provider class framework, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_FILEPROVIDER
+#define H_ADPLUG_FILEPROVIDER
+
+#include <string>
+#include <binio.h>
+
+class CFileProvider
+{
+public:
+ virtual ~CFileProvider()
+ {
+ }
+
+ virtual binistream *open(std::string filename) const = 0;
+ virtual void close(binistream *) const = 0;
+
+ static bool extension(const std::string &filename,
+ const std::string &extension);
+ static unsigned long filesize(binistream *f);
+};
+
+class CProvider_Filesystem: public CFileProvider
+{
+public:
+ virtual binistream *open(std::string filename) const;
+ virtual void close(binistream *f) const;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/hsc.cpp b/plugins/adplug/adplug/hsc.cpp
new file mode 100644
index 00000000..1a912ff1
--- /dev/null
+++ b/plugins/adplug/adplug/hsc.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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
+ *
+ * hsc.cpp - HSC Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "hsc.h"
+#include "debug.h"
+
+/*** public methods **************************************/
+
+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;
+
+ // file validation section
+ if(!f || !fp.extension(filename, ".hsc") || fp.filesize(f) > 59187) {
+ AdPlug_LogWrite("ChscPlayer::load(\"%s\"): Not a HSC file!\n", filename.c_str());
+ fp.close(f);
+ return false;
+ }
+
+ // load section
+ for(i=0;i<128*12;i++) // load instruments
+ *((unsigned char *)instr + i) = f->readInt(1);
+ for (i=0;i<128;i++) { // correct instruments
+ instr[i][2] ^= (instr[i][2] & 0x40) << 1;
+ instr[i][3] ^= (instr[i][3] & 0x40) << 1;
+ instr[i][11] >>= 4; // slide
+ }
+ for(i=0;i<51;i++) song[i] = f->readInt(1); // load tracklist
+ for(i=0;i<50*64*9;i++) // load patterns
+ *((char *)patterns + i) = f->readInt(1);
+
+ fp.close(f);
+ rewind(0); // rewind module
+ return true;
+}
+
+bool ChscPlayer::update()
+{
+ // general vars
+ unsigned char chan,pattnr,note,effect,eff_op,inst,vol,Okt,db;
+ unsigned short Fnr;
+ unsigned long pattoff;
+
+ del--; // player speed handling
+ if(del)
+ return !songend; // nothing done
+
+ if(fadein) // fade-in handling
+ fadein--;
+
+ pattnr = song[songpos];
+ if(pattnr == 0xff) { // arrangement handling
+ songend = 1; // set end-flag
+ songpos = 0;
+ pattnr = song[songpos];
+ } else
+ if ((pattnr & 128) && (pattnr <= 0xb1)) { // goto pattern "nr"
+ songpos = song[songpos] & 127;
+ pattpos = 0;
+ pattnr = song[songpos];
+ songend = 1;
+ }
+
+ pattoff = pattpos*9;
+ for (chan=0;chan<9;chan++) { // handle all channels
+ note = patterns[pattnr][pattoff].note;
+ effect = patterns[pattnr][pattoff].effect;
+ pattoff++;
+
+ if(note & 128) { // set instrument
+ setinstr(chan,effect);
+ continue;
+ }
+ eff_op = effect & 0x0f;
+ inst = channel[chan].inst;
+ if(note)
+ channel[chan].slide = 0;
+
+ switch (effect & 0xf0) { // effect handling
+ case 0: // global effect
+ /* The following fx are unimplemented on purpose:
+ * 02 - Slide Mainvolume up
+ * 03 - Slide Mainvolume down (here: fade in)
+ * 04 - Set Mainvolume to 0
+ *
+ * This is because i've never seen any HSC modules using the fx this way.
+ * All modules use the fx the way, i've implemented it.
+ */
+ switch(eff_op) {
+ case 1: pattbreak++; break; // jump to next pattern
+ case 3: fadein = 31; break; // fade in (divided by 2)
+ case 5: mode6 = 1; break; // 6 voice mode on
+ case 6: mode6 = 0; break; // 6 voice mode off
+ }
+ break;
+ case 0x20:
+ case 0x10: // manual slides
+ 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: // set percussion instrument (unimplemented)
+ break;
+ case 0x60: // set feedback
+ opl->write(0xc0 + chan, (instr[channel[chan].inst][8] & 1) + (eff_op << 1));
+ break;
+ case 0xa0: // set carrier volume
+ vol = eff_op << 2;
+ opl->write(0x43 + op_table[chan], vol | (instr[channel[chan].inst][2] & ~63));
+ break;
+ case 0xb0: // set modulator volume
+ 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: // set instrument volume
+ 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; // position jump
+ case 0xf0: // set speed
+ speed = eff_op;
+ del = ++speed;
+ break;
+ }
+
+ if(fadein) // fade-in volume setting
+ setvolume(chan,fadein*2,fadein*2);
+
+ if(!note) // note handling
+ continue;
+ note--;
+
+ if ((note == 0x7f-1) || ((note/12) & ~7)) { // pause (7fh)
+ adl_freq[chan] &= ~32;
+ opl->write(0xb0 + chan,adl_freq[chan]);
+ continue;
+ }
+
+ // play the note
+ if(mtkmode) // imitate MPU-401 Trakker bug
+ 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; // never set key for drums
+ opl->write(0xb0 + chan, 0);
+ setfreq(chan,Fnr);
+ if(mode6) {
+ switch(chan) { // play drums
+ case 6: opl->write(0xbd,bd & ~16); bd |= 48; break; // bass drum
+ case 7: opl->write(0xbd,bd & ~1); bd |= 33; break; // hihat
+ case 8: opl->write(0xbd,bd & ~2); bd |= 34; break; // cymbal
+ }
+ opl->write(0xbd,bd);
+ }
+ }
+
+ del = speed; // player speed-timing
+ if(pattbreak) { // do post-effect handling
+ pattpos=0; // pattern break!
+ pattbreak=0;
+ songpos++;
+ songpos %= 50;
+ if(!songpos)
+ songend = 1;
+ } else {
+ pattpos++;
+ pattpos &= 63; // advance in pattern data
+ if (!pattpos) {
+ songpos++;
+ songpos %= 50;
+ if(!songpos)
+ songend = 1;
+ }
+ }
+ return !songend; // still playing
+}
+
+void ChscPlayer::rewind(int subsong)
+{
+ int i; // counter
+
+ // rewind HSC player
+ pattpos = 0; songpos = 0; pattbreak = 0; speed = 2;
+ del = 1; songend = 0; mode6 = 0; bd = 0; fadein = 0;
+
+ opl->init(); // reset OPL chip
+ opl->write(1,32); opl->write(8,128); opl->write(0xbd,0);
+
+ for(i=0;i<9;i++)
+ setinstr((char) i,(char) i); // init channels
+}
+
+unsigned int ChscPlayer::getpatterns()
+{
+ unsigned char poscnt,pattcnt=0;
+
+ // count patterns
+ 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;
+
+ // count positions
+ 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;
+
+ // count instruments
+ 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;
+}
+
+/*** private methods *************************************/
+
+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) // carrier
+ opl->write(0x40 + op,volm | (ins[3] & ~63));
+ else
+ opl->write(0x40 + op, ins[3]); // modulator
+}
+
+void ChscPlayer::setinstr(unsigned char chan, unsigned char insnr)
+{
+ unsigned char *ins = instr[insnr];
+ char op = op_table[chan];
+
+ channel[chan].inst = insnr; // set internal instrument
+ opl->write(0xb0 + chan,0); // stop old note
+
+ // set instrument
+ opl->write(0xc0 + chan, ins[8]);
+ opl->write(0x23 + op, ins[0]); // carrier
+ opl->write(0x20 + op, ins[1]); // modulator
+ opl->write(0x63 + op, ins[4]); // bits 0..3 = decay; 4..7 = attack
+ opl->write(0x60 + op, ins[5]);
+ opl->write(0x83 + op, ins[6]); // 0..3 = release; 4..7 = sustain
+ opl->write(0x80 + op, ins[7]);
+ opl->write(0xe3 + op, ins[9]); // bits 0..1 = Wellenform
+ opl->write(0xe0 + op, ins[10]);
+ setvolume(chan, ins[2] & 63, ins[3] & 63);
+}
diff --git a/plugins/adplug/adplug/hsc.h b/plugins/adplug/adplug/hsc.h
new file mode 100644
index 00000000..d09b8906
--- /dev/null
+++ b/plugins/adplug/adplug/hsc.h
@@ -0,0 +1,75 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 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
+ *
+ * hsc.h - HSC Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_HSCPLAYER
+#define H_ADPLUG_HSCPLAYER
+
+#include "player.h"
+
+class ChscPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ ChscPlayer(Copl *newopl): CPlayer(newopl), mtkmode(0) {}
+
+ bool load(const std::string &fn, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh() { return 18.2f; }; // refresh rate is fixed at 18.2Hz
+
+ std::string gettype() { return std::string("HSC Adlib Composer / HSC-Tracker"); }
+ unsigned int getpatterns();
+ unsigned int getpattern() { return song[songpos]; }
+ unsigned int getorders();
+ unsigned int getorder() { return songpos; }
+ unsigned int getrow() { return pattpos; }
+ unsigned int getspeed() { return speed; }
+ unsigned int getinstruments();
+
+ protected:
+ struct hscnote {
+ unsigned char note, effect;
+ }; // note type in HSC pattern
+
+ struct hscchan {
+ unsigned char inst; // current instrument
+ signed char slide; // used for manual slide-effects
+ unsigned short freq; // actual replaying frequency
+ }; // HSC channel data
+
+ hscchan channel[9]; // player channel-info
+ unsigned char instr[128][12]; // instrument data
+ unsigned char song[0x80]; // song-arrangement (MPU-401 Trakker enhanced)
+ hscnote patterns[50][64*9]; // pattern data
+ unsigned char pattpos,songpos, // various bytes & flags
+ pattbreak,songend,mode6,bd,fadein;
+ unsigned int speed,del;
+ unsigned char adl_freq[9]; // adlib frequency registers
+ int mtkmode; // flag: MPU-401 Trakker mode on/off
+
+ private:
+ void setfreq(unsigned char chan, unsigned short freq);
+ void setvolume(unsigned char chan, int volc, int volm);
+ void setinstr(unsigned char chan, unsigned char insnr);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/hsp.cpp b/plugins/adplug/adplug/hsp.cpp
new file mode 100644
index 00000000..0430f6d7
--- /dev/null
+++ b/plugins/adplug/adplug/hsp.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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
+ *
+ * hsp.cpp - HSP Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "hsp.h"
+
+CPlayer *ChspLoader::factory(Copl *newopl)
+{
+ return new ChspLoader(newopl);
+}
+
+bool ChspLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned long i, j, orgsize, filesize;
+ unsigned char *cmp, *org;
+
+ // file validation section
+ if(!fp.extension(filename, ".hsp")) { fp.close(f); return false; }
+
+ filesize = fp.filesize(f);
+ orgsize = f->readInt(2);
+ if(orgsize > 59187) { fp.close(f); return false; }
+
+ // load section
+ cmp = new unsigned char[filesize];
+ for(i = 0; i < filesize; i++) cmp[i] = f->readInt(1);
+ fp.close(f);
+
+ org = new unsigned char[orgsize];
+ for(i = 0, j = 0; i < filesize; j += cmp[i], i += 2) { // RLE decompress
+ if(j >= orgsize) break; // memory boundary check
+ memset(org + j, cmp[i + 1], j + cmp[i] < orgsize ? cmp[i] : orgsize - j - 1);
+ }
+ delete [] cmp;
+
+ memcpy(instr, org, 128 * 12); // instruments
+ for(i = 0; i < 128; i++) { // correct instruments
+ instr[i][2] ^= (instr[i][2] & 0x40) << 1;
+ instr[i][3] ^= (instr[i][3] & 0x40) << 1;
+ instr[i][11] >>= 4; // slide
+ }
+ memcpy(song, org + 128 * 12, 51); // tracklist
+ memcpy(patterns, org + 128 * 12 + 51, orgsize - 128 * 12 - 51); // patterns
+ delete [] org;
+
+ rewind(0);
+ return true;
+}
diff --git a/plugins/adplug/adplug/hsp.h b/plugins/adplug/adplug/hsp.h
new file mode 100644
index 00000000..86fa9d69
--- /dev/null
+++ b/plugins/adplug/adplug/hsp.h
@@ -0,0 +1,39 @@
+/*
+ * 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
+ *
+ * hsp.h: HSC Packed Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_HSPLOADER
+#define H_ADPLUG_HSPLOADER
+
+#include "hsc.h"
+
+class ChspLoader: public ChscPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ ChspLoader(Copl *newopl)
+ : ChscPlayer(newopl)
+ {};
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/hybrid.cpp b/plugins/adplug/adplug/hybrid.cpp
new file mode 100644
index 00000000..cff08c88
--- /dev/null
+++ b/plugins/adplug/adplug/hybrid.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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
+ *
+ * [xad] HYBRID player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : HYBRID.EXE
+ type : Hybrid cracktro for Apache Longbow CD-RIP
+ tune : from 'Mig-29 Super Fulcrum' game by Domark
+ player : from 'Mig-29 Super Fulcrum' game by Domark
+*/
+
+#include "hybrid.h"
+#include "debug.h"
+
+const unsigned char CxadhybridPlayer::hyb_adlib_registers[99] =
+{
+ 0xE0, 0x60, 0x80, 0x20, 0x40, 0xE3, 0x63, 0x83, 0x23, 0x43, 0xC0,
+ 0xE1, 0x61, 0x81, 0x21, 0x41, 0xE4, 0x64, 0x84, 0x24, 0x44, 0xC1,
+ 0xE2, 0x62, 0x82, 0x22, 0x42, 0xE5, 0x65, 0x85, 0x25, 0x45, 0xC2,
+ 0xE8, 0x68, 0x88, 0x28, 0x48, 0xEB, 0x6B, 0x8B, 0x2B, 0x4B, 0xC3,
+ 0xE9, 0x69, 0x89, 0x29, 0x49, 0xEC, 0x6C, 0x8C, 0x2C, 0x4C, 0xC4,
+ 0xEA, 0x6A, 0x8A, 0x2A, 0x4A, 0xED, 0x6D, 0x8D, 0x2D, 0x4D, 0xC5,
+ 0xF0, 0x70, 0x90, 0x30, 0x50, 0xF3, 0x73, 0x93, 0x33, 0x53, 0xC6,
+ 0xF1, 0x71, 0x91, 0x31, 0x51, 0xF4, 0x74, 0x94, 0x34, 0x54, 0xC7,
+ 0xF2, 0x72, 0x92, 0x32, 0x52, 0xF5, 0x75, 0x95, 0x35, 0x55, 0xC8
+};
+
+const unsigned short CxadhybridPlayer::hyb_notes[98] =
+{
+ 0x0000, 0x0000,
+ 0x016B, 0x0181, 0x0198, 0x01B0, 0x01CA, 0x01E5, 0x0202, 0x0220, 0x0241, 0x0263, 0x0287, 0x02AE,
+ 0x056B, 0x0581, 0x0598, 0x05B0, 0x05CA, 0x05E5, 0x0602, 0x0620, 0x0641, 0x0663, 0x0687, 0x06AE,
+ 0x096B, 0x0981, 0x0998, 0x09B0, 0x09CA, 0x09E5, 0x0A02, 0x0A20, 0x0A41, 0x0A63, 0x0A87, 0x0AAE,
+ 0x0D6B, 0x0D81, 0x0D98, 0x0DB0, 0x0DCA, 0x0DE5, 0x0E02, 0x0E20, 0x0E41, 0x0E63, 0x0E87, 0x0EAE,
+ 0x116B, 0x1181, 0x1198, 0x11B0, 0x11CA, 0x11E5, 0x1202, 0x1220, 0x1241, 0x1263, 0x1287, 0x12AE,
+ 0x156B, 0x1581, 0x1598, 0x15B0, 0x15CA, 0x15E5, 0x1602, 0x1620, 0x1641, 0x1663, 0x1687, 0x16AE,
+ 0x196B, 0x1981, 0x1998, 0x19B0, 0x19CA, 0x19E5, 0x1A02, 0x1A20, 0x1A41, 0x1A63, 0x1A87, 0x1AAE,
+ 0x1D6B, 0x1D81, 0x1D98, 0x1DB0, 0x1DCA, 0x1DE5, 0x1E02, 0x1E20, 0x1E41, 0x1E63, 0x1E87, 0x1EAE
+};
+
+const unsigned char CxadhybridPlayer::hyb_default_instrument[11] =
+{
+ 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00
+};
+
+CPlayer *CxadhybridPlayer::factory(Copl *newopl)
+{
+ return new CxadhybridPlayer(newopl);
+}
+
+bool CxadhybridPlayer::xadplayer_load()
+{
+ if(xad.fmt != HYBRID)
+ return false;
+
+ // load instruments
+ hyb.inst = (hyb_instrument *)&tune[0];
+
+ // load order
+ hyb.order = &tune[0x1D4];
+
+ return true;
+}
+
+void CxadhybridPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ hyb.order_pos = 0;
+ hyb.pattern_pos = 0;
+
+ hyb.speed = 6;
+ hyb.speed_counter = 1;
+
+ plr.speed = 1;
+
+ // init channel data
+ for(i=0;i<9;i++)
+ {
+ hyb.channel[i].freq = 0x2000;
+ hyb.channel[i].freq_slide = 0x0000;
+ }
+
+ // basic OPL init
+ opl_write(0x01, 0x20);
+ opl_write(0xBD, 0x40);
+ opl_write(0x08, 0x00);
+
+ // init OPL channels
+ for(i=0;i<9;i++)
+ {
+ for(int j=0;j<11;j++)
+ opl_write(hyb_adlib_registers[i*11+j], 0x00 /* hyb_default_instrument[j] */ );
+
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x20);
+ }
+}
+
+void CxadhybridPlayer::xadplayer_update()
+{
+ int i,j;
+ unsigned char patpos,ordpos;
+
+ if (--hyb.speed_counter)
+ goto update_slides;
+
+ hyb.speed_counter = hyb.speed;
+
+ patpos = hyb.pattern_pos;
+ ordpos = hyb.order_pos;
+
+ // process channels
+ for(i=0;i<9;i++)
+ {
+ unsigned char *pos = &tune[0xADE + (hyb.order[hyb.order_pos*9 + i] * 64 * 2) + (patpos * 2)];
+ // read event
+ unsigned short event = (pos[1] << 8) + pos[0];
+
+#ifdef DEBUG
+ AdPlug_LogWrite("track %02X, channel %02X, event %04X:\n", hyb.order[hyb.order_pos*9 + i], i, event );
+#endif
+
+ // calculate variables
+ unsigned char note = event >> 9;
+ unsigned char ins = ((event & 0x01F0) >> 4);
+ unsigned char slide = event & 0x000F;
+
+ // play event
+ switch(note)
+ {
+ case 0x7D: // 0x7D: Set Speed
+ hyb.speed = event & 0xFF;
+ break;
+ case 0x7E: // 0x7E: Jump Position
+ hyb.order_pos = event & 0xFF;
+ hyb.pattern_pos = 0x3F;
+
+ // jumpback ?
+ if (hyb.order_pos <= ordpos)
+ plr.looping = 1;
+
+ break;
+ case 0x7F: // 0x7F: Pattern Break
+ hyb.pattern_pos = 0x3F;
+ break;
+ default:
+
+ // is instrument ?
+ if (ins)
+ for(j=0;j<11;j++)
+ opl_write(hyb_adlib_registers[i*11+j], *((unsigned char *)&hyb.inst[ins-1] + 7 + j)); // +7 = skip name...
+
+ // is note ?
+ if (note)
+ {
+ hyb.channel[i].freq = hyb_notes[note];
+ hyb.channel[i].freq_slide = 0;
+ }
+
+ // is slide ?
+ if (slide)
+ {
+ hyb.channel[i].freq_slide = (((slide >> 3) * -1) * (slide & 7)) << 1;
+
+ if (slide & 0x80)
+ slide = -(slide & 7);
+ }
+
+ // set frequency
+ if (!(hyb.channel[i].freq & 0x2000))
+ {
+ opl_write(0xA0+i, hyb.channel[i].freq & 0xFF);
+ opl_write(0xB0+i, hyb.channel[i].freq >> 8);
+
+ hyb.channel[i].freq |= 0x2000;
+
+ opl_write(0xA0+i, hyb.channel[i].freq & 0xFF);
+ opl_write(0xB0+i, hyb.channel[i].freq >> 8);
+ }
+
+ break;
+ }
+ }
+
+ hyb.pattern_pos++;
+
+ // end of pattern ?
+ if (hyb.pattern_pos >= 0x40)
+ {
+ hyb.pattern_pos = 0;
+
+ hyb.order_pos++;
+ }
+
+update_slides:
+#ifdef DEBUG
+ AdPlug_LogWrite("slides:\n");
+#endif
+ // update fine frequency slides
+ for(i=0;i<9;i++)
+ if (hyb.channel[i].freq_slide)
+ {
+ hyb.channel[i].freq = (((hyb.channel[i].freq & 0x1FFF) + hyb.channel[i].freq_slide) & 0x1FFF) | 0x2000;
+
+ opl_write(0xA0+i, hyb.channel[i].freq & 0xFF);
+ opl_write(0xB0+i, hyb.channel[i].freq >> 8);
+ }
+}
+
+float CxadhybridPlayer::xadplayer_getrefresh()
+{
+ return 50.0f;
+}
+
+std::string CxadhybridPlayer::xadplayer_gettype()
+{
+ return (std::string("xad: hybrid player"));
+}
+
+std::string CxadhybridPlayer::xadplayer_getinstrument(unsigned int i)
+{
+ return (std::string(hyb.inst[i].name,7));
+}
+
+unsigned int CxadhybridPlayer::xadplayer_getinstruments()
+{
+ return 26;
+}
diff --git a/plugins/adplug/adplug/hybrid.h b/plugins/adplug/adplug/hybrid.h
new file mode 100644
index 00000000..65d39d34
--- /dev/null
+++ b/plugins/adplug/adplug/hybrid.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+ *
+ * [xad] HYBRID player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadhybridPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadhybridPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct hyb_instrument
+ {
+ char name[7];
+ unsigned char mod_wave;
+ unsigned char mod_AD;
+ unsigned char mod_SR;
+ unsigned char mod_crtl;
+ unsigned char mod_volume;
+ unsigned char car_wave;
+ unsigned char car_AD;
+ unsigned char car_SR;
+ unsigned char car_crtl;
+ unsigned char car_volume;
+ unsigned char connect;
+ };
+
+ struct
+ {
+ unsigned char order_pos;
+ unsigned char pattern_pos;
+
+ unsigned char *order;
+
+ hyb_instrument *inst;
+
+ struct
+ {
+ unsigned short freq;
+ unsigned short freq_slide;
+ } channel[9];
+
+ unsigned char speed;
+ unsigned char speed_counter;
+ } hyb;
+ //
+ bool xadplayer_load();
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ std::string xadplayer_getinstrument(unsigned int i);
+ unsigned int xadplayer_getinstruments();
+
+private:
+ static const unsigned char hyb_adlib_registers[99];
+ static const unsigned short hyb_notes[98];
+ static const unsigned char hyb_default_instrument[11];
+};
diff --git a/plugins/adplug/adplug/hyp.cpp b/plugins/adplug/adplug/hyp.cpp
new file mode 100644
index 00000000..7ed3a315
--- /dev/null
+++ b/plugins/adplug/adplug/hyp.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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
+ *
+ * [xad] HYP player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : HT-EF2.COM, HT-EF3.COM
+ type : Eiserne Front BBStro
+ tune : by Shadowdancer [Hypnosis]
+ player : by (?)Hetero [LKCC/SAC]
+*/
+
+#include "hyp.h"
+#include "debug.h"
+
+const unsigned char CxadhypPlayer::hyp_adlib_registers[99] =
+{
+ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xA0, 0xB0, 0xC0,
+ 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xA1, 0xB1, 0xC1,
+ 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xA2, 0xB2, 0xC2,
+ 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xA3, 0xB3, 0xC3,
+ 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xA4, 0xB4, 0xC4,
+ 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xA5, 0xB5, 0xC5,
+ 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xA6, 0xB6, 0xC6,
+ 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xA7, 0xB7, 0xC7,
+ 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xA8, 0xB8, 0xC8
+};
+
+const unsigned short CxadhypPlayer::hyp_notes[73] =
+{
+ 0x0000, // by riven
+ 0x0956, 0x096B, 0x0980, 0x0998, 0x09B1, 0x09C9, 0x09E5, 0x0A03, 0x0A21,
+ 0x0A41, 0x0A63, 0x0A86, 0x0D56, 0x0D6B, 0x0D80, 0x0D98, 0x0DB1, 0x0DC9,
+ 0x0DE5, 0x0E03, 0x0E21, 0x0E41, 0x0E63, 0x0E86, 0x1156, 0x116B, 0x1180,
+ 0x1198, 0x11B1, 0x11C9, 0x11E5, 0x1203, 0x1221, 0x1241, 0x1263, 0x1286,
+ 0x1556, 0x156B, 0x1580, 0x1598, 0x15B1, 0x15C9, 0x15E5, 0x1603, 0x1621,
+ 0x1641, 0x1663, 0x1686, 0x1956, 0x196B, 0x1980, 0x1998, 0x19B1, 0x19C9,
+ 0x19E5, 0x1A03, 0x1A21, 0x1A41, 0x1A63, 0x1A86, 0x1D56, 0x1D6B, 0x1D80,
+ 0x1D98, 0x1DB1, 0x1DC9, 0x1DE5, 0x1E03, 0x1E21, 0x1E41, 0x1E63, 0x1E86
+};
+
+CPlayer *CxadhypPlayer::factory(Copl *newopl)
+{
+ return new CxadhypPlayer(newopl);
+}
+
+void CxadhypPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ plr.speed = tune[5];
+
+ opl_write(0xBD,0xC0);
+
+ for(i=0; i<9; i++)
+ adlib[0xB0+i] = 0;
+
+ // define instruments
+ for(i=0; i<99; i++)
+ opl_write(hyp_adlib_registers[i], tune[6+i]);
+
+ hyp.pointer = 0x69;
+}
+
+void CxadhypPlayer::xadplayer_update()
+{
+ for(int i=0; i<9; i++)
+ {
+ unsigned char event = tune[hyp.pointer++];
+
+ if (event)
+ {
+ unsigned short freq = hyp_notes[event & 0x3F];
+
+ unsigned char lofreq = (freq & 0xFF);
+ unsigned char hifreq = (freq >> 8);
+
+ opl_write(0xB0+i, adlib[0xB0+i]);
+
+ if (!(event & 0x40))
+ {
+ opl_write(0xA0+i, lofreq);
+ opl_write(0xB0+i, hifreq | 0x20);
+ }
+
+ adlib[0xB0+i] &= 0xDF;
+ }
+ }
+
+ hyp.pointer += 3;
+
+ if (hyp.pointer >= tune_size)
+ {
+ hyp.pointer = 0x69;
+ plr.looping = 1;
+ }
+}
+
+float CxadhypPlayer::xadplayer_getrefresh()
+{
+ return 60.0f;
+}
+
+std::string CxadhypPlayer::xadplayer_gettype()
+{
+ return std::string("xad: hypnosis player");
+}
diff --git a/plugins/adplug/adplug/hyp.h b/plugins/adplug/adplug/hyp.h
new file mode 100644
index 00000000..e2a5bf5a
--- /dev/null
+++ b/plugins/adplug/adplug/hyp.h
@@ -0,0 +1,53 @@
+/*
+ * 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
+ *
+ * [xad] HYP player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadhypPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadhypPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct
+ {
+ unsigned short pointer;
+ } hyp;
+ //
+ bool xadplayer_load()
+ {
+ if(xad.fmt == HYP)
+ return true;
+ else
+ return false;
+ }
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+
+private:
+ static const unsigned char hyp_adlib_registers[99];
+ static const unsigned short hyp_notes[73];
+};
diff --git a/plugins/adplug/adplug/imf.cpp b/plugins/adplug/adplug/imf.cpp
new file mode 100644
index 00000000..37af7aca
--- /dev/null
+++ b/plugins/adplug/adplug/imf.cpp
@@ -0,0 +1,196 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * imf.cpp - IMF Player by Simon Peter <dn.tlp@gmx.net>
+ *
+ * FILE FORMAT:
+ * There seem to be 2 different flavors of IMF formats out there. One version
+ * contains just the raw IMF music data. In this case, the first word of the
+ * file is always 0 (because the music data starts this way). This is already
+ * the music data! So read in the entire file and play it.
+ *
+ * If this word is greater than 0, it specifies the size of the following
+ * song data in bytes. In this case, the file has a footer that contains
+ * arbitrary infos about it. Mostly, this is plain ASCII text with some words
+ * of the author. Read and play the specified amount of song data and display
+ * the remaining data as ASCII text.
+ *
+ * NOTES:
+ * This player handles the two above mentioned formats, as well as a third
+ * type, invented by Martin Fernandez <mfernan@cnba.uba.ar>, that's got a
+ * proper header to add title/game name information. After the header starts
+ * the normal IMF file in one of the two above mentioned formats.
+ *
+ * This player also handles a special footer format by Adam Nielsen,
+ * which has defined fields of information about the song, the author
+ * and more.
+ */
+
+#include <string.h>
+
+#include "imf.h"
+#include "database.h"
+
+/*** public methods *************************************/
+
+CPlayer *CimfPlayer::factory(Copl *newopl)
+{
+ return new CimfPlayer(newopl);
+}
+
+bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned long fsize, flsize, mfsize = 0;
+ unsigned int i;
+
+ // file validation section
+ {
+ char header[5];
+ int version;
+
+ f->readString(header, 5);
+ version = f->readInt(1);
+
+ if(strncmp(header, "ADLIB", 5) || version != 1) {
+ if(!fp.extension(filename, ".imf") && !fp.extension(filename, ".wlf")) {
+ // It's no IMF file at all
+ fp.close(f);
+ return false;
+ } else
+ f->seek(0); // It's a normal IMF file
+ } else {
+ // It's a IMF file with header
+ track_name = f->readString('\0');
+ game_name = f->readString('\0');
+ f->ignore(1);
+ mfsize = f->pos() + 2;
+ }
+ }
+
+ // load section
+ if(mfsize)
+ fsize = f->readInt(4);
+ else
+ fsize = f->readInt(2);
+ flsize = fp.filesize(f);
+ if(!fsize) { // footerless file (raw music data)
+ if(mfsize)
+ f->seek(-4, binio::Add);
+ else
+ f->seek(-2, binio::Add);
+ size = (flsize - mfsize) / 4;
+ } else // file has got a footer
+ size = fsize / 4;
+
+ data = new Sdata[size];
+ for(i = 0; i < size; i++) {
+ data[i].reg = f->readInt(1); data[i].val = f->readInt(1);
+ data[i].time = f->readInt(2);
+ }
+
+ // read footer, if any
+ if(fsize && (fsize < flsize - 2 - mfsize))
+ if(f->readInt(1) == 0x1a) {
+ // Adam Nielsen's footer format
+ track_name = f->readString();
+ author_name = f->readString();
+ remarks = f->readString();
+ } else {
+ // Generic footer
+ unsigned long footerlen = flsize - fsize - 2 - mfsize;
+
+ footer = new char[footerlen + 1];
+ f->readString(footer, footerlen);
+ footer[footerlen] = '\0'; // Make ASCIIZ string
+ }
+
+ rate = getrate(filename, fp, f);
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CimfPlayer::update()
+{
+ do {
+ opl->write(data[pos].reg,data[pos].val);
+ del = data[pos].time;
+ pos++;
+ } while(!del && pos < size);
+
+ if(pos >= size) {
+ pos = 0;
+ songend = true;
+ }
+ else timer = rate / (float)del;
+
+ return !songend;
+}
+
+void CimfPlayer::rewind(int subsong)
+{
+ pos = 0; del = 0; timer = rate; songend = false;
+ opl->init(); opl->write(1,32); // go to OPL2 mode
+}
+
+std::string CimfPlayer::gettitle()
+{
+ std::string title;
+
+ title = track_name;
+
+ if(!track_name.empty() && !game_name.empty())
+ title += " - ";
+
+ title += game_name;
+
+ return title;
+}
+
+std::string CimfPlayer::getdesc()
+{
+ std::string desc;
+
+ if(footer)
+ desc = std::string(footer);
+
+ if(!remarks.empty() && footer)
+ desc += "\n\n";
+
+ desc += remarks;
+
+ return desc;
+}
+
+/*** private methods *************************************/
+
+float CimfPlayer::getrate(const std::string &filename, const CFileProvider &fp, binistream *f)
+{
+ if(db) { // Database available
+ f->seek(0, binio::Set);
+ CClockRecord *record = (CClockRecord *)db->search(CAdPlugDatabase::CKey(*f));
+ if (record && record->type == CAdPlugDatabase::CRecord::ClockSpeed)
+ return record->clock;
+ }
+
+ // Otherwise the database is either unavailable, or there's no entry for this file
+ if (fp.extension(filename, ".imf")) return 560.0f;
+ if (fp.extension(filename, ".wlf")) return 700.0f;
+ return 700.0f; // default speed for unknown files that aren't .IMF or .WLF
+}
diff --git a/plugins/adplug/adplug/imf.h b/plugins/adplug/adplug/imf.h
new file mode 100644
index 00000000..4c7d0daf
--- /dev/null
+++ b/plugins/adplug/adplug/imf.h
@@ -0,0 +1,68 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * imf.h - IMF Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_IMFPLAYER
+#define H_ADPLUG_IMFPLAYER
+
+#include "player.h"
+
+class CimfPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CimfPlayer(Copl *newopl)
+ : CPlayer(newopl), footer(0), data(0)
+ { }
+ ~CimfPlayer()
+ { if(data) delete [] data; if(footer) delete [] footer; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return timer; };
+
+ std::string gettype()
+ { return std::string("IMF File Format"); }
+ std::string gettitle();
+ std::string getauthor()
+ { return author_name; }
+ std::string getdesc();
+
+protected:
+ unsigned long pos, size;
+ unsigned short del;
+ bool songend;
+ float rate, timer;
+ char *footer;
+ std::string track_name, game_name, author_name, remarks;
+
+ struct Sdata {
+ unsigned char reg, val;
+ unsigned short time;
+ } *data;
+
+private:
+ float getrate(const std::string &filename, const CFileProvider &fp, binistream *f);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/kemuopl.h b/plugins/adplug/adplug/kemuopl.h
new file mode 100644
index 00000000..d2ca6e28
--- /dev/null
+++ b/plugins/adplug/adplug/kemuopl.h
@@ -0,0 +1,61 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * kemuopl.h - Emulated OPL using Ken Silverman's emulator, by Simon Peter
+ * <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_KEMUOPL
+#define H_ADPLUG_KEMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "adlibemu.h"
+}
+
+class CKemuopl: public Copl
+{
+public:
+ CKemuopl(int rate, bool bit16, bool usestereo)
+ : use16bit(bit16), stereo(usestereo)
+ {
+ adlibinit(rate, usestereo ? 2 : 1, bit16 ? 2 : 1);
+ currType = TYPE_OPL2;
+ };
+
+ void update(short *buf, int samples)
+ {
+ if(use16bit) samples *= 2;
+ if(stereo) samples *= 2;
+ adlibgetsample(buf, samples);
+ }
+
+ // template methods
+ void write(int reg, int val)
+ {
+ if(currChip == 0)
+ adlib0(reg, val);
+ };
+
+ void init() {};
+
+private:
+ bool use16bit,stereo;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/ksm.cpp b/plugins/adplug/adplug/ksm.cpp
new file mode 100644
index 00000000..c12f3d8f
--- /dev/null
+++ b/plugins/adplug/adplug/ksm.cpp
@@ -0,0 +1,336 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ksm.cpp - KSM Player for AdPlug by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "ksm.h"
+#include "debug.h"
+
+const unsigned int CksmPlayer::adlibfreq[63] = {
+ 0,
+ 2390,2411,2434,2456,2480,2506,2533,2562,2592,2625,2659,2695,
+ 3414,3435,3458,3480,3504,3530,3557,3586,3616,3649,3683,3719,
+ 4438,4459,4482,4504,4528,4554,4581,4610,4640,4673,4707,4743,
+ 5462,5483,5506,5528,5552,5578,5605,5634,5664,5697,5731,5767,
+ 6486,6507,6530,6552,6576,6602,6629,6658,6688,6721,6755,6791,
+ 7510};
+
+/*** public methods **************************************/
+
+CPlayer *CksmPlayer::factory(Copl *newopl)
+{
+ return new CksmPlayer(newopl);
+}
+
+bool CksmPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f;
+ int i;
+ char *fn = new char[filename.length() + 9];
+
+ // file validation section
+ if(!fp.extension(filename, ".ksm")) {
+ AdPlug_LogWrite("CksmPlayer::load(,\"%s\"): File doesn't have '.ksm' "
+ "extension! Rejected!\n", filename.c_str());
+ return false;
+ }
+ AdPlug_LogWrite("*** CksmPlayer::load(,\"%s\") ***\n", filename.c_str());
+
+ // Load instruments from 'insts.dat'
+ strcpy(fn, filename.c_str());
+ for(i = strlen(fn) - 1; i >= 0; i--)
+ if(fn[i] == '/' || fn[i] == '\\')
+ break;
+ strcpy(fn + i + 1, "insts.dat");
+ AdPlug_LogWrite("Instruments file: \"%s\"\n", fn);
+ f = fp.open(fn);
+ delete [] fn;
+ if(!f) {
+ AdPlug_LogWrite("Couldn't open instruments file! Aborting!\n");
+ AdPlug_LogWrite("--- CksmPlayer::load ---\n");
+ return false;
+ }
+ loadinsts(f);
+ fp.close(f);
+
+ f = fp.open(filename); if(!f) return false;
+ for(i = 0; i < 16; i++) trinst[i] = f->readInt(1);
+ for(i = 0; i < 16; i++) trquant[i] = f->readInt(1);
+ for(i = 0; i < 16; i++) trchan[i] = f->readInt(1);
+ f->ignore(16);
+ for(i = 0; i < 16; i++) trvol[i] = f->readInt(1);
+ numnotes = f->readInt(2);
+ note = new unsigned long [numnotes];
+ for(i = 0; i < numnotes; i++) note[i] = f->readInt(4);
+ fp.close(f);
+
+ if(!trchan[11]) {
+ drumstat = 0;
+ numchans = 9;
+ } else {
+ drumstat = 32;
+ numchans = 6;
+ }
+
+ rewind(0);
+ AdPlug_LogWrite("--- CksmPlayer::load ---\n");
+ return true;
+}
+
+bool CksmPlayer::update()
+{
+ int quanter,chan,drumnum,freq,track,volevel,volval;
+ unsigned int i,j,bufnum;
+ unsigned long temp,templong;
+
+ count++;
+ if (count >= countstop)
+ {
+ bufnum = 0;
+ while (count >= countstop)
+ {
+ templong = note[nownote];
+ track = (int)((templong>>8)&15);
+ if ((templong&192) == 0)
+ {
+ i = 0;
+
+ while ((i < numchans) &&
+ ((chanfreq[i] != (templong&63)) ||
+ (chantrack[i] != ((templong>>8)&15))))
+ i++;
+ if (i < numchans)
+ {
+ databuf[bufnum] = (char)0; bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)((adlibfreq[templong&63]>>8)&223); bufnum++;
+ chanfreq[i] = 0;
+ chanage[i] = 0;
+ }
+ }
+ else
+ {
+ volevel = trvol[track];
+ if ((templong&192) == 128)
+ {
+ volevel -= 4;
+ if (volevel < 0)
+ volevel = 0;
+ }
+ if ((templong&192) == 192)
+ {
+ volevel += 4;
+ if (volevel > 63)
+ volevel = 63;
+ }
+ if (track < 11)
+ {
+ temp = 0;
+ i = numchans;
+ for(j=0;j<numchans;j++)
+ if ((countstop - chanage[j] >= temp) && (chantrack[j] == track))
+ {
+ temp = countstop - chanage[j];
+ i = j;
+ }
+ if (i < numchans)
+ {
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)0; bufnum++;
+ volval = (inst[trinst[track]][1]&192)+(volevel^63);
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0x40+op_table[i]+3); bufnum++;
+ databuf[bufnum] = (unsigned char)volval; bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xa0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)(adlibfreq[templong&63]&255); bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++;
+ databuf[bufnum] = (unsigned char)((adlibfreq[templong&63]>>8)|32); bufnum++;
+ chanfreq[i] = templong&63;
+ chanage[i] = countstop;
+ }
+ }
+ else if ((drumstat&32) > 0)
+ {
+ freq = adlibfreq[templong&63];
+ switch(track)
+ {
+ case 11: drumnum = 16; chan = 6; freq -= 2048; break;
+ case 12: drumnum = 8; chan = 7; freq -= 2048; break;
+ case 13: drumnum = 4; chan = 8; break;
+ case 14: drumnum = 2; chan = 8; break;
+ case 15: drumnum = 1; chan = 7; freq -= 2048; break;
+ }
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xa0+chan); bufnum++;
+ databuf[bufnum] = (unsigned char)(freq&255); bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xb0+chan); bufnum++;
+ databuf[bufnum] = (unsigned char)((freq>>8)&223); bufnum++;
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xbd); bufnum++;
+ databuf[bufnum] = (unsigned char)(drumstat&(255-drumnum)); bufnum++;
+ drumstat |= drumnum;
+ if ((track == 11) || (track == 12) || (track == 14))
+ {
+ volval = (inst[trinst[track]][1]&192)+(volevel^63);
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0x40+op_table[chan]+3); bufnum++;
+ databuf[bufnum] = (unsigned char)(volval); bufnum++;
+ }
+ else
+ {
+ volval = (inst[trinst[track]][6]&192)+(volevel^63);
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0x40+op_table[chan]); bufnum++;
+ databuf[bufnum] = (unsigned char)(volval); bufnum++;
+ }
+ databuf[bufnum] = (char)0, bufnum++;
+ databuf[bufnum] = (unsigned char)(0xbd); bufnum++;
+ databuf[bufnum] = (unsigned char)(drumstat); bufnum++;
+ }
+ }
+ nownote++;
+ if (nownote >= numnotes) {
+ nownote = 0;
+ songend = true;
+ }
+ templong = note[nownote];
+ if (nownote == 0)
+ count = (templong>>12)-1;
+ quanter = (240/trquant[(templong>>8)&15]);
+ countstop = (((templong>>12)+(quanter>>1)) / quanter) * quanter;
+ }
+ for(i=0;i<bufnum;i+=3)
+ opl->write(databuf[i+1],databuf[i+2]);
+ }
+ return !songend;
+}
+
+void CksmPlayer::rewind(int subsong)
+{
+ unsigned int i,j,k;
+ unsigned char instbuf[11];
+ unsigned long templong;
+
+ songend = false;
+ opl->init(); opl->write(1,32); opl->write(4,0); opl->write(8,0); opl->write(0xbd,drumstat);
+
+ if (trchan[11] == 1) {
+ for(i=0;i<11;i++)
+ instbuf[i] = inst[trinst[11]][i];
+ instbuf[1] = ((instbuf[1]&192)|(trvol[11])^63);
+ setinst(6,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ for(i=0;i<5;i++)
+ instbuf[i] = inst[trinst[12]][i];
+ for(i=5;i<11;i++)
+ instbuf[i] = inst[trinst[15]][i];
+ instbuf[1] = ((instbuf[1]&192)|(trvol[12])^63);
+ instbuf[6] = ((instbuf[6]&192)|(trvol[15])^63);
+ setinst(7,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ for(i=0;i<5;i++)
+ instbuf[i] = inst[trinst[14]][i];
+ for(i=5;i<11;i++)
+ instbuf[i] = inst[trinst[13]][i];
+ instbuf[1] = ((instbuf[1]&192)|(trvol[14])^63);
+ instbuf[6] = ((instbuf[6]&192)|(trvol[13])^63);
+ setinst(8,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ }
+
+ for(i=0;i<numchans;i++)
+ {
+ chantrack[i] = 0;
+ chanage[i] = 0;
+ }
+ j = 0;
+ for(i=0;i<16;i++)
+ if ((trchan[i] > 0) && (j < numchans))
+ {
+ k = trchan[i];
+ while ((j < numchans) && (k > 0))
+ {
+ chantrack[j] = i;
+ k--;
+ j++;
+ }
+ }
+ for(i=0;i<numchans;i++)
+ {
+ for(j=0;j<11;j++)
+ instbuf[j] = inst[trinst[chantrack[i]]][j];
+ instbuf[1] = ((instbuf[1]&192)|(63-trvol[chantrack[i]]));
+ setinst(i,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]);
+ chanfreq[i] = 0;
+ }
+ k = 0;
+ templong = *note;
+ count = (templong>>12)-1;
+ countstop = (templong>>12)-1;
+ nownote = 0;
+}
+
+std::string CksmPlayer::getinstrument(unsigned int n)
+{
+ if(trchan[n])
+ return std::string(instname[trinst[n]]);
+ else
+ return std::string();
+}
+
+/*** private methods *************************************/
+
+void CksmPlayer::loadinsts(binistream *f)
+{
+ int i, j;
+
+ for(i = 0; i < 256; i++) {
+ f->readString(instname[i], 20);
+ for(j = 0; j < 11; j++) inst[i][j] = f->readInt(1);
+ f->ignore(2);
+ }
+}
+
+void CksmPlayer::setinst(int chan,
+ unsigned char v0,unsigned char v1,unsigned char v2,
+ unsigned char v3,unsigned char v4,unsigned char v5,
+ unsigned char v6,unsigned char v7,unsigned char v8,
+ unsigned char v9,unsigned char v10)
+{
+ int offs;
+
+ opl->write(0xa0+chan,0);
+ opl->write(0xb0+chan,0);
+ opl->write(0xc0+chan,v10);
+ offs = op_table[chan];
+ opl->write(0x20+offs,v5);
+ opl->write(0x40+offs,v6);
+ opl->write(0x60+offs,v7);
+ opl->write(0x80+offs,v8);
+ opl->write(0xe0+offs,v9);
+ offs+=3;
+ opl->write(0x20+offs,v0);
+ opl->write(0x40+offs,v1);
+ opl->write(0x60+offs,v2);
+ opl->write(0x80+offs,v3);
+ opl->write(0xe0+offs,v4);
+}
diff --git a/plugins/adplug/adplug/ksm.h b/plugins/adplug/adplug/ksm.h
new file mode 100644
index 00000000..98d643ea
--- /dev/null
+++ b/plugins/adplug/adplug/ksm.h
@@ -0,0 +1,62 @@
+/*
+ * 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
+ *
+ * ksm.h - KSM Player for AdPlug by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CksmPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CksmPlayer(Copl *newopl)
+ : CPlayer(newopl), note(0)
+ { };
+ ~CksmPlayer()
+ { if(note) delete [] note; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return 240.0f; };
+
+ std::string gettype()
+ { return std::string("Ken Silverman's Music Format"); };
+ unsigned int getinstruments()
+ { return 16; };
+ std::string getinstrument(unsigned int n);
+
+private:
+ static const unsigned int adlibfreq[63];
+
+ unsigned long count,countstop,chanage[18],*note;
+ unsigned short numnotes;
+ unsigned int nownote,numchans,drumstat;
+ unsigned char trinst[16],trquant[16],trchan[16],trvol[16],inst[256][11],databuf[2048],chanfreq[18],chantrack[18];
+ char instname[256][20];
+
+ bool songend;
+
+ void loadinsts(binistream *f);
+ void setinst(int chan,unsigned char v0,unsigned char v1,unsigned char v2,unsigned char v3,
+ unsigned char v4,unsigned char v5,unsigned char v6,unsigned char v7,
+ unsigned char v8,unsigned char v9,unsigned char v10);
+};
diff --git a/plugins/adplug/adplug/lds.cpp b/plugins/adplug/adplug/lds.cpp
new file mode 100644
index 00000000..fb70a067
--- /dev/null
+++ b/plugins/adplug/adplug/lds.cpp
@@ -0,0 +1,676 @@
+/*
+ * 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
+ *
+ * lds.cpp - LOUDNESS Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "lds.h"
+#include "debug.h"
+
+// Note frequency table (16 notes / octave)
+const unsigned short CldsPlayer::frequency[] = {
+ 343, 344, 345, 347, 348, 349, 350, 352, 353, 354, 356, 357, 358,
+ 359, 361, 362, 363, 365, 366, 367, 369, 370, 371, 373, 374, 375,
+ 377, 378, 379, 381, 382, 384, 385, 386, 388, 389, 391, 392, 393,
+ 395, 396, 398, 399, 401, 402, 403, 405, 406, 408, 409, 411, 412,
+ 414, 415, 417, 418, 420, 421, 423, 424, 426, 427, 429, 430, 432,
+ 434, 435, 437, 438, 440, 442, 443, 445, 446, 448, 450, 451, 453,
+ 454, 456, 458, 459, 461, 463, 464, 466, 468, 469, 471, 473, 475,
+ 476, 478, 480, 481, 483, 485, 487, 488, 490, 492, 494, 496, 497,
+ 499, 501, 503, 505, 506, 508, 510, 512, 514, 516, 518, 519, 521,
+ 523, 525, 527, 529, 531, 533, 535, 537, 538, 540, 542, 544, 546,
+ 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, 571, 573,
+ 575, 577, 579, 581, 583, 585, 587, 589, 591, 594, 596, 598, 600,
+ 602, 604, 607, 609, 611, 613, 615, 618, 620, 622, 624, 627, 629,
+ 631, 633, 636, 638, 640, 643, 645, 647, 650, 652, 654, 657, 659,
+ 662, 664, 666, 669, 671, 674, 676, 678, 681, 683
+};
+
+// Vibrato (sine) table
+const unsigned char CldsPlayer::vibtab[] = {
+ 0, 13, 25, 37, 50, 62, 74, 86, 98, 109, 120, 131, 142, 152, 162,
+ 171, 180, 189, 197, 205, 212, 219, 225, 231, 236, 240, 244, 247,
+ 250, 252, 254, 255, 255, 255, 254, 252, 250, 247, 244, 240, 236,
+ 231, 225, 219, 212, 205, 197, 189, 180, 171, 162, 152, 142, 131,
+ 120, 109, 98, 86, 74, 62, 50, 37, 25, 13
+};
+
+// Tremolo (sine * sine) table
+const unsigned char CldsPlayer::tremtab[] = {
+ 0, 0, 1, 1, 2, 4, 5, 7, 10, 12, 15, 18, 21, 25, 29, 33, 37, 42, 47,
+ 52, 57, 62, 67, 73, 79, 85, 90, 97, 103, 109, 115, 121, 128, 134,
+ 140, 146, 152, 158, 165, 170, 176, 182, 188, 193, 198, 203, 208,
+ 213, 218, 222, 226, 230, 234, 237, 240, 243, 245, 248, 250, 251,
+ 253, 254, 254, 255, 255, 255, 254, 254, 253, 251, 250, 248, 245,
+ 243, 240, 237, 234, 230, 226, 222, 218, 213, 208, 203, 198, 193,
+ 188, 182, 176, 170, 165, 158, 152, 146, 140, 134, 127, 121, 115,
+ 109, 103, 97, 90, 85, 79, 73, 67, 62, 57, 52, 47, 42, 37, 33, 29,
+ 25, 21, 18, 15, 12, 10, 7, 5, 4, 2, 1, 1, 0
+};
+
+// 'maxsound' is maximum number of patches (instruments)
+// 'maxpos' is maximum number of entries in position list (orderlist)
+const unsigned short CldsPlayer::maxsound = 0x3f, CldsPlayer::maxpos = 0xff;
+
+/*** public methods *************************************/
+
+CldsPlayer::CldsPlayer(Copl *newopl)
+ : CPlayer(newopl), soundbank(0), positions(0), patterns(0)
+{
+}
+
+CldsPlayer::~CldsPlayer()
+{
+ if(soundbank) delete [] soundbank;
+ if(positions) delete [] positions;
+ if(patterns) delete [] patterns;
+}
+
+bool CldsPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f;
+ unsigned int i, j;
+ SoundBank *sb;
+
+ // file validation section (actually just an extension check)
+ if(!fp.extension(filename, ".lds")) return false;
+ f = fp.open(filename); if(!f) return false;
+
+ // file load section (header)
+ mode = f->readInt(1);
+ if(mode > 2) { fp.close(f); return false; }
+ speed = f->readInt(2);
+ tempo = f->readInt(1);
+ pattlen = f->readInt(1);
+ for(i = 0; i < 9; i++) chandelay[i] = f->readInt(1);
+ regbd = f->readInt(1);
+
+ // load patches
+ numpatch = f->readInt(2);
+ soundbank = new SoundBank[numpatch];
+ for(i = 0; i < numpatch; i++) {
+ sb = &soundbank[i];
+ sb->mod_misc = f->readInt(1); sb->mod_vol = f->readInt(1);
+ sb->mod_ad = f->readInt(1); sb->mod_sr = f->readInt(1);
+ sb->mod_wave = f->readInt(1); sb->car_misc = f->readInt(1);
+ sb->car_vol = f->readInt(1); sb->car_ad = f->readInt(1);
+ sb->car_sr = f->readInt(1); sb->car_wave = f->readInt(1);
+ sb->feedback = f->readInt(1); sb->keyoff = f->readInt(1);
+ sb->portamento = f->readInt(1); sb->glide = f->readInt(1);
+ sb->finetune = f->readInt(1); sb->vibrato = f->readInt(1);
+ sb->vibdelay = f->readInt(1); sb->mod_trem = f->readInt(1);
+ sb->car_trem = f->readInt(1); sb->tremwait = f->readInt(1);
+ sb->arpeggio = f->readInt(1);
+ for(j = 0; j < 12; j++) sb->arp_tab[j] = f->readInt(1);
+ sb->start = f->readInt(2); sb->size = f->readInt(2);
+ sb->fms = f->readInt(1); sb->transp = f->readInt(2);
+ sb->midinst = f->readInt(1); sb->midvelo = f->readInt(1);
+ sb->midkey = f->readInt(1); sb->midtrans = f->readInt(1);
+ sb->middum1 = f->readInt(1); sb->middum2 = f->readInt(1);
+ }
+
+ // load positions
+ numposi = f->readInt(2);
+ positions = new Position[9 * numposi];
+ for(i = 0; i < numposi; i++)
+ for(j = 0; j < 9; j++) {
+ /*
+ * patnum is a pointer inside the pattern space, but patterns are 16bit
+ * word fields anyway, so it ought to be an even number (hopefully) and
+ * we can just divide it by 2 to get our array index of 16bit words.
+ */
+ positions[i * 9 + j].patnum = f->readInt(2) / 2;
+ positions[i * 9 + j].transpose = f->readInt(1);
+ }
+
+ AdPlug_LogWrite("CldsPlayer::load(\"%s\",fp): loading LOUDNESS file: mode = "
+ "%d, pattlen = %d, numpatch = %d, numposi = %d\n",
+ filename.c_str(), mode, pattlen, numpatch, numposi);
+
+ // load patterns
+ f->ignore(2); // ignore # of digital sounds (not played by this player)
+ patterns = new unsigned short[(fp.filesize(f) - f->pos()) / 2 + 1];
+ for(i = 0; !f->eof(); i++)
+ patterns[i] = f->readInt(2);
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CldsPlayer::update()
+{
+ unsigned short comword, freq, octave, chan, tune, wibc, tremc, arpreg;
+ bool vbreak;
+ unsigned char level, regnum, comhi, comlo;
+ int i;
+ Channel *c;
+
+ if(!playing) return false;
+
+ // handle fading
+ if(fadeonoff)
+ if(fadeonoff <= 128) {
+ if(allvolume > fadeonoff || allvolume == 0)
+ allvolume -= fadeonoff;
+ else {
+ allvolume = 1;
+ fadeonoff = 0;
+ if(hardfade != 0) {
+ playing = false;
+ hardfade = 0;
+ for(i = 0; i < 9; i++)
+ channel[i].keycount = 1;
+ }
+ }
+ } else
+ if(((allvolume + (0x100 - fadeonoff)) & 0xff) <= mainvolume)
+ allvolume += 0x100 - fadeonoff;
+ else {
+ allvolume = mainvolume;
+ fadeonoff = 0;
+ }
+
+ // handle channel delay
+ for(chan = 0; chan < 9; chan++) {
+ c = &channel[chan];
+ if(c->chancheat.chandelay)
+ if(!(--c->chancheat.chandelay))
+ playsound(c->chancheat.sound, chan, c->chancheat.high);
+ }
+
+ // handle notes
+ if(!tempo_now) {
+ vbreak = false;
+ for(chan = 0; chan < 9; chan++) {
+ c = &channel[chan];
+ if(!c->packwait) {
+ unsigned short patnum = positions[posplay * 9 + chan].patnum;
+ unsigned char transpose = positions[posplay * 9 + chan].transpose;
+
+ comword = patterns[patnum + c->packpos];
+ comhi = comword >> 8; comlo = comword & 0xff;
+ if(comword)
+ if(comhi == 0x80)
+ c->packwait = comlo;
+ else
+ if(comhi >= 0x80) {
+ switch(comhi) {
+ case 0xff:
+ c->volcar = (((c->volcar & 0x3f) * comlo) >> 6) & 0x3f;
+ if(fmchip[0xc0 + chan] & 1)
+ c->volmod = (((c->volmod & 0x3f) * comlo) >> 6) & 0x3f;
+ break;
+ case 0xfe:
+ tempo = comword & 0x3f;
+ break;
+ case 0xfd:
+ c->nextvol = comlo;
+ break;
+ case 0xfc:
+ playing = false;
+ // in real player there's also full keyoff here, but we don't need it
+ break;
+ case 0xfb:
+ c->keycount = 1;
+ break;
+ case 0xfa:
+ vbreak = true;
+ jumppos = (posplay + 1) & maxpos;
+ break;
+ case 0xf9:
+ vbreak = true;
+ jumppos = comlo & maxpos;
+ jumping = 1;
+ if(jumppos < posplay) songlooped = true;
+ break;
+ case 0xf8:
+ c->lasttune = 0;
+ break;
+ case 0xf7:
+ c->vibwait = 0;
+ // PASCAL: c->vibspeed = ((comlo >> 4) & 15) + 2;
+ c->vibspeed = (comlo >> 4) + 2;
+ c->vibrate = (comlo & 15) + 1;
+ break;
+ case 0xf6:
+ c->glideto = comlo;
+ break;
+ case 0xf5:
+ c->finetune = comlo;
+ break;
+ case 0xf4:
+ if(!hardfade) {
+ allvolume = mainvolume = comlo;
+ fadeonoff = 0;
+ }
+ break;
+ case 0xf3:
+ if(!hardfade) fadeonoff = comlo;
+ break;
+ case 0xf2:
+ c->trmstay = comlo;
+ break;
+ case 0xf1: // panorama
+ case 0xf0: // progch
+ // MIDI commands (unhandled)
+ AdPlug_LogWrite("CldsPlayer(): not handling MIDI command 0x%x, "
+ "value = 0x%x\n", comhi);
+ break;
+ default:
+ if(comhi < 0xa0)
+ c->glideto = comhi & 0x1f;
+ else
+ AdPlug_LogWrite("CldsPlayer(): unknown command 0x%x encountered!"
+ " value = 0x%x\n", comhi, comlo);
+ break;
+ }
+ } else {
+ unsigned char sound;
+ unsigned short high;
+ signed char transp = transpose & 127;
+
+ /*
+ * Originally, in assembler code, the player first shifted
+ * logically left the transpose byte by 1 and then shifted
+ * arithmetically right the same byte to achieve the final,
+ * signed transpose value. Since we can't do arithmetic shifts
+ * in C, we just duplicate the 7th bit into the 8th one and
+ * discard the 8th one completely.
+ */
+
+ if(transpose & 64) transp |= 128;
+
+ if(transpose & 128) {
+ sound = (comlo + transp) & maxsound;
+ high = comhi << 4;
+ } else {
+ sound = comlo & maxsound;
+ high = (comhi + transp) << 4;
+ }
+
+ /*
+ PASCAL:
+ sound = comlo & maxsound;
+ high = (comhi + (((transpose + 0x24) & 0xff) - 0x24)) << 4;
+ */
+
+ if(!chandelay[chan])
+ playsound(sound, chan, high);
+ else {
+ c->chancheat.chandelay = chandelay[chan];
+ c->chancheat.sound = sound;
+ c->chancheat.high = high;
+ }
+ }
+
+ c->packpos++;
+ } else
+ c->packwait--;
+ }
+
+ tempo_now = tempo;
+ /*
+ The continue table is updated here, but this is only used in the
+ original player, which can be paused in the middle of a song and then
+ unpaused. Since AdPlug does all this for us automatically, we don't
+ have a continue table here. The continue table update code is noted
+ here for reference only.
+
+ if(!pattplay) {
+ conttab[speed & maxcont].position = posplay & 0xff;
+ conttab[speed & maxcont].tempo = tempo;
+ }
+ */
+ pattplay++;
+ if(vbreak) {
+ pattplay = 0;
+ for(i = 0; i < 9; i++) channel[i].packpos = channel[i].packwait = 0;
+ posplay = jumppos;
+ } else
+ if(pattplay >= pattlen) {
+ pattplay = 0;
+ for(i = 0; i < 9; i++) channel[i].packpos = channel[i].packwait = 0;
+ posplay = (posplay + 1) & maxpos;
+ }
+ } else
+ tempo_now--;
+
+ // make effects
+ for(chan = 0; chan < 9; chan++) {
+ c = &channel[chan];
+ regnum = op_table[chan];
+ if(c->keycount > 0) {
+ if(c->keycount == 1)
+ setregs_adv(0xb0 + chan, 0xdf, 0);
+ c->keycount--;
+ }
+
+ // arpeggio
+ if(c->arp_size == 0)
+ arpreg = 0;
+ else {
+ arpreg = c->arp_tab[c->arp_pos] << 4;
+ if(arpreg == 0x800) {
+ if(c->arp_pos > 0) c->arp_tab[0] = c->arp_tab[c->arp_pos - 1];
+ c->arp_size = 1; c->arp_pos = 0;
+ arpreg = c->arp_tab[0] << 4;
+ }
+
+ if(c->arp_count == c->arp_speed) {
+ c->arp_pos++;
+ if(c->arp_pos >= c->arp_size) c->arp_pos = 0;
+ c->arp_count = 0;
+ } else
+ c->arp_count++;
+ }
+
+ // glide & portamento
+ if(c->lasttune && (c->lasttune != c->gototune)) {
+ if(c->lasttune > c->gototune) {
+ if(c->lasttune - c->gototune < c->portspeed)
+ c->lasttune = c->gototune;
+ else
+ c->lasttune -= c->portspeed;
+ } else {
+ if(c->gototune - c->lasttune < c->portspeed)
+ c->lasttune = c->gototune;
+ else
+ c->lasttune += c->portspeed;
+ }
+
+ if(arpreg >= 0x800)
+ arpreg = c->lasttune - (arpreg ^ 0xff0) - 16;
+ else
+ arpreg += c->lasttune;
+
+ freq = frequency[arpreg % (12 * 16)];
+ octave = arpreg / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ } else {
+ // vibrato
+ if(!c->vibwait) {
+ if(c->vibrate) {
+ wibc = vibtab[c->vibcount & 0x3f] * c->vibrate;
+
+ if((c->vibcount & 0x40) == 0)
+ tune = c->lasttune + (wibc >> 8);
+ else
+ tune = c->lasttune - (wibc >> 8);
+
+ if(arpreg >= 0x800)
+ tune = tune - (arpreg ^ 0xff0) - 16;
+ else
+ tune += arpreg;
+
+ freq = frequency[tune % (12 * 16)];
+ octave = tune / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ c->vibcount += c->vibspeed;
+ } else
+ if(c->arp_size != 0) { // no vibrato, just arpeggio
+ if(arpreg >= 0x800)
+ tune = c->lasttune - (arpreg ^ 0xff0) - 16;
+ else
+ tune = c->lasttune + arpreg;
+
+ freq = frequency[tune % (12 * 16)];
+ octave = tune / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ }
+ } else { // no vibrato, just arpeggio
+ c->vibwait--;
+
+ if(c->arp_size != 0) {
+ if(arpreg >= 0x800)
+ tune = c->lasttune - (arpreg ^ 0xff0) - 16;
+ else
+ tune = c->lasttune + arpreg;
+
+ freq = frequency[tune % (12 * 16)];
+ octave = tune / (12 * 16) - 1;
+ setregs(0xa0 + chan, freq & 0xff);
+ setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf);
+ }
+ }
+ }
+
+ // tremolo (modulator)
+ if(!c->trmwait) {
+ if(c->trmrate) {
+ tremc = tremtab[c->trmcount & 0x7f] * c->trmrate;
+ if((tremc >> 8) <= (c->volmod & 0x3f))
+ level = (c->volmod & 0x3f) - (tremc >> 8);
+ else
+ level = 0;
+
+ if(allvolume != 0 && (fmchip[0xc0 + chan] & 1))
+ setregs_adv(0x40 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f);
+ else
+ setregs_adv(0x40 + regnum, 0xc0, level ^ 0x3f);
+
+ c->trmcount += c->trmspeed;
+ } else
+ if(allvolume != 0 && (fmchip[0xc0 + chan] & 1))
+ setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ else
+ setregs_adv(0x40 + regnum, 0xc0, (c->volmod ^ 0x3f) & 0x3f);
+ } else {
+ c->trmwait--;
+ if(allvolume != 0 && (fmchip[0xc0 + chan] & 1))
+ setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ }
+
+ // tremolo (carrier)
+ if(!c->trcwait) {
+ if(c->trcrate) {
+ tremc = tremtab[c->trccount & 0x7f] * c->trcrate;
+ if((tremc >> 8) <= (c->volcar & 0x3f))
+ level = (c->volcar & 0x3f) - (tremc >> 8);
+ else
+ level = 0;
+
+ if(allvolume != 0)
+ setregs_adv(0x43 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f);
+ else
+ setregs_adv(0x43 + regnum, 0xc0, level ^ 0x3f);
+ c->trccount += c->trcspeed;
+ } else
+ if(allvolume != 0)
+ setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ else
+ setregs_adv(0x43 + regnum, 0xc0, (c->volcar ^ 0x3f) & 0x3f);
+ } else {
+ c->trcwait--;
+ if(allvolume != 0)
+ setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f);
+ }
+ }
+
+ return (!playing || songlooped) ? false : true;
+}
+
+void CldsPlayer::rewind(int subsong)
+{
+ int i;
+
+ // init all with 0
+ tempo_now = 3; playing = true; songlooped = false;
+ jumping = fadeonoff = allvolume = hardfade = pattplay = posplay = jumppos =
+ mainvolume = 0;
+ memset(channel, 0, sizeof(channel));
+ memset(fmchip, 0, sizeof(fmchip));
+
+ // OPL2 init
+ opl->init(); // Reset OPL chip
+ opl->write(1, 0x20);
+ opl->write(8, 0);
+ opl->write(0xbd, regbd);
+
+ for(i = 0; i < 9; i++) {
+ opl->write(0x20 + op_table[i], 0);
+ opl->write(0x23 + op_table[i], 0);
+ opl->write(0x40 + op_table[i], 0x3f);
+ opl->write(0x43 + op_table[i], 0x3f);
+ opl->write(0x60 + op_table[i], 0xff);
+ opl->write(0x63 + op_table[i], 0xff);
+ opl->write(0x80 + op_table[i], 0xff);
+ opl->write(0x83 + op_table[i], 0xff);
+ opl->write(0xe0 + op_table[i], 0);
+ opl->write(0xe3 + op_table[i], 0);
+ opl->write(0xa0 + i, 0);
+ opl->write(0xb0 + i, 0);
+ opl->write(0xc0 + i, 0);
+ }
+}
+
+/*** private methods *************************************/
+
+void CldsPlayer::playsound(int inst_number, int channel_number, int tunehigh)
+{
+ Channel *c = &channel[channel_number]; // current channel
+ SoundBank *i = &soundbank[inst_number]; // current instrument
+ unsigned int regnum = op_table[channel_number]; // channel's OPL2 register
+ unsigned char volcalc, octave;
+ unsigned short freq;
+
+ // set fine tune
+ tunehigh += ((i->finetune + c->finetune + 0x80) & 0xff) - 0x80;
+
+ // arpeggio handling
+ if(!i->arpeggio) {
+ unsigned short arpcalc = i->arp_tab[0] << 4;
+
+ if(arpcalc > 0x800)
+ tunehigh = tunehigh - (arpcalc ^ 0xff0) - 16;
+ else
+ tunehigh += arpcalc;
+ }
+
+ // glide handling
+ if(c->glideto != 0) {
+ c->gototune = tunehigh;
+ c->portspeed = c->glideto;
+ c->glideto = c->finetune = 0;
+ return;
+ }
+
+ // set modulator registers
+ setregs(0x20 + regnum, i->mod_misc);
+ volcalc = i->mod_vol;
+ if(!c->nextvol || !(i->feedback & 1))
+ c->volmod = volcalc;
+ else
+ c->volmod = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6));
+
+ if((i->feedback & 1) == 1 && allvolume != 0)
+ setregs(0x40 + regnum, ((c->volmod & 0xc0) | (((c->volmod & 0x3f) * allvolume) >> 8)) ^ 0x3f);
+ else
+ setregs(0x40 + regnum, c->volmod ^ 0x3f);
+ setregs(0x60 + regnum, i->mod_ad);
+ setregs(0x80 + regnum, i->mod_sr);
+ setregs(0xe0 + regnum, i->mod_wave);
+
+ // Set carrier registers
+ setregs(0x23 + regnum, i->car_misc);
+ volcalc = i->car_vol;
+ if(!c->nextvol)
+ c->volcar = volcalc;
+ else
+ c->volcar = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6));
+
+ if(allvolume)
+ setregs(0x43 + regnum, ((c->volcar & 0xc0) | (((c->volcar & 0x3f) * allvolume) >> 8)) ^ 0x3f);
+ else
+ setregs(0x43 + regnum, c->volcar ^ 0x3f);
+ setregs(0x63 + regnum, i->car_ad);
+ setregs(0x83 + regnum, i->car_sr);
+ setregs(0xe3 + regnum, i->car_wave);
+ setregs(0xc0 + channel_number, i->feedback);
+ setregs_adv(0xb0 + channel_number, 0xdf, 0); // key off
+
+ freq = frequency[tunehigh % (12 * 16)];
+ octave = tunehigh / (12 * 16) - 1;
+ if(!i->glide) {
+ if(!i->portamento || !c->lasttune) {
+ setregs(0xa0 + channel_number, freq & 0xff);
+ setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8));
+ c->lasttune = c->gototune = tunehigh;
+ } else {
+ c->gototune = tunehigh;
+ c->portspeed = i->portamento;
+ setregs_adv(0xb0 + channel_number, 0xdf, 0x20); // key on
+ }
+ } else {
+ setregs(0xa0 + channel_number, freq & 0xff);
+ setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8));
+ c->lasttune = tunehigh;
+ c->gototune = tunehigh + ((i->glide + 0x80) & 0xff) - 0x80; // set destination
+ c->portspeed = i->portamento;
+ }
+
+ if(!i->vibrato)
+ c->vibwait = c->vibspeed = c->vibrate = 0;
+ else {
+ c->vibwait = i->vibdelay;
+ // PASCAL: c->vibspeed = ((i->vibrato >> 4) & 15) + 1;
+ c->vibspeed = (i->vibrato >> 4) + 2;
+ c->vibrate = (i->vibrato & 15) + 1;
+ }
+
+ if(!(c->trmstay & 0xf0)) {
+ c->trmwait = (i->tremwait & 0xf0) >> 3;
+ // PASCAL: c->trmspeed = (i->mod_trem >> 4) & 15;
+ c->trmspeed = i->mod_trem >> 4;
+ c->trmrate = i->mod_trem & 15;
+ c->trmcount = 0;
+ }
+
+ if(!(c->trmstay & 0x0f)) {
+ c->trcwait = (i->tremwait & 15) << 1;
+ // PASCAL: c->trcspeed = (i->car_trem >> 4) & 15;
+ c->trcspeed = i->car_trem >> 4;
+ c->trcrate = i->car_trem & 15;
+ c->trccount = 0;
+ }
+
+ c->arp_size = i->arpeggio & 15;
+ c->arp_speed = i->arpeggio >> 4;
+ memcpy(c->arp_tab, i->arp_tab, 12);
+ c->keycount = i->keyoff;
+ c->nextvol = c->glideto = c->finetune = c->vibcount = c->arp_pos = c->arp_count = 0;
+}
+
+inline void CldsPlayer::setregs(unsigned char reg, unsigned char val)
+{
+ if(fmchip[reg] == val) return;
+
+ fmchip[reg] = val;
+ opl->write(reg, val);
+}
+
+inline void CldsPlayer::setregs_adv(unsigned char reg, unsigned char mask,
+ unsigned char val)
+{
+ setregs(reg, (fmchip[reg] & mask) | val);
+}
diff --git a/plugins/adplug/adplug/lds.h b/plugins/adplug/adplug/lds.h
new file mode 100644
index 00000000..ec61ad29
--- /dev/null
+++ b/plugins/adplug/adplug/lds.h
@@ -0,0 +1,91 @@
+/*
+ * 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
+ *
+ * lds.h - LOUDNESS Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CldsPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl) { return new CldsPlayer(newopl); }
+
+ CldsPlayer(Copl *newopl);
+ virtual ~CldsPlayer();
+
+ bool load(const std::string &fn, const CFileProvider &fp);
+ virtual bool update();
+ virtual void rewind(int subsong = -1);
+ float getrefresh() { return 70.0f; }
+
+ std::string gettype() { return std::string("LOUDNESS Sound System"); }
+ unsigned int getorders() { return numposi; }
+ unsigned int getorder() { return posplay; }
+ unsigned int getrow() { return pattplay; }
+ unsigned int getspeed() { return speed; }
+ unsigned int getinstruments() { return numpatch; }
+
+ private:
+ typedef struct {
+ unsigned char mod_misc, mod_vol, mod_ad, mod_sr, mod_wave,
+ car_misc, car_vol, car_ad, car_sr, car_wave, feedback, keyoff,
+ portamento, glide, finetune, vibrato, vibdelay, mod_trem, car_trem,
+ tremwait, arpeggio, arp_tab[12];
+ unsigned short start, size;
+ unsigned char fms;
+ unsigned short transp;
+ unsigned char midinst, midvelo, midkey, midtrans, middum1, middum2;
+ } SoundBank;
+
+ typedef struct {
+ unsigned short gototune, lasttune, packpos;
+ unsigned char finetune, glideto, portspeed, nextvol, volmod, volcar,
+ vibwait, vibspeed, vibrate, trmstay, trmwait, trmspeed, trmrate, trmcount,
+ trcwait, trcspeed, trcrate, trccount, arp_size, arp_speed, keycount,
+ vibcount, arp_pos, arp_count, packwait, arp_tab[12];
+
+ struct {
+ unsigned char chandelay, sound;
+ unsigned short high;
+ } chancheat;
+ } Channel;
+
+ typedef struct {
+ unsigned short patnum;
+ unsigned char transpose;
+ } Position;
+
+ static const unsigned short frequency[];
+ static const unsigned char vibtab[], tremtab[];
+ static const unsigned short maxsound, maxpos;
+
+ SoundBank *soundbank;
+ Channel channel[9];
+ Position *positions;
+ unsigned char fmchip[0xff], jumping, fadeonoff, allvolume, hardfade,
+ tempo_now, pattplay, tempo, regbd, chandelay[9], mode, pattlen;
+ unsigned short posplay, jumppos, *patterns, speed;
+ bool playing, songlooped;
+ unsigned int numpatch, numposi, patterns_size, mainvolume;
+
+ void playsound(int inst_number, int channel_number, int tunehigh);
+ inline void setregs(unsigned char reg, unsigned char val);
+ inline void setregs_adv(unsigned char reg, unsigned char mask,
+ unsigned char val);
+};
diff --git a/plugins/adplug/adplug/mad.cpp b/plugins/adplug/adplug/mad.cpp
new file mode 100644
index 00000000..d5a60c43
--- /dev/null
+++ b/plugins/adplug/adplug/mad.cpp
@@ -0,0 +1,127 @@
+/*
+ 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
+
+ mad.cpp - MAD loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include <string.h>
+#include "mad.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CPlayer *CmadLoader::factory(Copl *newopl)
+{
+ return new CmadLoader(newopl);
+}
+
+bool CmadLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ const unsigned char conv_inst[10] = { 2,1,10,9,4,3,6,5,8,7 };
+ unsigned int i, j, k, t = 0;
+
+ // 'MAD+' - signed ?
+ char id[4]; f->readString(id, 4);
+ if (strncmp(id,"MAD+",4)) { fp.close(f); return false; }
+
+ // load instruments
+ for(i = 0; i < 9; i++) {
+ f->readString(instruments[i].name, 8);
+ for(j = 0; j < 12; j++) instruments[i].data[j] = f->readInt(1);
+ }
+
+ f->ignore(1);
+
+ // data for Protracker
+ length = f->readInt(1); nop = f->readInt(1); timer = f->readInt(1);
+
+ // init CmodPlayer
+ realloc_instruments(9);
+ realloc_order(length);
+ realloc_patterns(nop,32,9);
+ init_trackord();
+
+ // load tracks
+ for(i = 0; i < nop; i++)
+ for(k = 0; k < 32; k++)
+ for(j = 0; j < 9; j++) {
+ t = i * 9 + j;
+
+ // read event
+ unsigned char event = f->readInt(1);
+
+ // convert event
+ if (event < 0x61)
+ tracks[t][k].note = event;
+ if (event == 0xFF) // 0xFF: Release note
+ tracks[t][k].command = 8;
+ if (event == 0xFE) // 0xFE: Pattern Break
+ tracks[t][k].command = 13;
+ }
+
+ // load order
+ for(i = 0; i < length; i++) order[i] = f->readInt(1) - 1;
+
+ fp.close(f);
+
+ // convert instruments
+ for(i = 0; i < 9; i++)
+ for(j = 0; j < 10; j++)
+ inst[i].data[conv_inst[j]] = instruments[i].data[j];
+
+ // data for Protracker
+ restartpos = 0;
+ initspeed = 1;
+
+ rewind(0);
+ return true;
+}
+
+void CmadLoader::rewind(int subsong)
+{
+ CmodPlayer::rewind(subsong);
+
+ // default instruments
+ for (int i=0;i<9;i++)
+ {
+ channel[i].inst = i;
+
+ channel[i].vol1 = 63 - (inst[i].data[10] & 63);
+ channel[i].vol2 = 63 - (inst[i].data[9] & 63);
+ }
+}
+
+float CmadLoader::getrefresh()
+{
+ return (float)timer;
+}
+
+std::string CmadLoader::gettype()
+{
+ return std::string("Mlat Adlib Tracker");
+}
+
+std::string CmadLoader::getinstrument(unsigned int n)
+{
+ return std::string(instruments[n].name,8);
+}
+
+unsigned int CmadLoader::getinstruments()
+{
+ return 9;
+}
diff --git a/plugins/adplug/adplug/mad.h b/plugins/adplug/adplug/mad.h
new file mode 100644
index 00000000..98d2a445
--- /dev/null
+++ b/plugins/adplug/adplug/mad.h
@@ -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
+
+ mad.h - MAD loader by Riven the Mage <riven@ok.ru>
+*/
+
+#include "protrack.h"
+
+class CmadLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmadLoader(Copl *newopl) : CmodPlayer(newopl) { };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string getinstrument(unsigned int n);
+ unsigned int getinstruments();
+
+private:
+
+ struct mad_instrument
+ {
+ char name[8];
+ unsigned char data[12]; // last two unused
+ } instruments[9];
+
+ unsigned char timer;
+};
diff --git a/plugins/adplug/adplug/mid.cpp b/plugins/adplug/adplug/mid.cpp
new file mode 100644
index 00000000..e3dd88c5
--- /dev/null
+++ b/plugins/adplug/adplug/mid.cpp
@@ -0,0 +1,1090 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * MIDI & MIDI-like file player - Last Update: 10/15/2005
+ * by Phil Hassey - www.imitationpickles.org
+ * philhassey@hotmail.com
+ *
+ * Can play the following
+ * .LAA - a raw save of a Lucas Arts Adlib music
+ * or
+ * a raw save of a LucasFilm Adlib music
+ * .MID - a "midi" save of a Lucas Arts Adlib music
+ * - or general MIDI files
+ * .CMF - Creative Music Format
+ * .SCI - the sierra "midi" format.
+ * Files must be in the form
+ * xxxNAME.sci
+ * So that the loader can load the right patch file:
+ * xxxPATCH.003 (patch.003 must be saved from the
+ * sierra resource from each game.)
+ *
+ * 6/2/2000: v1.0 relased by phil hassey
+ * Status: LAA is almost perfect
+ * - some volumes are a bit off (intrument too quiet)
+ * MID is fine (who wants to listen to MIDI vid adlib anyway)
+ * CMF is okay (still needs the adlib rythm mode implemented
+ * for real)
+ * 6/6/2000:
+ * Status: SCI: there are two SCI formats, orginal and advanced.
+ * original: (Found in SCI/EGA Sierra Adventures)
+ * played almost perfectly, I believe
+ * there is one mistake in the instrument
+ * loader that causes some sounds to
+ * not be quite right. Most sounds are fine.
+ * advanced: (Found in SCI/VGA Sierra Adventures)
+ * These are multi-track files. (Thus the
+ * player had to be modified to work with
+ * them.) This works fine.
+ * There are also multiple tunes in each file.
+ * I think some of them are supposed to be
+ * played at the same time, but I'm not sure
+ * when.
+ * 8/16/2000:
+ * Status: LAA: now EGA and VGA lucas games work pretty well
+ *
+ * 10/15/2005: Changes by Simon Peter
+ * Added rhythm mode support for CMF format.
+ *
+ * Other acknowledgements:
+ * Allegro - for the midi instruments and the midi volume table
+ * SCUMM Revisited - for getting the .LAA / .MIDs out of those
+ * LucasArts files.
+ * FreeSCI - for some information on the sci music files
+ * SD - the SCI Decoder (to get all .sci out of the Sierra files)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "mid.h"
+#include "mididata.h"
+
+/*#define TESTING*/
+#ifdef TESTING
+#define midiprintf printf
+#else
+void CmidPlayer::midiprintf(const char *format, ...)
+ {
+ }
+#endif
+
+#define LUCAS_STYLE 1
+#define CMF_STYLE 2
+#define MIDI_STYLE 4
+#define SIERRA_STYLE 8
+
+// AdLib melodic and rhythm mode defines
+#define ADLIB_MELODIC 0
+#define ADLIB_RYTHM 1
+
+// File types
+#define FILE_LUCAS 1
+#define FILE_MIDI 2
+#define FILE_CMF 3
+#define FILE_SIERRA 4
+#define FILE_ADVSIERRA 5
+#define FILE_OLDLUCAS 6
+
+// AdLib standard operator table
+const unsigned char CmidPlayer::adlib_opadd[] = {0x00 ,0x01 ,0x02 ,0x08 ,0x09 ,0x0A ,0x10 ,0x11 ,0x12};
+
+// dunno
+const int CmidPlayer::ops[] = {0x20,0x20,0x40,0x40,0x60,0x60,0x80,0x80,0xe0,0xe0,0xc0};
+
+// map CMF drum channels 12 - 15 to corresponding AdLib drum operators
+// bass drum (channel 11) not mapped, cause it's handled like a normal instrument
+const int CmidPlayer::map_chan[] = { 0x14, 0x12, 0x15, 0x11 };
+
+// Standard AdLib frequency table
+const int CmidPlayer::fnums[] = { 0x16b,0x181,0x198,0x1b0,0x1ca,0x1e5,0x202,0x220,0x241,0x263,0x287,0x2ae };
+
+// Map CMF drum channels 11 - 15 to corresponding AdLib drum channels
+const int CmidPlayer::percussion_map[] = { 6, 7, 8, 8, 7 };
+
+CPlayer *CmidPlayer::factory(Copl *newopl)
+{
+ return new CmidPlayer(newopl);
+}
+
+CmidPlayer::CmidPlayer(Copl *newopl)
+ : CPlayer(newopl), author(&emptystr), title(&emptystr), remarks(&emptystr),
+ emptystr('\0'), flen(0), data(0)
+{
+}
+
+unsigned char CmidPlayer::datalook(long pos)
+{
+ if (pos<0 || pos >= flen) return(0);
+ return(data[pos]);
+}
+
+unsigned long CmidPlayer::getnexti(unsigned long num)
+{
+ unsigned long v=0;
+ unsigned long i;
+
+ for (i=0; i<num; i++)
+ {
+ v+=(datalook(pos)<<(8*i)); pos++;
+ }
+ return(v);
+}
+
+unsigned long CmidPlayer::getnext(unsigned long num)
+{
+ unsigned long v=0;
+ unsigned long i;
+
+ for (i=0; i<num; i++)
+ {
+ v<<=8;
+ v+=datalook(pos); pos++;
+ }
+ return(v);
+}
+
+unsigned long CmidPlayer::getval()
+{
+ int v=0;
+ unsigned char b;
+
+ b=(unsigned char)getnext(1);
+ v=b&0x7f;
+ while ((b&0x80) !=0)
+ {
+ b=(unsigned char)getnext(1);
+ v = (v << 7) + (b & 0x7F);
+ }
+ return(v);
+}
+
+bool CmidPlayer::load_sierra_ins(const std::string &fname, const CFileProvider &fp)
+{
+ long i,j,k,l;
+ unsigned char ins[28];
+ char *pfilename;
+ binistream *f;
+
+ pfilename = (char *)malloc(fname.length()+9);
+ strcpy(pfilename,fname.c_str());
+ j=0;
+ for(i=strlen(pfilename)-1; i >= 0; i--)
+ if(pfilename[i] == '/' || pfilename[i] == '\\') {
+ j = i+1;
+ break;
+ }
+ sprintf(pfilename+j+3,"patch.003");
+
+ f = fp.open(pfilename);
+ free(pfilename);
+ if(!f) return false;
+
+ f->ignore(2);
+ stins = 0;
+ for (i=0; i<2; i++)
+ {
+ for (k=0; k<48; k++)
+ {
+ l=i*48+k;
+ midiprintf ("\n%2d: ",l);
+ for (j=0; j<28; j++)
+ ins[j] = f->readInt(1);
+
+ myinsbank[l][0]=
+ (ins[9]*0x80) + (ins[10]*0x40) +
+ (ins[5]*0x20) + (ins[11]*0x10) +
+ ins[1]; //1=ins5
+ myinsbank[l][1]=
+ (ins[22]*0x80) + (ins[23]*0x40) +
+ (ins[18]*0x20) + (ins[24]*0x10) +
+ ins[14]; //1=ins18
+
+ myinsbank[l][2]=(ins[0]<<6)+ins[8];
+ myinsbank[l][3]=(ins[13]<<6)+ins[21];
+
+ myinsbank[l][4]=(ins[3]<<4)+ins[6];
+ myinsbank[l][5]=(ins[16]<<4)+ins[19];
+ myinsbank[l][6]=(ins[4]<<4)+ins[7];
+ myinsbank[l][7]=(ins[17]<<4)+ins[20];
+
+ myinsbank[l][8]=ins[26];
+ myinsbank[l][9]=ins[27];
+
+ myinsbank[l][10]=((ins[2]<<1))+(1-(ins[12]&1));
+ //(ins[12] ? 0:1)+((ins[2]<<1));
+
+ for (j=0; j<11; j++)
+ midiprintf ("%02X ",myinsbank[l][j]);
+ stins++;
+ }
+ f->ignore(2);
+ }
+
+ fp.close(f);
+ memcpy(smyinsbank, myinsbank, 128 * 16);
+ return true;
+}
+
+void CmidPlayer::sierra_next_section()
+{
+ int i,j;
+
+ for (i=0; i<16; i++)
+ track[i].on=0;
+
+ midiprintf("\n\nnext adv sierra section:\n");
+
+ pos=sierra_pos;
+ i=0;j=0;
+ while (i!=0xff)
+ {
+ getnext(1);
+ curtrack=j; j++;
+ track[curtrack].on=1;
+ track[curtrack].spos = getnext(1);
+ track[curtrack].spos += (getnext(1) << 8) + 4; //4 best usually +3? not 0,1,2 or 5
+// track[curtrack].spos=getnext(1)+(getnext(1)<<8)+4; // dynamite!: doesn't optimize correctly!!
+ track[curtrack].tend=flen; //0xFC will kill it
+ track[curtrack].iwait=0;
+ track[curtrack].pv=0;
+ midiprintf ("track %d starts at %lx\n",curtrack,track[curtrack].spos);
+
+ getnext(2);
+ i=getnext(1);
+ }
+ getnext(2);
+ deltas=0x20;
+ sierra_pos=pos;
+ //getch();
+
+ fwait=0;
+ doing=1;
+}
+
+bool CmidPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ int good;
+ unsigned char s[6];
+
+ f->readString((char *)s, 6);
+ good=0;
+ subsongs=0;
+ switch(s[0])
+ {
+ case 'A':
+ if (s[1]=='D' && s[2]=='L') good=FILE_LUCAS;
+ break;
+ case 'M':
+ if (s[1]=='T' && s[2]=='h' && s[3]=='d') good=FILE_MIDI;
+ break;
+ case 'C':
+ if (s[1]=='T' && s[2]=='M' && s[3]=='F') good=FILE_CMF;
+ break;
+ case 0x84:
+ if (s[1]==0x00 && load_sierra_ins(filename, fp))
+ if (s[2]==0xf0)
+ good=FILE_ADVSIERRA;
+ else
+ good=FILE_SIERRA;
+ break;
+ default:
+ if (s[4]=='A' && s[5]=='D') good=FILE_OLDLUCAS;
+ break;
+ }
+
+ if (good!=0)
+ subsongs=1;
+ else {
+ fp.close(f);
+ return false;
+ }
+
+ type=good;
+ f->seek(0);
+ flen = fp.filesize(f);
+ data = new unsigned char [flen];
+ f->readString((char *)data, flen);
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+void CmidPlayer::midi_write_adlib(unsigned int r, unsigned char v)
+{
+ opl->write(r,v);
+ adlib_data[r]=v;
+}
+
+void CmidPlayer::midi_fm_instrument(int voice, unsigned char *inst)
+{
+ if ((adlib_style&SIERRA_STYLE)!=0)
+ midi_write_adlib(0xbd,0); //just gotta make sure this happens..
+ //'cause who knows when it'll be
+ //reset otherwise.
+
+
+ midi_write_adlib(0x20+adlib_opadd[voice],inst[0]);
+ midi_write_adlib(0x23+adlib_opadd[voice],inst[1]);
+
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ {
+ midi_write_adlib(0x43+adlib_opadd[voice],0x3f);
+ if ((inst[10] & 1)==0)
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ else
+ midi_write_adlib(0x40+adlib_opadd[voice],0x3f);
+ }
+ else
+ {
+ if ((adlib_style&SIERRA_STYLE)!=0)
+ {
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
+ }
+ else
+ {
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ if ((inst[10] & 1)==0)
+ midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
+ else
+ midi_write_adlib(0x43+adlib_opadd[voice],0);
+ }
+ }
+
+ midi_write_adlib(0x60+adlib_opadd[voice],inst[4]);
+ midi_write_adlib(0x63+adlib_opadd[voice],inst[5]);
+ midi_write_adlib(0x80+adlib_opadd[voice],inst[6]);
+ midi_write_adlib(0x83+adlib_opadd[voice],inst[7]);
+ midi_write_adlib(0xe0+adlib_opadd[voice],inst[8]);
+ midi_write_adlib(0xe3+adlib_opadd[voice],inst[9]);
+
+ midi_write_adlib(0xc0+voice,inst[10]);
+}
+
+void CmidPlayer::midi_fm_percussion(int ch, unsigned char *inst)
+{
+ int opadd = map_chan[ch - 12];
+
+ midi_write_adlib(0x20 + opadd, inst[0]);
+ midi_write_adlib(0x40 + opadd, inst[2]);
+ midi_write_adlib(0x60 + opadd, inst[4]);
+ midi_write_adlib(0x80 + opadd, inst[6]);
+ midi_write_adlib(0xe0 + opadd, inst[8]);
+ midi_write_adlib(0xc0 + opadd, inst[10]);
+}
+
+void CmidPlayer::midi_fm_volume(int voice, int volume)
+{
+ int vol;
+
+ if ((adlib_style&SIERRA_STYLE)==0) //sierra likes it loud!
+ {
+ vol=volume>>2;
+
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ {
+ if ((adlib_data[0xc0+voice]&1)==1)
+ midi_write_adlib(0x40+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x40+adlib_opadd[voice]]&0xc0)));
+ midi_write_adlib(0x43+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x43+adlib_opadd[voice]]&0xc0)));
+ }
+ else
+ {
+ if ((adlib_data[0xc0+voice]&1)==1)
+ midi_write_adlib(0x40+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x40+adlib_opadd[voice]]&0xc0)));
+ midi_write_adlib(0x43+adlib_opadd[voice], (unsigned char)((63-vol) |
+ (adlib_data[0x43+adlib_opadd[voice]]&0xc0)));
+ }
+ }
+}
+
+void CmidPlayer::midi_fm_playnote(int voice, int note, int volume)
+{
+ int freq=fnums[note%12];
+ int oct=note/12;
+ int c;
+
+ midi_fm_volume(voice,volume);
+ midi_write_adlib(0xa0+voice,(unsigned char)(freq&0xff));
+
+ c=((freq&0x300) >> 8)+(oct<<2) + (adlib_mode == ADLIB_MELODIC || voice < 6 ? (1<<5) : 0);
+ midi_write_adlib(0xb0+voice,(unsigned char)c);
+}
+
+void CmidPlayer::midi_fm_endnote(int voice)
+{
+ //midi_fm_volume(voice,0);
+ //midi_write_adlib(0xb0+voice,0);
+
+ midi_write_adlib(0xb0+voice,(unsigned char)(adlib_data[0xb0+voice]&(255-32)));
+}
+
+void CmidPlayer::midi_fm_reset()
+{
+ int i;
+
+ opl->init();
+
+ for (i=0; i<256; i++)
+ midi_write_adlib(i,0);
+
+ midi_write_adlib(0x01, 0x20);
+ midi_write_adlib(0xBD,0xc0);
+}
+
+bool CmidPlayer::update()
+{
+ long w,v,note,vel,ctrl,nv,x,l,lnum;
+ int i=0,j,c;
+ int on,onl,numchan;
+ int ret;
+
+ if (doing == 1)
+ {
+ // just get the first wait and ignore it :>
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on)
+ {
+ pos=track[curtrack].pos;
+ if (type != FILE_SIERRA && type !=FILE_ADVSIERRA)
+ track[curtrack].iwait+=getval();
+ else
+ track[curtrack].iwait+=getnext(1);
+ track[curtrack].pos=pos;
+ }
+ doing=0;
+ }
+
+ iwait=0;
+ ret=1;
+
+ while (iwait==0 && ret==1)
+ {
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on && track[curtrack].iwait==0 &&
+ track[curtrack].pos < track[curtrack].tend)
+ {
+ pos=track[curtrack].pos;
+
+ v=getnext(1);
+
+ // This is to do implied MIDI events.
+ if (v<0x80) {v=track[curtrack].pv; pos--;}
+ track[curtrack].pv=(unsigned char)v;
+
+ c=v&0x0f;
+ midiprintf ("[%2X]",v);
+ switch(v&0xf0)
+ {
+ case 0x80: /*note off*/
+ note=getnext(1); vel=getnext(1);
+ for (i=0; i<9; i++)
+ if (chp[i][0]==c && chp[i][1]==note)
+ {
+ midi_fm_endnote(i);
+ chp[i][0]=-1;
+ }
+ break;
+ case 0x90: /*note on*/
+ // doing=0;
+ note=getnext(1); vel=getnext(1);
+
+ if(adlib_mode == ADLIB_RYTHM)
+ numchan = 6;
+ else
+ numchan = 9;
+
+ if (ch[c].on!=0)
+ {
+ for (i=0; i<18; i++)
+ chp[i][2]++;
+
+ if(c < 11 || adlib_mode == ADLIB_MELODIC) {
+ j=0;
+ on=-1;onl=0;
+ for (i=0; i<numchan; i++)
+ if (chp[i][0]==-1 && chp[i][2]>onl)
+ { onl=chp[i][2]; on=i; j=1; }
+
+ if (on==-1)
+ {
+ onl=0;
+ for (i=0; i<numchan; i++)
+ if (chp[i][2]>onl)
+ { onl=chp[i][2]; on=i; }
+ }
+
+ if (j==0)
+ midi_fm_endnote(on);
+ } else
+ on = percussion_map[c - 11];
+
+ if (vel!=0 && ch[c].inum>=0 && ch[c].inum<128)
+ {
+ if (adlib_mode == ADLIB_MELODIC || c < 12)
+ midi_fm_instrument(on,ch[c].ins);
+ else
+ midi_fm_percussion(c, ch[c].ins);
+
+ if ((adlib_style&MIDI_STYLE)!=0)
+ {
+ nv=((ch[c].vol*vel)/128);
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ nv*=2;
+ if (nv>127) nv=127;
+ nv=my_midi_fm_vol_table[nv];
+ if ((adlib_style&LUCAS_STYLE)!=0)
+ nv=(int)((float)sqrt((float)nv)*11);
+ }
+ else
+ {
+ nv=vel;
+ }
+
+ midi_fm_playnote(on,note+ch[c].nshift,nv*2);
+ chp[on][0]=c;
+ chp[on][1]=note;
+ chp[on][2]=0;
+
+ if(adlib_mode == ADLIB_RYTHM && c >= 11) {
+ midi_write_adlib(0xbd, adlib_data[0xbd] & ~(0x10 >> (c - 11)));
+ midi_write_adlib(0xbd, adlib_data[0xbd] | (0x10 >> (c - 11)));
+ }
+
+ }
+ else
+ {
+ if (vel==0) //same code as end note
+ {
+ for (i=0; i<9; i++)
+ if (chp[i][0]==c && chp[i][1]==note)
+ {
+ // midi_fm_volume(i,0); // really end the note
+ midi_fm_endnote(i);
+ chp[i][0]=-1;
+ }
+ }
+ else
+ { // i forget what this is for.
+ chp[on][0]=-1;
+ chp[on][2]=0;
+ }
+ }
+ midiprintf(" [%d:%d:%d:%d]\n",c,ch[c].inum,note,vel);
+ }
+ else
+ midiprintf ("off");
+ break;
+ case 0xa0: /*key after touch */
+ note=getnext(1); vel=getnext(1);
+ /* //this might all be good
+ for (i=0; i<9; i++)
+ if (chp[i][0]==c & chp[i][1]==note)
+
+midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2);
+ */
+ break;
+ case 0xb0: /*control change .. pitch bend? */
+ ctrl=getnext(1); vel=getnext(1);
+
+ switch(ctrl)
+ {
+ case 0x07:
+ midiprintf ("(pb:%d: %d %d)",c,ctrl,vel);
+ ch[c].vol=vel;
+ midiprintf("vol");
+ break;
+ case 0x67:
+ midiprintf ("\n\nhere:%d\n\n",vel);
+ if ((adlib_style&CMF_STYLE)!=0) {
+ adlib_mode=vel;
+ if(adlib_mode == ADLIB_RYTHM)
+ midi_write_adlib(0xbd, adlib_data[0xbd] | (1 << 5));
+ else
+ midi_write_adlib(0xbd, adlib_data[0xbd] & ~(1 << 5));
+ }
+ break;
+ }
+ break;
+ case 0xc0: /*patch change*/
+ x=getnext(1);
+ ch[c].inum=x;
+ for (j=0; j<11; j++)
+ ch[c].ins[j]=myinsbank[ch[c].inum][j];
+ break;
+ case 0xd0: /*chanel touch*/
+ x=getnext(1);
+ break;
+ case 0xe0: /*pitch wheel*/
+ x=getnext(1);
+ x=getnext(1);
+ break;
+ case 0xf0:
+ switch(v)
+ {
+ case 0xf0:
+ case 0xf7: /*sysex*/
+ l=getval();
+ if (datalook(pos+l)==0xf7)
+ i=1;
+ midiprintf("{%d}",l);
+ midiprintf("\n");
+
+ if (datalook(pos)==0x7d &&
+ datalook(pos+1)==0x10 &&
+ datalook(pos+2)<16)
+ {
+ adlib_style=LUCAS_STYLE|MIDI_STYLE;
+ for (i=0; i<l; i++)
+ {
+ midiprintf ("%x ",datalook(pos+i));
+ if ((i-3)%10 == 0) midiprintf("\n");
+ }
+ midiprintf ("\n");
+ getnext(1);
+ getnext(1);
+ c=getnext(1);
+ getnext(1);
+
+ // getnext(22); //temp
+ ch[c].ins[0]=(unsigned char)((getnext(1)<<4)+getnext(1));
+ ch[c].ins[2]=(unsigned char)(0xff-(((getnext(1)<<4)+getnext(1))&0x3f));
+ ch[c].ins[4]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[6]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[8]=(unsigned char)((getnext(1)<<4)+getnext(1));
+
+ ch[c].ins[1]=(unsigned char)((getnext(1)<<4)+getnext(1));
+ ch[c].ins[3]=(unsigned char)(0xff-(((getnext(1)<<4)+getnext(1))&0x3f));
+ ch[c].ins[5]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[7]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1)));
+ ch[c].ins[9]=(unsigned char)((getnext(1)<<4)+getnext(1));
+
+ i=(getnext(1)<<4)+getnext(1);
+ ch[c].ins[10]=i;
+
+ //if ((i&1)==1) ch[c].ins[10]=1;
+
+ midiprintf ("\n%d: ",c);
+ for (i=0; i<11; i++)
+ midiprintf ("%2X ",ch[c].ins[i]);
+ getnext(l-26);
+ }
+ else
+ {
+ midiprintf("\n");
+ for (j=0; j<l; j++)
+ midiprintf ("%2X ",getnext(1));
+ }
+
+ midiprintf("\n");
+ if(i==1)
+ getnext(1);
+ break;
+ case 0xf1:
+ break;
+ case 0xf2:
+ getnext(2);
+ break;
+ case 0xf3:
+ getnext(1);
+ break;
+ case 0xf4:
+ break;
+ case 0xf5:
+ break;
+ case 0xf6: /*something*/
+ case 0xf8:
+ case 0xfa:
+ case 0xfb:
+ case 0xfc:
+ //this ends the track for sierra.
+ if (type == FILE_SIERRA ||
+ type == FILE_ADVSIERRA)
+ {
+ track[curtrack].tend=pos;
+ midiprintf ("endmark: %ld -- %lx\n",pos,pos);
+ }
+ break;
+ case 0xfe:
+ break;
+ case 0xfd:
+ break;
+ case 0xff:
+ v=getnext(1);
+ l=getval();
+ midiprintf ("\n");
+ midiprintf("{%X_%X}",v,l);
+ if (v==0x51)
+ {
+ lnum=getnext(l);
+ msqtr=lnum; /*set tempo*/
+ midiprintf ("(qtr=%ld)",msqtr);
+ }
+ else
+ {
+ for (i=0; i<l; i++)
+ midiprintf ("%2X ",getnext(1));
+ }
+ break;
+ }
+ break;
+ default: midiprintf("!",v); /* if we get down here, a error occurred */
+ break;
+ }
+
+ if (pos < track[curtrack].tend)
+ {
+ if (type != FILE_SIERRA && type !=FILE_ADVSIERRA)
+ w=getval();
+ else
+ w=getnext(1);
+ track[curtrack].iwait=w;
+ /*
+ if (w!=0)
+ {
+ midiprintf("\n<%d>",w);
+ f =
+((float)w/(float)deltas)*((float)msqtr/(float)1000000);
+ if (doing==1) f=0; //not playing yet. don't wait yet
+ }
+ */
+ }
+ else
+ track[curtrack].iwait=0;
+
+ track[curtrack].pos=pos;
+ }
+
+
+ ret=0; //end of song.
+ iwait=0;
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on == 1 &&
+ track[curtrack].pos < track[curtrack].tend)
+ ret=1; //not yet..
+
+ if (ret==1)
+ {
+ iwait=0xffffff; // bigger than any wait can be!
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on == 1 &&
+ track[curtrack].pos < track[curtrack].tend &&
+ track[curtrack].iwait < iwait)
+ iwait=track[curtrack].iwait;
+ }
+ }
+
+
+ if (iwait !=0 && ret==1)
+ {
+ for (curtrack=0; curtrack<16; curtrack++)
+ if (track[curtrack].on)
+ track[curtrack].iwait-=iwait;
+
+
+fwait=1.0f/(((float)iwait/(float)deltas)*((float)msqtr/(float)1000000));
+ }
+ else
+ fwait=50; // 1/50th of a second
+
+ midiprintf ("\n");
+ for (i=0; i<16; i++)
+ if (track[i].on)
+ if (track[i].pos < track[i].tend)
+ midiprintf ("<%d>",track[i].iwait);
+ else
+ midiprintf("stop");
+
+ /*
+ if (ret==0 && type==FILE_ADVSIERRA)
+ if (datalook(sierra_pos-2)!=0xff)
+ {
+ midiprintf ("next sectoin!");
+ sierra_next_section(p);
+ fwait=50;
+ ret=1;
+ }
+ */
+
+ if(ret)
+ return true;
+ else
+ return false;
+}
+
+float CmidPlayer::getrefresh()
+{
+ return (fwait > 0.01f ? fwait : 0.01f);
+}
+
+void CmidPlayer::rewind(int subsong)
+{
+ long i,j,n,m,l;
+ long o_sierra_pos;
+ unsigned char ins[16];
+
+ pos=0; tins=0;
+ adlib_style=MIDI_STYLE|CMF_STYLE;
+ adlib_mode=ADLIB_MELODIC;
+ for (i=0; i<128; i++)
+ for (j=0; j<16; j++)
+ myinsbank[i][j]=midi_fm_instruments[i][j];
+ for (i=0; i<16; i++)
+ {
+ ch[i].inum=0;
+ for (j=0; j<11; j++)
+ ch[i].ins[j]=myinsbank[ch[i].inum][j];
+ ch[i].vol=127;
+ ch[i].nshift=-25;
+ ch[i].on=1;
+ }
+
+ /* General init */
+ for (i=0; i<9; i++)
+ {
+ chp[i][0]=-1;
+ chp[i][2]=0;
+ }
+
+ deltas=250; // just a number, not a standard
+ msqtr=500000;
+ fwait=123; // gotta be a small thing.. sorta like nothing
+ iwait=0;
+
+ subsongs=1;
+
+ for (i=0; i<16; i++)
+ {
+ track[i].tend=0;
+ track[i].spos=0;
+ track[i].pos=0;
+ track[i].iwait=0;
+ track[i].on=0;
+ track[i].pv=0;
+ }
+ curtrack=0;
+
+ /* specific to file-type init */
+
+ pos=0;
+ i=getnext(1);
+ switch(type)
+ {
+ case FILE_LUCAS:
+ getnext(24); //skip junk and get to the midi.
+ adlib_style=LUCAS_STYLE|MIDI_STYLE;
+ //note: no break, we go right into midi headers...
+ case FILE_MIDI:
+ if (type != FILE_LUCAS)
+ tins=128;
+ getnext(11); /*skip header*/
+ deltas=getnext(2);
+ midiprintf ("deltas:%ld\n",deltas);
+ getnext(4);
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=getnext(4);
+ track[curtrack].spos=pos;
+ midiprintf ("tracklen:%ld\n",track[curtrack].tend);
+ break;
+ case FILE_CMF:
+ getnext(3); // ctmf
+ getnexti(2); //version
+ n=getnexti(2); // instrument offset
+ m=getnexti(2); // music offset
+ deltas=getnexti(2); //ticks/qtr note
+ msqtr=1000000/getnexti(2)*deltas;
+ //the stuff in the cmf is click ticks per second..
+
+ i=getnexti(2);
+ if(i) title = (char *)data+i;
+ i=getnexti(2);
+ if(i) author = (char *)data+i;
+ i=getnexti(2);
+ if(i) remarks = (char *)data+i;
+
+ getnext(16); // channel in use table ..
+ i=getnexti(2); // num instr
+ if (i>128) i=128; // to ward of bad numbers...
+ getnexti(2); //basic tempo
+
+ midiprintf("\nioff:%d\nmoff%d\ndeltas:%ld\nmsqtr:%ld\nnumi:%d\n",
+ n,m,deltas,msqtr,i);
+ pos=n; // jump to instruments
+ tins=i;
+ for (j=0; j<i; j++)
+ {
+ midiprintf ("\n%d: ",j);
+ for (l=0; l<16; l++)
+ {
+ myinsbank[j][l]=(unsigned char)getnext(1);
+ midiprintf ("%2X ",myinsbank[j][l]);
+ }
+ }
+
+ for (i=0; i<16; i++)
+ ch[i].nshift=-13;
+
+ adlib_style=CMF_STYLE;
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=flen; // music until the end of the file
+ track[curtrack].spos=m; //jump to midi music
+ break;
+ case FILE_OLDLUCAS:
+ msqtr=250000;
+ pos=9;
+ deltas=getnext(1);
+
+ i=8;
+ pos=0x19; // jump to instruments
+ tins=i;
+ for (j=0; j<i; j++)
+ {
+ midiprintf ("\n%d: ",j);
+ for (l=0; l<16; l++)
+ ins[l]=(unsigned char)getnext(1);
+
+ myinsbank[j][10]=ins[2];
+ myinsbank[j][0]=ins[3];
+ myinsbank[j][2]=ins[4];
+ myinsbank[j][4]=ins[5];
+ myinsbank[j][6]=ins[6];
+ myinsbank[j][8]=ins[7];
+ myinsbank[j][1]=ins[8];
+ myinsbank[j][3]=ins[9];
+ myinsbank[j][5]=ins[10];
+ myinsbank[j][7]=ins[11];
+ myinsbank[j][9]=ins[12];
+
+ for (l=0; l<11; l++)
+ midiprintf ("%2X ",myinsbank[j][l]);
+ }
+
+ for (i=0; i<16; i++)
+ {
+ if (i<tins)
+ {
+ ch[i].inum=i;
+ for (j=0; j<11; j++)
+ ch[i].ins[j]=myinsbank[ch[i].inum][j];
+ }
+ }
+
+ adlib_style=LUCAS_STYLE|MIDI_STYLE;
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=flen; // music until the end of the file
+ track[curtrack].spos=0x98; //jump to midi music
+ break;
+ case FILE_ADVSIERRA:
+ memcpy(myinsbank, smyinsbank, 128 * 16);
+ tins = stins;
+ deltas=0x20;
+ getnext(11); //worthless empty space and "stuff" :)
+
+ o_sierra_pos=sierra_pos=pos;
+ sierra_next_section();
+ while (datalook(sierra_pos-2)!=0xff)
+ {
+ sierra_next_section();
+ subsongs++;
+ }
+
+ if (subsong < 0 || subsong >= subsongs) subsong=0;
+
+ sierra_pos=o_sierra_pos;
+ sierra_next_section();
+ i=0;
+ while (i != subsong)
+ {
+ sierra_next_section();
+ i++;
+ }
+
+ adlib_style=SIERRA_STYLE|MIDI_STYLE; //advanced sierra tunes use volume
+ break;
+ case FILE_SIERRA:
+ memcpy(myinsbank, smyinsbank, 128 * 16);
+ tins = stins;
+ getnext(2);
+ deltas=0x20;
+
+ curtrack=0;
+ track[curtrack].on=1;
+ track[curtrack].tend=flen; // music until the end of the file
+
+ for (i=0; i<16; i++)
+ {
+ ch[i].nshift=-13;
+ ch[i].on=getnext(1);
+ ch[i].inum=getnext(1);
+ for (j=0; j<11; j++)
+ ch[i].ins[j]=myinsbank[ch[i].inum][j];
+ }
+
+ track[curtrack].spos=pos;
+ adlib_style=SIERRA_STYLE|MIDI_STYLE;
+ break;
+ }
+
+
+/* sprintf(info,"%s\r\nTicks/Quarter Note: %ld\r\n",info,deltas);
+ sprintf(info,"%sms/Quarter Note: %ld",info,msqtr); */
+
+ for (i=0; i<16; i++)
+ if (track[i].on)
+ {
+ track[i].pos=track[i].spos;
+ track[i].pv=0;
+ track[i].iwait=0;
+ }
+
+ doing=1;
+ midi_fm_reset();
+}
+
+std::string CmidPlayer::gettype()
+{
+ switch(type) {
+ case FILE_LUCAS:
+ return std::string("LucasArts AdLib MIDI");
+ case FILE_MIDI:
+ return std::string("General MIDI");
+ case FILE_CMF:
+ return std::string("Creative Music Format (CMF MIDI)");
+ case FILE_OLDLUCAS:
+ return std::string("Lucasfilm Adlib MIDI");
+ case FILE_ADVSIERRA:
+ return std::string("Sierra On-Line VGA MIDI");
+ case FILE_SIERRA:
+ return std::string("Sierra On-Line EGA MIDI");
+ default:
+ return std::string("MIDI unknown");
+ }
+}
diff --git a/plugins/adplug/adplug/mid.h b/plugins/adplug/adplug/mid.h
new file mode 100644
index 00000000..d28f7252
--- /dev/null
+++ b/plugins/adplug/adplug/mid.h
@@ -0,0 +1,112 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * mid.h - LAA, SCI, MID & CMF Player by Philip Hassey <philhassey@hotmail.com>
+ */
+
+#include "player.h"
+
+class CmidPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmidPlayer(Copl *newopl);
+ ~CmidPlayer()
+ { if(data) delete [] data; }
+
+ 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(title); }
+ std::string getauthor()
+ { return std::string(author); }
+ std::string getdesc()
+ { return std::string(remarks); }
+ unsigned int getinstruments()
+ { return tins; }
+ unsigned int getsubsongs()
+ { return subsongs; }
+
+ protected:
+ static const unsigned char adlib_opadd[];
+ static const int ops[], map_chan[], fnums[], percussion_map[];
+
+ struct midi_channel {
+ int inum;
+ unsigned char ins[11];
+ int vol;
+ int nshift;
+ int on;
+ };
+
+ struct midi_track {
+ unsigned long tend;
+ unsigned long spos;
+ unsigned long pos;
+ unsigned long iwait;
+ int on;
+ unsigned char pv;
+ };
+
+ char *author,*title,*remarks,emptystr;
+ long flen;
+ unsigned long pos;
+ unsigned long sierra_pos; //sierras gotta be special.. :>
+ int subsongs;
+ unsigned char *data;
+
+ unsigned char adlib_data[256];
+ int adlib_style;
+ int adlib_mode;
+ unsigned char myinsbank[128][16], smyinsbank[128][16];
+ midi_channel ch[16];
+ int chp[18][3];
+
+ long deltas;
+ long msqtr;
+
+ midi_track track[16];
+ unsigned int curtrack;
+
+ float fwait;
+ unsigned long iwait;
+ int doing;
+
+ int type,tins,stins;
+
+ private:
+ bool load_sierra_ins(const std::string &fname, const CFileProvider &fp);
+ void midiprintf(const char *format, ...);
+ unsigned char datalook(long pos);
+ unsigned long getnexti(unsigned long num);
+ unsigned long getnext(unsigned long num);
+ unsigned long getval();
+ void sierra_next_section();
+ void midi_write_adlib(unsigned int r, unsigned char v);
+ void midi_fm_instrument(int voice, unsigned char *inst);
+ void midi_fm_percussion(int ch, unsigned char *inst);
+ void midi_fm_volume(int voice, int volume);
+ void midi_fm_playnote(int voice, int note, int volume);
+ void midi_fm_endnote(int voice);
+ void midi_fm_reset();
+};
diff --git a/plugins/adplug/adplug/mididata.h b/plugins/adplug/adplug/mididata.h
new file mode 100644
index 00000000..2a83cd99
--- /dev/null
+++ b/plugins/adplug/adplug/mididata.h
@@ -0,0 +1,174 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999, 2000, 2001 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
+ *
+ *
+ * FM instrument definitions below borrowed from the Allegro library by
+ * Phil Hassey, <philhassey@hotmail.com> - see "adplug/players/mid.cpp"
+ * for further acknowledgements.
+ */
+
+unsigned char midi_fm_instruments[128][14] =
+{
+
+ /* This set of GM instrument patches was provided by Jorrit Rouwe...
+ */
+
+ { 0x21, 0x21, 0x8f, 0x0c, 0xf2, 0xf2, 0x45, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Acoustic Grand */
+ { 0x31, 0x21, 0x4b, 0x09, 0xf2, 0xf2, 0x54, 0x56, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Bright Acoustic */
+ { 0x31, 0x21, 0x49, 0x09, 0xf2, 0xf2, 0x55, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Electric Grand */
+ { 0xb1, 0x61, 0x0e, 0x09, 0xf2, 0xf3, 0x3b, 0x0b, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Honky-Tonk */
+ { 0x01, 0x21, 0x57, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 1 */
+ { 0x01, 0x21, 0x93, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 2 */
+ { 0x21, 0x36, 0x80, 0x17, 0xa2, 0xf1, 0x01, 0xd5, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Harpsichord */
+ { 0x01, 0x01, 0x92, 0x09, 0xc2, 0xc2, 0xa8, 0x58, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Clav */
+ { 0x0c, 0x81, 0x5c, 0x09, 0xf6, 0xf3, 0x54, 0xb5, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Celesta */
+ { 0x07, 0x11, 0x97, 0x89, 0xf6, 0xf5, 0x32, 0x11, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Glockenspiel */
+ { 0x17, 0x01, 0x21, 0x09, 0x56, 0xf6, 0x04, 0x04, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Music Box */
+ { 0x18, 0x81, 0x62, 0x09, 0xf3, 0xf2, 0xe6, 0xf6, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Vibraphone */
+ { 0x18, 0x21, 0x23, 0x09, 0xf7, 0xe5, 0x55, 0xd8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Marimba */
+ { 0x15, 0x01, 0x91, 0x09, 0xf6, 0xf6, 0xa6, 0xe6, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Xylophone */
+ { 0x45, 0x81, 0x59, 0x89, 0xd3, 0xa3, 0x82, 0xe3, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tubular Bells */
+ { 0x03, 0x81, 0x49, 0x89, 0x74, 0xb3, 0x55, 0x05, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Dulcimer */
+ { 0x71, 0x31, 0x92, 0x09, 0xf6, 0xf1, 0x14, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Drawbar Organ */
+ { 0x72, 0x30, 0x14, 0x09, 0xc7, 0xc7, 0x58, 0x08, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Percussive Organ */
+ { 0x70, 0xb1, 0x44, 0x09, 0xaa, 0x8a, 0x18, 0x08, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Rock Organ */
+ { 0x23, 0xb1, 0x93, 0x09, 0x97, 0x55, 0x23, 0x14, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Church Organ */
+ { 0x61, 0xb1, 0x13, 0x89, 0x97, 0x55, 0x04, 0x04, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Reed Organ */
+ { 0x24, 0xb1, 0x48, 0x09, 0x98, 0x46, 0x2a, 0x1a, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Accoridan */
+ { 0x61, 0x21, 0x13, 0x09, 0x91, 0x61, 0x06, 0x07, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Harmonica */
+ { 0x21, 0xa1, 0x13, 0x92, 0x71, 0x61, 0x06, 0x07, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tango Accordian */
+ { 0x02, 0x41, 0x9c, 0x89, 0xf3, 0xf3, 0x94, 0xc8, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(nylon) */
+ { 0x03, 0x11, 0x54, 0x09, 0xf3, 0xf1, 0x9a, 0xe7, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(steel) */
+ { 0x23, 0x21, 0x5f, 0x09, 0xf1, 0xf2, 0x3a, 0xf8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(jazz) */
+ { 0x03, 0x21, 0x87, 0x89, 0xf6, 0xf3, 0x22, 0xf8, 0x01, 0x00, 0x06, 0, 0, 0 }, /* Electric Guitar(clean) */
+ { 0x03, 0x21, 0x47, 0x09, 0xf9, 0xf6, 0x54, 0x3a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(muted) */
+ { 0x23, 0x21, 0x4a, 0x0e, 0x91, 0x84, 0x41, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Overdriven Guitar */
+ { 0x23, 0x21, 0x4a, 0x09, 0x95, 0x94, 0x19, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Distortion Guitar */
+ { 0x09, 0x84, 0xa1, 0x89, 0x20, 0xd1, 0x4f, 0xf8, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Guitar Harmonics */
+ { 0x21, 0xa2, 0x1e, 0x09, 0x94, 0xc3, 0x06, 0xa6, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Acoustic Bass */
+ { 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(finger) */
+ { 0x31, 0x31, 0x8d, 0x09, 0xf1, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(pick) */
+ { 0x31, 0x32, 0x5b, 0x09, 0x51, 0x71, 0x28, 0x48, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Fretless Bass */
+ { 0x01, 0x21, 0x8b, 0x49, 0xa1, 0xf2, 0x9a, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 1 */
+ { 0x21, 0x21, 0x8b, 0x11, 0xa2, 0xa1, 0x16, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 2 */
+ { 0x31, 0x31, 0x8b, 0x09, 0xf4, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 1 */
+ { 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 2 */
+ { 0x31, 0x21, 0x15, 0x09, 0xdd, 0x56, 0x13, 0x26, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Violin */
+ { 0x31, 0x21, 0x16, 0x09, 0xdd, 0x66, 0x13, 0x06, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Viola */
+ { 0x71, 0x31, 0x49, 0x09, 0xd1, 0x61, 0x1c, 0x0c, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Cello */
+ { 0x21, 0x23, 0x4d, 0x89, 0x71, 0x72, 0x12, 0x06, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Contrabass */
+ { 0xf1, 0xe1, 0x40, 0x09, 0xf1, 0x6f, 0x21, 0x16, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Tremolo Strings */
+ { 0x02, 0x01, 0x1a, 0x89, 0xf5, 0x85, 0x75, 0x35, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pizzicato Strings */
+ { 0x02, 0x01, 0x1d, 0x89, 0xf5, 0xf3, 0x75, 0xf4, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Orchestral Strings */
+ { 0x10, 0x11, 0x41, 0x09, 0xf5, 0xf2, 0x05, 0xc3, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Timpani */
+ { 0x21, 0xa2, 0x9b, 0x0a, 0xb1, 0x72, 0x25, 0x08, 0x01, 0x00, 0x0e, 0, 0, 0 }, /* String Ensemble 1 */
+ { 0xa1, 0x21, 0x98, 0x09, 0x7f, 0x3f, 0x03, 0x07, 0x01, 0x01, 0x00, 0, 0, 0 }, /* String Ensemble 2 */
+ { 0xa1, 0x61, 0x93, 0x09, 0xc1, 0x4f, 0x12, 0x05, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* SynthStrings 1 */
+ { 0x21, 0x61, 0x18, 0x09, 0xc1, 0x4f, 0x22, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* SynthStrings 2 */
+ { 0x31, 0x72, 0x5b, 0x8c, 0xf4, 0x8a, 0x15, 0x05, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Choir Aahs */
+ { 0xa1, 0x61, 0x90, 0x09, 0x74, 0x71, 0x39, 0x67, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Voice Oohs */
+ { 0x71, 0x72, 0x57, 0x09, 0x54, 0x7a, 0x05, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Synth Voice */
+ { 0x90, 0x41, 0x00, 0x09, 0x54, 0xa5, 0x63, 0x45, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Orchestra Hit */
+ { 0x21, 0x21, 0x92, 0x0a, 0x85, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trumpet */
+ { 0x21, 0x21, 0x94, 0x0e, 0x75, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trombone */
+ { 0x21, 0x61, 0x94, 0x09, 0x76, 0x82, 0x15, 0x37, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tuba */
+ { 0x31, 0x21, 0x43, 0x09, 0x9e, 0x62, 0x17, 0x2c, 0x01, 0x01, 0x02, 0, 0, 0 }, /* Muted Trumpet */
+ { 0x21, 0x21, 0x9b, 0x09, 0x61, 0x7f, 0x6a, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* French Horn */
+ { 0x61, 0x22, 0x8a, 0x0f, 0x75, 0x74, 0x1f, 0x0f, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Brass Section */
+ { 0xa1, 0x21, 0x86, 0x8c, 0x72, 0x71, 0x55, 0x18, 0x01, 0x00, 0x00, 0, 0, 0 }, /* SynthBrass 1 */
+ { 0x21, 0x21, 0x4d, 0x09, 0x54, 0xa6, 0x3c, 0x1c, 0x00, 0x00, 0x08, 0, 0, 0 }, /* SynthBrass 2 */
+ { 0x31, 0x61, 0x8f, 0x09, 0x93, 0x72, 0x02, 0x0b, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Soprano Sax */
+ { 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x03, 0x09, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Alto Sax */
+ { 0x31, 0x61, 0x91, 0x09, 0x93, 0x82, 0x03, 0x09, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Tenor Sax */
+ { 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x0f, 0x0f, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Baritone Sax */
+ { 0x21, 0x21, 0x4b, 0x09, 0xaa, 0x8f, 0x16, 0x0a, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Oboe */
+ { 0x31, 0x21, 0x90, 0x09, 0x7e, 0x8b, 0x17, 0x0c, 0x01, 0x01, 0x06, 0, 0, 0 }, /* English Horn */
+ { 0x31, 0x32, 0x81, 0x09, 0x75, 0x61, 0x19, 0x19, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Bassoon */
+ { 0x32, 0x21, 0x90, 0x09, 0x9b, 0x72, 0x21, 0x17, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Clarinet */
+ { 0xe1, 0xe1, 0x1f, 0x09, 0x85, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Piccolo */
+ { 0xe1, 0xe1, 0x46, 0x09, 0x88, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Flute */
+ { 0xa1, 0x21, 0x9c, 0x09, 0x75, 0x75, 0x1f, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Recorder */
+ { 0x31, 0x21, 0x8b, 0x09, 0x84, 0x65, 0x58, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pan Flute */
+ { 0xe1, 0xa1, 0x4c, 0x09, 0x66, 0x65, 0x56, 0x26, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Blown Bottle */
+ { 0x62, 0xa1, 0xcb, 0x09, 0x76, 0x55, 0x46, 0x36, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Skakuhachi */
+ { 0x62, 0xa1, 0xa2, 0x09, 0x57, 0x56, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Whistle */
+ { 0x62, 0xa1, 0x9c, 0x09, 0x77, 0x76, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Ocarina */
+ { 0x22, 0x21, 0x59, 0x09, 0xff, 0xff, 0x03, 0x0f, 0x02, 0x00, 0x00, 0, 0, 0 }, /* Lead 1 (square) */
+ { 0x21, 0x21, 0x0e, 0x09, 0xff, 0xff, 0x0f, 0x0f, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Lead 2 (sawtooth) */
+ { 0x22, 0x21, 0x46, 0x89, 0x86, 0x64, 0x55, 0x18, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 3 (calliope) */
+ { 0x21, 0xa1, 0x45, 0x09, 0x66, 0x96, 0x12, 0x0a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 4 (chiff) */
+ { 0x21, 0x22, 0x8b, 0x09, 0x92, 0x91, 0x2a, 0x2a, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Lead 5 (charang) */
+ { 0xa2, 0x61, 0x9e, 0x49, 0xdf, 0x6f, 0x05, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Lead 6 (voice) */
+ { 0x20, 0x60, 0x1a, 0x09, 0xef, 0x8f, 0x01, 0x06, 0x00, 0x02, 0x00, 0, 0, 0 }, /* Lead 7 (fifths) */
+ { 0x21, 0x21, 0x8f, 0x86, 0xf1, 0xf4, 0x29, 0x09, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Lead 8 (bass+lead) */
+ { 0x77, 0xa1, 0xa5, 0x09, 0x53, 0xa0, 0x94, 0x05, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Pad 1 (new age) */
+ { 0x61, 0xb1, 0x1f, 0x89, 0xa8, 0x25, 0x11, 0x03, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 2 (warm) */
+ { 0x61, 0x61, 0x17, 0x09, 0x91, 0x55, 0x34, 0x16, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Pad 3 (polysynth) */
+ { 0x71, 0x72, 0x5d, 0x09, 0x54, 0x6a, 0x01, 0x03, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pad 4 (choir) */
+ { 0x21, 0xa2, 0x97, 0x09, 0x21, 0x42, 0x43, 0x35, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Pad 5 (bowed) */
+ { 0xa1, 0x21, 0x1c, 0x09, 0xa1, 0x31, 0x77, 0x47, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Pad 6 (metallic) */
+ { 0x21, 0x61, 0x89, 0x0c, 0x11, 0x42, 0x33, 0x25, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 7 (halo) */
+ { 0xa1, 0x21, 0x15, 0x09, 0x11, 0xcf, 0x47, 0x07, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pad 8 (sweep) */
+ { 0x3a, 0x51, 0xce, 0x09, 0xf8, 0x86, 0xf6, 0x02, 0x00, 0x00, 0x02, 0, 0, 0 }, /* FX 1 (rain) */
+ { 0x21, 0x21, 0x15, 0x09, 0x21, 0x41, 0x23, 0x13, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 2 (soundtrack) */
+ { 0x06, 0x01, 0x5b, 0x09, 0x74, 0xa5, 0x95, 0x72, 0x00, 0x00, 0x00, 0, 0, 0 }, /* FX 3 (crystal) */
+ { 0x22, 0x61, 0x92, 0x8c, 0xb1, 0xf2, 0x81, 0x26, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* FX 4 (atmosphere) */
+ { 0x41, 0x42, 0x4d, 0x09, 0xf1, 0xf2, 0x51, 0xf5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 5 (brightness) */
+ { 0x61, 0xa3, 0x94, 0x89, 0x11, 0x11, 0x51, 0x13, 0x01, 0x00, 0x06, 0, 0, 0 }, /* FX 6 (goblins) */
+ { 0x61, 0xa1, 0x8c, 0x89, 0x11, 0x1d, 0x31, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* FX 7 (echoes) */
+ { 0xa4, 0x61, 0x4c, 0x09, 0xf3, 0x81, 0x73, 0x23, 0x01, 0x00, 0x04, 0, 0, 0 }, /* FX 8 (sci-fi) */
+ { 0x02, 0x07, 0x85, 0x0c, 0xd2, 0xf2, 0x53, 0xf6, 0x00, 0x01, 0x00, 0, 0, 0 }, /* Sitar */
+ { 0x11, 0x13, 0x0c, 0x89, 0xa3, 0xa2, 0x11, 0xe5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Banjo */
+ { 0x11, 0x11, 0x06, 0x09, 0xf6, 0xf2, 0x41, 0xe6, 0x01, 0x02, 0x04, 0, 0, 0 }, /* Shamisen */
+ { 0x93, 0x91, 0x91, 0x09, 0xd4, 0xeb, 0x32, 0x11, 0x00, 0x01, 0x08, 0, 0, 0 }, /* Koto */
+ { 0x04, 0x01, 0x4f, 0x09, 0xfa, 0xc2, 0x56, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Kalimba */
+ { 0x21, 0x22, 0x49, 0x09, 0x7c, 0x6f, 0x20, 0x0c, 0x00, 0x01, 0x06, 0, 0, 0 }, /* Bagpipe */
+ { 0x31, 0x21, 0x85, 0x09, 0xdd, 0x56, 0x33, 0x16, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Fiddle */
+ { 0x20, 0x21, 0x04, 0x8a, 0xda, 0x8f, 0x05, 0x0b, 0x02, 0x00, 0x06, 0, 0, 0 }, /* Shanai */
+ { 0x05, 0x03, 0x6a, 0x89, 0xf1, 0xc3, 0xe5, 0xe5, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tinkle Bell */
+ { 0x07, 0x02, 0x15, 0x09, 0xec, 0xf8, 0x26, 0x16, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Agogo */
+ { 0x05, 0x01, 0x9d, 0x09, 0x67, 0xdf, 0x35, 0x05, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Steel Drums */
+ { 0x18, 0x12, 0x96, 0x09, 0xfa, 0xf8, 0x28, 0xe5, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Woodblock */
+ { 0x10, 0x00, 0x86, 0x0c, 0xa8, 0xfa, 0x07, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Taiko Drum */
+ { 0x11, 0x10, 0x41, 0x0c, 0xf8, 0xf3, 0x47, 0x03, 0x02, 0x00, 0x04, 0, 0, 0 }, /* Melodic Tom */
+ { 0x01, 0x10, 0x8e, 0x09, 0xf1, 0xf3, 0x06, 0x02, 0x02, 0x00, 0x0e, 0, 0, 0 }, /* Synth Drum */
+ { 0x0e, 0xc0, 0x00, 0x09, 0x1f, 0x1f, 0x00, 0xff, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Reverse Cymbal */
+ { 0x06, 0x03, 0x80, 0x91, 0xf8, 0x56, 0x24, 0x84, 0x00, 0x02, 0x0e, 0, 0, 0 }, /* Guitar Fret Noise */
+ { 0x0e, 0xd0, 0x00, 0x0e, 0xf8, 0x34, 0x00, 0x04, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Breath Noise */
+ { 0x0e, 0xc0, 0x00, 0x09, 0xf6, 0x1f, 0x00, 0x02, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Seashore */
+ { 0xd5, 0xda, 0x95, 0x49, 0x37, 0x56, 0xa3, 0x37, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Bird Tweet */
+ { 0x35, 0x14, 0x5c, 0x11, 0xb2, 0xf4, 0x61, 0x15, 0x02, 0x00, 0x0a, 0, 0, 0 }, /* Telephone ring */
+ { 0x0e, 0xd0, 0x00, 0x09, 0xf6, 0x4f, 0x00, 0xf5, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Helicopter */
+ { 0x26, 0xe4, 0x00, 0x09, 0xff, 0x12, 0x01, 0x16, 0x00, 0x01, 0x0e, 0, 0, 0 }, /* Applause */
+ { 0x00, 0x00, 0x00, 0x09, 0xf3, 0xf6, 0xf0, 0xc9, 0x00, 0x02, 0x0e, 0, 0, 0 } /* Gunshot */
+
+};
+
+/* logarithmic relationship between midi and FM volumes */
+static int my_midi_fm_vol_table[128] = {
+ 0, 11, 16, 19, 22, 25, 27, 29, 32, 33, 35, 37, 39, 40, 42, 43,
+ 45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 75, 76, 77,
+ 78, 79, 80, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 89, 89,
+ 90, 91, 91, 92, 93, 93, 94, 95, 96, 96, 97, 97, 98, 99, 99, 100,
+ 101, 101, 102, 103, 103, 104, 104, 105, 106, 106, 107, 107, 108,
+ 109, 109, 110, 110, 111, 112, 112, 113, 113, 114, 114, 115, 115,
+ 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122,
+ 123, 123, 124, 124, 125, 125, 126, 126, 127
+};
+
diff --git a/plugins/adplug/adplug/mkj.cpp b/plugins/adplug/adplug/mkj.cpp
new file mode 100644
index 00000000..32d7e27c
--- /dev/null
+++ b/plugins/adplug/adplug/mkj.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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
+ *
+ * mkj.cpp - MKJamz Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "mkj.h"
+#include "debug.h"
+
+CPlayer *CmkjPlayer::factory(Copl *newopl)
+{
+ return new CmkjPlayer(newopl);
+}
+
+bool CmkjPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[6];
+ float ver;
+ int i, j;
+ short inst[8];
+
+ // file validation
+ f->readString(id, 6);
+ if(strncmp(id,"MKJamz",6)) { fp.close(f); return false; }
+ ver = f->readFloat(binio::Single);
+ if(ver > 1.12) { fp.close(f); return false; }
+
+ // load
+ maxchannel = f->readInt(2);
+ opl->init(); opl->write(1, 32);
+ for(i = 0; i < maxchannel; i++) {
+ for(j = 0; j < 8; j++) inst[j] = f->readInt(2);
+ opl->write(0x20+op_table[i],inst[4]);
+ opl->write(0x23+op_table[i],inst[0]);
+ opl->write(0x40+op_table[i],inst[5]);
+ opl->write(0x43+op_table[i],inst[1]);
+ opl->write(0x60+op_table[i],inst[6]);
+ opl->write(0x63+op_table[i],inst[2]);
+ opl->write(0x80+op_table[i],inst[7]);
+ opl->write(0x83+op_table[i],inst[3]);
+ }
+ maxnotes = f->readInt(2);
+ songbuf = new short [(maxchannel+1)*maxnotes];
+ for(i = 0; i < maxchannel; i++) channel[i].defined = f->readInt(2);
+ for(i = 0; i < (maxchannel + 1) * maxnotes; i++)
+ songbuf[i] = f->readInt(2);
+
+ AdPlug_LogWrite("CmkjPlayer::load(\"%s\"): loaded file ver %.2f, %d channels,"
+ " %d notes/channel.\n", filename.c_str(), ver, maxchannel,
+ maxnotes);
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CmkjPlayer::update()
+{
+ int c, i;
+ short note;
+
+ for(c = 0; c < maxchannel; c++) {
+ if(!channel[c].defined) // skip if channel is disabled
+ continue;
+
+ if(channel[c].pstat) {
+ channel[c].pstat--;
+ continue;
+ }
+
+ opl->write(0xb0 + c, 0); // key off
+ do {
+ assert(channel[c].songptr < (maxchannel + 1) * maxnotes);
+ note = songbuf[channel[c].songptr];
+ if(channel[c].songptr - c > maxchannel)
+ if(note && note < 250)
+ channel[c].pstat = channel[c].speed;
+ switch(note) {
+ // normal notes
+ case 68: opl->write(0xa0 + c,0x81); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 69: opl->write(0xa0 + c,0xb0); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 70: opl->write(0xa0 + c,0xca); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 71: opl->write(0xa0 + c,0x2); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 65: opl->write(0xa0 + c,0x41); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 66: opl->write(0xa0 + c,0x87); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 67: opl->write(0xa0 + c,0xae); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 17: opl->write(0xa0 + c,0x6b); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 18: opl->write(0xa0 + c,0x98); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 20: opl->write(0xa0 + c,0xe5); opl->write(0xb0 + c,0x21 + 4 * channel[c].octave); break;
+ case 21: opl->write(0xa0 + c,0x20); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 15: opl->write(0xa0 + c,0x63); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
+ case 255: // delay
+ channel[c].songptr += maxchannel;
+ channel[c].pstat = songbuf[channel[c].songptr];
+ break;
+ case 254: // set octave
+ channel[c].songptr += maxchannel;
+ channel[c].octave = songbuf[channel[c].songptr];
+ break;
+ case 253: // set speed
+ channel[c].songptr += maxchannel;
+ channel[c].speed = songbuf[channel[c].songptr];
+ break;
+ case 252: // set waveform
+ channel[c].songptr += maxchannel;
+ channel[c].waveform = songbuf[channel[c].songptr] - 300;
+ if(c > 2)
+ opl->write(0xe0 + c + (c+6),channel[c].waveform);
+ else
+ opl->write(0xe0 + c,channel[c].waveform);
+ break;
+ case 251: // song end
+ for(i = 0; i < maxchannel; i++) channel[i].songptr = i;
+ songend = true;
+ return false;
+ }
+
+ if(channel[c].songptr - c < maxnotes)
+ channel[c].songptr += maxchannel;
+ else
+ channel[c].songptr = c;
+ } while(!channel[c].pstat);
+ }
+
+ return !songend;
+}
+
+void CmkjPlayer::rewind(int subsong)
+{
+ int i;
+
+ for(i = 0; i < maxchannel; i++) {
+ channel[i].pstat = 0;
+ channel[i].speed = 0;
+ channel[i].waveform = 0;
+ channel[i].songptr = i;
+ channel[i].octave = 4;
+ }
+
+ songend = false;
+}
+
+float CmkjPlayer::getrefresh()
+{
+ return 100.0f;
+}
diff --git a/plugins/adplug/adplug/mkj.h b/plugins/adplug/adplug/mkj.h
new file mode 100644
index 00000000..a7a2ffad
--- /dev/null
+++ b/plugins/adplug/adplug/mkj.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+ *
+ * mkj.h - MKJamz Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CmkjPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmkjPlayer(Copl *newopl)
+ : CPlayer(newopl), songbuf(0)
+ { }
+ ~CmkjPlayer()
+ { if(songbuf) delete [] songbuf; }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("MKJamz Audio File"); }
+
+private:
+ short maxchannel,maxnotes,*songbuf;
+ bool songend;
+
+ struct {
+ short defined,songptr,octave,waveform,pstat,speed,delay;
+ } channel[9];
+};
diff --git a/plugins/adplug/adplug/msc.cpp b/plugins/adplug/adplug/msc.cpp
new file mode 100644
index 00000000..3702adcf
--- /dev/null
+++ b/plugins/adplug/adplug/msc.cpp
@@ -0,0 +1,313 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * msc.c - MSC Player by Lubomir Bulej (pallas@kadan.cz)
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "msc.h"
+#include "debug.h"
+
+const unsigned char CmscPlayer::msc_signature [MSC_SIGN_LEN] = {
+ 'C', 'e', 'r', 'e', 's', ' ', '\x13', ' ',
+ 'M', 'S', 'C', 'p', 'l', 'a', 'y', ' ' };
+
+/*** public methods *************************************/
+
+CPlayer *CmscPlayer::factory (Copl * newopl)
+{
+ return new CmscPlayer (newopl);
+}
+
+CmscPlayer::CmscPlayer(Copl * newopl) : CPlayer (newopl)
+{
+ desc = NULL;
+ msc_data = NULL;
+ raw_data = NULL;
+ nr_blocks = 0;
+}
+
+CmscPlayer::~CmscPlayer()
+{
+ if (raw_data != NULL)
+ delete [] raw_data;
+
+ if (msc_data != NULL) {
+ // free compressed blocks
+ for (int blk_num = 0; blk_num < nr_blocks; blk_num++) {
+ if (msc_data [blk_num].mb_data != NULL)
+ delete [] msc_data [blk_num].mb_data;
+ }
+
+ delete [] msc_data;
+ }
+
+ if (desc != NULL)
+ delete [] desc;
+}
+
+bool CmscPlayer::load(const std::string & filename, const CFileProvider & fp)
+{
+ binistream * bf;
+ msc_header hdr;
+
+ // open and validate the file
+ bf = fp.open (filename);
+ if (! bf)
+ return false;
+
+ if (! load_header (bf, & hdr)) {
+ fp.close (bf);
+ return false;
+ }
+
+ // get stuff from the header
+ version = hdr.mh_ver;
+ timer_div = hdr.mh_timer;
+ nr_blocks = hdr.mh_nr_blocks;
+ block_len = hdr.mh_block_len;
+
+ if (! nr_blocks) {
+ fp.close (bf);
+ return false;
+ }
+
+ // load compressed data blocks
+ msc_data = new msc_block [nr_blocks];
+ raw_data = new u8 [block_len];
+
+ for (int blk_num = 0; blk_num < nr_blocks; blk_num++) {
+ msc_block blk;
+
+ blk.mb_length = bf->readInt (2);
+ blk.mb_data = new u8 [blk.mb_length];
+ for (int oct_num = 0; oct_num < blk.mb_length; oct_num++) {
+ blk.mb_data [oct_num] = bf->readInt (1);
+ }
+
+ msc_data [blk_num] = blk;
+ }
+
+ // clean up & initialize
+ fp.close (bf);
+ rewind (0);
+
+ return true;
+}
+
+bool CmscPlayer::update()
+{
+ // output data
+ while (! delay) {
+ u8 cmnd;
+ u8 data;
+
+ // decode data
+ if (! decode_octet (& cmnd))
+ return false;
+
+ if (! decode_octet (& data))
+ return false;
+
+ // check for special commands
+ switch (cmnd) {
+
+ // delay
+ case 0xff:
+ delay = 1 + (u8) (data - 1);
+ break;
+
+ // play command & data
+ default:
+ opl->write (cmnd, data);
+
+ } // command switch
+ } // play pass
+
+
+ // count delays
+ if (delay)
+ delay--;
+
+ // advance player position
+ play_pos++;
+ return true;
+}
+
+void CmscPlayer::rewind(int subsong)
+{
+ // reset state
+ dec_prefix = 0;
+ block_num = 0;
+ block_pos = 0;
+ play_pos = 0;
+ raw_pos = 0;
+ delay = 0;
+
+ // init the OPL chip and go to OPL2 mode
+ opl->init();
+ opl->write(1, 32);
+}
+
+float CmscPlayer::getrefresh()
+{
+ // PC timer oscillator frequency / wait register
+ return 1193180 / (float) (timer_div ? timer_div : 0xffff);
+}
+
+std::string CmscPlayer::gettype()
+{
+ char vstr [40];
+
+ sprintf(vstr, "AdLib MSCplay (version %d)", version);
+ return std::string (vstr);
+}
+
+/*** private methods *************************************/
+
+bool CmscPlayer::load_header(binistream * bf, msc_header * hdr)
+{
+ // check signature
+ bf->readString ((char *) hdr->mh_sign, sizeof (hdr->mh_sign));
+ if (memcmp (msc_signature, hdr->mh_sign, MSC_SIGN_LEN) != 0)
+ return false;
+
+ // check version
+ hdr->mh_ver = bf->readInt (2);
+ if (hdr->mh_ver != 0)
+ return false;
+
+ bf->readString ((char *) hdr->mh_desc, sizeof (hdr->mh_desc));
+ hdr->mh_timer = bf->readInt (2);
+ hdr->mh_nr_blocks = bf->readInt (2);
+ hdr->mh_block_len = bf->readInt (2);
+ return true;
+}
+
+bool CmscPlayer::decode_octet(u8 * output)
+{
+ msc_block blk; // compressed data block
+
+ if (block_num >= nr_blocks)
+ return false;
+
+ blk = msc_data [block_num];
+ while (1) {
+ u8 octet; // decoded octet
+ u8 len_corr; // length correction
+
+ // advance to next block if necessary
+ if (block_pos >= blk.mb_length && dec_len == 0) {
+ block_num++;
+ if (block_num >= nr_blocks)
+ return false;
+
+ blk = msc_data [block_num];
+ block_pos = 0;
+ raw_pos = 0;
+ }
+
+ // decode the compressed music data
+ switch (dec_prefix) {
+
+ // decode prefix
+ case 155:
+ case 175:
+ octet = blk.mb_data [block_pos++];
+ if (octet == 0) {
+ // invalid prefix, output original
+ octet = dec_prefix;
+ dec_prefix = 0;
+ break;
+ }
+
+ // isolate length and distance
+ dec_len = (octet & 0x0F);
+ len_corr = 2;
+
+ dec_dist = (octet & 0xF0) >> 4;
+ if (dec_prefix == 155)
+ dec_dist++;
+
+ // next decode step for respective prefix type
+ dec_prefix++;
+ continue;
+
+
+ // check for extended length
+ case 156:
+ if (dec_len == 15)
+ dec_len += blk.mb_data [block_pos++];
+
+ // add length correction and go for copy mode
+ dec_len += len_corr;
+ dec_prefix = 255;
+ continue;
+
+
+ // get extended distance
+ case 176:
+ dec_dist += 17 + 16 * blk.mb_data [block_pos++];
+ len_corr = 3;
+
+ // check for extended length
+ dec_prefix = 156;
+ continue;
+
+
+ // prefix copy mode
+ case 255:
+ if((int)raw_pos >= dec_dist)
+ octet = raw_data [raw_pos - dec_dist];
+ else {
+ AdPlug_LogWrite("error! read before raw_data buffer.\n");
+ octet = 0;
+ }
+
+ dec_len--;
+ if (dec_len == 0) {
+ // back to normal mode
+ dec_prefix = 0;
+ }
+
+ break;
+
+
+ // normal mode
+ default:
+ octet = blk.mb_data [block_pos++];
+ if (octet == 155 || octet == 175) {
+ // it's a prefix, restart
+ dec_prefix = octet;
+ continue;
+ }
+ } // prefix switch
+
+
+ // output the octet
+ if (output != NULL)
+ *output = octet;
+
+ raw_data [raw_pos++] = octet;
+ break;
+ }; // decode pass
+
+ return true;
+}
diff --git a/plugins/adplug/adplug/msc.h b/plugins/adplug/adplug/msc.h
new file mode 100644
index 00000000..a42ec750
--- /dev/null
+++ b/plugins/adplug/adplug/msc.h
@@ -0,0 +1,87 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * msc.h - MSC Player by Lubomir Bulej (pallas@kadan.cz)
+ */
+
+#include "player.h"
+
+#define MSC_SIGN_LEN 16
+#define MSC_DESC_LEN 64
+
+class CmscPlayer: public CPlayer
+{
+ public:
+ static CPlayer * factory(Copl * newopl);
+
+ CmscPlayer(Copl * newopl);
+ ~CmscPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype ();
+
+ protected:
+ typedef unsigned char u8;
+ typedef unsigned short u16;
+
+ struct msc_header {
+ u8 mh_sign [MSC_SIGN_LEN];
+ u16 mh_ver;
+ u8 mh_desc [MSC_DESC_LEN];
+ u16 mh_timer;
+ u16 mh_nr_blocks;
+ u16 mh_block_len;
+ };
+
+ struct msc_block {
+ u16 mb_length;
+ u8 * mb_data;
+ };
+
+ // file data
+ char * desc; // song desctiption
+ unsigned short version; // file version
+ unsigned short nr_blocks; // number of music blocks
+ unsigned short block_len; // maximal block length
+ unsigned short timer_div; // timer divisor
+ msc_block * msc_data; // compressed music data
+
+ // decoder state
+ unsigned long block_num; // active block
+ unsigned long block_pos; // position in block
+ unsigned long raw_pos; // position in data buffer
+ u8 * raw_data; // decompression buffer
+
+ u8 dec_prefix; // prefix / state
+ int dec_dist; // prefix distance
+ unsigned int dec_len; // prefix length
+
+ // player state
+ unsigned char delay; // active delay
+ unsigned long play_pos; // player position
+
+ private:
+ static const u8 msc_signature [MSC_SIGN_LEN];
+
+ bool load_header (binistream * bf, msc_header * hdr);
+ bool decode_octet (u8 * output);
+};
diff --git a/plugins/adplug/adplug/mtk.cpp b/plugins/adplug/adplug/mtk.cpp
new file mode 100644
index 00000000..410e00e0
--- /dev/null
+++ b/plugins/adplug/adplug/mtk.cpp
@@ -0,0 +1,141 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mtk.cpp - MPU-401 Trakker Loader by Simon Peter (dn.tlp@gmx.net)
+ */
+
+#include <string.h>
+#include "mtk.h"
+
+/*** public methods **************************************/
+
+CPlayer *CmtkLoader::factory(Copl *newopl)
+{
+ return new CmtkLoader(newopl);
+}
+
+bool CmtkLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ struct {
+ char id[18];
+ unsigned short crc,size;
+ } header;
+ struct mtkdata {
+ char songname[34],composername[34],instname[0x80][34];
+ unsigned char insts[0x80][12],order[0x80],dummy,patterns[0x32][0x40][9];
+ } *data;
+ unsigned char *cmp,*org;
+ unsigned int i;
+ unsigned long cmpsize,cmpptr=0,orgptr=0;
+ unsigned short ctrlbits=0,ctrlmask=0,cmd,cnt,offs;
+
+ // read header
+ f->readString(header.id, 18);
+ header.crc = f->readInt(2);
+ header.size = f->readInt(2);
+
+ // file validation section
+ if(strncmp(header.id,"mpu401tr\x92kk\xeer@data",18))
+ { fp.close(f); return false; }
+
+ // load section
+ cmpsize = fp.filesize(f) - 22;
+ cmp = new unsigned char[cmpsize];
+ org = new unsigned char[header.size];
+ for(i = 0; i < cmpsize; i++) cmp[i] = f->readInt(1);
+ fp.close(f);
+
+ while(cmpptr < cmpsize) { // decompress
+ ctrlmask >>= 1;
+ if(!ctrlmask) {
+ ctrlbits = cmp[cmpptr] + (cmp[cmpptr + 1] << 8);
+ cmpptr += 2;
+ ctrlmask = 0x8000;
+ }
+ if(!(ctrlbits & ctrlmask)) { // uncompressed data
+ if(orgptr >= header.size)
+ goto err;
+
+ org[orgptr] = cmp[cmpptr];
+ orgptr++; cmpptr++;
+ continue;
+ }
+
+ // compressed data
+ cmd = (cmp[cmpptr] >> 4) & 0x0f;
+ cnt = cmp[cmpptr] & 0x0f;
+ cmpptr++;
+ switch(cmd) {
+ case 0:
+ if(orgptr + cnt > header.size) goto err;
+ cnt += 3;
+ memset(&org[orgptr],cmp[cmpptr],cnt);
+ cmpptr++; orgptr += cnt;
+ break;
+
+ case 1:
+ if(orgptr + cnt > header.size) goto err;
+ cnt += (cmp[cmpptr] << 4) + 19;
+ memset(&org[orgptr],cmp[++cmpptr],cnt);
+ cmpptr++; orgptr += cnt;
+ break;
+
+ case 2:
+ if(orgptr + cnt > header.size) goto err;
+ offs = (cnt+3) + (cmp[cmpptr] << 4);
+ cnt = cmp[++cmpptr] + 16; cmpptr++;
+ memcpy(&org[orgptr],&org[orgptr - offs],cnt);
+ orgptr += cnt;
+ break;
+
+ default:
+ if(orgptr + cmd > header.size) goto err;
+ offs = (cnt+3) + (cmp[cmpptr++] << 4);
+ memcpy(&org[orgptr],&org[orgptr-offs],cmd);
+ orgptr += cmd;
+ break;
+ }
+ }
+ delete [] cmp;
+ data = (struct mtkdata *) org;
+
+ // convert to HSC replay data
+ memset(title,0,34); strncpy(title,data->songname+1,33);
+ memset(composer,0,34); strncpy(composer,data->composername+1,33);
+ memset(instname,0,0x80*34);
+ for(i=0;i<0x80;i++)
+ strncpy(instname[i],data->instname[i]+1,33);
+ memcpy(instr,data->insts,0x80 * 12);
+ memcpy(song,data->order,0x80);
+ memcpy(patterns,data->patterns,header.size-6084);
+ for (i=0;i<128;i++) { // correct instruments
+ instr[i][2] ^= (instr[i][2] & 0x40) << 1;
+ instr[i][3] ^= (instr[i][3] & 0x40) << 1;
+ instr[i][11] >>= 4; // make unsigned
+ }
+
+ delete [] org;
+ rewind(0);
+ return true;
+
+ err:
+ delete [] cmp;
+ delete [] org;
+ return false;
+}
diff --git a/plugins/adplug/adplug/mtk.h b/plugins/adplug/adplug/mtk.h
new file mode 100644
index 00000000..175576a2
--- /dev/null
+++ b/plugins/adplug/adplug/mtk.h
@@ -0,0 +1,50 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * mtk.h - MPU-401 Trakker Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "hsc.h"
+
+class CmtkLoader: public ChscPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CmtkLoader(Copl *newopl)
+ : ChscPlayer(newopl)
+ {
+ mtkmode = 1;
+ };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+
+ std::string gettype()
+ { return std::string("MPU-401 Trakker"); };
+ std::string gettitle()
+ { return std::string(title); };
+ std::string getauthor()
+ { return std::string(composer); };
+ unsigned int getinstruments()
+ { return 128; };
+ std::string getinstrument(unsigned int n)
+ { return std::string(instname[n]); };
+
+ private:
+ char title[34],composer[34],instname[0x80][34];
+};
diff --git a/plugins/adplug/adplug/opl.h b/plugins/adplug/adplug/opl.h
new file mode 100644
index 00000000..401bcb98
--- /dev/null
+++ b/plugins/adplug/adplug/opl.h
@@ -0,0 +1,69 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * opl.h - OPL base class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_OPL
+#define H_ADPLUG_OPL
+
+class Copl
+{
+ public:
+ typedef enum {
+ TYPE_OPL2, TYPE_OPL3, TYPE_DUAL_OPL2
+ } ChipType;
+
+ Copl()
+ : currChip(0), currType(TYPE_OPL2)
+ {
+ }
+
+ virtual ~Copl()
+ {
+ }
+
+ virtual void write(int reg, int val) = 0; // combined register select + data write
+ virtual void setchip(int n) // select OPL chip
+ {
+ if(n < 2)
+ currChip = n;
+ }
+
+ virtual int getchip() // returns current OPL chip
+ {
+ return currChip;
+ }
+
+ virtual void init(void) = 0; // reinitialize OPL chip(s)
+
+ // return this OPL chip's type
+ ChipType gettype()
+ {
+ return currType;
+ }
+
+ // Emulation only: fill buffer
+ virtual void update(short *buf, int samples) {}
+
+ protected:
+ int currChip; // currently selected OPL chip number
+ ChipType currType; // this OPL chip's type
+};
+
+#endif
diff --git a/plugins/adplug/adplug/player.cpp b/plugins/adplug/adplug/player.cpp
new file mode 100644
index 00000000..9a0061af
--- /dev/null
+++ b/plugins/adplug/adplug/player.cpp
@@ -0,0 +1,70 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * player.cpp - Replayer base class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+#include "adplug.h"
+#include "silentopl.h"
+
+/***** CPlayer *****/
+
+const unsigned short CPlayer::note_table[12] =
+ {363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, 686};
+
+const unsigned char CPlayer::op_table[9] =
+ {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
+
+CPlayer::CPlayer(Copl *newopl)
+ : opl(newopl), db(CAdPlug::database)
+{
+}
+
+CPlayer::~CPlayer()
+{
+}
+
+unsigned long CPlayer::songlength(int subsong)
+{
+ CSilentopl tempopl;
+ Copl *saveopl = opl;
+ float slength = 0.0f;
+
+ // save original OPL from being overwritten
+ opl = &tempopl;
+
+ // get song length
+ rewind(subsong);
+ while(update() && slength < 600000) // song length limit: 10 minutes
+ slength += 1000.0f / getrefresh();
+ rewind(subsong);
+
+ // restore original OPL and return
+ opl = saveopl;
+ return (unsigned long)slength;
+}
+
+void CPlayer::seek(unsigned long ms)
+{
+ float pos = 0.0f;
+
+ rewind();
+ while(pos < ms && update()) // seek to new position
+ pos += 1000/getrefresh();
+}
diff --git a/plugins/adplug/adplug/player.h b/plugins/adplug/adplug/player.h
new file mode 100644
index 00000000..fae3ef85
--- /dev/null
+++ b/plugins/adplug/adplug/player.h
@@ -0,0 +1,84 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * player.h - Replayer base class, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_PLAYER
+#define H_ADPLUG_PLAYER
+
+#include <string>
+
+#include "../../../deadbeef.h"
+#include "fprovide.h"
+#include "opl.h"
+#include "database.h"
+
+class CPlayer
+{
+public:
+ CPlayer(Copl *newopl);
+ virtual ~CPlayer();
+
+/***** Operational methods *****/
+ void seek(unsigned long ms);
+
+ virtual bool load(const std::string &fn, // loads file
+ const CFileProvider &fp = CProvider_Filesystem()) = 0;
+ virtual bool update() = 0; // executes replay code for 1 tick
+ virtual void rewind(int subsong = -1) = 0; // rewinds to specified subsong
+ virtual float getrefresh() = 0; // returns needed timer refresh rate
+
+/***** Informational methods *****/
+ unsigned long songlength(int subsong = -1);
+
+ virtual std::string gettype() = 0; // returns file type
+ virtual std::string gettitle() // returns song title
+ { return std::string(); }
+ virtual std::string getauthor() // returns song author name
+ { return std::string(); }
+ virtual std::string getdesc() // returns song description
+ { return std::string(); }
+ virtual unsigned int getpatterns() // returns number of patterns
+ { return 0; }
+ virtual unsigned int getpattern() // returns currently playing pattern
+ { return 0; }
+ virtual unsigned int getorders() // returns size of orderlist
+ { return 0; }
+ virtual unsigned int getorder() // returns currently playing song position
+ { return 0; }
+ virtual unsigned int getrow() // returns currently playing row
+ { return 0; }
+ virtual unsigned int getspeed() // returns current song speed
+ { return 0; }
+ virtual unsigned int getsubsongs() // returns number of subsongs
+ { return 1; }
+ virtual unsigned int getinstruments() // returns number of instruments
+ { return 0; }
+ virtual std::string getinstrument(unsigned int n) // returns n-th instrument name
+ { return std::string(); }
+
+protected:
+ Copl *opl; // our OPL chip
+ CAdPlugDatabase *db; // AdPlug Database
+
+ static const unsigned short note_table[12]; // standard adlib note table
+ static const unsigned char op_table[9]; // the 9 operators as expected by the OPL
+};
+
+#endif
diff --git a/plugins/adplug/adplug/players.cpp b/plugins/adplug/adplug/players.cpp
new file mode 100644
index 00000000..09c32b73
--- /dev/null
+++ b/plugins/adplug/adplug/players.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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
+ *
+ * players.h - Players enumeration, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "players.h"
+
+/***** CPlayerDesc *****/
+
+CPlayerDesc::CPlayerDesc()
+ : factory(0), extensions(0), extlength(0)
+{
+}
+
+CPlayerDesc::CPlayerDesc(const CPlayerDesc &pd)
+ : factory(pd.factory), filetype(pd.filetype), extlength(pd.extlength)
+{
+ if(pd.extensions) {
+ extensions = (char *)malloc(extlength);
+ memcpy(extensions, pd.extensions, extlength);
+ } else
+ extensions = 0;
+}
+
+CPlayerDesc::CPlayerDesc(Factory f, const std::string &type, const char *ext)
+ : factory(f), filetype(type), extensions(0)
+{
+ const char *i = ext;
+
+ // Determine length of passed extensions list
+ while(*i) i += strlen(i) + 1;
+ extlength = i - ext + 1; // length = difference between last and first char + 1
+
+ extensions = (char *)malloc(extlength);
+ memcpy(extensions, ext, extlength);
+}
+
+CPlayerDesc::~CPlayerDesc()
+{
+ if(extensions) free(extensions);
+}
+
+void CPlayerDesc::add_extension(const char *ext)
+{
+ unsigned long newlength = extlength + strlen(ext) + 1;
+
+ extensions = (char *)realloc(extensions, newlength);
+ strcpy(extensions + extlength - 1, ext);
+ extensions[newlength - 1] = '\0';
+ extlength = newlength;
+}
+
+const char *CPlayerDesc::get_extension(unsigned int n) const
+{
+ const char *i = extensions;
+ unsigned int j;
+
+ for(j = 0; j < n && (*i); j++, i += strlen(i) + 1) ;
+ return (*i != '\0' ? i : 0);
+}
+
+/***** CPlayers *****/
+
+const CPlayerDesc *CPlayers::lookup_filetype(const std::string &ftype) const
+{
+ const_iterator i;
+
+ for(i = begin(); i != end(); i++)
+ if((*i)->filetype == ftype)
+ return *i;
+
+ return 0;
+}
+
+const CPlayerDesc *CPlayers::lookup_extension(const std::string &extension) const
+{
+ const_iterator i;
+ unsigned int j;
+
+ for(i = begin(); i != end(); i++)
+ for(j = 0; (*i)->get_extension(j); j++)
+ if(!stricmp(extension.c_str(), (*i)->get_extension(j)))
+ return *i;
+
+ return 0;
+}
diff --git a/plugins/adplug/adplug/players.h b/plugins/adplug/adplug/players.h
new file mode 100644
index 00000000..9d686aef
--- /dev/null
+++ b/plugins/adplug/adplug/players.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+ *
+ * players.h - Players enumeration, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_PLAYERS
+#define H_ADPLUG_PLAYERS
+
+#include <string>
+#include <list>
+
+#include "opl.h"
+#include "player.h"
+
+class CPlayerDesc
+{
+public:
+ typedef CPlayer *(*Factory)(Copl *);
+
+ Factory factory;
+ std::string filetype;
+
+ CPlayerDesc();
+ CPlayerDesc(const CPlayerDesc &pd);
+ CPlayerDesc(Factory f, const std::string &type, const char *ext);
+
+ ~CPlayerDesc();
+
+ void add_extension(const char *ext);
+ const char *get_extension(unsigned int n) const;
+
+private:
+ char *extensions;
+ unsigned long extlength;
+};
+
+class CPlayers: public std::list<const CPlayerDesc *>
+{
+public:
+ const CPlayerDesc *lookup_filetype(const std::string &ftype) const;
+ const CPlayerDesc *lookup_extension(const std::string &extension) const;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/protrack.cpp b/plugins/adplug/adplug/protrack.cpp
new file mode 100644
index 00000000..86716ef8
--- /dev/null
+++ b/plugins/adplug/adplug/protrack.cpp
@@ -0,0 +1,820 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * protrack.cpp - Generic Protracker Player
+ *
+ * NOTES:
+ * This is a generic Protracker-based formats player. It offers all Protracker
+ * features, plus a good set of extensions to be compatible to other Protracker
+ * derivatives. It is derived from the former SA2 player. If you got a
+ * Protracker-like format, this is most certainly the player you want to use.
+ */
+
+#include <string.h>
+#include "protrack.h"
+#include "debug.h"
+
+#define SPECIALARPLEN 256 // Standard length of special arpeggio lists
+#define JUMPMARKER 0x80 // Orderlist jump marker
+
+// SA2 compatible adlib note table
+const unsigned short CmodPlayer::sa2_notetable[12] =
+ {340,363,385,408,432,458,485,514,544,577,611,647};
+
+// SA2 compatible vibrato rate table
+const unsigned char CmodPlayer::vibratotab[32] =
+ {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 *************************************/
+
+CmodPlayer::CmodPlayer(Copl *newopl)
+ : CPlayer(newopl), inst(0), order(0), arplist(0), arpcmd(0), initspeed(6),
+ nop(0), activechan(0xffffffff), flags(Standard), curchip(opl->getchip()),
+ nrows(0), npats(0), nchans(0)
+{
+ realloc_order(128);
+ realloc_patterns(64, 64, 9);
+ realloc_instruments(250);
+ init_notetable(sa2_notetable);
+}
+
+CmodPlayer::~CmodPlayer()
+{
+ dealloc();
+}
+
+bool CmodPlayer::update()
+{
+ unsigned char pattbreak=0, donote, pattnr, chan, oplchan, info1,
+ info2, info, pattern_delay;
+ unsigned short track;
+ unsigned long row;
+
+ if(!speed) // song full stop
+ return !songend;
+
+ // effect handling (timer dependant)
+ for(chan = 0; chan < nchans; chan++) {
+ oplchan = set_opl_chip(chan);
+
+ if(arplist && arpcmd && inst[channel[chan].inst].arpstart) // special arpeggio
+ if(channel[chan].arpspdcnt)
+ channel[chan].arpspdcnt--;
+ else
+ if(arpcmd[channel[chan].arppos] != 255) {
+ switch(arpcmd[channel[chan].arppos]) {
+ case 252: channel[chan].vol1 = arplist[channel[chan].arppos]; // set volume
+ if(channel[chan].vol1 > 63) // ?????
+ channel[chan].vol1 = 63;
+ channel[chan].vol2 = channel[chan].vol1;
+ setvolume(chan);
+ break;
+ case 253: channel[chan].key = 0; setfreq(chan); break; // release sustaining note
+ case 254: channel[chan].arppos = arplist[channel[chan].arppos]; break; // arpeggio loop
+ default: if(arpcmd[channel[chan].arppos]) {
+ if(arpcmd[channel[chan].arppos] / 10)
+ opl->write(0xe3 + op_table[oplchan], arpcmd[channel[chan].arppos] / 10 - 1);
+ if(arpcmd[channel[chan].arppos] % 10)
+ opl->write(0xe0 + op_table[oplchan], (arpcmd[channel[chan].arppos] % 10) - 1);
+ if(arpcmd[channel[chan].arppos] < 10) // ?????
+ opl->write(0xe0 + op_table[oplchan], arpcmd[channel[chan].arppos] - 1);
+ }
+ }
+ if(arpcmd[channel[chan].arppos] != 252) {
+ if(arplist[channel[chan].arppos] <= 96)
+ setnote(chan,channel[chan].note + arplist[channel[chan].arppos]);
+ if(arplist[channel[chan].arppos] >= 100)
+ setnote(chan,arplist[channel[chan].arppos] - 100);
+ } else
+ setnote(chan,channel[chan].note);
+ setfreq(chan);
+ if(arpcmd[channel[chan].arppos] != 255)
+ channel[chan].arppos++;
+ channel[chan].arpspdcnt = inst[channel[chan].inst].arpspeed - 1;
+ }
+
+ info1 = channel[chan].info1;
+ info2 = channel[chan].info2;
+ if(flags & Decimal)
+ info = channel[chan].info1 * 10 + channel[chan].info2;
+ else
+ info = (channel[chan].info1 << 4) + channel[chan].info2;
+ switch(channel[chan].fx) {
+ case 0: if(info) { // arpeggio
+ if(channel[chan].trigger < 2)
+ channel[chan].trigger++;
+ else
+ channel[chan].trigger = 0;
+ switch(channel[chan].trigger) {
+ case 0: setnote(chan,channel[chan].note); break;
+ case 1: setnote(chan,channel[chan].note + info1); break;
+ case 2: setnote(chan,channel[chan].note + info2);
+ }
+ setfreq(chan);
+ }
+ break;
+ case 1: slide_up(chan,info); setfreq(chan); break; // slide up
+ case 2: slide_down(chan,info); setfreq(chan); break; // slide down
+ case 3: tone_portamento(chan,channel[chan].portainfo); break; // tone portamento
+ case 4: vibrato(chan,channel[chan].vibinfo1,channel[chan].vibinfo2); break; // vibrato
+ case 5: // tone portamento & volume slide
+ case 6: if(channel[chan].fx == 5) // vibrato & volume slide
+ tone_portamento(chan,channel[chan].portainfo);
+ else
+ vibrato(chan,channel[chan].vibinfo1,channel[chan].vibinfo2);
+ case 10: if(del % 4) // SA2 volume slide
+ break;
+ if(info1)
+ vol_up(chan,info1);
+ else
+ vol_down(chan,info2);
+ setvolume(chan);
+ break;
+ case 14: if(info1 == 3) // retrig note
+ if(!(del % (info2+1)))
+ playnote(chan);
+ break;
+ case 16: if(del % 4) // AMD volume slide
+ break;
+ if(info1)
+ vol_up_alt(chan,info1);
+ else
+ vol_down_alt(chan,info2);
+ setvolume(chan);
+ break;
+ case 20: // RAD volume slide
+ if(info < 50)
+ vol_down_alt(chan,info);
+ else
+ vol_up_alt(chan,info - 50);
+ setvolume(chan);
+ break;
+ case 26: // volume slide
+ if(info1)
+ vol_up(chan,info1);
+ else
+ vol_down(chan,info2);
+ setvolume(chan);
+ break;
+ case 28:
+ if (info1) {
+ slide_up(chan,1); channel[chan].info1--;
+ }
+ if (info2) {
+ slide_down(chan,1); channel[chan].info2--;
+ }
+ setfreq(chan);
+ break;
+ }
+ }
+
+ if(del) { // speed compensation
+ del--;
+ return !songend;
+ }
+
+ // arrangement handling
+ if(!resolve_order()) return !songend;
+ pattnr = order[ord];
+
+ if(!rw) AdPlug_LogWrite("\nCmodPlayer::update(): Pattern: %d, Order: %d\n", pattnr, ord);
+ AdPlug_LogWrite("CmodPlayer::update():%3d|", rw);
+
+ // play row
+ pattern_delay = 0;
+ row = rw;
+ for(chan = 0; chan < nchans; chan++) {
+ oplchan = set_opl_chip(chan);
+
+ if(!(activechan >> (31 - chan)) & 1) { // channel active?
+ AdPlug_LogWrite("N/A|");
+ continue;
+ }
+ if(!(track = trackord[pattnr][chan])) { // resolve track
+ AdPlug_LogWrite("------------|");
+ continue;
+ } else
+ track--;
+
+ AdPlug_LogWrite("%3d%3d%2X%2X%2X|", tracks[track][row].note,
+ tracks[track][row].inst, tracks[track][row].command,
+ tracks[track][row].param1, tracks[track][row].param2);
+
+ donote = 0;
+ if(tracks[track][row].inst) {
+ channel[chan].inst = tracks[track][row].inst - 1;
+ if (!(flags & Faust)) {
+ channel[chan].vol1 = 63 - (inst[channel[chan].inst].data[10] & 63);
+ channel[chan].vol2 = 63 - (inst[channel[chan].inst].data[9] & 63);
+ setvolume(chan);
+ }
+ }
+
+ if(tracks[track][row].note && tracks[track][row].command != 3) { // no tone portamento
+ channel[chan].note = tracks[track][row].note;
+ setnote(chan,tracks[track][row].note);
+ channel[chan].nextfreq = channel[chan].freq;
+ channel[chan].nextoct = channel[chan].oct;
+ channel[chan].arppos = inst[channel[chan].inst].arpstart;
+ channel[chan].arpspdcnt = 0;
+ if(tracks[track][row].note != 127) // handle key off
+ donote = 1;
+ }
+ channel[chan].fx = tracks[track][row].command;
+ channel[chan].info1 = tracks[track][row].param1;
+ channel[chan].info2 = tracks[track][row].param2;
+
+ if(donote)
+ playnote(chan);
+
+ // command handling (row dependant)
+ info1 = channel[chan].info1;
+ info2 = channel[chan].info2;
+ if(flags & Decimal)
+ info = channel[chan].info1 * 10 + channel[chan].info2;
+ else
+ info = (channel[chan].info1 << 4) + channel[chan].info2;
+ switch(channel[chan].fx) {
+ case 3: // tone portamento
+ if(tracks[track][row].note) {
+ if(tracks[track][row].note < 13)
+ channel[chan].nextfreq = notetable[tracks[track][row].note - 1];
+ else
+ if(tracks[track][row].note % 12 > 0)
+ channel[chan].nextfreq = notetable[(tracks[track][row].note % 12) - 1];
+ else
+ channel[chan].nextfreq = notetable[11];
+ channel[chan].nextoct = (tracks[track][row].note - 1) / 12;
+ if(tracks[track][row].note == 127) { // handle key off
+ channel[chan].nextfreq = channel[chan].freq;
+ channel[chan].nextoct = channel[chan].oct;
+ }
+ }
+ if(info) // remember vars
+ channel[chan].portainfo = info;
+ break;
+
+ case 4: // vibrato (remember vars)
+ if(info) {
+ channel[chan].vibinfo1 = info1;
+ channel[chan].vibinfo2 = info2;
+ }
+ break;
+
+ case 7: tempo = info; break; // set tempo
+
+ case 8: channel[chan].key = 0; setfreq(chan); break; // release sustaining note
+
+ case 9: // set carrier/modulator volume
+ if(info1)
+ channel[chan].vol1 = info1 * 7;
+ else
+ channel[chan].vol2 = info2 * 7;
+ setvolume(chan);
+ break;
+
+ case 11: // position jump
+ pattbreak = 1; rw = 0; if(info < ord) songend = 1; ord = info; break;
+
+ case 12: // set volume
+ channel[chan].vol1 = info;
+ channel[chan].vol2 = info;
+ if(channel[chan].vol1 > 63)
+ channel[chan].vol1 = 63;
+ if(channel[chan].vol2 > 63)
+ channel[chan].vol2 = 63;
+ setvolume(chan);
+ break;
+
+ case 13: // pattern break
+ if(!pattbreak) { pattbreak = 1; rw = info; ord++; } break;
+
+ case 14: // extended command
+ switch(info1) {
+ case 0: // define cell-tremolo
+ if(info2)
+ regbd |= 128;
+ else
+ regbd &= 127;
+ opl->write(0xbd,regbd);
+ break;
+
+ case 1: // define cell-vibrato
+ if(info2)
+ regbd |= 64;
+ else
+ regbd &= 191;
+ opl->write(0xbd,regbd);
+ break;
+
+ case 4: // increase volume fine
+ vol_up_alt(chan,info2);
+ setvolume(chan);
+ break;
+
+ case 5: // decrease volume fine
+ vol_down_alt(chan,info2);
+ setvolume(chan);
+ break;
+
+ case 6: // manual slide up
+ slide_up(chan,info2);
+ setfreq(chan);
+ break;
+
+ case 7: // manual slide down
+ slide_down(chan,info2);
+ setfreq(chan);
+ break;
+
+ case 8: // pattern delay (rows)
+ pattern_delay = info2 * speed;
+ break;
+ }
+ break;
+
+ case 15: // SA2 set speed
+ if(info <= 0x1f)
+ speed = info;
+ if(info >= 0x32)
+ tempo = info;
+ if(!info)
+ songend = 1;
+ break;
+
+ case 17: // alternate set volume
+ channel[chan].vol1 = info;
+ if(channel[chan].vol1 > 63)
+ channel[chan].vol1 = 63;
+ if(inst[channel[chan].inst].data[0] & 1) {
+ channel[chan].vol2 = info;
+ if(channel[chan].vol2 > 63)
+ channel[chan].vol2 = 63;
+ }
+
+ setvolume(chan);
+ break;
+
+ case 18: // AMD set speed
+ if(info <= 31 && info > 0)
+ speed = info;
+ if(info > 31 || !info)
+ tempo = info;
+ break;
+
+ case 19: // RAD/A2M set speed
+ speed = (info ? info : info + 1);
+ break;
+
+ case 21: // set modulator volume
+ if(info <= 63)
+ channel[chan].vol2 = info;
+ else
+ channel[chan].vol2 = 63;
+ setvolume(chan);
+ break;
+
+ case 22: // set carrier volume
+ if(info <= 63)
+ channel[chan].vol1 = info;
+ else
+ channel[chan].vol1 = 63;
+ setvolume(chan);
+ break;
+
+ case 23: // fine frequency slide up
+ slide_up(chan,info);
+ setfreq(chan);
+ break;
+
+ case 24: // fine frequency slide down
+ slide_down(chan,info);
+ setfreq(chan);
+ break;
+
+ case 25: // set carrier/modulator waveform
+ if(info1 != 0x0f)
+ opl->write(0xe3 + op_table[oplchan],info1);
+ if(info2 != 0x0f)
+ opl->write(0xe0 + op_table[oplchan],info2);
+ break;
+
+ case 27: // set chip tremolo/vibrato
+ if(info1)
+ regbd |= 128;
+ else
+ regbd &= 127;
+ if(info2)
+ regbd |= 64;
+ else
+ regbd &= 191;
+ opl->write(0xbd,regbd);
+ break;
+
+ case 29: // pattern delay (frames)
+ pattern_delay = info;
+ break;
+ }
+ }
+
+ // speed compensation
+ del = speed - 1 + pattern_delay;
+
+ if(!pattbreak) { // next row (only if no manual advance)
+ rw++;
+ if(rw >= nrows) {
+ rw = 0;
+ ord++;
+ }
+ }
+
+ resolve_order(); // so we can report songend right away
+ AdPlug_LogWrite("\n");
+ return !songend;
+}
+
+unsigned char CmodPlayer::set_opl_chip(unsigned char chan)
+ /*
+ * Sets OPL chip according to channel number. Channels 0-8 are on first chip,
+ * channels 9-17 are on second chip. Returns corresponding OPL channel
+ * number.
+ */
+{
+ int newchip = chan < 9 ? 0 : 1;
+
+ if(newchip != curchip) {
+ opl->setchip(newchip);
+ curchip = newchip;
+ }
+
+ return chan % 9;
+}
+
+bool CmodPlayer::resolve_order()
+ /*
+ * Resolves current orderlist entry, checking for jumps and loops.
+ *
+ * Returns true on correct processing, false if immediate recursive loop
+ * has been detected.
+ */
+{
+ if(ord < length) {
+ while(order[ord] >= JUMPMARKER) { // jump to order
+ unsigned long neword = order[ord] - JUMPMARKER;
+
+ if(neword <= ord) songend = 1;
+ if(neword == ord) return false;
+ ord = neword;
+ }
+ } else {
+ songend = 1;
+ ord = restartpos;
+ }
+
+ return true;
+}
+
+void CmodPlayer::rewind(int subsong)
+{
+ unsigned long i;
+
+ // Reset playing variables
+ songend = del = ord = rw = regbd = 0;
+ tempo = bpm; speed = initspeed;
+
+ // Reset channel data
+ memset(channel,0,sizeof(Channel)*nchans);
+
+ // Compute number of patterns, if needed
+ if(!nop)
+ for(i=0;i<length;i++)
+ nop = (order[i] > nop ? order[i] : nop);
+
+ opl->init(); // Reset OPL chip
+ opl->write(1, 32); // Go to ym3812 mode
+
+ // Enable OPL3 extensions if flagged
+ if(flags & Opl3) {
+ opl->setchip(1);
+ opl->write(1, 32);
+ opl->write(5, 1);
+ opl->setchip(0);
+ }
+
+ // Enable tremolo/vibrato depth if flagged
+ if(flags & Tremolo) regbd |= 128;
+ if(flags & Vibrato) regbd |= 64;
+ if(regbd) opl->write(0xbd, regbd);
+}
+
+float CmodPlayer::getrefresh()
+{
+ return (float) (tempo / 2.5);
+}
+
+void CmodPlayer::init_trackord()
+{
+ unsigned long i;
+
+ for(i=0;i<npats*nchans;i++)
+ trackord[i / nchans][i % nchans] = i + 1;
+}
+
+bool CmodPlayer::init_specialarp()
+{
+ arplist = new unsigned char[SPECIALARPLEN];
+ arpcmd = new unsigned char[SPECIALARPLEN];
+
+ return true;
+}
+
+void CmodPlayer::init_notetable(const unsigned short *newnotetable)
+{
+ memcpy(notetable, newnotetable, 12 * 2);
+}
+
+bool CmodPlayer::realloc_order(unsigned long len)
+{
+ if(order) delete [] order;
+ order = new unsigned char[len];
+ return true;
+}
+
+bool CmodPlayer::realloc_patterns(unsigned long pats, unsigned long rows, unsigned long chans)
+{
+ unsigned long i;
+
+ dealloc_patterns();
+
+ // set new number of tracks, rows and channels
+ npats = pats; nrows = rows; nchans = chans;
+
+ // alloc new patterns
+ tracks = new Tracks *[pats * chans];
+ for(i=0;i<pats*chans;i++) tracks[i] = new Tracks[rows];
+ trackord = new unsigned short *[pats];
+ for(i=0;i<pats;i++) trackord[i] = new unsigned short[chans];
+ channel = new Channel[chans];
+
+ // initialize new patterns
+ for(i=0;i<pats*chans;i++) memset(tracks[i],0,sizeof(Tracks)*rows);
+ for(i=0;i<pats;i++) memset(trackord[i],0,chans*2);
+
+ return true;
+}
+
+void CmodPlayer::dealloc_patterns()
+{
+ unsigned long i;
+
+ // dealloc everything previously allocated
+ if(npats && nrows && nchans) {
+ for(i=0;i<npats*nchans;i++) delete [] tracks[i];
+ delete [] tracks;
+ for(i=0;i<npats;i++) delete [] trackord[i];
+ delete [] trackord;
+ delete [] channel;
+ }
+}
+
+bool CmodPlayer::realloc_instruments(unsigned long len)
+{
+ // dealloc previous instance, if any
+ if(inst) delete [] inst;
+
+ inst = new Instrument[len];
+ memset(inst,0,sizeof(Instrument)*len); // reset instruments
+ return true;
+}
+
+void CmodPlayer::dealloc()
+{
+ if(inst) delete [] inst;
+ if(order) delete [] order;
+ if(arplist) delete [] arplist;
+ if(arpcmd) delete [] arpcmd;
+ dealloc_patterns();
+}
+
+/*** private methods *************************************/
+
+void CmodPlayer::setvolume(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+
+ if(flags & Faust)
+ setvolume_alt(chan);
+ else {
+ opl->write(0x40 + op_table[oplchan], 63-channel[chan].vol2 + (inst[channel[chan].inst].data[9] & 192));
+ opl->write(0x43 + op_table[oplchan], 63-channel[chan].vol1 + (inst[channel[chan].inst].data[10] & 192));
+ }
+}
+
+void CmodPlayer::setvolume_alt(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+ unsigned char ivol2 = inst[channel[chan].inst].data[9] & 63;
+ unsigned char ivol1 = inst[channel[chan].inst].data[10] & 63;
+
+ opl->write(0x40 + op_table[oplchan], (((63 - channel[chan].vol2 & 63) + ivol2) >> 1) + (inst[channel[chan].inst].data[9] & 192));
+ opl->write(0x43 + op_table[oplchan], (((63 - channel[chan].vol1 & 63) + ivol1) >> 1) + (inst[channel[chan].inst].data[10] & 192));
+}
+
+void CmodPlayer::setfreq(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+
+ opl->write(0xa0 + oplchan, channel[chan].freq & 255);
+ if(channel[chan].key)
+ opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
+ else
+ opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
+}
+
+void CmodPlayer::playnote(unsigned char chan)
+{
+ unsigned char oplchan = set_opl_chip(chan);
+ unsigned char op = op_table[oplchan], insnr = channel[chan].inst;
+
+ if(!(flags & NoKeyOn))
+ opl->write(0xb0 + oplchan, 0); // stop old note
+
+ // set instrument data
+ opl->write(0x20 + op, inst[insnr].data[1]);
+ opl->write(0x23 + op, inst[insnr].data[2]);
+ opl->write(0x60 + op, inst[insnr].data[3]);
+ opl->write(0x63 + op, inst[insnr].data[4]);
+ opl->write(0x80 + op, inst[insnr].data[5]);
+ opl->write(0x83 + op, inst[insnr].data[6]);
+ opl->write(0xe0 + op, inst[insnr].data[7]);
+ opl->write(0xe3 + op, inst[insnr].data[8]);
+ opl->write(0xc0 + oplchan, inst[insnr].data[0]);
+ opl->write(0xbd, inst[insnr].misc); // set misc. register
+
+ // set frequency, volume & play
+ channel[chan].key = 1;
+ setfreq(chan);
+
+ if (flags & Faust) {
+ channel[chan].vol2 = 63;
+ channel[chan].vol1 = 63;
+ }
+ setvolume(chan);
+}
+
+void CmodPlayer::setnote(unsigned char chan, int note)
+{
+ if(note > 96)
+ if(note == 127) { // key off
+ channel[chan].key = 0;
+ setfreq(chan);
+ return;
+ } else
+ note = 96;
+
+ if(note < 13)
+ channel[chan].freq = notetable[note - 1];
+ else
+ if(note % 12 > 0)
+ channel[chan].freq = notetable[(note % 12) - 1];
+ else
+ channel[chan].freq = notetable[11];
+ channel[chan].oct = (note - 1) / 12;
+ channel[chan].freq += inst[channel[chan].inst].slide; // apply pre-slide
+}
+
+void CmodPlayer::slide_down(unsigned char chan, int amount)
+{
+ channel[chan].freq -= amount;
+ if(channel[chan].freq <= 342)
+ if(channel[chan].oct) {
+ channel[chan].oct--;
+ channel[chan].freq <<= 1;
+ } else
+ channel[chan].freq = 342;
+}
+
+void CmodPlayer::slide_up(unsigned char chan, int amount)
+{
+ channel[chan].freq += amount;
+ if(channel[chan].freq >= 686)
+ if(channel[chan].oct < 7) {
+ channel[chan].oct++;
+ channel[chan].freq >>= 1;
+ } else
+ channel[chan].freq = 686;
+}
+
+void CmodPlayer::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)) {
+ channel[chan].freq = channel[chan].nextfreq;
+ channel[chan].oct = channel[chan].nextoct;
+ }
+ }
+ if(channel[chan].freq + (channel[chan].oct << 10) > channel[chan].nextfreq +
+ (channel[chan].nextoct << 10)) {
+ slide_down(chan,info);
+ if(channel[chan].freq + (channel[chan].oct << 10) < channel[chan].nextfreq +
+ (channel[chan].nextoct << 10)) {
+ channel[chan].freq = channel[chan].nextfreq;
+ channel[chan].oct = channel[chan].nextoct;
+ }
+ }
+ setfreq(chan);
+}
+
+void CmodPlayer::vibrato(unsigned char chan, unsigned char speed, unsigned char depth)
+{
+ int i;
+
+ if(!speed || !depth)
+ return;
+
+ if(depth > 14)
+ depth = 14;
+
+ 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,vibratotab[channel[chan].trigger - 16] / (16-depth));
+ if(channel[chan].trigger < 16)
+ slide_up(chan,vibratotab[channel[chan].trigger + 16] / (16-depth));
+ if(channel[chan].trigger >= 48)
+ slide_up(chan,vibratotab[channel[chan].trigger - 48] / (16-depth));
+ }
+ setfreq(chan);
+}
+
+void CmodPlayer::vol_up(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 + amount < 63)
+ channel[chan].vol1 += amount;
+ else
+ channel[chan].vol1 = 63;
+
+ if(channel[chan].vol2 + amount < 63)
+ channel[chan].vol2 += amount;
+ else
+ channel[chan].vol2 = 63;
+}
+
+void CmodPlayer::vol_down(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 - amount > 0)
+ channel[chan].vol1 -= amount;
+ else
+ channel[chan].vol1 = 0;
+
+ if(channel[chan].vol2 - amount > 0)
+ channel[chan].vol2 -= amount;
+ else
+ channel[chan].vol2 = 0;
+}
+
+void CmodPlayer::vol_up_alt(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 + amount < 63)
+ channel[chan].vol1 += amount;
+ else
+ channel[chan].vol1 = 63;
+ if(inst[channel[chan].inst].data[0] & 1)
+ if(channel[chan].vol2 + amount < 63)
+ channel[chan].vol2 += amount;
+ else
+ channel[chan].vol2 = 63;
+}
+
+void CmodPlayer::vol_down_alt(unsigned char chan, int amount)
+{
+ if(channel[chan].vol1 - amount > 0)
+ channel[chan].vol1 -= amount;
+ else
+ channel[chan].vol1 = 0;
+ if(inst[channel[chan].inst].data[0] & 1)
+ if(channel[chan].vol2 - amount > 0)
+ channel[chan].vol2 -= amount;
+ else
+ channel[chan].vol2 = 0;
+}
diff --git a/plugins/adplug/adplug/protrack.h b/plugins/adplug/adplug/protrack.h
new file mode 100644
index 00000000..8da9489e
--- /dev/null
+++ b/plugins/adplug/adplug/protrack.h
@@ -0,0 +1,119 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * protrack.h - Generic Protracker Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_PROTRACK
+#define H_PROTRACK
+
+#include "player.h"
+
+class CmodPlayer: public CPlayer
+{
+public:
+ CmodPlayer(Copl *newopl);
+ virtual ~CmodPlayer();
+
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ unsigned int getpatterns()
+ { return nop; }
+ unsigned int getpattern()
+ { return order[ord]; }
+ unsigned int getorders()
+ { return length; }
+ unsigned int getorder()
+ { return ord; }
+ unsigned int getrow()
+ { return rw; }
+ unsigned int getspeed()
+ { return speed; }
+
+ protected:
+ enum Flags {
+ Standard = 0,
+ Decimal = 1 << 0,
+ Faust = 1 << 1,
+ NoKeyOn = 1 << 2,
+ Opl3 = 1 << 3,
+ Tremolo = 1 << 4,
+ Vibrato = 1 << 5,
+ Percussion = 1 << 6
+ };
+
+ struct Instrument {
+ unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt,misc;
+ signed char slide;
+ } *inst;
+
+ struct Tracks {
+ unsigned char note,command,inst,param2,param1;
+ } **tracks;
+
+ unsigned char *order, *arplist, *arpcmd, initspeed;
+ unsigned short tempo, **trackord, bpm, nop;
+ unsigned long length, restartpos, activechan;
+ int flags, curchip;
+
+ struct Channel {
+ unsigned short freq,nextfreq;
+ unsigned char oct,vol1,vol2,inst,fx,info1,info2,key,nextoct,
+ note,portainfo,vibinfo1,vibinfo2,arppos,arpspdcnt;
+ signed char trigger;
+ } *channel;
+
+ void init_trackord();
+ bool init_specialarp();
+ void init_notetable(const unsigned short *newnotetable);
+ bool realloc_order(unsigned long len);
+ bool realloc_patterns(unsigned long pats, unsigned long rows, unsigned long chans);
+ bool realloc_instruments(unsigned long len);
+
+ void dealloc();
+
+ private:
+ static const unsigned short sa2_notetable[12];
+ static const unsigned char vibratotab[32];
+
+ unsigned char speed, del, songend, regbd;
+ unsigned short rows, notetable[12];
+ unsigned long rw, ord, nrows, npats, nchans;
+
+ void setvolume(unsigned char chan);
+ void setvolume_alt(unsigned char chan);
+ void setfreq(unsigned char chan);
+ void playnote(unsigned char chan);
+ void setnote(unsigned char chan, int note);
+ void slide_down(unsigned char chan, int amount);
+ void slide_up(unsigned char chan, int amount);
+ void tone_portamento(unsigned char chan, unsigned char info);
+ void vibrato(unsigned char chan, unsigned char speed, unsigned char depth);
+ void vol_up(unsigned char chan, int amount);
+ void vol_down(unsigned char chan, int amount);
+ void vol_up_alt(unsigned char chan, int amount);
+ void vol_down_alt(unsigned char chan, int amount);
+
+ void dealloc_patterns();
+ bool resolve_order();
+ unsigned char set_opl_chip(unsigned char chan);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/psi.cpp b/plugins/adplug/adplug/psi.cpp
new file mode 100644
index 00000000..00184526
--- /dev/null
+++ b/plugins/adplug/adplug/psi.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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
+ *
+ * [xad] PSI player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : 4BIDDEN.COM, PGRID.EXE
+ type : Forbidden Dreams BBStro
+ Power Grid BBStro
+ tune : by Friar Tuck [Shadow Faction/ICE]
+ player : by Psi [Future Crew]
+ comment : seems to me what 4bidden tune & player was ripped from pgrid
+
+ file(s) : MYSTRUNE.COM
+ type : Mystical Runes BBStro
+ tune : by ?
+ player : by Psi [Future Crew]
+*/
+
+#include "psi.h"
+#include "debug.h"
+
+const unsigned char CxadpsiPlayer::psi_adlib_registers[99] =
+{
+ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xE0, 0xE3, 0xC0,
+ 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xE1, 0xE4, 0xC1,
+ 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xE2, 0xE5, 0xC2,
+ 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xE8, 0xEB, 0xC3,
+ 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xE9, 0xEC, 0xC4,
+ 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xEA, 0xED, 0xC5,
+ 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xF0, 0xF3, 0xC6,
+ 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xF1, 0xF4, 0xC7,
+ 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xF2, 0xF5, 0xC8
+};
+
+const unsigned short CxadpsiPlayer::psi_notes[16] =
+{
+ 0x216B, 0x2181, 0x2198, 0x21B0, 0x21CA, 0x21E5, 0x2202, 0x2220,
+ 0x2241, 0x2263, 0x2287, 0x2364,
+ 0x0000, 0x0000, 0x0000, 0x0000 // by riven
+};
+
+CPlayer *CxadpsiPlayer::factory(Copl *newopl)
+{
+ return new CxadpsiPlayer(newopl);
+}
+
+void CxadpsiPlayer::xadplayer_rewind(int subsong)
+{
+ opl_write(0x01, 0x20);
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0x00);
+
+ // get header
+ header.instr_ptr = (tune[1] << 8) + tune[0];
+ header.seq_ptr = (tune[3] << 8) + tune[2];
+
+ // define instruments
+ psi.instr_table = &tune[header.instr_ptr];
+
+ for(int i=0; i<8; i++)
+ {
+ for(int j=0; j<11; j++) {
+ unsigned short inspos = (psi.instr_table[i * 2 + 1] << 8) + psi.instr_table[i * 2];
+
+ opl_write(psi_adlib_registers[i*11 + j],tune[inspos + j]);
+ }
+
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+
+ psi.note_delay[i] = 1;
+ psi.note_curdelay[i] = 1;
+ psi.looping[i] = 0;
+ }
+
+ // calculate sequence pointer
+ psi.seq_table = &tune[header.seq_ptr];
+}
+
+void CxadpsiPlayer::xadplayer_update()
+{
+ unsigned short ptr;
+
+ for(int i=0; i<8; i++)
+ {
+ ptr = (psi.seq_table[(i<<1) * 2 + 1] << 8) + psi.seq_table[(i<<1) * 2];
+
+ psi.note_curdelay[i]--;
+
+ if (!psi.note_curdelay[i])
+ {
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+
+ unsigned char event = tune[ptr++];
+#ifdef DEBUG
+ AdPlug_LogWrite("channel %02X, event %02X:\n",i+1,event);
+#endif
+
+ // end of sequence ?
+ if (!event)
+ {
+ ptr = (psi.seq_table[(i<<1) * 2 + 3] << 8) + psi.seq_table[(i<<1) * 2 + 2];
+
+ event = tune[ptr++];
+#ifdef DEBUG
+ AdPlug_LogWrite(" channel %02X, event %02X:\n",i+1,event);
+#endif
+
+ // set sequence loop flag
+ psi.looping[i] = 1;
+
+ // module loop ?
+ plr.looping = 1;
+ for(int j=0; j<8; j++)
+ plr.looping &= psi.looping[j];
+ }
+
+ // new note delay ?
+ if (event & 0x80)
+ {
+ psi.note_delay[i] = (event & 0x7F);
+
+ event = tune[ptr++];
+#ifdef DEBUG
+ AdPlug_LogWrite(" channel %02X, event %02X:\n",i+1,event);
+#endif
+ }
+
+ psi.note_curdelay[i] = psi.note_delay[i];
+
+ // play note
+ unsigned short note = psi_notes[event & 0x0F];
+
+ opl_write(0xA0+i, note & 0xFF);
+ opl_write(0xB0+i, (note >> 8) + ((event >> 2) & 0xFC));
+
+ // save position
+ psi.seq_table[(i<<1) * 2] = ptr & 0xff;
+ psi.seq_table[(i<<1) * 2 + 1] = ptr >> 8;
+ }
+ }
+}
+
+float CxadpsiPlayer::xadplayer_getrefresh()
+{
+ return 70.0f;
+}
+
+std::string CxadpsiPlayer::xadplayer_gettype()
+{
+ return std::string("xad: psi player");
+}
+
+unsigned int CxadpsiPlayer::xadplayer_getinstruments()
+{
+ return 8;
+}
diff --git a/plugins/adplug/adplug/psi.h b/plugins/adplug/adplug/psi.h
new file mode 100644
index 00000000..9714ed12
--- /dev/null
+++ b/plugins/adplug/adplug/psi.h
@@ -0,0 +1,64 @@
+/*
+ * 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
+ *
+ * [xad] PSI player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadpsiPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadpsiPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct psi_header
+ {
+ unsigned short instr_ptr;
+ unsigned short seq_ptr;
+ } header;
+
+ struct
+ {
+ unsigned char *instr_table;
+ unsigned char *seq_table;
+ unsigned char note_delay[9];
+ unsigned char note_curdelay[9];
+ unsigned char looping[9];
+ } psi;
+ //
+ bool xadplayer_load()
+ {
+ if(xad.fmt == PSI)
+ return true;
+ else
+ return false;
+ }
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ unsigned int xadplayer_getinstruments();
+
+private:
+ static const unsigned char psi_adlib_registers[99];
+ static const unsigned short psi_notes[16];
+};
diff --git a/plugins/adplug/adplug/rad.cpp b/plugins/adplug/adplug/rad.cpp
new file mode 100644
index 00000000..0178a6c3
--- /dev/null
+++ b/plugins/adplug/adplug/rad.cpp
@@ -0,0 +1,125 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * rad.cpp - RAD Loader by Simon Peter <dn.tlp@gmx.net>
+ *
+ * BUGS:
+ * some volumes are dropped out
+ */
+
+#include <string.h>
+#include "rad.h"
+
+CPlayer *CradLoader::factory(Copl *newopl)
+{
+ return new CradLoader(newopl);
+}
+
+bool CradLoader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[16];
+ unsigned char buf,ch,c,b,inp;
+ char bufstr[2] = "\0";
+ unsigned int i,j;
+ unsigned short patofs[32];
+ const unsigned char convfx[16] = {255,1,2,3,255,5,255,255,255,255,20,255,17,0xd,255,19};
+
+ // file validation section
+ f->readString(id, 16); version = f->readInt(1);
+ if(strncmp(id,"RAD by REALiTY!!",16) || version != 0x10)
+ { fp.close(f); return false; }
+
+ // load section
+ radflags = f->readInt(1);
+ if(radflags & 128) { // description
+ memset(desc,0,80*22);
+ while((buf = f->readInt(1)))
+ if(buf == 1)
+ strcat(desc,"\n");
+ else
+ if(buf >= 2 && buf <= 0x1f)
+ for(i=0;i<buf;i++)
+ strcat(desc," ");
+ else {
+ *bufstr = buf;
+ strcat(desc,bufstr);
+ }
+ }
+ while((buf = f->readInt(1))) { // instruments
+ buf--;
+ inst[buf].data[2] = f->readInt(1); inst[buf].data[1] = f->readInt(1);
+ inst[buf].data[10] = f->readInt(1); inst[buf].data[9] = f->readInt(1);
+ inst[buf].data[4] = f->readInt(1); inst[buf].data[3] = f->readInt(1);
+ inst[buf].data[6] = f->readInt(1); inst[buf].data[5] = f->readInt(1);
+ inst[buf].data[0] = f->readInt(1);
+ inst[buf].data[8] = f->readInt(1); inst[buf].data[7] = f->readInt(1);
+ }
+ length = f->readInt(1);
+ for(i = 0; i < length; i++) order[i] = f->readInt(1); // orderlist
+ for(i = 0; i < 32; i++) patofs[i] = f->readInt(2); // pattern offset table
+ init_trackord(); // patterns
+ for(i=0;i<32;i++)
+ if(patofs[i]) {
+ f->seek(patofs[i]);
+ do {
+ buf = f->readInt(1); b = buf & 127;
+ do {
+ ch = f->readInt(1); c = ch & 127;
+ inp = f->readInt(1);
+ tracks[i*9+c][b].note = inp & 127;
+ tracks[i*9+c][b].inst = (inp & 128) >> 3;
+ inp = f->readInt(1);
+ tracks[i*9+c][b].inst += inp >> 4;
+ tracks[i*9+c][b].command = inp & 15;
+ if(inp & 15) {
+ inp = f->readInt(1);
+ tracks[i*9+c][b].param1 = inp / 10;
+ tracks[i*9+c][b].param2 = inp % 10;
+ }
+ } while(!(ch & 128));
+ } while(!(buf & 128));
+ } else
+ memset(trackord[i],0,9*2);
+ fp.close(f);
+
+ // convert replay data
+ for(i=0;i<32*9;i++) // convert patterns
+ for(j=0;j<64;j++) {
+ if(tracks[i][j].note == 15)
+ tracks[i][j].note = 127;
+ if(tracks[i][j].note > 16 && tracks[i][j].note < 127)
+ tracks[i][j].note -= 4 * (tracks[i][j].note >> 4);
+ if(tracks[i][j].note && tracks[i][j].note < 126)
+ tracks[i][j].note++;
+ tracks[i][j].command = convfx[tracks[i][j].command];
+ }
+ restartpos = 0; initspeed = radflags & 31;
+ bpm = radflags & 64 ? 0 : 50; flags = Decimal;
+
+ rewind(0);
+ return true;
+}
+
+float CradLoader::getrefresh()
+{
+ if(tempo)
+ return (float) (tempo);
+ else
+ return 18.2f;
+}
diff --git a/plugins/adplug/adplug/rad.h b/plugins/adplug/adplug/rad.h
new file mode 100644
index 00000000..07814b8a
--- /dev/null
+++ b/plugins/adplug/adplug/rad.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ *
+ * rad.h - RAD Loader by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "protrack.h"
+
+class CradLoader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CradLoader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { *desc = '\0'; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("Reality ADlib Tracker"); };
+ std::string getdesc()
+ { return std::string(desc); };
+
+private:
+ unsigned char version,radflags;
+ char desc[80*22];
+};
diff --git a/plugins/adplug/adplug/rat.cpp b/plugins/adplug/adplug/rat.cpp
new file mode 100644
index 00000000..b20af250
--- /dev/null
+++ b/plugins/adplug/adplug/rat.cpp
@@ -0,0 +1,293 @@
+/*
+ * 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
+ *
+ * [xad] RAT player, by Riven the Mage <riven@ok.ru>
+ */
+
+/*
+ - discovery -
+
+ file(s) : PINA.EXE
+ type : Experimental Connection BBStro tune
+ tune : by (?)Ratt/GRIF
+ player : by (?)Ratt/GRIF
+ comment : there are bug in original replayer's adlib_init(): wrong frequency registers.
+*/
+
+#include <string.h>
+#include "rat.h"
+#include "debug.h"
+
+const unsigned char CxadratPlayer::rat_adlib_bases[18] =
+{
+ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12,
+ 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15
+};
+
+const unsigned short CxadratPlayer::rat_notes[16] =
+{
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287,
+ 0x000, 0x000, 0x000, 0x000 // by riven
+};
+
+CPlayer *CxadratPlayer::factory(Copl *newopl)
+{
+ return new CxadratPlayer(newopl);
+}
+
+bool CxadratPlayer::xadplayer_load()
+{
+ if(xad.fmt != RAT)
+ return false;
+
+ // load header
+ memcpy(&rat.hdr, &tune[0], sizeof(rat_header));
+
+ // is 'RAT'-signed ?
+ if (strncmp(rat.hdr.id,"RAT",3))
+ return false;
+
+ // is version 1.0 ?
+ if (rat.hdr.version != 0x10)
+ return false;
+
+ // load order
+ rat.order = &tune[0x40];
+
+ // load instruments
+ rat.inst = (rat_instrument *)&tune[0x140];
+
+ // load pattern data
+ unsigned short patseg = (rat.hdr.patseg[1] << 8) + rat.hdr.patseg[0];
+ unsigned char *event_ptr = &tune[patseg << 4];
+
+ for(int i=0;i<rat.hdr.numpat;i++)
+ for(int j=0;j<64;j++)
+ for(int k=0;k<rat.hdr.numchan;k++)
+ {
+ memcpy(&rat.tracks[i][j][k], event_ptr, sizeof(rat_event));
+
+ event_ptr += sizeof(rat_event);
+ }
+
+ return true;
+}
+
+void CxadratPlayer::xadplayer_rewind(int subsong)
+{
+ int i;
+
+ rat.order_pos = rat.hdr.order_start;
+ rat.pattern_pos = 0;
+ rat.volume = rat.hdr.volume;
+
+ plr.speed = rat.hdr.speed;
+
+ // clear channel data
+ memset(&rat.channel, 0, sizeof(rat.channel[0])*9);
+
+ // init OPL
+ opl_write(0x01, 0x20);
+ opl_write(0x08, 0x00);
+ opl_write(0xBD, 0x00);
+
+ // set default frequencies
+ for(i=0;i<9;i++)
+ {
+ opl_write(0xA0+i, 0x00);
+ opl_write(0xA3+i, 0x00);
+ opl_write(0xB0+i, 0x00);
+ opl_write(0xB3+i, 0x00);
+ }
+
+ // set default volumes
+ for(i=0;i<0x1F;i++)
+ opl_write(0x40+i, 0x3F);
+}
+
+void CxadratPlayer::xadplayer_update()
+{
+ int i;
+
+ rat_event event;
+
+ // process events
+ for(i=0;i<rat.hdr.numchan;i++)
+ {
+ memcpy(&event,&rat.tracks[rat.order[rat.order_pos]][rat.pattern_pos][i],sizeof(rat_event));
+#ifdef DEBUG
+ AdPlug_LogWrite("order %02X, pattern %02X, row %02X, channel %02X, event %02X %02X %02X %02X %02X:\n",
+ rat.order_pos, rat.order[rat.order_pos], rat.pattern_pos, i, event.note, event.instrument, event.volume, event.fx, event.fxp
+ );
+#endif
+
+ // is instrument ?
+ if (event.instrument != 0xFF)
+ {
+ rat.channel[i].instrument = event.instrument - 1;
+ rat.channel[i].volume = rat.inst[event.instrument - 1].volume;
+ }
+
+ // is volume ?
+ if (event.volume != 0xFF)
+ rat.channel[i].volume = event.volume;
+
+ // is note ?
+ if (event.note != 0xFF)
+ {
+ // mute channel
+ opl_write(0xB0+i, 0x00);
+ opl_write(0xA0+i, 0x00);
+
+ // if note != 0xFE then play
+ if (event.note != 0xFE)
+ {
+ unsigned char ins = rat.channel[i].instrument;
+
+ // synthesis/feedback
+ opl_write(0xC0+i, rat.inst[ins].connect);
+
+ // controls
+ opl_write(0x20+rat_adlib_bases[i], rat.inst[ins].mod_ctrl);
+ opl_write(0x20+rat_adlib_bases[i+9], rat.inst[ins].car_ctrl);
+
+ // volumes
+ opl_write(0x40+rat_adlib_bases[i], __rat_calc_volume(rat.inst[ins].mod_volume,rat.channel[i].volume,rat.volume));
+ opl_write(0x40+rat_adlib_bases[i+9], __rat_calc_volume(rat.inst[ins].car_volume,rat.channel[i].volume,rat.volume));
+
+ // attack/decay
+ opl_write(0x60+rat_adlib_bases[i], rat.inst[ins].mod_AD);
+ opl_write(0x60+rat_adlib_bases[i+9], rat.inst[ins].car_AD);
+
+ // sustain/release
+ opl_write(0x80+rat_adlib_bases[i], rat.inst[ins].mod_SR);
+ opl_write(0x80+rat_adlib_bases[i+9], rat.inst[ins].car_SR);
+
+ // waveforms
+ opl_write(0xE0+rat_adlib_bases[i], rat.inst[ins].mod_wave);
+ opl_write(0xE0+rat_adlib_bases[i+9], rat.inst[ins].car_wave);
+
+ // octave/frequency
+ unsigned short insfreq = (rat.inst[ins].freq[1] << 8) + rat.inst[ins].freq[0];
+ unsigned short freq = insfreq * rat_notes[event.note & 0x0F] / 0x20AB;
+
+ opl_write(0xA0+i, freq & 0xFF);
+ opl_write(0xB0+i, (freq >> 8) | ((event.note & 0xF0) >> 2) | 0x20);
+ }
+ }
+
+ // is effect ?
+ if (event.fx != 0xFF)
+ {
+ rat.channel[i].fx = event.fx;
+ rat.channel[i].fxp = event.fxp;
+ }
+ }
+
+ // next row
+ rat.pattern_pos++;
+
+ // process effects
+ for(i=0;i<rat.hdr.numchan;i++)
+ {
+ unsigned char old_order_pos = rat.order_pos;
+
+ switch (rat.channel[i].fx)
+ {
+ case 0x01: // 0x01: Set Speed
+ plr.speed = rat.channel[i].fxp;
+ break;
+ case 0x02: // 0x02: Position Jump
+ if (rat.channel[i].fxp < rat.hdr.order_end)
+ rat.order_pos = rat.channel[i].fxp;
+ else
+ rat.order_pos = 0;
+
+ // jumpback ?
+ if (rat.order_pos <= old_order_pos)
+ plr.looping = 1;
+
+ rat.pattern_pos = 0;
+ break;
+ case 0x03: // 0x03: Pattern Break (?)
+ rat.pattern_pos = 0x40;
+ break;
+ }
+
+ rat.channel[i].fx = 0;
+ }
+
+ // end of pattern ?
+ if (rat.pattern_pos >= 0x40)
+ {
+ rat.pattern_pos = 0;
+
+ rat.order_pos++;
+
+ // end of module ?
+ if (rat.order_pos == rat.hdr.order_end)
+ {
+ rat.order_pos = rat.hdr.order_loop;
+
+ plr.looping = 1;
+ }
+ }
+}
+
+float CxadratPlayer::xadplayer_getrefresh()
+{
+ return 60.0f;
+}
+
+std::string CxadratPlayer::xadplayer_gettype()
+{
+ return (std::string("xad: rat player"));
+}
+
+std::string CxadratPlayer::xadplayer_gettitle()
+{
+ return (std::string(rat.hdr.title,32));
+}
+
+unsigned int CxadratPlayer::xadplayer_getinstruments()
+{
+ return rat.hdr.numinst;
+}
+
+/* -------- Internal Functions ---------------------------- */
+
+unsigned char CxadratPlayer::__rat_calc_volume(unsigned char ivol, unsigned char cvol, unsigned char gvol)
+{
+#ifdef DEBUG
+ AdPlug_LogWrite("volumes: instrument %02X, channel %02X, global %02X:\n", ivol, cvol, gvol);
+#endif
+ unsigned short vol;
+
+ vol = ivol;
+ vol &= 0x3F;
+ vol ^= 0x3F;
+ vol *= cvol;
+ vol >>= 6;
+ vol *= gvol;
+ vol >>= 6;
+ vol ^= 0x3F;
+
+ vol |= ivol & 0xC0;
+
+ return vol;
+}
diff --git a/plugins/adplug/adplug/rat.h b/plugins/adplug/adplug/rat.h
new file mode 100644
index 00000000..cc9ddf71
--- /dev/null
+++ b/plugins/adplug/adplug/rat.h
@@ -0,0 +1,121 @@
+/*
+ * 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
+ *
+ * [xad] RAT player, by Riven the Mage <riven@ok.ru>
+ */
+
+#include "xad.h"
+
+class CxadratPlayer: public CxadPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadratPlayer(Copl *newopl): CxadPlayer(newopl)
+ { }
+
+protected:
+ struct rat_header
+ {
+ char id[3];
+ unsigned char version;
+ char title[32];
+ unsigned char numchan;
+ unsigned char reserved_25;
+ unsigned char order_end;
+ unsigned char reserved_27;
+ unsigned char numinst; // ?: Number of Instruments
+ unsigned char reserved_29;
+ unsigned char numpat; // ?: Number of Patterns
+ unsigned char reserved_2B;
+ unsigned char order_start;
+ unsigned char reserved_2D;
+ unsigned char order_loop;
+ unsigned char reserved_2F;
+ unsigned char volume;
+ unsigned char speed;
+ unsigned char reserved_32[12];
+ unsigned char patseg[2];
+ };
+
+ struct rat_event
+ {
+ unsigned char note;
+ unsigned char instrument;
+ unsigned char volume;
+ unsigned char fx;
+ unsigned char fxp;
+ };
+
+ struct rat_instrument
+ {
+ unsigned char freq[2];
+ unsigned char reserved_2[2];
+ unsigned char mod_ctrl;
+ unsigned char car_ctrl;
+ unsigned char mod_volume;
+ unsigned char car_volume;
+ unsigned char mod_AD;
+ unsigned char car_AD;
+ unsigned char mod_SR;
+ unsigned char car_SR;
+ unsigned char mod_wave;
+ unsigned char car_wave;
+ unsigned char connect;
+ unsigned char reserved_F;
+ unsigned char volume;
+ unsigned char reserved_11[3];
+ };
+
+ struct
+ {
+ rat_header hdr;
+
+ unsigned char volume;
+ unsigned char order_pos;
+ unsigned char pattern_pos;
+
+ unsigned char *order;
+
+ rat_instrument *inst;
+
+ rat_event tracks[256][64][9];
+
+ struct
+ {
+ unsigned char instrument;
+ unsigned char volume;
+ unsigned char fx;
+ unsigned char fxp;
+ } channel[9];
+ } rat;
+ //
+ bool xadplayer_load();
+ void xadplayer_rewind(int subsong);
+ void xadplayer_update();
+ float xadplayer_getrefresh();
+ std::string xadplayer_gettype();
+ std::string xadplayer_gettitle();
+ unsigned int xadplayer_getinstruments();
+ //
+private:
+ static const unsigned char rat_adlib_bases[18];
+ static const unsigned short rat_notes[16];
+
+ unsigned char __rat_calc_volume(unsigned char ivol, unsigned char cvol, unsigned char gvol);
+};
diff --git a/plugins/adplug/adplug/raw.cpp b/plugins/adplug/adplug/raw.cpp
new file mode 100644
index 00000000..1672a1d0
--- /dev/null
+++ b/plugins/adplug/adplug/raw.cpp
@@ -0,0 +1,104 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * raw.c - RAW Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+#include "raw.h"
+
+/*** public methods *************************************/
+
+CPlayer *CrawPlayer::factory(Copl *newopl)
+{
+ return new CrawPlayer(newopl);
+}
+
+bool CrawPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[8];
+ unsigned long i;
+
+ // file validation section
+ f->readString(id, 8);
+ if(strncmp(id,"RAWADATA",8)) { fp.close (f); return false; }
+
+ // load section
+ clock = f->readInt(2); // clock speed
+ length = (fp.filesize(f) - 10) / 2;
+ data = new Tdata [length];
+ for(i = 0; i < length; i++) {
+ data[i].param = f->readInt(1);
+ data[i].command = f->readInt(1);
+ }
+
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CrawPlayer::update()
+{
+ bool setspeed;
+
+ if(pos >= length) return false;
+
+ if(del) {
+ del--;
+ return !songend;
+ }
+
+ do {
+ setspeed = false;
+ switch(data[pos].command) {
+ case 0: del = data[pos].param - 1; break;
+ case 2:
+ if(!data[pos].param) {
+ pos++;
+ speed = data[pos].param + (data[pos].command << 8);
+ setspeed = true;
+ } else
+ opl->setchip(data[pos].param - 1);
+ break;
+ case 0xff:
+ if(data[pos].param == 0xff) {
+ rewind(0); // auto-rewind song
+ songend = true;
+ return !songend;
+ }
+ break;
+ default:
+ opl->write(data[pos].command,data[pos].param);
+ break;
+ }
+ } while(data[pos++].command || setspeed);
+
+ return !songend;
+}
+
+void CrawPlayer::rewind(int subsong)
+{
+ pos = del = 0; speed = clock; songend = false;
+ opl->init(); opl->write(1, 32); // go to 9 channel mode
+}
+
+float CrawPlayer::getrefresh()
+{
+ return 1193180.0 / (speed ? speed : 0xffff); // timer oscillator speed / wait register = clock frequency
+}
diff --git a/plugins/adplug/adplug/raw.h b/plugins/adplug/adplug/raw.h
new file mode 100644
index 00000000..e05f1fe7
--- /dev/null
+++ b/plugins/adplug/adplug/raw.h
@@ -0,0 +1,52 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * raw.h - RAW Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CrawPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CrawPlayer(Copl *newopl)
+ : CPlayer(newopl), data(0)
+ { };
+ ~CrawPlayer()
+ { if(data) delete [] data; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("RdosPlay RAW"); };
+
+protected:
+ struct Tdata {
+ unsigned char param, command;
+ } *data;
+
+ unsigned long pos, length;
+ unsigned short clock, speed;
+ unsigned char del;
+ bool songend;
+};
diff --git a/plugins/adplug/adplug/realopl.cpp b/plugins/adplug/adplug/realopl.cpp
new file mode 100644
index 00000000..e8dcda81
--- /dev/null
+++ b/plugins/adplug/adplug/realopl.cpp
@@ -0,0 +1,208 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * realopl.cpp - Real hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifdef _MSC_VER // Microsoft Visual C++
+# include <conio.h>
+# define INP _inp
+# define OUTP _outp
+#elif defined(__WATCOMC__) // Watcom C/C++ and OpenWatcom
+# include <conio.h>
+# define INP inp
+# define OUTP outp
+#elif defined(WIN32) && defined(__MSVCRT__) && defined(__MINGW32__) // MinGW32
+/*
+int __cdecl _inp(unsigned short);
+int __cdecl _outp(unsigned short, int);
+# define INP _inp
+# define OUTP _outp
+*/
+# define INP inb
+# define OUTP(reg, val) outb(val, reg)
+#elif defined(DJGPP) // DJGPP
+# include <pc.h>
+# define INP inportb
+# define OUTP outportb
+#else // no support on other platforms
+# define INP(reg) 0
+# define OUTP(reg, val)
+#endif
+
+#include "realopl.h"
+
+#define SHORTDELAY 6 // short delay in I/O port-reads after OPL hardware output
+#define LONGDELAY 35 // long delay in I/O port-reads after OPL hardware output
+
+const unsigned char CRealopl::op_table[9] =
+ {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
+
+#if defined(WIN32) && defined(__MINGW32__)
+static __inline unsigned char inb(unsigned short int port)
+{
+ unsigned char _v;
+
+ __asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (port));
+ return _v;
+}
+
+static __inline void outb(unsigned char value, unsigned short int port)
+{
+ __asm__ __volatile__ ("outb %b0,%w1": :"a" (value), "Nd" (port));
+}
+#endif
+
+CRealopl::CRealopl(unsigned short initport)
+ : adlport(initport), hardvol(0), bequiet(false), nowrite(false)
+{
+ for(int i=0;i<22;i++) {
+ hardvols[0][i][0] = 0;
+ hardvols[0][i][1] = 0;
+ hardvols[1][i][0] = 0;
+ hardvols[1][i][1] = 0;
+ }
+
+ currType = TYPE_OPL3;
+}
+
+bool CRealopl::harddetect()
+{
+ unsigned char stat1, stat2, i;
+ unsigned short adp = (currChip == 0 ? adlport : adlport + 2);
+
+ hardwrite(4,0x60); hardwrite(4,0x80);
+ stat1 = INP(adp);
+ hardwrite(2,0xff); hardwrite(4,0x21);
+ for(i=0;i<80;i++) // wait for adlib
+ INP(adp);
+ stat2 = INP(adp);
+ hardwrite(4,0x60); hardwrite(4,0x80);
+
+ if(((stat1 & 0xe0) == 0) && ((stat2 & 0xe0) == 0xc0))
+ return true;
+ else
+ return false;
+}
+
+bool CRealopl::detect()
+{
+ unsigned char stat;
+
+ setchip(0);
+ if(harddetect()) {
+ // is at least OPL2, check for OPL3
+ currType = TYPE_OPL2;
+
+ stat = INP(adlport);
+ if(stat & 6) {
+ // not OPL3, try dual-OPL2
+ setchip(1);
+ if(harddetect())
+ currType = TYPE_DUAL_OPL2;
+ } else
+ currType = TYPE_OPL3;
+
+ setchip(0);
+ return true;
+ } else
+ return false;
+}
+
+void CRealopl::setvolume(int volume)
+{
+ int i, j;
+
+ hardvol = volume;
+ for(j = 0; j < 2; j++)
+ for(i = 0; i < 9; i++) {
+ hardwrite(0x43+op_table[i],((hardvols[j][op_table[i]+3][0] & 63) + volume) > 63 ? 63 : hardvols[j][op_table[i]+3][0] + volume);
+ if(hardvols[j][i][1] & 1) // modulator too?
+ hardwrite(0x40+op_table[i],((hardvols[j][op_table[i]][0] & 63) + volume) > 63 ? 63 : hardvols[j][op_table[i]][0] + volume);
+ }
+}
+
+void CRealopl::setquiet(bool quiet)
+{
+ bequiet = quiet;
+
+ if(quiet) {
+ oldvol = hardvol;
+ setvolume(63);
+ } else
+ setvolume(oldvol);
+}
+
+void CRealopl::hardwrite(int reg, int val)
+{
+ int i;
+ unsigned short adp = (currChip == 0 ? adlport : adlport + 2);
+
+ OUTP(adp,reg); // set register
+ for(i=0;i<SHORTDELAY;i++) // wait for adlib
+ INP(adp);
+ OUTP(adp+1,val); // set value
+ for(i=0;i<LONGDELAY;i++) // wait for adlib
+ INP(adp);
+}
+
+void CRealopl::write(int reg, int val)
+{
+ int i;
+
+ if(nowrite)
+ return;
+
+ if(currType == TYPE_OPL2 && currChip > 0)
+ return;
+
+ if(bequiet && (reg >= 0xb0 && reg <= 0xb8)) // filter all key-on commands
+ val &= ~32;
+ if(reg >= 0x40 && reg <= 0x55) // cache volumes
+ hardvols[currChip][reg-0x40][0] = val;
+ if(reg >= 0xc0 && reg <= 0xc8)
+ hardvols[currChip][reg-0xc0][1] = val;
+ if(hardvol) // reduce volume
+ for(i=0;i<9;i++) {
+ if(reg == 0x43 + op_table[i])
+ val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol;
+ else
+ if((reg == 0x40 + op_table[i]) && (hardvols[currChip][i][1] & 1))
+ val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol;
+ }
+
+ hardwrite(reg,val);
+}
+
+void CRealopl::init()
+{
+ int i, j;
+
+ for(j = 0; j < 2; j++) {
+ setchip(j);
+
+ for(i=0;i<9;i++) { // stop instruments
+ hardwrite(0xb0 + i,0); // key off
+ hardwrite(0x80 + op_table[i],0xff); // fastest release
+ }
+
+ hardwrite(0xbd,0); // clear misc. register
+ }
+
+ setchip(0);
+}
diff --git a/plugins/adplug/adplug/realopl.h b/plugins/adplug/adplug/realopl.h
new file mode 100644
index 00000000..e675fd92
--- /dev/null
+++ b/plugins/adplug/adplug/realopl.h
@@ -0,0 +1,72 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * realopl.h - Real hardware OPL, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_REALOPL
+#define H_ADPLUG_REALOPL
+
+#include "opl.h"
+
+#define DFL_ADLIBPORT 0x388 // default adlib baseport
+
+class CRealopl: public Copl
+{
+ public:
+ CRealopl(unsigned short initport = DFL_ADLIBPORT); // initport = OPL2 hardware baseport
+
+ bool detect(); // returns true if adlib compatible board is found, else false
+ void setvolume(int volume); // set adlib master volume (0 - 63) 0 = loudest, 63 = softest
+ void setquiet(bool quiet = true); // sets the OPL2 quiet, while still writing to the registers
+ void setport(unsigned short port) // set new OPL2 hardware baseport
+ {
+ adlport = port;
+ }
+ void setnowrite(bool nw = true) // set hardware write status
+ {
+ nowrite = nw;
+ }
+
+ int getvolume() // get adlib master volume
+ {
+ return hardvol;
+ }
+
+ // template methods
+ void write(int reg, int val);
+ void init();
+ void settype(ChipType type)
+ {
+ currType = type;
+ }
+
+ protected:
+ void hardwrite(int reg, int val); // write to OPL2 hardware registers
+ bool harddetect(); // do real hardware detection
+
+ static const unsigned char op_table[9]; // the 9 operators as expected by the OPL2
+
+ unsigned short adlport; // adlib hardware baseport
+ int hardvol, oldvol; // hardware master volume
+ bool bequiet; // quiet status cache
+ char hardvols[2][22][2]; // volume cache
+ bool nowrite; // don't write to hardware, if true
+};
+
+#endif
diff --git a/plugins/adplug/adplug/rix.cpp b/plugins/adplug/adplug/rix.cpp
new file mode 100644
index 00000000..02ebc8d5
--- /dev/null
+++ b/plugins/adplug/adplug/rix.cpp
@@ -0,0 +1,495 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * rix.cpp - Softstar RIX OPL Format Player by palxex <palxex.ys168.com>
+ * BSPAL <BSPAL.ys168.com>
+ */
+
+#include <string.h>
+#include "rix.h"
+#include "debug.h"
+
+const unsigned char CrixPlayer::adflag[] = {0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1};
+const unsigned char CrixPlayer::reg_data[] = {0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21};
+const unsigned char CrixPlayer::ad_C0_offs[] = {0,1,2,0,1,2,3,4,5,3,4,5,6,7,8,6,7,8};
+const unsigned char CrixPlayer::modify[] = {0,3,1,4,2,5,6,9,7,10,8,11,12,15,13,16,14,17,12,\
+ 15,16,0,14,0,17,0,13,0};
+const unsigned char CrixPlayer::bd_reg_data[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0x04,0x02,0x01,
+ 0x00,0x01,0x01,0x03,0x0F,0x05,0x00,0x01,0x03,0x0F,0x00,
+ 0x00,0x00,0x01,0x00,0x00,0x01,0x01,0x0F,0x07,0x00,0x02,
+ 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0A,
+ 0x04,0x00,0x08,0x0C,0x0B,0x00,0x00,0x00,0x01,0x00,0x00,
+ 0x00,0x00,0x0D,0x04,0x00,0x06,0x0F,0x00,0x00,0x00,0x00,
+ 0x01,0x00,0x00,0x0C,0x00,0x0F,0x0B,0x00,0x08,0x05,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x0F,0x0B,0x00,
+ 0x07,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
+ 0x0F,0x0B,0x00,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x01,0x00,0x0F,0x0B,0x00,0x07,0x05,0x00,0x00,0x00,
+ 0x00,0x00,0x00};
+unsigned char CrixPlayer::for40reg[] = {0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
+ 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F};
+unsigned short CrixPlayer::mus_time = 0x4268;
+
+/*** public methods *************************************/
+
+CPlayer *CrixPlayer::factory(Copl *newopl)
+{
+ return new CrixPlayer(newopl);
+}
+
+CrixPlayer::CrixPlayer(Copl *newopl)
+ : CPlayer(newopl), flag_mkf(0), file_buffer(0), buf_addr(0)
+{
+}
+
+CrixPlayer::~CrixPlayer()
+{
+ if(file_buffer)
+ delete [] file_buffer;
+}
+
+bool CrixPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ unsigned long i=0;
+
+ if(stricmp(filename.substr(filename.length()-4,4).c_str(),".mkf")==0)
+ {
+ flag_mkf=1;
+ f->seek(0);
+ int offset=f->readInt(4);
+ f->seek(offset);
+ }
+ if(f->readInt(2)!=0x55aa){ fp.close(f);return false; }
+ file_buffer = new unsigned char [fp.filesize(f) + 1];
+ f->seek(0);
+ while(!f->eof())
+ file_buffer[i++]=f->readInt(1);
+ length=i;
+ fp.close(f);
+ if(!flag_mkf)
+ buf_addr=file_buffer;
+ rewind(0);
+ return true;
+}
+
+bool CrixPlayer::update()
+{
+ int_08h_entry();
+ return !play_end;
+}
+
+void CrixPlayer::rewind(int subsong)
+{
+ I = 0; T = 0;
+ mus_block = 0;
+ ins_block = 0;
+ rhythm = 0;
+ music_on = 0;
+ pause_flag = 0;
+ band = 0;
+ band_low = 0;
+ e0_reg_flag = 0;
+ bd_modify = 0;
+ sustain = 0;
+ play_end = 0;
+ pos = index = 0;
+
+ memset(f_buffer, 0, sizeof(unsigned short) * 300);
+ memset(a0b0_data2, 0, sizeof(unsigned short) * 11);
+ memset(a0b0_data3, 0, 18);
+ memset(a0b0_data4, 0, 18);
+ memset(a0b0_data5, 0, 96);
+ memset(addrs_head, 0, 96);
+ memset(insbuf, 0, 28 * sizeof(unsigned short));
+ memset(displace, 0, 11 * sizeof(unsigned short));
+ memset(reg_bufs, 0, 18 * sizeof(ADDT));
+
+ if(flag_mkf)
+ {
+ unsigned int *buf_index=(unsigned int *)file_buffer;
+ int offset1=buf_index[subsong],offset2;
+ while((offset2=buf_index[++subsong])==offset1);
+ length=offset2-offset1+1;
+ buf_addr=file_buffer+offset1;
+ }
+ opl->init();
+ opl->write(1,32); // go to OPL2 mode
+ set_new_int();
+ data_initial();
+}
+
+unsigned int CrixPlayer::getsubsongs()
+{
+ if(flag_mkf)
+ {
+ unsigned int *buf_index=(unsigned int *)file_buffer;
+ int songs=buf_index[0]/4,i=0;
+ for(i=0;i<songs;i++)
+ if(buf_index[i+1]==buf_index[i])
+ songs--;
+ return songs;
+ }
+ else
+ return 1;
+}
+
+float CrixPlayer::getrefresh()
+{
+ return 70.0f;
+}
+
+/*------------------Implemention----------------------------*/
+inline void CrixPlayer::set_new_int()
+{
+// if(!ad_initial()) exit(1);
+ ad_initial();
+}
+/*----------------------------------------------------------*/
+inline void CrixPlayer::Pause()
+{
+ register unsigned short i;
+ pause_flag = 1;
+ for(i=0;i<11;i++)
+ switch_ad_bd(i);
+}
+/*----------------------------------------------------------*/
+inline void CrixPlayer::ad_a0b0l_reg_(unsigned short index,unsigned short p2,unsigned short p3)
+{
+// unsigned short i = p2+a0b0_data2[index];
+ a0b0_data4[index] = p3;
+ a0b0_data3[index] = p2;
+}
+inline void CrixPlayer::data_initial()
+{
+ rhythm = buf_addr[2];
+ mus_block = (buf_addr[0x0D]<<8)+buf_addr[0x0C];
+ ins_block = (buf_addr[0x09]<<8)+buf_addr[0x08];
+ I = mus_block+1;
+ if(rhythm != 0)
+ {
+ // ad_a0b0_reg(6);
+ // ad_a0b0_reg(7);
+ // ad_a0b0_reg(8);
+ ad_a0b0l_reg_(8,0x18,0);
+ ad_a0b0l_reg_(7,0x1F,0);
+ }
+ bd_modify = 0;
+ // ad_bd_reg();
+ band = 0; music_on = 1;
+}
+/*----------------------------------------------------------*/
+inline unsigned short CrixPlayer::ad_initial()
+{
+ register unsigned short i,j,k = 0;
+ for(i=0;i<25;i++)
+ {
+ f_buffer[i*12]=(unsigned int)((i*24+10000)*0.27461678223+4)>>3;
+ for(int t=1;t<12;t++)
+ f_buffer[i*12+t]=f_buffer[i*12+t-1]*1.06;
+ }
+ for(i=0;i<8;i++)
+ for(j=0;j<12;j++)
+ {
+ a0b0_data5[k] = i;
+ addrs_head[k] = j;
+ k++;
+ }
+ //ad_bd_reg();
+ //ad_08_reg();
+ //for(i=0;i<9;i++) ad_a0b0_reg(i);
+ e0_reg_flag = 0x20;
+ //for(i=0;i<18;i++) ad_bop(0xE0+reg_data[i],0);
+ //ad_bop(1,e0_reg_flag);
+ return 1;//ad_test();
+}
+/*----------------------------------------------------------*/
+inline void CrixPlayer::ad_bop(unsigned short reg,unsigned short value)
+{
+ if(reg == 2 || reg == 3)
+ AdPlug_LogWrite("switch OPL2/3 mode!\n");
+ opl->write(reg & 0xff, value & 0xff);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::int_08h_entry()
+ {
+ unsigned short band_sus = 1;
+ while(band_sus)
+ {
+ if(sustain <= 0)
+ {
+ band_sus = rix_proc();
+ if(band_sus) sustain += band_sus;
+ else
+ {
+ play_end=1;
+ break;
+ }
+ }
+ else
+ {
+ if(band_sus) sustain -= 14; /* aging */
+ break;
+ }
+ }
+ }
+/*--------------------------------------------------------------*/
+inline unsigned short CrixPlayer::rix_proc()
+{
+ unsigned char ctrl = 0;
+ if(music_on == 0||pause_flag == 1) return 0;
+ band = 0;
+ while(buf_addr[I] != 0x80 && I<length-1)
+ {
+ band_low = buf_addr[I-1];
+ ctrl = buf_addr[I]; I+=2;
+ switch(ctrl&0xF0)
+ {
+ case 0x90: rix_get_ins(); rix_90_pro(ctrl&0x0F); break;
+ case 0xA0: rix_A0_pro(ctrl&0x0F,((unsigned short)band_low)<<6); break;
+ case 0xB0: rix_B0_pro(ctrl&0x0F,band_low); break;
+ case 0xC0: switch_ad_bd(ctrl&0x0F);
+ if(band_low != 0) rix_C0_pro(ctrl&0x0F,band_low);
+ break;
+ default: band = (ctrl<<8)+band_low; break;
+ }
+ if(band != 0) return band;
+ }
+ music_ctrl();
+ I = mus_block+1;
+ band = 0; music_on = 1;
+ return 0;
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_get_ins()
+{
+ int i;
+ unsigned char *baddr = (&buf_addr[ins_block])+(band_low<<6);
+
+ for(i = 0; i < 28; i++)
+ insbuf[i] = (baddr[i * 2 + 1] << 8) + baddr[i * 2];
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_90_pro(unsigned short ctrl_l)
+{
+ if(rhythm == 0 || ctrl_l < 6)
+ {
+ ins_to_reg(modify[ctrl_l*2],insbuf,insbuf[26]);
+ ins_to_reg(modify[ctrl_l*2+1],insbuf+13,insbuf[27]);
+ return;
+ }
+ else if(ctrl_l > 6)
+ {
+ ins_to_reg(modify[ctrl_l*2+6],insbuf,insbuf[26]);
+ return;
+ }
+ else
+ {
+ ins_to_reg(12,insbuf,insbuf[26]);
+ ins_to_reg(15,insbuf+13,insbuf[27]);
+ return;
+ }
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_A0_pro(unsigned short ctrl_l,unsigned short index)
+{
+ if(rhythm == 0 || ctrl_l <= 6)
+ {
+ prepare_a0b0(ctrl_l,index>0x3FFF?0x3FFF:index);
+ ad_a0b0l_reg(ctrl_l,a0b0_data3[ctrl_l],a0b0_data4[ctrl_l]);
+ }
+ else return;
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::prepare_a0b0(unsigned short index,unsigned short v) /* important !*/
+{
+ short high = 0,low = 0; unsigned int res;
+ int res1 = (v-0x2000)*0x19;
+ if(res1 == (int)0xff) return;
+ low = res1/0x2000;
+ if(low < 0)
+ {
+ low = 0x18-low; high = (signed short)low<0?0xFFFF:0;
+ res = high; res<<=16; res+=low;
+ low = ((signed short)res)/(signed short)0xFFE7;
+ a0b0_data2[index] = low;
+ low = res;
+ res = low - 0x18;
+ high = (signed short)res%0x19;
+ low = (signed short)res/0x19;
+ if(high != 0) {low = 0x19; low = low-high;}
+ }
+ else
+ {
+ res = high = low;
+ low = (signed short)res/(signed short)0x19;
+ a0b0_data2[index] = low;
+ res = high;
+ low = (signed short)res%(signed short)0x19;
+ }
+ low = (signed short)low*(signed short)0x18;
+ displace[index] = low;
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_a0b0l_reg(unsigned short index,unsigned short p2,unsigned short p3)
+{
+ unsigned short data; unsigned short i = p2+a0b0_data2[index];
+ a0b0_data4[index] = p3;
+ a0b0_data3[index] = p2;
+ i = ((signed short)i<=0x5F?i:0x5F);
+ i = ((signed short)i>=0?i:0);
+ data = f_buffer[addrs_head[i]+displace[index]/2];
+ ad_bop(0xA0+index,data);
+ data = a0b0_data5[i]*4+(p3<1?0:0x20)+((data>>8)&3);
+ ad_bop(0xB0+index,data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_B0_pro(unsigned short ctrl_l,unsigned short index)
+{
+ register int temp = 0;
+ if(rhythm == 0 || ctrl_l < 6) temp = modify[ctrl_l*2+1];
+ else
+ {
+ temp = ctrl_l > 6?ctrl_l*2:ctrl_l*2+1;
+ temp = modify[temp+6];
+ }
+ for40reg[temp] = index>0x7F?0x7F:index;
+ ad_40_reg(temp);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::rix_C0_pro(unsigned short ctrl_l,unsigned short index)
+{
+ register unsigned short i = index>=12?index-12:0;
+ if(ctrl_l < 6 || rhythm == 0)
+ {
+ ad_a0b0l_reg(ctrl_l,i,1);
+ return;
+ }
+ else
+ {
+ if(ctrl_l != 6)
+ {
+ if(ctrl_l == 8)
+ {
+ ad_a0b0l_reg(ctrl_l,i,0);
+ ad_a0b0l_reg(7,i+7,0);
+ }
+ }
+ else ad_a0b0l_reg(ctrl_l,i,0);
+ bd_modify |= bd_reg_data[ctrl_l];
+ ad_bd_reg();
+ return;
+ }
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::switch_ad_bd(unsigned short index)
+{
+
+ if(rhythm == 0 || index < 6) ad_a0b0l_reg(index,a0b0_data3[index],0);
+ else
+ {
+ bd_modify &= (~bd_reg_data[index]),
+ ad_bd_reg();
+ }
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ins_to_reg(unsigned short index,unsigned short* insb,unsigned short value)
+{
+ register unsigned short i;
+ for(i=0;i<13;i++) reg_bufs[index].v[i] = insb[i];
+ reg_bufs[index].v[13] = value&3;
+ ad_bd_reg(),ad_08_reg(),
+ ad_40_reg(index),ad_C0_reg(index),ad_60_reg(index),
+ ad_80_reg(index),ad_20_reg(index),ad_E0_reg(index);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_E0_reg(unsigned short index)
+{
+ unsigned short data = e0_reg_flag == 0?0:(reg_bufs[index].v[13]&3);
+ ad_bop(0xE0+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_20_reg(unsigned short index)
+{
+ unsigned short data = (reg_bufs[index].v[9] < 1?0:0x80);
+ data += (reg_bufs[index].v[10] < 1?0:0x40);
+ data += (reg_bufs[index].v[5] < 1?0:0x20);
+ data += (reg_bufs[index].v[11] < 1?0:0x10);
+ data += (reg_bufs[index].v[1]&0x0F);
+ ad_bop(0x20+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_80_reg(unsigned short index)
+{
+ unsigned short data = (reg_bufs[index].v[7]&0x0F),temp = reg_bufs[index].v[4];
+ data |= (temp << 4);
+ ad_bop(0x80+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_60_reg(unsigned short index)
+{
+ unsigned short data = reg_bufs[index].v[6]&0x0F,temp = reg_bufs[index].v[3];
+ data |= (temp << 4);
+ ad_bop(0x60+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_C0_reg(unsigned short index)
+{
+ unsigned short data = reg_bufs[index].v[2];
+ if(adflag[index] == 1) return;
+ data *= 2,
+ data |= (reg_bufs[index].v[12] < 1?1:0);
+ ad_bop(0xC0+ad_C0_offs[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_40_reg(unsigned short index)
+{
+ unsigned int res = 0;
+ unsigned short data = 0,temp = reg_bufs[index].v[0];
+ data = 0x3F - (0x3F & reg_bufs[index].v[8]),
+ data *= for40reg[index],
+ data *= 2,
+ data += 0x7F,
+ res = data;
+ data = res/0xFE,
+ data -= 0x3F,
+ data = -data,
+ data |= temp<<6;
+ ad_bop(0x40+reg_data[index],data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_bd_reg()
+{
+ unsigned short data = rhythm < 1? 0:0x20;
+ data |= bd_modify;
+ ad_bop(0xBD,data);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::ad_a0b0_reg(unsigned short index)
+{
+ ad_bop(0xA0+index,0);
+ ad_bop(0xB0+index,0);
+}
+/*--------------------------------------------------------------*/
+inline void CrixPlayer::music_ctrl()
+{
+ register int i;
+ for(i=0;i<11;i++)
+ switch_ad_bd(i);
+}
diff --git a/plugins/adplug/adplug/rix.h b/plugins/adplug/adplug/rix.h
new file mode 100644
index 00000000..68e08c8b
--- /dev/null
+++ b/plugins/adplug/adplug/rix.h
@@ -0,0 +1,112 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rix.h - Softstar RIX OPL Format Player by palxex <palxex.ys168.com>
+ * BSPAL <BSPAL.ys168.com>
+ */
+
+#include "player.h"
+
+class CrixPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CrixPlayer(Copl *newopl);
+ ~CrixPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+ unsigned int getsubsongs();
+
+ std::string gettype()
+ { return std::string("Softstar RIX OPL Music Format"); };
+
+ protected:
+ typedef struct {
+ unsigned char v[14];
+ } ADDT;
+
+ int flag_mkf;
+ unsigned char *file_buffer;
+ unsigned char *buf_addr; /* rix files' f_buffer */
+ unsigned short f_buffer[300];//9C0h-C18h
+ unsigned short a0b0_data2[11];
+ unsigned char a0b0_data3[18];
+ unsigned char a0b0_data4[18];
+ unsigned char a0b0_data5[96];
+ unsigned char addrs_head[96];
+ unsigned short insbuf[28];
+ unsigned short displace[11];
+ ADDT reg_bufs[18];
+ unsigned long pos,length;
+ unsigned char index;
+
+ static const unsigned char adflag[18];
+ static const unsigned char reg_data[18];
+ static const unsigned char ad_C0_offs[18];
+ static const unsigned char modify[28];
+ static const unsigned char bd_reg_data[124];
+ static unsigned char for40reg[18];
+ static unsigned short mus_time;
+ unsigned int I,T;
+ unsigned short mus_block;
+ unsigned short ins_block;
+ unsigned char rhythm;
+ unsigned char music_on;
+ unsigned char pause_flag;
+ unsigned short band;
+ unsigned char band_low;
+ unsigned short e0_reg_flag;
+ unsigned char bd_modify;
+ int sustain;
+ int play_end;
+
+#define ad_08_reg() ad_bop(8,0) /**/
+ inline void ad_20_reg(unsigned short); /**/
+ inline void ad_40_reg(unsigned short); /**/
+ inline void ad_60_reg(unsigned short); /**/
+ inline void ad_80_reg(unsigned short); /**/
+ inline void ad_a0b0_reg(unsigned short); /**/
+ inline void ad_a0b0l_reg(unsigned short,unsigned short,unsigned short); /**/
+ inline void ad_a0b0l_reg_(unsigned short,unsigned short,unsigned short); /**/
+ inline void ad_bd_reg(); /**/
+ inline void ad_bop(unsigned short,unsigned short); /**/
+ inline void ad_C0_reg(unsigned short); /**/
+ inline void ad_E0_reg(unsigned short); /**/
+ inline unsigned short ad_initial(); /**/
+ inline unsigned short ad_test(); /**/
+ inline void crc_trans(unsigned short,unsigned short); /**/
+ inline void data_initial(); /* done */
+ inline void init(); /**/
+ inline void ins_to_reg(unsigned short,unsigned short*,unsigned short); /**/
+ inline void int_08h_entry(); /**/
+ inline void music_ctrl(); /**/
+ inline void Pause(); /**/
+ inline void prepare_a0b0(unsigned short,unsigned short); /**/
+ inline void rix_90_pro(unsigned short); /**/
+ inline void rix_A0_pro(unsigned short,unsigned short); /**/
+ inline void rix_B0_pro(unsigned short,unsigned short); /**/
+ inline void rix_C0_pro(unsigned short,unsigned short); /**/
+ inline void rix_get_ins(); /**/
+ inline unsigned short rix_proc(); /**/
+ inline void set_new_int();
+ inline void switch_ad_bd(unsigned short); /**/
+};
diff --git a/plugins/adplug/adplug/rol.cpp b/plugins/adplug/adplug/rol.cpp
new file mode 100644
index 00000000..7507d79b
--- /dev/null
+++ b/plugins/adplug/adplug/rol.cpp
@@ -0,0 +1,723 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rol.h - ROL Player by OPLx <oplx@yahoo.com>
+ *
+ * Visit: http://tenacity.hispeed.com/aomit/oplx/
+ */
+#include <algorithm>
+
+#include "rol.h"
+#include "debug.h"
+
+int const CrolPlayer::kSizeofDataRecord = 30;
+int const CrolPlayer::kMaxTickBeat = 60;
+int const CrolPlayer::kSilenceNote = -12;
+int const CrolPlayer::kNumMelodicVoices = 9;
+int const CrolPlayer::kNumPercussiveVoices = 11;
+int const CrolPlayer::kBassDrumChannel = 6;
+int const CrolPlayer::kSnareDrumChannel = 7;
+int const CrolPlayer::kTomtomChannel = 8;
+int const CrolPlayer::kTomtomFreq = 2;//4;
+int const CrolPlayer::kSnareDrumFreq = 2;//kTomtomFreq + 7;
+float const CrolPlayer::kDefaultUpdateTme = 18.2f;
+float const CrolPlayer::kPitchFactor = 400.0f;
+
+static const unsigned char drum_table[4] = {0x14, 0x12, 0x15, 0x11};
+
+CrolPlayer::uint16 const CrolPlayer::kNoteTable[12] =
+{
+ 340, // C
+ 363, // C#
+ 385, // D
+ 408, // D#
+ 432, // E
+ 458, // F
+ 485, // F#
+ 514, // G
+ 544, // G#
+ 577, // A
+ 611, // A#
+ 647 // B
+};
+
+/*** public methods **************************************/
+
+CPlayer *CrolPlayer::factory(Copl *newopl)
+{
+ return new CrolPlayer(newopl);
+}
+//---------------------------------------------------------
+CrolPlayer::CrolPlayer(Copl *newopl)
+: CPlayer ( newopl )
+ ,rol_header ( NULL )
+ ,mNextTempoEvent ( 0 )
+ ,mCurrTick ( 0 )
+ ,mTimeOfLastNote ( 0 )
+ ,mRefresh ( kDefaultUpdateTme )
+ ,bdRegister ( 0 )
+{
+ int n;
+
+ memset(bxRegister, 0, sizeof(bxRegister) );
+ memset(volumeCache, 0, sizeof(volumeCache) );
+ memset(freqCache, 0, sizeof(freqCache) );
+
+ for(n=0; n<11; n++)
+ pitchCache[n]=1.0f;
+}
+//---------------------------------------------------------
+CrolPlayer::~CrolPlayer()
+{
+ if( rol_header != NULL )
+ {
+ delete rol_header;
+ rol_header=NULL;
+ }
+}
+//---------------------------------------------------------
+bool CrolPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+
+ char *fn = new char[filename.length()+12];
+ int i;
+ std::string bnk_filename;
+
+ AdPlug_LogWrite("*** CrolPlayer::load(f, \"%s\") ***\n", filename.c_str());
+ strcpy(fn,filename.data());
+ for (i=strlen(fn)-1; i>=0; i--)
+ if (fn[i] == '/' || fn[i] == '\\')
+ break;
+ strcpy(fn+i+1,"standard.bnk");
+ bnk_filename = fn;
+ delete [] fn;
+ AdPlug_LogWrite("bnk_filename = \"%s\"\n",bnk_filename.c_str());
+
+ rol_header = new SRolHeader;
+ memset( rol_header, 0, sizeof(SRolHeader) );
+
+ rol_header->version_major = f->readInt( 2 );
+ rol_header->version_minor = f->readInt( 2 );
+
+ // Version check
+ if(rol_header->version_major != 0 || rol_header->version_minor != 4) {
+ AdPlug_LogWrite("Unsupported file version %d.%d or not a ROL file!\n",
+ rol_header->version_major, rol_header->version_minor);
+ AdPlug_LogWrite("--- CrolPlayer::load ---\n");
+ fp.close(f);
+ return false;
+ }
+
+ f->seek( 40, binio::Add );
+
+ rol_header->ticks_per_beat = f->readInt( 2 );
+ rol_header->beats_per_measure = f->readInt( 2 );
+ rol_header->edit_scale_y = f->readInt( 2 );
+ rol_header->edit_scale_x = f->readInt( 2 );
+
+ f->seek( 1, binio::Add );
+
+ rol_header->mode = f->readInt(1);
+
+ f->seek( 90+38+15, binio::Add );
+
+ rol_header->basic_tempo = f->readFloat( binio::Single );
+
+ load_tempo_events( f );
+
+ mTimeOfLastNote = 0;
+
+ if( load_voice_data( f, bnk_filename, fp ) != true )
+ {
+ AdPlug_LogWrite("CrolPlayer::load_voice_data(f) failed!\n");
+ AdPlug_LogWrite("--- CrolPlayer::load ---\n");
+
+ fp.close( f );
+ return false;
+ }
+
+ fp.close( f );
+
+ rewind( 0 );
+ AdPlug_LogWrite("--- CrolPlayer::load ---\n");
+ return true;
+}
+//---------------------------------------------------------
+bool CrolPlayer::update()
+{
+ if( mNextTempoEvent < mTempoEvents.size() &&
+ mTempoEvents[mNextTempoEvent].time == mCurrTick )
+ {
+ SetRefresh( mTempoEvents[mNextTempoEvent].multiplier );
+ ++mNextTempoEvent;
+ }
+
+ TVoiceData::iterator curr = voice_data.begin();
+ TVoiceData::iterator end = voice_data.end();
+ int voice = 0;
+
+ while( curr != end )
+ {
+ UpdateVoice( voice, *curr );
+ ++curr;
+ ++voice;
+ }
+
+ ++mCurrTick;
+
+ if( mCurrTick > mTimeOfLastNote )
+ {
+ return false;
+ }
+
+ return true;
+ //return ( mCurrTick > mTimeOfLastNote ) ? false : true;
+}
+//---------------------------------------------------------
+void CrolPlayer::rewind( int subsong )
+{
+ TVoiceData::iterator curr = voice_data.begin();
+ TVoiceData::iterator end = voice_data.end();
+
+ while( curr != end )
+ {
+ CVoiceData &voice = *curr;
+
+ voice.Reset();
+ ++curr;
+ }
+
+ memset(bxRegister, 0, sizeof(bxRegister) );
+ memset(volumeCache, 0, sizeof(volumeCache) );
+
+ bdRegister = 0;
+
+ opl->init(); // initialize to melodic by default
+ opl->write(1,0x20); // Enable waveform select (bit 5)
+
+ if( rol_header->mode == 0 )
+ {
+ opl->write( 0xbd, 0x20 ); // select rhythm mode (bit 5)
+ bdRegister = 0x20;
+
+ SetFreq( kTomtomChannel, 24 );
+ SetFreq( kSnareDrumChannel, 31 );
+ }
+
+ mNextTempoEvent = 0;
+ mCurrTick = 0;
+
+ SetRefresh(1.0f);
+}
+//---------------------------------------------------------
+inline float fmin( int const a, int const b )
+{
+ return static_cast<float>( a < b ? a : b );
+}
+//---------------------------------------------------------
+void CrolPlayer::SetRefresh( float const multiplier )
+{
+ float const tickBeat = fmin(kMaxTickBeat, rol_header->ticks_per_beat);
+
+ mRefresh = (tickBeat*rol_header->basic_tempo*multiplier) / 60.0f;
+}
+//---------------------------------------------------------
+float CrolPlayer::getrefresh()
+{
+ return mRefresh;
+}
+//---------------------------------------------------------
+void CrolPlayer::UpdateVoice( int const voice, CVoiceData &voiceData )
+{
+ TNoteEvents const &nEvents = voiceData.note_events;
+
+ if( nEvents.empty() || voiceData.mEventStatus & CVoiceData::kES_NoteEnd )
+ {
+ return; // no note data to process, don't bother doing anything.
+ }
+
+ TInstrumentEvents &iEvents = voiceData.instrument_events;
+ TVolumeEvents &vEvents = voiceData.volume_events;
+ TPitchEvents &pEvents = voiceData.pitch_events;
+
+ if( !(voiceData.mEventStatus & CVoiceData::kES_InstrEnd ) &&
+ iEvents[voiceData.next_instrument_event].time == mCurrTick )
+ {
+ if( voiceData.next_instrument_event < iEvents.size() )
+ {
+ send_ins_data_to_chip( voice, iEvents[voiceData.next_instrument_event].ins_index );
+ ++voiceData.next_instrument_event;
+ }
+ else
+ {
+ voiceData.mEventStatus |= CVoiceData::kES_InstrEnd;
+ }
+ }
+
+ if( !(voiceData.mEventStatus & CVoiceData::kES_VolumeEnd ) &&
+ vEvents[voiceData.next_volume_event].time == mCurrTick )
+ {
+ SVolumeEvent const &volumeEvent = vEvents[voiceData.next_volume_event];
+
+ if( voiceData.next_volume_event < vEvents.size() )
+ {
+ int const volume = (int)(63.0f*(1.0f - volumeEvent.multiplier));
+
+ SetVolume( voice, volume );
+
+ ++voiceData.next_volume_event; // move to next volume event
+ }
+ else
+ {
+ voiceData.mEventStatus |= CVoiceData::kES_VolumeEnd;
+ }
+ }
+
+ if( voiceData.mForceNote || voiceData.current_note_duration > voiceData.mNoteDuration-1 )
+ {
+ if( mCurrTick != 0 )
+ {
+ ++voiceData.current_note;
+ }
+
+ if( voiceData.current_note < nEvents.size() )
+ {
+ SNoteEvent const &noteEvent = nEvents[voiceData.current_note];
+
+ SetNote( voice, noteEvent.number );
+ voiceData.current_note_duration = 0;
+ voiceData.mNoteDuration = noteEvent.duration;
+ voiceData.mForceNote = false;
+ }
+ else
+ {
+ SetNote( voice, kSilenceNote );
+ voiceData.mEventStatus |= CVoiceData::kES_NoteEnd;
+ return;
+ }
+ }
+
+ if( !(voiceData.mEventStatus & CVoiceData::kES_PitchEnd ) &&
+ pEvents[voiceData.next_pitch_event].time == mCurrTick )
+ {
+ if( voiceData.next_pitch_event < pEvents.size() )
+ {
+ SetPitch(voice,pEvents[voiceData.next_pitch_event].variation);
+ ++voiceData.next_pitch_event;
+ }
+ else
+ {
+ voiceData.mEventStatus |= CVoiceData::kES_PitchEnd;
+ }
+ }
+
+ ++voiceData.current_note_duration;
+}
+//---------------------------------------------------------
+void CrolPlayer::SetNote( int const voice, int const note )
+{
+ if( voice < kBassDrumChannel || rol_header->mode )
+ {
+ SetNoteMelodic( voice, note );
+ }
+ else
+ {
+ SetNotePercussive( voice, note );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::SetNotePercussive( int const voice, int const note )
+{
+ int const bit_pos = 4-voice+kBassDrumChannel;
+
+ bdRegister &= ~( 1<<bit_pos );
+ opl->write( 0xbd, bdRegister );
+
+ if( note != kSilenceNote )
+ {
+ switch( voice )
+ {
+ case kTomtomChannel:
+ SetFreq( kSnareDrumChannel, note+7 );
+ case kBassDrumChannel:
+ SetFreq( voice, note );
+ break;
+ }
+
+ bdRegister |= 1<<bit_pos;
+ opl->write( 0xbd, bdRegister );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::SetNoteMelodic( int const voice, int const note )
+{
+ opl->write( 0xb0+voice, bxRegister[voice] & ~0x20 );
+
+ if( note != kSilenceNote )
+ {
+ SetFreq( voice, note, true );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::SetPitch(int const voice, real32 const variation)
+{
+ pitchCache[voice] = variation;
+ freqCache[voice] += (uint16)((((float)freqCache[voice])*(variation-1.0f)) / kPitchFactor);
+
+ opl->write(0xa0+voice,freqCache[voice] & 0xff);
+}
+//---------------------------------------------------------
+void CrolPlayer::SetFreq( int const voice, int const note, bool const keyOn )
+{
+ uint16 freq = kNoteTable[note%12] + ((note/12) << 10);
+ freq += (uint16)((((float)freq)*(pitchCache[voice]-1.0f))/kPitchFactor);
+
+ freqCache[voice] = freq;
+ bxRegister[voice] = ((freq >> 8) & 0x1f);
+
+ opl->write( 0xa0+voice, freq & 0xff );
+ opl->write( 0xb0+voice, bxRegister[voice] | (keyOn ? 0x20 : 0x0) );
+}
+//---------------------------------------------------------
+void CrolPlayer::SetVolume( int const voice, int const volume )
+{
+ volumeCache[voice] = (volumeCache[voice] &0xc0) | volume;
+
+ int const op_offset = ( voice < kSnareDrumChannel || rol_header->mode ) ?
+ op_table[voice]+3 : drum_table[voice-kSnareDrumChannel];
+
+ opl->write( 0x40+op_offset, volumeCache[voice] );
+}
+//---------------------------------------------------------
+void CrolPlayer::send_ins_data_to_chip( int const voice, int const ins_index )
+{
+ SRolInstrument &instrument = ins_list[ins_index].instrument;
+
+ send_operator( voice, instrument.modulator, instrument.carrier );
+}
+//---------------------------------------------------------
+void CrolPlayer::send_operator( int const voice, SOPL2Op const &modulator, SOPL2Op const &carrier )
+{
+ if( voice < kSnareDrumChannel || rol_header->mode )
+ {
+ int const op_offset = op_table[voice];
+
+ opl->write( 0x20+op_offset, modulator.ammulti );
+ opl->write( 0x40+op_offset, modulator.ksltl );
+ opl->write( 0x60+op_offset, modulator.ardr );
+ opl->write( 0x80+op_offset, modulator.slrr );
+ opl->write( 0xc0+voice , modulator.fbc );
+ opl->write( 0xe0+op_offset, modulator.waveform );
+
+ volumeCache[voice] = (carrier.ksltl & 0xc0) | volumeCache[voice] & 0x3f;
+
+ opl->write( 0x23+op_offset, carrier.ammulti );
+ opl->write( 0x43+op_offset, volumeCache[voice] );
+ opl->write( 0x63+op_offset, carrier.ardr );
+ opl->write( 0x83+op_offset, carrier.slrr );
+// opl->write( 0xc3+voice , carrier.fbc ); <- don't bother writing this.
+ opl->write( 0xe3+op_offset, carrier.waveform );
+ }
+ else
+ {
+ int const op_offset = drum_table[voice-kSnareDrumChannel];
+
+ volumeCache[voice] = (modulator.ksltl & 0xc0) | volumeCache[voice] & 0x3f;
+
+ opl->write( 0x20+op_offset, modulator.ammulti );
+ opl->write( 0x40+op_offset, volumeCache[voice] );
+ opl->write( 0x60+op_offset, modulator.ardr );
+ opl->write( 0x80+op_offset, modulator.slrr );
+ opl->write( 0xc0+voice , modulator.fbc );
+ opl->write( 0xe0+op_offset, modulator.waveform );
+ }
+}
+//---------------------------------------------------------
+void CrolPlayer::load_tempo_events( binistream *f )
+{
+ int16 const num_tempo_events = f->readInt( 2 );
+
+ mTempoEvents.reserve( num_tempo_events );
+
+ for(int i=0; i<num_tempo_events; ++i)
+ {
+ STempoEvent event;
+
+ event.time = f->readInt( 2 );
+ event.multiplier = f->readFloat( binio::Single );
+ mTempoEvents.push_back( event );
+ }
+}
+//---------------------------------------------------------
+bool CrolPlayer::load_voice_data( binistream *f, std::string const &bnk_filename, const CFileProvider &fp )
+{
+ SBnkHeader bnk_header;
+ binistream *bnk_file = fp.open( bnk_filename.c_str() );
+
+ if( bnk_file )
+ {
+ load_bnk_info( bnk_file, bnk_header );
+
+ int const numVoices = rol_header->mode ? kNumMelodicVoices : kNumPercussiveVoices;
+
+ voice_data.reserve( numVoices );
+ for(int i=0; i<numVoices; ++i)
+ {
+ CVoiceData voice;
+
+ load_note_events( f, voice );
+ load_instrument_events( f, voice, bnk_file, bnk_header );
+ load_volume_events( f, voice );
+ load_pitch_events( f, voice );
+
+ voice_data.push_back( voice );
+ }
+
+ fp.close(bnk_file);
+
+ return true;
+ }
+
+ return false;
+}
+//---------------------------------------------------------
+void CrolPlayer::load_note_events( binistream *f, CVoiceData &voice )
+{
+ f->seek( 15, binio::Add );
+
+ int16 const time_of_last_note = f->readInt( 2 );
+
+ if( time_of_last_note != 0 )
+ {
+ TNoteEvents &note_events = voice.note_events;
+ int16 total_duration = 0;
+
+ do
+ {
+ SNoteEvent event;
+
+ event.number = f->readInt( 2 );
+ event.duration = f->readInt( 2 );
+
+ event.number += kSilenceNote; // adding -12
+
+ note_events.push_back( event );
+
+ total_duration += event.duration;
+ } while( total_duration < time_of_last_note );
+
+ if( time_of_last_note > mTimeOfLastNote )
+ {
+ mTimeOfLastNote = time_of_last_note;
+ }
+ }
+
+ f->seek( 15, binio::Add );
+}
+//---------------------------------------------------------
+void CrolPlayer::load_instrument_events( binistream *f, CVoiceData &voice,
+ binistream *bnk_file, SBnkHeader const &bnk_header )
+{
+ int16 const number_of_instrument_events = f->readInt( 2 );
+
+ TInstrumentEvents &instrument_events = voice.instrument_events;
+
+ instrument_events.reserve( number_of_instrument_events );
+
+ for(int i=0; i<number_of_instrument_events; ++i)
+ {
+ SInstrumentEvent event;
+ event.time = f->readInt( 2 );
+ f->readString( event.name, 9 );
+
+ std::string event_name = event.name;
+ event.ins_index = load_rol_instrument( bnk_file, bnk_header, event_name );
+
+ instrument_events.push_back( event );
+
+ f->seek( 1+2, binio::Add );
+ }
+
+ f->seek( 15, binio::Add );
+}
+//---------------------------------------------------------
+void CrolPlayer::load_volume_events( binistream *f, CVoiceData &voice )
+{
+ int16 const number_of_volume_events = f->readInt( 2 );
+
+ TVolumeEvents &volume_events = voice.volume_events;
+
+ volume_events.reserve( number_of_volume_events );
+
+ for(int i=0; i<number_of_volume_events; ++i)
+ {
+ SVolumeEvent event;
+ event.time = f->readInt( 2 );
+ event.multiplier = f->readFloat( binio::Single );
+
+ volume_events.push_back( event );
+ }
+
+ f->seek( 15, binio::Add );
+}
+//---------------------------------------------------------
+void CrolPlayer::load_pitch_events( binistream *f, CVoiceData &voice )
+{
+ int16 const number_of_pitch_events = f->readInt( 2 );
+
+ TPitchEvents &pitch_events = voice.pitch_events;
+
+ pitch_events.reserve( number_of_pitch_events );
+
+ for(int i=0; i<number_of_pitch_events; ++i)
+ {
+ SPitchEvent event;
+ event.time = f->readInt( 2 );
+ event.variation = f->readFloat( binio::Single );
+
+ pitch_events.push_back( event );
+ }
+}
+//---------------------------------------------------------
+bool CrolPlayer::load_bnk_info( binistream *f, SBnkHeader &header )
+{
+ header.version_major = f->readInt(1);
+ header.version_minor = f->readInt(1);
+ f->readString( header.signature, 6 );
+
+ header.number_of_list_entries_used = f->readInt( 2 );
+ header.total_number_of_list_entries = f->readInt( 2 );
+
+ header.abs_offset_of_name_list = f->readInt( 4 );
+ header.abs_offset_of_data = f->readInt( 4 );
+
+ f->seek( header.abs_offset_of_name_list, binio::Set );
+
+ TInstrumentNames &ins_name_list = header.ins_name_list;
+ ins_name_list.reserve( header.number_of_list_entries_used );
+
+ for(int i=0; i<header.number_of_list_entries_used; ++i)
+ {
+ SInstrumentName instrument;
+
+ instrument.index = f->readInt( 2 );
+ instrument.record_used = f->readInt(1);
+ f->readString( instrument.name, 9 );
+
+ // printf("%s = #%d\n", instrument.name, i );
+
+ ins_name_list.push_back( instrument );
+ }
+
+ //std::sort( ins_name_list.begin(), ins_name_list.end(), StringCompare() );
+
+ return true;
+}
+//---------------------------------------------------------
+int CrolPlayer::load_rol_instrument( binistream *f, SBnkHeader const &header, std::string &name )
+{
+ TInstrumentNames const &ins_name_list = header.ins_name_list;
+
+ int const ins_index = get_ins_index( name );
+
+ if( ins_index != -1 )
+ {
+ return ins_index;
+ }
+
+ typedef TInstrumentNames::const_iterator TInsIter;
+ typedef std::pair<TInsIter, TInsIter> TInsIterPair;
+
+ TInsIterPair range = std::equal_range( ins_name_list.begin(),
+ ins_name_list.end(),
+ name,
+ StringCompare() );
+
+ if( range.first != range.second )
+ {
+ int const seekOffs = header.abs_offset_of_data + (range.first->index*kSizeofDataRecord);
+ f->seek( seekOffs, binio::Set );
+ }
+
+ SUsedList usedIns;
+ usedIns.name = name;
+
+ if( range.first != range.second )
+ {
+ read_rol_instrument( f, usedIns.instrument );
+ }
+ else
+ {
+ // set up default instrument data here
+ memset( &usedIns.instrument, 0, kSizeofDataRecord );
+ }
+ ins_list.push_back( usedIns );
+
+ return ins_list.size()-1;
+}
+//---------------------------------------------------------
+int CrolPlayer::get_ins_index( std::string const &name ) const
+{
+ for(unsigned int i=0; i<ins_list.size(); ++i)
+ {
+ if( stricmp(ins_list[i].name.c_str(), name.c_str()) == 0 )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+//---------------------------------------------------------
+void CrolPlayer::read_rol_instrument( binistream *f, SRolInstrument &ins )
+{
+ ins.mode = f->readInt(1);
+ ins.voice_number = f->readInt(1);
+
+ read_fm_operator( f, ins.modulator );
+ read_fm_operator( f, ins.carrier );
+
+ ins.modulator.waveform = f->readInt(1);
+ ins.carrier.waveform = f->readInt(1);
+}
+//---------------------------------------------------------
+void CrolPlayer::read_fm_operator( binistream *f, SOPL2Op &opl2_op )
+{
+ SFMOperator fm_op;
+
+ fm_op.key_scale_level = f->readInt(1);
+ fm_op.freq_multiplier = f->readInt(1);
+ fm_op.feed_back = f->readInt(1);
+ fm_op.attack_rate = f->readInt(1);
+ fm_op.sustain_level = f->readInt(1);
+ fm_op.sustaining_sound = f->readInt(1);
+ fm_op.decay_rate = f->readInt(1);
+ fm_op.release_rate = f->readInt(1);
+ fm_op.output_level = f->readInt(1);
+ fm_op.amplitude_vibrato = f->readInt(1);
+ fm_op.frequency_vibrato = f->readInt(1);
+ fm_op.envelope_scaling = f->readInt(1);
+ fm_op.fm_type = f->readInt(1);
+
+ opl2_op.ammulti = fm_op.amplitude_vibrato << 7 | fm_op.frequency_vibrato << 6 | fm_op.sustaining_sound << 5 | fm_op.envelope_scaling << 4 | fm_op.freq_multiplier;
+ opl2_op.ksltl = fm_op.key_scale_level << 6 | fm_op.output_level;
+ opl2_op.ardr = fm_op.attack_rate << 4 | fm_op.decay_rate;
+ opl2_op.slrr = fm_op.sustain_level << 4 | fm_op.release_rate;
+ opl2_op.fbc = fm_op.feed_back << 1 | (fm_op.fm_type ^ 1);
+}
diff --git a/plugins/adplug/adplug/rol.h b/plugins/adplug/adplug/rol.h
new file mode 100644
index 00000000..fdb9fabb
--- /dev/null
+++ b/plugins/adplug/adplug/rol.h
@@ -0,0 +1,309 @@
+/*
+ * 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
+ *
+ * rol.h - ROL Player by OPLx <oplx@yahoo.com>
+ *
+ * Visit: http://tenacity.hispeed.com/aomit/oplx/
+ */
+#ifndef H_ROLPLAYER
+#define H_ROLPLAYER
+
+#include <vector>
+#include <string>
+#include <string.h>
+
+#include "player.h"
+
+class CrolPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CrolPlayer(Copl *newopl);
+
+ ~CrolPlayer();
+
+ bool load (const std::string &filename, const CFileProvider &fp);
+ bool update ();
+ void rewind (int subsong); // rewinds to specified subsong
+ float getrefresh(); // returns needed timer refresh rate
+
+ std::string gettype() { return std::string("Adlib Visual Composer"); }
+
+private:
+ typedef unsigned short uint16;
+ typedef signed short int16;
+#ifdef __x86_64__
+ typedef signed int int32;
+#else
+ typedef signed long int int32;
+#endif
+ typedef float real32;
+
+ typedef struct
+ {
+ uint16 version_major;
+ uint16 version_minor;
+ char unused0[40];
+ uint16 ticks_per_beat;
+ uint16 beats_per_measure;
+ uint16 edit_scale_y;
+ uint16 edit_scale_x;
+ char unused1;
+ char mode;
+ char unused2[90];
+ char filler0[38];
+ char filler1[15];
+ real32 basic_tempo;
+ } SRolHeader;
+
+ typedef struct
+ {
+ int16 time;
+ real32 multiplier;
+ } STempoEvent;
+
+ typedef struct
+ {
+ int16 number;
+ int16 duration;
+ } SNoteEvent;
+
+ typedef struct
+ {
+ int16 time;
+ char name[9];
+ int16 ins_index;
+ } SInstrumentEvent;
+
+ typedef struct
+ {
+ int16 time;
+ real32 multiplier;
+ } SVolumeEvent;
+
+ typedef struct
+ {
+ int16 time;
+ real32 variation;
+ } SPitchEvent;
+
+ typedef std::vector<SNoteEvent> TNoteEvents;
+ typedef std::vector<SInstrumentEvent> TInstrumentEvents;
+ typedef std::vector<SVolumeEvent> TVolumeEvents;
+ typedef std::vector<SPitchEvent> TPitchEvents;
+
+#define bit_pos( pos ) (1<<pos)
+
+ class CVoiceData
+ {
+ public:
+ enum EEventStatus
+ {
+ kES_NoteEnd = bit_pos( 0 ),
+ kES_PitchEnd = bit_pos( 1 ),
+ kES_InstrEnd = bit_pos( 2 ),
+ kES_VolumeEnd = bit_pos( 3 ),
+
+ kES_None = 0
+ };
+
+ explicit CVoiceData()
+ : mForceNote ( true )
+ ,mEventStatus ( kES_None )
+ ,current_note ( 0 )
+ ,current_note_duration( 0 )
+ ,mNoteDuration ( 0 )
+ ,next_instrument_event( 0 )
+ ,next_volume_event ( 0 )
+ ,next_pitch_event ( 0 )
+ {
+ }
+
+ void Reset()
+ {
+ mForceNote = true;
+ mEventStatus = kES_None;
+ current_note = 0;
+ current_note_duration = 0;
+ mNoteDuration = 0;
+ next_instrument_event = 0;
+ next_volume_event = 0;
+ next_pitch_event = 0;
+ }
+
+ TNoteEvents note_events;
+ TInstrumentEvents instrument_events;
+ TVolumeEvents volume_events;
+ TPitchEvents pitch_events;
+
+ bool mForceNote : 1;
+ int mEventStatus;
+ unsigned int current_note;
+ int current_note_duration;
+ int mNoteDuration;
+ unsigned int next_instrument_event;
+ unsigned int next_volume_event;
+ unsigned int next_pitch_event;
+ };
+
+ typedef struct
+ {
+ uint16 index;
+ char record_used;
+ char name[9];
+ } SInstrumentName;
+
+ typedef std::vector<SInstrumentName> TInstrumentNames;
+
+ typedef struct
+ {
+ char version_major;
+ char version_minor;
+ char signature[6];
+ uint16 number_of_list_entries_used;
+ uint16 total_number_of_list_entries;
+ int32 abs_offset_of_name_list;
+ int32 abs_offset_of_data;
+
+ TInstrumentNames ins_name_list;
+ } SBnkHeader;
+
+ typedef struct
+ {
+ unsigned char key_scale_level;
+ unsigned char freq_multiplier;
+ unsigned char feed_back;
+ unsigned char attack_rate;
+ unsigned char sustain_level;
+ unsigned char sustaining_sound;
+ unsigned char decay_rate;
+ unsigned char release_rate;
+ unsigned char output_level;
+ unsigned char amplitude_vibrato;
+ unsigned char frequency_vibrato;
+ unsigned char envelope_scaling;
+ unsigned char fm_type;
+ } SFMOperator;
+
+ typedef struct
+ {
+ unsigned char ammulti;
+ unsigned char ksltl;
+ unsigned char ardr;
+ unsigned char slrr;
+ unsigned char fbc;
+ unsigned char waveform;
+ } SOPL2Op;
+
+ typedef struct
+ {
+ char mode;
+ char voice_number;
+ SOPL2Op modulator;
+ SOPL2Op carrier;
+ } SRolInstrument;
+
+ typedef struct
+ {
+ std::string name;
+ SRolInstrument instrument;
+ } SUsedList;
+
+ void load_tempo_events ( binistream *f );
+ bool load_voice_data ( binistream *f, std::string const &bnk_filename, const CFileProvider &fp );
+ void load_note_events ( binistream *f, CVoiceData &voice );
+ void load_instrument_events( binistream *f, CVoiceData &voice,
+ binistream *bnk_file, SBnkHeader const &bnk_header );
+ void load_volume_events ( binistream *f, CVoiceData &voice );
+ void load_pitch_events ( binistream *f, CVoiceData &voice );
+
+ bool load_bnk_info ( binistream *f, SBnkHeader &header );
+ int load_rol_instrument ( binistream *f, SBnkHeader const &header, std::string &name );
+ void read_rol_instrument ( binistream *f, SRolInstrument &ins );
+ void read_fm_operator ( binistream *f, SOPL2Op &opl2_op );
+ int get_ins_index( std::string const &name ) const;
+
+ void UpdateVoice( int const voice, CVoiceData &voiceData );
+ void SetNote( int const voice, int const note );
+ void SetNoteMelodic( int const voice, int const note );
+ void SetNotePercussive( int const voice, int const note );
+ void SetFreq ( int const voice, int const note, bool const keyOn=false );
+ void SetPitch ( int const voice, real32 const variation );
+ void SetVolume ( int const voice, int const volume );
+ void SetRefresh( float const multiplier );
+ void send_ins_data_to_chip( int const voice, int const ins_index );
+ void send_operator( int const voice, SOPL2Op const &modulator, SOPL2Op const &carrier );
+
+ class StringCompare
+ {
+ public:
+ bool operator()( SInstrumentName const &lhs, SInstrumentName const &rhs ) const
+ {
+ return keyLess(lhs.name, rhs.name);
+ }
+
+ bool operator()( SInstrumentName const &lhs, std::string const &rhs ) const
+ {
+ return keyLess(lhs.name, rhs.c_str());
+ }
+
+ bool operator()( std::string const &lhs, SInstrumentName const &rhs ) const
+ {
+ return keyLess(lhs.c_str(), rhs.name);
+ }
+ private:
+ bool keyLess( const char *const lhs, const char *const rhs ) const
+ {
+ return stricmp(lhs, rhs) < 0;
+ }
+ };
+
+ typedef std::vector<CVoiceData> TVoiceData;
+
+ SRolHeader *rol_header;
+ std::vector<STempoEvent> mTempoEvents;
+ TVoiceData voice_data;
+ std::vector<SUsedList> ins_list;
+
+ unsigned int mNextTempoEvent;
+ int mCurrTick;
+ int mTimeOfLastNote;
+ float mRefresh;
+ unsigned char bdRegister;
+ unsigned char bxRegister[9];
+ unsigned char volumeCache[11];
+ uint16 freqCache[11];
+ real32 pitchCache[11];
+
+ static int const kSizeofDataRecord;
+ static int const kMaxTickBeat;
+ static int const kSilenceNote;
+ static int const kNumMelodicVoices;
+ static int const kNumPercussiveVoices;
+ static int const kBassDrumChannel;
+ static int const kSnareDrumChannel;
+ static int const kTomtomChannel;
+ static int const kTomtomFreq;
+ static int const kSnareDrumFreq;
+ static float const kDefaultUpdateTme;
+ static float const kPitchFactor;
+ static uint16 const kNoteTable[12];
+};
+
+#endif
diff --git a/plugins/adplug/adplug/s3m.cpp b/plugins/adplug/adplug/s3m.cpp
new file mode 100644
index 00000000..ddc2a3b9
--- /dev/null
+++ b/plugins/adplug/adplug/s3m.cpp
@@ -0,0 +1,536 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 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 <string.h>
+#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
+ || checkhead->insnum > 99) {
+ 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
+
+ // security check
+ if(header.ordnum > 256 || header.insnum > 99 || header.patnum > 99) {
+ fp.close(f);
+ return false;
+ }
+
+ 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;
+
+ // some commands reset the infobyte memory
+ switch(channel[realchan].fx) {
+ case 1:
+ case 2:
+ case 3:
+ case 20:
+ channel[realchan].info = pattern[pattnr][row][chan].info;
+ break;
+ }
+
+ // 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);
+}
diff --git a/plugins/adplug/adplug/s3m.h b/plugins/adplug/adplug/s3m.h
new file mode 100644
index 00000000..aefc492e
--- /dev/null
+++ b/plugins/adplug/adplug/s3m.h
@@ -0,0 +1,107 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 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
diff --git a/plugins/adplug/adplug/sa2.cpp b/plugins/adplug/adplug/sa2.cpp
new file mode 100644
index 00000000..2e1248bc
--- /dev/null
+++ b/plugins/adplug/adplug/sa2.cpp
@@ -0,0 +1,262 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * sa2.cpp - SAdT2 Loader by Simon Peter <dn.tlp@gmx.net>
+ * SAdT Loader by Mamiya <mamiya@users.sourceforge.net>
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "sa2.h"
+#include "debug.h"
+
+CPlayer *Csa2Loader::factory(Copl *newopl)
+{
+ return new Csa2Loader(newopl);
+}
+
+bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ struct {
+ unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt;
+ } insts;
+ unsigned char buf;
+ int i,j, k, notedis = 0;
+ const unsigned char convfx[16] = {0,1,2,3,4,5,6,255,8,255,10,11,12,13,255,15};
+ unsigned char sat_type;
+ enum SAT_TYPE {
+ HAS_ARPEGIOLIST = (1 << 7),
+ HAS_V7PATTERNS = (1 << 6),
+ HAS_ACTIVECHANNELS = (1 << 5),
+ HAS_TRACKORDER = (1 << 4),
+ HAS_ARPEGIO = (1 << 3),
+ HAS_OLDBPM = (1 << 2),
+ HAS_OLDPATTERNS = (1 << 1),
+ HAS_UNKNOWN127 = (1 << 0)
+ };
+
+ // read header
+ f->readString(header.sadt, 4);
+ header.version = f->readInt(1);
+
+ // file validation section
+ if(strncmp(header.sadt,"SAdT",4)) { fp.close(f); return false; }
+ switch(header.version) {
+ case 1:
+ notedis = +0x18;
+ sat_type = HAS_UNKNOWN127 | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 2:
+ notedis = +0x18;
+ sat_type = HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 3:
+ notedis = +0x0c;
+ sat_type = HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 4:
+ notedis = +0x0c;
+ sat_type = HAS_ARPEGIO | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 5:
+ notedis = +0x0c;
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 6:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM;
+ break;
+ case 7:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_V7PATTERNS;
+ break;
+ case 8:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_TRACKORDER;
+ break;
+ case 9:
+ sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_TRACKORDER | HAS_ACTIVECHANNELS;
+ break;
+ default: /* unknown */
+ fp.close(f);
+ return false;
+ }
+
+ // load section
+ // instruments
+ for(i = 0; i < 31; i++) {
+ if(sat_type & HAS_ARPEGIO) {
+ for(j = 0; j < 11; j++) insts.data[j] = f->readInt(1);
+ insts.arpstart = f->readInt(1);
+ insts.arpspeed = f->readInt(1);
+ insts.arppos = f->readInt(1);
+ insts.arpspdcnt = f->readInt(1);
+ inst[i].arpstart = insts.arpstart;
+ inst[i].arpspeed = insts.arpspeed;
+ inst[i].arppos = insts.arppos;
+ inst[i].arpspdcnt = insts.arpspdcnt;
+ } else {
+ for(j = 0; j < 11; j++) insts.data[j] = f->readInt(1);
+ inst[i].arpstart = 0;
+ inst[i].arpspeed = 0;
+ inst[i].arppos = 0;
+ inst[i].arpspdcnt = 0;
+ }
+ for(j=0;j<11;j++)
+ inst[i].data[j] = insts.data[j];
+ inst[i].misc = 0;
+ inst[i].slide = 0;
+ }
+
+ // instrument names
+ for(i = 0; i < 29; i++) f->readString(instname[i], 17);
+
+ f->ignore(3); // dummy bytes
+ for(i = 0; i < 128; i++) order[i] = f->readInt(1); // pattern orders
+ if(sat_type & HAS_UNKNOWN127) f->ignore(127);
+
+ // infos
+ nop = f->readInt(2); length = f->readInt(1); restartpos = f->readInt(1);
+
+ // bpm
+ bpm = f->readInt(2);
+ if(sat_type & HAS_OLDBPM) {
+ bpm = bpm * 125 / 50; // cps -> bpm
+ }
+
+ if(sat_type & HAS_ARPEGIOLIST) {
+ init_specialarp();
+ for(i = 0; i < 256; i++) arplist[i] = f->readInt(1); // arpeggio list
+ for(i = 0; i < 256; i++) arpcmd[i] = f->readInt(1); // arpeggio commands
+ }
+
+ for(i=0;i<64;i++) { // track orders
+ for(j=0;j<9;j++) {
+ if(sat_type & HAS_TRACKORDER)
+ trackord[i][j] = f->readInt(1);
+ else
+ {
+ trackord[i][j] = i * 9 + j;
+ }
+ }
+ }
+
+ if(sat_type & HAS_ACTIVECHANNELS)
+ activechan = f->readInt(2) << 16; // active channels
+
+ AdPlug_LogWrite("Csa2Loader::load(\"%s\"): sat_type = %x, nop = %d, "
+ "length = %d, restartpos = %d, activechan = %x, bpm = %d\n",
+ filename.c_str(), sat_type, nop, length, restartpos, activechan, bpm);
+
+ // track data
+ if(sat_type & HAS_OLDPATTERNS) {
+ i = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++) {
+ for(k=0;k<9;k++) {
+ buf = f->readInt(1);
+ tracks[i+k][j].note = buf ? (buf + notedis) : 0;
+ tracks[i+k][j].inst = f->readInt(1);
+ tracks[i+k][j].command = convfx[f->readInt(1) & 0xf];
+ tracks[i+k][j].param1 = f->readInt(1);
+ tracks[i+k][j].param2 = f->readInt(1);
+ }
+ }
+ i+=9;
+ }
+ } else
+ if(sat_type & HAS_V7PATTERNS) {
+ i = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++) {
+ for(k=0;k<9;k++) {
+ buf = f->readInt(1);
+ tracks[i+k][j].note = buf >> 1;
+ tracks[i+k][j].inst = (buf & 1) << 4;
+ buf = f->readInt(1);
+ tracks[i+k][j].inst += buf >> 4;
+ tracks[i+k][j].command = convfx[buf & 0x0f];
+ buf = f->readInt(1);
+ tracks[i+k][j].param1 = buf >> 4;
+ tracks[i+k][j].param2 = buf & 0x0f;
+ }
+ }
+ i+=9;
+ }
+ } else {
+ i = 0;
+ while(!f->ateof()) {
+ for(j=0;j<64;j++) {
+ buf = f->readInt(1);
+ tracks[i][j].note = buf >> 1;
+ tracks[i][j].inst = (buf & 1) << 4;
+ buf = f->readInt(1);
+ tracks[i][j].inst += buf >> 4;
+ tracks[i][j].command = convfx[buf & 0x0f];
+ buf = f->readInt(1);
+ tracks[i][j].param1 = buf >> 4;
+ tracks[i][j].param2 = buf & 0x0f;
+ }
+ i++;
+ }
+ }
+ fp.close(f);
+
+ // fix instrument names
+ for(i=0;i<29;i++)
+ for(j=0;j<17;j++)
+ if(!instname[i][j])
+ instname[i][j] = ' ';
+
+ rewind(0); // rewind module
+ return true;
+}
+
+std::string Csa2Loader::gettype()
+{
+ char tmpstr[40];
+
+ sprintf(tmpstr,"Surprise! Adlib Tracker 2 (version %d)",header.version);
+ return std::string(tmpstr);
+}
+
+std::string Csa2Loader::gettitle()
+{
+ char bufinst[29*17],buf[18];
+ int i,ptr;
+
+ // parse instrument names for song name
+ memset(bufinst,'\0',29*17);
+ for(i=0;i<29;i++) {
+ buf[16] = ' '; buf[17] = '\0';
+ memcpy(buf,instname[i]+1,16);
+ for(ptr=16;ptr>0;ptr--)
+ if(buf[ptr] == ' ')
+ buf[ptr] = '\0';
+ else {
+ if(ptr<16)
+ buf[ptr+1] = ' ';
+ break;
+ }
+ strcat(bufinst,buf);
+ }
+
+ if(strchr(bufinst,'"'))
+ return std::string(bufinst,strchr(bufinst,'"')-bufinst+1,strrchr(bufinst,'"')-strchr(bufinst,'"')-1);
+ else
+ return std::string();
+}
diff --git a/plugins/adplug/adplug/sa2.h b/plugins/adplug/adplug/sa2.h
new file mode 100644
index 00000000..ed904f0d
--- /dev/null
+++ b/plugins/adplug/adplug/sa2.h
@@ -0,0 +1,55 @@
+/*
+ * 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
+ *
+ * sa2.h - SAdT2 Loader by Simon Peter <dn.tlp@gmx.net>
+ * SAdT Loader by Mamiya <mamiya@users.sourceforge.net>
+ */
+
+#include "protrack.h"
+
+class Csa2Loader: public CmodPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ Csa2Loader(Copl *newopl)
+ : CmodPlayer(newopl)
+ { }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+
+ std::string gettype();
+ std::string gettitle();
+ unsigned int getinstruments()
+ { return 31; }
+ std::string getinstrument(unsigned int n)
+ {
+ if(n < 29)
+ return std::string(instname[n],1,16);
+ else
+ return std::string("-broken-");
+ }
+
+private:
+ struct sa2header {
+ char sadt[4];
+ unsigned char version;
+ } header;
+
+ char instname[29][17];
+};
diff --git a/plugins/adplug/adplug/silentopl.h b/plugins/adplug/adplug/silentopl.h
new file mode 100644
index 00000000..352754ab
--- /dev/null
+++ b/plugins/adplug/adplug/silentopl.h
@@ -0,0 +1,29 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * silentopl.h - Silent OPL device, by Simon Peter (dn.tlp@gmx.net)
+ */
+
+#include "opl.h"
+
+class CSilentopl: public Copl
+{
+public:
+ void write(int reg, int val) {}
+ void init() {}
+};
diff --git a/plugins/adplug/adplug/sng.cpp b/plugins/adplug/adplug/sng.cpp
new file mode 100644
index 00000000..bf0fa699
--- /dev/null
+++ b/plugins/adplug/adplug/sng.cpp
@@ -0,0 +1,85 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 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
+ *
+ * sng.cpp - SNG Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+#include "sng.h"
+
+CPlayer *CsngPlayer::factory(Copl *newopl)
+{
+ return new CsngPlayer(newopl);
+}
+
+bool CsngPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ int i;
+
+ // load header
+ f->readString(header.id, 4);
+ header.length = f->readInt(2); header.start = f->readInt(2);
+ header.loop = f->readInt(2); header.delay = f->readInt(1);
+ header.compressed = f->readInt(1) ? true : false;
+
+ // file validation section
+ if(strncmp(header.id,"ObsM",4)) { fp.close(f); return false; }
+
+ // load section
+ header.length /= 2; header.start /= 2; header.loop /= 2;
+ data = new Sdata [header.length];
+ for(i = 0; i < header.length; i++) {
+ data[i].val = f->readInt(1);
+ data[i].reg = f->readInt(1);
+ }
+
+ rewind(0);
+ fp.close(f);
+ return true;
+}
+
+bool CsngPlayer::update()
+{
+ if(header.compressed && del) {
+ del--;
+ return !songend;
+ }
+
+ while(data[pos].reg) {
+ opl->write(data[pos].reg, data[pos].val);
+ pos++;
+ if(pos >= header.length) {
+ songend = true;
+ pos = header.loop;
+ }
+ }
+
+ if(!header.compressed)
+ opl->write(data[pos].reg, data[pos].val);
+
+ if(data[pos].val) del = data[pos].val - 1; pos++;
+ if(pos >= header.length) { songend = true; pos = header.loop; }
+ return !songend;
+}
+
+void CsngPlayer::rewind(int subsong)
+{
+ pos = header.start; del = header.delay; songend = false;
+ opl->init(); opl->write(1,32); // go to OPL2 mode
+}
diff --git a/plugins/adplug/adplug/sng.h b/plugins/adplug/adplug/sng.h
new file mode 100644
index 00000000..6eef98de
--- /dev/null
+++ b/plugins/adplug/adplug/sng.h
@@ -0,0 +1,64 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2002 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
+ *
+ * sng.h - SNG Player by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_SNGPLAYER
+#define H_ADPLUG_SNGPLAYER
+
+#include "player.h"
+
+class CsngPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CsngPlayer(Copl *newopl)
+ : CPlayer(newopl), data(0)
+ { };
+ ~CsngPlayer()
+ { if(data) delete [] data; };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh()
+ { return 70.0f; };
+
+ std::string gettype()
+ { return std::string("SNG File Format"); };
+
+protected:
+ struct {
+ char id[4];
+ unsigned short length,start,loop;
+ unsigned char delay;
+ bool compressed;
+ } header;
+
+ struct Sdata {
+ unsigned char val,reg;
+ } *data;
+
+ unsigned char del;
+ unsigned short pos;
+ bool songend;
+};
+
+#endif
diff --git a/plugins/adplug/adplug/temuopl.cpp b/plugins/adplug/adplug/temuopl.cpp
new file mode 100644
index 00000000..13691d78
--- /dev/null
+++ b/plugins/adplug/adplug/temuopl.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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
+ *
+ * temuopl.cpp - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "temuopl.h"
+
+CTemuopl::CTemuopl(int rate, bool bit16, bool usestereo)
+ : use16bit(bit16), stereo(usestereo)
+{
+ opl = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
+}
+
+CTemuopl::~CTemuopl()
+{
+ OPLDestroy(opl);
+}
+
+void CTemuopl::update(short *buf, int samples)
+{
+ int i;
+
+ if(use16bit) {
+ YM3812UpdateOne(opl,buf,samples);
+
+ if(stereo)
+ for(i=samples-1;i>=0;i--) {
+ buf[i*2] = buf[i];
+ buf[i*2+1] = buf[i];
+ }
+ } else {
+ short *tempbuf = new short[stereo ? samples*2 : samples];
+ int i;
+
+ YM3812UpdateOne(opl,tempbuf,samples);
+
+ if(stereo)
+ for(i=samples-1;i>=0;i--) {
+ tempbuf[i*2] = tempbuf[i];
+ tempbuf[i*2+1] = tempbuf[i];
+ }
+
+ for(i=0;i<(stereo ? samples*2 : samples);i++)
+ ((char *)buf)[i] = (tempbuf[i] >> 8) ^ 0x80;
+
+ delete [] tempbuf;
+ }
+}
+
+void CTemuopl::write(int reg, int val)
+{
+ OPLWrite(opl,0,reg);
+ OPLWrite(opl,1,val);
+}
+
+void CTemuopl::init()
+{
+ OPLResetChip(opl);
+}
diff --git a/plugins/adplug/adplug/temuopl.h b/plugins/adplug/adplug/temuopl.h
new file mode 100644
index 00000000..564fe3d8
--- /dev/null
+++ b/plugins/adplug/adplug/temuopl.h
@@ -0,0 +1,47 @@
+/*
+ * 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
+ *
+ * temuopl.h - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_TEMUOPL
+#define H_ADPLUG_TEMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "fmopl.h"
+}
+
+class CTemuopl: public Copl
+{
+ public:
+ CTemuopl(int rate, bool bit16, bool usestereo); // rate = sample rate
+ virtual ~CTemuopl();
+
+ void update(short *buf, int samples); // fill buffer
+
+ // template methods
+ void write(int reg, int val);
+ void init();
+
+ private:
+ bool use16bit,stereo;
+ FM_OPL *opl; // holds emulator data
+};
+
+#endif
diff --git a/plugins/adplug/adplug/u6m.cpp b/plugins/adplug/adplug/u6m.cpp
new file mode 100644
index 00000000..5c0a2ca2
--- /dev/null
+++ b/plugins/adplug/adplug/u6m.cpp
@@ -0,0 +1,934 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * u6m.cpp - Ultima 6 Music Player by Marc Winterrowd.
+ * This code extends the Adlib Winamp plug-in by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "u6m.h"
+
+// Makes security checks on output buffer before writing
+#define SAVE_OUTPUT_ROOT(c, d, p) \
+if(p < d.size) \
+ output_root(c, d.data, p); \
+else \
+ return false;
+
+CPlayer *Cu6mPlayer::factory(Copl *newopl)
+{
+ return new Cu6mPlayer(newopl);
+}
+
+bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ // file validation section
+ // this section only checks a few *necessary* conditions
+ unsigned long filesize, decompressed_filesize;
+ binistream *f;
+
+ f = fp.open(filename); if(!f) return false;
+ filesize = fp.filesize(f);
+
+ if (filesize >= 6)
+ {
+ // check if the file has a valid pseudo-header
+ unsigned char pseudo_header[6];
+ f->readString((char *)pseudo_header, 6);
+ decompressed_filesize = pseudo_header[0] + (pseudo_header[1] << 8);
+
+ if (!( (pseudo_header[2]==0) && (pseudo_header[3]==0) &&
+ (pseudo_header[4] + ((pseudo_header[5] & 0x1)<<8) == 0x100) &&
+ (decompressed_filesize > (filesize-4)) ))
+ {
+ fp.close(f);
+ return(false);
+ }
+ }
+ else
+ {
+ fp.close(f);
+ return(false);
+ }
+
+ // load section
+ song_data = new unsigned char[decompressed_filesize];
+ unsigned char* compressed_song_data = new unsigned char[filesize-3];
+
+ f->seek(4);
+ f->readString((char *)compressed_song_data, filesize - 4);
+ fp.close(f);
+
+ // attempt to decompress the song data
+ // if unsuccessful, deallocate song_data[] on the spot, and return(false)
+ data_block source, destination;
+ source.size = filesize-4;
+ source.data = compressed_song_data;
+ destination.size = decompressed_filesize;
+ destination.data = song_data;
+
+ if (!lzw_decompress(source,destination))
+ {
+ delete[] compressed_song_data;
+ delete[] song_data;
+ return(false);
+ }
+
+ // deallocation section
+ delete[] compressed_song_data;
+
+ rewind(0);
+ return (true);
+}
+
+
+bool Cu6mPlayer::update()
+{
+ if (!driver_active)
+ {
+ driver_active = true;
+ dec_clip(read_delay);
+ if (read_delay == 0)
+ {
+ command_loop();
+ }
+
+ // on all Adlib channels: freq slide/vibrato, mute factor slide
+ for (int i = 0; i < 9; i++)
+ {
+ if (channel_freq_signed_delta[i]!=0)
+ // frequency slide + mute factor slide
+ {
+ // freq slide
+ freq_slide(i);
+
+ // mute factor slide
+ if (carrier_mf_signed_delta[i]!=0)
+ {
+ mf_slide(i);
+ }
+ }
+ else
+ // vibrato + mute factor slide
+ {
+ // vibrato
+ if ((vb_multiplier[i]!=0) && ((channel_freq[i].hi & 0x20)==0x20))
+ {
+ vibrato(i);
+ }
+
+ // mute factor slide
+ if (carrier_mf_signed_delta[i]!=0)
+ {
+ mf_slide(i);
+ }
+ }
+ }
+
+ driver_active = false;
+ }
+
+ return !songend;
+}
+
+
+void Cu6mPlayer::rewind(int subsong)
+{
+ played_ticks = 0;
+ songend = false;
+
+ // set the driver's internal variables
+ byte_pair freq_word = {0,0};
+
+ driver_active = false;
+ song_pos = 0;
+ loop_position = 0; // position of the loop point
+ read_delay = 0; // delay (in timer ticks) before further song data is read
+
+ for (int i = 0; i < 9; i++)
+ {
+ // frequency
+ channel_freq_signed_delta[i] = 0;
+ channel_freq[i] = freq_word; // Adlib freq settings for each channel
+
+ // vibrato ("vb")
+ vb_current_value[i] = 0;
+ vb_double_amplitude[i] = 0;
+ vb_multiplier[i] = 0;
+ vb_direction_flag[i] = 0;
+
+ // mute factor ("mf") == ~(volume)
+ carrier_mf[i] = 0;
+ carrier_mf_signed_delta[i] = 0;
+ carrier_mf_mod_delay_backup[i] = 0;
+ carrier_mf_mod_delay[i] = 0;
+ }
+
+ while (!subsong_stack.empty()) // empty subsong stack
+ subsong_stack.pop();
+
+ opl->init();
+ out_adlib(1,32); // go to OPL2 mode
+}
+
+
+float Cu6mPlayer::getrefresh()
+{
+ return ((float)60); // the Ultima 6 music driver expects to be called at 60 Hz
+}
+
+
+// ============================================================================================
+//
+//
+// Functions called by load()
+//
+//
+// ============================================================================================
+
+
+// decompress from memory to memory
+bool Cu6mPlayer::lzw_decompress(Cu6mPlayer::data_block source, Cu6mPlayer::data_block dest)
+{
+ bool end_marker_reached = false;
+ int codeword_size = 9;
+ long bits_read = 0;
+ int next_free_codeword = 0x102;
+ int dictionary_size = 0x200;
+ MyDict dictionary = MyDict();
+ std::stack<unsigned char> root_stack;
+
+ long bytes_written = 0;
+
+ int cW;
+ int pW;
+ unsigned char C;
+
+ while (!end_marker_reached)
+ {
+ cW = get_next_codeword(bits_read, source.data, codeword_size);
+ switch (cW)
+ {
+ // re-init the dictionary
+ case 0x100:
+ codeword_size = 9;
+ next_free_codeword = 0x102;
+ dictionary_size = 0x200;
+ dictionary.reset();
+ cW = get_next_codeword(bits_read, source.data, codeword_size);
+ SAVE_OUTPUT_ROOT((unsigned char)cW, dest, bytes_written);
+ break;
+ // end of compressed file has been reached
+ case 0x101:
+ end_marker_reached = true;
+ break;
+ // (cW <> 0x100) && (cW <> 0x101)
+ default:
+ if (cW < next_free_codeword) // codeword is already in the dictionary
+ {
+ // create the string associated with cW (on the stack)
+ get_string(cW,dictionary,root_stack);
+ C = root_stack.top();
+ // output the string represented by cW
+ while (!root_stack.empty())
+ {
+ SAVE_OUTPUT_ROOT(root_stack.top(), dest, bytes_written);
+ root_stack.pop();
+ }
+ // add pW+C to the dictionary
+ dictionary.add(C,pW);
+
+ next_free_codeword++;
+ if (next_free_codeword >= dictionary_size)
+ {
+ if (codeword_size < max_codeword_length)
+ {
+ codeword_size += 1;
+ dictionary_size *= 2;
+ }
+ }
+ }
+ else // codeword is not yet defined
+ {
+ // create the string associated with pW (on the stack)
+ get_string(pW,dictionary,root_stack);
+ C = root_stack.top();
+ // output the string represented by pW
+ while (!root_stack.empty())
+ {
+ SAVE_OUTPUT_ROOT(root_stack.top(), dest, bytes_written);
+ root_stack.pop();
+ }
+ // output the char C
+ SAVE_OUTPUT_ROOT(C, dest, bytes_written);
+
+ // the new dictionary entry must correspond to cW
+ // if it doesn't, something is wrong with the lzw-compressed data.
+ if (cW != next_free_codeword)
+ {
+ /* printf("cW != next_free_codeword!\n");
+ exit(-1); */
+ return false;
+ }
+ // add pW+C to the dictionary
+ dictionary.add(C,pW);
+
+ next_free_codeword++;
+ if (next_free_codeword >= dictionary_size)
+ {
+ if (codeword_size < max_codeword_length)
+ {
+ codeword_size += 1;
+ dictionary_size *= 2;
+ }
+ }
+ };
+ break;
+ }
+ // shift roles - the current cW becomes the new pW
+ pW = cW;
+ }
+
+ return(true); // indicate successful decompression
+}
+
+
+// --------------------
+// Additional functions
+// --------------------
+
+
+// Read the next code word from the source buffer
+int Cu6mPlayer::get_next_codeword (long& bits_read, unsigned char *source, int codeword_size)
+{
+ unsigned char b0,b1,b2;
+ int codeword;
+
+ b0 = source[bits_read/8];
+ b1 = source[bits_read/8+1];
+ b2 = source[bits_read/8+2];
+
+ codeword = ((b2 << 16) + (b1 << 8) + b0);
+ codeword = codeword >> (bits_read % 8);
+ switch (codeword_size)
+ {
+ case 0x9:
+ codeword = codeword & 0x1ff;
+ break;
+ case 0xa:
+ codeword = codeword & 0x3ff;
+ break;
+ case 0xb:
+ codeword = codeword & 0x7ff;
+ break;
+ case 0xc:
+ codeword = codeword & 0xfff;
+ break;
+ default:
+ codeword = -1; // indicates that an error has occurred
+ break;
+ }
+
+ bits_read += codeword_size;
+ return (codeword);
+}
+
+
+// output a root to memory
+void Cu6mPlayer::output_root(unsigned char root, unsigned char *destination, long& position)
+{
+ destination[position] = root;
+ position++;
+}
+
+
+// output the string represented by a codeword
+void Cu6mPlayer::get_string(int codeword, Cu6mPlayer::MyDict& dictionary, std::stack<unsigned char>& root_stack)
+{
+ unsigned char root;
+ int current_codeword;
+
+ current_codeword = codeword;
+
+ while (current_codeword > 0xff)
+ {
+ root = dictionary.get_root(current_codeword);
+ current_codeword = dictionary.get_codeword(current_codeword);
+ root_stack.push(root);
+ }
+
+ // push the root at the leaf
+ root_stack.push((unsigned char)current_codeword);
+}
+
+
+// ============================================================================================
+//
+//
+// Functions called by update()
+//
+//
+// ============================================================================================
+
+
+// This function reads the song data and executes the embedded commands.
+void Cu6mPlayer::command_loop()
+{
+ unsigned char command_byte; // current command byte
+ int command_nibble_hi; // command byte, bits 4-7
+ int command_nibble_lo; // command byte, bite 0-3
+ bool repeat_loop = true; //
+
+ do
+ {
+ // extract low and high command nibbles
+ command_byte = read_song_byte(); // implicitly increments song_pos
+ command_nibble_hi = command_byte >> 4;
+ command_nibble_lo = command_byte & 0xf;
+
+ switch (command_nibble_hi)
+ {
+ case 0x0: command_0(command_nibble_lo); break;
+ case 0x1: command_1(command_nibble_lo); break;
+ case 0x2: command_2(command_nibble_lo); break;
+ case 0x3: command_3(command_nibble_lo); break;
+ case 0x4: command_4(command_nibble_lo); break;
+ case 0x5: command_5(command_nibble_lo); break;
+ case 0x6: command_6(command_nibble_lo); break;
+ case 0x7: command_7(command_nibble_lo); break;
+ case 0x8:
+ switch (command_nibble_lo)
+ {
+ case 1: command_81(); break;
+ case 2: command_82(); repeat_loop = false; break;
+ case 3: command_83(); break;
+ case 5: command_85(); break;
+ case 6: command_86(); break;
+ default: break; // maybe generate an error?
+ }
+ break;
+ case 0xE: command_E(); break;
+ case 0xF: command_F(); break;
+ default: break; // maybe generate an error?
+ }
+
+ } while (repeat_loop);
+}
+
+
+// --------------------------------------------------------
+// The commands supported by the U6 music file format
+// --------------------------------------------------------
+
+// ----------------------------------------
+// Set octave and frequency, note off
+// Format: 0c nn
+// c = channel, nn = packed Adlib frequency
+// ----------------------------------------
+void Cu6mPlayer::command_0(int channel)
+{
+ unsigned char freq_byte;
+ byte_pair freq_word;
+
+ freq_byte = read_song_byte();
+ freq_word = expand_freq_byte(freq_byte);
+ set_adlib_freq(channel,freq_word);
+}
+
+
+// ---------------------------------------------------
+// Set octave and frequency, old note off, new note on
+// Format: 1c nn
+// c = channel, nn = packed Adlib frequency
+// ---------------------------------------------------
+void Cu6mPlayer::command_1(int channel)
+{
+ unsigned char freq_byte;
+ byte_pair freq_word;
+
+ vb_direction_flag[channel] = 0;
+ vb_current_value[channel] = 0;
+
+ freq_byte = read_song_byte();
+ freq_word = expand_freq_byte(freq_byte);
+ set_adlib_freq(channel,freq_word);
+
+ freq_word.hi = freq_word.hi | 0x20; // note on
+ set_adlib_freq(channel,freq_word);
+}
+
+
+// ----------------------------------------
+// Set octave and frequency, note on
+// Format: 2c nn
+// c = channel, nn = packed Adlib frequency
+// ----------------------------------------
+void Cu6mPlayer::command_2(int channel)
+{
+ unsigned char freq_byte;
+ byte_pair freq_word;
+
+ freq_byte = read_song_byte();
+ freq_word = expand_freq_byte(freq_byte);
+ freq_word.hi = freq_word.hi | 0x20; // note on
+ set_adlib_freq(channel,freq_word);
+}
+
+
+// --------------------------------------
+// Set "carrier mute factor"==not(volume)
+// Format: 3c nn
+// c = channel, nn = mute factor
+// --------------------------------------
+void Cu6mPlayer::command_3(int channel)
+{
+ unsigned char mf_byte;
+
+ carrier_mf_signed_delta[channel] = 0;
+ mf_byte = read_song_byte();
+ set_carrier_mf(channel,mf_byte);
+}
+
+
+// ----------------------------------------
+// set "modulator mute factor"==not(volume)
+// Format: 4c nn
+// c = channel, nn = mute factor
+// ----------------------------------------
+void Cu6mPlayer::command_4(int channel)
+{
+ unsigned char mf_byte;
+
+ mf_byte = read_song_byte();
+ set_modulator_mf(channel,mf_byte);
+}
+
+
+// --------------------------------------------
+// Set portamento (pitch slide)
+// Format: 5c nn
+// c = channel, nn = signed channel pitch delta
+// --------------------------------------------
+void Cu6mPlayer::command_5(int channel)
+{
+ channel_freq_signed_delta[channel] = read_signed_song_byte();
+}
+
+
+// --------------------------------------------
+// Set vibrato paramters
+// Format: 6c mn
+// c = channel
+// m = vibrato double amplitude
+// n = vibrato multiplier
+// --------------------------------------------
+void Cu6mPlayer::command_6(int channel)
+{
+ unsigned char vb_parameters;
+
+ vb_parameters = read_song_byte();
+ vb_double_amplitude[channel] = vb_parameters >> 4; // high nibble
+ vb_multiplier[channel] = vb_parameters & 0xF; // low nibble
+}
+
+
+// ----------------------------------------
+// Assign Adlib instrument to Adlib channel
+// Format: 7c nn
+// c = channel, nn = instrument number
+// ----------------------------------------
+void Cu6mPlayer::command_7(int channel)
+{
+ int instrument_offset = instrument_offsets[read_song_byte()];
+ out_adlib_opcell(channel, false, 0x20, *(song_data + instrument_offset+0));
+ out_adlib_opcell(channel, false, 0x40, *(song_data + instrument_offset+1));
+ out_adlib_opcell(channel, false, 0x60, *(song_data + instrument_offset+2));
+ out_adlib_opcell(channel, false, 0x80, *(song_data + instrument_offset+3));
+ out_adlib_opcell(channel, false, 0xE0, *(song_data + instrument_offset+4));
+ out_adlib_opcell(channel, true, 0x20, *(song_data + instrument_offset+5));
+ out_adlib_opcell(channel, true, 0x40, *(song_data + instrument_offset+6));
+ out_adlib_opcell(channel, true, 0x60, *(song_data + instrument_offset+7));
+ out_adlib_opcell(channel, true, 0x80, *(song_data + instrument_offset+8));
+ out_adlib_opcell(channel, true, 0xE0, *(song_data + instrument_offset+9));
+ out_adlib(0xC0+channel, *(song_data + instrument_offset+10));
+}
+
+
+// -------------------------------------------
+// Branch to a new subsong
+// Format: 81 nn aa bb
+// nn == number of times to repeat the subsong
+// aa == subsong offset (low byte)
+// bb == subsong offset (high byte)
+// -------------------------------------------
+void Cu6mPlayer::command_81()
+{
+ subsong_info new_ss_info;
+
+ new_ss_info.subsong_repetitions = read_song_byte();
+ new_ss_info.subsong_start = read_song_byte(); new_ss_info.subsong_start += read_song_byte() << 8;
+ new_ss_info.continue_pos = song_pos;
+
+ subsong_stack.push(new_ss_info);
+ song_pos = new_ss_info.subsong_start;
+}
+
+
+// ------------------------------------------------------------
+// Stop interpreting commands for this timer tick
+// Format: 82 nn
+// nn == delay (in timer ticks) until further data will be read
+// ------------------------------------------------------------
+void Cu6mPlayer::command_82()
+{
+ read_delay = read_song_byte();
+}
+
+
+// -----------------------------
+// Adlib instrument data follows
+// Format: 83 nn <11 bytes>
+// nn == instrument number
+// -----------------------------
+void Cu6mPlayer::command_83()
+{
+ unsigned char instrument_number = read_song_byte();
+ instrument_offsets[instrument_number] = song_pos;
+ song_pos += 11;
+}
+
+
+// ----------------------------------------------
+// Set -1 mute factor slide (upward volume slide)
+// Format: 85 cn
+// c == channel
+// n == slide delay
+// ----------------------------------------------
+void Cu6mPlayer::command_85()
+{
+ unsigned char data_byte = read_song_byte();
+ int channel = data_byte >> 4; // high nibble
+ unsigned char slide_delay = data_byte & 0xF; // low nibble
+ carrier_mf_signed_delta[channel] = +1;
+ carrier_mf_mod_delay[channel] = slide_delay + 1;
+ carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
+}
+
+
+// ------------------------------------------------
+// Set +1 mute factor slide (downward volume slide)
+// Format: 86 cn
+// c == channel
+// n == slide speed
+// ------------------------------------------------
+void Cu6mPlayer::command_86()
+{
+ unsigned char data_byte = read_song_byte();
+ int channel = data_byte >> 4; // high nibble
+ unsigned char slide_delay = data_byte & 0xF; // low nibble
+ carrier_mf_signed_delta[channel] = -1;
+ carrier_mf_mod_delay[channel] = slide_delay + 1;
+ carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
+}
+
+
+// --------------
+// Set loop point
+// Format: E?
+// --------------
+void Cu6mPlayer::command_E()
+{
+ loop_position = song_pos;
+}
+
+
+// ---------------------------
+// Return from current subsong
+// Format: F?
+// ---------------------------
+void Cu6mPlayer::command_F()
+{
+ if (!subsong_stack.empty())
+ {
+ subsong_info temp = subsong_stack.top();
+ subsong_stack.pop();
+ temp.subsong_repetitions--;
+ if (temp.subsong_repetitions==0)
+ {
+ song_pos = temp.continue_pos;
+ }
+ else
+ {
+ song_pos = temp.subsong_start;
+ subsong_stack.push(temp);
+ }
+ }
+ else
+ {
+ song_pos = loop_position;
+ songend = true;
+ }
+}
+
+
+// --------------------
+// Additional functions
+// --------------------
+
+// This function decrements its argument, without allowing it to become negative.
+void Cu6mPlayer::dec_clip(int& param)
+{
+ param--;
+ if (param < 0) { param = 0; }
+}
+
+
+// Returns the byte at the current song position.
+// Side effect: increments song_pos.
+unsigned char Cu6mPlayer::read_song_byte()
+{
+ unsigned char song_byte;
+ song_byte = song_data[song_pos];
+ song_pos++;
+ return(song_byte);
+}
+
+
+// Same as read_song_byte(), except that it returns a signed byte
+signed char Cu6mPlayer::read_signed_song_byte()
+{
+ unsigned char song_byte;
+ int signed_value;
+ song_byte = *(song_data + song_pos);
+ song_pos++;
+ if (song_byte <= 127)
+ {
+ signed_value = song_byte;
+ }
+ else
+ {
+ signed_value = (int)song_byte - 0x100;
+ }
+ return((signed char)signed_value);
+}
+
+
+Cu6mPlayer::byte_pair Cu6mPlayer::expand_freq_byte(unsigned char freq_byte)
+{
+ const byte_pair freq_table[24] =
+ {
+ {0x00,0x00}, {0x58,0x01}, {0x82,0x01}, {0xB0,0x01},
+ {0xCC,0x01}, {0x03,0x02}, {0x41,0x02}, {0x86,0x02},
+ {0x00,0x00}, {0x6A,0x01}, {0x96,0x01}, {0xC7,0x01},
+ {0xE4,0x01}, {0x1E,0x02}, {0x5F,0x02}, {0xA8,0x02},
+ {0x00,0x00}, {0x47,0x01}, {0x6E,0x01}, {0x9A,0x01},
+ {0xB5,0x01}, {0xE9,0x01}, {0x24,0x02}, {0x66,0x02}
+ };
+
+ int packed_freq;
+ int octave;
+ byte_pair freq_word;
+
+ packed_freq = freq_byte & 0x1F;
+ octave = freq_byte >> 5;
+
+ // range check (not present in the original U6 music driver)
+ if (packed_freq >= 24) { packed_freq = 0; }
+
+ freq_word.hi = freq_table[packed_freq].hi + (octave << 2);
+ freq_word.lo = freq_table[packed_freq].lo;
+
+ return(freq_word);
+}
+
+
+void Cu6mPlayer::set_adlib_freq(int channel,Cu6mPlayer::byte_pair freq_word)
+{
+ out_adlib(0xA0+channel,freq_word.lo);
+ out_adlib(0xB0+channel,freq_word.hi);
+ // update the Adlib register backups
+ channel_freq[channel] = freq_word;
+}
+
+
+// this function sets the Adlib frequency, but does not update the register backups
+void Cu6mPlayer::set_adlib_freq_no_update(int channel,Cu6mPlayer::byte_pair freq_word)
+{
+ out_adlib(0xA0+channel,freq_word.lo);
+ out_adlib(0xB0+channel,freq_word.hi);
+}
+
+
+void Cu6mPlayer::set_carrier_mf(int channel,unsigned char mute_factor)
+{
+ out_adlib_opcell(channel,true,0x40,mute_factor);
+ carrier_mf[channel] = mute_factor;
+}
+
+
+void Cu6mPlayer::set_modulator_mf(int channel,unsigned char mute_factor)
+{
+ out_adlib_opcell(channel,false,0x40,mute_factor);
+}
+
+
+void Cu6mPlayer::freq_slide(int channel)
+{
+ byte_pair freq = channel_freq[channel];
+
+ long freq_word = freq.lo + (freq.hi << 8) + channel_freq_signed_delta[channel];
+ if (freq_word < 0) { freq_word += 0x10000; }
+ if (freq_word > 0xFFFF) { freq_word -= 0x10000; }
+
+ freq.lo = freq_word & 0xFF;
+ freq.hi = (freq_word >> 8) & 0xFF;
+ set_adlib_freq(channel,freq);
+}
+
+
+void Cu6mPlayer::vibrato(int channel)
+{
+ byte_pair freq;
+
+ if (vb_current_value[channel] >= vb_double_amplitude[channel])
+ { vb_direction_flag[channel] = 1; }
+ else if (vb_current_value[channel] <= 0)
+ { vb_direction_flag[channel] = 0; }
+
+ if (vb_direction_flag[channel]==0)
+ { vb_current_value[channel]++; }
+ else
+ { vb_current_value[channel]--; }
+
+ long freq_word = channel_freq[channel].lo + (channel_freq[channel].hi << 8);
+ freq_word += (vb_current_value[channel] - (vb_double_amplitude[channel] >> 1))
+ * vb_multiplier[channel];
+ if (freq_word < 0) { freq_word += 0x10000; }
+ if (freq_word > 0xFFFF) { freq_word -= 0x10000; }
+
+ freq.lo = freq_word & 0xFF;
+ freq.hi = (freq_word >> 8) & 0xFF;
+ set_adlib_freq_no_update(channel,freq);
+}
+
+
+void Cu6mPlayer::mf_slide(int channel)
+{
+ carrier_mf_mod_delay[channel]--;
+ if (carrier_mf_mod_delay[channel]==0)
+ {
+ carrier_mf_mod_delay[channel] = carrier_mf_mod_delay_backup[channel];
+ int current_mf = carrier_mf[channel] + carrier_mf_signed_delta[channel];
+ if (current_mf > 0x3F)
+ {
+ current_mf = 0x3F;
+ carrier_mf_signed_delta[channel] = 0;
+ }
+ else if (current_mf < 0)
+ {
+ current_mf = 0;
+ carrier_mf_signed_delta[channel] = 0;
+ }
+
+ set_carrier_mf(channel,(unsigned char)current_mf);
+ }
+}
+
+
+void Cu6mPlayer::out_adlib(unsigned char adlib_register, unsigned char adlib_data)
+{
+ opl->write(adlib_register,adlib_data);
+}
+
+
+void Cu6mPlayer::out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte)
+{
+ const unsigned char adlib_channel_to_carrier_offset[9] =
+ {0x03,0x04,0x05,0x0B,0x0C,0x0D,0x13,0x14,0x15};
+ const unsigned char adlib_channel_to_modulator_offset[9] =
+ {0x00,0x01,0x02,0x08,0x09,0x0A,0x10,0x11,0x12};
+
+ if (carrier)
+ {
+ out_adlib(adlib_register+adlib_channel_to_carrier_offset[channel],out_byte);
+ }
+ else
+ {
+ out_adlib(adlib_register+adlib_channel_to_modulator_offset[channel],out_byte);
+ }
+}
+
+
+// ============================================================================================
+//
+//
+// The Dictionary
+//
+//
+// ============================================================================================
+
+
+Cu6mPlayer::MyDict::MyDict()
+{
+ dict_size = default_dict_size;
+ dictionary = new dict_entry[dict_size-0x100]; // don't allocate space for the roots
+ contains = 0x102;
+}
+
+
+Cu6mPlayer::MyDict::MyDict(int max_size)
+{
+ dict_size = max_size;
+ dictionary = new dict_entry[dict_size-0x100]; // don't allocate space for the roots
+ contains = 0x102;
+}
+
+
+Cu6mPlayer::MyDict::~MyDict()
+{
+ delete [] dictionary;
+}
+
+// re-initializes the dictionary
+void Cu6mPlayer::MyDict::reset()
+{
+ contains = 0x102;
+}
+
+
+// Note: If the dictionary is already full, this function does nothing.
+void Cu6mPlayer::MyDict::add(unsigned char root, int codeword)
+{
+ if (contains < dict_size)
+ {
+ dictionary[contains-0x100].root = root;
+ dictionary[contains-0x100].codeword = codeword;
+ contains++;
+ }
+}
+
+
+unsigned char Cu6mPlayer::MyDict::get_root(int codeword)
+{
+ return (dictionary[codeword-0x100].root);
+}
+
+
+int Cu6mPlayer::MyDict::get_codeword(int codeword)
+{
+ return (dictionary[codeword-0x100].codeword);
+}
diff --git a/plugins/adplug/adplug/u6m.h b/plugins/adplug/adplug/u6m.h
new file mode 100644
index 00000000..30e7f3b5
--- /dev/null
+++ b/plugins/adplug/adplug/u6m.h
@@ -0,0 +1,168 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * u6m.h - Ultima 6 Music Player by Marc Winterrowd.
+ * This code extends the Adlib Winamp plug-in by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stack>
+
+#include "player.h"
+
+#define default_dict_size 4096 // because maximum codeword size == 12 bits
+#define max_codeword_length 12 // maximum codeword length in bits
+
+class Cu6mPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ Cu6mPlayer(Copl *newopl) : CPlayer(newopl), song_data(0)
+ {
+ };
+
+
+ ~Cu6mPlayer()
+ {
+ if(song_data) delete[] song_data;
+ };
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ {
+ return std::string("Ultima 6 Music");
+ };
+
+ protected:
+
+ struct byte_pair
+ {
+ unsigned char lo;
+ unsigned char hi;
+ };
+
+ struct subsong_info // information about a subsong
+ {
+ int continue_pos;
+ int subsong_repetitions;
+ int subsong_start;
+ };
+
+ struct dict_entry // dictionary entry
+ {
+ unsigned char root;
+ int codeword;
+ };
+
+ struct data_block //
+ {
+ long size;
+ unsigned char *data;
+ };
+
+ class MyDict
+ {
+ private:
+ // The actual number of dictionary entries allocated
+ // is (dictionary_size-256), because there are 256 roots
+ // that do not need to be stored.
+ int contains; // number of entries currently in the dictionary
+ int dict_size; // max number of entries that will fit into the dictionary
+ dict_entry* dictionary;
+
+ public:
+ MyDict(); // use dictionary size of 4096
+ MyDict(int); // let the caller specify a dictionary size
+ ~MyDict();
+ void reset(); // re-initializes the dictionary
+ void add(unsigned char, int);
+ unsigned char get_root(int);
+ int get_codeword(int);
+ };
+
+
+ // class variables
+ long played_ticks;
+
+ unsigned char* song_data; // the uncompressed .m file (the "song")
+ bool driver_active; // flag to prevent reentrancy
+ bool songend; // indicates song end
+ int song_pos; // current offset within the song
+ int loop_position; // position of the loop point
+ int read_delay; // delay (in timer ticks) before further song data is read
+ std::stack<subsong_info> subsong_stack;
+
+ int instrument_offsets[9]; // offsets of the adlib instrument data
+ // vibrato ("vb")
+ unsigned char vb_current_value[9];
+ unsigned char vb_double_amplitude[9];
+ unsigned char vb_multiplier[9];
+ unsigned char vb_direction_flag[9];
+ // mute factor ("mf") = not(volume)
+ unsigned char carrier_mf[9];
+ signed char carrier_mf_signed_delta[9];
+ unsigned char carrier_mf_mod_delay_backup[9];
+ unsigned char carrier_mf_mod_delay[9];
+ // frequency
+ byte_pair channel_freq[9]; // adlib freq settings for each channel
+ signed char channel_freq_signed_delta[9];
+
+ // protected functions used by update()
+ void command_loop();
+ unsigned char read_song_byte();
+ signed char read_signed_song_byte();
+ void dec_clip(int&);
+ byte_pair expand_freq_byte(unsigned char);
+ void set_adlib_freq(int channel,byte_pair freq_word);
+ void set_adlib_freq_no_update(int channel,byte_pair freq_word);
+ void set_carrier_mf(int channel,unsigned char mute_factor);
+ void set_modulator_mf(int channel,unsigned char mute_factor);
+ void freq_slide(int channel);
+ void vibrato(int channel);
+ void mf_slide(int channel);
+
+ void command_0(int channel);
+ void command_1(int channel);
+ void command_2(int channel);
+ void command_3(int channel);
+ void command_4(int channel);
+ void command_5(int channel);
+ void command_6(int channel);
+ void command_7(int channel);
+ void command_81();
+ void command_82();
+ void command_83();
+ void command_85();
+ void command_86();
+ void command_E();
+ void command_F();
+
+ void out_adlib(unsigned char adlib_register, unsigned char adlib_data);
+ void out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte);
+
+ // protected functions used by load()
+ bool lzw_decompress(data_block source, data_block dest);
+ int get_next_codeword (long& bits_read, unsigned char *source, int codeword_size);
+ void output_root(unsigned char root, unsigned char *destination, long& position);
+ void get_string(int codeword, MyDict& dictionary, std::stack<unsigned char>& root_stack);
+};
+
diff --git a/plugins/adplug/adplug/xad.cpp b/plugins/adplug/adplug/xad.cpp
new file mode 100644
index 00000000..81c7c0a7
--- /dev/null
+++ b/plugins/adplug/adplug/xad.cpp
@@ -0,0 +1,140 @@
+/*
+ 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
+
+ xad.cpp - XAD shell player by Riven the Mage <riven@ok.ru>
+*/
+
+#include "xad.h"
+#include "debug.h"
+
+/* -------- Public Methods -------------------------------- */
+
+CxadPlayer::CxadPlayer(Copl * newopl) : CPlayer(newopl)
+{
+ tune = 0;
+}
+
+CxadPlayer::~CxadPlayer()
+{
+ if (tune)
+ delete [] tune;
+}
+
+bool CxadPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ bool ret = false;
+
+ // load header
+ xad.id = f->readInt(4);
+ f->readString(xad.title, 36);
+ f->readString(xad.author, 36);
+ xad.fmt = f->readInt(2);
+ xad.speed = f->readInt(1);
+ xad.reserved_a = f->readInt(1);
+
+ // 'XAD!' - signed ?
+ if(xad.id != 0x21444158) { fp.close(f); return false; }
+
+ // get file size
+ tune_size = fp.filesize(f) - 80;
+
+ // load()
+ tune = new unsigned char [tune_size];
+ f->readString((char *)tune, tune_size);
+ fp.close(f);
+
+ ret = xadplayer_load();
+
+ if (ret)
+ rewind(0);
+
+ return ret;
+}
+
+void CxadPlayer::rewind(int subsong)
+{
+ opl->init();
+
+ plr.speed = xad.speed;
+ plr.speed_counter = 1;
+ plr.playing = 1;
+ plr.looping = 0;
+
+ // rewind()
+ xadplayer_rewind(subsong);
+
+#ifdef DEBUG
+ AdPlug_LogWrite("-----------\n");
+#endif
+}
+
+bool CxadPlayer::update()
+{
+ if (--plr.speed_counter)
+ goto update_end;
+
+ plr.speed_counter = plr.speed;
+
+ // update()
+ xadplayer_update();
+
+update_end:
+ return (plr.playing && (!plr.looping));
+}
+
+float CxadPlayer::getrefresh()
+{
+ return xadplayer_getrefresh();
+}
+
+std::string CxadPlayer::gettype()
+{
+ return xadplayer_gettype();
+}
+
+std::string CxadPlayer::gettitle()
+{
+ return xadplayer_gettitle();
+}
+
+std::string CxadPlayer::getauthor()
+{
+ return xadplayer_getauthor();
+}
+
+std::string CxadPlayer::getinstrument(unsigned int i)
+{
+ return xadplayer_getinstrument(i);
+}
+
+unsigned int CxadPlayer::getinstruments()
+{
+ return xadplayer_getinstruments();
+}
+
+/* -------- Protected Methods ------------------------------- */
+
+void CxadPlayer::opl_write(int reg, int val)
+{
+ adlib[reg] = val;
+#ifdef DEBUG
+ AdPlug_LogWrite("[ %02X ] = %02X\n",reg,val);
+#endif
+ opl->write(reg,val);
+}
diff --git a/plugins/adplug/adplug/xad.h b/plugins/adplug/adplug/xad.h
new file mode 100644
index 00000000..16872558
--- /dev/null
+++ b/plugins/adplug/adplug/xad.h
@@ -0,0 +1,97 @@
+/*
+ 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
+
+ xad.h - XAD shell player by Riven the Mage <riven@ok.ru>
+*/
+
+#ifndef H_ADPLUG_XAD
+#define H_ADPLUG_XAD
+
+#include "player.h"
+
+class CxadPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl);
+
+ CxadPlayer(Copl * newopl);
+ ~CxadPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype();
+ std::string gettitle();
+ std::string getauthor();
+ std::string getinstrument(unsigned int i);
+ unsigned int getinstruments();
+
+protected:
+ virtual void xadplayer_rewind(int subsong) = 0;
+ virtual bool xadplayer_load() = 0;
+ virtual void xadplayer_update() = 0;
+ virtual float xadplayer_getrefresh() = 0;
+ virtual std::string xadplayer_gettype() = 0;
+ virtual std::string xadplayer_gettitle()
+ {
+ return std::string(xad.title);
+ }
+ virtual std::string xadplayer_getauthor()
+ {
+ return std::string(xad.author);
+ }
+ virtual std::string xadplayer_getinstrument(unsigned int i)
+ {
+ return std::string("");
+ }
+ virtual unsigned int xadplayer_getinstruments()
+ {
+ return 0;
+ }
+
+ enum { HYP=1, PSI, FLASH, BMF, RAT, HYBRID };
+
+ struct xad_header
+ {
+ unsigned long id;
+ char title[36];
+ char author[36];
+ unsigned short fmt;
+ unsigned char speed;
+ unsigned char reserved_a;
+ } xad;
+
+ unsigned char * tune;
+ unsigned long tune_size;
+
+ struct
+ {
+ int playing;
+ int looping;
+ unsigned char speed;
+ unsigned char speed_counter;
+ } plr;
+
+ unsigned char adlib[256];
+
+ void opl_write(int reg, int val);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/xsm.cpp b/plugins/adplug/adplug/xsm.cpp
new file mode 100644
index 00000000..3c97de04
--- /dev/null
+++ b/plugins/adplug/adplug/xsm.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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
+ *
+ * xsm.cpp - eXtra Simple Music Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "xsm.h"
+
+CxsmPlayer::CxsmPlayer(Copl *newopl)
+ : CPlayer(newopl), music(0)
+{
+}
+
+CxsmPlayer::~CxsmPlayer()
+{
+ if(music) delete [] music;
+}
+
+bool CxsmPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ char id[6];
+ int i, j;
+
+ // check if header matches
+ f->readString(id, 6); songlen = f->readInt(2);
+ if(strncmp(id, "ofTAZ!", 6) || songlen > 3200) { fp.close(f); return false; }
+
+ // read and set instruments
+ for(i = 0; i < 9; i++) {
+ opl->write(0x20 + op_table[i], f->readInt(1));
+ opl->write(0x23 + op_table[i], f->readInt(1));
+ opl->write(0x40 + op_table[i], f->readInt(1));
+ opl->write(0x43 + op_table[i], f->readInt(1));
+ opl->write(0x60 + op_table[i], f->readInt(1));
+ opl->write(0x63 + op_table[i], f->readInt(1));
+ opl->write(0x80 + op_table[i], f->readInt(1));
+ opl->write(0x83 + op_table[i], f->readInt(1));
+ opl->write(0xe0 + op_table[i], f->readInt(1));
+ opl->write(0xe3 + op_table[i], f->readInt(1));
+ opl->write(0xc0 + op_table[i], f->readInt(1));
+ f->ignore(5);
+ }
+
+ // read song data
+ music = new char [songlen * 9];
+ for(i = 0; i < 9; i++)
+ for(j = 0; j < songlen; j++)
+ music[j * 9 + i] = f->readInt(1);
+
+ // success
+ fp.close(f);
+ rewind(0);
+ return true;
+}
+
+bool CxsmPlayer::update()
+{
+ int c;
+
+ if(notenum >= songlen) {
+ songend = true;
+ notenum = last = 0;
+ }
+
+ for(c = 0; c < 9; c++)
+ if(music[notenum * 9 + c] != music[last * 9 + c])
+ opl->write(0xb0 + c, 0);
+
+ for(c = 0; c < 9; c++) {
+ if(music[notenum * 9 + c])
+ play_note(c, music[notenum * 9 + c] % 12, music[notenum * 9 + c] / 12);
+ else
+ play_note(c, 0, 0);
+ }
+
+ last = notenum;
+ notenum++;
+ return !songend;
+}
+
+void CxsmPlayer::rewind(int subsong)
+{
+ notenum = last = 0;
+ songend = false;
+}
+
+float CxsmPlayer::getrefresh()
+{
+ return 5.0f;
+}
+
+void CxsmPlayer::play_note(int c, int note, int octv)
+{
+ int freq = note_table[note];
+
+ if(!note && !octv) freq = 0;
+ opl->write(0xa0 + c, freq & 0xff);
+ opl->write(0xb0 + c, (freq / 0xff) | 32 | (octv * 4));
+}
diff --git a/plugins/adplug/adplug/xsm.h b/plugins/adplug/adplug/xsm.h
new file mode 100644
index 00000000..15129606
--- /dev/null
+++ b/plugins/adplug/adplug/xsm.h
@@ -0,0 +1,46 @@
+/*
+ * 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
+ *
+ * xsm.h - eXtra Simple Music Player, by Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "player.h"
+
+class CxsmPlayer: public CPlayer
+{
+public:
+ static CPlayer *factory(Copl *newopl) { return new CxsmPlayer(newopl); }
+
+ CxsmPlayer(Copl *newopl);
+ ~CxsmPlayer();
+
+ bool load(const std::string &fn, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype() { return std::string("eXtra Simple Music"); }
+
+private:
+ unsigned short songlen;
+ char *music;
+ unsigned int last, notenum;
+ bool songend;
+
+ void play_note(int c, int note, int octv);
+};
diff --git a/plugins/adplug/libbinio/binfile.cpp b/plugins/adplug/libbinio/binfile.cpp
new file mode 100644
index 00000000..446f899f
--- /dev/null
+++ b/plugins/adplug/libbinio/binfile.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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
+ *
+ * binfile.h - Binary file I/O
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "binfile.h"
+
+/***** binfbase *****/
+
+binfbase::binfbase()
+ : f(NULL)
+{
+}
+
+binfbase::~binfbase()
+{
+ if(f != NULL) close();
+}
+
+void binfbase::close()
+{
+ if(f != NULL) {
+ if(fclose(f) == EOF) err |= Fatal; else f = NULL;
+ } else
+ err |= NotOpen;
+}
+
+void binfbase::seek(long pos, Offset offs)
+{
+ int error;
+
+ if(f == NULL) { err |= NotOpen; return; }
+
+ switch(offs) {
+ case Set: error = fseek(f, pos, SEEK_SET); break;
+ case Add: error = fseek(f, pos, SEEK_CUR); break;
+ case End: error = fseek(f, pos, SEEK_END); break;
+ }
+
+ if(error == -1) err |= Fatal;
+}
+
+long binfbase::pos()
+{
+ long pos;
+
+ if(f == NULL) { err |= NotOpen; return 0; }
+
+ pos = ftell(f);
+
+ if(pos == -1) {
+ err |= Fatal;
+ return 0;
+ } else
+ return pos;
+}
+
+/***** binifstream *****/
+
+binifstream::binifstream()
+{
+}
+
+binifstream::binifstream(const char *filename, const Mode mode)
+{
+ open(filename, mode);
+}
+
+#if BINIO_ENABLE_STRING
+binifstream::binifstream(const std::string &filename, const Mode mode)
+{
+ open(filename, mode);
+}
+#endif
+
+binifstream::~binifstream()
+{
+}
+
+void binifstream::open(const char *filename, const Mode mode)
+{
+ f = fopen(filename, "rb");
+
+ if(f == NULL)
+ switch(errno) {
+ case ENOENT: err |= NotFound; break;
+ case EACCES: err |= Denied; break;
+ default: err |= NotOpen; break;
+ }
+}
+
+#if BINIO_ENABLE_STRING
+void binifstream::open(const std::string &filename, const Mode mode)
+{
+ open(filename.c_str(), mode);
+}
+#endif
+
+binifstream::Byte binifstream::getByte()
+{
+ int read;
+
+ if(f != NULL) {
+ read = fgetc(f);
+ if(read == EOF) err |= Eof;
+ return (Byte)read;
+ } else {
+ err |= NotOpen;
+ return 0;
+ }
+}
+
+/***** binofstream *****/
+
+binofstream::binofstream()
+{
+}
+
+binofstream::binofstream(const char *filename, const Mode mode)
+{
+ open(filename, mode);
+}
+
+#if BINIO_ENABLE_STRING
+binofstream::binofstream(const std::string &filename, const Mode mode)
+{
+ open(filename, mode);
+}
+#endif
+
+binofstream::~binofstream()
+{
+}
+
+void binofstream::open(const char *filename, const Mode mode)
+{
+ char *modestr = "wb";
+
+ // Check if append mode is desired
+ if(mode & Append) modestr = "ab";
+
+ f = fopen(filename, modestr);
+
+ if(f == NULL)
+ switch(errno) {
+ case EEXIST:
+ case EACCES:
+ case EROFS:
+ err |= Denied;
+ break;
+ case ENOENT: err |= NotFound; break;
+ default: err |= NotOpen; break;
+ }
+}
+
+#if BINIO_ENABLE_STRING
+void binofstream::open(const std::string &filename, const Mode mode)
+{
+ open(filename.c_str(), mode);
+}
+#endif
+
+void binofstream::putByte(Byte b)
+{
+ if(f == NULL) { err |= NotOpen; return; }
+
+ if(fputc(b, f) == EOF)
+ err |= Fatal;
+}
+
+/***** binfstream *****/
+
+binfstream::binfstream()
+{
+}
+
+binfstream::binfstream(const char *filename, const Mode mode)
+{
+ open(filename, mode);
+}
+
+#if BINIO_ENABLE_STRING
+binfstream::binfstream(const std::string &filename, const Mode mode)
+{
+ open(filename, mode);
+}
+#endif
+
+binfstream::~binfstream()
+{
+}
+
+void binfstream::open(const char *filename, const Mode mode)
+{
+ char *modestr = "w+b"; // Create & at beginning
+ int ferror = 0;
+
+ // Apply desired mode
+ if(mode & NoCreate) {
+ if(!(mode & Append))
+ modestr[0] = 'r'; // NoCreate & at beginning
+ } else
+ if(mode & Append) // Create & append
+ modestr[0] = 'a';
+
+ f = fopen(filename, modestr);
+
+ // NoCreate & append (emulated -- not possible with standard C fopen())
+ if(f != NULL && (mode & Append) && (mode & NoCreate))
+ ferror = fseek(f, 0, SEEK_END);
+
+ if(f == NULL || ferror == -1) {
+ switch(errno) {
+ case EEXIST:
+ case EACCES:
+ case EROFS:
+ err |= Denied;
+ break;
+ case ENOENT: err |= NotFound; break;
+ default: err |= NotOpen; break;
+ }
+ }
+}
+
+#if BINIO_ENABLE_STRING
+void binfstream::open(const std::string &filename, const Mode mode)
+{
+ open(filename.c_str(), mode);
+}
+#endif
diff --git a/plugins/adplug/libbinio/binfile.h b/plugins/adplug/libbinio/binfile.h
new file mode 100644
index 00000000..ad24baee
--- /dev/null
+++ b/plugins/adplug/libbinio/binfile.h
@@ -0,0 +1,110 @@
+/*
+ * 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
+ *
+ * binfile.h - Binary file I/O
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINFILE
+#define H_BINIO_BINFILE
+
+#include <stdio.h>
+
+#include "binio.h"
+
+class binfbase: virtual public binio
+{
+public:
+ typedef enum {
+ Append = 1 << 0,
+ NoCreate = 1 << 1
+ } ModeFlags;
+
+ typedef int Mode;
+
+ binfbase();
+ virtual ~binfbase();
+
+ virtual void open(const char *filename, const Mode mode) = 0;
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode) = 0;
+#endif
+ void close();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ FILE *f;
+};
+
+class binifstream: public binistream, virtual public binfbase
+{
+public:
+ binifstream();
+ binifstream(const char *filename, const Mode mode = NoCreate);
+#if BINIO_ENABLE_STRING
+ binifstream(const std::string &filename, const Mode mode = NoCreate);
+#endif
+
+ virtual ~binifstream();
+
+ virtual void open(const char *filename, const Mode mode = NoCreate);
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode = NoCreate);
+#endif
+
+protected:
+ virtual Byte getByte();
+};
+
+class binofstream: public binostream, virtual public binfbase
+{
+public:
+ binofstream();
+ binofstream(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ binofstream(const std::string &filename, const Mode mode = 0);
+#endif
+
+ virtual ~binofstream();
+
+ virtual void open(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode = 0);
+#endif
+
+protected:
+ virtual void putByte(Byte b);
+};
+
+class binfstream: public binifstream, public binofstream
+{
+public:
+ binfstream();
+ binfstream(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ binfstream(const std::string &filename, const Mode mode = 0);
+#endif
+
+ virtual ~binfstream();
+
+ virtual void open(const char *filename, const Mode mode = 0);
+#if BINIO_ENABLE_STRING
+ virtual void open(const std::string &filename, const Mode mode = 0);
+#endif
+};
+
+#endif
diff --git a/plugins/adplug/libbinio/binio.cpp b/plugins/adplug/libbinio/binio.cpp
new file mode 100644
index 00000000..75489788
--- /dev/null
+++ b/plugins/adplug/libbinio/binio.cpp
@@ -0,0 +1,640 @@
+/*
+ * 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
+ *
+ * binio.cpp - Binary stream I/O classes
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "binio.h"
+
+#if BINIO_WITH_MATH
+
+#include <math.h>
+
+#ifdef __QNXNTO__
+ #define pow std::powf
+#endif // __QNXNTO__
+
+// If 'math.h' doesn't define HUGE_VAL, we try to use HUGE instead.
+#ifndef HUGE_VAL
+# define HUGE_VAL HUGE
+#endif
+
+#endif
+
+/***** Defines *****/
+
+#if BINIO_ENABLE_STRING
+// String buffer size for std::string readString() method
+#define STRINGBUFSIZE 256
+#endif
+
+/***** binio *****/
+
+const binio::Flags binio::system_flags = binio::detect_system_flags();
+
+const binio::Flags binio::detect_system_flags()
+{
+ Flags f = 0;
+
+ // Endian test
+ union {
+ int word;
+ Byte byte;
+ } endian_test;
+
+ endian_test.word = 1;
+ if(endian_test.byte != 1) f |= BigEndian;
+
+ // IEEE-754 floating-point test
+ float fl = 6.5;
+ Byte *dat = (Byte *)&fl;
+
+ if(sizeof(float) == 4 && sizeof(double) == 8)
+ if(f & BigEndian) {
+ if(dat[0] == 0x40 && dat[1] == 0xD0 && !dat[2] && !dat[3])
+ f |= FloatIEEE;
+ } else
+ if(dat[3] == 0x40 && dat[2] == 0xD0 && !dat[1] && !dat[0])
+ f |= FloatIEEE;
+
+ return f;
+}
+
+binio::binio()
+ : my_flags(system_flags), err(NoError)
+{
+}
+
+binio::~binio()
+{
+}
+
+void binio::setFlag(Flag f, bool set)
+{
+ if(set)
+ my_flags |= f;
+ else
+ my_flags &= !f;
+}
+
+bool binio::getFlag(Flag f)
+{
+ return (my_flags & f ? true : false);
+}
+
+binio::Error binio::error()
+{
+ Error e = err;
+
+ err = NoError;
+ return e;
+}
+
+bool binio::eof()
+{
+ return (err & Eof ? true : false);
+}
+
+/***** binistream *****/
+
+binistream::binistream()
+{
+}
+
+binistream::~binistream()
+{
+}
+
+binistream::Int binistream::readInt(unsigned int size)
+{
+ unsigned int i;
+ Int val = 0, in;
+
+ // Check if 'size' doesn't exceed our system's biggest type.
+ if(size > sizeof(Int)) {
+ err |= Unsupported;
+ return 0;
+ }
+
+ for(i = 0; i < size; i++) {
+ in = getByte();
+ if(getFlag(BigEndian))
+ val <<= 8;
+ else
+ in <<= i * 8;
+ val |= in;
+ }
+
+ return val;
+}
+
+binistream::Float binistream::readFloat(FType ft)
+{
+ if(getFlag(FloatIEEE)) { // Read IEEE-754 floating-point value
+ unsigned int i, size;
+ Byte in[8];
+ bool swap;
+
+ // Determine appropriate size for given type.
+ switch(ft) {
+ case Single: size = 4; break; // 32 bits
+ case Double: size = 8; break; // 64 bits
+ }
+
+ // Determine byte ordering, depending on what we do next
+ if(system_flags & FloatIEEE)
+ swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
+ else
+ swap = !getFlag(BigEndian);
+
+ // Read the float byte by byte, converting endianess
+ for(i = 0; i < size; i++)
+ if(swap)
+ in[size - i - 1] = getByte();
+ else
+ in[i] = getByte();
+
+ if(system_flags & FloatIEEE) {
+ // Compatible system, let the hardware do the conversion
+ switch(ft) {
+ case Single: return *(float *)in;
+ case Double: return *(double *)in;
+ }
+ } else { // Incompatible system, convert manually
+ switch(ft) {
+ case Single: return ieee_single2float(in);
+ case Double: return ieee_double2float(in);
+ }
+ }
+ }
+
+ // User tried to read a (yet) unsupported floating-point type. Bail out.
+ err |= Unsupported; return 0.0;
+}
+
+binistream::Float binistream::ieee_single2float(Byte *data)
+{
+ signed int sign = data[0] >> 7 ? -1 : 1;
+ unsigned int exp = ((data[0] << 1) & 0xff) | ((data[1] >> 7) & 1),
+ fracthi7 = data[1] & 0x7f;
+ Float fract = fracthi7 * 65536.0 + data[2] * 256.0 + data[3];
+
+ // Signed and unsigned zero
+ if(!exp && !fracthi7 && !data[2] && !data[3]) return sign * 0.0;
+
+ // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
+ if(exp == 255)
+ if(!fracthi7 && !data[2] && !data[3]) {
+#ifdef HUGE_VAL
+ if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
+#else
+ err |= Unsupported;
+ if(sign == -1) return -1.0; else return 1.0;
+#endif
+ } else { // Not a number (maybe unsupported on non-IEEE systems)
+#ifdef NAN
+ return NAN;
+#else
+ err |= Unsupported; return 0.0;
+#endif
+ }
+
+ if(!exp) // Unnormalized float values
+ return sign * pow(2, -126) * fract * pow(2, -23);
+ else // Normalized float values
+ return sign * pow(2, exp - 127) * (fract * pow(2, -23) + 1);
+
+ err |= Fatal; return 0.0;
+}
+
+binistream::Float binistream::ieee_double2float(Byte *data)
+{
+ signed int sign = data[0] >> 7 ? -1 : 1;
+ unsigned int exp = ((unsigned int)(data[0] & 0x7f) << 4) | (data[1] >> 4),
+ fracthi4 = data[1] & 0xf;
+ Float fract = fracthi4 * pow(2, 48) + data[2] * pow(2, 40) + data[3] *
+ pow(2, 32) + data[4] * pow(2, 24) + data[5] * pow(2, 16) + data[6] *
+ pow(2, 8) + data[7];
+
+ // Signed and unsigned zero
+ if(!exp && !fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] &&
+ !data[6] && !data[7]) return sign * 0.0;
+
+ // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
+ if(exp == 2047)
+ if(!fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] && !data[6] &&
+ !data[7]) {
+#ifdef HUGE_VAL
+ if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
+#else
+ err |= Unsupported;
+ if(sign == -1) return -1.0; else return 1.0;
+#endif
+ } else { // Not a number (maybe unsupported on non-IEEE systems)
+#ifdef NAN
+ return NAN;
+#else
+ err |= Unsupported; return 0.0;
+#endif
+ }
+
+ if(!exp) // Unnormalized float values
+ return sign * pow(2, -1022) * fract * pow(2, -52);
+ else // Normalized float values
+ return sign * pow(2, exp - 1023) * (fract * pow(2, -52) + 1);
+
+ err |= Fatal; return 0.0;
+}
+
+#if !BINIO_WITH_MATH
+binio::Float binio::pow(Float base, signed int exp)
+/* Our own, stripped-down version of pow() for not having to depend on 'math.h'.
+ * This one handles float values for the base and an integer exponent, both
+ * positive and negative.
+ */
+{
+ int i;
+ Float val = base;
+
+ if(!exp) return 1.0;
+
+ for(i = 1; i < (exp < 0 ? -exp : exp); i++)
+ val *= base;
+
+ if(exp < 0) val = 1.0 / val;
+
+ return val;
+}
+#endif
+
+unsigned long binistream::readString(char *str, unsigned long maxlen)
+{
+ unsigned long i;
+
+ for(i = 0; i < maxlen; i++) {
+ str[i] = (char)getByte();
+ if(err) { str[i] = '\0'; return i; }
+ }
+
+ return maxlen;
+}
+
+unsigned long binistream::readString(char *str, unsigned long maxlen,
+ const char delim)
+{
+ unsigned long i;
+
+ for(i = 0; i < maxlen; i++) {
+ str[i] = (char)getByte();
+ if(str[i] == delim || err) { str[i] = '\0'; return i; }
+ }
+
+ str[maxlen] = '\0';
+ return maxlen;
+}
+
+#if BINIO_ENABLE_STRING
+std::string binistream::readString(const char delim)
+{
+ char buf[STRINGBUFSIZE + 1];
+ std::string tempstr;
+ unsigned long read;
+
+ do {
+ read = readString(buf, STRINGBUFSIZE, delim);
+ tempstr.append(buf, read);
+ } while(read == STRINGBUFSIZE);
+
+ return tempstr;
+}
+#endif
+
+binistream::Int binistream::peekInt(unsigned int size)
+{
+ Int val = readInt(size);
+ if(!err) seek(-(long)size, Add);
+ return val;
+}
+
+binistream::Float binistream::peekFloat(FType ft)
+{
+ Float val = readFloat(ft);
+
+ if(!err)
+ switch(ft) {
+ case Single: seek(-4, Add); break;
+ case Double: seek(-8, Add); break;
+ }
+
+ return val;
+}
+
+bool binistream::ateof()
+{
+ Error olderr = err; // Save current error state
+ bool eof_then;
+
+ peekInt(1);
+ eof_then = eof(); // Get error state of next byte
+ err = olderr; // Restore original error state
+ return eof_then;
+}
+
+void binistream::ignore(unsigned long amount)
+{
+ unsigned long i;
+
+ for(i = 0; i < amount; i++)
+ getByte();
+}
+
+/***** binostream *****/
+
+binostream::binostream()
+{
+}
+
+binostream::~binostream()
+{
+}
+
+void binostream::writeInt(Int val, unsigned int size)
+{
+ unsigned int i;
+
+ // Check if 'size' doesn't exceed our system's biggest type.
+ if(size > sizeof(Int)) { err |= Unsupported; return; }
+
+ for(i = 0; i < size; i++) {
+ if(getFlag(BigEndian))
+ putByte((val >> (size - i - 1) * 8) & 0xff);
+ else {
+ putByte(val & 0xff);
+ val >>= 8;
+ }
+ }
+}
+
+void binostream::writeFloat(Float f, FType ft)
+{
+ if(getFlag(FloatIEEE)) { // Write IEEE-754 floating-point value
+ unsigned int i, size;
+ Byte *out;
+ bool swap;
+
+ if(system_flags & FloatIEEE) {
+ // compatible system, let the hardware do the conversion
+ float outf = f;
+ double outd = f;
+
+ // Hardware could be big or little endian, convert appropriately
+ swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
+
+ // Determine appropriate size for given type and convert by hardware
+ switch(ft) {
+ case Single: size = 4; out = (Byte *)&outf; break; // 32 bits
+ case Double: size = 8; out = (Byte *)&outd; break; // 64 bits
+ }
+ } else {
+#if BINIO_WITH_MATH
+ // incompatible system, do the conversion manually
+ Byte buf[8];
+
+ // Our own value is always big endian, just check whether we have to
+ // convert for a different stream format.
+ swap = !getFlag(BigEndian);
+
+ // Convert system's float to requested IEEE-754 float
+ switch(ft) {
+ case Single: size = 4; float2ieee_single(f, buf); break;
+ case Double: size = 8; float2ieee_double(f, buf); break;
+ }
+
+ out = buf; // Make the value ready for writing
+#else
+ // No necessary support routines to do the conversion, bail out!
+ err |= Unsupported; return;
+#endif
+ }
+
+ // Write the float byte by byte, converting endianess
+ if(swap) out += size - 1;
+ for(i = 0; i < size; i++) {
+ putByte(*out);
+ if(swap) out--; else out++;
+ }
+
+ return; // We're done.
+ }
+
+ // User tried to write an unsupported floating-point type. Bail out.
+ err |= Unsupported;
+}
+
+#ifdef BINIO_WITH_MATH
+
+/*
+ * Single and double floating-point to IEEE-754 equivalent conversion functions
+ * courtesy of Ken Turkowski.
+ *
+ * Copyright (C) 1989-1991 Ken Turkowski. <turk@computer.org>
+ *
+ * All rights reserved.
+ *
+ * Warranty Information
+ * Even though I have reviewed this software, I make no warranty
+ * or representation, either express or implied, with respect to this
+ * software, its quality, accuracy, merchantability, or fitness for a
+ * particular purpose. As a result, this software is provided "as is,"
+ * and you, its user, are assuming the entire risk as to its quality
+ * and accuracy.
+ *
+ * This code may be used and freely distributed as long as it includes
+ * this copyright notice and the above warranty information.
+ */
+
+/****************************************************************
+ * The following two routines make up for deficiencies in many
+ * compilers to convert properly between unsigned integers and
+ * floating-point. Some compilers which have this bug are the
+ * THINK_C compiler for the Macintosh and the C compiler for the
+ * Silicon Graphics MIPS-based Iris.
+ ****************************************************************/
+
+#ifdef applec /* The Apple C compiler works */
+# define FloatToUnsigned(f) ((unsigned long)(f))
+#else
+# define FloatToUnsigned(f) ((unsigned long)(((long)((f) - 2147483648.0)) + 2147483647L + 1))
+#endif
+
+#define SEXP_MAX 255
+#define SEXP_OFFSET 127
+#define SEXP_SIZE 8
+#define SEXP_POSITION (32-SEXP_SIZE-1)
+
+void binostream::float2ieee_single(Float num, Byte *bytes)
+{
+ long sign;
+ register long bits;
+
+ if (num < 0) { /* Can't distinguish a negative zero */
+ sign = 0x80000000;
+ num *= -1;
+ } else {
+ sign = 0;
+ }
+
+ if (num == 0) {
+ bits = 0;
+ } else {
+ Float fMant;
+ int expon;
+
+ fMant = frexp(num, &expon);
+
+ if ((expon > (SEXP_MAX-SEXP_OFFSET+1)) || !(fMant < 1)) {
+ /* NaN's and infinities fail second test */
+ bits = sign | 0x7F800000; /* +/- infinity */
+ }
+
+ else {
+ long mantissa;
+
+ if (expon < -(SEXP_OFFSET-2)) { /* Smaller than normalized */
+ int shift = (SEXP_POSITION+1) + (SEXP_OFFSET-2) + expon;
+ if (shift < 0) { /* Way too small: flush to zero */
+ bits = sign;
+ }
+ else { /* Nonzero denormalized number */
+ mantissa = (long)(fMant * (1L << shift));
+ bits = sign | mantissa;
+ }
+ }
+
+ else { /* Normalized number */
+ mantissa = (long)floor(fMant * (1L << (SEXP_POSITION+1)));
+ mantissa -= (1L << SEXP_POSITION); /* Hide MSB */
+ bits = sign | ((long)((expon + SEXP_OFFSET - 1)) << SEXP_POSITION) | mantissa;
+ }
+ }
+ }
+
+ bytes[0] = bits >> 24; /* Copy to byte string */
+ bytes[1] = bits >> 16;
+ bytes[2] = bits >> 8;
+ bytes[3] = bits;
+}
+
+#define DEXP_MAX 2047
+#define DEXP_OFFSET 1023
+#define DEXP_SIZE 11
+#define DEXP_POSITION (32-DEXP_SIZE-1)
+
+void binostream::float2ieee_double(Float num, Byte *bytes)
+{
+ long sign;
+ long first, second;
+
+ if (num < 0) { /* Can't distinguish a negative zero */
+ sign = 0x80000000;
+ num *= -1;
+ } else {
+ sign = 0;
+ }
+
+ if (num == 0) {
+ first = 0;
+ second = 0;
+ } else {
+ Float fMant, fsMant;
+ int expon;
+
+ fMant = frexp(num, &expon);
+
+ if ((expon > (DEXP_MAX-DEXP_OFFSET+1)) || !(fMant < 1)) {
+ /* NaN's and infinities fail second test */
+ first = sign | 0x7FF00000; /* +/- infinity */
+ second = 0;
+ }
+
+ else {
+ long mantissa;
+
+ if (expon < -(DEXP_OFFSET-2)) { /* Smaller than normalized */
+ int shift = (DEXP_POSITION+1) + (DEXP_OFFSET-2) + expon;
+ if (shift < 0) { /* Too small for something in the MS word */
+ first = sign;
+ shift += 32;
+ if (shift < 0) { /* Way too small: flush to zero */
+ second = 0;
+ }
+ else { /* Pretty small demorn */
+ second = FloatToUnsigned(floor(ldexp(fMant, shift)));
+ }
+ }
+ else { /* Nonzero denormalized number */
+ fsMant = ldexp(fMant, shift);
+ mantissa = (long)floor(fsMant);
+ first = sign | mantissa;
+ second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
+ }
+ }
+
+ else { /* Normalized number */
+ fsMant = ldexp(fMant, DEXP_POSITION+1);
+ mantissa = (long)floor(fsMant);
+ mantissa -= (1L << DEXP_POSITION); /* Hide MSB */
+ fsMant -= (1L << DEXP_POSITION);
+ first = sign | ((long)((expon + DEXP_OFFSET - 1)) << DEXP_POSITION) | mantissa;
+ second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
+ }
+ }
+ }
+
+ bytes[0] = first >> 24;
+ bytes[1] = first >> 16;
+ bytes[2] = first >> 8;
+ bytes[3] = first;
+ bytes[4] = second >> 24;
+ bytes[5] = second >> 16;
+ bytes[6] = second >> 8;
+ bytes[7] = second;
+}
+
+#endif // BINIO_WITH_MATH
+
+unsigned long binostream::writeString(const char *str, unsigned long amount)
+{
+ unsigned int i;
+
+ if(!amount) amount = strlen(str);
+
+ for(i = 0; i < amount; i++) {
+ putByte(str[i]);
+ if(err) return i;
+ }
+
+ return amount;
+}
+
+#if BINIO_ENABLE_STRING
+unsigned long binostream::writeString(const std::string &str)
+{
+ return writeString(str.c_str());
+}
+#endif
diff --git a/plugins/adplug/libbinio/binio.h b/plugins/adplug/libbinio/binio.h
new file mode 100644
index 00000000..31bcfac6
--- /dev/null
+++ b/plugins/adplug/libbinio/binio.h
@@ -0,0 +1,175 @@
+/* -*-C++-*-
+ * 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
+ *
+ * binio.h - Binary stream I/O classes
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINIO
+#define H_BINIO_BINIO
+
+/***** Configuration *****/
+
+// BINIO_ENABLE_STRING - Build std::string supporting methods
+//
+// Set to 1 to build std::string supporting methods. You need the STL to
+// do this.
+#define BINIO_ENABLE_STRING 1
+
+// BINIO_ENABLE_IOSTREAM - Build iostream wrapper classes
+//
+// Set to 1 to build the iostream wrapper classes. You need the standard
+// C++ library to do this.
+#define BINIO_ENABLE_IOSTREAM 1
+
+// BINIO_ISO_STDLIB - Build with ISO C++ standard library compliance
+//
+// Set to 1 to build for the ISO standard C++ library (i.e. namespaces, STL and
+// templatized iostream). Set to 0 to build for the traditional C++ library.
+#define BINIO_ISO_STDLIB 1
+
+// BINIO_WITH_MATH - Build with 'math.h' dependency to allow float conversions
+//
+// Set to 1 to also build routines that depend on the 'math.h' standard C header
+// file (this sometimes also implies a 'libm' or 'libmath' dependency). These
+// routines are needed in order to write IEEE-754 floating-point numbers on a
+// system that doesn't support this format natively. For only reading these
+// numbers, however, these routines are not needed. If set to 0, writing
+// IEEE-754 numbers on an incompatible system will be disabled.
+#define BINIO_WITH_MATH 1
+
+/***** Implementation *****/
+
+// Disable annoying multiple inheritance compiler warning on MSVC6
+#ifdef _MSC_VER
+# pragma warning(disable: 4250)
+#endif
+
+#if BINIO_ENABLE_STRING
+#include <string>
+#endif
+
+class binio
+{
+public:
+ typedef enum {
+ BigEndian = 1 << 0,
+ FloatIEEE = 1 << 1
+ } Flag;
+
+ typedef enum {
+ NoError = 0,
+ Fatal = 1 << 0,
+ Unsupported = 1 << 1,
+ NotOpen = 1 << 2,
+ Denied = 1 << 3,
+ NotFound = 1 << 4,
+ Eof = 1 << 5
+ } ErrorCode;
+
+ typedef enum { Set, Add, End } Offset;
+ typedef enum { Single, Double } FType;
+ typedef int Error;
+
+ binio();
+ virtual ~binio();
+
+ void setFlag(Flag f, bool set = true);
+ bool getFlag(Flag f);
+
+ Error error();
+ bool eof();
+
+ virtual void seek(long, Offset = Set) = 0;
+ virtual long pos() = 0;
+
+protected:
+ typedef long long Int;
+ typedef long double Float;
+ typedef unsigned char Byte; // has to be unsigned!
+
+ typedef int Flags;
+
+ Flags my_flags;
+ static const Flags system_flags;
+ Error err;
+
+ // Some math.h emulation functions...
+#if !BINIO_WITH_MATH
+ Float pow(Float base, signed int exp);
+ Float ldexp(Float x, signed int exp) { return x * pow(2, exp); }
+#endif
+
+private:
+ static const Flags detect_system_flags();
+};
+
+class binistream: virtual public binio
+{
+public:
+ binistream();
+ virtual ~binistream();
+
+ Int readInt(unsigned int size);
+ Float readFloat(FType ft);
+ unsigned long readString(char *str, unsigned long amount);
+ unsigned long readString(char *str, unsigned long maxlen, const char delim);
+#if BINIO_ENABLE_STRING
+ std::string readString(const char delim = '\0');
+#endif
+
+ Int peekInt(unsigned int size);
+ Float peekFloat(FType ft);
+
+ bool ateof();
+ void ignore(unsigned long amount = 1);
+
+protected:
+ virtual Byte getByte() = 0;
+
+private:
+ Float ieee_single2float(Byte *data);
+ Float ieee_double2float(Byte *data);
+};
+
+class binostream: virtual public binio
+{
+public:
+ binostream();
+ virtual ~binostream();
+
+ void writeInt(Int val, unsigned int size);
+ void writeFloat(Float f, FType ft);
+ unsigned long writeString(const char *str, unsigned long amount = 0);
+#if BINIO_ENABLE_STRING
+ unsigned long writeString(const std::string &str);
+#endif
+
+protected:
+ virtual void putByte(Byte) = 0;
+
+private:
+ void float2ieee_single(Float f, Byte *data);
+ void float2ieee_double(Float f, Byte *data);
+};
+
+class binstream: public binistream, public binostream
+{
+public:
+ binstream();
+ virtual ~binstream();
+};
+
+#endif
diff --git a/plugins/adplug/libbinio/binstr.cpp b/plugins/adplug/libbinio/binstr.cpp
new file mode 100644
index 00000000..0e3a9f86
--- /dev/null
+++ b/plugins/adplug/libbinio/binstr.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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
+ *
+ * binstr.cpp - Binary I/O on standard C strings in memory
+ * Copyright (C) 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include "binstr.h"
+
+/***** binsbase *****/
+
+binsbase::binsbase(void *str, unsigned long len)
+ : data((Byte *)str), spos((Byte *)str), length(len)
+{
+}
+
+binsbase::~binsbase()
+{
+}
+
+void binsbase::seek(long p, Offset offs)
+{
+ switch(offs) {
+ case Set: spos = data + p; break;
+ case Add: spos += p; break;
+ case End: spos = data + length - 1 + p; break;
+ }
+
+ // Seek before start of data
+ if(spos < data) {
+ err |= Eof;
+ spos = data;
+ return;
+ }
+
+ // Seek after end of data
+ if(spos - data >= length) {
+ err |= Eof;
+ spos = data + length - 1;
+ }
+}
+
+long binsbase::pos()
+{
+ return (long)(spos - data);
+}
+
+/***** binisstream *****/
+
+binisstream::binisstream(void *str, unsigned long len)
+ : binsbase(str, len)
+{
+}
+
+binisstream::~binisstream()
+{
+}
+
+binisstream::Byte binisstream::getByte()
+{
+ Byte in = 0;
+
+ if(spos - data >= length)
+ err |= Eof;
+ else {
+ in = *spos;
+ spos++;
+ }
+
+ return in;
+}
+
+/***** binosstream *****/
+
+binosstream::binosstream(void *str, unsigned long len)
+ : binsbase(str, len)
+{
+}
+
+binosstream::~binosstream()
+{
+}
+
+void binosstream::putByte(Byte b)
+{
+ *spos = b;
+ spos++;
+
+ if(spos - data >= length)
+ spos = data + length - 1;
+}
+
+/***** binsstream *****/
+
+binsstream::binsstream(void *str, unsigned long len)
+ : binsbase(str, len), binisstream(str, len), binosstream(str, len)
+{
+}
+
+binsstream::~binsstream()
+{
+}
diff --git a/plugins/adplug/libbinio/binstr.h b/plugins/adplug/libbinio/binstr.h
new file mode 100644
index 00000000..33e4b12c
--- /dev/null
+++ b/plugins/adplug/libbinio/binstr.h
@@ -0,0 +1,66 @@
+/*
+ * 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
+ *
+ * binstr.h - Binary I/O on standard C strings in memory
+ * Copyright (C) 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINSTR
+#define H_BINIO_BINSTR
+
+#include "binio.h"
+
+class binsbase: virtual public binio
+{
+public:
+ binsbase(void *str, unsigned long len);
+ virtual ~binsbase();
+
+ virtual void seek(long p, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ Byte *data, *spos;
+ long length;
+};
+
+class binisstream: public binistream, virtual public binsbase
+{
+public:
+ binisstream(void *str, unsigned long len);
+ virtual ~binisstream();
+
+protected:
+ virtual Byte getByte();
+};
+
+class binosstream: public binostream, virtual public binsbase
+{
+public:
+ binosstream(void *str, unsigned long len);
+ virtual ~binosstream();
+
+protected:
+ virtual void putByte(Byte b);
+};
+
+class binsstream: public binisstream, public binosstream
+{
+public:
+ binsstream(void *str, unsigned long len);
+ virtual ~binsstream();
+};
+
+#endif
diff --git a/plugins/adplug/libbinio/binwrap.cpp b/plugins/adplug/libbinio/binwrap.cpp
new file mode 100644
index 00000000..740ee982
--- /dev/null
+++ b/plugins/adplug/libbinio/binwrap.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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
+ *
+ * binwrap.cpp - Binary I/O wrapper, using standard iostreams library
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <stdio.h>
+#include "binwrap.h"
+
+#if BINIO_ENABLE_IOSTREAM
+
+/***** biniwstream *****/
+
+biniwstream::biniwstream(istream *istr)
+ : in(istr)
+{
+}
+
+biniwstream::~biniwstream()
+{
+}
+
+void biniwstream::seek(long pos, Offset offs)
+{
+ if(!in) { err = NotOpen; return; }
+
+ switch(offs) {
+ case Set: in->seekg(pos, ios::beg); break;
+ case Add: in->seekg(pos, ios::cur); break;
+ case End: in->seekg(pos, ios::end); break;
+ }
+}
+
+biniwstream::Byte biniwstream::getByte()
+{
+ if(!in) { err = NotOpen; return 0; }
+
+ int i = in->get();
+ if(i == EOF) err |= Eof;
+ return (Byte)i;
+}
+
+long biniwstream::pos()
+{
+ if(!in) { err = NotOpen; return 0; }
+ return (long)in->tellg();
+}
+
+/***** binowstream *****/
+
+binowstream::binowstream(ostream *ostr)
+ : out(ostr)
+{
+}
+
+binowstream::~binowstream()
+{
+}
+
+void binowstream::seek(long pos, Offset offs)
+{
+ if(!out) { err = NotOpen; return; }
+
+ switch(offs) {
+ case Set: out->seekp(pos, ios::beg); break;
+ case Add: out->seekp(pos, ios::cur); break;
+ case End: out->seekp(pos, ios::end); break;
+ }
+}
+
+void binowstream::putByte(binio::Byte b)
+{
+ if(!out) { err = NotOpen; return; }
+ out->put((char)b);
+}
+
+long binowstream::pos()
+{
+ if(!out) { err = NotOpen; return 0; }
+ return (long)out->tellp();
+}
+
+/***** binwstream *****/
+
+binwstream::binwstream(iostream *str)
+ : biniwstream(str), binowstream(str), io(str)
+{
+}
+
+binwstream::~binwstream()
+{
+}
+
+void binwstream::seek(long pos, Offset offs)
+{
+ biniwstream::seek(pos, offs);
+ binowstream::seek(pos, offs);
+}
+
+long binwstream::pos()
+{
+ if(!io) { err = NotOpen; return 0; }
+ return (long)io->tellg();
+}
+
+binwstream::Byte binwstream::getByte()
+{
+ Byte in = biniwstream::getByte();
+ binowstream::seek(biniwstream::pos(), Set); // sync stream position
+ return in;
+}
+
+void binwstream::putByte(Byte b)
+{
+ binowstream::putByte(b);
+ biniwstream::seek(binowstream::pos(), Set); // sync stream position
+}
+
+#endif
diff --git a/plugins/adplug/libbinio/binwrap.h b/plugins/adplug/libbinio/binwrap.h
new file mode 100644
index 00000000..74b009f6
--- /dev/null
+++ b/plugins/adplug/libbinio/binwrap.h
@@ -0,0 +1,92 @@
+/*
+ * 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
+ *
+ * binwrap.h - Binary I/O wrapper, using standard iostreams library
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#ifndef H_BINIO_BINWRAP
+#define H_BINIO_BINWRAP
+
+#include "binio.h"
+
+#if BINIO_ENABLE_IOSTREAM
+
+#if BINIO_ISO_STDLIB
+#include <iostream>
+
+using std::iostream;
+using std::ostream;
+using std::istream;
+using std::ios;
+using std::streambuf;
+#else
+
+#include <iostream.h>
+
+#endif
+
+class biniwstream: public binistream
+{
+public:
+ biniwstream(istream *istr);
+ virtual ~biniwstream();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ virtual Byte getByte();
+
+private:
+ istream *in;
+};
+
+class binowstream: public binostream
+{
+public:
+ binowstream(ostream *ostr);
+ virtual ~binowstream();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ virtual void putByte(Byte b);
+
+private:
+ ostream *out;
+};
+
+class binwstream: public biniwstream, public binowstream
+{
+public:
+ binwstream(iostream *str);
+ virtual ~binwstream();
+
+ virtual void seek(long pos, Offset offs = Set);
+ virtual long pos();
+
+protected:
+ virtual Byte getByte();
+ virtual void putByte(Byte b);
+
+private:
+ iostream *io;
+};
+
+#endif
+
+#endif
diff --git a/plugins/adplug/plugin.c b/plugins/adplug/plugin.c
new file mode 100644
index 00000000..34e5d6d1
--- /dev/null
+++ b/plugins/adplug/plugin.c
@@ -0,0 +1,68 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009 Alexey Yakovenko
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+// this is a decoder plugin skeleton
+// use to create new decoder plugins
+
+#include "../../deadbeef.h"
+
+extern const char *adplug_exts[];
+extern const char *adplug_filetypes[];
+
+int
+adplug_init (DB_playItem_t *it);
+void
+adplug_free (void);
+int
+adplug_read_int16 (char *bytes, int size);
+int
+adplug_seek_sample (int sample);
+int
+adplug_seek (float time);
+DB_playItem_t *
+adplug_insert (DB_playItem_t *after, const char *fname);
+int
+adplug_start (void);
+int
+adplug_stop (void);
+
+// define plugin interface
+DB_decoder_t adplug_plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.name = "short plugin name",
+ .plugin.descr = "plugin description",
+ .plugin.author = "author name",
+ .plugin.email = "author email",
+ .plugin.website = "author/plugin website",
+ .plugin.start = adplug_start,
+ .plugin.stop = adplug_stop,
+ .init = adplug_init,
+ .free = adplug_free,
+ .read_int16 = adplug_read_int16,
+ .seek = adplug_seek,
+ .seek_sample = adplug_seek_sample,
+ .insert = adplug_insert,
+ .exts = adplug_exts,
+ .id = "adplug",
+ .filetypes = adplug_filetypes
+};
+