/* * Computes HUD layout addon name hashes at runtime using the game's own hash function. * AddonConfigEntry uses CRC32 of "name_a" - UIGlobals.ComputeAddonNameHash does this. * This ensures correct hashes across game patches without hardcoded values. */ using FFXIVClientStructs.FFXIV.Client.UI; using System; using System.Collections.Generic; namespace HSUI.Helpers { public static class HudLayoutHashHelper { private static readonly Dictionary _cache = new(); private static DateTime _lastResolveErrorLog = DateTime.MinValue; private const double ResolveErrorLogIntervalSeconds = 10.0; /// Get AddonNameHash for a layout addon. Addon name is without _a suffix (e.g. "_ParameterWidget"). public static uint GetHash(string addonName) { if (string.IsNullOrEmpty(addonName)) return 0; if (_cache.TryGetValue(addonName, out var cached)) return cached; try { uint hash = UIGlobals.ComputeAddonNameHash(addonName); _cache[addonName] = hash; return hash; } catch (Exception ex) { var now = DateTime.UtcNow; if ((now - _lastResolveErrorLog).TotalSeconds >= ResolveErrorLogIntervalSeconds) { _lastResolveErrorLog = now; Plugin.Logger.Warning($"[HSUI] HudLayoutHashHelper: resolver not ready (e.g. '{addonName}'): {ex.Message}"); } return 0; } } /// Dump all HudLayout addon names and their hashes to the log (for debugging). public static void DumpHudLayoutAddonsToLog() { try { var span = FFXIVClientStructs.FFXIV.Client.UI.Misc.HudLayoutAddon.GetSpan(); Plugin.Logger.Information("[HSUI] HudLayout addon names and hashes (name -> hash):"); for (int i = 0; i < span.Length; i++) { ref var addon = ref span[i]; if (!addon.AddonName.HasValue) continue; string name = addon.AddonName.ToString() ?? "(null)"; if (string.IsNullOrEmpty(name)) continue; uint hash = GetHash(name); Plugin.Logger.Information($" [{i}] {name} -> 0x{hash:X8}"); } } catch (Exception ex) { Plugin.Logger.Error($"[HSUI] HudLayoutHashHelper.DumpHudLayoutAddonsToLog failed: {ex.Message}\n{ex.StackTrace}"); } } } }