/* 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. */ /* XPK-SQSH depacker * Algorithm from the portable decruncher by Bert Jahn (24.12.97) * Checksum added by Sipos Attila * Rewritten for libxmp by Claudio Matsuoka * Rewritten for libmikmod by O. Sezer * * Copyright (C) 2013 Claudio Matsuoka * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef NO_DEPACKERS #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_MEMORY_H #include #endif #include #include "mikmod_internals.h" #ifdef SUNOS extern int fprintf(FILE *, const char *, ...); #endif struct io { UBYTE *src; UBYTE *dest; int offs; int srclen; }; static const UBYTE ctable[]={ 2, 3, 4, 5, 6, 7, 8, 0, 3, 2, 4, 5, 6, 7, 8, 0, 4, 3, 5, 2, 6, 7, 8, 0, 5, 4, 6, 2, 3, 7, 8, 0, 6, 5, 7, 2, 3, 4, 8, 0, 7, 6, 8, 2, 3, 4, 5, 0, 8, 7, 6, 2, 3, 4, 5, 0 }; static UWORD readmem16b(UBYTE *m) { ULONG a, b; a = m[0]; b = m[1]; return (a << 8) | b; } static ULONG readmem24b(UBYTE *m) { ULONG a, b, c; a = m[0]; b = m[1]; c = m[2]; return (a << 16) | (b << 8) | c; } static UWORD xchecksum(ULONG * ptr, ULONG count) { register ULONG sum = 0; while (count-- > 0) { sum ^= *ptr++; } return (UWORD) (sum ^ (sum >> 16)); } static int has_bits(struct io *io, int count) { return (count <= io->srclen - io->offs); } static int get_bits(struct io *io, int count) { int r; if (!has_bits(io, count)) { return -1; } r = readmem24b(io->src + (io->offs >> 3)); r <<= io->offs % 8; r &= 0xffffff; r >>= 24 - count; io->offs += count; return r; } static int get_bits_final(struct io *io, int count) { /* Note: has_bits check should be done separately since * this can return negative values. */ int r = readmem24b(io->src + (io->offs >> 3)); r <<= (io->offs % 8) + 8; r >>= 32 - count; io->offs += count; return r; } static int copy_data(struct io *io, int d1, int *data, UBYTE *dest_start, UBYTE *dest_end) { UBYTE *copy_src; int r, dest_offset, count, copy_len; if (get_bits(io, 1) == 0) { copy_len = get_bits(io, 1) + 2; } else if (get_bits(io, 1) == 0) { copy_len = get_bits(io, 1) + 4; } else if (get_bits(io, 1) == 0) { copy_len = get_bits(io, 1) + 6; } else if (get_bits(io, 1) == 0) { copy_len = get_bits(io, 3) + 8; } else { copy_len = get_bits(io, 5) + 16; } r = get_bits(io, 1); if (copy_len < 0 || r < 0) { return -1; } if (r == 0) { r = get_bits(io, 1); if (r < 0) { return -1; } if (r == 0) { count = 8; dest_offset = 0; } else { count = 14; dest_offset = -0x1100; } } else { count = 12; dest_offset = -0x100; } copy_len -= 3; if (copy_len >= 0) { if (copy_len != 0) { d1--; } d1--; if (d1 < 0) { d1 = 0; } } copy_len += 2; r = get_bits(io, count); if (r < 0) { return -1; } copy_src = io->dest + dest_offset - r - 1; /* Sanity check */ if (copy_src < dest_start || copy_src + copy_len >= dest_end) { return -1; } do { //printf("dest=%p src=%p end=%p\n", io->dest, copy_src, dest_end); *io->dest++ = *copy_src++; } while (copy_len--); *data = *(--copy_src); return d1; } static int unsqsh_block(struct io *io, UBYTE *dest_start, UBYTE *dest_end) { int r, d1, d2, data, unpack_len, count, old_count; d1 = d2 = data = old_count = 0; io->offs = 0; data = *(io->src++); *(io->dest++) = data; do { r = get_bits(io, 1); if (r < 0) return -1; if (d1 < 8) { if (r) { d1 = copy_data(io, d1, &data, dest_start, dest_end); if (d1 < 0) return -1; d2 -= d2 >> 3; continue; } unpack_len = 0; count = 8; } else { if (r) { count = 8; if (count == old_count) { if (d2 >= 20) { unpack_len = 1; d2 += 8; } else { unpack_len = 0; } } else { count = old_count; unpack_len = 4; d2 += 8; } } else { r = get_bits(io, 1); if (r < 0) return -1; if (r == 0) { d1 = copy_data(io, d1, &data, dest_start, dest_end); if (d1 < 0) return -1; d2 -= d2 >> 3; continue; } r = get_bits(io, 1); if (r < 0) return -1; if (r == 0) { count = 2; } else { r = get_bits(io, 1); if (r < 0) return -1; if (r) { io->offs--; count = get_bits(io, 3); if (count < 0) return -1; } else { count = 3; } } count = ctable[8 * old_count + count - 17]; if (count != 8) { unpack_len = 4; d2 += 8; } else { if (d2 >= 20) { unpack_len = 1; d2 += 8; } else { unpack_len = 0; } } } } if (!has_bits(io, count * (unpack_len + 2))) { return -1; } do { data -= get_bits_final(io, count); *io->dest++ = data; } while (unpack_len--); if (d1 != 31) { d1++; } old_count = count; d2 -= d2 >> 3; } while (io->dest < dest_end); return 0; } static int unsqsh(UBYTE *src, SLONG srclen, UBYTE *dest, SLONG destlen) { SLONG len = destlen; SLONG decrunched = 0; UBYTE type; SLONG packed_size, unpacked_size; ULONG sum, lchk; UBYTE *c, *dest_start, *dest_end; UBYTE bc[3]; struct io io; io.src = src; io.dest = dest; dest_start = io.dest; c = src + 20; while (len) { /* Sanity check */ if (c >= src + srclen) { return -1; } type = *c++; c++; /* hchk */ sum = *(UWORD *)c; c += 2; /* checksum */ packed_size = readmem16b(c); /* packed */ c += 2; unpacked_size = readmem16b(c); /* unpacked */ c += 2; /* Sanity check */ if (packed_size <= 0 || unpacked_size <= 0) { return -1; } if (c + packed_size + 3 > src + srclen) { return -1; } io.src = c + 2; io.srclen = packed_size << 3; memcpy(bc, c + packed_size, 3); memset(c + packed_size, 0, 3); lchk = xchecksum((ULONG *) (c), (packed_size + 3) >> 2); memcpy(c + packed_size, bc, 3); if (lchk != sum) { return decrunched; } if (type == 0) { /* verbatim block */ decrunched += packed_size; if (decrunched > destlen) { return -1; } memcpy(io.dest, c, packed_size); io.dest += packed_size; c += packed_size; len -= packed_size; continue; } if (type != 1) { /* unknown type */ return decrunched; } len -= unpacked_size; decrunched += unpacked_size; /* Sanity check */ if (decrunched > destlen) { return -1; } packed_size = (packed_size + 3) & 0xfffc; c += packed_size; dest_end = io.dest + unpacked_size; if (unsqsh_block(&io, dest_start, dest_end) < 0) { return -1; } io.dest = dest_end; } return decrunched; } BOOL XPK_Unpack(MREADER *reader, void **out, long *outlen) { UBYTE *src, *dest; SLONG inlen, srclen, destlen; _mm_fseek(reader,0,SEEK_END); inlen = _mm_ftell(reader); if (inlen <= 8 || inlen > 0x100000) return 0; _mm_rewind(reader); if (_mm_read_M_ULONG(reader) != 0x58504b46) /* XPKF */ return 0; srclen = _mm_read_M_SLONG(reader); if (srclen <= 8 || srclen > 0x100000 || srclen > inlen-8) return 0; if (_mm_read_M_ULONG(reader) != 0x53515348) /* SQSH */ return 0; destlen = _mm_read_M_SLONG(reader); if (destlen < 0 || destlen > 0x200000) return 0; if ((src = (UBYTE*) MikMod_malloc(srclen + 3)) == NULL) return 0; if ((dest = (UBYTE*) MikMod_malloc(destlen + 100)) == NULL) { MikMod_free(src); return 0; } if (!_mm_read_UBYTES(src, srclen - 8, reader)) goto err; if (unsqsh(src, srclen, dest, destlen) != destlen) goto err; *out = dest; *outlen = destlen; MikMod_free(src); return 1; err: MikMod_free(dest); MikMod_free(src); return 0; } #endif /* NO_DEPACKERS */