예제 #1
0
        /// <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);
        }