(SakanaGL): support multiple archives.

This commit is contained in:
morkt 2023-09-07 12:26:07 +04:00
parent a2d0118784
commit 78da1d7ab8

View File

@ -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<Entry> (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;
}