diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 7b165c6..9e669e3 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -25,6 +25,7 @@ INTERFACE streamsources/music_dumb.cpp streamsources/music_gme.cpp streamsources/music_libsndfile.cpp + streamsources/music_libxmp.cpp streamsources/music_opl.cpp streamsources/music_xa.cpp musicformats/music_stream.cpp @@ -127,7 +128,7 @@ if(WIN32) ) endif() -target_link_libraries(zmusic-obj INTERFACE dumb gme miniz ${CMAKE_DL_LIBS}) +target_link_libraries(zmusic-obj INTERFACE dumb gme libxmp miniz ${CMAKE_DL_LIBS}) target_include_directories(zmusic-obj INTERFACE diff --git a/source/streamsources/music_libxmp.cpp b/source/streamsources/music_libxmp.cpp new file mode 100644 index 0000000..fe8ab52 --- /dev/null +++ b/source/streamsources/music_libxmp.cpp @@ -0,0 +1,176 @@ +/* +** music_libxmp.cpp +** libxmp module player. +** +**--------------------------------------------------------------------------- +** Copyright 2024 Cacodemon345 +** All rights reserved. +** +** 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. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include +#include +#include "streamsource.h" + +#define LIBXMP_STATIC 1 +#include "../libxmp/include/xmp.h" +#include "zmusic/m_swap.h" +#include "zmusic/mididefs.h" +#include "zmusic/midiconfig.h" +#include "fileio.h" + +extern DumbConfig dumbConfig; + +static unsigned long xmp_read(void *dest, unsigned long len, unsigned long nmemb, void *priv) +{ + if (len == 0 || nmemb == 0) + return (unsigned long)0; + + MusicIO::FileInterface* interface = (MusicIO::FileInterface*)priv; + + auto origpos = interface->tell(); + auto length = interface->read(dest, (int32_t)(len * nmemb)); + + if (length != len * nmemb) + { + // Let's hope the compiler doesn't misoptimize this. + interface->seek(origpos + (length / len) * len, SEEK_SET); + } + return length / len; +} + +static struct xmp_callbacks callbacks = +{ + xmp_read, + [](void *priv, long offset, int whence) -> int { return ((MusicIO::FileInterface*)priv)->seek(offset, whence); }, + [](void *priv) -> long { return ((MusicIO::FileInterface*)priv)->tell(); }, + [](void *priv) -> int { return 0; } +}; + +class XMPSong : public StreamSource +{ +private: + xmp_context context = nullptr; + int samplerate = 44100; + int subsong = 0; + + // libxmp can't output in float. + std::vector int16_buffer; + +public: + XMPSong(xmp_context ctx, int samplerate); + ~XMPSong(); + bool SetSubsong(int subsong) override; + bool Start() override; + SoundStreamInfoEx GetFormatEx() override; + +protected: + bool GetData(void *buffer, size_t len) override; +}; + +XMPSong::XMPSong(xmp_context ctx, int rate) +{ + context = ctx; + samplerate = (dumbConfig.mod_samplerate != 0) ? dumbConfig.mod_samplerate : rate; + xmp_set_player(context, XMP_PLAYER_VOLUME, 100); + xmp_set_player(context, XMP_PLAYER_INTERP, dumbConfig.mod_interp); + + int16_buffer.reserve(16 * 1024); +} + +XMPSong::~XMPSong() +{ + xmp_end_player(context); + xmp_free_context(context); +} + +SoundStreamInfoEx XMPSong::GetFormatEx() +{ + return { 32 * 1024, samplerate, SampleType_Float32, ChannelConfig_Stereo }; +} + +bool XMPSong::SetSubsong(int subsong) +{ + this->subsong = subsong; + if (xmp_get_player(context, XMP_PLAYER_STATE) >= XMP_STATE_PLAYING) + return xmp_set_position(context, subsong) >= 0; + return true; +} + +bool XMPSong::GetData(void *buffer, size_t len) +{ + if ((len / 4) < int16_buffer.size()) + int16_buffer.resize(len / 4); + + int ret = xmp_play_buffer(context, (void*)int16_buffer.data(), len / 2, 0); + xmp_set_player(context, XMP_PLAYER_INTERP, dumbConfig.mod_interp); + + if (ret >= 0) + { + float* soundbuffer = (float*)buffer; + for (unsigned int i = 0; i < len / 4; i++) + { + soundbuffer[i] = ((int16_buffer[i] < 0.) ? (int16_buffer[i] / 32768.) : (int16_buffer[i] / 32767.)) * dumbConfig.mod_dumb_mastervolume; + } + } + + if (ret < 0 && m_Looping) + { + xmp_restart_module(context); + xmp_set_position(context, subsong); + return true; + } + + return ret >= 0; +} + +bool XMPSong::Start() +{ + return xmp_start_player(context, samplerate, 0) >= 0; +} + +StreamSource* XMP_OpenSong(MusicIO::FileInterface* reader, int samplerate) +{ + if (xmp_test_module_from_callbacks((void*)reader, callbacks, nullptr) < 0) + return nullptr; + + xmp_context ctx = xmp_create_context(); + if (!ctx) + return nullptr; + + reader->seek(0, SEEK_SET); + + if (xmp_load_module_from_callbacks(ctx, (void*)reader, callbacks) < 0) + { + return nullptr; + } + + return new XMPSong(ctx, samplerate); +} + diff --git a/source/streamsources/streamsource.h b/source/streamsources/streamsource.h index 633d381..9f31a36 100644 --- a/source/streamsources/streamsource.h +++ b/source/streamsources/streamsource.h @@ -33,6 +33,7 @@ class StreamSource StreamSource *MOD_OpenSong(MusicIO::FileInterface* reader, int samplerate); +StreamSource *XMP_OpenSong(MusicIO::FileInterface* reader, int samplerate); StreamSource* GME_OpenSong(MusicIO::FileInterface* reader, const char* fmt, int sample_rate); StreamSource *SndFile_OpenSong(MusicIO::FileInterface* fr); StreamSource* XA_OpenSong(MusicIO::FileInterface* reader); diff --git a/source/zmusic/zmusic.cpp b/source/zmusic/zmusic.cpp index 6dcda86..a0a8ab0 100644 --- a/source/zmusic/zmusic.cpp +++ b/source/zmusic/zmusic.cpp @@ -257,10 +257,19 @@ static MusInfo *ZMusic_OpenSongInternal (MusicIO::FileInterface *reader, EMidiD streamsource = GME_OpenSong(reader, fmt, miscConfig.snd_outputrate); } // Check for module formats - else + else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('D', 'S', 'M', 'F'))) { streamsource = MOD_OpenSong(reader, miscConfig.snd_outputrate); } + else + { + streamsource = XMP_OpenSong(reader, miscConfig.snd_outputrate); + if (!streamsource) { + ZMusic_Printf(ZMUSIC_MSG_WARNING, "Fallback to DUMB\n"); + reader->seek(0, SEEK_SET); + streamsource = MOD_OpenSong(reader, miscConfig.snd_outputrate); + } + } if (streamsource == nullptr) { streamsource = SndFile_OpenSong(reader); // this only takes over the reader if it succeeds. We need to look out for this. diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 6c7b6fd..dd791db 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -27,4 +27,5 @@ add_subdirectory(timidity) add_subdirectory(timidityplus) add_subdirectory(wildmidi) add_subdirectory(oplsynth) +add_subdirectory(libxmp) add_subdirectory(fluidsynth/src) diff --git a/thirdparty/libxmp/CMakeLists.txt b/thirdparty/libxmp/CMakeLists.txt new file mode 100644 index 0000000..1567c3f --- /dev/null +++ b/thirdparty/libxmp/CMakeLists.txt @@ -0,0 +1,102 @@ +make_release_only() + +add_library(libxmp OBJECT + src/virtual.c + src/format.c + src/period.c + src/player.c + src/read_event.c + src/dataio.c + src/misc.c + src/mkstemp.c + src/md5.c + src/lfo.c + src/scan.c + src/control.c + src/far_extras.c + src/med_extras.c + src/filter.c + src/effects.c + src/mixer.c + src/mix_all.c + src/load_helpers.c + src/load.c + src/hio.c + src/hmn_extras.c + src/extras.c + src/smix.c + src/filetype.c + src/memio.c + src/tempfile.c + src/mix_paula.c + src/win32.c + src/loaders/common.c + src/loaders/iff.c + src/loaders/itsex.c + src/loaders/lzw.c + src/loaders/voltable.c + src/loaders/sample.c + src/loaders/vorbis.c + src/loaders/xm_load.c + src/loaders/mod_load.c + src/loaders/s3m_load.c + src/loaders/stm_load.c + src/loaders/669_load.c + src/loaders/far_load.c + src/loaders/mtm_load.c + src/loaders/ptm_load.c + src/loaders/okt_load.c + src/loaders/ult_load.c + src/loaders/mdl_load.c + src/loaders/it_load.c + src/loaders/stx_load.c + src/loaders/pt3_load.c + src/loaders/sfx_load.c + src/loaders/flt_load.c + src/loaders/st_load.c + src/loaders/emod_load.c + src/loaders/imf_load.c + src/loaders/digi_load.c + src/loaders/fnk_load.c + src/loaders/ice_load.c + src/loaders/liq_load.c + src/loaders/ims_load.c + src/loaders/masi_load.c + src/loaders/masi16_load.c + src/loaders/amf_load.c + src/loaders/stim_load.c + src/loaders/mmd_common.c + src/loaders/mmd1_load.c + src/loaders/mmd3_load.c + src/loaders/rtm_load.c + src/loaders/dt_load.c + src/loaders/no_load.c + src/loaders/arch_load.c + src/loaders/sym_load.c + src/loaders/med2_load.c + src/loaders/med3_load.c + src/loaders/med4_load.c + src/loaders/dbm_load.c + src/loaders/umx_load.c + src/loaders/gdm_load.c + src/loaders/pw_load.c + src/loaders/gal5_load.c + src/loaders/gal4_load.c + src/loaders/mfp_load.c + src/loaders/asylum_load.c + src/loaders/muse_load.c + src/loaders/hmn_load.c + src/loaders/mgt_load.c + src/loaders/chip_load.c + src/loaders/abk_load.c + src/loaders/coco_load.c + src/loaders/xmf_load.c +) + +use_fast_math(libxmp) + +target_include_directories(libxmp PRIVATE include ../miniz/) +target_compile_definitions(libxmp PRIVATE -DHAVE_POWF=1 -DLIBXMP_STATIC=1 -DLIBXMP_NO_PROWIZARD=1 -DLIBXMP_NO_DEPACKERS=1) +if (NOT WIN32) + target_compile_definitions(libxmp PRIVATE -DHAVE_DIRENT=1 -DHAVE_UNISTD=1) +endif () diff --git a/thirdparty/libxmp/include/xmp.h b/thirdparty/libxmp/include/xmp.h new file mode 100644 index 0000000..22db2a3 --- /dev/null +++ b/thirdparty/libxmp/include/xmp.h @@ -0,0 +1,407 @@ +#ifndef XMP_H +#define XMP_H + +#if defined(EMSCRIPTEN) +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XMP_VERSION "4.6.0" +#define XMP_VERCODE 0x040600 +#define XMP_VER_MAJOR 4 +#define XMP_VER_MINOR 6 +#define XMP_VER_RELEASE 0 + +#if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# elif defined(BUILDING_DLL) +# define LIBXMP_EXPORT __declspec(dllexport) +# else +# define LIBXMP_EXPORT __declspec(dllimport) +# endif +#elif defined(__OS2__) && defined(__WATCOMC__) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# elif defined(BUILDING_DLL) +# define LIBXMP_EXPORT __declspec(dllexport) +# else +# define LIBXMP_EXPORT +# endif +#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# else +# define LIBXMP_EXPORT __attribute__((visibility("default"))) +# endif +#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# else +# define LIBXMP_EXPORT __global +# endif +#elif defined(EMSCRIPTEN) +# define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE +# define LIBXMP_EXPORT_VAR +#else +# define LIBXMP_EXPORT +#endif + +#if !defined(LIBXMP_EXPORT_VAR) +# define LIBXMP_EXPORT_VAR LIBXMP_EXPORT +#endif + +#define XMP_NAME_SIZE 64 /* Size of module name and type */ + +#define XMP_KEY_OFF 0x81 /* Note number for key off event */ +#define XMP_KEY_CUT 0x82 /* Note number for key cut event */ +#define XMP_KEY_FADE 0x83 /* Note number for fade event */ + +/* mixer parameter macros */ + +/* sample format flags */ +#define XMP_FORMAT_8BIT (1 << 0) /* Mix to 8-bit instead of 16 */ +#define XMP_FORMAT_UNSIGNED (1 << 1) /* Mix to unsigned samples */ +#define XMP_FORMAT_MONO (1 << 2) /* Mix to mono instead of stereo */ + +/* player parameters */ +#define XMP_PLAYER_AMP 0 /* Amplification factor */ +#define XMP_PLAYER_MIX 1 /* Stereo mixing */ +#define XMP_PLAYER_INTERP 2 /* Interpolation type */ +#define XMP_PLAYER_DSP 3 /* DSP effect flags */ +#define XMP_PLAYER_FLAGS 4 /* Player flags */ +#define XMP_PLAYER_CFLAGS 5 /* Player flags for current module */ +#define XMP_PLAYER_SMPCTL 6 /* Sample control flags */ +#define XMP_PLAYER_VOLUME 7 /* Player module volume */ +#define XMP_PLAYER_STATE 8 /* Internal player state (read only) */ +#define XMP_PLAYER_SMIX_VOLUME 9 /* SMIX volume */ +#define XMP_PLAYER_DEFPAN 10 /* Default pan setting */ +#define XMP_PLAYER_MODE 11 /* Player personality */ +#define XMP_PLAYER_MIXER_TYPE 12 /* Current mixer (read only) */ +#define XMP_PLAYER_VOICES 13 /* Maximum number of mixer voices */ + +/* interpolation types */ +#define XMP_INTERP_NEAREST 0 /* Nearest neighbor */ +#define XMP_INTERP_LINEAR 1 /* Linear (default) */ +#define XMP_INTERP_SPLINE 2 /* Cubic spline */ + +/* dsp effect types */ +#define XMP_DSP_LOWPASS (1 << 0) /* Lowpass filter effect */ +#define XMP_DSP_ALL (XMP_DSP_LOWPASS) + +/* player state */ +#define XMP_STATE_UNLOADED 0 /* Context created */ +#define XMP_STATE_LOADED 1 /* Module loaded */ +#define XMP_STATE_PLAYING 2 /* Module playing */ + +/* player flags */ +#define XMP_FLAGS_VBLANK (1 << 0) /* Use vblank timing */ +#define XMP_FLAGS_FX9BUG (1 << 1) /* Emulate FX9 bug */ +#define XMP_FLAGS_FIXLOOP (1 << 2) /* Emulate sample loop bug */ +#define XMP_FLAGS_A500 (1 << 3) /* Use Paula mixer in Amiga modules */ + +/* player modes */ +#define XMP_MODE_AUTO 0 /* Autodetect mode (default) */ +#define XMP_MODE_MOD 1 /* Play as a generic MOD player */ +#define XMP_MODE_NOISETRACKER 2 /* Play using Noisetracker quirks */ +#define XMP_MODE_PROTRACKER 3 /* Play using Protracker quirks */ +#define XMP_MODE_S3M 4 /* Play as a generic S3M player */ +#define XMP_MODE_ST3 5 /* Play using ST3 bug emulation */ +#define XMP_MODE_ST3GUS 6 /* Play using ST3+GUS quirks */ +#define XMP_MODE_XM 7 /* Play as a generic XM player */ +#define XMP_MODE_FT2 8 /* Play using FT2 bug emulation */ +#define XMP_MODE_IT 9 /* Play using IT quirks */ +#define XMP_MODE_ITSMP 10 /* Play using IT sample mode quirks */ + +/* mixer types */ +#define XMP_MIXER_STANDARD 0 /* Standard mixer */ +#define XMP_MIXER_A500 1 /* Amiga 500 */ +#define XMP_MIXER_A500F 2 /* Amiga 500 with led filter */ + +/* sample flags */ +#define XMP_SMPCTL_SKIP (1 << 0) /* Don't load samples */ + +/* limits */ +#define XMP_MAX_KEYS 121 /* Number of valid keys */ +#define XMP_MAX_ENV_POINTS 32 /* Max number of envelope points */ +#define XMP_MAX_MOD_LENGTH 256 /* Max number of patterns in module */ +#define XMP_MAX_CHANNELS 64 /* Max number of channels in module */ +#define XMP_MAX_SRATE 49170 /* max sampling rate (Hz) */ +#define XMP_MIN_SRATE 4000 /* min sampling rate (Hz) */ +#define XMP_MIN_BPM 20 /* min BPM */ +/* frame rate = (50 * bpm / 125) Hz */ +/* frame size = (sampling rate * channels * size) / frame rate */ +#define XMP_MAX_FRAMESIZE (5 * XMP_MAX_SRATE * 2 / XMP_MIN_BPM) + +/* error codes */ +#define XMP_END 1 +#define XMP_ERROR_INTERNAL 2 /* Internal error */ +#define XMP_ERROR_FORMAT 3 /* Unsupported module format */ +#define XMP_ERROR_LOAD 4 /* Error loading file */ +#define XMP_ERROR_DEPACK 5 /* Error depacking file */ +#define XMP_ERROR_SYSTEM 6 /* System error */ +#define XMP_ERROR_INVALID 7 /* Invalid parameter */ +#define XMP_ERROR_STATE 8 /* Invalid player state */ + +struct xmp_channel { + int pan; /* Channel pan (0x80 is center) */ + int vol; /* Channel volume */ +#define XMP_CHANNEL_SYNTH (1 << 0) /* Channel is synthesized */ +#define XMP_CHANNEL_MUTE (1 << 1) /* Channel is muted */ +#define XMP_CHANNEL_SPLIT (1 << 2) /* Split Amiga channel in bits 5-4 */ +#define XMP_CHANNEL_SURROUND (1 << 4) /* Surround channel */ + int flg; /* Channel flags */ +}; + +struct xmp_pattern { + int rows; /* Number of rows */ + int index[1]; /* Track index */ +}; + +struct xmp_event { + unsigned char note; /* Note number (0 means no note) */ + unsigned char ins; /* Patch number */ + unsigned char vol; /* Volume (0 to basevol) */ + unsigned char fxt; /* Effect type */ + unsigned char fxp; /* Effect parameter */ + unsigned char f2t; /* Secondary effect type */ + unsigned char f2p; /* Secondary effect parameter */ + unsigned char _flag; /* Internal (reserved) flags */ +}; + +struct xmp_track { + int rows; /* Number of rows */ + struct xmp_event event[1]; /* Event data */ +}; + +struct xmp_envelope { +#define XMP_ENVELOPE_ON (1 << 0) /* Envelope is enabled */ +#define XMP_ENVELOPE_SUS (1 << 1) /* Envelope has sustain point */ +#define XMP_ENVELOPE_LOOP (1 << 2) /* Envelope has loop */ +#define XMP_ENVELOPE_FLT (1 << 3) /* Envelope is used for filter */ +#define XMP_ENVELOPE_SLOOP (1 << 4) /* Envelope has sustain loop */ +#define XMP_ENVELOPE_CARRY (1 << 5) /* Don't reset envelope position */ + int flg; /* Flags */ + int npt; /* Number of envelope points */ + int scl; /* Envelope scaling */ + int sus; /* Sustain start point */ + int sue; /* Sustain end point */ + int lps; /* Loop start point */ + int lpe; /* Loop end point */ + short data[XMP_MAX_ENV_POINTS * 2]; +}; + +struct xmp_subinstrument { + int vol; /* Default volume */ + int gvl; /* Global volume */ + int pan; /* Pan */ + int xpo; /* Transpose */ + int fin; /* Finetune */ + int vwf; /* Vibrato waveform */ + int vde; /* Vibrato depth */ + int vra; /* Vibrato rate */ + int vsw; /* Vibrato sweep */ + int rvv; /* Random volume/pan variation (IT) */ + int sid; /* Sample number */ +#define XMP_INST_NNA_CUT 0x00 +#define XMP_INST_NNA_CONT 0x01 +#define XMP_INST_NNA_OFF 0x02 +#define XMP_INST_NNA_FADE 0x03 + int nna; /* New note action */ +#define XMP_INST_DCT_OFF 0x00 +#define XMP_INST_DCT_NOTE 0x01 +#define XMP_INST_DCT_SMP 0x02 +#define XMP_INST_DCT_INST 0x03 + int dct; /* Duplicate check type */ +#define XMP_INST_DCA_CUT XMP_INST_NNA_CUT +#define XMP_INST_DCA_OFF XMP_INST_NNA_OFF +#define XMP_INST_DCA_FADE XMP_INST_NNA_FADE + int dca; /* Duplicate check action */ + int ifc; /* Initial filter cutoff */ + int ifr; /* Initial filter resonance */ +}; + +struct xmp_instrument { + char name[32]; /* Instrument name */ + int vol; /* Instrument volume */ + int nsm; /* Number of samples */ + int rls; /* Release (fadeout) */ + struct xmp_envelope aei; /* Amplitude envelope info */ + struct xmp_envelope pei; /* Pan envelope info */ + struct xmp_envelope fei; /* Frequency envelope info */ + + struct { + unsigned char ins; /* Instrument number for each key */ + signed char xpo; /* Instrument transpose for each key */ + } map[XMP_MAX_KEYS]; + + struct xmp_subinstrument *sub; + + void *extra; /* Extra fields */ +}; + +struct xmp_sample { + char name[32]; /* Sample name */ + int len; /* Sample length */ + int lps; /* Loop start */ + int lpe; /* Loop end */ +#define XMP_SAMPLE_16BIT (1 << 0) /* 16bit sample */ +#define XMP_SAMPLE_LOOP (1 << 1) /* Sample is looped */ +#define XMP_SAMPLE_LOOP_BIDIR (1 << 2) /* Bidirectional sample loop */ +#define XMP_SAMPLE_LOOP_REVERSE (1 << 3) /* Backwards sample loop */ +#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */ +#define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */ +#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */ +#define XMP_SAMPLE_STEREO (1 << 7) /* Interlaced stereo sample */ +#define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */ + int flg; /* Flags */ + unsigned char *data; /* Sample data */ +}; + +struct xmp_sequence { + int entry_point; + int duration; +}; + +struct xmp_module { + char name[XMP_NAME_SIZE]; /* Module title */ + char type[XMP_NAME_SIZE]; /* Module format */ + int pat; /* Number of patterns */ + int trk; /* Number of tracks */ + int chn; /* Tracks per pattern */ + int ins; /* Number of instruments */ + int smp; /* Number of samples */ + int spd; /* Initial speed */ + int bpm; /* Initial BPM */ + int len; /* Module length in patterns */ + int rst; /* Restart position */ + int gvl; /* Global volume */ + + struct xmp_pattern **xxp; /* Patterns */ + struct xmp_track **xxt; /* Tracks */ + struct xmp_instrument *xxi; /* Instruments */ + struct xmp_sample *xxs; /* Samples */ + struct xmp_channel xxc[XMP_MAX_CHANNELS]; /* Channel info */ + unsigned char xxo[XMP_MAX_MOD_LENGTH]; /* Orders */ +}; + +struct xmp_test_info { + char name[XMP_NAME_SIZE]; /* Module title */ + char type[XMP_NAME_SIZE]; /* Module format */ +}; + +struct xmp_module_info { + unsigned char md5[16]; /* MD5 message digest */ + int vol_base; /* Volume scale */ + struct xmp_module *mod; /* Pointer to module data */ + char *comment; /* Comment text, if any */ + int num_sequences; /* Number of valid sequences */ + struct xmp_sequence *seq_data; /* Pointer to sequence data */ +}; + +struct xmp_channel_info { + unsigned int period; /* Sample period (* 4096) */ + unsigned int position; /* Sample position */ + short pitchbend; /* Linear bend from base note*/ + unsigned char note; /* Current base note number */ + unsigned char instrument; /* Current instrument number */ + unsigned char sample; /* Current sample number */ + unsigned char volume; /* Current volume */ + unsigned char pan; /* Current stereo pan */ + unsigned char reserved; /* Reserved */ + struct xmp_event event; /* Current track event */ +}; + +struct xmp_frame_info { /* Current frame information */ + int pos; /* Current position */ + int pattern; /* Current pattern */ + int row; /* Current row in pattern */ + int num_rows; /* Number of rows in current pattern */ + int frame; /* Current frame */ + int speed; /* Current replay speed */ + int bpm; /* Current bpm */ + int time; /* Current module time in ms */ + int total_time; /* Estimated replay time in ms*/ + int frame_time; /* Frame replay time in us */ + void *buffer; /* Pointer to sound buffer */ + int buffer_size; /* Used buffer size */ + int total_size; /* Total buffer size */ + int volume; /* Current master volume */ + int loop_count; /* Loop counter */ + int virt_channels; /* Number of virtual channels */ + int virt_used; /* Used virtual channels */ + int sequence; /* Current sequence */ + + struct xmp_channel_info channel_info[XMP_MAX_CHANNELS]; /* Current channel information */ +}; + +struct xmp_callbacks { + unsigned long (*read_func)(void *dest, unsigned long len, + unsigned long nmemb, void *priv); + int (*seek_func)(void *priv, long offset, int whence); + long (*tell_func)(void *priv); + int (*close_func)(void *priv); +}; + +typedef char *xmp_context; + +LIBXMP_EXPORT_VAR extern const char *xmp_version; +LIBXMP_EXPORT_VAR extern const unsigned int xmp_vercode; + +LIBXMP_EXPORT int xmp_syserrno (void); + +LIBXMP_EXPORT xmp_context xmp_create_context (void); +LIBXMP_EXPORT void xmp_free_context (xmp_context); + +LIBXMP_EXPORT int xmp_load_module (xmp_context, const char *); +LIBXMP_EXPORT int xmp_load_module_from_memory (xmp_context, const void *, long); +LIBXMP_EXPORT int xmp_load_module_from_file (xmp_context, void *, long); +LIBXMP_EXPORT int xmp_load_module_from_callbacks (xmp_context, void *, struct xmp_callbacks); + +LIBXMP_EXPORT int xmp_test_module (const char *, struct xmp_test_info *); +LIBXMP_EXPORT int xmp_test_module_from_memory (const void *, long, struct xmp_test_info *); +LIBXMP_EXPORT int xmp_test_module_from_file (void *, struct xmp_test_info *); +LIBXMP_EXPORT int xmp_test_module_from_callbacks (void *, struct xmp_callbacks, struct xmp_test_info *); + +LIBXMP_EXPORT void xmp_scan_module (xmp_context); +LIBXMP_EXPORT void xmp_release_module (xmp_context); + +LIBXMP_EXPORT int xmp_start_player (xmp_context, int, int); +LIBXMP_EXPORT int xmp_play_frame (xmp_context); +LIBXMP_EXPORT int xmp_play_buffer (xmp_context, void *, int, int); +LIBXMP_EXPORT void xmp_get_frame_info (xmp_context, struct xmp_frame_info *); +LIBXMP_EXPORT void xmp_end_player (xmp_context); +LIBXMP_EXPORT void xmp_inject_event (xmp_context, int, struct xmp_event *); +LIBXMP_EXPORT void xmp_get_module_info (xmp_context, struct xmp_module_info *); +LIBXMP_EXPORT const char *const *xmp_get_format_list (void); +LIBXMP_EXPORT int xmp_next_position (xmp_context); +LIBXMP_EXPORT int xmp_prev_position (xmp_context); +LIBXMP_EXPORT int xmp_set_position (xmp_context, int); +LIBXMP_EXPORT int xmp_set_row (xmp_context, int); +LIBXMP_EXPORT int xmp_set_tempo_factor(xmp_context, double); +LIBXMP_EXPORT void xmp_stop_module (xmp_context); +LIBXMP_EXPORT void xmp_restart_module (xmp_context); +LIBXMP_EXPORT int xmp_seek_time (xmp_context, int); +LIBXMP_EXPORT int xmp_channel_mute (xmp_context, int, int); +LIBXMP_EXPORT int xmp_channel_vol (xmp_context, int, int); +LIBXMP_EXPORT int xmp_set_player (xmp_context, int, int); +LIBXMP_EXPORT int xmp_get_player (xmp_context, int); +LIBXMP_EXPORT int xmp_set_instrument_path (xmp_context, const char *); + +/* External sample mixer API */ +LIBXMP_EXPORT int xmp_start_smix (xmp_context, int, int); +LIBXMP_EXPORT void xmp_end_smix (xmp_context); +LIBXMP_EXPORT int xmp_smix_play_instrument(xmp_context, int, int, int, int); +LIBXMP_EXPORT int xmp_smix_play_sample (xmp_context, int, int, int, int); +LIBXMP_EXPORT int xmp_smix_channel_pan (xmp_context, int, int); +LIBXMP_EXPORT int xmp_smix_load_sample (xmp_context, int, const char *); +LIBXMP_EXPORT int xmp_smix_release_sample (xmp_context, int); + +#ifdef __cplusplus +} +#endif + +#endif /* XMP_H */ diff --git a/thirdparty/libxmp/src/callbackio.h b/thirdparty/libxmp/src/callbackio.h new file mode 100644 index 0000000..de46eb4 --- /dev/null +++ b/thirdparty/libxmp/src/callbackio.h @@ -0,0 +1,187 @@ +#ifndef LIBXMP_CALLBACKIO_H +#define LIBXMP_CALLBACKIO_H + +#include +#include "common.h" + +typedef struct { + void *priv; + struct xmp_callbacks callbacks; + int eof; +} CBFILE; + +LIBXMP_BEGIN_DECLS + +static inline uint8 cbread8(CBFILE *f, int *err) +{ + uint8 x = 0xff; + size_t r = f->callbacks.read_func(&x, 1, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (err) *err = f->eof; + + return x; +} + +static inline int8 cbread8s(CBFILE *f, int *err) +{ + return (int8)cbread8(f, err); +} + +static inline uint16 cbread16l(CBFILE *f, int *err) +{ + uint8 buf[2]; + uint16 x = 0xffff; + size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem16l(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint16 cbread16b(CBFILE *f, int *err) +{ + uint8 buf[2]; + uint16 x = 0xffff; + size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem16b(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread24l(CBFILE *f, int *err) +{ + uint8 buf[3]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem24l(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread24b(CBFILE *f, int *err) +{ + uint8 buf[3]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem24b(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread32l(CBFILE *f, int *err) +{ + uint8 buf[4]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem32l(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread32b(CBFILE *f, int *err) +{ + uint8 buf[4]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem32b(buf); + if (err) *err = f->eof; + + return x; +} + +static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f) +{ + size_t r = f->callbacks.read_func(dest, (unsigned long)len, + (unsigned long)nmemb, f->priv); + f->eof = (r < nmemb) ? EOF : 0; + return r; +} + +static inline int cbseek(CBFILE *f, long offset, int whence) +{ + f->eof = 0; + return f->callbacks.seek_func(f->priv, offset, whence); +} + +static inline long cbtell(CBFILE *f) +{ + return f->callbacks.tell_func(f->priv); +} + +static inline int cbeof(CBFILE *f) +{ + return f->eof; +} + +static inline long cbfilelength(CBFILE *f) +{ + long pos = f->callbacks.tell_func(f->priv); + long length; + int r; + + if (pos < 0) + return EOF; + + r = f->callbacks.seek_func(f->priv, 0, SEEK_END); + if (r < 0) + return EOF; + + length = f->callbacks.tell_func(f->priv); + r = f->callbacks.seek_func(f->priv, pos, SEEK_SET); + + return length; +} + +static inline CBFILE *cbopen(void *priv, struct xmp_callbacks callbacks) +{ + CBFILE *f; + if (priv == NULL || callbacks.read_func == NULL || + callbacks.seek_func == NULL || callbacks.tell_func == NULL) + goto err; + + f = (CBFILE *)calloc(1, sizeof(CBFILE)); + if (f == NULL) + goto err; + + f->priv = priv; + f->callbacks = callbacks; + f->eof = 0; + return f; + + err: + if (priv && callbacks.close_func) + callbacks.close_func(priv); + + return NULL; +} + +static inline int cbclose(CBFILE *f) +{ + int r = 0; + if (f->callbacks.close_func != NULL) + r = f->callbacks.close_func(f->priv); + + free(f); + return r; +} + +LIBXMP_END_DECLS + +#endif /* LIBXMP_CALLBACKIO_H */ diff --git a/thirdparty/libxmp/src/common.h b/thirdparty/libxmp/src/common.h new file mode 100644 index 0000000..0f62151 --- /dev/null +++ b/thirdparty/libxmp/src/common.h @@ -0,0 +1,574 @@ +#ifndef LIBXMP_COMMON_H +#define LIBXMP_COMMON_H + +#ifdef LIBXMP_CORE_PLAYER +#ifndef LIBXMP_NO_PROWIZARD +#define LIBXMP_NO_PROWIZARD +#endif +#ifndef LIBXMP_NO_DEPACKERS +#define LIBXMP_NO_DEPACKERS +#endif +#else +#undef LIBXMP_CORE_DISABLE_IT +#endif + +#include +#include +#include +#include +#include +#include "xmp.h" + +#undef LIBXMP_EXPORT_VAR +#if defined(EMSCRIPTEN) +#include +#define LIBXMP_EXPORT_VAR EMSCRIPTEN_KEEPALIVE +#else +#define LIBXMP_EXPORT_VAR +#endif + +#ifndef __cplusplus +#define LIBXMP_BEGIN_DECLS +#define LIBXMP_END_DECLS +#else +#define LIBXMP_BEGIN_DECLS extern "C" { +#define LIBXMP_END_DECLS } +#endif + +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline +#endif + +#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ + (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ + (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) +#define LIBXMP_RESTRICT __restrict +#else +#define LIBXMP_RESTRICT +#endif + +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__EMX__) +#define XMP_MAXPATH _MAX_PATH +#elif defined(PATH_MAX) +#define XMP_MAXPATH PATH_MAX +#else +#define XMP_MAXPATH 1024 +#endif + +#if defined(__MORPHOS__) || defined(__AROS__) || defined(__AMIGA__) \ + || defined(__amigaos__) || defined(__amigaos4__) || defined(AMIGA) +#define LIBXMP_AMIGA 1 +#endif + +#if defined(_MSC_VER) && defined(__has_include) +#if __has_include() +#define HAVE_WINAPIFAMILY_H 1 +#else +#define HAVE_WINAPIFAMILY_H 0 +#endif +#endif + +#if HAVE_WINAPIFAMILY_H +#include +#define LIBXMP_UWP (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) +#else +#define LIBXMP_UWP 0 +#endif + +#ifdef HAVE_EXTERNAL_VISIBILITY +#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"),externally_visible)) +#else +#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"))) +#endif +#ifdef HAVE_ATTRIBUTE_SYMVER +#define LIBXMP_ATTRIB_SYMVER(_sym) __attribute__((__symver__(_sym))) +#else +#define LIBXMP_ATTRIB_SYMVER(_sym) +#endif + +/* AmigaOS fixes by Chris Young , Nov 25, 2007 + */ +#if defined B_BEOS_VERSION +# include +#elif defined __amigaos4__ +# include +#elif defined _arch_dreamcast /* KallistiOS */ +# include +#else +typedef signed char int8; +typedef signed short int int16; +typedef signed int int32; +typedef unsigned char uint8; +typedef unsigned short int uint16; +typedef unsigned int uint32; +#endif + +#ifdef _MSC_VER /* MSVC6 has no long long */ +typedef signed __int64 int64; +typedef unsigned __int64 uint64; +#elif !(defined(B_BEOS_VERSION) || defined(__amigaos4__)) +typedef unsigned long long uint64; +typedef signed long long int64; +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4100) /* unreferenced formal parameter */ +#endif + +#ifndef LIBXMP_CORE_PLAYER +#define LIBXMP_PAULA_SIMULATOR +#endif + +/* Constants */ +#define PAL_RATE 250.0 /* 1 / (50Hz * 80us) */ +#define NTSC_RATE 208.0 /* 1 / (60Hz * 80us) */ +#define C4_PAL_RATE 8287 /* 7093789.2 / period (C4) * 2 */ +#define C4_NTSC_RATE 8363 /* 7159090.5 / period (C4) * 2 */ + +/* [Amiga] PAL color carrier frequency (PCCF) = 4.43361825 MHz */ +/* [Amiga] CPU clock = 1.6 * PCCF = 7.0937892 MHz */ + +#define DEFAULT_AMPLIFY 1 +#define DEFAULT_MIX 100 + +#define MSN(x) (((x)&0xf0)>>4) +#define LSN(x) ((x)&0x0f) +#define SET_FLAG(a,b) ((a)|=(b)) +#define RESET_FLAG(a,b) ((a)&=~(b)) +#define TEST_FLAG(a,b) !!((a)&(b)) + +/* libxmp_get_filetype() return values */ +#define XMP_FILETYPE_NONE 0 +#define XMP_FILETYPE_DIR (1 << 0) +#define XMP_FILETYPE_FILE (1 << 1) + +#define CLAMP(x,a,b) do { \ + if ((x) < (a)) (x) = (a); \ + else if ((x) > (b)) (x) = (b); \ +} while (0) +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define TRACK_NUM(a,c) m->mod.xxp[a]->index[c] +#define EVENT(a,c,r) m->mod.xxt[TRACK_NUM((a),(c))]->event[r] + +#ifdef _MSC_VER +#define D_CRIT " Error: " +#define D_WARN "Warning: " +#define D_INFO " Info: " +#ifdef DEBUG +#define D_ libxmp_msvc_dbgprint /* in win32.c */ +void libxmp_msvc_dbgprint(const char *text, ...); +#else +/* VS prior to VC7.1 does not support variadic macros. + * VC8.0 does not optimize unused parameters passing. */ +#if _MSC_VER < 1400 +static void __inline D_(const char *text, ...) { + do { } while (0); +} +#else +#define D_(...) do {} while (0) +#endif +#endif + +#elif defined __ANDROID__ + +#ifdef DEBUG +#include +#define D_CRIT " Error: " +#define D_WARN "Warning: " +#define D_INFO " Info: " +#define D_(...) do { \ + __android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \ + } while (0) +#else +#define D_(...) do {} while (0) +#endif + +#else + +#ifdef DEBUG +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define LIBXMP_FUNC __func__ +#else +#define LIBXMP_FUNC __FUNCTION__ +#endif +#define D_INFO "\x1b[33m" +#define D_CRIT "\x1b[31m" +#define D_WARN "\x1b[36m" +#define D_(...) do { \ + printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \ + __FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \ + } while (0) +#else +#define D_(...) do {} while (0) +#endif + +#endif /* !_MSC_VER */ + +#ifdef _MSC_VER +#define dup _dup +#define fileno _fileno +#define strnicmp _strnicmp +#define fdopen _fdopen +#define open _open +#define close _close +#define unlink _unlink +#define S_ISDIR(x) (((x)&_S_IFDIR) != 0) +#endif +#if defined(_WIN32) || defined(__WATCOMC__) /* in win32.c */ +#define USE_LIBXMP_SNPRINTF +/* MSVC 2015+ has C99 compliant snprintf and vsnprintf implementations. + * If __USE_MINGW_ANSI_STDIO is defined for MinGW (which it is by default), + * compliant implementations will be used instead of the broken MSVCRT + * functions. Additionally, GCC may optimize some calls to those functions. */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#undef USE_LIBXMP_SNPRINTF +#endif +#if defined(__MINGW32__) && defined(__USE_MINGW_ANSI_STDIO) && (__USE_MINGW_ANSI_STDIO != 0) +#undef USE_LIBXMP_SNPRINTF +#endif +#ifdef USE_LIBXMP_SNPRINTF +#if defined(__GNUC__) || defined(__clang__) +#define LIBXMP_ATTRIB_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) +#else +#define LIBXMP_ATTRIB_PRINTF(x,y) +#endif +int libxmp_vsnprintf(char *, size_t, const char *, va_list) LIBXMP_ATTRIB_PRINTF(3,0); +int libxmp_snprintf (char *, size_t, const char *, ...) LIBXMP_ATTRIB_PRINTF(3,4); +#define snprintf libxmp_snprintf +#define vsnprintf libxmp_vsnprintf +#endif +#endif + +/* Output file size limit for files unpacked from unarchivers into RAM. Most + * general archive compression formats can't nicely bound the output size + * from their input filesize, and a cap is needed for a few reasons: + * + * - Linux is too dumb for its own good and its malloc/realloc will return + * pointers to RAM that doesn't exist instead of NULL. When these are used, + * it will kill the application instead of allowing it to fail gracefully. + * - libFuzzer and the clang sanitizers have malloc/realloc interceptors that + * terminate with an error instead of returning NULL. + * + * Depackers that have better ways of bounding the output size can ignore this. + * This value is fairly arbitrary and can be changed if needed. + */ +#define LIBXMP_DEPACK_LIMIT (512 << 20) + +/* Quirks */ +#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */ +#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */ +#define QUIRK_PROTRACK (1 << 2) /* Use Protracker-specific quirks */ +#define QUIRK_ST3BUGS (1 << 4) /* Scream Tracker 3 bug compatibility */ +#define QUIRK_FINEFX (1 << 5) /* Enable 0xf/0xe for fine effects */ +#define QUIRK_VSALL (1 << 6) /* Volume slides in all frames */ +#define QUIRK_PBALL (1 << 7) /* Pitch bending in all frames */ +#define QUIRK_PERPAT (1 << 8) /* Cancel persistent fx at pat start */ +#define QUIRK_VOLPDN (1 << 9) /* Set priority to volume slide down */ +#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */ +#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */ +#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */ +#define QUIRK_INVLOOP (1 << 13) /* Enable invert loop */ +/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */ +#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */ +#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */ +#define QUIRK_FILTER (1 << 16) /* Enable filter */ +#define QUIRK_IGSTPOR (1 << 17) /* Ignore stray tone portamento */ +#define QUIRK_KEYOFF (1 << 18) /* Keyoff doesn't reset fadeout */ +#define QUIRK_VIBHALF (1 << 19) /* Vibrato is half as deep */ +#define QUIRK_VIBALL (1 << 20) /* Vibrato in all frames */ +#define QUIRK_VIBINV (1 << 21) /* Vibrato has inverse waveform */ +#define QUIRK_PRENV (1 << 22) /* Portamento resets envelope & fade */ +#define QUIRK_ITOLDFX (1 << 23) /* IT old effects mode */ +#define QUIRK_S3MRTG (1 << 24) /* S3M-style retrig when count == 0 */ +#define QUIRK_RTDELAY (1 << 25) /* Delay effect retrigs instrument */ +#define QUIRK_FT2BUGS (1 << 26) /* FT2 bug compatibility */ +#define QUIRK_MARKER (1 << 27) /* Patterns 0xfe and 0xff reserved */ +#define QUIRK_NOBPM (1 << 28) /* Adjust speed only, no BPM */ +#define QUIRK_ARPMEM (1 << 29) /* Arpeggio has memory (S3M_ARPEGGIO) */ +#define QUIRK_RSTCHN (1 << 30) /* Reset channel on sample end */ + +#define HAS_QUIRK(x) (m->quirk & (x)) + + +/* Format quirks */ +#define QUIRKS_ST3 (QUIRK_S3MLOOP | QUIRK_VOLPDN | QUIRK_FINEFX | \ + QUIRK_S3MRTG | QUIRK_MARKER | QUIRK_RSTCHN ) +#define QUIRKS_FT2 (QUIRK_RTDELAY | QUIRK_FINEFX ) +#define QUIRKS_IT (QUIRK_S3MLOOP | QUIRK_FINEFX | QUIRK_VIBALL | \ + QUIRK_ENVFADE | QUIRK_ITVPOR | QUIRK_KEYOFF | \ + QUIRK_VIRTUAL | QUIRK_FILTER | QUIRK_RSTCHN | \ + QUIRK_IGSTPOR | QUIRK_S3MRTG | QUIRK_MARKER ) + +/* DSP effects */ +#define DSP_EFFECT_CUTOFF 0x02 +#define DSP_EFFECT_RESONANCE 0x03 +#define DSP_EFFECT_FILTER_A0 0xb0 +#define DSP_EFFECT_FILTER_B0 0xb1 +#define DSP_EFFECT_FILTER_B1 0xb2 + +/* Time factor */ +#define DEFAULT_TIME_FACTOR 10.0 +#define MED_TIME_FACTOR 2.64 +#define FAR_TIME_FACTOR 4.01373 /* See far_extras.c */ + +#define MAX_SEQUENCES 255 +#define MAX_SAMPLE_SIZE 0x10000000 +#define MAX_SAMPLES 1024 +#define MAX_INSTRUMENTS 255 +#define MAX_PATTERNS 256 + +#define IS_PLAYER_MODE_MOD() (m->read_event_type == READ_EVENT_MOD) +#define IS_PLAYER_MODE_FT2() (m->read_event_type == READ_EVENT_FT2) +#define IS_PLAYER_MODE_ST3() (m->read_event_type == READ_EVENT_ST3) +#define IS_PLAYER_MODE_IT() (m->read_event_type == READ_EVENT_IT) +#define IS_PLAYER_MODE_MED() (m->read_event_type == READ_EVENT_MED) +#define IS_PERIOD_MODRNG() (m->period_type == PERIOD_MODRNG) +#define IS_PERIOD_LINEAR() (m->period_type == PERIOD_LINEAR) +#define IS_PERIOD_CSPD() (m->period_type == PERIOD_CSPD) + +#define IS_AMIGA_MOD() (IS_PLAYER_MODE_MOD() && IS_PERIOD_MODRNG()) + +struct ord_data { + int speed; + int bpm; + int gvl; + int time; + int start_row; +#ifndef LIBXMP_CORE_PLAYER + int st26_speed; +#endif +}; + + +/* Context */ + +struct smix_data { + int chn; + int ins; + int smp; + struct xmp_instrument *xxi; + struct xmp_sample *xxs; +}; + +/* This will be added to the sample structure in the next API revision */ +struct extra_sample_data { + double c5spd; + int sus; + int sue; +}; + +struct midi_macro { + char data[32]; +}; + +struct midi_macro_data { + struct midi_macro param[16]; + struct midi_macro fixed[128]; +}; + +struct module_data { + struct xmp_module mod; + + char *dirname; /* file dirname */ + char *basename; /* file basename */ + const char *filename; /* Module file name */ + char *comment; /* Comments, if any */ + uint8 md5[16]; /* MD5 message digest */ + int size; /* File size */ + double rrate; /* Replay rate */ + double time_factor; /* Time conversion constant */ + int c4rate; /* C4 replay rate */ + int volbase; /* Volume base */ + int gvolbase; /* Global volume base */ + int gvol; /* Global volume */ + int mvolbase; /* Mix volume base (S3M/IT) */ + int mvol; /* Mix volume (S3M/IT) */ + const int *vol_table; /* Volume translation table */ + int quirk; /* player quirks */ +#define READ_EVENT_MOD 0 +#define READ_EVENT_FT2 1 +#define READ_EVENT_ST3 2 +#define READ_EVENT_IT 3 +#define READ_EVENT_MED 4 + int read_event_type; +#define PERIOD_AMIGA 0 +#define PERIOD_MODRNG 1 +#define PERIOD_LINEAR 2 +#define PERIOD_CSPD 3 + int period_type; + int smpctl; /* sample control flags */ + int defpan; /* default pan setting */ + struct ord_data xxo_info[XMP_MAX_MOD_LENGTH]; + int num_sequences; + struct xmp_sequence seq_data[MAX_SEQUENCES]; + char *instrument_path; + void *extra; /* format-specific extra fields */ + uint8 **scan_cnt; /* scan counters */ + struct extra_sample_data *xtra; + struct midi_macro_data *midi; + int compare_vblank; +}; + + +struct pattern_loop { + int start; + int count; +}; + +struct flow_control { + int pbreak; + int jump; + int delay; + int jumpline; + int loop_chn; +#ifndef LIBXMP_CORE_PLAYER + int jump_in_pat; +#endif + + struct pattern_loop *loop; + + int num_rows; + int end_point; +#define ROWDELAY_ON (1 << 0) +#define ROWDELAY_FIRST_FRAME (1 << 1) + int rowdelay; /* For IT pattern row delay */ + int rowdelay_set; +}; + +struct virt_channel { + int count; + int map; +}; + +struct scan_data { + int time; /* replay time in ms */ + int row; + int ord; + int num; +}; + +struct player_data { + int ord; + int pos; + int row; + int frame; + int speed; + int bpm; + int mode; + int player_flags; + int flags; + + double current_time; + double frame_time; + + int loop_count; + int sequence; + unsigned char sequence_control[XMP_MAX_MOD_LENGTH]; + + int smix_vol; /* SFX volume */ + int master_vol; /* Music volume */ + int gvol; + + struct flow_control flow; + + struct scan_data *scan; + + struct channel_data *xc_data; + + int channel_vol[XMP_MAX_CHANNELS]; + char channel_mute[XMP_MAX_CHANNELS]; + + struct virt_control { + int num_tracks; /* Number of tracks */ + int virt_channels; /* Number of virtual channels */ + int virt_used; /* Number of voices currently in use */ + int maxvoc; /* Number of sound card voices */ + + struct virt_channel *virt_channel; + + struct mixer_voice *voice_array; + } virt; + + struct xmp_event inject_event[XMP_MAX_CHANNELS]; + + struct { + int consumed; + int in_size; + char *in_buffer; + } buffer_data; + +#ifndef LIBXMP_CORE_PLAYER + int st26_speed; /* For IceTracker speed effect */ +#endif + int filter; /* Amiga led filter */ +}; + +struct mixer_data { + int freq; /* sampling rate */ + int format; /* sample format */ + int amplify; /* amplification multiplier */ + int mix; /* percentage of channel separation */ + int interp; /* interpolation type */ + int dsp; /* dsp effect flags */ + char *buffer; /* output buffer */ + int32 *buf32; /* temporary buffer for 32 bit samples */ + int numvoc; /* default softmixer voices number */ + int ticksize; + int dtright; /* anticlick control, right channel */ + int dtleft; /* anticlick control, left channel */ + int bidir_adjust; /* adjustment for IT bidirectional loops */ + double pbase; /* period base */ +}; + +struct context_data { + struct player_data p; + struct mixer_data s; + struct module_data m; + struct smix_data smix; + int state; +}; + + +/* Prototypes */ + +char *libxmp_adjust_string (char *); +int libxmp_prepare_scan (struct context_data *); +void libxmp_free_scan (struct context_data *); +int libxmp_scan_sequences (struct context_data *); +int libxmp_get_sequence (struct context_data *, int); +int libxmp_set_player_mode (struct context_data *); +void libxmp_reset_flow (struct context_data *); + +int8 read8s (FILE *, int *err); +uint8 read8 (FILE *, int *err); +uint16 read16l (FILE *, int *err); +uint16 read16b (FILE *, int *err); +uint32 read24l (FILE *, int *err); +uint32 read24b (FILE *, int *err); +uint32 read32l (FILE *, int *err); +uint32 read32b (FILE *, int *err); +static inline void write8 (FILE *f, uint8 b) { + fputc(b, f); +} +void write16l (FILE *, uint16); +void write16b (FILE *, uint16); +void write32l (FILE *, uint32); +void write32b (FILE *, uint32); + +uint16 readmem16l (const uint8 *); +uint16 readmem16b (const uint8 *); +uint32 readmem24l (const uint8 *); +uint32 readmem24b (const uint8 *); +uint32 readmem32l (const uint8 *); +uint32 readmem32b (const uint8 *); + +struct xmp_instrument *libxmp_get_instrument(struct context_data *, int); +struct xmp_sample *libxmp_get_sample(struct context_data *, int); + +char *libxmp_strdup(const char *); +int libxmp_get_filetype (const char *); + +#endif /* LIBXMP_COMMON_H */ diff --git a/thirdparty/libxmp/src/control.c b/thirdparty/libxmp/src/control.c new file mode 100644 index 0000000..1e56f48 --- /dev/null +++ b/thirdparty/libxmp/src/control.c @@ -0,0 +1,590 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "format.h" +#include "virtual.h" +#include "mixer.h" + +const char *xmp_version LIBXMP_EXPORT_VAR = XMP_VERSION; +const unsigned int xmp_vercode LIBXMP_EXPORT_VAR = XMP_VERCODE; + +xmp_context xmp_create_context(void) +{ + struct context_data *ctx; + + ctx = (struct context_data *) calloc(1, sizeof(struct context_data)); + if (ctx == NULL) { + return NULL; + } + + ctx->state = XMP_STATE_UNLOADED; + ctx->m.defpan = 100; + ctx->s.numvoc = SMIX_NUMVOC; + + return (xmp_context)ctx; +} + +void xmp_free_context(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + free(m->instrument_path); + free(opaque); +} + +static void set_position(struct context_data *ctx, int pos, int dir) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int seq; + int has_marker; + + /* If dir is 0, we can jump to a different sequence */ + if (dir == 0) { + seq = libxmp_get_sequence(ctx, pos); + } else { + seq = p->sequence; + } + + if (seq == 0xff) { + return; + } + + has_marker = HAS_QUIRK(QUIRK_MARKER); + + if (seq >= 0) { + int start = m->seq_data[seq].entry_point; + + p->sequence = seq; + + if (pos >= 0) { + int pat; + + while (has_marker && mod->xxo[pos] == 0xfe) { + if (dir < 0) { + if (pos > start) { + pos--; + } + } else { + pos++; + } + } + pat = mod->xxo[pos]; + + if (pat < mod->pat) { + if (has_marker && pat == 0xff) { + return; + } + + if (pos > p->scan[seq].ord) { + f->end_point = 0; + } else { + f->num_rows = mod->xxp[pat]->rows; + f->end_point = p->scan[seq].num; + f->jumpline = 0; + } + } + } + + if (pos < mod->len) { + if (pos == 0) { + p->pos = -1; + } else { + p->pos = pos; + } + /* Clear flow vars to prevent old pattern jumps and + * other junk from executing in the new position. */ + libxmp_reset_flow(ctx); + } + } +} + +int xmp_next_position(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (p->pos < m->mod.len) + set_position(ctx, p->pos + 1, 1); + + return p->pos; +} + +int xmp_prev_position(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (p->pos == m->seq_data[p->sequence].entry_point) { + set_position(ctx, -1, -1); + } else if (p->pos > m->seq_data[p->sequence].entry_point) { + set_position(ctx, p->pos - 1, -1); + } + return p->pos < 0 ? 0 : p->pos; +} + +int xmp_set_position(xmp_context opaque, int pos) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (pos >= m->mod.len) + return -XMP_ERROR_INVALID; + + set_position(ctx, pos, 0); + + return p->pos; +} + +int xmp_set_row(xmp_context opaque, int row) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int pos = p->pos; + int pattern; + + if (pos < 0 || pos >= mod->len) { + pos = 0; + } + pattern = mod->xxo[pos]; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (pattern >= mod->pat || row >= mod->xxp[pattern]->rows) + return -XMP_ERROR_INVALID; + + /* See set_position. */ + if (p->pos < 0) + p->pos = 0; + p->ord = p->pos; + p->row = row; + p->frame = -1; + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + + return row; +} + +void xmp_stop_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + + if (ctx->state < XMP_STATE_PLAYING) + return; + + p->pos = -2; +} + +void xmp_restart_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + + if (ctx->state < XMP_STATE_PLAYING) + return; + + p->loop_count = 0; + p->pos = -1; +} + +int xmp_seek_time(xmp_context opaque, int time) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int i, t; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + for (i = m->mod.len - 1; i >= 0; i--) { + int pat = m->mod.xxo[i]; + if (pat >= m->mod.pat) { + continue; + } + if (libxmp_get_sequence(ctx, i) != p->sequence) { + continue; + } + t = m->xxo_info[i].time; + if (time >= t) { + set_position(ctx, i, 1); + break; + } + } + if (i < 0) { + xmp_set_position(opaque, 0); + } + + return p->pos < 0 ? 0 : p->pos; +} + +int xmp_channel_mute(xmp_context opaque, int chn, int status) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (chn < 0 || chn >= XMP_MAX_CHANNELS) { + return -XMP_ERROR_INVALID; + } + + ret = p->channel_mute[chn]; + + if (status >= 2) { + p->channel_mute[chn] = !p->channel_mute[chn]; + } else if (status >= 0) { + p->channel_mute[chn] = status; + } + + return ret; +} + +int xmp_channel_vol(xmp_context opaque, int chn, int vol) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (chn < 0 || chn >= XMP_MAX_CHANNELS) { + return -XMP_ERROR_INVALID; + } + + ret = p->channel_vol[chn]; + + if (vol >= 0 && vol <= 100) { + p->channel_vol[chn] = vol; + } + + return ret; +} + +#ifdef USE_VERSIONED_SYMBOLS +LIBXMP_BEGIN_DECLS /* no name-mangling */ +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v40__(xmp_context, int, int) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.0"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v41__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.1"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v43__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.3"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v44__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@@XMP_4.4"); + +#ifndef HAVE_ATTRIBUTE_SYMVER +asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0"); +asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1"); +asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3"); +asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4"); +#endif +LIBXMP_END_DECLS + +#define xmp_set_player__ xmp_set_player_v40__ +#else +#define xmp_set_player__ xmp_set_player +#endif + +int xmp_set_player__(xmp_context opaque, int parm, int val) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int ret = -XMP_ERROR_INVALID; + + + if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) { + /* these should be set before loading the module */ + if (ctx->state >= XMP_STATE_LOADED) { + return -XMP_ERROR_STATE; + } + } else if (parm == XMP_PLAYER_VOICES) { + /* these should be set before start playing */ + if (ctx->state >= XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + } else if (ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + switch (parm) { + case XMP_PLAYER_AMP: + if (val >= 0 && val <= 3) { + s->amplify = val; + ret = 0; + } + break; + case XMP_PLAYER_MIX: + if (val >= -100 && val <= 100) { + s->mix = val; + ret = 0; + } + break; + case XMP_PLAYER_INTERP: + if (val >= XMP_INTERP_NEAREST && val <= XMP_INTERP_SPLINE) { + s->interp = val; + ret = 0; + } + break; + case XMP_PLAYER_DSP: + s->dsp = val; + ret = 0; + break; + case XMP_PLAYER_FLAGS: { + p->player_flags = val; + ret = 0; + break; } + + /* 4.1 */ + case XMP_PLAYER_CFLAGS: { + int vblank = p->flags & XMP_FLAGS_VBLANK; + p->flags = val; + if (vblank != (p->flags & XMP_FLAGS_VBLANK)) + libxmp_scan_sequences(ctx); + ret = 0; + break; } + case XMP_PLAYER_SMPCTL: + m->smpctl = val; + ret = 0; + break; + case XMP_PLAYER_VOLUME: + if (val >= 0 && val <= 200) { + p->master_vol = val; + ret = 0; + } + break; + case XMP_PLAYER_SMIX_VOLUME: + if (val >= 0 && val <= 200) { + p->smix_vol = val; + ret = 0; + } + break; + + /* 4.3 */ + case XMP_PLAYER_DEFPAN: + if (val >= 0 && val <= 100) { + m->defpan = val; + ret = 0; + } + break; + + /* 4.4 */ + case XMP_PLAYER_MODE: + p->mode = val; + libxmp_set_player_mode(ctx); + libxmp_scan_sequences(ctx); + ret = 0; + break; + case XMP_PLAYER_VOICES: + s->numvoc = val; + break; + } + + return ret; +} + +#ifdef USE_VERSIONED_SYMBOLS +LIBXMP_BEGIN_DECLS /* no name-mangling */ +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v40__(xmp_context, int) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.0"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v41__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.1"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v42__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.2"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v43__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.3"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v44__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@@XMP_4.4"); + +#ifndef HAVE_ATTRIBUTE_SYMVER +asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0"); +asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1"); +asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2"); +asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3"); +asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4"); +#endif +LIBXMP_END_DECLS + +#define xmp_get_player__ xmp_get_player_v40__ +#else +#define xmp_get_player__ xmp_get_player +#endif + +int xmp_get_player__(xmp_context opaque, int parm) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int ret = -XMP_ERROR_INVALID; + + if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) { + // can read these at any time + } else if (parm != XMP_PLAYER_STATE && ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + switch (parm) { + case XMP_PLAYER_AMP: + ret = s->amplify; + break; + case XMP_PLAYER_MIX: + ret = s->mix; + break; + case XMP_PLAYER_INTERP: + ret = s->interp; + break; + case XMP_PLAYER_DSP: + ret = s->dsp; + break; + case XMP_PLAYER_FLAGS: + ret = p->player_flags; + break; + + /* 4.1 */ + case XMP_PLAYER_CFLAGS: + ret = p->flags; + break; + case XMP_PLAYER_SMPCTL: + ret = m->smpctl; + break; + case XMP_PLAYER_VOLUME: + ret = p->master_vol; + break; + case XMP_PLAYER_SMIX_VOLUME: + ret = p->smix_vol; + break; + + /* 4.2 */ + case XMP_PLAYER_STATE: + ret = ctx->state; + break; + + /* 4.3 */ + case XMP_PLAYER_DEFPAN: + ret = m->defpan; + break; + + /* 4.4 */ + case XMP_PLAYER_MODE: + ret = p->mode; + break; + case XMP_PLAYER_MIXER_TYPE: + ret = XMP_MIXER_STANDARD; + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { +#ifdef LIBXMP_PAULA_SIMULATOR + if (p->filter) { + ret = XMP_MIXER_A500F; + } else { + ret = XMP_MIXER_A500; + } +#endif + } + } + break; + case XMP_PLAYER_VOICES: + ret = s->numvoc; + break; + } + + return ret; +} + +const char *const *xmp_get_format_list(void) +{ + return format_list(); +} + +void xmp_inject_event(xmp_context opaque, int channel, struct xmp_event *e) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + + if (ctx->state < XMP_STATE_PLAYING) + return; + + memcpy(&p->inject_event[channel], e, sizeof(struct xmp_event)); + p->inject_event[channel]._flag = 1; +} + +int xmp_set_instrument_path(xmp_context opaque, const char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + + if (m->instrument_path != NULL) + free(m->instrument_path); + + m->instrument_path = libxmp_strdup(path); + if (m->instrument_path == NULL) { + return -XMP_ERROR_SYSTEM; + } + + return 0; +} + +int xmp_set_tempo_factor(xmp_context opaque, double val) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int ticksize; + + if (val <= 0.0) { + return -1; + } + + val *= 10; + ticksize = s->freq * val * m->rrate / p->bpm / 1000 * sizeof(int); + if (ticksize > XMP_MAX_FRAMESIZE) { + return -1; + } + m->time_factor = val; + + return 0; +} diff --git a/thirdparty/libxmp/src/dataio.c b/thirdparty/libxmp/src/dataio.c new file mode 100644 index 0000000..3612a36 --- /dev/null +++ b/thirdparty/libxmp/src/dataio.c @@ -0,0 +1,254 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" + + +#define read_byte(x) do { \ + (x) = fgetc(f); \ + if ((x) < 0) goto error; \ +} while (0) + +#define set_error(x) do { \ + if (err != NULL) *err = (x); \ +} while (0) + +uint8 read8(FILE *f, int *err) +{ + int a; + + read_byte(a); + set_error(0); + return a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xff; +} + +int8 read8s(FILE *f, int *err) +{ + int a; + + read_byte(a); + set_error(0); + return (int8)a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0; +} + +uint16 read16l(FILE *f, int *err) +{ + int a, b; + + read_byte(a); + read_byte(b); + + set_error(0); + return ((uint16)b << 8) | a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffff; +} + +uint16 read16b(FILE *f, int *err) +{ + int a, b; + + read_byte(a); + read_byte(b); + + set_error(0); + return (a << 8) | b; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffff; +} + +uint32 read24l(FILE *f, int *err) +{ + int a, b, c; + + read_byte(a); + read_byte(b); + read_byte(c); + + set_error(0); + return (c << 16) | (b << 8) | a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint32 read24b(FILE *f, int *err) +{ + int a, b, c; + + read_byte(a); + read_byte(b); + read_byte(c); + + set_error(0); + return (a << 16) | (b << 8) | c; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint32 read32l(FILE *f, int *err) +{ + int a, b, c, d; + + read_byte(a); + read_byte(b); + read_byte(c); + read_byte(d); + + set_error(0); + return (d << 24) | (c << 16) | (b << 8) | a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint32 read32b(FILE *f, int *err) +{ + int a, b, c, d; + + read_byte(a); + read_byte(b); + read_byte(c); + read_byte(d); + + set_error(0); + return (a << 24) | (b << 16) | (c << 8) | d; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint16 readmem16l(const uint8 *m) +{ + uint32 a, b; + + a = m[0]; + b = m[1]; + + return (b << 8) | a; +} + +uint16 readmem16b(const uint8 *m) +{ + uint32 a, b; + + a = m[0]; + b = m[1]; + + return (a << 8) | b; +} + +uint32 readmem24l(const uint8 *m) +{ + uint32 a, b, c; + + a = m[0]; + b = m[1]; + c = m[2]; + + return (c << 16) | (b << 8) | a; +} + +uint32 readmem24b(const uint8 *m) +{ + uint32 a, b, c; + + a = m[0]; + b = m[1]; + c = m[2]; + + return (a << 16) | (b << 8) | c; +} + +uint32 readmem32l(const uint8 *m) +{ + uint32 a, b, c, d; + + a = m[0]; + b = m[1]; + c = m[2]; + d = m[3]; + + return (d << 24) | (c << 16) | (b << 8) | a; +} + +uint32 readmem32b(const uint8 *m) +{ + uint32 a, b, c, d; + + a = m[0]; + b = m[1]; + c = m[2]; + d = m[3]; + + return (a << 24) | (b << 16) | (c << 8) | d; +} + +#ifndef LIBXMP_CORE_PLAYER + +void write16l(FILE *f, uint16 w) +{ + write8(f, w & 0x00ff); + write8(f, (w & 0xff00) >> 8); +} + +void write16b(FILE *f, uint16 w) +{ + write8(f, (w & 0xff00) >> 8); + write8(f, w & 0x00ff); +} + +void write32l(FILE *f, uint32 w) +{ + write8(f, w & 0x000000ff); + write8(f, (w & 0x0000ff00) >> 8); + write8(f, (w & 0x00ff0000) >> 16); + write8(f, (w & 0xff000000) >> 24); +} + +void write32b(FILE *f, uint32 w) +{ + write8(f, (w & 0xff000000) >> 24); + write8(f, (w & 0x00ff0000) >> 16); + write8(f, (w & 0x0000ff00) >> 8); + write8(f, w & 0x000000ff); +} + +#endif diff --git a/thirdparty/libxmp/src/effects.c b/thirdparty/libxmp/src/effects.c new file mode 100644 index 0000000..5c5049c --- /dev/null +++ b/thirdparty/libxmp/src/effects.c @@ -0,0 +1,1133 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "effects.h" +#include "period.h" +#include "virtual.h" +#include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "extras.h" +#endif + +#define NOT_IMPLEMENTED +#define HAS_QUIRK(x) (m->quirk & (x)) + +#define SET_LFO_NOTZERO(lfo, depth, rate) do { \ + if ((depth) != 0) libxmp_lfo_set_depth(lfo, depth); \ + if ((rate) != 0) libxmp_lfo_set_rate(lfo, rate); \ +} while (0) + +#define EFFECT_MEMORY__(p, m) do { \ + if ((p) == 0) { (p) = (m); } else { (m) = (p); } \ +} while (0) + +/* ST3 effect memory is not a bug, but it's a weird implementation and it's + * unlikely to be supported in anything other than ST3 (or OpenMPT). + */ +#define EFFECT_MEMORY(p, m) do { \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + EFFECT_MEMORY__((p), xc->vol.memory); \ + } else { \ + EFFECT_MEMORY__((p), (m)); \ + } \ +} while (0) + +#define EFFECT_MEMORY_SETONLY(p, m) do { \ + EFFECT_MEMORY__((p), (m)); \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + if ((p) != 0) { xc->vol.memory = (p); } \ + } \ +} while (0) + +#define EFFECT_MEMORY_S3M(p) do { \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + EFFECT_MEMORY__((p), xc->vol.memory); \ + } \ +} while (0) + + +static void do_toneporta(struct context_data *ctx, + struct channel_data *xc, int note) +{ + struct module_data *m = &ctx->m; + struct xmp_instrument *instrument = &m->mod.xxi[xc->ins]; + struct xmp_subinstrument *sub; + int mapped_xpo = 0; + int mapped = 0; + + if (IS_VALID_NOTE(xc->key)) { + mapped = instrument->map[xc->key].ins; + } + + if (mapped >= instrument->nsm) { + mapped = 0; + } + + sub = &instrument->sub[mapped]; + + if (IS_VALID_NOTE(note - 1) && (uint32)xc->ins < m->mod.ins) { + note--; + if (IS_VALID_NOTE(xc->key_porta)) { + mapped_xpo = instrument->map[xc->key_porta].xpo; + } + xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo + + mapped_xpo, xc->finetune, xc->per_adj); + } + xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; +} + +void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int chn, + struct xmp_event *e, int fnum) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + uint8 note, fxp, fxt; + int h, l; + + /* key_porta is IT only */ + if (m->read_event_type != READ_EVENT_IT) { + xc->key_porta = xc->key; + } + + note = e->note; + if (fnum == 0) { + fxt = e->fxt; + fxp = e->fxp; + } else { + fxt = e->f2t; + fxp = e->f2p; + } + + switch (fxt) { + case FX_ARPEGGIO: + fx_arpeggio: + if (!HAS_QUIRK(QUIRK_ARPMEM) || fxp != 0) { + xc->arpeggio.val[0] = 0; + xc->arpeggio.val[1] = MSN(fxp); + xc->arpeggio.val[2] = LSN(fxp); + xc->arpeggio.size = 3; + } + break; + case FX_S3M_ARPEGGIO: + EFFECT_MEMORY(fxp, xc->arpeggio.memory); + goto fx_arpeggio; + +#ifndef LIBXMP_CORE_PLAYER + case FX_OKT_ARP3: + if (fxp != 0) { + xc->arpeggio.val[0] = -MSN(fxp); + xc->arpeggio.val[1] = 0; + xc->arpeggio.val[2] = LSN(fxp); + xc->arpeggio.size = 3; + } + break; + case FX_OKT_ARP4: + if (fxp != 0) { + xc->arpeggio.val[0] = 0; + xc->arpeggio.val[1] = LSN(fxp); + xc->arpeggio.val[2] = 0; + xc->arpeggio.val[3] = -MSN(fxp); + xc->arpeggio.size = 4; + } + break; + case FX_OKT_ARP5: + if (fxp != 0) { + xc->arpeggio.val[0] = LSN(fxp); + xc->arpeggio.val[1] = LSN(fxp); + xc->arpeggio.val[2] = 0; + xc->arpeggio.size = 3; + } + break; +#endif + case FX_PORTA_UP: /* Portamento up */ + EFFECT_MEMORY(fxp, xc->freq.memory); + + if (HAS_QUIRK(QUIRK_FINEFX) + && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) { + switch (MSN(fxp)) { + case 0xf: + fxp &= 0x0f; + goto fx_f_porta_up; + case 0xe: + fxp &= 0x0f; + fxp |= 0x10; + goto fx_xf_porta; + } + } + + SET(PITCHBEND); + + if (fxp != 0) { + xc->freq.slide = -fxp; + if (HAS_QUIRK(QUIRK_UNISLD)) + xc->porta.memory = fxp; + } else if (xc->freq.slide > 0) { + xc->freq.slide *= -1; + } + break; + case FX_PORTA_DN: /* Portamento down */ + EFFECT_MEMORY(fxp, xc->freq.memory); + + if (HAS_QUIRK(QUIRK_FINEFX) + && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) { + switch (MSN(fxp)) { + case 0xf: + fxp &= 0x0f; + goto fx_f_porta_dn; + case 0xe: + fxp &= 0x0f; + fxp |= 0x20; + goto fx_xf_porta; + } + } + + SET(PITCHBEND); + + if (fxp != 0) { + xc->freq.slide = fxp; + if (HAS_QUIRK(QUIRK_UNISLD)) + xc->porta.memory = fxp; + } else if (xc->freq.slide < 0) { + xc->freq.slide *= -1; + } + break; + case FX_TONEPORTA: /* Tone portamento */ + EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory); + + if (fxp != 0) { + if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */ + xc->freq.memory = fxp; + xc->porta.slide = fxp; + } + + if (HAS_QUIRK(QUIRK_IGSTPOR)) { + if (note == 0 && xc->porta.dir == 0) + break; + } + + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + + do_toneporta(ctx, xc, note); + + SET(TONEPORTA); + break; + + case FX_VIBRATO: /* Vibrato */ + EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory); + + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp)); + break; + case FX_FINE_VIBRATO: /* Fine vibrato */ + EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory); + + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp)); + break; + + case FX_TONE_VSLIDE: /* Toneporta + vol slide */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + do_toneporta(ctx, xc, note); + SET(TONEPORTA); + goto fx_volslide; + case FX_VIBRA_VSLIDE: /* Vibrato + vol slide */ + SET(VIBRATO); + goto fx_volslide; + + case FX_TREMOLO: /* Tremolo */ + EFFECT_MEMORY(fxp, xc->tremolo.memory); + SET(TREMOLO); + SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp)); + break; + + case FX_SETPAN: /* Set pan */ + if (HAS_QUIRK(QUIRK_PROTRACK)) { + break; + } + fx_setpan: + /* From OpenMPT PanOff.xm: + * "Another chapter of weird FT2 bugs: Note-Off + Note Delay + * + Volume Column Panning = Panning effect is ignored." + */ + if (!HAS_QUIRK(QUIRK_FT2BUGS) /* If not FT2 */ + || fnum == 0 /* or not vol column */ + || e->note != XMP_KEY_OFF /* or not keyoff */ + || e->fxt != FX_EXTENDED /* or not delay */ + || MSN(e->fxp) != EX_DELAY) { + xc->pan.val = fxp; + xc->pan.surround = 0; + } + xc->rpv = 0; /* storlek_20: set pan overrides random pan */ + xc->pan.surround = 0; + break; + case FX_OFFSET: /* Set sample offset */ + EFFECT_MEMORY(fxp, xc->offset.memory); + SET(OFFSET); + if (note) { + xc->offset.val &= xc->offset.val & ~0xffff; + xc->offset.val |= fxp << 8; + xc->offset.val2 = fxp << 8; + } + if (e->ins) { + xc->offset.val2 = fxp << 8; + } + break; + case FX_VOLSLIDE: /* Volume slide */ + fx_volslide: + /* S3M file volume slide note: + * DFy Fine volume down by y (...) If y is 0, the command will + * be treated as a volume slide up with a value of f (15). + * If a DFF command is specified, the volume will be slid + * up. + */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (l == 0xf && h != 0) { + xc->vol.memory = fxp; + fxp >>= 4; + goto fx_f_vslide_up; + } else if (h == 0xf && l != 0) { + xc->vol.memory = fxp; + fxp &= 0x0f; + goto fx_f_vslide_dn; + } + } + + /* recover memory */ + if (fxp == 0x00) { + if ((fxp = xc->vol.memory) != 0) + goto fx_volslide; + } + + SET(VOL_SLIDE); + + /* Skaven's 2nd reality (S3M) has volslide parameter D7 => pri + * down. Other trackers only compute volumes if the other + * parameter is 0, Fall from sky.xm has 2C => do nothing. + * Also don't assign xc->vol.memory if fxp is 0, see Guild + * of Sounds.xm + */ + if (fxp) { + xc->vol.memory = fxp; + h = MSN(fxp); + l = LSN(fxp); + if (fxp) { + if (HAS_QUIRK(QUIRK_VOLPDN)) { + xc->vol.slide = l ? -l : h; + } else { + xc->vol.slide = h ? h : -l; + } + } + } + + /* Mirko reports that a S3M with D0F effects created with ST321 + * should process volume slides in all frames like ST300. I + * suspect ST3/IT could be handling D0F effects like this. + */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (MSN(xc->vol.memory) == 0xf + || LSN(xc->vol.memory) == 0xf) { + SET(FINE_VOLS); + xc->vol.fslide = xc->vol.slide; + } + } + break; + case FX_VOLSLIDE_2: /* Secondary volume slide */ + SET(VOL_SLIDE_2); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + xc->vol.slide2 = h ? h : -l; + } + break; + case FX_JUMP: /* Order jump */ + p->flow.pbreak = 1; + p->flow.jump = fxp; + /* effect B resets effect D in lower channels */ + p->flow.jumpline = 0; + break; + case FX_VOLSET: /* Volume set */ + SET(NEW_VOL); + xc->volume = fxp; + if (xc->split) { + p->xc_data[xc->pair].volume = xc->volume; + } + break; + case FX_BREAK: /* Pattern break */ + p->flow.pbreak = 1; + p->flow.jumpline = 10 * MSN(fxp) + LSN(fxp); + break; + case FX_EXTENDED: /* Extended effect */ + EFFECT_MEMORY_S3M(fxp); + fxt = fxp >> 4; + fxp &= 0x0f; + switch (fxt) { + case EX_FILTER: /* Amiga led filter */ + if (IS_AMIGA_MOD()) { + p->filter = !(fxp & 1); + } + break; + case EX_F_PORTA_UP: /* Fine portamento up */ + EFFECT_MEMORY(fxp, xc->fine_porta.up_memory); + goto fx_f_porta_up; + case EX_F_PORTA_DN: /* Fine portamento down */ + EFFECT_MEMORY(fxp, xc->fine_porta.down_memory); + goto fx_f_porta_dn; + case EX_GLISS: /* Glissando toggle */ + if (fxp) { + SET_NOTE(NOTE_GLISSANDO); + } else { + RESET_NOTE(NOTE_GLISSANDO); + } + break; + case EX_VIBRATO_WF: /* Set vibrato waveform */ + fxp &= 3; + libxmp_lfo_set_waveform(&xc->vibrato.lfo, fxp); + break; + case EX_FINETUNE: /* Set finetune */ + if (!HAS_QUIRK(QUIRK_FT2BUGS) || note > 0) { + xc->finetune = (int8)(fxp << 4); + } + break; + case EX_PATTERN_LOOP: /* Loop pattern */ + if (fxp == 0) { + /* mark start of loop */ + f->loop[chn].start = p->row; + if (HAS_QUIRK(QUIRK_FT2BUGS)) + p->flow.jumpline = p->row; + } else { + /* end of loop */ + if (f->loop[chn].count) { + if (--f->loop[chn].count) { + /* **** H:FIXME **** */ + f->loop_chn = ++chn; + } else { + if (HAS_QUIRK(QUIRK_S3MLOOP)) + f->loop[chn].start = + p->row + 1; + } + } else { + f->loop[chn].count = fxp; + f->loop_chn = ++chn; + } + } + break; + case EX_TREMOLO_WF: /* Set tremolo waveform */ + libxmp_lfo_set_waveform(&xc->tremolo.lfo, fxp & 3); + break; + case EX_SETPAN: + fxp <<= 4; + goto fx_setpan; + case EX_RETRIG: /* Retrig note */ + SET(RETRIG); + xc->retrig.val = fxp; + xc->retrig.count = LSN(xc->retrig.val) + 1; + xc->retrig.type = 0; + xc->retrig.limit = 0; + break; + case EX_F_VSLIDE_UP: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + goto fx_f_vslide_up; + case EX_F_VSLIDE_DN: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->fine_vol.down_memory); + goto fx_f_vslide_dn; + case EX_CUT: /* Cut note */ + SET(RETRIG); + SET_NOTE(NOTE_CUT); /* for IT cut-carry */ + xc->retrig.val = fxp + 1; + xc->retrig.count = xc->retrig.val; + xc->retrig.type = 0x10; + break; + case EX_DELAY: /* Note delay */ + /* computed at frame loop */ + break; + case EX_PATT_DELAY: /* Pattern delay */ + goto fx_patt_delay; + case EX_INVLOOP: /* Invert loop / funk repeat */ + xc->invloop.speed = fxp; + break; + } + break; + case FX_SPEED: /* Set speed */ + if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK) { + goto fx_s3m_speed; + } + + /* speedup.xm needs BPM = 20 */ + if (fxp < 0x20) { + goto fx_s3m_speed; + } + goto fx_s3m_bpm; + + case FX_FINETUNE: + xc->finetune = (int16) (fxp - 0x80); + break; + + case FX_F_VSLIDE_UP: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + fx_f_vslide_up: + SET(FINE_VOLS); + xc->vol.fslide = fxp; + break; + case FX_F_VSLIDE_DN: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + fx_f_vslide_dn: + SET(FINE_VOLS); + xc->vol.fslide = -fxp; + break; + + case FX_F_PORTA_UP: /* Fine portamento up */ + fx_f_porta_up: + if (fxp) { + SET(FINE_BEND); + xc->freq.fslide = -fxp; + } + break; + case FX_F_PORTA_DN: /* Fine portamento down */ + fx_f_porta_dn: + if (fxp) { + SET(FINE_BEND); + xc->freq.fslide = fxp; + } + break; + case FX_PATT_DELAY: + fx_patt_delay: + if (m->read_event_type != READ_EVENT_ST3 || !p->flow.delay) { + p->flow.delay = fxp; + } + break; + + case FX_S3M_SPEED: /* Set S3M speed */ + EFFECT_MEMORY_S3M(fxp); + fx_s3m_speed: + if (fxp) { + p->speed = fxp; +#ifndef LIBXMP_CORE_PLAYER + p->st26_speed = 0; +#endif + } + break; + case FX_S3M_BPM: /* Set S3M BPM */ + fx_s3m_bpm: { + /* Lower time factor in MED allows lower BPM values */ + int min_bpm = (int)(0.5 + m->time_factor * XMP_MIN_BPM / 10); + if (fxp < min_bpm) + fxp = min_bpm; + p->bpm = fxp; + p->frame_time = m->time_factor * m->rrate / p->bpm; + break; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_IT_BPM: /* Set IT BPM */ + if (MSN(fxp) == 0) { + SET(TEMPO_SLIDE); + if (LSN(fxp)) /* T0x - Tempo slide down by x */ + xc->tempo.slide = -LSN(fxp); + /* T00 - Repeat previous slide */ + } else if (MSN(fxp) == 1) { /* T1x - Tempo slide up by x */ + SET(TEMPO_SLIDE); + xc->tempo.slide = LSN(fxp); + } else { + if (fxp < XMP_MIN_BPM) + fxp = XMP_MIN_BPM; + p->bpm = fxp; + } + p->frame_time = m->time_factor * m->rrate / p->bpm; + break; + case FX_IT_ROWDELAY: + if (!f->rowdelay_set) { + f->rowdelay = fxp; + f->rowdelay_set = ROWDELAY_ON | ROWDELAY_FIRST_FRAME; + } + break; + + /* From the OpenMPT VolColMemory.it test case: + * "Volume column commands a, b, c and d (volume slide) share one + * effect memory, but it should not be shared with Dxy in the effect + * column. + */ + case FX_VSLIDE_UP_2: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(VOL_SLIDE_2); + xc->vol.slide2 = fxp; + break; + case FX_VSLIDE_DN_2: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(VOL_SLIDE_2); + xc->vol.slide2 = -fxp; + break; + case FX_F_VSLIDE_UP_2: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(FINE_VOLS_2); + xc->vol.fslide2 = fxp; + break; + case FX_F_VSLIDE_DN_2: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(FINE_VOLS_2); + xc->vol.fslide2 = -fxp; + break; + case FX_IT_BREAK: /* Pattern break with hex parameter */ + if (!f->loop_chn) + { + p->flow.pbreak = 1; + p->flow.jumpline = fxp; + } + break; + +#endif + + case FX_GLOBALVOL: /* Set global volume */ + if (fxp > m->gvolbase) { + p->gvol = m->gvolbase; + } else { + p->gvol = fxp; + } + break; + case FX_GVOL_SLIDE: /* Global volume slide */ + fx_gvolslide: + if (fxp) { + SET(GVOL_SLIDE); + xc->gvol.memory = fxp; + h = MSN(fxp); + l = LSN(fxp); + + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (l == 0xf && h != 0) { + xc->gvol.slide = 0; + xc->gvol.fslide = h; + } else if (h == 0xf && l != 0) { + xc->gvol.slide = 0; + xc->gvol.fslide = -l; + } else { + xc->gvol.slide = h ? h : -l; + xc->gvol.fslide = 0; + } + } else { + xc->gvol.slide = h ? h : -l; + xc->gvol.fslide = 0; + } + } else { + if ((fxp = xc->gvol.memory) != 0) { + goto fx_gvolslide; + } + } + break; + case FX_KEYOFF: /* Key off */ + xc->keyoff = fxp + 1; + break; + case FX_ENVPOS: /* Set envelope position */ + /* From OpenMPT SetEnvPos.xm: + * "When using the Lxx effect, Fasttracker 2 only sets the + * panning envelope position if the volume envelope’s sustain + * flag is set. + */ + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + struct xmp_instrument *instrument; + instrument = libxmp_get_instrument(ctx, xc->ins); + if (instrument != NULL) { + if (instrument->aei.flg & XMP_ENVELOPE_SUS) { + xc->p_idx = fxp; + } + } + } else { + xc->p_idx = fxp; + } + xc->v_idx = fxp; + xc->f_idx = fxp; + break; + case FX_PANSLIDE: /* Pan slide (XM) */ + EFFECT_MEMORY(fxp, xc->pan.memory); + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + break; + case FX_PANSL_NOMEM: /* Pan slide (XM volume column) */ + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + break; + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_IT_PANSLIDE: /* Pan slide w/ fine pan (IT) */ + SET(PAN_SLIDE); + if (fxp) { + if (MSN(fxp) == 0xf) { + xc->pan.slide = 0; + xc->pan.fslide = LSN(fxp); + } else if (LSN(fxp) == 0xf) { + xc->pan.slide = 0; + xc->pan.fslide = -MSN(fxp); + } else { + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + xc->pan.fslide = 0; + } + } + break; +#endif + + case FX_MULTI_RETRIG: /* Multi retrig */ + EFFECT_MEMORY_S3M(fxp); + if (fxp) { + xc->retrig.val = fxp; + xc->retrig.type = MSN(xc->retrig.val); + } + if (note) { + xc->retrig.count = LSN(xc->retrig.val) + 1; + } + xc->retrig.limit = 0; + SET(RETRIG); + break; + case FX_TREMOR: /* Tremor */ + EFFECT_MEMORY(fxp, xc->tremor.memory); + xc->tremor.up = MSN(fxp); + xc->tremor.down = LSN(fxp); + if (IS_PLAYER_MODE_FT2()) { + xc->tremor.count |= 0x80; + } else { + if (xc->tremor.up == 0) { + xc->tremor.up++; + } + if (xc->tremor.down == 0) { + xc->tremor.down++; + } + } + SET(TREMOR); + break; + case FX_XF_PORTA: /* Extra fine portamento */ + fx_xf_porta: + SET(FINE_BEND); + switch (MSN(fxp)) { + case 1: + xc->freq.fslide = -0.25 * LSN(fxp); + break; + case 2: + xc->freq.fslide = 0.25 * LSN(fxp); + break; + } + break; + case FX_SURROUND: + xc->pan.surround = fxp; + break; + case FX_REVERSE: /* Play forward/backward */ + libxmp_virt_reverse(ctx, chn, fxp); + break; + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_TRK_VOL: /* Track volume setting */ + if (fxp <= m->volbase) { + xc->mastervol = fxp; + } + break; + case FX_TRK_VSLIDE: /* Track volume slide */ + if (fxp == 0) { + if ((fxp = xc->trackvol.memory) == 0) + break; + } + + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + xc->trackvol.memory = fxp; + fxp &= 0x0f; + goto fx_trk_fvslide; + } else if (l == 0xf && h != 0) { + xc->trackvol.memory = fxp; + fxp &= 0xf0; + goto fx_trk_fvslide; + } + } + + SET(TRK_VSLIDE); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + + xc->trackvol.memory = fxp; + if (HAS_QUIRK(QUIRK_VOLPDN)) { + xc->trackvol.slide = l ? -l : h; + } else { + xc->trackvol.slide = h ? h : -l; + } + } + + break; + case FX_TRK_FVSLIDE: /* Track fine volume slide */ + fx_trk_fvslide: + SET(TRK_FVSLIDE); + if (fxp) { + xc->trackvol.fslide = MSN(fxp) - LSN(fxp); + } + break; + + case FX_IT_INSTFUNC: + switch (fxp) { + case 0: /* Past note cut */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_CUT); + break; + case 1: /* Past note off */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_OFF); + break; + case 2: /* Past note fade */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_FADE); + break; + case 3: /* Set NNA to note cut */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT); + break; + case 4: /* Set NNA to continue */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CONT); + break; + case 5: /* Set NNA to note off */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_OFF); + break; + case 6: /* Set NNA to note fade */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_FADE); + break; + case 7: /* Turn off volume envelope */ + SET_PER(VENV_PAUSE); + break; + case 8: /* Turn on volume envelope */ + RESET_PER(VENV_PAUSE); + break; + case 9: /* Turn off pan envelope */ + SET_PER(PENV_PAUSE); + break; + case 0xa: /* Turn on pan envelope */ + RESET_PER(PENV_PAUSE); + break; + case 0xb: /* Turn off pitch envelope */ + SET_PER(FENV_PAUSE); + break; + case 0xc: /* Turn on pitch envelope */ + RESET_PER(FENV_PAUSE); + break; + } + break; + case FX_FLT_CUTOFF: + xc->filter.cutoff = fxp; + break; + case FX_FLT_RESN: + xc->filter.resonance = fxp; + break; + case FX_MACRO_SET: + xc->macro.active = LSN(fxp); + break; + case FX_MACRO: + SET(MIDI_MACRO); + xc->macro.val = fxp; + xc->macro.slide = 0; + break; + case FX_MACROSMOOTH: + if (ctx->p.speed && xc->macro.val < 0x80) { + SET(MIDI_MACRO); + xc->macro.target = fxp; + xc->macro.slide = ((float)fxp - xc->macro.val) / ctx->p.speed; + } + break; + case FX_PANBRELLO: /* Panbrello */ + SET(PANBRELLO); + SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp)); + break; + case FX_PANBRELLO_WF: /* Panbrello waveform */ + libxmp_lfo_set_waveform(&xc->panbrello.lfo, fxp & 3); + break; + case FX_HIOFFSET: /* High offset */ + xc->offset.val &= 0xffff; + xc->offset.val |= fxp << 16; + break; +#endif + +#ifndef LIBXMP_CORE_PLAYER + + /* SFX effects */ + case FX_VOL_ADD: + if (!IS_VALID_INSTRUMENT(xc->ins)) { + break; + } + SET(NEW_VOL); + xc->volume = m->mod.xxi[xc->ins].sub[0].vol + fxp; + if (xc->volume > m->volbase) { + xc->volume = m->volbase; + } + break; + case FX_VOL_SUB: + if (!IS_VALID_INSTRUMENT(xc->ins)) { + break; + } + SET(NEW_VOL); + xc->volume = m->mod.xxi[xc->ins].sub[0].vol - fxp; + if (xc->volume < 0) { + xc->volume =0; + } + break; + case FX_PITCH_ADD: + SET_PER(TONEPORTA); + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0) + + fxp; + xc->porta.slide = 2; + xc->porta.dir = 1; + break; + case FX_PITCH_SUB: + SET_PER(TONEPORTA); + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0) + - fxp; + xc->porta.slide = 2; + xc->porta.dir = -1; + break; + + /* Saga Musix says: + * + * "When both nibbles of an Fxx command are set, SoundTracker 2.6 + * applies the both values alternatingly, first the high nibble, + * then the low nibble on the next row, then the high nibble again... + * If only the high nibble is set, it should act like if only the low + * nibble is set (i.e. F30 is the same as F03). + */ + case FX_ICE_SPEED: + if (fxp) { + if (LSN(fxp)) { + p->st26_speed = (MSN(fxp) << 8) | LSN(fxp); + } else { + p->st26_speed = MSN(fxp); + } + } + break; + + case FX_VOLSLIDE_UP: /* Vol slide with uint8 arg */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + fxp &= 0x0f; + goto fx_f_vslide_up; + } + } + + if (fxp) + xc->vol.slide = fxp; + SET(VOL_SLIDE); + break; + case FX_VOLSLIDE_DN: /* Vol slide with uint8 arg */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + fxp &= 0x0f; + goto fx_f_vslide_dn; + } + } + + if (fxp) + xc->vol.slide = -fxp; + SET(VOL_SLIDE); + break; + case FX_F_VSLIDE: /* Fine volume slide */ + SET(FINE_VOLS); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + xc->vol.fslide = h ? h : -l; + } + break; + case FX_NSLIDE_DN: + case FX_NSLIDE_UP: + case FX_NSLIDE_R_DN: + case FX_NSLIDE_R_UP: + if (fxp != 0) { + if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) { + xc->retrig.val = MSN(fxp); + xc->retrig.count = MSN(fxp) + 1; + xc->retrig.type = 0; + xc->retrig.limit = 0; + } + + if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP) + xc->noteslide.slide = LSN(fxp); + else + xc->noteslide.slide = -LSN(fxp); + + xc->noteslide.count = xc->noteslide.speed = MSN(fxp); + } + if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) + SET(RETRIG); + SET(NOTE_SLIDE); + break; + case FX_NSLIDE2_DN: + SET(NOTE_SLIDE); + xc->noteslide.slide = -fxp; + xc->noteslide.count = xc->noteslide.speed = 1; + break; + case FX_NSLIDE2_UP: + SET(NOTE_SLIDE); + xc->noteslide.slide = fxp; + xc->noteslide.count = xc->noteslide.speed = 1; + break; + case FX_F_NSLIDE_DN: + SET(FINE_NSLIDE); + xc->noteslide.fslide = -fxp; + break; + case FX_F_NSLIDE_UP: + SET(FINE_NSLIDE); + xc->noteslide.fslide = fxp; + break; + + case FX_PER_VIBRATO: /* Persistent vibrato */ + if (LSN(fxp) != 0) { + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + } + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp)); + break; + case FX_PER_PORTA_UP: /* Persistent portamento up */ + SET_PER(PITCHBEND); + xc->freq.slide = -fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_PER_PORTA_DN: /* Persistent portamento down */ + SET_PER(PITCHBEND); + xc->freq.slide = fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_PER_TPORTA: /* Persistent tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + do_toneporta(ctx, xc, note); + xc->porta.slide = fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + case FX_PER_VSLD_UP: /* Persistent volslide up */ + SET_PER(VOL_SLIDE); + xc->vol.slide = fxp; + if (fxp == 0) + RESET_PER(VOL_SLIDE); + break; + case FX_PER_VSLD_DN: /* Persistent volslide down */ + SET_PER(VOL_SLIDE); + xc->vol.slide = -fxp; + if (fxp == 0) + RESET_PER(VOL_SLIDE); + break; + case FX_VIBRATO2: /* Deep vibrato (2x) */ + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 3, MSN(fxp)); + break; + case FX_SPEED_CP: /* Set speed and ... */ + if (fxp) { + p->speed = fxp; + p->st26_speed = 0; + } + /* fall through */ + case FX_PER_CANCEL: /* Cancel persistent effects */ + xc->per_flags = 0; + break; + + /* 669 effects */ + + case FX_669_PORTA_UP: /* 669 portamento up */ + SET_PER(PITCHBEND); + xc->freq.slide = 80 * fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_669_PORTA_DN: /* 669 portamento down */ + SET_PER(PITCHBEND); + xc->freq.slide = -80 * fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_669_TPORTA: /* 669 tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + do_toneporta(ctx, xc, note); + xc->porta.slide = 40 * fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + case FX_669_FINETUNE: /* 669 finetune */ + xc->finetune = 80 * (int8)fxp; + break; + case FX_669_VIBRATO: /* 669 vibrato */ + if (LSN(fxp) != 0) { + libxmp_lfo_set_waveform(&xc->vibrato.lfo, 669); + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + } + SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1); + break; + + /* ULT effects */ + + case FX_ULT_TPORTA: /* ULT tone portamento */ + /* Like normal persistent tone portamento, except: + * + * 1) Despite the documentation claiming 300 cancels tone + * portamento, it actually reuses the last parameter. + * + * 2) A 3xx without a note will reuse the last target note. + */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + EFFECT_MEMORY(fxp, xc->porta.memory); + EFFECT_MEMORY(note, xc->porta.note_memory); + do_toneporta(ctx, xc, note); + xc->porta.slide = fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + + /* Archimedes (!Tracker, Digital Symphony, et al.) effects */ + + case FX_LINE_JUMP: /* !Tracker and Digital Symphony "Line Jump" */ + /* Jump to a line within the current order. In Digital Symphony + * this can be combined with position jump (like pattern break) + * and overrides the pattern break line in lower channels. */ + if (p->flow.pbreak == 0) { + p->flow.pbreak = 1; + p->flow.jump = p->ord; + } + p->flow.jumpline = fxp; + p->flow.jump_in_pat = p->ord; + break; +#endif + + default: +#ifndef LIBXMP_CORE_PLAYER + libxmp_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); +#endif + break; + } +} diff --git a/thirdparty/libxmp/src/effects.h b/thirdparty/libxmp/src/effects.h new file mode 100644 index 0000000..fb48b49 --- /dev/null +++ b/thirdparty/libxmp/src/effects.h @@ -0,0 +1,163 @@ +#ifndef LIBXMP_EFFECTS_H +#define LIBXMP_EFFECTS_H + +/* Protracker effects */ +#define FX_ARPEGGIO 0x00 +#define FX_PORTA_UP 0x01 +#define FX_PORTA_DN 0x02 +#define FX_TONEPORTA 0x03 +#define FX_VIBRATO 0x04 +#define FX_TONE_VSLIDE 0x05 +#define FX_VIBRA_VSLIDE 0x06 +#define FX_TREMOLO 0x07 +#define FX_OFFSET 0x09 +#define FX_VOLSLIDE 0x0a +#define FX_JUMP 0x0b +#define FX_VOLSET 0x0c +#define FX_BREAK 0x0d +#define FX_EXTENDED 0x0e +#define FX_SPEED 0x0f + +/* Fast tracker effects */ +#define FX_SETPAN 0x08 + +/* Fast Tracker II effects */ +#define FX_GLOBALVOL 0x10 +#define FX_GVOL_SLIDE 0x11 +#define FX_KEYOFF 0x14 +#define FX_ENVPOS 0x15 +#define FX_PANSLIDE 0x19 +#define FX_MULTI_RETRIG 0x1b +#define FX_TREMOR 0x1d +#define FX_XF_PORTA 0x21 + +/* Protracker extended effects */ +#define EX_FILTER 0x00 +#define EX_F_PORTA_UP 0x01 +#define EX_F_PORTA_DN 0x02 +#define EX_GLISS 0x03 +#define EX_VIBRATO_WF 0x04 +#define EX_FINETUNE 0x05 +#define EX_PATTERN_LOOP 0x06 +#define EX_TREMOLO_WF 0x07 +#define EX_SETPAN 0x08 +#define EX_RETRIG 0x09 +#define EX_F_VSLIDE_UP 0x0a +#define EX_F_VSLIDE_DN 0x0b +#define EX_CUT 0x0c +#define EX_DELAY 0x0d +#define EX_PATT_DELAY 0x0e +#define EX_INVLOOP 0x0f + +#ifndef LIBXMP_CORE_PLAYER +/* Oktalyzer effects */ +#define FX_OKT_ARP3 0x70 +#define FX_OKT_ARP4 0x71 +#define FX_OKT_ARP5 0x72 +#define FX_NSLIDE2_DN 0x73 +#define FX_NSLIDE2_UP 0x74 +#define FX_F_NSLIDE_DN 0x75 +#define FX_F_NSLIDE_UP 0x76 + +/* Persistent effects -- for FNK */ +#define FX_PER_PORTA_DN 0x78 +#define FX_PER_PORTA_UP 0x79 +#define FX_PER_TPORTA 0x7a +#define FX_PER_VIBRATO 0x7b +#define FX_PER_VSLD_UP 0x7c +#define FX_PER_VSLD_DN 0x7d +#define FX_SPEED_CP 0x7e +#define FX_PER_CANCEL 0x7f + +/* 669 frequency based effects */ +#define FX_669_PORTA_UP 0x60 +#define FX_669_PORTA_DN 0x61 +#define FX_669_TPORTA 0x62 +#define FX_669_FINETUNE 0x63 +#define FX_669_VIBRATO 0x64 + +/* FAR effects */ +#define FX_FAR_PORTA_UP 0x65 /* FAR pitch offset up */ +#define FX_FAR_PORTA_DN 0x66 /* FAR pitch offset down */ +#define FX_FAR_TPORTA 0x67 /* FAR persistent tone portamento */ +#define FX_FAR_TEMPO 0x68 /* FAR coarse tempo and tempo mode */ +#define FX_FAR_F_TEMPO 0x69 /* FAR fine tempo slide up/down */ +#define FX_FAR_VIBDEPTH 0x6a /* FAR set vibrato depth */ +#define FX_FAR_VIBRATO 0x6b /* FAR persistent vibrato */ +#define FX_FAR_SLIDEVOL 0x6c /* FAR persistent slide-to-volume */ +#define FX_FAR_RETRIG 0x6d /* FAR retrigger */ +#define FX_FAR_DELAY 0x6e /* FAR note offset */ + +/* Other frequency based effects (ULT, etc) */ +#define FX_ULT_TPORTA 0x6f +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT +/* IT effects */ +#define FX_TRK_VOL 0x80 +#define FX_TRK_VSLIDE 0x81 +#define FX_TRK_FVSLIDE 0x82 +#define FX_IT_INSTFUNC 0x83 +#define FX_FLT_CUTOFF 0x84 +#define FX_FLT_RESN 0x85 +#define FX_IT_BPM 0x87 +#define FX_IT_ROWDELAY 0x88 +#define FX_IT_PANSLIDE 0x89 +#define FX_PANBRELLO 0x8a +#define FX_PANBRELLO_WF 0x8b +#define FX_HIOFFSET 0x8c +#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */ +#define FX_MACRO_SET 0xbd /* Set active IT parametered MIDI macro */ +#define FX_MACRO 0xbe /* Execute IT MIDI macro */ +#define FX_MACROSMOOTH 0xbf /* Execute IT MIDI macro slide */ +#endif + +#ifndef LIBXMP_CORE_PLAYER +/* MED effects */ +#define FX_HOLD_DECAY 0x90 +#define FX_SETPITCH 0x91 +#define FX_VIBRATO2 0x92 + +/* PTM effects */ +#define FX_NSLIDE_DN 0x9c /* IMF/PTM note slide down */ +#define FX_NSLIDE_UP 0x9d /* IMF/PTM note slide up */ +#define FX_NSLIDE_R_UP 0x9e /* PTM note slide down with retrigger */ +#define FX_NSLIDE_R_DN 0x9f /* PTM note slide up with retrigger */ + +/* Extra effects */ +#define FX_VOLSLIDE_UP 0xa0 /* SFX, MDL */ +#define FX_VOLSLIDE_DN 0xa1 +#define FX_F_VSLIDE 0xa5 /* IMF/MDL */ +#define FX_CHORUS 0xa9 /* IMF */ +#define FX_ICE_SPEED 0xa2 +#define FX_REVERB 0xaa /* IMF */ +#define FX_MED_HOLD 0xb1 /* MMD hold/decay */ +#define FX_MEGAARP 0xb2 /* Smaksak effect 7: MegaArp */ +#define FX_VOL_ADD 0xb6 /* SFX change volume up */ +#define FX_VOL_SUB 0xb7 /* SFX change volume down */ +#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */ +#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */ +#define FX_LINE_JUMP 0xba /* Archimedes jump to line in current order */ +#endif + +#define FX_SURROUND 0x8d /* S3M/IT */ +#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */ +#define FX_S3M_SPEED 0xa3 /* S3M */ +#define FX_VOLSLIDE_2 0xa4 +#define FX_FINETUNE 0xa6 +#define FX_S3M_BPM 0xab /* S3M */ +#define FX_FINE_VIBRATO 0xac /* S3M/PTM/IMF/LIQ */ +#define FX_F_VSLIDE_UP 0xad /* MMD */ +#define FX_F_VSLIDE_DN 0xae /* MMD */ +#define FX_F_PORTA_UP 0xaf /* MMD */ +#define FX_F_PORTA_DN 0xb0 /* MMD */ +#define FX_PATT_DELAY 0xb3 /* MMD */ +#define FX_S3M_ARPEGGIO 0xb4 +#define FX_PANSL_NOMEM 0xb5 /* XM volume column */ + +#define FX_VSLIDE_UP_2 0xc0 /* IT volume column volume slide */ +#define FX_VSLIDE_DN_2 0xc1 +#define FX_F_VSLIDE_UP_2 0xc2 +#define FX_F_VSLIDE_DN_2 0xc3 + +#endif /* LIBXMP_EFFECTS_H */ diff --git a/thirdparty/libxmp/src/extras.c b/thirdparty/libxmp/src/extras.c new file mode 100644 index 0000000..f56b7b1 --- /dev/null +++ b/thirdparty/libxmp/src/extras.c @@ -0,0 +1,163 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "extras.h" +#include "med_extras.h" +#include "hmn_extras.h" +#include "far_extras.h" + +/* + * Module extras + */ + +void libxmp_release_module_extras(struct context_data *ctx) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_MODULE_EXTRAS(*m)) + libxmp_med_release_module_extras(m); + else if (HAS_HMN_MODULE_EXTRAS(*m)) + libxmp_hmn_release_module_extras(m); + else if (HAS_FAR_MODULE_EXTRAS(*m)) + libxmp_far_release_module_extras(m); +} + +/* + * Channel extras + */ + +int libxmp_new_channel_extras(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_MODULE_EXTRAS(*m)) { + if (libxmp_med_new_channel_extras(xc) < 0) + return -1; + } else if (HAS_HMN_MODULE_EXTRAS(*m)) { + if (libxmp_hmn_new_channel_extras(xc) < 0) + return -1; + } else if (HAS_FAR_MODULE_EXTRAS(*m)) { + if (libxmp_far_new_channel_extras(xc) < 0) + return -1; + } + + return 0; +} + +void libxmp_release_channel_extras(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_CHANNEL_EXTRAS(*m)) + libxmp_med_release_channel_extras(xc); + else if (HAS_HMN_CHANNEL_EXTRAS(*m)) + libxmp_hmn_release_channel_extras(xc); + else if (HAS_FAR_CHANNEL_EXTRAS(*m)) + libxmp_far_release_channel_extras(xc); +} + +void libxmp_reset_channel_extras(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_CHANNEL_EXTRAS(*m)) + libxmp_med_reset_channel_extras(xc); + else if (HAS_HMN_CHANNEL_EXTRAS(*m)) + libxmp_hmn_reset_channel_extras(xc); + else if (HAS_FAR_CHANNEL_EXTRAS(*m)) + libxmp_far_reset_channel_extras(xc); +} + +/* + * Player extras + */ + +void libxmp_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct module_data *m = &ctx->m; + + if (HAS_FAR_CHANNEL_EXTRAS(*xc)) + libxmp_far_play_extras(ctx, xc, chn); + + if (xc->ins >= m->mod.ins) /* SFX instruments have no extras */ + return; + + if (HAS_MED_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + libxmp_med_play_extras(ctx, xc, chn); + else if (HAS_HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + libxmp_hmn_play_extras(ctx, xc, chn); +} + +int libxmp_extras_get_volume(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + int vol; + + if (xc->ins >= m->mod.ins) + vol = xc->volume; + else if (HAS_MED_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + vol = MED_CHANNEL_EXTRAS(*xc)->volume * xc->volume / 64; + else if (HAS_HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + vol = HMN_CHANNEL_EXTRAS(*xc)->volume * xc->volume / 64; + else + vol = xc->volume; + + return vol; +} + +int libxmp_extras_get_period(struct context_data *ctx, struct channel_data *xc) +{ + int period; + + if (HAS_MED_CHANNEL_EXTRAS(*xc)) + period = libxmp_med_change_period(ctx, xc); + else period = 0; + + return period; +} + +int libxmp_extras_get_linear_bend(struct context_data *ctx, struct channel_data *xc) +{ + int linear_bend; + + if (HAS_MED_CHANNEL_EXTRAS(*xc)) + linear_bend = libxmp_med_linear_bend(ctx, xc); + else if (HAS_HMN_CHANNEL_EXTRAS(*xc)) + linear_bend = libxmp_hmn_linear_bend(ctx, xc); + else + linear_bend = 0; + + return linear_bend; +} + +void libxmp_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + if (HAS_MED_CHANNEL_EXTRAS(*xc)) + libxmp_med_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); + else if (HAS_HMN_CHANNEL_EXTRAS(*xc)) + libxmp_hmn_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); + else if (HAS_FAR_CHANNEL_EXTRAS(*xc)) + libxmp_far_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); +} diff --git a/thirdparty/libxmp/src/extras.h b/thirdparty/libxmp/src/extras.h new file mode 100644 index 0000000..b8ef7b2 --- /dev/null +++ b/thirdparty/libxmp/src/extras.h @@ -0,0 +1,18 @@ +#ifndef LIBXMP_EXTRAS_H +#define LIBXMP_EXTRAS_H + +void libxmp_release_module_extras(struct context_data *); +int libxmp_new_channel_extras(struct context_data *, struct channel_data *); +void libxmp_release_channel_extras(struct context_data *, struct channel_data *); +void libxmp_reset_channel_extras(struct context_data *, struct channel_data *); +void libxmp_play_extras(struct context_data *, struct channel_data *, int); +int libxmp_extras_get_volume(struct context_data *, struct channel_data *); +int libxmp_extras_get_period(struct context_data *, struct channel_data *); +int libxmp_extras_get_linear_bend(struct context_data *, struct channel_data *); +void libxmp_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + + +/* FIXME */ +void libxmp_med_hold_hack(struct context_data *ctx, int, int, int); + +#endif diff --git a/thirdparty/libxmp/src/far_extras.c b/thirdparty/libxmp/src/far_extras.c new file mode 100644 index 0000000..d2f53ca --- /dev/null +++ b/thirdparty/libxmp/src/far_extras.c @@ -0,0 +1,405 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "lfo.h" +#include "effects.h" +#include "period.h" +#include "far_extras.h" + +#define FAR_GUS_CHANNELS 17 +#define FAR_OLD_TEMPO_SHIFT 2 /* Power of multiplier for old tempo mode. */ + +/** + * The time factor needed to directly use FAR tempos is a little unintuitive. + * + * Generally: FAR tries to run 32/[coarse tempo] rows per second, which + * (usually, but not always) are subdivided into 4 "ticks". To achieve + * this, it measures tempos in the number of ticks that should play per second + * (see far_tempos below). Fine tempo is added or subtracted from this number. + * To time these ticks, FAR uses the programmable interval timer (PIT) to run a + * player interrupt. + * + * libxmp effectively uses a calculation of 10.0 * 0.25 / BPM to get the tick + * duration in seconds. A base time factor of 4.0 makes this 1 / BPM, turning + * BPM into the ticks/sec measure that FAR uses. This isn't completely + * accurate to FAR, though. + * + * The x86 PIT runs at a rate of 1193182 Hz, but FAR does something strange + * when calculating PIT divisors and uses a constant of 1197255 Hz instead. + * This means FAR tempo is slightly slower by a factor of around: + * + * floor(1197255 / 32) / floor(1193182 / 32) ~= 1.003439 + * + * This still isn't perfect, but it gets the playback rate fairly close. + */ + +/* tempo[0] = 256; tempo[i] = floor(128 / i). */ +static const int far_tempos[16] = +{ + 256, 128, 64, 42, 32, 25, 21, 18, 16, 14, 12, 11, 10, 9, 9, 8 +}; + +/** + * FAR tempo has some unusual requirements that don't really match any other + * format: + * + * 1) The coarse tempo is roughly equivalent to speed, but a value of 0 is + * supported, and FAR doesn't actually have a concept of ticks: it translates + * this value to tempo. + * + * 2) There is some very bizarre clamping behavior involving fine tempo slides + * that needs to be emulated. + * + * 3) Tempos can range from 1 to 356(!). FAR uses a fixed row subdivision size + * of 16, so just shift the tempo by 4 and hope libxmp doesn't change it. + * + * 4) There are two tempo modes, and they can be switched between arbitrarily... + */ +int libxmp_far_translate_tempo(int mode, int fine_change, int coarse, + int *fine, int *_speed, int *_bpm) +{ + int speed, bpm; + + if (coarse < 0 || coarse > 15 || mode < 0 || mode > 1) + return -1; + + /* Compatibility for FAR's broken fine tempo "clamping". */ + if (fine_change < 0 && far_tempos[coarse] + *fine <= 0) { + *fine = 0; + } else if (fine_change > 0 && far_tempos[coarse] + *fine >= 100) { + *fine = 100; + } + + if (mode == 1) { + /* "New" FAR tempo + * Note that negative values are possible in Farandole Composer + * via changing fine tempo and then slowing coarse tempo. + * These result in very slow final tempos due to signed to + * unsigned conversion. Zero should just be ignored entirely. */ + int tempo = far_tempos[coarse] + *fine; + uint32 divisor; + if (tempo == 0) + return -1; + + divisor = 1197255 / tempo; + + /* Coincidentally(?), the "new" FAR tempo algorithm actually + * prevents the BPM from dropping too far under XMP_MIN_BPM, + * which is what libxmp needs anyway. */ + speed = 0; + while (divisor > 0xffff) { + divisor >>= 1; + tempo <<= 1; + speed++; + } + if (speed >= 2) + speed++; + speed += 3; + /* Add an extra tick because the FAR replayer checks the tick + * remaining count before decrementing it but after handling + * each tick, i.e. a count of "3" executes 4 ticks. */ + speed++; + bpm = tempo; + } else { + /* "Old" FAR tempo + * This runs into the XMP_MIN_BPM limit, but nothing uses it anyway. + * Old tempo mode in the original FAR replayer has 32 ticks, + * but ignores all except every 8th. */ + speed = 4 << FAR_OLD_TEMPO_SHIFT; + bpm = (far_tempos[coarse] + *fine * 2) << FAR_OLD_TEMPO_SHIFT; + } + + if (bpm < XMP_MIN_BPM) + bpm = XMP_MIN_BPM; + + *_speed = speed; + *_bpm = bpm; + return 0; +} + +static void libxmp_far_update_tempo(struct context_data *ctx, int fine_change) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct far_module_extras *me = (struct far_module_extras *)m->extra; + int speed, bpm; + + if (libxmp_far_translate_tempo(me->tempo_mode, fine_change, + me->coarse_tempo, &me->fine_tempo, &speed, &bpm) == 0) { + p->speed = speed; + p->bpm = bpm; + p->frame_time = m->time_factor * m->rrate / p->bpm; + } +} + +static void libxmp_far_update_vibrato(struct lfo *lfo, int rate, int depth) +{ + libxmp_lfo_set_depth(lfo, libxmp_gus_frequency_steps(depth << 1, FAR_GUS_CHANNELS)); + libxmp_lfo_set_rate(lfo, rate * 3); +} + +/* Convoluted algorithm for delay times for retrigger and note offset effects. */ +static int libxmp_far_retrigger_delay(struct far_module_extras *me, int param) +{ + int delay; + if (me->coarse_tempo < 0 || me->coarse_tempo > 15 || param < 1) + return -1; + + delay = (far_tempos[me->coarse_tempo] + me->fine_tempo) / param; + + if (me->tempo_mode) { + /* Effects divide by 4, timer increments by 2 (round up). */ + return ((delay >> 2) + 1) >> 1; + } else { + /* Effects divide by 2, timer increments by 2 (round up). + * Old tempo mode handles every 8th tick (<< FAR_OLD_TEMPO_SHIFT). + * Delay values >4 result in no retrigger. */ + delay = (((delay >> 1) + 1) >> 1) << FAR_OLD_TEMPO_SHIFT; + if (delay >= 16) + return -1; + if (delay < (1 << FAR_OLD_TEMPO_SHIFT)) + return (1 << FAR_OLD_TEMPO_SHIFT); + return delay; + } +} + + +void libxmp_far_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct far_module_extras *me = FAR_MODULE_EXTRAS(ctx->m); + struct far_channel_extras *ce = FAR_CHANNEL_EXTRAS(*xc); + + /* FAR vibrato depth is global, even though rate isn't. This might have + * been changed by a different channel, so make sure it's applied. */ + if (TEST(VIBRATO) || TEST_PER(VIBRATO)) + libxmp_far_update_vibrato(&xc->vibrato.lfo, ce->vib_rate, me->vib_depth); +} + +int libxmp_far_new_channel_extras(struct channel_data *xc) +{ + xc->extra = calloc(1, sizeof(struct far_channel_extras)); + if (xc->extra == NULL) + return -1; + FAR_CHANNEL_EXTRAS(*xc)->magic = FAR_EXTRAS_MAGIC; + return 0; +} + +void libxmp_far_reset_channel_extras(struct channel_data *xc) +{ + memset((char *)xc->extra + 4, 0, sizeof(struct far_channel_extras) - 4); +} + +void libxmp_far_release_channel_extras(struct channel_data *xc) +{ + free(xc->extra); + xc->extra = NULL; +} + +int libxmp_far_new_module_extras(struct module_data *m) +{ + m->extra = calloc(1, sizeof(struct far_module_extras)); + if (m->extra == NULL) + return -1; + FAR_MODULE_EXTRAS(*m)->magic = FAR_EXTRAS_MAGIC; + FAR_MODULE_EXTRAS(*m)->vib_depth = 4; + return 0; +} + +void libxmp_far_release_module_extras(struct module_data *m) +{ + free(m->extra); + m->extra = NULL; +} + +void libxmp_far_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + struct xmp_module *mod = &ctx->m.mod; + struct far_module_extras *me = FAR_MODULE_EXTRAS(ctx->m); + struct far_channel_extras *ce = FAR_CHANNEL_EXTRAS(*xc); + int update_tempo = 0; + int update_vibrato = 0; + int fine_change = 0; + int delay, target, tempo; + int32 diff, step; + + /* Tempo effects and vibrato are multiplexed to reduce the effects count. + * + * Misc. notes: FAR pitch offset effects can overflow/underflow GUS + * frequency, which isn't supported by libxmp (Haj/before.far). + */ + switch (fxt) { + case FX_FAR_PORTA_UP: /* FAR pitch offset up */ + SET(FINE_BEND); + RESET_PER(TONEPORTA); + xc->freq.fslide = libxmp_gus_frequency_steps(fxp << 2, FAR_GUS_CHANNELS); + break; + + case FX_FAR_PORTA_DN: /* FAR pitch offset down */ + SET(FINE_BEND); + RESET_PER(TONEPORTA); + xc->freq.fslide = -libxmp_gus_frequency_steps(fxp << 2, FAR_GUS_CHANNELS); + break; + + /* Despite some claims, this effect scales with tempo and only + * corresponds to (param) rows at tempo 4. See FORMATS.DOC. + */ + case FX_FAR_TPORTA: /* FAR persistent tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + + tempo = far_tempos[me->coarse_tempo] + me->fine_tempo; + + SET_PER(TONEPORTA); + if (IS_VALID_NOTE(note - 1)) { + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, xc->per_adj); + } + xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; + + /* Parameter of 0 is equivalent to 1. */ + if (fxp < 1) + fxp = 1; + /* Tempos <=0 cause crashes and other weird behavior + * here in Farandole Composer, don't emulate that. */ + if (tempo < 1) + tempo = 1; + + diff = xc->porta.target - xc->period; + step = (diff > 0 ? diff : -diff) * 8 / (tempo * fxp); + + xc->porta.slide = (step > 0) ? step : 1; + break; + + + /* Despite some claims, this effect scales with tempo and only + * corresponds to (param/2) rows at tempo 4. See FORMATS.DOC. + */ + case FX_FAR_SLIDEVOL: /* FAR persistent slide-to-volume */ + tempo = far_tempos[me->coarse_tempo] + me->fine_tempo; + target = MSN(fxp) << 4; + fxp = LSN(fxp); + + /* Parameter of 0 is equivalent to 1. */ + if (fxp < 1) + fxp = 1; + /* Tempos <=0 cause crashes and other weird behavior + * here in Farandole Composer, don't emulate that. */ + if (tempo < 1) + tempo = 1; + + diff = target - xc->volume; + step = diff * 16 / (tempo * fxp); + if (step == 0) + step = (diff > 0) ? 1 : -1; + + SET_PER(VOL_SLIDE); + xc->vol.slide = step; + xc->vol.target = target + 1; + break; + + case FX_FAR_VIBDEPTH: /* FAR set vibrato depth */ + me->vib_depth = LSN(fxp); + update_vibrato = 1; + break; + + case FX_FAR_VIBRATO: /* FAR vibrato and sustained vibrato */ + if (ce->vib_sustain == 0) { + /* With sustain, regular vibrato only sets the rate. */ + ce->vib_sustain = MSN(fxp); + if (ce->vib_sustain == 0) + SET(VIBRATO); + } + ce->vib_rate = LSN(fxp); + update_vibrato = 1; + break; + + /* Retrigger note param times at intervals that roughly evently + * divide the row. A param of 0 crashes Farandole Composer. + */ + case FX_FAR_RETRIG: /* FAR retrigger */ + delay = libxmp_far_retrigger_delay(me, fxp); + if (note && fxp > 1 && delay >= 0 && delay <= ctx->p.speed) { + SET(RETRIG); + xc->retrig.val = delay ? delay : 1; + xc->retrig.count = delay + 1; + xc->retrig.type = 0; + xc->retrig.limit = fxp - 1; + } + break; + + /* A better effect name would probably be "retrigger once". + * The description/intent seems to be that this is a delay + * effect, but an initial note always plays as well. The second + * note always plays on the (param)th tick due to player quirks, + * but it's supposed to be derived similar to retrigger. + * A param of zero works like effect 4F (bug?). + */ + case FX_FAR_DELAY: /* FAR note offset */ + if (note) { + delay = me->tempo_mode ? fxp : fxp << FAR_OLD_TEMPO_SHIFT; + SET(RETRIG); + xc->retrig.val = delay ? delay : 1; + xc->retrig.count = delay + 1; + xc->retrig.type = 0; + xc->retrig.limit = fxp ? 1 : 0; + } + break; + + case FX_FAR_TEMPO: /* FAR coarse tempo and tempo mode */ + if (MSN(fxp)) { + me->tempo_mode = MSN(fxp) - 1; + } else { + me->coarse_tempo = LSN(fxp); + } + update_tempo = 1; + break; + + case FX_FAR_F_TEMPO: /* FAR fine tempo slide up/down */ + if (MSN(fxp)) { + me->fine_tempo += MSN(fxp); + fine_change = MSN(fxp); + } else if (LSN(fxp)) { + me->fine_tempo -= LSN(fxp); + fine_change = -LSN(fxp); + } else { + me->fine_tempo = 0; + } + update_tempo = 1; + break; + } + + if (update_vibrato) { + if (ce->vib_rate != 0) { + if (ce->vib_sustain) + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + ce->vib_sustain = 0; + } + libxmp_far_update_vibrato(&xc->vibrato.lfo, ce->vib_rate, me->vib_depth); + } + + if (update_tempo) + libxmp_far_update_tempo(ctx, fine_change); +} diff --git a/thirdparty/libxmp/src/far_extras.h b/thirdparty/libxmp/src/far_extras.h new file mode 100644 index 0000000..b1b3ae0 --- /dev/null +++ b/thirdparty/libxmp/src/far_extras.h @@ -0,0 +1,55 @@ +#ifndef XMP_FAR_EXTRAS_H +#define XMP_FAR_EXTRAS_H + +#include "common.h" + +#define FAR_EXTRAS_MAGIC 0x7b12a83f + +/* +struct far_instrument_extras { + uint32 magic; +}; +*/ +struct far_channel_extras { + uint32 magic; + int vib_sustain; /* Is vibrato persistent? */ + int vib_rate; /* Vibrato rate. */ +}; + +struct far_module_extras { + uint32 magic; + int coarse_tempo; + int fine_tempo; + int tempo_mode; + int vib_depth; /* Vibrato depth for all channels. */ +}; + +/* +#define FAR_INSTRUMENT_EXTRAS(x) ((struct far_instrument_extras *)(x).extra) +#define HAS_FAR_INSTRUMENT_EXTRAS(x) \ + (FAR_INSTRUMENT_EXTRAS(x) != NULL && \ + FAR_INSTRUMENT_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC) +*/ +#define FAR_CHANNEL_EXTRAS(x) ((struct far_channel_extras *)(x).extra) +#define HAS_FAR_CHANNEL_EXTRAS(x) \ + (FAR_CHANNEL_EXTRAS(x) != NULL && \ + FAR_CHANNEL_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC) + +#define FAR_MODULE_EXTRAS(x) ((struct far_module_extras *)(x).extra) +#define HAS_FAR_MODULE_EXTRAS(x) \ + (FAR_MODULE_EXTRAS(x) != NULL && \ + FAR_MODULE_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC) + +int libxmp_far_translate_tempo(int, int, int, int *, int *, int *); + +void libxmp_far_play_extras(struct context_data *, struct channel_data *, int); +int libxmp_far_linear_bend(struct context_data *, struct channel_data *); +int libxmp_far_new_channel_extras(struct channel_data *); +void libxmp_far_reset_channel_extras(struct channel_data *); +void libxmp_far_release_channel_extras(struct channel_data *); +int libxmp_far_new_module_extras(struct module_data *); +void libxmp_far_release_module_extras(struct module_data *); +void libxmp_far_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + +#endif + diff --git a/thirdparty/libxmp/src/filetype.c b/thirdparty/libxmp/src/filetype.c new file mode 100644 index 0000000..363150c --- /dev/null +++ b/thirdparty/libxmp/src/filetype.c @@ -0,0 +1,145 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +int libxmp_get_filetype (const char *path) +{ + DWORD result = GetFileAttributesA(path); + if (result == (DWORD)(-1)) { + errno = ENOENT; + return XMP_FILETYPE_NONE; + } + return (result & FILE_ATTRIBUTE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__OS2__) || defined(__EMX__) + +#define INCL_DOSFILEMGR +#include + +int libxmp_get_filetype (const char *path) +{ + FILESTATUS3 fs; + if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != 0) { + errno = ENOENT; + return XMP_FILETYPE_NONE; + } + return (fs.attrFile & FILE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__DJGPP__) + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + int attr = _chmod(path, 0); + /* Root directories on some non-local drives (e.g. CD-ROM), as well as + * devices may fail _chmod, but we are not interested in such cases. */ + if (attr < 0) return XMP_FILETYPE_NONE; + /* we shouldn't hit _A_VOLID ! */ + return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__WATCOMC__) && defined(_DOS) + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + unsigned int attr; + if (_dos_getfileattr(path, &attr)) return XMP_FILETYPE_NONE; + return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__amigaos4__) + +#define __USE_INLINE__ +#include + +int libxmp_get_filetype (const char *path) +{ + int typ = XMP_FILETYPE_NONE; + struct ExamineData *data = ExamineObjectTags(EX_StringNameInput, path, TAG_END); + if (data) { + if (EXD_IS_FILE(data)) { + typ = XMP_FILETYPE_FILE; + } else + if (EXD_IS_DIRECTORY(data)) { + typ = XMP_FILETYPE_DIR; + } + FreeDosObject(DOS_EXAMINEDATA, data); + } + if (typ == XMP_FILETYPE_NONE) errno = ENOENT; + return typ; +} + +#elif defined(LIBXMP_AMIGA) + +#include + +int libxmp_get_filetype (const char *path) +{ + int typ = XMP_FILETYPE_NONE; + BPTR lock = Lock((const STRPTR)path, ACCESS_READ); + if (lock) { + struct FileInfoBlock *fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB,NULL); + if (fib) { + if (Examine(lock, fib)) { + typ = (fib->fib_DirEntryType < 0) ? XMP_FILETYPE_FILE : XMP_FILETYPE_DIR; + } + FreeDosObject(DOS_FIB, fib); + } + UnLock(lock); + } + if (typ == XMP_FILETYPE_NONE) errno = ENOENT; + return typ; +} + +#else /* unix (ish): */ + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + struct stat st; + memset(&st, 0, sizeof(st)); /* silence sanitizers.. */ + if (stat(path, &st) < 0) return XMP_FILETYPE_NONE; + if (S_ISDIR(st.st_mode)) return XMP_FILETYPE_DIR; + if (S_ISREG(st.st_mode)) return XMP_FILETYPE_FILE; + return XMP_FILETYPE_NONE; +} + +#endif + diff --git a/thirdparty/libxmp/src/filter.c b/thirdparty/libxmp/src/filter.c new file mode 100644 index 0000000..a8a34b6 --- /dev/null +++ b/thirdparty/libxmp/src/filter.c @@ -0,0 +1,109 @@ +/* + * Based on the public domain version by Olivier Lapicque + * Rewritten for libxmp by Claudio Matsuoka + * + * Copyright (C) 2012 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_CORE_DISABLE_IT +#include +#include "xmp.h" +#include "common.h" +#include "mixer.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, +}; + + +#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__) +/* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */ +#undef powf +#define powf(f1_,f2_) (float)pow((f1_),(f2_)) +#endif + + +/* + * Simple 2-poles resonant filter + */ +#define FREQ_PARAM_MULT (128.0f / (24.0f * 256.0f)) +void libxmp_filter_setup(int srate, int cutoff, int res, int *a0, int *b0, int *b1) +{ + float fc, fs = (float)srate; + float fg, fb0, fb1; + float r, d, e; + + /* [0-255] => [100Hz-8000Hz] */ + CLAMP(cutoff, 0, 255); + CLAMP(res, 0, 255); + + fc = 110.0f * powf(2.0f, (float)cutoff * FREQ_PARAM_MULT + 0.25f); + if (fc > fs / 2.0f) { + fc = fs / 2.0f; + } + + r = fs / (2.0 * 3.14159265358979f * fc); + d = resonance_table[res >> 1] * (r + 1.0) - 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); + + *a0 = (int)(fg * (1 << FILTER_SHIFT)); + *b0 = (int)(fb0 * (1 << FILTER_SHIFT)); + *b1 = (int)(fb1 * (1 << FILTER_SHIFT)); +} + +#endif diff --git a/thirdparty/libxmp/src/format.c b/thirdparty/libxmp/src/format.c new file mode 100644 index 0000000..593ace1 --- /dev/null +++ b/thirdparty/libxmp/src/format.c @@ -0,0 +1,127 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "format.h" +#ifndef LIBXMP_NO_PROWIZARD +#include "loaders/prowizard/prowiz.h" +#endif + +const struct format_loader *const format_loaders[NUM_FORMATS + 2] = { + &libxmp_loader_xm, + &libxmp_loader_mod, +#ifndef LIBXMP_CORE_DISABLE_IT + &libxmp_loader_it, +#endif + &libxmp_loader_s3m, +#ifndef LIBXMP_CORE_PLAYER + &libxmp_loader_flt, + &libxmp_loader_st, + &libxmp_loader_stm, + &libxmp_loader_stx, + &libxmp_loader_mtm, + &libxmp_loader_ice, + &libxmp_loader_imf, + &libxmp_loader_ptm, + &libxmp_loader_mdl, + &libxmp_loader_ult, + &libxmp_loader_liq, + &libxmp_loader_no, + &libxmp_loader_masi, + &libxmp_loader_masi16, + &libxmp_loader_muse, + &libxmp_loader_gal5, + &libxmp_loader_gal4, + &libxmp_loader_amf, + &libxmp_loader_asylum, + &libxmp_loader_gdm, + &libxmp_loader_mmd1, + &libxmp_loader_mmd3, + &libxmp_loader_med2, + &libxmp_loader_med3, + &libxmp_loader_med4, + /* &libxmp_loader_dmf, */ + &libxmp_loader_chip, + &libxmp_loader_rtm, + &libxmp_loader_pt3, + /* &libxmp_loader_tcb, */ + &libxmp_loader_dt, + /* &libxmp_loader_gtk, */ + /* &libxmp_loader_dtt, */ + &libxmp_loader_mgt, + &libxmp_loader_arch, + &libxmp_loader_sym, + &libxmp_loader_digi, + &libxmp_loader_dbm, + &libxmp_loader_emod, + &libxmp_loader_okt, + &libxmp_loader_sfx, + &libxmp_loader_far, + &libxmp_loader_umx, + &libxmp_loader_hmn, + &libxmp_loader_stim, + &libxmp_loader_coco, + /* &libxmp_loader_mtp, */ + &libxmp_loader_ims, + &libxmp_loader_669, + &libxmp_loader_fnk, + /* &libxmp_loader_amd, */ + /* &libxmp_loader_rad, */ + /* &libxmp_loader_hsc, */ + &libxmp_loader_mfp, + &libxmp_loader_abk, + /* &libxmp_loader_alm, */ + /* &libxmp_loader_polly, */ + /* &libxmp_loader_stc, */ + &libxmp_loader_xmf, +#ifndef LIBXMP_NO_PROWIZARD + &libxmp_loader_pw, +#endif +#endif /* LIBXMP_CORE_PLAYER */ + NULL /* list teminator */ +}; + +static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL }; + +const char *const *format_list(void) +{ + int count, i; + + if (_farray[0] == NULL) { + for (count = i = 0; format_loaders[i] != NULL; i++) { +#ifndef LIBXMP_NO_PROWIZARD + if (strcmp(format_loaders[i]->name, "prowizard") == 0) { + int j; + + for (j = 0; pw_formats[j] != NULL; j++) { + _farray[count++] = pw_formats[j]->name; + } + continue; + } +#endif + _farray[count++] = format_loaders[i]->name; + } + + _farray[count] = NULL; + } + + return _farray; +} diff --git a/thirdparty/libxmp/src/format.h b/thirdparty/libxmp/src/format.h new file mode 100644 index 0000000..a4cfd5b --- /dev/null +++ b/thirdparty/libxmp/src/format.h @@ -0,0 +1,103 @@ +#ifndef LIBXMP_FORMAT_H +#define LIBXMP_FORMAT_H + +#include "common.h" +#include "hio.h" + +struct format_loader { + const char *name; + int (*test)(HIO_HANDLE *, char *, const int); + int (*loader)(struct module_data *, HIO_HANDLE *, const int); +}; + +extern const struct format_loader *const format_loaders[]; + +const char *const *format_list(void); + +extern const struct format_loader libxmp_loader_xm; +extern const struct format_loader libxmp_loader_mod; +extern const struct format_loader libxmp_loader_it; +extern const struct format_loader libxmp_loader_s3m; + +#ifndef LIBXMP_CORE_PLAYER +extern const struct format_loader libxmp_loader_flt; +extern const struct format_loader libxmp_loader_st; +extern const struct format_loader libxmp_loader_stm; +extern const struct format_loader libxmp_loader_stx; +extern const struct format_loader libxmp_loader_mtm; +extern const struct format_loader libxmp_loader_ice; +extern const struct format_loader libxmp_loader_imf; +extern const struct format_loader libxmp_loader_ptm; +extern const struct format_loader libxmp_loader_mdl; +extern const struct format_loader libxmp_loader_ult; +extern const struct format_loader libxmp_loader_liq; +extern const struct format_loader libxmp_loader_no; +extern const struct format_loader libxmp_loader_masi; +extern const struct format_loader libxmp_loader_masi16; +extern const struct format_loader libxmp_loader_muse; +extern const struct format_loader libxmp_loader_gal5; +extern const struct format_loader libxmp_loader_gal4; +extern const struct format_loader libxmp_loader_amf; +extern const struct format_loader libxmp_loader_asylum; +extern const struct format_loader libxmp_loader_gdm; +extern const struct format_loader libxmp_loader_mmd1; +extern const struct format_loader libxmp_loader_mmd3; +extern const struct format_loader libxmp_loader_med2; +extern const struct format_loader libxmp_loader_med3; +extern const struct format_loader libxmp_loader_med4; +extern const struct format_loader libxmp_loader_rtm; +extern const struct format_loader libxmp_loader_pt3; +extern const struct format_loader libxmp_loader_dt; +extern const struct format_loader libxmp_loader_mgt; +extern const struct format_loader libxmp_loader_arch; +extern const struct format_loader libxmp_loader_sym; +extern const struct format_loader libxmp_loader_digi; +extern const struct format_loader libxmp_loader_dbm; +extern const struct format_loader libxmp_loader_emod; +extern const struct format_loader libxmp_loader_okt; +extern const struct format_loader libxmp_loader_sfx; +extern const struct format_loader libxmp_loader_far; +extern const struct format_loader libxmp_loader_umx; +extern const struct format_loader libxmp_loader_stim; +extern const struct format_loader libxmp_loader_coco; +extern const struct format_loader libxmp_loader_ims; +extern const struct format_loader libxmp_loader_669; +extern const struct format_loader libxmp_loader_fnk; +extern const struct format_loader libxmp_loader_mfp; +extern const struct format_loader libxmp_loader_pw; +extern const struct format_loader libxmp_loader_hmn; +extern const struct format_loader libxmp_loader_chip; +extern const struct format_loader libxmp_loader_abk; +extern const struct format_loader libxmp_loader_xmf; +#if 0 /* broken / unused, yet. */ +extern const struct format_loader libxmp_loader_dmf; +extern const struct format_loader libxmp_loader_tcb; +extern const struct format_loader libxmp_loader_gtk; +extern const struct format_loader libxmp_loader_dtt; +extern const struct format_loader libxmp_loader_mtp; +extern const struct format_loader libxmp_loader_amd; +extern const struct format_loader libxmp_loader_rad; +extern const struct format_loader libxmp_loader_hsc; +extern const struct format_loader libxmp_loader_alm; +extern const struct format_loader libxmp_loader_polly; +extern const struct format_loader libxmp_loader_stc; +#endif +#endif /* LIBXMP_CORE_PLAYER */ + +#ifndef LIBXMP_CORE_PLAYER +#define NUM_FORMATS 52 +#elif !defined(LIBXMP_CORE_DISABLE_IT) +#define NUM_FORMATS 4 +#else +#define NUM_FORMATS 3 +#endif + +#ifndef LIBXMP_NO_PROWIZARD +#define NUM_PW_FORMATS 43 +extern const struct pw_format *const pw_formats[]; +int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *); +#else +#define NUM_PW_FORMATS 0 +#endif + +#endif /* LIBXMP_FORMAT_H */ diff --git a/thirdparty/libxmp/src/hio.c b/thirdparty/libxmp/src/hio.c new file mode 100644 index 0000000..bee3851 --- /dev/null +++ b/thirdparty/libxmp/src/hio.c @@ -0,0 +1,544 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" +#include "hio.h" +#include "callbackio.h" +#include "mdataio.h" + +static long get_size(FILE *f) +{ + long size, pos; + + pos = ftell(f); + if (pos >= 0) { + if (fseek(f, 0, SEEK_END) < 0) { + return -1; + } + size = ftell(f); + if (fseek(f, pos, SEEK_SET) < 0) { + return -1; + } + return size; + } else { + return pos; + } +} + +int8 hio_read8s(HIO_HANDLE *h) +{ + int err; + int8 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read8s(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread8s(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread8s(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint8 hio_read8(HIO_HANDLE *h) +{ + int err; + uint8 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read8(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread8(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread8(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint16 hio_read16l(HIO_HANDLE *h) +{ + int err; + uint16 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read16l(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread16l(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread16l(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint16 hio_read16b(HIO_HANDLE *h) +{ + int err; + uint16 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read16b(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread16b(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread16b(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read24l(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read24l(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread24l(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread24l(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read24b(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read24b(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread24b(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread24b(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read32l(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read32l(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread32l(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread32l(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read32b(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read32b(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread32b(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread32b(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +size_t hio_read(void *buf, size_t size, size_t num, HIO_HANDLE *h) +{ + size_t ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = fread(buf, size, num, h->handle.file); + if (ret != num) { + if (ferror(h->handle.file)) { + h->error = errno; + } else { + h->error = feof(h->handle.file) ? EOF : -2; + } + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread(buf, size, num, h->handle.mem); + if (ret != num) { + h->error = EOF; + } + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread(buf, size, num, h->handle.cbfile); + if (ret != num) { + h->error = EOF; + } + break; + } + + return ret; +} + +int hio_seek(HIO_HANDLE *h, long offset, int whence) +{ + int ret = -1; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = fseek(h->handle.file, offset, whence); + if (ret < 0) { + h->error = errno; + } + else if (h->error == EOF) { + h->error = 0; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mseek(h->handle.mem, offset, whence); + if (ret < 0) { + h->error = EINVAL; + } + else if (h->error == EOF) { + h->error = 0; + } + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbseek(h->handle.cbfile, offset, whence); + if (ret < 0) { + h->error = EINVAL; + } + else if (h->error == EOF) { + h->error = 0; + } + break; + } + + return ret; +} + +long hio_tell(HIO_HANDLE *h) +{ + long ret = -1; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = ftell(h->handle.file); + if (ret < 0) { + h->error = errno; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mtell(h->handle.mem); + if (ret < 0) { + /* should _not_ happen! */ + h->error = EINVAL; + } + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbtell(h->handle.cbfile); + if (ret < 0) { + h->error = EINVAL; + } + break; + } + + return ret; +} + +int hio_eof(HIO_HANDLE *h) +{ + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + return feof(h->handle.file); + case HIO_HANDLE_TYPE_MEMORY: + return meof(h->handle.mem); + case HIO_HANDLE_TYPE_CBFILE: + return cbeof(h->handle.cbfile); + } + return EOF; +} + +int hio_error(HIO_HANDLE *h) +{ + int error = h->error; + h->error = 0; + return error; +} + +HIO_HANDLE *hio_open(const char *path, const char *mode) +{ + HIO_HANDLE *h; + + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) + goto err; + + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = fopen(path, mode); + if (h->handle.file == NULL) + goto err2; + + h->size = get_size(h->handle.file); + if (h->size < 0) + goto err3; + + return h; + + err3: + fclose(h->handle.file); + err2: + free(h); + err: + return NULL; +} + +HIO_HANDLE *hio_open_mem(const void *ptr, long size, int free_after_use) +{ + HIO_HANDLE *h; + + if (size <= 0) return NULL; + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) + return NULL; + + h->type = HIO_HANDLE_TYPE_MEMORY; + h->handle.mem = mopen(ptr, size, free_after_use); + h->size = size; + + if (!h->handle.mem) { + free(h); + h = NULL; + } + + return h; +} + +HIO_HANDLE *hio_open_file(FILE *f) +{ + HIO_HANDLE *h; + + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) + return NULL; + + h->noclose = 1; + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = f; + h->size = get_size(f); + if (h->size < 0) { + free(h); + return NULL; + } + + return h; +} + +HIO_HANDLE *hio_open_file2(FILE *f) +{ + HIO_HANDLE *h = hio_open_file(f); + if (h != NULL) { + h->noclose = 0; + } + else { + fclose(f); + } + return h; +} + +HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks) +{ + HIO_HANDLE *h; + CBFILE *f = cbopen(priv, callbacks); + if (!f) + return NULL; + + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) { + cbclose(f); + return NULL; + } + + h->type = HIO_HANDLE_TYPE_CBFILE; + h->handle.cbfile = f; + h->size = cbfilelength(f); + if (h->size < 0) { + cbclose(f); + free(h); + return NULL; + } + return h; +} + +static int hio_close_internal(HIO_HANDLE *h) +{ + int ret = -1; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = (h->noclose)? 0 : fclose(h->handle.file); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mclose(h->handle.mem); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbclose(h->handle.cbfile); + break; + } + return ret; +} + +/* hio_close + hio_open_mem. Reuses the same HIO_HANDLE. */ +int hio_reopen_mem(const void *ptr, long size, int free_after_use, HIO_HANDLE *h) +{ + MFILE *m; + int ret; + if (size <= 0) return -1; + + m = mopen(ptr, size, free_after_use); + if (m == NULL) { + return -1; + } + + ret = hio_close_internal(h); + if (ret < 0) { + m->free_after_use = 0; + mclose(m); + return ret; + } + + h->type = HIO_HANDLE_TYPE_MEMORY; + h->handle.mem = m; + h->size = size; + return 0; +} + +/* hio_close + hio_open_file. Reuses the same HIO_HANDLE. */ +int hio_reopen_file(FILE *f, int close_after_use, HIO_HANDLE *h) +{ + long size = get_size(f); + int ret; + if (size < 0) { + return -1; + } + + ret = hio_close_internal(h); + if (ret < 0) { + return -1; + } + + h->noclose = !close_after_use; + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = f; + h->size = size; + return 0; +} + +int hio_close(HIO_HANDLE *h) +{ + int ret = hio_close_internal(h); + free(h); + return ret; +} + +long hio_size(HIO_HANDLE *h) +{ + return h->size; +} diff --git a/thirdparty/libxmp/src/hio.h b/thirdparty/libxmp/src/hio.h new file mode 100644 index 0000000..3925a82 --- /dev/null +++ b/thirdparty/libxmp/src/hio.h @@ -0,0 +1,50 @@ +#ifndef XMP_HIO_H +#define XMP_HIO_H + +#include "callbackio.h" +#include "memio.h" + +#define HIO_HANDLE_TYPE(x) ((x)->type) + +enum hio_type { + HIO_HANDLE_TYPE_FILE, + HIO_HANDLE_TYPE_MEMORY, + HIO_HANDLE_TYPE_CBFILE +}; + +typedef struct { + enum hio_type type; + long size; + union { + FILE *file; + MFILE *mem; + CBFILE *cbfile; + } handle; + int error; + int noclose; +} HIO_HANDLE; + +int8 hio_read8s (HIO_HANDLE *); +uint8 hio_read8 (HIO_HANDLE *); +uint16 hio_read16l (HIO_HANDLE *); +uint16 hio_read16b (HIO_HANDLE *); +uint32 hio_read24l (HIO_HANDLE *); +uint32 hio_read24b (HIO_HANDLE *); +uint32 hio_read32l (HIO_HANDLE *); +uint32 hio_read32b (HIO_HANDLE *); +size_t hio_read (void *, size_t, size_t, HIO_HANDLE *); +int hio_seek (HIO_HANDLE *, long, int); +long hio_tell (HIO_HANDLE *); +int hio_eof (HIO_HANDLE *); +int hio_error (HIO_HANDLE *); +HIO_HANDLE *hio_open (const char *, const char *); +HIO_HANDLE *hio_open_mem (const void *, long, int); +HIO_HANDLE *hio_open_file (FILE *); +HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */ +HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks); +int hio_reopen_mem (const void *, long, int, HIO_HANDLE *); +int hio_reopen_file (FILE *, int, HIO_HANDLE *); +int hio_close (HIO_HANDLE *); +long hio_size (HIO_HANDLE *); + +#endif diff --git a/thirdparty/libxmp/src/hmn_extras.c b/thirdparty/libxmp/src/hmn_extras.c new file mode 100644 index 0000000..7339ef1 --- /dev/null +++ b/thirdparty/libxmp/src/hmn_extras.c @@ -0,0 +1,141 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "virtual.h" +#include "effects.h" +#include "hmn_extras.h" + +static uint8 megaarp[16][16] = { + { 0, 3, 7, 12, 15, 12, 7, 3, 0, 3, 7, 12, 15, 12, 7, 3 }, + { 0, 4, 7, 12, 16, 12, 7, 4, 0, 4, 7, 12, 16, 12, 7, 4 }, + { 0, 3, 8, 12, 15, 12, 8, 3, 0, 3, 8, 12, 15, 12, 8, 3 }, + { 0, 4, 8, 12, 16, 12, 8, 4, 0, 4, 8, 12, 16, 12, 8, 4 }, + { 0, 5, 8, 12, 17, 12, 8, 5, 0, 5, 8, 12, 17, 12, 8, 5 }, + { 0, 5, 9, 12, 17, 12, 9, 5, 0, 5, 9, 12, 17, 12, 9, 5 }, + { 12, 0, 7, 0, 3, 0, 7, 0, 12, 0, 7, 0, 3, 0, 7, 0 }, + { 12, 0, 7, 0, 4, 0, 7, 0, 12, 0, 7, 0, 4, 0, 7, 0 }, + + { 0, 3, 7, 3, 7, 12, 7, 12, 15, 12, 7, 12, 7, 3, 7, 3 }, + { 0, 4, 7, 4, 7, 12, 7, 12, 16, 12, 7, 12, 7, 4, 7, 4 }, + { 31, 27, 24, 19, 15, 12, 7, 3, 0, 3, 7, 12, 15, 19, 24, 27 }, + { 31, 28, 24, 19, 16, 12, 7, 4, 0, 4, 7, 12, 16, 19, 24, 28 }, + { 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12 }, + { 0, 12, 24, 12, 0, 12, 24, 12, 0, 12, 24, 12, 0, 12, 24, 12 }, + { 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3 }, + { 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4 } +}; + + +int libxmp_hmn_linear_bend(struct context_data *ctx, struct channel_data *xc) +{ + return 0; +} + +void libxmp_hmn_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct hmn_channel_extras *ce = (struct hmn_channel_extras *)xc->extra; + struct xmp_instrument *xxi; + int pos, waveform, volume; + + if (p->frame == 0 && TEST(NEW_NOTE|NEW_INS)) { + ce->datapos = 0; + } + + xxi = &m->mod.xxi[xc->ins]; + pos = ce->datapos; + waveform = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->data[pos]; + volume = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->progvolume[pos] & 0x7f; + + if (waveform < xxi->nsm && xxi->sub[waveform].sid != xc->smp) { + xc->smp = xxi->sub[waveform].sid; + libxmp_virt_setsmp(ctx, chn, xc->smp); + } + + pos++; + if (pos > HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->dataloopend) + pos = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->dataloopstart; + + ce->datapos = pos; + ce->volume = volume; +} + +int libxmp_hmn_new_instrument_extras(struct xmp_instrument *xxi) +{ + xxi->extra = calloc(1, sizeof(struct hmn_instrument_extras)); + if (xxi->extra == NULL) + return -1; + HMN_INSTRUMENT_EXTRAS((*xxi))->magic = HMN_EXTRAS_MAGIC; + return 0; +} + +int libxmp_hmn_new_channel_extras(struct channel_data *xc) +{ + xc->extra = calloc(1, sizeof(struct hmn_channel_extras)); + if (xc->extra == NULL) + return -1; + HMN_CHANNEL_EXTRAS((*xc))->magic = HMN_EXTRAS_MAGIC; + return 0; +} + +void libxmp_hmn_reset_channel_extras(struct channel_data *xc) +{ + memset((char *)xc->extra + 4, 0, sizeof(struct hmn_channel_extras) - 4); +} + +void libxmp_hmn_release_channel_extras(struct channel_data *xc) +{ + free(xc->extra); + xc->extra = NULL; +} + +int libxmp_hmn_new_module_extras(struct module_data *m) +{ + m->extra = calloc(1, sizeof(struct hmn_module_extras)); + if (m->extra == NULL) + return -1; + HMN_MODULE_EXTRAS((*m))->magic = HMN_EXTRAS_MAGIC; + return 0; +} + +void libxmp_hmn_release_module_extras(struct module_data *m) +{ + free(m->extra); + m->extra = NULL; +} + +void libxmp_hmn_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + switch (fxt) { + case FX_MEGAARP: + /* Not sure if this is correct... */ + fxp = LSN(fxp); + + memcpy(xc->arpeggio.val, megaarp[fxp], 16); + xc->arpeggio.size = 16; + break; + } +} diff --git a/thirdparty/libxmp/src/hmn_extras.h b/thirdparty/libxmp/src/hmn_extras.h new file mode 100644 index 0000000..ab739d2 --- /dev/null +++ b/thirdparty/libxmp/src/hmn_extras.h @@ -0,0 +1,51 @@ +#ifndef XMP_HMN_EXTRAS_H +#define XMP_HMN_EXTRAS_H + +#define HMN_EXTRAS_MAGIC 0x041bc81a + +struct hmn_instrument_extras { + uint32 magic; + int dataloopstart; + int dataloopend; + uint8 data[64]; + uint8 progvolume[64]; +}; + +struct hmn_channel_extras { + uint32 magic; + int datapos; /* HMN waveform table pointer */ + int volume; /* HMN synth volume */ +}; + +struct hmn_module_extras { + uint32 magic; +}; + +#define HMN_INSTRUMENT_EXTRAS(x) ((struct hmn_instrument_extras *)(x).extra) +#define HAS_HMN_INSTRUMENT_EXTRAS(x) \ + (HMN_INSTRUMENT_EXTRAS(x) != NULL && \ + HMN_INSTRUMENT_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC) + +#define HMN_CHANNEL_EXTRAS(x) ((struct hmn_channel_extras *)(x).extra) +#define HAS_HMN_CHANNEL_EXTRAS(x) \ + (HMN_CHANNEL_EXTRAS(x) != NULL && \ + HMN_CHANNEL_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC) + +#define HMN_MODULE_EXTRAS(x) ((struct hmn_module_extras *)(x).extra) +#define HAS_HMN_MODULE_EXTRAS(x) \ + (HMN_MODULE_EXTRAS(x) != NULL && \ + HMN_MODULE_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC) + +void libxmp_hmn_play_extras(struct context_data *, struct channel_data *, int); +void libxmp_hmn_set_arpeggio(struct channel_data *, int); +int libxmp_hmn_linear_bend(struct context_data *, struct channel_data *); +int libxmp_hmn_new_instrument_extras(struct xmp_instrument *); +int libxmp_hmn_new_channel_extras(struct channel_data *); +void libxmp_hmn_reset_channel_extras(struct channel_data *); +void libxmp_hmn_release_channel_extras(struct channel_data *); +int libxmp_hmn_new_module_extras(struct module_data *); +void libxmp_hmn_release_module_extras(struct module_data *); +void libxmp_hmn_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + +#endif + diff --git a/thirdparty/libxmp/src/lfo.c b/thirdparty/libxmp/src/lfo.c new file mode 100644 index 0000000..a393b96 --- /dev/null +++ b/thirdparty/libxmp/src/lfo.c @@ -0,0 +1,163 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "lfo.h" + +#define WAVEFORM_SIZE 64 + +static const int sine_wave[WAVEFORM_SIZE] = { + 0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, + 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197, + 180, 161, 141, 120, 97, 74, 49, 24, 0, -24, -49, -74, + -97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253, + -255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120, + -97, -74, -49, -24 +}; + +/* LFO */ + +static int get_lfo_mod(struct lfo *lfo) +{ + int val; + + if (lfo->rate == 0) + return 0; + + switch (lfo->type) { + case 0: /* sine */ + val = sine_wave[lfo->phase]; + break; + case 1: /* ramp down */ + val = 255 - (lfo->phase << 3); + break; + case 2: /* square */ + val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : -255; + break; + case 3: /* random */ + val = ((rand() & 0x1ff) - 256); + break; +#ifndef LIBXMP_CORE_PLAYER + case 669: /* 669 vibrato */ + val = lfo->phase & 1; + break; +#endif + default: + return 0; + } + + return val * lfo->depth; +} + +static int get_lfo_st3(struct lfo *lfo) +{ + if (lfo->rate == 0) { + return 0; + } + + /* S3M square */ + if (lfo->type == 2) { + int val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : 0; + return val * lfo->depth; + } + + return get_lfo_mod(lfo); +} + +/* From OpenMPT VibratoWaveforms.xm: + * "Generally the vibrato and tremolo tables are identical to those that + * ProTracker uses, but the vibrato’s “ramp down†table is upside down." + */ +static int get_lfo_ft2(struct lfo *lfo) +{ + if (lfo->rate == 0) + return 0; + + /* FT2 ramp */ + if (lfo->type == 1) { + int phase = (lfo->phase + (WAVEFORM_SIZE >> 1)) % WAVEFORM_SIZE; + int val = (phase << 3) - 255; + return val * lfo->depth; + } + + return get_lfo_mod(lfo); +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static int get_lfo_it(struct lfo *lfo) +{ + if (lfo->rate == 0) + return 0; + + return get_lfo_st3(lfo); +} + +#endif + +int libxmp_lfo_get(struct context_data *ctx, struct lfo *lfo, int is_vibrato) +{ + struct module_data *m = &ctx->m; + + switch (m->read_event_type) { + case READ_EVENT_ST3: + return get_lfo_st3(lfo); + case READ_EVENT_FT2: + if (is_vibrato) { + return get_lfo_ft2(lfo); + } else { + return get_lfo_mod(lfo); + } +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + return get_lfo_it(lfo); +#endif + default: + return get_lfo_mod(lfo); + } +} + + +void libxmp_lfo_update(struct lfo *lfo) +{ + lfo->phase += lfo->rate; + lfo->phase %= WAVEFORM_SIZE; +} + +void libxmp_lfo_set_phase(struct lfo *lfo, int phase) +{ + lfo->phase = phase; +} + +void libxmp_lfo_set_depth(struct lfo *lfo, int depth) +{ + lfo->depth = depth; +} + +void libxmp_lfo_set_rate(struct lfo *lfo, int rate) +{ + lfo->rate = rate; +} + +void libxmp_lfo_set_waveform(struct lfo *lfo, int type) +{ + lfo->type = type; +} diff --git a/thirdparty/libxmp/src/lfo.h b/thirdparty/libxmp/src/lfo.h new file mode 100644 index 0000000..a269ac3 --- /dev/null +++ b/thirdparty/libxmp/src/lfo.h @@ -0,0 +1,20 @@ +#ifndef LIBXMP_LFO_H +#define LIBXMP_LFO_H + +#include "common.h" + +struct lfo { + int type; + int rate; + int depth; + int phase; +}; + +int libxmp_lfo_get(struct context_data *, struct lfo *, int); +void libxmp_lfo_update(struct lfo *); +void libxmp_lfo_set_phase(struct lfo *, int); +void libxmp_lfo_set_depth(struct lfo *, int); +void libxmp_lfo_set_rate(struct lfo *, int); +void libxmp_lfo_set_waveform(struct lfo *, int); + +#endif diff --git a/thirdparty/libxmp/src/list.h b/thirdparty/libxmp/src/list.h new file mode 100644 index 0000000..2d79266 --- /dev/null +++ b/thirdparty/libxmp/src/list.h @@ -0,0 +1,141 @@ +#ifndef LIBXMP_LIST_H +#define LIBXMP_LIST_H + +#include /* offsetof */ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *_new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * list_add - add a new entry + * @_new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @_new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif /* LIBXMP_LIST_H */ diff --git a/thirdparty/libxmp/src/load.c b/thirdparty/libxmp/src/load.c new file mode 100644 index 0000000..13454ce --- /dev/null +++ b/thirdparty/libxmp/src/load.c @@ -0,0 +1,580 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "format.h" +#include "list.h" +#include "hio.h" +#include "loaders/loader.h" + +#ifndef LIBXMP_NO_DEPACKERS +#include "tempfile.h" +#include "depackers/depacker.h" +#endif + +#ifndef LIBXMP_CORE_PLAYER +#include "md5.h" +#include "extras.h" +#endif + + +void libxmp_load_prologue(struct context_data *); +void libxmp_load_epilogue(struct context_data *); +int libxmp_prepare_scan(struct context_data *); + +#ifndef LIBXMP_CORE_PLAYER +#define BUFLEN 16384 + +static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) +{ + unsigned char buf[BUFLEN]; + MD5_CTX ctx; + int bytes_read; + + hio_seek(f, 0, SEEK_SET); + + MD5Init(&ctx); + while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) { + MD5Update(&ctx, buf, bytes_read); + } + MD5Final(digest, &ctx); +} + +static char *get_dirname(const char *name) +{ + char *dirname; + const char *p; + ptrdiff_t len; + + if ((p = strrchr(name, '/')) != NULL) { + len = p - name + 1; + dirname = (char *) malloc(len + 1); + if (dirname != NULL) { + memcpy(dirname, name, len); + dirname[len] = 0; + } + } else { + dirname = libxmp_strdup(""); + } + + return dirname; +} + +static char *get_basename(const char *name) +{ + const char *p; + char *basename; + + if ((p = strrchr(name, '/')) != NULL) { + basename = libxmp_strdup(p + 1); + } else { + basename = libxmp_strdup(name); + } + + return basename; +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int test_module(struct xmp_test_info *info, HIO_HANDLE *h) +{ + char buf[XMP_NAME_SIZE]; + int i; + + if (info != NULL) { + *info->name = 0; /* reset name prior to testing */ + *info->type = 0; /* reset type prior to testing */ + } + + for (i = 0; format_loaders[i] != NULL; i++) { + hio_seek(h, 0, SEEK_SET); + if (format_loaders[i]->test(h, buf, 0) == 0) { + int is_prowizard = 0; + +#ifndef LIBXMP_NO_PROWIZARD + if (strcmp(format_loaders[i]->name, "prowizard") == 0) { + hio_seek(h, 0, SEEK_SET); + pw_test_format(h, buf, 0, info); + is_prowizard = 1; + } +#endif + + if (info != NULL && !is_prowizard) { + strncpy(info->name, buf, XMP_NAME_SIZE - 1); + info->name[XMP_NAME_SIZE - 1] = '\0'; + + strncpy(info->type, format_loaders[i]->name, + XMP_NAME_SIZE - 1); + info->type[XMP_NAME_SIZE - 1] = '\0'; + } + return 0; + } + } + return -XMP_ERROR_FORMAT; +} + +int xmp_test_module(const char *path, struct xmp_test_info *info) +{ + HIO_HANDLE *h; +#ifndef LIBXMP_NO_DEPACKERS + char *temp = NULL; +#endif + int ret; + + ret = libxmp_get_filetype(path); + + if (ret == XMP_FILETYPE_NONE) { + return -XMP_ERROR_SYSTEM; + } + if (ret & XMP_FILETYPE_DIR) { + errno = EISDIR; + return -XMP_ERROR_SYSTEM; + } + + if ((h = hio_open(path, "rb")) == NULL) + return -XMP_ERROR_SYSTEM; + +#ifndef LIBXMP_NO_DEPACKERS + if (libxmp_decrunch(h, path, &temp) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } +#endif + + ret = test_module(info, h); + +#ifndef LIBXMP_NO_DEPACKERS + err: + hio_close(h); + unlink_temp_file(temp); +#else + hio_close(h); +#endif + return ret; +} + +int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info *info) +{ + HIO_HANDLE *h; + int ret; + + if (size <= 0) { + return -XMP_ERROR_INVALID; + } + + if ((h = hio_open_mem(mem, size, 0)) == NULL) + return -XMP_ERROR_SYSTEM; + + ret = test_module(info, h); + + hio_close(h); + return ret; +} + +int xmp_test_module_from_file(void *file, struct xmp_test_info *info) +{ + HIO_HANDLE *h; + int ret; +#ifndef LIBXMP_NO_DEPACKERS + char *temp = NULL; +#endif + + if ((h = hio_open_file((FILE *)file)) == NULL) + return -XMP_ERROR_SYSTEM; + +#ifndef LIBXMP_NO_DEPACKERS + if (libxmp_decrunch(h, NULL, &temp) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } +#endif + + ret = test_module(info, h); + +#ifndef LIBXMP_NO_DEPACKERS + err: + hio_close(h); + unlink_temp_file(temp); +#else + hio_close(h); +#endif + return ret; +} + +int xmp_test_module_from_callbacks(void *priv, struct xmp_callbacks callbacks, + struct xmp_test_info *info) +{ + HIO_HANDLE *h; + int ret; + + if ((h = hio_open_callbacks(priv, callbacks)) == NULL) + return -XMP_ERROR_SYSTEM; + + ret = test_module(info, h); + + hio_close(h); + return ret; +} + +static int load_module(xmp_context opaque, HIO_HANDLE *h) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i, j, ret; + int test_result, load_result; + + libxmp_load_prologue(ctx); + + D_(D_WARN "load"); + test_result = load_result = -1; + for (i = 0; format_loaders[i] != NULL; i++) { + hio_seek(h, 0, SEEK_SET); + + D_(D_WARN "test %s", format_loaders[i]->name); + test_result = format_loaders[i]->test(h, NULL, 0); + if (test_result == 0) { + hio_seek(h, 0, SEEK_SET); + D_(D_WARN "load format: %s", format_loaders[i]->name); + load_result = format_loaders[i]->loader(m, h, 0); + break; + } + } + + if (test_result < 0) { + xmp_release_module(opaque); + return -XMP_ERROR_FORMAT; + } + + if (load_result < 0) { + goto err_load; + } + + /* Sanity check: number of channels, module length */ + if (mod->chn > XMP_MAX_CHANNELS || mod->len > XMP_MAX_MOD_LENGTH) { + goto err_load; + } + + /* Sanity check: channel pan */ + for (i = 0; i < mod->chn; i++) { + if (mod->xxc[i].vol < 0 || mod->xxc[i].vol > 0xff) { + goto err_load; + } + if (mod->xxc[i].pan < 0 || mod->xxc[i].pan > 0xff) { + goto err_load; + } + } + + /* Sanity check: patterns */ + if (mod->xxp == NULL) { + goto err_load; + } + for (i = 0; i < mod->pat; i++) { + if (mod->xxp[i] == NULL) { + goto err_load; + } + for (j = 0; j < mod->chn; j++) { + int t = mod->xxp[i]->index[j]; + if (t < 0 || t >= mod->trk || mod->xxt[t] == NULL) { + goto err_load; + } + } + } + + libxmp_adjust_string(mod->name); + for (i = 0; i < mod->ins; i++) { + libxmp_adjust_string(mod->xxi[i].name); + } + for (i = 0; i < mod->smp; i++) { + libxmp_adjust_string(mod->xxs[i].name); + } + +#ifndef LIBXMP_CORE_PLAYER + if (test_result == 0 && load_result == 0) + set_md5sum(h, m->md5); +#endif + + libxmp_load_epilogue(ctx); + + ret = libxmp_prepare_scan(ctx); + if (ret < 0) { + xmp_release_module(opaque); + return ret; + } + + ret = libxmp_scan_sequences(ctx); + if (ret < 0) { + xmp_release_module(opaque); + return -XMP_ERROR_LOAD; + } + + ctx->state = XMP_STATE_LOADED; + + return 0; + + err_load: + xmp_release_module(opaque); + return -XMP_ERROR_LOAD; +} + +int xmp_load_module(xmp_context opaque, const char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; +#ifndef LIBXMP_CORE_PLAYER + struct module_data *m = &ctx->m; +#endif +#ifndef LIBXMP_NO_DEPACKERS + char *temp_name; +#endif + HIO_HANDLE *h; + int ret; + + D_(D_WARN "path = %s", path); + + ret = libxmp_get_filetype(path); + + if (ret == XMP_FILETYPE_NONE) { + return -XMP_ERROR_SYSTEM; + } + if (ret & XMP_FILETYPE_DIR) { + errno = EISDIR; + return -XMP_ERROR_SYSTEM; + } + + if ((h = hio_open(path, "rb")) == NULL) { + return -XMP_ERROR_SYSTEM; + } + +#ifndef LIBXMP_NO_DEPACKERS + D_(D_INFO "decrunch"); + if (libxmp_decrunch(h, path, &temp_name) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } +#endif + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + +#ifndef LIBXMP_CORE_PLAYER + m->dirname = get_dirname(path); + if (m->dirname == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + m->basename = get_basename(path); + if (m->basename == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + m->filename = path; /* For ALM, SSMT, etc */ + m->size = hio_size(h); +#else + ctx->m.filename = NULL; + ctx->m.dirname = NULL; + ctx->m.basename = NULL; +#endif + + ret = load_module(opaque, h); + hio_close(h); + +#ifndef LIBXMP_NO_DEPACKERS + unlink_temp_file(temp_name); +#endif + + return ret; + +#ifndef LIBXMP_CORE_PLAYER + err: + hio_close(h); +#ifndef LIBXMP_NO_DEPACKERS + unlink_temp_file(temp_name); +#endif + return ret; +#endif +} + +int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + if (size <= 0) { + return -XMP_ERROR_INVALID; + } + + if ((h = hio_open_mem(mem, size, 0)) == NULL) + return -XMP_ERROR_SYSTEM; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + m->filename = NULL; + m->basename = NULL; + m->dirname = NULL; + m->size = size; + + ret = load_module(opaque, h); + + hio_close(h); + + return ret; +} + +int xmp_load_module_from_file(xmp_context opaque, void *file, long size) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + if ((h = hio_open_file((FILE *)file)) == NULL) + return -XMP_ERROR_SYSTEM; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + m->filename = NULL; + m->basename = NULL; + m->dirname = NULL; + m->size = hio_size(h); + + ret = load_module(opaque, h); + + hio_close(h); + + return ret; +} + +int xmp_load_module_from_callbacks(xmp_context opaque, void *priv, + struct xmp_callbacks callbacks) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + if ((h = hio_open_callbacks(priv, callbacks)) == NULL) + return -XMP_ERROR_SYSTEM; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + m->filename = NULL; + m->basename = NULL; + m->dirname = NULL; + m->size = hio_size(h); + + ret = load_module(opaque, h); + + hio_close(h); + + return ret; +} + +void xmp_release_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i; + + /* can't test this here, we must call release_module to clean up + * load errors + if (ctx->state < XMP_STATE_LOADED) + return; + */ + + if (ctx->state > XMP_STATE_LOADED) + xmp_end_player(opaque); + + ctx->state = XMP_STATE_UNLOADED; + + D_(D_INFO "Freeing memory"); + +#ifndef LIBXMP_CORE_PLAYER + libxmp_release_module_extras(ctx); +#endif + + if (mod->xxt != NULL) { + for (i = 0; i < mod->trk; i++) { + free(mod->xxt[i]); + } + free(mod->xxt); + mod->xxt = NULL; + } + + if (mod->xxp != NULL) { + for (i = 0; i < mod->pat; i++) { + free(mod->xxp[i]); + } + free(mod->xxp); + mod->xxp = NULL; + } + + if (mod->xxi != NULL) { + for (i = 0; i < mod->ins; i++) { + free(mod->xxi[i].sub); + free(mod->xxi[i].extra); + } + free(mod->xxi); + mod->xxi = NULL; + } + + if (mod->xxs != NULL) { + for (i = 0; i < mod->smp; i++) { + libxmp_free_sample(&mod->xxs[i]); + } + free(mod->xxs); + mod->xxs = NULL; + } + + free(m->xtra); + free(m->midi); + m->xtra = NULL; + m->midi = NULL; + + libxmp_free_scan(ctx); + + free(m->comment); + m->comment = NULL; + + D_("free dirname/basename"); + free(m->dirname); + free(m->basename); + m->basename = NULL; + m->dirname = NULL; +} + +void xmp_scan_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + + if (ctx->state < XMP_STATE_LOADED) + return; + + libxmp_scan_sequences(ctx); +} diff --git a/thirdparty/libxmp/src/load_helpers.c b/thirdparty/libxmp/src/load_helpers.c new file mode 100644 index 0000000..c0c5e88 --- /dev/null +++ b/thirdparty/libxmp/src/load_helpers.c @@ -0,0 +1,560 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" +#include "loaders/loader.h" + + +#ifndef LIBXMP_CORE_PLAYER + +/* + * Handle special "module quirks" that can't be detected automatically + * such as Protracker 2.x compatibility, vblank timing, etc. + */ + +struct module_quirk { + uint8 md5[16]; + int flags; + int mode; +}; + +const struct module_quirk mq[] = { + /* "No Mercy" by Alf/VTL (added by Martin Willers) */ + { + { 0x36, 0x6e, 0xc0, 0xfa, 0x96, 0x2a, 0xeb, 0xee, + 0x03, 0x4a, 0xa2, 0xdb, 0xaa, 0x49, 0xaa, 0xea }, + 0, XMP_MODE_PROTRACKER + }, + + /* mod.souvenir of china */ + { + { 0x93, 0xf1, 0x46, 0xae, 0xb7, 0x58, 0xc3, 0x9d, + 0x8b, 0x5f, 0xbc, 0x98, 0xbf, 0x23, 0x7a, 0x43 }, + XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO + }, + +#if 0 + /* "siedler ii" (added by Daniel Åkerud) */ + /* Timing fixed by vblank scan compare. CIA: 32m10s VBlank: 12m32s */ + { + { 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73, + 0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, +#endif + + /* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */ + { + { 0xe9, 0x98, 0x01, 0x2c, 0x70, 0x0e, 0xb4, 0x3a, + 0xf0, 0x32, 0x17, 0x11, 0x30, 0x58, 0x29, 0xb2 }, + 0, XMP_MODE_NOISETRACKER + }, + +#if 0 + /* -- Already covered by Noisetracker fingerprinting -- */ + + /* Another version of Klisje paa klisje sent by Steve Fernandez */ + { + { 0x12, 0x19, 0x1c, 0x90, 0x41, 0xe3, 0xfd, 0x70, + 0xb7, 0xe6, 0xb3, 0x94, 0x8b, 0x21, 0x07, 0x63 }, + XMP_FLAGS_VBLANK + }, +#endif + + /* "((((( nebulos )))))" sent by Tero Auvinen (AMP version) */ + { + { 0x51, 0x6e, 0x8d, 0xcc, 0x35, 0x7d, 0x50, 0xde, + 0xa9, 0x85, 0xbe, 0xbf, 0x90, 0x2e, 0x42, 0xdc }, + 0, XMP_MODE_NOISETRACKER + }, + + /* Purple Motion's Sundance.mod, Music Channel BBS edit */ + { + { 0x5d, 0x3e, 0x1e, 0x08, 0x28, 0x52, 0x12, 0xc7, + 0x17, 0x64, 0x95, 0x75, 0x98, 0xe6, 0x95, 0xc1 }, + 0, XMP_MODE_ST3 + }, + + /* Asle's Ode to Protracker */ + { + { 0x97, 0xa3, 0x7d, 0x30, 0xd7, 0xae, 0x6d, 0x50, + 0xc9, 0x62, 0xe9, 0xd8, 0x87, 0x1b, 0x7e, 0x8a }, + 0, XMP_MODE_PROTRACKER + }, + + /* grooving3.mod */ + /* length 150778 crc32c 0xfdcf9aadU */ + { + { 0xdb, 0x61, 0x22, 0x44, 0x39, 0x85, 0x74, 0xe9, + 0xfa, 0x11, 0xb8, 0xfb, 0x87, 0xe8, 0xde, 0xc5, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* mod.Rundgren */ + /* length 195078 crc32c 0x8fa827a4U */ + { + { 0x9a, 0xdb, 0xb2, 0x09, 0x07, 0x1c, 0x44, 0x82, + 0xc5, 0xdf, 0x83, 0x52, 0xcc, 0x73, 0x9f, 0x20, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* dance feeling by Audiomonster */ + /* length 169734 crc32c 0x79fa2c9bU */ + { + { 0x31, 0x2c, 0x3d, 0xaa, 0x5f, 0x1a, 0x54, 0x44, + 0x9d, 0xf7, 0xc4, 0x41, 0x8a, 0xc5, 0x01, 0x02, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* knights melody by Audiomonster */ + /* length 77798 crc32c 0x7bf19c5bU */ + { + { 0x31, 0xc3, 0x0e, 0x32, 0xfc, 0x99, 0x95, 0xd2, + 0x97, 0x20, 0xb3, 0x77, 0x50, 0x05, 0xfe, 0xa5, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* hcomme by Bouffon */ + /* length 71346 crc32c 0x4ad49cb3U */ + { + { 0x6e, 0xf9, 0x78, 0xc1, 0x80, 0xae, 0x51, 0x06, + 0x05, 0x7c, 0x6e, 0xd0, 0x26, 0x7e, 0xfe, 0x3d, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* ((((aquapool)))) by Dolphin */ + /* length 62932 crc32c 0x05b103fcU */ + { + { 0xff, 0x0b, 0xe0, 0x26, 0xc6, 0x31, 0xb5, 0x9b, + 0x94, 0x83, 0x94, 0x99, 0x7e, 0x24, 0x7c, 0xdd, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* 100yarddash by Dr. Awesome */ + /* length 104666 crc32c 0xd2b0e4a6U */ + { + { 0x5b, 0xff, 0x2f, 0xb8, 0xef, 0x3c, 0xbe, 0x55, + 0xa8, 0xe2, 0xa7, 0xcf, 0x5c, 0xbd, 0xdd, 0xb2, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* jazz-reggae-funk by Droid */ + /* length 115564 crc32c 0x41ff635fU */ + { + { 0xe5, 0x6e, 0x31, 0x2f, 0x62, 0x80, 0xc1, 0x9d, + 0x2f, 0x24, 0x54, 0xf3, 0x89, 0x3f, 0x94, 0x6c, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* hard and heavy by Fish */ + /* length 69814 crc32c 0x1f09d3d5U */ + { + { 0x6b, 0xce, 0x39, 0x94, 0x75, 0x42, 0x06, 0x74, + 0xd2, 0x83, 0xbc, 0x5e, 0x7b, 0x42, 0x1f, 0xa0, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* crazy valley by Julius and Droid */ + /* length 97496 crc32c 0xb8eec40eU */ + { + { 0x23, 0x77, 0x18, 0x1d, 0x21, 0x9b, 0x41, 0x8f, + 0xc1, 0xb4, 0xf4, 0xf8, 0x22, 0xdd, 0xd8, 0xb6, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* THE ILLOGICAL ONE by Rhino */ + /* length 173432 crc32c 0xcb4e2987U */ + { + { 0xd8, 0xc2, 0xbb, 0xe6, 0x11, 0xd0, 0x5c, 0x02, + 0x8e, 0x3b, 0xcb, 0x7c, 0x4a, 0x7d, 0x43, 0xa0, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* sounds of holiday by Spacebrain */ + /* length 309520 crc32c 0x28804a57U */ + { + { 0x36, 0x18, 0x19, 0xa4, 0x9d, 0xa2, 0xa2, 0x6f, + 0x58, 0x60, 0xc4, 0xd9, 0x0d, 0xa2, 0x9f, 0x49, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* sunisinus by Speed-Head */ + /* length 175706 crc32c 0x2e56451bU */ + { + { 0x7e, 0x69, 0x44, 0xb6, 0x38, 0x0d, 0x27, 0x14, + 0x70, 0x5d, 0x44, 0xce, 0xce, 0xdd, 0x37, 0x31, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* eat the fulcrum bop by The Assassin */ + /* length 160286 crc32c 0x583a4683U */ + { + { 0x11, 0xe9, 0x6f, 0x62, 0xe1, 0xc3, 0xc5, 0xcc, + 0x3b, 0xaf, 0xea, 0x69, 0x4b, 0xce, 0x5f, 0xec, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* obvious disaster by Tip */ + /* length 221086 crc32c 0x51c6d489U */ + { + { 0x06, 0x8e, 0x69, 0x01, 0x49, 0x8f, 0xbd, 0x0f, + 0xfc, 0xb7, 0x8f, 0x2a, 0x91, 0xe1, 0x8b, 0xe8, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* alien nation by Turtle */ + /* length 167548 crc32c 0xc9ec1674U */ + { + { 0x71, 0xdf, 0x11, 0xac, 0x5d, 0xec, 0x07, 0xf8, + 0x10, 0x6f, 0x28, 0x8d, 0x47, 0x59, 0x54, 0x9b, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* illusions!2 by Zuhl */ + /* length 289770 crc32c 0x6bf5fbcfU */ + { + { 0xca, 0x37, 0x8c, 0x0e, 0x87, 0x4f, 0x1e, 0xcd, + 0xa3, 0xe9, 0x8b, 0xdd, 0x11, 0x46, 0x8d, 0x69, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 0, 0 + } +}; + +static void module_quirks(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int i; + + for (i = 0; mq[i].flags != 0 || mq[i].mode != 0; i++) { + if (!memcmp(m->md5, mq[i].md5, 16)) { + p->flags |= mq[i].flags; + p->mode = mq[i].mode; + } + } +} + +#endif /* LIBXMP_CORE_PLAYER */ + +char *libxmp_adjust_string(char *s) +{ + int i; + + for (i = 0; i < strlen(s); i++) { + if (!isprint((unsigned char)s[i]) || ((uint8) s[i] > 127)) + s[i] = ' '; + } + + while (*s && (s[strlen(s) - 1] == ' ')) { + s[strlen(s) - 1] = 0; + } + + return s; +} + +static void check_envelope(struct xmp_envelope *env) +{ + /* Disable envelope if invalid number of points */ + if (env->npt <= 0 || env->npt > XMP_MAX_ENV_POINTS) { + env->flg &= ~XMP_ENVELOPE_ON; + } + + /* Disable envelope loop if invalid loop parameters */ + if (env->lps >= env->npt || env->lpe >= env->npt) { + env->flg &= ~XMP_ENVELOPE_LOOP; + } + + /* Disable envelope sustain if invalid sustain */ + if (env->sus >= env->npt || env->sue >= env->npt) { + env->flg &= ~XMP_ENVELOPE_SUS; + } +} + +static void clamp_volume_envelope(struct module_data *m, struct xmp_envelope *env) +{ + /* Clamp broken values in the volume envelope to the expected range. */ + if (env->flg & XMP_ENVELOPE_ON) { + int i; + for (i = 0; i < env->npt; i++) { + int16 *data = &env->data[i * 2 + 1]; + CLAMP(*data, 0, m->volbase); + } + } +} + +void libxmp_load_prologue(struct context_data *ctx) +{ + struct module_data *m = &ctx->m; + int i; + + /* Reset variables */ + memset(&m->mod, 0, sizeof (struct xmp_module)); + m->rrate = PAL_RATE; + m->c4rate = C4_PAL_RATE; + m->volbase = 0x40; + m->gvol = m->gvolbase = 0x40; + m->vol_table = NULL; + m->quirk = 0; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_AMIGA; + m->compare_vblank = 0; + m->comment = NULL; + m->scan_cnt = NULL; + m->midi = NULL; + + /* Set defaults */ + m->mod.pat = 0; + m->mod.trk = 0; + m->mod.chn = 4; + m->mod.ins = 0; + m->mod.smp = 0; + m->mod.spd = 6; + m->mod.bpm = 125; + m->mod.len = 0; + m->mod.rst = 0; + +#ifndef LIBXMP_CORE_PLAYER + m->extra = NULL; +#endif + + m->time_factor = DEFAULT_TIME_FACTOR; + + for (i = 0; i < 64; i++) { + int pan = (((i + 1) / 2) % 2) * 0xff; + m->mod.xxc[i].pan = 0x80 + (pan - 0x80) * m->defpan / 100; + m->mod.xxc[i].vol = 0x40; + m->mod.xxc[i].flg = 0; + } +} + +void libxmp_load_epilogue(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i, j; + + mod->gvl = m->gvol; + + /* Sanity check for module parameters */ + CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH); + CLAMP(mod->pat, 0, 257); /* some formats have an extra pattern */ + CLAMP(mod->ins, 0, 255); + CLAMP(mod->smp, 0, MAX_SAMPLES); + CLAMP(mod->chn, 0, XMP_MAX_CHANNELS); + + /* Fix cases where the restart value is invalid e.g. kc_fall8.xm + * from http://aminet.net/mods/mvp/mvp_0002.lha (reported by + * Ralf Hoffmann ) + */ + if (mod->rst >= mod->len) { + mod->rst = 0; + } + + /* Sanity check for tempo and BPM */ + if (mod->spd <= 0 || mod->spd > 255) { + mod->spd = 6; + } + CLAMP(mod->bpm, XMP_MIN_BPM, 1000); + + /* Set appropriate values for instrument volumes and subinstrument + * global volumes when QUIRK_INSVOL is not set, to keep volume values + * consistent if the user inspects struct xmp_module. We can later + * set volumes in the loaders and eliminate the quirk. + */ + for (i = 0; i < mod->ins; i++) { + if (~m->quirk & QUIRK_INSVOL) { + mod->xxi[i].vol = m->volbase; + } + for (j = 0; j < mod->xxi[i].nsm; j++) { + if (~m->quirk & QUIRK_INSVOL) { + mod->xxi[i].sub[j].gvl = m->volbase; + } + } + } + + /* Sanity check for envelopes + */ + for (i = 0; i < mod->ins; i++) { + check_envelope(&mod->xxi[i].aei); + check_envelope(&mod->xxi[i].fei); + check_envelope(&mod->xxi[i].pei); + clamp_volume_envelope(m, &mod->xxi[i].aei); + } + +#ifndef LIBXMP_CORE_DISABLE_IT + /* TODO: there's no unintrusive and clean way to get this struct into + * libxmp_load_sample currently, so bound these fields here for now. */ + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + struct extra_sample_data *xtra = &m->xtra[i]; + if (xtra->sus < 0) { + xtra->sus = 0; + } + if (xtra->sue > xxs->len) { + xtra->sue = xxs->len; + } + if (xtra->sus >= xxs->len || xtra->sus >= xtra->sue) { + xtra->sus = xtra->sue = 0; + xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR); + } + } +#endif + + p->filter = 0; + p->mode = XMP_MODE_AUTO; + p->flags = p->player_flags; +#ifndef LIBXMP_CORE_PLAYER + module_quirks(ctx); +#endif + libxmp_set_player_mode(ctx); +} + +int libxmp_prepare_scan(struct context_data *ctx) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i, ord; + + if (mod->xxp == NULL || mod->xxt == NULL) + return -XMP_ERROR_LOAD; + ord = 0; + while (ord < mod->len && mod->xxo[ord] >= mod->pat) { + ord++; + } + + if (ord >= mod->len) { + mod->len = 0; + return 0; + } + + m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *)); + if (m->scan_cnt == NULL) + return -XMP_ERROR_SYSTEM; + + for (i = 0; i < mod->len; i++) { + int pat_idx = mod->xxo[i]; + struct xmp_pattern *pat; + + /* Add pattern if referenced in orders */ + if (pat_idx < mod->pat && !mod->xxp[pat_idx]) { + if (libxmp_alloc_pattern(mod, pat_idx) < 0) { + return -XMP_ERROR_SYSTEM; + } + } + + pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx]; + m->scan_cnt[i] = (uint8 *) calloc(1, (pat && pat->rows)? pat->rows : 1); + if (m->scan_cnt[i] == NULL) + return -XMP_ERROR_SYSTEM; + } + + return 0; +} + +void libxmp_free_scan(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i; + + if (m->scan_cnt) { + for (i = 0; i < mod->len; i++) + free(m->scan_cnt[i]); + + free(m->scan_cnt); + m->scan_cnt = NULL; + } + + free(p->scan); + p->scan = NULL; +} + +/* Process player personality flags */ +int libxmp_set_player_mode(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int q; + + switch (p->mode) { + case XMP_MODE_AUTO: + break; + case XMP_MODE_MOD: + m->c4rate = C4_PAL_RATE; + m->quirk = 0; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_AMIGA; + break; + case XMP_MODE_NOISETRACKER: + m->c4rate = C4_PAL_RATE; + m->quirk = QUIRK_NOBPM; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_MODRNG; + break; + case XMP_MODE_PROTRACKER: + m->c4rate = C4_PAL_RATE; + m->quirk = QUIRK_PROTRACK; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_MODRNG; + break; + case XMP_MODE_S3M: + q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM); + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_ST3 | q; + m->read_event_type = READ_EVENT_ST3; + break; + case XMP_MODE_ST3: + q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM); + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q; + m->read_event_type = READ_EVENT_ST3; + break; + case XMP_MODE_ST3GUS: + q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM); + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q; + m->quirk &= ~QUIRK_RSTCHN; + m->read_event_type = READ_EVENT_ST3; + break; + case XMP_MODE_XM: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + break; + case XMP_MODE_FT2: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_FT2 | QUIRK_FT2BUGS; + m->read_event_type = READ_EVENT_FT2; + break; + case XMP_MODE_IT: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV; + m->read_event_type = READ_EVENT_IT; + break; + case XMP_MODE_ITSMP: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV; + m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN); + m->read_event_type = READ_EVENT_IT; + break; + default: + return -1; + } + + if (p->mode != XMP_MODE_AUTO) + m->compare_vblank = 0; + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/669_load.c b/thirdparty/libxmp/src/loaders/669_load.c new file mode 100644 index 0000000..fc3f27a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/669_load.c @@ -0,0 +1,262 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + + +static int c669_test (HIO_HANDLE *, char *, const int); +static int c669_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_669 = { + "Composer 669", + c669_test, + c669_load +}; + +static int c669_test(HIO_HANDLE *f, char *t, const int start) +{ + uint16 id; + + id = hio_read16b(f); + if (id != 0x6966 && id != 0x4a4e) + return -1; + + hio_seek(f, 110, SEEK_SET); + if (hio_read8(f) > 64) + return -1; + if (hio_read8(f) > 128) + return -1; + + hio_seek(f, 240, SEEK_SET); + if (hio_read8(f) != 0xff) + return -1; + + hio_seek(f, start + 2, SEEK_SET); + libxmp_read_title(f, t, 36); + + return 0; +} + + +struct c669_file_header { + uint8 marker[2]; /* 'if'=standard, 'JN'=extended */ + uint8 message[108]; /* Song message */ + uint8 nos; /* Number of samples (0-64) */ + uint8 nop; /* Number of patterns (0-128) */ + uint8 loop; /* Loop order number */ + uint8 order[128]; /* Order list */ + uint8 speed[128]; /* Tempo list for patterns */ + uint8 pbrk[128]; /* Break list for patterns */ +}; + +struct c669_instrument_header { + uint8 name[13]; /* ASCIIZ instrument name */ + uint32 length; /* Instrument length */ + uint32 loop_start; /* Instrument loop start */ + uint32 loopend; /* Instrument loop end */ +}; + + +#define NONE 0xff + +/* Effects bug fixed by Miod Vallat */ + +static const uint8 fx[6] = { + FX_669_PORTA_UP, + FX_669_PORTA_DN, + FX_669_TPORTA, + FX_669_FINETUNE, + FX_669_VIBRATO, + FX_SPEED_CP +}; + + +static int c669_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct c669_file_header sfh; + struct c669_instrument_header sih; + uint8 ev[3]; + + LOAD_INIT(); + + hio_read(sfh.marker, 2, 1, f); /* 'if'=standard, 'JN'=extended */ + hio_read(sfh.message, 108, 1, f); /* Song message */ + sfh.nos = hio_read8(f); /* Number of samples (0-64) */ + sfh.nop = hio_read8(f); /* Number of patterns (0-128) */ + + /* Sanity check */ + if (sfh.nos > 64 || sfh.nop > 128) + return -1; + + sfh.loop = hio_read8(f); /* Loop order number */ + if (hio_read(sfh.order, 1, 128, f) != 128) /* Order list */ + return -1; + if (hio_read(sfh.speed, 1, 128, f) != 128) /* Tempo list for patterns */ + return -1; + if (hio_read(sfh.pbrk, 1, 128, f) != 128) /* Break list for patterns */ + return -1; + + mod->chn = 8; + mod->ins = sfh.nos; + mod->pat = sfh.nop; + mod->trk = mod->chn * mod->pat; + for (i = 0; i < 128; i++) { + if (sfh.order[i] > sfh.nop) + break; + } + mod->len = i; + memcpy (mod->xxo, sfh.order, mod->len); + mod->spd = 6; + mod->bpm = 78; + mod->smp = mod->ins; + + m->period_type = PERIOD_CSPD; + m->c4rate = C4_NTSC_RATE; + + libxmp_copy_adjust(mod->name, sfh.message, 36); + libxmp_set_type(m, strncmp((char *)sfh.marker, "if", 2) ? + "UNIS 669" : "Composer 669"); + + MODULE_INFO(); + + m->comment = (char *) malloc(109); + memcpy(m->comment, sfh.message, 108); + m->comment[108] = 0; + + /* Read and convert instruments and samples */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->pat); + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read (sih.name, 13, 1, f); /* ASCIIZ instrument name */ + sih.length = hio_read32l(f); /* Instrument size */ + sih.loop_start = hio_read32l(f); /* Instrument loop start */ + sih.loopend = hio_read32l(f); /* Instrument loop end */ + + /* Sanity check */ + if (sih.length > MAX_SAMPLE_SIZE) + return -1; + + xxs->len = sih.length; + xxs->lps = sih.loop_start; + xxs->lpe = sih.loopend >= 0xfffff ? 0 : sih.loopend; + xxs->flg = xxs->lpe ? XMP_SAMPLE_LOOP : 0; /* 1 == Forward loop */ + + sub->vol = 0x40; + sub->pan = 0x80; + sub->sid = i; + + if (xxs->len > 0) + xxi->nsm = 1; + + libxmp_instrument_name(mod, i, sih.name, 13); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c", i, + xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' '); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + for (i = 0; i < mod->pat; i++) { + int pbrk; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + event = &EVENT(i, 0, 0); + event->f2t = FX_SPEED_CP; + event->f2p = sfh.speed[i]; + + pbrk = sfh.pbrk[i]; + if (pbrk >= 64) + return -1; + + event = &EVENT(i, 1, pbrk); + event->f2t = FX_BREAK; + event->f2p = 0; + + for (j = 0; j < 64 * 8; j++) { + event = &EVENT(i, j % 8, j / 8); + if(hio_read(ev, 1, 3, f) < 3) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + + if ((ev[0] & 0xfe) != 0xfe) { + event->note = 1 + 36 + (ev[0] >> 2); + event->ins = 1 + MSN(ev[1]) + ((ev[0] & 0x03) << 4); + } + + if (ev[0] != 0xff) + event->vol = (LSN(ev[1]) << 2) + 1; + + if (ev[2] != 0xff) { + if (MSN(ev[2]) >= ARRAY_SIZE(fx)) + continue; + + event->fxt = fx[MSN(ev[2])]; + event->fxp = LSN(ev[2]); + + if (event->fxt == FX_SPEED_CP) { + event->f2t = FX_PER_CANCEL; + } + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 2) + continue; + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((i % 2) * 0xff); + } + + m->quirk |= QUIRK_PBALL|QUIRK_PERPAT; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/abk_load.c b/thirdparty/libxmp/src/loaders/abk_load.c new file mode 100644 index 0000000..16c3bac --- /dev/null +++ b/thirdparty/libxmp/src/loaders/abk_load.c @@ -0,0 +1,655 @@ +/* Extended Module Player + * AMOS/STOS Music Bank Loader + * Copyright (C) 2014 Stephen J Leary and Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../effects.h" +#include "../period.h" + +#define AMOS_BANK 0x416d426b +#define AMOS_MUSIC_TYPE 0x0003 +#define AMOS_MAIN_HEADER 0x14L +#define AMOS_STRING_LEN 0x10 +#define AMOS_BASE_FREQ 8192 +#define AMOS_ABK_CHANNELS 4 +#define ABK_HEADER_SECTION_COUNT 3 + +static int abk_test (HIO_HANDLE *, char *, const int); +static int abk_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_abk = +{ + "AMOS Music Bank", + abk_test, + abk_load +}; + +/** + * @class abk_header + * @brief represents the main ABK header. + */ +struct abk_header +{ + uint32 instruments_offset; + uint32 songs_offset; + uint32 patterns_offset; +}; + +/** + * @class abk_song + * @brief represents a song in an ABK module. + */ +struct abk_song +{ + uint32 playlist_offset[AMOS_ABK_CHANNELS]; + uint16 tempo; + char song_name[AMOS_STRING_LEN]; +}; + +/** + * @class abk_playlist + * @brief represents an ABK playlist. + */ +struct abk_playlist +{ + uint16 length; + uint16 *pattern; +}; + +/** + * @class abk_instrument + * @brief represents an ABK instrument. + */ +struct abk_instrument +{ + uint32 sample_offset; + uint32 sample_length; + + uint32 repeat_offset; + uint16 repeat_end; + + uint16 sample_volume; + + char sample_name[AMOS_STRING_LEN]; +}; + + +/** + * @brief read the ABK playlist out from the file stream. This method malloc's some memory for the playlist + * and can realloc if the playlist is very long. + * @param f the stream to read from + * @param playlist_offset the offset to the playlist sections. + * @param playlist this structure is populated with the result. + */ +static void read_abk_playlist(HIO_HANDLE *f, uint32 playlist_offset, struct abk_playlist *playlist) +{ + uint16 playdata; + int arraysize; + + arraysize = 64; + + /* clear the length */ + playlist->length = 0; + + /* move to the start of the songs data section. */ + hio_seek(f, playlist_offset, SEEK_SET); + + playlist->pattern = (uint16 *) malloc(arraysize * sizeof(uint16)); + + playdata = hio_read16b(f); + + while((playdata != 0xFFFF) && (playdata != 0xFFFE)) + { + /* i hate doing a realloc in a loop + but i'd rather not traverse the list twice.*/ + + if (playlist->length >= arraysize) + { + arraysize *= 2; + playlist->pattern = (uint16 *) realloc(playlist->pattern , arraysize * sizeof(uint16)); + } + + playlist->pattern[playlist->length++] = playdata; + playdata = hio_read16b(f); + }; +} + +static int read_abk_song(HIO_HANDLE *f, struct abk_song *song, uint32 songs_section_offset) +{ + int i; + uint32 song_section; + + /* move to the start of the songs data section */ + hio_seek(f, songs_section_offset, SEEK_SET); + + if (hio_read16b(f) != 1) + { + /* we only support a single song. + * in a an abk file for now */ + return -1; + } + + song_section = hio_read32b(f); + + if (hio_seek(f, songs_section_offset + song_section, SEEK_SET) < 0) { + return -1; + } + + for (i=0; iplaylist_offset[i] = hio_read16b(f) + songs_section_offset + song_section; + } + + song->tempo = hio_read16b(f); + + /* unused. just progress the file pointer forward */ + (void) hio_read16b(f); + + if (hio_read(song->song_name, 1, AMOS_STRING_LEN, f) != AMOS_STRING_LEN) { + return -1; + } + + return 0; +} + +/** + * @brief reads an ABK pattern into a xmp_event structure. + * @param f stream to read data from. + * @param events events object to populate. + * @param pattern_offset_abs the absolute file offset to the start of the patter to read. + * @return returns the size of the pattern. + */ +static int read_abk_pattern(HIO_HANDLE *f, struct xmp_event *events, uint32 pattern_offset_abs) +{ + uint8 position; + uint8 command; + uint8 param; + uint8 inst = 0; + uint8 jumped = 0; + uint8 per_command = 0; + uint8 per_param = 0; + + uint16 delay; + uint16 patdata; + + int storepos; + if ((storepos = hio_tell(f)) < 0) { + return -1; + } + + /* count how many abk positions are used in this pattern */ + position = 0; + + hio_seek(f, pattern_offset_abs, SEEK_SET); + + /* read the first bit of pattern data */ + patdata = hio_read16b(f); + + while ((patdata != 0x8000) && (jumped == 0)) + { + if (patdata == 0x9100) + { + break; + } + + if (patdata & 0x8000) + { + command = (patdata >> 8) & 0x7F; + param = patdata & 0x007F; + + if (command != 0x03 && command != 0x09 && command != 0x0b && command != 0x0c && command != 0x0d && command < 0x10) { + per_command = 0; + per_param = 0; + } + + switch (command) + { + case 0x01: /* portamento up */ + case 0x0e: + events[position].fxt = FX_PORTA_UP; + events[position].fxp = param; + break; + case 0x02: /* portamento down */ + case 0x0f: + events[position].fxt = FX_PORTA_DN; + events[position].fxp = param; + break; + case 0x03: /* set volume */ + events[position].fxt = FX_VOLSET; + events[position].fxp = param; + break; + case 0x04: /* stop effect */ + break; + case 0x05: /* repeat */ + events[position].fxt = FX_EXTENDED; + if (param == 0) { + events[position].fxp = 0x50; + } else { + events[position].fxp = 0x60 | (param & 0x0f); + } + break; + case 0x06: /* lowpass filter off */ + events[position].fxt = FX_EXTENDED; + events[position].fxp = 0x00; + break; + case 0x07: /* lowpass filter on */ + events[position].fxt = FX_EXTENDED; + events[position].fxp = 0x01; + break; + case 0x08: /* set tempo */ + if (param > 0) { + events[position].fxt = FX_SPEED; + events[position].fxp = 100/param; + } + break; + case 0x09: /* set instrument */ + inst = param + 1; + break; + case 0x0a: /* arpeggio */ + per_command = FX_ARPEGGIO; + per_param = param; + break; + case 0x0b: /* tone portamento */ + per_command = FX_TONEPORTA; + per_param = param; + break; + case 0x0c: /* vibrato */ + per_command = FX_VIBRATO; + per_param = param; + break; + case 0x0d: /* volume slide */ + if (param != 0) { + per_command = FX_VOLSLIDE; + per_param = param; + } else { + per_command = 0; + per_param = 0; + } + break; + case 0x10: /* delay */ + if (per_command != 0 || per_param != 0) { + int i; + for (i = 0; i < param && position < 64; i++) { + events[position].fxt = per_command; + events[position].fxp = per_param; + position++; + } + } else { + position += param; + } + if (position >= 64) { + jumped = 1; + } + break; + case 0x11: /* position jump */ + events[position].fxt = FX_JUMP; + events[position].fxp = param; + /* break out of the loop because we've jumped.*/ + jumped = 1; + break; + default: +#if 0 + /* write out an error for any unprocessed commands.*/ + D_(D_WARN "ABK UNPROCESSED COMMAND: %x,%x\n", command, param); + break; +#else + return -1; +#endif + } + } + else + { + if (patdata & 0x4000) + { + /* old note format.*/ + /* old note format is 2 x 2 byte words with bit 14 set on the first word */ + /* WORD 1: 0x4XDD | X = dont care, D = delay to apply after note. (Usually in 7FDD form). + * WORD 2: 0xXPPP | PPP = Amiga Period */ + + delay = patdata & 0xff; + patdata = hio_read16b(f); + + if (patdata == 0 && delay == 0) + { + /* a zero note, with zero delay ends the pattern */ + break; + } + + if (patdata != 0) + { + /* convert the note from amiga period format to xmp's internal format.*/ + events[position].note = libxmp_period_to_note(patdata & 0x0fff); + events[position].ins = inst; + } + + /* now add on the delay */ + position += delay; + if (position >= 64) { + break; + } + } + else /* new note format */ + { + /* convert the note from amiga period format to xmp's internal format.*/ + events[position].note = libxmp_period_to_note(patdata & 0x0fff); + events[position].ins = inst; + } + } + + /* read the data for the next pass round the loop */ + patdata = hio_read16b(f); + + /* check for an EOF while reading */ + if (hio_eof(f)) + { + break; + } + } + if (position >= 1 && position < 64) { + events[position - 1].f2t = FX_BREAK; + } + + hio_seek(f, storepos, SEEK_SET); + + return position; +} + +static struct abk_instrument* read_abk_insts(HIO_HANDLE *f, uint32 inst_section_size, int count) +{ + uint16 i; + struct abk_instrument *inst; + + if (count < 1) + return NULL; + + inst = (struct abk_instrument*) malloc(count * sizeof(struct abk_instrument)); + memset(inst, 0, count * sizeof(struct abk_instrument)); + + for (i = 0; i < count; i++) + { + uint32 sampleLength; + + inst[i].sample_offset = hio_read32b(f); + inst[i].repeat_offset = hio_read32b(f); + inst[i].sample_length = hio_read16b(f); + inst[i].repeat_end = hio_read16b(f); + inst[i].sample_volume = hio_read16b(f); + sampleLength = hio_read16b(f); + + /* detect a potential bug where the sample length is not specified (and we might already know the length) */ + if (sampleLength > 4) + { + inst[i].sample_length = sampleLength; + } + + if (hio_read(inst[i].sample_name, 1, 16, f) != 16) { + free(inst); + return NULL; + } + } + + return inst; +} + +static int abk_test(HIO_HANDLE *f, char *t, const int start) +{ + struct abk_song song; + char music[8]; + uint32 song_section_offset; + + if (hio_read32b(f) != AMOS_BANK) + { + return -1; + } + + if (hio_read16b(f) != AMOS_MUSIC_TYPE) + { + return -1; + } + + /* skip over length and chip/fastmem.*/ + hio_seek(f, 6, SEEK_CUR); + + if (hio_read(music, 1, 8, f) != 8) /* get the "Music " */ + return -1; + + if (memcmp(music, "Music ", 8)) + { + return -1; + } + + /* Attempt to read title. */ + hio_read32b(f); /* instruments_offset */ + song_section_offset = hio_read32b(f); + + if (t != NULL && read_abk_song(f, &song, AMOS_MAIN_HEADER + song_section_offset) == 0) + { + libxmp_copy_adjust(t, (uint8 *)song.song_name, AMOS_STRING_LEN); + } + + return 0; +} + +static int abk_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + int i,j,k; + uint16 pattern; + uint32 first_sample_offset; + uint32 inst_section_size; + + struct xmp_module *mod = &m->mod; + + struct abk_header main_header; + struct abk_instrument *ci; + struct abk_song song; + struct abk_playlist playlist; + + hio_seek(f, AMOS_MAIN_HEADER, SEEK_SET); + + main_header.instruments_offset = hio_read32b(f); + main_header.songs_offset = hio_read32b(f); + main_header.patterns_offset = hio_read32b(f); + + /* Sanity check */ + if (main_header.instruments_offset > 0x00100000 || + main_header.songs_offset > 0x00100000 || + main_header.patterns_offset > 0x00100000) { + return -1; + } + + inst_section_size = main_header.instruments_offset; + D_(D_INFO "Sample Bytes: %d", inst_section_size); + + LOAD_INIT(); + + libxmp_set_type(m, "AMOS Music Bank"); + + if (read_abk_song(f, &song, AMOS_MAIN_HEADER + main_header.songs_offset) < 0) + { + return -1; + } + + libxmp_copy_adjust(mod->name, (uint8*) song.song_name, AMOS_STRING_LEN); + + MODULE_INFO(); + + hio_seek(f, AMOS_MAIN_HEADER + main_header.patterns_offset, SEEK_SET); + + mod->chn = AMOS_ABK_CHANNELS; + mod->pat = hio_read16b(f); + + /* Sanity check */ + if (mod->pat > 256) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + /* move to the start of the instruments section. */ + hio_seek(f, AMOS_MAIN_HEADER + main_header.instruments_offset, SEEK_SET); + mod->ins = hio_read16b(f); + + /* Sanity check */ + if (mod->ins > 255) { + return -1; + } + + mod->smp = mod->ins; + + /* Read and convert instruments and samples */ + + if (libxmp_init_instrument(m) < 0) + { + return -1; + } + + D_(D_INFO "Instruments: %d", mod->ins); + + /* read all the instruments in */ + ci = read_abk_insts(f, inst_section_size, mod->ins); + if (ci == NULL) { + return -1; + } + + /* store the location of the first sample so we can read them later. */ + first_sample_offset = AMOS_MAIN_HEADER + main_header.instruments_offset + ci[0].sample_offset; + + for (i = 0; i < mod->ins; i++) + { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + { + free(ci); + return -1; + } + + mod->xxs[i].len = ci[i].sample_length << 1; + + if (mod->xxs[i].len > 0) + { + mod->xxi[i].nsm = 1; + } + + /* the repeating stuff. */ + if (ci[i].repeat_offset > ci[i].sample_offset) + { + mod->xxs[i].lps = (ci[i].repeat_offset - ci[i].sample_offset) << 1; + } + else + { + mod->xxs[i].lps = 0; + } + mod->xxs[i].lpe = ci[i].repeat_end; + if (mod->xxs[i].lpe > 2) { + mod->xxs[i].lpe <<= 1; + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + } +/*printf("%02x lps=%04x lpe=%04x\n", i, mod->xxs[i].lps, mod->xxs[i].lpe);*/ + + mod->xxi[i].sub[0].vol = ci[i].sample_volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + libxmp_instrument_name(mod, i, (uint8*)ci[i].sample_name, 16); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' '); + } + + free(ci); + + if (libxmp_init_pattern(mod) < 0) + { + return -1; + } + + /* figure out the playlist order. + * TODO: if the 4 channels arent in the same order then + * we need to fail here. */ + + read_abk_playlist(f, song.playlist_offset[0], &playlist); + + /* move to the start of the instruments section */ + /* then convert the patterns one at a time. there is a pattern for each channel.*/ + hio_seek(f, AMOS_MAIN_HEADER + main_header.patterns_offset + 2, SEEK_SET); + + mod->len = 0; + + i = 0; + for (j = 0; j < mod->pat; j++) + { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + { + free(playlist.pattern); + return -1; + } + + for (k = 0; k < mod->chn; k++) + { + pattern = hio_read16b(f); + if (read_abk_pattern(f, mod->xxt[(i*mod->chn)+k]->event, AMOS_MAIN_HEADER + main_header.patterns_offset + pattern) < 0) { + free(playlist.pattern); + return -1; + } + } + + i++; + } + + /* Sanity check */ + if (playlist.length > 256) { + free(playlist.pattern); + return -1; + } + + mod->len = playlist.length; + + /* now push all the patterns into the module and set the length */ + for (i = 0; i < playlist.length; i++) + { + mod->xxo[i] = playlist.pattern[i]; + } + + /* free up some memory here */ + free(playlist.pattern); + + D_(D_INFO "Stored patterns: %d", mod->pat); + D_(D_INFO "Stored tracks: %d", mod->trk); + + /* Read samples */ + hio_seek(f, first_sample_offset, SEEK_SET); + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) + { + if (mod->xxs[i].len <= 2) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + { + return -1; + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/amf_load.c b/thirdparty/libxmp/src/loaders/amf_load.c new file mode 100644 index 0000000..db1ad7b --- /dev/null +++ b/thirdparty/libxmp/src/loaders/amf_load.c @@ -0,0 +1,592 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* AMF loader written based on the format specs by Miodrag Vallat with + * fixes by Andre Timmermans. + * + * The AMF format is the internal format used by DSMI, the DOS Sound and Music + * Interface, which is the engine of DMP. As DMP was able to play more and more + * module formats, the format evolved to support more features. There were 5 + * official formats, numbered from 10 (AMF 1.0) to 14 (AMF 1.4). + */ + +#include "loader.h" +#include "../period.h" + + +static int amf_test(HIO_HANDLE *, char *, const int); +static int amf_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_amf = { + "DSMI Advanced Module Format", + amf_test, + amf_load +}; + +static int amf_test(HIO_HANDLE * f, char *t, const int start) +{ + char buf[4]; + int ver; + + if (hio_read(buf, 1, 3, f) < 3) + return -1; + + if (buf[0] != 'A' || buf[1] != 'M' || buf[2] != 'F') + return -1; + + ver = hio_read8(f); + if ((ver != 0x01 && ver < 0x08) || ver > 0x0e) + return -1; + + libxmp_read_title(f, t, 32); + + return 0; +} + + +static int amf_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + uint8 buf[1024]; + int *trkmap, newtrk; + int no_loopend = 0; + int ver; + + LOAD_INIT(); + + hio_read(buf, 1, 3, f); + ver = hio_read8(f); + + if (hio_read(buf, 1, 32, f) != 32) + return -1; + + memcpy(mod->name, buf, 32); + mod->name[32] = '\0'; + libxmp_set_type(m, "DSMI %d.%d AMF", ver / 10, ver % 10); + + mod->ins = hio_read8(f); + mod->len = hio_read8(f); + mod->trk = hio_read16l(f); + mod->chn = 4; + + if (ver >= 0x09) { + mod->chn = hio_read8(f); + } + + /* Sanity check */ + if (mod->ins == 0 || mod->len == 0 || mod->trk == 0 + || mod->chn == 0 || mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + + mod->smp = mod->ins; + mod->pat = mod->len; + + if (ver == 0x09 || ver == 0x0a) + hio_read(buf, 1, 16, f); /* channel remap table */ + + if (ver >= 0x0d) { + if (hio_read(buf, 1, 32, f) != 32) /* panning table */ + return -1; + + for (i = 0; i < 32; i++) { + mod->xxc->pan = 0x80 + 2 * (int8)buf[i]; + } + mod->bpm = hio_read8(f); + mod->spd = hio_read8(f); + } else if (ver >= 0x0b) { + hio_read(buf, 1, 16, f); + } + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + + /* Orders */ + + /* + * Andre Timmermans says: + * + * Order table: track numbers in this table are not explained, + * but as you noticed you have to perform -1 to obtain the index + * in the track table. For value 0, found in some files, I think + * it means an empty track. + * + * 2021 note: this is misleading. Do not subtract 1 from the logical + * track values found in the order table; load the mapping table to + * index 1 instead. + */ + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = i; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + mod->xxp[i]->rows = ver >= 0x0e ? hio_read16l(f) : 64; + + if (mod->xxp[i]->rows > 256) + return -1; + + for (j = 0; j < mod->chn; j++) { + uint16 t = hio_read16l(f); + mod->xxp[i]->index[j] = t; + } + } + + /* Instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Probe for 2-byte loop start 1.0 format + * in facing_n.amf and sweetdrm.amf have only the sample + * loop start specified in 2 bytes + * + * These modules are an early variant of the AMF 1.0 format. Since + * normal AMF 1.0 files have 32-bit lengths/loop start/loop end, + * this is possibly caused by these fields having been expanded for + * the 1.0 format, but M2AMF 1.3 writing instrument structs with + * the old length (which would explain the missing 6 bytes). + */ + if (ver == 0x0a) { + uint8 b; + uint32 len, val; + long pos = hio_tell(f); + if (pos < 0) { + return -1; + } + for (i = 0; i < mod->ins; i++) { + b = hio_read8(f); + if (b != 0 && b != 1) { + no_loopend = 1; + break; + } + hio_seek(f, 32 + 13, SEEK_CUR); + if (hio_read32l(f) > (uint32)mod->ins) { /* check index */ + no_loopend = 1; + break; + } + len = hio_read32l(f); + if (len > 0x100000) { /* check len */ + no_loopend = 1; + break; + } + if (hio_read16l(f) == 0x0000) { /* check c2spd */ + no_loopend = 1; + break; + } + if (hio_read8(f) > 0x40) { /* check volume */ + no_loopend = 1; + break; + } + val = hio_read32l(f); /* check loop start */ + if (val > len) { + no_loopend = 1; + break; + } + val = hio_read32l(f); /* check loop end */ + if (val > len) { + no_loopend = 1; + break; + } + } + hio_seek(f, pos, SEEK_SET); + } + + if (no_loopend) { + D_(D_INFO "Detected AMF 1.0 truncated instruments."); + } + + for (i = 0; i < mod->ins; i++) { + int c2spd; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read8(f); + + hio_read(buf, 1, 32, f); + libxmp_instrument_name(mod, i, buf, 32); + + hio_read(buf, 1, 13, f); /* sample name */ + hio_read32l(f); /* sample index */ + + mod->xxi[i].nsm = 1; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].sub[0].pan = 0x80; + + if (ver >= 0x0a) { + mod->xxs[i].len = hio_read32l(f); + } else { + mod->xxs[i].len = hio_read16l(f); + } + c2spd = hio_read16l(f); + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + mod->xxi[i].sub[0].vol = hio_read8(f); + + /* + * Andre Timmermans says: + * + * [Miodrag Vallat's] doc tells that in version 1.0 only + * sample loop start is present (2 bytes) but the files I + * have tells both start and end are present (2*4 bytes). + * Maybe it should be read as version < 1.0. + * + * CM: confirmed with Maelcum's "The tribal zone" + */ + + if (no_loopend != 0) { + mod->xxs[i].lps = hio_read16l(f); + mod->xxs[i].lpe = mod->xxs[i].len; + } else if (ver >= 0x0a) { + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + } else { + /* Non-looping samples are stored with lpe=-1, not 0. */ + mod->xxs[i].lps = hio_read16l(f); + mod->xxs[i].lpe = hio_read16l(f); + + if (mod->xxs[i].lpe == 0xffff) + mod->xxs[i].lpe = 0; + } + + if (no_loopend != 0) { + mod->xxs[i].flg = mod->xxs[i].lps > 0 ? XMP_SAMPLE_LOOP : 0; + } else { + mod->xxs[i].flg = mod->xxs[i].lpe > mod->xxs[i].lps ? + XMP_SAMPLE_LOOP : 0; + } + + D_(D_INFO "[%2X] %-32.32s %05x %05x %05x %c V%02x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, mod->xxs[i].flg & XMP_SAMPLE_LOOP ? + 'L' : ' ', mod->xxi[i].sub[0].vol, c2spd); + } + + if (hio_error(f)) + return -1; + + + /* Tracks */ + + /* Index 0 is a blank track that isn't stored in the file. To keep + * things simple, load the mapping table to index 1 so the table + * index is the same as the logical track value. Older versions + * attempted to remap it to index 0 and subtract 1 from the index, + * breaking modules that directly reference the empty track in the + * order table (see "cosmos st.amf"). + */ + trkmap = (int *) calloc(mod->trk + 1, sizeof(int)); + if (trkmap == NULL) + return -1; + newtrk = 0; + + for (i = 1; i <= mod->trk; i++) { /* read track table */ + uint16 t; + t = hio_read16l(f); + trkmap[i] = t; + if (t > newtrk) newtrk = t; + } + + for (i = 0; i < mod->pat; i++) { /* read track table */ + for (j = 0; j < mod->chn; j++) { + uint16 k = mod->xxp[i]->index[j]; + + /* Use empty track if an invalid track is requested + * (such as in Lasse Makkonen "faster and louder") + */ + if (k > mod->trk) + k = 0; + mod->xxp[i]->index[j] = trkmap[k]; + } + } + + mod->trk = newtrk + 1; /* + empty track */ + free(trkmap); + + if (hio_error(f)) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk - 1); + + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); + if (mod->xxt == NULL) + return -1; + + /* Alloc track 0 as empty track */ + if (libxmp_alloc_track(mod, 0, 64) < 0) + return -1; + + /* Alloc rest of the tracks */ + for (i = 1; i < mod->trk; i++) { + uint8 t1, t2, t3; + int size; + + if (libxmp_alloc_track(mod, i, 64) < 0) /* FIXME! */ + return -1; + + /* Previous versions loaded this as a 24-bit value, but it's + * just a word. The purpose of the third byte is unknown, and + * DSMI just ignores it. + */ + size = hio_read16l(f); + hio_read8(f); + + if (hio_error(f)) + return -1; + + /* Version 0.1 AMFs do not count the end-of-track marker in + * the event count, so add 1. This hasn't been verified yet. */ + if (ver == 0x01 && size != 0) + size++; + + for (j = 0; j < size; j++) { + t1 = hio_read8(f); /* row */ + t2 = hio_read8(f); /* type */ + t3 = hio_read8(f); /* parameter */ + + if (t1 == 0xff && t2 == 0xff && t3 == 0xff) + break; + + /* If an event is encountered past the end of the + * track, treat it the same as the track end. This is + * encountered in "Avoid.amf". + */ + if (t1 >= mod->xxt[i]->rows) { + if (hio_seek(f, (size - j - 1) * 3, SEEK_CUR)) + return -1; + + break; + } + + event = &mod->xxt[i]->event[t1]; + + if (t2 < 0x7f) { /* note */ + if (t2 > 0) + event->note = t2 + 1; + /* A volume value of 0xff indicates that + * the old volume should be reused. Prior + * libxmp versions also forgot to add 1 here. + */ + event->vol = (t3 != 0xff) ? (t3 + 1) : 0; + } else if (t2 == 0x7f) { /* note retrigger */ + + /* AMF.TXT claims that this duplicates the + * previous event, which is a lie. M2AMF emits + * this for MODs when an instrument change + * occurs with no note, indicating it should + * retrigger (like in PT 2.3). Ignore this. + * + * See: "aladdin - aladd.pc.amf", "eye.amf". + */ + } else if (t2 == 0x80) { /* instrument */ + event->ins = t3 + 1; + } else { /* effects */ + uint8 fxp, fxt; + + fxp = fxt = 0; + + switch (t2) { + case 0x81: + fxt = FX_S3M_SPEED; + fxp = t3; + break; + case 0x82: + if ((int8)t3 > 0) { + fxt = FX_VOLSLIDE; + fxp = t3 << 4; + } else { + fxt = FX_VOLSLIDE; + fxp = -(int8)t3 & 0x0f; + } + break; + case 0x83: + /* See volume notes above. Previous + * releases forgot to add 1 here. */ + event->vol = (t3 + 1); + break; + case 0x84: + /* AT: Not explained for 0x84, pitch + * slide, value 0x00 corresponds to + * S3M E00 and 0x80 stands for S3M F00 + * (I checked with M2AMF) + */ + if ((int8)t3 >= 0) { + fxt = FX_PORTA_DN; + fxp = t3; + } else if (t3 == 0x80) { + fxt = FX_PORTA_UP; + fxp = 0; + } else { + fxt = FX_PORTA_UP; + fxp = -(int8)t3; + } + break; + case 0x85: + /* porta abs -- unknown */ + break; + case 0x86: + fxt = FX_TONEPORTA; + fxp = t3; + break; + + /* AT: M2AMF maps both tremolo and tremor to + * 0x87. Since tremor is only found in certain + * formats, maybe it would be better to + * consider it is a tremolo. + */ + case 0x87: + fxt = FX_TREMOLO; + fxp = t3; + break; + case 0x88: + fxt = FX_ARPEGGIO; + fxp = t3; + break; + case 0x89: + fxt = FX_VIBRATO; + fxp = t3; + break; + case 0x8a: + if ((int8)t3 > 0) { + fxt = FX_TONE_VSLIDE; + fxp = t3 << 4; + } else { + fxt = FX_TONE_VSLIDE; + fxp = -(int8)t3 & 0x0f; + } + break; + case 0x8b: + if ((int8)t3 > 0) { + fxt = FX_VIBRA_VSLIDE; + fxp = t3 << 4; + } else { + fxt = FX_VIBRA_VSLIDE; + fxp = -(int8)t3 & 0x0f; + } + break; + case 0x8c: + fxt = FX_BREAK; + fxp = t3; + break; + case 0x8d: + fxt = FX_JUMP; + fxp = t3; + break; + case 0x8e: + /* sync -- unknown */ + break; + case 0x8f: + fxt = FX_EXTENDED; + fxp = (EX_RETRIG << 4) | (t3 & 0x0f); + break; + case 0x90: + fxt = FX_OFFSET; + fxp = t3; + break; + case 0x91: + if ((int8)t3 > 0) { + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_UP << 4) | + (t3 & 0x0f); + } else { + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_DN << 4) | + (t3 & 0x0f); + } + break; + case 0x92: + if ((int8)t3 > 0) { + fxt = FX_PORTA_DN; + fxp = 0xf0 | (fxp & 0x0f); + } else { + fxt = FX_PORTA_UP; + fxp = 0xf0 | (fxp & 0x0f); + } + break; + case 0x93: + fxt = FX_EXTENDED; + fxp = (EX_DELAY << 4) | (t3 & 0x0f); + break; + case 0x94: + fxt = FX_EXTENDED; + fxp = (EX_CUT << 4) | (t3 & 0x0f); + break; + case 0x95: + fxt = FX_SPEED; + if (t3 < 0x21) + t3 = 0x21; + fxp = t3; + break; + case 0x96: + if ((int8)t3 > 0) { + fxt = FX_PORTA_DN; + fxp = 0xe0 | (fxp & 0x0f); + } else { + fxt = FX_PORTA_UP; + fxp = 0xe0 | (fxp & 0x0f); + } + break; + case 0x97: + /* Same as S3M pan, but param is offset by -0x40. */ + if (t3 == 0x64) { /* 0xA4 - 0x40 */ + fxt = FX_SURROUND; + fxp = 1; + } else if (t3 >= 0xC0 || t3 <= 0x40) { + int pan = ((int8)t3 << 1) + 0x80; + fxt = FX_SETPAN; + fxp = MIN(0xff, pan); + } + break; + } + + event->fxt = fxt; + event->fxp = fxp; + } + } + } + + + /* Samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->quirk |= QUIRK_FINEFX; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/arch_load.c b/thirdparty/libxmp/src/loaders/arch_load.c new file mode 100644 index 0000000..07b9a7d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/arch_load.c @@ -0,0 +1,510 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" + +#define MAGIC_MUSX MAGIC4('M','U','S','X') +#define MAGIC_MNAM MAGIC4('M','N','A','M') +#define MAGIC_SNAM MAGIC4('S','N','A','M') +#define MAGIC_SVOL MAGIC4('S','V','O','L') +#define MAGIC_SLEN MAGIC4('S','L','E','N') +#define MAGIC_ROFS MAGIC4('R','O','F','S') +#define MAGIC_RLEN MAGIC4('R','L','E','N') +#define MAGIC_SDAT MAGIC4('S','D','A','T') + + +static int arch_test (HIO_HANDLE *, char *, const int); +static int arch_load (struct module_data *, HIO_HANDLE *, const int); + + +const struct format_loader libxmp_loader_arch = { + "Archimedes Tracker", + arch_test, + arch_load +}; + +/* + * Linear (0 to 0x40) to logarithmic volume conversion. + * This is only used for the Protracker-compatible "linear volume" effect in + * Andy Southgate's StasisMod. In this implementation linear and logarithmic + * volumes can be freely intermixed. + */ +static const uint8 lin_table[65]={ + 0x00, 0x48, 0x64, 0x74, 0x82, 0x8a, 0x92, 0x9a, + 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xea, 0xbe, + 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, + 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, + 0xe2, 0xe2, 0xe4, 0xe4, 0xe6, 0xe6, 0xe8, 0xe8, + 0xea, 0xea, 0xec, 0xec, 0xee, 0xee, 0xf0, 0xf0, + 0xf2, 0xf2, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, + 0xfa, 0xfa, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe +}; + +#if 0 +static uint8 convert_vol(uint8 vol) { +/* return pow(2,6.0-(255.0-vol)/32)+.5; */ + return vol_table[vol]; +} +#endif + +static int arch_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MUSX) { + return -1; + } + + hio_read32l(f); + + while (!hio_eof(f)) { + uint32 id = hio_read32b(f); + uint32 len = hio_read32l(f); + + /* Sanity check */ + if (len > 0x100000) { + return -1; + } + + if (id == MAGIC_MNAM) { + libxmp_read_title(f, t, 32); + return 0; + } + + hio_seek(f, len, SEEK_CUR); + } + + libxmp_read_title(f, t, 0); + + return 0; +} + + +struct local_data { + int year, month, day; + int pflag, sflag, max_ins, max_pat; + int has_mvox; + int has_pnum; + uint8 ster[8], rows[64]; +}; + +static void fix_effect(struct xmp_event *e) +{ +#if 0 + /* for debugging */ + printf ("%c%02x ", e->fxt["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"], e->fxp); +#endif + switch (e->fxt) { + case 0x00: /* 00 xy Normal play or Arpeggio */ + e->fxt = FX_ARPEGGIO; + /* x: first halfnote to add + y: second halftone to subtract */ + break; + case 0x01: /* 01 xx Slide Up */ + e->fxt = FX_PORTA_UP; + break; + case 0x02: /* 02 xx Slide Down */ + e->fxt = FX_PORTA_DN; + break; + case 0x03: /* 03 xx Tone Portamento */ + e->fxt = FX_TONEPORTA; + break; + case 0x0b: /* 0B xx Break Pattern */ + e->fxt = FX_BREAK; + break; + case 0x0c: + /* Set linear volume */ + if (e->fxp <= 64) { + e->fxt = FX_VOLSET; + e->fxp = lin_table[e->fxp]; + } else { + e->fxp = e->fxt = 0; + } + break; + case 0x0e: /* 0E xy Set Stereo */ + case 0x19: /* StasisMod's non-standard set panning effect */ + /* y: stereo position (1-7,ignored). 1=left 4=center 7=right */ + if (e->fxp>0 && e->fxp<8) { + e->fxt = FX_SETPAN; + e->fxp = 42*e->fxp-40; + } else + e->fxt = e->fxp = 0; + break; + case 0x10: /* 10 xx Volume Slide Up */ + e->fxt = FX_VOLSLIDE_UP; + break; + case 0x11: /* 11 xx Volume Slide Down */ + e->fxt = FX_VOLSLIDE_DN; + break; + case 0x13: /* 13 xx Position Jump */ + e->fxt = FX_JUMP; + break; + case 0x15: /* 15 xy Line Jump. (not in manual) */ + /* Jump to line 10*x+y in same pattern. (10*x+y>63 ignored) */ + if (MSN(e->fxp) * 10 + LSN(e->fxp) < 64) { + e->fxt = FX_LINE_JUMP; + e->fxp = MSN(e->fxp) * 10 + LSN(e->fxp); + } else { + e->fxt = e->fxp = 0; + } + break; + case 0x1c: /* 1C xy Set Speed */ + e->fxt = FX_SPEED; + break; + case 0x1f: /* 1F xx Set Volume */ + e->fxt = FX_VOLSET; + /* all volumes are logarithmic */ + /* e->fxp = convert_vol (e->fxp); */ + break; + default: + e->fxt = e->fxp = 0; + } +} + +static int get_tinf(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int x; + + x = hio_read8(f); + data->year = ((x & 0xf0) >> 4) * 10 + (x & 0x0f); + x = hio_read8(f); + data->year += ((x & 0xf0) >> 4) * 1000 + (x & 0x0f) * 100; + + x = hio_read8(f); + data->month = ((x & 0xf0) >> 4) * 10 + (x & 0x0f); + + x = hio_read8(f); + data->day = ((x & 0xf0) >> 4) * 10 + (x & 0x0f); + + return 0; +} + +static int get_mvox(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + uint32 chn; + + chn = hio_read32l(f); + + /* Sanity check */ + if (chn < 1 || chn > 8 || data->has_mvox) { + return -1; + } + + mod->chn = chn; + data->has_mvox = 1; + return 0; +} + +static int get_ster(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + if (hio_read(data->ster, 1, 8, f) != 8) { + return -1; + } + + for (i = 0; i < mod->chn; i++) { + if (data->ster[i] > 0 && data->ster[i] < 8) { + mod->xxc[i].pan = 42 * data->ster[i] - 40; + } + } + + return 0; +} + +static int get_mnam(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + if (hio_read(mod->name, 1, 32, f) != 32) + return -1; + + return 0; +} + +static int get_anam(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + /*hio_read(m->author, 1, 32, f); */ + + return 0; +} + +static int get_mlen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + uint32 len; + + len = hio_read32l(f); + + /* Sanity check */ + if (len > 0xff) + return -1; + + mod->len = len; + return 0; +} + +static int get_pnum(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + uint32 pat; + + pat = hio_read32l(f); + + /* Sanity check */ + if (pat < 1 || pat > 64 || data->has_pnum) + return -1; + + mod->pat = pat; + data->has_pnum = 1; + return 0; +} + +static int get_plen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + + if (hio_read(data->rows, 1, 64, f) != 64) + return -1; + + return 0; +} + +static int get_sequ(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + hio_read(mod->xxo, 1, 128, f); + libxmp_set_type(m, "Archimedes Tracker"); + MODULE_INFO(); + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, k; + struct xmp_event *event; + + /* Sanity check */ + if (!data->has_mvox || !data->has_pnum) { + return -1; + } + + if (!data->pflag) { + D_(D_INFO "Stored patterns: %d", mod->pat); + data->pflag = 1; + data->max_pat = 0; + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) + return -1; + } + + /* Sanity check */ + if (data->max_pat >= mod->pat || data->max_pat >= 64) + return -1; + + i = data->max_pat; + + if (libxmp_alloc_pattern_tracks(mod, i, data->rows[i]) < 0) + return -1; + + for (j = 0; j < data->rows[i]; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + + event->fxp = hio_read8(f); + event->fxt = hio_read8(f); + event->ins = hio_read8(f); + event->note = hio_read8(f); + + if (event->note) + event->note += 48; + + fix_effect(event); + } + } + + data->max_pat++; + + return 0; +} + +static int get_samp(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + if (!data->sflag) { + mod->smp = mod->ins = 36; + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->ins); + + data->sflag = 1; + data->max_ins = 0; + } + + /* FIXME: More than 36 sample slots used. Unfortunately we + * have no way to handle this without two passes, and it's + * officially supposed to be 36, so ignore the rest. + */ + if (data->max_ins >= 36) + return 0; + + i = data->max_ins; + + mod->xxi[i].nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + if (hio_read32b(f) != MAGIC_SNAM) /* SNAM */ + return -1; + + { + /* should usually be 0x14 but zero is not unknown */ + int name_len = hio_read32l(f); + + /* Sanity check */ + if (name_len < 0 || name_len > 32) + return -1; + + hio_read(mod->xxi[i].name, 1, name_len, f); + } + + if (hio_read32b(f) != MAGIC_SVOL) /* SVOL */ + return -1; + hio_read32l(f); + /* mod->xxi[i].sub[0].vol = convert_vol(hio_read32l(f)); */ + mod->xxi[i].sub[0].vol = hio_read32l(f) & 0xff; + + if (hio_read32b(f) != MAGIC_SLEN) /* SLEN */ + return -1; + hio_read32l(f); + mod->xxs[i].len = hio_read32l(f); + + if (hio_read32b(f) != MAGIC_ROFS) /* ROFS */ + return -1; + hio_read32l(f); + mod->xxs[i].lps = hio_read32l(f); + + if (hio_read32b(f) != MAGIC_RLEN) /* RLEN */ + return -1; + hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + + if (hio_read32b(f) != MAGIC_SDAT) /* SDAT */ + return -1; + hio_read32l(f); + hio_read32l(f); /* 0x00000000 */ + + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].sub[0].pan = 0x80; + + m->vol_table = libxmp_arch_vol_table; + m->volbase = 0xff; + + if (mod->xxs[i].lpe > 2) { + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + mod->xxs[i].lpe = mod->xxs[i].lps + mod->xxs[i].lpe; + } else if (mod->xxs[i].lpe == 2 && mod->xxs[i].lps > 0) { + /* non-zero repeat offset and repeat length of 2 + * means loop to end of sample */ + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + mod->xxs[i].lpe = mod->xxs[i].len; + } + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0) + return -1; + + D_(D_INFO "[%2X] %-20.20s %05x %05x %05x %c V%02x", + i, mod->xxi[i].name, + mod->xxs[i].len, + mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + + data->max_ins++; + + return 0; +} + +static int arch_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* MUSX */ + hio_read32b(f); + + memset(&data, 0, sizeof(struct local_data)); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + libxmp_iff_register(handle, "TINF", get_tinf); + libxmp_iff_register(handle, "MVOX", get_mvox); + libxmp_iff_register(handle, "STER", get_ster); + libxmp_iff_register(handle, "MNAM", get_mnam); + libxmp_iff_register(handle, "ANAM", get_anam); + libxmp_iff_register(handle, "MLEN", get_mlen); + libxmp_iff_register(handle, "PNUM", get_pnum); + libxmp_iff_register(handle, "PLEN", get_plen); + libxmp_iff_register(handle, "SEQU", get_sequ); + libxmp_iff_register(handle, "PATT", get_patt); + libxmp_iff_register(handle, "SAMP", get_samp); + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff); + } + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/asylum_load.c b/thirdparty/libxmp/src/loaders/asylum_load.c new file mode 100644 index 0000000..1512957 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/asylum_load.c @@ -0,0 +1,185 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Based on AMF->MOD converter written by Mr. P / Powersource, 1995 + */ + +#include "loader.h" +#include "../period.h" + +static int asylum_test(HIO_HANDLE *, char *, const int); +static int asylum_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_asylum = { + "Asylum Music Format v1.0", + asylum_test, + asylum_load +}; + +static int asylum_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[32]; + + if (hio_read(buf, 1, 32, f) < 32) + return -1; + + if (memcmp(buf, "ASYLUM Music Format V1.0\0\0\0\0\0\0\0\0", 32)) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static int asylum_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + uint8 buf[2048]; + int i, j; + + LOAD_INIT(); + + hio_seek(f, 32, SEEK_CUR); /* skip magic */ + mod->spd = hio_read8(f); /* initial speed */ + mod->bpm = hio_read8(f); /* initial BPM */ + mod->ins = hio_read8(f); /* number of instruments */ + mod->pat = hio_read8(f); /* number of patterns */ + mod->len = hio_read8(f); /* module length */ + mod->rst = hio_read8(f); /* restart byte */ + + /* Sanity check - this format only stores 64 sample structures. */ + if (mod->ins > 64) { + D_(D_CRIT "invalid sample count %d", mod->ins); + return -1; + } + + hio_read(mod->xxo, 1, mod->len, f); /* read orders */ + hio_seek(f, start + 294, SEEK_SET); + + mod->chn = 8; + mod->smp = mod->ins; + mod->trk = mod->pat * mod->chn; + + snprintf(mod->type, XMP_NAME_SIZE, "Asylum Music Format v1.0"); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + for (i = 0; i < mod->ins; i++) { + uint8 insbuf[37]; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) { + return -1; + } + + if (hio_read(insbuf, 1, 37, f) != 37) { + return -1; + } + + libxmp_instrument_name(mod, i, insbuf, 22); + mod->xxi[i].sub[0].fin = (int8)(insbuf[22] << 4); + mod->xxi[i].sub[0].vol = insbuf[23]; + mod->xxi[i].sub[0].xpo = (int8)insbuf[24]; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + mod->xxs[i].len = readmem32l(insbuf + 25); + mod->xxs[i].lps = readmem32l(insbuf + 29); + mod->xxs[i].lpe = mod->xxs[i].lps + readmem32l(insbuf + 33); + + /* Sanity check - ASYLUM modules are converted from MODs. */ + if ((uint32)mod->xxs[i].len >= 0x20000) { + D_(D_CRIT "invalid sample %d length %d", i, mod->xxs[i].len); + return -1; + } + + mod->xxs[i].flg = mod->xxs[i].lpe > 2 ? XMP_SAMPLE_LOOP : 0; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %d", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin); + } + + hio_seek(f, 37 * (64 - mod->ins), SEEK_CUR); + + D_(D_INFO "Module length: %d", mod->len); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + uint8 *pos; + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (hio_read(buf, 1, 2048, f) < 2048) { + D_(D_CRIT "read error at pattern %d", i); + return -1; + } + + pos = buf; + for (j = 0; j < 64 * 8; j++) { + uint8 note; + + event = &EVENT(i, j % 8, j / 8); + memset(event, 0, sizeof(struct xmp_event)); + note = *pos++; + + if (note != 0) { + event->note = note + 13; + } + + event->ins = *pos++; + event->fxt = *pos++; + event->fxp = *pos++; + + /* TODO: m07.amf and m12.amf from Crusader: No Remorse + * use 0x1b for what looks *plausibly* like retrigger. + * No other ASYLUM modules use effects over 16. */ + if (event->fxt >= 0x10 && event->fxt != FX_MULTI_RETRIG) + event->fxt = event->fxp = 0; + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len > 1) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + mod->xxi[i].nsm = 1; + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/chip_load.c b/thirdparty/libxmp/src/loaders/chip_load.c new file mode 100644 index 0000000..bda7c47 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/chip_load.c @@ -0,0 +1,203 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +static int chip_test(HIO_HANDLE *, char *, const int); +static int chip_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_chip = { + "Chiptracker", + chip_test, + chip_load +}; + +static int chip_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[4]; + + hio_seek(f, start + 952, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + + /* Also RASP? */ + if (memcmp(buf, "KRIS", 4) != 0) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +static int chip_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct mod_header mh; + uint8 *tidx; + int i, j, tnum; + + LOAD_INIT(); + + tidx = (uint8 *) calloc(1, 1024); + if (tidx == NULL) { + goto err; + } + + hio_read(mh.name, 20, 1, f); + hio_read16b(f); + + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + + hio_read(mh.magic, 4, 1, f); + mh.len = hio_read8(f); + + /* Sanity check */ + if (mh.len > 128) { + goto err2; + } + + mh.restart = hio_read8(f); + hio_read(tidx, 1024, 1, f); + hio_read16b(f); + + mod->chn = 4; + mod->ins = 31; + mod->smp = mod->ins; + mod->len = mh.len; + mod->pat = mh.len; + mod->rst = mh.restart; + + tnum = 0; + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = i; + + for (j = 0; j < 4; j++) { + int t = tidx[2 * (4 * i + j)]; + if (t > tnum) + tnum = t; + } + } + + mod->trk = tnum + 1; + + strncpy(mod->name, (char *)mh.name, 20); + libxmp_set_type(m, "Chiptracker"); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + goto err2; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err2; + + sub = &xxi->sub[0]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = (int8) (mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + + if (xxs->len > 0) + xxi->nsm = 1; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + } + + if (libxmp_init_pattern(mod) < 0) + goto err2; + + for (i = 0; i < mod->len; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + goto err2; + mod->xxp[i]->rows = 64; + + for (j = 0; j < 4; j++) { + int t = tidx[2 * (4 * i + j)]; + mod->xxp[i]->index[j] = t; + } + } + + /* Load and convert tracks */ + D_(D_INFO "Stored tracks: %d", mod->trk); + + for (i = 0; i < mod->trk; i++) { + if (libxmp_alloc_track(mod, i, 64) < 0) + goto err2; + + for (j = 0; j < 64; j++) { + struct xmp_event *event = &mod->xxt[i]->event[j]; + uint8 e[4]; + + if (hio_read(e, 1, 4, f) < 4) { + D_(D_CRIT "read error in track %d", i); + goto err2; + } + if (e[0] && e[0] != 0xa8) + event->note = 13 + e[0] / 2; + event->ins = e[1]; + event->fxt = e[2] & 0x0f; + event->fxp = e[3]; + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (mod->xxs[i].len == 0) + continue; + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) < 0) + goto err2; + } + + free(tidx); + + return 0; + + err2: + free(tidx); + err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/coco_load.c b/thirdparty/libxmp/src/loaders/coco_load.c new file mode 100644 index 0000000..caca6f2 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/coco_load.c @@ -0,0 +1,318 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +static int coco_test (HIO_HANDLE *, char *, const int); +static int coco_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_coco = { + "Coconizer", + coco_test, + coco_load +}; + +static int check_cr(uint8 *s, int n) +{ + while (n--) { + if (*s++ == 0x0d) + return 0; + } + + return -1; +} + +static int coco_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 x, buf[20]; + uint32 y; + int n, i; + + x = hio_read8(f); + + /* check number of channels */ + if (x != 0x84 && x != 0x88) + return -1; + + if (hio_read(buf, 1, 20, f) != 20) /* read title */ + return -1; + if (check_cr(buf, 20) != 0) + return -1; + + n = hio_read8(f); /* instruments */ + if (n <= 0 || n > 100) + return -1; + + hio_read8(f); /* sequences */ + hio_read8(f); /* patterns */ + + y = hio_read32l(f); + if (y < 64 || y > 0x00100000) /* offset of sequence table */ + return -1; + + y = hio_read32l(f); /* offset of patterns */ + if (y < 64 || y > 0x00100000) + return -1; + + for (i = 0; i < n; i++) { + int ofs = hio_read32l(f); + int len = hio_read32l(f); + int vol = hio_read32l(f); + int lps = hio_read32l(f); + int lsz = hio_read32l(f); + + if (ofs < 64 || ofs > 0x00100000) + return -1; + + if (vol < 0 || vol > 0xff) + return -1; + + if (len < 0 || lps < 0 || lsz < 0) + return -1; + if (len > 0x00100000 || lps > 0x00100000 || lsz > 0x00100000) + return -1; + + if (lps > 0 && lps + lsz - 1 > len) + return -1; + + hio_read(buf, 1, 11, f); + hio_read8(f); /* unused */ + } + + hio_seek(f, start + 1, SEEK_SET); + libxmp_read_title(f, t, 20); + +#if 0 + for (i = 0; i < 20; i++) { + if (t[i] == 0x0d) + t[i] = 0; + } +#endif + + return 0; +} + + +static void fix_effect(struct xmp_event *e) +{ + switch (e->fxt) { + case 0x00: /* 00 xy Normal play or Arpeggio */ + e->fxt = FX_ARPEGGIO; + /* x: first halfnote to add + y: second halftone to subtract */ + break; + case 0x01: /* 01 xx Slide Pitch Up (until Amis Max), Frequency+InfoByte*64*/ + case 0x05: /* 05 xx Slide Pitch Up (no limit), Frequency+InfoByte*16 */ + e->fxt = FX_PORTA_UP; + break; + case 0x02: /* 02 xx Slide Pitch Down (until Amis Min), Frequency-InfoByte*64*/ + case 0x06: /* 06 xx Slide Pitch Down (0 limit), Frequency-InfoByte*16 */ + e->fxt = FX_PORTA_DN; + break; + case 0x03: /* 03 xx Fine Volume Up */ + e->fxt = FX_F_VSLIDE_UP; + break; + case 0x04: /* 04 xx Fine Volume Down */ + e->fxt = FX_F_VSLIDE_DN; + break; + case 0x07: /* 07 xy Set Stereo Position */ + /* y: stereo position (1-7,ignored). 1=left 4=center 7=right */ + if (e->fxp>0 && e->fxp<8) { + e->fxt = FX_SETPAN; + e->fxp = 42*e->fxp-40; + } else + e->fxt = e->fxp = 0; + break; + case 0x08: /* 08 xx Start Auto Fine Volume Up */ + case 0x09: /* 09 xx Start Auto Fine Volume Down */ + case 0x0a: /* 0A xx Start Auto Pitch Up */ + case 0x0b: /* 0B xx Start Auto Pitch Down */ + e->fxt = e->fxp = 0; /* FIXME */ + break; + case 0x0c: /* 0C xx Set Volume */ + e->fxt = FX_VOLSET; + e->fxp = 0xff - e->fxp; + break; + case 0x0d: /* 0D xy Pattern Break */ + e->fxt = FX_BREAK; + break; + case 0x0e: /* 0E xx Position Jump */ + e->fxt = FX_JUMP; + break; + case 0x0f: /* 0F xx Set Speed */ + e->fxt = FX_SPEED; + break; + case 0x10: /* 10 xx Unused */ + e->fxt = e->fxp = 0; + break; + case 0x11: /* 11 xx Fine Slide Pitch Up */ + case 0x12: /* 12 xx Fine Slide Pitch Down */ + e->fxt = e->fxp = 0; /* FIXME */ + break; + case 0x13: /* 13 xx Volume Up */ + e->fxt = FX_VOLSLIDE_UP; + break; + case 0x14: /* 14 xx Volume Down */ + e->fxt = FX_VOLSLIDE_DN; + break; + default: + e->fxt = e->fxp = 0; + } +} + +static int coco_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j, k; + int seq_ptr, pat_ptr, smp_ptr[100]; + + LOAD_INIT(); + + mod->chn = hio_read8(f) & 0x3f; + libxmp_read_title(f, mod->name, 20); + + for (i = 0; i < 20; i++) { + if (mod->name[i] == 0x0d) + mod->name[i] = 0; + } + + libxmp_set_type(m, "Coconizer"); + + mod->ins = mod->smp = hio_read8(f); + mod->len = hio_read8(f); + mod->pat = hio_read8(f); + mod->trk = mod->pat * mod->chn; + + seq_ptr = hio_read32l(f); + pat_ptr = hio_read32l(f); + + if (hio_error(f)) { + return -1; + } + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + m->vol_table = libxmp_arch_vol_table; + m->volbase = 0xff; + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + smp_ptr[i] = hio_read32l(f); + mod->xxs[i].len = hio_read32l(f); + mod->xxi[i].sub[0].vol = 0xff - hio_read32l(f); + mod->xxi[i].sub[0].pan = 0x80; + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = mod->xxs[i].lps + hio_read32l(f); + if (mod->xxs[i].lpe) + mod->xxs[i].lpe -= 1; + mod->xxs[i].flg = mod->xxs[i].lps > 0 ? XMP_SAMPLE_LOOP : 0; + hio_read(mod->xxi[i].name, 1, 11, f); + for (j = 0; j < 11; j++) { + if (mod->xxi[i].name[j] == 0x0d) + mod->xxi[i].name[j] = 0; + } + hio_read8(f); /* unused */ + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + if (hio_error(f)) { + return -1; + } + + D_(D_INFO "[%2X] %-10.10s %05x %05x %05x %c V%02x", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + } + + /* Sequence */ + + hio_seek(f, start + seq_ptr, SEEK_SET); + for (i = 0; ; i++) { + uint8 x = hio_read8(f); + if (x == 0xff) + break; + if (i < mod->len) + mod->xxo[i] = x; + } + + /* Patterns */ + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + if (hio_seek(f, start + pat_ptr, SEEK_SET) < 0) + return -1; + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + event->fxp = hio_read8(f); + event->fxt = hio_read8(f); + event->ins = hio_read8(f); + event->note = hio_read8(f); + if (event->note) + event->note += 12; + + if (hio_error(f)) { + return -1; + } + + fix_effect(event); + } + } + } + + /* Read samples */ + + D_(D_INFO "Stored samples : %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxi[i].nsm == 0) + continue; + + hio_seek(f, start + smp_ptr[i], SEEK_SET); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff); + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/common.c b/thirdparty/libxmp/src/loaders/common.c new file mode 100644 index 0000000..f84a9d9 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/common.c @@ -0,0 +1,594 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#if defined(HAVE_DIRENT) +#include +#include +#endif + +#include "../common.h" + +#include "xmp.h" +#include "../period.h" +#include "loader.h" + +int libxmp_init_instrument(struct module_data *m) +{ + struct xmp_module *mod = &m->mod; + + if (mod->ins > 0) { + mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument)); + if (mod->xxi == NULL) + return -1; + } + + if (mod->smp > 0) { + int i; + /* Sanity check */ + if (mod->smp > MAX_SAMPLES) { + D_(D_CRIT "sample count %d exceeds maximum (%d)", + mod->smp, MAX_SAMPLES); + return -1; + } + + mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample)); + if (mod->xxs == NULL) + return -1; + m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data)); + if (m->xtra == NULL) + return -1; + + for (i = 0; i < mod->smp; i++) { + m->xtra[i].c5spd = m->c4rate; + } + } + + return 0; +} + +/* Sample number adjustment (originally by Vitamin/CAIG). + * Only use this AFTER a previous usage of libxmp_init_instrument, + * and don't use this to free samples that have already been loaded. */ +int libxmp_realloc_samples(struct module_data *m, int new_size) +{ + struct xmp_module *mod = &m->mod; + struct xmp_sample *xxs; + struct extra_sample_data *xtra; + + /* Sanity check */ + if (new_size < 0) + return -1; + + if (new_size == 0) { + /* Don't rely on implementation-defined realloc(x,0) behavior. */ + mod->smp = 0; + free(mod->xxs); + mod->xxs = NULL; + free(m->xtra); + m->xtra = NULL; + return 0; + } + + xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * new_size); + if (xxs == NULL) + return -1; + mod->xxs = xxs; + + xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * new_size); + if (xtra == NULL) + return -1; + m->xtra = xtra; + + if (new_size > mod->smp) { + int clear_size = new_size - mod->smp; + int i; + + memset(xxs + mod->smp, 0, sizeof(struct xmp_sample) * clear_size); + memset(xtra + mod->smp, 0, sizeof(struct extra_sample_data) * clear_size); + + for (i = mod->smp; i < new_size; i++) { + m->xtra[i].c5spd = m->c4rate; + } + } + mod->smp = new_size; + return 0; +} + +int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num) +{ + if (num == 0) + return 0; + + mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(num, sizeof(struct xmp_subinstrument)); + if (mod->xxi[i].sub == NULL) + return -1; + + return 0; +} + +int libxmp_init_pattern(struct xmp_module *mod) +{ + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); + if (mod->xxt == NULL) + return -1; + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + return 0; +} + +int libxmp_alloc_pattern(struct xmp_module *mod, int num) +{ + /* Sanity check */ + if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL) + return -1; + + mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) + + sizeof(int) * (mod->chn - 1)); + if (mod->xxp[num] == NULL) + return -1; + + return 0; +} + +int libxmp_alloc_track(struct xmp_module *mod, int num, int rows) +{ + /* Sanity check */ + if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0) + return -1; + + mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * (rows - 1)); + if (mod->xxt[num] == NULL) + return -1; + + mod->xxt[num]->rows = rows; + + return 0; +} + +int libxmp_alloc_tracks_in_pattern(struct xmp_module *mod, int num) +{ + int i; + + D_(D_INFO "Alloc %d tracks of %d rows", mod->chn, mod->xxp[num]->rows); + for (i = 0; i < mod->chn; i++) { + int t = num * mod->chn + i; + int rows = mod->xxp[num]->rows; + + if (libxmp_alloc_track(mod, t, rows) < 0) + return -1; + + mod->xxp[num]->index[i] = t; + } + + return 0; +} + +int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows) +{ + /* Sanity check */ + if (rows <= 0 || rows > 256) + return -1; + + if (libxmp_alloc_pattern(mod, num) < 0) + return -1; + + mod->xxp[num]->rows = rows; + + if (libxmp_alloc_tracks_in_pattern(mod, num) < 0) + return -1; + + return 0; +} + +#ifndef LIBXMP_CORE_PLAYER +/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function + * allows those formats to work without disrupting the sanity check for other formats. + */ +int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows) +{ + /* Sanity check */ + if (rows <= 0 || rows > 32768) + return -1; + + if (libxmp_alloc_pattern(mod, num) < 0) + return -1; + + mod->xxp[num]->rows = rows; + + if (libxmp_alloc_tracks_in_pattern(mod, num) < 0) + return -1; + + return 0; +} +#endif + +char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n) +{ + CLAMP(n, 0, 31); + + return libxmp_copy_adjust(mod->xxi[i].name, r, n); +} + +char *libxmp_copy_adjust(char *s, uint8 *r, int n) +{ + int i; + + memset(s, 0, n + 1); + strncpy(s, (char *)r, n); + + for (i = 0; s[i] && i < n; i++) { + if (!isprint((unsigned char)s[i]) || ((uint8)s[i] > 127)) + s[i] = '.'; + } + + while (*s && (s[strlen(s) - 1] == ' ')) + s[strlen(s) - 1] = 0; + + return s; +} + +void libxmp_read_title(HIO_HANDLE *f, char *t, int s) +{ + uint8 buf[XMP_NAME_SIZE]; + + if (t == NULL || s < 0) + return; + + if (s >= XMP_NAME_SIZE) + s = XMP_NAME_SIZE -1; + + memset(t, 0, s + 1); + + s = hio_read(buf, 1, s, f); + buf[s] = 0; + libxmp_copy_adjust(t, buf, s); +} + +#ifndef LIBXMP_CORE_PLAYER + +int libxmp_test_name(const uint8 *s, int n, int flags) +{ + int i; + + for (i = 0; i < n; i++) { + if (s[i] == '\0' && (flags & TEST_NAME_IGNORE_AFTER_0)) + break; + if (s[i] == '\r' && (flags & TEST_NAME_IGNORE_AFTER_CR)) + break; + if (s[i] > 0x7f) + return -1; + /* ACS_Team2.mod has a backspace in instrument name */ + /* Numerous ST modules from Music Channel BBS have char 14. */ + if (s[i] > 0 && s[i] < 32 && s[i] != 0x08 && s[i] != 0x0e) + return -1; + } + + return 0; +} + +int libxmp_copy_name_for_fopen(char *dest, const char *name, int n) +{ + int converted_colon = 0; + int i; + + /* libxmp_copy_adjust, but make sure the filename won't do anything + * malicious when given to fopen. This should only be used on song files. + */ + if (!strcmp(name, ".") || strstr(name, "..") || + name[0] == '\\' || name[0] == '/' || name[0] == ':') + return -1; + + for (i = 0; i < n - 1; i++) { + uint8 t = name[i]; + if (!t) + break; + + /* Reject non-ASCII symbols as they have poorly defined behavior. + */ + if (t < 32 || t >= 0x7f) + return -1; + + /* Reject anything resembling a Windows-style root path. Allow + * converting a single : to / so things like ST-01:samplename + * work. (Leave the : as-is on Amiga.) + */ + if (i > 0 && t == ':' && !converted_colon) { + uint8 t2 = name[i + 1]; + if (!t2 || t2 == '/' || t2 == '\\') + return -1; + + converted_colon = 1; +#ifndef LIBXMP_AMIGA + dest[i] = '/'; + continue; +#endif + } + + if (t == '\\') { + dest[i] = '/'; + continue; + } + + dest[i] = t; + } + dest[i] = '\0'; + return 0; +} + +/* + * Honor Noisetracker effects: + * + * 0 - arpeggio + * 1 - portamento up + * 2 - portamento down + * 3 - Tone-portamento + * 4 - Vibrato + * A - Slide volume + * B - Position jump + * C - Set volume + * D - Pattern break + * E - Set filter (keep the led off, please!) + * F - Set speed (now up to $1F) + * + * Pex Tufvesson's notes from http://www.livet.se/mahoney/: + * + * Note that some of the modules will have bugs in the playback with all + * known PC module players. This is due to that in many demos where I synced + * events in the demo with the music, I used commands that these newer PC + * module players erroneously interpret as "newer-version-trackers commands". + * Which they aren't. + */ +void libxmp_decode_noisetracker_event(struct xmp_event *event, const uint8 *mod_event) +{ + int fxt; + + memset(event, 0, sizeof (struct xmp_event)); + event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]); + event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2])); + fxt = LSN(mod_event[2]); + + if (fxt <= 0x06 || (fxt >= 0x0a && fxt != 0x0e)) { + event->fxt = fxt; + event->fxp = mod_event[3]; + } + + libxmp_disable_continue_fx(event); +} +#endif + +void libxmp_decode_protracker_event(struct xmp_event *event, const uint8 *mod_event) +{ + int fxt = LSN(mod_event[2]); + + memset(event, 0, sizeof (struct xmp_event)); + event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]); + event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2])); + + if (fxt != 0x08) { + event->fxt = fxt; + event->fxp = mod_event[3]; + } + + libxmp_disable_continue_fx(event); +} + +void libxmp_disable_continue_fx(struct xmp_event *event) +{ + if (event->fxp == 0) { + switch (event->fxt) { + case 0x05: + event->fxt = 0x03; + break; + case 0x06: + event->fxt = 0x04; + break; + case 0x01: + case 0x02: + case 0x0a: + event->fxt = 0x00; + } + } else if (event->fxt == 0x0e) { + if (event->fxp == 0xa0 || event->fxp == 0xb0) { + event->fxt = event->fxp = 0; + } + } +} + +#ifndef LIBXMP_CORE_PLAYER +/* libxmp_check_filename_case(): */ +/* Given a directory, see if file exists there, ignoring case */ + +#if defined(_WIN32) || defined(__DJGPP__) || \ + defined(__OS2__) || defined(__EMX__) || \ + defined(_DOS) || defined(LIBXMP_AMIGA) || \ + defined(__riscos__) || \ + /* case-insensitive file system: directly probe the file */\ + \ + !defined(HAVE_DIRENT) /* or, target does not have dirent. */ +int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) +{ + char path[XMP_MAXPATH]; + snprintf(path, sizeof(path), "%s/%s", dir, name); + if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE)) + return 0; + strncpy(new_name, name, size); + return 1; +} +#else /* target has dirent */ +int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) +{ + int found = 0; + DIR *dirp; + struct dirent *d; + + dirp = opendir(dir); + if (dirp == NULL) + return 0; + + while ((d = readdir(dirp)) != NULL) { + if (!strcasecmp(d->d_name, name)) { + found = 1; + strncpy(new_name, d->d_name, size); + break; + } + } + + closedir(dirp); + + return found; +} +#endif + +void libxmp_get_instrument_path(struct module_data *m, char *path, int size) +{ + if (m->instrument_path) { + strncpy(path, m->instrument_path, size); + } else if (getenv("XMP_INSTRUMENT_PATH")) { + strncpy(path, getenv("XMP_INSTRUMENT_PATH"), size); + } else { + strncpy(path, ".", size); + } +} +#endif /* LIBXMP_CORE_PLAYER */ + +void libxmp_set_type(struct module_data *m, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + vsnprintf(m->mod.type, XMP_NAME_SIZE, fmt, ap); + va_end(ap); +} + +#ifndef LIBXMP_CORE_PLAYER +static int schism_tracker_date(int year, int month, int day) +{ + int mm = (month + 9) % 12; + int yy = year - mm / 10; + + yy = yy * 365 + (yy / 4) - (yy / 100) + (yy / 400); + mm = (mm * 306 + 5) / 10; + + return yy + mm + (day - 1); +} + +/* Generate a Schism Tracker version string. + * Schism Tracker versions are stored as follows: + * + * s_ver <= 0x50: 0.s_ver + * s_ver > 0x50, < 0xfff: days from epoch=(s_ver - 0x50) + * s_ver = 0xfff: days from epoch=l_ver + */ +void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver) +{ + if (s_ver >= 0x50) { + /* time_t epoch_sec = 1256947200; */ + int64 t = schism_tracker_date(2009, 10, 31); + int year, month, day, dayofyear; + + if (s_ver == 0xfff) { + t += l_ver; + } else + t += s_ver - 0x50; + + /* Date algorithm reimplemented from OpenMPT. + */ + year = (int)((t * 10000L + 14780) / 3652425); + dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400)); + if (dayofyear < 0) { + year--; + dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400)); + } + month = (100 * dayofyear + 52) / 3060; + day = dayofyear - (month * 306 + 5) / 10 + 1; + + year += (month + 2) / 12; + month = (month + 2) % 12 + 1; + + snprintf(buf, size, "Schism Tracker %04d-%02d-%02d", + year, month, day); + } else { + snprintf(buf, size, "Schism Tracker 0.%x", s_ver); + } +} + +/* Old MPT modules (from MPT <=1.16, older versions of OpenMPT) rely on a + * pre-amp routine that scales mix volume down. This is based on the module's + * channel count and a tracker pre-amp setting that isn't saved in the module. + * This setting defaults to 128. When fixed to 128, it can be optimized out. + * + * In OpenMPT, this pre-amp routine is only available in the MPT and OpenMPT + * 1.17 RC1 and RC2 mix modes. Changing a module to the compatible or 1.17 RC3 + * mix modes will permanently disable it for that module. OpenMPT applies the + * old mix modes to MPT <=1.16 modules, "IT 8.88", and in old OpenMPT-made + * modules that specify one of these mix modes in their extended properties. + * + * Set mod->chn and m->mvol first! + */ +void libxmp_apply_mpt_preamp(struct module_data *m) +{ + /* OpenMPT uses a slightly different table. */ + static const uint8 preamp_table[16] = + { + 0x60, 0x60, 0x60, 0x70, /* 0-7 */ + 0x80, 0x88, 0x90, 0x98, /* 8-15 */ + 0xA0, 0xA4, 0xA8, 0xB0, /* 16-23 */ + 0xB4, 0xB8, 0xBC, 0xC0, /* 24-31 */ + }; + + int chn = m->mod.chn; + CLAMP(chn, 1, 31); + + m->mvol = (m->mvol * 96) / preamp_table[chn >> 1]; + + /* Pre-amp is applied like this in the mixers of libmodplug/libopenmpt + * (still vastly simplified). + + int preamp = 128; + + if (preamp > 128) { + preamp = 128 + ((preamp - 128) * (chn + 4)) / 16; + } + preamp = preamp * m->mvol / 64; + preamp = (preamp << 7) / preamp_table[chn >> 1]; + + ... + + channel_volume_16bit = (channel_volume_16bit * preamp) >> 7; + */ +} +#endif + +char *libxmp_strdup(const char *src) +{ + size_t len = strlen(src) + 1; + char *buf = (char *) malloc(len); + if (buf) { + memcpy(buf, src, len); + } + return buf; +} diff --git a/thirdparty/libxmp/src/loaders/dbm_load.c b/thirdparty/libxmp/src/loaders/dbm_load.c new file mode 100644 index 0000000..fd96520 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/dbm_load.c @@ -0,0 +1,582 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on DigiBooster_E.guide from the DigiBoosterPro 2.20 package. + * DigiBooster Pro written by Tomasz & Waldemar Piasta + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_DBM0 MAGIC4('D','B','M','0') + + +static int dbm_test(HIO_HANDLE *, char *, const int); +static int dbm_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_dbm = { + "DigiBooster Pro", + dbm_test, + dbm_load +}; + +static int dbm_test(HIO_HANDLE * f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_DBM0) + return -1; + + hio_seek(f, 12, SEEK_CUR); + libxmp_read_title(f, t, 44); + + return 0; +} + + +struct local_data { + int have_info; + int have_song; + int have_patt; + int have_smpl; + int have_inst; + int have_venv; + int have_penv; + int maj_version; + int min_version; +}; + +struct dbm_envelope { + int ins; + int flg; + int npt; + int sus; + int lps; + int lpe; + int sus2; + struct dbm_envelope_node { + uint16 position; + int16 value; + } nodes[32]; +}; + + +static void dbm_translate_effect(struct xmp_event *event, uint8 *fxt, uint8 *fxp) +{ + switch (*fxt) { + case 0x0e: + switch (MSN(*fxp)) { + case 0x3: /* Play from backward */ + /* TODO: this is supposed to play the sample in + * reverse only once, then forward. */ + if (event->note) { + *fxt = FX_REVERSE; + *fxp = 1; + } else { + *fxt = *fxp = 0; + } + break; + case 0x4: /* Turn off sound in channel */ + *fxt = FX_EXTENDED; + *fxp = (EX_CUT << 4); + break; + case 0x5: /* Turn on/off channel */ + /* In DigiBooster Pro, this is tied to + * the channel mute toggle in the UI. */ + *fxt = FX_TRK_VOL; + *fxp = *fxp ? 0x40 : 0x00; + break; + } + break; + + case 0x1c: /* Set Real BPM */ + *fxt = FX_S3M_BPM; + break; + + default: + if (*fxt > 0x1c) + *fxt = *fxp = 0; + } +} + +static int get_info(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int val; + + /* Sanity check */ + if (data->have_info || size < 10) { + return -1; + } + data->have_info = 1; + + val = hio_read16b(f); + if (val < 0 || val > 255) { + D_(D_CRIT "Invalid number of instruments: %d", val); + goto err; + } + mod->ins = val; + + val = hio_read16b(f); + if (val < 0) { + D_(D_CRIT "Invalid number of samples: %d", val); + goto err2; + } + mod->smp = val; + + hio_read16b(f); /* Songs */ + + val = hio_read16b(f); + if (val < 0 || val > 256) { + D_(D_CRIT "Invalid number of patterns: %d", val); + goto err3; + } + mod->pat = val; + + val = hio_read16b(f); + if (val < 0 || val > XMP_MAX_CHANNELS) { + D_(D_CRIT "Invalid number of channels: %d", val); + goto err4; + } + mod->chn = val; + + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_instrument(m) < 0) + return -1; + + return 0; + + err4: + mod->pat = 0; + err3: + mod->smp = 0; + err2: + mod->ins = 0; + err: + return -1; +} + +static int get_song(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + char buffer[50]; + + /* Sanity check */ + if (data->have_song || size < 46) { + return 0; + } + data->have_song = 1; + + hio_read(buffer, 44, 1, f); + D_(D_INFO "Song name: %.44s", buffer); + + mod->len = hio_read16b(f); + D_(D_INFO "Song length: %d patterns", mod->len); + + /* Sanity check */ + if (mod->len > 256) { + return -1; + } + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = hio_read16b(f); + + return 0; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + int c2spd, flags, snum; + uint8 buffer[50]; + + /* Sanity check */ + if (data->have_inst || size < 50 * mod->ins) { + return -1; + } + data->have_inst = 1; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + mod->xxi[i].nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + if (hio_read(buffer, 30, 1, f) == 0) + return -1; + + libxmp_instrument_name(mod, i, buffer, 30); + snum = hio_read16b(f); + if (snum == 0 || snum > mod->smp) { + /* Skip remaining data for this instrument. */ + hio_seek(f, 18, SEEK_CUR); + continue; + } + + mod->xxi[i].sub[0].sid = --snum; + mod->xxi[i].sub[0].vol = hio_read16b(f); + c2spd = hio_read32b(f); + mod->xxs[snum].lps = hio_read32b(f); + mod->xxs[snum].lpe = mod->xxs[snum].lps + hio_read32b(f); + mod->xxi[i].sub[0].pan = 0x80 + (int16)hio_read16b(f); + if (mod->xxi[i].sub[0].pan > 0xff) + mod->xxi[i].sub[0].pan = 0xff; + flags = hio_read16b(f); + mod->xxs[snum].flg = flags & 0x03 ? XMP_SAMPLE_LOOP : 0; + mod->xxs[snum].flg |= flags & 0x02 ? XMP_SAMPLE_LOOP_BIDIR : 0; + + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + + D_(D_INFO "[%2X] %-30.30s #%02X V%02x P%02x %5d", + i, mod->xxi[i].name, snum, + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].pan, c2spd); + } + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, c, r, n, sz; + struct xmp_event *event, dummy; + uint8 x; + + /* Sanity check */ + if (data->have_patt || !data->have_info) { + return -1; + } + data->have_patt = 1; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d ", mod->pat); + + /* + * Note: channel and flag bytes are inverted in the format + * description document + */ + + for (i = 0; i < mod->pat; i++) { + int rows = hio_read16b(f); + if (hio_error(f)) + return -1; + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + sz = hio_read32b(f); + //printf("rows = %d, size = %d\n", mod->xxp[i]->rows, sz); + + r = 0; + /*c = -1;*/ + + while (sz > 0) { + //printf(" offset=%x, sz = %d, ", hio_tell(f), sz); + c = hio_read8(f); + if (hio_error(f)) + return -1; + + if (--sz <= 0) break; + //printf("c = %02x\n", c); + + if (c == 0) { + r++; + continue; + } + c--; + + n = hio_read8(f); + if (--sz <= 0) break; + //printf(" n = %d\n", n); + + if (c >= mod->chn || r >= mod->xxp[i]->rows) { + event = &dummy; + } else { + event = &EVENT(i, c, r); + } + + memset(event, 0, sizeof (struct xmp_event)); + + if (n & 0x01) { + x = hio_read8(f); + event->note = 13 + MSN(x) * 12 + LSN(x); + if (--sz <= 0) break; + } + if (n & 0x02) { + event->ins = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x04) { + event->fxt = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x08) { + event->fxp = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x10) { + event->f2t = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x20) { + event->f2p = hio_read8(f); + if (--sz <= 0) break; + } + + dbm_translate_effect(event, &event->fxt, &event->fxp); + dbm_translate_effect(event, &event->f2t, &event->f2p); + } + } + + return 0; +} + +static int get_smpl(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, flags; + + /* Sanity check */ + if (data->have_smpl || !data->have_info) { + return -1; + } + data->have_smpl = 1; + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + flags = hio_read32b(f); + mod->xxs[i].len = hio_read32b(f); + + if (flags & 0x02) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + } + + if (flags & 0x04) { /* Skip 32-bit samples */ + mod->xxs[i].len <<= 2; + hio_seek(f, mod->xxs[i].len, SEEK_CUR); + continue; + } + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, &mod->xxs[i], NULL) < 0) + return -1; + + if (mod->xxs[i].len == 0) + continue; + + D_(D_INFO "[%2X] %08x %05x%c%05x %05x %c", + i, flags, mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? + (mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : 'L') : ' '); + + } + + return 0; +} + +static int read_envelope(struct xmp_module *mod, struct dbm_envelope *env, HIO_HANDLE *f) +{ + int i; + + env->ins = (int)hio_read16b(f) - 1; + env->flg = hio_read8(f) & 0x7; + env->npt = (int)hio_read8(f) + 1; /* DBM counts sections, not points. */ + env->sus = hio_read8(f); + env->lps = hio_read8(f); + env->lpe = hio_read8(f); + env->sus2 = hio_read8(f); + + /* The format document claims there should be a reserved byte here but + * no DigiBooster Pro module actually has this. The revised document + * on the DigiBooster 3 website is corrected. + */ + + /* Sanity check */ + if (env->ins < 0 || env->ins >= mod->ins || env->npt > 32 || + env->sus >= 32 || env->lps >= 32 || env->lpe >= 32) + return -1; + + for (i = 0; i < 32; i++) { + env->nodes[i].position = hio_read16b(f); + env->nodes[i].value = (int16)hio_read16b(f); + } + + if (hio_error(f)) + return -1; + + return 0; +} + +static int get_venv(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct dbm_envelope env; + int i, j, nenv, ins; + + /* Sanity check */ + if (data->have_venv || !data->have_info) { + return -1; + } + data->have_venv = 1; + + nenv = hio_read16b(f); + + D_(D_INFO "Vol envelopes : %d ", nenv); + + for (i = 0; i < nenv; i++) { + if (read_envelope(mod, &env, f) != 0) + return -1; + + ins = env.ins; + mod->xxi[ins].aei.flg = env.flg; + mod->xxi[ins].aei.npt = env.npt; + mod->xxi[ins].aei.sus = env.sus; + mod->xxi[ins].aei.lps = env.lps; + mod->xxi[ins].aei.lpe = env.lpe; + + for (j = 0; j < 32; j++) { + mod->xxi[ins].aei.data[j * 2 + 0] = env.nodes[j].position; + mod->xxi[ins].aei.data[j * 2 + 1] = env.nodes[j].value; + } + } + + return 0; +} + +static int get_penv(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct dbm_envelope env; + int i, j, nenv, ins; + + /* Sanity check */ + if (data->have_penv || !data->have_info) { + return -1; + } + data->have_penv = 1; + + nenv = hio_read16b(f); + + D_(D_INFO "Pan envelopes : %d ", nenv); + + for (i = 0; i < nenv; i++) { + if (read_envelope(mod, &env, f) != 0) + return -1; + + ins = env.ins; + mod->xxi[ins].pei.flg = env.flg; + mod->xxi[ins].pei.npt = env.npt; + mod->xxi[ins].pei.sus = env.sus; + mod->xxi[ins].pei.lps = env.lps; + mod->xxi[ins].pei.lpe = env.lpe; + + for (j = 0; j < 32; j++) { + /* DigiBooster Pro 2 stores the pan value between 0 and 64. + * DigiBooster 3 stores it from -128 to 128 (Krashan - M2.dbm). + */ + if (data->maj_version >= 3) { + env.nodes[j].value = env.nodes[j].value / 4 + 32; + } + + mod->xxi[ins].pei.data[j * 2 + 0] = env.nodes[j].position; + mod->xxi[ins].pei.data[j * 2 + 1] = env.nodes[j].value; + } + } + + return 0; +} + +static int dbm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + char name[XMP_NAME_SIZE]; + uint16 version; + int i, ret; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* DBM0 */ + + memset(&data, 0, sizeof(struct local_data)); + version = hio_read16b(f); + data.maj_version = version >> 8; + data.min_version = version & 0xFF; + + hio_seek(f, 10, SEEK_CUR); + if (hio_read(name, 1, 44, f) < 44) + return -1; + name[44] = '\0'; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRK_FINEFX; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "INFO", get_info); + ret |= libxmp_iff_register(handle, "SONG", get_song); + ret |= libxmp_iff_register(handle, "INST", get_inst); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "SMPL", get_smpl); + ret |= libxmp_iff_register(handle, "VENV", get_venv); + ret |= libxmp_iff_register(handle, "PENV", get_penv); + + if (ret != 0) + return -1; + + strncpy(mod->name, name, XMP_NAME_SIZE); + snprintf(mod->type, XMP_NAME_SIZE, "DigiBooster Pro %d.%02x DBM0", + data.maj_version, data.min_version); + + MODULE_INFO(); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = 0x80; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/digi_load.c b/thirdparty/libxmp/src/loaders/digi_load.c new file mode 100644 index 0000000..e36d465 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/digi_load.c @@ -0,0 +1,252 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the DIGI Booster player v1.6 by Tap (Tomasz Piasta), with the + * help of Louise Heimann . The following + * DIGI Booster effects are _NOT_ recognized by this player: + * + * 8xx robot + * e00 filter off + * e01 filter on + * e30 backwd play sample + * e31 backwd play sample+loop + * e50 channel off + * e51 channel on + * e8x sample offset 2 + * e9x retrace + */ + +#include "loader.h" + + +static int digi_test (HIO_HANDLE *, char *, const int); +static int digi_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_digi = { + "DIGI Booster", + digi_test, + digi_load +}; + +static int digi_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[20]; + + if (hio_read(buf, 1, 20, f) < 20) + return -1; + + if (memcmp(buf, "DIGI Booster module", 19)) + return -1; + + hio_seek(f, 156, SEEK_CUR); + hio_seek(f, 3 * 4 * 32, SEEK_CUR); + hio_seek(f, 2 * 1 * 32, SEEK_CUR); + + libxmp_read_title(f, t, 32); + + return 0; +} + + +struct digi_header { + uint8 id[20]; /* ID: "DIGI Booster module\0" */ + uint8 vstr[4]; /* Version string: "Vx.y" */ + uint8 ver; /* Version hi-nibble.lo-nibble */ + uint8 chn; /* Number of channels */ + uint8 pack; /* PackEnable */ + uint8 unknown[19]; /* ?! */ + uint8 pat; /* Number of patterns */ + uint8 len; /* Song length */ + uint8 ord[128]; /* Orders */ + uint32 slen[31]; /* Sample length for 31 samples */ + uint32 sloop[31]; /* Sample loop start for 31 samples */ + uint32 sllen[31]; /* Sample loop length for 31 samples */ + uint8 vol[31]; /* Instrument volumes */ + int8 fin[31]; /* Finetunes */ + uint8 title[32]; /* Song name */ + uint8 insname[31][30]; /* Instrument names */ +}; + + +static int digi_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event = 0; + struct digi_header dh; + uint8 digi_event[4], chn_table[64]; + uint16 w; + int i, j, k, c; + + LOAD_INIT(); + + hio_read(dh.id, 20, 1, f); + + hio_read(dh.vstr, 4, 1, f); + dh.ver = hio_read8(f); + dh.chn = hio_read8(f); + dh.pack = hio_read8(f); + hio_read(dh.unknown, 19, 1, f); + dh.pat = hio_read8(f); + dh.len = hio_read8(f); + + /* Sanity check */ + if (dh.len > 127) { + return -1; + } + + hio_read(dh.ord, 128, 1, f); + + for (i = 0; i < 31; i++) + dh.slen[i] = hio_read32b(f); + for (i = 0; i < 31; i++) + dh.sloop[i] = hio_read32b(f); + for (i = 0; i < 31; i++) + dh.sllen[i] = hio_read32b(f); + for (i = 0; i < 31; i++) + dh.vol[i] = hio_read8(f); + for (i = 0; i < 31; i++) + dh.fin[i] = hio_read8s(f); + + if (hio_read(dh.title, 1, 32, f) < 32) { + D_(D_CRIT "read error at title"); + return -1; + } + + for (i = 0; i < 31; i++) { + if (hio_read(dh.insname[i], 1, 30, f) < 30) { + D_(D_CRIT "read error at instrument name %d", i); + return -1; + } + } + + mod->ins = 31; + mod->smp = mod->ins; + mod->pat = dh.pat + 1; + mod->chn = dh.chn; + mod->trk = mod->pat * mod->chn; + mod->len = dh.len + 1; + + m->period_type = PERIOD_MODRNG; + + libxmp_copy_adjust(mod->name, dh.title, 32); + libxmp_set_type(m, "DIGI Booster %-4.4s", dh.vstr); + + MODULE_INFO(); + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = dh.ord[i]; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = dh.slen[i]; + mod->xxs[i].lps = dh.sloop[i]; + mod->xxs[i].lpe = dh.sloop[i] + dh.sllen[i]; + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = dh.vol[i]; + mod->xxi[i].sub[0].fin = dh.fin[i]; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, dh.insname[i], 30); + + D_(D_INFO "[%2X] %-30.30s %04x %04x %04x %c V%02x", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', mod->xxi[i].sub[0].vol); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (dh.pack) { + w = (hio_read16b(f) - 64) >> 2; + if (hio_read(chn_table, 1, 64, f) < 64) { + D_(D_CRIT "read error at channel table %d", i); + return -1; + } + } else { + w = 64 * mod->chn; + memset(chn_table, 0xff, sizeof(chn_table)); + } + + for (j = 0; j < 64; j++) { + for (c = 0, k = 0x80; c < mod->chn; c++, k >>= 1) { + if (chn_table[j] & k) { + if (hio_read(digi_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + event = &EVENT (i, c, j); + libxmp_decode_protracker_event(event, digi_event); + switch (event->fxt) { + case 0x08: /* Robot */ + event->fxt = event->fxp = 0; + break; + case 0x0e: + switch (MSN (event->fxp)) { + case 0x00: + case 0x03: + case 0x08: + case 0x09: + event->fxt = event->fxp = 0; + break; + case 0x04: + event->fxt = 0x0c; + event->fxp = 0x00; + break; + } + } + w--; + } + } + } + + if (w) { + D_(D_CRIT "Corrupted file (w = %d)", w); + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/dt_load.c b/thirdparty/libxmp/src/loaders/dt_load.c new file mode 100644 index 0000000..175dd70 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/dt_load.c @@ -0,0 +1,354 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_D_T_ MAGIC4('D','.','T','.') + + +static int dt_test(HIO_HANDLE *, char *, const int); +static int dt_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_dt = { + "Digital Tracker", + dt_test, + dt_load +}; + +static int dt_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_D_T_) + return -1; + + hio_read32b(f); /* chunk size */ + hio_read16b(f); /* type */ + hio_read16b(f); /* 0xff then mono */ + hio_read16b(f); /* reserved */ + hio_read16b(f); /* tempo */ + hio_read16b(f); /* bpm */ + hio_read32b(f); /* undocumented */ + + libxmp_read_title(f, t, 32); + + return 0; +} + + +struct local_data { + int pflag, sflag; + int realpat; + int last_pat; + int insnum; +}; + + +static int get_d_t_(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int b; + + hio_read16b(f); /* type */ + hio_read16b(f); /* 0xff then mono */ + hio_read16b(f); /* reserved */ + mod->spd = hio_read16b(f); + if ((b = hio_read16b(f)) > 0) /* RAMBO.DTM has bpm 0 */ + mod->bpm = b; /* Not clamped by Digital Tracker. */ + hio_read32b(f); /* undocumented */ + + hio_read(mod->name, 32, 1, f); + libxmp_set_type(m, "Digital Tracker DTM"); + + MODULE_INFO(); + + return 0; +} + +static int get_s_q_(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i, maxpat; + + /* Sanity check */ + if (mod->pat != 0) { + return -1; + } + + mod->len = hio_read16b(f); + mod->rst = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 256 || mod->rst > 255) { + return -1; + } + + hio_read32b(f); /* reserved */ + + for (maxpat = i = 0; i < 128; i++) { + mod->xxo[i] = hio_read8(f); + if (mod->xxo[i] > maxpat) + maxpat = mod->xxo[i]; + } + mod->pat = maxpat + 1; + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + + /* Sanity check */ + if (data->pflag) { + return -1; + } + + mod->chn = hio_read16b(f); + data->realpat = hio_read16b(f); + mod->trk = mod->chn * mod->pat; + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + + return 0; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i, c2spd; + uint8 name[30]; + + /* Sanity check */ + if (mod->ins != 0) { + return -1; + } + + mod->ins = mod->smp = hio_read16b(f); + + /* Sanity check */ + if (mod->ins > MAX_INSTRUMENTS) { + return -1; + } + + D_(D_INFO "Instruments : %d ", mod->ins); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + uint32 repstart, replen; + int fine, flag; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read32b(f); /* reserved */ + mod->xxs[i].len = hio_read32b(f); + mod->xxi[i].nsm = !!mod->xxs[i].len; + fine = hio_read8s(f); /* finetune */ + mod->xxi[i].sub[0].vol = hio_read8(f); + mod->xxi[i].sub[0].pan = 0x80; + repstart = hio_read32b(f); + replen = hio_read32b(f); + mod->xxs[i].lps = repstart; + mod->xxs[i].lpe = repstart + replen - 1; + mod->xxs[i].flg = replen > 2 ? XMP_SAMPLE_LOOP : 0; + + if (hio_read(name, 22, 1, f) == 0) + return -1; + + libxmp_instrument_name(mod, i, name, 22); + + flag = hio_read16b(f); /* bit 0-7:resol 8:stereo */ + if ((flag & 0xff) > 8) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + mod->xxs[i].len >>= 1; + mod->xxs[i].lps >>= 1; + mod->xxs[i].lpe >>= 1; + } + + hio_read32b(f); /* midi note (0x00300000) */ + c2spd = hio_read32b(f); /* frequency */ + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + + /* It's strange that we have both c2spd and finetune */ + mod->xxi[i].sub[0].fin += fine; + + mod->xxi[i].sub[0].sid = i; + + D_(D_INFO "[%2X] %-22.22s %05x%c%05x %05x %c%c %2db V%02x F%+03d %5d", + i, mod->xxi[i].name, + mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + repstart, + replen, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + flag & 0x100 ? 'S' : ' ', + flag & 0xff, + mod->xxi[i].sub[0].vol, + fine, + c2spd); + } + + return 0; +} + +static int get_dapt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int pat, i, j, k; + struct xmp_event *event; + int rows; + + if (!data->pflag) { + D_(D_INFO "Stored patterns: %d", mod->pat); + data->pflag = 1; + data->last_pat = 0; + + if (libxmp_init_pattern(mod) < 0) + return -1; + } + + hio_read32b(f); /* 0xffffffff */ + pat = hio_read16b(f); + rows = hio_read16b(f); + + /* Sanity check */ + if (pat < 0 || pat >= mod->pat || rows < 0 || rows > 256) { + return -1; + } + if (pat < data->last_pat) { + return -1; + } + + for (i = data->last_pat; i <= pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + } + data->last_pat = pat + 1; + + for (j = 0; j < rows; j++) { + for (k = 0; k < mod->chn; k++) { + uint8 a, b, c, d; + + event = &EVENT(pat, k, j); + a = hio_read8(f); + b = hio_read8(f); + c = hio_read8(f); + d = hio_read8(f); + if (a) { + a--; + event->note = 12 * (a >> 4) + (a & 0x0f) + 12; + } + event->vol = (b & 0xfc) >> 2; + event->ins = ((b & 0x03) << 4) + (c >> 4); + event->fxt = c & 0xf; + event->fxp = d; + } + } + + return 0; +} + +static int get_dait(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + + if (!data->sflag) { + D_(D_INFO "Stored samples : %d ", mod->smp); + data->sflag = 1; + data->insnum = 0; + } + + if (size > 2) { + int ret; + + /* Sanity check */ + if (data->insnum >= mod->ins) { + return -1; + } + + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, + &mod->xxs[mod->xxi[data->insnum].sub[0].sid], NULL); + + if (ret < 0) + return -1; + } + + data->insnum++; + + return 0; +} + +static int dt_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + iff_handle handle; + struct local_data data; + struct xmp_module *mod = &m->mod; + int ret, i; + + LOAD_INIT(); + + memset(&data, 0, sizeof (struct local_data)); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "D.T.", get_d_t_); + ret |= libxmp_iff_register(handle, "S.Q.", get_s_q_); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "INST", get_inst); + ret |= libxmp_iff_register(handle, "DAPT", get_dapt); + ret |= libxmp_iff_register(handle, "DAIT", get_dait); + + if (ret != 0) + return -1; + + /* Load IFF chunks */ + ret = libxmp_iff_load(handle, m, f , &data); + libxmp_iff_release(handle); + if (ret < 0) + return -1; + + /* alloc remaining patterns */ + if (mod->xxp != NULL) { + for (i = data.last_pat; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + return -1; + } + } + } + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/emod_load.c b/thirdparty/libxmp/src/loaders/emod_load.c new file mode 100644 index 0000000..590c802 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/emod_load.c @@ -0,0 +1,262 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" + +#define MAGIC_FORM MAGIC4('F','O','R','M') +#define MAGIC_EMOD MAGIC4('E','M','O','D') +#define MAGIC_EMIC MAGIC4('E','M','I','C') + +static int emod_test(HIO_HANDLE *, char *, const int); +static int emod_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_emod = { + "Quadra Composer", + emod_test, + emod_load +}; + +static int emod_test(HIO_HANDLE * f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_FORM) + return -1; + + hio_read32b(f); + + if (hio_read32b(f) != MAGIC_EMOD) + return -1; + + if (hio_read32b(f) == MAGIC_EMIC) { + hio_read32b(f); /* skip size */ + hio_read16b(f); /* skip version */ + libxmp_read_title(f, t, 20); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + +struct local_data { + int has_emic; + int has_patt; + int has_8smp; +}; + +static int get_emic(struct module_data *m, int size, HIO_HANDLE * f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, ver; + uint8 reorder[256]; + + /* Sanity check */ + if (data->has_emic) { + return -1; + } + data->has_emic = 1; + + ver = hio_read16b(f); + hio_read(mod->name, 1, 20, f); + hio_seek(f, 20, SEEK_CUR); + mod->bpm = hio_read8(f); + mod->ins = hio_read8(f); + mod->smp = mod->ins; + + m->period_type = PERIOD_MODRNG; + + snprintf(mod->type, XMP_NAME_SIZE, "Quadra Composer EMOD v%d", ver); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + uint8 name[20]; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read8(f); /* num */ + sub->vol = hio_read8(f); + xxs->len = 2 * hio_read16b(f); + if (hio_read(name, 1, 20, f) < 20) + return -1; + libxmp_instrument_name(mod, i, name, 20); + xxs->flg = hio_read8(f) & 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = hio_read8s(f) << 4; + xxs->lps = 2 * hio_read16b(f); + xxs->lpe = xxs->lps + 2 * hio_read16b(f); + hio_read32b(f); /* ptr */ + + xxi->nsm = 1; + sub->pan = 0x80; + sub->sid = i; + + D_(D_INFO "[%2X] %-20.20s %05x %05x %05x %c V%02x %+d", + i, xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin >> 4); + } + + hio_read8(f); /* pad */ + mod->pat = hio_read8(f); + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + memset(reorder, 0, sizeof(reorder)); + + for (i = 0; i < mod->pat; i++) { + reorder[hio_read8(f)] = i; + + if (libxmp_alloc_pattern_tracks(mod, i, hio_read8(f) + 1) < 0) + return -1; + + hio_seek(f, 20, SEEK_CUR); /* skip name */ + hio_read32b(f); /* ptr */ + } + + mod->len = hio_read8(f); + + D_(D_INFO "Module length: %d", mod->len); + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = reorder[hio_read8(f)]; + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE * f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct xmp_event *event; + int i, j, k; + uint8 x; + + /* Sanity check */ + if (data->has_patt || !data->has_emic) { + return -1; + } + data->has_patt = 1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + event->ins = hio_read8(f); + event->note = hio_read8(f) + 1; + if (event->note != 0) + event->note += 48; + event->fxt = hio_read8(f) & 0x0f; + event->fxp = hio_read8(f); + + /* Fix effects */ + switch (event->fxt) { + case 0x04: + x = event->fxp; + event->fxp = + (x & 0xf0) | ((x << 1) & 0x0f); + break; + case 0x09: + event->fxt <<= 1; + break; + case 0x0b: + x = event->fxt; + event->fxt = 16 * (x / 10) + x % 10; + break; + } + } + } + } + + return 0; +} + +static int get_8smp(struct module_data *m, int size, HIO_HANDLE * f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->has_8smp || !data->has_emic) { + return -1; + } + data->has_8smp = 1; + + D_(D_INFO "Stored samples : %d ", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} + +static int emod_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + iff_handle handle; + struct local_data data; + int ret; + + LOAD_INIT(); + + memset(&data, 0, sizeof(struct local_data)); + + hio_read32b(f); /* FORM */ + hio_read32b(f); + hio_read32b(f); /* EMOD */ + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "EMIC", get_emic); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "8SMP", get_8smp); + + if (ret != 0) + return -1; + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/far_load.c b/thirdparty/libxmp/src/loaders/far_load.c new file mode 100644 index 0000000..1a6e129 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/far_load.c @@ -0,0 +1,472 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the Farandole Composer format specifications by Daniel Potter. + * + * "(...) this format is for EDITING purposes (storing EVERYTHING you're + * working on) so it may include information not completely neccessary." + */ + +#include "loader.h" +#include "../far_extras.h" + +struct far_header { + uint32 magic; /* File magic: 'FAR\xfe' */ + uint8 name[40]; /* Song name */ + uint8 crlf[3]; /* 0x0d 0x0a 0x1A */ + uint16 headersize; /* Remaining header size in bytes */ + uint8 version; /* Version MSN=major, LSN=minor */ + uint8 ch_on[16]; /* Channel on/off switches */ + uint8 rsvd1[9]; /* Current editing values */ + uint8 tempo; /* Default tempo */ + uint8 pan[16]; /* Channel pan definitions */ + uint8 rsvd2[4]; /* Grid, mode (for editor) */ + uint16 textlen; /* Length of embedded text */ +}; + +struct far_header2 { + uint8 order[256]; /* Orders */ + uint8 patterns; /* Number of stored patterns (?) */ + uint8 songlen; /* Song length in patterns */ + uint8 restart; /* Restart pos */ + uint16 patsize[256]; /* Size of each pattern in bytes */ +}; + +struct far_instrument { + uint8 name[32]; /* Instrument name */ + uint32 length; /* Length of sample (up to 64Kb) */ + uint8 finetune; /* Finetune (unsuported) */ + uint8 volume; /* Volume (unsuported?) */ + uint32 loop_start; /* Loop start */ + uint32 loopend; /* Loop end */ + uint8 sampletype; /* 1=16 bit sample */ + uint8 loopmode; +}; + +struct far_event { + uint8 note; + uint8 instrument; + uint8 volume; /* In reverse nibble order? */ + uint8 effect; +}; + + +#define MAGIC_FAR MAGIC4('F','A','R',0xfe) + + +static int far_test (HIO_HANDLE *, char *, const int); +static int far_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_far = { + "Farandole Composer", + far_test, + far_load +}; + +static int far_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_FAR) + return -1; + + libxmp_read_title(f, t, 40); + + return 0; +} + + +static void far_translate_effect(struct xmp_event *event, int fx, int param, int vol) +{ + switch (fx) { + case 0x0: /* 0x0? Global funct */ + switch (param) { + case 0x1: /* 0x01 Ramp delay on */ + case 0x2: /* 0x02 Ramp delay off */ + /* These control volume ramping and can be ignored. */ + break; + case 0x3: /* 0x03 Fulfill loop */ + /* This is intended to be sustain release, but the + * effect is buggy and just cuts most of the time. */ + event->fxt = FX_KEYOFF; + break; + case 0x4: /* 0x04 Old FAR tempo */ + event->fxt = FX_FAR_TEMPO; + event->fxp = 0x10; + break; + case 0x5: /* 0x05 New FAR tempo */ + event->fxt = FX_FAR_TEMPO; + event->fxp = 0x20; + break; + } + break; + case 0x1: /* 0x1? Pitch offset up */ + event->fxt = FX_FAR_PORTA_UP; + event->fxp = param; + break; + case 0x2: /* 0x2? Pitch offset down */ + event->fxt = FX_FAR_PORTA_DN; + event->fxp = param; + break; + case 0x3: /* 0x3? Note-port */ + event->fxt = FX_FAR_TPORTA; + event->fxp = param; + break; + case 0x4: /* 0x4? Retrigger */ + event->fxt = FX_FAR_RETRIG; + event->fxp = param; + break; + case 0x5: /* 0x5? Set Vibrato depth */ + event->fxt = FX_FAR_VIBDEPTH; + event->fxp = param; + break; + case 0x6: /* 0x6? Vibrato note */ + event->fxt = FX_FAR_VIBRATO; + event->fxp = param; + break; + case 0x7: /* 0x7? Vol Sld Up */ + event->fxt = FX_F_VSLIDE_UP; + event->fxp = (param << 4); + break; + case 0x8: /* 0x8? Vol Sld Dn */ + event->fxt = FX_F_VSLIDE_DN; + event->fxp = (param << 4); + break; + case 0x9: /* 0x9? Sustained vibrato */ + event->fxt = FX_FAR_VIBRATO; + event->fxp = 0x10 /* Vibrato sustain flag */ | param; + break; + case 0xa: /* 0xa? Slide-to-vol */ + if (vol >= 0x01 && vol <= 0x10) { + event->fxt = FX_FAR_SLIDEVOL; + event->fxp = ((vol - 1) << 4) | param; + event->vol = 0; + } + break; + case 0xb: /* 0xb? Balance */ + event->fxt = FX_SETPAN; + event->fxp = (param << 4) | param; + break; + case 0xc: /* 0xc? Note Offset */ + event->fxt = FX_FAR_DELAY; + event->fxp = param; + break; + case 0xd: /* 0xd? Fine tempo down */ + event->fxt = FX_FAR_F_TEMPO; + event->fxp = param; + break; + case 0xe: /* 0xe? Fine tempo up */ + event->fxt = FX_FAR_F_TEMPO; + event->fxp = param << 4; + break; + case 0xf: /* 0xf? Set tempo */ + event->fxt = FX_FAR_TEMPO; + event->fxp = param; + break; + } +} + +#define COMMENT_MAXLINES 44 + +static void far_read_text(char *dest, size_t textlen, HIO_HANDLE *f) +{ + /* FAR module text uses 132-char lines with no line breaks... */ + size_t end, lastchar, i; + + if (textlen > COMMENT_MAXLINES * 132) + textlen = COMMENT_MAXLINES * 132; + + while (textlen) { + end = MIN(textlen, 132); + textlen -= end; + end = hio_read(dest, 1, end, f); + + lastchar = 0; + for (i = 0; i < end; i++) { + /* Nulls in the text area are equivalent to spaces. */ + if (dest[i] == '\0') + dest[i] = ' '; + else if (dest[i] != ' ') + lastchar = i; + } + dest += lastchar + 1; + *dest++ = '\n'; + } + *dest = '\0'; +} + +static int far_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct far_module_extras *me; + int i, j, k; + struct xmp_event *event; + struct far_header ffh; + struct far_header2 ffh2; + struct far_instrument fih; + uint8 *patbuf = NULL; + uint8 sample_map[8]; + + LOAD_INIT(); + + hio_read32b(f); /* File magic: 'FAR\xfe' */ + hio_read(ffh.name, 40, 1, f); /* Song name */ + hio_read(ffh.crlf, 3, 1, f); /* 0x0d 0x0a 0x1A */ + ffh.headersize = hio_read16l(f); /* Remaining header size in bytes */ + ffh.version = hio_read8(f); /* Version MSN=major, LSN=minor */ + hio_read(ffh.ch_on, 16, 1, f); /* Channel on/off switches */ + hio_seek(f, 9, SEEK_CUR); /* Current editing values */ + ffh.tempo = hio_read8(f); /* Default tempo */ + hio_read(ffh.pan, 16, 1, f); /* Channel pan definitions */ + hio_read32l(f); /* Grid, mode (for editor) */ + ffh.textlen = hio_read16l(f); /* Length of embedded text */ + + /* Sanity check */ + if (ffh.tempo >= 16) { + return -1; + } + + if ((m->comment = (char *)malloc(ffh.textlen + COMMENT_MAXLINES + 1)) != NULL) { + far_read_text(m->comment, ffh.textlen, f); + } else { + hio_seek(f, ffh.textlen, SEEK_CUR); /* Skip song text */ + } + + hio_read(ffh2.order, 256, 1, f); /* Orders */ + ffh2.patterns = hio_read8(f); /* Number of stored patterns (?) */ + ffh2.songlen = hio_read8(f); /* Song length in patterns */ + ffh2.restart = hio_read8(f); /* Restart pos */ + for (i = 0; i < 256; i++) { + ffh2.patsize[i] = hio_read16l(f); /* Size of each pattern in bytes */ + } + + if (hio_error(f)) { + return -1; + } + + /* Skip unsupported header extension if it exists. The documentation claims + * this field is the "remaining" header size, but it's the total size. */ + if (ffh.headersize > 869 + ffh.textlen) { + if (hio_seek(f, ffh.headersize, SEEK_SET)) + return -1; + } + + mod->chn = 16; + /*mod->pat=ffh2.patterns; (Error in specs? --claudio) */ + mod->len = ffh2.songlen; + mod->rst = ffh2.restart; + memcpy (mod->xxo, ffh2.order, mod->len); + + for (mod->pat = i = 0; i < 256; i++) { + if (ffh2.patsize[i]) + mod->pat = i + 1; + } + /* Make sure referenced zero-sized patterns are also counted. */ + for (i = 0; i < mod->len; i++) { + if (mod->pat <= mod->xxo[i]) + mod->pat = mod->xxo[i] + 1; + } + + mod->trk = mod->chn * mod->pat; + + if (libxmp_far_new_module_extras(m) != 0) + return -1; + + me = FAR_MODULE_EXTRAS(*m); + me->coarse_tempo = ffh.tempo; + me->fine_tempo = 0; + me->tempo_mode = 1; + m->time_factor = FAR_TIME_FACTOR; + libxmp_far_translate_tempo(1, 0, me->coarse_tempo, &me->fine_tempo, &mod->spd, &mod->bpm); + + m->period_type = PERIOD_CSPD; + m->c4rate = C4_NTSC_RATE; + + m->quirk |= QUIRK_VSALL | QUIRK_PBALL | QUIRK_VIBALL; + + strncpy(mod->name, (char *)ffh.name, 40); + libxmp_set_type(m, "Farandole Composer %d.%d", MSN(ffh.version), LSN(ffh.version)); + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Comment bytes : %d", ffh.textlen); + D_(D_INFO "Stored patterns: %d", mod->pat); + + if ((patbuf = (uint8 *)malloc(256 * 16 * 4)) == NULL) + return -1; + + for (i = 0; i < mod->pat; i++) { + uint8 brk, note, ins, vol, fxb; + uint8 *pos; + int rows; + + if (libxmp_alloc_pattern(mod, i) < 0) + goto err; + + if (!ffh2.patsize[i]) + continue; + + rows = (ffh2.patsize[i] - 2) / 64; + + /* Sanity check */ + if (rows <= 0 || rows > 256) { + goto err; + } + + mod->xxp[i]->rows = rows; + + if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) + goto err; + + brk = hio_read8(f) + 1; + hio_read8(f); + + if (hio_read(patbuf, rows * 64, 1, f) < 1) { + D_(D_CRIT "read error at pat %d", i); + goto err; + } + + pos = patbuf; + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + + if (k == 0 && j == brk) + event->f2t = FX_BREAK; + + note = *pos++; + ins = *pos++; + vol = *pos++; + fxb = *pos++; + + if (note) + event->note = note + 48; + if (event->note || ins) + event->ins = ins + 1; + + if (vol >= 0x01 && vol <= 0x10) + event->vol = (vol - 1) * 16 + 1; + + far_translate_effect(event, MSN(fxb), LSN(fxb), vol); + } + } + } + free(patbuf); + + /* Allocate tracks for any patterns referenced with a size of 0. These + * use the configured pattern break position, which is 64 by default. */ + for (i = 0; i < mod->len; i++) { + int pat = mod->xxo[i]; + if (mod->xxp[pat]->rows == 0) { + mod->xxp[pat]->rows = 64; + if (libxmp_alloc_tracks_in_pattern(mod, pat) < 0) + return -1; + } + } + + mod->ins = -1; + if (hio_read(sample_map, 1, 8, f) < 8) { + D_(D_CRIT "read error at sample map"); + return -1; + } + for (i = 0; i < 64; i++) { + if (sample_map[i / 8] & (1 << (i % 8))) + mod->ins = i; + } + mod->ins++; + + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + + for (i = 0; i < mod->ins; i++) { + if (!(sample_map[i / 8] & (1 << (i % 8)))) + continue; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read(fih.name, 32, 1, f); /* Instrument name */ + fih.length = hio_read32l(f); /* Length of sample (up to 64Kb) */ + fih.finetune = hio_read8(f); /* Finetune (unsuported) */ + fih.volume = hio_read8(f); /* Volume (unsuported?) */ + fih.loop_start = hio_read32l(f);/* Loop start */ + fih.loopend = hio_read32l(f); /* Loop end */ + fih.sampletype = hio_read8(f); /* 1=16 bit sample */ + fih.loopmode = hio_read8(f); + + /* Sanity check */ + if (fih.length > 0x10000 || fih.loop_start > 0x10000 || + fih.loopend > 0x10000) { + return -1; + } + + mod->xxs[i].len = fih.length; + mod->xxs[i].lps = fih.loop_start; + mod->xxs[i].lpe = fih.loopend; + mod->xxs[i].flg = 0; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + if (fih.sampletype != 0) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + mod->xxs[i].len >>= 1; + mod->xxs[i].lps >>= 1; + mod->xxs[i].lpe >>= 1; + } + + mod->xxs[i].flg |= fih.loopmode ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = 0xff; /* fih.volume; */ + mod->xxi[i].sub[0].sid = i; + + libxmp_instrument_name(mod, i, fih.name, 32); + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, fih.loopmode ? 'L' : ' ', mod->xxi[i].sub[0].vol); + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + /* Panning map */ + for (i = 0; i < 16; i++) { + if (ffh.ch_on[i] == 0) + mod->xxc[i].flg |= XMP_CHANNEL_MUTE; + if (ffh.pan[i] < 0x10) + mod->xxc[i].pan = (ffh.pan[i] << 4) | ffh.pan[i]; + } + + m->volbase = 0xf0; + + return 0; + + err: + free(patbuf); + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/flt_load.c b/thirdparty/libxmp/src/loaders/flt_load.c new file mode 100644 index 0000000..a2d493c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/flt_load.c @@ -0,0 +1,491 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +static int flt_test(HIO_HANDLE *, char *, const int); +static int flt_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_flt = { + "Startrekker", + flt_test, + flt_load +}; + +static int flt_test(HIO_HANDLE * f, char *t, const int start) +{ + char buf[4]; + + hio_seek(f, start + 1080, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + + /* Also RASP? */ + if (memcmp(buf, "FLT", 3) && memcmp(buf, "EXO", 3)) + return -1; + + if (buf[3] != '4' && buf[3] != '8' && buf[3] != 'M') + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +/* Waveforms from the Startrekker 1.2 AM synth replayer code */ + +static const int8 am_waveform[3][32] = { + { 0, 25, 49, 71, 90, 106, 117, 125, /* Sine */ + 127, 125, 117, 106, 90, 71, 49, 25, + 0, -25, -49, -71, -90, -106, -117, -125, + -127, -125, -117, -106, -90, -71, -49, -25 + }, + + { -128, -120, -112, -104, -96, -88, -80, -72, /* Ramp */ + -64, -56, -48, -40, -32, -24, -16, -8, + 0, 8, 16, 24, 32, 40, 48, 56, + 64, 72, 80, 88, 96, 104, 112, 120 + }, + + { -128, -128, -128, -128, -128, -128, -128, -128, /* Square */ + -128, -128, -128, -128, -128, -128, -128, -128, + 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127 + } +}; + +struct am_instrument { + int16 l0; /* start amplitude */ + int16 a1l; /* attack level */ + int16 a1s; /* attack speed */ + int16 a2l; /* secondary attack level */ + int16 a2s; /* secondary attack speed */ + int16 sl; /* sustain level */ + int16 ds; /* decay speed */ + int16 st; /* sustain time */ + int16 rs; /* release speed */ + int16 wf; /* waveform */ + int16 p_fall; /* ? */ + int16 v_amp; /* vibrato amplitude */ + int16 v_spd; /* vibrato speed */ + int16 fq; /* base frequency */ +}; + +static int is_am_instrument(HIO_HANDLE *nt, int i) +{ + char buf[2]; + int16 wf; + + hio_seek(nt, 144 + i * 120, SEEK_SET); + hio_read(buf, 1, 2, nt); + if (memcmp(buf, "AM", 2)) + return 0; + hio_seek(nt, 24, SEEK_CUR); + wf = hio_read16b(nt); + if (hio_error(nt) || wf < 0 || wf > 3) + return 0; + + return 1; +} + +static int read_am_instrument(struct module_data *m, HIO_HANDLE *nt, int i) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_envelope *vol_env = &xxi->aei; + struct xmp_envelope *freq_env = &xxi->fei; + struct am_instrument am; + char *wave; + int a, b; + int8 am_noise[1024]; + + hio_seek(nt, 144 + i * 120 + 2 + 4, SEEK_SET); + am.l0 = hio_read16b(nt); + am.a1l = hio_read16b(nt); + am.a1s = hio_read16b(nt); + am.a2l = hio_read16b(nt); + am.a2s = hio_read16b(nt); + am.sl = hio_read16b(nt); + am.ds = hio_read16b(nt); + am.st = hio_read16b(nt); + hio_read16b(nt); + am.rs = hio_read16b(nt); + am.wf = hio_read16b(nt); + am.p_fall = -(int16) hio_read16b(nt); + am.v_amp = hio_read16b(nt); + am.v_spd = hio_read16b(nt); + am.fq = hio_read16b(nt); + + if (hio_error(nt)) { + return -1; + } + +#if 0 + printf + ("L0=%d A1L=%d A1S=%d A2L=%d A2S=%d SL=%d DS=%d ST=%d RS=%d WF=%d\n", + am.l0, am.a1l, am.a1s, am.a2l, am.a2s, am.sl, am.ds, am.st, am.rs, + am.wf); +#endif + + if (am.wf < 3) { + xxs->len = 32; + xxs->lps = 0; + xxs->lpe = 32; + wave = (char *)&am_waveform[am.wf][0]; + } else { + int j; + + xxs->len = 1024; + xxs->lps = 0; + xxs->lpe = 1024; + + for (j = 0; j < 1024; j++) + am_noise[j] = rand() % 256; + + wave = (char *)&am_noise[0]; + } + + xxs->flg = XMP_SAMPLE_LOOP; + xxi->sub[0].vol = 0x40; /* prelude.mod has 0 in instrument */ + xxi->nsm = 1; + xxi->sub[0].xpo = -12 * am.fq; + xxi->sub[0].vwf = 0; + xxi->sub[0].vde = am.v_amp << 2; + xxi->sub[0].vra = am.v_spd; + + /* + * AM synth envelope parameters based on the Startrekker 1.2 docs + * + * L0 Start amplitude for the envelope + * A1L Attack level + * A1S The speed that the amplitude changes to the attack level, $1 + * is slow and $40 is fast. + * A2L Secondary attack level, for those who likes envelopes... + * A2S Secondary attack speed. + * DS The speed that the amplitude decays down to the: + * SL Sustain level. There is remains for the time set by the + * ST Sustain time. + * RS Release speed. The speed that the amplitude falls from ST to 0. + */ + if (am.a1s == 0) + am.a1s = 1; + if (am.a2s == 0) + am.a2s = 1; + if (am.ds == 0) + am.ds = 1; + if (am.rs == 0) + am.rs = 1; + + vol_env->npt = 6; + vol_env->flg = XMP_ENVELOPE_ON; + + vol_env->data[0] = 0; + vol_env->data[1] = am.l0 / 4; + + /* + * Startrekker increments/decrements the envelope by the stage speed + * until it reaches the next stage level. + * + * ^ + * | + * 100 +.........o + * | /: + * A2L +.......o : x = 256 * (A2L - A1L) / (256 - A1L) + * | /: : + * | / : : + * A1L +....o..:.: + * | : : : + * | :x : : + * +----+--+-+-----> + * | | + * |256/| + * A2S + */ + + if (am.a1l > am.l0) { + a = am.a1l - am.l0; + b = 256 - am.l0; + } else { + a = am.l0 - am.a1l; + b = am.l0; + } + if (b == 0) + b = 1; + + vol_env->data[2] = vol_env->data[0] + (256 * a) / (am.a1s * b); + vol_env->data[3] = am.a1l / 4; + + if (am.a2l > am.a1l) { + a = am.a2l - am.a1l; + b = 256 - am.a1l; + } else { + a = am.a1l - am.a2l; + b = am.a1l; + } + if (b == 0) + b = 1; + + vol_env->data[4] = vol_env->data[2] + (256 * a) / (am.a2s * b); + vol_env->data[5] = am.a2l / 4; + + if (am.sl > am.a2l) { + a = am.sl - am.a2l; + b = 256 - am.a2l; + } else { + a = am.a2l - am.sl; + b = am.a2l; + } + if (b == 0) + b = 1; + + vol_env->data[6] = vol_env->data[4] + (256 * a) / (am.ds * b); + vol_env->data[7] = am.sl / 4; + vol_env->data[8] = vol_env->data[6] + am.st; + vol_env->data[9] = am.sl / 4; + vol_env->data[10] = vol_env->data[8] + (256 / am.rs); + vol_env->data[11] = 0; + + /* + * Implement P.FALL using pitch envelope + */ + + if (am.p_fall) { + freq_env->npt = 2; + freq_env->flg = XMP_ENVELOPE_ON; + freq_env->data[0] = 0; + freq_env->data[1] = 0; + freq_env->data[2] = 1024 / abs(am.p_fall); + freq_env->data[3] = 10 * (am.p_fall < 0 ? -256 : 256); + } + + if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD, xxs, wave)) + return -1; + + return 0; +} + +static int flt_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct mod_header mh; + uint8 mod_event[4]; + const char *tracker; + char filename[1024]; + char buf[16]; + HIO_HANDLE *nt; + int am_synth; + + LOAD_INIT(); + + /* See if we have the synth parameters file */ + am_synth = 0; + snprintf(filename, 1024, "%s%s.NT", m->dirname, m->basename); + if ((nt = hio_open(filename, "rb")) == NULL) { + snprintf(filename, 1024, "%s%s.nt", m->dirname, m->basename); + if ((nt = hio_open(filename, "rb")) == NULL) { + snprintf(filename, 1024, "%s%s.AS", m->dirname, + m->basename); + if ((nt = hio_open(filename, "rb")) == NULL) { + snprintf(filename, 1024, "%s%s.as", m->dirname, + m->basename); + nt = hio_open(filename, "rb"); + } + } + } + + tracker = "Startrekker"; + + if (nt) { + if (hio_read(buf, 1, 16, nt) != 16) { + goto err; + } + if (memcmp(buf, "ST1.2 ModuleINFO", 16) == 0) { + am_synth = 1; + tracker = "Startrekker 1.2"; + } else if (memcmp(buf, "ST1.3 ModuleINFO", 16) == 0) { + am_synth = 1; + tracker = "Startrekker 1.3"; + } else if (memcmp(buf, "AudioSculpture10", 16) == 0) { + am_synth = 1; + tracker = "AudioSculpture 1.0"; + } + } + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + hio_read(mh.magic, 4, 1, f); + + if (mh.magic[3] == '4') { + mod->chn = 4; + } else { + mod->chn = 8; + } + + mod->ins = 31; + mod->smp = mod->ins; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->chn > 4) + mod->xxo[i] >>= 1; + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + + mod->trk = mod->chn * mod->pat; + + strncpy(mod->name, (char *)mh.name, 20); + libxmp_set_type(m, "%s %4.4s", tracker, mh.magic); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + goto err; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err; + + sub = &xxi->sub[0]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = 2 * mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = (int8) (mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + xxi->rls = 0xfff; + + if (xxs->len > 0) + xxi->nsm = 1; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + } + + if (libxmp_init_pattern(mod) < 0) + goto err; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* "The format you are looking for is FLT8, and the ONLY two + * differences are: It says FLT8 instead of FLT4 or M.K., AND, the + * patterns are PAIRED. I thought this was the easiest 8 track + * format possible, since it can be loaded in a normal 4 channel + * tracker if you should want to rip sounds or patterns. So, in a + * 8 track FLT8 module, patterns 00 and 01 is "really" pattern 00. + * Patterns 02 and 03 together is "really" pattern 01. Thats it. + * Oh well, I didnt have the time to implement all effect commands + * either, so some FLT8 modules would play back badly (I think + * especially the portamento command uses a different "scale" than + * the normal portamento command, that would be hard to patch). + */ + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + goto err; + } + libxmp_decode_noisetracker_event(event, mod_event); + } + if (mod->chn > 4) { + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, (j % 4) + 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + goto err; + } + libxmp_decode_noisetracker_event(event, mod_event); + + /* no macros */ + if (event->fxt == 0x0e) + event->fxt = event->fxp = 0; + } + } + } + + /* no such limit for synth instruments + * mod->flg |= XXM_FLG_MODRNG; + */ + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (mod->xxs[i].len == 0) { + if (am_synth && is_am_instrument(nt, i)) { + if (read_am_instrument(m, nt, i) < 0) { + D_(D_CRIT "Missing nt file"); + goto err; + } + } + continue; + } + if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) < + 0) { + goto err; + } + } + + if (nt) { + hio_close(nt); + } + + return 0; + + err: + if (nt) { + hio_close(nt); + } + + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/fnk_load.c b/thirdparty/libxmp/src/loaders/fnk_load.c new file mode 100644 index 0000000..8eaff91 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/fnk_load.c @@ -0,0 +1,342 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +#define MAGIC_Funk MAGIC4('F','u','n','k') + + +static int fnk_test (HIO_HANDLE *, char *, const int); +static int fnk_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_fnk = { + "Funktracker", + fnk_test, + fnk_load +}; + +static int fnk_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 a, b; + int size; + + if (hio_read32b(f) != MAGIC_Funk) + return -1; + + hio_read8(f); + a = hio_read8(f); + b = hio_read8(f); + hio_read8(f); + + if ((a >> 1) < 10) /* creation year (-1980) */ + return -1; + + if (MSN(b) > 7 || LSN(b) > 9) /* CPU and card */ + return -1; + + size = hio_read32l(f); + if (size < 1024) + return -1; + + if (hio_size(f) != size) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + + +struct fnk_instrument { + uint8 name[19]; /* ASCIIZ instrument name */ + uint32 loop_start; /* Instrument loop start */ + uint32 length; /* Instrument length */ + uint8 volume; /* Volume (0-255) */ + uint8 pan; /* Pan (0-255) */ + uint8 shifter; /* Portamento and offset shift */ + uint8 waveform; /* Vibrato and tremolo waveforms */ + uint8 retrig; /* Retrig and arpeggio speed */ +}; + +struct fnk_header { + uint8 marker[4]; /* 'Funk' */ + uint8 info[4]; /* */ + uint32 filesize; /* File size */ + uint8 fmt[4]; /* F2xx, Fkxx or Fvxx */ + uint8 loop; /* Loop order number */ + uint8 order[256]; /* Order list */ + uint8 pbrk[128]; /* Break list for patterns */ + struct fnk_instrument fih[64]; /* Instruments */ +}; + + +static void fnk_translate_event(struct xmp_event *event, const uint8 ev[3], + const struct fnk_header *ffh) +{ + switch (ev[0] >> 2) { + case 0x3f: + case 0x3e: + case 0x3d: + break; + default: + event->note = 37 + (ev[0] >> 2); + event->ins = 1 + MSN(ev[1]) + ((ev[0] & 0x03) << 4); + event->vol = ffh->fih[event->ins - 1].volume; + } + + switch (LSN(ev[1])) { + case 0x00: + event->fxt = FX_PER_PORTA_UP; + event->fxp = ev[2]; + break; + case 0x01: + event->fxt = FX_PER_PORTA_DN; + event->fxp = ev[2]; + break; + case 0x02: + event->fxt = FX_PER_TPORTA; + event->fxp = ev[2]; + break; + case 0x03: + event->fxt = FX_PER_VIBRATO; + event->fxp = ev[2]; + break; + case 0x06: + event->fxt = FX_PER_VSLD_UP; + event->fxp = ev[2] << 1; + break; + case 0x07: + event->fxt = FX_PER_VSLD_DN; + event->fxp = ev[2] << 1; + break; + case 0x0b: + event->fxt = FX_ARPEGGIO; + event->fxp = ev[2]; + break; + case 0x0d: + event->fxt = FX_VOLSET; + event->fxp = ev[2]; + break; + case 0x0e: + if (ev[2] == 0x0a || ev[2] == 0x0b || ev[2] == 0x0c) { + event->fxt = FX_PER_CANCEL; + break; + } + + switch (MSN(ev[2])) { + case 0x1: + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | LSN(ev[2]); + break; + case 0x2: + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | LSN(ev[2]); + break; + case 0xd: + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | LSN(ev[2]); + break; + case 0xe: + event->fxt = FX_SETPAN; + event->fxp = 8 + (LSN(ev[2]) << 4); + break; + case 0xf: + event->fxt = FX_SPEED; + event->fxp = LSN(ev[2]); + break; + } + } +} + +static int fnk_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + /* int day, month, year; */ + struct xmp_event *event; + struct fnk_header ffh; + uint8 ev[3]; + + LOAD_INIT(); + + hio_read(ffh.marker, 4, 1, f); + hio_read(ffh.info, 4, 1, f); + ffh.filesize = hio_read32l(f); + hio_read(ffh.fmt, 4, 1, f); + ffh.loop = hio_read8(f); + hio_read(ffh.order, 256, 1, f); + hio_read(ffh.pbrk, 128, 1, f); + + for (i = 0; i < 128; i++) { + if (ffh.pbrk[i] >= 64) { + return -1; + } + } + + for (i = 0; i < 64; i++) { + hio_read(ffh.fih[i].name, 19, 1, f); + ffh.fih[i].loop_start = hio_read32l(f); + ffh.fih[i].length = hio_read32l(f); + ffh.fih[i].volume = hio_read8(f); + ffh.fih[i].pan = hio_read8(f); + ffh.fih[i].shifter = hio_read8(f); + ffh.fih[i].waveform = hio_read8(f); + ffh.fih[i].retrig = hio_read8(f); + /* Sanity check */ + if (ffh.fih[i].length >= ffh.filesize) { + return -1; + } + } + + /* day = ffh.info[0] & 0x1f; + month = ((ffh.info[1] & 0x01) << 3) | ((ffh.info[0] & 0xe0) >> 5); + year = 1980 + ((ffh.info[1] & 0xfe) >> 1); */ + + mod->smp = mod->ins = 64; + + for (i = 0; i < 256 && ffh.order[i] != 0xff; i++) { + if (ffh.order[i] > mod->pat) + mod->pat = ffh.order[i]; + } + mod->pat++; + + /* Sanity check */ + if (mod->pat > 128) { + return -1; + } + + mod->len = i; + memcpy (mod->xxo, ffh.order, mod->len); + + mod->spd = 4; + mod->bpm = 125; + mod->chn = 0; + + /* + * If an R1 fmt (funktype = Fk** or Fv**), then ignore byte 3. It's + * unreliable. It used to store the (GUS) sample memory requirement. + */ + if (ffh.fmt[0] == 'F' && ffh.fmt[1] == '2') { + if (((int8)ffh.info[3] >> 1) & 0x40) + mod->bpm -= (ffh.info[3] >> 1) & 0x3f; + else + mod->bpm += (ffh.info[3] >> 1) & 0x3f; + + libxmp_set_type(m, "FunktrackerGOLD"); + } else if (ffh.fmt[0] == 'F' && (ffh.fmt[1] == 'v' || ffh.fmt[1] == 'k')) { + libxmp_set_type(m, "Funktracker"); + } else { + mod->chn = 8; + libxmp_set_type(m, "Funktracker DOS32"); + } + + if (mod->chn == 0) { + mod->chn = (ffh.fmt[2] < '0') || (ffh.fmt[2] > '9') || + (ffh.fmt[3] < '0') || (ffh.fmt[3] > '9') ? 8 : + (ffh.fmt[2] - '0') * 10 + ffh.fmt[3] - '0'; + + /* Sanity check */ + if (mod->chn <= 0 || mod->chn > XMP_MAX_CHANNELS) + return -1; + } + + mod->bpm = 4 * mod->bpm / 5; + mod->trk = mod->chn * mod->pat; + + /* FNK allows mode per instrument but we don't, so use linear for all */ + m->period_type = PERIOD_LINEAR; + + MODULE_INFO(); + /* D_(D_INFO "Creation date: %02d/%02d/%04d", day, month, year); */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Convert instruments */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = ffh.fih[i].length; + mod->xxs[i].lps = ffh.fih[i].loop_start; + if (mod->xxs[i].lps == -1) + mod->xxs[i].lps = 0; + mod->xxs[i].lpe = ffh.fih[i].length; + mod->xxs[i].flg = ffh.fih[i].loop_start != -1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = ffh.fih[i].volume; + mod->xxi[i].sub[0].pan = ffh.fih[i].pan; + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, ffh.fih[i].name, 19); + + D_(D_INFO "[%2X] %-20.20s %04x %04x %04x %c V%02x P%02x", i, + mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].pan); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + EVENT(i, 0, ffh.pbrk[i]).f2t = FX_BREAK; + + for (j = 0; j < 64; j++) { + for(k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + if (hio_read(ev, 1, 3, f) < 3) + return -1; + + fnk_translate_event(event, ev, &ffh); + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 2) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = 0x80; + + m->volbase = 0xff; + m->quirk = QUIRK_VSALL; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/gal4_load.c b/thirdparty/libxmp/src/loaders/gal4_load.c new file mode 100644 index 0000000..1e11b80 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/gal4_load.c @@ -0,0 +1,481 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +/* Galaxy Music System 4.0 module file loader + * + * Based on modules converted using mod2j2b.exe + */ + +static int gal4_test(HIO_HANDLE *, char *, const int); +static int gal4_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_gal4 = { + "Galaxy Music System 4.0", + gal4_test, + gal4_load +}; + +static int gal4_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC4('R', 'I', 'F', 'F')) + return -1; + + hio_read32b(f); + + if (hio_read32b(f) != MAGIC4('A', 'M', 'F', 'F')) + return -1; + + if (hio_read32b(f) != MAGIC4('M', 'A', 'I', 'N')) + return -1; + + hio_read32b(f); /* skip size */ + libxmp_read_title(f, t, 64); + + return 0; +} + +struct local_data { + int snum; +}; + +static int get_main(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + char buf[64]; + int flags; + + if (hio_read(buf, 1, 64, f) < 64) + return -1; + strncpy(mod->name, buf, 63); /* ensure string terminator */ + mod->name[63] = '\0'; + libxmp_set_type(m, "Galaxy Music System 4.0"); + + flags = hio_read8(f); + if (~flags & 0x01) + m->period_type = PERIOD_LINEAR; + mod->chn = hio_read8(f); + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + hio_read16l(f); /* unknown - 0x01c5 */ + hio_read16l(f); /* unknown - 0xff00 */ + hio_read8(f); /* unknown - 0x80 */ + + /* Sanity check */ + if (mod->chn > 32) { + return -1; + } + + return 0; +} + +static int get_ordr(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + mod->len = hio_read8(f) + 1; + if (hio_error(f)) { + return -1; + } + + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = hio_read8(f); + } + + return 0; +} + +static int get_patt_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + i = hio_read8(f) + 1; /* pattern number */ + + if (i > mod->pat) + mod->pat = i; + + return 0; +} + +static int get_inst_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + hio_read8(f); /* 00 */ + i = hio_read8(f) + 1; /* instrument number */ + + /* Sanity check */ + if (i > MAX_INSTRUMENTS) + return -1; + + if (i > mod->ins) + mod->ins = i; + + hio_seek(f, 28, SEEK_CUR); /* skip name */ + + mod->smp += hio_read8(f); + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy; + int i, len, chan; + int rows, r; + uint8 flag; + + i = hio_read8(f); /* pattern number */ + len = hio_read32l(f); + + /* Sanity check */ + if (i >= mod->pat || len <= 0 || mod->xxp[i]) { + return -1; + } + + rows = hio_read8(f) + 1; + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + for (r = 0; r < rows; ) { + if ((flag = hio_read8(f)) == 0) { + r++; + continue; + } + if (hio_error(f)) { + return -1; + } + + chan = flag & 0x1f; + + event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy; + + if (flag & 0x80) { + uint8 fxp = hio_read8(f); + uint8 fxt = hio_read8(f); + + switch (fxt) { + case 0x14: /* speed */ + fxt = FX_S3M_SPEED; + break; + default: + if (fxt > 0x0f) { + D_(D_CRIT "p%d r%d c%d unknown effect %02x %02x", i, r, chan, fxt, fxp); + fxt = fxp = 0; + } + } + + event->fxt = fxt; + event->fxp = fxp; + } + + if (flag & 0x40) { + event->ins = hio_read8(f); + event->note = hio_read8(f); + + if (event->note == 128) { + event->note = XMP_KEY_OFF; + } + } + + if (flag & 0x20) { + event->vol = 1 + hio_read8(f) / 2; + } + } + + return 9; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j; + int srate, finetune, flags; + int val, vwf, vra, vde, vsw /*, fade*/; + uint8 buf[30]; + + hio_read8(f); /* 00 */ + i = hio_read8(f); /* instrument number */ + + /* Sanity check */ + if (i >= mod->ins || mod->xxi[i].nsm) { + return -1; + } + + hio_read(mod->xxi[i].name, 1, 28, f); + mod->xxi[i].nsm = hio_read8(f); + + for (j = 0; j < 108; j++) { + mod->xxi[i].map[j].ins = hio_read8(f); + } + + hio_seek(f, 11, SEEK_CUR); /* unknown */ + vwf = hio_read8(f); /* vibrato waveform */ + vsw = hio_read8(f); /* vibrato sweep */ + hio_read8(f); /* unknown */ + hio_read8(f); /* unknown */ + vde = hio_read8(f); /* vibrato depth */ + vra = hio_read16l(f) / 16; /* vibrato speed */ + hio_read8(f); /* unknown */ + + val = hio_read8(f); /* PV envelopes flags */ + if (LSN(val) & 0x01) + mod->xxi[i].aei.flg |= XMP_ENVELOPE_ON; + if (LSN(val) & 0x02) + mod->xxi[i].aei.flg |= XMP_ENVELOPE_SUS; + if (LSN(val) & 0x04) + mod->xxi[i].aei.flg |= XMP_ENVELOPE_LOOP; + if (MSN(val) & 0x01) + mod->xxi[i].pei.flg |= XMP_ENVELOPE_ON; + if (MSN(val) & 0x02) + mod->xxi[i].pei.flg |= XMP_ENVELOPE_SUS; + if (MSN(val) & 0x04) + mod->xxi[i].pei.flg |= XMP_ENVELOPE_LOOP; + + val = hio_read8(f); /* PV envelopes points */ + mod->xxi[i].aei.npt = LSN(val) + 1; + mod->xxi[i].pei.npt = MSN(val) + 1; + + val = hio_read8(f); /* PV envelopes sustain point */ + mod->xxi[i].aei.sus = LSN(val); + mod->xxi[i].pei.sus = MSN(val); + + val = hio_read8(f); /* PV envelopes loop start */ + mod->xxi[i].aei.lps = LSN(val); + mod->xxi[i].pei.lps = MSN(val); + + hio_read8(f); /* PV envelopes loop end */ + mod->xxi[i].aei.lpe = LSN(val); + mod->xxi[i].pei.lpe = MSN(val); + + if (mod->xxi[i].aei.npt <= 0 || mod->xxi[i].aei.npt > MIN(10, XMP_MAX_ENV_POINTS)) + mod->xxi[i].aei.flg &= ~XMP_ENVELOPE_ON; + + if (mod->xxi[i].pei.npt <= 0 || mod->xxi[i].pei.npt > MIN(10, XMP_MAX_ENV_POINTS)) + mod->xxi[i].pei.flg &= ~XMP_ENVELOPE_ON; + + if (hio_read(buf, 1, 30, f) < 30) { /* volume envelope points */ + D_(D_CRIT "read error at vol env %d", i); + return -1; + } + for (j = 0; j < mod->xxi[i].aei.npt; j++) { + if (j >= 10) { + break; + } + mod->xxi[i].aei.data[j * 2] = readmem16l(buf + j * 3) / 16; + mod->xxi[i].aei.data[j * 2 + 1] = buf[j * 3 + 2]; + } + + if (hio_read(buf, 1, 30, f) < 30) { /* pan envelope points */ + D_(D_CRIT "read error at pan env %d", i); + return -1; + } + for (j = 0; j < mod->xxi[i].pei.npt; j++) { + if (j >= 10) { + break; + } + mod->xxi[i].pei.data[j * 2] = readmem16l(buf + j * 3) / 16; + mod->xxi[i].pei.data[j * 2 + 1] = buf[j * 3 + 2]; + } + + /*fade =*/ hio_read8(f); /* fadeout - 0x80->0x02 0x310->0x0c */ + hio_read8(f); /* unknown */ + + D_(D_INFO "[%2X] %-28.28s %2d ", i, mod->xxi[i].name, mod->xxi[i].nsm); + + if (mod->xxi[i].nsm == 0) + return 0; + + if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0) + return -1; + + for (j = 0; j < mod->xxi[i].nsm; j++, data->snum++) { + hio_read32b(f); /* SAMP */ + hio_read32b(f); /* size */ + + hio_read(mod->xxs[data->snum].name, 1, 28, f); + + mod->xxi[i].sub[j].pan = hio_read8(f) * 4; + if (mod->xxi[i].sub[j].pan == 0) /* not sure about this */ + mod->xxi[i].sub[j].pan = 0x80; + + mod->xxi[i].sub[j].vol = hio_read8(f); + flags = hio_read8(f); + hio_read8(f); /* unknown - 0x80 */ + + mod->xxi[i].sub[j].vwf = vwf; + mod->xxi[i].sub[j].vde = vde; + mod->xxi[i].sub[j].vra = vra; + mod->xxi[i].sub[j].vsw = vsw; + mod->xxi[i].sub[j].sid = data->snum; + + mod->xxs[data->snum].len = hio_read32l(f); + mod->xxs[data->snum].lps = hio_read32l(f); + mod->xxs[data->snum].lpe = hio_read32l(f); + + mod->xxs[data->snum].flg = 0; + if (flags & 0x04) + mod->xxs[data->snum].flg |= XMP_SAMPLE_16BIT; + if (flags & 0x08) + mod->xxs[data->snum].flg |= XMP_SAMPLE_LOOP; + if (flags & 0x10) + mod->xxs[data->snum].flg |= XMP_SAMPLE_LOOP_BIDIR; + /* if (flags & 0x80) + mod->xxs[data->snum].flg |= ? */ + + srate = hio_read32l(f); + finetune = 0; + libxmp_c2spd_to_note(srate, &mod->xxi[i].sub[j].xpo, &mod->xxi[i].sub[j].fin); + mod->xxi[i].sub[j].fin += finetune; + + hio_read32l(f); /* 0x00000000 */ + hio_read32l(f); /* unknown */ + + D_(D_INFO " %X: %05x%c%05x %05x %c V%02x P%02x %5d", + j, mod->xxs[data->snum].len, + mod->xxs[data->snum].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[data->snum].lps, + mod->xxs[data->snum].lpe, + mod->xxs[data->snum].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[data->snum].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[j].vol, + mod->xxi[i].sub[j].pan, + srate); + + if (mod->xxs[data->snum].len > 1) { + int snum = data->snum; + if (libxmp_load_sample(m, f, 0, &mod->xxs[snum], NULL) < 0) + return -1; + } + } + + return 0; +} + +static int gal4_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i, ret, offset; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* Skip RIFF */ + hio_read32b(f); /* Skip size */ + hio_read32b(f); /* Skip AM */ + + offset = hio_tell(f); + + mod->smp = mod->ins = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "MAIN", get_main); + ret |= libxmp_iff_register(handle, "ORDR", get_ordr); + ret |= libxmp_iff_register(handle, "PATT", get_patt_cnt); + ret |= libxmp_iff_register(handle, "INST", get_inst_cnt); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_CHUNK_TRUNC4); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + mod->trk = mod->pat * mod->chn; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d\n", mod->pat); + D_(D_INFO "Stored samples : %d ", mod->smp); + + hio_seek(f, start + offset, SEEK_SET); + data.snum = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "INST", get_inst); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_CHUNK_TRUNC4); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + /* Alloc missing patterns */ + for (i = 0; i < mod->pat; i++) { + if (mod->xxp[i] == NULL) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + return -1; + } + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = 0x80; + } + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/gal5_load.c b/thirdparty/libxmp/src/loaders/gal5_load.c new file mode 100644 index 0000000..3b1b6df --- /dev/null +++ b/thirdparty/libxmp/src/loaders/gal5_load.c @@ -0,0 +1,404 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +/* Galaxy Music System 5.0 module file loader + * + * Based on the format description by Dr.Eggman + * (http://www.jazz2online.com/J2Ov2/articles/view.php?articleID=288) + * and Jazz Jackrabbit modules by Alexander Brandon from Lori Central + * (http://www.loricentral.com/jj2music.html) + */ + +static int gal5_test(HIO_HANDLE *, char *, const int); +static int gal5_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_gal5 = { + "Galaxy Music System 5.0 (J2B)", + gal5_test, + gal5_load +}; + + +struct local_data { + uint8 chn_pan[64]; +}; + +static int gal5_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC4('R', 'I', 'F', 'F')) + return -1; + + hio_read32b(f); + + if (hio_read32b(f) != MAGIC4('A', 'M', ' ', ' ')) + return -1; + + if (hio_read32b(f) != MAGIC4('I', 'N', 'I', 'T')) + return -1; + + hio_read32b(f); /* skip size */ + libxmp_read_title(f, t, 64); + + return 0; +} + +static int get_init(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + char buf[64]; + int flags; + + if (hio_read(buf, 1, 64, f) < 64) + return -1; + strncpy(mod->name, buf, 63); /* ensure string terminator */ + mod->name[63] = '\0'; + libxmp_set_type(m, "Galaxy Music System 5.0"); + flags = hio_read8(f); /* bit 0: Amiga period */ + if (~flags & 0x01) + m->period_type = PERIOD_LINEAR; + mod->chn = hio_read8(f); + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + hio_read16l(f); /* unknown - 0x01c5 */ + hio_read16l(f); /* unknown - 0xff00 */ + hio_read8(f); /* unknown - 0x80 */ + + if (hio_read(data->chn_pan, 1, 64, f) != 64) { + D_(D_CRIT "error reading INIT"); + return -1; + } + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS) + return -1; + + return 0; +} + +static int get_ordr(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + mod->len = hio_read8(f) + 1; + /* Don't follow Dr.Eggman's specs here */ + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = hio_read8(f); + + return 0; +} + +static int get_patt_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + i = hio_read8(f) + 1; /* pattern number */ + + if (i > mod->pat) + mod->pat = i; + + return 0; +} + +static int get_inst_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + hio_read32b(f); /* 42 01 00 00 */ + hio_read8(f); /* 00 */ + i = hio_read8(f) + 1; /* instrument number */ + + /* Sanity check */ + if (i > MAX_INSTRUMENTS) + return -1; + + if (i > mod->ins) + mod->ins = i; + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy; + int i, len, chan; + int rows, r; + uint8 flag; + + i = hio_read8(f); /* pattern number */ + len = hio_read32l(f); + + rows = hio_read8(f) + 1; + + /* Sanity check - don't allow duplicate patterns. */ + if (len < 0 || mod->xxp[i] != NULL) + return -1; + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + for (r = 0; r < rows; ) { + if ((flag = hio_read8(f)) == 0) { + r++; + continue; + } + if (hio_error(f)) { + return -1; + } + + chan = flag & 0x1f; + + event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy; + + if (flag & 0x80) { + uint8 fxp = hio_read8(f); + uint8 fxt = hio_read8(f); + + switch (fxt) { + case 0x14: /* speed */ + fxt = FX_S3M_SPEED; + break; + default: + if (fxt > 0x0f) { + D_(D_CRIT "p%d r%d c%d unknown effect %02x %02x", i, r, chan, fxt, fxp); + fxt = fxp = 0; + } + } + + event->fxt = fxt; + event->fxp = fxp; + } + + if (flag & 0x40) { + event->ins = hio_read8(f); + event->note = hio_read8(f); + + if (event->note == 128) { + event->note = XMP_KEY_OFF; + } + } + + if (flag & 0x20) { + event->vol = 1 + hio_read8(f) / 2; + } + } + + return 0; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i, srate, finetune, flags; + int has_unsigned_sample; + + hio_read32b(f); /* 42 01 00 00 */ + hio_read8(f); /* 00 */ + i = hio_read8(f); /* instrument number */ + + /* Sanity check - don't allow duplicate instruments. */ + if (mod->xxi[i].nsm != 0) + return -1; + + hio_read(mod->xxi[i].name, 1, 28, f); + hio_seek(f, 290, SEEK_CUR); /* Sample/note map, envelopes */ + mod->xxi[i].nsm = hio_read16l(f); + + D_(D_INFO "[%2X] %-28.28s %2d ", i, mod->xxi[i].name, mod->xxi[i].nsm); + + if (mod->xxi[i].nsm == 0) + return 0; + + if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0) + return -1; + + /* FIXME: Currently reading only the first sample */ + + hio_read32b(f); /* RIFF */ + hio_read32b(f); /* size */ + hio_read32b(f); /* AS */ + hio_read32b(f); /* SAMP */ + hio_read32b(f); /* size */ + hio_read32b(f); /* unknown - usually 0x40000000 */ + + hio_read(mod->xxs[i].name, 1, 28, f); + + hio_read32b(f); /* unknown - 0x0000 */ + hio_read8(f); /* unknown - 0x00 */ + + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].vol = hio_read8(f); + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].vol = (hio_read16l(f) + 1) / 512; + flags = hio_read16l(f); + hio_read16l(f); /* unknown - 0x0080 */ + mod->xxs[i].len = hio_read32l(f); + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + + mod->xxs[i].flg = 0; + has_unsigned_sample = 0; + if (flags & 0x04) + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + if (flags & 0x08) + mod->xxs[i].flg |= XMP_SAMPLE_LOOP; + if (flags & 0x10) + mod->xxs[i].flg |= XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR; + if (~flags & 0x80) + has_unsigned_sample = 1; + + srate = hio_read32l(f); + finetune = 0; + libxmp_c2spd_to_note(srate, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + mod->xxi[i].sub[0].fin += finetune; + + hio_read32l(f); /* 0x00000000 */ + hio_read32l(f); /* unknown */ + + D_(D_INFO " %x: %05x%c%05x %05x %c V%02x %04x %5d", + 0, mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, flags, srate); + + if (mod->xxs[i].len > 1) { + if (libxmp_load_sample(m, f, has_unsigned_sample ? + SAMPLE_FLAG_UNS : 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} + +static int gal5_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i, ret, offset; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* Skip RIFF */ + hio_read32b(f); /* Skip size */ + hio_read32b(f); /* Skip AM */ + + offset = hio_tell(f); + + mod->smp = mod->ins = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "INIT", get_init); /* Galaxy 5.0 */ + ret |= libxmp_iff_register(handle, "ORDR", get_ordr); + ret |= libxmp_iff_register(handle, "PATT", get_patt_cnt); + ret |= libxmp_iff_register(handle, "INST", get_inst_cnt); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_SKIP_EMBEDDED); + libxmp_iff_set_quirk(handle, IFF_CHUNK_ALIGN2); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + mod->trk = mod->pat * mod->chn; + mod->smp = mod->ins; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + D_(D_INFO "Stored samples: %d ", mod->smp); + + hio_seek(f, start + offset, SEEK_SET); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "INST", get_inst); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_SKIP_EMBEDDED); + libxmp_iff_set_quirk(handle, IFF_CHUNK_ALIGN2); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + /* Alloc missing patterns */ + for (i = 0; i < mod->pat; i++) { + if (mod->xxp[i] == NULL) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + return -1; + } + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = data.chn_pan[i] * 2; + } + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/gdm_load.c b/thirdparty/libxmp/src/loaders/gdm_load.c new file mode 100644 index 0000000..5139ef8 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/gdm_load.c @@ -0,0 +1,449 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Based on the GDM (General Digital Music) version 1.0 File Format + * Specification - Revision 2 by MenTaLguY + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_GDM MAGIC4('G','D','M',0xfe) +#define MAGIC_GMFS MAGIC4('G','M','F','S') + + +static int gdm_test(HIO_HANDLE *, char *, const int); +static int gdm_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_gdm = { + "General Digital Music", + gdm_test, + gdm_load +}; + +static int gdm_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_GDM) + return -1; + + hio_seek(f, start + 0x47, SEEK_SET); + if (hio_read32b(f) != MAGIC_GMFS) + return -1; + + hio_seek(f, start + 4, SEEK_SET); + libxmp_read_title(f, t, 32); + + return 0; +} + + + +void fix_effect(uint8 *fxt, uint8 *fxp) +{ + int h, l; + switch (*fxt) { + case 0x00: /* no effect */ + *fxp = 0; + break; + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: /* same as protracker */ + break; + case 0x08: + *fxt = FX_TREMOR; + break; + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: /* same as protracker */ + break; + case 0x0e: + /* Convert some extended effects to their S3M equivalents. This is + * necessary because the continue effects were left as the original + * effect (e.g. FX_VOLSLIDE for the fine volume slides) by 2GDM! + * Otherwise, these should be the same as protracker. + */ + h = MSN(*fxp); + l = LSN(*fxp); + switch(h) { + case EX_F_PORTA_UP: + *fxt = FX_PORTA_UP; + *fxp = l | 0xF0; + break; + case EX_F_PORTA_DN: + *fxt = FX_PORTA_DN; + *fxp = l | 0xF0; + break; + case 0x8: /* extra fine portamento up */ + *fxt = FX_PORTA_UP; + *fxp = l | 0xE0; + break; + case 0x9: /* extra fine portamento down */ + *fxt = FX_PORTA_DN; + *fxp = l | 0xE0; + break; + case EX_F_VSLIDE_UP: + /* Don't convert 0 as it would turn into volume slide down... */ + if (l) { + *fxt = FX_VOLSLIDE; + *fxp = (l << 4) | 0xF; + } + break; + case EX_F_VSLIDE_DN: + /* Don't convert 0 as it would turn into volume slide up... */ + if (l) { + *fxt = FX_VOLSLIDE; + *fxp = l | 0xF0; + } + break; + } + break; + case 0x0f: /* set speed */ + *fxt = FX_S3M_SPEED; + break; + case 0x10: /* arpeggio */ + *fxt = FX_S3M_ARPEGGIO; + break; + case 0x11: /* set internal flag */ + *fxt = *fxp = 0; + break; + case 0x12: + *fxt = FX_MULTI_RETRIG; + break; + case 0x13: + *fxt = FX_GLOBALVOL; + break; + case 0x14: + *fxt = FX_FINE_VIBRATO; + break; + case 0x1e: /* special misc */ + switch (MSN(*fxp)) { + case 0x0: /* sample control */ + if (LSN(*fxp) == 1) { /* enable surround */ + /* This is the only sample control effect + * that 2GDM emits. BWSB ignores it, + * but supporting it is harmless. */ + *fxt = FX_SURROUND; + *fxp = 1; + } else { + *fxt = *fxp = 0; + } + break; + case 0x8: /* set pan position */ + *fxt = FX_EXTENDED; + break; + default: + *fxt = *fxp = 0; + break; + } + break; + case 0x1f: + *fxt = FX_S3M_BPM; + break; + default: + *fxt = *fxp = 0; + } +} + + +static int gdm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int vermaj, vermin, tvmaj, tvmin, tracker; + int /*origfmt,*/ ord_ofs, pat_ofs, ins_ofs, smp_ofs; + uint8 buffer[32], panmap[32]; + int i; + + LOAD_INIT(); + + hio_read32b(f); /* skip magic */ + hio_read(mod->name, 1, 32, f); + hio_seek(f, 32, SEEK_CUR); /* skip author */ + + hio_seek(f, 7, SEEK_CUR); + + vermaj = hio_read8(f); + vermin = hio_read8(f); + tracker = hio_read16l(f); + tvmaj = hio_read8(f); + tvmin = hio_read8(f); + + if (tracker == 0) { + libxmp_set_type(m, "GDM %d.%02d (2GDM %d.%02d)", + vermaj, vermin, tvmaj, tvmin); + } else { + libxmp_set_type(m, "GDM %d.%02d (unknown tracker %d.%02d)", + vermaj, vermin, tvmaj, tvmin); + } + + if (hio_read(panmap, 32, 1, f) == 0) { + D_(D_CRIT "error reading header"); + return -1; + } + for (i = 0; i < 32; i++) { + if (panmap[i] == 255) { + panmap[i] = 8; + mod->xxc[i].vol = 0; + mod->xxc[i].flg |= XMP_CHANNEL_MUTE; + } else if (panmap[i] == 16) { + panmap[i] = 8; + } + mod->xxc[i].pan = 0x80 + (panmap[i] - 8) * 16; + } + + mod->gvl = hio_read8(f); + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + /*origfmt =*/ hio_read16l(f); + ord_ofs = hio_read32l(f); + mod->len = hio_read8(f) + 1; + pat_ofs = hio_read32l(f); + mod->pat = hio_read8(f) + 1; + ins_ofs = hio_read32l(f); + smp_ofs = hio_read32l(f); + mod->ins = mod->smp = hio_read8(f) + 1; + + /* Sanity check */ + if (mod->ins > MAX_INSTRUMENTS) + return -1; + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + hio_seek(f, start + ord_ofs, SEEK_SET); + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = hio_read8(f); + + /* Read instrument data */ + + hio_seek(f, start + ins_ofs, SEEK_SET); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + int flg, c4spd, vol, pan; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + if (hio_read(buffer, 1, 32, f) != 32) + return -1; + + libxmp_instrument_name(mod, i, buffer, 32); + hio_seek(f, 12, SEEK_CUR); /* skip filename */ + hio_read8(f); /* skip EMS handle */ + mod->xxs[i].len = hio_read32l(f); + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + flg = hio_read8(f); + c4spd = hio_read16l(f); + vol = hio_read8(f); + pan = hio_read8(f); + + mod->xxi[i].sub[0].vol = vol > 0x40 ? 0x40 : vol; + mod->xxi[i].sub[0].pan = pan > 15 ? 0x80 : 0x80 + (pan - 8) * 16; + libxmp_c2spd_to_note(c4spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + + mod->xxi[i].sub[0].sid = i; + mod->xxs[i].flg = 0; + + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + if (flg & 0x01) { + mod->xxs[i].flg |= XMP_SAMPLE_LOOP; + } + if (flg & 0x02) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + mod->xxs[i].len >>= 1; + mod->xxs[i].lps >>= 1; + mod->xxs[i].lpe >>= 1; + } + + D_(D_INFO "[%2X] %-32.32s %05x%c%05x %05x %c V%02x P%02x %5d", + i, mod->xxi[i].name, + mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].pan, + c4spd); + } + + /* Read and convert patterns */ + + hio_seek(f, start + pat_ofs, SEEK_SET); + + /* Effects in muted channels are processed, so scan patterns first to + * see the real number of channels + */ + mod->chn = 0; + for (i = 0; i < mod->pat; i++) { + int len, c, r, k; + + len = hio_read16l(f); + len -= 2; + + for (r = 0; len > 0; ) { + c = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + + if (c == 0) { + r++; + + /* Sanity check */ + if (len == 0) { + if (r > 64) + return -1; + } else { + if (r >= 64) + return -1; + } + + continue; + } + + if (mod->chn <= (c & 0x1f)) + mod->chn = (c & 0x1f) + 1; + + if (c & 0x20) { /* note and sample follows */ + hio_read8(f); + hio_read8(f); + len -= 2; + } + + if (c & 0x40) { /* effect(s) follow */ + do { + k = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + if ((k & 0xc0) != 0xc0) { + hio_read8(f); + len--; + } + } while (k & 0x20); + } + } + } + + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + hio_seek(f, start + pat_ofs, SEEK_SET); + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + int len, c, r, k; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + len = hio_read16l(f); + len -= 2; + + for (r = 0; len > 0; ) { + c = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + + if (c == 0) { + r++; + continue; + } + + /* Sanity check */ + if ((c & 0x1f) >= mod->chn || r >= 64) { + return -1; + } + + event = &EVENT(i, c & 0x1f, r); + + if (c & 0x20) { /* note and sample follows */ + k = hio_read8(f); + /* 0 is empty note */ + event->note = k ? 12 + 12 * MSN(k & 0x7f) + LSN(k) : 0; + event->ins = hio_read8(f); + len -= 2; + } + + if (c & 0x40) { /* effect(s) follow */ + do { + k = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + switch ((k & 0xc0) >> 6) { + case 0: + event->fxt = k & 0x1f; + event->fxp = hio_read8(f); + len--; + fix_effect(&event->fxt, &event->fxp); + break; + case 1: + event->f2t = k & 0x1f; + event->f2p = hio_read8(f); + len--; + fix_effect(&event->f2t, &event->f2p); + break; + case 2: + hio_read8(f); + len--; + } + } while (k & 0x20); + } + } + } + + /* Read samples */ + + hio_seek(f, start + smp_ofs, SEEK_SET); + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->quirk |= QUIRK_ARPMEM | QUIRK_FINEFX; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/hmn_load.c b/thirdparty/libxmp/src/loaders/hmn_load.c new file mode 100644 index 0000000..281d3ff --- /dev/null +++ b/thirdparty/libxmp/src/loaders/hmn_load.c @@ -0,0 +1,324 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" +#include "../hmn_extras.h" + +/* + * From http://www.livet.se/mahoney/: + * + * Most modules from His Master's Noise uses special chip-sounds or + * fine-tuning of samples that never was a part of the standard NoiseTracker + * v2.0 command set. So if you want to listen to them correctly use an Amiga + * emulator and run the demo! DeliPlayer does a good job of playing them + * (there are some occasional error mostly concerning vibrato and portamento + * effects, but I can live with that!), and it can be downloaded from + * http://www.deliplayer.com + */ + +/* + * From http://www.cactus.jawnet.pl/attitude/index.php?action=readtext&issue=12&which=12 + * + * [Bepp] For your final Amiga release, the music disk His Master's Noise, + * you developed a special version of NoiseTracker. Could you tell us a + * little about this project? + * + * [Mahoney] I wanted to make a music disk with loads of songs, without being + * too repetitive or boring. So all of my "experimental features" that did not + * belong to NoiseTracker v2.0 were put into a separate version that would + * feature wavetable sounds, chord calculations, off-line filter calculations, + * mixing, reversing, sample accurate delays, resampling, fades - calculations + * that would be done on a standard setup of sounds instead of on individual + * modules. This "compression technique" lead to some 100 songs fitting on two + * standard 3.5" disks, written by 22 different composers. I'd say that writing + * a music program does give you loads of talented friends - you should try + * that yourself someday! + */ + +/* + * From: Pex Tufvesson + * To: Claudio Matsuoka + * Date: Sat, Jun 1, 2013 at 4:16 AM + * Subject: Re: A question about (very) old stuff + * + * (...) + * If I remember correctly, these chip sounds were done with several short + * waveforms, and an index table that was loopable that would choose which + * waveform to play each frame. And, you didn't have to "draw" every + * waveform in the instrument - you would choose which waveforms to draw + * and the replayer would (at startup) interpolate the waveforms that you + * didn't draw. + * + * In the special noisetracker, you could draw all of these waveforms, draw + * the index table, and the instrument would be stored in one of the + * "patterns" of the song. + */ + +static int hmn_test(HIO_HANDLE *, char *, const int); +static int hmn_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_hmn = { + "His Master's Noise", + hmn_test, + hmn_load +}; + +/* His Master's Noise M&K! will fail in regular Noisetracker loading + * due to invalid finetune values. + */ +#define MAGIC_FEST MAGIC4('F', 'E', 'S', 'T') +#define MAGIC_MK MAGIC4('M', '&', 'K', '!') + +static int hmn_test(HIO_HANDLE * f, char *t, const int start) +{ + int magic; + + hio_seek(f, start + 1080, SEEK_SET); + magic = hio_read32b(f); + + if (magic != MAGIC_FEST && magic != MAGIC_MK) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +struct mupp { + uint8 prgon; + uint8 pattno; + uint8 dataloopstart; + uint8 dataloopend; +}; + +static int hmn_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct mod_header mh; + struct mupp mupp[31]; + uint8 mod_event[4]; + int mupp_index, num_mupp; + + LOAD_INIT(); + + /* + * clr.b $1c(a6) ;prog on/off + * CMP.L #'Mupp',-$16(a3,d4.l) + * bne.s noprgo + * move.l a0,-(a7) + * move.b #1,$1c(a6) ;prog on + * move.l l697,a0 + * lea $43c(a0),a0 + * moveq #0,d2 + * move.b -$16+$4(a3,d4.l),d2 ;pattno + * mulu #$400,d2 + * lea (a0,d2.l),a0 + * move.l a0,4(a6) ;proginstr data-start + * moveq #0,d2 + * MOVE.B $3C0(A0),$12(A6) + * AND.B #$7F,$12(A6) + * move.b $380(a0),d2 + * mulu #$20,d2 + * lea (a0,d2.w),a0 + * move.l a0,$a(a6) ;loopstartmempoi = startmempoi + * move.B $3(a3,d4.l),$13(a6) ;volume + * move.b -$16+$5(a3,d4.l),8(a6) ;dataloopstart + * move.b -$16+$6(a3,d4.l),9(a6) ;dataloopend + * move.w #$10,$e(a6) ;looplen + * move.l (a7)+,a0 + * MOVE.W $12(A6),(A2) + * AND.W #$FF,(A2) + * BRA.S L505_LQ + */ + + /* + * Wavetable structure is 22 * 32 byte waveforms and 32 byte + * wave control data with looping. + */ + memset(mupp, 0, 31 * sizeof (struct mupp)); + + hio_read(mh.name, 20, 1, f); + num_mupp = 0; + + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */ + if (memcmp(mh.ins[i].name, "Mupp", 4) == 0) { + mupp[i].prgon = 1; + mupp[i].pattno = mh.ins[i].name[4]; + mupp[i].dataloopstart = mh.ins[i].name[5]; + mupp[i].dataloopend = mh.ins[i].name[6]; + num_mupp++; + } + + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + hio_read(mh.magic, 4, 1, f); + + mod->chn = 4; + mod->ins = 31; + mod->smp = mod->ins + 28 * num_mupp; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + mod->trk = mod->chn * mod->pat; + + if (libxmp_hmn_new_module_extras(m) != 0) + return -1; + + strncpy(mod->name, (char *)mh.name, 20); + libxmp_set_type(m, "%s (%4.4s)", "His Master's Noise", mh.magic); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + if (mupp[i].prgon) { + mod->xxi[i].nsm = 28; + snprintf(mod->xxi[i].name, 32, + "Mupp %02x %02x %02x", mupp[i].pattno, + mupp[i].dataloopstart, mupp[i].dataloopend); + if (libxmp_hmn_new_instrument_extras(&mod->xxi[i]) != 0) + return -1; + } else { + mod->xxi[i].nsm = 1; + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + mod->xxs[i].len = 2 * mh.ins[i].size; + mod->xxs[i].lps = 2 * mh.ins[i].loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + + 2 * mh.ins[i].loop_size; + mod->xxs[i].flg = mh.ins[i].loop_size > 1 ? + XMP_SAMPLE_LOOP : 0; + } + + if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0) + return -1; + + for (j = 0; j < mod->xxi[i].nsm; j++) { + mod->xxi[i].sub[j].fin = + -(int8)(mh.ins[i].finetune << 3); + mod->xxi[i].sub[j].vol = mh.ins[i].volume; + mod->xxi[i].sub[j].pan = 0x80; + mod->xxi[i].sub[j].sid = i; + } + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + libxmp_decode_protracker_event(event, mod_event); + + switch (event->fxt) { + case 0x07: + event->fxt = FX_MEGAARP; + break; + case 0x08: + case 0x09: + case 0x0e: + event->fxt = event->fxp = 0; + break; + } + } + } + + /* Noisetracker does not support CIA timing (Glue Master/muppenkorva.mod) */ + m->quirk |= QUIRK_NOBPM; + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < 31; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, + &mod->xxs[i], NULL) < 0) { + return -1; + } + } + + + /* Load Mupp samples */ + + mupp_index = 0; + for (i = 0; i < 31; i ++) { + struct hmn_instrument_extras *extra = + (struct hmn_instrument_extras *)mod->xxi[i].extra; + + if (!mupp[i].prgon) + continue; + + hio_seek(f, start + 1084 + 1024 * mupp[i].pattno, SEEK_SET); + for (j = 0; j < 28; j++) { + int k = 31 + 28 * mupp_index + j; + mod->xxi[i].sub[j].sid = k; + mod->xxs[k].len = 32; + mod->xxs[k].lps = 0; + mod->xxs[k].lpe = 32; + mod->xxs[k].flg = XMP_SAMPLE_LOOP; + if (libxmp_load_sample(m, f, 0, &mod->xxs[k], NULL) < 0) + return -1; + } + + extra->dataloopstart = mupp[i].dataloopstart; + extra->dataloopend = mupp[i].dataloopend; + + hio_read(extra->data, 1, 64, f); + hio_read(extra->progvolume, 1, 64, f); + + mupp_index++; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/ice_load.c b/thirdparty/libxmp/src/loaders/ice_load.c new file mode 100644 index 0000000..55e1d76 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ice_load.c @@ -0,0 +1,206 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Soundtracker 2.6/Ice Tracker modules */ + +#include "loader.h" + +#define MAGIC_MTN_ MAGIC4('M','T','N',0) +#define MAGIC_IT10 MAGIC4('I','T','1','0') + +static int ice_test(HIO_HANDLE *, char *, const int); +static int ice_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ice = { + "Soundtracker 2.6/Ice Tracker", + ice_test, + ice_load +}; + +static int ice_test(HIO_HANDLE * f, char *t, const int start) +{ + uint32 magic; + + hio_seek(f, start + 1464, SEEK_SET); + magic = hio_read32b(f); + if (magic != MAGIC_MTN_ && magic != MAGIC_IT10) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 28); + + return 0; +} + +struct ice_ins { + char name[22]; /* Instrument name */ + uint16 len; /* Sample length / 2 */ + uint8 finetune; /* Finetune */ + uint8 volume; /* Volume (0-63) */ + uint16 loop_start; /* Sample loop start in file */ + uint16 loop_size; /* Loop size / 2 */ +}; + +struct ice_header { + char title[20]; + struct ice_ins ins[31]; /* Instruments */ + uint8 len; /* Size of the pattern list */ + uint8 trk; /* Number of tracks */ + uint8 ord[128][4]; + uint32 magic; /* 'MTN\0', 'IT10' */ +}; + +static int ice_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct ice_header ih; + uint8 ev[4]; + + LOAD_INIT(); + + hio_read(ih.title, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(ih.ins[i].name, 22, 1, f); + ih.ins[i].len = hio_read16b(f); + ih.ins[i].finetune = hio_read8(f); + ih.ins[i].volume = hio_read8(f); + ih.ins[i].loop_start = hio_read16b(f); + ih.ins[i].loop_size = hio_read16b(f); + } + ih.len = hio_read8(f); + ih.trk = hio_read8(f); + hio_read(ih.ord, 128 * 4, 1, f); + ih.magic = hio_read32b(f); + + /* Sanity check */ + if (ih.len > 128) { + return -1; + } + for (i = 0; i < ih.len; i++) { + for (j = 0; j < 4; j++) { + if (ih.ord[i][j] >= ih.trk) + return -1; + } + } + + if (ih.magic == MAGIC_IT10) + libxmp_set_type(m, "Ice Tracker"); + else if (ih.magic == MAGIC_MTN_) + libxmp_set_type(m, "Soundtracker 2.6"); + else + return -1; + + mod->ins = 31; + mod->smp = mod->ins; + mod->pat = ih.len; + mod->len = ih.len; + mod->trk = ih.trk; + + strncpy(mod->name, (char *)ih.title, 20); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * ih.ins[i].len; + xxs->lps = 2 * ih.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * ih.ins[i].loop_size; + xxs->flg = ih.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + xxi->sub[0].vol = ih.ins[i].volume; + /* xxi->sub[0].fin = (int8)(ih.ins[i].finetune << 4); */ + xxi->sub[0].pan = 0x80; + xxi->sub[0].sid = i; + + if (xxs->len > 0) + xxi->nsm = 1; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c %02x %01x", + i, ih.ins[i].name, xxs->len, xxs->lps, + xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + xxi->sub[0].vol, xxi->sub[0].fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + mod->xxp[i]->rows = 64; + + for (j = 0; j < mod->chn; j++) { + mod->xxp[i]->index[j] = ih.ord[i][j]; + } + mod->xxo[i] = i; + } + + D_(D_INFO "Stored tracks: %d", mod->trk); + + for (i = 0; i < mod->trk; i++) { + if (libxmp_alloc_track(mod, i, 64) < 0) + return -1; + + for (j = 0; j < mod->xxt[i]->rows; j++) { + event = &mod->xxt[i]->event[j]; + if (hio_read(ev, 1, 4, f) < 4) { + D_(D_CRIT "read error at track %d", i); + return -1; + } + libxmp_decode_protracker_event(event, ev); + + if (event->fxt == FX_SPEED) { + if (MSN(event->fxp) && LSN(event->fxp)) { + event->fxt = FX_ICE_SPEED; + } + } + } + } + + m->period_type = PERIOD_MODRNG; + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 4) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/iff.c b/thirdparty/libxmp/src/loaders/iff.c new file mode 100644 index 0000000..0214b87 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/iff.c @@ -0,0 +1,215 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../common.h" +#include "../list.h" +#include "iff.h" + +#include "loader.h" + +struct iff_data { + struct list_head iff_list; + unsigned id_size; + unsigned flags; +}; + +static int iff_process(iff_handle opaque, struct module_data *m, char *id, long size, + HIO_HANDLE *f, void *parm) +{ + struct iff_data *data = (struct iff_data *)opaque; + struct list_head *tmp; + struct iff_info *i; + int pos; + + pos = hio_tell(f); + + list_for_each(tmp, &data->iff_list) { + i = list_entry(tmp, struct iff_info, list); + if (id && !memcmp(id, i->id, data->id_size)) { + D_(D_WARN "Load IFF chunk %s (%ld) @%d", id, size, pos); + if (size > IFF_MAX_CHUNK_SIZE) { + return -1; + } + if (i->loader(m, size, f, parm) < 0) { + return -1; + } + break; + } + } + + if (hio_seek(f, pos + size, SEEK_SET) < 0) { + return -1; + } + + return 0; +} + +static int iff_chunk(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm) +{ + struct iff_data *data = (struct iff_data *)opaque; + unsigned size; + char id[17] = ""; + + D_(D_INFO "chunk id size: %d", data->id_size); + if (hio_read(id, 1, data->id_size, f) != data->id_size) { + (void)hio_error(f); /* clear error flag */ + return 1; + } + D_(D_INFO "chunk id: [%s]", id); + + if (data->flags & IFF_SKIP_EMBEDDED) { + /* embedded RIFF hack */ + if (!strncmp(id, "RIFF", 4)) { + hio_read32b(f); + hio_read32b(f); + /* read first chunk ID instead */ + if (hio_read(id, 1, data->id_size, f) != data->id_size){ + return 1; + } + } + } + + if (data->flags & IFF_LITTLE_ENDIAN) { + size = hio_read32l(f); + } else { + size = hio_read32b(f); + } + D_(D_INFO "size: %d", size); + + if (hio_error(f)) { + return -1; + } + + if (data->flags & IFF_CHUNK_ALIGN2) { + /* Sanity check */ + if (size > 0xfffffffe) { + return -1; + } + size = (size + 1) & ~1; + } + + if (data->flags & IFF_CHUNK_ALIGN4) { + /* Sanity check */ + if (size > 0xfffffffc) { + return -1; + } + size = (size + 3) & ~3; + } + + /* PT 3.6 hack: this does not seem to ever apply to "PTDT". + * This broke several modules (city lights.pt36, acid phase.pt36) */ + if ((data->flags & IFF_FULL_CHUNK_SIZE) && memcmp(id, "PTDT", 4)) { + if (size < data->id_size + 4) + return -1; + size -= data->id_size + 4; + } + + return iff_process(opaque, m, id, size, f, parm); +} + +iff_handle libxmp_iff_new() +{ + struct iff_data *data; + + data = (struct iff_data *) malloc(sizeof(struct iff_data)); + if (data == NULL) { + return NULL; + } + + INIT_LIST_HEAD(&data->iff_list); + data->id_size = 4; + data->flags = 0; + + return (iff_handle)data; +} + +int libxmp_iff_load(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm) +{ + int ret; + + while (!hio_eof(f)) { + ret = iff_chunk(opaque, m, f, parm); + if (ret > 0) + break; + if (ret < 0) + return -1; + } + + return 0; +} + +int libxmp_iff_register(iff_handle opaque, const char *id, + int (*loader)(struct module_data *, int, HIO_HANDLE *, void *)) +{ + struct iff_data *data = (struct iff_data *)opaque; + struct iff_info *f; + int i = 0; + + f = (struct iff_info *) malloc(sizeof(struct iff_info)); + if (f == NULL) + return -1; + + /* Note: previously was an strncpy */ + for (; i < 4 && id && id[i]; i++) + f->id[i] = id[i]; + for (; i < 4; i++) + f->id[i] = '\0'; + + f->loader = loader; + + list_add_tail(&f->list, &data->iff_list); + + return 0; +} + +void libxmp_iff_release(iff_handle opaque) +{ + struct iff_data *data = (struct iff_data *)opaque; + struct list_head *tmp; + struct iff_info *i; + + /* can't use list_for_each, we free the node before incrementing */ + for (tmp = (&data->iff_list)->next; tmp != (&data->iff_list);) { + i = list_entry(tmp, struct iff_info, list); + list_del(&i->list); + tmp = tmp->next; + free(i); + } + + free(data); +} + +/* Functions to tune IFF mutations */ + +void libxmp_iff_id_size(iff_handle opaque, int n) +{ + struct iff_data *data = (struct iff_data *)opaque; + + data->id_size = n; +} + +void libxmp_iff_set_quirk(iff_handle opaque, int i) +{ + struct iff_data *data = (struct iff_data *)opaque; + + data->flags |= i; +} diff --git a/thirdparty/libxmp/src/loaders/iff.h b/thirdparty/libxmp/src/loaders/iff.h new file mode 100644 index 0000000..df4c807 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/iff.h @@ -0,0 +1,43 @@ +#ifndef LIBXMP_IFF_H +#define LIBXMP_IFF_H + +#include "../hio.h" +#include "../list.h" + +#define IFF_NOBUFFER 0x0001 + +#define IFF_LITTLE_ENDIAN 0x01 +#define IFF_FULL_CHUNK_SIZE 0x02 +#define IFF_CHUNK_ALIGN2 0x04 +#define IFF_CHUNK_ALIGN4 0x08 +#define IFF_SKIP_EMBEDDED 0x10 +#define IFF_CHUNK_TRUNC4 0x20 + +#define IFF_MAX_CHUNK_SIZE 0x800000 + +typedef void *iff_handle; + +struct iff_header { + char form[4]; /* FORM */ + int len; /* File length */ + char id[4]; /* IFF type identifier */ +}; + +struct iff_info { + char id[4]; + int (*loader)(struct module_data *, int, HIO_HANDLE *, void *); + struct list_head list; +}; + +iff_handle libxmp_iff_new(void); +int libxmp_iff_load(iff_handle, struct module_data *, HIO_HANDLE *, void *); +/* int libxmp_iff_chunk(iff_handle, struct module_data *, HIO_HANDLE *, void *); */ +int libxmp_iff_register(iff_handle, const char *, + int (*loader)(struct module_data *, int, HIO_HANDLE *, void *)); +void libxmp_iff_id_size(iff_handle, int); +void libxmp_iff_set_quirk(iff_handle, int); +void libxmp_iff_release(iff_handle); +/* int libxmp_iff_process(iff_handle, struct module_data *, char *, long, + HIO_HANDLE *, void *); */ + +#endif /* LIBXMP_IFF_H */ diff --git a/thirdparty/libxmp/src/loaders/imf_load.c b/thirdparty/libxmp/src/loaders/imf_load.c new file mode 100644 index 0000000..a692dd6 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/imf_load.c @@ -0,0 +1,541 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Imago Orpheus modules based on the format description + * written by Lutz Roeder. + */ + +#include "loader.h" +#include "../period.h" + + +#define IMF_EOR 0x00 +#define IMF_CH_MASK 0x1f +#define IMF_NI_FOLLOW 0x20 +#define IMF_FX_FOLLOWS 0x80 +#define IMF_F2_FOLLOWS 0x40 + +struct imf_channel { + char name[12]; /* Channelname (ASCIIZ-String, max 11 chars) */ + uint8 status; /* Channel status */ + uint8 pan; /* Pan positions */ + uint8 chorus; /* Default chorus */ + uint8 reverb; /* Default reverb */ +}; + +struct imf_header { + char name[32]; /* Songname (ASCIIZ-String, max. 31 chars) */ + uint16 len; /* Number of orders saved */ + uint16 pat; /* Number of patterns saved */ + uint16 ins; /* Number of instruments saved */ + uint16 flg; /* Module flags */ + uint8 unused1[8]; + uint8 tpo; /* Default tempo (1..255) */ + uint8 bpm; /* Default beats per minute (BPM) (32..255) */ + uint8 vol; /* Default mastervolume (0..64) */ + uint8 amp; /* Amplification factor (4..127) */ + uint8 unused2[8]; + uint32 magic; /* 'IM10' */ + struct imf_channel chn[32]; /* Channel settings */ + uint8 pos[256]; /* Order list */ +}; + +struct imf_env { + uint8 npt; /* Number of envelope points */ + uint8 sus; /* Envelope sustain point */ + uint8 lps; /* Envelope loop start point */ + uint8 lpe; /* Envelope loop end point */ + uint8 flg; /* Envelope flags */ + uint8 unused[3]; +}; + +struct imf_instrument { + char name[32]; /* Inst. name (ASCIIZ-String, max. 31 chars) */ + uint8 map[120]; /* Multisample settings */ + uint8 unused[8]; + uint16 vol_env[32]; /* Volume envelope settings */ + uint16 pan_env[32]; /* Pan envelope settings */ + uint16 pitch_env[32]; /* Pitch envelope settings */ + struct imf_env env[3]; + uint16 fadeout; /* Fadeout rate (0...0FFFH) */ + uint16 nsm; /* Number of samples in instrument */ + uint32 magic; /* 'II10' */ +}; + +struct imf_sample { + char name[13]; /* Sample filename (12345678.ABC) */ + uint8 unused1[3]; + uint32 len; /* Length */ + uint32 lps; /* Loop start */ + uint32 lpe; /* Loop end */ + uint32 rate; /* Samplerate */ + uint8 vol; /* Default volume (0..64) */ + uint8 pan; /* Default pan (00h = Left / 80h = Middle) */ + uint8 unused2[14]; + uint8 flg; /* Sample flags */ + uint8 unused3[5]; + uint16 ems; /* Reserved for internal usage */ + uint32 dram; /* Reserved for internal usage */ + uint32 magic; /* 'IS10' */ +}; + + +#define MAGIC_IM10 MAGIC4('I','M','1','0') +#define MAGIC_II10 MAGIC4('I','I','1','0') + +static int imf_test (HIO_HANDLE *, char *, const int); +static int imf_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_imf = { + "Imago Orpheus v1.0", + imf_test, + imf_load +}; + +static int imf_test(HIO_HANDLE *f, char *t, const int start) +{ + hio_seek(f, start + 60, SEEK_SET); + if (hio_read32b(f) != MAGIC_IM10) + return -1; + + hio_seek(f, start, SEEK_SET); + libxmp_read_title(f, t, 32); + + return 0; +} + +#define NONE 0xff +#define FX_IMF_FPORTA_UP 0xfe +#define FX_IMF_FPORTA_DN 0xfd + + +/* Effect conversion table */ +static const uint8 fx[36] = { + NONE, + FX_S3M_SPEED, + FX_S3M_BPM, + FX_TONEPORTA, + FX_TONE_VSLIDE, + FX_VIBRATO, + FX_VIBRA_VSLIDE, + FX_FINE_VIBRATO, + FX_TREMOLO, + FX_S3M_ARPEGGIO, + FX_SETPAN, + FX_PANSLIDE, + FX_VOLSET, + FX_VOLSLIDE, + FX_F_VSLIDE, + FX_FINETUNE, + FX_NSLIDE_UP, + FX_NSLIDE_DN, + FX_PORTA_UP, + FX_PORTA_DN, + FX_IMF_FPORTA_UP, + FX_IMF_FPORTA_DN, + FX_FLT_CUTOFF, + FX_FLT_RESN, + FX_OFFSET, + NONE /* fine offset */, + FX_KEYOFF, + FX_MULTI_RETRIG, + FX_TREMOR, + FX_JUMP, + FX_BREAK, + FX_GLOBALVOL, + FX_GVOL_SLIDE, + FX_EXTENDED, + FX_CHORUS, + FX_REVERB +}; + + +/* Effect translation */ +static void xlat_fx (int c, uint8 *fxt, uint8 *fxp) +{ + uint8 h = MSN (*fxp), l = LSN (*fxp); + + if (*fxt >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %#02x", *fxt); + *fxt = *fxp = 0; + return; + } + + switch (*fxt = fx[*fxt]) { + case FX_IMF_FPORTA_UP: + *fxt = FX_PORTA_UP; + if (*fxp < 0x30) + *fxp = LSN (*fxp >> 2) | 0xe0; + else + *fxp = LSN (*fxp >> 4) | 0xf0; + break; + case FX_IMF_FPORTA_DN: + *fxt = FX_PORTA_DN; + if (*fxp < 0x30) + *fxp = LSN (*fxp >> 2) | 0xe0; + else + *fxp = LSN (*fxp >> 4) | 0xf0; + break; + case FX_EXTENDED: /* Extended effects */ + switch (h) { + case 0x1: /* Set filter */ + case 0x2: /* Undefined */ + case 0x4: /* Undefined */ + case 0x6: /* Undefined */ + case 0x7: /* Undefined */ + case 0x9: /* Undefined */ + case 0xe: /* Ignore envelope */ + case 0xf: /* Invert loop */ + *fxp = *fxt = 0; + break; + case 0x3: /* Glissando */ + *fxp = l | (EX_GLISS << 4); + break; + case 0x5: /* Vibrato waveform */ + *fxp = l | (EX_VIBRATO_WF << 4); + break; + case 0x8: /* Tremolo waveform */ + *fxp = l | (EX_TREMOLO_WF << 4); + break; + case 0xa: /* Pattern loop */ + *fxp = l | (EX_PATTERN_LOOP << 4); + break; + case 0xb: /* Pattern delay */ + *fxp = l | (EX_PATT_DELAY << 4); + break; + case 0xc: + if (l == 0) + *fxt = *fxp = 0; + } + break; + case NONE: /* No effect */ + *fxt = *fxp = 0; + break; + } +} + + +static int imf_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i, j; + struct xmp_event *event = 0, dummy; + struct imf_header ih; + struct imf_instrument ii; + struct imf_sample is; + int pat_len, smp_num; + uint8 n, b; + + LOAD_INIT(); + + /* Load and convert header */ + hio_read(ih.name, 32, 1, f); + ih.len = hio_read16l(f); + ih.pat = hio_read16l(f); + ih.ins = hio_read16l(f); + ih.flg = hio_read16l(f); + hio_read(ih.unused1, 8, 1, f); + ih.tpo = hio_read8(f); + ih.bpm = hio_read8(f); + ih.vol = hio_read8(f); + ih.amp = hio_read8(f); + hio_read(ih.unused2, 8, 1, f); + ih.magic = hio_read32b(f); + + /* Sanity check */ + if (ih.len > 256 || ih.pat > 256 || ih.ins > 255) { + return -1; + } + + for (i = 0; i < 32; i++) { + hio_read(ih.chn[i].name, 12, 1, f); + ih.chn[i].chorus = hio_read8(f); + ih.chn[i].reverb = hio_read8(f); + ih.chn[i].pan = hio_read8(f); + ih.chn[i].status = hio_read8(f); + } + + if (hio_read(ih.pos, 256, 1, f) < 1) { + D_(D_CRIT "read error at order list"); + return -1; + } + + if (ih.magic != MAGIC_IM10) { + return -1; + } + + libxmp_copy_adjust(mod->name, (uint8 *)ih.name, 32); + + mod->len = ih.len; + mod->ins = ih.ins; + mod->smp = 1024; + mod->pat = ih.pat; + + if (ih.flg & 0x01) + m->period_type = PERIOD_LINEAR; + + mod->spd = ih.tpo; + mod->bpm = ih.bpm; + + libxmp_set_type(m, "Imago Orpheus 1.0 IMF"); + + MODULE_INFO(); + + mod->chn = 0; + for (i = 0; i < 32; i++) { + /* 0=enabled; 1=muted, but still processed; 2=disabled.*/ + if (ih.chn[i].status >= 2) + continue; + + mod->chn = i + 1; + mod->xxc[i].pan = ih.chn[i].pan; +#if 0 + /* FIXME */ + mod->xxc[i].cho = ih.chn[i].chorus; + mod->xxc[i].rvb = ih.chn[i].reverb; + mod->xxc[i].flg |= XMP_CHANNEL_FX; +#endif + } + + mod->trk = mod->pat * mod->chn; + + memcpy(mod->xxo, ih.pos, mod->len); + for (i = 0; i < mod->len; i++) { + if (mod->xxo[i] == 0xff) + mod->xxo[i]--; + } + + m->c4rate = C4_NTSC_RATE; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + int rows; + + pat_len = hio_read16l(f) - 4; + + rows = hio_read16l(f); + + /* Sanity check */ + if (rows > 256) { + return -1; + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + r = 0; + + while (--pat_len >= 0) { + b = hio_read8(f); + + if (b == IMF_EOR) { + r++; + continue; + } + + /* Sanity check */ + if (r >= rows) { + return -1; + } + + c = b & IMF_CH_MASK; + event = c >= mod->chn ? &dummy : &EVENT(i, c, r); + + if (b & IMF_NI_FOLLOW) { + n = hio_read8(f); + switch (n) { + case 255: + case 160: /* ?! */ + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n = 13 + 12 * MSN (n) + LSN (n); + } + + event->note = n; + event->ins = hio_read8(f); + pat_len -= 2; + } + if (b & IMF_FX_FOLLOWS) { + event->fxt = hio_read8(f); + event->fxp = hio_read8(f); + xlat_fx(c, &event->fxt, &event->fxp); + pat_len -= 2; + } + if (b & IMF_F2_FOLLOWS) { + event->f2t = hio_read8(f); + event->f2p = hio_read8(f); + xlat_fx(c, &event->f2t, &event->f2p); + pat_len -= 2; + } + } + } + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + + D_(D_INFO "Instruments: %d", mod->ins); + + for (smp_num = i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + + hio_read(ii.name, 32, 1, f); + ii.name[31] = 0; + hio_read(ii.map, 120, 1, f); + hio_read(ii.unused, 8, 1, f); + for (j = 0; j < 32; j++) + ii.vol_env[j] = hio_read16l(f); + for (j = 0; j < 32; j++) + ii.pan_env[j] = hio_read16l(f); + for (j = 0; j < 32; j++) + ii.pitch_env[j] = hio_read16l(f); + for (j = 0; j < 3; j++) { + ii.env[j].npt = hio_read8(f); + ii.env[j].sus = hio_read8(f); + ii.env[j].lps = hio_read8(f); + ii.env[j].lpe = hio_read8(f); + ii.env[j].flg = hio_read8(f); + hio_read(ii.env[j].unused, 3, 1, f); + } + ii.fadeout = hio_read16l(f); + ii.nsm = hio_read16l(f); + ii.magic = hio_read32b(f); + + /* Sanity check */ + if (ii.nsm > 255) + return -1; + + if (ii.magic != MAGIC_II10) + return -2; + + xxi->nsm = ii.nsm; + + if (xxi->nsm > 0) { + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) + return -1; + } + + strncpy((char *)xxi->name, ii.name, 31); + xxi->name[31] = '\0'; + + for (j = 0; j < 108; j++) { + xxi->map[j + 12].ins = ii.map[j]; + } + + D_(D_INFO "[%2X] %-31.31s %2d %4x %c", i, ii.name, ii.nsm, + ii.fadeout, ii.env[0].flg & 0x01 ? 'V' : '-'); + + xxi->aei.npt = ii.env[0].npt; + xxi->aei.sus = ii.env[0].sus; + xxi->aei.lps = ii.env[0].lps; + xxi->aei.lpe = ii.env[0].lpe; + xxi->aei.flg = ii.env[0].flg & 0x01 ? XMP_ENVELOPE_ON : 0; + xxi->aei.flg |= ii.env[0].flg & 0x02 ? XMP_ENVELOPE_SUS : 0; + xxi->aei.flg |= ii.env[0].flg & 0x04 ? XMP_ENVELOPE_LOOP : 0; + + /* Sanity check */ + if (xxi->aei.npt > 16) { + return -1; + } + + for (j = 0; j < xxi->aei.npt; j++) { + xxi->aei.data[j * 2] = ii.vol_env[j * 2]; + xxi->aei.data[j * 2 + 1] = ii.vol_env[j * 2 + 1]; + } + + for (j = 0; j < ii.nsm; j++, smp_num++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[smp_num]; + int sid; + + hio_read(is.name, 13, 1, f); + hio_read(is.unused1, 3, 1, f); + is.len = hio_read32l(f); + is.lps = hio_read32l(f); + is.lpe = hio_read32l(f); + is.rate = hio_read32l(f); + is.vol = hio_read8(f); + is.pan = hio_read8(f); + hio_read(is.unused2, 14, 1, f); + is.flg = hio_read8(f); + hio_read(is.unused3, 5, 1, f); + is.ems = hio_read16l(f); + is.dram = hio_read32l(f); + is.magic = hio_read32b(f); + + /* Sanity check */ + if (is.len > 0x100000 || is.lps > 0x100000 || is.lpe > 0x100000) + return -1; + + sub->sid = smp_num; + sub->vol = is.vol; + sub->pan = is.pan; + xxs->len = is.len; + xxs->lps = is.lps; + xxs->lpe = is.lpe; + xxs->flg = is.flg & 1 ? XMP_SAMPLE_LOOP : 0; + + if (is.flg & 4) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + D_(D_INFO " %02x: %05x %05x %05x %5d", + j, is.len, is.lps, is.lpe, is.rate); + + libxmp_c2spd_to_note(is.rate, &sub->xpo, &sub->fin); + + if (xxs->len <= 0) + continue; + + sid = sub->sid; + if (libxmp_load_sample(m, f, 0, &mod->xxs[sid], NULL) < 0) + return -1; + } + } + + mod->smp = smp_num; + mod->xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * mod->smp); + if (mod->xxs == NULL) { + return -1; + } + m->xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * mod->smp); + if (m->xtra == NULL) { + return -1; + } + + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRK_FILTER | QUIRKS_ST3 | QUIRK_ARPMEM; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/ims_load.c b/thirdparty/libxmp/src/loaders/ims_load.c new file mode 100644 index 0000000..1942eb0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ims_load.c @@ -0,0 +1,293 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Images Music System modules based on the EP replayer. + * + * Date: Thu, 19 Apr 2001 19:13:06 +0200 + * From: Michael Doering + * + * I just "stumbled" upon something about the Unic.3C format when I was + * testing replayers for the upcoming UADE 0.21 that might be also + * interesting to you for xmp. The "Beastbusters" tune is not a UNIC file :) + * It's actually a different Format, although obviously related, called + * "Images Music System". + * + * I was testing the replayer from the Wanted Team with one of their test + * tunes, among them also the beastbuster music. When I first listened to + * it, I knew I have heard it somewhere, a bit different but it was alike. + * This one had more/richer percussions and there was no strange beep in + * the bg. ;) After some searching on my HD I found it among the xmp test + * tunes as a UNIC file. + */ + +#include "loader.h" +#include "../period.h" + +struct ims_instrument { + uint8 name[20]; + int16 finetune; /* Causes squeaks in beast-busters1! */ + uint16 size; + uint8 unknown; + uint8 volume; + uint16 loop_start; + uint16 loop_size; +}; + +struct ims_header { + uint8 title[20]; + struct ims_instrument ins[31]; + uint8 len; + uint8 zero; + uint8 orders[128]; + uint8 magic[4]; +}; + + +static int ims_test (HIO_HANDLE *, char *, const int); +static int ims_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ims = { + "Images Music System", + ims_test, + ims_load +}; + +static int ims_test(HIO_HANDLE *f, char *t, const int start) +{ + int i; + int smp_size, pat; + struct ims_header ih; + + smp_size = 0; + + hio_read(ih.title, 20, 1, f); + + for (i = 0; i < 31; i++) { + if (hio_read(ih.ins[i].name, 1, 20, f) < 20) + return -1; + + ih.ins[i].finetune = (int16)hio_read16b(f); + ih.ins[i].size = hio_read16b(f); + ih.ins[i].unknown = hio_read8(f); + ih.ins[i].volume = hio_read8(f); + ih.ins[i].loop_start = hio_read16b(f); + ih.ins[i].loop_size = hio_read16b(f); + + smp_size += ih.ins[i].size * 2; + + if (libxmp_test_name(ih.ins[i].name, 20, 0) < 0) + return -1; + + if (ih.ins[i].volume > 0x40) + return -1; + + if (ih.ins[i].size > 0x8000) + return -1; + + if (ih.ins[i].loop_start > ih.ins[i].size) + return -1; + + if (ih.ins[i].size && ih.ins[i].loop_size > 2 * ih.ins[i].size) + return -1; + } + + if (smp_size < 8) + return -1; + + ih.len = hio_read8(f); + ih.zero = hio_read8(f); + hio_read(ih.orders, 128, 1, f); + if (hio_read(ih.magic, 4, 1, f) == 0) + return -1; + + if (ih.zero > 1) /* not sure what this is */ + return -1; + + if (ih.magic[3] != 0x3c) + return -1; + + if (ih.len > 0x7f) + return -1; + + for (pat = i = 0; i < ih.len; i++) + if (ih.orders[i] > pat) + pat = ih.orders[i]; + pat++; + + if (pat > 0x7f || ih.len == 0 || ih.len > 0x7f) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + + +static int ims_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct ims_header ih; + uint8 ims_event[3]; + int xpo = 21; /* Tuned against UADE */ + + LOAD_INIT(); + + mod->chn = 4; + mod->ins = 31; + mod->smp = mod->ins; + + hio_read (ih.title, 20, 1, f); + + for (i = 0; i < 31; i++) { + hio_read (ih.ins[i].name, 20, 1, f); + ih.ins[i].finetune = (int16)hio_read16b(f); + ih.ins[i].size = hio_read16b(f); + ih.ins[i].unknown = hio_read8(f); + ih.ins[i].volume = hio_read8(f); + ih.ins[i].loop_start = hio_read16b(f); + ih.ins[i].loop_size = hio_read16b(f); + } + + ih.len = hio_read8(f); + if (ih.len > 128) { + return -1; + } + ih.zero = hio_read8(f); + hio_read (ih.orders, 128, 1, f); + hio_read (ih.magic, 4, 1, f); + + mod->len = ih.len; + memcpy (mod->xxo, ih.orders, mod->len); + + for (i = 0; i < mod->len; i++) + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + + mod->pat++; + mod->trk = mod->chn * mod->pat; + + strncpy(mod->name, (char *)ih.title, 20); + libxmp_set_type(m, "Images Music System"); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * ih.ins[i].size; + xxs->lps = 2 * ih.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * ih.ins[i].loop_size; + xxs->flg = ih.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = 0; /* ih.ins[i].finetune; */ + sub->vol = ih.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + //mod->xxi[i].rls = 0xfff; + + if (xxs->len > 0) { + xxi->nsm = 1; + } + + libxmp_instrument_name(mod, i, ih.ins[i].name, 20); + + D_(D_INFO "[%2X] %-20.20s %04x %04x %04x %c V%02x %+d", + i, xxi->name, xxs->len, xxs->lps, xxs->lpe, + ih.ins[i].loop_size > 1 ? 'L' : ' ', sub->vol, sub->fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) { + return -1; + } + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 0x100; j++) { + event = &EVENT (i, j & 0x3, j >> 2); + hio_read(ims_event, 1, 3, f); + + /* Event format: + * + * 0000 0000 0000 0000 0000 0000 + * |\ / \ / \ / \ / + * | note ins fx parameter + * ins + * + * 0x3f is a blank note. + */ + event->note = ims_event[0] & 0x3f; + if (event->note != 0x00 && event->note != 0x3f) + event->note += xpo + 12; + else + event->note = 0; + event->ins = ((ims_event[0] & 0x40) >> 2) | MSN(ims_event[1]); + event->fxt = LSN(ims_event[1]); + event->fxp = ims_event[2]; + + libxmp_disable_continue_fx (event); + + /* According to Asle: + * ``Just note that pattern break effect command (D**) uses + * HEX value in UNIC format (while it is DEC values in PTK). + * Thus, it has to be converted!'' + * + * Is this valid for IMS as well? --claudio + */ + if (event->fxt == 0x0d) + event->fxp = (event->fxp / 10) << 4 | (event->fxp % 10); + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (!mod->xxs[i].len) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/it.h b/thirdparty/libxmp/src/loaders/it.h new file mode 100644 index 0000000..6df35b4 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/it.h @@ -0,0 +1,196 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_LOADERS_IT_H +#define LIBXMP_LOADERS_IT_H + +#include "loader.h" + +/* IT flags */ +#define IT_STEREO 0x01 +#define IT_VOL_OPT 0x02 /* Not recognized */ +#define IT_USE_INST 0x04 +#define IT_LINEAR_FREQ 0x08 +#define IT_OLD_FX 0x10 +#define IT_LINK_GXX 0x20 +#define IT_MIDI_WHEEL 0x40 +#define IT_MIDI_CONFIG 0x80 + +/* IT special */ +#define IT_HAS_MSG 0x01 +#define IT_EDIT_HISTORY 0x02 +#define IT_HIGHLIGHTS 0x04 +#define IT_SPEC_MIDICFG 0x08 + +/* IT instrument flags */ +#define IT_INST_SAMPLE 0x01 +#define IT_INST_16BIT 0x02 +#define IT_INST_STEREO 0x04 +#define IT_INST_LOOP 0x10 +#define IT_INST_SLOOP 0x20 +#define IT_INST_BLOOP 0x40 +#define IT_INST_BSLOOP 0x80 + +/* IT sample flags */ +#define IT_SMP_SAMPLE 0x01 +#define IT_SMP_16BIT 0x02 +#define IT_SMP_STEREO 0x04 /* unsupported */ +#define IT_SMP_COMP 0x08 /* unsupported */ +#define IT_SMP_LOOP 0x10 +#define IT_SMP_SLOOP 0x20 +#define IT_SMP_BLOOP 0x40 +#define IT_SMP_BSLOOP 0x80 + +/* IT sample conversion flags */ +#define IT_CVT_SIGNED 0x01 +#define IT_CVT_BIGEND 0x02 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_DIFF 0x04 /* Compressed sample flag */ +#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_ADPCM 0xff /* Special: always indicates Modplug ADPCM4 */ + +/* IT envelope flags */ +#define IT_ENV_ON 0x01 +#define IT_ENV_LOOP 0x02 +#define IT_ENV_SLOOP 0x04 +#define IT_ENV_CARRY 0x08 +#define IT_ENV_FILTER 0x80 + + +struct it_file_header { + uint32 magic; /* 'IMPM' */ + uint8 name[26]; /* ASCIIZ Song name */ + uint8 hilite_min; /* Pattern editor highlight */ + uint8 hilite_maj; /* Pattern editor highlight */ + uint16 ordnum; /* Number of orders (must be even) */ + uint16 insnum; /* Number of instruments */ + uint16 smpnum; /* Number of samples */ + uint16 patnum; /* Number of patterns */ + uint16 cwt; /* Tracker ID and version */ + uint16 cmwt; /* Format version */ + uint16 flags; /* Flags */ + uint16 special; /* More flags */ + uint8 gv; /* Global volume */ + uint8 mv; /* Master volume */ + uint8 is; /* Initial speed */ + uint8 it; /* Initial tempo */ + uint8 sep; /* Panning separation */ + uint8 pwd; /* Pitch wheel depth */ + uint16 msglen; /* Message length */ + uint32 msgofs; /* Message offset */ + uint32 rsvd; /* Reserved */ + uint8 chpan[64]; /* Channel pan settings */ + uint8 chvol[64]; /* Channel volume settings */ +}; + +struct it_instrument1_header { + uint32 magic; /* 'IMPI' */ + uint8 dosname[12]; /* DOS filename */ + uint8 zero; /* Always zero */ + uint8 flags; /* Instrument flags */ + uint8 vls; /* Volume loop start */ + uint8 vle; /* Volume loop end */ + uint8 sls; /* Sustain loop start */ + uint8 sle; /* Sustain loop end */ + uint16 rsvd1; /* Reserved */ + uint16 fadeout; /* Fadeout (release) */ + uint8 nna; /* New note action */ + uint8 dnc; /* Duplicate note check */ + uint16 trkvers; /* Tracker version */ + uint8 nos; /* Number of samples */ + uint8 rsvd2; /* Reserved */ + uint8 name[26]; /* ASCIIZ Instrument name */ + uint8 rsvd3[6]; /* Reserved */ + uint8 keys[240]; + uint8 epoint[200]; + uint8 enode[50]; +}; + +struct it_instrument2_header { + uint32 magic; /* 'IMPI' */ + uint8 dosname[12]; /* DOS filename */ + uint8 zero; /* Always zero */ + uint8 nna; /* New Note Action */ + uint8 dct; /* Duplicate Check Type */ + uint8 dca; /* Duplicate Check Action */ + uint16 fadeout; + uint8 pps; /* Pitch-Pan Separation */ + uint8 ppc; /* Pitch-Pan Center */ + uint8 gbv; /* Global Volume */ + uint8 dfp; /* Default pan */ + uint8 rv; /* Random volume variation */ + uint8 rp; /* Random pan variation */ + uint16 trkvers; /* Not used: tracked version */ + uint8 nos; /* Not used: number of samples */ + uint8 rsvd1; /* Reserved */ + uint8 name[26]; /* ASCIIZ Instrument name */ + uint8 ifc; /* Initial filter cutoff */ + uint8 ifr; /* Initial filter resonance */ + uint8 mch; /* MIDI channel */ + uint8 mpr; /* MIDI program */ + uint16 mbnk; /* MIDI bank */ + uint8 keys[240]; +}; + +struct it_envelope_node { + int8 y; + uint16 x; +}; + +struct it_envelope { + uint8 flg; /* Flags */ + uint8 num; /* Number of node points */ + uint8 lpb; /* Loop beginning */ + uint8 lpe; /* Loop end */ + uint8 slb; /* Sustain loop beginning */ + uint8 sle; /* Sustain loop end */ + struct it_envelope_node node[25]; + uint8 unused; +}; + +struct it_sample_header { + uint32 magic; /* 'IMPS' */ + uint8 dosname[12]; /* DOS filename */ + uint8 zero; /* Always zero */ + uint8 gvl; /* Global volume for instrument */ + uint8 flags; /* Sample flags */ + uint8 vol; /* Volume */ + uint8 name[26]; /* ASCIIZ sample name */ + uint8 convert; /* Sample flags */ + uint8 dfp; /* Default pan */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint32 c5spd; /* C 5 speed */ + uint32 sloopbeg; /* SusLoop begin */ + uint32 sloopend; /* SusLoop end */ + uint32 sample_ptr; /* Sample pointer */ + uint8 vis; /* Vibrato speed */ + uint8 vid; /* Vibrato depth */ + uint8 vir; /* Vibrato rate */ + uint8 vit; /* Vibrato waveform */ +}; + +int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215); +int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215); + +#endif /* LIBXMP_LOADERS_IT_H */ diff --git a/thirdparty/libxmp/src/loaders/it_load.c b/thirdparty/libxmp/src/loaders/it_load.c new file mode 100644 index 0000000..f3cc8c0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/it_load.c @@ -0,0 +1,1465 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_CORE_DISABLE_IT + +#include "loader.h" +#include "it.h" +#include "../period.h" + +#define MAGIC_IMPM MAGIC4('I','M','P','M') +#define MAGIC_IMPI MAGIC4('I','M','P','I') +#define MAGIC_IMPS MAGIC4('I','M','P','S') + +static int it_test(HIO_HANDLE *, char *, const int); +static int it_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_it = { + "Impulse Tracker", + it_test, + it_load +}; + +static int it_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_IMPM) + return -1; + + libxmp_read_title(f, t, 26); + + return 0; +} + + +#define FX_NONE 0xff +#define FX_XTND 0xfe +#define L_CHANNELS 64 + +static const uint8 fx[32] = { + /* */ FX_NONE, + /* A */ FX_S3M_SPEED, + /* B */ FX_JUMP, + /* C */ FX_IT_BREAK, + /* D */ FX_VOLSLIDE, + /* E */ FX_PORTA_DN, + /* F */ FX_PORTA_UP, + /* G */ FX_TONEPORTA, + /* H */ FX_VIBRATO, + /* I */ FX_TREMOR, + /* J */ FX_S3M_ARPEGGIO, + /* K */ FX_VIBRA_VSLIDE, + /* L */ FX_TONE_VSLIDE, + /* M */ FX_TRK_VOL, + /* N */ FX_TRK_VSLIDE, + /* O */ FX_OFFSET, + /* P */ FX_IT_PANSLIDE, + /* Q */ FX_MULTI_RETRIG, + /* R */ FX_TREMOLO, + /* S */ FX_XTND, + /* T */ FX_IT_BPM, + /* U */ FX_FINE_VIBRATO, + /* V */ FX_GLOBALVOL, + /* W */ FX_GVOL_SLIDE, + /* X */ FX_SETPAN, + /* Y */ FX_PANBRELLO, + /* Z */ FX_MACRO, + /* ? */ FX_NONE, + /* / */ FX_MACROSMOOTH, + /* ? */ FX_NONE, + /* ? */ FX_NONE, + /* ? */ FX_NONE +}; + +static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx) +{ + uint8 h = MSN(e->fxp), l = LSN(e->fxp); + + switch (e->fxt = fx[e->fxt]) { + case FX_XTND: /* Extended effect */ + e->fxt = FX_EXTENDED; + + if (h == 0 && e->fxp == 0) { + e->fxp = last_fxp[c]; + h = MSN(e->fxp); + l = LSN(e->fxp); + } else { + last_fxp[c] = e->fxp; + } + + switch (h) { + case 0x1: /* Glissando */ + e->fxp = 0x30 | l; + break; + case 0x2: /* Finetune -- not supported */ + e->fxt = e->fxp = 0; + break; + case 0x3: /* Vibrato wave */ + e->fxp = 0x40 | l; + break; + case 0x4: /* Tremolo wave */ + e->fxp = 0x70 | l; + break; + case 0x5: /* Panbrello wave */ + if (l <= 3) { + e->fxt = FX_PANBRELLO_WF; + e->fxp = l; + } else { + e->fxt = e->fxp = 0; + } + break; + case 0x6: /* Pattern delay */ + e->fxp = 0xe0 | l; + break; + case 0x7: /* Instrument functions */ + e->fxt = FX_IT_INSTFUNC; + e->fxp &= 0x0f; + break; + case 0x8: /* Set pan position */ + e->fxt = FX_SETPAN; + e->fxp = l << 4; + break; + case 0x9: + if (l == 0 || l == 1) { + /* 0x91 = set surround */ + e->fxt = FX_SURROUND; + e->fxp = l; + } else if (l == 0xe || l == 0xf) { + /* 0x9f Play reverse (MPT) */ + e->fxt = FX_REVERSE; + e->fxp = l - 0xe; + } + break; + case 0xa: /* High offset */ + e->fxt = FX_HIOFFSET; + e->fxp = l; + break; + case 0xb: /* Pattern loop */ + e->fxp = 0x60 | l; + break; + case 0xc: /* Note cut */ + case 0xd: /* Note delay */ + if ((e->fxp = l) == 0) + e->fxp++; /* SD0 and SC0 become SD1 and SC1 */ + e->fxp |= h << 4; + break; + case 0xe: /* Pattern row delay */ + e->fxt = FX_IT_ROWDELAY; + e->fxp = l; + break; + case 0xf: /* Set parametered macro */ + e->fxt = FX_MACRO_SET; + e->fxp = l; + break; + default: + e->fxt = e->fxp = 0; + } + break; + case FX_TREMOR: + if (!new_fx && e->fxp != 0) { + e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1); + } + break; + case FX_GLOBALVOL: + if (e->fxp > 0x80) { /* See storlek test 16 */ + e->fxt = e->fxp = 0; + } + break; + case FX_NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + + +static void xlat_volfx(struct xmp_event *event) +{ + int b; + + b = event->vol; + event->vol = 0; + + if (b <= 0x40) { + event->vol = b + 1; + } else if (b >= 65 && b <= 74) { /* A */ + event->f2t = FX_F_VSLIDE_UP_2; + event->f2p = b - 65; + } else if (b >= 75 && b <= 84) { /* B */ + event->f2t = FX_F_VSLIDE_DN_2; + event->f2p = b - 75; + } else if (b >= 85 && b <= 94) { /* C */ + event->f2t = FX_VSLIDE_UP_2; + event->f2p = b - 85; + } else if (b >= 95 && b <= 104) { /* D */ + event->f2t = FX_VSLIDE_DN_2; + event->f2p = b - 95; + } else if (b >= 105 && b <= 114) { /* E */ + event->f2t = FX_PORTA_DN; + event->f2p = (b - 105) << 2; + } else if (b >= 115 && b <= 124) { /* F */ + event->f2t = FX_PORTA_UP; + event->f2p = (b - 115) << 2; + } else if (b >= 128 && b <= 192) { /* pan */ + if (b == 192) { + event->f2p = 0xff; + } else { + event->f2p = (b - 128) << 2; + } + event->f2t = FX_SETPAN; + } else if (b >= 193 && b <= 202) { /* G */ + uint8 val[10] = { + 0x00, 0x01, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x60, 0x80, 0xff + }; + event->f2t = FX_TONEPORTA; + event->f2p = val[b - 193]; + } else if (b >= 203 && b <= 212) { /* H */ + event->f2t = FX_VIBRATO; + event->f2p = b - 203; + } +} + + +static void fix_name(uint8 *s, int l) +{ + int i; + + /* IT names can have 0 at start of data, replace with space */ + for (l--, i = 0; i < l; i++) { + if (s[i] == 0) + s[i] = ' '; + } + for (i--; i >= 0 && s[i] == ' '; i--) { + if (s[i] == ' ') + s[i] = 0; + } +} + + +static int load_it_midi_config(struct module_data *m, HIO_HANDLE *f) +{ + int i; + + m->midi = (struct midi_macro_data *) calloc(1, sizeof(struct midi_macro_data)); + if (m->midi == NULL) + return -1; + + /* Skip global MIDI macros */ + if (hio_seek(f, 9 * 32, SEEK_CUR) < 0) + return -1; + + /* SFx macros */ + for (i = 0; i < 16; i++) { + if (hio_read(m->midi->param[i].data, 1, 32, f) < 32) + return -1; + m->midi->param[i].data[31] = '\0'; + } + /* Zxx macros */ + for (i = 0; i < 128; i++) { + if (hio_read(m->midi->fixed[i].data, 1, 32, f) < 32) + return -1; + m->midi->fixed[i].data[31] = '\0'; + } + return 0; +} + + +static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env, + HIO_HANDLE *f) +{ + int i; + uint8 buf[82]; + + if (hio_read(buf, 1, 82, f) != 82) { + return -1; + } + + env->flg = buf[0]; + env->num = MIN(buf[1], 25); /* Clamp to IT max */ + + env->lpb = buf[2]; + env->lpe = buf[3]; + env->slb = buf[4]; + env->sle = buf[5]; + + for (i = 0; i < 25; i++) { + env->node[i].y = buf[6 + i * 3]; + env->node[i].x = readmem16l(buf + 7 + i * 3); + } + + ei->flg = env->flg & IT_ENV_ON ? XMP_ENVELOPE_ON : 0; + + if (env->flg & IT_ENV_LOOP) { + ei->flg |= XMP_ENVELOPE_LOOP; + } + + if (env->flg & IT_ENV_SLOOP) { + ei->flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP; + } + + if (env->flg & IT_ENV_CARRY) { + ei->flg |= XMP_ENVELOPE_CARRY; + } + + ei->npt = env->num; + ei->sus = env->slb; + ei->sue = env->sle; + ei->lps = env->lpb; + ei->lpe = env->lpe; + + if (ei->npt > 0 && ei->npt <= 25 /* XMP_MAX_ENV_POINTS */) { + for (i = 0; i < ei->npt; i++) { + ei->data[i * 2] = env->node[i].x; + ei->data[i * 2 + 1] = env->node[i].y; + } + } else { + ei->flg &= ~XMP_ENVELOPE_ON; + } + + return 0; +} + +static void identify_tracker(struct module_data *m, struct it_file_header *ifh, + int pat_before_smp, int *is_mpt_116) +{ +#ifndef LIBXMP_CORE_PLAYER + char tracker_name[40]; + int sample_mode = ~ifh->flags & IT_USE_INST; + + switch (ifh->cwt >> 8) { + case 0x00: + strcpy(tracker_name, "unmo3"); + break; + case 0x01: + case 0x02: /* test from Schism Tracker sources */ + if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0214 + && ifh->flags == 9 && ifh->special == 0 + && ifh->hilite_maj == 0 && ifh->hilite_min == 0 + && ifh->insnum == 0 && ifh->patnum + 1 == ifh->ordnum + && ifh->gv == 128 && ifh->mv == 100 && ifh->is == 1 + && ifh->sep == 128 && ifh->pwd == 0 + && ifh->msglen == 0 && ifh->msgofs == 0 && ifh->rsvd == 0) { + strcpy(tracker_name, "OpenSPC conversion"); + } else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0217) { + strcpy(tracker_name, "ModPlug Tracker 1.16"); + /* ModPlug Tracker files aren't really IMPM 2.00 */ + ifh->cmwt = sample_mode ? 0x100 : 0x214; + *is_mpt_116 = 1; + } else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0202 && pat_before_smp) { + /* ModPlug Tracker ITs from pre-alpha 4 use tracker + * 0x0202 and format 0x0200. Unfortunately, ITs from + * Impulse Tracker may *also* use this. These MPT ITs + * can be detected because they write patterns before + * samples/instruments. */ + strcpy(tracker_name, "ModPlug Tracker 1.0 pre-alpha"); + ifh->cmwt = sample_mode ? 0x100 : 0x200; + *is_mpt_116 = 1; + } else if (ifh->cwt == 0x0216) { + strcpy(tracker_name, "Impulse Tracker 2.14v3"); + } else if (ifh->cwt == 0x0217) { + strcpy(tracker_name, "Impulse Tracker 2.14v5"); + } else if (ifh->cwt == 0x0214 && !memcmp(&ifh->rsvd, "CHBI", 4)) { + strcpy(tracker_name, "Chibi Tracker"); + } else { + snprintf(tracker_name, 40, "Impulse Tracker %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + } + break; + case 0x08: + case 0x7f: + if (ifh->cwt == 0x0888) { + strcpy(tracker_name, "OpenMPT 1.17"); + *is_mpt_116 = 1; + } else if (ifh->cwt == 0x7fff) { + strcpy(tracker_name, "munch.py"); + } else { + snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt); + } + break; + default: + switch (ifh->cwt >> 12) { + case 0x1: + libxmp_schism_tracker_string(tracker_name, 40, + (ifh->cwt & 0x0fff), ifh->rsvd); + break; + case 0x5: + snprintf(tracker_name, 40, "OpenMPT %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + if (memcmp(&ifh->rsvd, "OMPT", 4)) + strncat(tracker_name, " (compat.)", 39); + break; + case 0x06: + snprintf(tracker_name, 40, "BeRoTracker %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + break; + default: + snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt); + } + } + + libxmp_set_type(m, "%s IT %d.%02x", tracker_name, ifh->cmwt >> 8, + ifh->cmwt & 0xff); +#else + libxmp_set_type(m, "Impulse Tracker"); +#endif +} + +static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) +{ + int inst_map[120], inst_rmap[XMP_MAX_KEYS]; + struct it_instrument1_header i1h; + int c, k, j; + uint8 buf[64]; + + if (hio_read(buf, 1, 64, f) != 64) { + return -1; + } + + i1h.magic = readmem32b(buf); + if (i1h.magic != MAGIC_IMPI) { + D_(D_CRIT "bad instrument magic"); + return -1; + } + memcpy(i1h.dosname, buf + 4, 12); + i1h.zero = buf[16]; + i1h.flags = buf[17]; + i1h.vls = buf[18]; + i1h.vle = buf[19]; + i1h.sls = buf[20]; + i1h.sle = buf[21]; + i1h.fadeout = readmem16l(buf + 24); + i1h.nna = buf[26]; + i1h.dnc = buf[27]; + i1h.trkvers = readmem16l(buf + 28); + i1h.nos = buf[30]; + + memcpy(i1h.name, buf + 32, 26); + fix_name(i1h.name, 26); + + if (hio_read(i1h.keys, 1, 240, f) != 240) { + return -1; + } + if (hio_read(i1h.epoint, 1, 200, f) != 200) { + return -1; + } + if (hio_read(i1h.enode, 1, 50, f) != 50) { + return -1; + } + + libxmp_copy_adjust(xxi->name, i1h.name, 25); + + xxi->rls = i1h.fadeout << 7; + + xxi->aei.flg = 0; + if (i1h.flags & IT_ENV_ON) { + xxi->aei.flg |= XMP_ENVELOPE_ON; + } + if (i1h.flags & IT_ENV_LOOP) { + xxi->aei.flg |= XMP_ENVELOPE_LOOP; + } + if (i1h.flags & IT_ENV_SLOOP) { + xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP; + } + if (i1h.flags & IT_ENV_CARRY) { + xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_CARRY; + } + xxi->aei.lps = i1h.vls; + xxi->aei.lpe = i1h.vle; + xxi->aei.sus = i1h.sls; + xxi->aei.sue = i1h.sle; + + for (k = 0; k < 25 && i1h.enode[k * 2] != 0xff; k++) ; + + /* Sanity check */ + if (k >= 25 || i1h.enode[k * 2] != 0xff) { + return -1; + } + + for (xxi->aei.npt = k; k--;) { + xxi->aei.data[k * 2] = i1h.enode[k * 2]; + xxi->aei.data[k * 2 + 1] = i1h.enode[k * 2 + 1]; + } + + /* See how many different instruments we have */ + for (j = 0; j < 120; j++) + inst_map[j] = -1; + + for (k = j = 0; j < XMP_MAX_KEYS; j++) { + c = j < 120 ? i1h.keys[j * 2 + 1] - 1 : -1; + if (c < 0 || c >= 120) { + xxi->map[j].ins = 0; + xxi->map[j].xpo = 0; + continue; + } + if (inst_map[c] == -1) { + inst_map[c] = k; + inst_rmap[k] = c; + k++; + } + xxi->map[j].ins = inst_map[c]; + xxi->map[j].xpo = i1h.keys[j * 2] - j; + } + + xxi->nsm = k; + xxi->vol = 0x40; + + if (k) { + xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) { + return -1; + } + + for (j = 0; j < k; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = inst_rmap[j]; + sub->nna = i1h.nna; + sub->dct = + i1h.dnc ? XMP_INST_DCT_NOTE : XMP_INST_DCT_OFF; + sub->dca = XMP_INST_DCA_CUT; + sub->pan = -1; + } + } + + D_(D_INFO "[ ] %-26.26s %d %-4.4s %4d %2d %c%c%c %3d", + /*i,*/ i1h.name, + i1h.nna, + i1h.dnc ? "on" : "off", + i1h.fadeout, + xxi->aei.npt, + xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-', + xxi->aei.flg & XMP_ENVELOPE_LOOP ? 'L' : '-', + xxi->aei.flg & XMP_ENVELOPE_SUS ? 'S' : '-', xxi->nsm); + + return 0; +} + +static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) +{ + int inst_map[120], inst_rmap[XMP_MAX_KEYS]; + struct it_instrument2_header i2h; + struct it_envelope env; + int dca2nna[] = { 0, 2, 3, 3 /* Northern Sky (cj-north.it) has this... */ }; + int c, k, j; + uint8 buf[64]; + + if (hio_read(buf, 1, 64, f) != 64) { + return -1; + } + + i2h.magic = readmem32b(buf); + if (i2h.magic != MAGIC_IMPI) { + D_(D_CRIT "bad instrument magic"); + return -1; + } + memcpy(i2h.dosname, buf + 4, 12); + i2h.zero = buf[16]; + i2h.nna = buf[17]; + i2h.dct = buf[18]; + i2h.dca = buf[19]; + + /* Sanity check */ + if (i2h.dca > 3) { + /* Northern Sky has an instrument with DCA 3 */ + D_(D_WARN "bad instrument dca: %d", i2h.dca); + i2h.dca = 0; + } + + i2h.fadeout = readmem16l(buf + 20); + i2h.pps = buf[22]; + i2h.ppc = buf[23]; + i2h.gbv = buf[24]; + i2h.dfp = buf[25]; + i2h.rv = buf[26]; + i2h.rp = buf[27]; + i2h.trkvers = readmem16l(buf + 28); + i2h.nos = buf[30]; + + memcpy(i2h.name, buf + 32, 26); + fix_name(i2h.name, 26); + + i2h.ifc = buf[58]; + i2h.ifr = buf[59]; + i2h.mch = buf[60]; + i2h.mpr = buf[61]; + i2h.mbnk = readmem16l(buf + 62); + + if (hio_read(i2h.keys, 1, 240, f) != 240) { + D_(D_CRIT "key map read error"); + return -1; + } + + libxmp_copy_adjust(xxi->name, i2h.name, 25); + xxi->rls = i2h.fadeout << 6; + + /* Envelopes */ + + if (read_envelope(&xxi->aei, &env, f) < 0) { + return -1; + } + if (read_envelope(&xxi->pei, &env, f) < 0) { + return -1; + } + if (read_envelope(&xxi->fei, &env, f) < 0) { + return -1; + } + + if (xxi->pei.flg & XMP_ENVELOPE_ON) { + for (j = 0; j < xxi->pei.npt; j++) + xxi->pei.data[j * 2 + 1] += 32; + } + + if (xxi->aei.flg & XMP_ENVELOPE_ON && xxi->aei.npt == 0) { + xxi->aei.npt = 1; + } + if (xxi->pei.flg & XMP_ENVELOPE_ON && xxi->pei.npt == 0) { + xxi->pei.npt = 1; + } + if (xxi->fei.flg & XMP_ENVELOPE_ON && xxi->fei.npt == 0) { + xxi->fei.npt = 1; + } + + if (env.flg & IT_ENV_FILTER) { + xxi->fei.flg |= XMP_ENVELOPE_FLT; + for (j = 0; j < env.num; j++) { + xxi->fei.data[j * 2 + 1] += 32; + xxi->fei.data[j * 2 + 1] *= 4; + } + } else { + /* Pitch envelope is *50 to get fine interpolation */ + for (j = 0; j < env.num; j++) + xxi->fei.data[j * 2 + 1] *= 50; + } + + /* See how many different instruments we have */ + for (j = 0; j < 120; j++) + inst_map[j] = -1; + + for (k = j = 0; j < 120; j++) { + c = i2h.keys[j * 2 + 1] - 1; + if (c < 0 || c >= 120) { + xxi->map[j].ins = 0xff; /* No sample */ + xxi->map[j].xpo = 0; + continue; + } + if (inst_map[c] == -1) { + inst_map[c] = k; + inst_rmap[k] = c; + k++; + } + xxi->map[j].ins = inst_map[c]; + xxi->map[j].xpo = i2h.keys[j * 2] - j; + } + + xxi->nsm = k; + xxi->vol = i2h.gbv >> 1; + + if (k) { + xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) + return -1; + + for (j = 0; j < k; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = inst_rmap[j]; + sub->nna = i2h.nna; + sub->dct = i2h.dct; + sub->dca = dca2nna[i2h.dca]; + sub->pan = i2h.dfp & 0x80 ? -1 : i2h.dfp * 4; + sub->ifc = i2h.ifc; + sub->ifr = i2h.ifr; + sub->rvv = ((int)i2h.rp << 8) | i2h.rv; + } + } + + D_(D_INFO "[ ] %-26.26s %d %d %d %4d %4d %2x " + "%02x %c%c%c %3d %02x %02x", + /*i,*/ i2h.name, + i2h.nna, i2h.dct, i2h.dca, + i2h.fadeout, + i2h.gbv, + i2h.dfp & 0x80 ? 0x80 : i2h.dfp * 4, + i2h.rv, + xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-', + xxi->pei.flg & XMP_ENVELOPE_ON ? 'P' : '-', + env.flg & 0x01 ? env.flg & 0x80 ? 'F' : 'P' : '-', + xxi->nsm, i2h.ifc, i2h.ifr); + + return 0; +} + +static void force_sample_length(struct xmp_sample *xxs, struct extra_sample_data *xtra, int len) +{ + xxs->len = len; + + if (xxs->lpe > xxs->len) + xxs->lpe = xxs->len; + + if (xxs->lps >= xxs->len) + xxs->flg &= ~XMP_SAMPLE_LOOP; + + if (xtra) { + if (xtra->sue > xxs->len) + xtra->sue = xxs->len; + + if(xtra->sus >= xxs->len) + xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR); + } +} + +static int load_it_sample(struct module_data *m, int i, int start, + int sample_mode, HIO_HANDLE *f) +{ + struct it_sample_header ish; + struct xmp_module *mod = &m->mod; + struct extra_sample_data *xtra; + struct xmp_sample *xxs; + int j, k; + uint8 buf[80]; + + if (sample_mode) { + mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); + if (mod->xxi[i].sub == NULL) { + return -1; + } + } + + if (hio_read(buf, 1, 80, f) != 80) { + return -1; + } + + ish.magic = readmem32b(buf); + /* Changed to continue to allow use-brdg.it and use-funk.it to + * load correctly (both IT 2.04) + */ + if (ish.magic != MAGIC_IMPS) { + return 0; + } + + xxs = &mod->xxs[i]; + xtra = &m->xtra[i]; + + memcpy(ish.dosname, buf + 4, 12); + ish.zero = buf[16]; + ish.gvl = buf[17]; + ish.flags = buf[18]; + ish.vol = buf[19]; + + memcpy(ish.name, buf + 20, 26); + fix_name(ish.name, 26); + + ish.convert = buf[46]; + ish.dfp = buf[47]; + ish.length = readmem32l(buf + 48); + ish.loopbeg = readmem32l(buf + 52); + ish.loopend = readmem32l(buf + 56); + ish.c5spd = readmem32l(buf + 60); + ish.sloopbeg = readmem32l(buf + 64); + ish.sloopend = readmem32l(buf + 68); + ish.sample_ptr = readmem32l(buf + 72); + ish.vis = buf[76]; + ish.vid = buf[77]; + ish.vir = buf[78]; + ish.vit = buf[79]; + + if (ish.flags & IT_SMP_16BIT) { + xxs->flg = XMP_SAMPLE_16BIT; + } + xxs->len = ish.length; + + xxs->lps = ish.loopbeg; + xxs->lpe = ish.loopend; + xxs->flg |= ish.flags & IT_SMP_LOOP ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= ish.flags & IT_SMP_BLOOP ? XMP_SAMPLE_LOOP_BIDIR : 0; + xxs->flg |= ish.flags & IT_SMP_SLOOP ? XMP_SAMPLE_SLOOP : 0; + xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0; + + if (ish.flags & IT_SMP_SLOOP) { + xtra->sus = ish.sloopbeg; + xtra->sue = ish.sloopend; + } + + if (sample_mode) { + /* Create an instrument for each sample */ + mod->xxi[i].vol = 64; + mod->xxi[i].sub[0].vol = ish.vol; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].nsm = !!(xxs->len); + libxmp_instrument_name(mod, i, ish.name, 25); + } else { + libxmp_copy_adjust(xxs->name, ish.name, 25); + } + + D_(D_INFO "\n[%2X] %-26.26s %05x%c%05x %05x %05x %05x " + "%02x%02x %02x%02x %5d ", + i, sample_mode ? xxs->name : mod->xxs[i].name, + xxs->len, + ish.flags & IT_SMP_16BIT ? '+' : ' ', + MIN(xxs->lps, 0xfffff), MIN(xxs->lpe, 0xfffff), + MIN(ish.sloopbeg, 0xfffff), MIN(ish.sloopend, 0xfffff), + ish.flags, ish.convert, ish.vol, ish.gvl, ish.c5spd); + + /* Convert C5SPD to relnote/finetune + * + * In IT we can have a sample associated with two or more + * instruments, but c5spd is a sample attribute -- so we must + * scan all xmp instruments to set the correct transposition + */ + + for (j = 0; j < mod->ins; j++) { + for (k = 0; k < mod->xxi[j].nsm; k++) { + struct xmp_subinstrument *sub = &mod->xxi[j].sub[k]; + if (sub->sid == i) { + sub->vol = ish.vol; + sub->gvl = ish.gvl; + sub->vra = ish.vis; /* sample to sub-instrument vibrato */ + sub->vde = ish.vid << 1; + sub->vwf = ish.vit; + sub->vsw = (0xff - ish.vir) >> 1; + + libxmp_c2spd_to_note(ish.c5spd, + &mod->xxi[j].sub[k].xpo, + &mod->xxi[j].sub[k].fin); + + /* Set sample pan (overrides subinstrument) */ + if (ish.dfp & 0x80) { + sub->pan = (ish.dfp & 0x7f) * 4; + } else if (sample_mode) { + sub->pan = -1; + } + } + } + } + + if (ish.flags & IT_SMP_SAMPLE && xxs->len > 1) { + int cvt = 0; + + /* Sanity check - some modules may have invalid sizes on + * unused samples so only check this if the sample flag is set. */ + if (xxs->len > MAX_SAMPLE_SIZE) { + return -1; + } + + if (0 != hio_seek(f, start + ish.sample_ptr, SEEK_SET)) + return -1; + + if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe) + xxs->flg &= ~XMP_SAMPLE_LOOP; + + if (ish.convert == IT_CVT_ADPCM) + cvt |= SAMPLE_FLAG_ADPCM; + + if (~ish.convert & IT_CVT_SIGNED) + cvt |= SAMPLE_FLAG_UNS; + + /* compressed samples */ + if (ish.flags & IT_SMP_COMP) { + long min_size, file_len, left; + void *decbuf; + int ret; + + /* Sanity check - the lower bound on IT compressed + * sample size (in bytes) is a little over 1/8th of the + * number of SAMPLES in the sample. + */ + file_len = hio_size(f); + min_size = xxs->len >> 3; + left = file_len - (long)ish.sample_ptr; + /* No data to read at all? Just skip it... */ + if (left <= 0) + return 0; + + if ((file_len > 0) && (left < min_size)) { + D_(D_WARN "sample %X failed minimum size check " + "(len=%d, needs >=%ld bytes, %ld available): " + "resizing to %ld", + i, xxs->len, min_size, left, left << 3); + + force_sample_length(xxs, xtra, left << 3); + } + + decbuf = (uint8 *) calloc(1, xxs->len * 2); + if (decbuf == NULL) + return -1; + + if (ish.flags & IT_SMP_16BIT) { + itsex_decompress16(f, (int16 *)decbuf, xxs->len, + ish.convert & IT_CVT_DIFF); + +#ifdef WORDS_BIGENDIAN + /* decompression generates native-endian + * samples, but we want little-endian + */ + cvt |= SAMPLE_FLAG_BIGEND; +#endif + } else { + itsex_decompress8(f, (uint8 *)decbuf, xxs->len, + ish.convert & IT_CVT_DIFF); + } + + ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt, + &mod->xxs[i], decbuf); + if (ret < 0) { + free(decbuf); + return -1; + } + + free(decbuf); + } else { + if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0) + return -1; + } + } + + return 0; +} + +static int load_it_pattern(struct module_data *m, int i, int new_fx, + HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy, lastevent[L_CHANNELS]; + uint8 mask[L_CHANNELS]; + uint8 last_fxp[64]; + + int r, c, pat_len, num_rows; + uint8 b; + + r = 0; + + memset(last_fxp, 0, sizeof(last_fxp)); + memset(lastevent, 0, L_CHANNELS * sizeof(struct xmp_event)); + memset(&dummy, 0, sizeof(struct xmp_event)); + + pat_len = hio_read16l(f) /* - 4 */ ; + mod->xxp[i]->rows = num_rows = hio_read16l(f); + + if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) { + return -1; + } + + memset(mask, 0, L_CHANNELS); + hio_read16l(f); + hio_read16l(f); + + while (r < num_rows && --pat_len >= 0) { + b = hio_read8(f); + if (hio_error(f)) { + return -1; + } + if (!b) { + r++; + continue; + } + c = (b - 1) & 63; + + if (b & 0x80) { + mask[c] = hio_read8(f); + pat_len--; + } + /* + * WARNING: we IGNORE events in disabled channels. Disabled + * channels should be muted only, but we don't know the + * real number of channels before loading the patterns and + * we don't want to set it to 64 channels. + */ + if (c >= mod->chn) { + event = &dummy; + } else { + event = &EVENT(i, c, r); + } + + if (mask[c] & 0x01) { + b = hio_read8(f); + + /* From ittech.txt: + * Note ranges from 0->119 (C-0 -> B-9) + * 255 = note off, 254 = notecut + * Others = note fade (already programmed into IT's player + * but not available in the editor) + */ + switch (b) { + case 0xff: /* key off */ + b = XMP_KEY_OFF; + break; + case 0xfe: /* cut */ + b = XMP_KEY_CUT; + break; + default: + if (b > 119) { /* fade */ + b = XMP_KEY_FADE; + } else { + b++; /* note */ + } + } + lastevent[c].note = event->note = b; + pat_len--; + } + if (mask[c] & 0x02) { + b = hio_read8(f); + lastevent[c].ins = event->ins = b; + pat_len--; + } + if (mask[c] & 0x04) { + b = hio_read8(f); + lastevent[c].vol = event->vol = b; + xlat_volfx(event); + pat_len--; + } + if (mask[c] & 0x08) { + b = hio_read8(f); + if (b >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %#02x", b); + hio_read8(f); + + } else { + event->fxt = b; + event->fxp = hio_read8(f); + + xlat_fx(c, event, last_fxp, new_fx); + lastevent[c].fxt = event->fxt; + lastevent[c].fxp = event->fxp; + } + pat_len -= 2; + } + if (mask[c] & 0x10) { + event->note = lastevent[c].note; + } + if (mask[c] & 0x20) { + event->ins = lastevent[c].ins; + } + if (mask[c] & 0x40) { + event->vol = lastevent[c].vol; + xlat_volfx(event); + } + if (mask[c] & 0x80) { + event->fxt = lastevent[c].fxt; + event->fxp = lastevent[c].fxp; + } + } + + return 0; +} + +static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, i, j; + struct it_file_header ifh; + int max_ch; + uint32 *pp_ins; /* Pointers to instruments */ + uint32 *pp_smp; /* Pointers to samples */ + uint32 *pp_pat; /* Pointers to patterns */ + int new_fx, sample_mode; + int pat_before_smp = 0; + int is_mpt_116 = 0; + + LOAD_INIT(); + + /* Load and convert header */ + ifh.magic = hio_read32b(f); + if (ifh.magic != MAGIC_IMPM) { + return -1; + } + + hio_read(ifh.name, 26, 1, f); + ifh.hilite_min = hio_read8(f); + ifh.hilite_maj = hio_read8(f); + + ifh.ordnum = hio_read16l(f); + ifh.insnum = hio_read16l(f); + ifh.smpnum = hio_read16l(f); + ifh.patnum = hio_read16l(f); + + ifh.cwt = hio_read16l(f); + ifh.cmwt = hio_read16l(f); + ifh.flags = hio_read16l(f); + ifh.special = hio_read16l(f); + + ifh.gv = hio_read8(f); + ifh.mv = hio_read8(f); + ifh.is = hio_read8(f); + ifh.it = hio_read8(f); + ifh.sep = hio_read8(f); + ifh.pwd = hio_read8(f); + + /* Sanity check */ + if (ifh.gv > 0x80) { + D_(D_CRIT "invalid gv (%u)", ifh.gv); + goto err; + } + + ifh.msglen = hio_read16l(f); + ifh.msgofs = hio_read32l(f); + ifh.rsvd = hio_read32l(f); + + hio_read(ifh.chpan, 64, 1, f); + hio_read(ifh.chvol, 64, 1, f); + + if (hio_error(f)) { + D_(D_CRIT "error reading IT header"); + goto err; + } + + memcpy(mod->name, ifh.name, sizeof(ifh.name)); + /* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */ + mod->name[sizeof(ifh.name)] = '\0'; + mod->len = ifh.ordnum; + mod->ins = ifh.insnum; + mod->smp = ifh.smpnum; + mod->pat = ifh.patnum; + + /* Sanity check */ + if (mod->ins > 255 || mod->smp > 255 || mod->pat > 255) { + D_(D_CRIT "invalid ins (%u), smp (%u), or pat (%u)", + mod->ins, mod->smp, mod->pat); + goto err; + } + + if (mod->ins) { + pp_ins = (uint32 *) calloc(4, mod->ins); + if (pp_ins == NULL) + goto err; + } else { + pp_ins = NULL; + } + + pp_smp = (uint32 *) calloc(4, mod->smp); + if (pp_smp == NULL) + goto err2; + + pp_pat = (uint32 *) calloc(4, mod->pat); + if (pp_pat == NULL) + goto err3; + + mod->spd = ifh.is; + mod->bpm = ifh.it; + + sample_mode = ~ifh.flags & IT_USE_INST; + + if (ifh.flags & IT_LINEAR_FREQ) { + m->period_type = PERIOD_LINEAR; + } + + for (i = 0; i < 64; i++) { + struct xmp_channel *xxc = &mod->xxc[i]; + + if (ifh.chpan[i] == 100) { /* Surround -> center */ + xxc->flg |= XMP_CHANNEL_SURROUND; + } + + if (ifh.chpan[i] & 0x80) { /* Channel mute */ + xxc->flg |= XMP_CHANNEL_MUTE; + } + + if (ifh.flags & IT_STEREO) { + xxc->pan = (int)ifh.chpan[i] * 0x80 >> 5; + if (xxc->pan > 0xff) + xxc->pan = 0xff; + } else { + xxc->pan = 0x80; + } + + xxc->vol = ifh.chvol[i]; + } + + if (mod->len <= XMP_MAX_MOD_LENGTH) { + hio_read(mod->xxo, 1, mod->len, f); + } else { + hio_read(mod->xxo, 1, XMP_MAX_MOD_LENGTH, f); + hio_seek(f, mod->len - XMP_MAX_MOD_LENGTH, SEEK_CUR); + mod->len = XMP_MAX_MOD_LENGTH; + } + + new_fx = ifh.flags & IT_OLD_FX ? 0 : 1; + + for (i = 0; i < mod->ins; i++) + pp_ins[i] = hio_read32l(f); + for (i = 0; i < mod->smp; i++) + pp_smp[i] = hio_read32l(f); + for (i = 0; i < mod->pat; i++) + pp_pat[i] = hio_read32l(f); + + if ((ifh.flags & IT_MIDI_CONFIG) || (ifh.special & IT_SPEC_MIDICFG)) { + /* Skip edit history if it exists. */ + if (ifh.special & IT_EDIT_HISTORY) { + int skip = hio_read16l(f) * 8; + if (hio_error(f) || (skip && hio_seek(f, skip, SEEK_CUR) < 0)) + goto err4; + } + if (load_it_midi_config(m, f) < 0) + goto err4; + } + if (mod->smp && mod->pat && pp_pat[0] != 0 && pp_pat[0] < pp_smp[0]) + pat_before_smp = 1; + + m->c4rate = C4_NTSC_RATE; + + identify_tracker(m, &ifh, pat_before_smp, &is_mpt_116); + + MODULE_INFO(); + + D_(D_INFO "Instrument/FX mode: %s/%s", + sample_mode ? "sample" : ifh.cmwt >= 0x200 ? + "new" : "old", ifh.flags & IT_OLD_FX ? "old" : "IT"); + + if (sample_mode) + mod->ins = mod->smp; + + if (libxmp_init_instrument(m) < 0) + goto err4; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + /* + * IT files can have three different instrument types: 'New' + * instruments, 'old' instruments or just samples. We need a + * different loader for each of them. + */ + + struct xmp_instrument *xxi = &mod->xxi[i]; + + if (!sample_mode && ifh.cmwt >= 0x200) { + /* New instrument format */ + if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_new_it_instrument(xxi, f) < 0) { + goto err4; + } + + } else if (!sample_mode) { + /* Old instrument format */ + if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_old_it_instrument(xxi, f) < 0) { + goto err4; + } + } + } + + D_(D_INFO "Stored Samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + + if (hio_seek(f, start + pp_smp[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_it_sample(m, i, start, sample_mode, f) < 0) { + goto err4; + } + } + /* Reset any error status set by truncated samples. */ + hio_error(f); + + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* Effects in muted channels are processed, so scan patterns first to + * see the real number of channels + */ + max_ch = 0; + for (i = 0; i < mod->pat; i++) { + uint8 mask[L_CHANNELS]; + int pat_len, num_rows, row; + + /* If the offset to a pattern is 0, the pattern is empty */ + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + pp_pat[i], SEEK_SET); + pat_len = hio_read16l(f) /* - 4 */ ; + num_rows = hio_read16l(f); + memset(mask, 0, L_CHANNELS); + hio_read16l(f); + hio_read16l(f); + + /* Sanity check: + * - Impulse Tracker and Schism Tracker allow up to 200 rows. + * - ModPlug Tracker 1.16 allows 256 rows. + * - OpenMPT allows 1024 rows. + */ + if (num_rows > 1024) { + D_(D_WARN "skipping pattern %d (%d rows)", i, num_rows); + pp_pat[i] = 0; + continue; + } + + row = 0; + while (row < num_rows && --pat_len >= 0) { + int b = hio_read8(f); + if (hio_error(f)) { + D_(D_CRIT "error scanning pattern %d", i); + goto err4; + } + if (b == 0) { + row++; + continue; + } + + c = (b - 1) & 63; + + if (c > max_ch) + max_ch = c; + + if (b & 0x80) { + mask[c] = hio_read8(f); + pat_len--; + } + + if (mask[c] & 0x01) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x02) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x04) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x08) { + hio_read8(f); + hio_read8(f); + pat_len -= 2; + } + } + } + + /* Set the number of channels actually used + */ + mod->chn = max_ch + 1; + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) { + goto err4; + } + + /* Read patterns */ + for (i = 0; i < mod->pat; i++) { + + if (libxmp_alloc_pattern(mod, i) < 0) { + goto err4; + } + + /* If the offset to a pattern is 0, the pattern is empty */ + if (pp_pat[i] == 0) { + mod->xxp[i]->rows = 64; + for (j = 0; j < mod->chn; j++) { + int tnum = i * mod->chn + j; + if (libxmp_alloc_track(mod, tnum, 64) < 0) + goto err4; + mod->xxp[i]->index[j] = tnum; + } + continue; + } + + if (hio_seek(f, start + pp_pat[i], SEEK_SET) < 0) { + D_(D_CRIT "error seeking to %d", start + pp_pat[i]); + goto err4; + } + + if (load_it_pattern(m, i, new_fx, f) < 0) { + D_(D_CRIT "error loading pattern %d", i); + goto err4; + } + } + + free(pp_pat); + free(pp_smp); + free(pp_ins); + + /* Song message */ + if (ifh.special & IT_HAS_MSG) { + if ((m->comment = (char *)malloc(ifh.msglen)) != NULL) { + hio_seek(f, start + ifh.msgofs, SEEK_SET); + + D_(D_INFO "Message length : %d", ifh.msglen); + + for (j = 0; j + 1 < ifh.msglen; j++) { + int b = hio_read8(f); + if (b == '\r') { + b = '\n'; + } else if ((b < 32 || b > 127) && b != '\n' + && b != '\t') { + b = '.'; + } + m->comment[j] = b; + } + + if (ifh.msglen > 0) { + m->comment[j] = 0; + } + } + } + + /* Format quirks */ + + m->quirk |= QUIRKS_IT | QUIRK_ARPMEM | QUIRK_INSVOL; + + if (ifh.flags & IT_LINK_GXX) { + m->quirk |= QUIRK_PRENV; + } else { + m->quirk |= QUIRK_UNISLD; + } + + if (new_fx) { + m->quirk |= QUIRK_VIBHALF | QUIRK_VIBINV; + } else { + m->quirk &= ~QUIRK_VIBALL; + m->quirk |= QUIRK_ITOLDFX; + } + + if (sample_mode) { + m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN); + } + + m->gvolbase = 0x80; + m->gvol = ifh.gv; + m->mvolbase = 48; + m->mvol = ifh.mv; + m->read_event_type = READ_EVENT_IT; + +#ifndef LIBXMP_CORE_PLAYER + if (is_mpt_116) + libxmp_apply_mpt_preamp(m); +#endif + + return 0; + +err4: + free(pp_pat); +err3: + free(pp_smp); +err2: + free(pp_ins); +err: + return -1; +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ diff --git a/thirdparty/libxmp/src/loaders/itsex.c b/thirdparty/libxmp/src/loaders/itsex.c new file mode 100644 index 0000000..a56dd4f --- /dev/null +++ b/thirdparty/libxmp/src/loaders/itsex.c @@ -0,0 +1,243 @@ +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Public domain IT sample decompressor by Olivier Lapicque */ + +#include "loader.h" +#include "it.h" + +static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n, int *err) +{ + uint32 retval = 0; + int i = n; + int bnum = *bitnum; + uint32 bbuf = *bitbuf; + + if (n > 0 && n <= 32) { + do { + if (bnum == 0) { + if (hio_eof(ibuf)) { + *err = EOF; + return 0; + } + bbuf = hio_read8(ibuf); + bnum = 8; + } + retval >>= 1; + retval |= bbuf << 31; + bbuf >>= 1; + bnum--; + i--; + } while (i != 0); + + i = n; + + *bitnum = bnum; + *bitbuf = bbuf; + } else { + /* Invalid shift value. */ + *err = -2; + return 0; + } + + return (retval >> (32 - i)); +} + + +int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215) +{ + /* uint32 size = 0; */ + uint32 block_count = 0; + uint32 bitbuf = 0; + int bitnum = 0; + uint8 left = 0, temp = 0, temp2 = 0; + uint32 d, pos; + int err = 0; + + while (len) { + if (!block_count) { + block_count = 0x8000; + /*size =*/ hio_read16l(src); + left = 9; + temp = temp2 = 0; + bitbuf = bitnum = 0; + } + + d = block_count; + if (d > len) + d = len; + + /* Unpacking */ + pos = 0; + do { + uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err); + if (err != 0) + return -1; + + if (left < 7) { + uint32 i = 1 << (left - 1); + uint32 j = bits & 0xffff; + if (i != j) + goto unpack_byte; + bits = (read_bits(src, &bitbuf, &bitnum, 3, &err) + + 1) & 0xff; + if (err != 0) + return -1; + + left = ((uint8)bits < left) ? (uint8)bits : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left < 9) { + uint16 i = (0xff >> (9 - left)) + 4; + uint16 j = i - 8; + + if ((bits <= j) || (bits > i)) + goto unpack_byte; + + bits -= j; + left = ((uint8)(bits & 0xff) < left) ? + (uint8)(bits & 0xff) : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left >= 10) + goto skip_byte; + + if (bits >= 256) { + left = (uint8) (bits + 1) & 0xff; + goto next; + } + + unpack_byte: + if (left < 8) { + uint8 shift = 8 - left; + signed char c = (signed char)(bits << shift); + c >>= shift; + bits = (uint16) c; + } + bits += temp; + temp = (uint8)bits; + temp2 += temp; + dst[pos] = it215 ? temp2 : temp; + + skip_byte: + pos++; + + next: + /* if (slen <= 0) + return -1 */; + } while (pos < d); + + /* Move On */ + block_count -= d; + len -= d; + dst += d; + } + + return 0; +} + +int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215) +{ + /* uint32 size = 0; */ + uint32 block_count = 0; + uint32 bitbuf = 0; + int bitnum = 0; + uint8 left = 0; + int16 temp = 0, temp2 = 0; + uint32 d, pos; + int err = 0; + + while (len) { + if (!block_count) { + block_count = 0x4000; + /*size =*/ hio_read16l(src); + left = 17; + temp = temp2 = 0; + bitbuf = bitnum = 0; + } + + d = block_count; + if (d > len) + d = len; + + /* Unpacking */ + pos = 0; + do { + uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err); + if (err != 0) + return -1; + + if (left < 7) { + uint32 i = 1 << (left - 1); + uint32 j = bits; + + if (i != j) + goto unpack_byte; + + bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1; + if (err != 0) + return -1; + + left = ((uint8)(bits & 0xff) < left) ? + (uint8)(bits & 0xff) : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left < 17) { + uint32 i = (0xffff >> (17 - left)) + 8; + uint32 j = (i - 16) & 0xffff; + + if ((bits <= j) || (bits > (i & 0xffff))) + goto unpack_byte; + + bits -= j; + left = ((uint8)(bits & 0xff) < left) ? + (uint8)(bits & 0xff) : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left >= 18) + goto skip_byte; + + if (bits >= 0x10000) { + left = (uint8)(bits + 1) & 0xff; + goto next; + } + + unpack_byte: + if (left < 16) { + uint8 shift = 16 - left; + int16 c = (int16)(bits << shift); + c >>= shift; + bits = (uint32) c; + } + bits += temp; + temp = (int16)bits; + temp2 += temp; + dst[pos] = (it215) ? temp2 : temp; + + skip_byte: + pos++; + + next: + /* if (slen <= 0) + return -1 */; + } while (pos < d); + + /* Move On */ + block_count -= d; + len -= d; + dst += d; + if (len <= 0) + break; + } + + return 0; +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ diff --git a/thirdparty/libxmp/src/loaders/liq_load.c b/thirdparty/libxmp/src/loaders/liq_load.c new file mode 100644 index 0000000..5c2cf23 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/liq_load.c @@ -0,0 +1,657 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Liquid Tracker module loader based on the format description written + * by Nir Oren. Tested with Shell.liq sent by Adi Sapir. + */ + +#include "loader.h" +#include "../period.h" + + +struct liq_header { + uint8 magic[14]; /* "Liquid Module:" */ + uint8 name[30]; /* ASCIIZ module name */ + uint8 author[20]; /* Author name */ + uint8 _0x1a; /* 0x1a */ + uint8 tracker[20]; /* Tracker name */ + uint16 version; /* Format version */ + uint16 speed; /* Initial speed */ + uint16 bpm; /* Initial bpm */ + uint16 low; /* Lowest note (Amiga Period*4) */ + uint16 high; /* Uppest note (Amiga Period*4) */ + uint16 chn; /* Number of channels */ + uint32 flags; /* Module flags */ + uint16 pat; /* Number of patterns saved */ + uint16 ins; /* Number of instruments */ + uint16 len; /* Module length */ + uint16 hdrsz; /* Header size */ +}; + +struct liq_instrument { +#if 0 + uint8 magic[4]; /* 'L', 'D', 'S', 'S' */ +#endif + uint16 version; /* LDSS header version */ + uint8 name[30]; /* Instrument name */ + uint8 editor[20]; /* Generator name */ + uint8 author[20]; /* Author name */ + uint8 hw_id; /* Hardware used to record the sample */ + uint32 length; /* Sample length */ + uint32 loopstart; /* Sample loop start */ + uint32 loopend; /* Sample loop end */ + uint32 c2spd; /* C2SPD */ + uint8 vol; /* Volume */ + uint8 flags; /* Flags */ + uint8 pan; /* Pan */ + uint8 midi_ins; /* General MIDI instrument */ + uint8 gvl; /* Global volume */ + uint8 chord; /* Chord type */ + uint16 hdrsz; /* LDSS header size */ + uint16 comp; /* Compression algorithm */ + uint32 crc; /* CRC */ + uint8 midi_ch; /* MIDI channel */ + uint8 rsvd[11]; /* Reserved */ + uint8 filename[25]; /* DOS file name */ +}; + +struct liq_pattern { +#if 0 + uint8 magic[4]; /* 'L', 'P', 0, 0 */ +#endif + uint8 name[30]; /* ASCIIZ pattern name */ + uint16 rows; /* Number of rows */ + uint32 size; /* Size of packed pattern */ + uint32 reserved; /* Reserved */ +}; + + +static int liq_test (HIO_HANDLE *, char *, const int); +static int liq_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_liq = { + "Liquid Tracker", + liq_test, + liq_load +}; + +static int liq_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[15]; + + if (hio_read(buf, 1, 14, f) < 14) + return -1; + + if (memcmp(buf, "Liquid Module:", 14)) + return -1; + + libxmp_read_title(f, t, 30); + + return 0; +} + + +#define NONE 0xff + + +static const uint8 fx[25] = { + FX_ARPEGGIO, + FX_S3M_BPM, + FX_BREAK, + FX_PORTA_DN, + NONE, + FX_FINE_VIBRATO, + NONE, + NONE, + NONE, + FX_JUMP, + NONE, + FX_VOLSLIDE, + FX_EXTENDED, + FX_TONEPORTA, + FX_OFFSET, + NONE, /* FIXME: Pan */ + NONE, + NONE, /*FX_MULTI_RETRIG,*/ + FX_S3M_SPEED, + FX_TREMOLO, + FX_PORTA_UP, + FX_VIBRATO, + NONE, + FX_TONE_VSLIDE, + FX_VIBRA_VSLIDE +}; + + +/* Effect translation */ +static void xlat_fx(int c, struct xmp_event *e) +{ + uint8 h = MSN (e->fxp), l = LSN (e->fxp); + + if (e->fxt >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %#02x", e->fxt); + e->fxt = e->fxp = 0; + return; + } + + switch (e->fxt = fx[e->fxt]) { + case FX_EXTENDED: /* Extended effects */ + switch (h) { + case 0x3: /* Glissando */ + e->fxp = l | (EX_GLISS << 4); + break; + case 0x4: /* Vibrato wave */ + if (l == 3) + l++; + e->fxp = l | (EX_VIBRATO_WF << 4); + break; + case 0x5: /* Finetune */ + e->fxp = l | (EX_FINETUNE << 4); + break; + case 0x6: /* Pattern loop */ + e->fxp = l | (EX_PATTERN_LOOP << 4); + break; + case 0x7: /* Tremolo wave */ + if (l == 3) + l++; + e->fxp = l | (EX_TREMOLO_WF << 4); + break; + case 0xc: /* Cut */ + e->fxp = l | (EX_CUT << 4); + break; + case 0xd: /* Delay */ + e->fxp = l | (EX_DELAY << 4); + break; + case 0xe: /* Pattern delay */ + e->fxp = l | (EX_PATT_DELAY << 4); + break; + default: /* Ignore */ + e->fxt = e->fxp = 0; + break; + } + break; + case NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + + +static int decode_event(uint8 x1, struct xmp_event *event, HIO_HANDLE *f) +{ + uint8 x2; + + memset (event, 0, sizeof (struct xmp_event)); + + if (x1 & 0x01) { + x2 = hio_read8(f); + if (x2 == 0xfe) + event->note = XMP_KEY_OFF; + else + event->note = x2 + 1 + 36; + } + + if (x1 & 0x02) + event->ins = hio_read8(f) + 1; + + if (x1 & 0x04) + event->vol = hio_read8(f); + + if (x1 & 0x08) + event->fxt = hio_read8(f) - 'A'; + + if (x1 & 0x10) + event->fxp = hio_read8(f); + + D_(D_INFO " event: %02x %02x %02x %02x %02x", + event->note, event->ins, event->vol, event->fxt, event->fxp); + + /* Sanity check */ + if (event->note > 107 && event->note != XMP_KEY_OFF) + return -1; + + if (event->ins > 100 || event->vol > 64 || event->fxt > 26) + return -1; + + return 0; +} + +static int liq_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i; + struct xmp_event *event = NULL; + struct liq_header lh; + struct liq_instrument li; + struct liq_pattern lp; + uint8 x1, x2; + uint32 pmag; + char tracker_name[21]; + + LOAD_INIT(); + + hio_read(lh.magic, 14, 1, f); + hio_read(lh.name, 30, 1, f); + hio_read(lh.author, 20, 1, f); + hio_read8(f); + hio_read(lh.tracker, 20, 1, f); + + lh.version = hio_read16l(f); + lh.speed = hio_read16l(f); + lh.bpm = hio_read16l(f); + lh.low = hio_read16l(f); + lh.high = hio_read16l(f); + lh.chn = hio_read16l(f); + lh.flags = hio_read32l(f); + lh.pat = hio_read16l(f); + lh.ins = hio_read16l(f); + lh.len = hio_read16l(f); + lh.hdrsz = hio_read16l(f); + + /* Sanity check */ + if (lh.chn > XMP_MAX_CHANNELS || lh.pat > 256 || lh.ins > 256) { + return -1; + } + + if ((lh.version >> 8) == 0) { + lh.hdrsz = lh.len; + lh.len = 0; + hio_seek(f, -2, SEEK_CUR); + } + + if (lh.len > 256) { + return -1; + } + + mod->spd = lh.speed; + mod->bpm = MIN(lh.bpm, 255); + mod->chn = lh.chn; + mod->pat = lh.pat; + mod->ins = mod->smp = lh.ins; + mod->len = lh.len; + mod->trk = mod->chn * mod->pat; + + m->quirk |= QUIRK_INSVOL; + + strncpy(mod->name, (char *)lh.name, 30); + strncpy(tracker_name, (char *)lh.tracker, 20); + /* strncpy(m->author, (char *)lh.author, 20); */ + tracker_name[20] = 0; + for (i = 20; i >= 0; i--) { + if (tracker_name[i] == 0x20) + tracker_name[i] = 0; + if (tracker_name[i]) + break; + } + snprintf(mod->type, XMP_NAME_SIZE, "%s LIQ %d.%02d", + tracker_name, lh.version >> 8, lh.version & 0x00ff); + + if (lh.version > 0) { + for (i = 0; i < mod->chn; i++) { + uint8 pan = hio_read8(f); + + if (pan >= 64) { + if (pan == 64) { + pan = 63; + } else if (pan == 66) { + pan = 31; + mod->xxc[i].flg |= XMP_CHANNEL_SURROUND; + } else { + /* Sanity check */ + return -1; + } + } + + mod->xxc[i].pan = pan << 2; + } + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].vol = hio_read8(f); + + hio_read(mod->xxo, 1, mod->len, f); + + /* Skip 1.01 echo pools */ + hio_seek(f, lh.hdrsz - (0x6d + mod->chn * 2 + mod->len), SEEK_CUR); + } else { + hio_seek(f, start + 0xf0, SEEK_SET); + hio_read (mod->xxo, 1, 256, f); + hio_seek(f, start + lh.hdrsz, SEEK_SET); + + for (i = 0; i < 256; i++) { + if (mod->xxo[i] == 0xff) + break; + } + mod->len = i; + } + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + x1 = x2 = 0; + for (i = 0; i < mod->pat; i++) { + int row, channel, count; + + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + pmag = hio_read32b(f); + if (pmag == 0x21212121) /* !!!! */ + continue; + if (pmag != 0x4c500000) /* LP\0\0 */ + return -1; + + hio_read(lp.name, 30, 1, f); + lp.rows = hio_read16l(f); + lp.size = hio_read32l(f); + lp.reserved = hio_read32l(f); + + /* Sanity check */ + if (lp.rows > 256) { + return -1; + } + + D_(D_INFO "rows: %d size: %d\n", lp.rows, lp.size); + + mod->xxp[i]->rows = lp.rows; + libxmp_alloc_tracks_in_pattern(mod, i); + + row = 0; + channel = 0; + count = hio_tell(f); + +/* + * Packed pattern data is stored full Track after full Track from the left to + * the right (all Intervals in Track and then going Track right). You should + * expect 0C0h on any pattern end, and then your Unpacked Patterndata Pointer + * should be equal to the value in offset [24h]; if it's not, you should exit + * with an error. + */ + +read_event: + /* Sanity check */ + if (i >= mod->pat || channel >= mod->chn || row >= mod->xxp[i]->rows) + return -1; + + event = &EVENT(i, channel, row); + + if (x2) { + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + x2--; + goto next_row; + } + + x1 = hio_read8(f); + +test_event: + /* Sanity check */ + if (i >= mod->pat || channel >= mod->chn || row >= mod->xxp[i]->rows) + return -1; + + event = &EVENT(i, channel, row); + D_(D_INFO "* count=%ld chan=%d row=%d event=%02x", + hio_tell(f) - count, channel, row, x1); + + switch (x1) { + case 0xc0: /* end of pattern */ + D_(D_WARN "- end of pattern"); + if (hio_tell(f) - count != lp.size) + return -1; + goto next_pattern; + case 0xe1: /* skip channels */ + x1 = hio_read8(f); + channel += x1; + D_(D_INFO " [skip %d channels]", x1); + /* fall thru */ + case 0xa0: /* next channel */ + D_(D_INFO " [next channel]"); + channel++; + if (channel >= mod->chn) { + D_(D_CRIT "uh-oh! bad channel number!"); + channel--; + } + row = -1; + goto next_row; + case 0xe0: /* skip rows */ + x1 = hio_read8(f); + D_(D_INFO " [skip %d rows]", x1); + row += x1; + /* fall thru */ + case 0x80: /* next row */ + D_(D_INFO " [next row]"); + goto next_row; + } + + if (x1 > 0xc0 && x1 < 0xe0) { /* packed data */ + D_(D_INFO " [packed data]"); + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + goto next_row; + } + + if (x1 > 0xa0 && x1 < 0xc0) { /* packed data repeat */ + x2 = hio_read8(f); + D_(D_INFO " [packed data - repeat %d times]", x2); + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + goto next_row; + } + + if (x1 > 0x80 && x1 < 0xa0) { /* packed data repeat, keep note */ + x2 = hio_read8(f); + D_(D_INFO " [packed data - repeat %d times, keep note]", x2); + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + while (x2) { + row++; + + /* Sanity check */ + if (row >= lp.rows) + return -1; + + memcpy(&EVENT(i, channel, row), event, sizeof (struct xmp_event)); + x2--; + } + goto next_row; + } + + /* unpacked data */ + D_ (D_INFO " [unpacked data]"); + if (x1 < 0xfe) + event->note = 1 + 36 + x1; + else if (x1 == 0xfe) + event->note = XMP_KEY_OFF; + + x1 = hio_read8(f); + if (x1 > 100) { + row++; + goto test_event; + } + if (x1 != 0xff) + event->ins = x1 + 1; + + x1 = hio_read8(f); + if (x1 != 0xff) + event->vol = x1; + + x1 = hio_read8(f); + if (x1 != 0xff) + event->fxt = x1 - 'A'; + + x1 = hio_read8(f); + event->fxp = x1; + + /* Sanity check */ + if (event->fxt > 24) { + return -1; + } + + xlat_fx(channel, event); + + D_(D_INFO " event: %02x %02x %02x %02x %02x\n", + event->note, event->ins, event->vol, event->fxt, event->fxp); + + /* Sanity check */ + if (event->note > 119 && event->note != XMP_KEY_OFF) + return -1; + + if (event->ins > 100 || event->vol > 65) + return -1; + +next_row: + row++; + if (row >= mod->xxp[i]->rows) { + row = 0; + x2 = 0; + channel++; + } + + /* Sanity check */ + if (channel >= mod->chn) { + channel = 0; + } + + goto read_event; + +next_pattern: + ; + } + + /* Read and convert instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs = &mod->xxs[i]; + unsigned char b[4]; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + if (hio_read(b, 1, 4, f) < 4) + return -1; + + if (b[0] == '?' && b[1] == '?' && b[2] == '?' && b[3] == '?') + continue; + if (b[0] != 'L' || b[1] != 'D' || b[2] != 'S' || b[3] != 'S') + return -1; + + li.version = hio_read16l(f); + hio_read(li.name, 30, 1, f); + hio_read(li.editor, 20, 1, f); + hio_read(li.author, 20, 1, f); + li.hw_id = hio_read8(f); + + li.length = hio_read32l(f); + li.loopstart = hio_read32l(f); + li.loopend = hio_read32l(f); + li.c2spd = hio_read32l(f); + + li.vol = hio_read8(f); + li.flags = hio_read8(f); + li.pan = hio_read8(f); + li.midi_ins = hio_read8(f); + li.gvl = hio_read8(f); + li.chord = hio_read8(f); + + li.hdrsz = hio_read16l(f); + li.comp = hio_read16l(f); + li.crc = hio_read32l(f); + + li.midi_ch = hio_read8(f); + hio_read(li.rsvd, 11, 1, f); + hio_read(li.filename, 25, 1, f); + + /* Sanity check */ + if (hio_error(f)) { + return -1; + } + + xxi->nsm = !!(li.length); + xxi->vol = 0x40; + + xxs->len = li.length; + xxs->lps = li.loopstart; + xxs->lpe = li.loopend; + + if (li.flags & 0x01) { + xxs->flg = XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + if (li.loopend > 0) + xxs->flg = XMP_SAMPLE_LOOP; + + /* FIXME: LDSS 1.0 have global vol == 0 ? */ + /* if (li.gvl == 0) */ + li.gvl = 0x40; + + sub->vol = li.vol; + sub->gvl = li.gvl; + sub->pan = li.pan; + sub->sid = i; + + libxmp_instrument_name(mod, i, li.name, 31); + + D_(D_INFO "[%2X] %-30.30s %05x%c%05x %05x %c %02x %02x %2d.%02d %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', xxs->lps, xxs->lpe, + xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sub->gvl, + li.version >> 8, li.version & 0xff, li.c2spd); + + libxmp_c2spd_to_note(li.c2spd, &sub->xpo, &sub->fin); + hio_seek(f, li.hdrsz - 0x90, SEEK_CUR); + + if (xxs->len == 0) + continue; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + } + + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/loader.h b/thirdparty/libxmp/src/loaders/loader.h new file mode 100644 index 0000000..b5088fc --- /dev/null +++ b/thirdparty/libxmp/src/loaders/loader.h @@ -0,0 +1,74 @@ +#ifndef XMP_LOADER_H +#define XMP_LOADER_H + +#include "../common.h" +#include "../effects.h" +#include "../format.h" +#include "../hio.h" + +/* Sample flags */ +#define SAMPLE_FLAG_DIFF 0x0001 /* Differential */ +#define SAMPLE_FLAG_UNS 0x0002 /* Unsigned */ +#define SAMPLE_FLAG_8BDIFF 0x0004 +#define SAMPLE_FLAG_7BIT 0x0008 +#define SAMPLE_FLAG_NOLOAD 0x0010 /* Get from buffer, don't load */ +#define SAMPLE_FLAG_BIGEND 0x0040 /* Big-endian */ +#define SAMPLE_FLAG_VIDC 0x0080 /* Archimedes VIDC logarithmic */ +/*#define SAMPLE_FLAG_STEREO 0x0100 Interleaved stereo sample */ +#define SAMPLE_FLAG_FULLREP 0x0200 /* Play full sample before looping */ +#define SAMPLE_FLAG_ADLIB 0x1000 /* Adlib synth instrument */ +#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */ +#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */ + +/* libxmp_test_name flags */ +#define TEST_NAME_IGNORE_AFTER_0 0x0001 +#define TEST_NAME_IGNORE_AFTER_CR 0x0002 + +#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100) + +int libxmp_init_instrument (struct module_data *); +int libxmp_realloc_samples (struct module_data *, int); +int libxmp_alloc_subinstrument (struct xmp_module *, int, int); +int libxmp_init_pattern (struct xmp_module *); +int libxmp_alloc_pattern (struct xmp_module *, int); +int libxmp_alloc_track (struct xmp_module *, int, int); +int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int); +int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int); +#ifndef LIBXMP_CORE_PLAYER +int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int); +#endif +char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int); + +char *libxmp_copy_adjust (char *, uint8 *, int); +int libxmp_copy_name_for_fopen (char *, const char *, int); +int libxmp_test_name (const uint8 *, int, int); +void libxmp_read_title (HIO_HANDLE *, char *, int); +void libxmp_set_xxh_defaults (struct xmp_module *); +void libxmp_decode_protracker_event (struct xmp_event *, const uint8 *); +void libxmp_decode_noisetracker_event(struct xmp_event *, const uint8 *); +void libxmp_disable_continue_fx (struct xmp_event *); +int libxmp_check_filename_case (const char *, const char *, char *, int); +void libxmp_get_instrument_path (struct module_data *, char *, int); +void libxmp_set_type (struct module_data *, const char *, ...); +int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int, + struct xmp_sample *, const void *); +void libxmp_free_sample (struct xmp_sample *); +#ifndef LIBXMP_CORE_PLAYER +void libxmp_schism_tracker_string (char *, size_t, int, int); +void libxmp_apply_mpt_preamp (struct module_data *m); +#endif + +extern uint8 libxmp_ord_xlat[]; +extern const int libxmp_arch_vol_table[]; + +#define MAGIC4(a,b,c,d) \ + (((uint32)(a)<<24)|((uint32)(b)<<16)|((uint32)(c)<<8)|(d)) + +#define LOAD_INIT() + +#define MODULE_INFO() do { \ + D_(D_WARN "Module title: \"%s\"", m->mod.name); \ + D_(D_WARN "Module type: %s", m->mod.type); \ +} while (0) + +#endif diff --git a/thirdparty/libxmp/src/loaders/lzw.c b/thirdparty/libxmp/src/loaders/lzw.c new file mode 100644 index 0000000..6e1bd82 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/lzw.c @@ -0,0 +1,439 @@ +/* Extended Module Player + * Copyright (C) 2021-2023 Alice Rowan + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Simple LZW decoder for Digital Symphony. + * This does not handle the hacks required for ARC or UnShrink. */ + +#include "lzw.h" +#include + +/*#define LZW_DEBUG*/ + +#define LZW_NO_CODE ((uint16)-1) +#define LZW_CODE_CLEAR 256 +#define LZW_CODE_SYM_EOF 257 + +struct bitstream +{ + uint32 buf; + size_t num_read; + size_t max_read; + int bits; +}; + +struct LZW_code +{ + uint16 prev; + uint16 length; + uint8 value; +}; + +struct LZW_tree +{ + struct LZW_code *codes; + unsigned int bits; + unsigned int length; + unsigned int maxlength; + unsigned int defaultlength; + unsigned int alloclength; + unsigned int previous_code; + int new_inc; + int flags; + uint8 previous_first_char; +}; + +static void bs_init(struct bitstream *bs, size_t max_read) +{ + bs->buf = 0; + bs->num_read = 0; + bs->max_read = max_read; + bs->bits = 0; +} + +static int bs_read(struct bitstream *bs, HIO_HANDLE *f, int bits) +{ + uint8 byte; + int ret; + + if (bs->bits < bits) { + while (bs->bits < bits) { + if (bs->num_read >= bs->max_read) + return -1; + + byte = hio_read8(f); + bs->buf |= byte << bs->bits; + bs->bits += 8; + bs->num_read++; + } + if (hio_error(f)) + return -1; + } + + ret = bs->buf & ((1 << bits) - 1); + bs->buf >>= bits; + bs->bits -= bits; + return ret; +} + +static int LZW_init_tree(struct LZW_tree *lzw, int flags) +{ + unsigned int maxbits = LZW_FLAG_MAXBITS(flags); + unsigned int i; + + lzw->bits = 9; + if (maxbits < lzw->bits || maxbits > 16) + return -1; + + lzw->defaultlength = 258; /* 256 chars + clear + EOF. */ + lzw->maxlength = 1 << lzw->bits; + lzw->alloclength = 1 << maxbits; + + lzw->codes = (struct LZW_code *)calloc(lzw->alloclength, sizeof(struct LZW_code)); + if (lzw->codes == NULL) + return -1; + + lzw->length = lzw->defaultlength; + lzw->previous_code = LZW_NO_CODE; + lzw->new_inc = 0; + lzw->flags = flags; + lzw->previous_first_char = 0; + + for (i = 0; i < 256; i++) { + lzw->codes[i].length = 1; + lzw->codes[i].value = i; + lzw->codes[i].prev = LZW_NO_CODE; + } + return 0; +} + +static void LZW_free(struct LZW_tree *lzw) +{ + free(lzw->codes); + return; +} + +static void LZW_add(struct LZW_tree *lzw) +{ + struct LZW_code *current; + uint16 prev_length; + + if (lzw->length >= lzw->alloclength) + return; + + current = &(lzw->codes[lzw->length++]); + + /* Increase bitwidth if the NEXT code would be maxlength. */ + if (lzw->length >= lzw->maxlength && lzw->length < lzw->alloclength) { + lzw->maxlength <<= 1; + lzw->bits++; + lzw->new_inc = 1; + #ifdef LZW_DEBUG + printf("I: bitwidth increased to %d\n", lzw->bits); + #endif + } + + current->prev = lzw->previous_code; + current->value = lzw->previous_first_char; + + /* NOTE: when the length cache deadcode below is enabled, this may + * intentionally be set to or overflow to 0, in which case the length + * will be computed as-needed by iterating the tree. */ + prev_length = lzw->codes[lzw->previous_code].length; + current->length = prev_length ? prev_length + 1 : 0; +} + +/** + * Reset the LZW tree length. + */ +static void LZW_clear(struct LZW_tree *lzw) +{ + lzw->bits = 9; + lzw->maxlength = (1 << lzw->bits); + lzw->length = lzw->defaultlength; + lzw->previous_code = LZW_NO_CODE; +#if 0 +{ + int i; + for (i = lzw->defaultlength; i < lzw->alloclength; i++) + lzw->codes[i].length = 0; +} +#endif +} + +/** + * Get the length of an LZW code, or compute it if it isn't currently stored. + * This happens when one or mode codes in the sequence are marked for reuse. + */ +static uint16 LZW_get_length(const struct LZW_tree *lzw, const struct LZW_code *c) +{ +#if 0 + uint16 code; + uint16 length = 1; + + if (c->length) + return c->length; + + do { + /* Shouldn't happen, but... */ + if(length >= lzw->maxlength) + return 0; + + length++; + code = c->prev; + c = &(lzw->codes[code]); + } + while (code >= lzw->defaultlength); + return length; +#endif + return c->length; +} + +/** + * Output an LZW code. + */ +static int LZW_output(struct LZW_tree *lzw, uint16 code, uint8 **_pos, size_t *left) +{ + uint8 *pos = *_pos; + + struct LZW_code *codes = lzw->codes; + struct LZW_code *current = &(codes[code]); + unsigned int length = LZW_get_length(lzw, current); + unsigned int i; + + if (length == 0 || length > *left) + return -1; + + for (i = length - 1; i > 0; i--) { + pos[i] = current->value; + code = current->prev; + current = &(codes[code]); + } + *pos = code; + *_pos += length; + *left -= length; + + lzw->previous_first_char = code; + return 0; +} + +/** + * Decode an LZW code and create the next code from known data. + */ +static int LZW_decode(struct LZW_tree *lzw, uint16 code, uint8 **_pos, size_t *left) +{ + int kwkwk = 0; + int result; + + /* Digital Symphony LZW never seems to reference cleared codes, + * which allows some assumptions to be made (like never clearing the + * cached code lengths). If this decoder needs to support those, the + * cached length handling deadcode above needs to be uncommented. */ + if (code > lzw->length) + return -1; + + /* This is a special case--the current code is the previous code with the + * first character of the previous code appended, and needs to be added + * before the output occurs (instead of after). */ + if (code == lzw->length) { + if (lzw->previous_code == LZW_NO_CODE) + return -1; + + LZW_add(lzw); + lzw->previous_code = code; + kwkwk = 1; + } + + /* Otherwise, output first, and then add a new code, which is the previous + * code with the first character of the current code appended. */ + result = LZW_output(lzw, code, _pos, left); + if (result == 0 && !kwkwk) { + if (lzw->previous_code != LZW_NO_CODE) + LZW_add(lzw); + + lzw->previous_code = code; + } + return result; +} + +int libxmp_read_lzw(void *dest, size_t dest_len, size_t max_read_len, + int flags, HIO_HANDLE *f) +{ + struct LZW_tree lzw; + struct bitstream bs; + + uint8 *start = (uint8 *)dest; + uint8 *pos = start; + size_t left = dest_len; + int result; + int code; + + bs_init(&bs, max_read_len); + if (LZW_init_tree(&lzw, flags) != 0) + return -1; + + #ifdef LZW_DEBUG + printf("S: %zu\n", dest_len); + #endif + + while (left > 0) { + code = bs_read(&bs, f, lzw.bits); + #ifdef LZW_DEBUG + printf(" : %x\n", code); + #endif + if (code < 0) + break; + + if (code == LZW_CODE_CLEAR) { + #ifdef LZW_DEBUG + printf(" : >>> CLEAR <<<\n"); + #endif + LZW_clear(&lzw); + continue; + } else if ((flags & LZW_FLAG_SYMQUIRKS) && code == LZW_CODE_SYM_EOF) { + break; + } + + lzw.new_inc = 0; + result = LZW_decode(&lzw, code, &pos, &left); + if (result) + break; + } + + if (left > 0) { + D_(D_WARN "encountered error in stream or early EOF"); + memset(pos, 0, left); + } else if (flags & LZW_FLAG_SYMQUIRKS) { + /* Digital Symphony - read final EOF code. */ + if (lzw.new_inc) { + /* If the final code prior to EOF should have increased + * the bitwidth, read the EOF with the old bitwidth + * instead of the new one. + * + * This anomaly exists in FULLEFFECT, NARCOSIS and + * NEWDANCE. In NEWDANCE (libxmp's test file for this), + * it occurs specifically in the LZW-compressed sequence. + * https://github.com/libxmp/libxmp/issues/347 + */ + lzw.bits--; + } + + code = bs_read(&bs, f, lzw.bits); + #ifdef LZW_DEBUG + printf("E: %x\n", code); + #endif + if (code < 0) { + D_(D_WARN "missing LZW EOF code!"); + } else if (code != LZW_CODE_SYM_EOF) { + D_(D_WARN "LZW stream is longer than the provided buffer!"); + } + + } + + if (flags & LZW_FLAG_SYMQUIRKS) { + /* Digital Symphony LZW compressed stream size is 4 aligned. */ + size_t num_read = bs.num_read; + while (num_read & 3) { + #ifdef LZW_DEBUG + printf("A: align byte\n"); + #endif + hio_read8(f); + num_read++; + } + } + #ifdef LZW_DEBUG + printf("I: stream end position: %ld\n", hio_tell(f)); + #endif + + LZW_free(&lzw); + return 0; +} + +/* Decode Digital Symphony sigma-delta compressed samples. + * This isn't really LZW but it uses the same bitstream and alignment hacks. + * + * Based on the sigma-delta unpacker from OpenMPT by Saga Musix. + */ +int libxmp_read_sigma_delta(void *dest, size_t dest_len, size_t max_read_len, + HIO_HANDLE *f) +{ + struct bitstream bs; + uint8 *pos = (uint8 *)dest; + uint8 *end = pos + dest_len; + int max_runlength; + int runlength = 0; + int bits = 8; + uint8 accumulator; + + if (!dest_len) + return 0; + + bs_init(&bs, max_read_len); + + /* DOESN'T count towards alignment. */ + max_runlength = hio_read8(f); + /* DOES count. */ + accumulator = bs_read(&bs, f, bits); + *(pos++) = accumulator; + + while (pos < end) { + int value = bs_read(&bs, f, bits); + if (value < 0) + return -1; + + /* Expand bitwidth. */ + if (!value) { + if (bits >= 9) + return -1; + + bits++; + runlength = 0; + continue; + } + + if (value & 1) accumulator -= (value >> 1); + else accumulator += (value >> 1); + + *(pos++) = accumulator; + + /* High bit set resets the run length. */ + if (value >> (bits - 1)) { + runlength = 0; + continue; + } + /* Reduce bitwidth. */ + if (++runlength >= max_runlength) { + if (bits > 1) + bits--; + runlength = 0; + } + } + + /* Digital Symphony aligns bitstreams to lengths of 4. */ + if (bs.num_read & 3) { + size_t total = bs.num_read; + while (total & 3) { + hio_read8(f); + total++; + } + } + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/lzw.h b/thirdparty/libxmp/src/loaders/lzw.h new file mode 100644 index 0000000..12ecd44 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/lzw.h @@ -0,0 +1,17 @@ +#ifndef LIBXMP_LOADER_LZW_H +#define LIBXMP_LOADER_LZW_H + +#include "loader.h" + +#define LZW_FLAG_MAXBITS(x) ((x) & 15) +#define LZW_FLAG_SYMQUIRKS 0x100 + +#define LZW_FLAGS_SYM LZW_FLAG_MAXBITS(13) | LZW_FLAG_SYMQUIRKS + +int libxmp_read_lzw(void *dest, size_t dest_len, size_t max_read_len, + int flags, HIO_HANDLE *f); + +int libxmp_read_sigma_delta(void *dest, size_t dest_len, size_t max_read_len, + HIO_HANDLE *f); + +#endif /* LIBXMP_LOADER_LZW_H */ diff --git a/thirdparty/libxmp/src/loaders/masi16_load.c b/thirdparty/libxmp/src/loaders/masi16_load.c new file mode 100644 index 0000000..c33d10d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/masi16_load.c @@ -0,0 +1,437 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_PSM_ MAGIC4('P','S','M',0xfe) + + +static int masi16_test (HIO_HANDLE *, char *, const int); +static int masi16_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_masi16 = { + "Epic MegaGames MASI 16", + masi16_test, + masi16_load +}; + +static int masi16_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_PSM_) + return -1; + + libxmp_read_title(f, t, 60); + + return 0; +} + + +static void masi16_translate_effect(struct xmp_event *event, uint8 effect, + uint8 param, uint8 param2, uint8 param3) +{ + switch (effect) { + case 1: /* Fine Volume Slide Up */ + event->fxt = FX_F_VSLIDE_UP; + event->fxp = param; + break; + case 2: /* Volume Slide Up */ + event->fxt = FX_VOLSLIDE_UP; + event->fxp = param; + break; + case 3: /* Fine Volume Slide Down */ + event->fxt = FX_F_VSLIDE_DN; + event->fxp = param; + break; + case 4: /* Volume Slide Down */ + event->fxt = FX_VOLSLIDE_DN; + event->fxp = param; + break; + + case 10: /* Fine Porta Up */ + event->fxt = FX_F_PORTA_UP; + event->fxp = param; + break; + case 11: /* Portamento Up */ + event->fxt = FX_PORTA_UP; + event->fxp = param; + break; + case 12: /* Fine Porta Down */ + event->fxt = FX_F_PORTA_DN; + event->fxp = param; + break; + case 13: /* Portamento Down */ + event->fxt = FX_PORTA_DN; + event->fxp = param; + break; + case 14: /* Tone Portamento */ + event->fxt = FX_TONEPORTA; + event->fxp = param; + break; + case 15: /* Glissando control */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_GLISS << 4) | (param & 0x0f); + break; + case 16: /* Tone Portamento + Volslide Up */ + event->fxt = FX_TONEPORTA; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_UP; + event->f2p = param; + break; + case 17: /* Tone Portamento + Volslide Down */ + event->fxt = FX_TONEPORTA; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_DN; + event->f2p = param; + break; + + case 20: /* Vibrato */ + event->fxt = FX_VIBRATO; + event->fxp = param; + break; + case 21: /* Vibrato waveform */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_VIBRATO_WF << 4) | (param & 0x0f); + break; + case 22: /* Vibrato + Volume Slide Up */ + event->fxt = FX_VIBRATO; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_UP; + event->f2p = param; + break; + case 23: /* Vibrato + Volume Slide Down */ + event->fxt = FX_VIBRATO; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_DN; + event->f2p = param; + break; + + case 30: /* Tremolo */ + event->fxt = FX_TREMOLO; + event->fxp = param; + break; + case 31: /* Tremolo waveform */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_TREMOLO_WF << 4) | (param & 0x0f); + break; + + case 40: /* Sample Offset */ + /* TODO: param and param3 are the fine and high offsets. */ + event->fxt = FX_OFFSET; + event->fxp = param2; + break; + case 41: /* Retrigger Note */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | (param & 0x0f); + break; + case 42: /* Note Cut */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | (param & 0x0f); + break; + case 43: /* Note Delay */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | (param & 0x0f); + break; + + case 50: /* Position Jump */ + event->fxt = FX_JUMP; + event->fxp = param; + break; + case 51: /* Pattern Break */ + event->fxt = FX_BREAK; + event->fxp = param; + break; + case 52: /* Jump Loop */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_PATTERN_LOOP << 4) | (param & 0x0f); + break; + case 53: /* Pattern Delay */ + event->fxt = FX_PATT_DELAY; + event->fxp = param; + break; + + case 60: /* Set Speed */ + event->fxt = FX_S3M_SPEED; + event->fxp = param; + break; + case 61: /* Set BPM */ + event->fxt = FX_S3M_BPM; + event->fxp = param; + break; + + case 70: /* Arpeggio */ + event->fxt = FX_ARPEGGIO; + event->fxp = param; + break; + case 71: /* Set Finetune */ + event->fxt = FX_FINETUNE; + event->fxp = param; + break; + case 72: /* Set Balance */ + event->fxt = FX_SETPAN; + event->fxp = (param & 0x0f) | ((param & 0x0f) << 4); + break; + + default: + event->fxt = event->fxp = 0; + break; + } +} + +static int masi16_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i; + struct xmp_event *event; + uint8 buf[1024]; + uint32 p_ord, p_chn, p_pat, p_ins; + uint32 p_smp[256]; + uint8 sample_map[256]; + int type, ver /*, mode*/; + int stored_ins; + + LOAD_INIT(); + + hio_read32b(f); + + hio_read(buf, 1, 60, f); + memcpy(mod->name, (char *)buf, 59); + mod->name[59] = '\0'; + + type = hio_read8(f); /* song type */ + ver = hio_read8(f); /* song version */ + /*mode =*/ hio_read8(f); /* pattern version */ + + if (type & 0x01) /* song mode not supported */ + return -1; + + libxmp_set_type(m, "Epic MegaGames MASI 16 PSM %d.%02d", MSN(ver), LSN(ver)); + + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + hio_read8(f); /* master volume */ + hio_read16l(f); /* song length */ + mod->len = hio_read16l(f); + mod->pat = hio_read16l(f); + stored_ins = hio_read16l(f); + hio_read16l(f); /* ignore channels to play */ + mod->chn = hio_read16l(f); /* use channels to proceed */ + + /* Sanity check */ + if (mod->len > 256 || mod->pat > 256 || stored_ins > 255 || + mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + mod->trk = mod->pat * mod->chn; + + p_ord = hio_read32l(f); + p_chn = hio_read32l(f); + p_pat = hio_read32l(f); + p_ins = hio_read32l(f); + + /* should be this way but fails with Silverball song 6 */ + //mod->flg |= ~type & 0x02 ? XXM_FLG_MODRNG : 0; + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + hio_seek(f, start + p_ord, SEEK_SET); + hio_read(mod->xxo, 1, mod->len, f); + + memset(buf, 0, mod->chn); + hio_seek(f, start + p_chn, SEEK_SET); + hio_read(buf, 1, 16, f); + + for (i = 0; i < mod->chn; i++) { + if (buf[i] < 16) { + mod->xxc[i].pan = buf[i] | (buf[i] << 4); + } + } + + /* Get the actual instruments count... */ + mod->ins = 0; + for (i = 0; i < stored_ins; i++) { + hio_seek(f, start + p_ins + 64 * i + 45, SEEK_SET); + sample_map[i] = hio_read16l(f) - 1; + mod->ins = MAX(mod->ins, sample_map[i] + 1); + } + if (mod->ins > 255 || hio_error(f)) + return -1; + + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + return -1; + + memset(p_smp, 0, sizeof(p_smp)); + + hio_seek(f, start + p_ins, SEEK_SET); + for (i = 0; i < stored_ins; i++) { + struct xmp_instrument *xxi; + struct xmp_sample *xxs; + struct xmp_subinstrument *sub; + uint16 flags, c2spd; + int finetune; + int num = sample_map[i]; + + if (hio_read(buf, 1, 64, f) < 64) + return -1; + + xxi = &mod->xxi[num]; + xxs = &mod->xxs[num]; + + /* Don't load duplicate instruments */ + if (xxi->sub) + continue; + + if (libxmp_alloc_subinstrument(mod, num, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + /*hio_read(buf, 1, 13, f);*/ /* sample filename */ + /*hio_read(buf, 1, 24, f);*/ /* sample description */ + memcpy(xxi->name, buf + 13, 24); + xxi->name[24] = '\0'; + p_smp[i] = readmem32l(buf + 37); + /*hio_read32l(f);*/ /* memory location */ + /*hio_read16l(f);*/ /* sample number */ + flags = buf[47]; /* sample type */ + xxs->len = readmem32l(buf + 48); + xxs->lps = readmem32l(buf + 52); + xxs->lpe = readmem32l(buf + 56); + finetune = buf[60]; + sub->vol = buf[61]; + c2spd = readmem16l(buf + 62); + sub->pan = 0x80; + sub->sid = num; + xxs->flg = flags & 0x80 ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= flags & 0x20 ? XMP_SAMPLE_LOOP_BIDIR : 0; + + libxmp_c2spd_to_note(c2spd, &sub->xpo, &sub->fin); + sub->fin += (int8)((finetune & 0x0f) << 4); + sub->xpo += (finetune >> 4) - 7; + + /* The documentation claims samples shouldn't exceed 64k. The + * PS16 modules from Silverball and Epic Pinball confirm this. + * Later Protracker Studio Modules (MASI) allow up to 1MB. + */ + if ((uint32)xxs->len > 64 * 1024) { + D_(D_CRIT "invalid sample %d length %d", num, xxs->len); + return -1; + } + + if (xxs->len > 0) + xxi->nsm = 1; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %5d", + num, xxi->name, xxs->len, xxs->lps, + xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? + 'L' : ' ', sub->vol, c2spd); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + hio_seek(f, start + p_pat, SEEK_SET); + for (i = 0; i < mod->pat; i++) { + int len; + uint8 b, rows, chan; + + len = hio_read16l(f) - 4; + rows = hio_read8(f); + if (rows > 64) { + return -1; + } + chan = hio_read8(f); + if (chan > 32) { + return -1; + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + for (r = 0; r < rows; r++) { + while (len > 0) { + b = hio_read8(f); + len--; + + if (b == 0) + break; + + c = b & 0x0f; + if (c >= mod->chn) + return -1; + event = &EVENT(i, c, r); + + if (b & 0x80) { + event->note = hio_read8(f) + 36; + event->ins = hio_read8(f); + len -= 2; + } + + if (b & 0x40) { + event->vol = hio_read8(f) + 1; + len--; + } + + if (b & 0x20) { + uint8 effect = hio_read8(f); + uint8 param = hio_read8(f); + uint8 param2 = 0; + uint8 param3 = 0; + + if (effect == 40) { /* Sample Offset */ + param2 = hio_read8(f); + param3 = hio_read8(f); + } + masi16_translate_effect(event, effect, param, param2, param3); + len -= 2; + } + } + } + + if (len > 0) + hio_seek(f, len, SEEK_CUR); + } + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < stored_ins; i++) { + struct xmp_sample *xxs = &mod->xxs[sample_map[i]]; + + /* Don't load duplicate sample data */ + if (xxs->data) + continue; + + hio_seek(f, start + p_smp[i], SEEK_SET); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, xxs, NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/masi_load.c b/thirdparty/libxmp/src/loaders/masi_load.c new file mode 100644 index 0000000..8484939 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/masi_load.c @@ -0,0 +1,835 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Originally based on the PSM loader from Modplug by Olivier Lapicque and + * fixed comparing the One Must Fall! PSMs with Kenny Chou's MTM files. + */ + +/* + * From EPICTEST Readme.1st: + * + * The Music And Sound Interface, MASI, is the basis behind all new Epic + * games. MASI uses its own proprietary file format, PSM, for storing + * its music. + */ + +/* + * kode54's comment on Sinaria PSMs in the foo_dumb hydrogenaudio forum: + * + * "The Sinaria variant uses eight character pattern and instrument IDs, + * the sample headers are laid out slightly different, and the patterns + * use a different format for the note values, and also different effect + * scales for certain commands. + * + * [Epic] PSM uses high nibble for octave and low nibble for note, for + * a valid range up to 0x7F, for a range of D-1 through D#9 compared to + * IT. (...) Sinaria PSM uses plain note values, from 1 - 83, for a + * range of C-3 through B-9. + * + * [Epic] PSM also uses an effect scale for portamento, volume slides, + * and vibrato that is about four times as sensitive as the IT equivalents. + * Sinaria does not. This seems to coincide with the MOD/S3M to PSM + * converter that Joshua Jensen released in the EPICTEST.ZIP file which + * can still be found on a few FTP sites. It converted effects literally, + * even though the bundled players behaved as the libraries used with + * Epic's games did and made the effects sound too strong." + */ + +/* + * Claudio's note: Sinaria seems to have a finetune byte just before + * volume and some kind of (stereo?) interleaved sample, with 16-byte + * frames (see Sinaria songs 5 and 8). Sinaria song 10 still sounds + * ugly, maybe caused by finetune issues? + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_PSM_ MAGIC4('P','S','M',' ') +#define MAGIC_FILE MAGIC4('F','I','L','E') +#define MAGIC_TITL MAGIC4('T','I','T','L') +#define MAGIC_OPLH MAGIC4('O','P','L','H') +#define MAGIC_PPAN MAGIC4('P','P','A','N') + + +static int masi_test (HIO_HANDLE *, char *, const int); +static int masi_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_masi = { + "Epic MegaGames MASI", + masi_test, + masi_load +}; + +static int masi_test(HIO_HANDLE *f, char *t, const int start) +{ + int val; + + if (hio_read32b(f) != MAGIC_PSM_) + return -1; + + hio_read8(f); + hio_read8(f); + hio_read8(f); + if (hio_read8(f) != 0) + return -1; + + if (hio_read32b(f) != MAGIC_FILE) + return -1; + + hio_read32b(f); + val = hio_read32l(f); + hio_seek(f, val, SEEK_CUR); + + if (hio_read32b(f) == MAGIC_TITL) { + val = hio_read32l(f); + libxmp_read_title(f, t, val); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + +struct local_data { + int sinaria; + int cur_pat; + int cur_ins; + uint8 *pnam; + uint8 *pord; +}; + +static int get_sdft(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + return 0; +} + +static int get_titl(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + char buf[XMP_NAME_SIZE]; + + size = size > XMP_NAME_SIZE - 1 ? XMP_NAME_SIZE - 1 : size; + size = hio_read(buf, 1, size, f); + strncpy(mod->name, buf, size); + mod->name[size] = '\0'; + + return 0; +} + +static int get_dsmp_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + mod->ins++; + mod->smp = mod->ins; + + return 0; +} + +static int get_pbod_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + char buf[20]; + + mod->pat++; + if (hio_read(buf, 1, 20, f) < 20) { + D_(D_CRIT "read error at pat %d", mod->pat - 1); + return -1; + } + if (buf[9] != 0 && buf[13] == 0) + data->sinaria = 1; + + return 0; +} + + +static int get_dsmp(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct local_data *data = (struct local_data *)parm; + int i, srate, flags; + int finetune; + + flags = hio_read8(f); /* flags */ + hio_seek(f, 8, SEEK_CUR); /* songname */ + hio_seek(f, data->sinaria ? 8 : 4, SEEK_CUR); /* smpid */ + + i = data->cur_ins; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + hio_read(xxi->name, 1, 31, f); + hio_seek(f, 8, SEEK_CUR); + hio_read8(f); /* insno */ + hio_read8(f); + xxs->len = hio_read32l(f); + xxs->lps = hio_read32l(f); + xxs->lpe = hio_read32l(f); + xxs->flg = flags & 0x80 ? XMP_SAMPLE_LOOP : 0; + hio_read16l(f); + + if ((int32)xxs->lpe < 0) + xxs->lpe = 0; + + if (xxs->len > 0) + xxi->nsm = 1; + + finetune = 0; + if (data->sinaria) { + finetune = (int8)(hio_read8s(f) << 4); + } + + sub->vol = hio_read8(f) / 2 + 1; + hio_read32l(f); + sub->pan = 0x80; + sub->sid = i; + srate = hio_read16l(f); + + D_(D_INFO "[%2X] %-32.32s %05x %05x %05x %c V%02x %+04d %5d", i, + xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, finetune, srate); + + libxmp_c2spd_to_note(srate, &sub->xpo, &sub->fin); + sub->fin += finetune; + + hio_seek(f, 16, SEEK_CUR); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_8BDIFF, xxs, NULL) < 0) + return -1; + + data->cur_ins++; + + return 0; +} + + +static uint8 convert_porta(uint8 param, int sinaria) +{ + if (sinaria) { + return param; + } + + if (param < 4) { + return param | 0xf0; + } else { + return param >> 2; + } +} + +static int get_pbod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, r; + struct xmp_event *event, dummy; + uint8 flag, chan; + /* uint32 len; */ + int rows, rowlen; + + i = data->cur_pat; + + /*len =*/ hio_read32l(f); + hio_read(data->pnam + i * 8, 1, data->sinaria ? 8 : 4, f); + + rows = hio_read16l(f); + if (hio_error(f)) { + return -1; + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + r = 0; + + do { + rowlen = hio_read16l(f) - 2; + if (hio_error(f)) { + return -1; + } + while (rowlen > 0) { + flag = hio_read8(f); + + if (rowlen == 1) + break; + + chan = hio_read8(f); + rowlen -= 2; + + event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy; + + if (flag & 0x80) { + uint8 note = hio_read8(f); + rowlen--; + if (data->sinaria) + note += 36; + else + note = (note >> 4) * 12 + (note & 0x0f) + 1 + 12; + event->note = note; + } + + if (flag & 0x40) { + event->ins = hio_read8(f) + 1; + rowlen--; + } + + if (flag & 0x20) { + event->vol = hio_read8(f) / 2 + 1; + rowlen--; + } + + if (flag & 0x10) { + uint8 fxt = hio_read8(f); + uint8 fxp = hio_read8(f); + rowlen -= 2; + +#if 0 + /* compressed events */ + if (fxt >= 0x40) { + switch (fxp >> 4) { + case 0x0: { + uint8 note; + note = (fxt>>4)*12 + + (fxt & 0x0f) + 1; + event->note = note; + fxt = FX_TONEPORTA; + fxp = (fxp + 1) * 2; + break; } + default: +D_(D_CRIT "p%d r%d c%d: compressed event %02x %02x\n", i, r, chan, fxt, fxp); + return -1; + } + } else +#endif + + switch (fxt) { + + /* Volume slide */ + case 0x01: /* fine volslide up */ + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_UP << 4) | + ((fxp / 2) & 0x0f); + break; + case 0x02: /* volslide up */ + fxt = FX_VOLSLIDE; + fxp = (fxp / 2) << 4; + break; + case 0x03: /* fine volslide down */ + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_DN << 4) | + ((fxp / 2) & 0x0f); + break; + case 0x04: /* volslide down */ + fxt = FX_VOLSLIDE; + fxp /= 2; + break; + + /* Portamento */ + case 0x0b: /* fine portamento up */ + fxt = FX_PORTA_UP; + fxp = (EX_F_PORTA_UP << 4) | + convert_porta(fxp, data->sinaria); + break; + case 0x0c: /* portamento up */ + fxt = FX_PORTA_UP; + fxp = convert_porta(fxp, data->sinaria); + break; + case 0x0d: /* fine portamento up */ + fxt = FX_PORTA_DN; + fxp = (EX_F_PORTA_DN << 4) | + convert_porta(fxp, data->sinaria); + break; + case 0x0e: /* portamento down */ + fxt = FX_PORTA_DN; + fxp = convert_porta(fxp, data->sinaria); + break; + case 0x0f: /* tone portamento */ + fxt = FX_TONEPORTA; + fxp >>= 2; + break; + case 0x10: /* toneporta + vslide up */ + fxt = FX_TONE_VSLIDE; + fxp = fxt & 0xf0; + break; + case 0x11: /* glissando */ + fxt = FX_EXTENDED; + fxp = (EX_GLISS << 4) | (fxp & 0x0f); + break; + case 0x12: /* toneporta + vslide down */ + fxt = FX_TONE_VSLIDE; + fxp >>= 4; + break; + + /* 0x13: S3M S: crashes MASI */ + + /* Vibrato */ + case 0x15: /* vibrato */ + fxt = data->sinaria ? + FX_VIBRATO : FX_FINE_VIBRATO; + /* fxp remains the same */ + break; + case 0x16: /* vibrato waveform */ + fxt = FX_EXTENDED; + fxp = (EX_VIBRATO_WF << 4) | (fxp & 0x0f); + break; + case 0x17: /* vibrato + vslide up */ + fxt = FX_VIBRA_VSLIDE; + fxp >>= 4; + break; + case 0x18: /* vibrato + vslide down */ + fxt = FX_VIBRA_VSLIDE; + fxp = fxp & 0x0f; + break; + + /* Tremolo */ + case 0x1f: /* tremolo */ + fxt = FX_TREMOLO; + /* fxp remains the same */ + break; + case 0x20: /* tremolo waveform */ + fxt = FX_EXTENDED; + fxp = (EX_TREMOLO_WF << 4) | (fxp & 0x0f); + break; + + /* Sample commands */ + case 0x29: /* 3-byte offset */ + fxt = FX_OFFSET; + /* use only the middle byte */ + fxp = hio_read8(f); + hio_read8(f); + rowlen -= 2; + break; + case 0x2a: /* retrig note */ + fxt = FX_EXTENDED; + fxp = (EX_RETRIG << 4) | (fxp & 0x0f); + break; + case 0x2b: /* note cut */ + fxt = FX_EXTENDED; + fxp = (EX_CUT << 4) | (fxp & 0x0f); + break; + case 0x2c: /* note delay */ + fxt = FX_EXTENDED; + fxp = (EX_DELAY << 4) | (fxp & 0x0f); + break; + + /* Position change */ + case 0x33: /* position jump */ + /* not used in MASI */ + fxt = FX_JUMP; + fxp >>= 1; + hio_read8(f); + rowlen--; + break; + case 0x34: /* pattern break */ + /* not used in MASI */ + fxt = FX_BREAK; + break; + case 0x35: /* pattern loop */ + fxt = FX_EXTENDED; + fxp = (EX_PATTERN_LOOP << 4) | (fxp & 0x0f); + break; + case 0x36: /* pattern delay */ + fxt = FX_EXTENDED; + fxp = (EX_PATT_DELAY << 4) | (fxp & 0x0f); + break; + + /* Speed change */ + case 0x3d: /* speed */ + fxt = FX_SPEED; + break; + case 0x3e: /* tempo */ + fxt = FX_SPEED; + break; + + /* Other */ + case 0x47: /* arpeggio */ + fxt = FX_S3M_ARPEGGIO; + break; + case 0x48: /* set finetune */ + fxt = FX_EXTENDED; + fxp = (EX_FINETUNE << 4) | (fxp & 0x0f); + break; + case 0x49: /* set pan */ + fxt = FX_SETPAN; + fxp <<= 4; + break; + + default: +D_(D_CRIT "p%d r%d c%d: unknown effect %02x %02x\n", i, r, chan, fxt, fxp); + fxt = fxp = 0; + } + + event->fxt = fxt; + event->fxp = fxp; + } + } + r++; + } while (r < rows); + + data->cur_pat++; + + return 0; +} + +static int get_song(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + hio_seek(f, 10, SEEK_CUR); + mod->chn = hio_read8(f); + + return 0; +} + +static int subchunk_oplh(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int first_order_chunk = INT_MAX; + int num_chunk, i; + + /* First two bytes = Number of chunks that follow */ + num_chunk = hio_read16l(f); + + /* Sub sub chunks */ + for (i = 0; i < num_chunk && size > 0; i++) { + int opcode = hio_read8(f); + + size--; + + if (opcode == 0) { /* last sub sub chunk */ + break; + } + + /* Saga Musix's note in OpenMPT: + * + * "This is more like a playlist than a collection of global + * values. In theory, a tempo item inbetween two order items + * should modify the tempo when switching patterns. No module + * uses this feature in practice though, so we can keep our + * loader simple. Unimplemented opcodes do nothing or freeze + * MASI." + */ + switch (opcode) { + case 0x01: /* Play order list item */ + if (mod->len >= XMP_MAX_MOD_LENGTH) { + return -1; + } + hio_read(data->pord + mod->len * 8, 1, data->sinaria ? 8 : 4, f); + size -= data->sinaria ? 8 : 4; + mod->len++; + if (first_order_chunk == INT_MAX) { + first_order_chunk = i; + } + break; + + /* 0x02: Play range */ + /* 0x03: Jump loop */ + + case 0x04: { /* Jump line (restart position) */ + int restart_chunk = hio_read16l(f); + size -= 2; + + /* This jumps to the command line, but since we're converting + * play order list items to our order list, only change the + * restart position if it's after the first order chunk. + */ + + if (restart_chunk >= first_order_chunk) { + mod->rst = restart_chunk - first_order_chunk; + } + + break; } + + /* 0x05: Channel flip */ + /* 0x06: Transpose */ + + case 0x07: /* Default speed */ + mod->spd = hio_read8(f); + size--; + break; + case 0x08: /* Default tempo */ + mod->bpm = hio_read8(f); + size--; + break; + case 0x0c: /* Sample map table */ + hio_read16l(f); + hio_read16l(f); + hio_read16l(f); + size -= 6; + break; + case 0x0d: { /* Channel panning table */ + int chn = hio_read8(f); + int pan = hio_read8(f); + int type = hio_read8(f); + struct xmp_channel *xxc; + + if (chn >= XMP_MAX_CHANNELS) { + break; + } + + xxc = &mod->xxc[chn]; + + size -= 3; + + switch (type) { + case 0: /* use panning */ + xxc->pan = pan ^ 0x80; + break; + case 2: /* surround */ + xxc->pan = 0x80; + xxc->flg |= XMP_CHANNEL_SURROUND; + break; + case 4: /* center */ + xxc->pan = 0x80; + break; + } + break; } + case 0x0e: { /* Channel volume table */ + int chn = hio_read8(f); + int vol = hio_read8(f); + struct xmp_channel *xxc; + + if (chn >= XMP_MAX_CHANNELS) { + break; + } + + xxc = &mod->xxc[chn]; + + size -= 2; + + xxc->vol = (vol >> 2) + 1; + break; } + default: + /*printf("channel %d: %02x %02x\n", i, c, hio_read8(f));*/ + return -1; + } + } + + return 0; +} + +/* Sinaria channel panning table */ +static int subchunk_ppan(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + for (i = 0; i < XMP_MAX_CHANNELS && size > 0; i++) { + struct xmp_channel *xxc = &mod->xxc[i]; + int type = hio_read8(f); + int pan = hio_read8(f); + + size -= 2; + + switch (type) { + case 0: /* use panning */ + xxc->pan = pan ^ 0x80; + break; + case 2: /* surround */ + xxc->pan = 0x80; + xxc->flg |= XMP_CHANNEL_SURROUND; + break; + case 4: /* center */ + xxc->pan = 0x80; + break; + } + } + + return 0; +} + +/* Subchunk loader based on OpenMPT LoadPSM.cpp */ +static int get_song_2(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + uint32 magic; + char buf[20]; + + hio_read(buf, 1, 9, f); + hio_read16l(f); + size -= 11; + + D_(D_INFO "Subsong title: %-9.9s", buf); + + /* Iterate over subchunks. We want OPLH and PPAN */ + while (size > 0) { + int subchunk_size; + + magic = hio_read32b(f); + subchunk_size = hio_read32l(f); + if (subchunk_size <= 0 || hio_error(f)) { + return -1; + } + + size -= subchunk_size; + + switch (magic) { + case MAGIC_OPLH: + if (subchunk_oplh(m, size, f, parm) < 0) { + return -1; + } + break; + + case MAGIC_PPAN: + if (subchunk_ppan(m, size, f, parm) < 0) { + return -1; + } + break; + + default: + hio_seek(f, subchunk_size, SEEK_CUR); + } + } + + return 0; +} + +static int masi_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int ret, offset; + int i, j; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); + + data.sinaria = 0; + mod->name[0] = 0; + + hio_seek(f, 8, SEEK_CUR); /* skip file size and FILE */ + mod->smp = mod->ins = 0; + data.cur_pat = 0; + data.cur_ins = 0; + offset = hio_tell(f); + + handle = libxmp_iff_new(); + if (handle == NULL) + goto err; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "TITL", get_titl); + ret |= libxmp_iff_register(handle, "SDFT", get_sdft); + ret |= libxmp_iff_register(handle, "SONG", get_song); + ret |= libxmp_iff_register(handle, "DSMP", get_dsmp_cnt); + ret |= libxmp_iff_register(handle, "PBOD", get_pbod_cnt); + + if (ret != 0) + goto err; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + goto err; + } + + libxmp_iff_release(handle); + + mod->trk = mod->pat * mod->chn; + data.pnam = (uint8 *) malloc(mod->pat * 8); /* pattern names */ + if (data.pnam == NULL) + goto err; + + data.pord = (uint8 *) malloc(XMP_MAX_MOD_LENGTH * 8); /* pattern orders */ + if (data.pord == NULL) + goto err2; + + libxmp_set_type(m, data.sinaria ? + "Sinaria PSM" : "Epic MegaGames MASI PSM"); + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + goto err3; + + if (libxmp_init_pattern(mod) < 0) + goto err3; + + D_(D_INFO "Stored patterns: %d", mod->pat); + D_(D_INFO "Stored samples : %d", mod->smp); + + hio_seek(f, start + offset, SEEK_SET); + + mod->len = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + goto err3; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "SONG", get_song_2); + ret |= libxmp_iff_register(handle, "DSMP", get_dsmp); + ret |= libxmp_iff_register(handle, "PBOD", get_pbod); + + if (ret != 0) + goto err3; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + goto err3; + } + + libxmp_iff_release(handle); + + for (i = 0; i < mod->len; i++) { + for (j = 0; j < mod->pat; j++) { + if (!memcmp(data.pord + i * 8, data.pnam + j * 8, data.sinaria ? 8 : 4)) { + mod->xxo[i] = j; + break; + } + } + + if (j == mod->pat) + break; + } + + free(data.pord); + free(data.pnam); + + return 0; + + err3: + free(data.pord); + err2: + free(data.pnam); + err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/mdl_load.c b/thirdparty/libxmp/src/loaders/mdl_load.c new file mode 100644 index 0000000..5413857 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mdl_load.c @@ -0,0 +1,1252 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Note: envelope switching (effect 9) and sample status change (effect 8) + * not supported. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_DMDL MAGIC4('D','M','D','L') + + +static int mdl_test (HIO_HANDLE *, char *, const int); +static int mdl_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mdl = { + "Digitrakker", + mdl_test, + mdl_load +}; + +static int mdl_test(HIO_HANDLE *f, char *t, const int start) +{ + uint16 id; + + if (hio_read32b(f) != MAGIC_DMDL) + return -1; + + hio_read8(f); /* version */ + id = hio_read16b(f); + + if (id == 0x494e) { /* IN */ + hio_read32b(f); + libxmp_read_title(f, t, 32); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + + +#define MDL_NOTE_FOLLOWS 0x04 +#define MDL_INSTRUMENT_FOLLOWS 0x08 +#define MDL_VOLUME_FOLLOWS 0x10 +#define MDL_EFFECT_FOLLOWS 0x20 +#define MDL_PARAMETER1_FOLLOWS 0x40 +#define MDL_PARAMETER2_FOLLOWS 0x80 + + +struct mdl_envelope { + uint8 num; + uint8 data[30]; + uint8 sus; + uint8 loop; +}; + +struct local_data { + int *i_index; + int *s_index; + int *v_index; /* volume envelope */ + int *p_index; /* pan envelope */ + int *f_index; /* pitch envelope */ + int *packinfo; + int has_in; + int has_pa; + int has_tr; + int has_ii; + int has_is; + int has_sa; + int v_envnum; + int p_envnum; + int f_envnum; + struct mdl_envelope *v_env; + struct mdl_envelope *p_env; + struct mdl_envelope *f_env; +}; + + +static void fix_env(int i, struct xmp_envelope *ei, struct mdl_envelope *env, + int *idx, int envnum) +{ + int j, k, lastx; + + if (idx[i] >= 0) { + ei->flg = XMP_ENVELOPE_ON; + ei->npt = 15; + + for (j = 0; j < envnum; j++) { + if (idx[i] == env[j].num) { + ei->flg |= env[j].sus & 0x10 ? XMP_ENVELOPE_SUS : 0; + ei->flg |= env[j].sus & 0x20 ? XMP_ENVELOPE_LOOP : 0; + ei->sus = env[j].sus & 0x0f; + ei->lps = env[j].loop & 0x0f; + ei->lpe = env[j].loop & 0xf0; + + lastx = -1; + + for (k = 0; k < ei->npt; k++) { + int x = env[j].data[k * 2]; + + if (x == 0) + break; + ei->data[k * 2] = lastx + x; + ei->data[k * 2 + 1] = env[j].data[k * 2 + 1]; + + lastx = ei->data[k * 2]; + } + + ei->npt = k; + break; + } + } + } +} + + +/* Effects 1-6 (note effects) can only be entered in the first effect + * column, G-L (volume-effects) only in the second column. + */ + +static void xlat_fx_common(uint8 *t, uint8 *p) +{ + switch (*t) { + case 0x07: /* 7 - Set BPM */ + *t = FX_S3M_BPM; + break; + case 0x08: /* 8 - Set pan */ + case 0x09: /* 9 - Set envelope -- not supported */ + case 0x0a: /* A - Not used */ + *t = *p = 0x00; + break; + case 0x0b: /* B - Position jump */ + case 0x0c: /* C - Set volume */ + case 0x0d: /* D - Pattern break */ + /* Like protracker */ + break; + case 0x0e: /* E - Extended */ + switch (MSN (*p)) { + case 0x0: /* E0 - not used */ + case 0x3: /* E3 - not used */ + case 0x8: /* Set sample status -- unsupported */ + *t = *p = 0x00; + break; + case 0x1: /* Pan slide left */ + *t = FX_PANSLIDE; + *p <<= 4; + break; + case 0x2: /* Pan slide right */ + *t = FX_PANSLIDE; + *p &= 0x0f; + break; + } + break; + case 0x0f: + *t = FX_S3M_SPEED; + break; + } +} + +static void xlat_fx1(uint8 *t, uint8 *p) +{ + switch (*t) { + case 0x00: /* - - No effect */ + *p = 0; + break; + case 0x05: /* 5 - Arpeggio */ + *t = FX_ARPEGGIO; + break; + case 0x06: /* 6 - Not used */ + *t = *p = 0x00; + break; + } + + xlat_fx_common(t, p); +} + + +static void xlat_fx2(uint8 *t, uint8 *p) +{ + switch (*t) { + case 0x00: /* - - No effect */ + *p = 0; + break; + case 0x01: /* G - Volume slide up */ + *t = FX_VOLSLIDE_UP; + break; + case 0x02: /* H - Volume slide down */ + *t = FX_VOLSLIDE_DN; + break; + case 0x03: /* I - Multi-retrig */ + *t = FX_MULTI_RETRIG; + break; + case 0x04: /* J - Tremolo */ + *t = FX_TREMOLO; + break; + case 0x05: /* K - Tremor */ + *t = FX_TREMOR; + break; + case 0x06: /* L - Not used */ + *t = *p = 0x00; + break; + } + + xlat_fx_common(t, p); +} + +struct bits { + uint32 b, n; +}; + +static unsigned int get_bits(char i, uint8 **buf, int *len, struct bits *bits) +{ + unsigned int x; + + if (i == 0) { + bits->b = readmem32l(*buf); + *buf += 4; *len -= 4; + bits->n = 32; + } + + x = bits->b & ((1 << i) - 1); /* get i bits */ + bits->b >>= i; + if ((bits->n -= i) <= 24) { + if (*len <= 0) /* FIXME: last few bits can't be consumed */ + return x; + bits->b |= readmem32l((*buf)++) << bits->n; + bits->n += 8; + (*len)--; + } + + return x; +} + +/* From the Digitrakker docs: + * + * The description of the sample-packmethode (1) [8bit packing]:... + * ---------------------------------------------------------------- + * + * The method is based on the Huffman algorithm. It's easy and very fast + * and effective on samples. The packed sample is a bit stream: + * + * Byte 0 Byte 1 Byte 2 Byte 3 + * Bit 76543210 fedcba98 nmlkjihg ....rqpo + * + * A packed byte is stored in the following form: + * + * xxxx10..0s => byte = + (number of <0> bits between + * s and 1) * 16 - 8; + * if s==1 then byte = byte xor 255 + * + * If there are no <0> bits between the first bit (sign) and the <1> bit, + * you have the following form: + * + * xxx1s => byte = ; if s=1 then byte = byte xor 255 + */ + +static int unpack_sample8(uint8 *t, uint8 *f, int len, int l) +{ + int i, s; + uint8 b, d; + struct bits bits; + + D_(D_INFO "unpack sample 8bit, len=%d", len); + get_bits(0, &f, &len, &bits); + + for (i = b = d = 0; i < l; i++) { + + /* Sanity check */ + if (len < 0) + return -1; + + s = get_bits(1, &f, &len, &bits); + if (get_bits(1, &f, &len, &bits)) { + b = get_bits(3, &f, &len, &bits); + } else { + b = 8; + while (len >= 0 && get_bits(1, &f, &len, &bits) == 0) { + /* Sanity check */ + if (b >= 240) { return -1; } + b += 16; + } + b += get_bits(4, &f, &len, &bits); + } + + if (s) { + b ^= 0xff; + } + + d += b; + *t++ = d; + } + + return 0; +} + +/* + * The description of the sample-packmethode (2) [16bit packing]:... + * ---------------------------------------------------------------- + * + * It works as methode (1) but it only crunches every 2nd byte (the high- + * bytes of 16 bit samples). So when you depack 16 bit samples, you have to + * read 8 bits from the data-stream first. They present the lowbyte of the + * sample-word. Then depack the highbyte in the descripted way (methode [1]). + * Only the highbytes are delta-values. So take the lowbytes as they are. + * Go on this way for the whole sample! + */ + +static int unpack_sample16(uint8 *t, uint8 *f, int len, int l) +{ + int i, lo, s; + uint8 b, d; + struct bits bits; + + D_(D_INFO "unpack sample 16bit, len=%d", len); + get_bits(0, &f, &len, &bits); + + for (i = lo = b = d = 0; i < l; i++) { + /* Sanity check */ + if (len < 0) + return -1; + + lo = get_bits(8, &f, &len, &bits); + s = get_bits(1, &f, &len, &bits); + if (get_bits(1, &f, &len, &bits)) { + b = get_bits(3, &f, &len, &bits); + } else { + b = 8; + while (len >= 0 && get_bits(1, &f, &len, &bits) == 0) { + /* Sanity check */ + if (b >= 240) { return -1; } + b += 16; + } + b += get_bits(4, &f, &len, &bits); + } + + if (s) + b ^= 0xff; + d += b; + + *t++ = lo; + *t++ = d; + } + + return 0; +} + + +/* + * IFF chunk handlers + */ + +static int get_chunk_in(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->has_in) { + D_(D_CRIT "duplicate IN chunk"); + return -1; + } + data->has_in = 1; + + hio_read(mod->name, 1, 32, f); + mod->name[32] = '\0'; + hio_seek(f, 20, SEEK_CUR); + + mod->len = hio_read16l(f); + mod->rst = hio_read16l(f); + hio_read8(f); /* gvol */ + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + + /* Sanity check */ + if (mod->len > 256 || mod->rst > 255) { + return -1; + } + + for (i = 0; i < 32; i++) { + uint8 chinfo = hio_read8(f); + if (chinfo & 0x80) + break; + mod->xxc[i].pan = chinfo << 1; + } + mod->chn = i; + hio_seek(f, 32 - i - 1, SEEK_CUR); + + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) { + D_(D_CRIT "read error at order list"); + return -1; + } + + MODULE_INFO(); + + return 0; +} + +static int get_chunk_pa(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, chn; + int x; + + /* Sanity check */ + if (data->has_pa || !data->has_in) { + D_(D_CRIT "duplicate PA chunk or missing IN chunk"); + return -1; + } + data->has_pa = 1; + + mod->pat = hio_read8(f); + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + chn = hio_read8(f); + mod->xxp[i]->rows = (int)hio_read8(f) + 1; + + hio_seek(f, 16, SEEK_CUR); /* Skip pattern name */ + for (j = 0; j < chn; j++) { + x = hio_read16l(f); + + if (j < mod->chn) + mod->xxp[i]->index[j] = x; + } + } + + return 0; +} + +static int get_chunk_p0(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j; + uint16 x; + + /* Sanity check */ + if (data->has_pa || !data->has_in) { + D_(D_CRIT "duplicate PA (0.0) chunk or missing IN chunk"); + return -1; + } + data->has_pa = 1; + + mod->pat = hio_read8(f); + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + mod->xxp[i]->rows = 64; + + for (j = 0; j < 32; j++) { + x = hio_read16l(f); + + if (j < mod->chn) + mod->xxp[i]->index[j] = x; + } + } + + return 0; +} + +static int get_chunk_tr(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, k, row, len, max_trk; + struct xmp_track *track; + + /* Sanity check */ + if (data->has_tr || !data->has_pa) { + D_(D_CRIT "duplicate TR chunk or missing PA chunk"); + return -1; + } + data->has_tr = 1; + + mod->trk = hio_read16l(f) + 1; + + /* Sanity check */ + max_trk = 0; + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mod->chn; j++) { + if (max_trk < mod->xxp[i]->index[j]) + max_trk = mod->xxp[i]->index[j]; + } + } + if (max_trk >= mod->trk) { + return -1; + } + + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); + if (mod->xxt == NULL) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk); + + track = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * 255); + if (track == NULL) + goto err; + + /* Empty track 0 is not stored in the file */ + if (libxmp_alloc_track(mod, 0, 256) < 0) + goto err2; + + for (i = 1; i < mod->trk; i++) { + /* Length of the track in bytes */ + len = hio_read16l(f); + + memset(track, 0, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * 255); + + for (row = 0; len;) { + struct xmp_event *ev; + + /* Sanity check */ + if (row > 255) { + goto err2; + } + + ev = &track->event[row]; + + j = hio_read8(f); + + len--; + switch (j & 0x03) { + case 0: + row += j >> 2; + break; + case 1: + /* Sanity check */ + if (row < 1 || row + (j >> 2) > 255) + goto err2; + + for (k = 0; k <= (j >> 2); k++) + memcpy(&ev[k], &ev[-1], sizeof (struct xmp_event)); + row += k - 1; + break; + case 2: + /* Sanity check */ + if ((j >> 2) == row) { + goto err2; + } + memcpy(ev, &track->event[j >> 2], sizeof (struct xmp_event)); + break; + case 3: + if (j & MDL_NOTE_FOLLOWS) { + uint8 b = hio_read8(f); + len--; + ev->note = b == 0xff ? XMP_KEY_OFF : b + 12; + } + if (j & MDL_INSTRUMENT_FOLLOWS) + len--, ev->ins = hio_read8(f); + if (j & MDL_VOLUME_FOLLOWS) + len--, ev->vol = hio_read8(f); + if (j & MDL_EFFECT_FOLLOWS) { + len--, k = hio_read8(f); + ev->fxt = LSN(k); + ev->f2t = MSN(k); + } + if (j & MDL_PARAMETER1_FOLLOWS) + len--, ev->fxp = hio_read8(f); + if (j & MDL_PARAMETER2_FOLLOWS) + len--, ev->f2p = hio_read8(f); + break; + } + + row++; + } + + if (row <= 64) + row = 64; + else if (row <= 128) + row = 128; + else row = 256; + + if (libxmp_alloc_track(mod, i, row) < 0) + goto err2; + + memcpy(mod->xxt[i], track, sizeof (struct xmp_track) + + sizeof (struct xmp_event) * (row - 1)); + + mod->xxt[i]->rows = row; + + /* Translate effects */ + for (j = 0; j < row; j++) { + struct xmp_event *ev = &mod->xxt[i]->event[j]; + xlat_fx1(&ev->fxt, &ev->fxp); + xlat_fx2(&ev->f2t, &ev->f2p); + } + } + + free(track); + + return 0; + + err2: + free(track); + err: + return -1; +} + +static int get_chunk_ii(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, k; + int map, last_map; + uint8 buf[40]; + + /* Sanity check */ + if (data->has_ii) { + D_(D_CRIT "duplicate II chunk"); + return -1; + } + data->has_ii = 1; + + mod->ins = hio_read8(f); + D_(D_INFO "Instruments: %d", mod->ins); + + mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument)); + if (mod->xxi == NULL) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + + data->i_index[i] = hio_read8(f); + xxi->nsm = hio_read8(f); + if (hio_read(buf, 1, 32, f) < 32) { + D_(D_CRIT "read error at instrument %d", i); + return -1; + } + buf[32] = 0; + libxmp_instrument_name(mod, i, buf, 32); + + D_(D_INFO "[%2X] %-32.32s %2d", data->i_index[i], xxi->name, xxi->nsm); + + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) + return -1; + + for (j = 0; j < XMP_MAX_KEYS; j++) + xxi->map[j].ins = 0xff; + + for (last_map = j = 0; j < mod->xxi[i].nsm; j++) { + int x; + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = hio_read8(f); + map = hio_read8(f) + 12; + sub->vol = hio_read8(f); + for (k = last_map; k <= map; k++) { + if (k < XMP_MAX_KEYS) + xxi->map[k].ins = j; + } + last_map = map + 1; + + x = hio_read8(f); /* Volume envelope */ + if (j == 0) + data->v_index[i] = x & 0x80 ? x & 0x3f : -1; + if (~x & 0x40) + sub->vol = 0xff; + + mod->xxi[i].sub[j].pan = hio_read8(f) << 1; + + x = hio_read8(f); /* Pan envelope */ + if (j == 0) + data->p_index[i] = x & 0x80 ? x & 0x3f : -1; + if (~x & 0x40) + sub->pan = 0x80; + + x = hio_read16l(f); + if (j == 0) + xxi->rls = x; + + sub->vra = hio_read8(f); /* vibrato rate */ + sub->vde = hio_read8(f) << 1; /* vibrato depth */ + sub->vsw = hio_read8(f); /* vibrato sweep */ + sub->vwf = hio_read8(f); /* vibrato waveform */ + hio_read8(f); /* Reserved */ + + x = hio_read8(f); /* Pitch envelope */ + if (j == 0) + data->f_index[i] = x & 0x80 ? x & 0x3f : -1; + + D_(D_INFO " %2x: V%02x S%02x v%02x p%02x f%02x", + j, sub->vol, sub->sid, data->v_index[i], + data->p_index[i], data->f_index[i]); + } + } + + return 0; +} + +static int get_chunk_is(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + uint8 buf[64]; + uint8 x; + + /* Sanity check */ + if (data->has_is) { + D_(D_CRIT "duplicate IS chunk"); + return -1; + } + data->has_is = 1; + + mod->smp = hio_read8(f); + mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample)); + if (mod->xxs == NULL) + return -1; + m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data)); + if (m->xtra == NULL) + return -1; + + data->packinfo = (int *) calloc(mod->smp, sizeof(int)); + if (data->packinfo == NULL) + return -1; + + D_(D_INFO "Sample infos: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + int c5spd; + + data->s_index[i] = hio_read8(f); /* Sample number */ + if (hio_read(buf, 1, 32, f) < 32) { + D_(D_CRIT "read error at sample %d", i); + return -1; + } + buf[32] = 0; + libxmp_copy_adjust(xxs->name, buf, 31); + + hio_seek(f, 8, SEEK_CUR); /* Sample filename */ + + c5spd = hio_read32l(f); + + xxs->len = hio_read32l(f); + xxs->lps = hio_read32l(f); + xxs->lpe = hio_read32l(f); + + /* Sanity check */ + if (xxs->len < 0 || xxs->lps < 0 || + xxs->lps > xxs->len || xxs->lpe > (xxs->len - xxs->lps)) { + D_(D_CRIT "invalid sample %d - len:%d s:%d l:%d", + i, xxs->len, xxs->lps, xxs->lpe); + return -1; + } + + xxs->flg = xxs->lpe > 0 ? XMP_SAMPLE_LOOP : 0; + xxs->lpe = xxs->lps + xxs->lpe; + + m->xtra[i].c5spd = (double)c5spd; + + hio_read8(f); /* Volume in DMDL 0.0 */ + x = hio_read8(f); + if (x & 0x01) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + xxs->flg |= (x & 0x02) ? XMP_SAMPLE_LOOP_BIDIR : 0; + data->packinfo[i] = (x & 0x0c) >> 2; + + D_(D_INFO "[%2X] %-32.32s %05x%c %05x %05x %c %6d %d", + data->s_index[i], xxs->name, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + c5spd, data->packinfo[i]); + } + + return 0; +} + +static int get_chunk_i0(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + uint8 buf[64]; + uint8 x; + + /* Sanity check */ + if (data->has_ii || data->has_is) { + D_(D_CRIT "duplicate IS (0.0) chunk"); + return -1; + } + data->has_ii = 1; + data->has_is = 1; + + mod->ins = mod->smp = hio_read8(f); + + D_(D_INFO "Instruments (0.0): %d", mod->ins); + + if (libxmp_init_instrument(m) < 0) + return -1; + + data->packinfo = (int *) calloc(mod->smp, sizeof(int)); + if (data->packinfo == NULL) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs = &mod->xxs[i]; + int c5spd; + + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + sub->sid = data->i_index[i] = data->s_index[i] = hio_read8(f); + + if (hio_read(buf, 1, 32, f) < 32) { + D_(D_CRIT "read error at instrument %d", i); + return -1; + } + buf[32] = 0; + hio_seek(f, 8, SEEK_CUR); /* Sample filename */ + libxmp_instrument_name(mod, i, buf, 32); + + c5spd = hio_read16l(f); + + xxs->len = hio_read32l(f); + xxs->lps = hio_read32l(f); + xxs->lpe = hio_read32l(f); + + /* Sanity check */ + if (xxs->len < 0 || xxs->lps < 0 || + xxs->lps > xxs->len || xxs->lpe > (xxs->len - xxs->lps)) { + D_(D_CRIT "invalid sample %d - len:%d s:%d l:%d", + i, xxs->len, xxs->lps, xxs->lpe); + return -1; + } + + xxs->flg = xxs->lpe > 0 ? XMP_SAMPLE_LOOP : 0; + xxs->lpe = xxs->lps + xxs->lpe; + + sub->vol = hio_read8(f); /* Volume */ + sub->pan = 0x80; + + m->xtra[i].c5spd = (double)c5spd; + + x = hio_read8(f); + if (x & 0x01) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + xxs->flg |= (x & 0x02) ? XMP_SAMPLE_LOOP_BIDIR : 0; + data->packinfo[i] = (x & 0x0c) >> 2; + + D_(D_INFO "[%2X] %-32.32s %5d V%02x %05x%c %05x %05x %d", + data->i_index[i], xxi->name, c5spd, sub->vol, + xxs->len, xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, data->packinfo[i]); + } + + return 0; +} + +static int get_chunk_sa(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, len, size_bound; + uint8 *smpbuf = NULL, *buf; + int smpbuf_alloc = -1; + int left = hio_size(f) - hio_tell(f); + + /* Sanity check */ + if (data->has_sa || !data->has_is || data->packinfo == NULL) { + D_(D_CRIT "duplicate SA chunk or missing IS chunk"); + return -1; + } + data->has_sa = 1; + + if (size < left) + left = size; + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + + len = xxs->len; + if (xxs->flg & XMP_SAMPLE_16BIT) + len <<= 1; + + /* Bound the packed sample data size before trying to allocate RAM for it... */ + switch (data->packinfo[i]) { + case 0: + size_bound = len; + break; + case 1: + /* See unpack_sample8: each byte packs to 5 bits minimum. */ + size_bound = (len >> 3) * 5; + break; + case 2: + /* See unpack_sample16: each upper byte packs to 5 bits minimum, lower bytes are not packed. */ + size_bound = (len >> 4) * 13; + break; + default: + /* Sanity check */ + D_(D_CRIT "sample %d invalid pack %d", i, data->packinfo[i]); + goto err2; + } + + /* Sanity check */ + if (left < size_bound) { + D_(D_CRIT "sample %d (pack=%d) requested >=%d bytes, only %d available", + i, data->packinfo[i], size_bound, left); + goto err2; + } + + if (len > smpbuf_alloc) { + uint8 *tmp = (uint8 *) realloc(smpbuf, len); + if (!tmp) + goto err2; + + smpbuf = tmp; + smpbuf_alloc = len; + } + + switch (data->packinfo[i]) { + case 0: + if (hio_read(smpbuf, 1, len, f) < len) { + D_(D_CRIT "sample %d read error (no pack)", i); + goto err2; + } + left -= len; + break; + case 1: + len = hio_read32l(f); + /* Sanity check */ + if (xxs->flg & XMP_SAMPLE_16BIT) + goto err2; + if (len <= 0 || len > 0x80000) /* Max compressed sample size */ + goto err2; + if ((buf = (uint8 *)malloc(len + 4)) == NULL) + goto err2; + if (hio_read(buf, 1, len, f) != len) { + D_(D_CRIT "sample %d read error (8-bit)", i); + goto err3; + } + /* The unpack function may read slightly beyond the end. */ + buf[len] = buf[len + 1] = buf[len + 2] = buf[len + 3] = 0; + if (unpack_sample8(smpbuf, buf, len, xxs->len) < 0) { + D_(D_CRIT "sample %d unpack error (8-bit)", i); + goto err3; + } + free(buf); + left -= len + 4; + break; + case 2: + len = hio_read32l(f); + /* Sanity check */ + if (~xxs->flg & XMP_SAMPLE_16BIT) + goto err2; + if (len <= 0 || len > MAX_SAMPLE_SIZE) + goto err2; + if ((buf = (uint8 *)malloc(len + 4)) == NULL) + goto err2; + if (hio_read(buf, 1, len, f) != len) { + D_(D_CRIT "sample %d read error (16-bit)", i); + goto err3; + } + /* The unpack function may read slightly beyond the end. */ + buf[len] = buf[len + 1] = buf[len + 2] = buf[len + 3] = 0; + if (unpack_sample16(smpbuf, buf, len, xxs->len) < 0) { + D_(D_CRIT "sample %d unpack error (16-bit)", i); + goto err3; + } + free(buf); + left -= len + 4; + break; + } + + if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD, xxs, (char *)smpbuf) < 0) + goto err2; + } + + free(smpbuf); + return 0; + err3: + free(buf); + err2: + free(smpbuf); + return -1; +} + +static int get_chunk_ve(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->v_env) { + D_(D_CRIT "duplicate VE chunk"); + return -1; + } + + if ((data->v_envnum = hio_read8(f)) == 0) + return 0; + + D_(D_INFO "Vol envelopes: %d", data->v_envnum); + + data->v_env = (struct mdl_envelope *) calloc(data->v_envnum, sizeof(struct mdl_envelope)); + if (data->v_env == NULL) { + return -1; + } + + for (i = 0; i < data->v_envnum; i++) { + data->v_env[i].num = hio_read8(f); + hio_read(data->v_env[i].data, 1, 30, f); + data->v_env[i].sus = hio_read8(f); + data->v_env[i].loop = hio_read8(f); + } + + return 0; +} + +static int get_chunk_pe(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->p_env) { + D_(D_CRIT "duplicate PE chunk"); + return -1; + } + + if ((data->p_envnum = hio_read8(f)) == 0) + return 0; + + D_(D_INFO "Pan envelopes: %d", data->p_envnum); + + data->p_env = (struct mdl_envelope *) calloc(data->p_envnum, sizeof(struct mdl_envelope)); + if (data->p_env == NULL) { + return -1; + } + + for (i = 0; i < data->p_envnum; i++) { + data->p_env[i].num = hio_read8(f); + hio_read(data->p_env[i].data, 1, 30, f); + data->p_env[i].sus = hio_read8(f); + data->p_env[i].loop = hio_read8(f); + } + + return 0; +} + +static int get_chunk_fe(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->f_env) { + D_(D_CRIT "duplicate FE chunk"); + return -1; + } + + if ((data->f_envnum = hio_read8(f)) == 0) + return 0; + + D_(D_INFO "Pitch envelopes: %d", data->f_envnum); + + data->f_env = (struct mdl_envelope *) calloc(data->f_envnum, sizeof(struct mdl_envelope)); + if (data->f_env == NULL) { + return -1; + } + + for (i = 0; i < data->f_envnum; i++) { + data->f_env[i].num = hio_read8(f); + hio_read(data->f_env[i].data, 1, 30, f); + data->f_env[i].sus = hio_read8(f); + data->f_env[i].loop = hio_read8(f); + } + + return 0; +} + + +static int mdl_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i, j, k, l; + char buf[8]; + struct local_data data; + int retval = 0; + + LOAD_INIT(); + + memset(&data, 0, sizeof (struct local_data)); + + /* Check magic and get version */ + hio_read32b(f); + if (hio_read(buf, 1, 1, f) < 1) + return -1; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFFoid chunk IDs */ + libxmp_iff_register(handle, "IN", get_chunk_in); /* Module info */ + libxmp_iff_register(handle, "TR", get_chunk_tr); /* Tracks */ + libxmp_iff_register(handle, "SA", get_chunk_sa); /* Sampled data */ + libxmp_iff_register(handle, "VE", get_chunk_ve); /* Volume envelopes */ + libxmp_iff_register(handle, "PE", get_chunk_pe); /* Pan envelopes */ + libxmp_iff_register(handle, "FE", get_chunk_fe); /* Pitch envelopes */ + + if (MSN(*buf)) { + libxmp_iff_register(handle, "II", get_chunk_ii); /* Instruments */ + libxmp_iff_register(handle, "PA", get_chunk_pa); /* Patterns */ + libxmp_iff_register(handle, "IS", get_chunk_is); /* Sample info */ + } else { + libxmp_iff_register(handle, "PA", get_chunk_p0); /* Old 0.0 patterns */ + libxmp_iff_register(handle, "IS", get_chunk_i0); /* Old 0.0 Sample info */ + } + + /* MDL uses a IFF-style file format with 16 bit IDs and little endian + * 32 bit chunk size. There's only one chunk per data type (i.e. one + * big chunk for all samples). + */ + libxmp_iff_id_size(handle, 2); + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + libxmp_set_type(m, "Digitrakker MDL %d.%d", MSN(*buf), LSN(*buf)); + + m->volbase = 0xff; + m->c4rate = C4_NTSC_RATE; + + data.v_envnum = data.p_envnum = data.f_envnum = 0; + data.s_index = (int *) calloc(256, sizeof(int)); + data.i_index = (int *) calloc(256, sizeof(int)); + data.v_index = (int *) malloc(256 * sizeof(int)); + data.p_index = (int *) malloc(256 * sizeof(int)); + data.f_index = (int *) malloc(256 * sizeof(int)); + if (!data.s_index || !data.i_index || !data.v_index || !data.p_index || !data.f_index) { + goto err; + } + + for (i = 0; i < 256; i++) { + data.v_index[i] = data.p_index[i] = data.f_index[i] = -1; + } + + /* Load IFFoid chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + retval = -1; + goto err; + } + + libxmp_iff_release(handle); + + /* Reindex instruments */ + for (i = 0; i < mod->trk; i++) { + for (j = 0; j < mod->xxt[i]->rows; j++) { + struct xmp_event *e = &mod->xxt[i]->event[j]; + + for (l = 0; l < mod->ins; l++) { + if (e->ins && e->ins == data.i_index[l]) { + e->ins = l + 1; + break; + } + } + } + } + + /* Reindex envelopes, etc. */ + for (i = 0; i < mod->ins; i++) { + fix_env(i, &mod->xxi[i].aei, data.v_env, data.v_index, data.v_envnum); + fix_env(i, &mod->xxi[i].pei, data.p_env, data.p_index, data.p_envnum); + fix_env(i, &mod->xxi[i].fei, data.f_env, data.f_index, data.f_envnum); + + for (j = 0; j < mod->xxi[i].nsm; j++) { + for (k = 0; k < mod->smp; k++) { + if (mod->xxi[i].sub[j].sid == data.s_index[k]) { + mod->xxi[i].sub[j].sid = k; + /*libxmp_c2spd_to_note(data.c2spd[k], + &mod->xxi[i].sub[j].xpo, &mod->xxi[i].sub[j].fin);*/ + break; + } + } + } + } + + err: + free(data.f_index); + free(data.p_index); + free(data.v_index); + free(data.i_index); + free(data.s_index); + + free(data.v_env); + free(data.p_env); + free(data.f_env); + + free(data.packinfo); + + m->quirk |= QUIRKS_FT2 | QUIRK_KEYOFF; + m->read_event_type = READ_EVENT_FT2; + + return retval; +} diff --git a/thirdparty/libxmp/src/loaders/med.h b/thirdparty/libxmp/src/loaders/med.h new file mode 100644 index 0000000..93a77aa --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med.h @@ -0,0 +1,334 @@ +#ifndef LIBXMP_MED_H +#define LIBXMP_MED_H + +#include "../common.h" +#include "../hio.h" + +#define MMD_INST_TYPES 9 + +#ifdef DEBUG +extern const char *const mmd_inst_type[]; +#endif + +/* Structures as defined in the MED/OctaMED MMD0 and MMD1 file formats, + * revision 1, described by Teijo Kinnunen in Apr 25 1992 + */ + +struct PlaySeq { + char name[32]; /* (0) 31 chars + \0 */ + uint32 reserved[2]; /* (32) for possible extensions */ + uint16 length; /* (40) # of entries */ + uint16 seq[1]; /* (42) block numbers.. */ +}; + + +struct MMD0sample { + uint16 rep, replen; /* offs: 0(s), 2(s) */ + uint8 midich; /* offs: 4(s) */ + uint8 midipreset; /* offs: 5(s) */ + uint8 svol; /* offs: 6(s) */ + int8 strans; /* offs: 7(s) */ +}; + + +struct MMD0song { + struct MMD0sample sample[63]; /* 63 * 8 bytes = 504 bytes */ + uint16 numblocks; /* offs: 504 */ + uint16 songlen; /* offs: 506 */ + uint8 playseq[256]; /* offs: 508 */ + uint16 deftempo; /* offs: 764 */ + int8 playtransp; /* offs: 766 */ +#define FLAG_FILTERON 0x1 /* the hardware audio filter is on */ +#define FLAG_JUMPINGON 0x2 /* mouse pointer jumping on */ +#define FLAG_JUMP8TH 0x4 /* ump every 8th line (not in OctaMED Pro) */ +#define FLAG_INSTRSATT 0x8 /* sng+samples indicator (not useful in MMDs) */ +#define FLAG_VOLHEX 0x10 /* volumes are HEX */ +#define FLAG_STSLIDE 0x20 /* use ST/NT/PT compatible sliding */ +#define FLAG_8CHANNEL 0x40 /* this is OctaMED 5-8 channel song */ +#define FLAG_SLOWHQ 0X80 /* HQ V2-4 compatibility mode */ + uint8 flags; /* offs: 767 */ +#define FLAG2_BMASK 0x1F /* (bits 0-4) BPM beat length (in lines) */ +#define FLAG2_BPM 0x20 /* BPM mode on */ +#define FLAG2_MIX 0x80 /* Module uses mixing */ + uint8 flags2; /* offs: 768 */ + uint8 tempo2; /* offs: 769 */ + uint8 trkvol[16]; /* offs: 770 */ + uint8 mastervol; /* offs: 786 */ + uint8 numsamples; /* offs: 787 */ +}; /* length = 788 bytes */ + + +/* This structure is exactly as long as the MMDsong structure. Common fields + * are located at same offsets. You can also see, that there's a lot of room + * for expansion in this structure. + */ +struct MMD2song { + struct MMD0sample sample[63]; + uint16 numblocks; + uint16 songlen; /* NOTE: number of sections in MMD2 */ + struct PlaySeq **playseqtable; + uint16 *sectiontable; /* UWORD section numbers */ + uint8 *trackvols; /* UBYTE track volumes */ + uint16 numtracks; /* max. number of tracks in the song + * (also the number of entries in + * 'trackvols' table) */ + uint16 numpseqs; /* number of PlaySeqs in 'playseqtable' */ + int8 *trackpans; /* NULL means 'all centered */ +#define FLAG3_STEREO 0x1 /* Mixing in stereo */ +#define FLAG3_FREEPAN 0x2 /* Mixing flag: free pan */ + uint32 flags3; /* see defs below */ + uint16 voladj; /* volume adjust (%), 0 means 100 */ + uint16 channels; /* mixing channels, 0 means 4 */ + uint8 mix_echotype; /* 0 = nothing, 1 = normal, 2 = cross */ + uint8 mix_echodepth; /* 1 - 6, 0 = default */ + uint16 mix_echolen; /* echo length in milliseconds */ + int8 mix_stereosep; /* stereo separation */ + uint8 pad0[223]; /* reserved for future expansion */ +/* Fields below are MMD0/MMD1-compatible (except pad1[]) */ + uint16 deftempo; + int8 playtransp; + uint8 flags; + uint8 flags2; + uint8 tempo2; + uint8 pad1[16]; /* used to be trackvols, in MMD2 reserved */ + uint8 mastervol; + uint8 numsamples; +}; /* length = 788 bytes */ + + +struct MMD0 { + uint32 id; + uint32 modlen; + struct MMD0song *song; + uint16 psecnum; /* MMD2 only */ + uint16 pseq; /* MMD2 only */ + struct MMD0Block **blockarr; +#define MMD_LOADTOFASTMEM 0x1 + uint8 mmdflags; /* MMD2 only */ + uint8 reserved[3]; + struct InstrHdr **smplarr; + uint32 reserved2; + struct MMD0exp *expdata; + uint32 reserved3; + uint16 pstate; /* some data for the player routine */ + uint16 pblock; + uint16 pline; + uint16 pseqnum; + int16 actplayline; + uint8 counter; + uint8 extra_songs; /* number of songs - 1 */ +}; /* length = 52 bytes */ + + +struct MMD0Block { + uint8 numtracks, lines; +}; + + +struct BlockCmdPageTable { + uint16 num_pages; + uint16 reserved; + uint16 *page[1]; +}; + + +struct BlockInfo { + uint32 *hlmask; + uint8 *blockname; + uint32 blocknamelen; + struct BlockCmdPageTable *pagetable; + uint32 reserved[5]; +}; + + +struct MMD1Block { + uint16 numtracks; + uint16 lines; + struct BlockInfo *info; +}; + + +struct InstrHdr { + uint32 length; +#define S_16 0x10 /* 16-bit sample */ +#define MD16 0x18 /* 16-bit sample (Aura) */ +#define STEREO 0x20 /* Stereo sample, not interleaved */ + int16 type; + /* Followed by actual data */ +}; + + +struct SynthWF { + uint16 length; /* length in words */ + int8 wfdata[1]; /* the waveform */ +}; + + +struct SynthInstr { + uint32 length; /* length of this struct */ + int16 type; /* -1 or -2 (offs: 4) */ + uint8 defaultdecay; + uint8 reserved[3]; + uint16 rep; + uint16 replen; + uint16 voltbllen; /* offs: 14 */ + uint16 wftbllen; /* offs: 16 */ + uint8 volspeed; /* offs: 18 */ + uint8 wfspeed; /* offs: 19 */ + uint16 wforms; /* offs: 20 */ + uint8 voltbl[128]; /* offs: 22 */ + uint8 wftbl[128]; /* offs: 150 */ + uint32 wf[64]; /* offs: 278 */ +}; + + +/* OctaMED SoundStudio 1 and prior use the InstrExt default_pitch field as a + * default note value for the default note key 'F'. Pressing 'F' will insert a + * note event with this note value. + * + * MED Soundstudio 2 in mix mode treats note 0x01 as a default note event, + * which is emitted by the default note key instead of a regular note event. + * It also makes this more complicated, despite not having changed the file + * format: the user must enter a frequency in Hz instead of a note number, + * where 8363 Hz corresponds to the event C-2. This frequency is converted to a + * note number upon saving the module. Multi-octave instruments do not support + * this feature as they are not supported by MED Soundstudio 2. + * + * This editor-only behavior would be irrelevant, except when default_pitch + * is zero, the player uses the default frequency 22050 Hz instead. This + * results in a note between E-3 and F-3. Since this feature is currently + * implemented in the instrument map, use the mix mode note for F-3 instead. + */ +#define MMD3_DEFAULT_NOTE 53 + +struct InstrExt { + uint8 hold; + uint8 decay; + uint8 suppress_midi_off; + int8 finetune; + /* Below fields saved by >= V5 */ + uint8 default_pitch; +#define SSFLG_LOOP 0x01 /* Loop On/Off */ +#define SSFLG_EXTPSET 0x02 /* Ext. Preset */ +#define SSFLG_DISABLED 0x04 /* Disabled */ +#define SSFLG_PINGPONG 0x08 /* Ping-pong looping */ + uint8 instr_flags; + uint16 long_midi_preset; + /* Below fields saved by >= V5.02 */ + uint8 output_device; + uint8 reserved; + /* Below fields saved by >= V7 */ + uint32 long_repeat; + uint32 long_replen; +}; + + +struct MMDInfo { + struct MMDInfo *next; /* next MMDInfo structure */ + uint16 reserved; + uint16 type; /* data type (1 = ASCII) */ + uint32 length; /* data length in bytes */ + /* data follows... */ +}; + + +struct MMDARexxTrigCmd { + struct MMDARexxTrigCmd *next; /* the next command, or NULL */ + uint8 cmdnum; /* command number (01..FF) */ + uint8 pad; + int16 cmdtype; /* command type (OMACTION_...) */ + char *cmd; /* command, or NULL */ + char *port; /* port, or NULL */ + uint16 cmd_len; /* length of 'cmd' string (without + * term. 0) */ + uint16 port_len; /* length of 'port' string (without + * term. 0) */ +}; /* current (V7) structure size: 20 */ + + +struct MMDARexx { + uint16 res; /* reserved, must be zero! */ + uint16 trigcmdlen; /* size of trigcmd entries + * (MUST be used!!) */ + struct MMDARexxTrigCmd *trigcmd; /* chain of MMDARexxTrigCmds or NULL */ +}; + + +struct MMDMIDICmd3x { + uint8 struct_vers; /* current version = 0 */ + uint8 pad; + uint16 num_of_settings; /* number of Cmd3x settings + * (currently set to 15) */ + uint8 *ctrlr_types; /* controller types */ + uint16 *ctrlr_numbers; /* controller numbers */ +}; + + +struct MMDInstrInfo { + uint8 name[40]; +}; + +struct MMD0exp { + struct MMD0 *nextmod; /* pointer to the next module */ + struct InstrExt *exp_smp; /* pointer to InstrExt */ + uint16 s_ext_entries; /* size of InstrExt structure array */ + uint16 s_ext_entrsz; /* size of each InstrExt structure */ + uint8 *annotxt; /* pointer to the annotation text */ + uint32 annolen; /* length of 'annotxt' */ + struct MMDInstrInfo *iinfo; /* pointer to MMDInstrInfo */ + uint16 i_ext_entries; /* size of MMDInstrInfo struct array */ + uint16 i_ext_entrsz; /* size of each MMDInstrInfo struct */ + uint32 jumpmask; /* mouse pointer jump control */ + uint16 *rgbtable; /* screen colors */ + uint8 channelsplit[4]; /* channel splitting control */ + struct NotationInfo *n_info; /* info for the notation editor */ + uint8 *songname; /* song name of the current song */ + uint32 songnamelen; /* song name length */ + struct MMDDumpData *dumps; /* MIDI dump data */ + struct MMDInfo *mmdinfo; /* more information about the song */ + struct MMDARexx *mmdrexx; /* embedded ARexx commands */ + struct MMDMIDICmd3x *mmdcmd3x; /* settings for command 3x */ + uint32 reserved2[3]; /* future expansion fields */ + uint32 tag_end; +}; + + +struct NotationInfo { + uint8 n_of_sharps; /* number of sharps or flats */ +#define NFLG_FLAT 1 +#define NFLG_3_4 2 + uint8 flags; + int16 trksel[5]; /* number of the selected track */ + uint8 trkshow[16]; /* tracks shown */ + uint8 trkghost[16]; /* tracks ghosted */ + int8 notetr[63]; /* note transpose for each instrument */ + uint8 pad; +}; + + +struct MMDDumpData { + uint16 numdumps; + uint16 reserved[3]; +}; + + +struct MMDDump { + uint32 length; /* length of the MIDI message dump */ + uint8 *data; /* pointer to MIDI dump data */ + uint16 ext_len; /* MMDDump struct extension length */ + /* if ext_len >= 20: */ + uint8 name[20]; /* name of the dump */ +}; + +extern const int mmd_num_oct[6]; + +void mmd_xlat_fx(struct xmp_event *, int, int, int, int); +int mmd_alloc_tables(struct module_data *, int, struct SynthInstr *); + +int mmd_load_instrument(HIO_HANDLE *, struct module_data *, int, int, + struct MMD0exp *, struct InstrExt *, struct MMD0sample *, int); + +void mmd_set_bpm(struct module_data *, int, int, int, int); +void mmd_info_text(HIO_HANDLE *, struct module_data *, int); + +#endif /* LIBXMP_MED_H */ diff --git a/thirdparty/libxmp/src/loaders/med2_load.c b/thirdparty/libxmp/src/loaders/med2_load.c new file mode 100644 index 0000000..706e42d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med2_load.c @@ -0,0 +1,243 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * MED 1.12 is in Fish disk #255 + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_MED2 MAGIC4('M','E','D',2) + +static int med2_test(HIO_HANDLE *, char *, const int); +static int med2_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_med2 = { + "MED 1.12 MED2", + med2_test, + med2_load +}; + + +static int med2_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MED2) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static int med2_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + int sliding; + struct xmp_event *event; + uint8 buf[40]; + + LOAD_INIT(); + + if (hio_read32b(f) != MAGIC_MED2) + return -1; + + libxmp_set_type(m, "MED 1.12 MED2"); + + mod->ins = mod->smp = 32; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* read instrument names */ + hio_read(buf, 1, 40, f); /* skip 0 */ + for (i = 0; i < 31; i++) { + if (hio_read(buf, 1, 40, f) != 40) + return -1; + + libxmp_instrument_name(mod, i, buf, 40); + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + } + + /* read instrument volumes */ + hio_read8(f); /* skip 0 */ + for (i = 0; i < 31; i++) { + mod->xxi[i].sub[0].vol = hio_read8(f); + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].fin = 0; + mod->xxi[i].sub[0].sid = i; + } + + /* read instrument loops */ + hio_read16b(f); /* skip 0 */ + for (i = 0; i < 31; i++) { + mod->xxs[i].lps = hio_read16b(f); + } + + /* read instrument loop length */ + hio_read16b(f); /* skip 0 */ + for (i = 0; i < 31; i++) { + uint32 lsiz = hio_read16b(f); + mod->xxs[i].lpe = mod->xxs[i].lps + lsiz; + mod->xxs[i].flg = lsiz > 1 ? XMP_SAMPLE_LOOP : 0; + } + + mod->chn = 4; + mod->pat = hio_read16b(f); + mod->trk = mod->chn * mod->pat; + + if (hio_read(mod->xxo, 1, 100, f) != 100) + return -1; + + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->pat > 256 || mod->len > 100) + return -1; + + k = hio_read16b(f); + if (k < 1) { + return -1; + } + + mod->spd = 6; + mod->bpm = k; + m->time_factor = MED_TIME_FACTOR; + + hio_read16b(f); /* flags */ + sliding = hio_read16b(f); /* sliding */ + hio_read32b(f); /* jumping mask */ + hio_seek(f, 16, SEEK_CUR); /* rgb */ + + MODULE_INFO(); + + D_(D_INFO "Sliding: %d", sliding); + + if (sliding == 6) + m->quirk |= QUIRK_VSALL | QUIRK_PBALL; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + hio_read32b(f); + + for (j = 0; j < 64; j++) { + for (k = 0; k < 4; k++) { + uint8 x; + event = &EVENT(i, k, j); + event->note = libxmp_period_to_note(hio_read16b(f)); + x = hio_read8(f); + event->ins = x >> 4; + event->fxt = x & 0x0f; + event->fxp = hio_read8(f); + + switch (event->fxt) { + case 0x00: /* arpeggio */ + case 0x01: /* slide up */ + case 0x02: /* slide down */ + case 0x03: /* portamento */ + case 0x04: /* vibrato? */ + case 0x0c: /* volume */ + break; /* ...like protracker */ + case 0x0d: /* volslide */ + case 0x0e: /* volslide */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0f: + event->fxt = FX_S3M_BPM; + break; + } + } + } + } + + /* Load samples */ + + D_(D_INFO "Instruments : %d ", mod->ins); + + for (i = 0; i < 31; i++) { + char path[XMP_MAXPATH]; + char ins_path[256]; + char ins_name[32]; + char name[256]; + HIO_HANDLE *s = NULL; + int found = 0; + + if (libxmp_copy_name_for_fopen(ins_name, mod->xxi[i].name, 32) != 0) + continue; + + libxmp_get_instrument_path(m, ins_path, 256); + if (libxmp_check_filename_case(ins_path, ins_name, name, 256)) { + snprintf(path, XMP_MAXPATH, "%s/%s", ins_path, name); + found = 1; + } + + /* Try the module dir if the instrument path didn't work. */ + if (!found && m->dirname != NULL && + libxmp_check_filename_case(m->dirname, ins_name, name, 256)) { + snprintf(path, XMP_MAXPATH, "%s%s", m->dirname, name); + found = 1; + } + + if (found) { + if ((s = hio_open(path,"rb")) != NULL) { + mod->xxs[i].len = hio_size(s); + } + } + + if (mod->xxs[i].len > 0) { + mod->xxi[i].nsm = 1; + } + + if (!strlen(mod->xxi[i].name) && !mod->xxs[i].len) { + if (s != NULL) { + hio_close(s); + } + continue; + } + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + + if (s != NULL) { + int ret = libxmp_load_sample(m, s, 0, &mod->xxs[i], NULL); + hio_close(s); + if (ret < 0) { + return -1; + } + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/med3_load.c b/thirdparty/libxmp/src/loaders/med3_load.c new file mode 100644 index 0000000..d2b3054 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med3_load.c @@ -0,0 +1,439 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * MED 2.00 is in Fish disk #349 and has a couple of demo modules, get it + * from ftp://ftp.funet.fi/pub/amiga/fish/301-400/ff349 + */ + +#include "loader.h" + +#define MAGIC_MED3 MAGIC4('M','E','D',3) + + +static int med3_test(HIO_HANDLE *, char *, const int); +static int med3_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_med3 = { + "MED 2.00 MED3", + med3_test, + med3_load +}; + +static int med3_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MED3) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + + +#define MASK 0x80000000 + +#define M0F_LINEMSK0F 0x01 +#define M0F_LINEMSK1F 0x02 +#define M0F_FXMSK0F 0x04 +#define M0F_FXMSK1F 0x08 +#define M0F_LINEMSK00 0x10 +#define M0F_LINEMSK10 0x20 +#define M0F_FXMSK00 0x40 +#define M0F_FXMSK10 0x80 + + +/* + * From the MED 2.00 file loading/saving routines by Teijo Kinnunen, 1990 + */ + +static uint8 get_nibble(uint8 *mem, uint16 *nbnum) +{ + uint8 *mloc = mem + (*nbnum / 2), res; + + if(*nbnum & 0x1) + res = *mloc & 0x0f; + else + res = *mloc >> 4; + (*nbnum)++; + + return res; +} + +static uint16 get_nibbles(uint8 *mem, uint16 *nbnum, uint8 nbs) +{ + uint16 res = 0; + + while (nbs--) { + res <<= 4; + res |= get_nibble(mem, nbnum); + } + + return res; +} + +static int unpack_block(struct module_data *m, uint16 bnum, uint8 *from, uint16 convsz) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + uint32 linemsk0 = *((uint32 *)from), linemsk1 = *((uint32 *)from + 1); + uint32 fxmsk0 = *((uint32 *)from + 2), fxmsk1 = *((uint32 *)from + 3); + uint32 *lmptr = &linemsk0, *fxptr = &fxmsk0; + uint16 fromn = 0, lmsk; + uint8 *fromst = from + 16, bcnt, *tmpto; + uint8 *patbuf, *to; + uint32 nibs_left = convsz * 2; + int i, j, trkn = mod->chn; + + /*from += 16;*/ + patbuf = to = (uint8 *) calloc(3, 4 * 64); + if (to == NULL) { + goto err; + } + + for (i = 0; i < 64; i++) { + if (i == 32) { + lmptr = &linemsk1; + fxptr = &fxmsk1; + } + + if (*lmptr & MASK) { + if (trkn / 4 > nibs_left) { + goto err2; + } + nibs_left -= trkn / 4; + + lmsk = get_nibbles(fromst, &fromn, (uint8)(trkn / 4)); + lmsk <<= (16 - trkn); + tmpto = to; + + for (bcnt = 0; bcnt < trkn; bcnt++) { + if (lmsk & 0x8000) { + if (nibs_left < 3) { + goto err2; + } + nibs_left -= 3; + *tmpto = (uint8)get_nibbles(fromst, + &fromn,2); + *(tmpto + 1) = (get_nibble(fromst, + &fromn) << 4); + } + lmsk <<= 1; + tmpto += 3; + } + } + + if (*fxptr & MASK) { + if (trkn / 4 > nibs_left) { + goto err2; + } + nibs_left -= trkn / 4; + + lmsk = get_nibbles(fromst,&fromn,(uint8)(trkn / 4)); + lmsk <<= (16 - trkn); + tmpto = to; + + for (bcnt = 0; bcnt < trkn; bcnt++) { + if (lmsk & 0x8000) { + if (nibs_left < 3) { + goto err2; + } + nibs_left -= 3; + *(tmpto+1) |= get_nibble(fromst, + &fromn); + *(tmpto+2) = (uint8)get_nibbles(fromst, + &fromn,2); + } + lmsk <<= 1; + tmpto += 3; + } + } + to += 3 * trkn; + *lmptr <<= 1; + *fxptr <<= 1; + } + + for (i = 0; i < 64; i++) { + for (j = 0; j < 4; j++) { + event = &EVENT(bnum, j, i); + + event->note = patbuf[i * 12 + j * 3 + 0]; + if (event->note) + event->note += 48; + event->ins = patbuf[i * 12 + j * 3 + 1] >> 4; + if (event->ins) + event->ins++; + event->fxt = patbuf[i * 12 + j * 3 + 1] & 0x0f; + event->fxp = patbuf[i * 12 + j * 3 + 2]; + + switch (event->fxt) { + case 0x00: /* arpeggio */ + case 0x01: /* slide up */ + case 0x02: /* slide down */ + case 0x03: /* portamento */ + case 0x04: /* vibrato? */ + break; + case 0x0c: /* set volume (BCD) */ + event->fxp = MSN(event->fxp) * 10 + + LSN(event->fxp); + break; + case 0x0d: /* volume slides */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0f: /* tempo/break */ + if (event->fxp == 0) + event->fxt = FX_BREAK; + if (event->fxp == 0xff) { + event->fxp = event->fxt = 0; + event->vol = 1; + } else if (event->fxp == 0xfe) { + event->fxp = event->fxt = 0; + } else if (event->fxp == 0xf1) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 3; + } else if (event->fxp == 0xf2) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | 3; + } else if (event->fxp == 0xf3) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | 3; + } else if (event->fxp > 10) { + event->fxt = FX_S3M_BPM; + event->fxp = 125 * event->fxp / 33; + } + break; + default: + event->fxp = event->fxt = 0; + } + } + } + + free(patbuf); + + return 0; + + err2: + free(patbuf); + err: + return -1; +} + + +static int med3_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + uint32 mask; + int transp, sliding; + + LOAD_INIT(); + + hio_read32b(f); + + libxmp_set_type(m, "MED 2.00 MED3"); + + mod->ins = mod->smp = 32; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* read instrument names */ + for (i = 0; i < 32; i++) { + uint8 c, buf[40]; + for (j = 0; j < 40; j++) { + c = hio_read8(f); + buf[j] = c; + if (c == 0) + break; + } + libxmp_instrument_name(mod, i, buf, 32); + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + } + + /* read instrument volumes */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + mod->xxi[i].sub[0].vol = mask & MASK ? hio_read8(f) : 0; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].fin = 0; + mod->xxi[i].sub[0].sid = i; + } + + /* read instrument loops */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + mod->xxs[i].lps = mask & MASK ? hio_read16b(f) : 0; + } + + /* read instrument loop length */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + uint32 lsiz = mask & MASK ? hio_read16b(f) : 0; + mod->xxs[i].len = mod->xxs[i].lps + lsiz; + mod->xxs[i].lpe = mod->xxs[i].lps + lsiz; + mod->xxs[i].flg = lsiz > 1 ? XMP_SAMPLE_LOOP : 0; + } + + mod->chn = 4; + mod->pat = hio_read16b(f); + mod->trk = mod->chn * mod->pat; + + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 256 || mod->pat > 256) + return -1; + + hio_read(mod->xxo, 1, mod->len, f); + mod->spd = hio_read16b(f); + if (mod->spd > 10) { + mod->bpm = 125 * mod->spd / 33; + mod->spd = 6; + } + transp = hio_read8s(f); + hio_read8(f); /* flags */ + sliding = hio_read16b(f); /* sliding */ + hio_read32b(f); /* jumping mask */ + hio_seek(f, 16, SEEK_CUR); /* rgb */ + + /* read midi channels */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + if (mask & MASK) + hio_read8(f); + } + + /* read midi programs */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + if (mask & MASK) + hio_read8(f); + } + + MODULE_INFO(); + + D_(D_INFO "Sliding: %d", sliding); + D_(D_INFO "Play transpose: %d", transp); + + if (sliding == 6) + m->quirk |= QUIRK_VSALL | QUIRK_PBALL; + + for (i = 0; i < 32; i++) + mod->xxi[i].sub[0].xpo = transp; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + uint32 *conv; + uint8 b; + /*uint8 tracks;*/ + uint16 convsz; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + /* TODO: not clear if this should be respected. Later MED + * formats are capable of having different track counts. */ + /*tracks =*/ hio_read8(f); + + b = hio_read8(f); + convsz = hio_read16b(f); + conv = (uint32 *) calloc(1, convsz + 16); + if (conv == NULL) + return -1; + + if (b & M0F_LINEMSK00) + *conv = 0L; + else if (b & M0F_LINEMSK0F) + *conv = 0xffffffff; + else + *conv = hio_read32b(f); + + if (b & M0F_LINEMSK10) + *(conv + 1) = 0L; + else if (b & M0F_LINEMSK1F) + *(conv + 1) = 0xffffffff; + else + *(conv + 1) = hio_read32b(f); + + if (b & M0F_FXMSK00) + *(conv + 2) = 0L; + else if (b & M0F_FXMSK0F) + *(conv + 2) = 0xffffffff; + else + *(conv + 2) = hio_read32b(f); + + if (b & M0F_FXMSK10) + *(conv + 3) = 0L; + else if (b & M0F_FXMSK1F) + *(conv + 3) = 0xffffffff; + else + *(conv + 3) = hio_read32b(f); + + if (hio_read(conv + 4, 1, convsz, f) != convsz) { + free(conv); + return -1; + } + + if (unpack_block(m, i, (uint8 *)conv, convsz) < 0) { + free(conv); + return -1; + } + + free(conv); + } + + /* Load samples */ + + D_(D_INFO "Instruments: %d", mod->ins); + + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + if (~mask & MASK) + continue; + + mod->xxi[i].nsm = 1; + mod->xxs[i].len = hio_read32b(f); + + if (mod->xxs[i].len == 0) + mod->xxi[i].nsm = 0; + + if (hio_read16b(f)) /* type */ + continue; + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x ", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/med4_load.c b/thirdparty/libxmp/src/loaders/med4_load.c new file mode 100644 index 0000000..63e4ea0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med4_load.c @@ -0,0 +1,863 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * MED 2.13 is in Fish disk #424 and has a couple of demo modules, get it + * from ftp://ftp.funet.fi/pub/amiga/fish/401-500/ff424. Alex Van Starrex's + * HappySong MED4 is in ff401. MED 3.00 is in ff476. + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +#define MAGIC_MED4 MAGIC4('M','E','D',4) +#undef MED4_DEBUG + +static int med4_test(HIO_HANDLE *, char *, const int); +static int med4_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_med4 = { + "MED 2.10 MED4", + med4_test, + med4_load +}; + +static int med4_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MED4) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static void fix_effect(struct xmp_event *event, int hexvol) +{ + switch (event->fxt) { + case 0x00: /* arpeggio */ + case 0x01: /* slide up */ + case 0x02: /* slide down */ + case 0x03: /* portamento */ + case 0x04: /* vibrato? */ + break; + case 0x0c: /* set volume (BCD) */ + if (!hexvol) { + event->fxp = MSN(event->fxp) * 10 + LSN(event->fxp); + } + break; + case 0x0d: /* volume slides */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0f: /* tempo/break */ + if (event->fxp == 0) + event->fxt = FX_BREAK; + if (event->fxp == 0xff) { + event->fxp = event->fxt = 0; + event->vol = 1; + } else if (event->fxp == 0xf1) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 3; + } else if (event->fxp == 0xf2) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | 3; + } else if (event->fxp == 0xf3) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | 3; + } else if (event->fxp > 0xf0) { + event->fxp = event->fxt = 0; + } else if (event->fxp > 10) { + event->fxt = FX_S3M_BPM; + event->fxp = 125 * event->fxp / 33; + } + break; + default: + event->fxp = event->fxt = 0; + } +} + +struct stream { + HIO_HANDLE* f; + int has_nibble; + uint8 value; +}; + +static inline void stream_init(HIO_HANDLE* f, struct stream* s) +{ + s->f = f; + s->has_nibble = s->value = 0; +} + +static inline unsigned stream_read4(struct stream* s) +{ + s->has_nibble = !s->has_nibble; + if (!s->has_nibble) { + return s->value & 0x0f; + } else { + s->value = hio_read8(s->f); + return s->value >> 4; + } +} + +static inline unsigned stream_read8(struct stream* s) +{ + unsigned a = stream_read4(s); + unsigned b = stream_read4(s); + return (a << 4) | b; +} + +static inline unsigned stream_read12(struct stream* s) +{ + unsigned a = stream_read4(s); + unsigned b = stream_read4(s); + unsigned c = stream_read4(s); + return (a << 8) | (b << 4) | c; +} + +static inline uint16 stream_read16(struct stream* s) +{ + unsigned a = stream_read4(s); + unsigned b = stream_read4(s); + unsigned c = stream_read4(s); + unsigned d = stream_read4(s); + return (a << 12) | (b << 8) | (c << 4) | d; +} + +static inline uint16 stream_read_aligned16(struct stream* s, int bits) +{ + if (bits <= 4) { + return stream_read4(s) << 12; + } + if (bits <= 8) { + return stream_read8(s) << 8; + } + if (bits <= 12) { + return stream_read12(s) << 4; + } + return stream_read16(s); +} + +struct temp_inst { + char name[32]; + int loop_start; + int loop_end; + int volume; + int transpose; +}; + +static int med4_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k, y; + uint8 m0; + uint64 mask; + int transp, masksz; + int32 pos; + int vermaj, vermin; + uint8 trkvol[16], buf[1024]; + struct xmp_event *event; + int flags, hexvol = 0; + int num_ins, num_smp; + int smp_idx; + int tempo; + struct temp_inst temp_inst[64]; + + LOAD_INIT(); + + hio_read32b(f); /* Skip magic */ + + vermaj = 2; + vermin = 10; + + /* + * Check if we have a MEDV chunk at the end of the file + */ + if ((pos = hio_tell(f)) < 0) { + return -1; + } + + hio_seek(f, 0, SEEK_END); + if (hio_tell(f) > 2000) { + hio_seek(f, -1024, SEEK_CUR); + hio_read(buf, 1, 1024, f); + for (i = 0; i < 1013; i++) { + if (!memcmp(buf + i, "MEDV\000\000\000\004", 8)) { + vermaj = *(buf + i + 10); + vermin = *(buf + i + 11); + break; + } + } + } + hio_seek(f, start + pos, SEEK_SET); + + snprintf(mod->type, XMP_NAME_SIZE, "MED %d.%02d MED4", vermaj, vermin); + + m0 = hio_read8(f); + + mask = masksz = 0; + for (i = 0; m0 != 0 && i < 8; i++, m0 <<= 1) { + if (m0 & 0x80) { + mask <<= 8; + mask |= hio_read8(f); + masksz++; + } + } + + /* CID 128662 (#1 of 1): Bad bit shift operation (BAD_SHIFT) + * large_shift: left shifting by more than 63 bits has undefined + * behavior. + */ + if (masksz > 0) { + mask <<= 8 * (sizeof(mask) - masksz); + } + /*printf("m0=%x mask=%x\n", m0, mask);*/ + + /* read instrument names in temporary space */ + + num_ins = 0; + memset(temp_inst, 0, sizeof(temp_inst)); + for (i = 0; mask != 0 && i < 64; i++, mask <<= 1) { + uint8 c, size; + uint16 loop_len = 0; + + if ((int64)mask > 0) + continue; + + num_ins = i + 1; + + /* read flags */ + c = hio_read8(f); + + /* read instrument name */ + size = hio_read8(f); + for (j = 0; j < size; j++) + buf[j] = hio_read8(f); + buf[j] = 0; +#ifdef MED4_DEBUG + printf("%02x %02x %2d [%s]\n", i, c, size, buf); +#endif + + temp_inst[i].volume = 0x40; + + if ((c & 0x01) == 0) + temp_inst[i].loop_start = hio_read16b(f) << 1; + if ((c & 0x02) == 0) + loop_len = hio_read16b(f) << 1; + if ((c & 0x04) == 0) /* ? Tanko2 (MED 3.00 demo) */ + hio_read8(f); + if ((c & 0x08) == 0) /* Tim Newsham's "span" */ + hio_read8(f); + if ((c & 0x30) == 0) + temp_inst[i].volume = hio_read8(f); + if ((c & 0x40) == 0) + temp_inst[i].transpose = hio_read8s(f); + + temp_inst[i].loop_end = temp_inst[i].loop_start + loop_len; + + libxmp_copy_adjust(temp_inst[i].name, buf, 32); + } + + mod->pat = hio_read16b(f); + mod->len = hio_read16b(f); + + if (hio_error(f)) { + return -1; + } + +#ifdef MED4_DEBUG + printf("pat=%x len=%x\n", mod->pat, mod->len); +#endif + if (mod->pat > 256 || mod->len > XMP_MAX_MOD_LENGTH) + return -1; + hio_read(mod->xxo, 1, mod->len, f); + + /* From MED V3.00 docs: + * + * The left proportional gadget controls the primary tempo. It canbe + * 1 - 240. The bigger the number, the faster the speed. Note that + * tempos 1 - 10 are Tracker-compatible (but obsolete, because + * secondary tempo can be used now). + */ + tempo = hio_read16b(f); + if (tempo <= 10) { + mod->spd = tempo; + mod->bpm = 125; + } else { + mod->bpm = 125 * tempo / 33; + } + transp = hio_read8s(f); + flags = hio_read8s(f); + mod->spd = hio_read16b(f); + + if (~flags & 0x20) /* sliding */ + m->quirk |= QUIRK_VSALL | QUIRK_PBALL; + + if (flags & 0x10) /* dec/hex volumes */ + hexvol = 1; + + /* This is just a guess... */ + if (vermaj == 2) /* Happy.med has tempo 5 but loads as 6 */ + mod->spd = flags & 0x20 ? 5 : 6; + + hio_seek(f, 20, SEEK_CUR); + + hio_read(trkvol, 1, 16, f); + hio_read8(f); /* master vol */ + + MODULE_INFO(); + + D_(D_INFO "Play transpose: %d", transp); + + for (i = 0; i < 64; i++) + temp_inst[i].transpose += transp; + + /* Scan patterns to determine number of channels */ + mod->chn = 0; + if ((pos = hio_tell(f)) < 0) { + return -1; + } + + for (i = 0; i < mod->pat; i++) { + int size, plen, chn; + + size = hio_read8(f); /* pattern control block */ + chn = hio_read8(f); + if (chn > mod->chn) + mod->chn = chn; + hio_read8(f); /* skip number of rows */ + plen = hio_read16b(f); + + hio_seek(f, size + plen - 4, SEEK_CUR); + } + + /* Sanity check */ + if (mod->chn > 16) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + hio_seek(f, pos, SEEK_SET); + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + int size, plen, rows; + uint8 ctl[4], chn; + unsigned chmsk; + uint32 linemask[8], fxmask[8], x; + int num_masks; + struct stream stream; + +#ifdef MED4_DEBUG + printf("\n===== PATTERN %d =====\n", i); + printf("offset = %lx\n", hio_tell(f)); +#endif + + size = hio_read8(f); /* pattern control block */ + if ((pos = hio_tell(f)) < 0) { + return -1; + } + chn = hio_read8(f); + if (chn > mod->chn) { + return -1; + } + rows = (int)hio_read8(f) + 1; + plen = hio_read16b(f); +#ifdef MED4_DEBUG + printf("size = %02x\n", size); + printf("chn = %01x\n", chn); + printf("rows = %01x\n", rows); + printf("plen = %04x\n", plen); +#endif + /* read control byte */ + for (j = 0; j < 4; j++) { + if (rows > j * 64) + ctl[j] = hio_read8(f); + else + break; +#ifdef MED4_DEBUG + printf("ctl[%d] = %02x\n", j, ctl[j]); + +#endif + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + /* initialize masks */ + for (y = 0; y < 8; y++) { + linemask[y] = 0; + fxmask[y] = 0; + } + + /* read masks */ + num_masks = 0; + for (y = 0; y < 8; y++) { + if (rows > y * 32) { + int c = ctl[y / 2]; + int s = 4 * (y % 2); + linemask[y] = c & (0x80 >> s) ? ~0 : + c & (0x40 >> s) ? 0 : hio_read32b(f); + fxmask[y] = c & (0x20 >> s) ? ~0 : + c & (0x10 >> s) ? 0 : hio_read32b(f); + num_masks++; +#ifdef MED4_DEBUG + printf("linemask[%d] = %08x\n", y, linemask[y]); + printf("fxmask[%d] = %08x\n", y, fxmask[y]); +#endif + } else { + break; + } + } + + hio_seek(f, pos + size, SEEK_SET); + stream_init(f, &stream); + + for (y = 0; y < num_masks; y++) { + + for (j = 0; j < 32; j++) { + int line = y * 32 + j; + + if (line >= rows) + break; + + if (linemask[y] & 0x80000000) { + chmsk = stream_read_aligned16(&stream, chn); + for (k = 0; k < chn; k++, chmsk <<= 1) { + event = &EVENT(i, k, line); + + if (chmsk & 0x8000) { + x = stream_read12(&stream); + event->note = x >> 4; + if (event->note) + event->note += 48; + event->ins = x & 0x0f; + } + } + } + + if (fxmask[y] & 0x80000000) { + chmsk = stream_read_aligned16(&stream, chn); + for (k = 0; k < chn; k++, chmsk <<= 1) { + event = &EVENT(i, k, line); + + if (chmsk & 0x8000) { + x = stream_read12(&stream); + event->fxt = x >> 8; + event->fxp = x & 0xff; + fix_effect(event, hexvol); + } + } + } + +#ifdef MED4_DEBUG + printf("%03d ", line); + for (k = 0; k < 4; k++) { + event = &EVENT(i, k, line); + if (event->note) + printf("%03d", event->note); + else + printf("---"); + printf(" %1x%1x%02x ", + event->ins, event->fxt, event->fxp); + } + printf("\n"); +#endif + + linemask[y] <<= 1; + fxmask[y] <<= 1; + } + + } + hio_seek(f, pos + size + plen, SEEK_SET); + } + + mod->ins = num_ins; + + if (libxmp_med_new_module_extras(m) != 0) + return -1; + + /* + * Load samples + */ + mask = hio_read32b(f); + if (mask == MAGIC4('M','E','D','V')) { + mod->smp = 0; + + if (libxmp_init_instrument(m) < 0) + return -1; + hio_seek(f, -4, SEEK_CUR); + goto parse_iff; + } + mask <<= 32; + mask |= hio_read32b(f); + + mask <<= 1; /* no instrument #0 */ + + /* obtain number of samples */ + if ((pos = hio_tell(f)) < 0) { + return -1; + } + num_smp = 0; + { + int _len, _type; + uint64 _mask = mask; + for (i = 0; _mask != 0 && i < 64; i++, _mask <<= 1) { + if ((int64)_mask > 0) + continue; + + _len = hio_read32b(f); + _type = (int16)hio_read16b(f); + + if (_type == 0 || _type == -2) { + num_smp++; + } else if (_type == -1) { + if (_len < 22) { + D_(D_CRIT "invalid synth %d length", i); + return -1; + } + hio_seek(f, 20, SEEK_CUR); + num_smp += hio_read16b(f); + _len -= 22; + } + + if (_len < 0) { + D_(D_CRIT "invalid sample %d length", i); + return -1; + } + hio_seek(f, _len, SEEK_CUR); + } + } + hio_seek(f, pos, SEEK_SET); + + mod->smp = num_smp; + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + D_(D_INFO "Instruments: %d", mod->ins); + + smp_idx = 0; + for (i = 0; mask != 0 && i < num_ins; i++, mask <<= 1) { + int length, type; + struct SynthInstr synth; + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct med_instrument_extras *ie; + + if ((int64)mask > 0) { + continue; + } + + xxi = &mod->xxi[i]; + + length = hio_read32b(f); + type = (int16)hio_read16b(f); /* instrument type */ + + strncpy((char *)xxi->name, temp_inst[i].name, 32); + xxi->name[31] = '\0'; + + D_(D_INFO "\n[%2X] %-32.32s %d", i, xxi->name, type); + + /* This is very similar to MMD1 synth/hybrid instruments, + * but just different enough to be reimplemented here. + */ + + if (type == -2) { /* Hybrid */ + pos = hio_tell(f); + if (pos < 0) { + return -1; + } + + hio_read32b(f); /* ? - MSH 00 */ + hio_read16b(f); /* ? - ffff */ + hio_read16b(f); /* ? - 0000 */ + hio_read16b(f); /* ? - 0000 */ + synth.rep = hio_read16b(f); /* ? */ + synth.replen = hio_read16b(f); /* ? */ + synth.voltbllen = hio_read16b(f); + synth.wftbllen = hio_read16b(f); + synth.volspeed = hio_read8(f); + synth.wfspeed = hio_read8(f); + synth.wforms = hio_read16b(f); + + /* Sanity check */ + if (synth.voltbllen > 128 || + synth.wftbllen > 128 || + synth.wforms > 256) { + return -1; + } + + hio_read(synth.voltbl, 1, synth.voltbllen, f); + hio_read(synth.wftbl, 1, synth.wftbllen, f); + if (hio_error(f)) + return -1; + + hio_seek(f, pos + hio_read32b(f), SEEK_SET); + length = hio_read32b(f); + type = hio_read16b(f); + + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth.volspeed; + ie->wts = synth.wfspeed; + ie->vtlen = synth.voltbllen; + ie->wtlen = synth.wftbllen; + + sub->pan = 0x80; + sub->vol = temp_inst[i].volume; + sub->xpo = temp_inst[i].transpose; + sub->sid = smp_idx; + sub->fin = 0 /*exp_smp.finetune*/; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = length; + xxs->lps = temp_inst[i].loop_start; + xxs->lpe = temp_inst[i].loop_end; + xxs->flg = temp_inst[i].loop_end > 2 ? + XMP_SAMPLE_LOOP : 0; + + D_(D_INFO " %05x %05x %05x %02x %+03d", + xxs->len, xxs->lps, xxs->lpe, + sub->vol, sub->xpo /*, sub->fin >> 4*/); + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + continue; + } + + if (type == -1) { /* Synthetic */ + pos = hio_tell(f); + if (pos < 0) { + return -1; + } + + hio_read32b(f); /* ? - MSH 00 */ + hio_read16b(f); /* ? - ffff */ + hio_read16b(f); /* ? - 0000 */ + hio_read16b(f); /* ? - 0000 */ + synth.rep = hio_read16b(f); /* ? */ + synth.replen = hio_read16b(f); /* ? */ + synth.voltbllen = hio_read16b(f); + synth.wftbllen = hio_read16b(f); + synth.volspeed = hio_read8(f); + synth.wfspeed = hio_read8(f); + synth.wforms = hio_read16b(f); + + if (synth.wforms == 0xffff) + continue; + + /* Sanity check */ + if (synth.voltbllen > 128 || + synth.wftbllen > 128 || + synth.wforms > 64) { + return -1; + } + + hio_read(synth.voltbl, 1, synth.voltbllen, f); + hio_read(synth.wftbl, 1, synth.wftbllen, f); + + for (j = 0; j < synth.wforms; j++) + synth.wf[j] = hio_read32b(f); + + if (hio_error(f)) + return -1; + + D_(D_INFO " VS:%02x WS:%02x WF:%02x %02x %+03d", + synth.volspeed, synth.wfspeed, + synth.wforms & 0xff, + temp_inst[i].volume, + temp_inst[i].transpose /*, + exp_smp.finetune*/); + + if (libxmp_med_new_instrument_extras(&mod->xxi[i]) != 0) + return -1; + + mod->xxi[i].nsm = synth.wforms; + if (libxmp_alloc_subinstrument(mod, i, synth.wforms) < 0) + return -1; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth.volspeed; + ie->wts = synth.wfspeed; + ie->vtlen = synth.voltbllen; + ie->wtlen = synth.wftbllen; + + for (j = 0; j < synth.wforms; j++) { + /* Sanity check */ + if (smp_idx >= num_smp) { + return -1; + } + + sub = &xxi->sub[j]; + + sub->pan = 0x80; + sub->vol = 64; + sub->xpo = -24; + sub->sid = smp_idx; + sub->fin = 0 /*exp_smp.finetune*/; + + hio_seek(f, pos + synth.wf[j], SEEK_SET); + + xxs = &mod->xxs[smp_idx]; + + xxs->len = hio_read16b(f) * 2; + xxs->lps = 0; + xxs->lpe = xxs->len; + xxs->flg = XMP_SAMPLE_LOOP; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) { + return -1; + } + + smp_idx++; + } + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + hio_seek(f, pos + length, SEEK_SET); + continue; + } + + if (type != 0) { + hio_seek(f, length, SEEK_CUR); + continue; + } + + /* instr type is sample */ + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + sub->vol = temp_inst[i].volume; + sub->pan = 0x80; + sub->xpo = temp_inst[i].transpose; + sub->sid = smp_idx; + + /* Sanity check */ + if (smp_idx >= mod->smp) + return -1; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = length; + xxs->lps = temp_inst[i].loop_start; + xxs->lpe = temp_inst[i].loop_end; + xxs->flg = temp_inst[i].loop_end > 2 ? XMP_SAMPLE_LOOP : 0; + + D_(D_INFO " %04x %04x %04x %c V%02x %+03d", + xxs->len, mod->xxs[smp_idx].lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->xpo); + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + /* Limit range to 3 octave (see MED.El toro) */ + for (j = 0; j < 9; j++) { + for (k = 0; k < 12; k++) { + int xpo = 0; + + if (j < 4) + xpo = 12 * (4 - j); + else if (j > 6) + xpo = -12 * (j - 6); + + xxi->map[12 * j + k].xpo = xpo; + } + } + + smp_idx++; + } + + /* Not sure what this was supposed to be, but it isn't present in + * Synth-a-sysmic.med or any other MED4 module on ModLand. */ + /*hio_read16b(f);*/ /* unknown */ + + /* IFF-like section */ +parse_iff: + while (!hio_eof(f)) { + int32 id, size, s2, ver; + + if ((id = hio_read32b(f)) <= 0) + break; + + if ((size = hio_read32b(f)) <= 0) + break; + + switch (id) { + case MAGIC4('M','E','D','V'): + ver = hio_read32b(f); + size -= 4; + vermaj = (ver & 0xff00) >> 8; + vermin = (ver & 0xff); + D_(D_INFO "MED Version: %d.%0d", vermaj, vermin); + break; + case MAGIC4('A','N','N','O'): + /* annotation */ + s2 = size < 1023 ? size : 1023; + if ((m->comment = (char *) malloc(s2 + 1)) != NULL) { + int read_len = hio_read(m->comment, 1, s2, f); + m->comment[read_len] = '\0'; + + D_(D_INFO "Annotation: %s", m->comment); + size -= s2; + } + break; + case MAGIC4('H','L','D','C'): + /* hold & decay */ + break; + } + + hio_seek(f, size, SEEK_CUR); + } + + m->read_event_type = READ_EVENT_MED; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mfp_load.c b/thirdparty/libxmp/src/loaders/mfp_load.c new file mode 100644 index 0000000..7aa2c6a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mfp_load.c @@ -0,0 +1,253 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * A module packer created by Shaun Southern. Samples are stored in a + * separate file. File prefixes are mfp for song and smp for samples. For + * more information see http://www.exotica.org.uk/wiki/Magnetic_Fields_Packer + */ + +#include "loader.h" + +static int mfp_test(HIO_HANDLE *, char *, const int); +static int mfp_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mfp = { + "Magnetic Fields Packer", + mfp_test, + mfp_load +}; + +static int mfp_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 buf[384]; + int i, len, lps, lsz; + + if (HIO_HANDLE_TYPE(f) != HIO_HANDLE_TYPE_FILE) + return -1; + + if (hio_read(buf, 1, 384, f) < 384) + return -1; + + /* check restart byte */ + if (buf[249] != 0x7f) + return -1; + + for (i = 0; i < 31; i++) { + /* check size */ + len = readmem16b(buf + i * 8); + if (len > 0x7fff) + return -1; + + /* check finetune */ + if (buf[i * 8 + 2] & 0xf0) + return -1; + + /* check volume */ + if (buf[i * 8 + 3] > 0x40) + return -1; + + /* check loop start */ + lps = readmem16b(buf + i * 8 + 4); + if (lps > len) + return -1; + + /* check loop size */ + lsz = readmem16b(buf + i * 8 + 6); + if (lps + lsz - 1 > len) + return -1; + + if (len > 0 && lsz == 0) + return -1; + } + + if (buf[248] != readmem16b(buf + 378)) + return -1; + + if (readmem16b(buf + 378) != readmem16b(buf + 380)) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static int mfp_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k, x, y; + struct xmp_event *event; + char smp_filename[XMP_MAXPATH]; + HIO_HANDLE *s; + int size1 /*, size2*/; + int pat_addr, pat_table[128][4]; + uint8 buf[1024], mod_event[4]; + int row; + + LOAD_INIT(); + + libxmp_set_type(m, "Magnetic Fields Packer"); + MODULE_INFO(); + + mod->chn = 4; + mod->ins = mod->smp = 31; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < 31; i++) { + int loop_size; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = 2 * hio_read16b(f); + mod->xxi[i].sub[0].fin = (int8)(hio_read8(f) << 4); + mod->xxi[i].sub[0].vol = hio_read8(f); + mod->xxs[i].lps = 2 * hio_read16b(f); + loop_size = hio_read16b(f); + + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * loop_size; + mod->xxs[i].flg = loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + D_(D_INFO "[%2X] %04x %04x %04x %c V%02x %+d", + i, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin >> 4); + } + + mod->len = mod->pat = hio_read8(f); + hio_read8(f); /* restart */ + + for (i = 0; i < 128; i++) { + mod->xxo[i] = hio_read8(f); + } + + if (hio_error(f)) { + return -1; + } + + mod->trk = mod->pat * mod->chn; + + /* Read and convert patterns */ + + if (libxmp_init_pattern(mod) < 0) + return -1; + + size1 = hio_read16b(f); + /* size2 = */ hio_read16b(f); + + for (i = 0; i < size1; i++) { /* Read pattern table */ + for (j = 0; j < 4; j++) { + pat_table[i][j] = hio_read16b(f); + } + } + + D_(D_INFO "Stored patterns: %d ", mod->pat); + + pat_addr = hio_tell(f); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 4; j++) { + size_t len; + hio_seek(f, pat_addr + pat_table[i][j], SEEK_SET); + + len = hio_read(buf, 1, 1024, f); + + for (row = k = 0; k < 4; k++) { + for (x = 0; x < 4; x++) { + for (y = 0; y < 4; y++, row++) { + event = &EVENT(i, j, row); + + if (k >= len || + buf[k] + x >= len || + buf[buf[k] + x] + y >= len || + buf[buf[buf[k] + x] + y] * 2 + 4 > len) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + memcpy(mod_event, &buf[buf[buf[buf[k] + x] + y] * 2], 4); + libxmp_decode_protracker_event(event, mod_event); + } + } + } + } + } + + /* Read samples */ + D_(D_INFO "Loading samples: %d", mod->ins); + + /* first check smp.filename */ + if (strlen(m->basename) < 5 || m->basename[3] != '.') { + D_(D_CRIT "invalid filename %s", m->basename); + goto err; + } + + m->basename[0] = 's'; + m->basename[1] = 'm'; + m->basename[2] = 'p'; + snprintf(smp_filename, XMP_MAXPATH, "%s%s", m->dirname, m->basename); + if ((s = hio_open(smp_filename, "rb")) == NULL) { + /* handle .set filenames like in Kid Chaos*/ + if (strchr(m->basename, '-')) { + char *p = strrchr(smp_filename, '-'); + if (p != NULL) + strcpy(p, ".set"); + } + if ((s = hio_open(smp_filename, "rb")) == NULL) { + D_(D_CRIT "can't open sample file %s", smp_filename); + goto err; + } + } + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, s, SAMPLE_FLAG_FULLREP, + &mod->xxs[mod->xxi[i].sub[0].sid], NULL) < 0) { + free(s); + return -1; + } + } + + hio_close(s); + + m->period_type = PERIOD_MODRNG; + + return 0; + + err: + for (i = 0; i < mod->ins; i++) { + mod->xxi[i].nsm = 0; + memset(&mod->xxs[i], 0, sizeof(struct xmp_sample)); + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mgt_load.c b/thirdparty/libxmp/src/loaders/mgt_load.c new file mode 100644 index 0000000..98900cc --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mgt_load.c @@ -0,0 +1,382 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_MGT MAGIC4(0x00,'M','G','T') +#define MAGIC_MCS MAGIC4(0xbd,'M','C','S') + + +static int mgt_test (HIO_HANDLE *, char *, const int); +static int mgt_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mgt = { + "Megatracker", + mgt_test, + mgt_load +}; + +static int mgt_test(HIO_HANDLE *f, char *t, const int start) +{ + int sng_ptr; + + if (hio_read24b(f) != MAGIC_MGT) + return -1; + hio_read8(f); + if (hio_read32b(f) != MAGIC_MCS) + return -1; + + hio_seek(f, 18, SEEK_CUR); + sng_ptr = hio_read32b(f); + hio_seek(f, start + sng_ptr, SEEK_SET); + + libxmp_read_title(f, t, 32); + + return 0; +} + +static int mgt_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j; + int ver; + int sng_ptr, seq_ptr, ins_ptr, pat_ptr, trk_ptr; + int sdata[64]; + + LOAD_INIT(); + + hio_read24b(f); /* MGT */ + ver = hio_read8(f); + hio_read32b(f); /* MCS */ + + libxmp_set_type(m, "Megatracker MGT v%d.%d", MSN(ver), LSN(ver)); + + mod->chn = hio_read16b(f); + hio_read16b(f); /* number of songs */ + mod->len = hio_read16b(f); + mod->pat = hio_read16b(f); + mod->trk = hio_read16b(f); + mod->ins = mod->smp = hio_read16b(f); + hio_read16b(f); /* reserved */ + hio_read32b(f); /* reserved */ + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS || mod->pat > MAX_PATTERNS || mod->ins > 64) { + return -1; + } + + sng_ptr = hio_read32b(f); + seq_ptr = hio_read32b(f); + ins_ptr = hio_read32b(f); + pat_ptr = hio_read32b(f); + trk_ptr = hio_read32b(f); + hio_read32b(f); /* sample offset */ + hio_read32b(f); /* total smp len */ + hio_read32b(f); /* unpacked trk size */ + + hio_seek(f, start + sng_ptr, SEEK_SET); + + hio_read(mod->name, 1, 32, f); + seq_ptr = hio_read32b(f); + mod->len = hio_read16b(f); + mod->rst = hio_read16b(f); + mod->bpm = hio_read8(f); + mod->spd = hio_read8(f); + hio_read16b(f); /* global volume */ + hio_read8(f); /* master L */ + hio_read8(f); /* master R */ + + /* Sanity check */ + if (mod->len > XMP_MAX_MOD_LENGTH || mod->rst > 255) { + return -1; + } + + for (i = 0; i < mod->chn; i++) { + hio_read16b(f); /* pan */ + } + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + /* Sequence */ + + hio_seek(f, start + seq_ptr, SEEK_SET); + for (i = 0; i < mod->len; i++) { + int pos = hio_read16b(f); + + /* Sanity check */ + if (pos >= mod->pat) { + return -1; + } + mod->xxo[i] = pos; + } + + /* Instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + hio_seek(f, start + ins_ptr, SEEK_SET); + + for (i = 0; i < mod->ins; i++) { + int c2spd, flags; + + if (libxmp_alloc_subinstrument(mod, i , 1) < 0) + return -1; + + hio_read(mod->xxi[i].name, 1, 32, f); + sdata[i] = hio_read32b(f); + mod->xxs[i].len = hio_read32b(f); + + /* Sanity check */ + if (mod->xxs[i].len > MAX_SAMPLE_SIZE) { + return -1; + } + + mod->xxs[i].lps = hio_read32b(f); + mod->xxs[i].lpe = mod->xxs[i].lps + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + c2spd = hio_read32b(f); + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + mod->xxi[i].sub[0].vol = hio_read16b(f) >> 4; + hio_read8(f); /* vol L */ + hio_read8(f); /* vol R */ + mod->xxi[i].sub[0].pan = 0x80; + flags = hio_read8(f); + mod->xxs[i].flg = flags & 0x03 ? XMP_SAMPLE_LOOP : 0; + mod->xxs[i].flg |= flags & 0x02 ? XMP_SAMPLE_LOOP_BIDIR : 0; + mod->xxi[i].sub[0].fin += 0 * hio_read8(f); // FIXME + hio_read8(f); /* unused */ + hio_read8(f); + hio_read8(f); + hio_read8(f); + hio_read16b(f); + hio_read32b(f); + hio_read32b(f); + + mod->xxi[i].nsm = !!mod->xxs[i].len; + mod->xxi[i].sub[0].sid = i; + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x %5d\n", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, c2spd); + } + + /* PATTERN_INIT - alloc extra track*/ + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk); + + /* Tracks */ + + for (i = 1; i < mod->trk; i++) { + int offset, rows; + uint8 b; + + hio_seek(f, start + trk_ptr + i * 4, SEEK_SET); + offset = hio_read32b(f); + hio_seek(f, start + offset, SEEK_SET); + + rows = hio_read16b(f); + + /* Sanity check */ + if (rows > 255) + return -1; + + if (libxmp_alloc_track(mod, i, rows) < 0) + return -1; + + //printf("\n=== Track %d ===\n\n", i); + for (j = 0; j < rows; j++) { + uint8 note; + /* TODO libxmp can't really support the wide effect + * params Megatracker uses right now, but less bad + * conversions of certain effects could be attempted. */ + /* uint8 f2p ;*/ + + b = hio_read8(f); + j += b & 0x03; + + /* Sanity check */ + if (j >= rows) + return -1; + + note = 0; + event = &mod->xxt[i]->event[j]; + if (b & 0x04) + note = hio_read8(f); + if (b & 0x08) + event->ins = hio_read8(f); + if (b & 0x10) + event->vol = hio_read8(f); + if (b & 0x20) + event->fxt = hio_read8(f); + if (b & 0x40) + event->fxp = hio_read8(f); + if (b & 0x80) + /*f2p =*/ hio_read8(f); + + if (note == 1) + event->note = XMP_KEY_OFF; + else if (note > 11) /* adjusted to play codeine.mgt */ + event->note = note + 1; + + /* effects */ + if (event->fxt < 0x10) + /* like amiga */ ; + else switch (event->fxt) { + case 0x13: + case 0x14: + case 0x15: + case 0x17: + case 0x1c: + case 0x1d: + case 0x1e: + event->fxt = FX_EXTENDED; + event->fxp = ((event->fxt & 0x0f) << 4) | + (event->fxp & 0x0f); + break; + default: + event->fxt = event->fxp = 0; + } + + /* volume and volume column effects */ + if ((event->vol >= 0x10) && (event->vol <= 0x50)) { + event->vol -= 0x0f; + continue; + } + + switch (event->vol >> 4) { + case 0x06: /* Volume slide down */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = event->vol - 0x60; + break; + case 0x07: /* Volume slide up */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = (event->vol - 0x70) << 4; + break; + case 0x08: /* Fine volume slide down */ + event->f2t = FX_EXTENDED; + event->f2p = (EX_F_VSLIDE_DN << 4) | + (event->vol - 0x80); + break; + case 0x09: /* Fine volume slide up */ + event->f2t = FX_EXTENDED; + event->f2p = (EX_F_VSLIDE_UP << 4) | + (event->vol - 0x90); + break; + case 0x0a: /* Set vibrato speed */ + event->f2t = FX_VIBRATO; + event->f2p = (event->vol - 0xa0) << 4; + break; + case 0x0b: /* Vibrato */ + event->f2t = FX_VIBRATO; + event->f2p = event->vol - 0xb0; + break; + case 0x0c: /* Set panning */ + event->f2t = FX_SETPAN; + event->f2p = ((event->vol - 0xc0) << 4) + 8; + break; + case 0x0d: /* Pan slide left */ + event->f2t = FX_PANSLIDE; + event->f2p = (event->vol - 0xd0) << 4; + break; + case 0x0e: /* Pan slide right */ + event->f2t = FX_PANSLIDE; + event->f2p = event->vol - 0xe0; + break; + case 0x0f: /* Tone portamento */ + event->f2t = FX_TONEPORTA; + event->f2p = (event->vol - 0xf0) << 4; + break; + } + + event->vol = 0; + + /*printf("%02x %02x %02x %02x %02x %02x\n", + j, event->note, event->ins, event->vol, + event->fxt, event->fxp);*/ + } + } + + /* Extra track */ + if (mod->trk > 0) { + mod->xxt[0] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * 64 - 1); + mod->xxt[0]->rows = 64; + } + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + hio_seek(f, start + pat_ptr, SEEK_SET); + + for (i = 0; i < mod->pat; i++) { + int rows; + + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + rows = hio_read16b(f); + + /* Sanity check */ + if (rows > 256) { + return -1; + } + + mod->xxp[i]->rows = rows; + + for (j = 0; j < mod->chn; j++) { + int track = hio_read16b(f) - 1; + + /* Sanity check */ + if (track >= mod->trk) { + return -1; + } + + mod->xxp[i]->index[j] = track; + } + } + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxi[i].nsm == 0) + continue; + + hio_seek(f, start + sdata[i], SEEK_SET); + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mmd1_load.c b/thirdparty/libxmp/src/loaders/mmd1_load.c new file mode 100644 index 0000000..ab4b081 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mmd1_load.c @@ -0,0 +1,629 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * OctaMED v1.00b: ftp://ftp.funet.fi/pub/amiga/fish/501-600/ff579 + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +static int mmd1_test(HIO_HANDLE *, char *, const int); +static int mmd1_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mmd1 = { + "MED 2.10/OctaMED", + mmd1_test, + mmd1_load +}; + +static int mmd1_test(HIO_HANDLE *f, char *t, const int start) +{ + char id[4]; + uint32 offset, len; + + if (hio_read(id, 1, 4, f) < 4) + return -1; + + if (memcmp(id, "MMD0", 4) && memcmp(id, "MMD1", 4) && memcmp(id, "MMDC", 4)) + return -1; + + hio_seek(f, 28, SEEK_CUR); + offset = hio_read32b(f); /* expdata_offset */ + + if (offset) { + hio_seek(f, start + offset + 44, SEEK_SET); + offset = hio_read32b(f); + len = hio_read32b(f); + hio_seek(f, start + offset, SEEK_SET); + libxmp_read_title(f, t, len); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + + +static int mmd1_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct MMD0 header; + struct MMD0song song; + struct MMD1Block block; + struct InstrExt *exp_smp = NULL; + struct MMD0exp expdata; + struct xmp_event *event; + uint32 *blockarr = NULL; + uint32 *smplarr = NULL; + uint8 *patbuf = NULL; + int ver = 0; + int mmdc = 0; + int smp_idx = 0; + int song_offset; + int blockarr_offset; + int smplarr_offset; + int expdata_offset; + int expsmp_offset; + int songname_offset; + int iinfo_offset; + int annotxt_offset; + int bpm_on, bpmlen, med_8ch, hexvol; + int max_lines; + int retval = -1; + + LOAD_INIT(); + + hio_read(&header.id, 4, 1, f); + + ver = *((char *)&header.id + 3) - '1' + 1; + if (ver > 1) { + ver = 0; + mmdc = 1; + } + + D_(D_WARN "load header"); + header.modlen = hio_read32b(f); + song_offset = hio_read32b(f); + D_(D_INFO "song_offset = 0x%08x", song_offset); + hio_read16b(f); + hio_read16b(f); + blockarr_offset = hio_read32b(f); + D_(D_INFO "blockarr_offset = 0x%08x", blockarr_offset); + hio_read32b(f); + smplarr_offset = hio_read32b(f); + D_(D_INFO "smplarr_offset = 0x%08x", smplarr_offset); + hio_read32b(f); + expdata_offset = hio_read32b(f); + D_(D_INFO "expdata_offset = 0x%08x", expdata_offset); + hio_read32b(f); + header.pstate = hio_read16b(f); + header.pblock = hio_read16b(f); + header.pline = hio_read16b(f); + header.pseqnum = hio_read16b(f); + header.actplayline = hio_read16b(f); + header.counter = hio_read8(f); + header.extra_songs = hio_read8(f); + + /* + * song structure + */ + D_(D_WARN "load song"); + if (hio_seek(f, start + song_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at song"); + return -1; + } + for (i = 0; i < 63; i++) { + song.sample[i].rep = hio_read16b(f); + song.sample[i].replen = hio_read16b(f); + song.sample[i].midich = hio_read8(f); + song.sample[i].midipreset = hio_read8(f); + song.sample[i].svol = hio_read8(f); + song.sample[i].strans = hio_read8s(f); + } + song.numblocks = hio_read16b(f); + song.songlen = hio_read16b(f); + + /* Sanity check */ + if (song.numblocks > 255 || song.songlen > 256) { + D_(D_CRIT "unsupported block count (%d) or song length (%d)", + song.numblocks, song.songlen); + return -1; + } + + D_(D_INFO "song.songlen = %d", song.songlen); + for (i = 0; i < 256; i++) + song.playseq[i] = hio_read8(f); + song.deftempo = hio_read16b(f); + song.playtransp = hio_read8(f); + song.flags = hio_read8(f); + song.flags2 = hio_read8(f); + song.tempo2 = hio_read8(f); + for (i = 0; i < 16; i++) + song.trkvol[i] = hio_read8(f); + song.mastervol = hio_read8(f); + song.numsamples = hio_read8(f); + + /* Sanity check */ + if (song.numsamples > 63) { + D_(D_CRIT "invalid instrument count %d", song.numsamples); + return -1; + } + + /* + * convert header + */ + m->c4rate = C4_NTSC_RATE; + m->quirk |= song.flags & FLAG_STSLIDE ? 0 : QUIRK_VSALL | QUIRK_PBALL; + hexvol = song.flags & FLAG_VOLHEX; + med_8ch = song.flags & FLAG_8CHANNEL; + bpm_on = song.flags2 & FLAG2_BPM; + bpmlen = 1 + (song.flags2 & FLAG2_BMASK); + m->time_factor = MED_TIME_FACTOR; + + mmd_set_bpm(m, med_8ch, song.deftempo, bpm_on, bpmlen); + + mod->spd = song.tempo2; + mod->pat = song.numblocks; + mod->ins = song.numsamples; + mod->len = song.songlen; + mod->rst = 0; + mod->chn = 0; + memcpy(mod->xxo, song.playseq, mod->len); + mod->name[0] = 0; + + /* + * Read smplarr + */ + D_(D_WARN "read smplarr"); + smplarr = (uint32 *) malloc(mod->ins * sizeof(uint32)); + if (smplarr == NULL) { + return -1; + } + if (hio_seek(f, start + smplarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at smplarr"); + goto err_cleanup; + } + for (i = 0; i < mod->ins; i++) { + smplarr[i] = hio_read32b(f); + if (hio_eof(f)) { + D_(D_CRIT "read error at smplarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Obtain number of samples from each instrument + */ + mod->smp = 0; + for (i = 0; i < mod->ins; i++) { + int16 type; + if (smplarr[i] == 0) + continue; + if (hio_seek(f, start + smplarr[i], SEEK_SET) != 0) { + D_(D_CRIT "seek error at instrument %d", i); + goto err_cleanup; + } + + hio_read32b(f); /* length */ + type = hio_read16b(f); + if (type == -1 || type == -2) { /* type is synth? */ + int wforms; + hio_seek(f, 14, SEEK_CUR); + wforms = hio_read16b(f); + + /* Sanity check */ + if (wforms > 256) { + D_(D_CRIT "invalid wform count at instrument %d", i); + goto err_cleanup; + } + + mod->smp += wforms; + } else if (type >= 1 && type <= 6) { + mod->smp += mmd_num_oct[type - 1]; + } else { + mod->smp++; + } + } + + /* + * expdata + */ + D_(D_WARN "load expdata"); + expdata.s_ext_entries = 0; + expdata.s_ext_entrsz = 0; + expdata.i_ext_entries = 0; + expdata.i_ext_entrsz = 0; + expsmp_offset = 0; + iinfo_offset = 0; + if (expdata_offset) { + if (hio_seek(f, start + expdata_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expdata"); + goto err_cleanup; + } + hio_read32b(f); + expsmp_offset = hio_read32b(f); + D_(D_INFO "expsmp_offset = 0x%08x", expsmp_offset); + expdata.s_ext_entries = hio_read16b(f); + expdata.s_ext_entrsz = hio_read16b(f); + annotxt_offset = hio_read32b(f); + expdata.annolen = hio_read32b(f); + iinfo_offset = hio_read32b(f); + D_(D_INFO "iinfo_offset = 0x%08x", iinfo_offset); + expdata.i_ext_entries = hio_read16b(f); + expdata.i_ext_entrsz = hio_read16b(f); + + /* Sanity check */ + if (expsmp_offset < 0 || + annotxt_offset < 0 || + expdata.annolen > 0x10000 || + iinfo_offset < 0) { + D_(D_CRIT "invalid expdata (annotxt=0x%08x annolen=0x%08x)", + annotxt_offset, expdata.annolen); + goto err_cleanup; + } + + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + songname_offset = hio_read32b(f); + expdata.songnamelen = hio_read32b(f); + D_(D_INFO "songname_offset = 0x%08x", songname_offset); + D_(D_INFO "expdata.songnamelen = %d", expdata.songnamelen); + + hio_seek(f, start + songname_offset, SEEK_SET); + for (i = 0; i < expdata.songnamelen; i++) { + if (i >= XMP_NAME_SIZE) + break; + mod->name[i] = hio_read8(f); + } + + /* Read annotation */ + if (annotxt_offset != 0 && expdata.annolen != 0) { + D_(D_INFO "annotxt_offset = 0x%08x", annotxt_offset); + m->comment = (char *) malloc(expdata.annolen + 1); + if (m->comment != NULL) { + hio_seek(f, start + annotxt_offset, SEEK_SET); + hio_read(m->comment, 1, expdata.annolen, f); + m->comment[expdata.annolen] = 0; + } + } + } + + /* + * Read blockarr. + */ + D_(D_WARN "read blockarr"); + blockarr = (uint32 *) malloc(mod->pat * sizeof(uint32)); + if (blockarr == NULL) { + goto err_cleanup; + } + if (hio_seek(f, start + blockarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at blockarr"); + goto err_cleanup; + } + for (i = 0; i < mod->pat; i++) { + blockarr[i] = hio_read32b(f); + if (hio_error(f)) { + D_(D_CRIT "read error at blockarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Quickly scan patterns to check the number of channels + */ + D_(D_WARN "find number of channels"); + + max_lines = 1; + for (i = 0; i < mod->pat; i++) { + D_(D_INFO "block %d block_offset = 0x%08x", i, blockarr[i]); + if (blockarr[i] == 0) + continue; + + if (hio_seek(f, start + blockarr[i], SEEK_SET) != 0) { + D_(D_CRIT "seek error at block %d", i); + goto err_cleanup; + } + + if (ver > 0) { + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + } else { + block.numtracks = hio_read8(f); + block.lines = hio_read8(f); + } + + /* Sanity check--Amiga OctaMED files have an upper bound of 3200 lines per block. */ + if (block.lines + 1 > 3200) { + D_(D_CRIT "invalid line count %d in block %d", block.lines + 1, i); + goto err_cleanup; + } + + if (block.numtracks > mod->chn) { + mod->chn = block.numtracks; + } + if (block.lines + 1 > max_lines) { + max_lines = block.lines + 1; + } + } + + /* Sanity check */ + /* MMD0/MMD1 can't have more than 16 channels... */ + if (mod->chn > MIN(16, XMP_MAX_CHANNELS)) { + D_(D_CRIT "invalid channel count %d", mod->chn); + goto err_cleanup; + } + + mod->trk = mod->pat * mod->chn; + + libxmp_set_type(m, ver == 0 ? mmdc ? "MED Packer MMDC" : + mod->chn > 4 ? "OctaMED 2.00 MMD0" : + "MED 2.10 MMD0" : "OctaMED 4.00 MMD1"); + + MODULE_INFO(); + + D_(D_INFO "BPM mode: %s (length = %d)", bpm_on ? "on" : "off", bpmlen); + D_(D_INFO "Song transpose: %d", song.playtransp); + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* + * Read and convert patterns + */ + D_(D_WARN "read patterns"); + if (libxmp_init_pattern(mod) < 0) + goto err_cleanup; + + if ((patbuf = (uint8 *)malloc(mod->chn * max_lines * 4)) == NULL) { + goto err_cleanup; + } + + for (i = 0; i < mod->pat; i++) { + uint8 *pos; + size_t size; + + if (blockarr[i] == 0) + continue; + + if (hio_seek(f, start + blockarr[i], SEEK_SET) != 0) { + D_(D_CRIT "seek error at block %d", i); + goto err_cleanup; + } + + if (ver > 0) { + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + hio_read32b(f); + } else { + block.numtracks = hio_read8(f); + block.lines = hio_read8(f); + } + + size = block.numtracks * (block.lines + 1) * (ver ? 4 : 3); + + if (mmdc) { + /* MMDC is just MMD0 with simple pattern packing. */ + memset(patbuf, 0, size); + for (j = 0; j < size;) { + unsigned pack = hio_read8(f); + if (hio_error(f)) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + + if (pack & 0x80) { + /* Run of 0 */ + j += 256 - pack; + continue; + } + /* Uncompressed block */ + pack++; + if (pack > size - j) + pack = size - j; + + if (hio_read(patbuf + j, 1, pack, f) < pack) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + j += pack; + } + } else { + if (hio_read(patbuf, 1, size, f) < size) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + } + + if (libxmp_alloc_pattern_tracks_long(mod, i, block.lines + 1) < 0) + goto err_cleanup; + + pos = patbuf; + if (ver > 0) { /* MMD1 */ + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < block.numtracks; k++) { + event = &EVENT(i, k, j); + event->note = pos[0] & 0x7f; + if (event->note) + event->note += + 12 + song.playtransp; + + if (event->note >= XMP_MAX_KEYS) + event->note = 0; + + event->ins = pos[1] & 0x3f; + + /* Decay */ + if (event->ins && !event->note) { + event->f2t = FX_MED_HOLD; + } + + event->fxt = pos[2]; + event->fxp = pos[3]; + mmd_xlat_fx(event, bpm_on, bpmlen, + med_8ch, hexvol); + pos += 4; + } + } + } else { /* MMD0 */ + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < block.numtracks; k++) { + event = &EVENT(i, k, j); + event->note = pos[0] & 0x3f; + if (event->note) + event->note += 12 + song.playtransp; + + if (event->note >= XMP_MAX_KEYS) + event->note = 0; + + event->ins = + (pos[1] >> 4) | ((pos[0] & 0x80) >> 3) + | ((pos[0] & 0x40) >> 1); + + /* Decay */ + if (event->ins && !event->note) { + event->f2t = FX_MED_HOLD; + } + + event->fxt = pos[1] & 0x0f; + event->fxp = pos[2]; + mmd_xlat_fx(event, bpm_on, bpmlen, + med_8ch, hexvol); + pos += 3; + } + } + } + } + free(patbuf); + patbuf = NULL; + + if (libxmp_med_new_module_extras(m)) + goto err_cleanup; + + /* + * Read and convert instruments and samples + */ + D_(D_WARN "read instruments"); + if (libxmp_init_instrument(m) < 0) + goto err_cleanup; + + D_(D_INFO "Instruments: %d", mod->ins); + + /* Instrument extras */ + exp_smp = (struct InstrExt *) calloc(mod->ins, sizeof(struct InstrExt)); + if (exp_smp == NULL) { + goto err_cleanup; + } + + if (expsmp_offset) { + if (hio_seek(f, start + expsmp_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.s_ext_entries; i++) { + int skip = expdata.s_ext_entrsz - 4; + + D_(D_INFO "sample %d expsmp_offset = 0x%08lx", i, hio_tell(f)); + + exp_smp[i].hold = hio_read8(f); + exp_smp[i].decay = hio_read8(f); + exp_smp[i].suppress_midi_off = hio_read8(f); + exp_smp[i].finetune = hio_read8(f); + + if (hio_error(f)) { + D_(D_CRIT "read error at expsmp"); + goto err_cleanup; + } + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + } + } + + /* Instrument names */ + if (iinfo_offset) { + uint8 name[40]; + + if (hio_seek(f, start + iinfo_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at iinfo"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.i_ext_entries; i++) { + int skip = expdata.i_ext_entrsz - 40; + + D_(D_INFO "sample %d iinfo_offset = 0x%08lx", i, hio_tell(f)); + + if (hio_read(name, 40, 1, f) < 1) { + D_(D_CRIT "read error at iinfo %d", i); + goto err_cleanup; + } + libxmp_instrument_name(mod, i, name, 40); + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at iinfo %d", i); + goto err_cleanup; + } + } + } + + /* Sample data */ + for (smp_idx = i = 0; i < mod->ins; i++) { + D_(D_INFO "sample %d smpl_offset = 0x%08x", i, smplarr[i]); + if (smplarr[i] == 0) { + continue; + } + + if (hio_seek(f, start + smplarr[i], SEEK_SET) < 0) { + D_(D_CRIT "seek error at instrument %d", i); + goto err_cleanup; + } + + smp_idx = mmd_load_instrument(f, m, i, smp_idx, &expdata, + &exp_smp[i], &song.sample[i], ver); + + if (smp_idx < 0) { + goto err_cleanup; + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].vol = song.trkvol[i]; + mod->xxc[i].pan = DEFPAN((((i + 1) / 2) % 2) * 0xff); + } + + m->read_event_type = READ_EVENT_MED; + retval = 0; + + err_cleanup: + free(exp_smp); + free(blockarr); + free(smplarr); + free(patbuf); + + return retval; +} diff --git a/thirdparty/libxmp/src/loaders/mmd3_load.c b/thirdparty/libxmp/src/loaders/mmd3_load.c new file mode 100644 index 0000000..3ab74f4 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mmd3_load.c @@ -0,0 +1,596 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +static int mmd3_test (HIO_HANDLE *, char *, const int); +static int mmd3_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mmd3 = { + "OctaMED", + mmd3_test, + mmd3_load +}; + +static int mmd3_test(HIO_HANDLE *f, char *t, const int start) +{ + char id[4]; + uint32 offset, len; + + if (hio_read(id, 1, 4, f) < 4) + return -1; + + if (memcmp(id, "MMD2", 4) && memcmp(id, "MMD3", 4)) + return -1; + + hio_seek(f, 28, SEEK_CUR); + offset = hio_read32b(f); /* expdata_offset */ + + if (offset) { + hio_seek(f, start + offset + 44, SEEK_SET); + offset = hio_read32b(f); + len = hio_read32b(f); + hio_seek(f, start + offset, SEEK_SET); + libxmp_read_title(f, t, len); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + + +static int mmd3_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct MMD0 header; + struct MMD2song song; + struct MMD1Block block; + struct InstrExt *exp_smp = NULL; + struct MMD0exp expdata; + struct xmp_event *event; + uint32 *blockarr = NULL; + uint32 *smplarr = NULL; + uint8 *patbuf = NULL; + int ver = 0; + int smp_idx = 0; + int song_offset; + int seqtable_offset; + int trackvols_offset; + int trackpans_offset; + int blockarr_offset; + int smplarr_offset; + int expdata_offset; + int expsmp_offset; + int songname_offset; + int iinfo_offset; + int mmdinfo_offset; + int playseq_offset; + int bpm_on, bpmlen, med_8ch, hexvol; + int max_lines; + int retval = -1; + + LOAD_INIT(); + + hio_read(&header.id, 4, 1, f); + + ver = *((char *)&header.id + 3) - '1' + 1; + + D_(D_WARN "load header"); + header.modlen = hio_read32b(f); + song_offset = hio_read32b(f); + D_(D_INFO "song_offset = 0x%08x", song_offset); + hio_read16b(f); + hio_read16b(f); + blockarr_offset = hio_read32b(f); + D_(D_INFO "blockarr_offset = 0x%08x", blockarr_offset); + hio_read32b(f); + smplarr_offset = hio_read32b(f); + D_(D_INFO "smplarr_offset = 0x%08x", smplarr_offset); + hio_read32b(f); + expdata_offset = hio_read32b(f); + D_(D_INFO "expdata_offset = 0x%08x", expdata_offset); + hio_read32b(f); + header.pstate = hio_read16b(f); + header.pblock = hio_read16b(f); + header.pline = hio_read16b(f); + header.pseqnum = hio_read16b(f); + header.actplayline = hio_read16b(f); + header.counter = hio_read8(f); + header.extra_songs = hio_read8(f); + + /* + * song structure + */ + D_(D_WARN "load song"); + hio_seek(f, start + song_offset, SEEK_SET); + for (i = 0; i < 63; i++) { + song.sample[i].rep = hio_read16b(f); + song.sample[i].replen = hio_read16b(f); + song.sample[i].midich = hio_read8(f); + song.sample[i].midipreset = hio_read8(f); + song.sample[i].svol = hio_read8(f); + song.sample[i].strans = hio_read8s(f); + } + song.numblocks = hio_read16b(f); + song.songlen = hio_read16b(f); + D_(D_INFO "song.songlen = %d", song.songlen); + seqtable_offset = hio_read32b(f); + hio_read32b(f); + trackvols_offset = hio_read32b(f); + song.numtracks = hio_read16b(f); + song.numpseqs = hio_read16b(f); + trackpans_offset = hio_read32b(f); + song.flags3 = hio_read32b(f); + song.voladj = hio_read16b(f); + song.channels = hio_read16b(f); + song.mix_echotype = hio_read8(f); + song.mix_echodepth = hio_read8(f); + song.mix_echolen = hio_read16b(f); + song.mix_stereosep = hio_read8(f); + + hio_seek(f, 223, SEEK_CUR); + + song.deftempo = hio_read16b(f); + song.playtransp = hio_read8(f); + song.flags = hio_read8(f); + song.flags2 = hio_read8(f); + song.tempo2 = hio_read8(f); + for (i = 0; i < 16; i++) + hio_read8(f); /* reserved */ + song.mastervol = hio_read8(f); + song.numsamples = hio_read8(f); + + /* Sanity check */ + if (song.numsamples > 63) { + D_(D_CRIT "invalid instrument count %d", song.numsamples); + return -1; + } + + /* + * read sequence + */ + hio_seek(f, start + seqtable_offset, SEEK_SET); + playseq_offset = hio_read32b(f); + hio_seek(f, start + playseq_offset, SEEK_SET); + hio_seek(f, 32, SEEK_CUR); /* skip name */ + hio_read32b(f); + hio_read32b(f); + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 255) { + D_(D_CRIT "unsupported song length %d", mod->len); + return -1; + } + + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = hio_read16b(f); + } + + /* + * convert header + */ + m->c4rate = C4_NTSC_RATE; + m->quirk |= song.flags & FLAG_STSLIDE ? 0 : QUIRK_VSALL | QUIRK_PBALL; + hexvol = song.flags & FLAG_VOLHEX; + med_8ch = song.flags & FLAG_8CHANNEL; + bpm_on = song.flags2 & FLAG2_BPM; + bpmlen = 1 + (song.flags2 & FLAG2_BMASK); + m->time_factor = MED_TIME_FACTOR; + + mmd_set_bpm(m, med_8ch, song.deftempo, bpm_on, bpmlen); + + mod->spd = song.tempo2; + mod->pat = song.numblocks; + mod->ins = song.numsamples; + mod->rst = 0; + mod->chn = 0; + mod->name[0] = 0; + + /* + * Read smplarr + */ + D_(D_WARN "read smplarr"); + smplarr = (uint32 *) malloc(mod->ins * sizeof(uint32)); + if (smplarr == NULL) { + return -1; + } + if (hio_seek(f, start + smplarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at smplarr"); + goto err_cleanup; + } + for (i = 0; i < mod->ins; i++) { + smplarr[i] = hio_read32b(f); + if (hio_eof(f)) { + D_(D_CRIT "read error at smplarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Obtain number of samples from each instrument + */ + mod->smp = 0; + for (i = 0; i < mod->ins; i++) { + int16 type; + if (smplarr[i] == 0) + continue; + hio_seek(f, start + smplarr[i], SEEK_SET); + hio_read32b(f); /* length */ + type = hio_read16b(f); + if (type == -1 || type == -2) { /* type is synth? */ + hio_seek(f, 14, SEEK_CUR); + mod->smp += hio_read16b(f); /* wforms */ + } else if (type >= 1 && type <= 6) { /* octave samples */ + mod->smp += mmd_num_oct[type - 1]; + } else { + mod->smp++; + } + if (hio_error(f)) { + D_(D_CRIT "read error at sample %d", i); + goto err_cleanup; + } + } + + /* + * expdata + */ + D_(D_WARN "load expdata"); + expdata.s_ext_entries = 0; + expdata.s_ext_entrsz = 0; + expdata.i_ext_entries = 0; + expdata.i_ext_entrsz = 0; + expsmp_offset = 0; + iinfo_offset = 0; + + if (expdata_offset) { + if (hio_seek(f, start + expdata_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expdata"); + goto err_cleanup; + } + hio_read32b(f); + expsmp_offset = hio_read32b(f); + D_(D_INFO "expsmp_offset = 0x%08x", expsmp_offset); + expdata.s_ext_entries = hio_read16b(f); + expdata.s_ext_entrsz = hio_read16b(f); + hio_read32b(f); /* annotxt */ + hio_read32b(f); /* annolen */ + iinfo_offset = hio_read32b(f); + D_(D_INFO "iinfo_offset = 0x%08x", iinfo_offset); + expdata.i_ext_entries = hio_read16b(f); + expdata.i_ext_entrsz = hio_read16b(f); + + /* Sanity check */ + if (expsmp_offset < 0 || iinfo_offset < 0) { + D_(D_CRIT "invalid expdata"); + goto err_cleanup; + } + + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + songname_offset = hio_read32b(f); + D_(D_INFO "songname_offset = 0x%08x", songname_offset); + expdata.songnamelen = hio_read32b(f); + hio_read32b(f); /* dumps */ + mmdinfo_offset = hio_read32b(f); + + if (hio_error(f)) { + D_(D_CRIT "read error in expdata"); + goto err_cleanup; + } + + hio_seek(f, start + songname_offset, SEEK_SET); + D_(D_INFO "expdata.songnamelen = %d", expdata.songnamelen); + for (i = 0; i < expdata.songnamelen; i++) { + if (i >= XMP_NAME_SIZE) + break; + mod->name[i] = hio_read8(f); + } + + if (mmdinfo_offset != 0) { + D_(D_INFO "mmdinfo_offset = 0x%08x", mmdinfo_offset); + hio_seek(f, start + mmdinfo_offset, SEEK_SET); + mmd_info_text(f, m, mmdinfo_offset); + } + } + + /* + * Read blockarr. + */ + D_(D_WARN "read blockarr"); + blockarr = (uint32 *) malloc(mod->pat * sizeof(uint32)); + if (blockarr == NULL) { + goto err_cleanup; + } + if (hio_seek(f, start + blockarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at blockarr"); + goto err_cleanup; + } + for (i = 0; i < mod->pat; i++) { + blockarr[i] = hio_read32b(f); + if (hio_error(f)) { + D_(D_CRIT "read error at blockarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Quickly scan patterns to check the number of channels + */ + D_(D_WARN "find number of channels"); + + max_lines = 1; + for (i = 0; i < mod->pat; i++) { + D_(D_INFO "block %d block_offset = 0x%08x", i, blockarr[i]); + if (blockarr[i] == 0) + continue; + + hio_seek(f, start + blockarr[i], SEEK_SET); + + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + if (hio_error(f)) { + D_(D_CRIT "read error at block %d", i); + goto err_cleanup; + } + + /* Sanity check--Amiga OctaMED files have an upper bound of 3200 lines per block, + * but MED Soundstudio for Windows allows up to 9999 lines. + */ + if (block.lines + 1 > 9999) { + D_(D_CRIT "invalid line count %d in block %d", block.lines + 1, i); + goto err_cleanup; + } + + if (block.numtracks > mod->chn) { + mod->chn = block.numtracks; + } + if (block.lines + 1 > max_lines) { + max_lines = block.lines + 1; + } + } + + /* Sanity check */ + if (mod->chn <= 0 || mod->chn > XMP_MAX_CHANNELS) { + D_(D_CRIT "invalid channel count %d", mod->chn); + goto err_cleanup; + } + + mod->trk = mod->pat * mod->chn; + + if (ver == 2) + libxmp_set_type(m, "OctaMED v5 MMD2"); + else + libxmp_set_type(m, "OctaMED Soundstudio MMD%c", '0' + ver); + + MODULE_INFO(); + + D_(D_INFO "BPM mode: %s (length = %d)", bpm_on ? "on" : "off", bpmlen); + D_(D_INFO "Song transpose : %d", song.playtransp); + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* + * Read and convert patterns + */ + D_(D_WARN "read patterns"); + + if (libxmp_init_pattern(mod) < 0) + goto err_cleanup; + + if ((patbuf = (uint8 *)malloc(mod->chn * max_lines * 4)) == NULL) { + goto err_cleanup; + } + + for (i = 0; i < mod->pat; i++) { + uint8 *pos; + size_t size; + + if (blockarr[i] == 0) + continue; + + hio_seek(f, start + blockarr[i], SEEK_SET); + + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + hio_read32b(f); /* FIXME: should try to load extra command pages when they exist. */ + + size = block.numtracks * (block.lines + 1) * 4; + if (hio_read(patbuf, 1, size, f) < size) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + + if (libxmp_alloc_pattern_tracks_long(mod, i, block.lines + 1) < 0) + goto err_cleanup; + + pos = patbuf; + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < block.numtracks; k++) { + event = &EVENT(i, k, j); + event->note = pos[0] & 0x7f; + if (event->note) { + event->note += 12 + song.playtransp; + } + + if (event->note >= XMP_MAX_KEYS) + event->note = 0; + + event->ins = pos[1] & 0x3f; + + /* Decay */ + if (event->ins && !event->note) { + event->f2t = FX_MED_HOLD; + } + + event->fxt = pos[2]; + event->fxp = pos[3]; + mmd_xlat_fx(event, bpm_on, bpmlen, + med_8ch, hexvol); + pos += 4; + } + } + } + free(patbuf); + patbuf = NULL; + + if (libxmp_med_new_module_extras(m) != 0) + goto err_cleanup; + + /* + * Read and convert instruments and samples + */ + D_(D_WARN "read instruments"); + + if (libxmp_init_instrument(m) < 0) + goto err_cleanup; + + D_(D_INFO "Instruments: %d", mod->ins); + + /* Instrument extras */ + exp_smp = (struct InstrExt *) calloc(mod->ins, sizeof(struct InstrExt)); + if (exp_smp == NULL) { + goto err_cleanup; + } + + if (expsmp_offset) { + if (hio_seek(f, start + expsmp_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.s_ext_entries; i++) { + int skip = expdata.s_ext_entrsz - 4; + + D_(D_INFO "sample %d expsmp_offset = 0x%08lx", i, hio_tell(f)); + + exp_smp[i].hold = hio_read8(f); + exp_smp[i].decay = hio_read8(f); + exp_smp[i].suppress_midi_off = hio_read8(f); + exp_smp[i].finetune = hio_read8(f); + + if (expdata.s_ext_entrsz >= 8) { /* Octamed V5 */ + exp_smp[i].default_pitch = hio_read8(f); + exp_smp[i].instr_flags = hio_read8(f); + hio_read16b(f); + skip -= 4; + } + if (expdata.s_ext_entrsz >= 10) { /* OctaMED V5.02 */ + hio_read16b(f); + skip -= 2; + } + if (expdata.s_ext_entrsz >= 18) { /* OctaMED V7 */ + exp_smp[i].long_repeat = hio_read32b(f); + exp_smp[i].long_replen = hio_read32b(f); + skip -= 8; + } + + if (hio_error(f)) { + D_(D_CRIT "read error at expsmp"); + goto err_cleanup; + } + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + } + } + + /* Instrument names */ + if (iinfo_offset) { + uint8 name[40]; + + if (hio_seek(f, start + iinfo_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at iinfo"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.i_ext_entries; i++) { + int skip = expdata.i_ext_entrsz - 40; + + D_(D_INFO "sample %d iinfo_offset = 0x%08lx", i, hio_tell(f)); + + if (hio_read(name, 40, 1, f) < 1) { + D_(D_CRIT "read error at iinfo %d", i); + goto err_cleanup; + } + libxmp_instrument_name(mod, i, name, 40); + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at iinfo %d", i); + goto err_cleanup; + } + } + } + + /* Sample data */ + for (smp_idx = i = 0; i < mod->ins; i++) { + D_(D_INFO "sample %d smpl_offset = 0x%08x", i, smplarr[i]); + if (smplarr[i] == 0) { + continue; + } + + if (hio_seek(f, start + smplarr[i], SEEK_SET) < 0) { + D_(D_CRIT "seek error at instrument %d", i); + goto err_cleanup; + } + + smp_idx = mmd_load_instrument(f, m, i, smp_idx, &expdata, + &exp_smp[i], &song.sample[i], ver); + + if (smp_idx < 0) { + goto err_cleanup; + } + } + + hio_seek(f, start + trackvols_offset, SEEK_SET); + for (i = 0; i < mod->chn; i++) + mod->xxc[i].vol = hio_read8(f);; + + if (trackpans_offset) { + hio_seek(f, start + trackpans_offset, SEEK_SET); + for (i = 0; i < mod->chn; i++) { + int p = 8 * hio_read8s(f); + mod->xxc[i].pan = 0x80 + (p > 127 ? 127 : p); + } + } else { + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = 0x80; + } + + m->read_event_type = READ_EVENT_MED; + retval = 0; + + err_cleanup: + free(exp_smp); + free(blockarr); + free(smplarr); + free(patbuf); + + return retval; +} diff --git a/thirdparty/libxmp/src/loaders/mmd_common.c b/thirdparty/libxmp/src/loaders/mmd_common.c new file mode 100644 index 0000000..2a01972 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mmd_common.c @@ -0,0 +1,1028 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Common functions for MMD0/1 and MMD2/3 loaders + * Tempo fixed by Francis Russell + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +#ifdef DEBUG +const char *const mmd_inst_type[] = { + "HYB", /* -2 */ + "SYN", /* -1 */ + "SMP", /* 0 */ + "I5O", /* 1 */ + "I3O", /* 2 */ + "I2O", /* 3 */ + "I4O", /* 4 */ + "I6O", /* 5 */ + "I7O", /* 6 */ + "EXT", /* 7 */ +}; +#endif + +static int mmd_convert_tempo(int tempo, int bpm_on, int med_8ch) +{ + const int tempos_compat[10] = { + 195, 97, 65, 49, 39, 32, 28, 24, 22, 20 + }; + const int tempos_8ch[10] = { + 179, 164, 152, 141, 131, 123, 116, 110, 104, 99 + }; + + if (tempo > 0) { + /* From the OctaMEDv4 documentation: + * + * In 8-channel mode, you can control the playing speed more + * accurately (to techies: by changing the size of the mix buffer). + * This can be done with the left tempo gadget (values 1-10; the + * lower, the faster). Values 11-240 are equivalent to 10. + * + * NOTE: the tempos used for this are directly from OctaMED + * Soundstudio 2, but in older versions the playback speeds + * differed slightly between NTSC and PAL. This table seems to + * have been intended to be a compromise between the two. + */ + if (med_8ch) { + tempo = tempo > 10 ? 10 : tempo; + return tempos_8ch[tempo-1]; + } + /* Tempos 1-10 in tempo mode are compatibility tempos that + * approximate Soundtracker speeds. + */ + if (tempo <= 10 && !bpm_on) { + return tempos_compat[tempo-1]; + } + } + return tempo; +} + +void mmd_xlat_fx(struct xmp_event *event, int bpm_on, int bpmlen, int med_8ch, + int hexvol) +{ + switch (event->fxt) { + case 0x00: + /* ARPEGGIO 00 + * Changes the pitch six times between three different + * pitches during the duration of the note. It can create a + * chord sound or other special effect. Arpeggio works better + * with some instruments than others. + */ + break; + case 0x01: + /* SLIDE UP 01 + * This slides the pitch of the current track up. It decreases + * the period of the note by the amount of the argument on each + * timing pulse. OctaMED-Pro can create slides automatically, + * but you may want to use this function for special effects. + * Note: a param of 0 does nothing and should be ignored. + */ + if (!event->fxp) + event->fxt = 0; + break; + case 0x02: + /* SLIDE DOWN 02 + * The same as SLIDE UP, but it slides down. + * Note: a param of 0 does nothing and should be ignored. + */ + if (!event->fxp) + event->fxt = 0; + break; + case 0x03: + /* PORTAMENTO 03 + * Makes precise sliding easy. + */ + break; + case 0x04: + /* VIBRATO 04 + * The left half of the argument is the vibrato speed, the + * right half is the depth. If the numbers are zeros, the + * previous speed and depth are used. + */ + /* Note: this is twice as deep as the Protracker vibrato */ + event->fxt = FX_VIBRATO2; + break; + case 0x05: + /* SLIDE + FADE 05 + * ProTracker compatible. This command is the combination of + * commands 0300 and 0Dxx. The argument is the fade speed. + * The slide will continue during this command. + */ + /* fall-through */ + case 0x06: + /* VIBRATO + FADE 06 + * ProTracker compatible. Combines commands 0400 and 0Dxx. + * The argument is the fade speed. The vibrato will continue + * during this command. + */ + /* fall-through */ + case 0x07: + /* TREMOLO 07 + * ProTracker compatible. + * This command is a kind of "volume vibrato". The left + * number is the speed of the tremolo, and the right one is + * the depth. The depth must be quite high before the effect + * is audible. + */ + break; + case 0x08: + /* HOLD and DECAY 08 + * This command must be on the same line as the note. The + * left half of the argument determines the decay and the + * right half the hold. + */ + event->fxt = event->fxp = 0; + break; + case 0x09: + /* SECONDARY TEMPO 09 + * This sets the secondary tempo (the number of timing + * pulses per note). The argument must be from 01 to 20 (hex). + */ + if (event->fxp >= 0x01 && event->fxp <= 0x20) { + event->fxt = FX_SPEED; + } else { + event->fxt = event->fxp = 0; + } + break; + case 0x0a: + /* 0A not mentioned but it's Protracker-compatible */ + /* fall-through */ + case 0x0b: + /* POSITION JUMP 0B + * The song plays up to this command and then jumps to + * another position in the play sequence. The song then + * loops from the point jumped to, to the end of the song + * forever. The purpose is to allow for introductions that + * play only once. + */ + /* fall-through */ + case 0x0c: + /* SET VOLUME 0C + * Overrides the default volume of an instrument. + */ + if (!hexvol) { + int p = event->fxp; + event->fxp = (p >> 8) * 10 + (p & 0xff); + } + break; + case 0x0d: + /* VOLUME SLIDE 0D + * Smoothly slides the volume up or down. The left half of + * the argument increases the volume. The right decreases it. + */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0e: + /* SYNTH JUMP 0E + * When used with synthetic or hybrid instruments, it + * triggers a jump in the Waveform Sequence List. The argument + * is the jump destination (line no). + */ + event->fxt = event->fxp = 0; + break; + case 0x0f: + /* MISCELLANEOUS 0F + * The effect depends upon the value of the argument. + */ + if (event->fxp == 0x00) { /* Jump to next block */ + event->fxt = FX_BREAK; + break; + } else if (event->fxp <= 0xf0) { + event->fxt = FX_S3M_BPM; + event->fxp = mmd_convert_tempo(event->fxp, bpm_on, med_8ch); + break; + } else switch (event->fxp) { + case 0xf1: /* Play note twice */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 3; + break; + case 0xf2: /* Delay note */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | 3; + break; + case 0xf3: /* Play note three times */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 2; + break; + case 0xf8: /* Turn filter off */ + case 0xf9: /* Turn filter on */ + case 0xfa: /* MIDI pedal on */ + case 0xfb: /* MIDI pedal off */ + case 0xfd: /* Set pitch */ + case 0xfe: /* End of song */ + event->fxt = event->fxp = 0; + break; + case 0xff: /* Turn note off */ + event->fxt = event->fxp = 0; + event->note = XMP_KEY_CUT; + break; + default: + event->fxt = event->fxp = 0; + } + break; + case 0x11: + /* SLIDE PITCH UP (only once) 11 + * Equivalent to ProTracker command E1x. + * Lets you control the pitch with great accuracy. This + * command changes only this occurrence of the note. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = FX_F_PORTA_UP; + break; + case 0x12: + /* SLIDE DOWN (only once) 12 + * Equivalent to ProTracker command E2x. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = FX_F_PORTA_DN; + break; + case 0x14: + /* VIBRATO 14 + * ProTracker compatible. This is similar to command 04 + * except the depth is halved, to give greater accuracy. + */ + event->fxt = FX_VIBRATO; + break; + case 0x15: + /* SET FINETUNE 15 + * Set a finetune value for a note, overrides the default + * fine tune value of the instrument. Negative numbers must + * be entered as follows: + * -1 => FF -3 => FD -5 => FB -7 => F9 + * -2 => FE -4 => FC -6 => FA -8 => F8 + */ + event->fxt = FX_FINETUNE; + event->fxp = (event->fxp + 8) << 4; + break; + case 0x16: + /* LOOP 16 + * Creates a loop within a block. 1600 marks the beginning + * of the loop. The next occurrence of the 16 command + * designates the number of loops. Same as ProTracker E6x. + */ + event->fxt = FX_EXTENDED; + if (event->fxp > 0x0f) + event->fxp = 0x0f; + event->fxp |= 0x60; + break; + case 0x18: + /* STOP NOTE 18 + * Cuts the note by zeroing the volume at the pulse specified + * in the argument value. This is the same as ProTracker + * command ECx. + */ + event->fxt = FX_EXTENDED; + if (event->fxp > 0x0f) + event->fxp = 0x0f; + event->fxp |= 0xc0; + break; + case 0x19: + /* SET SAMPLE START OFFSET + * Same as ProTracker command 9. + * When playing a sample, this command sets the starting + * offset (at steps of $100 = 256 bytes). Useful for speech + * samples. + */ + event->fxt = FX_OFFSET; + break; + case 0x1a: + /* SLIDE VOLUME UP ONCE + * Only once ProTracker command EAx. Lets volume slide + * slowly once per line. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = event->fxp ? FX_F_VSLIDE_UP : 0; + break; + case 0x1b: + /* SLIDE VOLUME DOWN ONCE + * Only once ProTracker command EBx. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = event->fxp ? FX_F_VSLIDE_DN : 0; + break; + case 0x1d: + /* JUMP TO NEXT BLOCK 1D + * Jumps to the next line in the PLAY SEQUENCE LIST at the + * specified line. ProTracker command D. This command is + * like F00, except that you can specify the line number of + * the first line to be played. The line number must be + * specified in HEX. + */ + event->fxt = FX_BREAK; + break; + case 0x1e: + /* PLAY LINE x TIMES 1E + * Plays only commands, notes not replayed. ProTracker + * pattern delay. + */ + event->fxt = FX_PATT_DELAY; + break; + case 0x1f: + /* Command 1F: NOTE DELAY AND RETRIGGER + * (Protracker commands EC and ED) + * Gives you accurate control over note playing. You can + * delay the note any number of ticks, and initiate fast + * retrigger. Level 1 = note delay value, level 2 = retrigger + * value. + */ + if (MSN(event->fxp)) { + /* delay */ + event->fxt = FX_EXTENDED; + event->fxp = 0xd0 | (event->fxp >> 4); + } else if (LSN(event->fxp)) { + /* retrig */ + event->fxt = FX_EXTENDED; + event->fxp = 0x90 | (event->fxp & 0x0f); + } + break; + case 0x20: + /* Command 20: REVERSE SAMPLE / RELATIVE SAMPLE OFFSET + * With command level $00, the sample is reversed. With other + * levels, it changes the sample offset, just like command 19, + * except the command level is the new offset relative to the + * current sample position being played. + * Note: 20 00 only works on the same line as a new note. + */ + if (event->fxp == 0 && event->note != 0) { + event->fxt = FX_REVERSE; + event->fxp = 1; + } else { + event->fxt = event->fxp = 0; + } + break; + case 0x2e: + /* Command 2E: SET TRACK PANNING + * Allows track panning to be changed during play. The track + * on which the player command appears is the track affected. + * The command level is in signed hex: $F0 to $10 = -16 to 16 + * decimal. + */ + if (event->fxp >= 0xf0 || event->fxp <= 0x10) { + int fxp = (signed char)event->fxp + 16; + fxp <<= 3; + if (fxp == 0x100) + fxp--; + event->fxt = FX_SETPAN; + event->fxp = fxp; + } + break; + default: + event->fxt = event->fxp = 0; + break; + } +} + + +struct mmd_instrument_info +{ + uint32 length; + uint32 rep; + uint32 replen; + int sampletrans; + int synthtrans; + int flg; + int enable; +}; + +/* Interpret loop/flag parameters for sampled instruments (sample, hybrid, IFF). + * This is common code to avoid replicating this mess in each loader. */ +static void mmd_load_instrument_common( + struct mmd_instrument_info *info, struct InstrHdr *instr, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + info->enable = 1; + info->flg = 0; + if (ver >= 2 && expdata->s_ext_entrsz >= 8) { /* MMD2+ instrument flags */ + uint8 instr_flags = exp_smp->instr_flags; + + if (instr_flags & SSFLG_LOOP) { + info->flg |= XMP_SAMPLE_LOOP; + } + if (instr_flags & SSFLG_PINGPONG) { + info->flg |= XMP_SAMPLE_LOOP_BIDIR; + } + if (instr_flags & SSFLG_DISABLED) { + info->enable = 0; + } + } else { + if (sample->replen > 1) { + info->flg |= XMP_SAMPLE_LOOP; + } + } + + info->sampletrans = 36 + sample->strans; + info->synthtrans = 12 + sample->strans; + + if (instr) { + int sample_type = instr->type & ~(S_16|MD16|STEREO); + + if ((ver >= 3 && sample_type == 0) || sample_type == 7) { + /* Mix mode transposes samples down two octaves. + * This does not apply to octave samples or synths. + * ExtSamples (7) are transposed regardless. */ + info->sampletrans -= 24; + } + + info->length = instr->length; + + if (ver >= 2 && expdata->s_ext_entrsz >= 18) { /* MMD2+ long repeat */ + info->rep = exp_smp->long_repeat; + info->replen = exp_smp->long_replen; + } else { + info->rep = sample->rep << 1; + info->replen = sample->replen << 1; + } + + if (instr->type & S_16) { + info->flg |= XMP_SAMPLE_16BIT; + info->length >>= 1; + info->rep >>= 1; + info->replen >>= 1; + } + + /* STEREO means that this is a stereo sample. The sample + * is not interleaved. The left channel comes first, + * followed by the right channel. Important: Length + * specifies the size of one channel only! The actual memory + * usage for both samples is length * 2 bytes. + */ + if (instr->type & STEREO) { + D_(D_WARN "stereo sample unsupported"); + /* TODO: implement stereo sample support. + info.flg |= XMP_SAMPLE_STEREO; + */ + } + } +} + +/* Compatibility for MED Soundstudio v2 default pitch events. + * For single-octave samples and synthetics, MED mix mode note 0x01 + * plays the note number stored in the InstrExt default pitch field. + * Mix mode events are currently transposed up an octave and are offset + * down by 1 for the instrument map, hence index 12. + * + * See med.h for more info. + */ +static void mmd_set_default_pitch_note(struct xmp_instrument *xxi, + struct InstrExt *exp_smp, int ver) +{ + if (ver >= 3) { + int note = MMD3_DEFAULT_NOTE; + + if (exp_smp->default_pitch) + note = exp_smp->default_pitch - 1; + + if (note >= 0 && note < XMP_MAX_KEYS) + xxi->map[12].xpo = note; + } +} + +int mmd_alloc_tables(struct module_data *m, int i, struct SynthInstr *synth) +{ + struct med_module_extras *me = (struct med_module_extras *)m->extra; + + me->vol_table[i] = (uint8 *) calloc(1, synth->voltbllen); + if (me->vol_table[i] == NULL) + goto err; + memcpy(me->vol_table[i], synth->voltbl, synth->voltbllen); + + me->wav_table[i] = (uint8 *) calloc(1, synth->wftbllen); + if (me->wav_table[i] == NULL) + goto err1; + memcpy(me->wav_table[i], synth->wftbl, synth->wftbllen); + + return 0; + + err1: + free(me->vol_table[i]); + err: + return -1; +} + +static int mmd_load_hybrid_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct SynthInstr *synth, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct med_instrument_extras *ie; + struct mmd_instrument_info info; + struct InstrHdr instr; + int pos = hio_tell(f); + int j; + + /* Sanity check */ + if (smp_idx >= mod->smp) { + return -1; + } + + synth->defaultdecay = hio_read8(f); + hio_seek(f, 3, SEEK_CUR); + synth->rep = hio_read16b(f); + synth->replen = hio_read16b(f); + synth->voltbllen = hio_read16b(f); + synth->wftbllen = hio_read16b(f); + synth->volspeed = hio_read8(f); + synth->wfspeed = hio_read8(f); + synth->wforms = hio_read16b(f); + hio_read(synth->voltbl, 1, 128, f); + hio_read(synth->wftbl, 1, 128, f); + + /* Sanity check */ + if (synth->voltbllen > 128 || synth->wftbllen > 128 || + synth->wforms < 1 || synth->wforms > 64) { + return -1; + } + + for (j = 0; j < synth->wforms; j++) + synth->wf[j] = hio_read32b(f); + + if (hio_error(f)) + return -1; + + hio_seek(f, pos - 6 + synth->wf[0], SEEK_SET); + instr.length = hio_read32b(f); + instr.type = hio_read16b(f); + + /* Hybrids using IFFOCT/ext samples as their sample don't seem to + * exist. If one is found, this should be fixed. OctaMED SS 1.03 + * converts 16-bit samples to 8-bit when changed to hybrid. */ + if (instr.type != 0) { + D_(D_CRIT "unsupported sample type %d for hybrid", instr.type); + return -1; + } + + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + + xxi->nsm = synth->wforms; + if (libxmp_alloc_subinstrument(mod, i, synth->wforms) < 0) + return -1; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth->volspeed; + ie->wts = synth->wfspeed; + ie->vtlen = synth->voltbllen; + ie->wtlen = synth->wftbllen; + + mmd_load_instrument_common(&info, &instr, expdata, exp_smp, sample, ver); + mmd_set_default_pitch_note(xxi, exp_smp, ver); + sub = &xxi->sub[0]; + + sub->pan = 0x80; + sub->vol = info.enable ? sample->svol : 0; + sub->xpo = info.sampletrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = info.length; + xxs->lps = info.rep; + xxs->lpe = info.rep + info.replen; + xxs->flg = info.flg; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + + for (j = 1; j < synth->wforms; j++) { + sub = &xxi->sub[j]; + xxs = &mod->xxs[smp_idx]; + + /* Sanity check */ + if (j >= xxi->nsm || smp_idx >= mod->smp) + return -1; + + sub->pan = 0x80; + sub->vol = info.enable ? 64 : 0; + sub->xpo = info.synthtrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune; + + hio_seek(f, pos - 6 + synth->wf[j], SEEK_SET); + + xxs->len = hio_read16b(f) * 2; + xxs->lps = 0; + xxs->lpe = xxs->len; + xxs->flg = XMP_SAMPLE_LOOP; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + } + return 0; +} + +static int mmd_load_synth_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct SynthInstr *synth, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct med_instrument_extras *ie; + struct mmd_instrument_info info; + int pos = hio_tell(f); + int j; + + mmd_load_instrument_common(&info, NULL, expdata, exp_smp, sample, ver); + mmd_set_default_pitch_note(xxi, exp_smp, ver); + + synth->defaultdecay = hio_read8(f); + hio_seek(f, 3, SEEK_CUR); + synth->rep = hio_read16b(f); + synth->replen = hio_read16b(f); + synth->voltbllen = hio_read16b(f); + synth->wftbllen = hio_read16b(f); + synth->volspeed = hio_read8(f); + synth->wfspeed = hio_read8(f); + synth->wforms = hio_read16b(f); + hio_read(synth->voltbl, 1, 128, f); + hio_read(synth->wftbl, 1, 128, f); + + if (synth->wforms == 0xffff) { + xxi->nsm = 0; + return 1; + } + if (synth->voltbllen > 128 || + synth->wftbllen > 128 || + synth->wforms > 64) { + return -1; + } + + for (j = 0; j < synth->wforms; j++) + synth->wf[j] = hio_read32b(f); + + if (hio_error(f)) + return -1; + + D_(D_INFO " VS:%02x WS:%02x WF:%02x %02x %+3d %+1d", + synth->volspeed, synth->wfspeed, + synth->wforms & 0xff, + sample->svol, + sample->strans, + exp_smp->finetune); + + if (libxmp_med_new_instrument_extras(&mod->xxi[i]) != 0) + return -1; + + xxi->nsm = synth->wforms; + if (libxmp_alloc_subinstrument(mod, i, synth->wforms) < 0) + return -1; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth->volspeed; + ie->wts = synth->wfspeed; + ie->vtlen = synth->voltbllen; + ie->wtlen = synth->wftbllen; + + for (j = 0; j < synth->wforms; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[smp_idx]; + + /* Sanity check */ + if (j >= xxi->nsm || smp_idx >= mod->smp) + return -1; + + sub->pan = 0x80; + sub->vol = info.enable ? 64 : 0; + sub->xpo = info.synthtrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune; + + hio_seek(f, pos - 6 + synth->wf[j], SEEK_SET); + + xxs->len = hio_read16b(f) * 2; + xxs->lps = 0; + xxs->lpe = xxs->len; + xxs->flg = XMP_SAMPLE_LOOP; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + } + + return 0; +} + +static int mmd_load_sampled_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct InstrHdr *instr, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct mmd_instrument_info info; + int j, k; + + /* Sanity check */ + if (smp_idx >= mod->smp) + return -1; + + /* hold & decay support */ + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + MED_INSTRUMENT_EXTRAS(*xxi)->hold = exp_smp->hold; + xxi->rls = 0xfff - (exp_smp->decay << 4); + + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mmd_load_instrument_common(&info, instr, expdata, exp_smp, sample, ver); + mmd_set_default_pitch_note(xxi, exp_smp, ver); + sub = &xxi->sub[0]; + + sub->vol = info.enable ? sample->svol : 0; + sub->pan = 0x80; + sub->xpo = info.sampletrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune << 4; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = info.length; + xxs->lps = info.rep; + xxs->lpe = info.rep + info.replen; + xxs->flg = info.flg; + + /* Restrict sampled instruments to 3 octave range except for MMD3. + * Checked in MMD0 with med.egypian/med.medieval from Lemmings 2 + * and MED.ParasolStars, MMD1 with med.Lemmings2 + */ + + if (ver < 3) { + /* ExtSamples have two extra octaves. */ + int octaves = (instr->type & 7) == 7 ? 5 : 3; + for (j = 0; j < 9; j++) { + for (k = 0; k < 12; k++) { + int xpo = 0; + + if (j < 1) + xpo = 12 * (1 - j); + else if (j > octaves) + xpo = -12 * (j - octaves); + + xxi->map[12 * j + k].xpo = xpo; + } + } + } + + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, xxs, NULL) < 0) { + return -1; + } + + return 0; +} + +static const char iffoct_insmap[6][9] = { + /* 2 */ { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, + /* 3 */ { 2, 2, 2, 2, 2, 2, 1, 1, 0 }, + /* 4 */ { 3, 3, 3, 2, 2, 2, 1, 1, 0 }, + /* 5 */ { 4, 4, 4, 3, 2, 2, 1, 1, 0 }, + /* 6 */ { 5, 5, 5, 5, 4, 3, 2, 1, 0 }, + /* 7 */ { 6, 6, 6, 6, 5, 4, 3, 2, 1 } +}; + +static const char iffoct_xpomap[6][9] = { + /* 2 */ { 12, 12, 12, 0, 0, 0, 0, 0, 0 }, + /* 3 */ { 12, 12, 12, 12, 12, 12, 0, 0,-12 }, + /* 4 */ { 12, 12, 12, 0, 0, 0,-12,-12,-24 }, + /* 5 */ { 24, 24, 24, 12, 0, 0,-12,-24,-36 }, + /* 6 */ { 12, 12, 12, 12, 0,-12,-24,-36,-48 }, + /* 7 */ { 12, 12, 12, 12, 0,-12,-24,-36,-48 }, +}; + +static int mmd_load_iffoct_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct InstrHdr *instr, int num_oct, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct mmd_instrument_info info; + int size, j, k; + + if (num_oct < 2 || num_oct > 7) + return -1; + + /* Sanity check */ + if (smp_idx + num_oct > mod->smp) + return -1; + + /* Sanity check - ignore absurdly large IFFOCT instruments. */ + if ((int)instr->length < 0) + return -1; + + /* hold & decay support */ + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + + MED_INSTRUMENT_EXTRAS(*xxi)->hold = exp_smp->hold; + xxi->rls = 0xfff - (exp_smp->decay << 4); + + xxi->nsm = num_oct; + if (libxmp_alloc_subinstrument(mod, i, num_oct) < 0) + return -1; + + /* base octave size */ + size = instr->length / ((1 << num_oct) - 1); + mmd_load_instrument_common(&info, instr, expdata, exp_smp, sample, ver); + + for (j = 0; j < num_oct; j++) { + sub = &xxi->sub[j]; + + sub->vol = info.enable ? sample->svol : 0; + sub->pan = 0x80; + sub->xpo = info.sampletrans - 12; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune << 4; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = size; + xxs->lps = info.rep; + xxs->lpe = info.rep + info.replen; + xxs->flg = info.flg; + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, xxs, NULL) < 0) { + return -1; + } + + smp_idx++; + size <<= 1; + info.rep <<= 1; + info.replen <<= 1; + } + + /* instrument mapping */ + + for (j = 0; j < 9; j++) { + for (k = 0; k < 12; k++) { + xxi->map[12 * j + k].ins = iffoct_insmap[num_oct - 2][j]; + xxi->map[12 * j + k].xpo = iffoct_xpomap[num_oct - 2][j]; + } + } + + return 0; +} + +/* Number of octaves in IFFOCT samples */ +const int mmd_num_oct[6] = { 5, 3, 2, 4, 6, 7 }; + +int mmd_load_instrument(HIO_HANDLE *f, struct module_data *m, int i, int smp_idx, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct InstrHdr instr; + struct SynthInstr synth; + int sample_type; + + instr.length = hio_read32b(f); + instr.type = (int16)hio_read16b(f); + sample_type = instr.type & ~(S_16|MD16|STEREO); + + D_(D_INFO "[%2x] %-40.40s %d", i, m->mod.xxi[i].name, instr.type); + + if (instr.type == -2) { /* Hybrid */ + int ret = mmd_load_hybrid_instrument(f, m, i, smp_idx, + &synth, expdata, exp_smp, sample, ver); + + if (ret < 0) { + D_(D_CRIT "error loading hybrid instrument %d", i); + return -1; + } + + smp_idx += synth.wforms; + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + } else if (instr.type == -1) { /* Synthetic */ + int ret = mmd_load_synth_instrument(f, m, i, smp_idx, + &synth, expdata, exp_smp, sample, ver); + + if (ret > 0) + return smp_idx; + + if (ret < 0) { + D_(D_CRIT "error loading synthetic instrument %d", i); + return -1; + } + + smp_idx += synth.wforms; + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + } else if (instr.type >= 1 && instr.type <= 6) { /* IFFOCT */ + int ret; + const int oct = mmd_num_oct[instr.type - 1]; + + ret = mmd_load_iffoct_instrument(f, m, i, smp_idx, + &instr, oct, expdata, exp_smp, sample, ver); + + if (ret < 0) { + D_(D_CRIT "error loading IFFOCT instrument %d", i); + return -1; + } + + smp_idx += oct; + + } else if (sample_type == 0 || sample_type == 7) { /* Sample */ + int ret; + + ret = mmd_load_sampled_instrument(f, m, i, smp_idx, + &instr, expdata, exp_smp, sample, ver); + + if (ret < 0) { + D_(D_CRIT "error loading sample %d", i); + return -1; + } + + smp_idx++; + + } else { + /* Invalid instrument type */ + D_(D_CRIT "invalid instrument type: %d", instr.type); + return -1; + } + return smp_idx; +} + + +void mmd_set_bpm(struct module_data *m, int med_8ch, int deftempo, + int bpm_on, int bpmlen) +{ + struct xmp_module *mod = &m->mod; + + mod->bpm = mmd_convert_tempo(deftempo, bpm_on, med_8ch); + + /* 8-channel mode completely overrides regular timing. + * See mmd_convert_tempo for more info. + */ + if (med_8ch) { + m->time_factor = DEFAULT_TIME_FACTOR; + } else if (bpm_on) { + m->time_factor = DEFAULT_TIME_FACTOR * 4 / bpmlen; + } +} + + +void mmd_info_text(HIO_HANDLE *f, struct module_data *m, int offset) +{ + int type, len; + + /* Copy song info text */ + hio_read32b(f); /* skip next */ + hio_read16b(f); /* skip reserved */ + type = hio_read16b(f); + D_(D_INFO "mmdinfo type=%d", type); + if (type == 1) { /* 1 = ASCII */ + len = hio_read32b(f); + D_(D_INFO "mmdinfo length=%d", len); + if (len > 0 && len < hio_size(f)) { + m->comment = (char *) malloc(len + 1); + if (m->comment == NULL) + return; + hio_read(m->comment, 1, len, f); + m->comment[len] = 0; + } + } +} diff --git a/thirdparty/libxmp/src/loaders/mod.h b/thirdparty/libxmp/src/loaders/mod.h new file mode 100644 index 0000000..682aef0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mod.h @@ -0,0 +1,59 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_LOADERS_MOD_H +#define LIBXMP_LOADERS_MOD_H + +struct mod_instrument { + uint8 name[22]; /* Instrument name */ + uint16 size; /* Sample length in 16-bit words */ + int8 finetune; /* Finetune (signed nibble) */ + int8 volume; /* Linear playback volume */ + uint16 loop_start; /* Loop start in 16-bit words */ + uint16 loop_size; /* Loop length in 16-bit words */ +}; + +struct mod_header { + uint8 name[20]; + struct mod_instrument ins[31]; + uint8 len; + uint8 restart; /* Number of patterns in Soundtracker, + * Restart in Noisetracker/Startrekker, + * 0x7F in Protracker + */ + uint8 order[128]; + uint8 magic[4]; +}; + +#ifndef LIBXMP_CORE_PLAYER +/* Soundtracker 15-instrument module header */ + +struct st_header { + uint8 name[20]; + struct mod_instrument ins[15]; + uint8 len; + uint8 restart; + uint8 order[128]; +}; +#endif + +#endif /* LIBXMP_LOADERS_MOD_H */ diff --git a/thirdparty/libxmp/src/loaders/mod_load.c b/thirdparty/libxmp/src/loaders/mod_load.c new file mode 100644 index 0000000..425604c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mod_load.c @@ -0,0 +1,1010 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This loader recognizes the following variants of the Protracker + * module format: + * + * - Protracker M.K. and M!K! + * - Protracker songs + * - Noisetracker N.T. and M&K! (not tested) + * - Fast Tracker 6CHN and 8CHN + * - Fasttracker II/Take Tracker ?CHN and ??CH + * - Mod's Grave M.K. w/ 8 channels (WOW) + * - Atari Octalyser CD61 and CD81 + * - Digital Tracker FA04, FA06 and FA08 + * - TakeTracker TDZ1, TDZ2, TDZ3, and TDZ4 + * - (unknown) NSMS, LARD + * + * The 'lite' version only recognizes Protracker M.K. and + * Fasttracker ?CHN and ??CH formats. + */ + +#include +#include "loader.h" +#include "mod.h" + +#ifndef LIBXMP_CORE_PLAYER +struct mod_magic { + const char *magic; + int flag; + int id; + int ch; +}; + +#define TRACKER_PROTRACKER 0 +#define TRACKER_NOISETRACKER 1 +#define TRACKER_SOUNDTRACKER 2 +#define TRACKER_FASTTRACKER 3 +#define TRACKER_FASTTRACKER2 4 +#define TRACKER_OCTALYSER 5 +#define TRACKER_TAKETRACKER 6 +#define TRACKER_DIGITALTRACKER 7 +#define TRACKER_FLEXTRAX 8 +#define TRACKER_MODSGRAVE 9 +#define TRACKER_SCREAMTRACKER3 10 +#define TRACKER_OPENMPT 11 +#define TRACKER_UNKNOWN_CONV 95 +#define TRACKER_CONVERTEDST 96 +#define TRACKER_CONVERTED 97 +#define TRACKER_CLONE 98 +#define TRACKER_UNKNOWN 99 + +#define TRACKER_PROBABLY_NOISETRACKER 20 + +const struct mod_magic mod_magic[] = { + {"M.K.", 0, TRACKER_PROTRACKER, 4}, + {"M!K!", 1, TRACKER_PROTRACKER, 4}, + {"M&K!", 1, TRACKER_NOISETRACKER, 4}, + {"N.T.", 1, TRACKER_NOISETRACKER, 4}, + {"6CHN", 0, TRACKER_FASTTRACKER, 6}, + {"8CHN", 0, TRACKER_FASTTRACKER, 8}, + {"CD61", 1, TRACKER_OCTALYSER, 6}, /* Atari STe/Falcon */ + {"CD81", 1, TRACKER_OCTALYSER, 8}, /* Atari STe/Falcon */ + {"TDZ1", 1, TRACKER_TAKETRACKER, 1}, /* TakeTracker 1ch */ + {"TDZ2", 1, TRACKER_TAKETRACKER, 2}, /* TakeTracker 2ch */ + {"TDZ3", 1, TRACKER_TAKETRACKER, 3}, /* TakeTracker 3ch */ + {"TDZ4", 1, TRACKER_TAKETRACKER, 4}, /* see XModule SaveTracker.c */ + {"FA04", 1, TRACKER_DIGITALTRACKER, 4}, /* Atari Falcon */ + {"FA06", 1, TRACKER_DIGITALTRACKER, 6}, /* Atari Falcon */ + {"FA08", 1, TRACKER_DIGITALTRACKER, 8}, /* Atari Falcon */ + {"LARD", 1, TRACKER_UNKNOWN, 4}, /* in judgement_day_gvine.mod */ + {"NSMS", 1, TRACKER_UNKNOWN, 4}, /* in Kingdom.mod */ +}; + +/* Returns non-zero if the given tracker ONLY supports VBlank timing. This + * should be used only when the tracker is known for sure, e.g. magic match. */ +static int tracker_is_vblank(int id) +{ + switch (id) { + case TRACKER_NOISETRACKER: + case TRACKER_SOUNDTRACKER: + return 1; + default: + return 0; + } +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int mod_test(HIO_HANDLE *, char *, const int); +static int mod_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mod = { + #ifdef LIBXMP_CORE_PLAYER + "Protracker", + #else + "Amiga Protracker/Compatible", + #endif + mod_test, + mod_load +}; + +#ifndef LIBXMP_CORE_PLAYER +static int validate_pattern(uint8 *buf) +{ + int i, j; + + for (i = 0; i < 64; i++) { + for (j = 0; j < 4; j++) { + uint8 *d = buf + (i * 4 + j) * 4; + if ((d[0] >> 4) > 1) { + D_(D_CRIT "invalid pattern data: row %d ch %d: %02x", i, j, d[0]); + return -1; + } + } + } + + return 0; +} +#endif + +static int mod_test(HIO_HANDLE * f, char *t, const int start) +{ + int i; + char buf[4]; + #ifndef LIBXMP_CORE_PLAYER + uint8 pat_buf[1024]; + int smp_size, num_pat; + long size; + int count; + int detected; + #endif + + hio_seek(f, start + 1080, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) { + return -1; + } + + if (!strncmp(buf + 2, "CH", 2) && + isdigit((unsigned char)buf[0]) && isdigit((unsigned char)buf[1])) { + i = (buf[0] - '0') * 10 + buf[1] - '0'; + if (i > 0 && i <= 32) { + goto found; + } + } + + if (!strncmp(buf + 1, "CHN", 3) && isdigit((unsigned char)*buf)) { + if (*buf - '0') { + goto found; + } + } + +#ifdef LIBXMP_CORE_PLAYER + if (memcmp(buf, "M.K.", 4)) + return -1; +#else + for (i = 0; i < ARRAY_SIZE(mod_magic); i++) { + if (!memcmp(buf, mod_magic[i].magic, 4)) + break; + } + if (i >= ARRAY_SIZE(mod_magic)) { + return -1; + } + + detected = mod_magic[i].flag; + + /* + * Sanity check to prevent loading NoiseRunner and other module + * formats with valid magic at offset 1080 (e.g. His Master's Noise) + */ + + hio_seek(f, start + 20, SEEK_SET); + for (i = 0; i < 31; i++) { + uint8 x; + + hio_seek(f, 22, SEEK_CUR); /* Instrument name */ + + /* OpenMPT can create mods with large samples */ + hio_read16b(f); /* sample size */ + + /* Chris Spiegel tells me that sandman.mod has 0x20 in finetune */ + x = hio_read8(f); + if (x & 0xf0 && x != 0x20) /* test finetune */ + return -1; + if (hio_read8(f) > 0x40) /* test volume */ + return -1; + hio_read16b(f); /* loop start */ + hio_read16b(f); /* loop size */ + } + + /* The following checks are only relevant for filtering out atypical + * M.K. variants. If the magic is from a recognizable source, skip them. */ + if (detected) + goto found; + + /* Test for UNIC tracker modules + * + * From Gryzor's Pro-Wizard PW_FORMATS-Engl.guide: + * ``The UNIC format is very similar to Protracker... At least in the + * heading... same length : 1084 bytes. Even the "M.K." is present, + * sometimes !! Maybe to disturb the rippers.. hehe but Pro-Wizard + * doesn't test this only!'' + */ + + /* get file size */ + size = hio_size(f); + smp_size = 0; + hio_seek(f, start + 20, SEEK_SET); + + /* get samples size */ + for (i = 0; i < 31; i++) { + hio_seek(f, 22, SEEK_CUR); + smp_size += 2 * hio_read16b(f); /* Length in 16-bit words */ + hio_seek(f, 6, SEEK_CUR); + } + + /* get number of patterns */ + num_pat = 0; + hio_seek(f, start + 952, SEEK_SET); + for (i = 0; i < 128; i++) { + uint8 x = hio_read8(f); + if (x > 0x7f) + break; + if (x > num_pat) + num_pat = x; + } + num_pat++; + + /* see if module size matches UNIC */ + if (start + 1084 + num_pat * 0x300 + smp_size == size) { + D_(D_CRIT "module size matches UNIC"); + return -1; + } + + /* validate pattern data in an attempt to catch UNICs with MOD size */ + for (count = i = 0; i < num_pat; i++) { + hio_seek(f, start + 1084 + 1024 * i, SEEK_SET); + if (!hio_read(pat_buf, 1024, 1, f)) { + D_(D_WARN "pattern %d: failed to read pattern data", i); + return -1; + } + if (validate_pattern(pat_buf) < 0) { + D_(D_WARN "pattern %d: error in pattern data", i); + /* Allow a few errors, "lexstacy" has 0x52 */ + count++; + } + } + if (count > 2) { + return -1; + } +#endif /* LIBXMP_CORE_PLAYER */ + +found: + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + + +#ifndef LIBXMP_CORE_PLAYER +static int is_st_ins(const char *s) +{ + if (s[0] != 's' && s[0] != 'S') + return 0; + if (s[1] != 't' && s[1] != 'T') + return 0; + if (s[2] != '-' || s[5] != ':') + return 0; + if (!isdigit((unsigned char)s[3]) || !isdigit((unsigned char)s[4])) + return 0; + + return 1; +} + +static int get_tracker_id(struct module_data *m, struct mod_header *mh, int id) +{ + struct xmp_module *mod = &m->mod; + int has_loop_0 = 0; + int has_vol_in_empty_ins = 0; + int i; + + /* Check if has instruments with loop size 0 */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].loop_size == 0) { + has_loop_0 = 1; + break; + } + } + + /* Check if has instruments with size 0 and volume > 0 */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].size == 0 && mh->ins[i].volume > 0) { + has_vol_in_empty_ins = 1; + break; + } + } + + /* + * Test Protracker-like files + */ + if (mh->restart == mod->pat) { + if (mod->chn == 4) { + id = TRACKER_SOUNDTRACKER; + } else { + id = TRACKER_UNKNOWN; + } + } else if (mh->restart == 0x78) { + if (mod->chn == 4) { + /* Can't trust this for Noisetracker, MOD.Data City Remix + * has Protracker effects and Noisetracker restart byte + */ + id = TRACKER_PROBABLY_NOISETRACKER; + } else { + id = TRACKER_UNKNOWN; + } + return id; + } else if (mh->restart < 0x7f) { + if (mod->chn == 4 && !has_vol_in_empty_ins) { + id = TRACKER_NOISETRACKER; + } else { + id = TRACKER_UNKNOWN; /* ? */ + } + mod->rst = mh->restart; + } else if (mh->restart == 0x7f) { + if (mod->chn == 4) { + if (has_loop_0) { + id = TRACKER_CLONE; + } + } else { + id = TRACKER_SCREAMTRACKER3; + } + return id; + } else if (mh->restart > 0x7f) { + id = TRACKER_UNKNOWN; /* ? */ + return id; + } + + if (!has_loop_0) { /* All loops are size 2 or greater */ + + for (i = 0; i < 31; i++) { + if (mh->ins[i].size == 1 && mh->ins[i].volume == 0) { + return TRACKER_CONVERTED; + } + } + + for (i = 0; i < 31; i++) { + if (is_st_ins((char *)mh->ins[i].name)) + break; + } + if (i == 31) { /* No st- instruments */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].size != 0 + || mh->ins[i].loop_size != 1) { + continue; + } + + switch (mod->chn) { + case 4: + if (has_vol_in_empty_ins) { + id = TRACKER_OPENMPT; + } else { + id = TRACKER_NOISETRACKER; + /* or Octalyser */ + } + break; + case 6: + case 8: + id = TRACKER_OCTALYSER; + break; + default: + id = TRACKER_UNKNOWN; + } + return id; + } + + if (mod->chn == 4) { + id = TRACKER_PROTRACKER; + } else if (mod->chn == 6 || mod->chn == 8) { + /* FastTracker 1.01? */ + id = TRACKER_FASTTRACKER; + } else { + id = TRACKER_UNKNOWN; + } + } + } else { /* Has loops with size 0 */ + for (i = 15; i < 31; i++) { + /* Is the name or size set? */ + if (mh->ins[i].name[0] || mh->ins[i].size > 0) + break; + } + if (i == 31 && is_st_ins((char *)mh->ins[14].name)) { + return TRACKER_CONVERTEDST; + } + + /* Assume that Fast Tracker modules won't have ST- instruments */ + for (i = 0; i < 31; i++) { + if (is_st_ins((char *)mh->ins[i].name)) + break; + } + if (i < 31) { + return TRACKER_UNKNOWN_CONV; + } + + if (mod->chn == 4 || mod->chn == 6 || mod->chn == 8) { + return TRACKER_FASTTRACKER; + } + + id = TRACKER_UNKNOWN; /* ?! */ + } + + return id; +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct xmp_event *event; + struct mod_header mh; + char magic[8]; + uint8 *patbuf; +#ifndef LIBXMP_CORE_PLAYER + uint8 pat_high_fxx[256]; + const char *tracker = ""; + int detected = 0; + int tracker_id = TRACKER_PROTRACKER; + int out_of_range = 0; + int maybe_wow = 1; + int smp_size, ptsong = 0; + int needs_timing_detection = 0; + int samerow_fxx = 0; /* speed + BPM set on same row */ + int high_fxx = 0; /* high Fxx is used anywhere */ +#endif + int ptkloop = 0; /* Protracker loop */ + + LOAD_INIT(); + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 0; + #ifndef LIBXMP_CORE_PLAYER + smp_size = 0; + #else + m->quirk |= QUIRK_PROTRACK; + #endif + m->period_type = PERIOD_MODRNG; + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */ + mh.ins[i].size = hio_read16b(f); /* Length in 16-bit words */ + mh.ins[i].finetune = hio_read8(f); /* Finetune (signed nibble) */ + mh.ins[i].volume = hio_read8(f); /* Linear playback volume */ + mh.ins[i].loop_start = hio_read16b(f); /* Loop start in 16-bit words */ + mh.ins[i].loop_size = hio_read16b(f); /* Loop size in 16-bit words */ + + #ifndef LIBXMP_CORE_PLAYER + /* Mod's Grave WOW files are converted from 669s and have default + * finetune and volume. + */ + if (mh.ins[i].size && (mh.ins[i].finetune != 0 || mh.ins[i].volume != 64)) + maybe_wow = 0; + + smp_size += 2 * mh.ins[i].size; + #endif + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + memset(magic, 0, sizeof(magic)); + hio_read(magic, 1, 4, f); + if (hio_error(f)) { + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + /* Mod's Grave WOW files always have a 0 restart byte; 6692WOW implements + * 669 repeating by inserting a pattern jump and ignores this byte. + */ + if (mh.restart != 0) + maybe_wow = 0; + + for (i = 0; i < ARRAY_SIZE(mod_magic); i++) { + if (!(strncmp (magic, mod_magic[i].magic, 4))) { + mod->chn = mod_magic[i].ch; + tracker_id = mod_magic[i].id; + detected = mod_magic[i].flag; + break; + } + } + + /* Enable timing detection for M.K. and M!K! modules. */ + if (tracker_id == TRACKER_PROTRACKER) + needs_timing_detection = 1; + + /* Digital Tracker MODs have an extra four bytes after the magic. + * These are always 00h 40h 00h 00h and can probably be ignored. */ + if (tracker_id == TRACKER_DIGITALTRACKER) { + hio_read32b(f); + } +#endif + + if (mod->chn == 0) { + #ifdef LIBXMP_CORE_PLAYER + if (!memcmp(magic, "M.K.", 4)) { + mod->chn = 4; + } else + #endif + if (!strncmp(magic + 2, "CH", 2) && + isdigit((unsigned char)magic[0]) && isdigit((unsigned char)magic[1])) { + mod->chn = (*magic - '0') * 10 + magic[1] - '0'; + } else if (!strncmp(magic + 1, "CHN", 3) && isdigit((unsigned char)*magic)) { + mod->chn = *magic - '0'; + } else { + return -1; + } + #ifndef LIBXMP_CORE_PLAYER + tracker_id = mod->chn & 1 ? TRACKER_TAKETRACKER : TRACKER_FASTTRACKER2; + detected = 1; + #endif + } + + strncpy(mod->name, (char *) mh.name, 20); + + mod->len = mh.len; + /* mod->rst = mh.restart; */ + + if (mod->rst >= mod->len) + mod->rst = 0; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + /* This fixes dragnet.mod (garbage in the order list) */ + if (mod->xxo[i] > 0x7f) + break; + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + mod->pat++; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + +#ifndef LIBXMP_CORE_PLAYER + if (mh.ins[i].size >= 0x8000) { + tracker_id = TRACKER_OPENMPT; + needs_timing_detection = 0; + detected = 1; + } +#endif + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = 2 * mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + if (xxs->lpe > xxs->len) { + xxs->lpe = xxs->len; + } + xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ? + XMP_SAMPLE_LOOP : 0; + sub->fin = (int8)(mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + if (xxs->len > 0) { + xxi->nsm = 1; + } + } + +#ifndef LIBXMP_CORE_PLAYER + /* Experimental tracker-detection routine */ + + if (detected) + goto skip_test; + + /* Test for Flextrax modules + * + * FlexTrax is a soundtracker for Atari Falcon030 compatible computers. + * FlexTrax supports the standard MOD file format (up to eight channels) + * for compatibility reasons but also features a new enhanced module + * format FLX. The FLX format is an extended version of the standard + * MOD file format with support for real-time sound effects like reverb + * and delay. + */ + + if (0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size < m->size) { + char idbuffer[4]; + int pos = hio_tell(f); + int num_read; + if (pos < 0) { + return -1; + } + hio_seek(f, start + 0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size, SEEK_SET); + num_read = hio_read(idbuffer, 1, 4, f); + hio_seek(f, start + pos, SEEK_SET); + + if (num_read == 4 && !memcmp(idbuffer, "FLEX", 4)) { + tracker_id = TRACKER_FLEXTRAX; + needs_timing_detection = 0; + goto skip_test; + } + } + + /* Test for Mod's Grave WOW modules + * + * Stefan Danes said: + * This weird format is identical to '8CHN' but still uses the 'M.K.' ID. + * You can only test for WOW by calculating the size of the module for 8 + * channels and comparing this to the actual module length. If it's equal, + * the module is an 8 channel WOW. + * + * Addendum: very rarely, WOWs will have an odd length due to an extra byte, + * so round the filesize down in this check. False positive WOWs can be ruled + * out by checking the restart byte and sample volume (see above). + * + * Worst case if there are still issues with this, OpenMPT validates later + * patterns in potential WOW files (where sample data would be located in a + * regular M.K. MOD) to rule out false positives. + */ + + if (!strncmp(magic, "M.K.", 4) && maybe_wow && + (0x43c + mod->pat * 32 * 0x40 + smp_size) == (m->size & ~1)) { + mod->chn = 8; + tracker_id = TRACKER_MODSGRAVE; + needs_timing_detection = 0; + } else { + /* Test for Protracker song files */ + ptsong = !strncmp((char *)magic, "M.K.", 4) && + (0x43c + mod->pat * 0x400 == m->size); + if (ptsong) { + tracker_id = TRACKER_PROTRACKER; + goto skip_test; + } else { + /* something else */ + tracker_id = get_tracker_id(m, &mh, tracker_id); + } + } + +skip_test: +#endif + + if (mod->chn >= XMP_MAX_CHANNELS) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + for (i = 0; i < mod->ins; i++) { + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + (mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ? + 'L' : ' ', mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4, + ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 && + mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' '); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + if ((patbuf = (uint8 *) malloc(64 * 4 * mod->chn)) == NULL) { + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + memset(pat_high_fxx, 0, sizeof(pat_high_fxx)); +#endif + + for (i = 0; i < mod->pat; i++) { + uint8 *mod_event; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + free(patbuf); + return -1; + } + + if (hio_read(patbuf, 64 * 4 * mod->chn, 1, f) < 1) { + free(patbuf); + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + mod_event = patbuf; + for (j = 0; j < 64; j++) { + int speed_row = 0; + int bpm_row = 0; + for (k = 0; k < mod->chn; k++) { + int period; + + period = ((int)(LSN(mod_event[0])) << 8) | mod_event[1]; + if (period != 0 && (period < 108 || period > 907)) { + out_of_range = 1; + } + + /* Filter noisetracker events */ + if (tracker_id == TRACKER_PROBABLY_NOISETRACKER) { + unsigned char fxt = LSN(mod_event[2]); + unsigned char fxp = LSN(mod_event[3]); + + if ((fxt > 0x06 && fxt < 0x0a) || (fxt == 0x0e && fxp > 1)) { + tracker_id = TRACKER_UNKNOWN; + } + } + /* Needs CIA/VBlank detection? */ + if (LSN(mod_event[2]) == 0x0f) { + if (mod_event[3] >= 0x20) { + pat_high_fxx[i] = mod_event[3]; + m->compare_vblank = 1; + high_fxx = 1; + bpm_row = 1; + } else { + speed_row = 1; + } + } + mod_event += 4; + } + if (bpm_row && speed_row) { + samerow_fxx = 1; + } + } + + if (out_of_range) { + if (tracker_id == TRACKER_UNKNOWN && mh.restart == 0x7f) { + tracker_id = TRACKER_SCREAMTRACKER3; + } + + /* Check out-of-range notes in Amiga trackers */ + if (tracker_id == TRACKER_PROTRACKER || + tracker_id == TRACKER_NOISETRACKER || + tracker_id == TRACKER_PROBABLY_NOISETRACKER || + tracker_id == TRACKER_SOUNDTRACKER) { /* note > B-3 */ + + tracker_id = TRACKER_UNKNOWN; + } + } +#endif + + mod_event = patbuf; + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); +#ifdef LIBXMP_CORE_PLAYER + libxmp_decode_protracker_event(event, mod_event); +#else + switch (tracker_id) { + case TRACKER_PROBABLY_NOISETRACKER: + case TRACKER_NOISETRACKER: + libxmp_decode_noisetracker_event(event, mod_event); + break; + default: + libxmp_decode_protracker_event(event, mod_event); + } +#endif + mod_event += 4; + } + } + } + free(patbuf); + +#ifndef LIBXMP_CORE_PLAYER + /* VBlank detection routine. + * Despite VBlank being dependent on the tracker used, VBlank detection + * is complex and uses heuristics mostly independent from tracker ID. + * See also: the scan.c comparison code enabled by m->compare_vblank + */ + if (!needs_timing_detection) { + /* Noisetracker and some other trackers do not support CIA timing. The + * only known MOD in the wild that relies on this is muppenkorva.mod + * by Glue Master (loaded by the His Master's Noise loader). + */ + if (tracker_is_vblank(tracker_id)) { + m->quirk |= QUIRK_NOBPM; + } + m->compare_vblank = 0; + + } else if (samerow_fxx) { + /* If low Fxx and high Fxx are on the same row, there's a high chance + * this is from a CIA-based tracker. There are some exceptions. + */ + if (tracker_id == TRACKER_NOISETRACKER || + tracker_id == TRACKER_PROBABLY_NOISETRACKER || + tracker_id == TRACKER_SOUNDTRACKER) { + + tracker_id = TRACKER_UNKNOWN; + } + m->compare_vblank = 0; + + } else if (high_fxx && mod->len >= 8) { + /* Test for high Fxx at the end only--this is typically VBlank, + * and is used to add silence to the end of modules. + * + * Exception: if the final high Fxx is F7D, this module is either CIA + * or is VBlank that was modified to play as CIA, so do nothing. + * + * TODO: MPT resets modules on the end loop, so some of the very long + * silent sections in modules affected by this probably expect CIA. It + * should eventually be possible to detect those. + */ + const int threshold = mod->len - 2; + + for (i = 0; i < threshold; i++) { + if (pat_high_fxx[mod->xxo[i]]) + break; + } + if (i == threshold) { + for (i = mod->len - 1; i >= threshold; i--) { + uint8 fxx = pat_high_fxx[mod->xxo[i]]; + if (fxx == 0x00) + continue; + if (fxx == 0x7d) + break; + + m->compare_vblank = 0; + m->quirk |= QUIRK_NOBPM; + break; + } + } + } + + switch (tracker_id) { + case TRACKER_PROTRACKER: + tracker = "Protracker"; + ptkloop = 1; + break; + case TRACKER_PROBABLY_NOISETRACKER: + case TRACKER_NOISETRACKER: + tracker = "Noisetracker"; + break; + case TRACKER_SOUNDTRACKER: + tracker = "Soundtracker"; + break; + case TRACKER_FASTTRACKER: + case TRACKER_FASTTRACKER2: + tracker = "Fast Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_TAKETRACKER: + tracker = "Take Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_OCTALYSER: + tracker = "Octalyser"; + break; + case TRACKER_DIGITALTRACKER: + tracker = "Digital Tracker"; + break; + case TRACKER_FLEXTRAX: + tracker = "Flextrax"; + break; + case TRACKER_MODSGRAVE: + tracker = "Mod's Grave"; + break; + case TRACKER_SCREAMTRACKER3: + tracker = "Scream Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_CONVERTEDST: + case TRACKER_CONVERTED: + tracker = "Converted"; + break; + case TRACKER_CLONE: + tracker = "Protracker clone"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_OPENMPT: + tracker = "OpenMPT"; + ptkloop = 1; + break; + default: + case TRACKER_UNKNOWN_CONV: + case TRACKER_UNKNOWN: + tracker = "Unknown tracker"; + m->period_type = PERIOD_AMIGA; + break; + } + + if (out_of_range) { + m->period_type = PERIOD_AMIGA; + } + + if (tracker_id == TRACKER_MODSGRAVE) { + snprintf(mod->type, XMP_NAME_SIZE, "%s", tracker); + } else { + snprintf(mod->type, XMP_NAME_SIZE, "%s %s", tracker, magic); + } +#else + libxmp_set_type(m, (mod->chn == 4) ? "Protracker" : "Fasttracker"); +#endif + + MODULE_INFO(); + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + int flags; + + if (!mod->xxs[i].len) + continue; + + flags = (ptkloop && mod->xxs[i].lps == 0) ? SAMPLE_FLAG_FULLREP : 0; + + #ifdef LIBXMP_CORE_PLAYER + if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) + return -1; + #else + if (ptsong) { + HIO_HANDLE *s; + char sn[XMP_MAXPATH]; + char tmpname[32]; + const char *instname = mod->xxi[i].name; + + if (!instname[0] || !m->dirname) + continue; + + if (libxmp_copy_name_for_fopen(tmpname, instname, 32)) + continue; + + snprintf(sn, XMP_MAXPATH, "%s%s", m->dirname, tmpname); + + if ((s = hio_open(sn, "rb")) != NULL) { + if (libxmp_load_sample(m, s, flags, &mod->xxs[i], NULL) < 0) { + hio_close(s); + return -1; + } + hio_close(s); + } + } else { + uint8 buf[5]; + long pos; + int num; + + if ((pos = hio_tell(f)) < 0) { + return -1; + } + num = hio_read(buf, 1, 5, f); + + if (num == 5 && !memcmp(buf, "ADPCM", 5)) { + flags |= SAMPLE_FLAG_ADPCM; + } else { + hio_seek(f, pos, SEEK_SET); + } + + if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) + return -1; + } + #endif + } + + #ifdef LIBXMP_CORE_PLAYER + if (mod->chn > 4) { + m->quirk &= ~QUIRK_PROTRACK; + m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; + m->read_event_type = READ_EVENT_FT2; + m->period_type = PERIOD_AMIGA; + } + #else + if (tracker_id == TRACKER_PROTRACKER || tracker_id == TRACKER_OPENMPT) { + m->quirk |= QUIRK_PROTRACK; + } else if (tracker_id == TRACKER_SCREAMTRACKER3) { + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + } else if (tracker_id == TRACKER_FASTTRACKER || tracker_id == TRACKER_FASTTRACKER2 || tracker_id == TRACKER_TAKETRACKER || tracker_id == TRACKER_MODSGRAVE || mod->chn > 4) { + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; + m->read_event_type = READ_EVENT_FT2; + m->period_type = PERIOD_AMIGA; + } + #endif + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mtm_load.c b/thirdparty/libxmp/src/loaders/mtm_load.c new file mode 100644 index 0000000..60ee8a6 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mtm_load.c @@ -0,0 +1,344 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +struct mtm_file_header { + uint8 magic[3]; /* "MTM" */ + uint8 version; /* MSN=major, LSN=minor */ + uint8 name[20]; /* ASCIIZ Module name */ + uint16 tracks; /* Number of tracks saved */ + uint8 patterns; /* Number of patterns saved */ + uint8 modlen; /* Module length */ + uint16 extralen; /* Length of the comment field */ + uint8 samples; /* Number of samples */ + uint8 attr; /* Always zero */ + uint8 rows; /* Number rows per track */ + uint8 channels; /* Number of tracks per pattern */ + uint8 pan[32]; /* Pan positions for each channel */ +}; + +struct mtm_instrument_header { + uint8 name[22]; /* Instrument name */ + uint32 length; /* Instrument length in bytes */ + uint32 loop_start; /* Sample loop start */ + uint32 loopend; /* Sample loop end */ + uint8 finetune; /* Finetune */ + uint8 volume; /* Playback volume */ + uint8 attr; /* &0x01: 16bit sample */ +}; + +static int mtm_test(HIO_HANDLE *, char *, const int); +static int mtm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mtm = { + "Multitracker", + mtm_test, + mtm_load +}; + +static int mtm_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 buf[4]; + + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (memcmp(buf, "MTM", 3)) + return -1; + if (buf[3] != 0x10) + return -1; + + libxmp_read_title(f, t, 20); + + return 0; +} + +static int mtm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct mtm_file_header mfh; + struct mtm_instrument_header mih; + uint8 mt[192]; + int fxx[2]; + + LOAD_INIT(); + + hio_read(mfh.magic, 3, 1, f); /* "MTM" */ + mfh.version = hio_read8(f); /* MSN=major, LSN=minor */ + hio_read(mfh.name, 20, 1, f); /* ASCIIZ Module name */ + mfh.tracks = hio_read16l(f); /* Number of tracks saved */ + mfh.patterns = hio_read8(f); /* Number of patterns saved */ + mfh.modlen = hio_read8(f); /* Module length */ + mfh.extralen = hio_read16l(f); /* Length of the comment field */ + + mfh.samples = hio_read8(f); /* Number of samples */ + if (mfh.samples > 63) { + return -1; + } + + mfh.attr = hio_read8(f); /* Always zero */ + + mfh.rows = hio_read8(f); /* Number rows per track */ + if (mfh.rows != 64) + return -1; + + mfh.channels = hio_read8(f); /* Number of tracks per pattern */ + if (mfh.channels > MIN(32, XMP_MAX_CHANNELS)) { + return -1; + } + + hio_read(mfh.pan, 32, 1, f); /* Pan positions for each channel */ + + if (hio_error(f)) { + return -1; + } + +#if 0 + if (strncmp((char *)mfh.magic, "MTM", 3)) + return -1; +#endif + + mod->trk = mfh.tracks + 1; + mod->pat = mfh.patterns + 1; + mod->len = mfh.modlen + 1; + mod->ins = mfh.samples; + mod->smp = mod->ins; + mod->chn = mfh.channels; + mod->spd = 6; + mod->bpm = 125; + + strncpy(mod->name, (char *)mfh.name, 20); + libxmp_set_type(m, "MultiTracker %d.%02d MTM", MSN(mfh.version), + LSN(mfh.version)); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments */ + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read(mih.name, 22, 1, f); /* Instrument name */ + mih.length = hio_read32l(f); /* Instrument length in bytes */ + + if (mih.length > MAX_SAMPLE_SIZE) + return -1; + + mih.loop_start = hio_read32l(f); /* Sample loop start */ + mih.loopend = hio_read32l(f); /* Sample loop end */ + mih.finetune = hio_read8(f); /* Finetune */ + mih.volume = hio_read8(f); /* Playback volume */ + mih.attr = hio_read8(f); /* &0x01: 16bit sample */ + + xxs->len = mih.length; + xxs->lps = mih.loop_start; + xxs->lpe = mih.loopend; + xxs->flg = (xxs->lpe > 2) ? XMP_SAMPLE_LOOP : 0; /* 1 == Forward loop */ + if (mfh.attr & 1) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + sub->vol = mih.volume; + sub->fin = mih.finetune; + sub->pan = 0x80; + sub->sid = i; + + libxmp_instrument_name(mod, i, mih.name, 22); + + if (xxs->len > 0) + mod->xxi[i].nsm = 1; + + D_(D_INFO "[%2X] %-22.22s %04x%c%04x %04x %c V%02x F%+03d\n", i, + xxi->name, xxs->len, xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin - 0x80); + } + + hio_read(mod->xxo, 1, 128, f); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk - 1); + + fxx[0] = fxx[1] = 0; + for (i = 0; i < mod->trk; i++) { + + if (libxmp_alloc_track(mod, i, mfh.rows) < 0) + return -1; + + if (i == 0) + continue; + + if (hio_read(mt, 3, 64, f) != 64) + return -1; + + for (j = 0; j < 64; j++) { + struct xmp_event *e = &mod->xxt[i]->event[j]; + uint8 *d = mt + j * 3; + + e->note = d[0] >> 2; + if (e->note) { + e->note += 37; + } + e->ins = ((d[0] & 0x3) << 4) + MSN(d[1]); + e->fxt = LSN(d[1]); + e->fxp = d[2]; + if (e->fxt > FX_SPEED) { + e->fxt = e->fxp = 0; + } + + /* See tempo mode detection below. */ + if (e->fxt == FX_SPEED) { + fxx[e->fxp >= 0x20] = 1; + } + + /* Set pan effect translation */ + if (e->fxt == FX_EXTENDED && MSN(e->fxp) == 0x8) { + e->fxt = FX_SETPAN; + e->fxp <<= 4; + } + } + } + + /* Read patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat - 1); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + mod->xxp[i]->rows = 64; + for (j = 0; j < 32; j++) { + int track = hio_read16l(f); + + if (track >= mod->trk) { + track = 0; + } + + if (j < mod->chn) { + mod->xxp[i]->index[j] = track; + } + } + } + + /* Tempo mode detection. + * + * The MTM tempo effect has an unusual property: when speed is set, the + * tempo resets to 125, and when tempo is set, the speed resets to 6. + * Modules that use both speed and tempo effects need to emulate this. + * See: Absolve the Ambience by Sybaris, Soma by Ranger Rick. + * + * Dual Module Player and other DOS players did not know about this and + * did not implement support for it, and instead used Protracker Fxx. + * Many MTM authors created modules that rely on this which are various + * degrees of broken in the tracker they were made with! Several MTMs + * by Phoenix and Silent Mode expect this. The majority of them can be + * detected by checking for high Fxx and low Fxx on the same row. + */ + if (fxx[0] && fxx[1]) { + /* Both used, check patterns. */ + D_(D_INFO "checking patterns for MT or DMP Fxx effect usage"); + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mfh.rows; j++) { + fxx[0] = fxx[1] = 0; + for (k = 0; k < mod->chn; k++) { + struct xmp_event *e = &EVENT(i, k, j); + if (e->fxt == FX_SPEED) { + fxx[e->fxp >= 0x20] = 1; + } + } + if (fxx[0] && fxx[1]) { + /* Same row, no change required */ + D_(D_INFO "probably DMP (%d:%d)", i, j); + goto probably_dmp; + } + } + } + D_(D_INFO "probably MT; injecting speed/BPM reset effects"); + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mfh.rows; j++) { + for (k = 0; k < mod->chn; k++) { + struct xmp_event *e = &EVENT(i, k, j); + if (e->fxt == FX_SPEED) { + e->f2t = FX_SPEED; + e->f2p = (e->fxp < 0x20) ? 125 : 6; + } + } + } + } + } + probably_dmp: + + /* Comments */ + if (mfh.extralen) { + m->comment = (char *)malloc(mfh.extralen + 1); + if (m->comment) { + /* Comments are stored in 40 byte ASCIIZ lines. */ + int len = hio_read(m->comment, 1, mfh.extralen, f); + int line, last_line = 0; + + for (i = 0; i + 40 <= len; i += 40) { + if (m->comment[i] != '\0') + last_line = i + 40; + } + for (j = 0, line = 0; line < last_line; line += 40) { + char *pos = m->comment + line; + for (i = 0; i < 39; i++) { + if (pos[i] == '\0') + break; + m->comment[j++] = pos[i]; + } + m->comment[j++] = '\n'; + } + m->comment[j] = '\0'; + } else { + hio_seek(f, mfh.extralen, SEEK_CUR); + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = mfh.pan[i] << 4; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/muse_load.c b/thirdparty/libxmp/src/loaders/muse_load.c new file mode 100644 index 0000000..d011130 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/muse_load.c @@ -0,0 +1,110 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../miniz.h" + +static int muse_test(HIO_HANDLE *, char *, const int); +static int muse_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_muse = { + "MUSE container", + muse_test, + muse_load +}; + +static int muse_test(HIO_HANDLE * f, char *t, const int start) +{ + uint8 in[8]; + uint32 r; + + if (hio_read(in, 1, 8, f) != 8) { + return -1; + } + if (memcmp(in, "MUSE", 4) != 0) { + return -1; + } + r = readmem32b(in + 4); + if (r != 0xdeadbeaf && r != 0xdeadbabe) { + return -1; + } + if (t) { + *t = '\0'; /* FIXME */ + } + return 0; +} + +static int muse_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + void *in, *out; + long inlen; + size_t outlen; + int err; + + inlen = hio_size(f); + if (inlen < 24 || inlen >= LIBXMP_DEPACK_LIMIT) { + D_(D_CRIT "bad file size"); + return -1; + } + if (hio_seek(f, 24, SEEK_SET) < 0) { + D_(D_CRIT "hio_seek() failed"); + return -1; + } + + inlen -= 24; + in = (uint8 *)malloc(inlen); + if (!in) { + D_(D_CRIT "Out of memory"); + return -1; + } + if (hio_read(in, 1, inlen, f) != inlen) { + D_(D_CRIT "Failed reading input file"); + free(in); + return -1; + } + + out = tinfl_decompress_mem_to_heap(in, inlen, &outlen, TINFL_FLAG_PARSE_ZLIB_HEADER); + if (!out) { + free(in); + D_(D_CRIT "tinfl_decompress_mem_to_heap() failed"); + return -1; + } + free(in); + + if (hio_reopen_mem(out, outlen, 1, f) < 0) { + free(out); + return -1; + } + err = libxmp_loader_gal5.test(f, NULL, 0); + hio_seek(f, 0, SEEK_SET); + if (err == 0) { + err = libxmp_loader_gal5.loader(m, f, 0); + } else { + err = libxmp_loader_gal4.test(f, NULL, 0); + hio_seek(f, 0, SEEK_SET); + if (err == 0) { + err = libxmp_loader_gal4.loader(m, f, 0); + } + } + + return err; +} diff --git a/thirdparty/libxmp/src/loaders/no_load.c b/thirdparty/libxmp/src/loaders/no_load.c new file mode 100644 index 0000000..7846ba3 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/no_load.c @@ -0,0 +1,257 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +/* Nir Oren's Liquid Tracker old "NO" format. I have only one NO module, + * Moti Radomski's "Time after time" from ftp.modland.com. + */ + + +static int no_test (HIO_HANDLE *, char *, const int); +static int no_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_no = { + "Liquid Tracker NO", + no_test, + no_load +}; + +static int no_test(HIO_HANDLE *f, char *t, const int start) +{ + int nsize, pat, chn; + int i; + + hio_seek(f, start, SEEK_CUR); + + if (hio_read32b(f) != 0x4e4f0000) /* NO 0x00 0x00 */ + return -1; + + nsize = hio_read8(f); + if (nsize != 20) + return -1; + + /* test title */ + for (i = 0; i < nsize; i++) { + if (hio_read8(f) == 0) + return -1; + } + + hio_seek(f, 9, SEEK_CUR); + + /* test number of patterns */ + pat = hio_read8(f); + if (pat == 0) + return -1; + + hio_read8(f); + + /* test number of channels */ + chn = hio_read8(f); + if (chn <= 0 || chn > 16) + return -1; + + hio_seek(f, start + 5, SEEK_SET); + + libxmp_read_title(f, t, nsize); + + return 0; +} + + +static const uint8 fx[15] = { + FX_ARPEGGIO, + 0, + FX_BREAK, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + + +static int no_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j, k; + int nsize; + + LOAD_INIT(); + + hio_read32b(f); /* NO 0x00 0x00 */ + + libxmp_set_type(m, "Liquid Tracker"); + + nsize = hio_read8(f); + for (i = 0; i < nsize; i++) { + uint8 x = hio_read8(f); + if (i < XMP_NAME_SIZE) + mod->name[i] = x; + } + + hio_read16l(f); + hio_read16l(f); + hio_read16l(f); + hio_read16l(f); + hio_read8(f); + mod->pat = hio_read8(f); + hio_read8(f); + mod->chn = hio_read8(f); + mod->trk = mod->pat * mod->chn; + hio_read8(f); + hio_read16l(f); + hio_read16l(f); + hio_read8(f); + mod->ins = mod->smp = 63; + + for (i = 0; i < 256; i++) { + uint8 x = hio_read8(f); + if (x == 0xff) + break; + mod->xxo[i] = x; + } + hio_seek(f, 255 - i, SEEK_CUR); + mod->len = i; + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read instrument names */ + for (i = 0; i < mod->ins; i++) { + int hasname, c2spd; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + nsize = hio_read8(f); + if (hio_error(f)) { + return -1; + } + + hasname = 0; + for (j = 0; j < nsize; j++) { + uint8 x = hio_read8(f); + if (x != 0x20) + hasname = 1; + if (j < 32) + mod->xxi[i].name[j] = x; + } + if (!hasname) + mod->xxi[i].name[0] = 0; + + hio_read32l(f); + hio_read32l(f); + mod->xxi[i].sub[0].vol = hio_read8(f); + c2spd = hio_read16l(f); + mod->xxs[i].len = hio_read16l(f); + mod->xxs[i].lps = hio_read16l(f); + mod->xxs[i].lpe = hio_read16l(f); + hio_read32l(f); + hio_read16l(f); + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + /* + mod->xxs[i].lps = 0; + mod->xxs[i].lpe = 0; + */ + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].fin = 0; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %5d", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, c2spd); + + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d ", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < mod->chn; k++) { + uint32 x, note, ins, vol, fxt, fxp; + + event = &EVENT (i, k, j); + + x = hio_read32l(f); + note = x & 0x0000003f; + ins = (x & 0x00001fc0) >> 6; + vol = (x & 0x000fe000) >> 13; + fxt = (x & 0x00f00000) >> 20; + fxp = (x & 0xff000000) >> 24; + + if (note != 0x3f) + event->note = 36 + note; + if (ins != 0x7f) + event->ins = 1 + ins; + if (vol != 0x7f) + event->vol = vol; + if (fxt != 0x0f) { + event->fxt = fx[fxt]; + event->fxp = fxp; + } + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len == 0) + continue; + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/okt_load.c b/thirdparty/libxmp/src/loaders/okt_load.c new file mode 100644 index 0000000..65a9ab4 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/okt_load.c @@ -0,0 +1,389 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the format description written by Harald Zappe. + * Additional information about Oktalyzer modules from Bernardo + * Innocenti's XModule 3.4 sources. + */ + +#include "loader.h" +#include "iff.h" + +static int okt_test(HIO_HANDLE *, char *, const int); +static int okt_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_okt = { + "Oktalyzer", + okt_test, + okt_load +}; + +static int okt_test(HIO_HANDLE *f, char *t, const int start) +{ + char magic[8]; + + if (hio_read(magic, 1, 8, f) < 8) + return -1; + + if (strncmp(magic, "OKTASONG", 8)) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +#define OKT_MODE8 0x00 /* 7 bit samples */ +#define OKT_MODE4 0x01 /* 8 bit samples */ +#define OKT_MODEB 0x02 /* Both */ + +#define NONE 0xff + +struct local_data { + int mode[36]; + int idx[36]; + int pattern; + int sample; + int samples; + int has_cmod; + int has_samp; + int has_slen; +}; + +static const int fx[32] = { + NONE, + FX_PORTA_UP, /* 1 */ + FX_PORTA_DN, /* 2 */ + NONE, + NONE, + NONE, + NONE, + NONE, + NONE, + NONE, + FX_OKT_ARP3, /* 10 */ + FX_OKT_ARP4, /* 11 */ + FX_OKT_ARP5, /* 12 */ + FX_NSLIDE2_DN, /* 13 */ + NONE, + NONE, /* 15 - filter */ + NONE, + FX_NSLIDE2_UP, /* 17 */ + NONE, + NONE, + NONE, + FX_NSLIDE_DN, /* 21 */ + NONE, + NONE, + NONE, + FX_JUMP, /* 25 */ + NONE, + NONE, /* 27 - release */ + FX_SPEED, /* 28 */ + NONE, + FX_NSLIDE_UP, /* 30 */ + FX_VOLSET /* 31 */ +}; + +static int get_cmod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->has_cmod || size < 8) { + return -1; + } + data->has_cmod = 1; + + mod->chn = 0; + for (i = 0; i < 4; i++) { + int pan = (((i + 1) / 2) % 2) * 0xff; + int p = 0x80 + (pan - 0x80) * m->defpan / 100; + + if (hio_read16b(f) == 0) { + mod->xxc[mod->chn++].pan = p; + } else { + mod->xxc[mod->chn].flg |= XMP_CHANNEL_SPLIT | (i << 4); + mod->xxc[mod->chn++].pan = p; + mod->xxc[mod->chn].flg |= XMP_CHANNEL_SPLIT | (i << 4); + mod->xxc[mod->chn++].pan = p; + } + + } + + return 0; +} + +static int get_samp(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j; + int looplen; + + /* Sanity check */ + if (data->has_samp || size != 36 * 32) { + return -1; + } + data->has_samp = 1; + + /* Should be always 36 */ + mod->ins = size / 32; /* sizeof(struct okt_instrument_header); */ + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (j = i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[j]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read(xxi->name, 1, 20, f); + + /* Sample size is always rounded down */ + xxs->len = hio_read32b(f) & ~1; + xxs->lps = hio_read16b(f) << 1; + looplen = hio_read16b(f) << 1; + xxs->lpe = xxs->lps + looplen; + xxs->flg = looplen > 2 ? XMP_SAMPLE_LOOP : 0; + + sub->vol = hio_read16b(f); + data->mode[i] = hio_read16b(f); + + sub->pan = 0x80; + sub->sid = j; + + data->idx[j] = i; + + if (xxs->len > 0) { + xxi->nsm = 1; + j++; + } + } + data->samples = j; + + return 0; +} + +static int get_spee(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + mod->spd = hio_read16b(f); + mod->bpm = 125; + + return 0; +} + +static int get_slen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + + /* Sanity check */ + if (data->has_slen || !data->has_cmod || size < 2) { + return -1; + } + data->has_slen = 1; + + mod->pat = hio_read16b(f); + mod->trk = mod->pat * mod->chn; + + return 0; +} + +static int get_plen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 256) + return -1; + + D_(D_INFO "Module length: %d", mod->len); + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) + return -1; + + return 0; +} + +static int get_pbod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct xmp_event *e; + uint16 rows; + int j; + + /* Sanity check */ + if (!data->has_slen || !data->has_cmod) { + return -1; + } + + if (data->pattern >= mod->pat) + return 0; + + if (!data->pattern) { + if (libxmp_init_pattern(mod) < 0) + return -1; + D_(D_INFO "Stored patterns: %d", mod->pat); + } + + rows = hio_read16b(f); + + if (libxmp_alloc_pattern_tracks(mod, data->pattern, rows) < 0) + return -1; + + for (j = 0; j < rows * mod->chn; j++) { + uint8 note, ins, fxt; + + e = &EVENT(data->pattern, j % mod->chn, j / mod->chn); + memset(e, 0, sizeof(struct xmp_event)); + + note = hio_read8(f); + ins = hio_read8(f); + + if (note) { + e->note = 48 + note; + e->ins = 1 + ins; + } + + fxt = hio_read8(f); + if (fxt >= ARRAY_SIZE(fx)) { + return -1; + } + e->fxt = fx[fxt]; + e->fxp = hio_read8(f); + + if ((e->fxt == FX_VOLSET) && (e->fxp > 0x40)) { + if (e->fxp <= 0x50) { + e->fxt = FX_VOLSLIDE; + e->fxp -= 0x40; + } else if (e->fxp <= 0x60) { + e->fxt = FX_VOLSLIDE; + e->fxp = (e->fxp - 0x50) << 4; + } else if (e->fxp <= 0x70) { + e->fxt = FX_F_VSLIDE_DN; + e->fxp = e->fxp - 0x60; + } else if (e->fxp <= 0x80) { + e->fxt = FX_F_VSLIDE_UP; + e->fxp = e->fxp - 0x70; + } + } + if (e->fxt == FX_ARPEGGIO) /* Arpeggio fixup */ + e->fxp = (((24 - MSN(e->fxp)) % 12) << 4) | LSN(e->fxp); + if (e->fxt == NONE) + e->fxt = e->fxp = 0; + } + data->pattern++; + + return 0; +} + +static int get_sbod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int flags = 0; + int i, sid; + + if (data->sample >= data->samples) + return 0; + + D_(D_INFO "Stored samples: %d", mod->smp); + + i = data->idx[data->sample]; + if (data->mode[i] == OKT_MODE8 || data->mode[i] == OKT_MODEB) + flags = SAMPLE_FLAG_7BIT; + + sid = mod->xxi[i].sub[0].sid; + if (libxmp_load_sample(m, f, flags, &mod->xxs[sid], NULL) < 0) + return -1; + + data->sample++; + + return 0; +} + +static int okt_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + iff_handle handle; + struct local_data data; + int ret; + + LOAD_INIT(); + + hio_seek(f, 8, SEEK_CUR); /* OKTASONG */ + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + memset(&data, 0, sizeof(struct local_data)); + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "CMOD", get_cmod); + ret |= libxmp_iff_register(handle, "SAMP", get_samp); + ret |= libxmp_iff_register(handle, "SPEE", get_spee); + ret |= libxmp_iff_register(handle, "SLEN", get_slen); + ret |= libxmp_iff_register(handle, "PLEN", get_plen); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "PBOD", get_pbod); + ret |= libxmp_iff_register(handle, "SBOD", get_sbod); + + if (ret != 0) + return -1; + + libxmp_set_type(m, "Oktalyzer"); + + MODULE_INFO(); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + m->period_type = PERIOD_MODRNG; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/pt3_load.c b/thirdparty/libxmp/src/loaders/pt3_load.c new file mode 100644 index 0000000..8c62560 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/pt3_load.c @@ -0,0 +1,305 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "iff.h" + +#define MAGIC_FORM MAGIC4('F','O','R','M') +#define MAGIC_MODL MAGIC4('M','O','D','L') +#define MAGIC_VERS MAGIC4('V','E','R','S') +#define MAGIC_INFO MAGIC4('I','N','F','O') + +static int pt3_test(HIO_HANDLE *, char *, const int); +static int pt3_load(struct module_data *, HIO_HANDLE *, const int); +static int ptdt_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_pt3 = { + "Protracker 3", + pt3_test, + pt3_load +}; + +static int pt3_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_FORM) + return -1; + + hio_read32b(f); /* skip size */ + + if (hio_read32b(f) != MAGIC_MODL) + return -1; + + if (hio_read32b(f) != MAGIC_VERS) + return -1; + + hio_read32b(f); /* skip size */ + + hio_seek(f, 10, SEEK_CUR); + + if (hio_read32b(f) == MAGIC_INFO) { + hio_read32b(f); /* skip size */ + libxmp_read_title(f, t, 32); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + +#define PT3_FLAG_CIA 0x0001 /* VBlank if not set */ +#define PT3_FLAG_FILTER 0x0002 /* Filter status */ +#define PT3_FLAG_SONG 0x0004 /* Modules have this bit unset */ +#define PT3_FLAG_IRQ 0x0008 /* Soft IRQ */ +#define PT3_FLAG_VARPAT 0x0010 /* Variable pattern length */ +#define PT3_FLAG_8VOICE 0x0020 /* 4 voices if not set */ +#define PT3_FLAG_16BIT 0x0040 /* 8 bit samples if not set */ +#define PT3_FLAG_RAWPAT 0x0080 /* Packed patterns if not set */ + +struct local_data { + int has_ptdt; +}; + +static int get_info(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + /* int flags; */ + /* int day, month, year, hour, min, sec; + int dhour, dmin, dsec; */ + + /* Sanity check */ + if(data->has_ptdt) { + return -1; + } + + hio_read(mod->name, 1, 32, f); + mod->ins = hio_read16b(f); + mod->len = hio_read16b(f); + mod->pat = hio_read16b(f); + mod->gvl = hio_read16b(f); + mod->bpm = hio_read16b(f); /* Not clamped by Protracker 3.6 */ + /*flags =*/ hio_read16b(f); + /*day =*/ hio_read16b(f); + /*month =*/ hio_read16b(f); + /*year =*/ hio_read16b(f); + /*hour =*/ hio_read16b(f); + /*min =*/ hio_read16b(f); + /*sec =*/ hio_read16b(f); + /*dhour =*/ hio_read16b(f); + /*dmin =*/ hio_read16b(f); + /*dsec =*/ hio_read16b(f); + + /* Sanity check */ + if (mod->ins > 255 || mod->len > 256 || mod->pat > 255) { + return -1; + } + + MODULE_INFO(); + + /*D_(D_INFO "Creation date: %02d/%02d/%02d %02d:%02d:%02d", + day, month, year, hour, min, sec); + D_(D_INFO "Playing time: %02d:%02d:%02d", dhour, dmin, dsec);*/ + + return 0; +} + +static int get_cmnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + D_(D_INFO "Comment size: %d", size); + + return 0; +} + +static int get_ptdt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + + /* Sanity check */ + if(data->has_ptdt) { + return -1; + } + data->has_ptdt = 1; + + ptdt_load(m, f, 0); + + return 0; +} + +static int pt3_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + iff_handle handle; + struct local_data data; + char buf[20]; + int ret; + + LOAD_INIT(); + + memset(&data, 0, sizeof(struct local_data)); + + hio_read32b(f); /* FORM */ + hio_read32b(f); /* size */ + hio_read32b(f); /* MODL */ + hio_read32b(f); /* VERS */ + hio_read32b(f); /* VERS size */ + + if (hio_read(buf, 1, 10, f) < 10) + return -1; + libxmp_set_type(m, "%-6.6s IFFMODL", buf + 4); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "INFO", get_info); + ret |= libxmp_iff_register(handle, "CMNT", get_cmnt); + ret |= libxmp_iff_register(handle, "PTDT", get_ptdt); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_FULL_CHUNK_SIZE); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + /* Sanity check */ + if (m->mod.smp <= 0) { + return -1; + } + + return 0; +} + +static int ptdt_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct mod_header mh; + uint8 mod_event[4]; + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + if (hio_read(mh.order, 128, 1, f) < 1) { + D_(D_CRIT "read error at order list"); + return -1; + } + hio_read(mh.magic, 4, 1, f); + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 4; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + mod->trk = mod->chn * mod->pat; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = 2 * mh.ins[i].size; + mod->xxs[i].lps = 2 * mh.ins[i].loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * mh.ins[i].loop_size; + mod->xxs[i].flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + mod->xxi[i].sub[0].fin = (int8)(mh.ins[i].finetune << 4); + mod->xxi[i].sub[0].vol = mh.ins[i].volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mh.ins[i].loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + libxmp_decode_protracker_event(event, mod_event); + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (!mod->xxs[i].len) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/ptm_load.c b/thirdparty/libxmp/src/loaders/ptm_load.c new file mode 100644 index 0000000..16804ae --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ptm_load.c @@ -0,0 +1,372 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define PTM_CH_MASK 0x1f +#define PTM_NI_FOLLOW 0x20 +#define PTM_VOL_FOLLOWS 0x80 +#define PTM_FX_FOLLOWS 0x40 + +struct ptm_file_header { + uint8 name[28]; /* Song name */ + uint8 doseof; /* 0x1a */ + uint8 vermin; /* Minor version */ + uint8 vermaj; /* Major type */ + uint8 rsvd1; /* Reserved */ + uint16 ordnum; /* Number of orders (must be even) */ + uint16 insnum; /* Number of instruments */ + uint16 patnum; /* Number of patterns */ + uint16 chnnum; /* Number of channels */ + uint16 flags; /* Flags (set to 0) */ + uint16 rsvd2; /* Reserved */ + uint32 magic; /* 'PTMF' */ + uint8 rsvd3[16]; /* Reserved */ + uint8 chset[32]; /* Channel settings */ + uint8 order[256]; /* Orders */ + uint16 patseg[128]; +}; + +struct ptm_instrument_header { + uint8 type; /* Sample type */ + uint8 dosname[12]; /* DOS file name */ + uint8 vol; /* Volume */ + uint16 c4spd; /* C4 speed */ + uint16 smpseg; /* Sample segment (not used) */ + uint32 smpofs; /* Sample offset */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint32 gusbeg; /* GUS begin address */ + uint32 guslps; /* GUS loop start address */ + uint32 guslpe; /* GUS loop end address */ + uint8 gusflg; /* GUS loop flags */ + uint8 rsvd1; /* Reserved */ + uint8 name[28]; /* Instrument name */ + uint32 magic; /* 'PTMS' */ +}; + +#define MAGIC_PTMF MAGIC4('P','T','M','F') + +static int ptm_test(HIO_HANDLE *, char *, const int); +static int ptm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ptm = { + "Poly Tracker", + ptm_test, + ptm_load +}; + +static int ptm_test(HIO_HANDLE *f, char *t, const int start) +{ + hio_seek(f, start + 44, SEEK_SET); + if (hio_read32b(f) != MAGIC_PTMF) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 28); + + return 0; +} + +static const int ptm_vol[] = { + 0, 5, 8, 10, 12, 14, 15, 17, 18, 20, 21, 22, 23, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, + 41, 42, 42, 43, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, + 52, 53, 54, 54, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, + 62, 63, 63, 64, 64 +}; + +static int ptm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i, smp_ofs[256]; + struct xmp_event *event; + struct ptm_file_header pfh; + struct ptm_instrument_header pih; + uint8 n, b; + + LOAD_INIT(); + + /* Load and convert header */ + + hio_read(pfh.name, 28, 1, f); /* Song name */ + pfh.doseof = hio_read8(f); /* 0x1a */ + pfh.vermin = hio_read8(f); /* Minor version */ + pfh.vermaj = hio_read8(f); /* Major type */ + pfh.rsvd1 = hio_read8(f); /* Reserved */ + pfh.ordnum = hio_read16l(f); /* Number of orders (must be even) */ + pfh.insnum = hio_read16l(f); /* Number of instruments */ + pfh.patnum = hio_read16l(f); /* Number of patterns */ + pfh.chnnum = hio_read16l(f); /* Number of channels */ + pfh.flags = hio_read16l(f); /* Flags (set to 0) */ + pfh.rsvd2 = hio_read16l(f); /* Reserved */ + pfh.magic = hio_read32b(f); /* 'PTMF' */ + + if (pfh.magic != MAGIC_PTMF) + return -1; + + /* Sanity check */ + if (pfh.ordnum > 256 || pfh.insnum > 255 || pfh.patnum > 128 || + pfh.chnnum > 32) { + return -1; + } + + hio_read(pfh.rsvd3, 16, 1, f); /* Reserved */ + hio_read(pfh.chset, 32, 1, f); /* Channel settings */ + hio_read(pfh.order, 256, 1, f); /* Orders */ + for (i = 0; i < 128; i++) + pfh.patseg[i] = hio_read16l(f); + + if (hio_error(f)) + return -1; + + mod->len = pfh.ordnum; + mod->ins = pfh.insnum; + mod->pat = pfh.patnum; + mod->chn = pfh.chnnum; + mod->trk = mod->pat * mod->chn; + mod->smp = mod->ins; + mod->spd = 6; + mod->bpm = 125; + memcpy(mod->xxo, pfh.order, 256); + + m->c4rate = C4_NTSC_RATE; + + libxmp_copy_adjust(mod->name, pfh.name, 28); + libxmp_set_type(m, "Poly Tracker PTM %d.%02x", pfh.vermaj, pfh.vermin); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + /* Read and convert instruments and samples */ + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + pih.type = hio_read8(f); /* Sample type */ + hio_read(pih.dosname, 12, 1, f); /* DOS file name */ + pih.vol = hio_read8(f); /* Volume */ + pih.c4spd = hio_read16l(f); /* C4 speed */ + pih.smpseg = hio_read16l(f); /* Sample segment (not used) */ + pih.smpofs = hio_read32l(f); /* Sample offset */ + pih.length = hio_read32l(f); /* Length */ + pih.loopbeg = hio_read32l(f); /* Loop begin */ + pih.loopend = hio_read32l(f); /* Loop end */ + pih.gusbeg = hio_read32l(f); /* GUS begin address */ + pih.guslps = hio_read32l(f); /* GUS loop start address */ + pih.guslpe = hio_read32l(f); /* GUS loop end address */ + pih.gusflg = hio_read8(f); /* GUS loop flags */ + pih.rsvd1 = hio_read8(f); /* Reserved */ + hio_read(pih.name, 28, 1, f); /* Instrument name */ + pih.magic = hio_read32b(f); /* 'PTMS' */ + + if (hio_error(f)) { + return -1; + } + + if ((pih.type & 3) != 1) + continue; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) { + return -1; + } + + sub = &xxi->sub[0]; + + smp_ofs[i] = pih.smpofs; + xxs->len = pih.length; + xxs->lps = pih.loopbeg; + xxs->lpe = pih.loopend; + + if (mod->xxs[i].len > 0) { + mod->xxi[i].nsm = 1; + } + + xxs->flg = 0; + if (pih.type & 0x04) { + xxs->flg |= XMP_SAMPLE_LOOP; + } + if (pih.type & 0x08) { + xxs->flg |= XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR; + } + if (pih.type & 0x10) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + sub->vol = pih.vol; + sub->pan = 0x80; + sub->sid = i; + pih.magic = 0; + + libxmp_instrument_name(mod, i, pih.name, 28); + + D_(D_INFO "[%2X] %-28.28s %05x%c%05x %05x %c V%02x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + pih.type & 0x10 ? '+' : ' ', + xxs->lps, xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, pih.c4spd); + + /* Convert C4SPD to relnote/finetune */ + libxmp_c2spd_to_note(pih.c4spd, &sub->xpo, &sub->fin); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + + /* channel control to prevent infinite loop in pattern reading */ + /* addresses fuzz bug reported by Lionel Debroux in 20161223 */ + char chn_ctrl[32]; + + if (!pfh.patseg[i]) + continue; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + hio_seek(f, start + 16L * pfh.patseg[i], SEEK_SET); + r = 0; + + memset(chn_ctrl, 0, sizeof(chn_ctrl)); + + while (r < 64) { + + b = hio_read8(f); + if (!b) { + r++; + memset(chn_ctrl, 0, sizeof(chn_ctrl)); + continue; + } + + c = b & PTM_CH_MASK; + if (chn_ctrl[c]) { + /* uh-oh, something wrong happened */ + return -1; + } + /* mark this channel as read */ + chn_ctrl[c] = 1; + + if (c >= mod->chn) { + continue; + } + + event = &EVENT(i, c, r); + if (b & PTM_NI_FOLLOW) { + n = hio_read8(f); + switch (n) { + case 255: + n = 0; + break; /* Empty note */ + case 254: + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n += 12; + } + event->note = n; + event->ins = hio_read8(f); + } + if (b & PTM_FX_FOLLOWS) { + event->fxt = hio_read8(f); + event->fxp = hio_read8(f); + + if (event->fxt > 0x17) + event->fxt = event->fxp = 0; + + switch (event->fxt) { + case 0x0e: /* Extended effect */ + if (MSN(event->fxp) == 0x8) { /* Pan set */ + event->fxt = FX_SETPAN; + event->fxp = + LSN(event->fxp) << 4; + } + break; + case 0x10: /* Set global volume */ + event->fxt = FX_GLOBALVOL; + break; + case 0x11: /* Multi retrig */ + event->fxt = FX_MULTI_RETRIG; + break; + case 0x12: /* Fine vibrato */ + event->fxt = FX_FINE_VIBRATO; + break; + case 0x13: /* Note slide down */ + event->fxt = FX_NSLIDE_DN; + break; + case 0x14: /* Note slide up */ + event->fxt = FX_NSLIDE_UP; + break; + case 0x15: /* Note slide down + retrig */ + event->fxt = FX_NSLIDE_R_DN; + break; + case 0x16: /* Note slide up + retrig */ + event->fxt = FX_NSLIDE_R_UP; + break; + case 0x17: /* Reverse sample */ + event->fxt = event->fxp = 0; + break; + } + } + if (b & PTM_VOL_FOLLOWS) { + event->vol = hio_read8(f) + 1; + } + } + } + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (mod->xxi[i].nsm == 0) + continue; + + if (mod->xxs[i].len == 0) + continue; + + hio_seek(f, start + smp_ofs[i], SEEK_SET); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_8BDIFF, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->vol_table = ptm_vol; + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = pfh.chset[i] << 4; + + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/pw_load.c b/thirdparty/libxmp/src/loaders/pw_load.c new file mode 100644 index 0000000..38600c9 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/pw_load.c @@ -0,0 +1,202 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +#ifndef LIBXMP_NO_PROWIZARD + +#include "prowizard/prowiz.h" +#include "../tempfile.h" + +extern struct list_head *checked_format; + +static int pw_test(HIO_HANDLE *, char *, const int); +static int pw_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_pw = { + "prowizard", + pw_test, + pw_load +}; + +int pw_test_format(HIO_HANDLE *f, char *t, const int start, + struct xmp_test_info *info) +{ + const struct pw_format *format; + + format = pw_check(f, info); + + return format ? 0 : -1; +} + +static int pw_test(HIO_HANDLE *f, char *t, const int start) +{ + return pw_test_format(f, t, start, NULL); +} + +static int pw_load(struct module_data *m, HIO_HANDLE *h, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + struct mod_header mh; + uint8 mod_event[4]; + HIO_HANDLE *f; + FILE *temp; + const char *name; + char *temp_name; + int i, j; + + /* Prowizard depacking */ + + if ((temp = make_temp_file(&temp_name)) == NULL) { + goto err; + } + + if (pw_wizardry(h, temp, &name) < 0) { + fclose(temp); + goto err2; + } + + /* Module loading */ + + D_(D_INFO "loading generated module"); + if ((f = hio_open_file2(temp)) == NULL) { + goto err2; + } + + if (hio_seek(f, 0, start) < 0) { + goto err3; + } + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + hio_read(mh.magic, 4, 1, f); + + if (memcmp(mh.magic, "M.K.", 4)) { + goto err3; + } + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 4; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + + mod->trk = mod->chn * mod->pat; + + snprintf(mod->name, XMP_NAME_SIZE, "%s", (char *)mh.name); + snprintf(mod->type, XMP_NAME_SIZE, "%s", name); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) { + goto err3; + } + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err3; + + mod->xxs[i].len = 2 * mh.ins[i].size; + mod->xxs[i].lps = 2 * mh.ins[i].loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * mh.ins[i].loop_size; + mod->xxs[i].flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].fin = (int8) (mh.ins[i].finetune << 4); + mod->xxi[i].sub[0].vol = mh.ins[i].volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d", + i, mod->xxi[i].name, mod->xxs[i].len, + mod->xxs[i].lps, mod->xxs[i].lpe, + mh.ins[i].loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) { + goto err3; + } + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err3; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) + goto err3; + libxmp_decode_protracker_event(event, mod_event); + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + for (i = 0; i < mod->smp; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + goto err3; + } + + hio_close(f); + unlink_temp_file(temp_name); + return 0; + + err3: + hio_close(f); + err2: + unlink_temp_file(temp_name); + err: + return -1; +} + +#endif /* LIBXMP_NO_PROWIZARD */ diff --git a/thirdparty/libxmp/src/loaders/rtm_load.c b/thirdparty/libxmp/src/loaders/rtm_load.c new file mode 100644 index 0000000..0a2e3d1 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/rtm_load.c @@ -0,0 +1,521 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +/* Data structures from the specification of the RTM format version 1.10 by + * Arnaud Hasenfratz + */ + +struct ObjectHeader { + char id[4]; /* "RTMM", "RTND", "RTIN" or "RTSM" */ + char rc; /* 0x20 */ + char name[32]; /* object name */ + char eof; /* "\x1A" */ + uint16 version; /* version of the format (actual : 0x110) */ + uint16 headerSize; /* object header size */ +}; + +struct RTMMHeader { /* Real Tracker Music Module */ + char software[20]; /* software used for saving the module */ + char composer[32]; + uint16 flags; /* song flags */ + /* bit 0 : linear table, + bit 1 : track names present */ + uint8 ntrack; /* number of tracks */ + uint8 ninstr; /* number of instruments */ + uint16 nposition; /* number of positions */ + uint16 npattern; /* number of patterns */ + uint8 speed; /* initial speed */ + uint8 tempo; /* initial tempo */ + char panning[32]; /* initial pannings (for S3M compatibility) */ + uint32 extraDataSize; /* length of data after the header */ + +/* version 1.12 */ + char originalName[32]; +}; + +struct RTNDHeader { /* Real Tracker Note Data */ + uint16 flags; /* Always 1 */ + uint8 ntrack; + uint16 nrows; + uint32 datasize; /* Size of packed data */ +}; + +struct EnvelopePoint { + long x; + long y; +}; + +struct Envelope { + uint8 npoint; + struct EnvelopePoint point[12]; + uint8 sustain; + uint8 loopstart; + uint8 loopend; + uint16 flags; /* bit 0 : enable envelope, + bit 1 : sustain, bit 2 : loop */ +}; + +struct RTINHeader { /* Real Tracker Instrument */ + uint8 nsample; + uint16 flags; /* bit 0 : default panning enabled + bit 1 : mute samples */ + uint8 table[120]; /* sample number for each note */ + struct Envelope volumeEnv; + struct Envelope panningEnv; + char vibflg; /* vibrato type */ + char vibsweep; /* vibrato sweep */ + char vibdepth; /* vibrato depth */ + char vibrate; /* vibrato rate */ + uint16 volfade; + +/* version 1.10 */ + uint8 midiPort; + uint8 midiChannel; + uint8 midiProgram; + uint8 midiEnable; + +/* version 1.12 */ + char midiTranspose; + uint8 midiBenderRange; + uint8 midiBaseVolume; + char midiUseVelocity; +}; + +struct RTSMHeader { /* Real Tracker Sample */ + uint16 flags; /* bit 1 : 16 bits, + bit 2 : delta encoded (always) */ + uint8 basevolume; + uint8 defaultvolume; + uint32 length; + uint8 loop; /* =0:no loop, =1:forward loop, + =2:bi-directional loop */ + uint8 reserved[3]; + uint32 loopbegin; + uint32 loopend; + uint32 basefreq; + uint8 basenote; + char panning; /* Panning from -64 to 64 */ +}; + + +static int rtm_test(HIO_HANDLE *, char *, const int); +static int rtm_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_rtm = { + "Real Tracker", + rtm_test, + rtm_load +}; + +static int rtm_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[4]; + + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (memcmp(buf, "RTMM", 4)) + return -1; + + if (hio_read8(f) != 0x20) + return -1; + + libxmp_read_title(f, t, 32); + + return 0; +} + + +#define MAX_SAMP 1024 + +static int read_object_header(HIO_HANDLE *f, struct ObjectHeader *h, const char *id) +{ + hio_read(h->id, 4, 1, f); + D_(D_WARN "object id: %02x %02x %02x %02x", h->id[0], + h->id[1], h->id[2], h->id[3]); + + if (memcmp(id, h->id, 4)) + return -1; + + h->rc = hio_read8(f); + if (h->rc != 0x20) + return -1; + if (hio_read(h->name, 1, 32, f) != 32) + return -1; + h->eof = hio_read8(f); + h->version = hio_read16l(f); + h->headerSize = hio_read16l(f); + D_(D_INFO "object %-4.4s (%d)", h->id, h->headerSize); + + return 0; +} + + +static int rtm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, r; + struct xmp_event *event; + struct ObjectHeader oh; + struct RTMMHeader rh; + struct RTNDHeader rp; + struct RTINHeader ri; + struct RTSMHeader rs; + int offset, smpnum, version; + char tracker_name[21], composer[33]; + + LOAD_INIT(); + + if (read_object_header(f, &oh, "RTMM") < 0) + return -1; + + version = oh.version; + + hio_read(tracker_name, 1, 20, f); + tracker_name[20] = 0; + hio_read(composer, 1, 32, f); + composer[32] = 0; + rh.flags = hio_read16l(f); /* bit 0: linear table, bit 1: track names */ + rh.ntrack = hio_read8(f); + rh.ninstr = hio_read8(f); + rh.nposition = hio_read16l(f); + rh.npattern = hio_read16l(f); + rh.speed = hio_read8(f); + rh.tempo = hio_read8(f); + hio_read(rh.panning, 32, 1, f); + rh.extraDataSize = hio_read32l(f); + + /* Sanity check */ + if (hio_error(f) || rh.nposition > 255 || rh.ntrack > 32 || rh.npattern > 255) { + return -1; + } + + if (version >= 0x0112) + hio_seek(f, 32, SEEK_CUR); /* skip original name */ + + for (i = 0; i < rh.nposition; i++) { + mod->xxo[i] = hio_read16l(f); + if (mod->xxo[i] >= rh.npattern) { + return -1; + } + } + + strncpy(mod->name, oh.name, 32); + mod->name[32] = '\0'; + snprintf(mod->type, XMP_NAME_SIZE, "%s RTM %x.%02x", + tracker_name, version >> 8, version & 0xff); + /* strncpy(m->author, composer, XMP_NAME_SIZE); */ + + mod->len = rh.nposition; + mod->pat = rh.npattern; + mod->chn = rh.ntrack; + mod->trk = mod->chn * mod->pat; + mod->ins = rh.ninstr; + mod->spd = rh.speed; + mod->bpm = rh.tempo; + + m->c4rate = C4_NTSC_RATE; + m->period_type = rh.flags & 0x01 ? PERIOD_LINEAR : PERIOD_AMIGA; + + MODULE_INFO(); + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = rh.panning[i] & 0xff; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + offset = 42 + oh.headerSize + rh.extraDataSize; + + for (i = 0; i < mod->pat; i++) { + uint8 c; + + hio_seek(f, start + offset, SEEK_SET); + + if (read_object_header(f, &oh, "RTND") < 0) { + D_(D_CRIT "Error reading pattern %d", i); + return -1; + } + + rp.flags = hio_read16l(f); + rp.ntrack = hio_read8(f); + rp.nrows = hio_read16l(f); + rp.datasize = hio_read32l(f); + + /* Sanity check */ + if (rp.ntrack > rh.ntrack || rp.nrows > 256) { + return -1; + } + + offset += 42 + oh.headerSize + rp.datasize; + + if (libxmp_alloc_pattern_tracks(mod, i, rp.nrows) < 0) + return -1; + + for (r = 0; r < rp.nrows; r++) { + for (j = 0; /*j < rp.ntrack */; j++) { + + c = hio_read8(f); + if (c == 0) /* next row */ + break; + + /* Sanity check */ + if (j >= rp.ntrack) { + return -1; + } + + event = &EVENT(i, j, r); + + if (c & 0x01) { /* set track */ + j = hio_read8(f); + + /* Sanity check */ + if (j >= rp.ntrack) { + return -1; + } + + event = &EVENT(i, j, r); + } + if (c & 0x02) { /* read note */ + event->note = hio_read8(f) + 1; + if (event->note == 0xff) { + event->note = XMP_KEY_OFF; + } else { + event->note += 12; + } + } + if (c & 0x04) /* read instrument */ + event->ins = hio_read8(f); + if (c & 0x08) /* read effect */ + event->fxt = hio_read8(f); + if (c & 0x10) /* read parameter */ + event->fxp = hio_read8(f); + if (c & 0x20) /* read effect 2 */ + event->f2t = hio_read8(f); + if (c & 0x40) /* read parameter 2 */ + event->f2p = hio_read8(f); + } + } + } + + /* + * load instruments + */ + + D_(D_INFO "Instruments: %d", mod->ins); + + hio_seek(f, start + offset, SEEK_SET); + + /* ESTIMATED value! We don't know the actual value at this point */ + mod->smp = MAX_SAMP; + + if (libxmp_init_instrument(m) < 0) + return -1; + + smpnum = 0; + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + + if (read_object_header(f, &oh, "RTIN") < 0) { + D_(D_CRIT "Error reading instrument %d", i); + return -1; + } + + libxmp_instrument_name(mod, i, (uint8 *)oh.name, 32); + + if (oh.headerSize == 0) { + D_(D_INFO "[%2X] %-26.26s %2d ", i, + xxi->name, xxi->nsm); + ri.nsample = 0; + continue; + } + + ri.nsample = hio_read8(f); + ri.flags = hio_read16l(f); /* bit 0 : default panning enabled */ + if (hio_read(ri.table, 1, 120, f) != 120) + return -1; + + ri.volumeEnv.npoint = hio_read8(f); + + /* Sanity check */ + if (ri.volumeEnv.npoint >= 12) + return -1; + + for (j = 0; j < 12; j++) { + ri.volumeEnv.point[j].x = hio_read32l(f); + ri.volumeEnv.point[j].y = hio_read32l(f); + } + ri.volumeEnv.sustain = hio_read8(f); + ri.volumeEnv.loopstart = hio_read8(f); + ri.volumeEnv.loopend = hio_read8(f); + ri.volumeEnv.flags = hio_read16l(f); /* bit 0:enable 1:sus 2:loop */ + + ri.panningEnv.npoint = hio_read8(f); + + /* Sanity check */ + if (ri.panningEnv.npoint >= 12) + return -1; + + for (j = 0; j < 12; j++) { + ri.panningEnv.point[j].x = hio_read32l(f); + ri.panningEnv.point[j].y = hio_read32l(f); + } + ri.panningEnv.sustain = hio_read8(f); + ri.panningEnv.loopstart = hio_read8(f); + ri.panningEnv.loopend = hio_read8(f); + ri.panningEnv.flags = hio_read16l(f); + + ri.vibflg = hio_read8(f); + ri.vibsweep = hio_read8(f); + ri.vibdepth = hio_read8(f); + ri.vibrate = hio_read8(f); + ri.volfade = hio_read16l(f); + + if (version >= 0x0110) { + ri.midiPort = hio_read8(f); + ri.midiChannel = hio_read8(f); + ri.midiProgram = hio_read8(f); + ri.midiEnable = hio_read8(f); + } + if (version >= 0x0112) { + ri.midiTranspose = hio_read8(f); + ri.midiBenderRange = hio_read8(f); + ri.midiBaseVolume = hio_read8(f); + ri.midiUseVelocity = hio_read8(f); + } + + xxi->nsm = ri.nsample; + + D_(D_INFO "[%2X] %-26.26s %2d", i, xxi->name, xxi->nsm); + + if (xxi->nsm > 16) + xxi->nsm = 16; + + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) + return -1; + + for (j = 0; j < 120; j++) + xxi->map[j].ins = ri.table[j]; + + /* Envelope */ + xxi->rls = ri.volfade; + xxi->aei.npt = ri.volumeEnv.npoint; + xxi->aei.sus = ri.volumeEnv.sustain; + xxi->aei.lps = ri.volumeEnv.loopstart; + xxi->aei.lpe = ri.volumeEnv.loopend; + xxi->aei.flg = ri.volumeEnv.flags; + xxi->pei.npt = ri.panningEnv.npoint; + xxi->pei.sus = ri.panningEnv.sustain; + xxi->pei.lps = ri.panningEnv.loopstart; + xxi->pei.lpe = ri.panningEnv.loopend; + xxi->pei.flg = ri.panningEnv.flags; + + for (j = 0; j < xxi->aei.npt; j++) { + xxi->aei.data[j * 2 + 0] = ri.volumeEnv.point[j].x; + xxi->aei.data[j * 2 + 1] = ri.volumeEnv.point[j].y / 2; + } + for (j = 0; j < xxi->pei.npt; j++) { + xxi->pei.data[j * 2 + 0] = ri.panningEnv.point[j].x; + xxi->pei.data[j * 2 + 1] = 32 + ri.panningEnv.point[j].y / 2; + } + + /* For each sample */ + for (j = 0; j < xxi->nsm; j++, smpnum++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs; + + if (read_object_header(f, &oh, "RTSM") < 0) { + D_(D_CRIT "Error reading sample %d", j); + return -1; + } + + rs.flags = hio_read16l(f); + rs.basevolume = hio_read8(f); + rs.defaultvolume = hio_read8(f); + rs.length = hio_read32l(f); + rs.loop = hio_read32l(f); + rs.loopbegin = hio_read32l(f); + rs.loopend = hio_read32l(f); + rs.basefreq = hio_read32l(f); + rs.basenote = hio_read8(f); + rs.panning = hio_read8(f); + + libxmp_c2spd_to_note(rs.basefreq, &sub->xpo, &sub->fin); + sub->xpo += 48 - rs.basenote; + sub->vol = rs.defaultvolume * rs.basevolume / 0x40; + sub->pan = 0x80 + rs.panning * 2; + sub->vwf = ri.vibflg; + sub->vde = ri.vibdepth << 2; + sub->vra = ri.vibrate; + sub->vsw = ri.vibsweep; + sub->sid = smpnum; + + if (smpnum >= mod->smp) { + if (libxmp_realloc_samples(m, mod->smp * 3 / 2) < 0) + return -1; + } + xxs = &mod->xxs[smpnum]; + + libxmp_copy_adjust(xxs->name, (uint8 *)oh.name, 31); + + xxs->len = rs.length; + xxs->lps = rs.loopbegin; + xxs->lpe = rs.loopend; + + xxs->flg = 0; + if (rs.flags & 0x02) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + xxs->flg |= rs.loop & 0x03 ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= rs.loop == 2 ? XMP_SAMPLE_LOOP_BIDIR : 0; + + D_(D_INFO " [%1x] %05x%c%05x %05x %c " + "V%02x F%+04d P%02x R%+03d", + j, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin, sub->pan, sub->xpo); + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, xxs, NULL) < 0) + return -1; + } + } + + /* Final sample number adjustment */ + if (libxmp_realloc_samples(m, smpnum) < 0) + return -1; + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/s3m.h b/thirdparty/libxmp/src/loaders/s3m.h new file mode 100644 index 0000000..9b88f1b --- /dev/null +++ b/thirdparty/libxmp/src/loaders/s3m.h @@ -0,0 +1,126 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_LOADERS_S3M_H +#define LIBXMP_LOADERS_S3M_H + +/* S3M packed pattern macros */ +#define S3M_EOR 0 /* End of row */ +#define S3M_CH_MASK 0x1f /* Channel */ +#define S3M_NI_FOLLOW 0x20 /* Note and instrument follow */ +#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */ +#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */ + +/* S3M mix volume macros */ +#define S3M_MV_VOLUME 0x7f /* Module mix volume, typically 16 to 127 */ +#define S3M_MV_STEREO 0x80 /* Module is stereo if set, otherwise mono */ + +/* S3M channel info macros */ +#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */ +#define S3M_CH_OFF 0xff +#define S3M_CH_NUMBER 0x1f +#define S3M_CH_RIGHT 0x08 +#define S3M_CH_ADLIB 0x10 + +/* S3M channel pan macros */ +#define S3M_PAN_SET 0x20 +#define S3M_PAN_MASK 0x0f + +/* S3M flags */ +#define S3M_ST2_VIB 0x01 /* Not recognized */ +#define S3M_ST2_TEMPO 0x02 /* Not recognized */ +#define S3M_AMIGA_SLIDE 0x04 /* Not recognized */ +#define S3M_VOL_OPT 0x08 /* Not recognized */ +#define S3M_AMIGA_RANGE 0x10 +#define S3M_SB_FILTER 0x20 /* Not recognized */ +#define S3M_ST300_VOLS 0x40 +#define S3M_CUSTOM_DATA 0x80 /* Not recognized */ + +/* S3M Adlib instrument types */ +#define S3M_INST_SAMPLE 0x01 +#define S3M_INST_AMEL 0x02 +#define S3M_INST_ABD 0x03 +#define S3M_INST_ASNARE 0x04 +#define S3M_INST_ATOM 0x05 +#define S3M_INST_ACYM 0x06 +#define S3M_INST_AHIHAT 0x07 + +struct s3m_file_header { + uint8 name[28]; /* Song name */ + uint8 doseof; /* 0x1a */ + uint8 type; /* File type */ + uint8 rsvd1[2]; /* Reserved */ + uint16 ordnum; /* Number of orders (must be even) */ + uint16 insnum; /* Number of instruments */ + uint16 patnum; /* Number of patterns */ + uint16 flags; /* Flags */ + uint16 version; /* Tracker ID and version */ + uint16 ffi; /* File format information */ + uint32 magic; /* 'SCRM' */ + uint8 gv; /* Global volume */ + uint8 is; /* Initial speed */ + uint8 it; /* Initial tempo */ + uint8 mv; /* Master volume */ + uint8 uc; /* Ultra click removal */ + uint8 dp; /* Default pan positions if 0xfc */ + uint8 rsvd2[8]; /* Reserved */ + uint16 special; /* Ptr to special custom data */ + uint8 chset[32]; /* Channel settings */ +}; + +struct s3m_instrument_header { + uint8 dosname[12]; /* DOS file name */ + uint8 memseg_hi; /* High byte of sample pointer */ + uint16 memseg; /* Pointer to sample data */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint8 vol; /* Volume */ + uint8 rsvd1; /* Reserved */ + uint8 pack; /* Packing type (not used) */ + uint8 flags; /* Loop/stereo/16bit samples flags */ + uint16 c2spd; /* C 4 speed */ + uint16 rsvd2; /* Reserved */ + uint8 rsvd3[4]; /* Reserved */ + uint16 int_gp; /* Internal - GUS pointer */ + uint16 int_512; /* Internal - SB pointer */ + uint32 int_last; /* Internal - SB index */ + uint8 name[28]; /* Instrument name */ + uint32 magic; /* 'SCRS' */ +}; + +#ifndef LIBXMP_CORE_PLAYER +struct s3m_adlib_header { + uint8 dosname[12]; /* DOS file name */ + uint8 rsvd1[3]; /* 0x00 0x00 0x00 */ + uint8 reg[12]; /* Adlib registers */ + uint8 vol; + uint8 dsk; + uint8 rsvd2[2]; + uint16 c2spd; /* C 4 speed */ + uint16 rsvd3; /* Reserved */ + uint8 rsvd4[12]; /* Reserved */ + uint8 name[28]; /* Instrument name */ + uint32 magic; /* 'SCRI' */ +}; +#endif +#endif /* LIBXMP_LOADERS_S3M_H */ diff --git a/thirdparty/libxmp/src/loaders/s3m_load.c b/thirdparty/libxmp/src/loaders/s3m_load.c new file mode 100644 index 0000000..2cd7d5b --- /dev/null +++ b/thirdparty/libxmp/src/loaders/s3m_load.c @@ -0,0 +1,666 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Tue, 30 Jun 1998 20:23:11 +0200 + * Reported by John v/d Kamp : + * I have this song from Purple Motion called wcharts.s3m, the global + * volume was set to 0, creating a devide by 0 error in xmp. There should + * be an extra test if it's 0 or not. + * + * Claudio's fix: global volume ignored + */ + +/* + * Sat, 29 Aug 1998 18:50:43 -0500 (CDT) + * Reported by Joel Jordan : + * S3M files support tempos outside the ranges defined by xmp (that is, + * the MOD/XM tempo ranges). S3M's can have tempos from 0 to 255 and speeds + * from 0 to 255 as well, since these are handled with separate effects + * unlike the MOD format. This becomes an issue in such songs as Skaven's + * "Catch that Goblin", which uses speeds above 0x1f. + * + * Claudio's fix: FX_S3M_SPEED added. S3M supports speeds from 0 to 255 and + * tempos from 32 to 255 (S3M speed == xmp tempo, S3M tempo == xmp BPM). + */ + +/* Wed, 21 Oct 1998 15:03:44 -0500 Geoff Reedy + * It appears that xmp has issues loading/playing a specific instrument + * used in LUCCA.S3M. + * (Fixed by Hipolito in xmp-2.0.0dev34) + */ + +/* + * From http://code.pui.ch/2007/02/18/turn-demoscene-modules-into-mp3s/ + * The only flaw I noticed [in xmp] is a problem with portamento in Purple + * Motion's second reality soundtrack (1:06-1:17) + * + * Claudio's note: that's a dissonant beating between channels 6 and 7 + * starting at pos12, caused by pitchbending effect F25. + */ + +#include "loader.h" +#include "s3m.h" +#include "../period.h" + +#define MAGIC_SCRM MAGIC4('S','C','R','M') +#define MAGIC_SCRI MAGIC4('S','C','R','I') +#define MAGIC_SCRS MAGIC4('S','C','R','S') + +static int s3m_test(HIO_HANDLE *, char *, const int); +static int s3m_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_s3m = { + "Scream Tracker 3", + s3m_test, + s3m_load +}; + +static int s3m_test(HIO_HANDLE *f, char *t, const int start) +{ + hio_seek(f, start + 44, SEEK_SET); + if (hio_read32b(f) != MAGIC_SCRM) + return -1; + + hio_seek(f, start + 29, SEEK_SET); + if (hio_read8(f) != 0x10) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 28); + + return 0; +} + +#define NONE 0xff +#define FX_S3M_EXTENDED 0xfe + +/* Effect conversion table */ +static const uint8 fx[27] = { + NONE, + FX_S3M_SPEED, /* Axx Set speed to xx (the default is 06) */ + FX_JUMP, /* Bxx Jump to order xx (hexadecimal) */ + FX_BREAK, /* Cxx Break pattern to row xx (decimal) */ + FX_VOLSLIDE, /* Dxy Volume slide down by y/up by x */ + FX_PORTA_DN, /* Exx Slide down by xx */ + FX_PORTA_UP, /* Fxx Slide up by xx */ + FX_TONEPORTA, /* Gxx Tone portamento with speed xx */ + FX_VIBRATO, /* Hxy Vibrato with speed x and depth y */ + FX_TREMOR, /* Ixy Tremor with ontime x and offtime y */ + FX_S3M_ARPEGGIO, /* Jxy Arpeggio with halfnote additions */ + FX_VIBRA_VSLIDE, /* Kxy Dual command: H00 and Dxy */ + FX_TONE_VSLIDE, /* Lxy Dual command: G00 and Dxy */ + NONE, + NONE, + FX_OFFSET, /* Oxy Set sample offset */ + NONE, + FX_MULTI_RETRIG, /* Qxy Retrig (+volumeslide) note */ + FX_TREMOLO, /* Rxy Tremolo with speed x and depth y */ + FX_S3M_EXTENDED, /* Sxx (misc effects) */ + FX_S3M_BPM, /* Txx Tempo = xx (hex) */ + FX_FINE_VIBRATO, /* Uxx Fine vibrato */ + FX_GLOBALVOL, /* Vxx Set global volume */ + NONE, + FX_SETPAN, /* Xxx Set pan */ + NONE, + NONE +}; + +/* Effect translation */ +static void xlat_fx(int c, struct xmp_event *e) +{ + uint8 h = MSN(e->fxp), l = LSN(e->fxp); + + if (e->fxt >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %02x", e->fxt); + e->fxt = e->fxp = 0; + return; + } + + switch (e->fxt = fx[e->fxt]) { + case FX_S3M_BPM: + if (e->fxp < 0x20) { + e->fxp = e->fxt = 0; + } + break; + case FX_S3M_EXTENDED: /* Extended effects */ + e->fxt = FX_EXTENDED; + switch (h) { + case 0x1: /* Glissando */ + e->fxp = LSN(e->fxp) | (EX_GLISS << 4); + break; + case 0x2: /* Finetune */ + e->fxp = + ((LSN(e->fxp) - 8) & 0x0f) | (EX_FINETUNE << 4); + break; + case 0x3: /* Vibrato wave */ + e->fxp = LSN(e->fxp) | (EX_VIBRATO_WF << 4); + break; + case 0x4: /* Tremolo wave */ + e->fxp = LSN(e->fxp) | (EX_TREMOLO_WF << 4); + break; + case 0x5: + case 0x6: + case 0x7: + case 0x9: + case 0xa: /* Ignore */ + e->fxt = e->fxp = 0; + break; + case 0x8: /* Set pan */ + e->fxt = FX_SETPAN; + e->fxp = l << 4; + break; + case 0xb: /* Pattern loop */ + e->fxp = LSN(e->fxp) | (EX_PATTERN_LOOP << 4); + break; + case 0xc: + if (!l) + e->fxt = e->fxp = 0; + } + break; + case FX_SETPAN: + /* Saga Musix says: "The X effect in S3M files is not + * exclusive to IT and clones. You will find tons of S3Ms made + * with ST3 itself using this effect (and relying on an + * external player being used). X in S3M also behaves + * differently than in IT, which your code does not seem to + * handle: X00 - X80 is left... right, XA4 is surround (like + * S91 in IT), other values are not supposed to do anything. + */ + if (e->fxp == 0xa4) { + // surround + e->fxt = FX_SURROUND; + e->fxp = 1; + } else { + int pan = ((int)e->fxp) << 1; + if (pan > 0xff) { + pan = 0xff; + } + e->fxp = pan; + } + break; + case NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + +static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i; + struct xmp_event *event = 0, dummy; + struct s3m_file_header sfh; + struct s3m_instrument_header sih; +#ifndef LIBXMP_CORE_PLAYER + struct s3m_adlib_header sah; + char tracker_name[40]; +#endif + int pat_len; + uint8 n, b; + uint16 *pp_ins; /* Parapointers to instruments */ + uint16 *pp_pat; /* Parapointers to patterns */ + int stereo; + int ret; + uint8 buf[96] + + LOAD_INIT(); + + if (hio_read(buf, 1, 96, f) != 96) { + goto err; + } + + memcpy(sfh.name, buf, 28); /* Song name */ + sfh.type = buf[30]; /* File type */ + sfh.ordnum = readmem16l(buf + 32); /* Number of orders (must be even) */ + sfh.insnum = readmem16l(buf + 34); /* Number of instruments */ + sfh.patnum = readmem16l(buf + 36); /* Number of patterns */ + sfh.flags = readmem16l(buf + 38); /* Flags */ + sfh.version = readmem16l(buf + 40); /* Tracker ID and version */ + sfh.ffi = readmem16l(buf + 42); /* File format information */ + + /* Sanity check */ + if (sfh.ffi != 1 && sfh.ffi != 2) { + goto err; + } + if (sfh.ordnum > 255 || sfh.insnum > 255 || sfh.patnum > 255) { + goto err; + } + + sfh.magic = readmem32b(buf + 44); /* 'SCRM' */ + sfh.gv = buf[48]; /* Global volume */ + sfh.is = buf[49]; /* Initial speed */ + sfh.it = buf[50]; /* Initial tempo */ + sfh.mv = buf[51]; /* Master volume */ + sfh.uc = buf[52]; /* Ultra click removal */ + sfh.dp = buf[53]; /* Default pan positions if 0xfc */ + memcpy(sfh.rsvd2, buf + 54, 8); /* Reserved */ + sfh.special = readmem16l(buf + 62); /* Ptr to special custom data */ + memcpy(sfh.chset, buf + 64, 32); /* Channel settings */ + + if (sfh.magic != MAGIC_SCRM) { + goto err; + } + + libxmp_copy_adjust(mod->name, sfh.name, 28); + + pp_ins = (uint16 *) calloc(sfh.insnum, sizeof(uint16)); + if (pp_ins == NULL) { + goto err; + } + + pp_pat = (uint16 *) calloc(sfh.patnum, sizeof(uint16)); + if (pp_pat == NULL) { + goto err2; + } + + if (sfh.flags & S3M_AMIGA_RANGE) { + m->period_type = PERIOD_MODRNG; + } + if (sfh.flags & S3M_ST300_VOLS) { + m->quirk |= QUIRK_VSALL; + } + /* m->volbase = 4096 / sfh.gv; */ + mod->spd = sfh.is; + mod->bpm = sfh.it; + mod->chn = 0; + + /* Mix volume and stereo flag conversion (reported by Saga Musix). + * 1) Old format uses mix volume 0-7, and the stereo flag is 0x10. + * 2) Newer ST3s unconditionally convert MV 0x02 and 0x12 to 0x20. + */ + m->mvolbase = 48; + + if (sfh.ffi == 1) { + m->mvol = ((sfh.mv & 0xf) + 1) * 0x10; + stereo = sfh.mv & 0x10; + CLAMP(m->mvol, 0x10, 0x7f); + + } else if (sfh.mv == 0x02 || sfh.mv == 0x12) { + m->mvol = 0x20; + stereo = sfh.mv & 0x10; + + } else { + m->mvol = sfh.mv & S3M_MV_VOLUME; + stereo = sfh.mv & S3M_MV_STEREO; + + if (m->mvol == 0) { + m->mvol = 48; /* Default is 48 */ + } else if (m->mvol < 16) { + m->mvol = 16; /* Minimum is 16 */ + } + } + + /* "Note that in stereo, the mastermul is internally multiplied by + * 11/8 inside the player since there is generally more room in the + * output stream." Do the inverse to affect fewer modules. */ + if (!stereo) { + m->mvol = m->mvol * 8 / 11; + } + + for (i = 0; i < 32; i++) { + int x; + if (sfh.chset[i] == S3M_CH_OFF) + continue; + + mod->chn = i + 1; + + x = sfh.chset[i] & S3M_CH_NUMBER; + if (stereo && x < S3M_CH_ADLIB) { + mod->xxc[i].pan = x < S3M_CH_RIGHT ? 0x30 : 0xc0; + } else { + mod->xxc[i].pan = 0x80; + } + } + + if (sfh.ordnum <= XMP_MAX_MOD_LENGTH) { + mod->len = sfh.ordnum; + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) { + goto err3; + } + } else { + mod->len = XMP_MAX_MOD_LENGTH; + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) { + goto err3; + } + if (hio_seek(f, sfh.ordnum - XMP_MAX_MOD_LENGTH, SEEK_CUR) < 0) { + goto err3; + } + } + + /* Don't trust sfh.patnum */ + mod->pat = -1; + for (i = 0; i < mod->len; ++i) { + if (mod->xxo[i] < 0xfe && mod->xxo[i] > mod->pat) { + mod->pat = mod->xxo[i]; + } + } + mod->pat++; + if (mod->pat > sfh.patnum) { + mod->pat = sfh.patnum; + } + if (mod->pat == 0) { + goto err3; + } + + mod->trk = mod->pat * mod->chn; + /* Load and convert header */ + mod->ins = sfh.insnum; + mod->smp = mod->ins; + + for (i = 0; i < sfh.insnum; i++) { + pp_ins[i] = hio_read16l(f); + } + + for (i = 0; i < sfh.patnum; i++) { + pp_pat[i] = hio_read16l(f); + } + + /* Default pan positions */ + + for (i = 0, sfh.dp -= 0xfc; !sfh.dp /* && n */ && (i < 32); i++) { + uint8 x = hio_read8(f); + if (x & S3M_PAN_SET) { + mod->xxc[i].pan = (x << 4) & 0xff; + } + } + + m->c4rate = C4_NTSC_RATE; + + if (sfh.version == 0x1300) { + m->quirk |= QUIRK_VSALL; + } + +#ifndef LIBXMP_CORE_PLAYER + switch (sfh.version >> 12) { + case 1: + snprintf(tracker_name, 40, "Scream Tracker %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + m->quirk |= QUIRK_ST3BUGS; + break; + case 2: + snprintf(tracker_name, 40, "Imago Orpheus %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + break; + case 3: + if (sfh.version == 0x3216) { + strcpy(tracker_name, "Impulse Tracker 2.14v3"); + } else if (sfh.version == 0x3217) { + strcpy(tracker_name, "Impulse Tracker 2.14v5"); + } else { + snprintf(tracker_name, 40, "Impulse Tracker %d.%02x", + (sfh.version & 0x0f00) >> 8, + sfh.version & 0xff); + } + break; + case 5: + if (sfh.version == 0x5447) { + strcpy(tracker_name, "Graoumf Tracker"); + } else if (sfh.rsvd2[0] || sfh.rsvd2[1]) { + snprintf(tracker_name, 40, "OpenMPT %d.%02x.%02x.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff, sfh.rsvd2[1], sfh.rsvd2[0]); + } else { + snprintf(tracker_name, 40, "OpenMPT %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + } + m->quirk |= QUIRK_ST3BUGS; + break; + case 4: + if (sfh.version != 0x4100) { + libxmp_schism_tracker_string(tracker_name, 40, + (sfh.version & 0x0fff), sfh.rsvd2[0] | (sfh.rsvd2[1] << 8)); + break; + } + /* fall through */ + case 6: + snprintf(tracker_name, 40, "BeRoTracker %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + break; + default: + snprintf(tracker_name, 40, "unknown (%04x)", sfh.version); + } + + libxmp_set_type(m, "%s S3M", tracker_name); +#else + libxmp_set_type(m, "Scream Tracker 3"); + m->quirk |= QUIRK_ST3BUGS; +#endif + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + goto err3; + + /* Read patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err3; + + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + pp_pat[i] * 16, SEEK_SET); + r = 0; + pat_len = hio_read16l(f) - 2; + + while (pat_len >= 0 && r < mod->xxp[i]->rows) { + b = hio_read8(f); + + if (hio_error(f)) { + goto err3; + } + + if (b == S3M_EOR) { + r++; + continue; + } + + c = b & S3M_CH_MASK; + event = c >= mod->chn ? &dummy : &EVENT(i, c, r); + + if (b & S3M_NI_FOLLOW) { + switch (n = hio_read8(f)) { + case 255: + n = 0; + break; /* Empty note */ + case 254: + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n = 13 + 12 * MSN(n) + LSN(n); + } + event->note = n; + event->ins = hio_read8(f); + pat_len -= 2; + } + + if (b & S3M_VOL_FOLLOWS) { + event->vol = hio_read8(f) + 1; + pat_len--; + } + + if (b & S3M_FX_FOLLOWS) { + event->fxt = hio_read8(f); + event->fxp = hio_read8(f); + xlat_fx(c, event); + + pat_len -= 2; + } + } + } + + D_(D_INFO "Stereo enabled: %s", stereo ? "yes" : "no"); + D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes"); + + if (libxmp_init_instrument(m) < 0) + goto err3; + + /* Read and convert instruments and samples */ + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + int load_sample_flags; + uint32 sample_segment; + + xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) { + goto err3; + } + + sub = &xxi->sub[0]; + + hio_seek(f, start + pp_ins[i] * 16, SEEK_SET); + sub->pan = 0x80; + sub->sid = i; + + if (hio_read(buf, 1, 80, f) != 80) { + goto err3; + } + + if (buf[0] >= 2) { +#ifndef LIBXMP_CORE_PLAYER + /* OPL2 FM instrument */ + + memcpy(sah.dosname, buf + 1, 12); /* DOS file name */ + memcpy(sah.reg, buf + 16, 12); /* Adlib registers */ + sah.vol = buf[28]; + sah.dsk = buf[29]; + sah.c2spd = readmem16l(buf + 32); /* C4 speed */ + memcpy(sah.name, buf + 48, 28); /* Instrument name */ + sah.magic = readmem32b(buf + 76); /* 'SCRI' */ + + if (sah.magic != MAGIC_SCRI) { + D_(D_CRIT "error: FM instrument magic"); + goto err3; + } + sah.magic = 0; + + libxmp_instrument_name(mod, i, sah.name, 28); + + xxi->nsm = 1; + sub->vol = sah.vol; + libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin); + sub->xpo += 12; + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, (char *)sah.reg); + if (ret < 0) + goto err3; + + D_(D_INFO "[%2X] %-28.28s", i, xxi->name); + + continue; +#else + goto err3; +#endif + } + + memcpy(sih.dosname, buf + 1, 12); /* DOS file name */ + sih.memseg_hi = buf[13]; /* High byte of sample pointer */ + sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */ + sih.length = readmem32l(buf + 16); /* Length */ + +#if 0 + /* ST3 limit */ + if ((sfh.version >> 12) == 1 && sih.length > 64000) + sih.length = 64000; +#endif + + if (sih.length > MAX_SAMPLE_SIZE) { + goto err3; + } + + sih.loopbeg = readmem32l(buf + 20); /* Loop begin */ + sih.loopend = readmem32l(buf + 24); /* Loop end */ + sih.vol = buf[28]; /* Volume */ + sih.pack = buf[30]; /* Packing type */ + sih.flags = buf[31]; /* Loop/stereo/16bit flags */ + sih.c2spd = readmem16l(buf + 32); /* C4 speed */ + memcpy(sih.name, buf + 48, 28); /* Instrument name */ + sih.magic = readmem32b(buf + 76); /* 'SCRS' */ + + if (buf[0] == 1 && sih.magic != MAGIC_SCRS) { + D_(D_CRIT "error: instrument magic"); + goto err3; + } + + xxs->len = sih.length; + xxi->nsm = sih.length > 0 ? 1 : 0; + xxs->lps = sih.loopbeg; + xxs->lpe = sih.loopend; + + xxs->flg = sih.flags & 1 ? XMP_SAMPLE_LOOP : 0; + + if (sih.flags & 4) { + xxs->flg |= XMP_SAMPLE_16BIT; + } + + load_sample_flags = (sfh.ffi == 1) ? 0 : SAMPLE_FLAG_UNS; + if (sih.pack == 4) { + load_sample_flags = SAMPLE_FLAG_ADPCM; + } + + sub->vol = sih.vol; + sih.magic = 0; + + libxmp_instrument_name(mod, i, sih.name, 28); + + D_(D_INFO "[%2X] %-28.28s %04x%c%04x %04x %c V%02x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, mod->xxs[i].lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sih.c2spd); + + libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin); + + sample_segment = sih.memseg + ((uint32)sih.memseg_hi << 16); + + if (hio_seek(f, start + 16L * sample_segment, SEEK_SET) < 0) { + goto err3; + } + + ret = libxmp_load_sample(m, f, load_sample_flags, xxs, NULL); + if (ret < 0) { + goto err3; + } + } + + free(pp_pat); + free(pp_ins); + + m->quirk |= QUIRKS_ST3 | QUIRK_ARPMEM; + m->read_event_type = READ_EVENT_ST3; + + return 0; + +err3: + free(pp_pat); +err2: + free(pp_ins); +err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/sample.c b/thirdparty/libxmp/src/loaders/sample.c new file mode 100644 index 0000000..74e4e75 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/sample.c @@ -0,0 +1,375 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../common.h" +#include "loader.h" + +#ifndef LIBXMP_CORE_PLAYER +/* + * From the Audio File Formats (version 2.5) + * Submitted-by: Guido van Rossum + * Last-modified: 27-Aug-1992 + * + * The Acorn Archimedes uses a variation on U-LAW with the bit order + * reversed and the sign bit in bit 0. Being a 'minority' architecture, + * Arc owners are quite adept at converting sound/image formats from + * other machines, and it is unlikely that you'll ever encounter sound in + * one of the Arc's own formats (there are several). + */ +static const int8 vdic_table[128] = { + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24 */ 1, 1, 1, 1, 1, 1, 1, 1, + /* 32 */ 1, 1, 1, 1, 2, 2, 2, 2, + /* 40 */ 2, 2, 2, 2, 3, 3, 3, 3, + /* 48 */ 3, 3, 4, 4, 4, 4, 5, 5, + /* 56 */ 5, 5, 6, 6, 6, 6, 7, 7, + /* 64 */ 7, 8, 8, 9, 9, 10, 10, 11, + /* 72 */ 11, 12, 12, 13, 13, 14, 14, 15, + /* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, + /* 88 */ 23, 24, 25, 26, 27, 28, 29, 30, + /* 96 */ 31, 33, 34, 36, 38, 40, 42, 44, + /* 104 */ 46, 48, 50, 52, 54, 56, 58, 60, + /* 112 */ 62, 65, 68, 72, 77, 80, 84, 91, + /* 120 */ 95, 98, 103, 109, 114, 120, 126, 127 +}; + +/* Convert 7 bit samples to 8 bit */ +static void convert_7bit_to_8bit(uint8 *p, int l) +{ + for (; l--; p++) { + *p <<= 1; + } +} + +/* Convert Archimedes VIDC samples to linear */ +static void convert_vidc_to_linear(uint8 *p, int l) +{ + int i; + uint8 x; + + for (i = 0; i < l; i++) { + x = p[i]; + p[i] = vdic_table[x >> 1]; + if (x & 0x01) + p[i] *= -1; + } +} + +static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len) +{ + char delta = 0; + uint8 b0, b1; + int i; + + len = (len + 1) / 2; + + for (i = 0; i < len; i++) { + b0 = *inp; + b1 = *inp++ >> 4; + delta += tab[b0 & 0x0f]; + *outp++ = delta; + delta += tab[b1 & 0x0f]; + *outp++ = delta; + } +} +#endif + +/* Convert differential to absolute sample data */ +static void convert_delta(uint8 *p, int l, int r) +{ + uint16 *w = (uint16 *)p; + uint16 absval = 0; + + if (r) { + for (; l--;) { + absval = *w + absval; + *w++ = absval; + } + } else { + for (; l--;) { + absval = *p + absval; + *p++ = (uint8) absval; + } + } +} + +/* Convert signed to unsigned sample data */ +static void convert_signal(uint8 *p, int l, int r) +{ + uint16 *w = (uint16 *)p; + + if (r) { + for (; l--; w++) + *w += 0x8000; + } else { + for (; l--; p++) + *p += (char)0x80; /* cast needed by MSVC++ */ + } +} + +/* Convert little-endian 16 bit samples to big-endian */ +static void convert_endian(uint8 *p, int l) +{ + uint8 b; + int i; + + for (i = 0; i < l; i++) { + b = p[0]; + p[0] = p[1]; + p[1] = b; + p += 2; + } +} + +#if 0 +/* Downmix stereo samples to mono */ +static void convert_stereo_to_mono(uint8 *p, int l, int r) +{ + int16 *b = (int16 *)p; + int i; + + if (r) { + l /= 2; + for (i = 0; i < l; i++) + b[i] = (b[i * 2] + b[i * 2 + 1]) / 2; + } else { + for (i = 0; i < l; i++) + p[i] = (p[i * 2] + p[i * 2 + 1]) / 2; + } +} +#endif + + +int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer) +{ + int bytelen, extralen, i; + +#ifndef LIBXMP_CORE_PLAYER + /* Adlib FM patches */ + if (flags & SAMPLE_FLAG_ADLIB) { + return 0; + } +#endif + + /* Empty or invalid samples + */ + if (xxs->len <= 0) { + return 0; + } + + /* Skip sample loading + * FIXME: fails for ADPCM samples + * + * + Sanity check: skip huge samples (likely corrupt module) + */ + if (xxs->len > MAX_SAMPLE_SIZE || (m && m->smpctl & XMP_SMPCTL_SKIP)) { + if (~flags & SAMPLE_FLAG_NOLOAD) { + /* coverity[check_return] */ + hio_seek(f, xxs->len, SEEK_CUR); + } + return 0; + } + + /* If this sample starts at or after EOF, skip it entirely. + */ + if (~flags & SAMPLE_FLAG_NOLOAD) { + long file_pos, file_len; + if (!f) { + return 0; + } + file_pos = hio_tell(f); + file_len = hio_size(f); + if (file_pos >= file_len) { + D_(D_WARN "ignoring sample at EOF"); + return 0; + } + /* If this sample goes past EOF, truncate it. */ + if (file_pos + xxs->len > file_len && (~flags & SAMPLE_FLAG_ADPCM)) { + D_(D_WARN "sample would extend %ld bytes past EOF; truncating to %ld", + file_pos + xxs->len - file_len, file_len - file_pos); + xxs->len = file_len - file_pos; + } + } + + /* Loop parameters sanity check + */ + if (xxs->lps < 0) { + xxs->lps = 0; + } + if (xxs->lpe > xxs->len) { + xxs->lpe = xxs->len; + } + if (xxs->lps >= xxs->len || xxs->lps >= xxs->lpe) { + xxs->lps = xxs->lpe = 0; + xxs->flg &= ~(XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR); + } + + /* Patches with samples + * Allocate extra sample for interpolation. + */ + bytelen = xxs->len; + extralen = 4; + + /* Disable birectional loop flag if sample is not looped + */ + if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { + if (~xxs->flg & XMP_SAMPLE_LOOP) + xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR; + } + if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) { + if (~xxs->flg & XMP_SAMPLE_SLOOP) + xxs->flg &= ~XMP_SAMPLE_SLOOP_BIDIR; + } + + if (xxs->flg & XMP_SAMPLE_16BIT) { + bytelen *= 2; + extralen *= 2; + } + + /* add guard bytes before the buffer for higher order interpolation */ + xxs->data = (unsigned char *) malloc(bytelen + extralen + 4); + if (xxs->data == NULL) { + goto err; + } + + *(uint32 *)xxs->data = 0; + xxs->data += 4; + + if (flags & SAMPLE_FLAG_NOLOAD) { + memcpy(xxs->data, buffer, bytelen); + } else +#ifndef LIBXMP_CORE_PLAYER + if (flags & SAMPLE_FLAG_ADPCM) { + int x2 = (bytelen + 1) >> 1; + char table[16]; + + if (hio_read(table, 1, 16, f) != 16) { + goto err2; + } + if (hio_read(xxs->data + x2, 1, x2, f) != x2) { + goto err2; + } + adpcm4_decoder((uint8 *)xxs->data + x2, + (uint8 *)xxs->data, table, bytelen); + } else +#endif + { + int x = hio_read(xxs->data, 1, bytelen, f); + if (x != bytelen) { + D_(D_WARN "short read (%d) in sample load", x - bytelen); + memset(xxs->data + x, 0, bytelen - x); + } + } + +#ifndef LIBXMP_CORE_PLAYER + if (flags & SAMPLE_FLAG_7BIT) { + convert_7bit_to_8bit(xxs->data, xxs->len); + } +#endif + + /* Fix endianism if needed */ + if (xxs->flg & XMP_SAMPLE_16BIT) { +#ifdef WORDS_BIGENDIAN + if (~flags & SAMPLE_FLAG_BIGEND) + convert_endian(xxs->data, xxs->len); +#else + if (flags & SAMPLE_FLAG_BIGEND) + convert_endian(xxs->data, xxs->len); +#endif + } + + /* Convert delta samples */ + if (flags & SAMPLE_FLAG_DIFF) { + convert_delta(xxs->data, xxs->len, xxs->flg & XMP_SAMPLE_16BIT); + } else if (flags & SAMPLE_FLAG_8BDIFF) { + int len = xxs->len; + if (xxs->flg & XMP_SAMPLE_16BIT) { + len *= 2; + } + convert_delta(xxs->data, len, 0); + } + + /* Convert samples to signed */ + if (flags & SAMPLE_FLAG_UNS) { + convert_signal(xxs->data, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT); + } + +#if 0 + /* Downmix stereo samples */ + if (flags & SAMPLE_FLAG_STEREO) { + convert_stereo_to_mono(xxs->data, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT); + xxs->len /= 2; + } +#endif + +#ifndef LIBXMP_CORE_PLAYER + if (flags & SAMPLE_FLAG_VIDC) { + convert_vidc_to_linear(xxs->data, xxs->len); + } +#endif + + /* Check for full loop samples */ + if (flags & SAMPLE_FLAG_FULLREP) { + if (xxs->lps == 0 && xxs->len > xxs->lpe) + xxs->flg |= XMP_SAMPLE_LOOP_FULL; + } + + /* Add extra samples at end */ + if (xxs->flg & XMP_SAMPLE_16BIT) { + for (i = 0; i < 8; i++) { + xxs->data[bytelen + i] = xxs->data[bytelen - 2 + i]; + } + } else { + for (i = 0; i < 4; i++) { + xxs->data[bytelen + i] = xxs->data[bytelen - 1 + i]; + } + } + + /* Add extra samples at start */ + if (xxs->flg & XMP_SAMPLE_16BIT) { + xxs->data[-2] = xxs->data[0]; + xxs->data[-1] = xxs->data[1]; + } else { + xxs->data[-1] = xxs->data[0]; + } + + return 0; + +#ifndef LIBXMP_CORE_PLAYER + err2: + libxmp_free_sample(xxs); +#endif + err: + return -1; +} + +void libxmp_free_sample(struct xmp_sample *s) +{ + if (s->data) { + free(s->data - 4); + s->data = NULL; /* prevent double free in PCM load error */ + } +} diff --git a/thirdparty/libxmp/src/loaders/sfx_load.c b/thirdparty/libxmp/src/loaders/sfx_load.c new file mode 100644 index 0000000..0ccd965 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/sfx_load.c @@ -0,0 +1,261 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Reverse engineered from the two SFX files in the Delitracker mods disk + * and music from Future Wars, Twinworld and Operation Stealth. Effects + * must be verified/implemented. + */ + +/* From the ExoticRipper docs: + * [SoundFX 2.0 is] simply the same as SoundFX 1.3, except that it + * uses 31 samples [instead of 15]. + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_SONG MAGIC4('S','O','N','G') + +static int sfx_test(HIO_HANDLE *, char *, const int); +static int sfx_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_sfx = { + "SoundFX v1.3/2.0", + sfx_test, + sfx_load +}; + +static int sfx_test(HIO_HANDLE * f, char *t, const int start) +{ + uint32 a, b; + + hio_seek(f, 4 * 15, SEEK_CUR); + a = hio_read32b(f); + hio_seek(f, 4 * 15, SEEK_CUR); + b = hio_read32b(f); + + if (a != MAGIC_SONG && b != MAGIC_SONG) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +struct sfx_ins { + uint8 name[22]; /* Instrument name */ + uint16 len; /* Sample length in words */ + uint8 finetune; /* Finetune */ + uint8 volume; /* Volume (0-63) */ + uint16 loop_start; /* Sample loop start in bytes */ + uint16 loop_length; /* Sample loop length in words */ +}; + +struct sfx_header { + uint32 magic; /* 'SONG' */ + uint16 delay; /* Delay value (tempo), default is 0x38e5 */ + uint16 unknown[7]; /* ? */ +}; + +struct sfx_header2 { + uint8 len; /* Song length */ + uint8 restart; /* Restart pos (?) */ + uint8 order[128]; /* Order list */ +}; + +static int sfx_13_20_load(struct module_data *m, HIO_HANDLE *f, const int nins, + const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct sfx_header sfx; + struct sfx_header2 sfx2; + uint8 ev[4]; + int ins_size[31]; + struct sfx_ins ins[31]; /* Instruments */ + + LOAD_INIT(); + + for (i = 0; i < nins; i++) + ins_size[i] = hio_read32b(f); + + sfx.magic = hio_read32b(f); + sfx.delay = hio_read16b(f); + if (sfx.delay < 178) /* min value for 10000bpm */ + return -1; + + hio_read(sfx.unknown, 14, 1, f); + + if (sfx.magic != MAGIC_SONG) + return -1; + + mod->chn = 4; + mod->ins = nins; + mod->smp = mod->ins; + mod->bpm = 14565 * 122 / sfx.delay; + + for (i = 0; i < mod->ins; i++) { + hio_read(ins[i].name, 22, 1, f); + ins[i].len = hio_read16b(f); + ins[i].finetune = hio_read8(f); + ins[i].volume = hio_read8(f); + ins[i].loop_start = hio_read16b(f); + ins[i].loop_length = hio_read16b(f); + } + + sfx2.len = hio_read8(f); + sfx2.restart = hio_read8(f); + if (hio_read(sfx2.order, 1, 128, f) != 128) + return -1; + + mod->len = sfx2.len; + if (mod->len > 0x7f) + return -1; + + memcpy(mod->xxo, sfx2.order, mod->len); + for (mod->pat = i = 0; i < mod->len; i++) + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + mod->pat++; + + mod->trk = mod->chn * mod->pat; + + if (mod->ins == 15) { + libxmp_set_type(m, "SoundFX 1.3"); + } else { + libxmp_set_type(m, "SoundFX 2.0"); + } + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + xxs = &mod->xxs[i]; + sub = &xxi->sub[0]; + + xxs->len = ins_size[i]; + xxs->lps = ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * ins[i].loop_length; + xxs->flg = ins[i].loop_length > 1 ? XMP_SAMPLE_LOOP : 0; + xxi->nsm = 1; + sub->vol = ins[i].volume; + sub->fin = (int8) (ins[i].finetune << 4); /* unsure */ + sub->pan = 0x80; + sub->sid = i; + + libxmp_instrument_name(mod, i, ins[i].name, 22); + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c %02x %+d", + i, xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, + sub->fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 64 * mod->chn; j++) { + event = &EVENT(i, j % mod->chn, j / mod->chn); + if (hio_read(ev, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + + event->note = libxmp_period_to_note((LSN(ev[0]) << 8) | ev[1]); + event->ins = (MSN(ev[0]) << 4) | MSN(ev[2]); + event->fxp = ev[3]; + + switch (LSN(ev[2])) { + case 0x01: /* Arpeggio */ + event->fxt = FX_ARPEGGIO; + break; + case 0x02: /* Pitch bend */ + if (event->fxp >> 4) { + event->fxt = FX_PORTA_DN; + event->fxp >>= 4; + } else if (event->fxp & 0x0f) { + event->fxt = FX_PORTA_UP; + event->fxp &= 0x0f; + } + break; + case 0x5: /* Add to volume */ + event->fxt = FX_VOL_ADD; + break; + case 0x6: /* Subtract from volume */ + event->fxt = FX_VOL_SUB; + break; + case 0x7: /* Add semitones to period */ + event->fxt = FX_PITCH_ADD; + break; + case 0x8: /* Subtract semitones from period */ + event->fxt = FX_PITCH_SUB; + break; + case 0x3: /* LED on */ + case 0x4: /* LED off */ + default: + event->fxt = event->fxp = 0; + break; + } + } + } + + m->quirk |= QUIRK_PBALL; + m->period_type = PERIOD_MODRNG; + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 2) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} + +static int sfx_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + if (sfx_13_20_load(m, f, 15, start) < 0) + return sfx_13_20_load(m, f, 31, start); + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/st_load.c b/thirdparty/libxmp/src/loaders/st_load.c new file mode 100644 index 0000000..97540bf --- /dev/null +++ b/thirdparty/libxmp/src/loaders/st_load.c @@ -0,0 +1,517 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Ultimate Soundtracker support based on the module format description + * written by Michael Schwendt + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +static int st_test(HIO_HANDLE *, char *, const int); +static int st_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_st = { + "Soundtracker", + st_test, + st_load +}; + +/* musanx.mod contains 22 period and instrument errors */ +#define ST_MAX_PATTERN_ERRORS 22 +/* Allow some degree of sample truncation for ST modules. + * The worst known module currently is u2.mod with 7% truncation. */ +#define ST_TRUNCATION_LIMIT 93 + +static const int period[] = { + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, + /* Off-by-one period values found in blueberry.mod, snd.mod, + * quite a lot.mod, sweet dreams.mod, and bar----fringdus.mod */ + 763, 679, 641, 571, 539, 509, 429, 340, 321, 300, 286, 270, + 227, 191, 162, + -1 +}; + +static int st_expected_size(int smp_size, int pat) +{ + return 600 + smp_size + 1024 * pat; +} + +static int st_test(HIO_HANDLE *f, char *t, const int start) +{ + int i, j, k; + int pat, pat_short, ins, smp_size; + struct st_header mh; + uint8 pat_buf[1024]; + uint8 *mod_event; + int pattern_errors; + int test_flags = TEST_NAME_IGNORE_AFTER_CR; + long size; + + size = hio_size(f); + + if (size < 600) { + return -1; + } + + smp_size = 0; + + hio_seek(f, start, SEEK_SET); + hio_read(mh.name, 1, 20, f); + /* The Super Ski 2 modules have unusual "SONG\x13\x88" names. */ + if (mh.name[5] == 0x88) { + mh.name[5] = 'X'; + if (mh.name[4] == 0x13) + mh.name[4] = 'X'; + } + if (libxmp_test_name(mh.name, 20, 0) < 0) { + D_(D_CRIT "bad module name; not ST"); + return -1; + } + + for (i = 0; i < 15; i++) { + hio_read(mh.ins[i].name, 1, 22, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 1, 128, f); + + for (pat = pat_short = i = 0; i < 128; i++) { + if (mh.order[i] > 0x7f) + return -1; + if (mh.order[i] > pat) { + pat = mh.order[i]; + if (i < mh.len) + pat_short = pat; + } + } + pat++; + pat_short++; + + if (pat > 0x7f || mh.len == 0 || mh.len > 0x80) + return -1; + + for (i = 0; i < 15; i++) { + smp_size += 2 * mh.ins[i].size; + + /* pennylane.mod and heymusic-sssexremix.mod have unusual + * values after the \0. */ + if (i == 0 && + (!memcmp(mh.ins[i].name, "funbass\0\r", 9) || + !memcmp(mh.ins[i].name, "st-69:baseline\0R\0\0\xA5", 17))) { + D_(D_INFO "ignoring junk name values after \\0"); + test_flags |= TEST_NAME_IGNORE_AFTER_0; + } + + /* Crepequs.mod has random values in first byte */ + mh.ins[i].name[0] = 'X'; + + if (libxmp_test_name(mh.ins[i].name, 22, test_flags) < 0) { + D_(D_CRIT "bad instrument name %d; not ST", i); + return -1; + } + + if (mh.ins[i].volume > 0x40) + return -1; + + if (mh.ins[i].finetune > 0x0f) + return -1; + + if (mh.ins[i].size > 0x8000) + return -1; + + /* This test is always false, disable it + * + * if ((mh.ins[i].loop_start >> 1) > 0x8000) + * return -1; + */ + + if (mh.ins[i].loop_size > 0x8000) + return -1; + + /* This test fails in atmosfer.mod, disable it + * + * if (mh.ins[i].loop_size > 1 && mh.ins[i].loop_size > mh.ins[i].size) + * return -1; + */ + + /* Bad rip of fin-nv1.mod has this unused instrument. */ + if (mh.ins[i].size == 0 && + mh.ins[i].loop_start == 4462 && + mh.ins[i].loop_size == 2078) { + D_(D_INFO "ignoring bad instrument for fin-nv1.mod"); + continue; + } + + if ((mh.ins[i].loop_start >> 1) > mh.ins[i].size) + return -1; + + if (mh.ins[i].size + && (mh.ins[i].loop_start >> 1) == mh.ins[i].size) + return -1; + + if (mh.ins[i].size == 0 && mh.ins[i].loop_start > 0) + return -1; + } + + if (smp_size < 8) { + return -1; + } + + /* If the file size is correct when counting only patterns prior to the + * module length, use the shorter count. This quirk is found in some + * ST modules, most of them authored by Jean Baudlot. See razor-1911.mod, + * the Operation Wolf soundtrack, or the Bad Dudes soundtrack. + */ + if (size < st_expected_size(smp_size, pat) && + size == st_expected_size(smp_size, pat_short)) { + D_(D_INFO "ST pattern list probably quirked, ignoring patterns past len"); + pat = pat_short; + } + + pattern_errors = 0; + for (ins = i = 0; i < pat; i++) { + if (hio_read(pat_buf, 1, 1024, f) < 1024) { + D_(D_CRIT "read error at pattern %d; not ST", i); + return -1; + } + mod_event = pat_buf; + for (j = 0; j < (64 * 4); j++, mod_event += 4) { + int p, s; + + s = (mod_event[0] & 0xf0) | MSN(mod_event[2]); + + if (s > 15) { /* sample number > 15 */ + D_(D_INFO "%d/%d/%d: invalid sample number: %d", i, j / 4, j % 4, s); + if ((++pattern_errors) > ST_MAX_PATTERN_ERRORS) + goto bad_pattern_data; + } + + if (s > ins) { /* find highest used sample */ + ins = s; + } + + p = 256 * LSN(mod_event[0]) + mod_event[1]; + + if (p == 0) { + continue; + } + + for (k = 0; period[k] >= 0; k++) { + if (p == period[k]) + break; + } + if (period[k] < 0) { + D_(D_INFO "%d/%d/%d: invalid period: %d", i, j / 4, j % 4, p); + if ((++pattern_errors) > ST_MAX_PATTERN_ERRORS) + goto bad_pattern_data; + } + } + } + + /* Check if file was cut before any unused samples */ + if (size < st_expected_size(smp_size, pat)) { + int ss, limit; + for (ss = i = 0; i < 15 && i < ins; i++) { + ss += 2 * mh.ins[i].size; + } + + limit = st_expected_size(ss, pat) * ST_TRUNCATION_LIMIT / 100; + if (size < limit) { + D_(D_CRIT "expected size %d, minimum allowed size %d, real size %ld, diff %ld", + st_expected_size(smp_size, pat), limit, size, size - limit); + return -1; + } + } + + hio_seek(f, start, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; + +bad_pattern_data: + D_(D_CRIT "too many pattern errors; not ST: %d", pattern_errors); + return -1; +} + +static int st_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct st_header mh; + uint8 pat_buf[1024]; + uint8 *mod_event; + int ust = 1; + /* int lps_mult = m->fetch & XMP_CTL_FIXLOOP ? 1 : 2; */ + const char *modtype; + int fxused; + int smp_size, pat_short; + long size; + + LOAD_INIT(); + + mod->chn = 4; + mod->ins = 15; + mod->smp = mod->ins; + + smp_size = 0; + + hio_read(mh.name, 1, 20, f); + for (i = 0; i < 15; i++) { + hio_read(mh.ins[i].name, 1, 22, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + smp_size += 2 * mh.ins[i].size; + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 1, 128, f); + + mod->len = mh.len; + mod->rst = mh.restart; + + /* UST: The byte at module offset 471 is BPM, not the song restart + * The default for UST modules is 0x78 = 120 BPM = 48 Hz. + */ + if (mod->rst < 0x40) /* should be 0x20 */ + ust = 0; + + memcpy(mod->xxo, mh.order, 128); + + for (pat_short = i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) { + mod->pat = mod->xxo[i]; + if (i < mh.len) + pat_short = mod->pat; + } + } + mod->pat++; + pat_short++; + + /* If the file size is correct when counting only patterns prior to the + * module length, use the shorter count. See test function for info. + */ + size = hio_size(f); + if (size < st_expected_size(smp_size, mod->pat) && + size == st_expected_size(smp_size, pat_short)) { + mod->pat = pat_short; + } + + for (i = 0; i < mod->ins; i++) { + /* UST: Volume word does not contain a "Finetuning" value in its + * high-byte. + */ + if (mh.ins[i].finetune) + ust = 0; + + /* if (mh.ins[i].size == 0 && mh.ins[i].loop_size == 1) + nt = 1; */ + + /* UST: Maximum sample length is 9999 bytes decimal, but 1387 + * words hexadecimal. Longest samples on original sample disk + * ST-01 were 9900 bytes. + */ + if (mh.ins[i].size > 0x1387 || mh.ins[i].loop_start > 9999 + || mh.ins[i].loop_size > 0x1387) { + ust = 0; + } + } + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + xxs->len = 2 * mh.ins[i].size - mh.ins[i].loop_start; + xxs->lps = 0; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = (int8) (mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + strncpy((char *)xxi->name, (char *)mh.ins[i].name, 22); + + if (xxs->len > 0) { + xxi->nsm = 1; + } + } + + mod->trk = mod->chn * mod->pat; + + strncpy(mod->name, (char *)mh.name, 20); + + if (libxmp_init_pattern(mod) < 0) { + return -1; + } + + /* Load and convert patterns */ + /* Also scan patterns for tracker detection */ + fxused = 0; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (hio_read(pat_buf, 1, 1024, f) < 1024) + return -1; + + mod_event = pat_buf; + for (j = 0; j < (64 * 4); j++, mod_event += 4) { + event = &EVENT(i, j % 4, j / 4); + libxmp_decode_protracker_event(event, mod_event); + + if (event->fxt) + fxused |= 1 << event->fxt; + else if (event->fxp) + fxused |= 1; + + /* UST: Only effects 1 (arpeggio) and 2 (pitchbend) are + * available. + */ + if (event->fxt && event->fxt != 1 && event->fxt != 2) + ust = 0; + + /* Karsten Obarski's sleepwalk uses arpeggio 30 and 40 */ + if (event->fxt == 1) { /* unlikely arpeggio */ + if (event->fxp == 0x00) + ust = 0; + /*if ((ev.fxp & 0x0f) == 0 || (ev.fxp & 0xf0) == 0) + ust = 0; */ + } + + if (event->fxt == 2) { /* bend up and down at same time? */ + if ((event->fxp & 0x0f) != 0 && (event->fxp & 0xf0) != 0) + ust = 0; + } + } + } + + if (fxused & ~0x0006) { + ust = 0; + } + + if (ust) { + modtype = "Ultimate Soundtracker"; + } else if ((fxused & ~0xd007) == 0) { + modtype = "Soundtracker IX"; /* or MasterSoundtracker? */ + } else if ((fxused & ~0xf807) == 0) { + modtype = "D.O.C Soundtracker 2.0"; + } else { + modtype = "unknown tracker 15 instrument"; + } + + snprintf(mod->type, XMP_NAME_SIZE, "%s", modtype); + + MODULE_INFO(); + + for (i = 0; i < mod->ins; i++) { + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, mh.ins[i].loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin >> 4); + } + + m->quirk |= QUIRK_NOBPM; + m->period_type = PERIOD_MODRNG; + + /* Perform the necessary conversions for Ultimate Soundtracker */ + if (ust) { + /* Fix restart & bpm */ + mod->bpm = mod->rst; + mod->rst = 0; + + /* Fix sample loops */ + for (i = 0; i < mod->ins; i++) { + /* FIXME */ + } + + /* Fix effects (arpeggio and pitchbending) */ + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (event->fxt == 1) + event->fxt = 0; + else if (event->fxt == 2 + && (event->fxp & 0xf0) == 0) + event->fxt = 1; + else if (event->fxt == 2 + && (event->fxp & 0x0f) == 0) + event->fxp >>= 4; + } + } + } else { + if (mod->rst >= mod->len) + mod->rst = 0; + } + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (!mod->xxs[i].len) + continue; + + /* Skip transient part of sample. + * + * Dennis Lindroos reports: One main thing is + * sample-looping which on all trackers up to Noisetracker 1 (i + * think it was Mahoney who actually changed the loopstart to be + * in WORDS) never play looped samples from the beginning, i.e. + * only plays the looped part. This can be heard in old modules + * especially with "analogstring", "strings2" or "strings3" + * samples because these have "slow attack" that is not part of + * the loop and thus they sound "sharper".. + */ + hio_seek(f, mh.ins[i].loop_start, SEEK_CUR); + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) { + return -1; + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/stim_load.c b/thirdparty/libxmp/src/loaders/stim_load.c new file mode 100644 index 0000000..d1daa0a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/stim_load.c @@ -0,0 +1,216 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Slamtilt modules based on the format description + * written by Sylvain Chipaux (Asle/ReDoX). Get the Slamtilt demo + * from game/demo in Aminet. + */ + +/* Tested with the Slamtilt modules sent by Sipos Attila */ + +#include "loader.h" + +#define MAGIC_STIM MAGIC4('S','T','I','M') + +static int stim_test(HIO_HANDLE *, char *, const int); +static int stim_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_stim = { + "Slamtilt", + stim_test, + stim_load +}; + +static int stim_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_STIM) + return -1; + + if (hio_read16b(f) > 16) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +struct stim_instrument { + uint16 size; /* Lenght of the sample (/2) */ + uint8 finetune; /* Finetune (as ptk) */ + uint8 volume; /* Volume (as ptk) */ + uint16 loop_start; /* Loop start (/2) */ + uint16 loop_size; /* Loop lenght (/2) */ +}; + +struct stim_header { + uint32 id; /* "STIM" ID string */ + uint32 smpaddr; /* Address of the sample descriptions */ + uint32 unknown[2]; + uint16 nos; /* Number of samples (?) */ + uint16 len; /* Size of pattern list */ + uint16 pat; /* Number of patterns saved */ + uint8 order[128]; /* Pattern list */ + uint32 pataddr[64]; /* Pattern addresses (add 0xc) */ +}; + +static int stim_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct xmp_event *event; + struct stim_header sh; + struct stim_instrument si; + uint8 b1, b2, b3; + + LOAD_INIT(); + + sh.id = hio_read32b(f); + sh.smpaddr = hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + sh.nos = hio_read16b(f); + sh.len = hio_read16b(f); + sh.pat = hio_read16b(f); + if (hio_read(sh.order, 128, 1, f) == 0) { + return -1; + } + + /* Sanity check */ + if (sh.nos > 31 || sh.len > 128 || sh.pat > 64) { + return -1; + } + + for (i = 0; i < 64; i++) { + sh.pataddr[i] = hio_read32b(f) + 0x0c; + if (sh.pataddr[i] > 0x00100000) + return -1; + } + + mod->chn = 4; + mod->len = sh.len; + mod->pat = sh.pat; + mod->ins = sh.nos; + mod->smp = mod->ins; + mod->trk = mod->pat * mod->chn; + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = sh.order[i]; + + libxmp_set_type(m, "Slamtilt"); + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + hio_seek(f, start + sh.pataddr[i] + 8, SEEK_SET); + + for (j = 0; j < 4; j++) { + for (k = 0; k < 64; k++) { + event = &EVENT(i, j, k); + b1 = hio_read8(f); + + if (b1 & 0x80) { + k += b1 & 0x7f; + continue; + } + + /* STIM event format: + * + * __ Fx __ + * / \ + * || || + * 0000 0000 0000 0000 0000 0000 + * | | | | | | | + * | \ / \ / \ / + * | smp note Fx Val + * | + * Description bit set to 0. + */ + + b2 = hio_read8(f); + b3 = hio_read8(f); + + if ((event->note = b2 & 0x3f) != 0) + event->note += 47; + event->ins = b1 & 0x1f; + event->fxt = ((b2 >> 4) & 0x0c) | (b1 >> 5); + event->fxp = b3; + + libxmp_disable_continue_fx(event); + } + } + } + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Stored samples: %d", mod->smp); + + hio_seek(f, start + sh.smpaddr + mod->smp * 4, SEEK_SET); + + for (i = 0; i < mod->smp; i++) { + si.size = hio_read16b(f); + si.finetune = hio_read8(f); + si.volume = hio_read8(f); + si.loop_start = hio_read16b(f); + si.loop_size = hio_read16b(f); + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = 2 * si.size; + mod->xxs[i].lps = 2 * si.loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * si.loop_size; + mod->xxs[i].flg = si.loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].fin = (int8) (si.finetune << 4); + mod->xxi[i].sub[0].vol = si.volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + D_(D_INFO "[%2X] %04x %04x %04x %c V%02x %+d", + i, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, si.loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin >> 4); + + if (!mod->xxs[i].len) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->period_type = PERIOD_MODRNG; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/stm_load.c b/thirdparty/libxmp/src/loaders/stm_load.c new file mode 100644 index 0000000..d4b11b9 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/stm_load.c @@ -0,0 +1,416 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define STM_TYPE_SONG 0x01 +#define STM_TYPE_MODULE 0x02 + +struct stm_instrument_header { + uint8 name[12]; /* ASCIIZ instrument name */ + uint8 id; /* Id=0 */ + uint8 idisk; /* Instrument disk */ + uint16 rsvd1; /* Reserved */ + uint16 length; /* Sample length */ + uint16 loopbeg; /* Loop begin */ + uint16 loopend; /* Loop end */ + uint8 volume; /* Playback volume */ + uint8 rsvd2; /* Reserved */ + uint16 c2spd; /* C4 speed */ + uint32 rsvd3; /* Reserved */ + uint16 paralen; /* Length in paragraphs */ +}; + +/* v1 format header based on disassembled ST2 */ +struct stm_file_subheader_v1 { + uint16 insnum; /* Number of instruments */ + uint16 ordnum; /* Number of orders */ + uint16 patnum; /* Number of patterns */ + uint16 srate; /* Sample rate? */ + uint8 tempo; /* Playback tempo */ + uint8 channels; /* Number of channels */ + uint16 psize; /* Pattern size */ + uint16 rsvd2; /* Reserved */ + uint16 skip; /* Bytes to skip */ +}; + +struct stm_file_subheader_v2 { + uint8 tempo; /* Playback tempo */ + uint8 patterns; /* Number of patterns */ + uint8 gvol; /* Global volume */ + uint8 rsvd2[13]; /* Reserved */ +}; + +struct stm_file_header { + uint8 name[20]; /* ASCIIZ song name */ + uint8 magic[8]; /* '!Scream!' */ + uint8 rsvd1; /* '\x1a' */ + uint8 type; /* 1=song, 2=module */ + uint8 vermaj; /* Major version number */ + uint8 vermin; /* Minor version number */ + union { + struct stm_file_subheader_v1 v1; + struct stm_file_subheader_v2 v2; + } sub; + struct stm_instrument_header ins[32]; +}; + +static int stm_test(HIO_HANDLE *, char *, const int); +static int stm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_stm = { + "Scream Tracker 2", + stm_test, + stm_load +}; + +static int stm_test(HIO_HANDLE * f, char *t, const int start) +{ + uint8 buf[8]; + + hio_seek(f, start + 20, SEEK_SET); + if (hio_read(buf, 1, 8, f) < 8) + return -1; + + if (libxmp_test_name(buf, 8, 0)) /* Tracker name should be ASCII */ + return -1; + + if (hio_read8(f) != 0x1a) + return -1; + + if (hio_read8(f) > STM_TYPE_MODULE) + return -1; + + hio_seek(f, start + 60, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (!memcmp(buf, "SCRM", 4)) /* We don't want STX files */ + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +#define FX_NONE 0xff + +/* + * Skaven's note from http://www.futurecrew.com/skaven/oldies_music.html + * + * FYI for the tech-heads: In the old Scream Tracker 2 the Arpeggio command + * (Jxx), if used in a single row with a 0x value, caused the note to skip + * the specified amount of halftones upwards halfway through the row. I used + * this in some songs to give the lead some character. However, when played + * in ModPlug Tracker, this effect doesn't work the way it did back then. + */ +static const uint8 fx[16] = { + FX_NONE, + FX_SPEED, /* A - Set tempo to [INFO]. 60 normal. */ + FX_JUMP, /* B - Break pattern and jmp to order [INFO] */ + FX_BREAK, /* C - Break pattern */ + FX_VOLSLIDE, /* D - Slide volume; Hi-nibble=up, Lo-nibble=down */ + FX_PORTA_DN, /* E - Slide down at speed [INFO] */ + FX_PORTA_UP, /* F - Slide up at speed [INFO] */ + FX_TONEPORTA, /* G - Slide to the note specified at speed [INFO] */ + FX_VIBRATO, /* H - Vibrato; Hi-nibble, speed. Lo-nibble, size */ + FX_TREMOR, /* I - Tremor; Hi-nibble, ontime. Lo-nibble, offtime */ + FX_ARPEGGIO, /* J - Arpeggio */ + FX_NONE, + FX_NONE, + FX_NONE, + FX_NONE, + FX_NONE +}; + +static int stm_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + struct stm_file_header sfh; + uint8 b; + uint16 version; + int blank_pattern = 0; + int stored_patterns; + int i, j, k; + + LOAD_INIT(); + + hio_read(sfh.name, 20, 1, f); /* ASCIIZ song name */ + hio_read(sfh.magic, 8, 1, f); /* '!Scream!' */ + sfh.rsvd1 = hio_read8(f); /* '\x1a' */ + sfh.type = hio_read8(f); /* 1=song, 2=module */ + sfh.vermaj = hio_read8(f); /* Major version number */ + sfh.vermin = hio_read8(f); /* Minor version number */ + version = (100 * sfh.vermaj) + sfh.vermin; + + if (version != 110 && + version != 200 && version != 210 && + version != 220 && version != 221) { + D_(D_CRIT "Unknown version: %d", version); + return -1; + } + + // TODO: improve robustness of the loader against bad parameters + + if (version >= 200) { + sfh.sub.v2.tempo = hio_read8(f); /* Playback tempo */ + sfh.sub.v2.patterns = hio_read8(f); /* Number of patterns */ + sfh.sub.v2.gvol = hio_read8(f); /* Global volume */ + hio_read(sfh.sub.v2.rsvd2, 13, 1, f); /* Reserved */ + mod->chn = 4; + mod->pat = sfh.sub.v2.patterns; + mod->spd = (version < 221) ? LSN(sfh.sub.v2.tempo / 10) : MSN(sfh.sub.v2.tempo); + mod->ins = 31; + mod->len = (version == 200) ? 64 : 128; + } else { + if ((sfh.sub.v1.insnum = hio_read16l(f)) > 32) { /* Number of instruments */ + D_(D_CRIT "Wrong number of instruments: %d (max 32)", sfh.sub.v1.insnum); + return -1; + } + if ((sfh.sub.v1.ordnum = hio_read16l(f)) > XMP_MAX_MOD_LENGTH) { /* Number of orders */ + D_(D_CRIT "Wrong number of orders: %d (max %d)", sfh.sub.v1.ordnum, XMP_MAX_MOD_LENGTH); + return -1; + } + if ((sfh.sub.v1.patnum = hio_read16l(f)) > XMP_MAX_MOD_LENGTH) { /* Number of patterns */ + D_(D_CRIT "Wrong number of patterns: %d (max %d)", sfh.sub.v1.patnum, XMP_MAX_MOD_LENGTH); + return -1; + } + sfh.sub.v1.srate = hio_read16l(f); /* Sample rate? */ + sfh.sub.v1.tempo = hio_read8(f); /* Playback tempo */ + if ((sfh.sub.v1.channels = hio_read8(f)) != 4) { /* Number of channels */ + D_(D_CRIT "Wrong number of sound channels: %d", sfh.sub.v1.channels); + return -1; + } + if ((sfh.sub.v1.psize = hio_read16l(f)) != 64) { /* Pattern size */ + D_(D_CRIT "Wrong number of rows per pattern: %d", sfh.sub.v1.psize); + return -1; + } + sfh.sub.v1.rsvd2 = hio_read16l(f); /* Reserved */ + sfh.sub.v1.skip = hio_read16l(f); /* Bytes to skip */ + hio_seek(f, sfh.sub.v1.skip, SEEK_CUR); /* Skip bytes */ + mod->chn = sfh.sub.v1.channels; + mod->pat = sfh.sub.v1.patnum; + mod->spd = (version != 100) ? LSN(sfh.sub.v1.tempo / 10) : LSN(sfh.sub.v1.tempo); + mod->ins = sfh.sub.v1.insnum; + mod->len = sfh.sub.v1.ordnum; + } + + for (i = 0; i < mod->ins; i++) { + hio_read(sfh.ins[i].name, 12, 1, f); /* Instrument name */ + sfh.ins[i].id = hio_read8(f); /* Id=0 */ + sfh.ins[i].idisk = hio_read8(f); /* Instrument disk */ + sfh.ins[i].rsvd1 = hio_read16l(f); /* Reserved */ + sfh.ins[i].length = hio_read16l(f); /* Sample length */ + sfh.ins[i].loopbeg = hio_read16l(f); /* Loop begin */ + sfh.ins[i].loopend = hio_read16l(f); /* Loop end */ + sfh.ins[i].volume = hio_read8(f); /* Playback volume */ + sfh.ins[i].rsvd2 = hio_read8(f); /* Reserved */ + sfh.ins[i].c2spd = hio_read16l(f); /* C4 speed */ + sfh.ins[i].rsvd3 = hio_read32l(f); /* Reserved */ + sfh.ins[i].paralen = hio_read16l(f); /* Length in paragraphs */ + } + + if (hio_error(f)) { + return -1; + } + + mod->smp = mod->ins; + m->c4rate = C4_NTSC_RATE; + + libxmp_copy_adjust(mod->name, sfh.name, 20); + + if (!sfh.magic[0] || !strncmp((char *)sfh.magic, "PCSTV", 5) || !strncmp((char *)sfh.magic, "!Scream!", 8)) + libxmp_set_type(m, "Scream Tracker %d.%02d", sfh.vermaj, sfh.vermin); + else if (!strncmp((char *)sfh.magic, "SWavePro", 8)) + libxmp_set_type(m, "SoundWave Pro %d.%02d", sfh.vermaj, sfh.vermin); + else + libxmp_copy_adjust(mod->type, sfh.magic, 8); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = sfh.ins[i].length; + mod->xxs[i].lps = sfh.ins[i].loopbeg; + mod->xxs[i].lpe = sfh.ins[i].loopend; + if (mod->xxs[i].lpe == 0xffff) + mod->xxs[i].lpe = 0; + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = sfh.ins[i].volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, sfh.ins[i].name, 12); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c V%02x %5d", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, sfh.ins[i].c2spd); + + libxmp_c2spd_to_note(sfh.ins[i].c2spd, &mod->xxi[i].sub[0].xpo, + &mod->xxi[i].sub[0].fin); + } + + if (hio_read(mod->xxo, 1, mod->len, f) < mod->len) + return -1; + + for (i = 0; i < mod->len; i++) { + if (mod->xxo[i] >= 99) { + break; + } + /* Patterns >= the pattern count are valid blank patterns. + * Examples: jimmy.stm, Rauno/dogs.stm, Skaven/hevijanis istu maas.stm. + * Patterns >= 64 have undefined behavior in Screamtracker 2. + */ + if (mod->xxo[i] >= mod->pat) { + mod->xxo[i] = mod->pat; + blank_pattern = 1; + } + } + stored_patterns = mod->pat; + if(blank_pattern) + mod->pat++; + + mod->trk = mod->pat * mod->chn; + mod->len = i; + + D_(D_INFO "Module length: %d", mod->len); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", stored_patterns); + + if(blank_pattern) { + if (libxmp_alloc_pattern_tracks(mod, stored_patterns, 64) < 0) + return -1; + } + + for (i = 0; i < stored_patterns; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (hio_error(f)) + return -1; + + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + b = hio_read8(f); + if (b == 251 || b == 252) + continue; /* Empty note */ + + if (b == 253) { + event->note = XMP_KEY_OFF; + continue; /* Key off */ + } + + if (b == 254) + event->note = XMP_KEY_OFF; + else if (b == 255) + event->note = 0; + else + event->note = 1 + LSN(b) + 12 * (3 + MSN(b)); + + b = hio_read8(f); + event->vol = b & 0x07; + event->ins = (b & 0xf8) >> 3; + + b = hio_read8(f); + event->vol += (b & 0xf0) >> 1; + if (version >= 200) { + event->vol = (event->vol > 0x40) ? 0 : event->vol + 1; + } else { + if (event->vol > 0) { + event->vol = (event->vol > 0x40) ? 1 : event->vol + 1; + } + } + + event->fxt = fx[LSN(b)]; + event->fxp = hio_read8(f); + switch (event->fxt) { + case FX_SPEED: + event->fxp = (version < 221) ? LSN(event->fxp / 10) : MSN(event->fxp); + break; + case FX_NONE: + event->fxp = event->fxt = 0; + break; + } + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (!sfh.ins[i].volume || !sfh.ins[i].length) { + mod->xxi[i].nsm = 0; + continue; + } + + if (sfh.type == STM_TYPE_SONG) { + HIO_HANDLE *s; + char sn[XMP_MAXPATH]; + char tmpname[32]; + const char *instname = mod->xxi[i].name; + + if (!instname[0] || !m->dirname) + continue; + + if (libxmp_copy_name_for_fopen(tmpname, instname, 32)) + continue; + + snprintf(sn, XMP_MAXPATH, "%s%s", m->dirname, tmpname); + s = hio_open(sn, "rb"); + if (s != NULL) { + if (libxmp_load_sample(m, s, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) { + hio_close(s); + return -1; + } + hio_close(s); + } + } else { + hio_seek(f, start + (sfh.ins[i].rsvd1 << 4), SEEK_SET); + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + } + + m->quirk |= QUIRK_VSALL | QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/stx_load.c b/thirdparty/libxmp/src/loaders/stx_load.c new file mode 100644 index 0000000..56a7a66 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/stx_load.c @@ -0,0 +1,385 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From the STMIK 0.2 documentation: + * + * "The STMIK uses a special [Scream Tracker] beta-V3.0 module format. + * Due to the module formats beta nature, the current STMIK uses a .STX + * extension instead of the normal .STM. I'm not intending to do a + * STX->STM converter, so treat STX as the format to be used in finished + * programs, NOT as a format to be used in distributing modules. A program + * called STM2STX is included, and it'll convert STM modules to the STX + * format for usage in your own programs." + * + * Tested using "Future Brain" from Mental Surgery by Future Crew and + * STMs converted with STM2STX. + */ + +#include "loader.h" +#include "s3m.h" +#include "../period.h" + +struct stx_file_header { + uint8 name[20]; /* Song name */ + uint8 magic[8]; /* !Scream! */ + uint16 psize; /* Pattern 0 size? */ + uint16 unknown1; /* ?! */ + uint16 pp_pat; /* Pointer to pattern table */ + uint16 pp_ins; /* Pattern to instrument table */ + uint16 pp_chn; /* Pointer to channel table (?) */ + uint16 unknown2; + uint16 unknown3; + uint8 gvol; /* Global volume */ + uint8 tempo; /* Playback tempo */ + uint16 unknown4; + uint16 unknown5; + uint16 patnum; /* Number of patterns */ + uint16 insnum; /* Number of instruments */ + uint16 ordnum; /* Number of orders */ + uint16 unknown6; /* Flags? */ + uint16 unknown7; /* Version? */ + uint16 unknown8; /* Ffi? */ + uint8 magic2[4]; /* 'SCRM' */ +}; + +struct stx_instrument_header { + uint8 type; /* Instrument type */ + uint8 dosname[13]; /* DOS file name */ + uint16 memseg; /* Pointer to sample data */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint8 vol; /* Volume */ + uint8 rsvd1; /* Reserved */ + uint8 pack; /* Packing type (not used) */ + uint8 flags; /* Loop/stereo/16bit samples flags */ + uint16 c2spd; /* C 4 speed */ + uint16 rsvd2; /* Reserved */ + uint8 rsvd3[4]; /* Reserved */ + uint16 int_gp; /* Internal - GUS pointer */ + uint16 int_512; /* Internal - SB pointer */ + uint32 int_last; /* Internal - SB index */ + uint8 name[28]; /* Instrument name */ + uint8 magic[4]; /* Reserved (for 'SCRS') */ +}; + +static int stx_test(HIO_HANDLE *, char *, const int); +static int stx_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_stx = { + "STMIK 0.2", + stx_test, + stx_load +}; + +static int stx_test(HIO_HANDLE * f, char *t, const int start) +{ + char buf[8]; + + hio_seek(f, start + 20, SEEK_SET); + if (hio_read(buf, 1, 8, f) < 8) + return -1; + if (memcmp(buf, "!Scream!", 8) && memcmp(buf, "BMOD2STM", 8)) + return -1; + + hio_seek(f, start + 60, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (memcmp(buf, "SCRM", 4)) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +#define FX_NONE 0xff + +static const uint8 fx[11] = { + FX_NONE, + FX_SPEED, + FX_JUMP, + FX_BREAK, + FX_VOLSLIDE, + FX_PORTA_DN, + FX_PORTA_UP, + FX_TONEPORTA, + FX_VIBRATO, + FX_TREMOR, + FX_ARPEGGIO +}; + +static int stx_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i, broken = 0; + struct xmp_event *event = 0, dummy; + struct stx_file_header sfh; + struct stx_instrument_header sih; + uint8 n, b; + uint16 x16; + int bmod2stm = 0; + uint16 *pp_ins; /* Parapointers to instruments */ + uint16 *pp_pat; /* Parapointers to patterns */ + + LOAD_INIT(); + + hio_read(sfh.name, 20, 1, f); + hio_read(sfh.magic, 8, 1, f); + sfh.psize = hio_read16l(f); + sfh.unknown1 = hio_read16l(f); + sfh.pp_pat = hio_read16l(f); + sfh.pp_ins = hio_read16l(f); + sfh.pp_chn = hio_read16l(f); + sfh.unknown2 = hio_read16l(f); + sfh.unknown3 = hio_read16l(f); + sfh.gvol = hio_read8(f); + sfh.tempo = hio_read8(f); + sfh.unknown4 = hio_read16l(f); + sfh.unknown5 = hio_read16l(f); + sfh.patnum = hio_read16l(f); + sfh.insnum = hio_read16l(f); + sfh.ordnum = hio_read16l(f); + sfh.unknown6 = hio_read16l(f); + sfh.unknown7 = hio_read16l(f); + sfh.unknown8 = hio_read16l(f); + hio_read(sfh.magic2, 4, 1, f); + + /* Sanity check */ + if (sfh.patnum > 254 || sfh.insnum > MAX_INSTRUMENTS || sfh.ordnum > 256) + return -1; + + /* BMOD2STM does not convert pitch */ + if (!strncmp((char *)sfh.magic, "BMOD2STM", 8)) + bmod2stm = 1; + +#if 0 + if ((strncmp((char *)sfh.magic, "!Scream!", 8) && + !bmod2stm) || strncmp((char *)sfh.magic2, "SCRM", 4)) + return -1; +#endif + + mod->ins = sfh.insnum; + mod->pat = sfh.patnum; + mod->trk = mod->pat * mod->chn; + mod->len = sfh.ordnum; + mod->spd = MSN(sfh.tempo); + mod->smp = mod->ins; + m->c4rate = C4_NTSC_RATE; + + /* STM2STX 1.0 released with STMIK 0.2 converts STMs with the pattern + * length encoded in the first two bytes of the pattern (like S3M). + */ + hio_seek(f, start + (sfh.pp_pat << 4), SEEK_SET); + x16 = hio_read16l(f); + hio_seek(f, start + (x16 << 4), SEEK_SET); + x16 = hio_read16l(f); + if (x16 == sfh.psize) + broken = 1; + + strncpy(mod->name, (char *)sfh.name, 20); + if (bmod2stm) + libxmp_set_type(m, "BMOD2STM STX"); + else + snprintf(mod->type, XMP_NAME_SIZE, "STM2STX 1.%d", broken ? 0 : 1); + + MODULE_INFO(); + + pp_pat = (uint16 *) calloc(mod->pat, sizeof(uint16)); + if (pp_pat == NULL) + goto err; + + pp_ins = (uint16 *) calloc(mod->ins, sizeof(uint16)); + if (pp_ins == NULL) + goto err2; + + /* Read pattern pointers */ + hio_seek(f, start + (sfh.pp_pat << 4), SEEK_SET); + for (i = 0; i < mod->pat; i++) + pp_pat[i] = hio_read16l(f); + + /* Read instrument pointers */ + hio_seek(f, start + (sfh.pp_ins << 4), SEEK_SET); + for (i = 0; i < mod->ins; i++) + pp_ins[i] = hio_read16l(f); + + /* Skip channel table (?) */ + hio_seek(f, start + (sfh.pp_chn << 4) + 32, SEEK_SET); + + /* Read orders */ + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = hio_read8(f); + hio_seek(f, 4, SEEK_CUR); + } + + if (libxmp_init_instrument(m) < 0) + goto err3; + + /* Read and convert instruments and samples */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err3; + + hio_seek(f, start + (pp_ins[i] << 4), SEEK_SET); + + sih.type = hio_read8(f); + hio_read(sih.dosname, 13, 1, f); + sih.memseg = hio_read16l(f); + sih.length = hio_read32l(f); + sih.loopbeg = hio_read32l(f); + sih.loopend = hio_read32l(f); + sih.vol = hio_read8(f); + sih.rsvd1 = hio_read8(f); + sih.pack = hio_read8(f); + sih.flags = hio_read8(f); + sih.c2spd = hio_read16l(f); + sih.rsvd2 = hio_read16l(f); + hio_read(sih.rsvd3, 4, 1, f); + sih.int_gp = hio_read16l(f); + sih.int_512 = hio_read16l(f); + sih.int_last = hio_read32l(f); + hio_read(sih.name, 28, 1, f); + hio_read(sih.magic, 4, 1, f); + if (hio_error(f)) { + D_(D_CRIT "read error at instrument %d", i); + goto err3; + } + + mod->xxs[i].len = sih.length; + mod->xxs[i].lps = sih.loopbeg; + mod->xxs[i].lpe = sih.loopend; + if (mod->xxs[i].lpe == 0xffff) + mod->xxs[i].lpe = 0; + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = sih.vol; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, sih.name, 12); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c V%02x %5d\n", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, sih.c2spd); + + libxmp_c2spd_to_note(sih.c2spd, &mod->xxi[i].sub[0].xpo, + &mod->xxi[i].sub[0].fin); + } + + if (libxmp_init_pattern(mod) < 0) + goto err3; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err3; + + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + (pp_pat[i] << 4), SEEK_SET); + if (broken) + hio_seek(f, 2, SEEK_CUR); + + for (r = 0; r < 64;) { + b = hio_read8(f); + if (hio_error(f)) { + goto err3; + } + + if (b == S3M_EOR) { + r++; + continue; + } + + c = b & S3M_CH_MASK; + event = c >= mod->chn ? &dummy : &EVENT(i, c, r); + + if (b & S3M_NI_FOLLOW) { + n = hio_read8(f); + + switch (n) { + case 255: + n = 0; + break; /* Empty note */ + case 254: + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n = 37 + 12 * MSN(n) + LSN(n); + } + + event->note = n; + event->ins = hio_read8(f);; + } + + if (b & S3M_VOL_FOLLOWS) { + event->vol = hio_read8(f) + 1; + } + + if (b & S3M_FX_FOLLOWS) { + int t = hio_read8(f); + int p = hio_read8(f); + if (t < ARRAY_SIZE(fx)) { + event->fxt = fx[t]; + event->fxp = p; + switch (event->fxt) { + case FX_SPEED: + event->fxp = MSN(event->fxp); + break; + case FX_NONE: + event->fxp = event->fxt = 0; + break; + } + } + } + } + } + + free(pp_ins); + free(pp_pat); + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + goto err; + } + + m->quirk |= QUIRK_VSALL | QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; + +err3: + free(pp_ins); +err2: + free(pp_pat); +err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/sym_load.c b/thirdparty/libxmp/src/loaders/sym_load.c new file mode 100644 index 0000000..3cc062b --- /dev/null +++ b/thirdparty/libxmp/src/loaders/sym_load.c @@ -0,0 +1,632 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "lzw.h" + + +static int sym_test(HIO_HANDLE *, char *, const int); +static int sym_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_sym = { + "Digital Symphony", + sym_test, + sym_load +}; + +static int sym_test(HIO_HANDLE *f, char *t, const int start) +{ + uint32 a, b; + int i, ver; + + a = hio_read32b(f); + b = hio_read32b(f); + + if (a != 0x02011313 || b != 0x1412010B) /* BASSTRAK */ + return -1; + + ver = hio_read8(f); + + /* v1 files are the same as v0 but may contain strange compression + * formats. Deal with that problem later if it arises. + */ + if (ver > 1) { + return -1; + } + + hio_read8(f); /* chn */ + hio_read16l(f); /* pat */ + hio_read16l(f); /* trk */ + hio_read24l(f); /* infolen */ + + for (i = 0; i < 63; i++) { + if (~hio_read8(f) & 0x80) + hio_read24l(f); + } + + libxmp_read_title(f, t, hio_read8(f)); + + return 0; +} + + +static void fix_effect(struct xmp_event *e, int parm) +{ + switch (e->fxt) { + case 0x00: /* 00 xyz Normal play or Arpeggio + Volume Slide Up */ + case 0x01: /* 01 xyy Slide Up + Volume Slide Up */ + case 0x02: /* 02 xyy Slide Down + Volume Slide Up */ + if (parm & 0xff) { + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_UP; + e->f2p = parm >> 8; + } + break; + case 0x03: /* 03 xyy Tone Portamento */ + case 0x04: /* 04 xyz Vibrato */ + case 0x07: /* 07 xyz Tremolo */ + e->fxp = parm; + break; + case 0x05: /* 05 xyz Tone Portamento + Volume Slide */ + case 0x06: /* 06 xyz Vibrato + Volume Slide */ + e->fxp = parm; + if (!parm) + e->fxt -= 2; + break; + case 0x09: /* 09 xxx Set Sample Offset */ + e->fxp = parm >> 1; + e->f2t = FX_HIOFFSET; + e->f2p = parm >> 9; + break; + case 0x0a: /* 0A xyz Volume Slide + Fine Slide Up */ + if (parm & 0xff) { + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + e->f2t = FX_EXTENDED; + e->f2p = (EX_F_PORTA_UP << 4) | ((parm & 0xf00) >> 8); + break; + case 0x0b: /* 0B xxx Position Jump */ + case 0x0c: /* 0C xyy Set Volume */ + e->fxp = parm; + break; + case 0x0d: /* 0D xyy Pattern Break */ + e->fxt = FX_IT_BREAK; + e->fxp = (parm & 0xff) < 0x40 ? parm : 0; + break; + case 0x0f: /* 0F xxx Set Speed */ + if (parm) { + e->fxt = FX_S3M_SPEED; + e->fxp = MIN(parm, 255); + } else { + e->fxt = 0; + } + break; + case 0x13: /* 13 xxy Glissando Control */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_GLISS << 4) | (parm & 0x0f); + break; + case 0x14: /* 14 xxy Set Vibrato Waveform */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_VIBRATO_WF << 4) | (parm & 0x0f); + break; + case 0x15: /* 15 xxy Set Fine Tune */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_FINETUNE << 4) | (parm & 0x0f); + break; + case 0x16: /* 16 xxx Jump to Loop */ + /* TODO: 16, 19 should be able to support larger params. */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_PATTERN_LOOP << 4) | MIN(parm, 0x0f); + break; + case 0x17: /* 17 xxy Set Tremolo Waveform */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_TREMOLO_WF << 4) | (parm & 0x0f); + break; + case 0x19: /* 19 xxx Retrig Note */ + if (parm < 0x10) { + e->fxt = FX_EXTENDED; + e->fxp = (EX_RETRIG << 4) | (parm & 0x0f); + } else { + /* ignore */ + e->fxt = 0; + } + break; + case 0x11: /* 11 xyy Fine Slide Up + Fine Volume Slide Up */ + case 0x12: /* 12 xyy Fine Slide Down + Fine Volume Slide Up */ + case 0x1a: /* 1A xyy Fine Slide Up + Fine Volume Slide Down */ + case 0x1b: /* 1B xyy Fine Slide Down + Fine Volume Slide Down */ + { + uint8 pitch_effect = ((e->fxt == 0x11 || e->fxt == 0x1a) ? + FX_F_PORTA_UP : FX_F_PORTA_DN); + uint8 vol_effect = ((e->fxt == 0x11 || e->fxt == 0x12) ? + FX_F_VSLIDE_UP : FX_F_VSLIDE_DN); + + if (parm & 0xff) { + e->fxt = pitch_effect; + e->fxp = parm & 0xff; + } else + e->fxt = 0; + if (parm >> 8) { + e->f2t = vol_effect; + e->f2p = parm >> 8; + } + break; + } + case 0x1c: /* 1C xxx Note Cut */ + /* TODO: 1c, 1d, 1e should be able to support larger params. */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_CUT << 4) | MIN(parm, 0x0f); + break; + case 0x1d: /* 1D xxx Note Delay */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_DELAY << 4) | MIN(parm, 0x0f); + break; + case 0x1e: /* 1E xxx Pattern Delay */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_PATT_DELAY << 4) | MIN(parm, 0x0f); + break; + case 0x1f: /* 1F xxy Invert Loop */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_INVLOOP << 4) | (parm & 0xf); + break; + case 0x20: /* 20 xyz Normal play or Arpeggio + Volume Slide Down */ + e->fxt = FX_ARPEGGIO; + e->fxp = parm & 0xff; + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_DN; + e->f2p = parm >> 8; + } + break; + case 0x21: /* 21 xyy Slide Up + Volume Slide Down */ + if (parm & 0xff) { + e->fxt = FX_PORTA_UP; + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_DN; + e->f2p = parm >> 8; + } + break; + case 0x22: /* 22 xyy Slide Down + Volume Slide Down */ + if (parm & 0xff) { + e->fxt = FX_PORTA_DN; + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_DN; + e->f2p = parm >> 8; + } + break; + case 0x2a: /* 2A xyz Volume Slide + Fine Slide Down */ + if (parm & 0xff) { + e->fxt = FX_VOLSLIDE; + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + e->f2t = FX_EXTENDED; + e->f2p = (EX_F_PORTA_DN << 4) | (parm >> 8); + break; + case 0x2b: /* 2B xyy Line Jump */ + e->fxt = FX_LINE_JUMP; + e->fxp = (parm < 0x40) ? parm : 0; + break; + case 0x2f: /* 2F xxx Set Tempo */ + if (parm) { + parm = (parm + 4) >> 3; /* round to nearest */ + CLAMP(parm, XMP_MIN_BPM, 255); + e->fxt = FX_S3M_BPM; + e->fxp = parm; + } else { + e->fxt = 0; + } + break; + case 0x30: /* 30 xxy Set Stereo */ + e->fxt = FX_SETPAN; + if (parm & 7) { + /* !Tracker-style panning: 1=left, 4=center, 7=right. */ + if (!(parm & 8)) { + e->fxp = 42 * ((parm & 7) - 1) + 2; + } else { + e->fxt = 0; + } + } else { + parm >>= 4; + if (parm < 128) { + e->fxp = parm + 128; + } else if (parm > 128) { + e->fxp = 255 - parm; + } else { + e->fxt = 0; + } + } + break; + case 0x31: /* 31 xxx Song Upcall */ + e->fxt = 0; + break; + case 0x32: /* 32 xxx Unset Sample Repeat */ + e->fxt = FX_KEYOFF; + e->fxp = 0; + break; + default: + e->fxt = 0; + } +} + +static uint32 readptr32l(uint8 *p) +{ + uint32 a, b, c, d; + + a = *p++; + b = *p++; + c = *p++; + d = *p++; + + return (d << 24) | (c << 16) | (b << 8) | a; +} + +static uint32 readptr16l(uint8 *p) +{ + uint32 a, b; + + a = *p++; + b = *p++; + + return (b << 8) | a; +} + +static int sym_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j; + int /*ver,*/ infolen, sn[64]; + uint32 a, b; + uint8 *buf; + int size, ret; + int tracks_size; + int sequence_size; + int max_sample_size = 1; + uint8 allowed_effects[8]; + + LOAD_INIT(); + + hio_seek(f, 8, SEEK_CUR); /* BASSTRAK */ + + /*ver =*/ hio_read8(f); + libxmp_set_type(m, "Digital Symphony"); + + mod->chn = hio_read8(f); + mod->len = mod->pat = hio_read16l(f); + + /* Sanity check */ + if (mod->chn < 1 || mod->chn > 8 || mod->pat > XMP_MAX_MOD_LENGTH) + return -1; + + mod->trk = hio_read16l(f); /* Symphony patterns are actually tracks */ + infolen = hio_read24l(f); + + /* Sanity check - track 0x1000 is used to indicate the empty track. */ + if (mod->trk > 0x1000) + return -1; + + mod->ins = mod->smp = 63; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sn[i] = hio_read8(f); /* sample name length */ + + if (~sn[i] & 0x80) { + mod->xxs[i].len = hio_read24l(f) << 1; + mod->xxi[i].nsm = 1; + + /* Sanity check */ + if (mod->xxs[i].len > 0x80000) + return -1; + + if (max_sample_size < mod->xxs[i].len) + max_sample_size = mod->xxs[i].len; + } + } + + a = hio_read8(f); /* track name length */ + if (a > 32) { + hio_read(mod->name, 1, 32, f); + hio_seek(f, a - 32, SEEK_SET); + } else { + hio_read(mod->name, 1, a, f); + } + + hio_read(allowed_effects, 1, 8, f); + + MODULE_INFO(); + + mod->trk++; /* alloc extra empty track */ + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Determine the required size of temporary buffer and allocate it now. */ + /* Uncompressed sequence size */ + sequence_size = mod->len * mod->chn * 2; + if (sequence_size > max_sample_size) + max_sample_size = sequence_size; + + tracks_size = 64 * (mod->trk - 1) * 4; /* Uncompressed tracks size */ + if (tracks_size > max_sample_size) + max_sample_size = tracks_size; + + if ((buf = (uint8 *)malloc(max_sample_size)) == NULL) + return -1; + + /* Sequence */ + a = hio_read8(f); /* packing */ + + if (a != 0 && a != 1) + goto err; + + D_(D_INFO "Packed sequence: %s", a ? "yes" : "no"); + + if (a) { + if (libxmp_read_lzw(buf, sequence_size, sequence_size, LZW_FLAGS_SYM, f) < 0) + goto err; + } else { + if (hio_read(buf, 1, sequence_size, f) != sequence_size) + goto err; + } + + for (i = 0; i < mod->len; i++) { /* len == pat */ + if (libxmp_alloc_pattern(mod, i) < 0) + goto err; + + mod->xxp[i]->rows = 64; + + for (j = 0; j < mod->chn; j++) { + int idx = 2 * (i * mod->chn + j); + int t = readptr16l(&buf[idx]); + + if (t == 0x1000) { + /* empty trk */ + t = mod->trk - 1; + } else if (t >= mod->trk - 1) { + /* Sanity check */ + goto err; + } + + mod->xxp[i]->index[j] = t; + } + mod->xxo[i] = i; + } + + /* Read and convert patterns */ + + D_(D_INFO "Stored tracks: %d", mod->trk - 1); + + /* Patterns are stored in blocks of up to 2000 patterns. If there are + * more than 2000 patterns, they need to be read in multiple passes. + * + * See 4096_patterns.dsym. + */ + for (i = 0; i < tracks_size; i += 4 * 64 * 2000) { + int blk_size = MIN(tracks_size - i, 4 * 64 * 2000); + + a = hio_read8(f); + + if (a != 0 && a != 1) + goto err; + + D_(D_INFO "Packed tracks: %s", a ? "yes" : "no"); + + if (a) { + if (libxmp_read_lzw(buf + i, blk_size, blk_size, LZW_FLAGS_SYM, f) < 0) + goto err; + } else { + if (hio_read(buf + i, 1, blk_size, f) != blk_size) + goto err; + } + } + + for (i = 0; i < mod->trk - 1; i++) { + if (libxmp_alloc_track(mod, i, 64) < 0) + goto err; + + for (j = 0; j < mod->xxt[i]->rows; j++) { + int parm; + + event = &mod->xxt[i]->event[j]; + + b = readptr32l(&buf[4 * (i * 64 + j)]); + event->note = b & 0x0000003f; + if (event->note) + event->note += 48; + event->ins = (b & 0x00001fc0) >> 6; + event->fxt = (b & 0x000fc000) >> 14; + parm = (b & 0xfff00000) >> 20; + + if (allowed_effects[event->fxt >> 3] & (1 << (event->fxt & 7))) { + fix_effect(event, parm); + } else { + event->fxt = 0; + } + } + } + + /* Extra track */ + if (libxmp_alloc_track(mod, i, 64) < 0) + goto err; + + /* Load and convert instruments */ + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct extra_sample_data *xtra = &m->xtra[i]; + uint8 namebuf[128]; + + memset(namebuf, 0, sizeof(namebuf)); + hio_read(namebuf, 1, sn[i] & 0x7f, f); + libxmp_instrument_name(mod, i, namebuf, 32); + + if (~sn[i] & 0x80) { + int looplen; + + xtra->sus = hio_read24l(f) << 1; + looplen = hio_read24l(f) << 1; + xtra->sue = xtra->sus + looplen; + if (xtra->sus < xxs->len && xtra->sus < xtra->sue && + xtra->sue <= xxs->len && looplen > 2) + xxs->flg |= XMP_SAMPLE_SLOOP; + xxi->sub[0].vol = hio_read8(f); + xxi->sub[0].pan = 0x80; + /* finetune adjusted comparing DSym and S3M versions + * of "inside out" */ + xxi->sub[0].fin = (int8)(hio_read8(f) << 4); + xxi->sub[0].sid = i; + } + + D_(D_INFO "[%2X] %-22.22s %05x %05x %05x %c V%02x %+03d", + i, xxi->name, xxs->len, + xtra->sus, xtra->sue, + xxs->flg & XMP_SAMPLE_SLOOP ? 'L' : ' ', + xxi->sub[0].vol, xxi->sub[0].fin); + + if (sn[i] & 0x80 || xxs->len == 0) + continue; + + a = hio_read8(f); + + switch (a) { + case 0: /* Signed 8-bit, logarithmic. */ + D_(D_INFO "%27s VIDC", ""); + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, + xxs, NULL); + break; + + case 1: /* LZW compressed signed 8-bit delta, linear. */ + D_(D_INFO "%27s LZW", ""); + size = xxs->len; + + if (libxmp_read_lzw(buf, size, size, LZW_FLAGS_SYM, f) < 0) + goto err; + + ret = libxmp_load_sample(m, NULL, + SAMPLE_FLAG_NOLOAD | SAMPLE_FLAG_DIFF, + xxs, buf); + break; + + case 2: /* Signed 8-bit, linear. */ + D_(D_INFO "%27s 8-bit", ""); + ret = libxmp_load_sample(m, f, 0, xxs, NULL); + break; + + case 3: /* Signed 16-bit, linear. */ + D_(D_INFO "%27s 16-bit", ""); + xxs->flg |= XMP_SAMPLE_16BIT; + ret = libxmp_load_sample(m, f, 0, xxs, NULL); + break; + + case 4: /* Sigma-delta compressed unsigned 8-bit, linear. */ + D_(D_INFO "%27s Sigma-delta", ""); + size = xxs->len; + if (libxmp_read_sigma_delta(buf, size, size, f) < 0) + goto err; + + ret = libxmp_load_sample(m, NULL, + SAMPLE_FLAG_NOLOAD | SAMPLE_FLAG_UNS, + xxs, buf); + break; + + case 5: /* Sigma-delta compressed signed 8-bit, logarithmic. */ + D_(D_INFO "%27s Sigma-delta VIDC", ""); + size = xxs->len; + if (libxmp_read_sigma_delta(buf, size, size, f) < 0) + goto err; + + /* This uses a bit packing that isn't either mu-law or + * normal Archimedes VIDC. Convert to the latter... */ + for (j = 0; j < size; j++) { + uint8 t = (buf[j] < 128) ? ~buf[j] : buf[j]; + buf[j] = (buf[j] >> 7) | (t << 1); + } + + ret = libxmp_load_sample(m, NULL, + SAMPLE_FLAG_NOLOAD | SAMPLE_FLAG_VIDC, + xxs, buf); + break; + + default: + D_(D_CRIT "unknown sample type %d @ %ld\n", a, hio_tell(f)); + goto err; + } + + if (ret < 0) + goto err; + } + + /* Information text */ + if (infolen > 0) { + a = hio_read8(f); /* Packing */ + + m->comment = (char *)malloc(infolen + 1); + if (m->comment) { + m->comment[infolen] = '\0'; + if (a) { + ret = libxmp_read_lzw(m->comment, infolen, infolen, LZW_FLAGS_SYM, f); + } else { + size = hio_read(m->comment, 1, infolen, f); + ret = (size < infolen) ? -1 : 0; + } + if (ret < 0) { + free(m->comment); + m->comment = NULL; + } + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff); + } + + m->quirk = QUIRK_VIBALL | QUIRK_KEYOFF | QUIRK_INVLOOP; + + free(buf); + return 0; + +err: + free(buf); + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/ult_load.c b/thirdparty/libxmp/src/loaders/ult_load.c new file mode 100644 index 0000000..2ed88d7 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ult_load.c @@ -0,0 +1,364 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the format description by FreeJack of The Elven Nation + * + * The loader recognizes four subformats: + * - MAS_UTrack_V001: Ultra Tracker version < 1.4 + * - MAS_UTrack_V002: Ultra Tracker version 1.4 + * - MAS_UTrack_V003: Ultra Tracker version 1.5 + * - MAS_UTrack_V004: Ultra Tracker version 1.6 + */ + +#include "loader.h" +#include "../period.h" + + +static int ult_test (HIO_HANDLE *, char *, const int); +static int ult_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ult = { + "Ultra Tracker", + ult_test, + ult_load +}; + +static int ult_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[15]; + + if (hio_read(buf, 1, 15, f) < 15) + return -1; + + if (memcmp(buf, "MAS_UTrack_V00", 14)) + return -1; + + if (buf[14] < '1' || buf[14] > '4') + return -1; + + libxmp_read_title(f, t, 32); + + return 0; +} + + +struct ult_header { + uint8 magic[15]; /* 'MAS_UTrack_V00x' */ + uint8 name[32]; /* Song name */ + uint8 msgsize; /* ver < 1.4: zero */ +}; + +struct ult_header2 { + uint8 order[256]; /* Orders */ + uint8 channels; /* Number of channels - 1 */ + uint8 patterns; /* Number of patterns - 1 */ +}; + +struct ult_instrument { + uint8 name[32]; /* Instrument name */ + uint8 dosname[12]; /* DOS file name */ + uint32 loop_start; /* Loop start */ + uint32 loopend; /* Loop end */ + uint32 sizestart; /* Sample size is sizeend - sizestart */ + uint32 sizeend; + uint8 volume; /* Volume (log; ver >= 1.4 linear) */ + uint8 bidiloop; /* Sample loop flags */ + int16 finetune; /* Finetune */ + uint16 c2spd; /* C2 frequency */ +}; + +struct ult_event { + /* uint8 note; */ + uint8 ins; + uint8 fxt; /* MSN = fxt, LSN = f2t */ + uint8 f2p; /* Secondary comes first -- little endian! */ + uint8 fxp; +}; + + +static int ult_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k, ver, cnt; + struct xmp_event *event; + struct ult_header ufh; + struct ult_header2 ufh2; + struct ult_instrument uih; + struct ult_event ue; + const char *verstr[4] = { "< 1.4", "1.4", "1.5", "1.6" }; + + uint8 x8; + + LOAD_INIT(); + + hio_read(ufh.magic, 15, 1, f); + hio_read(ufh.name, 32, 1, f); + ufh.msgsize = hio_read8(f); + + ver = ufh.magic[14] - '0'; + + strncpy(mod->name, (char *)ufh.name, 32); + mod->name[32] = '\0'; + libxmp_set_type(m, "Ultra Tracker %s ULT V%03d", verstr[ver - 1], ver); + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + hio_seek(f, ufh.msgsize * 32, SEEK_CUR); + + mod->ins = mod->smp = hio_read8(f); + /* mod->flg |= XXM_FLG_LINEAR; */ + + /* Read and convert instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read(uih.name, 32, 1, f); + hio_read(uih.dosname, 12, 1, f); + uih.loop_start = hio_read32l(f); + uih.loopend = hio_read32l(f); + uih.sizestart = hio_read32l(f); + uih.sizeend = hio_read32l(f); + uih.volume = hio_read8(f); + uih.bidiloop = hio_read8(f); + uih.c2spd = (ver >= 4) ? hio_read16l(f) : 0; /* Incorrect in ult_form.txt */ + uih.finetune = hio_read16l(f); + if (hio_error(f)) { + D_(D_CRIT "read error at instrument %d", i); + return -1; + } + + /* Sanity check: + * "[SizeStart] seems to tell UT how to load the sample into the GUS's + * onboard memory." The maximum supported GUS RAM is 16 MB (PnP). + * Samples also can't cross 256k boundaries. In practice it seems like + * nothing ever goes over 1 MB, the maximum on most GUS cards. + */ + if (uih.sizestart > uih.sizeend || uih.sizeend > (16 << 20) || + uih.sizeend - uih.sizestart > (256 << 10)) { + D_(D_CRIT "invalid sample %d sizestart/sizeend", i); + return -1; + } + + mod->xxs[i].len = uih.sizeend - uih.sizestart; + mod->xxs[i].lps = uih.loop_start; + mod->xxs[i].lpe = uih.loopend; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + /* BiDi Loop : (Bidirectional Loop) + * + * UT takes advantage of the Gus's ability to loop a sample in + * several different ways. By setting the Bidi Loop, the sample can + * be played forward or backwards, looped or not looped. The Bidi + * variable also tracks the sample resolution (8 or 16 bit). + * + * The following table shows the possible values of the Bidi Loop. + * Bidi = 0 : No looping, forward playback, 8bit sample + * Bidi = 4 : No Looping, forward playback, 16bit sample + * Bidi = 8 : Loop Sample, forward playback, 8bit sample + * Bidi = 12 : Loop Sample, forward playback, 16bit sample + * Bidi = 24 : Loop Sample, reverse playback 8bit sample + * Bidi = 28 : Loop Sample, reverse playback, 16bit sample + */ + + /* Claudio's note: I'm ignoring reverse playback for samples */ + + switch (uih.bidiloop) { + case 20: /* Type 20 is in seasons.ult */ + case 4: + mod->xxs[i].flg = XMP_SAMPLE_16BIT; + break; + case 8: + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + break; + case 12: + mod->xxs[i].flg = XMP_SAMPLE_16BIT | XMP_SAMPLE_LOOP; + break; + case 24: + mod->xxs[i].flg = XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_REVERSE; + break; + case 28: + mod->xxs[i].flg = XMP_SAMPLE_16BIT | XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_REVERSE; + break; + } + +/* TODO: Add logarithmic volume support */ + mod->xxi[i].sub[0].vol = uih.volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + libxmp_instrument_name(mod, i, uih.name, 24); + + D_(D_INFO "[%2X] %-32.32s %05x%c%05x %05x %c V%02x F%04x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, uih.finetune, uih.c2spd); + + if (ver > 3) + libxmp_c2spd_to_note(uih.c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + } + + hio_read(ufh2.order, 256, 1, f); + ufh2.channels = hio_read8(f); + ufh2.patterns = hio_read8(f); + + if (hio_error(f)) { + return -1; + } + + for (i = 0; i < 256; i++) { + if (ufh2.order[i] == 0xff) + break; + mod->xxo[i] = ufh2.order[i]; + } + mod->len = i; + mod->chn = ufh2.channels + 1; + mod->pat = ufh2.patterns + 1; + mod->spd = 6; + mod->bpm = 125; + mod->trk = mod->chn * mod->pat; + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + + for (i = 0; i < mod->chn; i++) { + if (ver >= 3) { + x8 = hio_read8(f); + mod->xxc[i].pan = 255 * x8 / 15; + } else { + mod->xxc[i].pan = DEFPAN((((i + 1) / 2) % 2) * 0xff); /* ??? */ + } + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* Events are stored by channel */ + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) { + for (j = 0; j < 64 * mod->pat; ) { + cnt = 1; + x8 = hio_read8(f); /* Read note or repeat code (0xfc) */ + if (x8 == 0xfc) { + cnt = hio_read8(f); /* Read repeat count */ + x8 = hio_read8(f); /* Read note */ + } + if (hio_read(&ue, 1, 4, f) < 4) { /* Read rest of the event */ + D_(D_CRIT "read error at channel %d pos %d", i, j); + return -1; + } + + if (cnt == 0) + cnt++; + + if (j + cnt > 64 * mod->pat) { + D_(D_WARN "invalid track data packing"); + return -1; + } + + for (k = 0; k < cnt; k++, j++) { + event = &EVENT (j >> 6, i , j & 0x3f); + memset(event, 0, sizeof (struct xmp_event)); + if (x8) + event->note = x8 + 36; + event->ins = ue.ins; + event->fxt = MSN (ue.fxt); + event->f2t = LSN (ue.fxt); + event->fxp = ue.fxp; + event->f2p = ue.f2p; + + switch (event->fxt) { + case 0x03: /* Tone portamento */ + event->fxt = FX_ULT_TPORTA; + break; + case 0x05: /* 'Special' effect */ + case 0x06: /* Reserved */ + event->fxt = event->fxp = 0; + break; + case 0x0b: /* Pan */ + event->fxt = FX_SETPAN; + event->fxp <<= 4; + break; + case 0x09: /* Sample offset */ +/* TODO: fine sample offset */ + event->fxp <<= 2; + break; + } + + switch (event->f2t) { + case 0x03: /* Tone portamento */ + event->f2t = FX_ULT_TPORTA; + break; + case 0x05: /* 'Special' effect */ + case 0x06: /* Reserved */ + event->f2t = event->f2p = 0; + break; + case 0x0b: /* Pan */ + event->f2t = FX_SETPAN; + event->f2p <<= 4; + break; + case 0x09: /* Sample offset */ +/* TODO: fine sample offset */ + event->f2p <<= 2; + break; + } + + } + } + } + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (!mod->xxs[i].len) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->volbase = 0x100; + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/umx_load.c b/thirdparty/libxmp/src/loaders/umx_load.c new file mode 100644 index 0000000..20e91aa --- /dev/null +++ b/thirdparty/libxmp/src/loaders/umx_load.c @@ -0,0 +1,422 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include /* offsetof() */ +#include "loader.h" + +static int umx_test (HIO_HANDLE *, char *, const int); +static int umx_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_umx = { + "Epic Games UMX", + umx_test, + umx_load +}; + +/* UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3 + * by Andy Ward , with additional fixes & updates + * by O. Sezer - see git repo at https://github.com/sezero/umr.git + */ +typedef int32 fci_t; /* FCompactIndex */ + +#define UPKG_HDR_TAG 0x9e2a83c1 + +struct _genhist { /* for upkg versions >= 68 */ + int32 export_count; + int32 name_count; +}; + +struct upkg_hdr { + uint32 tag; /* UPKG_HDR_TAG */ + int32 file_version; + uint32 pkg_flags; + int32 name_count; /* number of names in name table (>= 0) */ + int32 name_offset; /* offset to name table (>= 0) */ + int32 export_count; /* num. exports in export table (>= 0) */ + int32 export_offset; /* offset to export table (>= 0) */ + int32 import_count; /* num. imports in export table (>= 0) */ + int32 import_offset; /* offset to import table (>= 0) */ + + /* number of GUIDs in heritage table (>= 1) and table's offset: + * only with versions < 68. */ + int32 heritage_count; + int32 heritage_offset; + /* with versions >= 68: a GUID, a dword for generation count + * and export_count and name_count dwords for each generation: */ + uint32 guid[4]; + int32 generation_count; +#define UPKG_HDR_SIZE 64 /* 64 bytes up until here */ + struct _genhist *gen; +}; +/* compile time assert for upkg_hdr size */ +typedef int _check_hdrsize[2 * (offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE) - 1]; + +#define UMUSIC_IT 0 +#define UMUSIC_S3M 1 +#define UMUSIC_XM 2 +#define UMUSIC_MOD 3 +#define UMUSIC_WAV 4 +#define UMUSIC_MP2 5 + +static const char *mustype[] = { + "IT", "S3M", "XM", "MOD", + "WAV", "MP2", NULL +}; + +/* decode an FCompactIndex. + * original documentation by Tim Sweeney was at + * http://unreal.epicgames.com/Packages.htm + * also see Unreal Wiki: + * http://wiki.beyondunreal.com/Legacy:Package_File_Format/Data_Details + */ +static fci_t get_fci (const char *in, int *pos) +{ + int32 a; + int size; + + size = 1; + a = in[0] & 0x3f; + + if (in[0] & 0x40) { + size++; + a |= (in[1] & 0x7f) << 6; + + if (in[1] & 0x80) { + size++; + a |= (in[2] & 0x7f) << 13; + + if (in[2] & 0x80) { + size++; + a |= (in[3] & 0x7f) << 20; + + if (in[3] & 0x80) { + size++; + a |= (in[4] & 0x3f) << 27; + } + } + } + } + + if (in[0] & 0x80) + a = -a; + + *pos += size; + + return a; +} + +static int get_objtype (HIO_HANDLE *f, int32 ofs, int type) +{ + char sig[16]; +_retry: + memset(sig, 0, sizeof(sig)); + hio_seek(f, ofs, SEEK_SET); + hio_read(sig, 16, 1, f); + if (type == UMUSIC_IT) { + if (memcmp(sig, "IMPM", 4) == 0) + return UMUSIC_IT; + return -1; + } + if (type == UMUSIC_XM) { + if (memcmp(sig, "Extended Module:", 16) != 0) + return -1; + hio_read(sig, 16, 1, f); + if (sig[0] != ' ') return -1; + hio_read(sig, 16, 1, f); + if (sig[5] != 0x1a) return -1; + return UMUSIC_XM; + } + if (type == UMUSIC_MP2) { + unsigned char *p = (unsigned char *)sig; + uint16 u = ((p[0] << 8) | p[1]) & 0xFFFE; + if (u == 0xFFFC || u == 0xFFF4) + return UMUSIC_MP2; + return -1; + } + if (type == UMUSIC_WAV) { + if (memcmp(sig, "RIFF", 4) == 0 && memcmp(&sig[8], "WAVE", 4) == 0) + return UMUSIC_WAV; + return -1; + } + + hio_seek(f, ofs + 44, SEEK_SET); + hio_read(sig, 4, 1, f); + if (type == UMUSIC_S3M) { + if (memcmp(sig, "SCRM", 4) == 0) + return UMUSIC_S3M; + /*return -1;*/ + /* SpaceMarines.umx and Starseek.umx from Return to NaPali + * report as "s3m" whereas the actual music format is "it" */ + type = UMUSIC_IT; + goto _retry; + } + + hio_seek(f, ofs + 1080, SEEK_SET); + hio_read(sig, 4, 1, f); + if (type == UMUSIC_MOD) { + if (memcmp(sig, "M.K.", 4) == 0 || memcmp(sig, "M!K!", 4) == 0) + return UMUSIC_MOD; + return -1; + } + + return -1; +} + +static int read_export (HIO_HANDLE *f, const struct upkg_hdr *hdr, + int32 *ofs, int32 *objsize) +{ + char buf[40]; + int idx = 0, t; + + hio_seek(f, *ofs, SEEK_SET); + if (hio_read(buf, 4, 10, f) < 10) + return -1; + + if (hdr->file_version < 40) idx += 8; /* 00 00 00 00 00 00 00 00 */ + if (hdr->file_version < 60) idx += 16; /* 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 */ + get_fci(&buf[idx], &idx); /* skip junk */ + t = get_fci(&buf[idx], &idx); /* type_name */ + if (hdr->file_version > 61) idx += 4; /* skip export size */ + *objsize = get_fci(&buf[idx], &idx); + *ofs += idx; /* offset for real data */ + + return t; /* return type_name index */ +} + +static int read_typname(HIO_HANDLE *f, const struct upkg_hdr *hdr, + int idx, char *out) +{ + int i, s; + long l; + char buf[64]; + + if (idx >= hdr->name_count) return -1; + memset(buf, 0, 64); + for (i = 0, l = 0; i <= idx; i++) { + if (hio_seek(f, hdr->name_offset + l, SEEK_SET) < 0) return -1; + if (!hio_read(buf, 1, 63, f)) return -1; + if (hdr->file_version >= 64) { + s = *(signed char *)buf; /* numchars *including* terminator */ + if (s <= 0) return -1; + l += s + 5; /* 1 for buf[0], 4 for int32 name_flags */ + } else { + l += (long)strlen(buf); + l += 5; /* 1 for terminator, 4 for int32 name_flags */ + } + } + + strcpy(out, (hdr->file_version >= 64)? &buf[1] : buf); + return 0; +} + +static void umx_strupr(char *str) +{ + while (*str) { + if (*str >= 'a' && *str <= 'z') { + *str -= ('a' - 'A'); + } + str++; + } +} + +static int probe_umx (HIO_HANDLE *f, const struct upkg_hdr *hdr, + int32 *ofs, int32 *objsize) +{ + int i, idx, t; + int32 s, pos; + long fsiz; + char buf[64]; + + idx = 0; + fsiz = hio_size(f); + + if (hdr->name_offset >= fsiz || + hdr->export_offset >= fsiz || + hdr->import_offset >= fsiz) { + D_(D_INFO "UMX: Illegal values in header.\n"); + return -1; + } + + /* Find the offset and size of the first IT, S3M or XM + * by parsing the exports table. The umx files should + * have only one export. Kran32.umx from Unreal has two, + * but both pointing to the same music. */ + if (hdr->export_offset >= fsiz) return -1; + memset(buf, 0, 64); + hio_seek(f, hdr->export_offset, SEEK_SET); + hio_read(buf, 1, 64, f); + + get_fci(&buf[idx], &idx); /* skip class_index */ + get_fci(&buf[idx], &idx); /* skip super_index */ + if (hdr->file_version >= 60) idx += 4; /* skip int32 package_index */ + get_fci(&buf[idx], &idx); /* skip object_name */ + idx += 4; /* skip int32 object_flags */ + + s = get_fci(&buf[idx], &idx); /* get serial_size */ + if (s <= 0) return -1; + pos = get_fci(&buf[idx],&idx); /* get serial_offset */ + if (pos < 0 || pos > fsiz - 40) return -1; + + if ((t = read_export(f, hdr, &pos, &s)) < 0) return -1; + if (s <= 0 || s > fsiz - pos) return -1; + + if (read_typname(f, hdr, t, buf) < 0) return -1; + umx_strupr(buf); + for (i = 0; mustype[i] != NULL; i++) { + if (!strcmp(buf, mustype[i])) { + t = i; + break; + } + } + if (mustype[i] == NULL) return -1; + if ((t = get_objtype(f, pos, t)) < 0) return -1; + + *ofs = pos; + *objsize = s; + return t; +} + +static int32 probe_header (HIO_HANDLE *f, struct upkg_hdr *hdr) +{ + hdr->tag = hio_read32l(f); + hdr->file_version = (int32) hio_read32l(f); + hdr->pkg_flags = hio_read32l(f); + hdr->name_count = (int32) hio_read32l(f); + hdr->name_offset = (int32) hio_read32l(f); + hdr->export_count = (int32) hio_read32l(f); + hdr->export_offset = (int32) hio_read32l(f); + hdr->import_count = (int32) hio_read32l(f); + hdr->import_offset = (int32) hio_read32l(f); + + if (hdr->tag != UPKG_HDR_TAG) { + D_(D_INFO "UMX: Unknown header tag 0x%x\n", hdr->tag); + return -1; + } + if (hdr->name_count < 0 || + hdr->export_count < 0 || + hdr->import_count < 0 || + hdr->name_offset < 36 || + hdr->export_offset < 36 || + hdr->import_offset < 36) { + D_(D_INFO "UMX: Illegal values in header.\n"); + return -1; + } + +#if 1 /* no need being overzealous */ + return 0; +#else + switch (hdr->file_version) { + case 35: case 37: /* Unreal beta - */ + case 40: case 41: /* 1998 */ + case 61:/* Unreal */ + case 62:/* Unreal Tournament */ + case 63:/* Return to NaPali */ + case 64:/* Unreal Tournament */ + case 66:/* Unreal Tournament */ + case 68:/* Unreal Tournament */ + case 69:/* Tactical Ops */ + case 75:/* Harry Potter and the Philosopher's Stone */ + case 76: /* mpeg layer II data */ + case 83:/* Mobile Forces */ + return 0; + } + + D_(D_INFO "UMX: Unknown upkg version %d\n", hdr->file_version); + return -1; +#endif /* #if 0 */ +} + +static int process_upkg (HIO_HANDLE *f, int32 *ofs, int32 *objsize) +{ + struct upkg_hdr header; + + memset(&header, 0, sizeof(header)); + if (probe_header(f, &header) < 0) return -1; + return probe_umx(f, &header, ofs, objsize); +} + +static int umx_test(HIO_HANDLE *f, char *t, const int start) +{ + int32 ofs, size; + int type; + + type = process_upkg(f, &ofs, &size); + (void) hio_error(f); + if (type < 0) { + return -1; + } + + ofs += start; /** FIXME? **/ + switch (type) { + case UMUSIC_IT: + hio_seek(f, ofs + 4, SEEK_SET); + libxmp_read_title(f, t, 26); + return 0; + case UMUSIC_S3M: + hio_seek(f, ofs, SEEK_SET); + libxmp_read_title(f, t, 28); + return 0; + case UMUSIC_XM: + hio_seek(f, ofs + 17, SEEK_SET); + libxmp_read_title(f, t, 20); + return 0; + case UMUSIC_MOD: + hio_seek(f, ofs, SEEK_SET); + libxmp_read_title(f, t, 20); + return 0; + } + + return -1; +} + +static int umx_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + int32 ofs, size; + int type; + + LOAD_INIT(); + + D_(D_INFO "Container type : Epic Games UMX"); + + type = process_upkg(f, &ofs, &size); + (void) hio_error(f); + if (type < 0) { + return -1; + } + + D_(D_INFO "UMX: %s data @ 0x%x, %d bytes\n", mustype[type], ofs, size); + + ofs += start; /** FIXME? **/ + hio_seek(f, ofs, SEEK_SET); + switch (type) { + case UMUSIC_IT: + return libxmp_loader_it.loader(m, f, ofs); + case UMUSIC_S3M: + return libxmp_loader_s3m.loader(m, f, ofs); + case UMUSIC_XM: + return libxmp_loader_xm.loader(m, f, ofs); + case UMUSIC_MOD: + return libxmp_loader_mod.loader(m, f, ofs); + } + + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/voltable.c b/thirdparty/libxmp/src/loaders/voltable.c new file mode 100644 index 0000000..d997a5d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/voltable.c @@ -0,0 +1,120 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +/* +from Tom Hargreaves +date Sat, Jan 16, 2010 at 9:38 PM + +the volume table for volume commands and the VIDC lookup table for +sample conversion are (should be) one and the same. A full-precision +version of the table is as follows: + +0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 +48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 +112 120 128 136 144 152 160 168 176 184 192 200 208 216 224 232 +240 256 272 288 304 320 336 352 368 384 400 416 432 448 464 480 +496 528 560 592 624 656 688 720 752 784 816 848 880 912 944 976 +1008 1072 1136 1200 1264 1328 1392 1456 1520 1584 1648 1712 1776 1840 1904 1968 +2032 2160 2288 2416 2544 2672 2800 2928 3056 3184 3312 3440 3568 3696 3824 3952 */ + + +/* Claudio's note: this is a curve approximation using linear segments, + * so I'll perform linear interpolation to have all 256 values + */ + +int const libxmp_arch_vol_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, + 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, + 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1a, + 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x40, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4b, 0x4d, + 0x4f, 0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, + 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, + 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, + 0x81, 0x83, 0x87, 0x8b, 0x8f, 0x93, 0x97, 0x9b, + 0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc, + 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd5, 0xd9, 0xdd, + 0xe2, 0xe6, 0xea, 0xee, 0xf2, 0xf6, 0xfa, 0xff, + 0xff +}; + +#if 0 +int arch_vol_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, + 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, + 0x16, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, + 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1e, 0x1e, 0x1f, + 0x20, 0x20, 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3e, 0x3f, + 0x40, 0x42, 0x43, 0x45, 0x46, 0x48, 0x49, 0x4b, + 0x4d, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, + 0x5b, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6b, + 0x6d, 0x6f, 0x72, 0x74, 0x77, 0x7a, 0x7c, 0x7f, + 0x82, 0x85, 0x88, 0x8b, 0x8e, 0x91, 0x94, 0x97, + 0x9b, 0x9e, 0xa1, 0xa5, 0xa9, 0xac, 0xb0, 0xb4, + 0xb8, 0xbc, 0xc0, 0xc4, 0xc9, 0xcd, 0xd2, 0xd6, + 0xdb, 0xe0, 0xe5, 0xea, 0xef, 0xf4, 0xfa, 0xff, + 0xff +}; +#endif + diff --git a/thirdparty/libxmp/src/loaders/vorbis.c b/thirdparty/libxmp/src/loaders/vorbis.c new file mode 100644 index 0000000..2778207 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/vorbis.c @@ -0,0 +1,5676 @@ +// Ogg Vorbis audio decoder - v1.22 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster github:alxprd +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// github:manxorist Saga Musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier +// +// Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +/* libxmp customizations: */ +#define STB_VORBIS_C +#include "vorbis.h" + +#ifdef _MSC_VER +#pragma warning(disable:4456) /* shadowing (hides previous local decl) */ +#pragma warning(disable:4457) /* shadowing (hides function parameter.) */ +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +#ifndef STB_VORBIS_NO_COMMENTS +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); +#endif + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +#ifndef STB_VORBIS_NO_SEEK_API +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) +#endif + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +#ifndef STB_VORBIS_NO_FLOAT_CONVERSION +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + +// STB_VORBIS_NO_COMMENTS +// disables reading and storing user comments +// #define STB_VORBIS_NO_COMMENTS + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(p) ((void) 0) + #define realloc(p, s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifndef STB_FORCEINLINE + #if defined(_MSC_VER) + #define STB_FORCEINLINE __forceinline + #elif (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2))) || defined(__clang__) + #define STB_FORCEINLINE static __inline __attribute__((always_inline)) + #else + #define STB_FORCEINLINE static inline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) do {} while(0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +/* libxmp-specific change */ +#if 1 +#include "../common.h" +#else +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +#endif + +#ifdef __has_feature +#if __has_feature(undefined_behavior_sanitizer) +#define HAS_UBSAN +#endif +#endif +#ifdef HAS_UBSAN +#define STB_NO_SANITIZE(s) __attribute__((no_sanitize(s))) +#else +#define STB_NO_SANITIZE(s) +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#elif defined(__VBCC__) +#define STBV_NOTUSED(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + // libxmp hack: https://github.com/nothings/stb/pull/1312 + MappingChannel *chan; + uint16 coupling_steps; + uint8 submaps; + uint8 submap_floor[16]; // varies + uint8 submap_residue[16]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + +#ifndef STB_VORBIS_NO_COMMENTS + char *vendor; + int comment_list_length; + char **comment_list; +#endif + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; + + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; + + // libxmp hack: decode work buffer (used in inverse_mdct and decode_residues) + void *work_buffer; + + // temporary buffers + void *temp_lengths; + void *temp_codewords; + void *temp_values; + void *temp_mults; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : f->work_buffer) +#define temp_free(f,p) do {} while (0) +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + if (sz <= 0) return NULL; /* libxmp hack: https://github.com/nothings/stb/issues/1248 */ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? calloc(sz, 1) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + if (sz <= 0) return NULL; /* libxmp hack: https://github.com/nothings/stb/issues/1248 */ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return calloc(sz, 1); +} + +static void setup_temp_free(vorb *f, void **_p, int sz) +{ + void *p = *_p; + *_p = NULL; + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+7)&~7; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +STB_FORCEINLINE uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, (int)exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +// +// libxmp hack: suppress UBSan error caused by invalid input data. +// Reported upstream: https://github.com/nothings/stb/issues/1168. +STB_NO_SANITIZE("float-cast-overflow") +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif + return 0; /* silence warnings */ +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif + return 0; /* silence warnings */ +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif + return 0; /* silence warnings */ +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +#ifndef STB_VORBIS_NO_COMMENTS +static int get32_packet(vorb *f) +{ + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} +#endif + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + + assert(f->valid_bits >= n); + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +STB_FORCEINLINE void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + /* libxmp hack: unsigned left shift for 32-bit codewords. + * https://github.com/nothings/stb/issues/1168 */ + if (c->codewords[i] == (f->acc & ((1U << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +STB_FORCEINLINE void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y&255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y&255]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch > 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +STB_FORCEINLINE void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + float l00,l11; + + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; + + k00 = z[ -4] - z[-12]; + k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + STBV_NOTUSED(left_end); + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) return 0; + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + f->first_decode = TRUE; + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + +#ifndef STB_VORBIS_NO_COMMENTS + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; +#endif // STB_VORBIS_NO_COMMENTS + + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + + if (c->sparse) { + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + f->temp_lengths = lengths; + } + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) return error(f, VORBIS_invalid_setup); + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, &f->temp_lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + f->temp_codewords = c->codewords; + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + f->temp_values = values; + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, &f->temp_values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, &f->temp_codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, &f->temp_lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; + } else { + /* libxmp hack: unsigned multiply to suppress (legitimate) warning. + * https://github.com/nothings/stb/issues/1168 */ + c->lookup_values = (unsigned)c->entries * (unsigned)c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + f->temp_mults = mults; + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (f->valid_bits < 0) return error(f, VORBIS_invalid_setup); + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) return error(f, VORBIS_outofmem); + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, &f->temp_mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low = 0,hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } else { + f->work_buffer = setup_malloc(f, f->temp_memory_required); + if (f->work_buffer == NULL) return error(f, VORBIS_outofmem); + } + + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + +#ifndef STB_VORBIS_NO_COMMENTS + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); +#endif + + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + if (c->codewords != p->temp_codewords) // Might be the temporary buffer-allocated array. + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + if (!p->alloc.alloc_buffer) { + setup_free(p, p->work_buffer); + setup_temp_free(p, &p->temp_lengths, 0); + setup_temp_free(p, &p->temp_codewords, 0); + setup_temp_free(p, &p->temp_values, 0); + setup_temp_free(p, &p->temp_mults, 0); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes &= ~7; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +#ifndef STB_VORBIS_NO_COMMENTS +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} +#endif + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + vorbis_deinit(&p); + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif + return 0; /* silence warnings */ +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +#ifndef STB_VORBIS_NO_SEEK_API +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + if (!getn(f, header, 27)) + return 0; + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + if (!getn(f, lacing, header[26])) + return 0; + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe; + uint32 end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + last_sample_limit = 0; + else + last_sample_limit = sample_number - padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + } + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} +#endif /* STB_VORBIS_NO_SEEK_API */ + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + uint32 end,last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + // libxmp hack: changed this to unsigned to suppress a UBSan error. + // Reported upstream: https://github.com/nothings/stb/issues/1168. + unsigned int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (int)(temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +// libxmp hack: replaced signed overflow clamps with unsigned overflow (UBSan). +// Reported upstream: https://github.com/nothings/stb/issues/1168. + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output = NULL; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +#ifndef STB_VORBIS_NO_FLOAT_CONVERSION +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_FLOAT_CONVERSION +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/libxmp/src/loaders/vorbis.h b/thirdparty/libxmp/src/loaders/vorbis.h new file mode 100644 index 0000000..1ca02ec --- /dev/null +++ b/thirdparty/libxmp/src/loaders/vorbis.h @@ -0,0 +1,46 @@ +#ifndef DEBUG /* not a debug build */ +#ifndef NDEBUG +#define NDEBUG /* disable assert()s */ +#endif +#endif + +#define STB_VORBIS_NO_PUSHDATA_API +#define STB_VORBIS_NO_STDIO +#define STB_VORBIS_NO_COMMENTS +#define STB_VORBIS_NO_SEEK_API +#define STB_VORBIS_NO_FLOAT_CONVERSION + +/* change namespace from stb_ to libxmp_ for public functions: */ +#define stb_vorbis_get_info libxmp_vorbis_get_info +#define stb_vorbis_get_comment libxmp_vorbis_get_comment +#define stb_vorbis_get_error libxmp_vorbis_get_error +#define stb_vorbis_close libxmp_vorbis_close +#define stb_vorbis_get_sample_offset libxmp_vorbis_get_sample_offset +#define stb_vorbis_get_file_offset libxmp_vorbis_get_file_offset +#define stb_vorbis_open_pushdata libxmp_vorbis_open_pushdata +#define stb_vorbis_decode_frame_pushdata libxmp_vorbis_decode_frame_pushdata +#define stb_vorbis_flush_pushdata libxmp_vorbis_flush_pushdata +#define stb_vorbis_decode_filename libxmp_vorbis_decode_filename +#define stb_vorbis_decode_memory libxmp_vorbis_decode_memory +#define stb_vorbis_open_memory libxmp_vorbis_open_memory +#define stb_vorbis_open_filename libxmp_vorbis_open_filename +#define stb_vorbis_open_file libxmp_vorbis_open_file +#define stb_vorbis_open_file_section libxmp_vorbis_open_file_section +#define stb_vorbis_seek_frame libxmp_vorbis_seek_frame +#define stb_vorbis_seek libxmp_vorbis_seek +#define stb_vorbis_seek_start libxmp_vorbis_seek_start +#define stb_vorbis_stream_length_in_samples libxmp_vorbis_stream_length_in_samples +#define stb_vorbis_stream_length_in_seconds libxmp_vorbis_stream_length_in_seconds +#define stb_vorbis_get_frame_float libxmp_vorbis_get_frame_float +#define stb_vorbis_get_frame_short_interleaved libxmp_vorbis_get_frame_short_interleaved +#define stb_vorbis_get_frame_short libxmp_vorbis_get_frame_short +#define stb_vorbis_get_samples_float_interleaved libxmp_vorbis_get_samples_float_interleaved +#define stb_vorbis_get_samples_float libxmp_vorbis_get_samples_float +#define stb_vorbis_get_samples_short_interleaved libxmp_vorbis_get_samples_short_interleaved +#define stb_vorbis_get_samples_short libxmp_vorbis_get_samples_short + +#ifndef STB_VORBIS_C +/* client: */ +#define STB_VORBIS_HEADER_ONLY +#include "vorbis.c" +#endif diff --git a/thirdparty/libxmp/src/loaders/xm.h b/thirdparty/libxmp/src/loaders/xm.h new file mode 100644 index 0000000..4d41486 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/xm.h @@ -0,0 +1,101 @@ +#ifndef LIBXMP_LOADERS_XM_H +#define LIBXMP_LOADERS_XM_H + +#define XM_EVENT_PACKING 0x80 +#define XM_EVENT_PACK_MASK 0x7f +#define XM_EVENT_NOTE_FOLLOWS 0x01 +#define XM_EVENT_INSTRUMENT_FOLLOWS 0x02 +#define XM_EVENT_VOLUME_FOLLOWS 0x04 +#define XM_EVENT_FXTYPE_FOLLOWS 0x08 +#define XM_EVENT_FXPARM_FOLLOWS 0x10 +#define XM_LINEAR_FREQ 0x01 +#define XM_LOOP_MASK 0x03 +#define XM_LOOP_NONE 0 +#define XM_LOOP_FORWARD 1 +#define XM_LOOP_PINGPONG 2 +#define XM_SAMPLE_16BIT 0x10 +#define XM_SAMPLE_STEREO 0x20 +#define XM_ENVELOPE_ON 0x01 +#define XM_ENVELOPE_SUSTAIN 0x02 +#define XM_ENVELOPE_LOOP 0x04 +#define XM_LINEAR_PERIOD_MODE 0x01 + +struct xm_file_header { + uint8 id[17]; /* ID text: "Extended module: " */ + uint8 name[20]; /* Module name, padded with zeroes */ + uint8 doseof; /* 0x1a */ + uint8 tracker[20]; /* Tracker name */ + uint16 version; /* Version number, minor-major */ + uint32 headersz; /* Header size */ + uint16 songlen; /* Song length (in patten order table) */ + uint16 restart; /* Restart position */ + uint16 channels; /* Number of channels (2,4,6,8,10,...,32) */ + uint16 patterns; /* Number of patterns (max 256) */ + uint16 instruments; /* Number of instruments (max 128) */ + uint16 flags; /* bit 0: 0=Amiga freq table, 1=Linear */ + uint16 tempo; /* Default tempo */ + uint16 bpm; /* Default BPM */ + uint8 order[256]; /* Pattern order table */ +}; + +struct xm_pattern_header { + uint32 length; /* Pattern header length */ + uint8 packing; /* Packing type (always 0) */ + uint16 rows; /* Number of rows in pattern (1..256) */ + uint16 datasize; /* Packed patterndata size */ +}; + +struct xm_instrument_header { + uint32 size; /* Instrument size */ + uint8 name[22]; /* Instrument name */ + uint8 type; /* Instrument type (always 0) */ + uint16 samples; /* Number of samples in instrument */ + uint32 sh_size; /* Sample header size */ +}; + +struct xm_instrument { + uint8 sample[96]; /* Sample number for all notes */ + uint16 v_env[24]; /* Points for volume envelope */ + uint16 p_env[24]; /* Points for panning envelope */ + uint8 v_pts; /* Number of volume points */ + uint8 p_pts; /* Number of panning points */ + uint8 v_sus; /* Volume sustain point */ + uint8 v_start; /* Volume loop start point */ + uint8 v_end; /* Volume loop end point */ + uint8 p_sus; /* Panning sustain point */ + uint8 p_start; /* Panning loop start point */ + uint8 p_end; /* Panning loop end point */ + uint8 v_type; /* Bit 0: On; 1: Sustain; 2: Loop */ + uint8 p_type; /* Bit 0: On; 1: Sustain; 2: Loop */ + uint8 y_wave; /* Vibrato waveform */ + uint8 y_sweep; /* Vibrato sweep */ + uint8 y_depth; /* Vibrato depth */ + uint8 y_rate; /* Vibrato rate */ + uint16 v_fade; /* Volume fadeout */ +#if 0 + uint8 reserved[22]; /* Reserved; 2 bytes in specs, 22 in 1.04 */ +#endif +}; + +struct xm_sample_header { + uint32 length; /* Sample length */ + uint32 loop_start; /* Sample loop start */ + uint32 loop_length; /* Sample loop length */ + uint8 volume; /* Volume */ + int8 finetune; /* Finetune (signed byte -128..+127) */ + uint8 type; /* 0=No loop,1=Fwd loop,2=Ping-pong,16-bit */ + uint8 pan; /* Panning (0-255) */ + int8 relnote; /* Relative note number (signed byte) */ + uint8 reserved; /* Reserved */ + uint8 name[22]; /* Sample_name */ +}; + +struct xm_event { + uint8 note; /* Note (0-71, 0 = C-0) */ + uint8 instrument; /* Instrument (0-128) */ + uint8 volume; /* Volume column byte */ + uint8 fx_type; /* Effect type */ + uint8 fx_parm; /* Effect parameter */ +}; + +#endif /* LIBXMP_LOADERS_XM_H */ diff --git a/thirdparty/libxmp/src/loaders/xm_load.c b/thirdparty/libxmp/src/loaders/xm_load.c new file mode 100644 index 0000000..7218960 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/xm_load.c @@ -0,0 +1,1023 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Fri, 26 Jun 1998 17:45:59 +1000 Andrew Leahy + * Finally got it working on the DEC Alpha running DEC UNIX! In the pattern + * reading loop I found I was getting "0" for (p-patbuf) and "0" for + * xph.datasize, the next if statement (where it tries to read the patbuf) + * would then cause a seg_fault. + * + * Sun Sep 27 12:07:12 EST 1998 Claudio Matsuoka + * Extended Module 1.02 stores data in a different order, we must handle + * this accordingly. MAX_SAMP used as a workaround to check the number of + * samples recognized by the player. + */ + +#include "loader.h" +#include "xm.h" +#ifndef LIBXMP_CORE_PLAYER +#include "vorbis.h" +#endif + +static int xm_test(HIO_HANDLE *, char *, const int); +static int xm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_xm = { + "Fast Tracker II", + xm_test, + xm_load +}; + +static int xm_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[20]; + + if (hio_read(buf, 1, 17, f) < 17) /* ID text */ + return -1; + + if (memcmp(buf, "Extended Module: ", 17)) + return -1; + + libxmp_read_title(f, t, 20); + + return 0; +} + +static int load_xm_pattern(struct module_data *m, int num, int version, + uint8 *patbuf, HIO_HANDLE *f) +{ + const int headsize = version > 0x0102 ? 9 : 8; + struct xmp_module *mod = &m->mod; + struct xm_pattern_header xph; + struct xmp_event *event; + uint8 *pat, b; + int j, k, r; + int size, size_read; + + xph.length = hio_read32l(f); + xph.packing = hio_read8(f); + xph.rows = version > 0x0102 ? hio_read16l(f) : hio_read8(f) + 1; + + /* Sanity check */ + if (xph.rows > 256) { + goto err; + } + + xph.datasize = hio_read16l(f); + hio_seek(f, xph.length - headsize, SEEK_CUR); + if (hio_error(f)) { + goto err; + } + + r = xph.rows; + if (r == 0) { + r = 0x100; + } + + if (libxmp_alloc_pattern_tracks(mod, num, r) < 0) { + goto err; + } + + if (xph.datasize == 0) { + return 0; + } + + size = xph.datasize; + pat = patbuf; + + size_read = hio_read(patbuf, 1, size, f); + if (size_read < size) { + memset(patbuf + size_read, 0, size - size_read); + } + + for (j = 0; j < r; j++) { + for (k = 0; k < mod->chn; k++) { + /* + if ((pat - patbuf) >= xph.datasize) + break; + */ + + event = &EVENT(num, k, j); + + if (--size < 0) { + goto err; + } + + if ((b = *pat++) & XM_EVENT_PACKING) { + if (b & XM_EVENT_NOTE_FOLLOWS) { + if (--size < 0) + goto err; + event->note = *pat++; + } + if (b & XM_EVENT_INSTRUMENT_FOLLOWS) { + if (--size < 0) + goto err; + event->ins = *pat++; + } + if (b & XM_EVENT_VOLUME_FOLLOWS) { + if (--size < 0) + goto err; + event->vol = *pat++; + } + if (b & XM_EVENT_FXTYPE_FOLLOWS) { + if (--size < 0) + goto err; + event->fxt = *pat++; + } + if (b & XM_EVENT_FXPARM_FOLLOWS) { + if (--size < 0) + goto err; + event->fxp = *pat++; + } + } else { + size -= 4; + if (size < 0) + goto err; + event->note = b; + event->ins = *pat++; + event->vol = *pat++; + event->fxt = *pat++; + event->fxp = *pat++; + } + + /* Sanity check */ + switch (event->fxt) { + case 18: + case 19: + case 22: + case 23: + case 24: + case 26: + case 28: + case 30: + case 31: + case 32: + event->fxt = 0; + } + if (event->fxt > 34) { + event->fxt = 0; + } + + if (event->note == 0x61) { + /* See OpenMPT keyoff+instr.xm test case */ + if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) { + event->note = XMP_KEY_OFF; + } else { + event->note = + event->ins ? XMP_KEY_FADE : XMP_KEY_OFF; + } + } else if (event->note > 0) { + event->note += 12; + } + + if (event->fxt == 0x0e) { + if (MSN(event->fxp) == EX_FINETUNE) { + unsigned char val = (LSN(event->fxp) - 8) & 0xf; + event->fxp = (EX_FINETUNE << 4) | val; + } + switch (event->fxp) { + case 0x43: + case 0x73: + event->fxp--; + break; + } + } + if (event->fxt == FX_XF_PORTA && MSN(event->fxp) == 0x09) { + /* Translate MPT hacks */ + switch (LSN(event->fxp)) { + case 0x0: /* Surround off */ + case 0x1: /* Surround on */ + event->fxt = FX_SURROUND; + event->fxp = LSN(event->fxp); + break; + case 0xe: /* Play forward */ + case 0xf: /* Play reverse */ + event->fxt = FX_REVERSE; + event->fxp = LSN(event->fxp) - 0xe; + } + } + + if (!event->vol) { + continue; + } + + /* Volume set */ + if ((event->vol >= 0x10) && (event->vol <= 0x50)) { + event->vol -= 0x0f; + continue; + } + + /* Volume column effects */ + switch (event->vol >> 4) { + case 0x06: /* Volume slide down */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = event->vol - 0x60; + break; + case 0x07: /* Volume slide up */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = (event->vol - 0x70) << 4; + break; + case 0x08: /* Fine volume slide down */ + event->f2t = FX_EXTENDED; + event->f2p = + (EX_F_VSLIDE_DN << 4) | (event->vol - 0x80); + break; + case 0x09: /* Fine volume slide up */ + event->f2t = FX_EXTENDED; + event->f2p = + (EX_F_VSLIDE_UP << 4) | (event->vol - 0x90); + break; + case 0x0a: /* Set vibrato speed */ + event->f2t = FX_VIBRATO; + event->f2p = (event->vol - 0xa0) << 4; + break; + case 0x0b: /* Vibrato */ + event->f2t = FX_VIBRATO; + event->f2p = event->vol - 0xb0; + break; + case 0x0c: /* Set panning */ + event->f2t = FX_SETPAN; + event->f2p = (event->vol - 0xc0) << 4; + break; + case 0x0d: /* Pan slide left */ + event->f2t = FX_PANSL_NOMEM; + event->f2p = (event->vol - 0xd0) << 4; + break; + case 0x0e: /* Pan slide right */ + event->f2t = FX_PANSL_NOMEM; + event->f2p = event->vol - 0xe0; + break; + case 0x0f: /* Tone portamento */ + event->f2t = FX_TONEPORTA; + event->f2p = (event->vol - 0xf0) << 4; + + /* From OpenMPT TonePortamentoMemory.xm: + * "Another nice bug (...) is the combination of both + * portamento commands (Mx and 3xx) in the same cell: + * The 3xx parameter is ignored completely, and the Mx + * parameter is doubled. (M2 3FF is the same as M4 000) + */ + if (event->fxt == FX_TONEPORTA + || event->fxt == FX_TONE_VSLIDE) { + if (event->fxt == FX_TONEPORTA) { + event->fxt = 0; + } else { + event->fxt = FX_VOLSLIDE; + } + event->fxp = 0; + + if (event->f2p < 0x80) { + event->f2p <<= 1; + } else { + event->f2p = 0xff; + } + } + + /* From OpenMPT porta-offset.xm: + * "If there is a portamento command next to an offset + * command, the offset command is ignored completely. In + * particular, the offset parameter is not memorized." + */ + if (event->fxt == FX_OFFSET + && event->f2t == FX_TONEPORTA) { + event->fxt = event->fxp = 0; + } + break; + } + event->vol = 0; + } + } + + return 0; + +err: + return -1; +} + +static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + uint8 *patbuf; + int i, j; + + mod->pat++; + if (libxmp_init_pattern(mod) < 0) { + return -1; + } + + D_(D_INFO "Stored patterns: %d", mod->pat - 1); + + if ((patbuf = (uint8 *) calloc(1, 65536)) == NULL) { + return -1; + } + + for (i = 0; i < mod->pat - 1; i++) { + if (load_xm_pattern(m, i, version, patbuf, f) < 0) { + goto err; + } + } + + /* Alloc one extra pattern */ + { + int t = i * mod->chn; + + if (libxmp_alloc_pattern(mod, i) < 0) { + goto err; + } + + mod->xxp[i]->rows = 64; + + if (libxmp_alloc_track(mod, t, 64) < 0) { + goto err; + } + + for (j = 0; j < mod->chn; j++) { + mod->xxp[i]->index[j] = t; + } + } + + free(patbuf); + return 0; + +err: + free(patbuf); + return -1; +} + +/* Packed structures size */ +#define XM_INST_HEADER_SIZE 29 +#define XM_INST_SIZE 212 + +/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs + * specify at most 16. See https://github.com/libxmp/libxmp/issues/168 + * for more details. */ +#define XM_MAX_SAMPLES_PER_INST 32 + +#ifndef LIBXMP_CORE_PLAYER +#define MAGIC_OGGS 0x4f676753 + +static int is_ogg_sample(HIO_HANDLE *f, struct xmp_sample *xxs) +{ + /* uint32 size; */ + uint32 id; + + /* Sample must be at least 4 bytes long to be an OGG sample. + * Bonnie's Bookstore music.oxm contains zero length samples + * followed immediately by OGG samples. */ + if (xxs->len < 4) + return 0; + + /* size = */ hio_read32l(f); + id = hio_read32b(f); + if (hio_error(f) != 0 || hio_seek(f, -8, SEEK_CUR) < 0) + return 0; + + if (id != MAGIC_OGGS) { /* copy input data if not Ogg file */ + return 0; + } + + return 1; +} + +static int oggdec(struct module_data *m, HIO_HANDLE *f, struct xmp_sample *xxs, int len) +{ + int i, n, ch, rate, ret, flags = 0; + uint8 *data; + int16 *pcm16 = NULL; + + if ((data = (uint8 *)calloc(1, len)) == NULL) + return -1; + + hio_read32b(f); + if (hio_error(f) != 0 || hio_read(data, 1, len - 4, f) != len - 4) { + free(data); + return -1; + } + + n = stb_vorbis_decode_memory(data, len, &ch, &rate, &pcm16); + free(data); + + if (n <= 0) { + free(pcm16); + return -1; + } + + xxs->len = n; + + if ((xxs->flg & XMP_SAMPLE_16BIT) == 0) { + uint8 *pcm = (uint8 *)pcm16; + + for (i = 0; i < n; i++) { + pcm[i] = pcm16[i] >> 8; + } + pcm = (uint8 *)realloc(pcm16, n); + if (pcm == NULL) { + free(pcm16); + return -1; + } + pcm16 = (int16 *)pcm; + } + + flags |= SAMPLE_FLAG_NOLOAD; +#ifdef WORDS_BIGENDIAN + flags |= SAMPLE_FLAG_BIGEND; +#endif + + ret = libxmp_load_sample(m, NULL, flags, xxs, pcm16); + free(pcm16); + + return ret; +} +#endif + +static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + struct xm_instrument_header xih; + struct xm_instrument xi; + struct xm_sample_header xsh[XM_MAX_SAMPLES_PER_INST]; + int sample_num = 0; + long total_sample_size; + int i, j; + uint8 buf[208]; + + D_(D_INFO "Instruments: %d", mod->ins); + + /* ESTIMATED value! We don't know the actual value at this point */ + mod->smp = MAX_SAMPLES; + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + for (i = 0; i < mod->ins; i++) { + long instr_pos = hio_tell(f); + struct xmp_instrument *xxi = &mod->xxi[i]; + + /* Modules converted with MOD2XM 1.0 always say we have 31 + * instruments, but file may end abruptly before that. Also covers + * XMLiTE stripped modules and truncated files. This test will not + * work if file has trailing garbage. + * + * Note: loading 4 bytes past the instrument header to get the + * sample header size (if it exists). This is NOT considered to + * be part of the instrument header. + */ + if (hio_read(buf, XM_INST_HEADER_SIZE + 4, 1, f) != 1) { + D_(D_WARN "short read in instrument header data"); + break; + } + + xih.size = readmem32l(buf); /* Instrument size */ + memcpy(xih.name, buf + 4, 22); /* Instrument name */ + xih.type = buf[26]; /* Instrument type (always 0) */ + xih.samples = readmem16l(buf + 27); /* Number of samples */ + xih.sh_size = readmem32l(buf + 29); /* Sample header size */ + + /* Sanity check */ + if ((int)xih.size < XM_INST_HEADER_SIZE) { + D_(D_CRIT "instrument %d: instrument header size:%d", i + 1, xih.size); + return -1; + } + + if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) { + D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size); + return -1; + } + + libxmp_instrument_name(mod, i, xih.name, 22); + + xxi->nsm = xih.samples; + + D_(D_INFO "instrument:%2X (%s) samples:%2d", i, xxi->name, xxi->nsm); + + if (xxi->nsm == 0) { + /* Sample size should be in struct xm_instrument according to + * the official format description, but FT2 actually puts it in + * struct xm_instrument header. There's a tracker or converter + * that follow the specs, so we must handle both cases (see + * "Braintomb" by Jazztiz/ART). + */ + + /* Umm, Cyke O'Path sent me a couple of + * mods ("Breath of the Wind" and "Broken Dimension") that + * reserve the instrument data space after the instrument header + * even if the number of instruments is set to 0. In these modules + * the instrument header size is marked as 263. The following + * generalization should take care of both cases. + */ + + if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR) < 0) { + return -1; + } + + continue; + } + + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) { + return -1; + } + + /* for BoobieSqueezer (see http://boobie.rotfl.at/) + * It works pretty much the same way as Impulse Tracker's sample + * only mode, where it will strip off the instrument data. + */ + if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) { + memset(&xi, 0, sizeof(struct xm_instrument)); + hio_seek(f, xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR); + } else { + uint8 *b = buf; + + if (hio_read(buf, 208, 1, f) != 1) { + D_(D_CRIT "short read in instrument data"); + return -1; + } + + memcpy(xi.sample, b, 96); /* Sample map */ + b += 96; + + for (j = 0; j < 24; j++) { + xi.v_env[j] = readmem16l(b); /* Points for volume envelope */ + b += 2; + } + + for (j = 0; j < 24; j++) { + xi.p_env[j] = readmem16l(b); /* Points for pan envelope */ + b += 2; + } + + xi.v_pts = *b++; /* Number of volume points */ + xi.p_pts = *b++; /* Number of pan points */ + xi.v_sus = *b++; /* Volume sustain point */ + xi.v_start = *b++; /* Volume loop start point */ + xi.v_end = *b++; /* Volume loop end point */ + xi.p_sus = *b++; /* Pan sustain point */ + xi.p_start = *b++; /* Pan loop start point */ + xi.p_end = *b++; /* Pan loop end point */ + xi.v_type = *b++; /* Bit 0:On 1:Sustain 2:Loop */ + xi.p_type = *b++; /* Bit 0:On 1:Sustain 2:Loop */ + xi.y_wave = *b++; /* Vibrato waveform */ + xi.y_sweep = *b++; /* Vibrato sweep */ + xi.y_depth = *b++; /* Vibrato depth */ + xi.y_rate = *b++; /* Vibrato rate */ + xi.v_fade = readmem16l(b); /* Volume fadeout */ + + /* Skip reserved space */ + if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + XM_INST_SIZE), SEEK_CUR) < 0) { + return -1; + } + + /* Envelope */ + xxi->rls = xi.v_fade << 1; + xxi->aei.npt = xi.v_pts; + xxi->aei.sus = xi.v_sus; + xxi->aei.lps = xi.v_start; + xxi->aei.lpe = xi.v_end; + xxi->aei.flg = xi.v_type; + xxi->pei.npt = xi.p_pts; + xxi->pei.sus = xi.p_sus; + xxi->pei.lps = xi.p_start; + xxi->pei.lpe = xi.p_end; + xxi->pei.flg = xi.p_type; + + if (xxi->aei.npt <= 0 || xxi->aei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) { + xxi->aei.flg &= ~XMP_ENVELOPE_ON; + } else { + memcpy(xxi->aei.data, xi.v_env, xxi->aei.npt * 4); + } + + if (xxi->pei.npt <= 0 || xxi->pei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) { + xxi->pei.flg &= ~XMP_ENVELOPE_ON; + } else { + memcpy(xxi->pei.data, xi.p_env, xxi->pei.npt * 4); + } + + for (j = 12; j < 108; j++) { + xxi->map[j].ins = xi.sample[j - 12]; + if (xxi->map[j].ins >= xxi->nsm) + xxi->map[j].ins = 0xff; + } + } + + /* Read subinstrument and sample parameters */ + + for (j = 0; j < xxi->nsm; j++, sample_num++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs; + uint8 *b = buf; + + D_(D_INFO " sample index:%d sample id:%d", j, sample_num); + + if (sample_num >= mod->smp) { + if (libxmp_realloc_samples(m, mod->smp * 3 / 2) < 0) + return -1; + } + xxs = &mod->xxs[sample_num]; + + if (hio_read(buf, 40, 1, f) != 1) { + D_(D_CRIT "short read in sample data"); + return -1; + } + + xsh[j].length = readmem32l(b); /* Sample length */ + b += 4; + + /* Sanity check */ + if (xsh[j].length > MAX_SAMPLE_SIZE) { + D_(D_CRIT "sanity check: %d: bad sample size", j); + return -1; + } + + xsh[j].loop_start = readmem32l(b); /* Sample loop start */ + b += 4; + xsh[j].loop_length = readmem32l(b); /* Sample loop length */ + b += 4; + xsh[j].volume = *b++; /* Volume */ + xsh[j].finetune = *b++; /* Finetune (-128..+127) */ + xsh[j].type = *b++; /* Flags */ + xsh[j].pan = *b++; /* Panning (0-255) */ + xsh[j].relnote = *(int8 *) b++; /* Relative note number */ + xsh[j].reserved = *b++; + memcpy(xsh[j].name, b, 22); + + sub->vol = xsh[j].volume; + sub->pan = xsh[j].pan; + sub->xpo = xsh[j].relnote; + sub->fin = xsh[j].finetune; + sub->vwf = xi.y_wave; + sub->vde = xi.y_depth << 2; + sub->vra = xi.y_rate; + sub->vsw = xi.y_sweep; + sub->sid = sample_num; + + libxmp_copy_adjust(xxs->name, xsh[j].name, 22); + + xxs->len = xsh[j].length; + xxs->lps = xsh[j].loop_start; + xxs->lpe = xsh[j].loop_start + xsh[j].loop_length; + + xxs->flg = 0; + if (xsh[j].type & XM_SAMPLE_16BIT) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + if (xsh[j].type & XM_SAMPLE_STEREO) { + /* xxs->flg |= XMP_SAMPLE_STEREO; */ + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0; + + D_(D_INFO " size:%06x loop start:%06x loop end:%06x %c V%02x F%+04d P%02x R%+03d %s", + mod->xxs[sub->sid].len, + mod->xxs[sub->sid].lps, + mod->xxs[sub->sid].lpe, + mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin, sub->pan, sub->xpo, + mod->xxs[sub->sid].flg & XMP_SAMPLE_16BIT ? " (16 bit)" : ""); + } + + /* Read actual sample data */ + + total_sample_size = 0; + for (j = 0; j < xxi->nsm; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[sub->sid]; + int flags; + + flags = SAMPLE_FLAG_DIFF; +#ifndef LIBXMP_CORE_PLAYER + if (xsh[j].reserved == 0xad) { + flags = SAMPLE_FLAG_ADPCM; + } +#endif + + if (version > 0x0103) { + D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid); + +#ifndef LIBXMP_CORE_PLAYER + if (is_ogg_sample(f, xxs)) { + if (oggdec(m, f, xxs, xsh[j].length) < 0) { + return -1; + } + + D_(D_INFO " sample is vorbis"); + total_sample_size += xsh[j].length; + continue; + } +#endif + + if (libxmp_load_sample(m, f, flags, xxs, NULL) < 0) { + return -1; + } + if (flags & SAMPLE_FLAG_ADPCM) { + D_(D_INFO " sample is adpcm"); + total_sample_size += 16 + ((xsh[j].length + 1) >> 1); + } else { + total_sample_size += xsh[j].length; + } + + /* TODO: implement stereo samples. + * For now, just skip the right channel. */ + if (xsh[j].type & XM_SAMPLE_STEREO) { + hio_seek(f, xsh[j].length >> 1, SEEK_CUR); + } + } + } + + /* Reposition correctly in case of 16-bit sample having odd in-file length. + * See "Lead Lined for '99", reported by Dennis Mulleneers. + */ + if (hio_seek(f, instr_pos + xih.size + 40 * xih.samples + total_sample_size, SEEK_SET) < 0) { + return -1; + } + } + + /* Final sample number adjustment */ + if (libxmp_realloc_samples(m, sample_num) < 0) { + return -1; + } + + return 0; +} + +static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xm_file_header xfh; + char tracker_name[21]; +#ifndef LIBXMP_CORE_PLAYER + int claims_ft2 = 0; + int is_mpt_116 = 0; +#endif + int len; + uint8 buf[80]; + + LOAD_INIT(); + + if (hio_read(buf, 80, 1, f) != 1) { + D_(D_CRIT "error reading header"); + return -1; + } + + memcpy(xfh.id, buf, 17); /* ID text */ + memcpy(xfh.name, buf + 17, 20); /* Module name */ + /* */ /* skip 0x1a */ + memcpy(xfh.tracker, buf + 38, 20); /* Tracker name */ + xfh.version = readmem16l(buf + 58); /* Version number, minor-major */ + xfh.headersz = readmem32l(buf + 60); /* Header size */ + xfh.songlen = readmem16l(buf + 64); /* Song length */ + xfh.restart = readmem16l(buf + 66); /* Restart position */ + xfh.channels = readmem16l(buf + 68); /* Number of channels */ + xfh.patterns = readmem16l(buf + 70); /* Number of patterns */ + xfh.instruments = readmem16l(buf + 72); /* Number of instruments */ + xfh.flags = readmem16l(buf + 74); /* 0=Amiga freq table, 1=Linear */ + xfh.tempo = readmem16l(buf + 76); /* Default tempo */ + xfh.bpm = readmem16l(buf + 78); /* Default BPM */ + + /* Sanity checks */ + if (xfh.songlen > 256) { + D_(D_CRIT "bad song length: %d", xfh.songlen); + return -1; + } + if (xfh.patterns > 256) { + D_(D_CRIT "bad pattern count: %d", xfh.patterns); + return -1; + } + if (xfh.instruments > 255) { + D_(D_CRIT "bad instrument count: %d", xfh.instruments); + return -1; + } + + if (xfh.restart > 255) { + D_(D_CRIT "bad restart position: %d", xfh.restart); + return -1; + } + if (xfh.channels > XMP_MAX_CHANNELS) { + D_(D_CRIT "bad channel count: %d", xfh.channels); + return -1; + } + + /* FT2 and MPT allow up to 255 BPM. OpenMPT allows up to 1000 BPM. */ + if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 1000) { + if (memcmp("MED2XM", xfh.tracker, 6)) { + D_(D_CRIT "bad tempo or BPM: %d %d", xfh.tempo, xfh.bpm); + return -1; + } + } + + /* Honor header size -- needed by BoobieSqueezer XMs */ + len = xfh.headersz - 0x14; + if (len < 0 || len > 256) { + D_(D_CRIT "bad XM header length: %d", len); + return -1; + } + + memset(xfh.order, 0, sizeof(xfh.order)); + if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */ + D_(D_CRIT "error reading orders"); + return -1; + } + + strncpy(mod->name, (char *)xfh.name, 20); + + mod->len = xfh.songlen; + mod->chn = xfh.channels; + mod->pat = xfh.patterns; + mod->ins = xfh.instruments; + mod->rst = xfh.restart; + mod->spd = xfh.tempo; + mod->bpm = xfh.bpm; + mod->trk = mod->chn * mod->pat + 1; + + m->c4rate = C4_NTSC_RATE; + m->period_type = xfh.flags & XM_LINEAR_PERIOD_MODE ? PERIOD_LINEAR : PERIOD_AMIGA; + + memcpy(mod->xxo, xfh.order, mod->len); + /*tracker_name[20] = 0;*/ + snprintf(tracker_name, 21, "%-20.20s", xfh.tracker); + for (i = 20; i >= 0; i--) { + if (tracker_name[i] == 0x20) + tracker_name[i] = 0; + if (tracker_name[i]) + break; + } + + /* OpenMPT accurately emulates weird FT2 bugs */ + if (!strncmp(tracker_name, "FastTracker v2.00", 17)) { + m->quirk |= QUIRK_FT2BUGS; +#ifndef LIBXMP_CORE_PLAYER + claims_ft2 = 1; +#endif + } else if (!strncmp(tracker_name, "OpenMPT ", 8)) { + m->quirk |= QUIRK_FT2BUGS; + } +#ifndef LIBXMP_CORE_PLAYER + if (xfh.headersz == 0x0113) { + strcpy(tracker_name, "unknown tracker"); + m->quirk &= ~QUIRK_FT2BUGS; + } else if (*tracker_name == 0) { + strcpy(tracker_name, "Digitrakker"); /* best guess */ + m->quirk &= ~QUIRK_FT2BUGS; + } + + /* See MMD1 loader for explanation */ + if (!strncmp(tracker_name, "MED2XM by J.Pynnone", 19)) { + if (mod->bpm <= 10) { + mod->bpm = 125 * (0x35 - mod->bpm * 2) / 33; + } + m->quirk &= ~QUIRK_FT2BUGS; + } + + if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) { + strcpy(tracker_name, "old ModPlug Tracker"); + m->quirk &= ~QUIRK_FT2BUGS; + is_mpt_116 = 1; + } + + libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff); +#else + libxmp_set_type(m, tracker_name); +#endif + + MODULE_INFO(); + + /* Honor header size */ + if (hio_seek(f, start + xfh.headersz + 60, SEEK_SET) < 0) { + return -1; + } + + /* XM 1.02/1.03 has a different patterns and instruments order */ + if (xfh.version <= 0x0103) { + if (load_instruments(m, xfh.version, f) < 0) { + return -1; + } + if (load_patterns(m, xfh.version, f) < 0) { + return -1; + } + } else { + if (load_patterns(m, xfh.version, f) < 0) { + return -1; + } + if (load_instruments(m, xfh.version, f) < 0) { + return -1; + } + } + + D_(D_INFO "Stored samples: %d", mod->smp); + + /* XM 1.02 stores all samples after the patterns */ + if (xfh.version <= 0x0103) { + for (i = 0; i < mod->ins; i++) { + for (j = 0; j < mod->xxi[i].nsm; j++) { + int sid = mod->xxi[i].sub[j].sid; + if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, &mod->xxs[sid], NULL) < 0) { + return -1; + } + } + } + } + +#ifndef LIBXMP_CORE_PLAYER + /* Load MPT properties from the end of the file. */ + while (1) { + uint32 ext = hio_read32b(f); + uint32 sz = hio_read32l(f); + int known = 0; + + if (hio_error(f) || sz > 0x7fffffff /* INT32_MAX */) + break; + + switch (ext) { + case MAGIC4('t','e','x','t'): /* Song comment */ + known = 1; + if (m->comment != NULL) + break; + + if ((m->comment = (char *)malloc(sz + 1)) == NULL) + break; + + sz = hio_read(m->comment, 1, sz, f); + m->comment[sz] = '\0'; + + for (i = 0; i < (int)sz; i++) { + int b = m->comment[i]; + if (b == '\r') { + m->comment[i] = '\n'; + } else if ((b < 32 || b > 127) && b != '\n' + && b != '\t') { + m->comment[i] = '.'; + } + } + break; + + case MAGIC4('M','I','D','I'): /* MIDI config */ + case MAGIC4('P','N','A','M'): /* Pattern names */ + case MAGIC4('C','N','A','M'): /* Channel names */ + case MAGIC4('C','H','F','X'): /* Channel plugins */ + case MAGIC4('X','T','P','M'): /* Inst. extensions */ + known = 1; + /* fall-through */ + + default: + /* Plugin definition */ + if ((ext & MAGIC4('F','X', 0, 0)) == MAGIC4('F','X', 0, 0)) + known = 1; + + if (sz) hio_seek(f, sz, SEEK_CUR); + break; + } + + if(known && claims_ft2) + is_mpt_116 = 1; + + if (ext == MAGIC4('X','T','P','M')) + break; + } + + if (is_mpt_116) { + libxmp_set_type(m, "ModPlug Tracker 1.16 XM %d.%02d", + xfh.version >> 8, xfh.version & 0xff); + + m->mvolbase = 48; + m->mvol = 48; + libxmp_apply_mpt_preamp(m); + } +#endif + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = 0x80; + } + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/xmf_load.c b/thirdparty/libxmp/src/loaders/xmf_load.c new file mode 100644 index 0000000..3b57a6c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/xmf_load.c @@ -0,0 +1,433 @@ +/* Extended Module Player + * Copyright (C) 2023 Alice Rowan + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Astroidea XMF, used by Imperium Galactica and some other modules. + * This format is completely unrelated to the MIDI XMF format. + */ + +#include "loader.h" + +static int xmf_test(HIO_HANDLE *, char *, const int); +static int xmf_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_xmf = { + "Astroidea XMF", + xmf_test, + xmf_load +}; + +#define XMF_SAMPLE_ARRAY_SIZE (16 * 256) + +static int xmf_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 buf[XMF_SAMPLE_ARRAY_SIZE]; + uint8 *pos; + uint32 samples_length = 0; + long length; + int samples_start; + int num_patterns; + int num_channels; + int num_ins; + int xmf_type; + int i; + + /* This value is 0x03 for all Imperium Galactica modules. + * The demo "Prostate 666" and all other XMFs use 0x04 instead. */ + xmf_type = hio_read8(f); + if (xmf_type != 0x03 && xmf_type != 0x04) + return -1; + + if (hio_read(buf, 1, XMF_SAMPLE_ARRAY_SIZE, f) < XMF_SAMPLE_ARRAY_SIZE) + return -1; + + /* Test instruments */ + pos = buf; + num_ins = 0; + for (i = 0; i < 256; i++) { + uint32 loopstart = readmem24l(pos + 0); + uint32 loopend = readmem24l(pos + 3); + uint32 datastart = readmem24l(pos + 6); + uint32 dataend = readmem24l(pos + 9); + uint8 flags = pos[13]; + uint16 srate = readmem16l(pos + 14); + + uint32 len = dataend - datastart; + pos += 16; + + if (flags & ~(0x04 | 0x08 | 0x10)) { + D_(D_WARN "not XMF: smp %d: unknown flags", i); + return -1; + } + /* if ping-pong loop flag is enabled, normal loop flag should be enabled too */ + if ((flags & (0x08 | 0x10)) == 0x10) { + D_(D_WARN "not XMF: smp %d: inconsistent loop flags", i); + return -1; + } + /* if loop flag is enabled, the loop should have a valid end point */ + if ((flags & 0x08) && !loopend) { + D_(D_WARN "not XMF: smp %d: inconsistent loop data", i); + return -1; + } + /* a 16-bit sample should have an even number of bytes */ + if ((flags & 0x04) && (len & 1)) { + D_(D_WARN "not XMF: smp %d: inconsistent 16-bit sample length", i); + return -1; + } + /* if this slot contains a valid sample, it should have a somewhat realistic middle-c frequency */ + if (len && srate < 100) { + D_(D_WARN "not XMF: smp %d: low sample rate", i); + return -1; + } + + /* Despite the data start and end values, samples are stored + * sequentially after the pattern data. These fields are still + * required to calculate the sample length. */ + if (datastart > dataend) { + D_(D_WARN "not XMF: smp %d: data start %u > end %u", + i, (unsigned)datastart, (unsigned)dataend); + return -1; + } + + samples_length += len; + + /* All known XMFs have well-formed loops. */ + if (loopend != 0 && (loopstart >= len || loopend > len || loopstart > loopend)) { + D_(D_WARN "not XMF: smp %d: bad loop %u %u (len: %u)", + i, (unsigned)loopstart, (unsigned)loopend, (unsigned)len); + return -1; + } + + if (len > 0) + num_ins = i + 1; + } + if (num_ins > MAX_INSTRUMENTS) + return -1; + + /* Get pattern data size. */ + if (hio_read(buf, 1, 258, f) < 258) + return -1; + + num_channels = buf[256] + 1; + num_patterns = buf[257] + 1; + + if (num_channels > XMP_MAX_CHANNELS) + return -1; + + /* Test total module length */ + samples_start = 0x1103 + num_channels + num_patterns * num_channels * 64 * 6; + length = hio_size(f); + if (length < samples_start || (size_t)length - samples_start < samples_length) { + D_(D_WARN "not XMF: file length %ld is shorter than required %zu", + length, (size_t)samples_start + samples_length); + return -1; + } + + libxmp_read_title(f, t, 0); + + return 0; +} + + +/* TODO: command pages would be nice, but no official modules rely on 5xy/6xy. */ +static void xmf_insert_effect(struct xmp_event *event, uint8 fxt, uint8 fxp, int chn) +{ + if (chn == 0) { + event->fxt = fxt; + event->fxp = fxp; + } else { + event->f2t = fxt; + event->f2p = fxp; + } +} + +static void xmf_translate_effect(struct xmp_event *event, uint8 effect, uint8 param, int chn) +{ + /* Most effects are Protracker compatible. Only the effects actually + * implemented by Imperium Galactica are handled here. */ + + switch (effect) { + case 0x00: /* none/arpeggio */ + case 0x01: /* portamento up */ + case 0x02: /* portamento down */ + case 0x0f: /* set speed + set BPM */ + if (param) { + xmf_insert_effect(event, effect, param, chn); + } + break; + + case 0x03: /* tone portamento */ + case 0x04: /* vibrato */ + case 0x0c: /* set volume */ + case 0x0d: /* break */ + xmf_insert_effect(event, effect, param, chn); + break; + + case 0x05: /* volume slide + tone portamento */ + case 0x06: /* volume slide + vibrato */ + if (effect == 0x05) { + xmf_insert_effect(event, FX_TONEPORTA, 0, chn ^ 1); + } + if (effect == 0x06) { + xmf_insert_effect(event, FX_VIBRATO, 0, chn ^ 1); + } + + /* fall-through */ + + case 0x0a: /* volume slide */ + if (param & 0x0f) { + /* down takes precedence and uses the full param. */ + xmf_insert_effect(event, FX_VOLSLIDE_DN, param << 2, chn); + } else if (param & 0xf0) { + xmf_insert_effect(event, FX_VOLSLIDE_UP, param >> 2, chn); + } + break; + + case 0x0b: /* pattern jump (jumps to xx + 1) */ + if (param < 255) { + xmf_insert_effect(event, FX_JUMP, param + 1, chn); + } + break; + + case 0x0e: /* extended */ + switch (param >> 4) { + case 0x01: /* fine slide up */ + case 0x02: /* fine slide down */ + case 0x06: /* pattern loop (broken) */ + case 0x09: /* note retrigger (TODO: only once) */ + case 0x0c: /* note cut */ + case 0x0d: /* note delay */ + case 0x0e: /* pattern delay */ + if (param & 0x0f) { + xmf_insert_effect(event, effect, param, chn); + } + break; + + case 0x04: /* vibrato waveform */ + param &= 3; + param = param < 3 ? param : 2; + xmf_insert_effect(event, effect, param, chn); + break; + + case 0x0a: /* fine volume slide up */ + if (param & 0x0f) { + xmf_insert_effect(event, FX_F_VSLIDE_UP, (param & 0x0f) << 2, chn); + } + break; + + case 0x0b: /* fine volume slide down */ + if (param & 0x0f) { + xmf_insert_effect(event, FX_F_VSLIDE_DN, (param & 0x0f) << 2, chn); + } + break; + } + break; + + case 0x10: /* panning (4-bit, GUS driver only) */ + param &= 0x0f; + param |= (param << 4); + xmf_insert_effect(event, FX_SETPAN, param, chn); + break; + + case 0x11: /* Ultra Tracker retrigger */ + /* TODO: should support the full param range, needs testing. */ + xmf_insert_effect(event, FX_EXTENDED, (EX_RETRIG << 4) | (param & 0x0f), chn); + break; + } +} + +static int xmf_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + uint8 *buf, *pos; + size_t pat_sz; + int xmf_type; + int i, j, k; + + LOAD_INIT(); + + /* Imperium Galactica uses 0x03, other Astroidea tracks use 0x04 */ + xmf_type = hio_read8(f); + if(xmf_type == 0x03) + snprintf(mod->type, XMP_NAME_SIZE, "Imperium Galactica XMF"); + else + snprintf(mod->type, XMP_NAME_SIZE, "Astroidea XMF"); + + MODULE_INFO(); + + if ((buf = (uint8 *)malloc(XMF_SAMPLE_ARRAY_SIZE)) == NULL) + return -1; + + /* Count instruments */ + if (hio_read(buf, 1, XMF_SAMPLE_ARRAY_SIZE, f) < XMF_SAMPLE_ARRAY_SIZE) + goto err; + + mod->ins = 0; + pos = buf; + for (i = 0; i < 256; i++, pos += 16) { + if (readmem24l(pos + 9) > readmem24l(pos + 6)) + mod->ins = i; + } + mod->ins++; + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + goto err; + + /* Instruments */ + pos = buf; + for (i = 0; i < mod->ins; i++, pos += 16) { + struct extra_sample_data *xtra = &(m->xtra[i]); + struct xmp_instrument *xxi = &(mod->xxi[i]); + struct xmp_sample *xxs = &(mod->xxs[i]); + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err; + + sub = &(xxi->sub[0]); + + xxs->len = readmem24l(pos + 9) - readmem24l(pos + 6); + xxs->lps = readmem24l(pos + 0); + xxs->lpe = readmem24l(pos + 3); + xtra->c5spd = readmem16l(pos + 14); + sub->vol = pos[12]; + sub->sid = i; + + /* The Sound Blaster driver will only loop if both the + * loop start and loop end are non-zero. The Sound Blaster + * driver does not support 16-bit samples or bidirectional + * looping, and plays these as regular 8-bit looped samples. + * + * GUS: 16-bit samples are loaded as 8-bit but play as 16-bit. + * If the first sample is 16-bit it will partly work (due to + * having a GUS RAM address of 0?). Other 16-bit samples will + * read from silence, garbage, or other samples. + */ + if (pos[13] & 0x04) { /* GUS 16-bit flag */ + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + } + if (pos[13] & 0x08) /* GUS loop enable */ + xxs->flg |= XMP_SAMPLE_LOOP; + if (pos[13] & 0x10) /* GUS reverse flag */ + xxs->flg |= XMP_SAMPLE_LOOP_BIDIR; + + if (xxs->len > 0) + xxi->nsm = 1; + + D_(D_INFO "[%2X] %06x %06x %06x %c%c V%02x %5d", i, + xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + xxs->flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : ' ', + sub->vol, (int)xtra->c5spd); + } + + /* Sequence */ + if (hio_read(mod->xxo, 1, 256, f) < 256) + return -1; + + mod->chn = hio_read8(f) + 1; + mod->pat = hio_read8(f) + 1; + mod->trk = mod->chn * mod->pat; + + for (i = 0; i < 256; i++) { + if (mod->xxo[i] == 0xff) + break; + } + mod->len = i; + + /* Panning table (supported by the Gravis UltraSound driver only) */ + if (hio_read(buf, 1, mod->chn, f) < mod->chn) + goto err; + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = 0x80 + (buf[i] - 7) * 16; + if (mod->xxc[i].pan > 255) + mod->xxc[i].pan = 255; + } + + D_(D_INFO "Module length: %d", mod->len); + + pat_sz = mod->chn * 6 * 64; + if (pat_sz > XMF_SAMPLE_ARRAY_SIZE) { + if ((pos = (uint8 *)realloc(buf, pat_sz)) == NULL) + goto err; + buf = pos; + } + + if (libxmp_init_pattern(mod) < 0) + goto err; + + /* Patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err; + + if (hio_read(buf, 1, pat_sz, f) < pat_sz) + goto err; + + pos = buf; + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + + if (pos[0] > 0) + event->note = pos[0] + 36; + event->ins = pos[1]; + + xmf_translate_effect(event, pos[2], pos[5], 0); + xmf_translate_effect(event, pos[3], pos[4], 1); + pos += 6; + } + } + } + free(buf); + + /* Sample data */ + D_(D_INFO "Stored samples: %d", mod->smp); + + /* Despite the GUS sample start and end pointers saved in the file, + * these are actually just loaded sequentially. */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL)) + return -1; + } + + /* With the Sound Blaster driver, full volume samples have a -0dB mix. + * Doing this in libxmp (x4 mvolbase) clips a little bit, so use a + * slightly lower level (x3 mvolbase, ~192 in IT terms). + * + * This only applies to the Imperium Galactica tracks; the tracks with + * 0x04 use the full GUS volume range. + */ + m->volbase = 0xff; + m->mvolbase = 48; + m->mvol = (xmf_type == 0x03) ? m->mvolbase * 3 : m->mvolbase; + return 0; + + err: + free(buf); + return -1; +} diff --git a/thirdparty/libxmp/src/md5.c b/thirdparty/libxmp/src/md5.c new file mode 100644 index 0000000..b9433a4 --- /dev/null +++ b/thirdparty/libxmp/src/md5.c @@ -0,0 +1,240 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "common.h" +#include "md5.h" + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static uint8 PADDING[MD5_BLOCK_LENGTH] = { + 0x80, 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 +}; + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 state[4], const uint8 block[MD5_BLOCK_LENGTH]) +{ + uint32 a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; + +#ifndef WORDS_BIGENDIAN + memcpy(in, block, sizeof(in)); +#else + for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { + in[a] = (uint32)( + (uint32)(block[a * 4 + 0]) | + (uint32)(block[a * 4 + 1]) << 8 | + (uint32)(block[a * 4 + 2]) << 16 | + (uint32)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(MD5_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + need = MD5_BLOCK_LENGTH - have; + + /* Update bitcount */ + ctx->count += (uint64)len << 3; + + if (len >= need) { + if (have != 0) { + memcpy(ctx->buffer + have, input, need); + MD5Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ + while (len >= MD5_BLOCK_LENGTH) { + MD5Transform(ctx->state, input); + input += MD5_BLOCK_LENGTH; + len -= MD5_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + memcpy(ctx->buffer + have, input, len); +} + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5Pad(MD5_CTX *ctx) +{ + uint8 count[8]; + size_t padlen; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD5_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD5_BLOCK_LENGTH; + MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD5Update(ctx, count, 8); +} + +/* + * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + */ +void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) +{ + int i; + + MD5Pad(ctx); + if (digest != NULL) { + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + memset(ctx, 0, sizeof(*ctx)); + } +} + diff --git a/thirdparty/libxmp/src/md5.h b/thirdparty/libxmp/src/md5.h new file mode 100644 index 0000000..b5c44ba --- /dev/null +++ b/thirdparty/libxmp/src/md5.h @@ -0,0 +1,37 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + */ + +#ifndef LIBXMP_MD5_H +#define LIBXMP_MD5_H + +#include "common.h" + +#define MD5_BLOCK_LENGTH 64 +#define MD5_DIGEST_LENGTH 16 +#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) + +typedef struct MD5Context { + uint32 state[4]; /* state */ + uint64 count; /* number of bits, mod 2^64 */ + uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */ +} MD5_CTX; + +LIBXMP_BEGIN_DECLS + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, const unsigned char *, size_t); +void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *); + +LIBXMP_END_DECLS + +#endif /* LIBXMP_MD5_H */ + diff --git a/thirdparty/libxmp/src/mdataio.h b/thirdparty/libxmp/src/mdataio.h new file mode 100644 index 0000000..c076c08 --- /dev/null +++ b/thirdparty/libxmp/src/mdataio.h @@ -0,0 +1,124 @@ +#ifndef LIBXMP_MDATAIO_H +#define LIBXMP_MDATAIO_H + +#include +#include "common.h" + +static inline ptrdiff_t CAN_READ(MFILE *m) +{ + if (m->size >= 0) + return m->pos >= 0 ? m->size - m->pos : 0; + + return INT_MAX; +} + +static inline uint8 mread8(MFILE *m, int *err) +{ + uint8 x = 0xff; + size_t r = mread(&x, 1, 1, m); + if (err) { + *err = (r == 1) ? 0 : EOF; + } + return x; +} + +static inline int8 mread8s(MFILE *m, int *err) +{ + int r = mgetc(m); + if (err) { + *err = (r < 0) ? EOF : 0; + } + return (int8)r; +} + +static inline uint16 mread16l(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 2) { + uint16 n = readmem16l(m->start + m->pos); + m->pos += 2; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffff; + } +} + +static inline uint16 mread16b(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 2) { + uint16 n = readmem16b(m->start + m->pos); + m->pos += 2; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffff; + } +} + +static inline uint32 mread24l(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 3) { + uint32 n = readmem24l(m->start + m->pos); + m->pos += 3; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +static inline uint32 mread24b(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 3) { + uint32 n = readmem24b(m->start + m->pos); + m->pos += 3; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +static inline uint32 mread32l(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 4) { + uint32 n = readmem32l(m->start + m->pos); + m->pos += 4; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +static inline uint32 mread32b(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 4) { + uint32 n = readmem32b(m->start + m->pos); + m->pos += 4; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +#endif diff --git a/thirdparty/libxmp/src/med_extras.c b/thirdparty/libxmp/src/med_extras.c new file mode 100644 index 0000000..9f890b3 --- /dev/null +++ b/thirdparty/libxmp/src/med_extras.c @@ -0,0 +1,448 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "virtual.h" +#include "effects.h" +#include "med_extras.h" + +#ifdef __SUNPRO_C +#pragma error_messages (off,E_STATEMENT_NOT_REACHED) +#endif + +/* Commands in the volume and waveform sequence table: + * + * Cmd Vol Wave Action + * + * 0xff END End sequence + * 0xfe JMP Jump + * 0xfd - ARE End arpeggio definition + * 0xfc - ARP Begin arpeggio definition + * 0xfb HLT Halt + * 0xfa JWS JVS Jump waveform/volume sequence + * 0xf9 - - + * 0xf8 - - + * 0xf7 - VWF Set vibrato waveform + * 0xf6 EST RES ?/reset pitch + * 0xf5 EN2 VBS Looping envelope/set vibrato speed + * 0xf4 EN1 VBD One shot envelope/set vibrato depth + * 0xf3 CHU Change volume/pitch up speed + * 0xf2 CHD Change volume/pitch down speed + * 0xf1 WAI Wait + * 0xf0 SPD Set speed + */ + +#define VT ((ce->vp >= 0 && ce->vp < ie->vtlen) ? me->vol_table[xc->ins][ce->vp++] : 0xff) +#define WT ((ce->wp >= 0 && ce->wp < ie->wtlen) ? me->wav_table[xc->ins][ce->wp++] : 0xff) +#define VT_SKIP ce->vp++ +#define WT_SKIP ce->wp++ + +#define ARP(idx) ((idx) < ie->wtlen ? me->wav_table[xc->ins][(idx)] : 0xfd) + + +static const int sine[32] = { + 0, 49, 97, 141, 180, 212, 235, 250, + 255, 250, 235, 212, 180, 141, 97, 49, + 0, -49, -97,-141,-180,-212,-235,-250, + -255,-250,-235,-212,-180,-141, -97, -49 +}; + +int libxmp_med_change_period(struct context_data *ctx, struct channel_data *xc) +{ + struct med_channel_extras *ce = (struct med_channel_extras *)xc->extra; + int vib; + + /* Vibrato */ + +#if 0 + if (ce->vib_wf >= xxi[xc->ins].nsm) /* invalid waveform */ + return 0; + + if (xxs[xxi[xc->ins][ce->vib_wf].sid].len != 32) + return 0; +#endif + + /* FIXME: always using sine waveform */ + + vib = (sine[ce->vib_idx >> 5] * ce->vib_depth) >> 10; + ce->vib_idx += ce->vib_speed; + ce->vib_idx %= (32 << 5); + + return vib; +} + + +int libxmp_med_linear_bend(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_instrument *xxi = &m->mod.xxi[xc->ins]; + struct med_module_extras *me = (struct med_module_extras *)m->extra; + struct med_channel_extras *ce = (struct med_channel_extras *)xc->extra; + struct med_instrument_extras *ie = MED_INSTRUMENT_EXTRAS(*xxi); + int arp; + + /* Arpeggio */ + + if (ce->arp == 0) + return 0; + + if (ARP(ce->arp) == 0xfd) /* empty arpeggio */ + return 0; + + arp = ARP(ce->aidx); + if (arp == 0xfd) { + ce->aidx = ce->arp; + arp = ARP(ce->aidx); + } + ce->aidx++; + + return (100 << 7) * arp; +} + + +void libxmp_med_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct module_data *m = &ctx->m; + struct player_data *p = &ctx->p; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &m->mod.xxi[xc->ins]; + struct med_module_extras *me; + struct med_channel_extras *ce; + struct med_instrument_extras *ie; + int b, jws = 0, jvs = 0, loop; + int temp; + + if (!HAS_MED_MODULE_EXTRAS(*m)) + return; + + me = (struct med_module_extras *)m->extra; + ce = (struct med_channel_extras *)xc->extra; + ie = MED_INSTRUMENT_EXTRAS(*xxi); + + /* Handle hold/decay */ + + /* on the first row of a held note, continue note if ce->hold is 2 + * (this is set after pre-fetching the next row and see if we + * continue to hold. On remaining rows with hold on, we have the + * FX_MED_HOLD effect and ce->hold set to 1. On the last row, see + * if ce->hold_count is set (meaning that a note was held) and + * ce->hold is 0 (meaning that it's not held anymore). Then + * procceed with normal frame counting until decay. + */ + + if (ce->hold_count) { /* was held in the past */ + if (!ce->hold && p->frame >= ie->hold) { /* but not now */ + SET_NOTE(NOTE_FADEOUT); + ce->hold_count = 0; + } + } else if (ie->hold) { /* has instrument hold */ + if (p->frame >= ie->hold && ce->hold == 0) { + SET_NOTE(NOTE_FADEOUT); + } + } + + if (p->frame == (p->speed - 1) && ce->hold != 2) { + ce->hold = 0; + } + + /* Handle synth */ + + if (me->vol_table[xc->ins] == NULL || me->wav_table[xc->ins] == NULL) { + ce->volume = 64; /* we need this in extras_get_volume() */ + return; + } + + if (p->frame == 0 && TEST(NEW_NOTE)) { + ce->period = xc->period; + if (TEST(NEW_INS)) { + ce->arp = ce->aidx = 0; + ce->vp = ce->vc = ce->vw = 0; + ce->wp = ce->wc = ce->ww = 0; + ce->env_wav = -1; + ce->env_idx = 0; + ce->flags &= ~MED_SYNTH_ENV_LOOP; + ce->vv = 0; + ce->wv = 0; + ce->vs = ie->vts; + ce->ws = ie->wts; + } + } + + if (ce->vs > 0 && ce->vc-- == 0) { + ce->vc = ce->vs - 1; + + if (ce->vw > 0) { + ce->vw--; + goto skip_vol; + } + + loop = jws = 0; + + /* Volume commands */ + + next_vt: + switch (b = VT) { + case 0xff: /* END */ + case 0xfb: /* HLT */ + ce->vp--; + break; + case 0xfe: /* JMP */ + if (loop) /* avoid infinite loop */ + break; + temp = VT; + ce->vp = temp; + loop = 1; + goto next_vt; + case 0xfa: /* JWS */ + jws = VT; + break; + case 0xf5: /* EN2 */ + ce->env_wav = VT; + ce->flags |= MED_SYNTH_ENV_LOOP; + break; + case 0xf4: /* EN1 */ + ce->env_wav = VT; + break; + case 0xf3: /* CHU */ + ce->vv = VT; + break; + case 0xf2: /* CHD */ + ce->vv = -VT; + break; + case 0xf1: /* WAI */ + ce->vw = VT; + break; + case 0xf0: /* SPD */ + ce->vs = VT; + break; + default: + if (b >= 0x00 && b <= 0x40) + ce->volume = b; + } + + skip_vol: + /* volume envelope */ + if (ce->env_wav >= 0 && ce->env_wav < xxi->nsm) { + int sid = xxi->sub[ce->env_wav].sid; + struct xmp_sample *xxs = &mod->xxs[sid]; + if (xxs->len == 0x80) { /* sanity check */ + ce->volume = ((int8)xxs->data[ce->env_idx] + 0x80) >> 2; + ce->env_idx++; + + if (ce->env_idx >= 0x80) { + if (~ce->flags & MED_SYNTH_ENV_LOOP) { + ce->env_wav = -1; + } + ce->env_idx = 0; + } + } + } + + ce->volume += ce->vv; + CLAMP(ce->volume, 0, 64); + + if (ce->ww > 0) { + ce->ww--; + goto skip_wav; + } + + loop = jvs = 0; + + /* Waveform commands */ + + next_wt: + switch (b = WT) { + case 0xff: /* END */ + case 0xfb: /* HLT */ + ce->wp--; + break; + case 0xfe: /* JMP */ + if (loop) /* avoid infinite loop */ + break; + temp = WT; + if (temp == 0xff) { /* handle JMP END case */ + ce->wp--; /* see lepeltheme ins 0x02 */ + break; + } + ce->wp = temp; + loop = 1; + goto next_wt; + case 0xfd: /* ARE */ + break; + case 0xfc: /* ARP */ + ce->arp = ce->aidx = ce->wp++; + while (b != 0xfd && b != 0xff) b = WT; + break; + case 0xfa: /* JVS */ + jvs = WT; + break; + case 0xf7: /* VWF */ + ce->vwf = WT; + break; + case 0xf6: /* RES */ + xc->period = ce->period; + break; + case 0xf5: /* VBS */ + ce->vib_speed = WT; + break; + case 0xf4: /* VBD */ + ce->vib_depth = WT; + break; + case 0xf3: /* CHU */ + ce->wv = -WT; + break; + case 0xf2: /* CHD */ + ce->wv = WT; + break; + case 0xf1: /* WAI */ + ce->ww = WT; + break; + case 0xf0: /* SPD */ + ce->ws = WT; + break; + default: + if (b < xxi->nsm && xxi->sub[b].sid != xc->smp) { + xc->smp = xxi->sub[b].sid; + libxmp_virt_setsmp(ctx, chn, xc->smp); + } + } + + skip_wav: + xc->period += ce->wv; + } + + if (jws) { + ce->wp = jws; + /* jws = 0; */ + } + + if (jvs) { + ce->vp = jvs; + /* jvs = 0; */ + } +} + +int libxmp_med_new_instrument_extras(struct xmp_instrument *xxi) +{ + xxi->extra = calloc (1, sizeof(struct med_instrument_extras)); + if (xxi->extra == NULL) + return -1; + MED_INSTRUMENT_EXTRAS((*xxi))->magic = MED_EXTRAS_MAGIC; + + return 0; +} + +int libxmp_med_new_channel_extras(struct channel_data *xc) +{ + xc->extra = calloc(1, sizeof(struct med_channel_extras)); + if (xc->extra == NULL) + return -1; + MED_CHANNEL_EXTRAS((*xc))->magic = MED_EXTRAS_MAGIC; + + return 0; +} + +void libxmp_med_reset_channel_extras(struct channel_data *xc) +{ + memset((char *)xc->extra + 4, 0, sizeof(struct med_channel_extras) - 4); +} + +void libxmp_med_release_channel_extras(struct channel_data *xc) +{ + free(xc->extra); + xc->extra = NULL; +} + +int libxmp_med_new_module_extras(struct module_data *m) +{ + struct med_module_extras *me; + struct xmp_module *mod = &m->mod; + + m->extra = calloc(1, sizeof(struct med_module_extras)); + if (m->extra == NULL) + return -1; + MED_MODULE_EXTRAS((*m))->magic = MED_EXTRAS_MAGIC; + + me = (struct med_module_extras *)m->extra; + + me->vol_table = (uint8 **) calloc(sizeof(uint8 *), mod->ins); + if (me->vol_table == NULL) + return -1; + me->wav_table = (uint8 **) calloc(sizeof(uint8 *), mod->ins); + if (me->wav_table == NULL) + return -1; + + return 0; +} + +void libxmp_med_release_module_extras(struct module_data *m) +{ + struct med_module_extras *me; + struct xmp_module *mod = &m->mod; + int i; + + me = (struct med_module_extras *)m->extra; + + if (me->vol_table) { + for (i = 0; i < mod->ins; i++) + free(me->vol_table[i]); + free(me->vol_table); + } + + if (me->wav_table) { + for (i = 0; i < mod->ins; i++) + free(me->wav_table[i]); + free(me->wav_table); + } + + free(m->extra); + m->extra = NULL; +} + +void libxmp_med_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + switch (fxt) { + case FX_MED_HOLD: + MED_CHANNEL_EXTRAS((*xc))->hold_count++; + MED_CHANNEL_EXTRAS((*xc))->hold = 1; + break; + } +} + +void libxmp_med_hold_hack(struct context_data *ctx, int pat, int chn, int row) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + const int num_rows = mod->xxt[TRACK_NUM(pat, chn)]->rows; + + if (row + 1 < num_rows) { + struct player_data *p = &ctx->p; + struct xmp_event *event = &EVENT(pat, chn, row + 1); + struct channel_data *xc = &p->xc_data[chn]; + + if (event->f2t == FX_MED_HOLD) { + MED_CHANNEL_EXTRAS(*xc)->hold = 2; + } + } +} diff --git a/thirdparty/libxmp/src/med_extras.h b/thirdparty/libxmp/src/med_extras.h new file mode 100644 index 0000000..63087a1 --- /dev/null +++ b/thirdparty/libxmp/src/med_extras.h @@ -0,0 +1,77 @@ +#ifndef LIBXMP_MED_EXTRAS_H +#define LIBXMP_MED_EXTRAS_H + +#define MED_EXTRAS_MAGIC 0x7f20ca5 + +struct med_instrument_extras { + uint32 magic; + int vts; /* Volume table speed */ + int wts; /* Waveform table speed */ + int vtlen; /* Volume table length */ + int wtlen; /* Waveform table length */ + int hold; +}; + +struct med_channel_extras { + uint32 magic; + int vp; /* MED synth volume table pointer */ + int vv; /* MED synth volume slide value */ + int vs; /* MED synth volume speed */ + int vc; /* MED synth volume speed counter */ + int vw; /* MED synth volume wait counter */ + int wp; /* MED synth waveform table pointer */ + int wv; /* MED synth waveform slide value */ + int ws; /* MED synth waveform speed */ + int wc; /* MED synth waveform speed counter */ + int ww; /* MED synth waveform wait counter */ + int period; /* MED synth period for RES */ + int arp; /* MED synth arpeggio start */ + int aidx; /* MED synth arpeggio index */ + int vwf; /* MED synth vibrato waveform */ + int vib_depth; /* MED synth vibrato depth */ + int vib_speed; /* MED synth vibrato speed */ + int vib_idx; /* MED synth vibrato index */ + int vib_wf; /* MED synth vibrato waveform */ + int volume; /* MED synth note volume */ + int hold; /* MED note on hold flag */ + int hold_count; /* MED note on hold frame counter */ + int env_wav; /* MED synth volume envelope waveform */ + int env_idx; /* MED synth volume envelope index */ +#define MED_SYNTH_ENV_LOOP (1 << 0) + int flags; /* flags */ +}; + +struct med_module_extras { + uint32 magic; + uint8 **vol_table; /* MED volume sequence table */ + uint8 **wav_table; /* MED waveform sequence table */ +}; + +#define MED_INSTRUMENT_EXTRAS(x) ((struct med_instrument_extras *)(x).extra) +#define HAS_MED_INSTRUMENT_EXTRAS(x) \ + (MED_INSTRUMENT_EXTRAS(x) != NULL && \ + MED_INSTRUMENT_EXTRAS(x)->magic == MED_EXTRAS_MAGIC) + +#define MED_CHANNEL_EXTRAS(x) ((struct med_channel_extras *)(x).extra) +#define HAS_MED_CHANNEL_EXTRAS(x) \ + (MED_CHANNEL_EXTRAS(x) != NULL && \ + MED_CHANNEL_EXTRAS(x)->magic == MED_EXTRAS_MAGIC) + +#define MED_MODULE_EXTRAS(x) ((struct med_module_extras *)(x).extra) +#define HAS_MED_MODULE_EXTRAS(x) \ + (MED_MODULE_EXTRAS(x) != NULL && \ + MED_MODULE_EXTRAS(x)->magic == MED_EXTRAS_MAGIC) + +int libxmp_med_change_period(struct context_data *, struct channel_data *); +int libxmp_med_linear_bend(struct context_data *, struct channel_data *); +int libxmp_med_get_vibrato(struct channel_data *); +void libxmp_med_play_extras(struct context_data *, struct channel_data *, int); +int libxmp_med_new_instrument_extras(struct xmp_instrument *); +int libxmp_med_new_channel_extras(struct channel_data *); +void libxmp_med_reset_channel_extras(struct channel_data *); +void libxmp_med_release_channel_extras(struct channel_data *); +int libxmp_med_new_module_extras(struct module_data *); +void libxmp_med_release_module_extras(struct module_data *); +void libxmp_med_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + +#endif diff --git a/thirdparty/libxmp/src/memio.c b/thirdparty/libxmp/src/memio.c new file mode 100644 index 0000000..b3b61cd --- /dev/null +++ b/thirdparty/libxmp/src/memio.c @@ -0,0 +1,118 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "memio.h" + +static inline ptrdiff_t CAN_READ(MFILE *m) +{ + return m->pos >= 0 ? m->size - m->pos : 0; +} + + +int mgetc(MFILE *m) +{ + if (CAN_READ(m) >= 1) + return *(uint8 *)(m->start + m->pos++); + return EOF; +} + +size_t mread(void *buf, size_t size, size_t num, MFILE *m) +{ + size_t should_read = size * num; + ptrdiff_t can_read = CAN_READ(m); + + if (!size || !num || can_read <= 0) { + return 0; + } + + if (should_read > can_read) { + memcpy(buf, m->start + m->pos, can_read); + m->pos += can_read; + + return can_read / size; + } else { + memcpy(buf, m->start + m->pos, should_read); + m->pos += should_read; + + return num; + } +} + + +int mseek(MFILE *m, long offset, int whence) +{ + ptrdiff_t ofs = offset; + + switch (whence) { + case SEEK_SET: + break; + case SEEK_CUR: + ofs += m->pos; + break; + case SEEK_END: + ofs += m->size; + break; + default: + return -1; + } + if (ofs < 0) return -1; + if (ofs > m->size) + ofs = m->size; + m->pos = ofs; + return 0; +} + +long mtell(MFILE *m) +{ + return (long)m->pos; +} + +int meof(MFILE *m) +{ + return CAN_READ(m) <= 0; +} + +MFILE *mopen(const void *ptr, long size, int free_after_use) +{ + MFILE *m; + + m = (MFILE *) malloc(sizeof(MFILE)); + if (m == NULL) + return NULL; + + m->start = (const unsigned char *)ptr; + m->pos = 0; + m->size = size; + m->free_after_use = free_after_use; + + return m; +} + +int mclose(MFILE *m) +{ + if (m->free_after_use) + free((void *)m->start); + free(m); + return 0; +} + diff --git a/thirdparty/libxmp/src/memio.h b/thirdparty/libxmp/src/memio.h new file mode 100644 index 0000000..66bdc09 --- /dev/null +++ b/thirdparty/libxmp/src/memio.h @@ -0,0 +1,26 @@ +#ifndef LIBXMP_MEMIO_H +#define LIBXMP_MEMIO_H + +#include +#include "common.h" + +typedef struct { + const unsigned char *start; + ptrdiff_t pos; + ptrdiff_t size; + int free_after_use; +} MFILE; + +LIBXMP_BEGIN_DECLS + +MFILE *mopen(const void *, long, int); +int mgetc(MFILE *stream); +size_t mread(void *, size_t, size_t, MFILE *); +int mseek(MFILE *, long, int); +long mtell(MFILE *); +int mclose(MFILE *); +int meof(MFILE *); + +LIBXMP_END_DECLS + +#endif diff --git a/thirdparty/libxmp/src/miniz.h b/thirdparty/libxmp/src/miniz.h new file mode 100644 index 0000000..9fcfffc --- /dev/null +++ b/thirdparty/libxmp/src/miniz.h @@ -0,0 +1,1422 @@ +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "11.0.2" +#define MZ_VERNUM 0xB002 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; +#else + MZ_TIME_T m_time; +#endif +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; + + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; +#else + mz_uint file_crc32; +#endif + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +#ifndef MINIZ_NO_STDIO +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/thirdparty/libxmp/src/misc.c b/thirdparty/libxmp/src/misc.c new file mode 100644 index 0000000..c775c04 --- /dev/null +++ b/thirdparty/libxmp/src/misc.c @@ -0,0 +1,30 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "xmp.h" + +int xmp_syserrno (void) +{ + return errno; +} + diff --git a/thirdparty/libxmp/src/mix_all.c b/thirdparty/libxmp/src/mix_all.c new file mode 100644 index 0000000..b39826c --- /dev/null +++ b/thirdparty/libxmp/src/mix_all.c @@ -0,0 +1,456 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "virtual.h" +#include "mixer.h" +#include "precomp_lut.h" + +/* Mixers + * + * To increase performance eight mixers are defined, one for each + * combination of the following parameters: interpolation, resolution + * and number of channels. + */ +#define NEAREST_NEIGHBOR() do { \ + smp_in = ((int16)sptr[pos] << 8); \ +} while (0) + +#define NEAREST_NEIGHBOR_16BIT() do { \ + smp_in = sptr[pos]; \ +} while (0) + +#define LINEAR_INTERP() do { \ + smp_l1 = ((int16)sptr[pos] << 8); \ + smp_dt = ((int16)sptr[pos + 1] << 8) - smp_l1; \ + smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \ +} while (0) + +#define LINEAR_INTERP_16BIT() do { \ + smp_l1 = sptr[pos]; \ + smp_dt = sptr[pos + 1] - smp_l1; \ + smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \ +} while (0) + +/* 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_SHIFT (SPLINE_QUANTBITS) + +/* log2(number) of precalculated splines (range is [4..14]) */ +#define SPLINE_FRACBITS 10 +#define SPLINE_LUTLEN (1L<> 6; \ + smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \ + cubic_spline_lut1[f] * sptr[pos ] + \ + cubic_spline_lut3[f] * sptr[pos + 2] + \ + cubic_spline_lut2[f] * sptr[pos + 1]) >> (SPLINE_SHIFT - 8); \ +} while (0) + +#define SPLINE_INTERP_16BIT() do { \ + int f = frac >> 6; \ + smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \ + cubic_spline_lut1[f] * sptr[pos ] + \ + cubic_spline_lut3[f] * sptr[pos + 2] + \ + cubic_spline_lut2[f] * sptr[pos + 1]) >> SPLINE_SHIFT; \ +} while (0) + +#define LOOP_AC for (; count > ramp; count--) + +#define LOOP for (; count; count--) + +#define UPDATE_POS() do { \ + frac += step; \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ +} while (0) + +#define MIX_MONO() do { \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define MIX_MONO_AC() do { \ + *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \ +} while (0) + +/* IT's WAV output driver uses a clamp that seems to roughly match this: + * compare the WAV output of OpenMPT env-flt-max.it and filter-reset.it */ +#define MIX_FILTER_CLAMP(a) CLAMP((a), -65536, 65535) + +#define MIX_MONO_FILTER() do { \ + sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sl); \ + fl2 = fl1; fl1 = sl; \ + *(buffer++) += sl * vl; \ +} while (0) + +#define MIX_MONO_FILTER_AC() do { \ + int vl = old_vl >> 8; \ + MIX_MONO_FILTER(); \ + old_vl += delta_l; \ +} while (0) + +#define MIX_STEREO() do { \ + *(buffer++) += smp_in * vr; \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define MIX_STEREO_AC() do { \ + *(buffer++) += smp_in * (old_vr >> 8); old_vr += delta_r; \ + *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \ +} while (0) + +#define MIX_STEREO_FILTER() do { \ + sr = (a0 * smp_in + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sr); \ + fr2 = fr1; fr1 = sr; \ + sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sl); \ + fl2 = fl1; fl1 = sl; \ + *(buffer++) += sr * vr; \ + *(buffer++) += sl * vl; \ +} while (0) + +#define MIX_STEREO_FILTER_AC() do { \ + int vr = old_vr >> 8; \ + int vl = old_vl >> 8; \ + MIX_STEREO_FILTER(); \ + old_vr += delta_r; \ + old_vl += delta_l; \ +} while (0) + +/* For "nearest" to be nearest neighbor (instead of floor), the position needs + * to be rounded. This only needs to be done once at the start of mixing, and + * is required for reverse samples to round the same as forward samples. + */ +#define NEAREST_ROUND() do { \ + frac += (1 << (SMIX_SHIFT - 1)); \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ +} while (0) + +#define VAR_NORM(x) \ + register int smp_in; \ + x *sptr = (x *)vi->sptr; \ + unsigned int pos = vi->pos; \ + int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos) + +#define VAR_LINEAR_MONO(x) \ + VAR_NORM(x); \ + int old_vl = vi->old_vl; \ + int smp_l1, smp_dt + +#define VAR_LINEAR_STEREO(x) \ + VAR_LINEAR_MONO(x); \ + int old_vr = vi->old_vr + +#define VAR_SPLINE_MONO(x) \ + int old_vl = vi->old_vl; \ + VAR_NORM(x) + +#define VAR_SPLINE_STEREO(x) \ + VAR_SPLINE_MONO(x); \ + int old_vr = vi->old_vr + +#ifndef LIBXMP_CORE_DISABLE_IT + +#define VAR_FILTER_MONO \ + int fl1 = vi->filter.l1, fl2 = vi->filter.l2; \ + int64 a0 = vi->filter.a0, b0 = vi->filter.b0, b1 = vi->filter.b1; \ + int sl + +#define VAR_FILTER_STEREO \ + VAR_FILTER_MONO; \ + int fr1 = vi->filter.r1, fr2 = vi->filter.r2; \ + int sr + +#define SAVE_FILTER_MONO() do { \ + vi->filter.l1 = fl1; \ + vi->filter.l2 = fl2; \ +} while (0) + +#define SAVE_FILTER_STEREO() do { \ + SAVE_FILTER_MONO(); \ + vi->filter.r1 = fr1; \ + vi->filter.r2 = fr2; \ +} while (0) + +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4457) /* shadowing */ +#endif + + +/* + * Nearest neighbor mixers + */ + +/* Handler for 8 bit samples, nearest neighbor mono output + */ +MIXER(mono_8bit_nearest) +{ + VAR_NORM(int8); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); } +} + + +/* Handler for 16 bit samples, nearest neighbor mono output + */ +MIXER(mono_16bit_nearest) +{ + VAR_NORM(int16); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 8 bit samples, nearest neighbor stereo output + */ +MIXER(stereo_8bit_nearest) +{ + VAR_NORM(int8); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, nearest neighbor stereo output + */ +MIXER(stereo_16bit_nearest) +{ + VAR_NORM(int16); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); } +} + + +/* + * Linear mixers + */ + +/* Handler for 8 bit samples, linear interpolated mono output + */ +MIXER(mono_8bit_linear) +{ + VAR_LINEAR_MONO(int8); + + LOOP_AC { LINEAR_INTERP(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, linear interpolated mono output + */ +MIXER(mono_16bit_linear) +{ + VAR_LINEAR_MONO(int16); + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 8 bit samples, linear interpolated stereo output + */ +MIXER(stereo_8bit_linear) +{ + VAR_LINEAR_STEREO(int8); + + LOOP_AC { LINEAR_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_STEREO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, linear interpolated stereo output + */ +MIXER(stereo_16bit_linear) +{ + VAR_LINEAR_STEREO(int16); + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); } +} + + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Handler for 8 bit samples, filtered linear interpolated mono output + */ +MIXER(mono_8bit_linear_filter) +{ + VAR_LINEAR_MONO(int8); + VAR_FILTER_MONO; + + LOOP_AC { LINEAR_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 16 bit samples, filtered linear interpolated mono output + */ +MIXER(mono_16bit_linear_filter) +{ + VAR_LINEAR_MONO(int16); + VAR_FILTER_MONO; + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 8 bit samples, filtered linear interpolated stereo output + */ +MIXER(stereo_8bit_linear_filter) +{ + VAR_LINEAR_STEREO(int8); + VAR_FILTER_STEREO; + + LOOP_AC { LINEAR_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +/* Handler for 16 bit samples, filtered linear interpolated stereo output + */ +MIXER(stereo_16bit_linear_filter) +{ + VAR_LINEAR_STEREO(int16); + VAR_FILTER_STEREO; + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +#endif + +/* + * Spline mixers + */ + +/* Handler for 8 bit samples, spline interpolated mono output + */ +MIXER(mono_8bit_spline) +{ + VAR_SPLINE_MONO(int8); + + LOOP_AC { SPLINE_INTERP(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, spline interpolated mono output + */ +MIXER(mono_16bit_spline) +{ + VAR_SPLINE_MONO(int16); + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 8 bit samples, spline interpolated stereo output + */ +MIXER(stereo_8bit_spline) +{ + VAR_SPLINE_STEREO(int8); + + LOOP_AC { SPLINE_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_STEREO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, spline interpolated stereo output + */ +MIXER(stereo_16bit_spline) +{ + VAR_SPLINE_STEREO(int16); + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); } +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Handler for 8 bit samples, filtered spline interpolated mono output + */ +MIXER(mono_8bit_spline_filter) +{ + VAR_SPLINE_MONO(int8); + VAR_FILTER_MONO; + + LOOP_AC { SPLINE_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 16 bit samples, filtered spline interpolated mono output + */ +MIXER(mono_16bit_spline_filter) +{ + VAR_SPLINE_MONO(int16); + VAR_FILTER_MONO; + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 8 bit samples, filtered spline interpolated stereo output + */ +MIXER(stereo_8bit_spline_filter) +{ + VAR_SPLINE_STEREO(int8); + VAR_FILTER_STEREO; + + LOOP_AC { SPLINE_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +/* Handler for 16 bit samples, filtered spline interpolated stereo output + */ +MIXER(stereo_16bit_spline_filter) +{ + VAR_SPLINE_STEREO(int16); + VAR_FILTER_STEREO; + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +#endif diff --git a/thirdparty/libxmp/src/mix_paula.c b/thirdparty/libxmp/src/mix_paula.c new file mode 100644 index 0000000..92ed61d --- /dev/null +++ b/thirdparty/libxmp/src/mix_paula.c @@ -0,0 +1,165 @@ +#include "common.h" + +#ifdef LIBXMP_PAULA_SIMULATOR +/* + * Based on Antti S. Lankila's reference code, modified for libxmp + * by Claudio Matsuoka. + */ +#include "virtual.h" +#include "mixer.h" +#include "paula.h" +#include "precomp_blep.h" + +void libxmp_paula_init(struct context_data *ctx, struct paula_state *paula) +{ + struct mixer_data *s = &ctx->s; + + paula->global_output_level = 0; + paula->active_bleps = 0; + paula->fdiv = (double)PAULA_HZ / s->freq; + paula->remainder = paula->fdiv; +} + +/* return output simulated as series of bleps */ +static int16 output_sample(struct paula_state *paula, int tabnum) +{ + int i; + int32 output; + + output = paula->global_output_level << BLEP_SCALE; + for (i = 0; i < paula->active_bleps; i++) { + int age = paula->blepstate[i].age; + int level = paula->blepstate[i].level; + output -= winsinc_integral[tabnum][age] * level; + } + output >>= BLEP_SCALE; + + if (output < -32768) + output = -32768; + else if (output > 32767) + output = 32767; + + return output; +} + +static void input_sample(struct paula_state *paula, int16 sample) +{ + if (sample != paula->global_output_level) { + /* Start a new blep: level is the difference, age (or phase) is 0 clocks. */ + if (paula->active_bleps > MAX_BLEPS - 1) { + D_(D_WARN "active blep list truncated!"); + paula->active_bleps = MAX_BLEPS - 1; + } + + /* Make room for new blep */ + memmove(&paula->blepstate[1], &paula->blepstate[0], + sizeof(struct blep_state) * paula->active_bleps); + + /* Update state to account for the new blep */ + paula->active_bleps++; + paula->blepstate[0].age = 0; + paula->blepstate[0].level = sample - paula->global_output_level; + paula->global_output_level = sample; + } +} + +static void do_clock(struct paula_state *paula, int cycles) +{ + int i; + + if (cycles <= 0) { + return; + } + + for (i = 0; i < paula->active_bleps; i++) { + paula->blepstate[i].age += cycles; + if (paula->blepstate[i].age >= BLEP_SIZE) { + paula->active_bleps = i; + break; + } + } +} + +#define LOOP for (; count; count--) + +#define UPDATE_POS(x) do { \ + frac += (x); \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ +} while (0) + +#define PAULA_SIMULATION(x) do { \ + int num_in = vi->paula->remainder / MINIMUM_INTERVAL; \ + int ministep = step / num_in; \ + int i; \ + \ + /* input is always sampled at a higher rate than output */ \ + for (i = 0; i < num_in - 1; i++) { \ + input_sample(vi->paula, sptr[pos]); \ + do_clock(vi->paula, MINIMUM_INTERVAL); \ + UPDATE_POS(ministep); \ + } \ + input_sample(vi->paula, sptr[pos]); \ + vi->paula->remainder -= num_in * MINIMUM_INTERVAL; \ + \ + do_clock(vi->paula, (int)vi->paula->remainder); \ + smp_in = output_sample(vi->paula, (x)); \ + do_clock(vi->paula, MINIMUM_INTERVAL - (int)vi->paula->remainder); \ + UPDATE_POS(step - (num_in - 1) * ministep); \ + \ + vi->paula->remainder += vi->paula->fdiv; \ +} while (0) + +#define MIX_MONO() do { \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define MIX_STEREO() do { \ + *(buffer++) += smp_in * vr; \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define VAR_NORM(x) \ + int smp_in; \ + x *sptr = (x *)vi->sptr; \ + unsigned int pos = vi->pos; \ + int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos) + +#define VAR_PAULA_MONO(x) \ + VAR_NORM(x); \ + vl <<= 8 + +#define VAR_PAULA(x) \ + VAR_NORM(x); \ + vl <<= 8; \ + vr <<= 8 + +MIXER(mono_a500) +{ + VAR_PAULA_MONO(int8); + + LOOP { PAULA_SIMULATION(0); MIX_MONO(); } +} + +MIXER(mono_a500_filter) +{ + VAR_PAULA_MONO(int8); + + LOOP { PAULA_SIMULATION(1); MIX_MONO(); } +} + +MIXER(stereo_a500) +{ + VAR_PAULA(int8); + + LOOP { PAULA_SIMULATION(0); MIX_STEREO(); } +} + +MIXER(stereo_a500_filter) +{ + VAR_PAULA(int8); + + LOOP { PAULA_SIMULATION(1); MIX_STEREO(); } +} + +#endif /* LIBXMP_PAULA_SIMULATOR */ diff --git a/thirdparty/libxmp/src/mixer.c b/thirdparty/libxmp/src/mixer.c new file mode 100644 index 0000000..168f5d5 --- /dev/null +++ b/thirdparty/libxmp/src/mixer.c @@ -0,0 +1,1028 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" +#include "virtual.h" +#include "mixer.h" +#include "period.h" +#include "player.h" /* for set_sample_end() */ + +#ifdef LIBXMP_PAULA_SIMULATOR +#include "paula.h" +#endif + + +#define FLAG_16_BITS 0x01 +#define FLAG_STEREO 0x02 +#define FLAG_FILTER 0x04 +#define FLAG_ACTIVE 0x10 +/* #define FLAG_SYNTH 0x20 */ +#define FIDX_FLAGMASK (FLAG_16_BITS | FLAG_STEREO | FLAG_FILTER) + +#define DOWNMIX_SHIFT 12 +#define LIM8_HI 127 +#define LIM8_LO -128 +#define LIM16_HI 32767 +#define LIM16_LO -32768 + +struct loop_data +{ +#define LOOP_PROLOGUE 1 +#define LOOP_EPILOGUE 2 + void *sptr; + int start; + int end; + int first_loop; + int _16bit; + int active; + uint32 prologue[LOOP_PROLOGUE]; + uint32 epilogue[LOOP_EPILOGUE]; +}; + +#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int) + +#define ANTICLICK_FPSHIFT 24 + +MIX_FN(mono_8bit_nearest); +MIX_FN(mono_8bit_linear); +MIX_FN(mono_16bit_nearest); +MIX_FN(mono_16bit_linear); +MIX_FN(stereo_8bit_nearest); +MIX_FN(stereo_8bit_linear); +MIX_FN(stereo_16bit_nearest); +MIX_FN(stereo_16bit_linear); +MIX_FN(mono_8bit_spline); +MIX_FN(mono_16bit_spline); +MIX_FN(stereo_8bit_spline); +MIX_FN(stereo_16bit_spline); + +#ifndef LIBXMP_CORE_DISABLE_IT +MIX_FN(mono_8bit_linear_filter); +MIX_FN(mono_16bit_linear_filter); +MIX_FN(stereo_8bit_linear_filter); +MIX_FN(stereo_16bit_linear_filter); +MIX_FN(mono_8bit_spline_filter); +MIX_FN(mono_16bit_spline_filter); +MIX_FN(stereo_8bit_spline_filter); +MIX_FN(stereo_16bit_spline_filter); +#endif + +#ifdef LIBXMP_PAULA_SIMULATOR +MIX_FN(mono_a500); +MIX_FN(mono_a500_filter); +MIX_FN(stereo_a500); +MIX_FN(stereo_a500_filter); +#endif + +/* Mixers array index: + * + * bit 0: 0=8 bit sample, 1=16 bit sample + * bit 1: 0=mono output, 1=stereo output + * bit 2: 0=unfiltered, 1=filtered + */ + +typedef void (*MIX_FP) (struct mixer_voice *, int32 *, int, int, int, int, int, int, int); + +static MIX_FP nearest_mixers[] = { + libxmp_mix_mono_8bit_nearest, + libxmp_mix_mono_16bit_nearest, + libxmp_mix_stereo_8bit_nearest, + libxmp_mix_stereo_16bit_nearest, + +#ifndef LIBXMP_CORE_DISABLE_IT + libxmp_mix_mono_8bit_nearest, + libxmp_mix_mono_16bit_nearest, + libxmp_mix_stereo_8bit_nearest, + libxmp_mix_stereo_16bit_nearest, +#endif +}; + +static MIX_FP linear_mixers[] = { + libxmp_mix_mono_8bit_linear, + libxmp_mix_mono_16bit_linear, + libxmp_mix_stereo_8bit_linear, + libxmp_mix_stereo_16bit_linear, + +#ifndef LIBXMP_CORE_DISABLE_IT + libxmp_mix_mono_8bit_linear_filter, + libxmp_mix_mono_16bit_linear_filter, + libxmp_mix_stereo_8bit_linear_filter, + libxmp_mix_stereo_16bit_linear_filter +#endif +}; + +static MIX_FP spline_mixers[] = { + libxmp_mix_mono_8bit_spline, + libxmp_mix_mono_16bit_spline, + libxmp_mix_stereo_8bit_spline, + libxmp_mix_stereo_16bit_spline, + +#ifndef LIBXMP_CORE_DISABLE_IT + libxmp_mix_mono_8bit_spline_filter, + libxmp_mix_mono_16bit_spline_filter, + libxmp_mix_stereo_8bit_spline_filter, + libxmp_mix_stereo_16bit_spline_filter +#endif +}; + +#ifdef LIBXMP_PAULA_SIMULATOR +static MIX_FP a500_mixers[] = { + libxmp_mix_mono_a500, + NULL, + libxmp_mix_stereo_a500, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +static MIX_FP a500led_mixers[] = { + libxmp_mix_mono_a500_filter, + NULL, + libxmp_mix_stereo_a500_filter, + NULL, + NULL, + NULL, + NULL, + NULL +}; +#endif + + +/* Downmix 32bit samples to 8bit, signed or unsigned, mono or stereo output */ +static void downmix_int_8bit(char *dest, int32 *src, int num, int amp, int offs) +{ + int smp; + int shift = DOWNMIX_SHIFT + 8 - amp; + + for (; num--; src++, dest++) { + smp = *src >> shift; + if (smp > LIM8_HI) { + *dest = LIM8_HI; + } else if (smp < LIM8_LO) { + *dest = LIM8_LO; + } else { + *dest = smp; + } + + if (offs) *dest += offs; + } +} + + +/* Downmix 32bit samples to 16bit, signed or unsigned, mono or stereo output */ +static void downmix_int_16bit(int16 *dest, int32 *src, int num, int amp, int offs) +{ + int smp; + int shift = DOWNMIX_SHIFT - amp; + + for (; num--; src++, dest++) { + smp = *src >> shift; + if (smp > LIM16_HI) { + *dest = LIM16_HI; + } else if (smp < LIM16_LO) { + *dest = LIM16_LO; + } else { + *dest = smp; + } + + if (offs) *dest += offs; + } +} + +static void anticlick(struct mixer_voice *vi) +{ + vi->flags |= ANTICLICK; + vi->old_vl = 0; + vi->old_vr = 0; +} + +/* Ok, it's messy, but it works :-) Hipolito */ +static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int count) +{ + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + int smp_l, smp_r; + int discharge = s->ticksize >> ANTICLICK_SHIFT; + int stepmul, stepval; + uint32 stepmul_sq; + + smp_r = vi->sright; + smp_l = vi->sleft; + vi->sright = vi->sleft = 0; + + if (smp_l == 0 && smp_r == 0) { + return; + } + + if (buf == NULL) { + buf = s->buf32; + count = discharge; + } else if (count > discharge) { + count = discharge; + } + + if (count <= 0) { + return; + } + + stepval = (1 << ANTICLICK_FPSHIFT) / count; + stepmul = stepval * count; + + if (~s->format & XMP_FORMAT_MONO) { + while ((stepmul -= stepval) > 0) { + /* Truncate to 16-bits of precision so the product is 32-bits. */ + stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16); + stepmul_sq *= stepmul_sq; + *buf++ += (stepmul_sq * (int64)smp_r) >> 32; + *buf++ += (stepmul_sq * (int64)smp_l) >> 32; + } + } else { + while ((stepmul -= stepval) > 0) { + stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16); + stepmul_sq *= stepmul_sq; + *buf++ += (stepmul_sq * (int64)smp_l) >> 32; + } + } +} + +static void set_sample_end(struct context_data *ctx, int voc, int end) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct channel_data *xc; + + if ((uint32)voc >= p->virt.maxvoc) + return; + + xc = &p->xc_data[vi->chn]; + + if (end) { + SET_NOTE(NOTE_SAMPLE_END); + vi->fidx &= ~FLAG_ACTIVE; + if (HAS_QUIRK(QUIRK_RSTCHN)) { + libxmp_virt_resetvoice(ctx, voc, 0); + } + } else { + RESET_NOTE(NOTE_SAMPLE_END); + } +} + +/* Back up sample data before and after loop and replace it for interpolation. + * TODO: use an overlap buffer like OpenMPT? This is easier, but a little dirty. */ +static void init_sample_wraparound(struct mixer_data *s, struct loop_data *ld, + struct mixer_voice *vi, struct xmp_sample *xxs) +{ + int bidir; + int i; + + if (!vi->sptr || s->interp == XMP_INTERP_NEAREST || (~xxs->flg & XMP_SAMPLE_LOOP)) { + ld->active = 0; + return; + } + + ld->sptr = vi->sptr; + ld->start = vi->start; + ld->end = vi->end; + ld->first_loop = !(vi->flags & SAMPLE_LOOP); + ld->_16bit = (xxs->flg & XMP_SAMPLE_16BIT); + ld->active = 1; + + bidir = vi->flags & VOICE_BIDIR; + + if (ld->_16bit) { + uint16 *start = (uint16 *)vi->sptr + vi->start; + uint16 *end = (uint16 *)vi->sptr + vi->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) { + int j = i - LOOP_PROLOGUE; + ld->prologue[i] = start[j]; + start[j] = bidir ? start[-1 - j] : end[j]; + } + } + for (i = 0; i < LOOP_EPILOGUE; i++) { + ld->epilogue[i] = end[i]; + end[i] = bidir ? end[-1 - i] : start[i]; + } + } else { + uint8 *start = (uint8 *)vi->sptr + vi->start; + uint8 *end = (uint8 *)vi->sptr + vi->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) { + int j = i - LOOP_PROLOGUE; + ld->prologue[i] = start[j]; + start[j] = bidir ? start[-1 - j] : end[j]; + } + } + for (i = 0; i < LOOP_EPILOGUE; i++) { + ld->epilogue[i] = end[i]; + end[i] = bidir ? end[-1 - i] : start[i]; + } + } +} + +/* Restore old sample data from before and after loop. */ +static void reset_sample_wraparound(struct loop_data *ld) +{ + int i; + + if (!ld->active) + return; + + if (ld->_16bit) { + uint16 *start = (uint16 *)ld->sptr + ld->start; + uint16 *end = (uint16 *)ld->sptr + ld->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) + start[i - LOOP_PROLOGUE] = ld->prologue[i]; + } + for (i = 0; i < LOOP_EPILOGUE; i++) + end[i] = ld->epilogue[i]; + } else { + uint8 *start = (uint8 *)ld->sptr + ld->start; + uint8 *end = (uint8 *)ld->sptr + ld->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) + start[i - LOOP_PROLOGUE] = ld->prologue[i]; + } + for (i = 0; i < LOOP_EPILOGUE; i++) + end[i] = ld->epilogue[i]; + } +} + +static int has_active_sustain_loop(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct module_data *m = &ctx->m; + return vi->smp < m->mod.smp && (xxs->flg & XMP_SAMPLE_SLOOP) && (~vi->flags & VOICE_RELEASE); +#else + return 0; +#endif +} + +static int has_active_loop(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs) +{ + return (xxs->flg & XMP_SAMPLE_LOOP) || has_active_sustain_loop(ctx, vi, xxs); +} + +/* Update the voice endpoints based on current sample loop state. */ +static void adjust_voice_end(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs, struct extra_sample_data *xtra) +{ + vi->flags &= ~VOICE_BIDIR; + + if (xtra && has_active_sustain_loop(ctx, vi, xxs)) { + vi->start = xtra->sus; + vi->end = xtra->sue; + if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) vi->flags |= VOICE_BIDIR; + + } else if (xxs->flg & XMP_SAMPLE_LOOP) { + vi->start = xxs->lps; + if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) { + vi->end = xxs->len; + } else { + vi->end = xxs->lpe; + if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) vi->flags |= VOICE_BIDIR; + } + } else { + vi->start = 0; + vi->end = xxs->len; + } +} + +static int loop_reposition(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs, struct extra_sample_data *xtra) +{ + int loop_changed = !(vi->flags & SAMPLE_LOOP); + + vi->flags |= SAMPLE_LOOP; + + if(loop_changed) + adjust_voice_end(ctx, vi, xxs, xtra); + + if (~vi->flags & VOICE_BIDIR) { + /* Reposition for next loop */ + if (~vi->flags & VOICE_REVERSE) + vi->pos -= vi->end - vi->start; + else + vi->pos += vi->end - vi->start; + } else { + /* Bidirectional loop: switch directions */ + vi->flags ^= VOICE_REVERSE; + + /* Wrap voice position around endpoint */ + if (vi->flags & VOICE_REVERSE) { + /* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software + * mixer, ping-pong loops are shortened by one sample." + */ + vi->pos = vi->end * 2 - ctx->s.bidir_adjust - vi->pos; + } else { + vi->pos = vi->start * 2 - vi->pos; + } + } + return loop_changed; +} + + +/* Prepare the mixer for the next tick */ +void libxmp_mixer_prepare(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int bytelen; + + s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000; + + if (s->ticksize < (1 << ANTICLICK_SHIFT)) + s->ticksize = 1 << ANTICLICK_SHIFT; + + bytelen = s->ticksize * sizeof(int32); + if (~s->format & XMP_FORMAT_MONO) { + bytelen *= 2; + } + memset(s->buf32, 0, bytelen); +} +/* Fill the output buffer calling one of the handlers. The buffer contains + * sound for one tick (a PAL frame or 1/50s for standard vblank-timed mods) + */ +void libxmp_mixer_softmixer(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct extra_sample_data *xtra; + struct xmp_sample *xxs; + struct mixer_voice *vi; + struct loop_data loop_data; + double step, step_dir; + int samples, size; + int vol, vol_l, vol_r, voc, usmp; + int prev_l, prev_r = 0; + int32 *buf_pos; + MIX_FP mix_fn; + MIX_FP *mixerset; + + switch (s->interp) { + case XMP_INTERP_NEAREST: + mixerset = nearest_mixers; + break; + case XMP_INTERP_LINEAR: + mixerset = linear_mixers; + break; + case XMP_INTERP_SPLINE: + mixerset = spline_mixers; + break; + default: + mixerset = linear_mixers; + } + +#ifdef LIBXMP_PAULA_SIMULATOR + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + if (p->filter) { + mixerset = a500led_mixers; + } else { + mixerset = a500_mixers; + } + } + } +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT + /* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software + * mixer, ping-pong loops are shortened by one sample." + */ + s->bidir_adjust = IS_PLAYER_MODE_IT() ? 1 : 0; +#endif + + libxmp_mixer_prepare(ctx); + + for (voc = 0; voc < p->virt.maxvoc; voc++) { + int c5spd, rampsize, delta_l, delta_r; + + vi = &p->virt.voice_array[voc]; + + if (vi->flags & ANTICLICK) { + if (s->interp > XMP_INTERP_NEAREST) { + do_anticlick(ctx, voc, NULL, 0); + } + vi->flags &= ~ANTICLICK; + } + + if (vi->chn < 0) { + continue; + } + + if (vi->period < 1) { + libxmp_virt_resetvoice(ctx, voc, 1); + continue; + } + + /* Negative positions can be left over from some + * loop edge cases. These can be safely clamped. */ + if (vi->pos < 0.0) + vi->pos = 0.0; + + vi->pos0 = vi->pos; + + buf_pos = s->buf32; + vol = vi->vol; + + /* Mix volume (S3M and IT) */ + if (m->mvolbase > 0 && m->mvol != m->mvolbase) { + vol = vol * m->mvol / m->mvolbase; + } + + if (vi->pan == PAN_SURROUND) { + vol_r = vol * 0x80; + vol_l = -vol * 0x80; + } else { + vol_r = vol * (0x80 - vi->pan); + vol_l = vol * (0x80 + vi->pan); + } + + if (vi->smp < mod->smp) { + xxs = &mod->xxs[vi->smp]; + xtra = &m->xtra[vi->smp]; + c5spd = m->xtra[vi->smp].c5spd; + } else { + xxs = &ctx->smix.xxs[vi->smp - mod->smp]; + xtra = NULL; + c5spd = m->c4rate; + } + + step = C4_PERIOD * c5spd / s->freq / vi->period; + + /* Don't allow <=0, otherwise m5v-nwlf.it crashes + * Extremely high values that can cause undefined float/int + * conversion are also possible for c5spd modules. */ + if (step < 0.001 || step > (double)SHRT_MAX) { + continue; + } + + adjust_voice_end(ctx, vi, xxs, xtra); + init_sample_wraparound(s, &loop_data, vi, xxs); + + rampsize = s->ticksize >> ANTICLICK_SHIFT; + delta_l = (vol_l - vi->old_vl) / rampsize; + delta_r = (vol_r - vi->old_vr) / rampsize; + + for (size = usmp = s->ticksize; size > 0; ) { + int split_noloop = 0; + + if (p->xc_data[vi->chn].split) { + split_noloop = 1; + } + + /* How many samples we can write before the loop break + * or sample end... */ + if (~vi->flags & VOICE_REVERSE) { + if (vi->pos >= vi->end) { + samples = 0; + if (--usmp <= 0) + break; + } else { + double c = ceil(((double)vi->end - vi->pos) / step); + /* ...inside the tick boundaries */ + if (c > size) { + c = size; + } + samples = c; + } + step_dir = step; + } else { + /* Reverse */ + if (vi->pos <= vi->start) { + samples = 0; + if (--usmp <= 0) + break; + } else { + double c = ceil((vi->pos - (double)vi->start) / step); + if (c > size) { + c = size; + } + samples = c; + } + step_dir = -step; + } + + if (vi->vol) { + int mix_size = samples; + int mixer_id = vi->fidx & FIDX_FLAGMASK; + + if (~s->format & XMP_FORMAT_MONO) { + mix_size *= 2; + } + + /* For Hipolito's anticlick routine */ + if (samples > 0) { + if (~s->format & XMP_FORMAT_MONO) { + prev_r = buf_pos[mix_size - 2]; + } + prev_l = buf_pos[mix_size - 1]; + } else { + prev_r = prev_l = 0; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + /* See OpenMPT env-flt-max.it */ + if (vi->filter.cutoff >= 0xfe && + vi->filter.resonance == 0) { + mixer_id &= ~FLAG_FILTER; + } +#endif + + mix_fn = mixerset[mixer_id]; + + /* Call the output handler */ + if (samples > 0 && vi->sptr != NULL) { + int rsize = 0; + + if (rampsize > samples) { + rampsize -= samples; + } else { + rsize = samples - rampsize; + rampsize = 0; + } + + if (delta_l == 0 && delta_r == 0) { + /* no need to ramp */ + rsize = samples; + } + + if (mix_fn != NULL) { + mix_fn(vi, buf_pos, samples, + vol_l >> 8, vol_r >> 8, step_dir * (1 << SMIX_SHIFT), rsize, delta_l, delta_r); + } + + buf_pos += mix_size; + vi->old_vl += samples * delta_l; + vi->old_vr += samples * delta_r; + + + /* For Hipolito's anticlick routine */ + if (~s->format & XMP_FORMAT_MONO) { + vi->sright = buf_pos[-2] - prev_r; + } + vi->sleft = buf_pos[-1] - prev_l; + } + } + + vi->pos += step_dir * samples; + + /* No more samples in this tick */ + size -= samples; + if (size <= 0) { + if (has_active_loop(ctx, vi, xxs)) { + /* This isn't particularly important for + * forward loops, but reverse loops need + * to be corrected here to avoid their + * negative positions getting clamped + * in later ticks. */ + if (((~vi->flags & VOICE_REVERSE) && vi->pos >= vi->end) || + ((vi->flags & VOICE_REVERSE) && vi->pos <= vi->start)) { + if (loop_reposition(ctx, vi, xxs, xtra)) { + reset_sample_wraparound(&loop_data); + init_sample_wraparound(s, &loop_data, vi, xxs); + } + } + } + continue; + } + + /* First sample loop run */ + if (!has_active_loop(ctx, vi, xxs) || split_noloop) { + do_anticlick(ctx, voc, buf_pos, size); + set_sample_end(ctx, voc, 1); + size = 0; + continue; + } + + if (loop_reposition(ctx, vi, xxs, xtra)) { + reset_sample_wraparound(&loop_data); + init_sample_wraparound(s, &loop_data, vi, xxs); + } + } + + reset_sample_wraparound(&loop_data); + vi->old_vl = vol_l; + vi->old_vr = vol_r; + } + + /* Render final frame */ + + size = s->ticksize; + if (~s->format & XMP_FORMAT_MONO) { + size *= 2; + } + + if (size > XMP_MAX_FRAMESIZE) { + size = XMP_MAX_FRAMESIZE; + } + + if (s->format & XMP_FORMAT_8BIT) { + downmix_int_8bit(s->buffer, s->buf32, size, s->amplify, + s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0); + } else { + downmix_int_16bit((int16 *)s->buffer, s->buf32, size, s->amplify, + s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0); + } + + s->dtright = s->dtleft = 0; +} + +void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct xmp_sample *xxs; + struct extra_sample_data *xtra; + + if (vi->smp < m->mod.smp) { + xxs = &m->mod.xxs[vi->smp]; + xtra = &m->xtra[vi->smp]; + } else { + xxs = &ctx->smix.xxs[vi->smp - m->mod.smp]; + xtra = NULL; + } + + if (xxs->flg & XMP_SAMPLE_SYNTH) { + return; + } + + vi->pos = pos; + + adjust_voice_end(ctx, vi, xxs, xtra); + + if (vi->pos >= vi->end) { + vi->pos = vi->end; + /* Restart forward sample loops. */ + if ((~vi->flags & VOICE_REVERSE) && has_active_loop(ctx, vi, xxs)) + loop_reposition(ctx, vi, xxs, xtra); + } else if ((vi->flags & VOICE_REVERSE) && vi->pos <= 0.1) { + /* Hack: 0 maps to the end for reversed samples. */ + vi->pos = vi->end; + } + + if (ac) { + anticlick(vi); + } +} + +double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct xmp_sample *xxs; + + xxs = libxmp_get_sample(ctx, vi->smp); + + if (xxs->flg & XMP_SAMPLE_SYNTH) { + return 0; + } + + return vi->pos; +} + +void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac) +{ + struct player_data *p = &ctx->p; +#ifndef LIBXMP_CORE_DISABLE_IT + struct module_data *m = &ctx->m; +#endif + struct mixer_data *s = &ctx->s; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct xmp_sample *xxs; + + xxs = libxmp_get_sample(ctx, smp); + + vi->smp = smp; + vi->vol = 0; + vi->pan = 0; + vi->flags &= ~(SAMPLE_LOOP | VOICE_REVERSE | VOICE_BIDIR); + + vi->fidx = 0; + + if (~s->format & XMP_FORMAT_MONO) { + vi->fidx |= FLAG_STEREO; + } + + set_sample_end(ctx, voc, 0); + + /*mixer_setvol(ctx, voc, 0);*/ + + vi->sptr = xxs->data; + vi->fidx |= FLAG_ACTIVE; + +#ifndef LIBXMP_CORE_DISABLE_IT + if (HAS_QUIRK(QUIRK_FILTER) && s->dsp & XMP_DSP_LOWPASS) { + vi->fidx |= FLAG_FILTER; + } +#endif + + if (xxs->flg & XMP_SAMPLE_16BIT) { + vi->fidx |= FLAG_16_BITS; + } + + libxmp_mixer_voicepos(ctx, voc, 0, ac); +} + +void libxmp_mixer_setnote(struct context_data *ctx, int voc, int note) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + /* FIXME: Workaround for crash on notes that are too high + * see 6nations.it (+114 transposition on instrument 16) + */ + if (note > 149) { + note = 149; + } + + vi->note = note; + vi->period = libxmp_note_to_period_mix(note, 0); + + anticlick(vi); +} + +void libxmp_mixer_setperiod(struct context_data *ctx, int voc, double period) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + vi->period = period; +} + +void libxmp_mixer_setvol(struct context_data *ctx, int voc, int vol) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + if (vol == 0) { + anticlick(vi); + } + + vi->vol = vol; +} + +void libxmp_mixer_release(struct context_data *ctx, int voc, int rel) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + if (rel) { +#ifndef LIBXMP_CORE_DISABLE_IT + /* Cancel voice reverse when releasing an active sustain loop, + * unless the main loop is bidirectional. This is done both for + * bidirectional sustain loops and for forward sustain loops + * that have been reversed with MPT S9F Play Backward. */ + if (~vi->flags & VOICE_RELEASE) { + struct xmp_sample *xxs = libxmp_get_sample(ctx, vi->smp); + + if (has_active_sustain_loop(ctx, vi, xxs) && + (~xxs->flg & XMP_SAMPLE_LOOP_BIDIR)) + vi->flags &= ~VOICE_REVERSE; + } +#endif + vi->flags |= VOICE_RELEASE; + } else { + vi->flags &= ~VOICE_RELEASE; + } +} + +void libxmp_mixer_reverse(struct context_data *ctx, int voc, int rev) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + /* Don't reverse samples that have already ended */ + if (~vi->fidx & FLAG_ACTIVE) { + return; + } + + if (rev) { + vi->flags |= VOICE_REVERSE; + } else { + vi->flags &= ~VOICE_REVERSE; + } +} + +void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + switch (type) { + case DSP_EFFECT_CUTOFF: + vi->filter.cutoff = val; + break; + case DSP_EFFECT_RESONANCE: + vi->filter.resonance = val; + break; + case DSP_EFFECT_FILTER_A0: + vi->filter.a0 = val; + break; + case DSP_EFFECT_FILTER_B0: + vi->filter.b0 = val; + break; + case DSP_EFFECT_FILTER_B1: + vi->filter.b1 = val; + break; + } +#endif +} + +void libxmp_mixer_setpan(struct context_data *ctx, int voc, int pan) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + vi->pan = pan; +} + +int libxmp_mixer_numvoices(struct context_data *ctx, int num) +{ + struct mixer_data *s = &ctx->s; + + if (num > s->numvoc || num < 0) { + return s->numvoc; + } else { + return num; + } +} + +int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate) +{ + struct mixer_data *s = &ctx->s; + + s->buffer = (char *) calloc(2, XMP_MAX_FRAMESIZE); + if (s->buffer == NULL) + goto err; + + s->buf32 = (int32 *) calloc(sizeof(int32), XMP_MAX_FRAMESIZE); + if (s->buf32 == NULL) + goto err1; + + s->freq = rate; + s->format = format; + s->amplify = DEFAULT_AMPLIFY; + s->mix = DEFAULT_MIX; + /* s->pbase = C4_PERIOD * c4rate / s->freq; */ + s->interp = XMP_INTERP_LINEAR; /* default interpolation type */ + s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */ + /* s->numvoc = SMIX_NUMVOC; */ + s->dtright = s->dtleft = 0; + s->bidir_adjust = 0; + + return 0; + + err1: + free(s->buffer); + s->buffer = NULL; + err: + return -1; +} + +void libxmp_mixer_off(struct context_data *ctx) +{ + struct mixer_data *s = &ctx->s; + + free(s->buffer); + free(s->buf32); + s->buf32 = NULL; + s->buffer = NULL; +} diff --git a/thirdparty/libxmp/src/mixer.h b/thirdparty/libxmp/src/mixer.h new file mode 100644 index 0000000..c80b1e8 --- /dev/null +++ b/thirdparty/libxmp/src/mixer.h @@ -0,0 +1,83 @@ +#ifndef LIBXMP_MIXER_H +#define LIBXMP_MIXER_H + +#define C4_PERIOD 428.0 + +#define SMIX_NUMVOC 128 /* default number of softmixer voices */ +#define SMIX_SHIFT 16 +#define SMIX_MASK 0xffff + +#define FILTER_SHIFT 16 +#define ANTICLICK_SHIFT 3 + +#ifdef LIBXMP_PAULA_SIMULATOR +#include "paula.h" +#endif + +#define MIXER(f) void libxmp_mix_##f(struct mixer_voice *vi, int *buffer, \ + int count, int vl, int vr, int step, int ramp, int delta_l, int delta_r) + +struct mixer_voice { + int chn; /* channel number */ + int root; /* */ + int note; /* */ +#define PAN_SURROUND 0x8000 + int pan; /* */ + int vol; /* */ + double period; /* current period */ + double pos; /* position in sample */ + int pos0; /* position in sample before mixing */ + int fidx; /* mixer function index */ + int ins; /* instrument number */ + int smp; /* sample number */ + int start; /* loop start */ + int end; /* loop end */ + int act; /* nna info & status of voice */ + int key; /* key for DCA note check */ + int old_vl; /* previous volume, left channel */ + int old_vr; /* previous volume, right channel */ + int sleft; /* last left sample output, in 32bit */ + int sright; /* last right sample output, in 32bit */ +#define VOICE_RELEASE (1 << 0) +#define ANTICLICK (1 << 1) +#define SAMPLE_LOOP (1 << 2) +#define VOICE_REVERSE (1 << 3) +#define VOICE_BIDIR (1 << 4) + int flags; /* flags */ + void *sptr; /* sample pointer */ +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula; /* paula simulation state */ +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT + struct { + int r1; /* filter variables */ + int r2; + int l1; + int l2; + int a0; + int b0; + int b1; + int cutoff; + int resonance; + } filter; +#endif +}; + +int libxmp_mixer_on (struct context_data *, int, int, int); +void libxmp_mixer_off (struct context_data *); +void libxmp_mixer_setvol (struct context_data *, int, int); +void libxmp_mixer_seteffect (struct context_data *, int, int, int); +void libxmp_mixer_setpan (struct context_data *, int, int); +int libxmp_mixer_numvoices (struct context_data *, int); +void libxmp_mixer_softmixer (struct context_data *); +void libxmp_mixer_reset (struct context_data *); +void libxmp_mixer_setpatch (struct context_data *, int, int, int); +void libxmp_mixer_voicepos (struct context_data *, int, double, int); +double libxmp_mixer_getvoicepos(struct context_data *, int); +void libxmp_mixer_setnote (struct context_data *, int, int); +void libxmp_mixer_setperiod (struct context_data *, int, double); +void libxmp_mixer_release (struct context_data *, int, int); +void libxmp_mixer_reverse (struct context_data *, int, int); + +#endif /* LIBXMP_MIXER_H */ diff --git a/thirdparty/libxmp/src/mkstemp.c b/thirdparty/libxmp/src/mkstemp.c new file mode 100644 index 0000000..1ef25af --- /dev/null +++ b/thirdparty/libxmp/src/mkstemp.c @@ -0,0 +1,105 @@ +#ifdef __SUNPRO_C +#pragma error_messages (off,E_EMPTY_TRANSLATION_UNIT) +#endif + +#include "common.h" + +#if !(defined(LIBXMP_NO_PROWIZARD) && defined(LIBXMP_NO_DEPACKERS)) + +#ifndef HAVE_MKSTEMP + +/* + * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + */ + +#include +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif +#if defined(_MSC_VER) || defined(__WATCOMC__) +#include +#else +#include +#endif +#ifdef _MSC_VER +#include +#define open _open +#endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +int mkstemp(char *pattern) +{ + int start, i; +#ifdef _WIN32 + int val = GetCurrentProcessId(); +#else + pid_t val = getpid(); +#endif + + start = strlen(pattern) - 1; + + while (pattern[start] == 'X') { + pattern[start] = '0' + val % 10; + val /= 10; + start--; + } + + do { + int fd; + fd = open(pattern, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); + if (fd >= 0 || errno != EEXIST) + return fd; + i = start + 1; + do { + if (pattern[i] == 0) + return -1; + pattern[i]++; + if (pattern[i] == '9' + 1) + pattern[i] = 'a'; + if (pattern[i] <= 'z') + break; + pattern[i] = 'a'; + i++; + } while (1); + } while (1); +} + +#endif + +#endif diff --git a/thirdparty/libxmp/src/paula.h b/thirdparty/libxmp/src/paula.h new file mode 100644 index 0000000..8f3f381 --- /dev/null +++ b/thirdparty/libxmp/src/paula.h @@ -0,0 +1,37 @@ +#ifndef LIBXMP_PAULA_H +#define LIBXMP_PAULA_H + +/* 131072 to 0, 2048 entries */ +#define PAULA_HZ 3546895 +#define MINIMUM_INTERVAL 16 +#define BLEP_SCALE 17 +#define BLEP_SIZE 2048 +#define MAX_BLEPS (BLEP_SIZE / MINIMUM_INTERVAL) + +/* the structure that holds data of bleps */ +struct blep_state { + int16 level; + int16 age; +}; + +struct paula_state { + /* the instantenous value of Paula output */ + int16 global_output_level; + + /* count of simultaneous bleps to keep track of */ + unsigned int active_bleps; + + /* place to keep our bleps in. MAX_BLEPS should be + * defined as a BLEP_SIZE / MINIMUM_EVENT_INTERVAL. + * For Paula, minimum event interval could be even 1, but it makes + * sense to limit it to some higher value such as 16. */ + struct blep_state blepstate[MAX_BLEPS]; + + double remainder; + double fdiv; +}; + + +void libxmp_paula_init (struct context_data *, struct paula_state *); + +#endif /* !LIBXMP_PAULA_H */ diff --git a/thirdparty/libxmp/src/period.c b/thirdparty/libxmp/src/period.c new file mode 100644 index 0000000..2cbf955 --- /dev/null +++ b/thirdparty/libxmp/src/period.c @@ -0,0 +1,285 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "common.h" +#include "period.h" + +#include + +#ifdef LIBXMP_PAULA_SIMULATOR +/* + * Period table from the Protracker V2.1A play routine + */ +static uint16 pt_period_table[16][36] = { + /* Tuning 0, Normal */ + { + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113 + }, + /* Tuning 1 */ + { + 850,802,757,715,674,637,601,567,535,505,477,450, + 425,401,379,357,337,318,300,284,268,253,239,225, + 213,201,189,179,169,159,150,142,134,126,119,113 + }, + /* Tuning 2 */ + { + 844,796,752,709,670,632,597,563,532,502,474,447, + 422,398,376,355,335,316,298,282,266,251,237,224, + 211,199,188,177,167,158,149,141,133,125,118,112 + }, + /* Tuning 3 */ + { + 838,791,746,704,665,628,592,559,528,498,470,444, + 419,395,373,352,332,314,296,280,264,249,235,222, + 209,198,187,176,166,157,148,140,132,125,118,111 + }, + /* Tuning 4 */ + { + 832,785,741,699,660,623,588,555,524,495,467,441, + 416,392,370,350,330,312,294,278,262,247,233,220, + 208,196,185,175,165,156,147,139,131,124,117,110 + }, + /* Tuning 5 */ + { + 826,779,736,694,655,619,584,551,520,491,463,437, + 413,390,368,347,328,309,292,276,260,245,232,219, + 206,195,184,174,164,155,146,138,130,123,116,109 + }, + /* Tuning 6 */ + { + 820,774,730,689,651,614,580,547,516,487,460,434, + 410,387,365,345,325,307,290,274,258,244,230,217, + 205,193,183,172,163,154,145,137,129,122,115,109 + }, + /* Tuning 7 */ + { + 814,768,725,684,646,610,575,543,513,484,457,431, + 407,384,363,342,323,305,288,272,256,242,228,216, + 204,192,181,171,161,152,144,136,128,121,114,108 + }, + /* Tuning -8 */ + { + 907,856,808,762,720,678,640,604,570,538,508,480, + 453,428,404,381,360,339,320,302,285,269,254,240, + 226,214,202,190,180,170,160,151,143,135,127,120 + }, + /* Tuning -7 */ + { + 900,850,802,757,715,675,636,601,567,535,505,477, + 450,425,401,379,357,337,318,300,284,268,253,238, + 225,212,200,189,179,169,159,150,142,134,126,119 + }, + /* Tuning -6 */ + { + 894,844,796,752,709,670,632,597,563,532,502,474, + 447,422,398,376,355,335,316,298,282,266,251,237, + 223,211,199,188,177,167,158,149,141,133,125,118 + }, + /* Tuning -5 */ + { + 887,838,791,746,704,665,628,592,559,528,498,470, + 444,419,395,373,352,332,314,296,280,264,249,235, + 222,209,198,187,176,166,157,148,140,132,125,118 + }, + /* Tuning -4 */ + { + 881,832,785,741,699,660,623,588,555,524,494,467, + 441,416,392,370,350,330,312,294,278,262,247,233, + 220,208,196,185,175,165,156,147,139,131,123,117 + }, + /* Tuning -3 */ + { + 875,826,779,736,694,655,619,584,551,520,491,463, + 437,413,390,368,347,328,309,292,276,260,245,232, + 219,206,195,184,174,164,155,146,138,130,123,116 + }, + /* Tuning -2 */ + { + 868,820,774,730,689,651,614,580,547,516,487,460, + 434,410,387,365,345,325,307,290,274,258,244,230, + 217,205,193,183,172,163,154,145,137,129,122,115 + }, + /* Tuning -1 */ + { + 862,814,768,725,684,646,610,575,543,513,484,457, + 431,407,384,363,342,323,305,288,272,256,242,228, + 216,203,192,181,171,161,152,144,136,128,121,114 + } +}; +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +static inline double libxmp_round(double val) +{ + return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5); +} + +#ifdef LIBXMP_PAULA_SIMULATOR +/* Get period from note using Protracker tuning */ +static inline int libxmp_note_to_period_pt(int n, int f) +{ + if (n < MIN_NOTE_MOD || n > MAX_NOTE_MOD) { + return -1; + } + + n -= 48; + f >>= 4; + if (f < -8 || f > 7) { + return 0; + } + + if (f < 0) { + f += 16; + } + + return (int)pt_period_table[f][n]; +} +#endif + +/* Get period from note */ +double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj) +{ + double d, per; + struct module_data *m = &ctx->m; +#ifdef LIBXMP_PAULA_SIMULATOR + struct player_data *p = &ctx->p; + + /* If mod replayer, modrng and Amiga mixing are active */ + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + return libxmp_note_to_period_pt(n, f); + } + } +#endif + + d = (double)n + (double)f / 128; + + switch (m->period_type) { + case PERIOD_LINEAR: + per = (240.0 - d) * 16; /* Linear */ + break; + case PERIOD_CSPD: + per = 8363.0 * pow(2, n / 12.0) / 32 + f; /* Hz */ + break; + default: + per = PERIOD_BASE / pow(2, d / 12); /* Amiga */ + } + +#ifndef LIBXMP_CORE_PLAYER + if (adj > 0.1) { + per *= adj; + } +#endif + + return per; +} + +/* For the software mixer */ +double libxmp_note_to_period_mix(int n, int b) +{ + double d = (double)n + (double)b / 12800; + return PERIOD_BASE / pow(2, d / 12); +} + +/* Get note from period */ +/* This function is used only by the MOD loader */ +int libxmp_period_to_note(int p) +{ + if (p <= 0) { + return 0; + } + + return libxmp_round(12.0 * log(PERIOD_BASE / p) / M_LN2) + 1; +} + +/* Get pitchbend from base note and amiga period */ +int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj) +{ + struct module_data *m = &ctx->m; + double d; + + if (n == 0 || p < 0.1) { + return 0; + } + + switch (m->period_type) { + case PERIOD_LINEAR: + return 100 * (8 * (((240 - n) << 4) - p)); + case PERIOD_CSPD: + d = libxmp_note_to_period(ctx, n, 0, adj); + return libxmp_round(100.0 * (1536.0 / M_LN2) * log(p / d)); + default: + /* Amiga */ + d = libxmp_note_to_period(ctx, n, 0, adj); + return libxmp_round(100.0 * (1536.0 / M_LN2) * log(d / p)); + } +} + +/* Convert finetune = 1200 * log2(C2SPD/8363)) + * + * c = (1200.0 * log(c2spd) - 1200.0 * log(c4_rate)) / M_LN2; + * xpo = c/100; + * fin = 128 * (c%100) / 100; + */ +void libxmp_c2spd_to_note(int c2spd, int *n, int *f) +{ + int c; + + if (c2spd <= 0) { + *n = *f = 0; + return; + } + + c = (int)(1536.0 * log((double)c2spd / 8363) / M_LN2); + *n = c / 128; + *f = c % 128; +} + +#ifndef LIBXMP_CORE_PLAYER +/* Gravis Ultrasound frequency increments in steps of Hz/1024, where Hz is the + * current rate of the card and is dependent on the active channel count. + * For <=14 channels, the rate is 44100. For 15 to 32 channels, the rate is + * round(14 * 44100 / active_channels). + */ +static const double GUS_rates[19] = { + /* <= 14 */ 44100.0, + /* 15-20 */ 41160.0, 38587.5, 36317.65, 34300.0, 32494.74, 30870.0, + /* 21-26 */ 29400.0, 28063.64, 26843.48, 25725.0, 24696.0, 23746.15, + /* 27-32 */ 22866.67, 22050.0, 21289.66, 20580.0, 19916.13, 19294.75 +}; + +/* Get a Gravis Ultrasound frequency offset in Hz for a given number of steps. + */ +double libxmp_gus_frequency_steps(int num_steps, int num_channels_active) +{ + CLAMP(num_channels_active, 14, 32); + return (num_steps * GUS_rates[num_channels_active - 14]) / 1024.0; +} +#endif diff --git a/thirdparty/libxmp/src/period.h b/thirdparty/libxmp/src/period.h new file mode 100644 index 0000000..bf4bd17 --- /dev/null +++ b/thirdparty/libxmp/src/period.h @@ -0,0 +1,27 @@ +#ifndef LIBXMP_PERIOD_H +#define LIBXMP_PERIOD_H + +#define PERIOD_BASE 13696.0 /* C0 period */ + +/* Macros for period conversion */ +#define NOTE_B0 11 +#define NOTE_Bb0 (NOTE_B0 + 1) +#define MAX_NOTE (NOTE_B0 * 8) +#define MAX_PERIOD 0x1c56 +#define MIN_PERIOD_A 0x0071 +#define MAX_PERIOD_A 0x0358 +#define MIN_PERIOD_L 0x0000 +#define MAX_PERIOD_L 0x1e00 +#define MIN_NOTE_MOD 48 +#define MAX_NOTE_MOD 83 + +double libxmp_note_to_period (struct context_data *, int, int, double); +double libxmp_note_to_period_mix (int, int); +int libxmp_period_to_note (int); +int libxmp_period_to_bend (struct context_data *, double, int, double); +void libxmp_c2spd_to_note (int, int *, int *); +#ifndef LIBXMP_CORE_PLAYER +double libxmp_gus_frequency_steps (int, int); +#endif + +#endif /* LIBXMP_PERIOD_H */ diff --git a/thirdparty/libxmp/src/player.c b/thirdparty/libxmp/src/player.c new file mode 100644 index 0000000..4eacab6 --- /dev/null +++ b/thirdparty/libxmp/src/player.c @@ -0,0 +1,2216 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Sat, 18 Apr 1998 20:23:07 +0200 Frederic Bujon + * Pan effect bug fixed: In Fastracker II the track panning effect erases + * the instrument panning effect, and the same should happen in xmp. + */ + +/* + * Fri, 26 Jun 1998 13:29:25 -0400 (EDT) + * Reported by Jared Spiegel + * when the volume envelope is not enabled (disabled) on a sample, and a + * notoff is delivered to ft2 (via either a noteoff in the note column or + * command Kxx [where xx is # of ticks into row to give a noteoff to the + * sample]), ft2 will set the volume of playback of the sample to 00h. + * + * Claudio's fix: implementing effect K + */ + +#include "virtual.h" +#include "period.h" +#include "effects.h" +#include "player.h" +#include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "extras.h" +#endif + +/* Values for multi-retrig */ +static const struct retrig_control rval[] = { + { 0, 1, 1 }, { -1, 1, 1 }, { -2, 1, 1 }, { -4, 1, 1 }, + { -8, 1, 1 }, { -16, 1, 1 }, { 0, 2, 3 }, { 0, 1, 2 }, + { 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 }, + { 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 }, + { 0, 0, 1 } /* Note cut */ +}; + + +/* + * "Anyway I think this is the most brilliant piece of crap we + * have managed to put up!" + * -- Ice of FC about "Mental Surgery" + */ + + +/* Envelope */ + +static int check_envelope_end(struct xmp_envelope *env, int x) +{ + int16 *data = env->data; + int idx; + + if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) + return 0; + + idx = (env->npt - 1) * 2; + + /* last node */ + if (x >= data[idx] || idx == 0) { + if (~env->flg & XMP_ENVELOPE_LOOP) { + return 1; + } + } + + return 0; +} + +static int get_envelope(struct xmp_envelope *env, int x, int def) +{ + int x1, x2, y1, y2; + int16 *data = env->data; + int idx; + + if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) + return def; + + idx = (env->npt - 1) * 2; + + x1 = data[idx]; /* last node */ + if (x >= x1 || idx == 0) { + return data[idx + 1]; + } + + do { + idx -= 2; + x1 = data[idx]; + } while (idx > 0 && x1 > x); + + /* interpolate */ + y1 = data[idx + 1]; + x2 = data[idx + 2]; + y2 = data[idx + 3]; + + /* Interpolation requires x1 <= x <= x2 */ + if (x < x1 || x2 < x1) return y1; + + return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1; +} + +static int update_envelope_xm(struct xmp_envelope *env, int x, int release) +{ + int16 *data = env->data; + int has_loop, has_sus; + int lpe, lps, sus; + + has_loop = env->flg & XMP_ENVELOPE_LOOP; + has_sus = env->flg & XMP_ENVELOPE_SUS; + + lps = env->lps << 1; + lpe = env->lpe << 1; + sus = env->sus << 1; + + /* FT2 and IT envelopes behave in a different way regarding loops, + * sustain and release. When the sustain point is at the end of the + * envelope loop end and the key is released, FT2 escapes the loop + * while IT runs another iteration. (See EnvLoops.xm in the OpenMPT + * test cases.) + */ + if (has_loop && has_sus && sus == lpe) { + if (!release) + has_sus = 0; + } + + /* If the envelope point is set to somewhere after the sustain point + * or sustain loop, enable release to prevent the envelope point to + * return to the sustain point or loop start. (See Filip Skutela's + * farewell_tear.xm.) + */ + if (has_loop && x > data[lpe] + 1) { + release = 1; + } else if (has_sus && x > data[sus] + 1) { + release = 1; + } + + /* If enabled, stay at the sustain point */ + if (has_sus && !release) { + if (x >= data[sus]) { + x = data[sus]; + } + } + + /* Envelope loops */ + if (has_loop && x >= data[lpe]) { + if (!(release && has_sus && sus == lpe)) + x = data[lps]; + } + + return x; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static int update_envelope_it(struct xmp_envelope *env, int x, int release, int key_off) +{ + int16 *data = env->data; + int has_loop, has_sus; + int lpe, lps, sus, sue; + + has_loop = env->flg & XMP_ENVELOPE_LOOP; + has_sus = env->flg & XMP_ENVELOPE_SUS; + + lps = env->lps << 1; + lpe = env->lpe << 1; + sus = env->sus << 1; + sue = env->sue << 1; + + /* Release at the end of a sustain loop, run another loop */ + if (has_sus && key_off && x == data[sue] + 1) { + x = data[sus]; + } else + /* If enabled, stay in the sustain loop */ + if (has_sus && !release) { + if (x == data[sue] + 1) { + x = data[sus]; + } + } else + /* Finally, execute the envelope loop */ + if (has_loop) { + if (x > data[lpe]) { + x = data[lps]; + } + } + + return x; +} + +#endif + +static int update_envelope(struct xmp_envelope *env, int x, int release, int key_off, int it_env) +{ + if (x < 0xffff) { /* increment tick */ + x++; + } + + if (x < 0) { + return -1; + } + + if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) { + return x; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + return it_env ? + update_envelope_it(env, x, release, key_off) : + update_envelope_xm(env, x, release); +#else + return update_envelope_xm(env, x, release); +#endif +} + + +/* Returns: 0 if do nothing, <0 to reset channel, >0 if has fade */ +static int check_envelope_fade(struct xmp_envelope *env, int x) +{ + int16 *data = env->data; + int idx; + + if (~env->flg & XMP_ENVELOPE_ON) + return 0; + + idx = (env->npt - 1) * 2; /* last node */ + if (x > data[idx]) { + if (data[idx + 1] == 0) + return -1; + else + return 1; + } + + return 0; +} + + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Impulse Tracker's filter effects are implemented using its MIDI macros. + * Any module can customize these and they are parameterized using various + * player and mixer values, which requires parsing them here instead of in + * the loader. Since they're MIDI macros, they can contain actual MIDI junk + * that needs to be skipped, and one macro may have multiple IT commands. */ + +struct midi_stream +{ + const char *pos; + int buffer; + int param; +}; + +static int midi_nibble(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_stream *in) +{ + struct xmp_instrument *xxi; + struct mixer_voice *vi; + int voc, val, byte = -1; + if (in->buffer >= 0) { + val = in->buffer; + in->buffer = -1; + return val; + } + + while (*in->pos) { + val = *(in->pos)++; + if (val >= '0' && val <= '9') return val - '0'; + if (val >= 'A' && val <= 'F') return val - 'A' + 10; + switch (val) { + case 'z': /* Macro parameter */ + byte = in->param; + break; + case 'n': /* Host key */ + byte = xc->key & 0x7f; + break; + case 'h': /* Host channel */ + byte = chn; + break; + case 'o': /* Offset effect memory */ + /* Intentionally not clamped, see ZxxSecrets.it */ + byte = xc->offset.memory; + break; + case 'm': /* Voice reverse flag */ + voc = libxmp_virt_mapchannel(ctx, chn); + vi = (voc >= 0) ? &ctx->p.virt.voice_array[voc] : NULL; + byte = vi ? !!(vi->flags & VOICE_REVERSE) : 0; + break; + case 'v': /* Note velocity */ + xxi = libxmp_get_instrument(ctx, xc->ins); + byte = ((uint32)ctx->p.gvol * + (uint32)xc->volume * + (uint32)xc->mastervol * + (uint32)xc->gvl * + (uint32)(xxi ? xxi->vol : 0x40)) >> 24UL; + CLAMP(byte, 1, 127); + break; + case 'u': /* Computed velocity */ + byte = xc->macro.finalvol >> 3; + CLAMP(byte, 1, 127); + break; + case 'x': /* Note panning */ + byte = xc->macro.notepan >> 1; + CLAMP(byte, 0, 127); + break; + case 'y': /* Computed panning */ + byte = xc->info_finalpan >> 1; + CLAMP(byte, 0, 127); + break; + case 'a': /* Ins MIDI Bank hi */ + case 'b': /* Ins MIDI Bank lo */ + case 'p': /* Ins MIDI Program */ + case 's': /* MPT: SysEx checksum */ + byte = 0; + break; + case 'c': /* Ins MIDI Channel */ + return 0; + } + + /* Byte output */ + if (byte >= 0) { + in->buffer = byte & 0xf; + return (byte >> 4) & 0xf; + } + } + return -1; +} + +static int midi_byte(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_stream *in) +{ + int a = midi_nibble(ctx, xc, chn, in); + int b = midi_nibble(ctx, xc, chn, in); + return (a >= 0 && b >= 0) ? (a << 4) | b : -1; +} + +static void apply_midi_macro_effect(struct channel_data *xc, int type, int val) +{ + switch (type) { + case 0: /* Filter cutoff */ + xc->filter.cutoff = val << 1; + break; + case 1: /* Filter resonance */ + xc->filter.resonance = val << 1; + break; + } +} + +static void execute_midi_macro(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_macro *midi, int param) +{ + struct midi_stream in; + int byte, cmd, val; + + in.pos = midi->data; + in.buffer = -1; + in.param = param; + + while (*in.pos) { + /* Very simple MIDI 1.0 parser--most bytes can just be ignored + * (or passed through, if libxmp gets MIDI output). All bytes + * with bit 7 are statuses which interrupt unfinished messages + * ("Data Types: Status Bytes") or are real time messages. + * This holds even for SysEx messages, which end at ANY non- + * real time status ("System Common Messages: EOX"). + * + * IT intercepts internal "messages" that begin with F0 F0, + * which in MIDI is a useless zero-length SysEx followed by + * a second SysEx. They are four bytes long including F0 F0, + * and shouldn't be passed through. OpenMPT also uses F0 F1. + */ + cmd = -1; + byte = midi_byte(ctx, xc, chn, &in); + if (byte == 0xf0) { + byte = midi_byte(ctx, xc, chn, &in); + if (byte == 0xf0 || byte == 0xf1) + cmd = byte & 0xf; + } + if (cmd < 0) { + if (byte == 0xfa || byte == 0xfc || byte == 0xff) { + /* These real time statuses can appear anywhere + * (even in SysEx) and reset the channel filter + * params. See: OpenMPT ZxxSecrets.it */ + apply_midi_macro_effect(xc, 0, 127); + apply_midi_macro_effect(xc, 1, 0); + } + continue; + } + cmd = midi_byte(ctx, xc, chn, &in) | (cmd << 8); + val = midi_byte(ctx, xc, chn, &in); + if (cmd < 0 || cmd >= 0x80 || val < 0 || val >= 0x80) { + continue; + } + apply_midi_macro_effect(xc, cmd, val); + } +} + +/* This needs to occur before all process_* functions: + * - It modifies the filter parameters, used by process_frequency. + * - process_volume and process_pan apply slide effects, which the + * filter parameters expect to occur after macro effect parsing. */ +static void update_midi_macro(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct midi_macro_data *midicfg = m->midi; + struct midi_macro *macro; + int val; + + if (TEST(MIDI_MACRO) && HAS_QUIRK(QUIRK_FILTER)) { + if (xc->macro.slide > 0) { + xc->macro.val += xc->macro.slide; + if (xc->macro.val > xc->macro.target) { + xc->macro.val = xc->macro.target; + xc->macro.slide = 0; + } + } else if (xc->macro.slide < 0) { + xc->macro.val += xc->macro.slide; + if (xc->macro.val < xc->macro.target) { + xc->macro.val = xc->macro.target; + xc->macro.slide = 0; + } + } else if (p->frame) { + /* Execute non-smooth macros on frame 0 only */ + return; + } + + val = (int)xc->macro.val; + if (val >= 0x80) { + if (midicfg) { + macro = &midicfg->fixed[val - 0x80]; + execute_midi_macro(ctx, xc, chn, macro, val); + } else if (val < 0x90) { + /* Default fixed macro: set resonance */ + apply_midi_macro_effect(xc, 1, (val - 0x80) << 3); + } + } else if (midicfg) { + macro = &midicfg->param[xc->macro.active]; + execute_midi_macro(ctx, xc, chn, macro, val); + } else if (xc->macro.active == 0) { + /* Default parameterized macro 0: set filter cutoff */ + apply_midi_macro_effect(xc, 0, val); + } + } +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ + + +#ifndef LIBXMP_CORE_PLAYER + +/* From http://www.un4seen.com/forum/?topic=7554.0 + * + * "Invert loop" effect replaces (!) sample data bytes within loop with their + * bitwise complement (NOT). The parameter sets speed of altering the samples. + * This effectively trashes the sample data. Because of that this effect was + * supposed to be removed in the very next ProTracker versions, but it was + * never removed. + * + * Prior to [Protracker 1.1A] this effect is called "Funk Repeat" and it moves + * loop of the instrument (just the loop information - sample data is not + * altered). The parameter is the speed of moving the loop. + */ + +static const int invloop_table[] = { + 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 +}; + +static void update_invloop(struct context_data *ctx, struct channel_data *xc) +{ + struct xmp_sample *xxs = libxmp_get_sample(ctx, xc->smp); + struct module_data *m = &ctx->m; + int lps, len = -1; + + xc->invloop.count += invloop_table[xc->invloop.speed]; + + if (xxs != NULL) { + if (xxs->flg & XMP_SAMPLE_LOOP) { + lps = xxs->lps; + len = xxs->lpe - lps; + } else if (xxs->flg & XMP_SAMPLE_SLOOP) { + /* Some formats that support invert loop use sustain + * loops instead (Digital Symphony). */ + lps = m->xtra[xc->smp].sus; + len = m->xtra[xc->smp].sue - lps; + } + } + + if (len >= 0 && xc->invloop.count >= 128) { + xc->invloop.count = 0; + + if (++xc->invloop.pos > len) { + xc->invloop.pos = 0; + } + + if (xxs->data == NULL) { + return; + } + + if (~xxs->flg & XMP_SAMPLE_16BIT) { + xxs->data[lps + xc->invloop.pos] ^= 0xff; + } + } +} + +#endif + +/* + * From OpenMPT Arpeggio.xm test: + * + * "[FT2] Arpeggio behavior is very weird with more than 16 ticks per row. This + * comes from the fact that Fasttracker 2 uses a LUT for computing the arpeggio + * note (instead of doing something like tick%3 or similar). The LUT only has + * 16 entries, so when there are more than 16 ticks, it reads beyond array + * boundaries. The vibrato table happens to be stored right after arpeggio + * table. The tables look like this in memory: + * + * ArpTab: 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0 + * VibTab: 0,24,49,74,97,120,141,161,180,197,... + * + * All values except for the first in the vibrato table are greater than 1, so + * they trigger the third arpeggio note. Keep in mind that Fasttracker 2 counts + * downwards, so the table has to be read from back to front, i.e. at 16 ticks + * per row, the 16th entry in the LUT is the first to be read. This is also the + * reason why Arpeggio is played 'backwards' in Fasttracker 2." + */ +static int ft2_arpeggio(struct context_data *ctx, struct channel_data *xc) +{ + struct player_data *p = &ctx->p; + int i; + + if (xc->arpeggio.val[1] == 0 && xc->arpeggio.val[2] == 0) { + return 0; + } + + if (p->frame == 0) { + return 0; + } + + i = p->speed - (p->frame % p->speed); + + if (i == 16) { + return 0; + } else if (i > 16) { + return xc->arpeggio.val[2]; + } + + return xc->arpeggio.val[i % 3]; +} + +static int arpeggio(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + int arp; + + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + arp = ft2_arpeggio(ctx, xc); + } else { + arp = xc->arpeggio.val[xc->arpeggio.count]; + } + + xc->arpeggio.count++; + xc->arpeggio.count %= xc->arpeggio.size; + + return arp; +} + +static int is_first_frame(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + switch (m->read_event_type) { +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + /* fall through */ +#endif + case READ_EVENT_ST3: + return p->frame % p->speed == 0; + default: + return p->frame == 0; + } +} + +static void reset_channels(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + struct channel_data *xc; + int i; + +#ifndef LIBXMP_CORE_PLAYER + for (i = 0; i < p->virt.virt_channels; i++) { + void *extra; + + xc = &p->xc_data[i]; + extra = xc->extra; + memset(xc, 0, sizeof (struct channel_data)); + xc->extra = extra; + libxmp_reset_channel_extras(ctx, xc); + xc->ins = -1; + xc->old_ins = 1; /* raw value */ + xc->key = -1; + xc->volume = m->volbase; + } +#else + for (i = 0; i < p->virt.virt_channels; i++) { + xc = &p->xc_data[i]; + memset(xc, 0, sizeof (struct channel_data)); + xc->ins = -1; + xc->old_ins = 1; /* raw value */ + xc->key = -1; + xc->volume = m->volbase; + } +#endif + + for (i = 0; i < p->virt.num_tracks; i++) { + xc = &p->xc_data[i]; + + if (i >= mod->chn && i < mod->chn + smix->chn) { + xc->mastervol = 0x40; + xc->pan.val = 0x80; + } else { + xc->mastervol = mod->xxc[i].vol; + xc->pan.val = mod->xxc[i].pan; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + xc->filter.cutoff = 0xff; + + /* Amiga split channel */ + if (mod->xxc[i].flg & XMP_CHANNEL_SPLIT) { + int j; + + xc->split = ((mod->xxc[i].flg & 0x30) >> 4) + 1; + /* Connect split channel pairs */ + for (j = 0; j < i; j++) { + if (mod->xxc[j].flg & XMP_CHANNEL_SPLIT) { + if (p->xc_data[j].split == xc->split) { + p->xc_data[j].pair = i; + xc->pair = j; + } + } + } + } else { + xc->split = 0; + } +#endif + + /* Surround channel */ + if (mod->xxc[i].flg & XMP_CHANNEL_SURROUND) { + xc->pan.surround = 1; + } + } +} + +static int check_delay(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct module_data *m = &ctx->m; + + /* Tempo affects delay and must be computed first */ + if ((e->fxt == FX_SPEED && e->fxp < 0x20) || e->fxt == FX_S3M_SPEED) { + if (e->fxp) { + p->speed = e->fxp; + } + } + if ((e->f2t == FX_SPEED && e->f2p < 0x20) || e->f2t == FX_S3M_SPEED) { + if (e->f2p) { + p->speed = e->f2p; + } + } + + /* Delay event read */ + if (e->fxt == FX_EXTENDED && MSN(e->fxp) == EX_DELAY && LSN(e->fxp)) { + xc->delay = LSN(e->fxp) + 1; + goto do_delay; + } + if (e->f2t == FX_EXTENDED && MSN(e->f2p) == EX_DELAY && LSN(e->f2p)) { + xc->delay = LSN(e->f2p) + 1; + goto do_delay; + } + + return 0; + + do_delay: + memcpy(&xc->delayed_event, e, sizeof (struct xmp_event)); + + if (e->ins) { + xc->delayed_ins = e->ins; + } + + if (HAS_QUIRK(QUIRK_RTDELAY)) { + if (e->vol == 0 && e->f2t == 0 && e->ins == 0 && e->note != XMP_KEY_OFF) + xc->delayed_event.vol = xc->volume + 1; + if (e->note == 0) + xc->delayed_event.note = xc->key + 1; + if (e->ins == 0) + xc->delayed_event.ins = xc->old_ins; + } + + return 1; +} + +static inline void read_row(struct context_data *ctx, int pat, int row) +{ + int chn; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + struct xmp_event ev; + + for (chn = 0; chn < mod->chn; chn++) { + const int num_rows = mod->xxt[TRACK_NUM(pat, chn)]->rows; + if (row < num_rows) { + memcpy(&ev, &EVENT(pat, chn, row), sizeof(ev)); + } else { + memset(&ev, 0, sizeof(ev)); + } + + if (ev.note == XMP_KEY_OFF) { + int env_on = 0; + int ins = ev.ins - 1; + + if (IS_VALID_INSTRUMENT(ins) && + (mod->xxi[ins].aei.flg & XMP_ENVELOPE_ON)) { + env_on = 1; + } + + if (ev.fxt == FX_EXTENDED && MSN(ev.fxp) == EX_DELAY) { + if (ev.ins && (LSN(ev.fxp) || env_on)) { + if (LSN(ev.fxp)) { + ev.note = 0; + } + ev.fxp = ev.fxt = 0; + } + } + } + + if (check_delay(ctx, &ev, chn) == 0) { + /* rowdelay_set bit 1 is set only in the first tick of the row + * event if the delay causes the tick count resets to 0. We test + * it to read row events only in the start of the row. (see the + * OpenMPT test case FineVolColSlide.it) + */ + if (!f->rowdelay_set || ((f->rowdelay_set & ROWDELAY_FIRST_FRAME) && f->rowdelay > 0)) { + libxmp_read_event(ctx, &ev, chn); +#ifndef LIBXMP_CORE_PLAYER + libxmp_med_hold_hack(ctx, pat, chn, row); +#endif + } + } else { + if (IS_PLAYER_MODE_IT()) { + /* Reset flags. See SlideDelay.it */ + p->xc_data[chn].flags = 0; + } + } + } +} + +static inline int get_channel_vol(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int root; + + /* channel is a root channel */ + if (chn < p->virt.num_tracks) + return p->channel_vol[chn]; + + /* channel is invalid */ + if (chn >= p->virt.virt_channels) + return 0; + + /* root is invalid */ + root = libxmp_virt_getroot(ctx, chn); + if (root < 0) + return 0; + + return p->channel_vol[root]; +} + +static int tremor_ft2(struct context_data *ctx, int chn, int finalvol) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (xc->tremor.count & 0x80) { + if (TEST(TREMOR) && p->frame != 0) { + xc->tremor.count &= ~0x20; + if (xc->tremor.count == 0x80) { + /* end of down cycle, set up counter for up */ + xc->tremor.count = xc->tremor.up | 0xc0; + } else if (xc->tremor.count == 0xc0) { + /* end of up cycle, set up counter for down */ + xc->tremor.count = xc->tremor.down | 0x80; + } else { + xc->tremor.count--; + } + } + + if ((xc->tremor.count & 0xe0) == 0x80) { + finalvol = 0; + } + } + + return finalvol; +} + +static int tremor_s3m(struct context_data *ctx, int chn, int finalvol) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (TEST(TREMOR)) { + if (xc->tremor.count == 0) { + /* end of down cycle, set up counter for up */ + xc->tremor.count = xc->tremor.up | 0x80; + } else if (xc->tremor.count == 0x80) { + /* end of up cycle, set up counter for down */ + xc->tremor.count = xc->tremor.down; + } + + xc->tremor.count--; + + if (~xc->tremor.count & 0x80) { + finalvol = 0; + } + } + + return finalvol; +} + +/* + * Update channel data + */ + +#define DOENV_RELEASE ((TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF)) + +static void process_volume(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + int finalvol; + uint16 vol_envelope; + int fade = 0; + + instrument = libxmp_get_instrument(ctx, xc->ins); + + /* Keyoff and fadeout */ + + /* Keyoff event in IT doesn't reset fadeout (see jeff93.it) + * In XM it depends on envelope (see graff-strange_land.xm vs + * Decibelter - Cosmic 'Wegian Mamas.xm) + */ + if (HAS_QUIRK(QUIRK_KEYOFF)) { + /* If IT, only apply fadeout on note release if we don't + * have envelope, or if we have envelope loop + */ + if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) { + if ((~instrument->aei.flg & XMP_ENVELOPE_ON) || + (instrument->aei.flg & XMP_ENVELOPE_LOOP)) { + fade = 1; + } + } + } else { + if (~instrument->aei.flg & XMP_ENVELOPE_ON) { + if (TEST_NOTE(NOTE_ENV_RELEASE)) { + xc->fadeout = 0; + } + } + + if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) { + fade = 1; + } + } + + if (!TEST_PER(VENV_PAUSE)) { + xc->v_idx = update_envelope(&instrument->aei, xc->v_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + + vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64); + if (check_envelope_end(&instrument->aei, xc->v_idx)) { + if (vol_envelope == 0) { + SET_NOTE(NOTE_END); + } + SET_NOTE(NOTE_ENV_END); + } + + /* IT starts fadeout automatically at the end of the volume envelope. */ + switch (check_envelope_fade(&instrument->aei, xc->v_idx)) { + case -1: + SET_NOTE(NOTE_END); + /* Don't reset channel, we may have a tone portamento later + * virt_resetchannel(ctx, chn); + */ + break; + case 0: + break; + default: + if (HAS_QUIRK(QUIRK_ENVFADE)) { + SET_NOTE(NOTE_FADEOUT); + } + } + + /* IT envelope fadeout starts immediately after the envelope tick, + * so process fadeout after the volume envelope. */ + if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) { + fade = 1; + } + + if (fade) { + if (xc->fadeout > xc->ins_fade) { + xc->fadeout -= xc->ins_fade; + } else { + xc->fadeout = 0; + SET_NOTE(NOTE_END); + } + } + + /* If note ended in background channel, we can safely reset it */ + if (TEST_NOTE(NOTE_END) && chn >= p->virt.num_tracks) { + libxmp_virt_resetchannel(ctx, chn); + return; + } + +#ifndef LIBXMP_CORE_PLAYER + finalvol = libxmp_extras_get_volume(ctx, xc); +#else + finalvol = xc->volume; +#endif + + if (IS_PLAYER_MODE_IT()) { + finalvol = xc->volume * (100 - xc->rvv) / 100; + } + + if (TEST(TREMOLO)) { + /* OpenMPT VibratoReset.mod */ + if (!is_first_frame(ctx) || !HAS_QUIRK(QUIRK_PROTRACK)) { + finalvol += libxmp_lfo_get(ctx, &xc->tremolo.lfo, 0) / (1 << 6); + } + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_VIBALL)) { + libxmp_lfo_update(&xc->tremolo.lfo); + } + } + + CLAMP(finalvol, 0, m->volbase); + + finalvol = (finalvol * xc->fadeout) >> 6; /* 16 bit output */ + + finalvol = (uint32)(vol_envelope * p->gvol * xc->mastervol / + m->gvolbase * ((int)finalvol * 0x40 / m->volbase)) >> 18; + + /* Apply channel volume */ + finalvol = finalvol * get_channel_vol(ctx, chn) / 100; + +#ifndef LIBXMP_CORE_PLAYER + /* Volume translation table (for PTM, ARCH, COCO) */ + if (m->vol_table) { + finalvol = m->volbase == 0xff ? + m->vol_table[finalvol >> 2] << 2 : + m->vol_table[finalvol >> 4] << 4; + } +#endif + + if (HAS_QUIRK(QUIRK_INSVOL)) { + finalvol = (finalvol * instrument->vol * xc->gvl) >> 12; + } + + if (IS_PLAYER_MODE_FT2()) { + finalvol = tremor_ft2(ctx, chn, finalvol); + } else { + finalvol = tremor_s3m(ctx, chn, finalvol); + } +#ifndef LIBXMP_CORE_DISABLE_IT + xc->macro.finalvol = finalvol; +#endif + + if (chn < m->mod.chn) { + finalvol = finalvol * p->master_vol / 100; + } else { + finalvol = finalvol * p->smix_vol / 100; + } + + xc->info_finalvol = TEST_NOTE(NOTE_SAMPLE_END) ? 0 : finalvol; + + libxmp_virt_setvol(ctx, chn, finalvol); + + /* Check Amiga split channel */ + if (xc->split) { + libxmp_virt_setvol(ctx, xc->pair, finalvol); + } +} + +static void process_frequency(struct context_data *ctx, int chn, int act) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct mixer_data *s = &ctx->s; +#endif + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + double period, vibrato; + double final_period; + int linear_bend; + int frq_envelope; + int arp; +#ifndef LIBXMP_CORE_DISABLE_IT + int cutoff, resonance; +#endif + + instrument = libxmp_get_instrument(ctx, xc->ins); + + if (!TEST_PER(FENV_PAUSE)) { + xc->f_idx = update_envelope(&instrument->fei, xc->f_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + frq_envelope = get_envelope(&instrument->fei, xc->f_idx, 0); + +#ifndef LIBXMP_CORE_PLAYER + /* Do note slide */ + + if (TEST(NOTE_SLIDE)) { + if (xc->noteslide.count == 0) { + xc->note += xc->noteslide.slide; + xc->period = libxmp_note_to_period(ctx, xc->note, + xc->finetune, xc->per_adj); + xc->noteslide.count = xc->noteslide.speed; + } + xc->noteslide.count--; + + libxmp_virt_setnote(ctx, chn, xc->note); + } +#endif + + /* Instrument vibrato */ + vibrato = 1.0 * libxmp_lfo_get(ctx, &xc->insvib.lfo, 1) / + (4096 * (1 + xc->insvib.sweep)); + libxmp_lfo_update(&xc->insvib.lfo); + if (xc->insvib.sweep > 1) { + xc->insvib.sweep -= 2; + } else { + xc->insvib.sweep = 0; + } + + /* Vibrato */ + if (TEST(VIBRATO) || TEST_PER(VIBRATO)) { + /* OpenMPT VibratoReset.mod */ + if (!is_first_frame(ctx) || !HAS_QUIRK(QUIRK_PROTRACK)) { + int shift = HAS_QUIRK(QUIRK_VIBHALF) ? 10 : 9; + int vib = libxmp_lfo_get(ctx, &xc->vibrato.lfo, 1) / (1 << shift); + + if (HAS_QUIRK(QUIRK_VIBINV)) { + vibrato -= vib; + } else { + vibrato += vib; + } + } + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_VIBALL)) { + libxmp_lfo_update(&xc->vibrato.lfo); + } + } + + period = xc->period; +#ifndef LIBXMP_CORE_PLAYER + period += libxmp_extras_get_period(ctx, xc); +#endif + + if (HAS_QUIRK(QUIRK_ST3BUGS)) { + if (period < 0.25) { + libxmp_virt_resetchannel(ctx, chn); + } + } + /* Sanity check */ + if (period < 0.1) { + period = 0.1; + } + + /* Arpeggio */ + arp = arpeggio(ctx, xc); + + /* Pitch bend */ + + linear_bend = libxmp_period_to_bend(ctx, period + vibrato, xc->note, xc->per_adj); + + if (TEST_NOTE(NOTE_GLISSANDO) && TEST(TONEPORTA)) { + if (linear_bend > 0) { + linear_bend = (linear_bend + 6400) / 12800 * 12800; + } else if (linear_bend < 0) { + linear_bend = (linear_bend - 6400) / 12800 * 12800; + } + } + + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + if (arp) { + /* OpenMPT ArpSlide.xm */ + linear_bend = linear_bend / 12800 * 12800 + + xc->finetune * 100; + + /* OpenMPT ArpeggioClamp.xm */ + if (xc->note + arp > 107) { + if (p->speed - (p->frame % p->speed) > 0) { + arp = 108 - xc->note; + } + } + } + } + + /* Envelope */ + + if (xc->f_idx >= 0 && (~instrument->fei.flg & XMP_ENVELOPE_FLT)) { + /* IT pitch envelopes are always linear, even in Amiga period + * mode. Each unit in the envelope scale is 1/25 semitone. + */ + linear_bend += frq_envelope << 7; + } + + /* Arpeggio */ + + if (arp != 0) { + linear_bend += (100 << 7) * arp; + + /* OpenMPT ArpWrapAround.mod */ + if (HAS_QUIRK(QUIRK_PROTRACK)) { + if (xc->note + arp > MAX_NOTE_MOD + 1) { + linear_bend -= 12800 * (3 * 12); + } else if (xc->note + arp > MAX_NOTE_MOD) { + libxmp_virt_setvol(ctx, chn, 0); + } + } + } + + +#ifndef LIBXMP_CORE_PLAYER + linear_bend += libxmp_extras_get_linear_bend(ctx, xc); +#endif + + final_period = libxmp_note_to_period_mix(xc->note, linear_bend); + + /* From OpenMPT PeriodLimit.s3m: + * "ScreamTracker 3 limits the final output period to be at least 64, + * i.e. when playing a note that is too high or when sliding the + * period lower than 64, the output period will simply be clamped to + * 64. However, when reaching a period of 0 through slides, the + * output on the channel should be stopped." + */ + /* ST3 uses periods*4, so the limit is 16. Adjusted to the exact + * A6 value because we compute periods in floating point. + */ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { + if (final_period < 16.239270) { /* A6 */ + final_period = 16.239270; + } + } + + libxmp_virt_setperiod(ctx, chn, final_period); + + /* For xmp_get_frame_info() */ + xc->info_pitchbend = linear_bend >> 7; + xc->info_period = MIN(final_period * 4096, INT_MAX); + + if (IS_PERIOD_MODRNG()) { + const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096; + const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096; + CLAMP(xc->info_period, min_period, max_period); + } else if (xc->info_period < (1 << 12)) { + xc->info_period = (1 << 12); + } + + +#ifndef LIBXMP_CORE_DISABLE_IT + + /* Process filter */ + + if (!HAS_QUIRK(QUIRK_FILTER)) { + return; + } + + if (xc->f_idx >= 0 && (instrument->fei.flg & XMP_ENVELOPE_FLT)) { + if (frq_envelope < 0xfe) { + xc->filter.envelope = frq_envelope; + } + cutoff = xc->filter.cutoff * xc->filter.envelope >> 8; + } else { + cutoff = xc->filter.cutoff; + } + resonance = xc->filter.resonance; + + if (cutoff > 0xff) { + cutoff = 0xff; + } + /* IT: cutoff 127 + resonance 0 turns off the filter, but this + * is only applied when playing a new note without toneporta. + * All other combinations take effect immediately. + * See OpenMPT filter-reset.it, filter-reset-carry.it */ + if (cutoff < 0xfe || resonance > 0 || xc->filter.can_disable) { + int a0, b0, b1; + libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff); + xc->filter.can_disable = 0; + } + +#endif +} + +static void process_pan(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + int finalpan, panbrello = 0; + int pan_envelope; + int channel_pan; + + instrument = libxmp_get_instrument(ctx, xc->ins); + + if (!TEST_PER(PENV_PAUSE)) { + xc->p_idx = update_envelope(&instrument->pei, xc->p_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + pan_envelope = get_envelope(&instrument->pei, xc->p_idx, 32); + +#ifndef LIBXMP_CORE_DISABLE_IT + if (TEST(PANBRELLO)) { + panbrello = libxmp_lfo_get(ctx, &xc->panbrello.lfo, 0) / 512; + if (is_first_frame(ctx)) { + libxmp_lfo_update(&xc->panbrello.lfo); + } + } + xc->macro.notepan = xc->pan.val + panbrello + 0x80; +#endif + + channel_pan = xc->pan.val; + +#if 0 +#ifdef LIBXMP_PAULA_SIMULATOR + /* Always use 100% pan separation in Amiga mode */ + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + channel_pan = channel_pan < 0x80 ? 0 : 0xff; + } + } +#endif +#endif + + finalpan = channel_pan + panbrello + (pan_envelope - 32) * + (128 - abs(xc->pan.val - 128)) / 32; + + if (IS_PLAYER_MODE_IT()) { + finalpan = finalpan + xc->rpv * 4; + } + + CLAMP(finalpan, 0, 255); + + if (s->format & XMP_FORMAT_MONO || xc->pan.surround) { + finalpan = 0; + } else { + finalpan = (finalpan - 0x80) * s->mix / 100; + } + + xc->info_finalpan = finalpan + 0x80; + + if (xc->pan.surround) { + libxmp_virt_setpan(ctx, chn, PAN_SURROUND); + } else { + libxmp_virt_setpan(ctx, chn, finalpan); + } +} + +static void update_volume(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; +#ifndef LIBXMP_CORE_DISABLE_IT + struct flow_control *f = &p->flow; +#endif + struct channel_data *xc = &p->xc_data[chn]; + + /* Volume slides happen in all frames but the first, except when the + * "volume slide on all frames" flag is set. + */ + if (p->frame % p->speed != 0 || HAS_QUIRK(QUIRK_VSALL)) { + if (TEST(GVOL_SLIDE)) { + p->gvol += xc->gvol.slide; + } + + if (TEST(VOL_SLIDE) || TEST_PER(VOL_SLIDE)) { + xc->volume += xc->vol.slide; + } + +#ifndef LIBXMP_CORE_PLAYER + if (TEST_PER(VOL_SLIDE)) { + if (xc->vol.slide > 0) { + int target = MAX(xc->vol.target - 1, m->volbase); + if (xc->volume > target) { + xc->volume = target; + RESET_PER(VOL_SLIDE); + } + } + if (xc->vol.slide < 0) { + int target = xc->vol.target > 0 ? MIN(0, xc->vol.target - 1) : 0; + if (xc->volume < target) { + xc->volume = target; + RESET_PER(VOL_SLIDE); + } + } + } +#endif + + if (TEST(VOL_SLIDE_2)) { + xc->volume += xc->vol.slide2; + } + if (TEST(TRK_VSLIDE)) { + xc->mastervol += xc->trackvol.slide; + } + } + + if (p->frame % p->speed == 0) { + /* Process "fine" effects */ + if (TEST(FINE_VOLS)) { + xc->volume += xc->vol.fslide; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if (TEST(FINE_VOLS_2)) { + /* OpenMPT FineVolColSlide.it: + * Unlike fine volume slides in the effect column, + * fine volume slides in the volume column are only + * ever executed on the first tick -- not on multiples + * of the first tick if there is a pattern delay. + */ + if (!f->rowdelay_set || f->rowdelay_set & ROWDELAY_FIRST_FRAME) { + xc->volume += xc->vol.fslide2; + } + } +#endif + + if (TEST(TRK_FVSLIDE)) { + xc->mastervol += xc->trackvol.fslide; + } + + if (TEST(GVOL_SLIDE)) { + p->gvol += xc->gvol.fslide; + } + } + + /* Clamp volumes */ + CLAMP(xc->volume, 0, m->volbase); + CLAMP(p->gvol, 0, m->gvolbase); + CLAMP(xc->mastervol, 0, m->volbase); + + if (xc->split) { + p->xc_data[xc->pair].volume = xc->volume; + } +} + +static void update_frequency(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_PBALL)) { + if (TEST(PITCHBEND) || TEST_PER(PITCHBEND)) { + xc->period += xc->freq.slide; + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->porta.target = xc->period; + } + } + + /* Do tone portamento */ + if (TEST(TONEPORTA) || TEST_PER(TONEPORTA)) { + if (xc->porta.target > 0) { + int end = 0; + if (xc->porta.dir > 0) { + xc->period += xc->porta.slide; + if (xc->period >= xc->porta.target) + end = 1; + } else { + xc->period -= xc->porta.slide; + if (xc->period <= xc->porta.target) + end = 1; + } + + if (end) { + /* reached end */ + xc->period = xc->porta.target; + xc->porta.dir = 0; + RESET(TONEPORTA); + RESET_PER(TONEPORTA); + + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->porta.target = -1; + } + } + } + } + } + + if (is_first_frame(ctx)) { + if (TEST(FINE_BEND)) { + xc->period += xc->freq.fslide; + } + +#ifndef LIBXMP_CORE_PLAYER + if (TEST(FINE_NSLIDE)) { + xc->note += xc->noteslide.fslide; + xc->period = libxmp_note_to_period(ctx, xc->note, + xc->finetune, xc->per_adj); + } +#endif + } + + switch (m->period_type) { + case PERIOD_LINEAR: + CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L); + break; + case PERIOD_MODRNG: { + const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0); + const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0); + CLAMP(xc->period, min_period, max_period); + } + break; + } + + /* Check for invalid periods (from Toru Egashira's NSPmod) + * panic.s3m has negative periods + * ambio.it uses low (~8) period values + */ + if (xc->period < 0.25) { + libxmp_virt_setvol(ctx, chn, 0); + } +} + +static void update_pan(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (TEST(PAN_SLIDE)) { + if (is_first_frame(ctx)) { + xc->pan.val += xc->pan.fslide; + } else { + xc->pan.val += xc->pan.slide; + } + + if (xc->pan.val < 0) { + xc->pan.val = 0; + } else if (xc->pan.val > 0xff) { + xc->pan.val = 0xff; + } + } +} + +static void play_channel(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int act; + + xc->info_finalvol = 0; + +#ifndef LIBXMP_CORE_DISABLE_IT + /* IT tempo slide */ + if (!is_first_frame(ctx) && TEST(TEMPO_SLIDE)) { + p->bpm += xc->tempo.slide; + CLAMP(p->bpm, 0x20, 0xff); + } +#endif + + /* Do delay */ + if (xc->delay > 0) { + if (--xc->delay == 0) { + libxmp_read_event(ctx, &xc->delayed_event, chn); + } + } + +#ifndef LIBXMP_CORE_DISABLE_IT + /* IT MIDI macros need to update regardless of the current voice state. */ + update_midi_macro(ctx, chn); +#endif + + act = libxmp_virt_cstat(ctx, chn); + if (act == VIRT_INVALID) { + /* We need this to keep processing global volume slides */ + update_volume(ctx, chn); + return; + } + + if (p->frame == 0 && act != VIRT_ACTIVE) { + if (!IS_VALID_INSTRUMENT_OR_SFX(xc->ins) || act == VIRT_ACTION_CUT) { + libxmp_virt_resetchannel(ctx, chn); + return; + } + } + + if (!IS_VALID_INSTRUMENT_OR_SFX(xc->ins)) + return; + +#ifndef LIBXMP_CORE_PLAYER + libxmp_play_extras(ctx, xc, chn); +#endif + + /* Do cut/retrig */ + if (TEST(RETRIG)) { + int cond = HAS_QUIRK(QUIRK_S3MRTG) ? + --xc->retrig.count <= 0 : + --xc->retrig.count == 0; + + if (cond) { + if (xc->retrig.type < 0x10) { + /* don't retrig on cut */ + libxmp_virt_voicepos(ctx, chn, 0); + } else { + SET_NOTE(NOTE_END); + } + xc->volume += rval[xc->retrig.type].s; + xc->volume *= rval[xc->retrig.type].m; + xc->volume /= rval[xc->retrig.type].d; + xc->retrig.count = LSN(xc->retrig.val); + + if (xc->retrig.limit > 0) { + /* Limit the number of retriggers. */ + --xc->retrig.limit; + if (xc->retrig.limit == 0) + RESET(RETRIG); + } + } + } + + /* Do keyoff */ + if (xc->keyoff) { + if (--xc->keyoff == 0) + SET_NOTE(NOTE_RELEASE); + } + + libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_SAMPLE_RELEASE)); + + update_volume(ctx, chn); + update_frequency(ctx, chn); + update_pan(ctx, chn); + + process_volume(ctx, chn, act); + process_frequency(ctx, chn, act); + process_pan(ctx, chn, act); + +#ifndef LIBXMP_CORE_PLAYER + if (HAS_QUIRK(QUIRK_PROTRACK | QUIRK_INVLOOP) && xc->ins < mod->ins) { + update_invloop(ctx, xc); + } +#endif + + if (TEST_NOTE(NOTE_SUSEXIT)) { + SET_NOTE(NOTE_ENV_RELEASE); + } + + xc->info_position = libxmp_virt_getvoicepos(ctx, chn); +} + +/* + * Event injection + */ + +static void inject_event(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + int chn; + + for (chn = 0; chn < mod->chn + smix->chn; chn++) { + struct xmp_event *e = &p->inject_event[chn]; + if (e->_flag > 0) { + libxmp_read_event(ctx, e, chn); + e->_flag = 0; + } + } +} + +/* + * Sequencing + */ + +static void next_order(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int reset_gvol = 0; + int mark; + + do { + p->ord++; + + /* Restart module */ + mark = HAS_QUIRK(QUIRK_MARKER) && p->ord < mod->len && mod->xxo[p->ord] == 0xff; + if (p->ord >= mod->len || mark) { + if (mod->rst > mod->len || + mod->xxo[mod->rst] >= mod->pat || + p->ord < m->seq_data[p->sequence].entry_point) { + p->ord = m->seq_data[p->sequence].entry_point; + } else { + if (libxmp_get_sequence(ctx, mod->rst) == p->sequence) { + p->ord = mod->rst; + } else { + p->ord = m->seq_data[p->sequence].entry_point; + } + } + /* This might be a marker, so delay updating global + * volume until an actual pattern is found */ + reset_gvol = 1; + } + } while (mod->xxo[p->ord] >= mod->pat); + + if (reset_gvol) + p->gvol = m->xxo_info[p->ord].gvl; + +#ifndef LIBXMP_CORE_PLAYER + /* Archimedes line jump -- don't reset time tracking. */ + if (f->jump_in_pat != p->ord) +#endif + p->current_time = m->xxo_info[p->ord].time; + + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + if (f->jumpline >= f->num_rows) + f->jumpline = 0; + p->row = f->jumpline; + f->jumpline = 0; + + p->pos = p->ord; + p->frame = 0; + +#ifndef LIBXMP_CORE_PLAYER + f->jump_in_pat = -1; + + /* Reset persistent effects at new pattern */ + if (HAS_QUIRK(QUIRK_PERPAT)) { + int chn; + for (chn = 0; chn < mod->chn; chn++) { + p->xc_data[chn].per_flags = 0; + } + } +#endif +} + +static void next_row(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + + p->frame = 0; + f->delay = 0; + + if (f->pbreak) { + f->pbreak = 0; + + if (f->jump != -1) { + p->ord = f->jump - 1; + f->jump = -1; + } + + next_order(ctx); + } else { + if (f->rowdelay == 0) { + p->row++; + f->rowdelay_set = 0; + } else { + f->rowdelay--; + } + + if (f->loop_chn) { + p->row = f->loop[f->loop_chn - 1].start; + f->loop_chn = 0; + } + + /* check end of pattern */ + if (p->row >= f->num_rows) { + next_order(ctx); + } + } +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* + * Set note action for libxmp_virt_pastnote + */ +void libxmp_player_set_release(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + SET_NOTE(NOTE_RELEASE); +} + +void libxmp_player_set_fadeout(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + SET_NOTE(NOTE_FADEOUT); +} + +#endif + +static void update_from_ord_info(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct ord_data *oinfo = &m->xxo_info[p->ord]; + + if (oinfo->speed) + p->speed = oinfo->speed; + p->bpm = oinfo->bpm; + p->gvol = oinfo->gvl; + p->current_time = oinfo->time; + p->frame_time = m->time_factor * m->rrate / p->bpm; + +#ifndef LIBXMP_CORE_PLAYER + p->st26_speed = oinfo->st26_speed; +#endif +} + +void libxmp_reset_flow(struct context_data *ctx) +{ + struct flow_control *f = &ctx->p.flow; + f->jumpline = 0; + f->jump = -1; + f->pbreak = 0; + f->loop_chn = 0; + f->delay = 0; + f->rowdelay = 0; + f->rowdelay_set = 0; +#ifndef LIBXMP_CORE_PLAYER + f->jump_in_pat = -1; +#endif +} + +int xmp_start_player(xmp_context opaque, int rate, int format) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int i; + int ret = 0; + + if (rate < XMP_MIN_SRATE || rate > XMP_MAX_SRATE) + return -XMP_ERROR_INVALID; + + if (ctx->state < XMP_STATE_LOADED) + return -XMP_ERROR_STATE; + + if (ctx->state > XMP_STATE_LOADED) + xmp_end_player(opaque); + + if (libxmp_mixer_on(ctx, rate, format, m->c4rate) < 0) + return -XMP_ERROR_INTERNAL; + + p->master_vol = 100; + p->smix_vol = 100; + p->gvol = m->volbase; + p->pos = p->ord = 0; + p->frame = -1; + p->row = 0; + p->current_time = 0; + p->loop_count = 0; + p->sequence = 0; + + /* Set default volume and mute status */ + for (i = 0; i < mod->chn; i++) { + if (mod->xxc[i].flg & XMP_CHANNEL_MUTE) + p->channel_mute[i] = 1; + p->channel_vol[i] = 100; + } + for (i = mod->chn; i < XMP_MAX_CHANNELS; i++) { + p->channel_mute[i] = 0; + p->channel_vol[i] = 100; + } + + /* Skip invalid patterns at start (the seventh laboratory.it) */ + while (p->ord < mod->len && mod->xxo[p->ord] >= mod->pat) { + p->ord++; + } + /* Check if all positions skipped */ + if (p->ord >= mod->len) { + mod->len = 0; + } + + if (mod->len == 0) { + /* set variables to sane state */ + /* Note: previously did this for mod->chn == 0, which caused + * crashes on invalid order 0s. 0 channel modules are technically + * valid (if useless) so just let them play normally. */ + p->ord = p->scan[0].ord = 0; + p->row = p->scan[0].row = 0; + f->end_point = 0; + f->num_rows = 0; + } else { + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + f->end_point = p->scan[0].num; + } + + update_from_ord_info(ctx); + + if (libxmp_virt_on(ctx, mod->chn + smix->chn) != 0) { + ret = -XMP_ERROR_INTERNAL; + goto err; + } + + libxmp_reset_flow(ctx); + + f->loop = (struct pattern_loop *) calloc(p->virt.virt_channels, sizeof(struct pattern_loop)); + if (f->loop == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + p->xc_data = (struct channel_data *) calloc(p->virt.virt_channels, sizeof(struct channel_data)); + if (p->xc_data == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err1; + } + + /* Reset our buffer pointers */ + xmp_play_buffer(opaque, NULL, 0, 0); + +#ifndef LIBXMP_CORE_DISABLE_IT + for (i = 0; i < p->virt.virt_channels; i++) { + struct channel_data *xc = &p->xc_data[i]; + xc->filter.cutoff = 0xff; +#ifndef LIBXMP_CORE_PLAYER + if (libxmp_new_channel_extras(ctx, xc) < 0) + goto err2; +#endif + } +#endif + reset_channels(ctx); + + ctx->state = XMP_STATE_PLAYING; + + return 0; + +#ifndef LIBXMP_CORE_PLAYER + err2: + free(p->xc_data); + p->xc_data = NULL; +#endif + err1: + free(f->loop); + f->loop = NULL; + err: + return ret; +} + +static void check_end_of_module(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + + /* check end of module */ + if (p->ord == p->scan[p->sequence].ord && + p->row == p->scan[p->sequence].row) { + if (f->end_point == 0) { + p->loop_count++; + f->end_point = p->scan[p->sequence].num; + /* return -1; */ + } + f->end_point--; + } +} + +int xmp_play_frame(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int i; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (mod->len <= 0) { + return -XMP_END; + } + + if (HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff) { + return -XMP_END; + } + + /* check reposition */ + if (p->ord != p->pos) { + int start = m->seq_data[p->sequence].entry_point; + + if (p->pos == -2) { /* set by xmp_module_stop */ + return -XMP_END; /* that's all folks */ + } + + if (p->pos == -1) { + /* restart sequence */ + p->pos = start; + } + + if (p->pos == start) { + f->end_point = p->scan[p->sequence].num; + } + + /* Check if lands after a loop point */ + if (p->pos > p->scan[p->sequence].ord) { + f->end_point = 0; + } + + f->jumpline = 0; + f->jump = -1; + + p->ord = p->pos - 1; + + /* Stay inside our subsong */ + if (p->ord < start) { + p->ord = start - 1; + } + + next_order(ctx); + + update_from_ord_info(ctx); + + libxmp_virt_reset(ctx); + reset_channels(ctx); + } else { + p->frame++; + if (p->frame >= (p->speed * (1 + f->delay))) { + /* If break during pattern delay, next row is skipped. + * See corruption.mod order 1D (pattern 0D) last line: + * EE2 + D31 ignores D00 in order 1C line 31. Reported + * by The Welder , Jan 14 2012 + */ + if (HAS_QUIRK(QUIRK_PROTRACK) && f->delay && f->pbreak) + { + next_row(ctx); + check_end_of_module(ctx); + } + next_row(ctx); + } + } + + for (i = 0; i < mod->chn; i++) { + struct channel_data *xc = &p->xc_data[i]; + RESET(KEY_OFF); + } + + /* check new row */ + + if (p->frame == 0) { /* first frame in row */ + check_end_of_module(ctx); + read_row(ctx, mod->xxo[p->ord], p->row); + +#ifndef LIBXMP_CORE_PLAYER + if (p->st26_speed) { + if (p->st26_speed & 0x10000) { + p->speed = (p->st26_speed & 0xff00) >> 8; + } else { + p->speed = p->st26_speed & 0xff; + } + p->st26_speed ^= 0x10000; + } +#endif + } + + inject_event(ctx); + + /* play_frame */ + for (i = 0; i < p->virt.virt_channels; i++) { + play_channel(ctx, i); + } + + f->rowdelay_set &= ~ROWDELAY_FIRST_FRAME; + + p->frame_time = m->time_factor * m->rrate / p->bpm; + p->current_time += p->frame_time; + + libxmp_mixer_softmixer(ctx); + + return 0; +} + +int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret = 0, filled = 0, copy_size; + struct xmp_frame_info fi; + + /* Reset internal state + * Syncs buffer start with frame start */ + if (out_buffer == NULL) { + p->loop_count = 0; + p->buffer_data.consumed = 0; + p->buffer_data.in_size = 0; + return 0; + } + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + /* Fill buffer */ + while (filled < size) { + /* Check if buffer full */ + if (p->buffer_data.consumed == p->buffer_data.in_size) { + ret = xmp_play_frame(opaque); + xmp_get_frame_info(opaque, &fi); + + /* Check end of module */ + if (ret < 0 || (loop > 0 && fi.loop_count >= loop)) { + /* Start of frame, return end of replay */ + if (filled == 0) { + p->buffer_data.consumed = 0; + p->buffer_data.in_size = 0; + return -1; + } + + /* Fill remaining of this buffer */ + memset((char *)out_buffer + filled, 0, size - filled); + return 0; + } + + p->buffer_data.consumed = 0; + p->buffer_data.in_buffer = (char *)fi.buffer; + p->buffer_data.in_size = fi.buffer_size; + } + + /* Copy frame data to user buffer */ + copy_size = MIN(size - filled, p->buffer_data.in_size - + p->buffer_data.consumed); + memcpy((char *)out_buffer + filled, p->buffer_data.in_buffer + + p->buffer_data.consumed, copy_size); + p->buffer_data.consumed += copy_size; + filled += copy_size; + } + + return ret; +} + +void xmp_end_player(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; +#ifndef LIBXMP_CORE_PLAYER + struct channel_data *xc; + int i; +#endif + + if (ctx->state < XMP_STATE_PLAYING) + return; + + ctx->state = XMP_STATE_LOADED; + +#ifndef LIBXMP_CORE_PLAYER + /* Free channel extras */ + for (i = 0; i < p->virt.virt_channels; i++) { + xc = &p->xc_data[i]; + libxmp_release_channel_extras(ctx, xc); + } +#endif + + libxmp_virt_off(ctx); + + free(p->xc_data); + free(f->loop); + + p->xc_data = NULL; + f->loop = NULL; + + libxmp_mixer_off(ctx); +} + +void xmp_get_module_info(xmp_context opaque, struct xmp_module_info *info) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (ctx->state < XMP_STATE_LOADED) + return; + + memcpy(info->md5, m->md5, 16); + info->mod = mod; + info->comment = m->comment; + info->num_sequences = m->num_sequences; + info->seq_data = m->seq_data; + info->vol_base = m->volbase; +} + +void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int chn, i; + + if (ctx->state < XMP_STATE_LOADED) + return; + + chn = mod->chn; + + if (p->pos >= 0 && p->pos < mod->len) { + info->pos = p->pos; + } else { + info->pos = 0; + } + + info->pattern = mod->xxo[info->pos]; + + if (info->pattern < mod->pat) { + info->num_rows = mod->xxp[info->pattern]->rows; + } else { + info->num_rows = 0; + } + + info->row = p->row; + info->frame = p->frame; + info->speed = p->speed; + info->bpm = p->bpm; + info->total_time = p->scan[p->sequence].time; + info->frame_time = p->frame_time * 1000; + info->time = p->current_time; + info->buffer = s->buffer; + + info->total_size = XMP_MAX_FRAMESIZE; + info->buffer_size = s->ticksize; + if (~s->format & XMP_FORMAT_MONO) { + info->buffer_size *= 2; + } + if (~s->format & XMP_FORMAT_8BIT) { + info->buffer_size *= 2; + } + + info->volume = p->gvol; + info->loop_count = p->loop_count; + info->virt_channels = p->virt.virt_channels; + info->virt_used = p->virt.virt_used; + + info->sequence = p->sequence; + + if (p->xc_data != NULL) { + for (i = 0; i < chn; i++) { + struct channel_data *c = &p->xc_data[i]; + struct xmp_channel_info *ci = &info->channel_info[i]; + struct xmp_track *track; + struct xmp_event *event; + int trk; + + ci->note = c->key; + ci->pitchbend = c->info_pitchbend; + ci->period = c->info_period; + ci->position = c->info_position; + ci->instrument = c->ins; + ci->sample = c->smp; + ci->volume = c->info_finalvol >> 4; + ci->pan = c->info_finalpan; + ci->reserved = 0; + memset(&ci->event, 0, sizeof(*event)); + + if (info->pattern < mod->pat && info->row < info->num_rows) { + trk = mod->xxp[info->pattern]->index[i]; + track = mod->xxt[trk]; + if (info->row < track->rows) { + event = &track->event[info->row]; + memcpy(&ci->event, event, sizeof(*event)); + } + } + } + } +} diff --git a/thirdparty/libxmp/src/player.h b/thirdparty/libxmp/src/player.h new file mode 100644 index 0000000..e1fc9ab --- /dev/null +++ b/thirdparty/libxmp/src/player.h @@ -0,0 +1,282 @@ +#ifndef LIBXMP_PLAYER_H +#define LIBXMP_PLAYER_H + +#include "lfo.h" + +/* Quirk control */ +#define HAS_QUIRK(x) (m->quirk & (x)) + +/* Channel flag control */ +#define SET(f) SET_FLAG(xc->flags,(f)) +#define RESET(f) RESET_FLAG(xc->flags,(f)) +#define TEST(f) TEST_FLAG(xc->flags,(f)) + +/* Persistent effect flag control */ +#define SET_PER(f) SET_FLAG(xc->per_flags,(f)) +#define RESET_PER(f) RESET_FLAG(xc->per_flags,(f)) +#define TEST_PER(f) TEST_FLAG(xc->per_flags,(f)) + +/* Note flag control */ +#define SET_NOTE(f) SET_FLAG(xc->note_flags,(f)) +#define RESET_NOTE(f) RESET_FLAG(xc->note_flags,(f)) +#define TEST_NOTE(f) TEST_FLAG(xc->note_flags,(f)) + +struct retrig_control { + int s; + int m; + int d; +}; + +/* The following macros are used to set the flags for each channel */ +#define VOL_SLIDE (1 << 0) +#define PAN_SLIDE (1 << 1) +#define TONEPORTA (1 << 2) +#define PITCHBEND (1 << 3) +#define VIBRATO (1 << 4) +#define TREMOLO (1 << 5) +#define FINE_VOLS (1 << 6) +#define FINE_BEND (1 << 7) +#define OFFSET (1 << 8) +#define TRK_VSLIDE (1 << 9) +#define TRK_FVSLIDE (1 << 10) +#define NEW_INS (1 << 11) +#define NEW_VOL (1 << 12) +#define VOL_SLIDE_2 (1 << 13) +#define NOTE_SLIDE (1 << 14) +#define FINE_NSLIDE (1 << 15) +#define NEW_NOTE (1 << 16) +#define FINE_TPORTA (1 << 17) +#define RETRIG (1 << 18) +#define PANBRELLO (1 << 19) +#define GVOL_SLIDE (1 << 20) +#define TEMPO_SLIDE (1 << 21) +#define VENV_PAUSE (1 << 22) +#define PENV_PAUSE (1 << 23) +#define FENV_PAUSE (1 << 24) +#define FINE_VOLS_2 (1 << 25) +#define KEY_OFF (1 << 26) /* for IT release on envloop end */ +#define TREMOR (1 << 27) /* for XM tremor */ +#define MIDI_MACRO (1 << 28) /* IT midi macro */ + +#define NOTE_FADEOUT (1 << 0) +#define NOTE_ENV_RELEASE (1 << 1) /* envelope sustain loop release */ +#define NOTE_END (1 << 2) +#define NOTE_CUT (1 << 3) +#define NOTE_ENV_END (1 << 4) +#define NOTE_SAMPLE_END (1 << 5) +#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */ +#define NOTE_SUSEXIT (1 << 7) /* for delayed envelope release */ +#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */ +#define NOTE_GLISSANDO (1 << 9) +#define NOTE_SAMPLE_RELEASE (1 << 10) /* sample sustain loop release */ + +/* Most of the time, these should be set/reset together. */ +#define NOTE_RELEASE (NOTE_ENV_RELEASE | NOTE_SAMPLE_RELEASE) + +/* Note: checking the data pointer for samples should be good enough to filter + * broken samples, since libxmp_load_sample will always allocate it for valid + * samples of >0 length and bound the loop values for these samples. */ +#define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) +#define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins)) +#define IS_VALID_SAMPLE(x) ((uint32)(x) < mod->smp && mod->xxs[(x)].data != NULL) +#define IS_VALID_NOTE(x) ((uint32)(x) < XMP_MAX_KEYS) + +struct instrument_vibrato { + int phase; + int sweep; +}; + +struct channel_data { + int flags; /* Channel flags */ + int per_flags; /* Persistent effect channel flags */ + int note_flags; /* Note release, fadeout or end */ + int note; /* Note number */ + int key; /* Key number */ + double period; /* Amiga or linear period */ + double per_adj; /* MED period/pitch adjustment factor hack */ + int finetune; /* Guess what */ + int ins; /* Instrument number */ + int old_ins; /* Last instruemnt */ + int smp; /* Sample number */ + int mastervol; /* Master vol -- for IT track vol effect */ + int delay; /* Note delay in frames */ + int keyoff; /* Key off counter */ + int fadeout; /* Current fadeout (release) value */ + int ins_fade; /* Instrument fadeout value */ + int volume; /* Current volume */ + int gvl; /* Global volume for instrument for IT */ + + int rvv; /* Random volume variation */ + int rpv; /* Random pan variation */ + + uint8 split; /* Split channel */ + uint8 pair; /* Split channel pair */ + + int v_idx; /* Volume envelope index */ + int p_idx; /* Pan envelope index */ + int f_idx; /* Freq envelope index */ + + int key_porta; /* Key number for portamento target + * -- needed to handle IT portamento xpo */ + struct { + struct lfo lfo; + int memory; + } vibrato; + + struct { + struct lfo lfo; + int memory; + } tremolo; + +#ifndef LIBXMP_CORE_DISABLE_IT + struct { + struct lfo lfo; + int memory; + } panbrello; +#endif + + struct { + int8 val[16]; /* 16 for Smaksak MegaArps */ + int size; + int count; + int memory; + } arpeggio; + + struct { + struct lfo lfo; + int sweep; + } insvib; + + struct { + int val; + int val2; /* For fx9 bug emulation */ + int memory; + } offset; + + struct { + int val; /* Retrig value */ + int count; /* Retrig counter */ + int type; /* Retrig type */ + int limit; /* Number of retrigs */ + } retrig; + + struct { + uint8 up,down; /* Tremor value */ + uint8 count; /* Tremor counter */ + uint8 memory; /* Tremor memory */ + } tremor; + + struct { + int slide; /* Volume slide value */ + int fslide; /* Fine volume slide value */ + int slide2; /* Volume slide value */ + int memory; /* Volume slide effect memory */ +#ifndef LIBXMP_CORE_DISABLE_IT + int fslide2; + int memory2; /* Volume slide effect memory */ +#endif +#ifndef LIBXMP_CORE_PLAYER + int target; /* Target for persistent volslide */ +#endif + } vol; + + struct { + int up_memory; /* Fine volume slide up memory (XM) */ + int down_memory;/* Fine volume slide up memory (XM) */ + } fine_vol; + + struct { + int slide; /* Global volume slide value */ + int fslide; /* Fine global volume slide value */ + int memory; /* Global volume memory is saved per channel */ + } gvol; + + struct { + int slide; /* Track volume slide value */ + int fslide; /* Track fine volume slide value */ + int memory; /* Track volume slide effect memory */ + } trackvol; + + struct { + int slide; /* Frequency slide value */ + double fslide; /* Fine frequency slide value */ + int memory; /* Portamento effect memory */ + } freq; + + struct { + double target; /* Target period for tone portamento */ + int dir; /* Tone portamento up/down directionh */ + int slide; /* Delta for tone portamento */ + int memory; /* Tone portamento effect memory */ + int note_memory;/* Tone portamento note memory (ULT) */ + } porta; + + struct { + int up_memory; /* FT2 has separate memories for these */ + int down_memory;/* cases (see Porta-LinkMem.xm) */ + } fine_porta; + + struct { + int val; /* Current pan value */ + int slide; /* Pan slide value */ + int fslide; /* Pan fine slide value */ + int memory; /* Pan slide effect memory */ + int surround; /* Surround channel flag */ + } pan; + + struct { + int speed; + int count; + int pos; + } invloop; + +#ifndef LIBXMP_CORE_DISABLE_IT + struct { + int slide; /* IT tempo slide */ + } tempo; + + struct { + int cutoff; /* IT filter cutoff frequency */ + int resonance; /* IT filter resonance */ + int envelope; /* IT filter envelope */ + int can_disable;/* IT hack: allow disabling for cutoff 127 */ + } filter; + + struct { + float val; /* Current macro effect (use float for slides) */ + float target; /* Current macro target (smooth macro) */ + float slide; /* Current macro slide (smooth macro) */ + int active; /* Current active parameterized macro */ + int finalvol; /* Previous tick calculated volume (0-0x400) */ + int notepan; /* Previous tick note panning (0x80 center) */ + } macro; +#endif + +#ifndef LIBXMP_CORE_PLAYER + struct { + int slide; /* PTM note slide amount */ + int fslide; /* OKT fine note slide amount */ + int speed; /* PTM note slide speed */ + int count; /* PTM note slide counter */ + } noteslide; + + void *extra; +#endif + + struct xmp_event delayed_event; + int delayed_ins; /* IT save instrument emulation */ + + int info_period; /* Period */ + int info_pitchbend; /* Linear pitchbend */ + int info_position; /* Position before mixing */ + int info_finalvol; /* Final volume including envelopes */ + int info_finalpan; /* Final pan including envelopes */ +}; + + +void libxmp_process_fx (struct context_data *, struct channel_data *, + int, struct xmp_event *, int); +void libxmp_filter_setup (int, int, int, int*, int*, int *); +int libxmp_read_event (struct context_data *, struct xmp_event *, int); + +#endif /* LIBXMP_PLAYER_H */ diff --git a/thirdparty/libxmp/src/precomp_blep.h b/thirdparty/libxmp/src/precomp_blep.h new file mode 100644 index 0000000..ec38073 --- /dev/null +++ b/thirdparty/libxmp/src/precomp_blep.h @@ -0,0 +1,256 @@ + /* + * Table generated by compute-blep.py (a1200 and vanilla tables removed) + */ + +/* tables are: a500 off, a500 on */ +const int winsinc_integral[2][2048] = { + { +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131070,131070,131070, +131070,131070,131069,131069,131069,131068,131068,131068,131067,131067,131066,131066, +131065,131065,131064,131063,131063,131062,131061,131060,131059,131058,131056,131055, +131054,131052,131050,131049,131047,131045,131043,131040,131038,131035,131033,131030, +131026,131023,131020,131016,131012,131008,131003,130998,130993,130988,130982,130976, +130970,130963,130956,130949,130941,130932,130924,130914,130905,130895,130884,130872, +130861,130848,130835,130821,130807,130792,130776,130759,130742,130724,130705,130685, +130664,130642,130620,130596,130571,130545,130518,130490,130461,130430,130398,130365, +130331,130295,130257,130219,130178,130136,130093,130047,130000,129951,129901,129848, +129794,129737,129679,129618,129555,129490,129423,129353,129281,129207,129130,129050, +128968,128883,128795,128704,128611,128514,128415,128312,128206,128097,127985,127869, +127750,127627,127501,127371,127237,127100,126959,126813,126664,126510,126353,126191, +126025,125854,125679,125499,125315,125126,124933,124734,124531,124323,124110,123891, +123668,123439,123205,122965,122720,122470,122214,121952,121685,121412,121133,120849, +120558,120261,119959,119650,119335,119014,118687,118354,118014,117668,117315,116956, +116591,116219,115840,115455,115063,114665,114260,113849,113430,113005,112574,112135, +111690,111239,110780,110315,109843,109364,108879,108387,107888,107383,106871,106352, +105827,105295,104757,104212,103661,103104,102540,101970,101394,100812,100223,99629, +99028,98422,97810,97192,96568,95939,95305,94665,94020,93370,92714,92054,91389,90719, +90045,89366,88682,87995,87303,86607,85908,85205,84498,83788,83075,82358,81639,80916, +80191,79464,78734,78002,77268,76533,75795,75056,74316,73575,72833,72090,71346,70602, +69858,69114,68370,67626,66883,66140,65399,64658,63919,63181,62445,61711,60979,60249, +59521,58796,58074,57355,56639,55926,55217,54512,53810,53113,52419,51731,51046,50367, +49693,49023,48359,47701,47048,46400,45759,45124,44495,43872,43256,42646,42043,41447, +40858,40276,39702,39134,38575,38023,37478,36941,36413,35892,35379,34874,34378,33890, +33410,32938,32475,32020,31574,31137,30708,30288,29876,29473,29079,28693,28317,27948, +27589,27238,26896,26562,26238,25921,25613,25314,25023,24740,24466,24200,23942,23692, +23451,23217,22991,22773,22562,22359,22164,21975,21794,21621,21454,21294,21140,20994, +20853,20719,20592,20470,20354,20244,20139,20040,19946,19857,19774,19694,19620,19550, +19484,19422,19364,19310,19260,19213,19169,19128,19090,19054,19022,18991,18963,18936, +18912,18889,18867,18847,18828,18810,18792,18776,18759,18743,18727,18711,18695,18679, +18662,18644,18626,18607,18587,18565,18542,18518,18492,18465,18436,18404,18371,18336, +18298,18259,18216,18172,18124,18074,18022,17966,17908,17847,17783,17716,17646,17572, +17496,17416,17334,17248,17159,17066,16971,16872,16770,16664,16556,16444,16329,16211, +16090,15966,15839,15709,15576,15440,15301,15159,15015,14868,14718,14566,14412,14255, +14096,13935,13771,13606,13439,13270,13099,12927,12753,12578,12401,12224,12045,11866, +11685,11504,11322,11140,10958,10775,10592,10409,10226,10044,9862,9680,9499,9319,9139, +8961,8783,8607,8432,8258,8086,7915,7747,7580,7415,7252,7091,6932,6776,6622,6471, +6322,6176,6032,5892,5754,5619,5488,5359,5234,5111,4992,4877,4764,4655,4550,4448, +4349,4254,4163,4075,3990,3910,3832,3759,3689,3622,3560,3500,3445,3393,3344,3299, +3257,3219,3184,3153,3124,3099,3078,3059,3044,3031,3022,3015,3011,3010,3012,3016, +3023,3033,3044,3058,3075,3093,3113,3136,3160,3186,3213,3242,3273,3305,3338,3372, +3408,3444,3481,3520,3558,3597,3637,3677,3718,3758,3799,3839,3880,3920,3960,4000, +4039,4077,4115,4152,4188,4224,4258,4291,4323,4354,4384,4412,4439,4464,4488,4510, +4530,4549,4566,4581,4594,4606,4615,4623,4628,4631,4633,4632,4629,4624,4617,4608, +4597,4583,4568,4550,4530,4508,4484,4458,4429,4399,4366,4332,4296,4257,4217,4175, +4130,4085,4037,3988,3937,3884,3830,3774,3717,3658,3598,3537,3475,3411,3347,3281, +3215,3147,3079,3010,2940,2870,2799,2728,2657,2585,2513,2440,2368,2296,2224,2151, +2080,2008,1937,1866,1796,1726,1657,1589,1521,1454,1389,1324,1260,1197,1135,1075, +1016,958,901,846,792,740,689,640,592,546,502,459,419,379,342,307,273,241,211,183, +156,132,109,88,69,52,37,24,12,2,-5,-11,-16,-18,-19,-18,-16,-11,-6,2,11,21,33,47,61, +77,95,113,133,154,176,200,224,249,275,302,329,358,387,416,447,477,508,540,572,604, +636,669,702,734,767,800,832,864,896,928,960,991,1021,1051,1081,1110,1138,1166,1193, +1219,1245,1270,1293,1316,1338,1359,1379,1398,1416,1433,1448,1463,1476,1488,1499, +1509,1518,1525,1531,1536,1540,1542,1543,1543,1542,1539,1536,1530,1524,1517,1508, +1498,1487,1475,1462,1447,1432,1415,1397,1379,1359,1338,1317,1294,1271,1247,1222, +1196,1170,1143,1115,1086,1057,1028,998,967,936,905,874,842,809,777,744,712,679,646, +613,581,548,515,483,450,418,387,355,324,293,263,233,204,175,147,119,92,66,40,15, +-10,-33,-56,-78,-99,-120,-139,-158,-176,-193,-209,-224,-238,-252,-264,-275,-286, +-295,-304,-311,-318,-324,-329,-332,-335,-337,-338,-338,-338,-336,-333,-330,-326, +-321,-315,-308,-301,-293,-284,-274,-264,-253,-242,-229,-217,-203,-190,-175,-161, +-145,-130,-114,-98,-81,-64,-47,-29,-11,6,24,42,61,79,97,115,134,152,170,188,206,223, +241,258,275,291,308,324,340,355,370,384,399,412,425,438,450,462,473,484,494,503, +512,521,529,536,542,548,553,558,562,566,568,570,572,573,573,573,572,570,568,565, +562,558,553,548,543,537,530,523,515,507,498,489,479,469,459,448,437,426,414,402, +389,377,364,351,337,324,310,296,282,268,254,239,225,211,196,182,168,153,139,125, +111,97,83,70,56,43,30,17,5,-7,-19,-31,-42,-53,-64,-75,-85,-94,-104,-113,-121,-129, +-137,-144,-151,-158,-164,-170,-175,-180,-184,-188,-192,-195,-198,-200,-202,-203, +-204,-205,-205,-204,-204,-203,-201,-199,-197,-195,-192,-188,-185,-181,-176,-172, +-167,-162,-156,-151,-145,-139,-132,-126,-119,-112,-104,-97,-90,-82,-74,-66,-59,-51, +-42,-34,-26,-18,-10,-2,7,15,23,31,39,47,54,62,70,77,85,92,99,106,112,119,125,131, +137,143,148,154,159,163,168,172,176,180,183,187,190,192,195,197,199,201,202,203, +204,205,205,205,205,204,203,202,201,200,198,196,194,192,189,186,183,180,177,173, +169,165,161,157,153,148,143,139,134,129,124,119,113,108,103,97,92,86,81,75,70,64, +59,53,48,42,37,32,26,21,16,11,6,1,-4,-9,-13,-18,-22,-26,-30,-34,-38,-42,-46,-49, +-52,-55,-58,-61,-64,-66,-69,-71,-73,-74,-76,-78,-79,-80,-81,-82,-82,-83,-83,-83, +-83,-83,-83,-82,-82,-81,-80,-79,-78,-77,-75,-74,-72,-70,-68,-66,-64,-62,-60,-57, +-55,-52,-50,-47,-44,-42,-39,-36,-33,-30,-27,-24,-21,-18,-15,-12,-9,-6,-3,0,3,6,8, +11,14,17,20,22,25,27,30,32,35,37,39,41,43,45,47,49,50,52,54,55,56,58,59,60,61,61, +62,63,63,64,64,65,65,65,65,65,64,64,64,63,63,62,61,61,60,59,58,57,56,55,53,52,51, +49,48,46,45,43,41,40,38,36,35,33,31,29,28,26,24,22,20,19,17,15,13,11,10,8,6,5,3,1, +0,-2,-3,-5,-6,-8,-9,-10,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-23, +-24,-24,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24, +-24,-23,-23,-22,-21,-21,-20,-19,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-10,-9, +-8,-7,-6,-5,-4,-3,-2,-1,0,1,1,2,3,4,5,6,6,7,8,9,9,10,11,11,12,12,13,14,14,15,15, +15,16,16,16,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,16,16, +16,16,15,15,14,14,14,13,13,12,12,11,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,-1,-2,-2,-3,-3,-3,-4,-4,-4,-4,-5,-5,-5,-5,-6,-6,-6,-6,-6,-6,-6,-7,-7, +-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-4,-4,-4, +-4,-4,-3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,3,3,3, +3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3, +3,3,3,3,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,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,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,0,0,0,0,0,0,0,0,0,0,0, + }, + { +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, +131072,131072,131072,131072,131072,131072,131072,131072,131072,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131070,131070,131070,131070,131070,131070, +131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131069,131069, +131069,131069,131069,131069,131069,131069,131068,131068,131068,131068,131068,131068, +131067,131067,131067,131067,131066,131066,131066,131066,131065,131065,131064,131064, +131064,131063,131063,131062,131062,131061,131061,131060,131060,131059,131058,131058, +131057,131056,131055,131055,131054,131053,131052,131051,131050,131049,131047,131046, +131045,131044,131042,131041,131039,131038,131036,131034,131032,131031,131029,131027, +131024,131022,131020,131017,131015,131012,131009,131007,131004,131001,130997,130994, +130991,130987,130983,130979,130975,130971,130967,130962,130958,130953,130948,130942, +130937,130931,130926,130920,130913,130907,130900,130893,130886,130879,130871,130863, +130855,130847,130838,130829,130820,130810,130800,130790,130779,130768,130757,130745, +130733,130721,130708,130695,130682,130668,130654,130639,130624,130608,130592,130576, +130559,130541,130523,130505,130486,130466,130446,130426,130405,130383,130361,130338, +130314,130290,130265,130240,130214,130187,130160,130132,130103,130074,130044,130013, +129981,129949,129916,129882,129847,129812,129775,129738,129700,129661,129621,129581, +129539,129497,129453,129409,129364,129318,129270,129222,129173,129123,129071,129019, +128966,128911,128856,128799,128742,128683,128623,128562,128500,128436,128372,128306, +128239,128171,128101,128031,127959,127886,127811,127736,127659,127580,127501,127420, +127337,127254,127169,127082,126995,126906,126815,126723,126630,126535,126439,126341, +126242,126142,126040,125936,125831,125725,125617,125507,125396,125284,125170,125055, +124938,124819,124699,124577,124454,124329,124203,124075,123946,123815,123683,123549, +123413,123276,123137,122997,122855,122711,122566,122420,122272,122122,121971,121818, +121663,121507,121350,121191,121030,120868,120704,120539,120372,120204,120034,119863, +119690,119515,119339,119162,118983,118803,118621,118438,118253,118067,117879,117690, +117500,117308,117114,116920,116724,116526,116327,116127,115926,115723,115519,115313, +115106,114898,114689,114478,114266,114053,113839,113624,113407,113189,112970,112750, +112528,112306,112082,111858,111632,111405,111177,110948,110718,110487,110255,110022, +109788,109553,109317,109080,108842,108604,108364,108124,107883,107641,107398,107154, +106909,106664,106418,106171,105923,105675,105426,105176,104926,104675,104423,104171, +103918,103664,103410,103155,102899,102643,102387,102130,101872,101614,101356,101096, +100837,100577,100316,100055,99794,99532,99270,99007,98745,98481,98217,97953,97689, +97424,97159,96894,96628,96362,96096,95829,95563,95296,95028,94761,94493,94225,93957, +93688,93419,93151,92881,92612,92343,92073,91803,91534,91263,90993,90723,90452,90182, +89911,89640,89369,89098,88827,88556,88284,88013,87741,87470,87198,86926,86654,86382, +86110,85838,85566,85294,85022,84750,84477,84205,83933,83660,83388,83116,82843,82571, +82298,82026,81753,81481,81208,80936,80663,80391,80118,79846,79573,79301,79029,78756, +78484,78212,77939,77667,77395,77123,76851,76579,76307,76035,75763,75491,75220,74948, +74676,74405,74134,73862,73591,73320,73049,72778,72507,72237,71966,71696,71426,71156, +70886,70616,70346,70077,69807,69538,69269,69000,68732,68463,68195,67927,67659,67391, +67124,66857,66590,66323,66056,65790,65524,65258,64993,64728,64463,64198,63934,63670, +63406,63143,62880,62617,62354,62092,61830,61569,61308,61047,60787,60527,60267,60008, +59749,59491,59233,58975,58718,58461,58205,57949,57694,57439,57184,56930,56676,56423, +56171,55918,55667,55416,55165,54915,54665,54416,54167,53919,53671,53424,53178,52932, +52686,52442,52197,51954,51710,51468,51226,50984,50743,50503,50263,50024,49786,49548, +49310,49074,48838,48602,48367,48133,47899,47666,47434,47202,46971,46740,46510,46281, +46052,45824,45597,45370,45144,44918,44693,44469,44245,44022,43800,43578,43357,43137, +42917,42698,42479,42261,42044,41827,41612,41396,41181,40967,40754,40541,40329,40118, +39907,39696,39487,39278,39069,38862,38655,38448,38242,38037,37832,37628,37425,37222, +37020,36819,36618,36417,36218,36019,35820,35622,35425,35228,35032,34837,34642,34448, +34254,34061,33869,33677,33486,33295,33105,32916,32727,32539,32352,32164,31978,31792, +31607,31422,31238,31055,30872,30690,30508,30327,30147,29967,29787,29609,29431,29253, +29076,28900,28724,28549,28374,28200,28027,27854,27682,27510,27339,27169,26999,26830, +26661,26493,26326,26159,25993,25827,25662,25498,25334,25171,25008,24846,24685,24524, +24364,24204,24045,23887,23729,23572,23416,23260,23104,22950,22796,22642,22490,22337, +22186,22035,21884,21735,21586,21437,21289,21142,20996,20850,20704,20560,20416,20272, +20129,19987,19845,19705,19564,19425,19285,19147,19009,18872,18735,18599,18464,18329, +18195,18062,17929,17797,17665,17534,17404,17274,17145,17017,16889,16762,16635,16509, +16384,16259,16135,16011,15888,15766,15644,15523,15403,15283,15163,15044,14926,14809, +14692,14575,14460,14344,14230,14116,14002,13889,13777,13665,13554,13443,13333,13224, +13115,13007,12899,12792,12685,12579,12473,12368,12264,12160,12056,11953,11851,11749, +11648,11547,11447,11347,11248,11149,11051,10953,10856,10759,10663,10568,10472,10378, +10284,10190,10097,10004,9912,9820,9729,9638,9548,9458,9369,9280,9191,9104,9016,8929, +8843,8757,8671,8586,8501,8417,8333,8250,8167,8085,8003,7921,7840,7759,7679,7599, +7520,7441,7363,7285,7207,7130,7053,6977,6901,6826,6751,6676,6602,6528,6455,6382, +6310,6237,6166,6095,6024,5954,5884,5814,5745,5676,5608,5540,5473,5405,5339,5273, +5207,5141,5076,5012,4947,4884,4820,4757,4694,4632,4570,4509,4448,4387,4327,4267, +4208,4149,4090,4032,3974,3917,3859,3803,3746,3691,3635,3580,3525,3471,3417,3363, +3310,3257,3204,3152,3100,3049,2998,2947,2897,2847,2797,2748,2699,2651,2603,2555, +2507,2460,2414,2367,2321,2276,2230,2185,2141,2096,2052,2009,1966,1923,1880,1838, +1796,1754,1713,1672,1631,1591,1551,1511,1472,1432,1394,1355,1317,1279,1242,1204, +1167,1131,1094,1058,1023,987,952,917,882,848,814,780,747,713,680,648,615,583,551, +519,488,457,426,396,365,335,305,276,246,217,188,160,132,103,76,48,21,-7,-34,-60, +-87,-113,-139,-165,-190,-216,-241,-266,-290,-315,-339,-363,-387,-410,-434,-457, +-480,-502,-525,-547,-569,-591,-613,-634,-656,-677,-697,-718,-739,-759,-779,-799, +-818,-838,-857,-876,-895,-914,-932,-951,-969,-987,-1005,-1022,-1040,-1057,-1074, +-1091,-1107,-1124,-1140,-1156,-1172,-1188,-1203,-1219,-1234,-1249,-1264,-1279,-1293, +-1308,-1322,-1336,-1350,-1363,-1377,-1390,-1403,-1416,-1429,-1442,-1454,-1467,-1479, +-1491,-1503,-1514,-1526,-1537,-1548,-1559,-1570,-1581,-1592,-1602,-1613,-1623,-1633, +-1643,-1652,-1662,-1671,-1680,-1690,-1699,-1707,-1716,-1725,-1733,-1741,-1750,-1758, +-1765,-1773,-1781,-1788,-1796,-1803,-1810,-1817,-1824,-1830,-1837,-1843,-1850,-1856, +-1862,-1868,-1874,-1880,-1885,-1891,-1896,-1902,-1907,-1912,-1917,-1922,-1926,-1931, +-1935,-1940,-1944,-1948,-1952,-1956,-1960,-1964,-1968,-1971,-1975,-1978,-1982,-1985, +-1988,-1991,-1994,-1997,-2000,-2002,-2005,-2007,-2010,-2012,-2014,-2017,-2019,-2021, +-2022,-2024,-2026,-2028,-2029,-2031,-2032,-2034,-2035,-2036,-2037,-2038,-2039,-2040, +-2041,-2042,-2042,-2043,-2044,-2044,-2045,-2045,-2045,-2045,-2046,-2046,-2046,-2046, +-2046,-2045,-2045,-2045,-2044,-2044,-2044,-2043,-2042,-2042,-2041,-2040,-2039,-2039, +-2038,-2037,-2035,-2034,-2033,-2032,-2031,-2029,-2028,-2026,-2025,-2023,-2022,-2020, +-2018,-2017,-2015,-2013,-2011,-2009,-2007,-2005,-2003,-2001,-1999,-1996,-1994,-1992, +-1989,-1987,-1984,-1982,-1979,-1977,-1974,-1971,-1969,-1966,-1963,-1960,-1957,-1954, +-1951,-1948,-1945,-1942,-1939,-1936,-1933,-1929,-1926,-1923,-1919,-1916,-1913,-1909, +-1906,-1902,-1899,-1895,-1891,-1888,-1884,-1880,-1877,-1873,-1869,-1865,-1861,-1857, +-1853,-1849,-1845,-1841,-1837,-1833,-1829,-1825,-1821,-1817,-1813,-1809,-1804,-1800, +-1796,-1791,-1787,-1783,-1778,-1774,-1770,-1765,-1761,-1756,-1752,-1747,-1743,-1738, +-1734,-1729,-1725,-1720,-1716,-1711,-1706,-1702,-1697,-1692,-1688,-1683,-1678,-1673, +-1669,-1664,-1659,-1654,-1650,-1645,-1640,-1635,-1630,-1626,-1621,-1616,-1611,-1606, +-1601,-1596,-1591,-1586,-1582,-1577,-1572,-1567,-1562,-1557,-1552,-1547,-1542,-1537, +-1532,-1527,-1522,-1517,-1512,-1507,-1502,-1497,-1492,-1487,-1482,-1477,-1472,-1467, +-1462,-1457,-1452,-1447,-1442,-1437,-1432,-1427,-1422,-1417,-1412,-1407,-1402,-1397, +-1392,-1386,-1381,-1376,-1371,-1366,-1361,-1356,-1351,-1346,-1341,-1336,-1331,-1326, +-1321,-1316,-1311,-1306,-1301,-1296,-1291,-1286,-1281,-1276,-1271,-1266,-1261,-1256, +-1251,-1246,-1241,-1236,-1231,-1226,-1221,-1216,-1211,-1206,-1201,-1196,-1191,-1186, +-1181,-1176,-1171,-1166,-1161,-1156,-1152,-1147,-1142,-1137,-1132,-1127,-1122,-1117, +-1112,-1108,-1103,-1098,-1093,-1088,-1083,-1079,-1074,-1069,-1064,-1060,-1055,-1050, +-1045,-1040,-1036,-1031,-1026,-1022,-1017,-1012,-1008,-1003,-998,-994,-989,-984,-980, +-975,-970,-966,-961,-957,-952,-947,-943,-938,-934,-929,-925,-920,-916,-911,-907, +-902,-898,-894,-889,-885,-880,-876,-872,-867,-863,-859,-854,-850,-846,-841,-837, +-833,-828,-824,-820,-816,-812,-807,-803,-799,-795,-791,-787,-782,-778,-774,-770, +-766,-762,-758,-754,-750,-746,-742,-738,-734,-730,-726,-722,-718,-714,-710,-706, +-702,-699,-695,-691,-687,-683,-679,-676,-672,-668,-664,-661,-657,-653,-649,-646, +-642,-638,-635,-631,-627,-624,-620,-617,-613,-609,-606,-602,-599,-595,-592,-588, +-585,-581,-578,-574,-571,-568,-564,-561,-557,-554,-551,-547,-544,-541,-537,-534, +-531,-528,-524,-521,-518,-515,-511,-508,-505,-502,-499,-496,-492,-489,-486,-483, +-480,-477,-474,-471,-468,-465,-462,-459,-456,-453,-450,-447,-444,-441,-438,-435, +-433,-430,-427,-424,-421,-418,-416,-413,-410,-407,-405,-402,-399,-396,-394,-391, +-388,-386,-383,-380,-378,-375,-373,-370,-367,-365,-362,-360,-357,-355,-352,-350, +-347,-345,-342,-340,-337,-335,-333,-330,-328,-325,-323,-321,-318,-316,-314,-311, +-309,-307,-305,-302,-300,-298,-296,-293,-291,-289,-287,-285,-282,-280,-278,-276, +-274,-272,-270,-268,-266,-264,-261,-259,-257,-255,-253,-251,-249,-247,-246,-244, +-242,-240,-238,-236,-234,-232,-230,-228,-226,-225,-223,-221,-219,-217,-216,-214, +-212,-210,-209,-207,-205,-203,-202,-200,-198,-197,-195,-193,-192,-190,-188,-187, +-185,-183,-182,-180,-179,-177,-176,-174,-172,-171,-169,-168,-166,-165,-163,-162, +-161,-159,-158,-156,-155,-153,-152,-151,-149,-148,-146,-145,-144,-142,-141,-140, +-138,-137,-136,-134,-133,-132,-131,-129,-128,-127,-126,-124,-123,-122,-121,-120, +-118,-117,-116,-115,-114,-113,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102, +-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-85,-84,-83,-82, +-81,-80,-79,-78,-77,-76,-75,-75,-74,-73,-72,-71,-70,-70,-69,-68,-67,-66,-66,-65, +-64,-63,-62,-62,-61,-60,-59,-59,-58,-57,-56,-56,-55,-54,-54,-53,-52,-52,-51,-50, +-50,-49,-48,-48,-47,-46,-46,-45,-45,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38, +-38,-37,-36,-36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-29,-28, +-28,-27,-27,-26,-26,-25,-25,-25,-24,-24,-23,-23,-23,-22,-22,-21,-21,-21,-20,-20, +-20,-19,-19,-18,-18,-18,-17,-17,-17,-16,-16,-16,-15,-15,-15,-15,-14,-14,-14,-13, +-13,-13,-13,-12,-12,-12,-11,-11,-11,-11,-10,-10,-10,-10,-9,-9,-9,-9,-9,-8,-8,-8,-8, +-7,-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-4,-4,-3,-3,-3, +-3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +0,0,0,0,0,0,0,0,0, + } +}; diff --git a/thirdparty/libxmp/src/precomp_lut.h b/thirdparty/libxmp/src/precomp_lut.h new file mode 100644 index 0000000..6714672 --- /dev/null +++ b/thirdparty/libxmp/src/precomp_lut.h @@ -0,0 +1,524 @@ +static int16 cubic_spline_lut0[1024] = { + 0, -8, -16, -24, -32, -40, -47, -55, + -63, -71, -78, -86, -94, -101, -109, -117, + -124, -132, -139, -146, -154, -161, -169, -176, + -183, -190, -198, -205, -212, -219, -226, -233, + -240, -247, -254, -261, -268, -275, -282, -289, + -295, -302, -309, -316, -322, -329, -336, -342, + -349, -355, -362, -368, -375, -381, -388, -394, + -400, -407, -413, -419, -425, -432, -438, -444, + -450, -456, -462, -468, -474, -480, -486, -492, + -498, -504, -510, -515, -521, -527, -533, -538, + -544, -550, -555, -561, -566, -572, -577, -583, + -588, -594, -599, -604, -610, -615, -620, -626, + -631, -636, -641, -646, -651, -656, -662, -667, + -672, -677, -682, -686, -691, -696, -701, -706, + -711, -715, -720, -725, -730, -734, -739, -744, + -748, -753, -757, -762, -766, -771, -775, -780, + -784, -788, -793, -797, -801, -806, -810, -814, + -818, -822, -826, -831, -835, -839, -843, -847, + -851, -855, -859, -863, -866, -870, -874, -878, + -882, -886, -889, -893, -897, -900, -904, -908, + -911, -915, -918, -922, -925, -929, -932, -936, + -939, -943, -946, -949, -953, -956, -959, -962, + -966, -969, -972, -975, -978, -981, -984, -987, + -991, -994, -997, -999, -1002, -1005, -1008, -1011, + -1014, -1017, -1020, -1022, -1025, -1028, -1031, -1033, + -1036, -1039, -1041, -1044, -1047, -1049, -1052, -1054, + -1057, -1059, -1062, -1064, -1066, -1069, -1071, -1074, + -1076, -1078, -1080, -1083, -1085, -1087, -1089, -1092, + -1094, -1096, -1098, -1100, -1102, -1104, -1106, -1108, + -1110, -1112, -1114, -1116, -1118, -1120, -1122, -1124, + -1125, -1127, -1129, -1131, -1133, -1134, -1136, -1138, + -1139, -1141, -1143, -1144, -1146, -1147, -1149, -1150, + -1152, -1153, -1155, -1156, -1158, -1159, -1161, -1162, + -1163, -1165, -1166, -1167, -1169, -1170, -1171, -1172, + -1174, -1175, -1176, -1177, -1178, -1179, -1180, -1181, + -1182, -1184, -1185, -1186, -1187, -1187, -1188, -1189, + -1190, -1191, -1192, -1193, -1194, -1195, -1195, -1196, + -1197, -1198, -1198, -1199, -1200, -1200, -1201, -1202, + -1202, -1203, -1204, -1204, -1205, -1205, -1206, -1206, + -1207, -1207, -1208, -1208, -1208, -1209, -1209, -1210, + -1210, -1210, -1211, -1211, -1211, -1212, -1212, -1212, + -1212, -1212, -1213, -1213, -1213, -1213, -1213, -1213, + -1213, -1213, -1214, -1214, -1214, -1214, -1214, -1214, + -1214, -1214, -1213, -1213, -1213, -1213, -1213, -1213, + -1213, -1213, -1212, -1212, -1212, -1212, -1211, -1211, + -1211, -1211, -1210, -1210, -1210, -1209, -1209, -1209, + -1208, -1208, -1207, -1207, -1207, -1206, -1206, -1205, + -1205, -1204, -1204, -1203, -1202, -1202, -1201, -1201, + -1200, -1199, -1199, -1198, -1197, -1197, -1196, -1195, + -1195, -1194, -1193, -1192, -1192, -1191, -1190, -1189, + -1188, -1187, -1187, -1186, -1185, -1184, -1183, -1182, + -1181, -1180, -1179, -1178, -1177, -1176, -1175, -1174, + -1173, -1172, -1171, -1170, -1169, -1168, -1167, -1166, + -1165, -1163, -1162, -1161, -1160, -1159, -1158, -1156, + -1155, -1154, -1153, -1151, -1150, -1149, -1148, -1146, + -1145, -1144, -1142, -1141, -1140, -1138, -1137, -1135, + -1134, -1133, -1131, -1130, -1128, -1127, -1125, -1124, + -1122, -1121, -1119, -1118, -1116, -1115, -1113, -1112, + -1110, -1109, -1107, -1105, -1104, -1102, -1101, -1099, + -1097, -1096, -1094, -1092, -1091, -1089, -1087, -1085, + -1084, -1082, -1080, -1079, -1077, -1075, -1073, -1071, + -1070, -1068, -1066, -1064, -1062, -1061, -1059, -1057, + -1055, -1053, -1051, -1049, -1047, -1046, -1044, -1042, + -1040, -1038, -1036, -1034, -1032, -1030, -1028, -1026, + -1024, -1022, -1020, -1018, -1016, -1014, -1012, -1010, + -1008, -1006, -1004, -1002, -999, -997, -995, -993, + -991, -989, -987, -985, -982, -980, -978, -976, + -974, -972, -969, -967, -965, -963, -961, -958, + -956, -954, -952, -950, -947, -945, -943, -941, + -938, -936, -934, -931, -929, -927, -924, -922, + -920, -918, -915, -913, -911, -908, -906, -903, + -901, -899, -896, -894, -892, -889, -887, -884, + -882, -880, -877, -875, -872, -870, -867, -865, + -863, -860, -858, -855, -853, -850, -848, -845, + -843, -840, -838, -835, -833, -830, -828, -825, + -823, -820, -818, -815, -813, -810, -808, -805, + -803, -800, -798, -795, -793, -790, -787, -785, + -782, -780, -777, -775, -772, -769, -767, -764, + -762, -759, -757, -754, -751, -749, -746, -744, + -741, -738, -736, -733, -730, -728, -725, -723, + -720, -717, -715, -712, -709, -707, -704, -702, + -699, -696, -694, -691, -688, -686, -683, -680, + -678, -675, -672, -670, -667, -665, -662, -659, + -657, -654, -651, -649, -646, -643, -641, -638, + -635, -633, -630, -627, -625, -622, -619, -617, + -614, -611, -609, -606, -603, -601, -598, -595, + -593, -590, -587, -585, -582, -579, -577, -574, + -571, -569, -566, -563, -561, -558, -555, -553, + -550, -547, -545, -542, -539, -537, -534, -531, + -529, -526, -523, -521, -518, -516, -513, -510, + -508, -505, -502, -500, -497, -495, -492, -489, + -487, -484, -481, -479, -476, -474, -471, -468, + -466, -463, -461, -458, -455, -453, -450, -448, + -445, -442, -440, -437, -435, -432, -430, -427, + -424, -422, -419, -417, -414, -412, -409, -407, + -404, -402, -399, -397, -394, -392, -389, -387, + -384, -382, -379, -377, -374, -372, -369, -367, + -364, -362, -359, -357, -354, -352, -349, -347, + -345, -342, -340, -337, -335, -332, -330, -328, + -325, -323, -320, -318, -316, -313, -311, -309, + -306, -304, -302, -299, -297, -295, -292, -290, + -288, -285, -283, -281, -278, -276, -274, -272, + -269, -267, -265, -263, -260, -258, -256, -254, + -251, -249, -247, -245, -243, -240, -238, -236, + -234, -232, -230, -228, -225, -223, -221, -219, + -217, -215, -213, -211, -209, -207, -205, -202, + -200, -198, -196, -194, -192, -190, -188, -186, + -184, -182, -180, -178, -176, -175, -173, -171, + -169, -167, -165, -163, -161, -159, -157, -156, + -154, -152, -150, -148, -146, -145, -143, -141, + -139, -137, -136, -134, -132, -130, -129, -127, + -125, -124, -122, -120, -119, -117, -115, -114, + -112, -110, -109, -107, -106, -104, -102, -101, + -99, -98, -96, -95, -93, -92, -90, -89, + -87, -86, -84, -83, -82, -80, -79, -77, + -76, -75, -73, -72, -70, -69, -68, -67, + -65, -64, -63, -61, -60, -59, -58, -57, + -55, -54, -53, -52, -51, -49, -48, -47, + -46, -45, -44, -43, -42, -41, -40, -39, + -38, -37, -36, -35, -34, -33, -32, -31, + -30, -29, -28, -27, -26, -26, -25, -24, + -23, -22, -22, -21, -20, -19, -19, -18, + -17, -16, -16, -15, -14, -14, -13, -13, + -12, -11, -11, -10, -10, -9, -9, -8, + -8, -7, -7, -6, -6, -6, -5, -5, + -4, -4, -4, -3, -3, -3, -2, -2, + -2, -2, -2, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static int16 cubic_spline_lut1[1024] = { + 16384, 16384, 16384, 16384, 16384, 16383, 16382, 16381, + 16381, 16381, 16380, 16379, 16379, 16377, 16377, 16376, + 16374, 16373, 16371, 16370, 16369, 16366, 16366, 16364, + 16361, 16360, 16358, 16357, 16354, 16351, 16349, 16347, + 16345, 16342, 16340, 16337, 16335, 16331, 16329, 16326, + 16322, 16320, 16317, 16314, 16309, 16307, 16304, 16299, + 16297, 16293, 16290, 16285, 16282, 16278, 16274, 16269, + 16265, 16262, 16257, 16253, 16247, 16244, 16239, 16235, + 16230, 16225, 16220, 16216, 16211, 16206, 16201, 16196, + 16191, 16185, 16180, 16174, 16169, 16163, 16158, 16151, + 16146, 16140, 16133, 16128, 16122, 16116, 16109, 16104, + 16097, 16092, 16085, 16077, 16071, 16064, 16058, 16052, + 16044, 16038, 16030, 16023, 16015, 16009, 16002, 15995, + 15988, 15980, 15973, 15964, 15957, 15949, 15941, 15934, + 15926, 15918, 15910, 15903, 15894, 15886, 15877, 15870, + 15861, 15853, 15843, 15836, 15827, 15818, 15810, 15801, + 15792, 15783, 15774, 15765, 15756, 15747, 15738, 15729, + 15719, 15709, 15700, 15691, 15681, 15672, 15662, 15652, + 15642, 15633, 15623, 15613, 15602, 15592, 15582, 15572, + 15562, 15552, 15540, 15530, 15520, 15509, 15499, 15489, + 15478, 15467, 15456, 15446, 15433, 15423, 15412, 15401, + 15390, 15379, 15367, 15356, 15345, 15333, 15321, 15310, + 15299, 15287, 15276, 15264, 15252, 15240, 15228, 15216, + 15205, 15192, 15180, 15167, 15155, 15143, 15131, 15118, + 15106, 15094, 15081, 15067, 15056, 15043, 15031, 15017, + 15004, 14992, 14979, 14966, 14953, 14940, 14927, 14913, + 14900, 14887, 14874, 14860, 14846, 14833, 14819, 14806, + 14793, 14778, 14764, 14752, 14737, 14723, 14709, 14696, + 14681, 14668, 14653, 14638, 14625, 14610, 14595, 14582, + 14567, 14553, 14538, 14523, 14509, 14494, 14480, 14465, + 14450, 14435, 14420, 14406, 14391, 14376, 14361, 14346, + 14330, 14316, 14301, 14285, 14270, 14254, 14239, 14223, + 14208, 14192, 14177, 14161, 14146, 14130, 14115, 14099, + 14082, 14067, 14051, 14035, 14019, 14003, 13986, 13971, + 13955, 13939, 13923, 13906, 13890, 13873, 13857, 13840, + 13823, 13808, 13791, 13775, 13758, 13741, 13724, 13707, + 13691, 13673, 13657, 13641, 13623, 13607, 13589, 13572, + 13556, 13538, 13521, 13504, 13486, 13469, 13451, 13435, + 13417, 13399, 13383, 13365, 13347, 13330, 13312, 13294, + 13277, 13258, 13241, 13224, 13205, 13188, 13170, 13152, + 13134, 13116, 13098, 13080, 13062, 13044, 13026, 13008, + 12989, 12971, 12953, 12934, 12916, 12898, 12879, 12860, + 12842, 12823, 12806, 12787, 12768, 12750, 12731, 12712, + 12694, 12675, 12655, 12637, 12618, 12599, 12580, 12562, + 12542, 12524, 12504, 12485, 12466, 12448, 12427, 12408, + 12390, 12370, 12351, 12332, 12312, 12293, 12273, 12254, + 12235, 12215, 12195, 12176, 12157, 12137, 12118, 12097, + 12079, 12059, 12039, 12019, 11998, 11980, 11960, 11940, + 11920, 11900, 11880, 11860, 11839, 11821, 11801, 11780, + 11761, 11741, 11720, 11700, 11680, 11660, 11640, 11619, + 11599, 11578, 11559, 11538, 11518, 11498, 11477, 11457, + 11436, 11415, 11394, 11374, 11354, 11333, 11313, 11292, + 11272, 11251, 11231, 11209, 11189, 11168, 11148, 11127, + 11107, 11084, 11064, 11043, 11023, 11002, 10982, 10959, + 10939, 10918, 10898, 10876, 10856, 10834, 10814, 10792, + 10772, 10750, 10728, 10708, 10687, 10666, 10644, 10623, + 10602, 10581, 10560, 10538, 10517, 10496, 10474, 10453, + 10431, 10410, 10389, 10368, 10346, 10325, 10303, 10283, + 10260, 10239, 10217, 10196, 10175, 10152, 10132, 10110, + 10088, 10068, 10045, 10023, 10002, 9981, 9959, 9936, + 9915, 9893, 9872, 9851, 9829, 9806, 9784, 9763, + 9742, 9720, 9698, 9676, 9653, 9633, 9611, 9589, + 9567, 9545, 9523, 9501, 9479, 9458, 9436, 9414, + 9392, 9370, 9348, 9326, 9304, 9282, 9260, 9238, + 9216, 9194, 9172, 9150, 9128, 9106, 9084, 9062, + 9040, 9018, 8996, 8974, 8951, 8929, 8907, 8885, + 8863, 8841, 8819, 8797, 8775, 8752, 8730, 8708, + 8686, 8664, 8642, 8620, 8597, 8575, 8553, 8531, + 8509, 8487, 8464, 8442, 8420, 8398, 8376, 8353, + 8331, 8309, 8287, 8265, 8242, 8220, 8198, 8176, + 8154, 8131, 8109, 8087, 8065, 8042, 8020, 7998, + 7976, 7954, 7931, 7909, 7887, 7865, 7842, 7820, + 7798, 7776, 7754, 7731, 7709, 7687, 7665, 7643, + 7620, 7598, 7576, 7554, 7531, 7509, 7487, 7465, + 7443, 7421, 7398, 7376, 7354, 7332, 7310, 7288, + 7265, 7243, 7221, 7199, 7177, 7155, 7132, 7110, + 7088, 7066, 7044, 7022, 7000, 6978, 6956, 6934, + 6911, 6889, 6867, 6845, 6823, 6801, 6779, 6757, + 6735, 6713, 6691, 6669, 6647, 6625, 6603, 6581, + 6559, 6537, 6515, 6493, 6472, 6450, 6428, 6406, + 6384, 6362, 6340, 6318, 6297, 6275, 6253, 6231, + 6209, 6188, 6166, 6144, 6122, 6101, 6079, 6057, + 6035, 6014, 5992, 5970, 5949, 5927, 5905, 5884, + 5862, 5841, 5819, 5797, 5776, 5754, 5733, 5711, + 5690, 5668, 5647, 5625, 5604, 5582, 5561, 5540, + 5518, 5497, 5476, 5454, 5433, 5412, 5390, 5369, + 5348, 5327, 5305, 5284, 5263, 5242, 5221, 5199, + 5178, 5157, 5136, 5115, 5094, 5073, 5052, 5031, + 5010, 4989, 4968, 4947, 4926, 4905, 4885, 4864, + 4843, 4822, 4801, 4780, 4760, 4739, 4718, 4698, + 4677, 4656, 4636, 4615, 4595, 4574, 4553, 4533, + 4512, 4492, 4471, 4451, 4431, 4410, 4390, 4370, + 4349, 4329, 4309, 4288, 4268, 4248, 4228, 4208, + 4188, 4167, 4147, 4127, 4107, 4087, 4067, 4047, + 4027, 4007, 3988, 3968, 3948, 3928, 3908, 3889, + 3869, 3849, 3829, 3810, 3790, 3771, 3751, 3732, + 3712, 3693, 3673, 3654, 3634, 3615, 3595, 3576, + 3557, 3538, 3518, 3499, 3480, 3461, 3442, 3423, + 3404, 3385, 3366, 3347, 3328, 3309, 3290, 3271, + 3252, 3233, 3215, 3196, 3177, 3159, 3140, 3121, + 3103, 3084, 3066, 3047, 3029, 3010, 2992, 2974, + 2955, 2937, 2919, 2901, 2882, 2864, 2846, 2828, + 2810, 2792, 2774, 2756, 2738, 2720, 2702, 2685, + 2667, 2649, 2631, 2614, 2596, 2579, 2561, 2543, + 2526, 2509, 2491, 2474, 2456, 2439, 2422, 2405, + 2387, 2370, 2353, 2336, 2319, 2302, 2285, 2268, + 2251, 2234, 2218, 2201, 2184, 2167, 2151, 2134, + 2117, 2101, 2084, 2068, 2052, 2035, 2019, 2003, + 1986, 1970, 1954, 1938, 1922, 1906, 1890, 1874, + 1858, 1842, 1826, 1810, 1794, 1779, 1763, 1747, + 1732, 1716, 1701, 1685, 1670, 1654, 1639, 1624, + 1608, 1593, 1578, 1563, 1548, 1533, 1518, 1503, + 1488, 1473, 1458, 1444, 1429, 1414, 1400, 1385, + 1370, 1356, 1342, 1327, 1313, 1298, 1284, 1270, + 1256, 1242, 1228, 1214, 1200, 1186, 1172, 1158, + 1144, 1131, 1117, 1103, 1090, 1076, 1063, 1049, + 1036, 1022, 1009, 996, 983, 970, 956, 943, + 930, 917, 905, 892, 879, 866, 854, 841, + 828, 816, 803, 791, 778, 766, 754, 742, + 729, 717, 705, 693, 681, 669, 658, 646, + 634, 622, 611, 599, 588, 576, 565, 553, + 542, 531, 520, 508, 497, 486, 475, 464, + 453, 443, 432, 421, 411, 400, 389, 379, + 369, 358, 348, 338, 327, 317, 307, 297, + 287, 277, 268, 258, 248, 238, 229, 219, + 210, 200, 191, 182, 172, 163, 154, 145, + 136, 127, 118, 109, 100, 92, 83, 75, + 66, 58, 49, 41, 32, 24, 16, 8, +}; + +static int16 cubic_spline_lut2[1024] = { + 0, 8, 16, 24, 32, 41, 49, 58, + 66, 75, 83, 92, 100, 109, 118, 127, + 136, 145, 154, 163, 172, 182, 191, 200, + 210, 219, 229, 238, 248, 258, 268, 277, + 287, 297, 307, 317, 327, 338, 348, 358, + 369, 379, 389, 400, 411, 421, 432, 443, + 453, 464, 475, 486, 497, 508, 520, 531, + 542, 553, 565, 576, 588, 599, 611, 622, + 634, 646, 658, 669, 681, 693, 705, 717, + 729, 742, 754, 766, 778, 791, 803, 816, + 828, 841, 854, 866, 879, 892, 905, 917, + 930, 943, 956, 970, 983, 996, 1009, 1022, + 1036, 1049, 1063, 1076, 1090, 1103, 1117, 1131, + 1144, 1158, 1172, 1186, 1200, 1214, 1228, 1242, + 1256, 1270, 1284, 1298, 1313, 1327, 1342, 1356, + 1370, 1385, 1400, 1414, 1429, 1444, 1458, 1473, + 1488, 1503, 1518, 1533, 1548, 1563, 1578, 1593, + 1608, 1624, 1639, 1654, 1670, 1685, 1701, 1716, + 1732, 1747, 1763, 1779, 1794, 1810, 1826, 1842, + 1858, 1874, 1890, 1906, 1922, 1938, 1954, 1970, + 1986, 2003, 2019, 2035, 2052, 2068, 2084, 2101, + 2117, 2134, 2151, 2167, 2184, 2201, 2218, 2234, + 2251, 2268, 2285, 2302, 2319, 2336, 2353, 2370, + 2387, 2405, 2422, 2439, 2456, 2474, 2491, 2509, + 2526, 2543, 2561, 2579, 2596, 2614, 2631, 2649, + 2667, 2685, 2702, 2720, 2738, 2756, 2774, 2792, + 2810, 2828, 2846, 2864, 2882, 2901, 2919, 2937, + 2955, 2974, 2992, 3010, 3029, 3047, 3066, 3084, + 3103, 3121, 3140, 3159, 3177, 3196, 3215, 3233, + 3252, 3271, 3290, 3309, 3328, 3347, 3366, 3385, + 3404, 3423, 3442, 3461, 3480, 3499, 3518, 3538, + 3557, 3576, 3595, 3615, 3634, 3654, 3673, 3693, + 3712, 3732, 3751, 3771, 3790, 3810, 3829, 3849, + 3869, 3889, 3908, 3928, 3948, 3968, 3988, 4007, + 4027, 4047, 4067, 4087, 4107, 4127, 4147, 4167, + 4188, 4208, 4228, 4248, 4268, 4288, 4309, 4329, + 4349, 4370, 4390, 4410, 4431, 4451, 4471, 4492, + 4512, 4533, 4553, 4574, 4595, 4615, 4636, 4656, + 4677, 4698, 4718, 4739, 4760, 4780, 4801, 4822, + 4843, 4864, 4885, 4905, 4926, 4947, 4968, 4989, + 5010, 5031, 5052, 5073, 5094, 5115, 5136, 5157, + 5178, 5199, 5221, 5242, 5263, 5284, 5305, 5327, + 5348, 5369, 5390, 5412, 5433, 5454, 5476, 5497, + 5518, 5540, 5561, 5582, 5604, 5625, 5647, 5668, + 5690, 5711, 5733, 5754, 5776, 5797, 5819, 5841, + 5862, 5884, 5905, 5927, 5949, 5970, 5992, 6014, + 6035, 6057, 6079, 6101, 6122, 6144, 6166, 6188, + 6209, 6231, 6253, 6275, 6297, 6318, 6340, 6362, + 6384, 6406, 6428, 6450, 6472, 6493, 6515, 6537, + 6559, 6581, 6603, 6625, 6647, 6669, 6691, 6713, + 6735, 6757, 6779, 6801, 6823, 6845, 6867, 6889, + 6911, 6934, 6956, 6978, 7000, 7022, 7044, 7066, + 7088, 7110, 7132, 7155, 7177, 7199, 7221, 7243, + 7265, 7288, 7310, 7332, 7354, 7376, 7398, 7421, + 7443, 7465, 7487, 7509, 7531, 7554, 7576, 7598, + 7620, 7643, 7665, 7687, 7709, 7731, 7754, 7776, + 7798, 7820, 7842, 7865, 7887, 7909, 7931, 7954, + 7976, 7998, 8020, 8042, 8065, 8087, 8109, 8131, + 8154, 8176, 8198, 8220, 8242, 8265, 8287, 8309, + 8331, 8353, 8376, 8398, 8420, 8442, 8464, 8487, + 8509, 8531, 8553, 8575, 8597, 8620, 8642, 8664, + 8686, 8708, 8730, 8752, 8775, 8797, 8819, 8841, + 8863, 8885, 8907, 8929, 8951, 8974, 8996, 9018, + 9040, 9062, 9084, 9106, 9128, 9150, 9172, 9194, + 9216, 9238, 9260, 9282, 9304, 9326, 9348, 9370, + 9392, 9414, 9436, 9458, 9479, 9501, 9523, 9545, + 9567, 9589, 9611, 9633, 9653, 9676, 9698, 9720, + 9742, 9763, 9784, 9806, 9829, 9851, 9872, 9893, + 9915, 9936, 9959, 9981, 10002, 10023, 10045, 10068, + 10088, 10110, 10132, 10152, 10175, 10196, 10217, 10239, + 10260, 10283, 10303, 10325, 10346, 10368, 10389, 10410, + 10431, 10453, 10474, 10496, 10517, 10538, 10560, 10581, + 10602, 10623, 10644, 10666, 10687, 10708, 10728, 10750, + 10772, 10792, 10814, 10834, 10856, 10876, 10898, 10918, + 10939, 10959, 10982, 11002, 11023, 11043, 11064, 11084, + 11107, 11127, 11148, 11168, 11189, 11209, 11231, 11251, + 11272, 11292, 11313, 11333, 11354, 11374, 11394, 11415, + 11436, 11457, 11477, 11498, 11518, 11538, 11559, 11578, + 11599, 11619, 11640, 11660, 11680, 11700, 11720, 11741, + 11761, 11780, 11801, 11821, 11839, 11860, 11880, 11900, + 11920, 11940, 11960, 11980, 11998, 12019, 12039, 12059, + 12079, 12097, 12118, 12137, 12157, 12176, 12195, 12215, + 12235, 12254, 12273, 12293, 12312, 12332, 12351, 12370, + 12390, 12408, 12427, 12448, 12466, 12485, 12504, 12524, + 12542, 12562, 12580, 12599, 12618, 12637, 12655, 12675, + 12694, 12712, 12731, 12750, 12768, 12787, 12806, 12823, + 12842, 12860, 12879, 12898, 12916, 12934, 12953, 12971, + 12989, 13008, 13026, 13044, 13062, 13080, 13098, 13116, + 13134, 13152, 13170, 13188, 13205, 13224, 13241, 13258, + 13277, 13294, 13312, 13330, 13347, 13365, 13383, 13399, + 13417, 13435, 13451, 13469, 13486, 13504, 13521, 13538, + 13556, 13572, 13589, 13607, 13623, 13641, 13657, 13673, + 13691, 13707, 13724, 13741, 13758, 13775, 13791, 13808, + 13823, 13840, 13857, 13873, 13890, 13906, 13923, 13939, + 13955, 13971, 13986, 14003, 14019, 14035, 14051, 14067, + 14082, 14099, 14115, 14130, 14146, 14161, 14177, 14192, + 14208, 14223, 14239, 14254, 14270, 14285, 14301, 14316, + 14330, 14346, 14361, 14376, 14391, 14406, 14420, 14435, + 14450, 14465, 14480, 14494, 14509, 14523, 14538, 14553, + 14567, 14582, 14595, 14610, 14625, 14638, 14653, 14668, + 14681, 14696, 14709, 14723, 14737, 14752, 14764, 14778, + 14793, 14806, 14819, 14833, 14846, 14860, 14874, 14887, + 14900, 14913, 14927, 14940, 14953, 14966, 14979, 14992, + 15004, 15017, 15031, 15043, 15056, 15067, 15081, 15094, + 15106, 15118, 15131, 15143, 15155, 15167, 15180, 15192, + 15205, 15216, 15228, 15240, 15252, 15264, 15276, 15287, + 15299, 15310, 15321, 15333, 15345, 15356, 15367, 15379, + 15390, 15401, 15412, 15423, 15433, 15446, 15456, 15467, + 15478, 15489, 15499, 15509, 15520, 15530, 15540, 15552, + 15562, 15572, 15582, 15592, 15602, 15613, 15623, 15633, + 15642, 15652, 15662, 15672, 15681, 15691, 15700, 15709, + 15719, 15729, 15738, 15747, 15756, 15765, 15774, 15783, + 15792, 15801, 15810, 15818, 15827, 15836, 15843, 15853, + 15861, 15870, 15877, 15886, 15894, 15903, 15910, 15918, + 15926, 15934, 15941, 15949, 15957, 15964, 15973, 15980, + 15988, 15995, 16002, 16009, 16015, 16023, 16030, 16038, + 16044, 16052, 16058, 16064, 16071, 16077, 16085, 16092, + 16097, 16104, 16109, 16116, 16122, 16128, 16133, 16140, + 16146, 16151, 16158, 16163, 16169, 16174, 16180, 16185, + 16191, 16196, 16201, 16206, 16211, 16216, 16220, 16225, + 16230, 16235, 16239, 16244, 16247, 16253, 16257, 16262, + 16265, 16269, 16274, 16278, 16282, 16285, 16290, 16293, + 16297, 16299, 16304, 16307, 16309, 16314, 16317, 16320, + 16322, 16326, 16329, 16331, 16335, 16337, 16340, 16342, + 16345, 16347, 16349, 16351, 16354, 16357, 16358, 16360, + 16361, 16364, 16366, 16366, 16369, 16370, 16371, 16373, + 16374, 16376, 16377, 16377, 16379, 16379, 16380, 16381, + 16381, 16381, 16382, 16383, 16384, 16384, 16384, 16384, +}; + +static int16 cubic_spline_lut3[1024] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, -1, -1, -1, -1, -2, -2, + -2, -2, -2, -3, -3, -3, -4, -4, + -4, -5, -5, -6, -6, -6, -7, -7, + -8, -8, -9, -9, -10, -10, -11, -11, + -12, -13, -13, -14, -14, -15, -16, -16, + -17, -18, -19, -19, -20, -21, -22, -22, + -23, -24, -25, -26, -26, -27, -28, -29, + -30, -31, -32, -33, -34, -35, -36, -37, + -38, -39, -40, -41, -42, -43, -44, -45, + -46, -47, -48, -49, -51, -52, -53, -54, + -55, -57, -58, -59, -60, -61, -63, -64, + -65, -67, -68, -69, -70, -72, -73, -75, + -76, -77, -79, -80, -82, -83, -84, -86, + -87, -89, -90, -92, -93, -95, -96, -98, + -99, -101, -102, -104, -106, -107, -109, -110, + -112, -114, -115, -117, -119, -120, -122, -124, + -125, -127, -129, -130, -132, -134, -136, -137, + -139, -141, -143, -145, -146, -148, -150, -152, + -154, -156, -157, -159, -161, -163, -165, -167, + -169, -171, -173, -175, -176, -178, -180, -182, + -184, -186, -188, -190, -192, -194, -196, -198, + -200, -202, -205, -207, -209, -211, -213, -215, + -217, -219, -221, -223, -225, -228, -230, -232, + -234, -236, -238, -240, -243, -245, -247, -249, + -251, -254, -256, -258, -260, -263, -265, -267, + -269, -272, -274, -276, -278, -281, -283, -285, + -288, -290, -292, -295, -297, -299, -302, -304, + -306, -309, -311, -313, -316, -318, -320, -323, + -325, -328, -330, -332, -335, -337, -340, -342, + -345, -347, -349, -352, -354, -357, -359, -362, + -364, -367, -369, -372, -374, -377, -379, -382, + -384, -387, -389, -392, -394, -397, -399, -402, + -404, -407, -409, -412, -414, -417, -419, -422, + -424, -427, -430, -432, -435, -437, -440, -442, + -445, -448, -450, -453, -455, -458, -461, -463, + -466, -468, -471, -474, -476, -479, -481, -484, + -487, -489, -492, -495, -497, -500, -502, -505, + -508, -510, -513, -516, -518, -521, -523, -526, + -529, -531, -534, -537, -539, -542, -545, -547, + -550, -553, -555, -558, -561, -563, -566, -569, + -571, -574, -577, -579, -582, -585, -587, -590, + -593, -595, -598, -601, -603, -606, -609, -611, + -614, -617, -619, -622, -625, -627, -630, -633, + -635, -638, -641, -643, -646, -649, -651, -654, + -657, -659, -662, -665, -667, -670, -672, -675, + -678, -680, -683, -686, -688, -691, -694, -696, + -699, -702, -704, -707, -709, -712, -715, -717, + -720, -723, -725, -728, -730, -733, -736, -738, + -741, -744, -746, -749, -751, -754, -757, -759, + -762, -764, -767, -769, -772, -775, -777, -780, + -782, -785, -787, -790, -793, -795, -798, -800, + -803, -805, -808, -810, -813, -815, -818, -820, + -823, -825, -828, -830, -833, -835, -838, -840, + -843, -845, -848, -850, -853, -855, -858, -860, + -863, -865, -867, -870, -872, -875, -877, -880, + -882, -884, -887, -889, -892, -894, -896, -899, + -901, -903, -906, -908, -911, -913, -915, -918, + -920, -922, -924, -927, -929, -931, -934, -936, + -938, -941, -943, -945, -947, -950, -952, -954, + -956, -958, -961, -963, -965, -967, -969, -972, + -974, -976, -978, -980, -982, -985, -987, -989, + -991, -993, -995, -997, -999, -1002, -1004, -1006, + -1008, -1010, -1012, -1014, -1016, -1018, -1020, -1022, + -1024, -1026, -1028, -1030, -1032, -1034, -1036, -1038, + -1040, -1042, -1044, -1046, -1047, -1049, -1051, -1053, + -1055, -1057, -1059, -1061, -1062, -1064, -1066, -1068, + -1070, -1071, -1073, -1075, -1077, -1079, -1080, -1082, + -1084, -1085, -1087, -1089, -1091, -1092, -1094, -1096, + -1097, -1099, -1101, -1102, -1104, -1105, -1107, -1109, + -1110, -1112, -1113, -1115, -1116, -1118, -1119, -1121, + -1122, -1124, -1125, -1127, -1128, -1130, -1131, -1133, + -1134, -1135, -1137, -1138, -1140, -1141, -1142, -1144, + -1145, -1146, -1148, -1149, -1150, -1151, -1153, -1154, + -1155, -1156, -1158, -1159, -1160, -1161, -1162, -1163, + -1165, -1166, -1167, -1168, -1169, -1170, -1171, -1172, + -1173, -1174, -1175, -1176, -1177, -1178, -1179, -1180, + -1181, -1182, -1183, -1184, -1185, -1186, -1187, -1187, + -1188, -1189, -1190, -1191, -1192, -1192, -1193, -1194, + -1195, -1195, -1196, -1197, -1197, -1198, -1199, -1199, + -1200, -1201, -1201, -1202, -1202, -1203, -1204, -1204, + -1205, -1205, -1206, -1206, -1207, -1207, -1207, -1208, + -1208, -1209, -1209, -1209, -1210, -1210, -1210, -1211, + -1211, -1211, -1211, -1212, -1212, -1212, -1212, -1213, + -1213, -1213, -1213, -1213, -1213, -1213, -1213, -1214, + -1214, -1214, -1214, -1214, -1214, -1214, -1214, -1213, + -1213, -1213, -1213, -1213, -1213, -1213, -1213, -1212, + -1212, -1212, -1212, -1212, -1211, -1211, -1211, -1210, + -1210, -1210, -1209, -1209, -1208, -1208, -1208, -1207, + -1207, -1206, -1206, -1205, -1205, -1204, -1204, -1203, + -1202, -1202, -1201, -1200, -1200, -1199, -1198, -1198, + -1197, -1196, -1195, -1195, -1194, -1193, -1192, -1191, + -1190, -1189, -1188, -1187, -1187, -1186, -1185, -1184, + -1182, -1181, -1180, -1179, -1178, -1177, -1176, -1175, + -1174, -1172, -1171, -1170, -1169, -1167, -1166, -1165, + -1163, -1162, -1161, -1159, -1158, -1156, -1155, -1153, + -1152, -1150, -1149, -1147, -1146, -1144, -1143, -1141, + -1139, -1138, -1136, -1134, -1133, -1131, -1129, -1127, + -1125, -1124, -1122, -1120, -1118, -1116, -1114, -1112, + -1110, -1108, -1106, -1104, -1102, -1100, -1098, -1096, + -1094, -1092, -1089, -1087, -1085, -1083, -1080, -1078, + -1076, -1074, -1071, -1069, -1066, -1064, -1062, -1059, + -1057, -1054, -1052, -1049, -1047, -1044, -1041, -1039, + -1036, -1033, -1031, -1028, -1025, -1022, -1020, -1017, + -1014, -1011, -1008, -1005, -1002, -999, -997, -994, + -991, -987, -984, -981, -978, -975, -972, -969, + -966, -962, -959, -956, -953, -949, -946, -943, + -939, -936, -932, -929, -925, -922, -918, -915, + -911, -908, -904, -900, -897, -893, -889, -886, + -882, -878, -874, -870, -866, -863, -859, -855, + -851, -847, -843, -839, -835, -831, -826, -822, + -818, -814, -810, -806, -801, -797, -793, -788, + -784, -780, -775, -771, -766, -762, -757, -753, + -748, -744, -739, -734, -730, -725, -720, -715, + -711, -706, -701, -696, -691, -686, -682, -677, + -672, -667, -662, -656, -651, -646, -641, -636, + -631, -626, -620, -615, -610, -604, -599, -594, + -588, -583, -577, -572, -566, -561, -555, -550, + -544, -538, -533, -527, -521, -515, -510, -504, + -498, -492, -486, -480, -474, -468, -462, -456, + -450, -444, -438, -432, -425, -419, -413, -407, + -400, -394, -388, -381, -375, -368, -362, -355, + -349, -342, -336, -329, -322, -316, -309, -302, + -295, -289, -282, -275, -268, -261, -254, -247, + -240, -233, -226, -219, -212, -205, -198, -190, + -183, -176, -169, -161, -154, -146, -139, -132, + -124, -117, -109, -101, -94, -86, -78, -71, + -63, -55, -47, -40, -32, -24, -16, -8, +}; + diff --git a/thirdparty/libxmp/src/read_event.c b/thirdparty/libxmp/src/read_event.c new file mode 100644 index 0000000..6d1e40a --- /dev/null +++ b/thirdparty/libxmp/src/read_event.c @@ -0,0 +1,1654 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "effects.h" +#include "virtual.h" +#include "period.h" + +#ifndef LIBXMP_CORE_PLAYER +#include "med_extras.h" +#endif + + +static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx, + int ins, int key) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *instrument; + + if (IS_VALID_INSTRUMENT(ins)) { + instrument = &mod->xxi[ins]; + if (IS_VALID_NOTE(key)) { + int mapped = instrument->map[key].ins; + if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm) + return &instrument->sub[mapped]; + } else { + if (mod->xxi[ins].nsm > 0) { + return &instrument->sub[0]; + } + } + } + + return NULL; +} + +static void reset_envelopes(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xc->v_idx = -1; + xc->p_idx = -1; + xc->f_idx = -1; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static void reset_envelope_volume(struct context_data *ctx, + struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xc->v_idx = -1; +} + +static void reset_envelopes_carry(struct context_data *ctx, + struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xxi = libxmp_get_instrument(ctx, xc->ins); + + /* Reset envelope positions */ + if (~xxi->aei.flg & XMP_ENVELOPE_CARRY) { + xc->v_idx = -1; + } + if (~xxi->pei.flg & XMP_ENVELOPE_CARRY) { + xc->p_idx = -1; + } + if (~xxi->fei.flg & XMP_ENVELOPE_CARRY) { + xc->f_idx = -1; + } +} + +#endif + +static void set_effect_defaults(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + struct module_data *m = &ctx->m; + + if (sub != NULL && note >= 0) { + if (!HAS_QUIRK(QUIRK_PROTRACK)) { + xc->finetune = sub->fin; + } + xc->gvl = sub->gvl; + +#ifndef LIBXMP_CORE_DISABLE_IT + if (sub->ifc & 0x80) { + xc->filter.cutoff = (sub->ifc - 0x80) * 2; + } + xc->filter.envelope = 0x100; + + if (sub->ifr & 0x80) { + xc->filter.resonance = (sub->ifr - 0x80) * 2; + } + + /* IT: on a new note without toneporta, allow a computed cutoff + * of 127 with resonance 0 to disable the filter. */ + xc->filter.can_disable = !is_toneporta; +#endif + + /* TODO: should probably expand the LFO period size instead + * of reducing the vibrato rate precision here. + */ + libxmp_lfo_set_depth(&xc->insvib.lfo, sub->vde); + libxmp_lfo_set_rate(&xc->insvib.lfo, (sub->vra + 2) >> 2); + libxmp_lfo_set_waveform(&xc->insvib.lfo, sub->vwf); + xc->insvib.sweep = sub->vsw; + + libxmp_lfo_set_phase(&xc->vibrato.lfo, 0); + libxmp_lfo_set_phase(&xc->tremolo.lfo, 0); + } + + xc->delay = 0; + xc->tremor.up = xc->tremor.down = 0; + + /* Reset arpeggio */ + xc->arpeggio.val[0] = 0; + xc->arpeggio.count = 0; + xc->arpeggio.size = 1; +} + +/* From OpenMPT PortaTarget.mod: + * "A new note (with no portamento command next to it) does not reset the + * portamento target. That is, if a previous portamento has not finished yet, + * calling 3xx or 5xx after the new note will slide it towards the old target. + * Once the portamento target period is reached, the target is reset. This + * means that if the period is modified by another slide (e.g. 1xx or 2xx), + * a following 3xx will not slide back to the original target." + */ +static void set_period(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + struct module_data *m = &ctx->m; + + if (sub != NULL && note >= 0) { + double per = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + + if (!HAS_QUIRK(QUIRK_PROTRACK) || (note > 0 && is_toneporta)) { + xc->porta.target = per; + } + + if (xc->period < 1 || !is_toneporta) { + xc->period = per; + } + } +} + +/* From OpenMPT Porta-Pickup.xm: + * "An instrument number should not reset the current portamento target. The + * portamento target is valid until a new target is specified by combining a + * note and a portamento effect." + */ +static void set_period_ft2(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + if (note > 0 && is_toneporta) { + xc->porta.target = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } + if (sub != NULL && note >= 0) { + if (xc->period < 1 || !is_toneporta) { + xc->period = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } + } +} + + +#ifndef LIBXMP_CORE_PLAYER +#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB) +#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \ + || (x) == FX_PER_TPORTA || (x) == FX_ULT_TPORTA \ + || (x) == FX_FAR_TPORTA) +#else +#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE) +#endif + +#define set_patch(ctx,chn,ins,smp,note) \ + libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0, 0) + +static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int new_invalid_ins = 0; + int is_toneporta; + int use_ins_vol; + + xc->flags = 0; + note = -1; + is_toneporta = 0; + use_ins_vol = 0; + + if (IS_TONEPORTA(e->fxt) || IS_TONEPORTA(e->f2t)) { + is_toneporta = 1; + } + + /* Check instrument */ + + if (e->ins) { + int ins = e->ins - 1; + use_ins_vol = 1; + SET(NEW_INS); + xc->fadeout = 0x10000; /* for painlace.mod pat 0 ch 3 echo */ + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + sub = get_subinstrument(ctx, ins, e->note - 1); + + if (is_toneporta) { + /* Get new instrument volume */ + if (sub != NULL) { + /* Dennis Lindroos: instrument volume + * is not used on split channels + */ + if (!xc->split) { + xc->volume = sub->vol; + } + use_ins_vol = 0; + } + } else { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + + if (sub != NULL) { + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->finetune = sub->fin; + } + } + } + } else { + new_invalid_ins = 1; + libxmp_virt_resetchannel(ctx, chn); + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (!is_toneporta && IS_VALID_NOTE(e->note - 1)) { + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + RESET_PER(VOL_SLIDE); /* FIXME: should this be for FAR only? */ + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + +#ifndef LIBXMP_CORE_PLAYER + if (IS_SFX_PITCH(e->fxt)) { + xc->period = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } else +#endif + { + set_period(ctx, note, sub, xc, is_toneporta); + } + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (TEST(OFFSET)) { + if (HAS_QUIRK(QUIRK_PROTRACK) || p->flags & XMP_FLAGS_FX9BUG) { + xc->offset.val += xc->offset.val2; + } + RESET(OFFSET); + } + + if (use_ins_vol && !TEST(NEW_VOL) && !xc->split) { + xc->volume = sub->vol; + } + + return 0; +} + +static int sustain_check(struct xmp_envelope *env, int idx) +{ + return (env && + (env->flg & XMP_ENVELOPE_ON) && + (env->flg & XMP_ENVELOPE_SUS) && + (~env->flg & XMP_ENVELOPE_LOOP) && + idx == env->data[env->sus << 1]); +} + +static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note, key, ins; + struct xmp_subinstrument *sub; + int new_invalid_ins; + int is_toneporta; + int use_ins_vol; + int k00 = 0; + struct xmp_event ev; + + /* From the OpenMPT DelayCombination.xm test case: + * "Naturally, Fasttracker 2 ignores notes next to an out-of-range + * note delay. However, to check whether the delay is out of range, + * it is simply compared against the current song speed, not taking + * any pattern delays into account." + */ + if (p->frame >= p->speed) { + return 0; + } + + memcpy(&ev, e, sizeof (struct xmp_event)); + + /* From OpenMPT TremorReset.xm test case: + * "Even if a tremor effect muted the sample on a previous row, volume + * commands should be able to override this effect." + */ + if (ev.vol) { + xc->tremor.count &= ~0x80; + } + + xc->flags = 0; + note = -1; + key = ev.note; + ins = ev.ins; + new_invalid_ins = 0; + is_toneporta = 0; + use_ins_vol = 0; + + /* From the OpenMPT key_off.xm test case: + * "Key off at tick 0 (K00) is very dodgy command. If there is a note + * next to it, the note is ignored. If there is a volume column + * command or instrument next to it and the current instrument has + * no volume envelope, the note is faded out instead of being cut." + */ + if (ev.fxt == FX_KEYOFF && ev.fxp == 0) { + k00 = 1; + key = 0; + + if (ins || ev.vol || ev.f2t) { + if (IS_VALID_INSTRUMENT(xc->ins) && + ~mod->xxi[xc->ins].aei.flg & XMP_ENVELOPE_ON) { + SET_NOTE(NOTE_FADEOUT); + ev.fxt = 0; + } + } + } + + if (IS_TONEPORTA(ev.fxt) || IS_TONEPORTA(ev.f2t)) { + is_toneporta = 1; + } + + /* Check instrument */ + + /* Ignore invalid instruments. The last instrument, invalid or + * not, is preserved in channel data (see read_event() below). + * Fixes stray delayed notes in forgotten_city.xm. + */ + if (ins > 0 && !IS_VALID_INSTRUMENT(ins - 1)) { + ins = 0; + } + + /* FT2: Retrieve old instrument volume */ + if (ins) { + if (key == 0 || key >= XMP_KEY_OFF) { + /* Previous instrument */ + sub = get_subinstrument(ctx, xc->ins, xc->key); + + /* No note */ + if (sub != NULL) { + int pan = mod->xxc[chn].pan - 128; + xc->volume = sub->vol; + + if (!HAS_QUIRK(QUIRK_FTMOD)) { + xc->pan.val = pan + ((sub->pan - 128) * + (128 - abs(pan))) / 128 + 128; + } + + xc->ins_fade = mod->xxi[xc->ins].rls; + SET(NEW_VOL); + } + } + } + + /* Do this regardless if the instrument is invalid or not -- unless + * XM keyoff is used. Fixes xyce-dans_la_rue.xm chn 0 patterns 0E/0F and + * chn 10 patterns 0D/0E, see https://github.com/libxmp/libxmp/issues/152 + * for details. + */ + if (ev.ins && key != XMP_KEY_FADE) { + SET(NEW_INS); + use_ins_vol = 1; + xc->per_flags = 0; + + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT); + if (!k00) { + RESET_NOTE(NOTE_FADEOUT); + } + + xc->fadeout = 0x10000; + + if (IS_VALID_INSTRUMENT(ins - 1)) { + if (!is_toneporta) + xc->ins = ins - 1; + } else { + new_invalid_ins = 1; + + /* If no note is set FT2 doesn't cut on invalid + * instruments (it keeps playing the previous one). + * If a note is set it cuts the current sample. + */ + xc->flags = 0; + + if (is_toneporta) { + key = 0; + } + } + + xc->tremor.count = 0x20; + } + + /* Check note */ + if (ins) { + if (key > 0 && key < XMP_KEY_OFF) { + /* Retrieve volume when we have note */ + + /* and only if we have instrument, otherwise we're in + * case 1: new note and no instrument + */ + + /* Current instrument */ + sub = get_subinstrument(ctx, xc->ins, key - 1); + if (sub != NULL) { + int pan = mod->xxc[chn].pan - 128; + xc->volume = sub->vol; + + if (!HAS_QUIRK(QUIRK_FTMOD)) { + xc->pan.val = pan + ((sub->pan - 128) * + (128 - abs(pan))) / 128 + 128; + } + + xc->ins_fade = mod->xxi[xc->ins].rls; + } else { + xc->volume = 0; + } + SET(NEW_VOL); + } + } + + if (key) { + SET(NEW_NOTE); + + if (key == XMP_KEY_OFF) { + int env_on = 0; + int vol_set = ev.vol != 0 || ev.fxt == FX_VOLSET; + int delay_fx = ev.fxt == FX_EXTENDED && ev.fxp == 0xd0; + struct xmp_envelope *env = NULL; + + /* OpenMPT NoteOffVolume.xm: + * "If an instrument has no volume envelope, a note-off + * command should cut the sample completely - unless + * there is a volume command next it. This applies to + * both volume commands (volume and effect column)." + * + * ...and unless we have a keyoff+delay without setting + * an instrument. See OffDelay.xm. + */ + if (IS_VALID_INSTRUMENT(xc->ins)) { + env = &mod->xxi[xc->ins].aei; + if (env->flg & XMP_ENVELOPE_ON) { + env_on = 1; + } + } + + if (env_on || (!vol_set && (!ev.ins || !delay_fx))) { + if (sustain_check(env, xc->v_idx)) { + /* See OpenMPT EnvOff.xm. In certain + * cases a release event is effective + * only in the next frame + */ + SET_NOTE(NOTE_SUSEXIT); + } else { + SET_NOTE(NOTE_RELEASE); + } + use_ins_vol = 0; + } else { + SET_NOTE(NOTE_FADEOUT); + } + + /* See OpenMPT keyoff+instr.xm, pattern 2 row 0x40 */ + if (env_on && ev.fxt == FX_EXTENDED && + (ev.fxp >> 4) == EX_DELAY) { + /* See OpenMPT OffDelay.xm test case */ + if ((ev.fxp & 0xf) != 0) { + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT); + } + } + } else if (key == XMP_KEY_FADE) { + /* Handle keyoff + instrument case (NoteOff2.xm) */ + SET_NOTE(NOTE_FADEOUT); + } else if (is_toneporta) { + /* set key to 0 so we can have the tone portamento from + * the original note (see funky_stars.xm pos 5 ch 9) + */ + key = 0; + + /* And do the same if there's no keyoff (see comic + * bakery remix.xm pos 1 ch 3) + */ + } + + if (ev.ins == 0 && !IS_VALID_INSTRUMENT(xc->old_ins - 1)) { + new_invalid_ins = 1; + } + + if (new_invalid_ins) { + libxmp_virt_resetchannel(ctx, chn); + } + } + + + /* Check note range -- from the OpenMPT test NoteLimit.xm: + * "I think one of the first things Fasttracker 2 does when parsing a + * pattern cell is calculating the “real†note (i.e. pattern note + + * sample transpose), and if this “real†note falls out of its note + * range, it is ignored completely (wiped from its internal channel + * memory). The instrument number next it, however, is not affected + * and remains in the memory." + */ + sub = NULL; + if (IS_VALID_NOTE(key - 1)) { + int k = key - 1; + sub = get_subinstrument(ctx, xc->ins, k); + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[k].xpo; + int k2 = k + sub->xpo + transp; + if (k2 < 12 || k2 > 130) { + key = 0; + RESET(NEW_NOTE); + } + } + } + + if (IS_VALID_NOTE(key - 1)) { + xc->key = --key; + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_END); + + if (sub != NULL) { + if (~mod->xxi[xc->ins].aei.flg & XMP_ENVELOPE_ON) { + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + } + } + + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[key].xpo; + int smp; + + note = key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + + if (ins && sub != NULL && !k00) { + /* Reset envelopes on new instrument, see olympic.xm pos 10 + * But make sure we have an instrument set, see Letting go + * pos 4 chn 20 + */ + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (ev.vol) { + xc->volume = ev.vol - 1; + SET(NEW_VOL); + if (TEST_NOTE(NOTE_END)) { /* m5v-nine.xm */ + xc->fadeout = 0x10000; /* OpenMPT NoteOff.xm */ + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + } + } + + /* FT2: always reset sample offset */ + xc->offset.val = 0; + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, &ev, 1); + libxmp_process_fx(ctx, xc, chn, &ev, 0); + set_period_ft2(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + + /* From the OpenMPT test cases (3xx-no-old-samp.xm): + * "An offset effect that points beyond the sample end should + * stop playback on this channel." + * + * ... except in Skale Tracker (and possibly others), so make this a + * FastTracker2 quirk. See Armada Tanks game.it (actually an XM). + * Reported by Vladislav Suschikh. + */ + if (HAS_QUIRK(QUIRK_FT2BUGS) && xc->offset.val >= mod->xxs[sub->sid].len) { + libxmp_virt_resetchannel(ctx, chn); + } else { + + /* (From Decibelter - Cosmic 'Wegian Mamas.xm p04 ch7) + * We retrigger the sample only if we have a new note + * without tone portamento, otherwise we won't play + * sweeps and loops correctly. + */ + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int not_same_ins; + int is_toneporta; + int use_ins_vol; + + xc->flags = 0; + note = -1; + not_same_ins = 0; + is_toneporta = 0; + use_ins_vol = 0; + + if (IS_TONEPORTA(e->fxt) || IS_TONEPORTA(e->f2t)) { + is_toneporta = 1; + } + + if (libxmp_virt_mapchannel(ctx, chn) < 0 && xc->ins != e->ins - 1) { + is_toneporta = 0; + } + + /* Check instrument */ + + if (e->ins) { + int ins = e->ins - 1; + SET(NEW_INS); + use_ins_vol = 1; + xc->fadeout = 0x10000; + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + /* valid ins */ + if (xc->ins != ins) { + not_same_ins = 1; + if (!is_toneporta) { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + } else { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, e->note - 1); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } + } + } else { + /* invalid ins */ + + /* Ignore invalid instruments */ + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (is_toneporta) { + /* Always retrig in tone portamento: Fix portamento in + * 7spirits.s3m, mod.Biomechanoid + */ + if (not_same_ins) { + xc->offset.val = 0; + } + } else if (IS_VALID_NOTE(e->note - 1)) { + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (sub != NULL) { + int transp = mod->xxi[xc->ins].map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + if (HAS_QUIRK(QUIRK_ST3BUGS) && TEST(NEW_VOL)) { + xc->volume = xc->volume * p->gvol / m->volbase; + } + + return 0; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static inline void copy_channel(struct player_data *p, int to, int from) +{ + if (to > 0 && to != from) { + memcpy(&p->xc_data[to], &p->xc_data[from], + sizeof (struct channel_data)); + } +} + +static inline int has_note_event(struct xmp_event *e) +{ + return (e->note && e->note <= XMP_MAX_KEYS); +} + +static int check_fadeout(struct context_data *ctx, struct channel_data *xc, int ins) +{ + struct xmp_instrument *xxi = libxmp_get_instrument(ctx, ins); + + if (xxi == NULL) { + return 1; + } + + return (~xxi->aei.flg & XMP_ENVELOPE_ON || + ~xxi->aei.flg & XMP_ENVELOPE_CARRY || + xc->ins_fade == 0 || + xc->fadeout <= xc->ins_fade); +} + +static int check_invalid_sample(struct context_data *ctx, int ins, int key) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (ins < mod->ins) { + int smp = mod->xxi[ins].map[key].ins; + if (smp == 0xff || smp >= mod->smp) { + return 1; + }; + } + + return 0; +} + +static void fix_period(struct context_data *ctx, int chn, struct xmp_subinstrument *sub) +{ + if (sub->nna == XMP_INST_NNA_CONT) { + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *xxi = libxmp_get_instrument(ctx, xc->ins); + + xc->period = libxmp_note_to_period(ctx, xc->key + sub->xpo + + xxi->map[xc->key_porta].xpo, xc->finetune, xc->per_adj); + } +} + +static int is_same_sid(struct context_data *ctx, int chn, int ins, int key) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_subinstrument *s1, *s2; + + s1 = get_subinstrument(ctx, ins, key); + s2 = get_subinstrument(ctx, xc->ins, xc->key); + + return (s1 && s2 && s1->sid == s2->sid); +} + +static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note, key; + struct xmp_subinstrument *sub; + int not_same_ins, not_same_smp; + int new_invalid_ins; + int is_toneporta, is_release; + int candidate_ins; + int reset_env; + int reset_susloop; + int use_ins_vol; + int sample_mode; + int toneporta_offset; + int retrig_ins; + struct xmp_event ev; + + memcpy(&ev, e, sizeof (struct xmp_event)); + + /* Emulate Impulse Tracker "always read instrument" bug */ + if (ev.ins) { + xc->delayed_ins = 0; + } else if (ev.note && xc->delayed_ins) { + ev.ins = xc->delayed_ins; + xc->delayed_ins = 0; + } + + xc->flags = 0; + note = -1; + key = ev.note; + not_same_ins = 0; + not_same_smp = 0; + new_invalid_ins = 0; + is_toneporta = 0; + is_release = 0; + reset_env = 0; + reset_susloop = 0; + use_ins_vol = 0; + candidate_ins = xc->ins; + sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL); + toneporta_offset = 0; + retrig_ins = 0; + + /* Keyoff + instrument retrigs current instrument in old fx mode */ + if (HAS_QUIRK(QUIRK_ITOLDFX)) { + if (ev.note == XMP_KEY_OFF && IS_VALID_INSTRUMENT(ev.ins -1)) { + retrig_ins = 1; + } + } + + /* Notes with unmapped instruments are ignored */ + if (ev.ins) { + if (ev.ins <= mod->ins && has_note_event(&ev)) { + int ins = ev.ins - 1; + if (check_invalid_sample(ctx, ins, ev.note - 1)) { + candidate_ins = ins; + memset(&ev, 0, sizeof (ev)); + } + } + } else { + if (has_note_event(&ev)) { + int ins = xc->old_ins - 1; + if (!IS_VALID_INSTRUMENT(ins)) { + new_invalid_ins = 1; + } else if (check_invalid_sample(ctx, ins, ev.note - 1)) { + memset(&ev, 0, sizeof (ev)); + } + } + } + + if (IS_TONEPORTA(ev.fxt) || IS_TONEPORTA(ev.f2t)) { + is_toneporta = 1; + } + + if (TEST_NOTE(NOTE_ENV_RELEASE | NOTE_FADEOUT)) { + is_release = 1; + } + + if (xc->period <= 0 || TEST_NOTE(NOTE_END)) { + is_toneporta = 0; + } + + /* Off-Porta.it */ + if (is_toneporta && ev.fxt == FX_OFFSET) { + toneporta_offset = 1; + if (!HAS_QUIRK(QUIRK_PRENV)) { + RESET_NOTE(NOTE_ENV_END); + } + } + + /* Check instrument */ + + if (ev.ins) { + int ins = ev.ins - 1; + int set_new_ins = 1; + + /* portamento_after_keyoff.it test case */ + if (is_release && !key) { + if (is_toneporta) { + if (HAS_QUIRK(QUIRK_PRENV) || TEST_NOTE(NOTE_SET)) { + is_toneporta = 0; + reset_envelopes_carry(ctx, xc); + } + } else { + /* fixes OpenMPT wnoteoff.it */ + reset_envelopes_carry(ctx, xc); + } + } + + if (is_toneporta && xc->ins == ins) { + if (!HAS_QUIRK(QUIRK_PRENV)) { + if (is_same_sid(ctx, chn, ins, key - 1)) { + /* same instrument and same sample */ + set_new_ins = !is_release; + } else { + /* same instrument, different sample */ + not_same_ins = 1; /* need this too */ + not_same_smp = 1; + } + } + } + + if (set_new_ins) { + SET(NEW_INS); + reset_env = 1; + } + /* Sample default volume is always enabled if a valid sample + * is provided (Atomic Playboy, default_volume.it). */ + use_ins_vol = 1; + xc->per_flags = 0; + + if (IS_VALID_INSTRUMENT(ins)) { + /* valid ins */ + + /* See OpenMPT StoppedInstrSwap.it for cut case */ + if (!key && !TEST_NOTE(NOTE_KEY_CUT)) { + /* Retrig in new ins in sample mode */ + if (sample_mode && TEST_NOTE(NOTE_END)) { + libxmp_virt_voicepos(ctx, chn, 0); + } + + /* IT: Reset note for every new != ins */ + if (xc->ins == ins) { + SET(NEW_INS); + use_ins_vol = 1; + } else { + key = xc->key + 1; + } + + RESET_NOTE(NOTE_SET); + } + + if (xc->ins != ins && (!is_toneporta || !HAS_QUIRK(QUIRK_PRENV))) { + candidate_ins = ins; + + if (!is_same_sid(ctx, chn, ins, key - 1)) { + not_same_ins = 1; + if (is_toneporta) { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, key); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } + } + } + } else { + /* In sample mode invalid instruments cut the current + * note (OpenMPT SampleNumberChange.it). + * TODO: portamento_sustain.it order 3 row 19: when + * sample release is set, this isn't always done? */ + if (sample_mode) { + xc->volume = 0; + } + + /* Ignore invalid instruments */ + new_invalid_ins = 1; + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Check note */ + + if (key) { + SET(NEW_NOTE); + SET_NOTE(NOTE_SET); + + if (key == XMP_KEY_FADE) { + SET_NOTE(NOTE_FADEOUT); + reset_env = 0; + reset_susloop = 0; + use_ins_vol = 0; + } else if (key == XMP_KEY_CUT) { + SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + } else if (key == XMP_KEY_OFF) { + struct xmp_envelope *env = NULL; + if (IS_VALID_INSTRUMENT(xc->ins)) { + env = &mod->xxi[xc->ins].aei; + } + if (sustain_check(env, xc->v_idx)) { + SET_NOTE(NOTE_SUSEXIT); + } else { + SET_NOTE(NOTE_RELEASE); + } + SET(KEY_OFF); + /* Use instrument volume if an instrument was explicitly + * provided on this row (see OpenMPT NoteOffInstr.it row 4). + * However, never reset the envelope (see OpenMPT wnoteoff.it). + */ + reset_env = 0; + reset_susloop = 0; + if (!ev.ins) { + use_ins_vol = 0; + } + } else if (!new_invalid_ins) { + /* Sample sustain release should always carry for tone + * portamento, and is not reset unless a note is + * present (Atomic Playboy, portamento_sustain.it). */ + /* portamento_after_keyoff.it test case */ + /* also see suburban_streets o13 c45 */ + if (!is_toneporta) { + reset_env = 1; + reset_susloop = 1; + } + + if (is_toneporta) { + if (not_same_ins || TEST_NOTE(NOTE_END)) { + SET(NEW_INS); + RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } else { + if (IS_VALID_NOTE(key - 1)) { + xc->key_porta = key - 1; + } + key = 0; + } + } + } + } + + /* TODO: instrument change+porta(+release?) doesn't require a key. + * Order 3/row 11 of portamento_sustain.it should change the sample. */ + if (IS_VALID_NOTE(key - 1) && !new_invalid_ins) { + if (TEST_NOTE(NOTE_CUT)) { + use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */ + } + xc->key = --key; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, candidate_ins, key); + + if (sub != NULL) { + int transp = mod->xxi[candidate_ins].map[key].xpo; + int smp, to; + int dct; + int rvv; + + /* Clear note delay before duplicating channels: + * it_note_delay_nna.it */ + xc->delay = 0; + + note = key + sub->xpo + transp; + smp = sub->sid; + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + dct = sub->dct; + + if (not_same_smp) { + fix_period(ctx, chn, sub); + /* Toneporta, even when not executed, disables + * NNA and DCAs for the current note: + * portamento_nna_sample.it, gxsmp2.it */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT); + dct = XMP_INST_DCT_OFF; + } + to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp, + note, key, sub->nna, dct, sub->dca); + + /* Random value for volume swing */ + rvv = sub->rvv & 0xff; + if (rvv) { + CLAMP(rvv, 0, 100); + xc->rvv = rand() % (rvv + 1); + } else { + xc->rvv = 0; + } + + /* Random value for pan swing */ + rvv = (sub->rvv & 0xff00) >> 8; + if (rvv) { + CLAMP(rvv, 0, 64); + xc->rpv = rand() % (rvv + 1) - (rvv / 2); + } else { + xc->rpv = 0; + } + + if (to < 0) + return -1; + if (to != chn) { + copy_channel(p, to, chn); + p->xc_data[to].flags = 0; + } + + if (smp >= 0) { /* Not sure if needed */ + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Do after virtual channel copy */ + if (is_toneporta || retrig_ins) { + if (HAS_QUIRK(QUIRK_PRENV) && ev.ins) { + reset_envelopes_carry(ctx, xc); + } + } + + if (IS_VALID_INSTRUMENT(candidate_ins)) { + if (xc->ins != candidate_ins) { + /* Reset envelopes if instrument changes */ + reset_envelopes(ctx, xc); + } + xc->ins = candidate_ins; + xc->ins_fade = mod->xxi[candidate_ins].rls; + } + + /* Reset in case of new instrument and the previous envelope has + * finished (OpenMPT test EnvReset.it). This must take place after + * channel copies in case of NNA (see test/test.it) + * Also if we have envelope in carry mode, check fadeout + * Also, only reset the volume envelope. (it_fade_env_reset_carry.it) + */ + if (ev.ins && TEST_NOTE(NOTE_ENV_END)) { + if (check_fadeout(ctx, xc, candidate_ins)) { + reset_envelope_volume(ctx, xc); + } else { + reset_env = 0; + } + } + + if (reset_env) { + if (ev.note) { + RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } + /* Set after copying to new virtual channel (see ambio.it) */ + xc->fadeout = 0x10000; + } + if (reset_susloop && ev.note) { + RESET_NOTE(NOTE_SAMPLE_RELEASE); + } + + /* See OpenMPT wnoteoff.it vs noteoff3.it */ + if (retrig_ins && not_same_ins) { + SET(NEW_INS); + libxmp_virt_voicepos(ctx, chn, 0); + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (sub != NULL) { + if (note >= 0) { + /* Reset pan, see OpenMPT PanReset.it */ + if (sub->pan >= 0) { + xc->pan.val = sub->pan; + xc->pan.surround = 0; + } + + if (TEST_NOTE(NOTE_CUT)) { + reset_envelopes(ctx, xc); + } else if (!toneporta_offset || HAS_QUIRK(QUIRK_PRENV)) { + reset_envelopes_carry(ctx, xc); + } + RESET_NOTE(NOTE_CUT); + } + } + + /* Process new volume */ + if (ev.vol && (!TEST_NOTE(NOTE_CUT) || ev.ins != 0)) { + /* Do this even for XMP_KEY_OFF (see OpenMPT NoteOffInstr.it row 4). */ + xc->volume = ev.vol - 1; + SET(NEW_VOL); + } + + /* IT: always reset sample offset */ + xc->offset.val &= ~0xffff; + + /* According to Storlek test 25, Impulse Tracker handles the volume + * column effects after the standard effects. + */ + libxmp_process_fx(ctx, xc, chn, &ev, 0); + libxmp_process_fx(ctx, xc, chn, &ev, 1); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + } + if (note >= 0 || toneporta_offset) { + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +#endif + +#ifndef LIBXMP_CORE_PLAYER + +static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int new_invalid_ins = 0; + int is_toneporta; + int use_ins_vol; + int finetune; + + xc->flags = 0; + note = -1; + is_toneporta = 0; + use_ins_vol = 0; + + if (e->fxt == FX_TONEPORTA || e->fxt == FX_TONE_VSLIDE) { + is_toneporta = 1; + } + + /* Check instrument */ + + if (e->ins && e->note) { + int ins = e->ins - 1; + use_ins_vol = 1; + SET(NEW_INS); + xc->fadeout = 0x10000; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + if (is_toneporta) { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, e->note - 1); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } else { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + } + } else { + new_invalid_ins = 1; + libxmp_virt_resetchannel(ctx, chn); + } + + MED_CHANNEL_EXTRAS(*xc)->arp = 0; + MED_CHANNEL_EXTRAS(*xc)->aidx = 0; + } else { + /* Hold */ + if (e->ins && !e->note) { + use_ins_vol = 1; + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (e->note == XMP_KEY_CUT) { + SET_NOTE(NOTE_END); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + } else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && IS_VALID_NOTE(e->note - 1)) { + struct xmp_instrument *xxi = &mod->xxi[xc->ins]; + + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + xc->per_adj = 0.0; + if (xxi->nsm > 1 && HAS_MED_INSTRUMENT_EXTRAS(*xxi)) { + /* synth or iffoct */ + if (MED_INSTRUMENT_EXTRAS(*xxi)->vts == 0 && + MED_INSTRUMENT_EXTRAS(*xxi)->wts == 0) { + /* iffoct */ + xc->per_adj = 2.0; + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (!new_invalid_ins && sub != NULL) { + int transp = xxi->map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + /* Keep effect-set finetune if no instrument set */ + finetune = xc->finetune; + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (!e->ins) { + xc->finetune = finetune; + } + + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +#endif + +static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_subinstrument *sub; + struct xmp_instrument *xxi; + int ins, note, transp, smp; + + xc->flags = 0; + + if (!e->ins) + return 0; + + ins = e->ins - 1; + SET(NEW_INS); + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + xxi = libxmp_get_instrument(ctx, ins); + if (xxi != NULL) { + xc->ins_fade = xxi->rls; + } + xc->ins = ins; + + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + return 0; + } else if (e->note == XMP_KEY_FADE) { + SET_NOTE(NOTE_FADEOUT); + return 0; + } else if (e->note == XMP_KEY_CUT) { + SET_NOTE(NOTE_END); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + return 0; + } + + xc->key = e->note - 1; + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_END); + + if (ins >= mod->ins && ins < mod->ins + smix->ins) { + sub = &xxi->sub[0]; + if (sub == NULL) { + return 0; + } + + note = xc->key + sub->xpo; + smp = sub->sid; + if (smix->xxs[smp].len == 0) + smp = -1; + if (smp >= 0 && smp < smix->smp) { + smp += mod->smp; + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + sub = IS_VALID_NOTE(xc->key) ? + get_subinstrument(ctx, xc->ins, xc->key) : NULL; + if (sub == NULL) { + return 0; + } + transp = xxi->map[xc->key].xpo; + note = xc->key + sub->xpo + transp; + smp = sub->sid; + if (!IS_VALID_SAMPLE(smp)) + smp = -1; + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } + + set_effect_defaults(ctx, note, sub, xc, 0); + set_period(ctx, note, sub, xc, 0); + + if (e->ins) { + reset_envelopes(ctx, xc); + } + + xc->volume = e->vol - 1; + + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + + return 0; +} + +int libxmp_read_event(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + + if (e->ins != 0) + xc->old_ins = e->ins; + + if (TEST_NOTE(NOTE_SAMPLE_END)) { + SET_NOTE(NOTE_END); + } + + if (chn >= m->mod.chn) { + return read_event_smix(ctx, e, chn); + } else switch (m->read_event_type) { + case READ_EVENT_MOD: + return read_event_mod(ctx, e, chn); + case READ_EVENT_FT2: + return read_event_ft2(ctx, e, chn); + case READ_EVENT_ST3: + return read_event_st3(ctx, e, chn); +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + return read_event_it(ctx, e, chn); +#endif +#ifndef LIBXMP_CORE_PLAYER + case READ_EVENT_MED: + return read_event_med(ctx, e, chn); +#endif + default: + return read_event_mod(ctx, e, chn); + } +} diff --git a/thirdparty/libxmp/src/scan.c b/thirdparty/libxmp/src/scan.c new file mode 100644 index 0000000..a1a2daa --- /dev/null +++ b/thirdparty/libxmp/src/scan.c @@ -0,0 +1,721 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Sun, 31 May 1998 17:50:02 -0600 + * Reported by ToyKeeper : + * For loop-prevention, I know a way to do it which lets most songs play + * fine once through even if they have backward-jumps. Just keep a small + * array (256 bytes, or even bits) of flags, each entry determining if a + * pattern in the song order has been played. If you get to an entry which + * is already non-zero, skip to the next song (assuming looping is off). + */ + +/* + * Tue, 6 Oct 1998 21:23:17 +0200 (CEST) + * Reported by John v/d Kamp : + * scan.c was hanging when it jumps to an invalid restart value. + * (Fixed by hipolito) + */ + + +#include "common.h" +#include "effects.h" +#include "mixer.h" + +#ifndef LIBXMP_CORE_PLAYER +#include "far_extras.h" +#endif + +#define VBLANK_TIME_THRESHOLD 480000 /* 8 minutes */ + +#define S3M_END 0xff +#define S3M_SKIP 0xfe + + +static int scan_module(struct context_data *ctx, int ep, int chain) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + const struct xmp_module *mod = &m->mod; + const struct xmp_track *tracks[XMP_MAX_CHANNELS]; + const struct xmp_event *event; + int parm, gvol_memory, f1, f2, p1, p2, ord, ord2; + int row, last_row, break_row, row_count, row_count_total; + int orders_since_last_valid, any_valid; + int gvl, bpm, speed, base_time, chn; + int frame_count; + double time, start_time; + int loop_chn, loop_num, inside_loop, line_jump; + int pdelay = 0; + int loop_count[XMP_MAX_CHANNELS]; + int loop_row[XMP_MAX_CHANNELS]; + int i, pat; + int has_marker; + struct ord_data *info; +#ifndef LIBXMP_CORE_PLAYER + int st26_speed; + int far_tempo_coarse, far_tempo_fine, far_tempo_mode; +#endif + /* was 255, but Global trash goes to 318. + * Higher limit for MEDs, defiance.crybaby.5 has blocks with 2048+ rows. */ + const int row_limit = IS_PLAYER_MODE_MED() ? 3200 : 512; + + if (mod->len == 0) + return 0; + + for (i = 0; i < mod->len; i++) { + pat = mod->xxo[i]; + memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 : + mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1); + } + + for (i = 0; i < mod->chn; i++) { + loop_count[i] = 0; + loop_row[i] = -1; + } + loop_num = 0; + loop_chn = -1; + line_jump = 0; + + gvl = mod->gvl; + bpm = mod->bpm; + + speed = mod->spd; + base_time = m->rrate; +#ifndef LIBXMP_CORE_PLAYER + st26_speed = 0; + far_tempo_coarse = 4; + far_tempo_fine = 0; + far_tempo_mode = 1; + + if (HAS_FAR_MODULE_EXTRAS(ctx->m)) { + far_tempo_coarse = FAR_MODULE_EXTRAS(ctx->m)->coarse_tempo; + libxmp_far_translate_tempo(far_tempo_mode, 0, far_tempo_coarse, + &far_tempo_fine, &speed, &bpm); + } +#endif + + has_marker = HAS_QUIRK(QUIRK_MARKER); + + /* By erlk ozlr + * + * xmp doesn't handle really properly the "start" option (-s for the + * command-line) for DeusEx's .umx files. These .umx files contain + * several loop "tracks" that never join together. That's how they put + * multiple musics on each level with a file per level. Each "track" + * starts at the same order in all files. The problem is that xmp starts + * decoding the module at order 0 and not at the order specified with + * the start option. If we have a module that does "0 -> 2 -> 0 -> ...", + * we cannot play order 1, even with the supposed right option. + * + * was: ord2 = ord = -1; + * + * CM: Fixed by using different "sequences" for each loop or subsong. + * Each sequence has its entry point. Sequences don't overlap. + */ + ord2 = -1; + ord = ep - 1; + + gvol_memory = break_row = row_count = row_count_total = frame_count = 0; + orders_since_last_valid = any_valid = 0; + start_time = time = 0.0; + inside_loop = 0; + + while (42) { + /* Sanity check to prevent getting stuck due to broken patterns. */ + if (orders_since_last_valid > 512) { + D_(D_CRIT "orders_since_last_valid = %d @ ord %d; ending scan", orders_since_last_valid, ord); + break; + } + orders_since_last_valid++; + + if ((uint32)++ord >= mod->len) { + if (mod->rst > mod->len || mod->xxo[mod->rst] >= mod->pat) { + ord = ep; + } else { + if (libxmp_get_sequence(ctx, mod->rst) == chain) { + ord = mod->rst; + } else { + ord = ep; + } + } + + pat = mod->xxo[ord]; + if (has_marker && pat == S3M_END) { + break; + } + } + + pat = mod->xxo[ord]; + info = &m->xxo_info[ord]; + + /* Allow more complex order reuse only in main sequence */ + if (ep != 0 && p->sequence_control[ord] != 0xff) { + break; + } + p->sequence_control[ord] = chain; + + /* All invalid patterns skipped, only S3M_END aborts replay */ + if (pat >= mod->pat) { + if (has_marker && pat == S3M_END) { + ord = mod->len; + continue; + } + continue; + } + + if (break_row >= mod->xxp[pat]->rows) { + break_row = 0; + } + + /* Loops can cross pattern boundaries, so check if we're not looping */ + if (m->scan_cnt[ord][break_row] && !inside_loop) { + break; + } + + /* Only update pattern information if we weren't here before. This also + * means that we don't update pattern information if we're inside a loop, + * otherwise a loop containing e.g. a global volume fade can make the + * pattern start with the wrong volume. (fixes xyce-dans_la_rue.xm replay, + * see https://github.com/libxmp/libxmp/issues/153 for more details). + */ + if (info->time < 0) { + info->gvl = gvl; + info->bpm = bpm; + info->speed = speed; + info->time = time + m->time_factor * frame_count * base_time / bpm; +#ifndef LIBXMP_CORE_PLAYER + info->st26_speed = st26_speed; +#endif + } + + if (info->start_row == 0 && ord != 0) { + if (ord == ep) { + start_time = time + m->time_factor * frame_count * base_time / bpm; + } + + info->start_row = break_row; + } + + /* Get tracks in advance to speed up the event parsing loop. */ + for (chn = 0; chn < mod->chn; chn++) { + tracks[chn] = mod->xxt[TRACK_NUM(pat, chn)]; + } + + last_row = mod->xxp[pat]->rows; + for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) { + /* Prevent crashes caused by large softmixer frames */ + if (bpm < XMP_MIN_BPM) { + bpm = XMP_MIN_BPM; + } + + /* Date: Sat, 8 Sep 2007 04:01:06 +0200 + * Reported by Zbigniew Luszpinski + * The scan routine falls into infinite looping and doesn't let + * xmp play jos-dr4k.xm. + * Claudio's workaround: we'll break infinite loops here. + * + * Date: Oct 27, 2007 8:05 PM + * From: Adric Riedel + * Jesper Kyd: Global Trash 3.mod (the 'Hardwired' theme) only + * plays the first 4:41 of what should be a 10 minute piece. + * (...) it dies at the end of position 2F + */ + + if (row_count_total > row_limit) { + D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row); + goto end_module; + } + + if (!loop_num && !line_jump && m->scan_cnt[ord][row]) { + row_count--; + goto end_module; + } + m->scan_cnt[ord][row]++; + orders_since_last_valid = 0; + any_valid = 1; + + /* If the scan count for this row overflows, break. + * A scan count of 0 will help break this loop in playback (storlek_11.it). + */ + if (!m->scan_cnt[ord][row]) { + goto end_module; + } + + pdelay = 0; + line_jump = 0; + + for (chn = 0; chn < mod->chn; chn++) { + if (row >= tracks[chn]->rows) + continue; + + //event = &EVENT(mod->xxo[ord], chn, row); + event = &tracks[chn]->event[row]; + + f1 = event->fxt; + p1 = event->fxp; + f2 = event->f2t; + p2 = event->f2p; + + if (f1 == FX_GLOBALVOL || f2 == FX_GLOBALVOL) { + gvl = (f1 == FX_GLOBALVOL) ? p1 : p2; + gvl = gvl > m->gvolbase ? m->gvolbase : gvl < 0 ? 0 : gvl; + } + + /* Process fine global volume slide */ + if (f1 == FX_GVOL_SLIDE || f2 == FX_GVOL_SLIDE) { + int h, l; + parm = (f1 == FX_GVOL_SLIDE) ? p1 : p2; + + process_gvol: + if (parm) { + gvol_memory = parm; + h = MSN(parm); + l = LSN(parm); + + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (l == 0xf && h != 0) { + gvl += h; + } else if (h == 0xf && l != 0) { + gvl -= l; + } else { + if (m->quirk & QUIRK_VSALL) { + gvl += (h - l) * speed; + } else { + gvl += (h - l) * (speed - 1); + } + } + } else { + if (m->quirk & QUIRK_VSALL) { + gvl += (h - l) * speed; + } else { + gvl += (h - l) * (speed - 1); + } + } + } else { + if ((parm = gvol_memory) != 0) + goto process_gvol; + } + } + + /* Some formats can have two FX_SPEED effects, and both need + * to be checked. Slot 2 is currently handled first. */ + for (i = 0; i < 2; i++) { + parm = i ? p1 : p2; + if ((i ? f1 : f2) != FX_SPEED || parm == 0) + continue; + frame_count += row_count * speed; + row_count = 0; + if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) { + speed = parm; +#ifndef LIBXMP_CORE_PLAYER + st26_speed = 0; +#endif + } else { + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + bpm = parm; + } + } + +#ifndef LIBXMP_CORE_PLAYER + if (f1 == FX_SPEED_CP) { + f1 = FX_S3M_SPEED; + } + if (f2 == FX_SPEED_CP) { + f2 = FX_S3M_SPEED; + } + + /* ST2.6 speed processing */ + + if (f1 == FX_ICE_SPEED && p1) { + if (LSN(p1)) { + st26_speed = (MSN(p1) << 8) | LSN(p1); + } else { + st26_speed = MSN(p1); + } + } + + /* FAR tempo processing */ + + if (f1 == FX_FAR_TEMPO || f1 == FX_FAR_F_TEMPO) { + int far_speed, far_bpm, fine_change = 0; + if (f1 == FX_FAR_TEMPO) { + if (MSN(p1)) { + far_tempo_mode = MSN(p1) - 1; + } else { + far_tempo_coarse = LSN(p1); + } + } + if (f1 == FX_FAR_F_TEMPO) { + if (MSN(p1)) { + far_tempo_fine += MSN(p1); + fine_change = MSN(p1); + } else if (LSN(p1)) { + far_tempo_fine -= LSN(p1); + fine_change = -LSN(p1); + } else { + far_tempo_fine = 0; + } + } + if (libxmp_far_translate_tempo(far_tempo_mode, fine_change, + far_tempo_coarse, &far_tempo_fine, &far_speed, &far_bpm) == 0) { + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + speed = far_speed; + bpm = far_bpm; + } + } +#endif + + if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) { + parm = (f1 == FX_S3M_SPEED) ? p1 : p2; + if (parm > 0) { + frame_count += row_count * speed; + row_count = 0; + speed = parm; +#ifndef LIBXMP_CORE_PLAYER + st26_speed = 0; +#endif + } + } + + if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) { + parm = (f1 == FX_S3M_BPM) ? p1 : p2; + if (parm >= XMP_MIN_BPM) { + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + bpm = parm; + } + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if ((f1 == FX_IT_BPM && p1) || (f2 == FX_IT_BPM && p2)) { + parm = (f1 == FX_IT_BPM) ? p1 : p2; + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + + if (MSN(parm) == 0) { + time += m->time_factor * base_time / bpm; + for (i = 1; i < speed; i++) { + bpm -= LSN(parm); + if (bpm < 0x20) + bpm = 0x20; + time += m->time_factor * base_time / bpm; + } + + /* remove one row at final bpm */ + time -= m->time_factor * speed * base_time / bpm; + + } else if (MSN(parm) == 1) { + time += m->time_factor * base_time / bpm; + for (i = 1; i < speed; i++) { + bpm += LSN(parm); + if (bpm > 0xff) + bpm = 0xff; + time += m->time_factor * base_time / bpm; + } + + /* remove one row at final bpm */ + time -= m->time_factor * speed * base_time / bpm; + + } else { + bpm = parm; + } + } + + if (f1 == FX_IT_ROWDELAY) { + /* Don't allow the scan count for this row to overflow here. */ + int x = m->scan_cnt[ord][row] + (p1 & 0x0f); + m->scan_cnt[ord][row] = MIN(x, 255); + frame_count += (p1 & 0x0f) * speed; + } + + if (f1 == FX_IT_BREAK) { + break_row = p1; + last_row = 0; + } +#endif + + if (f1 == FX_JUMP || f2 == FX_JUMP) { + ord2 = (f1 == FX_JUMP) ? p1 : p2; + break_row = 0; + last_row = 0; + + /* prevent infinite loop, see OpenMPT PatLoop-Various.xm */ + inside_loop = 0; + } + + if (f1 == FX_BREAK || f2 == FX_BREAK) { + parm = (f1 == FX_BREAK) ? p1 : p2; + break_row = 10 * MSN(parm) + LSN(parm); + last_row = 0; + } + +#ifndef LIBXMP_CORE_PLAYER + /* Archimedes line jump */ + if (f1 == FX_LINE_JUMP || f2 == FX_LINE_JUMP) { + /* Don't set order if preceded by jump or break. */ + if (last_row > 0) + ord2 = ord; + parm = (f1 == FX_LINE_JUMP) ? p1 : p2; + break_row = parm; + last_row = 0; + line_jump = 1; + } +#endif + + if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) { + parm = (f1 == FX_EXTENDED) ? p1 : p2; + + if ((parm >> 4) == EX_PATT_DELAY) { + if (m->read_event_type != READ_EVENT_ST3 || !pdelay) { + pdelay = parm & 0x0f; + frame_count += pdelay * speed; + } + } + + if ((parm >> 4) == EX_PATTERN_LOOP) { + if (parm &= 0x0f) { + /* Loop end */ + if (loop_count[chn]) { + if (--loop_count[chn]) { + /* next iteraction */ + loop_chn = chn; + } else { + /* finish looping */ + loop_num--; + inside_loop = 0; + if (m->quirk & QUIRK_S3MLOOP) + loop_row[chn] = row; + } + } else { + loop_count[chn] = parm; + loop_chn = chn; + loop_num++; + } + } else { + /* Loop start */ + loop_row[chn] = row - 1; + inside_loop = 1; + if (HAS_QUIRK(QUIRK_FT2BUGS)) + break_row = row; + } + } + } + } + + if (loop_chn >= 0) { + row = loop_row[loop_chn]; + loop_chn = -1; + } + +#ifndef LIBXMP_CORE_PLAYER + if (st26_speed) { + frame_count += row_count * speed; + row_count = 0; + if (st26_speed & 0x10000) { + speed = (st26_speed & 0xff00) >> 8; + } else { + speed = st26_speed & 0xff; + } + st26_speed ^= 0x10000; + } +#endif + } + + if (break_row && pdelay) { + break_row++; + } + + if (ord2 >= 0) { + ord = ord2 - 1; + ord2 = -1; + } + + frame_count += row_count * speed; + row_count_total = 0; + row_count = 0; + } + row = break_row; + +end_module: + + /* Sanity check */ + { + if (!any_valid) { + return -1; + } + pat = mod->xxo[ord]; + if (pat >= mod->pat || row >= mod->xxp[pat]->rows) { + row = 0; + } + } + + p->scan[chain].num = m->scan_cnt[ord][row]; + p->scan[chain].row = row; + p->scan[chain].ord = ord; + + time -= start_time; + frame_count += row_count * speed; + + return (time + m->time_factor * frame_count * base_time / bpm); +} + +static void reset_scan_data(struct context_data *ctx) +{ + int i; + for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) { + ctx->m.xxo_info[i].time = -1; + } + memset(ctx->p.sequence_control, 0xff, XMP_MAX_MOD_LENGTH); +} + +#ifndef LIBXMP_CORE_PLAYER +static void compare_vblank_scan(struct context_data *ctx) +{ + /* Calculate both CIA and VBlank time for certain long MODs + * and pick the more likely (i.e. shorter) one. The same logic + * works regardless of the initial mode selected--either way, + * the wrong timing mode usually makes modules MUCH longer. */ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct ord_data *info_backup; + struct scan_data scan_backup; + unsigned char ctrl_backup[256]; + + if ((info_backup = (struct ord_data *)malloc(sizeof(m->xxo_info))) != NULL) { + /* Back up the current info to avoid a third scan. */ + scan_backup = p->scan[0]; + memcpy(info_backup, m->xxo_info, sizeof(m->xxo_info)); + memcpy(ctrl_backup, p->sequence_control, + sizeof(p->sequence_control)); + + reset_scan_data(ctx); + + m->quirk ^= QUIRK_NOBPM; + p->scan[0].time = scan_module(ctx, 0, 0); + + D_(D_INFO "%-6s %dms", !HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", scan_backup.time); + D_(D_INFO "%-6s %dms", HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", p->scan[0].time); + + if (p->scan[0].time >= scan_backup.time) { + m->quirk ^= QUIRK_NOBPM; + p->scan[0] = scan_backup; + memcpy(m->xxo_info, info_backup, sizeof(m->xxo_info)); + memcpy(p->sequence_control, ctrl_backup, + sizeof(p->sequence_control)); + } + + free(info_backup); + } +} +#endif + +int libxmp_get_sequence(struct context_data *ctx, int ord) +{ + struct player_data *p = &ctx->p; + return p->sequence_control[ord]; +} + +int libxmp_scan_sequences(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct scan_data *s; + int i, ep; + int seq; + unsigned char temp_ep[XMP_MAX_MOD_LENGTH]; + + s = (struct scan_data *) realloc(p->scan, MAX(1, mod->len) * sizeof(struct scan_data)); + if (!s) { + D_(D_CRIT "failed to allocate scan data"); + return -1; + } + p->scan = s; + + /* Initialize order data to prevent overwrite when a position is used + * multiple times at different starting points (see janosik.xm). + */ + reset_scan_data(ctx); + + ep = 0; + temp_ep[0] = 0; + p->scan[0].time = scan_module(ctx, ep, 0); + seq = 1; + +#ifndef LIBXMP_CORE_PLAYER + if (m->compare_vblank && !(p->flags & XMP_FLAGS_VBLANK) && + p->scan[0].time >= VBLANK_TIME_THRESHOLD) { + compare_vblank_scan(ctx); + } +#endif + + if (p->scan[0].time < 0) { + D_(D_CRIT "scan was not able to find any valid orders"); + return -1; + } + + while (1) { + /* Scan song starting at given entry point */ + /* Check if any patterns left */ + for (i = 0; i < mod->len; i++) { + if (p->sequence_control[i] == 0xff) { + break; + } + } + if (i != mod->len && seq < MAX_SEQUENCES) { + /* New entry point */ + ep = i; + temp_ep[seq] = ep; + p->scan[seq].time = scan_module(ctx, ep, seq); + if (p->scan[seq].time > 0) + seq++; + } else { + break; + } + } + + if (seq < mod->len) { + s = (struct scan_data *) realloc(p->scan, seq * sizeof(struct scan_data)); + if (s != NULL) { + p->scan = s; + } + } + m->num_sequences = seq; + + /* Now place entry points in the public accessible array */ + for (i = 0; i < m->num_sequences; i++) { + m->seq_data[i].entry_point = temp_ep[i]; + m->seq_data[i].duration = p->scan[i].time; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/smix.c b/thirdparty/libxmp/src/smix.c new file mode 100644 index 0000000..0077345 --- /dev/null +++ b/thirdparty/libxmp/src/smix.c @@ -0,0 +1,335 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "period.h" +#include "player.h" +#include "hio.h" +#include "loaders/loader.h" + + +struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins) +{ + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + + if (ins < 0) { + xxi = NULL; + } else if (ins < mod->ins) { + xxi = &mod->xxi[ins]; + } else if (ins < mod->ins + smix->ins) { + xxi = &smix->xxi[ins - mod->ins]; + } else { + xxi = NULL; + } + + return xxi; +} + +struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp) +{ + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_sample *xxs; + + if (smp < 0) { + xxs = NULL; + } else if (smp < mod->smp) { + xxs = &mod->xxs[smp]; + } else if (smp < mod->smp + smix->smp) { + xxs = &smix->xxs[smp - mod->smp]; + } else { + xxs = NULL; + } + + return xxs; +} + +int xmp_start_smix(xmp_context opaque, int chn, int smp) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + + if (ctx->state > XMP_STATE_LOADED) { + return -XMP_ERROR_STATE; + } + + smix->xxi = (struct xmp_instrument *) calloc(smp, sizeof(struct xmp_instrument)); + if (smix->xxi == NULL) { + goto err; + } + smix->xxs = (struct xmp_sample *) calloc(smp, sizeof(struct xmp_sample)); + if (smix->xxs == NULL) { + goto err1; + } + + smix->chn = chn; + smix->ins = smix->smp = smp; + + return 0; + + err1: + free(smix->xxi); + smix->xxi = NULL; + err: + return -XMP_ERROR_INTERNAL; +} + +int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int chn) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + + if (ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + if (chn >= smix->chn || chn < 0 || ins >= mod->ins || ins < 0) { + return -XMP_ERROR_INVALID; + } + + if (note == 0) { + note = 60; /* middle C note number */ + } + + event = &p->inject_event[mod->chn + chn]; + memset(event, 0, sizeof (struct xmp_event)); + event->note = (note < XMP_MAX_KEYS) ? note + 1 : note; + event->ins = ins + 1; + event->vol = vol + 1; + event->_flag = 1; + + return 0; +} + +int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + + if (ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + if (chn >= smix->chn || chn < 0 || ins >= smix->ins || ins < 0) { + return -XMP_ERROR_INVALID; + } + + if (note == 0) { + note = 60; /* middle C note number */ + } + + event = &p->inject_event[mod->chn + chn]; + memset(event, 0, sizeof (struct xmp_event)); + event->note = (note < XMP_MAX_KEYS) ? note + 1 : note; + event->ins = mod->ins + ins + 1; + event->vol = vol + 1; + event->_flag = 1; + + return 0; +} + +int xmp_smix_channel_pan(xmp_context opaque, int chn, int pan) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct channel_data *xc; + + if (chn >= smix->chn || pan < 0 || pan > 255) { + return -XMP_ERROR_INVALID; + } + + xc = &p->xc_data[m->mod.chn + chn]; + xc->pan.val = pan; + + return 0; +} + +int xmp_smix_load_sample(xmp_context opaque, int num, const char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_instrument *xxi; + struct xmp_sample *xxs; + HIO_HANDLE *h; + uint32 magic; + int chn, rate, bits, size; + int retval = -XMP_ERROR_INTERNAL; + + if (num >= smix->ins) { + retval = -XMP_ERROR_INVALID; + goto err; + } + + xxi = &smix->xxi[num]; + xxs = &smix->xxs[num]; + + h = hio_open(path, "rb"); + if (h == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err; + } + + /* Init instrument */ + + xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err1; + } + + xxi->vol = m->volbase; + xxi->nsm = 1; + xxi->sub[0].sid = num; + xxi->sub[0].vol = xxi->vol; + xxi->sub[0].pan = 0x80; + + /* Load sample */ + + magic = hio_read32b(h); + if (magic != 0x52494646) { /* RIFF */ + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + if (hio_seek(h, 22, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + chn = hio_read16l(h); + if (chn != 1) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + rate = hio_read32l(h); + if (rate == 0) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + if (hio_seek(h, 34, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + bits = hio_read16l(h); + if (bits == 0) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + if (hio_seek(h, 40, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + size = hio_read32l(h); + if (size == 0) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + libxmp_c2spd_to_note(rate, &xxi->sub[0].xpo, &xxi->sub[0].fin); + + xxs->len = 8 * size / bits; + xxs->lps = 0; + xxs->lpe = 0; + xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0; + + xxs->data = (unsigned char *) malloc(size + 8); + if (xxs->data == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + + /* ugly hack to make the interpolator happy */ + memset(xxs->data, 0, 4); + memset(xxs->data + 4 + size, 0, 4); + xxs->data += 4; + + if (hio_seek(h, 44, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + if (hio_read(xxs->data, 1, size, h) != size) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + hio_close(h); + + return 0; + + err2: + free(xxi->sub); + xxi->sub = NULL; + err1: + hio_close(h); + err: + return retval; +} + +int xmp_smix_release_sample(xmp_context opaque, int num) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + + if (num >= smix->ins) { + return -XMP_ERROR_INVALID; + } + + libxmp_free_sample(&smix->xxs[num]); + free(smix->xxi[num].sub); + + smix->xxs[num].data = NULL; + smix->xxi[num].sub = NULL; + + return 0; +} + +void xmp_end_smix(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + int i; + + for (i = 0; i < smix->smp; i++) { + xmp_smix_release_sample(opaque, i); + } + + free(smix->xxs); + free(smix->xxi); + smix->xxs = NULL; + smix->xxi = NULL; +} diff --git a/thirdparty/libxmp/src/tempfile.c b/thirdparty/libxmp/src/tempfile.c new file mode 100644 index 0000000..6b05820 --- /dev/null +++ b/thirdparty/libxmp/src/tempfile.c @@ -0,0 +1,165 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef __SUNPRO_C +#pragma error_messages (off,E_EMPTY_TRANSLATION_UNIT) +#endif + +#include "common.h" + +#if !(defined(LIBXMP_NO_PROWIZARD) && defined(LIBXMP_NO_DEPACKERS)) + +#if defined(_MSC_VER) || defined(__WATCOMC__) +#include +#else +#include +#endif +#ifdef HAVE_UMASK +#include +#include +#endif + +#include "tempfile.h" + +#ifdef _WIN32 + +int mkstemp(char *); + +static int get_temp_dir(char *buf, size_t size) +{ + static const char def[] = "C:\\WINDOWS\\TEMP"; + const char *tmp = getenv("TEMP"); + snprintf(buf, size, "%s\\", (tmp != NULL)? tmp : def); + return 0; +} + +#elif defined(__OS2__) || defined(__EMX__) + +static int get_temp_dir(char *buf, size_t size) +{ + static const char def[] = "C:"; + const char *tmp = getenv("TMP"); + snprintf(buf, size, "%s\\", (tmp != NULL)? tmp : def); + return 0; +} + +#elif defined(__MSDOS__) || defined(_DOS) + +static int get_temp_dir(char *buf, size_t size) +{ + strcpy(buf, "C:\\"); /* size-safe against XMP_MAXPATH */ + return 0; +} + +#elif defined LIBXMP_AMIGA + +static int get_temp_dir(char *buf, size_t size) +{ + strcpy(buf, "T:"); /* size-safe against XMP_MAXPATH */ + return 0; +} + +#elif defined __ANDROID__ + +#include +#include + +static int get_temp_dir(char *buf, size_t size) +{ +#define APPDIR "/sdcard/Xmp for Android" + struct stat st; + if (stat(APPDIR, &st) < 0) { + if (mkdir(APPDIR, 0777) < 0) + return -1; + } + if (stat(APPDIR "/tmp", &st) < 0) { + if (mkdir(APPDIR "/tmp", 0777) < 0) + return -1; + } + strncpy(buf, APPDIR "/tmp/", size); + + return 0; +} + +#else /* unix */ + +static int get_temp_dir(char *buf, size_t size) +{ + const char *tmp = getenv("TMPDIR"); + + if (tmp) { + snprintf(buf, size, "%s/", tmp); + } else { + strncpy(buf, "/tmp/", size); + } + + return 0; +} + +#endif + + +FILE *make_temp_file(char **filename) { + char tmp[XMP_MAXPATH]; + FILE *temp; + int fd; + + if (get_temp_dir(tmp, XMP_MAXPATH) < 0) + return NULL; + + strncat(tmp, "xmp_XXXXXX", XMP_MAXPATH - 10); + + if ((*filename = libxmp_strdup(tmp)) == NULL) + goto err; + +#ifdef HAVE_UMASK + umask(0177); +#endif + if ((fd = mkstemp(*filename)) < 0) + goto err2; + + if ((temp = fdopen(fd, "w+b")) == NULL) + goto err3; + + return temp; + + err3: + close(fd); + err2: + free(*filename); + err: + return NULL; +} + +/* + * Windows doesn't allow you to unlink an open file, so we changed the + * temp file cleanup system to remove temporary files after we close it + */ +void unlink_temp_file(char *temp) +{ + if (temp) { + unlink(temp); + free(temp); + } +} + +#endif diff --git a/thirdparty/libxmp/src/tempfile.h b/thirdparty/libxmp/src/tempfile.h new file mode 100644 index 0000000..f3c8f77 --- /dev/null +++ b/thirdparty/libxmp/src/tempfile.h @@ -0,0 +1,7 @@ +#ifndef XMP_PLATFORM_H +#define XMP_PLATFORM_H + +FILE *make_temp_file(char **); +void unlink_temp_file(char *); + +#endif diff --git a/thirdparty/libxmp/src/virtual.c b/thirdparty/libxmp/src/virtual.c new file mode 100644 index 0000000..ed9a69f --- /dev/null +++ b/thirdparty/libxmp/src/virtual.c @@ -0,0 +1,626 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "virtual.h" +#include "mixer.h" + +#ifdef LIBXMP_PAULA_SIMULATOR +#include "paula.h" +#endif + +#define FREE -1 + +/* For virt_pastnote() */ +void libxmp_player_set_release(struct context_data *, int); +void libxmp_player_set_fadeout(struct context_data *, int); + + +/* Get parent channel */ +int libxmp_virt_getroot(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi; + int voc; + + voc = p->virt.virt_channel[chn].map; + if (voc < 0) { + return -1; + } + + vi = &p->virt.voice_array[voc]; + + return vi->root; +} + +void libxmp_virt_resetvoice(struct context_data *ctx, int voc, int mute) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula; +#endif + + if ((uint32)voc >= p->virt.maxvoc) { + return; + } + + if (mute) { + libxmp_mixer_setvol(ctx, voc, 0); + } + + p->virt.virt_used--; + p->virt.virt_channel[vi->root].count--; + p->virt.virt_channel[vi->chn].map = FREE; +#ifdef LIBXMP_PAULA_SIMULATOR + paula = vi->paula; +#endif + memset(vi, 0, sizeof(struct mixer_voice)); +#ifdef LIBXMP_PAULA_SIMULATOR + vi->paula = paula; +#endif + vi->chn = vi->root = FREE; +} + +/* virt_on (number of tracks) */ +int libxmp_virt_on(struct context_data *ctx, int num) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int i; + + p->virt.num_tracks = num; + num = libxmp_mixer_numvoices(ctx, -1); + + p->virt.virt_channels = p->virt.num_tracks; + + if (HAS_QUIRK(QUIRK_VIRTUAL)) { + p->virt.virt_channels += num; + } else if (num > p->virt.virt_channels) { + num = p->virt.virt_channels; + } + + p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num); + + p->virt.voice_array = (struct mixer_voice *) calloc(p->virt.maxvoc, + sizeof(struct mixer_voice)); + if (p->virt.voice_array == NULL) + goto err; + + for (i = 0; i < p->virt.maxvoc; i++) { + p->virt.voice_array[i].chn = FREE; + p->virt.voice_array[i].root = FREE; + } + +#ifdef LIBXMP_PAULA_SIMULATOR + /* Initialize Paula simulator */ + if (IS_AMIGA_MOD()) { + for (i = 0; i < p->virt.maxvoc; i++) { + p->virt.voice_array[i].paula = (struct paula_state *) calloc(1, sizeof(struct paula_state)); + if (p->virt.voice_array[i].paula == NULL) { + goto err2; + } + libxmp_paula_init(ctx, p->virt.voice_array[i].paula); + } + } +#endif + + p->virt.virt_channel = (struct virt_channel *) malloc(p->virt.virt_channels * + sizeof(struct virt_channel)); + if (p->virt.virt_channel == NULL) + goto err2; + + for (i = 0; i < p->virt.virt_channels; i++) { + p->virt.virt_channel[i].map = FREE; + p->virt.virt_channel[i].count = 0; + } + + p->virt.virt_used = 0; + + return 0; + + err2: +#ifdef LIBXMP_PAULA_SIMULATOR + if (IS_AMIGA_MOD()) { + for (i = 0; i < p->virt.maxvoc; i++) { + free(p->virt.voice_array[i].paula); + } + } +#endif + free(p->virt.voice_array); + p->virt.voice_array = NULL; + err: + return -1; +} + +void libxmp_virt_off(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; +#ifdef LIBXMP_PAULA_SIMULATOR + struct module_data *m = &ctx->m; + int i; +#endif + +#ifdef LIBXMP_PAULA_SIMULATOR + /* Free Paula simulator state */ + if (IS_AMIGA_MOD()) { + for (i = 0; i < p->virt.maxvoc; i++) { + free(p->virt.voice_array[i].paula); + } + } +#endif + + p->virt.virt_used = p->virt.maxvoc = 0; + p->virt.virt_channels = 0; + p->virt.num_tracks = 0; + + free(p->virt.voice_array); + free(p->virt.virt_channel); + p->virt.voice_array = NULL; + p->virt.virt_channel = NULL; +} + +void libxmp_virt_reset(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + int i; + + if (p->virt.virt_channels < 1) { + return; + } + + /* CID 129203 (#1 of 1): Useless call (USELESS_CALL) + * Call is only useful for its return value, which is ignored. + * + * libxmp_mixer_numvoices(ctx, p->virt.maxvoc); + */ + + for (i = 0; i < p->virt.maxvoc; i++) { + struct mixer_voice *vi = &p->virt.voice_array[i]; +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula = vi->paula; +#endif + memset(vi, 0, sizeof(struct mixer_voice)); +#ifdef LIBXMP_PAULA_SIMULATOR + vi->paula = paula; +#endif + vi->chn = FREE; + vi->root = FREE; + } + + for (i = 0; i < p->virt.virt_channels; i++) { + p->virt.virt_channel[i].map = FREE; + p->virt.virt_channel[i].count = 0; + } + + p->virt.virt_used = 0; +} + +static int free_voice(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + int i, num, vol; + + /* Find background voice with lowest volume*/ + num = FREE; + vol = INT_MAX; + for (i = 0; i < p->virt.maxvoc; i++) { + struct mixer_voice *vi = &p->virt.voice_array[i]; + + if (vi->chn >= p->virt.num_tracks && vi->vol < vol) { + num = i; + vol = vi->vol; + } + } + + /* Free voice */ + if (num >= 0) { + p->virt.virt_channel[p->virt.voice_array[num].chn].map = FREE; + p->virt.virt_channel[p->virt.voice_array[num].root].count--; + p->virt.virt_used--; + } + + return num; +} + +static int alloc_voice(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int i; + + /* Find free voice */ + for (i = 0; i < p->virt.maxvoc; i++) { + if (p->virt.voice_array[i].chn == FREE) + break; + } + + /* not found */ + if (i == p->virt.maxvoc) { + i = free_voice(ctx); + } + + if (i >= 0) { + p->virt.virt_channel[chn].count++; + p->virt.virt_used++; + + p->virt.voice_array[i].chn = chn; + p->virt.voice_array[i].root = chn; + p->virt.virt_channel[chn].map = i; + } + + return i; +} + +static int map_virt_channel(struct player_data *p, int chn) +{ + int voc; + + if ((uint32)chn >= p->virt.virt_channels) + return -1; + + voc = p->virt.virt_channel[chn].map; + + if ((uint32)voc >= p->virt.maxvoc) + return -1; + + return voc; +} + +int libxmp_virt_mapchannel(struct context_data *ctx, int chn) +{ + return map_virt_channel(&ctx->p, chn); +} + +void libxmp_virt_resetchannel(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi; +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula; +#endif + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) + return; + + libxmp_mixer_setvol(ctx, voc, 0); + + p->virt.virt_used--; + p->virt.virt_channel[p->virt.voice_array[voc].root].count--; + p->virt.virt_channel[chn].map = FREE; + + vi = &p->virt.voice_array[voc]; +#ifdef LIBXMP_PAULA_SIMULATOR + paula = vi->paula; +#endif + memset(vi, 0, sizeof(struct mixer_voice)); +#ifdef LIBXMP_PAULA_SIMULATOR + vi->paula = paula; +#endif + vi->chn = vi->root = FREE; +} + +void libxmp_virt_setvol(struct context_data *ctx, int chn, int vol) +{ + struct player_data *p = &ctx->p; + int voc, root; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + root = p->virt.voice_array[voc].root; + if (root < XMP_MAX_CHANNELS && p->channel_mute[root]) { + vol = 0; + } + + libxmp_mixer_setvol(ctx, voc, vol); + + if (vol == 0 && chn >= p->virt.num_tracks) { + libxmp_virt_resetvoice(ctx, voc, 1); + } +} + +void libxmp_virt_release(struct context_data *ctx, int chn, int rel) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_release(ctx, voc, rel); +} + +void libxmp_virt_reverse(struct context_data *ctx, int chn, int rev) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_reverse(ctx, voc, rev); +} + +void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_setpan(ctx, voc, pan); +} + +void libxmp_virt_seteffect(struct context_data *ctx, int chn, int type, int val) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_seteffect(ctx, voc, type, val); +} + +double libxmp_virt_getvoicepos(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return -1; + } + + return libxmp_mixer_getvoicepos(ctx, voc); +} + +#ifndef LIBXMP_CORE_PLAYER + +void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi; + double pos; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + vi = &p->virt.voice_array[voc]; + if (vi->smp == smp) { + return; + } + + pos = libxmp_mixer_getvoicepos(ctx, voc); + libxmp_mixer_setpatch(ctx, voc, smp, 0); + libxmp_mixer_voicepos(ctx, voc, pos, 0); /* Restore old position */ +} + +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT + +void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int voc; + + if (!HAS_QUIRK(QUIRK_VIRTUAL)) { + return; + } + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + p->virt.voice_array[voc].act = nna; +} + +static void check_dct(struct context_data *ctx, int i, int chn, int ins, + int smp, int key, int nna, int dct, int dca) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[i]; + int voc; + + voc = p->virt.virt_channel[chn].map; + + if (vi->root == chn && vi->ins == ins) { + + if (nna == XMP_INST_NNA_CUT) { + libxmp_virt_resetvoice(ctx, i, 1); + return; + } + + vi->act = nna; + + if ((dct == XMP_INST_DCT_INST) || + (dct == XMP_INST_DCT_SMP && vi->smp == smp) || + (dct == XMP_INST_DCT_NOTE && vi->key == key)) { + + if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) { + vi->act = VIRT_ACTION_OFF; + } else if (dca) { + if (i != voc || vi->act) { + vi->act = dca; + } + } else { + libxmp_virt_resetvoice(ctx, i, 1); + } + } + } +} + +#endif + +/* For note slides */ +void libxmp_virt_setnote(struct context_data *ctx, int chn, int note) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_setnote(ctx, voc, note); +} + +int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp, + int note, int key, int nna, int dct, int dca) +{ + struct player_data *p = &ctx->p; + int voc, vfree; + + if ((uint32)chn >= p->virt.virt_channels) { + return -1; + } + + if (ins < 0) { + smp = -1; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if (dct) { + int i; + + for (i = 0; i < p->virt.maxvoc; i++) { + check_dct(ctx, i, chn, ins, smp, key, nna, dct, dca); + } + } +#endif + + voc = p->virt.virt_channel[chn].map; + + if (voc > FREE) { + if (p->virt.voice_array[voc].act) { + vfree = alloc_voice(ctx, chn); + + if (vfree < 0) { + return -1; + } + + for (chn = p->virt.num_tracks; chn < p->virt.virt_channels && + p->virt.virt_channel[chn++].map > FREE;) ; + + p->virt.voice_array[voc].chn = --chn; + p->virt.virt_channel[chn].map = voc; + voc = vfree; + } + } else { + voc = alloc_voice(ctx, chn); + if (voc < 0) { + return -1; + } + } + + if (smp < 0) { + libxmp_virt_resetvoice(ctx, voc, 1); + return chn; /* was -1 */ + } + + libxmp_mixer_setpatch(ctx, voc, smp, 1); + libxmp_mixer_setnote(ctx, voc, note); + p->virt.voice_array[voc].ins = ins; + p->virt.voice_array[voc].act = nna; + p->virt.voice_array[voc].key = key; + + return chn; +} + +void libxmp_virt_setperiod(struct context_data *ctx, int chn, double period) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_setperiod(ctx, voc, period); +} + +void libxmp_virt_voicepos(struct context_data *ctx, int chn, double pos) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_voicepos(ctx, voc, pos, 1); +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +void libxmp_virt_pastnote(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + int c, voc; + + for (c = p->virt.num_tracks; c < p->virt.virt_channels; c++) { + if ((voc = map_virt_channel(p, c)) < 0) + continue; + + if (p->virt.voice_array[voc].root == chn) { + switch (act) { + case VIRT_ACTION_CUT: + libxmp_virt_resetvoice(ctx, voc, 1); + break; + case VIRT_ACTION_OFF: + libxmp_player_set_release(ctx, c); + break; + case VIRT_ACTION_FADE: + libxmp_player_set_fadeout(ctx, c); + break; + } + } + } +} + +#endif + +int libxmp_virt_cstat(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return VIRT_INVALID; + } + + if (chn < p->virt.num_tracks) { + return VIRT_ACTIVE; + } + + return p->virt.voice_array[voc].act; +} diff --git a/thirdparty/libxmp/src/virtual.h b/thirdparty/libxmp/src/virtual.h new file mode 100644 index 0000000..88a8cdc --- /dev/null +++ b/thirdparty/libxmp/src/virtual.h @@ -0,0 +1,39 @@ +#ifndef LIBXMP_VIRTUAL_H +#define LIBXMP_VIRTUAL_H + +#include "common.h" + +#define VIRT_ACTION_CUT XMP_INST_NNA_CUT +#define VIRT_ACTION_CONT XMP_INST_NNA_CONT +#define VIRT_ACTION_OFF XMP_INST_NNA_OFF +#define VIRT_ACTION_FADE XMP_INST_NNA_FADE + +#define VIRT_ACTIVE 0x100 +#define VIRT_INVALID -1 + +int libxmp_virt_on (struct context_data *, int); +void libxmp_virt_off (struct context_data *); +int libxmp_virt_mute (struct context_data *, int, int); +int libxmp_virt_setpatch (struct context_data *, int, int, int, int, + int, int, int, int); +int libxmp_virt_cvt8bit (void); +void libxmp_virt_setnote (struct context_data *, int, int); +void libxmp_virt_setsmp (struct context_data *, int, int); +void libxmp_virt_setnna (struct context_data *, int, int); +void libxmp_virt_pastnote (struct context_data *, int, int); +void libxmp_virt_setvol (struct context_data *, int, int); +void libxmp_virt_voicepos (struct context_data *, int, double); +double libxmp_virt_getvoicepos (struct context_data *, int); +void libxmp_virt_setperiod (struct context_data *, int, double); +void libxmp_virt_setpan (struct context_data *, int, int); +void libxmp_virt_seteffect (struct context_data *, int, int, int); +int libxmp_virt_cstat (struct context_data *, int); +int libxmp_virt_mapchannel (struct context_data *, int); +void libxmp_virt_resetchannel(struct context_data *, int); +void libxmp_virt_resetvoice (struct context_data *, int, int); +void libxmp_virt_reset (struct context_data *); +void libxmp_virt_release (struct context_data *, int, int); +void libxmp_virt_reverse (struct context_data *, int, int); +int libxmp_virt_getroot (struct context_data *, int); + +#endif /* LIBXMP_VIRTUAL_H */ diff --git a/thirdparty/libxmp/src/win32.c b/thirdparty/libxmp/src/win32.c new file mode 100644 index 0000000..88ac97d --- /dev/null +++ b/thirdparty/libxmp/src/win32.c @@ -0,0 +1,47 @@ +/* _[v]snprintf() from msvcrt.dll might not nul terminate */ +/* OpenWatcom-provided versions seem to behave the same... */ + +#include "common.h" + +#if defined(USE_LIBXMP_SNPRINTF) + +#undef snprintf +#undef vsnprintf + +int libxmp_vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) +{ + int rc = _vsnprintf(str, sz, fmt, ap); + if (sz != 0) { + if (rc < 0) rc = (int)sz; + if ((size_t)rc >= sz) str[sz - 1] = '\0'; + } + return rc; +} + +int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...) +{ + va_list ap; + int rc; + + va_start (ap, fmt); + rc = _vsnprintf(str, sz, fmt, ap); + va_end (ap); + + return rc; +} + +#endif + +/* Win32 debug message helper by Mirko Buffoni */ +#if defined(_MSC_VER) && defined(DEBUG) +void libxmp_msvc_dbgprint(const char *format, ...) +{ + va_list argptr; + + /* do the output */ + va_start(argptr, format); + vprintf(format, argptr); + printf("\n"); + va_end(argptr); +} +#endif