Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions src/OrthoStreamXP/Scenery/FileSystem/SceneryOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ public SceneryOverlay(SceneryRoots roots, TextureScheduler? textures, bool lazyT

/// <summary>
/// What X-Plane gets for a path. A tile's terrain definition is made in memory. With streaming, a photo texture
/// is streamed level by level unless it is already complete in the cache or the package has its own copy (both
/// are served as they are); everything else, and every texture without streaming, is a backing file from
/// is streamed level by level unless it is already complete in the cache or the lazy store has a copy (both
/// are served as they are; the package's own photo copies are ignored); everything else, and every texture
/// without streaming, is a backing file from
/// <see cref="Resolve(VirtualPath)"/>.
/// </summary>
public OverlayEntry Open(VirtualPath path)
Expand Down Expand Up @@ -188,25 +189,31 @@ private void ReportTextureRequest(VirtualPath path, RequestOutcome outcome, Time

/// <summary>
/// Merged listing of a directory: source entries, replaced by same-named cache entries. The textures
/// directory merges the source's textures\ and _textures\; _textures itself is not shown.
/// directory merges the source's textures\ and _textures\, without the package's own photo textures;
/// _textures itself is not shown.
/// </summary>
public IReadOnlyList<FileSystemInfo> List(VirtualPath directory)
{
if (_lazyTerrainListing && directory.Is("terrain"))
return []; // DSF references terrain definitions by path; X-Plane opens them directly.
var entries = new Dictionary<string, FileSystemInfo>(StringComparer.OrdinalIgnoreCase);
// Lowest priority first, so later folders replace same-named entries.
foreach (string path in _roots.SourceFolders(directory).Reverse().Append(_roots.CachePath(directory)))
foreach (string path in _roots.SourceFolders(directory).Reverse())
AddEntries(entries, directory, path, fromSource: true);
AddEntries(entries, directory, _roots.CachePath(directory), fromSource: false);
return [.. entries.Values.OrderBy(e => e.Name, StringComparer.OrdinalIgnoreCase)];
}

private static void AddEntries(Dictionary<string, FileSystemInfo> entries, VirtualPath directory, string path, bool fromSource)
{
var folder = new DirectoryInfo(path);
if (!folder.Exists) return;
foreach (FileSystemInfo child in folder.EnumerateFileSystemInfos())
{
var folder = new DirectoryInfo(path);
if (!folder.Exists) continue;
foreach (FileSystemInfo child in folder.EnumerateFileSystemInfos())
{
if (directory.IsRoot && HiddenRootEntries.Contains(child.Name, StringComparer.OrdinalIgnoreCase)) continue;
entries[child.Name] = child;
}
if (directory.IsRoot && HiddenRootEntries.Contains(child.Name, StringComparer.OrdinalIgnoreCase)) continue;
if (fromSource && SceneryRoots.IsPackagePhoto(directory.Child(child.Name))) continue;
entries[child.Name] = child;
}
return [.. entries.Values.OrderBy(e => e.Name, StringComparer.OrdinalIgnoreCase)];
}

public (int Terrain, int Textures) RequestCounts()
Expand Down
22 changes: 17 additions & 5 deletions src/OrthoStreamXP/Scenery/FileSystem/SceneryPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,20 @@ public IEnumerable<string> SourceFolders(VirtualPath path) =>
? SourceTextureFolders.Select(folder => Path.Join(Source, folder, string.Join('\\', path.Parts[1..])))
: [Path.Join(Source, string.Join('\\', path.Parts))];

/// <summary>The existing source file or directory behind a virtual path, looking in textures\ then _textures\.</summary>
/// <summary>
/// The existing source file or directory behind a virtual path, looking in textures\ then _textures\.
/// The package's own photo textures are never found (see <see cref="IsPackagePhoto"/>).
/// </summary>
public string? FindInSource(VirtualPath path) =>
SourceFolders(path).FirstOrDefault(candidate => File.Exists(candidate) || Directory.Exists(candidate));
IsPackagePhoto(path) ? null
: SourceFolders(path).FirstOrDefault(candidate => File.Exists(candidate) || Directory.Exists(candidate));

/// <summary>
/// Whether a path names a photo texture (textures\21904_35824_BI16.dds). The package's own copies of these
/// (made by Ortho4XP or similar) are hidden and never served: photos are always streamed or downloaded,
/// while the package's masks (PNGs) and other files pass through as they are.
/// </summary>
public static bool IsPackagePhoto(VirtualPath path) => PhotoTexture(path) is not null;

/// <summary>
/// Where a virtual path is kept in the cache: a photo texture under its cache name (textures\87520_143440_BI18.dds
Expand Down Expand Up @@ -176,9 +187,10 @@ public bool DdsMatchesSize(VirtualPath path, string file)
}

/// <summary>
/// A file under textures/ that the package itself provides: from the source's textures\ or _textures\,
/// or from the lazy texture store (which holds a textures folder's contents). For a DDS this is the
/// local copy that can be served while, or instead of, a download.
/// A file under textures/ that the package itself provides: from the source's textures\ or _textures\
/// (never a photo texture, see <see cref="IsPackagePhoto"/>), or from the lazy texture store (which holds a
/// textures folder's contents). For a store DDS this is the local copy that can be served while, or instead
/// of, a download.
/// </summary>
public string? FindTexture(VirtualPath path)
{
Expand Down
28 changes: 28 additions & 0 deletions tests/OrthoStreamXP.Tests/SceneryOverlayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ public void Textures_merges_the_cache_textures_and_underscore_textures_and_hides
Assert.Equal(["Earth nav data", "terrain", "textures"], overlay.List(VirtualPath.Parse(@"\")).Select(e => e.Name));
}

[Theory]
[InlineData("textures")]
[InlineData("_textures")]
public void The_packages_own_photo_textures_are_hidden_and_never_served_but_its_pngs_are(string folder)
{
_tree.WriteText($"source/{folder}/21904_35824_BI16.dds", "Ortho4XP photo");
string mask = _tree.WritePngHeader($"source/{folder}/21904_35824_ZL16.png", 1024);
string transition = _tree.WritePngHeader($"source/{folder}/water_transition.png", 64);
string other = _tree.WriteText($"source/{folder}/custom.dds", "not a photo texture");
SceneryOverlay overlay = Overlay();

Assert.Equal(["21904_35824_ZL16.png", "custom.dds", "water_transition.png"],
overlay.List(VirtualPath.Of("textures")).Select(e => e.Name));
var notFound = Assert.Throws<NtStatusException>(() => overlay.Resolve(@"\textures\21904_35824_BI16.dds"));
Assert.Equal(NtStatus.ObjectNameNotFound, notFound.Status);
Assert.Equal(mask, overlay.Resolve(@"\textures\21904_35824_ZL16.png"));
Assert.Equal(transition, overlay.Resolve(@"\textures\water_transition.png"));
Assert.Equal(other, overlay.Resolve(@"\textures\custom.dds"));
}

[Fact]
public void A_downloaded_photo_texture_is_served_over_the_packages_own_copy()
{
_tree.WriteText("source/textures/87520_143440_BI18.dds", "Ortho4XP photo");
string cached = _tree.WriteText("cache/textures/10940_17930_PX15.dds", "DDS "); // Overlay() is at level -3
Assert.Equal(cached, Overlay().Resolve(@"\textures\87520_143440_BI18.dds"));
}

[Fact]
public void A_lazy_store_dds_is_served_in_place_without_copying()
{
Expand Down
Loading