(DscDecoder): derive from MsbBitStream class.

This commit is contained in:
morkt 2015-08-27 03:12:42 +04:00
parent 29b0658460
commit d67b4b05fa

View File

@ -2,7 +2,7 @@
//! \date Tue Sep 09 09:29:12 2014 //! \date Tue Sep 09 09:29:12 2014
//! \brief BGI/Ethornell engine archive implementation. //! \brief BGI/Ethornell engine archive implementation.
// //
// Copyright (C) 2014 by morkt // Copyright (C) 2014-2015 by morkt
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to
@ -111,36 +111,76 @@ namespace GameRes.Formats.BGI
} }
return new ArcView.ArcStream (input); return new ArcView.ArcStream (input);
} }
catch catch (Exception X)
{ {
input.Dispose(); System.Diagnostics.Trace.WriteLine (X.Message, "BgiOpener");
throw; return arc.File.CreateStream (entry.Offset, entry.Size);
} }
} }
} }
internal sealed class DscDecoder : IDisposable internal class BgiDecoderBase : MsbBitStream
{ {
private Stream m_input; protected uint m_key;
private byte[] m_output; protected uint m_magic;
private uint m_key; protected BgiDecoderBase (Stream input, bool leave_open = false) : base (input, leave_open)
private uint m_dst_size; {
private uint m_dec_count; }
private uint m_magic;
protected byte UpdateKey ()
{
uint v0 = 20021 * (m_key & 0xffff);
uint v1 = m_magic | (m_key >> 16);
v1 = v1 * 20021 + m_key * 346;
v1 = (v1 + (v0 >> 16)) & 0xffff;
m_key = (v1 << 16) + (v0 & 0xffff) + 1;
return (byte)v1;
}
}
internal sealed class DscDecoder : BgiDecoderBase
{
byte[] m_output;
uint m_dst_size;
uint m_dec_count;
public byte[] Output { get { return m_output; } } public byte[] Output { get { return m_output; } }
public DscDecoder (ArcView.Frame input) public DscDecoder (ArcView.Frame input)
: base (new ArcView.ArcStream (input, input.Offset+0x20, input.Reserved-0x20))
{ {
m_magic = (uint)input.ReadUInt16 (input.Offset) << 16; m_magic = (uint)input.ReadUInt16 (input.Offset) << 16;
m_key = input.ReadUInt32 (input.Offset+0x10); m_key = input.ReadUInt32 (input.Offset+0x10);
m_dst_size = input.ReadUInt32 (input.Offset+0x14); m_dst_size = input.ReadUInt32 (input.Offset+0x14);
m_dec_count = input.ReadUInt32 (input.Offset+0x18); m_dec_count = input.ReadUInt32 (input.Offset+0x18);
m_input = new ArcView.ArcStream (input, input.Offset+0x20, input.Reserved-0x20);
m_output = new byte[m_dst_size]; m_output = new byte[m_dst_size];
} }
public void Unpack ()
{
HuffmanCode[] hcodes = new HuffmanCode[512];
HuffmanNode[] hnodes = new HuffmanNode[1023];
int leaf_node_count = 0;
for (ushort i = 0; i < 512; i++)
{
int src = Input.ReadByte();
if (-1 == src)
throw new EndOfStreamException ("Incomplete compressed stream");
byte depth = (byte)(src - UpdateKey());
if (0 != depth)
{
hcodes[leaf_node_count].Depth = depth;
hcodes[leaf_node_count].Code = i;
leaf_node_count++;
}
}
Array.Sort (hcodes, 0, leaf_node_count);
CreateHuffmanTree (hnodes, hcodes, leaf_node_count);
HuffmanDecompress (hnodes, m_dec_count);
}
struct HuffmanCode : IComparable<HuffmanCode> struct HuffmanCode : IComparable<HuffmanCode>
{ {
public ushort Code; public ushort Code;
@ -163,31 +203,7 @@ namespace GameRes.Formats.BGI
public uint RightChildIndex; public uint RightChildIndex;
} }
public void Unpack () static void CreateHuffmanTree (HuffmanNode[] hnodes, HuffmanCode[] hcode, int node_count)
{
HuffmanCode[] hcodes = new HuffmanCode[512];
HuffmanNode[] hnodes = new HuffmanNode[1023];
int leaf_node_count = 0;
for (ushort i = 0; i < 512; i++)
{
int src = m_input.ReadByte();
if (-1 == src)
throw new EndOfStreamException ("Incomplete compressed stream");
byte depth = (byte)(src - UpdateKey());
if (0 != depth)
{
hcodes[leaf_node_count].Depth = depth;
hcodes[leaf_node_count].Code = i;
leaf_node_count++;
}
}
Array.Sort (hcodes, 0, leaf_node_count);
CreateHuffmanTree (hnodes, hcodes, leaf_node_count);
HuffmanDecompress (hnodes, m_dec_count);
}
void CreateHuffmanTree (HuffmanNode[] hnodes, HuffmanCode[] hcode, int node_count)
{ {
uint[,] nodes_index = new uint[2,512]; uint[,] nodes_index = new uint[2,512];
uint next_node_index = 1; uint next_node_index = 1;
@ -221,34 +237,6 @@ namespace GameRes.Formats.BGI
} }
} }
int m_bits = 0;
bool GetNextBit ()
{
bool carry = 0 != (m_bits & 0x80);
m_bits <<= 1;
if (0 == (m_bits & 0xff))
{
m_bits = m_input.ReadByte();
if (-1 == m_bits)
throw new EndOfStreamException ("Invalid compressed stream");
carry = 0 != (m_bits & 0x80);
m_bits = (m_bits << 1) | 1;
}
return carry;
}
bool GetBits (int count, out int bits)
{
bits = 0;
for (int i = 0; i < count; ++i)
{
bits <<= 1;
bits |= GetNextBit() ? 1 : 0;
}
return true;
}
uint HuffmanDecompress (HuffmanNode[] hnodes, uint dec_count) uint HuffmanDecompress (HuffmanNode[] hnodes, uint dec_count)
{ {
uint dst_ptr = 0; uint dst_ptr = 0;
@ -258,7 +246,10 @@ namespace GameRes.Formats.BGI
uint node_index = 0; uint node_index = 0;
do do
{ {
if (!GetNextBit()) int bit = GetNextBit();
if (-1 == bit)
throw new EndOfStreamException();
if (0 == bit)
node_index = hnodes[node_index].LeftChildIndex; node_index = hnodes[node_index].LeftChildIndex;
else else
node_index = hnodes[node_index].RightChildIndex; node_index = hnodes[node_index].RightChildIndex;
@ -268,8 +259,8 @@ namespace GameRes.Formats.BGI
uint code = hnodes[node_index].Code; uint code = hnodes[node_index].Code;
if (code >= 256) if (code >= 256)
{ {
int win_pos; int win_pos = GetBits (12);
if (!GetBits (12, out win_pos)) if (-1 == win_pos)
break; break;
uint copy_bytes = (code & 0xff) + 2; uint copy_bytes = (code & 0xff) + 2;
@ -284,27 +275,5 @@ namespace GameRes.Formats.BGI
} }
return dst_ptr; return dst_ptr;
} }
byte UpdateKey ()
{
uint v0 = 20021 * (m_key & 0xffff);
uint v1 = m_magic | (m_key >> 16);
v1 = v1 * 20021 + m_key * 346;
v1 = (v1 + (v0 >> 16)) & 0xffff;
m_key = (v1 << 16) + (v0 & 0xffff) + 1;
return (byte)v1;
}
#region IDisposable Members
public void Dispose ()
{
if (null != m_input)
{
m_input.Dispose();
m_input = null;
}
GC.SuppressFinalize (this);
}
#endregion
} }
} }