예제 #1
0
 /// <summary>
 /// Updates the tag instance's state from a block of tag data.
 /// </summary>
 /// <param name="data">The tag data.</param>
 /// <param name="dataOffset">The offset of the tag data relative to the tag instance's header.</param>
 internal void Update(CachedTagData data, uint dataOffset)
 {
     Group            = data.Group;
     DefinitionOffset = data.MainStructOffset + dataOffset;
     Dependencies     = new ReadOnlySet <int>(new HashSet <int>(data.Dependencies));
     _pointerOffsets  = data.PointerFixups.Select(fixup => fixup.WriteOffset + dataOffset).ToList();
     _resourceOffsets = data.ResourcePointerOffsets.Select(offset => offset + dataOffset).ToList();
 }
예제 #2
0
        /// <summary>
        /// Builds a description for a tag's data without extracting anything.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="tag">The tag to read.</param>
        /// <param name="dataOffset">On return, this will contain the offset of the tag's data relative to its header.</param>
        /// <returns>The description that was built. </returns>
        private static CachedTagData BuildTagData(Stream stream, CachedTagInstance tag, out uint dataOffset)
        {
            var data = new CachedTagData
            {
                Group            = tag.Group,
                MainStructOffset = tag.DefinitionOffset,
            };

            foreach (var dependency in tag.Dependencies)
            {
                data.Dependencies.Add(dependency);
            }

            // Read pointer fixups
            var reader = new BinaryReader(stream);

            foreach (var pointerOffset in tag.PointerOffsets)
            {
                reader.BaseStream.Position = tag.HeaderOffset + pointerOffset;
                data.PointerFixups.Add(new CachedTagData.PointerFixup
                {
                    WriteOffset  = pointerOffset,
                    TargetOffset = tag.PointerToOffset(reader.ReadUInt32()),
                });
            }

            // Find the start of the tag's data by finding the offset of the first block which is pointed to by something
            // We CAN'T just calculate a header size here because we don't know for sure if there's padding and how big it is
            var startOffset = tag.DefinitionOffset;

            foreach (var fixup in data.PointerFixups)
            {
                startOffset = Math.Min(startOffset, Math.Min(fixup.WriteOffset, fixup.TargetOffset));
            }

            // Now convert all offsets into relative ones
            foreach (var fixup in data.PointerFixups)
            {
                fixup.WriteOffset  -= startOffset;
                fixup.TargetOffset -= startOffset;
            }

            data.ResourcePointerOffsets.AddRange(tag.ResourcePointerOffsets.Select(offset => offset - startOffset));

            data.TagReferenceOffsets.AddRange(tag.TagReferenceOffsets.Select(offset => offset - startOffset));

            data.MainStructOffset -= startOffset;
            dataOffset             = startOffset;

            return(data);
        }
예제 #3
0
        /// <summary>
        /// Overwrites a tag's data.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="tag">The tag to overwrite.</param>
        /// <param name="data">The data to store.</param>
        public void SetTagData(Stream stream, CachedTagInstance tag, CachedTagData data)
        {
            if (tag == null)
            {
                throw new ArgumentNullException(nameof(tag));
            }
            else if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }
            else if (data.Group == TagGroup.None)
            {
                throw new ArgumentException("Cannot assign a tag to a null tag group");
            }
            else if (data.Data == null)
            {
                throw new ArgumentException("The tag data buffer is null");
            }

            // Ensure the data fits
            var headerSize        = CachedTagInstance.CalculateHeaderSize(data);
            var alignedHeaderSize = (uint)((headerSize + 0xF) & ~0xF);

            if (tag.HeaderOffset < 0)
            {
                tag.HeaderOffset = GetNewTagOffset(tag.Index);
            }
            var alignedLength = (data.Data.Length + 0xF) & ~0xF;

            ResizeBlock(stream, tag, tag.HeaderOffset, tag.TotalSize, alignedHeaderSize + alignedLength);
            tag.TotalSize = alignedHeaderSize + alignedLength;
            tag.Update(data, alignedHeaderSize);

            // Write in the new header and data
            stream.Position = tag.HeaderOffset;
            var writer = new BinaryWriter(stream);

            tag.WriteHeader(writer);
            StreamUtil.Fill(stream, 0, (int)(alignedHeaderSize - headerSize));
            stream.Write(data.Data, 0, data.Data.Length);
            StreamUtil.Fill(stream, 0, alignedLength - data.Data.Length);

            // Correct pointers
            foreach (var fixup in data.PointerFixups)
            {
                writer.BaseStream.Position = tag.HeaderOffset + alignedHeaderSize + fixup.WriteOffset;
                writer.Write(tag.OffsetToPointer(alignedHeaderSize + fixup.TargetOffset));
            }

            UpdateTagOffsets(writer);
        }
예제 #4
0
 /// <summary>
 /// Calculates the header size that would be needed for a given block of tag data.
 /// </summary>
 /// <param name="data">The descriptor to use.</param>
 /// <returns>The size of the tag's header.</returns>
 internal static uint CalculateHeaderSize(CachedTagData data) =>
 (uint)(TagHeaderSize +
        data.Dependencies.Count * 4 +
        data.PointerFixups.Count * 4 +
        data.ResourcePointerOffsets.Count * 4 +
        data.TagReferenceOffsets.Count * 4);