public static void Export(MetaMap map, TextWriter writer, bool unreachableIsSolid = true) { string DetermineCharacter(int x, int y) => map[x, y] switch { TileType.Empty => " ", TileType.Wall => "█", TileType.Door => "□", TileType.PushWall => "□", TileType.Unreachable => unreachableIsSolid ? "█" : " ", _ => "!" }; for (int y = 0; y < map.Height; y++) { for (int x = 0; x < map.Width; x++) { writer.Write(DetermineCharacter(x, y)); } if (y < map.Height - 1) { writer.WriteLine(); } } writer.Flush(); } }
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) { return; } foreach (MetaValueGuess guess in map.Guesses) { if (guess.Type != MetaValueType.Reflexive) { continue; } MetaMap subMap = map.GetSubMap(guess.Offset); if (subMap != null) { EstimateMapSize(subMap, guess.Pointer, memMap, (int)guess.Data1); } } }
private void WritePlugin(MetaMap map, int size, IPluginVisitor 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, "bytes", false, 4, 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, 4, 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); } } }
public static void Export(MetaMap map, MapPalette palette, string outputFilePath, int scale = 1) { using var image = new FastImage(map.Width, map.Height, scale); for (var tileY = 0; tileY < map.Height; tileY++) { for (var tileX = 0; tileX < map.Width; tileX++) { var tileColor = palette.PickColor(map[tileX, tileY]); image.SetPixel(tileX, tileY, tileColor); } } image.Save(outputFilePath); }
private static void RunOptionsAndReturnExitCode(Options opts) { var map = MetaMap.Load(opts.InputMapPath); var imageName = opts.ImageName; if (!string.IsNullOrEmpty(imageName)) { if (!imageName.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) { imageName += ".png"; } } else { imageName = Path.GetFileNameWithoutExtension(opts.InputMapPath) + ".png"; }
private static void ConvertMetaMapsToImages(IEnumerable <string> inputFiles, string outputPath) { var allFiles = inputFiles.ToArray(); if (Directory.Exists(outputPath)) { Directory.Delete(outputPath, recursive: true); } Directory.CreateDirectory(outputPath); using var progress = new ProgressBar(allFiles.Length, "Converting images..."); Parallel.ForEach(allFiles, mapPath => { var imagePath = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(mapPath) + ".png"); var metaMap = MetaMap.Load(mapPath); MetaMapImageExporter.Export(metaMap, MapPalette.Full, imagePath, scale: 4); progress.Tick(); }); }
private void GenerateSubMaps(Queue <MetaMap> maps, MetaAnalyzer analyzer, IReader reader, ICacheFile cacheFile) { var generatedMaps = new Dictionary <uint, MetaMap>(); while (maps.Count > 0) { MetaMap map = maps.Dequeue(); foreach (MetaValueGuess guess in map.Guesses.Where(guess => guess.Type == MetaValueType.Reflexive)) { MetaMap subMap; if (!generatedMaps.TryGetValue(guess.Pointer, out subMap)) { subMap = new MetaMap(); reader.SeekTo(cacheFile.MetaArea.PointerToOffset(guess.Pointer)); analyzer.AnalyzeArea(reader, guess.Pointer, subMap); maps.Enqueue(subMap); generatedMaps[guess.Pointer] = subMap; } map.AssociateSubMap(guess.Offset, subMap); } } }
public void ShouldRoundTripMetaMap() { var numTypes = Enum.GetValues(typeof(TileType)).Length; var m = new MetaMap(4, 5); for (int y = 0; y < m.Height; y++) { for (int x = 0; x < m.Width; x++) { m[x, y] = (TileType)((y * m.Width + x) % numTypes); } } var tempPath = Path.GetTempFileName(); try { m.Save(tempPath); var roundTripped = MetaMap.Load(tempPath); roundTripped.Width.Should().Be(m.Width); roundTripped.Height.Should().Be(m.Height); for (int y = 0; y < m.Height; y++) { for (int x = 0; x < m.Width; x++) { roundTripped[x, y].Should().Be(m[x, y], $"should have matched at ({x},{y})"); } } } finally { if (File.Exists(tempPath)) { File.Delete(tempPath); } } }
private void FoldSubMaps(MetaMap map) { foreach (MetaValueGuess guess in map.Guesses) { if (guess.Type != MetaValueType.Reflexive) { continue; } MetaMap subMap = map.GetSubMap(guess.Offset); if (subMap == null) { continue; } //var entryCount = (int)guess.Data1; int firstBlockSize = subMap.GetBestSizeEstimate(); //if (firstBlockSize > 0 && !subMap.IsFolded(firstBlockSize)) //{ subMap.Fold(firstBlockSize); FoldSubMaps(subMap); //} } }
private void worker_DoWork(object sender, DoWorkEventArgs e, IList <MapEntry> generatorMaps, string outputPath, BackgroundWorker worker) { var globalMaps = new Dictionary <string, MetaMap>(); DateTime startTime = DateTime.Now; string gameIdentifier = ""; worker.ReportProgress(0); for (int i = 0; i < generatorMaps.Count; i++) { var tagMaps = new Dictionary <ITag, MetaMap>(); IReader reader; KeyValuePair <ICacheFile, EngineDescription> cacheData = LoadMap(generatorMaps[i].LocalMapPath, out reader); ICacheFile cacheFile = cacheData.Key; var analyzer = new MetaAnalyzer(cacheFile); if (gameIdentifier == "") { gameIdentifier = cacheData.Value.Settings.GetSetting <string>("shortName"); } var mapsToProcess = new Queue <MetaMap>(); foreach (ITag tag in cacheFile.Tags) { if (tag.MetaLocation == null) { continue; } var map = new MetaMap(); tagMaps[tag] = map; mapsToProcess.Enqueue(map); reader.SeekTo(tag.MetaLocation.AsOffset()); analyzer.AnalyzeArea(reader, tag.MetaLocation.AsPointer(), map); } GenerateSubMaps(mapsToProcess, analyzer, reader, cacheFile); var classMaps = new Dictionary <string, MetaMap>(); foreach (ITag tag in cacheFile.Tags) { if (tag.MetaLocation == null) { continue; } MetaMap map = tagMaps[tag]; EstimateMapSize(map, tag.MetaLocation.AsPointer(), 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 (var map in classMaps) { MetaMap globalMap; if (globalMaps.TryGetValue(map.Key, out globalMap)) { globalMap.MergeWith(map.Value); } else { globalMaps[map.Key] = map.Value; } } reader.Close(); worker.ReportProgress(100 * (i + 1) / (generatorMaps.Count)); } string badChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); foreach (var map in globalMaps) { string filename = badChars.Aggregate(map.Key, (current, badChar) => current.Replace(badChar, '_')); filename += ".xml"; string path = Path.Combine(outputPath, filename); var settings = new XmlWriterSettings { Indent = true, IndentChars = "\t" }; using (XmlWriter writer = XmlWriter.Create(path, settings)) { var pluginWriter = new AssemblyPluginWriter(writer, gameIdentifier); 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(); } } DateTime endTime = DateTime.Now; e.Result = endTime.Subtract(startTime); }
public static void Export(MetaMap map, string outputFilePath, bool unreachableIsSolid = true) { using var fs = File.CreateText(outputFilePath); Export(map, fs, unreachableIsSolid); }