public IoStoreReader(FileInfo utocFile, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, EGame game = EGame.GAME_UE4_LATEST, UE4Version ver = UE4Version.VER_UE4_DETERMINE_BY_GAME) : this(new FByteArchive(utocFile.FullName, File.ReadAllBytes(utocFile.FullName), game, ver == UE4Version.VER_UE4_DETERMINE_BY_GAME ? game.GetVersion() : ver), it => new FStreamArchive(it, File.Open(it, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), game, ver == UE4Version.VER_UE4_DETERMINE_BY_GAME ? game.GetVersion() : ver), readOptions) { }
public FIoStoreTocResource(Stream tocStream, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.Default) { var streamBuffer = new byte[tocStream.Length]; tocStream.Read(streamBuffer, 0, streamBuffer.Length); using var reader = new BinaryReader(new MemoryStream(streamBuffer)); Header = new FIoStoreTocHeader(reader); reader.BaseStream.Position = Header.TocHeaderSize; var totalTocSize = tocStream.Length - Header.TocHeaderSize; 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 verify 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); } } }
public IoStoreReader(FArchive tocStream, Func <string, FArchive> openContainerStreamFunc, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex) : base(tocStream.Name, tocStream.Game, tocStream.Ver) { Length = tocStream.Length; TocResource = new FIoStoreTocResource(tocStream, readOptions); List <FArchive> containerStreams; if (TocResource.Header.PartitionCount <= 0) { containerStreams = new List <FArchive>(1); try { containerStreams.Add(openContainerStreamFunc(tocStream.Name.SubstringBeforeLast('.') + ".ucas")); } catch (Exception e) { throw new FIoStatusException(EIoErrorCode.FileOpenFailed, $"Failed to open container partition 0 for {tocStream.Name}", e); } } else { containerStreams = new List <FArchive>((int)TocResource.Header.PartitionCount); var environmentPath = tocStream.Name.SubstringBeforeLast('.'); for (int i = 0; i < TocResource.Header.PartitionCount; i++) { try { var path = i > 0 ? string.Concat(environmentPath, "_s", i, ".ucas") : string.Concat(environmentPath, ".ucas"); containerStreams.Add(openContainerStreamFunc(path)); } catch (Exception e) { throw new FIoStatusException(EIoErrorCode.FileOpenFailed, $"Failed to open container partition {i} for {tocStream.Name}", e); } } } Length += containerStreams.Sum(x => x.Length); ContainerStreams = containerStreams; #if GENERATE_CHUNK_ID_DICT Toc = new Dictionary <FIoChunkId, FIoOffsetAndLength>((int)TocResource.Header.TocEntryCount); for (var i = 0; i < TocResource.ChunkIds.Length; i++) { Toc[TocResource.ChunkIds[i]] = TocResource.ChunkOffsetLengths[i]; } #endif Info = TocResource.Header; if (TocResource.Header.Version > EIoStoreTocVersion.Latest) { log.Warning("Io Store \"{0}\" has unsupported version {1}", Path, (int)Info.Version); } }
public IoStoreReader(string tocPath, Stream tocStream, Stream casStream, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, EGame game = EGame.GAME_UE4_LATEST, UE4Version ver = UE4Version.VER_UE4_DETERMINE_BY_GAME) : this(new FStreamArchive(tocPath, tocStream, game, ver == UE4Version.VER_UE4_DETERMINE_BY_GAME ? game.GetVersion() : ver), it => new FStreamArchive(it, casStream, game, ver == UE4Version.VER_UE4_DETERMINE_BY_GAME ? game.GetVersion() : ver), readOptions) { }
public IoStoreReader(string tocPath, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, EGame game = EGame.GAME_UE4_LATEST, UE4Version ver = UE4Version.VER_UE4_DETERMINE_BY_GAME) : this(new FileInfo(tocPath), readOptions, game, ver) { }
public FIoStoreTocResource(FArchive Ar, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.Default) { var streamBuffer = new byte[Ar.Length]; Ar.Read(streamBuffer, 0, streamBuffer.Length); using var archive = new FByteArchive(Ar.Name, streamBuffer); // Header Header = new FIoStoreTocHeader(archive); if (Header.Version < EIoStoreTocVersion.PartitionSize) { Header.PartitionCount = 1; Header.PartitionSize = uint.MaxValue; } // Chunk IDs ChunkIds = archive.ReadArray <FIoChunkId>((int)Header.TocEntryCount); // Chunk offsets ChunkOffsetLengths = new FIoOffsetAndLength[Header.TocEntryCount]; for (int i = 0; i < Header.TocEntryCount; i++) { ChunkOffsetLengths[i] = new FIoOffsetAndLength(archive); } // Chunk perfect hash map uint perfectHashSeedsCount = 0; uint chunksWithoutPerfectHashCount = 0; if (Header.Version >= EIoStoreTocVersion.PerfectHashWithOverflow) { perfectHashSeedsCount = Header.TocChunkPerfectHashSeedsCount; chunksWithoutPerfectHashCount = Header.TocChunksWithoutPerfectHashCount; } else if (Header.Version >= EIoStoreTocVersion.PerfectHash) { perfectHashSeedsCount = Header.TocChunkPerfectHashSeedsCount; } if (perfectHashSeedsCount > 0) { ChunkPerfectHashSeeds = archive.ReadArray <int>((int)perfectHashSeedsCount); } if (chunksWithoutPerfectHashCount > 0) { ChunkIndicesWithoutPerfectHash = archive.ReadArray <int>((int)chunksWithoutPerfectHashCount); } // Compression blocks CompressionBlocks = new FIoStoreTocCompressedBlockEntry[Header.TocCompressedBlockEntryCount]; for (int i = 0; i < Header.TocCompressedBlockEntryCount; i++) { CompressionBlocks[i] = new FIoStoreTocCompressedBlockEntry(archive); } // Compression methods unsafe { var bufferSize = (int)(Header.CompressionMethodNameLength * Header.CompressionMethodNameCount); var buffer = stackalloc byte[bufferSize]; archive.Serialize(buffer, bufferSize); CompressionMethods = new CompressionMethod[Header.CompressionMethodNameCount + 1]; CompressionMethods[0] = CompressionMethod.None; for (var i = 0; i < Header.CompressionMethodNameCount; i++) { var name = new string((sbyte *)buffer + i * Header.CompressionMethodNameLength, 0, (int)Header.CompressionMethodNameLength).TrimEnd('\0'); if (string.IsNullOrEmpty(name)) { continue; } if (!Enum.TryParse(name, true, out CompressionMethod method)) { Log.Warning($"Unknown compression method '{name}' in {Ar.Name}"); method = CompressionMethod.Unknown; } CompressionMethods[i + 1] = method; } } // Chunk block signatures if (Header.ContainerFlags.HasFlag(EIoContainerFlags.Signed)) { var hashSize = archive.Read <int>(); // tocSignature and blockSignature both byte[hashSize] // and ChunkBlockSignature of FSHAHash[Header.TocCompressedBlockEntryCount] archive.Position += hashSize + hashSize + FSHAHash.SIZE * Header.TocCompressedBlockEntryCount; // You could verify hashes here but nah } // Directory index if (Header.Version >= EIoStoreTocVersion.DirectoryIndex && readOptions.HasFlag(EIoStoreTocReadOptions.ReadDirectoryIndex) && Header.ContainerFlags.HasFlag(EIoContainerFlags.Indexed) && Header.DirectoryIndexSize > 0) { DirectoryIndexBuffer = archive.ReadBytes((int)Header.DirectoryIndexSize); } // Meta if (readOptions.HasFlag(EIoStoreTocReadOptions.ReadTocMeta)) { ChunkMetas = new FIoStoreTocEntryMeta[Header.TocEntryCount]; for (int i = 0; i < Header.TocEntryCount; i++) { ChunkMetas[i] = new FIoStoreTocEntryMeta(archive); } } }
public IoStoreReader(string tocPath, Stream tocStream, Stream casStream, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, VersionContainer?versions = null) : this(new FStreamArchive(tocPath, tocStream, versions), it => new FStreamArchive(it, casStream, versions), readOptions) { }
public IoStoreReader(FileInfo utocFile, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, VersionContainer?versions = null) : this(new FByteArchive(utocFile.FullName, File.ReadAllBytes(utocFile.FullName), versions), it => new FStreamArchive(it, File.Open(it, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), versions), readOptions) { }
public IoStoreReader(string tocPath, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, VersionContainer?versions = null) : this(new FileInfo(tocPath), readOptions, versions) { }