mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 13:45:34 +08:00
(Lz4Stream): new compression algorithm.
This commit is contained in:
parent
375bc352b0
commit
2dbc380b80
@ -102,6 +102,7 @@
|
||||
<Compile Include="Cmvs\ImagePB2.cs" />
|
||||
<Compile Include="Ellefin\ArcEPK.cs" />
|
||||
<Compile Include="Entis\ArcPAC.cs" />
|
||||
<Compile Include="Lz4Stream.cs" />
|
||||
<Compile Include="Majiro\ImageRC8.cs" />
|
||||
<Compile Include="NSystem\ArcFJSYS.cs" />
|
||||
<Compile Include="NSystem\ImageMGD.cs" />
|
||||
|
293
ArcFormats/Lz4Stream.cs
Normal file
293
ArcFormats/Lz4Stream.cs
Normal file
@ -0,0 +1,293 @@
|
||||
//! \file Lz4Stream.cs
|
||||
//! \date Fri Jan 13 20:45:26 2017
|
||||
//! \brief LZ4 - Fast LZ compression algorithm
|
||||
//
|
||||
// Copyright (C) 2011-2016, Yann Collet.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// C# implementation Copyright (C) 2017 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Compression
|
||||
{
|
||||
public class Lz4FrameInfo
|
||||
{
|
||||
public int BlockSize;
|
||||
public bool IndependentBlocks;
|
||||
public bool HasBlockChecksum;
|
||||
public bool HasContentLength;
|
||||
public bool HasContentChecksum;
|
||||
public bool HasDictionary;
|
||||
public long OriginalLength;
|
||||
public int DictionaryId;
|
||||
|
||||
public Lz4FrameInfo ()
|
||||
{
|
||||
}
|
||||
|
||||
public Lz4FrameInfo (byte flags)
|
||||
{
|
||||
int version = flags >> 6;
|
||||
if (version != 1)
|
||||
throw Lz4Stream.InvalidData();
|
||||
IndependentBlocks = 0 != (flags & 0x20);
|
||||
HasBlockChecksum = 0 != (flags & 0x10);
|
||||
HasContentLength = 0 != (flags & 4);
|
||||
HasContentChecksum = 0 != (flags & 3);
|
||||
HasDictionary = 0 != (flags & 1);
|
||||
}
|
||||
|
||||
public void SetBlockSize (int code)
|
||||
{
|
||||
switch ((code >> 4) & 7)
|
||||
{
|
||||
case 4: BlockSize = 0x10000; break;
|
||||
case 5: BlockSize = 0x40000; break;
|
||||
case 6: BlockSize = 0x100000; break;
|
||||
case 7: BlockSize = 0x400000; break;
|
||||
default: throw Lz4Stream.InvalidData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Lz4Stream : GameRes.Formats.InputProxyStream
|
||||
{
|
||||
Lz4FrameInfo m_info;
|
||||
readonly byte[] m_block_header;
|
||||
byte[] m_block;
|
||||
int m_block_size;
|
||||
byte[] m_data;
|
||||
int m_data_size;
|
||||
int m_data_pos;
|
||||
bool m_eof;
|
||||
|
||||
public Lz4Stream (Stream input, Lz4FrameInfo info, bool leave_open = false) : base (input, leave_open)
|
||||
{
|
||||
if (null == info)
|
||||
throw new ArgumentNullException ("info");
|
||||
if (info.BlockSize <= 0)
|
||||
throw new ArgumentOutOfRangeException ("info.BlockSize");
|
||||
if (!info.IndependentBlocks)
|
||||
throw new NotImplementedException ("LZ4 compression with linked blocks not implemented.");
|
||||
if (info.HasDictionary)
|
||||
throw new NotImplementedException ("LZ4 compression with dictionary not implemented.");
|
||||
m_info = info;
|
||||
m_block_header = new byte[4];
|
||||
m_data = new byte[m_info.BlockSize];
|
||||
m_data_size = 0;
|
||||
m_data_pos = 0;
|
||||
m_eof = false;
|
||||
}
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int count)
|
||||
{
|
||||
int total_read = 0;
|
||||
while (count > 0)
|
||||
{
|
||||
if (m_data_pos < m_data_size)
|
||||
{
|
||||
int available = Math.Min (m_data_size - m_data_pos, count);
|
||||
Buffer.BlockCopy (m_data, m_data_pos, buffer, offset, available);
|
||||
total_read += available;
|
||||
m_data_pos += available;
|
||||
offset += available;
|
||||
count -= available;
|
||||
}
|
||||
else if (m_eof)
|
||||
break;
|
||||
else
|
||||
ReadNextBlock();
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
|
||||
void ReadNextBlock ()
|
||||
{
|
||||
if (4 != BaseStream.Read (m_block_header, 0, 4))
|
||||
throw new EndOfStreamException();
|
||||
int block_size = LittleEndian.ToInt32 (m_block_header, 0);
|
||||
if (0 == block_size)
|
||||
{
|
||||
m_eof = true;
|
||||
m_data_size = 0;
|
||||
if (m_info.HasContentChecksum)
|
||||
ReadChecksum();
|
||||
}
|
||||
else if (block_size < 0)
|
||||
{
|
||||
m_data_size = block_size & 0x7FFFFFFF;
|
||||
if (m_data_size > m_data.Length)
|
||||
m_data = new byte[m_data_size];
|
||||
m_data_size = BaseStream.Read (m_data, 0, m_data_size);
|
||||
if (m_info.HasBlockChecksum)
|
||||
ReadChecksum();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_block_size = block_size;
|
||||
if (null == m_block || m_block_size > m_block.Length)
|
||||
m_block = new byte[m_block_size];
|
||||
if (m_block_size != BaseStream.Read (m_block, 0, m_block_size))
|
||||
throw new EndOfStreamException();
|
||||
m_data_size = DecompressBlock();
|
||||
if (m_info.HasBlockChecksum)
|
||||
ReadChecksum();
|
||||
}
|
||||
m_data_pos = 0;
|
||||
}
|
||||
|
||||
void ReadChecksum ()
|
||||
{
|
||||
if (4 != BaseStream.Read (m_block_header, 0, 4))
|
||||
throw new EndOfStreamException();
|
||||
// XXX checksum is ignored
|
||||
}
|
||||
|
||||
const int LastLiterals = 5;
|
||||
const int MFLimit = 12;
|
||||
|
||||
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 >> 4;
|
||||
if (0xF == length)
|
||||
{
|
||||
int n;
|
||||
do
|
||||
{
|
||||
n = m_block[src++];
|
||||
length += n;
|
||||
}
|
||||
while ((src < iend - 0xF) && (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 & 0xF;
|
||||
if (0xF == 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 += 4;
|
||||
|
||||
/* 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
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException ("Lz4Stream.Length property is not supported"); }
|
||||
}
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException ("Lz4Stream.Position property is not supported"); }
|
||||
set { throw new NotSupportedException ("Lz4Stream.Position property is not supported"); }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ("Lz4Stream.Seek method is not supported");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user