From e72138f3c96dbf7e562fe28b5fcb08aa7137d1f5 Mon Sep 17 00:00:00 2001
From: AsY!um- <377468+AsYlum-@users.noreply.github.com>
Date: Mon, 27 Apr 2026 20:30:00 +0200
Subject: [PATCH 1/4] Add tooltip about how compression applies to files in
UopPacker.
---
.../UserControls/UopPackerControl.Designer.cs | 28 +++++++++++++++++-
.../UserControls/UopPackerControl.resx | 29 ++++++++++++++++---
2 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.Designer.cs b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.Designer.cs
index 806c13f1..35f34c2e 100644
--- a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.Designer.cs
+++ b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.Designer.cs
@@ -39,6 +39,8 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
+ components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UopPackerControl));
label1 = new System.Windows.Forms.Label();
label2 = new System.Windows.Forms.Label();
inmul = new System.Windows.Forms.TextBox();
@@ -85,6 +87,8 @@ private void InitializeComponent()
SelectFolderButton = new System.Windows.Forms.Button();
ExtractSingleFileTabPage = new System.Windows.Forms.TabPage();
compressionBox = new System.Windows.Forms.ComboBox();
+ compressionLabel = new System.Windows.Forms.Label();
+ compressionTip = new System.Windows.Forms.ToolTip(components);
MainStatusStrip = new System.Windows.Forms.StatusStrip();
toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
guilabel = new System.Windows.Forms.ToolStripStatusLabel();
@@ -557,6 +561,7 @@ private void InitializeComponent()
// ExtractSingleFileTabPage
//
ExtractSingleFileTabPage.Controls.Add(compressionBox);
+ ExtractSingleFileTabPage.Controls.Add(compressionLabel);
ExtractSingleFileTabPage.Controls.Add(label1);
ExtractSingleFileTabPage.Controls.Add(label2);
ExtractSingleFileTabPage.Controls.Add(inmul);
@@ -601,13 +606,32 @@ private void InitializeComponent()
// compressionBox
//
compressionBox.BackColor = System.Drawing.Color.White;
+ compressionBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
compressionBox.Items.AddRange(new object[] { "None", "Zlib", "Mythic" });
compressionBox.Location = new System.Drawing.Point(168, 158);
compressionBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
compressionBox.Name = "compressionBox";
compressionBox.Size = new System.Drawing.Size(191, 23);
compressionBox.TabIndex = 8;
- compressionBox.Text = "None";
+ compressionTip.SetToolTip(compressionBox, resources.GetString("compressionBox.ToolTip"));
+ //
+ // compressionLabel
+ //
+ compressionLabel.AutoSize = true;
+ compressionLabel.ForeColor = System.Drawing.SystemColors.GrayText;
+ compressionLabel.Location = new System.Drawing.Point(365, 161);
+ compressionLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ compressionLabel.Name = "compressionLabel";
+ compressionLabel.Size = new System.Drawing.Size(20, 15);
+ compressionLabel.TabIndex = 46;
+ compressionLabel.Text = "(?)";
+ compressionTip.SetToolTip(compressionLabel, resources.GetString("compressionLabel.ToolTip"));
+ //
+ // compressionTip
+ //
+ compressionTip.AutoPopDelay = 20000;
+ compressionTip.InitialDelay = 400;
+ compressionTip.ReshowDelay = 200;
//
// MainStatusStrip
//
@@ -740,6 +764,8 @@ private void InitializeComponent()
private System.Windows.Forms.ComboBox uoptype;
private System.Windows.Forms.ToolStripStatusLabel VersionLabel;
private System.Windows.Forms.ComboBox compressionBox;
+ private System.Windows.Forms.Label compressionLabel;
+ private System.Windows.Forms.ToolTip compressionTip;
private System.Windows.Forms.TextBox inhousingbin;
private System.Windows.Forms.Button inhousingbinbtn;
private System.Windows.Forms.Label labelHousingBin;
diff --git a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx
index 4cbd8996..02402dfe 100644
--- a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx
+++ b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx
@@ -118,15 +118,36 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- 17, 17
+ 155, 17
- 347, 17
+ 519, 17
+
+
+ 17, 17
+
+ Pick the compression that matches the original UOP for the file type:
+ Art / Map / Sound: None
+ Gumpart: None on older clients, Mythic on newer clients (New Legacy 7.0.103)
+ MultiCollection: Zlib
+Picking the wrong option produces a UOP that is much larger than the original or unreadable by the client.
+
+
+
+ Pick the compression that matches the original UOP for the file type:
+ Art / Map / Sound: None
+ Gumpart: None on older clients, Mythic on newer clients (New Legacy 7.0.103)
+ MultiCollection: Zlib
+Picking the wrong option produces a UOP that is much larger than the original or unreadable by the client.
+
- 116, 17
+ 261, 17
- 232, 17
+ 398, 17
+
+
+ 25
\ No newline at end of file
From bc939f68b198a735e3c7d27ad63d06b31e6d389e Mon Sep 17 00:00:00 2001
From: AsY!um- <377468+AsYlum-@users.noreply.github.com>
Date: Mon, 27 Apr 2026 20:53:43 +0200
Subject: [PATCH 2/4] Add logging abstractions and update packages.
---
UoFiddler.Controls/Classes/AppLog.cs | 36 ++++++
.../Classes/DynamicItemsConfig.cs | 4 +-
UoFiddler.Controls/Classes/Options.cs | 11 --
UoFiddler.Controls/Plugin/PluginBase.cs | 8 ++
UoFiddler.Controls/Plugin/PluginServices.cs | 13 +-
UoFiddler.Controls/UoFiddler.Controls.csproj | 6 +-
.../UoFiddler.Plugin.Compare.csproj | 3 -
.../UoFiddler.Plugin.ExamplePlugin.csproj | 3 -
.../UoFiddler.Plugin.MassImport.csproj | 3 -
.../UoFiddler.Plugin.MultiEditor.csproj | 3 +-
.../UoFiddler.Plugin.SendItem.csproj | 3 -
.../UoFiddler.Plugin.UopPacker.csproj | 2 +-
.../UserControls/UopPackerControl.cs | 6 +-
UoFiddler/Classes/FiddlerOptions.cs | 40 +++---
UoFiddler/Classes/UpdateRunner.cs | 6 +-
UoFiddler/FiddlerAppContext.cs | 33 ++---
UoFiddler/Forms/LoadProfileForm.cs | 13 +-
UoFiddler/Forms/MainForm.cs | 14 +-
UoFiddler/Forms/ManagePluginsForm.cs | 10 +-
UoFiddler/Program.cs | 121 +++++++++---------
UoFiddler/UoFiddler.csproj | 5 +-
21 files changed, 194 insertions(+), 149 deletions(-)
create mode 100644 UoFiddler.Controls/Classes/AppLog.cs
diff --git a/UoFiddler.Controls/Classes/AppLog.cs b/UoFiddler.Controls/Classes/AppLog.cs
new file mode 100644
index 00000000..c2a6570b
--- /dev/null
+++ b/UoFiddler.Controls/Classes/AppLog.cs
@@ -0,0 +1,36 @@
+/***************************************************************************
+ *
+ * $Author: Turley
+ *
+ * "THE BEER-WARE LICENSE"
+ * As long as you retain this notice you can do whatever you want with
+ * this stuff. If we meet some day, and you think this stuff is worth it,
+ * you can buy me a beer in return.
+ *
+ ***************************************************************************/
+
+using System;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace UoFiddler.Controls.Classes
+{
+ ///
+ /// Static logger façade for sites that cannot accept ILogger via constructor injection
+ /// (static helpers, designer-built UserControls, plugins instantiated via Activator).
+ /// Constructor-injectable types should take ILogger<T> directly instead of using this.
+ ///
+ public static class AppLog
+ {
+ private static ILoggerFactory _factory = NullLoggerFactory.Instance;
+
+ public static void Initialize(ILoggerFactory factory)
+ {
+ _factory = factory ?? NullLoggerFactory.Instance;
+ }
+
+ public static ILogger For() => _factory.CreateLogger();
+
+ public static ILogger For(Type type) => _factory.CreateLogger(type.FullName);
+ }
+}
diff --git a/UoFiddler.Controls/Classes/DynamicItemsConfig.cs b/UoFiddler.Controls/Classes/DynamicItemsConfig.cs
index 16c8c4f0..fb1c928e 100644
--- a/UoFiddler.Controls/Classes/DynamicItemsConfig.cs
+++ b/UoFiddler.Controls/Classes/DynamicItemsConfig.cs
@@ -14,12 +14,14 @@
using System.Globalization;
using System.IO;
using System.Xml;
+using Microsoft.Extensions.Logging;
using Ultima;
namespace UoFiddler.Controls.Classes
{
public static class DynamicItemsConfig
{
+ private static readonly ILogger _log = AppLog.For(typeof(DynamicItemsConfig));
private static bool _loaded;
public static void EnsureLoaded()
@@ -33,7 +35,7 @@ public static void EnsureLoaded()
string path = Path.Combine(Options.AppDataPath, "DynamicItems.xml");
if (!File.Exists(path))
{
- Options.Logger?.Warning("DynamicItems.xml not found at {Path}", path);
+ _log.LogWarning("DynamicItems.xml not found at {Path}", path);
return;
}
diff --git a/UoFiddler.Controls/Classes/Options.cs b/UoFiddler.Controls/Classes/Options.cs
index bcd74cf4..96d4e0d2 100644
--- a/UoFiddler.Controls/Classes/Options.cs
+++ b/UoFiddler.Controls/Classes/Options.cs
@@ -12,22 +12,11 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
-using Serilog;
namespace UoFiddler.Controls.Classes
{
public static class Options
{
- ///
- /// Logger instance
- ///
- public static ILogger Logger { get; private set; }
-
- public static void SetLogger(ILogger logger)
- {
- Logger = logger;
- }
-
///
/// Defines Element Width in ItemShow
///
diff --git a/UoFiddler.Controls/Plugin/PluginBase.cs b/UoFiddler.Controls/Plugin/PluginBase.cs
index 1204d03c..44321854 100644
--- a/UoFiddler.Controls/Plugin/PluginBase.cs
+++ b/UoFiddler.Controls/Plugin/PluginBase.cs
@@ -10,6 +10,8 @@
***************************************************************************/
using System.Windows.Forms;
+using Microsoft.Extensions.Logging;
+using UoFiddler.Controls.Classes;
using UoFiddler.Controls.Plugin.Interfaces;
namespace UoFiddler.Controls.Plugin
@@ -23,6 +25,12 @@ public abstract class PluginBase
public abstract string Author { get; }
public abstract string Version { get; }
+ ///
+ /// Per-plugin logger, categorized by the concrete plugin type. Resolved on demand
+ /// since plugins are instantiated via Activator and cannot take constructor parameters.
+ ///
+ protected ILogger Logger => AppLog.For(GetType());
+
public abstract void Initialize();
public abstract void Unload();
diff --git a/UoFiddler.Controls/Plugin/PluginServices.cs b/UoFiddler.Controls/Plugin/PluginServices.cs
index 958cc7fb..d21c4199 100644
--- a/UoFiddler.Controls/Plugin/PluginServices.cs
+++ b/UoFiddler.Controls/Plugin/PluginServices.cs
@@ -12,6 +12,7 @@
using System;
using System.IO;
using System.Reflection;
+using Microsoft.Extensions.Logging;
using UoFiddler.Controls.Classes;
using UoFiddler.Controls.Plugin.Interfaces;
using UoFiddler.Controls.UserControls;
@@ -21,6 +22,8 @@ namespace UoFiddler.Controls.Plugin
{
public class PluginServices : IPluginHost
{
+ private static readonly ILogger _log = AppLog.For(typeof(PluginServices));
+
///
/// A Collection of all Plugins Found
///
@@ -40,12 +43,12 @@ public void FindPlugins()
/// Directory to search for Plugins in
public void FindPlugins(string path)
{
- Options.Logger.Information("FindPlugins - searching for plugins in [AppDomain.CurrentDomain.BaseDirectory = {Path}]", path);
+ _log.LogInformation("FindPlugins - searching for plugins in [AppDomain.CurrentDomain.BaseDirectory = {Path}]", path);
AvailablePlugins.Clear();
if (!Directory.Exists(path))
{
- Options.Logger.Warning("FindPlugins - plugin directory doesn't exist: {Path}", path);
+ _log.LogWarning("FindPlugins - plugin directory doesn't exist: {Path}", path);
return;
}
@@ -57,7 +60,7 @@ public void FindPlugins(string path)
}
catch (Exception ex)
{
- Options.Logger.Fatal(ex, "FindPlugins - exception caught");
+ _log.LogCritical(ex, "FindPlugins - exception caught");
}
}
}
@@ -74,7 +77,7 @@ public void ClosePlugins()
continue;
}
- Options.Logger.Information("FindPlugins - disposing plugin: {Plugin}", plugin.Type.ToString());
+ _log.LogInformation("FindPlugins - disposing plugin: {Plugin}", plugin.Type.ToString());
plugin.Instance.Unload();
plugin.Instance = null;
}
@@ -106,7 +109,7 @@ private void AddPlugin(string fileName)
if (Options.PluginsToLoad?.Contains(pluginType.ToString()) == true)
{
- Options.Logger.Information("FindPlugins - AddPlugin of type: {Type} from file: {FileName}", pluginType.ToString(), newPlugin.AssemblyPath);
+ _log.LogInformation("FindPlugins - AddPlugin of type: {Type} from file: {FileName}", pluginType.ToString(), newPlugin.AssemblyPath);
newPlugin.CreateInstance();
newPlugin.Instance.Host = this;
newPlugin.Instance.Initialize();
diff --git a/UoFiddler.Controls/UoFiddler.Controls.csproj b/UoFiddler.Controls/UoFiddler.Controls.csproj
index 2ecc6e19..26402b76 100644
--- a/UoFiddler.Controls/UoFiddler.Controls.csproj
+++ b/UoFiddler.Controls/UoFiddler.Controls.csproj
@@ -427,9 +427,9 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/UoFiddler.Plugin.Compare/UoFiddler.Plugin.Compare.csproj b/UoFiddler.Plugin.Compare/UoFiddler.Plugin.Compare.csproj
index 67a48ff1..214a6193 100644
--- a/UoFiddler.Plugin.Compare/UoFiddler.Plugin.Compare.csproj
+++ b/UoFiddler.Plugin.Compare/UoFiddler.Plugin.Compare.csproj
@@ -23,9 +23,6 @@
..\UoFiddler\bin\$(Configuration)\plugins\
Off
-
-
-
UserControl
diff --git a/UoFiddler.Plugin.ExamplePlugin/UoFiddler.Plugin.ExamplePlugin.csproj b/UoFiddler.Plugin.ExamplePlugin/UoFiddler.Plugin.ExamplePlugin.csproj
index be6bb882..c5c94d00 100644
--- a/UoFiddler.Plugin.ExamplePlugin/UoFiddler.Plugin.ExamplePlugin.csproj
+++ b/UoFiddler.Plugin.ExamplePlugin/UoFiddler.Plugin.ExamplePlugin.csproj
@@ -20,9 +20,6 @@
..\UoFiddler\bin\$(Configuration)\plugins\
Off
-
-
-
Form
diff --git a/UoFiddler.Plugin.MassImport/UoFiddler.Plugin.MassImport.csproj b/UoFiddler.Plugin.MassImport/UoFiddler.Plugin.MassImport.csproj
index d2255940..a9bc664b 100644
--- a/UoFiddler.Plugin.MassImport/UoFiddler.Plugin.MassImport.csproj
+++ b/UoFiddler.Plugin.MassImport/UoFiddler.Plugin.MassImport.csproj
@@ -25,9 +25,6 @@
..\UoFiddler\bin\$(Configuration)\plugins\
Off
-
-
-
Form
diff --git a/UoFiddler.Plugin.MultiEditor/UoFiddler.Plugin.MultiEditor.csproj b/UoFiddler.Plugin.MultiEditor/UoFiddler.Plugin.MultiEditor.csproj
index 4cfa059e..50616991 100644
--- a/UoFiddler.Plugin.MultiEditor/UoFiddler.Plugin.MultiEditor.csproj
+++ b/UoFiddler.Plugin.MultiEditor/UoFiddler.Plugin.MultiEditor.csproj
@@ -71,7 +71,6 @@
-
-
+
\ No newline at end of file
diff --git a/UoFiddler.Plugin.SendItem/UoFiddler.Plugin.SendItem.csproj b/UoFiddler.Plugin.SendItem/UoFiddler.Plugin.SendItem.csproj
index ed12a976..e26e6995 100644
--- a/UoFiddler.Plugin.SendItem/UoFiddler.Plugin.SendItem.csproj
+++ b/UoFiddler.Plugin.SendItem/UoFiddler.Plugin.SendItem.csproj
@@ -22,9 +22,6 @@
..\UoFiddler\bin\$(Configuration)\plugins\
Off
-
-
-
Form
diff --git a/UoFiddler.Plugin.UopPacker/UoFiddler.Plugin.UopPacker.csproj b/UoFiddler.Plugin.UopPacker/UoFiddler.Plugin.UopPacker.csproj
index b1ee9161..3f9d0b60 100644
--- a/UoFiddler.Plugin.UopPacker/UoFiddler.Plugin.UopPacker.csproj
+++ b/UoFiddler.Plugin.UopPacker/UoFiddler.Plugin.UopPacker.csproj
@@ -23,7 +23,7 @@
Off
-
+
diff --git a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs
index 14b697f9..e5a18879 100644
--- a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs
+++ b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs
@@ -12,7 +12,7 @@
using System;
using System.IO;
using System.Windows.Forms;
-using Serilog;
+using Microsoft.Extensions.Logging;
using Ultima;
using UoFiddler.Controls.Classes;
using UoFiddler.Controls.Forms;
@@ -481,9 +481,9 @@ private void Pack(string inFile, string inIdx, string outFile, FileType type, in
private static void LogConverterError(Exception ex, string operation, string input, string output, FileType type)
{
- ILogger logger = Options.Logger;
+ ILogger logger = AppLog.For(typeof(UopPackerControl));
- logger?.Error(ex, "UopPacker {Operation} failed (type={FileType}, input={Input}, output={Output})",
+ logger.LogError(ex, "UopPacker {Operation} failed (type={FileType}, input={Input}, output={Output})",
operation, type, input, output);
}
diff --git a/UoFiddler/Classes/FiddlerOptions.cs b/UoFiddler/Classes/FiddlerOptions.cs
index 254d1722..ee361531 100644
--- a/UoFiddler/Classes/FiddlerOptions.cs
+++ b/UoFiddler/Classes/FiddlerOptions.cs
@@ -15,27 +15,21 @@
using System.IO;
using System.Windows.Forms;
using System.Xml;
+using Microsoft.Extensions.Logging;
using Ultima;
-using UoFiddler.Controls.Classes;
-using Serilog;
using Ultima.Helpers;
+using UoFiddler.Controls.Classes;
namespace UoFiddler.Classes
{
public static class FiddlerOptions
{
+ private static readonly ILogger _log = AppLog.For(typeof(FiddlerOptions));
+
public static List ExternTools { get; private set; }
public static Version AppVersion => typeof(FiddlerOptions).Assembly.GetName().Version;
- public static ILogger Logger { get; private set; }
-
- internal static void SetLogger(ILogger logger)
- {
- Logger = logger;
- Options.SetLogger(logger);
- }
-
///
/// Defines if an Update Check should be made on startup
///
@@ -56,11 +50,11 @@ private static void MoveFiles(IEnumerable files, string path)
string destFileName = Path.Combine(path, file.Name);
if (File.Exists(destFileName))
{
- Logger.Information("MoveFiles. File exists. Skipping: {File}", destFileName);
+ _log.LogInformation("MoveFiles. File exists. Skipping: {File}", destFileName);
continue;
}
- Logger.Information("MoveFiles. Copying file: {File}", destFileName);
+ _log.LogInformation("MoveFiles. Copying file: {File}", destFileName);
file.CopyTo(destFileName);
}
}
@@ -69,14 +63,14 @@ public static void Startup()
{
if (!Directory.Exists(Options.AppDataPath))
{
- Logger.Information("Creating main app data path {AppDataPath}", Options.AppDataPath);
+ _log.LogInformation("Creating main app data path {AppDataPath}", Options.AppDataPath);
Directory.CreateDirectory(Options.AppDataPath);
}
string plugInPath = Path.Combine(Options.AppDataPath, "plugins");
if (!Directory.Exists(plugInPath))
{
- Logger.Information("Creating app data plugin {AppDataPath}", plugInPath);
+ _log.LogInformation("Creating app data plugin {AppDataPath}", plugInPath);
Directory.CreateDirectory(plugInPath);
}
@@ -93,7 +87,7 @@ public static void Startup()
string fileName = Path.Combine(Options.AppDataPath, "Options_default.xml");
if (!File.Exists(fileName))
{
- Logger.Fatal("Can't find default profile file: {FileName}", fileName);
+ _log.LogCritical("Can't find default profile file: {FileName}", fileName);
throw new FileNotFoundException($"Can't load default profile file {fileName}", "Options_default.xml");
}
@@ -104,12 +98,12 @@ public static void SaveProfile()
{
if (Options.ProfileName is null)
{
- Logger.Warning("SaveProfile - ProfileName is null!");
+ _log.LogWarning("SaveProfile - ProfileName is null!");
return;
}
string fileName = Path.Combine(Options.AppDataPath, Options.ProfileName);
- Logger.Information("SaveProfile - start {Filename}", fileName);
+ _log.LogInformation("SaveProfile - start {Filename}", fileName);
XmlDocument dom = new XmlDocument();
XmlDeclaration decl = dom.CreateXmlDeclaration("1.0", "utf-8", null);
@@ -231,7 +225,7 @@ public static void SaveProfile()
{
foreach (string plugIn in Options.PluginsToLoad)
{
- Logger.Information("SaveProfile - saving plugin {PlugIn}", plugIn);
+ _log.LogInformation("SaveProfile - saving plugin {PlugIn}", plugIn);
XmlElement xmlPlugin = dom.CreateElement("Plugin");
xmlPlugin.SetAttribute("name", plugIn);
sr.AppendChild(xmlPlugin);
@@ -286,17 +280,17 @@ public static void SaveProfile()
sr.AppendChild(elem);
dom.Save(fileName);
- Logger.Information("SaveProfile - done {Filename}", fileName);
+ _log.LogInformation("SaveProfile - done {Filename}", fileName);
}
public static void LoadProfile(string filename)
{
- Logger.Information("LoadProfile - start: {Filename}", filename);
+ _log.LogInformation("LoadProfile - start: {Filename}", filename);
string fileName = Path.Combine(Options.AppDataPath, filename);
if (!File.Exists(fileName))
{
- Logger.Warning("LoadProfile: profile file doesn't exist: {Filename}", filename);
+ _log.LogWarning("LoadProfile: profile file doesn't exist: {Filename}", filename);
return;
}
@@ -422,7 +416,7 @@ public static void LoadProfile(string filename)
foreach (XmlElement xPlug in xOptions.SelectNodes("Plugin"))
{
string name = xPlug.GetAttribute("name");
- Logger.Information("LoadProfile: adding plugin to load: {PluginName}", name);
+ _log.LogInformation("LoadProfile: adding plugin to load: {PluginName}", name);
Options.PluginsToLoad.Add(name);
}
@@ -458,7 +452,7 @@ public static void LoadProfile(string filename)
MapHelper.CheckForNewMapSize();
- Logger.Information("LoadProfile - done: {Filename}", filename);
+ _log.LogInformation("LoadProfile - done: {Filename}", filename);
}
}
}
diff --git a/UoFiddler/Classes/UpdateRunner.cs b/UoFiddler/Classes/UpdateRunner.cs
index de1d7fcf..7abb810a 100644
--- a/UoFiddler/Classes/UpdateRunner.cs
+++ b/UoFiddler/Classes/UpdateRunner.cs
@@ -2,11 +2,15 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
+using Microsoft.Extensions.Logging;
+using UoFiddler.Controls.Classes;
namespace UoFiddler.Classes
{
internal static class UpdateRunner
{
+ private static readonly ILogger _log = AppLog.For(typeof(UpdateRunner));
+
public static async Task RunAsync(string repositoryOwner, string repositoryName, Version currentVersion, bool upToDateNotification = true)
{
const string updateCheckCaption = "Check for update";
@@ -51,7 +55,7 @@ public static async Task RunAsync(string repositoryOwner, string repositoryName,
MessageBox.Show("Update check failed. Check application log for details.", updateCheckCaption);
}
- FiddlerOptions.Logger.Error(ex, "Update check failed");
+ _log.LogError(ex, "Update check failed");
}
}
}
diff --git a/UoFiddler/FiddlerAppContext.cs b/UoFiddler/FiddlerAppContext.cs
index 12fa9c0b..adb6ca3b 100644
--- a/UoFiddler/FiddlerAppContext.cs
+++ b/UoFiddler/FiddlerAppContext.cs
@@ -1,9 +1,9 @@
-/***************************************************************************
+/***************************************************************************
*
* $Author: Turley
- *
+ *
* "THE BEER-WARE LICENSE"
- * As long as you retain this notice you can do whatever you want with
+ * As long as you retain this notice you can do whatever you want with
* this stuff. If we meet some day, and you think this stuff is worth it,
* you can buy me a beer in return.
*
@@ -11,7 +11,8 @@
using System;
using System.Windows.Forms;
-using Serilog;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using UoFiddler.Classes;
using UoFiddler.Controls.Classes;
using UoFiddler.Controls.UserControls;
@@ -21,37 +22,37 @@ namespace UoFiddler
{
internal sealed class FiddlerAppContext : ApplicationContext
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
- internal FiddlerAppContext(ILogger logger)
+ internal FiddlerAppContext(IServiceProvider services)
{
- _logger = logger;
+ AppLog.Initialize(services.GetRequiredService());
+ _logger = services.GetRequiredService>();
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ApplicationExit += OnApplicationExit;
- FiddlerOptions.SetLogger(_logger);
FiddlerOptions.Startup();
- _logger.Information("Starting loading profile form...");
- var profile = new LoadProfileForm { TopMost = true };
+ _logger.LogInformation("Starting loading profile form...");
+ var profile = new LoadProfileForm(services.GetRequiredService>()) { TopMost = true };
var profileResult = profile.ShowDialog();
if (profileResult == DialogResult.Cancel)
{
- _logger.Information("No profile loaded... exiting");
+ _logger.LogInformation("No profile loaded... exiting");
return;
}
if (FiddlerOptions.UpdateCheckOnStart)
{
- _logger.Information("Update check. Current version is {Version}", FiddlerOptions.AppVersion);
+ _logger.LogInformation("Update check. Current version is {Version}", FiddlerOptions.AppVersion);
UpdateRunner.RunAsync(FiddlerOptions.RepositoryOwner, FiddlerOptions.RepositoryName, FiddlerOptions.AppVersion, false).GetAwaiter().GetResult();
}
- _logger.Information("Starting main form...");
- MainForm = new MainForm
+ _logger.LogInformation("Starting main form...");
+ MainForm = new MainForm(services.GetRequiredService>())
{
Text = $"{Application.ProductName} (Profile: {Options.ProfileName.Replace("Options_", "").Replace(".xml", "")})"
};
@@ -62,7 +63,7 @@ private void OnApplicationExit(object sender, EventArgs e)
{
FiddlerOptions.SaveProfile();
MapControl.SaveMapOverlays();
- _logger.Information("UOFiddler - Application exit");
+ _logger.LogInformation("UOFiddler - Application exit");
}
}
-}
\ No newline at end of file
+}
diff --git a/UoFiddler/Forms/LoadProfileForm.cs b/UoFiddler/Forms/LoadProfileForm.cs
index 9b3fb64f..01a94f04 100644
--- a/UoFiddler/Forms/LoadProfileForm.cs
+++ b/UoFiddler/Forms/LoadProfileForm.cs
@@ -12,6 +12,7 @@
using System;
using System.IO;
using System.Windows.Forms;
+using Microsoft.Extensions.Logging;
using UoFiddler.Controls.Classes;
using UoFiddler.Classes;
@@ -20,14 +21,18 @@ namespace UoFiddler.Forms
public partial class LoadProfileForm : Form
{
private readonly string[] _profiles;
+ private readonly ILogger _log;
- public LoadProfileForm()
+ public LoadProfileForm() : this(AppLog.For()) { }
+
+ public LoadProfileForm(ILogger logger)
{
+ _log = logger;
InitializeComponent();
Icon = Options.GetFiddlerIcon();
_profiles = GetProfiles();
- FiddlerOptions.Logger.Information("Found profiles: {Profiles}", _profiles);
+ _log.LogInformation("Found profiles: {Profiles}", _profiles);
foreach (string profile in _profiles)
{
@@ -67,7 +72,7 @@ private void LoadSelectedProfile()
}
Options.ProfileName = $"{_profiles[comboBoxLoad.SelectedIndex]}.xml";
- FiddlerOptions.Logger.Information("Loading profile: {ProfileName}", Options.ProfileName);
+ _log.LogInformation("Loading profile: {ProfileName}", Options.ProfileName);
FiddlerOptions.LoadProfile($"{_profiles[comboBoxLoad.SelectedIndex]}.xml");
Close();
@@ -82,7 +87,7 @@ private void OnClickCreate(object sender, EventArgs e)
}
Options.ProfileName = $"Options_{textBoxCreate.Text}.xml";
- FiddlerOptions.Logger.Information("Creating profile: {ProfileName}", Options.ProfileName);
+ _log.LogInformation("Creating profile: {ProfileName}", Options.ProfileName);
FiddlerOptions.LoadProfile($"{_profiles[comboBoxBasedOn.SelectedIndex]}.xml");
Close();
diff --git a/UoFiddler/Forms/MainForm.cs b/UoFiddler/Forms/MainForm.cs
index 04567475..5ed75eee 100644
--- a/UoFiddler/Forms/MainForm.cs
+++ b/UoFiddler/Forms/MainForm.cs
@@ -15,6 +15,7 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
+using Microsoft.Extensions.Logging;
using Ultima;
using Ultima.Helpers;
using UoFiddler.Classes;
@@ -41,8 +42,13 @@ private struct NativeRect { public int Left, Top, Right, Bottom; }
private const int WM_LBUTTONUP = 0x0202;
private const int MK_LBUTTON = 0x0001;
- public MainForm()
+ private readonly ILogger _log;
+
+ public MainForm() : this(AppLog.For()) { }
+
+ public MainForm(ILogger logger)
{
+ _log = logger;
InitializeComponent();
if (FiddlerOptions.StoreFormState)
@@ -461,7 +467,7 @@ private void OnClickManagePlugins(object sender, EventArgs e)
private void OnClosing(object sender, FormClosingEventArgs e)
{
- FiddlerOptions.Logger.Information("MainForm - OnClosing - start");
+ _log.LogInformation("MainForm - OnClosing - start");
string files = Options.ChangedUltimaClass
.Where(key => key.Value)
.Aggregate(string.Empty, (current, key) => current + $"- {key.Key} \r\n");
@@ -483,10 +489,10 @@ private void OnClosing(object sender, FormClosingEventArgs e)
FiddlerOptions.FormPosition = Location;
FiddlerOptions.FormSize = Size;
- FiddlerOptions.Logger.Information("MainForm - OnClosing - unloading plugins");
+ _log.LogInformation("MainForm - OnClosing - unloading plugins");
GlobalPlugins.Plugins.ClosePlugins();
- FiddlerOptions.Logger.Information("MainForm - OnClosing - done");
+ _log.LogInformation("MainForm - OnClosing - done");
}
private static bool IsOkFormStateLocation(Point loc, Size size)
diff --git a/UoFiddler/Forms/ManagePluginsForm.cs b/UoFiddler/Forms/ManagePluginsForm.cs
index 1af30bf4..a1d2cb39 100644
--- a/UoFiddler/Forms/ManagePluginsForm.cs
+++ b/UoFiddler/Forms/ManagePluginsForm.cs
@@ -12,7 +12,7 @@
using System;
using System.Drawing;
using System.Windows.Forms;
-using UoFiddler.Classes;
+using Microsoft.Extensions.Logging;
using UoFiddler.Controls.Classes;
using UoFiddler.Controls.Plugin;
@@ -20,6 +20,8 @@ namespace UoFiddler.Forms
{
public partial class ManagePluginsForm : Form
{
+ private static readonly ILogger _log = AppLog.For(typeof(ManagePluginsForm));
+
public ManagePluginsForm()
{
InitializeComponent();
@@ -30,7 +32,7 @@ public ManagePluginsForm()
bool loaded = true;
if (plugin.Instance == null)
{
- FiddlerOptions.Logger.Information("ManagePlugins - creating plugin instance: {Plugin} path: {AssemblyPath}", plugin.Type, plugin.AssemblyPath);
+ _log.LogInformation("ManagePlugins - creating plugin instance: {Plugin} path: {AssemblyPath}", plugin.Type, plugin.AssemblyPath);
plugin.CreateInstance();
loaded = false;
}
@@ -75,7 +77,7 @@ private void OnClosing(object sender, FormClosingEventArgs e)
{
if (checkedListBox1.CheckedItems.Contains(plug.Instance.Name))
{
- FiddlerOptions.Logger.Information("ManagePlugins - adding plugin to profile: {Plugin}", plug.Type.ToString());
+ _log.LogInformation("ManagePlugins - adding plugin to profile: {Plugin}", plug.Type.ToString());
Options.PluginsToLoad.Add(plug.Type.ToString());
}
@@ -85,7 +87,7 @@ private void OnClosing(object sender, FormClosingEventArgs e)
{
if (!checkedListBox1.CheckedItems.Contains(plug.Instance.Name))
{
- FiddlerOptions.Logger.Information("ManagePlugins - removing plugin from profile: {Plugin}", plug.Type.ToString());
+ _log.LogInformation("ManagePlugins - removing plugin from profile: {Plugin}", plug.Type.ToString());
Options.PluginsToLoad.Remove(plug.Type.ToString());
}
}
diff --git a/UoFiddler/Program.cs b/UoFiddler/Program.cs
index d3fc5757..fc8b7b48 100644
--- a/UoFiddler/Program.cs
+++ b/UoFiddler/Program.cs
@@ -1,58 +1,63 @@
-/***************************************************************************
- *
- * $Author: Turley
- *
- * "THE BEER-WARE LICENSE"
- * As long as you retain this notice you can do whatever you want with
- * this stuff. If we meet some day, and you think this stuff is worth it,
- * you can buy me a beer in return.
- *
- ***************************************************************************/
-
-using System;
-using System.IO;
-using System.Text;
-using System.Windows.Forms;
-using Serilog;
-using UoFiddler.Forms;
-
-namespace UoFiddler
-{
- internal static class Program
- {
- ///
- /// The main entry point for the application.
- ///
- [STAThread]
- private static void Main()
- {
- string fiddlerAppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "UoFiddler");
- string logFileName = Path.Combine(fiddlerAppDataPath, "log", "uo-fiddler-log.txt");
-
- var logger = new LoggerConfiguration()
- .MinimumLevel.Information()
- .WriteTo.File(logFileName,
- fileSizeLimitBytes: 44040192,
- rollingInterval: RollingInterval.Day,
- rollOnFileSizeLimit: true)
- .CreateLogger();
-
- logger.Information("UOFiddler - Application start");
-
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
-
- try
- {
- FiddlerAppContext fiddlerAppContext = new FiddlerAppContext(logger);
- Application.Run(fiddlerAppContext);
- }
- catch (Exception err)
- {
- Clipboard.SetDataObject(err.ToString(), true);
- logger.Fatal(err, "UOFiddler - unhandled exception caught!");
- Application.Run(new ExceptionForm(err));
- logger.Fatal("UOFiddler - unhandled exception - Application exit");
- }
- }
- }
-}
\ No newline at end of file
+/***************************************************************************
+ *
+ * $Author: Turley
+ *
+ * "THE BEER-WARE LICENSE"
+ * As long as you retain this notice you can do whatever you want with
+ * this stuff. If we meet some day, and you think this stuff is worth it,
+ * you can buy me a beer in return.
+ *
+ ***************************************************************************/
+
+using System;
+using System.IO;
+using System.Text;
+using System.Windows.Forms;
+using Microsoft.Extensions.DependencyInjection;
+using Serilog;
+using UoFiddler.Forms;
+
+namespace UoFiddler
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ private static void Main()
+ {
+ string fiddlerAppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "UoFiddler");
+ string logFileName = Path.Combine(fiddlerAppDataPath, "log", "uo-fiddler-log.txt");
+
+ var serilogLogger = new LoggerConfiguration()
+ .MinimumLevel.Information()
+ .WriteTo.File(logFileName,
+ fileSizeLimitBytes: 44040192,
+ rollingInterval: RollingInterval.Day,
+ rollOnFileSizeLimit: true)
+ .CreateLogger();
+
+ serilogLogger.Information("UOFiddler - Application start");
+
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ var services = new ServiceCollection();
+ services.AddLogging(b => b.AddSerilog(serilogLogger, dispose: true));
+ using var serviceProvider = services.BuildServiceProvider();
+
+ try
+ {
+ FiddlerAppContext fiddlerAppContext = new FiddlerAppContext(serviceProvider);
+ Application.Run(fiddlerAppContext);
+ }
+ catch (Exception err)
+ {
+ Clipboard.SetDataObject(err.ToString(), true);
+ serilogLogger.Fatal(err, "UOFiddler - unhandled exception caught!");
+ Application.Run(new ExceptionForm(err));
+ serilogLogger.Fatal("UOFiddler - unhandled exception - Application exit");
+ }
+ }
+ }
+}
diff --git a/UoFiddler/UoFiddler.csproj b/UoFiddler/UoFiddler.csproj
index 9ac78ff6..588c86e2 100644
--- a/UoFiddler/UoFiddler.csproj
+++ b/UoFiddler/UoFiddler.csproj
@@ -153,10 +153,13 @@
+
+
+
-
+
From 2e706492ef3b0bdac6719d020329938f40ff95e5 Mon Sep 17 00:00:00 2001
From: AsY!um- <377468+AsYlum-@users.noreply.github.com>
Date: Thu, 30 Apr 2026 16:13:55 +0200
Subject: [PATCH 3/4] Redo ValidateStaticSize() method in Art.
---
Ultima/Art.cs | 58 +++++++++++++++----
.../UserControls/ItemsControl.cs | 8 +--
2 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/Ultima/Art.cs b/Ultima/Art.cs
index 4ab3b15c..a75a67ff 100644
--- a/Ultima/Art.cs
+++ b/Ultima/Art.cs
@@ -36,12 +36,16 @@ static Art()
}
///
- /// Validates if a static bitmap will fit within the MUL format limits.
- /// The format uses 16-bit lookup table offsets, limiting total encoded data to ~65,535 ushorts.
+ /// Validates if a static bitmap will fit within the MUL format limits by computing
+ /// the exact encoded size. The format uses 16-bit lookup table offsets, limiting total
+ /// encoded data to 65,535 ushorts. A pixel is considered opaque when its alpha bit
+ /// (0x8000) is set in 16bppArgb1555 — callers that want pure-black/white treated as
+ /// transparent must run the bitmap through Utils.ConvertBmp first (mirrors the save path).
+ /// Per-row encoded cost: 2 ushorts header per opaque run + 1 ushort per opaque pixel + 2 end markers.
///
/// The bitmap to validate
- /// Estimated size in ushorts (output)
- /// True if the image should fit, false if it exceeds limits
+ /// Encoded size in ushorts (output)
+ /// True if the image fits, false if it exceeds limits
public static unsafe bool ValidateStaticSize(Bitmap bmp, out int estimatedSize)
{
estimatedSize = 0;
@@ -50,14 +54,48 @@ public static unsafe bool ValidateStaticSize(Bitmap bmp, out int estimatedSize)
return true;
}
- // Estimate worst case: each scanline has full width of visible pixels
- // Format: 2 ushorts for offset/run + width ushorts for data + 2 ushorts for end markers per line
- int maxUshortsPerLine = 4 + bmp.Width;
- estimatedSize = bmp.Height * maxUshortsPerLine;
+ BitmapData bd = bmp.LockBits(
+ new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format16bppArgb1555);
- // The lookup table uses 16-bit offsets, so we're limited to 65535 ushorts
- const int maxUshorts = 65535;
+ int total = 0;
+ try
+ {
+ var line = (ushort*)bd.Scan0;
+ int delta = bd.Stride >> 1;
+
+ for (int y = 0; y < bmp.Height; ++y, line += delta)
+ {
+ ushort* cur = line;
+ int x = 0;
+ while (x < bmp.Width)
+ {
+ while (x < bmp.Width && (cur[x] & 0x8000) == 0)
+ {
+ ++x;
+ }
+ if (x >= bmp.Width)
+ {
+ break;
+ }
+ int runStart = x;
+ while (x < bmp.Width && (cur[x] & 0x8000) != 0)
+ {
+ ++x;
+ }
+ total += 2 + (x - runStart);
+ }
+ total += 2;
+ }
+ }
+ finally
+ {
+ bmp.UnlockBits(bd);
+ }
+
+ estimatedSize = total;
+
+ const int maxUshorts = 65535;
return estimatedSize <= maxUshorts;
}
diff --git a/UoFiddler.Controls/UserControls/ItemsControl.cs b/UoFiddler.Controls/UserControls/ItemsControl.cs
index 4a345a0d..7f0d636e 100644
--- a/UoFiddler.Controls/UserControls/ItemsControl.cs
+++ b/UoFiddler.Controls/UserControls/ItemsControl.cs
@@ -519,11 +519,11 @@ private void OnClickReplace(object sender, EventArgs e)
MessageBox.Show(
$"Image is too large for MUL format!\n\n" +
$"Image dimensions: {bitmap.Width}x{bitmap.Height}\n" +
- $"Estimated encoded size: {estimatedSize:N0} ushorts\n" +
+ $"Encoded size: {estimatedSize:N0} ushorts\n" +
$"Maximum allowed: 65,535 ushorts\n\n" +
- $"Recommended maximum for solid color images: ~254x254 pixels\n" +
- $"Images with transparency can be larger.\n\n" +
- $"Please use a smaller image.",
+ $"The static art format encodes opaque pixel runs only; cost per row is\n" +
+ $"2 ushorts per run + 1 ushort per opaque pixel + 2 end markers.\n" +
+ $"Reduce the image size or the amount of opaque content.",
"Image Too Large",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
From 9e84574f92f1911da03d37691fa049a1c519c17b Mon Sep 17 00:00:00 2001
From: AsY!um- <377468+AsYlum-@users.noreply.github.com>
Date: Thu, 30 Apr 2026 16:26:29 +0200
Subject: [PATCH 4/4] Update change log and version.
---
UoFiddler/Forms/AboutBoxForm.resx | 7 ++++++-
UoFiddler/UoFiddler.csproj | 6 +++---
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/UoFiddler/Forms/AboutBoxForm.resx b/UoFiddler/Forms/AboutBoxForm.resx
index 47a0bc6e..46d41fcb 100644
--- a/UoFiddler/Forms/AboutBoxForm.resx
+++ b/UoFiddler/Forms/AboutBoxForm.resx
@@ -118,7 +118,12 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- Version 4.19.1
+ Version 4.19.2
+- Fix validation when adding and replacing images in Items and Land tiles tabs.
+- Added tooltips explaining compression settings in UopPacker plugin.
+- Minor fixes to internal logger construction.
+
+Version 4.19.1
- UopPacker: added save support for MultiCollection.uop (including housing.bin) and refreshed the plugin UI.
- Map copy/replace dialog now supports UOP map files. #105
- Added "Replace from folder" bulk action for Items and Land Tiles tabs. #102
diff --git a/UoFiddler/UoFiddler.csproj b/UoFiddler/UoFiddler.csproj
index 588c86e2..93c29dc0 100644
--- a/UoFiddler/UoFiddler.csproj
+++ b/UoFiddler/UoFiddler.csproj
@@ -9,9 +9,9 @@
UoFiddler
UoFiddler
Copyright © 2026
- 4.19.1
- 4.19.1
- 4.19.1
+ 4.19.2
+ 4.19.2
+ 4.19.2
true