diff --git a/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs b/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs new file mode 100644 index 0000000000..2af46374fe --- /dev/null +++ b/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs @@ -0,0 +1,73 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Database; +using osu.Game.IO; + +namespace osu.Game.Tests.Database +{ + [TestFixture] + public class LegacyBeatmapImporterTest + { + private readonly TestLegacyBeatmapImporter importer = new TestLegacyBeatmapImporter(); + + [Test] + public void TestSongsSubdirectories() + { + using (var storage = new TemporaryNativeStorage("stable-songs-folder")) + { + var songsStorage = storage.GetStorageForDirectory(StableStorage.STABLE_DEFAULT_SONGS_PATH); + + // normal beatmap folder + var beatmap1 = songsStorage.GetStorageForDirectory("beatmap1"); + createFile(beatmap1, "beatmap.osu"); + + // songs subdirectory + var subdirectory = songsStorage.GetStorageForDirectory("subdirectory"); + createFile(subdirectory, Path.Combine("beatmap2", "beatmap.osu")); + createFile(subdirectory, Path.Combine("beatmap3", "beatmap.osu")); + createFile(subdirectory, Path.Combine("sub-subdirectory", "beatmap4", "beatmap.osu")); + + // songs subdirectory with system file + var subdirectory2 = songsStorage.GetStorageForDirectory("subdirectory2"); + createFile(subdirectory2, ".DS_Store"); + createFile(subdirectory2, Path.Combine("beatmap5", "beatmap.osu")); + createFile(subdirectory2, Path.Combine("beatmap6", "beatmap.osu")); + + // empty songs subdirectory + songsStorage.GetStorageForDirectory("subdirectory3"); + + string[] paths = importer.GetStableImportPaths(songsStorage).ToArray(); + Assert.That(paths.Length, Is.EqualTo(6)); + Assert.That(paths.Contains(songsStorage.GetFullPath("beatmap1"))); + Assert.That(paths.Contains(songsStorage.GetFullPath(Path.Combine("subdirectory", "beatmap2")))); + Assert.That(paths.Contains(songsStorage.GetFullPath(Path.Combine("subdirectory", "beatmap3")))); + Assert.That(paths.Contains(songsStorage.GetFullPath(Path.Combine("subdirectory", "sub-subdirectory", "beatmap4")))); + Assert.That(paths.Contains(songsStorage.GetFullPath(Path.Combine("subdirectory2", "beatmap5")))); + Assert.That(paths.Contains(songsStorage.GetFullPath(Path.Combine("subdirectory2", "beatmap6")))); + } + + static void createFile(Storage storage, string path) + { + using (var stream = storage.CreateFileSafely(path)) + stream.WriteByte(0); + } + } + + private class TestLegacyBeatmapImporter : LegacyBeatmapImporter + { + public TestLegacyBeatmapImporter() + : base(null) + { + } + + public new IEnumerable GetStableImportPaths(Storage storage) => base.GetStableImportPaths(storage); + } + } +} diff --git a/osu.Game/Database/LegacyBeatmapImporter.cs b/osu.Game/Database/LegacyBeatmapImporter.cs index 97f6eba6c2..3477312154 100644 --- a/osu.Game/Database/LegacyBeatmapImporter.cs +++ b/osu.Game/Database/LegacyBeatmapImporter.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.IO; @@ -13,6 +16,24 @@ namespace osu.Game.Database protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); + protected override IEnumerable GetStableImportPaths(Storage storage) + { + foreach (string directory in storage.GetDirectories(string.Empty)) + { + var directoryStorage = storage.GetStorageForDirectory(directory); + + if (!directoryStorage.GetFiles(string.Empty).ExcludeSystemFileNames().Any()) + { + // if a directory doesn't contain files, attempt looking for beatmaps inside of that directory. + // this is a special behaviour in stable for beatmaps only, see https://github.com/ppy/osu/issues/18615. + foreach (string subDirectory in GetStableImportPaths(directoryStorage)) + yield return subDirectory; + } + else + yield return storage.GetFullPath(directory); + } + } + public LegacyBeatmapImporter(IModelImporter importer) : base(importer) { diff --git a/osu.Game/IO/StableStorage.cs b/osu.Game/IO/StableStorage.cs index 84b7da91fc..d4ff8fcdda 100644 --- a/osu.Game/IO/StableStorage.cs +++ b/osu.Game/IO/StableStorage.cs @@ -14,7 +14,7 @@ namespace osu.Game.IO /// public class StableStorage : DesktopStorage { - private const string stable_default_songs_path = "Songs"; + public const string STABLE_DEFAULT_SONGS_PATH = "Songs"; private readonly DesktopGameHost host; private readonly Lazy songsPath; @@ -62,7 +62,7 @@ namespace osu.Game.IO } } - return GetFullPath(stable_default_songs_path); + return GetFullPath(STABLE_DEFAULT_SONGS_PATH); } } }