mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 21:55:34 +08:00
1172 lines
40 KiB
C#
1172 lines
40 KiB
C#
//! \file ImageWBM.cs
|
|
//! \date Thu Jul 09 20:59:09 2015
|
|
//! \brief Wild Bug's image format.
|
|
//
|
|
// Copyright (C) 2015-2016 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.ComponentModel.Composition;
|
|
using System.IO;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using GameRes.Utility;
|
|
|
|
namespace GameRes.Formats.WildBug
|
|
{
|
|
internal class WbmMetaData : ImageMetaData
|
|
{
|
|
public int EntryCount;
|
|
public int EntrySize;
|
|
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))]
|
|
public class WbmFormat : ImageFormat
|
|
{
|
|
public override string Tag { get { return "WBM"; } }
|
|
public override string Description { get { return "Wild Bug's image format"; } }
|
|
public override uint Signature { get { return 0x1A585057; } } // 'WPX'
|
|
|
|
public override ImageMetaData ReadMetaData (Stream stream)
|
|
{
|
|
byte[] header = new byte[0x10];
|
|
if (header.Length != stream.Read (header, 0, header.Length))
|
|
return null;
|
|
if (!Binary.AsciiEqual (header, 4, "BMP"))
|
|
return null;
|
|
int count = header[0xE];
|
|
int dir_size = header[0xF];
|
|
if (1 != header[0xC] || 0 == count || 0 == dir_size)
|
|
return null;
|
|
header = new byte[count * dir_size];
|
|
if (header.Length != stream.Read (header, 0, header.Length))
|
|
return null;
|
|
var section = WpxSection.Find (header, 0x10, count, dir_size);
|
|
if (null == section)
|
|
return null;
|
|
if (section.UnpackedSize < 0x10)
|
|
return null;
|
|
|
|
stream.Seek (section.Offset, SeekOrigin.Begin);
|
|
byte[] data = new byte[section.UnpackedSize];
|
|
if (data.Length != stream.Read (data, 0, data.Length))
|
|
return null;
|
|
|
|
return new WbmMetaData
|
|
{
|
|
Width = LittleEndian.ToUInt16 (data, 4),
|
|
Height = LittleEndian.ToUInt16 (data, 6),
|
|
BPP = data[0xC],
|
|
EntryCount = count,
|
|
EntrySize = dir_size,
|
|
Header = header,
|
|
};
|
|
}
|
|
|
|
public override ImageData Read (Stream stream, ImageMetaData info)
|
|
{
|
|
var meta = info as WbmMetaData;
|
|
if (null == meta)
|
|
throw new ArgumentException ("WbmFormat.Read should be supplied with WbmMetaData", "info");
|
|
|
|
var section = WpxSection.Find (meta.Header, 0x11, meta.EntryCount, meta.EntrySize);
|
|
if (null == section)
|
|
throw new InvalidFormatException();
|
|
|
|
PixelFormat format;
|
|
int pixel_size;
|
|
switch (meta.BPP)
|
|
{
|
|
case 24:
|
|
format = PixelFormats.Bgr24;
|
|
pixel_size = 3;
|
|
break;
|
|
case 32:
|
|
format = PixelFormats.Bgr32;
|
|
pixel_size = 4;
|
|
break;
|
|
case 16:
|
|
format = PixelFormats.Bgr555;
|
|
pixel_size = 2;
|
|
break;
|
|
case 8:
|
|
format = PixelFormats.Indexed8;
|
|
pixel_size = 1;
|
|
break;
|
|
default:
|
|
throw new NotSupportedException ("Not supported WBM bitdepth");
|
|
}
|
|
int stride = ((int)meta.Width * pixel_size + 3) & -4;
|
|
var reader = new WbmReader (stream, section);
|
|
var pixels = reader.Unpack (stride, pixel_size, section.DataFormat);
|
|
if (null == pixels)
|
|
throw new InvalidFormatException();
|
|
|
|
if (8 == meta.BPP)
|
|
{
|
|
section = WpxSection.Find (meta.Header, 0x12, meta.EntryCount, meta.EntrySize);
|
|
if (null == section)
|
|
return ImageData.Create (info, PixelFormats.Gray8, null, pixels, stride);
|
|
reader = new WbmReader (stream, section);
|
|
var palette_data = reader.Unpack (48, 3, section.DataFormat);
|
|
var palette = CreatePalette (palette_data);
|
|
return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels, stride);
|
|
}
|
|
|
|
if (meta.BPP < 24)
|
|
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)
|
|
{
|
|
throw new System.NotImplementedException ("WbmFormat.Write not implemented");
|
|
}
|
|
|
|
static BitmapPalette CreatePalette (byte[] palette_data)
|
|
{
|
|
int colors = Math.Min (palette_data.Length/3, 0x100);
|
|
var palette = new Color[0x100];
|
|
for (int i = 0; i < colors; ++i)
|
|
{
|
|
int c = i * 3;
|
|
palette[i] = Color.FromRgb (palette_data[c], palette_data[c+1], palette_data[c+2]);
|
|
}
|
|
return new BitmapPalette (palette);
|
|
}
|
|
}
|
|
|
|
internal class WbmReader : WpxDecoder
|
|
{
|
|
public WbmReader (Stream input, WpxSection section) : base (input, section)
|
|
{
|
|
}
|
|
|
|
void GenerateOffsetTableV1 (int[] offset_table, int stride, int pixel_size)
|
|
{
|
|
offset_table[4] = pixel_size;
|
|
offset_table[2] = 2 * pixel_size;
|
|
offset_table[5] = 3 * pixel_size;
|
|
if (5 * pixel_size < stride)
|
|
{
|
|
offset_table[6] = stride - pixel_size;
|
|
offset_table[0] = stride;
|
|
offset_table[7] = pixel_size + stride;
|
|
offset_table[3] = 2 * pixel_size + stride;
|
|
offset_table[1] = 2 * stride;
|
|
}
|
|
else
|
|
{
|
|
offset_table[6] = 4 * pixel_size;
|
|
offset_table[0] = 5 * pixel_size;
|
|
offset_table[7] = 6 * pixel_size;
|
|
offset_table[3] = 7 * 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_version;
|
|
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_version = 2; m_version >= 0; --m_version)
|
|
{
|
|
m_condition = m_version > 0 ? 1 : 0;
|
|
try
|
|
{
|
|
ResetInput();
|
|
if (0 == (flags & 0x80) && 0 != PackedSize)
|
|
{
|
|
byte[] ref_table = new byte[0x10000];
|
|
if (0 != (flags & 1))
|
|
{
|
|
if (0 != (flags & 8))
|
|
{
|
|
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);
|
|
else if (0 != (flags & 2))
|
|
return UnpackV3 (ref_table, offset_table, pixel_size);
|
|
else
|
|
return UnpackV1 (offset_table, pixel_size);
|
|
}
|
|
else if (0 != (flags & 4))
|
|
return UnpackV4 (ref_table, offset_table, pixel_size);
|
|
else if (0 != (flags & 2))
|
|
return UnpackV2 (ref_table, offset_table, pixel_size);
|
|
else
|
|
return UnpackV0 (offset_table, pixel_size);
|
|
}
|
|
else
|
|
return ReadUncompressed();
|
|
}
|
|
catch
|
|
{
|
|
if (0 == m_version)
|
|
throw;
|
|
}
|
|
if (1 == m_version)
|
|
GenerateOffsetTableV1 (offset_table, stride, pixel_size);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
byte[] UnpackVD (byte[] a4, int[] offset_table, int pixel_size) // 0x0F 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;
|
|
|
|
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
|
|
{
|
|
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)
|
|
return null;
|
|
|
|
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 + (-pixel_size & 3);
|
|
|
|
m_bits = m_buffer[m_current++];
|
|
m_bit_count = 8;
|
|
while (remaining > 0)
|
|
{
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
m_output[dst++] = ReadNext();
|
|
--remaining;
|
|
if (0 == remaining)
|
|
return m_output;
|
|
}
|
|
int count, src_offset;
|
|
if (GetNextBit() != 0)
|
|
{
|
|
src_offset = dst - 1 - ReadNext();
|
|
count = 2;
|
|
}
|
|
else
|
|
{
|
|
count = min_count;
|
|
int v35 = GetNextBit();
|
|
v35 += v35 + GetNextBit();
|
|
v35 += v35 + GetNextBit();
|
|
src_offset = dst - offset_table[v35];
|
|
}
|
|
|
|
if (GetNextBit() == 0)
|
|
{
|
|
count += ReadCount();
|
|
}
|
|
if (remaining < count)
|
|
return null;
|
|
Binary.CopyOverlapped (m_output, src_offset, dst, count);
|
|
dst += count;
|
|
remaining -= count;
|
|
}
|
|
return m_output;
|
|
}
|
|
|
|
byte[] UnpackV5 (byte[] a4, int[] offset_table, int pixel_size) // 0x07 format
|
|
{
|
|
byte[] v46 = BuildTable();
|
|
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 v10 = -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 + v10 + 128;
|
|
|
|
if (!FillRefTable (a4, pixel_size + v10))
|
|
return null;
|
|
|
|
int v43 = 16384;
|
|
while (remaining > 0)
|
|
{
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
int v25 = 0;
|
|
int v26 = 0;
|
|
v43 &= ~0xff00;
|
|
v43 |= m_output[dst-pixel_size] << 8;
|
|
int v27 = 16384;
|
|
for (;;)
|
|
{
|
|
v25 = (v25 + 1) & 0xff;
|
|
if (GetNextBit() != 0)
|
|
v26 |= v27;
|
|
if (a4[2 * v26] == v25)
|
|
break;
|
|
v27 >>= 1;
|
|
if (0 == v27)
|
|
return null;
|
|
}
|
|
v25 = a4[2 * v26 + 1];
|
|
byte v29 = v46[v43 + v25];
|
|
if (0 != v25)
|
|
{
|
|
Buffer.BlockCopy (v46, v43, v46, v43+1, v25);
|
|
v46[v43] = v29;
|
|
}
|
|
m_output[dst++] = v29;
|
|
--remaining;
|
|
if (0 == remaining)
|
|
return m_output;
|
|
}
|
|
int count, src_offset;
|
|
if (m_version > 1)
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
int v35;
|
|
if (GetNextBit() != 0)
|
|
{
|
|
v35 = ReadNext();
|
|
count = 2;
|
|
}
|
|
else
|
|
{
|
|
v35 = ReadNext();
|
|
v35 |= ReadNext() << 8;
|
|
count = 3;
|
|
}
|
|
src_offset = dst - 1 - v35;
|
|
}
|
|
else
|
|
{
|
|
count = min_count;
|
|
int v35 = GetNextBit();
|
|
v35 += v35 + GetNextBit();
|
|
v35 += v35 + GetNextBit();
|
|
src_offset = dst - offset_table[v35];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
count = min_count;
|
|
int v32 = GetNextBit() << 2;
|
|
v32 |= GetNextBit() << 1;
|
|
v32 |= GetNextBit();
|
|
src_offset = dst - offset_table[v32];
|
|
}
|
|
else
|
|
{
|
|
byte v35 = ReadNext();
|
|
count = 2;
|
|
src_offset = dst - 1 - v35;
|
|
}
|
|
}
|
|
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[] UnpackV4 (byte[] a4, int[] offset_table, int pixel_size) // 0x06 format
|
|
{
|
|
byte[] v48 = BuildTable();
|
|
int min_count = 1 == pixel_size ? 2 : 1;
|
|
m_available = FillBuffer();
|
|
if (0 == m_available)
|
|
return null;
|
|
|
|
int step = (pixel_size + 4) & -4;
|
|
if (m_available < step + 0x80)
|
|
return null;
|
|
|
|
int v10 = -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 + v10 + 128;
|
|
|
|
if (!FillRefTable (a4, pixel_size + v10))
|
|
return null;
|
|
|
|
int v46 = 16384;
|
|
while (remaining > 0)
|
|
{
|
|
int v28;
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
int v27 = 0;
|
|
v28 = 0;
|
|
v46 &= ~0xff00;
|
|
v46 |= m_output[dst - pixel_size] << 8;
|
|
int v29 = 16384;
|
|
for (;;)
|
|
{
|
|
v27 = (v27 + 1) & 0xff;
|
|
if (GetNextBit() != 0)
|
|
v28 |= v29;
|
|
if (a4[2 * v28] == v27)
|
|
break;
|
|
v29 >>= 1;
|
|
if (0 == v29)
|
|
return null;
|
|
}
|
|
v27 = a4[2 * v28 + 1];
|
|
byte v31 = v48[v46 + v27];
|
|
if (0 != v27)
|
|
{
|
|
Buffer.BlockCopy (v48, v46, v48, v46+1, v27);
|
|
v48[v46] = v31;
|
|
}
|
|
m_output[dst++] = v31;
|
|
--remaining;
|
|
if (0 == remaining)
|
|
return m_output;
|
|
}
|
|
v28 = GetNextBit() << 1;
|
|
v28 |= GetNextBit();
|
|
v28 <<= 1;
|
|
v28 |= GetNextBit();
|
|
int src_offset = dst - offset_table[v28];
|
|
int count;
|
|
if (GetNextBit() != 0)
|
|
{
|
|
count = min_count;
|
|
}
|
|
else
|
|
{
|
|
count = min_count + ReadCount();
|
|
}
|
|
if (remaining < count)
|
|
return null;
|
|
Binary.CopyOverlapped (m_output, src_offset, dst, count);
|
|
dst += count;
|
|
remaining -= count;
|
|
}
|
|
return m_output;
|
|
}
|
|
|
|
byte[] UnpackV3 (byte[] a4, int[] offset_table, int pixel_size) // 0x03 format
|
|
{
|
|
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 v9 = -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 + v9 + 128;
|
|
|
|
if (!FillRefTable (a4, pixel_size + v9))
|
|
return null;
|
|
|
|
while (remaining > 0)
|
|
{
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
int v24 = 0;
|
|
int v25 = 0;
|
|
int v26 = 16384;
|
|
for (;;)
|
|
{
|
|
++v24;
|
|
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, src_offset;
|
|
if (m_version > 1)
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
count = 2;
|
|
src_offset = ReadNext();
|
|
}
|
|
else
|
|
{
|
|
count = 3;
|
|
src_offset = ReadNext();
|
|
src_offset |= ReadNext() << 8;
|
|
}
|
|
src_offset = dst - 1 - src_offset;
|
|
}
|
|
else
|
|
{
|
|
count = min_count;
|
|
int v28 = GetNextBit();
|
|
v28 += v28 + GetNextBit();
|
|
v28 += v28 + GetNextBit();
|
|
src_offset = dst - offset_table[v28];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
count = min_count;
|
|
int v28 = GetNextBit() << 1;
|
|
v28 |= GetNextBit();
|
|
v28 <<= 1;
|
|
v28 |= GetNextBit();
|
|
src_offset = dst - offset_table[v28];
|
|
}
|
|
else
|
|
{
|
|
count = 2;
|
|
src_offset = dst - 1 - ReadNext();
|
|
}
|
|
}
|
|
if (GetNextBit() == 0)
|
|
{
|
|
count += ReadCount();
|
|
}
|
|
if (remaining < count)
|
|
return null;
|
|
Binary.CopyOverlapped (m_output, src_offset, dst, count);
|
|
dst += count;
|
|
remaining -= count;
|
|
}
|
|
return m_output;
|
|
}
|
|
|
|
byte[] UnpackV2 (byte[] a4, int[] offset_table, int pixel_size) // 0x02 format
|
|
{
|
|
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 v9 = -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 + v9 + 128; // within m_buffer
|
|
|
|
if (!FillRefTable (a4, pixel_size + v9))
|
|
return null;
|
|
|
|
while (remaining > 0)
|
|
{
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
int v20 = 0;
|
|
int v21 = 0;
|
|
v9 = 16384;
|
|
for (;;)
|
|
{
|
|
++v20;
|
|
if (0 != GetNextBit())
|
|
v21 |= v9;
|
|
if (a4[2 * v21] == v20)
|
|
break;
|
|
v9 >>= 1;
|
|
if (0 == v9)
|
|
return null;
|
|
}
|
|
m_output[dst++] = a4[2 * v21 + 1];
|
|
--remaining;
|
|
if (0 == remaining)
|
|
return m_output;
|
|
}
|
|
int v22 = GetNextBit() << 1;
|
|
v22 |= GetNextBit();
|
|
v22 <<= 1;
|
|
v22 |= GetNextBit();
|
|
int src_offset = dst - offset_table[v22];
|
|
int count;
|
|
if (0 != GetNextBit())
|
|
{
|
|
count = min_count;
|
|
}
|
|
else
|
|
{
|
|
count = min_count + ReadCount();
|
|
}
|
|
if (remaining < count)
|
|
return null;
|
|
Binary.CopyOverlapped (m_output, src_offset, dst, count);
|
|
dst += count;
|
|
remaining -= count;
|
|
}
|
|
return m_output;
|
|
}
|
|
|
|
byte[] UnpackV1 (int[] offset_table, int pixel_size) // 0x01 format
|
|
{
|
|
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)
|
|
return null;
|
|
|
|
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 + (-pixel_size & 3);
|
|
|
|
m_bits = m_buffer[m_current++];
|
|
m_bit_count = 8;
|
|
while (remaining > 0)
|
|
{
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
m_output[dst++] = ReadNext();
|
|
--remaining;
|
|
if (0 == remaining)
|
|
return m_output;
|
|
}
|
|
int count, src_offset;
|
|
if (m_version > 1)
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
src_offset = ReadNext();
|
|
count = 2;
|
|
}
|
|
else
|
|
{
|
|
src_offset = ReadNext();
|
|
src_offset |= ReadNext() << 8;
|
|
count = 3;
|
|
}
|
|
src_offset = dst - 1 - src_offset;
|
|
}
|
|
else
|
|
{
|
|
count = min_count;
|
|
int v20 = GetNextBit();
|
|
v20 += v20 + GetNextBit();
|
|
v20 += v20 + GetNextBit();
|
|
src_offset = dst - offset_table[v20];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetNextBit() != 0)
|
|
{
|
|
count = min_count;
|
|
int v14 = GetNextBit() << 2;
|
|
v14 |= GetNextBit() << 1;
|
|
v14 |= GetNextBit();
|
|
src_offset = dst - offset_table[v14];
|
|
}
|
|
else
|
|
{
|
|
count = 2;
|
|
src_offset = dst - 1 - ReadNext();
|
|
}
|
|
}
|
|
if (GetNextBit() == 0)
|
|
{
|
|
count += ReadCount();
|
|
}
|
|
if (remaining < count)
|
|
return null;
|
|
Binary.CopyOverlapped (m_output, src_offset, dst, count);
|
|
dst += count;
|
|
remaining -= count;
|
|
}
|
|
return m_output;
|
|
}
|
|
|
|
byte[] UnpackV0 (int[] offset_table, int pixel_size) // 0x00 format
|
|
{
|
|
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)
|
|
return null;
|
|
|
|
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 + (-pixel_size & 3);
|
|
|
|
m_bits = m_buffer[m_current++];
|
|
m_bit_count = 8;
|
|
while (remaining > 0)
|
|
{
|
|
while (0 == (GetNextBit() ^ m_condition))
|
|
{
|
|
m_output[dst++] = ReadNext();
|
|
--remaining;
|
|
if (0 == remaining)
|
|
return m_output;
|
|
}
|
|
int v14 = GetNextBit() << 1;
|
|
v14 |= GetNextBit();
|
|
v14 <<= 1;
|
|
v14 |= GetNextBit();
|
|
int src_offset = dst - offset_table[v14];
|
|
int count;
|
|
if (GetNextBit() != 0)
|
|
{
|
|
count = min_count;
|
|
}
|
|
else
|
|
{
|
|
count = min_count + ReadCount();
|
|
}
|
|
if (remaining < count)
|
|
return null;
|
|
Binary.CopyOverlapped (m_output, src_offset, dst, count);
|
|
dst += count;
|
|
remaining -= count;
|
|
}
|
|
return m_output;
|
|
}
|
|
}
|
|
|
|
internal class WpxDecoder
|
|
{
|
|
Stream m_input;
|
|
protected byte[] m_output;
|
|
int m_packed_size;
|
|
int m_start_pos;
|
|
|
|
public byte[] Data { get { return m_output; } }
|
|
protected int PackedSize { get { return m_packed_size; } }
|
|
|
|
protected WpxDecoder (Stream input, WpxSection section)
|
|
{
|
|
m_input = input;
|
|
m_start_pos = section.Offset;
|
|
m_output = new byte[section.UnpackedSize];
|
|
m_packed_size = section.PackedSize;
|
|
}
|
|
|
|
protected byte[] ReadUncompressed ()
|
|
{
|
|
if (m_output.Length == m_input.Read (m_output, 0, m_output.Length))
|
|
return m_output;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
protected static byte[] BuildTable () // sub_4090E0
|
|
{
|
|
var table = new byte[0x100*0x100];
|
|
for (int i = 0; i < 0x100; ++i)
|
|
{
|
|
byte v2 = (byte)(-1 - i);
|
|
for (int j = 0; j < 0x100; ++j)
|
|
{
|
|
table[0x100*i + j] = v2--;
|
|
}
|
|
}
|
|
return table;
|
|
}
|
|
|
|
protected bool FillRefTable (byte[] table, int src)
|
|
{
|
|
m_bits = m_buffer[m_current++];
|
|
m_bit_count = 8;
|
|
for (int n = 0; n < 0x100; )
|
|
{
|
|
byte v16 = m_buffer[src++];
|
|
for (int half = 0; half < 2; ++half)
|
|
{
|
|
byte v17 = (byte)(v16 & 0xF);
|
|
if (0 != v17)
|
|
{
|
|
int v18 = 0;
|
|
for (int i = v17; i != 0; --i)
|
|
{
|
|
if (0 == m_bit_count)
|
|
{
|
|
if (m_current >= m_available)
|
|
return false;
|
|
m_bits = m_buffer[m_current++];
|
|
m_bit_count = 8;
|
|
}
|
|
int bit = m_bits >> 7;
|
|
m_bits <<= 1;
|
|
--m_bit_count;
|
|
v18 += v18 + bit;
|
|
}
|
|
if (15 != v17)
|
|
v18 <<= 15 - v17;
|
|
table[2 * v18] = v17;
|
|
table[2 * v18 + 1] = (byte)n;
|
|
}
|
|
++n;
|
|
v16 >>= 4;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected byte[] m_buffer = new byte[0x8000];
|
|
protected int m_current = 0;
|
|
protected int m_available = 0;
|
|
|
|
protected byte ReadNext ()
|
|
{
|
|
if (m_current >= m_available)
|
|
{
|
|
m_available = FillBuffer();
|
|
if (0 == m_available)
|
|
throw new InvalidFormatException ("Unexpected end of file");
|
|
m_current = 0;
|
|
}
|
|
return m_buffer[m_current++];
|
|
}
|
|
|
|
protected int ReadCount ()
|
|
{
|
|
int n = 1;
|
|
while (0 == GetNextBit())
|
|
{
|
|
++n;
|
|
}
|
|
int count = 1;
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
count += count + GetNextBit();
|
|
}
|
|
return count - 1;
|
|
}
|
|
|
|
protected int m_input_remaining;
|
|
|
|
protected void ResetInput ()
|
|
{
|
|
m_input.Position = m_start_pos;
|
|
m_input_remaining = m_packed_size;
|
|
}
|
|
|
|
protected int FillBuffer () // sub_409B02
|
|
{
|
|
int read = 0;
|
|
if (m_input_remaining > 0)
|
|
{
|
|
int size = Math.Min (m_input_remaining, 0x8000);
|
|
m_input_remaining -= size;
|
|
read = m_input.Read (m_buffer, 0, size);
|
|
}
|
|
return read;
|
|
}
|
|
|
|
protected byte m_bits;
|
|
protected int m_bit_count = 0;
|
|
|
|
protected int GetNextBit ()
|
|
{
|
|
if (0 == m_bit_count)
|
|
{
|
|
m_bits = ReadNext();
|
|
m_bit_count = 8;
|
|
}
|
|
int bit = m_bits >> 7;
|
|
m_bits <<= 1;
|
|
--m_bit_count;
|
|
return bit;
|
|
}
|
|
}
|
|
}
|