initial import
[dosrtxon] / libs / mikmod / depackers / s404.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 /*
21  * StoneCracker S404 algorithm data decompression routine
22  * (c) 2006 Jouni 'Mr.Spiv' Korhonen. The code is in public domain.
23  *
24  * modified for xmp by Claudio Matsuoka, Jan 2010
25  * modified for libmikmod by O. Sezer -- May 2015
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #include <stdio.h>
37 #ifdef HAVE_MEMORY_H
38 #include <memory.h>
39 #endif
40 #include <string.h>
41 /*#include <assert.h>*/
42
43 #include "mikmod_internals.h"
44
45 #ifdef SUNOS
46 extern int fprintf(FILE *, const char *, ...);
47 #endif
48
49
50 /*#define STC_DEBUG*/
51
52 static UWORD readmem16b(UBYTE *m)
53 {
54   ULONG a, b;
55   a = m[0];
56   b = m[1];
57   return (a << 8) | b;
58 }
59
60 struct bitstream {
61   /* bit buffer for rolling data bit by bit from the compressed file */
62   ULONG word;
63
64   /* bits left in the bit buffer */
65   int left;
66
67   /* compressed data source */
68   UWORD *src;
69   UBYTE *orgsrc;
70 };
71
72 static int initGetb(struct bitstream *bs, UBYTE *src, ULONG src_length)
73 {
74   int eff;
75
76   bs->src = (UWORD *) (src + src_length);
77   bs->orgsrc = src;
78
79   bs->left = readmem16b((UBYTE *)bs->src); /* bit counter */
80 #ifdef STC_DEBUG
81   if (bs->left & (~0xf))
82     fprintf(stderr, "Worked around an ancient stc bug\n");
83 #endif
84   /* mask off any corrupt bits */
85   bs->left &= 0x000f;
86   bs->src--;
87
88   /* get the first 16-bits of the compressed stream */
89   bs->word = readmem16b((UBYTE *)bs->src);
90   bs->src--;
91
92   eff = readmem16b((UBYTE *)bs->src); /* efficiency */
93   bs->src--;
94
95   return eff;
96 }
97
98 /* get nbits from the compressed stream */
99 static int getb(struct bitstream *bs, int nbits)
100 {
101   bs->word &= 0x0000ffff;
102
103   /* If not enough bits in the bit buffer, get more */
104   if (bs->left < nbits) {
105     bs->word <<= bs->left;
106     /*assert((bs->word & 0x0000ffffU) == 0);*/
107
108     /* Check that we don't go out of bounds */
109     if (bs->orgsrc > (UBYTE *)bs->src) {
110        return -1;
111     }
112
113     bs->word |= readmem16b((UBYTE *)bs->src);
114     bs->src--;
115
116     nbits -= bs->left;
117     bs->left = 16; /* 16 unused (and some used) bits left in the word */
118   }
119
120   /* Shift nbits off the word and return them */
121   bs->left -= nbits;
122   bs->word <<= nbits;
123   return bs->word >> 16;
124 }
125
126 static int decompressS404(UBYTE *src, UBYTE *orgdst,
127                           SLONG dst_length, SLONG src_length)
128 {
129   UWORD w;
130   SLONG eff;
131   SLONG n;
132   UBYTE *dst;
133   SLONG oLen = dst_length;
134   struct bitstream bs;
135   int x;
136
137   dst = orgdst + oLen;
138
139   eff = initGetb(&bs, src, src_length);
140
141   while (oLen > 0) {
142     x = getb(&bs, 9);
143     /* Sanity check */
144     if (x < 0) {
145       return -1;
146     }
147
148     w = x;
149
150     if (w < 0x100) {
151       if (orgdst >= dst) {
152         return -1;
153       }
154       *--dst = w;
155       oLen--;
156     } else if (w == 0x13e || w == 0x13f) {
157       w <<= 4;
158       x = getb(&bs, 4);
159       /* Sanity check */
160       if (x < 0) {
161         return -1;
162       }
163       w |= x;
164
165       n = (w & 0x1f) + 14;
166       oLen -= n;
167       while (n-- > 0) {
168         x = getb(&bs, 8);
169         /* Sanity check */
170         if (x < 0) {
171           return -1;
172         }
173         w = x;
174
175         if (orgdst >= dst) {
176           return -1;
177         }
178
179         *--dst = w;
180       }
181     } else {
182       if (w >= 0x180) {
183         /* copy 2-3 */
184         n = w & 0x40 ? 3 : 2;
185
186         if (w & 0x20) {
187           /* dist 545 -> */
188           w = (w & 0x1f) << (eff - 5);
189           x = getb(&bs, eff - 5);
190           /* Sanity check */
191           if (x < 0) {
192             return -1;
193           }
194           w |= x;
195           w += 544;
196         } else if (w & 0x30) {
197           /* dist 1 -> 32 */
198           w = (w & 0x0f) << 1;
199           x = getb(&bs, 1);
200           /* Sanity check */
201           if (x < 0) {
202             return -1;
203           }
204           w |= x;
205         } else {
206           /* dist 33 -> 544 */
207           w = (w & 0x0f) << 5;
208           x = getb(&bs, 5);
209           /* Sanity check */
210           if (x < 0) {
211             return -1;
212           }
213           w |= x;
214           w += 32;
215         }
216       } else if (w >= 0x140) {
217         /* copy 4-7 */
218         n = ((w & 0x30) >> 4) + 4;
219
220         if (w & 0x08) {
221           /* dist 545 -> */
222           w = (w & 0x07) << (eff - 3);
223           x = getb(&bs, eff - 3);
224           /* Sanity check */
225           if (x < 0) {
226             return -1;
227           }
228           w |= x;
229           w += 544;
230         } else if (w & 0x0c) {
231           /* dist 1 -> 32 */
232           w = (w & 0x03) << 3;
233           x = getb(&bs, 3);
234           /* Sanity check */
235           if (x < 0) {
236             return -1;
237           }
238           w |= x;
239         } else {
240           /* dist 33 -> 544 */
241           w = (w & 0x03) << 7;
242           x = getb(&bs, 7);
243           /* Sanity check */
244           if (x < 0) {
245             return -1;
246           }
247           w |= x;
248           w += 32;
249         }
250       } else if (w >= 0x120) {
251         /* copy 8-22 */
252         n = ((w & 0x1e) >> 1) + 8;
253
254         if (w & 0x01) {
255           /* dist 545 -> */
256           x = getb(&bs, eff);
257           /* Sanity check */
258           if (x < 0) {
259             return -1;
260           }
261           w = x;
262           w += 544;
263         } else {
264           x = getb(&bs, 6);
265           /* Sanity check */
266           if (x < 0) {
267             return -1;
268           }
269           w = x;
270
271           if (w & 0x20) {
272             /* dist 1 -> 32 */
273             w &= 0x1f;
274           } else {
275             /* dist 33 -> 544 */
276             w <<= 4;
277             x = getb(&bs, 4);
278             /* Sanity check */
279             if (x < 0) {
280               return -1;
281             }
282             w |= x;
283             w += 32;
284           }
285         }
286       } else {
287         w = (w & 0x1f) << 3;
288         x = getb(&bs, 3);
289         /* Sanity check */
290         if (x < 0) {
291           return -1;
292         }
293         w |= x;
294         n = 23;
295
296         while (w == 0xff) {
297           n += w;
298           x = getb(&bs, 8);
299           /* Sanity check */
300           if (x < 0) {
301             return -1;
302           }
303           w = x;
304         }
305         n += w;
306
307         x = getb(&bs, 7);
308         w = x;
309
310         if (w & 0x40) {
311           /* dist 545 -> */
312           w = (w & 0x3f) << (eff - 6);
313           x = getb(&bs, eff - 6);
314           /* Sanity check */
315           if (x < 0) {
316             return -1;
317           }
318           w |= x;
319           w += 544;
320         } else if (w & 0x20) {
321           /* dist 1 -> 32 */
322           w &= 0x1f;
323         } else {
324           /* dist 33 -> 544; */
325           w <<= 4;
326           x = getb(&bs, 4);
327           /* Sanity check */
328           if (x < 0) {
329             return -1;
330           }
331           w |= x;
332           w += 32;
333         }
334       }
335
336       oLen -= n;
337
338       while (n-- > 0) {
339         dst--;
340         if (dst < orgdst || (dst + w + 1) >= (orgdst + dst_length))
341             return -1;
342         *dst = dst[w + 1];
343       }
344     }
345   }
346
347   return 0;
348 }
349
350 BOOL S404_Unpack(MREADER *reader, void **out, long *outlen)
351 {
352         SLONG iLen, sLen, oLen, pLen;
353         UBYTE *src, *dst = NULL;
354         int err;
355
356         _mm_fseek(reader,0,SEEK_END);
357         iLen = _mm_ftell(reader);
358         if (iLen <= 16) return 0;
359
360         _mm_rewind(reader);
361         if (_mm_read_M_ULONG(reader) != 0x53343034) /* S404 */
362                 return 0;
363
364         sLen = _mm_read_M_SLONG(reader); /* Security length */
365         oLen = _mm_read_M_SLONG(reader); /* Depacked length */
366         pLen = _mm_read_M_SLONG(reader); /* Packed length */
367 #ifdef STC_DEBUG
368         fprintf(stderr,"S404: iLen= %d, sLen= %d, pLen= %d, oLen= %d\n",
369                 iLen, sLen, pLen, oLen);
370 #endif
371         if (sLen < 0 || oLen <= 0 || pLen <= 0) return 0;
372         if (pLen + 16 >= iLen) return 0; /* sanity check */
373
374         if (!(src = (UBYTE*) MikMod_malloc(iLen - 16)))
375                 return 0;
376         if (!(dst = (UBYTE*) MikMod_malloc(oLen))) {
377                 MikMod_free(src);
378                 return 0;
379         }
380
381         _mm_read_UBYTES(src, iLen - 16, reader);
382         err = decompressS404(src, dst, oLen, pLen);
383         MikMod_free(src);
384
385         if (!err) {
386                 *out = dst;
387                 *outlen = oLen;
388                 return 1;
389         }
390
391         MikMod_free(dst);
392         return 0;
393 }