@@ -395,107 +395,147 @@ BOOL MMCMP_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength)
395395// PowerPack PP20 Unpacker
396396//
397397
398- typedef struct _PPBITBUFFER
399- {
400- UINT bitcount;
401- ULONG bitbuffer;
402- LPCBYTE pStart;
403- LPCBYTE pSrc;
404-
405- ULONG GetBits (UINT n);
406- } PPBITBUFFER;
407-
408-
409- ULONG PPBITBUFFER::GetBits (UINT n)
410- {
411- ULONG result = 0 ;
412-
413- for (UINT i=0 ; i<n; i++)
414- {
415- if (!bitcount)
416- {
417- bitcount = 8 ;
418- if (pSrc != pStart) pSrc--;
419- bitbuffer = *pSrc;
420- }
421- result = (result<<1 ) | (bitbuffer&1 );
422- bitbuffer >>= 1 ;
423- bitcount--;
424- }
425- return result;
426- }
427-
428-
429- static BOOL PP20_DoUnpack (const BYTE *pSrc, UINT nSrcLen, BYTE *pDst, UINT nDstLen)
398+ /* Code from Heikki Orsila's amigadepack 0.02
399+ * based on code by Stuart Caie <kyzer@4u.net>
400+ * This software is in the Public Domain
401+ *
402+ * Modified for xmp by Claudio Matsuoka, 08/2007
403+ * - merged mld's checks from the old depack sources. Original credits:
404+ * - corrupt file and data detection
405+ * (thanks to Don Adan and Dirk Stoecker for help and infos)
406+ * - implemeted "efficiency" checks
407+ * - further detection based on code by Georg Hoermann
408+ *
409+ * Modified for xmp by Claudio Matsuoka, 05/2013
410+ * - decryption code removed
411+ *
412+ * Modified for libmodplug by O. Sezer, Apr. 2015
413+ */
414+
415+ #define PP_READ_BITS (nbits, var ) do { \
416+ bit_cnt = (nbits); \
417+ while (bits_left < bit_cnt) { \
418+ if (buf_src < src) return 0 ; /* out of source bits */ \
419+ bit_buffer |= (*--buf_src << bits_left); \
420+ bits_left += 8 ; \
421+ } \
422+ (var) = 0 ; \
423+ bits_left -= bit_cnt; \
424+ while (bit_cnt--) { \
425+ (var) = ((var) << 1 ) | (bit_buffer & 1 ); \
426+ bit_buffer >>= 1 ; \
427+ } \
428+ } while (0 )
429+
430+ #define PP_BYTE_OUT (byte ) do { \
431+ if (out <= dest) return 0 ; /* output overflow */ \
432+ *--out = (byte); \
433+ written++; \
434+ } while (0 )
435+
436+ static BOOL ppDecrunch (LPCBYTE src, LPBYTE dest,
437+ LPCBYTE offset_lens,
438+ DWORD src_len, DWORD dest_len,
439+ BYTE skip_bits)
430440{
431- PPBITBUFFER BitBuffer;
432- ULONG nBytesLeft;
433-
434- BitBuffer.pStart = pSrc + 4 ;
435- BitBuffer.pSrc = pSrc + nSrcLen - 4 ;
436- BitBuffer.bitbuffer = 0 ;
437- BitBuffer.bitcount = 0 ;
438- BitBuffer.GetBits (pSrc[nSrcLen-1 ]);
439- nBytesLeft = nDstLen;
440- while (nBytesLeft > 0 )
441- {
442- if (!BitBuffer.GetBits (1 ))
443- {
444- UINT n = 1 ;
445- while (n < nBytesLeft)
446- {
447- UINT code = BitBuffer.GetBits (2 );
448- n += code;
449- if (code != 3 ) break ;
450- }
451- for (UINT i=0 ; i<n; i++)
452- {
453- pDst[--nBytesLeft] = (BYTE)BitBuffer.GetBits (8 );
454- }
455- if (!nBytesLeft) break ;
456- }
457- {
458- UINT n = BitBuffer.GetBits (2 )+1 ;
459- if (n < 1 || n-1 >= nSrcLen) return FALSE ; // can this ever happen?
460- UINT nbits = pSrc[n-1 ];
461- UINT nofs;
462- if (n==4 )
463- {
464- nofs = BitBuffer.GetBits ( (BitBuffer.GetBits (1 )) ? nbits : 7 );
465- while (n < nBytesLeft)
466- {
467- UINT code = BitBuffer.GetBits (3 );
468- n += code;
469- if (code != 7 ) break ;
470- }
471- } else
472- {
473- nofs = BitBuffer.GetBits (nbits);
474- }
475- for (UINT i=0 ; i<=n; i++)
476- {
477- pDst[nBytesLeft-1 ] = (nBytesLeft+nofs < nDstLen) ? pDst[nBytesLeft+nofs] : 0 ;
478- if (!--nBytesLeft) break ;
479- }
480- }
481- }
482- return TRUE ;
441+ DWORD bit_buffer, x, todo, offbits, offset, written;
442+ LPCBYTE buf_src;
443+ LPBYTE out, dest_end;
444+ BYTE bits_left, bit_cnt;
445+
446+ /* set up input and output pointers */
447+ buf_src = src + src_len;
448+ out = dest_end = dest + dest_len;
449+
450+ written = 0 ;
451+ bit_buffer = 0 ;
452+ bits_left = 0 ;
453+
454+ /* skip the first few bits */
455+ PP_READ_BITS (skip_bits, x);
456+
457+ /* while there are input bits left */
458+ while (written < dest_len) {
459+ PP_READ_BITS (1 , x);
460+ if (x == 0 ) {
461+ /* 1bit==0: literal, then match. 1bit==1: just match */
462+ todo = 1 ; do { PP_READ_BITS (2 , x); todo += x; } while (x == 3 );
463+ while (todo--) { PP_READ_BITS (8 , x); PP_BYTE_OUT (x); }
464+
465+ /* should we end decoding on a literal, break out of the main loop */
466+ if (written == dest_len) break ;
467+ }
468+
469+ /* match: read 2 bits for initial offset bitlength / match length */
470+ PP_READ_BITS (2 , x);
471+ offbits = offset_lens[x];
472+ todo = x+2 ;
473+ if (x == 3 ) {
474+ PP_READ_BITS (1 , x);
475+ if (x==0 ) offbits = 7 ;
476+ PP_READ_BITS (offbits, offset);
477+ do { PP_READ_BITS (3 , x); todo += x; } while (x == 7 );
478+ }
479+ else {
480+ PP_READ_BITS (offbits, offset);
481+ }
482+ if ((out + offset) >= dest_end) return 0 ; /* match overflow */
483+ while (todo--) { x = out[offset]; PP_BYTE_OUT (x); }
484+ }
485+
486+ /* all output bytes written without error */
487+ return 1 ;
488+ /* return (src == buf_src) ? 1 : 0; */
483489}
484490
485-
486491BOOL PP20_Unpack (LPCBYTE *ppMemFile, LPDWORD pdwMemLength)
487492{
488493 DWORD dwMemLength = *pdwMemLength;
489494 LPCBYTE lpMemFile = *ppMemFile;
490495 DWORD dwDstLen;
496+ BYTE tmp[4 ], skip;
491497 LPBYTE pBuffer;
492498
493- if ((!lpMemFile) || (dwMemLength < 256 ) || (memcmp (lpMemFile," PP20" ,4 ) != 0 )) return FALSE ;
494- dwDstLen = (lpMemFile[dwMemLength-4 ]<<16 ) | (lpMemFile[dwMemLength-3 ]<<8 ) | (lpMemFile[dwMemLength-2 ]);
499+ if ((!lpMemFile) || (dwMemLength < 256 ) || (memcmp (lpMemFile," PP20" ,4 ) != 0 ))
500+ return FALSE ;
501+ if (dwMemLength & 3 ) /* file length should be a multiple of 4 */
502+ return FALSE ;
503+
504+ /* PP FORMAT:
505+ * 1 longword identifier 'PP20' or 'PX20'
506+ * [1 word checksum (if 'PX20') $ssss]
507+ * 1 longword efficiency $eeeeeeee
508+ * X longwords crunched file $cccccccc,$cccccccc,...
509+ * 1 longword decrunch info 'decrlen' << 8 | '8 bits other info'
510+ */
511+
512+ memcpy (tmp,&lpMemFile[dwMemLength-4 ],4 );
513+ dwDstLen = (tmp[0 ]<<16 ) | (tmp[1 ]<<8 ) | tmp[2 ];
514+ skip = tmp[3 ];
515+
516+ /* original pp20 only support efficiency
517+ * from 9 9 9 9 up to 9 10 12 13, afaik,
518+ * but the xfd detection code says this...
519+ *
520+ * move.l 4(a0),d0
521+ * cmp.b #9,d0
522+ * blo.b .Exit
523+ * and.l #$f0f0f0f0,d0
524+ * bne.s .Exit
525+ */
526+ memcpy (tmp,&lpMemFile[4 ],4 );
527+ if ((tmp[0 ] < 9 ) || (tmp[0 ] & 0xf0 )) return FALSE ;
528+ if ((tmp[1 ] < 9 ) || (tmp[1 ] & 0xf0 )) return FALSE ;
529+ if ((tmp[2 ] < 9 ) || (tmp[2 ] & 0xf0 )) return FALSE ;
530+ if ((tmp[3 ] < 9 ) || (tmp[3 ] & 0xf0 )) return FALSE ;
531+
495532 // Log("PP20 detected: Packed length=%d, Unpacked length=%d\n", dwMemLength, dwDstLen);
496- if ((dwDstLen < 512 ) || (dwDstLen > 0x400000 ) || (dwDstLen > 16 *dwMemLength)) return FALSE ;
497- if ((pBuffer = (LPBYTE)GlobalAllocPtr (GHND, (dwDstLen + 31 ) & ~15 )) == NULL ) return FALSE ;
498- if (!PP20_DoUnpack (lpMemFile+4 , dwMemLength-4 , pBuffer, dwDstLen)) {
533+ if ((dwDstLen < 512 ) || (dwDstLen > 0x400000 ) || (dwDstLen > 16 *dwMemLength))
534+ return FALSE ;
535+ if ((pBuffer = (LPBYTE)GlobalAllocPtr (GHND, (dwDstLen + 31 ) & ~15 )) == NULL )
536+ return FALSE ;
537+
538+ if (!ppDecrunch (lpMemFile+8 , pBuffer, tmp, dwMemLength-12 , dwDstLen, skip)) {
499539 free (pBuffer);
500540 return FALSE ;
501541 }
0 commit comments