diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4766411cbd..538e7fa4b7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -11,9 +11,14 @@ using NUnit.Framework; using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.Database; using osu.Game.IO; +using osu.Game.IO.Archives; using osu.Game.Tests.Resources; using SharpCompress.Archives; using SharpCompress.Archives.Zip; @@ -552,6 +557,46 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestUpdateFile() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateFile))) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var temp = TestResources.GetTestBeatmapForImport(); + await osu.Dependencies.Get().Import(temp); + + BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; + BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); + + using (var stream = new MemoryStream()) + { + using (var writer = new StreamWriter(stream, leaveOpen: true)) + { + beatmapToUpdate.BeatmapInfo.Version = "updated"; + new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); + } + + stream.Seek(0, SeekOrigin.Begin); + + using (var reader = new UpdateArchiveReader(manager.Files.Store, setToUpdate, fileToUpdate, stream)) + await manager.Import(setToUpdate, reader); + } + + var allBeatmaps = manager.GetAllUsableBeatmapSets(); + } + finally + { + host.Exit(); + } + } + } + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); @@ -655,5 +700,56 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(task.Wait(timeout), failureMessage); } + + private class UpdateArchiveReader : ArchiveReader + where TModel : class, IHasFiles + where TFileModel : INamedFileInfo, new() + { + private readonly IResourceStore store; + private readonly TModel modelToUpdate; + private readonly TFileModel fileToUpdate; + private readonly Stream newContents; + + public UpdateArchiveReader(IResourceStore store, TModel modelToUpdate, TFileModel fileToUpdate, Stream newContents) + : base(string.Empty) + { + this.store = store; + this.modelToUpdate = modelToUpdate; + this.fileToUpdate = fileToUpdate; + this.newContents = newContents; + } + + public override Stream GetStream(string name) + { + name = name.ToStandardisedPath(); + + if (name.Contains(fileToUpdate.Filename, StringComparison.Ordinal)) + { + var stream = new MemoryStream(); + + newContents.Seek(0, SeekOrigin.Begin); + newContents.CopyTo(stream); + + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } + + TFileModel existing = modelToUpdate.Files.FirstOrDefault(m => name.Contains(m.Filename, StringComparison.Ordinal)); + + if (!string.IsNullOrEmpty(existing?.FileInfo?.StoragePath)) + return store.GetStream(existing.FileInfo.StoragePath); + + return null; + } + + public override void Dispose() + { + } + + public override IEnumerable Filenames => Enumerable.Empty(); // modelToUpdate.Files.Select(f => f.Filename); + + public override Stream GetUnderlyingStream() => null; + } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 433becd8cc..09f40ce7b6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); // Todo: Not all countdown types are supported by lazer yet writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}")); - writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); + writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}")); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 45fe034a70..3838fca282 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -68,7 +68,7 @@ namespace osu.Game.Database public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - protected readonly FileStore Files; + public readonly FileStore Files; protected readonly IDatabaseContextFactory ContextFactory; @@ -222,9 +222,8 @@ namespace osu.Game.Database { model = CreateModel(archive); - if (model == null) return Task.FromResult(null); - - model.Hash = computeHash(archive); + if (model == null) + return Task.FromResult(null); } catch (TaskCanceledException) { @@ -303,6 +302,7 @@ namespace osu.Game.Database LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); + item.Hash = archive != null ? computeHash(archive) : item.Hash; await Populate(item, archive, cancellationToken);