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); }