using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using AcDream.Content; using DatReaderWriter; using DatReaderWriter.Enums; using DatReaderWriter.Lib.IO; namespace AcDream.Content.Tests; /// /// Adapts to for /// MeshExtractor's constructor, scoped to the equivalence test suite. A /// third copy alongside AcDream.App.Rendering.Wb.DatCollectionAdapter /// (internal to AcDream.App) and AcDream.Bake.BakeDatCollectionAdapter /// (internal to AcDream.Bake) — tests live in the project matching the /// layer under test (structure rule 6), so Content.Tests cannot reference /// AcDream.Bake just to borrow its copy. Plain dat-access glue, not an /// AC-specific algorithm. /// internal sealed class ContentTestDatCollectionAdapter : IDatReaderWriter { private readonly DatCollection _dats; private readonly TestDatDatabaseWrapper _portal; private readonly TestDatDatabaseWrapper _cell; private readonly TestDatDatabaseWrapper _highRes; private readonly TestDatDatabaseWrapper _language; private readonly ReadOnlyDictionary _cellRegions; public ContentTestDatCollectionAdapter(DatCollection dats) { ArgumentNullException.ThrowIfNull(dats); _dats = dats; _portal = new TestDatDatabaseWrapper(dats.Portal); _cell = new TestDatDatabaseWrapper(dats.Cell); _highRes = new TestDatDatabaseWrapper(dats.HighRes); _language = new TestDatDatabaseWrapper(dats.Local); var regions = new Dictionary { [0u] = _cell }; _cellRegions = new ReadOnlyDictionary(regions); } public string SourceDirectory => _dats.Options.DatDirectory ?? string.Empty; public IDatDatabase Portal => _portal; public ReadOnlyDictionary CellRegions => _cellRegions; public IDatDatabase HighRes => _highRes; public IDatDatabase Language => _language; public ReadOnlyDictionary RegionFileMap => new ReadOnlyDictionary(new Dictionary()); public int PortalIteration => _portal.Iteration; public int CellIteration => _cell.Iteration; public int HighResIteration => _highRes.Iteration; public int LanguageIteration => _language.Iteration; public bool TryGetFileBytes(uint regionId, uint fileId, ref byte[] bytes, out int bytesRead) { return _dats.Cell.TryGetFileBytes(fileId, ref bytes, out bytesRead); } public IEnumerable ResolveId(uint id) { var results = new List(); void CheckDb(TestDatDatabaseWrapper wrapper) { var rawDb = wrapper.RawDatabase; if (rawDb.Tree.TryGetFile(id, out _)) { var type = rawDb.TypeFromId(id); if (type != DBObjType.Unknown) { results.Add(new IDatReaderWriter.IdResolution(wrapper, type)); } } } CheckDb(_highRes); CheckDb(_portal); CheckDb(_language); CheckDb(_cell); return results; } public bool TrySave(T obj, int iteration = 0) where T : IDBObj => throw new NotSupportedException($"{nameof(ContentTestDatCollectionAdapter)} is read-only."); public bool TrySave(uint regionId, T obj, int iteration = 0) where T : IDBObj => throw new NotSupportedException($"{nameof(ContentTestDatCollectionAdapter)} is read-only."); public void Dispose() { // The underlying DatCollection is owned by the caller. } } internal sealed class TestDatDatabaseWrapper : IDatDatabase { private readonly DatDatabase _db; private readonly ConcurrentDictionary<(Type, uint), IDBObj> _cache = new(); private readonly object _lock = new(); public TestDatDatabaseWrapper(DatDatabase db) { ArgumentNullException.ThrowIfNull(db); _db = db; } internal DatDatabase RawDatabase => _db; public DatDatabase Db => _db; public int Iteration => _db.Iteration?.CurrentIteration ?? 0; public IEnumerable GetAllIdsOfType() where T : IDBObj => _db.GetAllIdsOfType(); public bool TryGet(uint fileId, [MaybeNullWhen(false)] out T value) where T : IDBObj { if (_cache.TryGetValue((typeof(T), fileId), out var cached)) { value = (T)cached; return true; } lock (_lock) { if (_db.TryGet(fileId, out value)) { _cache.TryAdd((typeof(T), fileId), value); return true; } } return false; } public bool TryGetFileBytes(uint fileId, [MaybeNullWhen(false)] out byte[] value) { lock (_lock) { return _db.TryGetFileBytes(fileId, out value); } } public bool TryGetFileBytes(uint fileId, ref byte[] bytes, out int bytesRead) { lock (_lock) { return _db.TryGetFileBytes(fileId, ref bytes, out bytesRead); } } public bool TrySave(T obj, int iteration = 0) where T : IDBObj => throw new NotSupportedException($"{nameof(TestDatDatabaseWrapper)} is read-only."); public void Dispose() { // The underlying DatDatabase is owned by DatCollection. } }