using DatReaderWriter; using DatReaderWriter.Options; using AcDream.Content; using DatReaderWriter.Lib.IO; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; namespace AcDream.App; /// /// Opens the read-only DAT collection owned for the lifetime of an App process. /// /// /// DatReaderWriter's default retains /// every unpacked file entry for the lifetime of the collection. The runtime has /// bounded decoded-content caches of its own, so retaining that second, /// unbounded copy makes memory depend on every landblock visited. Keep the small /// B-tree lookup cache, but read file payloads on demand and let the owning /// runtime caches decide their residency. /// internal static class RuntimeDatCollectionFactory { internal static IDatReaderWriter OpenReadOnly(string datDirectory) => new RuntimeDatCollection(new DatCollection(CreateReadOnlyOptions(datDirectory))); internal static DatCollectionOptions CreateReadOnlyOptions(string datDirectory) { ArgumentException.ThrowIfNullOrWhiteSpace(datDirectory); return new DatCollectionOptions { DatDirectory = datDirectory, AccessType = DatAccessType.Read, IndexCachingStrategy = IndexCachingStrategy.OnDemand, FileCachingStrategy = FileCachingStrategy.Never, }; } } /// /// Owns the runtime's one raw DAT handle set and its one shared bounded typed /// object facade. Disposing this owner closes both layers exactly once. /// internal sealed class RuntimeDatCollection : IDatReaderWriter { private readonly DatCollection _raw; private readonly DatCollectionAdapter _bounded; private bool _disposed; internal RuntimeDatCollection(DatCollection raw) { ArgumentNullException.ThrowIfNull(raw); _raw = raw; _bounded = new DatCollectionAdapter(raw); } public string SourceDirectory => _bounded.SourceDirectory; public IDatDatabase Portal => _bounded.Portal; public IDatDatabase Cell => _bounded.Cell; public ReadOnlyDictionary CellRegions => _bounded.CellRegions; public IDatDatabase HighRes => _bounded.HighRes; public IDatDatabase Language => _bounded.Language; public IDatDatabase Local => _bounded.Local; public ReadOnlyDictionary RegionFileMap => _bounded.RegionFileMap; public int PortalIteration => _bounded.PortalIteration; public int CellIteration => _bounded.CellIteration; public int HighResIteration => _bounded.HighResIteration; public int LanguageIteration => _bounded.LanguageIteration; [return: MaybeNull] public T Get(uint fileId) where T : IDBObj => _bounded.Get(fileId); public bool TryGet(uint fileId, [MaybeNullWhen(false)] out T value) where T : IDBObj => _bounded.TryGet(fileId, out value); public IEnumerable GetAllIdsOfType() where T : IDBObj => _bounded.GetAllIdsOfType(); public bool TryGetFileBytes(uint regionId, uint fileId, ref byte[] bytes, out int bytesRead) => _bounded.TryGetFileBytes(regionId, fileId, ref bytes, out bytesRead); public IEnumerable ResolveId(uint id) => _bounded.ResolveId(id); public bool TrySave(T obj, int iteration = 0) where T : IDBObj => _bounded.TrySave(obj, iteration); public bool TrySave(uint regionId, T obj, int iteration = 0) where T : IDBObj => _bounded.TrySave(regionId, obj, iteration); public void Dispose() { if (_disposed) return; _disposed = true; _bounded.Dispose(); _raw.Dispose(); } }