gstreamer/gst/modplug/libmodplug/mmcmp.cpp

407 lines
9.2 KiB
C++
Raw Normal View History

/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
BOOL PP20_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength);
typedef struct MMCMPFILEHEADER
{
DWORD id_ziRC; // "ziRC"
DWORD id_ONia; // "ONia"
WORD hdrsize;
} MMCMPFILEHEADER, *LPMMCMPFILEHEADER;
typedef struct MMCMPHEADER
{
WORD version;
WORD nblocks;
DWORD filesize;
DWORD blktable;
BYTE glb_comp;
BYTE fmt_comp;
} MMCMPHEADER, *LPMMCMPHEADER;
typedef struct MMCMPBLOCK
{
DWORD unpk_size;
DWORD pk_size;
DWORD xor_chk;
WORD sub_blk;
WORD flags;
WORD tt_entries;
WORD num_bits;
} MMCMPBLOCK, *LPMMCMPBLOCK;
typedef struct MMCMPSUBBLOCK
{
DWORD unpk_pos;
DWORD unpk_size;
} MMCMPSUBBLOCK, *LPMMCMPSUBBLOCK;
#define MMCMP_COMP 0x0001
#define MMCMP_DELTA 0x0002
#define MMCMP_16BIT 0x0004
#define MMCMP_STEREO 0x0100
#define MMCMP_ABS16 0x0200
#define MMCMP_ENDIAN 0x0400
typedef struct MMCMPBITBUFFER
{
UINT bitcount;
DWORD bitbuffer;
LPCBYTE pSrc;
LPCBYTE pEnd;
DWORD GetBits(UINT nBits);
} MMCMPBITBUFFER;
DWORD MMCMPBITBUFFER::GetBits(UINT nBits)
//---------------------------------------
{
DWORD d;
if (!nBits) return 0;
while (bitcount < 24)
{
bitbuffer |= ((pSrc < pEnd) ? *pSrc++ : 0) << bitcount;
bitcount += 8;
}
d = bitbuffer & ((1 << nBits) - 1);
bitbuffer >>= nBits;
bitcount -= nBits;
return d;
}
//#define MMCMP_LOG
#ifdef MMCMP_LOG
extern void Log(LPCSTR s, ...);
#endif
const DWORD MMCMP8BitCommands[8] =
{
0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8
};
const UINT MMCMP8BitFetch[8] =
{
3, 3, 3, 3, 2, 1, 0, 0
};
const DWORD MMCMP16BitCommands[16] =
{
0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0
};
const UINT MMCMP16BitFetch[16] =
{
4, 4, 4, 4, 3, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
BOOL MMCMP_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength)
//---------------------------------------------------------
{
DWORD dwMemLength = *pdwMemLength;
LPCBYTE lpMemFile = *ppMemFile;
LPBYTE pBuffer;
LPMMCMPFILEHEADER pmfh = (LPMMCMPFILEHEADER)(lpMemFile);
LPMMCMPHEADER pmmh = (LPMMCMPHEADER)(lpMemFile+10);
LPDWORD pblk_table;
DWORD dwFileSize;
if (PP20_Unpack(ppMemFile, pdwMemLength))
{
return TRUE;
}
if ((dwMemLength < 256) || (!pmfh) || (pmfh->id_ziRC != 0x4352697A) || (pmfh->id_ONia != 0x61694e4f) || (pmfh->hdrsize < 14)
|| (!pmmh->nblocks) || (pmmh->filesize < 16) || (pmmh->filesize > 0x8000000)
|| (pmmh->blktable >= dwMemLength) || (pmmh->blktable + 4*pmmh->nblocks > dwMemLength)) return FALSE;
dwFileSize = pmmh->filesize;
if ((pBuffer = (LPBYTE)GlobalAllocPtr(GHND, (dwFileSize + 31) & ~15)) == NULL) return FALSE;
pblk_table = (LPDWORD)(lpMemFile+pmmh->blktable);
for (UINT nBlock=0; nBlock<pmmh->nblocks; nBlock++)
{
DWORD dwMemPos = pblk_table[nBlock];
LPMMCMPBLOCK pblk = (LPMMCMPBLOCK)(lpMemFile+dwMemPos);
LPMMCMPSUBBLOCK psubblk = (LPMMCMPSUBBLOCK)(lpMemFile+dwMemPos+20);
if ((dwMemPos + 20 >= dwMemLength) || (dwMemPos + 20 + pblk->sub_blk*8 >= dwMemLength)) break;
dwMemPos += 20 + pblk->sub_blk*8;
#ifdef MMCMP_LOG
Log("block %d: flags=%04X sub_blocks=%d", nBlock, (UINT)pblk->flags, (UINT)pblk->sub_blk);
Log(" pksize=%d unpksize=%d", pblk->pk_size, pblk->unpk_size);
Log(" tt_entries=%d num_bits=%d\n", pblk->tt_entries, pblk->num_bits);
#endif
// Data is not packed
if (!(pblk->flags & MMCMP_COMP))
{
for (UINT i=0; i<pblk->sub_blk; i++)
{
if ((psubblk->unpk_pos > dwFileSize) || (psubblk->unpk_pos + psubblk->unpk_size > dwFileSize)) break;
#ifdef MMCMP_LOG
Log(" Unpacked sub-block %d: offset %d, size=%d\n", i, psubblk->unpk_pos, psubblk->unpk_size);
#endif
memcpy(pBuffer+psubblk->unpk_pos, lpMemFile+dwMemPos, psubblk->unpk_size);
dwMemPos += psubblk->unpk_size;
psubblk++;
}
} else
// Data is 16-bit packed
if (pblk->flags & MMCMP_16BIT)
{
MMCMPBITBUFFER bb;
LPWORD pDest = (LPWORD)(pBuffer + psubblk->unpk_pos);
DWORD dwSize = psubblk->unpk_size >> 1;
DWORD dwPos = 0;
UINT numbits = pblk->num_bits;
UINT subblk = 0, oldval = 0;
#ifdef MMCMP_LOG
Log(" 16-bit block: pos=%d size=%d ", psubblk->unpk_pos, psubblk->unpk_size);
if (pblk->flags & MMCMP_DELTA) Log("DELTA ");
if (pblk->flags & MMCMP_ABS16) Log("ABS16 ");
Log("\n");
#endif
bb.bitcount = 0;
bb.bitbuffer = 0;
bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries;
bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size;
while (subblk < pblk->sub_blk)
{
UINT newval = 0x10000;
DWORD d = bb.GetBits(numbits+1);
if (d >= MMCMP16BitCommands[numbits])
{
UINT nFetch = MMCMP16BitFetch[numbits];
UINT newbits = bb.GetBits(nFetch) + ((d - MMCMP16BitCommands[numbits]) << nFetch);
if (newbits != numbits)
{
numbits = newbits & 0x0F;
} else
{
if ((d = bb.GetBits(4)) == 0x0F)
{
if (bb.GetBits(1)) break;
newval = 0xFFFF;
} else
{
newval = 0xFFF0 + d;
}
}
} else
{
newval = d;
}
if (newval < 0x10000)
{
newval = (newval & 1) ? (UINT)(-(LONG)((newval+1) >> 1)) : (UINT)(newval >> 1);
if (pblk->flags & MMCMP_DELTA)
{
newval += oldval;
oldval = newval;
} else
if (!(pblk->flags & MMCMP_ABS16))
{
newval ^= 0x8000;
}
pDest[dwPos++] = (WORD)newval;
}
if (dwPos >= dwSize)
{
subblk++;
dwPos = 0;
dwSize = psubblk[subblk].unpk_size >> 1;
pDest = (LPWORD)(pBuffer + psubblk[subblk].unpk_pos);
}
}
} else
// Data is 8-bit packed
{
MMCMPBITBUFFER bb;
LPBYTE pDest = pBuffer + psubblk->unpk_pos;
DWORD dwSize = psubblk->unpk_size;
DWORD dwPos = 0;
UINT numbits = pblk->num_bits;
UINT subblk = 0, oldval = 0;
LPCBYTE ptable = lpMemFile+dwMemPos;
bb.bitcount = 0;
bb.bitbuffer = 0;
bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries;
bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size;
while (subblk < pblk->sub_blk)
{
UINT newval = 0x100;
DWORD d = bb.GetBits(numbits+1);
if (d >= MMCMP8BitCommands[numbits])
{
UINT nFetch = MMCMP8BitFetch[numbits];
UINT newbits = bb.GetBits(nFetch) + ((d - MMCMP8BitCommands[numbits]) << nFetch);
if (newbits != numbits)
{
numbits = newbits & 0x07;
} else
{
if ((d = bb.GetBits(3)) == 7)
{
if (bb.GetBits(1)) break;
newval = 0xFF;
} else
{
newval = 0xF8 + d;
}
}
} else
{
newval = d;
}
if (newval < 0x100)
{
int n = ptable[newval];
if (pblk->flags & MMCMP_DELTA)
{
n += oldval;
oldval = n;
}
pDest[dwPos++] = (BYTE)n;
}
if (dwPos >= dwSize)
{
subblk++;
dwPos = 0;
dwSize = psubblk[subblk].unpk_size;
pDest = pBuffer + psubblk[subblk].unpk_pos;
}
}
}
}
*ppMemFile = pBuffer;
*pdwMemLength = dwFileSize;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
// PowerPack PP20 Unpacker
//
typedef struct _PPBITBUFFER
{
UINT bitcount;
ULONG bitbuffer;
LPCBYTE pStart;
LPCBYTE pSrc;
ULONG GetBits(UINT n);
} PPBITBUFFER;
ULONG PPBITBUFFER::GetBits(UINT n)
{
ULONG result = 0;
for (UINT i=0; i<n; i++)
{
if (!bitcount)
{
bitcount = 8;
if (pSrc != pStart) pSrc--;
bitbuffer = *pSrc;
}
result = (result<<1) | (bitbuffer&1);
bitbuffer >>= 1;
bitcount--;
}
return result;
}
VOID PP20_DoUnpack(const BYTE *pSrc, UINT nSrcLen, BYTE *pDst, UINT nDstLen)
{
PPBITBUFFER BitBuffer;
ULONG nBytesLeft;
BitBuffer.pStart = pSrc;
BitBuffer.pSrc = pSrc + nSrcLen - 4;
BitBuffer.bitbuffer = 0;
BitBuffer.bitcount = 0;
BitBuffer.GetBits(pSrc[nSrcLen-1]);
nBytesLeft = nDstLen;
while (nBytesLeft > 0)
{
if (!BitBuffer.GetBits(1))
{
UINT n = 1;
while (n < nBytesLeft)
{
UINT code = BitBuffer.GetBits(2);
n += code;
if (code != 3) break;
}
for (UINT i=0; i<n; i++)
{
pDst[--nBytesLeft] = (BYTE)BitBuffer.GetBits(8);
}
if (!nBytesLeft) break;
}
{
UINT n = BitBuffer.GetBits(2)+1;
UINT nbits = pSrc[n-1];
UINT nofs;
if (n==4)
{
nofs = BitBuffer.GetBits( (BitBuffer.GetBits(1)) ? nbits : 7 );
while (n < nBytesLeft)
{
UINT code = BitBuffer.GetBits(3);
n += code;
if (code != 7) break;
}
} else
{
nofs = BitBuffer.GetBits(nbits);
}
for (UINT i=0; i<=n; i++)
{
pDst[nBytesLeft-1] = (nBytesLeft+nofs < nDstLen) ? pDst[nBytesLeft+nofs] : 0;
if (!--nBytesLeft) break;
}
}
}
}
BOOL PP20_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength)
{
DWORD dwMemLength = *pdwMemLength;
LPCBYTE lpMemFile = *ppMemFile;
DWORD dwDstLen;
LPBYTE pBuffer;
if ((!lpMemFile) || (dwMemLength < 256) || (*(DWORD *)lpMemFile != 0x30325050)) return FALSE;
dwDstLen = (lpMemFile[dwMemLength-4]<<16) | (lpMemFile[dwMemLength-3]<<8) | (lpMemFile[dwMemLength-2]);
//Log("PP20 detected: Packed length=%d, Unpacked length=%d\n", dwMemLength, dwDstLen);
if ((dwDstLen < 512) || (dwDstLen > 0x400000) || (dwDstLen > 16*dwMemLength)) return FALSE;
if ((pBuffer = (LPBYTE)GlobalAllocPtr(GHND, (dwDstLen + 31) & ~15)) == NULL) return FALSE;
PP20_DoUnpack(lpMemFile+4, dwMemLength-4, pBuffer, dwDstLen);
*ppMemFile = pBuffer;
*pdwMemLength = dwDstLen;
return TRUE;
}