From b2be6ae892cfeca1dc73d64d35b70a4d3e395ea1 Mon Sep 17 00:00:00 2001 From: morkt Date: Sun, 2 Sep 2018 23:52:17 +0400 Subject: [PATCH] (Legacy): PKZ archives, JBP images and KOG audio. --- Legacy/Legacy.csproj | 6 ++ Legacy/Sviu/ArcPKZ.cs | 186 ++++++++++++++++++++++++++++++++++++++++ Legacy/Sviu/AudioKOG.cs | 55 ++++++++++++ Legacy/Sviu/ImageGBP.cs | 116 +++++++++++++++++++++++++ Legacy/Sviu/ImageJBP.cs | 62 ++++++++++++++ 5 files changed, 425 insertions(+) create mode 100644 Legacy/Sviu/ArcPKZ.cs create mode 100644 Legacy/Sviu/AudioKOG.cs create mode 100644 Legacy/Sviu/ImageGBP.cs create mode 100644 Legacy/Sviu/ImageJBP.cs diff --git a/Legacy/Legacy.csproj b/Legacy/Legacy.csproj index 334f51c9..8993bbdd 100644 --- a/Legacy/Legacy.csproj +++ b/Legacy/Legacy.csproj @@ -70,11 +70,16 @@ + + + + + @@ -115,6 +120,7 @@ + diff --git a/Legacy/Sviu/ArcPKZ.cs b/Legacy/Sviu/ArcPKZ.cs new file mode 100644 index 00000000..f1f227fb --- /dev/null +++ b/Legacy/Sviu/ArcPKZ.cs @@ -0,0 +1,186 @@ +//! \file ArcPKZ.cs +//! \date 2018 Aug 26 +//! \brief SVIU System resource archive. +// +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using GameRes.Utility; + +// [030725][Hayashigumi] Fall in Love + +namespace GameRes.Formats.Sviu +{ + [Serializable] + public class PkzScheme : ResourceScheme + { + public Dictionary KnownSchemes; + } + + internal class PkzArchive : ArcFile + { + public readonly byte[] Key; + + public PkzArchive (ArcView arc, ArchiveFormat impl, ICollection dir, byte[] key) + : base (arc, impl, dir) + { + Key = key; + } + } + + [Export(typeof(ArchiveFormat))] + public class PkzOpener : ArchiveFormat + { + public override string Tag { get { return "PKZ"; } } + public override string Description { get { return "SVIU System resource archive"; } } + public override uint Signature { get { return 0x305A4B50; } } // 'PKZ0' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + PkzScheme DefaultScheme = new PkzScheme { KnownSchemes = new Dictionary() }; + + public override ResourceScheme Scheme + { + get { return DefaultScheme; } + set { DefaultScheme = (PkzScheme)value; } + } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + var key = QueryKey(); + if (null == key) + return null; + uint data_offset = (uint)count * 0x2Cu + 0x14u; + var index = file.View.ReadBytes (8, data_offset - 8); + DecryptData (index, key); + var dir = new List (count); + int index_offset = 0xC; + for (int i = 0; i < count; ++i) + { + var name = Binary.GetCString (index, index_offset, 0x20); + var entry = FormatCatalog.Instance.Create (name); + entry.Size = index.ToUInt32 (index_offset+0x20); + entry.Offset = index.ToUInt32 (index_offset+0x24) + data_offset; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x2C; + } + return new PkzArchive (file, this, dir, key); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var parc = (PkzArchive)arc; + var data = arc.File.View.ReadBytes (entry.Offset, entry.Size); + DecryptData (data, parc.Key); + if (data.AsciiEqual (0, "SVS18")) + data = UnpackScript (data); + return new BinMemoryStream (data, entry.Name); + } + + byte[] UnpackScript (byte[] data) + { + if (data.ToInt32 (0x10) == 0) + return data; + int unpacked_size = data.ToInt32 (0xC); + int header_size = data.ToInt32 (0x14); + int packed_size = data.ToInt32 (0x1C); + var output = new byte[unpacked_size]; + Buffer.BlockCopy (data, 0, output, 0, header_size); + LittleEndian.Pack (0, output, 0x10); + using (var input = new BinMemoryStream (data, header_size, packed_size)) + LzUnpack (input, output, header_size); + return output; + } + + void LzUnpack (IBinaryStream input, byte[] output, int dst) + { + var frame = new byte[0x800]; + int frame_pos = 0x7E8; + int ctl = 1; + while (dst < output.Length) + { + if (1 == ctl) + { + ctl = input.ReadByte(); + if (-1 == ctl) + break; + ctl |= 0x100; + } + if (0 != (ctl & 1)) + { + byte b = input.ReadUInt8(); + output[dst++] = b; + frame[frame_pos++ & 0x7FF] = b; + } + else + { + byte lo = input.ReadUInt8(); + byte hi = input.ReadUInt8(); + int offset = lo | (hi & 0xE0) << 3; + int count = (hi & 0x1F) + 2; + for (int i = 0; i < count; ++i) + { + byte b = frame[(offset + i) & 0x7FF]; + output[dst++] = b; + frame[frame_pos++ & 0x7FF] = b; + } + } + ctl >>= 1; + } + } + + public static byte[] CreateKey (string key) + { + var bkey = Encodings.cp932.GetBytes (key); + for (int i = 0; i < bkey.Length; ++i) + { + bkey[i] += 6; + } + return bkey; + } + + void DecryptData (byte[] data, byte[] key) + { + for (int i = 0; i < data.Length; ++i) + { + data[i] ^= key[i % key.Length]; + data[i] += 0x80; + } + } + + byte[] QueryKey () + { + if (DefaultScheme.KnownSchemes.Count == 0) + return null; + return DefaultScheme.KnownSchemes.Values.First(); + } + } +} diff --git a/Legacy/Sviu/AudioKOG.cs b/Legacy/Sviu/AudioKOG.cs new file mode 100644 index 00000000..a98feeb3 --- /dev/null +++ b/Legacy/Sviu/AudioKOG.cs @@ -0,0 +1,55 @@ +//! \file AudioKOG.cs +//! \date 2018 Aug 27 +//! \brief SVIU System audio (OGG) +// +// 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; + +namespace GameRes.Formats.Sviu +{ + [Export(typeof(AudioFormat))] + public class KogAudio : AudioFormat + { + public override string Tag { get { return "KOG"; } } + public override string Description { get { return "SVIU System audio format (Ogg/Vorbis)"; } } + public override uint Signature { get { return 0; } } + public override bool CanWrite { get { return false; } } + + public override SoundInput TryOpen (IBinaryStream file) + { + if (file.Signature != 0) + return null; + var header = file.ReadHeader (8); + int header_size = header.ToInt32 (4); + file.Position = header_size; + uint signature = file.ReadUInt32(); + if (signature != OggAudio.Instance.Signature) + return null; + var input = new StreamRegion (file.AsStream, header_size); + var ogg = new BinaryStream (input, file.Name); + return new OggInput (ogg); + } + } +} diff --git a/Legacy/Sviu/ImageGBP.cs b/Legacy/Sviu/ImageGBP.cs new file mode 100644 index 00000000..cba7d15f --- /dev/null +++ b/Legacy/Sviu/ImageGBP.cs @@ -0,0 +1,116 @@ +//! \file ImageGBP.cs +//! \date 2018 Aug 27 +//! \brief SVIU System 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; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; + +namespace GameRes.Formats.Sviu +{ + internal class GbpMetaData : ImageMetaData + { + public int HeaderSize; + public int Method; + } + + [Export(typeof(ImageFormat))] + public class GbpFormat : ImageFormat + { + public override string Tag { get { return "GBP"; } } + public override string Description { get { return "SVIU system image format"; } } + public override uint Signature { get { return 0x50425947; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x14); + file.Seek (-0x13, SeekOrigin.End); + var key = file.ReadBytes (0x13); + for (int i = 4; i < 0x14; i += 2) + { + header[i] ^= key[0x10]; + header[i+1] ^= key[0x11]; + } + for (int i = 0; i < 0x10; ++i) + { + header[i+4] -= key[i]; + } + return new GbpMetaData { + Width = header.ToUInt16 (0xE), + Height = header.ToUInt16 (0x10), + BPP = header.ToUInt16 (0x12), + HeaderSize = header.ToInt32 (4), + Method = header.ToUInt16 (0xC), + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new GbpReader (file, (GbpMetaData)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 ("GbpFormat.Write not implemented"); + } + } + + internal class GbpReader + { + IBinaryStream m_input; + GbpMetaData m_info; + byte[] m_output; + + public PixelFormat Format { get; private set; } + + public GbpReader (IBinaryStream input, GbpMetaData info) + { + m_input = input; + m_info = info; + if (32 == info.BPP) + Format = PixelFormats.Bgra32; + else + Format = PixelFormats.Bgr32; + m_output = new byte[4 * (int)m_info.Width * (int)m_info.Height]; + } + + public byte[] Unpack () + { + m_input.Position = m_info.HeaderSize; + if (3 == m_info.Method) + UnpackV3(); + else + throw new NotImplementedException(); + return m_output; + } + + void UnpackV3 () + { + throw new NotImplementedException(); + } + } +} diff --git a/Legacy/Sviu/ImageJBP.cs b/Legacy/Sviu/ImageJBP.cs new file mode 100644 index 00000000..3179a00a --- /dev/null +++ b/Legacy/Sviu/ImageJBP.cs @@ -0,0 +1,62 @@ +//! \file ImageJBP.cs +//! \date 2018 Aug 27 +//! \brief SVIU System 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.Sviu +{ + [Export(typeof(ImageFormat))] + public class JbpFormat : ImageFormat + { + public override string Tag { get { return "JBP"; } } + public override string Description { get { return "SVIU System image format"; } } + public override uint Signature { get { return 0x3150424A; } } // 'JBP1' + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x20); + return new ImageMetaData { + Width = header.ToUInt16 (0x10), + Height = header.ToUInt16 (0x12), + BPP = 24, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var input = file.ReadBytes ((int)file.Length); + var reader = new Purple.JbpReader (input, 0); + var pixels = reader.Unpack(); + return ImageData.Create (info, PixelFormats.Bgr32, null, pixels, reader.Stride); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("JbpFormat.Write not implemented"); + } + } +}