From aa93dabd2dd4eb6dfa40e94d6cd0f30e6b6126a5 Mon Sep 17 00:00:00 2001 From: morkt Date: Tue, 16 Jan 2018 11:53:25 +0400 Subject: [PATCH] implemented D.O.'s VRS, GGA and GGS images. --- ArcFormats/ArcFormats.csproj | 5 ++ ArcFormats/Ikura/ArcDRS.cs | 25 ++---- ArcFormats/Ikura/ArcTAN.cs | 128 ++++++++++++++++++++++++++ ArcFormats/Ikura/ImageDRG.cs | 4 +- ArcFormats/Ikura/ImageGGA.cs | 81 +++++++++++++++++ ArcFormats/Ikura/ImageGGS.cs | 140 +++++++++++++++++++++++++++++ ArcFormats/Ikura/ImageTAN.cs | 170 +++++++++++++++++++++++++++++++++++ ArcFormats/Ikura/ImageVRS.cs | 132 +++++++++++++++++++++++++++ 8 files changed, 665 insertions(+), 20 deletions(-) create mode 100644 ArcFormats/Ikura/ArcTAN.cs create mode 100644 ArcFormats/Ikura/ImageGGA.cs create mode 100644 ArcFormats/Ikura/ImageGGS.cs create mode 100644 ArcFormats/Ikura/ImageTAN.cs create mode 100644 ArcFormats/Ikura/ImageVRS.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index e0c345a3..4e4e0aef 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -123,6 +123,11 @@ + + + + + diff --git a/ArcFormats/Ikura/ArcDRS.cs b/ArcFormats/Ikura/ArcDRS.cs index 845cde41..786d5bec 100644 --- a/ArcFormats/Ikura/ArcDRS.cs +++ b/ArcFormats/Ikura/ArcDRS.cs @@ -45,7 +45,7 @@ namespace GameRes.Formats.Ikura public DrsOpener () { - Extensions = Enumerable.Empty(); // DRS archives have no extensions + Extensions = new string[] { "", "dat", "snr" }; } public override ArcFile TryOpen (ArcView file) @@ -56,7 +56,7 @@ namespace GameRes.Formats.Ikura if (dir_size < 0x20 || 0 != (dir_size & 0xf) || dir_size + 2 >= file.MaxOffset) return null; byte first = file.View.ReadByte (2); - if (0 == first) + if (first <= 0x20) return null; file.View.Reserve (0, (uint)dir_size + 2); int dir_offset = 2; @@ -64,25 +64,19 @@ namespace GameRes.Formats.Ikura uint next_offset = file.View.ReadUInt32 (dir_offset+12); if (next_offset > file.MaxOffset || next_offset < dir_size+2) return null; - var encoding = Encodings.cp932.WithFatalFallback(); - byte[] name_raw = new byte[12]; int count = dir_size / 0x10 - 1; var dir = new List (count); for (int i = 0; i < count; ++i) { - file.View.Read (dir_offset, name_raw, 0, 12); - int name_length = name_raw.Length; - while (name_length > 0 && 0 == name_raw[name_length-1]) - --name_length; - if (0 == name_length) + var name = file.View.ReadString (dir_offset, 12); + if (string.IsNullOrEmpty (name)) return null; uint offset = next_offset; dir_offset += 0x10; next_offset = file.View.ReadUInt32 (dir_offset+12); if (next_offset > file.MaxOffset || next_offset < offset) return null; - string name = encoding.GetString (name_raw, 0, name_length).ToLowerInvariant(); var entry = FormatCatalog.Instance.Create (name); entry.Offset = offset; entry.Size = next_offset - offset; @@ -138,21 +132,16 @@ namespace GameRes.Formats.Ikura uint index_size = file.View.ReadUInt32 (12); if (index_size > file.MaxOffset) return null; - var encoding = Encodings.cp932.WithFatalFallback(); - byte[] name_raw = new byte[12]; long dir_offset = 0x20; var dir = new List (count); bool has_scripts = false; for (int i = 0; i < count; ++i) { - file.View.Read (dir_offset, name_raw, 0, 12); - int name_length = name_raw.Length; - while (name_length > 0 && 0 == name_raw[name_length-1]) - --name_length; - if (0 == name_length) + var name = file.View.ReadString (dir_offset, 12); + if (string.IsNullOrEmpty (name)) return null; - string name = encoding.GetString (name_raw, 0, name_length).ToLowerInvariant(); + name = name.ToLowerInvariant(); Entry entry; if (name.EndsWith (".isf") || name.EndsWith (".snr")) { diff --git a/ArcFormats/Ikura/ArcTAN.cs b/ArcFormats/Ikura/ArcTAN.cs new file mode 100644 index 00000000..f6fef404 --- /dev/null +++ b/ArcFormats/Ikura/ArcTAN.cs @@ -0,0 +1,128 @@ +//! \file ArcTAN.cs +//! \date 2018 Jan 16 +//! \brief D.O. animation resource format. +// +// Copyright (C) 2018 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + + +namespace GameRes.Formats.Ikura +{ + internal class TanEntry : Entry + { + public int Index; + } + + internal class TanArchive : ArcFile + { + public readonly TanMetaData Info; + + public TanArchive (ArcView arc, ArchiveFormat impl, ICollection dir, TanMetaData info) + : base (arc, impl, dir) + { + Info = info; + } + } + + [Export(typeof(ArchiveFormat))] + public class TanOpener : ArchiveFormat + { + public override string Tag { get { return "TAN/DO"; } } + public override string Description { get { return "D.O. animation resource"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.Name.HasExtension (".tan")) + return null; + int count = file.View.ReadInt16 (0); + if (!IsSaneCount (count)) + return null; + uint index_pos = 2 + (uint)count * 4; + var info = new TanMetaData { + Width = file.View.ReadUInt16 (index_pos), + Height = file.View.ReadUInt16 (index_pos+2), + DataOffset = index_pos + 4, + }; + index_pos += 0x404; + count = file.View.ReadInt16 (index_pos); + if (!IsSaneCount (count)) + return null; + index_pos += 2; + + var base_name = Path.GetFileNameWithoutExtension (file.Name); + var base_offset = index_pos + 4 * count; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var entry = new TanEntry { + Name = string.Format ("{0}#{1:D2}", base_name, i), + Type = "image", + Offset = base_offset + file.View.ReadUInt32 (index_pos), + Index = i, + }; + dir.Add (entry); + index_pos += 4; + } + for (int i = 1; i < count; ++i) + { + dir[i-1].Size = (uint)(dir[i].Offset - dir[i-1].Offset); + if (!dir[i-1].CheckPlacement (file.MaxOffset)) + return null; + } + dir[dir.Count-1].Size = (uint)(file.MaxOffset - dir[dir.Count-1].Offset); + return new TanArchive (file, this, dir, info); + } + + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var tarc = (TanArchive)arc; + var tent = (TanEntry)entry; + var input = arc.File.CreateStream(); + return new TanFrameDecoder (input, tarc.Info, tent.Index); + } + } + + internal class TanFrameDecoder : BinaryImageDecoder + { + int m_frame; + TanReader m_reader; + + public TanFrameDecoder (IBinaryStream input, TanMetaData info, int frame) + : base (input, info) + { + m_frame = frame; + m_reader = new TanReader (m_input, info); + } + + protected override ImageData GetImageData () + { + var pixels = m_reader.UnpackFrame (m_frame); + return ImageData.Create (Info, m_reader.Format, m_reader.Palette, pixels); + } + } +} diff --git a/ArcFormats/Ikura/ImageDRG.cs b/ArcFormats/Ikura/ImageDRG.cs index 9a99292f..80948466 100644 --- a/ArcFormats/Ikura/ImageDRG.cs +++ b/ArcFormats/Ikura/ImageDRG.cs @@ -460,13 +460,13 @@ namespace GameRes.Formats.Ikura } [Export(typeof(ImageFormat))] - public class GgaFormat : ImageFormat + public class Gga0Format : ImageFormat { public override string Tag { get { return "GG2"; } } public override string Description { get { return "IKURA GDL image format"; } } public override uint Signature { get { return 0x30414747u; } } // 'GGA0' - public GgaFormat () + public Gga0Format () { Extensions = new string[] { "gg1", "gg2", "gg3", "gg0" }; } diff --git a/ArcFormats/Ikura/ImageGGA.cs b/ArcFormats/Ikura/ImageGGA.cs new file mode 100644 index 00000000..d7443c90 --- /dev/null +++ b/ArcFormats/Ikura/ImageGGA.cs @@ -0,0 +1,81 @@ +//! \file ImageGGA.cs +//! \date 2018 Jan 16 +//! \brief D.O. compressed image format. +// +// Copyright (C) 2018 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.Windows.Media; +using GameRes.Compression; + +namespace GameRes.Formats.Ikura +{ + [Export(typeof(ImageFormat))] + public class GgaFormat : ImageFormat + { + public override string Tag { get { return "GGA"; } } + public override string Description { get { return "D.O. image format"; } } + public override uint Signature { get { return 0; } } + + class GgaMetaData : ImageMetaData + { + public int UnpackedSize; + } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + if (!file.Name.HasExtension (".gga")) + return null; + int x = file.ReadInt16(); + int y = file.ReadInt16(); + uint w = file.ReadUInt16(); + uint h = file.ReadUInt16(); + int unpacked_size = file.ReadInt32(); + if (0 == w || 0 == h || w > 0x7FFF || h > 0x7FFF || x < 0 || y < 0 + || 3 * w * h != unpacked_size) + return null; + return new GgaMetaData { + Width = w, Height = h, OffsetX = x, OffsetY = y, BPP = 24, + UnpackedSize = unpacked_size, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var meta = (GgaMetaData)info; + file.Position = 12; + var pixels = new byte[meta.UnpackedSize]; + using (var input = new LzssStream (file.AsStream, LzssMode.Decompress, true)) + { + if (pixels.Length != input.Read (pixels, 0, pixels.Length)) + throw new InvalidFormatException(); + } + return ImageData.Create (info, PixelFormats.Bgr24, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GgaFormat.Write not implemented"); + } + } +} diff --git a/ArcFormats/Ikura/ImageGGS.cs b/ArcFormats/Ikura/ImageGGS.cs new file mode 100644 index 00000000..2c5beba8 --- /dev/null +++ b/ArcFormats/Ikura/ImageGGS.cs @@ -0,0 +1,140 @@ +//! \file ImageGGS.cs +//! \date 2018 Jan 16 +//! \brief D.O. compressed image format. +// +// Copyright (C) 2018 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.Windows.Media; + +namespace GameRes.Formats.Ikura +{ + [Export(typeof(ImageFormat))] + public class GgsFormat : ImageFormat + { + public override string Tag { get { return "GGS"; } } + public override string Description { get { return "D.O. image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + if (!file.Name.HasExtension (".ggs")) + return null; + int x = file.ReadInt16(); + int y = file.ReadInt16(); + uint w = file.ReadUInt16(); + uint h = file.ReadUInt16(); + if (0 == w || 0 == h || w > 0x4000 || h > 0x4000 || x < 0 || y < 0) + return null; + return new ImageMetaData { Width = w, Height = h, OffsetX = x, OffsetY = y, BPP = 24 }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new GgsReader (file, info); + var pixels = reader.Unpack(); + return ImageData.Create (info, reader.Format, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GgsFormat.Write not implemented"); + } + } + + internal class GgsReader + { + IBinaryStream m_input; + byte[] m_output; + + public PixelFormat Format { get { return PixelFormats.Bgr24; } } + + public GgsReader (IBinaryStream input, ImageMetaData info) + { + m_input = input; + m_output = new byte[3 * info.Width * info.Height]; + } + + public byte[] Unpack () + { + m_input.Position = 8; + for (int channel = 0; channel < 3; ++channel) + { + int dst = channel; + while (dst < m_output.Length) + { + int count; + byte ctl = m_input.ReadUInt8(); + if (0 == ctl--) + { + count = m_input.ReadUInt8(); + byte v = m_input.ReadUInt8(); + while (count --> 0) + { + m_output[dst] = v; + dst += 3; + } + } + else if (0 == ctl--) + { + count = m_input.ReadUInt8(); + int offset = m_input.ReadUInt8(); + while (count --> 0) + { + m_output[dst] = m_output[dst-offset]; + dst += 3; + } + } + else if (0 == ctl--) + { + count = m_input.ReadUInt8(); + int offset = m_input.ReadUInt16(); + while (count --> 0) + { + m_output[dst] = m_output[dst-offset]; + dst += 3; + } + } + else if (0 == ctl--) + { + dst += m_input.ReadUInt8(); + } + else if (0 == ctl) + { + dst += m_input.ReadUInt16(); + } + else + { + count = ctl; + while (count --> 0) + { + m_output[dst] = m_input.ReadUInt8(); + dst += 3; + } + } + } + } + return m_output; + } + } +} diff --git a/ArcFormats/Ikura/ImageTAN.cs b/ArcFormats/Ikura/ImageTAN.cs new file mode 100644 index 00000000..bbc7e057 --- /dev/null +++ b/ArcFormats/Ikura/ImageTAN.cs @@ -0,0 +1,170 @@ +//! \file ImageTAN.cs +//! \date 2018 Jan 16 +//! \brief D.O. animation resource format. +// +// Copyright (C) 2018 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.Ikura +{ + internal class TanMetaData : ImageMetaData + { + public uint DataOffset; + } + + [Export(typeof(ImageFormat))] + public class TanFormat : ImageFormat + { + public override string Tag { get { return "TAN"; } } + public override string Description { get { return "D.O. animation resource"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + if (!file.Name.HasExtension (".tan")) + return null; + int count = file.ReadUInt16(); + if (0 == count) + return null; + file.Position = 2 + count * 4; + uint w = file.ReadUInt16(); + uint h = file.ReadUInt16(); + if (0 == w || 0 == h) + return null; + return new TanMetaData { + Width = w, Height = h, BPP = 8, + DataOffset = (uint)file.Position, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new TanReader (file, (TanMetaData)info); + var pixels = reader.UnpackFrame (0); + return ImageData.Create (info, reader.Format, reader.Palette, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("TanFormat.Write not implemented"); + } + } + + internal class TanReader + { + IBinaryStream m_input; + byte[] m_output; + TanMetaData m_info; + + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } + + public TanReader (IBinaryStream input, TanMetaData info) + { + m_input = input; + m_output = new byte[info.Width * info.Height]; + m_info = info; + Format = 8 == m_info.BPP ? PixelFormats.Indexed8 : PixelFormats.Bgr24; + } + + public byte[] UnpackFrame (int frame) + { + m_input.Position = m_info.DataOffset; + if (8 == m_info.BPP) + Palette = ImageFormat.ReadPalette (m_input.AsStream); + int count = m_input.ReadUInt16(); + if (frame >= count) + throw new InvalidFormatException ("Not enough frames in TAN file."); + long base_pos = m_input.Position + 4 * count; + var frame_table = new uint[count]; + for (int i = 0; i < count; ++i) + frame_table[i] = m_input.ReadUInt32(); + Action Unpack; + if (8 == m_info.BPP) + Unpack = Unpack8bpp; + else + Unpack = Unpack24bpp; + for (int i = 0; i <= frame; ++i) + { + m_input.Position = base_pos + frame_table[i]; + Unpack(); + } + return m_output; + } + + void Unpack8bpp () + { + int dst = 0; + while (dst < m_output.Length) + { + int count; + byte ctl = m_input.ReadUInt8(); + if (0 == ctl--) + { + count = m_input.ReadUInt8(); + byte v = m_input.ReadUInt8(); + while (count --> 0) + m_output[dst++] = v; + } + else if (0 == ctl--) + { + count = m_input.ReadUInt8(); + int offset = m_input.ReadUInt8(); + Binary.CopyOverlapped (m_output, dst-offset, dst, count); + dst += count; + } + else if (0 == ctl--) + { + count = m_input.ReadUInt8(); + int offset = m_input.ReadUInt16(); + Binary.CopyOverlapped (m_output, dst-offset, dst, count); + dst += count; + } + else if (0 == ctl--) + { + dst += m_input.ReadUInt8(); + } + else if (0 == ctl) + { + dst += m_input.ReadUInt16(); + } + else + { + count = ctl; + m_input.Read (m_output, dst, count); + dst += count; + } + } + } + + void Unpack24bpp () + { + throw new NotImplementedException(); + } + } +} diff --git a/ArcFormats/Ikura/ImageVRS.cs b/ArcFormats/Ikura/ImageVRS.cs new file mode 100644 index 00000000..84dab678 --- /dev/null +++ b/ArcFormats/Ikura/ImageVRS.cs @@ -0,0 +1,132 @@ +//! \file ImageVRS.cs +//! \date 2018 Jan 16 +//! \brief D.O. image format. +// +// Copyright (C) 2018 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.Windows.Media; +using System.Windows.Media.Imaging; +using GameRes.Utility; + +namespace GameRes.Formats.Ikura +{ + [Export(typeof(ImageFormat))] + public class DoFormat : ImageFormat + { + public override string Tag { get { return "VRS/DO"; } } + public override string Description { get { return "D.O. image format"; } } + public override uint Signature { get { return 0x4F44; } } // 'DO' + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (8); + return new ImageMetaData { + Width = header.ToUInt16 (4), + Height = header.ToUInt16 (6), + BPP = 8, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new DoReader (file, info); + reader.Unpack(); + return ImageData.Create (info, reader.Format, reader.Palette, reader.Data); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("DoFormat.Write not implemented"); + } + } + + internal class DoReader + { + IBinaryStream m_input; + byte[] m_output; + + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } + public byte[] Data { get { return m_output; } } + + public DoReader (IBinaryStream input, ImageMetaData info) + { + m_input = input; + m_output = new byte[info.Width * info.Height]; + Format = PixelFormats.Indexed8; + } + + public void Unpack () + { + m_input.Position = 12; + Palette = ReadPalette(); + int dst = 0; + while (dst < m_output.Length) + { + int count; + byte ctl = m_input.ReadUInt8(); + if (0 == (ctl & 0xC0)) + { + count = ctl & 0x3F; + if (0 == count) + count = m_input.ReadUInt8() + 0x40; + m_input.Read (m_output, dst, count); + } + else if (0 == (ctl & 0x80)) + { + count = ctl & 0x3F; + if (0 == count) + count = m_input.ReadUInt8() + 0x40; + ++count; + Binary.CopyOverlapped (m_output, dst-1, dst, count); + } + else + { + int offset = (m_input.ReadUInt8() | (ctl & 0xF) << 8) + 1; + count = (ctl >> 4) & 7; + if (0 == count) + count = m_input.ReadUInt8() + 8; + count += 2; + Binary.CopyOverlapped (m_output, dst-offset, dst, count); + } + dst += count; + } + } + + BitmapPalette ReadPalette () + { + var palette_data = m_input.ReadBytes (0x300); + if (palette_data.Length != 0x300) + throw new EndOfStreamException(); + int src = 0; + var color_map = new Color[0x100]; + for (int i = 0; i < 0x100; ++i) + { + color_map[i] = Color.FromRgb (palette_data[src+1], palette_data[src+2], palette_data[src]); + src += 3; + } + return new BitmapPalette (color_map); + } + } +}