code:
/*
 * libsmackerdec - Smacker video decoder
 * Copyright (C) 2011 Barry Duncan
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/* This code is heavily based on smacker.c from the FFmpeg project which can be obtained from http://www.ffmpeg.org/
 * below is the license from smacker.c
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Smacker decoder
 * Copyright (c) 2006 Konstantin Shishkov
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "SmackerDecoder.h"
#include "HuffmanVLC.h"
#include "LogError.h"
#include <assert.h>
#include <algorithm>
#include "compat.h"
#include "baselayer.h"

std::vector<class SmackerDecoder*> classInstances;

SmackerHandle Smacker_Open(const char* fileName)
{
	SmackerHandle newHandle;
	newHandle.isValid = false;
	newHandle.instanceIndex = -1;

	SmackerDecoder *newDecoder = new SmackerDecoder();
	if (!newDecoder->Open(fileName))
	{
		delete newDecoder;
		return newHandle;
	}

	// add instance to global instance vector
	classInstances.push_back(newDecoder);

	// get a handle ID
	newHandle.instanceIndex = classInstances.size() - 1;

	// loaded ok, make handle valid
	newHandle.isValid = true;

	return newHandle;
}

void Smacker_Close(SmackerHandle &handle)
{
	if (!classInstances.at(handle.instanceIndex))
	{
		// invalid handle
		return;
	}

	// close bink decoder
	delete classInstances[handle.instanceIndex];
	classInstances[handle.instanceIndex] = 0;

	handle.instanceIndex = -1;
	handle.isValid = false;
}

uint32_t Smacker_GetNumAudioTracks(SmackerHandle &handle)
{
	UNREFERENCED_PARAMETER(handle);
	// TODO: fixme
	return 1;
}

SmackerAudioInfo Smacker_GetAudioTrackDetails(SmackerHandle &handle, uint32_t trackIndex)
{
	return classInstances[handle.instanceIndex]->GetAudioTrackDetails(trackIndex);
}

/* Get a frame's worth of audio data.
 *
 * 'data' needs to be a pointer to allocated memory that this function will fill.
 * You can find the size (in bytes) to make this buffer by calling Bink_GetAudioTrackDetails()
 * and checking the 'idealBufferSize' member in the returned AudioInfo struct
 */
uint32_t Smacker_GetAudioData(SmackerHandle &handle, uint32_t trackIndex, int16_t *data)
{
	return classInstances[handle.instanceIndex]->GetAudioData(trackIndex, data);
}

uint32_t Smacker_GetNumFrames(SmackerHandle &handle)
{
	return classInstances[handle.instanceIndex]->GetNumFrames();
}

void Smacker_GetFrameSize(SmackerHandle &handle, uint32_t &width, uint32_t &height)
{
	width  = classInstances[handle.instanceIndex]->frameWidth;
	height = classInstances[handle.instanceIndex]->frameHeight;
}

uint32_t Smacker_GetCurrentFrameNum(SmackerHandle &handle)
{
	return classInstances[handle.instanceIndex]->GetCurrentFrameNum();
}

uint32_t Smacker_GetNextFrame(SmackerHandle &handle)
{
	SmackerDecoder *decoder = classInstances[handle.instanceIndex];

	uint32_t frameIndex = decoder->GetCurrentFrameNum();

	decoder->GetNextFrame();

	return frameIndex;
}

float Smacker_GetFrameRate(SmackerHandle &handle)
{
	return classInstances[handle.instanceIndex]->GetFrameRate();
}

void Smacker_GetPalette(SmackerHandle &handle, uint8_t *palette)
{
	classInstances[handle.instanceIndex]->GetPalette(palette);
}

void Smacker_GetFrame(SmackerHandle &handle, uint8_t *frame)
{
	classInstances[handle.instanceIndex]->GetFrame(frame);
}

void Smacker_GotoFrame(SmackerHandle &handle, uint32_t frameNum)
{
	classInstances[handle.instanceIndex]->GotoFrame(frameNum);
}

SmackerDecoder::SmackerDecoder()
{
	isVer4 = false;
	currentFrame = 0;
	picture = 0;
	nextPos = 0;

	for (int i = 0; i < kMaxAudioTracks; i++)
	{
		audioTracks[i].buffer = 0;
	}
}

SmackerDecoder::~SmackerDecoder()
{
	for (int i = 0; i < kMaxAudioTracks; i++)
	{
		delete[] audioTracks[i].buffer;
	}

	delete[] picture;
}

// from bswap.h
static /*av_always_inline av_const*/ uint16_t av_bswap16(uint16_t x)
{
    x= (x>>8) | (x<<8);
    return x;
}

/* possible runs of blocks */
static const int block_runs[64] = {
      1,    2,    3,    4,    5,    6,    7,    8,
      9,   10,   11,   12,   13,   14,   15,   16,
     17,   18,   19,   20,   21,   22,   23,   24,
     25,   26,   27,   28,   29,   30,   31,   32,
     33,   34,   35,   36,   37,   38,   39,   40,
     41,   42,   43,   44,   45,   46,   47,   48,
     49,   50,   51,   52,   53,   54,   55,   56,
     57,   58,   59,  128,  256,  512, 1024, 2048 };

enum SmkBlockTypes {
    SMK_BLK_MONO = 0,
    SMK_BLK_FULL = 1,
    SMK_BLK_SKIP = 2,
    SMK_BLK_FILL = 3 };

/* palette used in Smacker */
static const uint8_t smk_pal[64] = {
    0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C,
    0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
    0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D,
    0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D,
    0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E,
    0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE,
    0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF,
    0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF
};

enum SAudFlags {
    SMK_AUD_PACKED  = 0x80000000,
    SMK_AUD_16BITS  = 0x20000000,
    SMK_AUD_STEREO  = 0x10000000,
    SMK_AUD_BINKAUD = 0x08000000,
    SMK_AUD_USEDCT  = 0x04000000
};

const int kSMKpal = 0x01;
const int kFlagRingFrame = 0x01;

const int kTreeBits = 9;
const int kSMKnode = 0x80000000;

const char *kSMK2iD = "SMK2";
const char *kSMK4iD = "SMK4";

/**
 * Context used for code reconstructing
 */
typedef struct HuffContext {
    int length;
    int maxlength;
    int current;

	std::vector<uint32_t> bits;
	std::vector<int> lengths;
	std::vector<int> values;

} HuffContext;

/* common parameters used for decode_bigtree */
typedef struct DBCtx {
	SmackerCommon::VLCtable v1;
	SmackerCommon::VLCtable v2;
	std::vector<int> recode1, recode2;
    int escapes[3];
    int *last;
    int lcur;
} DBCtx;

static void last_reset(std::vector<int> &recode, int *last) {
    recode[last[0]] = recode[last[1]] = recode[last[2]] = 0;
}

/* get code and update history */
int SmackerDecoder::GetCode(SmackerCommon::BitReader &bits, std::vector<int> &recode, int *last)
{
	int *table = &recode[0];

    int v, b;

    b = bits.GetPosition();

    while (*table & kSMKnode)
	{
        if (bits.GetBit())
            table += (*table) & (~kSMKnode);
        table++;
    }
    v = *table;
    b = bits.GetPosition() - b;

    if (v != recode[last[0]]) {
        recode[last[2]] = recode[last[1]];
        recode[last[1]] = recode[last[0]];
        recode[last[0]] = v;
    }
    return v;
}

bool SmackerDecoder::Open(const char *fileName)
{
	// open the file (read only)
	file.Open(fileName);
	if (!file.Is_Open())
	{
        buildprintf("SmackerDecoder::Open() - Can't open file %s\n", fileName);
		return false;
	}

	// check the file signature
	file.ReadBytes((uint8_t*)signature, 4);
	if (memcmp(signature, kSMK2iD, 4) != 0
		&& memcmp(signature, kSMK4iD, 4) != 0)
	{
        buildprintf("SmackerDecoder::Open() - Unknown Smacker signature\n");
		return false;
	}

	if (!memcmp(signature, kSMK4iD, 4)) {
		isVer4 = true;
	}

	frameWidth  = file.ReadUint32LE();
	frameHeight = file.ReadUint32LE();
	nFrames = file.ReadUint32LE();

	picture = new uint8_t[frameWidth * frameHeight];

	int32_t frameRate = file.ReadUint32LE();

	if (frameRate > 0)
		fps = 1000 / frameRate;
	else if (frameRate < 0)
		fps = 100000 / (-frameRate);
	else
		fps = 10;

	uint32_t flags = file.ReadUint32LE();

	if (flags & kFlagRingFrame) {
		nFrames++;
	}

	for (int i = 0; i < kMaxAudioTracks; i++) {
		audioTracks[i].sizeInBytes = file.ReadUint32LE();
	}

	treeSize = file.ReadUint32LE();
	mMapSize = file.ReadUint32LE();
	MClrSize = file.ReadUint32LE();
	fullSize = file.ReadUint32LE();
	typeSize = file.ReadUint32LE();

	for (int i = 0; i < kMaxAudioTracks; i++) {
		audioTracks[i].flags = file.ReadUint32LE();
	}

	// skip pad
	file.Skip(4);

	if (nFrames > 0xFFFFFF)
	{
        buildprintf("SmackerDecoder::Open() - Too many frames\n");
		return false;
	}

	frameSizes.resize(nFrames);
	frameFlags.resize(nFrames);

	// read frame info
	for (uint32_t i = 0; i < nFrames; i++) {
		frameSizes[i] = file.ReadUint32LE();
	}
	for (uint32_t i = 0; i < nFrames; i++) {
		frameFlags[i] = file.ReadByte();
	}

	// handle possible audio streams
	for (int i = 0; i < kMaxAudioTracks; i++)
	{
		audioTracks[i].buffer = 0;
		audioTracks[i].bufferSize = 0;
		audioTracks[i].bytesReadThisFrame = 0;

		if (audioTracks[i].flags & 0xFFFFFF)
		{
/*
            if (audioTracks[i].flags & SMK_AUD_BINKAUD) {
                audioTracks[i].compressionType = SMK_AUD_BINKAUD;
            } else if (audioTracks[i].flags & SMK_AUD_USEDCT) {
                audioTracks[i].compressionType = SMK_AUD_USEDCT;
            } else if (audioTracks[i].flags & SMK_AUD_PACKED){
                ast[i]->codec->codec_id = CODEC_ID_SMACKAUDIO;
                ast[i]->codec->codec_tag = MKTAG('S', 'M', 'K', 'A');
            } else {
                ast[i]->codec->codec_id = CODEC_ID_PCM_U8;
            }
*/
			audioTracks[i].nChannels = (audioTracks[i].flags & SMK_AUD_STEREO) ? 2 : 1;
			audioTracks[i].sampleRate = audioTracks[i].flags & 0xFFFFFF;
			audioTracks[i].bitsPerSample = (audioTracks[i].flags & SMK_AUD_16BITS) ? 16 : 8;
		}
	}

	memset(palette, 0, 768);

	DecodeHeaderTrees();

	// set nextPos to where we are now, as next data is frame 1
	nextPos = file.GetPosition();
    firstFrameFilePos = nextPos;

	// determine max buffer sizes for audio tracks
//	file.Seek(nextPos, SmackerCommon::FileStream::kSeekStart);

	uint32_t UNUSED(frameSize) = frameSizes[0] & (~3);
	uint8_t frameFlag  = frameFlags[0];

	// skip over palette
	if (frameFlag & kSMKpal)
	{
		uint32_t size = file.ReadByte();
		size = size * 4 - 1;
		file.Skip(size);
	}

	frameFlag >>= 1;

	for (int i = 0; i < kMaxAudioTracks; i++)
	{
		if (frameFlag & 1)
		{
			// skip size
			file.Skip(4);

			uint32_t unpackedSize = file.ReadUint32LE();

			audioTracks[i].bufferSize = unpackedSize;
			audioTracks[i].buffer = new uint8_t[unpackedSize];
		}
		frameFlag >>= 1;
	}

	return true;
}

// static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, uint32_t prefix, int length)
int SmackerDecoder::DecodeTree(SmackerCommon::BitReader &bits, HuffContext *hc, uint32_t prefix, int length)
{
    if (!bits.GetBit()) // Leaf
	{
        if (hc->current >= 256){
            buildprintf("SmackerDecoder::DecodeTree() - Tree size exceeded\n");
            return -1;
        }
        if (length){
            hc->bits[hc->current] = prefix;
            hc->lengths[hc->current] = length;
        } else {
            hc->bits[hc->current] = 0;
            hc->lengths[hc->current] = 0;
        }
        hc->values[hc->current] = bits.GetBits(8);

        hc->current++;
        if (hc->maxlength < length)
            hc->maxlength = length;
        return 0;
    } else { //Node
        int r;
        length++;
        r = DecodeTree(bits, hc, prefix, length);
        if (r)
            return r;
        return DecodeTree(bits, hc, prefix | (1 << (length - 1)), length);
    }
}

/**
 * Decode header tree
 */
int SmackerDecoder::DecodeBigTree(SmackerCommon::BitReader &bits, HuffContext *hc, DBCtx *ctx)
{
	if (!bits.GetBit()) // Leaf
	{
		int val, i1, i2, b1, b2;

		i1 = 0;
		i2 = 0;

		if (hc->current >= hc->length){
            buildprintf("SmackerDecoder::DecodeBigTree() - Tree size exceeded");
            return -1;
        }

        b1 = bits.GetPosition();

		if (VLC_GetSize(ctx->v1))
		{
			i1 = VLC_GetCodeBits(bits, ctx->v1);
		}

        b1 = bits.GetPosition() - b1;
        b2 = bits.GetPosition();

		if (VLC_GetSize(ctx->v2))
		{
			i2 = VLC_GetCodeBits(bits, ctx->v2);
		}

        b2 = bits.GetPosition() - b2;
        if (i1 < 0 || i2 < 0)
            return -1;
        val = ctx->recode1[i1] | (ctx->recode2[i2] << 8);
        if (val == ctx->escapes[0]) {
            ctx->last[0] = hc->current;
            val = 0;
        } else if (val == ctx->escapes[1]) {
            ctx->last[1] = hc->current;
            val = 0;
        } else if (val == ctx->escapes[2]) {
            ctx->last[2] = hc->current;
            val = 0;
        }

        hc->values[hc->current++] = val;
        return 1;
    } else { //Node
        int r = 0, t;

        t = hc->current++;
        r = DecodeBigTree(bits, hc, ctx);
        if (r < 0)
            return r;
        hc->values[t] = kSMKnode | r;
        r++;
        r += DecodeBigTree(bits, hc, ctx);
        return r;
    }
}

/**
 * Store large tree as Libav's vlc codes
 */
int SmackerDecoder::DecodeHeaderTree(SmackerCommon::BitReader &bits, std::vector<int> &recodes, int *last, int size)
{
	HuffContext huff;
	HuffContext tmp1, tmp2;
	int escapes[3];
	DBCtx ctx;

	if ((uint32_t)size >= UINT_MAX>>4)
	{
        buildprintf("SmackerDecoder::DecodeHeaderTree() - Size too large\n");
		return -1;
	}

	tmp1.length = 256;
	tmp1.maxlength = 0;
	tmp1.current = 0;

	tmp1.bits.resize(256);
	tmp1.lengths.resize(256);
	tmp1.values.resize(256);

	tmp2.length = 256;
	tmp2.maxlength = 0;
	tmp2.current = 0;

	tmp2.bits.resize(256);
	tmp2.lengths.resize(256);
	tmp2.values.resize(256);

	// low byte tree
	if (bits.GetBit()) // 1: Read Tag
	{
		DecodeTree(bits, &tmp1, 0, 0);

		bits.SkipBits(1);

		VLC_InitTable(ctx.v1, tmp1.maxlength, tmp1.current, &tmp1.lengths[0], &tmp1.bits[0]);
	}
	else
	{
		// Skipping low bytes tree
	}

	// high byte tree
	if (bits.GetBit())
	{
		DecodeTree(bits, &tmp2, 0, 0);

		uint32_t UNUSED(end) = bits.GetPosition();

		bits.SkipBits(1);

		VLC_InitTable(ctx.v2, tmp2.maxlength, tmp2.current, &tmp2.lengths[0], &tmp2.bits[0]);
	}
	else
	{
		// Skipping high bytes tree
	}

	escapes[0]  = bits.GetBits(8);
	escapes[0] |= bits.GetBits(8) << 8;
	escapes[1]  = bits.GetBits(8);
	escapes[1] |= bits.GetBits(8) << 8;
	escapes[2]  = bits.GetBits(8);
	escapes[2] |= bits.GetBits(8) << 8;

	last[0] = last[1] = last[2] = -1;

	ctx.escapes[0] = escapes[0];
    ctx.escapes[1] = escapes[1];
    ctx.escapes[2] = escapes[2];

    ctx.recode1 = tmp1.values;
    ctx.recode2 = tmp2.values;
    ctx.last = last;

	huff.length = ((size + 3) >> 2) + 3;
    huff.maxlength = 0;
    huff.current = 0;
	huff.values.resize(huff.length);

	DecodeBigTree(bits, &huff, &ctx);

    bits.SkipBits(1);

    if (ctx.last[0] == -1) ctx.last[0] = huff.current++;
    if (ctx.last[1] == -1) ctx.last[1] = huff.current++;
    if (ctx.last[2] == -1) ctx.last[2] = huff.current++;

	recodes = huff.values;

	return 0;
}

// static int decode_header_trees(SmackVContext *smk) {
bool SmackerDecoder::DecodeHeaderTrees()
{
	SmackerCommon::BitReader bits(file, treeSize);

	if (!bits.GetBit())
	{
		// Skipping MMAP tree
		mmap_tbl.resize(2);
		mmap_tbl[0] = 0;
		mmap_last[0] = mmap_last[1] = mmap_last[2] = 1;
	}
	else
	{
		DecodeHeaderTree(bits, mmap_tbl, mmap_last, mMapSize);
	}

	if (!bits.GetBit())
	{
		// Skipping MCLR tree
		mclr_tbl.resize(2);
		mclr_tbl[0] = 0;
		mclr_last[0] = mclr_last[1] = mclr_last[2] = 1;
	}
	else
	{
		DecodeHeaderTree(bits, mclr_tbl, mclr_last, MClrSize);
	}

	if (!bits.GetBit())
	{
		// Skipping FULL tree
		full_tbl.resize(2);
		full_tbl[0] = 0;
		full_last[0] = full_last[1] = full_last[2] = 1;
	}
	else
	{
		DecodeHeaderTree(bits, full_tbl, full_last, fullSize);
	}

	if (!bits.GetBit())
	{
		// Skipping TYPE tree
		type_tbl.resize(2);
		type_tbl[0] = 0;
		type_last[0] = type_last[1] = type_last[2] = 1;
	}
	else
	{
		DecodeHeaderTree(bits, type_tbl, type_last, typeSize);
	}

	/* FIXME - we don't seems to read/use EVERY bit we 'load' into the bit reader
	 * and as my bitreader reads from the file rather than a buffer read from file
	 * of size 'treeSize', I need to make sure I consume the remaining bits (and thus increment
	 * the file read position to where the code expects it to be when this function returns (ie
	 * 'treeSize' number of bytes must be read
	 */
	uint32_t left = bits.GetSize() - bits.GetPosition();
	bits.SkipBits(left);

	return true;
}

void SmackerDecoder::GetNextFrame()
{
	ReadPacket();
}

int SmackerDecoder::ReadPacket()
{
	// test-remove
	if (currentFrame >= nFrames)
		return 1;

	// seek to next frame position
	file.Seek(nextPos, SmackerCommon::FileStream::kSeekStart);

	uint32_t frameSize = frameSizes[currentFrame] & (~3);
	uint8_t frameFlag  = frameFlags[currentFrame];

	// handle palette change
	if (frameFlag & kSMKpal)
	{
		int size, sz, t, off, j, pos;
        uint8_t *pal = palette;
        uint8_t oldpal[768];

        memcpy(oldpal, pal, 768);
        size = file.ReadByte();
        size = size * 4 - 1;
        frameSize -= size;
        frameSize--;
        sz = 0;
        pos = file.GetPosition() + size;

        while (sz < 256)
		{
            t = file.ReadByte();
            if (t & 0x80){ /* skip palette entries */
                sz  += (t & 0x7F)  + 1;
                pal += ((t & 0x7F) + 1) * 3;
            } else if (t & 0x40){ /* copy with offset */
                off = file.ReadByte() * 3;
                j = (t & 0x3F) + 1;
                while (j-- && sz < 256) {
                    *pal++ = oldpal[off + 0];
                    *pal++ = oldpal[off + 1];
                    *pal++ = oldpal[off + 2];
                    sz++;
                    off += 3;
                }
            } else { /* new entries */
                *pal++ = smk_pal[t];
                *pal++ = smk_pal[file.ReadByte() & 0x3F];
                *pal++ = smk_pal[file.ReadByte() & 0x3F];
                sz++;
            }
        }

		file.Seek(pos, SmackerCommon::FileStream::kSeekStart);
	}

	frameFlag >>= 1;

	// check for and handle audio
	for (int i = 0; i < kMaxAudioTracks; i++)
	{
		audioTracks[i].bytesReadThisFrame = 0;

		if (frameFlag & 1)
		{
			uint32_t size = file.ReadUint32LE() - 4;
			frameSize -= size;
			frameSize -= 4;

			DecodeAudio(size, audioTracks[i]);
		}
		frameFlag >>= 1;
	}

	if (frameSize == 0) {
		return -1;
	}

	DecodeFrame(frameSize);

	currentFrame++;

	nextPos = file.GetPosition();

	return 0;
}

int SmackerDecoder::DecodeFrame(uint32_t frameSize)
{
	last_reset(mmap_tbl, mmap_last);
    last_reset(mclr_tbl, mclr_last);
    last_reset(full_tbl, full_last);
    last_reset(type_tbl, type_last);

	int blocks, blk, bw, bh;
    int i;
    int stride;

	uint8_t *out = picture; // set to output image

	blk = 0;
    bw = frameWidth  >> 2;
    bh = frameHeight >> 2;
    blocks = bw * bh;

	stride = frameWidth;

	uint32_t UNUSED(fileStart) = file.GetPosition();

	SmackerCommon::BitReader bits(file, frameSize);

	while (blk < blocks)
	{
		int type, run, mode;
        uint16_t pix;

        type = GetCode(bits, type_tbl, type_last);
        run = block_runs[(type >> 2) & 0x3F];
        switch (type & 3)
		{
        case SMK_BLK_MONO:
            while (run-- && blk < blocks)
			{
                int clr, map;
                int hi, lo;
                clr = GetCode(bits, mclr_tbl, mclr_last);
                map = GetCode(bits, mmap_tbl, mmap_last);

                out = picture + (blk / bw) * (stride * 4) + (blk % bw) * 4;

                hi = clr >> 8;
                lo = clr & 0xFF;
                for (i = 0; i < 4; i++)
				{
                    if (map & 1) out[0] = hi; else out[0] = lo;
                    if (map & 2) out[1] = hi; else out[1] = lo;
                    if (map & 4) out[2] = hi; else out[2] = lo;
                    if (map & 8) out[3] = hi; else out[3] = lo;
                    map >>= 4;
                    out += stride;
                }
                blk++;
            }
            break;
        case SMK_BLK_FULL:
            mode = 0;
			if (isVer4) // In case of Smacker v4 we have three modes
			{
				if (bits.GetBit()) mode = 1;
				else if (bits.GetBit()) mode = 2;
			}

            while (run-- && blk < blocks)
			{
                out = picture + (blk / bw) * (stride * 4) + (blk % bw) * 4;
                switch (mode)
				{
                case 0:
                    for (i = 0; i < 4; i++)
					{
                        pix = GetCode(bits, full_tbl, full_last);
// FIX                        AV_WL16(out+2, pix);
						out[2] = pix & 0xff;
						out[3] = pix >> 8;

                        pix = GetCode(bits, full_tbl, full_last);
// FIX                        AV_WL16(out, pix);
						out[0] = pix & 0xff;
						out[1] = pix >> 8;
                        out += stride;
                    }
                    break;
                case 1:
                    pix = GetCode(bits, full_tbl, full_last);
                    out[0] = out[1] = pix & 0xFF;
                    out[2] = out[3] = pix >> 8;
                    out += stride;
                    out[0] = out[1] = pix & 0xFF;
                    out[2] = out[3] = pix >> 8;
                    out += stride;
                    pix = GetCode(bits, full_tbl, full_last);
                    out[0] = out[1] = pix & 0xFF;
                    out[2] = out[3] = pix >> 8;
                    out += stride;
                    out[0] = out[1] = pix & 0xFF;
                    out[2] = out[3] = pix >> 8;
                    out += stride;
                    break;
                case 2:
                    for (i = 0; i < 2; i++)
					{
                        uint16_t pix1, pix2;
                        pix2 = GetCode(bits, full_tbl, full_last);
                        pix1 = GetCode(bits, full_tbl, full_last);

// FIX                        AV_WL16(out, pix1);
// FIX                        AV_WL16(out+2, pix2);
						out[0] = pix1 & 0xff;
						out[1] = pix1 >> 8;
						out[2] = pix2 & 0xff;
						out[3] = pix2 >> 8;

                        out += stride;

// FIX                        AV_WL16(out, pix1);
// FIX                        AV_WL16(out+2, pix2);
						out[0] = pix1 & 0xff;
						out[1] = pix1 >> 8;
						out[2] = pix2 & 0xff;
						out[3] = pix2 >> 8;

                        out += stride;
                    }
                    break;
                }
                blk++;
            }
            break;
        case SMK_BLK_SKIP:
            while (run-- && blk < blocks)
                blk++;
            break;
        case SMK_BLK_FILL:
            mode = type >> 8;
            while (run-- && blk < blocks)
			{
                uint32_t col;
                out = picture + (blk / bw) * (stride * 4) + (blk % bw) * 4;
                col = mode * 0x01010101;
                for (i = 0; i < 4; i++) {
                    *((uint32_t*)out) = col;
                    out += stride;
                }
                blk++;
            }
            break;
        }
	}

	/* FIXME - we don't seems to read/use EVERY bit we 'load' into the bit reader
	 * and as my bitreader reads from the file rather than a buffer read from file
	 * of size 'frameSize', I need to make sure I consume the remaining bits (and thus increment
	 * the file read position to where the code expects it to be when this function returns (ie
	 * 'frameSize' number of bytes must be read
	 */
	uint32_t left = bits.GetSize() - bits.GetPosition();
	bits.SkipBits(left);

	return 0;
}

/**
 * Decode Smacker audio data
 */
int SmackerDecoder::DecodeAudio(uint32_t size, SmackerAudioTrack &track)
{
    HuffContext h[4];
	SmackerCommon::VLCtable vlc[4];
    int val;
    int i, res;
    int unpackedSize;
    int sampleBits, stereo;
    int pred[2] = {0, 0};

	int16_t *samples = reinterpret_cast<int16_t*>(track.buffer);
    int8_t *samples8 = reinterpret_cast<int8_t*>(track.buffer);

	int buf_size = track.bufferSize;

    if (buf_size <= 4) {
        buildprintf("SmackerDecoder::DecodeAudio() - Packet is too small\n");
		return -1;
    }

	SmackerCommon::BitReader bits(file, size);

    unpackedSize = bits.GetBits(32);

    if (!bits.GetBit()) {
		// no sound data
        return 1;
    }

    stereo     = bits.GetBit();
    sampleBits = bits.GetBit();

    if (stereo ^ (track.nChannels != 1)) {
        buildprintf("SmackerDecoder::DecodeAudio() - Channels mismatch\n");
		return -1;
    }

    memset(h, 0, sizeof(HuffContext) * 4);

    // Initialize
    for (i = 0; i < (1 << (sampleBits + stereo)); i++) {
        h[i].length = 256;
        h[i].maxlength = 0;
        h[i].current = 0;
        h[i].bits.resize(256);
        h[i].lengths.resize(256);
        h[i].values.resize(256);

        bits.SkipBits(1);
		DecodeTree(bits, &h[i], 0, 0);
        bits.SkipBits(1);

        if (h[i].current > 1) {
			VLC_InitTable(vlc[i], h[i].maxlength, h[i].current, &h[i].lengths[0], &h[i].bits[0]);
        }
    }
    if (sampleBits) { //decode 16-bit data
        for (i = stereo; i >= 0; i--)
            pred[i] = av_bswap16(bits.GetBits(16));
        for (i = 0; i <= stereo; i++)
            *samples++ = pred[i];
        for (; i < unpackedSize / 2; i++) {
            if (i & stereo) {
				if (VLC_GetSize(vlc[2]))
					res = VLC_GetCodeBits(bits, vlc[2]);
                else
                    res = 0;
                val  = h[2].values[res];
				if (VLC_GetSize(vlc[3]))
					res = VLC_GetCodeBits(bits, vlc[3]);
                else
                    res = 0;
                val |= h[3].values[res] << 8;
                pred[1] += (int16_t)val;
                *samples++ = pred[1];
            } else {
				if (VLC_GetSize(vlc[0]))
					res = VLC_GetCodeBits(bits, vlc[0]);
                else
                    res = 0;
                val  = h[0].values[res];
				if (VLC_GetSize(vlc[1]))
					res = VLC_GetCodeBits(bits, vlc[1]);
                else
                    res = 0;
                val |= h[1].values[res] << 8;
                pred[0] += val;
                *samples++ = pred[0];
            }
        }
    }
	else { //8-bit data
        for (i = stereo; i >= 0; i--)
            pred[i] = bits.GetBits(8);
        for (i = 0; i <= stereo; i++)
            *samples8++ = pred[i];
        for (; i < unpackedSize; i++) {
            if (i & stereo){
				if (VLC_GetSize(vlc[1]))
					res = VLC_GetCodeBits(bits, vlc[1]);
                else
                    res = 0;
                pred[1] += (int8_t)h[1].values[res];
                *samples8++ = pred[1];
            } else {
				if (VLC_GetSize(vlc[0]))
				res = VLC_GetCodeBits(bits, vlc[0]);
                else
                    res = 0;
                pred[0] += (int8_t)h[0].values[res];
                *samples8++ = pred[0];
            }
        }
    }

	track.bytesReadThisFrame = unpackedSize;

	uint32_t left = bits.GetSize() - bits.GetPosition();
	bits.SkipBits(left);

	return 0;
}

void SmackerDecoder::GetPalette(uint8_t *palette)
{
	memcpy(palette, this->palette, 768);
}

void SmackerDecoder::GetFrame(uint8_t *frame)
{
	memcpy(frame, this->picture, frameWidth * frameHeight);
}

void SmackerDecoder::GetFrameSize(uint32_t &width, uint32_t &height)
{
	width  = this->frameWidth;
	height = this->frameHeight;
}

uint32_t SmackerDecoder::GetNumFrames()
{
	return nFrames;
}

uint32_t SmackerDecoder::GetCurrentFrameNum()
{
	return currentFrame;
}

float SmackerDecoder::GetFrameRate()
{
	return (float)fps;
}

void SmackerDecoder::GotoFrame(uint32_t frameNum)
{
	if (frameNum > nFrames) {
        buildprintf("SmackerDecoder::GotoFrame() - Invalid frame number\n");
		return;
	}

//    file.Seek(firstFrameFilePos, SmackerCommon::FileStream::kSeekStart);

    currentFrame = 0;
    nextPos = firstFrameFilePos;

    for (int i = 0; i < frameNum + 1; i++)
        GetNextFrame();
}

SmackerAudioInfo SmackerDecoder::GetAudioTrackDetails(uint32_t trackIndex)
{
	SmackerAudioInfo info;
	SmackerAudioTrack *track = &audioTracks[trackIndex];

	info.sampleRate = track->sampleRate;
	info.nChannels  = track->nChannels;
	info.bitsPerSample = track->bitsPerSample;

	// audio buffer size in bytes
	info.idealBufferSize = track->bufferSize;

	return info;
}

uint32_t SmackerDecoder::GetAudioData(uint32_t trackIndex, int16_t *audioBuffer)
{
	if (!audioBuffer) {
		return 0;
	}

	SmackerAudioTrack *track = &audioTracks[trackIndex];

	if (track->bytesReadThisFrame) {
		memcpy(audioBuffer, track->buffer, std::min(track->bufferSize, track->bytesReadThisFrame));
	}

	return track->bytesReadThisFrame;
}


code:
   __   _ __                       ___ __
  / /  (_) / __ __ __ _  ___  ____/ (_) /____
 / /__/ / _ \\ \ //  ' \/ _ \/___/ / / __/ -_)
/____/_/_.__/_\_\/_/_/_/ .__/   /_/_/\__/\__/
                      /_/

Libxmp-lite is a lean and lightweight subset of Libxmp that plays MOD, S3M,
XM, and IT modules and retains full compatibility with the original API.
It's intended for games and small or embedded applications where module
format diversity and file depacking are not required.

Library size can be further reduced by disabling Impulse Tracker format
support (configure with --disable-it). This option will also disable IT
effects and lowpass filtering.

Please refer to http://xmp.sf.net/libxmp.html for details on the current
Libxmp API.

LICENSE

Extended Module Player Lite
Copyright (C) 1996-2016 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.

repo_name: orochi/nblood

path: source/libxmp-lite/README

license: mit


code:
#ifndef XMP_H
#define XMP_H

#ifdef __cplusplus
extern "C" {
#endif

#define XMP_VERSION "4.5.0"
#define XMP_VERCODE 0x040500
#define XMP_VER_MAJOR 4
#define XMP_VER_MINOR 5
#define XMP_VER_RELEASE 0

#if defined(_WIN32) && !defined(__CYGWIN__)
# if defined(BUILDING_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__) && defined(__SW_BD)
#  define LIBXMP_EXPORT __declspec(dllexport)
#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY)
# define LIBXMP_EXPORT __attribute__((visibility ("default")))
#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL)
# define LIBXMP_EXPORT __global
#elif defined(EMSCRIPTEN)
# include <emscripten.h>
# 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_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);

#ifdef EDUKE32_DISABLE
LIBXMP_EXPORT int         xmp_load_module     (xmp_context, const char *);
#endif // EDUKE32_DISABLE
LIBXMP_EXPORT int         xmp_load_module_from_memory (xmp_context, const void *, long);
#ifdef EDUKE32_DISABLE
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 *);
#endif // EDUKE32_DISABLE
LIBXMP_EXPORT int         xmp_test_module_from_memory (const void *, long, struct xmp_test_info *);
#ifdef EDUKE32_DISABLE
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 *);
#endif // EDUKE32_DISABLE

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 */


code:
/* 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 <ctype.h>

#include "common.h"

#ifndef LIBXMP_CORE_PLAYER
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <limits.h>
#elif defined(__OS2__) || defined(__EMX__)
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>
#elif defined(__DJGPP__)
#include <dos.h>
#include <io.h>
#elif defined(LIBXMP_AMIGA)
#ifdef __amigaos4__
#define __USE_INLINE__
#endif
#include <proto/dos.h>
#elif defined(HAVE_DIRENT)
#include <dirent.h>
#endif
#endif /* LIBXMP_CORE_PLAYER */

#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(sizeof (struct xmp_instrument), mod->ins);
		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(sizeof (struct xmp_sample), mod->smp);
		if (mod->xxs == NULL)
			return -1;
		m->xtra = (struct extra_sample_data *)calloc(sizeof (struct extra_sample_data), mod->smp);
		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(sizeof (struct xmp_subinstrument), num);
	if (mod->xxi[i].sub == NULL)
		return -1;

	return 0;
}

int libxmp_init_pattern(struct xmp_module *mod)
{
	mod->xxt = (struct xmp_track **)calloc(sizeof (struct xmp_track *), mod->trk);
	if (mod->xxt == NULL)
		return -1;

	mod->xxp = (struct xmp_pattern **)calloc(sizeof (struct xmp_pattern *), mod->pat);
	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(sizeof (struct xmp_track) +
			       sizeof (struct xmp_event) * (rows - 1), 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;
}

/* 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;
}

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((int)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);

	hio_read(buf, 1, s, f);		/* coverity[check_return] */
	buf[s] = 0;
	libxmp_copy_adjust(t, buf, s);
}

#ifndef LIBXMP_CORE_PLAYER

int libxmp_test_name(uint8 *s, int n)
{
	int i;

	for (i = 0; i < n; i++) {
		if (s[i] > 0x7f)
			return -1;
		/* ACS_Team2.mod has a backspace in instrument name */
		if (s[i] > 0 && s[i] < 32 && s[i] != 0x08)
			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;
}
#endif

/*
 * 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, 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);
}

void libxmp_decode_protracker_event(struct xmp_event *event, 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)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
	char path[_MAX_PATH];
	DWORD rc;
	/* win32 is case-insensitive: directly probe the file. */
	snprintf(path, sizeof(path), "%s/%s", dir, name);
	rc = GetFileAttributesA(path);
	if (rc == (DWORD)(-1)) return 0;
	if (rc & FILE_ATTRIBUTE_DIRECTORY) return 0;
	strncpy(new_name, name, size);
	return 1;
}
#elif defined(__OS2__) || defined(__EMX__)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
	char path[CCHMAXPATH];
	FILESTATUS3 fs;
	/* os/2 is case-insensitive: directly probe the file. */
	snprintf(path, sizeof(path), "%s/%s", dir, name);
	if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != NO_ERROR) return 0;
	if (fs.attrFile & FILE_DIRECTORY) return 0;
	strncpy(new_name, name, size);
	return 1;
}
#elif defined(__DJGPP__)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
	char path[256];
	int attr;
	/* dos is case-insensitive: directly probe the file. */
	snprintf(path, sizeof(path), "%s/%s", dir, name);
	attr = _chmod(path, 0);
	if (attr == -1) return 0;
	if (attr & (_A_SUBDIR|_A_VOLID)) return 0;
	strncpy(new_name, name, size);
	return 1;
}
#elif defined(LIBXMP_AMIGA)
#ifdef __amigaos4__
#include <dos/obsolete.h>
#endif
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
	char path[256]; BPTR lock;
	struct FileInfoBlock *fib;
	int found = 0;
	/* amigados is case-insensitive: directly probe the file. */
	snprintf(path, sizeof(path), "%s/%s", dir, name);
	lock = Lock((const STRPTR)path, ACCESS_READ);
	if (lock) {
		fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL);
		if (fib != NULL) {
		    if (Examine(lock, fib)) {
			if (fib->fib_DirEntryType < 0) {
				found = 1;
				strncpy(new_name, name, size);
			}
		    }
		    FreeDosObject(DOS_FIB, fib);
		}
		UnLock(lock);
	}
	return found;
}
#elif defined(HAVE_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;
}
#else
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
	return 0;
}
#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);
}

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; */
		int 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)(((int64)t * 10000L + 14780) / 3652425);
		dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
		if (dayofyear < 0) {
			year--;
			dayofyear = t - (365 * 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);
	}
}


code:
#ifndef LIBXMP_COMMON_H
#define LIBXMP_COMMON_H

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "xmp.h"

#undef  LIBXMP_EXPORT_VAR
#if defined(EMSCRIPTEN)
#include <emscripten.h>
#define LIBXMP_EXPORT_VAR EMSCRIPTEN_KEEPALIVE
#else
#define LIBXMP_EXPORT_VAR
#endif

#if defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \
    defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \
    defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__)
#define LIBXMP_AMIGA	1	/* to identify amiga platforms. */
#endif

#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY)
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(LIBXMP_AMIGA) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__)
#define USE_VERSIONED_SYMBOLS
#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
#endif
#endif

/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
 */
#if defined B_BEOS_VERSION
#  include <SupportDefs.h>
#elif defined __amigaos4__
#  include <exec/types.h>
#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				/* MSVC++6.0 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

#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))

#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: "
#ifndef CLIB_DECL
#define CLIB_DECL
#endif
#ifdef DEBUG
#ifndef ATTR_PRINTF
#define ATTR_PRINTF(x,y)
#endif
void CLIB_DECL D_(const char *text, ...) ATTR_PRINTF(1,2);
#else
// VS prior to VC7.1 does not support variadic macros. VC8.0 does not optimize unused parameters passing
#if _MSC_VER < 1400
void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#else
#define D_(args, ...) do {} while (0)
#endif
#endif

#elif defined __ANDROID__

#ifdef DEBUG
#include <android/log.h>
#define D_CRIT "  Error: "
#define D_WARN "Warning: "
#define D_INFO "   Info: "
#define D_(args...) do { \
	__android_log_print(ANDROID_LOG_DEBUG, "libxmp", args); \
	} while (0)
#else
#define D_(args...) do {} while (0)
#endif

#elif defined(__WATCOMC__)
#ifdef DEBUG
#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, __FUNCTION__, \
		__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
	} while (0)
#else
#define D_(...) do {} while (0)
#endif

#else

#ifdef DEBUG
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(args...) do { \
	printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
		__FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \
	} while (0)
#else
#define D_(args...) do {} while (0)
#endif

#endif	/* !_MSC_VER */

#ifdef _MSC_VER
#define dup _dup
#define fileno _fileno
#define strnicmp _strnicmp
#define strdup _strdup
#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
int libxmp_vsnprintf(char *, size_t, const char *, va_list);
int libxmp_snprintf (char *, size_t, const char *, ...);
#define snprintf  libxmp_snprintf
#define vsnprintf libxmp_vsnprintf
#endif
#endif

/* 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_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 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;
};

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 */
	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;
#ifndef LIBXMP_CORE_DISABLE_IT
	struct xmp_sample *xsmp;	/* sustain loop samples */
#endif
};

struct pattern_loop {
	int start;
	int count;
};

struct flow_control {
	int pbreak;
	int jump;
	int delay;
	int jumpline;
	int loop_chn;

	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 */
	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 *);

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);
int	move_data		(FILE *, FILE *, int);

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);

#endif /* LIBXMP_COMMON_H */


code:
/* 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 "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;
			}
			f->jumpline = 0;
			f->jump = -1;
			f->pbreak = 0;
			f->loop_chn = 0;
			f->delay = 0;
			f->rowdelay = 0;
			f->rowdelay_set = 0;
		}
	}
}

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_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

#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_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

#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 = 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;
}


code:
/* 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 <errno.h>
#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 0xffffff;
}

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 0xffffff;
}

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);
}

int move_data(FILE *out, FILE *in, int len)
{
	uint8 buf[1024];
	int l;

	do {
		l = fread(buf, 1, len > 1024 ? 1024 : len, in);
		fwrite(buf, 1, l, out);
		len -= l;
	} while (l > 0 && len > 0);

	return 0;
}

#endif