示例#1
0
 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)
 {
 }
示例#2
0
        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);
                }
            }
        }
示例#3
0
        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);
            }
        }
示例#4
0
 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)
 {
 }
示例#5
0
 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)
 {
 }
示例#6
0
        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);
                }
            }
        }
示例#7
0
 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)
 {
 }
示例#8
0
 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)
 {
 }
示例#9
0
 public IoStoreReader(string tocPath, EIoStoreTocReadOptions readOptions = EIoStoreTocReadOptions.ReadDirectoryIndex, VersionContainer?versions = null)
     : this(new FileInfo(tocPath), readOptions, versions)
 {
 }