From 725c01498c665df988962de99a65e875605ce3b6 Mon Sep 17 00:00:00 2001 From: morkt Date: Wed, 22 Jul 2015 09:30:14 +0400 Subject: [PATCH] implemented AdvSys_T formats. FPK/MFWY archives and GR2 images. --- ArcFormats/ArcAdvSysT.cs | 74 +++++++++ ArcFormats/ArcFormats.csproj | 2 + ArcFormats/ImageGR2.cs | 295 +++++++++++++++++++++++++++++++++++ supported.html | 5 + 4 files changed, 376 insertions(+) create mode 100644 ArcFormats/ArcAdvSysT.cs create mode 100644 ArcFormats/ImageGR2.cs diff --git a/ArcFormats/ArcAdvSysT.cs b/ArcFormats/ArcAdvSysT.cs new file mode 100644 index 00000000..0ee96d1a --- /dev/null +++ b/ArcFormats/ArcAdvSysT.cs @@ -0,0 +1,74 @@ +//! \file ArcAdvSysT.cs +//! \date Tue Jul 21 03:42:25 2015 +//! \brief AdvSys FPK resource archive. +// +// Copyright (C) 2015 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using GameRes.Utility; + +namespace GameRes.Formats.AdvSys +{ + [Export(typeof(ArchiveFormat))] + public class FpkOpener : ArchiveFormat + { + public override string Tag { get { return "FPK/MFWY"; } } + public override string Description { get { return "AdvSys_T engine resource archive"; } } + public override uint Signature { get { return 0x5957464D; } } // 'MFWY' + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + public FpkOpener () + { + Extensions = new string[] { "fpk" }; + } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + uint data_offset = file.View.ReadUInt32 (8); + if (data_offset < 0x10 + count * 0x20) + return null; + if (data_offset > file.View.Reserve (0, data_offset)) + return null; + uint index_offset = 0x10; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = file.View.ReadString (index_offset, 0x18); + var entry = FormatCatalog.Instance.CreateEntry (name); + entry.Size = file.View.ReadUInt32 (index_offset+0x18); + entry.Offset = file.View.ReadUInt32 (index_offset+0x1C); + if (entry.Offset < data_offset || !entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x20; + } + return new ArcFile (file, this, dir); + } + } +} diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 0e57c509..40a2c16a 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -66,6 +66,7 @@ + @@ -200,6 +201,7 @@ + diff --git a/ArcFormats/ImageGR2.cs b/ArcFormats/ImageGR2.cs new file mode 100644 index 00000000..40ee74b7 --- /dev/null +++ b/ArcFormats/ImageGR2.cs @@ -0,0 +1,295 @@ +//! \file ImageGR2.cs +//! \date Tue Jul 21 03:54:51 2015 +//! \brief AdvSys engine image format. +// +// Copyright (C) 2015 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 GameRes.Utility; + +namespace GameRes.Formats.AdvSys +{ + [Export(typeof(ImageFormat))] + public class Gr2Format : ImageFormat + { + public override string Tag { get { return "GR2"; } } + public override string Description { get { return "AdvSys engine image format"; } } + public override uint Signature { get { return 0x5F325247; } } // 'GR2_' + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[16]; + if (16 != stream.Read (header, 0, 16)) + return null; + return new ImageMetaData + { + Width = LittleEndian.ToUInt16 (header, 4), + Height = LittleEndian.ToUInt16 (header, 6), + BPP = LittleEndian.ToInt16 (header, 12) * 8 + }; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + stream.Position = 0x10; + int stride = ((int)info.Width * info.BPP/8 + 3) & ~3; + var pixels = new byte[stride * info.Height]; + if (pixels.Length != stream.Read (pixels, 0, pixels.Length)) + throw new InvalidFormatException ("Unexpected end of file"); + PixelFormat format; + switch (info.BPP) + { + case 32: format = PixelFormats.Bgra32; break; + case 24: format = PixelFormats.Bgr24; break; + case 16: format = PixelFormats.Bgr565; break; + default: throw new NotSupportedException ("Not supported image bitdepth"); + } + return ImageData.Create (info, format, null, pixels, stride); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("Gr2Format.Write not implemented"); + } + } + + internal class PolaMetaData : ImageMetaData + { + public int UnpackedSize; + } + + [Export(typeof(ImageFormat))] + public class PolaFormat : Gr2Format + { + public override string Tag { get { return "GR2/Pola"; } } + public override string Description { get { return "AdvSys engine compressed image format"; } } + public override uint Signature { get { return 0x6C6F502A; } } // '*Pola*' + + public PolaFormat () + { + Extensions = new string[] { "gr2" }; + } + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[20]; + if (20 != stream.Read (header, 0, 20)) + return null; + if (!Binary.AsciiEqual (header, "*Pola* ")) + return null; + int unpacked_size = LittleEndian.ToInt32 (header, 8); + using (var reader = new PolaReader (stream, 64)) + { + reader.Unpack(); + using (var temp = new MemoryStream (reader.Data)) + { + var info = base.ReadMetaData (temp); + if (null == info) + return null; + return new PolaMetaData + { + Width = info.Width, + Height = info.Height, + BPP = info.BPP, + UnpackedSize = unpacked_size, + }; + } + } + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var meta = info as PolaMetaData; + if (null == meta) + throw new ArgumentException ("PolaFormat.Read should be supplied with PolaMetaData", "info"); + + stream.Position = 0x14; + using (var reader = new PolaReader (stream, meta.UnpackedSize)) + { + reader.Unpack(); + using (var temp = new MemoryStream (reader.Data)) + return base.Read (temp, info); + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("PolaFormat.Write not implemented"); + } + } + + internal sealed class PolaReader : IDisposable + { + BinaryReader m_input; + byte[] m_output; + + public byte[] Data { get { return m_output; } } + + public PolaReader (Stream input, int unpacked_size) + { + m_input = new ArcView.Reader (input); + m_output = new byte[unpacked_size+2]; + } + + public void Unpack () + { + NextBit(); + int dst = 0; + while (dst < m_output.Length-2) + { + if (0 != NextBit()) + { + m_output[dst++] = m_input.ReadByte(); + continue; + } + int offset, count = 0; + + if (0 != NextBit()) + { + offset = m_input.ReadByte() - 256; + + if (0 == NextBit()) + { + count += 256; + } + if (0 == NextBit()) + { + offset -= 512; + if (0 == NextBit()) + { + count *= 2; + if (0 == NextBit()) + count += 256; + offset -= 512; + if (0 == NextBit()) + { + count *= 2; + if (0 == NextBit()) + count += 256; + offset -= 1024; + if (0 == NextBit()) + { + offset -= 2048; + count *= 2; + if (0 == NextBit()) + count += 256; + } + } + } + } + + offset -= count; + if (0 != NextBit()) + count = 3; + else if (0 != NextBit()) + count = 4; + else if (0 != NextBit()) + count = 5; + else if (0 != NextBit()) + count = 6; + else if (0 != NextBit()) + { + if (0 != NextBit()) + count = 8; + else + count = 7; + } + else if (0 == NextBit()) + { + count = 9; + if (0 != NextBit()) + count += 4; + if (0 != NextBit()) + count += 2; + if (0 != NextBit()) + ++count; + } + else + { + count = m_input.ReadByte() + 17; + } + if (dst + count > m_output.Length) + count = m_output.Length - dst; + Binary.CopyOverlapped (m_output, dst + offset, dst, count); + dst += count; + } + else + { + offset = m_input.ReadByte() - 256; + if (0 == NextBit()) + { + if (offset != -1) + { + int src = dst + offset; + m_output[dst++] = m_output[src++]; + m_output[dst++] = m_output[src++]; + } + else if (0 == NextBit()) + break; + } + else + { + offset -= 256; + if (0 == NextBit()) + offset -= 1024; + if (0 == NextBit()) + offset -= 512; + if (0 == NextBit()) + offset -= 256; + int src = dst + offset; + m_output[dst++] = m_output[src++]; + m_output[dst++] = m_output[src++]; + } + } + } + } + + int m_flag = 2; + + int NextBit () + { + int bit = m_flag & 1; + m_flag >>= 1; + if (1 == m_flag) + { + m_flag = m_input.ReadUInt16() | 0x10000; + } + return bit; + } + + #region IDisposable Members + bool m_disposed = false; + public void Dispose () + { + if (!m_disposed) + { + m_input.Dispose(); + m_disposed = true; + } + } + #endregion + } +} + diff --git a/supported.html b/supported.html index 0bcc8f37..856ed1ff 100644 --- a/supported.html +++ b/supported.html @@ -186,6 +186,7 @@ Rikorisu ~Lycoris Radiata~
*.pmp
*.pmw-YesScenePlayerNyuujoku Hitozuma Jogakuen *.datGAMEDAT PACK
GAMEDAT PAC2No bootUP!
Pajamas Soft Aneimo 2 ~Second Stage~
+Natsu no Owari no Nirvana
Prism Heart
*.epaEPNo @@ -257,6 +258,10 @@ Jokei Kazoku ~Inbou~
Fetish 2: Omote no Kioku/Ura no Kioku
*.grxGRX\x1a
SGX\x1aNo +*.fpkMFWYNoCaligula +Shinsetsu Ryouki no Ori
+ +*.gr2GR2_
*Pola*No

[1] Non-encrypted only