diff --git a/BossMod/BossMod.csproj b/BossMod/BossMod.csproj index b10c11ccbc..94632f22f3 100644 --- a/BossMod/BossMod.csproj +++ b/BossMod/BossMod.csproj @@ -94,7 +94,6 @@ - - + diff --git a/BossMod/Debug/DebugObstacles.cs b/BossMod/Debug/DebugObstacles.cs index eae95bc331..cff5e49cd4 100644 --- a/BossMod/Debug/DebugObstacles.cs +++ b/BossMod/Debug/DebugObstacles.cs @@ -51,7 +51,8 @@ protected override void DrawSidebar() ImGui.SameLine(); if (ImGui.Button("Reload")) { - CheckpointNoClone(new(owner.Obstacles.RootPath + e.Filename)); + using var stream = File.OpenRead(owner.Obstacles.RootPath + e.Filename); + CheckpointNoClone(new(stream)); } ImGui.SetNextItemWidth(100); @@ -162,6 +163,7 @@ public void Draw() private void DrawEntries(List entries) { Action? modifications = null; + using var disableScope = ImRaii.Disabled(!Obstacles.CanEditDatabase()); for (int i = 0; i < entries.Count; ++i) { using var id = ImRaii.PushId(i); @@ -174,9 +176,8 @@ private void DrawEntries(List entries) if (ImGui.Button("Move down")) modifications += () => (entries[index], entries[index + 1]) = (entries[index + 1], entries[index]); ImGui.SameLine(); - using (ImRaii.Disabled(!Obstacles.CanEditDatabase())) - if (ImGui.Button("Delete")) - modifications += () => DeleteMap(entries, index); + if (ImGui.Button("Delete")) + modifications += () => DeleteMap(entries, index); ImGui.SameLine(); if (ImGui.Button("Edit")) OpenEditor(entries[index]); @@ -230,7 +231,8 @@ private string GenerateMapName() private void OpenEditor(ObstacleMapDatabase.Entry entry) { - var editor = new Editor(this, new(Obstacles.RootPath + entry.Filename), entry); + using var stream = File.OpenRead(Obstacles.RootPath + entry.Filename); + var editor = new Editor(this, new(stream), entry); _ = new UISimpleWindow($"Obstacle map {entry.Filename}", editor.Draw, true, new(1000, 1000)); } } diff --git a/BossMod/Pathfinding/ObstacleMapDatabase.cs b/BossMod/Pathfinding/ObstacleMapDatabase.cs index c4076ddf1b..a2d5fab3ce 100644 --- a/BossMod/Pathfinding/ObstacleMapDatabase.cs +++ b/BossMod/Pathfinding/ObstacleMapDatabase.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Reflection; using System.Text.Json; namespace BossMod.Pathfinding; @@ -20,36 +19,10 @@ public sealed record class Entry(Vector3 MinBounds, Vector3 MaxBounds, WPos Orig public readonly Dictionary> Entries = []; - public void LoadFromAssembly() - { - try - { - using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("BossMod.Pathfinding.ObstacleMaps.maplist.json") ?? throw new InvalidDataException("Embedded obstacle map file missing"); - LoadInternal(stream); - } - catch (Exception ex) - { - Service.Log($"Failed to load embedded obstacle map database: {ex}"); - } - } - - public void LoadFromFile(string listPath) - { - using var fstream = File.OpenRead(listPath); - try - { - LoadInternal(fstream); - } - catch (Exception ex) - { - Service.Log($"Failed to load obstacle map database '{listPath}': {ex}"); - } - } - - private void LoadInternal(Stream stream) + public void Load(Stream stream) { Entries.Clear(); - using var json = JsonDocument.Parse(stream, new JsonDocumentOptions() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); + using var json = Serialization.ReadJson(stream); foreach (var jentries in json.RootElement.EnumerateObject()) { var sep = jentries.Name.IndexOf('.', StringComparison.Ordinal); diff --git a/BossMod/Pathfinding/ObstacleMapManager.cs b/BossMod/Pathfinding/ObstacleMapManager.cs index 52e7b3a292..7b9662fc49 100644 --- a/BossMod/Pathfinding/ObstacleMapManager.cs +++ b/BossMod/Pathfinding/ObstacleMapManager.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.IO; +using System.Reflection; namespace BossMod.Pathfinding; @@ -45,17 +46,18 @@ public void Dispose() public void ReloadDatabase() { - if (LoadFromSource) + Service.Log($"Loading obstacle database from {(_config.LoadFromSource ? _config.SourcePath : "")}"); + RootPath = _config.LoadFromSource ? _config.SourcePath[..(_config.SourcePath.LastIndexOfAny(['\\', '/']) + 1)] : ""; + + try { - var dbPath = _config.SourcePath; - Service.Log($"Loading obstacle database from '{dbPath}'"); - Database.LoadFromFile(dbPath); - RootPath = dbPath[..(dbPath.LastIndexOfAny(['\\', '/']) + 1)]; + using var dbStream = _config.LoadFromSource ? File.OpenRead(_config.SourcePath) : GetEmbeddedResource("maplist.json"); + Database.Load(dbStream); } - else + catch (Exception ex) { - Service.Log($"Loading embedded obstacle database"); - Database.LoadFromAssembly(); + Service.Log($"Failed to load obstacle database: {ex}"); + Database.Entries.Clear(); } LoadMaps(World.CurrentZone, World.CurrentCFCID); @@ -76,38 +78,19 @@ private void LoadMaps(ushort zoneId, ushort cfcId) { foreach (var e in entries) { - if (LoadFromSource) + try { - var filename = RootPath + e.Filename; - try - { - var bitmap = new Bitmap(filename); - _entries.Add((e, bitmap)); - } - catch (Exception ex) - { - Service.Log($"Failed to load map {filename} for {zoneId}.{cfcId}: {ex}"); - } + using var eStream = _config.LoadFromSource ? File.OpenRead(RootPath + e.Filename) : GetEmbeddedResource(e.Filename); + var bitmap = new Bitmap(eStream); + _entries.Add((e, bitmap)); } - else + catch (Exception ex) { - using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream($"BossMod.Pathfinding.ObstacleMaps.{e.Filename}"); - if (stream == null) - { - Service.Log($"Embedded map missing for {zoneId}.{cfcId}"); - continue; - } - try - { - var bitmap = new Bitmap(stream); - _entries.Add((e, bitmap)); - } - catch (Exception ex) - { - Service.Log($"Failed to load embedded map for {zoneId}.{cfcId}: {ex}"); - } + Service.Log($"Failed to load map {e.Filename} from {(_config.LoadFromSource ? RootPath : "")} for {zoneId}.{cfcId}: {ex}"); } } } } + + private Stream GetEmbeddedResource(string name) => Assembly.GetExecutingAssembly().GetManifestResourceStream($"BossMod.Pathfinding.ObstacleMaps.{name}") ?? throw new InvalidDataException($"Missing embedded resource {name}"); } diff --git a/BossMod/Util/Bitmap.cs b/BossMod/Util/Bitmap.cs index a1f0edfe48..6a530236c4 100644 --- a/BossMod/Util/Bitmap.cs +++ b/BossMod/Util/Bitmap.cs @@ -150,28 +150,28 @@ public Bitmap(int width, int height, Color color0, Color color1, int resolution Pixels = new byte[height * BytesPerRow]; } - public Bitmap(Stream fstream, string filename = "") + public Bitmap(Stream stream) { - using var reader = new BinaryReader(fstream); - var fileHeader = fstream.ReadStruct(); + using var reader = new BinaryReader(stream); + var fileHeader = stream.ReadStruct(); if (fileHeader.Type != Magic) - throw new ArgumentException($"File '{filename}' is not a bitmap: magic is {fileHeader.Type:X4}"); + throw new ArgumentException($"Not a bitmap: magic is {fileHeader.Type:X4}"); - var header = fstream.ReadStruct(); + var header = stream.ReadStruct(); if (header.SizeInBytes != Marshal.SizeOf()) - throw new ArgumentException($"Bitmap '{filename}' has unsupported header size {header.SizeInBytes}"); + throw new ArgumentException($"Bitmap has unsupported header size {header.SizeInBytes}"); if (header.Width <= 0) - throw new ArgumentException($"Bitmap '{filename}' has non-positive width {header.Width}"); + throw new ArgumentException($"Bitmap has non-positive width {header.Width}"); if (header.Height >= 0) - throw new ArgumentException($"Bitmap '{filename}' is not top-down (height={header.Height})"); + throw new ArgumentException($"Bitmap is not top-down (height={header.Height})"); if (header.BitCount != 1) - throw new ArgumentException($"Bitmap '{filename}' is not 1bpp (bitcount={header.BitCount})"); + throw new ArgumentException($"Bitmap is not 1bpp (bitcount={header.BitCount})"); if (header.Compression != 0) - throw new ArgumentException($"Bitmap '{filename}' has unsupported compression method {header.Compression:X8}"); + throw new ArgumentException($"Bitmap has unsupported compression method {header.Compression:X8}"); if (header.XPixelsPerMeter != header.YPixelsPerMeter || header.XPixelsPerMeter <= 0) - throw new ArgumentException($"Bitmap '{filename}' has inconsistent or non-positive resolution {header.XPixelsPerMeter}x{header.YPixelsPerMeter}"); + throw new ArgumentException($"Bitmap has inconsistent or non-positive resolution {header.XPixelsPerMeter}x{header.YPixelsPerMeter}"); if (header.ColorUsedCount is not 0 or 2) - throw new ArgumentException($"Bitmap '{filename}' has wrong palette size {header.ColorUsedCount}"); + throw new ArgumentException($"Bitmap has wrong palette size {header.ColorUsedCount}"); Width = header.Width; Height = -header.Height; @@ -182,8 +182,6 @@ public Bitmap(Stream fstream, string filename = "") Pixels = reader.ReadBytes(Height * BytesPerRow); } - public Bitmap(string filename) : this(File.OpenRead(filename), filename) { } - public void Save(string filename) { using var fstream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read); diff --git a/BossMod/Util/Serialization.cs b/BossMod/Util/Serialization.cs index 97e48dc8d9..01c72b2d32 100644 --- a/BossMod/Util/Serialization.cs +++ b/BossMod/Util/Serialization.cs @@ -26,9 +26,10 @@ public class JsonTypeConverter : JsonConverter public static JsonDocument ReadJson(string path) { using var fstream = File.OpenRead(path); - return JsonDocument.Parse(fstream, new JsonDocumentOptions() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); + return ReadJson(fstream); } + public static JsonDocument ReadJson(Stream stream) => JsonDocument.Parse(stream, new JsonDocumentOptions() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); public static Utf8JsonWriter WriteJson(Stream fstream, bool indented = true) => new(fstream, new JsonWriterOptions() { Indented = indented }); public static unsafe T ReadStruct(this Stream stream) where T : unmanaged diff --git a/TODO b/TODO index bef64be35f..5c5907301d 100644 --- a/TODO +++ b/TODO @@ -44,8 +44,6 @@ general: - zone modules: module info ui - refactor ipc/dtr - questbattles -- collisions for pathfinding --- embedded mode boss modules: - timers diff --git a/UIDev/BitmapEditorTest.cs b/UIDev/BitmapEditorTest.cs index da4601fc6f..1c6e0dcf94 100644 --- a/UIDev/BitmapEditorTest.cs +++ b/UIDev/BitmapEditorTest.cs @@ -1,5 +1,6 @@ using BossMod; using ImGuiNET; +using System.IO; namespace UIDev; @@ -43,7 +44,8 @@ protected override void DrawSidebar() if (ImGui.Button("load")) { - CheckpointNoClone(new("D:\\test.bmp")); + using var stream = File.OpenRead("D:\\test.bmp"); + CheckpointNoClone(new(stream)); } }