diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2017-05-26 21:51:04 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2017-05-26 22:48:09 -0700 |
commit | 78f8fce7f286fd0c71774e2567404ed51f24fef3 (patch) | |
tree | f3de4987f7a9fc1bc03331e97b65a851b041051a /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')
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 Binary files differnew file mode 100644 index 0000000..8ed3637 --- /dev/null +++ b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/appIcon.icns diff --git a/src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icns b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icns Binary files differnew file mode 100644 index 0000000..597056b --- /dev/null +++ b/src/sys/macosx/Schism_Tracker.app/Contents/Resources/moduleIcon.icns 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 Binary files differnew file mode 100644 index 0000000..2184107 --- /dev/null +++ b/src/sys/wii/data/certs.bin diff --git a/src/sys/wii/data/su_tik.bin b/src/sys/wii/data/su_tik.bin Binary files differnew file mode 100644 index 0000000..eb2bd12 --- /dev/null +++ b/src/sys/wii/data/su_tik.bin diff --git a/src/sys/wii/data/su_tmd.bin b/src/sys/wii/data/su_tmd.bin Binary files differnew file mode 100644 index 0000000..7f68618 --- /dev/null +++ b/src/sys/wii/data/su_tmd.bin 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 Binary files differnew file mode 100644 index 0000000..ccab9c5 --- /dev/null +++ b/src/sys/wii/schismtracker/icon.png 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 +} |