public void InjectFileSubcpk(Stream generatedFile, string subcpkPath, string relativePath, CompressionStyle compressionStyle)
        {
            if (DisableInjection)
            {
                return;
            }

            // logic for injection into subcpk

            SubcpkData data     = GetOrCreateSubcpkData(subcpkPath);
            var        filedata = data.Files.Where(x => x.name == relativePath).First();

            // maybe compress
            OutputStream.Position = filedata.fsizepos;
            uint oldfsize = OutputStream.ReadUInt32(EndianUtils.Endianness.BigEndian);

            OutputStream.Position = filedata.esizepos;
            uint   oldesize            = OutputStream.ReadUInt32(EndianUtils.Endianness.BigEndian);
            Stream maybeCompressedFile = MaybeCompress(generatedFile, oldfsize, oldesize, compressionStyle, subcpkPath + "/" + relativePath);

            // copy file into output stream
            maybeCompressedFile.Position = 0;
            uint newFilesize    = (uint)maybeCompressedFile.Length;
            uint newExtractsize = (uint)generatedFile.Length;
            long newFileoffs    = CurrentInjectionOffset;

            OutputStream.Position = newFileoffs;
            StreamUtils.CopyStream(maybeCompressedFile, OutputStream, maybeCompressedFile.Length);
            EnsureAligned();

            // fix sizes and offset
            OutputStream.Position = filedata.fsizepos;
            OutputStream.WriteUInt32(newFilesize.ToEndian(EndianUtils.Endianness.BigEndian));
            OutputStream.Position = filedata.esizepos;
            OutputStream.WriteUInt32(newExtractsize.ToEndian(EndianUtils.Endianness.BigEndian));
            OutputStream.Position = filedata.foffspos;
            OutputStream.WriteUInt64(((ulong)(newFileoffs - (data.SubcpkOffset + data.ContentTocOffset))).ToEndian(EndianUtils.Endianness.BigEndian));
            OutputStream.Position = CurrentInjectionOffset;
        }
        private SubcpkData GetOrCreateSubcpkData(string subcpkPath)
        {
            SubcpkData data;

            if (RemappedSubcpks.TryGetValue(subcpkPath, out data))
            {
                return(data);
            }

            int  subcpkidx             = Cpk.GetChildIndexFromName(subcpkPath).Value;
            var  subcpkoffs            = Cpk.QueryChildInfoByIndex(subcpkidx, "FileOffset");
            var  subcpkstream          = Cpk.GetChildByIndex(subcpkidx).AsFile.DataStream;
            var  subcpk                = new CpkContainer(subcpkstream.Duplicate());
            long subcpkOffsetInMainCpk = (long)subcpkoffs.value_u64 + Math.Min(Cpk.content_offset, Cpk.toc_offset);

            // header goes from start of file to content_offset in all of these
            long headerInjectedOffset = CurrentInjectionOffset;
            long headerSize           = subcpk.content_offset;

            OutputStream.Position = headerInjectedOffset;
            subcpkstream.Position = 0;
            StreamUtils.CopyStream(subcpkstream, OutputStream, headerSize);
            EnsureAligned();

            // and ToC is from toc_offset until the end of file
            long tocInjectedOffset = CurrentInjectionOffset;
            long tocSize           = subcpkstream.Length - subcpk.toc_offset;

            OutputStream.Position = tocInjectedOffset;
            subcpkstream.Position = subcpk.toc_offset;
            StreamUtils.CopyStream(subcpkstream, OutputStream, tocSize);
            EnsureAligned();

            // fetch relevant metadata for all files and repoint the offsets
            List <SubcpkFileInfo> files = new List <SubcpkFileInfo>((int)subcpk.toc_entries);
            long shift = subcpkOffsetInMainCpk - headerInjectedOffset;

            for (int i = 0; i < subcpk.toc_entries; ++i)
            {
                var  name     = subcpk.GetChildNameFromIndex(i);
                var  fsize    = subcpk.QueryChildInfoByIndex(i, "FileSize");
                var  esize    = subcpk.QueryChildInfoByIndex(i, "ExtractSize");
                var  foffs    = subcpk.QueryChildInfoByIndex(i, "FileOffset");
                long fsizepos = tocInjectedOffset + (fsize.data_position - subcpk.toc_offset);
                long esizepos = tocInjectedOffset + (esize.data_position - subcpk.toc_offset);
                long foffspos = tocInjectedOffset + (foffs.data_position - subcpk.toc_offset);
                OutputStream.Position = foffspos;
                long newFileoffs = (long)foffs.value_u64 + shift;
                OutputStream.WriteUInt64(((ulong)newFileoffs).ToEndian(EndianUtils.Endianness.BigEndian));
                files.Add(new SubcpkFileInfo()
                {
                    name = name, fsizepos = fsizepos, esizepos = esizepos, foffspos = foffspos
                });
            }

            // repoint offsets to be correct
            List <string> offsetnames = new List <string>()
            {
                "ContentOffset", "TocOffset", "EtocOffset", "ItocOffset", "GtocOffset"
            };

            foreach (string offsetname in offsetnames)
            {
                var  result   = subcpk.QueryInfo(offsetname);
                long value    = (long)result.value_u64;
                long position = result.data_position;
                if (offsetname == "ContentOffset")
                {
                    // hack to force ContentOffset and TocOffset to be the same
                    value = subcpk.toc_offset;
                }
                if (value != 0)
                {
                    long newOffset = (value - subcpk.toc_offset) + (tocInjectedOffset - headerInjectedOffset);
                    OutputStream.Position = headerInjectedOffset + position;
                    OutputStream.WriteUInt64(((ulong)newOffset).ToEndian(EndianUtils.Endianness.BigEndian));
                }
            }

            // repoint file offset in main cpk to copied headers
            long newOffsetOfSubcpkInMaincpk = headerInjectedOffset - Math.Min(Cpk.content_offset, Cpk.toc_offset);

            OutputStream.Position = subcpkoffs.data_position;
            OutputStream.WriteUInt64(((ulong)newOffsetOfSubcpkInMaincpk).ToEndian(EndianUtils.Endianness.BigEndian));

            // remember and return
            OutputStream.Position = CurrentInjectionOffset;
            data = new SubcpkData()
            {
                Cpk = subcpk, SubcpkOffset = headerInjectedOffset, ContentTocOffset = tocInjectedOffset - headerInjectedOffset, Files = files
            };
            RemappedSubcpks.Add(subcpkPath, data);
            return(data);
        }