From d621cf4c03004a276c21c376519e17b9bcba4a09 Mon Sep 17 00:00:00 2001 From: MkfsSion Date: Sat, 31 Aug 2024 00:39:40 +0800 Subject: [PATCH] ArcFormats: Support S5IC archieve * Tested game: https://vndb.org/r119690 Signed-off-by: MkfsSion --- ArcFormats/Eushully/ArcALF.cs | 73 +++++++++++++++++++++++++++-------- GameRes/BinaryStream.cs | 26 +++++++++++-- 2 files changed, 78 insertions(+), 21 deletions(-) diff --git a/ArcFormats/Eushully/ArcALF.cs b/ArcFormats/Eushully/ArcALF.cs index 7bae12bd..dae9d110 100644 --- a/ArcFormats/Eushully/ArcALF.cs +++ b/ArcFormats/Eushully/ArcALF.cs @@ -27,8 +27,9 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; -using GameRes.Utility; using GameRes.Compression; +using System.Text; +using System.Linq; namespace GameRes.Formats.Eushully { @@ -65,6 +66,7 @@ namespace GameRes.Formats.Eushully internal IEnumerable GetIndexNames (string alf_name) { + yield return "sys5ini.bin"; yield return "sys4ini.bin"; yield return "sys3ini.bin"; yield return Path.ChangeExtension (alf_name, "AAI"); @@ -72,6 +74,44 @@ namespace GameRes.Formats.Eushully Tuple>> LastAccessedIndex; + + internal class AGEArchiveInfo + { + public readonly byte[] signature; + public readonly int offset; + public readonly bool isNameUnicode; + public readonly bool isLZSSCompressed; + + public AGEArchiveInfo(byte[] Signature, int Offset, bool IsNameUnicode, bool IsLZSSCompressed) + { + signature = Signature; + offset = Offset; + isNameUnicode = IsNameUnicode; + isLZSSCompressed = IsLZSSCompressed; + } + } + + static AGEArchiveInfo[] infos = + { + new AGEArchiveInfo(Encoding.ASCII.GetBytes("S3IN"), 0x12C, false, false), + new AGEArchiveInfo(Encoding.ASCII.GetBytes("S3IC"), 0x134, false, true), + new AGEArchiveInfo(Encoding.ASCII.GetBytes("S4IC"), 0x134, false, true), + new AGEArchiveInfo(Encoding.ASCII.GetBytes("S4AC"), 0x114, false, true), + new AGEArchiveInfo(Encoding.Unicode.GetBytes("S5IC"), 0x224, true, true), + new AGEArchiveInfo(Encoding.Unicode.GetBytes("S5AC"), 0x21C, true, true) + }; + + static internal AGEArchiveInfo GetAGEArcInfo(ArcView view) + { + byte[] sig = view.View.ReadBytes(0, 8); + var siglow = sig.Take(4); + var res = infos.Where(i => Enumerable.SequenceEqual(i.signature, siglow)); + if (res.Any()) return res.First(); + res = infos.Where(i => Enumerable.SequenceEqual(i.signature, sig)); + if (res.Any()) return res.First(); + return null; + } + List ReadIndex (string ini_file, string arc_name) { if (null == LastAccessedIndex @@ -81,24 +121,21 @@ namespace GameRes.Formats.Eushully using (var ini = VFS.OpenView (ini_file)) { IBinaryStream index; - bool is_append = ini.View.AsciiEqual (0, "S4AC"); - if (is_append || ini.View.AsciiEqual (0, "S4IC") || ini.View.AsciiEqual (0, "S3IC")) + + AGEArchiveInfo info = GetAGEArcInfo (ini); + if (info == null) return null; + + if (info.isLZSSCompressed) { - uint offset = is_append ? 0x114u : 0x134u; - uint packed_size = ini.View.ReadUInt32 (offset); - var packed = ini.CreateStream (offset+4, packed_size); - var unpacked = new LzssStream (packed); - index = new BinaryStream (unpacked, ini_file); - } - else if (ini.View.AsciiEqual (0, "S3IN")) - { - index = ini.CreateStream (0x12C); + index = new BinaryStream(new LzssStream(ini.CreateStream(info.offset + 4, (uint)ini.View.ReadInt32(info.offset))), ini_file); } else - return null; + { + index = ini.CreateStream(info.offset); + } using (index) { - var file_table = ReadSysIni (index); + var file_table = ReadSysIni (index, info); if (null == file_table) return null; LastAccessedIndex = Tuple.Create (ini_file, file_table); @@ -110,7 +147,7 @@ namespace GameRes.Formats.Eushully return dir; } - internal Dictionary> ReadSysIni (IBinaryStream index) + internal Dictionary> ReadSysIni (IBinaryStream index, AGEArchiveInfo info) { int arc_count = index.ReadInt32(); if (!IsSaneCount (arc_count)) @@ -119,7 +156,8 @@ namespace GameRes.Formats.Eushully var arc_list = new List[arc_count]; for (int i = 0; i < arc_count; ++i) { - var name = index.ReadCString (0x100); + string name = info.isNameUnicode ? index.ReadCString(0x200, Encoding.Unicode) : index.ReadCString(0x100); + var file_list = new List(); file_table.Add (name, file_list); arc_list[i] = file_list; @@ -127,9 +165,10 @@ namespace GameRes.Formats.Eushully int file_count = index.ReadInt32(); if (!IsSaneCount (file_count)) return null; + for (int i = 0; i < file_count; ++i) { - var name = index.ReadCString (0x40); + string name = info.isNameUnicode ? index.ReadCString(0x80, Encoding.Unicode) : index.ReadCString(0x40); int arc_id = index.ReadInt32(); if (arc_id < 0 || arc_id >= arc_list.Length) return null; diff --git a/GameRes/BinaryStream.cs b/GameRes/BinaryStream.cs index 905ffc12..54bd39d7 100644 --- a/GameRes/BinaryStream.cs +++ b/GameRes/BinaryStream.cs @@ -170,6 +170,27 @@ namespace GameRes return bin; } + internal int FindEoS(int start, int length, Encoding enc) + { + int eos_pos = -1; + if (enc.IsUtf16()) + { + for (int i = start + 1; i < start + length; i += 2) + { + if (m_buffer[i - 1] == 0 && m_buffer[i] == 0) + { + eos_pos = i - 1; + break; + } + } + } + else + { + eos_pos = Array.IndexOf(m_buffer, 0, start, length); + } + return eos_pos; + } + uint ReadSignature () { if (m_header_size >= 4) @@ -307,10 +328,7 @@ namespace GameRes public string ReadCString (int length, Encoding enc) { length = FillBuffer (length); - int i; - for (i = 0; i < length; ++i) - if (0 == m_buffer[m_buffer_pos+i]) - break; + int i = FindEoS(m_buffer_pos, length, enc) - m_buffer_pos; string s = enc.GetString (m_buffer, m_buffer_pos, i); m_buffer_pos += length; return s;