private static TagFixup FinalizeFixup(TagFixup fixup, uint dataOffset)
 {
     return(new TagFixup
     {
         TargetOffset = fixup.TargetOffset,
         WriteOffset = dataOffset + fixup.WriteOffset
     });
 }
        private bool ExecuteAdd(HaloTag tag, List <string> args)
        {
            if (args.Count != 4)
            {
                return(false);
            }
            uint writeOffset, targetOffset;

            if (!uint.TryParse(args[2], NumberStyles.HexNumber, null, out writeOffset))
            {
                return(false);
            }
            if (!uint.TryParse(args[3], NumberStyles.HexNumber, null, out targetOffset))
            {
                return(false);
            }
            if (!CheckWriteOffset(tag, writeOffset))
            {
                return(true);
            }
            if (!CheckTargetOffset(tag, targetOffset))
            {
                return(true);
            }

            // Create a new fixup if there isn't one, otherwise overwrite it
            var fixup = tag.DataFixups.FirstOrDefault(f => f.WriteOffset == writeOffset);

            if (fixup == null)
            {
                fixup = new TagFixup();
                tag.DataFixups.Add(fixup);
            }
            fixup.WriteOffset  = writeOffset;
            fixup.TargetOffset = targetOffset;

            // Update the tag
            using (var stream = _fileInfo.Open(FileMode.Open, FileAccess.ReadWrite))
                _cache.UpdateTag(stream, tag);

            Console.WriteLine("Added fixup at offset 0x{0:X} to 0x{1:X}.", writeOffset, targetOffset);
            return(true);
        }
        public override bool Execute(List<string> args)
        {
            if (args.Count < 3)
                return false;

            int offset;
            if (!int.TryParse(args[1], NumberStyles.HexNumber, null, out offset))
                return false;
            int elementSize;
            if (!int.TryParse(args[2], NumberStyles.HexNumber, null, out elementSize))
                return false;

            // Find the tag
            var tag = FindTagWithOffset(offset);
            if (tag == null)
                return false;
            Console.Write("Detected tag: ");
            TagPrinter.PrintTagShort(tag);
            var offsetFromTag = offset - tag.Offset;

            using (var stream = _fileInfo.Open(FileMode.Open, FileAccess.ReadWrite))
            {
                // Read block info
                stream.Position = offset;
                var reader = new BinaryReader(stream);
                var oldCount = reader.ReadInt32();
                var newCount = oldCount;
                var address = reader.ReadUInt32();
                var fixup = tag.DataFixups.FirstOrDefault(f => f.WriteOffset == offsetFromTag + 4);
                if (fixup == null)
                {
                    if (address != 0)
                    {
                        Console.Error.WriteLine("0x{0:X8} doesn't look like a valid tag block offset!", offset);
                        return true;
                    }

                    // Allocate space at the end of the tag
                    fixup = new TagFixup
                    {
                        WriteOffset = (uint)offsetFromTag + 4,
                        TargetOffset = tag.Size
                    };
                }
                var blockOffset = (int)fixup.TargetOffset;

                // Execute the subcommand
                bool result;
                switch (args[0])
                {
                    case "add":
                        result = AddSubcommand(stream, tag, blockOffset, elementSize, ref newCount, args);
                        break;
                    case "remove":
                        result = RemoveSubcommand(stream, tag, blockOffset, elementSize, ref newCount, args);
                        break;
                    case "insertat":
                        result = InsertAtSubcommand(stream, tag, blockOffset, elementSize, ref newCount, args);
                        break;
                    case "removeat":
                        result = RemoveAtSubcommand(stream, tag, blockOffset, elementSize, ref newCount, args);
                        break;
                    default:
                        return false;
                }
                if (!result)
                    return false;

                // Update the block pointer if necessary
                if (oldCount != newCount)
                {
                    stream.Position = tag.Offset + fixup.WriteOffset - 4;
                    var writer = new BinaryWriter(stream);
                    writer.Write(newCount);
                    if (newCount > 0)
                    {
                        fixup.TargetOffset = (uint)blockOffset;
                        if (address == 0)
                            tag.DataFixups.Add(fixup);
                    }
                    else
                    {
                        tag.DataFixups.Remove(fixup);
                        writer.Write(0);
                    }
                    _cache.UpdateTag(stream, tag);
                }
            }

            Console.WriteLine("Done!");
            return true;
        }
 private static TagFixup FinalizeFixup(TagFixup fixup, uint dataOffset)
 {
     return new TagFixup
     {
         TargetOffset = fixup.TargetOffset,
         WriteOffset = dataOffset + fixup.WriteOffset
     };
 }
Example #5
0
        private bool ExecuteAdd(HaloTag tag, List<string> args)
        {
            if (args.Count != 4)
                return false;
            uint writeOffset, targetOffset;
            if (!uint.TryParse(args[2], NumberStyles.HexNumber, null, out writeOffset))
                return false;
            if (!uint.TryParse(args[3], NumberStyles.HexNumber, null, out targetOffset))
                return false;
            if (!CheckWriteOffset(tag, writeOffset))
                return true;
            if (!CheckTargetOffset(tag, targetOffset))
                return true;

            // Create a new fixup if there isn't one, otherwise overwrite it
            var fixup = tag.DataFixups.FirstOrDefault(f => f.WriteOffset == writeOffset);
            if (fixup == null)
            {
                fixup = new TagFixup();
                tag.DataFixups.Add(fixup);
            }
            fixup.WriteOffset = writeOffset;
            fixup.TargetOffset = targetOffset;

            // Update the tag
            using (var stream = _fileInfo.Open(FileMode.Open, FileAccess.ReadWrite))
                _cache.UpdateTag(stream, tag);

            Console.WriteLine("Added fixup at offset 0x{0:X} to 0x{1:X}.", writeOffset, targetOffset);
            return true;
        }
        private void AnalyzeStructure(BinaryReader reader, uint baseOffset, uint size, TagLayoutGuess result)
        {
            var      lookBehind     = new uint[4];
            TagFixup potentialGuess = null;

            for (uint offset = 0; offset < size; offset += 4)
            {
                var      val = reader.ReadUInt32();
                TagFixup fixup;
                if (_resourceFixupsByWriteOffset.TryGetValue(baseOffset + offset, out fixup))
                {
                    // Value is a resource reference
                    result.Add(offset, new ResourceReferenceGuess());
                }
                else if (_dataFixupsByWriteOffset.TryGetValue(baseOffset + offset, out fixup))
                {
                    // Value is a pointer
                    if (offset >= 0x4)
                    {
                        // Tag block or data reference - need another padding value to confirm it
                        potentialGuess = fixup;
                    }
                }
                else if (offset >= 0xC && lookBehind[0] == 0 && lookBehind[1] == 0 && _cache.ContainsClass(new MagicNumber((int)lookBehind[2])))
                {
                    // Tag reference
                    if (val != 0xFFFFFFFF && val < _cache.Tags.Count)
                    {
                        var referencedTag = _cache.Tags[(int)val];
                        if (referencedTag != null && referencedTag.Class.Value == (int)lookBehind[2])
                        {
                            result.Add(offset - 0xC, new TagReferenceGuess());
                        }
                    }
                }
                else if (val == 0 && potentialGuess != null)
                {
                    // Found a potential padding value - check if we can confirm the potential guess's type
                    if (lookBehind[1] != 0)
                    {
                        // Tag block - seek to it and analyze it
                        reader.BaseStream.Position = potentialGuess.TargetOffset;
                        var elementLayout = AnalyzeStructure(reader, lookBehind[1]);
                        reader.BaseStream.Position = baseOffset + offset + 4;
                        result.Add(offset - 0x8, new TagBlockGuess(elementLayout));
                    }
                    else if (offset >= 0x10 && lookBehind[1] == 0 && lookBehind[2] == 0 && lookBehind[3] != 0)
                    {
                        // Data reference
                        result.Add(offset - 0x10, new DataReferenceGuess());
                    }
                    potentialGuess = null;
                }
                else
                {
                    // Tag block and data reference guesses must be followed by padding
                    potentialGuess = null;
                }
                for (var i = 3; i > 0; i--)
                {
                    lookBehind[i] = lookBehind[i - 1];
                }
                lookBehind[0] = val;
            }
        }