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/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.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); 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.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.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.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 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/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/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..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 @@ -153,10 +153,13 @@ + + + - +