From 78da1d7ab84c0cd5eb341151d0038f719f45cb20 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 7 Sep 2023 12:26:07 +0400 Subject: [PATCH] (SakanaGL): support multiple archives. --- Experimental/SakanaGL/ArcSX.cs | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/Experimental/SakanaGL/ArcSX.cs b/Experimental/SakanaGL/ArcSX.cs index b88f5e8e..44ef1e65 100644 --- a/Experimental/SakanaGL/ArcSX.cs +++ b/Experimental/SakanaGL/ArcSX.cs @@ -28,13 +28,16 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using System.Linq; using System.Text; +using System.Text.RegularExpressions; namespace GameRes.Formats.Sakana { internal class SxEntry : PackedEntry { public ushort Flags; + public ushort ArcIndex; public bool IsEncrypted { get { return 0 == (Flags & 0x10); } } } @@ -49,13 +52,23 @@ namespace GameRes.Formats.Sakana public override bool CanWrite { get { return false; } } const uint DefaultKey = 0x2E76034B; + static readonly Regex ArchiveNameRe = new Regex (@"^(.*)-([^-]+)$"); public override ArcFile TryOpen (ArcView file) { - var base_name = Path.GetFileName (file.Name); + var base_name = Path.GetFileNameWithoutExtension (file.Name); var sx_name = base_name.Substring (0, 4) + "(00).sx"; sx_name = VFS.ChangeFileName (file.Name, sx_name); - if (!VFS.FileExists (sx_name) || file.Name.Equals (sx_name, StringComparison.InvariantCultureIgnoreCase)) + if (!VFS.FileExists (sx_name)) + { + var match = ArchiveNameRe.Match (base_name); + if (!match.Success) + return null; + sx_name = VFS.ChangeFileName (file.Name, match.Groups[1] + "(00).sx"); + if (!VFS.FileExists (sx_name)) + return null; + } + if (file.Name.Equals (sx_name, StringComparison.OrdinalIgnoreCase)) return null; byte[] index_data; using (var sx = VFS.OpenView (sx_name)) @@ -80,6 +93,8 @@ namespace GameRes.Formats.Sakana { var reader = new SxIndexDeserializer (index, file.MaxOffset); var dir = reader.Deserialize(); + if (null == dir || dir.Count == 0) + return null; return new ArcFile (file, this, dir); } } @@ -97,7 +112,11 @@ namespace GameRes.Formats.Sakana DecryptData (input, key_lo, key_hi); } if (sx_entry.IsPacked) + { input = UnpackZstd (input); + if (sx_entry.UnpackedSize == 0) + sx_entry.UnpackedSize = (uint)input.Length; + } return new BinMemoryStream (input, entry.Name); } @@ -170,7 +189,7 @@ namespace GameRes.Formats.Sakana m_dir = new List (count); for (int i = 0; i < count; ++i) { - m_index.ReadUInt16(); + ushort arc = Binary.BigEndian (m_index.ReadUInt16()); ushort flags = Binary.BigEndian (m_index.ReadUInt16()); uint offset = Binary.BigEndian (m_index.ReadUInt32()); uint size = Binary.BigEndian (m_index.ReadUInt32()); @@ -179,19 +198,21 @@ namespace GameRes.Formats.Sakana Offset = (long)offset << 4, Size = size, IsPacked = 0 != (flags & 0x03), + ArcIndex = arc, }; - if (!entry.CheckPlacement (m_max_offset)) - return null; m_dir.Add (entry); } - count = Binary.BigEndian (m_index.ReadUInt16()); - for (int i = 0; i < count; ++i) + int arc_count = Binary.BigEndian (m_index.ReadUInt16()); + int arc_index = -1; + for (int i = 0; i < arc_count; ++i) { m_index.ReadUInt32(); m_index.ReadUInt32(); m_index.ReadUInt32(); - Binary.BigEndian (m_index.ReadUInt32()); // archive body length + long arc_size = (long)Binary.BigEndian (m_index.ReadUInt32()) << 4; // archive body length + if (m_max_offset == arc_size) + arc_index = i; m_index.ReadUInt64(); m_index.Seek (16, SeekOrigin.Current); // MD5 sum } @@ -200,6 +221,10 @@ namespace GameRes.Formats.Sakana if (count > 0) m_index.Seek (count * 24, SeekOrigin.Current); DeserializeTree(); + if (arc_count > 1 && arc_index != -1) + { + return m_dir.Where (e => (e as SxEntry).ArcIndex == arc_index).ToList(); + } return m_dir; }