Merge pull request #46 from MkfsSion/ageengine

ArcFormats: Support S5IC archieve
This commit is contained in:
Crsky 2024-09-12 06:48:50 +08:00 committed by GitHub
commit 5915546fdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 90 additions and 22 deletions

View File

@ -27,8 +27,10 @@ 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;
using System.Text.RegularExpressions;
namespace GameRes.Formats.Eushully
{
@ -63,15 +65,64 @@ namespace GameRes.Formats.Eushully
return null;
}
static internal string GetAAIName(string alf_name)
{
const string pattern = @"^(APPEND(?:[0-9]+)?)(?:_[0-9]+)?\.ALF$";
var match = Regex.Match(alf_name, pattern);
if (match.Success)
return match.Groups[1].Value;
return alf_name;
}
internal IEnumerable<string> GetIndexNames (string alf_name)
{
yield return "sys5ini.bin";
yield return "sys4ini.bin";
yield return "sys3ini.bin";
yield return Path.ChangeExtension (alf_name, "AAI");
yield return Path.ChangeExtension (GetAAIName(alf_name), "AAI");
}
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("S3AC"), 0x114, 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)
{
if (null == LastAccessedIndex
@ -81,24 +132,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 +158,7 @@ namespace GameRes.Formats.Eushully
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();
if (!IsSaneCount (arc_count))
@ -119,7 +167,8 @@ namespace GameRes.Formats.Eushully
var arc_list = new List<Entry>[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<Entry>();
file_table.Add (name, file_list);
arc_list[i] = file_list;
@ -127,9 +176,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;

View File

@ -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<byte>(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;