(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.Collections.Generic;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
namespace GameRes.Formats.Sakana namespace GameRes.Formats.Sakana
{ {
internal class SxEntry : PackedEntry internal class SxEntry : PackedEntry
{ {
public ushort Flags; public ushort Flags;
public ushort ArcIndex;
public bool IsEncrypted { get { return 0 == (Flags & 0x10); } } public bool IsEncrypted { get { return 0 == (Flags & 0x10); } }
} }
@ -49,13 +52,23 @@ namespace GameRes.Formats.Sakana
public override bool CanWrite { get { return false; } } public override bool CanWrite { get { return false; } }
const uint DefaultKey = 0x2E76034B; const uint DefaultKey = 0x2E76034B;
static readonly Regex ArchiveNameRe = new Regex (@"^(.*)-([^-]+)$");
public override ArcFile TryOpen (ArcView file) 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"; var sx_name = base_name.Substring (0, 4) + "(00).sx";
sx_name = VFS.ChangeFileName (file.Name, sx_name); 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; return null;
byte[] index_data; byte[] index_data;
using (var sx = VFS.OpenView (sx_name)) using (var sx = VFS.OpenView (sx_name))
@ -80,6 +93,8 @@ namespace GameRes.Formats.Sakana
{ {
var reader = new SxIndexDeserializer (index, file.MaxOffset); var reader = new SxIndexDeserializer (index, file.MaxOffset);
var dir = reader.Deserialize(); var dir = reader.Deserialize();
if (null == dir || dir.Count == 0)
return null;
return new ArcFile (file, this, dir); return new ArcFile (file, this, dir);
} }
} }
@ -97,7 +112,11 @@ namespace GameRes.Formats.Sakana
DecryptData (input, key_lo, key_hi); DecryptData (input, key_lo, key_hi);
} }
if (sx_entry.IsPacked) if (sx_entry.IsPacked)
{
input = UnpackZstd (input); input = UnpackZstd (input);
if (sx_entry.UnpackedSize == 0)
sx_entry.UnpackedSize = (uint)input.Length;
}
return new BinMemoryStream (input, entry.Name); return new BinMemoryStream (input, entry.Name);
} }
@ -170,7 +189,7 @@ namespace GameRes.Formats.Sakana
m_dir = new List<Entry> (count); m_dir = new List<Entry> (count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
m_index.ReadUInt16(); ushort arc = Binary.BigEndian (m_index.ReadUInt16());
ushort flags = Binary.BigEndian (m_index.ReadUInt16()); ushort flags = Binary.BigEndian (m_index.ReadUInt16());
uint offset = Binary.BigEndian (m_index.ReadUInt32()); uint offset = Binary.BigEndian (m_index.ReadUInt32());
uint size = Binary.BigEndian (m_index.ReadUInt32()); uint size = Binary.BigEndian (m_index.ReadUInt32());
@ -179,19 +198,21 @@ namespace GameRes.Formats.Sakana
Offset = (long)offset << 4, Offset = (long)offset << 4,
Size = size, Size = size,
IsPacked = 0 != (flags & 0x03), IsPacked = 0 != (flags & 0x03),
ArcIndex = arc,
}; };
if (!entry.CheckPlacement (m_max_offset))
return null;
m_dir.Add (entry); m_dir.Add (entry);
} }
count = Binary.BigEndian (m_index.ReadUInt16()); int arc_count = Binary.BigEndian (m_index.ReadUInt16());
for (int i = 0; i < count; ++i) int arc_index = -1;
for (int i = 0; i < arc_count; ++i)
{ {
m_index.ReadUInt32(); m_index.ReadUInt32();
m_index.ReadUInt32(); 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.ReadUInt64();
m_index.Seek (16, SeekOrigin.Current); // MD5 sum m_index.Seek (16, SeekOrigin.Current); // MD5 sum
} }
@ -200,6 +221,10 @@ namespace GameRes.Formats.Sakana
if (count > 0) if (count > 0)
m_index.Seek (count * 24, SeekOrigin.Current); m_index.Seek (count * 24, SeekOrigin.Current);
DeserializeTree(); DeserializeTree();
if (arc_count > 1 && arc_index != -1)
{
return m_dir.Where (e => (e as SxEntry).ArcIndex == arc_index).ToList();
}
return m_dir; return m_dir;
} }