/// <summary> /// Defines a new segment in the file. /// </summary> /// <param name="offset">The offset at which the segment starts.</param> /// <param name="size">The size of the segment.</param> /// <param name="offsetAlignment">A power of two which the offset must be a multiple of (e.g. 0x1000 = 4kb-aligned).</param> /// <param name="sizeAlignment">A power of two which the size must be a multiple of (e.g. 0x1000 = 4kb-aligned).</param> /// <param name="resizeOrigin">The origin at which to insert/remove data in the segment.</param> /// <returns>The segment's ID number which can be used to retrieve information about it.</returns> public int DefineSegment(int offset, int size, int offsetAlignment, int sizeAlignment, SegmentResizeOrigin resizeOrigin) { if (offset != Align(offset, offsetAlignment)) { throw new ArgumentException("The segment offset is not aligned to the given alignment."); } if (size != Align(size, sizeAlignment)) { throw new ArgumentException("The segment size is not aligned to the given alignment."); } if (_segmentsByOffset.ContainsKey(offset)) { throw new ArgumentException("A segment has already been defined at the given offset."); } // Create and add the segment definition int id = _segmentsById.Count; var segment = new InternalSegment { ID = id, Offset = offset, Size = size, OffsetAlignment = offsetAlignment, SizeAlignment = sizeAlignment, ResizeOrigin = resizeOrigin }; _segmentsById.Add(segment); _segmentsByOffset[offset] = segment; RecalculateActualSizes(); return(id); }
/// <summary> /// Resizes a segment in the file. /// </summary> /// <param name="id">The ID of the segment to resize.</param> /// <param name="newSize">The minimum amount of space that the segment must occupy.</param> /// <param name="stream">The stream to write padding to.</param> /// <returns>The new size of the segment after alignment has been applied.</returns> public int ResizeSegment(int id, int newSize, IStream stream) { InternalSegment segment = GetSegmentFromID(id); int oldSize = segment.Size; if (newSize == oldSize) { return(oldSize); } // Change the size of the segment int oldActualSize = segment.ActualSize; int oldSegmentEnd = segment.Offset + segment.ActualSize; segment.Size = Align(newSize, segment.SizeAlignment); segment.ActualSize = Math.Max(segment.Size, segment.ActualSize); // Recalculate the segment offsets, // and then use the recalculated offsets to recalculate the sizes RecalculateOffsets(); RecalculateActualSizes(); int resizeOffset; switch (segment.ResizeOrigin) { case SegmentResizeOrigin.Beginning: resizeOffset = segment.Offset; break; case SegmentResizeOrigin.End: resizeOffset = oldSegmentEnd; break; default: throw new ArgumentException("The segment cannot be resized."); } // Insert/remove data into/from the stream // FIXME: There's a bug here where changing the size of a SegmentResizeOrigin.Beginning segment won't move the data back if ActualSize doesn't change // Some sort of StreamUtil.Copy() method is necessary for that stream.SeekTo(resizeOffset); if (oldActualSize < segment.ActualSize) { StreamUtil.Insert(stream, segment.ActualSize - oldActualSize, 0); } else if (segment.ActualSize < oldActualSize) { throw new NotImplementedException("Segment shrinking is not supported yet."); } OnSegmentResized(new SegmentResizedEventArgs(id, oldSize, segment.Size, segment.ResizeOrigin)); return(segment.Size); }
/// <summary> /// Recalculates the offset of each segment in the file after a segment's size has been changed. /// </summary> private void RecalculateOffsets() { IList <InternalSegment> segments = _segmentsByOffset.Values; for (int i = 1; i < segments.Count; i++) { InternalSegment thisSegment = segments[i]; InternalSegment prevSegment = segments[i - 1]; thisSegment.Offset = Align(prevSegment.Offset + prevSegment.ActualSize, thisSegment.OffsetAlignment); } }
/// <summary> /// Defines a new segment in the file. /// </summary> /// <param name="offset">The offset at which the segment starts.</param> /// <param name="size">The size of the segment.</param> /// <param name="offsetAlignment">A power of two which the offset must be a multiple of (e.g. 0x1000 = 4kb-aligned).</param> /// <param name="sizeAlignment">A power of two which the size must be a multiple of (e.g. 0x1000 = 4kb-aligned).</param> /// <param name="resizeOrigin">The origin at which to insert/remove data in the segment.</param> /// <returns>The segment's ID number which can be used to retrieve information about it.</returns> public int DefineSegment(int offset, int size, int offsetAlignment, int sizeAlignment, SegmentResizeOrigin resizeOrigin) { if (offset != Align(offset, offsetAlignment)) throw new ArgumentException("The segment offset is not aligned to the given alignment."); if (size != Align(size, sizeAlignment)) throw new ArgumentException("The segment size is not aligned to the given alignment."); if (_segmentsByOffset.ContainsKey(offset)) throw new ArgumentException("A segment has already been defined at the given offset."); // Create and add the segment definition int id = _segmentsById.Count; var segment = new InternalSegment { ID = id, Offset = offset, Size = size, OffsetAlignment = offsetAlignment, SizeAlignment = sizeAlignment, ResizeOrigin = resizeOrigin }; _segmentsById.Add(segment); _segmentsByOffset[offset] = segment; RecalculateActualSizes(); return id; }