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; } }
public void Begin(MagicNumber tagClass, int classStringId, TagLayoutGuess layout) { if (_writer != null) throw new InvalidOperationException("Cannot begin a new tag while another is still in progress"); // Convert the class name to a pascal case string and use that as the file name var name = _stringIds.GetString(classStringId); if (string.IsNullOrEmpty(name)) throw new InvalidOperationException("Unable to look up the tag class name"); name = ConvertToPascalCase(name); var path = Path.Combine(_outDir, name + ".hpp"); _writer = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write)); // Write the C++ header _writer.WriteLine("#pragma once"); _writer.WriteLine("#include \"Tags.hpp\""); _writer.WriteLine(); _writer.WriteLine("namespace Blam"); _writer.WriteLine("{"); _writer.WriteLine("\tnamespace Tags"); _writer.WriteLine("\t{"); _builder = new StructBuilder(_writer, 2, name); _builder.Begin(tagClass, classStringId, layout); }
public void Begin(MagicNumber tagClass, int classStringId, TagLayoutGuess layout) { if (_writer != null) throw new InvalidOperationException("Cannot begin a new tag while another is still in progress"); // Convert the class name to a pascal case string and use that as the file name var name = _stringIds.GetString(classStringId); if (string.IsNullOrEmpty(name)) throw new InvalidOperationException("Unable to look up the tag class name"); name = ConvertToPascalCase(name); var path = Path.Combine(_outDir, name + ".cs"); _writer = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write)); // Write the C# header _writer.WriteLine("using System;"); _writer.WriteLine("using System.Collections.Generic;"); _writer.WriteLine("using System.Linq;"); _writer.WriteLine("using System.Text;"); _writer.WriteLine("using System.Threading.Tasks;"); _writer.WriteLine("using EldoradoLib.Serialization;"); _writer.WriteLine(); _writer.WriteLine("namespace EldoradoLib.TagStructures"); _writer.WriteLine("{"); _builder = new ClassBuilder(_writer, 1, name); _builder.Begin(tagClass, classStringId, layout); }
/// <summary> /// Merges anoter layout into this one. /// </summary> /// <param name="otherLayout">The layout to merge with.</param> public void Merge(TagLayoutGuess otherLayout) { if (otherLayout.Size != Size) Size = Math.Min(Size, otherLayout.Size); // hackhackhack foreach (var guess in otherLayout._guessesByOffset) Add(guess.Key, guess.Value); }
public TagLayoutGuess AnalyzeStructure(BinaryReader reader, uint count) { if (count == 0) throw new ArgumentException("count is 0", "count"); var startOffset = (uint)reader.BaseStream.Position; if (!_tagMap.IsBoundary(startOffset)) throw new InvalidOperationException("Cannot analyze a structure which does not start on a boundary"); var endOffset = _tagMap.GetNextBoundary(startOffset); if (startOffset == endOffset) throw new InvalidOperationException("Structure is empty"); var offset = startOffset; var elementSize = (endOffset - startOffset) / count; var result = new TagLayoutGuess(elementSize); for (var i = 0; i < count; i++) { AnalyzeStructure(reader, offset, elementSize, result); offset += elementSize; reader.BaseStream.Position = offset; } return result; }
public static void Write(HaloTag tag, TagLayoutGuess layout, ITagLayoutWriter writer) { if (tag != null) writer.Begin(tag.Class, tag.ClassId, layout); else writer.Begin(new MagicNumber(0), 0, layout); for (uint offset = 0; offset < layout.Size; offset += 4) { var guess = layout.TryGet(offset); if (guess != null) { writer.AddGuess(offset, guess); offset += guess.Size - 4; } else { var remaining = layout.Size - offset; switch (remaining) { case 3: writer.AddUnknownInt16(offset); writer.AddUnknownByte(offset + 2); break; case 2: writer.AddUnknownInt16(offset); break; case 1: writer.AddUnknownByte(offset); break; default: // >= 4 writer.AddUnknownInt32(offset); break; } } } writer.End(); }
public void Begin(MagicNumber tagClass, int classStringId, TagLayoutGuess layout) { _size = layout.Size; if (tagClass.Value != 0) _writer.WriteLine("{0}struct {1} : Tag<'{2}'>", _indent, _name, tagClass); else _writer.WriteLine("{0}struct {1}", _indent, _name); _writer.WriteLine("{0}{{", _indent); SetIndent(_indentLevel + 1); }
public TagBlockGuess(TagLayoutGuess layout) { if (layout == null) throw new ArgumentNullException("layout"); ElementLayout = layout; }
public void Begin(MagicNumber tagClass, int classStringId, TagLayoutGuess layout) { if (classStringId != 0) _writer.WriteLine("{0}[TagStructure(Class = \"{1}\", Size = 0x{2:X})]", _indent, tagClass, layout.Size); else _writer.WriteLine("{0}[TagStructure(Size = 0x{1:X})]", _indent, layout.Size); _writer.WriteLine("{0}public class {1}", _indent, _name); _writer.WriteLine("{0}{{", _indent); SetIndent(_indentLevel + 1); }