using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Decal.Interop.Core; using Decal.Interop.Dat; namespace Decal.DecalDat { internal class FileFilterInfo { public string Name; public Guid Clsid; public bool Cache; } internal class CacheEntry { public FileFilterInfo Filter; public LibraryType Library; public uint FileId; public object Instance; } [ComVisible(true)] [Guid("37B083F0-276E-43AD-8D26-3F7449B519DC")] [ClassInterface(ClassInterfaceType.None)] [ProgId("DecalDat.DatService")] public class DatServiceImpl : IDatService, IDecalService, IDecalDirectory { private DatLibraryImpl _cell; private DatLibraryImpl _portal; private readonly List _filters = new List(); private readonly List _cache = new List(); private IDecalCore _decalCore; public void Initialize(DecalCore pDecal) { _decalCore = (IDecalCore)pDecal; // Resolve DAT file paths via MapPath string cellPath = _decalCore.MapPath("%ac%\\cell.dat"); string portalPath = _decalCore.MapPath("%ac%\\portal.dat"); // Load cell.dat (sector size 256) _cell = new DatLibraryImpl(); _cell.Load(this, cellPath, LibraryType.Cell, 256); // Load portal.dat (sector size 1024) _portal = new DatLibraryImpl(); _portal.Load(this, portalPath, LibraryType.Portal, 1024); // Load filter configuration from Decal LoadFilters(); } private void LoadFilters() { // The C++ original calls: // m_pDecal->get_Configuration(L"FileFilters", GUID_NULL, &pEnum) // then iterates the IDecalEnum reading: // pEnum->get_ComClass(&clsid) // pEnum->get_Property(L"Prefix", &vPrefix) // protocol name, e.g. "portal" // pEnum->get_Property(L"Cache", &vCache) // 0 or 1 // // However, IDecalCore.Configuration and IDecalEnum.Property are // parameterized COM properties that the decompiler flattened to // simple getters. They need to be called via IDispatch to pass // the category name / property name arguments. // // Filters are registered externally (by plugins/installer in the // registry under HKLM\SOFTWARE\Decal\FileFilters\{CLSID}) and // will be loaded once Decal.Core exposes the Configuration enum. // // DecalDat still works without filters — callers get raw IDatStream // objects. Filters are an optional layer that parses the stream into // typed objects (e.g., spell tables, character data). } public void BeforePlugins() { // No-op in original implementation } public void AfterPlugins() { // No-op in original implementation } public void Terminate() { _cache.Clear(); _filters.Clear(); _cell?.Dispose(); _cell = null; _portal?.Dispose(); _portal = null; _decalCore = null; } /// /// IDecalDirectory.Lookup - returns cell or portal library by name. /// public object Lookup(string strName) { if (string.Equals(strName, "portal", StringComparison.OrdinalIgnoreCase)) return _portal; if (string.Equals(strName, "cell", StringComparison.OrdinalIgnoreCase)) return _cell; return null; } // --- Filter management (internal, used by DatLibraryImpl) --- internal FileFilterInfo GetFilter(string name) { foreach (var f in _filters) { if (string.Equals(f.Name, name, StringComparison.OrdinalIgnoreCase)) return f; } return null; } internal object CreateFilter(FileFilterInfo filter) { try { var type = Type.GetTypeFromCLSID(filter.Clsid); if (type == null) return null; return Activator.CreateInstance(type); } catch { return null; } } internal object FindInCache(FileFilterInfo filter, LibraryType library, uint fileId) { foreach (var entry in _cache) { if (entry.Filter == filter && entry.Library == library && entry.FileId == fileId) return entry.Instance; } return null; } internal void AddToCache(FileFilterInfo filter, LibraryType library, uint fileId, object instance) { _cache.Add(new CacheEntry { Filter = filter, Library = library, FileId = fileId, Instance = instance }); } /// /// Register a file filter at runtime. /// internal void RegisterFilter(string name, Guid clsid, bool cache) { _filters.Add(new FileFilterInfo { Name = name, Clsid = clsid, Cache = cache }); } } }