initial import
[dosrtxon] / libs / mikmod / depackers / pp20.c
1 /* MikMod sound library (c) 2003-2015 Raphael Assenat and others -
2  * see AUTHORS file for a complete list.
3  *
4  * This library is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Library General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17  * 02111-1307, USA.
18  */
19
20 /* amiga Powerpack PP20 decompression support
21  *
22  * Code from Heikki Orsila's amigadepack 0.02
23  * based on code by Stuart Caie <kyzer@4u.net>
24  * This software is in the Public Domain
25  *
26  * Modified for xmp by Claudio Matsuoka, 08/2007
27  * - merged mld's checks from the old depack sources. Original credits:
28  *   - corrupt file and data detection
29  *     (thanks to Don Adan and Dirk Stoecker for help and infos)
30  *   - implemeted "efficiency" checks
31  *   - further detection based on code by Georg Hoermann
32  *
33  * Modified for xmp by Claudio Matsuoka, 05/2013
34  * - decryption code removed
35  *
36  * Modified for libmikmod by O. Sezer, Apr. 2015, with a few extra bits
37  * from the libmodplug library by Olivier Lapicque.
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47
48 #include <stdio.h>
49 #ifdef HAVE_MEMORY_H
50 #include <memory.h>
51 #endif
52 #include <string.h>
53
54 #include "mikmod_internals.h"
55
56 #ifdef SUNOS
57 extern int fprintf(FILE *, const char *, ...);
58 #endif
59
60 #define PP_READ_BITS(nbits, var) do {                          \
61   bit_cnt = (nbits);                                           \
62   while (bits_left < bit_cnt) {                                \
63     if (buf_src < src) return 0; /* out of source bits */      \
64     bit_buffer |= (*--buf_src << bits_left);                   \
65     bits_left += 8;                                            \
66   }                                                            \
67   (var) = 0;                                                   \
68   bits_left -= bit_cnt;                                        \
69   while (bit_cnt--) {                                          \
70     (var) = ((var) << 1) | (bit_buffer & 1);                   \
71     bit_buffer >>= 1;                                          \
72   }                                                            \
73 } while(0)
74
75 #define PP_BYTE_OUT(byte) do {                                 \
76   if (out <= dest) return 0; /* output overflow */             \
77   *--out = (byte);                                             \
78   written++;                                                   \
79 } while (0)
80
81 static BOOL ppDecrunch(const UBYTE *src, UBYTE *dest,
82                        const UBYTE *offset_lens,
83                        ULONG src_len, ULONG dest_len,
84                        UBYTE skip_bits)
85 {
86   ULONG bit_buffer, x, todo, offbits, offset, written;
87   const UBYTE *buf_src;
88   UBYTE *out, *dest_end, bits_left, bit_cnt;
89
90   /* set up input and output pointers */
91   buf_src = src + src_len;
92   out = dest_end = dest + dest_len;
93
94   written = 0;
95   bit_buffer = 0;
96   bits_left = 0;
97
98   /* skip the first few bits */
99   PP_READ_BITS(skip_bits, x);
100
101   /* while there are input bits left */
102   while (written < dest_len) {
103     PP_READ_BITS(1, x);
104     if (x == 0) {
105       /* 1bit==0: literal, then match. 1bit==1: just match */
106       todo = 1; do { PP_READ_BITS(2, x); todo += x; } while (x == 3);
107       while (todo--) { PP_READ_BITS(8, x); PP_BYTE_OUT(x); }
108
109       /* should we end decoding on a literal, break out of the main loop */
110       if (written == dest_len) break;
111     }
112
113     /* match: read 2 bits for initial offset bitlength / match length */
114     PP_READ_BITS(2, x);
115     offbits = offset_lens[x];
116     todo = x+2;
117     if (x == 3) {
118       PP_READ_BITS(1, x);
119       if (x==0) offbits = 7;
120       PP_READ_BITS(offbits, offset);
121       do { PP_READ_BITS(3, x); todo += x; } while (x == 7);
122     }
123     else {
124       PP_READ_BITS(offbits, offset);
125     }
126     if ((out + offset) >= dest_end) return 0; /* match overflow */
127     while (todo--) { x = out[offset]; PP_BYTE_OUT(x); }
128   }
129
130   /* all output bytes written without error */
131   return 1;
132   /* return (src == buf_src) ? 1 : 0; */
133 }
134
135 BOOL PP20_Unpack(MREADER* reader, void** out, long* outlen)
136 {
137         ULONG srclen, destlen;
138         UBYTE *destbuf, *srcbuf;
139         UBYTE tmp[4], skip;
140         BOOL ret;
141
142         /* PP FORMAT:
143          *  1 longword identifier       'PP20' or 'PX20'
144          * [1 word checksum (if 'PX20') $ssss]
145          *  1 longword efficiency       $eeeeeeee
146          *  X longwords crunched file   $cccccccc,$cccccccc,...
147          *  1 longword decrunch info    'decrlen' << 8 | '8 bits other info'
148          */
149
150         _mm_fseek(reader,0,SEEK_END);
151         srclen = _mm_ftell(reader);
152         if (srclen < 256) return 0;
153         /* file length should be a multiple of 4 */
154         if (srclen & 3) return 0;
155
156         _mm_rewind(reader);
157         if (_mm_read_I_ULONG(reader) != 0x30325050)     /* 'PP20' */
158                 return 0;
159
160         _mm_fseek(reader,srclen-4,SEEK_SET);
161         _mm_read_UBYTES(tmp,4,reader);
162         destlen = tmp[0] << 16;
163         destlen |= tmp[1] << 8;
164         destlen |= tmp[2];
165         skip = tmp[3];
166
167         _mm_fseek(reader,4,SEEK_SET);
168         _mm_read_UBYTES(tmp,4,reader);
169
170         /* original pp20 only support efficiency
171          * from 9 9 9 9 up to 9 10 12 13, afaik,
172          * but the xfd detection code says this...
173          *
174          * move.l 4(a0),d0
175          * cmp.b #9,d0
176          * blo.b .Exit
177          * and.l #$f0f0f0f0,d0
178          * bne.s .Exit
179          */
180         if ((tmp[0] < 9) || (tmp[0] & 0xf0)) return 0;
181         if ((tmp[1] < 9) || (tmp[1] & 0xf0)) return 0;
182         if ((tmp[2] < 9) || (tmp[2] & 0xf0)) return 0;
183         if ((tmp[3] < 9) || (tmp[3] & 0xf0)) return 0;
184
185         if ((destlen < 512) || (destlen > 0x400000) || (destlen > 16*srclen))
186                 return 0;
187         if ((destbuf = (UBYTE*)MikMod_malloc(destlen)) == NULL)
188                 return 0;
189
190         srclen -= 12;
191         if ((srcbuf = (UBYTE*)MikMod_malloc(srclen)) == NULL) {
192                 MikMod_free(destbuf);
193                 return 0;
194         }
195         _mm_read_UBYTES(srcbuf,srclen,reader);
196
197         ret = ppDecrunch(srcbuf, destbuf, tmp, srclen, destlen, skip);
198         MikMod_free(srcbuf);
199
200         if (!ret) {
201                 MikMod_free(destbuf);
202         }
203         else {
204                 *out = destbuf;
205                 *outlen = destlen;
206         }
207         return ret;
208 }