mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-27 07:34:00 +08:00
(LZ4): decompression routine moved to separate class.
This commit is contained in:
parent
90564a52a8
commit
e98a6a2fca
@ -73,7 +73,7 @@ namespace GameRes.Compression
|
|||||||
{
|
{
|
||||||
int version = flags >> 6;
|
int version = flags >> 6;
|
||||||
if (version != 1)
|
if (version != 1)
|
||||||
throw Lz4Stream.InvalidData();
|
throw Lz4Compressor.InvalidData();
|
||||||
IndependentBlocks = 0 != (flags & 0x20);
|
IndependentBlocks = 0 != (flags & 0x20);
|
||||||
HasBlockChecksum = 0 != (flags & 0x10);
|
HasBlockChecksum = 0 != (flags & 0x10);
|
||||||
HasContentLength = 0 != (flags & 8);
|
HasContentLength = 0 != (flags & 8);
|
||||||
@ -89,7 +89,7 @@ namespace GameRes.Compression
|
|||||||
case 5: BlockSize = 0x40000; break;
|
case 5: BlockSize = 0x40000; break;
|
||||||
case 6: BlockSize = 0x100000; break;
|
case 6: BlockSize = 0x100000; break;
|
||||||
case 7: BlockSize = 0x400000; break;
|
case 7: BlockSize = 0x400000; break;
|
||||||
default: throw Lz4Stream.InvalidData();
|
default: throw Lz4Compressor.InvalidData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ namespace GameRes.Compression
|
|||||||
m_block = new byte[m_block_size];
|
m_block = new byte[m_block_size];
|
||||||
if (m_block_size != BaseStream.Read (m_block, 0, m_block_size))
|
if (m_block_size != BaseStream.Read (m_block, 0, m_block_size))
|
||||||
throw new EndOfStreamException();
|
throw new EndOfStreamException();
|
||||||
m_data_size = DecompressBlock();
|
m_data_size = Lz4Compressor.DecompressBlock (m_block, m_block_size, m_data, m_data.Length);
|
||||||
if (m_info.HasBlockChecksum)
|
if (m_info.HasBlockChecksum)
|
||||||
ReadChecksum();
|
ReadChecksum();
|
||||||
}
|
}
|
||||||
@ -187,91 +187,6 @@ namespace GameRes.Compression
|
|||||||
// XXX checksum is ignored
|
// XXX checksum is ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
const int MinMatch = 4;
|
|
||||||
const int LastLiterals = 5;
|
|
||||||
const int MFLimit = 12;
|
|
||||||
const int MatchLengthBits = 4;
|
|
||||||
const int MatchLengthMask = 0xF;
|
|
||||||
const int RunMask = 0xF;
|
|
||||||
|
|
||||||
int DecompressBlock ()
|
|
||||||
{
|
|
||||||
int src = 0;
|
|
||||||
int iend = m_block_size;
|
|
||||||
|
|
||||||
int dst = 0;
|
|
||||||
int oend = m_data.Length;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
/* get literal length */
|
|
||||||
int token = m_block[src++];
|
|
||||||
int length = token >> MatchLengthBits;
|
|
||||||
if (RunMask == length)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
n = m_block[src++];
|
|
||||||
length += n;
|
|
||||||
}
|
|
||||||
while ((src < iend - RunMask) && (0xFF == n));
|
|
||||||
if (dst + length < dst || src + length < src) // overflow detection
|
|
||||||
throw InvalidData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy literals */
|
|
||||||
int copy_end = dst + length;
|
|
||||||
if ((copy_end > oend - MFLimit) || (src + length > iend - (3+LastLiterals)))
|
|
||||||
{
|
|
||||||
if ((src + length != iend) || copy_end > oend)
|
|
||||||
throw InvalidData();
|
|
||||||
Buffer.BlockCopy (m_block, src, m_data, dst, length);
|
|
||||||
src += length;
|
|
||||||
dst += length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Buffer.BlockCopy (m_block, src, m_data, dst, length);
|
|
||||||
src += length;
|
|
||||||
dst = copy_end;
|
|
||||||
|
|
||||||
/* get offset */
|
|
||||||
int offset = LittleEndian.ToUInt16 (m_block, src);
|
|
||||||
src += 2;
|
|
||||||
int match = dst - offset;
|
|
||||||
if (match < 0)
|
|
||||||
throw InvalidData();
|
|
||||||
|
|
||||||
/* get matchlength */
|
|
||||||
length = token & MatchLengthMask;
|
|
||||||
if (MatchLengthMask == length)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
n = m_block[src++];
|
|
||||||
if (src > iend - LastLiterals)
|
|
||||||
throw InvalidData();
|
|
||||||
length += n;
|
|
||||||
}
|
|
||||||
while (0xFF == n);
|
|
||||||
if (dst + length < dst) // overflow detection
|
|
||||||
throw InvalidData();
|
|
||||||
}
|
|
||||||
length += MinMatch;
|
|
||||||
|
|
||||||
/* copy match within block */
|
|
||||||
Binary.CopyOverlapped (m_data, match, dst, length);
|
|
||||||
dst += length;
|
|
||||||
}
|
|
||||||
return dst; // number of output bytes decoded
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static InvalidDataException InvalidData ()
|
|
||||||
{
|
|
||||||
return new InvalidDataException ("Invalid LZ4 compressed stream.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Not supported IO.Stream methods
|
#region Not supported IO.Stream methods
|
||||||
public override bool CanSeek { get { return false; } }
|
public override bool CanSeek { get { return false; } }
|
||||||
public override long Length
|
public override long Length
|
||||||
@ -294,4 +209,92 @@ namespace GameRes.Compression
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Lz4Compressor
|
||||||
|
{
|
||||||
|
const int MinMatch = 4;
|
||||||
|
const int LastLiterals = 5;
|
||||||
|
const int MFLimit = 12;
|
||||||
|
const int MatchLengthBits = 4;
|
||||||
|
const int MatchLengthMask = 0xF;
|
||||||
|
const int RunMask = 0xF;
|
||||||
|
|
||||||
|
public static int DecompressBlock (byte[] block, int block_size, byte[] output, int output_size)
|
||||||
|
{
|
||||||
|
int src = 0;
|
||||||
|
int iend = block_size;
|
||||||
|
|
||||||
|
int dst = 0;
|
||||||
|
int oend = output_size;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/* get literal length */
|
||||||
|
int token = block[src++];
|
||||||
|
int length = token >> MatchLengthBits;
|
||||||
|
if (RunMask == length)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n = block[src++];
|
||||||
|
length += n;
|
||||||
|
}
|
||||||
|
while ((src < iend - RunMask) && (0xFF == n));
|
||||||
|
if (dst + length < dst || src + length < src) // overflow detection
|
||||||
|
throw InvalidData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy literals */
|
||||||
|
int copy_end = dst + length;
|
||||||
|
if ((copy_end > oend - MFLimit) || (src + length > iend - (3+LastLiterals)))
|
||||||
|
{
|
||||||
|
if ((src + length != iend) || copy_end > oend)
|
||||||
|
throw InvalidData();
|
||||||
|
Buffer.BlockCopy (block, src, output, dst, length);
|
||||||
|
src += length;
|
||||||
|
dst += length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Buffer.BlockCopy (block, src, output, dst, length);
|
||||||
|
src += length;
|
||||||
|
dst = copy_end;
|
||||||
|
|
||||||
|
/* get offset */
|
||||||
|
int offset = LittleEndian.ToUInt16 (block, src);
|
||||||
|
src += 2;
|
||||||
|
int match = dst - offset;
|
||||||
|
if (match < 0)
|
||||||
|
throw InvalidData();
|
||||||
|
|
||||||
|
/* get matchlength */
|
||||||
|
length = token & MatchLengthMask;
|
||||||
|
if (MatchLengthMask == length)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n = block[src++];
|
||||||
|
if (src > iend - LastLiterals)
|
||||||
|
throw InvalidData();
|
||||||
|
length += n;
|
||||||
|
}
|
||||||
|
while (0xFF == n);
|
||||||
|
if (dst + length < dst) // overflow detection
|
||||||
|
throw InvalidData();
|
||||||
|
}
|
||||||
|
length += MinMatch;
|
||||||
|
|
||||||
|
/* copy match within block */
|
||||||
|
Binary.CopyOverlapped (output, match, dst, length);
|
||||||
|
dst += length;
|
||||||
|
}
|
||||||
|
return dst; // number of output bytes decoded
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static InvalidDataException InvalidData ()
|
||||||
|
{
|
||||||
|
return new InvalidDataException ("Invalid LZ4 compressed stream.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user