/// <summary> /// Resizes a block of tag data, updating relative pointers which do not point into the block. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="tag">The tag.</param> /// <param name="startOffset">The offset, from the start of the tag's data, where the block to resize begins at.</param> /// <param name="oldSize">The current size of the block to resize.</param> /// <param name="newSize">The new size of the block.</param> /// <param name="origin">The origin where data should be inserted or removed.</param> public void ResizeTagData(Stream stream, TagInstance tag, long startOffset, long oldSize, long newSize, ResizeOrigin origin) { if (tag == null) { throw new ArgumentNullException("tag"); } if (tag.HeaderOffset < 0) { throw new ArgumentException("The tag is not in the cache file"); } if (oldSize < 0) { throw new ArgumentException("The old block size cannot be negative"); } if (newSize < 0) { throw new ArgumentException("Cannot resize a block to a negative size"); } if (newSize == tag.DataSize) { return; } // Correct offsets pointing after the block var sizeDelta = newSize - oldSize; var blockEndOffset = startOffset + oldSize; tag.DataSize += sizeDelta; foreach (var fixup in tag.DataFixups.Concat(tag.ResourceFixups)) { if (fixup.WriteOffset >= blockEndOffset) { fixup.WriteOffset = (uint)(fixup.WriteOffset + sizeDelta); } if (fixup.TargetOffset >= blockEndOffset) { fixup.TargetOffset = (uint)(fixup.TargetOffset + sizeDelta); } } if (tag.MainStructOffset >= blockEndOffset) { tag.MainStructOffset = (uint)(tag.MainStructOffset + sizeDelta); } FixTagOffsets(tag.DataOffset + blockEndOffset, sizeDelta, tag); // Insert/remove the data long editOffset; if (origin == ResizeOrigin.Beginning) { editOffset = startOffset; } else if (sizeDelta > 0) { editOffset = blockEndOffset; } else { editOffset = blockEndOffset + sizeDelta; } stream.Position = tag.DataOffset + editOffset; if (sizeDelta > 0) { StreamUtil.Insert(stream, (int)sizeDelta, 0); } else { StreamUtil.Remove(stream, (int)-sizeDelta); } UpdateTag(stream, tag); }
/// <summary> /// Inserts or removes data in a tag. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="tag">The tag.</param> /// <param name="insertOffset">The offset, from the start of the tag's header, to insert data at.</param> /// <param name="sizeDelta">The size of the data to insert or remove. If positive, data will be inserted. If negative, data will be removed.</param> /// <param name="origin">The type of resize to perform. See <see cref="InsertOrigin"/>.</param> /// <param name="mode">The resize mode. See <see cref="ResizeMode"/>.</param> private void ResizeTag(Stream stream, HaloTag tag, uint insertOffset, int sizeDelta, InsertOrigin origin, ResizeMode mode) { if (sizeDelta == 0) { return; } var headerSize = GetHeaderSize(tag); if (sizeDelta < 0 && ((origin == InsertOrigin.Before && -sizeDelta > insertOffset) || (origin == InsertOrigin.After && insertOffset + -sizeDelta > headerSize + tag.Size))) { throw new ArgumentException("Cannot remove more bytes than there are available in the tag"); } // In insertion mode, correct relative offsets to account for inserted data var relativeCompareOffset = (origin == InsertOrigin.Before) ? insertOffset : insertOffset + 1; // hack if (headerSize < relativeCompareOffset) { tag.Size = (uint)(tag.Size + sizeDelta); if (mode == ResizeMode.Insert) { foreach (var fixup in tag.DataFixups.Concat(tag.ResourceFixups)) { if (fixup.WriteOffset + headerSize >= relativeCompareOffset) { fixup.WriteOffset = (uint)(fixup.WriteOffset + sizeDelta); } if (fixup.TargetOffset + headerSize >= relativeCompareOffset) { fixup.TargetOffset = (uint)(fixup.TargetOffset + sizeDelta); } } if (tag.MainStructOffset + headerSize >= relativeCompareOffset) { tag.MainStructOffset = (uint)(tag.MainStructOffset + sizeDelta); } } } // Correct tag offsets var absoluteOffset = _headerOffsets[tag.Index] + insertOffset; var absoluteCompareOffset = (origin == InsertOrigin.Before) ? absoluteOffset : absoluteOffset + 1; // hack for (var i = 0; i < _tags.Count; i++) { if (_tags[i] == null) { continue; } if (_headerOffsets[i] >= absoluteCompareOffset) // Header offset (absolute) { _headerOffsets[i] = (uint)(_headerOffsets[i] + sizeDelta); } if (_tags[i].Offset >= absoluteCompareOffset) // Data offset (absolute) { _tags[i].Offset = (uint)(_tags[i].Offset + sizeDelta); } } // Insert/remove the data if (sizeDelta < 0 && origin == InsertOrigin.Before) { absoluteOffset = (uint)(absoluteOffset + sizeDelta); } stream.Position = absoluteOffset; if (sizeDelta > 0) { StreamUtil.Insert(stream, sizeDelta, 0); } else { StreamUtil.Remove(stream, -sizeDelta); } }