/*public void AddGuessAndSubMap(MetaValueGuess guess, MetaMap map) { if (map == null) { AddGuess(guess); return; } // Don't overwrite good guesses with null pointers if (_guesses.ContainsKey(guess.Offset)) { if (guess.Pointer == 0 || guess.Pointer == 0xFFFFFFFF || guess.Pointer == 0xCDCDCDCD) return; } MetaMap previousMap; if (_submaps.TryGetValue(guess.Offset, out previousMap)) { if (map.EstimatedSize > previousMap.EstimatedSize) _guesses[guess.Offset] = guess; } else { _guesses[guess.Offset] = guess; } AssociateSubMap(guess.Offset, map); }*/ public void AssociateSubMap(int offset, MetaMap map) { if (map == null) return; if (!_guesses.ContainsKey(offset)) return; // Bail out if this will cause a cyclic dependency to happen if (map == this || _ancestors.Contains(map) || map._ancestors.Contains(this)) return; // If a map has already been associated with the offset, then merge it, // otherwise just insert it MetaMap oldMap; if (_submaps.TryGetValue(offset, out oldMap)) oldMap.MergeWith(map); else _submaps[offset] = map; // Combine the map's ancestor set with ours map._ancestors.UnionWith(_ancestors); map._ancestors.Add(this); }
private void EstimateMapSize(MetaMap map, uint mapAddress, MemoryMap memMap, int entryCount) { bool alreadyVisited = map.HasSizeEstimates; int newSize = memMap.EstimateBlockSize(mapAddress); map.EstimateSize(newSize / entryCount); map.Truncate(newSize); if (!alreadyVisited) { foreach (MetaValueGuess guess in map.Guesses) { if (guess.Type == MetaValueType.Reflexive) { MetaMap subMap = map.GetSubMap(guess.Offset); if (subMap != null) EstimateMapSize(subMap, guess.Pointer, memMap, (int)guess.Data1); } } } }
private void WritePlugin(MetaMap map, int size, AssemblyPluginWriter writer) { for (int offset = 0; offset < size; offset += 4) { MetaValueGuess guess = map.GetGuess(offset); if (guess != null) { switch (guess.Type) { case MetaValueType.DataReference: if (offset <= size - 0x14) { writer.VisitDataReference("Unknown", (uint)offset, false, 0); offset += 0x10; continue; } break; case MetaValueType.TagReference: if (offset <= size - 0x10) { writer.VisitTagReference("Unknown", (uint)offset, false, true, true, 0); offset += 0xC; continue; } break; case MetaValueType.Reflexive: if (offset <= size - 0xC) { MetaMap subMap = map.GetSubMap(offset); if (subMap != null) { int subMapSize = subMap.GetBestSizeEstimate(); writer.EnterReflexive("Unknown", (uint)offset, false, (uint)subMapSize, 0); WritePlugin(subMap, subMapSize, writer); writer.LeaveReflexive(); offset += 0x8; continue; } } break; } } // Just write an unknown value depending upon how much space we have left if (offset <= size - 4) writer.VisitUndefined("Unknown", (uint)offset, false, 0); else if (offset <= size - 2) writer.VisitInt16("Unknown", (uint)offset, false, 0); else writer.VisitInt8("Unknown", (uint)offset, false, 0); } }
void worker_DoWork(object sender, DoWorkEventArgs e) { GeneratorWorkerArgs args = (GeneratorWorkerArgs)e.Argument; Dictionary<string, MetaMap> globalMaps = new Dictionary<string, MetaMap>(); DateTime startTime = DateTime.Now; List<string> mapFiles = Directory.EnumerateFiles(args.InputFolder, "*.map").ToList(); for (int i = mapFiles.Count - 1; i >= 0; i--) { if (mapFiles[i].EndsWith("shared.map") || mapFiles[i].EndsWith("campaign.map")) mapFiles.RemoveAt(i); } for (int i = 0; i < mapFiles.Count; i++) { Dictionary<ITag, MetaMap> tagMaps = new Dictionary<ITag, MetaMap>(); IReader reader; ICacheFile cacheFile = LoadMap(mapFiles[i], out reader); MetaAnalyzer analyzer = new MetaAnalyzer(cacheFile); Queue<MetaMap> mapsToProcess = new Queue<MetaMap>(); foreach (ITag tag in cacheFile.Tags) { if (tag.MetaLocation.AsAddress() > 0) { MetaMap map = new MetaMap(); tagMaps[tag] = map; mapsToProcess.Enqueue(map); reader.SeekTo(tag.MetaLocation.AsOffset()); analyzer.AnalyzeArea(reader, tag.MetaLocation.AsAddress(), map); } } GenerateSubMaps(mapsToProcess, analyzer, reader, cacheFile); Dictionary<string, MetaMap> classMaps = new Dictionary<string, MetaMap>(); foreach (ITag tag in cacheFile.Tags) { if (tag.MetaLocation.AsAddress() > 0) { MetaMap map = tagMaps[tag]; EstimateMapSize(map, tag.MetaLocation.AsAddress(), analyzer.GeneratedMemoryMap, 1); string magicStr = CharConstant.ToString(tag.Class.Magic); MetaMap oldClassMap; if (classMaps.TryGetValue(magicStr, out oldClassMap)) oldClassMap.MergeWith(map); else classMaps[magicStr] = map; } } foreach (KeyValuePair<string, MetaMap> map in classMaps) { MetaMap globalMap; if (globalMaps.TryGetValue(map.Key, out globalMap)) globalMap.MergeWith(map.Value); else globalMaps[map.Key] = map.Value; } reader.Close(); args.Worker.ReportProgress(100 * i / (mapFiles.Count - 1)); } string badChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); foreach (KeyValuePair<string, MetaMap> map in globalMaps) { string filename = map.Key; foreach (char badChar in badChars) filename = filename.Replace(badChar, '_'); filename += ".xml"; string path = Path.Combine(args.OutputFolder, filename); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = "\t"; XmlWriter writer = XmlWriter.Create(path, settings); AssemblyPluginWriter pluginWriter = new AssemblyPluginWriter(writer, "Halo4"); int size = map.Value.GetBestSizeEstimate(); FoldSubMaps(map.Value); pluginWriter.EnterPlugin(size); pluginWriter.EnterRevisions(); pluginWriter.VisitRevision(new PluginRevision("Assembly", 1, "Generated plugin from scratch.")); pluginWriter.LeaveRevisions(); WritePlugin(map.Value, size, pluginWriter); pluginWriter.LeavePlugin(); writer.Dispose(); } DateTime endTime = DateTime.Now; e.Result = endTime.Subtract(startTime); }
private void GenerateSubMaps(Queue<MetaMap> maps, MetaAnalyzer analyzer, IReader reader, ICacheFile cacheFile) { Dictionary<uint, MetaMap> generatedMaps = new Dictionary<uint, MetaMap>(); while (maps.Count > 0) { MetaMap map = maps.Dequeue(); foreach (MetaValueGuess guess in map.Guesses) { if (guess.Type == MetaValueType.Reflexive) { MetaMap subMap; if (!generatedMaps.TryGetValue(guess.Pointer, out subMap)) { subMap = new MetaMap(); reader.SeekTo(cacheFile.MetaPointerConverter.AddressToOffset(guess.Pointer)); analyzer.AnalyzeArea(reader, guess.Pointer, subMap); maps.Enqueue(subMap); generatedMaps[guess.Pointer] = subMap; } map.AssociateSubMap(guess.Offset, subMap); } } } }
private void FoldSubMaps(MetaMap map) { foreach (MetaValueGuess guess in map.Guesses) { if (guess.Type == MetaValueType.Reflexive) { MetaMap subMap = map.GetSubMap(guess.Offset); if (subMap != null) { int entryCount = (int)guess.Data1; int firstBlockSize = subMap.GetBestSizeEstimate(); //if (firstBlockSize > 0 && !subMap.IsFolded(firstBlockSize)) //{ subMap.Fold(firstBlockSize); FoldSubMaps(subMap); //} } } } }
public void AnalyzeArea(IReader reader, uint startAddress, MetaMap resultMap) { // Right now, this method only searches for the signatures of a few complex meta values. // Reflexives: int32 entry count + uint32 address + 4 bytes of padding // Data references: int32 size + 8 bytes of padding + uint32 address // Tag references: int32 class id + 8 bytes of padding + uint32 datum index // ASCII strings: characters with the values 0 or 0x20 - 0x7F // End at the next-highest address uint endAddress = _memMap.GetNextHighestAddress(startAddress); if (endAddress == 0xFFFFFFFF) throw new InvalidOperationException("Invalid start address for area analysis"); uint size = endAddress - startAddress; // The size of the block of data int paddingLength = 0; // The number of 4-byte padding values that have been read uint prePadding = 0; // The last non-padding uint32 that was read MetaValueGuess pendingGuess = null; // If not null and padding is encountered, this guess is confirmed for (int offset = 0; offset < size; offset += 4) { uint value = reader.ReadUInt32(); if (IsPadding(value)) { if (paddingLength == 0 && pendingGuess != null) { resultMap.AddGuess(pendingGuess); // Add the address to the memory map uint address = pendingGuess.Pointer; _memMap.AddAddress(address, (int)reader.Position); } // More padding! :D paddingLength++; pendingGuess = null; } else { pendingGuess = null; if (offset <= size - 8 && prePadding > 0 && prePadding < 0x80000000 && (value & 3) == 0 && IsValidAddress(value) && (uint)(value + prePadding) > value && IsValidAddress(value + prePadding - 1) && !_memMap.BlockCrossesBoundary(value, (int)prePadding)) { // Either a reflexive or a data reference // Check the padding to determine which (see the comments at the beginning of this method) if (paddingLength == 2 && offset >= 12 && (prePadding & 3) == 0) { // Found a data reference uint dataSize = prePadding; uint address = value; pendingGuess = new MetaValueGuess(offset - 12, MetaValueType.DataReference, address, dataSize); // Guess with Pointer = address, Data1 = data size } else if (paddingLength == 0 && offset >= 4) { // Found a reflexive! uint entryCount = prePadding; uint address = value; pendingGuess = new MetaValueGuess(offset - 4, MetaValueType.Reflexive, address, entryCount); // Guess with Pointer = address, Data1 = entry count } } if (paddingLength == 2 && offset >= 12 && (_classIds.Contains((int)prePadding) || (prePadding == 0xFFFFFFFF && value == 0xFFFFFFFF))) { // Found a tag reference uint classId = prePadding; uint datumIndex = value; MetaValueGuess guess = new MetaValueGuess(offset - 12, MetaValueType.TagReference, datumIndex, classId); // Guess with Pointer = datum index, Data1 = class id resultMap.AddGuess(guess); } // This obviously isn't a padding value because IsPadding returned false, // so update padding run information accordingly prePadding = value; paddingLength = 0; } } }
public void MergeWith(MetaMap other) { if (other == this || _ancestors.Contains(other) || other._ancestors.Contains(this)) return; foreach (MetaValueGuess guess in other._guesses.Values) { MetaMap otherMap = other.GetSubMap(guess.Offset); MetaMap myMap = GetSubMap(guess.Offset); if (otherMap != null || myMap == null) { if (AddGuess(guess)) AssociateSubMap(guess.Offset, otherMap); } } foreach (KeyValuePair<int, int> estimate in other._sizeEstimates) EstimateSize(estimate.Key, estimate.Value); }