using AcDream.Content; using DatReaderWriter; using DatReaderWriter.DBObjs; namespace AcDream.App.Audio; /// /// Resolves the DID of retail's interface sound bank by walking the dats, the /// way retail does, instead of carrying a literal. /// /// /// ClientUISystem::GetUISoundTable @ 0x00563FB0 calls /// DBObj::GetByEnum @ 0x00415490, and /// DBCache::GetDIDFromEnum @ 0x00413940 resolves the answer with /// two EnumIDMap hops off m_MasterMapID: keyed first by the enum /// INDEX (7 for the UI bank), then by the type key. Same chain and same helper /// shape as and the /// portal tunnel's setup/animation lookup — the master id comes from the portal /// dat header rather than being searched for. /// /// /// /// Against the shipped dats this resolves to 0x2000004B, a table holding /// exactly the 32 UI_* slots. /// UiSoundTableResolutionTests pins the walk, the DID, and the content. /// /// public static class UiSoundTableResolver { /// The enum index the UI sound bank lives at. public const uint UiSoundTableEnumSlot = 7u; /// /// The type key the second hop uses. GetByEnum(type, idx, cache) hands /// (type, idx) to the enum chain and the resolved DID to /// DBCache::Get(did, cache); a SoundTable's CACHE type is 0x22 /// (CLOCache(cache, CSoundTable::Allocator, 0x22)), which is not this /// key. The lane-5 research note transposed the two arguments. /// public const uint UiSoundTableTypeKey = 0x10000003u; /// /// Resolve the bank's DID, or 0 when the dats carry no such chain (in which /// case interface sounds stay silent rather than playing a guessed table). /// public static uint Resolve(IDatReaderWriter dats) { if (dats is null) return 0u; uint masterDid = (uint)dats.Portal.Db.Header.MasterMapId; if (masterDid == 0) return 0u; if (!dats.Portal.TryGet(masterDid, out var master) || master is null) return 0u; if (!master.ClientEnumToID.TryGetValue(UiSoundTableEnumSlot, out uint perSlotDid) || perSlotDid == 0) { return 0u; } if (!dats.Portal.TryGet(perSlotDid, out var perSlot) || perSlot is null) return 0u; return perSlot.ClientEnumToID.TryGetValue(UiSoundTableTypeKey, out uint did) ? did : 0u; } }