/// <summary> /// Patches the file segments in a stream. /// </summary> /// <param name="changes">The changes to make to the segments and their data.</param> /// <param name="stream">The stream to write changes to.</param> public static void PatchSegments(IEnumerable <SegmentChange> changes, IStream stream) { // Sort changes by their offsets var changesByOffset = new SortedList <uint, SegmentChange>(); foreach (SegmentChange change in changes) { changesByOffset[change.OldOffset] = change; } // Now adjust each segment foreach (SegmentChange change in changesByOffset.Values) { // Resize it if necessary if (change.NewSize != change.OldSize) { if (change.ResizeAtEnd) { stream.SeekTo(change.NewOffset + change.OldSize); } else { stream.SeekTo(change.NewOffset); } StreamUtil.Insert(stream, change.NewSize - change.OldSize, 0); } // Patch its data DataPatcher.PatchData(change.DataChanges, change.NewOffset, stream); } }
/// <summary> /// Compresses and saves data for a resource. /// </summary> /// <param name="inStream">The stream open on the resource data. It must have read/write access.</param> /// <param name="resourceIndex">The index of the resource to edit.</param> /// <param name="data">The data to compress.</param> /// <returns>The total size of the compressed resource in bytes.</returns> public uint Compress(Stream inStream, int resourceIndex, byte[] data) { if (resourceIndex < 0 || resourceIndex > _resources.Count) { throw new ArgumentOutOfRangeException("resourceIndex"); } // Compress the data (just use a single chunk) var compressed = LZ4Codec.EncodeHC(data, 0, data.Length); // Resize the resource's data so that the chunk can fit var resource = _resources[resourceIndex]; var newSize = (uint)compressed.Length + ChunkHeaderSize; var roundedSize = (newSize + 0xF) & ~0xFU; // Round up to a multiple of 0x10 var sizeDelta = (int)(roundedSize - resource.Size); if (sizeDelta > 0) { // Resource needs to grow inStream.Position = resource.Offset + resource.Size; StreamUtil.Insert(inStream, sizeDelta, 0); } else { // Resource needs to shrink inStream.Position = resource.Offset + roundedSize; StreamUtil.Remove(inStream, -sizeDelta); } // Write the chunk in inStream.Position = resource.Offset; var writer = new BinaryWriter(inStream); writer.Write(data.Length); writer.Write(compressed.Length); writer.Write(compressed, 0, compressed.Length); StreamUtil.Fill(inStream, 0, (int)(roundedSize - newSize)); // Padding // Adjust resource offsets for (var i = resourceIndex + 1; i < _resources.Count; i++) { _resources[i].Offset = (uint)(_resources[i].Offset + sizeDelta); } UpdateResourceTable(writer); return((uint)compressed.Length + ChunkHeaderSize); }