ArcFormats: Support S5IC archieve

* Tested game: https://vndb.org/r119690

Signed-off-by: MkfsSion <[email protected]>
This commit is contained in:
MkfsSion
2024-08-31 00:39:40 +08:00
parent e2d4ddb59a
commit d621cf4c03
2 changed files with 78 additions and 21 deletions

View File

@@ -27,8 +27,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
using GameRes.Utility;
using GameRes.Compression; using GameRes.Compression;
using System.Text;
using System.Linq;
namespace GameRes.Formats.Eushully namespace GameRes.Formats.Eushully
{ {
@@ -65,6 +66,7 @@ namespace GameRes.Formats.Eushully
internal IEnumerable<string> GetIndexNames (string alf_name) internal IEnumerable<string> GetIndexNames (string alf_name)
{ {
yield return "sys5ini.bin";
yield return "sys4ini.bin"; yield return "sys4ini.bin";
yield return "sys3ini.bin"; yield return "sys3ini.bin";
yield return Path.ChangeExtension (alf_name, "AAI"); yield return Path.ChangeExtension (alf_name, "AAI");
@@ -72,6 +74,44 @@ namespace GameRes.Formats.Eushully
Tuple<string, Dictionary<string, List<Entry>>> LastAccessedIndex; Tuple<string, Dictionary<string, List<Entry>>> 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<Entry> ReadIndex (string ini_file, string arc_name) List<Entry> ReadIndex (string ini_file, string arc_name)
{ {
if (null == LastAccessedIndex if (null == LastAccessedIndex
@@ -81,24 +121,21 @@ namespace GameRes.Formats.Eushully
using (var ini = VFS.OpenView (ini_file)) using (var ini = VFS.OpenView (ini_file))
{ {
IBinaryStream index; 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; index = new BinaryStream(new LzssStream(ini.CreateStream(info.offset + 4, (uint)ini.View.ReadInt32(info.offset))), ini_file);
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);
} }
else else
return null; {
index = ini.CreateStream(info.offset);
}
using (index) using (index)
{ {
var file_table = ReadSysIni (index); var file_table = ReadSysIni (index, info);
if (null == file_table) if (null == file_table)
return null; return null;
LastAccessedIndex = Tuple.Create (ini_file, file_table); LastAccessedIndex = Tuple.Create (ini_file, file_table);
@@ -110,7 +147,7 @@ namespace GameRes.Formats.Eushully
return dir; return dir;
} }
internal Dictionary<string, List<Entry>> ReadSysIni (IBinaryStream index) internal Dictionary<string, List<Entry>> ReadSysIni (IBinaryStream index, AGEArchiveInfo info)
{ {
int arc_count = index.ReadInt32(); int arc_count = index.ReadInt32();
if (!IsSaneCount (arc_count)) if (!IsSaneCount (arc_count))
@@ -119,7 +156,8 @@ namespace GameRes.Formats.Eushully
var arc_list = new List<Entry>[arc_count]; var arc_list = new List<Entry>[arc_count];
for (int i = 0; i < arc_count; ++i) 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<Entry>(); var file_list = new List<Entry>();
file_table.Add (name, file_list); file_table.Add (name, file_list);
arc_list[i] = file_list; arc_list[i] = file_list;
@@ -127,9 +165,10 @@ namespace GameRes.Formats.Eushully
int file_count = index.ReadInt32(); int file_count = index.ReadInt32();
if (!IsSaneCount (file_count)) if (!IsSaneCount (file_count))
return null; return null;
for (int i = 0; i < file_count; ++i) 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(); int arc_id = index.ReadInt32();
if (arc_id < 0 || arc_id >= arc_list.Length) if (arc_id < 0 || arc_id >= arc_list.Length)
return null; return null;

View File

@@ -170,6 +170,27 @@ namespace GameRes
return bin; 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<byte>(m_buffer, 0, start, length);
}
return eos_pos;
}
uint ReadSignature () uint ReadSignature ()
{ {
if (m_header_size >= 4) if (m_header_size >= 4)
@@ -307,10 +328,7 @@ namespace GameRes
public string ReadCString (int length, Encoding enc) public string ReadCString (int length, Encoding enc)
{ {
length = FillBuffer (length); length = FillBuffer (length);
int i; int i = FindEoS(m_buffer_pos, length, enc) - m_buffer_pos;
for (i = 0; i < length; ++i)
if (0 == m_buffer[m_buffer_pos+i])
break;
string s = enc.GetString (m_buffer, m_buffer_pos, i); string s = enc.GetString (m_buffer, m_buffer_pos, i);
m_buffer_pos += length; m_buffer_pos += length;
return s; return s;