Skip to content

Commit 30ef205

Browse files
committed
replaced the PP20 depacker with ppdepack from libxmp:
which is originally authored by Stuart Caie and is in the public domain. the old depacker badly decompressed a bunch of files I tested, e.g. mod.Another_Day had 22 bytes of garbage at its beginning. New code works just fine.
1 parent 6fef085 commit 30ef205

File tree

1 file changed

+130
-90
lines changed

1 file changed

+130
-90
lines changed

src/mmcmp.cpp

Lines changed: 130 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
486491
BOOL 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

Comments
 (0)