diff --git a/ArcFormats/FC01/ArcMCA.cs b/ArcFormats/FC01/ArcMCA.cs index 61fb4a5a..ff80dbd3 100644 --- a/ArcFormats/FC01/ArcMCA.cs +++ b/ArcFormats/FC01/ArcMCA.cs @@ -30,17 +30,22 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; using System.Windows.Media; +using System.Windows.Media.Imaging; namespace GameRes.Formats.FC01 { internal class McaArchive : ArcFile { public readonly byte Key; + public readonly int BPP; + public readonly BitmapPalette Palette; - public McaArchive (ArcView arc, ArchiveFormat impl, ICollection dir, byte key) + public McaArchive (ArcView arc, ArchiveFormat impl, ICollection dir, byte key, int bpp = 24, BitmapPalette palette = null) : base (arc, impl, dir) { Key = key; + BPP = bpp; + Palette = palette; } } @@ -60,6 +65,13 @@ namespace GameRes.Formats.FC01 if (index_offset >= file.MaxOffset || !IsSaneCount (count)) return null; + int bpp = file.View.ReadInt32 (0x14); + BitmapPalette palette = null; + if (8 == bpp) + { + palette = ReadPalette (file, index_offset); + index_offset += 0x400; + } string base_name = Path.GetFileNameWithoutExtension (file.Name); long next_offset = file.View.ReadUInt32 (index_offset); var dir = new List (count); @@ -82,7 +94,7 @@ namespace GameRes.Formats.FC01 if (0 == dir.Count) return null; var options = Query (arcStrings.MCAEncryptedNotice); - return new McaArchive (file, this, dir, options.Key); + return new McaArchive (file, this, dir, options.Key, bpp, palette); } public override IImageDecoder OpenImage (ArcFile arc, Entry entry) @@ -91,7 +103,7 @@ namespace GameRes.Formats.FC01 var input = arc.File.CreateStream (entry.Offset, entry.Size); try { - return new McaDecoder (input, mca.Key); + return new McaDecoder (input, mca.Key, mca.BPP, mca.Palette); } catch { @@ -100,6 +112,19 @@ namespace GameRes.Formats.FC01 } } + BitmapPalette ReadPalette (ArcView file, uint offset) + { + var palette = file.View.ReadBytes (offset, 0x400); + int src = 0; + var colors = new Color[0x100]; + for (int i = 0; i < 0x100; ++i) + { + colors[i] = Color.FromRgb (palette[src+2], palette[src+1], palette[src]); + src += 4; + } + return new BitmapPalette (colors); + } + public override ResourceOptions GetDefaultOptions () { return new McgOptions { Key = Settings.Default.MCGLastKey }; @@ -122,22 +147,25 @@ namespace GameRes.Formats.FC01 internal sealed class McaDecoder : BinaryImageDecoder { byte m_key; - int m_method; + bool m_compressed; int m_packed_size; int m_unpacked_size; + BitmapPalette m_palette; - public McaDecoder (IBinaryStream input, byte key) : base (input) + public McaDecoder (IBinaryStream input, byte key, int bpp, BitmapPalette palette) : base (input) { m_key = key; var header = m_input.ReadHeader (0x20); - m_method = header.ToInt32 (0); - if (m_method < 0 || m_method > 1) + int method = header.ToInt32 (0); + if (method < 0 || method > 1) throw new InvalidFormatException(); + m_compressed = method != 0; uint width = header.ToUInt32 (0xC); uint height = header.ToUInt32 (0x10); m_packed_size = header.ToInt32 (0x14); m_unpacked_size = header.ToInt32 (0x18); - Info = new ImageMetaData { Width = width, Height = height, BPP = 24 }; + Info = new ImageMetaData { Width = width, Height = height, BPP = bpp }; + m_palette = palette; } protected override ImageData GetImageData () @@ -145,7 +173,7 @@ namespace GameRes.Formats.FC01 m_input.Position = 0x20; var data = m_input.ReadBytes (m_packed_size); MrgOpener.Decrypt (data, 0, data.Length, m_key); - if (m_method > 0) + if (m_compressed) { using (var input = new BinMemoryStream (data)) using (var lzss = new MrgLzssReader (input, data.Length, m_unpacked_size)) @@ -154,8 +182,9 @@ namespace GameRes.Formats.FC01 data = lzss.Data; } } - int stride = ((int)Info.Width * 3 + 3) & ~3; - return ImageData.Create (Info, PixelFormats.Bgr24, null, data, stride); + int stride = ((int)Info.Width * Info.BPP / 8 + 3) & ~3; + var format = 8 == Info.BPP ? PixelFormats.Indexed8 : PixelFormats.Bgr24; + return ImageData.Create (Info, format, m_palette, data, stride); } } } diff --git a/ArcFormats/FC01/ImageMCG.cs b/ArcFormats/FC01/ImageMCG.cs index 227c1c46..1cd30cad 100644 --- a/ArcFormats/FC01/ImageMCG.cs +++ b/ArcFormats/FC01/ImageMCG.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; using System.Windows.Media; +using System.Windows.Media.Imaging; using GameRes.Formats.Properties; using GameRes.Formats.Strings; using GameRes.Utility; @@ -79,7 +80,7 @@ namespace GameRes.Formats.FC01 if (header_size < 0x40) return null; int bpp = header.ToInt32 (0x24); - if (24 != bpp) + if (24 != bpp && 8 != bpp) throw new NotSupportedException ("Not supported MCG image bitdepth"); return new McgMetaData { @@ -111,11 +112,11 @@ namespace GameRes.Formats.FC01 else key = LastKey.Value; } - var reader = new McgDecoder (stream.AsStream, meta, key); + var reader = new McgDecoder (stream, meta, key); reader.Unpack(); if (reader.Key != 0) LastKey = reader.Key; - return ImageData.Create (info, PixelFormats.Bgr24, null, reader.Data, reader.Stride); + return ImageData.Create (info, reader.Format, reader.Palette, reader.Data, reader.Stride); } public override void Write (Stream file, ImageData image) @@ -152,33 +153,49 @@ namespace GameRes.Formats.FC01 int m_height; int m_pixels; byte m_key; - int m_version; + IBinaryStream m_file; + McgMetaData m_info; - public byte[] Data { get { return m_output; } } - public int Stride { get; private set; } - public byte Key { get { return m_key; } } + public byte Key { get { return m_key; } } + public byte[] Data { get { return m_output; } } + public int Stride { get; private set; } + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } - public McgDecoder (Stream input, McgMetaData info, byte key) + public McgDecoder (IBinaryStream input, McgMetaData info, byte key) { - input.Position = info.DataOffset; - m_input = new byte[info.PackedSize]; - if (m_input.Length != input.Read (m_input, 0, m_input.Length)) - throw new InvalidFormatException ("Unexpected end of file"); + m_file = input; + m_info = info; m_width = (int)info.Width; m_height = (int)info.Height; m_pixels = m_width*m_height; m_key = key; - m_version = info.Version; - Stride = 3 * m_width; - if (101 == m_version) + Stride = m_width * m_info.BPP / 8; + if (101 == m_info.Version) Stride = (Stride + 3) & -4; + if (24 == m_info.BPP) + Format = PixelFormats.Bgr24; + else if (8 == m_info.BPP) + Format = PixelFormats.Indexed8; + else + throw new InvalidFormatException(); } static readonly byte[] ChannelOrder = { 1, 0, 2 }; public void Unpack () { - if (200 == m_version) + m_file.Position = m_info.DataOffset; + int input_size = m_info.PackedSize; + if (8 == m_info.BPP) + { + ReadPalette(); + input_size -= 0x400; + } + m_input = m_file.ReadBytes (input_size); + if (m_input.Length != input_size) + throw new InvalidFormatException ("Unexpected end of file"); + if (200 == m_info.Version) UnpackV200(); else UnpackV101(); @@ -297,5 +314,18 @@ namespace GameRes.Formats.FC01 m_output[dst++] = (byte)(r + g); } } + + void ReadPalette () + { + var palette_data = m_file.ReadBytes (0x400); + int src = 0; + var colors = new Color[0x100]; + for (int i = 0; i < 0x100; ++i) + { + colors[i] = Color.FromRgb (palette_data[src+2], palette_data[src+1], palette_data[src]); + src += 4; + } + Palette = new BitmapPalette (colors); + } } }