diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index b5c4f750..bfa6ce0c 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -102,6 +102,7 @@ + diff --git a/ArcFormats/CsWare/ImageBPC.cs b/ArcFormats/CsWare/ImageBPC.cs new file mode 100644 index 00000000..739e38ca --- /dev/null +++ b/ArcFormats/CsWare/ImageBPC.cs @@ -0,0 +1,202 @@ +//! \file ImageBPC.cs +//! \date 2017 Nov 22 +//! \brief C's ware bitmap. +// +// 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.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace GameRes.Formats.CsWare +{ + [Export(typeof(ImageFormat))] + public class BpcFormat : ImageFormat + { + public override string Tag { get { return "BPC"; } } + public override string Description { get { return "C's ware bitmap format"; } } + public override uint Signature { get { return 0x28; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x10); + uint width = header.ToUInt32 (4); + uint height = header.ToUInt32 (8); + int bpp = header.ToUInt16 (0xE); + if (bpp != 2 && bpp != 8 && bpp != 24) + return null; + return new ImageMetaData { Width = width, Height = height, BPP = bpp }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var bpc = new BpcReader (file, info); + bpc.Unpack(); + return ImageData.CreateFlipped (info, bpc.Format, bpc.Palette, bpc.Data, bpc.Stride); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("BpcFormat.Write not implemented"); + } + } + + internal sealed class BpcReader + { + IBinaryStream m_input; + int m_width; + int m_height; + int m_bpp; + byte[] m_output; + + public BitmapPalette Palette { get; private set; } + public PixelFormat Format { get; private set; } + public byte[] Data { get { return m_output; } } + public int Stride { get { return m_width * m_bpp / 8; } } + + public BpcReader (IBinaryStream input, ImageMetaData info) + { + m_input = input; + m_width = (int)info.Width; + m_height = (int)info.Height; + m_bpp = info.BPP; + } + + public void Unpack () + { + m_input.Position = m_input.Signature; + if (m_bpp <= 8) + Palette = ImageFormat.ReadPalette (m_input.AsStream, 1 << m_bpp); + switch (m_bpp) + { + case 1: Unpack1bpp(); break; + case 8: Unpack8bpp(); break; + case 24: Unpack24bpp(); break; + default: throw new InvalidFormatException(); + } + } + + void Unpack1bpp () + { + Format = PixelFormats.Indexed1; + m_output = m_input.ReadBytes (m_height * Stride); + } + + void Unpack8bpp () + { + Format = PixelFormats.Indexed8; + m_output = new byte[m_height * m_width]; + + int packed_size = m_input.ReadInt32(); + byte index = 0; + byte ctl = m_input.ReadUInt8(); + if (0xF5 == ctl) + index = m_input.ReadUInt8(); + var input = m_input.ReadBytes (packed_size); + if (input.Length != packed_size) + throw new InvalidFormatException(); + int src = 0; + int dst = 0; + while (src < packed_size) + { + if (input[src] != ctl) + { + m_output[dst++] = input[src++]; + } + else if (ctl != 0xF5) + { + int count = input[src+1]; + for (int i = 0; i < count; ++i) + m_output[dst++] = input[src-1]; + src += 2; + } + else if (input[src+1] == index) + { + int count = input[src+2]; + for (int i = 0; i < count; ++i) + m_output[dst++] = input[src-1]; + src += 3; + } + else + { + m_output[dst++] = input[src++]; + } + } + } + + void Unpack24bpp () + { + Format = PixelFormats.Bgr24; + m_output = new byte[m_height * Stride]; + + var plane_size = new int[3]; + plane_size[0] = m_input.ReadInt32(); + plane_size[1] = m_input.ReadInt32(); + plane_size[2] = m_input.ReadInt32(); + var ctl = m_input.ReadBytes (3); + var pixel = m_input.ReadBytes (3); + var input = m_input.ReadBytes (plane_size.Sum()); + int src = 0; + for (int plane = 0; plane < 3; ++plane) + { + int dst = plane; + for (int p = 0; p < plane_size[plane]; ++p) + { + if (ctl[plane] != input[src]) + { + m_output[dst] = input[src++]; + dst += 3; + } + else if (ctl[plane] != 0xF5) + { + int count = input[src + 1]; + for (int i = 0; i < count; ++i) + { + m_output[dst] = input[src - 1]; + dst += 3; + } + ++p; + src += 2; + } + else if (pixel[plane] == input[src + 1]) + { + int count = input[src + 2]; + for (int i = 0; i < count; ++i) + { + m_output[dst] = input[src - 1]; + dst += 3; + } + p += 2; + src += 3; + } + else + { + m_output[dst] = input[src++]; + dst += 3; + } + } + } + } + } +}