mirror of
https://github.com/crskycode/GARbro.git
synced 2025-01-11 12:39:16 +08:00
implemented CompressedBG image format.
This commit is contained in:
parent
ff54563560
commit
0a8a6247a4
@ -23,11 +23,11 @@
|
|||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using GameRes.Utility;
|
||||||
|
|
||||||
namespace GameRes.Formats.BGI
|
namespace GameRes.Formats.BGI
|
||||||
{
|
{
|
||||||
@ -57,7 +57,7 @@ namespace GameRes.Formats.BGI
|
|||||||
if (width <= 0 || height <= 0)
|
if (width <= 0 || height <= 0)
|
||||||
return null;
|
return null;
|
||||||
int bpp = input.ReadInt32();
|
int bpp = input.ReadInt32();
|
||||||
if (24 != bpp && 32 != bpp)
|
if (24 != bpp && 32 != bpp && 8 != bpp)
|
||||||
return null;
|
return null;
|
||||||
if (0 != input.ReadInt64())
|
if (0 != input.ReadInt64())
|
||||||
return null;
|
return null;
|
||||||
@ -72,21 +72,295 @@ namespace GameRes.Formats.BGI
|
|||||||
|
|
||||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||||
{
|
{
|
||||||
|
PixelFormat format;
|
||||||
|
if (24 == info.BPP)
|
||||||
|
format = PixelFormats.Bgr24;
|
||||||
|
else if (32 == info.BPP)
|
||||||
|
format = PixelFormats.Bgra32;
|
||||||
|
else
|
||||||
|
format = PixelFormats.Gray8;
|
||||||
int stride = (int)info.Width*((info.BPP+7)/8);
|
int stride = (int)info.Width*((info.BPP+7)/8);
|
||||||
var pixels = new byte[stride*info.Height];
|
var pixels = new byte[stride*info.Height];
|
||||||
stream.Position = 0x10;
|
stream.Position = 0x10;
|
||||||
int read = stream.Read (pixels, 0, pixels.Length);
|
int read = stream.Read (pixels, 0, pixels.Length);
|
||||||
if (read != pixels.Length)
|
if (read != pixels.Length)
|
||||||
throw new InvalidFormatException();
|
throw new InvalidFormatException();
|
||||||
PixelFormat format;
|
return ImageData.Create (info, format, null, pixels, stride);
|
||||||
if (24 == info.BPP)
|
}
|
||||||
format = PixelFormats.Bgr24;
|
}
|
||||||
|
|
||||||
|
internal class CbgMetaData : ImageMetaData
|
||||||
|
{
|
||||||
|
public int IntermediateLength;
|
||||||
|
public uint Key;
|
||||||
|
public int EncLength;
|
||||||
|
public byte CheckSum;
|
||||||
|
public byte CheckXor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Export(typeof(ImageFormat))]
|
||||||
|
public class CompressedBGFormat : ImageFormat
|
||||||
|
{
|
||||||
|
public override string Tag { get { return "CompressedBG"; } }
|
||||||
|
public override string Description { get { return "BGI/Ethornell compressed image format"; } }
|
||||||
|
public override uint Signature { get { return 0x706D6F43; } }
|
||||||
|
|
||||||
|
public CompressedBGFormat ()
|
||||||
|
{
|
||||||
|
Extensions = new string[] { "", "bgi" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write (Stream file, ImageData image)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException ("BgiFormat.Write not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ImageMetaData ReadMetaData (Stream stream)
|
||||||
|
{
|
||||||
|
var header = new byte[0x30];
|
||||||
|
if (header.Length != stream.Read (header, 0, header.Length))
|
||||||
|
return null;
|
||||||
|
if (!Binary.AsciiEqual (header, "CompressedBG___"))
|
||||||
|
return null;
|
||||||
|
return new CbgMetaData
|
||||||
|
{
|
||||||
|
Width = LittleEndian.ToUInt16 (header, 0x10),
|
||||||
|
Height = LittleEndian.ToUInt16 (header, 0x12),
|
||||||
|
BPP = LittleEndian.ToInt32 (header, 0x14),
|
||||||
|
IntermediateLength = LittleEndian.ToInt32 (header, 0x20),
|
||||||
|
Key = LittleEndian.ToUInt32 (header, 0x24),
|
||||||
|
EncLength = LittleEndian.ToInt32 (header, 0x28),
|
||||||
|
CheckSum = header[0x2C],
|
||||||
|
CheckXor = header[0x2D],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||||
|
{
|
||||||
|
var meta = info as CbgMetaData;
|
||||||
|
if (null == meta)
|
||||||
|
throw new ArgumentException ("CompressedBGFormat.Read should be supplied with CbgMetaData", "info");
|
||||||
|
using (var reader = new CbgReader (stream, meta))
|
||||||
|
{
|
||||||
|
reader.Unpack();
|
||||||
|
return ImageData.Create (meta, reader.Format, null, reader.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CbgReader : BgiDecoderBase
|
||||||
|
{
|
||||||
|
byte[] m_output;
|
||||||
|
CbgMetaData m_info;
|
||||||
|
|
||||||
|
public byte[] Data { get { return m_output; } }
|
||||||
|
public PixelFormat Format { get; private set; }
|
||||||
|
|
||||||
|
public CbgReader (Stream input, CbgMetaData info) : base (input, true)
|
||||||
|
{
|
||||||
|
m_info = info;
|
||||||
|
int stride = (int)info.Width * info.BPP / 8;
|
||||||
|
m_output = new byte[stride * (int)info.Height];
|
||||||
|
m_key = m_info.Key;
|
||||||
|
m_magic = 0;
|
||||||
|
switch (m_info.BPP)
|
||||||
|
{
|
||||||
|
case 32: Format = PixelFormats.Bgra32; break;
|
||||||
|
case 24: Format = PixelFormats.Bgr24; break;
|
||||||
|
case 16: Format = PixelFormats.Bgr565; break;
|
||||||
|
case 8: Format = PixelFormats.Gray8; break;
|
||||||
|
default: throw new InvalidFormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unpack ()
|
||||||
|
{
|
||||||
|
Input.Position = 0x30;
|
||||||
|
var enc_buf = new byte[m_info.EncLength];
|
||||||
|
if (enc_buf.Length != Input.Read (enc_buf, 0, enc_buf.Length))
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
byte sum = 0;
|
||||||
|
byte xor = 0;
|
||||||
|
int enc_pos = 0;
|
||||||
|
uint[] leaf_nodes_weight = new uint[0x100];
|
||||||
|
for (int i = 0; i < 0x100; ++i)
|
||||||
|
{
|
||||||
|
uint weight = 0;
|
||||||
|
byte code;
|
||||||
|
int code_length = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (enc_pos >= enc_buf.Length)
|
||||||
|
throw new InvalidFormatException ("Invalid compressed stream");
|
||||||
|
code = enc_buf[enc_pos++];
|
||||||
|
code -= UpdateKey();
|
||||||
|
sum += code;
|
||||||
|
xor ^= code;
|
||||||
|
weight |= ((uint)code & 0x7f) << code_length;
|
||||||
|
code_length += 7;
|
||||||
|
}
|
||||||
|
while (0 != (code & 0x80));
|
||||||
|
leaf_nodes_weight[i] = weight;
|
||||||
|
}
|
||||||
|
if (sum != m_info.CheckSum || xor != m_info.CheckXor)
|
||||||
|
throw new InvalidFormatException ("Compressed stream failed checksum check");
|
||||||
|
|
||||||
|
var nodes = new HuffmanNode[0x200];
|
||||||
|
int root_index = CreateHuffmanTree (nodes, leaf_nodes_weight);
|
||||||
|
byte[] packed = new byte[m_info.IntermediateLength];
|
||||||
|
|
||||||
|
HuffmanDecompress (nodes, root_index, packed);
|
||||||
|
UnpackZeros (packed);
|
||||||
|
ReverseAverageSampling();
|
||||||
|
}
|
||||||
|
|
||||||
|
class HuffmanNode
|
||||||
|
{
|
||||||
|
public bool Valid;
|
||||||
|
public bool IsParent;
|
||||||
|
public uint Weight;
|
||||||
|
public int ParentIndex;
|
||||||
|
public int LeftChildIndex;
|
||||||
|
public int RightChildIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CreateHuffmanTree (HuffmanNode[] nodes, uint[] leaf_nodes_weight)
|
||||||
|
{
|
||||||
|
uint root_node_weight = 0;
|
||||||
|
for (int i = 0; i < 0x100; ++i)
|
||||||
|
{
|
||||||
|
nodes[i] = new HuffmanNode
|
||||||
|
{
|
||||||
|
Valid = leaf_nodes_weight[i] != 0,
|
||||||
|
Weight = leaf_nodes_weight[i],
|
||||||
|
IsParent = false
|
||||||
|
};
|
||||||
|
root_node_weight += nodes[i].Weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parent_node_index = 0x100;
|
||||||
|
int[] child_node_index = new int[2];
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
var parent_node = new HuffmanNode();
|
||||||
|
nodes[parent_node_index] = parent_node;
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
uint min_weight = uint.MaxValue;
|
||||||
|
child_node_index[i] = -1;
|
||||||
|
|
||||||
|
for (int n = 0; n < parent_node_index; n++)
|
||||||
|
{
|
||||||
|
if (nodes[n].Valid)
|
||||||
|
{
|
||||||
|
if (nodes[n].Weight < min_weight)
|
||||||
|
{
|
||||||
|
min_weight = nodes[n].Weight;
|
||||||
|
child_node_index[i] = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodes[child_node_index[i]].Valid = false;
|
||||||
|
nodes[child_node_index[i]].ParentIndex = parent_node_index;
|
||||||
|
}
|
||||||
|
parent_node.Valid = true;
|
||||||
|
parent_node.IsParent = true;
|
||||||
|
parent_node.LeftChildIndex = child_node_index[0];
|
||||||
|
parent_node.RightChildIndex = child_node_index[1];
|
||||||
|
parent_node.Weight = nodes[parent_node.LeftChildIndex].Weight
|
||||||
|
+ nodes[parent_node.RightChildIndex].Weight;
|
||||||
|
if (parent_node.Weight == root_node_weight)
|
||||||
|
break;
|
||||||
|
++parent_node_index;
|
||||||
|
}
|
||||||
|
return parent_node_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HuffmanDecompress (HuffmanNode[] huffman_nodes, int root_node_index, byte[] output)
|
||||||
|
{
|
||||||
|
for (int dst = 0; dst < output.Length; dst++)
|
||||||
|
{
|
||||||
|
int node_index = root_node_index;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int bit = GetNextBit();
|
||||||
|
if (-1 == bit)
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
if (0 == bit)
|
||||||
|
node_index = huffman_nodes[node_index].LeftChildIndex;
|
||||||
else
|
else
|
||||||
format = PixelFormats.Bgra32;
|
node_index = huffman_nodes[node_index].RightChildIndex;
|
||||||
var bitmap = BitmapSource.Create ((int)info.Width, (int)info.Height, 96, 96,
|
}
|
||||||
format, null, pixels, stride);
|
while (huffman_nodes[node_index].IsParent);
|
||||||
bitmap.Freeze();
|
output[dst] = (byte)node_index;
|
||||||
return new ImageData (bitmap, info);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnpackZeros (byte[] input)
|
||||||
|
{
|
||||||
|
int dst = 0;
|
||||||
|
int dec_zero = 0;
|
||||||
|
int src = 0;
|
||||||
|
while (dst < m_output.Length)
|
||||||
|
{
|
||||||
|
int code_length = 0;
|
||||||
|
int count = 0;
|
||||||
|
byte code;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (src >= input.Length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
code = input[src++];
|
||||||
|
count |= (code & 0x7f) << code_length;
|
||||||
|
code_length += 7;
|
||||||
|
}
|
||||||
|
while (0 != (code & 0x80));
|
||||||
|
|
||||||
|
if (dst + count > m_output.Length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (0 == dec_zero)
|
||||||
|
{
|
||||||
|
if (src + count > input.Length)
|
||||||
|
break;
|
||||||
|
Buffer.BlockCopy (input, src, m_output, dst, count);
|
||||||
|
src += count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
m_output[dst+i] = 0;
|
||||||
|
}
|
||||||
|
dec_zero ^= 1;
|
||||||
|
dst += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReverseAverageSampling ()
|
||||||
|
{
|
||||||
|
int pixel_size = m_info.BPP / 8;
|
||||||
|
int stride = (int)m_info.Width * pixel_size;
|
||||||
|
for (int y = 0; y < m_info.Height; ++y)
|
||||||
|
{
|
||||||
|
int line = y * stride;
|
||||||
|
for (int x = 0; x < m_info.Width; ++x)
|
||||||
|
{
|
||||||
|
int pixel = line + x * pixel_size;
|
||||||
|
for (int p = 0; p < pixel_size; p++)
|
||||||
|
{
|
||||||
|
int avg = 0;
|
||||||
|
if (x > 0)
|
||||||
|
avg += m_output[pixel + p - pixel_size];
|
||||||
|
if (y > 0)
|
||||||
|
avg += m_output[pixel + p - stride];
|
||||||
|
if (x > 0 && y > 0)
|
||||||
|
avg /= 2;
|
||||||
|
if (0 != avg)
|
||||||
|
m_output[pixel + p] += (byte)avg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion ("1.1.9.419")]
|
[assembly: AssemblyVersion ("1.1.9.421")]
|
||||||
[assembly: AssemblyFileVersion ("1.1.9.419")]
|
[assembly: AssemblyFileVersion ("1.1.9.421")]
|
||||||
|
@ -21,10 +21,11 @@ tr.odd td { background-color: #eee }
|
|||||||
<tr><td>*.afs</td><td><tt>AFS</tt></td><td>No</td><td rowspan="2">PlayStation 2</td><td rowspan="2">Remember11</td></tr>
|
<tr><td>*.afs</td><td><tt>AFS</tt></td><td>No</td><td rowspan="2">PlayStation 2</td><td rowspan="2">Remember11</td></tr>
|
||||||
<tr><td>*.bip</td><td>-</td><td>No</td></tr>
|
<tr><td>*.bip</td><td>-</td><td>No</td></tr>
|
||||||
<tr class="odd"><td>data.ami</td><td><tt>AMI</tt></td><td>Yes</td><td>-</td><td>Muv-Luv Amaterasu Translation data files</td></tr>
|
<tr class="odd"><td>data.ami</td><td><tt>AMI</tt></td><td>Yes</td><td>-</td><td>Muv-Luv Amaterasu Translation data files</td></tr>
|
||||||
<tr><td>*.arc</td><td><tt>PackFile</tt></td><td>No</td><td>BGI/Ethornell</td><td>
|
<tr><td>*.arc</td><td><tt>PackFile</tt></td><td>No</td><td rowspan="2">BGI/Ethornell</td><td rowspan="2">
|
||||||
Chou Dengeki Stryker<br/>
|
Chou Dengeki Stryker<br/>
|
||||||
H2O -Footprints in the Sand-<br/>
|
H2O -Footprints in the Sand-<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
|
<tr><td>-</td><td><tt>CompressedBG___</tt></td><td>No</td></tr>
|
||||||
<tr class="odd"><td>*</td><td>-<br/><tt>SM2MPX10</tt></td><td>No</td><td rowspan="4">DRS</td><td rowspan="4">
|
<tr class="odd"><td>*</td><td>-<br/><tt>SM2MPX10</tt></td><td>No</td><td rowspan="4">DRS</td><td rowspan="4">
|
||||||
Anata no Osanazuma<br/>
|
Anata no Osanazuma<br/>
|
||||||
Ecchi na Bunny-san wa Kirai?<br/>
|
Ecchi na Bunny-san wa Kirai?<br/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user