From 115ac939f58bc3b5307bc5a60a22fdc05ac46260 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 25 Jun 2015 05:40:55 +0400 Subject: [PATCH] implemented Cherry Soft resources. --- ArcFormats/ArcCherry.cs | 92 +++++++++++ ArcFormats/ArcFormats.csproj | 2 + ArcFormats/ImageGRP.cs | 223 ++++++++++++++++++++++++++ ArcFormats/Properties/AssemblyInfo.cs | 4 +- supported.html | 9 +- 5 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 ArcFormats/ArcCherry.cs create mode 100644 ArcFormats/ImageGRP.cs diff --git a/ArcFormats/ArcCherry.cs b/ArcFormats/ArcCherry.cs new file mode 100644 index 00000000..c0742718 --- /dev/null +++ b/ArcFormats/ArcCherry.cs @@ -0,0 +1,92 @@ +//! \file ArcCherry.cs +//! \date Wed Jun 24 21:22:56 2015 +//! \brief Cherry Soft archives implementation. +// +// 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 System.Linq; +using GameRes.Utility; + +namespace GameRes.Formats.Cherry +{ + [Export(typeof(ArchiveFormat))] + public class PakOpener : ArchiveFormat + { + public override string Tag { get { return "PAK/CHERRY"; } } + public override string Description { get { return "Cherry Soft resource archive"; } } + public override uint Signature { get { return 0x52454843; } } // 'CHER' + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + public PakOpener () + { + Extensions = new string[] { "pak" }; + Signatures = new uint[] { 0x52454843, 0 }; + } + + public override ArcFile TryOpen (ArcView file) + { + int index_offset = 0; + if (file.View.AsciiEqual (0, "CHERRY PACK 2.0")) + index_offset = 0x14; + int count = file.View.ReadInt32 (index_offset); + if (count <= 0 || count > 0xfffff) + return null; + long base_offset = file.View.ReadUInt32 (index_offset+4); + index_offset += 8; + uint index_size = (uint)count * 0x18u; + if (index_size > file.View.Reserve (index_offset, index_size)) + return null; + string arc_name = Path.GetFileNameWithoutExtension (file.Name); + bool is_grp = arc_name.EndsWith ("GRP", StringComparison.InvariantCultureIgnoreCase); + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + string name = file.View.ReadString (index_offset, 0x10); + var offset = base_offset + file.View.ReadUInt32 (index_offset+0x10); + Entry entry; + if (is_grp) + { + entry = new Entry { + Name = Path.ChangeExtension (name, "grp"), + Type = "image", + Offset = offset + }; + } + else + { + entry = AutoEntry.Create (file, offset, name); + } + entry.Size = file.View.ReadUInt32 (index_offset+0x14); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x18; + } + return new ArcFile (file, this, dir); + } + } +} diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 8ca55b88..635eb509 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -74,6 +74,7 @@ + @@ -183,6 +184,7 @@ + diff --git a/ArcFormats/ImageGRP.cs b/ArcFormats/ImageGRP.cs new file mode 100644 index 00000000..60658da5 --- /dev/null +++ b/ArcFormats/ImageGRP.cs @@ -0,0 +1,223 @@ +//! \file ImageGRP.cs +//! \date Wed Jun 24 22:14:41 2015 +//! \brief Cherry Soft compressed 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.Diagnostics; +using System.IO; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using GameRes.Utility; + +namespace GameRes.Formats.Cherry +{ + internal class GrpMetaData : ImageMetaData + { + public int PackedSize; + public int UnpackedSize; + public int Offset; + } + + [Export(typeof(ImageFormat))] + public class GrpFormat : ImageFormat + { + public override string Tag { get { return "GRP/CHERRY"; } } + public override string Description { get { return "Cherry Soft comprressed image format"; } } + public override uint Signature { get { return 0; } } + + public GrpFormat () + { + Extensions = new string[] { "grp" }; + } + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x18]; + if (header.Length != stream.Read (header, 0, header.Length)) + return null; + uint width = LittleEndian.ToUInt32 (header, 0); + uint height = LittleEndian.ToUInt32 (header, 4); + int bpp = LittleEndian.ToInt32 (header, 8); + int packed_size = LittleEndian.ToInt32 (header, 0x0C); + int unpacked_size = LittleEndian.ToInt32 (header, 0x10); + if (0 == width || 0 == height || width > 0x7fff || height > 0x7fff + || (bpp != 24 && bpp != 8) + || unpacked_size <= 0 || packed_size < 0) + return null; + return new GrpMetaData + { + Width = width, + Height = height, + BPP = bpp, + PackedSize = packed_size, + UnpackedSize = unpacked_size, + Offset = LittleEndian.ToInt32 (header, 0x14), + }; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var meta = info as GrpMetaData; + if (null == meta) + throw new ArgumentException ("GrpFormat.Read should be supplied with GrpMetaData", "info"); + var reader = new GrpReader (stream, meta); + return reader.CreateImage(); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("GrpFormat.Write not implemented"); + } + } + + internal class GrpReader + { + GrpMetaData m_info; + Stream m_input; + byte[] m_image_data; + int m_stride; + + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } + public byte[] Pixels { get { return m_image_data; } } + + public GrpReader (Stream input, GrpMetaData info) + { + m_info = info; + m_input = input; + PixelFormat format; + if (8 == info.BPP) + Format = PixelFormats.Indexed8; + else if (24 == info.BPP) + Format = PixelFormats.Bgr24; + else + throw new NotSupportedException ("Not supported GRP image depth"); + m_stride = (int)m_info.Width*((Format.BitsPerPixel+7)/8); + } + + public ImageData CreateImage () + { + m_input.Position = 0x18; + int data_size = m_info.UnpackedSize; + if (m_info.PackedSize != 0) + data_size = m_info.PackedSize; + + if (0x0f0f0f0f == m_info.Offset && 0x18 + data_size == m_input.Length) + return ReadV2(); + else if (8 == m_info.BPP && 0x418 == m_info.Offset || + 24 == m_info.BPP && 0x018 == m_info.Offset) + return ReadV1(); + else + throw new InvalidFormatException(); + } + + private ImageData ReadV1 () + { + if (8 == m_info.BPP) + { + var palette_data = new byte[0x400]; + if (palette_data.Length != m_input.Read (palette_data, 0, palette_data.Length)) + throw new InvalidFormatException ("Unexpected end of file"); + SetPalette (palette_data); + } + var packed = new byte[m_info.PackedSize]; + if (packed.Length != m_input.Read (packed, 0, packed.Length)) + throw new InvalidFormatException ("Unexpected end of file"); + for (int i = 0; i < packed.Length; ++i) + packed[i] ^= (byte)i; + + using (var input = new MemoryStream (packed)) + using (var reader = new LzssReader (input, packed.Length, m_info.UnpackedSize)) + { + reader.Unpack(); + m_image_data = new byte[m_info.UnpackedSize]; + // flip pixels vertically + int dst = 0; + for (int src = m_stride * ((int)m_info.Height-1); src >= 0; src -= m_stride) + { + Buffer.BlockCopy (reader.Data, src, m_image_data, dst, m_stride); + dst += m_stride; + } + } + return ImageData.Create (m_info, Format, Palette, m_image_data); + } + + private ImageData ReadV2 () + { + if (0 != m_info.PackedSize) + { + using (var reader = new LzssReader (m_input, m_info.PackedSize, m_info.UnpackedSize)) + { + reader.Unpack(); + m_image_data = reader.Data; + } + } + else + { + m_image_data = new byte[m_info.UnpackedSize]; + if (m_image_data.Length != m_input.Read (m_image_data, 0, m_image_data.Length)) + throw new InvalidFormatException ("Unexpected end of file"); + } + int pixels_offset = 0; + if (8 == m_info.BPP) + { + SetPalette (m_image_data); + pixels_offset += 0x400; + } + + if (0 == pixels_offset) + return ImageData.Create (m_info, Format, Palette, m_image_data); + + if (pixels_offset + m_stride*(int)m_info.Height > m_image_data.Length) + throw new InvalidFormatException(); + unsafe + { + fixed (byte* pixels = &m_image_data[pixels_offset]) + { + var bitmap = BitmapSource.Create ((int)m_info.Width, (int)m_info.Height, + ImageData.DefaultDpiX, ImageData.DefaultDpiY, Format, Palette, + (IntPtr)pixels, m_image_data.Length-pixels_offset, m_stride); + bitmap.Freeze(); + return new ImageData (bitmap, m_info); + } + } + } + + private void SetPalette (byte[] palette_data) + { + if (palette_data.Length < 0x400) + throw new InvalidFormatException(); + + var palette = new Color[0x100]; + for (int i = 0; i < palette.Length; ++i) + { + int c = i * 4; + palette[i] = Color.FromRgb (palette_data[c+2], palette_data[c+1], palette_data[c]); + } + Palette = new BitmapPalette (palette); + } + } +} diff --git a/ArcFormats/Properties/AssemblyInfo.cs b/ArcFormats/Properties/AssemblyInfo.cs index f370b1dc..4f57f0da 100644 --- a/ArcFormats/Properties/AssemblyInfo.cs +++ b/ArcFormats/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.0.7.67")] -[assembly: AssemblyFileVersion ("1.0.7.67")] +[assembly: AssemblyVersion ("1.0.7.68")] +[assembly: AssemblyFileVersion ("1.0.7.68")] diff --git a/supported.html b/supported.html index 40508b80..17fe14c9 100644 --- a/supported.html +++ b/supported.html @@ -155,7 +155,10 @@ Itsuka, Dokoka de ~Ano Ameoto no Kioku~
*.s25S25No *.ogvOGVNo *.padPADNo -*ARC2
ARC1NoASTJokyoushi wo Kurau +*ARC2
ARC1NoAST +Jokyoushi wo Kurau
+Time Trouble ~Marie ni Kubikkake~
+ *.dat-NoAilRagna☆彡Science *.lpkLPK1NoLucifen Doki Doki Rooming
@@ -213,6 +216,10 @@ Maid no Yakata ~Zetsubou Hen~
*.crxCRXGNo *.pcmXPCMNo +*.pakCHERRY PACK 2.0
-NoCherry +Double
+Kimon Youitan
+

[1] Non-encrypted only