using AcDream.Content; using DatReaderWriter.DBObjs; using DatReaderWriter.Types; namespace AcDream.App.UI.Layout; /// /// Campaign OP slice OP5: reads the Chat tab's two opacity-slider defaults from the /// installed DAT's DBProperties collection — retail /// UIOption::InqDefaultGameplayOptionProperty @0x004ef8d0's /// m_propName-bound path, the ONE genuine consumer of that mechanism (the /// Character tab's 49/50 checkboxes are PlayerOption-bound, not /// m_propName-bound — see 's own /// U1 trace for why THAT tab does not use this seam). /// /// /// Byte-verified recipe (DBCache::GetDIDFromEnum @0x00413940): the outer call /// site is GetDIDFromEnumStatic(&ret, 0x16, 2) — outer arg2 (0x16) /// feeds the inner function's arg3 (the SUB-MAP lookup key), outer arg3 (2) /// feeds arg4 (the MASTER-MAP lookup key FIRST). So: /// master.ClientEnumToID[2] → subMapDid; subMap.ClientEnumToID[0x16] → DID — /// i.e. 's own (enumValue, enumCategory) /// parameter order is (0x16, 2), matching /// RetailUiRuntime.cs's existing Resolve(dats, 2u, 5u) convention /// (enumCategory selects the master-map submap FIRST, enumValue indexes it SECOND). /// Empirically confirmed against the installed DATs: resolves to DID /// 0x78000001, a 3-entry DBProperties — /// 0x1000007F (Bitfield64, the Chat filter blocks' shared /// m_propName — NOT a per-window default; the five blocks' actual defaults are /// the literal constants gmChatOptionsUI::InitOptions authors per window, /// ported verbatim in ChatWindowState/), /// 0x10000080 (Float, 0.5Option_DefaultOpacity_Property), /// 0x10000081 (Float, 1.0Option_ActiveOpacity_Property). /// /// public static class ChatOptionsDatDefaults { /// DAT enum-category key for the FIRST (master-map) lookup — see the /// class doc's byte-verified recipe. private const uint EnumCategory = 2u; /// DAT enum-value key for the SECOND (sub-map) lookup. private const uint EnumValue = 0x16u; /// Option_DefaultOpacity_Property — the unfocused-window opacity /// slider's m_propName. public const uint DefaultOpacityPropertyId = 0x10000080u; /// Option_ActiveOpacity_Property — the focused-window opacity /// slider's m_propName. public const uint ActiveOpacityPropertyId = 0x10000081u; /// Retail base-constructor fallback (ChatInterface::ChatInterface /// @0x004F4550) — used only when the DAT read fails (missing DAT entry, /// corrupt install); the live installed DAT resolves to these exact values today. public const float FallbackDefaultOpacity = 0.5f; public const float FallbackActiveOpacity = 1.0f; /// /// Resolves the two slider defaults from the live DAT. Returns /// (with both out params set to the retail-constructor /// fallback) if the DID does not resolve, the DBProperties object is /// missing, or either property is absent/not a Float — matching this /// codebase's "degrade to a documented fallback, never invent a value" discipline. /// public static bool TryRead( IDatReaderWriter dats, out float defaultOpacity, out float activeOpacity) { defaultOpacity = FallbackDefaultOpacity; activeOpacity = FallbackActiveOpacity; uint did = RetailDataIdResolver.Resolve(dats, EnumValue, EnumCategory); if (did == 0u || !dats.Portal.TryGet(did, out DBProperties? props) || props is null) return false; bool ok = true; if (props.Properties.TryGetValue(DefaultOpacityPropertyId, out BaseProperty? d) && d is FloatBaseProperty defaultProp) defaultOpacity = defaultProp.Value; else ok = false; if (props.Properties.TryGetValue(ActiveOpacityPropertyId, out BaseProperty? a) && a is FloatBaseProperty activeProp) activeOpacity = activeProp.Value; else ok = false; return ok; } }