/* 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. */ /* amiga Powerpack PP20 decompression support * * Code from Heikki Orsila's amigadepack 0.02 * based on code by Stuart Caie * This software is in the Public Domain * * Modified for xmp by Claudio Matsuoka, 08/2007 * - merged mld's checks from the old depack sources. Original credits: * - corrupt file and data detection * (thanks to Don Adan and Dirk Stoecker for help and infos) * - implemeted "efficiency" checks * - further detection based on code by Georg Hoermann * * Modified for xmp by Claudio Matsuoka, 05/2013 * - decryption code removed * * Modified for libmikmod by O. Sezer, Apr. 2015, with a few extra bits * from the libmodplug library by Olivier Lapicque. */ #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 #define PP_READ_BITS(nbits, var) do { \ bit_cnt = (nbits); \ while (bits_left < bit_cnt) { \ if (buf_src <= src) return 0; /* out of source bits */ \ bit_buffer |= (*--buf_src << bits_left); \ bits_left += 8; \ } \ (var) = 0; \ bits_left -= bit_cnt; \ while (bit_cnt--) { \ (var) = ((var) << 1) | (bit_buffer & 1); \ bit_buffer >>= 1; \ } \ } while(0) #define PP_BYTE_OUT(byte) do { \ if (out <= dest) return 0; /* output overflow */ \ *--out = (byte); \ written++; \ } while (0) static BOOL ppDecrunch(const UBYTE *src, UBYTE *dest, const UBYTE *offset_lens, ULONG src_len, ULONG dest_len, UBYTE skip_bits) { ULONG bit_buffer, x, todo, offbits, offset, written; const UBYTE *buf_src; UBYTE *out, *dest_end, bits_left, bit_cnt; /* set up input and output pointers */ buf_src = src + src_len; out = dest_end = dest + dest_len; written = 0; bit_buffer = 0; bits_left = 0; /* skip the first few bits */ PP_READ_BITS(skip_bits, x); /* while there are input bits left */ while (written < dest_len) { PP_READ_BITS(1, x); if (x == 0) { /* 1bit==0: literal, then match. 1bit==1: just match */ todo = 1; do { PP_READ_BITS(2, x); todo += x; } while (x == 3); while (todo--) { PP_READ_BITS(8, x); PP_BYTE_OUT(x); } /* should we end decoding on a literal, break out of the main loop */ if (written == dest_len) break; } /* match: read 2 bits for initial offset bitlength / match length */ PP_READ_BITS(2, x); offbits = offset_lens[x]; todo = x+2; if (x == 3) { PP_READ_BITS(1, x); if (x==0) offbits = 7; PP_READ_BITS(offbits, offset); do { PP_READ_BITS(3, x); todo += x; } while (x == 7); } else { PP_READ_BITS(offbits, offset); } if ((out + offset) >= dest_end) return 0; /* match overflow */ while (todo--) { x = out[offset]; PP_BYTE_OUT(x); } } /* all output bytes written without error */ return 1; /* return (src == buf_src) ? 1 : 0; */ } BOOL PP20_Unpack(MREADER* reader, void** out, long* outlen) { ULONG srclen, destlen; UBYTE *destbuf, *srcbuf; UBYTE tmp[4], skip; BOOL ret; /* PP FORMAT: * 1 longword identifier 'PP20' or 'PX20' * [1 word checksum (if 'PX20') $ssss] * 1 longword efficiency $eeeeeeee * X longwords crunched file $cccccccc,$cccccccc,... * 1 longword decrunch info 'decrlen' << 8 | '8 bits other info' */ _mm_fseek(reader,0,SEEK_END); srclen = _mm_ftell(reader); if (srclen < 256) return 0; /* file length should be a multiple of 4 */ if (srclen & 3) return 0; _mm_rewind(reader); if (_mm_read_I_ULONG(reader) != 0x30325050) /* 'PP20' */ return 0; _mm_fseek(reader,srclen-4,SEEK_SET); _mm_read_UBYTES(tmp,4,reader); destlen = tmp[0] << 16; destlen |= tmp[1] << 8; destlen |= tmp[2]; skip = tmp[3]; _mm_fseek(reader,4,SEEK_SET); _mm_read_UBYTES(tmp,4,reader); /* original pp20 only support efficiency * from 9 9 9 9 up to 9 10 12 13, afaik, * but the xfd detection code says this... * * move.l 4(a0),d0 * cmp.b #9,d0 * blo.b .Exit * and.l #$f0f0f0f0,d0 * bne.s .Exit */ if ((tmp[0] < 9) || (tmp[0] & 0xf0)) return 0; if ((tmp[1] < 9) || (tmp[1] & 0xf0)) return 0; if ((tmp[2] < 9) || (tmp[2] & 0xf0)) return 0; if ((tmp[3] < 9) || (tmp[3] & 0xf0)) return 0; if ((destlen < 512) || (destlen > 0x400000) || (destlen > 16*srclen)) return 0; if ((destbuf = (UBYTE*)MikMod_malloc(destlen)) == NULL) return 0; srclen -= 12; if ((srcbuf = (UBYTE*)MikMod_malloc(srclen)) == NULL) { MikMod_free(destbuf); return 0; } _mm_read_UBYTES(srcbuf,srclen,reader); ret = ppDecrunch(srcbuf, destbuf, tmp, srclen, destlen, skip); MikMod_free(srcbuf); if (!ret) { MikMod_free(destbuf); } else { *out = destbuf; *outlen = destlen; } return ret; } #endif /* NO_DEPACKERS */