(WbmFormat): added compression methods and alpha-channel support.

This commit is contained in:
morkt 2015-08-17 23:08:02 +04:00
parent 947e0a6664
commit d526ef96fc
2 changed files with 337 additions and 77 deletions

View File

@ -39,6 +39,34 @@ namespace GameRes.Formats.WildBug
public byte[] Header; public byte[] Header;
} }
internal class WpxSection
{
public int DataFormat;
public int Offset;
public int UnpackedSize;
public int PackedSize;
public static WpxSection Find (byte[] header, byte id, int count, int dir_size)
{
int ptr = 0;
int n = 0;
while (header[ptr] != id)
{
ptr += dir_size;
if (ptr >= header.Length)
return null;
if (++n >= count)
return null;
}
return new WpxSection {
DataFormat = header[ptr+1],
Offset = LittleEndian.ToInt32 (header, ptr+4),
UnpackedSize = LittleEndian.ToInt32 (header, ptr+8),
PackedSize = LittleEndian.ToInt32 (header, ptr+12),
};
}
}
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]
public class WbmFormat : ImageFormat public class WbmFormat : ImageFormat
{ {
@ -51,6 +79,8 @@ namespace GameRes.Formats.WildBug
byte[] header = new byte[0x10]; byte[] header = new byte[0x10];
if (header.Length != stream.Read (header, 0, header.Length)) if (header.Length != stream.Read (header, 0, header.Length))
return null; return null;
if (!Binary.AsciiEqual (header, 4, "BMP"))
return null;
int count = header[0xE]; int count = header[0xE];
int dir_size = header[0xF]; int dir_size = header[0xF];
if (1 != header[0xC] || 0 == count || 0 == dir_size) if (1 != header[0xC] || 0 == count || 0 == dir_size)
@ -58,17 +88,15 @@ namespace GameRes.Formats.WildBug
header = new byte[count * dir_size]; header = new byte[count * dir_size];
if (header.Length != stream.Read (header, 0, header.Length)) if (header.Length != stream.Read (header, 0, header.Length))
return null; return null;
int? ptr = GetDataBlock (header, 0x10, count, dir_size); var section = WpxSection.Find (header, 0x10, count, dir_size);
if (null == ptr) if (null == section)
return null; return null;
int data_offset = LittleEndian.ToInt32 (header, ptr.Value+4); if (section.UnpackedSize < 0x10)
int size = LittleEndian.ToInt32 (header, ptr.Value+8);
if (size < 0x10)
return null; return null;
stream.Seek (data_offset, SeekOrigin.Begin); stream.Seek (section.Offset, SeekOrigin.Begin);
byte[] data = new byte[size]; byte[] data = new byte[section.UnpackedSize];
if (size != stream.Read (data, 0, size)) if (data.Length != stream.Read (data, 0, data.Length))
return null; return null;
return new WbmMetaData return new WbmMetaData
@ -82,34 +110,15 @@ namespace GameRes.Formats.WildBug
}; };
} }
private int? GetDataBlock (byte[] header, byte id, int count, int dir_size)
{
int ptr = 0;
int n = 0;
while (header[ptr] != id)
{
ptr += dir_size;
if (ptr >= header.Length)
return null;
if (++n >= count)
return null;
}
return ptr;
}
public override ImageData Read (Stream stream, ImageMetaData info) public override ImageData Read (Stream stream, ImageMetaData info)
{ {
var meta = info as WbmMetaData; var meta = info as WbmMetaData;
if (null == meta) if (null == meta)
throw new ArgumentException ("WbmFormat.Read should be supplied with WbmMetaData", "info"); throw new ArgumentException ("WbmFormat.Read should be supplied with WbmMetaData", "info");
int? ptr = GetDataBlock (meta.Header, 0x11, meta.EntryCount, meta.EntrySize); var section = WpxSection.Find (meta.Header, 0x11, meta.EntryCount, meta.EntrySize);
if (null == ptr) if (null == section)
throw new InvalidFormatException(); throw new InvalidFormatException();
int data_format = meta.Header[ptr.Value+1];
int data_offset = LittleEndian.ToInt32 (meta.Header, ptr.Value+4);
int unpacked_size = LittleEndian.ToInt32 (meta.Header, ptr.Value+8);
int packed_size = LittleEndian.ToInt32 (meta.Header, ptr.Value+12);
PixelFormat format; PixelFormat format;
int pixel_size; int pixel_size;
@ -131,12 +140,43 @@ namespace GameRes.Formats.WildBug
throw new NotSupportedException ("Not supported WBM bitdepth"); throw new NotSupportedException ("Not supported WBM bitdepth");
} }
int stride = ((int)meta.Width * pixel_size + 3) & -4; int stride = ((int)meta.Width * pixel_size + 3) & -4;
var reader = new WbmReader (stream, data_offset, unpacked_size, packed_size); var reader = new WbmReader (stream, section);
var pixels = reader.Unpack (stride, pixel_size, meta.Header[ptr.Value+1]); var pixels = reader.Unpack (stride, pixel_size, section.DataFormat);
if (null == pixels) if (null == pixels)
throw new InvalidFormatException(); throw new InvalidFormatException();
if (meta.BPP < 24)
return ImageData.Create (info, format, null, pixels, stride); return ImageData.Create (info, format, null, pixels, stride);
section = WpxSection.Find (meta.Header, 0x13, meta.EntryCount, meta.EntrySize);
if (null == section)
return ImageData.Create (info, format, null, pixels, stride);
int alpha_stride = ((int)meta.Width + 3) & -4;
byte[] alpha = null;
try
{
reader = new WbmReader (stream, section);
alpha = reader.Unpack (alpha_stride, 1, section.DataFormat);
}
catch { }
if (null == alpha)
return ImageData.Create (info, format, null, pixels, stride);
byte[] alpha_image = new byte[4*meta.Width*meta.Height];
int dst = 0;
for (int y = 0; y < meta.Height; ++y)
{
int alpha_src = y * alpha_stride;
int src = y * stride;
for (int x = 0; x < meta.Width; ++x)
{
alpha_image[dst++] = pixels[src];
alpha_image[dst++] = pixels[src+1];
alpha_image[dst++] = pixels[src+2];
alpha_image[dst++] = alpha[alpha_src+x];
src += pixel_size;
}
}
return ImageData.Create (info, PixelFormats.Bgra32, null, alpha_image, (int)meta.Width*4);
} }
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
@ -150,20 +190,20 @@ namespace GameRes.Formats.WildBug
Stream m_input; Stream m_input;
byte[] m_output; byte[] m_output;
int m_packed_size; int m_packed_size;
int m_start_pos;
public byte[] Data { get { return m_output; } } public byte[] Data { get { return m_output; } }
public WbmReader (Stream input, int offset, int unpacked_size, int packed_size) public WbmReader (Stream input, WpxSection section)
{ {
m_input = input; m_input = input;
m_input.Position = offset; m_start_pos = section.Offset;
m_output = new byte[unpacked_size]; m_output = new byte[section.UnpackedSize];
m_packed_size = packed_size; m_packed_size = section.PackedSize;
} }
public byte[] Unpack (int stride, int pixel_size, int flags) // sub_40919C void GenerateOffsetTableV1 (int[] offset_table, int stride, int pixel_size)
{ {
int[] offset_table = new int[8];
offset_table[4] = pixel_size; offset_table[4] = pixel_size;
offset_table[2] = 2 * pixel_size; offset_table[2] = 2 * pixel_size;
offset_table[5] = 3 * pixel_size; offset_table[5] = 3 * pixel_size;
@ -183,12 +223,57 @@ namespace GameRes.Formats.WildBug
offset_table[3] = 7 * pixel_size; offset_table[3] = 7 * pixel_size;
offset_table[1] = 8 * pixel_size; offset_table[1] = 8 * pixel_size;
} }
}
void GenerateOffsetTableV2 (int[] offset_table, int stride, int pixel_size)
{
offset_table[0] = pixel_size;
offset_table[1] = 2 * pixel_size;
offset_table[2] = 3 * pixel_size;
if (5 * pixel_size < stride)
{
offset_table[3] = stride - pixel_size;
offset_table[4] = stride;
offset_table[5] = pixel_size + stride;
offset_table[6] = 2 * pixel_size + stride;
offset_table[7] = 2 * stride;
}
else
{
offset_table[3] = 4 * pixel_size;
offset_table[4] = 5 * pixel_size;
offset_table[5] = 6 * pixel_size;
offset_table[6] = 7 * pixel_size;
offset_table[7] = 8 * pixel_size;
}
}
int m_condition;
public byte[] Unpack (int stride, int pixel_size, int flags) // sub_40919C
{
int[] offset_table = new int[8];
GenerateOffsetTableV2 (offset_table, stride, pixel_size);
for (m_condition = 1; m_condition >= 0; --m_condition)
{
try
{
ResetInput();
if (0 == (flags & 0x80) && 0 != m_packed_size) if (0 == (flags & 0x80) && 0 != m_packed_size)
{ {
byte[] ref_table = new byte[0x10000]; byte[] ref_table = new byte[0x10000];
if (0 != (flags & 1)) if (0 != (flags & 1))
{
if (0 != (flags & 8))
{ {
if (0 != (flags & 4)) if (0 != (flags & 4))
return UnpackVD (ref_table, offset_table, pixel_size);
else if (0 != (flags & 2))
return UnpackVB (ref_table, offset_table, pixel_size);
else
return UnpackV9 (offset_table, pixel_size);
}
else if (0 != (flags & 4))
return UnpackV5 (ref_table, offset_table, pixel_size); return UnpackV5 (ref_table, offset_table, pixel_size);
else if (0 != (flags & 2)) else if (0 != (flags & 2))
return UnpackV3 (ref_table, offset_table, pixel_size); return UnpackV3 (ref_table, offset_table, pixel_size);
@ -204,10 +289,179 @@ namespace GameRes.Formats.WildBug
} }
else if (m_output.Length == m_input.Read (m_output, 0, m_output.Length)) else if (m_output.Length == m_input.Read (m_output, 0, m_output.Length))
return m_output; return m_output;
}
catch
{
if (0 == m_condition)
throw;
}
GenerateOffsetTableV1 (offset_table, stride, pixel_size);
}
return null; return null;
} }
byte[] UnpackVD (byte[] a4, int[] offset_table, int pixel_size) // 0x0F format
// int sub_460470(void *dst, const void *src, unsigned __int8 *ref_table, unsigned int packed_size, int *offset_table, int unpacked_size, unsigned int pixel_size)
{
byte[] v47 = BuildTable(); //sub_46C26C();
int min_count = 1 == pixel_size ? 2 : 1;
m_available = FillBuffer();
if (0 == m_available)
return null;
int step = (pixel_size + 3) & -4;
if (m_available < step + 0x80)
return null;
int v7 = -pixel_size & 3;
Buffer.BlockCopy (m_buffer, 0, m_output, 0, pixel_size);
int dst = pixel_size;
int remaining = m_output.Length - pixel_size;
m_current = pixel_size + v7 + 128;
if (!FillRefTable (a4, pixel_size + v7))
return null;
int v45 = 16384;
while (remaining > 0)
{
while (0 == (GetNextBit() ^ m_condition))
{
int v24 = 0;
int v25 = 0;
v45 &= ~0xff00;
v45 |= m_output[dst - pixel_size] << 8;
int v26 = 16384;
for (;;)
{
v24 = (v24 + 1) & 0xFF;
if (GetNextBit() != 0)
v25 |= v26;
if (a4[2 * v25] == v24)
break;
v26 >>= 1;
if (0 == v26)
return null;
}
v24 = a4[2 * v25 + 1];
byte v28 = v47[v45 + v24];
if (0 != v24)
{
Buffer.BlockCopy (v47, v45, v47, v45+1, v24);
v47[v45] = v28;
}
m_output[dst++] = v28;
--remaining;
if (0 == remaining)
return m_output;
}
int count;
int src_offset;
if (GetNextBit() != 0)
{
int v37 = ReadNext();
count = 2;
src_offset = dst - 1 - v37;
}
else
{
count = min_count;
int v36 = GetNextBit() << 2;
v36 |= GetNextBit() << 1;
v36 |= GetNextBit();
src_offset = dst - offset_table[v36];
}
if (0 == GetNextBit())
{
count += ReadCount();
}
if (remaining < count)
return null;
Binary.CopyOverlapped (m_output, src_offset, dst, count);
dst += count;
remaining -= count;
}
return m_output;
}
byte[] UnpackVB (byte[] a4, int[] offset_table, int pixel_size) // 0x0B format
{
byte[] v47 = BuildTable(); //sub_46C26C();
int min_count = 1 == pixel_size ? 2 : 1;
m_available = FillBuffer();
if (0 == m_available)
return null;
int step = (pixel_size + 3) & -4;
if (m_available < step + 0x80)
return null;
int v7 = -pixel_size & 3;
Buffer.BlockCopy (m_buffer, 0, m_output, 0, pixel_size);
int dst = pixel_size;
int remaining = m_output.Length - pixel_size;
m_current = pixel_size + v7 + 128;
if (!FillRefTable (a4, pixel_size + v7))
return null;
while (remaining > 0)
{
while (0 == (GetNextBit() ^ m_condition))
{
int v24 = 0;
int v25 = 0;
int v26 = 16384;
for (;;)
{
v24 = (v24 + 1) & 0xFF;
if (GetNextBit() != 0)
v25 |= v26;
if (a4[2 * v25] == v24)
break;
v26 >>= 1;
if (0 == v26)
return null;
}
m_output[dst++] = a4[2 * v25 + 1];
--remaining;
if (0 == remaining)
return m_output;
}
int count;
int src_offset;
if (GetNextBit() != 0)
{
int v37 = ReadNext();
count = 2;
src_offset = dst - 1 - v37;
}
else
{
count = min_count;
int v36 = GetNextBit() << 2;
v36 |= GetNextBit() << 1;
v36 |= GetNextBit();
src_offset = dst - offset_table[v36];
}
if (0 == GetNextBit())
{
count += ReadCount();
}
if (remaining < count)
return null;
Binary.CopyOverlapped (m_output, src_offset, dst, count);
dst += count;
remaining -= count;
}
return m_output;
}
byte[] UnpackV9 (int[] offset_table, int pixel_size) // 0x09 format
{
throw new NotImplementedException();
}
byte[] UnpackV5 (byte[] a4, int[] offset_table, int pixel_size) // 0x07 format byte[] UnpackV5 (byte[] a4, int[] offset_table, int pixel_size) // 0x07 format
// int sub_409AF4 (void *a1, FILE *stream, void *ptr, void *a4, unsigned int m_packed_size, int *offset_table, int unpacked_size, unsigned int pixel_size) // int sub_409AF4 (void *a1, FILE *stream, void *ptr, void *a4, unsigned int m_packed_size, int *offset_table, int unpacked_size, unsigned int pixel_size)
{ {
@ -235,7 +489,7 @@ namespace GameRes.Formats.WildBug
int v43 = 16384; int v43 = 16384;
while (remaining > 0) while (remaining > 0)
{ {
while (0 == GetNextBit()) while (0 == (GetNextBit() ^ m_condition))
{ {
int v25 = 0; int v25 = 0;
int v26 = 0; int v26 = 0;
@ -270,9 +524,8 @@ namespace GameRes.Formats.WildBug
if (GetNextBit() != 0) if (GetNextBit() != 0)
{ {
count = min_count; count = min_count;
int v32 = GetNextBit() << 1; int v32 = GetNextBit() << 2;
v32 |= GetNextBit(); v32 |= GetNextBit() << 1;
v32 <<= 1;
v32 |= GetNextBit(); v32 |= GetNextBit();
src_offset = dst - offset_table[v32]; src_offset = dst - offset_table[v32];
} }
@ -323,7 +576,7 @@ namespace GameRes.Formats.WildBug
while (remaining > 0) while (remaining > 0)
{ {
int v28; int v28;
while (0 == GetNextBit()) while (0 == (GetNextBit() ^ m_condition))
{ {
int v27 = 0; int v27 = 0;
v28 = 0; v28 = 0;
@ -402,7 +655,7 @@ namespace GameRes.Formats.WildBug
while (remaining > 0) while (remaining > 0)
{ {
while (GetNextBit() == 0) while (0 == (GetNextBit() ^ m_condition))
{ {
int v24 = 0; int v24 = 0;
int v25 = 0; int v25 = 0;
@ -477,7 +730,7 @@ namespace GameRes.Formats.WildBug
while (remaining > 0) while (remaining > 0)
{ {
while (0 == GetNextBit()) while (0 == (GetNextBit() ^ m_condition))
{ {
int v20 = 0; int v20 = 0;
int v21 = 0; int v21 = 0;
@ -544,7 +797,7 @@ namespace GameRes.Formats.WildBug
m_bit_count = 8; m_bit_count = 8;
while (remaining > 0) while (remaining > 0)
{ {
while (0 == GetNextBit()) while (0 == (GetNextBit() ^ m_condition))
{ {
m_output[dst++] = ReadNext(); m_output[dst++] = ReadNext();
--remaining; --remaining;
@ -555,9 +808,8 @@ namespace GameRes.Formats.WildBug
if (GetNextBit() != 0) if (GetNextBit() != 0)
{ {
count = min_count; count = min_count;
int v14 = GetNextBit() << 1; int v14 = GetNextBit() << 2;
v14 |= GetNextBit(); v14 |= GetNextBit() << 1;
v14 <<= 1;
v14 |= GetNextBit(); v14 |= GetNextBit();
src_offset = dst - offset_table[v14]; src_offset = dst - offset_table[v14];
} }
@ -602,7 +854,7 @@ namespace GameRes.Formats.WildBug
m_bit_count = 8; m_bit_count = 8;
while (remaining > 0) while (remaining > 0)
{ {
while (0 == GetNextBit()) while (0 == (GetNextBit() ^ m_condition))
{ {
m_output[dst++] = ReadNext(); m_output[dst++] = ReadNext();
--remaining; --remaining;
@ -655,7 +907,7 @@ namespace GameRes.Formats.WildBug
byte v2 = (byte)(-1 - i); byte v2 = (byte)(-1 - i);
for (int j = 0; j < 0x100; ++j) for (int j = 0; j < 0x100; ++j)
{ {
table[0x100*i + j] = v2--; // *(&byte_41B0C0[256 * i] + j) = v2--; table[0x100*i + j] = v2--;
} }
} }
return table; return table;
@ -677,13 +929,21 @@ namespace GameRes.Formats.WildBug
return m_buffer[m_current++]; return m_buffer[m_current++];
} }
int m_input_remaining;
void ResetInput ()
{
m_input.Position = m_start_pos;
m_input_remaining = m_packed_size;
}
int FillBuffer () // sub_409B02 int FillBuffer () // sub_409B02
{ {
int read = 0; int read = 0;
if (m_packed_size > 0) if (m_input_remaining > 0)
{ {
int size = Math.Min (m_packed_size, 0x8000); int size = Math.Min (m_input_remaining, 0x8000);
m_packed_size -= size; m_input_remaining -= size;
read = m_input.Read (m_buffer, 0, size); read = m_input.Read (m_buffer, 0, size);
} }
return read; return read;

View File

@ -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.94")] [assembly: AssemblyVersion ("1.1.9.95")]
[assembly: AssemblyFileVersion ("1.1.9.94")] [assembly: AssemblyFileVersion ("1.1.9.95")]