summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2017-05-26 21:51:04 -0700
committerVito Caputo <vcaputo@pengaru.com>2017-05-26 22:48:09 -0700
commit78f8fce7f286fd0c71774e2567404ed51f24fef3 (patch)
treef3de4987f7a9fc1bc03331e97b65a851b041051a /src
*: initial commit of stripped schism stuff
Forking schism tracker's IT playback stuff into a little playback library for embedding in demos.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am126
-rw-r--r--src/fmt/compression.c256
-rw-r--r--src/fmt/it.c772
-rw-r--r--src/include/cmixer.h56
-rw-r--r--src/include/disko.h136
-rw-r--r--src/include/dmoz.h211
-rw-r--r--src/include/event.h46
-rw-r--r--src/include/fmopl.h122
-rw-r--r--src/include/fmt-types.h153
-rw-r--r--src/include/fmt.h175
-rw-r--r--src/include/headers.h250
-rw-r--r--src/include/it_defs.h128
-rw-r--r--src/include/log.h59
-rw-r--r--src/include/midi.h162
-rw-r--r--src/include/osdefs.h160
-rw-r--r--src/include/precomp_lut.h327
-rw-r--r--src/include/slurp.h81
-rw-r--r--src/include/snd_fm.h201
-rw-r--r--src/include/sndfile.h708
-rw-r--r--src/include/tables.h56
-rw-r--r--src/include/util.h200
-rw-r--r--src/player/csndfile.c1094
-rw-r--r--src/player/effects.c2070
-rw-r--r--src/player/equalizer.c255
-rw-r--r--src/player/filters.c116
-rw-r--r--src/player/fmopl.c2396
-rw-r--r--src/player/fmpatches.c179
-rw-r--r--src/player/mixer.c1555
-rw-r--r--src/player/mixutil.c258
-rw-r--r--src/player/opl-util.c134
-rw-r--r--src/player/snd_fm.c385
-rw-r--r--src/player/sndmix.c1259
-rw-r--r--src/player/tables.c443
-rw-r--r--src/sys/alsa/init.c73
-rw-r--r--src/sys/alsa/midi-alsa.c481
-rw-r--r--src/sys/alsa/volume-alsa.c237
-rw-r--r--src/sys/fd.org/autopackage.apspec57
-rw-r--r--src/sys/fd.org/itf.desktop11
-rw-r--r--src/sys/fd.org/schism.desktop18
-rw-r--r--src/sys/macosx/Schism_Tracker.app/Contents/Info.plist112
-rw-r--r--src/sys/macosx/Schism_Tracker.app/Contents/PkgInfo1
-rw-r--r--src/sys/macosx/Schism_Tracker.app/Contents/Resources/AppSettings.plist18
-rw-r--r--src/sys/macosx/Schism_Tracker.app/Contents/Resources/appIcon.icnsbin0 -> 52550 bytes
-rw-r--r--src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icnsbin0 -> 58109 bytes
-rw-r--r--src/sys/macosx/ibook-support.c103
-rw-r--r--src/sys/macosx/macosx-sdlmain.m619
-rw-r--r--src/sys/macosx/midi-macosx.c222
-rw-r--r--src/sys/macosx/osdefs.c54
-rw-r--r--src/sys/macosx/volume-macosx.c119
-rw-r--r--src/sys/oss/midi-oss.c177
-rw-r--r--src/sys/oss/volume-oss.c126
-rw-r--r--src/sys/posix/slurp-mmap.c68
-rw-r--r--src/sys/sdl/README2
-rw-r--r--src/sys/stdlib/asprintf.c34
-rw-r--r--src/sys/stdlib/memcmp.c12
-rw-r--r--src/sys/stdlib/mkstemp.c104
-rw-r--r--src/sys/stdlib/strptime.c396
-rw-r--r--src/sys/stdlib/vasprintf.c97
-rw-r--r--src/sys/wii/certs_bin.h163
-rw-r--r--src/sys/wii/data/certs.binbin0 -> 2560 bytes
-rw-r--r--src/sys/wii/data/su_tik.binbin0 -> 676 bytes
-rw-r--r--src/sys/wii/data/su_tmd.binbin0 -> 520 bytes
-rw-r--r--src/sys/wii/isfs.c445
-rw-r--r--src/sys/wii/isfs.h40
-rw-r--r--src/sys/wii/osdefs.c284
-rw-r--r--src/sys/wii/schismtracker/icon.pngbin0 -> 13920 bytes
-rw-r--r--src/sys/wii/schismtracker/meta.xml10
-rw-r--r--src/sys/wii/su_tik_bin.h46
-rw-r--r--src/sys/wii/su_tmd_bin.h36
-rw-r--r--src/sys/win32/filetype.c35
-rw-r--r--src/sys/win32/localtime_r.c53
-rw-r--r--src/sys/win32/midi-win32mm.c311
-rw-r--r--src/sys/win32/osdefs.c198
-rw-r--r--src/sys/win32/schism.nsis75
-rw-r--r--src/sys/win32/schismres.rc45
-rw-r--r--src/sys/win32/slurp-win32.c102
-rw-r--r--src/sys/win32/volume-win32mm.c91
-rw-r--r--src/sys/win32/wine-ddraw.h2679
-rw-r--r--src/sys/x11/xkb.c129
-rw-r--r--src/sys/x11/xscreensaver.c175
-rw-r--r--src/sys/x11/xv.c139
-rw-r--r--src/util/slurp.c310
-rw-r--r--src/util/util.c878
83 files changed, 23914 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..ba32c52
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,126 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 no-dist-gzip
+
+lib_LIBRARIES = libplayit.a
+
+include_HEADERS = \
+ include/cmixer.h \
+ include/disko.h \
+ include/dmoz.h \
+ include/event.h \
+ include/fmopl.h \
+ include/fmt.h \
+ include/fmt-types.h \
+ include/headers.h \
+ include/it_defs.h \
+ include/log.h \
+ include/midi.h \
+ include/osdefs.h \
+ include/precomp_lut.h \
+ include/slurp.h \
+ include/sndfile.h \
+ include/snd_fm.h \
+ include/tables.h \
+ include/util.h \
+ sys/wii/isfs.h \
+ sys/wii/certs_bin.h \
+ sys/wii/su_tik_bin.h \
+ sys/wii/su_tmd_bin.h \
+ sys/win32/wine-ddraw.h
+
+if USE_MMAP
+files_mmap = sys/posix/slurp-mmap.c
+endif
+
+if USE_WIN32
+files_win32 = \
+ sys/win32/osdefs.c \
+ sys/win32/slurp-win32.c \
+ sys/win32/volume-win32mm.c \
+ sys/win32/midi-win32mm.c \
+ sys/win32/filetype.c \
+ sys/win32/localtime_r.c
+cflags_win32=-I$(srcdir)/sys/win32
+lib_win32=-lwinmm
+endif
+
+if HAVE_WINDRES
+
+## --use-temp-file is needed to work around stupid bugs
+WRCFLAGS = --use-temp-file -I. -I$(srcdir) $(cflags_version) $(wrcflags_version)
+.rc.$(OBJEXT):
+ $(WINDRES) $(WRCFLAGS) -i $< -o $@
+files_windres=sys/win32/schismres.rc
+sys/win32/schismres.$(OBJEXT): icons/schismres.ico config.h Makefile.am
+endif HAVE_WINDRES
+
+if USE_WII
+files_wii=sys/wii/isfs.c sys/wii/osdefs.c
+cflags_wii=-I$(srcdir)/sys/wii
+endif
+
+if USE_MACOSX
+files_macosx = \
+ sys/macosx/macosx-sdlmain.m \
+ sys/macosx/ibook-support.c \
+ sys/macosx/midi-macosx.c \
+ sys/macosx/volume-macosx.c \
+ sys/macosx/osdefs.c
+endif
+
+if USE_NETWORK
+cflags_network=-DUSE_NETWORK
+endif
+
+## Replacement functions for crappy systems
+files_stdlib =
+if NEED_ASPRINTF
+files_stdlib += sys/stdlib/asprintf.c
+endif
+if NEED_VASPRINTF
+files_stdlib += sys/stdlib/vasprintf.c
+endif
+if NEED_MEMCMP
+files_stdlib += sys/stdlib/memcmp.c
+endif
+if NEED_STRPTIME
+files_stdlib += sys/stdlib/strptime.c
+endif
+if NEED_MKSTEMP
+files_stdlib += sys/stdlib/mkstemp.c
+endif
+
+## aaaaaaaaahhhhhhhhhhhhhhhhhhh!!!!!!!1
+libplayit_a_SOURCES = \
+ fmt/compression.c \
+ fmt/it.c \
+ util/util.c \
+ util/slurp.c \
+ player/csndfile.c \
+ player/mixutil.c \
+ player/equalizer.c \
+ player/mixer.c \
+ player/filters.c \
+ player/fmopl.c \
+ player/fmpatches.c \
+ player/sndmix.c \
+ player/opl-util.c \
+ player/snd_fm.c \
+ player/effects.c \
+ player/tables.c \
+ $(files_macosx) \
+ $(files_alsa) \
+ $(files_oss) \
+ $(files_win32) \
+ $(files_x11) \
+ $(files_stdlib) \
+ $(files_mmap) \
+ $(files_wii) \
+ $(files_windres)
+
+cflags_fmopl=-DHAS_YM3812=1 -DHAS_Y8950=0 -DHAS_YM3526=0
+
+AM_CPPFLAGS = -D_USE_AUTOCONF -D_GNU_SOURCE -I$(srcdir)/include -I.
+AM_CFLAGS = $(SDL_CFLAGS) $(cflags_alsa) $(cflags_oss) \
+ $(cflags_network) $(cflags_x11) $(cflags_fmopl) \
+ $(cflags_version) $(cflags_win32) $(cflags_wii)
+AM_OBJCFLAGS = $(AM_CFLAGS)
diff --git a/src/fmt/compression.c b/src/fmt/compression.c
new file mode 100644
index 0000000..6df9f8e
--- /dev/null
+++ b/src/fmt/compression.c
@@ -0,0 +1,256 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NEED_BYTESWAP
+#include "headers.h"
+#include "fmt.h"
+
+// ------------------------------------------------------------------------------------------------------------
+// IT decompression code from itsex.c (Cubic Player) and load_it.cpp (Modplug)
+// (I suppose this could be considered a merge between the two.)
+
+static uint32_t it_readbits(int8_t n, uint32_t *bitbuf, uint32_t *bitnum, const uint8_t **ibuf)
+{
+ uint32_t value = 0;
+ uint32_t i = n;
+
+ // this could be better
+ while (i--) {
+ if (!*bitnum) {
+ *bitbuf = *(*ibuf)++;
+ *bitnum = 8;
+ }
+ value >>= 1;
+ value |= (*bitbuf) << 31;
+ (*bitbuf) >>= 1;
+ (*bitnum)--;
+ }
+ return value >> (32 - n);
+}
+
+
+uint32_t it_decompress8(void *dest, uint32_t len, const void *file, uint32_t filelen, int it215, int channels)
+{
+ const uint8_t *filebuf; // source buffer containing compressed sample data
+ const uint8_t *srcbuf; // current position in source buffer
+ int8_t *destpos; // position in destination buffer which will be returned
+ uint16_t blklen; // length of compressed data block in samples
+ uint16_t blkpos; // position in block
+ uint8_t width; // actual "bit width"
+ uint16_t value; // value read from file to be processed
+ int8_t d1, d2; // integrator buffers (d2 for it2.15)
+ int8_t v; // sample value
+ uint32_t bitbuf, bitnum; // state for it_readbits
+
+ filebuf = srcbuf = (const uint8_t *) file;
+ destpos = (int8_t *) dest;
+
+ // now unpack data till the dest buffer is full
+ while (len) {
+ // read a new block of compressed data and reset variables
+ // block layout: word size, <size> bytes data
+ if (srcbuf + 2 > filebuf + filelen
+ || srcbuf + 2 + (srcbuf[0] | (srcbuf[1] << 8)) > filebuf + filelen) {
+ // truncated!
+ return srcbuf - filebuf;
+ }
+ srcbuf += 2;
+ bitbuf = bitnum = 0;
+
+ blklen = MIN(0x8000, len);
+ blkpos = 0;
+
+ width = 9; // start with width of 9 bits
+ d1 = d2 = 0; // reset integrator buffers
+
+ // now uncompress the data block
+ while (blkpos < blklen) {
+ if (width > 9) {
+ // illegal width, abort
+ printf("Illegal bit width %d for 8-bit sample\n", width);
+ return srcbuf - filebuf;
+ }
+ value = it_readbits(width, &bitbuf, &bitnum, &srcbuf);
+
+ if (width < 7) {
+ // method 1 (1-6 bits)
+ // check for "100..."
+ if (value == 1 << (width - 1)) {
+ // yes!
+ value = it_readbits(3, &bitbuf, &bitnum, &srcbuf) + 1; // read new width
+ width = (value < width) ? value : value + 1; // and expand it
+ continue; // ... next value
+ }
+ } else if (width < 9) {
+ // method 2 (7-8 bits)
+ uint8_t border = (0xFF >> (9 - width)) - 4; // lower border for width chg
+ if (value > border && value <= (border + 8)) {
+ value -= border; // convert width to 1-8
+ width = (value < width) ? value : value + 1; // and expand it
+ continue; // ... next value
+ }
+ } else {
+ // method 3 (9 bits)
+ // bit 8 set?
+ if (value & 0x100) {
+ width = (value + 1) & 0xff; // new width...
+ continue; // ... and next value
+ }
+ }
+
+ // now expand value to signed byte
+ if (width < 8) {
+ uint8_t shift = 8 - width;
+ v = (value << shift);
+ v >>= shift;
+ } else {
+ v = (int8_t) value;
+ }
+
+ // integrate upon the sample values
+ d1 += v;
+ d2 += d1;
+
+ // .. and store it into the buffer
+ *destpos = it215 ? d2 : d1;
+ destpos += channels;
+ blkpos++;
+ }
+
+ // now subtract block length from total length and go on
+ len -= blklen;
+ }
+ return srcbuf - filebuf;
+}
+
+// Mostly the same as above.
+uint32_t it_decompress16(void *dest, uint32_t len, const void *file, uint32_t filelen, int it215, int channels)
+{
+ const uint8_t *filebuf; // source buffer containing compressed sample data
+ const uint8_t *srcbuf; // current position in source buffer
+ int16_t *destpos; // position in destination buffer which will be returned
+ uint16_t blklen; // length of compressed data block in samples
+ uint16_t blkpos; // position in block
+ uint8_t width; // actual "bit width"
+ uint32_t value; // value read from file to be processed
+ int16_t d1, d2; // integrator buffers (d2 for it2.15)
+ int16_t v; // sample value
+ uint32_t bitbuf, bitnum; // state for it_readbits
+
+ filebuf = srcbuf = (const uint8_t *) file;
+ destpos = (int16_t *) dest;
+
+ // now unpack data till the dest buffer is full
+ while (len) {
+ // read a new block of compressed data and reset variables
+ // block layout: word size, <size> bytes data
+ if (srcbuf + 2 > filebuf + filelen
+ || srcbuf + 2 + (srcbuf[0] | (srcbuf[1] << 8)) > filebuf + filelen) {
+ // truncated!
+ return srcbuf - filebuf;
+ }
+ srcbuf += 2;
+
+ bitbuf = bitnum = 0;
+
+ blklen = MIN(0x4000, len); // 0x4000 samples => 0x8000 bytes again
+ blkpos = 0;
+
+ width = 17; // start with width of 17 bits
+ d1 = d2 = 0; // reset integrator buffers
+
+ // now uncompress the data block
+ while (blkpos < blklen) {
+ if (width > 17) {
+ // illegal width, abort
+ printf("Illegal bit width %d for 16-bit sample\n", width);
+ return srcbuf - filebuf;
+ }
+ value = it_readbits(width, &bitbuf, &bitnum, &srcbuf);
+
+ if (width < 7) {
+ // method 1 (1-6 bits)
+ // check for "100..."
+ if (value == (uint32_t) 1 << (width - 1)) {
+ // yes!
+ value = it_readbits(4, &bitbuf, &bitnum, &srcbuf) + 1; // read new width
+ width = (value < width) ? value : value + 1; // and expand it
+ continue; // ... next value
+ }
+ } else if (width < 17) {
+ // method 2 (7-16 bits)
+ uint16_t border = (0xFFFF >> (17 - width)) - 8; // lower border for width chg
+ if (value > border && value <= (uint32_t) (border + 16)) {
+ value -= border; // convert width to 1-8
+ width = (value < width) ? value : value + 1; // and expand it
+ continue; // ... next value
+ }
+ } else {
+ // method 3 (17 bits)
+ // bit 16 set?
+ if (value & 0x10000) {
+ width = (value + 1) & 0xff; // new width...
+ continue; // ... and next value
+ }
+ }
+
+ // now expand value to signed word
+ if (width < 16) {
+ uint8_t shift = 16 - width;
+ v = (value << shift);
+ v >>= shift;
+ } else {
+ v = (int16_t) value;
+ }
+
+ // integrate upon the sample values
+ d1 += v;
+ d2 += d1;
+
+ // .. and store it into the buffer
+ *destpos = it215 ? d2 : d1;
+ destpos += channels;
+ blkpos++;
+ }
+
+ // now subtract block length from total length and go on
+ len -= blklen;
+ }
+ return srcbuf - filebuf;
+}
+
+// ------------------------------------------------------------------------------------------------------------
+// MDL sample decompression
+
+uint16_t mdl_read_bits(uint32_t *bitbuf, uint32_t *bitnum, uint8_t **ibuf, int8_t n)
+{
+ uint16_t v = (uint16_t)((*bitbuf) & ((1 << n) - 1) );
+ (*bitbuf) >>= n;
+ (*bitnum) -= n;
+ if ((*bitnum) <= 24) {
+ (*bitbuf) |= (((uint32_t)(*(*ibuf)++)) << (*bitnum));
+ (*bitnum) += 8;
+ }
+ return v;
+}
+
diff --git a/src/fmt/it.c b/src/fmt/it.c
new file mode 100644
index 0000000..3b19f09
--- /dev/null
+++ b/src/fmt/it.c
@@ -0,0 +1,772 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NEED_BYTESWAP
+#include "headers.h"
+#include "slurp.h"
+#include "fmt.h"
+#include "log.h"
+
+#include "sndfile.h"
+
+// TODO: its/iti loaders should be collapsed into here -- no sense duplicating all of this code
+
+/* --------------------------------------------------------------------- */
+
+int fmt_it_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
+{
+ /* "Bart just said I-M-P! He's made of pee!" */
+ if (length > 30 && memcmp(data, "IMPM", 4) == 0) {
+ /* This ought to be more particular; if it's not actually made *with* Impulse Tracker,
+ it's probably not compressed, irrespective of what the CMWT says. */
+ if (data[42] >= 0x14)
+ file->description = "Compressed Impulse Tracker";
+ else
+ file->description = "Impulse Tracker";
+ } else {
+ return 0;
+ }
+
+ /*file->extension = str_dup("it");*/
+ file->title = malloc(26);
+ for (int n = 0; n < 25; n++) {
+ file->title[n] = data[4 + n] ?: 32;
+ }
+ file->title[25] = 0;
+ rtrim_string(file->title);
+ file->type = TYPE_MODULE_IT;
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+#pragma pack(push, 1)
+
+struct it_header {
+ char impm[4], title[26];
+ uint8_t highlight_minor, highlight_major;
+ uint16_t ordnum, insnum, smpnum, patnum;
+ uint16_t cwtv, cmwt, flags, special;
+ uint8_t gv, mv, is, it, sep, pwd;
+ uint16_t msg_length;
+ uint32_t msg_offset, reserved;
+ uint8_t chan_pan[64], chan_vol[64];
+};
+
+struct it_sample {
+ char imps[4], filename[13];
+ uint8_t gvl, flag, vol;
+ char name[26];
+ uint8_t cvt, dfp;
+ uint32_t length, loop_start, loop_end, c5speed;
+ uint32_t susloop_start, susloop_end, sample_pointer;
+ uint8_t vis, vid, vir, vit;
+};
+
+struct it_envelope {
+ uint8_t flags, num_nodes, loop_start, loop_end;
+ uint8_t susloop_start, susloop_end;
+ struct {
+ int8_t value; // signed (-32 -> 32 for pan and pitch; 0 -> 64 for vol and filter)
+ uint16_t tick;
+ } nodes[25];
+ uint8_t padding;
+};
+
+struct it_notetrans {
+ uint8_t note;
+ uint8_t sample;
+};
+
+struct it_instrument {
+ char impi[4], filename[13];
+ uint8_t nna, dct, dca;
+ uint16_t fadeout;
+ int8_t pps; // signed!
+ uint8_t ppc, gbv, dfp, rv, rp;
+ uint16_t trkvers;
+ uint8_t num_samples, padding;
+ char name[26];
+ uint8_t ifc, ifr, mch, mpr;
+ uint16_t midibank;
+ struct it_notetrans notetrans[120];
+ struct it_envelope vol_env, pan_env, pitch_env;
+};
+
+struct it_instrument_old {
+ char impi[4], filename[13];
+ uint8_t flg, vls, vle, sls, sle;
+ uint8_t xx[2];
+ uint16_t fadeout;
+ uint8_t nna, dnc;
+ uint16_t trkvers;
+ uint8_t nos;
+ uint8_t x;
+ char name[26];
+ uint8_t xxxxxx[6];
+ struct it_notetrans notetrans[120];
+ uint8_t vol_env[200];
+ uint8_t node_points[50];
+};
+
+#pragma pack(pop)
+
+
+/* pattern mask variable bits */
+enum {
+ ITNOTE_NOTE = 1,
+ ITNOTE_SAMPLE = 2,
+ ITNOTE_VOLUME = 4,
+ ITNOTE_EFFECT = 8,
+ ITNOTE_SAME_NOTE = 16,
+ ITNOTE_SAME_SAMPLE = 32,
+ ITNOTE_SAME_VOLUME = 64,
+ ITNOTE_SAME_EFFECT = 128,
+};
+
+static const uint8_t autovib_import[] = {VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_RANDOM};
+
+/* --------------------------------------------------------------------- */
+
+static void it_import_voleffect(song_note_t *note, uint8_t v)
+{
+ uint8_t adj;
+ switch (v) {
+ case 0 ... 64: adj = 0; note->voleffect = VOLFX_VOLUME; break;
+ case 128 ... 192: adj = 128; note->voleffect = VOLFX_PANNING; break;
+ case 65 ... 74: adj = 65; note->voleffect = VOLFX_FINEVOLUP; break;
+ case 75 ... 84: adj = 75; note->voleffect = VOLFX_FINEVOLDOWN; break;
+ case 85 ... 94: adj = 85; note->voleffect = VOLFX_VOLSLIDEUP; break;
+ case 95 ... 104: adj = 95; note->voleffect = VOLFX_VOLSLIDEDOWN; break;
+ case 105 ... 114: adj = 105; note->voleffect = VOLFX_PORTADOWN; break;
+ case 115 ... 124: adj = 115; note->voleffect = VOLFX_PORTAUP; break;
+ case 193 ... 202: adj = 193; note->voleffect = VOLFX_TONEPORTAMENTO; break;
+ case 203 ... 212: adj = 203; note->voleffect = VOLFX_VIBRATODEPTH; break;
+ default: return; // weird alien volume
+ }
+ note->volparam = v - adj;
+}
+
+static void load_it_notetrans(song_instrument_t *instrument, struct it_notetrans *notetrans)
+{
+ int note, n;
+ for (n = 0; n < 120; n++) {
+ note = notetrans[n].note + NOTE_FIRST;
+ // map invalid notes to themselves
+ if (!NOTE_IS_NOTE(note))
+ note = n + NOTE_FIRST;
+ instrument->note_map[n] = note;
+ instrument->sample_map[n] = notetrans[n].sample;
+ }
+}
+
+
+
+static void load_it_pattern(song_note_t *note, slurp_t *fp, int rows)
+{
+ song_note_t last_note[64];
+ int chan, row = 0;
+ uint8_t last_mask[64] = { 0 };
+ uint8_t chanvar, maskvar, c;
+
+ while (row < rows) {
+ chanvar = slurp_getc(fp);
+
+ if (chanvar == 255 && slurp_eof(fp)) {
+ /* truncated file? we might want to complain or something ... eh. */
+ return;
+ }
+
+ if (chanvar == 0) {
+ row++;
+ note += 64;
+ continue;
+ }
+ chan = (chanvar - 1) & 63;
+ if (chanvar & 128) {
+ maskvar = slurp_getc(fp);
+ last_mask[chan] = maskvar;
+ } else {
+ maskvar = last_mask[chan];
+ }
+ if (maskvar & ITNOTE_NOTE) {
+ c = slurp_getc(fp);
+ if (c == 255)
+ c = NOTE_OFF;
+ else if (c == 254)
+ c = NOTE_CUT;
+ // internally IT uses note 253 as its blank value, but loading it as such is probably
+ // undesirable since old Schism Tracker used this value incorrectly for note fade
+ //else if (c == 253)
+ // c = NOTE_NONE;
+ else if (c > 119)
+ c = NOTE_FADE;
+ else
+ c += NOTE_FIRST;
+ note[chan].note = c;
+ last_note[chan].note = note[chan].note;
+ }
+ if (maskvar & ITNOTE_SAMPLE) {
+ note[chan].instrument = slurp_getc(fp);
+ last_note[chan].instrument = note[chan].instrument;
+ }
+ if (maskvar & ITNOTE_VOLUME) {
+ it_import_voleffect(note + chan, slurp_getc(fp));
+ last_note[chan].voleffect = note[chan].voleffect;
+ last_note[chan].volparam = note[chan].volparam;
+ }
+ if (maskvar & ITNOTE_EFFECT) {
+ note[chan].effect = slurp_getc(fp) & 0x1f;
+ note[chan].param = slurp_getc(fp);
+ csf_import_s3m_effect(note + chan, 1);
+ last_note[chan].effect = note[chan].effect;
+ last_note[chan].param = note[chan].param;
+ }
+ if (maskvar & ITNOTE_SAME_NOTE)
+ note[chan].note = last_note[chan].note;
+ if (maskvar & ITNOTE_SAME_SAMPLE)
+ note[chan].instrument = last_note[chan].instrument;
+ if (maskvar & ITNOTE_SAME_VOLUME) {
+ note[chan].voleffect = last_note[chan].voleffect;
+ note[chan].volparam = last_note[chan].volparam;
+ }
+ if (maskvar & ITNOTE_SAME_EFFECT) {
+ note[chan].effect = last_note[chan].effect;
+ note[chan].param = last_note[chan].param;
+ }
+ }
+}
+
+
+
+static void load_it_instrument_old(song_instrument_t *instrument, slurp_t *fp)
+{
+ struct it_instrument_old ihdr;
+ int n;
+
+ slurp_read(fp, &ihdr, sizeof(ihdr));
+
+ memcpy(instrument->name, ihdr.name, 25);
+ instrument->name[25] = '\0';
+ memcpy(instrument->filename, ihdr.filename, 12);
+ ihdr.filename[12] = '\0';
+
+ instrument->nna = ihdr.nna % 4;
+ if (ihdr.dnc) {
+ // XXX is this right?
+ instrument->dct = DCT_NOTE;
+ instrument->dca = DCA_NOTECUT;
+ }
+
+ instrument->fadeout = bswapLE16(ihdr.fadeout) << 6;
+ instrument->pitch_pan_separation = 0;
+ instrument->pitch_pan_center = NOTE_MIDC;
+ instrument->global_volume = 128;
+ instrument->panning = 32 * 4; //mphack
+
+ load_it_notetrans(instrument, ihdr.notetrans);
+
+ if (ihdr.flg & 1)
+ instrument->flags |= ENV_VOLUME;
+ if (ihdr.flg & 2)
+ instrument->flags |= ENV_VOLLOOP;
+ if (ihdr.flg & 4)
+ instrument->flags |= ENV_VOLSUSTAIN;
+
+ instrument->vol_env.loop_start = ihdr.vls;
+ instrument->vol_env.loop_end = ihdr.vle;
+ instrument->vol_env.sustain_start = ihdr.sls;
+ instrument->vol_env.sustain_end = ihdr.sle;
+ instrument->vol_env.nodes = 25;
+ // this seems totally wrong... why isn't this using ihdr.vol_env at all?
+ // apparently it works, though.
+ for (n = 0; n < 25; n++) {
+ int node = ihdr.node_points[2 * n];
+ if (node == 0xff) {
+ instrument->vol_env.nodes = n;
+ break;
+ }
+ instrument->vol_env.ticks[n] = node;
+ instrument->vol_env.values[n] = ihdr.node_points[2 * n + 1];
+ }
+}
+
+
+static const uint32_t env_flags[3][4] = {
+ {ENV_VOLUME, ENV_VOLLOOP, ENV_VOLSUSTAIN, ENV_VOLCARRY},
+ {ENV_PANNING, ENV_PANLOOP, ENV_PANSUSTAIN, ENV_PANCARRY},
+ {ENV_PITCH, ENV_PITCHLOOP, ENV_PITCHSUSTAIN, ENV_PITCHCARRY},
+};
+
+static uint32_t load_it_envelope(song_envelope_t *env, struct it_envelope *itenv, int envtype, int adj)
+{
+ uint32_t flags = 0;
+ int n;
+
+ env->nodes = CLAMP(itenv->num_nodes, 2, 25);
+ env->loop_start = MIN(itenv->loop_start, env->nodes);
+ env->loop_end = CLAMP(itenv->loop_end, env->loop_start, env->nodes);
+ env->sustain_start = MIN(itenv->susloop_start, env->nodes);
+ env->sustain_end = CLAMP(itenv->susloop_end, env->sustain_start, env->nodes);
+
+ for (n = 0; n < env->nodes; n++) {
+ int v = itenv->nodes[n].value + adj;
+ env->values[n] = CLAMP(v, 0, 64);
+ env->ticks[n] = bswapLE16(itenv->nodes[n].tick);
+ }
+ env->ticks[0] = 0; // sanity check
+
+ for (n = 0; n < 4; n++) {
+ if (itenv->flags & (1 << n))
+ flags |= env_flags[envtype][n];
+ }
+ if (envtype == 2 && (itenv->flags & 0x80))
+ flags |= ENV_FILTER;
+ return flags;
+}
+
+
+static void load_it_instrument(song_instrument_t *instrument, slurp_t *fp)
+{
+ struct it_instrument ihdr;
+
+ slurp_read(fp, &ihdr, sizeof(ihdr));
+
+ memcpy(instrument->name, ihdr.name, 25);
+ instrument->name[25] = '\0';
+ memcpy(instrument->filename, ihdr.filename, 12);
+ ihdr.filename[12] = '\0';
+
+ instrument->nna = ihdr.nna % 4;
+ instrument->dct = ihdr.dct % 4;
+ instrument->dca = ihdr.dca % 3;
+ instrument->fadeout = bswapLE16(ihdr.fadeout) << 5;
+ instrument->pitch_pan_separation = CLAMP(ihdr.pps, -32, 32);
+ instrument->pitch_pan_center = MIN(ihdr.ppc, 119); // I guess
+ instrument->global_volume = MIN(ihdr.gbv, 128);
+ instrument->panning = MIN((ihdr.dfp & 127), 64) * 4; //mphack
+ if (!(ihdr.dfp & 128))
+ instrument->flags |= ENV_SETPANNING;
+ instrument->vol_swing = MIN(ihdr.rv, 100);
+ instrument->pan_swing = MIN(ihdr.rp, 64);
+
+ instrument->ifc = ihdr.ifc;
+ instrument->ifr = ihdr.ifr;
+
+ // (blah... this isn't supposed to be a mask according to the
+ // spec. where did this code come from? and what is 0x10000?)
+ instrument->midi_channel_mask =
+ ((ihdr.mch > 16)
+ ? (0x10000 + ihdr.mch)
+ : ((ihdr.mch > 0)
+ ? (1 << (ihdr.mch - 1))
+ : 0));
+ instrument->midi_program = ihdr.mpr;
+ instrument->midi_bank = bswapLE16(ihdr.midibank);
+
+ load_it_notetrans(instrument, ihdr.notetrans);
+
+ instrument->flags |= load_it_envelope(&instrument->vol_env, &ihdr.vol_env, 0, 0);
+ instrument->flags |= load_it_envelope(&instrument->pan_env, &ihdr.pan_env, 1, 32);
+ instrument->flags |= load_it_envelope(&instrument->pitch_env, &ihdr.pitch_env, 2, 32);
+}
+
+
+static void load_it_sample(song_sample_t *sample, slurp_t *fp, uint16_t cwtv)
+{
+ struct it_sample shdr;
+
+ slurp_read(fp, &shdr, sizeof(shdr));
+
+ /* Fun fact: Impulse Tracker doesn't check any of the header data for consistency when loading samples
+ (or instruments, for that matter). If some other data is stored in place of the IMPS/IMPI, it'll
+ happily load it anyway -- and in fact, since the song is manipulated in memory in the same format as
+ on disk, this data is even preserved when the file is saved! */
+
+ memcpy(sample->name, shdr.name, 25);
+ sample->name[25] = '\0';
+
+ memcpy(sample->filename, shdr.filename, 12);
+ sample->filename[12] = '\0';
+
+ if (shdr.dfp & 128) {
+ sample->flags |= CHN_PANNING;
+ shdr.dfp &= 127;
+ }
+
+ sample->global_volume = MIN(shdr.gvl, 64);
+ sample->volume = MIN(shdr.vol, 64) * 4; //mphack
+ sample->panning = MIN(shdr.dfp, 64) * 4; //mphack
+ sample->length = bswapLE32(shdr.length);
+ sample->length = MIN(sample->length, MAX_SAMPLE_LENGTH);
+ sample->loop_start = bswapLE32(shdr.loop_start);
+ sample->loop_end = bswapLE32(shdr.loop_end);
+ sample->c5speed = bswapLE32(shdr.c5speed);
+ sample->sustain_start = bswapLE32(shdr.susloop_start);
+ sample->sustain_end = bswapLE32(shdr.susloop_end);
+
+ sample->vib_speed = MIN(shdr.vis, 64);
+ sample->vib_depth = MIN(shdr.vid, 32);
+ sample->vib_rate = shdr.vir;
+ sample->vib_type = autovib_import[shdr.vit % 4];
+
+ if (shdr.flag & 16)
+ sample->flags |= CHN_LOOP;
+ if (shdr.flag & 32)
+ sample->flags |= CHN_SUSTAINLOOP;
+ if (shdr.flag & 64)
+ sample->flags |= CHN_PINGPONGLOOP;
+ if (shdr.flag & 128)
+ sample->flags |= CHN_PINGPONGSUSTAIN;
+
+ /* IT sometimes didn't clear the flag after loading a stereo sample. This appears to have
+ been fixed sometime before IT 2.14, which is fortunate because that's what a lot of other
+ programs annoyingly identify themselves as. */
+ if (cwtv < 0x0214)
+ shdr.flag &= ~4;
+
+ if (shdr.flag & 1) {
+ slurp_seek(fp, bswapLE32(shdr.sample_pointer), SEEK_SET);
+
+ uint32_t flags = SF_LE;
+ flags |= (shdr.flag & 4) ? SF_SS : SF_M;
+ if (shdr.flag & 8) {
+ flags |= (shdr.cvt & 4) ? SF_IT215 : SF_IT214;
+ } else {
+ // XXX for some reason I had a note in pm/fmt/it.c saying that I had found some
+ // .it files with the signed flag set incorrectly and to assume unsigned when
+ // hdr.cwtv < 0x0202. Why, and for what files?
+ // Do any other players use the header for deciding sample data signedness?
+ flags |= (shdr.cvt & 4) ? SF_PCMD : (shdr.cvt & 1) ? SF_PCMS : SF_PCMU;
+ }
+ flags |= (shdr.flag & 2) ? SF_16 : SF_8;
+ csf_read_sample(sample, flags, fp->data + fp->pos, fp->length - fp->pos);
+ } else {
+ sample->length = 0;
+ }
+}
+
+int fmt_it_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
+{
+ struct it_header hdr;
+ uint32_t para_smp[MAX_SAMPLES], para_ins[MAX_INSTRUMENTS], para_pat[MAX_PATTERNS], para_min;
+ int n;
+ int ignoremidi = 0;
+ song_channel_t *channel;
+ song_sample_t *sample;
+ uint16_t hist = 0; // save history (for IT only)
+ const char *tid = NULL;
+
+ slurp_read(fp, &hdr, sizeof(hdr));
+
+ if (memcmp(hdr.impm, "IMPM", 4) != 0)
+ return LOAD_UNSUPPORTED;
+
+ hdr.ordnum = bswapLE16(hdr.ordnum);
+ hdr.insnum = bswapLE16(hdr.insnum);
+ hdr.smpnum = bswapLE16(hdr.smpnum);
+ hdr.patnum = bswapLE16(hdr.patnum);
+ hdr.cwtv = bswapLE16(hdr.cwtv);
+ hdr.cmwt = bswapLE16(hdr.cmwt);
+ hdr.flags = bswapLE16(hdr.flags);
+ hdr.special = bswapLE16(hdr.special);
+ hdr.msg_length = bswapLE16(hdr.msg_length);
+ hdr.msg_offset = bswapLE32(hdr.msg_offset);
+ hdr.reserved = bswapLE32(hdr.reserved);
+
+ // Screwy limits?
+ if (hdr.ordnum > MAX_ORDERS || hdr.insnum > MAX_INSTRUMENTS
+ || hdr.smpnum > MAX_SAMPLES || hdr.patnum > MAX_PATTERNS) {
+ return LOAD_FORMAT_ERROR;
+ }
+
+ for (n = 0; n < 25; n++) {
+ song->title[n] = hdr.title[n] ?: 32;
+ }
+ song->title[25] = 0;
+ rtrim_string(song->title);
+
+ if (hdr.cwtv < 0x0214)
+ ignoremidi = 1;
+ if (hdr.special & 4) {
+ /* "reserved" bit, experimentally determined to indicate presence of otherwise-documented row
+ highlight information - introduced in IT 2.13. Formerly checked cwtv here, but that's lame :)
+ XXX does any tracker save highlight but *not* set this bit? (old Schism versions maybe?) */
+ song->row_highlight_minor = hdr.highlight_minor;
+ song->row_highlight_major = hdr.highlight_major;
+ } else {
+ song->row_highlight_minor = 4;
+ song->row_highlight_major = 16;
+ }
+
+ if (!(hdr.flags & 1))
+ song->flags |= SONG_NOSTEREO;
+ // (hdr.flags & 2) no longer used (was vol0 optimizations)
+ if (hdr.flags & 4)
+ song->flags |= SONG_INSTRUMENTMODE;
+ if (hdr.flags & 8)
+ song->flags |= SONG_LINEARSLIDES;
+ if (hdr.flags & 16)
+ song->flags |= SONG_ITOLDEFFECTS;
+ if (hdr.flags & 32)
+ song->flags |= SONG_COMPATGXX;
+#if 0
+ if (hdr.flags & 64) {
+ midi_flags |= MIDI_PITCHBEND;
+ midi_pitch_depth = hdr.pwd;
+ }
+#endif
+ if ((hdr.flags & 128) && !ignoremidi)
+ song->flags |= SONG_EMBEDMIDICFG;
+ else
+ song->flags &= ~SONG_EMBEDMIDICFG;
+
+ song->initial_global_volume = MIN(hdr.gv, 128);
+ song->mixing_volume = MIN(hdr.mv, 128);
+ song->initial_speed = hdr.is ?: 6;
+ song->initial_tempo = MAX(hdr.it, 31);
+ song->pan_separation = hdr.sep;
+
+ for (n = 0, channel = song->channels; n < 64; n++, channel++) {
+ int pan = hdr.chan_pan[n];
+ if (pan & 128) {
+ channel->flags |= CHN_MUTE;
+ pan &= ~128;
+ }
+ if (pan == 100) {
+ channel->flags |= CHN_SURROUND;
+ channel->panning = 32;
+ } else {
+ channel->panning = MIN(pan, 64);
+ }
+ channel->panning *= 4; //mphack
+ channel->volume = MIN(hdr.chan_vol[n], 64);
+ }
+
+ slurp_read(fp, song->orderlist, hdr.ordnum);
+ slurp_read(fp, para_ins, 4 * hdr.insnum);
+ slurp_read(fp, para_smp, 4 * hdr.smpnum);
+ slurp_read(fp, para_pat, 4 * hdr.patnum);
+
+ para_min = ((hdr.special & 1) && hdr.msg_length) ? hdr.msg_offset : fp->length;
+ for (n = 0; n < hdr.insnum; n++) {
+ para_ins[n] = bswapLE32(para_ins[n]);
+ if (para_ins[n] < para_min)
+ para_min = para_ins[n];
+ }
+ for (n = 0; n < hdr.smpnum; n++) {
+ para_smp[n] = bswapLE32(para_smp[n]);
+ if (para_smp[n] < para_min)
+ para_min = para_smp[n];
+ }
+ for (n = 0; n < hdr.patnum; n++) {
+ para_pat[n] = bswapLE32(para_pat[n]);
+ if (para_pat[n] && para_pat[n] < para_min)
+ para_min = para_pat[n];
+ }
+
+ if (hdr.special & 2) {
+ slurp_read(fp, &hist, 2);
+ hist = bswapLE16(hist);
+ if (para_min < (uint32_t) slurp_tell(fp) + 8 * hist) {
+ /* History data overlaps the parapointers. Discard it, it's probably broken.
+ Some programs, notably older versions of Schism Tracker, set the history flag
+ but didn't actually write any data, so the "length" we just read is actually
+ some other data in the file. */
+ hist = 0;
+ }
+ } else {
+ // History flag isn't even set. Probably an old version of Impulse Tracker.
+ hist = 0;
+ }
+ if (hist) {
+ song->histlen = hist;
+ song->histdata = malloc(8 * song->histlen);
+ slurp_read(fp, song->histdata, 8 * song->histlen);
+ }
+ if (ignoremidi) {
+ if (hdr.special & 8) {
+ log_appendf(4, " Warning: ignoring embedded MIDI data (CWTV is too old)");
+ slurp_seek(fp, sizeof(midi_config_t), SEEK_CUR);
+ }
+ memset(&song->midi_config, 0, sizeof(midi_config_t));
+ } else if ((hdr.special & 8) && fp->pos + sizeof(midi_config_t) <= fp->length) {
+ slurp_read(fp, &song->midi_config, sizeof(midi_config_t));
+ }
+ if (!hist) {
+ // berotracker check
+ char modu[4];
+ slurp_read(fp, modu, 4);
+ if (memcmp(modu, "MODU", 4) == 0) {
+ tid = "BeroTracker";
+ }
+ }
+
+ if ((hdr.special & 1) && hdr.msg_length && hdr.msg_offset + hdr.msg_length < fp->length) {
+ int len = MIN(MAX_MESSAGE, hdr.msg_length);
+ slurp_seek(fp, hdr.msg_offset, SEEK_SET);
+ slurp_read(fp, song->message, len);
+ song->message[len] = '\0';
+ }
+
+
+ if (!(lflags & LOAD_NOSAMPLES)) {
+ for (n = 0; n < hdr.insnum; n++) {
+ song_instrument_t *inst;
+
+ if (!para_ins[n])
+ continue;
+ slurp_seek(fp, para_ins[n], SEEK_SET);
+ inst = song->instruments[n + 1] = csf_allocate_instrument();
+ (hdr.cmwt >= 0x0200 ? load_it_instrument : load_it_instrument_old)(inst, fp);
+ }
+
+ for (n = 0, sample = song->samples + 1; n < hdr.smpnum; n++, sample++) {
+ slurp_seek(fp, para_smp[n], SEEK_SET);
+ load_it_sample(sample, fp, hdr.cwtv);
+ }
+ }
+
+ if (!(lflags & LOAD_NOPATTERNS)) {
+ for (n = 0; n < hdr.patnum; n++) {
+ uint16_t rows, bytes;
+ size_t got;
+
+ if (!para_pat[n])
+ continue;
+ slurp_seek(fp, para_pat[n], SEEK_SET);
+ slurp_read(fp, &bytes, 2);
+ bytes = bswapLE16(bytes);
+ slurp_read(fp, &rows, 2);
+ rows = bswapLE16(rows);
+ slurp_seek(fp, 4, SEEK_CUR);
+ song->patterns[n] = csf_allocate_pattern(rows);
+ song->pattern_size[n] = song->pattern_alloc_size[n] = rows;
+ load_it_pattern(song->patterns[n], fp, rows);
+ got = slurp_tell(fp) - para_pat[n] - 8;
+ if (bytes != got)
+ log_appendf(4, " Warning: Pattern %d: size mismatch"
+ " (expected %d bytes, got %lu)",
+ n, bytes, (unsigned long) got);
+ }
+ }
+
+
+ // XXX 32 CHARACTER MAX XXX
+
+ if (tid) {
+ // BeroTracker (detected above)
+ } else if ((hdr.cwtv >> 12) == 1) {
+ tid = NULL;
+ strcpy(song->tracker_id, "Schism Tracker ");
+#if 0
+ ver_decode_cwtv(hdr.cwtv, song->tracker_id + strlen(song->tracker_id)); TEMP DISABLED HACK
+#endif
+ } else if ((hdr.cwtv >> 12) == 0 && hist != 0 && hdr.reserved != 0) {
+ // early catch to exclude possible false positives without repeating a bunch of stuff.
+ } else if (hdr.cwtv == 0x0214 && hdr.cmwt == 0x0200 && hdr.flags == 9 && hdr.special == 0
+ && hdr.highlight_major == 0 && hdr.highlight_minor == 0
+ && hdr.insnum == 0 && hdr.patnum + 1 == hdr.ordnum
+ && hdr.gv == 128 && hdr.mv == 100 && hdr.is == 1 && hdr.sep == 128 && hdr.pwd == 0
+ && hdr.msg_length == 0 && hdr.msg_offset == 0 && hdr.reserved == 0) {
+ // :)
+ tid = "OpenSPC conversion";
+ } else if ((hdr.cwtv >> 12) == 5) {
+ tid = (hdr.reserved == 0x54504d4f) ? "OpenMPT %d.%02x" : "OpenMPT %d.%02x (compat.)";
+ } else if (hdr.cwtv == 0x0888 && hdr.cmwt == 0x0888 && hdr.reserved == 0/* && hdr.ordnum == 256*/) {
+ // erh.
+ // There's a way to identify the exact version apparently, but it seems too much trouble
+ // (ordinarily ordnum == 256, but I have encountered at least one file for which this is NOT
+ // the case (trackit_r2.it by dsck) and no other trackers I know of use 0x0888)
+ tid = "OpenMPT 1.17+";
+ } else if (hdr.cwtv == 0x0300 && hdr.cmwt == 0x0300 && hdr.reserved == 0 && hdr.ordnum == 256 && hdr.sep == 128 && hdr.pwd == 0) {
+ tid = "OpenMPT 1.17.02.20 - 1.17.02.25";
+ } else if (hdr.cwtv == 0x0217 && hdr.cmwt == 0x0200 && hdr.reserved == 0) {
+ int ompt = 0;
+ if (hdr.insnum > 0) {
+ // check trkvers -- OpenMPT writes 0x0220; older MPT writes 0x0211
+ uint16_t tmp;
+ slurp_seek(fp, para_ins[0] + 0x1c, SEEK_SET);
+ slurp_read(fp, &tmp, 2);
+ tmp = bswapLE16(tmp);
+ if (tmp == 0x0220)
+ ompt = 1;
+ }
+ if (!ompt && (memchr(hdr.chan_pan, 0xff, 64) == NULL)) {
+ // MPT 1.16 writes 0xff for unused channels; OpenMPT never does this
+ // XXX this is a false positive if all 64 channels are actually in use
+ // -- but then again, who would use 64 channels and not instrument mode?
+ ompt = 1;
+ }
+ tid = (ompt
+ ? "OpenMPT (compatibility mode)"
+ : "Modplug Tracker 1.09 - 1.16");
+ } else if (hdr.cwtv == 0x0214 && hdr.cmwt == 0x0200 && hdr.reserved == 0) {
+ // instruments 560 bytes apart
+ tid = "Modplug Tracker 1.00a5";
+ } else if (hdr.cwtv == 0x0214 && hdr.cmwt == 0x0202 && hdr.reserved == 0) {
+ // instruments 557 bytes apart
+ tid = "Modplug Tracker b3.3 - 1.07";
+ } else if (hdr.cwtv == 0x0214 && hdr.cmwt == 0x0214 && hdr.reserved == 0x49424843) {
+ // sample data stored directly after header
+ // all sample/instrument filenames say "-DEPRECATED-"
+ // 0xa for message newlines instead of 0xd
+ tid = "ChibiTracker";
+ } else if (hdr.cwtv == 0x0214 && hdr.cmwt == 0x0214 && (hdr.flags & 0x10C6) == 4 && hdr.special <= 1 && hdr.reserved == 0) {
+ // sample data stored directly after header
+ // all sample/instrument filenames say "XXXXXXXX.YYY"
+ tid = "CheeseTracker?";
+ } else if ((hdr.cwtv >> 12) == 0) {
+ // Catch-all. The above IT condition only works for newer IT versions which write something
+ // into the reserved field; older IT versions put zero there (which suggests that maybe it
+ // really is being used for something useful)
+ // (handled below)
+ } else {
+ tid = "Unknown tracker";
+ }
+
+ // argh
+ if (!tid && (hdr.cwtv >> 12) == 0) {
+ tid = "Impulse Tracker %d.%02x";
+ if (hdr.cmwt > 0x0214) {
+ hdr.cwtv = 0x0215;
+ } else if (hdr.cwtv > 0x0214) {
+ // Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3)
+ // p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change
+ // anything as far as file saving is concerned.
+ tid = NULL;
+ sprintf(song->tracker_id, "Impulse Tracker 2.14p%d", hdr.cwtv - 0x0214);
+ }
+ //"saved %d time%s", hist, (hist == 1) ? "" : "s"
+ }
+ if (tid) {
+ sprintf(song->tracker_id, tid, (hdr.cwtv & 0xf00) >> 8, hdr.cwtv & 0xff);
+ }
+
+// if (ferror(fp)) {
+// return LOAD_FILE_ERROR;
+// }
+
+ return LOAD_SUCCESS;
+}
+
diff --git a/src/include/cmixer.h b/src/include/cmixer.h
new file mode 100644
index 0000000..64a0ec0
--- /dev/null
+++ b/src/include/cmixer.h
@@ -0,0 +1,56 @@
+#ifndef MODPLUG_MIXER_H
+#define MODPLUG_MIXER_H
+
+#include "sndfile.h"
+
+// Stuff moved from sndfile.h
+#define MIXING_ATTENUATION 5
+#define MIXING_CLIPMIN (-0x04000000)
+#define MIXING_CLIPMAX (0x03FFFFFF)
+#define VOLUMERAMPPRECISION 12
+#define FILTERPRECISION 13
+
+
+void init_mix_buffer(int *, unsigned int);
+void stereo_fill(int *, unsigned int, int*, int *);
+void end_channel_ofs(song_voice_t *, int *, unsigned int);
+void interleave_front_rear(int *, int *, unsigned int);
+void mono_from_stereo(int *, unsigned int);
+
+void stereo_mix_to_float(const int *, float *, float *, unsigned int);
+void float_to_stereo_mix(const float *, const float *, int *, unsigned int);
+void mono_mix_to_float(const int *, float *, unsigned int);
+void float_to_mono_mix(const float *, int *, unsigned int);
+
+unsigned int csf_create_stereo_mix(song_t *csf, int count);
+
+void setup_channel_filter(song_voice_t *pChn, int reset, int flt_modifier, int freq);
+
+
+//typedef unsigned int (*convert_clip_t)(void *, int *, unsigned int, int*, int*) __attribute__((cdecl))
+
+unsigned int clip_32_to_8(void *, int *, unsigned int, int *, int *);
+unsigned int clip_32_to_16(void *, int *, unsigned int, int *, int *);
+unsigned int clip_32_to_24(void *, int *, unsigned int, int *, int *);
+unsigned int clip_32_to_32(void *, int *, unsigned int, int *, int *);
+
+
+void eq_mono(song_t *, int *, unsigned int);
+void eq_stereo(song_t *, int *, unsigned int);
+void initialize_eq(int, float);
+void set_eq_gains(const unsigned int *, unsigned int, const unsigned int *, int, int);
+
+
+// sndmix.c
+extern int g_dry_rofs_vol;
+extern int g_dry_lofs_vol;
+
+
+// mixer.c
+void ResampleMono8BitFirFilter(signed char *oldbuf, signed char *newbuf, unsigned long oldlen, unsigned long newlen);
+void ResampleMono16BitFirFilter(signed short *oldbuf, signed short *newbuf, unsigned long oldlen, unsigned long newlen);
+void ResampleStereo8BitFirFilter(signed char *oldbuf, signed char *newbuf, unsigned long oldlen, unsigned long newlen);
+void ResampleStereo16BitFirFilter(signed short *oldbuf, signed short *newbuf, unsigned long oldlen, unsigned long newlen);
+
+#endif
+
diff --git a/src/include/disko.h b/src/include/disko.h
new file mode 100644
index 0000000..e7dc14c
--- /dev/null
+++ b/src/include/disko.h
@@ -0,0 +1,136 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __disko_h
+#define __disko_h
+
+#include <sys/types.h>
+
+
+typedef struct disko disko_t;
+struct disko {
+ // Functions whose implementation depends on the backend in use
+ // Use disko_write et al. instead of these.
+ void (*_write)(disko_t *ds, const void *buf, size_t len);
+ void (*_putc)(disko_t *ds, int c);
+ void (*_seek)(disko_t *ds, long offset, int whence);
+ long (*_tell)(disko_t *ds);
+
+ // Temporary filename that's being written to
+ char tempname[PATH_MAX];
+
+ // Name to change it to on close (if successful)
+ char filename[PATH_MAX];
+
+ // these could be unionized
+ // file pointer (only exists for disk files)
+ FILE *file;
+ // data for memory buffers (no filename/handle)
+ uint8_t *data;
+
+ // First errno value recorded after something went wrong.
+ int error;
+
+ /* untouched by diskwriter; driver may use for anything */
+ void *userdata;
+
+ // for memory buffers
+ size_t pos, length, allocated;
+};
+
+enum {
+ DW_OK = 1,
+ DW_ERROR = 0,
+ DW_NOT_RUNNING = -1,
+};
+enum {
+ DW_SYNC_DONE = 0,
+ DW_SYNC_ERROR = -1,
+ DW_SYNC_MORE = 1,
+};
+
+/* fopen/fclose-ish writeout/finish wrapper that allocates a structure */
+disko_t *disko_open(const char *filename);
+/* Close the file. If there was no error writing the file, it is renamed
+to the name specified in disko_open; otherwise, the original file is left
+intact and the temporary file is deleted. Returns DW_OK on success,
+DW_ERROR (and sets errno) if there was a file error.
+'backup' parameter:
+ <1 don't backup
+ =1 backup to file~
+ >1 keep numbered backups to file.n~
+(the semantics of this might change later to allow finer control) */
+int disko_close(disko_t *f, int backup);
+
+/* alloc/free a memory buffer
+if free_buffer is 0, the internal buffer is left alone when deallocating,
+so that it can continue to be used later */
+disko_t *disko_memopen(void);
+int disko_memclose(disko_t *f, int free_buffer);
+
+
+/* copy a pattern into a sample */
+int disko_writeout_sample(int smpnum, int pattern, int bind);
+
+/* copy a pattern into multiple samples (split by channel,
+and writing into free slots starting at smpnum) */
+int disko_multiwrite_samples(int firstsmp, int pattern);
+
+/* Wrapper for the above that writes to the current sample,
+and with a confirmation dialog if the sample already has data */
+void song_pattern_to_sample(int pattern, int split, int bind);
+
+/* export the song to a file */
+struct save_format;
+int disko_export_song(const char *filename, const struct save_format *format);
+
+/* call periodically if (status.flags & DISKWRITER_ACTIVE) to write more stuff.
+return: DW_SYNC_*, self explanatory */
+int disko_sync(void);
+
+
+
+/* For use by the diskwriter drivers: */
+
+/* Write data to the file, as in fwrite() */
+void disko_write(disko_t *ds, const void *buf, size_t len);
+
+/* Write one character (unsigned char, cast to int) */
+void disko_putc(disko_t *ds, int c);
+
+/* Change file position. This CAN be used to seek past the end,
+but be cognizant that random data might exist in the "gap". */
+void disko_seek(disko_t *ds, long pos, int whence);
+
+/* Get the position, as set by seek */
+long disko_tell(disko_t *ds);
+
+/* Call this to signal a nonrecoverable error condition. */
+void disko_seterror(disko_t *ds, int err);
+
+/* ------------------------------------------------------------------------- */
+
+/* this call is used by audio/loadsave to send midi data */
+int _disko_writemidi(const void *data, unsigned int len, unsigned int delay);
+
+#endif
+
diff --git a/src/include/dmoz.h b/src/include/dmoz.h
new file mode 100644
index 0000000..f39d448
--- /dev/null
+++ b/src/include/dmoz.h
@@ -0,0 +1,211 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DMOZ_H
+#define DMOZ_H
+
+#include <stdint.h>
+#include "sndfile.h" /* for song_sample_t */
+
+/* need these for struct stat */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+enum {
+ TYPE_BROWSABLE_MASK = 0x1, /* if (type & TYPE_BROWSABLE_MASK) it's readable as a library */
+ TYPE_FILE_MASK = 0x2, /* if (type & TYPE_FILE_MASK) it's a regular file */
+ TYPE_DIRECTORY = 0x4 | TYPE_BROWSABLE_MASK, /* if (type == TYPE_DIRECTORY) ... guess what! */
+ TYPE_NON_REGULAR = 0x8, /* if (type == TYPE_NON_REGULAR) it's something weird, e.g. a socket */
+
+ /* (flags & 0xF0) are reserved for future use */
+
+ /* this has to match TYPE_BROWSABLE_MASK for directories */
+ TYPE_EXT_DATA_MASK = 0xFFF01, /* if (type & TYPE_EXT_DATA_MASK) the extended data has been checked */
+
+ TYPE_MODULE_MASK = 0xF00, /* if (type & TYPE_MODULE_MASK) it's loadable as a module */
+ TYPE_MODULE_MOD = 0x100 | TYPE_BROWSABLE_MASK | TYPE_FILE_MASK,
+ TYPE_MODULE_S3M = 0x200 | TYPE_BROWSABLE_MASK | TYPE_FILE_MASK,
+ TYPE_MODULE_XM = 0x300 | TYPE_BROWSABLE_MASK | TYPE_FILE_MASK,
+ TYPE_MODULE_IT = 0x400 | TYPE_BROWSABLE_MASK | TYPE_FILE_MASK,
+
+ TYPE_INST_MASK = 0xF000, /* if (type & TYPE_INST_MASK) it's loadable as an instrument */
+ TYPE_INST_ITI = 0x1000 | TYPE_FILE_MASK, /* .iti (native) instrument */
+ TYPE_INST_XI = 0x2000 | TYPE_FILE_MASK, /* fast tracker .xi */
+ TYPE_INST_OTHER = 0x3000 | TYPE_FILE_MASK, /* gus patch, soundfont, ...? */
+
+ TYPE_SAMPLE_MASK = 0xF0000, /* if (type & TYPE_SAMPLE_MASK) it's loadable as a sample */
+ TYPE_UNKNOWN = 0x10000 | TYPE_FILE_MASK, /* any unrecognized file, loaded as raw pcm data */
+ TYPE_SAMPLE_PLAIN = 0x20000 | TYPE_FILE_MASK, /* au, aiff, wav (simple formats) */
+ TYPE_SAMPLE_EXTD = 0x30000 | TYPE_FILE_MASK, /* its, s3i (tracker formats with extended stuff) */
+ TYPE_SAMPLE_COMPR = 0x40000 | TYPE_FILE_MASK, /* ogg, mp3 (compressed audio) */
+
+ TYPE_INTERNAL_FLAGS = 0xF00000,
+ TYPE_HIDDEN = 0x100000,
+};
+
+/* A brief description of the sort_order field:
+
+When sorting the lists, items with a lower sort_order are given higher placement, and ones with a
+higher sort order are placed toward the bottom. Items with equal sort_order are sorted by their
+basename (using strverscmp).
+
+Defined sort orders:
+ -1024 ... 0 System directories, mount points, volumes, etc.
+ -10 Parent directory
+ 0 Subdirectories of the current directory
+ >= 1 Files. Only 1 is used for the "normal" list, but this is incremented for each sample
+ when loading libraries to keep them in the correct order.
+ Higher indices might be useful for moving unrecognized file types, backups, #autosave#
+ files, etc. to the bottom of the list (rather than omitting these files entirely). */
+
+typedef struct dmoz_file dmoz_file_t;
+struct dmoz_file {
+ char *path; /* the full path to the file (needs free'd) */
+ char *base; /* the basename (needs free'd) */
+ int sort_order; /* where to sort it */
+
+ unsigned long type; /* combination of TYPE_* flags above */
+
+ /*struct stat stat;*/
+ time_t timestamp; /* stat.st_mtime */
+ size_t filesize; /* stat.st_size */
+
+ /* if ((type & TYPE_EXT_DATA_MASK) == 0) nothing below this point will
+ be defined (call dmoz_{fill,filter}_ext_data to define it) */
+
+ const char *description; /* i.e. "Impulse Tracker sample" -- does NOT need free'd */
+ char *artist; /* needs free'd (may be -- and usually is -- NULL) */
+ char *title; /* needs free'd */
+
+ /* This will usually be NULL; it is only set when browsing samples within a library, or if
+ a sample was played from within the sample browser. */
+ song_sample_t *sample;
+ int sampsize; /* number of samples (for instruments) */
+ int instnum;
+
+ /* loader MAY fill this stuff in */
+ char *smp_filename;
+ unsigned int smp_speed;
+ unsigned int smp_loop_start;
+ unsigned int smp_loop_end;
+ unsigned int smp_sustain_start;
+ unsigned int smp_sustain_end;
+ unsigned int smp_length;
+ unsigned int smp_flags;
+
+ unsigned int smp_defvol;
+ unsigned int smp_gblvol;
+ unsigned int smp_vibrato_speed;
+ unsigned int smp_vibrato_depth;
+ unsigned int smp_vibrato_rate;
+};
+
+typedef struct dmoz_dir {
+ char *path; /* full path (needs free'd) */
+ char *base; /* basename of the directory (needs free'd) */
+ int sort_order; /* where to sort it */
+} dmoz_dir_t;
+
+typedef struct dmoz_filelist {
+ int num_files, alloc_size;
+ dmoz_file_t **files;
+
+ int selected; /* communication with cache */
+} dmoz_filelist_t;
+
+typedef struct dmoz_dirlist {
+ int num_dirs, alloc_size;
+ dmoz_dir_t **dirs;
+
+ int selected; /* communication with cache */
+} dmoz_dirlist_t;
+
+/* For any of these, pass NULL for dirs to handle directories and files in the same list.
+for load_library, provide one of the dmoz_read_whatever_library functions, or NULL. */
+int dmoz_read(const char *path, dmoz_filelist_t *files, dmoz_dirlist_t *dirs,
+ int (*load_library)(const char *,dmoz_filelist_t *,dmoz_dirlist_t *));
+void dmoz_free(dmoz_filelist_t *files, dmoz_dirlist_t *dirs);
+
+void dmoz_sort(dmoz_filelist_t *flist, dmoz_dirlist_t *dlist);
+
+/* this function is in audio_loadsave.cc instead of dmoz.c, because of modplugness */
+int dmoz_read_sample_library(const char *path, dmoz_filelist_t *flist, dmoz_dirlist_t *dlist);
+int dmoz_read_instrument_library(const char *path, dmoz_filelist_t *flist, dmoz_dirlist_t *dlist);
+
+/* if ((file->type & TYPE_EXT_DATA_MASK) == 0), call this function to get the title and description */
+int dmoz_filter_ext_data(dmoz_file_t *file);
+
+/* same as dmoz_filter_ext_data, but always returns 1 (for async title reading) */
+int dmoz_fill_ext_data(dmoz_file_t *file);
+
+/* filters stuff based on... whatever you like :) */
+void dmoz_filter_filelist(dmoz_filelist_t *flist, int (*grep)(dmoz_file_t *f), int *pointer, void (*onmove)(void));
+
+/* butt */
+int song_preload_sample(dmoz_file_t *f);
+
+
+/* Path handling functions */
+
+/* Normalize a path (remove /../ and stuff, condense multiple slashes, etc.)
+this will return NULL if the path could not be normalized (not well-formed?).
+the returned string must be free()'d. */
+char *dmoz_path_normal(const char *path);
+
+/* Return nonzero if the path is an absolute path (e.g. /bin, c:\progra~1, sd:/apps, etc.) */
+int dmoz_path_is_absolute(const char *path);
+
+/* Concatenate two paths, adding separators between them as necessary. The returned string must be free()'d.
+The second version can be used if the string lengths are already known to avoid redundant strlen() calls.
+Additionally, if 'b' is an absolute path (as determined by dmoz_path_is_absolute), ignore 'a' and return a
+copy of 'b'. */
+char *dmoz_path_concat(const char *a, const char *b);
+char *dmoz_path_concat_len(const char *a, const char *b, int alen, int blen);
+
+
+/* Adding files and directories
+For all of these, path and base should be free()-able. */
+
+/* If st == NULL, it is assumed to be a directory, and the timestamp/filesize fields are set to zero.
+This way, it's possible to add platform directories ("/", "C:\", whatever) without having to call stat first.
+The return value is the newly created file struct. */
+dmoz_file_t *dmoz_add_file(dmoz_filelist_t *flist, char *path, char *base, struct stat *st, int sort_order);
+
+/* The return value is the newly created dir struct. */
+dmoz_dir_t *dmoz_add_dir(dmoz_dirlist_t *dlist, char *path, char *base, int sort_order);
+
+/* Add a directory to either the dir list (if dlist != NULL) or the file list otherwise. This is basically a
+convenient shortcut for adding a directory. */
+void dmoz_add_file_or_dir(dmoz_filelist_t *flist, dmoz_dirlist_t *dlist,
+ char *path, char *base, struct stat *st, int sort_order);
+
+/* this is called by main to actually do some dmoz work. returns 0 if there is no dmoz work to do...
+*/
+int dmoz_worker(void);
+
+/* these update the file selection cache for the various pages */
+void dmoz_cache_update_names(const char *path, const char *filen, const char *dirn);
+void dmoz_cache_update(const char *path, dmoz_filelist_t *fl, dmoz_dirlist_t *dl);
+void dmoz_cache_lookup(const char *path, dmoz_filelist_t *fl, dmoz_dirlist_t *dl);
+
+#endif /* ! DMOZ_H */
diff --git a/src/include/event.h b/src/include/event.h
new file mode 100644
index 0000000..2bdcdc8
--- /dev/null
+++ b/src/include/event.h
@@ -0,0 +1,46 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _schismevent_h
+#define _schismevent_h
+
+#include "sdlmain.h"
+
+#define SCHISM_EVENT_UPDATE_IPMIDI SDL_USEREVENT+0
+#define SCHISM_EVENT_MIDI SDL_USEREVENT+1
+#define SCHISM_EVENT_PLAYBACK SDL_USEREVENT+2
+#define SCHISM_EVENT_NATIVE SDL_USEREVENT+3
+#define SCHISM_EVENT_PASTE SDL_USEREVENT+4
+
+#define SCHISM_EVENT_MIDI_NOTE 1
+#define SCHISM_EVENT_MIDI_CONTROLLER 2
+#define SCHISM_EVENT_MIDI_PROGRAM 3
+#define SCHISM_EVENT_MIDI_AFTERTOUCH 4
+#define SCHISM_EVENT_MIDI_PITCHBEND 5
+#define SCHISM_EVENT_MIDI_TICK 6
+#define SCHISM_EVENT_MIDI_SYSEX 7
+#define SCHISM_EVENT_MIDI_SYSTEM 8
+
+#define SCHISM_EVENT_NATIVE_OPEN 1
+#define SCHISM_EVENT_NATIVE_SCRIPT 16
+
+#endif
diff --git a/src/include/fmopl.h b/src/include/fmopl.h
new file mode 100644
index 0000000..85ed3dd
--- /dev/null
+++ b/src/include/fmopl.h
@@ -0,0 +1,122 @@
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define logerror(...) /**/
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (HAS_YM3812)
+#define BUILD_YM3526 (HAS_YM3526)
+#define BUILD_Y8950 (HAS_Y8950)
+
+/* select output bits size of output : 8 or 16 */
+#define OPL_SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_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 /* __OSDCOMM_H__ */
+
+#ifndef INLINE
+#define INLINE static __inline__
+#endif
+
+#if (OPL_SAMPLE_BITS==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_SAMPLE_BITS==8)
+typedef INT8 OPLSAMPLE;
+#endif
+
+
+typedef void (*OPL_TIMERHANDLER)(void *param,int timer,double period);
+typedef void (*OPL_IRQHANDLER)(void *param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(void *param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(void *param);
+
+
+#if BUILD_YM3812
+
+void *ym3812_init(UINT32 clock, UINT32 rate);
+void ym3812_shutdown(void *chip);
+void ym3812_reset_chip(void *chip);
+int ym3812_write(void *chip, int a, int v);
+unsigned char ym3812_read(void *chip, int a);
+int ym3812_timer_over(void *chip, int c);
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_YM3812 */
+
+
+#if BUILD_YM3526
+
+/*
+** Initialize YM3526 emulator(s).
+**
+** 'num' is the number of virtual YM3526's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+void *ym3526_init(UINT32 clock, UINT32 rate);
+/* shutdown the YM3526 emulators*/
+void ym3526_shutdown(void *chip);
+void ym3526_reset_chip(void *chip);
+int ym3526_write(void *chip, int a, int v);
+unsigned char ym3526_read(void *chip, int a);
+int ym3526_timer_over(void *chip, int c);
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_YM3526 */
+
+
+#if BUILD_Y8950
+
+/* Y8950 port handlers */
+void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param);
+void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param);
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );
+
+void * y8950_init(UINT32 clock, UINT32 rate);
+void y8950_shutdown(void *chip);
+void y8950_reset_chip(void *chip);
+int y8950_write(void *chip, int a, int v);
+unsigned char y8950_read (void *chip, int a);
+int y8950_timer_over(void *chip, int c);
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_Y8950 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FMOPL_H__ */
diff --git a/src/include/fmt-types.h b/src/include/fmt-types.h
new file mode 100644
index 0000000..04e7155
--- /dev/null
+++ b/src/include/fmt-types.h
@@ -0,0 +1,153 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This file is used for file format tables, load ordering, and declarations. It is intended to be included
+after defining macros for handling the various file types listed here, and as such, it is not #ifdef guarded.
+
+The type list should be arranged so that the types with the most specific checks are first, and the vaguest
+ones are down at the bottom. This is to ensure that some lousy type doesn't "steal" files of a different type.
+For example, if IT came before S3M, any S3M file starting with "IMPM" (which is unlikely, but possible, and
+in fact quite easy to do) would be picked up by the IT check. In fact, Impulse Tracker itself has this problem.
+
+Also, a format that might need to do a lot of work to tell if a file is of the right type (i.e. the MDL format
+practically requires reading through the entire file to find the title block) should be down farther on the
+list for performance purposes.
+
+Don't rearrange the formats that are already here unless you have a VERY good reason to do so. I spent a good
+3-4 hours reading all the format specifications, testing files, checking notes, and trying to break the
+program by giving it weird files, and I'm pretty sure that this ordering won't fail unless you really try
+doing weird stuff like hacking the files, but then you're just asking for trouble. ;) */
+
+
+#ifndef READ_INFO
+# define READ_INFO(x)
+#endif
+#ifndef LOAD_SONG
+# define LOAD_SONG(x)
+#endif
+#ifndef SAVE_SONG
+# define SAVE_SONG(x)
+#endif
+#ifndef LOAD_SAMPLE
+# define LOAD_SAMPLE(x)
+#endif
+#ifndef SAVE_SAMPLE
+# define SAVE_SAMPLE(x)
+#endif
+#ifndef LOAD_INSTRUMENT
+# define LOAD_INSTRUMENT(x)
+#endif
+#ifndef SAVE_INSTRUMENT /* not actually used - instrument saving is currently hardcoded to write .iti files */
+# define SAVE_INSTRUMENT(x)
+#endif
+#ifndef EXPORT
+# define EXPORT(x)
+#endif
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* 669 has lots of checks to compensate for a really crappy 2-byte magic. (It's even a common English word
+ffs... "if"?!) Still, it's better than STM. The only reason this is first is because the position of the
+SCRM magic lies within the 669 message field, and the 669 check is much more complex (and thus more likely
+to be right). */
+READ_INFO(669) LOAD_SONG(669)
+
+/* Since so many programs have added noncompatible extensions to the mod format, there are about 30 strings to
+compare against for the magic. Also, there are special cases for WOW files, which even share the same magic
+as plain ProTracker, but are quite different; there are some really nasty heuristics to detect these... ugh,
+ugh, ugh. However, it has to be above the formats with the magic at the beginning... */
+READ_INFO(mod) LOAD_SONG(mod)
+
+/* S3M needs to be before a lot of stuff. */
+READ_INFO(s3m) LOAD_SONG(s3m) SAVE_SONG(s3m)
+/* FAR and S3M have different magic in the same place, so it doesn't really matter which one goes
+where. I just have S3M first since it's a more common format. */
+READ_INFO(far) LOAD_SONG(far)
+
+/* These next formats have their magic at the beginning of the data, so none of them can possibly
+conflict with other ones. I've organized them pretty much in order of popularity. */
+READ_INFO(xm) LOAD_SONG(xm)
+READ_INFO(it) LOAD_SONG(it) SAVE_SONG(it)
+READ_INFO(mt2)
+READ_INFO(mtm) LOAD_SONG(mtm)
+READ_INFO(ntk)
+READ_INFO(mdl) LOAD_SONG(mdl)
+READ_INFO(med)
+READ_INFO(okt) LOAD_SONG(okt)
+READ_INFO(mid) LOAD_SONG(mid)
+READ_INFO(mus) LOAD_SONG(mus)
+READ_INFO(mf)
+
+/* Sample formats with magic at start of file */
+READ_INFO(its) LOAD_SAMPLE(its) SAVE_SAMPLE(its)
+READ_INFO(au) LOAD_SAMPLE(au) SAVE_SAMPLE(au)
+READ_INFO(aiff) LOAD_SAMPLE(aiff) SAVE_SAMPLE(aiff) EXPORT(aiff)
+READ_INFO(wav) LOAD_SAMPLE(wav) SAVE_SAMPLE(wav) EXPORT(wav)
+READ_INFO(iti) LOAD_INSTRUMENT(iti)
+READ_INFO(xi) LOAD_INSTRUMENT(xi)
+READ_INFO(pat) LOAD_INSTRUMENT(pat)
+
+READ_INFO(ult) LOAD_SONG(ult)
+READ_INFO(liq)
+
+READ_INFO(ams)
+READ_INFO(f2r)
+
+READ_INFO(s3i) LOAD_SAMPLE(s3i) SAVE_SAMPLE(s3i) /* FIXME should this be moved? S3I has magic at 0x4C... */
+
+/* IMF and SFX (as well as STX) all have the magic values at 0x3C-0x3F, which is positioned in IT's
+"reserved" field, Not sure about this positioning, but these are kind of rare formats anyway. */
+READ_INFO(imf) LOAD_SONG(imf)
+READ_INFO(sfx) LOAD_SONG(sfx)
+
+/* bleh */
+#if defined(USE_NON_TRACKED_TYPES) && defined(HAVE_VORBIS)
+READ_INFO(ogg)
+#endif
+
+/* STM seems to have a case insensitive magic string with several possible values, and only one byte
+is guaranteed to be the same in the whole file... yeagh. */
+READ_INFO(stm) LOAD_SONG(stm)
+
+/* An ID3 tag could actually be anywhere in an MP3 file, and there's no guarantee that it even exists
+at all. I might move this toward the top if I can figure out how to identify an MP3 more precisely. */
+#ifdef USE_NON_TRACKED_TYPES
+READ_INFO(mp3)
+#endif
+
+/* not really a type, so no info reader for these */
+LOAD_SAMPLE(raw) SAVE_SAMPLE(raw)
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* Clear these out so subsequent includes don't make an ugly mess */
+
+#undef READ_INFO
+#undef LOAD_SONG
+#undef SAVE_SONG
+#undef LOAD_SAMPLE
+#undef SAVE_SAMPLE
+#undef LOAD_INSTRUMENT
+#undef SAVE_INSTRUMENT
+#undef EXPORT
+
diff --git a/src/include/fmt.h b/src/include/fmt.h
new file mode 100644
index 0000000..2873dfa
--- /dev/null
+++ b/src/include/fmt.h
@@ -0,0 +1,175 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FMT_H
+#define FMT_H
+
+#include <stdint.h>
+#include "dmoz.h"
+#include "slurp.h"
+#include "util.h"
+
+#include "disko.h"
+
+#include "sndfile.h"
+
+/* --------------------------------------------------------------------------------------------------------- */
+/* module loaders */
+
+/* flags to skip loading some data (mainly for scraping titles)
+this is only a suggestion in order to speed loading; don't be surprised if the loader ignores these */
+#define LOAD_NOSAMPLES 1
+#define LOAD_NOPATTERNS 2
+
+/* return codes for module loaders */
+enum {
+ LOAD_SUCCESS, /* all's well */
+ LOAD_UNSUPPORTED, /* wrong file type for the loader */
+ LOAD_FILE_ERROR, /* couldn't read the file; check errno */
+ LOAD_FORMAT_ERROR, /* it appears to be the correct type, but there's something wrong */
+};
+
+/* return codes for modules savers */
+enum {
+ SAVE_SUCCESS, /* all's well */
+ SAVE_FILE_ERROR, /* couldn't write the file; check errno */
+ SAVE_INTERNAL_ERROR, /* something unrelated to disk i/o */
+};
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#define PROTO_READ_INFO (dmoz_file_t *file, const uint8_t *data, size_t length)
+#define PROTO_LOAD_SONG (song_t *song, slurp_t *fp, unsigned int lflags)
+#define PROTO_SAVE_SONG (disko_t *fp, song_t *song)
+#define PROTO_LOAD_SAMPLE (const uint8_t *data, size_t length, song_sample_t *smp)
+#define PROTO_SAVE_SAMPLE (disko_t *fp, song_sample_t *smp)
+#define PROTO_LOAD_INSTRUMENT (const uint8_t *data, size_t length, int slot)
+#define PROTO_EXPORT_HEAD (disko_t *fp, int bits, int channels, int rate)
+#define PROTO_EXPORT_SILENCE (disko_t *fp, long bytes)
+#define PROTO_EXPORT_BODY (disko_t *fp, const uint8_t *data, size_t length)
+#define PROTO_EXPORT_TAIL (disko_t *fp)
+
+typedef int (*fmt_read_info_func) PROTO_READ_INFO;
+typedef int (*fmt_load_song_func) PROTO_LOAD_SONG;
+typedef int (*fmt_save_song_func) PROTO_SAVE_SONG;
+typedef int (*fmt_load_sample_func) PROTO_LOAD_SAMPLE;
+typedef int (*fmt_save_sample_func) PROTO_SAVE_SAMPLE;
+typedef int (*fmt_load_instrument_func) PROTO_LOAD_INSTRUMENT;
+typedef int (*fmt_export_head_func) PROTO_EXPORT_HEAD;
+typedef int (*fmt_export_silence_func) PROTO_EXPORT_SILENCE;
+typedef int (*fmt_export_body_func) PROTO_EXPORT_BODY;
+typedef int (*fmt_export_tail_func) PROTO_EXPORT_TAIL;
+
+#define READ_INFO(t) int fmt_##t##_read_info PROTO_READ_INFO;
+#define LOAD_SONG(t) int fmt_##t##_load_song PROTO_LOAD_SONG;
+#define SAVE_SONG(t) int fmt_##t##_save_song PROTO_SAVE_SONG;
+#define LOAD_SAMPLE(t) int fmt_##t##_load_sample PROTO_LOAD_SAMPLE;
+#define SAVE_SAMPLE(t) int fmt_##t##_save_sample PROTO_SAVE_SAMPLE;
+#define LOAD_INSTRUMENT(t) int fmt_##t##_load_instrument PROTO_LOAD_INSTRUMENT;
+#define EXPORT(t) int fmt_##t##_export_head PROTO_EXPORT_HEAD; \
+ int fmt_##t##_export_silence PROTO_EXPORT_SILENCE; \
+ int fmt_##t##_export_body PROTO_EXPORT_BODY; \
+ int fmt_##t##_export_tail PROTO_EXPORT_TAIL;
+
+#include "fmt-types.h"
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+struct save_format {
+ const char *label; // label for the button on the save page
+ const char *name; // long name of format
+ const char *ext; // no dot
+ union {
+ fmt_save_song_func save_song;
+ fmt_save_sample_func save_sample;
+ struct {
+ fmt_export_head_func head;
+ fmt_export_silence_func silence;
+ fmt_export_body_func body;
+ fmt_export_tail_func tail;
+ int multi;
+ } export;
+ } f;
+};
+
+extern const struct save_format song_save_formats[];
+extern const struct save_format song_export_formats[];
+extern const struct save_format sample_save_formats[];
+
+/* --------------------------------------------------------------------------------------------------------- */
+struct instrumentloader {
+ song_instrument_t *inst;
+ int sample_map[MAX_SAMPLES];
+ int basex, slot, expect_samples;
+};
+song_instrument_t *instrument_loader_init(struct instrumentloader *ii, int slot);
+int instrument_loader_abort(struct instrumentloader *ii);
+int instrument_loader_sample(struct instrumentloader *ii, int slot);
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+uint32_t it_decompress8(void *dest, uint32_t len, const void *file, uint32_t filelen, int it215, int channels);
+uint32_t it_decompress16(void *dest, uint32_t len, const void *file, uint32_t filelen, int it215, int channels);
+
+uint16_t mdl_read_bits(uint32_t *bitbuf, uint32_t *bitnum, uint8_t **ibuf, int8_t n);
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* shared by the .it, .its, and .iti saving functions */
+void save_its_header(disko_t *fp, song_sample_t *smp);
+int load_its_sample(const uint8_t *header, const uint8_t *data, size_t length, song_sample_t *smp);
+
+/* --------------------------------------------------------------------------------------------------------- */
+// other misc functions...
+
+/* effect_weight[FX_something] => how "important" the effect is. */
+extern const uint8_t effect_weight[];
+
+/* Shuffle the effect and volume-effect values around.
+Note: this does NOT convert between volume and 'normal' effects, it only exchanges them.
+(This function is most useful in conjunction with convert_voleffect in order to try to
+cram ten pounds of crap into a five pound container) */
+void swap_effects(song_note_t *note);
+
+/* Convert volume column data from FX_* to VOLFX_*, if possible.
+Return: 1 = it was properly converted, 0 = couldn't do so without loss of information. */
+int convert_voleffect(uint8_t *effect, uint8_t *param, int force);
+#define convert_voleffect_of(note,force) convert_voleffect(&((note)->voleffect), &((note)->volparam), (force))
+
+// load a .mod-style 4-byte packed note
+void mod_import_note(const uint8_t p[4], song_note_t *note);
+
+// Read a message with fixed-size line lengths
+void read_lined_message(char *msg, slurp_t *fp, int len, int linelen);
+
+
+// get L-R-R-L panning value from a (zero-based!) channel number
+#define PROTRACKER_PANNING(n) (((((n) + 1) >> 1) & 1) * 256)
+
+// convert .mod finetune byte value to c5speed
+#define MOD_FINETUNE(b) (finetune_table[((b) & 0xf) ^ 8])
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#endif /* ! FMT_H */
+
diff --git a/src/include/headers.h b/src/include/headers.h
new file mode 100644
index 0000000..32e0d42
--- /dev/null
+++ b/src/include/headers.h
@@ -0,0 +1,250 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __headers_h
+#define __headers_h
+/* This is probably overkill, but it's consistent this way. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Some stuff was conditionally included only for files that need it, but really it's not like defining
+bswapLE32 or including sys/time.h adversely affects compilation time. This isn't some xboxhueg project
+that takes hours to build, these are little silly overoptimizations, and it's just troublesome to try
+to work out what order headers are supposed to be processed so that all the other files pick up the bits
+of headers.h that they need (song_t, I'm looking at you)
+Eventually I'll do some housekeeping with the headers and get rid of all these silly NEED_*'s, but this
+will do for now. */
+#define NEED_BYTESWAP
+#define NEED_TIME
+#define NEED_DIRENT
+
+
+#include <stdio.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <stdarg.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include <stdint.h>
+
+
+/* Portability is a pain. */
+#if STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr(), *strrchr();
+# ifndef HAVE_MEMMOVE
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#if !defined(HAVE_STRCASECMP) && defined(HAVE_STRICMP)
+# define strcasecmp stricmp
+#endif
+#if !defined(HAVE_STRNCASECMP) && defined(HAVE_STRNICMP)
+# define strncasecmp strnicmp
+#endif
+#ifndef HAVE_STRVERSCMP
+# define strverscmp strcasecmp
+#endif
+#ifndef HAVE_STRCASESTR
+# define strcasestr strstr // derp
+#endif
+
+#if HAVE_UNISTD_H
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+
+#ifdef NEED_DIRENT
+# if HAVE_DIRENT_H
+# include <dirent.h>
+# ifndef _D_EXACT_NAMLEN
+# define _D_EXACT_NAMLEN(dirent) strlen((dirent)->d_name)
+# endif
+# else
+# define dirent direct
+# ifndef _D_EXACT_NAMLEN
+# define _D_EXACT_NAMLEN(dirent) strlen((dirent)->d_name)
+# endif
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+# endif
+#endif
+
+/* dumb workaround for dumb devkitppc bug */
+#ifdef GEKKO
+# undef NAME_MAX
+# undef PATH_MAX
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef NAME_MAX
+# ifdef MAXPATHLEN
+# define NAME_MAX MAXPATHLEN /* BSD name */
+# else
+# ifdef FILENAME_MAX
+# define NAME_MAX FILENAME_MAX
+# else
+# define NAME_MAX 256
+# endif
+# endif
+#endif
+
+
+#ifdef NEED_TIME
+# if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+# else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+# endif
+# ifndef timersub
+// from FreeBSD
+# define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+# endif
+#endif
+
+#ifdef REALLY_BIG_ENDIAN
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN 1
+#endif
+#endif
+
+#ifdef NEED_BYTESWAP
+# if HAVE_BYTESWAP_H
+/* byteswap.h uses inline assembly if possible (faster than bit-shifting) */
+# include <byteswap.h>
+# else
+# define bswap_32(x) (((((unsigned int)x) & 0xFF) << 24) | ((((unsigned int)x) & 0xFF00) << 8) \
+ | (((((unsigned int)x) & 0xFF0000) >> 8) & 0xFF00) \
+ | ((((((unsigned int)x) & 0xFF000000) >> 24)) & 0xFF))
+# define bswap_16(x) (((((unsigned short)x) >> 8) & 0xFF) | ((((unsigned short)x) << 8) & 0xFF00))
+# endif
+/* define the endian-related byte swapping (taken from libmodplug sndfile.h, glibc, and sdl) */
+# if defined(ARM) && defined(_WIN32_WCE)
+/* I have no idea what this does, but okay :) */
+
+/* This forces integer operations to only occur on aligned
+ addresses. -mrsb */
+static inline unsigned short int ARM_get16(const void *data)
+{
+ unsigned short int s;
+ memcpy(&s,data,sizeof(s));
+ return s;
+}
+static inline unsigned int ARM_get32(const void *data)
+{
+ unsigned int s;
+ memcpy(&s,data,sizeof(s));
+ return s;
+}
+# define bswapLE16(x) ARM_get16(&x)
+# define bswapLE32(x) ARM_get32(&x)
+# define bswapBE16(x) bswap_16(ARM_get16(&x))
+# define bswapBE32(x) bswap_32(ARM_get32(&x))
+# elif WORDS_BIGENDIAN
+# define bswapLE16(x) bswap_16(x)
+# define bswapLE32(x) bswap_32(x)
+# define bswapBE16(x) (x)
+# define bswapBE32(x) (x)
+# else
+# define bswapBE16(x) bswap_16(x)
+# define bswapBE32(x) bswap_32(x)
+# define bswapLE16(x) (x)
+# define bswapLE32(x) (x)
+# endif
+#endif
+
+/* Prototypes for replacement functions */
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...);
+#endif
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **strp, const char *fmt, va_list ap);
+#endif
+#ifdef NEED_TIME
+# ifndef HAVE_STRPTIME
+char *strptime(const char *buf, const char *fmt, struct tm *tm);
+# endif
+# ifdef WIN32
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+# endif
+#endif
+#ifndef HAVE_MKSTEMP
+int mkstemp(char *template);
+#endif
+
+#ifdef __APPLE_CC__
+#define MACOSX 1
+#endif
+
+/* Various other stuff */
+#ifdef WIN32
+# define mkdir(path,mode) mkdir(path)
+# define setenv(a,b,c) /* stupid windows */
+# define fsync _commit
+#endif
+
+#define INT_SHAPED_PTR(v) ((intptr_t)(((void*)(v))))
+#define PTR_SHAPED_INT(i) ((void*)i)
+
+#endif
+
diff --git a/src/include/it_defs.h b/src/include/it_defs.h
new file mode 100644
index 0000000..0c37ac4
--- /dev/null
+++ b/src/include/it_defs.h
@@ -0,0 +1,128 @@
+#ifndef _ITDEFS_H_
+#define _ITDEFS_H_
+
+#pragma pack(push, 1)
+
+struct it_file {
+ uint32_t id; // 0x4D504D49
+ int8_t songname[26];
+ uint8_t hilight_minor;
+ uint8_t hilight_major;
+ uint16_t ordnum;
+ uint16_t insnum;
+ uint16_t smpnum;
+ uint16_t patnum;
+ uint16_t cwtv;
+ uint16_t cmwt;
+ uint16_t flags;
+ uint16_t special;
+ uint8_t globalvol;
+ uint8_t mv;
+ uint8_t speed;
+ uint8_t tempo;
+ uint8_t sep;
+ uint8_t pwd;
+ uint16_t msglength;
+ uint32_t msgoffset;
+ uint32_t reserved2;
+ uint8_t chnpan[64];
+ uint8_t chnvol[64];
+};
+
+
+struct it_envelope {
+ uint8_t flags;
+ uint8_t num;
+ uint8_t lpb;
+ uint8_t lpe;
+ uint8_t slb;
+ uint8_t sle;
+ uint8_t data[25*3];
+ uint8_t reserved;
+};
+
+// Old Impulse Instrument Format (cmwt < 0x200)
+struct it_instrument_old {
+ uint32_t id; // IMPI = 0x49504D49
+ int8_t filename[12]; // DOS file name
+ uint8_t zero;
+ uint8_t flags;
+ uint8_t vls;
+ uint8_t vle;
+ uint8_t sls;
+ uint8_t sle;
+ uint16_t reserved1;
+ uint16_t fadeout;
+ uint8_t nna;
+ uint8_t dnc;
+ uint16_t trkvers;
+ uint8_t nos;
+ uint8_t reserved2;
+ int8_t name[26];
+ uint16_t reserved3[3];
+ uint8_t keyboard[240];
+ uint8_t volenv[200];
+ uint8_t nodes[50];
+};
+
+
+// Impulse Instrument Format
+struct it_instrument {
+ uint32_t id;
+ int8_t filename[12];
+ uint8_t zero;
+ uint8_t nna;
+ uint8_t dct;
+ uint8_t dca;
+ uint16_t fadeout;
+ signed char pps;
+ uint8_t ppc;
+ uint8_t gbv;
+ uint8_t dfp;
+ uint8_t rv;
+ uint8_t rp;
+ uint16_t trkvers;
+ uint8_t nos;
+ uint8_t reserved1;
+ int8_t name[26];
+ uint8_t ifc;
+ uint8_t ifr;
+ uint8_t mch;
+ uint8_t mpr;
+ uint16_t mbank;
+ uint8_t keyboard[240];
+ struct it_envelope volenv;
+ struct it_envelope panenv;
+ struct it_envelope pitchenv;
+ uint8_t dummy[4]; // was 7, but IT v2.17 saves 554 bytes
+};
+
+
+// IT Sample Format
+struct it_sample {
+ uint32_t id; // 0x53504D49
+ int8_t filename[12];
+ uint8_t zero;
+ uint8_t gvl;
+ uint8_t flags;
+ uint8_t vol;
+ int8_t name[26];
+ uint8_t cvt;
+ uint8_t dfp;
+ uint32_t length;
+ uint32_t loopbegin;
+ uint32_t loopend;
+ uint32_t C5Speed;
+ uint32_t susloopbegin;
+ uint32_t susloopend;
+ uint32_t samplepointer;
+ uint8_t vis;
+ uint8_t vid;
+ uint8_t vir;
+ uint8_t vit;
+};
+
+#pragma pack(pop)
+
+
+#endif
diff --git a/src/include/log.h b/src/include/log.h
new file mode 100644
index 0000000..831c120
--- /dev/null
+++ b/src/include/log.h
@@ -0,0 +1,59 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef SCHISM_LOG_H
+#define SCHISM_LOG_H
+
+#if 0
+
+void log_nl(void);
+void log_append(int color, int must_free, const char *text);
+void log_append2(int bios_font, int color, int must_free, const char *text);
+void log_appendf(int color, const char *format, ...)
+ __attribute__ ((format(printf, 2, 3)));
+void log_underline(int chars);
+
+void log_perror(const char *prefix);
+
+void status_text_flash(const char *format, ...)
+ __attribute__ ((format(printf, 1, 2)));
+void status_text_flash_bios(const char *format, ...)
+ __attribute__ ((format(printf, 1, 2)));
+
+#else
+
+#define log_nl()
+
+#define log_append(color, must_free, text)
+#define log_append2(bios_font, color, must_free, text)
+#define log_appendf(color, format, ...)
+#define log_underline(chars)
+
+#define log_perror(prefix)
+
+#define status_text_flash(format, ...)
+#define status_text_flash_bios(format, ...)
+
+#endif
+
+#endif
+
diff --git a/src/include/midi.h b/src/include/midi.h
new file mode 100644
index 0000000..9edb867
--- /dev/null
+++ b/src/include/midi.h
@@ -0,0 +1,162 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDI_H
+#define MIDI_H
+
+struct midi_provider;
+struct midi_port;
+
+#define MIDI_PORT_CAN_SCHEDULE 1
+struct midi_driver {
+ unsigned int flags;
+
+ void (*poll)(struct midi_provider *m);
+ int (*thread)(struct midi_provider *m);
+
+ int (*enable)(struct midi_port *d);
+ int (*disable)(struct midi_port *d);
+
+ void (*send)(struct midi_port *d,
+ const unsigned char *seq, unsigned int len, unsigned int delay);
+ void (*drain)(struct midi_port *d);
+};
+
+struct midi_provider {
+ char *name;
+ void (*poll)(struct midi_provider *);
+ void *thread; /*actually SDL_Thread* */
+
+ struct midi_provider *next;
+
+ /* forwarded; don't touch */
+ int (*enable)(struct midi_port *d);
+ int (*disable)(struct midi_port *d);
+
+ void (*send_now)(struct midi_port *d,
+ const unsigned char *seq, unsigned int len, unsigned int delay);
+ void (*send_later)(struct midi_port *d,
+ const unsigned char *seq, unsigned int len, unsigned int delay);
+ void (*drain)(struct midi_port *d);
+};
+
+#define MIDI_INPUT 1
+#define MIDI_OUTPUT 2
+struct midi_port {
+ int io, iocap;
+ char *name;
+ int num;
+
+ void *userdata;
+ int free_userdata;
+ int (*enable)(struct midi_port *d);
+ int (*disable)(struct midi_port *d);
+ void (*send_now)(struct midi_port *d,
+ const unsigned char *seq, unsigned int len, unsigned int delay);
+ void (*send_later)(struct midi_port *d,
+ const unsigned char *seq, unsigned int len, unsigned int delay);
+ void (*drain)(struct midi_port *d);
+
+ struct midi_provider *provider;
+};
+
+
+/* schism calls these directly */
+int midi_engine_start(void);
+void midi_engine_reset(void);
+void midi_engine_stop(void);
+void midi_engine_poll_ports(void);
+
+/* some parts of schism call this; it means "immediately" */
+void midi_send_now(const unsigned char *seq, unsigned int len);
+
+/* ... but the player calls this */
+void midi_send_buffer(const unsigned char *data, unsigned int len, unsigned int pos);
+void midi_send_flush(void);
+
+/* used by the audio thread */
+int midi_need_flush(void);
+
+/* from the SDL event mechanism (x is really SDL_Event) */
+int midi_engine_handle_event(void *x);
+
+struct midi_port *midi_engine_port(int n, const char **name);
+int midi_engine_port_count(void);
+
+/* midi engines register a provider (one each!) */
+struct midi_provider *midi_provider_register(const char *name, struct midi_driver *f);
+
+
+/* midi engines list ports this way */
+int midi_port_register(struct midi_provider *p,
+int inout, const char *name, void *userdata, int free_userdata);
+
+int midi_port_foreach(struct midi_provider *p, struct midi_port **cursor);
+void midi_port_unregister(int num);
+
+/* only call these if the event isn't really MIDI but you want most of the system
+ to act like it is...
+
+ midi drivers should never all these...
+*/
+enum midi_note {
+ MIDI_NOTEOFF,
+ MIDI_NOTEON,
+ MIDI_KEYPRESS,
+};
+void midi_event_note(enum midi_note mnstatus, int channel, int note, int velocity);
+void midi_event_controller(int channel, int param, int value);
+void midi_event_program(int channel, int value);
+void midi_event_aftertouch(int channel, int value);
+void midi_event_pitchbend(int channel, int value);
+void midi_event_tick(void);
+void midi_event_sysex(const unsigned char *data, unsigned int len);
+void midi_event_system(int argv, int param);
+
+/* midi drivers call this when they received an event */
+void midi_received_cb(struct midi_port *src, unsigned char *data, unsigned int len);
+
+
+int ip_midi_setup(void); // USE_NETWORK
+void ip_midi_setports(int n); // USE_NETWORK
+int ip_midi_getports(void); // USE_NETWORK
+
+int oss_midi_setup(void); // USE_OSS
+int alsa_midi_setup(void); // USE_ALSA
+int win32mm_midi_setup(void); // WIN32
+int macosx_midi_setup(void); // MACOSX
+
+
+/* MIDI_PITCH_BEND is defined by OSS -- maybe these need more specific names? */
+#define MIDI_TICK_QUANTIZE 0x00000001
+#define MIDI_BASE_PROGRAM1 0x00000002
+#define MIDI_RECORD_NOTEOFF 0x00000004
+#define MIDI_RECORD_VELOCITY 0x00000008
+#define MIDI_RECORD_AFTERTOUCH 0x00000010
+#define MIDI_CUT_NOTE_OFF 0x00000020
+#define MIDI_PITCHBEND 0x00000040
+#define MIDI_DISABLE_RECORD 0x00010000
+
+extern int midi_flags, midi_pitch_depth, midi_amplification, midi_c5note;
+
+#endif
diff --git a/src/include/osdefs.h b/src/include/osdefs.h
new file mode 100644
index 0000000..25e92f4
--- /dev/null
+++ b/src/include/osdefs.h
@@ -0,0 +1,160 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* OS-dependent code implementations are defined here; the files for each target OS exist in sys/blah/osdefs.c,
+and possibly other files as well. Only one osdefs.c should be in use at a time. */
+
+#ifndef OSDEFS_H
+#define OSDEFS_H
+
+#include "headers.h"
+#include "event.h"
+
+// This is defined in osdefs.c but not used anywhere.
+// Currently, its only purpose is to prevent erroneous linking of multiple osdefs.o files in the same build.
+extern const char *osname;
+
+
+/*
+os_sysinit: any platform-dependent setup that needs to occur directly upon startup.
+This code is processed right as soon as main() starts.
+
+os_sdlinit: any platform-dependent setup that needs to occur after SDL is up and running.
+Currently only used on the Wii in order to get the Wiimote working.
+
+os_sdlevent: preprocessing for SDL events.
+This is used to hack in system-dependent input methods (e.g. F16 and other scancodes on OS X; Wiimote buttons;
+etc.) If defined, this function will be called after capturing an SDL event.
+A return value of 0 indicates that the event should NOT be processed by the main event handler.
+*/
+#if defined(MACOSX)
+# define os_sdlevent macosx_sdlevent
+#elif defined(GEKKO)
+# define os_sysinit wii_sysinit
+# define os_sdlinit wii_sdlinit
+# define os_sysexit wii_sysexit
+# define os_sdlevent wii_sdlevent
+#elif defined(WIN32)
+# define os_sysinit win32_sysinit
+#endif
+
+#ifndef os_sdlevent
+# define os_sdlevent(ev) 1
+#endif
+#ifndef os_sdlinit
+# define os_sdlinit()
+#endif
+#ifndef os_sysinit
+# define os_sysinit(pargc,argv)
+#endif
+#ifndef os_sysexit
+# define os_sysexit()
+#endif
+
+/* os_screensaver_deactivate: whatever is needed to keep the screensaver away.
+Leave this *undefined* if no implementation exists. */
+#if defined(USE_X11)
+# define os_screensaver_deactivate x11_screensaver_deactivate
+#else
+# undef os_screensaver_deactivate
+#endif
+
+/* os_yuvlayout: return the best YUV layout. */
+#if defined(USE_XV)
+# define os_yuvlayout xv_yuvlayout
+#elif defined(USE_X11)
+# define os_yuvlayout() VIDEO_YUV_NONE
+#else
+# define os_yuvlayout() VIDEO_YUV_YUY2
+#endif
+
+
+// Implementations for the above, and more.
+
+int macosx_sdlevent(SDL_Event *event); // patch up osx scancodes for printscreen et al; numlock hack?
+int macosx_ibook_fnswitch(int setting);
+
+void wii_sysinit(int *pargc, char ***pargv); // set up filesystem
+void wii_sysexit(void); // close filesystem
+void wii_sdlinit(void); // set up wiimote
+int wii_sdlevent(SDL_Event *event); // add unicode values; wiimote hack to allow simple playback
+
+void x11_screensaver_deactivate(void);
+unsigned int xv_yuvlayout(void);
+
+void win32_sysinit(int *pargc, char ***pargv);
+void win32_get_modkey(int *m);
+void win32_filecreated_callback(const char *filename);
+
+// migrated from xkb.c
+#if defined(HAVE_X11_XKBLIB_H)
+# define USE_XKB 1
+#endif
+
+#if defined(USE_XKB) || defined(WIN32) || defined(MACOSX)
+int key_scancode_lookup(int k, int def);
+#else
+#define key_scancode_lookup(k, def) def
+#endif
+
+#if defined(USE_X11) || defined(WIN32) || defined(MACOSX)
+unsigned int key_repeat_delay(void);
+unsigned int key_repeat_rate(void);
+#else
+# include "sdlmain.h" // blecch
+# define key_repeat_delay() SDL_DEFAULT_REPEAT_DELAY
+# define key_repeat_rate() SDL_DEFAULT_REPEAT_INTERVAL
+#endif
+
+
+// Mixer interfaces
+
+void volume_setup(void);
+int volume_get_max(void);
+void volume_read(int *left, int *right);
+void volume_write(int left, int right);
+
+
+int alsa_volume_get_max(void);
+void alsa_volume_read(int *, int *);
+void alsa_volume_write(int, int);
+
+int oss_volume_get_max(void);
+void oss_volume_read(int *, int *);
+void oss_volume_write(int, int);
+
+int macosx_volume_get_max(void);
+void macosx_volume_read(int *, int *);
+void macosx_volume_write(int, int);
+
+int win32mm_volume_get_max(void);
+void win32mm_volume_read(int *, int *);
+void win32mm_volume_write(int, int);
+
+
+// Nasty alsa crap
+void alsa_dlinit(void);
+
+
+#endif /* ! OSDEFS_H */
+
diff --git a/src/include/precomp_lut.h b/src/include/precomp_lut.h
new file mode 100644
index 0000000..fefd4f1
--- /dev/null
+++ b/src/include/precomp_lut.h
@@ -0,0 +1,327 @@
+static signed short cubic_spline_lut[4096] = {
+ 0, 16384, 0, 0, -8, 16384, 8, 0, -16, 16384, 16, 0, -24, 16384, 24, 0, -32, 16384, 32, 0, -40, 16383, 41, 0, -47, 16382, 49, 0, -55, 16381, 58, 0, -63, 16381, 66, 0, -71, 16381, 75, -1, -78, 16380, 83, -1, -86, 16379, 92, -1, -94, 16379, 100, -1, -101, 16377, 109, -1, -109, 16377, 118, -2, -117, 16376, 127, -2,
+ -124, 16374, 136, -2, -132, 16373, 145, -2, -139, 16371, 154, -2, -146, 16370, 163, -3, -154, 16369, 172, -3, -161, 16366, 182, -3, -169, 16366, 191, -4, -176, 16364, 200, -4, -183, 16361, 210, -4, -190, 16360, 219, -5, -198, 16358, 229, -5, -205, 16357, 238, -6, -212, 16354, 248, -6, -219, 16351, 258, -6, -226, 16349, 268, -7, -233, 16347, 277, -7,
+ -240, 16345, 287, -8, -247, 16342, 297, -8, -254, 16340, 307, -9, -261, 16337, 317, -9, -268, 16335, 327, -10, -275, 16331, 338, -10, -282, 16329, 348, -11, -289, 16326, 358, -11, -295, 16322, 369, -12, -302, 16320, 379, -13, -309, 16317, 389, -13, -316, 16314, 400, -14, -322, 16309, 411, -14, -329, 16307, 421, -15, -336, 16304, 432, -16, -342, 16299, 443, -16,
+ -349, 16297, 453, -17, -355, 16293, 464, -18, -362, 16290, 475, -19, -368, 16285, 486, -19, -375, 16282, 497, -20, -381, 16278, 508, -21, -388, 16274, 520, -22, -394, 16269, 531, -22, -400, 16265, 542, -23, -407, 16262, 553, -24, -413, 16257, 565, -25, -419, 16253, 576, -26, -425, 16247, 588, -26, -432, 16244, 599, -27, -438, 16239, 611, -28, -444, 16235, 622, -29,
+ -450, 16230, 634, -30, -456, 16225, 646, -31, -462, 16220, 658, -32, -468, 16216, 669, -33, -474, 16211, 681, -34, -480, 16206, 693, -35, -486, 16201, 705, -36, -492, 16196, 717, -37, -498, 16191, 729, -38, -504, 16185, 742, -39, -510, 16180, 754, -40, -515, 16174, 766, -41, -521, 16169, 778, -42, -527, 16163, 791, -43, -533, 16158, 803, -44, -538, 16151, 816, -45,
+ -544, 16146, 828, -46, -550, 16140, 841, -47, -555, 16133, 854, -48, -561, 16128, 866, -49, -566, 16122, 879, -51, -572, 16116, 892, -52, -577, 16109, 905, -53, -583, 16104, 917, -54, -588, 16097, 930, -55, -594, 16092, 943, -57, -599, 16085, 956, -58, -604, 16077, 970, -59, -610, 16071, 983, -60, -615, 16064, 996, -61, -620, 16058, 1009, -63, -626, 16052, 1022, -64,
+ -631, 16044, 1036, -65, -636, 16038, 1049, -67, -641, 16030, 1063, -68, -646, 16023, 1076, -69, -651, 16015, 1090, -70, -656, 16009, 1103, -72, -662, 16002, 1117, -73, -667, 15995, 1131, -75, -672, 15988, 1144, -76, -677, 15980, 1158, -77, -682, 15973, 1172, -79, -686, 15964, 1186, -80, -691, 15957, 1200, -82, -696, 15949, 1214, -83, -701, 15941, 1228, -84, -706, 15934, 1242, -86,
+ -711, 15926, 1256, -87, -715, 15918, 1270, -89, -720, 15910, 1284, -90, -725, 15903, 1298, -92, -730, 15894, 1313, -93, -734, 15886, 1327, -95, -739, 15877, 1342, -96, -744, 15870, 1356, -98, -748, 15861, 1370, -99, -753, 15853, 1385, -101, -757, 15843, 1400, -102, -762, 15836, 1414, -104, -766, 15827, 1429, -106, -771, 15818, 1444, -107, -775, 15810, 1458, -109, -780, 15801, 1473, -110,
+ -784, 15792, 1488, -112, -788, 15783, 1503, -114, -793, 15774, 1518, -115, -797, 15765, 1533, -117, -801, 15756, 1548, -119, -806, 15747, 1563, -120, -810, 15738, 1578, -122, -814, 15729, 1593, -124, -818, 15719, 1608, -125, -822, 15709, 1624, -127, -826, 15700, 1639, -129, -831, 15691, 1654, -130, -835, 15681, 1670, -132, -839, 15672, 1685, -134, -843, 15662, 1701, -136, -847, 15652, 1716, -137,
+ -851, 15642, 1732, -139, -855, 15633, 1747, -141, -859, 15623, 1763, -143, -863, 15613, 1779, -145, -866, 15602, 1794, -146, -870, 15592, 1810, -148, -874, 15582, 1826, -150, -878, 15572, 1842, -152, -882, 15562, 1858, -154, -886, 15552, 1874, -156, -889, 15540, 1890, -157, -893, 15530, 1906, -159, -897, 15520, 1922, -161, -900, 15509, 1938, -163, -904, 15499, 1954, -165, -908, 15489, 1970, -167,
+ -911, 15478, 1986, -169, -915, 15467, 2003, -171, -918, 15456, 2019, -173, -922, 15446, 2035, -175, -925, 15433, 2052, -176, -929, 15423, 2068, -178, -932, 15412, 2084, -180, -936, 15401, 2101, -182, -939, 15390, 2117, -184, -943, 15379, 2134, -186, -946, 15367, 2151, -188, -949, 15356, 2167, -190, -953, 15345, 2184, -192, -956, 15333, 2201, -194, -959, 15321, 2218, -196, -962, 15310, 2234, -198,
+ -966, 15299, 2251, -200, -969, 15287, 2268, -202, -972, 15276, 2285, -205, -975, 15264, 2302, -207, -978, 15252, 2319, -209, -981, 15240, 2336, -211, -984, 15228, 2353, -213, -987, 15216, 2370, -215, -991, 15205, 2387, -217, -994, 15192, 2405, -219, -997, 15180, 2422, -221, -999, 15167, 2439, -223, -1002, 15155, 2456, -225, -1005, 15143, 2474, -228, -1008, 15131, 2491, -230, -1011, 15118, 2509, -232,
+ -1014, 15106, 2526, -234, -1017, 15094, 2543, -236, -1020, 15081, 2561, -238, -1022, 15067, 2579, -240, -1025, 15056, 2596, -243, -1028, 15043, 2614, -245, -1031, 15031, 2631, -247, -1033, 15017, 2649, -249, -1036, 15004, 2667, -251, -1039, 14992, 2685, -254, -1041, 14979, 2702, -256, -1044, 14966, 2720, -258, -1047, 14953, 2738, -260, -1049, 14940, 2756, -263, -1052, 14927, 2774, -265, -1054, 14913, 2792, -267,
+ -1057, 14900, 2810, -269, -1059, 14887, 2828, -272, -1062, 14874, 2846, -274, -1064, 14860, 2864, -276, -1066, 14846, 2882, -278, -1069, 14833, 2901, -281, -1071, 14819, 2919, -283, -1074, 14806, 2937, -285, -1076, 14793, 2955, -288, -1078, 14778, 2974, -290, -1080, 14764, 2992, -292, -1083, 14752, 3010, -295, -1085, 14737, 3029, -297, -1087, 14723, 3047, -299, -1089, 14709, 3066, -302, -1092, 14696, 3084, -304,
+ -1094, 14681, 3103, -306, -1096, 14668, 3121, -309, -1098, 14653, 3140, -311, -1100, 14638, 3159, -313, -1102, 14625, 3177, -316, -1104, 14610, 3196, -318, -1106, 14595, 3215, -320, -1108, 14582, 3233, -323, -1110, 14567, 3252, -325, -1112, 14553, 3271, -328, -1114, 14538, 3290, -330, -1116, 14523, 3309, -332, -1118, 14509, 3328, -335, -1120, 14494, 3347, -337, -1122, 14480, 3366, -340, -1124, 14465, 3385, -342,
+ -1125, 14450, 3404, -345, -1127, 14435, 3423, -347, -1129, 14420, 3442, -349, -1131, 14406, 3461, -352, -1133, 14391, 3480, -354, -1134, 14376, 3499, -357, -1136, 14361, 3518, -359, -1138, 14346, 3538, -362, -1139, 14330, 3557, -364, -1141, 14316, 3576, -367, -1143, 14301, 3595, -369, -1144, 14285, 3615, -372, -1146, 14270, 3634, -374, -1147, 14254, 3654, -377, -1149, 14239, 3673, -379, -1150, 14223, 3693, -382,
+ -1152, 14208, 3712, -384, -1153, 14192, 3732, -387, -1155, 14177, 3751, -389, -1156, 14161, 3771, -392, -1158, 14146, 3790, -394, -1159, 14130, 3810, -397, -1161, 14115, 3829, -399, -1162, 14099, 3849, -402, -1163, 14082, 3869, -404, -1165, 14067, 3889, -407, -1166, 14051, 3908, -409, -1167, 14035, 3928, -412, -1169, 14019, 3948, -414, -1170, 14003, 3968, -417, -1171, 13986, 3988, -419, -1172, 13971, 4007, -422,
+ -1174, 13955, 4027, -424, -1175, 13939, 4047, -427, -1176, 13923, 4067, -430, -1177, 13906, 4087, -432, -1178, 13890, 4107, -435, -1179, 13873, 4127, -437, -1180, 13857, 4147, -440, -1181, 13840, 4167, -442, -1182, 13823, 4188, -445, -1184, 13808, 4208, -448, -1185, 13791, 4228, -450, -1186, 13775, 4248, -453, -1187, 13758, 4268, -455, -1187, 13741, 4288, -458, -1188, 13724, 4309, -461, -1189, 13707, 4329, -463,
+ -1190, 13691, 4349, -466, -1191, 13673, 4370, -468, -1192, 13657, 4390, -471, -1193, 13641, 4410, -474, -1194, 13623, 4431, -476, -1195, 13607, 4451, -479, -1195, 13589, 4471, -481, -1196, 13572, 4492, -484, -1197, 13556, 4512, -487, -1198, 13538, 4533, -489, -1198, 13521, 4553, -492, -1199, 13504, 4574, -495, -1200, 13486, 4595, -497, -1200, 13469, 4615, -500, -1201, 13451, 4636, -502, -1202, 13435, 4656, -505,
+ -1202, 13417, 4677, -508, -1203, 13399, 4698, -510, -1204, 13383, 4718, -513, -1204, 13365, 4739, -516, -1205, 13347, 4760, -518, -1205, 13330, 4780, -521, -1206, 13312, 4801, -523, -1206, 13294, 4822, -526, -1207, 13277, 4843, -529, -1207, 13258, 4864, -531, -1208, 13241, 4885, -534, -1208, 13224, 4905, -537, -1208, 13205, 4926, -539, -1209, 13188, 4947, -542, -1209, 13170, 4968, -545, -1210, 13152, 4989, -547,
+ -1210, 13134, 5010, -550, -1210, 13116, 5031, -553, -1211, 13098, 5052, -555, -1211, 13080, 5073, -558, -1211, 13062, 5094, -561, -1212, 13044, 5115, -563, -1212, 13026, 5136, -566, -1212, 13008, 5157, -569, -1212, 12989, 5178, -571, -1212, 12971, 5199, -574, -1213, 12953, 5221, -577, -1213, 12934, 5242, -579, -1213, 12916, 5263, -582, -1213, 12898, 5284, -585, -1213, 12879, 5305, -587, -1213, 12860, 5327, -590,
+ -1213, 12842, 5348, -593, -1213, 12823, 5369, -595, -1214, 12806, 5390, -598, -1214, 12787, 5412, -601, -1214, 12768, 5433, -603, -1214, 12750, 5454, -606, -1214, 12731, 5476, -609, -1214, 12712, 5497, -611, -1214, 12694, 5518, -614, -1214, 12675, 5540, -617, -1213, 12655, 5561, -619, -1213, 12637, 5582, -622, -1213, 12618, 5604, -625, -1213, 12599, 5625, -627, -1213, 12580, 5647, -630, -1213, 12562, 5668, -633,
+ -1213, 12542, 5690, -635, -1213, 12524, 5711, -638, -1212, 12504, 5733, -641, -1212, 12485, 5754, -643, -1212, 12466, 5776, -646, -1212, 12448, 5797, -649, -1211, 12427, 5819, -651, -1211, 12408, 5841, -654, -1211, 12390, 5862, -657, -1211, 12370, 5884, -659, -1210, 12351, 5905, -662, -1210, 12332, 5927, -665, -1210, 12312, 5949, -667, -1209, 12293, 5970, -670, -1209, 12273, 5992, -672, -1209, 12254, 6014, -675,
+ -1208, 12235, 6035, -678, -1208, 12215, 6057, -680, -1207, 12195, 6079, -683, -1207, 12176, 6101, -686, -1207, 12157, 6122, -688, -1206, 12137, 6144, -691, -1206, 12118, 6166, -694, -1205, 12097, 6188, -696, -1205, 12079, 6209, -699, -1204, 12059, 6231, -702, -1204, 12039, 6253, -704, -1203, 12019, 6275, -707, -1202, 11998, 6297, -709, -1202, 11980, 6318, -712, -1201, 11960, 6340, -715, -1201, 11940, 6362, -717,
+ -1200, 11920, 6384, -720, -1199, 11900, 6406, -723, -1199, 11880, 6428, -725, -1198, 11860, 6450, -728, -1197, 11839, 6472, -730, -1197, 11821, 6493, -733, -1196, 11801, 6515, -736, -1195, 11780, 6537, -738, -1195, 11761, 6559, -741, -1194, 11741, 6581, -744, -1193, 11720, 6603, -746, -1192, 11700, 6625, -749, -1192, 11680, 6647, -751, -1191, 11660, 6669, -754, -1190, 11640, 6691, -757, -1189, 11619, 6713, -759,
+ -1188, 11599, 6735, -762, -1187, 11578, 6757, -764, -1187, 11559, 6779, -767, -1186, 11538, 6801, -769, -1185, 11518, 6823, -772, -1184, 11498, 6845, -775, -1183, 11477, 6867, -777, -1182, 11457, 6889, -780, -1181, 11436, 6911, -782, -1180, 11415, 6934, -785, -1179, 11394, 6956, -787, -1178, 11374, 6978, -790, -1177, 11354, 7000, -793, -1176, 11333, 7022, -795, -1175, 11313, 7044, -798, -1174, 11292, 7066, -800,
+ -1173, 11272, 7088, -803, -1172, 11251, 7110, -805, -1171, 11231, 7132, -808, -1170, 11209, 7155, -810, -1169, 11189, 7177, -813, -1168, 11168, 7199, -815, -1167, 11148, 7221, -818, -1166, 11127, 7243, -820, -1165, 11107, 7265, -823, -1163, 11084, 7288, -825, -1162, 11064, 7310, -828, -1161, 11043, 7332, -830, -1160, 11023, 7354, -833, -1159, 11002, 7376, -835, -1158, 10982, 7398, -838, -1156, 10959, 7421, -840,
+ -1155, 10939, 7443, -843, -1154, 10918, 7465, -845, -1153, 10898, 7487, -848, -1151, 10876, 7509, -850, -1150, 10856, 7531, -853, -1149, 10834, 7554, -855, -1148, 10814, 7576, -858, -1146, 10792, 7598, -860, -1145, 10772, 7620, -863, -1144, 10750, 7643, -865, -1142, 10728, 7665, -867, -1141, 10708, 7687, -870, -1140, 10687, 7709, -872, -1138, 10666, 7731, -875, -1137, 10644, 7754, -877, -1135, 10623, 7776, -880,
+ -1134, 10602, 7798, -882, -1133, 10581, 7820, -884, -1131, 10560, 7842, -887, -1130, 10538, 7865, -889, -1128, 10517, 7887, -892, -1127, 10496, 7909, -894, -1125, 10474, 7931, -896, -1124, 10453, 7954, -899, -1122, 10431, 7976, -901, -1121, 10410, 7998, -903, -1119, 10389, 8020, -906, -1118, 10368, 8042, -908, -1116, 10346, 8065, -911, -1115, 10325, 8087, -913, -1113, 10303, 8109, -915, -1112, 10283, 8131, -918,
+ -1110, 10260, 8154, -920, -1109, 10239, 8176, -922, -1107, 10217, 8198, -924, -1105, 10196, 8220, -927, -1104, 10175, 8242, -929, -1102, 10152, 8265, -931, -1101, 10132, 8287, -934, -1099, 10110, 8309, -936, -1097, 10088, 8331, -938, -1096, 10068, 8353, -941, -1094, 10045, 8376, -943, -1092, 10023, 8398, -945, -1091, 10002, 8420, -947, -1089, 9981, 8442, -950, -1087, 9959, 8464, -952, -1085, 9936, 8487, -954,
+ -1084, 9915, 8509, -956, -1082, 9893, 8531, -958, -1080, 9872, 8553, -961, -1079, 9851, 8575, -963, -1077, 9829, 8597, -965, -1075, 9806, 8620, -967, -1073, 9784, 8642, -969, -1071, 9763, 8664, -972, -1070, 9742, 8686, -974, -1068, 9720, 8708, -976, -1066, 9698, 8730, -978, -1064, 9676, 8752, -980, -1062, 9653, 8775, -982, -1061, 9633, 8797, -985, -1059, 9611, 8819, -987, -1057, 9589, 8841, -989,
+ -1055, 9567, 8863, -991, -1053, 9545, 8885, -993, -1051, 9523, 8907, -995, -1049, 9501, 8929, -997, -1047, 9479, 8951, -999, -1046, 9458, 8974, -1002, -1044, 9436, 8996, -1004, -1042, 9414, 9018, -1006, -1040, 9392, 9040, -1008, -1038, 9370, 9062, -1010, -1036, 9348, 9084, -1012, -1034, 9326, 9106, -1014, -1032, 9304, 9128, -1016, -1030, 9282, 9150, -1018, -1028, 9260, 9172, -1020, -1026, 9238, 9194, -1022,
+ -1024, 9216, 9216, -1024, -1022, 9194, 9238, -1026, -1020, 9172, 9260, -1028, -1018, 9150, 9282, -1030, -1016, 9128, 9304, -1032, -1014, 9106, 9326, -1034, -1012, 9084, 9348, -1036, -1010, 9062, 9370, -1038, -1008, 9040, 9392, -1040, -1006, 9018, 9414, -1042, -1004, 8996, 9436, -1044, -1002, 8974, 9458, -1046, -999, 8951, 9479, -1047, -997, 8929, 9501, -1049, -995, 8907, 9523, -1051, -993, 8885, 9545, -1053,
+ -991, 8863, 9567, -1055, -989, 8841, 9589, -1057, -987, 8819, 9611, -1059, -985, 8797, 9633, -1061, -982, 8775, 9653, -1062, -980, 8752, 9676, -1064, -978, 8730, 9698, -1066, -976, 8708, 9720, -1068, -974, 8686, 9742, -1070, -972, 8664, 9763, -1071, -969, 8642, 9784, -1073, -967, 8620, 9806, -1075, -965, 8597, 9829, -1077, -963, 8575, 9851, -1079, -961, 8553, 9872, -1080, -958, 8531, 9893, -1082,
+ -956, 8509, 9915, -1084, -954, 8487, 9936, -1085, -952, 8464, 9959, -1087, -950, 8442, 9981, -1089, -947, 8420, 10002, -1091, -945, 8398, 10023, -1092, -943, 8376, 10045, -1094, -941, 8353, 10068, -1096, -938, 8331, 10088, -1097, -936, 8309, 10110, -1099, -934, 8287, 10132, -1101, -931, 8265, 10152, -1102, -929, 8242, 10175, -1104, -927, 8220, 10196, -1105, -924, 8198, 10217, -1107, -922, 8176, 10239, -1109,
+ -920, 8154, 10260, -1110, -918, 8131, 10283, -1112, -915, 8109, 10303, -1113, -913, 8087, 10325, -1115, -911, 8065, 10346, -1116, -908, 8042, 10368, -1118, -906, 8020, 10389, -1119, -903, 7998, 10410, -1121, -901, 7976, 10431, -1122, -899, 7954, 10453, -1124, -896, 7931, 10474, -1125, -894, 7909, 10496, -1127, -892, 7887, 10517, -1128, -889, 7865, 10538, -1130, -887, 7842, 10560, -1131, -884, 7820, 10581, -1133,
+ -882, 7798, 10602, -1134, -880, 7776, 10623, -1135, -877, 7754, 10644, -1137, -875, 7731, 10666, -1138, -872, 7709, 10687, -1140, -870, 7687, 10708, -1141, -867, 7665, 10728, -1142, -865, 7643, 10750, -1144, -863, 7620, 10772, -1145, -860, 7598, 10792, -1146, -858, 7576, 10814, -1148, -855, 7554, 10834, -1149, -853, 7531, 10856, -1150, -850, 7509, 10876, -1151, -848, 7487, 10898, -1153, -845, 7465, 10918, -1154,
+ -843, 7443, 10939, -1155, -840, 7421, 10959, -1156, -838, 7398, 10982, -1158, -835, 7376, 11002, -1159, -833, 7354, 11023, -1160, -830, 7332, 11043, -1161, -828, 7310, 11064, -1162, -825, 7288, 11084, -1163, -823, 7265, 11107, -1165, -820, 7243, 11127, -1166, -818, 7221, 11148, -1167, -815, 7199, 11168, -1168, -813, 7177, 11189, -1169, -810, 7155, 11209, -1170, -808, 7132, 11231, -1171, -805, 7110, 11251, -1172,
+ -803, 7088, 11272, -1173, -800, 7066, 11292, -1174, -798, 7044, 11313, -1175, -795, 7022, 11333, -1176, -793, 7000, 11354, -1177, -790, 6978, 11374, -1178, -787, 6956, 11394, -1179, -785, 6934, 11415, -1180, -782, 6911, 11436, -1181, -780, 6889, 11457, -1182, -777, 6867, 11477, -1183, -775, 6845, 11498, -1184, -772, 6823, 11518, -1185, -769, 6801, 11538, -1186, -767, 6779, 11559, -1187, -764, 6757, 11578, -1187,
+ -762, 6735, 11599, -1188, -759, 6713, 11619, -1189, -757, 6691, 11640, -1190, -754, 6669, 11660, -1191, -751, 6647, 11680, -1192, -749, 6625, 11700, -1192, -746, 6603, 11720, -1193, -744, 6581, 11741, -1194, -741, 6559, 11761, -1195, -738, 6537, 11780, -1195, -736, 6515, 11801, -1196, -733, 6493, 11821, -1197, -730, 6472, 11839, -1197, -728, 6450, 11860, -1198, -725, 6428, 11880, -1199, -723, 6406, 11900, -1199,
+ -720, 6384, 11920, -1200, -717, 6362, 11940, -1201, -715, 6340, 11960, -1201, -712, 6318, 11980, -1202, -709, 6297, 11998, -1202, -707, 6275, 12019, -1203, -704, 6253, 12039, -1204, -702, 6231, 12059, -1204, -699, 6209, 12079, -1205, -696, 6188, 12097, -1205, -694, 6166, 12118, -1206, -691, 6144, 12137, -1206, -688, 6122, 12157, -1207, -686, 6101, 12176, -1207, -683, 6079, 12195, -1207, -680, 6057, 12215, -1208,
+ -678, 6035, 12235, -1208, -675, 6014, 12254, -1209, -672, 5992, 12273, -1209, -670, 5970, 12293, -1209, -667, 5949, 12312, -1210, -665, 5927, 12332, -1210, -662, 5905, 12351, -1210, -659, 5884, 12370, -1211, -657, 5862, 12390, -1211, -654, 5841, 12408, -1211, -651, 5819, 12427, -1211, -649, 5797, 12448, -1212, -646, 5776, 12466, -1212, -643, 5754, 12485, -1212, -641, 5733, 12504, -1212, -638, 5711, 12524, -1213,
+ -635, 5690, 12542, -1213, -633, 5668, 12562, -1213, -630, 5647, 12580, -1213, -627, 5625, 12599, -1213, -625, 5604, 12618, -1213, -622, 5582, 12637, -1213, -619, 5561, 12655, -1213, -617, 5540, 12675, -1214, -614, 5518, 12694, -1214, -611, 5497, 12712, -1214, -609, 5476, 12731, -1214, -606, 5454, 12750, -1214, -603, 5433, 12768, -1214, -601, 5412, 12787, -1214, -598, 5390, 12806, -1214, -595, 5369, 12823, -1213,
+ -593, 5348, 12842, -1213, -590, 5327, 12860, -1213, -587, 5305, 12879, -1213, -585, 5284, 12898, -1213, -582, 5263, 12916, -1213, -579, 5242, 12934, -1213, -577, 5221, 12953, -1213, -574, 5199, 12971, -1212, -571, 5178, 12989, -1212, -569, 5157, 13008, -1212, -566, 5136, 13026, -1212, -563, 5115, 13044, -1212, -561, 5094, 13062, -1211, -558, 5073, 13080, -1211, -555, 5052, 13098, -1211, -553, 5031, 13116, -1210,
+ -550, 5010, 13134, -1210, -547, 4989, 13152, -1210, -545, 4968, 13170, -1209, -542, 4947, 13188, -1209, -539, 4926, 13205, -1208, -537, 4905, 13224, -1208, -534, 4885, 13241, -1208, -531, 4864, 13258, -1207, -529, 4843, 13277, -1207, -526, 4822, 13294, -1206, -523, 4801, 13312, -1206, -521, 4780, 13330, -1205, -518, 4760, 13347, -1205, -516, 4739, 13365, -1204, -513, 4718, 13383, -1204, -510, 4698, 13399, -1203,
+ -508, 4677, 13417, -1202, -505, 4656, 13435, -1202, -502, 4636, 13451, -1201, -500, 4615, 13469, -1200, -497, 4595, 13486, -1200, -495, 4574, 13504, -1199, -492, 4553, 13521, -1198, -489, 4533, 13538, -1198, -487, 4512, 13556, -1197, -484, 4492, 13572, -1196, -481, 4471, 13589, -1195, -479, 4451, 13607, -1195, -476, 4431, 13623, -1194, -474, 4410, 13641, -1193, -471, 4390, 13657, -1192, -468, 4370, 13673, -1191,
+ -466, 4349, 13691, -1190, -463, 4329, 13707, -1189, -461, 4309, 13724, -1188, -458, 4288, 13741, -1187, -455, 4268, 13758, -1187, -453, 4248, 13775, -1186, -450, 4228, 13791, -1185, -448, 4208, 13808, -1184, -445, 4188, 13823, -1182, -442, 4167, 13840, -1181, -440, 4147, 13857, -1180, -437, 4127, 13873, -1179, -435, 4107, 13890, -1178, -432, 4087, 13906, -1177, -430, 4067, 13923, -1176, -427, 4047, 13939, -1175,
+ -424, 4027, 13955, -1174, -422, 4007, 13971, -1172, -419, 3988, 13986, -1171, -417, 3968, 14003, -1170, -414, 3948, 14019, -1169, -412, 3928, 14035, -1167, -409, 3908, 14051, -1166, -407, 3889, 14067, -1165, -404, 3869, 14082, -1163, -402, 3849, 14099, -1162, -399, 3829, 14115, -1161, -397, 3810, 14130, -1159, -394, 3790, 14146, -1158, -392, 3771, 14161, -1156, -389, 3751, 14177, -1155, -387, 3732, 14192, -1153,
+ -384, 3712, 14208, -1152, -382, 3693, 14223, -1150, -379, 3673, 14239, -1149, -377, 3654, 14254, -1147, -374, 3634, 14270, -1146, -372, 3615, 14285, -1144, -369, 3595, 14301, -1143, -367, 3576, 14316, -1141, -364, 3557, 14330, -1139, -362, 3538, 14346, -1138, -359, 3518, 14361, -1136, -357, 3499, 14376, -1134, -354, 3480, 14391, -1133, -352, 3461, 14406, -1131, -349, 3442, 14420, -1129, -347, 3423, 14435, -1127,
+ -345, 3404, 14450, -1125, -342, 3385, 14465, -1124, -340, 3366, 14480, -1122, -337, 3347, 14494, -1120, -335, 3328, 14509, -1118, -332, 3309, 14523, -1116, -330, 3290, 14538, -1114, -328, 3271, 14553, -1112, -325, 3252, 14567, -1110, -323, 3233, 14582, -1108, -320, 3215, 14595, -1106, -318, 3196, 14610, -1104, -316, 3177, 14625, -1102, -313, 3159, 14638, -1100, -311, 3140, 14653, -1098, -309, 3121, 14668, -1096,
+ -306, 3103, 14681, -1094, -304, 3084, 14696, -1092, -302, 3066, 14709, -1089, -299, 3047, 14723, -1087, -297, 3029, 14737, -1085, -295, 3010, 14752, -1083, -292, 2992, 14764, -1080, -290, 2974, 14778, -1078, -288, 2955, 14793, -1076, -285, 2937, 14806, -1074, -283, 2919, 14819, -1071, -281, 2901, 14833, -1069, -278, 2882, 14846, -1066, -276, 2864, 14860, -1064, -274, 2846, 14874, -1062, -272, 2828, 14887, -1059,
+ -269, 2810, 14900, -1057, -267, 2792, 14913, -1054, -265, 2774, 14927, -1052, -263, 2756, 14940, -1049, -260, 2738, 14953, -1047, -258, 2720, 14966, -1044, -256, 2702, 14979, -1041, -254, 2685, 14992, -1039, -251, 2667, 15004, -1036, -249, 2649, 15017, -1033, -247, 2631, 15031, -1031, -245, 2614, 15043, -1028, -243, 2596, 15056, -1025, -240, 2579, 15067, -1022, -238, 2561, 15081, -1020, -236, 2543, 15094, -1017,
+ -234, 2526, 15106, -1014, -232, 2509, 15118, -1011, -230, 2491, 15131, -1008, -228, 2474, 15143, -1005, -225, 2456, 15155, -1002, -223, 2439, 15167, -999, -221, 2422, 15180, -997, -219, 2405, 15192, -994, -217, 2387, 15205, -991, -215, 2370, 15216, -987, -213, 2353, 15228, -984, -211, 2336, 15240, -981, -209, 2319, 15252, -978, -207, 2302, 15264, -975, -205, 2285, 15276, -972, -202, 2268, 15287, -969,
+ -200, 2251, 15299, -966, -198, 2234, 15310, -962, -196, 2218, 15321, -959, -194, 2201, 15333, -956, -192, 2184, 15345, -953, -190, 2167, 15356, -949, -188, 2151, 15367, -946, -186, 2134, 15379, -943, -184, 2117, 15390, -939, -182, 2101, 15401, -936, -180, 2084, 15412, -932, -178, 2068, 15423, -929, -176, 2052, 15433, -925, -175, 2035, 15446, -922, -173, 2019, 15456, -918, -171, 2003, 15467, -915,
+ -169, 1986, 15478, -911, -167, 1970, 15489, -908, -165, 1954, 15499, -904, -163, 1938, 15509, -900, -161, 1922, 15520, -897, -159, 1906, 15530, -893, -157, 1890, 15540, -889, -156, 1874, 15552, -886, -154, 1858, 15562, -882, -152, 1842, 15572, -878, -150, 1826, 15582, -874, -148, 1810, 15592, -870, -146, 1794, 15602, -866, -145, 1779, 15613, -863, -143, 1763, 15623, -859, -141, 1747, 15633, -855,
+ -139, 1732, 15642, -851, -137, 1716, 15652, -847, -136, 1701, 15662, -843, -134, 1685, 15672, -839, -132, 1670, 15681, -835, -130, 1654, 15691, -831, -129, 1639, 15700, -826, -127, 1624, 15709, -822, -125, 1608, 15719, -818, -124, 1593, 15729, -814, -122, 1578, 15738, -810, -120, 1563, 15747, -806, -119, 1548, 15756, -801, -117, 1533, 15765, -797, -115, 1518, 15774, -793, -114, 1503, 15783, -788,
+ -112, 1488, 15792, -784, -110, 1473, 15801, -780, -109, 1458, 15810, -775, -107, 1444, 15818, -771, -106, 1429, 15827, -766, -104, 1414, 15836, -762, -102, 1400, 15843, -757, -101, 1385, 15853, -753, -99, 1370, 15861, -748, -98, 1356, 15870, -744, -96, 1342, 15877, -739, -95, 1327, 15886, -734, -93, 1313, 15894, -730, -92, 1298, 15903, -725, -90, 1284, 15910, -720, -89, 1270, 15918, -715,
+ -87, 1256, 15926, -711, -86, 1242, 15934, -706, -84, 1228, 15941, -701, -83, 1214, 15949, -696, -82, 1200, 15957, -691, -80, 1186, 15964, -686, -79, 1172, 15973, -682, -77, 1158, 15980, -677, -76, 1144, 15988, -672, -75, 1131, 15995, -667, -73, 1117, 16002, -662, -72, 1103, 16009, -656, -70, 1090, 16015, -651, -69, 1076, 16023, -646, -68, 1063, 16030, -641, -67, 1049, 16038, -636,
+ -65, 1036, 16044, -631, -64, 1022, 16052, -626, -63, 1009, 16058, -620, -61, 996, 16064, -615, -60, 983, 16071, -610, -59, 970, 16077, -604, -58, 956, 16085, -599, -57, 943, 16092, -594, -55, 930, 16097, -588, -54, 917, 16104, -583, -53, 905, 16109, -577, -52, 892, 16116, -572, -51, 879, 16122, -566, -49, 866, 16128, -561, -48, 854, 16133, -555, -47, 841, 16140, -550,
+ -46, 828, 16146, -544, -45, 816, 16151, -538, -44, 803, 16158, -533, -43, 791, 16163, -527, -42, 778, 16169, -521, -41, 766, 16174, -515, -40, 754, 16180, -510, -39, 742, 16185, -504, -38, 729, 16191, -498, -37, 717, 16196, -492, -36, 705, 16201, -486, -35, 693, 16206, -480, -34, 681, 16211, -474, -33, 669, 16216, -468, -32, 658, 16220, -462, -31, 646, 16225, -456,
+ -30, 634, 16230, -450, -29, 622, 16235, -444, -28, 611, 16239, -438, -27, 599, 16244, -432, -26, 588, 16247, -425, -26, 576, 16253, -419, -25, 565, 16257, -413, -24, 553, 16262, -407, -23, 542, 16265, -400, -22, 531, 16269, -394, -22, 520, 16274, -388, -21, 508, 16278, -381, -20, 497, 16282, -375, -19, 486, 16285, -368, -19, 475, 16290, -362, -18, 464, 16293, -355,
+ -17, 453, 16297, -349, -16, 443, 16299, -342, -16, 432, 16304, -336, -15, 421, 16307, -329, -14, 411, 16309, -322, -14, 400, 16314, -316, -13, 389, 16317, -309, -13, 379, 16320, -302, -12, 369, 16322, -295, -11, 358, 16326, -289, -11, 348, 16329, -282, -10, 338, 16331, -275, -10, 327, 16335, -268, -9, 317, 16337, -261, -9, 307, 16340, -254, -8, 297, 16342, -247,
+ -8, 287, 16345, -240, -7, 277, 16347, -233, -7, 268, 16349, -226, -6, 258, 16351, -219, -6, 248, 16354, -212, -6, 238, 16357, -205, -5, 229, 16358, -198, -5, 219, 16360, -190, -4, 210, 16361, -183, -4, 200, 16364, -176, -4, 191, 16366, -169, -3, 182, 16366, -161, -3, 172, 16369, -154, -3, 163, 16370, -146, -2, 154, 16371, -139, -2, 145, 16373, -132,
+ -2, 136, 16374, -124, -2, 127, 16376, -117, -2, 118, 16377, -109, -1, 109, 16377, -101, -1, 100, 16379, -94, -1, 92, 16379, -86, -1, 83, 16380, -78, -1, 75, 16381, -71, 0, 66, 16381, -63, 0, 58, 16381, -55, 0, 49, 16382, -47, 0, 41, 16383, -40, 0, 32, 16384, -32, 0, 24, 16384, -24, 0, 16, 16384, -16, 0, 8, 16384, -8,
+};
+
+static signed short windowed_fir_lut[16392] = {
+ 55, -727, 2306, 29549, 2306, -727, 55, -48, 54, -725, 2294, 29549, 2317, -729, 55, -48, 54, -723, 2282, 29549, 2329, -731, 55, -48, 54, -721, 2271, 29549, 2341, -733, 55, -48, 54, -718, 2259, 29549, 2353, -735, 55, -48, 54, -716, 2247, 29549, 2364, -738, 56, -48, 54, -714, 2236, 29548, 2376, -740, 56, -48, 53, -712, 2224, 29548, 2388, -742, 56, -48,
+ 53, -710, 2213, 29548, 2400, -744, 56, -48, 53, -708, 2201, 29548, 2411, -746, 56, -48, 53, -706, 2189, 29547, 2423, -748, 56, -47, 53, -704, 2178, 29547, 2435, -750, 57, -47, 53, -702, 2166, 29547, 2447, -752, 57, -47, 52, -699, 2155, 29546, 2459, -755, 57, -47, 52, -697, 2143, 29546, 2471, -757, 57, -47, 52, -695, 2132, 29546, 2483, -759, 57, -47,
+ 52, -693, 2120, 29545, 2494, -761, 58, -47, 52, -691, 2109, 29545, 2506, -763, 58, -47, 52, -689, 2097, 29544, 2518, -765, 58, -47, 51, -687, 2086, 29544, 2530, -768, 58, -47, 51, -685, 2074, 29543, 2542, -770, 58, -47, 51, -683, 2063, 29543, 2554, -772, 58, -47, 51, -681, 2052, 29542, 2566, -774, 59, -47, 51, -679, 2040, 29542, 2578, -776, 59, -47,
+ 50, -677, 2029, 29541, 2590, -778, 59, -46, 50, -674, 2017, 29540, 2602, -781, 59, -46, 50, -672, 2006, 29540, 2614, -783, 59, -46, 50, -670, 1995, 29539, 2626, -785, 60, -46, 50, -668, 1983, 29538, 2638, -787, 60, -46, 50, -666, 1972, 29537, 2650, -789, 60, -46, 49, -664, 1961, 29537, 2662, -791, 60, -46, 49, -662, 1949, 29536, 2675, -794, 60, -46,
+ 49, -660, 1938, 29535, 2687, -796, 60, -46, 49, -658, 1927, 29534, 2699, -798, 61, -46, 49, -656, 1916, 29533, 2711, -800, 61, -46, 49, -654, 1904, 29533, 2723, -802, 61, -46, 48, -652, 1893, 29532, 2735, -804, 61, -46, 48, -650, 1882, 29531, 2747, -807, 61, -45, 48, -648, 1871, 29530, 2760, -809, 62, -45, 48, -646, 1860, 29529, 2772, -811, 62, -45,
+ 48, -644, 1848, 29528, 2784, -813, 62, -45, 48, -642, 1837, 29527, 2796, -815, 62, -45, 47, -640, 1826, 29526, 2808, -818, 62, -45, 47, -638, 1815, 29525, 2821, -820, 63, -45, 47, -635, 1804, 29524, 2833, -822, 63, -45, 47, -633, 1793, 29522, 2845, -824, 63, -45, 47, -631, 1782, 29521, 2858, -826, 63, -45, 47, -629, 1771, 29520, 2870, -829, 63, -45,
+ 47, -627, 1760, 29519, 2882, -831, 64, -45, 46, -625, 1749, 29518, 2895, -833, 64, -45, 46, -623, 1738, 29517, 2907, -835, 64, -44, 46, -621, 1727, 29515, 2919, -838, 64, -44, 46, -619, 1716, 29514, 2932, -840, 64, -44, 46, -617, 1705, 29513, 2944, -842, 64, -44, 46, -615, 1694, 29511, 2956, -844, 65, -44, 45, -613, 1683, 29510, 2969, -846, 65, -44,
+ 45, -611, 1672, 29509, 2981, -849, 65, -44, 45, -609, 1661, 29507, 2994, -851, 65, -44, 45, -607, 1650, 29506, 3006, -853, 65, -44, 45, -605, 1639, 29504, 3019, -855, 66, -44, 45, -603, 1628, 29503, 3031, -858, 66, -44, 44, -601, 1617, 29502, 3044, -860, 66, -44, 44, -599, 1606, 29500, 3056, -862, 66, -44, 44, -597, 1595, 29498, 3069, -864, 66, -43,
+ 44, -595, 1585, 29497, 3081, -867, 67, -43, 44, -593, 1574, 29495, 3094, -869, 67, -43, 44, -591, 1563, 29494, 3106, -871, 67, -43, 43, -589, 1552, 29492, 3119, -873, 67, -43, 43, -587, 1541, 29490, 3131, -876, 67, -43, 43, -585, 1531, 29489, 3144, -878, 68, -43, 43, -583, 1520, 29487, 3157, -880, 68, -43, 43, -581, 1509, 29485, 3169, -882, 68, -43,
+ 43, -579, 1498, 29484, 3182, -885, 68, -43, 43, -578, 1488, 29482, 3194, -887, 68, -43, 42, -576, 1477, 29480, 3207, -889, 69, -43, 42, -574, 1466, 29478, 3220, -891, 69, -43, 42, -572, 1456, 29476, 3232, -894, 69, -42, 42, -570, 1445, 29475, 3245, -896, 69, -42, 42, -568, 1434, 29473, 3258, -898, 69, -42, 42, -566, 1424, 29471, 3271, -900, 70, -42,
+ 42, -564, 1413, 29469, 3283, -903, 70, -42, 41, -562, 1403, 29467, 3296, -905, 70, -42, 41, -560, 1392, 29465, 3309, -907, 70, -42, 41, -558, 1381, 29463, 3322, -909, 70, -42, 41, -556, 1371, 29461, 3334, -912, 71, -42, 41, -554, 1360, 29459, 3347, -914, 71, -42, 41, -552, 1350, 29457, 3360, -916, 71, -42, 40, -550, 1339, 29455, 3373, -919, 71, -42,
+ 40, -548, 1329, 29452, 3386, -921, 71, -42, 40, -546, 1318, 29450, 3399, -923, 72, -41, 40, -544, 1308, 29448, 3411, -925, 72, -41, 40, -542, 1297, 29446, 3424, -928, 72, -41, 40, -541, 1287, 29444, 3437, -930, 72, -41, 40, -539, 1276, 29442, 3450, -932, 72, -41, 39, -537, 1266, 29439, 3463, -935, 73, -41, 39, -535, 1256, 29437, 3476, -937, 73, -41,
+ 39, -533, 1245, 29435, 3489, -939, 73, -41, 39, -531, 1235, 29432, 3502, -941, 73, -41, 39, -529, 1224, 29430, 3515, -944, 74, -41, 39, -527, 1214, 29428, 3528, -946, 74, -41, 39, -525, 1204, 29425, 3541, -948, 74, -41, 38, -523, 1193, 29423, 3554, -951, 74, -41, 38, -521, 1183, 29420, 3567, -953, 74, -40, 38, -520, 1173, 29418, 3580, -955, 75, -40,
+ 38, -518, 1163, 29415, 3593, -958, 75, -40, 38, -516, 1152, 29413, 3606, -960, 75, -40, 38, -514, 1142, 29410, 3619, -962, 75, -40, 38, -512, 1132, 29408, 3632, -965, 75, -40, 37, -510, 1122, 29405, 3645, -967, 76, -40, 37, -508, 1111, 29403, 3658, -969, 76, -40, 37, -506, 1101, 29400, 3671, -971, 76, -40, 37, -504, 1091, 29397, 3684, -974, 76, -40,
+ 37, -502, 1081, 29395, 3698, -976, 76, -40, 37, -501, 1071, 29392, 3711, -978, 77, -40, 37, -499, 1061, 29389, 3724, -981, 77, -40, 36, -497, 1050, 29386, 3737, -983, 77, -39, 36, -495, 1040, 29384, 3750, -985, 77, -39, 36, -493, 1030, 29381, 3764, -988, 77, -39, 36, -491, 1020, 29378, 3777, -990, 78, -39, 36, -489, 1010, 29375, 3790, -992, 78, -39,
+ 36, -488, 1000, 29372, 3803, -995, 78, -39, 36, -486, 990, 29369, 3816, -997, 78, -39, 35, -484, 980, 29367, 3830, -999, 79, -39, 35, -482, 970, 29364, 3843, -1002, 79, -39, 35, -480, 960, 29361, 3856, -1004, 79, -39, 35, -478, 950, 29358, 3870, -1006, 79, -39, 35, -476, 940, 29355, 3883, -1009, 79, -39, 35, -475, 930, 29352, 3896, -1011, 80, -39,
+ 35, -473, 920, 29349, 3910, -1014, 80, -38, 34, -471, 910, 29346, 3923, -1016, 80, -38, 34, -469, 900, 29342, 3936, -1018, 80, -38, 34, -467, 890, 29339, 3950, -1021, 80, -38, 34, -465, 880, 29336, 3963, -1023, 81, -38, 34, -464, 871, 29333, 3976, -1025, 81, -38, 34, -462, 861, 29330, 3990, -1028, 81, -38, 34, -460, 851, 29327, 4003, -1030, 81, -38,
+ 34, -458, 841, 29323, 4017, -1032, 82, -38, 33, -456, 831, 29320, 4030, -1035, 82, -38, 33, -454, 821, 29317, 4044, -1037, 82, -38, 33, -453, 812, 29314, 4057, -1039, 82, -38, 33, -451, 802, 29310, 4071, -1042, 82, -38, 33, -449, 792, 29307, 4084, -1044, 83, -37, 33, -447, 782, 29303, 4098, -1047, 83, -37, 33, -445, 773, 29300, 4111, -1049, 83, -37,
+ 32, -444, 763, 29297, 4125, -1051, 83, -37, 32, -442, 753, 29293, 4138, -1054, 84, -37, 32, -440, 743, 29290, 4152, -1056, 84, -37, 32, -438, 734, 29286, 4165, -1058, 84, -37, 32, -436, 724, 29283, 4179, -1061, 84, -37, 32, -435, 714, 29279, 4193, -1063, 84, -37, 32, -433, 705, 29276, 4206, -1066, 85, -37, 32, -431, 695, 29272, 4220, -1068, 85, -37,
+ 31, -429, 686, 29268, 4234, -1070, 85, -37, 31, -427, 676, 29265, 4247, -1073, 85, -36, 31, -426, 666, 29261, 4261, -1075, 86, -36, 31, -424, 657, 29258, 4274, -1077, 86, -36, 31, -422, 647, 29254, 4288, -1080, 86, -36, 31, -420, 638, 29250, 4302, -1082, 86, -36, 31, -419, 628, 29246, 4316, -1085, 86, -36, 31, -417, 619, 29243, 4329, -1087, 87, -36,
+ 30, -415, 609, 29239, 4343, -1089, 87, -36, 30, -413, 600, 29235, 4357, -1092, 87, -36, 30, -411, 590, 29231, 4371, -1094, 87, -36, 30, -410, 581, 29227, 4384, -1097, 88, -36, 30, -408, 571, 29223, 4398, -1099, 88, -36, 30, -406, 562, 29220, 4412, -1101, 88, -36, 30, -404, 552, 29216, 4426, -1104, 88, -35, 30, -403, 543, 29212, 4440, -1106, 88, -35,
+ 29, -401, 534, 29208, 4453, -1109, 89, -35, 29, -399, 524, 29204, 4467, -1111, 89, -35, 29, -397, 515, 29200, 4481, -1113, 89, -35, 29, -396, 506, 29196, 4495, -1116, 89, -35, 29, -394, 496, 29192, 4509, -1118, 90, -35, 29, -392, 487, 29188, 4523, -1121, 90, -35, 29, -391, 478, 29183, 4537, -1123, 90, -35, 29, -389, 468, 29179, 4551, -1125, 90, -35,
+ 28, -387, 459, 29175, 4564, -1128, 91, -35, 28, -385, 450, 29171, 4578, -1130, 91, -35, 28, -384, 441, 29167, 4592, -1133, 91, -35, 28, -382, 431, 29163, 4606, -1135, 91, -34, 28, -380, 422, 29158, 4620, -1138, 91, -34, 28, -378, 413, 29154, 4634, -1140, 92, -34, 28, -377, 404, 29150, 4648, -1142, 92, -34, 28, -375, 395, 29145, 4662, -1145, 92, -34,
+ 27, -373, 385, 29141, 4676, -1147, 92, -34, 27, -372, 376, 29137, 4690, -1150, 93, -34, 27, -370, 367, 29132, 4704, -1152, 93, -34, 27, -368, 358, 29128, 4718, -1154, 93, -34, 27, -366, 349, 29124, 4732, -1157, 93, -34, 27, -365, 340, 29119, 4747, -1159, 94, -34, 27, -363, 331, 29115, 4761, -1162, 94, -34, 27, -361, 322, 29110, 4775, -1164, 94, -34,
+ 27, -360, 313, 29106, 4789, -1167, 94, -33, 26, -358, 304, 29101, 4803, -1169, 94, -33, 26, -356, 295, 29096, 4817, -1172, 95, -33, 26, -355, 286, 29092, 4831, -1174, 95, -33, 26, -353, 277, 29087, 4845, -1176, 95, -33, 26, -351, 268, 29083, 4860, -1179, 95, -33, 26, -350, 259, 29078, 4874, -1181, 96, -33, 26, -348, 250, 29073, 4888, -1184, 96, -33,
+ 26, -346, 241, 29069, 4902, -1186, 96, -33, 25, -345, 232, 29064, 4916, -1189, 96, -33, 25, -343, 223, 29059, 4931, -1191, 97, -33, 25, -341, 214, 29054, 4945, -1194, 97, -33, 25, -340, 205, 29050, 4959, -1196, 97, -33, 25, -338, 196, 29045, 4973, -1198, 97, -32, 25, -336, 187, 29040, 4988, -1201, 97, -32, 25, -335, 179, 29035, 5002, -1203, 98, -32,
+ 25, -333, 170, 29030, 5016, -1206, 98, -32, 25, -331, 161, 29025, 5031, -1208, 98, -32, 24, -330, 152, 29020, 5045, -1211, 98, -32, 24, -328, 143, 29015, 5059, -1213, 99, -32, 24, -326, 135, 29010, 5074, -1216, 99, -32, 24, -325, 126, 29005, 5088, -1218, 99, -32, 24, -323, 117, 29000, 5102, -1221, 99, -32, 24, -321, 108, 28995, 5117, -1223, 100, -32,
+ 24, -320, 100, 28990, 5131, -1225, 100, -32, 24, -318, 91, 28985, 5146, -1228, 100, -32, 24, -317, 82, 28980, 5160, -1230, 100, -31, 23, -315, 74, 28975, 5174, -1233, 101, -31, 23, -313, 65, 28970, 5189, -1235, 101, -31, 23, -312, 56, 28965, 5203, -1238, 101, -31, 23, -310, 48, 28960, 5218, -1240, 101, -31, 23, -308, 39, 28954, 5232, -1243, 102, -31,
+ 23, -307, 30, 28949, 5247, -1245, 102, -31, 23, -305, 22, 28944, 5261, -1248, 102, -31, 23, -304, 13, 28939, 5276, -1250, 102, -31, 23, -302, 5, 28933, 5290, -1253, 103, -31, 23, -300, -4, 28928, 5305, -1255, 103, -31, 22, -299, -12, 28923, 5319, -1258, 103, -31, 22, -297, -21, 28917, 5334, -1260, 103, -31, 22, -296, -29, 28912, 5348, -1262, 103, -30,
+ 22, -294, -38, 28906, 5363, -1265, 104, -30, 22, -292, -46, 28901, 5378, -1267, 104, -30, 22, -291, -55, 28896, 5392, -1270, 104, -30, 22, -289, -63, 28890, 5407, -1272, 104, -30, 22, -288, -72, 28885, 5421, -1275, 105, -30, 22, -286, -80, 28879, 5436, -1277, 105, -30, 21, -285, -88, 28873, 5451, -1280, 105, -30, 21, -283, -97, 28868, 5465, -1282, 105, -30,
+ 21, -281, -105, 28862, 5480, -1285, 106, -30, 21, -280, -114, 28857, 5495, -1287, 106, -30, 21, -278, -122, 28851, 5509, -1290, 106, -30, 21, -277, -130, 28845, 5524, -1292, 106, -30, 21, -275, -139, 28840, 5539, -1295, 107, -29, 21, -274, -147, 28834, 5553, -1297, 107, -29, 21, -272, -155, 28828, 5568, -1300, 107, -29, 21, -270, -163, 28822, 5583, -1302, 107, -29,
+ 20, -269, -172, 28817, 5598, -1305, 108, -29, 20, -267, -180, 28811, 5612, -1307, 108, -29, 20, -266, -188, 28805, 5627, -1310, 108, -29, 20, -264, -196, 28799, 5642, -1312, 108, -29, 20, -263, -205, 28793, 5657, -1315, 109, -29, 20, -261, -213, 28788, 5672, -1317, 109, -29, 20, -260, -221, 28782, 5686, -1320, 109, -29, 20, -258, -229, 28776, 5701, -1322, 109, -29,
+ 20, -257, -237, 28770, 5716, -1325, 110, -29, 20, -255, -246, 28764, 5731, -1327, 110, -28, 19, -254, -254, 28758, 5746, -1330, 110, -28, 19, -252, -262, 28752, 5761, -1332, 110, -28, 19, -250, -270, 28746, 5776, -1335, 111, -28, 19, -249, -278, 28740, 5791, -1337, 111, -28, 19, -247, -286, 28734, 5805, -1340, 111, -28, 19, -246, -294, 28727, 5820, -1342, 111, -28,
+ 19, -244, -302, 28721, 5835, -1345, 112, -28, 19, -243, -310, 28715, 5850, -1347, 112, -28, 19, -241, -318, 28709, 5865, -1350, 112, -28, 19, -240, -326, 28703, 5880, -1352, 112, -28, 19, -238, -334, 28697, 5895, -1355, 113, -28, 18, -237, -342, 28690, 5910, -1357, 113, -28, 18, -235, -350, 28684, 5925, -1360, 113, -28, 18, -234, -358, 28678, 5940, -1362, 113, -27,
+ 18, -232, -366, 28672, 5955, -1365, 114, -27, 18, -231, -374, 28665, 5970, -1367, 114, -27, 18, -229, -382, 28659, 5985, -1370, 114, -27, 18, -228, -390, 28653, 6000, -1372, 114, -27, 18, -226, -398, 28646, 6015, -1375, 115, -27, 18, -225, -405, 28640, 6030, -1377, 115, -27, 18, -223, -413, 28633, 6045, -1380, 115, -27, 18, -222, -421, 28627, 6061, -1382, 115, -27,
+ 17, -220, -429, 28620, 6076, -1385, 116, -27, 17, -219, -437, 28614, 6091, -1387, 116, -27, 17, -218, -444, 28607, 6106, -1390, 116, -27, 17, -216, -452, 28601, 6121, -1392, 116, -27, 17, -215, -460, 28594, 6136, -1395, 117, -26, 17, -213, -468, 28588, 6151, -1397, 117, -26, 17, -212, -476, 28581, 6166, -1400, 117, -26, 17, -210, -483, 28574, 6182, -1403, 117, -26,
+ 17, -209, -491, 28568, 6197, -1405, 118, -26, 17, -207, -499, 28561, 6212, -1408, 118, -26, 17, -206, -506, 28554, 6227, -1410, 118, -26, 16, -204, -514, 28548, 6242, -1413, 118, -26, 16, -203, -522, 28541, 6258, -1415, 119, -26, 16, -202, -529, 28534, 6273, -1418, 119, -26, 16, -200, -537, 28527, 6288, -1420, 119, -26, 16, -199, -545, 28521, 6303, -1423, 119, -26,
+ 16, -197, -552, 28514, 6319, -1425, 120, -26, 16, -196, -560, 28507, 6334, -1428, 120, -26, 16, -194, -567, 28500, 6349, -1430, 120, -25, 16, -193, -575, 28493, 6365, -1433, 120, -25, 16, -191, -582, 28486, 6380, -1435, 121, -25, 16, -190, -590, 28479, 6395, -1438, 121, -25, 16, -189, -597, 28472, 6410, -1440, 121, -25, 15, -187, -605, 28465, 6426, -1443, 121, -25,
+ 15, -186, -612, 28458, 6441, -1445, 122, -25, 15, -184, -620, 28451, 6457, -1448, 122, -25, 15, -183, -627, 28444, 6472, -1451, 122, -25, 15, -182, -635, 28437, 6487, -1453, 122, -25, 15, -180, -642, 28430, 6503, -1456, 123, -25, 15, -179, -650, 28423, 6518, -1458, 123, -25, 15, -177, -657, 28416, 6533, -1461, 123, -25, 15, -176, -664, 28409, 6549, -1463, 124, -25,
+ 15, -175, -672, 28402, 6564, -1466, 124, -24, 15, -173, -679, 28395, 6580, -1468, 124, -24, 15, -172, -687, 28387, 6595, -1471, 124, -24, 14, -170, -694, 28380, 6611, -1473, 125, -24, 14, -169, -701, 28373, 6626, -1476, 125, -24, 14, -168, -708, 28366, 6642, -1478, 125, -24, 14, -166, -716, 28358, 6657, -1481, 125, -24, 14, -165, -723, 28351, 6673, -1484, 126, -24,
+ 14, -163, -730, 28344, 6688, -1486, 126, -24, 14, -162, -738, 28336, 6704, -1489, 126, -24, 14, -161, -745, 28329, 6719, -1491, 126, -24, 14, -159, -752, 28321, 6735, -1494, 127, -24, 14, -158, -759, 28314, 6750, -1496, 127, -24, 14, -157, -766, 28307, 6766, -1499, 127, -24, 14, -155, -774, 28299, 6781, -1501, 127, -23, 14, -154, -781, 28292, 6797, -1504, 128, -23,
+ 13, -153, -788, 28284, 6813, -1506, 128, -23, 13, -151, -795, 28277, 6828, -1509, 128, -23, 13, -150, -802, 28269, 6844, -1511, 128, -23, 13, -149, -809, 28261, 6859, -1514, 129, -23, 13, -147, -816, 28254, 6875, -1517, 129, -23, 13, -146, -824, 28246, 6891, -1519, 129, -23, 13, -144, -831, 28239, 6906, -1522, 130, -23, 13, -143, -838, 28231, 6922, -1524, 130, -23,
+ 13, -142, -845, 28223, 6938, -1527, 130, -23, 13, -140, -852, 28216, 6953, -1529, 130, -23, 13, -139, -859, 28208, 6969, -1532, 131, -23, 13, -138, -866, 28200, 6985, -1534, 131, -23, 13, -136, -873, 28192, 7001, -1537, 131, -22, 12, -135, -880, 28185, 7016, -1539, 131, -22, 12, -134, -887, 28177, 7032, -1542, 132, -22, 12, -133, -894, 28169, 7048, -1545, 132, -22,
+ 12, -131, -901, 28161, 7063, -1547, 132, -22, 12, -130, -907, 28153, 7079, -1550, 132, -22, 12, -129, -914, 28145, 7095, -1552, 133, -22, 12, -127, -921, 28138, 7111, -1555, 133, -22, 12, -126, -928, 28130, 7127, -1557, 133, -22, 12, -125, -935, 28122, 7142, -1560, 133, -22, 12, -123, -942, 28114, 7158, -1562, 134, -22, 12, -122, -949, 28106, 7174, -1565, 134, -22,
+ 12, -121, -956, 28098, 7190, -1567, 134, -22, 12, -119, -962, 28090, 7206, -1570, 135, -22, 12, -118, -969, 28082, 7221, -1573, 135, -21, 11, -117, -976, 28073, 7237, -1575, 135, -21, 11, -116, -983, 28065, 7253, -1578, 135, -21, 11, -114, -989, 28057, 7269, -1580, 136, -21, 11, -113, -996, 28049, 7285, -1583, 136, -21, 11, -112, -1003, 28041, 7301, -1585, 136, -21,
+ 11, -110, -1010, 28033, 7317, -1588, 136, -21, 11, -109, -1016, 28025, 7333, -1590, 137, -21, 11, -108, -1023, 28016, 7349, -1593, 137, -21, 11, -107, -1030, 28008, 7365, -1595, 137, -21, 11, -105, -1036, 28000, 7380, -1598, 137, -21, 11, -104, -1043, 27992, 7396, -1601, 138, -21, 11, -103, -1050, 27983, 7412, -1603, 138, -21, 11, -102, -1056, 27975, 7428, -1606, 138, -21,
+ 11, -100, -1063, 27967, 7444, -1608, 138, -21, 11, -99, -1070, 27958, 7460, -1611, 139, -20, 10, -98, -1076, 27950, 7476, -1613, 139, -20, 10, -97, -1083, 27941, 7492, -1616, 139, -20, 10, -95, -1089, 27933, 7508, -1618, 140, -20, 10, -94, -1096, 27925, 7524, -1621, 140, -20, 10, -93, -1102, 27916, 7540, -1624, 140, -20, 10, -92, -1109, 27908, 7556, -1626, 140, -20,
+ 10, -90, -1115, 27899, 7572, -1629, 141, -20, 10, -89, -1122, 27891, 7589, -1631, 141, -20, 10, -88, -1128, 27882, 7605, -1634, 141, -20, 10, -87, -1135, 27874, 7621, -1636, 141, -20, 10, -85, -1141, 27865, 7637, -1639, 142, -20, 10, -84, -1148, 27856, 7653, -1641, 142, -20, 10, -83, -1154, 27848, 7669, -1644, 142, -20, 10, -82, -1160, 27839, 7685, -1646, 142, -20,
+ 10, -81, -1167, 27830, 7701, -1649, 143, -19, 9, -79, -1173, 27822, 7717, -1652, 143, -19, 9, -78, -1180, 27813, 7733, -1654, 143, -19, 9, -77, -1186, 27804, 7750, -1657, 144, -19, 9, -76, -1192, 27795, 7766, -1659, 144, -19, 9, -74, -1199, 27787, 7782, -1662, 144, -19, 9, -73, -1205, 27778, 7798, -1664, 144, -19, 9, -72, -1211, 27769, 7814, -1667, 145, -19,
+ 9, -71, -1218, 27760, 7830, -1669, 145, -19, 9, -70, -1224, 27751, 7847, -1672, 145, -19, 9, -68, -1230, 27743, 7863, -1674, 145, -19, 9, -67, -1236, 27734, 7879, -1677, 146, -19, 9, -66, -1243, 27725, 7895, -1680, 146, -19, 9, -65, -1249, 27716, 7912, -1682, 146, -19, 9, -64, -1255, 27707, 7928, -1685, 147, -19, 9, -63, -1261, 27698, 7944, -1687, 147, -18,
+ 9, -61, -1267, 27689, 7960, -1690, 147, -18, 9, -60, -1273, 27680, 7977, -1692, 147, -18, 8, -59, -1280, 27671, 7993, -1695, 148, -18, 8, -58, -1286, 27662, 8009, -1697, 148, -18, 8, -57, -1292, 27653, 8025, -1700, 148, -18, 8, -55, -1298, 27644, 8042, -1703, 148, -18, 8, -54, -1304, 27634, 8058, -1705, 149, -18, 8, -53, -1310, 27625, 8074, -1708, 149, -18,
+ 8, -52, -1316, 27616, 8091, -1710, 149, -18, 8, -51, -1322, 27607, 8107, -1713, 149, -18, 8, -50, -1328, 27598, 8123, -1715, 150, -18, 8, -49, -1334, 27589, 8140, -1718, 150, -18, 8, -47, -1340, 27579, 8156, -1720, 150, -18, 8, -46, -1346, 27570, 8173, -1723, 151, -18, 8, -45, -1352, 27561, 8189, -1725, 151, -18, 8, -44, -1358, 27552, 8205, -1728, 151, -17,
+ 8, -43, -1364, 27542, 8222, -1730, 151, -17, 8, -42, -1370, 27533, 8238, -1733, 152, -17, 8, -40, -1376, 27523, 8255, -1736, 152, -17, 8, -39, -1382, 27514, 8271, -1738, 152, -17, 7, -38, -1388, 27505, 8287, -1741, 152, -17, 7, -37, -1394, 27495, 8304, -1743, 153, -17, 7, -36, -1400, 27486, 8320, -1746, 153, -17, 7, -35, -1406, 27476, 8337, -1748, 153, -17,
+ 7, -34, -1412, 27467, 8353, -1751, 154, -17, 7, -33, -1417, 27457, 8370, -1753, 154, -17, 7, -31, -1423, 27448, 8386, -1756, 154, -17, 7, -30, -1429, 27438, 8403, -1758, 154, -17, 7, -29, -1435, 27429, 8419, -1761, 155, -17, 7, -28, -1441, 27419, 8436, -1763, 155, -17, 7, -27, -1446, 27410, 8452, -1766, 155, -17, 7, -26, -1452, 27400, 8469, -1769, 155, -16,
+ 7, -25, -1458, 27390, 8485, -1771, 156, -16, 7, -24, -1464, 27381, 8502, -1774, 156, -16, 7, -23, -1469, 27371, 8518, -1776, 156, -16, 7, -22, -1475, 27361, 8535, -1779, 157, -16, 7, -20, -1481, 27352, 8552, -1781, 157, -16, 7, -19, -1486, 27342, 8568, -1784, 157, -16, 7, -18, -1492, 27332, 8585, -1786, 157, -16, 6, -17, -1498, 27322, 8601, -1789, 158, -16,
+ 6, -16, -1503, 27313, 8618, -1791, 158, -16, 6, -15, -1509, 27303, 8634, -1794, 158, -16, 6, -14, -1515, 27293, 8651, -1796, 158, -16, 6, -13, -1520, 27283, 8668, -1799, 159, -16, 6, -12, -1526, 27273, 8684, -1801, 159, -16, 6, -11, -1532, 27263, 8701, -1804, 159, -16, 6, -10, -1537, 27254, 8718, -1807, 159, -16, 6, -9, -1543, 27244, 8734, -1809, 160, -16,
+ 6, -8, -1548, 27234, 8751, -1812, 160, -15, 6, -6, -1554, 27224, 8768, -1814, 160, -15, 6, -5, -1559, 27214, 8784, -1817, 161, -15, 6, -4, -1565, 27204, 8801, -1819, 161, -15, 6, -3, -1570, 27194, 8818, -1822, 161, -15, 6, -2, -1576, 27184, 8834, -1824, 161, -15, 6, -1, -1581, 27174, 8851, -1827, 162, -15, 6, 0, -1587, 27164, 8868, -1829, 162, -15,
+ 6, 1, -1592, 27153, 8885, -1832, 162, -15, 6, 2, -1597, 27143, 8901, -1834, 162, -15, 6, 3, -1603, 27133, 8918, -1837, 163, -15, 6, 4, -1608, 27123, 8935, -1839, 163, -15, 6, 5, -1614, 27113, 8952, -1842, 163, -15, 5, 6, -1619, 27103, 8968, -1844, 164, -15, 5, 7, -1624, 27092, 8985, -1847, 164, -15, 5, 8, -1630, 27082, 9002, -1849, 164, -15,
+ 5, 9, -1635, 27072, 9019, -1852, 164, -15, 5, 10, -1640, 27062, 9035, -1854, 165, -14, 5, 11, -1646, 27051, 9052, -1857, 165, -14, 5, 12, -1651, 27041, 9069, -1859, 165, -14, 5, 13, -1656, 27031, 9086, -1862, 165, -14, 5, 14, -1661, 27020, 9103, -1865, 166, -14, 5, 15, -1667, 27010, 9119, -1867, 166, -14, 5, 16, -1672, 27000, 9136, -1870, 166, -14,
+ 5, 17, -1677, 26989, 9153, -1872, 167, -14, 5, 18, -1682, 26979, 9170, -1875, 167, -14, 5, 19, -1688, 26968, 9187, -1877, 167, -14, 5, 20, -1693, 26958, 9204, -1880, 167, -14, 5, 21, -1698, 26948, 9221, -1882, 168, -14, 5, 22, -1703, 26937, 9237, -1885, 168, -14, 5, 23, -1708, 26926, 9254, -1887, 168, -14, 5, 24, -1713, 26916, 9271, -1890, 168, -14,
+ 5, 25, -1719, 26905, 9288, -1892, 169, -14, 5, 26, -1724, 26895, 9305, -1895, 169, -14, 5, 27, -1729, 26884, 9322, -1897, 169, -13, 5, 28, -1734, 26874, 9339, -1900, 170, -13, 4, 29, -1739, 26863, 9356, -1902, 170, -13, 4, 30, -1744, 26852, 9373, -1905, 170, -13, 4, 31, -1749, 26842, 9390, -1907, 170, -13, 4, 32, -1754, 26831, 9407, -1910, 171, -13,
+ 4, 33, -1759, 26820, 9424, -1912, 171, -13, 4, 34, -1764, 26810, 9441, -1915, 171, -13, 4, 35, -1769, 26799, 9458, -1917, 171, -13, 4, 36, -1774, 26788, 9475, -1920, 172, -13, 4, 37, -1779, 26777, 9492, -1922, 172, -13, 4, 38, -1784, 26767, 9509, -1925, 172, -13, 4, 39, -1789, 26756, 9526, -1927, 173, -13, 4, 40, -1794, 26745, 9543, -1930, 173, -13,
+ 4, 41, -1799, 26734, 9560, -1932, 173, -13, 4, 42, -1804, 26723, 9577, -1935, 173, -13, 4, 43, -1809, 26712, 9594, -1937, 174, -13, 4, 44, -1813, 26701, 9611, -1939, 174, -13, 4, 45, -1818, 26690, 9628, -1942, 174, -13, 4, 46, -1823, 26679, 9645, -1944, 174, -12, 4, 46, -1828, 26669, 9662, -1947, 175, -12, 4, 47, -1833, 26658, 9679, -1949, 175, -12,
+ 4, 48, -1838, 26647, 9696, -1952, 175, -12, 4, 49, -1843, 26636, 9713, -1954, 176, -12, 4, 50, -1847, 26625, 9730, -1957, 176, -12, 4, 51, -1852, 26613, 9747, -1959, 176, -12, 4, 52, -1857, 26602, 9764, -1962, 176, -12, 4, 53, -1862, 26591, 9781, -1964, 177, -12, 4, 54, -1866, 26580, 9799, -1967, 177, -12, 3, 55, -1871, 26569, 9816, -1969, 177, -12,
+ 3, 56, -1876, 26558, 9833, -1972, 177, -12, 3, 57, -1880, 26547, 9850, -1974, 178, -12, 3, 58, -1885, 26536, 9867, -1977, 178, -12, 3, 58, -1890, 26524, 9884, -1979, 178, -12, 3, 59, -1895, 26513, 9901, -1982, 178, -12, 3, 60, -1899, 26502, 9919, -1984, 179, -12, 3, 61, -1904, 26491, 9936, -1986, 179, -12, 3, 62, -1908, 26479, 9953, -1989, 179, -11,
+ 3, 63, -1913, 26468, 9970, -1991, 180, -11, 3, 64, -1918, 26457, 9987, -1994, 180, -11, 3, 65, -1922, 26445, 10004, -1996, 180, -11, 3, 66, -1927, 26434, 10022, -1999, 180, -11, 3, 67, -1931, 26423, 10039, -2001, 181, -11, 3, 67, -1936, 26411, 10056, -2004, 181, -11, 3, 68, -1940, 26400, 10073, -2006, 181, -11, 3, 69, -1945, 26389, 10090, -2009, 181, -11,
+ 3, 70, -1949, 26377, 10108, -2011, 182, -11, 3, 71, -1954, 26366, 10125, -2013, 182, -11, 3, 72, -1958, 26354, 10142, -2016, 182, -11, 3, 73, -1963, 26343, 10159, -2018, 183, -11, 3, 74, -1967, 26331, 10177, -2021, 183, -11, 3, 74, -1972, 26320, 10194, -2023, 183, -11, 3, 75, -1976, 26308, 10211, -2026, 183, -11, 3, 76, -1981, 26297, 10228, -2028, 184, -11,
+ 3, 77, -1985, 26285, 10246, -2031, 184, -11, 3, 78, -1990, 26273, 10263, -2033, 184, -11, 3, 79, -1994, 26262, 10280, -2035, 184, -11, 3, 80, -1998, 26250, 10298, -2038, 185, -10, 3, 80, -2003, 26239, 10315, -2040, 185, -10, 3, 81, -2007, 26227, 10332, -2043, 185, -10, 3, 82, -2011, 26215, 10350, -2045, 185, -10, 2, 83, -2016, 26204, 10367, -2048, 186, -10,
+ 2, 84, -2020, 26192, 10384, -2050, 186, -10, 2, 85, -2024, 26180, 10402, -2052, 186, -10, 2, 85, -2029, 26168, 10419, -2055, 187, -10, 2, 86, -2033, 26157, 10436, -2057, 187, -10, 2, 87, -2037, 26145, 10454, -2060, 187, -10, 2, 88, -2041, 26133, 10471, -2062, 187, -10, 2, 89, -2046, 26121, 10488, -2065, 188, -10, 2, 90, -2050, 26109, 10506, -2067, 188, -10,
+ 2, 90, -2054, 26097, 10523, -2069, 188, -10, 2, 91, -2058, 26086, 10540, -2072, 188, -10, 2, 92, -2062, 26074, 10558, -2074, 189, -10, 2, 93, -2067, 26062, 10575, -2077, 189, -10, 2, 94, -2071, 26050, 10593, -2079, 189, -10, 2, 95, -2075, 26038, 10610, -2081, 189, -10, 2, 95, -2079, 26026, 10627, -2084, 190, -10, 2, 96, -2083, 26014, 10645, -2086, 190, -10,
+ 2, 97, -2087, 26002, 10662, -2089, 190, -9, 2, 98, -2091, 25990, 10680, -2091, 191, -9, 2, 99, -2096, 25978, 10697, -2093, 191, -9, 2, 99, -2100, 25966, 10715, -2096, 191, -9, 2, 100, -2104, 25954, 10732, -2098, 191, -9, 2, 101, -2108, 25942, 10749, -2101, 192, -9, 2, 102, -2112, 25930, 10767, -2103, 192, -9, 2, 103, -2116, 25918, 10784, -2105, 192, -9,
+ 2, 103, -2120, 25905, 10802, -2108, 192, -9, 2, 104, -2124, 25893, 10819, -2110, 193, -9, 2, 105, -2128, 25881, 10837, -2113, 193, -9, 2, 106, -2132, 25869, 10854, -2115, 193, -9, 2, 106, -2136, 25857, 10872, -2117, 193, -9, 2, 107, -2140, 25845, 10889, -2120, 194, -9, 2, 108, -2144, 25832, 10907, -2122, 194, -9, 2, 109, -2148, 25820, 10924, -2125, 194, -9,
+ 2, 110, -2152, 25808, 10942, -2127, 194, -9, 2, 110, -2155, 25796, 10959, -2129, 195, -9, 2, 111, -2159, 25783, 10977, -2132, 195, -9, 2, 112, -2163, 25771, 10994, -2134, 195, -9, 2, 113, -2167, 25759, 11012, -2136, 196, -9, 2, 113, -2171, 25746, 11029, -2139, 196, -9, 2, 114, -2175, 25734, 11047, -2141, 196, -8, 1, 115, -2179, 25722, 11064, -2143, 196, -8,
+ 1, 116, -2182, 25709, 11082, -2146, 197, -8, 1, 116, -2186, 25697, 11099, -2148, 197, -8, 1, 117, -2190, 25684, 11117, -2151, 197, -8, 1, 118, -2194, 25672, 11135, -2153, 197, -8, 1, 119, -2198, 25659, 11152, -2155, 198, -8, 1, 119, -2201, 25647, 11170, -2158, 198, -8, 1, 120, -2205, 25634, 11187, -2160, 198, -8, 1, 121, -2209, 25622, 11205, -2162, 198, -8,
+ 1, 122, -2213, 25609, 11222, -2165, 199, -8, 1, 122, -2216, 25597, 11240, -2167, 199, -8, 1, 123, -2220, 25584, 11258, -2169, 199, -8, 1, 124, -2224, 25572, 11275, -2172, 199, -8, 1, 125, -2227, 25559, 11293, -2174, 200, -8, 1, 125, -2231, 25546, 11311, -2176, 200, -8, 1, 126, -2235, 25534, 11328, -2179, 200, -8, 1, 127, -2238, 25521, 11346, -2181, 200, -8,
+ 1, 127, -2242, 25508, 11363, -2183, 201, -8, 1, 128, -2246, 25496, 11381, -2186, 201, -8, 1, 129, -2249, 25483, 11399, -2188, 201, -8, 1, 130, -2253, 25470, 11416, -2190, 202, -8, 1, 130, -2256, 25458, 11434, -2193, 202, -8, 1, 131, -2260, 25445, 11452, -2195, 202, -8, 1, 132, -2264, 25432, 11469, -2197, 202, -7, 1, 132, -2267, 25419, 11487, -2200, 203, -7,
+ 1, 133, -2271, 25407, 11505, -2202, 203, -7, 1, 134, -2274, 25394, 11522, -2204, 203, -7, 1, 135, -2278, 25381, 11540, -2207, 203, -7, 1, 135, -2281, 25368, 11558, -2209, 204, -7, 1, 136, -2285, 25355, 11575, -2211, 204, -7, 1, 137, -2288, 25342, 11593, -2213, 204, -7, 1, 137, -2292, 25329, 11611, -2216, 204, -7, 1, 138, -2295, 25317, 11628, -2218, 205, -7,
+ 1, 139, -2299, 25304, 11646, -2220, 205, -7, 1, 139, -2302, 25291, 11664, -2223, 205, -7, 1, 140, -2305, 25278, 11681, -2225, 205, -7, 1, 141, -2309, 25265, 11699, -2227, 206, -7, 1, 141, -2312, 25252, 11717, -2229, 206, -7, 1, 142, -2316, 25239, 11735, -2232, 206, -7, 1, 143, -2319, 25226, 11752, -2234, 206, -7, 1, 143, -2322, 25213, 11770, -2236, 207, -7,
+ 1, 144, -2326, 25200, 11788, -2239, 207, -7, 1, 145, -2329, 25187, 11806, -2241, 207, -7, 1, 145, -2332, 25173, 11823, -2243, 207, -7, 1, 146, -2336, 25160, 11841, -2245, 208, -7, 1, 147, -2339, 25147, 11859, -2248, 208, -7, 1, 147, -2342, 25134, 11877, -2250, 208, -7, 1, 148, -2346, 25121, 11894, -2252, 208, -7, 1, 149, -2349, 25108, 11912, -2254, 209, -6,
+ 1, 149, -2352, 25095, 11930, -2257, 209, -6, 1, 150, -2355, 25081, 11948, -2259, 209, -6, 1, 151, -2359, 25068, 11965, -2261, 209, -6, 1, 151, -2362, 25055, 11983, -2264, 210, -6, 1, 152, -2365, 25042, 12001, -2266, 210, -6, 1, 153, -2368, 25029, 12019, -2268, 210, -6, 1, 153, -2371, 25015, 12036, -2270, 210, -6, 0, 154, -2375, 25002, 12054, -2272, 211, -6,
+ 0, 154, -2378, 24989, 12072, -2275, 211, -6, 0, 155, -2381, 24975, 12090, -2277, 211, -6, 0, 156, -2384, 24962, 12108, -2279, 211, -6, 0, 156, -2387, 24949, 12126, -2281, 212, -6, 0, 157, -2390, 24935, 12143, -2284, 212, -6, 0, 158, -2393, 24922, 12161, -2286, 212, -6, 0, 158, -2396, 24909, 12179, -2288, 212, -6, 0, 159, -2400, 24895, 12197, -2290, 213, -6,
+ 0, 159, -2403, 24882, 12215, -2293, 213, -6, 0, 160, -2406, 24868, 12232, -2295, 213, -6, 0, 161, -2409, 24855, 12250, -2297, 213, -6, 0, 161, -2412, 24841, 12268, -2299, 214, -6, 0, 162, -2415, 24828, 12286, -2301, 214, -6, 0, 163, -2418, 24814, 12304, -2304, 214, -6, 0, 163, -2421, 24801, 12322, -2306, 214, -6, 0, 164, -2424, 24787, 12340, -2308, 215, -6,
+ 0, 164, -2427, 24774, 12357, -2310, 215, -6, 0, 165, -2430, 24760, 12375, -2312, 215, -6, 0, 166, -2433, 24747, 12393, -2315, 215, -5, 0, 166, -2436, 24733, 12411, -2317, 216, -5, 0, 167, -2439, 24719, 12429, -2319, 216, -5, 0, 167, -2442, 24706, 12447, -2321, 216, -5, 0, 168, -2444, 24692, 12465, -2323, 216, -5, 0, 168, -2447, 24679, 12482, -2326, 217, -5,
+ 0, 169, -2450, 24665, 12500, -2328, 217, -5, 0, 170, -2453, 24651, 12518, -2330, 217, -5, 0, 170, -2456, 24637, 12536, -2332, 217, -5, 0, 171, -2459, 24624, 12554, -2334, 217, -5, 0, 171, -2462, 24610, 12572, -2336, 218, -5, 0, 172, -2465, 24596, 12590, -2339, 218, -5, 0, 173, -2467, 24583, 12608, -2341, 218, -5, 0, 173, -2470, 24569, 12626, -2343, 218, -5,
+ 0, 174, -2473, 24555, 12644, -2345, 219, -5, 0, 174, -2476, 24541, 12661, -2347, 219, -5, 0, 175, -2479, 24527, 12679, -2349, 219, -5, 0, 175, -2481, 24514, 12697, -2351, 219, -5, 0, 176, -2484, 24500, 12715, -2354, 220, -5, 0, 176, -2487, 24486, 12733, -2356, 220, -5, 0, 177, -2490, 24472, 12751, -2358, 220, -5, 0, 178, -2492, 24458, 12769, -2360, 220, -5,
+ 0, 178, -2495, 24444, 12787, -2362, 221, -5, 0, 179, -2498, 24430, 12805, -2364, 221, -5, 0, 179, -2500, 24416, 12823, -2366, 221, -5, 0, 180, -2503, 24402, 12841, -2369, 221, -5, 0, 180, -2506, 24388, 12859, -2371, 222, -5, 0, 181, -2508, 24374, 12877, -2373, 222, -5, 0, 181, -2511, 24360, 12895, -2375, 222, -5, 0, 182, -2514, 24346, 12913, -2377, 222, -5,
+ 0, 183, -2516, 24332, 12931, -2379, 222, -5, 0, 183, -2519, 24318, 12949, -2381, 223, -4, 0, 184, -2521, 24304, 12966, -2383, 223, -4, 0, 184, -2524, 24290, 12984, -2385, 223, -4, 0, 185, -2527, 24276, 13002, -2388, 223, -4, 0, 185, -2529, 24262, 13020, -2390, 224, -4, 0, 186, -2532, 24248, 13038, -2392, 224, -4, 0, 186, -2534, 24234, 13056, -2394, 224, -4,
+ 0, 187, -2537, 24220, 13074, -2396, 224, -4, 0, 187, -2539, 24206, 13092, -2398, 225, -4, 0, 188, -2542, 24191, 13110, -2400, 225, -4, 0, 188, -2544, 24177, 13128, -2402, 225, -4, 0, 189, -2547, 24163, 13146, -2404, 225, -4, 0, 189, -2549, 24149, 13164, -2406, 225, -4, 0, 190, -2552, 24135, 13182, -2408, 226, -4, 0, 190, -2554, 24120, 13200, -2410, 226, -4,
+ 0, 191, -2557, 24106, 13218, -2412, 226, -4, 0, 191, -2559, 24092, 13236, -2414, 226, -4, 0, 192, -2562, 24078, 13254, -2417, 227, -4, 0, 192, -2564, 24063, 13272, -2419, 227, -4, 0, 193, -2566, 24049, 13290, -2421, 227, -4, 0, 193, -2569, 24035, 13308, -2423, 227, -4, 0, 194, -2571, 24020, 13326, -2425, 228, -4, 0, 194, -2574, 24006, 13344, -2427, 228, -4,
+ 0, 195, -2576, 23992, 13362, -2429, 228, -4, 0, 195, -2578, 23977, 13380, -2431, 228, -4, 0, 196, -2581, 23963, 13398, -2433, 228, -4, 0, 196, -2583, 23948, 13416, -2435, 229, -4, 0, 197, -2585, 23934, 13434, -2437, 229, -4, 0, 197, -2588, 23920, 13452, -2439, 229, -4, 0, 198, -2590, 23905, 13470, -2441, 229, -4, 0, 198, -2592, 23891, 13488, -2443, 230, -4,
+ 0, 199, -2594, 23876, 13506, -2445, 230, -4, 0, 199, -2597, 23862, 13525, -2447, 230, -4, 0, 200, -2599, 23847, 13543, -2449, 230, -4, 0, 200, -2601, 23833, 13561, -2451, 230, -3, 0, 201, -2603, 23818, 13579, -2453, 231, -3, 0, 201, -2606, 23804, 13597, -2455, 231, -3, 0, 201, -2608, 23789, 13615, -2457, 231, -3, 0, 202, -2610, 23775, 13633, -2459, 231, -3,
+ 0, 202, -2612, 23760, 13651, -2461, 231, -3, 0, 203, -2614, 23745, 13669, -2463, 232, -3, 0, 203, -2617, 23731, 13687, -2465, 232, -3, 0, 204, -2619, 23716, 13705, -2467, 232, -3, 0, 204, -2621, 23702, 13723, -2469, 232, -3, 0, 205, -2623, 23687, 13741, -2471, 233, -3, 0, 205, -2625, 23672, 13759, -2473, 233, -3, 0, 206, -2627, 23658, 13777, -2475, 233, -3,
+ 0, 206, -2629, 23643, 13795, -2477, 233, -3, 0, 206, -2631, 23628, 13813, -2479, 233, -3, 0, 207, -2634, 23614, 13831, -2480, 234, -3, 0, 207, -2636, 23599, 13849, -2482, 234, -3, 0, 208, -2638, 23584, 13868, -2484, 234, -3, 0, 208, -2640, 23569, 13886, -2486, 234, -3, 0, 209, -2642, 23555, 13904, -2488, 234, -3, 0, 209, -2644, 23540, 13922, -2490, 235, -3,
+ 0, 210, -2646, 23525, 13940, -2492, 235, -3, 0, 210, -2648, 23510, 13958, -2494, 235, -3, 0, 210, -2650, 23495, 13976, -2496, 235, -3, 0, 211, -2652, 23481, 13994, -2498, 236, -3, 0, 211, -2654, 23466, 14012, -2500, 236, -3, 0, 212, -2656, 23451, 14030, -2502, 236, -3, 0, 212, -2658, 23436, 14048, -2504, 236, -3, 0, 212, -2660, 23421, 14066, -2505, 236, -3,
+ 0, 213, -2662, 23406, 14084, -2507, 237, -3, 0, 213, -2664, 23391, 14103, -2509, 237, -3, 0, 214, -2665, 23376, 14121, -2511, 237, -3, 0, 214, -2667, 23361, 14139, -2513, 237, -3, 0, 215, -2669, 23346, 14157, -2515, 237, -3, 0, 215, -2671, 23332, 14175, -2517, 238, -3, 0, 215, -2673, 23317, 14193, -2519, 238, -3, 0, 216, -2675, 23302, 14211, -2521, 238, -3,
+ 0, 216, -2677, 23287, 14229, -2522, 238, -3, 0, 217, -2679, 23272, 14247, -2524, 238, -3, 0, 217, -2680, 23257, 14265, -2526, 239, -3, 0, 217, -2682, 23242, 14283, -2528, 239, -3, 0, 218, -2684, 23226, 14302, -2530, 239, -2, 0, 218, -2686, 23211, 14320, -2532, 239, -2, 0, 219, -2688, 23196, 14338, -2533, 239, -2, 0, 219, -2689, 23181, 14356, -2535, 240, -2,
+ 0, 219, -2691, 23166, 14374, -2537, 240, -2, 0, 220, -2693, 23151, 14392, -2539, 240, -2, 0, 220, -2695, 23136, 14410, -2541, 240, -2, 0, 220, -2697, 23121, 14428, -2543, 240, -2, 0, 221, -2698, 23106, 14446, -2544, 241, -2, 0, 221, -2700, 23090, 14464, -2546, 241, -2, 0, 222, -2702, 23075, 14483, -2548, 241, -2, 0, 222, -2703, 23060, 14501, -2550, 241, -2,
+ 0, 222, -2705, 23045, 14519, -2552, 241, -2, 0, 223, -2707, 23030, 14537, -2553, 242, -2, 0, 223, -2708, 23015, 14555, -2555, 242, -2, 0, 223, -2710, 22999, 14573, -2557, 242, -2, 0, 224, -2712, 22984, 14591, -2559, 242, -2, 0, 224, -2713, 22969, 14609, -2561, 242, -2, 0, 225, -2715, 22954, 14627, -2562, 243, -2, 0, 225, -2717, 22938, 14646, -2564, 243, -2,
+ 0, 225, -2718, 22923, 14664, -2566, 243, -2, 0, 226, -2720, 22908, 14682, -2568, 243, -2, 0, 226, -2721, 22892, 14700, -2569, 243, -2, 0, 226, -2723, 22877, 14718, -2571, 243, -2, 0, 227, -2725, 22862, 14736, -2573, 244, -2, 0, 227, -2726, 22846, 14754, -2575, 244, -2, 0, 227, -2728, 22831, 14772, -2576, 244, -2, 0, 228, -2729, 22816, 14790, -2578, 244, -2,
+ 0, 228, -2731, 22800, 14809, -2580, 244, -2, 0, 228, -2732, 22785, 14827, -2582, 245, -2, 0, 229, -2734, 22769, 14845, -2583, 245, -2, 0, 229, -2735, 22754, 14863, -2585, 245, -2, 0, 229, -2737, 22738, 14881, -2587, 245, -2, 0, 230, -2738, 22723, 14899, -2589, 245, -2, 0, 230, -2740, 22708, 14917, -2590, 245, -2, 0, 230, -2741, 22692, 14935, -2592, 246, -2,
+ 0, 231, -2743, 22677, 14953, -2594, 246, -2, 0, 231, -2744, 22661, 14972, -2595, 246, -2, 0, 231, -2746, 22646, 14990, -2597, 246, -2, 0, 232, -2747, 22630, 15008, -2599, 246, -2, 0, 232, -2748, 22615, 15026, -2600, 247, -2, 0, 232, -2750, 22599, 15044, -2602, 247, -2, 0, 233, -2751, 22583, 15062, -2604, 247, -2, 0, 233, -2753, 22568, 15080, -2606, 247, -2,
+ 0, 233, -2754, 22552, 15098, -2607, 247, -2, 0, 234, -2755, 22537, 15116, -2609, 247, -2, 0, 234, -2757, 22521, 15135, -2611, 248, -2, 0, 234, -2758, 22506, 15153, -2612, 248, -2, 0, 235, -2759, 22490, 15171, -2614, 248, -2, 0, 235, -2761, 22474, 15189, -2615, 248, -2, 0, 235, -2762, 22459, 15207, -2617, 248, -2, 0, 236, -2763, 22443, 15225, -2619, 248, -2,
+ 0, 236, -2765, 22427, 15243, -2620, 249, -1, 0, 236, -2766, 22412, 15261, -2622, 249, -1, 0, 236, -2767, 22396, 15279, -2624, 249, -1, 0, 237, -2769, 22380, 15298, -2625, 249, -1, 0, 237, -2770, 22365, 15316, -2627, 249, -1, 0, 237, -2771, 22349, 15334, -2628, 249, -1, 0, 238, -2772, 22333, 15352, -2630, 250, -1, 0, 238, -2774, 22317, 15370, -2632, 250, -1,
+ 0, 238, -2775, 22302, 15388, -2633, 250, -1, 0, 239, -2776, 22286, 15406, -2635, 250, -1, 0, 239, -2777, 22270, 15424, -2636, 250, -1, 0, 239, -2778, 22254, 15442, -2638, 250, -1, 0, 239, -2780, 22238, 15460, -2640, 251, -1, 0, 240, -2781, 22223, 15479, -2641, 251, -1, 0, 240, -2782, 22207, 15497, -2643, 251, -1, 0, 240, -2783, 22191, 15515, -2644, 251, -1,
+ 0, 241, -2784, 22175, 15533, -2646, 251, -1, 0, 241, -2785, 22159, 15551, -2647, 251, -1, 0, 241, -2786, 22143, 15569, -2649, 252, -1, 0, 241, -2788, 22128, 15587, -2651, 252, -1, 0, 242, -2789, 22112, 15605, -2652, 252, -1, 0, 242, -2790, 22096, 15623, -2654, 252, -1, 0, 242, -2791, 22080, 15641, -2655, 252, -1, 0, 242, -2792, 22064, 15660, -2657, 252, -1,
+ 0, 243, -2793, 22048, 15678, -2658, 253, -1, 0, 243, -2794, 22032, 15696, -2660, 253, -1, 0, 243, -2795, 22016, 15714, -2661, 253, -1, 0, 243, -2796, 22000, 15732, -2663, 253, -1, 0, 244, -2797, 21984, 15750, -2664, 253, -1, 0, 244, -2798, 21968, 15768, -2666, 253, -1, 0, 244, -2799, 21952, 15786, -2667, 253, -1, 0, 244, -2800, 21936, 15804, -2669, 254, -1,
+ 0, 245, -2801, 21920, 15822, -2670, 254, -1, 0, 245, -2802, 21904, 15840, -2672, 254, -1, 0, 245, -2803, 21888, 15858, -2673, 254, -1, 0, 245, -2804, 21872, 15877, -2675, 254, -1, 0, 246, -2805, 21856, 15895, -2676, 254, -1, 0, 246, -2806, 21840, 15913, -2678, 254, -1, 0, 246, -2807, 21824, 15931, -2679, 255, -1, 0, 246, -2808, 21808, 15949, -2680, 255, -1,
+ 0, 247, -2809, 21792, 15967, -2682, 255, -1, 0, 247, -2810, 21776, 15985, -2683, 255, -1, 0, 247, -2811, 21759, 16003, -2685, 255, -1, 0, 247, -2812, 21743, 16021, -2686, 255, -1, 0, 248, -2813, 21727, 16039, -2688, 255, -1, 0, 248, -2813, 21711, 16057, -2689, 256, -1, 0, 248, -2814, 21695, 16075, -2691, 256, -1, 0, 248, -2815, 21679, 16093, -2692, 256, -1,
+ 0, 249, -2816, 21662, 16111, -2693, 256, -1, 0, 249, -2817, 21646, 16130, -2695, 256, -1, 0, 249, -2818, 21630, 16148, -2696, 256, -1, 0, 249, -2819, 21614, 16166, -2698, 256, -1, 0, 249, -2819, 21598, 16184, -2699, 257, -1, 0, 250, -2820, 21581, 16202, -2700, 257, -1, 0, 250, -2821, 21565, 16220, -2702, 257, -1, 0, 250, -2822, 21549, 16238, -2703, 257, -1,
+ 0, 250, -2823, 21533, 16256, -2704, 257, -1, 0, 251, -2823, 21516, 16274, -2706, 257, -1, 0, 251, -2824, 21500, 16292, -2707, 257, -1, 0, 251, -2825, 21484, 16310, -2708, 257, -1, 0, 251, -2826, 21468, 16328, -2710, 258, -1, 0, 251, -2826, 21451, 16346, -2711, 258, -1, 0, 252, -2827, 21435, 16364, -2712, 258, -1, 0, 252, -2828, 21419, 16382, -2714, 258, -1,
+ 0, 252, -2829, 21402, 16400, -2715, 258, -1, 0, 252, -2829, 21386, 16418, -2716, 258, -1, 0, 252, -2830, 21370, 16436, -2718, 258, -1, 0, 253, -2831, 21353, 16454, -2719, 259, -1, 0, 253, -2831, 21337, 16472, -2720, 259, -1, 0, 253, -2832, 21320, 16490, -2722, 259, -1, 0, 253, -2833, 21304, 16508, -2723, 259, -1, 0, 253, -2833, 21288, 16526, -2724, 259, -1,
+ 0, 254, -2834, 21271, 16544, -2726, 259, -1, 0, 254, -2835, 21255, 16562, -2727, 259, -1, 0, 254, -2835, 21238, 16580, -2728, 259, -1, 0, 254, -2836, 21222, 16598, -2729, 259, -1, 0, 254, -2836, 21205, 16616, -2731, 260, -1, 0, 255, -2837, 21189, 16634, -2732, 260, -1, 0, 255, -2838, 21173, 16652, -2733, 260, -1, 0, 255, -2838, 21156, 16670, -2734, 260, -1,
+ 0, 255, -2839, 21140, 16688, -2736, 260, -1, 0, 255, -2839, 21123, 16706, -2737, 260, 0, 0, 255, -2840, 21107, 16724, -2738, 260, 0, 0, 256, -2840, 21090, 16742, -2739, 260, 0, 0, 256, -2841, 21074, 16760, -2740, 260, 0, 0, 256, -2841, 21057, 16778, -2742, 261, 0, 0, 256, -2842, 21040, 16796, -2743, 261, 0, 0, 256, -2842, 21024, 16814, -2744, 261, 0,
+ 0, 256, -2843, 21007, 16832, -2745, 261, 0, 0, 257, -2843, 20991, 16850, -2746, 261, 0, 0, 257, -2844, 20974, 16868, -2748, 261, 0, 0, 257, -2844, 20958, 16886, -2749, 261, 0, 0, 257, -2845, 20941, 16904, -2750, 261, 0, 0, 257, -2845, 20924, 16922, -2751, 261, 0, 0, 257, -2846, 20908, 16940, -2752, 262, 0, 0, 258, -2846, 20891, 16958, -2753, 262, 0,
+ 0, 258, -2847, 20875, 16976, -2755, 262, 0, 0, 258, -2847, 20858, 16994, -2756, 262, 0, 0, 258, -2847, 20841, 17012, -2757, 262, 0, 0, 258, -2848, 20825, 17030, -2758, 262, 0, 0, 258, -2848, 20808, 17048, -2759, 262, 0, 0, 258, -2849, 20791, 17066, -2760, 262, 0, 0, 259, -2849, 20775, 17084, -2761, 262, 0, 0, 259, -2849, 20758, 17101, -2763, 262, 0,
+ 0, 259, -2850, 20741, 17119, -2764, 262, 0, 0, 259, -2850, 20724, 17137, -2765, 263, 0, 0, 259, -2851, 20708, 17155, -2766, 263, 0, 0, 259, -2851, 20691, 17173, -2767, 263, 0, 0, 259, -2851, 20674, 17191, -2768, 263, 0, 0, 260, -2852, 20657, 17209, -2769, 263, 0, 0, 260, -2852, 20641, 17227, -2770, 263, 0, 0, 260, -2852, 20624, 17245, -2771, 263, 0,
+ 0, 260, -2852, 20607, 17263, -2772, 263, 0, 0, 260, -2853, 20590, 17281, -2773, 263, 0, 0, 260, -2853, 20574, 17299, -2774, 263, 0, 0, 260, -2853, 20557, 17316, -2775, 263, 0, 0, 261, -2854, 20540, 17334, -2776, 264, 0, 0, 261, -2854, 20523, 17352, -2777, 264, 0, 0, 261, -2854, 20506, 17370, -2778, 264, 0, 0, 261, -2854, 20490, 17388, -2779, 264, 0,
+ 0, 261, -2855, 20473, 17406, -2780, 264, 0, 0, 261, -2855, 20456, 17424, -2782, 264, 0, 0, 261, -2855, 20439, 17442, -2783, 264, 0, 0, 261, -2855, 20422, 17459, -2783, 264, 0, 0, 262, -2855, 20405, 17477, -2784, 264, 0, 0, 262, -2856, 20388, 17495, -2785, 264, 0, 0, 262, -2856, 20372, 17513, -2786, 264, 0, 0, 262, -2856, 20355, 17531, -2787, 264, 0,
+ 0, 262, -2856, 20338, 17549, -2788, 264, 0, 0, 262, -2856, 20321, 17567, -2789, 264, 0, 0, 262, -2856, 20304, 17584, -2790, 265, 0, 0, 262, -2857, 20287, 17602, -2791, 265, 0, 0, 262, -2857, 20270, 17620, -2792, 265, 0, 0, 263, -2857, 20253, 17638, -2793, 265, 0, 0, 263, -2857, 20236, 17656, -2794, 265, 0, 0, 263, -2857, 20219, 17673, -2795, 265, 0,
+ 0, 263, -2857, 20202, 17691, -2796, 265, 0, 0, 263, -2857, 20185, 17709, -2797, 265, 0, 0, 263, -2857, 20168, 17727, -2798, 265, 0, 0, 263, -2857, 20151, 17745, -2799, 265, 0, 0, 263, -2858, 20134, 17763, -2799, 265, 0, 0, 263, -2858, 20117, 17780, -2800, 265, 0, 0, 263, -2858, 20100, 17798, -2801, 265, 0, 0, 264, -2858, 20083, 17816, -2802, 265, 0,
+ 0, 264, -2858, 20066, 17834, -2803, 265, 0, 0, 264, -2858, 20049, 17851, -2804, 265, 0, 0, 264, -2858, 20032, 17869, -2805, 266, 0, 0, 264, -2858, 20015, 17887, -2806, 266, 0, 0, 264, -2858, 19998, 17905, -2806, 266, 0, 0, 264, -2858, 19981, 17923, -2807, 266, 0, 0, 264, -2858, 19964, 17940, -2808, 266, 0, 0, 264, -2858, 19947, 17958, -2809, 266, 0,
+ 0, 264, -2858, 19930, 17976, -2810, 266, 0, 0, 264, -2858, 19913, 17994, -2810, 266, 0, 0, 264, -2858, 19896, 18011, -2811, 266, 0, 0, 265, -2858, 19878, 18029, -2812, 266, 0, 0, 265, -2858, 19861, 18047, -2813, 266, 0, 0, 265, -2858, 19844, 18064, -2814, 266, 0, 0, 265, -2858, 19827, 18082, -2814, 266, 0, 0, 265, -2857, 19810, 18100, -2815, 266, 0,
+ 0, 265, -2857, 19793, 18118, -2816, 266, 0, 0, 265, -2857, 19776, 18135, -2817, 266, 0, 0, 265, -2857, 19758, 18153, -2817, 266, 0, 0, 265, -2857, 19741, 18171, -2818, 266, 0, 0, 265, -2857, 19724, 18188, -2819, 266, 0, 0, 265, -2857, 19707, 18206, -2820, 266, 0, 0, 265, -2857, 19690, 18224, -2820, 266, 0, 0, 265, -2857, 19673, 18241, -2821, 266, 0,
+ 0, 265, -2856, 19655, 18259, -2822, 266, 0, 0, 265, -2856, 19638, 18277, -2823, 266, 0, 0, 266, -2856, 19621, 18294, -2823, 267, 0, 0, 266, -2856, 19604, 18312, -2824, 267, 0, 0, 266, -2856, 19586, 18330, -2825, 267, 0, 0, 266, -2856, 19569, 18347, -2825, 267, 0, 0, 266, -2855, 19552, 18365, -2826, 267, 0, 0, 266, -2855, 19535, 18383, -2827, 267, 0,
+ 0, 266, -2855, 19518, 18400, -2827, 267, 0, 0, 266, -2855, 19500, 18418, -2828, 267, 0, 0, 266, -2855, 19483, 18436, -2829, 267, 0, 0, 266, -2854, 19466, 18453, -2829, 267, 0, 0, 266, -2854, 19448, 18471, -2830, 267, 0, 0, 266, -2854, 19431, 18488, -2830, 267, 0, 0, 266, -2854, 19414, 18506, -2831, 267, 0, 0, 266, -2853, 19397, 18524, -2832, 267, 0,
+ 0, 266, -2853, 19379, 18541, -2832, 267, 0, 0, 266, -2853, 19362, 18559, -2833, 267, 0, 0, 266, -2853, 19345, 18576, -2833, 267, 0, 0, 266, -2852, 19327, 18594, -2834, 267, 0, 0, 266, -2852, 19310, 18612, -2835, 267, 0, 0, 266, -2852, 19293, 18629, -2835, 267, 0, 0, 266, -2851, 19275, 18647, -2836, 267, 0, 0, 266, -2851, 19258, 18664, -2836, 267, 0,
+ 0, 266, -2851, 19241, 18682, -2837, 267, 0, 0, 267, -2851, 19223, 18699, -2837, 267, 0, 0, 267, -2850, 19206, 18717, -2838, 267, 0, 0, 267, -2850, 19188, 18734, -2839, 267, 0, 0, 267, -2850, 19171, 18752, -2839, 267, 0, 0, 267, -2849, 19154, 18770, -2840, 267, 0, 0, 267, -2849, 19136, 18787, -2840, 267, 0, 0, 267, -2848, 19119, 18805, -2841, 267, 0,
+ 0, 267, -2848, 19101, 18822, -2841, 267, 0, 0, 267, -2848, 19084, 18840, -2842, 267, 0, 0, 267, -2847, 19067, 18857, -2842, 267, 0, 0, 267, -2847, 19049, 18875, -2843, 267, 0, 0, 267, -2846, 19032, 18892, -2843, 267, 0, 0, 267, -2846, 19014, 18910, -2843, 267, 0, 0, 267, -2846, 18997, 18927, -2844, 267, 0, 0, 267, -2845, 18979, 18944, -2844, 267, 0,
+ 0, 267, -2845, 18962, 18962, -2845, 267, 0, 0, 267, -2844, 18944, 18979, -2845, 267, 0, 0, 267, -2844, 18927, 18997, -2846, 267, 0, 0, 267, -2843, 18910, 19014, -2846, 267, 0, 0, 267, -2843, 18892, 19032, -2846, 267, 0, 0, 267, -2843, 18875, 19049, -2847, 267, 0, 0, 267, -2842, 18857, 19067, -2847, 267, 0, 0, 267, -2842, 18840, 19084, -2848, 267, 0,
+ 0, 267, -2841, 18822, 19101, -2848, 267, 0, 0, 267, -2841, 18805, 19119, -2848, 267, 0, 0, 267, -2840, 18787, 19136, -2849, 267, 0, 0, 267, -2840, 18770, 19154, -2849, 267, 0, 0, 267, -2839, 18752, 19171, -2850, 267, 0, 0, 267, -2839, 18734, 19188, -2850, 267, 0, 0, 267, -2838, 18717, 19206, -2850, 267, 0, 0, 267, -2837, 18699, 19223, -2851, 267, 0,
+ 0, 267, -2837, 18682, 19241, -2851, 266, 0, 0, 267, -2836, 18664, 19258, -2851, 266, 0, 0, 267, -2836, 18647, 19275, -2851, 266, 0, 0, 267, -2835, 18629, 19293, -2852, 266, 0, 0, 267, -2835, 18612, 19310, -2852, 266, 0, 0, 267, -2834, 18594, 19327, -2852, 266, 0, 0, 267, -2833, 18576, 19345, -2853, 266, 0, 0, 267, -2833, 18559, 19362, -2853, 266, 0,
+ 0, 267, -2832, 18541, 19379, -2853, 266, 0, 0, 267, -2832, 18524, 19397, -2853, 266, 0, 0, 267, -2831, 18506, 19414, -2854, 266, 0, 0, 267, -2830, 18488, 19431, -2854, 266, 0, 0, 267, -2830, 18471, 19448, -2854, 266, 0, 0, 267, -2829, 18453, 19466, -2854, 266, 0, 0, 267, -2829, 18436, 19483, -2855, 266, 0, 0, 267, -2828, 18418, 19500, -2855, 266, 0,
+ 0, 267, -2827, 18400, 19518, -2855, 266, 0, 0, 267, -2827, 18383, 19535, -2855, 266, 0, 0, 267, -2826, 18365, 19552, -2855, 266, 0, 0, 267, -2825, 18347, 19569, -2856, 266, 0, 0, 267, -2825, 18330, 19586, -2856, 266, 0, 0, 267, -2824, 18312, 19604, -2856, 266, 0, 0, 267, -2823, 18294, 19621, -2856, 266, 0, 0, 266, -2823, 18277, 19638, -2856, 265, 0,
+ 0, 266, -2822, 18259, 19655, -2856, 265, 0, 0, 266, -2821, 18241, 19673, -2857, 265, 0, 0, 266, -2820, 18224, 19690, -2857, 265, 0, 0, 266, -2820, 18206, 19707, -2857, 265, 0, 0, 266, -2819, 18188, 19724, -2857, 265, 0, 0, 266, -2818, 18171, 19741, -2857, 265, 0, 0, 266, -2817, 18153, 19758, -2857, 265, 0, 0, 266, -2817, 18135, 19776, -2857, 265, 0,
+ 0, 266, -2816, 18118, 19793, -2857, 265, 0, 0, 266, -2815, 18100, 19810, -2857, 265, 0, 0, 266, -2814, 18082, 19827, -2858, 265, 0, 0, 266, -2814, 18064, 19844, -2858, 265, 0, 0, 266, -2813, 18047, 19861, -2858, 265, 0, 0, 266, -2812, 18029, 19878, -2858, 265, 0, 0, 266, -2811, 18011, 19896, -2858, 264, 0, 0, 266, -2810, 17994, 19913, -2858, 264, 0,
+ 0, 266, -2810, 17976, 19930, -2858, 264, 0, 0, 266, -2809, 17958, 19947, -2858, 264, 0, 0, 266, -2808, 17940, 19964, -2858, 264, 0, 0, 266, -2807, 17923, 19981, -2858, 264, 0, 0, 266, -2806, 17905, 19998, -2858, 264, 0, 0, 266, -2806, 17887, 20015, -2858, 264, 0, 0, 266, -2805, 17869, 20032, -2858, 264, 0, 0, 265, -2804, 17851, 20049, -2858, 264, 0,
+ 0, 265, -2803, 17834, 20066, -2858, 264, 0, 0, 265, -2802, 17816, 20083, -2858, 264, 0, 0, 265, -2801, 17798, 20100, -2858, 263, 0, 0, 265, -2800, 17780, 20117, -2858, 263, 0, 0, 265, -2799, 17763, 20134, -2858, 263, 0, 0, 265, -2799, 17745, 20151, -2857, 263, 0, 0, 265, -2798, 17727, 20168, -2857, 263, 0, 0, 265, -2797, 17709, 20185, -2857, 263, 0,
+ 0, 265, -2796, 17691, 20202, -2857, 263, 0, 0, 265, -2795, 17673, 20219, -2857, 263, 0, 0, 265, -2794, 17656, 20236, -2857, 263, 0, 0, 265, -2793, 17638, 20253, -2857, 263, 0, 0, 265, -2792, 17620, 20270, -2857, 262, 0, 0, 265, -2791, 17602, 20287, -2857, 262, 0, 0, 265, -2790, 17584, 20304, -2856, 262, 0, 0, 264, -2789, 17567, 20321, -2856, 262, 0,
+ 0, 264, -2788, 17549, 20338, -2856, 262, 0, 0, 264, -2787, 17531, 20355, -2856, 262, 0, 0, 264, -2786, 17513, 20372, -2856, 262, 0, 0, 264, -2785, 17495, 20388, -2856, 262, 0, 0, 264, -2784, 17477, 20405, -2855, 262, 0, 0, 264, -2783, 17459, 20422, -2855, 261, 0, 0, 264, -2783, 17442, 20439, -2855, 261, 0, 0, 264, -2782, 17424, 20456, -2855, 261, 0,
+ 0, 264, -2780, 17406, 20473, -2855, 261, 0, 0, 264, -2779, 17388, 20490, -2854, 261, 0, 0, 264, -2778, 17370, 20506, -2854, 261, 0, 0, 264, -2777, 17352, 20523, -2854, 261, 0, 0, 264, -2776, 17334, 20540, -2854, 261, 0, 0, 263, -2775, 17316, 20557, -2853, 260, 0, 0, 263, -2774, 17299, 20574, -2853, 260, 0, 0, 263, -2773, 17281, 20590, -2853, 260, 0,
+ 0, 263, -2772, 17263, 20607, -2852, 260, 0, 0, 263, -2771, 17245, 20624, -2852, 260, 0, 0, 263, -2770, 17227, 20641, -2852, 260, 0, 0, 263, -2769, 17209, 20657, -2852, 260, 0, 0, 263, -2768, 17191, 20674, -2851, 259, 0, 0, 263, -2767, 17173, 20691, -2851, 259, 0, 0, 263, -2766, 17155, 20708, -2851, 259, 0, 0, 263, -2765, 17137, 20724, -2850, 259, 0,
+ 0, 262, -2764, 17119, 20741, -2850, 259, 0, 0, 262, -2763, 17101, 20758, -2849, 259, 0, 0, 262, -2761, 17084, 20775, -2849, 259, 0, 0, 262, -2760, 17066, 20791, -2849, 258, 0, 0, 262, -2759, 17048, 20808, -2848, 258, 0, 0, 262, -2758, 17030, 20825, -2848, 258, 0, 0, 262, -2757, 17012, 20841, -2847, 258, 0, 0, 262, -2756, 16994, 20858, -2847, 258, 0,
+ 0, 262, -2755, 16976, 20875, -2847, 258, 0, 0, 262, -2753, 16958, 20891, -2846, 258, 0, 0, 262, -2752, 16940, 20908, -2846, 257, 0, 0, 261, -2751, 16922, 20924, -2845, 257, 0, 0, 261, -2750, 16904, 20941, -2845, 257, 0, 0, 261, -2749, 16886, 20958, -2844, 257, 0, 0, 261, -2748, 16868, 20974, -2844, 257, 0, 0, 261, -2746, 16850, 20991, -2843, 257, 0,
+ 0, 261, -2745, 16832, 21007, -2843, 256, 0, 0, 261, -2744, 16814, 21024, -2842, 256, 0, 0, 261, -2743, 16796, 21040, -2842, 256, 0, 0, 261, -2742, 16778, 21057, -2841, 256, 0, 0, 260, -2740, 16760, 21074, -2841, 256, 0, 0, 260, -2739, 16742, 21090, -2840, 256, 0, 0, 260, -2738, 16724, 21107, -2840, 255, 0, 0, 260, -2737, 16706, 21123, -2839, 255, 0,
+ -1, 260, -2736, 16688, 21140, -2839, 255, 0, -1, 260, -2734, 16670, 21156, -2838, 255, 0, -1, 260, -2733, 16652, 21173, -2838, 255, 0, -1, 260, -2732, 16634, 21189, -2837, 255, 0, -1, 260, -2731, 16616, 21205, -2836, 254, 0, -1, 259, -2729, 16598, 21222, -2836, 254, 0, -1, 259, -2728, 16580, 21238, -2835, 254, 0, -1, 259, -2727, 16562, 21255, -2835, 254, 0,
+ -1, 259, -2726, 16544, 21271, -2834, 254, 0, -1, 259, -2724, 16526, 21288, -2833, 253, 0, -1, 259, -2723, 16508, 21304, -2833, 253, 0, -1, 259, -2722, 16490, 21320, -2832, 253, 0, -1, 259, -2720, 16472, 21337, -2831, 253, 0, -1, 259, -2719, 16454, 21353, -2831, 253, 0, -1, 258, -2718, 16436, 21370, -2830, 252, 0, -1, 258, -2716, 16418, 21386, -2829, 252, 0,
+ -1, 258, -2715, 16400, 21402, -2829, 252, 0, -1, 258, -2714, 16382, 21419, -2828, 252, 0, -1, 258, -2712, 16364, 21435, -2827, 252, 0, -1, 258, -2711, 16346, 21451, -2826, 251, 0, -1, 258, -2710, 16328, 21468, -2826, 251, 0, -1, 257, -2708, 16310, 21484, -2825, 251, 0, -1, 257, -2707, 16292, 21500, -2824, 251, 0, -1, 257, -2706, 16274, 21516, -2823, 251, 0,
+ -1, 257, -2704, 16256, 21533, -2823, 250, 0, -1, 257, -2703, 16238, 21549, -2822, 250, 0, -1, 257, -2702, 16220, 21565, -2821, 250, 0, -1, 257, -2700, 16202, 21581, -2820, 250, 0, -1, 257, -2699, 16184, 21598, -2819, 249, 0, -1, 256, -2698, 16166, 21614, -2819, 249, 0, -1, 256, -2696, 16148, 21630, -2818, 249, 0, -1, 256, -2695, 16130, 21646, -2817, 249, 0,
+ -1, 256, -2693, 16111, 21662, -2816, 249, 0, -1, 256, -2692, 16093, 21679, -2815, 248, 0, -1, 256, -2691, 16075, 21695, -2814, 248, 0, -1, 256, -2689, 16057, 21711, -2813, 248, 0, -1, 255, -2688, 16039, 21727, -2813, 248, 0, -1, 255, -2686, 16021, 21743, -2812, 247, 0, -1, 255, -2685, 16003, 21759, -2811, 247, 0, -1, 255, -2683, 15985, 21776, -2810, 247, 0,
+ -1, 255, -2682, 15967, 21792, -2809, 247, 0, -1, 255, -2680, 15949, 21808, -2808, 246, 0, -1, 255, -2679, 15931, 21824, -2807, 246, 0, -1, 254, -2678, 15913, 21840, -2806, 246, 0, -1, 254, -2676, 15895, 21856, -2805, 246, 0, -1, 254, -2675, 15877, 21872, -2804, 245, 0, -1, 254, -2673, 15858, 21888, -2803, 245, 0, -1, 254, -2672, 15840, 21904, -2802, 245, 0,
+ -1, 254, -2670, 15822, 21920, -2801, 245, 0, -1, 254, -2669, 15804, 21936, -2800, 244, 0, -1, 253, -2667, 15786, 21952, -2799, 244, 0, -1, 253, -2666, 15768, 21968, -2798, 244, 0, -1, 253, -2664, 15750, 21984, -2797, 244, 0, -1, 253, -2663, 15732, 22000, -2796, 243, 0, -1, 253, -2661, 15714, 22016, -2795, 243, 0, -1, 253, -2660, 15696, 22032, -2794, 243, 0,
+ -1, 253, -2658, 15678, 22048, -2793, 243, 0, -1, 252, -2657, 15660, 22064, -2792, 242, 0, -1, 252, -2655, 15641, 22080, -2791, 242, 0, -1, 252, -2654, 15623, 22096, -2790, 242, 0, -1, 252, -2652, 15605, 22112, -2789, 242, 0, -1, 252, -2651, 15587, 22128, -2788, 241, 0, -1, 252, -2649, 15569, 22143, -2786, 241, 0, -1, 251, -2647, 15551, 22159, -2785, 241, 0,
+ -1, 251, -2646, 15533, 22175, -2784, 241, 0, -1, 251, -2644, 15515, 22191, -2783, 240, 0, -1, 251, -2643, 15497, 22207, -2782, 240, 0, -1, 251, -2641, 15479, 22223, -2781, 240, 0, -1, 251, -2640, 15460, 22238, -2780, 239, 0, -1, 250, -2638, 15442, 22254, -2778, 239, 0, -1, 250, -2636, 15424, 22270, -2777, 239, 0, -1, 250, -2635, 15406, 22286, -2776, 239, 0,
+ -1, 250, -2633, 15388, 22302, -2775, 238, 0, -1, 250, -2632, 15370, 22317, -2774, 238, 0, -1, 250, -2630, 15352, 22333, -2772, 238, 0, -1, 249, -2628, 15334, 22349, -2771, 237, 0, -1, 249, -2627, 15316, 22365, -2770, 237, 0, -1, 249, -2625, 15298, 22380, -2769, 237, 0, -1, 249, -2624, 15279, 22396, -2767, 236, 0, -1, 249, -2622, 15261, 22412, -2766, 236, 0,
+ -1, 249, -2620, 15243, 22427, -2765, 236, 0, -2, 248, -2619, 15225, 22443, -2763, 236, 0, -2, 248, -2617, 15207, 22459, -2762, 235, 0, -2, 248, -2615, 15189, 22474, -2761, 235, 0, -2, 248, -2614, 15171, 22490, -2759, 235, 0, -2, 248, -2612, 15153, 22506, -2758, 234, 0, -2, 248, -2611, 15135, 22521, -2757, 234, 0, -2, 247, -2609, 15116, 22537, -2755, 234, 0,
+ -2, 247, -2607, 15098, 22552, -2754, 233, 0, -2, 247, -2606, 15080, 22568, -2753, 233, 0, -2, 247, -2604, 15062, 22583, -2751, 233, 0, -2, 247, -2602, 15044, 22599, -2750, 232, 0, -2, 247, -2600, 15026, 22615, -2748, 232, 0, -2, 246, -2599, 15008, 22630, -2747, 232, 0, -2, 246, -2597, 14990, 22646, -2746, 231, 0, -2, 246, -2595, 14972, 22661, -2744, 231, 0,
+ -2, 246, -2594, 14953, 22677, -2743, 231, 0, -2, 246, -2592, 14935, 22692, -2741, 230, 0, -2, 245, -2590, 14917, 22708, -2740, 230, 0, -2, 245, -2589, 14899, 22723, -2738, 230, 0, -2, 245, -2587, 14881, 22738, -2737, 229, 0, -2, 245, -2585, 14863, 22754, -2735, 229, 0, -2, 245, -2583, 14845, 22769, -2734, 229, 0, -2, 245, -2582, 14827, 22785, -2732, 228, 0,
+ -2, 244, -2580, 14809, 22800, -2731, 228, 0, -2, 244, -2578, 14790, 22816, -2729, 228, 0, -2, 244, -2576, 14772, 22831, -2728, 227, 0, -2, 244, -2575, 14754, 22846, -2726, 227, 0, -2, 244, -2573, 14736, 22862, -2725, 227, 0, -2, 243, -2571, 14718, 22877, -2723, 226, 0, -2, 243, -2569, 14700, 22892, -2721, 226, 0, -2, 243, -2568, 14682, 22908, -2720, 226, 0,
+ -2, 243, -2566, 14664, 22923, -2718, 225, 0, -2, 243, -2564, 14646, 22938, -2717, 225, 0, -2, 243, -2562, 14627, 22954, -2715, 225, 0, -2, 242, -2561, 14609, 22969, -2713, 224, 0, -2, 242, -2559, 14591, 22984, -2712, 224, 0, -2, 242, -2557, 14573, 22999, -2710, 223, 0, -2, 242, -2555, 14555, 23015, -2708, 223, 0, -2, 242, -2553, 14537, 23030, -2707, 223, 0,
+ -2, 241, -2552, 14519, 23045, -2705, 222, 0, -2, 241, -2550, 14501, 23060, -2703, 222, 0, -2, 241, -2548, 14483, 23075, -2702, 222, 0, -2, 241, -2546, 14464, 23090, -2700, 221, 0, -2, 241, -2544, 14446, 23106, -2698, 221, 0, -2, 240, -2543, 14428, 23121, -2697, 220, 0, -2, 240, -2541, 14410, 23136, -2695, 220, 0, -2, 240, -2539, 14392, 23151, -2693, 220, 0,
+ -2, 240, -2537, 14374, 23166, -2691, 219, 0, -2, 240, -2535, 14356, 23181, -2689, 219, 0, -2, 239, -2533, 14338, 23196, -2688, 219, 0, -2, 239, -2532, 14320, 23211, -2686, 218, 0, -2, 239, -2530, 14302, 23226, -2684, 218, 0, -3, 239, -2528, 14283, 23242, -2682, 217, 0, -3, 239, -2526, 14265, 23257, -2680, 217, 0, -3, 238, -2524, 14247, 23272, -2679, 217, 0,
+ -3, 238, -2522, 14229, 23287, -2677, 216, 0, -3, 238, -2521, 14211, 23302, -2675, 216, 0, -3, 238, -2519, 14193, 23317, -2673, 215, 0, -3, 238, -2517, 14175, 23332, -2671, 215, 0, -3, 237, -2515, 14157, 23346, -2669, 215, 0, -3, 237, -2513, 14139, 23361, -2667, 214, 0, -3, 237, -2511, 14121, 23376, -2665, 214, 0, -3, 237, -2509, 14103, 23391, -2664, 213, 0,
+ -3, 237, -2507, 14084, 23406, -2662, 213, 0, -3, 236, -2505, 14066, 23421, -2660, 212, 0, -3, 236, -2504, 14048, 23436, -2658, 212, 0, -3, 236, -2502, 14030, 23451, -2656, 212, 0, -3, 236, -2500, 14012, 23466, -2654, 211, 0, -3, 236, -2498, 13994, 23481, -2652, 211, 0, -3, 235, -2496, 13976, 23495, -2650, 210, 0, -3, 235, -2494, 13958, 23510, -2648, 210, 0,
+ -3, 235, -2492, 13940, 23525, -2646, 210, 0, -3, 235, -2490, 13922, 23540, -2644, 209, 0, -3, 234, -2488, 13904, 23555, -2642, 209, 0, -3, 234, -2486, 13886, 23569, -2640, 208, 0, -3, 234, -2484, 13868, 23584, -2638, 208, 0, -3, 234, -2482, 13849, 23599, -2636, 207, 0, -3, 234, -2480, 13831, 23614, -2634, 207, 0, -3, 233, -2479, 13813, 23628, -2631, 206, 0,
+ -3, 233, -2477, 13795, 23643, -2629, 206, 0, -3, 233, -2475, 13777, 23658, -2627, 206, 0, -3, 233, -2473, 13759, 23672, -2625, 205, 0, -3, 233, -2471, 13741, 23687, -2623, 205, 0, -3, 232, -2469, 13723, 23702, -2621, 204, 0, -3, 232, -2467, 13705, 23716, -2619, 204, 0, -3, 232, -2465, 13687, 23731, -2617, 203, 0, -3, 232, -2463, 13669, 23745, -2614, 203, 0,
+ -3, 231, -2461, 13651, 23760, -2612, 202, 0, -3, 231, -2459, 13633, 23775, -2610, 202, 0, -3, 231, -2457, 13615, 23789, -2608, 201, 0, -3, 231, -2455, 13597, 23804, -2606, 201, 0, -3, 231, -2453, 13579, 23818, -2603, 201, 0, -3, 230, -2451, 13561, 23833, -2601, 200, 0, -4, 230, -2449, 13543, 23847, -2599, 200, 0, -4, 230, -2447, 13525, 23862, -2597, 199, 0,
+ -4, 230, -2445, 13506, 23876, -2594, 199, 0, -4, 230, -2443, 13488, 23891, -2592, 198, 0, -4, 229, -2441, 13470, 23905, -2590, 198, 0, -4, 229, -2439, 13452, 23920, -2588, 197, 0, -4, 229, -2437, 13434, 23934, -2585, 197, 0, -4, 229, -2435, 13416, 23948, -2583, 196, 0, -4, 228, -2433, 13398, 23963, -2581, 196, 0, -4, 228, -2431, 13380, 23977, -2578, 195, 0,
+ -4, 228, -2429, 13362, 23992, -2576, 195, 0, -4, 228, -2427, 13344, 24006, -2574, 194, 0, -4, 228, -2425, 13326, 24020, -2571, 194, 0, -4, 227, -2423, 13308, 24035, -2569, 193, 0, -4, 227, -2421, 13290, 24049, -2566, 193, 0, -4, 227, -2419, 13272, 24063, -2564, 192, 0, -4, 227, -2417, 13254, 24078, -2562, 192, 0, -4, 226, -2414, 13236, 24092, -2559, 191, 0,
+ -4, 226, -2412, 13218, 24106, -2557, 191, 0, -4, 226, -2410, 13200, 24120, -2554, 190, 0, -4, 226, -2408, 13182, 24135, -2552, 190, 0, -4, 225, -2406, 13164, 24149, -2549, 189, 0, -4, 225, -2404, 13146, 24163, -2547, 189, 0, -4, 225, -2402, 13128, 24177, -2544, 188, 0, -4, 225, -2400, 13110, 24191, -2542, 188, 0, -4, 225, -2398, 13092, 24206, -2539, 187, 0,
+ -4, 224, -2396, 13074, 24220, -2537, 187, 0, -4, 224, -2394, 13056, 24234, -2534, 186, 0, -4, 224, -2392, 13038, 24248, -2532, 186, 0, -4, 224, -2390, 13020, 24262, -2529, 185, 0, -4, 223, -2388, 13002, 24276, -2527, 185, 0, -4, 223, -2385, 12984, 24290, -2524, 184, 0, -4, 223, -2383, 12966, 24304, -2521, 184, 0, -4, 223, -2381, 12949, 24318, -2519, 183, 0,
+ -5, 222, -2379, 12931, 24332, -2516, 183, 0, -5, 222, -2377, 12913, 24346, -2514, 182, 0, -5, 222, -2375, 12895, 24360, -2511, 181, 0, -5, 222, -2373, 12877, 24374, -2508, 181, 0, -5, 222, -2371, 12859, 24388, -2506, 180, 0, -5, 221, -2369, 12841, 24402, -2503, 180, 0, -5, 221, -2366, 12823, 24416, -2500, 179, 0, -5, 221, -2364, 12805, 24430, -2498, 179, 0,
+ -5, 221, -2362, 12787, 24444, -2495, 178, 0, -5, 220, -2360, 12769, 24458, -2492, 178, 0, -5, 220, -2358, 12751, 24472, -2490, 177, 0, -5, 220, -2356, 12733, 24486, -2487, 176, 0, -5, 220, -2354, 12715, 24500, -2484, 176, 0, -5, 219, -2351, 12697, 24514, -2481, 175, 0, -5, 219, -2349, 12679, 24527, -2479, 175, 0, -5, 219, -2347, 12661, 24541, -2476, 174, 0,
+ -5, 219, -2345, 12644, 24555, -2473, 174, 0, -5, 218, -2343, 12626, 24569, -2470, 173, 0, -5, 218, -2341, 12608, 24583, -2467, 173, 0, -5, 218, -2339, 12590, 24596, -2465, 172, 0, -5, 218, -2336, 12572, 24610, -2462, 171, 0, -5, 217, -2334, 12554, 24624, -2459, 171, 0, -5, 217, -2332, 12536, 24637, -2456, 170, 0, -5, 217, -2330, 12518, 24651, -2453, 170, 0,
+ -5, 217, -2328, 12500, 24665, -2450, 169, 0, -5, 217, -2326, 12482, 24679, -2447, 168, 0, -5, 216, -2323, 12465, 24692, -2444, 168, 0, -5, 216, -2321, 12447, 24706, -2442, 167, 0, -5, 216, -2319, 12429, 24719, -2439, 167, 0, -5, 216, -2317, 12411, 24733, -2436, 166, 0, -5, 215, -2315, 12393, 24747, -2433, 166, 0, -6, 215, -2312, 12375, 24760, -2430, 165, 0,
+ -6, 215, -2310, 12357, 24774, -2427, 164, 0, -6, 215, -2308, 12340, 24787, -2424, 164, 0, -6, 214, -2306, 12322, 24801, -2421, 163, 0, -6, 214, -2304, 12304, 24814, -2418, 163, 0, -6, 214, -2301, 12286, 24828, -2415, 162, 0, -6, 214, -2299, 12268, 24841, -2412, 161, 0, -6, 213, -2297, 12250, 24855, -2409, 161, 0, -6, 213, -2295, 12232, 24868, -2406, 160, 0,
+ -6, 213, -2293, 12215, 24882, -2403, 159, 0, -6, 213, -2290, 12197, 24895, -2400, 159, 0, -6, 212, -2288, 12179, 24909, -2396, 158, 0, -6, 212, -2286, 12161, 24922, -2393, 158, 0, -6, 212, -2284, 12143, 24935, -2390, 157, 0, -6, 212, -2281, 12126, 24949, -2387, 156, 0, -6, 211, -2279, 12108, 24962, -2384, 156, 0, -6, 211, -2277, 12090, 24975, -2381, 155, 0,
+ -6, 211, -2275, 12072, 24989, -2378, 154, 0, -6, 211, -2272, 12054, 25002, -2375, 154, 0, -6, 210, -2270, 12036, 25015, -2371, 153, 1, -6, 210, -2268, 12019, 25029, -2368, 153, 1, -6, 210, -2266, 12001, 25042, -2365, 152, 1, -6, 210, -2264, 11983, 25055, -2362, 151, 1, -6, 209, -2261, 11965, 25068, -2359, 151, 1, -6, 209, -2259, 11948, 25081, -2355, 150, 1,
+ -6, 209, -2257, 11930, 25095, -2352, 149, 1, -6, 209, -2254, 11912, 25108, -2349, 149, 1, -7, 208, -2252, 11894, 25121, -2346, 148, 1, -7, 208, -2250, 11877, 25134, -2342, 147, 1, -7, 208, -2248, 11859, 25147, -2339, 147, 1, -7, 208, -2245, 11841, 25160, -2336, 146, 1, -7, 207, -2243, 11823, 25173, -2332, 145, 1, -7, 207, -2241, 11806, 25187, -2329, 145, 1,
+ -7, 207, -2239, 11788, 25200, -2326, 144, 1, -7, 207, -2236, 11770, 25213, -2322, 143, 1, -7, 206, -2234, 11752, 25226, -2319, 143, 1, -7, 206, -2232, 11735, 25239, -2316, 142, 1, -7, 206, -2229, 11717, 25252, -2312, 141, 1, -7, 206, -2227, 11699, 25265, -2309, 141, 1, -7, 205, -2225, 11681, 25278, -2305, 140, 1, -7, 205, -2223, 11664, 25291, -2302, 139, 1,
+ -7, 205, -2220, 11646, 25304, -2299, 139, 1, -7, 205, -2218, 11628, 25317, -2295, 138, 1, -7, 204, -2216, 11611, 25329, -2292, 137, 1, -7, 204, -2213, 11593, 25342, -2288, 137, 1, -7, 204, -2211, 11575, 25355, -2285, 136, 1, -7, 204, -2209, 11558, 25368, -2281, 135, 1, -7, 203, -2207, 11540, 25381, -2278, 135, 1, -7, 203, -2204, 11522, 25394, -2274, 134, 1,
+ -7, 203, -2202, 11505, 25407, -2271, 133, 1, -7, 203, -2200, 11487, 25419, -2267, 132, 1, -7, 202, -2197, 11469, 25432, -2264, 132, 1, -8, 202, -2195, 11452, 25445, -2260, 131, 1, -8, 202, -2193, 11434, 25458, -2256, 130, 1, -8, 202, -2190, 11416, 25470, -2253, 130, 1, -8, 201, -2188, 11399, 25483, -2249, 129, 1, -8, 201, -2186, 11381, 25496, -2246, 128, 1,
+ -8, 201, -2183, 11363, 25508, -2242, 127, 1, -8, 200, -2181, 11346, 25521, -2238, 127, 1, -8, 200, -2179, 11328, 25534, -2235, 126, 1, -8, 200, -2176, 11311, 25546, -2231, 125, 1, -8, 200, -2174, 11293, 25559, -2227, 125, 1, -8, 199, -2172, 11275, 25572, -2224, 124, 1, -8, 199, -2169, 11258, 25584, -2220, 123, 1, -8, 199, -2167, 11240, 25597, -2216, 122, 1,
+ -8, 199, -2165, 11222, 25609, -2213, 122, 1, -8, 198, -2162, 11205, 25622, -2209, 121, 1, -8, 198, -2160, 11187, 25634, -2205, 120, 1, -8, 198, -2158, 11170, 25647, -2201, 119, 1, -8, 198, -2155, 11152, 25659, -2198, 119, 1, -8, 197, -2153, 11135, 25672, -2194, 118, 1, -8, 197, -2151, 11117, 25684, -2190, 117, 1, -8, 197, -2148, 11099, 25697, -2186, 116, 1,
+ -8, 197, -2146, 11082, 25709, -2182, 116, 1, -8, 196, -2143, 11064, 25722, -2179, 115, 1, -8, 196, -2141, 11047, 25734, -2175, 114, 2, -9, 196, -2139, 11029, 25746, -2171, 113, 2, -9, 196, -2136, 11012, 25759, -2167, 113, 2, -9, 195, -2134, 10994, 25771, -2163, 112, 2, -9, 195, -2132, 10977, 25783, -2159, 111, 2, -9, 195, -2129, 10959, 25796, -2155, 110, 2,
+ -9, 194, -2127, 10942, 25808, -2152, 110, 2, -9, 194, -2125, 10924, 25820, -2148, 109, 2, -9, 194, -2122, 10907, 25832, -2144, 108, 2, -9, 194, -2120, 10889, 25845, -2140, 107, 2, -9, 193, -2117, 10872, 25857, -2136, 106, 2, -9, 193, -2115, 10854, 25869, -2132, 106, 2, -9, 193, -2113, 10837, 25881, -2128, 105, 2, -9, 193, -2110, 10819, 25893, -2124, 104, 2,
+ -9, 192, -2108, 10802, 25905, -2120, 103, 2, -9, 192, -2105, 10784, 25918, -2116, 103, 2, -9, 192, -2103, 10767, 25930, -2112, 102, 2, -9, 192, -2101, 10749, 25942, -2108, 101, 2, -9, 191, -2098, 10732, 25954, -2104, 100, 2, -9, 191, -2096, 10715, 25966, -2100, 99, 2, -9, 191, -2093, 10697, 25978, -2096, 99, 2, -9, 191, -2091, 10680, 25990, -2091, 98, 2,
+ -9, 190, -2089, 10662, 26002, -2087, 97, 2, -10, 190, -2086, 10645, 26014, -2083, 96, 2, -10, 190, -2084, 10627, 26026, -2079, 95, 2, -10, 189, -2081, 10610, 26038, -2075, 95, 2, -10, 189, -2079, 10593, 26050, -2071, 94, 2, -10, 189, -2077, 10575, 26062, -2067, 93, 2, -10, 189, -2074, 10558, 26074, -2062, 92, 2, -10, 188, -2072, 10540, 26086, -2058, 91, 2,
+ -10, 188, -2069, 10523, 26097, -2054, 90, 2, -10, 188, -2067, 10506, 26109, -2050, 90, 2, -10, 188, -2065, 10488, 26121, -2046, 89, 2, -10, 187, -2062, 10471, 26133, -2041, 88, 2, -10, 187, -2060, 10454, 26145, -2037, 87, 2, -10, 187, -2057, 10436, 26157, -2033, 86, 2, -10, 187, -2055, 10419, 26168, -2029, 85, 2, -10, 186, -2052, 10402, 26180, -2024, 85, 2,
+ -10, 186, -2050, 10384, 26192, -2020, 84, 2, -10, 186, -2048, 10367, 26204, -2016, 83, 2, -10, 185, -2045, 10350, 26215, -2011, 82, 3, -10, 185, -2043, 10332, 26227, -2007, 81, 3, -10, 185, -2040, 10315, 26239, -2003, 80, 3, -10, 185, -2038, 10298, 26250, -1998, 80, 3, -11, 184, -2035, 10280, 26262, -1994, 79, 3, -11, 184, -2033, 10263, 26273, -1990, 78, 3,
+ -11, 184, -2031, 10246, 26285, -1985, 77, 3, -11, 184, -2028, 10228, 26297, -1981, 76, 3, -11, 183, -2026, 10211, 26308, -1976, 75, 3, -11, 183, -2023, 10194, 26320, -1972, 74, 3, -11, 183, -2021, 10177, 26331, -1967, 74, 3, -11, 183, -2018, 10159, 26343, -1963, 73, 3, -11, 182, -2016, 10142, 26354, -1958, 72, 3, -11, 182, -2013, 10125, 26366, -1954, 71, 3,
+ -11, 182, -2011, 10108, 26377, -1949, 70, 3, -11, 181, -2009, 10090, 26389, -1945, 69, 3, -11, 181, -2006, 10073, 26400, -1940, 68, 3, -11, 181, -2004, 10056, 26411, -1936, 67, 3, -11, 181, -2001, 10039, 26423, -1931, 67, 3, -11, 180, -1999, 10022, 26434, -1927, 66, 3, -11, 180, -1996, 10004, 26445, -1922, 65, 3, -11, 180, -1994, 9987, 26457, -1918, 64, 3,
+ -11, 180, -1991, 9970, 26468, -1913, 63, 3, -11, 179, -1989, 9953, 26479, -1908, 62, 3, -12, 179, -1986, 9936, 26491, -1904, 61, 3, -12, 179, -1984, 9919, 26502, -1899, 60, 3, -12, 178, -1982, 9901, 26513, -1895, 59, 3, -12, 178, -1979, 9884, 26524, -1890, 58, 3, -12, 178, -1977, 9867, 26536, -1885, 58, 3, -12, 178, -1974, 9850, 26547, -1880, 57, 3,
+ -12, 177, -1972, 9833, 26558, -1876, 56, 3, -12, 177, -1969, 9816, 26569, -1871, 55, 3, -12, 177, -1967, 9799, 26580, -1866, 54, 4, -12, 177, -1964, 9781, 26591, -1862, 53, 4, -12, 176, -1962, 9764, 26602, -1857, 52, 4, -12, 176, -1959, 9747, 26613, -1852, 51, 4, -12, 176, -1957, 9730, 26625, -1847, 50, 4, -12, 176, -1954, 9713, 26636, -1843, 49, 4,
+ -12, 175, -1952, 9696, 26647, -1838, 48, 4, -12, 175, -1949, 9679, 26658, -1833, 47, 4, -12, 175, -1947, 9662, 26669, -1828, 46, 4, -12, 174, -1944, 9645, 26679, -1823, 46, 4, -13, 174, -1942, 9628, 26690, -1818, 45, 4, -13, 174, -1939, 9611, 26701, -1813, 44, 4, -13, 174, -1937, 9594, 26712, -1809, 43, 4, -13, 173, -1935, 9577, 26723, -1804, 42, 4,
+ -13, 173, -1932, 9560, 26734, -1799, 41, 4, -13, 173, -1930, 9543, 26745, -1794, 40, 4, -13, 173, -1927, 9526, 26756, -1789, 39, 4, -13, 172, -1925, 9509, 26767, -1784, 38, 4, -13, 172, -1922, 9492, 26777, -1779, 37, 4, -13, 172, -1920, 9475, 26788, -1774, 36, 4, -13, 171, -1917, 9458, 26799, -1769, 35, 4, -13, 171, -1915, 9441, 26810, -1764, 34, 4,
+ -13, 171, -1912, 9424, 26820, -1759, 33, 4, -13, 171, -1910, 9407, 26831, -1754, 32, 4, -13, 170, -1907, 9390, 26842, -1749, 31, 4, -13, 170, -1905, 9373, 26852, -1744, 30, 4, -13, 170, -1902, 9356, 26863, -1739, 29, 4, -13, 170, -1900, 9339, 26874, -1734, 28, 5, -13, 169, -1897, 9322, 26884, -1729, 27, 5, -14, 169, -1895, 9305, 26895, -1724, 26, 5,
+ -14, 169, -1892, 9288, 26905, -1719, 25, 5, -14, 168, -1890, 9271, 26916, -1713, 24, 5, -14, 168, -1887, 9254, 26926, -1708, 23, 5, -14, 168, -1885, 9237, 26937, -1703, 22, 5, -14, 168, -1882, 9221, 26947, -1698, 21, 5, -14, 167, -1880, 9204, 26958, -1693, 20, 5, -14, 167, -1877, 9187, 26968, -1688, 19, 5, -14, 167, -1875, 9170, 26979, -1682, 18, 5,
+ -14, 167, -1872, 9153, 26989, -1677, 17, 5, -14, 166, -1870, 9136, 27000, -1672, 16, 5, -14, 166, -1867, 9119, 27010, -1667, 15, 5, -14, 166, -1865, 9103, 27020, -1661, 14, 5, -14, 165, -1862, 9086, 27031, -1656, 13, 5, -14, 165, -1859, 9069, 27041, -1651, 12, 5, -14, 165, -1857, 9052, 27051, -1646, 11, 5, -14, 165, -1854, 9035, 27062, -1640, 10, 5,
+ -15, 164, -1852, 9019, 27072, -1635, 9, 5, -15, 164, -1849, 9002, 27082, -1630, 8, 5, -15, 164, -1847, 8985, 27092, -1624, 7, 5, -15, 164, -1844, 8968, 27103, -1619, 6, 5, -15, 163, -1842, 8952, 27113, -1614, 5, 6, -15, 163, -1839, 8935, 27123, -1608, 4, 6, -15, 163, -1837, 8918, 27133, -1603, 3, 6, -15, 162, -1834, 8901, 27143, -1597, 2, 6,
+ -15, 162, -1832, 8885, 27153, -1592, 1, 6, -15, 162, -1829, 8868, 27164, -1587, 0, 6, -15, 162, -1827, 8851, 27174, -1581, -1, 6, -15, 161, -1824, 8834, 27184, -1576, -2, 6, -15, 161, -1822, 8818, 27194, -1570, -3, 6, -15, 161, -1819, 8801, 27204, -1565, -4, 6, -15, 161, -1817, 8784, 27214, -1559, -5, 6, -15, 160, -1814, 8768, 27224, -1554, -6, 6,
+ -15, 160, -1812, 8751, 27234, -1548, -8, 6, -16, 160, -1809, 8734, 27244, -1543, -9, 6, -16, 159, -1807, 8718, 27254, -1537, -10, 6, -16, 159, -1804, 8701, 27263, -1532, -11, 6, -16, 159, -1801, 8684, 27273, -1526, -12, 6, -16, 159, -1799, 8668, 27283, -1520, -13, 6, -16, 158, -1796, 8651, 27293, -1515, -14, 6, -16, 158, -1794, 8634, 27303, -1509, -15, 6,
+ -16, 158, -1791, 8618, 27313, -1503, -16, 6, -16, 158, -1789, 8601, 27322, -1498, -17, 6, -16, 157, -1786, 8585, 27332, -1492, -18, 7, -16, 157, -1784, 8568, 27342, -1486, -19, 7, -16, 157, -1781, 8552, 27352, -1481, -20, 7, -16, 157, -1779, 8535, 27361, -1475, -22, 7, -16, 156, -1776, 8518, 27371, -1469, -23, 7, -16, 156, -1774, 8502, 27381, -1464, -24, 7,
+ -16, 156, -1771, 8485, 27390, -1458, -25, 7, -16, 155, -1769, 8469, 27400, -1452, -26, 7, -17, 155, -1766, 8452, 27410, -1446, -27, 7, -17, 155, -1763, 8436, 27419, -1441, -28, 7, -17, 155, -1761, 8419, 27429, -1435, -29, 7, -17, 154, -1758, 8403, 27438, -1429, -30, 7, -17, 154, -1756, 8386, 27448, -1423, -31, 7, -17, 154, -1753, 8370, 27457, -1417, -33, 7,
+ -17, 154, -1751, 8353, 27467, -1412, -34, 7, -17, 153, -1748, 8337, 27476, -1406, -35, 7, -17, 153, -1746, 8320, 27486, -1400, -36, 7, -17, 153, -1743, 8304, 27495, -1394, -37, 7, -17, 152, -1741, 8287, 27505, -1388, -38, 7, -17, 152, -1738, 8271, 27514, -1382, -39, 8, -17, 152, -1736, 8255, 27523, -1376, -40, 8, -17, 152, -1733, 8238, 27533, -1370, -42, 8,
+ -17, 151, -1730, 8222, 27542, -1364, -43, 8, -17, 151, -1728, 8205, 27552, -1358, -44, 8, -18, 151, -1725, 8189, 27561, -1352, -45, 8, -18, 151, -1723, 8173, 27570, -1346, -46, 8, -18, 150, -1720, 8156, 27579, -1340, -47, 8, -18, 150, -1718, 8140, 27589, -1334, -49, 8, -18, 150, -1715, 8123, 27598, -1328, -50, 8, -18, 149, -1713, 8107, 27607, -1322, -51, 8,
+ -18, 149, -1710, 8091, 27616, -1316, -52, 8, -18, 149, -1708, 8074, 27625, -1310, -53, 8, -18, 149, -1705, 8058, 27634, -1304, -54, 8, -18, 148, -1703, 8042, 27644, -1298, -55, 8, -18, 148, -1700, 8025, 27653, -1292, -57, 8, -18, 148, -1697, 8009, 27662, -1286, -58, 8, -18, 148, -1695, 7993, 27671, -1280, -59, 8, -18, 147, -1692, 7977, 27680, -1273, -60, 9,
+ -18, 147, -1690, 7960, 27689, -1267, -61, 9, -18, 147, -1687, 7944, 27698, -1261, -63, 9, -19, 147, -1685, 7928, 27707, -1255, -64, 9, -19, 146, -1682, 7912, 27716, -1249, -65, 9, -19, 146, -1680, 7895, 27725, -1243, -66, 9, -19, 146, -1677, 7879, 27734, -1236, -67, 9, -19, 145, -1674, 7863, 27743, -1230, -68, 9, -19, 145, -1672, 7847, 27751, -1224, -70, 9,
+ -19, 145, -1669, 7830, 27760, -1218, -71, 9, -19, 145, -1667, 7814, 27769, -1211, -72, 9, -19, 144, -1664, 7798, 27778, -1205, -73, 9, -19, 144, -1662, 7782, 27787, -1199, -74, 9, -19, 144, -1659, 7766, 27795, -1192, -76, 9, -19, 144, -1657, 7750, 27804, -1186, -77, 9, -19, 143, -1654, 7733, 27813, -1180, -78, 9, -19, 143, -1652, 7717, 27822, -1173, -79, 9,
+ -19, 143, -1649, 7701, 27830, -1167, -81, 10, -20, 142, -1646, 7685, 27839, -1160, -82, 10, -20, 142, -1644, 7669, 27848, -1154, -83, 10, -20, 142, -1641, 7653, 27856, -1148, -84, 10, -20, 142, -1639, 7637, 27865, -1141, -85, 10, -20, 141, -1636, 7621, 27874, -1135, -87, 10, -20, 141, -1634, 7605, 27882, -1128, -88, 10, -20, 141, -1631, 7589, 27891, -1122, -89, 10,
+ -20, 141, -1629, 7572, 27899, -1115, -90, 10, -20, 140, -1626, 7556, 27908, -1109, -92, 10, -20, 140, -1624, 7540, 27916, -1102, -93, 10, -20, 140, -1621, 7524, 27925, -1096, -94, 10, -20, 140, -1618, 7508, 27933, -1089, -95, 10, -20, 139, -1616, 7492, 27941, -1083, -97, 10, -20, 139, -1613, 7476, 27950, -1076, -98, 10, -20, 139, -1611, 7460, 27958, -1070, -99, 11,
+ -21, 138, -1608, 7444, 27967, -1063, -100, 11, -21, 138, -1606, 7428, 27975, -1056, -102, 11, -21, 138, -1603, 7412, 27983, -1050, -103, 11, -21, 138, -1601, 7396, 27992, -1043, -104, 11, -21, 137, -1598, 7380, 28000, -1036, -105, 11, -21, 137, -1595, 7365, 28008, -1030, -107, 11, -21, 137, -1593, 7349, 28016, -1023, -108, 11, -21, 137, -1590, 7333, 28025, -1016, -109, 11,
+ -21, 136, -1588, 7317, 28033, -1010, -110, 11, -21, 136, -1585, 7301, 28041, -1003, -112, 11, -21, 136, -1583, 7285, 28049, -996, -113, 11, -21, 136, -1580, 7269, 28057, -989, -114, 11, -21, 135, -1578, 7253, 28065, -983, -116, 11, -21, 135, -1575, 7237, 28073, -976, -117, 11, -21, 135, -1573, 7221, 28082, -969, -118, 12, -22, 135, -1570, 7206, 28090, -962, -119, 12,
+ -22, 134, -1567, 7190, 28098, -956, -121, 12, -22, 134, -1565, 7174, 28106, -949, -122, 12, -22, 134, -1562, 7158, 28114, -942, -123, 12, -22, 133, -1560, 7142, 28122, -935, -125, 12, -22, 133, -1557, 7127, 28130, -928, -126, 12, -22, 133, -1555, 7111, 28137, -921, -127, 12, -22, 133, -1552, 7095, 28145, -914, -129, 12, -22, 132, -1550, 7079, 28153, -907, -130, 12,
+ -22, 132, -1547, 7063, 28161, -901, -131, 12, -22, 132, -1545, 7048, 28169, -894, -133, 12, -22, 132, -1542, 7032, 28177, -887, -134, 12, -22, 131, -1539, 7016, 28185, -880, -135, 12, -22, 131, -1537, 7001, 28192, -873, -136, 13, -23, 131, -1534, 6985, 28200, -866, -138, 13, -23, 131, -1532, 6969, 28208, -859, -139, 13, -23, 130, -1529, 6953, 28216, -852, -140, 13,
+ -23, 130, -1527, 6938, 28223, -845, -142, 13, -23, 130, -1524, 6922, 28231, -838, -143, 13, -23, 130, -1522, 6906, 28239, -831, -144, 13, -23, 129, -1519, 6891, 28246, -824, -146, 13, -23, 129, -1517, 6875, 28254, -816, -147, 13, -23, 129, -1514, 6859, 28261, -809, -149, 13, -23, 128, -1511, 6844, 28269, -802, -150, 13, -23, 128, -1509, 6828, 28277, -795, -151, 13,
+ -23, 128, -1506, 6813, 28284, -788, -153, 13, -23, 128, -1504, 6797, 28292, -781, -154, 14, -23, 127, -1501, 6781, 28299, -774, -155, 14, -24, 127, -1499, 6766, 28307, -766, -157, 14, -24, 127, -1496, 6750, 28314, -759, -158, 14, -24, 127, -1494, 6735, 28321, -752, -159, 14, -24, 126, -1491, 6719, 28329, -745, -161, 14, -24, 126, -1489, 6704, 28336, -738, -162, 14,
+ -24, 126, -1486, 6688, 28344, -730, -163, 14, -24, 126, -1484, 6673, 28351, -723, -165, 14, -24, 125, -1481, 6657, 28358, -716, -166, 14, -24, 125, -1478, 6642, 28366, -708, -168, 14, -24, 125, -1476, 6626, 28373, -701, -169, 14, -24, 125, -1473, 6611, 28380, -694, -170, 14, -24, 124, -1471, 6595, 28387, -687, -172, 15, -24, 124, -1468, 6580, 28395, -679, -173, 15,
+ -24, 124, -1466, 6564, 28402, -672, -175, 15, -25, 124, -1463, 6549, 28409, -664, -176, 15, -25, 123, -1461, 6533, 28416, -657, -177, 15, -25, 123, -1458, 6518, 28423, -650, -179, 15, -25, 123, -1456, 6503, 28430, -642, -180, 15, -25, 122, -1453, 6487, 28437, -635, -182, 15, -25, 122, -1451, 6472, 28444, -627, -183, 15, -25, 122, -1448, 6457, 28451, -620, -184, 15,
+ -25, 122, -1445, 6441, 28458, -612, -186, 15, -25, 121, -1443, 6426, 28465, -605, -187, 15, -25, 121, -1440, 6410, 28472, -597, -189, 16, -25, 121, -1438, 6395, 28479, -590, -190, 16, -25, 121, -1435, 6380, 28486, -582, -191, 16, -25, 120, -1433, 6365, 28493, -575, -193, 16, -25, 120, -1430, 6349, 28500, -567, -194, 16, -26, 120, -1428, 6334, 28507, -560, -196, 16,
+ -26, 120, -1425, 6319, 28514, -552, -197, 16, -26, 119, -1423, 6303, 28521, -545, -199, 16, -26, 119, -1420, 6288, 28527, -537, -200, 16, -26, 119, -1418, 6273, 28534, -529, -202, 16, -26, 119, -1415, 6258, 28541, -522, -203, 16, -26, 118, -1413, 6242, 28548, -514, -204, 16, -26, 118, -1410, 6227, 28554, -506, -206, 17, -26, 118, -1408, 6212, 28561, -499, -207, 17,
+ -26, 118, -1405, 6197, 28568, -491, -209, 17, -26, 117, -1403, 6182, 28574, -483, -210, 17, -26, 117, -1400, 6166, 28581, -476, -212, 17, -26, 117, -1397, 6151, 28588, -468, -213, 17, -26, 117, -1395, 6136, 28594, -460, -215, 17, -27, 116, -1392, 6121, 28601, -452, -216, 17, -27, 116, -1390, 6106, 28607, -444, -218, 17, -27, 116, -1387, 6091, 28614, -437, -219, 17,
+ -27, 116, -1385, 6076, 28620, -429, -220, 17, -27, 115, -1382, 6061, 28627, -421, -222, 18, -27, 115, -1380, 6045, 28633, -413, -223, 18, -27, 115, -1377, 6030, 28640, -405, -225, 18, -27, 115, -1375, 6015, 28646, -398, -226, 18, -27, 114, -1372, 6000, 28653, -390, -228, 18, -27, 114, -1370, 5985, 28659, -382, -229, 18, -27, 114, -1367, 5970, 28665, -374, -231, 18,
+ -27, 114, -1365, 5955, 28672, -366, -232, 18, -27, 113, -1362, 5940, 28678, -358, -234, 18, -28, 113, -1360, 5925, 28684, -350, -235, 18, -28, 113, -1357, 5910, 28690, -342, -237, 18, -28, 113, -1355, 5895, 28697, -334, -238, 19, -28, 112, -1352, 5880, 28703, -326, -240, 19, -28, 112, -1350, 5865, 28709, -318, -241, 19, -28, 112, -1347, 5850, 28715, -310, -243, 19,
+ -28, 112, -1345, 5835, 28721, -302, -244, 19, -28, 111, -1342, 5820, 28727, -294, -246, 19, -28, 111, -1340, 5805, 28734, -286, -247, 19, -28, 111, -1337, 5791, 28740, -278, -249, 19, -28, 111, -1335, 5776, 28746, -270, -250, 19, -28, 110, -1332, 5761, 28752, -262, -252, 19, -28, 110, -1330, 5746, 28758, -254, -254, 19, -28, 110, -1327, 5731, 28764, -246, -255, 20,
+ -29, 110, -1325, 5716, 28770, -237, -257, 20, -29, 109, -1322, 5701, 28776, -229, -258, 20, -29, 109, -1320, 5686, 28782, -221, -260, 20, -29, 109, -1317, 5672, 28788, -213, -261, 20, -29, 109, -1315, 5657, 28793, -205, -263, 20, -29, 108, -1312, 5642, 28799, -196, -264, 20, -29, 108, -1310, 5627, 28805, -188, -266, 20, -29, 108, -1307, 5612, 28811, -180, -267, 20,
+ -29, 108, -1305, 5598, 28817, -172, -269, 20, -29, 107, -1302, 5583, 28822, -163, -270, 21, -29, 107, -1300, 5568, 28828, -155, -272, 21, -29, 107, -1297, 5553, 28834, -147, -274, 21, -29, 107, -1295, 5539, 28840, -139, -275, 21, -30, 106, -1292, 5524, 28845, -130, -277, 21, -30, 106, -1290, 5509, 28851, -122, -278, 21, -30, 106, -1287, 5495, 28857, -114, -280, 21,
+ -30, 106, -1285, 5480, 28862, -105, -281, 21, -30, 105, -1282, 5465, 28868, -97, -283, 21, -30, 105, -1280, 5451, 28873, -88, -285, 21, -30, 105, -1277, 5436, 28879, -80, -286, 22, -30, 105, -1275, 5421, 28885, -72, -288, 22, -30, 104, -1272, 5407, 28890, -63, -289, 22, -30, 104, -1270, 5392, 28896, -55, -291, 22, -30, 104, -1267, 5378, 28901, -46, -292, 22,
+ -30, 104, -1265, 5363, 28906, -38, -294, 22, -30, 103, -1262, 5348, 28912, -29, -296, 22, -31, 103, -1260, 5334, 28917, -21, -297, 22, -31, 103, -1258, 5319, 28923, -12, -299, 22, -31, 103, -1255, 5305, 28928, -4, -300, 23, -31, 103, -1253, 5290, 28933, 5, -302, 23, -31, 102, -1250, 5276, 28939, 13, -304, 23, -31, 102, -1248, 5261, 28944, 22, -305, 23,
+ -31, 102, -1245, 5247, 28949, 30, -307, 23, -31, 102, -1243, 5232, 28954, 39, -308, 23, -31, 101, -1240, 5218, 28960, 48, -310, 23, -31, 101, -1238, 5203, 28965, 56, -312, 23, -31, 101, -1235, 5189, 28970, 65, -313, 23, -31, 101, -1233, 5174, 28975, 74, -315, 23, -31, 100, -1230, 5160, 28980, 82, -317, 24, -32, 100, -1228, 5146, 28985, 91, -318, 24,
+ -32, 100, -1225, 5131, 28990, 100, -320, 24, -32, 100, -1223, 5117, 28995, 108, -321, 24, -32, 99, -1221, 5102, 29000, 117, -323, 24, -32, 99, -1218, 5088, 29006, 126, -325, 24, -32, 99, -1216, 5074, 29010, 135, -326, 24, -32, 99, -1213, 5059, 29015, 143, -328, 24, -32, 98, -1211, 5045, 29020, 152, -330, 24, -32, 98, -1208, 5031, 29025, 161, -331, 25,
+ -32, 98, -1206, 5016, 29030, 170, -333, 25, -32, 98, -1203, 5002, 29035, 179, -335, 25, -32, 97, -1201, 4988, 29040, 187, -336, 25, -32, 97, -1198, 4973, 29045, 196, -338, 25, -33, 97, -1196, 4959, 29050, 205, -340, 25, -33, 97, -1194, 4945, 29054, 214, -341, 25, -33, 97, -1191, 4931, 29059, 223, -343, 25, -33, 96, -1189, 4916, 29064, 232, -345, 25,
+ -33, 96, -1186, 4902, 29069, 241, -346, 26, -33, 96, -1184, 4888, 29073, 250, -348, 26, -33, 96, -1181, 4874, 29078, 259, -350, 26, -33, 95, -1179, 4860, 29083, 268, -351, 26, -33, 95, -1176, 4845, 29087, 277, -353, 26, -33, 95, -1174, 4831, 29092, 286, -355, 26, -33, 95, -1172, 4817, 29096, 295, -356, 26, -33, 94, -1169, 4803, 29101, 304, -358, 26,
+ -33, 94, -1167, 4789, 29106, 313, -360, 27, -34, 94, -1164, 4775, 29110, 322, -361, 27, -34, 94, -1162, 4761, 29115, 331, -363, 27, -34, 94, -1159, 4747, 29119, 340, -365, 27, -34, 93, -1157, 4732, 29124, 349, -366, 27, -34, 93, -1154, 4718, 29128, 358, -368, 27, -34, 93, -1152, 4704, 29132, 367, -370, 27, -34, 93, -1150, 4690, 29137, 376, -372, 27,
+ -34, 92, -1147, 4676, 29141, 385, -373, 27, -34, 92, -1145, 4662, 29145, 395, -375, 28, -34, 92, -1142, 4648, 29150, 404, -377, 28, -34, 92, -1140, 4634, 29154, 413, -378, 28, -34, 91, -1138, 4620, 29158, 422, -380, 28, -34, 91, -1135, 4606, 29163, 431, -382, 28, -35, 91, -1133, 4592, 29167, 441, -384, 28, -35, 91, -1130, 4578, 29171, 450, -385, 28,
+ -35, 91, -1128, 4564, 29175, 459, -387, 28, -35, 90, -1125, 4551, 29179, 468, -389, 29, -35, 90, -1123, 4537, 29183, 478, -391, 29, -35, 90, -1121, 4523, 29188, 487, -392, 29, -35, 90, -1118, 4509, 29192, 496, -394, 29, -35, 89, -1116, 4495, 29196, 506, -396, 29, -35, 89, -1113, 4481, 29200, 515, -397, 29, -35, 89, -1111, 4467, 29204, 524, -399, 29,
+ -35, 89, -1109, 4453, 29208, 534, -401, 29, -35, 88, -1106, 4440, 29212, 543, -403, 30, -35, 88, -1104, 4426, 29216, 552, -404, 30, -36, 88, -1101, 4412, 29220, 562, -406, 30, -36, 88, -1099, 4398, 29223, 571, -408, 30, -36, 88, -1097, 4384, 29227, 581, -410, 30, -36, 87, -1094, 4371, 29231, 590, -411, 30, -36, 87, -1092, 4357, 29235, 600, -413, 30,
+ -36, 87, -1089, 4343, 29239, 609, -415, 30, -36, 87, -1087, 4329, 29243, 619, -417, 31, -36, 86, -1085, 4316, 29246, 628, -419, 31, -36, 86, -1082, 4302, 29250, 638, -420, 31, -36, 86, -1080, 4288, 29254, 647, -422, 31, -36, 86, -1077, 4274, 29258, 657, -424, 31, -36, 86, -1075, 4261, 29261, 666, -426, 31, -36, 85, -1073, 4247, 29265, 676, -427, 31,
+ -37, 85, -1070, 4234, 29268, 686, -429, 31, -37, 85, -1068, 4220, 29272, 695, -431, 32, -37, 85, -1066, 4206, 29276, 705, -433, 32, -37, 84, -1063, 4193, 29279, 714, -435, 32, -37, 84, -1061, 4179, 29283, 724, -436, 32, -37, 84, -1058, 4165, 29286, 734, -438, 32, -37, 84, -1056, 4152, 29290, 743, -440, 32, -37, 84, -1054, 4138, 29293, 753, -442, 32,
+ -37, 83, -1051, 4125, 29297, 763, -444, 32, -37, 83, -1049, 4111, 29300, 773, -445, 33, -37, 83, -1047, 4098, 29303, 782, -447, 33, -37, 83, -1044, 4084, 29307, 792, -449, 33, -38, 82, -1042, 4071, 29310, 802, -451, 33, -38, 82, -1039, 4057, 29314, 812, -453, 33, -38, 82, -1037, 4044, 29317, 821, -454, 33, -38, 82, -1035, 4030, 29320, 831, -456, 33,
+ -38, 82, -1032, 4017, 29323, 841, -458, 34, -38, 81, -1030, 4003, 29327, 851, -460, 34, -38, 81, -1028, 3990, 29330, 861, -462, 34, -38, 81, -1025, 3976, 29333, 871, -464, 34, -38, 81, -1023, 3963, 29336, 880, -465, 34, -38, 80, -1021, 3950, 29339, 890, -467, 34, -38, 80, -1018, 3936, 29342, 900, -469, 34, -38, 80, -1016, 3923, 29346, 910, -471, 34,
+ -38, 80, -1014, 3910, 29349, 920, -473, 35, -39, 80, -1011, 3896, 29352, 930, -475, 35, -39, 79, -1009, 3883, 29355, 940, -476, 35, -39, 79, -1006, 3870, 29358, 950, -478, 35, -39, 79, -1004, 3856, 29361, 960, -480, 35, -39, 79, -1002, 3843, 29364, 970, -482, 35, -39, 79, -999, 3830, 29367, 980, -484, 35, -39, 78, -997, 3816, 29369, 990, -486, 36,
+ -39, 78, -995, 3803, 29372, 1000, -488, 36, -39, 78, -992, 3790, 29375, 1010, -489, 36, -39, 78, -990, 3777, 29378, 1020, -491, 36, -39, 77, -988, 3764, 29381, 1030, -493, 36, -39, 77, -985, 3750, 29384, 1040, -495, 36, -39, 77, -983, 3737, 29386, 1050, -497, 36, -40, 77, -981, 3724, 29389, 1061, -499, 37, -40, 77, -978, 3711, 29392, 1071, -501, 37,
+ -40, 76, -976, 3698, 29395, 1081, -502, 37, -40, 76, -974, 3684, 29397, 1091, -504, 37, -40, 76, -971, 3671, 29400, 1101, -506, 37, -40, 76, -969, 3658, 29403, 1111, -508, 37, -40, 76, -967, 3645, 29405, 1122, -510, 37, -40, 75, -965, 3632, 29408, 1132, -512, 38, -40, 75, -962, 3619, 29410, 1142, -514, 38, -40, 75, -960, 3606, 29413, 1152, -516, 38,
+ -40, 75, -958, 3593, 29415, 1163, -518, 38, -40, 75, -955, 3580, 29418, 1173, -520, 38, -40, 74, -953, 3567, 29420, 1183, -521, 38, -41, 74, -951, 3554, 29423, 1193, -523, 38, -41, 74, -948, 3541, 29425, 1204, -525, 39, -41, 74, -946, 3528, 29428, 1214, -527, 39, -41, 74, -944, 3515, 29430, 1224, -529, 39, -41, 73, -941, 3502, 29432, 1235, -531, 39,
+ -41, 73, -939, 3489, 29435, 1245, -533, 39, -41, 73, -937, 3476, 29437, 1256, -535, 39, -41, 73, -935, 3463, 29439, 1266, -537, 39, -41, 72, -932, 3450, 29442, 1276, -539, 40, -41, 72, -930, 3437, 29444, 1287, -541, 40, -41, 72, -928, 3424, 29446, 1297, -542, 40, -41, 72, -925, 3411, 29448, 1308, -544, 40, -41, 72, -923, 3399, 29450, 1318, -546, 40,
+ -42, 71, -921, 3386, 29452, 1329, -548, 40, -42, 71, -919, 3373, 29455, 1339, -550, 40, -42, 71, -916, 3360, 29457, 1350, -552, 41, -42, 71, -914, 3347, 29459, 1360, -554, 41, -42, 71, -912, 3334, 29461, 1371, -556, 41, -42, 70, -909, 3322, 29463, 1381, -558, 41, -42, 70, -907, 3309, 29465, 1392, -560, 41, -42, 70, -905, 3296, 29467, 1403, -562, 41,
+ -42, 70, -903, 3283, 29469, 1413, -564, 42, -42, 70, -900, 3271, 29471, 1424, -566, 42, -42, 69, -898, 3258, 29473, 1434, -568, 42, -42, 69, -896, 3245, 29475, 1445, -570, 42, -42, 69, -894, 3232, 29476, 1456, -572, 42, -43, 69, -891, 3220, 29478, 1466, -574, 42, -43, 69, -889, 3207, 29480, 1477, -576, 42, -43, 68, -887, 3194, 29482, 1488, -578, 43,
+ -43, 68, -885, 3182, 29484, 1498, -579, 43, -43, 68, -882, 3169, 29485, 1509, -581, 43, -43, 68, -880, 3157, 29487, 1520, -583, 43, -43, 68, -878, 3144, 29489, 1531, -585, 43, -43, 67, -876, 3131, 29490, 1541, -587, 43, -43, 67, -873, 3119, 29492, 1552, -589, 43, -43, 67, -871, 3106, 29494, 1563, -591, 44, -43, 67, -869, 3094, 29495, 1574, -593, 44,
+ -43, 67, -867, 3081, 29497, 1585, -595, 44, -43, 66, -864, 3069, 29498, 1595, -597, 44, -44, 66, -862, 3056, 29500, 1606, -599, 44, -44, 66, -860, 3044, 29502, 1617, -601, 44, -44, 66, -858, 3031, 29503, 1628, -603, 45, -44, 66, -855, 3019, 29504, 1639, -605, 45, -44, 65, -853, 3006, 29506, 1650, -607, 45, -44, 65, -851, 2994, 29507, 1661, -609, 45,
+ -44, 65, -849, 2981, 29509, 1672, -611, 45, -44, 65, -846, 2969, 29510, 1683, -613, 45, -44, 65, -844, 2956, 29511, 1694, -615, 46, -44, 64, -842, 2944, 29513, 1705, -617, 46, -44, 64, -840, 2932, 29514, 1716, -619, 46, -44, 64, -838, 2919, 29515, 1727, -621, 46, -44, 64, -835, 2907, 29517, 1738, -623, 46, -45, 64, -833, 2895, 29518, 1749, -625, 46,
+ -45, 64, -831, 2882, 29519, 1760, -627, 47, -45, 63, -829, 2870, 29520, 1771, -629, 47, -45, 63, -826, 2858, 29521, 1782, -631, 47, -45, 63, -824, 2845, 29523, 1793, -633, 47, -45, 63, -822, 2833, 29524, 1804, -635, 47, -45, 63, -820, 2821, 29525, 1815, -638, 47, -45, 62, -818, 2808, 29526, 1826, -640, 47, -45, 62, -815, 2796, 29527, 1837, -642, 48,
+ -45, 62, -813, 2784, 29528, 1848, -644, 48, -45, 62, -811, 2772, 29529, 1860, -646, 48, -45, 62, -809, 2760, 29530, 1871, -648, 48, -45, 61, -807, 2747, 29531, 1882, -650, 48, -46, 61, -804, 2735, 29532, 1893, -652, 48, -46, 61, -802, 2723, 29533, 1904, -654, 49, -46, 61, -800, 2711, 29533, 1916, -656, 49, -46, 61, -798, 2699, 29534, 1927, -658, 49,
+ -46, 60, -796, 2687, 29535, 1938, -660, 49, -46, 60, -794, 2675, 29536, 1949, -662, 49, -46, 60, -791, 2662, 29537, 1961, -664, 49, -46, 60, -789, 2650, 29537, 1972, -666, 50, -46, 60, -787, 2638, 29538, 1983, -668, 50, -46, 60, -785, 2626, 29539, 1995, -670, 50, -46, 59, -783, 2614, 29540, 2006, -672, 50, -46, 59, -781, 2602, 29540, 2017, -674, 50,
+ -46, 59, -778, 2590, 29541, 2029, -677, 50, -47, 59, -776, 2578, 29542, 2040, -679, 51, -47, 59, -774, 2566, 29542, 2052, -681, 51, -47, 58, -772, 2554, 29543, 2063, -683, 51, -47, 58, -770, 2542, 29543, 2074, -685, 51, -47, 58, -768, 2530, 29544, 2086, -687, 51, -47, 58, -765, 2518, 29544, 2097, -689, 52, -47, 58, -763, 2506, 29545, 2109, -691, 52,
+ -47, 58, -761, 2494, 29545, 2120, -693, 52, -47, 57, -759, 2483, 29546, 2132, -695, 52, -47, 57, -757, 2471, 29546, 2143, -697, 52, -47, 57, -755, 2459, 29546, 2155, -699, 52, -47, 57, -752, 2447, 29547, 2166, -702, 53, -47, 57, -750, 2435, 29547, 2178, -704, 53, -47, 56, -748, 2423, 29547, 2189, -706, 53, -48, 56, -746, 2411, 29548, 2201, -708, 53,
+ -48, 56, -744, 2400, 29548, 2213, -710, 53, -48, 56, -742, 2388, 29548, 2224, -712, 53, -48, 56, -740, 2376, 29548, 2236, -714, 54, -48, 56, -738, 2364, 29549, 2247, -716, 54, -48, 55, -735, 2353, 29549, 2259, -718, 54, -48, 55, -733, 2341, 29549, 2271, -721, 54, -48, 55, -731, 2329, 29549, 2282, -723, 54, -48, 55, -729, 2317, 29549, 2294, -725, 54,
+ -48, 55, -727, 2306, 29549, 2306, -727, 55,
+};
+
diff --git a/src/include/slurp.h b/src/include/slurp.h
new file mode 100644
index 0000000..35f947e
--- /dev/null
+++ b/src/include/slurp.h
@@ -0,0 +1,81 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SLURP_H
+#define SLURP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+
+/* --------------------------------------------------------------------- */
+
+typedef struct _slurp_struct slurp_t;
+struct _slurp_struct {
+ size_t length;
+ uint8_t *data;
+ int extra;
+ void *bextra;
+ void (*closure)(slurp_t *);
+ /* for reading streams */
+ size_t pos;
+};
+
+/* --------------------------------------------------------------------- */
+
+/* slurp returns NULL and sets errno on error. 'buf' is only meaningful if you've already stat()'d
+the file; in most cases it can simply be NULL. If size is nonzero, it overrides the file's size as
+returned by stat -- this can be used to read only part of a file, or if the file size is known but
+a stat structure is not available. */
+slurp_t *slurp(const char *filename, struct stat *buf, size_t size);
+
+void unslurp(slurp_t * t);
+
+#ifdef WIN32
+int slurp_win32(slurp_t *useme, const char *filename, size_t st);
+#endif
+
+#if HAVE_MMAP
+int slurp_mmap(slurp_t *useme, const char *filename, size_t st);
+#endif
+
+/* stdio-style file processing */
+int slurp_seek(slurp_t *t, long offset, int whence); /* whence => SEEK_SET, SEEK_CUR, SEEK_END */
+long slurp_tell(slurp_t *t);
+#define slurp_rewind(t) slurp_seek((t), 0, SEEK_SET)
+
+size_t slurp_read(slurp_t *t, void *ptr, size_t count); /* i never realy liked fread */
+int slurp_getc(slurp_t *t); /* returns unsigned char cast to int, or EOF */
+int slurp_eof(slurp_t *t); /* 1 = end of file */
+
+#endif /* ! SLURP_H */
+
diff --git a/src/include/snd_fm.h b/src/include/snd_fm.h
new file mode 100644
index 0000000..c83b32d
--- /dev/null
+++ b/src/include/snd_fm.h
@@ -0,0 +1,201 @@
+#ifndef _BqtModplugSndFm
+#define _BqtModplugSndFm
+
+void Fmdrv_Init(int mixfreq);
+void Fmdrv_MixTo(int* buf, int count);
+
+void OPL_NoteOff(int c);
+void OPL_HertzTouch(int c, int Hertz, int keyoff); // also for pitch bending
+void OPL_Touch(int c, const unsigned char *D, unsigned Vol);
+void OPL_Pan(int c, signed char val);
+void OPL_Patch(int c, const unsigned char *D);
+void OPL_Reset(void);
+int OPL_Detect(void);
+void OPL_Close(void);
+
+/*************/
+
+/* 7.6.1999 01:51 / Bisqwit:
+ * The rest of this file is clipped from OSS/Free for Linux
+ */
+
+/*
+ * The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ * of the right side.
+ *
+ * Another special register at the right side is at offset 4. It contains
+ * a bit mask defining which voices are used as 4 OP voices.
+ *
+ * The percussive mode is implemented in the left side only.
+ *
+ * With the above exceptions the both sides can be operated independently.
+ *
+ * A 4 OP voice can be created by setting the corresponding
+ * bit at offset 4 of the right side.
+ *
+ * For example setting the rightmost bit (0x01) changes the
+ * first voice on the right side to the 4 OP mode. The fourth
+ * voice is made inaccessible.
+ *
+ * If a voice is set to the 2 OP mode, it works like 2 OP modes
+ * of the original YM3812 (AdLib). In addition the voice can
+ * be connected the left, right or both stereo channels. It can
+ * even be left unconnected. This works with 4 OP voices also.
+ *
+ * The stereo connection bits are located in the FEEDBACK_CONNECTION
+ * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ * in the second half of the voice.
+ */
+
+/*
+ * Register numbers for the global registers
+ */
+
+#define TEST_REGISTER 0x01
+#define ENABLE_WAVE_SELECT 0x20
+
+#define TIMER1_REGISTER 0x02
+#define TIMER2_REGISTER 0x03
+#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
+#define IRQ_RESET 0x80
+#define TIMER1_MASK 0x40
+#define TIMER2_MASK 0x20
+#define TIMER1_START 0x01
+#define TIMER2_START 0x02
+
+#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
+#define RIGHT_4OP_0 0x01
+#define RIGHT_4OP_1 0x02
+#define RIGHT_4OP_2 0x04
+#define LEFT_4OP_0 0x08
+#define LEFT_4OP_1 0x10
+#define LEFT_4OP_2 0x20
+
+#define OPL3_MODE_REGISTER 0x05 /* Right side */
+#define OPL3_ENABLE 0x01
+#define OPL4_ENABLE 0x02
+
+#define KBD_SPLIT_REGISTER 0x08 /* Left side */
+#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
+#define KEYBOARD_SPLIT 0x40
+
+#define PERCUSSION_REGISTER 0xbd /* Left side only */
+#define TREMOLO_DEPTH 0x80
+#define VIBRATO_DEPTH 0x40
+#define PERCOSSION_ENABLE 0x20
+#define BASSDRUM_ON 0x10
+#define SNAREDRUM_ON 0x08
+#define TOMTOM_ON 0x04
+#define CYMBAL_ON 0x02
+#define HIHAT_ON 0x01
+
+/*
+ * Offsets to the register banks for operators. To get the
+ * register number just add the operator offset to the bank offset
+ *
+ * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+#define AM_VIB 0x20
+#define TREMOLO_ON 0x80
+#define VIBRATO_ON 0x40
+#define SUSTAIN_ON 0x20
+#define KSR 0x10 /* Key scaling rate */
+#define MULTIPLE_MASK 0x0f /* Frequency multiplier */
+
+ /*
+ * KSL/Total level (0x40 to 0x55)
+ */
+#define KSL_LEVEL 0x40
+#define KSL_MASK 0xc0 /* Envelope scaling bits */
+#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
+
+/*
+ * Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY 0x60
+#define ATTACK_MASK 0xf0
+#define DECAY_MASK 0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE 0x80
+#define SUSTAIN_MASK 0xf0
+#define RELEASE_MASK 0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT 0xe0
+
+/*
+ * Offsets to the register banks for voices. Just add to the
+ * voice number to get the register number.
+ *
+ * F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW 0xa0
+
+/*
+ * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK 0xb0
+#define KEYON_BIT 0x20
+#define BLOCKNUM_MASK 0x1c
+#define FNUM_HIGH_MASK 0x03
+
+/*
+ * Feedback / Connection (0xc0 to 0xc8)
+ *
+ * These registers have two new bits when the OPL-3 mode
+ * is selected. These bits controls connecting the voice
+ * to the stereo channels. For 4 OP voices this bit is
+ * defined in the second half of the voice (add 3 to the
+ * register offset).
+ *
+ * For 4 OP voices the connection bit is used in the
+ * both halves (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION 0xc0
+#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
+#define CONNECTION_BIT 0x01
+/*
+ * In the 4 OP mode there is four possible configurations how the
+ * operators can be connected together (in 2 OP modes there is just
+ * AM or FM). The 4 OP connection mode is defined by the rightmost
+ * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
+ *
+ * First half Second half Mode
+ *
+ * +---+
+ * v |
+ * 0 0 >+-1-+--2--3--4-->
+ *
+ *
+ *
+ * +---+
+ * | |
+ * 0 1 >+-1-+--2-+
+ * |->
+ * >--3----4-+
+ *
+ * +---+
+ * | |
+ * 1 0 >+-1-+-----+
+ * |->
+ * >--2--3--4-+
+ *
+ * +---+
+ * | |
+ * 1 1 >+-1-+--+
+ * |
+ * >--2--3-+->
+ * |
+ * >--4----+
+ */
+#define STEREO_BITS 0x30 /* OPL-3 only */
+#define VOICE_TO_LEFT 0x10
+#define VOICE_TO_RIGHT 0x20
+
+#endif
+
diff --git a/src/include/sndfile.h b/src/include/sndfile.h
new file mode 100644
index 0000000..22ce895
--- /dev/null
+++ b/src/include/sndfile.h
@@ -0,0 +1,708 @@
+/*
+ * This source code is public domain.
+ *
+ * Authors: Olivier Lapicque <olivierl@jps.net>,
+ * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
+*/
+#ifndef __SNDFILE_H
+#define __SNDFILE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define NEED_BYTESWAP
+#include "headers.h"
+
+#include "tables.h"
+
+
+#define MOD_AMIGAC2 0x1AB
+#define MAX_SAMPLE_LENGTH 16000000
+#define MAX_SAMPLE_RATE 192000
+#define MAX_ORDERS 256
+#define MAX_PATTERNS 240
+#define MAX_SAMPLES 236
+#define MAX_INSTRUMENTS MAX_SAMPLES
+#define MAX_VOICES 256
+#define MAX_CHANNELS 64
+#define MAX_ENVPOINTS 32
+#define MAX_INFONAME 80
+#define MAX_EQ_BANDS 6
+#define MAX_MESSAGE 8000
+
+#define MIXBUFFERSIZE 512
+
+
+#define CHN_16BIT 0x01 // 16-bit sample
+#define CHN_LOOP 0x02 // looped sample
+#define CHN_PINGPONGLOOP 0x04 // bi-directional (useless unless CHN_LOOP is also set)
+#define CHN_SUSTAINLOOP 0x08 // sample with sustain loop
+#define CHN_PINGPONGSUSTAIN 0x10 // bi-directional (useless unless CHN_SUSTAINLOOP is also set)
+#define CHN_PANNING 0x20 // sample with default panning set
+#define CHN_STEREO 0x40 // stereo sample
+#define CHN_PINGPONGFLAG 0x80 // when flag is on, sample is processed backwards
+#define CHN_MUTE 0x100 // muted channel
+#define CHN_KEYOFF 0x200 // exit sustain (note-off encountered)
+#define CHN_NOTEFADE 0x400 // fade note (~~~ or end of instrument envelope)
+#define CHN_SURROUND 0x800 // use surround channel (S91)
+#define CHN_NOIDO 0x1000 // near enough to an exact multiple of c5speed that interpolation
+ // won't be noticeable (or interpolation is disabled completely)
+#define CHN_HQSRC 0x2000 // ???
+#define CHN_FILTER 0x4000 // filtered output (i.e., Zxx)
+#define CHN_VOLUMERAMP 0x8000 // ramp volume
+#define CHN_VIBRATO 0x10000 // apply vibrato
+#define CHN_TREMOLO 0x20000 // apply tremolo
+//#define CHN_PANBRELLO 0x40000 // apply panbrello (handled elsewhere now)
+#define CHN_PORTAMENTO 0x80000 // apply portamento
+#define CHN_GLISSANDO 0x100000 // glissando mode ("stepped" pitch slides)
+#define CHN_VOLENV 0x200000 // volume envelope is active
+#define CHN_PANENV 0x400000 // pan envelope is active
+#define CHN_PITCHENV 0x800000 // pitch/filter envelope is active
+#define CHN_FASTVOLRAMP 0x1000000 // ramp volume very fast (XXX this is a dumb flag)
+//#define CHN_EXTRALOUD 0x2000000
+//#define CHN_REVERB 0x4000000
+//#define CHN_NOREVERB 0x8000000
+#define CHN_NNAMUTE 0x10000000 // turn off mute, but have it reset later
+#define CHN_ADLIB 0x20000000 // OPL mode
+
+#define CHN_SAMPLE_FLAGS (CHN_16BIT | CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP \
+ | CHN_PINGPONGSUSTAIN | CHN_PANNING | CHN_STEREO | CHN_PINGPONGFLAG | CHN_ADLIB)
+
+
+#define ENV_VOLUME 0x0001
+#define ENV_VOLSUSTAIN 0x0002
+#define ENV_VOLLOOP 0x0004
+#define ENV_PANNING 0x0008
+#define ENV_PANSUSTAIN 0x0010
+#define ENV_PANLOOP 0x0020
+#define ENV_PITCH 0x0040
+#define ENV_PITCHSUSTAIN 0x0080
+#define ENV_PITCHLOOP 0x0100
+#define ENV_SETPANNING 0x0200
+#define ENV_FILTER 0x0400
+#define ENV_VOLCARRY 0x0800
+#define ENV_PANCARRY 0x1000
+#define ENV_PITCHCARRY 0x2000
+#define ENV_MUTE 0x4000
+
+#define FX_NONE 0 // .
+#define FX_ARPEGGIO 1 // J
+#define FX_PORTAMENTOUP 2 // F
+#define FX_PORTAMENTODOWN 3 // E
+#define FX_TONEPORTAMENTO 4 // G
+#define FX_VIBRATO 5 // H
+#define FX_TONEPORTAVOL 6 // L
+#define FX_VIBRATOVOL 7 // K
+#define FX_TREMOLO 8 // R
+#define FX_PANNING 9 // X
+#define FX_OFFSET 10 // O
+#define FX_VOLUMESLIDE 11 // D
+#define FX_POSITIONJUMP 12 // B
+#define FX_VOLUME 13 // ! (FT2/IMF Cxx)
+#define FX_PATTERNBREAK 14 // C
+#define FX_RETRIG 15 // Q
+#define FX_SPEED 16 // A
+#define FX_TEMPO 17 // T
+#define FX_TREMOR 18 // I
+#define FX_SPECIAL 20 // S
+#define FX_CHANNELVOLUME 21 // M
+#define FX_CHANNELVOLSLIDE 22 // N
+#define FX_GLOBALVOLUME 23 // V
+#define FX_GLOBALVOLSLIDE 24 // W
+#define FX_KEYOFF 25 // $ (FT2 Kxx)
+#define FX_FINEVIBRATO 26 // U
+#define FX_PANBRELLO 27 // Y
+#define FX_PANNINGSLIDE 29 // P
+#define FX_SETENVPOSITION 30 // & (FT2 Lxx)
+#define FX_MIDI 31 // Z
+#define FX_NOTESLIDEUP 32 // ( (IMF Gxy)
+#define FX_NOTESLIDEDOWN 33 // ) (IMF Hxy)
+#define FX_MAX 34
+#define FX_UNIMPLEMENTED FX_MAX // no-op, displayed as "?"
+
+#define FX_IS_EFFECT(v) ((v) > 0 && (v) < FX_MAX)
+
+// Volume Column commands
+#define VOLFX_NONE 0
+#define VOLFX_VOLUME 1
+#define VOLFX_PANNING 2
+#define VOLFX_VOLSLIDEUP 3 // C
+#define VOLFX_VOLSLIDEDOWN 4 // D
+#define VOLFX_FINEVOLUP 5 // A
+#define VOLFX_FINEVOLDOWN 6 // B
+#define VOLFX_VIBRATOSPEED 7 // $ (FT2 Ax)
+#define VOLFX_VIBRATODEPTH 8 // H
+#define VOLFX_PANSLIDELEFT 9 // < (FT2 Dx)
+#define VOLFX_PANSLIDERIGHT 10 // > (FT2 Ex)
+#define VOLFX_TONEPORTAMENTO 11 // G
+#define VOLFX_PORTAUP 12 // F
+#define VOLFX_PORTADOWN 13 // E
+
+// orderlist
+#define ORDER_SKIP 254 // +++
+#define ORDER_LAST 255 // ---
+
+// 'Special' notes
+// Note fade IS actually supported in Impulse Tracker, but there's no way to handle it in the editor
+// (Actually, any non-valid note is handled internally as a note fade, but it's good to have a single
+// value for internal representation)
+// update 20090805: ok just discovered that IT internally uses 253 for its "no note" value.
+// guess we'll use a different value for fade!
+// note: 246 is rather arbitrary, but IT conveniently displays this value as "F#D" ("FD" with 2-char notes)
+#define NOTE_NONE 0 // ...
+#define NOTE_FIRST 1 // C-0
+#define NOTE_MIDC 61 // C-5
+#define NOTE_LAST 120 // B-9
+#define NOTE_FADE 246 // ~~~
+#define NOTE_CUT 254 // ^^^
+#define NOTE_OFF 255 // ===
+#define NOTE_IS_NOTE(n) ((n) > NOTE_NONE && (n) <= NOTE_LAST) // anything playable - C-0 to B-9
+#define NOTE_IS_CONTROL(n) ((n) > NOTE_LAST) // not a note, but non-empty
+#define NOTE_IS_INVALID(n) ((n) > NOTE_LAST && (n) < NOTE_CUT && (n) != NOTE_FADE) // ???
+
+// Auto-vibrato types
+#define VIB_SINE 0
+#define VIB_RAMP_DOWN 1
+#define VIB_SQUARE 2
+#define VIB_RANDOM 3
+
+// NNA types
+#define NNA_NOTECUT 0
+#define NNA_CONTINUE 1
+#define NNA_NOTEOFF 2
+#define NNA_NOTEFADE 3
+
+// DCT types
+#define DCT_NONE 0
+#define DCT_NOTE 1
+#define DCT_SAMPLE 2
+#define DCT_INSTRUMENT 3
+
+// DCA types
+#define DCA_NOTECUT 0
+#define DCA_NOTEOFF 1
+#define DCA_NOTEFADE 2
+
+// Nothing innately special about this -- just needs to be above the max pattern length.
+// process row is set to this in order to get the player to jump to the end of the pattern.
+// (See ITTECH.TXT)
+#define PROCESS_NEXT_ORDER 0xFFFE
+
+// Module flags
+#define SONG_EMBEDMIDICFG 0x0001 // Embed MIDI macros (Shift-F1) in file
+//#define SONG_FASTVOLSLIDES 0x0002
+#define SONG_ITOLDEFFECTS 0x0004 // Old Impulse Tracker effect implementations
+#define SONG_COMPATGXX 0x0008 // "Compatible Gxx" (handle portamento more like other trackers)
+#define SONG_LINEARSLIDES 0x0010 // Linear slides vs. Amiga slides
+#define SONG_PATTERNPLAYBACK 0x0020 // Only playing current pattern
+//#define SONG_STEP 0x0040
+#define SONG_PAUSED 0x0080 // Playback paused (Shift-F8)
+//#define SONG_FADINGSONG 0x0100
+#define SONG_ENDREACHED 0x0200 // Song is finished (standalone keyjazz mode)
+//#define SONG_GLOBALFADE 0x0400
+//#define SONG_CPUVERYHIGH 0x0800
+#define SONG_FIRSTTICK 0x1000 // Current tick is the first tick of the row (dopey flow-control flag)
+//#define SONG_MPTFILTERMODE 0x2000
+//#define SONG_SURROUNDPAN 0x4000
+//#define SONG_EXFILTERRANGE 0x8000
+//#define SONG_AMIGALIMITS 0x10000
+#define SONG_INSTRUMENTMODE 0x20000 // Process instruments
+#define SONG_ORDERLOCKED 0x40000 // Don't advance orderlist *(Alt-F11)
+#define SONG_NOSTEREO 0x80000 // secret code for "mono"
+#define SONG_PATTERNLOOP (SONG_PATTERNPLAYBACK | SONG_ORDERLOCKED) // Loop current pattern (F6)
+
+// Global Options (Renderer)
+#define SNDMIX_REVERSESTEREO 0x0001 // swap L/R audio channels
+//#define SNDMIX_NOISEREDUCTION 0x0002 // reduce hiss (do not use, it's just a simple low-pass filter)
+//#define SNDMIX_AGC 0x0004 // automatic gain control
+#define SNDMIX_NORESAMPLING 0x0008 // force no resampling (uninterpolated)
+#define SNDMIX_HQRESAMPLER 0x0010 // cubic resampling
+//#define SNDMIX_MEGABASS 0x0020
+//#define SNDMIX_SURROUND 0x0040
+//#define SNDMIX_REVERB 0x0080
+//#define SNDMIX_EQ 0x0100 // apply EQ (always on)
+//#define SNDMIX_SOFTPANNING 0x0200
+#define SNDMIX_ULTRAHQSRCMODE 0x0400 // polyphase resampling (or FIR? I don't know)
+// Misc Flags (can safely be turned on or off)
+#define SNDMIX_DIRECTTODISK 0x10000 // disk writer mode
+#define SNDMIX_NOBACKWARDJUMPS 0x40000 // disallow Bxx jumps from going backward in the orderlist
+//#define SNDMIX_MAXDEFAULTPAN 0x80000 // (no longer) Used by the MOD loader
+#define SNDMIX_MUTECHNMODE 0x100000 // Notes are not played on muted channels
+#define SNDMIX_NOSURROUND 0x200000 // ignore S91
+//#define SNDMIX_NOMIXING 0x400000
+#define SNDMIX_NORAMPING 0x800000 // don't apply ramping on volume change (causes clicks)
+
+enum {
+ SRCMODE_NEAREST,
+ SRCMODE_LINEAR,
+ SRCMODE_SPLINE,
+ SRCMODE_POLYPHASE,
+ NUM_SRC_MODES
+};
+
+// ------------------------------------------------------------------------------------------------------------
+// Flags for csf_read_sample
+
+// Sample data characteristics
+// Note:
+// - None of these constants are zero
+// - The format specifier must have a value set for each "section"
+// - csf_read_sample DOES check the values for validity
+
+// Bit width (8 bits for simplicity)
+#define _SDV_BIT(n) ((n) << 0)
+#define SF_BIT_MASK 0xff
+#define SF_7 _SDV_BIT(7) // 7-bit (weird!)
+#define SF_8 _SDV_BIT(8) // 8-bit
+#define SF_16 _SDV_BIT(16) // 16-bit
+#define SF_24 _SDV_BIT(24) // 24-bit
+#define SF_32 _SDV_BIT(32) // 32-bit
+
+// Channels (4 bits)
+#define _SDV_CHN(n) ((n) << 8)
+#define SF_CHN_MASK 0xf00
+#define SF_M _SDV_CHN(1) // mono
+#define SF_SI _SDV_CHN(2) // stereo, interleaved
+#define SF_SS _SDV_CHN(3) // stereo, split
+
+// Endianness (4 bits)
+#define _SDV_END(n) ((n) << 12)
+#define SF_END_MASK 0xf000
+#define SF_LE _SDV_END(1) // little-endian
+#define SF_BE _SDV_END(2) // big-endian
+
+// Encoding (8 bits)
+#define _SDV_ENC(n) ((n) << 16)
+#define SF_ENC_MASK 0xff0000
+#define SF_PCMS _SDV_ENC(1) // PCM, signed
+#define SF_PCMU _SDV_ENC(2) // PCM, unsigned
+#define SF_PCMD _SDV_ENC(3) // PCM, delta-encoded
+#define SF_IT214 _SDV_ENC(4) // Impulse Tracker 2.14 compressed
+#define SF_IT215 _SDV_ENC(5) // Impulse Tracker 2.15 compressed
+#define SF_AMS _SDV_ENC(6) // AMS / Velvet Studio packed
+#define SF_DMF _SDV_ENC(7) // DMF Huffman compression
+#define SF_MDL _SDV_ENC(8) // MDL Huffman compression
+#define SF_PTM _SDV_ENC(9) // PTM 8-bit delta value -> 16-bit sample
+
+// Sample format shortcut
+#define SF(a,b,c,d) (SF_ ## a | SF_ ## b| SF_ ## c | SF_ ## d)
+
+// Deprecated constants
+#define RS_AMS16 SF(AMS,16,M,LE)
+#define RS_AMS8 SF(AMS,8,M,LE)
+#define RS_DMF16 SF(DMF,16,M,LE)
+#define RS_DMF8 SF(DMF,8,M,LE)
+#define RS_IT21416 SF(IT214,16,M,LE)
+#define RS_IT2148 SF(IT214,8,M,LE)
+#define RS_IT21516 SF(IT215,16,M,LE)
+#define RS_IT2158 SF(IT215,8,M,LE)
+#define RS_IT21416S SF(IT214,16,SS,LE)
+#define RS_IT2148S SF(IT214,8,SS,LE)
+#define RS_IT21516S SF(IT215,16,SS,LE)
+#define RS_IT2158S SF(IT215,8,SS,LE)
+#define RS_MDL16 SF(MDL,16,M,LE)
+#define RS_MDL8 SF(MDL,8,M,LE)
+#define RS_PCM16D SF(PCMD,16,M,LE)
+#define RS_PCM16M SF(PCMS,16,M,BE)
+#define RS_PCM16S SF(PCMS,16,M,LE)
+#define RS_PCM16U SF(PCMU,16,M,LE)
+#define RS_PCM24S SF(PCMS,24,M,LE)
+#define RS_PCM32S SF(PCMS,32,M,LE)
+#define RS_PCM8D SF(PCMD,8,M,LE)
+#define RS_PCM8S SF(PCMS,8,M,LE)
+#define RS_PCM8U SF(PCMU,8,M,LE)
+#define RS_PTM8DTO16 SF(PTM,16,M,LE)
+#define RS_STIPCM16M SF(PCMS,16,SI,BE)
+#define RS_STIPCM16S SF(PCMS,16,SI,LE)
+#define RS_STIPCM16U SF(PCMU,16,SI,LE)
+#define RS_STIPCM24S SF(PCMS,24,SI,LE)
+#define RS_STIPCM32S SF(PCMS,32,SI,LE)
+#define RS_STIPCM8S SF(PCMS,8,SI,LE)
+#define RS_STIPCM8U SF(PCMU,8,SI,LE)
+#define RS_STPCM16D SF(PCMD,16,SS,LE)
+#define RS_STPCM16M SF(PCMS,16,SS,BE)
+#define RS_STPCM16S SF(PCMS,16,SS,LE)
+#define RS_STPCM16U SF(PCMU,16,SS,LE)
+#define RS_STPCM8D SF(PCMD,8,SS,LE)
+#define RS_STPCM8S SF(PCMS,8,SS,LE)
+#define RS_STPCM8U SF(PCMU,8,SS,LE)
+
+// ------------------------------------------------------------------------------------------------------------
+
+typedef struct song_sample {
+ uint32_t length;
+ uint32_t loop_start;
+ uint32_t loop_end;
+ uint32_t sustain_start;
+ uint32_t sustain_end;
+ signed char *data;
+ uint32_t c5speed;
+ uint32_t panning;
+ uint32_t volume;
+ uint32_t global_volume;
+ uint32_t flags;
+ uint32_t vib_type;
+ uint32_t vib_rate;
+ uint32_t vib_depth;
+ uint32_t vib_speed;
+ char name[32];
+ char filename[22];
+ int played; // for note playback dots
+ uint32_t globalvol_saved; // for muting individual samples
+
+ // This must be 12-bytes to work around a bug in some gcc4.2s (XXX why? what bug?)
+ unsigned char adlib_bytes[12];
+} song_sample_t;
+
+typedef struct song_envelope {
+ int ticks[32];
+ uint8_t values[32];
+ int nodes;
+ int loop_start;
+ int loop_end;
+ int sustain_start;
+ int sustain_end;
+} song_envelope_t;
+
+typedef struct song_instrument {
+ uint32_t fadeout;
+ uint32_t flags;
+ unsigned int global_volume;
+ unsigned int panning;
+ uint8_t sample_map[128];
+ uint8_t note_map[128];
+ song_envelope_t vol_env;
+ song_envelope_t pan_env;
+ song_envelope_t pitch_env;
+ unsigned int nna;
+ unsigned int dct;
+ unsigned int dca;
+ unsigned int pan_swing;
+ unsigned int vol_swing;
+ unsigned int ifc;
+ unsigned int ifr;
+ int midi_bank; // TODO split this?
+ int midi_program;
+ unsigned int midi_channel_mask; // FIXME why is this a mask? why is a mask useful? does 2.15 use a mask?
+ int pitch_pan_separation;
+ unsigned int pitch_pan_center;
+ char name[32];
+ char filename[16];
+ int played; // for note playback dots
+} song_instrument_t;
+
+// (TODO write decent descriptions of what the various volume
+// variables are used for - are all of them *really* necessary?)
+// (TODO also the majority of this is irrelevant outside of the "main" 64 channels;
+// this struct should really only be holding the stuff actually needed for mixing)
+typedef struct song_voice {
+ // First 32-bytes: Most used mixing information: don't change it
+ signed char * current_sample_data;
+ uint32_t position; // sample position, fixed-point -- integer part
+ uint32_t position_frac; // fractional part
+ int32_t increment; // 16.16 fixed point, how much to add to position per sample-frame of output
+ int32_t right_volume; // ?
+ int32_t left_volume; // ?
+ int32_t right_ramp; // ?
+ int32_t left_ramp; // ?
+ // 2nd cache line
+ uint32_t length; // only to the end of the loop
+ uint32_t flags;
+ uint32_t loop_start; // loop or sustain, whichever is active
+ uint32_t loop_end;
+ int32_t right_ramp_volume; // ?
+ int32_t left_ramp_volume; // ?
+ int32_t strike; // decremented to zero. this affects how long the initial hit on the playback marks lasts (bigger dot in instrument and sample list windows)
+
+ int32_t filter_y1, filter_y2, filter_y3, filter_y4;
+ int32_t filter_a0, filter_b0, filter_b1;
+
+ int32_t rofs, lofs; // ?
+ int32_t ramp_length;
+ // Information not used in the mixer
+ int32_t right_volume_new, left_volume_new; // ?
+ int32_t final_volume; // range 0-16384 (?), accounting for sample+channel+global+etc. volumes
+ int32_t final_panning; // range 0-256 (but can temporarily exceed that range during calculations)
+ int32_t volume, panning; // range 0-256 (?); these are the current values set for the channel
+ int32_t fadeout_volume;
+ int32_t period;
+ int32_t c5speed;
+ int32_t sample_freq; // only used on the info page (F5)
+ int32_t portamento_target;
+ song_instrument_t *ptr_instrument; // these two suck, and should
+ song_sample_t *ptr_sample; // be replaced with numbers
+ int vol_env_position;
+ int pan_env_position;
+ int pitch_env_position;
+ uint32_t master_channel; // nonzero = background/NNA voice, indicates what channel it "came from"
+ uint32_t vu_meter;
+ int32_t global_volume;
+ int32_t instrument_volume;
+ int32_t autovib_depth;
+ uint32_t autovib_position, vibrato_position, tremolo_position, panbrello_position;
+ // 16-bit members
+ int vol_swing, pan_swing;
+
+ // formally 8-bit members
+ unsigned int note; // the note that's playing
+ unsigned int nna;
+ unsigned int new_note, new_instrument; // ?
+ // Effect memory and handling
+ unsigned int n_command; // This sucks and needs to go away (dumb "flag" for arpeggio / tremor)
+ unsigned int mem_vc_volslide; // Ax Bx Cx Dx (volume column)
+ unsigned int mem_arpeggio; // Axx
+ unsigned int mem_volslide; // Dxx
+ unsigned int mem_pitchslide; // Exx Fxx (and Gxx maybe)
+ int32_t mem_portanote; // Gxx (synced with mem_pitchslide if compat gxx is set)
+ unsigned int mem_tremor; // Ixx
+ unsigned int mem_channel_volslide; // Nxx
+ unsigned int mem_offset; // final, combined yxx00h from Oxx and SAy
+ unsigned int mem_panslide; // Pxx
+ unsigned int mem_retrig; // Qxx
+ unsigned int mem_special; // Sxx
+ unsigned int mem_tempo; // Txx
+ unsigned int mem_global_volslide; // Wxx
+ unsigned int note_slide_counter, note_slide_speed, note_slide_step; // IMF effect
+ unsigned int vib_type, vibrato_speed, vibrato_depth;
+ unsigned int tremolo_type, tremolo_speed, tremolo_depth;
+ unsigned int panbrello_type, panbrello_speed, panbrello_depth;
+ int tremolo_delta, panbrello_delta;
+
+ unsigned int cutoff;
+ unsigned int resonance;
+ int cd_note_delay; // countdown: note starts when this hits zero
+ int cd_note_cut; // countdown: note stops when this hits zero
+ int cd_retrig; // countdown: note retrigs when this hits zero
+ unsigned int cd_tremor; // (weird) countdown + flag: see snd_fx.c and sndmix.c
+ unsigned int patloop_row; // row number that SB0 was on
+ unsigned int cd_patloop; // countdown: pattern loops back when this hits zero
+
+ unsigned int row_note, row_instr;
+ unsigned int row_voleffect, row_volparam;
+ unsigned int row_effect, row_param;
+ unsigned int active_macro, last_instrument;
+} song_voice_t;
+
+typedef struct song_channel {
+ uint32_t panning;
+ uint32_t volume;
+ uint32_t flags;
+} song_channel_t;
+
+typedef struct song_note {
+ uint8_t note;
+ uint8_t instrument;
+ uint8_t voleffect;
+ uint8_t volparam;
+ uint8_t effect;
+ uint8_t param;
+} song_note_t;
+
+////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ char start[32];
+ char stop[32];
+ char tick[32];
+ char note_on[32];
+ char note_off[32];
+ char set_volume[32];
+ char set_panning[32];
+ char set_bank[32];
+ char set_program[32];
+ char sfx[16][32];
+ char zxx[128][32];
+} midi_config_t;
+
+extern midi_config_t default_midi_config;
+
+
+extern uint32_t max_voices;
+extern uint32_t global_vu_left, global_vu_right;
+
+extern const song_note_t blank_pattern[64 * 64];
+extern const song_note_t *blank_note;
+
+
+struct multi_write {
+ int used;
+ void *data;
+ /* Conveniently, this has the same prototype as disko_write :) */
+ void (*write)(void *data, const uint8_t *buf, size_t bytes);
+ /* this is optimization for channels that haven't had any data yet
+ (nothing to convert/write, just seek ahead in the data stream) */
+ void (*silence)(void *data, long bytes);
+ int buffer[MIXBUFFERSIZE * 2];
+};
+
+typedef struct song {
+ int mix_buffer[MIXBUFFERSIZE * 2];
+ float mix_buffer_float[MIXBUFFERSIZE * 2]; // is this needed?
+
+ song_voice_t voices[MAX_VOICES]; // Channels
+ uint32_t voice_mix[MAX_VOICES]; // Channels to be mixed
+ song_sample_t samples[MAX_SAMPLES+1]; // Samples (1-based!)
+ song_instrument_t *instruments[MAX_INSTRUMENTS+1]; // Instruments (1-based!)
+ song_channel_t channels[MAX_CHANNELS]; // Channel settings
+ song_note_t *patterns[MAX_PATTERNS]; // Patterns
+ uint16_t pattern_size[MAX_PATTERNS]; // Pattern Lengths
+ uint16_t pattern_alloc_size[MAX_PATTERNS]; // Allocated lengths (for async. resizing/playback)
+ uint8_t orderlist[MAX_ORDERS + 1]; // Pattern Orders
+ midi_config_t midi_config; // Midi macro config table
+ uint32_t initial_speed;
+ uint32_t initial_tempo;
+ uint32_t initial_global_volume;
+ uint32_t flags; // Song flags SONG_XXXX
+ uint32_t pan_separation;
+ uint32_t num_voices; // how many are currently playing. (POTENTIALLY larger than global max_voices)
+ uint32_t mix_stat; // number of channels being mixed (not really used)
+ uint32_t buffer_count; // number of samples to mix per tick
+ uint32_t tick_count;
+ int32_t row_count; /* IMPORTANT needs to be signed */
+ uint32_t current_speed;
+ uint32_t current_tempo;
+ uint32_t process_row;
+ uint32_t row; // no analogue in pm.h? should be either renamed or factored out.
+ uint32_t break_row;
+ uint32_t current_pattern;
+ uint32_t current_order;
+ uint32_t process_order;
+ uint32_t current_global_volume;
+ uint32_t mixing_volume;
+ uint32_t freq_factor; // not used -- for tweaking the song speed LP-style (interesting!)
+ uint32_t tempo_factor; // ditto
+ int32_t repeat_count; // 0 = first playback, etc. (note: set to -1 to stop instead of looping)
+ uint8_t row_highlight_major;
+ uint8_t row_highlight_minor;
+ char message[MAX_MESSAGE + 1];
+ char title[32];
+ char tracker_id[32]; // irrelevant to the song, just used by some loaders (fingerprint)
+
+ // These store the existing IT save history from prior editing sessions.
+ // Current session data is added at save time, and is NOT a part of histdata.
+ int histlen; // How many session history data entries exist (each entry is eight bytes)
+ uint8_t *histdata; // Preserved entries from prior sessions, might be NULL if histlen = 0
+ struct timeval editstart; // When the song was loaded
+
+ // mixer stuff
+ uint32_t mix_flags; // SNDMIX_*
+ uint32_t mix_frequency, mix_bits_per_sample, mix_channels;
+
+ // noise reduction filter
+ int32_t left_nr, right_nr;
+
+ // chaseback
+ int stop_at_order;
+ int stop_at_row;
+ unsigned int stop_at_time;
+
+ // multi-write stuff -- NULL if no multi-write is in progress, else array of one struct per channel
+ struct multi_write *multi_write;
+} song_t;
+
+song_note_t *csf_allocate_pattern(uint32_t rows);
+void csf_free_pattern(void *pat);
+signed char *csf_allocate_sample(uint32_t nbytes);
+void csf_free_sample(void *p);
+song_instrument_t *csf_allocate_instrument(void);
+void csf_init_instrument(song_instrument_t *ins, int samp);
+void csf_free_instrument(song_instrument_t *p);
+
+uint32_t csf_read_sample(song_sample_t *sample, uint32_t flags, const void *filedata, uint32_t datalength);
+void csf_adjust_sample_loop(song_sample_t *sample);
+
+extern void (*csf_midi_out_note)(int chan, const song_note_t *m);
+extern void (*csf_midi_out_raw)(const unsigned char *, unsigned int, unsigned int);
+
+void csf_import_s3m_effect(song_note_t *m, int it);
+
+
+// counting stuff
+
+int csf_note_is_empty(song_note_t *note);
+int csf_pattern_is_empty(song_t *csf, int n);
+int csf_sample_is_empty(song_sample_t *smp);
+int csf_instrument_is_empty(song_instrument_t *ins);
+int csf_last_order(song_t *csf); // last order of "main" song (IT-style, only for display)
+int csf_get_num_orders(song_t *csf); // last non-blank order (for saving)
+int csf_get_num_patterns(song_t *csf);
+int csf_get_num_samples(song_t *csf);
+int csf_get_num_instruments(song_t *csf);
+
+// for these, 'start' indicates minimum sample/instrument to check
+int csf_first_blank_sample(song_t *csf, int start);
+int csf_first_blank_instrument(song_t *csf, int start);
+
+
+
+int csf_set_wave_config(song_t *csf, uint32_t rate, uint32_t bits, uint32_t channels);
+
+// Mixer Config
+int csf_init_player(song_t *csf, int reset); // bReset=false
+int csf_set_resampling_mode(song_t *csf, uint32_t mode); // SRCMODE_XXXX
+
+
+// sndmix
+unsigned int csf_read(song_t *csf, void *v_buffer, unsigned int bufsize);
+int csf_process_tick(song_t *csf);
+int csf_read_note(song_t *csf);
+
+// snd_fx
+unsigned int csf_get_length(song_t *csf); // (in seconds)
+void csf_instrument_change(song_t *csf, song_voice_t *chn, uint32_t instr, int porta, int instr_column);
+void csf_note_change(song_t *csf, uint32_t chan, int note, int porta, int retrig, int have_inst);
+uint32_t csf_get_nna_channel(song_t *csf, uint32_t chan);
+void csf_check_nna(song_t *csf, uint32_t chan, uint32_t instr, int note, int force_cut);
+void csf_process_effects(song_t *csf, int firsttick);
+
+void fx_note_cut(song_t *csf, uint32_t chan, int clear_note);
+void fx_key_off(song_t *csf, uint32_t chan);
+void csf_midi_send(song_t *csf, const unsigned char *data, unsigned int len, uint32_t chan, int fake);
+void csf_process_midi_macro(song_t *csf, uint32_t chan, const char *midi_macro, uint32_t param,
+ uint32_t note, uint32_t velocity, uint32_t use_instr);
+song_sample_t *csf_translate_keyboard(song_t *csf, song_instrument_t *ins, uint32_t note, song_sample_t *def);
+
+// various utility functions in snd_fx.c
+int get_note_from_period(int period);
+int get_period_from_note(int note, unsigned int c5speed, int linear);
+unsigned int get_freq_from_period(int period, int linear);
+unsigned int transpose_to_frequency(int transp, int ftune);
+int frequency_to_transpose(unsigned int freq);
+unsigned long calc_halftone(unsigned long hz, int rel);
+
+
+// sndfile
+song_t *csf_allocate(void);
+void csf_free(song_t *csf);
+
+void csf_destroy(song_t *csf); /* erase everything -- equiv. to new song */
+
+void csf_reset_midi_cfg(song_t *csf);
+void csf_copy_midi_cfg(song_t *dest, song_t *src);
+void csf_set_current_order(song_t *csf, uint32_t position);
+void csf_reset_playmarks(song_t *csf);
+
+void csf_forget_history(song_t *csf); // Send the edit log down the memory hole.
+
+/* apply a preset Adlib patch */
+void adlib_patch_apply(song_sample_t *smp, int patchnum);
+
+///////////////////////////////////////////////////////////
+
+// Return (a*b)/c - no divide error
+static inline int _muldiv(int a, int b, int c)
+{
+ return ((unsigned long long) a * (unsigned long long) b ) / c;
+}
+
+
+// Return (a*b+c/2)/c - no divide error
+static inline int _muldivr(int a, int b, int c)
+{
+ return ((unsigned long long) a * (unsigned long long) b + (c >> 1)) / c;
+}
+
+
+#endif
+
diff --git a/src/include/tables.h b/src/include/tables.h
new file mode 100644
index 0000000..5bac1d7
--- /dev/null
+++ b/src/include/tables.h
@@ -0,0 +1,56 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TABLES_H
+#define TABLES_H
+
+#include <stdint.h>
+
+// <eightbitbubsy> better than having a table.
+#define SHORT_PANNING(i) (((((i) << 4) | (i)) + 2) >> 2)
+
+/* TODO: I know just sticking _fast on all of these will break the player, but for some of 'em...? */
+
+extern const uint8_t vc_portamento_table[16]; // volume column Gx
+
+extern const uint16_t period_table[12];
+extern const uint16_t finetune_table[16];
+
+extern const int8_t sine_table[256];
+extern const int8_t ramp_down_table[256];
+extern const int8_t square_table[256];
+
+extern const int8_t retrig_table_1[16];
+extern const int8_t retrig_table_2[16];
+
+extern const uint32_t fine_linear_slide_up_table[16];
+extern const uint32_t fine_linear_slide_down_table[16];
+extern const uint32_t linear_slide_up_table[256];
+extern const uint32_t linear_slide_down_table[256];
+
+extern const char *midi_group_names[17];
+extern const char *midi_program_names[128];
+extern const char *midi_percussion_names[61];
+
+#endif /* ! TABLES_H */
+
diff --git a/src/include/util.h b/src/include/util.h
new file mode 100644
index 0000000..9a4024b
--- /dev/null
+++ b/src/include/util.h
@@ -0,0 +1,200 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <sys/stat.h> /* roundabout way to get time_t */
+#include <sys/types.h>
+
+/* --------------------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) ((signed)(sizeof(a)/sizeof(*(a))))
+
+
+/* macros stolen from glib */
+#ifndef MAX
+# define MAX(X,Y) (((X)>(Y))?(X):(Y))
+#endif
+#ifndef MIN
+# define MIN(X,Y) (((X)<(Y))?(X):(Y))
+#endif
+#ifndef CLAMP
+# define CLAMP(N,L,H) (((N)>(H))?(H):(((N)<(L))?(L):(N)))
+#endif
+
+#ifdef __GNUC__
+# ifndef LIKELY
+# define LIKELY(x) __builtin_expect(!!(x),1)
+# endif
+# ifndef UNLIKELY
+# define UNLIKELY(x) __builtin_expect(!!(x),0)
+# endif
+# ifndef UNUSED
+# define UNUSED __attribute__((unused))
+# endif
+# ifndef NORETURN
+# define NORETURN __attribute__((noreturn))
+# endif
+# ifndef PACKED
+# define PACKED __attribute__((packed))
+# endif
+# ifndef MALLOC
+# define MALLOC __attribute__ ((malloc))
+# endif
+#else
+# ifndef UNUSED
+# define UNUSED
+# endif
+# ifndef PACKED
+# define PACKED
+# endif
+# ifndef NORETURN
+# define NORETURN
+# endif
+# ifndef LIKELY
+# define LIKELY(x)
+# endif
+# ifndef UNLIKELY
+# define UNLIKELY(x)
+# endif
+# ifndef MALLOC
+# define MALLOC
+# endif
+#endif
+
+/* Path stuff that differs by platform */
+#ifdef WIN32
+# define DIR_SEPARATOR '\\'
+# define DIR_SEPARATOR_STR "\\"
+# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
+#else
+# define DIR_SEPARATOR '/'
+# define DIR_SEPARATOR_STR "/"
+# define IS_DIR_SEPARATOR(c) ((c) == '/')
+#endif
+
+/* --------------------------------------------------------------------- */
+/* functions returning const char * use a static buffer; ones returning char * malloc their return value
+(thus it needs free'd)... except numtostr, get_time_string, and get_date_string, which return the buffer
+passed to them in the 'buf' parameter. */
+
+/* memory */
+extern MALLOC void *mem_alloc(size_t);
+extern MALLOC char *str_dup(const char *);
+extern void *mem_realloc(void *,size_t);
+extern void mem_free(void *);
+
+/*Conversion*/
+/* linear -> deciBell*/
+/* amplitude normalized to 1.0f.*/
+extern float dB(float amplitude);
+
+/// deciBell -> linear*/
+extern float dB2_amp(float db);
+
+/* linear -> deciBell*/
+/* power normalized to 1.0f.*/
+extern float pdB(float power);
+
+/* deciBell -> linear*/
+extern float dB2_power(float db);
+
+/* linear -> deciBell*/
+/* amplitude normalized to 1.0f.*/
+/* Output scaled (and clipped) to 128 lines with noisefloor range.*/
+/* ([0..128] = [-noisefloor..0dB])*/
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+extern short dB_s(int noisefloor, float amplitude, float correction_dBs);
+
+/* deciBell -> linear*/
+/* Input scaled to 128 lines with noisefloor range.*/
+/* ([0..128] = [-noisefloor..0dB])*/
+/* amplitude normalized to 1.0f.*/
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+extern short dB2_amp_s(int noisefloor, int db, float correction_dBs);
+
+/* linear -> deciBell*/
+/* power normalized to 1.0f.*/
+/* Output scaled (and clipped) to 128 lines with noisefloor range.*/
+/* ([0..128] = [-noisefloor..0dB])*/
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+extern short pdB_s(int noisefloor, float power, float correction_dBs);
+
+/* deciBell -> linear*/
+/* Input scaled to 128 lines with noisefloor range.*/
+/* ([0..128] = [-noisefloor..0dB])*/
+/* power normalized to 1.0f.*/
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+extern short dB2_power_s(int noisefloor, int db, float correction_dBs);
+
+/* formatting */
+/* for get_{time,date}_string, buf should be (at least) 27 chars; anything past that isn't used. */
+char *get_date_string(time_t when, char *buf);
+char *get_time_string(time_t when, char *buf);
+char *numtostr(int digits, unsigned int n, char *buf);
+char *numtostr_signed(int digits, int n, char *buf);
+char *num99tostr(int n, char *buf);
+
+/* string handling */
+const char *get_basename(const char *filename);
+const char *get_extension(const char *filename); // including dot; "" if no extension (note: used to strip dot)
+char *get_parent_directory(const char *dirname);
+int ltrim_string(char *s); // return: length of string after trimming
+int rtrim_string(char *s);
+int trim_string(char *s);
+int str_break(const char *s, char c, char **first, char **second);
+char *str_escape(const char *source, int space_hack);
+char *str_unescape(const char *source);
+char *pretty_name(const char *filename);
+int get_num_lines(const char *text);
+char *str_concat(const char *s, ...);
+
+
+/* filesystem */
+int make_backup_file(const char *filename, int numbered);
+long file_size(const char *filename);
+int is_directory(const char *filename);
+/* following functions should free() the resulting strings */
+char *get_home_directory(void); /* "default" directory for user files, i.e. $HOME, My Documents, etc. */
+char *get_dot_directory(void); /* where settings files go (%AppData% on Windows, same as $HOME elsewhere) */
+char *get_current_directory(void); /* just a getcwd() wrapper */
+
+void put_env_var(const char *key, const char *value);
+void unset_env_var(const char *key);
+
+/* integer sqrt (very fast; 32 bits limited) */
+unsigned int i_sqrt(unsigned int r);
+
+/* sleep in msec */
+void ms_sleep(unsigned int m);
+
+/* run a hook */
+int run_hook(const char *dir, const char *name, const char *maybe_arg);
+
+/* Mostly a glorified rename(), with fixes for certain dumb OSes.
+If 'overwrite' is zero, attempts to rename over an existing file will fail with EEXIST. */
+int rename_file(const char *old, const char *newf, int overwrite);
+
+#endif /* ! UTIL_H */
+
diff --git a/src/player/csndfile.c b/src/player/csndfile.c
new file mode 100644
index 0000000..bba8ae0
--- /dev/null
+++ b/src/player/csndfile.c
@@ -0,0 +1,1094 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NEED_BYTESWAP
+#define NEED_TIME
+#include "headers.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "sndfile.h"
+#include "log.h"
+#include "util.h"
+#include "fmt.h" // for it_decompress8 / it_decompress16
+
+
+static void _csf_reset(song_t *csf)
+{
+ unsigned int i;
+
+ csf->flags = 0;
+ csf->pan_separation = 128;
+ csf->num_voices = 0;
+ csf->freq_factor = csf->tempo_factor = 128;
+ csf->initial_global_volume = 128;
+ csf->current_global_volume = 128;
+ csf->initial_speed = 6;
+ csf->initial_tempo = 125;
+ csf->process_row = 0;
+ csf->row = 0;
+ csf->current_pattern = 0;
+ csf->current_order = 0;
+ csf->process_order = 0;
+ csf->mixing_volume = 0x30;
+ memset(csf->message, 0, sizeof(csf->message));
+
+ csf->row_highlight_major = 16;
+ csf->row_highlight_minor = 4;
+
+ /* This is intentionally crappy quality, so that it's very obvious if it didn't get initialized */
+ csf->mix_flags = 0;
+ csf_set_wave_config(csf, 4000, 8, 1);
+
+ memset(csf->voices, 0, sizeof(csf->voices));
+ memset(csf->voice_mix, 0, sizeof(csf->voice_mix));
+ memset(csf->samples, 0, sizeof(csf->samples));
+ memset(csf->instruments, 0, sizeof(csf->instruments));
+ memset(csf->orderlist, 0xFF, sizeof(csf->orderlist));
+ memset(csf->patterns, 0, sizeof(csf->patterns));
+
+ csf_reset_midi_cfg(csf);
+ csf_forget_history(csf);
+
+ for (i = 0; i < MAX_PATTERNS; i++) {
+ csf->pattern_size[i] = 64;
+ csf->pattern_alloc_size[i] = 64;
+ }
+ for (i = 0; i < MAX_SAMPLES; i++) {
+ csf->samples[i].c5speed = 8363;
+ csf->samples[i].volume = 64 * 4;
+ csf->samples[i].global_volume = 64;
+ }
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ csf->channels[i].panning = 128;
+ csf->channels[i].volume = 64;
+ csf->channels[i].flags = 0;
+ }
+}
+
+//////////////////////////////////////////////////////////
+// song_t
+
+song_t *csf_allocate(void)
+{
+ song_t *csf = calloc(1, sizeof(song_t));
+ _csf_reset(csf);
+ return csf;
+}
+
+void csf_free(song_t *csf)
+{
+ if (csf) {
+ csf_destroy(csf);
+ free(csf);
+ }
+}
+
+
+static void _init_envelope(song_envelope_t *env, int n)
+{
+ env->nodes = 2;
+ env->ticks[0] = 0;
+ env->ticks[1] = 100;
+ env->values[0] = n;
+ env->values[1] = n;
+}
+
+void csf_init_instrument(song_instrument_t *ins, int samp)
+{
+ int n;
+ _init_envelope(&ins->vol_env, 64);
+ _init_envelope(&ins->pan_env, 32);
+ _init_envelope(&ins->pitch_env, 32);
+ ins->global_volume = 128;
+ ins->panning = 128;
+ ins->midi_bank = -1;
+ ins->midi_program = -1;
+ ins->pitch_pan_center = 60; // why does pitch/pan not use the same note values as everywhere else?!
+ for (n = 0; n < 128; n++) {
+ ins->sample_map[n] = samp;
+ ins->note_map[n] = n + 1;
+ }
+}
+
+song_instrument_t *csf_allocate_instrument(void)
+{
+ song_instrument_t *ins = calloc(1, sizeof(song_instrument_t));
+ csf_init_instrument(ins, 0);
+ return ins;
+}
+
+void csf_free_instrument(song_instrument_t *i)
+{
+ free(i);
+}
+
+
+void csf_destroy(song_t *csf)
+{
+ int i;
+
+ for (i = 0; i < MAX_PATTERNS; i++) {
+ if (csf->patterns[i]) {
+ csf_free_pattern(csf->patterns[i]);
+ csf->patterns[i] = NULL;
+ }
+ }
+ for (i = 1; i < MAX_SAMPLES; i++) {
+ song_sample_t *pins = &csf->samples[i];
+ if (pins->data) {
+ csf_free_sample(pins->data);
+ pins->data = NULL;
+ }
+ }
+ for (i = 0; i < MAX_INSTRUMENTS; i++) {
+ if (csf->instruments[i]) {
+ csf_free_instrument(csf->instruments[i]);
+ csf->instruments[i] = NULL;
+ }
+ }
+
+ _csf_reset(csf);
+}
+
+song_note_t *csf_allocate_pattern(uint32_t rows)
+{
+ return calloc(rows * MAX_CHANNELS, sizeof(song_note_t));
+}
+
+void csf_free_pattern(void *pat)
+{
+ free(pat);
+}
+
+/* Note: this function will appear in valgrind to be a sieve for memory leaks.
+It isn't; it's just being confused by the adjusted pointer being stored. */
+signed char *csf_allocate_sample(uint32_t nbytes)
+{
+ signed char *p = calloc(1, (nbytes + 39) & ~7); // magic
+ if (p)
+ p += 16;
+ return p;
+}
+
+void csf_free_sample(void *p)
+{
+ if (p)
+ free(p - 16);
+}
+
+void csf_forget_history(song_t *csf)
+{
+ free(csf->histdata);
+ csf->histdata = NULL;
+ csf->histlen = 0;
+ gettimeofday(&csf->editstart, NULL);
+}
+
+/* --------------------------------------------------------------------------------------------------------- */
+/* Counting and checking stuff. */
+
+static int name_is_blank(char *name)
+{
+ int n;
+ for (n = 0; n < 25; n++) {
+ if (name[n] != '\0' && name[n] != ' ')
+ return 0;
+ }
+ return 1;
+}
+
+const song_note_t blank_pattern[64 * 64];
+const song_note_t *blank_note = blank_pattern; // Same thing, really.
+
+int csf_note_is_empty(song_note_t *note)
+{
+ return !memcmp(note, blank_pattern, sizeof(song_note_t));
+}
+
+int csf_pattern_is_empty(song_t *csf, int n)
+{
+ if (!csf->patterns[n])
+ return 1;
+ if (csf->pattern_size[n] != 64)
+ return 0;
+ return !memcmp(csf->patterns[n], blank_pattern, sizeof(blank_pattern));
+}
+
+int csf_sample_is_empty(song_sample_t *smp)
+{
+ return (smp->data == NULL
+ && name_is_blank(smp->name)
+ && smp->filename[0] == '\0'
+ && smp->c5speed == 8363
+ && smp->volume == 64*4 //mphack
+ && smp->global_volume == 64
+ && smp->panning == 0
+ && !(smp->flags & (CHN_LOOP | CHN_SUSTAINLOOP | CHN_PANNING))
+ && smp->length == 0
+ && smp->loop_start == 0
+ && smp->loop_end == 0
+ && smp->sustain_start == 0
+ && smp->sustain_end == 0
+ && smp->vib_type == VIB_SINE
+ && smp->vib_rate == 0
+ && smp->vib_depth == 0
+ && smp->vib_speed == 0
+ );
+}
+
+static int env_is_blank(song_envelope_t *env, int value)
+{
+ return (env->nodes == 2
+ && env->loop_start == 0
+ && env->loop_end == 0
+ && env->sustain_start == 0
+ && env->sustain_end == 0
+ && env->ticks[0] == 0
+ && env->ticks[1] == 100
+ && env->values[0] == value
+ && env->values[1] == value
+ );
+}
+
+int csf_instrument_is_empty(song_instrument_t *ins)
+{
+ int n;
+ if (!ins)
+ return 1;
+
+ for (n = 0; n < NOTE_LAST - NOTE_FIRST; n++) {
+ if (ins->sample_map[n] != 0 || ins->note_map[n] != (n + NOTE_FIRST))
+ return 0;
+ }
+ return (name_is_blank(ins->name)
+ && ins->filename[0] == '\0'
+ && ins->flags == 0 /* No envelopes, loop points, panning, or carry flags set */
+ && ins->nna == NNA_NOTECUT
+ && ins->dct == DCT_NONE
+ && ins->dca == DCA_NOTECUT
+ && env_is_blank(&ins->vol_env, 64)
+ && ins->global_volume == 128
+ && ins->fadeout == 0
+ && ins->vol_swing == 0
+ && env_is_blank(&ins->pan_env, 32)
+ && ins->panning == 32*4 //mphack
+ && ins->pitch_pan_center == 60 // C-5 (blah)
+ && ins->pitch_pan_separation == 0
+ && ins->pan_swing == 0
+ && env_is_blank(&ins->pitch_env, 32)
+ && ins->ifc == 0
+ && ins->ifr == 0
+ && ins->midi_channel_mask == 0
+ && ins->midi_program == -1
+ && ins->midi_bank == -1
+ );
+}
+
+// IT-compatible: last order of "main song", or 0
+int csf_last_order(song_t *csf)
+{
+ int n = 0;
+ while (n < MAX_ORDERS && csf->orderlist[n] != ORDER_LAST)
+ n++;
+ return n ? n - 1 : 0;
+}
+
+// Total count of orders in orderlist before end of data
+int csf_get_num_orders(song_t *csf)
+{
+ int n = MAX_ORDERS;
+ while (n >= 0 && csf->orderlist[--n] == ORDER_LAST) {
+ }
+ return n + 1;
+}
+
+// Total number of non-empty patterns in song, according to csf_pattern_is_empty
+int csf_get_num_patterns(song_t *csf)
+{
+ int n = MAX_PATTERNS - 1;
+ while (n && csf_pattern_is_empty(csf, n))
+ n--;
+ return n+ 1;
+}
+
+int csf_get_num_samples(song_t *csf)
+{
+ int n = MAX_SAMPLES - 1;
+ while (n > 0 && csf_sample_is_empty(csf->samples + n))
+ n--;
+ return n;
+}
+
+int csf_get_num_instruments(song_t *csf)
+{
+ int n = MAX_INSTRUMENTS - 1;
+ while (n > 0 && csf_instrument_is_empty(csf->instruments[n]))
+ n--;
+ return n;
+}
+
+
+int csf_first_blank_sample(song_t *csf, int start)
+{
+ int n;
+ for (n = MAX(start, 1); n < MAX_SAMPLES; n++) {
+ if (csf_sample_is_empty(csf->samples + n))
+ return n;
+ }
+ return -1;
+}
+
+int csf_first_blank_instrument(song_t *csf, int start)
+{
+ int n;
+ for (n = MAX(start, 1); n < MAX_INSTRUMENTS; n++) {
+ if (csf_instrument_is_empty(csf->instruments[n]))
+ return n;
+ }
+ return -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Misc functions
+
+midi_config_t default_midi_config;
+
+
+void csf_reset_midi_cfg(song_t *csf)
+{
+ memcpy(&csf->midi_config, &default_midi_config, sizeof(default_midi_config));
+}
+
+void csf_copy_midi_cfg(song_t *dest, song_t *src)
+{
+ memcpy(&dest->midi_config, &src->midi_config, sizeof(midi_config_t));
+}
+
+
+int csf_set_wave_config(song_t *csf, uint32_t rate,uint32_t bits,uint32_t channels)
+{
+ int reset = ((csf->mix_frequency != rate)
+ || (csf->mix_bits_per_sample != bits)
+ || (csf->mix_channels != channels));
+ csf->mix_channels = channels;
+ csf->mix_frequency = rate;
+ csf->mix_bits_per_sample = bits;
+ csf_init_player(csf, reset);
+ return 1;
+}
+
+
+int csf_set_resampling_mode(song_t *csf, uint32_t mode)
+{
+ uint32_t d = csf->mix_flags & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE);
+ switch(mode) {
+ case SRCMODE_NEAREST: d |= SNDMIX_NORESAMPLING; break;
+ case SRCMODE_LINEAR: break;
+ case SRCMODE_SPLINE: d |= SNDMIX_HQRESAMPLER; break;
+ case SRCMODE_POLYPHASE: d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); break;
+ default: return 0;
+ }
+ csf->mix_flags = d;
+ return 1;
+}
+
+
+// This used to use some retarded positioning based on the total number of rows elapsed, which is useless.
+// However, the only code calling this function is in this file, to set it to the start, so I'm optimizing
+// out the row count.
+static void set_current_pos_0(song_t *csf)
+{
+ song_voice_t *v = csf->voices;
+ for (uint32_t i = 0; i < MAX_VOICES; i++, v++) {
+ memset(v, 0, sizeof(*v));
+ v->cutoff = 0x7F;
+ v->volume = 256;
+ if (i < MAX_CHANNELS) {
+ v->panning = csf->channels[i].panning;
+ v->global_volume = csf->channels[i].volume;
+ v->flags = csf->channels[i].flags;
+ } else {
+ v->panning = 128;
+ v->global_volume = 64;
+ }
+ }
+ csf->current_global_volume = csf->initial_global_volume;
+ csf->current_speed = csf->initial_speed;
+ csf->current_tempo = csf->initial_tempo;
+}
+
+
+void csf_set_current_order(song_t *csf, uint32_t position)
+{
+ for (uint32_t j = 0; j < MAX_VOICES; j++) {
+ song_voice_t *v = csf->voices + j;
+
+ v->period = 0;
+ v->note = v->new_note = v->new_instrument = 0;
+ v->portamento_target = 0;
+ v->n_command = 0;
+ v->cd_patloop = 0;
+ v->patloop_row = 0;
+ v->cd_tremor = 0;
+ // modplug sets vib pos to 16 in old effects mode for some reason *shrug*
+ v->vibrato_position = (csf->flags & SONG_ITOLDEFFECTS) ? 0 : 0x10;
+ v->tremolo_position = 0;
+ }
+ if (position > MAX_ORDERS)
+ position = 0;
+ if (!position)
+ set_current_pos_0(csf);
+
+ csf->process_order = position - 1;
+ csf->process_row = PROCESS_NEXT_ORDER;
+ csf->row = 0;
+ csf->break_row = 0; /* set this to whatever row to jump to */
+ csf->tick_count = 1;
+ csf->row_count = 0;
+ csf->buffer_count = 0;
+
+ csf->flags &= ~(SONG_PATTERNLOOP|SONG_ENDREACHED);
+}
+
+void csf_reset_playmarks(song_t *csf)
+{
+ int n;
+
+ for (n = 1; n < MAX_SAMPLES; n++) {
+ csf->samples[n].played = 0;
+ }
+ for (n = 1; n < MAX_INSTRUMENTS; n++) {
+ if (csf->instruments[n])
+ csf->instruments[n]->played = 0;
+ }
+}
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#define SF_FAIL(name, n) \
+ ({ log_appendf(4, "%s: internal error: unsupported %s %d", __FUNCTION__, name, n); return 0; })
+
+
+uint32_t csf_read_sample(song_sample_t *sample, uint32_t flags, const void *filedata, uint32_t memsize)
+{
+ uint32_t len = 0, mem;
+ const char *buffer = (const char *) filedata;
+
+ if (sample->flags & CHN_ADLIB) return 0; // no sample data
+
+ if (!sample || sample->length < 1 || !buffer) return 0;
+
+ // validate the read flags before anything else
+ switch (flags & SF_BIT_MASK) {
+ case SF_7: case SF_8: case SF_16: case SF_24: case SF_32: break;
+ default: SF_FAIL("bit width", flags & SF_BIT_MASK);
+ }
+ switch (flags & SF_CHN_MASK) {
+ case SF_M: case SF_SI: case SF_SS: break;
+ default: SF_FAIL("channel mask", flags & SF_CHN_MASK);
+ }
+ switch (flags & SF_END_MASK) {
+ case SF_LE: case SF_BE: break;
+ default: SF_FAIL("endianness", flags & SF_END_MASK);
+ }
+ switch (flags & SF_ENC_MASK) {
+ case SF_PCMS: case SF_PCMU: case SF_PCMD: case SF_IT214: case SF_IT215:
+ case SF_AMS: case SF_DMF: case SF_MDL: case SF_PTM:
+ break;
+ default: SF_FAIL("encoding", flags & SF_ENC_MASK);
+ }
+ if ((flags & ~(SF_BIT_MASK | SF_CHN_MASK | SF_END_MASK | SF_ENC_MASK)) != 0) {
+ SF_FAIL("extra flag", flags & ~(SF_BIT_MASK | SF_CHN_MASK | SF_END_MASK | SF_ENC_MASK));
+ }
+
+ if (sample->length > MAX_SAMPLE_LENGTH) sample->length = MAX_SAMPLE_LENGTH;
+ mem = sample->length+6;
+ sample->flags &= ~(CHN_16BIT|CHN_STEREO);
+ switch (flags & SF_BIT_MASK) {
+ case SF_16: case SF_24: case SF_32:
+ // these are all stuffed into 16 bits.
+ mem *= 2;
+ sample->flags |= CHN_16BIT;
+ }
+ switch (flags & SF_CHN_MASK) {
+ case SF_SI: case SF_SS:
+ mem *= 2;
+ sample->flags |= CHN_STEREO;
+ }
+ if ((sample->data = csf_allocate_sample(mem)) == NULL) {
+ sample->length = 0;
+ return 0;
+ }
+ switch(flags) {
+ // 1: 8-bit unsigned PCM data
+ case RS_PCM8U:
+ {
+ len = sample->length;
+ if (len > memsize) len = sample->length = memsize;
+ signed char *data = sample->data;
+ for (uint32_t j=0; j<len; j++) data[j] = (signed char)(buffer[j] - 0x80);
+ }
+ break;
+
+ // 2: 8-bit ADPCM data with linear table
+ case RS_PCM8D:
+ {
+ len = sample->length;
+ if (len > memsize) break;
+ signed char *data = sample->data;
+ const signed char *p = (const signed char *)buffer;
+ int delta = 0;
+ for (uint32_t j=0; j<len; j++) {
+ delta += p[j];
+ *data++ = (signed char)delta;
+ }
+ }
+ break;
+
+ // 4: 16-bit ADPCM data with linear table
+ case RS_PCM16D:
+ {
+ len = sample->length * 2;
+ if (len > memsize) break;
+ short *data = (short *)sample->data;
+ short *p = (short *)buffer;
+ unsigned short tmp;
+ int delta16 = 0;
+ for (uint32_t j=0; j<len; j+=2) {
+ tmp = *((unsigned short *)p++);
+ delta16 += bswapLE16(tmp);
+ *data++ = (short) delta16;
+ }
+ }
+ break;
+
+ // 5: 16-bit signed PCM data
+ case RS_PCM16S:
+ {
+ len = sample->length * 2;
+ if (len <= memsize) memcpy(sample->data, buffer, len);
+ short int *data = (short int *)sample->data;
+ for (uint32_t j=0; j<len; j+=2) {
+ *data = bswapLE16(*data);
+ data++;
+ }
+ }
+ break;
+
+ // 16-bit signed mono PCM motorola byte order
+ case RS_PCM16M:
+ len = sample->length * 2;
+ if (len > memsize) len = memsize & ~1;
+ if (len > 1) {
+ signed char *data = (signed char *)sample->data;
+ signed char *src = (signed char *)buffer;
+ for (uint32_t j=0; j<len; j+=2) {
+ // data[j] = src[j+1];
+ // data[j+1] = src[j];
+ *((unsigned short *)(data+j)) = bswapBE16(*((unsigned short *)(src+j)));
+ }
+ }
+ break;
+
+ // 6: 16-bit unsigned PCM data
+ case RS_PCM16U:
+ {
+ len = sample->length * 2;
+ if (len <= memsize) memcpy(sample->data, buffer, len);
+ short int *data = (short int *)sample->data;
+ for (uint32_t j=0; j<len; j+=2) {
+ *data = bswapLE16(*data) - 0x8000;
+ data++;
+ }
+ }
+ break;
+
+ // 16-bit signed stereo big endian
+ case RS_STPCM16M:
+ len = sample->length * 2;
+ if (len*2 <= memsize) {
+ signed char *data = (signed char *)sample->data;
+ signed char *src = (signed char *)buffer;
+ for (uint32_t j=0; j<len; j+=2) {
+ // data[j*2] = src[j+1];
+ // data[j*2+1] = src[j];
+ // data[j*2+2] = src[j+1+len];
+ // data[j*2+3] = src[j+len];
+ *((unsigned short *)(data+j*2))
+ = bswapBE16(*((unsigned short *)(src+j)));
+ *((unsigned short *)(data+j*2+2))
+ = bswapBE16(*((unsigned short *)(src+j+len)));
+ }
+ len *= 2;
+ }
+ break;
+
+ // 8-bit stereo samples
+ case RS_STPCM8S:
+ case RS_STPCM8U:
+ case RS_STPCM8D:
+ {
+ int iadd_l, iadd_r;
+ iadd_l = iadd_r = (flags == RS_STPCM8U) ? -128 : 0;
+ len = sample->length;
+ signed char *psrc = (signed char *)buffer;
+ signed char *data = (signed char *)sample->data;
+ if (len*2 > memsize) break;
+ for (uint32_t j=0; j<len; j++) {
+ data[j*2] = (signed char)(psrc[0] + iadd_l);
+ data[j*2+1] = (signed char)(psrc[len] + iadd_r);
+ psrc++;
+ if (flags == RS_STPCM8D) {
+ iadd_l = data[j*2];
+ iadd_r = data[j*2+1];
+ }
+ }
+ len *= 2;
+ }
+ break;
+
+ // 16-bit stereo samples
+ case RS_STPCM16S:
+ case RS_STPCM16U:
+ case RS_STPCM16D:
+ {
+ int iadd_l, iadd_r;
+ iadd_l = iadd_r = (flags == RS_STPCM16U) ? -0x8000 : 0;
+ len = sample->length;
+ short int *psrc = (short int *)buffer;
+ short int *data = (short int *)sample->data;
+ if (len*4 > memsize) break;
+ for (uint32_t j=0; j<len; j++) {
+ data[j*2] = (short int) (bswapLE16(psrc[0]) + iadd_l);
+ data[j*2+1] = (short int) (bswapLE16(psrc[len]) + iadd_r);
+ psrc++;
+ if (flags == RS_STPCM16D) {
+ iadd_l = data[j*2];
+ iadd_r = data[j*2+1];
+ }
+ }
+ len *= 4;
+ }
+ break;
+
+ // IT 2.14 compressed samples
+ case RS_IT2148:
+ case RS_IT21416:
+ case RS_IT2158:
+ case RS_IT21516:
+ len = memsize;
+ if (len < 2) break;
+ if (flags == RS_IT2148 || flags == RS_IT2158) {
+ it_decompress8(sample->data, sample->length,
+ buffer, memsize, (flags == RS_IT2158), 1);
+ } else {
+ it_decompress16(sample->data, sample->length,
+ buffer, memsize, (flags == RS_IT21516), 1);
+ }
+ break;
+ case RS_IT2148S:
+ case RS_IT21416S:
+ case RS_IT2158S:
+ case RS_IT21516S:
+ len = memsize;
+ if (len < 4) break;
+ if (flags == RS_IT2148S || flags == RS_IT2158S) {
+ uint32_t offset = it_decompress8(sample->data, sample->length,
+ buffer, memsize, (flags == RS_IT2158S), 2);
+ it_decompress8(sample->data + 1, sample->length,
+ buffer + offset, memsize - offset, (flags == RS_IT2158S), 2);
+ } else {
+ uint32_t offset = it_decompress16(sample->data, sample->length,
+ buffer, memsize, (flags == RS_IT21516S), 2);
+ it_decompress16(sample->data + 2, sample->length,
+ buffer + offset, memsize - offset, (flags == RS_IT21516S), 2);
+ }
+ break;
+
+ // 8-bit interleaved stereo samples
+ case RS_STIPCM8S:
+ case RS_STIPCM8U:
+ {
+ int iadd = 0;
+ if (flags == RS_STIPCM8U) { iadd = -0x80; }
+ len = sample->length;
+ if (len*2 > memsize) len = memsize >> 1;
+ uint8_t * psrc = (uint8_t *)buffer;
+ uint8_t * data = (uint8_t *)sample->data;
+ for (uint32_t j=0; j<len; j++) {
+ data[j*2] = (signed char)(psrc[0] + iadd);
+ data[j*2+1] = (signed char)(psrc[1] + iadd);
+ psrc+=2;
+ }
+ len *= 2;
+ }
+ break;
+
+ // 16-bit interleaved stereo samples
+ case RS_STIPCM16S:
+ case RS_STIPCM16U:
+ {
+ int iadd = 0;
+ if (flags == RS_STIPCM16U) iadd = -32768;
+ len = sample->length;
+ if (len*4 > memsize) len = memsize >> 2;
+ short int *psrc = (short int *)buffer;
+ short int *data = (short int *)sample->data;
+ for (uint32_t j=0; j<len; j++) {
+ data[j*2] = (short int)(bswapLE16(psrc[0]) + iadd);
+ data[j*2+1] = (short int)(bswapLE16(psrc[1]) + iadd);
+ psrc += 2;
+ }
+ len *= 4;
+ }
+ break;
+
+#if 0
+ // AMS compressed samples
+ case RS_AMS8:
+ case RS_AMS16:
+ len = 9;
+ if (memsize > 9) {
+ const char *psrc = buffer;
+ char packcharacter = buffer[8], *pdest = (char *)sample->data;
+ len += bswapLE32(*((uint32_t *)(buffer+4)));
+ if (len > memsize) len = memsize;
+ uint32_t dmax = sample->length;
+ if (sample->flags & CHN_16BIT) dmax <<= 1;
+ AMSUnpack(psrc+9, len-9, pdest, dmax, packcharacter);
+ }
+ break;
+#endif
+
+ // PTM 8bit delta to 16-bit sample
+ case RS_PTM8DTO16:
+ {
+ len = sample->length * 2;
+ if (len > memsize) break;
+ signed char *data = (signed char *)sample->data;
+ signed char delta8 = 0;
+ for (uint32_t j=0; j<len; j++) {
+ delta8 += buffer[j];
+ *data++ = delta8;
+ }
+ uint16_t *data16 = (uint16_t *)sample->data;
+ for (uint32_t j=0; j<len; j+=2) {
+ *data16 = bswapLE16(*data16);
+ data16++;
+ }
+ }
+ break;
+
+ // Huffman MDL compressed samples
+ case RS_MDL8:
+ case RS_MDL16:
+ if (memsize >= 8) {
+ // first 4 bytes indicate packed length
+ len = bswapLE32(*((uint32_t *) buffer));
+ len = MIN(len, memsize) + 4;
+ uint8_t * data = (uint8_t *)sample->data;
+ uint8_t * ibuf = (uint8_t *)(buffer + 4);
+ uint32_t bitbuf = bswapLE32(*((uint32_t *)ibuf));
+ uint32_t bitnum = 32;
+ uint8_t dlt = 0, lowbyte = 0;
+ ibuf += 4;
+ // TODO move all this junk to fmt/compression.c
+ for (uint32_t j=0; j<sample->length; j++) {
+ uint8_t hibyte;
+ uint8_t sign;
+ if (flags == RS_MDL16)
+ lowbyte = (uint8_t)mdl_read_bits(&bitbuf, &bitnum, &ibuf, 8);
+ sign = (uint8_t)mdl_read_bits(&bitbuf, &bitnum, &ibuf, 1);
+ if (mdl_read_bits(&bitbuf, &bitnum, &ibuf, 1)) {
+ hibyte = (uint8_t)mdl_read_bits(&bitbuf, &bitnum, &ibuf, 3);
+ } else {
+ hibyte = 8;
+ while (!mdl_read_bits(&bitbuf, &bitnum, &ibuf, 1)) hibyte += 0x10;
+ hibyte += mdl_read_bits(&bitbuf, &bitnum, &ibuf, 4);
+ }
+ if (sign) hibyte = ~hibyte;
+ dlt += hibyte;
+ if (flags == RS_MDL8) {
+ data[j] = dlt;
+ } else {
+#ifdef WORDS_BIGENDIAN
+ data[j<<1] = dlt;
+ data[(j<<1)+1] = lowbyte;
+#else
+ data[j<<1] = lowbyte;
+ data[(j<<1)+1] = dlt;
+#endif
+ }
+ }
+ }
+ break;
+
+#if 0
+ case RS_DMF8:
+ case RS_DMF16:
+ len = memsize;
+ if (len >= 4) {
+ uint32_t maxlen = sample->length;
+ if (sample->flags & CHN_16BIT) maxlen <<= 1;
+ uint8_t * ibuf = (uint8_t *)buffer;
+ uint8_t * ibufmax = (uint8_t *)(buffer+memsize);
+ len = DMFUnpack((uint8_t *)sample->data, ibuf, ibufmax, maxlen);
+ }
+ break;
+#endif
+
+ // PCM 24-bit signed -> load sample, and normalize it to 16-bit
+ case RS_PCM24S:
+ case RS_PCM32S:
+ len = sample->length * 3;
+ if (flags == RS_PCM32S) len += sample->length;
+ if (len > memsize) break;
+ if (len > 4*8) {
+ uint32_t slsize = (flags == RS_PCM32S) ? 4 : 3;
+ uint8_t * src = (uint8_t *)buffer;
+ int32_t max = 255;
+ if (flags == RS_PCM32S) src++;
+ for (uint32_t j=0; j<len; j+=slsize) {
+ int32_t l = ((((src[j+2] << 8) + src[j+1]) << 8) + src[j]) << 8;
+ l /= 256;
+ if (l > max) max = l;
+ if (-l > max) max = -l;
+ }
+ max = (max / 128) + 1;
+ signed short *dest = (signed short *)sample->data;
+ for (uint32_t k=0; k<len; k+=slsize) {
+ int32_t l = ((((src[k+2] << 8) + src[k+1]) << 8) + src[k]) << 8;
+ *dest++ = (signed short)(l / max);
+ }
+ }
+ break;
+
+
+ // Stereo PCM 24-bit signed -> load sample, and normalize it to 16-bit
+ case RS_STIPCM24S:
+ case RS_STIPCM32S:
+ len = sample->length * 6;
+ if (flags == RS_STIPCM32S) len += sample->length * 2;
+ if (len > memsize) break;
+ if (len > 8*8) {
+ uint32_t slsize = (flags == RS_STIPCM32S) ? 4 : 3;
+ uint8_t * src = (uint8_t *)buffer;
+ int32_t max = 255;
+ if (flags == RS_STIPCM32S) src++;
+ for (uint32_t j=0; j<len; j+=slsize) {
+ int32_t l = ((((src[j+2] << 8) + src[j+1]) << 8) + src[j]) << 8;
+ l /= 256;
+ if (l > max) max = l;
+ if (-l > max) max = -l;
+ }
+ max = (max / 128) + 1;
+ signed short *dest = (signed short *)sample->data;
+ for (uint32_t k=0; k<len; k+=slsize) {
+ int32_t ll = ((((src[k+2] << 8) + src[k+1]) << 8) + src[k]) << 8;
+ k += slsize;
+ int32_t lr = ((((src[k+2] << 8) + src[k+1]) << 8) + src[k]) << 8;
+ dest[0] = (signed short)(ll/max);
+ dest[1] = (signed short)(lr/max);
+ dest += 2;
+ }
+ }
+ break;
+
+
+ // 16-bit signed big endian interleaved stereo
+ case RS_STIPCM16M:
+ {
+ len = sample->length;
+ if (len*4 > memsize) len = memsize >> 2;
+ const uint8_t * psrc = (const uint8_t *)buffer;
+ short int *data = (short int *)sample->data;
+ for (uint32_t j=0; j<len; j++) {
+ data[j*2] = (signed short)(((uint32_t)psrc[0] << 8) | (psrc[1]));
+ data[j*2+1] = (signed short)(((uint32_t)psrc[2] << 8) | (psrc[3]));
+ psrc += 4;
+ }
+ len *= 4;
+ }
+ break;
+
+ // 7-bit (data shifted one bit left)
+ case SF(7,M,BE,PCMS):
+ case SF(7,M,LE,PCMS):
+ sample->flags &= ~(CHN_16BIT | CHN_STEREO);
+ len = sample->length = MIN(sample->length, memsize);
+ for (uint32_t j = 0; j < len; j++)
+ sample->data[j] = CLAMP(buffer[j] * 2, -128, 127);
+ break;
+
+ // Default: 8-bit signed PCM data
+ default:
+ printf("DEFAULT: %d\n", flags);
+ case SF(8,M,BE,PCMS): /* endianness is irrelevant for 8-bit samples */
+ case SF(8,M,LE,PCMS):
+ sample->flags &= ~(CHN_16BIT | CHN_STEREO);
+ len = sample->length;
+ if (len > memsize) len = sample->length = memsize;
+ memcpy(sample->data, buffer, len);
+ break;
+ }
+ if (len > memsize) {
+ if (sample->data) {
+ sample->length = 0;
+ csf_free_sample(sample->data);
+ sample->data = NULL;
+ }
+ return 0;
+ }
+ csf_adjust_sample_loop(sample);
+ return len;
+}
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+void csf_adjust_sample_loop(song_sample_t *sample)
+{
+ if (!sample->data) return;
+ if (sample->loop_end > sample->length) sample->loop_end = sample->length;
+ if (sample->loop_start+2 >= sample->loop_end) {
+ sample->loop_start = sample->loop_end = 0;
+ sample->flags &= ~CHN_LOOP;
+ }
+
+ // poopy, removing all that loop-hacking code has produced... very nasty sounding loops!
+ // so I guess I should rewrite the crap at the end of the sample at least.
+ uint32_t len = sample->length;
+ if (sample->flags & CHN_16BIT) {
+ short int *data = (short int *)sample->data;
+ // Adjust end of sample
+ if (sample->flags & CHN_STEREO) {
+ data[len*2+6]
+ = data[len*2+4]
+ = data[len*2+2]
+ = data[len*2]
+ = data[len*2-2];
+ data[len*2+7]
+ = data[len*2+5]
+ = data[len*2+3]
+ = data[len*2+1]
+ = data[len*2-1];
+ } else {
+ data[len+4]
+ = data[len+3]
+ = data[len+2]
+ = data[len+1]
+ = data[len]
+ = data[len-1];
+ }
+ } else {
+ signed char *data = sample->data;
+ // Adjust end of sample
+ if (sample->flags & CHN_STEREO) {
+ data[len*2+6]
+ = data[len*2+4]
+ = data[len*2+2]
+ = data[len*2]
+ = data[len*2-2];
+ data[len*2+7]
+ = data[len*2+5]
+ = data[len*2+3]
+ = data[len*2+1]
+ = data[len*2-1];
+ } else {
+ data[len+4]
+ = data[len+3]
+ = data[len+2]
+ = data[len+1]
+ = data[len]
+ = data[len-1];
+ }
+ }
+}
+
+
+void csf_import_s3m_effect(song_note_t *m, int from_it)
+{
+ uint32_t effect = m->effect;
+ uint32_t param = m->param;
+ switch (effect + 0x40)
+ {
+ case 'A': effect = FX_SPEED; break;
+ case 'B': effect = FX_POSITIONJUMP; break;
+ case 'C':
+ effect = FX_PATTERNBREAK;
+ if (!from_it)
+ param = (param >> 4) * 10 + (param & 0x0F);
+ break;
+ case 'D': effect = FX_VOLUMESLIDE; break;
+ case 'E': effect = FX_PORTAMENTODOWN; break;
+ case 'F': effect = FX_PORTAMENTOUP; break;
+ case 'G': effect = FX_TONEPORTAMENTO; break;
+ case 'H': effect = FX_VIBRATO; break;
+ case 'I': effect = FX_TREMOR; break;
+ case 'J': effect = FX_ARPEGGIO; break;
+ case 'K': effect = FX_VIBRATOVOL; break;
+ case 'L': effect = FX_TONEPORTAVOL; break;
+ case 'M': effect = FX_CHANNELVOLUME; break;
+ case 'N': effect = FX_CHANNELVOLSLIDE; break;
+ case 'O': effect = FX_OFFSET; break;
+ case 'P': effect = FX_PANNINGSLIDE; break;
+ case 'Q': effect = FX_RETRIG; break;
+ case 'R': effect = FX_TREMOLO; break;
+ case 'S':
+ effect = FX_SPECIAL;
+ // convert old SAx to S8x
+ if (!from_it && ((param & 0xf0) == 0xa0))
+ param = 0x80 | ((param & 0xf) ^ 8);
+ break;
+ case 'T': effect = FX_TEMPO; break;
+ case 'U': effect = FX_FINEVIBRATO; break;
+ case 'V':
+ effect = FX_GLOBALVOLUME;
+ if (!from_it)
+ param *= 2;
+ break;
+ case 'W': effect = FX_GLOBALVOLSLIDE; break;
+ case 'X':
+ effect = FX_PANNING;
+ if (!from_it) {
+ if (param == 0xa4) {
+ effect = FX_SPECIAL;
+ param = 0x91;
+ } else if (param > 0x7f) {
+ param = 0xff;
+ } else {
+ param *= 2;
+ }
+ }
+ break;
+ case 'Y': effect = FX_PANBRELLO; break;
+ case 'Z': effect = FX_MIDI; break;
+ default: effect = 0;
+ }
+ m->effect = effect;
+ m->param = param;
+}
diff --git a/src/player/effects.c b/src/player/effects.c
new file mode 100644
index 0000000..93dc299
--- /dev/null
+++ b/src/player/effects.c
@@ -0,0 +1,2070 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sndfile.h"
+
+#include "cmixer.h"
+#include "snd_fm.h"
+#include "tables.h"
+
+#include "util.h" /* for clamp/min */
+
+#include <math.h>
+
+
+// see also csf_midi_out_note in sndmix.c
+void (*csf_midi_out_raw)(const unsigned char *,unsigned int, unsigned int) = NULL;
+
+/* --------------------------------------------------------------------------------------------------------- */
+/* note/freq/period conversion functions */
+
+int get_note_from_period(int period)
+{
+ int n;
+ if (!period)
+ return 0;
+ for (n = 0; n <= 120; n++) {
+ /* Essentially, this is just doing a note_to_period(n, 8363), but with less
+ computation since there's no c5speed to deal with. */
+ if (period >= (32 * period_table[n % 12] >> (n / 12)))
+ return n + 1;
+ }
+ return 120;
+}
+
+int get_period_from_note(int note, unsigned int c5speed, int linear)
+{
+ if (!note || note > 0xF0)
+ return 0;
+ note--;
+ if (linear)
+ return _muldiv(c5speed, linear_slide_up_table[(note % 12) * 16] << (note / 12), 65536 << 5);
+ else if (!c5speed)
+ return INT_MAX;
+ else
+ return _muldiv(8363, (period_table[note % 12] << 5), c5speed << (note / 12));
+}
+
+
+unsigned int transpose_to_frequency(int transp, int ftune)
+{
+ return (unsigned int) (8363.0 * pow(2, (transp * 128.0 + ftune) / 1536.0));
+}
+
+int frequency_to_transpose(unsigned int freq)
+{
+ return (int) (1536.0 * (log(freq / 8363.0) / log(2)));
+}
+
+
+unsigned long calc_halftone(unsigned long hz, int rel)
+{
+ return pow(2, rel / 12.0) * hz + 0.5;
+}
+
+/* --------------------------------------------------------------------------------------------------------- */
+/* the full content of snd_fx.cpp follows. */
+
+
+////////////////////////////////////////////////////////////
+// Channels effects
+
+void fx_note_cut(song_t *csf, uint32_t nchan, int clear_note)
+{
+ song_voice_t *chan = &csf->voices[nchan];
+ // stop the current note:
+ chan->flags |= CHN_FASTVOLRAMP;
+ chan->length = 0;
+ if (clear_note) {
+ // keep instrument numbers from picking up old notes
+ // (SCx doesn't do this)
+ chan->period = 0;
+ }
+ if (chan->flags & CHN_ADLIB) {
+ //Do this only if really an adlib chan. Important!
+ OPL_NoteOff(nchan);
+ OPL_Touch(nchan, NULL, 0);
+ }
+}
+
+void fx_key_off(song_t *csf, uint32_t nchan)
+{
+ song_voice_t *chan = &csf->voices[nchan];
+
+ /*fprintf(stderr, "KeyOff[%d] [ch%u]: flags=0x%X\n",
+ tick_count, (unsigned)nchan, chan->flags);*/
+ if (chan->flags & CHN_ADLIB) {
+ //Do this only if really an adlib chan. Important!
+ OPL_NoteOff(nchan);
+ }
+
+ song_instrument_t *penv = (csf->flags & SONG_INSTRUMENTMODE) ? chan->ptr_instrument : NULL;
+
+ /*if ((chan->flags & CHN_ADLIB)
+ || (penv && penv->midi_channel_mask))
+ {
+ // When in AdLib / MIDI mode, end the sample
+ chan->flags |= CHN_FASTVOLRAMP;
+ chan->length = 0;
+ chan->position = 0;
+ return;
+ }*/
+
+ chan->flags |= CHN_KEYOFF;
+ //if ((!chan->ptr_instrument) || (!(chan->flags & CHN_VOLENV)))
+ if ((csf->flags & SONG_INSTRUMENTMODE) && chan->ptr_instrument && !(chan->flags & CHN_VOLENV)) {
+ chan->flags |= CHN_NOTEFADE;
+ }
+ if (!chan->length)
+ return;
+ if ((chan->flags & CHN_SUSTAINLOOP) && chan->ptr_sample) {
+ song_sample_t *psmp = chan->ptr_sample;
+ if (psmp->flags & CHN_LOOP) {
+ if (psmp->flags & CHN_PINGPONGLOOP)
+ chan->flags |= CHN_PINGPONGLOOP;
+ else
+ chan->flags &= ~(CHN_PINGPONGLOOP|CHN_PINGPONGFLAG);
+ chan->flags |= CHN_LOOP;
+ chan->length = psmp->length;
+ chan->loop_start = psmp->loop_start;
+ chan->loop_end = psmp->loop_end;
+ if (chan->length > chan->loop_end) chan->length = chan->loop_end;
+ if (chan->position >= chan->length)
+ chan->position = chan->position - chan->length + chan->loop_start;
+ } else {
+ chan->flags &= ~(CHN_LOOP|CHN_PINGPONGLOOP|CHN_PINGPONGFLAG);
+ chan->length = psmp->length;
+ }
+ }
+ if (penv && penv->fadeout && (penv->flags & ENV_VOLLOOP))
+ chan->flags |= CHN_NOTEFADE;
+}
+
+
+// negative value for slide = up, positive = down
+static void fx_do_freq_slide(uint32_t flags, song_voice_t *chan, int32_t slide)
+{
+ // IT Linear slides
+ if (!chan->period) return;
+ if (flags & SONG_LINEARSLIDES) {
+ int32_t old_period = chan->period;
+ if (slide < 0) {
+ uint32_t n = (-slide) >> 2;
+ if (n > 255)
+ n = 255;
+ chan->period = _muldivr(chan->period, linear_slide_up_table[n], 65536);
+ if (old_period == chan->period)
+ chan->period++;
+ } else {
+ uint32_t n = (slide) >> 2;
+ if (n > 255)
+ n = 255;
+ chan->period = _muldivr(chan->period, linear_slide_down_table[n], 65536);
+ if (old_period == chan->period)
+ chan->period--;
+ }
+ } else {
+ chan->period += slide;
+ }
+}
+
+static void fx_fine_portamento_up(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if ((flags & SONG_FIRSTTICK) && chan->period && param) {
+ if (flags & SONG_LINEARSLIDES) {
+ chan->period = _muldivr(chan->period, linear_slide_up_table[param & 0x0F], 65536);
+ } else {
+ chan->period -= (int)(param * 4);
+ }
+ }
+}
+
+static void fx_fine_portamento_down(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if ((flags & SONG_FIRSTTICK) && chan->period && param) {
+ if (flags & SONG_LINEARSLIDES) {
+ chan->period = _muldivr(chan->period, linear_slide_down_table[param & 0x0F], 65536);
+ } else {
+ chan->period += (int)(param * 4);
+ }
+ }
+}
+
+static void fx_extra_fine_portamento_up(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if ((flags & SONG_FIRSTTICK) && chan->period && param) {
+ if (flags & SONG_LINEARSLIDES) {
+ chan->period = _muldivr(chan->period, fine_linear_slide_up_table[param & 0x0F], 65536);
+ } else {
+ chan->period -= (int)(param);
+ }
+ }
+}
+
+static void fx_extra_fine_portamento_down(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if ((flags & SONG_FIRSTTICK) && chan->period && param) {
+ if (flags & SONG_LINEARSLIDES) {
+ chan->period = _muldivr(chan->period, fine_linear_slide_down_table[param & 0x0F], 65536);
+ } else {
+ chan->period += (int)(param);
+ }
+ }
+}
+
+static void fx_reg_portamento_up(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if (!(flags & SONG_FIRSTTICK))
+ fx_do_freq_slide(flags, chan, -(int)(param * 4));
+}
+
+static void fx_reg_portamento_down(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if (!(flags & SONG_FIRSTTICK))
+ fx_do_freq_slide(flags, chan, (int)(param * 4));
+}
+
+
+static void fx_portamento_up(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if (!param)
+ param = chan->mem_pitchslide;
+
+ switch (param & 0xf0) {
+ case 0xe0:
+ fx_extra_fine_portamento_up(flags, chan, param & 0x0F);
+ break;
+ case 0xf0:
+ fx_fine_portamento_up(flags, chan, param & 0x0F);
+ break;
+ default:
+ fx_reg_portamento_up(flags, chan, param);
+ break;
+ }
+}
+
+static void fx_portamento_down(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ if (!param)
+ param = chan->mem_pitchslide;
+
+ switch (param & 0xf0) {
+ case 0xe0:
+ fx_extra_fine_portamento_down(flags, chan, param & 0x0F);
+ break;
+ case 0xf0:
+ fx_fine_portamento_down(flags, chan, param & 0x0F);
+ break;
+ default:
+ fx_reg_portamento_down(flags, chan, param);
+ break;
+ }
+}
+
+static void fx_tone_portamento(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ int delta;
+
+ if (!param)
+ param = chan->mem_portanote;
+
+ chan->flags |= CHN_PORTAMENTO;
+ if (chan->period && chan->portamento_target && !(flags & SONG_FIRSTTICK)) {
+ if (chan->period < chan->portamento_target) {
+ if (flags & SONG_LINEARSLIDES) {
+ uint32_t n = MIN(255, param);
+ delta = _muldivr(chan->period, linear_slide_up_table[n], 65536) - chan->period;
+ if (delta < 1) delta = 1;
+ } else {
+ delta = param * 4;
+ }
+ chan->period += delta;
+ if (chan->period > chan->portamento_target) {
+ chan->period = chan->portamento_target;
+ chan->portamento_target = 0;
+ }
+ } else if (chan->period > chan->portamento_target) {
+ if (flags & SONG_LINEARSLIDES) {
+ uint32_t n = MIN(255, param);
+ delta = _muldivr(chan->period, linear_slide_down_table[n], 65536) - chan->period;
+ if (delta > -1) delta = -1;
+ } else {
+ delta = -param * 4;
+ }
+ chan->period += delta;
+ if (chan->period < chan->portamento_target) {
+ chan->period = chan->portamento_target;
+ chan->portamento_target = 0;
+ }
+ }
+ }
+}
+
+// Implemented for IMF compatibility, can't actually save this in any formats
+// sign should be 1 (up) or -1 (down)
+static void fx_note_slide(uint32_t flags, song_voice_t *chan, uint32_t param, int sign)
+{
+ uint8_t x, y;
+ if (flags & SONG_FIRSTTICK) {
+ x = param & 0xf0;
+ if (x)
+ chan->note_slide_speed = (x >> 4);
+ y = param & 0xf;
+ if (y)
+ chan->note_slide_step = y;
+ chan->note_slide_counter = chan->note_slide_speed;
+ } else {
+ if (--chan->note_slide_counter == 0) {
+ chan->note_slide_counter = chan->note_slide_speed;
+ // update it
+ chan->period = get_period_from_note
+ (sign * chan->note_slide_step + get_note_from_period(chan->period),
+ 8363, 0);
+ }
+ }
+}
+
+
+
+static void fx_vibrato(song_voice_t *p, uint32_t param)
+{
+ if (param & 0x0F)
+ p->vibrato_depth = (param & 0x0F) * 4;
+ if (param & 0xF0)
+ p->vibrato_speed = (param >> 4) & 0x0F;
+ p->flags |= CHN_VIBRATO;
+}
+
+static void fx_fine_vibrato(song_voice_t *p, uint32_t param)
+{
+ if (param & 0x0F)
+ p->vibrato_depth = param & 0x0F;
+ if (param & 0xF0)
+ p->vibrato_speed = (param >> 4) & 0x0F;
+ p->flags |= CHN_VIBRATO;
+}
+
+
+static void fx_panbrello(song_voice_t *chan, uint32_t param)
+{
+ unsigned int panpos = chan->panbrello_position & 0xFF;
+ int pdelta;
+
+ if (param & 0x0F)
+ chan->panbrello_depth = param & 0x0F;
+ if (param & 0xF0)
+ chan->panbrello_speed = (param >> 4) & 0x0F;
+
+ switch (chan->panbrello_type) {
+ case VIB_SINE:
+ default:
+ pdelta = sine_table[panpos];
+ break;
+ case VIB_RAMP_DOWN:
+ pdelta = ramp_down_table[panpos];
+ break;
+ case VIB_SQUARE:
+ pdelta = square_table[panpos];
+ break;
+ case VIB_RANDOM:
+ pdelta = 128 * ((double) rand() / RAND_MAX) - 64;
+ break;
+ }
+
+ chan->panbrello_position += chan->panbrello_speed;
+ pdelta = ((pdelta * (int)chan->panbrello_depth) + 2) >> 3;
+ chan->panbrello_delta = pdelta;
+}
+
+
+static void fx_volume_up(song_voice_t *chan, uint32_t param)
+{
+ chan->volume += param * 4;
+ if (chan->volume > 256)
+ chan->volume = 256;
+}
+
+static void fx_volume_down(song_voice_t *chan, uint32_t param)
+{
+ chan->volume -= param * 4;
+ if (chan->volume < 0)
+ chan->volume = 0;
+}
+
+static void fx_volume_slide(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ // Dxx Volume slide down
+ //
+ // if (xx == 0) then xx = last xx for (Dxx/Kxx/Lxx) for this channel.
+ if (param)
+ chan->mem_volslide = param;
+ else
+ param = chan->mem_volslide;
+
+ // Order of testing: Dx0, D0x, DxF, DFx
+ if (param == (param & 0xf0)) {
+ // Dx0 Set effect update for channel enabled if channel is ON.
+ // If x = F, then slide up volume by 15 straight away also (for S3M compat)
+ // Every update, add x to the volume, check and clip values > 64 to 64
+ param >>= 4;
+ if (param == 0xf || !(flags & SONG_FIRSTTICK))
+ fx_volume_up(chan, param);
+ } else if (param == (param & 0xf)) {
+ // D0x Set effect update for channel enabled if channel is ON.
+ // If x = F, then slide down volume by 15 straight away also (for S3M)
+ // Every update, subtract x from the volume, check and clip values < 0 to 0
+ if (param == 0xf || !(flags & SONG_FIRSTTICK))
+ fx_volume_down(chan, param);
+ } else if ((param & 0xf) == 0xf) {
+ // DxF Add x to volume straight away. Check and clip values > 64 to 64
+ param >>= 4;
+ if (flags & SONG_FIRSTTICK)
+ fx_volume_up(chan, param);
+ } else if ((param & 0xf0) == 0xf0) {
+ // DFx Subtract x from volume straight away. Check and clip values < 0 to 0
+ param &= 0xf;
+ if (flags & SONG_FIRSTTICK)
+ fx_volume_down(chan, param);
+ }
+}
+
+
+static void fx_panning_slide(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ int32_t slide = 0;
+ if (param)
+ chan->mem_panslide = param;
+ else
+ param = chan->mem_panslide;
+ if ((param & 0x0F) == 0x0F && (param & 0xF0)) {
+ if (flags & SONG_FIRSTTICK) {
+ param = (param & 0xF0) >> 2;
+ slide = - (int)param;
+ }
+ } else if ((param & 0xF0) == 0xF0 && (param & 0x0F)) {
+ if (flags & SONG_FIRSTTICK) {
+ slide = (param & 0x0F) << 2;
+ }
+ } else {
+ if (!(flags & SONG_FIRSTTICK)) {
+ if (param & 0x0F)
+ slide = (int)((param & 0x0F) << 2);
+ else
+ slide = -(int)((param & 0xF0) >> 2);
+ }
+ }
+ if (slide) {
+ slide += chan->panning;
+ chan->panning = CLAMP(slide, 0, 256);
+ }
+ chan->flags &= ~CHN_SURROUND;
+ chan->panbrello_delta = 0;
+}
+
+
+static void fx_tremolo(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ unsigned int trempos = chan->tremolo_position & 0xFF;
+ int tdelta;
+
+ if (param & 0x0F)
+ chan->tremolo_depth = (param & 0x0F) << 2;
+ if (param & 0xF0)
+ chan->tremolo_speed = (param >> 4) & 0x0F;
+
+ chan->flags |= CHN_TREMOLO;
+
+ // don't handle on first tick if old-effects mode
+ if ((flags & SONG_FIRSTTICK) && (flags & SONG_ITOLDEFFECTS))
+ return;
+
+ switch (chan->tremolo_type) {
+ case VIB_SINE:
+ default:
+ tdelta = sine_table[trempos];
+ break;
+ case VIB_RAMP_DOWN:
+ tdelta = ramp_down_table[trempos];
+ break;
+ case VIB_SQUARE:
+ tdelta = square_table[trempos];
+ break;
+ case VIB_RANDOM:
+ tdelta = 128 * ((double) rand() / RAND_MAX) - 64;
+ break;
+ }
+
+ chan->tremolo_position = (trempos + 4 * chan->tremolo_speed) & 0xFF;
+ tdelta = (tdelta * (int)chan->tremolo_depth) >> 5;
+ chan->tremolo_delta = tdelta;
+}
+
+
+static void fx_retrig_note(song_t *csf, uint32_t nchan, uint32_t param)
+{
+ song_voice_t *chan = &csf->voices[nchan];
+
+ //printf("Q%02X note=%02X tick%d %d\n", param, chan->row_note, tick_count, chan->cd_retrig);
+ if ((csf->flags & SONG_FIRSTTICK) && chan->row_note != NOTE_NONE) {
+ chan->cd_retrig = param & 0xf;
+ } else if (--chan->cd_retrig <= 0) {
+
+ // in Impulse Tracker, retrig only works if a sample is currently playing in the channel
+ if (chan->position == 0)
+ return;
+
+ chan->cd_retrig = param & 0xf;
+ param >>= 4;
+ if (param) {
+ int vol = chan->volume;
+ if (retrig_table_1[param])
+ vol = (vol * retrig_table_1[param]) >> 4;
+ else
+ vol += (retrig_table_2[param]) << 2;
+ chan->volume = CLAMP(vol, 0, 256);
+ chan->flags |= CHN_FASTVOLRAMP;
+ }
+
+ uint32_t note = chan->new_note;
+ int32_t period = chan->period;
+ if (NOTE_IS_NOTE(note) && chan->length)
+ csf_check_nna(csf, nchan, 0, note, 1);
+ csf_note_change(csf, nchan, note, 1, 1, 0);
+ if (period && chan->row_note == NOTE_NONE)
+ chan->period = period;
+ chan->position = chan->position_frac = 0;
+ }
+}
+
+
+static void fx_channel_vol_slide(uint32_t flags, song_voice_t *chan, uint32_t param)
+{
+ int32_t slide = 0;
+ if (param)
+ chan->mem_channel_volslide = param;
+ else
+ param = chan->mem_channel_volslide;
+ if ((param & 0x0F) == 0x0F && (param & 0xF0)) {
+ if (flags & SONG_FIRSTTICK)
+ slide = param >> 4;
+ } else if ((param & 0xF0) == 0xF0 && (param & 0x0F)) {
+ if (flags & SONG_FIRSTTICK)
+ slide = - (int)(param & 0x0F);
+ } else {
+ if (!(flags & SONG_FIRSTTICK)) {
+ if (param & 0x0F)
+ slide = -(int)(param & 0x0F);
+ else
+ slide = (int)((param & 0xF0) >> 4);
+ }
+ }
+ if (slide) {
+ slide += chan->global_volume;
+ chan->global_volume = CLAMP(slide, 0, 64);
+ }
+}
+
+
+static void fx_global_vol_slide(song_t *csf, song_voice_t *chan, uint32_t param)
+{
+ int32_t slide = 0;
+ if (param)
+ chan->mem_global_volslide = param;
+ else
+ param = chan->mem_global_volslide;
+ if ((param & 0x0F) == 0x0F && (param & 0xF0)) {
+ if (csf->flags & SONG_FIRSTTICK)
+ slide = param >> 4;
+ } else if ((param & 0xF0) == 0xF0 && (param & 0x0F)) {
+ if (csf->flags & SONG_FIRSTTICK)
+ slide = -(int)(param & 0x0F);
+ } else {
+ if (!(csf->flags & SONG_FIRSTTICK)) {
+ if (param & 0xF0)
+ slide = (int)((param & 0xF0) >> 4);
+ else
+ slide = -(int)(param & 0x0F);
+ }
+ }
+ if (slide) {
+ slide += csf->current_global_volume;
+ csf->current_global_volume = CLAMP(slide, 0, 128);
+ }
+}
+
+
+static void fx_pattern_loop(song_t *csf, song_voice_t *chan, uint32_t param)
+{
+ if (param) {
+ if (chan->cd_patloop) {
+ if (!--chan->cd_patloop) {
+ // this should get rid of that nasty infinite loop for cases like
+ // ... .. .. SB0
+ // ... .. .. SB1
+ // ... .. .. SB1
+ // it still doesn't work right in a few strange cases, but oh well :P
+ chan->patloop_row = csf->row + 1;
+ return; // don't loop!
+ }
+ } else {
+ chan->cd_patloop = param;
+ }
+ csf->process_row = chan->patloop_row - 1;
+ } else {
+ chan->patloop_row = csf->row;
+ }
+}
+
+
+static void fx_special(song_t *csf, uint32_t nchan, uint32_t param)
+{
+ song_voice_t *chan = &csf->voices[nchan];
+ uint32_t command = param & 0xF0;
+ param &= 0x0F;
+ switch(command) {
+ // S0x: Set Filter
+ // S1x: Set Glissando Control
+ case 0x10:
+ chan->flags &= ~CHN_GLISSANDO;
+ if (param) chan->flags |= CHN_GLISSANDO;
+ break;
+ // S2x: Set FineTune (no longer implemented)
+ // S3x: Set Vibrato WaveForm
+ case 0x30:
+ chan->vib_type = param;
+ break;
+ // S4x: Set Tremolo WaveForm
+ case 0x40:
+ chan->tremolo_type = param;
+ break;
+ // S5x: Set Panbrello WaveForm
+ case 0x50:
+ chan->panbrello_type = param;
+ break;
+ // S6x: Pattern Delay for x ticks
+ case 0x60:
+ if (csf->flags & SONG_FIRSTTICK)
+ csf->tick_count += param;
+ break;
+ // S7x: Envelope Control
+ case 0x70:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ switch(param) {
+ case 0:
+ case 1:
+ case 2:
+ {
+ song_voice_t *bkp = &csf->voices[MAX_CHANNELS];
+ for (uint32_t i=MAX_CHANNELS; i<MAX_VOICES; i++, bkp++) {
+ if (bkp->master_channel == nchan+1) {
+ if (param == 1) {
+ fx_key_off(csf, i);
+ } else if (param == 2) {
+ bkp->flags |= CHN_NOTEFADE;
+ } else {
+ bkp->flags |= CHN_NOTEFADE;
+ bkp->fadeout_volume = 0;
+ }
+ }
+ }
+ }
+ break;
+ case 3: chan->nna = NNA_NOTECUT; break;
+ case 4: chan->nna = NNA_CONTINUE; break;
+ case 5: chan->nna = NNA_NOTEOFF; break;
+ case 6: chan->nna = NNA_NOTEFADE; break;
+ case 7: chan->flags &= ~CHN_VOLENV; break;
+ case 8: chan->flags |= CHN_VOLENV; break;
+ case 9: chan->flags &= ~CHN_PANENV; break;
+ case 10: chan->flags |= CHN_PANENV; break;
+ case 11: chan->flags &= ~CHN_PITCHENV; break;
+ case 12: chan->flags |= CHN_PITCHENV; break;
+ }
+ break;
+ // S8x: Set 4-bit Panning
+ case 0x80:
+ if (csf->flags & SONG_FIRSTTICK) {
+ chan->flags &= ~CHN_SURROUND;
+ chan->panbrello_delta = 0;
+ chan->panning = (param << 4) + 8;
+ chan->flags |= CHN_FASTVOLRAMP;
+ chan->pan_swing = 0;
+ }
+ break;
+ // S9x: Set Surround
+ case 0x90:
+ if (param == 1 && (csf->flags & SONG_FIRSTTICK)) {
+ chan->flags |= CHN_SURROUND;
+ chan->panbrello_delta = 0;
+ chan->panning = 128;
+ }
+ break;
+ // SAx: Set 64k Offset
+ // Note: don't actually APPLY the offset, and don't clear the regular offset value, either.
+ case 0xA0:
+ if (csf->flags & SONG_FIRSTTICK) {
+ chan->mem_offset = (param << 16) | (chan->mem_offset & ~0xf0000);
+ }
+ break;
+ // SBx: Pattern Loop
+ case 0xB0:
+ if (csf->flags & SONG_FIRSTTICK)
+ fx_pattern_loop(csf, chan, param & 0x0F);
+ break;
+ // SCx: Note Cut
+ case 0xC0:
+ if (csf->flags & SONG_FIRSTTICK)
+ chan->cd_note_cut = param ?: 1;
+ else if (--chan->cd_note_cut == 0)
+ fx_note_cut(csf, nchan, 0);
+ break;
+ // SDx: Note Delay
+ // SEx: Pattern Delay for x rows
+ case 0xE0:
+ if (csf->flags & SONG_FIRSTTICK) {
+ if (!csf->row_count) // ugh!
+ csf->row_count = param + 1;
+ }
+ break;
+ // SFx: Set Active Midi Macro
+ case 0xF0:
+ chan->active_macro = param;
+ break;
+ }
+}
+
+
+// this is all brisby
+void csf_midi_send(song_t *csf, const unsigned char *data, unsigned int len, uint32_t nchan, int fake)
+{
+ song_voice_t *chan = &csf->voices[nchan];
+ int oldcutoff;
+ const unsigned char *idata = data;
+ unsigned int ilen = len;
+
+ while (ilen > 4 && idata[0] == 0xF0 && idata[1] == 0xF0) {
+ // impulse tracker filter control (mfg. 0xF0)
+ switch (idata[2]) {
+ case 0x00: // set cutoff
+ oldcutoff = chan->cutoff;
+ if (idata[3] < 0x80)
+ chan->cutoff = idata[3];
+ oldcutoff -= chan->cutoff;
+ if (oldcutoff < 0)
+ oldcutoff = -oldcutoff;
+ if (chan->volume > 0 || oldcutoff < 0x10
+ || !(chan->flags & CHN_FILTER)
+ || !(chan->left_volume|chan->right_volume)) {
+ setup_channel_filter(chan, !(chan->flags & CHN_FILTER), 256, csf->mix_frequency);
+ }
+ break;
+ case 0x01: // set resonance
+ if (idata[3] < 0x80)
+ chan->resonance = idata[3];
+ setup_channel_filter(chan, !(chan->flags & CHN_FILTER), 256, csf->mix_frequency);
+ break;
+ }
+ idata += 4;
+ ilen -= 4;
+ }
+
+ if (!fake && csf_midi_out_raw) {
+ /* okay, this is kind of how it works.
+ we pass buffer_count as here because while
+ 1000 * ((8((buffer_size/2) - buffer_count)) / sample_rate)
+ is the number of msec we need to delay by, libmodplug simply doesn't know
+ what the buffer size is at this point so buffer_count simply has no
+ frame of reference.
+
+ fortunately, schism does and can complete this (tags: _schism_midi_out_raw )
+
+ */
+ csf_midi_out_raw(data, len, csf->buffer_count);
+ }
+}
+
+
+static int _was_complete_midi(unsigned char *q, unsigned int len, int nextc)
+{
+ if (len == 0) return 0;
+ if (*q == 0xF0) return (q[len-1] == 0xF7 ? 1 : 0);
+ return ((nextc & 0x80) ? 1 : 0);
+}
+
+void csf_process_midi_macro(song_t *csf, uint32_t nchan, const char * macro, uint32_t param,
+ uint32_t note, uint32_t velocity, uint32_t use_instr)
+{
+/* this was all wrong. -mrsb */
+ song_voice_t *chan = &csf->voices[nchan];
+ song_instrument_t *penv = ((csf->flags & SONG_INSTRUMENTMODE)
+ && chan->last_instrument < MAX_INSTRUMENTS)
+ ? csf->instruments[use_instr ?: chan->last_instrument]
+ : NULL;
+ unsigned char outbuffer[64];
+ unsigned char cx;
+ int mc, fake = 0;
+ int saw_c;
+ int i, j, x;
+
+ saw_c = 0;
+ if (!penv || penv->midi_channel_mask == 0) {
+ /* okay, there _IS_ no real midi channel. forget this for now... */
+ mc = 15;
+ fake = 1;
+
+ } else if (penv->midi_channel_mask >= 0x10000) {
+ mc = (nchan-1) % 16;
+ } else {
+ mc = 0;
+ while(!(penv->midi_channel_mask & (1 << mc))) ++mc;
+ }
+
+ for (i = j = x = 0, cx =0; i <= 32 && macro[i]; i++) {
+ int c, cw;
+ if (macro[i] >= '0' && macro[i] <= '9') {
+ c = macro[i] - '0';
+ cw = 1;
+ } else if (macro[i] >= 'A' && macro[i] <= 'F') {
+ c = (macro[i] - 'A') + 10;
+ cw = 1;
+ } else if (macro[i] == 'c') {
+ c = mc;
+ cw = 1;
+ saw_c = 1;
+ } else if (macro[i] == 'n') {
+ c = (note-1);
+ cw = 2;
+ } else if (macro[i] == 'v') {
+ c = velocity;
+ cw = 2;
+ } else if (macro[i] == 'u') {
+ c = (chan->volume >> 1);
+ if (c > 127) c = 127;
+ cw = 2;
+ } else if (macro[i] == 'x') {
+ c = chan->panning;
+ if (c > 127) c = 127;
+ cw = 2;
+ } else if (macro[i] == 'y') {
+ c = chan->final_panning;
+ if (c > 127) c = 127;
+ cw = 2;
+ } else if (macro[i] == 'a') {
+ if (!penv)
+ c = 0;
+ else
+ c = (penv->midi_bank >> 7) & 127;
+ cw = 2;
+ } else if (macro[i] == 'b') {
+ if (!penv)
+ c = 0;
+ else
+ c = penv->midi_bank & 127;
+ cw = 2;
+ } else if (macro[i] == 'z' || macro[i] == 'p') {
+ c = param & 0x7F;
+ cw = 2;
+ } else {
+ continue;
+ }
+ if (j == 0 && cw == 1) {
+ cx = c;
+ j = 1;
+ continue;
+ } else if (j == 1 && cw == 1) {
+ cx = (cx << 4) | c;
+ j = 0;
+ } else if (j == 0) {
+ cx = c;
+ } else if (j == 1) {
+ outbuffer[x] = cx;
+ x++;
+
+ cx = c;
+ j = 0;
+ }
+ // start of midi message
+ if (_was_complete_midi(outbuffer, x, cx)) {
+ csf_midi_send(csf, outbuffer, x, nchan, saw_c && fake);
+ x = 0;
+ }
+ outbuffer[x] = cx;
+ x++;
+ }
+ if (j == 1) {
+ outbuffer[x] = cx;
+ x++;
+ }
+ if (x) {
+ // terminate sysex
+ if (!_was_complete_midi(outbuffer, x, 0xFF)) {
+ if (*outbuffer == 0xF0) {
+ outbuffer[x] = 0xF7;
+ x++;
+ }
+ }
+ csf_midi_send(csf, outbuffer, x, nchan, saw_c && fake);
+ }
+}
+
+
+////////////////////////////////////////////////////////////
+// Length
+
+#if MAX_CHANNELS != 64
+# error csf_get_length assumes 64 channels
+#endif
+
+unsigned int csf_get_length(song_t *csf)
+{
+ uint32_t elapsed = 0, row = 0, next_row = 0, cur_order = 0, next_order = 0, pat = csf->orderlist[0],
+ speed = csf->initial_speed, tempo = csf->initial_tempo, psize, n;
+ uint32_t patloop[MAX_CHANNELS] = {0};
+ uint8_t mem_tempo[MAX_CHANNELS] = {0};
+ uint64_t setloop = 0; // bitmask
+ const song_note_t *pdata;
+
+ for (;;) {
+ uint32_t speed_count = 0;
+ row = next_row;
+ cur_order = next_order;
+
+ // Check if pattern is valid
+ pat = csf->orderlist[cur_order];
+ while (pat >= MAX_PATTERNS) {
+ // End of song ?
+ if (pat == ORDER_LAST || cur_order >= MAX_ORDERS) {
+ pat = ORDER_LAST; // cause break from outer loop too
+ break;
+ } else {
+ cur_order++;
+ pat = (cur_order < MAX_ORDERS) ? csf->orderlist[cur_order] : ORDER_LAST;
+ }
+ next_order = cur_order;
+ }
+ // Weird stuff?
+ if (pat >= MAX_PATTERNS)
+ break;
+ pdata = csf->patterns[pat];
+ if (pdata) {
+ psize = csf->pattern_size[pat];
+ } else {
+ pdata = blank_pattern;
+ psize = 64;
+ }
+ // guard against Cxx to invalid row, etc.
+ if (row >= psize)
+ row = 0;
+ // Update next position
+ next_row = row + 1;
+ if (next_row >= psize) {
+ next_order = cur_order + 1;
+ next_row = 0;
+ }
+
+ /* muahahaha */
+ if (csf->stop_at_order > -1 && csf->stop_at_row > -1) {
+ if (csf->stop_at_order <= (signed) cur_order && csf->stop_at_row <= (signed) row)
+ break;
+ if (csf->stop_at_time > 0) {
+ /* stupid api decision */
+ if (((elapsed + 500) / 1000) >= csf->stop_at_time) {
+ csf->stop_at_order = cur_order;
+ csf->stop_at_row = row;
+ break;
+ }
+ }
+ }
+
+ /* This is nasty, but it fixes inaccuracies with SB0 SB1 SB1. (Simultaneous
+ loops in multiple channels are still wildly incorrect, though.) */
+ if (!row)
+ setloop = ~0;
+ if (setloop) {
+ for (n = 0; n < MAX_CHANNELS; n++)
+ if (setloop & (1 << n))
+ patloop[n] = elapsed;
+ setloop = 0;
+ }
+ const song_note_t *note = pdata + row * MAX_CHANNELS;
+ for (n = 0; n < MAX_CHANNELS; note++, n++) {
+ uint32_t param = note->param;
+ switch (note->effect) {
+ case FX_NONE:
+ break;
+ case FX_POSITIONJUMP:
+ next_order = param > cur_order ? param : cur_order + 1;
+ next_row = 0;
+ break;
+ case FX_PATTERNBREAK:
+ next_order = cur_order + 1;
+ next_row = param;
+ break;
+ case FX_SPEED:
+ if (param)
+ speed = param;
+ break;
+ case FX_TEMPO:
+ if (param)
+ mem_tempo[n] = param;
+ else
+ param = mem_tempo[n];
+ int d = (param & 0xf);
+ switch (param >> 4) {
+ default:
+ tempo = param;
+ break;
+ case 0:
+ d = -d;
+ case 1:
+ d = d * (speed - 1) + tempo;
+ tempo = CLAMP(d, 32, 255);
+ break;
+ }
+ break;
+ case FX_SPECIAL:
+ switch (param >> 4) {
+ case 0x6:
+ speed_count = param & 0x0F;
+ break;
+ case 0xb:
+ if (param & 0x0F) {
+ elapsed += (elapsed - patloop[n]) * (param & 0x0F);
+ patloop[n] = 0xffffffff;
+ setloop = 1;
+ } else {
+ patloop[n] = elapsed;
+ }
+ break;
+ case 0xe:
+ speed_count = (param & 0x0F) * speed;
+ break;
+ }
+ break;
+ }
+ }
+ // sec/tick = 5 / (2 * tempo)
+ // msec/tick = 5000 / (2 * tempo)
+ // = 2500 / tempo
+ elapsed += (speed + speed_count) * 2500 / tempo;
+ }
+
+ return (elapsed + 500) / 1000;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Effects
+
+song_sample_t *csf_translate_keyboard(song_t *csf, song_instrument_t *penv, uint32_t note, song_sample_t *def)
+{
+ uint32_t n = penv->sample_map[note - 1];
+ return (n && n < MAX_SAMPLES) ? &csf->samples[n] : def;
+}
+
+static void env_reset(song_voice_t *chan, int always)
+{
+ if (chan->ptr_instrument) {
+ chan->flags |= CHN_FASTVOLRAMP;
+ if (always) {
+ chan->vol_env_position = 0;
+ chan->pan_env_position = 0;
+ chan->pitch_env_position = 0;
+ } else {
+ /* only reset envelopes with carry off */
+ if (!(chan->ptr_instrument->flags & ENV_VOLCARRY))
+ chan->vol_env_position = 0;
+ if (!(chan->ptr_instrument->flags & ENV_PANCARRY))
+ chan->pan_env_position = 0;
+ if (!(chan->ptr_instrument->flags & ENV_PITCHCARRY))
+ chan->pitch_env_position = 0;
+ }
+ }
+
+ // this was migrated from csf_note_change, should it be here?
+ chan->flags &= ~CHN_NOTEFADE;
+ chan->fadeout_volume = 65536;
+}
+
+void csf_instrument_change(song_t *csf, song_voice_t *chan, uint32_t instr, int porta, int inst_column)
+{
+ int inst_changed = 0;
+
+ if (instr >= MAX_INSTRUMENTS) return;
+ song_instrument_t *penv = (csf->flags & SONG_INSTRUMENTMODE) ? csf->instruments[instr] : NULL;
+ song_sample_t *psmp = chan->ptr_sample;
+ uint32_t note = chan->new_note;
+
+ if (note == NOTE_NONE) {
+ /* nothing to see here */
+ } else if (NOTE_IS_CONTROL(note)) {
+ /* nothing here either */
+ } else if (penv) {
+ if (NOTE_IS_CONTROL(penv->note_map[note-1]))
+ return;
+ if (!(porta && penv == chan->ptr_instrument && chan->ptr_sample && chan->current_sample_data))
+ psmp = csf_translate_keyboard(csf, penv, note, NULL);
+ chan->flags &= ~CHN_SUSTAINLOOP; // turn off sustain
+ } else {
+ psmp = csf->samples + instr;
+ }
+
+ // Update Volume
+ if (inst_column && psmp) chan->volume = psmp->volume;
+ // inst_changed is used for IT carry-on env option
+ if (penv != chan->ptr_instrument || !chan->current_sample_data) {
+ inst_changed = 1;
+ chan->ptr_instrument = penv;
+ }
+
+ // Instrument adjust
+ chan->new_instrument = 0;
+ if (psmp) {
+ psmp->played = 1;
+ if (penv) {
+ penv->played = 1;
+ chan->instrument_volume = (psmp->global_volume * penv->global_volume) >> 7;
+ if (penv->flags & ENV_SETPANNING) {
+ chan->panning = penv->panning;
+ chan->flags &= ~CHN_SURROUND;
+ }
+ chan->nna = penv->nna;
+ } else {
+ chan->instrument_volume = psmp->global_volume;
+ }
+ if (psmp->flags & CHN_PANNING) {
+ chan->panning = psmp->panning;
+ chan->flags &= ~CHN_SURROUND;
+ }
+ }
+
+ // Reset envelopes
+
+ // Conditions experimentally determined to cause envelope reset in Impulse Tracker:
+ // - no note currently playing (of course)
+ // - note given, no portamento
+ // - instrument number given, portamento, compat gxx enabled
+ // - instrument number given, no portamento, after keyoff, old effects enabled
+ // If someone can enlighten me to what the logic really is here, I'd appreciate it.
+ // Seems like it's just a total mess though, probably to get XMs to play right.
+ if (penv) {
+ if ((
+ !chan->length
+ ) || (
+ inst_column
+ && porta
+ && (csf->flags & SONG_COMPATGXX)
+ ) || (
+ inst_column
+ && !porta
+ && (chan->flags & (CHN_NOTEFADE|CHN_KEYOFF))
+ && (csf->flags & SONG_ITOLDEFFECTS)
+ )) {
+ env_reset(chan, inst_changed || (chan->flags & CHN_KEYOFF));
+ } else if (!(penv->flags & ENV_VOLUME)) {
+ // XXX why is this being done?
+ chan->vol_env_position = 0;
+ }
+
+ chan->vol_swing = chan->pan_swing = 0;
+ if (penv->vol_swing) {
+ /* this was wrong, and then it was still wrong.
+ (possibly it continues to be wrong even now?) */
+ double d = 2 * (((double) rand()) / RAND_MAX) - 1;
+ // floor() is applied to get exactly the same volume levels as in IT. -- Saga
+ chan->vol_swing = floor(d * penv->vol_swing / 100.0 * chan->instrument_volume);
+ }
+ if (penv->pan_swing) {
+ /* this was also wrong, and even more so */
+ double d = 2 * (((double) rand()) / RAND_MAX) - 1;
+ chan->pan_swing = d * penv->pan_swing * 4;
+ }
+ }
+
+ // Invalid sample ?
+ if (!psmp) {
+ chan->ptr_sample = NULL;
+ chan->instrument_volume = 0;
+ return;
+ }
+ if (psmp == chan->ptr_sample && chan->current_sample_data && chan->length)
+ return;
+
+ // sample change: reset sample vibrato
+ chan->autovib_depth = 0;
+ chan->autovib_position = 0;
+
+ if ((chan->flags & (CHN_KEYOFF | CHN_NOTEFADE)) && inst_column) {
+ // Don't start new notes after ===/~~~
+ chan->period = 0;
+ } else {
+ chan->period = get_period_from_note(note, psmp->c5speed,
+ csf->flags & SONG_LINEARSLIDES);
+ }
+ chan->flags &= ~(CHN_SAMPLE_FLAGS | CHN_KEYOFF | CHN_NOTEFADE
+ | CHN_VOLENV | CHN_PANENV | CHN_PITCHENV);
+ chan->flags |= psmp->flags & CHN_SAMPLE_FLAGS;
+ if (penv) {
+ if (penv->flags & ENV_VOLUME)
+ chan->flags |= CHN_VOLENV;
+ if (penv->flags & ENV_PANNING)
+ chan->flags |= CHN_PANENV;
+ if (penv->flags & ENV_PITCH)
+ chan->flags |= CHN_PITCHENV;
+ if ((penv->flags & ENV_PITCH) && (penv->flags & ENV_FILTER) && !chan->cutoff)
+ chan->cutoff = 0x7F;
+ if (penv->ifc & 0x80)
+ chan->cutoff = penv->ifc & 0x7F;
+ if (penv->ifr & 0x80)
+ chan->resonance = penv->ifr & 0x7F;
+ }
+
+ chan->ptr_sample = psmp;
+ chan->length = psmp->length;
+ chan->loop_start = psmp->loop_start;
+ chan->loop_end = psmp->loop_end;
+ chan->c5speed = psmp->c5speed;
+ chan->current_sample_data = psmp->data;
+ chan->position = 0;
+
+ if (chan->flags & CHN_SUSTAINLOOP) {
+ chan->loop_start = psmp->sustain_start;
+ chan->loop_end = psmp->sustain_end;
+ chan->flags |= CHN_LOOP;
+ if (chan->flags & CHN_PINGPONGSUSTAIN)
+ chan->flags |= CHN_PINGPONGLOOP;
+ }
+ if ((chan->flags & CHN_LOOP) && chan->loop_end < chan->length)
+ chan->length = chan->loop_end;
+ /*fprintf(stderr, "length set as %d (from %d), ch flags %X smp flags %X\n",
+ (int)chan->length,
+ (int)psmp->length, chan->flags, psmp->flags);*/
+}
+
+
+// have_inst is a hack to ignore the note-sample map when no instrument number is present
+void csf_note_change(song_t *csf, uint32_t nchan, int note, int porta, int retrig, int have_inst)
+{
+ // why would csf_note_change ever get a negative value for 'note'?
+ if (note == NOTE_NONE || note < 0)
+ return;
+
+ // save the note that's actually used, as it's necessary to properly calculate PPS and stuff
+ // (and also needed for correct display of note dots)
+ int truenote = note;
+
+ song_voice_t *chan = &csf->voices[nchan];
+ song_sample_t *pins = chan->ptr_sample;
+ song_instrument_t *penv = (csf->flags & SONG_INSTRUMENTMODE) ? chan->ptr_instrument : NULL;
+ if (penv && NOTE_IS_NOTE(note)) {
+ if (!(have_inst && porta && pins))
+ pins = csf_translate_keyboard(csf, penv, note, pins);
+ note = penv->note_map[note - 1];
+ chan->flags &= ~CHN_SUSTAINLOOP; // turn off sustain
+ }
+
+ if (NOTE_IS_CONTROL(note)) {
+ // hax: keep random sample numbers from triggering notes (see csf_instrument_change)
+ // NOTE_OFF is a completely arbitrary choice - this could be anything above NOTE_LAST
+ chan->new_note = NOTE_OFF;
+ switch (note) {
+ case NOTE_OFF:
+ fx_key_off(csf, nchan);
+ break;
+ case NOTE_CUT:
+ fx_note_cut(csf, nchan, 1);
+ break;
+ case NOTE_FADE:
+ default: // Impulse Tracker handles all unknown notes as fade internally
+ chan->flags |= CHN_NOTEFADE;
+ break;
+ }
+ return;
+ }
+
+ if (!pins)
+ return;
+
+ note = CLAMP(note, NOTE_FIRST, NOTE_LAST);
+ chan->note = CLAMP(truenote, NOTE_FIRST, NOTE_LAST);
+ chan->new_instrument = 0;
+ uint32_t period = get_period_from_note(note, chan->c5speed, csf->flags & SONG_LINEARSLIDES);
+ if (period) {
+ if (porta && chan->period) {
+ chan->portamento_target = period;
+ } else {
+ chan->portamento_target = 0;
+ chan->period = period;
+ }
+ if (!porta || !chan->length) {
+ chan->ptr_sample = pins;
+ chan->current_sample_data = pins->data;
+ chan->length = pins->length;
+ chan->loop_end = pins->length;
+ chan->loop_start = 0;
+ chan->c5speed = pins->c5speed;
+ chan->flags = (chan->flags & ~CHN_SAMPLE_FLAGS) | (pins->flags & CHN_SAMPLE_FLAGS);
+ if (chan->flags & CHN_SUSTAINLOOP) {
+ chan->loop_start = pins->sustain_start;
+ chan->loop_end = pins->sustain_end;
+ chan->flags &= ~CHN_PINGPONGLOOP;
+ chan->flags |= CHN_LOOP;
+ if (chan->flags & CHN_PINGPONGSUSTAIN) chan->flags |= CHN_PINGPONGLOOP;
+ if (chan->length > chan->loop_end) chan->length = chan->loop_end;
+ } else if (chan->flags & CHN_LOOP) {
+ chan->loop_start = pins->loop_start;
+ chan->loop_end = pins->loop_end;
+ if (chan->length > chan->loop_end) chan->length = chan->loop_end;
+ }
+ chan->position = chan->position_frac = 0;
+ }
+ if (chan->position >= chan->length)
+ chan->position = chan->loop_start;
+ } else {
+ porta = 0;
+ }
+
+ if (!porta)
+ env_reset(chan, 0);
+
+ chan->flags &= ~CHN_KEYOFF;
+ // Enable Ramping
+ if (!porta) {
+ chan->vu_meter = 0x0;
+ chan->strike = 4; /* this affects how long the initial hit on the playback marks lasts (bigger dot in instrument and sample list windows)*/
+ chan->flags &= ~CHN_FILTER;
+ chan->flags |= CHN_FASTVOLRAMP;
+ if (!retrig) {
+ chan->autovib_depth = 0;
+ chan->autovib_position = 0;
+ chan->vibrato_position = 0;
+ }
+ chan->left_volume = chan->right_volume = 0;
+ // Setup Initial Filter for this note
+ if (penv) {
+ if (penv->ifr & 0x80)
+ chan->resonance = penv->ifr & 0x7F;
+ if (penv->ifc & 0x80)
+ chan->cutoff = penv->ifc & 0x7F;
+ } else {
+ chan->vol_swing = chan->pan_swing = 0;
+ }
+
+ if (chan->cutoff < 0x7F)
+ setup_channel_filter(chan, 1, 256, csf->mix_frequency);
+ }
+}
+
+
+uint32_t csf_get_nna_channel(song_t *csf, uint32_t nchan)
+{
+ song_voice_t *chan = &csf->voices[nchan];
+ // Check for empty channel
+ song_voice_t *pi = &csf->voices[MAX_CHANNELS];
+ for (uint32_t i=MAX_CHANNELS; i<MAX_VOICES; i++, pi++) {
+ if (!pi->length) {
+ if (pi->flags & CHN_MUTE) {
+ if (pi->flags & CHN_NNAMUTE) {
+ pi->flags &= ~(CHN_NNAMUTE|CHN_MUTE);
+ } else {
+ /* this channel is muted; skip */
+ continue;
+ }
+ }
+ return i;
+ }
+ }
+ if (!chan->fadeout_volume) return 0;
+ // All channels are used: check for lowest volume
+ uint32_t result = 0;
+ uint32_t vol = 64*65536; // 25%
+ int envpos = 0xFFFFFF;
+ const song_voice_t *pj = &csf->voices[MAX_CHANNELS];
+ for (uint32_t j=MAX_CHANNELS; j<MAX_VOICES; j++, pj++) {
+ if (!pj->fadeout_volume) return j;
+ uint32_t v = pj->volume;
+ if (pj->flags & CHN_NOTEFADE)
+ v = v * pj->fadeout_volume;
+ else
+ v <<= 16;
+ if (pj->flags & CHN_LOOP) v >>= 1;
+ if (v < vol || (v == vol && pj->vol_env_position > envpos)) {
+ envpos = pj->vol_env_position;
+ vol = v;
+ result = j;
+ }
+ }
+ if (result) {
+ /* unmute new nna channel */
+ csf->voices[result].flags &= ~(CHN_MUTE|CHN_NNAMUTE);
+ }
+ return result;
+}
+
+
+void csf_check_nna(song_t *csf, uint32_t nchan, uint32_t instr, int note, int force_cut)
+{
+ song_voice_t *p;
+ song_voice_t *chan = &csf->voices[nchan];
+ song_instrument_t *penv = (csf->flags & SONG_INSTRUMENTMODE) ? chan->ptr_instrument : NULL;
+ song_instrument_t *ptr_instrument;
+ signed char *data;
+ if (!NOTE_IS_NOTE(note))
+ return;
+ // Always NNA cut - using
+ if (force_cut || !(csf->flags & SONG_INSTRUMENTMODE)) {
+ if (!chan->length || (chan->flags & CHN_MUTE) || (!chan->left_volume && !chan->right_volume))
+ return;
+ uint32_t n = csf_get_nna_channel(csf, nchan);
+ if (!n) return;
+ p = &csf->voices[n];
+ // Copy Channel
+ *p = *chan;
+ p->flags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PORTAMENTO);
+ p->tremolo_delta = 0;
+ p->master_channel = nchan+1;
+ p->n_command = 0;
+ // Cut the note
+ p->fadeout_volume = 0;
+ p->flags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP);
+ // Stop this channel
+ chan->length = chan->position = chan->position_frac = 0;
+ chan->rofs = chan->lofs = 0;
+ chan->left_volume = chan->right_volume = 0;
+ if (chan->flags & CHN_ADLIB) {
+ //Do this only if really an adlib chan. Important!
+ OPL_NoteOff(nchan);
+ OPL_Touch(nchan, NULL, 0);
+ }
+ return;
+ }
+ if (instr >= MAX_INSTRUMENTS) instr = 0;
+ data = chan->current_sample_data;
+ ptr_instrument = chan->ptr_instrument;
+ if (instr && note) {
+ ptr_instrument = (csf->flags & SONG_INSTRUMENTMODE) ? csf->instruments[instr] : NULL;
+ if (ptr_instrument) {
+ uint32_t n = 0;
+ if (!NOTE_IS_CONTROL(note)) {
+ n = ptr_instrument->sample_map[note-1];
+ note = ptr_instrument->note_map[note-1];
+ if (n && n < MAX_SAMPLES)
+ data = csf->samples[n].data;
+ }
+ } else {
+ data = NULL;
+ }
+ }
+ if (!penv) return;
+ p = chan;
+ for (uint32_t i=nchan; i<MAX_VOICES; p++, i++) {
+ if (!((i >= MAX_CHANNELS || p == chan)
+ && ((p->master_channel == nchan+1 || p == chan)
+ && p->ptr_instrument)))
+ continue;
+ int ok = 0;
+ // Duplicate Check Type
+ switch (p->ptr_instrument->dct) {
+ case DCT_NOTE:
+ ok = (NOTE_IS_NOTE(note) && (int) p->note == note && ptr_instrument == p->ptr_instrument);
+ break;
+ case DCT_SAMPLE:
+ ok = (data && data == p->current_sample_data);
+ break;
+ case DCT_INSTRUMENT:
+ ok = (ptr_instrument == p->ptr_instrument);
+ break;
+ }
+ // Duplicate Note Action
+ if (ok) {
+ switch(p->ptr_instrument->dca) {
+ case DCA_NOTECUT:
+ fx_note_cut(csf, i, 1);
+ break;
+ case DCA_NOTEOFF:
+ fx_key_off(csf, i);
+ break;
+ case DCA_NOTEFADE:
+ p->flags |= CHN_NOTEFADE;
+ break;
+ }
+ if (!p->volume) {
+ p->fadeout_volume = 0;
+ p->flags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP);
+ }
+ }
+ }
+ if (chan->flags & CHN_MUTE)
+ return;
+ // New Note Action
+ if (chan->volume && chan->length) {
+ uint32_t n = csf_get_nna_channel(csf, nchan);
+ if (n) {
+ p = &csf->voices[n];
+ // Copy Channel
+ *p = *chan;
+ p->flags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PORTAMENTO);
+ p->tremolo_delta = 0;
+ p->master_channel = nchan+1;
+ p->n_command = 0;
+ // Key Off the note
+ switch(chan->nna) {
+ case NNA_NOTEOFF:
+ fx_key_off(csf, n);
+ break;
+ case NNA_NOTECUT:
+ p->fadeout_volume = 0;
+ case NNA_NOTEFADE:
+ p->flags |= CHN_NOTEFADE;
+ break;
+ }
+ if (!p->volume) {
+ p->fadeout_volume = 0;
+ p->flags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP);
+ }
+ // Stop this channel
+ chan->length = chan->position = chan->position_frac = 0;
+ chan->rofs = chan->lofs = 0;
+ }
+ }
+}
+
+
+
+static void handle_effect(song_t *csf, uint32_t nchan, uint32_t cmd, uint32_t param, int porta, int firsttick)
+{
+ song_voice_t *chan = csf->voices + nchan;
+
+ switch (cmd) {
+ case FX_NONE:
+ break;
+
+ // Set Volume
+ case FX_VOLUME:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ chan->volume = (param < 64) ? param*4 : 256;
+ chan->flags |= CHN_FASTVOLRAMP;
+ break;
+
+ case FX_PORTAMENTOUP:
+ if (firsttick) {
+ if (param)
+ chan->mem_pitchslide = param;
+ if (!(csf->flags & SONG_COMPATGXX))
+ chan->mem_portanote = chan->mem_pitchslide;
+ }
+ fx_portamento_up(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_PORTAMENTODOWN:
+ if (firsttick) {
+ if (param)
+ chan->mem_pitchslide = param;
+ if (!(csf->flags & SONG_COMPATGXX))
+ chan->mem_portanote = chan->mem_pitchslide;
+ }
+ fx_portamento_down(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_VOLUMESLIDE:
+ fx_volume_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_TONEPORTAMENTO:
+ if (firsttick) {
+ if (param)
+ chan->mem_portanote = param;
+ if (!(csf->flags & SONG_COMPATGXX))
+ chan->mem_pitchslide = chan->mem_portanote;
+ }
+ fx_tone_portamento(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_TONEPORTAVOL:
+ fx_volume_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ fx_tone_portamento(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, 0);
+ break;
+
+ case FX_VIBRATO:
+ fx_vibrato(chan, param);
+ break;
+
+ case FX_VIBRATOVOL:
+ fx_volume_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ fx_vibrato(chan, 0);
+ break;
+
+ case FX_SPEED:
+ if ((csf->flags & SONG_FIRSTTICK) && param) {
+ csf->tick_count = param;
+ csf->current_speed = param;
+ }
+ break;
+
+ case FX_TEMPO:
+ if (csf->flags & SONG_FIRSTTICK) {
+ if (param)
+ chan->mem_tempo = param;
+ else
+ param = chan->mem_tempo;
+ if (param >= 0x20)
+ csf->current_tempo = param;
+ } else {
+ param = chan->mem_tempo; // this just got set on tick zero
+
+ switch (param >> 4) {
+ case 0:
+ csf->current_tempo -= param & 0xf;
+ if (csf->current_tempo < 32)
+ csf->current_tempo = 32;
+ break;
+ case 1:
+ csf->current_tempo += param & 0xf;
+ if (csf->current_tempo > 255)
+ csf->current_tempo = 255;
+ break;
+ }
+ }
+ break;
+
+ case FX_OFFSET:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ if (param)
+ chan->mem_offset = (chan->mem_offset & ~0xff00) | (param << 8);
+ if (NOTE_IS_NOTE(chan->row_note)) {
+ // when would position *not* be zero if there's a note but no portamento?
+ if (porta)
+ chan->position = chan->mem_offset;
+ else
+ chan->position += chan->mem_offset;
+ if (chan->position > chan->length) {
+ chan->position = (csf->flags & SONG_ITOLDEFFECTS) ? chan->length : 0;
+ }
+ }
+ break;
+
+ case FX_ARPEGGIO:
+ chan->n_command = FX_ARPEGGIO;
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ if (param)
+ chan->mem_arpeggio = param;
+ break;
+
+ case FX_RETRIG:
+ if (param)
+ chan->mem_retrig = param & 0xFF;
+ fx_retrig_note(csf, nchan, chan->mem_retrig);
+ break;
+
+ case FX_TREMOR:
+ // Tremor logic lifted from DUMB, which is the only player that actually gets it right.
+ // I *sort of* understand it.
+ if (csf->flags & SONG_FIRSTTICK) {
+ if (!param)
+ param = chan->mem_tremor;
+ else if (!(csf->flags & SONG_ITOLDEFFECTS)) {
+ if (param & 0xf0) param -= 0x10;
+ if (param & 0x0f) param -= 0x01;
+ }
+ chan->mem_tremor = param;
+ chan->cd_tremor |= 128;
+ }
+
+ if ((chan->cd_tremor & 128) && chan->length) {
+ if (chan->cd_tremor == 128)
+ chan->cd_tremor = (chan->mem_tremor >> 4) | 192;
+ else if (chan->cd_tremor == 192)
+ chan->cd_tremor = (chan->mem_tremor & 0xf) | 128;
+ else
+ chan->cd_tremor--;
+ }
+
+ chan->n_command = FX_TREMOR;
+
+ break;
+
+ case FX_GLOBALVOLUME:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ if (param <= 128)
+ csf->current_global_volume = param;
+ break;
+
+ case FX_GLOBALVOLSLIDE:
+ fx_global_vol_slide(csf, chan, param);
+ break;
+
+ case FX_PANNING:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ chan->flags &= ~CHN_SURROUND;
+ chan->panbrello_delta = 0;
+ chan->panning = param;
+ chan->pan_swing = 0;
+ chan->flags |= CHN_FASTVOLRAMP;
+ break;
+
+ case FX_PANNINGSLIDE:
+ fx_panning_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_TREMOLO:
+ fx_tremolo(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_FINEVIBRATO:
+ fx_fine_vibrato(chan, param);
+ break;
+
+ case FX_SPECIAL:
+ fx_special(csf, nchan, param);
+ break;
+
+ case FX_KEYOFF:
+ if ((csf->current_speed - csf->tick_count) == param)
+ fx_key_off(csf, nchan);
+ break;
+
+ case FX_CHANNELVOLUME:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ // FIXME rename global_volume to channel_volume in the channel struct
+ if (param <= 64) {
+ chan->global_volume = param;
+ chan->flags |= CHN_FASTVOLRAMP;
+ }
+ break;
+
+ case FX_CHANNELVOLSLIDE:
+ fx_channel_vol_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param);
+ break;
+
+ case FX_PANBRELLO:
+ fx_panbrello(chan, param);
+ break;
+
+ case FX_SETENVPOSITION:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ chan->vol_env_position = param;
+ chan->pan_env_position = param;
+ chan->pitch_env_position = param;
+ if ((csf->flags & SONG_INSTRUMENTMODE) && chan->ptr_instrument) {
+ song_instrument_t *penv = chan->ptr_instrument;
+ if ((chan->flags & CHN_PANENV)
+ && (penv->pan_env.nodes)
+ && ((int)param > penv->pan_env.ticks[penv->pan_env.nodes-1])) {
+ chan->flags &= ~CHN_PANENV;
+ }
+ }
+ break;
+
+ case FX_POSITIONJUMP:
+ if (csf->flags & SONG_FIRSTTICK) {
+ if (!(csf->mix_flags & SNDMIX_NOBACKWARDJUMPS) || csf->process_order < param)
+ csf->process_order = param - 1;
+ csf->process_row = PROCESS_NEXT_ORDER;
+ }
+ break;
+
+ case FX_PATTERNBREAK:
+ if (csf->flags & SONG_FIRSTTICK) {
+ csf->break_row = param;
+ csf->process_row = PROCESS_NEXT_ORDER;
+ }
+ break;
+
+ case FX_MIDI:
+ if (!(csf->flags & SONG_FIRSTTICK))
+ break;
+ if (param < 0x80) {
+ csf_process_midi_macro(csf, nchan,
+ csf->midi_config.sfx[chan->active_macro],
+ param, 0, 0, 0);
+ } else {
+ csf_process_midi_macro(csf, nchan,
+ csf->midi_config.zxx[param & 0x7F],
+ 0, 0, 0, 0);
+ }
+ break;
+
+ case FX_NOTESLIDEUP:
+ fx_note_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param, 1);
+ break;
+ case FX_NOTESLIDEDOWN:
+ fx_note_slide(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan, param, -1);
+ break;
+ }
+}
+
+static void handle_voleffect(song_t *csf, song_voice_t *chan, uint32_t volcmd, uint32_t vol,
+ int firsttick, int start_note)
+{
+ /* A few notes, paraphrased from ITTECH.TXT:
+ Ex/Fx/Gx are shared with Exx/Fxx/Gxx; Ex/Fx are 4x the 'normal' slide value
+ Gx is linked with Ex/Fx if Compat Gxx is off, just like Gxx is with Exx/Fxx
+ Gx values: 1, 4, 8, 16, 32, 64, 96, 128, 255
+ Ax/Bx/Cx/Dx values are used directly (i.e. D9 == D09), and are NOT shared with Dxx
+ (value is stored into mem_vc_volslide and used by A0/B0/C0/D0)
+ Hx uses the same value as Hxx and Uxx, and affects the *depth*
+ so... hxx = (hx | (oldhxx & 0xf0)) ???
+
+ Additionally: volume and panning are handled on the start tick, not
+ the first tick of the row (that is, SDx alters their behavior) */
+
+ switch (volcmd) {
+ case VOLFX_NONE:
+ break;
+
+ case VOLFX_VOLUME:
+ if (start_note) {
+ if (vol > 64) vol = 64;
+ chan->volume = vol << 2;
+ chan->flags |= CHN_FASTVOLRAMP;
+ }
+ break;
+
+ case VOLFX_PANNING:
+ if (start_note) {
+ if (vol > 64) vol = 64;
+ chan->panning = vol << 2;
+ chan->pan_swing = 0;
+ chan->flags |= CHN_FASTVOLRAMP;
+ chan->flags &= ~CHN_SURROUND;
+ chan->panbrello_delta = 0;
+ }
+ break;
+
+ case VOLFX_PORTAUP: // Fx
+ if (firsttick) {
+ if (vol)
+ chan->mem_pitchslide = 4 * vol;
+ if (!(csf->flags & SONG_COMPATGXX))
+ chan->mem_portanote = chan->mem_pitchslide;
+ } else {
+ fx_reg_portamento_up(csf->flags, chan, chan->mem_pitchslide);
+ }
+ break;
+
+ case VOLFX_PORTADOWN: // Ex
+ if (firsttick) {
+ if (vol)
+ chan->mem_pitchslide = 4 * vol;
+ if (!(csf->flags & SONG_COMPATGXX))
+ chan->mem_portanote = chan->mem_pitchslide;
+ } else {
+ fx_reg_portamento_down(csf->flags, chan, chan->mem_pitchslide);
+ }
+ break;
+
+ case VOLFX_TONEPORTAMENTO: // Gx
+ if (firsttick) {
+ if (vol)
+ chan->mem_portanote = vc_portamento_table[vol & 0x0F];
+ if (!(csf->flags & SONG_COMPATGXX))
+ chan->mem_pitchslide = chan->mem_portanote;
+ }
+ fx_tone_portamento(csf->flags | (firsttick ? SONG_FIRSTTICK : 0), chan,
+ vc_portamento_table[vol & 0x0F]);
+ break;
+
+ case VOLFX_VOLSLIDEUP: // Cx
+ if (firsttick) {
+ if (vol)
+ chan->mem_vc_volslide = vol;
+ } else {
+ fx_volume_up(chan, chan->mem_vc_volslide);
+ }
+ break;
+
+ case VOLFX_VOLSLIDEDOWN: // Dx
+ if (firsttick) {
+ if (vol)
+ chan->mem_vc_volslide = vol;
+ } else {
+ fx_volume_down(chan, chan->mem_vc_volslide);
+ }
+ break;
+
+ case VOLFX_FINEVOLUP: // Ax
+ if (firsttick) {
+ if (vol)
+ chan->mem_vc_volslide = vol;
+ else
+ vol = chan->mem_vc_volslide;
+ fx_volume_up(chan, vol);
+ }
+ break;
+
+ case VOLFX_FINEVOLDOWN: // Bx
+ if (firsttick) {
+ if (vol)
+ chan->mem_vc_volslide = vol;
+ else
+ vol = chan->mem_vc_volslide;
+ fx_volume_down(chan, vol);
+ }
+ break;
+
+ case VOLFX_VIBRATODEPTH: // Hx
+ fx_vibrato(chan, vol);
+ break;
+
+ case VOLFX_VIBRATOSPEED: // $x (FT2 compat.)
+ fx_vibrato(chan, vol << 4);
+ break;
+
+ case VOLFX_PANSLIDELEFT: // <x (FT2)
+ fx_panning_slide(csf->flags, chan, vol);
+ break;
+
+ case VOLFX_PANSLIDERIGHT: // >x (FT2)
+ fx_panning_slide(csf->flags, chan, vol << 4);
+ break;
+ }
+}
+
+/* firsttick is only used for SDx at the moment */
+void csf_process_effects(song_t *csf, int firsttick)
+{
+ song_voice_t *chan = csf->voices;
+ for (uint32_t nchan=0; nchan<MAX_CHANNELS; nchan++, chan++) {
+ chan->n_command=0;
+
+ uint32_t instr = chan->row_instr;
+ uint32_t volcmd = chan->row_voleffect;
+ uint32_t vol = chan->row_volparam;
+ uint32_t cmd = chan->row_effect;
+ uint32_t param = chan->row_param;
+ int porta = (cmd == FX_TONEPORTAMENTO
+ || cmd == FX_TONEPORTAVOL
+ || volcmd == VOLFX_TONEPORTAMENTO);
+ int start_note = csf->flags & SONG_FIRSTTICK;
+
+ chan->flags &= ~CHN_FASTVOLRAMP;
+
+ // set instrument before doing anything else
+ if (instr) chan->new_instrument = instr;
+
+ /* Have to handle SDx specially because of the way the effects are structured.
+ In a PERFECT world, this would be very straightforward:
+ - Handle the effect column, and set flags for things that should happen
+ (portamento, volume slides, arpeggio, vibrato, tremolo)
+ - If note delay counter is set, stop processing that channel
+ - Trigger all notes if it's their start tick
+ - Handle volume column.
+ The obvious implication of this is that all effects are checked only once, and
+ volumes only need to be set for notes once. Additionally this helps for separating
+ the mixing code from the rest of the interface (which is always good, especially
+ for hardware mixing...)
+ Oh well, the world is not perfect. */
+
+ if (cmd == FX_SPECIAL) {
+ if (param)
+ chan->mem_special = param;
+ else
+ param = chan->mem_special;
+ if (param >> 4 == 0xd) {
+ // Ideally this would use SONG_FIRSTTICK, but Impulse Tracker has a bug here :)
+ if (firsttick) {
+ chan->cd_note_delay = (param & 0xf) ?: 1;
+ continue; // notes never play on the first tick with SDx, go away
+ }
+ if (--chan->cd_note_delay > 0)
+ continue; // not our turn yet, go away
+ start_note = (chan->cd_note_delay == 0);
+ }
+ }
+
+ // Handles note/instrument/volume changes
+ if (start_note) {
+ uint32_t note = chan->row_note;
+ if (instr && note == NOTE_NONE) {
+ if (csf->flags & SONG_INSTRUMENTMODE) {
+ if (chan->ptr_sample)
+ chan->volume = chan->ptr_sample->volume;
+ } else {
+ if (instr < MAX_SAMPLES)
+ chan->volume = csf->samples[instr].volume;
+ }
+ }
+ // Invalid Instrument ?
+ if (instr >= MAX_INSTRUMENTS) instr = 0;
+ // Note Cut/Off => ignore instrument
+ if ((NOTE_IS_CONTROL(note)) || (note != NOTE_NONE && !porta)) {
+ /* This is required when the instrument changes (KeyOff is not called) */
+ /* Possibly a better bugfix could be devised. --Bisqwit */
+ if (chan->flags & CHN_ADLIB) {
+ //Do this only if really an adlib chan. Important!
+ OPL_NoteOff(nchan);
+ OPL_Touch(nchan, NULL, 0);
+ }
+ }
+
+ if (NOTE_IS_CONTROL(note)) {
+ instr = 0;
+ } else if (NOTE_IS_NOTE(note)) {
+ chan->new_note = note;
+ // New Note Action ? (not when paused!!!)
+ if (!porta)
+ csf_check_nna(csf, nchan, instr, note, 0);
+ }
+ // Instrument Change ?
+ if (instr) {
+ song_sample_t *psmp = chan->ptr_sample;
+ csf_instrument_change(csf, chan, instr, porta, 1);
+ if (csf->samples[instr].flags & CHN_ADLIB) {
+ OPL_Patch(nchan, csf->samples[instr].adlib_bytes);
+ }
+
+
+ chan->new_instrument = 0;
+ // Special IT case: portamento+note causes sample change -> ignore portamento
+ if (psmp != chan->ptr_sample && NOTE_IS_NOTE(note)) {
+ porta = 0;
+ }
+ }
+ // New Note ?
+ if (note != NOTE_NONE) {
+ if (!instr && chan->new_instrument && NOTE_IS_NOTE(note)) {
+ csf_instrument_change(csf, chan, chan->new_instrument, porta, 0);
+ if ((csf->flags & SONG_INSTRUMENTMODE)
+ && csf->instruments[chan->new_instrument]) {
+ if (csf->samples[chan->new_instrument].flags & CHN_ADLIB) {
+ OPL_Patch(nchan, csf->samples[chan->new_instrument].adlib_bytes);
+ }
+ }
+ chan->new_instrument = 0;
+ }
+ csf_note_change(csf, nchan, note, porta, 0, !instr);
+ }
+ }
+
+ handle_effect(csf, nchan, cmd, param, porta, firsttick);
+ handle_voleffect(csf, chan, volcmd, vol, firsttick, start_note);
+ }
+}
diff --git a/src/player/equalizer.c b/src/player/equalizer.c
new file mode 100644
index 0000000..feecef4
--- /dev/null
+++ b/src/player/equalizer.c
@@ -0,0 +1,255 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sndfile.h"
+#include "cmixer.h"
+#include <math.h>
+
+
+#define EQ_BANDWIDTH 2.0
+#define EQ_ZERO 0.000001
+
+
+
+typedef struct {
+ float a0, a1, a2, b1, b2;
+ float x1, x2, y1, y2;
+ float gain, center_frequency;
+ int enabled;
+} eq_band;
+
+
+
+//static REAL f2ic = (REAL)(1 << 28);
+//static REAL i2fc = (REAL)(1.0 / (1 << 28));
+
+static eq_band eq[MAX_EQ_BANDS * 2] =
+{
+ // Default: Flat EQ
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 120, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 600, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1200, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 120, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 600, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1200, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10000, 0},
+};
+
+
+static void eq_filter(eq_band *pbs, float *pbuffer, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ float x = pbuffer[i];
+ float y = pbs->a1 * pbs->x1 +
+ pbs->a2 * pbs->x2 +
+ pbs->a0 * x +
+ pbs->b1 * pbs->y1 +
+ pbs->b2 * pbs->y2;
+
+ pbs->x2 = pbs->x1;
+ pbs->y2 = pbs->y1;
+ pbs->x1 = x;
+ pbuffer[i] = y;
+ pbs->y1 = y;
+ }
+}
+
+
+void eq_mono(song_t *csf, int *buffer, unsigned int count)
+{
+ mono_mix_to_float(buffer, csf->mix_buffer_float, count);
+
+ for (unsigned int b = 0; b < MAX_EQ_BANDS; b++)
+ {
+ if (eq[b].enabled && eq[b].gain != 1.0f)
+ eq_filter(&eq[b], csf->mix_buffer_float, count);
+ }
+
+ float_to_mono_mix(csf->mix_buffer_float, buffer, count);
+}
+
+
+// XXX: I rolled the two loops into one. Make sure this works.
+void eq_stereo(song_t *csf, int *buffer, unsigned int count)
+{
+ stereo_mix_to_float(buffer, csf->mix_buffer_float, csf->mix_buffer_float + MIXBUFFERSIZE, count);
+
+ for (unsigned int b = 0; b < MAX_EQ_BANDS; b++) {
+ int br = b + MAX_EQ_BANDS;
+
+ // Left band
+ if (eq[b].enabled && eq[b].gain != 1.0f)
+ eq_filter(&eq[b], csf->mix_buffer_float, count);
+
+ // Right band
+ if (eq[br].enabled && eq[br].gain != 1.0f)
+ eq_filter(&eq[br], csf->mix_buffer_float + MIXBUFFERSIZE, count);
+ }
+
+ float_to_stereo_mix(csf->mix_buffer_float, csf->mix_buffer_float + MIXBUFFERSIZE, buffer, count);
+}
+
+
+void initialize_eq(int reset, float freq)
+{
+ //float fMixingFreq = (REAL)mix_frequency;
+
+ // Gain = 0.5 (-6dB) .. 2 (+6dB)
+ for (unsigned int band = 0; band < MAX_EQ_BANDS * 2; band++) {
+ float k, k2, r, f;
+ float v0, v1;
+ int b = reset;
+
+ if (!eq[band].enabled) {
+ eq[band].a0 = 0;
+ eq[band].a1 = 0;
+ eq[band].a2 = 0;
+ eq[band].b1 = 0;
+ eq[band].b2 = 0;
+ eq[band].x1 = 0;
+ eq[band].x2 = 0;
+ eq[band].y1 = 0;
+ eq[band].y2 = 0;
+ continue;
+ }
+
+ f = eq[band].center_frequency / freq;
+
+ if (f > 0.45f)
+ eq[band].gain = 1;
+
+ //if (f > 0.25)
+ // f = 0.25;
+
+ //k = tan(PI * f);
+
+ k = f * 3.141592654f;
+ k = k + k * f;
+
+ //if (k > (float) 0.707)
+ // k = (float) 0.707;
+
+ k2 = k*k;
+ v0 = eq[band].gain;
+ v1 = 1;
+
+ if (eq[band].gain < 1.0) {
+ v0 *= 0.5f / EQ_BANDWIDTH;
+ v1 *= 0.5f / EQ_BANDWIDTH;
+ }
+ else {
+ v0 *= 1.0f / EQ_BANDWIDTH;
+ v1 *= 1.0f / EQ_BANDWIDTH;
+ }
+
+ r = (1 + v0 * k + k2) / (1 + v1 * k + k2);
+
+ if (r != eq[band].a0) {
+ eq[band].a0 = r;
+ b = 1;
+ }
+
+ r = 2 * (k2 - 1) / (1 + v1 * k + k2);
+
+ if (r != eq[band].a1) {
+ eq[band].a1 = r;
+ b = 1;
+ }
+
+ r = (1 - v0 * k + k2) / (1 + v1 * k + k2);
+
+ if (r != eq[band].a2) {
+ eq[band].a2 = r;
+ b = 1;
+ }
+
+ r = -2 * (k2 - 1) / (1 + v1 * k + k2);
+
+ if (r != eq[band].b1) {
+ eq[band].b1 = r;
+ b = 1;
+ }
+
+ r = -(1 - v1 * k + k2) / (1 + v1 * k + k2);
+
+ if (r != eq[band].b2) {
+ eq[band].b2 = r;
+ b = 1;
+ }
+
+ if (b) {
+ eq[band].x1 = 0;
+ eq[band].x2 = 0;
+ eq[band].y1 = 0;
+ eq[band].y2 = 0;
+ }
+ }
+}
+
+
+void set_eq_gains(const unsigned int *gainbuff, unsigned int gains, const unsigned int *freqs,
+ int reset, int mix_freq)
+{
+ for (unsigned int i = 0; i < MAX_EQ_BANDS; i++) {
+ float g, f = 0;
+
+ if (i < gains) {
+ unsigned int n = gainbuff[i];
+
+ //if (n > 32)
+ // n = 32;
+
+ g = 1.0 + (((double) n) / 64.0);
+
+ if (freqs)
+ f = (float)(int) freqs[i];
+ }
+ else {
+ g = 1;
+ }
+
+ eq[i].gain =
+ eq[i + MAX_EQ_BANDS].gain = g;
+ eq[i].center_frequency =
+ eq[i + MAX_EQ_BANDS].center_frequency = f;
+
+ /* don't enable bands outside... */
+ if (f > 20.0f &&
+ i < gains) {
+ eq[i].enabled =
+ eq[i + MAX_EQ_BANDS].enabled = 1;
+ }
+ else {
+ eq[i].enabled =
+ eq[i + MAX_EQ_BANDS].enabled = 0;
+ }
+ }
+
+ initialize_eq(reset, mix_freq);
+}
+
diff --git a/src/player/filters.c b/src/player/filters.c
new file mode 100644
index 0000000..ddd74cc
--- /dev/null
+++ b/src/player/filters.c
@@ -0,0 +1,116 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sndfile.h"
+#include "cmixer.h"
+#include <math.h>
+
+
+// LUT for 2 * damping factor
+static const float resonance_table[128] = {
+ 1.0000000000000000f, 0.9786446094512940f, 0.9577452540397644f, 0.9372922182083130f,
+ 0.9172759056091309f, 0.8976871371269226f, 0.8785166740417481f, 0.8597555756568909f,
+ 0.8413951396942139f, 0.8234267830848694f, 0.8058421611785889f, 0.7886331081390381f,
+ 0.7717915177345276f, 0.7553095817565918f, 0.7391796708106995f, 0.7233941555023193f,
+ 0.7079457640647888f, 0.6928272843360901f, 0.6780316829681397f, 0.6635520458221436f,
+ 0.6493816375732422f, 0.6355138421058655f, 0.6219421625137329f, 0.6086603403091431f,
+ 0.5956621170043945f, 0.5829415321350098f, 0.5704925656318665f, 0.5583094954490662f,
+ 0.5463865399360657f, 0.5347182154655457f, 0.5232990980148315f, 0.5121238231658936f,
+ 0.5011872053146362f, 0.4904841780662537f, 0.4800096750259399f, 0.4697588682174683f,
+ 0.4597269892692566f, 0.4499093294143677f, 0.4403013288974762f, 0.4308985173702240f,
+ 0.4216965138912201f, 0.4126909971237183f, 0.4038778245449066f, 0.3952528536319733f,
+ 0.3868120610713959f, 0.3785515129566193f, 0.3704673945903778f, 0.3625559210777283f,
+ 0.3548133969306946f, 0.3472362160682678f, 0.3398208320140839f, 0.3325638175010681f,
+ 0.3254617750644684f, 0.3185114264488220f, 0.3117094635963440f, 0.3050527870655060f,
+ 0.2985382676124573f, 0.2921628654003143f, 0.2859236001968384f, 0.2798175811767578f,
+ 0.2738419771194458f, 0.2679939568042755f, 0.2622708380222321f, 0.2566699385643005f,
+ 0.2511886358261108f, 0.2458244115114212f, 0.2405747324228287f, 0.2354371547698975f,
+ 0.2304092943668366f, 0.2254888117313385f, 0.2206734120845795f, 0.2159608304500580f,
+ 0.2113489061594009f, 0.2068354636430740f, 0.2024184018373489f, 0.1980956792831421f,
+ 0.1938652694225311f, 0.1897251904010773f, 0.1856735348701477f, 0.1817083954811096f,
+ 0.1778279393911362f, 0.1740303486585617f, 0.1703138649463654f, 0.1666767448186874f,
+ 0.1631172895431519f, 0.1596338599920273f, 0.1562248021364212f, 0.1528885662555695f,
+ 0.1496235728263855f, 0.1464282870292664f, 0.1433012634515762f, 0.1402409970760346f,
+ 0.1372461020946503f, 0.1343151479959488f, 0.1314467936754227f, 0.1286396980285645f,
+ 0.1258925348520279f, 0.1232040524482727f, 0.1205729842185974f, 0.1179980933666229f,
+ 0.1154781952500343f, 0.1130121126770973f, 0.1105986908078194f, 0.1082368120551109f,
+ 0.1059253737330437f, 0.1036632955074310f, 0.1014495193958283f, 0.0992830246686935f,
+ 0.0971627980470657f, 0.0950878411531448f, 0.0930572077631950f, 0.0910699293017387f,
+ 0.0891250967979431f, 0.0872217938303947f, 0.0853591337800026f, 0.0835362523794174f,
+ 0.0817523002624512f, 0.0800064504146576f, 0.0782978758215904f, 0.0766257941722870f,
+ 0.0749894231557846f, 0.0733879879117012f, 0.0718207582831383f, 0.0702869966626167f,
+ 0.0687859877943993f, 0.0673170387744904f, 0.0658794566988945f, 0.0644725710153580f,
+};
+
+
+// Simple 2-poles resonant filter
+//
+// XXX freq WAS unused but is now mix_frequency!
+//
+#define FREQ_PARAM_MULT (128.0 / (24.0 * 256.0))
+void setup_channel_filter(song_voice_t *chan, int reset, int flt_modifier, int freq)
+{
+ int cutoff = chan->cutoff;
+ int resonance = chan->resonance;
+ float frequency, r, d, e, fg, fb0, fb1;
+
+ cutoff = cutoff * (flt_modifier + 256) / 256;
+
+ if (cutoff > 255)
+ cutoff = 255;
+
+ if (resonance > 255)
+ resonance = 255;
+
+ // TODO: The enabling/disabling of channel filter is a bit more complex.
+ // More info in snd_flt.cpp in OpenMPT and filter-reset.it, filter-reset-carry.it
+ // and filter-nna.it from https://wiki.openmpt.org/Development:_Test_Cases/IT
+ // Should be 255, but Zxx cutoff is limited to 127, so...
+ if (cutoff < 254)
+ chan->flags |= CHN_FILTER;
+ else
+ cutoff = 255;
+
+ // 2 ^ (i / 24 * 256)
+ frequency = 110.0 * powf(2.0, (float) cutoff * FREQ_PARAM_MULT + 0.25);
+ if (frequency > freq / 2.0)
+ frequency = freq / 2.0;
+ r = freq / (2.0 * M_PI * frequency);
+
+ d = resonance_table[resonance] * r + resonance_table[resonance] - 1.0;
+ e = r * r;
+
+ fg = 1.0 / (1.0 + d + e);
+ fb0 = (d + e + e) / (1.0 + d + e);
+ fb1 = -e / (1.0 + d + e);
+
+ chan->filter_a0 = (int32_t)(fg * (1 << FILTERPRECISION));
+ chan->filter_b0 = (int32_t)(fb0 * (1 << FILTERPRECISION));
+ chan->filter_b1 = (int32_t)(fb1 * (1 << FILTERPRECISION));
+
+ if (reset) {
+ chan->filter_y1 = chan->filter_y2 = 0;
+ chan->filter_y3 = chan->filter_y4 = 0;
+ }
+}
+
diff --git a/src/player/fmopl.c b/src/player/fmopl.c
new file mode 100644
index 0000000..7594ebb
--- /dev/null
+++ b/src/player/fmopl.c
@@ -0,0 +1,2396 @@
+/*
+**
+** File: fmopl.c - software implementation of FM sound generator
+** types OPL and OPL2
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 0.72
+**
+
+Revision History:
+
+04-08-2003 Jarek Burczynski:
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip
+ handles memory read/write or during the adpcm synthesis when the chip
+ requests another byte of ADPCM data.
+
+24-07-2003 Jarek Burczynski:
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after
+ some (unknown) delay). Right now it's always set.
+
+14-06-2003 Jarek Burczynski:
+ - implemented all of the status register flags in Y8950 emulation
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since
+ they can be either RAM or ROM
+
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)
+ - corrected ym3526_read() to always set bit 2 and bit 1
+ to HIGH state - identical to ym3812_read (verified on real YM3526)
+
+04-28-2002 Jarek Burczynski:
+ - binary exact Envelope Generator (verified on real YM3812);
+ compared to YM2151: the EG clock is equal to internal_clock,
+ rates are 2 times slower and volume resolution is one bit less
+ - modified interface functions (they no longer return pointer -
+ that's internal to the emulator now):
+ - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()
+ - corrected 'off by one' error in feedback calculations (when feedback is off)
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)
+ - speeded up noise generator calculations (Nicola Salmoria)
+
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)
+ Complete rewrite (all verified on real YM3812):
+ - corrected sin_tab and tl_tab data
+ - corrected operator output calculations
+ - corrected waveform_select_enable register;
+ simply: ignore all writes to waveform_select register when
+ waveform_select_enable == 0 and do not change the waveform previously selected.
+ - corrected KSR handling
+ - corrected Envelope Generator: attack shape, Sustain mode and
+ Percussive/Non-percussive modes handling
+ - Envelope Generator rates are two times slower now
+ - LFO amplitude (tremolo) and phase modulation (vibrato)
+ - rhythm sounds phase generation
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)
+
+12-28-2001 Acho A. Tang
+ - reflected Delta-T EOS status on Y8950 status port.
+ - fixed subscription range of attack/decay tables
+
+
+ To do:
+ add delay before key off in CSM mode (see CSMKeyControll)
+ verify volume of the FM part on the Y8950
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+//#include "driver.h" /* use M.A.M.E. */
+#include "fmopl.h"
+
+
+/* output final shift */
+#if (OPL_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+#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 */
+
+/* ---------- 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)
+
+
+
+typedef struct
+{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 Cnt; /* frequency counter */
+ UINT32 Incr; /* frequency counter step */
+ UINT8 FB; /* feedback shift value */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+ UINT8 CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/non-percussive mode */
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ UINT16 wavetable;
+} OPL_SLOT;
+
+typedef struct
+{
+ OPL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+} OPL_CH;
+
+/* OPL state */
+typedef struct
+{
+ /* FM channel slots */
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency=chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+
+ UINT8 lfo_am_depth;
+ UINT8 lfo_pm_depth_range;
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+ UINT8 wavesel; /* waveform select enable flag */
+
+ UINT32 T[2]; /* timer counters */
+ UINT8 st[2]; /* timer enable */
+
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+
+ YM_DELTAT *deltat;
+
+ /* Keyboard and I/O ports interface */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ void * port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ void * keyboard_param;
+#endif
+
+ /* external event callback handlers */
+ OPL_TIMERHANDLER timer_handler;/* TIMER handler */
+ void *TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ void *IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ void *UpdateParam; /* stream update parameter */
+
+ UINT8 type; /* chip type */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT8 mode; /* Reg.08 : CSM,notesel,etc. */
+
+ UINT32 clock; /* master clock (Hz) */
+ UINT32 rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time)*/
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int output[1];
+#if BUILD_Y8950
+ INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
+#endif
+} FM_OPL;
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+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/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define SC(x) ((UINT32)((x)/(0.1875/2.0)))
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ /* OCT 1 */
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ SC(0.000), SC(0.750), SC(1.125), SC(1.500),
+ SC(1.875), SC(2.250), SC(2.625), SC(3.000),
+ /* OCT 2 */
+ SC(0.000), SC(0.000), SC(0.000), SC(0.000),
+ SC(0.000), SC(1.125), SC(1.875), SC(2.625),
+ SC(3.000), SC(3.750), SC(4.125), SC(4.500),
+ SC(4.875), SC(5.250), SC(5.625), SC(6.000),
+ /* OCT 3 */
+ SC(0.000), SC(0.000), SC(0.000), SC(1.875),
+ SC(3.000), SC(4.125), SC(4.875), SC(5.625),
+ SC(6.000), SC(6.750), SC(7.125), SC(7.500),
+ SC(7.875), SC(8.250), SC(8.625), SC(9.000),
+ /* OCT 4 */
+ SC(0.000), SC(0.000), SC(3.000), SC(4.875),
+ SC(6.000), SC(7.125), SC(7.875), SC(8.625),
+ SC(9.000), SC(9.750),SC(10.125),SC(10.500),
+ SC(10.875),SC(11.250),SC(11.625),SC(12.000),
+ /* OCT 5 */
+ SC(0.000), SC(3.000), SC(6.000), SC(7.875),
+ SC(9.000),SC(10.125),SC(10.875),SC(11.625),
+ SC(12.000),SC(12.750),SC(13.125),SC(13.500),
+ SC(13.875),SC(14.250),SC(14.625),SC(15.000),
+ /* OCT 6 */
+ SC(0.000), SC(6.000), SC(9.000),SC(10.875),
+ SC(12.000),SC(13.125),SC(13.875),SC(14.625),
+ SC(15.000),SC(15.750),SC(16.125),SC(16.500),
+ SC(16.875),SC(17.250),SC(17.625),SC(18.000),
+ /* OCT 7 */
+ SC(0.000), SC(9.000),SC(12.000),SC(13.875),
+ SC(15.000),SC(16.125),SC(16.875),SC(17.625),
+ SC(18.000),SC(18.750),SC(19.125),SC(19.500),
+ SC(19.875),SC(20.250),SC(20.625),SC(21.000)
+};
+#undef SC
+
+/* sustain level 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) (UINT32) ( db * (2.0/ENV_STEP) )
+static const UINT32 sl_tab[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 RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+/* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+static const unsigned char eg_rate_select[16+64+16]={
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+/* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+static const unsigned char eg_rate_shift[16+64+16]={
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define SC(x) ((UINT8)((x)*2))
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ SC(0.50), SC(1.00), SC(2.00), SC(3.00), SC(4.00), SC(5.00), SC(6.00), SC(7.00),
+ SC(8.00), SC(9.00),SC(10.00),SC(10.00),SC(12.00),SC(12.00),SC(15.00),SC(15.00)
+};
+#undef SC
+
+/* TL_TAB_LEN is calculated as:
+* 12 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (12*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* four waveforms on OPL2 type chips */
+static unsigned int sin_tab[SIN_LEN * 4];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (losing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static const INT8 lfo_pm_table[8*8*2] = {
+
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+static int num_lock = 0;
+
+
+#define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1])
+#define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2])
+#define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1])
+#define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2])
+
+
+
+
+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);
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPL *OPL)
+{
+ UINT8 tmp;
+
+ /* LFO */
+ OPL->lfo_am_cnt += OPL->lfo_am_inc;
+ if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ OPL->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];
+
+ if (OPL->lfo_am_depth)
+ OPL->LFO_AM = tmp;
+ else
+ OPL->LFO_AM = tmp>>2;
+
+ OPL->lfo_pm_cnt += OPL->lfo_pm_inc;
+ OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+INLINE void advance(FM_OPL *OPL)
+{
+ OPL_CH *CH;
+ OPL_SLOT *op;
+ int i;
+
+ OPL->eg_timer += OPL->eg_timer_add;
+
+ while (OPL->eg_timer >= OPL->eg_timer_overflow)
+ {
+ OPL->eg_timer -= OPL->eg_timer_overflow;
+
+ OPL->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( (unsigned int) op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr
+ + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+ UINT32 block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ OPL->noise_p += OPL->noise_f;
+ i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ OPL->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ((OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22)) & 1;
+ OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;
+ OPL->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm << 16)))
+ >> FREQ_SH) & SIN_MASK)];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))
+ >> FREQ_SH) & SIN_MASK)];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )
+{
+ OPL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ OPL->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ *SLOT->connect1 += SLOT->op1_out[0];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )
+{
+ OPL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ OPL->phase_modulation = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ OPL->phase_modulation = SLOT->op1_out[0];
+ /* else ignore output of operator 1 */
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2
+ (same combination as TOP CYMBAL but different output phases) */
+ /* SD (16) channel 7->slot 1 */
+ /* TOM (14) channel 8->slot 1 */
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2
+ (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on: */
+ /* HH channel 7->slot1 */
+ /* SD channel 7->slot2 */
+ /* TOM channel 8->slot1 */
+ /* TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
+ }
+
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2.0, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<12; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<12; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i,
+ sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i,
+ sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i,
+ sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i,
+ sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/
+ }
+ /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+}
+
+
+
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int i;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0;
+#if 0
+ OPL->rate = (double)OPL->clock / 72.0;
+ OPL->freqbase = 1.0;
+#endif
+
+ /*logerror("freqbase=%f\n", OPL->freqbase);*/
+
+ /* Timer base time */
+ OPL->TimerBase = 72.0 / (double)OPL->clock;
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) );
+#if 0
+ logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("FMOPL.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform);
+ 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ OPL->lfo_am_inc = (UINT32)((1.0 / 64.0 ) * (1<<LFO_SH) * OPL->freqbase);
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ OPL->lfo_pm_inc = (UINT32)((1.0 / 1024.0) * (1<<LFO_SH) * OPL->freqbase);
+
+ /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ OPL->noise_f = (UINT32)((1.0 / 1.0) * (1<<FREQ_SH) * OPL->freqbase);
+
+ OPL->eg_timer_add = (UINT32)((1<<EG_SH) * OPL->freqbase);
+ OPL->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n",
+ OPL->eg_timer_add, OPL->eg_timer_overflow);*/
+
+}
+
+INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* 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_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ 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.0 / 6.0 dB/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ 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];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* 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];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+/* write a value v to register r on OPL chip */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ UINT32 block_fnum;
+
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* waveform select enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ /* do not change the waveform previously selected */
+ }
+ break;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ break;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ /* don't reset BFRDY flag or we will have to call deltat module to set the flag */
+ OPL_STATUS_RESET(OPL,0x7f-0x08);
+ }
+ 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-0x08) );
+ OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );
+
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double period = st2 ? (OPL->TimerBase * OPL->T[1]) : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double period = st1 ? (OPL->TimerBase * OPL->T[0]) : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);
+ }
+ }
+ break;
+#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
+ logerror("Y8950: write unmapped KEYBOARD port\n");
+ }
+ break;
+ case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+#endif
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM) {
+ /* mask 4 LSBs in register 08 for DELTA-T unit */
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f);
+ }
+#endif
+ break;
+
+#if BUILD_Y8950
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data write */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* ADPCM volume */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+
+ case 0x15: /* DAC data high 8 bits (F7,F6...F2) */
+ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */
+ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */
+ logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",
+ r,v);
+ break;
+
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ break;
+ 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);
+ }
+ break;
+#endif
+ default:
+ /*logerror("FMOPL.C: write to unknown register: %02x\n",r);*/
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(OPL,slot,v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(OPL,slot,v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(OPL,slot,v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(OPL,slot,v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ OPL->lfo_am_depth = v & 0x80;
+ OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ OPL->rhythm = v&0x3f;
+
+ if(OPL->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ 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 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = ksl_tab[block_fnum>>6];
+ CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals
+ (verifed on real YM3812) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (OPL->mode&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;
+ break;
+ case 0xe0: /* waveform select */
+ /* simply ignore write to the waveform select register
+ if selecting not enabled in test register */
+ if(OPL->wavesel)
+ {
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ CH = &OPL->P_CH[slot/2];
+
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;
+ }
+ break;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable()
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+
+ OPLCloseTable();
+
+}
+
+static void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ OPL->eg_timer = 0;
+ OPL->eg_cnt = 0;
+
+ OPL->noise_rng = 1; /* noise shift register */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wavesel 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 parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = &OPL->output_deltat[0];
+ DELTAT->portshift = 5;
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);
+ }
+#endif
+}
+
+
+#if 0 // not used anywhere
+static void OPL_postload(FM_OPL *OPL)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[ch];
+
+ /* Look up key scale level */
+ UINT32 block_fnum = CH->block_fnum;
+ CH->ksl_base = ksl_tab[block_fnum >> 6];
+ CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
+
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ /* Calculate key scale rate */
+ SLOT->ksr = CH->kcode >> SLOT->KSR;
+
+ /* Calculate attack, decay and release rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ /* Calculate phase increment */
+ SLOT->Incr = CH->fc * SLOT->mul;
+
+ /* Total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+
+ /* Connect output */
+ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;
+ }
+ }
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ // We really should call the postlod function for the YM_DELTAT, but it's hard without registers
+ // (see the way the YM2610 does it)
+ //YM_DELTAT_postload(OPL->deltat, REGS);
+ }
+#endif
+}
+#endif
+
+
+/* Create one of virtual YM3812/YM3526/Y8950 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+
+ if (OPL_LockTable() == -1) return NULL;
+
+ /* calculate OPL state size */
+ state_size = sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+
+ /* allocate memory block */
+ ptr = (char *)malloc(state_size);
+
+ if (ptr==NULL)
+ return NULL;
+
+ /* clear */
+ memset(ptr,0,state_size);
+
+ OPL = (FM_OPL *)ptr;
+
+ ptr += sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM)
+ {
+ OPL->deltat = (YM_DELTAT *)ptr;
+ }
+ ptr += sizeof(YM_DELTAT);
+#endif
+
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+
+ /* init global tables */
+ OPL_initalize(OPL);
+
+ return OPL;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLDestroy(FM_OPL *OPL)
+{
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* Optional handlers */
+
+static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)
+{
+ OPL->timer_handler = timer_handler;
+ OPL->TimerParam = param;
+}
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+static 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);
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+static unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+
+ #if BUILD_Y8950
+
+ if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */
+ {
+ return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);
+ }
+
+ #endif
+
+ /* OPL and OPL2 */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+
+#if BUILD_Y8950
+ /* 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
+ logerror("Y8950: read unmapped KEYBOARD port\n");
+ }
+ return 0;
+
+ case 0x0f: /* ADPCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ UINT8 val;
+
+ val = YM_DELTAT_ADPCM_Read(OPL->deltat);
+ /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/
+ return val;
+ }
+ return 0;
+
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ logerror("Y8950:read unmapped I/O port\n");
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ logerror("Y8950 A/D convertion is accessed but not implemented !\n");
+ return 0x80; /* 2's complement PCM data - result from A/D convertion */
+ }
+ return 0;
+ }
+#endif
+
+ return 0xff;
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ FM_KEYON (&CH->SLOT[SLOT1], 4);
+ FM_KEYON (&CH->SLOT[SLOT2], 4);
+
+ /* The key off should happen exactly one sample later - not implemented correctly yet */
+
+ FM_KEYOFF(&CH->SLOT[SLOT1], ~4);
+ FM_KEYOFF(&CH->SLOT[SLOT2], ~4);
+}
+
+
+static 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->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]);
+ return OPL->status>>7;
+}
+
+
+#define MAX_OPL_CHIPS 2
+
+
+#if (BUILD_YM3812)
+
+void * ym3812_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812);
+ if (YM3812)
+ {
+ ym3812_reset_chip(YM3812);
+ }
+ return YM3812;
+}
+
+void ym3812_shutdown(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+
+ /* emulator shutdown */
+ OPLDestroy(YM3812);
+ }
+void ym3812_reset_chip(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLResetChip(YM3812);
+}
+
+int ym3812_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLWrite(YM3812, a, v);
+}
+
+unsigned char ym3812_read(void *chip, int a)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ /* YM3812 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3812, a) | 0x06 ;
+}
+int ym3812_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3812, c);
+}
+
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3812, timer_handler, param);
+}
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3812, IRQHandler, param);
+}
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3812, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3812's
+**
+** 'which' is the virtual YM3812 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3812 */
+
+
+
+#if (BUILD_YM3526)
+
+void *ym3526_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526);
+ if (YM3526)
+ {
+ ym3526_reset_chip(YM3526);
+ }
+ return YM3526;
+}
+
+void ym3526_shutdown(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(YM3526);
+}
+void ym3526_reset_chip(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLResetChip(YM3526);
+}
+
+int ym3526_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLWrite(YM3526, a, v);
+}
+
+unsigned char ym3526_read(void *chip, int a)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* YM3526 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3526, a) | 0x06 ;
+}
+int ym3526_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3526, c);
+}
+
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3526, timer_handler, param);
+}
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3526, IRQHandler, param);
+}
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3526, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3526 */
+
+
+
+
+#if BUILD_Y8950
+
+static void Y8950_deltat_status_set(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_SET(Y8950, changebits);
+}
+static void Y8950_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_RESET(Y8950, changebits);
+}
+
+void *y8950_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950);
+ if (Y8950)
+ {
+ Y8950->deltat->status_set_handler = Y8950_deltat_status_set;
+ Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;
+ Y8950->deltat->status_change_which_chip = Y8950;
+ Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */
+ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */
+
+ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */
+ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */
+ /* reset */
+ y8950_reset_chip(Y8950);
+ }
+
+ return Y8950;
+}
+
+void y8950_shutdown(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(Y8950);
+}
+void y8950_reset_chip(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLResetChip(Y8950);
+}
+
+int y8950_write(void *chip, int a, int v)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLWrite(Y8950, a, v);
+}
+
+unsigned char y8950_read(void *chip, int a)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLRead(Y8950, a);
+}
+int y8950_timer_over(void *chip, int c)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLTimerOver(Y8950, c);
+}
+
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetTimerHandler(Y8950, timer_handler, param);
+}
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetIRQHandler(Y8950, IRQHandler, param);
+}
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(Y8950, UpdateHandler, param);
+}
+
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr);
+ OPL->deltat->memory_size = deltat_mem_size;
+}
+
+/*
+** Generate samples for one of the Y8950's
+**
+** 'which' is the virtual Y8950 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ int i;
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ YM_DELTAT *DELTAT = OPL->deltat;
+ OPLSAMPLE *buf = buffer;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+ OPL->output_deltat[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0] + (OPL->output_deltat[0]>>11);
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+
+#endif
+
diff --git a/src/player/fmpatches.c b/src/player/fmpatches.c
new file mode 100644
index 0000000..44058cd
--- /dev/null
+++ b/src/player/fmpatches.c
@@ -0,0 +1,179 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* General MIDI assignments used by Creative Labs' MIDI player (PLAY.EXE)
+(As found in dro2midi.) */
+
+#include "headers.h"
+#include "sndfile.h"
+
+static const uint8_t patches[][11] = {
+ {0x00,0x00,0x4F,0x00,0xF1,0xD2,0x51,0x43,0x00,0x00,0x06}, /*1*/
+ {0x02,0x12,0x4F,0x00,0xF1,0xD2,0x51,0x43,0x00,0x00,0x02}, /*2*/
+ {0x00,0x11,0x4A,0x00,0xF1,0xD2,0x53,0x74,0x00,0x00,0x06}, /*3*/
+ {0x03,0x11,0x4F,0x00,0xF1,0xD2,0x53,0x74,0x01,0x01,0x06}, /*4*/
+ {0x01,0x11,0x66,0x00,0xF1,0xD2,0x51,0xC3,0x00,0x00,0x06}, /*5*/
+ {0xC0,0xD2,0x52,0x00,0xF1,0xD2,0x53,0x94,0x00,0x00,0x06}, /*6*/
+ {0x12,0x18,0x86,0x00,0xF3,0xFC,0x00,0x33,0x00,0x00,0x08}, /*7*/
+ {0xD0,0x12,0x4E,0x00,0xA8,0x92,0x32,0xA7,0x03,0x02,0x00}, /*8*/
+ {0xC8,0xD1,0x4F,0x00,0xF2,0xF3,0x64,0x77,0x00,0x00,0x08}, /*9*/
+ {0x33,0x34,0x0E,0x00,0x01,0x7D,0x11,0x34,0x00,0x00,0x08}, /*10*/
+ {0x17,0x16,0x50,0x00,0xD1,0xD3,0x52,0x92,0x00,0x01,0x04}, /*11*/
+ {0xE7,0xE1,0x21,0x00,0xF5,0xF6,0x77,0x14,0x00,0x00,0x08}, /*12*/
+ {0x95,0x81,0x4E,0x00,0xDA,0xF9,0x25,0x15,0x00,0x00,0x0A}, /*13*/
+ {0x27,0x21,0x1F,0x00,0xF5,0xF5,0x96,0x57,0x00,0x00,0x08}, /*14*/
+ {0x87,0xF1,0x4E,0x80,0xB1,0xE6,0x33,0x42,0x00,0x00,0x00}, /*15*/
+ {0x31,0x11,0x87,0x80,0xA1,0x7D,0x11,0x43,0x00,0x00,0x08}, /*16*/
+ {0x32,0xB1,0x8C,0x00,0x91,0xA1,0x07,0x19,0x02,0x00,0x05}, /*17*/
+ {0x31,0xB4,0x54,0x80,0xF1,0xF5,0x07,0x19,0x00,0x00,0x07}, /*18*/
+ {0x24,0x21,0x40,0x49,0xFF,0xFF,0x0F,0x0F,0x00,0x00,0x01}, /*19*/
+ {0xD2,0xF1,0x44,0x80,0x91,0xA1,0x57,0x09,0x01,0x01,0x03}, /*20*/
+ {0x01,0x02,0x52,0x80,0xF0,0xF0,0x1F,0x1F,0x01,0x00,0x0A}, /*21*/
+ {0x21,0x32,0x4F,0x01,0xF2,0x52,0x0B,0x0B,0x00,0x01,0x0A}, /*22*/
+ {0xF0,0xF2,0x93,0x00,0xD8,0xB3,0x0B,0x0B,0x02,0x01,0x0A}, /*23*/
+ {0x20,0x31,0x5D,0x00,0xF2,0x52,0x0B,0x0B,0x03,0x02,0x00}, /*24*/
+ {0x01,0x01,0x1B,0x00,0xF4,0xF3,0x25,0x46,0x02,0x00,0x00}, /*25*/
+ {0x11,0x01,0x0F,0x00,0xF4,0xF3,0x25,0x46,0x01,0x00,0x00}, /*26*/
+ {0x01,0x01,0x27,0x00,0xF1,0xF4,0x1F,0x88,0x02,0x00,0x0A}, /*27*/
+ {0x12,0x13,0x44,0x00,0xEA,0xD2,0x32,0xE7,0x01,0x01,0x00}, /*28*/
+ {0x30,0x31,0x45,0x00,0xA4,0xF5,0x32,0xE7,0x03,0x00,0x00}, /*29*/
+ {0x21,0x21,0x0F,0x00,0xF5,0xF1,0x17,0x78,0x02,0x01,0x04}, /*30*/
+ {0x01,0x20,0x41,0x00,0xD1,0xC1,0x34,0xA5,0x03,0x03,0x04}, /*31*/
+ {0x10,0x12,0x43,0x00,0xA7,0xE3,0x97,0xE7,0x03,0x02,0x00}, /*32*/
+ {0x20,0x21,0x28,0x00,0xC5,0xD2,0x15,0xA4,0x00,0x00,0x0C}, /*33*/
+ {0x30,0x21,0x16,0x00,0xF2,0xF3,0x9F,0x78,0x00,0x00,0x0C}, /*34*/
+ {0x30,0x21,0x11,0x00,0x82,0xF3,0x9F,0x78,0x00,0x00,0x0A}, /*35*/
+ {0x21,0x21,0x23,0x00,0x73,0x93,0x1A,0x87,0x00,0x00,0x0C}, /*36*/
+ {0x30,0x21,0x0E,0x00,0x62,0xF3,0x55,0x68,0x02,0x00,0x0A}, /*37*/
+ {0x30,0x22,0x0C,0x00,0x62,0xD5,0xB5,0x98,0x01,0x00,0x08}, /*38*/
+ {0x70,0x72,0x93,0x40,0x64,0xA1,0x43,0x43,0x00,0x00,0x0A}, /*39*/
+ {0x30,0x32,0x8D,0x80,0x44,0x92,0x43,0x43,0x02,0x00,0x0A}, /*40*/
+ {0xE1,0xE2,0x4E,0x00,0x65,0x61,0x43,0x44,0x02,0x02,0x00}, /*41*/
+ {0xA1,0xA2,0x8E,0x00,0x65,0x63,0x43,0x45,0x02,0x02,0x00}, /*42*/
+ {0xB0,0x61,0x87,0x40,0xD1,0x62,0x11,0x15,0x02,0x01,0x06}, /*43*/
+ {0xF0,0x20,0x8A,0x80,0xB1,0xA0,0x11,0x15,0x02,0x01,0x06}, /*44*/
+ {0xF1,0xE2,0x89,0x40,0x73,0x43,0x01,0x05,0x02,0x00,0x06}, /*45*/
+ {0x31,0x21,0x57,0x80,0xF8,0xF7,0xF9,0xE6,0x03,0x02,0x0E}, /*46*/
+ {0x32,0x01,0x24,0x80,0xF1,0xF5,0x35,0x35,0x00,0x00,0x00}, /*47*/
+ {0x00,0x00,0x04,0x00,0xAA,0xD2,0xC8,0xB3,0x00,0x00,0x0A}, /*48*/
+ {0xE0,0xF1,0x4F,0x00,0xD4,0x55,0x0B,0x0B,0x02,0x02,0x0A}, /*49*/
+ {0xE0,0xF0,0x52,0x00,0x96,0x35,0x05,0x01,0x02,0x02,0x0A}, /*50*/
+ {0xE1,0xF1,0x4F,0x00,0x36,0x45,0x05,0x02,0x02,0x02,0x0A}, /*51*/
+ {0xE2,0xE1,0x48,0x80,0x21,0x41,0x43,0x45,0x02,0x01,0x00}, /*52*/
+ {0xE0,0xF1,0x16,0x00,0x41,0x20,0x52,0x72,0x02,0x02,0x00}, /*53*/
+ {0xE0,0xF1,0x11,0x00,0x01,0xD0,0x52,0x72,0x02,0x02,0x00}, /*54*/
+ {0xE0,0xF1,0x1A,0x00,0x61,0x30,0x52,0x73,0x00,0x02,0x00}, /*55*/
+ {0x50,0x50,0x0B,0x00,0x84,0xA4,0x4B,0x99,0x00,0x00,0x0A}, /*56*/
+ {0x31,0x61,0x1C,0x80,0x41,0x92,0x0B,0x3B,0x00,0x00,0x0E}, /*57*/
+ {0xB1,0x61,0x1C,0x00,0x41,0x92,0x1F,0x3B,0x00,0x00,0x0E}, /*58*/
+ {0x20,0x21,0x18,0x00,0x52,0xA2,0x15,0x24,0x00,0x00,0x0C}, /*59*/
+ {0xC1,0xC1,0x94,0x80,0x74,0xA3,0xEA,0xF5,0x02,0x01,0x0E}, /*60*/
+ {0x21,0x21,0x28,0x00,0x41,0x81,0xB4,0x98,0x00,0x00,0x0E}, /*61*/
+ {0x21,0x21,0x1D,0x00,0x51,0xE1,0xAE,0x3E,0x02,0x01,0x0E}, /*62*/
+ {0xE0,0xE0,0x93,0x80,0x51,0x81,0xA6,0x97,0x02,0x01,0x0E}, /*63*/
+ {0xE0,0xE1,0x93,0x80,0x51,0xE1,0xA6,0x97,0x02,0x01,0x0E}, /*64*/
+ {0xE0,0xF2,0x4B,0x01,0xD8,0xB3,0x0B,0x0B,0x02,0x01,0x08}, /*65*/
+ {0xE0,0xF1,0x49,0x01,0xB8,0xB3,0x0B,0x0B,0x02,0x01,0x08}, /*66*/
+ {0xE0,0xF0,0x4E,0x01,0x98,0xC3,0x0B,0x0B,0x01,0x02,0x08}, /*67*/
+ {0xE0,0xF1,0x4C,0x01,0x88,0xD3,0x0B,0x0B,0x01,0x01,0x08}, /*68*/
+ {0xF1,0xE4,0xC5,0x00,0x7E,0x8C,0x17,0x0E,0x00,0x00,0x08}, /*69*/
+ {0x60,0x72,0x4F,0x00,0xD8,0xB3,0x0B,0x0B,0x00,0x01,0x0A}, /*70*/
+ {0x31,0x72,0xD1,0x80,0xD5,0x91,0x19,0x1B,0x00,0x00,0x0C}, /*71*/
+ {0x32,0x71,0xC8,0x80,0xD5,0x73,0x19,0x1B,0x00,0x00,0x0C}, /*72*/
+ {0xE2,0x62,0x6A,0x00,0x9E,0x55,0x8F,0x2A,0x00,0x00,0x0E}, /*73*/
+ {0xE0,0x61,0xEC,0x00,0x7E,0x65,0x8F,0x2A,0x00,0x00,0x0E}, /*74*/
+ {0x62,0xA2,0x88,0x83,0x84,0x75,0x27,0x17,0x00,0x00,0x09}, /*75*/
+ {0x62,0xA2,0x84,0x83,0x84,0x75,0x27,0x17,0x00,0x00,0x09}, /*76*/
+ {0xE3,0x62,0x6D,0x00,0x57,0x57,0x04,0x77,0x00,0x00,0x0E}, /*77*/
+ {0xF1,0xE1,0x28,0x00,0x57,0x67,0x34,0x5D,0x03,0x00,0x0E}, /*78*/
+ {0xD1,0x72,0xC7,0x00,0x31,0x42,0x0F,0x09,0x00,0x00,0x0B}, /*79*/
+ {0xF2,0x72,0xC7,0x00,0x51,0x42,0x05,0x69,0x00,0x00,0x0B}, /*80*/
+ {0x23,0x31,0x4F,0x00,0x51,0x60,0x5B,0x25,0x01,0x01,0x00}, /*81*/
+ {0x22,0x31,0x48,0x00,0x31,0xC0,0x9B,0x65,0x02,0x01,0x00}, /*82*/
+ {0xF1,0xE1,0x28,0x00,0x57,0x67,0x34,0x0D,0x03,0x00,0x0E}, /*83*/
+ {0xE1,0xE1,0x23,0x00,0x57,0x67,0x04,0x4D,0x03,0x00,0x0E}, /*84*/
+ {0xE2,0x31,0x42,0x08,0x78,0xF3,0x0B,0x0B,0x01,0x01,0x08}, /*85*/
+ {0xE2,0xE2,0x21,0x00,0x11,0x40,0x52,0x73,0x01,0x01,0x08}, /*86*/
+ {0x23,0xA4,0xC0,0x00,0x51,0x35,0x07,0x79,0x01,0x02,0x0D}, /*87*/
+ {0x24,0xA0,0xC0,0x00,0x51,0x75,0x07,0x09,0x01,0x02,0x09}, /*88*/
+ {0xE0,0xF0,0x16,0x00,0xB1,0xE0,0x51,0x75,0x02,0x02,0x00}, /*89*/
+ {0x03,0xA4,0xC0,0x00,0x52,0xF4,0x03,0x55,0x00,0x00,0x09}, /*90*/
+ {0xE1,0xE1,0x93,0x80,0x31,0xA1,0xA6,0x97,0x01,0x01,0x0A}, /*91*/
+ {0xF0,0x71,0xC4,0x80,0x10,0x11,0x01,0xC1,0x02,0x02,0x01}, /*92*/
+ {0xC1,0xE0,0x4F,0x00,0xB1,0x12,0x53,0x74,0x02,0x02,0x06}, /*93*/
+ {0xC0,0x41,0x6D,0x00,0xF9,0xF2,0x21,0xB3,0x01,0x00,0x0E}, /*94*/
+ {0xE3,0xE2,0x4C,0x00,0x21,0xA1,0x43,0x45,0x01,0x01,0x00}, /*95*/
+ {0xE3,0xE2,0x0C,0x00,0x11,0x80,0x52,0x73,0x01,0x01,0x08}, /*96*/
+ {0x26,0x88,0xC0,0x00,0x55,0xF8,0x47,0x19,0x00,0x00,0x0B}, /*97*/
+ {0x23,0xE4,0xD4,0x00,0xE5,0x35,0x03,0x65,0x00,0x00,0x07}, /*98*/
+ {0x27,0x32,0xC0,0x00,0x32,0xA4,0x62,0x33,0x00,0x00,0x00}, /*99*/
+ {0xD0,0x31,0x4E,0x00,0x98,0xA2,0x32,0x47,0x01,0x02,0x00}, /*100*/
+ {0xF0,0x71,0xC0,0x00,0x93,0x43,0x03,0x02,0x01,0x00,0x0F}, /*101*/
+ {0xE0,0xF1,0x1A,0x80,0x13,0x33,0x52,0x13,0x01,0x02,0x00}, /*102*/
+ {0xE0,0xF1,0x1A,0x00,0x45,0x32,0xBA,0x91,0x00,0x02,0x00}, /*103*/
+ {0x11,0x15,0x18,0x03,0x58,0xA2,0x02,0x72,0x01,0x00,0x0A}, /*104*/
+ {0x10,0x18,0x80,0x40,0xF1,0xF1,0x53,0x53,0x00,0x00,0x00}, /*105*/
+ {0x31,0x17,0x86,0x80,0xA1,0x7D,0x11,0x23,0x00,0x00,0x08}, /*106*/
+ {0x10,0x18,0x80,0x40,0xF1,0xF6,0x53,0x54,0x00,0x00,0x00}, /*107*/
+ {0x31,0x34,0x21,0x00,0xF5,0x93,0x56,0xE8,0x01,0x00,0x08}, /*108*/
+ {0x03,0x15,0x4F,0x00,0xF1,0xD6,0x39,0x74,0x03,0x00,0x06}, /*109*/
+ {0x31,0x22,0x43,0x00,0x6E,0x8B,0x17,0x0C,0x01,0x02,0x02}, /*110*/
+ {0x31,0x22,0x1C,0x80,0x61,0x52,0x03,0x67,0x00,0x00,0x0E}, /*111*/
+ {0x60,0xF0,0x0C,0x80,0x81,0x61,0x03,0x0C,0x00,0x01,0x08}, /*112*/
+ {0x27,0x05,0x55,0x00,0x31,0xA7,0x62,0x75,0x00,0x00,0x00}, /*113*/
+ {0x95,0x16,0x81,0x00,0xE7,0x96,0x01,0x67,0x00,0x00,0x04}, /*114*/
+ {0x0C,0x01,0x87,0x80,0xF0,0xF2,0x05,0x05,0x01,0x01,0x04}, /*115*/
+ {0x35,0x11,0x44,0x00,0xF8,0xF5,0xFF,0x75,0x00,0x00,0x0E}, /*116*/
+ {0x10,0x10,0x0B,0x00,0xA7,0xD5,0xEC,0xF5,0x00,0x00,0x00}, /*117*/
+ {0x20,0x01,0x0B,0x00,0xA8,0xD6,0xC8,0xB7,0x00,0x00,0x00}, /*118*/
+ {0x00,0x01,0x0B,0x00,0x88,0xD5,0xC4,0xB7,0x00,0x00,0x00}, /*119*/
+ {0x0C,0x10,0x8F,0x80,0x41,0x33,0x31,0x2B,0x00,0x03,0x08}, /*120*/
+ {0x17,0xF7,0x00,0x00,0x3B,0xEA,0xDF,0x97,0x03,0x00,0x0B}, /*121*/
+ {0x12,0x18,0x06,0x00,0x73,0x3C,0x02,0x74,0x00,0x00,0x0E}, /*122*/
+ {0x02,0x08,0x00,0x00,0x3E,0x14,0x01,0xF3,0x02,0x02,0x0E}, /*123*/
+ {0xF5,0xF6,0xD4,0x00,0xEB,0x45,0x03,0x68,0x00,0x00,0x07}, /*124*/
+ {0xF0,0xCA,0x00,0xC0,0xDA,0xB0,0x71,0x17,0x01,0x01,0x08}, /*125*/
+ {0xF0,0xE2,0x00,0xC0,0x1E,0x11,0x11,0x11,0x01,0x01,0x08}, /*126*/
+ {0xE7,0xE8,0x00,0x04,0x34,0x10,0x00,0xB2,0x02,0x02,0x0E}, /*127*/
+ {0x0C,0x04,0x00,0x00,0xF0,0xF6,0xF0,0xE6,0x02,0x00,0x0E}, /*128*/
+};
+
+void adlib_patch_apply(song_sample_t *smp, int patchnum)
+{
+ if (patchnum < 0 || patchnum > 127) {
+ printf("adlib_patch_apply: invalid patch %d\n", patchnum);
+ return;
+ }
+ memcpy(smp->adlib_bytes, patches[patchnum], 11);
+ strncpy(smp->name, midi_program_names[patchnum], sizeof(smp->name) - 1);
+ smp->name[sizeof(smp->name) - 1] = '\0'; // Paranoid.
+ sprintf(smp->filename, "MIDI#%03d", patchnum + 1);
+ smp->flags |= CHN_ADLIB;
+ if (smp->data) {
+ csf_free_sample(smp->data);
+ smp->data = NULL;
+ }
+ smp->length = 1;
+ smp->data = csf_allocate_sample(1);
+}
+
diff --git a/src/player/mixer.c b/src/player/mixer.c
new file mode 100644
index 0000000..2f3ddf1
--- /dev/null
+++ b/src/player/mixer.c
@@ -0,0 +1,1555 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdint.h>
+#include <math.h>
+
+#include "sndfile.h"
+#include "snd_fm.h"
+#include "cmixer.h"
+#include "util.h" // for CLAMP
+
+// For pingpong loops that work like most of Impulse Tracker's drivers
+// (including SB16, SBPro, and the disk writer) -- as well as XMPlay, use 2
+// To make them sound like the GUS driver, use 1.
+// It's really only noticeable for very small loops... (e.g. chip samples)
+// (thanks Saga_Musix for this)
+#define PINGPONG_OFFSET 2
+
+
+
+/* The following lut settings are PRECOMPUTED.
+ *
+ * If you plan on changing these settings, you
+ * MUST also regenerate the arrays.
+ */
+// number of bits used to scale spline coefs
+#define SPLINE_QUANTBITS 14
+#define SPLINE_QUANTSCALE (1L << SPLINE_QUANTBITS)
+#define SPLINE_8SHIFT (SPLINE_QUANTBITS - 8)
+#define SPLINE_16SHIFT (SPLINE_QUANTBITS)
+
+// forces coefsset to unity gain
+#define SPLINE_CLAMPFORUNITY
+
+// log2(number) of precalculated splines (range is [4..14])
+#define SPLINE_FRACBITS 10
+#define SPLINE_LUTLEN (1L<<SPLINE_FRACBITS)
+
+
+// quantizer scale of window coefs
+#define WFIR_QUANTBITS 15
+#define WFIR_QUANTSCALE (1L << WFIR_QUANTBITS)
+#define WFIR_8SHIFT (WFIR_QUANTBITS - 8)
+#define WFIR_16BITSHIFT (WFIR_QUANTBITS)
+
+// log2(number)-1 of precalculated taps range is [4..12]
+#define WFIR_FRACBITS 10
+#define WFIR_LUTLEN ((1L << (WFIR_FRACBITS + 1)) + 1)
+
+// number of samples in window
+#define WFIR_LOG2WIDTH 3
+#define WFIR_WIDTH (1L << WFIR_LOG2WIDTH)
+#define WFIR_SMPSPERWING ((WFIR_WIDTH-1)>>1)
+// cutoff (1.0 == pi/2)
+#define WFIR_CUTOFF 0.90f
+// wfir type
+#define WFIR_HANN 0
+#define WFIR_HAMMING 1
+#define WFIR_BLACKMANEXACT 2
+#define WFIR_BLACKMAN3T61 3
+#define WFIR_BLACKMAN3T67 4
+#define WFIR_BLACKMAN4T92 5
+#define WFIR_BLACKMAN4T74 6
+#define WFIR_KAISER4T 7
+#define WFIR_TYPE WFIR_BLACKMANEXACT
+// wfir help
+#ifndef M_zPI
+#define M_zPI 3.1415926535897932384626433832795
+#endif
+#define M_zEPS 1e-8
+#define M_zBESSELEPS 1e-21
+
+
+#include "precomp_lut.h"
+
+
+// ----------------------------------------------------------------------------
+// MIXING MACROS
+// ----------------------------------------------------------------------------
+
+#define SNDMIX_BEGINSAMPLELOOP8 \
+ register song_voice_t * const chan = channel; \
+ position = chan->position_frac; \
+ const signed char *p = (signed char *)(chan->current_sample_data + chan->position); \
+ if (chan->flags & CHN_STEREO) p += chan->position; \
+ int *pvol = pbuffer;\
+ do {
+
+
+#define SNDMIX_BEGINSAMPLELOOP16\
+ register song_voice_t * const chan = channel;\
+ position = chan->position_frac;\
+ const signed short *p = (signed short *)(chan->current_sample_data+(chan->position*2));\
+ if (chan->flags & CHN_STEREO) p += chan->position;\
+ int *pvol = pbuffer;\
+ do {
+
+
+#define SNDMIX_ENDSAMPLELOOP \
+ position += chan->increment; \
+ } while (pvol < pbufmax); \
+ chan->position += position >> 16; \
+ chan->position_frac = position & 0xFFFF;
+
+
+#define SNDMIX_ENDSAMPLELOOP8 SNDMIX_ENDSAMPLELOOP
+#define SNDMIX_ENDSAMPLELOOP16 SNDMIX_ENDSAMPLELOOP
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Mono
+
+// No interpolation
+#define SNDMIX_GETMONOVOL8NOIDO \
+ int vol = p[position >> 16] << 8;
+
+
+#define SNDMIX_GETMONOVOL16NOIDO \
+ int vol = p[position >> 16];
+
+
+// Linear Interpolation
+#define SNDMIX_GETMONOVOL8LINEAR \
+ int poshi = position >> 16; \
+ int poslo = (position >> 8) & 0xFF; \
+ int srcvol = p[poshi]; \
+ int destvol = p[poshi+1]; \
+ int vol = (srcvol<<8) + ((int)(poslo * (destvol - srcvol)));
+
+
+#define SNDMIX_GETMONOVOL16LINEAR \
+ int poshi = position >> 16; \
+ int poslo = (position >> 8) & 0xFF; \
+ int srcvol = p[poshi]; \
+ int destvol = p[poshi + 1]; \
+ int vol = srcvol + ((int)(poslo * (destvol - srcvol)) >> 8);
+
+
+// spline interpolation (2 guard bits should be enough???)
+#define SPLINE_FRACSHIFT ((16 - SPLINE_FRACBITS) - 2)
+#define SPLINE_FRACMASK (((1L << (16 - SPLINE_FRACSHIFT)) - 1) & ~3)
+
+
+#define SNDMIX_GETMONOVOL8SPLINE \
+ int poshi = position >> 16; \
+ int poslo = (position >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \
+ int vol = (cubic_spline_lut[poslo ] * (int)p[poshi - 1] + \
+ cubic_spline_lut[poslo + 1] * (int)p[poshi ] + \
+ cubic_spline_lut[poslo + 3] * (int)p[poshi + 2] + \
+ cubic_spline_lut[poslo + 2] * (int)p[poshi + 1]) >> SPLINE_8SHIFT;
+
+
+#define SNDMIX_GETMONOVOL16SPLINE \
+ int poshi = position >> 16; \
+ int poslo = (position >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \
+ int vol = (cubic_spline_lut[poslo ] * (int)p[poshi - 1] + \
+ cubic_spline_lut[poslo + 1] * (int)p[poshi ] + \
+ cubic_spline_lut[poslo + 3] * (int)p[poshi + 2] + \
+ cubic_spline_lut[poslo + 2] * (int)p[poshi + 1]) >> SPLINE_16SHIFT;
+
+
+// fir interpolation
+#define WFIR_FRACSHIFT (16 - (WFIR_FRACBITS + 1 + WFIR_LOG2WIDTH))
+#define WFIR_FRACMASK ((((1L << (17 - WFIR_FRACSHIFT)) - 1) & ~((1L << WFIR_LOG2WIDTH) - 1)))
+#define WFIR_FRACHALVE (1L << (16 - (WFIR_FRACBITS + 2)))
+
+
+#define SNDMIX_GETMONOVOL8FIRFILTER \
+ int poshi = position >> 16;\
+ int poslo = (position & 0xFFFF);\
+ int firidx = ((poslo + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK; \
+ int vol = (windowed_fir_lut[firidx + 0] * (int)p[poshi + 1 - 4]); \
+ vol += (windowed_fir_lut[firidx + 1] * (int)p[poshi + 2 - 4]); \
+ vol += (windowed_fir_lut[firidx + 2] * (int)p[poshi + 3 - 4]); \
+ vol += (windowed_fir_lut[firidx + 3] * (int)p[poshi + 4 - 4]); \
+ vol += (windowed_fir_lut[firidx + 4] * (int)p[poshi + 5 - 4]); \
+ vol += (windowed_fir_lut[firidx + 5] * (int)p[poshi + 6 - 4]); \
+ vol += (windowed_fir_lut[firidx + 6] * (int)p[poshi + 7 - 4]); \
+ vol += (windowed_fir_lut[firidx + 7] * (int)p[poshi + 8 - 4]); \
+ vol >>= WFIR_8SHIFT;
+
+
+#define SNDMIX_GETMONOVOL16FIRFILTER \
+ int poshi = position >> 16;\
+ int poslo = (position & 0xFFFF);\
+ int firidx = ((poslo + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK; \
+ int vol1 = (windowed_fir_lut[firidx + 0] * (int)p[poshi + 1 - 4]); \
+ vol1 += (windowed_fir_lut[firidx + 1] * (int)p[poshi + 2 - 4]); \
+ vol1 += (windowed_fir_lut[firidx + 2] * (int)p[poshi + 3 - 4]); \
+ vol1 += (windowed_fir_lut[firidx + 3] * (int)p[poshi + 4 - 4]); \
+ int vol2 = (windowed_fir_lut[firidx + 4] * (int)p[poshi + 5 - 4]); \
+ vol2 += (windowed_fir_lut[firidx + 5] * (int)p[poshi + 6 - 4]); \
+ vol2 += (windowed_fir_lut[firidx + 6] * (int)p[poshi + 7 - 4]); \
+ vol2 += (windowed_fir_lut[firidx + 7] * (int)p[poshi + 8 - 4]); \
+ int vol = ((vol1 >> 1) + (vol2 >> 1)) >> (WFIR_16BITSHIFT - 1);
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Stereo
+
+// No interpolation
+#define SNDMIX_GETSTEREOVOL8NOIDO \
+ int vol_l = p[(position >> 16) * 2 ] << 8; \
+ int vol_r = p[(position >> 16) * 2 + 1] << 8;
+
+
+#define SNDMIX_GETSTEREOVOL16NOIDO \
+ int vol_l = p[(position >> 16) * 2 ]; \
+ int vol_r = p[(position >> 16) * 2 + 1];
+
+
+// Linear Interpolation
+#define SNDMIX_GETSTEREOVOL8LINEAR \
+ int poshi = position >> 16; \
+ int poslo = (position >> 8) & 0xFF; \
+ int srcvol_l = p[poshi * 2]; \
+ int vol_l = (srcvol_l << 8) + ((int)(poslo * (p[poshi * 2 + 2] - srcvol_l))); \
+ int srcvol_r = p[poshi * 2 + 1]; \
+ int vol_r = (srcvol_r << 8) + ((int)(poslo * (p[poshi * 2 + 3] - srcvol_r)));
+
+
+#define SNDMIX_GETSTEREOVOL16LINEAR \
+ int poshi = position >> 16; \
+ int poslo = (position >> 8) & 0xFF; \
+ int srcvol_l = p[poshi * 2]; \
+ int vol_l = srcvol_l + ((int)(poslo * (p[poshi * 2 + 2] - srcvol_l)) >> 8);\
+ int srcvol_r = p[poshi * 2 + 1];\
+ int vol_r = srcvol_r + ((int)(poslo * (p[poshi * 2 + 3] - srcvol_r)) >> 8);\
+
+
+// Spline Interpolation
+#define SNDMIX_GETSTEREOVOL8SPLINE \
+ int poshi = position >> 16; \
+ int poslo = (position >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \
+ int vol_l = (cubic_spline_lut[poslo ] * (int)p[(poshi - 1) * 2 ] + \
+ cubic_spline_lut[poslo + 1] * (int)p[(poshi ) * 2 ] + \
+ cubic_spline_lut[poslo + 2] * (int)p[(poshi + 1) * 2 ] + \
+ cubic_spline_lut[poslo + 3] * (int)p[(poshi + 2) * 2 ]) >> SPLINE_8SHIFT; \
+ int vol_r = (cubic_spline_lut[poslo ] * (int)p[(poshi - 1) * 2 + 1] + \
+ cubic_spline_lut[poslo + 1] * (int)p[(poshi ) * 2 + 1] + \
+ cubic_spline_lut[poslo + 2] * (int)p[(poshi + 1) * 2 + 1] + \
+ cubic_spline_lut[poslo + 3] * (int)p[(poshi + 2) * 2 + 1]) >> SPLINE_8SHIFT;
+
+
+#define SNDMIX_GETSTEREOVOL16SPLINE \
+ int poshi = position >> 16; \
+ int poslo = (position >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \
+ int vol_l = (cubic_spline_lut[poslo ] * (int)p[(poshi - 1) * 2 ] + \
+ cubic_spline_lut[poslo + 1] * (int)p[(poshi ) * 2 ] + \
+ cubic_spline_lut[poslo + 2] * (int)p[(poshi + 1) * 2 ] + \
+ cubic_spline_lut[poslo + 3] * (int)p[(poshi + 2) * 2 ]) >> SPLINE_16SHIFT; \
+ int vol_r = (cubic_spline_lut[poslo ] * (int)p[(poshi - 1) * 2 + 1] + \
+ cubic_spline_lut[poslo + 1] * (int)p[(poshi ) * 2 + 1] + \
+ cubic_spline_lut[poslo + 2] * (int)p[(poshi + 1) * 2 + 1] + \
+ cubic_spline_lut[poslo + 3] * (int)p[(poshi + 2) * 2 + 1]) >> SPLINE_16SHIFT;
+
+
+// fir interpolation
+#define SNDMIX_GETSTEREOVOL8FIRFILTER \
+ int poshi = position >> 16;\
+ int poslo = (position & 0xFFFF);\
+ int firidx = ((poslo + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK; \
+ int vol_l = (windowed_fir_lut[firidx + 0] * (int)p[(poshi + 1 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 1] * (int)p[(poshi + 2 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 2] * (int)p[(poshi + 3 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 3] * (int)p[(poshi + 4 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 4] * (int)p[(poshi + 5 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 5] * (int)p[(poshi + 6 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 6] * (int)p[(poshi + 7 - 4) * 2]); \
+ vol_l += (windowed_fir_lut[firidx + 7] * (int)p[(poshi + 8 - 4) * 2]); \
+ vol_l >>= WFIR_8SHIFT; \
+ int vol_r = (windowed_fir_lut[firidx + 0] * (int)p[(poshi + 1 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 1] * (int)p[(poshi + 2 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 2] * (int)p[(poshi + 3 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 3] * (int)p[(poshi + 4 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 4] * (int)p[(poshi + 5 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 5] * (int)p[(poshi + 6 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 6] * (int)p[(poshi + 7 - 4) * 2 + 1]); \
+ vol_r += (windowed_fir_lut[firidx + 7] * (int)p[(poshi + 8 - 4) * 2 + 1]); \
+ vol_r >>= WFIR_8SHIFT;
+
+
+#define SNDMIX_GETSTEREOVOL16FIRFILTER \
+ int poshi = position >> 16;\
+ int poslo = (position & 0xFFFF);\
+ int firidx = ((poslo + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK; \
+ int vol1_l = (windowed_fir_lut[firidx + 0] * (int)p[(poshi + 1 - 4) * 2]); \
+ vol1_l += (windowed_fir_lut[firidx + 1] * (int)p[(poshi + 2 - 4) * 2]); \
+ vol1_l += (windowed_fir_lut[firidx + 2] * (int)p[(poshi + 3 - 4) * 2]); \
+ vol1_l += (windowed_fir_lut[firidx + 3] * (int)p[(poshi + 4 - 4) * 2]); \
+ int vol2_l = (windowed_fir_lut[firidx + 4] * (int)p[(poshi + 5 - 4) * 2]); \
+ vol2_l += (windowed_fir_lut[firidx + 5] * (int)p[(poshi + 6 - 4) * 2]); \
+ vol2_l += (windowed_fir_lut[firidx + 6] * (int)p[(poshi + 7 - 4) * 2]); \
+ vol2_l += (windowed_fir_lut[firidx + 7] * (int)p[(poshi + 8 - 4) * 2]); \
+ int vol_l = ((vol1_l >> 1) + (vol2_l >> 1)) >> (WFIR_16BITSHIFT - 1); \
+ int vol1_r = (windowed_fir_lut[firidx + 0] * (int)p[(poshi + 1 - 4) * 2 + 1]); \
+ vol1_r += (windowed_fir_lut[firidx + 1] * (int)p[(poshi + 2 - 4) * 2 + 1]); \
+ vol1_r += (windowed_fir_lut[firidx + 2] * (int)p[(poshi + 3 - 4) * 2 + 1]); \
+ vol1_r += (windowed_fir_lut[firidx + 3] * (int)p[(poshi + 4 - 4) * 2 + 1]); \
+ int vol2_r = (windowed_fir_lut[firidx + 4] * (int)p[(poshi + 5 - 4) * 2 + 1]); \
+ vol2_r += (windowed_fir_lut[firidx + 5] * (int)p[(poshi + 6 - 4) * 2 + 1]); \
+ vol2_r += (windowed_fir_lut[firidx + 6] * (int)p[(poshi + 7 - 4) * 2 + 1]); \
+ vol2_r += (windowed_fir_lut[firidx + 7] * (int)p[(poshi + 8 - 4) * 2 + 1]); \
+ int vol_r = ((vol1_r >> 1) + (vol2_r >> 1)) >> (WFIR_16BITSHIFT - 1);
+
+
+#define SNDMIX_STOREMONOVOL \
+ pvol[0] += vol * chan->right_volume; \
+ pvol[1] += vol * chan->left_volume; \
+ pvol += 2;
+
+
+#define SNDMIX_STORESTEREOVOL \
+ pvol[0] += vol_l * chan->right_volume; \
+ pvol[1] += vol_r * chan->left_volume; \
+ pvol += 2;
+
+
+#define SNDMIX_STOREFASTMONOVOL \
+ int v = vol * chan->right_volume; \
+ pvol[0] += v; \
+ pvol[1] += v; \
+ pvol += 2;
+
+
+#define SNDMIX_RAMPMONOVOL \
+ left_ramp_volume += chan->left_ramp; \
+ right_ramp_volume += chan->right_ramp; \
+ pvol[0] += vol * (right_ramp_volume >> VOLUMERAMPPRECISION); \
+ pvol[1] += vol * (left_ramp_volume >> VOLUMERAMPPRECISION); \
+ pvol += 2;
+
+
+#define SNDMIX_RAMPFASTMONOVOL \
+ right_ramp_volume += chan->right_ramp; \
+ int fastvol = vol * (right_ramp_volume >> VOLUMERAMPPRECISION); \
+ pvol[0] += fastvol; \
+ pvol[1] += fastvol; \
+ pvol += 2;
+
+
+#define SNDMIX_RAMPSTEREOVOL \
+ left_ramp_volume += chan->left_ramp; \
+ right_ramp_volume += chan->right_ramp; \
+ pvol[0] += vol_l * (right_ramp_volume >> VOLUMERAMPPRECISION); \
+ pvol[1] += vol_r * (left_ramp_volume >> VOLUMERAMPPRECISION); \
+ pvol += 2;
+
+
+///////////////////////////////////////////////////
+// Resonant Filters
+
+#define FILT_CLIP(i) CLAMP(i, -65536, 65534)
+
+// Mono
+#define MIX_BEGIN_FILTER \
+ int32_t fy1 = channel->filter_y1; \
+ int32_t fy2 = channel->filter_y2; \
+ int32_t ta;
+
+
+#define MIX_END_FILTER \
+ channel->filter_y1 = fy1; \
+ channel->filter_y2 = fy2;
+
+
+#define SNDMIX_PROCESSFILTER \
+ ta = (vol * chan->filter_a0 + FILT_CLIP(fy1) * chan->filter_b0 + FILT_CLIP(fy2) * chan->filter_b1 \
+ + (1 << (FILTERPRECISION - 1))) >> FILTERPRECISION; \
+ fy2 = fy1; \
+ fy1 = ta; \
+ vol = ta;
+
+
+// Stereo
+#define MIX_BEGIN_STEREO_FILTER \
+ int32_t fy1 = channel->filter_y1; \
+ int32_t fy2 = channel->filter_y2; \
+ int32_t fy3 = channel->filter_y3; \
+ int32_t fy4 = channel->filter_y4; \
+ int32_t ta, tb;
+
+
+#define MIX_END_STEREO_FILTER \
+ channel->filter_y1 = fy1; \
+ channel->filter_y2 = fy2; \
+ channel->filter_y3 = fy3; \
+ channel->filter_y4 = fy4; \
+
+
+#define SNDMIX_PROCESSSTEREOFILTER \
+ ta = (vol_l * chan->filter_a0 + FILT_CLIP(fy1) * chan->filter_b0 + FILT_CLIP(fy2) * chan->filter_b1 \
+ + (1 << (FILTERPRECISION - 1))) >> FILTERPRECISION; \
+ tb = (vol_r * chan->filter_a0 + FILT_CLIP(fy3) * chan->filter_b0 + FILT_CLIP(fy4) * chan->filter_b1 \
+ + (1 << (FILTERPRECISION - 1))) >> FILTERPRECISION; \
+ fy2 = fy1; fy1 = ta; vol_l = ta; \
+ fy4 = fy3; fy3 = tb; vol_r = tb;
+
+
+//////////////////////////////////////////////////////////
+// Interfaces
+
+typedef void(* mix_interface_t)(song_voice_t *, int *, int *);
+
+
+#define BEGIN_MIX_INTERFACE(func) \
+ static void func(song_voice_t *channel, int *pbuffer, int *pbufmax) \
+ { \
+ int position;
+
+
+#define END_MIX_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ }
+
+
+// Volume Ramps
+#define BEGIN_RAMPMIX_INTERFACE(func) \
+ BEGIN_MIX_INTERFACE(func) \
+ int right_ramp_volume = channel->right_ramp_volume; \
+ int left_ramp_volume = channel->left_ramp_volume;
+
+
+#define END_RAMPMIX_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ channel->right_ramp_volume = right_ramp_volume; \
+ channel->right_volume = right_ramp_volume >> VOLUMERAMPPRECISION; \
+ channel->left_ramp_volume = left_ramp_volume; \
+ channel->left_volume = left_ramp_volume >> VOLUMERAMPPRECISION; \
+ }
+
+
+#define BEGIN_FASTRAMPMIX_INTERFACE(func) \
+ BEGIN_MIX_INTERFACE(func) \
+ int right_ramp_volume = channel->right_ramp_volume;
+
+
+#define END_FASTRAMPMIX_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ channel->right_ramp_volume = right_ramp_volume; \
+ channel->left_ramp_volume = right_ramp_volume; \
+ channel->right_volume = right_ramp_volume >> VOLUMERAMPPRECISION; \
+ channel->left_volume = channel->right_volume; \
+ }
+
+
+// Mono Resonant Filters
+#define BEGIN_MIX_FLT_INTERFACE(func) \
+ BEGIN_MIX_INTERFACE(func) \
+ MIX_BEGIN_FILTER
+
+
+#define END_MIX_FLT_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ MIX_END_FILTER \
+ }
+
+
+#define BEGIN_RAMPMIX_FLT_INTERFACE(func) \
+ BEGIN_MIX_INTERFACE(func) \
+ int right_ramp_volume = channel->right_ramp_volume; \
+ int left_ramp_volume = channel->left_ramp_volume; \
+ MIX_BEGIN_FILTER
+
+
+#define END_RAMPMIX_FLT_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ MIX_END_FILTER \
+ channel->right_ramp_volume = right_ramp_volume; \
+ channel->right_volume = right_ramp_volume >> VOLUMERAMPPRECISION; \
+ channel->left_ramp_volume = left_ramp_volume; \
+ channel->left_volume = left_ramp_volume >> VOLUMERAMPPRECISION; \
+ }
+
+
+// Stereo Resonant Filters
+#define BEGIN_MIX_STFLT_INTERFACE(func) \
+ BEGIN_MIX_INTERFACE(func) \
+ MIX_BEGIN_STEREO_FILTER
+
+
+#define END_MIX_STFLT_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ MIX_END_STEREO_FILTER \
+ }
+
+
+#define BEGIN_RAMPMIX_STFLT_INTERFACE(func) \
+ BEGIN_MIX_INTERFACE(func) \
+ int right_ramp_volume = channel->right_ramp_volume; \
+ int left_ramp_volume = channel->left_ramp_volume; \
+ MIX_BEGIN_STEREO_FILTER
+
+
+#define END_RAMPMIX_STFLT_INTERFACE() \
+ SNDMIX_ENDSAMPLELOOP \
+ MIX_END_STEREO_FILTER \
+ channel->right_ramp_volume = right_ramp_volume; \
+ channel->right_volume = right_ramp_volume >> VOLUMERAMPPRECISION; \
+ channel->left_ramp_volume = left_ramp_volume; \
+ channel->left_volume = left_ramp_volume >> VOLUMERAMPPRECISION; \
+ }
+
+#define BEGIN_RESAMPLE_INTERFACE(func, sampletype, numchannels) \
+ void func(sampletype *oldbuf, sampletype *newbuf, unsigned long oldlen, unsigned long newlen) \
+ { \
+ unsigned long long position = 0; \
+ const sampletype *p = oldbuf; \
+ sampletype *pvol = newbuf; \
+ const sampletype *pbufmax = &newbuf[newlen* numchannels]; \
+ unsigned long long increment = (((unsigned long long)oldlen)<<16)/((unsigned long long)newlen); \
+ do {
+
+#define END_RESAMPLE_INTERFACEMONO() \
+ *pvol = vol; \
+ pvol++; \
+ position += increment; \
+ } while (pvol < pbufmax); \
+ }
+
+#define END_RESAMPLE_INTERFACESTEREO() \
+ pvol[0] = vol_l; \
+ pvol[1] = vol_r; \
+ pvol += 2; \
+ position += increment; \
+ } while (pvol < pbufmax); \
+ }
+
+
+
+/////////////////////////////////////////////////////
+// Mono samples functions
+
+BEGIN_MIX_INTERFACE(Mono8BitMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8NOIDO
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono16BitMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16NOIDO
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono8BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8LINEAR
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono16BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16LINEAR
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono8BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8SPLINE
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono16BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16SPLINE
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono8BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8FIRFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Mono16BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16FIRFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_INTERFACE()
+
+
+// Volume Ramps
+BEGIN_RAMPMIX_INTERFACE(Mono8BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8NOIDO
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono16BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16NOIDO
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono8BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8LINEAR
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono16BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16LINEAR
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono8BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8SPLINE
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono16BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16SPLINE
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono8BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8FIRFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Mono16BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16FIRFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_INTERFACE()
+
+
+//////////////////////////////////////////////////////
+// Fast mono mix for leftvol=rightvol (1 less imul)
+
+BEGIN_MIX_INTERFACE(FastMono8BitMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8NOIDO
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono16BitMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16NOIDO
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono8BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8LINEAR
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono16BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16LINEAR
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono8BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8SPLINE
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono16BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16SPLINE
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono8BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8FIRFILTER
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(FastMono16BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16FIRFILTER
+ SNDMIX_STOREFASTMONOVOL
+END_MIX_INTERFACE()
+
+
+// Fast Ramps
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8NOIDO
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16NOIDO
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8LINEAR
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16LINEAR
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8SPLINE
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16SPLINE
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8FIRFILTER
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16FIRFILTER
+ SNDMIX_RAMPFASTMONOVOL
+END_FASTRAMPMIX_INTERFACE()
+
+
+//////////////////////////////////////////////////////
+// Stereo samples
+BEGIN_MIX_INTERFACE(Stereo8BitMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8NOIDO
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo16BitMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16NOIDO
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo8BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8LINEAR
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo16BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16LINEAR
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo8BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8SPLINE
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo16BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16SPLINE
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo8BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8FIRFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+BEGIN_MIX_INTERFACE(Stereo16BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16FIRFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_INTERFACE()
+
+
+// Volume Ramps
+BEGIN_RAMPMIX_INTERFACE(Stereo8BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8NOIDO
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo16BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16NOIDO
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo8BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8LINEAR
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo16BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16LINEAR
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo8BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8SPLINE
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo16BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16SPLINE
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo8BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8FIRFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+BEGIN_RAMPMIX_INTERFACE(Stereo16BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16FIRFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_INTERFACE()
+
+
+//////////////////////////////////////////////////////
+// Resonant Filter Mix
+// Mono Filter Mix
+BEGIN_MIX_FLT_INTERFACE(FilterMono8BitMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8NOIDO
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono16BitMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16NOIDO
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono8BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8LINEAR
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono16BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16LINEAR
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono8BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8SPLINE
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono16BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16SPLINE
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono8BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8FIRFILTER
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+BEGIN_MIX_FLT_INTERFACE(FilterMono16BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16FIRFILTER
+ SNDMIX_PROCESSFILTER
+ SNDMIX_STOREMONOVOL
+END_MIX_FLT_INTERFACE()
+
+
+// Filter + Ramp
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8NOIDO
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16NOIDO
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8LINEAR
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16LINEAR
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8SPLINE
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16SPLINE
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETMONOVOL8FIRFILTER
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETMONOVOL16FIRFILTER
+ SNDMIX_PROCESSFILTER
+ SNDMIX_RAMPMONOVOL
+END_RAMPMIX_FLT_INTERFACE()
+
+
+// Stereo Filter Mix
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8NOIDO
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16NOIDO
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8LINEAR
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitLinearMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16LINEAR
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8SPLINE
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitSplineMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16SPLINE
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8FIRFILTER
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitFirFilterMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16FIRFILTER
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_STORESTEREOVOL
+END_MIX_STFLT_INTERFACE()
+
+
+// Stereo Filter + Ramp
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8NOIDO
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16NOIDO
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8LINEAR
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitLinearRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16LINEAR
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8SPLINE
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitSplineRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16SPLINE
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP8
+ SNDMIX_GETSTEREOVOL8FIRFILTER
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitFirFilterRampMix)
+ SNDMIX_BEGINSAMPLELOOP16
+ SNDMIX_GETSTEREOVOL16FIRFILTER
+ SNDMIX_PROCESSSTEREOFILTER
+ SNDMIX_RAMPSTEREOVOL
+END_RAMPMIX_STFLT_INTERFACE()
+
+
+
+// Public resampling Methods (
+BEGIN_RESAMPLE_INTERFACE(ResampleMono8BitFirFilter, signed char, 1)
+ SNDMIX_GETMONOVOL8FIRFILTER
+ vol >>= (WFIR_16BITSHIFT-WFIR_8SHIFT); //This is used to compensate, since the code assumes that it always outputs to 16bits
+ vol = CLAMP(vol,-128,127);
+END_RESAMPLE_INTERFACEMONO()
+
+BEGIN_RESAMPLE_INTERFACE(ResampleMono16BitFirFilter, signed short, 1)
+ SNDMIX_GETMONOVOL16FIRFILTER
+ vol = CLAMP(vol,-32768,32767);
+END_RESAMPLE_INTERFACEMONO()
+
+BEGIN_RESAMPLE_INTERFACE(ResampleStereo8BitFirFilter, signed char, 2)
+ SNDMIX_GETSTEREOVOL8FIRFILTER
+ vol_l >>= (WFIR_16BITSHIFT-WFIR_8SHIFT); //This is used to compensate, since the code assumes that it always outputs to 16bits
+ vol_r >>= (WFIR_16BITSHIFT-WFIR_8SHIFT); //This is used to compensate, since the code assumes that it always outputs to 16bits
+ vol_l = CLAMP(vol_l,-128,127);
+ vol_r = CLAMP(vol_r,-128,127);
+END_RESAMPLE_INTERFACESTEREO()
+
+BEGIN_RESAMPLE_INTERFACE(ResampleStereo16BitFirFilter, signed short, 2)
+ SNDMIX_GETSTEREOVOL16FIRFILTER
+ vol_l = CLAMP(vol_l,-32768,32767);
+ vol_r = CLAMP(vol_r,-32768,32767);
+END_RESAMPLE_INTERFACESTEREO()
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+//
+// Mix function tables
+//
+//
+// Index is as follow:
+// [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo)
+// [b2] ramp
+// [b3] filter
+// [b5-b4] src type
+//
+#define MIXNDX_16BIT 0x01
+#define MIXNDX_STEREO 0x02
+#define MIXNDX_RAMP 0x04
+#define MIXNDX_FILTER 0x08
+#define MIXNDX_LINEARSRC 0x10
+#define MIXNDX_SPLINESRC 0x20
+#define MIXNDX_FIRSRC 0x30
+
+
+// mix_(bits)(m/s)[_filt]_(interp/spline/fir/whatever)[_ramp]
+static const mix_interface_t mix_functions[2 * 2 * 16] = {
+ // No SRC
+ Mono8BitMix, Mono16BitMix,
+ Stereo8BitMix, Stereo16BitMix,
+ Mono8BitRampMix, Mono16BitRampMix,
+ Stereo8BitRampMix, Stereo16BitRampMix,
+
+ // No SRC, Filter
+ FilterMono8BitMix, FilterMono16BitMix,
+ FilterStereo8BitMix, FilterStereo16BitMix,
+ FilterMono8BitRampMix, FilterMono16BitRampMix,
+ FilterStereo8BitRampMix, FilterStereo16BitRampMix,
+
+ // Linear SRC
+ Mono8BitLinearMix, Mono16BitLinearMix,
+ Stereo8BitLinearMix, Stereo16BitLinearMix,
+ Mono8BitLinearRampMix, Mono16BitLinearRampMix,
+ Stereo8BitLinearRampMix, Stereo16BitLinearRampMix,
+
+ // Linear SRC, Filter
+ FilterMono8BitLinearMix, FilterMono16BitLinearMix,
+ FilterStereo8BitLinearMix, FilterStereo16BitLinearMix,
+ FilterMono8BitLinearRampMix, FilterMono16BitLinearRampMix,
+ FilterStereo8BitLinearRampMix, FilterStereo16BitLinearRampMix,
+
+ // Spline SRC
+ Mono8BitSplineMix, Mono16BitSplineMix,
+ Stereo8BitSplineMix, Stereo16BitSplineMix,
+ Mono8BitSplineRampMix, Mono16BitSplineRampMix,
+ Stereo8BitSplineRampMix, Stereo16BitSplineRampMix,
+
+ // Spline SRC, Filter
+ FilterMono8BitSplineMix, FilterMono16BitSplineMix,
+ FilterStereo8BitSplineMix, FilterStereo16BitSplineMix,
+ FilterMono8BitSplineRampMix, FilterMono16BitSplineRampMix,
+ FilterStereo8BitSplineRampMix, FilterStereo16BitSplineRampMix,
+
+ // FirFilter SRC
+ Mono8BitFirFilterMix, Mono16BitFirFilterMix,
+ Stereo8BitFirFilterMix, Stereo16BitFirFilterMix,
+ Mono8BitFirFilterRampMix, Mono16BitFirFilterRampMix,
+ Stereo8BitFirFilterRampMix, Stereo16BitFirFilterRampMix,
+
+ // FirFilter SRC, Filter
+ FilterMono8BitFirFilterMix, FilterMono16BitFirFilterMix,
+ FilterStereo8BitFirFilterMix, FilterStereo16BitFirFilterMix,
+ FilterMono8BitFirFilterRampMix, FilterMono16BitFirFilterRampMix,
+ FilterStereo8BitFirFilterRampMix, FilterStereo16BitFirFilterRampMix
+};
+
+
+static const mix_interface_t fastmix_functions[2 * 2 * 16] = {
+ // No SRC
+ FastMono8BitMix, FastMono16BitMix,
+ Stereo8BitMix, Stereo16BitMix,
+ FastMono8BitRampMix, FastMono16BitRampMix,
+ Stereo8BitRampMix, Stereo16BitRampMix,
+
+ // No SRC, Filter
+ FilterMono8BitMix, FilterMono16BitMix,
+ FilterStereo8BitMix, FilterStereo16BitMix,
+ FilterMono8BitRampMix, FilterMono16BitRampMix,
+ FilterStereo8BitRampMix, FilterStereo16BitRampMix,
+
+ // Linear SRC
+ FastMono8BitLinearMix, FastMono16BitLinearMix,
+ Stereo8BitLinearMix, Stereo16BitLinearMix,
+ FastMono8BitLinearRampMix, FastMono16BitLinearRampMix,
+ Stereo8BitLinearRampMix, Stereo16BitLinearRampMix,
+
+ // Linear SRC, Filter
+ FilterMono8BitLinearMix, FilterMono16BitLinearMix,
+ FilterStereo8BitLinearMix, FilterStereo16BitLinearMix,
+ FilterMono8BitLinearRampMix, FilterMono16BitLinearRampMix,
+ FilterStereo8BitLinearRampMix, FilterStereo16BitLinearRampMix,
+
+ // Spline SRC
+ FastMono8BitSplineMix, FastMono16BitSplineMix,
+ Stereo8BitSplineMix, Stereo16BitSplineMix,
+ FastMono8BitSplineRampMix, FastMono16BitSplineRampMix,
+ Stereo8BitSplineRampMix, Stereo16BitSplineRampMix,
+
+ // Spline SRC, Filter
+ FilterMono8BitSplineMix, FilterMono16BitSplineMix,
+ FilterStereo8BitSplineMix, FilterStereo16BitSplineMix,
+ FilterMono8BitSplineRampMix, FilterMono16BitSplineRampMix,
+ FilterStereo8BitSplineRampMix, FilterStereo16BitSplineRampMix,
+
+ // FirFilter SRC
+ FastMono8BitFirFilterMix, FastMono16BitFirFilterMix,
+ Stereo8BitFirFilterMix, Stereo16BitFirFilterMix,
+ FastMono8BitFirFilterRampMix, FastMono16BitFirFilterRampMix,
+ Stereo8BitFirFilterRampMix, Stereo16BitFirFilterRampMix,
+
+ // FirFilter SRC, Filter
+ FilterMono8BitFirFilterMix, FilterMono16BitFirFilterMix,
+ FilterStereo8BitFirFilterMix, FilterStereo16BitFirFilterMix,
+ FilterMono8BitFirFilterRampMix, FilterMono16BitFirFilterRampMix,
+ FilterStereo8BitFirFilterRampMix, FilterStereo16BitFirFilterRampMix,
+};
+
+
+static int get_sample_count(song_voice_t *chan, int samples)
+{
+ int loop_start = (chan->flags & CHN_LOOP) ? chan->loop_start : 0;
+ int increment = chan->increment;
+
+ if (samples <= 0 || !increment || !chan->length)
+ return 0;
+
+ // Under zero ?
+ if ((int) chan->position < loop_start) {
+ if (increment < 0) {
+ // Invert loop for bidi loops
+ int delta = ((loop_start - chan->position) << 16) - (chan->position_frac & 0xFFFF);
+ chan->position = loop_start + (delta >> 16);
+ chan->position_frac = delta & 0xFFFF;
+
+ if ((int) chan->position < loop_start ||
+ chan->position >= (loop_start + chan->length) / 2) {
+ chan->position = loop_start;
+ chan->position_frac = 0;
+ }
+
+ increment = -increment;
+ chan->increment = increment;
+ // go forward
+ chan->flags &= ~(CHN_PINGPONGFLAG);
+
+ if ((!(chan->flags & CHN_LOOP)) ||
+ (chan->position >= chan->length)) {
+ chan->position = chan->length;
+ chan->position_frac = 0;
+ return 0;
+ }
+ }
+ else {
+ // We probably didn't hit the loop end yet (first loop), so we do nothing
+ if ((int) chan->position < 0)
+ chan->position = 0;
+ }
+ }
+ // Past the end
+ else if (chan->position >= chan->length) {
+ // not looping -> stop this channel
+ if (!(chan->flags & CHN_LOOP))
+ return 0;
+
+ if (chan->flags & CHN_PINGPONGLOOP) {
+ // Invert loop
+ if (increment > 0) {
+ increment = -increment;
+ chan->increment = increment;
+ }
+
+ chan->flags |= CHN_PINGPONGFLAG;
+ // adjust loop position
+ int delta_hi = (chan->position - chan->length);
+ int delta_lo = 0x10000 - (chan->position_frac & 0xFFFF);
+ chan->position = chan->length - delta_hi - (delta_lo >> 16);
+ chan->position_frac = delta_lo & 0xFFFF;
+
+ if (chan->position <= chan->loop_start || chan->position >= chan->length)
+ chan->position = chan->length - PINGPONG_OFFSET;
+ }
+ else {
+ // This is a bug
+ if (increment < 0) {
+ increment = -increment;
+ chan->increment = increment;
+ }
+
+ // Restart at loop start
+ chan->position += loop_start - chan->length;
+
+ if ((int) chan->position < loop_start)
+ chan->position = chan->loop_start;
+ }
+ }
+
+ int position = chan->position;
+
+ // too big increment, and/or too small loop length
+ if (position < loop_start) {
+ if (position < 0 || increment < 0)
+ return 0;
+ }
+
+ if (position < 0 || position >= (int) chan->length)
+ return 0;
+
+ int position_frac = (unsigned short) chan->position_frac,
+ sample_count = samples;
+
+ if (increment < 0) {
+ int inv = -increment;
+ int maxsamples = 16384 / ((inv >> 16) + 1);
+
+ if (maxsamples < 2)
+ maxsamples = 2;
+
+ if (samples > maxsamples)
+ samples = maxsamples;
+
+ int delta_hi = (inv >> 16) * (samples - 1);
+ int delta_lo = (inv & 0xffff) * (samples - 1);
+ int pos_dest = position - delta_hi + ((position_frac - delta_lo) >> 16);
+
+ if (pos_dest < loop_start) {
+ sample_count =
+ (unsigned int) (((((long long) position -
+ loop_start) << 16) + position_frac -
+ 1) / inv) + 1;
+ }
+ }
+ else {
+ int maxsamples = 16384 / ((increment >> 16) + 1);
+
+ if (maxsamples < 2)
+ maxsamples = 2;
+
+ if (samples > maxsamples)
+ samples = maxsamples;
+
+ int delta_hi = (increment >> 16) * (samples - 1);
+ int delta_lo = (increment & 0xffff) * (samples - 1);
+ int pos_dest = position + delta_hi + ((position_frac + delta_lo) >> 16);
+
+ if (pos_dest >= (int) chan->length) {
+ sample_count = (unsigned int)
+ (((((long long) chan->length - position) << 16) - position_frac - 1) / increment) + 1;
+ }
+ }
+
+ if (sample_count <= 1)
+ return 1;
+ else if (sample_count > samples)
+ return samples;
+
+ return sample_count;
+}
+
+
+unsigned int csf_create_stereo_mix(song_t *csf, int count)
+{
+ int* ofsl, *ofsr;
+ unsigned int nchused, nchmixed;
+
+ if (!count)
+ return 0;
+
+ nchused = nchmixed = 0;
+
+ // yuck
+ if (csf->multi_write)
+ for (unsigned int nchan = 0; nchan < MAX_CHANNELS; nchan++)
+ memset(csf->multi_write[nchan].buffer, 0, sizeof(csf->multi_write[nchan].buffer));
+
+ for (unsigned int nchan = 0; nchan < csf->num_voices; nchan++) {
+ const mix_interface_t *mix_func_table;
+ song_voice_t *const channel = &csf->voices[csf->voice_mix[nchan]];
+ unsigned int flags;
+ unsigned int nrampsamples;
+ int smpcount;
+ int nsamples;
+ int *pbuffer;
+
+ if (!channel->current_sample_data)
+ continue;
+
+ ofsr = &g_dry_rofs_vol;
+ ofsl = &g_dry_lofs_vol;
+ flags = 0;
+
+ if (channel->flags & CHN_16BIT)
+ flags |= MIXNDX_16BIT;
+
+ if (channel->flags & CHN_STEREO)
+ flags |= MIXNDX_STEREO;
+
+ if (channel->flags & CHN_FILTER)
+ flags |= MIXNDX_FILTER;
+
+ if (!(channel->flags & CHN_NOIDO) &&
+ !(csf->mix_flags & SNDMIX_NORESAMPLING)) {
+ // use hq-fir mixer?
+ if ((csf->mix_flags & (SNDMIX_HQRESAMPLER | SNDMIX_ULTRAHQSRCMODE))
+ == (SNDMIX_HQRESAMPLER | SNDMIX_ULTRAHQSRCMODE))
+ flags |= MIXNDX_FIRSRC;
+ else if (csf->mix_flags & SNDMIX_HQRESAMPLER)
+ flags |= MIXNDX_SPLINESRC;
+ else
+ flags |= MIXNDX_LINEARSRC; // use
+ }
+
+ if ((flags < 0x40) &&
+ (channel->left_volume == channel->right_volume) &&
+ ((!channel->ramp_length) ||
+ (channel->left_ramp == channel->right_ramp))) {
+ mix_func_table = fastmix_functions;
+ } else {
+ mix_func_table = mix_functions;
+ }
+
+ nsamples = count;
+
+ if (csf->multi_write) {
+ int master = (csf->voice_mix[nchan] < MAX_CHANNELS)
+ ? csf->voice_mix[nchan]
+ : (channel->master_channel - 1);
+ pbuffer = csf->multi_write[master].buffer;
+ csf->multi_write[master].used = 1;
+ } else {
+ pbuffer = csf->mix_buffer;
+ }
+
+ nchused++;
+ ////////////////////////////////////////////////////
+ unsigned int naddmix = 0;
+
+ do {
+ nrampsamples = nsamples;
+
+ if (channel->ramp_length > 0) {
+ if ((int) nrampsamples > channel->ramp_length)
+ nrampsamples = channel->ramp_length;
+ }
+
+ smpcount = 1;
+
+ /* Figure out the number of remaining samples,
+ * unless we're in AdLib or MIDI mode (to prevent
+ * artificial KeyOffs)
+ */
+ if (!(channel->flags & CHN_ADLIB)) {
+ smpcount = get_sample_count(channel, nrampsamples);
+ }
+
+ if (smpcount <= 0) {
+ // Stopping the channel
+ channel->current_sample_data = NULL;
+ channel->length = 0;
+ channel->position = 0;
+ channel->position_frac = 0;
+ channel->ramp_length = 0;
+ end_channel_ofs(channel, pbuffer, nsamples);
+ *ofsr += channel->rofs;
+ *ofsl += channel->lofs;
+ channel->rofs = channel->lofs = 0;
+ channel->flags &= ~CHN_PINGPONGFLAG;
+ break;
+ }
+
+ // Should we mix this channel ?
+
+ if ((nchmixed >= max_voices && !(csf->mix_flags & SNDMIX_DIRECTTODISK))
+ || (!channel->ramp_length && !(channel->left_volume | channel->right_volume))) {
+ int delta = (channel->increment * (int) smpcount) + (int) channel->position_frac;
+ channel->position_frac = delta & 0xFFFF;
+ channel->position += (delta >> 16);
+ channel->rofs = channel->lofs = 0;
+ pbuffer += smpcount * 2;
+ } else {
+ // Do mixing
+
+ /* Mix the stream, unless we're in AdLib mode */
+ if (!(channel->flags & CHN_ADLIB)) {
+ // Choose function for mixing
+ mix_interface_t mix_func;
+ mix_func = channel->ramp_length
+ ? mix_func_table[flags | MIXNDX_RAMP]
+ : mix_func_table[flags];
+ int *pbufmax = pbuffer + (smpcount * 2);
+ channel->rofs = -*(pbufmax - 2);
+ channel->lofs = -*(pbufmax - 1);
+
+ mix_func(channel, pbuffer, pbufmax);
+ channel->rofs += *(pbufmax - 2);
+ channel->lofs += *(pbufmax - 1);
+ pbuffer = pbufmax;
+ naddmix = 1;
+ }
+ }
+
+ nsamples -= smpcount;
+
+ if (channel->ramp_length) {
+ channel->ramp_length -= smpcount;
+ if (channel->ramp_length <= 0) {
+ channel->ramp_length = 0;
+ channel->right_volume = channel->right_volume_new;
+ channel->left_volume = channel->left_volume_new;
+ channel->right_ramp = channel->left_ramp = 0;
+
+ if ((channel->flags & CHN_NOTEFADE)
+ && (!(channel->fadeout_volume))) {
+ channel->length = 0;
+ channel->current_sample_data = NULL;
+ }
+ }
+ }
+
+ } while (nsamples > 0);
+
+ nchmixed += naddmix;
+ }
+
+ if (csf->multi_write) {
+ /* mix all adlib onto track one */
+ Fmdrv_MixTo(csf->multi_write[0].buffer, count);
+ } else {
+ Fmdrv_MixTo(csf->mix_buffer, count);
+ }
+
+ return nchused;
+}
diff --git a/src/player/mixutil.c b/src/player/mixutil.c
new file mode 100644
index 0000000..4518832
--- /dev/null
+++ b/src/player/mixutil.c
@@ -0,0 +1,258 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+
+#include "sndfile.h"
+
+#include "cmixer.h"
+
+#define OFSDECAYSHIFT 8
+#define OFSDECAYMASK 0xFF
+
+
+void init_mix_buffer(int *buffer, unsigned int samples)
+{
+ memset(buffer, 0, samples * sizeof(int));
+}
+
+
+void stereo_fill(int *buffer, unsigned int samples, int* profs, int *plofs)
+{
+ int rofs = *profs;
+ int lofs = *plofs;
+
+ if (!rofs && !lofs) {
+ init_mix_buffer(buffer, samples * 2);
+ return;
+ }
+
+ for (unsigned int i = 0; i < samples; i++) {
+ int x_r = (rofs + (((-rofs) >> 31) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
+ int x_l = (lofs + (((-lofs) >> 31) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
+
+ rofs -= x_r;
+ lofs -= x_l;
+ buffer[i * 2 ] = x_r;
+ buffer[i * 2 + 1] = x_l;
+ }
+
+ *profs = rofs;
+ *plofs = lofs;
+}
+
+
+void end_channel_ofs(song_voice_t *channel, int *buffer, unsigned int samples)
+{
+ int rofs = channel->rofs;
+ int lofs = channel->lofs;
+
+ if (!rofs && !lofs)
+ return;
+
+ for (unsigned int i = 0; i < samples; i++) {
+ int x_r = (rofs + (((-rofs) >> 31) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
+ int x_l = (lofs + (((-lofs) >> 31) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
+
+ rofs -= x_r;
+ lofs -= x_l;
+ buffer[i * 2] += x_r;
+ buffer[i * 2 + 1] += x_l;
+ }
+
+ channel->rofs = rofs;
+ channel->lofs = lofs;
+}
+
+
+void mono_from_stereo(int *mix_buf, unsigned int samples)
+{
+ for (unsigned int j, i = 0; i < samples; i++) {
+ j = i << 1;
+ mix_buf[i] = (mix_buf[j] + mix_buf[j + 1]) >> 1;
+ }
+}
+
+
+static const float f2ic = (float) (1 << 28);
+static const float i2fc = (float) (1.0 / (1 << 28));
+
+
+void stereo_mix_to_float(const int *src, float *out1, float *out2, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ *out1++ = *src * i2fc;
+ src++;
+
+ *out2++ = *src * i2fc;
+ src++;
+ }
+}
+
+
+void float_to_stereo_mix(const float *in1, const float *in2, int *out, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ *out++ = (int) (*in1 * f2ic);
+ *out++ = (int) (*in2 * f2ic);
+ in1++;
+ in2++;
+ }
+}
+
+
+void mono_mix_to_float(const int *src, float *out, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ *out++ = *src * i2fc;
+ src++;
+ }
+}
+
+
+void float_to_mono_mix(const float *in, int *out, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ *out++ = (int) (*in * f2ic);
+ in++;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Clip and convert functions
+// ----------------------------------------------------------------------------
+// XXX mins/max were int[2]
+//
+// The original C version was written by Rani Assaf <rani@magic.metawire.com>
+
+
+// Clip and convert to 8 bit. mins and maxs returned in 27bits: [MIXING_CLIPMIN..MIXING_CLIPMAX]. mins[0] left, mins[1] right.
+unsigned int clip_32_to_8(void *ptr, int *buffer, unsigned int samples, int *mins, int *maxs)
+{
+ unsigned char *p = (unsigned char *) ptr;
+
+ for (unsigned int i = 0; i < samples; i++) {
+ int n = buffer[i];
+
+ if (n < MIXING_CLIPMIN)
+ n = MIXING_CLIPMIN;
+ else if (n > MIXING_CLIPMAX)
+ n = MIXING_CLIPMAX;
+
+ if (n < mins[i & 1])
+ mins[i & 1] = n;
+ else if (n > maxs[i & 1])
+ maxs[i & 1] = n;
+
+ // 8-bit unsigned
+ p[i] = (n >> (24 - MIXING_ATTENUATION)) ^ 0x80;
+ }
+
+ return samples;
+}
+
+
+// Clip and convert to 16 bit. mins and maxs returned in 27bits: [MIXING_CLIPMIN..MIXING_CLIPMAX]. mins[0] left, mins[1] right.
+unsigned int clip_32_to_16(void *ptr, int *buffer, unsigned int samples, int *mins, int *maxs)
+{
+ signed short *p = (signed short *) ptr;
+
+ for (unsigned int i = 0; i < samples; i++) {
+ int n = buffer[i];
+
+ if (n < MIXING_CLIPMIN)
+ n = MIXING_CLIPMIN;
+ else if (n > MIXING_CLIPMAX)
+ n = MIXING_CLIPMAX;
+
+ if (n < mins[i & 1])
+ mins[i & 1] = n;
+ else if (n > maxs[i & 1])
+ maxs[i & 1] = n;
+
+ // 16-bit signed
+ p[i] = n >> (16 - MIXING_ATTENUATION);
+ }
+
+ return samples * 2;
+}
+
+
+// Clip and convert to 24 bit. mins and maxs returned in 27bits: [MIXING_CLIPMIN..MIXING_CLIPMAX]. mins[0] left, mins[1] right.
+// Note, this is 24bit, not 24-in-32bits. The former is used in .wav. The latter is used in audio IO
+unsigned int clip_32_to_24(void *ptr, int *buffer, unsigned int samples, int *mins, int *maxs)
+{
+ /* the inventor of 24bit anything should be shot */
+ unsigned char *p = (unsigned char *) ptr;
+
+ for (unsigned int i = 0; i < samples; i++) {
+ int n = buffer[i];
+
+ if (n < MIXING_CLIPMIN)
+ n = MIXING_CLIPMIN;
+ else if (n > MIXING_CLIPMAX)
+ n = MIXING_CLIPMAX;
+
+ if (n < mins[i & 1])
+ mins[i & 1] = n;
+ else if (n > maxs[i & 1])
+ maxs[i & 1] = n;
+
+ // 24-bit signed
+ n = n >> (8 - MIXING_ATTENUATION);
+
+ /* err, assume same endian */
+ memcpy(p, &n, 3);
+ p += 3;
+ }
+
+ return samples * 3;
+}
+
+
+// Clip and convert to 32 bit(int). mins and maxs returned in 27bits: [MIXING_CLIPMIN..MIXING_CLIPMAX]. mins[0] left, mins[1] right.
+unsigned int clip_32_to_32(void *ptr, int *buffer, unsigned int samples, int *mins, int *maxs)
+{
+ signed int *p = (signed int *) ptr;
+
+ for (unsigned int i = 0; i < samples; i++) {
+ int n = buffer[i];
+
+ if (n < MIXING_CLIPMIN)
+ n = MIXING_CLIPMIN;
+ else if (n > MIXING_CLIPMAX)
+ n = MIXING_CLIPMAX;
+
+ if (n < mins[i & 1])
+ mins[i & 1] = n;
+ else if (n > maxs[i & 1])
+ maxs[i & 1] = n;
+
+ // 32-bit signed
+ p[i] = (n << MIXING_ATTENUATION);
+ }
+
+ return samples * 4;
+}
+
diff --git a/src/player/opl-util.c b/src/player/opl-util.c
new file mode 100644
index 0000000..cfb32f7
--- /dev/null
+++ b/src/player/opl-util.c
@@ -0,0 +1,134 @@
+/**
+ * @file opl-util.cpp
+ * @brief Utility functions related to OPL chips.
+ *
+ * Copyright (C) 2010-2013 Adam Nielsen <malvineous@shikadi.net>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+//Stripped down version for Schismtracker, in C.
+
+// this really should be in a header but it's only used in one other file
+int fnumToMilliHertz(unsigned int fnum, unsigned int block,
+ unsigned int conversionFactor);
+void milliHertzToFnum(unsigned int milliHertz,
+ unsigned int *fnum, unsigned int *block, unsigned int conversionFactor);
+
+
+/// Convert the given f-number and block into a note frequency.
+/**
+* @param fnum
+* Input frequency number, between 0 and 1023 inclusive. Values outside this
+* range will cause assertion failures.
+*
+* @param block
+* Input block number, between 0 and 7 inclusive. Values outside this range
+* will cause assertion failures.
+*
+* @param conversionFactor
+* Conversion factor to use. Normally will be 49716 and occasionally 50000.
+*
+* @return The converted frequency in milliHertz.
+*/
+int fnumToMilliHertz(unsigned int fnum, unsigned int block,
+ unsigned int conversionFactor)
+{
+ // Original formula
+ //return 1000 * conversionFactor * (double)fnum * pow(2, (double)((signed)block - 20));
+
+ // More efficient version
+ return (1000ull * conversionFactor * fnum) >> (20 - block);
+}
+/// Convert a frequency into an OPL f-number
+/**
+* @param milliHertz
+* Input frequency.
+*
+* @param fnum
+* Output frequency number for OPL chip. This is a 10-bit number, so it will
+* always be between 0 and 1023 inclusive.
+*
+* @param block
+* Output block number for OPL chip. This is a 3-bit number, so it will
+* always be between 0 and 7 inclusive.
+*
+* @param conversionFactor
+* Conversion factor to use. Normally will be 49716 and occasionally 50000.
+*
+* @post fnum will be set to a value between 0 and 1023 inclusive. block will
+* be set to a value between 0 and 7 inclusive. assert() calls inside this
+* function ensure this will always be the case.
+*
+* @note As the block value increases, the frequency difference between two
+* adjacent fnum values increases. This means the higher the frequency,
+* the less precision is available to represent it. Therefore, converting
+* a value to fnum/block and back to milliHertz is not guaranteed to reproduce
+* the original value.
+*/
+void milliHertzToFnum(unsigned int milliHertz,
+ unsigned int *fnum, unsigned int *block, unsigned int conversionFactor)
+{
+ // Special case to avoid divide by zero
+ if (milliHertz <= 0) {
+ *block = 0; // actually any block will work
+ *fnum = 0;
+ return;
+ }
+
+ // Special case for frequencies too high to produce
+ if (milliHertz > 6208431) {
+ *block = 7;
+ *fnum = 1023;
+ return;
+ }
+
+ /// This formula will provide a pretty good estimate as to the best block to
+ /// use for a given frequency. It tries to use the lowest possible block
+ /// number that is capable of representing the given frequency. This is
+ /// because as the block number increases, the precision decreases (i.e. there
+ /// are larger steps between adjacent note frequencies.) The 6M constant is
+ /// the largest frequency (in milliHertz) that can be represented by the
+ /// block/fnum system.
+ //int invertedBlock = log2(6208431 / milliHertz);
+
+ // Very low frequencies will produce very high inverted block numbers, but
+ // as they can all be covered by inverted block 7 (block 0) we can just clip
+ // the value.
+ //if (invertedBlock > 7) invertedBlock = 7;
+ //*block = 7 - invertedBlock;
+
+ // This is a bit more efficient and doesn't need log2() from math.h
+ if (milliHertz > 3104215) *block = 7;
+ else if (milliHertz > 1552107) *block = 6;
+ else if (milliHertz > 776053) *block = 5;
+ else if (milliHertz > 388026) *block = 4;
+ else if (milliHertz > 194013) *block = 3;
+ else if (milliHertz > 97006) *block = 2;
+ else if (milliHertz > 48503) *block = 1;
+ else *block = 0;
+
+ // Original formula
+ //*fnum = milliHertz * pow(2, 20 - *block) / 1000 / conversionFactor + 0.5;
+
+ // Slightly more efficient version
+ *fnum = ((unsigned long long)milliHertz << (20 - *block)) / (conversionFactor * 1000.0) + 0.5;
+
+ if (*fnum > 1023) {
+ (*block)++;
+ *fnum = ((unsigned long long)milliHertz << (20 - *block)) / (conversionFactor * 1000.0) + 0.5;
+ }
+
+ return;
+}
diff --git a/src/player/snd_fm.c b/src/player/snd_fm.c
new file mode 100644
index 0000000..4ee6db8
--- /dev/null
+++ b/src/player/snd_fm.c
@@ -0,0 +1,385 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "fmopl.h"
+#include "snd_fm.h"
+
+#define MAX_VOICES 256 /* Must not be less than the setting in sndfile.h */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define OPLNew(x,r) ym3812_init(x, r)
+#define OPLResetChip ym3812_reset_chip
+#define OPLWrite ym3812_write
+#define OPLUpdateOne ym3812_update_one
+#define OPLClose ym3812_shutdown
+
+/* Mostly pulled from my posterior. Original value was 2000, but Manwe says that's too quiet.
+It'd help if this was at all connected to the song's mixing volume...
+*/
+#define OPL_VOLUME 5000
+
+/*
+The documentation in this file regarding the output ports,
+including the comment "Don't ask me why", are attributed
+to Jeffrey S. Lee's article:
+ Programming the AdLib/Sound Blaster
+ FM Music Chips
+ Version 2.0 (24 Feb 1992)
+*/
+
+static const int oplbase = 0x388;
+
+// OPL info
+static struct OPL* opl = NULL;
+static UINT32 oplretval = 0,
+ oplregno = 0;
+static UINT32 fm_active = 0;
+
+extern int fnumToMilliHertz(unsigned int fnum, unsigned int block,
+ unsigned int conversionFactor);
+
+extern void milliHertzToFnum(unsigned int milliHertz,
+ unsigned int *fnum, unsigned int *block, unsigned int conversionFactor);
+
+
+static void Fmdrv_Outportb(unsigned port, unsigned value)
+{
+ if (opl == NULL ||
+ ((int) port) < oplbase ||
+ ((int) port) >= oplbase + 4)
+ return;
+
+ unsigned ind = port - oplbase;
+ OPLWrite(opl, ind, value);
+
+ if (ind & 1) {
+ if (oplregno == 4) {
+ if (value == 0x80)
+ oplretval = 0x02;
+ else if (value == 0x21)
+ oplretval = 0xC0;
+ }
+ }
+ else
+ oplregno = value;
+}
+
+
+static unsigned char Fmdrv_Inportb(unsigned port)
+{
+ return (((int) port) >= oplbase &&
+ ((int) port) < oplbase + 4) ? oplretval : 0;
+}
+
+
+void Fmdrv_Init(int mixfreq)
+{
+ if (opl != NULL) {
+ OPLClose(opl);
+ opl = NULL;
+ }
+ //Clock for frequency 49716Hz. Mixfreq is used for output mix frequency.
+ opl = OPLNew(1789776 * 2, mixfreq);
+ OPLResetChip(opl);
+ OPL_Detect();
+}
+
+
+void Fmdrv_MixTo(int *target, int count)
+{
+ static short *buf = NULL;
+ static int buf_size = 0;
+
+ if (!fm_active)
+ return;
+
+ if (buf_size != count * 2) {
+ int before = buf_size;
+ buf_size = sizeof(short) * count;
+
+ if (before) {
+ buf = (short *) realloc(buf, buf_size);
+ }
+ else {
+ buf = (short *) malloc(buf_size);
+ }
+ }
+
+ memset(buf, 0, count * 2);
+ OPLUpdateOne(opl, buf, count);
+
+ /*
+ static int counter = 0;
+
+ for(int a = 0; a < count; ++a)
+ buf[a] = ((counter++) & 0x100) ? -10000 : 10000;
+ */
+
+ for (int a = 0; a < count; ++a) {
+ target[a * 2 + 0] += buf[a] * OPL_VOLUME;
+ target[a * 2 + 1] += buf[a] * OPL_VOLUME;
+ }
+}
+
+
+/***************************************/
+
+
+static const char PortBases[9] = {0, 1, 2, 8, 9, 10, 16, 17, 18};
+static signed char Pans[MAX_VOICES];
+static const unsigned char *Dtab[MAX_VOICES] = {NULL};
+
+
+static int SetBase(int c)
+{
+ return c % 9;
+}
+
+
+static void OPL_Byte(unsigned char idx, unsigned char data)
+{
+ //register int a;
+ Fmdrv_Outportb(oplbase, idx); // for(a = 0; a < 6; a++) Fmdrv_Inportb(oplbase);
+ Fmdrv_Outportb(oplbase + 1, data); // for(a = 0; a < 35; a++) Fmdrv_Inportb(oplbase);
+}
+
+
+void OPL_NoteOff(int c)
+{
+ c = SetBase(c);
+
+ if (c<9) {
+ /* KEYON_BLOCK+c seems to not work alone?? */
+ OPL_Byte(KEYON_BLOCK + c, 0);
+ //OPL_Byte(KSL_LEVEL + Ope, 0xFF);
+ //OPL_Byte(KSL_LEVEL + 3 + Ope, 0xFF);
+ }
+}
+
+
+/* OPL_NoteOn changes the frequency on specified
+ channel and guarantees the key is on. (Doesn't
+ retrig, just turns the note on and sets freq.)
+ If keyoff is nonzero, doesn't even set the note on.
+ Could be used for pitch bending also. */
+void OPL_HertzTouch(int c, int milliHertz, int keyoff)
+{
+ c = SetBase(c);
+
+ if (c >= 9)
+ return;
+
+ fm_active = 1;
+
+/*
+ Bytes A0-B8 - Octave / F-Number / Key-On
+
+ 7 6 5 4 3 2 1 0
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | F-Number (least significant byte) | (A0-A8)
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | Unused | Key | Octave | F-Number | (B0-B8)
+ | | On | | most sig. |
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+*/
+ unsigned int outfnum;
+ unsigned int outblock;
+ const int conversion_factor = 49716; // Frequency of OPL.
+ milliHertzToFnum(milliHertz, &outfnum, &outblock, conversion_factor);
+ OPL_Byte(0xA0 + c, outfnum & 255); // F-Number low 8 bits
+ OPL_Byte(0xB0 + c, (keyoff ? 0 : 0x20) // Key on
+ | ((outfnum >> 8) & 3) // F-number high 2 bits
+ | (outblock << 2)
+ );
+
+}
+
+
+void OPL_Touch(int c, const unsigned char *D, unsigned vol)
+{
+ if (!D) {
+ if (c < MAX_VOICES)
+ D = Dtab[c];
+ if (!D)
+ return;
+ }
+
+//fprintf(stderr, "OPL_Touch(%d, %p:%02X.%02X.%02X.%02X-%02X.%02X.%02X.%02X-%02X.%02X.%02X, %d)\n",
+// c, D,D[0],D[1],D[2],D[3],D[4],D[5],D[6],D[7],D[8],D[9],D[10], Vol);
+
+ Dtab[c] = D;
+
+ c = SetBase(c);
+
+ if (c >= 9)
+ return;
+
+ int Ope = PortBases[c];
+
+/*
+ Bytes 40-55 - Level Key Scaling / Total Level
+
+ 7 6 5 4 3 2 1 0
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | Scaling | Total Level |
+ | Level | 24 12 6 3 1.5 .75 | <-- dB
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ bits 7-6 - causes output levels to decrease as the frequency
+ rises:
+ 00 - no change
+ 10 - 1.5 dB/8ve
+ 01 - 3 dB/8ve
+ 11 - 6 dB/8ve
+ bits 5-0 - controls the total output level of the operator.
+ all bits CLEAR is loudest; all bits SET is the
+ softest. Don't ask me why.
+*/
+ OPL_Byte(KSL_LEVEL + Ope, (D[2] & KSL_MASK) |
+ // (63 + (d[2] & 63) * vol / 63 - vol) - old formula
+ // (63 - ((63 - (d[2] & 63)) * vol ) / 63) - older formula
+ // (63 - ((63 - (d[2] & 63)) * vol + 32) / 64) - revised formula, like ST3
+ (((int)(D[2] & 63) - 63) * vol + 63 * 64 - 32) / 64 // - optimized revised formula
+ );
+
+ OPL_Byte(KSL_LEVEL + 3 + Ope, (D[3] & KSL_MASK) |
+ (((int)(D[3] & 63) - 63) * vol + 63 * 64 - 32) / 64
+ );
+
+ /* 2008-09-27 Bisqwit:
+ * Did tests in ST3: The value poked
+ * to 0x43, minus from 63, is:
+ *
+ * OplVol 63 47 31
+ * SmpVol
+ * 64 63 47 31
+ * 32 32 24 15
+ * 16 16 12 8
+ *
+ * This seems to clearly indicate that the value
+ * poked is calculated with 63 - round(oplvol*smpvol/64.0).
+ *
+ * Also, from the documentation we can deduce that
+ * the maximum volume to be set is 47.25 dB and that
+ * each increase by 1 corresponds to 0.75 dB.
+ *
+ * Since we know that 6 dB is equivalent to a doubling
+ * of the volume, we can deduce that an increase or
+ * decrease by 8 will double / halve the volume.
+ *
+ */
+}
+
+
+void OPL_Pan(int c, signed char val)
+{
+ Pans[c] = val;
+ /* Doesn't happen immediately! */
+}
+
+
+void OPL_Patch(int c, const unsigned char *D)
+{
+//fprintf(stderr, "OPL_Patch(%d, %p:%02X.%02X.%02X.%02X-%02X.%02X.%02X.%02X-%02X.%02X.%02X)\n",
+// c, D,D[0],D[1],D[2],D[3],D[4],D[5],D[6],D[7],D[8],D[9],D[10]);
+ Dtab[c] = D;
+
+ c = SetBase(c);
+ if(c >= 9)return;
+
+ int Ope = PortBases[c];
+
+ OPL_Byte(AM_VIB+ Ope, D[0]);
+ OPL_Byte(ATTACK_DECAY+ Ope, D[4]);
+ OPL_Byte(SUSTAIN_RELEASE+ Ope, D[6]);
+ OPL_Byte(WAVE_SELECT+ Ope, D[8]&3);// 6 high bits used elsewhere
+
+ OPL_Byte(AM_VIB+ 3+Ope, D[1]);
+ OPL_Byte(ATTACK_DECAY+ 3+Ope, D[5]);
+ OPL_Byte(SUSTAIN_RELEASE+3+Ope, D[7]);
+ OPL_Byte(WAVE_SELECT+ 3+Ope, D[9]&3);// 6 high bits used elsewhere
+
+ /* feedback, additive synthesis and Panning... */
+ OPL_Byte(FEEDBACK_CONNECTION+c,
+ (D[10] & ~STEREO_BITS)
+ | (Pans[c]<-32 ? VOICE_TO_LEFT
+ : Pans[c]>32 ? VOICE_TO_RIGHT
+ : (VOICE_TO_LEFT | VOICE_TO_RIGHT)
+ ));
+}
+
+
+void OPL_Reset(void)
+{
+//fprintf(stderr, "OPL_Reset\n");
+ int a;
+
+ for(a = 0; a < 244; a++)
+ OPL_Byte(a, 0);
+
+ for(a = 0; a < MAX_VOICES; ++a)
+ Dtab[a] = NULL;
+
+ OPL_Byte(TEST_REGISTER, ENABLE_WAVE_SELECT);
+
+ fm_active = 0;
+}
+
+
+int OPL_Detect(void)
+{
+ SetBase(0);
+
+ /* Reset timers 1 and 2 */
+ OPL_Byte(TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+
+ /* Reset the IRQ of the FM chip */
+ OPL_Byte(TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+ unsigned char ST1 = Fmdrv_Inportb(oplbase); /* Status register */
+
+ OPL_Byte(TIMER1_REGISTER, 255);
+ OPL_Byte(TIMER_CONTROL_REGISTER, TIMER2_MASK | TIMER1_START);
+
+ /*_asm xor cx,cx;P1:_asm loop P1*/
+ unsigned char ST2 = Fmdrv_Inportb(oplbase);
+
+ OPL_Byte(TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+ OPL_Byte(TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+ int OPLMode = (ST2 & 0xE0) == 0xC0 && !(ST1 & 0xE0);
+
+ if (!OPLMode)
+ return -1;
+
+ return 0;
+}
+
+
+void OPL_Close(void)
+{
+ OPL_Reset();
+}
+
diff --git a/src/player/sndmix.c b/src/player/sndmix.c
new file mode 100644
index 0000000..3fb8e07
--- /dev/null
+++ b/src/player/sndmix.c
@@ -0,0 +1,1259 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sndfile.h"
+#include "snd_fm.h"
+#include "cmixer.h"
+
+#include "util.h" /* for clamp */
+
+// Volume ramp length, in 1/10 ms
+#define VOLUMERAMPLEN 146 // 1.46ms = 64 samples at 44.1kHz
+
+// VU meter
+#define VUMETER_DECAY 16
+
+// SNDMIX: These are global flags for playback control
+unsigned int max_voices = 32; // ITT it is 1994
+
+// Mixing data initialized in
+static unsigned int volume_ramp_samples = 64;
+unsigned int global_vu_left = 0;
+unsigned int global_vu_right = 0;
+int32_t g_dry_rofs_vol = 0;
+int32_t g_dry_lofs_vol = 0;
+
+typedef uint32_t (* convert_t)(void *, int *, uint32_t, int *, int *);
+
+
+// see also csf_midi_out_raw in effects.c
+void (*csf_midi_out_note)(int chan, const song_note_t *m) = NULL;
+
+
+// The volume we have here is in range 0..(63*255) (0..16065)
+// We should keep that range, but convert it into a logarithmic
+// one such that a change of 256*8 (2048) corresponds to a halving
+// of the volume.
+// logvolume = 2^(linvolume / (4096/8)) * (4096/64)
+// However, because the resolution of MIDI volumes
+// is merely 128 units, we can use a lookup table.
+//
+// In this table, each value signifies the minimum value
+// that volume must be in order for the result to be
+// that table index.
+static const unsigned short GMvolTransition[128] =
+{
+ 0, 2031, 4039, 5214, 6048, 6694, 7222, 7669,
+ 8056, 8397, 8702, 8978, 9230, 9462, 9677, 9877,
+10064,10239,10405,10562,10710,10852,10986,11115,
+11239,11357,11470,11580,11685,11787,11885,11980,
+12072,12161,12248,12332,12413,12493,12570,12645,
+12718,12790,12860,12928,12995,13060,13123,13186,
+13247,13306,13365,13422,13479,13534,13588,13641,
+13693,13745,13795,13844,13893,13941,13988,14034,
+14080,14125,14169,14213,14256,14298,14340,14381,
+14421,14461,14501,14540,14578,14616,14653,14690,
+14727,14763,14798,14833,14868,14902,14936,14970,
+15003,15035,15068,15100,15131,15163,15194,15224,
+15255,15285,15315,15344,15373,15402,15430,15459,
+15487,15514,15542,15569,15596,15623,15649,15675,
+15701,15727,15753,15778,15803,15828,15853,15877,
+15901,15925,15949,15973,15996,16020,16043,16065,
+};
+
+
+// We use binary search to find the right slot
+// with at most 7 comparisons.
+static unsigned int find_volume(unsigned short vol)
+{
+ unsigned int l = 0, r = 128;
+
+ while (l < r) {
+ unsigned int m = l + ((r - l) / 2);
+ unsigned short p = GMvolTransition[m];
+
+ if (p < vol)
+ l = m + 1;
+ else
+ r = m;
+ }
+
+ return l;
+}
+
+
+unsigned int get_freq_from_period(int period, int linear)
+{
+ if (period <= 0)
+ return INT_MAX;
+ else if (linear)
+ return period;
+ else
+ return _muldiv(8363, 1712L << 8, (period << 8));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// XXX * I prefixed these with `rn_' to avoid any namespace conflicts
+// XXX Needs better naming!
+// XXX * Keep inline?
+// XXX * Get rid of the pointer passing where it is not needed
+//
+
+
+static inline void rn_tremor(song_voice_t *chan, int *vol)
+{
+ if ((chan->cd_tremor & 192) == 128)
+ *vol = 0;
+
+ chan->flags |= CHN_FASTVOLRAMP;
+}
+
+
+static inline int rn_vibrato(song_t *csf, song_voice_t *chan, int period)
+{
+ unsigned int vibpos = chan->vibrato_position & 0xFF;
+ int vdelta;
+ unsigned int vdepth;
+
+ switch (chan->vib_type) {
+ case VIB_SINE:
+ default:
+ vdelta = sine_table[vibpos];
+ break;
+ case VIB_RAMP_DOWN:
+ vdelta = ramp_down_table[vibpos];
+ break;
+ case VIB_SQUARE:
+ vdelta = square_table[vibpos];
+ break;
+ case VIB_RANDOM:
+ vdelta = 128 * ((double) rand() / RAND_MAX) - 64;
+ break;
+ }
+
+ if (csf->flags & SONG_ITOLDEFFECTS) {
+ vdepth = 5;
+ vdelta = -vdelta; // yes, IT does vibrato backwards in old-effects mode. try it.
+ } else {
+ vdepth = 6;
+ }
+ vdelta = (vdelta * (int)chan->vibrato_depth) >> vdepth;
+
+ if (csf->flags & SONG_LINEARSLIDES) {
+ int l = abs(vdelta);
+
+ if (vdelta < 0) {
+ vdelta = _muldiv(period, linear_slide_up_table[l >> 2], 0x10000) - period;
+
+ if (l & 0x03)
+ vdelta += _muldiv(period, fine_linear_slide_up_table[l & 0x03], 0x10000) - period;
+ } else {
+ vdelta = _muldiv(period, linear_slide_down_table[l >> 2], 0x10000) - period;
+
+ if (l & 0x03)
+ vdelta += _muldiv(period, fine_linear_slide_down_table[l & 0x03], 0x10000) - period;
+ }
+ }
+
+ period -= vdelta;
+
+ // handle on tick-N, or all ticks if not in old-effects mode
+ if (!(csf->flags & SONG_FIRSTTICK) || !(csf->flags & SONG_ITOLDEFFECTS)) {
+ chan->vibrato_position = (vibpos + 4 * chan->vibrato_speed) & 0xFF;
+ }
+
+ return period;
+}
+
+static inline int rn_sample_vibrato(song_voice_t *chan, int period)
+{
+ unsigned int vibpos = chan->autovib_position & 0xFF;
+ int vdelta, adepth;
+ song_sample_t *pins = chan->ptr_sample;
+
+ /*
+ 1) Mov AX, [SomeVariableNameRelatingToVibrato]
+ 2) Add AL, Rate
+ 3) AdC AH, 0
+ 4) AH contains the depth of the vibrato as a fine-linear slide.
+ 5) Mov [SomeVariableNameRelatingToVibrato], AX ; For the next cycle.
+ */
+
+ adepth = chan->autovib_depth; // (1)
+ adepth += pins->vib_rate & 0xff; // (2 & 3)
+ /* need this cast -- if adepth is unsigned, large autovib will crash the mixer (why? I don't know!)
+ but if vib_depth is changed to signed, that screws up other parts of the code. ugh. */
+ adepth = MIN(adepth, (int) (pins->vib_depth << 8));
+ chan->autovib_depth = adepth; // (5)
+ adepth >>= 8; // (4)
+
+ chan->autovib_position += pins->vib_speed;
+
+ switch(pins->vib_type) {
+ case VIB_SINE:
+ default:
+ vdelta = sine_table[vibpos];
+ break;
+ case VIB_RAMP_DOWN:
+ vdelta = ramp_down_table[vibpos];
+ break;
+ case VIB_SQUARE:
+ vdelta = square_table[vibpos];
+ break;
+ case VIB_RANDOM:
+ vdelta = 128 * ((double) rand() / RAND_MAX) - 64;
+ break;
+ }
+ vdelta = (vdelta * adepth) >> 6;
+
+ int l = abs(vdelta);
+ if (vdelta < 0) {
+ vdelta = _muldiv(period, linear_slide_up_table[l >> 2], 0x10000) - period;
+
+ if (l & 0x03)
+ vdelta += _muldiv(period, fine_linear_slide_up_table[l & 0x03], 0x10000) - period;
+ } else {
+ vdelta = _muldiv(period, linear_slide_down_table[l >> 2], 0x10000) - period;
+
+ if (l & 0x03)
+ vdelta += _muldiv(period, fine_linear_slide_down_table[l & 0x03], 0x10000) - period;
+ }
+
+ return period - vdelta;
+}
+
+
+static inline void rn_process_envelope(song_voice_t *chan, int *nvol)
+{
+ song_instrument_t *penv = chan->ptr_instrument;
+ int vol = *nvol;
+
+ // Volume Envelope
+ if (chan->flags & CHN_VOLENV && penv->vol_env.nodes) {
+ int envpos = chan->vol_env_position;
+ unsigned int pt = penv->vol_env.nodes - 1;
+
+ for (unsigned int i = 0; i < (unsigned int)(penv->vol_env.nodes - 1); i++) {
+ if (envpos <= penv->vol_env.ticks[i]) {
+ pt = i;
+ break;
+ }
+ }
+
+ int x2 = penv->vol_env.ticks[pt];
+ int x1, envvol;
+
+ if (envpos >= x2) {
+ envvol = penv->vol_env.values[pt] << 2;
+ x1 = x2;
+ } else if (pt) {
+ envvol = penv->vol_env.values[pt-1] << 2;
+ x1 = penv->vol_env.ticks[pt-1];
+ } else {
+ envvol = 0;
+ x1 = 0;
+ }
+
+ if (envpos > x2)
+ envpos = x2;
+
+ if (x2 > x1 && envpos > x1) {
+ envvol += ((envpos - x1) * (((int)penv->vol_env.values[pt]<<2) - envvol)) / (x2 - x1);
+ }
+
+ envvol = CLAMP(envvol, 0, 256);
+ vol = (vol * envvol) >> 8;
+ }
+
+ // Panning Envelope
+ if ((chan->flags & CHN_PANENV) && (penv->pan_env.nodes)) {
+ int envpos = chan->pan_env_position;
+ unsigned int pt = penv->pan_env.nodes - 1;
+
+ for (unsigned int i=0; i<(unsigned int)(penv->pan_env.nodes-1); i++) {
+ if (envpos <= penv->pan_env.ticks[i]) {
+ pt = i;
+ break;
+ }
+ }
+
+ int x2 = penv->pan_env.ticks[pt], y2 = penv->pan_env.values[pt];
+ int x1, envpan;
+
+ if (envpos >= x2) {
+ envpan = y2;
+ x1 = x2;
+ } else if (pt) {
+ envpan = penv->pan_env.values[pt-1];
+ x1 = penv->pan_env.ticks[pt-1];
+ } else {
+ envpan = 128;
+ x1 = 0;
+ }
+
+ if (x2 > x1 && envpos > x1) {
+ envpan += ((envpos - x1) * (y2 - envpan)) / (x2 - x1);
+ }
+
+ envpan = CLAMP(envpan, 0, 64);
+
+ int pan = chan->final_panning;
+
+ if (pan >= 128) {
+ pan += ((envpan - 32) * (256 - pan)) / 32;
+ } else {
+ pan += ((envpan - 32) * (pan)) / 32;
+ }
+
+ chan->final_panning = pan;
+ }
+
+ // FadeOut volume
+ if (chan->flags & CHN_NOTEFADE) {
+ unsigned int fadeout = penv->fadeout;
+
+ if (fadeout) {
+ chan->fadeout_volume -= fadeout << 1;
+
+ if (chan->fadeout_volume <= 0)
+ chan->fadeout_volume = 0;
+
+ vol = (vol * chan->fadeout_volume) >> 16;
+ } else if (!chan->fadeout_volume) {
+ vol = 0;
+ }
+ }
+
+ // Pitch/Pan separation
+ if (penv->pitch_pan_separation && chan->final_panning && chan->note) {
+ // PPS value is 1/512, i.e. PPS=1 will adjust by 8/512 = 1/64 for each 8 semitones
+ // with PPS = 32 / PPC = C-5, E-6 will pan hard right (and D#6 will not)
+ chan->final_panning += ((int) (chan->note - penv->pitch_pan_center - 1)
+ * penv->pitch_pan_separation) / 4;
+ }
+
+ *nvol = vol;
+}
+
+
+static inline int rn_arpeggio(song_t *csf, song_voice_t *chan, int period)
+{
+ int a;
+
+ switch ((csf->current_speed - csf->tick_count) % 3) {
+ case 1:
+ a = chan->mem_arpeggio >> 4;
+ break;
+ case 2:
+ a = chan->mem_arpeggio & 0xf;
+ break;
+ default:
+ a = 0;
+ }
+
+ if (!a)
+ return period;
+
+ a = linear_slide_up_table[a * 16];
+ return ((csf->flags & SONG_LINEARSLIDES)
+ ? _muldiv(period, a, 65536)
+ : _muldiv(period, 65536, a));
+}
+
+
+static inline void rn_pitch_filter_envelope(song_voice_t *chan, int *nenvpitch, int *nperiod)
+{
+ song_instrument_t *penv = chan->ptr_instrument;
+ int envpos = chan->pitch_env_position;
+ unsigned int pt = penv->pitch_env.nodes - 1;
+ int period = *nperiod;
+ int envpitch = *nenvpitch;
+
+ for (unsigned int i = 0; i < (unsigned int)(penv->pitch_env.nodes - 1); i++) {
+ if (envpos <= penv->pitch_env.ticks[i]) {
+ pt = i;
+ break;
+ }
+ }
+
+ int x2 = penv->pitch_env.ticks[pt];
+ int x1;
+
+ if (envpos >= x2) {
+ envpitch = (((int)penv->pitch_env.values[pt]) - 32) * 8;
+ x1 = x2;
+ } else if (pt) {
+ envpitch = (((int)penv->pitch_env.values[pt - 1]) - 32) * 8;
+ x1 = penv->pitch_env.ticks[pt - 1];
+ } else {
+ envpitch = 0;
+ x1 = 0;
+ }
+
+ if (envpos > x2)
+ envpos = x2;
+
+ if (x2 > x1 && envpos > x1) {
+ int envpitchdest = (((int)penv->pitch_env.values[pt]) - 32) * 8;
+ envpitch += ((envpos - x1) * (envpitchdest - envpitch)) / (x2 - x1);
+ }
+
+ // clamp to -255/255?
+ envpitch = CLAMP(envpitch, -256, 256);
+
+ // Pitch Envelope
+ if (!(penv->flags & ENV_FILTER)) {
+ int l = abs(envpitch);
+
+ if (l > 255)
+ l = 255;
+
+ period = _muldiv(period, (envpitch < 0 ?
+ linear_slide_down_table : linear_slide_up_table)[l], 0x10000);
+ }
+
+ *nperiod = period;
+ *nenvpitch = envpitch;
+}
+
+
+static inline void _process_envelope(song_voice_t *chan, song_instrument_t *penv, song_envelope_t *envelope,
+ int *position, uint32_t env_flag, uint32_t loop_flag, uint32_t sus_flag,
+ uint32_t fade_flag)
+{
+ int start = 0, end = 0x7fffffff;
+
+ if (!(chan->flags & env_flag)) {
+ return;
+ }
+
+ (*position)++;
+
+ if ((penv->flags & sus_flag) && !(chan->flags & CHN_KEYOFF)) {
+ start = envelope->ticks[envelope->sustain_start];
+ end = envelope->ticks[envelope->sustain_end] + 1;
+ fade_flag = 0;
+ } else if (penv->flags & loop_flag) {
+ start = envelope->ticks[envelope->loop_start];
+ end = envelope->ticks[envelope->loop_end] + 1;
+ fade_flag = 0;
+ } else {
+ // End of envelope (?)
+ start = end = envelope->ticks[envelope->nodes - 1];
+ }
+ if (*position >= end) {
+ if (fade_flag && !envelope->values[envelope->nodes - 1]) {
+ chan->fadeout_volume = chan->final_volume = 0;
+ }
+ *position = start;
+ chan->flags |= fade_flag; // only relevant for volume envelope
+ }
+}
+
+static inline void rn_increment_env_pos(song_voice_t *chan)
+{
+ song_instrument_t *penv = chan->ptr_instrument;
+
+ _process_envelope(chan, penv, &penv->vol_env, &chan->vol_env_position,
+ CHN_VOLENV, ENV_VOLLOOP, ENV_VOLSUSTAIN, CHN_NOTEFADE);
+ _process_envelope(chan, penv, &penv->pan_env, &chan->pan_env_position,
+ CHN_PANENV, ENV_PANLOOP, ENV_PANSUSTAIN, 0);
+ _process_envelope(chan, penv, &penv->pitch_env, &chan->pitch_env_position,
+ CHN_PITCHENV, ENV_PITCHLOOP, ENV_PITCHSUSTAIN, 0);
+}
+
+
+static inline int rn_update_sample(song_t *csf, song_voice_t *chan, int nchan, int master_vol)
+{
+ // Adjusting volumes
+ if (csf->mix_channels < 2 || (csf->flags & SONG_NOSTEREO)) {
+ chan->right_volume_new = (chan->final_volume * master_vol) >> 8;
+ chan->left_volume_new = chan->right_volume_new;
+ } else if ((chan->flags & CHN_SURROUND) && !(csf->mix_flags & SNDMIX_NOSURROUND)) {
+ chan->right_volume_new = (chan->final_volume * master_vol) >> 8;
+ chan->left_volume_new = -chan->right_volume_new;
+ } else {
+ int pan = ((int) chan->final_panning) - 128;
+ pan *= (int) csf->pan_separation;
+ pan /= 128;
+
+
+ pan += 128;
+ pan = CLAMP(pan, 0, 256);
+
+ if (csf->mix_flags & SNDMIX_REVERSESTEREO)
+ pan = 256 - pan;
+
+ int realvol = (chan->final_volume * master_vol) >> (8 - 1);
+
+ chan->left_volume_new = (realvol * pan) >> 8;
+ chan->right_volume_new = (realvol * (256 - pan)) >> 8;
+ }
+
+ // Clipping volumes
+ if (chan->right_volume_new > 0xFFFF)
+ chan->right_volume_new = 0xFFFF;
+
+ if (chan->left_volume_new > 0xFFFF)
+ chan->left_volume_new = 0xFFFF;
+
+ // Check IDO
+ if (csf->mix_flags & SNDMIX_NORESAMPLING) {
+ chan->flags &= ~(CHN_HQSRC);
+ chan->flags |= CHN_NOIDO;
+ } else {
+ chan->flags &= ~(CHN_NOIDO | CHN_HQSRC);
+
+ if (chan->increment == 0x10000) {
+ chan->flags |= CHN_NOIDO;
+ } else {
+ if (!(csf->mix_flags & SNDMIX_HQRESAMPLER) &&
+ !(csf->mix_flags & SNDMIX_ULTRAHQSRCMODE)) {
+ if (chan->increment >= 0xFF00)
+ chan->flags |= CHN_NOIDO;
+ }
+ }
+ }
+
+ chan->right_volume_new >>= MIXING_ATTENUATION;
+ chan->left_volume_new >>= MIXING_ATTENUATION;
+ chan->right_ramp =
+ chan->left_ramp = 0;
+
+ // Checking Ping-Pong Loops
+ if (chan->flags & CHN_PINGPONGFLAG)
+ chan->increment = -chan->increment;
+
+ if (chan->flags & CHN_MUTE) {
+ chan->left_volume = chan->right_volume = 0;
+ } else if (!(csf->mix_flags & SNDMIX_NORAMPING) &&
+ chan->flags & CHN_VOLUMERAMP &&
+ (chan->right_volume != chan->right_volume_new ||
+ chan->left_volume != chan->left_volume_new)) {
+ // Setting up volume ramp
+ int ramp_length = volume_ramp_samples;
+ int right_delta = ((chan->right_volume_new - chan->right_volume) << VOLUMERAMPPRECISION);
+ int left_delta = ((chan->left_volume_new - chan->left_volume) << VOLUMERAMPPRECISION);
+
+ if (csf->mix_flags & SNDMIX_HQRESAMPLER) {
+ if (chan->right_volume | chan->left_volume &&
+ chan->right_volume_new | chan->left_volume_new &&
+ !(chan->flags & CHN_FASTVOLRAMP)) {
+ ramp_length = csf->buffer_count;
+
+ int l = (1 << (VOLUMERAMPPRECISION - 1));
+ int r =(int) volume_ramp_samples;
+
+ ramp_length = CLAMP(ramp_length, l, r);
+ }
+ }
+
+ chan->right_ramp = right_delta / ramp_length;
+ chan->left_ramp = left_delta / ramp_length;
+ chan->right_volume = chan->right_volume_new - ((chan->right_ramp * ramp_length) >> VOLUMERAMPPRECISION);
+ chan->left_volume = chan->left_volume_new - ((chan->left_ramp * ramp_length) >> VOLUMERAMPPRECISION);
+
+ if (chan->right_ramp | chan->left_ramp) {
+ chan->ramp_length = ramp_length;
+ } else {
+ chan->flags &= ~CHN_VOLUMERAMP;
+ chan->right_volume = chan->right_volume_new;
+ chan->left_volume = chan->left_volume_new;
+ }
+ } else {
+ chan->flags &= ~CHN_VOLUMERAMP;
+ chan->right_volume = chan->right_volume_new;
+ chan->left_volume = chan->left_volume_new;
+ }
+
+ chan->right_ramp_volume = chan->right_volume << VOLUMERAMPPRECISION;
+ chan->left_ramp_volume = chan->left_volume << VOLUMERAMPPRECISION;
+
+ // Adding the channel in the channel list
+ csf->voice_mix[csf->num_voices++] = nchan;
+
+ if (csf->num_voices >= MAX_VOICES)
+ return 0;
+
+ return 1;
+}
+
+
+// XXX Rename this
+static inline void rn_gen_key(song_t *csf, song_voice_t *chan, int chan_num, int freq, int vol)
+{
+ if (chan->flags & CHN_MUTE) {
+ // don't do anything
+ return;
+ } else if (csf->flags & SONG_INSTRUMENTMODE &&
+ chan->ptr_instrument &&
+ chan->ptr_instrument->midi_channel_mask > 0) {
+
+ // Vol maximum is 64*64 here. (4096)
+ int volume = vol;
+
+ if ((chan->flags & CHN_ADLIB) && volume > 0) {
+ // This gives a value in the range 0..127.
+ //int o = volume;
+ volume = find_volume((unsigned short) volume) * chan->instrument_volume / 64;
+ //fprintf(stderr, "%d -> %d[%d]\n", o, volume, chan->instrument_volume);
+ } else {
+ // This gives a value in the range 0..127.
+ volume = volume * chan->instrument_volume / 8192;
+ }
+
+ }
+ if (chan->flags & CHN_ADLIB) {
+ // Scaling is needed to get a frequency that matches with ST3 notes.
+ // 8363 is st3s middle C sample rate. 261.625 is the Hertz for middle C in a tempered scale (A4 = 440)
+ //Also, note that to be true to ST3, the frequencies should be quantized, like using the glissando control.
+
+ int oplmilliHertz = (long long int)freq*261625L/8363L;
+ OPL_HertzTouch(chan_num, oplmilliHertz, chan->flags & CHN_KEYOFF);
+
+ // ST32 ignores global & master volume in adlib mode, guess we should do the same -Bisqwit
+ OPL_Touch(chan_num, NULL, vol * chan->instrument_volume * 63 / (1 << 20));
+ }
+}
+
+
+static inline void update_vu_meter(song_voice_t *chan)
+{
+ // Update VU-Meter (final_volume is 14-bit)
+ // TODO: missing background channels by doing it this way.
+ // need to use nMasterCh, add the vu meters for each physical voice, and bit shift.
+ uint32_t vutmp = chan->final_volume >> (14 - 8);
+ if (vutmp > 0xFF) vutmp = 0xFF;
+ if (chan->flags & CHN_ADLIB) {
+ if (chan->strike>2) { chan->vu_meter=(0xFF*chan->final_volume)>>14;}
+ // fake VU decay (intentionally similar to ST3)
+ if (chan->vu_meter > VUMETER_DECAY) {
+ chan->vu_meter -= VUMETER_DECAY;
+ } else {
+ chan->vu_meter = 0;
+ }
+ if (chan->vu_meter >= 0x100) {
+ chan->vu_meter = vutmp;
+ }
+ } else if (vutmp && chan->current_sample_data) {
+ // can't fake the funk
+ int n;
+ int pos = chan->position; // necessary on 64-bit systems (sometimes pos == -1, weird)
+ if (chan->flags & CHN_16BIT) {
+ const signed short *p = (signed short *)(chan->current_sample_data);
+ if (chan->flags & CHN_STEREO)
+ n = p[2 * pos];
+ else
+ n = p[pos];
+ n >>= 8;
+ } else {
+ const signed char *p = (signed char *)(chan->current_sample_data);
+ if (chan->flags & CHN_STEREO)
+ n = p[2 * pos];
+ else
+ n = p[pos];
+ }
+ if (n < 0)
+ n = -n;
+ vutmp *= n;
+ vutmp >>= 7; // 0..255
+ chan->vu_meter = vutmp;
+ } else {
+ chan->vu_meter = 0;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+int csf_init_player(song_t *csf, int reset)
+{
+ if (max_voices > MAX_VOICES)
+ max_voices = MAX_VOICES;
+
+ csf->mix_frequency = CLAMP(csf->mix_frequency, 4000, MAX_SAMPLE_RATE);
+ volume_ramp_samples = (csf->mix_frequency * VOLUMERAMPLEN) / 100000;
+
+ if (volume_ramp_samples < 8)
+ volume_ramp_samples = 8;
+
+ if (csf->mix_flags & SNDMIX_NORAMPING)
+ volume_ramp_samples = 2;
+
+ g_dry_rofs_vol = g_dry_lofs_vol = 0;
+
+ if (reset) {
+ global_vu_left = 0;
+ global_vu_right = 0;
+ }
+
+ initialize_eq(reset, csf->mix_frequency);
+
+ // retarded hackaround to get adlib to suck less
+ if (csf->mix_frequency != 4000)
+ Fmdrv_Init(csf->mix_frequency);
+ OPL_Reset();
+ return 1;
+}
+
+
+unsigned int csf_read(song_t *csf, void * v_buffer, unsigned int bufsize)
+{
+ uint8_t * buffer = (uint8_t *)v_buffer;
+ convert_t convert_func = clip_32_to_8;
+ int32_t vu_min[2];
+ int32_t vu_max[2];
+ unsigned int bufleft, max, sample_size, count, smpcount, mix_stat=0;
+
+ vu_min[0] = vu_min[1] = 0x7FFFFFFF;
+ vu_max[0] = vu_max[1] = -0x7FFFFFFF;
+
+
+ csf->mix_stat = 0;
+ sample_size = csf->mix_channels;
+
+ if (csf->mix_bits_per_sample == 16) { sample_size *= 2; convert_func = clip_32_to_16; }
+ else if (csf->mix_bits_per_sample == 24) { sample_size *= 3; convert_func = clip_32_to_24; }
+ else if (csf->mix_bits_per_sample == 32) { sample_size *= 4; convert_func = clip_32_to_32; }
+
+ max = bufsize / sample_size;
+
+ if (!max || !buffer) {
+ return 0;
+ }
+
+ bufleft = max;
+
+ if (csf->flags & SONG_ENDREACHED)
+ bufleft = 0; // skip the loop
+
+ while (bufleft > 0) {
+ // Update Channel Data
+
+ if (!csf->buffer_count) {
+ if (!(csf->mix_flags & SNDMIX_DIRECTTODISK))
+ csf->buffer_count = bufleft;
+
+ if (!csf_read_note(csf)) {
+ csf->flags |= SONG_ENDREACHED;
+
+ if (csf->stop_at_order > -1)
+ return 0; /* faster */
+
+ if (bufleft == max)
+ break;
+
+ if (!(csf->mix_flags & SNDMIX_DIRECTTODISK))
+ csf->buffer_count = bufleft;
+ }
+
+ if (!csf->buffer_count)
+ break;
+ }
+
+ count = csf->buffer_count;
+
+ if (count > MIXBUFFERSIZE)
+ count = MIXBUFFERSIZE;
+
+ if (count > bufleft)
+ count = bufleft;
+
+ if (!count)
+ break;
+
+ smpcount = count;
+
+ // Resetting sound buffer
+ stereo_fill(csf->mix_buffer, smpcount, &g_dry_rofs_vol, &g_dry_lofs_vol);
+
+ if (csf->mix_channels >= 2) {
+ smpcount *= 2;
+ csf->mix_stat += csf_create_stereo_mix(csf, count);
+ } else {
+ csf->mix_stat += csf_create_stereo_mix(csf, count);
+ mono_from_stereo(csf->mix_buffer, count);
+ }
+
+ // Handle eq
+ if (csf->mix_channels >= 2)
+ eq_stereo(csf, csf->mix_buffer, count);
+ else
+ eq_mono(csf, csf->mix_buffer, count);
+
+ mix_stat++;
+
+ if (csf->multi_write) {
+ /* multi doesn't actually write meaningful data into 'buffer', so we can use that
+ as temp space for converting */
+ for (unsigned int n = 0; n < 64; n++) {
+ if (csf->multi_write[n].used) {
+ unsigned int bytes = convert_func(buffer, csf->multi_write[n].buffer,
+ smpcount, vu_min, vu_max);
+ csf->multi_write[n].write(csf->multi_write[n].data, buffer, bytes);
+ } else {
+ csf->multi_write[n].silence(csf->multi_write[n].data,
+ smpcount * ((csf->mix_bits_per_sample + 7) / 8));
+ }
+ }
+ } else {
+ // Perform clipping + VU-Meter
+ buffer += convert_func(buffer, csf->mix_buffer, smpcount, vu_min, vu_max);
+ }
+
+ // Buffer ready
+ bufleft -= count;
+ csf->buffer_count -= count;
+ }
+
+ if (bufleft)
+ memset(buffer, (csf->mix_bits_per_sample == 8) ? 0x80 : 0, bufleft * sample_size);
+
+ // VU-Meter
+ //Reduce range to 8bits signed (-128 to 127).
+ vu_min[0] >>= 19;
+ vu_min[1] >>= 19;
+ vu_max[0] >>= 19;
+ vu_max[1] >>= 19;
+
+ if (vu_max[0] < vu_min[0])
+ vu_max[0] = vu_min[0];
+
+ if (vu_max[1] < vu_min[1])
+ vu_max[1] = vu_min[1];
+
+ global_vu_left = (unsigned int)(vu_max[0] - vu_min[0]);
+
+ global_vu_right = (unsigned int)(vu_max[1] - vu_min[1]);
+
+ if (mix_stat) {
+ csf->mix_stat += mix_stat - 1;
+ csf->mix_stat /= mix_stat;
+ }
+
+ return max - bufleft;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Handles navigation/effects
+
+static int increment_order(song_t *csf)
+{
+ csf->process_row = csf->break_row; /* [ProcessRow = BreakRow] */
+ csf->break_row = 0; /* [BreakRow = 0] */
+
+ /* some ugly copypasta, this should be less dumb */
+ if (csf->flags & SONG_PATTERNPLAYBACK) {
+ /* process_order is hijacked as a "playback initiated" flag -- otherwise repeat count
+ would be incremented as soon as pattern playback started. (this is a stupid hack) */
+ if (csf->process_order) {
+ if (++csf->repeat_count) {
+ if (UNLIKELY(csf->repeat_count < 0)) {
+ csf->repeat_count = 1; // it overflowed!
+ }
+ } else {
+ csf->process_row = PROCESS_NEXT_ORDER;
+ return 0;
+ }
+ } else {
+ csf->process_order = 1;
+ }
+ } else if (!(csf->flags & SONG_ORDERLOCKED)) {
+ /* [Increase ProcessOrder] */
+ /* [while Order[ProcessOrder] = 0xFEh, increase ProcessOrder] */
+ do {
+ csf->process_order++;
+ } while (csf->orderlist[csf->process_order] == ORDER_SKIP);
+
+ /* [if Order[ProcessOrder] = 0xFFh, ProcessOrder = 0] (... or just stop playing) */
+ if (csf->orderlist[csf->process_order] == ORDER_LAST) {
+ if (++csf->repeat_count) {
+ if (UNLIKELY(csf->repeat_count < 0)) {
+ csf->repeat_count = 1; // it overflowed!
+ }
+ } else {
+ csf->process_row = PROCESS_NEXT_ORDER;
+ return 0;
+ }
+
+ csf->process_order = 0;
+ while (csf->orderlist[csf->process_order] == ORDER_SKIP)
+ csf->process_order++;
+ }
+ if (csf->orderlist[csf->process_order] >= MAX_PATTERNS) {
+ // what the butt?
+ csf->process_row = PROCESS_NEXT_ORDER;
+ return 0;
+ }
+
+ /* [CurrentPattern = Order[ProcessOrder]] */
+ csf->current_order = csf->process_order;
+ csf->current_pattern = csf->orderlist[csf->process_order];
+ }
+
+ if (!csf->pattern_size[csf->current_pattern] || !csf->patterns[csf->current_pattern]) {
+ /* okay, this is wrong. allocate the pattern _NOW_ */
+ csf->patterns[csf->current_pattern] = csf_allocate_pattern(64);
+ csf->pattern_size[csf->current_pattern] = 64;
+ csf->pattern_alloc_size[csf->current_pattern] = 64;
+ }
+
+ if (csf->process_row >= csf->pattern_size[csf->current_pattern]) {
+ // Cxx to row beyond end of pattern: use 0 instead
+ csf->process_row = 0;
+ }
+
+ return 1;
+}
+
+
+int csf_process_tick(song_t *csf)
+{
+ csf->flags &= ~SONG_FIRSTTICK;
+ /* [Decrease tick counter. Is tick counter 0?] */
+ if (--csf->tick_count == 0) {
+ /* [-- Yes --] */
+
+ /* [Tick counter = Tick counter set (the current 'speed')] */
+ csf->tick_count = csf->current_speed;
+
+ /* [Decrease row counter. Is row counter 0?] */
+ if (--csf->row_count <= 0) {
+ /* [-- Yes --] */
+
+ /* [Row counter = 1]
+ this uses zero, in order to simplify SEx effect handling -- SEx has no effect if a
+ channel to its left has already set the delay value. thus we set the row counter
+ there to (value + 1) which is never zero, but 0 and 1 are fundamentally equivalent
+ as far as csf_process_tick is concerned. */
+ csf->row_count = 0;
+
+ /* [Increase ProcessRow. Is ProcessRow > NumberOfRows?] */
+ if (++csf->process_row >= csf->pattern_size[csf->current_pattern]) {
+ /* [-- Yes --] */
+
+ if (!increment_order(csf))
+ return 0;
+ } /* else [-- No --] */
+
+ /* [CurrentRow = ProcessRow] */
+ csf->row = csf->process_row;
+
+ /* [Update Pattern Variables]
+ (this is handled along with update effects) */
+ csf->flags |= SONG_FIRSTTICK;
+ } else {
+ /* [-- No --] */
+ /* Call update-effects for each channel. */
+ }
+
+
+ // Reset channel values
+ song_voice_t *chan = csf->voices;
+ song_note_t *m = csf->patterns[csf->current_pattern] + csf->row * MAX_CHANNELS;
+
+ for (unsigned int nchan=0; nchan<MAX_CHANNELS; chan++, nchan++, m++) {
+ // this is where we're going to spit out our midi
+ // commands... ALL WE DO is dump raw midi data to
+ // our super-secret "midi buffer"
+ // -mrsb
+ if (csf_midi_out_note)
+ csf_midi_out_note(nchan, m);
+
+ chan->row_note = m->note;
+
+ if (m->instrument)
+ chan->last_instrument = m->instrument;
+
+ chan->row_instr = m->instrument;
+ chan->row_voleffect = m->voleffect;
+ chan->row_volparam = m->volparam;
+ chan->row_effect = m->effect;
+ chan->row_param = m->param;
+
+ chan->left_volume = chan->left_volume_new;
+ chan->right_volume = chan->right_volume_new;
+ chan->flags &= ~(CHN_PORTAMENTO | CHN_VIBRATO | CHN_TREMOLO);
+ chan->n_command = 0;
+ }
+
+ csf_process_effects(csf, 1);
+ } else {
+ /* [-- No --] */
+ /* [Update effects for each channel as required.] */
+
+ if (csf_midi_out_note) {
+ song_note_t *m = csf->patterns[csf->current_pattern] + csf->row * MAX_CHANNELS;
+
+ for (unsigned int nchan=0; nchan<MAX_CHANNELS; nchan++, m++) {
+ /* m==NULL allows schism to receive notification of SDx and Scx commands */
+ csf_midi_out_note(nchan, NULL);
+ }
+ }
+
+ csf_process_effects(csf, 0);
+ }
+
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Handles envelopes & mixer setup
+
+int csf_read_note(song_t *csf)
+{
+ song_voice_t *chan;
+ unsigned int cn;
+
+ // Checking end of row ?
+ if (csf->flags & SONG_PAUSED) {
+ if (!csf->current_speed)
+ csf->current_speed = csf->initial_speed ?: 6;
+ if (!csf->current_tempo)
+ csf->current_tempo = csf->initial_tempo ?: 125;
+
+ csf->flags &= ~SONG_FIRSTTICK;
+
+ if (--csf->tick_count == 0) {
+ csf->tick_count = csf->current_speed;
+ if (--csf->row_count <= 0) {
+ csf->row_count = 0;
+ //csf->flags |= SONG_FIRSTTICK;
+ }
+ // clear channel values (similar to csf_process_tick)
+ for (cn = 0, chan = csf->voices; cn < MAX_CHANNELS; cn++, chan++) {
+ chan->row_note = 0;
+ chan->row_instr = 0;
+ chan->row_voleffect = 0;
+ chan->row_volparam = 0;
+ chan->row_effect = 0;
+ chan->row_param = 0;
+ chan->n_command = 0;
+ }
+ }
+ csf_process_effects(csf, 0);
+ } else {
+ if (!csf_process_tick(csf))
+ return 0;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////
+
+ if (!csf->current_tempo)
+ return 0;
+
+ csf->buffer_count = (csf->mix_frequency * 5 * csf->tempo_factor) / (csf->current_tempo << 8);
+
+ // chaseback hoo hah
+ if (csf->stop_at_order > -1 && csf->stop_at_row > -1) {
+ if (csf->stop_at_order <= (signed) csf->current_order &&
+ csf->stop_at_row <= (signed) csf->row) {
+ return 0;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////
+ // Update channels data
+
+ // Master Volume + Pre-Amplification / Attenuation setup
+ uint32_t master_vol = csf->mixing_volume << 2; // yields maximum of 0x200
+
+ csf->num_voices = 0;
+
+ for (cn = 0, chan = csf->voices; cn < MAX_VOICES; cn++, chan++) {
+ /*if(cn == 0 || cn == 1)
+ fprintf(stderr, "considering channel %d (per %d, pos %d/%d, flags %X)\n",
+ (int)cn, chan->period, chan->position, chan->length, chan->flags);*/
+
+ if (chan->flags & CHN_NOTEFADE &&
+ !(chan->fadeout_volume | chan->right_volume | chan->left_volume)) {
+ chan->length = 0;
+ chan->rofs =
+ chan->lofs = 0;
+ continue;
+ }
+
+ // Check for unused channel
+ if (cn >= MAX_CHANNELS && !chan->length) {
+ continue;
+ }
+
+ // Reset channel data
+ chan->increment = 0;
+ chan->final_volume = 0;
+ chan->final_panning = chan->panning + chan->pan_swing + chan->panbrello_delta;
+ chan->ramp_length = 0;
+
+ // Calc Frequency
+ if (chan->period && (chan->length || (chan->flags & CHN_ADLIB))) {
+ int vol = chan->volume;
+
+ if (chan->flags & CHN_TREMOLO)
+ vol += chan->tremolo_delta;
+
+ vol = CLAMP(vol, 0, 256);
+
+ // Tremor
+ if (chan->n_command == FX_TREMOR)
+ rn_tremor(chan, &vol);
+
+ // Clip volume
+ vol = CLAMP(vol, 0, 0x100);
+ vol <<= 6;
+
+ // Process Envelopes
+ if ((csf->flags & SONG_INSTRUMENTMODE) && chan->ptr_instrument) {
+ rn_process_envelope(chan, &vol);
+ } else {
+ // No Envelope: key off => note cut
+ // 1.41-: CHN_KEYOFF|CHN_NOTEFADE
+ if (chan->flags & CHN_NOTEFADE) {
+ chan->fadeout_volume = 0;
+ vol = 0;
+ }
+ }
+
+ // vol is 14-bits
+ if (vol) {
+ // IMPORTANT: chan->final_volume is 14 bits !!!
+ // -> _muldiv( 14+7, 6+6, 18); => RealVolume: 14-bit result (21+12-19)
+ chan->final_volume = _muldiv
+ (vol * csf->current_global_volume,
+ chan->global_volume
+ * CLAMP(chan->instrument_volume + chan->vol_swing, 0, 64),
+ 1 << 19);
+ }
+
+ int period = chan->period;
+
+ if ((chan->flags & (CHN_GLISSANDO|CHN_PORTAMENTO)) == (CHN_GLISSANDO|CHN_PORTAMENTO)) {
+ period = get_period_from_note(get_note_from_period(period),
+ chan->c5speed, csf->flags & SONG_LINEARSLIDES);
+ }
+
+ // Arpeggio ?
+ if (chan->n_command == FX_ARPEGGIO)
+ period = rn_arpeggio(csf, chan, period);
+
+ // Pitch/Filter Envelope
+ int envpitch = 0;
+
+ if ((csf->flags & SONG_INSTRUMENTMODE) && chan->ptr_instrument
+ && (chan->flags & CHN_PITCHENV) && chan->ptr_instrument->pitch_env.nodes)
+ rn_pitch_filter_envelope(chan, &envpitch, &period);
+
+ // Vibrato
+ if (chan->flags & CHN_VIBRATO)
+ period = rn_vibrato(csf, chan, period);
+
+ // Sample Auto-Vibrato
+ if (chan->ptr_sample && chan->ptr_sample->vib_depth) {
+ period = rn_sample_vibrato(chan, period);
+ }
+
+ unsigned int freq = get_freq_from_period(period, csf->flags & SONG_LINEARSLIDES);
+
+ if (!(chan->flags & CHN_NOTEFADE))
+ rn_gen_key(csf, chan, cn, freq, vol);
+
+ // Filter Envelope: controls cutoff frequency
+ if (chan && chan->ptr_instrument && chan->ptr_instrument->flags & ENV_FILTER) {
+ setup_channel_filter(chan,
+ !(chan->flags & CHN_FILTER), envpitch, csf->mix_frequency);
+ }
+
+ chan->sample_freq = freq;
+
+ unsigned int ninc = _muldiv(freq, 0x10000, csf->mix_frequency);
+
+ if (ninc >= 0xFFB0 && ninc <= 0x10090)
+ ninc = 0x10000;
+
+ if (csf->freq_factor != 128)
+ ninc = (ninc * csf->freq_factor) >> 7;
+
+ if (ninc > 0xFF0000)
+ ninc = 0xFF0000;
+
+ chan->increment = (ninc + 1) & ~3;
+ }
+
+ // Increment envelope position
+ if (csf->flags & SONG_INSTRUMENTMODE && chan->ptr_instrument)
+ rn_increment_env_pos(chan);
+
+ chan->final_panning = CLAMP(chan->final_panning, 0, 256);
+
+ // Volume ramping
+ chan->flags &= ~CHN_VOLUMERAMP;
+
+ if (chan->final_volume || chan->left_volume || chan->right_volume)
+ chan->flags |= CHN_VOLUMERAMP;
+
+ if (chan->strike)
+ chan->strike--;
+
+ // Check for too big increment
+ if (((chan->increment >> 16) + 1) >= (int)(chan->loop_end - chan->loop_start))
+ chan->flags &= ~CHN_LOOP;
+
+ chan->right_volume_new = chan->left_volume_new = 0;
+ if (!(chan->length && chan->increment))
+ chan->current_sample_data = NULL;
+
+ update_vu_meter(chan);
+
+ if (chan->current_sample_data) {
+ if (!rn_update_sample(csf, chan, cn, master_vol))
+ break;
+ } else {
+ // Note change but no sample
+ //if (chan->vu_meter > 0xFF) chan->vu_meter = 0;
+ chan->left_volume = chan->right_volume = 0;
+ chan->length = 0;
+ }
+ }
+
+ // Checking Max Mix Channels reached: ordering by volume
+ if (csf->num_voices >= max_voices && (!(csf->mix_flags & SNDMIX_DIRECTTODISK))) {
+ for (unsigned int i = 0; i < csf->num_voices; i++) {
+ unsigned int j = i;
+
+ while ((j + 1 < csf->num_voices) &&
+ (csf->voices[csf->voice_mix[j]].final_volume
+ < csf->voices[csf->voice_mix[j + 1]].final_volume))
+ {
+ unsigned int n = csf->voice_mix[j];
+ csf->voice_mix[j] = csf->voice_mix[j + 1];
+ csf->voice_mix[j + 1] = n;
+ j++;
+ }
+ }
+ }
+
+ return 1;
+}
+
diff --git a/src/player/tables.c b/src/player/tables.c
new file mode 100644
index 0000000..e312cd7
--- /dev/null
+++ b/src/player/tables.c
@@ -0,0 +1,443 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "tables.h"
+
+const uint8_t vc_portamento_table[16] = {
+ 0x00, 0x01, 0x04, 0x08, 0x10, 0x20, 0x40, 0x60,
+ 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+const uint16_t period_table[12] = {
+ 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907,
+};
+
+
+const uint16_t finetune_table[16] = {
+ 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280,
+ 8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757, // 8363*2^((i-8)/(12*8))
+};
+
+
+
+// Tables from ITTECH.TXT
+
+const int8_t sine_table[256] = {
+ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+ 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+ 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+ 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+ 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+ 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
+ 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+ -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+ -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+ -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+ -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+ -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+ -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2,
+};
+
+const int8_t ramp_down_table[256] = {
+ 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
+ 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48,
+ 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40,
+ 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32,
+ 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24,
+ 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+ 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8,
+ 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0,
+ 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8,
+ -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16,
+ -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,
+ -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,
+ -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,
+ -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,
+ -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56,
+ -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64,
+};
+
+const int8_t square_table[256] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+
+
+// volume fade tables for Retrig Note:
+const int8_t retrig_table_1[16] = { 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 0, 0, 24, 32 };
+const int8_t retrig_table_2[16] = { 0, -1, -2, -4, -8, -16, 0, 0, 0, 1, 2, 4, 8, 16, 0, 0 };
+
+
+
+// round(65536 * 2**(n/768))
+// 768 = 64 extra-fine finetune steps for 12 notes
+// Table content is in 16.16 format
+const uint32_t fine_linear_slide_up_table[16] = {
+ 65536, 65595, 65654, 65714, 65773, 65832, 65892, 65951,
+ 66011, 66071, 66130, 66190, 66250, 66309, 66369, 66429
+};
+
+
+// round(65536 * 2**(-n/768))
+// 768 = 64 extra-fine finetune steps for 12 notes
+// Table content is in 16.16 format
+// Note that there are a few errors in this table (typos?), but well, this table comes straight from ITTECH.TXT...
+// Entry 0 (65535) should be 65536 (this value is unused and most likely stored this way so that it fits in a 16-bit integer)
+// Entry 11 (64888) should be 64889 - rounding error?
+// Entry 15 (64645) should be 64655 - typo?
+const uint32_t fine_linear_slide_down_table[16] = {
+ 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65123,
+ 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645
+};
+
+
+// floor(65536 * 2**(n/192))
+// 192 = 16 finetune steps for 12 notes
+// Table content is in 16.16 format
+const uint32_t linear_slide_up_table[256] = {
+ 65536, 65773, 66010, 66249, 66489, 66729, 66971, 67213,
+ 67456, 67700, 67945, 68190, 68437, 68685, 68933, 69182,
+ 69432, 69684, 69936, 70189, 70442, 70697, 70953, 71209,
+ 71467, 71725, 71985, 72245, 72507, 72769, 73032, 73296,
+ 73561, 73827, 74094, 74362, 74631, 74901, 75172, 75444,
+ 75717, 75991, 76265, 76541, 76818, 77096, 77375, 77655,
+ 77935, 78217, 78500, 78784, 79069, 79355, 79642, 79930,
+ 80219, 80509, 80800, 81093, 81386, 81680, 81976, 82272,
+ 82570, 82868, 83168, 83469, 83771, 84074, 84378, 84683,
+ 84989, 85297, 85605, 85915, 86225, 86537, 86850, 87164,
+ 87480, 87796, 88113, 88432, 88752, 89073, 89395, 89718,
+ 90043, 90369, 90695, 91023, 91353, 91683, 92015, 92347,
+ 92681, 93017, 93353, 93691, 94029, 94370, 94711, 95053,
+ 95397, 95742, 96088, 96436, 96785, 97135, 97486, 97839,
+ 98193, 98548, 98904, 99262, 99621, 99981, 100343, 100706,
+ 101070, 101435, 101802, 102170, 102540, 102911, 103283, 103657,
+ 104031, 104408, 104785, 105164, 105545, 105926, 106309, 106694,
+ 107080, 107467, 107856, 108246, 108637, 109030, 109425, 109820,
+ 110217, 110616, 111016, 111418, 111821, 112225, 112631, 113038,
+ 113447, 113857, 114269, 114682, 115097, 115514, 115931, 116351,
+ 116771, 117194, 117618, 118043, 118470, 118898, 119328, 119760,
+ 120193, 120628, 121064, 121502, 121941, 122382, 122825, 123269,
+ 123715, 124162, 124611, 125062, 125514, 125968, 126424, 126881,
+ 127340, 127801, 128263, 128727, 129192, 129660, 130129, 130599,
+ 131072, 131546, 132021, 132499, 132978, 133459, 133942, 134426,
+ 134912, 135400, 135890, 136381, 136875, 137370, 137866, 138365,
+ 138865, 139368, 139872, 140378, 140885, 141395, 141906, 142419,
+ 142935, 143451, 143970, 144491, 145014, 145538, 146064, 146593,
+ 147123, 147655, 148189, 148725, 149263, 149803, 150344, 150888,
+ 151434, 151982, 152531, 153083, 153637, 154192, 154750, 155310,
+ 155871, 156435, 157001, 157569, 158138, 158710, 159284, 159860,
+ 160439, 161019, 161601, 162186, 162772, 163361, 163952, 164545,
+};
+
+
+// floor(65536 * 2**(-n/192))
+// 192 = 16 finetune steps for 12 notes
+// Table content is in 16.16 format
+const uint32_t linear_slide_down_table[256] = {
+ 65536, 65299, 65064, 64830, 64596, 64363, 64131, 63900,
+ 63670, 63440, 63212, 62984, 62757, 62531, 62305, 62081,
+ 61857, 61634, 61412, 61191, 60970, 60751, 60532, 60314,
+ 60096, 59880, 59664, 59449, 59235, 59021, 58809, 58597,
+ 58385, 58175, 57965, 57757, 57548, 57341, 57134, 56928,
+ 56723, 56519, 56315, 56112, 55910, 55709, 55508, 55308,
+ 55108, 54910, 54712, 54515, 54318, 54123, 53928, 53733,
+ 53540, 53347, 53154, 52963, 52772, 52582, 52392, 52204,
+ 52015, 51828, 51641, 51455, 51270, 51085, 50901, 50717,
+ 50535, 50353, 50171, 49990, 49810, 49631, 49452, 49274,
+ 49096, 48919, 48743, 48567, 48392, 48218, 48044, 47871,
+ 47698, 47526, 47355, 47185, 47014, 46845, 46676, 46508,
+ 46340, 46173, 46007, 45841, 45676, 45511, 45347, 45184,
+ 45021, 44859, 44697, 44536, 44376, 44216, 44056, 43898,
+ 43740, 43582, 43425, 43268, 43112, 42957, 42802, 42648,
+ 42494, 42341, 42189, 42037, 41885, 41734, 41584, 41434,
+ 41285, 41136, 40988, 40840, 40693, 40546, 40400, 40254,
+ 40109, 39965, 39821, 39677, 39534, 39392, 39250, 39108,
+ 38967, 38827, 38687, 38548, 38409, 38270, 38132, 37995,
+ 37858, 37722, 37586, 37450, 37315, 37181, 37047, 36913,
+ 36780, 36648, 36516, 36384, 36253, 36122, 35992, 35862,
+ 35733, 35604, 35476, 35348, 35221, 35094, 34968, 34842,
+ 34716, 34591, 34466, 34342, 34218, 34095, 33972, 33850,
+ 33728, 33606, 33485, 33364, 33244, 33124, 33005, 32886,
+ 32768, 32649, 32532, 32415, 32298, 32181, 32065, 31950,
+ 31835, 31720, 31606, 31492, 31378, 31265, 31152, 31040,
+ 30928, 30817, 30706, 30595, 30485, 30375, 30266, 30157,
+ 30048, 29940, 29832, 29724, 29617, 29510, 29404, 29298,
+ 29192, 29087, 28982, 28878, 28774, 28670, 28567, 28464,
+ 28361, 28259, 28157, 28056, 27955, 27854, 27754, 27654,
+ 27554, 27455, 27356, 27257, 27159, 27061, 26964, 26866,
+ 26770, 26673, 26577, 26481, 26386, 26291, 26196, 26102,
+};
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+const char *midi_group_names[17] = {
+ "Piano",
+ "Chromatic Percussion",
+ "Organ",
+ "Guitar",
+ "Bass",
+ "Strings",
+ "Ensemble",
+ "Brass",
+ "Reed",
+ "Pipe",
+ "Synth Lead",
+ "Synth Pad",
+ "Synth Effects",
+ "Ethnic",
+ "Percussive",
+ "Sound Effects",
+ "Percussions",
+};
+
+const char *midi_program_names[128] = {
+ // 1-8: Piano
+ "Acoustic Grand Piano",
+ "Bright Acoustic Piano",
+ "Electric Grand Piano",
+ "Honky-tonk Piano",
+ "Electric Piano 1",
+ "Electric Piano 2",
+ "Harpsichord",
+ "Clavi",
+ // 9-16: Chromatic Percussion
+ "Celesta",
+ "Glockenspiel",
+ "Music Box",
+ "Vibraphone",
+ "Marimba",
+ "Xylophone",
+ "Tubular Bells",
+ "Dulcimer",
+ // 17-24: Organ
+ "Drawbar Organ",
+ "Percussive Organ",
+ "Rock Organ",
+ "Church Organ",
+ "Reed Organ",
+ "Accordion",
+ "Harmonica",
+ "Tango Accordion",
+ // 25-32: Guitar
+ "Acoustic Guitar (nylon)",
+ "Acoustic Guitar (steel)",
+ "Electric Guitar (jazz)",
+ "Electric Guitar (clean)",
+ "Electric Guitar (muted)",
+ "Overdriven Guitar",
+ "Distortion Guitar",
+ "Guitar harmonics",
+ // 33-40 Bass
+ "Acoustic Bass",
+ "Electric Bass (finger)",
+ "Electric Bass (pick)",
+ "Fretless Bass",
+ "Slap Bass 1",
+ "Slap Bass 2",
+ "Synth Bass 1",
+ "Synth Bass 2",
+ // 41-48 Strings
+ "Violin",
+ "Viola",
+ "Cello",
+ "Contrabass",
+ "Tremolo Strings",
+ "Pizzicato Strings",
+ "Orchestral Harp",
+ "Timpani",
+ // 49-56 Ensemble
+ "String Ensemble 1",
+ "String Ensemble 2",
+ "SynthStrings 1",
+ "SynthStrings 2",
+ "Choir Aahs",
+ "Voice Oohs",
+ "Synth Voice",
+ "Orchestra Hit",
+ // 57-64 Brass
+ "Trumpet",
+ "Trombone",
+ "Tuba",
+ "Muted Trumpet",
+ "French Horn",
+ "Brass Section",
+ "SynthBrass 1",
+ "SynthBrass 2",
+ // 65-72 Reed
+ "Soprano Sax",
+ "Alto Sax",
+ "Tenor Sax",
+ "Baritone Sax",
+ "Oboe",
+ "English Horn",
+ "Bassoon",
+ "Clarinet",
+ // 73-80 Pipe
+ "Piccolo",
+ "Flute",
+ "Recorder",
+ "Pan Flute",
+ "Blown Bottle",
+ "Shakuhachi",
+ "Whistle",
+ "Ocarina",
+ // 81-88 Synth Lead
+ "Lead 1 (square)",
+ "Lead 2 (sawtooth)",
+ "Lead 3 (calliope)",
+ "Lead 4 (chiff)",
+ "Lead 5 (charang)",
+ "Lead 6 (voice)",
+ "Lead 7 (fifths)",
+ "Lead 8 (bass + lead)",
+ // 89-96 Synth Pad
+ "Pad 1 (new age)",
+ "Pad 2 (warm)",
+ "Pad 3 (polysynth)",
+ "Pad 4 (choir)",
+ "Pad 5 (bowed)",
+ "Pad 6 (metallic)",
+ "Pad 7 (halo)",
+ "Pad 8 (sweep)",
+ // 97-104 Synth Effects
+ "FX 1 (rain)",
+ "FX 2 (soundtrack)",
+ "FX 3 (crystal)",
+ "FX 4 (atmosphere)",
+ "FX 5 (brightness)",
+ "FX 6 (goblins)",
+ "FX 7 (echoes)",
+ "FX 8 (sci-fi)",
+ // 105-112 Ethnic
+ "Sitar",
+ "Banjo",
+ "Shamisen",
+ "Koto",
+ "Kalimba",
+ "Bag pipe",
+ "Fiddle",
+ "Shanai",
+ // 113-120 Percussive
+ "Tinkle Bell",
+ "Agogo",
+ "Steel Drums",
+ "Woodblock",
+ "Taiko Drum",
+ "Melodic Tom",
+ "Synth Drum",
+ "Reverse Cymbal",
+ // 121-128 Sound Effects
+ "Guitar Fret Noise",
+ "Breath Noise",
+ "Seashore",
+ "Bird Tweet",
+ "Telephone Ring",
+ "Helicopter",
+ "Applause",
+ "Gunshot",
+};
+
+// Notes 25-85
+const char *midi_percussion_names[61] = {
+ "Seq Click",
+ "Brush Tap",
+ "Brush Swirl",
+ "Brush Slap",
+ "Brush Swirl W/Attack",
+ "Snare Roll",
+ "Castanet",
+ "Snare Lo",
+ "Sticks",
+ "Bass Drum Lo",
+ "Open Rim Shot",
+ "Acoustic Bass Drum",
+ "Bass Drum 1",
+ "Side Stick",
+ "Acoustic Snare",
+ "Hand Clap",
+ "Electric Snare",
+ "Low Floor Tom",
+ "Closed Hi Hat",
+ "High Floor Tom",
+ "Pedal Hi-Hat",
+ "Low Tom",
+ "Open Hi-Hat",
+ "Low-Mid Tom",
+ "Hi Mid Tom",
+ "Crash Cymbal 1",
+ "High Tom",
+ "Ride Cymbal 1",
+ "Chinese Cymbal",
+ "Ride Bell",
+ "Tambourine",
+ "Splash Cymbal",
+ "Cowbell",
+ "Crash Cymbal 2",
+ "Vibraslap",
+ "Ride Cymbal 2",
+ "Hi Bongo",
+ "Low Bongo",
+ "Mute Hi Conga",
+ "Open Hi Conga",
+ "Low Conga",
+ "High Timbale",
+ "Low Timbale",
+ "High Agogo",
+ "Low Agogo",
+ "Cabasa",
+ "Maracas",
+ "Short Whistle",
+ "Long Whistle",
+ "Short Guiro",
+ "Long Guiro",
+ "Claves",
+ "Hi Wood Block",
+ "Low Wood Block",
+ "Mute Cuica",
+ "Open Cuica",
+ "Mute Triangle",
+ "Open Triangle",
+ "Shaker",
+ "Jingle Bell",
+ "Bell Tree",
+};
+
diff --git a/src/sys/alsa/init.c b/src/sys/alsa/init.c
new file mode 100644
index 0000000..88a0d87
--- /dev/null
+++ b/src/sys/alsa/init.c
@@ -0,0 +1,73 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Here be demons. */
+
+#include "headers.h"
+#include "util.h"
+#include "osdefs.h"
+
+#if defined(USE_DLTRICK_ALSA)
+# include <dlfcn.h>
+void *_dltrick_handle = NULL;
+static void *_alsaless_sdl_hack = NULL;
+#else
+# error You are in a maze of twisty little passages, all alike.
+#endif
+
+/* --------------------------------------------------------------------- */
+
+void alsa_dlinit(void)
+{
+ /* okay, this is how this works:
+ * to operate the alsa mixer and alsa midi, we need functions in
+ * libasound.so.2 -- if we can do that, *AND* libSDL has the
+ * ALSA_bootstrap routine- then SDL was built with alsa-support-
+ * which means schism can probably use ALSA - so we set that as the
+ * default here.
+ */
+ _dltrick_handle = dlopen("libasound.so.2", RTLD_NOW);
+ if (!_dltrick_handle)
+ _dltrick_handle = dlopen("libasound.so", RTLD_NOW);
+ if (!getenv("SDL_AUDIODRIVER")) {
+ _alsaless_sdl_hack = dlopen("libSDL-1.2.so.0", RTLD_NOW);
+ if (!_alsaless_sdl_hack)
+ _alsaless_sdl_hack = RTLD_DEFAULT;
+
+#if 0
+ if (_dltrick_handle && _alsaless_sdl_hack
+ && (dlsym(_alsaless_sdl_hack, "ALSA_bootstrap")
+ || dlsym(_alsaless_sdl_hack, "snd_pcm_open"))) {
+ static int (*alsa_snd_pcm_open)(void **pcm,
+ const char *name,
+ int stream,
+ int mode);
+ static int (*alsa_snd_pcm_close)(void *pcm);
+
+ alsa_snd_pcm_open = dlsym(_dltrick_handle, "snd_pcm_open");
+ alsa_snd_pcm_close = dlsym(_dltrick_handle, "snd_pcm_close");
+ }
+#endif
+ }
+}
+
diff --git a/src/sys/alsa/midi-alsa.c b/src/sys/alsa/midi-alsa.c
new file mode 100644
index 0000000..9314939
--- /dev/null
+++ b/src/sys/alsa/midi-alsa.c
@@ -0,0 +1,481 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+
+#include "it.h"
+#include "midi.h"
+
+#include "util.h"
+
+#ifdef USE_ALSA
+#include <sys/poll.h>
+
+#include <alsa/asoundlib.h>
+#ifdef USE_DLTRICK_ALSA
+/* ... */
+#include <alsa/control.h>
+#endif
+#include <alsa/seq.h>
+
+#include <sys/stat.h>
+
+
+
+#define PORT_NAME "Schism Tracker"
+
+
+static snd_seq_t *seq;
+static int local_port = -1;
+
+#define MIDI_BUFSIZE 65536
+static unsigned char big_midi_buf[MIDI_BUFSIZE];
+static int alsa_queue;
+
+struct alsa_midi {
+ int c, p;
+ const char *client;
+ const char *port;
+ snd_midi_event_t *dev;
+ int mark;
+};
+
+/* okay, we do the same trick SDL does to get our alsa library put together */
+#ifdef USE_DLTRICK_ALSA
+/* alright, some explanation:
+
+The libSDL library on Linux doesn't "link with" the alsa library (-lasound)
+so that dynamically-linked binaries using libSDL on Linux will work on systems
+that don't have libasound.so.2 anywhere.
+
+There DO EXIST generic solutions (relaytool) but they interact poorly with what
+SDL is doing, so here is my ad-hoc solution:
+
+We define a bunch of these routines similar to how the dynamic linker does it
+when RTLD_LAZY is used. There might be a slight performance increase if we
+linked them all at once (like libSDL does), but this is certainly a lot easier
+to inline.
+
+If you need additional functions in -lasound in schism, presently they will
+have to be declared here for my binary builds to work.
+
+to use:
+ size_t snd_seq_port_info_sizeof(void);
+
+add here:
+ _any_dltrick(size_t,snd_seq_port_info_sizeof,(void),())
+
+(okay, that one is already done). Here's another one:
+
+ int snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *e,
+ snd_mixer_selem_channel_id_t ch,
+ long *v);
+
+gets:
+ _any_dltrick(int,snd_mixer_selem_get_playback_volume,
+ (snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t ch,long*v),(e,ch,v))
+
+If they return void, like:
+ void snd_midi_event_reset_decode(snd_midi_event_t *d);
+use:
+ _void_dltrick(snd_midi_event_reset_decode,(snd_midi_event_t*d),(d))
+
+None of this code is used, btw, if --enable-alsadltrick isn't supplied to
+the configure script, so to test it, you should use that when developing.
+
+Editor's note: currently there's an explicit directive for the build to fail if
+USE_DLTRICK_ALSA isn't defined. Doesn't say why.
+*/
+
+
+#include <dlfcn.h>
+
+extern void *_dltrick_handle;
+
+/* don't try this at home... */
+#define _void_dltrick(a,b,c) static void (*_dltrick_ ## a)b = NULL; \
+void a b { if (!_dltrick_##a) _dltrick_##a = dlsym(_dltrick_handle, #a); \
+if (!_dltrick_##a) abort(); _dltrick_ ## a c; }
+
+#define _any_dltrick(r,a,b,c) static r (*_dltrick_ ## a)b = NULL; \
+r a b { if (!_dltrick_##a) _dltrick_##a = dlsym(_dltrick_handle, #a); \
+if (!_dltrick_##a) abort(); return _dltrick_ ## a c; }
+
+
+_any_dltrick(size_t,snd_seq_port_info_sizeof,(void),())
+_any_dltrick(size_t,snd_seq_client_info_sizeof,(void),())
+
+_any_dltrick(int,snd_seq_control_queue,(snd_seq_t*s,int q,int type, int value, snd_seq_event_t *ev),
+ (s,q,type,value,ev))
+
+_any_dltrick(int,snd_seq_queue_tempo_malloc,(snd_seq_queue_tempo_t**ptr),(ptr))
+_void_dltrick(snd_seq_queue_tempo_set_tempo,(snd_seq_queue_tempo_t *info, unsigned int tempo),(info,tempo))
+_void_dltrick(snd_seq_queue_tempo_set_ppq,(snd_seq_queue_tempo_t *info, int ppq),(info,ppq))
+_any_dltrick(int,snd_seq_set_queue_tempo,(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo),
+ (handle,q,tempo))
+_any_dltrick(long,snd_midi_event_encode,
+(snd_midi_event_t *dev,const unsigned char *buf,long count,snd_seq_event_t *ev),
+(dev,buf,count,ev))
+_any_dltrick(int,snd_seq_event_output,
+(snd_seq_t *handle, snd_seq_event_t *ev),
+(handle,ev))
+_any_dltrick(int,snd_seq_alloc_queue,(snd_seq_t*h),(h))
+_any_dltrick(int,snd_seq_free_event,
+(snd_seq_event_t *ev),
+(ev))
+_any_dltrick(int,snd_seq_connect_from,
+(snd_seq_t*seeq,int my_port,int src_client, int src_port),
+(seeq,my_port,src_client,src_port))
+_any_dltrick(int,snd_seq_connect_to,
+(snd_seq_t*seeq,int my_port,int dest_client,int dest_port),
+(seeq,my_port,dest_client,dest_port))
+_any_dltrick(int,snd_seq_disconnect_from,
+(snd_seq_t*seeq,int my_port,int src_client, int src_port),
+(seeq,my_port,src_client,src_port))
+_any_dltrick(int,snd_seq_disconnect_to,
+(snd_seq_t*seeq,int my_port,int dest_client,int dest_port),
+(seeq,my_port,dest_client,dest_port))
+_any_dltrick(const char *,snd_strerror,(int errnum),(errnum))
+_any_dltrick(int,snd_seq_poll_descriptors_count,(snd_seq_t*h,short e),(h,e))
+_any_dltrick(int,snd_seq_poll_descriptors,(snd_seq_t*h,struct pollfd*pfds,unsigned int space, short e),
+ (h,pfds,space,e))
+_any_dltrick(int,snd_seq_event_input,(snd_seq_t*h,snd_seq_event_t**ev),(h,ev))
+_any_dltrick(int,snd_seq_event_input_pending,(snd_seq_t*h,int fs),(h,fs))
+_any_dltrick(int,snd_midi_event_new,(size_t s,snd_midi_event_t **rd),(s,rd))
+_any_dltrick(long,snd_midi_event_decode,
+(snd_midi_event_t *dev,unsigned char *buf,long count, const snd_seq_event_t*ev),
+(dev,buf,count,ev))
+_void_dltrick(snd_midi_event_reset_decode,(snd_midi_event_t*d),(d))
+_any_dltrick(int,snd_seq_create_simple_port,
+(snd_seq_t*h,const char *name,unsigned int caps,unsigned int type),
+(h,name,caps,type))
+_any_dltrick(int,snd_seq_drain_output,(snd_seq_t*h),(h))
+_any_dltrick(int,snd_seq_query_next_client,
+(snd_seq_t*h,snd_seq_client_info_t*info),(h,info))
+_any_dltrick(int,snd_seq_client_info_get_client,
+(const snd_seq_client_info_t *info),(info))
+_void_dltrick(snd_seq_client_info_set_client,(snd_seq_client_info_t*inf,int cl),(inf,cl))
+_void_dltrick(snd_seq_port_info_set_client,(snd_seq_port_info_t*inf,int cl),(inf,cl))
+_void_dltrick(snd_seq_port_info_set_port,(snd_seq_port_info_t*inf,int pl),(inf,pl))
+_any_dltrick(int,snd_seq_query_next_port,(snd_seq_t*h,snd_seq_port_info_t*inf),(h,inf))
+_any_dltrick(unsigned int,snd_seq_port_info_get_capability,
+(const snd_seq_port_info_t *inf),(inf))
+_any_dltrick(int,snd_seq_port_info_get_client,(const snd_seq_port_info_t*inf),(inf))
+_any_dltrick(int,snd_seq_port_info_get_port,(const snd_seq_port_info_t*inf),(inf))
+_any_dltrick(const char *,snd_seq_client_info_get_name,(snd_seq_client_info_t*inf),(inf))
+_any_dltrick(const char *,snd_seq_port_info_get_name,(const snd_seq_port_info_t*inf),(inf))
+_any_dltrick(int,snd_seq_open,(snd_seq_t**h,const char *name,int str, int mode),
+(h,name,str,mode))
+_any_dltrick(int,snd_seq_set_client_name,(snd_seq_t*seeq,const char *name),(seeq,name))
+#endif
+
+/* see mixer-alsa.c */
+#undef assert
+#define assert(x)
+
+static void _alsa_drain(struct midi_port *p UNUSED)
+{
+ /* not port specific */
+ snd_seq_drain_output(seq);
+}
+static void _alsa_send(struct midi_port *p, const unsigned char *data, unsigned int len, unsigned int delay)
+{
+ struct alsa_midi *ex;
+ snd_seq_event_t ev;
+ long rr;
+
+ ex = (struct alsa_midi *)p->userdata;
+
+ while (len > 0) {
+ snd_seq_ev_clear(&ev);
+ snd_seq_ev_set_source(&ev, local_port);
+ snd_seq_ev_set_subs(&ev);
+ if (!delay) {
+ snd_seq_ev_set_direct(&ev);
+ } else {
+ snd_seq_ev_schedule_tick(&ev, alsa_queue, 1, delay);
+ }
+
+ /* we handle our own */
+ ev.dest.port = ex->p;
+ ev.dest.client = ex->c;
+
+ rr = snd_midi_event_encode(ex->dev, data, len, &ev);
+ if (rr < 1) break;
+ snd_seq_event_output(seq, &ev);
+ snd_seq_free_event(&ev);
+ data += rr;
+ len -= rr;
+ }
+}
+static int _alsa_start(struct midi_port *p)
+{
+ struct alsa_midi *data;
+ int err;
+
+ err = 0;
+ data = (struct alsa_midi *)p->userdata;
+ if (p->io & MIDI_INPUT) {
+ err = snd_seq_connect_from(seq, 0, data->c, data->p);
+ }
+ if (p->io & MIDI_OUTPUT) {
+ err = snd_seq_connect_to(seq, 0, data->c, data->p);
+ }
+ if (err < 0) {
+ log_appendf(4, "ALSA: %s", snd_strerror(err));
+ return 0;
+ }
+ return 1;
+}
+static int _alsa_stop(struct midi_port *p)
+{
+ struct alsa_midi *data;
+ int err;
+
+ err = 0;
+ data = (struct alsa_midi *)p->userdata;
+ if (p->io & MIDI_OUTPUT) {
+ err = snd_seq_disconnect_to(seq, 0, data->c, data->p);
+ }
+ if (p->io & MIDI_INPUT) {
+ err = snd_seq_disconnect_from(seq, 0, data->c, data->p);
+ }
+ if (err < 0) {
+ log_appendf(4, "ALSA: %s", snd_strerror(err));
+ return 0;
+ }
+ return 1;
+}
+static int _alsa_thread(struct midi_provider *p)
+{
+ int npfd;
+ struct pollfd *pfd;
+ struct midi_port *ptr, *src;
+ struct alsa_midi *data;
+ static snd_midi_event_t *dev = NULL;
+ snd_seq_event_t *ev;
+ long s;
+
+ npfd = snd_seq_poll_descriptors_count(seq, POLLIN);
+ if (npfd <= 0) return 0;
+
+ pfd = (struct pollfd *)mem_alloc(npfd * sizeof(struct pollfd));
+ if (!pfd) return 0;
+
+ for (;;) {
+ if (snd_seq_poll_descriptors(seq, pfd, npfd, POLLIN) != npfd) {
+ free(pfd);
+ return 0;
+ }
+
+ (void)poll(pfd, npfd, -1);
+ do {
+ if (snd_seq_event_input(seq, &ev) < 0) {
+ break;
+ }
+ if (!ev) continue;
+
+ ptr = src = NULL;
+ while (midi_port_foreach(p, &ptr)) {
+ data = (struct alsa_midi *)ptr->userdata;
+ if (ev->source.client == data->c
+ && ev->source.port == data->p
+ && (ptr->io & MIDI_INPUT)) {
+ src = ptr;
+ }
+ }
+ if (!src || !ev) {
+ snd_seq_free_event(ev);
+ continue;
+ }
+
+ if (!dev && snd_midi_event_new(sizeof(big_midi_buf), &dev) < 0) {
+ /* err... */
+ break;
+ }
+
+ s = snd_midi_event_decode(dev, big_midi_buf,
+ sizeof(big_midi_buf), ev);
+ if (s > 0) midi_received_cb(src, big_midi_buf, s);
+ snd_midi_event_reset_decode(dev);
+ snd_seq_free_event(ev);
+ } while (snd_seq_event_input_pending(seq, 0) > 0);
+// snd_seq_drain_output(seq);
+ }
+ return 0;
+}
+static void _alsa_poll(struct midi_provider *_alsa_provider)
+{
+ struct midi_port *ptr;
+ struct alsa_midi *data;
+ char *buffer;
+ int c, p, ok, io;
+ const char *ctext, *ptext;
+
+ snd_seq_client_info_t *cinfo;
+ snd_seq_port_info_t *pinfo;
+
+ if (local_port == -1) {
+
+ local_port = snd_seq_create_simple_port(seq,
+ PORT_NAME,
+ SND_SEQ_PORT_CAP_READ
+ | SND_SEQ_PORT_CAP_WRITE
+ | SND_SEQ_PORT_CAP_SYNC_READ
+ | SND_SEQ_PORT_CAP_SYNC_WRITE
+ | SND_SEQ_PORT_CAP_DUPLEX
+ | SND_SEQ_PORT_CAP_SUBS_READ
+ | SND_SEQ_PORT_CAP_SUBS_WRITE,
+
+ SND_SEQ_PORT_TYPE_APPLICATION
+ | SND_SEQ_PORT_TYPE_SYNTH
+ | SND_SEQ_PORT_TYPE_MIDI_GENERIC
+ | SND_SEQ_PORT_TYPE_MIDI_GM
+ | SND_SEQ_PORT_TYPE_MIDI_GS
+ | SND_SEQ_PORT_TYPE_MIDI_XG
+ | SND_SEQ_PORT_TYPE_MIDI_MT32);
+ } else {
+ /* XXX check to see if changes have been made */
+ return;
+ }
+
+ ptr = NULL;
+ while (midi_port_foreach(_alsa_provider, &ptr)) {
+ data = (struct alsa_midi *)ptr->userdata;
+ data->mark = 0;
+ }
+
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_port_info_alloca(&pinfo);
+ snd_seq_client_info_set_client(cinfo, -1);
+ while (snd_seq_query_next_client(seq, cinfo) >= 0) {
+ int cn = snd_seq_client_info_get_client(cinfo);
+ snd_seq_port_info_set_client(pinfo, cn);
+ snd_seq_port_info_set_port(pinfo, -1);
+ while (snd_seq_query_next_port(seq, pinfo) >= 0) {
+ io = 0;
+ if ((snd_seq_port_info_get_capability(pinfo)
+ & (SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ))
+ == (SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ))
+ io |= MIDI_INPUT;
+ if ((snd_seq_port_info_get_capability(pinfo)
+ & (SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE))
+ == (SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE))
+ io |= MIDI_OUTPUT;
+
+ if (!io) continue;
+
+ c = snd_seq_port_info_get_client(pinfo);
+ if (c == SND_SEQ_CLIENT_SYSTEM) continue;
+
+ p = snd_seq_port_info_get_port(pinfo);
+ ptr = NULL;
+ ok = 0;
+ while (midi_port_foreach(_alsa_provider, &ptr)) {
+ data = (struct alsa_midi *)ptr->userdata;
+ if (data->c == c && data->p == p) {
+ data->mark = 1;
+ ok = 1;
+ }
+ }
+ if (ok) continue;
+ ctext = snd_seq_client_info_get_name(cinfo);
+ ptext = snd_seq_port_info_get_name(pinfo);
+ if (strcmp(ctext, PORT_NAME) == 0
+ && strcmp(ptext, PORT_NAME) == 0) {
+ continue;
+ }
+ data = mem_alloc(sizeof(struct alsa_midi));
+ data->c = c; data->p = p;
+ data->client = ctext;
+ data->mark = 1;
+ data->port = ptext;
+ buffer = NULL;
+
+ if (snd_midi_event_new(MIDI_BUFSIZE, &data->dev) < 0) {
+ /* err... */
+ free(data);
+ continue;
+ }
+
+ if (asprintf(&buffer, "%3d:%-3d %-20.20s %s",
+ c, p, ctext, ptext) == -1) {
+ free(data);
+ continue;
+ }
+
+ midi_port_register(_alsa_provider, io, buffer, data, 1);
+ }
+ }
+
+ /* remove "disappeared" midi ports */
+ ptr = NULL;
+ while (midi_port_foreach(_alsa_provider, &ptr)) {
+ data = (struct alsa_midi *)ptr->userdata;
+ if (data->mark) continue;
+ midi_port_unregister(ptr->num);
+ }
+}
+int alsa_midi_setup(void)
+{
+ static snd_seq_queue_tempo_t *tempo;
+ static struct midi_driver driver;
+
+ /* only bother if alsa midi actually exists, otherwise this will
+ produce useless and annoying error messages on systems where alsa
+ libs are installed but which aren't actually running it */
+ struct stat sbuf;
+ if (stat("/dev/snd/seq", &sbuf) != 0)
+ return 0;
+
+
+#ifdef USE_DLTRICK_ALSA
+ if (!dlsym(_dltrick_handle,"snd_seq_open")) return 0;
+#endif
+ driver.poll = _alsa_poll;
+ driver.thread = _alsa_thread;
+ driver.enable = _alsa_start;
+ driver.disable = _alsa_stop;
+ driver.send = _alsa_send;
+ driver.flags = MIDI_PORT_CAN_SCHEDULE;
+ driver.drain = _alsa_drain;
+
+ if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0
+ || snd_seq_set_client_name(seq, PORT_NAME) < 0) {
+ return 0;
+ }
+
+ alsa_queue = snd_seq_alloc_queue(seq);
+ snd_seq_queue_tempo_malloc(&tempo);
+ snd_seq_queue_tempo_set_tempo(tempo,480000);
+ snd_seq_queue_tempo_set_ppq(tempo, 480);
+ snd_seq_set_queue_tempo(seq, alsa_queue, tempo);
+ snd_seq_start_queue(seq, alsa_queue, NULL);
+ snd_seq_drain_output(seq);
+
+ if (!midi_provider_register("ALSA", &driver)) return 0;
+ return 1;
+}
+
+
+#endif
diff --git a/src/sys/alsa/volume-alsa.c b/src/sys/alsa/volume-alsa.c
new file mode 100644
index 0000000..9f81374
--- /dev/null
+++ b/src/sys/alsa/volume-alsa.c
@@ -0,0 +1,237 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+
+#include "util.h"
+
+#ifdef USE_ALSA
+#include <alsa/asoundlib.h>
+#include <alsa/mixer.h>
+
+static const char *alsa_card_id = "default";
+
+/* --------------------------------------------------------------------- */
+#ifdef USE_DLTRICK_ALSA
+#include <alsa/mixer.h>
+
+/* see midi-alsa for details about how this works */
+#include <dlfcn.h>
+
+extern void *_dltrick_handle;
+
+/* don't try this at home... */
+#define _void_dltrick(a,b,c) static void (*_dltrick_ ## a)b = NULL; \
+void a b { if (!_dltrick_##a) _dltrick_##a = dlsym(_dltrick_handle, #a); \
+if (!_dltrick_##a) abort(); _dltrick_ ## a c; }
+
+#define _any_dltrick(r,a,b,c) static r (*_dltrick_ ## a)b = NULL; \
+r a b { if (!_dltrick_##a) _dltrick_##a = dlsym(_dltrick_handle, #a); \
+if (!_dltrick_##a) abort(); return _dltrick_ ## a c; }
+
+_any_dltrick(snd_mixer_elem_t*, snd_mixer_find_selem,
+(snd_mixer_t*e,const snd_mixer_selem_id_t*sid),(e,sid))
+
+_void_dltrick(snd_mixer_selem_id_set_index,
+(snd_mixer_selem_id_t*obj,unsigned int val),(obj,val))
+
+_void_dltrick(snd_mixer_selem_id_set_name,
+(snd_mixer_selem_id_t*obj,const char *val),(obj,val))
+
+_any_dltrick(int,snd_mixer_selem_set_playback_volume,
+(snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t ch,long v),(e,ch,v))
+
+_any_dltrick(int,snd_mixer_selem_is_playback_mono,(snd_mixer_elem_t*e),(e))
+_any_dltrick(int,snd_mixer_selem_has_playback_channel,(snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t c),(e,c))
+
+_any_dltrick(int,snd_ctl_card_info,(snd_ctl_t*c,snd_ctl_card_info_t*i),(c,i))
+_any_dltrick(size_t,snd_ctl_card_info_sizeof,(void),())
+_any_dltrick(size_t,snd_mixer_selem_id_sizeof,(void),())
+
+_any_dltrick(int,snd_mixer_close,(snd_mixer_t*mm),(mm))
+_any_dltrick(int,snd_mixer_selem_get_playback_volume,
+(snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t ch,long*v),(e,ch,v))
+#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 10) || (SND_LIB_MAJOR < 1)
+_void_dltrick(snd_mixer_selem_get_playback_volume_range,
+(snd_mixer_elem_t*e,long*m,long*v),(e,m,v))
+#else
+_any_dltrick(int,snd_mixer_selem_get_playback_volume_range,
+(snd_mixer_elem_t*e,long*m,long*v),(e,m,v))
+#endif
+_any_dltrick(int,snd_ctl_open,(snd_ctl_t**c,const char *name,int mode),(c,name,mode))
+_any_dltrick(int,snd_ctl_close,(snd_ctl_t*ctl),(ctl))
+_any_dltrick(int,snd_mixer_open,(snd_mixer_t**m,int mode),(m,mode))
+_any_dltrick(int,snd_mixer_attach,(snd_mixer_t*m,const char *name),(m,name))
+_any_dltrick(int,snd_mixer_selem_register,(snd_mixer_t*m,
+ struct snd_mixer_selem_regopt*opt, snd_mixer_class_t **cp),(m,opt,cp))
+_any_dltrick(int,snd_mixer_selem_is_active,(snd_mixer_elem_t*e),(e))
+_any_dltrick(int,snd_mixer_selem_has_playback_volume,(snd_mixer_elem_t*e),(e))
+_any_dltrick(int,snd_mixer_selem_has_capture_switch,(snd_mixer_elem_t*e),(e))
+_any_dltrick(int,snd_mixer_selem_has_capture_switch_joined,(snd_mixer_elem_t*e),(e))
+_any_dltrick(int,snd_mixer_selem_has_capture_switch_exclusive,(snd_mixer_elem_t*e),(e))
+
+_any_dltrick(int,snd_mixer_load,(snd_mixer_t*m),(m))
+_any_dltrick(snd_mixer_elem_t*,snd_mixer_first_elem,(snd_mixer_t*m),(m))
+_any_dltrick(snd_mixer_elem_t*,snd_mixer_elem_next,(snd_mixer_elem_t*m),(m))
+_any_dltrick(snd_mixer_elem_type_t,snd_mixer_elem_get_type,(const snd_mixer_elem_t *obj),(obj))
+#endif
+
+/* alsa is paranoid, so snd_mixer_selem_id_alloca does an assert(&sid), which
+ * of course will never fail, so gcc complains. this shuts that warning up. */
+#undef assert
+#define assert(x)
+
+/* this _could_ change */
+static int current_alsa_range = 255;
+
+
+static void _alsa_writeout(snd_mixer_elem_t *em,
+ snd_mixer_selem_channel_id_t d,
+ int use, int lim)
+{
+ if (use > lim) use = lim;
+ (void)snd_mixer_selem_set_playback_volume(em, d, (long)use);
+}
+static void _alsa_write(snd_mixer_elem_t *em, int *l, int *r, long min, long range)
+{
+ long al, ar;
+ long mr, md;
+
+ al = ((*l) * range / current_alsa_range) + min;
+ ar = ((*r) * range / current_alsa_range) + min;
+
+ mr = min+range;
+
+ if (snd_mixer_selem_is_playback_mono(em)) {
+ md = ((al) + (ar)) / 2;
+
+ _alsa_writeout(em, SND_MIXER_SCHN_MONO, md, mr);
+ } else {
+ _alsa_writeout(em, SND_MIXER_SCHN_FRONT_LEFT, al, mr);
+ _alsa_writeout(em, SND_MIXER_SCHN_FRONT_RIGHT, ar, mr);
+#if 0
+/* this was probably wrong */
+ _alsa_writeout(em, SND_MIXER_SCHN_FRONT_CENTER, md, mr);
+ _alsa_writeout(em, SND_MIXER_SCHN_REAR_LEFT, al, mr);
+ _alsa_writeout(em, SND_MIXER_SCHN_REAR_RIGHT, ar, mr);
+ _alsa_writeout(em, SND_MIXER_SCHN_WOOFER, md, mr);
+#endif
+ }
+}
+static void _alsa_readin(snd_mixer_elem_t *em, snd_mixer_selem_channel_id_t d,
+ int *aa, long min, long range)
+{
+ long v;
+ if (snd_mixer_selem_has_playback_channel(em, d)) {
+ snd_mixer_selem_get_playback_volume(em, d, &v);
+ v -= min;
+ v = (v * current_alsa_range) / range;
+ (*aa) = v;
+ }
+
+}
+static void _alsa_config(UNUSED snd_mixer_elem_t *em, UNUSED int *l, UNUSED int *r, UNUSED long min, long range)
+{
+ current_alsa_range = range;
+}
+
+static void _alsa_read(snd_mixer_elem_t *em, int *l, int *r, long min, long range)
+{
+ if (snd_mixer_selem_is_playback_mono(em)) {
+ _alsa_readin(em, SND_MIXER_SCHN_MONO, l, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_MONO, r, min, range);
+ } else {
+ _alsa_readin(em, SND_MIXER_SCHN_FRONT_LEFT, l, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_FRONT_RIGHT, r, min, range);
+#if 0
+/* this was probably wrong */
+ _alsa_readin(em, SND_MIXER_SCHN_REAR_LEFT, l, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_REAR_RIGHT, r, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_FRONT_CENTER, l, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_FRONT_CENTER, r, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_WOOFER, l, min, range);
+ _alsa_readin(em, SND_MIXER_SCHN_WOOFER, r, min, range);
+#endif
+ }
+}
+
+static void _alsa_doit(void (*busy)(snd_mixer_elem_t *em,
+ int *, int *, long, long), int *l, int *r)
+{
+ long ml, mr;
+ snd_mixer_selem_id_t *sid;
+ snd_mixer_elem_t *em;
+ snd_mixer_t *mix;
+
+ snd_mixer_selem_id_alloca(&sid);
+ snd_mixer_selem_id_set_index(sid, 0);
+ snd_mixer_selem_id_set_name(sid, "Master");
+
+ if (snd_mixer_open(&mix, 0) == 0) {
+ if (snd_mixer_attach(mix, alsa_card_id) < 0) {
+ snd_mixer_close(mix);
+ return;
+ }
+ if (snd_mixer_selem_register(mix, NULL, NULL) < 0) {
+ snd_mixer_close(mix);
+ return;
+ }
+ if (snd_mixer_load(mix) < 0) {
+ snd_mixer_close(mix);
+ return;
+ }
+ em = snd_mixer_find_selem(mix, sid);
+ if (em) {
+ ml = mr = 0;
+ snd_mixer_selem_get_playback_volume_range(em, &ml, &mr);
+ if (ml != mr) {
+ busy(em, l, r, ml, mr - ml);
+ }
+ }
+ snd_mixer_close(mix);
+ }
+
+}
+
+
+int alsa_volume_get_max(void);
+int alsa_volume_get_max(void)
+{
+ int a1, a2;
+ _alsa_doit(_alsa_config, &a1, &a2);
+ return current_alsa_range;
+}
+
+void alsa_volume_read(int *left, int *right);
+void alsa_volume_read(int *left, int *right)
+{
+ *left = *right = 0;
+ _alsa_doit(_alsa_read, left, right);
+}
+
+void alsa_volume_write(int left, int right);
+void alsa_volume_write(int left, int right)
+{
+ _alsa_doit(_alsa_write, &left, &right);
+}
+#endif
diff --git a/src/sys/fd.org/autopackage.apspec b/src/sys/fd.org/autopackage.apspec
new file mode 100644
index 0000000..5d4fb21
--- /dev/null
+++ b/src/sys/fd.org/autopackage.apspec
@@ -0,0 +1,57 @@
+# -*-shell-script-*-
+
+[Meta]
+RootName: @schismtracker.org:1.1
+DisplayName: Schism Tracker
+ShortName: schismtracker
+Maintainer: Mrs. Brisby <mrs.brisby@nimh.org>
+Packager: Mrs. Brisby <mrs.brisby@nimh.org>
+Summary: Schism Tracker is a music editor that matches the look and feel of Impulse Tracker as closely as possible.
+URL: http://schismtracker.org/
+License: GNU General Public License, Version 2
+SoftwareVersion: 1.1
+AutopackageTarget: 1.0
+
+[Description]
+Schism Tracker is a music editor in the spirit of Impulse Tracker. Nearly every
+feature of Impulse Tracker is available in exactly the same manner. Improvements
+have been extremely careful to avoid disturbing any muscle memory that the user
+might have developed with Impulse Tracker.
+
+[BuildPrepare]
+mkdir -p linux-x86-build && cd linux-x86-build && prepareBuild --src .. $EXTRA_ARGS
+
+[BuildUnprepare]
+unprepareBuild
+
+[Imports]
+import <<EOF
+$source_dir/linux-x86-build/schismtracker
+$source_dir/icons/schism-icon-128.png
+$source_dir/icons/schism-itf-icon-128.png
+$source_dir/sys/fd.org/schism.desktop
+$source_dir/sys/fd.org/itf.desktop
+$source_dir/NEWS
+$source_dir/README
+$source_dir/COPYING
+$source_dir/ChangeLog
+EOF
+
+[Prepare]
+# Dependency checking
+require @libsdl.org/sdl 1.2
+
+[Install]
+# Put your installation script here
+installExe schismtracker
+installIcon schism-icon-128.png schism-itf-icon-128.png >/dev/null 2>&1
+installDesktop "AudioVideo" schism.desktop
+installDesktop "AudioVideo" itf.desktop
+installData NEWS
+installData README
+installData COPYING
+installData ChangeLog
+
+[Uninstall]
+# Usually just the following line is enough to uninstall everything
+uninstallFromLog
diff --git a/src/sys/fd.org/itf.desktop b/src/sys/fd.org/itf.desktop
new file mode 100644
index 0000000..0a71838
--- /dev/null
+++ b/src/sys/fd.org/itf.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Version=1.0
+Name=Schism Tracker Font Editor
+Comment=ITF Clone
+Encoding=UTF-8
+Terminal=false
+Exec=schismtracker --font-editor
+Type=Application
+Icon=schism-itf-icon-128.png
+Categories=GNOME;Application;AudioVideo;Audio;Video;
+X-Desktop-File-Install-Version=0.10
diff --git a/src/sys/fd.org/schism.desktop b/src/sys/fd.org/schism.desktop
new file mode 100644
index 0000000..d563964
--- /dev/null
+++ b/src/sys/fd.org/schism.desktop
@@ -0,0 +1,18 @@
+[Desktop Entry]
+Actions=Play;
+Version=1.0
+Name=Schism Tracker
+Comment=Impulse Tracker Clone
+Encoding=UTF-8
+Terminal=false
+TryExec=schismtracker
+Exec=schismtracker %f
+Type=Application
+Icon=schism-icon-128.png
+Categories=GNOME;Application;AudioVideo;Audio;Video;
+MimeType=audio/x-mod
+X-Desktop-File-Install-Version=0.10
+
+[Desktop Action Play]
+Name=Schism Tracker (play song)
+Exec=schismtracker -p %f
diff --git a/src/sys/macosx/Schism_Tracker.app/Contents/Info.plist b/src/sys/macosx/Schism_Tracker.app/Contents/Info.plist
new file mode 100644
index 0000000..ca4d05d
--- /dev/null
+++ b/src/sys/macosx/Schism_Tracker.app/Contents/Info.plist
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>schismtracker</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>it</string>
+ <string>IT</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>moduleIcon.icns</string>
+ <key>CFBundleMIMETypes</key>
+ <array>
+ <string>audio/mod</string>
+ <string>audio/x-mod</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Impulse Tracker Module</string>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>LSIsAppleDefaultForType</key>
+ <true/>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>669</string>
+ <string>amf</string>
+ <string>AMF</string>
+ <string>ams</string>
+ <string>AMS</string>
+ <string>dbm</string>
+ <string>DBM</string>
+ <string>dmf</string>
+ <string>DMF</string>
+ <string>far</string>
+ <string>FAR</string>
+ <string>mdl</string>
+ <string>MDL</string>
+ <string>med</string>
+ <string>MED</string>
+ <string>mod</string>
+ <string>MOD</string>
+ <string>mt2</string>
+ <string>MT2</string>
+ <string>mtm</string>
+ <string>MTM</string>
+ <string>okt</string>
+ <string>OKT</string>
+ <string>psm</string>
+ <string>PSM</string>
+ <string>ptm</string>
+ <string>PTM</string>
+ <string>s3m</string>
+ <string>S3M</string>
+ <string>stm</string>
+ <string>STM</string>
+ <string>ult</string>
+ <string>ULT</string>
+ <string>umx</string>
+ <string>UMX</string>
+ <string>xm</string>
+ <string>XM</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>moduleIcon.icns</string>
+ <key>CFBundleMIMETypes</key>
+ <array>
+ <string>audio/mod</string>
+ <string>audio/x-mod</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Audio Module</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>LSIsAppleDefaultForType</key>
+ <true/>
+ </dict>
+ </array>
+ <key>CFBundleGetInfoString</key>
+ <string>Schism Tracker Copyright 2003-2012 Storlek</string>
+ <key>CFBundleIconFile</key>
+ <string>appIcon.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.schismtracker.SchismTracker</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Schism Tracker</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>hg</string>
+ <key>CFBundleSignature</key>
+ <string>Schm</string>
+ <key>CFBundleVersion</key>
+ <string>hg</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>CGDisableCoalescedUpdates</key>
+ <true/>
+</dict>
+</plist>
diff --git a/src/sys/macosx/Schism_Tracker.app/Contents/PkgInfo b/src/sys/macosx/Schism_Tracker.app/Contents/PkgInfo
new file mode 100644
index 0000000..07af6f8
--- /dev/null
+++ b/src/sys/macosx/Schism_Tracker.app/Contents/PkgInfo
@@ -0,0 +1 @@
+APPLSchm \ No newline at end of file
diff --git a/src/sys/macosx/Schism_Tracker.app/Contents/Resources/AppSettings.plist b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/AppSettings.plist
new file mode 100644
index 0000000..62376f5
--- /dev/null
+++ b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/AppSettings.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>EncryptAndChecksum</key>
+ <false/>
+ <key>IsDroppable</key>
+ <true/>
+ <key>OutputType</key>
+ <string>None</string>
+ <key>RemainRunningAfterCompletion</key>
+ <false/>
+ <key>RequiresAdminPrivileges</key>
+ <false/>
+ <key>ScriptInterpreter</key>
+ <string>/bin/sh</string>
+</dict>
+</plist>
diff --git a/src/sys/macosx/Schism_Tracker.app/Contents/Resources/appIcon.icns b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/appIcon.icns
new file mode 100644
index 0000000..8ed3637
--- /dev/null
+++ b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/appIcon.icns
Binary files differ
diff --git a/src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icns b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icns
new file mode 100644
index 0000000..597056b
--- /dev/null
+++ b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icns
Binary files differ
diff --git a/src/sys/macosx/ibook-support.c b/src/sys/macosx/ibook-support.c
new file mode 100644
index 0000000..71bd00b
--- /dev/null
+++ b/src/sys/macosx/ibook-support.c
@@ -0,0 +1,103 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "headers.h"
+#include "util.h"
+
+int macosx_ibook_fnswitch(int setting); /* FIXME: ugliness */
+
+#ifdef MACOSX
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOReturn.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <IOKit/hidsystem/IOHIDLib.h>
+#include <IOKit/hidsystem/IOHIDParameter.h>
+
+#define kMyDriversKeyboardClassName "AppleADBKeyboard"
+#define kfnSwitchError 200
+#define kfnAppleMode 0
+#define kfntheOtherMode 1
+
+#ifndef kIOHIDFKeyModeKey
+#define kIOHIDFKeyModeKey "HIDFKeyMode"
+#endif
+
+int macosx_ibook_fnswitch(int setting)
+{
+ kern_return_t kr;
+ mach_port_t mp;
+ io_service_t so;
+ /*io_name_t sn;*/
+ io_connect_t dp;
+ io_iterator_t it;
+ CFDictionaryRef classToMatch;
+ /*CFNumberRef fnMode;*/
+ unsigned int res, dummy;
+
+ kr = IOMasterPort(bootstrap_port, &mp);
+ if (kr != KERN_SUCCESS) return -1;
+
+ classToMatch = IOServiceMatching(kIOHIDSystemClass);
+ if (classToMatch == NULL) {
+ return -1;
+ }
+ kr = IOServiceGetMatchingServices(mp, classToMatch, &it);
+ if (kr != KERN_SUCCESS) return -1;
+
+ so = IOIteratorNext(it);
+ IOObjectRelease(it);
+
+ if (!so) return -1;
+
+ kr = IOServiceOpen(so, mach_task_self(), kIOHIDParamConnectType, &dp);
+ if (kr != KERN_SUCCESS) return -1;
+
+ kr = IOHIDGetParameter(dp, CFSTR(kIOHIDFKeyModeKey), sizeof(res),
+ &res, (IOByteCount *) &dummy);
+ if (kr != KERN_SUCCESS) {
+ IOServiceClose(dp);
+ return -1;
+ }
+
+ if (setting == kfnAppleMode || setting == kfntheOtherMode) {
+ dummy = setting;
+ kr = IOHIDSetParameter(dp, CFSTR(kIOHIDFKeyModeKey),
+ &dummy, sizeof(dummy));
+ if (kr != KERN_SUCCESS) {
+ IOServiceClose(dp);
+ return -1;
+ }
+ }
+
+ IOServiceClose(dp);
+ /* old setting... */
+ return res;
+}
+
+#else
+
+int macosx_ibook_fnswitch(UNUSED int setting)
+{
+ return 0;
+}
+#endif
diff --git a/src/sys/macosx/macosx-sdlmain.m b/src/sys/macosx/macosx-sdlmain.m
new file mode 100644
index 0000000..df7f440
--- /dev/null
+++ b/src/sys/macosx/macosx-sdlmain.m
@@ -0,0 +1,619 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* wee....
+
+this is used to do some schism-on-macosx customization
+and get access to cocoa stuff
+
+pruned up some here :) -mrsb
+
+ */
+
+/* SDLMain.m - main entry point for our Cocoa-ized SDL app
+ Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+ Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+ Feel free to customize this file to suit your needs
+*/
+
+extern char *initial_song;
+
+#include <SDL.h> /* necessary here */
+#include "event.h"
+#include "osdefs.h"
+
+#define Cursor AppleCursor
+#import <Cocoa/Cocoa.h>
+#undef Cursor
+
+@interface SDLMain : NSObject
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
+@end
+
+#import <sys/param.h> /* for MAXPATHLEN */
+#import <unistd.h>
+
+/* Portions of CPS.h */
+typedef struct CPSProcessSerNum
+{
+ UInt32 lo;
+ UInt32 hi;
+} CPSProcessSerNum;
+
+extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr CPSSetProcessName ( CPSProcessSerNum *psn, char *processname);
+extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+static int gArgc;
+static char **gArgv;
+static BOOL gFinderLaunch;
+int macosx_did_finderlaunch;
+
+#define KEQ_FN(n) [NSString stringWithFormat:@"%C", NSF##n##FunctionKey]
+
+@interface SDLApplication : NSApplication
+@end
+
+@interface NSApplication(OtherMacOSXExtensions)
+-(void)setAppleMenu:(NSMenu*)m;
+@end
+
+@implementation SDLApplication
+/* Invoked from the Quit menu item */
+- (void)terminate:(id)sender
+{
+ /* Post a SDL_QUIT event */
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ SDL_PushEvent(&event);
+}
+- (void)_menu_callback:(id)sender
+{
+ SDL_Event e;
+ NSString *px;
+ const char *po;
+
+ px = [sender representedObject];
+ po = [px UTF8String];
+ if (po) {
+ e.type = SCHISM_EVENT_NATIVE;
+ e.user.code = SCHISM_EVENT_NATIVE_SCRIPT;
+ e.user.data1 = strdup(po);
+ SDL_PushEvent(&e);
+ }
+}
+
+
+@end
+
+/* The main class of the application, the application's delegate */
+@implementation SDLMain
+
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+ SDL_Event e;
+ const char *po;
+
+ if (!filename) return NO;
+
+ po = [filename UTF8String];
+ if (po) {
+ e.type = SCHISM_EVENT_NATIVE;
+ e.user.code = SCHISM_EVENT_NATIVE_OPEN;
+ e.user.data1 = strdup(po);
+ /* if we started as a result of a doubleclick on
+ a document, then Main still hasn't really started yet.
+ */
+ initial_song = strdup(po);
+ SDL_PushEvent(&e);
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+/* other interesting ones:
+- (BOOL)application:(NSApplication *)theApplication printFile:(NSString *)filename
+- (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication
+*/
+/* Set the working directory to the .app's parent directory */
+- (void) setupWorkingDirectory:(BOOL)shouldChdir
+{
+ if (shouldChdir)
+ {
+ char parentdir[MAXPATHLEN];
+ CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+ if (CFURLGetFileSystemRepresentation(url2, true, (unsigned char *) parentdir, MAXPATHLEN)) {
+ assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
+ }
+ CFRelease(url);
+ CFRelease(url2);
+ }
+
+}
+
+static void setApplicationMenu(void)
+{
+ /* warning: this code is very odd */
+ NSMenu *appleMenu;
+ NSMenu *otherMenu;
+ NSMenuItem *menuItem;
+
+ appleMenu = [[NSMenu alloc] initWithTitle:@""];
+
+ /* Add menu items */
+ [appleMenu addItemWithTitle:@"About Schism Tracker"
+ action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ /* other schism items */
+ menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Help"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(1)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"help"];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+ menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"View Patterns"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(2)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"pattern"];
+ menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Orders/Panning"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(11)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"orders"];
+ menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Variables"
+ action:@selector(_menu_callback:)
+ keyEquivalent:[NSString stringWithFormat:@"%C", NSF12FunctionKey]];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"variables"];
+ menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Message Editor"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(9)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"message_edit"];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ [appleMenu addItemWithTitle:@"Hide Schism Tracker" action:@selector(hide:) keyEquivalent:@"h"];
+
+ menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+
+ [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ [appleMenu addItemWithTitle:@"Quit Schism Tracker" action:@selector(terminate:) keyEquivalent:@"q"];
+
+ /* Put menu into the menubar */
+ menuItem = (NSMenuItem*)[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:appleMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* File menu */
+ otherMenu = [[NSMenu alloc] initWithTitle:@"File"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"New..."
+ action:@selector(_menu_callback:)
+ keyEquivalent:@"n"];
+ [menuItem setKeyEquivalentModifierMask:NSControlKeyMask];
+ [menuItem setRepresentedObject: @"new"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Load..."
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(9)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"load"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Save Current"
+ action:@selector(_menu_callback:)
+ keyEquivalent:@"s"];
+ [menuItem setKeyEquivalentModifierMask:NSControlKeyMask];
+ [menuItem setRepresentedObject: @"save"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Save As..."
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(10)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"save_as"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Export..."
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(10)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"export_song"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Message Log"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(11)];
+ [menuItem setKeyEquivalentModifierMask:NSFunctionKeyMask|NSControlKeyMask];
+ [menuItem setRepresentedObject: @"logviewer"];
+ menuItem = (NSMenuItem*)[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:otherMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Playback menu */
+ otherMenu = [[NSMenu alloc] initWithTitle:@"Playback"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Show Infopage"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(5)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"info"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Play Song"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(5)];
+ [menuItem setKeyEquivalentModifierMask:NSControlKeyMask];
+ [menuItem setRepresentedObject: @"play"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Play Pattern"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(6)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"play_pattern"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Play from Order"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(6)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"play_order"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Play from Mark/Cursor"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(7)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"play_mark"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Stop"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(8)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"stop"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Calculate Length"
+ action:@selector(_menu_callback:)
+ keyEquivalent:@"p"];
+ [menuItem setKeyEquivalentModifierMask:(NSFunctionKeyMask|NSControlKeyMask)];
+ [menuItem setRepresentedObject: @"calc_length"];
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:otherMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Sample menu */
+ otherMenu = [[NSMenu alloc] initWithTitle:@"Samples"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Sample List"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(3)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"sample_page"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Sample Library"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(3)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"sample_library"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Reload Soundcard"
+ action:@selector(_menu_callback:)
+ keyEquivalent:@"g"];
+ [menuItem setKeyEquivalentModifierMask:NSControlKeyMask];
+ [menuItem setRepresentedObject: @"init_sound"];
+ menuItem = (NSMenuItem*)[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:otherMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Instrument menu */
+ otherMenu = [[NSMenu alloc] initWithTitle:@"Instruments"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Instrument List"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(4)];
+ [menuItem setKeyEquivalentModifierMask:0];
+ [menuItem setRepresentedObject: @"inst_page"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Instrument Library"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(4)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"inst_library"];
+ menuItem = (NSMenuItem*)[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:otherMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Settings menu */
+ otherMenu = [[NSMenu alloc] initWithTitle:@"Settings"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Preferences"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(5)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"preferences"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"MIDI Configuration"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(1)];
+ [menuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
+ [menuItem setRepresentedObject: @"midi_config"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Palette Editor"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(12)];
+ [menuItem setKeyEquivalentModifierMask:NSControlKeyMask];
+ [menuItem setRepresentedObject: @"palette_page"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Font Editor"
+ action:@selector(_menu_callback:)
+ keyEquivalent:@""];
+ [menuItem setRepresentedObject: @"font_editor"];
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"System Configuration"
+ action:@selector(_menu_callback:)
+ keyEquivalent:KEQ_FN(1)];
+ [menuItem setKeyEquivalentModifierMask:NSControlKeyMask];
+ [menuItem setRepresentedObject: @"system_config"];
+
+ menuItem = (NSMenuItem*)[otherMenu addItemWithTitle:@"Toggle Fullscreen"
+ action:@selector(_menu_callback:)
+ keyEquivalent:@"\r"];
+ [menuItem setKeyEquivalentModifierMask:(NSControlKeyMask|NSAlternateKeyMask)];
+ [menuItem setRepresentedObject: @"fullscreen"];
+ menuItem = (NSMenuItem*)[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:otherMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Tell the application object that this is now the application menu */
+ [NSApp setAppleMenu:appleMenu];
+
+ /* Finally give up our references to the objects */
+ [appleMenu release];
+ [menuItem release];
+}
+
+/* Create a window menu */
+static void setupWindowMenu(void)
+{
+ NSMenu *windowMenu;
+ NSMenuItem *windowMenuItem;
+ NSMenuItem *menuItem;
+
+ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+
+ /* "Minimize" item */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+ [windowMenu addItem:menuItem];
+ [menuItem release];
+
+ /* Put menu into the menubar */
+ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
+ [windowMenuItem setSubmenu:windowMenu];
+ [[NSApp mainMenu] addItem:windowMenuItem];
+
+ /* Tell the application object that this is now the window menu */
+ [NSApp setWindowsMenu:windowMenu];
+
+ /* Finally give up our references to the objects */
+ [windowMenu release];
+ [windowMenuItem release];
+}
+
+/* Replacement for NSApplicationMain */
+static void CustomApplicationMain (int argc, char **argv)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ SDLMain *sdlMain;
+ CPSProcessSerNum PSN;
+
+ /* Ensure the application object is initialised */
+ [SDLApplication sharedApplication];
+
+ /* Tell the dock about us */
+ if (!CPSGetCurrentProcess(&PSN)) {
+ if (!macosx_did_finderlaunch) {
+ CPSSetProcessName(&PSN,"Schism Tracker");
+ }
+ if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+ if (!CPSSetFrontProcess(&PSN))
+ [SDLApplication sharedApplication];
+ }
+
+ /* Set up the menubar */
+ [NSApp setMainMenu:[[NSMenu alloc] init]];
+ setApplicationMenu();
+ setupWindowMenu();
+
+ /* Create SDLMain and make it the app delegate */
+ sdlMain = [[SDLMain alloc] init];
+ [NSApp setDelegate:sdlMain];
+
+ /* Start the main event loop */
+ [NSApp run];
+
+ [sdlMain release];
+ [pool release];
+}
+
+/* Called when the internal event loop has just started running */
+- (void) applicationDidFinishLaunching: (NSNotification *) note
+{
+ int status;
+
+ /* Set the working directory to the .app's parent directory */
+ [self setupWorkingDirectory:gFinderLaunch];
+
+ /* Hand off to main application code */
+ status = SDL_main (gArgc, gArgv);
+
+ /* We're done, thank you for playing */
+ exit(status);
+}
+@end
+
+
+@implementation NSString (ReplaceSubString)
+
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
+{
+ unsigned int bufferSize;
+ unsigned int selfLen = [self length];
+ unsigned int aStringLen = [aString length];
+ unichar *buffer;
+ NSRange localRange;
+ NSString *result;
+
+ bufferSize = selfLen + aStringLen - aRange.length;
+ buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
+
+ /* Get first part into buffer */
+ localRange.location = 0;
+ localRange.length = aRange.location;
+ [self getCharacters:buffer range:localRange];
+
+ /* Get middle part into buffer */
+ localRange.location = 0;
+ localRange.length = aStringLen;
+ [aString getCharacters:(buffer+aRange.location) range:localRange];
+
+ /* Get last part into buffer */
+ localRange.location = aRange.location + aRange.length;
+ localRange.length = selfLen - localRange.location;
+ [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
+
+ /* Build output string */
+ result = [NSString stringWithCharacters:buffer length:bufferSize];
+
+ NSDeallocateMemoryPages(buffer, bufferSize);
+
+ return result;
+}
+
+@end
+
+
+
+#ifdef main
+# undef main
+#endif
+
+
+/* Main entry point to executable - should *not* be SDL_main! */
+int main (int argc, char **argv)
+{
+ /* Copy the arguments into a global variable */
+ /* This is passed if we are launched by double-clicking */
+ if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
+ gArgc = 1;
+ gFinderLaunch = YES;
+ macosx_did_finderlaunch = 1;
+ } else {
+ gArgc = argc;
+ gFinderLaunch = NO;
+ macosx_did_finderlaunch = 0;
+ }
+ gArgv = argv;
+
+ CustomApplicationMain (argc, argv);
+
+ return 0;
+}
+
+/* these routines provide clipboard encapsulation */
+const char *macosx_clippy_get(void)
+{
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ NSString *type = [pb availableTypeFromArray:[NSArray
+ arrayWithObject:NSStringPboardType]];
+ NSString *contents;
+ const char *po;
+
+ if (type == nil) return "";
+
+ contents = [pb stringForType:type];
+ if (contents == nil) return "";
+ po = [contents UTF8String];
+ if (!po) return "";
+ return po;
+}
+void macosx_clippy_put(const char *buf)
+{
+ NSString *contents = [NSString stringWithUTF8String:buf];
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+ [pb setString:contents forType:NSStringPboardType];
+}
+// ktt appears to be 1/60th of a second?
+unsigned int key_repeat_rate(void)
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ int ktt = [defaults integerForKey:@"KeyRepeat"];
+ if (!ktt || ktt < 0) ktt = 4; // eh?
+ ktt = (ktt * 1000) / 60;
+ return (unsigned)ktt;
+}
+unsigned int key_repeat_delay(void)
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ int ktt = [defaults integerForKey:@"InitialKeyRepeat"];
+ if (!ktt || ktt < 0) ktt = 35;
+ ktt = (ktt * 1000) / 60;
+ return (unsigned)ktt;
+}
+int key_scancode_lookup(int k, int def)
+{
+ switch (k & 127) {
+ case 0x32: /* QZ_BACKQUOTE */ return SDLK_BACKQUOTE;
+ case 0x12: /* QZ_1 */ return SDLK_1;
+ case 0x13: /* QZ_2 */ return SDLK_2;
+ case 0x14: /* QZ_3 */ return SDLK_3;
+ case 0x15: /* QZ_4 */ return SDLK_4;
+ case 0x17: /* QZ_5 */ return SDLK_5;
+ case 0x16: /* QZ_6 */ return SDLK_6;
+ case 0x1A: /* QZ_7 */ return SDLK_7;
+ case 0x1C: /* QZ_8 */ return SDLK_8;
+ case 0x19: /* QZ_9 */ return SDLK_9;
+ case 0x1D: /* QZ_0 */ return SDLK_0;
+ case 0x1B: /* QZ_MINUS */ return SDLK_MINUS;
+ case 0x18: /* QZ_EQUALS */ return SDLK_EQUALS;
+ case 0x0C: /* QZ_q */ return SDLK_q;
+ case 0x0D: /* QZ_w */ return SDLK_w;
+ case 0x0E: /* QZ_e */ return SDLK_e;
+ case 0x0F: /* QZ_r */ return SDLK_r;
+ case 0x11: /* QZ_t */ return SDLK_t;
+ case 0x10: /* QZ_y */ return SDLK_y;
+ case 0x20: /* QZ_u */ return SDLK_u;
+ case 0x22: /* QZ_i */ return SDLK_i;
+ case 0x1F: /* QZ_o */ return SDLK_o;
+ case 0x23: /* QZ_p */ return SDLK_p;
+ case 0x21: /* QZ_[ */ return SDLK_LEFTBRACKET;
+ case 0x1E: /* QZ_] */ return SDLK_RIGHTBRACKET;
+ case 0x2A: /* QZ_backslash */ return SDLK_BACKSLASH;
+ case 0x00: /* QZ_a */ return SDLK_a;
+ case 0x01: /* QZ_s */ return SDLK_s;
+ case 0x02: /* QZ_d */ return SDLK_d;
+ case 0x03: /* QZ_f */ return SDLK_f;
+ case 0x05: /* QZ_g */ return SDLK_g;
+ case 0x04: /* QZ_h */ return SDLK_h;
+ case 0x26: /* QZ_j */ return SDLK_j;
+ case 0x28: /* QZ_k */ return SDLK_k;
+ case 0x25: /* QZ_l */ return SDLK_l;
+ case 0x29: /* QZ_; */ return SDLK_SEMICOLON;
+ case 0x27: /* QZ_quote */ return SDLK_QUOTE;
+ case 0x06: /* QZ_z */ return SDLK_z;
+ case 0x07: /* QZ_x */ return SDLK_x;
+ case 0x08: /* QZ_c */ return SDLK_c;
+ case 0x09: /* QZ_v */ return SDLK_v;
+ case 0x0B: /* QZ_b */ return SDLK_b;
+ case 0x2D: /* QZ_n */ return SDLK_n;
+ case 0x2E: /* QZ_m */ return SDLK_m;
+ case 0x2B: /* QZ_, */ return SDLK_COMMA;
+ case 0x2F: /* QZ_. */ return SDLK_PERIOD;
+ case 0x2C: /* QZ_slash */ return SDLK_SLASH;
+ case 0x31: /* QZ_space */ return SDLK_SPACE;
+ default: return def;
+ };
+}
diff --git a/src/sys/macosx/midi-macosx.c b/src/sys/macosx/midi-macosx.c
new file mode 100644
index 0000000..3342e2c
--- /dev/null
+++ b/src/sys/macosx/midi-macosx.c
@@ -0,0 +1,222 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+
+#include "midi.h"
+
+#include "util.h"
+
+#ifdef MACOSX
+
+#include <CoreServices/CoreServices.h>
+#include <CoreMIDI/MIDIServices.h>
+#include <CoreAudio/HostTime.h>
+
+static MIDIClientRef client = NULL;
+static MIDIPortRef portIn = NULL;
+static MIDIPortRef portOut = NULL;
+
+static int max_outputs = 0;
+static int max_inputs = 0;
+
+struct macosx_midi {
+ char *name;
+ MIDIEndpointRef ep;
+ unsigned char packet[1024];
+ MIDIPacketList *pl;
+ MIDIPacket *x;
+};
+
+static void readProc(const MIDIPacketList *np, UNUSED void *rc, void *crc)
+{
+ struct midi_port *p;
+ struct macosx_midi *m;
+ MIDIPacket *x;
+ unsigned long i;
+
+ p = (struct midi_port *)crc;
+ m = (struct macosx_midi *)p->userdata;
+
+ x = (MIDIPacket*)&np->packet[0];
+ for (i = 0; i < np->numPackets; i++) {
+ midi_received_cb(p, x->data, x->length);
+ x = MIDIPacketNext(x);
+ }
+}
+static void _macosx_send(struct midi_port *p, const unsigned char *data,
+ unsigned int len, unsigned int delay)
+{
+ struct macosx_midi *m;
+
+ m = (struct macosx_midi *)p->userdata;
+ if (!m->x) {
+ m->x = MIDIPacketListInit(m->pl);
+ }
+
+ /* msec to nsec? */
+ m->x = MIDIPacketListAdd(m->pl, sizeof(m->packet),
+ m->x, (MIDITimeStamp)AudioConvertNanosToHostTime(
+ AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) + (1000000*delay)),
+ len, data);
+}
+static void _macosx_drain(struct midi_port *p)
+{
+ struct macosx_midi *m;
+
+ m = (struct macosx_midi *)p->userdata;
+ if (m->x) {
+ MIDISend(portOut, m->ep, m->pl);
+ m->x = NULL;
+ }
+}
+
+/* lifted from portmidi */
+static char *get_ep_name(MIDIEndpointRef ep)
+{
+ MIDIEntityRef entity;
+ MIDIDeviceRef device;
+ CFStringRef endpointName = NULL, deviceName = NULL, fullName = NULL;
+ CFStringEncoding defaultEncoding;
+ char* newName;
+
+ /* get the default string encoding */
+ defaultEncoding = CFStringGetSystemEncoding();
+
+ /* get the entity and device info */
+ MIDIEndpointGetEntity(ep, &entity);
+ MIDIEntityGetDevice(entity, &device);
+
+ /* create the nicely formated name */
+ MIDIObjectGetStringProperty(ep, kMIDIPropertyName, &endpointName);
+ MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
+ if (deviceName != NULL) {
+ fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"),
+ deviceName, endpointName);
+ } else {
+ fullName = endpointName;
+ }
+
+ /* copy the string into our buffer */
+ newName = (char*)mem_alloc(CFStringGetLength(fullName) + 1);
+ CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1,
+ defaultEncoding);
+
+ /* clean up */
+ if (endpointName) CFRelease(endpointName);
+ if (deviceName) CFRelease(deviceName);
+ if (fullName) CFRelease(fullName);
+
+ return newName;
+}
+
+static int _macosx_start(struct midi_port *p)
+{
+ struct macosx_midi *m;
+ m = (struct macosx_midi *)p->userdata;
+
+ if (p->io & MIDI_INPUT
+ && MIDIPortConnectSource(portIn, m->ep, (void*)p) != noErr) {
+ return 0;
+ }
+
+ if (p->io & MIDI_OUTPUT) {
+ m->pl = (MIDIPacketList*)m->packet;
+ m->x = NULL;
+ }
+ return 1;
+}
+static int _macosx_stop(struct midi_port *p)
+{
+ struct macosx_midi *m;
+ m = (struct macosx_midi *)p->userdata;
+ if (p->io & MIDI_INPUT
+ && MIDIPortDisconnectSource(portIn, m->ep) != noErr) {
+ return 0;
+ }
+ return 1;
+}
+
+static void _macosx_poll(struct midi_provider *p)
+{
+ struct macosx_midi *data;
+ MIDIEndpointRef ep;
+ int i;
+
+ int num_out, num_in;
+
+ num_out = MIDIGetNumberOfDestinations();
+ num_in = MIDIGetNumberOfSources();
+
+ for (i = max_outputs; i < num_out; i++) {
+ ep = MIDIGetDestination(i);
+ if (!ep) continue;
+ data = mem_alloc(sizeof(struct macosx_midi));
+ memcpy(&data->ep, &ep, sizeof(ep));
+ data->name = get_ep_name(ep);
+ midi_port_register(p, MIDI_OUTPUT, data->name, data, 1);
+ }
+ max_outputs = i;
+
+
+ for (i = max_inputs; i < num_in; i++) {
+ ep = MIDIGetSource(i);
+ if (!ep) continue;
+ data = mem_alloc(sizeof(struct macosx_midi));
+ memcpy(&data->ep, &ep, sizeof(ep));
+ data->name = get_ep_name(ep);
+ midi_port_register(p, MIDI_INPUT, data->name, data, 1);
+ }
+ max_inputs = i;
+
+}
+
+int macosx_midi_setup(void)
+{
+ static struct midi_driver driver;
+
+ memset(&driver,0,sizeof(driver));
+ driver.flags = MIDI_PORT_CAN_SCHEDULE;
+ driver.poll = _macosx_poll;
+ driver.thread = NULL;
+ driver.enable = _macosx_start;
+ driver.disable = _macosx_stop;
+ driver.send = _macosx_send;
+ driver.drain = _macosx_drain;
+
+ if (MIDIClientCreate(CFSTR("Schism Tracker"), NULL, NULL, &client) != noErr) {
+ return 0;
+ }
+ if (MIDIInputPortCreate(client, CFSTR("Input port"), readProc, NULL, &portIn) != noErr) {
+ return 0;
+ }
+ if (MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut) != noErr) {
+ return 0;
+ }
+
+ if (!midi_provider_register("Mac OS X", &driver)) return 0;
+
+ return 1;
+}
+
+#endif
diff --git a/src/sys/macosx/osdefs.c b/src/sys/macosx/osdefs.c
new file mode 100644
index 0000000..57fd832
--- /dev/null
+++ b/src/sys/macosx/osdefs.c
@@ -0,0 +1,54 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+#include "osdefs.h"
+#include "event.h"
+#include "song.h"
+
+const char *osname = "macosx";
+
+
+int macosx_sdlevent(SDL_Event *event)
+{
+ if (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) {
+ if (event->key.keysym.sym == 0) {
+ switch (event->key.keysym.scancode) {
+ case 106: // mac F16 key
+ event->key.keysym.sym = SDLK_PRINT;
+ event->key.keysym.mod = KMOD_CTRL;
+ return 1;
+ case 234: // XXX what key is this?
+ if (event->type == SDL_KEYDOWN)
+ song_set_current_order(song_get_current_order() - 1);
+ return 0;
+ case 233: // XXX what key is this?
+ if (event->type == SDL_KEYUP)
+ song_set_current_order(song_get_current_order() + 1);
+ return 0;
+ };
+ }
+ }
+ return 1;
+}
+
diff --git a/src/sys/macosx/volume-macosx.c b/src/sys/macosx/volume-macosx.c
new file mode 100644
index 0000000..d07d947
--- /dev/null
+++ b/src/sys/macosx/volume-macosx.c
@@ -0,0 +1,119 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+
+#include "util.h"
+
+#ifdef MACOSX
+
+#include <CoreServices/CoreServices.h>
+#include <CoreAudio/AudioHardware.h>
+
+int macosx_volume_get_max(void);
+int macosx_volume_get_max(void)
+{
+ return 65535;
+}
+
+void macosx_volume_read(int *left, int *right);
+void macosx_volume_read(int *left, int *right)
+{
+ UInt32 size;
+ AudioDeviceID od;
+ OSStatus e;
+ UInt32 ch[2];
+ Float32 fl[2];
+ int i;
+
+ if (left) *left = 0;
+ if (right) *right = 0;
+
+ size=sizeof(od);
+ e = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+ &size, &od);
+ if (e != 0) return;
+
+ size=sizeof(ch);
+ e = AudioDeviceGetProperty(od,
+ 0, /* QA1016 says "0" is master channel */
+ false,
+ kAudioDevicePropertyPreferredChannelsForStereo,
+ &size,
+ &ch);
+ if (e != 0) return;
+
+ for (i = 0; i < 2; i++) {
+ size = sizeof(Float32);
+ e = AudioDeviceGetProperty(od, /* device */
+ ch[i], /* preferred stereo channel */
+ false, /* output device */
+ kAudioDevicePropertyVolumeScalar,
+ &size,
+ &fl[i]);
+ if (e != 0) return;
+ }
+ if (left) *left = fl[0] * 65536.0f;
+ if (right) *right = fl[1] * 65536.0f;
+}
+
+void macosx_volume_write(int left, int right);
+void macosx_volume_write(int left, int right)
+{
+ UInt32 size;
+ AudioDeviceID od;
+ OSStatus e;
+ UInt32 ch[2];
+ Float32 fl[2];
+ int i;
+
+ size=sizeof(od);
+ e = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+ &size, &od);
+ if (e != 0) return;
+
+ size=sizeof(ch);
+ e = AudioDeviceGetProperty(od,
+ 0, /* QA1016 says "0" is master channel */
+ false,
+ kAudioDevicePropertyPreferredChannelsForStereo,
+ &size,
+ &ch);
+ if (e != 0) return;
+
+ fl[0] = ((float)left) / 65536.0f;
+ fl[1] = ((float)right) / 65536.0f;
+
+ for (i = 0; i < 2; i++) {
+ e = AudioDeviceSetProperty(od, /* device */
+ NULL, /* no timestamp */
+ ch[i], /* preferred stereo channel */
+ false, /* output device */
+ kAudioDevicePropertyVolumeScalar,
+ sizeof(Float32),
+ &fl[i]);
+ if (e != 0) return;
+ }
+}
+
+#endif
diff --git a/src/sys/oss/midi-oss.c b/src/sys/oss/midi-oss.c
new file mode 100644
index 0000000..8d6f7ba
--- /dev/null
+++ b/src/sys/oss/midi-oss.c
@@ -0,0 +1,177 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "headers.h"
+
+#include "midi.h"
+
+#include "util.h"
+
+#ifdef USE_OSS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/poll.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+/* this is stupid; oss doesn't have a concept of ports... */
+#define MAX_OSS_MIDI 64
+
+#define MAX_MIDI_PORTS MAX_OSS_MIDI+2
+static int opened[MAX_MIDI_PORTS];
+
+
+static void _oss_send(struct midi_port *p, const unsigned char *data, unsigned int len,
+ UNUSED unsigned int delay)
+{
+ int fd, r, n;
+ fd = opened[ n = INT_SHAPED_PTR(p->userdata) ];
+ if (fd < 0) return;
+ while (len > 0) {
+ r = write(fd, data, len);
+ if (r < -1 && errno == EINTR) continue;
+ if (r < 1) {
+ /* err, can't happen? */
+ (void)close(opened[n]);
+ opened[n] = -1;
+ p->userdata = PTR_SHAPED_INT(-1);
+ p->io = 0; /* failure! */
+ return;
+ }
+ data += r;
+ len -= r;
+ }
+}
+
+static int _oss_start(UNUSED struct midi_port *p) { return 1; /* do nothing */ }
+static int _oss_stop(UNUSED struct midi_port *p) { return 1; /* do nothing */ }
+
+
+static int _oss_thread(struct midi_provider *p)
+{
+ struct pollfd pfd[MAX_MIDI_PORTS];
+ struct midi_port *ptr, *src;
+ unsigned char midi_buf[4096];
+ int i, j, r;
+
+ for (;;) {
+ ptr = NULL;
+ j = 0;
+ while (midi_port_foreach(p, &ptr)) {
+ i = INT_SHAPED_PTR(ptr->userdata);
+ if (i == -1) continue; /* err... */
+ if (!(ptr->io & MIDI_INPUT)) continue;
+ pfd[j].fd = i;
+ pfd[j].events = POLLIN;
+ pfd[j].revents = 0; /* RH 5 bug */
+ j++;
+ }
+ if (!j || poll(pfd, j, -1) < 1) {
+ sleep(1);
+ continue;
+ }
+ for (i = 0; i < j; i++) {
+ if (!(pfd[i].revents & POLLIN)) continue;
+ do {
+ r = read(pfd[i].fd, midi_buf, sizeof(midi_buf));
+ } while (r == -1 && errno == EINTR);
+ if (r > 0) {
+ ptr = src = NULL;
+ while (midi_port_foreach(p, &ptr)) {
+ if (INT_SHAPED_PTR(ptr->userdata) == pfd[i].fd) {
+ src = ptr;
+ }
+ }
+ midi_received_cb(src, midi_buf, r);
+ }
+ }
+ }
+ /* stupid gcc */
+ return 0;
+}
+
+
+static void _tryopen(int n, const char *name, struct midi_provider *_oss_provider)
+{
+ int io;
+ char *ptr;
+
+ if (opened[n+1] != -1) return;
+ opened[n+1] = open(name, O_RDWR|O_NOCTTY|O_NONBLOCK);
+ if (opened[n+1] == -1) {
+ opened[n+1] = open(name, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if (opened[n+1] == -1) {
+ opened[n+1] = open(name, O_WRONLY|O_NOCTTY|O_NONBLOCK);
+ if (opened[n+1] == -1) return;
+ io = MIDI_OUTPUT;
+ } else {
+ io = MIDI_INPUT;
+ }
+ } else {
+ io = MIDI_INPUT | MIDI_OUTPUT;
+ }
+
+ ptr = NULL;
+ if (asprintf(&ptr, " %-16s (OSS)", name) == -1) {
+ return;
+ }
+ midi_port_register(_oss_provider, io, ptr, PTR_SHAPED_INT((long)n+1), 0);
+ free(ptr);
+}
+
+static void _oss_poll(struct midi_provider *_oss_provider)
+{
+ char sbuf[64];
+ int i;
+
+ _tryopen(-1, "/dev/midi", _oss_provider);
+ for (i = 0; i < MAX_OSS_MIDI; i++) {
+ sprintf(sbuf, "/dev/midi%d", i);
+ _tryopen(i, sbuf, _oss_provider);
+
+ sprintf(sbuf, "/dev/midi%02d", i);
+ _tryopen(i, sbuf, _oss_provider);
+ }
+}
+int oss_midi_setup(void)
+{
+ static struct midi_driver driver;
+ int i;
+
+ driver.flags = 0;
+ driver.poll = _oss_poll;
+ driver.thread = _oss_thread;
+ driver.enable = _oss_start;
+ driver.disable = _oss_stop;
+ driver.send = _oss_send;
+
+ for (i = 0; i < MAX_MIDI_PORTS; i++) opened[i] = -1;
+ if (!midi_provider_register("OSS", &driver)) return 0;
+ return 1;
+}
+#endif
diff --git a/src/sys/oss/volume-oss.c b/src/sys/oss/volume-oss.c
new file mode 100644
index 0000000..7771307
--- /dev/null
+++ b/src/sys/oss/volume-oss.c
@@ -0,0 +1,126 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+
+#include "util.h"
+#include "log.h"
+
+#ifdef USE_OSS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+/* Hmm. I've found that some systems actually don't support the idea of a
+ * "master" volume, so this became necessary. I suppose PCM is really the
+ * more useful setting to change anyway. */
+#if 0
+# define SCHISM_MIXER_CONTROL SOUND_MIXER_VOLUME
+#else
+# define SCHISM_MIXER_CONTROL SOUND_MIXER_PCM
+#endif
+
+#define VOLUME_MAX 100
+
+/* --------------------------------------------------------------------- */
+
+static const char *device_file = NULL;
+
+/* --------------------------------------------------------------------- */
+
+static int open_mixer_device(void)
+{
+ const char *ptr;
+
+ if (!device_file) {
+ ptr = "/dev/sound/mixer";
+ if (access(ptr, F_OK) < 0) {
+ /* this had better work :) */
+ ptr = "/dev/mixer";
+ }
+ device_file = ptr;
+ }
+
+ return open(device_file, O_RDWR);
+}
+
+/* --------------------------------------------------------------------- */
+
+int oss_volume_get_max(void);
+int oss_volume_get_max(void)
+{
+ return VOLUME_MAX;
+}
+
+void oss_volume_read(int *left, int *right);
+void oss_volume_read(int *left, int *right)
+{
+ int fd;
+ uint8_t volume[4];
+
+ fd = open_mixer_device();
+ if (fd < 0) {
+ log_perror(device_file);
+ *left = *right = 0;
+ return;
+ }
+
+ if (ioctl(fd, MIXER_READ(SCHISM_MIXER_CONTROL), volume) == EOF) {
+ log_perror(device_file);
+ *left = *right = 0;
+ } else {
+ *left = volume[0];
+ *right = volume[1];
+ }
+
+ close(fd);
+}
+
+void oss_volume_write(int left, int right);
+void oss_volume_write(int left, int right)
+{
+ int fd;
+ uint8_t volume[4];
+
+ volume[0] = CLAMP(left, 0, VOLUME_MAX);
+ volume[1] = CLAMP(right, 0, VOLUME_MAX);
+
+ fd = open_mixer_device();
+ if (fd < 0) {
+ log_perror(device_file);
+ return;
+ }
+
+ if (ioctl(fd, MIXER_WRITE(SCHISM_MIXER_CONTROL), volume) == EOF) {
+ log_perror(device_file);
+ }
+
+ close(fd);
+}
+
+#endif
diff --git a/src/sys/posix/slurp-mmap.c b/src/sys/posix/slurp-mmap.c
new file mode 100644
index 0000000..9996486
--- /dev/null
+++ b/src/sys/posix/slurp-mmap.c
@@ -0,0 +1,68 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_MMAP
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "slurp.h"
+
+static void _munmap_slurp(slurp_t *useme)
+{
+ (void)munmap((void*)useme->data, useme->length);
+ (void)close(useme->extra);
+}
+
+int slurp_mmap(slurp_t *useme, const char *filename, size_t st)
+{
+ int fd;
+ void *addr;
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) return 0;
+ addr = mmap(NULL, st, PROT_READ, MAP_SHARED
+#if defined(MAP_POPULATE) && defined(MAP_NONBLOCK)
+ | MAP_POPULATE | MAP_NONBLOCK
+#endif
+#if defined(MAP_NORESERVE)
+ | MAP_NORESERVE
+#endif
+ , fd, 0);
+ if (!addr || addr == ((void*)-1)) {
+ (void)close(fd);
+ return -1;
+ }
+ useme->closure = _munmap_slurp;
+ useme->length = st;
+ useme->data = addr;
+ useme->extra = fd;
+ return 1;
+}
+
+#endif
diff --git a/src/sys/sdl/README b/src/sys/sdl/README
new file mode 100644
index 0000000..6e22655
--- /dev/null
+++ b/src/sys/sdl/README
@@ -0,0 +1,2 @@
+this directory will eventually have SDL-specific stuff in it.
+right now, schism is SDL specific :)
diff --git a/src/sys/stdlib/asprintf.c b/src/sys/stdlib/asprintf.c
new file mode 100644
index 0000000..002e573
--- /dev/null
+++ b/src/sys/stdlib/asprintf.c
@@ -0,0 +1,34 @@
+/* Like sprintf but provides a pointer to malloc'd storage, which must
+ be freed by the caller.
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Contributed by Cygnus Solutions.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <stdarg.h>
+
+int vasprintf(char **result, const char *format, va_list args);
+int asprintf(char **buf, const char *fmt, ...);
+int asprintf(char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+ va_start(ap, fmt);
+ status = vasprintf(buf, fmt, ap);
+ va_end(ap);
+ return status;
+}
diff --git a/src/sys/stdlib/memcmp.c b/src/sys/stdlib/memcmp.c
new file mode 100644
index 0000000..9178250
--- /dev/null
+++ b/src/sys/stdlib/memcmp.c
@@ -0,0 +1,12 @@
+#include <string.h> /* for size_t */
+int memcmp(const void *s1, const void *s2, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ register unsigned char *c1 = (unsigned char *) s1;
+ register unsigned char *c2 = (unsigned char *) s2;
+
+ while (n-- > 0)
+ if (*c1++ != *c2++)
+ return c1[-1] > c2[-1] ? 1 : -1;
+ return 0;
+}
diff --git a/src/sys/stdlib/mkstemp.c b/src/sys/stdlib/mkstemp.c
new file mode 100644
index 0000000..395df37
--- /dev/null
+++ b/src/sys/stdlib/mkstemp.c
@@ -0,0 +1,104 @@
+/* Copyright (C) 1991, 1992, 1996, 1998 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <stdint.h>
+typedef uint64_t big_type;
+
+/* this ifdef crap lifted from slurp
+This is a hack!! We need binary mode files, but the 'b' flag to fdopen is
+evidently useless, so I'm doing it at this level instead. Dumb. */
+#ifndef O_BINARY
+# ifdef O_RAW
+# define O_BINARY O_RAW
+# else
+# define O_BINARY 0
+# endif
+#endif
+
+int mkstemp(char *template);
+
+/* Generate a unique temporary file name from TEMPLATE.
+ The last six characters of TEMPLATE must be "XXXXXX";
+ they are replaced with a string that makes the filename unique.
+ Returns a file descriptor open on the file for reading and writing. */
+int mkstemp(char *template)
+{
+ static const char letters[]
+ = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ static big_type value;
+ struct timeval tv;
+ char *XXXXXX;
+ size_t len;
+ int count;
+
+ len = strlen (template);
+ if (len < 6 || strcmp (&template[len - 6], "XXXXXX"))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* This is where the Xs start. */
+ XXXXXX = &template[len - 6];
+
+ /* Get some more or less random data. */
+ gettimeofday (&tv, NULL);
+ value += ((big_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
+
+ for (count = 0; count < TMP_MAX; ++count)
+ {
+ big_type v = value;
+ int fd;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+ v /= 62;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+ fd = open (template, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0600);
+ if (fd >= 0)
+ /* The file does not exist. */
+ return fd;
+
+ /* This is a random value. It is only necessary that the next
+ TMP_MAX values generated by adding 7777 to VALUE are different
+ with (module 2^32). */
+ value += 7777;
+ }
+
+ /* We return the null string if we can't find a unique file name. */
+ template[0] = '\0';
+ return -1;
+}
+
diff --git a/src/sys/stdlib/strptime.c b/src/sys/stdlib/strptime.c
new file mode 100644
index 0000000..50d9dfc
--- /dev/null
+++ b/src/sys/stdlib/strptime.c
@@ -0,0 +1,396 @@
+/* $Id$ */
+/* $NetBSD: strptime.c,v 1.18 1999/04/29 02:58:30 tv Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Klaus Klein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define NEED_TIME
+#include "headers.h"
+#include <ctype.h>
+
+#define TM_YEAR_BASE 1900
+
+/*
+ * We do not implement alternate representations. However, we always
+ * check whether a given modifier is allowed for a certain conversion.
+ */
+#define ALT_E 0x01
+#define ALT_O 0x02
+#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
+
+
+static int conv_num(const char **, int *, int, int);
+
+static const char *day[7] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday"
+};
+static const char *abday[7] = {
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+};
+static const char *mon[12] = {
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December"
+};
+static const char *abmon[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+static const char *am_pm[2] = {
+ "AM", "PM"
+};
+
+
+char *
+strptime(const char *buf, const char *fmt, struct tm *tm)
+{
+ char c;
+ const char *bp;
+ size_t len = 0;
+ int alt_format, i, split_year = 0;
+
+ bp = buf;
+
+ while ((c = *fmt) != '\0') {
+ /* Clear `alternate' modifier prior to new conversion. */
+ alt_format = 0;
+
+ /* Eat up white-space. */
+ if (isspace(c)) {
+ while (isspace(*bp))
+ bp++;
+
+ fmt++;
+ continue;
+ }
+
+ if ((c = *fmt++) != '%')
+ goto literal;
+
+
+again: switch (c = *fmt++) {
+ case '%': /* "%%" is converted to "%". */
+literal:
+ if (c != *bp++)
+ return (0);
+ break;
+
+ /*
+ * "Alternative" modifiers. Just set the appropriate flag
+ * and start over again.
+ */
+ case 'E': /* "%E?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_E;
+ goto again;
+
+ case 'O': /* "%O?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_O;
+ goto again;
+
+ /*
+ * "Complex" conversion rules, implemented through recursion.
+ */
+ case 'c': /* Date and time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ if (!(bp = strptime(bp, "%x %X", tm)))
+ return (0);
+ break;
+
+ case 'D': /* The date as "%m/%d/%y". */
+ LEGAL_ALT(0);
+ if (!(bp = strptime(bp, "%m/%d/%y", tm)))
+ return (0);
+ break;
+
+ case 'R': /* The time as "%H:%M". */
+ LEGAL_ALT(0);
+ if (!(bp = strptime(bp, "%H:%M", tm)))
+ return (0);
+ break;
+
+ case 'r': /* The time in 12-hour clock representation. */
+ LEGAL_ALT(0);
+ if (!(bp = strptime(bp, "%I:%M:%S %p", tm)))
+ return (0);
+ break;
+
+ case 'T': /* The time as "%H:%M:%S". */
+ LEGAL_ALT(0);
+ if (!(bp = strptime(bp, "%H:%M:%S", tm)))
+ return (0);
+ break;
+
+ case 'X': /* The time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ if (!(bp = strptime(bp, "%H:%M:%S", tm)))
+ return (0);
+ break;
+
+ case 'x': /* The date, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ if (!(bp = strptime(bp, "%m/%d/%y", tm)))
+ return (0);
+ break;
+
+ /*
+ * "Elementary" conversion rules.
+ */
+ case 'A': /* The day of week, using the locale's form. */
+ case 'a':
+ LEGAL_ALT(0);
+ for (i = 0; i < 7; i++) {
+ /* Full name. */
+ len = strlen(day[i]);
+ if (strncasecmp(day[i], bp, len) == 0)
+ break;
+
+ /* Abbreviated name. */
+ len = strlen(abday[i]);
+ if (strncasecmp(abday[i], bp, len) == 0)
+ break;
+ }
+
+ /* Nothing matched. */
+ if (i == 7)
+ return (0);
+
+ tm->tm_wday = i;
+ bp += len;
+ break;
+
+ case 'B': /* The month, using the locale's form. */
+ case 'b':
+ case 'h':
+ LEGAL_ALT(0);
+ for (i = 0; i < 12; i++) {
+ /* Full name. */
+ len = strlen(mon[i]);
+ if (strncasecmp(mon[i], bp, len) == 0)
+ break;
+
+ /* Abbreviated name. */
+ len = strlen(abmon[i]);
+ if (strncasecmp(abmon[i], bp, len) == 0)
+ break;
+ }
+
+ /* Nothing matched. */
+ if (i == 12)
+ return (0);
+
+ tm->tm_mon = i;
+ bp += len;
+ break;
+
+ case 'C': /* The century number. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 99)))
+ return (0);
+
+ if (split_year) {
+ tm->tm_year = (tm->tm_year % 100) + (i * 100);
+ } else {
+ tm->tm_year = i * 100;
+ split_year = 1;
+ }
+ break;
+
+ case 'd': /* The day of month. */
+ case 'e':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
+ return (0);
+ break;
+
+ case 'k': /* The hour (24-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'H':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
+ return (0);
+ break;
+
+ case 'l': /* The hour (12-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'I':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
+ return (0);
+ if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ break;
+
+ case 'j': /* The day of year. */
+ LEGAL_ALT(0);
+ if (!(conv_num(&bp, &i, 1, 366)))
+ return (0);
+ tm->tm_yday = i - 1;
+ break;
+
+ case 'M': /* The minute. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
+ return (0);
+ break;
+
+ case 'm': /* The month. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &i, 1, 12)))
+ return (0);
+ tm->tm_mon = i - 1;
+ break;
+
+ case 'p': /* The locale's equivalent of AM/PM. */
+ LEGAL_ALT(0);
+ /* AM? */
+ if (strcasecmp(am_pm[0], bp) == 0) {
+ if (tm->tm_hour > 11)
+ return (0);
+
+ bp += strlen(am_pm[0]);
+ break;
+ }
+ /* PM? */
+ else if (strcasecmp(am_pm[1], bp) == 0) {
+ if (tm->tm_hour > 11)
+ return (0);
+
+ tm->tm_hour += 12;
+ bp += strlen(am_pm[1]);
+ break;
+ }
+
+ /* Nothing matched. */
+ return (0);
+
+ case 'S': /* The seconds. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
+ return (0);
+ break;
+
+ case 'U': /* The week of year, beginning on sunday. */
+ case 'W': /* The week of year, beginning on monday. */
+ LEGAL_ALT(ALT_O);
+ /*
+ * XXX This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so just check the
+ * range for now.
+ */
+ if (!(conv_num(&bp, &i, 0, 53)))
+ return (0);
+ break;
+
+ case 'w': /* The day of week, beginning on sunday. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
+ return (0);
+ break;
+
+ case 'Y': /* The year. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 9999)))
+ return (0);
+
+ tm->tm_year = i - TM_YEAR_BASE;
+ break;
+
+ case 'y': /* The year within 100 years of the epoch. */
+ LEGAL_ALT(ALT_E | ALT_O);
+ if (!(conv_num(&bp, &i, 0, 99)))
+ return (0);
+
+ if (split_year) {
+ tm->tm_year = ((tm->tm_year / 100) * 100) + i;
+ break;
+ }
+ split_year = 1;
+ if (i <= 68)
+ tm->tm_year = i + 2000 - TM_YEAR_BASE;
+ else
+ tm->tm_year = i + 1900 - TM_YEAR_BASE;
+ break;
+
+ /*
+ * Miscellaneous conversions.
+ */
+ case 'n': /* Any kind of white-space. */
+ case 't':
+ LEGAL_ALT(0);
+ while (isspace(*bp))
+ bp++;
+ break;
+
+
+ default: /* Unknown/unsupported conversion. */
+ return (0);
+ }
+
+
+ }
+
+ /* LINTED functional specification */
+ return ((char *)bp);
+}
+
+
+static int
+conv_num(const char **buf, int *dest, int llim, int ulim)
+{
+ int result = 0;
+
+ /* The limit also determines the number of valid digits. */
+ int rulim = ulim;
+
+ if (**buf < '0' || **buf > '9')
+ return (0);
+
+ do {
+ result *= 10;
+ result += *(*buf)++ - '0';
+ rulim /= 10;
+ } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
+
+ if (result < llim || result > ulim)
+ return (0);
+
+ *dest = result;
+ return (1);
+}
diff --git a/src/sys/stdlib/vasprintf.c b/src/sys/stdlib/vasprintf.c
new file mode 100644
index 0000000..f767645
--- /dev/null
+++ b/src/sys/stdlib/vasprintf.c
@@ -0,0 +1,97 @@
+/* Like vsprintf but provides a pointer to malloc'd storage, which must
+ be freed by the caller.
+ Copyright (C) 1994 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+static int int_vasprintf(char **result, const char *format, va_list *args)
+{
+ const char *p = format;
+ /* Add one to make sure that it is never zero, which might cause malloc
+ to return NULL. */
+ int total_width = strlen (format) + 1;
+ va_list ap;
+
+ memcpy(&ap, args, sizeof(va_list));
+
+ while (*p != '\0') {
+ if (*p++ == '%') {
+ while (strchr ("-+ #0", *p))
+ ++p;
+ if (*p == '*') {
+ ++p;
+ total_width += abs(va_arg (ap, int));
+ } else
+ total_width += strtoul(p, (char **) &p, 10);
+ if (*p == '.') {
+ ++p;
+ if (*p == '*') {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ } else
+ total_width += strtoul(p, (char **) &p, 10);
+ }
+ while (strchr ("hlL", *p))
+ ++p;
+ /* Should be big enough for any format specifier except %s and floats. */
+ total_width += 30;
+ switch (*p) {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'c':
+ (void) va_arg (ap, int);
+ break;
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ (void) va_arg (ap, double);
+ /* Since an ieee double can have an exponent of 307, we'll
+ make the buffer wide enough to cover the gross case. */
+ total_width += 307;
+ break;
+ case 's':
+ total_width += strlen (va_arg (ap, char *));
+ break;
+ case 'p':
+ case 'n':
+ (void) va_arg (ap, char *);
+ break;
+ }
+ }
+ }
+ *result = (char*)malloc (total_width);
+ return vsprintf (*result, format, *args);
+}
+
+int vasprintf(char **result, const char *format, va_list args);
+int vasprintf(char **result, const char *format, va_list args)
+{
+ return int_vasprintf (result, format, &args);
+}
diff --git a/src/sys/wii/certs_bin.h b/src/sys/wii/certs_bin.h
new file mode 100644
index 0000000..95091c3
--- /dev/null
+++ b/src/sys/wii/certs_bin.h
@@ -0,0 +1,163 @@
+static unsigned const char certs_bin[] = {
+'\000', '\001', '\000', '\000', '\263', '\255', '\263', '\042', '\153', '\074', '\075', '\377', '\033', '\113', '\100', '\167',
+'\026', '\377', '\117', '\172', '\327', '\144', '\206', '\310', '\225', '\254', '\126', '\055', '\041', '\361', '\006', '\001',
+'\324', '\366', '\144', '\050', '\031', '\034', '\007', '\166', '\217', '\337', '\032', '\342', '\316', '\173', '\047', '\311',
+'\017', '\274', '\012', '\320', '\061', '\045', '\170', '\354', '\007', '\171', '\266', '\127', '\324', '\067', '\044', '\023',
+'\247', '\370', '\157', '\014', '\024', '\300', '\357', '\156', '\011', '\101', '\355', '\053', '\005', '\354', '\071', '\127',
+'\066', '\007', '\211', '\000', '\112', '\207', '\215', '\056', '\235', '\370', '\307', '\245', '\251', '\370', '\312', '\263',
+'\021', '\261', '\030', '\171', '\127', '\273', '\370', '\230', '\342', '\242', '\124', '\002', '\317', '\124', '\071', '\317',
+'\053', '\277', '\240', '\341', '\370', '\134', '\006', '\156', '\203', '\232', '\340', '\224', '\312', '\107', '\340', '\025',
+'\130', '\365', '\156', '\157', '\064', '\351', '\052', '\242', '\334', '\070', '\223', '\176', '\067', '\315', '\214', '\134',
+'\115', '\375', '\057', '\021', '\117', '\350', '\150', '\311', '\250', '\331', '\376', '\330', '\156', '\014', '\041', '\165',
+'\242', '\275', '\176', '\211', '\271', '\307', '\265', '\023', '\364', '\032', '\171', '\141', '\104', '\071', '\020', '\357',
+'\371', '\327', '\376', '\127', '\042', '\030', '\325', '\155', '\373', '\177', '\111', '\172', '\244', '\313', '\220', '\324',
+'\361', '\256', '\261', '\166', '\344', '\150', '\135', '\247', '\224', '\100', '\140', '\230', '\057', '\004', '\110', '\100',
+'\037', '\317', '\306', '\272', '\353', '\332', '\026', '\060', '\264', '\163', '\264', '\025', '\043', '\065', '\010', '\007',
+'\012', '\237', '\117', '\211', '\170', '\346', '\054', '\354', '\136', '\222', '\106', '\245', '\250', '\275', '\240', '\205',
+'\170', '\150', '\165', '\014', '\072', '\021', '\057', '\257', '\225', '\350', '\070', '\310', '\231', '\016', '\207', '\261',
+'\142', '\315', '\020', '\332', '\263', '\061', '\226', '\145', '\357', '\210', '\233', '\124', '\033', '\263', '\066', '\273',
+'\147', '\123', '\237', '\257', '\302', '\256', '\055', '\012', '\056', '\165', '\300', '\043', '\164', '\352', '\116', '\254',
+'\215', '\231', '\120', '\177', '\131', '\271', '\123', '\167', '\060', '\137', '\046', '\065', '\306', '\010', '\251', '\220',
+'\223', '\254', '\217', '\306', '\336', '\043', '\271', '\172', '\352', '\160', '\264', '\304', '\317', '\146', '\263', '\016',
+'\130', '\062', '\016', '\305', '\266', '\162', '\004', '\110', '\316', '\073', '\261', '\034', '\123', '\037', '\313', '\160',
+'\050', '\174', '\265', '\302', '\174', '\147', '\117', '\273', '\375', '\214', '\177', '\311', '\102', '\040', '\244', '\163',
+'\043', '\035', '\130', '\176', '\132', '\032', '\032', '\202', '\343', '\165', '\171', '\241', '\273', '\202', '\156', '\316',
+'\001', '\161', '\311', '\165', '\143', '\107', '\113', '\035', '\106', '\346', '\171', '\262', '\202', '\067', '\142', '\021',
+'\315', '\307', '\000', '\057', '\106', '\207', '\302', '\074', '\155', '\300', '\325', '\265', '\170', '\156', '\341', '\362',
+'\163', '\377', '\001', '\222', '\120', '\017', '\364', '\307', '\120', '\152', '\356', '\162', '\266', '\364', '\075', '\366',
+'\010', '\376', '\245', '\203', '\241', '\371', '\206', '\017', '\207', '\257', '\122', '\104', '\124', '\273', '\107', '\303',
+'\006', '\014', '\224', '\351', '\233', '\367', '\326', '\062', '\247', '\310', '\253', '\113', '\117', '\365', '\065', '\041',
+'\037', '\301', '\200', '\107', '\273', '\172', '\372', '\132', '\053', '\327', '\270', '\204', '\255', '\216', '\126', '\117',
+'\133', '\211', '\377', '\067', '\227', '\067', '\361', '\365', '\001', '\073', '\037', '\236', '\304', '\030', '\157', '\222',
+'\052', '\325', '\304', '\263', '\300', '\325', '\207', '\013', '\234', '\004', '\257', '\032', '\265', '\363', '\274', '\155',
+'\012', '\361', '\175', '\107', '\010', '\344', '\103', '\351', '\163', '\367', '\267', '\160', '\167', '\124', '\272', '\363',
+'\354', '\322', '\254', '\111', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\122', '\157', '\157', '\164', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\001', '\103', '\101', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\061', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\133', '\372', '\175', '\134', '\262', '\171', '\311', '\342', '\356', '\341', '\041', '\306',
+'\352', '\364', '\117', '\366', '\071', '\370', '\217', '\007', '\213', '\113', '\167', '\355', '\237', '\225', '\140', '\260',
+'\065', '\202', '\201', '\265', '\016', '\125', '\253', '\162', '\021', '\025', '\241', '\167', '\160', '\074', '\172', '\060',
+'\376', '\072', '\351', '\357', '\034', '\140', '\274', '\035', '\227', '\106', '\166', '\262', '\072', '\150', '\314', '\004',
+'\261', '\230', '\122', '\133', '\311', '\150', '\361', '\035', '\342', '\333', '\120', '\344', '\331', '\347', '\360', '\161',
+'\345', '\142', '\332', '\342', '\011', '\042', '\063', '\351', '\323', '\143', '\366', '\035', '\327', '\301', '\237', '\363',
+'\244', '\251', '\036', '\217', '\145', '\123', '\324', '\161', '\335', '\173', '\204', '\271', '\361', '\270', '\316', '\163',
+'\065', '\360', '\365', '\124', '\005', '\143', '\241', '\352', '\270', '\071', '\143', '\340', '\233', '\351', '\001', '\001',
+'\037', '\231', '\124', '\143', '\141', '\050', '\160', '\040', '\351', '\314', '\015', '\253', '\110', '\177', '\024', '\015',
+'\146', '\046', '\241', '\203', '\155', '\047', '\021', '\037', '\040', '\150', '\336', '\107', '\162', '\024', '\221', '\121',
+'\317', '\151', '\306', '\033', '\246', '\016', '\371', '\331', '\111', '\240', '\367', '\037', '\124', '\231', '\362', '\323',
+'\232', '\322', '\214', '\160', '\005', '\064', '\202', '\223', '\304', '\061', '\377', '\275', '\063', '\366', '\274', '\246',
+'\015', '\307', '\031', '\136', '\242', '\274', '\305', '\155', '\040', '\013', '\257', '\155', '\006', '\320', '\234', '\101',
+'\333', '\215', '\351', '\307', '\040', '\025', '\114', '\244', '\203', '\053', '\151', '\300', '\214', '\151', '\315', '\073',
+'\007', '\072', '\000', '\143', '\140', '\057', '\106', '\055', '\063', '\200', '\141', '\245', '\352', '\154', '\221', '\134',
+'\325', '\142', '\065', '\171', '\303', '\353', '\144', '\316', '\104', '\357', '\130', '\155', '\024', '\272', '\252', '\210',
+'\064', '\001', '\233', '\076', '\353', '\356', '\323', '\171', '\000', '\001', '\000', '\001', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\001', '\000', '\001', '\116', '\000', '\137', '\361', '\077', '\206', '\165', '\215', '\266', '\234', '\105', '\143',
+'\017', '\324', '\233', '\364', '\314', '\135', '\124', '\317', '\314', '\042', '\064', '\162', '\127', '\253', '\244', '\272',
+'\123', '\322', '\263', '\075', '\346', '\354', '\236', '\241', '\127', '\124', '\123', '\256', '\137', '\223', '\075', '\226',
+'\277', '\367', '\314', '\172', '\171', '\126', '\156', '\204', '\173', '\033', '\140', '\167', '\302', '\251', '\070', '\161',
+'\060', '\032', '\214', '\323', '\311', '\075', '\115', '\263', '\046', '\351', '\207', '\222', '\146', '\351', '\323', '\272',
+'\237', '\171', '\274', '\106', '\070', '\372', '\055', '\040', '\240', '\072', '\160', '\147', '\244', '\021', '\247', '\240',
+'\267', '\331', '\022', '\255', '\021', '\152', '\072', '\304', '\156', '\062', '\102', '\107', '\302', '\010', '\272', '\264',
+'\224', '\234', '\305', '\056', '\320', '\057', '\031', '\366', '\121', '\340', '\337', '\056', '\066', '\123', '\252', '\257',
+'\227', '\246', '\222', '\273', '\251', '\035', '\330', '\156', '\044', '\056', '\263', '\010', '\167', '\125', '\021', '\316',
+'\230', '\366', '\242', '\364', '\046', '\311', '\047', '\004', '\320', '\374', '\215', '\324', '\200', '\236', '\327', '\141',
+'\275', '\021', '\267', '\205', '\224', '\214', '\326', '\320', '\172', '\333', '\244', '\010', '\320', '\360', '\206', '\366',
+'\132', '\256', '\031', '\024', '\262', '\210', '\232', '\250', '\256', '\112', '\242', '\252', '\307', '\141', '\251', '\015',
+'\101', '\054', '\261', '\120', '\011', '\253', '\076', '\223', '\374', '\251', '\044', '\336', '\316', '\117', '\174', '\006',
+'\253', '\334', '\056', '\140', '\235', '\150', '\276', '\000', '\163', '\372', '\200', '\127', '\152', '\024', '\136', '\355',
+'\304', '\213', '\164', '\062', '\207', '\007', '\223', '\310', '\374', '\246', '\330', '\076', '\011', '\156', '\305', '\362',
+'\251', '\304', '\041', '\347', '\110', '\263', '\163', '\100', '\133', '\342', '\372', '\212', '\341', '\130', '\170', '\351',
+'\325', '\043', '\210', '\165', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\122', '\157', '\157', '\164', '\055', '\103', '\101', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\061', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\001', '\103', '\120', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\064', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\361', '\270', '\240', '\144', '\301', '\155', '\363', '\203', '\051', '\125', '\303', '\051',
+'\133', '\162', '\360', '\063', '\056', '\227', '\357', '\024', '\204', '\212', '\150', '\004', '\234', '\246', '\216', '\254',
+'\336', '\024', '\120', '\063', '\270', '\154', '\020', '\215', '\110', '\063', '\134', '\135', '\014', '\253', '\167', '\004',
+'\142', '\124', '\107', '\125', '\105', '\052', '\220', '\000', '\160', '\261', '\126', '\222', '\134', '\027', '\206', '\342',
+'\315', '\040', '\155', '\314', '\334', '\054', '\056', '\067', '\156', '\047', '\374', '\264', '\040', '\146', '\314', '\012',
+'\214', '\351', '\376', '\350', '\127', '\004', '\346', '\312', '\143', '\032', '\056', '\176', '\221', '\176', '\224', '\174',
+'\071', '\221', '\167', '\066', '\051', '\321', '\125', '\141', '\205', '\273', '\327', '\267', '\163', '\312', '\067', '\107',
+'\236', '\137', '\252', '\243', '\266', '\005', '\340', '\001', '\341', '\254', '\345', '\215', '\330', '\370', '\107', '\202',
+'\326', '\105', '\374', '\343', '\241', '\315', '\003', '\253', '\066', '\360', '\363', '\206', '\261', '\242', '\321', '\067',
+'\100', '\241', '\224', '\212', '\123', '\272', '\033', '\015', '\214', '\110', '\143', '\315', '\153', '\054', '\056', '\040',
+'\144', '\224', '\200', '\114', '\142', '\372', '\251', '\072', '\176', '\063', '\251', '\352', '\170', '\153', '\131', '\312',
+'\343', '\253', '\066', '\105', '\364', '\313', '\217', '\327', '\220', '\153', '\202', '\150', '\315', '\254', '\361', '\173',
+'\072', '\354', '\106', '\203', '\033', '\221', '\366', '\336', '\030', '\141', '\203', '\274', '\113', '\062', '\147', '\223',
+'\307', '\056', '\120', '\331', '\036', '\066', '\240', '\334', '\342', '\271', '\175', '\240', '\041', '\076', '\106', '\226',
+'\002', '\037', '\063', '\034', '\276', '\256', '\215', '\374', '\222', '\207', '\062', '\252', '\104', '\334', '\170', '\347',
+'\031', '\232', '\075', '\335', '\127', '\042', '\176', '\236', '\167', '\336', '\062', '\143', '\206', '\223', '\154', '\021',
+'\254', '\247', '\017', '\201', '\031', '\323', '\072', '\231', '\000', '\001', '\000', '\001', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\001', '\000', '\001', '\175', '\235', '\136', '\272', '\122', '\201', '\334', '\247', '\006', '\135', '\057', '\010',
+'\150', '\333', '\212', '\307', '\072', '\316', '\176', '\251', '\221', '\361', '\226', '\237', '\341', '\320', '\362', '\301',
+'\037', '\256', '\300', '\303', '\360', '\032', '\334', '\264', '\106', '\255', '\345', '\312', '\003', '\266', '\045', '\041',
+'\224', '\142', '\306', '\341', '\101', '\015', '\271', '\346', '\077', '\336', '\230', '\321', '\257', '\046', '\073', '\114',
+'\262', '\207', '\204', '\047', '\202', '\162', '\357', '\047', '\023', '\113', '\207', '\302', '\130', '\326', '\173', '\142',
+'\362', '\265', '\277', '\234', '\266', '\272', '\214', '\211', '\031', '\056', '\305', '\006', '\211', '\254', '\164', '\044',
+'\240', '\042', '\011', '\100', '\003', '\356', '\230', '\244', '\275', '\057', '\001', '\073', '\131', '\077', '\345', '\146',
+'\154', '\325', '\353', '\132', '\327', '\244', '\223', '\020', '\363', '\116', '\373', '\264', '\075', '\106', '\313', '\361',
+'\265', '\043', '\317', '\202', '\366', '\216', '\265', '\155', '\271', '\004', '\247', '\302', '\250', '\053', '\341', '\035',
+'\170', '\323', '\233', '\242', '\015', '\220', '\323', '\007', '\102', '\333', '\136', '\172', '\301', '\357', '\362', '\041',
+'\121', '\011', '\142', '\317', '\251', '\024', '\250', '\200', '\334', '\364', '\027', '\272', '\231', '\223', '\012', '\356',
+'\010', '\260', '\260', '\345', '\032', '\076', '\237', '\257', '\315', '\302', '\327', '\343', '\313', '\241', '\057', '\072',
+'\300', '\007', '\220', '\336', '\104', '\172', '\303', '\305', '\070', '\250', '\147', '\222', '\070', '\007', '\213', '\324',
+'\304', '\262', '\105', '\254', '\051', '\026', '\210', '\155', '\052', '\016', '\131', '\116', '\355', '\134', '\310', '\065',
+'\151', '\213', '\115', '\142', '\070', '\337', '\005', '\162', '\115', '\314', '\366', '\201', '\200', '\212', '\160', '\164',
+'\006', '\131', '\060', '\277', '\370', '\121', '\101', '\067', '\350', '\025', '\372', '\272', '\241', '\162', '\270', '\340',
+'\151', '\154', '\141', '\344', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\122', '\157', '\157', '\164', '\055', '\103', '\101', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\061', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\001', '\130', '\123', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\063', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\361', '\270', '\237', '\321', '\255', '\007', '\251', '\067', '\212', '\173', '\020', '\014',
+'\175', '\307', '\071', '\276', '\236', '\335', '\267', '\062', '\000', '\211', '\253', '\045', '\261', '\370', '\161', '\257',
+'\132', '\251', '\364', '\130', '\236', '\321', '\203', '\002', '\062', '\216', '\201', '\032', '\037', '\357', '\320', '\011',
+'\310', '\006', '\066', '\103', '\370', '\124', '\271', '\341', '\073', '\273', '\141', '\072', '\172', '\317', '\207', '\024',
+'\205', '\153', '\244', '\133', '\252', '\347', '\273', '\306', '\116', '\262', '\367', '\135', '\207', '\353', '\362', '\147',
+'\355', '\017', '\244', '\101', '\251', '\063', '\146', '\136', '\127', '\175', '\132', '\336', '\253', '\373', '\106', '\056',
+'\166', '\000', '\312', '\234', '\351', '\115', '\304', '\313', '\230', '\071', '\222', '\253', '\172', '\057', '\263', '\243',
+'\236', '\242', '\277', '\234', '\123', '\354', '\320', '\334', '\372', '\153', '\213', '\136', '\262', '\313', '\244', '\017',
+'\372', '\100', '\165', '\370', '\362', '\262', '\336', '\227', '\070', '\021', '\207', '\055', '\365', '\342', '\246', '\303',
+'\213', '\057', '\334', '\216', '\127', '\335', '\275', '\137', '\106', '\353', '\047', '\326', '\031', '\122', '\366', '\256',
+'\370', '\142', '\267', '\356', '\232', '\306', '\202', '\242', '\261', '\232', '\251', '\265', '\130', '\373', '\353', '\263',
+'\211', '\057', '\275', '\120', '\311', '\365', '\334', '\112', '\156', '\234', '\233', '\376', '\105', '\200', '\064', '\251',
+'\102', '\030', '\055', '\336', '\267', '\137', '\340', '\321', '\263', '\337', '\016', '\227', '\343', '\231', '\200', '\207',
+'\160', '\030', '\302', '\262', '\203', '\361', '\065', '\165', '\174', '\132', '\060', '\374', '\077', '\060', '\204', '\244',
+'\232', '\252', '\300', '\036', '\347', '\006', '\151', '\117', '\216', '\024', '\110', '\332', '\022', '\072', '\314', '\117',
+'\372', '\046', '\252', '\070', '\367', '\357', '\277', '\047', '\217', '\066', '\227', '\171', '\167', '\135', '\267', '\305',
+'\255', '\307', '\211', '\221', '\334', '\370', '\103', '\215', '\000', '\001', '\000', '\001', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+
+};
diff --git a/src/sys/wii/data/certs.bin b/src/sys/wii/data/certs.bin
new file mode 100644
index 0000000..2184107
--- /dev/null
+++ b/src/sys/wii/data/certs.bin
Binary files differ
diff --git a/src/sys/wii/data/su_tik.bin b/src/sys/wii/data/su_tik.bin
new file mode 100644
index 0000000..eb2bd12
--- /dev/null
+++ b/src/sys/wii/data/su_tik.bin
Binary files differ
diff --git a/src/sys/wii/data/su_tmd.bin b/src/sys/wii/data/su_tmd.bin
new file mode 100644
index 0000000..7f68618
--- /dev/null
+++ b/src/sys/wii/data/su_tmd.bin
Binary files differ
diff --git a/src/sys/wii/isfs.c b/src/sys/wii/isfs.c
new file mode 100644
index 0000000..6168864
--- /dev/null
+++ b/src/sys/wii/isfs.c
@@ -0,0 +1,445 @@
+/*
+
+libisfs -- a NAND filesystem devoptab library for the Wii
+
+Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from
+the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1.The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software in a
+product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+2.Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+3.This notice may not be removed or altered from any source distribution.
+
+
+[In compliance with the above: I patched this code up somewhat so that it
+builds with all warnings. -- Storlek]
+*/
+#include <errno.h>
+#include <ogc/isfs.h>
+#include <ogc/lwp_watchdog.h>
+#include <ogcsys.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/dir.h>
+#include <sys/iosupport.h>
+
+#include "isfs.h"
+
+#define DEVICE_NAME "isfs"
+
+#define FLAG_DIR 1
+#define DIR_SEPARATOR '/'
+#define SECTOR_SIZE 0x800
+#define BUFFER_SIZE 0x8000
+
+#define UNUSED __attribute__((unused))
+
+typedef struct DIR_ENTRY_STRUCT {
+ char *name;
+ char *abspath;
+ u32 size;
+ u8 flags;
+ u32 fileCount;
+ struct DIR_ENTRY_STRUCT *children;
+} DIR_ENTRY;
+
+typedef struct {
+ DIR_ENTRY *entry;
+ s32 isfs_fd;
+ bool inUse;
+} FILE_STRUCT;
+
+typedef struct {
+ DIR_ENTRY *entry;
+ u32 index;
+ bool inUse;
+} DIR_STATE_STRUCT;
+
+static char read_buffer[BUFFER_SIZE] __attribute__((aligned(32)));
+
+static DIR_ENTRY *root = NULL;
+static DIR_ENTRY *current = NULL;
+static s32 dotab_device = -1;
+
+static bool is_dir(DIR_ENTRY *entry) {
+ return entry->flags & FLAG_DIR;
+}
+
+static bool invalid_drive_specifier(const char *path) {
+ if (strchr(path, ':') == NULL) return false;
+ int namelen = strlen(DEVICE_NAME);
+ if (!strncmp(DEVICE_NAME, path, namelen) && path[namelen] == ':') return false;
+ return true;
+}
+
+static DIR_ENTRY *entry_from_path(const char *path) {
+ if (invalid_drive_specifier(path)) return NULL;
+ if (strchr(path, ':') != NULL) path = strchr(path, ':') + 1;
+ DIR_ENTRY *entry;
+ bool found = false;
+ bool notFound = false;
+ const char *pathPosition = path;
+ const char *pathEnd = strchr(path, '\0');
+ if (pathPosition[0] == DIR_SEPARATOR) {
+ entry = root;
+ while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
+ if (pathPosition >= pathEnd) found = true;
+ } else {
+ entry = current;
+ }
+ if (entry == root && !strcmp(".", pathPosition)) found = true;
+ DIR_ENTRY *dir = entry;
+ while (!found && !notFound) {
+ const char *nextPathPosition = strchr(pathPosition, DIR_SEPARATOR);
+ size_t dirnameLength;
+ if (nextPathPosition != NULL) dirnameLength = nextPathPosition - pathPosition;
+ else dirnameLength = strlen(pathPosition);
+ if (dirnameLength >= ISFS_MAXPATHLEN) return NULL;
+
+ u32 fileIndex = 0;
+ while (fileIndex < dir->fileCount && !found && !notFound) {
+ entry = &dir->children[fileIndex];
+ if (dirnameLength == strnlen(entry->name, ISFS_MAXPATHLEN - 1)
+ && !strncasecmp(pathPosition, entry->name, dirnameLength)) found = true;
+ if (found && !is_dir(entry) && nextPathPosition) found = false;
+ if (!found) fileIndex++;
+ }
+
+ if (fileIndex >= dir->fileCount) {
+ notFound = true;
+ found = false;
+ } else if (!nextPathPosition || nextPathPosition >= pathEnd) {
+ found = true;
+ } else if (is_dir(entry)) {
+ dir = entry;
+ pathPosition = nextPathPosition;
+ while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
+ if (pathPosition >= pathEnd) found = true;
+ else found = false;
+ }
+ }
+
+ if (found && !notFound) return entry;
+ return NULL;
+}
+
+static int _ISFS_open_r(struct _reent *r, void *fileStruct, const char *path,
+ UNUSED int flags, UNUSED int mode) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fileStruct;
+ DIR_ENTRY *entry = entry_from_path(path);
+ if (!entry) {
+ r->_errno = ENOENT;
+ return -1;
+ } else if (is_dir(entry)) {
+ r->_errno = EISDIR;
+ return -1;
+ }
+
+ file->entry = entry;
+ file->inUse = true;
+ file->isfs_fd = ISFS_Open(entry->abspath, ISFS_OPEN_READ);
+ if (file->isfs_fd < 0) {
+ if (file->isfs_fd == ISFS_EINVAL) r->_errno = EACCES;
+ else r->_errno = -file->isfs_fd;
+ return -1;
+ }
+
+ return (int)file;
+}
+
+static int _ISFS_close_r(struct _reent *r, int fd) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ file->inUse = false;
+
+ s32 ret = ISFS_Close(file->isfs_fd);
+ if (ret < 0) {
+ r->_errno = -ret;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _ISFS_read_r(struct _reent *r, int fd, char *ptr, size_t len) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ if (len <= 0) {
+ return 0;
+ }
+
+ s32 ret = ISFS_Read(file->isfs_fd, read_buffer, len);
+ if (ret < 0) {
+ r->_errno = -ret;
+ return -1;
+ } else if ((size_t) ret < len) {
+ r->_errno = EOVERFLOW;
+ }
+
+ memcpy(ptr, read_buffer, ret);
+ return ret;
+}
+
+static off_t _ISFS_seek_r(struct _reent *r, int fd, off_t pos, int dir) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ s32 ret = ISFS_Seek(file->isfs_fd, pos, dir);
+ if (ret < 0) {
+ r->_errno = -ret;
+ return -1;
+ }
+ return ret;
+}
+
+static void stat_entry(DIR_ENTRY *entry, struct stat *st) {
+ st->st_dev = 0x4957;
+ st->st_ino = 0;
+ st->st_mode = ((is_dir(entry)) ? S_IFDIR : S_IFREG) | (S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_nlink = 1;
+ st->st_uid = 1;
+ st->st_gid = 2;
+ st->st_rdev = st->st_dev;
+ st->st_size = entry->size;
+ st->st_atime = 0;
+ st->st_spare1 = 0;
+ st->st_mtime = 0;
+ st->st_spare2 = 0;
+ st->st_ctime = 0;
+ st->st_spare3 = 0;
+ st->st_blksize = SECTOR_SIZE;
+ st->st_blocks = (entry->size + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ st->st_spare4[0] = 0;
+ st->st_spare4[1] = 0;
+}
+
+static int _ISFS_fstat_r(struct _reent *r, int fd, struct stat *st) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ stat_entry(file->entry, st);
+ return 0;
+}
+
+static int _ISFS_stat_r(struct _reent *r, const char *path, struct stat *st) {
+ DIR_ENTRY *entry = entry_from_path(path);
+ if (!entry) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+ stat_entry(entry, st);
+ return 0;
+}
+
+static int _ISFS_chdir_r(struct _reent *r, const char *path) {
+ DIR_ENTRY *entry = entry_from_path(path);
+ if (!entry) {
+ r->_errno = ENOENT;
+ return -1;
+ } else if (!is_dir(entry)) {
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ return 0;
+}
+
+static DIR_ITER *_ISFS_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ state->entry = entry_from_path(path);
+ if (!state->entry) {
+ r->_errno = ENOENT;
+ return NULL;
+ } else if (!is_dir(state->entry)) {
+ r->_errno = ENOTDIR;
+ return NULL;
+ }
+ state->index = 0;
+ state->inUse = true;
+ return dirState;
+}
+
+static int _ISFS_dirreset_r(struct _reent *r, DIR_ITER *dirState) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ state->index = 0;
+ return 0;
+}
+
+static int _ISFS_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ if (state->index >= state->entry->fileCount) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+ DIR_ENTRY *entry = &state->entry->children[state->index++];
+ strncpy(filename, entry->name, ISFS_MAXPATHLEN - 1);
+ stat_entry(entry, st);
+ return 0;
+}
+
+static int _ISFS_dirclose_r(struct _reent *r, DIR_ITER *dirState) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ state->inUse = false;
+ return 0;
+}
+
+static const devoptab_t dotab_isfs = {
+ DEVICE_NAME,
+ sizeof(FILE_STRUCT),
+ _ISFS_open_r,
+ _ISFS_close_r,
+ NULL,
+ _ISFS_read_r,
+ _ISFS_seek_r,
+ _ISFS_fstat_r,
+ _ISFS_stat_r,
+ NULL,
+ NULL,
+ _ISFS_chdir_r,
+ NULL,
+ NULL,
+ sizeof(DIR_STATE_STRUCT),
+ _ISFS_diropen_r,
+ _ISFS_dirreset_r,
+ _ISFS_dirnext_r,
+ _ISFS_dirclose_r,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static DIR_ENTRY *add_child_entry(DIR_ENTRY *dir, const char *name) {
+ DIR_ENTRY *newChildren = realloc(dir->children, (dir->fileCount + 1) * sizeof(DIR_ENTRY));
+ if (!newChildren) return NULL;
+ bzero(newChildren + dir->fileCount, sizeof(DIR_ENTRY));
+ dir->children = newChildren;
+ DIR_ENTRY *child = &dir->children[dir->fileCount++];
+ child->name = strdup(name);
+ if (!child->name) return NULL;
+ child->abspath = malloc(strlen(dir->abspath) + (dir != root) + strlen(name) + 1);
+ if (!child->abspath) return NULL;
+ sprintf(child->abspath, "%s/%s", dir == root ? "" : dir->abspath, name);
+ return child;
+}
+
+static bool read_recursive(DIR_ENTRY *parent) {
+ u32 fileCount;
+ s32 ret = ISFS_ReadDir(parent->abspath, NULL, &fileCount);
+ if (ret != ISFS_OK) {
+ s32 fd = ISFS_Open(parent->abspath, ISFS_OPEN_READ);
+ if (fd >= 0) {
+ static fstats st __attribute__((aligned(32)));
+ if (ISFS_GetFileStats(fd, &st) == ISFS_OK) parent->size = st.file_length;
+ ISFS_Close(fd);
+ }
+ return true;
+ }
+ parent->flags = FLAG_DIR;
+ if (fileCount > 0) {
+ if ((ISFS_MAXPATHLEN * fileCount) > BUFFER_SIZE) return false;
+ ret = ISFS_ReadDir(parent->abspath, read_buffer, &fileCount);
+ if (ret != ISFS_OK) return false;
+ u32 fileNum;
+ char *name = read_buffer;
+ for (fileNum = 0; fileNum < fileCount; fileNum++) {
+ DIR_ENTRY *child = add_child_entry(parent, name);
+ if (!child) return false;
+ name += strlen(name) + 1;
+ }
+ for (fileNum = 0; fileNum < fileCount; fileNum++)
+ if (!read_recursive(parent->children + fileNum))
+ return false;
+ }
+ return true;
+}
+
+static bool read_isfs() {
+ root = malloc(sizeof(DIR_ENTRY));
+ if (!root) return false;
+ bzero(root, sizeof(DIR_ENTRY));
+ current = root;
+ root->name = strdup("/");
+ if (!root->name) return false;
+ root->abspath = strdup("/");
+ if (!root->abspath) return false;
+ return read_recursive(root);
+}
+
+static void cleanup_recursive(DIR_ENTRY *entry) {
+ u32 i;
+ for (i = 0; i < entry->fileCount; i++) cleanup_recursive(&entry->children[i]);
+ if (entry->children) free(entry->children);
+ if (entry->name) free(entry->name);
+ if (entry->abspath) free(entry->abspath);
+}
+
+bool ISFS_Mount() {
+ ISFS_Unmount();
+ bool success = read_isfs() && (dotab_device = AddDevice(&dotab_isfs)) >= 0;
+ if (!success) ISFS_Unmount();
+ return success;
+}
+
+bool ISFS_Unmount() {
+ if (root) {
+ cleanup_recursive(root);
+ free(root);
+ root = NULL;
+ }
+ current = root;
+ if (dotab_device >= 0) {
+ dotab_device = -1;
+ return !RemoveDevice(DEVICE_NAME ":");
+ }
+ return true;
+}
+
+#include "certs_bin.h"
+#include "su_tik_bin.h"
+#include "su_tmd_bin.h"
+
+s32 ISFS_SU() {
+ u32 key = 0;
+ return ES_Identify((signed_blob *) certs_bin, sizeof(certs_bin),
+ (signed_blob *) su_tmd_bin, sizeof(su_tmd_bin),
+ (signed_blob *) su_tik_bin, sizeof(su_tik_bin),
+ &key);
+}
diff --git a/src/sys/wii/isfs.h b/src/sys/wii/isfs.h
new file mode 100644
index 0000000..ad2bcff
--- /dev/null
+++ b/src/sys/wii/isfs.h
@@ -0,0 +1,40 @@
+/*
+
+libisfs -- a NAND filesystem devoptab library for the Wii
+
+Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from
+the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1.The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software in a
+product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+2.Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+3.This notice may not be removed or altered from any source distribution.
+
+
+[In compliance with the above: I patched this code up somewhat so that it
+builds with all warnings. -- Storlek]
+*/
+#ifndef _LIBISFS_H
+#define _LIBISFS_H
+
+#include <ogc/isfs.h>
+
+#define ISFS_MAXPATHLEN (ISFS_MAXPATH + 1)
+
+bool ISFS_Mount(void);
+bool ISFS_Unmount(void);
+s32 ISFS_SU(void);
+
+#endif /* _LIBISFS_H_ */
diff --git a/src/sys/wii/osdefs.c b/src/sys/wii/osdefs.c
new file mode 100644
index 0000000..72fcf5f
--- /dev/null
+++ b/src/sys/wii/osdefs.c
@@ -0,0 +1,284 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+#include "osdefs.h"
+#include "event.h"
+#include "song.h"
+#include "it.h" // need for kbd_get_alnum
+#include "page.h" // need for struct key_event
+#include "log.h"
+
+#include <di/di.h>
+#include <fat.h>
+#include <ogc/machine/processor.h>
+#include <ogc/system.h>
+#include <ogc/es.h>
+#include <ogc/ios.h>
+#include <errno.h>
+#include <sys/dir.h>
+#include "isfs.h"
+#define CACHE_PAGES 8
+
+// cargopasta'd from libogc git __di_check_ahbprot
+static u32 _check_ahbprot(void) {
+ s32 res;
+ u64 title_id;
+ u32 tmd_size;
+ STACK_ALIGN(u32, tmdbuf, 1024, 32);
+
+ res = ES_GetTitleID(&title_id);
+ if (res < 0) {
+ log_appendf(4, "ES_GetTitleID() failed: %d", res);
+ return res;
+ }
+
+ res = ES_GetStoredTMDSize(title_id, &tmd_size);
+ if (res < 0) {
+ log_appendf(4, "ES_GetStoredTMDSize() failed: %d", res);
+ return res;
+ }
+
+ if (tmd_size > 4096) {
+ log_appendf(4, "TMD too big: %d", tmd_size);
+ return -EINVAL;
+ }
+
+ res = ES_GetStoredTMD(title_id, tmdbuf, tmd_size);
+ if (res < 0) {
+ log_appendf(4, "ES_GetStoredTMD() failed: %d", res);
+ return -EINVAL;
+ }
+
+ if ((tmdbuf[0x76] & 3) == 3) {
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *osname = "wii";
+
+void wii_sysinit(int *pargc, char ***pargv)
+{
+ DIR_ITER *dir;
+ char *ptr = NULL;
+
+ log_appendf(1, "[Wii] This is IOS%d v%X, and AHBPROT is %s",
+ IOS_GetVersion(), IOS_GetRevision(), _check_ahbprot() > 0 ? "enabled" : "disabled");
+ if (*pargc == 0 && *pargv == NULL) {
+ // I don't know if any other loaders provide similarly broken environments
+ log_appendf(1, "[Wii] Was I just bannerbombed? Prepare for crash at exit...");
+ } else if (memcmp((void *) 0x80001804, "STUBHAXX", 8) == 0) {
+ log_appendf(1, "[Wii] Hello, HBC user!");
+ } else {
+ log_appendf(1, "[Wii] Where am I?!");
+ }
+
+ ISFS_SU();
+ if (ISFS_Initialize() == IPC_OK)
+ ISFS_Mount();
+ fatInit(CACHE_PAGES, 0);
+
+ // Attempt to locate a suitable home directory.
+ if (!*pargc || !*pargv) {
+ // loader didn't bother setting these
+ *pargc = 1;
+ *pargv = malloc(sizeof(char **));
+ *pargv[0] = str_dup("?");
+ } else if (strchr(*pargv[0], '/') != NULL) {
+ // presumably launched from hbc menu - put stuff in the boot dir
+ // (does get_parent_directory do what I want here?)
+ ptr = get_parent_directory(*pargv[0]);
+ }
+ if (!ptr) {
+ // Make a guess anyway
+ ptr = str_dup("sd:/apps/schismtracker");
+ }
+ if (chdir(ptr) != 0) {
+ free(ptr);
+ dir = diropen("sd:/");
+ if (dir) {
+ // Ok at least the sd card works, there's some other dysfunction
+ dirclose(dir);
+ ptr = str_dup("sd:/");
+ } else {
+ // Safe (but useless) default
+ ptr = str_dup("isfs:/");
+ }
+ chdir(ptr); // Hope that worked, otherwise we're hosed
+ }
+ put_env_var("HOME", ptr);
+ free(ptr);
+}
+
+void wii_sysexit(void)
+{
+ ISFS_Deinitialize();
+}
+
+void wii_sdlinit(void)
+{
+ int n, total;
+
+ if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0) {
+ log_appendf(4, "joystick init failed: %s", SDL_GetError());
+ return;
+ }
+
+ total = SDL_NumJoysticks();
+ for (n = 0; n < total; n++) {
+ SDL_Joystick *js = SDL_JoystickOpen(n);
+ if (js == NULL) {
+ log_appendf(4, "[%d] open fail", n);
+ continue;
+ }
+ }
+}
+
+
+static int lasthatsym = 0;
+
+static SDLKey hat_to_keysym(int value)
+{
+ // up/down take precedence over left/right
+ switch (value) {
+ case SDL_HAT_LEFTUP:
+ case SDL_HAT_UP:
+ case SDL_HAT_RIGHTUP:
+ return SDLK_UP;
+ case SDL_HAT_LEFTDOWN:
+ case SDL_HAT_DOWN:
+ case SDL_HAT_RIGHTDOWN:
+ return SDLK_DOWN;
+ case SDL_HAT_LEFT:
+ return SDLK_LEFT;
+ case SDL_HAT_RIGHT:
+ return SDLK_RIGHT;
+ default: // SDL_HAT_CENTERED
+ return 0;
+ }
+}
+
+// Huge event-rewriting hack to get at least a sort of useful interface with no keyboard.
+// It's obviously impossible to provide any sort of editing functions in this manner,
+// but it at least allows simple song playback.
+int wii_sdlevent(SDL_Event *event)
+{
+ SDL_Event newev = {};
+ SDLKey sym;
+
+ switch (event->type) {
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ { // argh
+ struct key_event k = {
+ .mod = event->key.keysym.mod,
+ .sym = event->key.keysym.sym,
+ };
+ event->key.keysym.unicode = kbd_get_alnum(&k);
+ }
+ return 1;
+
+ case SDL_JOYHATMOTION:
+ // TODO key repeat for these, somehow
+ sym = hat_to_keysym(event->jhat.value);
+ if (sym) {
+ newev.type = SDL_KEYDOWN;
+ newev.key.state = SDL_PRESSED;
+ lasthatsym = sym;
+ } else {
+ newev.type = SDL_KEYUP;
+ newev.key.state = SDL_RELEASED;
+ sym = lasthatsym;
+ lasthatsym = 0;
+ }
+ newev.key.which = event->jhat.which;
+ newev.key.keysym.sym = sym;
+ newev.key.type = newev.type; // is this a no-op?
+ *event = newev;
+ return 1;
+
+ case SDL_JOYBUTTONDOWN:
+ case SDL_JOYBUTTONUP:
+ switch (event->jbutton.button) {
+ case 0: // A
+ case 1: // B
+ default:
+ return 0;
+ case 2: // 1
+ if (song_get_mode() == MODE_STOPPED) {
+ // nothing playing? go to load screen
+ sym = SDLK_F9;
+ } else {
+ sym = SDLK_F8;
+ }
+ break;
+ case 3: // 2
+ if (status.current_page == PAGE_LOAD_MODULE) {
+ // if the cursor is on a song, load then play; otherwise handle as enter
+ // (hmm. ctrl-enter?)
+ sym = SDLK_RETURN;
+ } else {
+ // F5 key
+ sym = SDLK_F5;
+ }
+ break;
+ case 4: // -
+ // dialog escape, or jump back a pattern
+ if (status.dialog_type) {
+ sym = SDLK_ESCAPE;
+ break;
+ } else if (event->type == SDL_JOYBUTTONDOWN && song_get_mode() == MODE_PLAYING) {
+ song_set_current_order(song_get_current_order() - 1);
+ }
+ return 0;
+ case 5: // +
+ // dialog enter, or jump forward a pattern
+ if (status.dialog_type) {
+ sym = SDLK_RETURN;
+ break;
+ } else if (event->type == SDL_JOYBUTTONDOWN && song_get_mode() == MODE_PLAYING) {
+ song_set_current_order(song_get_current_order() + 1);
+ }
+ return 0;
+ case 6: // Home
+ event->type = SDL_QUIT;
+ return 1;
+ }
+ newev.key.which = event->jbutton.which;
+ newev.key.keysym.sym = sym;
+ if (event->type == SDL_JOYBUTTONDOWN) {
+ newev.type = SDL_KEYDOWN;
+ newev.key.state = SDL_PRESSED;
+ } else {
+ newev.type = SDL_KEYUP;
+ newev.key.state = SDL_RELEASED;
+ }
+ newev.key.type = newev.type; // no-op?
+ *event = newev;
+ return 1;
+ }
+ return 1;
+}
+
diff --git a/src/sys/wii/schismtracker/icon.png b/src/sys/wii/schismtracker/icon.png
new file mode 100644
index 0000000..ccab9c5
--- /dev/null
+++ b/src/sys/wii/schismtracker/icon.png
Binary files differ
diff --git a/src/sys/wii/schismtracker/meta.xml b/src/sys/wii/schismtracker/meta.xml
new file mode 100644
index 0000000..81a771e
--- /dev/null
+++ b/src/sys/wii/schismtracker/meta.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<app version="1">
+<name>Schism Tracker</name>
+<coder>Storlek</coder>
+<version>hg</version>
+<release_date>YYYYMMDD000000</release_date>
+<short_description>Tracked music editor</short_description>
+<long_description>Schism Tracker is an editor and player for tracked music (IT, XM, S3M, MOD, etc.), heavily based on the look and feel of the DOS program Impulse Tracker.
+</long_description>
+</app>
diff --git a/src/sys/wii/su_tik_bin.h b/src/sys/wii/su_tik_bin.h
new file mode 100644
index 0000000..29f42c8
--- /dev/null
+++ b/src/sys/wii/su_tik_bin.h
@@ -0,0 +1,46 @@
+static unsigned const char su_tik_bin[] = {
+'\000', '\001', '\000', '\001', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\122', '\157', '\157', '\164', '\055', '\103', '\101', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\061', '\055',
+'\130', '\123', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\063', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\070', '\244', '\122', '\066', '\356', '\137', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\001',
+'\000', '\000', '\000', '\002', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
+'\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
+'\377', '\377', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\021', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000',
+
+};
diff --git a/src/sys/wii/su_tmd_bin.h b/src/sys/wii/su_tmd_bin.h
new file mode 100644
index 0000000..c6ca575
--- /dev/null
+++ b/src/sys/wii/su_tmd_bin.h
@@ -0,0 +1,36 @@
+static unsigned const char su_tmd_bin[] = {
+'\000', '\001', '\000', '\001', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\122', '\157', '\157', '\164', '\055', '\103', '\101', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\061', '\055',
+'\103', '\120', '\060', '\060', '\060', '\060', '\060', '\060', '\060', '\064', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\001',
+'\000', '\000', '\000', '\002', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\001',
+'\000', '\000', '\000', '\015', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+'\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000',
+
+};
diff --git a/src/sys/win32/filetype.c b/src/sys/win32/filetype.c
new file mode 100644
index 0000000..7a79802
--- /dev/null
+++ b/src/sys/win32/filetype.c
@@ -0,0 +1,35 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "headers.h"
+#include "osdefs.h"
+
+#include <shlobj.h>
+#include <windows.h>
+#include <string.h>
+
+void win32_filecreated_callback(const char *filename)
+{
+ /* let explorer know when we create a file. */
+ SHChangeNotify(SHCNE_CREATE, SHCNF_PATH|SHCNF_FLUSHNOWAIT, filename, NULL);
+ SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH|SHCNF_FLUSHNOWAIT, filename, NULL);
+}
diff --git a/src/sys/win32/localtime_r.c b/src/sys/win32/localtime_r.c
new file mode 100644
index 0000000..3331e77
--- /dev/null
+++ b/src/sys/win32/localtime_r.c
@@ -0,0 +1,53 @@
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2007 Tatsuhiro Tsujikawa
+ *
+ * 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
+ *
+ * (Modified slightly to build with Schism Tracker)
+ */
+
+#define NEED_TIME
+#include "headers.h"
+
+#include <windows.h>
+
+
+static CRITICAL_SECTION localtime_r_cs;
+
+static void localtime_r_atexit(void)
+{
+ DeleteCriticalSection(&localtime_r_cs);
+}
+
+struct tm * localtime_r(const time_t *timep, struct tm *result)
+{
+ static struct tm *local_tm;
+ static int initialized = 0;
+
+ if (!initialized) {
+ ++initialized;
+ InitializeCriticalSection(&localtime_r_cs);
+ atexit(localtime_r_atexit);
+ }
+
+ EnterCriticalSection(&localtime_r_cs);
+ local_tm = localtime(timep);
+ memcpy(result, local_tm, sizeof(struct tm));
+ LeaveCriticalSection(&localtime_r_cs);
+ return result;
+}
+
diff --git a/src/sys/win32/midi-win32mm.c b/src/sys/win32/midi-win32mm.c
new file mode 100644
index 0000000..1c1f27e
--- /dev/null
+++ b/src/sys/win32/midi-win32mm.c
@@ -0,0 +1,311 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+#include "sdlmain.h"
+
+#include "log.h"
+#include "midi.h"
+
+#include "util.h"
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <stdio.h>
+
+#ifndef WIN32
+# error You have no winmm. Why are you trying to build this file?
+#endif
+
+
+struct win32mm_midi {
+ DWORD id;
+
+ HMIDIOUT out;
+ HMIDIIN in;
+
+ MIDIINCAPS icp;
+ MIDIOUTCAPS ocp;
+
+ MIDIHDR hh;
+ LPMIDIHDR obuf;
+ unsigned char sysx[1024];
+};
+static unsigned int mm_period = 0;
+static unsigned int last_known_in_port = 0;
+static unsigned int last_known_out_port = 0;
+
+static void _win32mm_sysex(LPMIDIHDR *q, const unsigned char *d, unsigned int len)
+{
+ char *z;
+ LPMIDIHDR m;
+
+ if (!d) len=0;
+ z = mem_alloc(sizeof(MIDIHDR) + len);
+ m = (LPMIDIHDR)z;
+
+ memset(m, 0, sizeof(MIDIHDR));
+ if (len) memcpy(z + sizeof(MIDIHDR), d, len);
+
+ m->lpData = (z+sizeof(MIDIHDR));
+ m->dwBufferLength = len;
+ m->lpNext = *q;
+ m->dwOffset = 0;
+ (*q) = (m);
+}
+static void _win32mm_send(struct midi_port *p, const unsigned char *data,
+ unsigned int len, UNUSED unsigned int delay)
+{
+ struct win32mm_midi *m;
+ DWORD q;
+
+ if (len == 0) return;
+
+ m = p->userdata;
+ if (len <= 4) {
+ q = data[0];
+ if (len > 1) q |= (data[1] << 8);
+ if (len > 2) q |= (data[2] << 16);
+ if (len > 3) q |= (data[3] << 24); /* eh... */
+ (void)midiOutShortMsg(m->out, q);
+ } else {
+ /* SysEX */
+ _win32mm_sysex(&m->obuf, data, len);
+ if (midiOutPrepareHeader(m->out, m->obuf, sizeof(MIDIHDR)) == MMSYSERR_NOERROR) {
+ (void)midiOutLongMsg(m->out, m->obuf, sizeof(MIDIHDR));
+ }
+ }
+}
+
+
+
+
+struct curry {
+ struct midi_port *p;
+ unsigned char *d;
+ unsigned int len;
+};
+
+
+static CALLBACK void _win32mm_xp_output(UNUSED UINT uTimerID,
+ UNUSED UINT uMsg,
+ DWORD_PTR dwUser,
+ UNUSED DWORD_PTR dw1,
+ UNUSED DWORD_PTR dw2)
+{
+ struct curry *c;
+ c = (struct curry *)dwUser;
+ _win32mm_send(c->p, c->d, c->len, 0);
+ free(c);
+}
+static void _win32mm_send_xp(struct midi_port *p, const unsigned char *buf,
+ unsigned int len, unsigned int delay)
+{
+ /* version for windows XP */
+ struct curry *c;
+
+ if (!delay) _win32mm_send(p,buf,len,0);
+ if (len == 0) return;
+
+ c = mem_alloc(sizeof(struct curry) + len);
+ c->p = p;
+ c->d = ((unsigned char*)c)+sizeof(struct curry);
+ c->len = len;
+ timeSetEvent(delay, mm_period, _win32mm_xp_output, (DWORD_PTR)c,
+ TIME_ONESHOT | TIME_CALLBACK_FUNCTION);
+}
+
+static CALLBACK void _win32mm_inputcb(UNUSED HMIDIIN in, UINT wmsg, DWORD_PTR inst,
+ DWORD_PTR param1, DWORD_PTR param2)
+{
+ struct midi_port *p = (struct midi_port *)inst;
+ struct win32mm_midi *m;
+ unsigned char c[4];
+
+ switch (wmsg) {
+ case MIM_OPEN:
+ SDL_Delay(0); /* eh? */
+ case MIM_CLOSE:
+ break;
+ case MIM_DATA:
+ c[0] = param1 & 255;
+ c[1] = (param1 >> 8) & 255;
+ c[2] = (param1 >> 16) & 255;
+ midi_received_cb(p, c, 3);
+ break;
+ case MIM_LONGDATA:
+ {
+ MIDIHDR* hdr = (MIDIHDR*) param1;
+ if (hdr->dwBytesRecorded > 0)
+ {
+ /* long data */
+ m = p->userdata;
+ midi_received_cb(p, (unsigned char *) m->hh.lpData, m->hh.dwBytesRecorded);
+ //TODO: The event for the midi sysex (midi-core.c SCHISM_EVENT_MIDI_SYSEX) should
+ // call us back so that we can add the buffer back with midiInAddBuffer().
+ }
+ break;
+ }
+ }
+}
+
+
+static int _win32mm_start(struct midi_port *p)
+{
+ struct win32mm_midi *m;
+ UINT id;
+ WORD r;
+
+ m = p->userdata;
+ id = m->id;
+ if (p->io == MIDI_INPUT) {
+ m->in = NULL;
+ r = midiInOpen(&m->in,
+ (UINT_PTR)id,
+ (DWORD_PTR)_win32mm_inputcb,
+ (DWORD_PTR)p,
+ CALLBACK_FUNCTION);
+ if (r != MMSYSERR_NOERROR) return 0;
+ memset(&m->hh, 0, sizeof(m->hh));
+ m->hh.lpData = (LPSTR)m->sysx;
+ m->hh.dwBufferLength = sizeof(m->sysx);
+ m->hh.dwFlags = 0;
+ r = midiInPrepareHeader(m->in, &m->hh, sizeof(MIDIHDR));
+ if (r != MMSYSERR_NOERROR) return 0;
+ r = midiInAddBuffer(m->in, &m->hh, sizeof(MIDIHDR));
+ if (r != MMSYSERR_NOERROR) return 0;
+ if (midiInStart(m->in) != MMSYSERR_NOERROR) return 0;
+
+ }
+ if (p->io & MIDI_OUTPUT) {
+ m->out = NULL;
+ if (midiOutOpen(&m->out,
+ (UINT_PTR)id,
+ 0, 0,
+ CALLBACK_NULL) != MMSYSERR_NOERROR) return 0;
+ }
+ return 1;
+}
+static int _win32mm_stop(struct midi_port *p)
+{
+ struct win32mm_midi *m;
+ LPMIDIHDR ptr;
+
+ m = p->userdata;
+ if (p->io & MIDI_INPUT) {
+ /* portmidi appears to (essentially) ignore the error codes
+ for these guys */
+ (void)midiInStop(m->in);
+ (void)midiInReset(m->in);
+ (void)midiInUnprepareHeader(m->in,&m->hh,sizeof(m->hh));
+ (void)midiInClose(m->in);
+ }
+ if (p->io & MIDI_OUTPUT) {
+ (void)midiOutReset(m->out);
+ (void)midiOutClose(m->out);
+ /* free output chain */
+ ptr = m->obuf;
+ while (ptr) {
+ m->obuf = m->obuf->lpNext;
+ (void)free(m->obuf);
+ ptr = m->obuf;
+ }
+ }
+ return 1;
+}
+
+
+static void _win32mm_poll(struct midi_provider *p)
+{
+ struct win32mm_midi *data;
+
+ UINT i;
+ UINT mmin, mmout;
+ WORD r;
+
+ mmin = midiInGetNumDevs();
+ for (i = last_known_in_port; i < mmin; i++) {
+ data = mem_alloc(sizeof(struct win32mm_midi));
+ memset(data,0,sizeof(struct win32mm_midi));
+ r = midiInGetDevCaps(i, (LPMIDIINCAPS)&data->icp,
+ sizeof(MIDIINCAPS));
+ if (r != MMSYSERR_NOERROR) {
+ free(data);
+ continue;
+ }
+ data->id = i;
+ midi_port_register(p, MIDI_INPUT, data->icp.szPname, data, 1);
+ }
+ last_known_in_port = mmin;
+
+ mmout = midiOutGetNumDevs();
+ for (i = last_known_out_port; i < mmout; i++) {
+ data = mem_alloc(sizeof(struct win32mm_midi));
+ memset(data,0,sizeof(struct win32mm_midi));
+ r = midiOutGetDevCaps(i, (LPMIDIOUTCAPS)&data->ocp,
+ sizeof(MIDIOUTCAPS));
+ if (r != MMSYSERR_NOERROR) {
+ if (data) free(data);
+ continue;
+ }
+ data->id = i;
+ midi_port_register(p, MIDI_OUTPUT, data->ocp.szPname, data, 1);
+ }
+ last_known_out_port = mmout;
+}
+
+int win32mm_midi_setup(void)
+{
+ static struct midi_driver driver;
+
+ TIMECAPS caps;
+
+ memset(&driver, 0, sizeof(driver));
+
+ driver.flags = 0;
+ driver.poll = _win32mm_poll;
+ driver.thread = NULL;
+ driver.enable = _win32mm_start;
+ driver.disable = _win32mm_stop;
+
+ {
+ if (timeGetDevCaps(&caps, sizeof(caps)) == 0) {
+ mm_period = caps.wPeriodMin;
+ if (timeBeginPeriod(mm_period) == 0) {
+ driver.send = _win32mm_send_xp;
+ driver.flags |= MIDI_PORT_CAN_SCHEDULE;
+ } else {
+ driver.send = _win32mm_send;
+ log_appendf(4, "Cannot install WINMM timer (midi output will skip)");
+ }
+ } else {
+ driver.send = _win32mm_send;
+ log_appendf(4, "Cannot get WINMM timer capabilities (midi output will skip)");
+ }
+ }
+
+ if (!midi_provider_register("Win32MM", &driver)) return 0;
+
+ return 1;
+}
+
diff --git a/src/sys/win32/osdefs.c b/src/sys/win32/osdefs.c
new file mode 100644
index 0000000..0beccbf
--- /dev/null
+++ b/src/sys/win32/osdefs.c
@@ -0,0 +1,198 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Predominantly this file is keyboard crap, but we also get the network configured here */
+
+#include "headers.h"
+#include "sdlmain.h"
+#include "it.h"
+#include "osdefs.h"
+
+#include <windows.h>
+#include <ws2tcpip.h>
+
+/* eek... */
+void win32_get_modkey(int *mk)
+{
+ BYTE ks[256];
+ if (GetKeyboardState(ks) == 0) return;
+
+ if (ks[VK_CAPITAL] & 128) {
+ status.flags |= CAPS_PRESSED;
+ } else {
+ status.flags &= ~CAPS_PRESSED;
+ }
+
+ (*mk) = ((*mk) & ~(KMOD_NUM|KMOD_CAPS))
+ | ((ks[VK_NUMLOCK]&1) ? KMOD_NUM : 0)
+ | ((ks[VK_CAPITAL]&1) ? KMOD_CAPS : 0);
+}
+
+/* more windows key stuff... */
+unsigned int key_repeat_rate(void)
+{
+ DWORD spd;
+ if (!SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &spd, 0)) return 0;
+ if (!spd) return 1;
+ return spd;
+}
+unsigned int key_repeat_delay(void)
+{
+ int delay;
+
+ if (!SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) return 0;
+ switch (delay) {
+ case 0: return 250;
+ case 1: return 500;
+ case 2: return 750;
+ };
+ return 1000;
+}
+
+static HKL default_keymap;
+static HKL us_keymap;
+
+static void win32_setup_keymap(void)
+{
+ default_keymap = GetKeyboardLayout(0);
+ us_keymap = LoadKeyboardLayout("00000409", KLF_ACTIVATE|KLF_REPLACELANG|KLF_NOTELLSHELL);
+ ActivateKeyboardLayout(default_keymap,0);
+}
+
+int key_scancode_lookup(int k, int def)
+{
+#ifndef VK_0
+#define VK_0 '0'
+#define VK_1 '1'
+#define VK_2 '2'
+#define VK_3 '3'
+#define VK_4 '4'
+#define VK_5 '5'
+#define VK_6 '6'
+#define VK_7 '7'
+#define VK_8 '8'
+#define VK_9 '9'
+#define VK_A 'A'
+#define VK_B 'B'
+#define VK_C 'C'
+#define VK_D 'D'
+#define VK_E 'E'
+#define VK_F 'F'
+#define VK_G 'G'
+#define VK_H 'H'
+#define VK_I 'I'
+#define VK_J 'J'
+#define VK_K 'K'
+#define VK_L 'L'
+#define VK_M 'M'
+#define VK_N 'N'
+#define VK_O 'O'
+#define VK_P 'P'
+#define VK_Q 'Q'
+#define VK_R 'R'
+#define VK_S 'S'
+#define VK_T 'T'
+#define VK_U 'U'
+#define VK_V 'V'
+#define VK_W 'W'
+#define VK_X 'X'
+#define VK_Y 'Y'
+#define VK_Z 'Z'
+#endif /* VK_0 */
+
+/* These keys haven't been defined, but were experimentally determined */
+#define VK_SEMICOLON 0xBA
+#define VK_EQUALS 0xBB
+#define VK_COMMA 0xBC
+#define VK_MINUS 0xBD
+#define VK_PERIOD 0xBE
+#define VK_SLASH 0xBF
+#define VK_GRAVE 0xC0
+#define VK_LBRACKET 0xDB
+#define VK_BACKSLASH 0xDC
+#define VK_RBRACKET 0xDD
+#define VK_APOSTROPHE 0xDE
+#define VK_BACKTICK 0xDF
+#define VK_OEM_102 0xE2
+ switch (MapVirtualKeyEx(k, 1 /* MAPVK_VSC_TO_VK */, us_keymap)) {
+ case VK_0: return SDLK_0;
+ case VK_1: return SDLK_1;
+ case VK_2: return SDLK_2;
+ case VK_3: return SDLK_3;
+ case VK_4: return SDLK_4;
+ case VK_5: return SDLK_5;
+ case VK_6: return SDLK_6;
+ case VK_7: return SDLK_7;
+ case VK_8: return SDLK_8;
+ case VK_9: return SDLK_9;
+ case VK_A: return SDLK_a;
+ case VK_B: return SDLK_b;
+ case VK_C: return SDLK_c;
+ case VK_D: return SDLK_d;
+ case VK_E: return SDLK_e;
+ case VK_F: return SDLK_f;
+ case VK_G: return SDLK_g;
+ case VK_H: return SDLK_h;
+ case VK_I: return SDLK_i;
+ case VK_J: return SDLK_j;
+ case VK_K: return SDLK_k;
+ case VK_L: return SDLK_l;
+ case VK_M: return SDLK_m;
+ case VK_N: return SDLK_n;
+ case VK_O: return SDLK_o;
+ case VK_P: return SDLK_p;
+ case VK_Q: return SDLK_q;
+ case VK_R: return SDLK_r;
+ case VK_S: return SDLK_s;
+ case VK_T: return SDLK_t;
+ case VK_U: return SDLK_u;
+ case VK_V: return SDLK_v;
+ case VK_W: return SDLK_w;
+ case VK_X: return SDLK_x;
+ case VK_Y: return SDLK_y;
+ case VK_Z: return SDLK_z;
+ case VK_SEMICOLON: return SDLK_SEMICOLON;
+ case VK_GRAVE: return SDLK_BACKQUOTE;
+ case VK_APOSTROPHE: return SDLK_QUOTE;
+ case VK_BACKTICK: return SDLK_BACKQUOTE;
+ case VK_BACKSLASH: return SDLK_BACKSLASH;
+ case VK_LBRACKET: return SDLK_LEFTBRACKET;
+ case VK_RBRACKET: return SDLK_RIGHTBRACKET;
+ };
+ return def;
+}
+
+
+void win32_sysinit(UNUSED int *pargc, UNUSED char ***pargv)
+{
+ static WSADATA ignored;
+
+ win32_setup_keymap();
+
+ memset(&ignored, 0, sizeof(ignored));
+ if (WSAStartup(0x202, &ignored) == SOCKET_ERROR) {
+ WSACleanup(); /* ? */
+ status.flags |= NO_NETWORK;
+ }
+}
+
diff --git a/src/sys/win32/schism.nsis b/src/sys/win32/schism.nsis
new file mode 100644
index 0000000..525e1d2
--- /dev/null
+++ b/src/sys/win32/schism.nsis
@@ -0,0 +1,75 @@
+Name "Schism Tracker"
+Caption 'Schism Tracker'
+OutFile 'install.exe'
+InstallDir "$PROGRAMFILES\Schism Tracker"
+LicenseData 'COPYING.txt'
+LicenseBkColor 0xFFFFFF
+ShowInstDetails show
+XpStyle on
+
+Page License
+Page Directory
+Page InstFiles
+
+UninstPage uninstConfirm
+UninstPage instfiles
+
+AutoCloseWindow false
+
+Section
+ SetOutPath $INSTDIR
+ File "schismtracker.exe"
+ File "schism.ico"
+ File "SDL.dll"
+ File "COPYING.txt"
+ File "README.txt"
+ File "NEWS.txt"
+ File "ChangeLog.txt"
+ WriteUninstaller "uninstall.exe"
+
+ WriteRegStr HKCR ".it" "" "Schism Tracker"
+ WriteRegStr HKCR ".s3m" "" "Schism Tracker"
+ WriteRegStr HKCR ".mod" "" "Schism Tracker"
+ WriteRegStr HKCR ".669" "" "Schism Tracker"
+ WriteRegStr HKCR ".amf" "" "Schism Tracker"
+ WriteRegStr HKCR ".ams" "" "Schism Tracker"
+ WriteRegStr HKCR ".dbm" "" "Schism Tracker"
+ WriteRegStr HKCR ".dmf" "" "Schism Tracker"
+ WriteRegStr HKCR ".dsm" "" "Schism Tracker"
+ WriteRegStr HKCR ".far" "" "Schism Tracker"
+ WriteRegStr HKCR ".mdl" "" "Schism Tracker"
+ WriteRegStr HKCR ".med" "" "Schism Tracker"
+ WriteRegStr HKCR ".mt2" "" "Schism Tracker"
+ WriteRegStr HKCR ".mtm" "" "Schism Tracker"
+ WriteRegStr HKCR ".okt" "" "Schism Tracker"
+ WriteRegStr HKCR ".psm" "" "Schism Tracker"
+ WriteRegStr HKCR ".ptm" "" "Schism Tracker"
+ WriteRegStr HKCR ".stm" "" "Schism Tracker"
+ WriteRegStr HKCR ".ult" "" "Schism Tracker"
+ WriteRegStr HKCR ".umx" "" "Schism Tracker"
+ WriteRegStr HKCR ".xm" "" "Schism Tracker"
+
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Schism Tracker" "DisplayName" "Schism Tracker (remove only)"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Schism Tracker" "UninstallString" '"$INSTDIR\uninstall.exe"'
+ WriteRegStr HKCR "Schism Tracker\Shell\open\command\" "" '"$INSTDIR\schismtracker.exe" "%1"'
+ WriteRegStr HKCR "Schism Tracker\DefaultIcon" "" "$INSTDIR\schism.ico"
+
+ CreateDirectory "$SMPROGRAMS\Schism Tracker"
+ CreateShortCut "$SMPROGRAMS\Schism Tracker\Schism Tracker.lnk" "$INSTDIR\schismtracker.exe" "" "$INSTDIR\schism.ico"
+ CreateShortCut "$SMPROGRAMS\Schism Tracker\Schism Font Editor.lnk" "$INSTDIR\schismtracker.exe" "--font-editor" "$INSTDIR\schism.ico"
+ CreateShortCut "$SMPROGRAMS\Schism Tracker\Uninstall Schism Tracker.lnk" "$INSTDIR\uninstall.exe"
+SectionEnd
+
+
+Section "Uninstall"
+ Delete "$INSTDIR\schismtracker.exe"
+ Delete "$INSTDIR\SDL.dll"
+ Delete "$INSTDIR\schism.ico"
+ Delete "$INSTDIR\COPYING.txt"
+ Delete "$INSTDIR\README.txt"
+ Delete "$INSTDIR\NEWS.txt"
+ Delete "$INSTDIR\ChangeLog.txt"
+ Delete "$SMPROGRAMS\Schism Tracker\Schism Tracker.lnk"
+ Delete "$SMPROGRAMS\Schism Tracker\Font Editor.lnk"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Schism Tracker"
+SectionEnd
diff --git a/src/sys/win32/schismres.rc b/src/sys/win32/schismres.rc
new file mode 100644
index 0000000..ded8320
--- /dev/null
+++ b/src/sys/win32/schismres.rc
@@ -0,0 +1,45 @@
+//vi:set bd=syn\ c:
+#include <winver.h>
+#include <config.h> // grab PACKAGE_VERSION
+
+#ifndef WRC_VERSION
+# define WRC_VERSION 0,0,0,0
+#endif
+
+#define WRC_PRODUCTVER_STR PACKAGE_VERSION
+
+#define VER_PRODUCTNAME "Schism Tracker"
+
+schismicon ICON "icons/schismres.ico"
+
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION WRC_VERSION
+PRODUCTVERSION WRC_VERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEFLAGS 0
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "CompanyName", "Storlek"
+ VALUE "LegalCopyright", "Copyright \xA9 2003-2012 Storlek"
+ VALUE "Comments", "http://schismtracker.org/"
+ VALUE "ProductName", VER_PRODUCTNAME
+ VALUE "FileDescription", VER_PRODUCTNAME
+ VALUE "InternalName", PACKAGE_NAME
+ VALUE "OriginalFilename", "schismtracker.exe"
+ VALUE "FileVersion", WRC_PRODUCTVER_STR
+ VALUE "ProductVersion", WRC_PRODUCTVER_STR
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/src/sys/win32/slurp-win32.c b/src/sys/win32/slurp-win32.c
new file mode 100644
index 0000000..c2015e7
--- /dev/null
+++ b/src/sys/win32/slurp-win32.c
@@ -0,0 +1,102 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#ifndef WIN32
+# error You are not on Windows. What are you doing?
+#endif
+
+/* FIXME | this really ought to just provide an mmap() wrapper
+ FIXME | instead of reimplementing everything separately */
+
+#include <windows.h>
+#include <sys/stat.h>
+
+#include "log.h"
+#include "slurp.h"
+
+// indices for 'h' (handles)
+enum { FILE_HANDLE = 0, MAPPING_HANDLE = 1 };
+
+static void _win32_unmap(slurp_t *slurp)
+{
+ if (slurp->data != NULL) {
+ UnmapViewOfFile(slurp->data);
+ slurp->data = NULL;
+ }
+
+ HANDLE *h = slurp->bextra;
+ if (h[FILE_HANDLE] != INVALID_HANDLE_VALUE) {
+ CloseHandle(h[FILE_HANDLE]);
+ }
+ if (h[MAPPING_HANDLE] != NULL) {
+ CloseHandle(h[MAPPING_HANDLE]);
+ }
+ free(h);
+ slurp->bextra = NULL;
+}
+
+// This reader used to return -1 sometimes, which is kind of a hack to tell the
+// the rest of the loading code to try some other means of opening the file,
+// which on win32 is basically just fopen + malloc + fread. If MapViewOfFile
+// won't work, chances are pretty good that stdio is going to fail as well, so
+// I'm just writing these cases off as every bit as unrecoverable as if the
+// file didn't exist.
+// Note: this doesn't bother setting errno; maybe it should?
+
+static int _win32_error_unmap(slurp_t *slurp, const char *filename, const char *function)
+{
+ DWORD err = GetLastError();
+ LPTSTR errmsg;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+ err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errmsg, 0, NULL);
+ // I don't particularly want to split this stuff onto two lines, but
+ // it's the only way to make the error message readable in some cases
+ // (though no matter what, the message is still probably going to be
+ // truncated because Windows is excessively verbose)
+ log_appendf(4, "%s: %s: error %lu:", filename, function, err);
+ log_appendf(4, " %s", errmsg);
+ LocalFree(errmsg);
+ _win32_unmap(slurp);
+ return 0;
+}
+
+int slurp_win32(slurp_t *slurp, const char *filename, size_t st)
+{
+ LPVOID addr;
+ HANDLE *h = slurp->bextra = mem_alloc(sizeof(HANDLE) * 2);
+
+ if ((h[FILE_HANDLE] = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
+ return _win32_error_unmap(slurp, filename, "CreateFile");
+ }
+ if ((h[MAPPING_HANDLE] = CreateFileMapping(h[FILE_HANDLE], NULL, PAGE_READONLY, 0, 0, NULL)) == NULL) {
+ return _win32_error_unmap(slurp, filename, "CreateFileMapping");
+ }
+ if ((slurp->data = MapViewOfFile(h[MAPPING_HANDLE], FILE_MAP_READ, 0, 0, 0)) == NULL) {
+ return _win32_error_unmap(slurp, filename, "MapViewOfFile");
+ }
+ slurp->length = st;
+ slurp->closure = _win32_unmap;
+ return 1;
+}
diff --git a/src/sys/win32/volume-win32mm.c b/src/sys/win32/volume-win32mm.c
new file mode 100644
index 0000000..e3d966c
--- /dev/null
+++ b/src/sys/win32/volume-win32mm.c
@@ -0,0 +1,91 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "headers.h"
+
+#include "util.h"
+#include "osdefs.h"
+
+#ifndef WIN32
+# error Why do you want to build this if you do not intend to use it?
+#endif
+
+#include <windows.h>
+#include <mmsystem.h>
+
+/* Note: [Gargaj]
+
+ WinMM DOES support max volumes up to 65535, but the scroller is
+ so goddamn slow and it only supports 3 digits anyway, that
+ it doesn't make any sense to keep the precision.
+*/
+
+int win32mm_volume_get_max(void)
+{
+ return 0xFF;
+}
+
+static HWAVEOUT open_mixer(void)
+{
+ HWAVEOUT hwo=NULL;
+ WAVEFORMATEX pwfx;
+#if 0
+ pwfx.wFormatTag = WAVE_FORMAT_UNKNOWN;
+ pwfx.nChannels = 0;
+ pwfx.nSamplesPerSec = 0;
+ pwfx.wBitsPerSample = 0;
+ pwfx.nBlockAlign = 0;
+ pwfx.nAvgBytesPerSec = 0;
+ pwfx.cbSize = 0;
+#else
+ pwfx.wFormatTag = WAVE_FORMAT_PCM;
+ pwfx.nChannels = 1;
+ pwfx.nSamplesPerSec = 44100;
+ pwfx.wBitsPerSample = 8;
+ pwfx.nBlockAlign = 4;
+ pwfx.nAvgBytesPerSec = 44100*1*1;
+ pwfx.cbSize = 0;
+#endif
+ if (waveOutOpen(&hwo, WAVE_MAPPER, &pwfx, 0, 0, CALLBACK_NULL)!=MMSYSERR_NOERROR)
+ return NULL;
+ return hwo;
+}
+
+void win32mm_volume_read(int *left, int *right)
+{
+ DWORD vol;
+
+ *left = *right = 0;
+
+ waveOutGetVolume(NULL,&vol);
+
+ *left = (vol & 0xFFFF) >> 8;
+ *right = (vol >> 16) >> 8;
+}
+
+void win32mm_volume_write(int left, int right)
+{
+ DWORD vol = ((left & 0xFF)<<8) | ((right & 0xFF)<<(16+8));
+
+ waveOutSetVolume(NULL,vol);
+}
diff --git a/src/sys/win32/wine-ddraw.h b/src/sys/win32/wine-ddraw.h
new file mode 100644
index 0000000..325dbcf
--- /dev/null
+++ b/src/sys/win32/wine-ddraw.h
@@ -0,0 +1,2679 @@
+/* this was hacked up slightly to build outside of the rest of wine
+ * it works well enough to build schism.
+ */
+
+/*
+ * Copyright (C) the Wine project
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_DDRAW_H
+#define __WINE_DDRAW_H
+
+#define COM_NO_WINDOWS_H
+#include <objbase.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+#ifndef DIRECTDRAW_VERSION
+#define DIRECTDRAW_VERSION 0x0700
+#endif /* DIRECTDRAW_VERSION */
+
+/*****************************************************************************
+ * Predeclare the interfaces
+ */
+#ifndef __DDRAW_GUID_DEFINED__
+DEFINE_GUID( CLSID_DirectDraw, 0xD7B70EE0,0x4340,0x11CF,0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35 );
+DEFINE_GUID( CLSID_DirectDraw7, 0x3C305196,0x50DB,0x11D3,0x9C,0xFE,0x00,0xC0,0x4F,0xD9,0x30,0xC5 );
+DEFINE_GUID( CLSID_DirectDrawClipper, 0x593817A0,0x7DB3,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xb9,0x33,0x56 );
+DEFINE_GUID( IID_IDirectDraw, 0x6C14DB80,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 );
+DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 );
+DEFINE_GUID( IID_IDirectDraw3, 0x618f8ad4,0x8b7a,0x11d0,0x8f,0xcc,0x0,0xc0,0x4f,0xd9,0x18,0x9d );
+DEFINE_GUID( IID_IDirectDraw4, 0x9c59509a,0x39bd,0x11d1,0x8c,0x4a,0x00,0xc0,0x4f,0xd9,0x30,0xc5 );
+DEFINE_GUID( IID_IDirectDraw7, 0x15e65ec0,0x3b9c,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b );
+DEFINE_GUID( IID_IDirectDrawSurface, 0x6C14DB81,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 );
+DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 );
+DEFINE_GUID( IID_IDirectDrawSurface3, 0xDA044E00,0x69B2,0x11D0,0xA1,0xD5,0x00,0xAA,0x00,0xB8,0xDF,0xBB );
+DEFINE_GUID( IID_IDirectDrawSurface4, 0x0B2B8630,0xAD35,0x11D0,0x8E,0xA6,0x00,0x60,0x97,0x97,0xEA,0x5B );
+DEFINE_GUID( IID_IDirectDrawSurface7, 0x06675a80,0x3b9b,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b );
+DEFINE_GUID( IID_IDirectDrawPalette, 0x6C14DB84,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 );
+DEFINE_GUID( IID_IDirectDrawClipper, 0x6C14DB85,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 );
+DEFINE_GUID( IID_IDirectDrawColorControl,0x4B9F0EE0,0x0D7E,0x11D0,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8 );
+DEFINE_GUID( IID_IDirectDrawGammaControl,0x69C11C3E,0xB46B,0x11D1,0xAD,0x7A,0x00,0xC0,0x4F,0xC2,0x9B,0x4E );
+#endif
+
+typedef struct IDirectDraw *LPDIRECTDRAW;
+typedef struct IDirectDraw2 *LPDIRECTDRAW2;
+typedef struct IDirectDraw3 *LPDIRECTDRAW3;
+typedef struct IDirectDraw4 *LPDIRECTDRAW4;
+typedef struct IDirectDraw7 *LPDIRECTDRAW7;
+typedef struct IDirectDrawClipper *LPDIRECTDRAWCLIPPER;
+typedef struct IDirectDrawPalette *LPDIRECTDRAWPALETTE;
+typedef struct IDirectDrawSurface *LPDIRECTDRAWSURFACE;
+typedef struct IDirectDrawSurface2 *LPDIRECTDRAWSURFACE2;
+typedef struct IDirectDrawSurface3 *LPDIRECTDRAWSURFACE3;
+typedef struct IDirectDrawSurface4 *LPDIRECTDRAWSURFACE4;
+typedef struct IDirectDrawSurface7 *LPDIRECTDRAWSURFACE7;
+typedef struct IDirectDrawColorControl *LPDIRECTDRAWCOLORCONTROL;
+typedef struct IDirectDrawGammaControl *LPDIRECTDRAWGAMMACONTROL;
+
+
+#define DDENUMRET_CANCEL 0
+#define DDENUMRET_OK 1
+
+#define DD_OK 0
+
+
+#define _FACDD 0x876
+#define MAKE_DDHRESULT( code ) MAKE_HRESULT( 1, _FACDD, code )
+
+#define DDERR_ALREADYINITIALIZED MAKE_DDHRESULT( 5 )
+#define DDERR_CANNOTATTACHSURFACE MAKE_DDHRESULT( 10 )
+#define DDERR_CANNOTDETACHSURFACE MAKE_DDHRESULT( 20 )
+#define DDERR_CURRENTLYNOTAVAIL MAKE_DDHRESULT( 40 )
+#define DDERR_EXCEPTION MAKE_DDHRESULT( 55 )
+#define DDERR_GENERIC E_FAIL
+#define DDERR_HEIGHTALIGN MAKE_DDHRESULT( 90 )
+#define DDERR_INCOMPATIBLEPRIMARY MAKE_DDHRESULT( 95 )
+#define DDERR_INVALIDCAPS MAKE_DDHRESULT( 100 )
+#define DDERR_INVALIDCLIPLIST MAKE_DDHRESULT( 110 )
+#define DDERR_INVALIDMODE MAKE_DDHRESULT( 120 )
+#define DDERR_INVALIDOBJECT MAKE_DDHRESULT( 130 )
+#define DDERR_INVALIDPARAMS E_INVALIDARG
+#define DDERR_INVALIDPIXELFORMAT MAKE_DDHRESULT( 145 )
+#define DDERR_INVALIDRECT MAKE_DDHRESULT( 150 )
+#define DDERR_LOCKEDSURFACES MAKE_DDHRESULT( 160 )
+#define DDERR_NO3D MAKE_DDHRESULT( 170 )
+#define DDERR_NOALPHAHW MAKE_DDHRESULT( 180 )
+#define DDERR_NOSTEREOHARDWARE MAKE_DDHRESULT( 181 )
+#define DDERR_NOSURFACELEFT MAKE_DDHRESULT( 182 )
+#define DDERR_NOCLIPLIST MAKE_DDHRESULT( 205 )
+#define DDERR_NOCOLORCONVHW MAKE_DDHRESULT( 210 )
+#define DDERR_NOCOOPERATIVELEVELSET MAKE_DDHRESULT( 212 )
+#define DDERR_NOCOLORKEY MAKE_DDHRESULT( 215 )
+#define DDERR_NOCOLORKEYHW MAKE_DDHRESULT( 220 )
+#define DDERR_NODIRECTDRAWSUPPORT MAKE_DDHRESULT( 222 )
+#define DDERR_NOEXCLUSIVEMODE MAKE_DDHRESULT( 225 )
+#define DDERR_NOFLIPHW MAKE_DDHRESULT( 230 )
+#define DDERR_NOGDI MAKE_DDHRESULT( 240 )
+#define DDERR_NOMIRRORHW MAKE_DDHRESULT( 250 )
+#define DDERR_NOTFOUND MAKE_DDHRESULT( 255 )
+#define DDERR_NOOVERLAYHW MAKE_DDHRESULT( 260 )
+#define DDERR_OVERLAPPINGRECTS MAKE_DDHRESULT( 270 )
+#define DDERR_NORASTEROPHW MAKE_DDHRESULT( 280 )
+#define DDERR_NOROTATIONHW MAKE_DDHRESULT( 290 )
+#define DDERR_NOSTRETCHHW MAKE_DDHRESULT( 310 )
+#define DDERR_NOT4BITCOLOR MAKE_DDHRESULT( 316 )
+#define DDERR_NOT4BITCOLORINDEX MAKE_DDHRESULT( 317 )
+#define DDERR_NOT8BITCOLOR MAKE_DDHRESULT( 320 )
+#define DDERR_NOTEXTUREHW MAKE_DDHRESULT( 330 )
+#define DDERR_NOVSYNCHW MAKE_DDHRESULT( 335 )
+#define DDERR_NOZBUFFERHW MAKE_DDHRESULT( 340 )
+#define DDERR_NOZOVERLAYHW MAKE_DDHRESULT( 350 )
+#define DDERR_OUTOFCAPS MAKE_DDHRESULT( 360 )
+#define DDERR_OUTOFMEMORY E_OUTOFMEMORY
+#define DDERR_OUTOFVIDEOMEMORY MAKE_DDHRESULT( 380 )
+#define DDERR_OVERLAYCANTCLIP MAKE_DDHRESULT( 382 )
+#define DDERR_OVERLAYCOLORKEYONLYONEACTIVE MAKE_DDHRESULT( 384 )
+#define DDERR_PALETTEBUSY MAKE_DDHRESULT( 387 )
+#define DDERR_COLORKEYNOTSET MAKE_DDHRESULT( 400 )
+#define DDERR_SURFACEALREADYATTACHED MAKE_DDHRESULT( 410 )
+#define DDERR_SURFACEALREADYDEPENDENT MAKE_DDHRESULT( 420 )
+#define DDERR_SURFACEBUSY MAKE_DDHRESULT( 430 )
+#define DDERR_CANTLOCKSURFACE MAKE_DDHRESULT( 435 )
+#define DDERR_SURFACEISOBSCURED MAKE_DDHRESULT( 440 )
+#define DDERR_SURFACELOST MAKE_DDHRESULT( 450 )
+#define DDERR_SURFACENOTATTACHED MAKE_DDHRESULT( 460 )
+#define DDERR_TOOBIGHEIGHT MAKE_DDHRESULT( 470 )
+#define DDERR_TOOBIGSIZE MAKE_DDHRESULT( 480 )
+#define DDERR_TOOBIGWIDTH MAKE_DDHRESULT( 490 )
+#define DDERR_UNSUPPORTED E_NOTIMPL
+#define DDERR_UNSUPPORTEDFORMAT MAKE_DDHRESULT( 510 )
+#define DDERR_UNSUPPORTEDMASK MAKE_DDHRESULT( 520 )
+#define DDERR_INVALIDSTREAM MAKE_DDHRESULT( 521 )
+#define DDERR_VERTICALBLANKINPROGRESS MAKE_DDHRESULT( 537 )
+#define DDERR_WASSTILLDRAWING MAKE_DDHRESULT( 540 )
+#define DDERR_DDSCAPSCOMPLEXREQUIRED MAKE_DDHRESULT( 542 )
+#define DDERR_XALIGN MAKE_DDHRESULT( 560 )
+#define DDERR_INVALIDDIRECTDRAWGUID MAKE_DDHRESULT( 561 )
+#define DDERR_DIRECTDRAWALREADYCREATED MAKE_DDHRESULT( 562 )
+#define DDERR_NODIRECTDRAWHW MAKE_DDHRESULT( 563 )
+#define DDERR_PRIMARYSURFACEALREADYEXISTS MAKE_DDHRESULT( 564 )
+#define DDERR_NOEMULATION MAKE_DDHRESULT( 565 )
+#define DDERR_REGIONTOOSMALL MAKE_DDHRESULT( 566 )
+#define DDERR_CLIPPERISUSINGHWND MAKE_DDHRESULT( 567 )
+#define DDERR_NOCLIPPERATTACHED MAKE_DDHRESULT( 568 )
+#define DDERR_NOHWND MAKE_DDHRESULT( 569 )
+#define DDERR_HWNDSUBCLASSED MAKE_DDHRESULT( 570 )
+#define DDERR_HWNDALREADYSET MAKE_DDHRESULT( 571 )
+#define DDERR_NOPALETTEATTACHED MAKE_DDHRESULT( 572 )
+#define DDERR_NOPALETTEHW MAKE_DDHRESULT( 573 )
+#define DDERR_BLTFASTCANTCLIP MAKE_DDHRESULT( 574 )
+#define DDERR_NOBLTHW MAKE_DDHRESULT( 575 )
+#define DDERR_NODDROPSHW MAKE_DDHRESULT( 576 )
+#define DDERR_OVERLAYNOTVISIBLE MAKE_DDHRESULT( 577 )
+#define DDERR_NOOVERLAYDEST MAKE_DDHRESULT( 578 )
+#define DDERR_INVALIDPOSITION MAKE_DDHRESULT( 579 )
+#define DDERR_NOTAOVERLAYSURFACE MAKE_DDHRESULT( 580 )
+#define DDERR_EXCLUSIVEMODEALREADYSET MAKE_DDHRESULT( 581 )
+#define DDERR_NOTFLIPPABLE MAKE_DDHRESULT( 582 )
+#define DDERR_CANTDUPLICATE MAKE_DDHRESULT( 583 )
+#define DDERR_NOTLOCKED MAKE_DDHRESULT( 584 )
+#define DDERR_CANTCREATEDC MAKE_DDHRESULT( 585 )
+#define DDERR_NODC MAKE_DDHRESULT( 586 )
+#define DDERR_WRONGMODE MAKE_DDHRESULT( 587 )
+#define DDERR_IMPLICITLYCREATED MAKE_DDHRESULT( 588 )
+#define DDERR_NOTPALETTIZED MAKE_DDHRESULT( 589 )
+#define DDERR_UNSUPPORTEDMODE MAKE_DDHRESULT( 590 )
+#define DDERR_NOMIPMAPHW MAKE_DDHRESULT( 591 )
+#define DDERR_INVALIDSURFACETYPE MAKE_DDHRESULT( 592 )
+#define DDERR_NOOPTIMIZEHW MAKE_DDHRESULT( 600 )
+#define DDERR_NOTLOADED MAKE_DDHRESULT( 601 )
+#define DDERR_NOFOCUSWINDOW MAKE_DDHRESULT( 602 )
+#define DDERR_NOTONMIPMAPSUBLEVEL MAKE_DDHRESULT( 603 )
+#define DDERR_DCALREADYCREATED MAKE_DDHRESULT( 620 )
+#define DDERR_NONONLOCALVIDMEM MAKE_DDHRESULT( 630 )
+#define DDERR_CANTPAGELOCK MAKE_DDHRESULT( 640 )
+#define DDERR_CANTPAGEUNLOCK MAKE_DDHRESULT( 660 )
+#define DDERR_NOTPAGELOCKED MAKE_DDHRESULT( 680 )
+#define DDERR_MOREDATA MAKE_DDHRESULT( 690 )
+#define DDERR_EXPIRED MAKE_DDHRESULT( 691 )
+#define DDERR_TESTFINISHED MAKE_DDHRESULT( 692 )
+#define DDERR_NEWMODE MAKE_DDHRESULT( 693 )
+#define DDERR_D3DNOTINITIALIZED MAKE_DDHRESULT( 694 )
+#define DDERR_VIDEONOTACTIVE MAKE_DDHRESULT( 695 )
+#define DDERR_NOMONITORINFORMATION MAKE_DDHRESULT( 696 )
+#define DDERR_NODRIVERSUPPORT MAKE_DDHRESULT( 697 )
+#define DDERR_DEVICEDOESNTOWNSURFACE MAKE_DDHRESULT( 699 )
+#define DDERR_NOTINITIALIZED CO_E_NOTINITIALIZED
+
+/* dwFlags for Blt* */
+#define DDBLT_ALPHADEST 0x00000001
+#define DDBLT_ALPHADESTCONSTOVERRIDE 0x00000002
+#define DDBLT_ALPHADESTNEG 0x00000004
+#define DDBLT_ALPHADESTSURFACEOVERRIDE 0x00000008
+#define DDBLT_ALPHAEDGEBLEND 0x00000010
+#define DDBLT_ALPHASRC 0x00000020
+#define DDBLT_ALPHASRCCONSTOVERRIDE 0x00000040
+#define DDBLT_ALPHASRCNEG 0x00000080
+#define DDBLT_ALPHASRCSURFACEOVERRIDE 0x00000100
+#define DDBLT_ASYNC 0x00000200
+#define DDBLT_COLORFILL 0x00000400
+#define DDBLT_DDFX 0x00000800
+#define DDBLT_DDROPS 0x00001000
+#define DDBLT_KEYDEST 0x00002000
+#define DDBLT_KEYDESTOVERRIDE 0x00004000
+#define DDBLT_KEYSRC 0x00008000
+#define DDBLT_KEYSRCOVERRIDE 0x00010000
+#define DDBLT_ROP 0x00020000
+#define DDBLT_ROTATIONANGLE 0x00040000
+#define DDBLT_ZBUFFER 0x00080000
+#define DDBLT_ZBUFFERDESTCONSTOVERRIDE 0x00100000
+#define DDBLT_ZBUFFERDESTOVERRIDE 0x00200000
+#define DDBLT_ZBUFFERSRCCONSTOVERRIDE 0x00400000
+#define DDBLT_ZBUFFERSRCOVERRIDE 0x00800000
+#define DDBLT_WAIT 0x01000000
+#define DDBLT_DEPTHFILL 0x02000000
+#define DDBLT_DONOTWAIT 0x08000000
+
+/* dwTrans for BltFast */
+#define DDBLTFAST_NOCOLORKEY 0x00000000
+#define DDBLTFAST_SRCCOLORKEY 0x00000001
+#define DDBLTFAST_DESTCOLORKEY 0x00000002
+#define DDBLTFAST_WAIT 0x00000010
+#define DDBLTFAST_DONOTWAIT 0x00000020
+
+/* dwFlags for Flip */
+#define DDFLIP_WAIT 0x00000001
+#define DDFLIP_EVEN 0x00000002 /* only valid for overlay */
+#define DDFLIP_ODD 0x00000004 /* only valid for overlay */
+#define DDFLIP_NOVSYNC 0x00000008
+#define DDFLIP_STEREO 0x00000010
+#define DDFLIP_DONOTWAIT 0x00000020
+#define DDFLIP_INTERVAL2 0x02000000
+#define DDFLIP_INTERVAL3 0x03000000
+#define DDFLIP_INTERVAL4 0x04000000
+
+
+/* dwFlags for GetBltStatus */
+#define DDGBS_CANBLT 0x00000001
+#define DDGBS_ISBLTDONE 0x00000002
+
+/* dwFlags for IDirectDrawSurface7::GetFlipStatus */
+#define DDGFS_CANFLIP 1L
+#define DDGFS_ISFLIPDONE 2L
+
+/* dwFlags for IDirectDrawSurface7::SetPrivateData */
+#define DDSPD_IUNKNOWNPOINTER 1L
+#define DDSPD_VOLATILE 2L
+
+/* DDSCAPS.dwCaps */
+/* reserved1, was 3d capable */
+#define DDSCAPS_RESERVED1 0x00000001
+/* surface contains alpha information */
+#define DDSCAPS_ALPHA 0x00000002
+/* this surface is a backbuffer */
+#define DDSCAPS_BACKBUFFER 0x00000004
+/* complex surface structure */
+#define DDSCAPS_COMPLEX 0x00000008
+/* part of surface flipping structure */
+#define DDSCAPS_FLIP 0x00000010
+/* this surface is the frontbuffer surface */
+#define DDSCAPS_FRONTBUFFER 0x00000020
+/* this is a plain offscreen surface */
+#define DDSCAPS_OFFSCREENPLAIN 0x00000040
+/* overlay */
+#define DDSCAPS_OVERLAY 0x00000080
+/* palette objects can be created and attached to us */
+#define DDSCAPS_PALETTE 0x00000100
+/* primary surface (the one the user looks at currently)(right eye)*/
+#define DDSCAPS_PRIMARYSURFACE 0x00000200
+/* primary surface for left eye */
+#define DDSCAPS_PRIMARYSURFACELEFT 0x00000400
+/* surface exists in systemmemory */
+#define DDSCAPS_SYSTEMMEMORY 0x00000800
+/* surface can be used as a texture */
+#define DDSCAPS_TEXTURE 0x00001000
+/* surface may be destination for 3d rendering */
+#define DDSCAPS_3DDEVICE 0x00002000
+/* surface exists in videomemory */
+#define DDSCAPS_VIDEOMEMORY 0x00004000
+/* surface changes immediately visible */
+#define DDSCAPS_VISIBLE 0x00008000
+/* write only surface */
+#define DDSCAPS_WRITEONLY 0x00010000
+/* zbuffer surface */
+#define DDSCAPS_ZBUFFER 0x00020000
+/* has its own DC */
+#define DDSCAPS_OWNDC 0x00040000
+/* surface should be able to receive live video */
+#define DDSCAPS_LIVEVIDEO 0x00080000
+/* should be able to have a hw codec decompress stuff into it */
+#define DDSCAPS_HWCODEC 0x00100000
+/* mode X (320x200 or 320x240) surface */
+#define DDSCAPS_MODEX 0x00200000
+/* one mipmap surface (1 level) */
+#define DDSCAPS_MIPMAP 0x00400000
+#define DDSCAPS_RESERVED2 0x00800000
+/* memory allocation delayed until Load() */
+#define DDSCAPS_ALLOCONLOAD 0x04000000
+/* Indicates that the surface will receive data from a video port */
+#define DDSCAPS_VIDEOPORT 0x08000000
+/* surface is in local videomemory */
+#define DDSCAPS_LOCALVIDMEM 0x10000000
+/* surface is in nonlocal videomemory */
+#define DDSCAPS_NONLOCALVIDMEM 0x20000000
+/* surface is a standard VGA mode surface (NOT ModeX) */
+#define DDSCAPS_STANDARDVGAMODE 0x40000000
+/* optimized? surface */
+#define DDSCAPS_OPTIMIZED 0x80000000
+
+typedef struct _DDSCAPS {
+ DWORD dwCaps; /* capabilities of surface wanted */
+} DDSCAPS,*LPDDSCAPS;
+
+/* DDSCAPS2.dwCaps2 */
+/* indicates the surface will receive data from a video port using
+ deinterlacing hardware. */
+#define DDSCAPS2_HARDWAREDEINTERLACE 0x00000002
+/* indicates the surface will be locked very frequently. */
+#define DDSCAPS2_HINTDYNAMIC 0x00000004
+/* indicates surface can be re-ordered or retiled on load() */
+#define DDSCAPS2_HINTSTATIC 0x00000008
+/* indicates surface to be managed by directdraw/direct3D */
+#define DDSCAPS2_TEXTUREMANAGE 0x00000010
+/* reserved bits */
+#define DDSCAPS2_RESERVED1 0x00000020
+#define DDSCAPS2_RESERVED2 0x00000040
+/* indicates surface will never be locked again */
+#define DDSCAPS2_OPAQUE 0x00000080
+/* set at CreateSurface() time to indicate antialiasing will be used */
+#define DDSCAPS2_HINTANTIALIASING 0x00000100
+/* set at CreateSurface() time to indicate cubic environment map */
+#define DDSCAPS2_CUBEMAP 0x00000200
+/* face flags for cube maps */
+#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
+#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
+#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
+#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
+#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
+#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
+/* specifies all faces of a cube for CreateSurface() */
+#define DDSCAPS2_CUBEMAP_ALLFACES ( DDSCAPS2_CUBEMAP_POSITIVEX |\
+ DDSCAPS2_CUBEMAP_NEGATIVEX |\
+ DDSCAPS2_CUBEMAP_POSITIVEY |\
+ DDSCAPS2_CUBEMAP_NEGATIVEY |\
+ DDSCAPS2_CUBEMAP_POSITIVEZ |\
+ DDSCAPS2_CUBEMAP_NEGATIVEZ )
+/* set for mipmap sublevels on DirectX7 and later. ignored by CreateSurface() */
+#define DDSCAPS2_MIPMAPSUBLEVEL 0x00010000
+/* indicates texture surface to be managed by Direct3D *only* */
+#define DDSCAPS2_D3DTEXTUREMANAGE 0x00020000
+/* indicates managed surface that can safely be lost */
+#define DDSCAPS2_DONOTPERSIST 0x00040000
+/* indicates surface is part of a stereo flipping chain */
+#define DDSCAPS2_STEREOSURFACELEFT 0x00080000
+
+typedef struct _DDSCAPS2 {
+ DWORD dwCaps; /* capabilities of surface wanted */
+ DWORD dwCaps2; /* additional capabilities */
+ DWORD dwCaps3; /* reserved capabilities */
+ DWORD dwCaps4; /* more reserved capabilities */
+} DDSCAPS2,*LPDDSCAPS2;
+
+#define DD_ROP_SPACE (256/32) /* space required to store ROP array */
+
+typedef struct _DDCAPS_DX7 /* DirectX 7 version of caps struct */
+{
+ DWORD dwSize; /* size of the DDDRIVERCAPS structure */
+ DWORD dwCaps; /* driver specific capabilities */
+ DWORD dwCaps2; /* more driver specific capabilities */
+ DWORD dwCKeyCaps; /* color key capabilities of the surface */
+ DWORD dwFXCaps; /* driver specific stretching and effects capabilities */
+ DWORD dwFXAlphaCaps; /* alpha driver specific capabilities */
+ DWORD dwPalCaps; /* palette capabilities */
+ DWORD dwSVCaps; /* stereo vision capabilities */
+ DWORD dwAlphaBltConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaBltPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaBltSurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlayConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaOverlayPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlaySurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwZBufferBitDepths; /* DDBD_8,16,24,32 */
+ DWORD dwVidMemTotal; /* total amount of video memory */
+ DWORD dwVidMemFree; /* amount of free video memory */
+ DWORD dwMaxVisibleOverlays; /* maximum number of visible overlays */
+ DWORD dwCurrVisibleOverlays; /* current number of visible overlays */
+ DWORD dwNumFourCCCodes; /* number of four cc codes */
+ DWORD dwAlignBoundarySrc; /* source rectangle alignment */
+ DWORD dwAlignSizeSrc; /* source rectangle byte size */
+ DWORD dwAlignBoundaryDest; /* dest rectangle alignment */
+ DWORD dwAlignSizeDest; /* dest rectangle byte size */
+ DWORD dwAlignStrideAlign; /* stride alignment */
+ DWORD dwRops[DD_ROP_SPACE]; /* ROPS supported */
+ DDSCAPS ddsOldCaps; /* old DDSCAPS - superseded for DirectX6+ */
+ DWORD dwMinOverlayStretch; /* minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxOverlayStretch; /* maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinLiveVideoStretch; /* minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxLiveVideoStretch; /* maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinHwCodecStretch; /* minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxHwCodecStretch; /* maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwReserved1;
+ DWORD dwReserved2;
+ DWORD dwReserved3;
+ DWORD dwSVBCaps; /* driver specific capabilities for System->Vmem blts */
+ DWORD dwSVBCKeyCaps; /* driver color key capabilities for System->Vmem blts */
+ DWORD dwSVBFXCaps; /* driver FX capabilities for System->Vmem blts */
+ DWORD dwSVBRops[DD_ROP_SPACE];/* ROPS supported for System->Vmem blts */
+ DWORD dwVSBCaps; /* driver specific capabilities for Vmem->System blts */
+ DWORD dwVSBCKeyCaps; /* driver color key capabilities for Vmem->System blts */
+ DWORD dwVSBFXCaps; /* driver FX capabilities for Vmem->System blts */
+ DWORD dwVSBRops[DD_ROP_SPACE];/* ROPS supported for Vmem->System blts */
+ DWORD dwSSBCaps; /* driver specific capabilities for System->System blts */
+ DWORD dwSSBCKeyCaps; /* driver color key capabilities for System->System blts */
+ DWORD dwSSBFXCaps; /* driver FX capabilities for System->System blts */
+ DWORD dwSSBRops[DD_ROP_SPACE];/* ROPS supported for System->System blts */
+ DWORD dwMaxVideoPorts; /* maximum number of usable video ports */
+ DWORD dwCurrVideoPorts; /* current number of video ports used */
+ DWORD dwSVBCaps2; /* more driver specific capabilities for System->Vmem blts */
+ DWORD dwNLVBCaps; /* driver specific capabilities for non-local->local vidmem blts */
+ DWORD dwNLVBCaps2; /* more driver specific capabilities non-local->local vidmem blts */
+ DWORD dwNLVBCKeyCaps; /* driver color key capabilities for non-local->local vidmem blts */
+ DWORD dwNLVBFXCaps; /* driver FX capabilities for non-local->local blts */
+ DWORD dwNLVBRops[DD_ROP_SPACE]; /* ROPS supported for non-local->local blts */
+ DDSCAPS2 ddsCaps; /* surface capabilities */
+} DDCAPS_DX7,*LPDDCAPS_DX7;
+
+typedef struct _DDCAPS_DX6 /* DirectX 6 version of caps struct */
+{
+ DWORD dwSize; /* size of the DDDRIVERCAPS structure */
+ DWORD dwCaps; /* driver specific capabilities */
+ DWORD dwCaps2; /* more driver specific capabilities */
+ DWORD dwCKeyCaps; /* color key capabilities of the surface */
+ DWORD dwFXCaps; /* driver specific stretching and effects capabilities */
+ DWORD dwFXAlphaCaps; /* alpha driver specific capabilities */
+ DWORD dwPalCaps; /* palette capabilities */
+ DWORD dwSVCaps; /* stereo vision capabilities */
+ DWORD dwAlphaBltConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaBltPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaBltSurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlayConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaOverlayPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlaySurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwZBufferBitDepths; /* DDBD_8,16,24,32 */
+ DWORD dwVidMemTotal; /* total amount of video memory */
+ DWORD dwVidMemFree; /* amount of free video memory */
+ DWORD dwMaxVisibleOverlays; /* maximum number of visible overlays */
+ DWORD dwCurrVisibleOverlays; /* current number of visible overlays */
+ DWORD dwNumFourCCCodes; /* number of four cc codes */
+ DWORD dwAlignBoundarySrc; /* source rectangle alignment */
+ DWORD dwAlignSizeSrc; /* source rectangle byte size */
+ DWORD dwAlignBoundaryDest; /* dest rectangle alignment */
+ DWORD dwAlignSizeDest; /* dest rectangle byte size */
+ DWORD dwAlignStrideAlign; /* stride alignment */
+ DWORD dwRops[DD_ROP_SPACE]; /* ROPS supported */
+ DDSCAPS ddsOldCaps; /* old DDSCAPS - superseded for DirectX6+ */
+ DWORD dwMinOverlayStretch; /* minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxOverlayStretch; /* maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinLiveVideoStretch; /* minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxLiveVideoStretch; /* maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinHwCodecStretch; /* minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxHwCodecStretch; /* maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwReserved1;
+ DWORD dwReserved2;
+ DWORD dwReserved3;
+ DWORD dwSVBCaps; /* driver specific capabilities for System->Vmem blts */
+ DWORD dwSVBCKeyCaps; /* driver color key capabilities for System->Vmem blts */
+ DWORD dwSVBFXCaps; /* driver FX capabilities for System->Vmem blts */
+ DWORD dwSVBRops[DD_ROP_SPACE];/* ROPS supported for System->Vmem blts */
+ DWORD dwVSBCaps; /* driver specific capabilities for Vmem->System blts */
+ DWORD dwVSBCKeyCaps; /* driver color key capabilities for Vmem->System blts */
+ DWORD dwVSBFXCaps; /* driver FX capabilities for Vmem->System blts */
+ DWORD dwVSBRops[DD_ROP_SPACE];/* ROPS supported for Vmem->System blts */
+ DWORD dwSSBCaps; /* driver specific capabilities for System->System blts */
+ DWORD dwSSBCKeyCaps; /* driver color key capabilities for System->System blts */
+ DWORD dwSSBFXCaps; /* driver FX capabilities for System->System blts */
+ DWORD dwSSBRops[DD_ROP_SPACE];/* ROPS supported for System->System blts */
+ DWORD dwMaxVideoPorts; /* maximum number of usable video ports */
+ DWORD dwCurrVideoPorts; /* current number of video ports used */
+ DWORD dwSVBCaps2; /* more driver specific capabilities for System->Vmem blts */
+ DWORD dwNLVBCaps; /* driver specific capabilities for non-local->local vidmem blts */
+ DWORD dwNLVBCaps2; /* more driver specific capabilities non-local->local vidmem blts */
+ DWORD dwNLVBCKeyCaps; /* driver color key capabilities for non-local->local vidmem blts */
+ DWORD dwNLVBFXCaps; /* driver FX capabilities for non-local->local blts */
+ DWORD dwNLVBRops[DD_ROP_SPACE]; /* ROPS supported for non-local->local blts */
+ /* and one new member for DirectX 6 */
+ DDSCAPS2 ddsCaps; /* surface capabilities */
+} DDCAPS_DX6,*LPDDCAPS_DX6;
+
+typedef struct _DDCAPS_DX5 /* DirectX5 version of caps struct */
+{
+ DWORD dwSize; /* size of the DDDRIVERCAPS structure */
+ DWORD dwCaps; /* driver specific capabilities */
+ DWORD dwCaps2; /* more driver specific capabilities */
+ DWORD dwCKeyCaps; /* color key capabilities of the surface */
+ DWORD dwFXCaps; /* driver specific stretching and effects capabilities */
+ DWORD dwFXAlphaCaps; /* alpha driver specific capabilities */
+ DWORD dwPalCaps; /* palette capabilities */
+ DWORD dwSVCaps; /* stereo vision capabilities */
+ DWORD dwAlphaBltConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaBltPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaBltSurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlayConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaOverlayPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlaySurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwZBufferBitDepths; /* DDBD_8,16,24,32 */
+ DWORD dwVidMemTotal; /* total amount of video memory */
+ DWORD dwVidMemFree; /* amount of free video memory */
+ DWORD dwMaxVisibleOverlays; /* maximum number of visible overlays */
+ DWORD dwCurrVisibleOverlays; /* current number of visible overlays */
+ DWORD dwNumFourCCCodes; /* number of four cc codes */
+ DWORD dwAlignBoundarySrc; /* source rectangle alignment */
+ DWORD dwAlignSizeSrc; /* source rectangle byte size */
+ DWORD dwAlignBoundaryDest; /* dest rectangle alignment */
+ DWORD dwAlignSizeDest; /* dest rectangle byte size */
+ DWORD dwAlignStrideAlign; /* stride alignment */
+ DWORD dwRops[DD_ROP_SPACE]; /* ROPS supported */
+ DDSCAPS ddsCaps; /* DDSCAPS structure has all the general capabilities */
+ DWORD dwMinOverlayStretch; /* minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxOverlayStretch; /* maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinLiveVideoStretch; /* minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxLiveVideoStretch; /* maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinHwCodecStretch; /* minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxHwCodecStretch; /* maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwReserved1;
+ DWORD dwReserved2;
+ DWORD dwReserved3;
+ DWORD dwSVBCaps; /* driver specific capabilities for System->Vmem blts */
+ DWORD dwSVBCKeyCaps; /* driver color key capabilities for System->Vmem blts */
+ DWORD dwSVBFXCaps; /* driver FX capabilities for System->Vmem blts */
+ DWORD dwSVBRops[DD_ROP_SPACE];/* ROPS supported for System->Vmem blts */
+ DWORD dwVSBCaps; /* driver specific capabilities for Vmem->System blts */
+ DWORD dwVSBCKeyCaps; /* driver color key capabilities for Vmem->System blts */
+ DWORD dwVSBFXCaps; /* driver FX capabilities for Vmem->System blts */
+ DWORD dwVSBRops[DD_ROP_SPACE];/* ROPS supported for Vmem->System blts */
+ DWORD dwSSBCaps; /* driver specific capabilities for System->System blts */
+ DWORD dwSSBCKeyCaps; /* driver color key capabilities for System->System blts */
+ DWORD dwSSBFXCaps; /* driver FX capabilities for System->System blts */
+ DWORD dwSSBRops[DD_ROP_SPACE];/* ROPS supported for System->System blts */
+ /* the following are the new DirectX 5 members */
+ DWORD dwMaxVideoPorts; /* maximum number of usable video ports */
+ DWORD dwCurrVideoPorts; /* current number of video ports used */
+ DWORD dwSVBCaps2; /* more driver specific capabilities for System->Vmem blts */
+ DWORD dwNLVBCaps; /* driver specific capabilities for non-local->local vidmem blts */
+ DWORD dwNLVBCaps2; /* more driver specific capabilities non-local->local vidmem blts */
+ DWORD dwNLVBCKeyCaps; /* driver color key capabilities for non-local->local vidmem blts */
+ DWORD dwNLVBFXCaps; /* driver FX capabilities for non-local->local blts */
+ DWORD dwNLVBRops[DD_ROP_SPACE]; /* ROPS supported for non-local->local blts */
+} DDCAPS_DX5,*LPDDCAPS_DX5;
+
+typedef struct _DDCAPS_DX3 /* DirectX3 version of caps struct */
+{
+ DWORD dwSize; /* size of the DDDRIVERCAPS structure */
+ DWORD dwCaps; /* driver specific capabilities */
+ DWORD dwCaps2; /* more driver specific capabilities */
+ DWORD dwCKeyCaps; /* color key capabilities of the surface */
+ DWORD dwFXCaps; /* driver specific stretching and effects capabilities */
+ DWORD dwFXAlphaCaps; /* alpha driver specific capabilities */
+ DWORD dwPalCaps; /* palette capabilities */
+ DWORD dwSVCaps; /* stereo vision capabilities */
+ DWORD dwAlphaBltConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaBltPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaBltSurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlayConstBitDepths; /* DDBD_2,4,8 */
+ DWORD dwAlphaOverlayPixelBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwAlphaOverlaySurfaceBitDepths; /* DDBD_1,2,4,8 */
+ DWORD dwZBufferBitDepths; /* DDBD_8,16,24,32 */
+ DWORD dwVidMemTotal; /* total amount of video memory */
+ DWORD dwVidMemFree; /* amount of free video memory */
+ DWORD dwMaxVisibleOverlays; /* maximum number of visible overlays */
+ DWORD dwCurrVisibleOverlays; /* current number of visible overlays */
+ DWORD dwNumFourCCCodes; /* number of four cc codes */
+ DWORD dwAlignBoundarySrc; /* source rectangle alignment */
+ DWORD dwAlignSizeSrc; /* source rectangle byte size */
+ DWORD dwAlignBoundaryDest; /* dest rectangle alignment */
+ DWORD dwAlignSizeDest; /* dest rectangle byte size */
+ DWORD dwAlignStrideAlign; /* stride alignment */
+ DWORD dwRops[DD_ROP_SPACE]; /* ROPS supported */
+ DDSCAPS ddsCaps; /* DDSCAPS structure has all the general capabilities */
+ DWORD dwMinOverlayStretch; /* minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxOverlayStretch; /* maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinLiveVideoStretch; /* minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxLiveVideoStretch; /* maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMinHwCodecStretch; /* minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwMaxHwCodecStretch; /* maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 */
+ DWORD dwReserved1;
+ DWORD dwReserved2;
+ DWORD dwReserved3;
+ DWORD dwSVBCaps; /* driver specific capabilities for System->Vmem blts */
+ DWORD dwSVBCKeyCaps; /* driver color key capabilities for System->Vmem blts */
+ DWORD dwSVBFXCaps; /* driver FX capabilities for System->Vmem blts */
+ DWORD dwSVBRops[DD_ROP_SPACE];/* ROPS supported for System->Vmem blts */
+ DWORD dwVSBCaps; /* driver specific capabilities for Vmem->System blts */
+ DWORD dwVSBCKeyCaps; /* driver color key capabilities for Vmem->System blts */
+ DWORD dwVSBFXCaps; /* driver FX capabilities for Vmem->System blts */
+ DWORD dwVSBRops[DD_ROP_SPACE];/* ROPS supported for Vmem->System blts */
+ DWORD dwSSBCaps; /* driver specific capabilities for System->System blts */
+ DWORD dwSSBCKeyCaps; /* driver color key capabilities for System->System blts */
+ DWORD dwSSBFXCaps; /* driver FX capabilities for System->System blts */
+ DWORD dwSSBRops[DD_ROP_SPACE];/* ROPS supported for System->System blts */
+ DWORD dwReserved4;
+ DWORD dwReserved5;
+ DWORD dwReserved6;
+} DDCAPS_DX3,*LPDDCAPS_DX3;
+
+/* set caps struct according to DIRECTDRAW_VERSION */
+
+#if DIRECTDRAW_VERSION <= 0x300
+typedef DDCAPS_DX3 DDCAPS;
+#elif DIRECTDRAW_VERSION <= 0x500
+typedef DDCAPS_DX5 DDCAPS;
+#elif DIRECTDRAW_VERSION <= 0x600
+typedef DDCAPS_DX6 DDCAPS;
+#else
+typedef DDCAPS_DX7 DDCAPS;
+#endif
+
+typedef DDCAPS *LPDDCAPS;
+
+/* DDCAPS.dwCaps */
+#define DDCAPS_3D 0x00000001
+#define DDCAPS_ALIGNBOUNDARYDEST 0x00000002
+#define DDCAPS_ALIGNSIZEDEST 0x00000004
+#define DDCAPS_ALIGNBOUNDARYSRC 0x00000008
+#define DDCAPS_ALIGNSIZESRC 0x00000010
+#define DDCAPS_ALIGNSTRIDE 0x00000020
+#define DDCAPS_BLT 0x00000040
+#define DDCAPS_BLTQUEUE 0x00000080
+#define DDCAPS_BLTFOURCC 0x00000100
+#define DDCAPS_BLTSTRETCH 0x00000200
+#define DDCAPS_GDI 0x00000400
+#define DDCAPS_OVERLAY 0x00000800
+#define DDCAPS_OVERLAYCANTCLIP 0x00001000
+#define DDCAPS_OVERLAYFOURCC 0x00002000
+#define DDCAPS_OVERLAYSTRETCH 0x00004000
+#define DDCAPS_PALETTE 0x00008000
+#define DDCAPS_PALETTEVSYNC 0x00010000
+#define DDCAPS_READSCANLINE 0x00020000
+#define DDCAPS_STEREOVIEW 0x00040000
+#define DDCAPS_VBI 0x00080000
+#define DDCAPS_ZBLTS 0x00100000
+#define DDCAPS_ZOVERLAYS 0x00200000
+#define DDCAPS_COLORKEY 0x00400000
+#define DDCAPS_ALPHA 0x00800000
+#define DDCAPS_COLORKEYHWASSIST 0x01000000
+#define DDCAPS_NOHARDWARE 0x02000000
+#define DDCAPS_BLTCOLORFILL 0x04000000
+#define DDCAPS_BANKSWITCHED 0x08000000
+#define DDCAPS_BLTDEPTHFILL 0x10000000
+#define DDCAPS_CANCLIP 0x20000000
+#define DDCAPS_CANCLIPSTRETCHED 0x40000000
+#define DDCAPS_CANBLTSYSMEM 0x80000000
+
+/* DDCAPS.dwCaps2 */
+#define DDCAPS2_CERTIFIED 0x00000001
+#define DDCAPS2_NO2DDURING3DSCENE 0x00000002
+#define DDCAPS2_VIDEOPORT 0x00000004
+#define DDCAPS2_AUTOFLIPOVERLAY 0x00000008
+#define DDCAPS2_CANBOBINTERLEAVED 0x00000010
+#define DDCAPS2_CANBOBNONINTERLEAVED 0x00000020
+#define DDCAPS2_COLORCONTROLOVERLAY 0x00000040
+#define DDCAPS2_COLORCONTROLPRIMARY 0x00000080
+#define DDCAPS2_CANDROPZ16BIT 0x00000100
+#define DDCAPS2_NONLOCALVIDMEM 0x00000200
+#define DDCAPS2_NONLOCALVIDMEMCAPS 0x00000400
+#define DDCAPS2_NOPAGELOCKREQUIRED 0x00000800
+#define DDCAPS2_WIDESURFACES 0x00001000
+#define DDCAPS2_CANFLIPODDEVEN 0x00002000
+#define DDCAPS2_CANBOBHARDWARE 0x00004000
+#define DDCAPS2_COPYFOURCC 0x00008000
+#define DDCAPS2_PRIMARYGAMMA 0x00020000
+#define DDCAPS2_CANRENDERWINDOWED 0x00080000
+#define DDCAPS2_CANCALIBRATEGAMMA 0x00100000
+#define DDCAPS2_FLIPINTERVAL 0x00200000
+#define DDCAPS2_FLIPNOVSYNC 0x00400000
+#define DDCAPS2_CANMANAGETEXTURE 0x00800000
+#define DDCAPS2_TEXMANINNONLOCALVIDMEM 0x01000000
+#define DDCAPS2_STEREO 0x02000000
+#define DDCAPS2_SYSTONONLOCAL_AS_SYSTOLOCAL 0x04000000
+
+
+/* Set/Get Colour Key Flags */
+#define DDCKEY_COLORSPACE 0x00000001 /* Struct is single colour space */
+#define DDCKEY_DESTBLT 0x00000002 /* To be used as dest for blt */
+#define DDCKEY_DESTOVERLAY 0x00000004 /* To be used as dest for CK overlays */
+#define DDCKEY_SRCBLT 0x00000008 /* To be used as src for blt */
+#define DDCKEY_SRCOVERLAY 0x00000010 /* To be used as src for CK overlays */
+
+typedef struct _DDCOLORKEY
+{
+ DWORD dwColorSpaceLowValue;/* low boundary of color space that is to
+ * be treated as Color Key, inclusive
+ */
+ DWORD dwColorSpaceHighValue;/* high boundary of color space that is
+ * to be treated as Color Key, inclusive
+ */
+} DDCOLORKEY,*LPDDCOLORKEY;
+
+/* ddCKEYCAPS bits */
+#define DDCKEYCAPS_DESTBLT 0x00000001
+#define DDCKEYCAPS_DESTBLTCLRSPACE 0x00000002
+#define DDCKEYCAPS_DESTBLTCLRSPACEYUV 0x00000004
+#define DDCKEYCAPS_DESTBLTYUV 0x00000008
+#define DDCKEYCAPS_DESTOVERLAY 0x00000010
+#define DDCKEYCAPS_DESTOVERLAYCLRSPACE 0x00000020
+#define DDCKEYCAPS_DESTOVERLAYCLRSPACEYUV 0x00000040
+#define DDCKEYCAPS_DESTOVERLAYONEACTIVE 0x00000080
+#define DDCKEYCAPS_DESTOVERLAYYUV 0x00000100
+#define DDCKEYCAPS_SRCBLT 0x00000200
+#define DDCKEYCAPS_SRCBLTCLRSPACE 0x00000400
+#define DDCKEYCAPS_SRCBLTCLRSPACEYUV 0x00000800
+#define DDCKEYCAPS_SRCBLTYUV 0x00001000
+#define DDCKEYCAPS_SRCOVERLAY 0x00002000
+#define DDCKEYCAPS_SRCOVERLAYCLRSPACE 0x00004000
+#define DDCKEYCAPS_SRCOVERLAYCLRSPACEYUV 0x00008000
+#define DDCKEYCAPS_SRCOVERLAYONEACTIVE 0x00010000
+#define DDCKEYCAPS_SRCOVERLAYYUV 0x00020000
+#define DDCKEYCAPS_NOCOSTOVERLAY 0x00040000
+
+typedef struct _DDPIXELFORMAT {
+ DWORD dwSize; /* 0: size of structure */
+ DWORD dwFlags; /* 4: pixel format flags */
+ DWORD dwFourCC; /* 8: (FOURCC code) */
+ union {
+ DWORD dwRGBBitCount; /* C: how many bits per pixel */
+ DWORD dwYUVBitCount; /* C: how many bits per pixel */
+ DWORD dwZBufferBitDepth; /* C: how many bits for z buffers */
+ DWORD dwAlphaBitDepth; /* C: how many bits for alpha channels*/
+ DWORD dwLuminanceBitCount;
+ DWORD dwBumpBitCount;
+ } DUMMYUNIONNAME1;
+ union {
+ DWORD dwRBitMask; /* 10: mask for red bit*/
+ DWORD dwYBitMask; /* 10: mask for Y bits*/
+ DWORD dwStencilBitDepth;
+ DWORD dwLuminanceBitMask;
+ DWORD dwBumpDuBitMask;
+ } DUMMYUNIONNAME2;
+ union {
+ DWORD dwGBitMask; /* 14: mask for green bits*/
+ DWORD dwUBitMask; /* 14: mask for U bits*/
+ DWORD dwZBitMask;
+ DWORD dwBumpDvBitMask;
+ } DUMMYUNIONNAME3;
+ union {
+ DWORD dwBBitMask; /* 18: mask for blue bits*/
+ DWORD dwVBitMask; /* 18: mask for V bits*/
+ DWORD dwStencilBitMask;
+ DWORD dwBumpLuminanceBitMask;
+ } DUMMYUNIONNAME4;
+ union {
+ DWORD dwRGBAlphaBitMask; /* 1C: mask for alpha channel */
+ DWORD dwYUVAlphaBitMask; /* 1C: mask for alpha channel */
+ DWORD dwLuminanceAlphaBitMask;
+ DWORD dwRGBZBitMask; /* 1C: mask for Z channel */
+ DWORD dwYUVZBitMask; /* 1C: mask for Z channel */
+ } DUMMYUNIONNAME5;
+ /* 20: next structure */
+} DDPIXELFORMAT,*LPDDPIXELFORMAT;
+
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
+ ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))
+
+/* DDCAPS.dwFXCaps */
+#define DDFXCAPS_BLTALPHA 0x00000001
+#define DDFXCAPS_OVERLAYALPHA 0x00000004
+#define DDFXCAPS_BLTARITHSTRETCHYN 0x00000010
+#define DDFXCAPS_BLTARITHSTRETCHY 0x00000020
+#define DDFXCAPS_BLTMIRRORLEFTRIGHT 0x00000040
+#define DDFXCAPS_BLTMIRRORUPDOWN 0x00000080
+#define DDFXCAPS_BLTROTATION 0x00000100
+#define DDFXCAPS_BLTROTATION90 0x00000200
+#define DDFXCAPS_BLTSHRINKX 0x00000400
+#define DDFXCAPS_BLTSHRINKXN 0x00000800
+#define DDFXCAPS_BLTSHRINKY 0x00001000
+#define DDFXCAPS_BLTSHRINKYN 0x00002000
+#define DDFXCAPS_BLTSTRETCHX 0x00004000
+#define DDFXCAPS_BLTSTRETCHXN 0x00008000
+#define DDFXCAPS_BLTSTRETCHY 0x00010000
+#define DDFXCAPS_BLTSTRETCHYN 0x00020000
+#define DDFXCAPS_OVERLAYARITHSTRETCHY 0x00040000
+#define DDFXCAPS_OVERLAYARITHSTRETCHYN 0x00000008
+#define DDFXCAPS_OVERLAYSHRINKX 0x00080000
+#define DDFXCAPS_OVERLAYSHRINKXN 0x00100000
+#define DDFXCAPS_OVERLAYSHRINKY 0x00200000
+#define DDFXCAPS_OVERLAYSHRINKYN 0x00400000
+#define DDFXCAPS_OVERLAYSTRETCHX 0x00800000
+#define DDFXCAPS_OVERLAYSTRETCHXN 0x01000000
+#define DDFXCAPS_OVERLAYSTRETCHY 0x02000000
+#define DDFXCAPS_OVERLAYSTRETCHYN 0x04000000
+#define DDFXCAPS_OVERLAYMIRRORLEFTRIGHT 0x08000000
+#define DDFXCAPS_OVERLAYMIRRORUPDOWN 0x10000000
+
+#define DDFXCAPS_OVERLAYFILTER DDFXCAPS_OVERLAYARITHSTRETCHY
+
+/* DDCAPS.dwFXAlphaCaps */
+#define DDFXALPHACAPS_BLTALPHAEDGEBLEND 0x00000001
+#define DDFXALPHACAPS_BLTALPHAPIXELS 0x00000002
+#define DDFXALPHACAPS_BLTALPHAPIXELSNEG 0x00000004
+#define DDFXALPHACAPS_BLTALPHASURFACES 0x00000008
+#define DDFXALPHACAPS_BLTALPHASURFACESNEG 0x00000010
+#define DDFXALPHACAPS_OVERLAYALPHAEDGEBLEND 0x00000020
+#define DDFXALPHACAPS_OVERLAYALPHAPIXELS 0x00000040
+#define DDFXALPHACAPS_OVERLAYALPHAPIXELSNEG 0x00000080
+#define DDFXALPHACAPS_OVERLAYALPHASURFACES 0x00000100
+#define DDFXALPHACAPS_OVERLAYALPHASURFACESNEG 0x00000200
+
+/* DDCAPS.dwPalCaps */
+#define DDPCAPS_4BIT 0x00000001
+#define DDPCAPS_8BITENTRIES 0x00000002
+#define DDPCAPS_8BIT 0x00000004
+#define DDPCAPS_INITIALIZE 0x00000008
+#define DDPCAPS_PRIMARYSURFACE 0x00000010
+#define DDPCAPS_PRIMARYSURFACELEFT 0x00000020
+#define DDPCAPS_ALLOW256 0x00000040
+#define DDPCAPS_VSYNC 0x00000080
+#define DDPCAPS_1BIT 0x00000100
+#define DDPCAPS_2BIT 0x00000200
+#define DDPCAPS_ALPHA 0x00000400
+
+/* DDCAPS.dwSVCaps */
+/* the first 4 of these are now obsolete */
+#if DIRECTDRAW_VERSION >= 0x700 /* FIXME: I'm not sure when this switch occurred */
+#define DDSVCAPS_RESERVED1 0x00000001
+#define DDSVCAPS_RESERVED2 0x00000002
+#define DDSVCAPS_RESERVED3 0x00000004
+#define DDSVCAPS_RESERVED4 0x00000008
+#else
+#define DDSVCAPS_ENIGMA 0x00000001
+#define DDSVCAPS_FLICKER 0x00000002
+#define DDSVCAPS_REDBLUE 0x00000004
+#define DDSVCAPS_SPLIT 0x00000008
+#endif
+#define DDSVCAPS_STEREOSEQUENTIAL 0x00000010
+
+/* BitDepths */
+#define DDBD_1 0x00004000
+#define DDBD_2 0x00002000
+#define DDBD_4 0x00001000
+#define DDBD_8 0x00000800
+#define DDBD_16 0x00000400
+#define DDBD_24 0x00000200
+#define DDBD_32 0x00000100
+
+/* DDOVERLAYFX.dwDDFX */
+#define DDOVERFX_ARITHSTRETCHY 0x00000001
+#define DDOVERFX_MIRRORLEFTRIGHT 0x00000002
+#define DDOVERFX_MIRRORUPDOWN 0x00000004
+
+/* UpdateOverlay flags */
+#define DDOVER_ALPHADEST 0x00000001
+#define DDOVER_ALPHADESTCONSTOVERRIDE 0x00000002
+#define DDOVER_ALPHADESTNEG 0x00000004
+#define DDOVER_ALPHADESTSURFACEOVERRIDE 0x00000008
+#define DDOVER_ALPHAEDGEBLEND 0x00000010
+#define DDOVER_ALPHASRC 0x00000020
+#define DDOVER_ALPHASRCCONSTOVERRIDE 0x00000040
+#define DDOVER_ALPHASRCNEG 0x00000080
+#define DDOVER_ALPHASRCSURFACEOVERRIDE 0x00000100
+#define DDOVER_HIDE 0x00000200
+#define DDOVER_KEYDEST 0x00000400
+#define DDOVER_KEYDESTOVERRIDE 0x00000800
+#define DDOVER_KEYSRC 0x00001000
+#define DDOVER_KEYSRCOVERRIDE 0x00002000
+#define DDOVER_SHOW 0x00004000
+#define DDOVER_ADDDIRTYRECT 0x00008000
+#define DDOVER_REFRESHDIRTYRECTS 0x00010000
+#define DDOVER_REFRESHALL 0x00020000
+#define DDOVER_DDFX 0x00080000
+#define DDOVER_AUTOFLIP 0x00100000
+#define DDOVER_BOB 0x00200000
+#define DDOVER_OVERRIDEBOBWEAVE 0x00400000
+#define DDOVER_INTERLEAVED 0x00800000
+
+/* DDCOLORKEY.dwFlags */
+#define DDPF_ALPHAPIXELS 0x00000001
+#define DDPF_ALPHA 0x00000002
+#define DDPF_FOURCC 0x00000004
+#define DDPF_PALETTEINDEXED4 0x00000008
+#define DDPF_PALETTEINDEXEDTO8 0x00000010
+#define DDPF_PALETTEINDEXED8 0x00000020
+#define DDPF_RGB 0x00000040
+#define DDPF_COMPRESSED 0x00000080
+#define DDPF_RGBTOYUV 0x00000100
+#define DDPF_YUV 0x00000200
+#define DDPF_ZBUFFER 0x00000400
+#define DDPF_PALETTEINDEXED1 0x00000800
+#define DDPF_PALETTEINDEXED2 0x00001000
+#define DDPF_ZPIXELS 0x00002000
+#define DDPF_STENCILBUFFER 0x00004000
+#define DDPF_ALPHAPREMULT 0x00008000
+#define DDPF_LUMINANCE 0x00020000
+#define DDPF_BUMPLUMINANCE 0x00040000
+#define DDPF_BUMPDUDV 0x00080000
+
+/* SetCooperativeLevel dwFlags */
+#define DDSCL_FULLSCREEN 0x00000001
+#define DDSCL_ALLOWREBOOT 0x00000002
+#define DDSCL_NOWINDOWCHANGES 0x00000004
+#define DDSCL_NORMAL 0x00000008
+#define DDSCL_EXCLUSIVE 0x00000010
+#define DDSCL_ALLOWMODEX 0x00000040
+#define DDSCL_SETFOCUSWINDOW 0x00000080
+#define DDSCL_SETDEVICEWINDOW 0x00000100
+#define DDSCL_CREATEDEVICEWINDOW 0x00000200
+#define DDSCL_MULTITHREADED 0x00000400
+#define DDSCL_FPUSETUP 0x00000800
+#define DDSCL_FPUPRESERVE 0x00001000
+
+
+/* DDSURFACEDESC.dwFlags */
+#define DDSD_CAPS 0x00000001
+#define DDSD_HEIGHT 0x00000002
+#define DDSD_WIDTH 0x00000004
+#define DDSD_PITCH 0x00000008
+#define DDSD_BACKBUFFERCOUNT 0x00000020
+#define DDSD_ZBUFFERBITDEPTH 0x00000040
+#define DDSD_ALPHABITDEPTH 0x00000080
+#define DDSD_LPSURFACE 0x00000800
+#define DDSD_PIXELFORMAT 0x00001000
+#define DDSD_CKDESTOVERLAY 0x00002000
+#define DDSD_CKDESTBLT 0x00004000
+#define DDSD_CKSRCOVERLAY 0x00008000
+#define DDSD_CKSRCBLT 0x00010000
+#define DDSD_MIPMAPCOUNT 0x00020000
+#define DDSD_REFRESHRATE 0x00040000
+#define DDSD_LINEARSIZE 0x00080000
+#define DDSD_TEXTURESTAGE 0x00100000
+#define DDSD_FVF 0x00200000
+#define DDSD_SRCVBHANDLE 0x00400000
+#define DDSD_ALL 0x007ff9ee
+
+/* EnumSurfaces flags */
+#define DDENUMSURFACES_ALL 0x00000001
+#define DDENUMSURFACES_MATCH 0x00000002
+#define DDENUMSURFACES_NOMATCH 0x00000004
+#define DDENUMSURFACES_CANBECREATED 0x00000008
+#define DDENUMSURFACES_DOESEXIST 0x00000010
+
+/* SetDisplayMode flags */
+#define DDSDM_STANDARDVGAMODE 0x00000001
+
+/* EnumDisplayModes flags */
+#define DDEDM_REFRESHRATES 0x00000001
+#define DDEDM_STANDARDVGAMODES 0x00000002
+
+/* WaitForVerticalDisplay flags */
+
+#define DDWAITVB_BLOCKBEGIN 0x00000001
+#define DDWAITVB_BLOCKBEGINEVENT 0x00000002
+#define DDWAITVB_BLOCKEND 0x00000004
+
+typedef struct _DDSURFACEDESC
+{
+ DWORD dwSize; /* 0: size of the DDSURFACEDESC structure*/
+ DWORD dwFlags; /* 4: determines what fields are valid*/
+ DWORD dwHeight; /* 8: height of surface to be created*/
+ DWORD dwWidth; /* C: width of input surface*/
+ union {
+ LONG lPitch; /* 10: distance to start of next line (return value only)*/
+ DWORD dwLinearSize;
+ } DUMMYUNIONNAME1;
+ DWORD dwBackBufferCount;/* 14: number of back buffers requested*/
+ union {
+ DWORD dwMipMapCount;/* 18:number of mip-map levels requested*/
+ DWORD dwZBufferBitDepth;/*18: depth of Z buffer requested*/
+ DWORD dwRefreshRate;/* 18:refresh rate (used when display mode is described)*/
+ } DUMMYUNIONNAME2;
+ DWORD dwAlphaBitDepth;/* 1C:depth of alpha buffer requested*/
+ DWORD dwReserved; /* 20:reserved*/
+ LPVOID lpSurface; /* 24:pointer to the associated surface memory*/
+ DDCOLORKEY ddckCKDestOverlay;/* 28: CK for dest overlay use*/
+ DDCOLORKEY ddckCKDestBlt; /* 30: CK for destination blt use*/
+ DDCOLORKEY ddckCKSrcOverlay;/* 38: CK for source overlay use*/
+ DDCOLORKEY ddckCKSrcBlt; /* 40: CK for source blt use*/
+ DDPIXELFORMAT ddpfPixelFormat;/* 48: pixel format description of the surface*/
+ DDSCAPS ddsCaps; /* 68: direct draw surface caps */
+} DDSURFACEDESC,*LPDDSURFACEDESC;
+
+typedef struct _DDSURFACEDESC2
+{
+ DWORD dwSize; /* 0: size of the DDSURFACEDESC2 structure*/
+ DWORD dwFlags; /* 4: determines what fields are valid*/
+ DWORD dwHeight; /* 8: height of surface to be created*/
+ DWORD dwWidth; /* C: width of input surface*/
+ union {
+ LONG lPitch; /*10: distance to start of next line (return value only)*/
+ DWORD dwLinearSize; /*10: formless late-allocated optimized surface size */
+ } DUMMYUNIONNAME1;
+ DWORD dwBackBufferCount;/* 14: number of back buffers requested*/
+ union {
+ DWORD dwMipMapCount;/* 18:number of mip-map levels requested*/
+ DWORD dwRefreshRate;/* 18:refresh rate (used when display mode is described)*/
+ DWORD dwSrcVBHandle;/* 18:source used in VB::Optimize */
+ } DUMMYUNIONNAME2;
+ DWORD dwAlphaBitDepth;/* 1C:depth of alpha buffer requested*/
+ DWORD dwReserved; /* 20:reserved*/
+ LPVOID lpSurface; /* 24:pointer to the associated surface memory*/
+ union {
+ DDCOLORKEY ddckCKDestOverlay; /* 28: CK for dest overlay use*/
+ DWORD dwEmptyFaceColor; /* 28: color for empty cubemap faces */
+ } DUMMYUNIONNAME3;
+ DDCOLORKEY ddckCKDestBlt; /* 30: CK for destination blt use*/
+ DDCOLORKEY ddckCKSrcOverlay;/* 38: CK for source overlay use*/
+ DDCOLORKEY ddckCKSrcBlt; /* 40: CK for source blt use*/
+
+ union {
+ DDPIXELFORMAT ddpfPixelFormat;/* 48: pixel format description of the surface*/
+ DWORD dwFVF; /* 48: vertex format description of vertex buffers */
+ } DUMMYUNIONNAME4;
+ DDSCAPS2 ddsCaps; /* 68: DDraw surface caps */
+ DWORD dwTextureStage; /* 78: stage in multitexture cascade */
+} DDSURFACEDESC2,*LPDDSURFACEDESC2;
+
+/* DDCOLORCONTROL.dwFlags */
+#define DDCOLOR_BRIGHTNESS 0x00000001
+#define DDCOLOR_CONTRAST 0x00000002
+#define DDCOLOR_HUE 0x00000004
+#define DDCOLOR_SATURATION 0x00000008
+#define DDCOLOR_SHARPNESS 0x00000010
+#define DDCOLOR_GAMMA 0x00000020
+#define DDCOLOR_COLORENABLE 0x00000040
+
+typedef struct {
+ DWORD dwSize;
+ DWORD dwFlags;
+ LONG lBrightness;
+ LONG lContrast;
+ LONG lHue;
+ LONG lSaturation;
+ LONG lSharpness;
+ LONG lGamma;
+ LONG lColorEnable;
+ DWORD dwReserved1;
+} DDCOLORCONTROL,*LPDDCOLORCONTROL;
+
+typedef struct {
+ WORD red[256];
+ WORD green[256];
+ WORD blue[256];
+} DDGAMMARAMP,*LPDDGAMMARAMP;
+
+typedef BOOL (CALLBACK *LPDDENUMCALLBACKA)(GUID *, LPSTR, LPSTR, LPVOID);
+typedef BOOL (CALLBACK *LPDDENUMCALLBACKW)(GUID *, LPWSTR, LPWSTR, LPVOID);
+
+typedef HRESULT (CALLBACK *LPDDENUMMODESCALLBACK)(LPDDSURFACEDESC, LPVOID);
+typedef HRESULT (CALLBACK *LPDDENUMMODESCALLBACK2)(LPDDSURFACEDESC2, LPVOID);
+typedef HRESULT (CALLBACK *LPDDENUMSURFACESCALLBACK)(LPDIRECTDRAWSURFACE, LPDDSURFACEDESC, LPVOID);
+typedef HRESULT (CALLBACK *LPDDENUMSURFACESCALLBACK2)(LPDIRECTDRAWSURFACE4, LPDDSURFACEDESC2, LPVOID);
+typedef HRESULT (CALLBACK *LPDDENUMSURFACESCALLBACK7)(LPDIRECTDRAWSURFACE7, LPDDSURFACEDESC2, LPVOID);
+
+typedef BOOL (CALLBACK *LPDDENUMCALLBACKEXA)(GUID *, LPSTR, LPSTR, LPVOID, HMONITOR);
+typedef BOOL (CALLBACK *LPDDENUMCALLBACKEXW)(GUID *, LPWSTR, LPWSTR, LPVOID, HMONITOR);
+
+HRESULT WINAPI DirectDrawEnumerateA(LPDDENUMCALLBACKA,LPVOID);
+HRESULT WINAPI DirectDrawEnumerateW(LPDDENUMCALLBACKW,LPVOID);
+#define DirectDrawEnumerate WINELIB_NAME_AW(DirectDrawEnumerate)
+
+HRESULT WINAPI DirectDrawEnumerateExA( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
+HRESULT WINAPI DirectDrawEnumerateExW( LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags);
+#define DirectDrawEnumerateEx WINELIB_NAME_AW(DirectDrawEnumerateEx)
+
+typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEXA)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
+typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEXW)( LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags);
+
+/* flags for DirectDrawEnumerateEx */
+#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001
+#define DDENUM_DETACHEDSECONDARYDEVICES 0x00000002
+#define DDENUM_NONDISPLAYDEVICES 0x00000004
+
+/* flags for DirectDrawCreate or IDirectDraw::Initialize */
+#define DDCREATE_HARDWAREONLY 1L
+#define DDCREATE_EMULATIONONLY 2L
+
+typedef struct _DDBLTFX
+{
+ DWORD dwSize; /* size of structure */
+ DWORD dwDDFX; /* FX operations */
+ DWORD dwROP; /* Win32 raster operations */
+ DWORD dwDDROP; /* Raster operations new for DirectDraw */
+ DWORD dwRotationAngle; /* Rotation angle for blt */
+ DWORD dwZBufferOpCode; /* ZBuffer compares */
+ DWORD dwZBufferLow; /* Low limit of Z buffer */
+ DWORD dwZBufferHigh; /* High limit of Z buffer */
+ DWORD dwZBufferBaseDest; /* Destination base value */
+ DWORD dwZDestConstBitDepth; /* Bit depth used to specify Z constant for destination */
+ union
+ {
+ DWORD dwZDestConst; /* Constant to use as Z buffer for dest */
+ LPDIRECTDRAWSURFACE lpDDSZBufferDest; /* Surface to use as Z buffer for dest */
+ } DUMMYUNIONNAME1;
+ DWORD dwZSrcConstBitDepth; /* Bit depth used to specify Z constant for source */
+ union
+ {
+ DWORD dwZSrcConst; /* Constant to use as Z buffer for src */
+ LPDIRECTDRAWSURFACE lpDDSZBufferSrc; /* Surface to use as Z buffer for src */
+ } DUMMYUNIONNAME2;
+ DWORD dwAlphaEdgeBlendBitDepth; /* Bit depth used to specify constant for alpha edge blend */
+ DWORD dwAlphaEdgeBlend; /* Alpha for edge blending */
+ DWORD dwReserved;
+ DWORD dwAlphaDestConstBitDepth; /* Bit depth used to specify alpha constant for destination */
+ union
+ {
+ DWORD dwAlphaDestConst; /* Constant to use as Alpha Channel */
+ LPDIRECTDRAWSURFACE lpDDSAlphaDest; /* Surface to use as Alpha Channel */
+ } DUMMYUNIONNAME3;
+ DWORD dwAlphaSrcConstBitDepth; /* Bit depth used to specify alpha constant for source */
+ union
+ {
+ DWORD dwAlphaSrcConst; /* Constant to use as Alpha Channel */
+ LPDIRECTDRAWSURFACE lpDDSAlphaSrc; /* Surface to use as Alpha Channel */
+ } DUMMYUNIONNAME4;
+ union
+ {
+ DWORD dwFillColor; /* color in RGB or Palettized */
+ DWORD dwFillDepth; /* depth value for z-buffer */
+ DWORD dwFillPixel; /* pixel val for RGBA or RGBZ */
+ LPDIRECTDRAWSURFACE lpDDSPattern; /* Surface to use as pattern */
+ } DUMMYUNIONNAME5;
+ DDCOLORKEY ddckDestColorkey; /* DestColorkey override */
+ DDCOLORKEY ddckSrcColorkey; /* SrcColorkey override */
+} DDBLTFX,*LPDDBLTFX;
+
+/* dwDDFX */
+/* arithmetic stretching along y axis */
+#define DDBLTFX_ARITHSTRETCHY 0x00000001
+/* mirror on y axis */
+#define DDBLTFX_MIRRORLEFTRIGHT 0x00000002
+/* mirror on x axis */
+#define DDBLTFX_MIRRORUPDOWN 0x00000004
+/* do not tear */
+#define DDBLTFX_NOTEARING 0x00000008
+/* 180 degrees clockwise rotation */
+#define DDBLTFX_ROTATE180 0x00000010
+/* 270 degrees clockwise rotation */
+#define DDBLTFX_ROTATE270 0x00000020
+/* 90 degrees clockwise rotation */
+#define DDBLTFX_ROTATE90 0x00000040
+/* dwZBufferLow and dwZBufferHigh specify limits to the copied Z values */
+#define DDBLTFX_ZBUFFERRANGE 0x00000080
+/* add dwZBufferBaseDest to every source z value before compare */
+#define DDBLTFX_ZBUFFERBASEDEST 0x00000100
+
+typedef struct _DDOVERLAYFX
+{
+ DWORD dwSize; /* size of structure */
+ DWORD dwAlphaEdgeBlendBitDepth; /* Bit depth used to specify constant for alpha edge blend */
+ DWORD dwAlphaEdgeBlend; /* Constant to use as alpha for edge blend */
+ DWORD dwReserved;
+ DWORD dwAlphaDestConstBitDepth; /* Bit depth used to specify alpha constant for destination */
+ union
+ {
+ DWORD dwAlphaDestConst; /* Constant to use as alpha channel for dest */
+ LPDIRECTDRAWSURFACE lpDDSAlphaDest; /* Surface to use as alpha channel for dest */
+ } DUMMYUNIONNAME1;
+ DWORD dwAlphaSrcConstBitDepth; /* Bit depth used to specify alpha constant for source */
+ union
+ {
+ DWORD dwAlphaSrcConst; /* Constant to use as alpha channel for src */
+ LPDIRECTDRAWSURFACE lpDDSAlphaSrc; /* Surface to use as alpha channel for src */
+ } DUMMYUNIONNAME2;
+ DDCOLORKEY dckDestColorkey; /* DestColorkey override */
+ DDCOLORKEY dckSrcColorkey; /* DestColorkey override */
+ DWORD dwDDFX; /* Overlay FX */
+ DWORD dwFlags; /* flags */
+} DDOVERLAYFX,*LPDDOVERLAYFX;
+
+typedef struct _DDBLTBATCH
+{
+ LPRECT lprDest;
+ LPDIRECTDRAWSURFACE lpDDSSrc;
+ LPRECT lprSrc;
+ DWORD dwFlags;
+ LPDDBLTFX lpDDBltFx;
+} DDBLTBATCH,*LPDDBLTBATCH;
+
+#define MAX_DDDEVICEID_STRING 512
+
+#define DDGDI_GETHOSTIDENTIFIER 1
+
+typedef struct tagDDDEVICEIDENTIFIER {
+ char szDriver[MAX_DDDEVICEID_STRING];
+ char szDescription[MAX_DDDEVICEID_STRING];
+ LARGE_INTEGER liDriverVersion;
+ DWORD dwVendorId;
+ DWORD dwDeviceId;
+ DWORD dwSubSysId;
+ DWORD dwRevision;
+ GUID guidDeviceIdentifier;
+} DDDEVICEIDENTIFIER, * LPDDDEVICEIDENTIFIER;
+
+typedef struct tagDDDEVICEIDENTIFIER2 {
+ char szDriver[MAX_DDDEVICEID_STRING]; /* user readable driver name */
+ char szDescription[MAX_DDDEVICEID_STRING]; /* user readable description */
+ LARGE_INTEGER liDriverVersion; /* driver version */
+ DWORD dwVendorId; /* vendor ID, zero if unknown */
+ DWORD dwDeviceId; /* chipset ID, zero if unknown */
+ DWORD dwSubSysId; /* board ID, zero if unknown */
+ DWORD dwRevision; /* chipset version, zero if unknown */
+ GUID guidDeviceIdentifier; /* unique ID for this driver/chipset combination */
+ DWORD dwWHQLLevel; /* Windows Hardware Quality Lab certification level */
+} DDDEVICEIDENTIFIER2, * LPDDDEVICEIDENTIFIER2;
+
+/*****************************************************************************
+ * IDirectDrawPalette interface
+ */
+#define INTERFACE IDirectDrawPalette
+DECLARE_INTERFACE_(IDirectDrawPalette,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawPalette methods ***/
+ STDMETHOD(GetCaps)(THIS_ LPDWORD lpdwCaps) PURE;
+ STDMETHOD(GetEntries)(THIS_ DWORD dwFlags, DWORD dwBase, DWORD dwNumEntries, LPPALETTEENTRY lpEntries) PURE;
+ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, DWORD dwFlags, LPPALETTEENTRY lpDDColorTable) PURE;
+ STDMETHOD(SetEntries)(THIS_ DWORD dwFlags, DWORD dwStartingEntry, DWORD dwCount, LPPALETTEENTRY lpEntries) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawPalette_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawPalette_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawPalette_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawPalette methods ***/
+#define IDirectDrawPalette_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
+#define IDirectDrawPalette_GetEntries(p,a,b,c,d) (p)->lpVtbl->GetEntries(p,a,b,c,d)
+#define IDirectDrawPalette_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c)
+#define IDirectDrawPalette_SetEntries(p,a,b,c,d) (p)->lpVtbl->SetEntries(p,a,b,c,d)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawPalette_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawPalette_AddRef(p) (p)->AddRef()
+#define IDirectDrawPalette_Release(p) (p)->Release()
+/*** IDirectDrawPalette methods ***/
+#define IDirectDrawPalette_GetCaps(p,a) (p)->GetCaps(a)
+#define IDirectDrawPalette_GetEntries(p,a,b,c,d) (p)->GetEntries(a,b,c,d)
+#define IDirectDrawPalette_Initialize(p,a,b,c) (p)->Initialize(a,b,c)
+#define IDirectDrawPalette_SetEntries(p,a,b,c,d) (p)->SetEntries(a,b,c,d)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDrawClipper interface
+ */
+#define INTERFACE IDirectDrawClipper
+DECLARE_INTERFACE_(IDirectDrawClipper,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawClipper methods ***/
+ STDMETHOD(GetClipList)(THIS_ LPRECT lpRect, LPRGNDATA lpClipList, LPDWORD lpdwSize) PURE;
+ STDMETHOD(GetHWnd)(THIS_ HWND *lphWnd) PURE;
+ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, DWORD dwFlags) PURE;
+ STDMETHOD(IsClipListChanged)(THIS_ BOOL *lpbChanged) PURE;
+ STDMETHOD(SetClipList)(THIS_ LPRGNDATA lpClipList, DWORD dwFlags) PURE;
+ STDMETHOD(SetHWnd)(THIS_ DWORD dwFlags, HWND hWnd) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawClipper_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawClipper_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawClipper_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawClipper methods ***/
+#define IDirectDrawClipper_GetClipList(p,a,b,c) (p)->lpVtbl->GetClipList(p,a,b,c)
+#define IDirectDrawClipper_GetHWnd(p,a) (p)->lpVtbl->GetHWnd(p,a)
+#define IDirectDrawClipper_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
+#define IDirectDrawClipper_IsClipListChanged(p,a) (p)->lpVtbl->IsClipListChanged(p,a)
+#define IDirectDrawClipper_SetClipList(p,a,b) (p)->lpVtbl->SetClipList(p,a,b)
+#define IDirectDrawClipper_SetHWnd(p,a,b) (p)->lpVtbl->SetHWnd(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawClipper_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawClipper_AddRef(p) (p)->AddRef()
+#define IDirectDrawClipper_Release(p) (p)->Release()
+/*** IDirectDrawClipper methods ***/
+#define IDirectDrawClipper_GetClipList(p,a,b,c) (p)->GetClipList(a,b,c)
+#define IDirectDrawClipper_GetHWnd(p,a) (p)->GetHWnd(a)
+#define IDirectDrawClipper_Initialize(p,a,b) (p)->Initialize(a,b)
+#define IDirectDrawClipper_IsClipListChanged(p,a) (p)->IsClipListChanged(a)
+#define IDirectDrawClipper_SetClipList(p,a,b) (p)->SetClipList(a,b)
+#define IDirectDrawClipper_SetHWnd(p,a,b) (p)->SetHWnd(a,b)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDraw interface
+ */
+#define INTERFACE IDirectDraw
+DECLARE_INTERFACE_(IDirectDraw,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDraw methods ***/
+ STDMETHOD(Compact)(THIS) PURE;
+ STDMETHOD(CreateClipper)(THIS_ DWORD dwFlags, LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter) PURE;
+ STDMETHOD(CreatePalette)(THIS_ DWORD dwFlags, LPPALETTEENTRY lpColorTable, LPDIRECTDRAWPALETTE *lplpDDPalette, IUnknown *pUnkOuter) PURE;
+ STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE *lplpDDSurface, IUnknown *pUnkOuter) PURE;
+ STDMETHOD(DuplicateSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDSurface, LPDIRECTDRAWSURFACE *lplpDupDDSurface) PURE;
+ STDMETHOD(EnumDisplayModes)(THIS_ DWORD dwFlags, LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext, LPDDENUMMODESCALLBACK lpEnumModesCallback) PURE;
+ STDMETHOD(EnumSurfaces)(THIS_ DWORD dwFlags, LPDDSURFACEDESC lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+ STDMETHOD(FlipToGDISurface)(THIS) PURE;
+ STDMETHOD(GetCaps)(THIS_ LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) PURE;
+ STDMETHOD(GetDisplayMode)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+ STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD lpNumCodes, LPDWORD lpCodes) PURE;
+ STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE *lplpGDIDDSurface) PURE;
+ STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD lpdwFrequency) PURE;
+ STDMETHOD(GetScanLine)(THIS_ LPDWORD lpdwScanLine) PURE;
+ STDMETHOD(GetVerticalBlankStatus)(THIS_ BOOL *lpbIsInVB) PURE;
+ STDMETHOD(Initialize)(THIS_ GUID *lpGUID) PURE;
+ STDMETHOD(RestoreDisplayMode)(THIS) PURE;
+ STDMETHOD(SetCooperativeLevel)(THIS_ HWND hWnd, DWORD dwFlags) PURE;
+ STDMETHOD(SetDisplayMode)(THIS_ DWORD dwWidth, DWORD dwHeight, DWORD dwBPP) PURE;
+ STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD dwFlags, HANDLE hEvent) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDraw_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDraw_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDraw_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDraw methods ***/
+#define IDirectDraw_Compact(p) (p)->lpVtbl->Compact(p)
+#define IDirectDraw_CreateClipper(p,a,b,c) (p)->lpVtbl->CreateClipper(p,a,b,c)
+#define IDirectDraw_CreatePalette(p,a,b,c,d) (p)->lpVtbl->CreatePalette(p,a,b,c,d)
+#define IDirectDraw_CreateSurface(p,a,b,c) (p)->lpVtbl->CreateSurface(p,a,b,c)
+#define IDirectDraw_DuplicateSurface(p,a,b) (p)->lpVtbl->DuplicateSurface(p,a,b)
+#define IDirectDraw_EnumDisplayModes(p,a,b,c,d) (p)->lpVtbl->EnumDisplayModes(p,a,b,c,d)
+#define IDirectDraw_EnumSurfaces(p,a,b,c,d) (p)->lpVtbl->EnumSurfaces(p,a,b,c,d)
+#define IDirectDraw_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p)
+#define IDirectDraw_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b)
+#define IDirectDraw_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a)
+#define IDirectDraw_GetFourCCCodes(p,a,b) (p)->lpVtbl->GetFourCCCodes(p,a,b)
+#define IDirectDraw_GetGDISurface(p,a) (p)->lpVtbl->GetGDISurface(p,a)
+#define IDirectDraw_GetMonitorFrequency(p,a) (p)->lpVtbl->GetMonitorFrequency(p,a)
+#define IDirectDraw_GetScanLine(p,a) (p)->lpVtbl->GetScanLine(p,a)
+#define IDirectDraw_GetVerticalBlankStatus(p,a) (p)->lpVtbl->GetVerticalBlankStatus(p,a)
+#define IDirectDraw_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
+#define IDirectDraw_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p)
+#define IDirectDraw_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b)
+#define IDirectDraw_SetDisplayMode(p,a,b,c) (p)->lpVtbl->SetDisplayMode(p,a,b,c)
+#define IDirectDraw_WaitForVerticalBlank(p,a,b) (p)->lpVtbl->WaitForVerticalBlank(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDraw_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDraw_AddRef(p) (p)->AddRef()
+#define IDirectDraw_Release(p) (p)->Release()
+/*** IDirectDraw methods ***/
+#define IDirectDraw_Compact(p) (p)->Compact()
+#define IDirectDraw_CreateClipper(p,a,b,c) (p)->CreateClipper(a,b,c)
+#define IDirectDraw_CreatePalette(p,a,b,c,d) (p)->CreatePalette(a,b,c,d)
+#define IDirectDraw_CreateSurface(p,a,b,c) (p)->CreateSurface(a,b,c)
+#define IDirectDraw_DuplicateSurface(p,a,b) (p)->DuplicateSurface(a,b)
+#define IDirectDraw_EnumDisplayModes(p,a,b,c,d) (p)->EnumDisplayModes(a,b,c,d)
+#define IDirectDraw_EnumSurfaces(p,a,b,c,d) (p)->EnumSurfaces(a,b,c,d)
+#define IDirectDraw_FlipToGDISurface(p) (p)->FlipToGDISurface()
+#define IDirectDraw_GetCaps(p,a,b) (p)->GetCaps(a,b)
+#define IDirectDraw_GetDisplayMode(p,a) (p)->GetDisplayMode(a)
+#define IDirectDraw_GetFourCCCodes(p,a,b) (p)->GetFourCCCodes(a,b)
+#define IDirectDraw_GetGDISurface(p,a) (p)->GetGDISurface(a)
+#define IDirectDraw_GetMonitorFrequency(p,a) (p)->GetMonitorFrequency(a)
+#define IDirectDraw_GetScanLine(p,a) (p)->GetScanLine(a)
+#define IDirectDraw_GetVerticalBlankStatus(p,a) (p)->GetVerticalBlankStatus(a)
+#define IDirectDraw_Initialize(p,a) (p)->Initialize(a)
+#define IDirectDraw_RestoreDisplayMode(p) (p)->RestoreDisplayMode()
+#define IDirectDraw_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b)
+#define IDirectDraw_SetDisplayMode(p,a,b,c) (p)->SetDisplayMode(a,b,c)
+#define IDirectDraw_WaitForVerticalBlank(p,a,b) (p)->WaitForVerticalBlank(a,b)
+#endif
+
+
+/* flags for Lock() */
+#define DDLOCK_SURFACEMEMORYPTR 0x00000000
+#define DDLOCK_WAIT 0x00000001
+#define DDLOCK_EVENT 0x00000002
+#define DDLOCK_READONLY 0x00000010
+#define DDLOCK_WRITEONLY 0x00000020
+#define DDLOCK_NOSYSLOCK 0x00000800
+#define DDLOCK_NOOVERWRITE 0x00001000
+#define DDLOCK_DISCARDCONTENTS 0x00002000
+
+
+/*****************************************************************************
+ * IDirectDraw2 interface
+ */
+/* Note: IDirectDraw2 cannot derive from IDirectDraw because the number of
+ * arguments of SetDisplayMode has changed !
+ */
+#define INTERFACE IDirectDraw2
+DECLARE_INTERFACE_(IDirectDraw2,IUnknown)
+{
+ /*** IUnknown methods ***/
+/*00*/ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+/*04*/ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+/*08*/ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDraw2 methods ***/
+/*0c*/ STDMETHOD(Compact)(THIS) PURE;
+/*10*/ STDMETHOD(CreateClipper)(THIS_ DWORD dwFlags, LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter) PURE;
+/*14*/ STDMETHOD(CreatePalette)(THIS_ DWORD dwFlags, LPPALETTEENTRY lpColorTable, LPDIRECTDRAWPALETTE *lplpDDPalette, IUnknown *pUnkOuter) PURE;
+/*18*/ STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE *lplpDDSurface, IUnknown *pUnkOuter) PURE;
+/*1c*/ STDMETHOD(DuplicateSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDSurface, LPDIRECTDRAWSURFACE *lplpDupDDSurface) PURE;
+/*20*/ STDMETHOD(EnumDisplayModes)(THIS_ DWORD dwFlags, LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext, LPDDENUMMODESCALLBACK lpEnumModesCallback) PURE;
+/*24*/ STDMETHOD(EnumSurfaces)(THIS_ DWORD dwFlags, LPDDSURFACEDESC lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+/*28*/ STDMETHOD(FlipToGDISurface)(THIS) PURE;
+/*2c*/ STDMETHOD(GetCaps)(THIS_ LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) PURE;
+/*30*/ STDMETHOD(GetDisplayMode)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+/*34*/ STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD lpNumCodes, LPDWORD lpCodes) PURE;
+/*38*/ STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE *lplpGDIDDSurface) PURE;
+/*3c*/ STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD lpdwFrequency) PURE;
+/*40*/ STDMETHOD(GetScanLine)(THIS_ LPDWORD lpdwScanLine) PURE;
+/*44*/ STDMETHOD(GetVerticalBlankStatus)(THIS_ BOOL *lpbIsInVB) PURE;
+/*48*/ STDMETHOD(Initialize)(THIS_ GUID *lpGUID) PURE;
+/*4c*/ STDMETHOD(RestoreDisplayMode)(THIS) PURE;
+/*50*/ STDMETHOD(SetCooperativeLevel)(THIS_ HWND hWnd, DWORD dwFlags) PURE;
+/*54*/ STDMETHOD(SetDisplayMode)(THIS_ DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags) PURE;
+/*58*/ STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD dwFlags, HANDLE hEvent) PURE;
+ /* added in v2 */
+/*5c*/ STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS lpDDCaps, LPDWORD lpdwTotal, LPDWORD lpdwFree) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDraw2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDraw2_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDraw2_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDraw methods ***/
+#define IDirectDraw2_Compact(p) (p)->lpVtbl->Compact(p)
+#define IDirectDraw2_CreateClipper(p,a,b,c) (p)->lpVtbl->CreateClipper(p,a,b,c)
+#define IDirectDraw2_CreatePalette(p,a,b,c,d) (p)->lpVtbl->CreatePalette(p,a,b,c,d)
+#define IDirectDraw2_CreateSurface(p,a,b,c) (p)->lpVtbl->CreateSurface(p,a,b,c)
+#define IDirectDraw2_DuplicateSurface(p,a,b) (p)->lpVtbl->DuplicateSurface(p,a,b)
+#define IDirectDraw2_EnumDisplayModes(p,a,b,c,d) (p)->lpVtbl->EnumDisplayModes(p,a,b,c,d)
+#define IDirectDraw2_EnumSurfaces(p,a,b,c,d) (p)->lpVtbl->EnumSurfaces(p,a,b,c,d)
+#define IDirectDraw2_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p)
+#define IDirectDraw2_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b)
+#define IDirectDraw2_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a)
+#define IDirectDraw2_GetFourCCCodes(p,a,b) (p)->lpVtbl->GetFourCCCodes(p,a,b)
+#define IDirectDraw2_GetGDISurface(p,a) (p)->lpVtbl->GetGDISurface(p,a)
+#define IDirectDraw2_GetMonitorFrequency(p,a) (p)->lpVtbl->GetMonitorFrequency(p,a)
+#define IDirectDraw2_GetScanLine(p,a) (p)->lpVtbl->GetScanLine(p,a)
+#define IDirectDraw2_GetVerticalBlankStatus(p,a) (p)->lpVtbl->GetVerticalBlankStatus(p,a)
+#define IDirectDraw2_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
+#define IDirectDraw2_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p)
+#define IDirectDraw2_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b)
+#define IDirectDraw2_SetDisplayMode(p,a,b,c,d,e) (p)->lpVtbl->SetDisplayMode(p,a,b,c,d,e)
+#define IDirectDraw2_WaitForVerticalBlank(p,a,b) (p)->lpVtbl->WaitForVerticalBlank(p,a,b)
+/*** IDirectDraw2 methods ***/
+#define IDirectDraw2_GetAvailableVidMem(p,a,b,c) (p)->lpVtbl->GetAvailableVidMem(p,a,b,c)
+#else
+/*** IUnknown methods ***/
+#define IDirectDraw2_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDraw2_AddRef(p) (p)->AddRef()
+#define IDirectDraw2_Release(p) (p)->Release()
+/*** IDirectDraw methods ***/
+#define IDirectDraw2_Compact(p) (p)->Compact()
+#define IDirectDraw2_CreateClipper(p,a,b,c) (p)->CreateClipper(a,b,c)
+#define IDirectDraw2_CreatePalette(p,a,b,c,d) (p)->CreatePalette(a,b,c,d)
+#define IDirectDraw2_CreateSurface(p,a,b,c) (p)->CreateSurface(a,b,c)
+#define IDirectDraw2_DuplicateSurface(p,a,b) (p)->DuplicateSurface(a,b)
+#define IDirectDraw2_EnumDisplayModes(p,a,b,c,d) (p)->EnumDisplayModes(a,b,c,d)
+#define IDirectDraw2_EnumSurfaces(p,a,b,c,d) (p)->EnumSurfaces(a,b,c,d)
+#define IDirectDraw2_FlipToGDISurface(p) (p)->FlipToGDISurface()
+#define IDirectDraw2_GetCaps(p,a,b) (p)->GetCaps(a,b)
+#define IDirectDraw2_GetDisplayMode(p,a) (p)->GetDisplayMode(a)
+#define IDirectDraw2_GetFourCCCodes(p,a,b) (p)->GetFourCCCodes(a,b)
+#define IDirectDraw2_GetGDISurface(p,a) (p)->GetGDISurface(a)
+#define IDirectDraw2_GetMonitorFrequency(p,a) (p)->GetMonitorFrequency(a)
+#define IDirectDraw2_GetScanLine(p,a) (p)->GetScanLine(a)
+#define IDirectDraw2_GetVerticalBlankStatus(p,a) (p)->GetVerticalBlankStatus(a)
+#define IDirectDraw2_Initialize(p,a) (p)->Initialize(a)
+#define IDirectDraw2_RestoreDisplayMode(p) (p)->RestoreDisplayMode()
+#define IDirectDraw2_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b)
+#define IDirectDraw2_SetDisplayMode(p,a,b,c,d,e) (p)->SetDisplayMode(a,b,c,d,e)
+#define IDirectDraw2_WaitForVerticalBlank(p,a,b) (p)->WaitForVerticalBlank(a,b)
+/*** IDirectDraw2 methods ***/
+#define IDirectDraw2_GetAvailableVidMem(p,a,b,c) (p)->GetAvailableVidMem(a,b,c)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDraw3 interface
+ */
+#define INTERFACE IDirectDraw3
+DECLARE_INTERFACE_(IDirectDraw3,IUnknown)
+{
+ /*** IUnknown methods ***/
+/*00*/ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+/*04*/ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+/*08*/ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDraw2 methods ***/
+/*0c*/ STDMETHOD(Compact)(THIS) PURE;
+/*10*/ STDMETHOD(CreateClipper)(THIS_ DWORD dwFlags, LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter) PURE;
+/*14*/ STDMETHOD(CreatePalette)(THIS_ DWORD dwFlags, LPPALETTEENTRY lpColorTable, LPDIRECTDRAWPALETTE *lplpDDPalette, IUnknown *pUnkOuter) PURE;
+/*18*/ STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE *lplpDDSurface, IUnknown *pUnkOuter) PURE;
+/*1c*/ STDMETHOD(DuplicateSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDSurface, LPDIRECTDRAWSURFACE *lplpDupDDSurface) PURE;
+/*20*/ STDMETHOD(EnumDisplayModes)(THIS_ DWORD dwFlags, LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext, LPDDENUMMODESCALLBACK lpEnumModesCallback) PURE;
+/*24*/ STDMETHOD(EnumSurfaces)(THIS_ DWORD dwFlags, LPDDSURFACEDESC lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+/*28*/ STDMETHOD(FlipToGDISurface)(THIS) PURE;
+/*2c*/ STDMETHOD(GetCaps)(THIS_ LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) PURE;
+/*30*/ STDMETHOD(GetDisplayMode)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+/*34*/ STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD lpNumCodes, LPDWORD lpCodes) PURE;
+/*38*/ STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE *lplpGDIDDSurface) PURE;
+/*3c*/ STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD lpdwFrequency) PURE;
+/*40*/ STDMETHOD(GetScanLine)(THIS_ LPDWORD lpdwScanLine) PURE;
+/*44*/ STDMETHOD(GetVerticalBlankStatus)(THIS_ BOOL *lpbIsInVB) PURE;
+/*48*/ STDMETHOD(Initialize)(THIS_ GUID *lpGUID) PURE;
+/*4c*/ STDMETHOD(RestoreDisplayMode)(THIS) PURE;
+/*50*/ STDMETHOD(SetCooperativeLevel)(THIS_ HWND hWnd, DWORD dwFlags) PURE;
+/*54*/ STDMETHOD(SetDisplayMode)(THIS_ DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags) PURE;
+/*58*/ STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD dwFlags, HANDLE hEvent) PURE;
+ /* added in v2 */
+/*5c*/ STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS lpDDCaps, LPDWORD lpdwTotal, LPDWORD lpdwFree) PURE;
+ /* added in v3 */
+/*60*/ STDMETHOD(GetSurfaceFromDC)(THIS_ HDC hdc, LPDIRECTDRAWSURFACE *pSurf) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDraw3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDraw3_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDraw3_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDraw methods ***/
+#define IDirectDraw3_Compact(p) (p)->lpVtbl->Compact(p)
+#define IDirectDraw3_CreateClipper(p,a,b,c) (p)->lpVtbl->CreateClipper(p,a,b,c)
+#define IDirectDraw3_CreatePalette(p,a,b,c,d) (p)->lpVtbl->CreatePalette(p,a,b,c,d)
+#define IDirectDraw3_CreateSurface(p,a,b,c) (p)->lpVtbl->CreateSurface(p,a,b,c)
+#define IDirectDraw3_DuplicateSurface(p,a,b) (p)->lpVtbl->DuplicateSurface(p,a,b)
+#define IDirectDraw3_EnumDisplayModes(p,a,b,c,d) (p)->lpVtbl->EnumDisplayModes(p,a,b,c,d)
+#define IDirectDraw3_EnumSurfaces(p,a,b,c,d) (p)->lpVtbl->EnumSurfaces(p,a,b,c,d)
+#define IDirectDraw3_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p)
+#define IDirectDraw3_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b)
+#define IDirectDraw3_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a)
+#define IDirectDraw3_GetFourCCCodes(p,a,b) (p)->lpVtbl->GetFourCCCodes(p,a,b)
+#define IDirectDraw3_GetGDISurface(p,a) (p)->lpVtbl->GetGDISurface(p,a)
+#define IDirectDraw3_GetMonitorFrequency(p,a) (p)->lpVtbl->GetMonitorFrequency(p,a)
+#define IDirectDraw3_GetScanLine(p,a) (p)->lpVtbl->GetScanLine(p,a)
+#define IDirectDraw3_GetVerticalBlankStatus(p,a) (p)->lpVtbl->GetVerticalBlankStatus(p,a)
+#define IDirectDraw3_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
+#define IDirectDraw3_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p)
+#define IDirectDraw3_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b)
+#define IDirectDraw3_SetDisplayMode(p,a,b,c,d,e) (p)->lpVtbl->SetDisplayMode(p,a,b,c,d,e)
+#define IDirectDraw3_WaitForVerticalBlank(p,a,b) (p)->lpVtbl->WaitForVerticalBlank(p,a,b)
+/*** IDirectDraw2 methods ***/
+#define IDirectDraw3_GetAvailableVidMem(p,a,b,c) (p)->lpVtbl->GetAvailableVidMem(p,a,b,c)
+/*** IDirectDraw3 methods ***/
+#define IDirectDraw3_GetSurfaceFromDC(p,a,b) (p)->lpVtbl->GetSurfaceFromDC(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDraw3_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDraw3_AddRef(p) (p)->AddRef()
+#define IDirectDraw3_Release(p) (p)->Release()
+/*** IDirectDraw methods ***/
+#define IDirectDraw3_Compact(p) (p)->Compact()
+#define IDirectDraw3_CreateClipper(p,a,b,c) (p)->CreateClipper(a,b,c)
+#define IDirectDraw3_CreatePalette(p,a,b,c,d) (p)->CreatePalette(a,b,c,d)
+#define IDirectDraw3_CreateSurface(p,a,b,c) (p)->CreateSurface(a,b,c)
+#define IDirectDraw3_DuplicateSurface(p,a,b) (p)->DuplicateSurface(a,b)
+#define IDirectDraw3_EnumDisplayModes(p,a,b,c,d) (p)->EnumDisplayModes(a,b,c,d)
+#define IDirectDraw3_EnumSurfaces(p,a,b,c,d) (p)->EnumSurfaces(a,b,c,d)
+#define IDirectDraw3_FlipToGDISurface(p) (p)->FlipToGDISurface()
+#define IDirectDraw3_GetCaps(p,a,b) (p)->GetCaps(a,b)
+#define IDirectDraw3_GetDisplayMode(p,a) (p)->GetDisplayMode(a)
+#define IDirectDraw3_GetFourCCCodes(p,a,b) (p)->GetFourCCCodes(a,b)
+#define IDirectDraw3_GetGDISurface(p,a) (p)->GetGDISurface(a)
+#define IDirectDraw3_GetMonitorFrequency(p,a) (p)->GetMonitorFrequency(a)
+#define IDirectDraw3_GetScanLine(p,a) (p)->GetScanLine(a)
+#define IDirectDraw3_GetVerticalBlankStatus(p,a) (p)->GetVerticalBlankStatus(a)
+#define IDirectDraw3_Initialize(p,a) (p)->Initialize(a)
+#define IDirectDraw3_RestoreDisplayMode(p) (p)->RestoreDisplayMode()
+#define IDirectDraw3_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b)
+#define IDirectDraw3_SetDisplayMode(p,a,b,c,d,e) (p)->SetDisplayMode(a,b,c,d,e)
+#define IDirectDraw3_WaitForVerticalBlank(p,a,b) (p)->WaitForVerticalBlank(a,b)
+/*** IDirectDraw2 methods ***/
+#define IDirectDraw3_GetAvailableVidMem(p,a,b,c) (p)->GetAvailableVidMem(a,b,c)
+/*** IDirectDraw3 methods ***/
+#define IDirectDraw3_GetSurfaceFromDC(p,a,b) (p)->GetSurfaceFromDC(a,b)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDraw4 interface
+ */
+#define INTERFACE IDirectDraw4
+DECLARE_INTERFACE_(IDirectDraw4,IUnknown)
+{
+ /*** IUnknown methods ***/
+/*00*/ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+/*04*/ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+/*08*/ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDraw4 methods ***/
+/*0c*/ STDMETHOD(Compact)(THIS) PURE;
+/*10*/ STDMETHOD(CreateClipper)(THIS_ DWORD dwFlags, LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter) PURE;
+/*14*/ STDMETHOD(CreatePalette)(THIS_ DWORD dwFlags, LPPALETTEENTRY lpColorTable, LPDIRECTDRAWPALETTE *lplpDDPalette, IUnknown *pUnkOuter) PURE;
+/*18*/ STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC2 lpDDSurfaceDesc, LPDIRECTDRAWSURFACE4 *lplpDDSurface, IUnknown *pUnkOuter) PURE;
+/*1c*/ STDMETHOD(DuplicateSurface)(THIS_ LPDIRECTDRAWSURFACE4 lpDDSurface, LPDIRECTDRAWSURFACE4 *lplpDupDDSurface) PURE;
+/*20*/ STDMETHOD(EnumDisplayModes)(THIS_ DWORD dwFlags, LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext, LPDDENUMMODESCALLBACK2 lpEnumModesCallback) PURE;
+/*24*/ STDMETHOD(EnumSurfaces)(THIS_ DWORD dwFlags, LPDDSURFACEDESC2 lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK2 lpEnumSurfacesCallback) PURE;
+/*28*/ STDMETHOD(FlipToGDISurface)(THIS) PURE;
+/*2c*/ STDMETHOD(GetCaps)(THIS_ LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) PURE;
+/*30*/ STDMETHOD(GetDisplayMode)(THIS_ LPDDSURFACEDESC2 lpDDSurfaceDesc) PURE;
+/*34*/ STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD lpNumCodes, LPDWORD lpCodes) PURE;
+/*38*/ STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE4 *lplpGDIDDSurface) PURE;
+/*3c*/ STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD lpdwFrequency) PURE;
+/*40*/ STDMETHOD(GetScanLine)(THIS_ LPDWORD lpdwScanLine) PURE;
+/*44*/ STDMETHOD(GetVerticalBlankStatus)(THIS_ BOOL *lpbIsInVB) PURE;
+/*48*/ STDMETHOD(Initialize)(THIS_ GUID *lpGUID) PURE;
+/*4c*/ STDMETHOD(RestoreDisplayMode)(THIS) PURE;
+/*50*/ STDMETHOD(SetCooperativeLevel)(THIS_ HWND hWnd, DWORD dwFlags) PURE;
+/*54*/ STDMETHOD(SetDisplayMode)(THIS_ DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags) PURE;
+/*58*/ STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD dwFlags, HANDLE hEvent) PURE;
+ /* added in v2 */
+/*5c*/ STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS2 lpDDCaps, LPDWORD lpdwTotal, LPDWORD lpdwFree) PURE;
+ /* added in v4 */
+/*60*/ STDMETHOD(GetSurfaceFromDC)(THIS_ HDC hdc, LPDIRECTDRAWSURFACE4 *pSurf) PURE;
+/*64*/ STDMETHOD(RestoreAllSurfaces)(THIS) PURE;
+/*68*/ STDMETHOD(TestCooperativeLevel)(THIS) PURE;
+/*6c*/ STDMETHOD(GetDeviceIdentifier)(THIS_ LPDDDEVICEIDENTIFIER pDDDI, DWORD dwFlags) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDraw4_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDraw4_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDraw4_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDraw methods ***/
+#define IDirectDraw4_Compact(p) (p)->lpVtbl->Compact(p)
+#define IDirectDraw4_CreateClipper(p,a,b,c) (p)->lpVtbl->CreateClipper(p,a,b,c)
+#define IDirectDraw4_CreatePalette(p,a,b,c,d) (p)->lpVtbl->CreatePalette(p,a,b,c,d)
+#define IDirectDraw4_CreateSurface(p,a,b,c) (p)->lpVtbl->CreateSurface(p,a,b,c)
+#define IDirectDraw4_DuplicateSurface(p,a,b) (p)->lpVtbl->DuplicateSurface(p,a,b)
+#define IDirectDraw4_EnumDisplayModes(p,a,b,c,d) (p)->lpVtbl->EnumDisplayModes(p,a,b,c,d)
+#define IDirectDraw4_EnumSurfaces(p,a,b,c,d) (p)->lpVtbl->EnumSurfaces(p,a,b,c,d)
+#define IDirectDraw4_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p)
+#define IDirectDraw4_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b)
+#define IDirectDraw4_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a)
+#define IDirectDraw4_GetFourCCCodes(p,a,b) (p)->lpVtbl->GetFourCCCodes(p,a,b)
+#define IDirectDraw4_GetGDISurface(p,a) (p)->lpVtbl->GetGDISurface(p,a)
+#define IDirectDraw4_GetMonitorFrequency(p,a) (p)->lpVtbl->GetMonitorFrequency(p,a)
+#define IDirectDraw4_GetScanLine(p,a) (p)->lpVtbl->GetScanLine(p,a)
+#define IDirectDraw4_GetVerticalBlankStatus(p,a) (p)->lpVtbl->GetVerticalBlankStatus(p,a)
+#define IDirectDraw4_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
+#define IDirectDraw4_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p)
+#define IDirectDraw4_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b)
+#define IDirectDraw4_SetDisplayMode(p,a,b,c,d,e) (p)->lpVtbl->SetDisplayMode(p,a,b,c,d,e)
+#define IDirectDraw4_WaitForVerticalBlank(p,a,b) (p)->lpVtbl->WaitForVerticalBlank(p,a,b)
+/*** IDirectDraw2 methods ***/
+#define IDirectDraw4_GetAvailableVidMem(p,a,b,c) (p)->lpVtbl->GetAvailableVidMem(p,a,b,c)
+/*** IDirectDraw4 methods ***/
+#define IDirectDraw4_GetSurfaceFromDC(p,a,b) (p)->lpVtbl->GetSurfaceFromDC(p,a,b)
+#define IDirectDraw4_RestoreAllSurfaces(pc) (p)->lpVtbl->RestoreAllSurfaces(p)
+#define IDirectDraw4_TestCooperativeLevel(p) (p)->lpVtbl->TestCooperativeLevel(p)
+#define IDirectDraw4_GetDeviceIdentifier(p,a,b) (p)->lpVtbl->GetDeviceIdentifier(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDraw4_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDraw4_AddRef(p) (p)->AddRef()
+#define IDirectDraw4_Release(p) (p)->Release()
+/*** IDirectDraw methods ***/
+#define IDirectDraw4_Compact(p) (p)->Compact()
+#define IDirectDraw4_CreateClipper(p,a,b,c) (p)->CreateClipper(a,b,c)
+#define IDirectDraw4_CreatePalette(p,a,b,c,d) (p)->CreatePalette(a,b,c,d)
+#define IDirectDraw4_CreateSurface(p,a,b,c) (p)->CreateSurface(a,b,c)
+#define IDirectDraw4_DuplicateSurface(p,a,b) (p)->DuplicateSurface(a,b)
+#define IDirectDraw4_EnumDisplayModes(p,a,b,c,d) (p)->EnumDisplayModes(a,b,c,d)
+#define IDirectDraw4_EnumSurfaces(p,a,b,c,d) (p)->EnumSurfaces(a,b,c,d)
+#define IDirectDraw4_FlipToGDISurface(p) (p)->FlipToGDISurface()
+#define IDirectDraw4_GetCaps(p,a,b) (p)->GetCaps(a,b)
+#define IDirectDraw4_GetDisplayMode(p,a) (p)->GetDisplayMode(a)
+#define IDirectDraw4_GetFourCCCodes(p,a,b) (p)->GetFourCCCodes(a,b)
+#define IDirectDraw4_GetGDISurface(p,a) (p)->GetGDISurface(a)
+#define IDirectDraw4_GetMonitorFrequency(p,a) (p)->GetMonitorFrequency(a)
+#define IDirectDraw4_GetScanLine(p,a) (p)->GetScanLine(a)
+#define IDirectDraw4_GetVerticalBlankStatus(p,a) (p)->GetVerticalBlankStatus(a)
+#define IDirectDraw4_Initialize(p,a) (p)->Initialize(a)
+#define IDirectDraw4_RestoreDisplayMode(p) (p)->RestoreDisplayMode()
+#define IDirectDraw4_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b)
+#define IDirectDraw4_SetDisplayMode(p,a,b,c,d,e) (p)->SetDisplayMode(a,b,c,d,e)
+#define IDirectDraw4_WaitForVerticalBlank(p,a,b) (p)->WaitForVerticalBlank(a,b)
+/*** IDirectDraw2 methods ***/
+#define IDirectDraw4_GetAvailableVidMem(p,a,b,c) (p)->GetAvailableVidMem(a,b,c)
+/*** IDirectDraw4 methods ***/
+#define IDirectDraw4_GetSurfaceFromDC(p,a,b) (p)->GetSurfaceFromDC(a,b)
+#define IDirectDraw4_RestoreAllSurfaces(pc) (p)->RestoreAllSurfaces()
+#define IDirectDraw4_TestCooperativeLevel(p) (p)->TestCooperativeLevel()
+#define IDirectDraw4_GetDeviceIdentifier(p,a,b) (p)->GetDeviceIdentifier(a,b)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDraw7 interface
+ */
+/* Note: IDirectDraw7 cannot derive from IDirectDraw4; it is even documented
+ * as not interchangeable with earlier DirectDraw interfaces.
+ */
+#define INTERFACE IDirectDraw7
+DECLARE_INTERFACE_(IDirectDraw7,IUnknown)
+{
+ /*** IUnknown methods ***/
+/*00*/ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+/*04*/ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+/*08*/ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDraw7 methods ***/
+/*0c*/ STDMETHOD(Compact)(THIS) PURE;
+/*10*/ STDMETHOD(CreateClipper)(THIS_ DWORD dwFlags, LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter) PURE;
+/*14*/ STDMETHOD(CreatePalette)(THIS_ DWORD dwFlags, LPPALETTEENTRY lpColorTable, LPDIRECTDRAWPALETTE *lplpDDPalette, IUnknown *pUnkOuter) PURE;
+/*18*/ STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC2 lpDDSurfaceDesc, LPDIRECTDRAWSURFACE7 *lplpDDSurface, IUnknown *pUnkOuter) PURE;
+/*1c*/ STDMETHOD(DuplicateSurface)(THIS_ LPDIRECTDRAWSURFACE7 lpDDSurface, LPDIRECTDRAWSURFACE7 *lplpDupDDSurface) PURE;
+/*20*/ STDMETHOD(EnumDisplayModes)(THIS_ DWORD dwFlags, LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext, LPDDENUMMODESCALLBACK2 lpEnumModesCallback) PURE;
+/*24*/ STDMETHOD(EnumSurfaces)(THIS_ DWORD dwFlags, LPDDSURFACEDESC2 lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK7 lpEnumSurfacesCallback) PURE;
+/*28*/ STDMETHOD(FlipToGDISurface)(THIS) PURE;
+/*2c*/ STDMETHOD(GetCaps)(THIS_ LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) PURE;
+/*30*/ STDMETHOD(GetDisplayMode)(THIS_ LPDDSURFACEDESC2 lpDDSurfaceDesc) PURE;
+/*34*/ STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD lpNumCodes, LPDWORD lpCodes) PURE;
+/*38*/ STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE7 *lplpGDIDDSurface) PURE;
+/*3c*/ STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD lpdwFrequency) PURE;
+/*40*/ STDMETHOD(GetScanLine)(THIS_ LPDWORD lpdwScanLine) PURE;
+/*44*/ STDMETHOD(GetVerticalBlankStatus)(THIS_ BOOL *lpbIsInVB) PURE;
+/*48*/ STDMETHOD(Initialize)(THIS_ GUID *lpGUID) PURE;
+/*4c*/ STDMETHOD(RestoreDisplayMode)(THIS) PURE;
+/*50*/ STDMETHOD(SetCooperativeLevel)(THIS_ HWND hWnd, DWORD dwFlags) PURE;
+/*54*/ STDMETHOD(SetDisplayMode)(THIS_ DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags) PURE;
+/*58*/ STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD dwFlags, HANDLE hEvent) PURE;
+ /* added in v2 */
+/*5c*/ STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS2 lpDDCaps, LPDWORD lpdwTotal, LPDWORD lpdwFree) PURE;
+ /* added in v4 */
+/*60*/ STDMETHOD(GetSurfaceFromDC)(THIS_ HDC hdc, LPDIRECTDRAWSURFACE7 *pSurf) PURE;
+/*64*/ STDMETHOD(RestoreAllSurfaces)(THIS) PURE;
+/*68*/ STDMETHOD(TestCooperativeLevel)(THIS) PURE;
+/*6c*/ STDMETHOD(GetDeviceIdentifier)(THIS_ LPDDDEVICEIDENTIFIER2 pDDDI, DWORD dwFlags) PURE;
+ /* added in v7 */
+/*70*/ STDMETHOD(StartModeTest)(THIS_ LPSIZE pModes, DWORD dwNumModes, DWORD dwFlags) PURE;
+/*74*/ STDMETHOD(EvaluateMode)(THIS_ DWORD dwFlags, DWORD *pTimeout) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDraw7_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDraw7_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDraw7_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDraw methods ***/
+#define IDirectDraw7_Compact(p) (p)->lpVtbl->Compact(p)
+#define IDirectDraw7_CreateClipper(p,a,b,c) (p)->lpVtbl->CreateClipper(p,a,b,c)
+#define IDirectDraw7_CreatePalette(p,a,b,c,d) (p)->lpVtbl->CreatePalette(p,a,b,c,d)
+#define IDirectDraw7_CreateSurface(p,a,b,c) (p)->lpVtbl->CreateSurface(p,a,b,c)
+#define IDirectDraw7_DuplicateSurface(p,a,b) (p)->lpVtbl->DuplicateSurface(p,a,b)
+#define IDirectDraw7_EnumDisplayModes(p,a,b,c,d) (p)->lpVtbl->EnumDisplayModes(p,a,b,c,d)
+#define IDirectDraw7_EnumSurfaces(p,a,b,c,d) (p)->lpVtbl->EnumSurfaces(p,a,b,c,d)
+#define IDirectDraw7_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p)
+#define IDirectDraw7_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b)
+#define IDirectDraw7_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a)
+#define IDirectDraw7_GetFourCCCodes(p,a,b) (p)->lpVtbl->GetFourCCCodes(p,a,b)
+#define IDirectDraw7_GetGDISurface(p,a) (p)->lpVtbl->GetGDISurface(p,a)
+#define IDirectDraw7_GetMonitorFrequency(p,a) (p)->lpVtbl->GetMonitorFrequency(p,a)
+#define IDirectDraw7_GetScanLine(p,a) (p)->lpVtbl->GetScanLine(p,a)
+#define IDirectDraw7_GetVerticalBlankStatus(p,a) (p)->lpVtbl->GetVerticalBlankStatus(p,a)
+#define IDirectDraw7_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
+#define IDirectDraw7_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p)
+#define IDirectDraw7_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b)
+#define IDirectDraw7_SetDisplayMode(p,a,b,c,d,e) (p)->lpVtbl->SetDisplayMode(p,a,b,c,d,e)
+#define IDirectDraw7_WaitForVerticalBlank(p,a,b) (p)->lpVtbl->WaitForVerticalBlank(p,a,b)
+/*** added in IDirectDraw2 ***/
+#define IDirectDraw7_GetAvailableVidMem(p,a,b,c) (p)->lpVtbl->GetAvailableVidMem(p,a,b,c)
+/*** added in IDirectDraw4 ***/
+#define IDirectDraw7_GetSurfaceFromDC(p,a,b) (p)->lpVtbl->GetSurfaceFromDC(p,a,b)
+#define IDirectDraw7_RestoreAllSurfaces(p) (p)->lpVtbl->RestoreAllSurfaces(p)
+#define IDirectDraw7_TestCooperativeLevel(p) (p)->lpVtbl->TestCooperativeLevel(p)
+#define IDirectDraw7_GetDeviceIdentifier(p,a,b) (p)->lpVtbl->GetDeviceIdentifier(p,a,b)
+/*** added in IDirectDraw 7 ***/
+#define IDirectDraw7_StartModeTest(p,a,b,c) (p)->lpVtbl->StartModeTest(p,a,b,c)
+#define IDirectDraw7_EvaluateMode(p,a,b) (p)->lpVtbl->EvaluateMode(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDraw7_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDraw7_AddRef(p) (p)->AddRef()
+#define IDirectDraw7_Release(p) (p)->Release()
+/*** IDirectDraw methods ***/
+#define IDirectDraw7_Compact(p) (p)->Compact()
+#define IDirectDraw7_CreateClipper(p,a,b,c) (p)->CreateClipper(a,b,c)
+#define IDirectDraw7_CreatePalette(p,a,b,c,d) (p)->CreatePalette(a,b,c,d)
+#define IDirectDraw7_CreateSurface(p,a,b,c) (p)->CreateSurface(a,b,c)
+#define IDirectDraw7_DuplicateSurface(p,a,b) (p)->DuplicateSurface(a,b)
+#define IDirectDraw7_EnumDisplayModes(p,a,b,c,d) (p)->EnumDisplayModes(a,b,c,d)
+#define IDirectDraw7_EnumSurfaces(p,a,b,c,d) (p)->EnumSurfaces(a,b,c,d)
+#define IDirectDraw7_FlipToGDISurface(p) (p)->FlipToGDISurface()
+#define IDirectDraw7_GetCaps(p,a,b) (p)->GetCaps(a,b)
+#define IDirectDraw7_GetDisplayMode(p,a) (p)->GetDisplayMode(a)
+#define IDirectDraw7_GetFourCCCodes(p,a,b) (p)->GetFourCCCodes(a,b)
+#define IDirectDraw7_GetGDISurface(p,a) (p)->GetGDISurface(a)
+#define IDirectDraw7_GetMonitorFrequency(p,a) (p)->GetMonitorFrequency(a)
+#define IDirectDraw7_GetScanLine(p,a) (p)->GetScanLine(a)
+#define IDirectDraw7_GetVerticalBlankStatus(p,a) (p)->GetVerticalBlankStatus(a)
+#define IDirectDraw7_Initialize(p,a) (p)->Initialize(a)
+#define IDirectDraw7_RestoreDisplayMode(p) (p)->RestoreDisplayMode()
+#define IDirectDraw7_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b)
+#define IDirectDraw7_SetDisplayMode(p,a,b,c,d,e) (p)->SetDisplayMode(a,b,c,d,e)
+#define IDirectDraw7_WaitForVerticalBlank(p,a,b) (p)->WaitForVerticalBlank(a,b)
+/*** added in IDirectDraw2 ***/
+#define IDirectDraw7_GetAvailableVidMem(p,a,b,c) (p)->GetAvailableVidMem(a,b,c)
+/*** added in IDirectDraw4 ***/
+#define IDirectDraw7_GetSurfaceFromDC(p,a,b) (p)->GetSurfaceFromDC(a,b)
+#define IDirectDraw7_RestoreAllSurfaces(p) (p)->RestoreAllSurfaces()
+#define IDirectDraw7_TestCooperativeLevel(p) (p)->TestCooperativeLevel()
+#define IDirectDraw7_GetDeviceIdentifier(p,a,b) (p)->GetDeviceIdentifier(a,b)
+/*** added in IDirectDraw 7 ***/
+#define IDirectDraw7_StartModeTest(p,a,b,c) (p)->StartModeTest(a,b,c)
+#define IDirectDraw7_EvaluateMode(p,a,b) (p)->EvaluateMode(a,b)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDrawSurface interface
+ */
+#define INTERFACE IDirectDrawSurface
+DECLARE_INTERFACE_(IDirectDrawSurface,IUnknown)
+{
+ /*** IUnknown methods ***/
+/*00*/ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+/*04*/ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+/*08*/ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawSurface methods ***/
+/*0c*/ STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDSAttachedSurface) PURE;
+/*10*/ STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT lpRect) PURE;
+/*14*/ STDMETHOD(Blt)(THIS_ LPRECT lpDestRect, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) PURE;
+/*18*/ STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH lpDDBltBatch, DWORD dwCount, DWORD dwFlags) PURE;
+/*1c*/ STDMETHOD(BltFast)(THIS_ DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) PURE;
+/*20*/ STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE lpDDSAttachedSurface) PURE;
+/*24*/ STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+/*28*/ STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD dwFlags, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpfnCallback) PURE;
+/*2c*/ STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DWORD dwFlags) PURE;
+/*30*/ STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS lpDDSCaps, LPDIRECTDRAWSURFACE *lplpDDAttachedSurface) PURE;
+/*34*/ STDMETHOD(GetBltStatus)(THIS_ DWORD dwFlags) PURE;
+/*38*/ STDMETHOD(GetCaps)(THIS_ LPDDSCAPS lpDDSCaps) PURE;
+/*3c*/ STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER *lplpDDClipper) PURE;
+/*40*/ STDMETHOD(GetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+/*44*/ STDMETHOD(GetDC)(THIS_ HDC *lphDC) PURE;
+/*48*/ STDMETHOD(GetFlipStatus)(THIS_ DWORD dwFlags) PURE;
+/*4c*/ STDMETHOD(GetOverlayPosition)(THIS_ LPLONG lplX, LPLONG lplY) PURE;
+/*50*/ STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE *lplpDDPalette) PURE;
+/*54*/ STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT lpDDPixelFormat) PURE;
+/*58*/ STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+/*5c*/ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+/*60*/ STDMETHOD(IsLost)(THIS) PURE;
+/*64*/ STDMETHOD(Lock)(THIS_ LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) PURE;
+/*68*/ STDMETHOD(ReleaseDC)(THIS_ HDC hDC) PURE;
+/*6c*/ STDMETHOD(Restore)(THIS) PURE;
+/*70*/ STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper) PURE;
+/*74*/ STDMETHOD(SetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+/*78*/ STDMETHOD(SetOverlayPosition)(THIS_ LONG lX, LONG lY) PURE;
+/*7c*/ STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE lpDDPalette) PURE;
+/*80*/ STDMETHOD(Unlock)(THIS_ LPVOID lpSurfaceData) PURE;
+/*84*/ STDMETHOD(UpdateOverlay)(THIS_ LPRECT lpSrcRect, LPDIRECTDRAWSURFACE lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) PURE;
+/*88*/ STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD dwFlags) PURE;
+/*8c*/ STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE lpDDSReference) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawSurface_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawSurface_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawSurface_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawSurface methods ***/
+#define IDirectDrawSurface_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a)
+#define IDirectDrawSurface_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a)
+#define IDirectDrawSurface_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e)
+#define IDirectDrawSurface_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c)
+#define IDirectDrawSurface_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e)
+#define IDirectDrawSurface_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b)
+#define IDirectDrawSurface_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b)
+#define IDirectDrawSurface_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c)
+#define IDirectDrawSurface_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b)
+#define IDirectDrawSurface_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b)
+#define IDirectDrawSurface_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a)
+#define IDirectDrawSurface_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
+#define IDirectDrawSurface_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a)
+#define IDirectDrawSurface_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b)
+#define IDirectDrawSurface_GetDC(p,a) (p)->lpVtbl->GetDC(p,a)
+#define IDirectDrawSurface_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a)
+#define IDirectDrawSurface_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b)
+#define IDirectDrawSurface_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a)
+#define IDirectDrawSurface_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a)
+#define IDirectDrawSurface_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a)
+#define IDirectDrawSurface_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
+#define IDirectDrawSurface_IsLost(p) (p)->lpVtbl->IsLost(p)
+#define IDirectDrawSurface_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d)
+#define IDirectDrawSurface_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a)
+#define IDirectDrawSurface_Restore(p) (p)->lpVtbl->Restore(p)
+#define IDirectDrawSurface_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a)
+#define IDirectDrawSurface_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b)
+#define IDirectDrawSurface_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b)
+#define IDirectDrawSurface_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a)
+#define IDirectDrawSurface_Unlock(p,a) (p)->lpVtbl->Unlock(p,a)
+#define IDirectDrawSurface_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e)
+#define IDirectDrawSurface_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a)
+#define IDirectDrawSurface_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawSurface_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawSurface_AddRef(p) (p)->AddRef()
+#define IDirectDrawSurface_Release(p) (p)->Release()
+/*** IDirectDrawSurface methods ***/
+#define IDirectDrawSurface_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a)
+#define IDirectDrawSurface_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a)
+#define IDirectDrawSurface_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e)
+#define IDirectDrawSurface_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c)
+#define IDirectDrawSurface_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e)
+#define IDirectDrawSurface_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b)
+#define IDirectDrawSurface_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b)
+#define IDirectDrawSurface_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c)
+#define IDirectDrawSurface_Flip(p,a,b) (p)->Flip(a,b)
+#define IDirectDrawSurface_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b)
+#define IDirectDrawSurface_GetBltStatus(p,a) (p)->GetBltStatus(a)
+#define IDirectDrawSurface_GetCaps(p,a) (p)->GetCaps(a)
+#define IDirectDrawSurface_GetClipper(p,a) (p)->GetClipper(a)
+#define IDirectDrawSurface_GetColorKey(p,a,b) (p)->GetColorKey(a,b)
+#define IDirectDrawSurface_GetDC(p,a) (p)->GetDC(a)
+#define IDirectDrawSurface_GetFlipStatus(p,a) (p)->GetFlipStatus(a)
+#define IDirectDrawSurface_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b)
+#define IDirectDrawSurface_GetPalette(p,a) (p)->GetPalette(a)
+#define IDirectDrawSurface_GetPixelFormat(p,a) (p)->GetPixelFormat(a)
+#define IDirectDrawSurface_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a)
+#define IDirectDrawSurface_Initialize(p,a,b) (p)->Initialize(a,b)
+#define IDirectDrawSurface_IsLost(p) (p)->IsLost()
+#define IDirectDrawSurface_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d)
+#define IDirectDrawSurface_ReleaseDC(p,a) (p)->ReleaseDC(a)
+#define IDirectDrawSurface_Restore(p) (p)->Restore()
+#define IDirectDrawSurface_SetClipper(p,a) (p)->SetClipper(a)
+#define IDirectDrawSurface_SetColorKey(p,a,b) (p)->SetColorKey(a,b)
+#define IDirectDrawSurface_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b)
+#define IDirectDrawSurface_SetPalette(p,a) (p)->SetPalette(a)
+#define IDirectDrawSurface_Unlock(p,a) (p)->Unlock(a)
+#define IDirectDrawSurface_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e)
+#define IDirectDrawSurface_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a)
+#define IDirectDrawSurface_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDrawSurface2 interface
+ */
+/* Cannot inherit from IDirectDrawSurface because the LPDIRECTDRAWSURFACE parameters
+ * have been converted to LPDIRECTDRAWSURFACE2.
+ */
+#define INTERFACE IDirectDrawSurface2
+DECLARE_INTERFACE_(IDirectDrawSurface2,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawSurface2 methods ***/
+ STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE2 lpDDSAttachedSurface) PURE;
+ STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT lpRect) PURE;
+ STDMETHOD(Blt)(THIS_ LPRECT lpDestRect, LPDIRECTDRAWSURFACE2 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) PURE;
+ STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH lpDDBltBatch, DWORD dwCount, DWORD dwFlags) PURE;
+ STDMETHOD(BltFast)(THIS_ DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE2 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) PURE;
+ STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE2 lpDDSAttachedSurface) PURE;
+ STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+ STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD dwFlags, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpfnCallback) PURE;
+ STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE2 lpDDSurfaceTargetOverride, DWORD dwFlags) PURE;
+ STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS lpDDSCaps, LPDIRECTDRAWSURFACE2 *lplpDDAttachedSurface) PURE;
+ STDMETHOD(GetBltStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetCaps)(THIS_ LPDDSCAPS lpDDSCaps) PURE;
+ STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER *lplpDDClipper) PURE;
+ STDMETHOD(GetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(GetDC)(THIS_ HDC *lphDC) PURE;
+ STDMETHOD(GetFlipStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetOverlayPosition)(THIS_ LPLONG lplX, LPLONG lplY) PURE;
+ STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE *lplpDDPalette) PURE;
+ STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT lpDDPixelFormat) PURE;
+ STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+ STDMETHOD(IsLost)(THIS) PURE;
+ STDMETHOD(Lock)(THIS_ LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) PURE;
+ STDMETHOD(ReleaseDC)(THIS_ HDC hDC) PURE;
+ STDMETHOD(Restore)(THIS) PURE;
+ STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper) PURE;
+ STDMETHOD(SetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(SetOverlayPosition)(THIS_ LONG lX, LONG lY) PURE;
+ STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE lpDDPalette) PURE;
+ STDMETHOD(Unlock)(THIS_ LPVOID lpSurfaceData) PURE;
+ STDMETHOD(UpdateOverlay)(THIS_ LPRECT lpSrcRect, LPDIRECTDRAWSURFACE2 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) PURE;
+ STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE2 lpDDSReference) PURE;
+ /* added in v2 */
+ STDMETHOD(GetDDInterface)(THIS_ LPVOID *lplpDD) PURE;
+ STDMETHOD(PageLock)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(PageUnlock)(THIS_ DWORD dwFlags) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawSurface2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawSurface2_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawSurface2_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawSurface methods (almost) ***/
+#define IDirectDrawSurface2_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a)
+#define IDirectDrawSurface2_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a)
+#define IDirectDrawSurface2_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e)
+#define IDirectDrawSurface2_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c)
+#define IDirectDrawSurface2_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e)
+#define IDirectDrawSurface2_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b)
+#define IDirectDrawSurface2_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b)
+#define IDirectDrawSurface2_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c)
+#define IDirectDrawSurface2_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b)
+#define IDirectDrawSurface2_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b)
+#define IDirectDrawSurface2_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a)
+#define IDirectDrawSurface2_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
+#define IDirectDrawSurface2_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a)
+#define IDirectDrawSurface2_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b)
+#define IDirectDrawSurface2_GetDC(p,a) (p)->lpVtbl->GetDC(p,a)
+#define IDirectDrawSurface2_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a)
+#define IDirectDrawSurface2_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b)
+#define IDirectDrawSurface2_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a)
+#define IDirectDrawSurface2_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a)
+#define IDirectDrawSurface2_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a)
+#define IDirectDrawSurface2_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
+#define IDirectDrawSurface2_IsLost(p) (p)->lpVtbl->IsLost(p)
+#define IDirectDrawSurface2_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d)
+#define IDirectDrawSurface2_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a)
+#define IDirectDrawSurface2_Restore(p) (p)->lpVtbl->Restore(p)
+#define IDirectDrawSurface2_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a)
+#define IDirectDrawSurface2_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b)
+#define IDirectDrawSurface2_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b)
+#define IDirectDrawSurface2_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a)
+#define IDirectDrawSurface2_Unlock(p,a) (p)->lpVtbl->Unlock(p,a)
+#define IDirectDrawSurface2_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e)
+#define IDirectDrawSurface2_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a)
+#define IDirectDrawSurface2_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface2_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a)
+#define IDirectDrawSurface2_PageLock(p,a) (p)->lpVtbl->PageLock(p,a)
+#define IDirectDrawSurface2_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawSurface2_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawSurface2_AddRef(p) (p)->AddRef()
+#define IDirectDrawSurface2_Release(p) (p)->Release()
+/*** IDirectDrawSurface methods (almost) ***/
+#define IDirectDrawSurface2_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a)
+#define IDirectDrawSurface2_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a)
+#define IDirectDrawSurface2_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e)
+#define IDirectDrawSurface2_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c)
+#define IDirectDrawSurface2_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e)
+#define IDirectDrawSurface2_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b)
+#define IDirectDrawSurface2_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b)
+#define IDirectDrawSurface2_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c)
+#define IDirectDrawSurface2_Flip(p,a,b) (p)->Flip(a,b)
+#define IDirectDrawSurface2_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b)
+#define IDirectDrawSurface2_GetBltStatus(p,a) (p)->GetBltStatus(a)
+#define IDirectDrawSurface2_GetCaps(p,a) (p)->GetCaps(a)
+#define IDirectDrawSurface2_GetClipper(p,a) (p)->GetClipper(a)
+#define IDirectDrawSurface2_GetColorKey(p,a,b) (p)->GetColorKey(a,b)
+#define IDirectDrawSurface2_GetDC(p,a) (p)->GetDC(a)
+#define IDirectDrawSurface2_GetFlipStatus(p,a) (p)->GetFlipStatus(a)
+#define IDirectDrawSurface2_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b)
+#define IDirectDrawSurface2_GetPalette(p,a) (p)->GetPalette(a)
+#define IDirectDrawSurface2_GetPixelFormat(p,a) (p)->GetPixelFormat(a)
+#define IDirectDrawSurface2_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a)
+#define IDirectDrawSurface2_Initialize(p,a,b) (p)->Initialize(a,b)
+#define IDirectDrawSurface2_IsLost(p) (p)->IsLost()
+#define IDirectDrawSurface2_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d)
+#define IDirectDrawSurface2_ReleaseDC(p,a) (p)->ReleaseDC(a)
+#define IDirectDrawSurface2_Restore(p) (p)->Restore()
+#define IDirectDrawSurface2_SetClipper(p,a) (p)->SetClipper(a)
+#define IDirectDrawSurface2_SetColorKey(p,a,b) (p)->SetColorKey(a,b)
+#define IDirectDrawSurface2_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b)
+#define IDirectDrawSurface2_SetPalette(p,a) (p)->SetPalette(a)
+#define IDirectDrawSurface2_Unlock(p,a) (p)->Unlock(a)
+#define IDirectDrawSurface2_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e)
+#define IDirectDrawSurface2_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a)
+#define IDirectDrawSurface2_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface2_GetDDInterface(p,a) (p)->GetDDInterface(a)
+#define IDirectDrawSurface2_PageLock(p,a) (p)->PageLock(a)
+#define IDirectDrawSurface2_PageUnlock(p,a) (p)->PageUnlock(a)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDrawSurface3 interface
+ */
+/* Cannot inherit from IDirectDrawSurface2 because the LPDIRECTDRAWSURFACE2 parameters
+ * have been converted to LPDIRECTDRAWSURFACE3.
+ */
+#define INTERFACE IDirectDrawSurface3
+DECLARE_INTERFACE_(IDirectDrawSurface3,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawSurface3 methods ***/
+ STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE3 lpDDSAttachedSurface) PURE;
+ STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT lpRect) PURE;
+ STDMETHOD(Blt)(THIS_ LPRECT lpDestRect, LPDIRECTDRAWSURFACE3 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) PURE;
+ STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH lpDDBltBatch, DWORD dwCount, DWORD dwFlags) PURE;
+ STDMETHOD(BltFast)(THIS_ DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE3 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) PURE;
+ STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE3 lpDDSAttachedSurface) PURE;
+ STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+ STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD dwFlags, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpfnCallback) PURE;
+ STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE3 lpDDSurfaceTargetOverride, DWORD dwFlags) PURE;
+ STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS lpDDSCaps, LPDIRECTDRAWSURFACE3 *lplpDDAttachedSurface) PURE;
+ STDMETHOD(GetBltStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetCaps)(THIS_ LPDDSCAPS lpDDSCaps) PURE;
+ STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER *lplpDDClipper) PURE;
+ STDMETHOD(GetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(GetDC)(THIS_ HDC *lphDC) PURE;
+ STDMETHOD(GetFlipStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetOverlayPosition)(THIS_ LPLONG lplX, LPLONG lplY) PURE;
+ STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE *lplpDDPalette) PURE;
+ STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT lpDDPixelFormat) PURE;
+ STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc) PURE;
+ STDMETHOD(IsLost)(THIS) PURE;
+ STDMETHOD(Lock)(THIS_ LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) PURE;
+ STDMETHOD(ReleaseDC)(THIS_ HDC hDC) PURE;
+ STDMETHOD(Restore)(THIS) PURE;
+ STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper) PURE;
+ STDMETHOD(SetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(SetOverlayPosition)(THIS_ LONG lX, LONG lY) PURE;
+ STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE lpDDPalette) PURE;
+ STDMETHOD(Unlock)(THIS_ LPVOID lpSurfaceData) PURE;
+ STDMETHOD(UpdateOverlay)(THIS_ LPRECT lpSrcRect, LPDIRECTDRAWSURFACE3 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) PURE;
+ STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE3 lpDDSReference) PURE;
+ /* added in v2 */
+ STDMETHOD(GetDDInterface)(THIS_ LPVOID *lplpDD) PURE;
+ STDMETHOD(PageLock)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(PageUnlock)(THIS_ DWORD dwFlags) PURE;
+ /* added in v3 */
+ STDMETHOD(SetSurfaceDesc)(THIS_ LPDDSURFACEDESC lpDDSD, DWORD dwFlags) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawSurface3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawSurface3_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawSurface3_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawSurface methods (almost) ***/
+#define IDirectDrawSurface3_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a)
+#define IDirectDrawSurface3_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a)
+#define IDirectDrawSurface3_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e)
+#define IDirectDrawSurface3_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c)
+#define IDirectDrawSurface3_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e)
+#define IDirectDrawSurface3_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b)
+#define IDirectDrawSurface3_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b)
+#define IDirectDrawSurface3_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c)
+#define IDirectDrawSurface3_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b)
+#define IDirectDrawSurface3_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b)
+#define IDirectDrawSurface3_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a)
+#define IDirectDrawSurface3_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
+#define IDirectDrawSurface3_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a)
+#define IDirectDrawSurface3_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b)
+#define IDirectDrawSurface3_GetDC(p,a) (p)->lpVtbl->GetDC(p,a)
+#define IDirectDrawSurface3_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a)
+#define IDirectDrawSurface3_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b)
+#define IDirectDrawSurface3_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a)
+#define IDirectDrawSurface3_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a)
+#define IDirectDrawSurface3_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a)
+#define IDirectDrawSurface3_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
+#define IDirectDrawSurface3_IsLost(p) (p)->lpVtbl->IsLost(p)
+#define IDirectDrawSurface3_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d)
+#define IDirectDrawSurface3_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a)
+#define IDirectDrawSurface3_Restore(p) (p)->lpVtbl->Restore(p)
+#define IDirectDrawSurface3_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a)
+#define IDirectDrawSurface3_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b)
+#define IDirectDrawSurface3_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b)
+#define IDirectDrawSurface3_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a)
+#define IDirectDrawSurface3_Unlock(p,a) (p)->lpVtbl->Unlock(p,a)
+#define IDirectDrawSurface3_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e)
+#define IDirectDrawSurface3_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a)
+#define IDirectDrawSurface3_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface3_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a)
+#define IDirectDrawSurface3_PageLock(p,a) (p)->lpVtbl->PageLock(p,a)
+#define IDirectDrawSurface3_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a)
+/*** IDirectDrawSurface3 methods ***/
+#define IDirectDrawSurface3_SetSurfaceDesc(p,a,b) (p)->lpVtbl->SetSurfaceDesc(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawSurface3_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawSurface3_AddRef(p) (p)->AddRef()
+#define IDirectDrawSurface3_Release(p) (p)->Release()
+/*** IDirectDrawSurface methods (almost) ***/
+#define IDirectDrawSurface3_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a)
+#define IDirectDrawSurface3_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a)
+#define IDirectDrawSurface3_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e)
+#define IDirectDrawSurface3_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c)
+#define IDirectDrawSurface3_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e)
+#define IDirectDrawSurface3_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b)
+#define IDirectDrawSurface3_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b)
+#define IDirectDrawSurface3_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c)
+#define IDirectDrawSurface3_Flip(p,a,b) (p)->Flip(a,b)
+#define IDirectDrawSurface3_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b)
+#define IDirectDrawSurface3_GetBltStatus(p,a) (p)->GetBltStatus(a)
+#define IDirectDrawSurface3_GetCaps(p,a) (p)->GetCaps(a)
+#define IDirectDrawSurface3_GetClipper(p,a) (p)->GetClipper(a)
+#define IDirectDrawSurface3_GetColorKey(p,a,b) (p)->GetColorKey(a,b)
+#define IDirectDrawSurface3_GetDC(p,a) (p)->GetDC(a)
+#define IDirectDrawSurface3_GetFlipStatus(p,a) (p)->GetFlipStatus(a)
+#define IDirectDrawSurface3_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b)
+#define IDirectDrawSurface3_GetPalette(p,a) (p)->GetPalette(a)
+#define IDirectDrawSurface3_GetPixelFormat(p,a) (p)->GetPixelFormat(a)
+#define IDirectDrawSurface3_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a)
+#define IDirectDrawSurface3_Initialize(p,a,b) (p)->Initialize(a,b)
+#define IDirectDrawSurface3_IsLost(p) (p)->IsLost()
+#define IDirectDrawSurface3_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d)
+#define IDirectDrawSurface3_ReleaseDC(p,a) (p)->ReleaseDC(a)
+#define IDirectDrawSurface3_Restore(p) (p)->Restore()
+#define IDirectDrawSurface3_SetClipper(p,a) (p)->SetClipper(a)
+#define IDirectDrawSurface3_SetColorKey(p,a,b) (p)->SetColorKey(a,b)
+#define IDirectDrawSurface3_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b)
+#define IDirectDrawSurface3_SetPalette(p,a) (p)->SetPalette(a)
+#define IDirectDrawSurface3_Unlock(p,a) (p)->Unlock(a)
+#define IDirectDrawSurface3_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e)
+#define IDirectDrawSurface3_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a)
+#define IDirectDrawSurface3_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface3_GetDDInterface(p,a) (p)->GetDDInterface(a)
+#define IDirectDrawSurface3_PageLock(p,a) (p)->PageLock(a)
+#define IDirectDrawSurface3_PageUnlock(p,a) (p)->PageUnlock(a)
+/*** IDirectDrawSurface3 methods ***/
+#define IDirectDrawSurface3_SetSurfaceDesc(p,a,b) (p)->SetSurfaceDesc(a,b)
+#endif
+
+
+/*****************************************************************************
+ * IDirectDrawSurface4 interface
+ */
+/* Cannot inherit from IDirectDrawSurface2 because DDSCAPS changed to DDSCAPS2.
+ */
+#define INTERFACE IDirectDrawSurface4
+DECLARE_INTERFACE_(IDirectDrawSurface4,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawSurface4 methods ***/
+ STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE4 lpDDSAttachedSurface) PURE;
+ STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT lpRect) PURE;
+ STDMETHOD(Blt)(THIS_ LPRECT lpDestRect, LPDIRECTDRAWSURFACE4 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) PURE;
+ STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH lpDDBltBatch, DWORD dwCount, DWORD dwFlags) PURE;
+ STDMETHOD(BltFast)(THIS_ DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE4 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) PURE;
+ STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE4 lpDDSAttachedSurface) PURE;
+ STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) PURE;
+ STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD dwFlags, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpfnCallback) PURE;
+ STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE4 lpDDSurfaceTargetOverride, DWORD dwFlags) PURE;
+ STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS2 lpDDSCaps, LPDIRECTDRAWSURFACE4 *lplpDDAttachedSurface) PURE;
+ STDMETHOD(GetBltStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetCaps)(THIS_ LPDDSCAPS2 lpDDSCaps) PURE;
+ STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER *lplpDDClipper) PURE;
+ STDMETHOD(GetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(GetDC)(THIS_ HDC *lphDC) PURE;
+ STDMETHOD(GetFlipStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetOverlayPosition)(THIS_ LPLONG lplX, LPLONG lplY) PURE;
+ STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE *lplpDDPalette) PURE;
+ STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT lpDDPixelFormat) PURE;
+ STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC2 lpDDSurfaceDesc) PURE;
+ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, LPDDSURFACEDESC2 lpDDSurfaceDesc) PURE;
+ STDMETHOD(IsLost)(THIS) PURE;
+ STDMETHOD(Lock)(THIS_ LPRECT lpDestRect, LPDDSURFACEDESC2 lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) PURE;
+ STDMETHOD(ReleaseDC)(THIS_ HDC hDC) PURE;
+ STDMETHOD(Restore)(THIS) PURE;
+ STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper) PURE;
+ STDMETHOD(SetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(SetOverlayPosition)(THIS_ LONG lX, LONG lY) PURE;
+ STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE lpDDPalette) PURE;
+ STDMETHOD(Unlock)(THIS_ LPRECT lpSurfaceData) PURE;
+ STDMETHOD(UpdateOverlay)(THIS_ LPRECT lpSrcRect, LPDIRECTDRAWSURFACE4 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) PURE;
+ STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE4 lpDDSReference) PURE;
+ /* added in v2 */
+ STDMETHOD(GetDDInterface)(THIS_ LPVOID *lplpDD) PURE;
+ STDMETHOD(PageLock)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(PageUnlock)(THIS_ DWORD dwFlags) PURE;
+ /* added in v3 */
+ STDMETHOD(SetSurfaceDesc)(THIS_ LPDDSURFACEDESC2 lpDDSD, DWORD dwFlags) PURE;
+ /* added in v4 */
+ STDMETHOD(SetPrivateData)(THIS_ REFGUID tag, LPVOID pData, DWORD cbSize, DWORD dwFlags) PURE;
+ STDMETHOD(GetPrivateData)(THIS_ REFGUID tag, LPVOID pBuffer, LPDWORD pcbBufferSize) PURE;
+ STDMETHOD(FreePrivateData)(THIS_ REFGUID tag) PURE;
+ STDMETHOD(GetUniquenessValue)(THIS_ LPDWORD pValue) PURE;
+ STDMETHOD(ChangeUniquenessValue)(THIS) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawSurface4_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawSurface4_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawSurface4_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawSurface (almost) methods ***/
+#define IDirectDrawSurface4_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a)
+#define IDirectDrawSurface4_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a)
+#define IDirectDrawSurface4_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e)
+#define IDirectDrawSurface4_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c)
+#define IDirectDrawSurface4_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e)
+#define IDirectDrawSurface4_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b)
+#define IDirectDrawSurface4_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b)
+#define IDirectDrawSurface4_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c)
+#define IDirectDrawSurface4_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b)
+#define IDirectDrawSurface4_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b)
+#define IDirectDrawSurface4_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a)
+#define IDirectDrawSurface4_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
+#define IDirectDrawSurface4_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a)
+#define IDirectDrawSurface4_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b)
+#define IDirectDrawSurface4_GetDC(p,a) (p)->lpVtbl->GetDC(p,a)
+#define IDirectDrawSurface4_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a)
+#define IDirectDrawSurface4_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b)
+#define IDirectDrawSurface4_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a)
+#define IDirectDrawSurface4_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a)
+#define IDirectDrawSurface4_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a)
+#define IDirectDrawSurface4_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
+#define IDirectDrawSurface4_IsLost(p) (p)->lpVtbl->IsLost(p)
+#define IDirectDrawSurface4_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d)
+#define IDirectDrawSurface4_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a)
+#define IDirectDrawSurface4_Restore(p) (p)->lpVtbl->Restore(p)
+#define IDirectDrawSurface4_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a)
+#define IDirectDrawSurface4_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b)
+#define IDirectDrawSurface4_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b)
+#define IDirectDrawSurface4_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a)
+#define IDirectDrawSurface4_Unlock(p,a) (p)->lpVtbl->Unlock(p,a)
+#define IDirectDrawSurface4_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e)
+#define IDirectDrawSurface4_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a)
+#define IDirectDrawSurface4_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface4_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a)
+#define IDirectDrawSurface4_PageLock(p,a) (p)->lpVtbl->PageLock(p,a)
+#define IDirectDrawSurface4_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a)
+/*** IDirectDrawSurface3 methods ***/
+#define IDirectDrawSurface4_SetSurfaceDesc(p,a,b) (p)->lpVtbl->SetSurfaceDesc(p,a,b)
+/*** IDirectDrawSurface4 methods ***/
+#define IDirectDrawSurface4_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d)
+#define IDirectDrawSurface4_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c)
+#define IDirectDrawSurface4_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a)
+#define IDirectDrawSurface4_GetUniquenessValue(p,a) (p)->lpVtbl->GetUniquenessValue(p,a)
+#define IDirectDrawSurface4_ChangeUniquenessValue(p) (p)->lpVtbl->ChangeUniquenessValue(p)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawSurface4_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawSurface4_AddRef(p) (p)->AddRef()
+#define IDirectDrawSurface4_Release(p) (p)->Release()
+/*** IDirectDrawSurface (almost) methods ***/
+#define IDirectDrawSurface4_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a)
+#define IDirectDrawSurface4_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a)
+#define IDirectDrawSurface4_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e)
+#define IDirectDrawSurface4_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c)
+#define IDirectDrawSurface4_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e)
+#define IDirectDrawSurface4_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b)
+#define IDirectDrawSurface4_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b)
+#define IDirectDrawSurface4_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c)
+#define IDirectDrawSurface4_Flip(p,a,b) (p)->Flip(a,b)
+#define IDirectDrawSurface4_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b)
+#define IDirectDrawSurface4_GetBltStatus(p,a) (p)->GetBltStatus(a)
+#define IDirectDrawSurface4_GetCaps(p,a) (p)->GetCaps(a)
+#define IDirectDrawSurface4_GetClipper(p,a) (p)->GetClipper(a)
+#define IDirectDrawSurface4_GetColorKey(p,a,b) (p)->GetColorKey(a,b)
+#define IDirectDrawSurface4_GetDC(p,a) (p)->GetDC(a)
+#define IDirectDrawSurface4_GetFlipStatus(p,a) (p)->GetFlipStatus(a)
+#define IDirectDrawSurface4_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b)
+#define IDirectDrawSurface4_GetPalette(p,a) (p)->GetPalette(a)
+#define IDirectDrawSurface4_GetPixelFormat(p,a) (p)->GetPixelFormat(a)
+#define IDirectDrawSurface4_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a)
+#define IDirectDrawSurface4_Initialize(p,a,b) (p)->Initialize(a,b)
+#define IDirectDrawSurface4_IsLost(p) (p)->IsLost()
+#define IDirectDrawSurface4_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d)
+#define IDirectDrawSurface4_ReleaseDC(p,a) (p)->ReleaseDC(a)
+#define IDirectDrawSurface4_Restore(p) (p)->Restore()
+#define IDirectDrawSurface4_SetClipper(p,a) (p)->SetClipper(a)
+#define IDirectDrawSurface4_SetColorKey(p,a,b) (p)->SetColorKey(a,b)
+#define IDirectDrawSurface4_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b)
+#define IDirectDrawSurface4_SetPalette(p,a) (p)->SetPalette(a)
+#define IDirectDrawSurface4_Unlock(p,a) (p)->Unlock(a)
+#define IDirectDrawSurface4_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e)
+#define IDirectDrawSurface4_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a)
+#define IDirectDrawSurface4_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface4_GetDDInterface(p,a) (p)->GetDDInterface(a)
+#define IDirectDrawSurface4_PageLock(p,a) (p)->PageLock(a)
+#define IDirectDrawSurface4_PageUnlock(p,a) (p)->PageUnlock(a)
+/*** IDirectDrawSurface3 methods ***/
+#define IDirectDrawSurface4_SetSurfaceDesc(p,a,b) (p)->SetSurfaceDesc(a,b)
+/*** IDirectDrawSurface4 methods ***/
+#define IDirectDrawSurface4_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d)
+#define IDirectDrawSurface4_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c)
+#define IDirectDrawSurface4_FreePrivateData(p,a) (p)->FreePrivateData(a)
+#define IDirectDrawSurface4_GetUniquenessValue(p,a) (p)->GetUniquenessValue(a)
+#define IDirectDrawSurface4_ChangeUniquenessValue(p) (p)->ChangeUniquenessValue()
+#endif
+
+
+/*****************************************************************************
+ * IDirectDrawSurface7 interface
+ */
+#define INTERFACE IDirectDrawSurface7
+DECLARE_INTERFACE_(IDirectDrawSurface7,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawSurface7 methods ***/
+ STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE7 lpDDSAttachedSurface) PURE;
+ STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT lpRect) PURE;
+ STDMETHOD(Blt)(THIS_ LPRECT lpDestRect, LPDIRECTDRAWSURFACE7 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) PURE;
+ STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH lpDDBltBatch, DWORD dwCount, DWORD dwFlags) PURE;
+ STDMETHOD(BltFast)(THIS_ DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE7 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) PURE;
+ STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE7 lpDDSAttachedSurface) PURE;
+ STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID lpContext, LPDDENUMSURFACESCALLBACK7 lpEnumSurfacesCallback) PURE;
+ STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD dwFlags, LPVOID lpContext, LPDDENUMSURFACESCALLBACK7 lpfnCallback) PURE;
+ STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE7 lpDDSurfaceTargetOverride, DWORD dwFlags) PURE;
+ STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS2 lpDDSCaps, LPDIRECTDRAWSURFACE7 *lplpDDAttachedSurface) PURE;
+ STDMETHOD(GetBltStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetCaps)(THIS_ LPDDSCAPS2 lpDDSCaps) PURE;
+ STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER *lplpDDClipper) PURE;
+ STDMETHOD(GetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(GetDC)(THIS_ HDC *lphDC) PURE;
+ STDMETHOD(GetFlipStatus)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(GetOverlayPosition)(THIS_ LPLONG lplX, LPLONG lplY) PURE;
+ STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE *lplpDDPalette) PURE;
+ STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT lpDDPixelFormat) PURE;
+ STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC2 lpDDSurfaceDesc) PURE;
+ STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW lpDD, LPDDSURFACEDESC2 lpDDSurfaceDesc) PURE;
+ STDMETHOD(IsLost)(THIS) PURE;
+ STDMETHOD(Lock)(THIS_ LPRECT lpDestRect, LPDDSURFACEDESC2 lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) PURE;
+ STDMETHOD(ReleaseDC)(THIS_ HDC hDC) PURE;
+ STDMETHOD(Restore)(THIS) PURE;
+ STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper) PURE;
+ STDMETHOD(SetColorKey)(THIS_ DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) PURE;
+ STDMETHOD(SetOverlayPosition)(THIS_ LONG lX, LONG lY) PURE;
+ STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE lpDDPalette) PURE;
+ STDMETHOD(Unlock)(THIS_ LPRECT lpSurfaceData) PURE;
+ STDMETHOD(UpdateOverlay)(THIS_ LPRECT lpSrcRect, LPDIRECTDRAWSURFACE7 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) PURE;
+ STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD dwFlags, LPDIRECTDRAWSURFACE7 lpDDSReference) PURE;
+ /* added in v2 */
+ STDMETHOD(GetDDInterface)(THIS_ LPVOID *lplpDD) PURE;
+ STDMETHOD(PageLock)(THIS_ DWORD dwFlags) PURE;
+ STDMETHOD(PageUnlock)(THIS_ DWORD dwFlags) PURE;
+ /* added in v3 */
+ STDMETHOD(SetSurfaceDesc)(THIS_ LPDDSURFACEDESC2 lpDDSD, DWORD dwFlags) PURE;
+ /* added in v4 */
+ STDMETHOD(SetPrivateData)(THIS_ REFGUID tag, LPVOID pData, DWORD cbSize, DWORD dwFlags) PURE;
+ STDMETHOD(GetPrivateData)(THIS_ REFGUID tag, LPVOID pBuffer, LPDWORD pcbBufferSize) PURE;
+ STDMETHOD(FreePrivateData)(THIS_ REFGUID tag) PURE;
+ STDMETHOD(GetUniquenessValue)(THIS_ LPDWORD pValue) PURE;
+ STDMETHOD(ChangeUniquenessValue)(THIS) PURE;
+ /* added in v7 */
+ STDMETHOD(SetPriority)(THIS_ DWORD prio) PURE;
+ STDMETHOD(GetPriority)(THIS_ LPDWORD prio) PURE;
+ STDMETHOD(SetLOD)(THIS_ DWORD lod) PURE;
+ STDMETHOD(GetLOD)(THIS_ LPDWORD lod) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawSurface7_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawSurface7_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawSurface7_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawSurface (almost) methods ***/
+#define IDirectDrawSurface7_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a)
+#define IDirectDrawSurface7_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a)
+#define IDirectDrawSurface7_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e)
+#define IDirectDrawSurface7_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c)
+#define IDirectDrawSurface7_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e)
+#define IDirectDrawSurface7_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b)
+#define IDirectDrawSurface7_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b)
+#define IDirectDrawSurface7_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c)
+#define IDirectDrawSurface7_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b)
+#define IDirectDrawSurface7_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b)
+#define IDirectDrawSurface7_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a)
+#define IDirectDrawSurface7_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
+#define IDirectDrawSurface7_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a)
+#define IDirectDrawSurface7_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b)
+#define IDirectDrawSurface7_GetDC(p,a) (p)->lpVtbl->GetDC(p,a)
+#define IDirectDrawSurface7_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a)
+#define IDirectDrawSurface7_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b)
+#define IDirectDrawSurface7_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a)
+#define IDirectDrawSurface7_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a)
+#define IDirectDrawSurface7_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a)
+#define IDirectDrawSurface7_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
+#define IDirectDrawSurface7_IsLost(p) (p)->lpVtbl->IsLost(p)
+#define IDirectDrawSurface7_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d)
+#define IDirectDrawSurface7_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a)
+#define IDirectDrawSurface7_Restore(p) (p)->lpVtbl->Restore(p)
+#define IDirectDrawSurface7_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a)
+#define IDirectDrawSurface7_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b)
+#define IDirectDrawSurface7_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b)
+#define IDirectDrawSurface7_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a)
+#define IDirectDrawSurface7_Unlock(p,a) (p)->lpVtbl->Unlock(p,a)
+#define IDirectDrawSurface7_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e)
+#define IDirectDrawSurface7_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a)
+#define IDirectDrawSurface7_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface7_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a)
+#define IDirectDrawSurface7_PageLock(p,a) (p)->lpVtbl->PageLock(p,a)
+#define IDirectDrawSurface7_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a)
+/*** IDirectDrawSurface3 methods ***/
+#define IDirectDrawSurface7_SetSurfaceDesc(p,a,b) (p)->lpVtbl->SetSurfaceDesc(p,a,b)
+/*** IDirectDrawSurface4 methods ***/
+#define IDirectDrawSurface7_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d)
+#define IDirectDrawSurface7_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c)
+#define IDirectDrawSurface7_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a)
+#define IDirectDrawSurface7_GetUniquenessValue(p,a) (p)->lpVtbl->GetUniquenessValue(p,a)
+#define IDirectDrawSurface7_ChangeUniquenessValue(p) (p)->lpVtbl->ChangeUniquenessValue(p)
+/*** IDirectDrawSurface7 methods ***/
+#define IDirectDrawSurface7_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a)
+#define IDirectDrawSurface7_GetPriority(p,a) (p)->lpVtbl->GetPriority(p,a)
+#define IDirectDrawSurface7_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a)
+#define IDirectDrawSurface7_GetLOD(p,a) (p)->lpVtbl->GetLOD(p,a)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawSurface7_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawSurface7_AddRef(p) (p)->AddRef()
+#define IDirectDrawSurface7_Release(p) (p)->Release()
+/*** IDirectDrawSurface (almost) methods ***/
+#define IDirectDrawSurface7_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a)
+#define IDirectDrawSurface7_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a)
+#define IDirectDrawSurface7_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e)
+#define IDirectDrawSurface7_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c)
+#define IDirectDrawSurface7_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e)
+#define IDirectDrawSurface7_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b)
+#define IDirectDrawSurface7_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b)
+#define IDirectDrawSurface7_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c)
+#define IDirectDrawSurface7_Flip(p,a,b) (p)->Flip(a,b)
+#define IDirectDrawSurface7_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b)
+#define IDirectDrawSurface7_GetBltStatus(p,a) (p)->GetBltStatus(a)
+#define IDirectDrawSurface7_GetCaps(p,a) (p)->GetCaps(a)
+#define IDirectDrawSurface7_GetClipper(p,a) (p)->GetClipper(a)
+#define IDirectDrawSurface7_GetColorKey(p,a,b) (p)->GetColorKey(a,b)
+#define IDirectDrawSurface7_GetDC(p,a) (p)->GetDC(a)
+#define IDirectDrawSurface7_GetFlipStatus(p,a) (p)->GetFlipStatus(a)
+#define IDirectDrawSurface7_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b)
+#define IDirectDrawSurface7_GetPalette(p,a) (p)->GetPalette(a)
+#define IDirectDrawSurface7_GetPixelFormat(p,a) (p)->GetPixelFormat(a)
+#define IDirectDrawSurface7_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a)
+#define IDirectDrawSurface7_Initialize(p,a,b) (p)->Initialize(a,b)
+#define IDirectDrawSurface7_IsLost(p) (p)->IsLost()
+#define IDirectDrawSurface7_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d)
+#define IDirectDrawSurface7_ReleaseDC(p,a) (p)->ReleaseDC(a)
+#define IDirectDrawSurface7_Restore(p) (p)->Restore()
+#define IDirectDrawSurface7_SetClipper(p,a) (p)->SetClipper(a)
+#define IDirectDrawSurface7_SetColorKey(p,a,b) (p)->SetColorKey(a,b)
+#define IDirectDrawSurface7_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b)
+#define IDirectDrawSurface7_SetPalette(p,a) (p)->SetPalette(a)
+#define IDirectDrawSurface7_Unlock(p,a) (p)->Unlock(a)
+#define IDirectDrawSurface7_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e)
+#define IDirectDrawSurface7_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a)
+#define IDirectDrawSurface7_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b)
+/*** IDirectDrawSurface2 methods ***/
+#define IDirectDrawSurface7_GetDDInterface(p,a) (p)->GetDDInterface(a)
+#define IDirectDrawSurface7_PageLock(p,a) (p)->PageLock(a)
+#define IDirectDrawSurface7_PageUnlock(p,a) (p)->PageUnlock(a)
+/*** IDirectDrawSurface3 methods ***/
+#define IDirectDrawSurface7_SetSurfaceDesc(p,a,b) (p)->SetSurfaceDesc(a,b)
+/*** IDirectDrawSurface4 methods ***/
+#define IDirectDrawSurface7_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d)
+#define IDirectDrawSurface7_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c)
+#define IDirectDrawSurface7_FreePrivateData(p,a) (p)->FreePrivateData(a)
+#define IDirectDrawSurface7_GetUniquenessValue(p,a) (p)->GetUniquenessValue(a)
+#define IDirectDrawSurface7_ChangeUniquenessValue(p) (p)->ChangeUniquenessValue()
+/*** IDirectDrawSurface7 methods ***/
+#define IDirectDrawSurface7_SetPriority(p,a) (p)->SetPriority(a)
+#define IDirectDrawSurface7_GetPriority(p,a) (p)->GetPriority(a)
+#define IDirectDrawSurface7_SetLOD(p,a) (p)->SetLOD(a)
+#define IDirectDrawSurface7_GetLOD(p,a) (p)->GetLOD(a)
+#endif
+
+/*****************************************************************************
+ * IDirectDrawColorControl interface
+ */
+#define INTERFACE IDirectDrawColorControl
+DECLARE_INTERFACE_(IDirectDrawColorControl,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawColorControl methods ***/
+ STDMETHOD(GetColorControls)(THIS_ LPDDCOLORCONTROL lpColorControl) PURE;
+ STDMETHOD(SetColorControls)(THIS_ LPDDCOLORCONTROL lpColorControl) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawColorControl_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawColorControl_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawColorControl_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawColorControl methods ***/
+#define IDirectDrawColorControl_GetColorControls(p,a) (p)->lpVtbl->GetColorControls(p,a)
+#define IDirectDrawColorControl_SetColorControls(p,a) (p)->lpVtbl->SetColorControls(p,a)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawColorControl_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawColorControl_AddRef(p) (p)->AddRef()
+#define IDirectDrawColorControl_Release(p) (p)->Release()
+/*** IDirectDrawColorControl methods ***/
+#define IDirectDrawColorControl_GetColorControls(p,a) (p)->GetColorControls(a)
+#define IDirectDrawColorControl_SetColorControls(p,a) (p)->SetColorControls(a)
+#endif
+
+/*****************************************************************************
+ * IDirectDrawGammaControl interface
+ */
+#define INTERFACE IDirectDrawGammaControl
+DECLARE_INTERFACE_(IDirectDrawGammaControl,IUnknown)
+{
+ /*** IUnknown methods ***/
+ STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
+ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+ STDMETHOD_(ULONG,Release)(THIS) PURE;
+ /*** IDirectDrawGammaControl methods ***/
+ STDMETHOD(GetGammaRamp)(THIS_ DWORD dwFlags, LPDDGAMMARAMP lpGammaRamp) PURE;
+ STDMETHOD(SetGammaRamp)(THIS_ DWORD dwFlags, LPDDGAMMARAMP lpGammaRamp) PURE;
+};
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+/*** IUnknown methods ***/
+#define IDirectDrawGammaControl_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IDirectDrawGammaControl_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IDirectDrawGammaControl_Release(p) (p)->lpVtbl->Release(p)
+/*** IDirectDrawGammaControl methods ***/
+#define IDirectDrawGammaControl_GetGammaRamp(p,a,b) (p)->lpVtbl->GetGammaRamp(p,a,b)
+#define IDirectDrawGammaControl_SetGammaRamp(p,a,b) (p)->lpVtbl->SetGammaRamp(p,a,b)
+#else
+/*** IUnknown methods ***/
+#define IDirectDrawGammaControl_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IDirectDrawGammaControl_AddRef(p) (p)->AddRef()
+#define IDirectDrawGammaControl_Release(p) (p)->Release()
+/*** IDirectDrawGammaControl methods ***/
+#define IDirectDrawGammaControl_GetGammaRamp(p,a,b) (p)->GetGammaRamp(a,b)
+#define IDirectDrawGammaControl_SetGammaRamp(p,a,b) (p)->SetGammaRamp(a,b)
+#endif
+
+
+HRESULT WINAPI DirectDrawCreate(GUID*,LPDIRECTDRAW*,IUnknown*);
+HRESULT WINAPI DirectDrawCreateEx(GUID*,LPVOID*,REFIID,IUnknown*);
+HRESULT WINAPI DirectDrawCreateClipper(DWORD,LPDIRECTDRAWCLIPPER*,IUnknown*);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* __WINE_DDRAW_H */
diff --git a/src/sys/x11/xkb.c b/src/sys/x11/xkb.c
new file mode 100644
index 0000000..ee0114b
--- /dev/null
+++ b/src/sys/x11/xkb.c
@@ -0,0 +1,129 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "headers.h"
+
+#include "sdlmain.h"
+#include "it.h"
+#include "osdefs.h"
+
+#include <X11/Xproto.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+
+#ifdef USE_XKB
+# include <X11/XKBlib.h>
+#endif
+
+static int virgin = 1;
+static unsigned int delay, rate;
+
+#ifdef USE_XKB
+static XkbDescPtr us_kb_map;
+#endif
+
+static void _key_info_setup(void)
+{
+ Display *dpy;
+ SDL_SysWMinfo info;
+
+ if (!virgin) return;
+ virgin = 0;
+
+ memset(&info, 0, sizeof(info));
+ SDL_VERSION(&info.version);
+ if (SDL_GetWMInfo(&info)) {
+ if (info.info.x11.lock_func)
+ info.info.x11.lock_func();
+ dpy = info.info.x11.display;
+ } else {
+ dpy = NULL;
+ }
+ if (!dpy) {
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) return;
+ memset(&info, 0, sizeof(info));
+ }
+
+#ifdef USE_XKB
+ /* Dear X11,
+ You suck.
+ Sincerely, Storlek */
+ char blank[] = "";
+ char symbols[] = "+us(basic)";
+ XkbComponentNamesRec rec = {
+ .symbols = symbols,
+ .keymap = blank,
+ .keycodes = blank,
+ .types = blank,
+ .compat = blank,
+ .geometry = blank,
+ };
+ us_kb_map = XkbGetKeyboardByName(dpy, XkbUseCoreKbd, &rec,
+ XkbGBN_AllComponentsMask, XkbGBN_AllComponentsMask, False);
+ if (!us_kb_map)
+ log_appendf(3, "Warning: XKB support missing or broken; keyjamming might not work right");
+
+ if (XkbGetAutoRepeatRate(dpy, XkbUseCoreKbd, &delay, &rate)) {
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+ return;
+ }
+#else
+ log_appendf(3, "Warning: XKB support not compiled in; keyjamming might not work right");
+#endif
+
+ /* eh... */
+ delay = 125;
+ rate = 30;
+
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+}
+
+unsigned int key_repeat_rate(void)
+{
+ _key_info_setup();
+ return rate;
+}
+
+unsigned int key_repeat_delay(void)
+{
+ _key_info_setup();
+ return delay;
+}
+
+#ifdef USE_XKB
+int key_scancode_lookup(int k, int def)
+{
+ static unsigned int d;
+ KeySym sym;
+
+ if (us_kb_map != NULL &&
+ XkbTranslateKeyCode(us_kb_map, k, 0, &d, &sym)) {
+ return sym;
+ }
+ return def;
+}
+#endif
diff --git a/src/sys/x11/xscreensaver.c b/src/sys/x11/xscreensaver.c
new file mode 100644
index 0000000..5f1f4ce
--- /dev/null
+++ b/src/sys/x11/xscreensaver.c
@@ -0,0 +1,175 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* I wanted to just lift this code out of xscreensaver-command,
+ but that wasn't really convenient. xscreensaver really should've
+ had this (or something like it) as a simple library call like:
+ xscreensaver_synthetic_user_active(display);
+ or something like that. spawning a subprocess is really expensive on
+ some systems, and might have subtle interactions with SDL or the player.
+
+ -mrsb
+*/
+
+#define NEED_TIME
+#include "headers.h"
+
+#include "sdlmain.h"
+#include "osdefs.h"
+
+#include <X11/Xproto.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+
+
+static XErrorHandler old_handler = NULL;
+static int BadWindow_ehandler(Display *dpy, XErrorEvent *error)
+{
+ if (error->error_code == BadWindow) {
+ return 0;
+ } else {
+ if (old_handler) return (*old_handler)(dpy,error);
+ /* shrug */
+ return 1;
+ }
+}
+
+void x11_screensaver_deactivate(void)
+{
+ static Atom XA_SCREENSAVER_VERSION;
+ static Atom XA_DEACTIVATE;
+ static Atom XA_SCREENSAVER;
+ static int setup = 0;
+ static int useit = 0;
+ static SDL_SysWMinfo info;
+
+ Window root, tmp, parent, *kids;
+ unsigned int nkids, i;
+
+ static time_t lastpoll = 0;
+ Window win;
+ time_t now;
+
+ Display *dpy = NULL;
+ XEvent ev;
+
+ if (!setup) {
+ setup = 1;
+ SDL_GetWMInfo(&info);
+ dpy = info.info.x11.display;
+ if (!dpy) {
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) return;
+ memset(&info, 0, sizeof(info));
+ info.info.x11.display = dpy;
+ }
+
+ useit = 1;
+ if (info.info.x11.lock_func) info.info.x11.lock_func();
+ XA_SCREENSAVER = XInternAtom(dpy, "SCREENSAVER", False);
+ XA_SCREENSAVER_VERSION = XInternAtom(dpy,
+ "_SCREENSAVER_VERSION", False);
+ XA_DEACTIVATE = XInternAtom(dpy, "DEACTIVATE", False);
+ if (info.info.x11.unlock_func) info.info.x11.unlock_func();
+ }
+
+ if (!useit) return;
+
+ time(&now);
+ if (!(lastpoll - now)) {
+ return;
+ }
+ lastpoll = now;
+
+ if (info.info.x11.lock_func)
+ info.info.x11.lock_func();
+ dpy = info.info.x11.display;
+ if (!dpy) {
+ useit = 0;
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+ return;
+ }
+
+ root = RootWindowOfScreen(DefaultScreenOfDisplay(dpy));
+ if (!XQueryTree(dpy, root, &tmp, &parent, &kids, &nkids)) {
+ useit = 0;
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+ return;
+ }
+ if (root != tmp || parent || !(kids && nkids)) {
+ useit = 0;
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+ return;
+ }
+
+ win = 0;
+ for (i = 0; i < nkids; i++) {
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *v = NULL;
+
+ XSync(dpy, False);
+ old_handler = XSetErrorHandler(BadWindow_ehandler);
+ if (XGetWindowProperty(dpy, kids[i],
+ XA_SCREENSAVER_VERSION, 0, 200,
+ False, XA_STRING, &type, &format,
+ &nitems, &bytesafter,
+ (unsigned char **)&v) == Success) {
+ XSetErrorHandler(old_handler);
+ if (v) XFree(v); /* don't care */
+ if (type != None) {
+ win = kids[i];
+ break;
+ }
+ }
+ XSetErrorHandler(old_handler);
+ }
+ XFree(kids);
+ if (!win) {
+ useit = 0;
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+ return;
+ }
+
+ ev.xany.type = ClientMessage;
+ ev.xclient.display = dpy;
+ ev.xclient.window = win;
+ ev.xclient.message_type = XA_SCREENSAVER;
+ ev.xclient.format = 32;
+ memset(&ev.xclient.data, 0, sizeof(ev.xclient.data));
+ ev.xclient.data.l[0] = XA_DEACTIVATE;
+ ev.xclient.data.l[1] = 0;
+ ev.xclient.data.l[2] = 0;
+ (void)XSendEvent(dpy, win, False, 0L, &ev);
+ XSync(dpy, 0);
+ if (info.info.x11.unlock_func)
+ info.info.x11.unlock_func();
+}
+
diff --git a/src/sys/x11/xv.c b/src/sys/x11/xv.c
new file mode 100644
index 0000000..1a275e8
--- /dev/null
+++ b/src/sys/x11/xv.c
@@ -0,0 +1,139 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "headers.h"
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xvlib.h>
+
+#ifndef HAVE_X11_EXTENSIONS_XVLIB_H
+# error what
+#endif
+
+#include "sdlmain.h"
+#include "video.h"
+#include "osdefs.h"
+
+unsigned int xv_yuvlayout(void)
+{
+ unsigned int ver, rev, eventB, reqB, errorB;
+ XvImageFormatValues *formats;
+ XvAdaptorInfo *ainfo;
+ XvEncodingInfo *encodings;
+ SDL_SysWMinfo info;
+ Display *dpy;
+ unsigned int nencode, nadaptors;
+ unsigned int fmt = VIDEO_YUV_NONE;
+ int numImages;
+ int screen, nscreens, img;
+ unsigned int adaptor, enc;
+ unsigned int w, h;
+ unsigned int best;
+
+ memset(&info, 0, sizeof(info));
+ SDL_VERSION(&info.version);
+ if (SDL_GetWMInfo(&info)) {
+ dpy = info.info.x11.display;
+ } else {
+ dpy = NULL;
+ printf("sdl_getwminfo?\n");
+ }
+
+ if (!dpy) {
+#if 0
+ /* this never closes the display, thus causing a memleak
+ (and we can't reasonably call XCloseDisplay ourselves) */
+ dpy = XOpenDisplay(NULL);
+ if (!dpy)
+ return VIDEO_YUV_NONE;
+#else
+ return VIDEO_YUV_NONE;
+#endif
+ }
+
+
+ ver = rev = reqB = eventB = errorB = 0;
+ if (XvQueryExtension(dpy, &ver, &rev, &reqB, &eventB, &errorB) != Success) {
+ /* no XV support */
+ return VIDEO_YUV_NONE;
+ }
+
+ nscreens = ScreenCount(dpy);
+ w = h = 0;
+ for (screen = 0; screen < nscreens; screen++) {
+ XvQueryAdaptors(dpy, RootWindow(dpy, screen), &nadaptors, &ainfo);
+ for (adaptor = 0; adaptor < nadaptors; adaptor++) {
+ XvQueryEncodings(dpy, ainfo[adaptor].base_id, &nencode, &encodings);
+ best = nencode; // impossible value
+ for (enc = 0; enc < nencode; enc++) {
+ if (strcmp(encodings[enc].name, "XV_IMAGE") != 0)
+ continue;
+ if (encodings[enc].width > w || encodings[enc].height > h) {
+ w = encodings[enc].width;
+ h = encodings[enc].height;
+ best = enc;
+ }
+ }
+ XvFreeEncodingInfo(encodings);
+
+ if (best == nencode || w < 640 || h < 400)
+ continue;
+
+ formats = XvListImageFormats(dpy, ainfo[adaptor].base_id, &numImages);
+ for (img = 0; img < numImages; img++) {
+ if (formats[img].type == XvRGB) continue;
+ if (w < 1280 || h < 400) {
+ /* not enough xv memory for packed */
+ switch (formats[img].id) {
+ case VIDEO_YUV_YV12:
+ fmt = VIDEO_YUV_YV12_TV;
+ break;
+ case VIDEO_YUV_IYUV:
+ fmt = VIDEO_YUV_IYUV_TV;
+ break;
+ }
+ continue;
+ }
+ switch (formats[img].id) {
+ case VIDEO_YUV_UYVY:
+ case VIDEO_YUV_YUY2:
+ case VIDEO_YUV_YVYU:
+ /* a packed format, and we have enough memory... */
+ fmt = formats[img].id;
+ XFree(formats);
+ XvFreeAdaptorInfo(ainfo);
+ return fmt;
+
+ case VIDEO_YUV_YV12:
+ case VIDEO_YUV_IYUV:
+ fmt = formats[img].id;
+ break;
+ }
+ }
+ XFree(formats);
+ }
+ XvFreeAdaptorInfo(ainfo);
+ }
+ return fmt;
+}
+
diff --git a/src/util/slurp.c b/src/util/slurp.c
new file mode 100644
index 0000000..8b5168a
--- /dev/null
+++ b/src/util/slurp.c
@@ -0,0 +1,310 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "slurp.h"
+#include "util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+/* The dup's are because fclose closes its file descriptor even if the FILE* was acquired with fdopen, and when
+the control gets back to slurp, it closes the fd (again). It doesn't seem to exist on Amiga OS though, so... */
+#ifndef HAVE_DUP
+# define dup(fd) fd
+#endif
+
+/* I hate this... */
+#ifndef O_BINARY
+# ifdef O_RAW
+# define O_BINARY O_RAW
+# else
+# define O_BINARY 0
+# endif
+#endif
+
+static void _slurp_closure_free(slurp_t *t)
+{
+ free(t->data);
+}
+
+/* --------------------------------------------------------------------- */
+
+/* CHUNK is how much memory is allocated at once. Too large a number is a
+ * waste of memory; too small means constantly realloc'ing.
+ *
+ * <mml> also, too large a number might take the OS more than an efficient number of reads to read in one
+ * hit -- which you could be processing/reallocing while waiting for the next bit
+ * <mml> we had something for some proggy on the server that was sucking data off stdin
+ * <mml> and had our resident c programmer and resident perl programmer competing for the fastest code
+ * <mml> but, the c coder found that after a bunch of test runs with time, 64k worked out the best case
+ * ...
+ * <mml> but, on another system with a different block size, 64 blocks may still be efficient, but 64k
+ * might not be 64 blocks
+ * (so maybe this should grab the block size from stat() instead...) */
+#define CHUNK 65536
+
+static int _slurp_stdio_pipe(slurp_t * t, int fd)
+{
+ int old_errno;
+ FILE *fp;
+ uint8_t *read_buf, *realloc_buf;
+ size_t this_len;
+ int chunks = 0;
+
+ t->data = NULL;
+ fp = fdopen(dup(fd), "rb");
+ if (fp == NULL)
+ return 0;
+
+ do {
+ chunks++;
+ /* Have to cast away the const... */
+ realloc_buf = realloc((void *) t->data, CHUNK * chunks);
+ if (realloc_buf == NULL) {
+ old_errno = errno;
+ fclose(fp);
+ free(t->data);
+ errno = old_errno;
+ return 0;
+ }
+ t->data = realloc_buf;
+ read_buf = (void *) (t->data + (CHUNK * (chunks - 1)));
+ this_len = fread(read_buf, 1, CHUNK, fp);
+ if (this_len <= 0) {
+ if (ferror(fp)) {
+ old_errno = errno;
+ fclose(fp);
+ free(t->data);
+ errno = old_errno;
+ return 0;
+ }
+ }
+ t->length += this_len;
+ } while (this_len);
+ fclose(fp);
+ t->closure = _slurp_closure_free;
+ return 1;
+}
+
+static int _slurp_stdio(slurp_t * t, int fd)
+{
+ int old_errno;
+ FILE *fp;
+ size_t got = 0, need, len;
+
+ if (t->length == 0) {
+ /* Hrmph. Probably a pipe or something... gotta do it the REALLY ugly way. */
+ return _slurp_stdio_pipe(t, fd);
+ }
+
+ fp = fdopen(dup(fd), "rb");
+
+ if (!fp)
+ return 0;
+
+ t->data = (uint8_t *) malloc(t->length);
+ if (t->data == NULL) {
+ old_errno = errno;
+ fclose(fp);
+ errno = old_errno;
+ return 0;
+ }
+
+ /* Read the WHOLE thing -- fread might not get it all at once,
+ * so keep trying until it returns zero. */
+ need = t->length;
+ do {
+ len = fread(t->data + got, 1, need, fp);
+ if (len <= 0) {
+ if (ferror(fp)) {
+ old_errno = errno;
+ fclose(fp);
+ free(t->data);
+ errno = old_errno;
+ return 0;
+ }
+
+ if (need > 0) {
+ /* short file */
+ need = 0;
+ t->length = got;
+ }
+ } else {
+ got += len;
+ need -= len;
+ }
+ } while (need > 0);
+
+ fclose(fp);
+ t->closure = _slurp_closure_free;
+ return 1;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static slurp_t *_slurp_open(const char *filename, struct stat * buf, size_t size)
+{
+ slurp_t *t;
+ int fd, old_errno;
+
+ if (buf && S_ISDIR(buf->st_mode)) {
+ errno = EISDIR;
+ return NULL;
+ }
+
+ t = (slurp_t *) mem_alloc(sizeof(slurp_t));
+ if (t == NULL)
+ return NULL;
+ t->pos = 0;
+
+ if (strcmp(filename, "-") == 0) {
+ if (_slurp_stdio(t, STDIN_FILENO))
+ return t;
+ free(t);
+ return NULL;
+ }
+
+ if (size <= 0) {
+ size = (buf ? buf->st_size : file_size(filename));
+ }
+
+#ifdef WIN32
+ switch (slurp_win32(t, filename, size)) {
+ case 0: free(t); return NULL;
+ case 1: return t;
+ };
+#endif
+
+#if HAVE_MMAP
+ switch (slurp_mmap(t, filename, size)) {
+ case 0: free(t); return NULL;
+ case 1: return t;
+ };
+#endif
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+
+ if (fd < 0) {
+ free(t);
+ return NULL;
+ }
+
+ t->length = size;
+
+ if (_slurp_stdio(t, fd)) {
+ close(fd);
+ return t;
+ }
+
+ old_errno = errno;
+ close(fd);
+ free(t);
+ errno = old_errno;
+ return NULL;
+}
+
+slurp_t *slurp(const char *filename, struct stat * buf, size_t size)
+{
+ slurp_t *t = _slurp_open(filename, buf, size);
+
+ if (!t) {
+ return NULL;
+ }
+
+ return t;
+}
+
+
+void unslurp(slurp_t * t)
+{
+ if (!t)
+ return;
+ if (t->data && t->closure) {
+ t->closure(t);
+ }
+ free(t);
+}
+
+/* --------------------------------------------------------------------- */
+
+int slurp_seek(slurp_t *t, long offset, int whence)
+{
+ switch (whence) {
+ default:
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += t->pos;
+ break;
+ case SEEK_END:
+ offset += t->length;
+ break;
+ }
+ if (offset < 0 || (size_t) offset > t->length)
+ return -1;
+ t->pos = offset;
+ return 0;
+}
+
+long slurp_tell(slurp_t *t)
+{
+ return (long) t->pos;
+}
+
+size_t slurp_read(slurp_t *t, void *ptr, size_t count)
+{
+ size_t bytesleft = t->length - t->pos;
+ if (count > bytesleft) {
+ // short read -- fill in any extra bytes with zeroes
+ size_t tail = count - bytesleft;
+ count = bytesleft;
+ memset(ptr + count, 0, tail);
+ }
+ if (count)
+ memcpy(ptr, t->data + t->pos, count);
+ t->pos += count;
+ return count;
+}
+
+int slurp_getc(slurp_t *t)
+{
+ return (t->pos < t->length) ? t->data[t->pos++] : EOF;
+}
+
+int slurp_eof(slurp_t *t)
+{
+ return t->pos >= t->length;
+}
+
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 0000000..7a3ecee
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,878 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This is just a collection of some useful functions. None of these use any
+extraneous libraries (i.e. GLib). */
+
+
+#define NEED_DIRENT
+#define NEED_TIME
+#include "headers.h"
+
+#include "util.h"
+// #include "osdefs.h" /* need this for win32_filecreated_callback */ REMOVING TO SEE WHAT BREAKS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#include <stdarg.h>
+
+#include <math.h>
+
+#if defined(__amigaos4__)
+# define FALLBACK_DIR "." /* not used... */
+#elif defined(WIN32)
+# define FALLBACK_DIR "C:\\"
+#elif defined(GEKKO)
+# define FALLBACK_DIR "isfs:/" // always exists, seldom useful
+#else /* POSIX? */
+# define FALLBACK_DIR "/"
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#include <shlobj.h>
+#else
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+void ms_sleep(unsigned int ms)
+{
+#ifdef WIN32
+ SleepEx(ms,FALSE);
+#else
+ usleep(ms*1000);
+#endif
+}
+
+char *str_dup(const char *s)
+{
+ char *q;
+ q = strdup(s);
+ if (!q) {
+ /* throw out of memory exception */
+ perror("strdup");
+ exit(255);
+ }
+ return q;
+}
+
+void *mem_alloc(size_t amount)
+{
+ void *q;
+ q = malloc(amount);
+ if (!q) {
+ /* throw out of memory exception */
+ perror("malloc");
+ exit(255);
+ }
+ return q;
+}
+void *mem_realloc(void *orig, size_t amount)
+{
+ void *q;
+ if (!orig) return mem_alloc(amount);
+ q = realloc(orig, amount);
+ if (!q) {
+ /* throw out of memory exception */
+ perror("malloc");
+ exit(255);
+ }
+ return q;
+}
+
+
+/* --------------------------------------------------------------------- */
+/* CONVERSION FUNCTIONS */
+
+/* linear -> deciBell */
+/* amplitude normalized to 1.0f. */
+float dB(float amplitude)
+{
+ return 20.0f * log10f(amplitude);
+}
+
+/* deciBell -> linear */
+float dB2_amp(float db)
+{
+ return powf(10.0f, db / 20.0f);
+}
+
+/* linear -> deciBell */
+/* power normalized to 1.0f. */
+float pdB(float power)
+{
+ return 10.0f * log10f(power);
+}
+
+/* deciBell -> linear */
+float dB2_power(float db)
+{
+ return powf(10.0f, db / 10.0f);
+}
+/* linear -> deciBell */
+/* amplitude normalized to 1.0f. */
+/* Output scaled (and clipped) to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short dB_s(int noisefloor, float amplitude, float correction_dBs)
+{
+ float db = dB(amplitude) + correction_dBs;
+ return CLAMP((int)(128.f*(db+noisefloor))/noisefloor, 0, 127);
+}
+
+/* deciBell -> linear */
+/* Input scaled to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* amplitude normalized to 1.0f. */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short dB2_amp_s(int noisefloor, int db, float correction_dBs)
+{
+ return dB2_amp((db*noisefloor/128.f)-noisefloor-correction_dBs);
+}
+/* linear -> deciBell */
+/* power normalized to 1.0f. */
+/* Output scaled (and clipped) to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short pdB_s(int noisefloor, float power, float correction_dBs)
+{
+ float db = pdB(power)+correction_dBs;
+ return CLAMP((int)(128.f*(db+noisefloor))/noisefloor, 0, 127);
+}
+
+/* deciBell -> linear */
+/* Input scaled to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* power normalized to 1.0f. */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short dB2_power_s(int noisefloor, int db, float correction_dBs)
+{
+ return dB2_power((db*noisefloor/128.f)-noisefloor-correction_dBs);
+}
+/* --------------------------------------------------------------------- */
+/* FORMATTING FUNCTIONS */
+
+char *get_date_string(time_t when, char *buf)
+{
+ struct tm tmr;
+ const char *month_str[12] = {
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ };
+
+ /* DO NOT change this back to localtime(). If some backward platform
+ doesn't have localtime_r, it needs to be implemented separately. */
+ localtime_r(&when, &tmr);
+ snprintf(buf, 27, "%s %d, %d", month_str[tmr.tm_mon], tmr.tm_mday, 1900 + tmr.tm_year);
+ return buf;
+}
+
+char *get_time_string(time_t when, char *buf)
+{
+ struct tm tmr;
+
+ localtime_r(&when, &tmr);
+ snprintf(buf, 27, "%d:%02d%s", tmr.tm_hour % 12 ? : 12, tmr.tm_min, tmr.tm_hour < 12 ? "am" : "pm");
+ return buf;
+}
+
+char *num99tostr(int n, char *buf)
+{
+ static const char *qv = "HIJKLMNOPQRSTUVWXYZ";
+ if (n < 100) {
+ sprintf(buf, "%02d", n);
+ } else if (n <= 256) {
+ n -= 100;
+ sprintf(buf, "%c%d",
+ qv[(n/10)], (n % 10));
+ }
+ return buf;
+
+}
+char *numtostr(int digits, unsigned int n, char *buf)
+{
+ if (digits > 0) {
+ char fmt[] = "%03u";
+
+ digits %= 10;
+ fmt[2] = '0' + digits;
+ snprintf(buf, digits + 1, fmt, n);
+ buf[digits] = 0;
+ } else {
+ sprintf(buf, "%u", n);
+ }
+ return buf;
+}
+char *numtostr_signed(int digits, int n, char *buf)
+{
+ if (digits > 0) {
+ char fmt[] = "%03d";
+
+ digits %= 10;
+ fmt[2] = '0' + digits;
+ snprintf(buf, digits + 1, fmt, n);
+ buf[digits] = 0;
+ } else {
+ sprintf(buf, "%d", n);
+ }
+ return buf;
+}
+
+/* --------------------------------------------------------------------- */
+/* STRING HANDLING FUNCTIONS */
+
+/* I was intending to get rid of this and use glibc's basename() instead,
+but it doesn't do what I want (i.e. not bother with the string) and thanks
+to the stupid libgen.h basename that's totally different, it'd cause some
+possible portability issues. */
+const char *get_basename(const char *filename)
+{
+ const char *base = strrchr(filename, DIR_SEPARATOR);
+ if (base) {
+ /* skip the slash */
+ base++;
+ }
+ if (!(base && *base)) {
+ /* well, there isn't one, so just return the filename */
+ base = filename;
+ }
+
+ return base;
+}
+
+const char *get_extension(const char *filename)
+{
+ filename = get_basename(filename);
+
+ const char *extension = strrchr(filename, '.');
+ if (!extension) {
+ /* no extension? bummer. point to the \0 at the end of the string. */
+ extension = strchr(filename, '\0');
+ }
+
+ return extension;
+}
+
+char *get_parent_directory(const char *dirname)
+{
+ char *ret, *pos;
+ int n;
+
+ if (!dirname || !dirname[0])
+ return NULL;
+
+ ret = str_dup(dirname);
+ if (!ret)
+ return NULL;
+ n = strlen(ret) - 1;
+ if (ret[n] == DIR_SEPARATOR)
+ ret[n] = 0;
+ pos = strrchr(ret, DIR_SEPARATOR);
+ if (!pos) {
+ free(ret);
+ return NULL;
+ }
+ pos[1] = 0;
+ return ret;
+}
+
+static const char *whitespace = " \t\v\r\n";
+
+inline int ltrim_string(char *s)
+{
+ int ws = strspn(s, whitespace);
+ int len = strlen(s) - ws;
+
+ if (ws)
+ memmove(s, s + ws, len + 1);
+ return len;
+}
+
+inline int rtrim_string(char *s)
+{
+ int len = strlen(s) - 1;
+
+ while (len > 0 && strchr(whitespace, s[len]))
+ len--;
+ len++;
+ s[len] = '\0';
+ return len;
+}
+
+int trim_string(char *s)
+{
+ ltrim_string(s);
+ return rtrim_string(s);
+}
+
+
+/* break the string 's' with the character 'c', placing the two parts in 'first' and 'second'.
+return: 1 if the string contained the character (and thus could be split), 0 if not.
+the pointers returned in first/second should be free()'d by the caller. */
+int str_break(const char *s, char c, char **first, char **second)
+{
+ const char *p = strchr(s, c);
+ if (!p)
+ return 0;
+ *first = mem_alloc(p - s + 1);
+ strncpy(*first, s, p - s);
+ (*first)[p - s] = 0;
+ *second = str_dup(p + 1);
+ return 1;
+}
+
+/* adapted from glib. in addition to the normal c escapes, this also escapes the hashmark and semicolon
+ * (comment characters). if space is true, the first/last character is also escaped if it is a space. */
+char *str_escape(const char *s, int space)
+{
+ /* Each source byte needs maximally four destination chars (\777) */
+ char *dest = calloc(4 * strlen(s) + 1, sizeof(char));
+ char *d = dest;
+
+ if (space && *s == ' ') {
+ *d++ = '\\';
+ *d++ = '0';
+ *d++ = '4';
+ *d++ = '0';
+ s++;
+ }
+
+ while (*s) {
+ switch (*s) {
+ case '\a':
+ *d++ = '\\';
+ *d++ = 'a';
+ break;
+ case '\b':
+ *d++ = '\\';
+ *d++ = 'b';
+ break;
+ case '\f':
+ *d++ = '\\';
+ *d++ = 'f';
+ break;
+ case '\n':
+ *d++ = '\\';
+ *d++ = 'n';
+ break;
+ case '\r':
+ *d++ = '\\';
+ *d++ = 'r';
+ break;
+ case '\t':
+ *d++ = '\\';
+ *d++ = 't';
+ break;
+ case '\v':
+ *d++ = '\\';
+ *d++ = 'v';
+ break;
+ case '\\': case '"':
+ *d++ = '\\';
+ *d++ = *s;
+ break;
+
+ default:
+ if (*s < ' ' || *s >= 127 || (space && *s == ' ' && s[1] == '\0')) {
+ case '#': case ';':
+ *d++ = '\\';
+ *d++ = '0' + ((((uint8_t) *s) >> 6) & 7);
+ *d++ = '0' + ((((uint8_t) *s) >> 3) & 7);
+ *d++ = '0' + ( ((uint8_t) *s) & 7);
+ } else {
+ *d++ = *s;
+ }
+ break;
+ }
+ s++;
+ }
+
+ *d = 0;
+ return dest;
+}
+
+static inline int readhex(const char *s, int w)
+{
+ int o = 0;
+
+ while (w--) {
+ o <<= 4;
+ switch (*s) {
+ case '0'...'9': o |= *s - '0'; break;
+ case 'a'...'f': o |= *s - 'a' + 10; break;
+ case 'A'...'F': o |= *s - 'A' + 10; break;
+ default: return -1;
+ }
+ s++;
+ }
+ return o;
+}
+
+/* opposite of str_escape. (this is glib's 'compress' function renamed more clearly) */
+char *str_unescape(const char *s)
+{
+ const char *end;
+ int hex;
+ char *dest = calloc(strlen(s) + 1, sizeof(char));
+ char *d = dest;
+
+ while (*s) {
+ if (*s == '\\') {
+ s++;
+ switch (*s) {
+ case '0'...'7':
+ *d = 0;
+ end = s + 3;
+ while (s < end && *s >= '0' && *s <= '7') {
+ *d = *d * 8 + *s - '0';
+ s++;
+ }
+ d++;
+ s--;
+ break;
+ case 'a':
+ *d++ = '\a';
+ break;
+ case 'b':
+ *d++ = '\b';
+ break;
+ case 'f':
+ *d++ = '\f';
+ break;
+ case 'n':
+ *d++ = '\n';
+ break;
+ case 'r':
+ *d++ = '\r';
+ break;
+ case 't':
+ *d++ = '\t';
+ break;
+ case 'v':
+ *d++ = '\v';
+ break;
+ case '\0': // trailing backslash?
+ *d++ = '\\';
+ s--;
+ break;
+ case 'x':
+ hex = readhex(s + 1, 2);
+ if (hex >= 0) {
+ *d++ = hex;
+ s += 2;
+ break;
+ }
+ /* fall through */
+ default: /* Also handles any other char, like \" \\ \; etc. */
+ *d++ = *s;
+ break;
+ }
+ } else {
+ *d++ = *s;
+ }
+ s++;
+ }
+ *d = 0;
+
+ return dest;
+}
+
+char *pretty_name(const char *filename)
+{
+ char *ret, *temp;
+ const char *ptr;
+ int len;
+
+ ptr = strrchr(filename, DIR_SEPARATOR);
+ ptr = ((ptr && ptr[1]) ? ptr + 1 : filename);
+ len = strrchr(ptr, '.') - ptr;
+ if (len <= 0) {
+ ret = str_dup(ptr);
+ } else {
+ ret = calloc(len + 1, sizeof(char));
+ strncpy(ret, ptr, len);
+ ret[len] = 0;
+ }
+
+ /* change underscores to spaces (of course, this could be adapted
+ * to use strpbrk and strip any number of characters) */
+ while ((temp = strchr(ret, '_')) != NULL)
+ *temp = ' ';
+
+ /* TODO | the first letter, and any letter following a space,
+ * TODO | should be capitalized; multiple spaces should be cut
+ * TODO | down to one */
+
+ trim_string(ret);
+ return ret;
+}
+
+/* blecch */
+int get_num_lines(const char *text)
+{
+ const char *ptr = text;
+ int n = 0;
+
+ if (!text)
+ return 0;
+ for (;;) {
+ ptr = strpbrk(ptr, "\015\012");
+ if (!ptr)
+ return n;
+ if (ptr[0] == 13 && ptr[1] == 10)
+ ptr += 2;
+ else
+ ptr++;
+ n++;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* FILE INFO FUNCTIONS */
+
+/* 0 = success, !0 = failed (check errno) */
+int make_backup_file(const char *filename, int numbered)
+{
+ char buf[PATH_MAX];
+
+ /* ensure plenty of room to breathe */
+ if (strlen(filename) > PATH_MAX - 16) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ if (numbered) {
+ /* If some crazy person needs more than 65536 backup files,
+ they probably have more serious issues to tend to. */
+ int n = 1, ret;
+ do {
+ sprintf(buf, "%s.%d~", filename, n++);
+ ret = rename_file(filename, buf, 0);
+ } while (ret != 0 && errno == EEXIST && n < 65536);
+ return ret;
+ } else {
+ strcpy(buf, filename);
+ strcat(buf, "~");
+ return rename_file(filename, buf, 1);
+ }
+}
+
+long file_size(const char *filename)
+{
+ struct stat buf;
+
+ if (stat(filename, &buf) < 0) {
+ return EOF;
+ }
+ if (S_ISDIR(buf.st_mode)) {
+ errno = EISDIR;
+ return EOF;
+ }
+ return buf.st_size;
+}
+
+/* --------------------------------------------------------------------- */
+/* FILESYSTEM FUNCTIONS */
+
+int is_directory(const char *filename)
+{
+ struct stat buf;
+
+ if (stat(filename, &buf) == -1) {
+ /* Well, at least we tried. */
+ return 0;
+ }
+
+ return S_ISDIR(buf.st_mode);
+}
+
+char *get_current_directory(void)
+{
+ char buf[PATH_MAX + 1];
+
+ /* hmm. fall back to the current dir */
+ if (getcwd(buf, PATH_MAX))
+ return str_dup(buf);
+ return str_dup(".");
+}
+
+/* this function is horrible */
+char *get_home_directory(void)
+{
+ char buf[PATH_MAX + 1];
+
+#if defined(__amigaos4__)
+ return str_dup("PROGDIR:");
+#elif defined(WIN32)
+ if (SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, buf) == ERROR_SUCCESS)
+ return strdup(buf);
+#else
+ char *ptr = getenv("HOME");
+ if (ptr)
+ return str_dup(ptr);
+#endif
+
+ /* hmm. fall back to the current dir */
+ if (getcwd(buf, PATH_MAX))
+ return str_dup(buf);
+
+ /* still don't have a directory? sheesh. */
+ return str_dup(FALLBACK_DIR);
+}
+
+char *get_dot_directory(void)
+{
+#ifdef WIN32
+ char buf[PATH_MAX + 1];
+ if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, buf) == ERROR_SUCCESS)
+ return strdup(buf);
+ // else fall back to home (but if this ever happens, things are really screwed...)
+#endif
+ return get_home_directory();
+}
+
+char *str_concat(const char *s, ...)
+{
+ va_list ap;
+ char *out = NULL;
+ int len = 0;
+
+ va_start(ap,s);
+ while (s) {
+ out = mem_realloc(out, (len += strlen(s)+1));
+ strcat(out, s);
+ s = va_arg(ap, const char *);
+ }
+ va_end(ap);
+ return out;
+
+}
+
+void unset_env_var(const char *key)
+{
+#ifdef HAVE_UNSETENV
+ unsetenv(key);
+#else
+ /* assume POSIX-style semantics */
+ putenv(key);
+#endif
+}
+
+void put_env_var(const char *key, const char *value)
+{
+ char *x;
+ x = mem_alloc(strlen(key) + strlen(value)+2);
+ sprintf(x, "%s=%s", key,value);
+ if (putenv(x) == -1) {
+ perror("putenv");
+ exit(255); /* memory exception */
+ }
+}
+
+/* fast integer sqrt */
+unsigned int i_sqrt(unsigned int r)
+{
+ unsigned int t, b, c=0;
+ for (b = 0x10000000; b != 0; b >>= 2) {
+ t = c + b;
+ c >>= 1;
+ if (t <= r) {
+ r -= t;
+ c += b;
+ }
+ }
+ return(c);
+}
+
+int run_hook(const char *dir, const char *name, const char *maybe_arg)
+{
+#ifdef WIN32
+ char buf[PATH_MAX];
+ const char *ptr;
+ char buf2[PATH_MAX];
+ struct stat sb;
+ int r;
+
+ if (!GetCurrentDirectory(PATH_MAX-1,buf)) return 0;
+ snprintf(buf2, PATH_MAX-2, "%s.bat", name);
+ if (chdir(dir) == -1) return 0;
+ if (stat(buf2, &sb) == -1) {
+ r = 0;
+ } else {
+ ptr = getenv("COMSPEC") ?: "command.com";
+ r = _spawnlp(_P_WAIT, ptr, ptr, "/c", buf2, maybe_arg, 0);
+ }
+ SetCurrentDirectory(buf);
+ chdir(buf);
+ if (r == 0) return 1;
+ return 0;
+#elif defined(GEKKO)
+ // help how do I operating system
+ (void) dir;
+ (void) name;
+ (void) maybe_arg;
+ return 0;
+#else
+ char *tmp;
+ int st;
+
+ switch (fork()) {
+ case -1: return 0;
+ case 0:
+ if (chdir(dir) == -1) _exit(255);
+ tmp = malloc(strlen(name)+4);
+ if (!tmp) _exit(255);
+ sprintf(tmp, "./%s", name);
+ execl(tmp, tmp, maybe_arg, NULL);
+ free(tmp);
+ _exit(255);
+ };
+ while (wait(&st) == -1) {
+ }
+ if (WIFEXITED(st) && WEXITSTATUS(st) == 0) return 1;
+ return 0;
+#endif
+}
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+static int _rename_nodestroy(const char *old, const char *new)
+{
+/* XXX does __amigaos4__ have a special need for this? */
+#ifdef WIN32
+ /* is this code not finished? it never returns success */
+ UINT em = SetErrorMode(0);
+ if (!MoveFile(old, new)) {
+ switch (GetLastError()) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ SetErrorMode(em);
+ errno = EEXIST;
+ return -1;
+ };
+ SetErrorMode(em);
+ return -1;
+ }
+ SetErrorMode(em);
+ return 0;
+#else
+ if (link(old, new) == -1) {
+ return -1;
+ }
+ if (unlink(old) == -1) {
+ /* This can occur when people are using a system with
+ broken link() semantics, or if the user can create files
+ that he cannot remove. these systems are decidedly not POSIX.1
+ but they may try to compile schism, and we won't know they
+ are broken unless we warn them.
+ */
+ fprintf(stderr, "link() succeeded, but unlink() failed. something is very wrong\n");
+ }
+ return 0;
+#endif
+}
+
+/* 0 = success, !0 = failed (check errno) */
+int rename_file(const char *old, const char *new, int overwrite)
+{
+ if (!overwrite)
+ return _rename_nodestroy(old, new);
+
+#ifdef WIN32
+ UINT em;
+ em = SetErrorMode(0);
+ if (MoveFile(old, new)) {
+ win32_filecreated_callback(new);
+ return 0;
+ }
+ switch (GetLastError()) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ break;
+ default:
+ /* eh... */
+ SetErrorMode(em);
+ return -1;
+ };
+
+ if (MoveFileEx(old, new, MOVEFILE_REPLACE_EXISTING)) {
+ /* yay */
+ SetErrorMode(em);
+ return 0;
+ }
+ /* this sometimes work with win95 and novell shares */
+ chmod(new, 0777);
+ chmod(old, 0777);
+ /* more junk */
+ SetFileAttributesA(new, FILE_ATTRIBUTE_NORMAL);
+ SetFileAttributesA(new, FILE_ATTRIBUTE_TEMPORARY);
+
+ if (MoveFile(old, new)) {
+ /* err.. yay! */
+ win32_filecreated_callback(new);
+ SetErrorMode(em);
+ return 0;
+ }
+ /* okay, let's try again */
+ if (!DeleteFileA(new)) {
+ /* no chance! */
+ SetErrorMode(em);
+ return -1;
+ }
+ if (MoveFile(old, new)) {
+ /* .... */
+ win32_filecreated_callback(new);
+ SetErrorMode(em);
+ return 0;
+ }
+ /* alright, thems the breaks. win95 eats your files,
+ and not a damn thing I can do about it.
+ */
+ SetErrorMode(em);
+ return -1;
+#else
+ int r = rename(old, new);
+ if (r != 0 && errno == EEXIST) {
+ /* Broken rename()? Try smashing the old file first,
+ and hope *that* doesn't also fail ;) */
+ if (unlink(old) != 0 || rename(old, new) == -1)
+ return -1;
+ }
+ return r;
+#endif
+}
© All Rights Reserved