using System; using System.Globalization; using System.Runtime.InteropServices; using Decal.Interop.Core; using Decal.Interop.Dat; namespace Decal.DecalDat { internal enum LibraryType { Cell, Portal } [ComVisible(true)] [Guid("6FA05FDA-B4B5-4386-AB45-92D7E6A5D698")] [ClassInterface(ClassInterfaceType.None)] [ProgId("DecalDat.DatLibrary")] public class DatLibraryImpl : IDatLibrary, IDecalDirectory { private DatFile _datFile; private LibraryType _libraryType; private DatServiceImpl _service; internal void Load(DatServiceImpl service, string filename, LibraryType libraryType, int sectorSize) { _service = service; _libraryType = libraryType; try { _datFile = new DatFile(filename, sectorSize); } catch { // Silent failure matching original C++ behavior _datFile = null; } } /// /// IDatLibrary.Stream - parameterized property returning raw stream for a file ID. /// The COM interface declares this as a parameterized property (DispId 1) taking a DWORD File parameter. /// In the C++ implementation, get_Stream(DWORD dwFile, LPUNKNOWN *pVal) creates a DatStream directly. /// public object Stream { get { // This parameterized property is called via IDispatch with the file ID argument. // When called without parameters from managed code, return null. return null; } } internal object GetStream(uint fileId) { if (_datFile == null) return null; try { var fileEntry = _datFile.GetFile(fileId); var stream = new DatStreamImpl(); stream.Load(fileEntry); return stream; } catch { return null; } } public object Open(string Protocol, uint File) { if (_datFile == null) return null; // Look up filter by protocol name var filter = _service.GetFilter(Protocol); if (filter != null) { // Check cache first var cached = _service.FindInCache(filter, _libraryType, File); if (cached != null) return cached; // Create raw stream DatFileEntry fileEntry; try { fileEntry = _datFile.GetFile(File); } catch { return null; } var stream = new DatStreamImpl(); stream.Load(fileEntry); // Create and initialize filter var filterObj = _service.CreateFilter(filter); if (filterObj == null) return null; var fileFilter = filterObj as IFileFilter; if (fileFilter != null) { fileFilter.Initialize((DatStream)(object)stream); } // Cache if applicable if (filter.Cache) { _service.AddToCache(filter, _libraryType, File, filterObj); } return filterObj; } // No filter - return raw stream return GetStream(File); } /// /// IDecalDirectory.Lookup - parses "PROTOCOL:HEXID" or just "HEXID" format. /// public object Lookup(string strName) { if (string.IsNullOrEmpty(strName)) return null; int colonIdx = strName.IndexOf(':'); if (colonIdx >= 0) { // Format: "protocol:0xHEXID" string protocol = strName.Substring(0, colonIdx); string hexPart = strName.Substring(colonIdx + 1).Trim(); uint fileId = ParseHex(hexPart); return Open(protocol, fileId); } else { // Format: "0xHEXID" - raw stream uint fileId = ParseHex(strName.Trim()); return GetStream(fileId); } } private static uint ParseHex(string hex) { if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) hex = hex.Substring(2); return uint.Parse(hex, NumberStyles.HexNumber); } internal void Dispose() { _datFile?.Dispose(); _datFile = null; } } }