io store reading

This commit is contained in:
Fabian
2020-10-21 22:59:33 +02:00
parent 8dd348638d
commit 3835717dfc
19 changed files with 786 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ namespace PakReader
{
static class AESDecryptor
{
public const int ALIGN = 16;
public const int BLOCK_SIZE = 16 * 8; // 128
static readonly Rijndael Cipher;
static readonly Dictionary<byte[], ICryptoTransform> CachedTransforms = new Dictionary<byte[], ICryptoTransform>();

View File

@@ -0,0 +1,20 @@
namespace PakReader.Pak.IO
{
/// <summary>
/// Addressable chunk types.
/// </summary>
public enum EIoChunkType : byte
{
Invalid,
InstallManifest,
ExportBundleData,
BulkData,
OptionalBulkData,
MemoryMappedBulkData,
LoaderGlobalMeta,
LoaderInitialLoadMeta,
LoaderGlobalNames,
LoaderGlobalNameHashes,
ContainerHeader
};
}

View File

@@ -0,0 +1,11 @@
namespace PakReader.Pak.IO
{
public enum EIoContainerFlags : byte
{
None,
Compressed = (1 << 0),
Encrypted = (1 << 1),
Signed = (1 << 2),
Indexed = (1 << 3),
};
}

View File

@@ -0,0 +1,20 @@
using System.IO;
using PakReader.Parsers.Objects;
namespace PakReader.Pak.IO
{
public struct FFileIoStoreContainerFile
{
public Stream FileHandle;
public string FileName;
public long CompressionBlockSize;
public string[] CompressionMethods;
public FIoStoreTocCompressedBlockEntry[] CompressionBlocks;
public FGuid EncryptionKeyGuid;
public byte[] EncryptionKey;
public EIoContainerFlags ContainerFlags;
public FSHAHash[] BlockSignatureHashes;
public long FileSize => FileHandle.Length;
}
}

View File

@@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using FModel.Utils;
using Ionic.Zlib;
using PakReader.Parsers;
using PakReader.Parsers.Objects;
namespace PakReader.Pak.IO
{
public class FFileIoStoreReader
{
public readonly FIoStoreTocResource TocResource;
public readonly Dictionary<FIoChunkId, FIoOffsetAndLength> Toc;
public readonly FFileIoStoreContainerFile ContainerFile;
public readonly FIoContainerId ContainerId;
private byte[] _aesKey;
public byte[] AesKey
{
get => _aesKey;
set
{
if (value != null && !TestAesKey(value)) //if value not null, test but fail, throw not working
throw new ArgumentException(string.Format(FModel.Properties.Resources.AesNotWorking, value.ToStringKey(), ContainerFile.FileName));
_aesKey = value; // else, even if value is null, set it
// setting _aesKey to null will disable the corresponding menu item
}
}
public FGuid EncryptionKeyGuid => ContainerFile.EncryptionKeyGuid;
public bool IsEncrypted => ContainerFile.ContainerFlags.HasAnyFlags(EIoContainerFlags.Encrypted);
public FIoDirectoryIndexResource _directoryIndex;
private byte[] _directoryIndexBuffer;
public FFileIoStoreReader(Stream tocStream, Stream containerStream, EIoStoreTocReadOptions tocReadOptions = EIoStoreTocReadOptions.ReadDirectoryIndex)
{
ContainerFile.FileHandle = containerStream;
var tocResource = new FIoStoreTocResource(tocStream, tocReadOptions);
TocResource = tocResource;
var containerUncompressedSize = tocResource.Header.TocCompressedBlockEntryCount > 0
? tocResource.Header.TocCompressedBlockEntryCount * tocResource.Header.CompressionBlockSize
: containerStream.Length;
Toc = new Dictionary<FIoChunkId, FIoOffsetAndLength>((int) tocResource.Header.TocEntryCount);
for (var chunkIndex = 0; chunkIndex < tocResource.Header.TocEntryCount; chunkIndex++)
{
ref var chunkOffsetLength = ref tocResource.ChunkOffsetLengths[chunkIndex];
if (chunkOffsetLength.Offset + chunkOffsetLength.Length > containerUncompressedSize)
{
throw new FileLoadException("TocEntry out of container bounds");
}
Toc[tocResource.ChunkIds[chunkIndex]] = chunkOffsetLength;
}
for (var compressedBlockIndex = 0; compressedBlockIndex < tocResource.CompressionBlocks.Length; compressedBlockIndex++)
{
ref var compressedBlockEntry = ref tocResource.CompressionBlocks[compressedBlockIndex];
if (compressedBlockEntry.Offset + compressedBlockEntry.CompressedSize > ContainerFile.FileSize)
{
throw new FileLoadException("TocCompressedBlockEntry out of container bounds");
}
}
ContainerFile.CompressionMethods = tocResource.CompressionMethods;
ContainerFile.CompressionBlockSize = tocResource.Header.CompressionBlockSize;
ContainerFile.CompressionBlocks = tocResource.CompressionBlocks;
ContainerFile.ContainerFlags = tocResource.Header.ContainerFlags;
ContainerFile.EncryptionKeyGuid = tocResource.Header.EncryptionKeyGuid;
ContainerFile.BlockSignatureHashes = tocResource.ChunkBlockSignatures;
ContainerId = tocResource.Header.ContainerId;
_directoryIndexBuffer = tocResource.DirectoryIndexBuffer;
}
public void ReadIndex()
{
using Stream indexStream = IsEncrypted
? new MemoryStream(AESDecryptor.DecryptAES(_directoryIndexBuffer, _aesKey))
: new MemoryStream(_directoryIndexBuffer);
_directoryIndex = new FIoDirectoryIndexResource(indexStream);
}
public string MountPoint => _directoryIndex.MountPoint;
public bool TestAesKey(byte[] key)
{
if (!IsEncrypted)
return true;
return TestAesKey(_directoryIndexBuffer, key);
}
public static bool TestAesKey(byte[] bytes, byte[] key)
{
using BinaryReader indexReader = new BinaryReader(new MemoryStream(AESDecryptor.DecryptAES(bytes, key)));
var stringLen = indexReader.ReadInt32();
if (stringLen > 128 || stringLen < -128)
{
return false;
}
if (stringLen == 0)
{
return indexReader.ReadUInt16() == 0;
}
if (stringLen < 0)
{
var nullTerminatedPos = 4 - (stringLen - 1) * 2;
indexReader.BaseStream.Seek(nullTerminatedPos, SeekOrigin.Begin);
return indexReader.ReadInt16() == 0;
}
else
{
var nullTerminatedPos = 4 + stringLen - 1;
indexReader.BaseStream.Seek(nullTerminatedPos, SeekOrigin.Begin);
return indexReader.ReadSByte() == 0;
}
}
public byte[] Read(FIoChunkId chunkId)
{
var offsetAndLength = Toc[chunkId];
var tocResource = TocResource;
var compressionBlockSize = tocResource.Header.CompressionBlockSize;
var dst = new byte[offsetAndLength.Length];
var firstBlockIndex = (int) (offsetAndLength.Offset / compressionBlockSize);
var lastBlockIndex = (int) ((BinaryHelper.Align(offsetAndLength.Offset + dst.Length, compressionBlockSize) - 1) / compressionBlockSize);
var offsetInBlock = offsetAndLength.Offset % compressionBlockSize;
byte[] src;
var remainingSize = dst.Length;
var dstOffset = 0;
for (int blockIndex = firstBlockIndex; blockIndex < lastBlockIndex; blockIndex++)
{
var compressionBlock = tocResource.CompressionBlocks[blockIndex];
var rawSize = BinaryHelper.Align(compressionBlock.CompressedSize, AESDecryptor.ALIGN);
var compressedBuffer = new byte[rawSize];
var uncompressedSize = compressionBlock.UncompressedSize;
var uncompressedBuffer = new byte[uncompressedSize];
var containerStream = ContainerFile.FileHandle;
containerStream.Position = compressionBlock.Offset;
containerStream.Read(compressedBuffer, 0, (int) rawSize);
if (TocResource.Header.ContainerFlags.HasAnyFlags(EIoContainerFlags.Encrypted))
{
compressedBuffer = AESDecryptor.DecryptAES(compressedBuffer, _aesKey);
}
if (compressionBlock.CompressionMethodIndex == 0)
{
src = compressedBuffer;
}
else
{
var compressionMethod = tocResource.CompressionMethods[compressionBlock.CompressionMethodIndex - 1];
Decompress(compressedBuffer, uncompressedBuffer, compressionMethod);
src = uncompressedBuffer;
}
var sizeInBlock = (int) Math.Min(compressionBlockSize - offsetInBlock, remainingSize);
Buffer.BlockCopy(src, (int) offsetInBlock, dst, dstOffset, sizeInBlock);
offsetInBlock = 0;
remainingSize -= sizeInBlock;
dstOffset += sizeInBlock;
}
return dst;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FIoDirectoryIndexHandle GetChildDirectory(FIoDirectoryIndexHandle directory) =>
directory.IsValid() && IsValidIndex()
? FIoDirectoryIndexHandle.FromIndex(GetDirectoryEntry(directory).FirstChildEntry)
: FIoDirectoryIndexHandle.InvalidHandle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FIoDirectoryIndexHandle GetNextDirectory(FIoDirectoryIndexHandle directory) =>
directory.IsValid() && IsValidIndex()
? FIoDirectoryIndexHandle.FromIndex(GetDirectoryEntry(directory).NextSiblingEntry)
: FIoDirectoryIndexHandle.InvalidHandle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FIoDirectoryIndexHandle GetFile(FIoDirectoryIndexHandle directory) =>
directory.IsValid() && IsValidIndex()
? FIoDirectoryIndexHandle.FromIndex(GetDirectoryEntry(directory).FirstFileEntry)
: FIoDirectoryIndexHandle.InvalidHandle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FIoDirectoryIndexHandle GetNextFile(FIoDirectoryIndexHandle file) => file.IsValid() && IsValidIndex()
? FIoDirectoryIndexHandle.FromIndex(GetFileEntry(file).NextFileEntry)
: FIoDirectoryIndexHandle.InvalidHandle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetDirectoryName(FIoDirectoryIndexHandle directory)
{
if (directory.IsValid() && IsValidIndex())
{
var nameIndex = GetDirectoryEntry(directory).Name;
return _directoryIndex.StringTable[nameIndex];
}
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetFileName(FIoDirectoryIndexHandle file)
{
if (file.IsValid() && IsValidIndex())
{
var nameIndex = GetFileEntry(file).Name;
return _directoryIndex.StringTable[nameIndex];
}
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetFileData(FIoDirectoryIndexHandle file) => file.IsValid() && IsValidIndex()
? _directoryIndex.FileEntries[file.ToIndex()].UserData
: 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref FIoDirectoryIndexEntry GetDirectoryEntry(FIoDirectoryIndexHandle directory) =>
ref _directoryIndex.DirectoryEntries[directory.ToIndex()];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref FIoFileIndexEntry GetFileEntry(FIoDirectoryIndexHandle file) =>
ref _directoryIndex.FileEntries[file.ToIndex()];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsValidIndex() => _directoryIndex.DirectoryEntries.Length > 0;
private void Decompress(byte[] src, byte[] outData, string compressionMethod)
{
using var blockMs = new MemoryStream(src, false);
using Stream compressionStream = compressionMethod switch
{
"Zlib" => new ZlibStream(blockMs, CompressionMode.Decompress),
"Gzip" => new GZipStream(blockMs, CompressionMode.Decompress),
"Oodle" => new OodleStream(src, outData.Length),
_ => throw new NotImplementedException($"Decompression not yet implemented ({compressionMethod})")
};
compressionStream.Read(outData, 0, outData.Length);
}
}
}

View File

@@ -0,0 +1,14 @@
using System.IO;
namespace PakReader.Pak.IO
{
public struct FIoChunkHash
{
public byte[] Hash;
public FIoChunkHash(BinaryReader reader)
{
Hash = reader.ReadBytes(32);
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
namespace PakReader.Pak.IO
{
public readonly struct FIoChunkId
{
public readonly byte[] Id;
public ulong ChunkId => BitConverter.ToUInt64(Id);
public ushort ChunkIndex => BitConverter.ToUInt16(Id, 8);
public EIoChunkType ChunkType => (EIoChunkType) Id[11];
public FIoChunkId(BinaryReader reader)
{
Id = reader.ReadBytes(12);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
var hash = 5381;
for (int i = 0; i < 12; i++)
{
hash = hash * 33 + Id[i];
}
return hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object? obj)
{
if (!(obj is FIoChunkId cast)) return false;
return Id.SequenceEqual(cast.Id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(FIoChunkId a, FIoChunkId b) => a.Id.SequenceEqual(b.Id);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(FIoChunkId a, FIoChunkId b) => !a.Id.SequenceEqual(b.Id);
public override string ToString() => BitConverter.ToString(Id).Replace("-","");
}
}

View File

@@ -0,0 +1,14 @@
using System.IO;
namespace PakReader.Pak.IO
{
public struct FIoContainerId
{
public ulong Id;
public FIoContainerId(BinaryReader reader)
{
Id = reader.ReadUInt64();
}
}
}

View File

@@ -0,0 +1,20 @@
using System.IO;
namespace PakReader.Pak.IO
{
public readonly struct FIoDirectoryIndexEntry
{
public readonly uint Name;
public readonly uint FirstChildEntry;
public readonly uint NextSiblingEntry;
public readonly uint FirstFileEntry;
public FIoDirectoryIndexEntry(BinaryReader reader)
{
Name = reader.ReadUInt32();
FirstChildEntry = reader.ReadUInt32();
NextSiblingEntry = reader.ReadUInt32();
FirstFileEntry = reader.ReadUInt32();
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Runtime.CompilerServices;
namespace PakReader.Pak.IO
{
public readonly struct FIoDirectoryIndexHandle
{
public static FIoDirectoryIndexHandle InvalidHandle = new FIoDirectoryIndexHandle(uint.MaxValue);
private readonly uint _handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ToIndex() => _handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FIoDirectoryIndexHandle(uint handle)
{
_handle = handle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsValid() => this != InvalidHandle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => (int) _handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(FIoDirectoryIndexHandle a, FIoDirectoryIndexHandle b) => a._handle == b._handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(FIoDirectoryIndexHandle a, FIoDirectoryIndexHandle b) => a._handle != b._handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FIoDirectoryIndexHandle FromIndex(uint index) => new FIoDirectoryIndexHandle(index);
}
}

View File

@@ -0,0 +1,30 @@
using System.IO;
namespace PakReader.Pak.IO
{
public class FIoDirectoryIndexResource
{
public readonly string MountPoint;
public readonly FIoDirectoryIndexEntry[] DirectoryEntries;
public readonly FIoFileIndexEntry[] FileEntries;
public readonly string[] StringTable;
public FIoDirectoryIndexResource(Stream directoryIndexStream)
{
using var reader = new BinaryReader(directoryIndexStream);
MountPoint = reader.ReadFString();
if (MountPoint.StartsWith("../../.."))
{
MountPoint = MountPoint[9..];
}
else
{
// Weird mount point location...
MountPoint = "";
}
DirectoryEntries = reader.ReadTArray(() => new FIoDirectoryIndexEntry(reader));
FileEntries = reader.ReadTArray(() => new FIoFileIndexEntry(reader));
StringTable = reader.ReadTArray(reader.ReadFString);
}
}
}

View File

@@ -0,0 +1,18 @@
using System.IO;
namespace PakReader.Pak.IO
{
public readonly struct FIoFileIndexEntry
{
public readonly uint Name;
public readonly uint NextFileEntry;
public readonly uint UserData;
public FIoFileIndexEntry(BinaryReader reader)
{
Name = reader.ReadUInt32();
NextFileEntry = reader.ReadUInt32();
UserData = reader.ReadUInt32();
}
}
}

View File

@@ -0,0 +1,26 @@
using System.IO;
namespace PakReader.Pak.IO
{
public readonly struct FIoOffsetAndLength
{
// We use 5 bytes for offset and size, this is enough to represent
// an offset and size of 1PB
public readonly byte[] OffsetAndLength;
public long Offset => OffsetAndLength[4]
| ((long) OffsetAndLength[3] << 8)
| ((long) OffsetAndLength[2] << 16)
| ((long) OffsetAndLength[1] << 24)
| ((long) OffsetAndLength[0] << 32);
public long Length => OffsetAndLength[9]
| ((long) OffsetAndLength[8] << 8)
| ((long) OffsetAndLength[7] << 16)
| ((long) OffsetAndLength[6] << 24)
| ((long) OffsetAndLength[5] << 32);
public FIoOffsetAndLength(BinaryReader reader)
{
OffsetAndLength = reader.ReadBytes(5 + 5);
}
}
}

View File

@@ -0,0 +1,69 @@
using System.IO;
using System.Runtime.CompilerServices;
namespace PakReader.Pak.IO
{
public readonly struct FIoStoreTocCompressedBlockEntry
{
private const int OffsetBits = 40;
private const ulong OffsetMask = (1ul << OffsetBits) - 1ul;
private const int SizeBits = 24;
private const uint SizeMask = (1 << SizeBits) - 1;
private const int SizeShift = 8;
/* 5 bytes offset, 3 bytes for size / uncompressed size and 1 byte for compresseion method. */
public readonly byte[] Data;
public long Offset
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe { fixed (byte* ptr = Data) {
var offset = (ulong*) ptr;
return (long) (*offset & OffsetMask);
} }
}
}
public uint CompressedSize
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe { fixed (byte* ptr = Data) {
var size = ((uint*) ptr) + 1;
return (*size >> SizeShift) & SizeMask;
} }
}
}
public uint UncompressedSize
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe { fixed (byte* ptr = Data) {
var uncompressedSize = ((uint*) ptr) + 2;
return *uncompressedSize & SizeMask;
} }
}
}
public byte CompressionMethodIndex
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe { fixed (byte* ptr = Data) {
var index = ((uint*) ptr) + 2;
return (byte) (*index >> SizeBits);
} }
}
}
public FIoStoreTocCompressedBlockEntry(BinaryReader reader)
{
Data = reader.ReadBytes(5 + 3 + 3 + 1);
}
}
}

View File

@@ -0,0 +1,18 @@
using System.IO;
namespace PakReader.Pak.IO
{
public readonly struct FIoStoreTocEntryMeta
{
public const int SIZE = 32 + 4;
public readonly FIoChunkHash ChunkHash;
public readonly FIoStoreTocEntryMetaFlags Flags;
public FIoStoreTocEntryMeta(BinaryReader reader)
{
ChunkHash = new FIoChunkHash(reader);
Flags = (FIoStoreTocEntryMetaFlags) reader.ReadByte();
}
}
}

View File

@@ -0,0 +1,9 @@
namespace PakReader.Pak.IO
{
public enum FIoStoreTocEntryMetaFlags : byte
{
None,
Compressed = (1 << 0),
MemoryMapped = (1 << 1)
}
}

View File

@@ -0,0 +1,56 @@
using System.IO;
using System.Linq;
using PakReader.Parsers.Objects;
namespace PakReader.Pak.IO
{
public enum EIoStoreTocVersion : byte
{
Invalid = 0,
Initial,
DirectoryIndex,
LatestPlusOne,
Latest = LatestPlusOne - 1
}
public class FIoStoreTocHeader
{
public const int SIZE = 144;
public static byte[] TOC_MAGIC = new byte[]
{0x2D, 0x3D, 0x3D, 0x2D, 0x2D, 0x3D, 0x3D, 0x2D, 0x2D, 0x3D, 0x3D, 0x2D, 0x2D, 0x3D, 0x3D, 0x2D};
public byte[] TocMagic;
public EIoStoreTocVersion Version;
public uint TocHeaderSize;
public uint TocEntryCount;
public uint TocCompressedBlockEntryCount;
public uint TocCompressedBlockEntrySize; // For sanity checking
public uint CompressionMethodNameCount;
public uint CompressionMethodNameLength;
public uint CompressionBlockSize;
public long DirectoryIndexSize;
public FIoContainerId ContainerId;
public FGuid EncryptionKeyGuid;
public EIoContainerFlags ContainerFlags;
public FIoStoreTocHeader(BinaryReader reader)
{
TocMagic = reader.ReadBytes(16);
if (!TOC_MAGIC.SequenceEqual(TocMagic))
throw new FileLoadException("Invalid utoc magic");
Version = (EIoStoreTocVersion) reader.ReadInt32();
TocHeaderSize = reader.ReadUInt32();
TocEntryCount = reader.ReadUInt32();
TocCompressedBlockEntryCount = reader.ReadUInt32();
TocCompressedBlockEntrySize = reader.ReadUInt32();
CompressionMethodNameCount = reader.ReadUInt32();
CompressionMethodNameLength = reader.ReadUInt32();
CompressionBlockSize = reader.ReadUInt32();
DirectoryIndexSize = reader.ReadInt64();
ContainerId = new FIoContainerId(reader);
EncryptionKeyGuid = new FGuid(reader);
ContainerFlags = (EIoContainerFlags) reader.ReadInt32();
reader.BaseStream.Position += 60; // Padding
}
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using FModel.Utils;
using PakReader.Parsers.Objects;
namespace PakReader.Pak.IO
{
public enum EIoStoreTocReadOptions
{
Default,
ReadDirectoryIndex = (1 << 0),
ReadTocMeta = (1 << 1),
ReadAll = ReadDirectoryIndex | ReadTocMeta
}
public class FIoStoreTocResource
{
public readonly FIoStoreTocHeader Header;
public readonly FIoChunkId[] ChunkIds;
public readonly FIoOffsetAndLength[] ChunkOffsetLengths;
public readonly FIoStoreTocCompressedBlockEntry[] CompressionBlocks;
public readonly string[] CompressionMethods;
public readonly FSHAHash[] ChunkBlockSignatures;
public readonly byte[] DirectoryIndexBuffer;
public readonly FIoStoreTocEntryMeta[] ChunkMetas;
public FIoStoreTocResource(Stream tocStream, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.Default)
{
using var reader = new BinaryReader(tocStream);
Header = new FIoStoreTocHeader(reader);
var totalTocSize = tocStream.Length - FIoStoreTocHeader.SIZE;
var tocMetaSize = Header.TocEntryCount * FIoStoreTocEntryMeta.SIZE;
var defaultTocSize = totalTocSize - Header.DirectoryIndexSize - tocMetaSize;
var tocSize = defaultTocSize;
if (readOptions.HasAnyFlags(EIoStoreTocReadOptions.ReadTocMeta))
{
tocSize = totalTocSize; // Meta data is at the end of the TOC file
}
if (readOptions.HasAnyFlags(EIoStoreTocReadOptions.ReadDirectoryIndex))
{
tocSize = defaultTocSize + Header.DirectoryIndexSize;
}
// Chunk IDs
ChunkIds = new FIoChunkId[Header.TocEntryCount];
for (var i = 0; i < Header.TocEntryCount; i++)
{
ChunkIds[i] = new FIoChunkId(reader);
}
// Chunk offsets
ChunkOffsetLengths = new FIoOffsetAndLength[Header.TocEntryCount];
for (var i = 0; i < Header.TocEntryCount; i++)
{
ChunkOffsetLengths[i] = new FIoOffsetAndLength(reader);
}
// Compression blocks
CompressionBlocks = new FIoStoreTocCompressedBlockEntry[Header.TocCompressedBlockEntryCount];
for (var i = 0; i < Header.TocCompressedBlockEntryCount; i++)
{
CompressionBlocks[i] = new FIoStoreTocCompressedBlockEntry(reader);
}
// Compression methods
CompressionMethods = new string[Header.CompressionMethodNameCount]; // Not doing +1 nor adding CompressionMethod none here since the FPakInfo implementation doesn't as well
for (var i = 0; i < Header.CompressionMethodNameCount; i++)
{
CompressionMethods[i] = Encoding.ASCII.GetString(reader.ReadBytes((int) Header.CompressionMethodNameLength)).TrimEnd('\0');
}
// Chunk block signatures
if (Header.ContainerFlags.HasAnyFlags(EIoContainerFlags.Signed))
{
var hashSize = reader.ReadInt32();
reader.BaseStream.Position += hashSize; // actually: var tocSignature = reader.ReadBytes(hashSize);
reader.BaseStream.Position += hashSize; // actually: var blockSignature = reader.ReadBytes(hashSize);
ChunkBlockSignatures = new FSHAHash[Header.TocCompressedBlockEntryCount];
for (var i = 0; i < Header.TocCompressedBlockEntryCount; i++)
{
ChunkBlockSignatures[i] = new FSHAHash(reader);
}
// You could very hashes here but nah
}
// Directory index
if (Header.Version >= EIoStoreTocVersion.DirectoryIndex &&
readOptions.HasAnyFlags(EIoStoreTocReadOptions.ReadDirectoryIndex) &&
Header.ContainerFlags.HasAnyFlags(EIoContainerFlags.Indexed) &&
Header.DirectoryIndexSize > 0)
{
DirectoryIndexBuffer = reader.ReadBytes((int) Header.DirectoryIndexSize);
}
// Meta
if (readOptions.HasAnyFlags(EIoStoreTocReadOptions.ReadTocMeta))
{
ChunkMetas = new FIoStoreTocEntryMeta[Header.TocEntryCount];
for (var i = 0; i < Header.TocEntryCount; i++)
{
ChunkMetas[i] = new FIoStoreTocEntryMeta(reader);
}
}
}
}
}

14
FModel/Utils/Enums.cs Normal file
View File

@@ -0,0 +1,14 @@
using System;
using System.Runtime.CompilerServices;
namespace FModel.Utils
{
public static class Enums
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasAnyFlags<T>(this T flags, T contains) where T : System.Enum, IConvertible
{
return (flags.ToInt32(null) & contains.ToInt32(null)) != 0;
}
}
}