diff --git a/ArcFormats/NEKOWORKs/ArcPACK.cs b/ArcFormats/NEKOWORKs/ArcPACK.cs index 38962c746..61724d0d9 100644 --- a/ArcFormats/NEKOWORKs/ArcPACK.cs +++ b/ArcFormats/NEKOWORKs/ArcPACK.cs @@ -26,7 +26,11 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; -using GameRes.Formats; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using CommunityToolkit.HighPerformance; +using GameRes.Utility.Serialization; namespace GameRes.Formats.NEKOWORKs { @@ -39,61 +43,111 @@ public class NekoWorksPackOpener : ArchiveFormat public override bool IsHierarchic { get { return true; } } public override bool CanWrite { get { return false; } } - public override ArcFile TryOpen(ArcView file) + [StructLayout(LayoutKind.Explicit, Size = 0x50)] + private struct EXFSHeader { - if (!file.View.AsciiEqual(0, "EXFS")) - return null; + [FieldOffset(0x00)] + public uint Signature; + [FieldOffset(0x04)] + public uint ReaderVersion; + [FieldOffset(0x08)] + public uint WriterVersion; + [FieldOffset(0x0C)] + public uint FileCount; + [FieldOffset(0x10)] + public long HeaderSize; + [FieldOffset(0x18)] + public long EntryTableSize; + [FieldOffset(0x20)] + public long PathTableSize; + [FieldOffset(0x28)] + public long ResourceTableOffset; - int fileCount = file.View.ReadInt32(0x0C); - uint infoOffset = file.View.ReadUInt32(0x10); - uint infoSize = file.View.ReadUInt32(0x18); - uint nameBlockSize = file.View.ReadUInt32(0x20); - uint baseDataOffset = file.View.ReadUInt32(0x28); + [FieldOffset(0x30)] + public long Reserve1; + [FieldOffset(0x38)] + public long Reserve2; + [FieldOffset(0x40)] + public long Reserve3; + [FieldOffset(0x48)] + public long Reserve4; - if (!IsSaneCount(fileCount)) - return null; + public unsafe static int Size() + { + return Unsafe.SizeOf(); + } + } - long namesOffset = infoOffset + infoSize; - if (namesOffset + nameBlockSize > file.MaxOffset) - return null; + [StructLayout(LayoutKind.Explicit, Size = 0x20)] + private struct EXFSFileEntry + { + [FieldOffset(0x00)] + public long FilePathOffset; + [FieldOffset(0x08)] + public long FilePathSize; + [FieldOffset(0x10)] + public long FileOffset; + [FieldOffset(0x18)] + public long FileSize; - byte[] nameData = file.View.ReadBytes(namesOffset, nameBlockSize); - var nameList = new List(fileCount); - int start = 0; - for (int i = 0; i < nameData.Length; ++i) + public unsafe static int Size() { - if (nameData[i] == (byte)'\n') - { - if (i > start) - nameList.Add(System.Text.Encoding.UTF8.GetString(nameData, start, i - start)); - start = i + 1; - } + return Unsafe.SizeOf(); } + } - if (nameList.Count < fileCount) + public override ArcFile TryOpen(ArcView file) + { + if (file.MaxOffset < EXFSHeader.Size()) + { return null; + } - var dir = new List(fileCount); - long indexOffset = infoOffset; - - for (int i = 0; i < fileCount; ++i) + using (ArcViewStream stream = file.CreateStream()) { - long entryPos = indexOffset + i * 0x20; + stream.ReadStruct(out EXFSHeader hdr); + if (hdr.Signature != 0x53465845u) + { + return null; + } + if (hdr.ReaderVersion == 0u) + { + return null; + } - uint dataOffset = file.View.ReadUInt32(entryPos + 0x10); - uint dataSize = file.View.ReadUInt32(entryPos + 0x18); + stream.Position = hdr.HeaderSize; + byte[] entryTableBytes = new byte[hdr.EntryTableSize]; + byte[] pathTableBytes = new byte[hdr.PathTableSize]; + if (stream.Read(entryTableBytes) != entryTableBytes.Length) + { + return null; + } + if (stream.Read(pathTableBytes) != pathTableBytes.Length) + { + return null; + } - var entry = Create(nameList[i]); - entry.Offset = baseDataOffset + dataOffset; - entry.Size = dataSize; + ReadOnlySpan exfsEntries = MemoryMarshal.Cast(entryTableBytes); - if (!entry.CheckPlacement(file.MaxOffset)) - return null; + List dir = new List((int)hdr.FileCount); + for (uint i = 0; i < hdr.FileCount; ++i) + { + EXFSFileEntry fe = exfsEntries[(int)i]; + string fn = Encoding.UTF8.GetString(pathTableBytes, (int)fe.FilePathOffset, (int)fe.FilePathSize); - dir.Add(entry); - } + Entry entry = Create(fn); + entry.Offset = hdr.ResourceTableOffset + fe.FileOffset; + entry.Size = (uint)fe.FileSize; + + if (!entry.CheckPlacement(file.MaxOffset)) + { + return null; + } - return new ArcFile(file, this, dir); + dir.Add(entry); + } + return new ArcFile(file, this, dir); + } } } }