--- /dev/null
+/* 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
+
+#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;
+ 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;
+
+ _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) 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;
+ }
+ destptr[pos++] = (UBYTE) (((UWORD)newval) & 0xff);
+ destptr[pos++] = (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;
+}