diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 85c46276..32c7c8a6 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -90,6 +90,9 @@ + + + diff --git a/ArcFormats/BlackRainbow/ArcCCF.cs b/ArcFormats/BlackRainbow/ArcCCF.cs new file mode 100644 index 00000000..74cf041b --- /dev/null +++ b/ArcFormats/BlackRainbow/ArcCCF.cs @@ -0,0 +1,76 @@ +//! \file ArcCCF.cs +//! \date Sun Feb 19 22:05:59 2017 +//! \brief Black Rainbow audio archive. +// +// 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; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.BlackRainbow +{ + [Export(typeof(ArchiveFormat))] + public class CcfOpener : ArchiveFormat + { + public override string Tag { get { return "CCF"; } } + public override string Description { get { return "BlackRainbow audio archive"; } } + public override uint Signature { get { return 0x22664343; } } // 'CCf"' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public CcfOpener () + { + Extensions = new string[] { "pak" }; + } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + + var base_name = Path.GetFileNameWithoutExtension (file.Name); + uint index_offset = 8; + long base_offset = index_offset + count * 4; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + uint offset = file.View.ReadUInt32 (index_offset); + index_offset += 4; + var name = string.Format ("{0}#{1:D4}", base_name, i); + var entry = AutoEntry.Create (file, base_offset+offset, name); + if (entry.Offset >= file.MaxOffset) + return null; + dir.Add (entry); + } + for (int i = 1; i < dir.Count; ++i) + { + dir[i-1].Size = (uint)(dir[i].Offset - dir[i-1].Offset); + } + var last_entry = dir[dir.Count-1]; + last_entry.Size = (uint)(file.MaxOffset - last_entry.Offset); + return new ArcFile (file, this, dir); + } + } +} diff --git a/ArcFormats/BlackRainbow/ArcIMP.cs b/ArcFormats/BlackRainbow/ArcIMP.cs new file mode 100644 index 00000000..f9948542 --- /dev/null +++ b/ArcFormats/BlackRainbow/ArcIMP.cs @@ -0,0 +1,147 @@ +//! \file ArcIMP.cs +//! \date Sun Feb 19 22:23:48 2017 +//! \brief Black Rainbow image archive. +// +// 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; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using GameRes.Compression; +using GameRes.Utility; + +namespace GameRes.Formats.BlackRainbow +{ + internal class ImpArchive : ArcFile + { + public readonly uint Key; + + public ImpArchive (ArcView arc, ArchiveFormat impl, ICollection dir, uint key) + : base (arc, impl, dir) + { + Key = key; + } + } + + [Export(typeof(ArchiveFormat))] + public class ImpOpener : ArchiveFormat + { + public override string Tag { get { return "IMP"; } } + public override string Description { get { return "BlackRainbow image archive"; } } + public override uint Signature { get { return 0x3D66; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public ImpOpener () + { + Signatures = KnownSchemes.Keys; + } + + static readonly Dictionary KnownSchemes = new Dictionary + { + { 0x3D66, 0xCE032ADB }, // Kannagi + { 0x59E8, 0xD36050EC }, // From M + }; + + public override ArcFile TryOpen (ArcView file) + { + uint key = KnownSchemes[file.View.ReadUInt32 (0)]; + var base_name = Path.GetFileNameWithoutExtension (file.Name); + uint base_offset = 0x404; + uint offset = file.View.ReadUInt32 (4); + uint index_offset = 8; + var dir = new List(); + for (int i = 0; i < 0xFF; ++i) + { + uint next_offset = file.View.ReadUInt32 (index_offset); + uint size = next_offset - offset; + if (size > 0x10) + { + var entry = new Entry { + Name = string.Format ("{0}#{1:D3}", base_name, i), + Type = "image", + Offset = base_offset + offset, + Size = size, + }; + dir.Add (entry); + } + index_offset += 4; + offset = next_offset; + } + if (0 == dir.Count) + return null; + return new ImpArchive (file, this, dir, key); + } + + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var imp_arc = (ImpArchive)arc; + var offset = entry.Offset; + var info = new ImpMetaData + { + Width = arc.File.View.ReadUInt32 (offset), + Height = arc.File.View.ReadUInt32 (offset+4), + BPP = 32, + Key = imp_arc.Key, + HasAlpha = arc.File.View.ReadUInt32 (offset+12) != 0, + }; + uint packed_size = arc.File.View.ReadUInt32 (offset+8); + var input = arc.File.CreateStream (offset, packed_size+0x10); + return new ImpDecoder (input, info); + } + } + + internal class ImpMetaData : ImageMetaData + { + public uint Key; + public bool HasAlpha; + } + + internal sealed class ImpDecoder : BinaryImageDecoder + { + byte[] m_key; + bool m_has_alpha; + + public ImpDecoder (IBinaryStream input, ImpMetaData info) : base (input, info) + { + m_has_alpha = info.HasAlpha; + m_key = new byte[4]; + LittleEndian.Pack (info.Key, m_key, 0); + } + + protected override ImageData GetImageData () + { + m_input.Position = 0x10; + var pixels = new byte[Info.Width * Info.Height * 4]; + using (var lzs = new ByteStringEncryptedStream (m_input.AsStream, m_key)) + using (var input = new LzssStream (lzs)) + { + if (pixels.Length != input.Read (pixels, 0, pixels.Length)) + throw new InvalidFormatException(); + var format = m_has_alpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32; + return ImageData.Create (Info, format, null, pixels); + } + } + } +} diff --git a/ArcFormats/BlackRainbow/ArcSPPAK.cs b/ArcFormats/BlackRainbow/ArcSPPAK.cs new file mode 100644 index 00000000..5a306813 --- /dev/null +++ b/ArcFormats/BlackRainbow/ArcSPPAK.cs @@ -0,0 +1,104 @@ +//! \file ArcSPPAK.cs +//! \date Mon Feb 20 07:45:17 2017 +//! \brief Black Rainbow script archive. +// +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using GameRes.Utility; + +namespace GameRes.Formats.BlackRainbow +{ + internal class SpArchive : ArcFile + { + public readonly byte Key; + + public SpArchive (ArcView arc, ArchiveFormat impl, ICollection dir, byte key) + : base (arc, impl, dir) + { + Key = key; + } + } + + [Export(typeof(ArchiveFormat))] + public class SpPakOpener : ArchiveFormat + { + public override string Tag { get { return "PAK/SP"; } } + public override string Description { get { return "BlackRainbow script archive"; } } + public override uint Signature { get { return 0x69695669; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public SpPakOpener () + { + Signatures = KnownSchemes.Keys; + } + + static readonly Dictionary KnownSchemes = new Dictionary + { + { 0x69695669, 0x07 }, // Kannagi + { 0x8492E36F, 0x9C }, // From M + }; + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + byte key = KnownSchemes[file.View.ReadUInt32 (0)]; + + var base_name = Path.GetFileNameWithoutExtension (file.Name); + uint index_offset = 8; + long base_offset = index_offset + 4 * count; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = string.Format ("{0}#{1:D4}", base_name, i); + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = base_offset + file.View.ReadUInt32 (index_offset); + index_offset += 4; + dir.Add (entry); + } + for (int i = 1; i < dir.Count; ++i) + { + dir[i-1].Size = (uint)(dir[i].Offset - dir[i-1].Offset); + } + var last_entry = dir[dir.Count-1]; + last_entry.Size = (uint)(file.MaxOffset - last_entry.Offset); + return new SpArchive (file, this, dir, key); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var sp_arc = (SpArchive)arc; + var key = sp_arc.Key; + var data = arc.File.View.ReadBytes (entry.Offset, entry.Size); + for (int i = 0; i < data.Length; ++i) + { + data[i] = Binary.RotByteR ((byte)(data[i] ^ key), 2); + } + return new BinMemoryStream (data, entry.Name); + } + } +}