Files
Konrad Beckmann 20d7fefaf9 Import mikmod
2021-08-02 02:19:41 +02:00

440 lines
11 KiB
C

/* MikMod sound library (c) 2003-2015 Raphael Assenat and others -
* see AUTHORS file for a complete list.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
/* MMCMP ("ziRCONia") decompression support
*
* Based on the public domain version from the libmodplug library by
* Olivier Lapicque <olivierl@jps.net> (sezero's fork of libmodplug
* at github: http://github.com/sezero/libmodplug/tree/sezero )
*
* Rewritten for libmikmod by O. Sezer <sezero@users.sourceforge.net>
* with some extra ideas from the libxmp version by Claudio Matsuoka.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef NO_DEPACKERS
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <string.h>
#include "mikmod_internals.h"
#ifdef SUNOS
extern int fprintf(FILE *, const char *, ...);
#endif
/*#define MMCMP_DEBUG*/
typedef struct MMCMPFILEHDR
{
UBYTE id[8]; /* string 'ziRCONia' */
UWORD hdrsize; /* sizeof MMCMPHEADER */
} MMCMPFILEHDR; /* 10 bytes */
typedef struct MMCMPHEADER
{
UWORD version;
UWORD nblocks;
ULONG filesize;
ULONG blktable;
UBYTE glb_comp;
UBYTE fmt_comp;
} MMCMPHEADER; /* 14 bytes */
typedef struct MMCMPBLOCK
{
ULONG unpk_size;
ULONG pk_size;
ULONG xor_chk;
UWORD sub_blk;
UWORD flags;
UWORD tt_entries;
UWORD num_bits;
} MMCMPBLOCK; /* 20 bytes */
typedef struct MMCMPSUBBLOCK
{
ULONG unpk_pos;
ULONG unpk_size;
} MMCMPSUBBLOCK; /* 8 bytes */
#define MMCMP_COMP 0x0001
#define MMCMP_DELTA 0x0002
#define MMCMP_16BIT 0x0004
#define MMCMP_STEREO 0x0100
#define MMCMP_ABS16 0x0200
#define MMCMP_ENDIAN 0x0400
typedef struct MMCMPBITBUFFER
{
ULONG bitcount;
ULONG bitbuffer;
const UBYTE *start;
const UBYTE *end;
} MMCMPBITBUFFER;
static ULONG MMCMP_GetBits(MMCMPBITBUFFER* b, ULONG nBits)
{
ULONG d;
if (!nBits) return 0;
while (b->bitcount < 24)
{
b->bitbuffer |= ((b->start < b->end) ? *b->start++ : 0) << b->bitcount;
b->bitcount += 8;
}
d = b->bitbuffer & ((1 << nBits) - 1);
b->bitbuffer >>= nBits;
b->bitcount -= nBits;
return d;
}
static const ULONG MMCMP8BitCommands[8] =
{
0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8
};
static const ULONG MMCMP8BitFetch[8] =
{
3, 3, 3, 3, 2, 1, 0, 0
};
static const ULONG MMCMP16BitCommands[16] =
{
0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0
};
static const ULONG MMCMP16BitFetch[16] =
{
4, 4, 4, 4, 3, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
BOOL MMCMP_Unpack(MREADER* reader, void** out, long* outlen)
{
ULONG srclen, destlen;
UBYTE *destbuf, *destptr, *destend;
MMCMPHEADER mmh;
ULONG *pblk_table;
MMCMPSUBBLOCK *subblocks;
ULONG i, blockidx, numsubs;
UBYTE *buf; ULONG bufsize;
_mm_fseek(reader,0,SEEK_END);
srclen = _mm_ftell(reader);
if (srclen < 256) return 0;
_mm_rewind(reader);
if (_mm_read_I_ULONG(reader) != 0x4352697A) /* 'ziRC' */
return 0;
if (_mm_read_I_ULONG(reader) != 0x61694e4f) /* 'ONia' */
return 0;
if (_mm_read_I_UWORD(reader) != 14) /* header size */
return 0;
mmh.version = _mm_read_I_UWORD(reader);
mmh.nblocks = _mm_read_I_UWORD(reader);
mmh.filesize = _mm_read_I_ULONG(reader);
mmh.blktable = _mm_read_I_ULONG(reader);
mmh.glb_comp = _mm_read_UBYTE(reader);
mmh.fmt_comp = _mm_read_UBYTE(reader);
if ((!mmh.nblocks) || (mmh.filesize < 16) || (mmh.filesize > 0x8000000) ||
(mmh.blktable >= srclen) || (mmh.blktable + 4*mmh.nblocks > srclen)) {
return 0;
}
destlen = mmh.filesize;
numsubs = 32;
bufsize = 65536;
destbuf = (UBYTE*)MikMod_malloc(destlen);
buf = (UBYTE*)MikMod_malloc(bufsize);
pblk_table = (ULONG*)MikMod_malloc(mmh.nblocks*4);
subblocks = (MMCMPSUBBLOCK*)MikMod_malloc(numsubs*sizeof(MMCMPSUBBLOCK));
if (!destbuf || !buf || !pblk_table || !subblocks)
goto err;
destend = destbuf + destlen;
_mm_fseek(reader,mmh.blktable,SEEK_SET);
for (blockidx = 0; blockidx < mmh.nblocks; blockidx++) {
pblk_table[blockidx] = _mm_read_I_ULONG(reader);
}
for (blockidx = 0; blockidx < mmh.nblocks; blockidx++)
{
ULONG srcpos = pblk_table[blockidx];
MMCMPBLOCK block;
if (srcpos + 20 >= srclen) goto err;
_mm_fseek(reader,srcpos,SEEK_SET);
block.unpk_size = _mm_read_I_ULONG(reader);
block.pk_size = _mm_read_I_ULONG(reader);
block.xor_chk = _mm_read_I_ULONG(reader);
block.sub_blk = _mm_read_I_UWORD(reader);
block.flags = _mm_read_I_UWORD(reader);
block.tt_entries = _mm_read_I_UWORD(reader);
block.num_bits = _mm_read_I_UWORD(reader);
if (!block.unpk_size || !block.pk_size || !block.sub_blk)
goto err;
if (block.pk_size <= block.tt_entries)
goto err;
if (block.flags & MMCMP_COMP) {
if (block.flags & MMCMP_16BIT) {
if (block.num_bits >= 16)
goto err;
}
else {
if (block.num_bits >= 8)
goto err;
}
}
srcpos += 20 + block.sub_blk*8;
if (srcpos >= srclen) goto err;
if (numsubs < block.sub_blk) {
numsubs = (block.sub_blk + 31) & ~31;
MikMod_free(subblocks);
subblocks = (MMCMPSUBBLOCK*)MikMod_malloc(numsubs*sizeof(MMCMPSUBBLOCK));
if (!subblocks) goto err;
}
for (i = 0; i < block.sub_blk; i++) {
subblocks[i].unpk_pos = _mm_read_I_ULONG(reader);
subblocks[i].unpk_size = _mm_read_I_ULONG(reader);
if (subblocks[i].unpk_pos >= destlen) goto err;
if (subblocks[i].unpk_size > destlen - subblocks[i].unpk_pos) goto err;
}
#ifdef MMCMP_DEBUG
fprintf(stderr, "block %u: flags=%04X sub_blocks=%u",
blockidx, (unsigned)block.flags, (unsigned)block.sub_blk);
fprintf(stderr, " pksize=%u unpksize=%u", block.pk_size, block.unpk_size);
fprintf(stderr, " tt_entries=%u num_bits=%u\n", block.tt_entries, block.num_bits);
#endif
if (!(block.flags & MMCMP_COMP))
{ /* Data is not packed */
_mm_fseek(reader,srcpos,SEEK_SET);
destptr = destbuf + subblocks[0].unpk_pos;
i = 0;
while (1) {
#ifdef MMCMP_DEBUG
fprintf(stderr, " Unpacked sub-block %u: offset %u, size=%u\n",
i, subblocks[i].unpk_pos, subblocks[i].unpk_size);
#endif
_mm_read_UBYTES(destptr,subblocks[i].unpk_size,reader);
destptr += subblocks[i].unpk_size;
if (++i == block.sub_blk) break;
}
}
else if (block.flags & MMCMP_16BIT)
{ /* Data is 16-bit packed */
MMCMPBITBUFFER bb;
ULONG size;
ULONG pos = 0;
ULONG numbits = block.num_bits;
ULONG oldval = 0;
#ifdef MMCMP_DEBUG
fprintf(stderr, " 16-bit block: pos=%u size=%u ",
subblocks[0].unpk_pos, subblocks[0].unpk_size);
if (block.flags & MMCMP_DELTA) fprintf(stderr, "DELTA ");
if (block.flags & MMCMP_ABS16) fprintf(stderr, "ABS16 ");
fprintf(stderr, "\n");
#endif
size = block.pk_size - block.tt_entries;
if (bufsize < size) {
while (bufsize < size) bufsize += 65536;
MikMod_free(buf);
if (!(buf = (UBYTE*)MikMod_malloc(bufsize)))
goto err;
}
bb.bitcount = 0;
bb.bitbuffer = 0;
bb.start = buf;
bb.end = buf + size;
_mm_fseek(reader,srcpos+block.tt_entries,SEEK_SET);
_mm_read_UBYTES(buf,size,reader);
destptr = destbuf + subblocks[0].unpk_pos;
size = subblocks[0].unpk_size;
i = 0;
while (1)
{
ULONG newval = 0x10000;
ULONG d = MMCMP_GetBits(&bb, numbits+1);
if (d >= MMCMP16BitCommands[numbits])
{
ULONG nFetch = MMCMP16BitFetch[numbits];
ULONG newbits = MMCMP_GetBits(&bb, nFetch) + ((d - MMCMP16BitCommands[numbits]) << nFetch);
if (newbits != numbits)
{
numbits = newbits & 0x0F;
} else
{
if ((d = MMCMP_GetBits(&bb, 4)) == 0x0F)
{
if (MMCMP_GetBits(&bb, 1)) break;
newval = 0xFFFF;
} else
{
newval = 0xFFF0 + d;
}
}
} else
{
newval = d;
}
if (newval < 0x10000)
{
newval = (newval & 1) ? (ULONG)(-(SLONG)((newval+1) >> 1)) : (ULONG)(newval >> 1);
if (block.flags & MMCMP_DELTA)
{
newval += oldval;
oldval = newval;
} else
if (!(block.flags & MMCMP_ABS16))
{
newval ^= 0x8000;
}
if (destend - destptr < 2) goto err;
pos += 2;
*destptr++ = (UBYTE) (((UWORD)newval) & 0xff);
*destptr++ = (UBYTE) (((UWORD)newval) >> 8);
}
if (pos >= size)
{
if (++i == block.sub_blk) break;
size = subblocks[i].unpk_size;
destptr = destbuf + subblocks[i].unpk_pos;
pos = 0;
}
}
}
else
{ /* Data is 8-bit packed */
MMCMPBITBUFFER bb;
ULONG size;
ULONG pos = 0;
ULONG numbits = block.num_bits;
ULONG oldval = 0;
UBYTE ptable[0x100];
size = block.pk_size - block.tt_entries;
if (bufsize < size) {
while (bufsize < size) bufsize += 65536;
MikMod_free(buf);
if (!(buf = (UBYTE*)MikMod_malloc(bufsize)))
goto err;
}
bb.bitcount = 0;
bb.bitbuffer = 0;
bb.start = buf;
bb.end = buf + size;
_mm_read_UBYTES(ptable,0x100,reader);
_mm_fseek(reader,srcpos+block.tt_entries,SEEK_SET);
_mm_read_UBYTES(buf,size,reader);
destptr = destbuf + subblocks[0].unpk_pos;
size = subblocks[0].unpk_size;
i = 0;
while (1)
{
ULONG newval = 0x100;
ULONG d = MMCMP_GetBits(&bb, numbits+1);
if (d >= MMCMP8BitCommands[numbits])
{
ULONG nFetch = MMCMP8BitFetch[numbits];
ULONG newbits = MMCMP_GetBits(&bb, nFetch) + ((d - MMCMP8BitCommands[numbits]) << nFetch);
if (newbits != numbits)
{
numbits = newbits & 0x07;
} else
{
if ((d = MMCMP_GetBits(&bb, 3)) == 7)
{
if (MMCMP_GetBits(&bb, 1)) break;
newval = 0xFF;
} else
{
newval = 0xF8 + d;
}
}
} else
{
newval = d;
}
if (newval < 0x100)
{
int n = ptable[newval];
if (block.flags & MMCMP_DELTA)
{
n += oldval;
oldval = n;
}
destptr[pos++] = (UBYTE)n;
}
if (pos >= size)
{
if (++i == block.sub_blk) break;
size = subblocks[i].unpk_size;
destptr = destbuf + subblocks[i].unpk_pos;
pos = 0;
}
}
}
}
MikMod_free(buf);
MikMod_free(pblk_table);
MikMod_free(subblocks);
*out = destbuf;
*outlen = destlen;
return 1;
err:
MikMod_free(buf);
MikMod_free(pblk_table);
MikMod_free(subblocks);
MikMod_free(destbuf);
return 0;
}
#endif /* NO_DEPACKERS */