Ejemplo n.º 1
0
        /// <summary>
        /// Inserts data into 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 (currently must be positive).</param>
        /// <param name="type">The type of resize to perform. See <see cref="InsertType"/>.</param>
        private void ResizeTag(Stream stream, HaloTag tag, uint insertOffset, int sizeDelta, InsertType type)
        {
            if (sizeDelta == 0)
            {
                return;
            }
            if (sizeDelta < 0)
            {
                throw new ArgumentException("sizeDelta must be positive for now");                 // i'm lazy
            }
            // If the tag data is being resized, correct relative offsets to account for inserted data
            var headerSize            = GetHeaderSize(tag);
            var relativeCompareOffset = (type == InsertType.Before) ? insertOffset : insertOffset + 1;             // hack

            if (headerSize < relativeCompareOffset)
            {
                tag.Size = (uint)(tag.Size + sizeDelta);
                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 = (type == InsertType.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 the data
            stream.Position = absoluteOffset;
            StreamUtil.Insert(stream, sizeDelta, 0);
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Inserts data into a tag and then updates it.
 /// </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 data, to insert data at.</param>
 /// <param name="sizeDelta">The size of the data to insert (currently must be positive).</param>
 /// <param name="type">The type of resize to perform. See <see cref="InsertType"/>.</param>
 public void ResizeTagData(Stream stream, HaloTag tag, uint insertOffset, int sizeDelta, InsertType type)
 {
     if (tag == null)
     {
         throw new ArgumentNullException("tag");
     }
     ResizeTag(stream, tag, insertOffset + GetHeaderSize(tag), sizeDelta, type);
     UpdateTag(stream, tag);
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Updates a tag's fixup information.
        /// </summary>
        /// <param name="writer">The stream to write to.</param>
        /// <param name="tag">The tag.</param>
        private static void UpdateTagFixups(BinaryWriter writer, HaloTag tag)
        {
            var totalHeaderSize = CalculateHeaderSize(tag.Dependencies.Count, tag.DataFixups.Count, tag.ResourceFixups.Count);

            foreach (var fixup in tag.DataFixups)
            {
                writer.BaseStream.Position = tag.Offset + fixup.WriteOffset;
                writer.Write(fixup.TargetOffset + totalHeaderSize + FixupPointerBase);
            }
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Rebases fixup pointers in tag data.
 /// </summary>
 /// <param name="tag">The tag.</param>
 /// <param name="data">The data.</param>
 /// <param name="newBase">The new base offset to use.</param>
 private void RebasePointers(HaloTag tag, byte[] data, uint newBase)
 {
     using (var writer = new BinaryWriter(new MemoryStream(data)))
     {
         foreach (var fixup in tag.DataFixups)
         {
             writer.BaseStream.Position = fixup.WriteOffset;
             writer.Write(newBase + fixup.TargetOffset + FixupPointerBase);
         }
     }
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Saves any changes made to a tag's properties.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="tag">The tag to update.</param>
        public void UpdateTag(Stream stream, HaloTag tag)
        {
            if (tag == null)
            {
                throw new ArgumentNullException("tag");
            }
            var writer = new BinaryWriter(stream);

            UpdateTagHeader(writer, tag);
            UpdateTagFixups(writer, tag);
            UpdateTagOffsets(writer);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Reads a tag's data from the file, not including its header.
        /// Any pointers in the tag will be adjusted to be relative to the start of the tag's data.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="tag">The tag to read.</param>
        /// <returns>The data that was read.</returns>
        public byte[] ExtractTag(Stream stream, HaloTag tag)
        {
            if (tag == null)
            {
                throw new ArgumentNullException("tag");
            }
            stream.Position = tag.Offset;
            var result = new byte[tag.Size];

            stream.Read(result, 0, result.Length);
            RebasePointers(tag, result, 0);
            return(result);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Overwrites all of a tag's data, including its header, expanding it as necessary.
        /// Any pointers in the tag will be adjusted automatically.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="tag">The tag to overwrite.</param>
        /// <param name="data">The data to write.</param>
        public void OverwriteTag(Stream stream, HaloTag tag, byte[] data)
        {
            if (tag == null)
            {
                throw new ArgumentNullException("tag");
            }
            var headerSize = GetHeaderSize(tag);

            if (tag.Size < data.Length)
            {
                // New data is too big - need to resize the tag
                ResizeTag(stream, tag, headerSize + tag.Size, data.Length - (int)tag.Size, InsertType.Before);
                var writer = new BinaryWriter(stream);
                UpdateTagHeader(writer, tag);
                UpdateTagOffsets(writer);
            }

            // Adjust fixups before injecting the data
            using (var reader = new BinaryReader(new MemoryStream(data)))
            {
                for (var i = 0; i < tag.DataFixups.Count; i++)
                {
                    var fixup = tag.DataFixups[i];
                    reader.BaseStream.Position = fixup.WriteOffset;
                    var newPointer = reader.ReadUInt32();
                    if (newPointer == 0)
                    {
                        // Pointer was nulled - removed it
                        tag.DataFixups.RemoveAt(i);
                        i--;
                        continue;
                    }
                    fixup.TargetOffset = newPointer - FixupPointerBase;
                }
            }
            RebasePointers(tag, data, GetHeaderSize(tag));

            // Write the data
            stream.Position = tag.Offset;
            stream.Write(data, 0, data.Length);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Writes a tag's header back to the file, resizing it if necessary.
        /// </summary>
        /// <param name="writer">The stream to write to.</param>
        /// <param name="tag">The tag.</param>
        private void UpdateTagHeader(BinaryWriter writer, HaloTag tag)
        {
            // Resize the header if necessary
            var newHeaderSize = CalculateHeaderSize(tag.Dependencies.Count, tag.DataFixups.Count, tag.ResourceFixups.Count);
            var oldHeaderSize = GetHeaderSize(tag);

            if (newHeaderSize > oldHeaderSize)
            {
                ResizeTag(writer.BaseStream, tag, oldHeaderSize, (int)newHeaderSize - (int)oldHeaderSize, InsertType.Before);
            }

            // Write the tag header
            // See TagCacheReader for more info on this layout
            var newHeaderOffset = tag.Offset - newHeaderSize;

            _headerOffsets[tag.Index]  = newHeaderOffset;
            writer.BaseStream.Position = newHeaderOffset;
            writer.Write(tag.Checksum);
            writer.Write(tag.Size + newHeaderSize);
            writer.Write((short)tag.Dependencies.Count);
            writer.Write((short)tag.DataFixups.Count);
            writer.Write((short)tag.ResourceFixups.Count);
            writer.Write((short)0);
            writer.Write(tag.MainStructOffset + newHeaderSize);
            writer.Write(tag.Class.Value);
            writer.Write(tag.ParentClass.Value);
            writer.Write(tag.GrandparentClass.Value);
            writer.Write(tag.ClassId);

            // Write dependencies
            foreach (var dependency in tag.Dependencies)
            {
                writer.Write(dependency);
            }

            // Write fixup pointers
            foreach (var fixup in tag.DataFixups.Concat(tag.ResourceFixups))
            {
                writer.Write(fixup.WriteOffset + newHeaderSize + FixupPointerBase);
            }
        }
Ejemplo n.º 9
0
 /// <summary>
 /// Gets the current size of a tag's header.
 /// </summary>
 /// <param name="tag">The tag.</param>
 /// <returns>The current size of the tag's header in bytes.</returns>
 private uint GetHeaderSize(HaloTag tag)
 {
     return(tag.Offset - _headerOffsets[tag.Index]);
 }
Ejemplo n.º 10
0
        /// <summary>
        /// Reads the tags.dat file.
        /// </summary>
        /// <param name="reader">The stream to read from.</param>
        private void Load(BinaryReader reader)
        {
            // Read file header
            reader.BaseStream.Position = 0x4;
            var tagListOffset = reader.ReadInt32();             // 0x4 uint32 offset table offset
            var tagCount      = reader.ReadInt32();             // 0x8 uint32 number of tags

            // Read tag offset list
            reader.BaseStream.Position = tagListOffset;
            for (var i = 0; i < tagCount; i++)
            {
                _headerOffsets.Add(reader.ReadUInt32());
            }

            // Read each tag
            for (var i = 0; i < tagCount; i++)
            {
                if (_headerOffsets[i] == 0)
                {
                    // Offset of 0 = null tag
                    _tags.Add(null);
                    continue;
                }

                // Read header
                reader.BaseStream.Position = _headerOffsets[i];
                var checksum          = reader.ReadUInt32();                                // 0x00 uint32 checksum?
                var totalSize         = reader.ReadUInt32();                                // 0x04 uint32 total size
                var numDependencies   = reader.ReadInt16();                                 // 0x08 int16  dependencies count
                var numDataFixups     = reader.ReadInt16();                                 // 0x0A int16  data fixup count
                var numResourceFixups = reader.ReadInt16();                                 // 0x0C int16  resource fixup count
                reader.BaseStream.Position += 2;                                            // 0x0E int16  (padding)
                var mainStructOffset = reader.ReadUInt32();                                 // 0x10 uint32 main struct offset
                var tagClass         = new MagicNumber(reader.ReadInt32());                 // 0x14 int32  class
                var parentClass      = new MagicNumber(reader.ReadInt32());                 // 0x18 int32  parent class
                var grandparentClass = new MagicNumber(reader.ReadInt32());                 // 0x1C int32  grandparent class
                var classId          = reader.ReadUInt32();                                 // 0x20 uint32 class stringid
                var totalHeaderSize  = CalculateHeaderSize(numDependencies, numDataFixups, numResourceFixups);

                // Construct the tag object
                var tag = new HaloTag
                {
                    Index            = i,
                    Class            = tagClass,
                    ParentClass      = parentClass,
                    GrandparentClass = grandparentClass,
                    MainStructOffset = mainStructOffset - totalHeaderSize,
                    Offset           = _headerOffsets[i] + totalHeaderSize,
                    Size             = totalSize - totalHeaderSize,
                    Checksum         = checksum,
                    ClassId          = classId
                };
                _tags.Add(tag);

                // Read dependencies
                for (var j = 0; j < numDependencies; j++)
                {
                    tag.Dependencies.Add(reader.ReadInt32());
                }

                // Read fixup pointers
                var dataFixupPointers = new uint[numDataFixups];
                for (var j = 0; j < numDataFixups; j++)
                {
                    dataFixupPointers[j] = reader.ReadUInt32();
                }
                var resourceFixupPointers = new uint[numResourceFixups];
                for (var j = 0; j < numResourceFixups; j++)
                {
                    resourceFixupPointers[j] = reader.ReadUInt32();
                }

                // Process fixups
                foreach (var fixup in dataFixupPointers)
                {
                    tag.DataFixups.Add(ReadFixup(reader, fixup, _headerOffsets[i], totalHeaderSize));
                }
                foreach (var fixup in resourceFixupPointers)
                {
                    tag.ResourceFixups.Add(ReadFixup(reader, fixup, _headerOffsets[i], totalHeaderSize));
                }
            }
        }