diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index edb5e98e..e949d727 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -108,6 +108,7 @@ + diff --git a/ArcFormats/Foster/ArcC24.cs b/ArcFormats/Foster/ArcC24.cs index b734c323..08ba59c0 100644 --- a/ArcFormats/Foster/ArcC24.cs +++ b/ArcFormats/Foster/ArcC24.cs @@ -23,7 +23,6 @@ // IN THE SOFTWARE. // -using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; @@ -75,18 +74,38 @@ namespace GameRes.Formats.Foster public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { - var offset = entry.Offset; - var info = new C24MetaData - { - Width = arc.File.View.ReadUInt32 (offset), - Height = arc.File.View.ReadUInt32 (offset+4), - OffsetX = arc.File.View.ReadInt32 (offset+8), - OffsetY = arc.File.View.ReadInt32 (offset+12), - BPP = 24, - DataOffset = (uint)(offset + 0x10), - }; + var info = ReadImageInfo (arc.File, entry.Offset, 24); var input = arc.File.CreateStream (0, (uint)arc.File.MaxOffset); return new C24Decoder (input, info); } + + internal C24MetaData ReadImageInfo (ArcView file, long offset, int bpp) + { + return new C24MetaData + { + Width = file.View.ReadUInt32 (offset), + Height = file.View.ReadUInt32 (offset+4), + OffsetX = file.View.ReadInt32 (offset+8), + OffsetY = file.View.ReadInt32 (offset+12), + BPP = bpp, + DataOffset = (uint)(offset + 0x10), + }; + } + } + + [Export(typeof(ArchiveFormat))] + public class C25Opener : C24Opener + { + public override string Tag { get { return "C25"; } } + public override uint Signature { get { return 0x00353243; } } // 'C25' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var info = ReadImageInfo (arc.File, entry.Offset, 32); + var input = arc.File.CreateStream (0, (uint)arc.File.MaxOffset); + return new C25Decoder (input, info); + } } } diff --git a/ArcFormats/Foster/ImageC24.cs b/ArcFormats/Foster/ImageC24.cs index 9695f09a..4a4ef1ff 100644 --- a/ArcFormats/Foster/ImageC24.cs +++ b/ArcFormats/Foster/ImageC24.cs @@ -23,6 +23,7 @@ // IN THE SOFTWARE. // +using System; using System.ComponentModel.Composition; using System.IO; using System.Windows.Media; @@ -50,8 +51,13 @@ namespace GameRes.Formats.Foster int count = header.ToInt32 (4); if (count <= 0) return null; - file.Position = header.ToUInt32 (8); - var info = new C24MetaData { BPP = 24 }; + return ReadMetaData (file, header.ToUInt32 (8), 24); + } + + internal C24MetaData ReadMetaData (IBinaryStream file, long offset, int bpp) + { + file.Position = offset; + var info = new C24MetaData { BPP = bpp }; info.Width = file.ReadUInt32(); info.Height = file.ReadUInt32(); info.OffsetX = file.ReadInt32(); @@ -72,17 +78,18 @@ namespace GameRes.Formats.Foster } } - internal sealed class C24Decoder : IImageDecoder + internal abstract class CDecoderBase : IImageDecoder { - IBinaryStream m_input; - C24MetaData m_info; - byte[] m_output; - bool m_should_dispose; - ImageData m_image; + protected IBinaryStream m_input; + protected C24MetaData m_info; + protected byte[] m_output; + private ImageData m_image; + private bool m_should_dispose; public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } } public ImageFormat SourceFormat { get { return null; } } public ImageMetaData Info { get { return m_info; } } + public PixelFormat Format { get; private set; } public ImageData Image { @@ -90,27 +97,66 @@ namespace GameRes.Formats.Foster { if (null == m_image) { - var pixels = Unpack(); - m_image = ImageData.Create (m_info, PixelFormats.Bgr24, null, pixels); + Unpack(); + m_image = ImageData.Create (m_info, Format, null, m_output); } return m_image; } } - public C24Decoder (IBinaryStream file, C24MetaData info, bool leave_open = false) + public CDecoderBase (IBinaryStream file, C24MetaData info, PixelFormat format, bool leave_open = false) { m_input = file; m_info = info; - m_output = new byte[3 * m_info.Width * m_info.Height]; + m_output = new byte[(info.BPP / 8) * (int)m_info.Width * (int)m_info.Height]; m_should_dispose = !leave_open; + Format = format; } - public byte[] Unpack () + protected uint[] ReadRows () { m_input.Position = m_info.DataOffset; var rows = new uint[m_info.Height]; for (int i = 0; i < rows.Length; ++i) rows[i] = m_input.ReadUInt32(); + return rows; + } + + protected abstract void Unpack (); + + #region IDisposable Members + bool m_disposed = false; + + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + + protected virtual void Dispose (bool disposing) + { + if (!m_disposed) + { + if (disposing && m_should_dispose) + { + m_input.Dispose(); + } + m_disposed = true; + } + } + #endregion + } + + internal sealed class C24Decoder : CDecoderBase + { + public C24Decoder (IBinaryStream file, C24MetaData info, bool leave_open = false) + : base (file, info, PixelFormats.Bgr24, leave_open) + { + } + + protected override void Unpack () + { + var rows = ReadRows(); int dst = 0; int width = (int)m_info.Width; foreach (uint row_offset in rows) @@ -140,23 +186,6 @@ namespace GameRes.Formats.Foster rle = !rle; } } - return m_output; } - - #region IDisposable Members - bool m_disposed = false; - public void Dispose () - { - if (!m_disposed) - { - if (m_should_dispose) - { - m_input.Dispose(); - } - m_disposed = true; - } - System.GC.SuppressFinalize (this); - } - #endregion } } diff --git a/ArcFormats/Foster/ImageC25.cs b/ArcFormats/Foster/ImageC25.cs new file mode 100644 index 00000000..66451371 --- /dev/null +++ b/ArcFormats/Foster/ImageC25.cs @@ -0,0 +1,112 @@ +//! \file ImageC25.cs +//! \date 2017 Nov 20 +//! \brief BeF game engine image format. +// +// 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.Windows.Media; + +namespace GameRes.Formats.Foster +{ + /// + /// ShiinaRio S25 predecessor. + /// + [Export(typeof(ImageFormat))] + public class C25Format : C24Format + { + public override string Tag { get { return "C25"; } } + public override string Description { get { return "BeF game engine image format"; } } + public override uint Signature { get { return 0x00353243; } } // 'C25' + public override bool CanWrite { get { return false; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (12); + int count = header.ToInt32 (4); + if (count <= 0) + return null; + return ReadMetaData (file, header.ToUInt32 (8), 32); + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + using (var reader = new C25Decoder (file, (C24MetaData)info, true)) + return reader.Image; + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("C25Format.Write not implemented"); + } + } + + internal class C25Decoder : CDecoderBase + { + public C25Decoder (IBinaryStream file, C24MetaData info, bool leave_open = false) + : base (file, info, PixelFormats.Bgra32, leave_open) + { + } + + protected override void Unpack () + { + var rows = ReadRows(); + int dst = 0; + int width = (int)m_info.Width; + foreach (uint row_offset in rows) + { + m_input.Position = row_offset; + for (int x = 0; x < width; ) + { + int count = m_input.ReadUInt8(); + if (count > 0x7F) + { + int bpp = 3; + count -= 0x80; + if (count >= 0x70) + { + bpp = 4; + count -= 0x70; + } + if (0 == count) + count = m_input.ReadUInt16(); + for (int i = 0; i < count; ++i) + { + m_input.Read (m_output, dst, bpp); + dst += bpp; + if (3 == bpp) + m_output[dst++] = 0xFF; + } + } + else + { + if (0 == count) + count = m_input.ReadUInt16(); + dst += count * 4; + } + x += count; + } + } + } + } +}