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);
+ }
+ }
+}