From 06e5ef88c041c6298e3b2d1e8d64c5f28171d2ac Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 11 Jul 2023 02:30:16 +0200 Subject: [PATCH 01/15] legacy export broken --- osu.Game/Beatmaps/BeatmapManager.cs | 53 ++++++++++++++++++++ osu.Game/Beatmaps/BeatmapSetInfo.cs | 13 +++++ osu.Game/Database/ModelManager.cs | 2 + osu.Game/Localisation/EditorStrings.cs | 5 ++ osu.Game/Rulesets/Objects/BezierConverter.cs | 7 +++ osu.Game/Screens/Edit/Editor.cs | 7 +++ 6 files changed, 87 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 305dc01844..7d8c47ea5a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -18,12 +18,15 @@ using osu.Framework.Platform; using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.Extensions; +using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Models; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; using osu.Game.Utils; @@ -400,6 +403,56 @@ namespace osu.Game.Beatmaps public Task Export(BeatmapSetInfo beatmap) => beatmapExporter.ExportAsync(beatmap.ToLive(Realm)); + /// + /// Creates a copy of the and converts all beatmaps to legacy format, then exports it as a legacy package. + /// + /// + /// + public Task ExportLegacy(BeatmapSetInfo beatmap) + { + var copy = beatmap.Clone(); // does the detach from realm + + // convert all beatmaps to legacy format + foreach (var beatmapInfo in copy.Beatmaps) + { + // Convert beatmap + var file = beatmapInfo.File; + + if (file == null) + continue; + + using var oldStream = new LineBufferedReader(ReadFile(file)); + var beatmapContent = new LegacyBeatmapDecoder().Decode(oldStream); + + foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints) + controlPoint.Time = Math.Floor(controlPoint.Time); + + foreach (var hitObject in beatmapContent.HitObjects) + { + hitObject.StartTime = Math.Floor(hitObject.StartTime); + + if (hitObject is IHasPath hasPath && BezierConverter.CountSegments(hasPath.Path.ControlPoints) > 1) + { + var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); + hasPath.Path.ControlPoints.Clear(); + hasPath.Path.ControlPoints.AddRange(newControlPoints); + } + } + + using var newStream = new MemoryStream(); + using var sw = new StreamWriter(newStream, Encoding.UTF8, 1024, true); + new LegacyBeatmapEncoder(beatmapContent, null).Encode(sw); + newStream.Seek(0, SeekOrigin.Begin); + + beatmapInfo.MD5Hash = newStream.ComputeMD5Hash(); + beatmapInfo.Hash = newStream.ComputeSHA2Hash(); + + AddFile(copy, newStream, file.Filename); + } + + return beatmapExporter.ExportAsync(new RealmLiveUnmanaged(copy)); + } + private void updateHashAndMarkDirty(BeatmapSetInfo setInfo) { setInfo.Hash = beatmapImporter.ComputeHash(setInfo); diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 59e413d935..923c498df8 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -90,6 +90,19 @@ namespace osu.Game.Beatmaps return ID == other.ID; } + public BeatmapSetInfo Clone() + { + var clone = (BeatmapSetInfo)this.Detach().MemberwiseClone(); + + for (int i = 0; i < clone.Beatmaps.Count; i++) + clone.Beatmaps[i] = clone.Beatmaps[i].Clone(); + + for (int i = 0; i < clone.Files.Count; i++) + clone.Files[i] = new RealmNamedFileUsage(clone.Files[i].File, clone.Files[i].Filename); + + return clone; + } + public override string ToString() => Metadata.GetDisplayString(); public bool Equals(IBeatmapSetInfo? other) => other is BeatmapSetInfo b && Equals(b); diff --git a/osu.Game/Database/ModelManager.cs b/osu.Game/Database/ModelManager.cs index 7d1dc5239a..8dafe0874c 100644 --- a/osu.Game/Database/ModelManager.cs +++ b/osu.Game/Database/ModelManager.cs @@ -33,6 +33,8 @@ namespace osu.Game.Database Realm = realm; } + public Stream ReadFile(RealmNamedFileUsage file) => realmFileStore.Storage.GetStream(file.File.GetStoragePath()); + public void DeleteFile(TModel item, RealmNamedFileUsage file) => performFileOperation(item, managed => DeleteFile(managed, managed.Files.First(f => f.Filename == file.Filename), managed.Realm)); diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 20258b9c35..f1f650cf66 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -39,6 +39,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ExportPackage => new TranslatableString(getKey(@"export_package"), @"Export package"); + /// + /// "Export legacy package" + /// + public static LocalisableString ExportLegacyPackage => new TranslatableString(getKey(@"export_legacy_package"), @"Export legacy package"); + /// /// "Create new difficulty" /// diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index ebee36a7db..0c878fa1fd 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -39,6 +39,13 @@ namespace osu.Game.Rulesets.Objects new[] { new Vector2d(1, 0), new Vector2d(1, 1.2447058f), new Vector2d(-0.8526471f, 2.118367f), new Vector2d(-2.6211002f, 7.854936e-06f), new Vector2d(-0.8526448f, -2.118357f), new Vector2d(1, -1.2447058f), new Vector2d(1, 0) }) }; + /// + /// Counts the number of segments in a slider path. + /// + /// The control points of the path. + /// The number of segments in a slider path. + public static int CountSegments(IList controlPoints) => controlPoints.Where((t, i) => t.Type != null && i < controlPoints.Count - 1).Count(); + /// /// Converts a slider path to bezier control point positions compatible with the legacy osu! client. /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b8fa7f6579..bc5df37570 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -966,6 +966,7 @@ namespace osu.Game.Screens.Edit { new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), new EditorMenuItem(EditorStrings.ExportPackage, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + new EditorMenuItem(EditorStrings.ExportLegacyPackage, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, new EditorMenuItemSpacer(), createDifficultyCreationMenu(), createDifficultySwitchMenu(), @@ -981,6 +982,12 @@ namespace osu.Game.Screens.Edit beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); } + private void exportLegacyBeatmap() + { + Save(); + beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo); + } + /// /// Beatmaps of the currently edited set, grouped by ruleset and ordered by difficulty. /// From 2db25722cbf29eb7d93e59df5d3e6b414e4d2dd4 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 11 Jul 2023 20:18:54 +0200 Subject: [PATCH 02/15] It works now --- osu.Game/Beatmaps/BeatmapManager.cs | 89 +++++++++++++++++++---------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 13 ----- osu.Game/Database/ModelManager.cs | 10 ++-- 3 files changed, 62 insertions(+), 50 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7d8c47ea5a..7c21b87a97 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -29,6 +29,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; using osu.Game.Utils; +using osuTK; namespace osu.Game.Beatmaps { @@ -406,51 +407,77 @@ namespace osu.Game.Beatmaps /// /// Creates a copy of the and converts all beatmaps to legacy format, then exports it as a legacy package. /// - /// + /// /// - public Task ExportLegacy(BeatmapSetInfo beatmap) + public Task ExportLegacy(BeatmapSetInfo beatmapSetInfo) { - var copy = beatmap.Clone(); // does the detach from realm + // Create a clone of the original beatmap set which we will convert to legacy format and then export + var clone = new BeatmapSetInfo(beatmapSetInfo.Beatmaps.Select(b => b.Clone())); + clone.Files.AddRange(beatmapSetInfo.Files.Select(f => new RealmNamedFileUsage(f.File, f.Filename))); - // convert all beatmaps to legacy format - foreach (var beatmapInfo in copy.Beatmaps) + // convert all beatmaps in the cloned beatmap set to legacy format + foreach (var beatmapInfo in clone.Beatmaps) { - // Convert beatmap - var file = beatmapInfo.File; + beatmapInfo.BeatmapSet = clone; + beatmapInfo.ID = Guid.NewGuid(); + var file = beatmapInfo.File; if (file == null) continue; - using var oldStream = new LineBufferedReader(ReadFile(file)); - var beatmapContent = new LegacyBeatmapDecoder().Decode(oldStream); - - foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints) - controlPoint.Time = Math.Floor(controlPoint.Time); - - foreach (var hitObject in beatmapContent.HitObjects) + var beatmapContent = new LegacyBeatmapDecoder().Decode(new LineBufferedReader(RealmFileStore.Storage.GetStream(file.File.GetStoragePath()))); + var beatmapSkin = new LegacySkin(new SkinInfo(), null!) { - hitObject.StartTime = Math.Floor(hitObject.StartTime); + Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(RealmFileStore.Storage.GetStream(file.File.GetStoragePath()))) + }; - if (hitObject is IHasPath hasPath && BezierConverter.CountSegments(hasPath.Path.ControlPoints) > 1) - { - var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); - hasPath.Path.ControlPoints.Clear(); - hasPath.Path.ControlPoints.AddRange(newControlPoints); - } - } + Realm.Realm.Write(realm => + { + using var stream = new MemoryStream(); + convertAndEncodeLegacyBeatmap(beatmapContent, beatmapSkin, stream); - using var newStream = new MemoryStream(); - using var sw = new StreamWriter(newStream, Encoding.UTF8, 1024, true); - new LegacyBeatmapEncoder(beatmapContent, null).Encode(sw); - newStream.Seek(0, SeekOrigin.Begin); + beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); + beatmapInfo.Hash = stream.ComputeSHA2Hash(); - beatmapInfo.MD5Hash = newStream.ComputeMD5Hash(); - beatmapInfo.Hash = newStream.ComputeSHA2Hash(); - - AddFile(copy, newStream, file.Filename); + file.File = RealmFileStore.Add(stream, realm).Detach(); + }); } - return beatmapExporter.ExportAsync(new RealmLiveUnmanaged(copy)); + return beatmapExporter.ExportAsync(new RealmLiveUnmanaged(clone)); + } + + private void convertAndEncodeLegacyBeatmap(IBeatmap beatmapContent, ISkin beatmapSkin, Stream stream) + { + // Convert beatmap elements to be compatible with legacy format + // So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves + foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints) + controlPoint.Time = Math.Floor(controlPoint.Time); + + foreach (var hitObject in beatmapContent.HitObjects) + { + hitObject.StartTime = Math.Floor(hitObject.StartTime); + + if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue; + + var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); + + // Truncate control points to integer positions + foreach (var pathControlPoint in newControlPoints) + { + pathControlPoint.Position = new Vector2( + (float)Math.Floor(pathControlPoint.Position.X), + (float)Math.Floor(pathControlPoint.Position.Y)); + } + + hasPath.Path.ControlPoints.Clear(); + hasPath.Path.ControlPoints.AddRange(newControlPoints); + } + + // Encode to legacy format + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); + + stream.Seek(0, SeekOrigin.Begin); } private void updateHashAndMarkDirty(BeatmapSetInfo setInfo) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 923c498df8..59e413d935 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -90,19 +90,6 @@ namespace osu.Game.Beatmaps return ID == other.ID; } - public BeatmapSetInfo Clone() - { - var clone = (BeatmapSetInfo)this.Detach().MemberwiseClone(); - - for (int i = 0; i < clone.Beatmaps.Count; i++) - clone.Beatmaps[i] = clone.Beatmaps[i].Clone(); - - for (int i = 0; i < clone.Files.Count; i++) - clone.Files[i] = new RealmNamedFileUsage(clone.Files[i].File, clone.Files[i].Filename); - - return clone; - } - public override string ToString() => Metadata.GetDisplayString(); public bool Equals(IBeatmapSetInfo? other) => other is BeatmapSetInfo b && Equals(b); diff --git a/osu.Game/Database/ModelManager.cs b/osu.Game/Database/ModelManager.cs index 8dafe0874c..172a2df5a3 100644 --- a/osu.Game/Database/ModelManager.cs +++ b/osu.Game/Database/ModelManager.cs @@ -25,16 +25,14 @@ namespace osu.Game.Database protected RealmAccess Realm { get; } - private readonly RealmFileStore realmFileStore; + protected readonly RealmFileStore RealmFileStore; public ModelManager(Storage storage, RealmAccess realm) { - realmFileStore = new RealmFileStore(realm, storage); + RealmFileStore = new RealmFileStore(realm, storage); Realm = realm; } - public Stream ReadFile(RealmNamedFileUsage file) => realmFileStore.Storage.GetStream(file.File.GetStoragePath()); - public void DeleteFile(TModel item, RealmNamedFileUsage file) => performFileOperation(item, managed => DeleteFile(managed, managed.Files.First(f => f.Filename == file.Filename), managed.Realm)); @@ -79,7 +77,7 @@ namespace osu.Game.Database /// public void ReplaceFile(RealmNamedFileUsage file, Stream contents, Realm realm) { - file.File = realmFileStore.Add(contents, realm); + file.File = RealmFileStore.Add(contents, realm); } /// @@ -95,7 +93,7 @@ namespace osu.Game.Database return; } - var file = realmFileStore.Add(contents, realm); + var file = RealmFileStore.Add(contents, realm); var namedUsage = new RealmNamedFileUsage(file, filename); item.Files.Add(namedUsage); From b577b6b6ae9ce8e4b047cfed8ba0820c04546db7 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 11 Jul 2023 21:04:09 +0200 Subject: [PATCH 03/15] Export legacy converted beatmaps as .osz and non-converted beatmaps as .osz2 --- osu.Game/Beatmaps/BeatmapImporter.cs | 4 ++-- osu.Game/Beatmaps/BeatmapManager.cs | 13 ++++++++++--- osu.Game/Database/BeatmapExporter.cs | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Database/BeatmapExporter.cs diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 7d367ef77d..a2d74d089f 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps /// public class BeatmapImporter : RealmArchiveModelImporter { - public override IEnumerable HandledExtensions => new[] { ".osz" }; + public override IEnumerable HandledExtensions => new[] { ".osz", ".osz2" }; protected override string[] HashableFileTypes => new[] { ".osu" }; @@ -145,7 +145,7 @@ namespace osu.Game.Beatmaps } } - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path).ToLowerInvariant() == ".osz"; + protected override bool ShouldDeleteArchive(string path) => HandledExtensions.Contains(Path.GetExtension(path).ToLowerInvariant()); protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default) { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7c21b87a97..ded26f79cd 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -44,7 +44,9 @@ namespace osu.Game.Beatmaps private readonly WorkingBeatmapCache workingBeatmapCache; - private readonly LegacyBeatmapExporter beatmapExporter; + private readonly BeatmapExporter beatmapExporter; + + private readonly LegacyBeatmapExporter legacyBeatmapExporter; public ProcessBeatmapDelegate? ProcessBeatmap { private get; set; } @@ -81,7 +83,12 @@ namespace osu.Game.Beatmaps workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host); - beatmapExporter = new LegacyBeatmapExporter(storage) + beatmapExporter = new BeatmapExporter(storage) + { + PostNotification = obj => PostNotification?.Invoke(obj) + }; + + legacyBeatmapExporter = new LegacyBeatmapExporter(storage) { PostNotification = obj => PostNotification?.Invoke(obj) }; @@ -443,7 +450,7 @@ namespace osu.Game.Beatmaps }); } - return beatmapExporter.ExportAsync(new RealmLiveUnmanaged(clone)); + return legacyBeatmapExporter.ExportAsync(new RealmLiveUnmanaged(clone)); } private void convertAndEncodeLegacyBeatmap(IBeatmap beatmapContent, ISkin beatmapSkin, Stream stream) diff --git a/osu.Game/Database/BeatmapExporter.cs b/osu.Game/Database/BeatmapExporter.cs new file mode 100644 index 0000000000..b3a85dd5d9 --- /dev/null +++ b/osu.Game/Database/BeatmapExporter.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Platform; +using osu.Game.Beatmaps; + +namespace osu.Game.Database +{ + public class BeatmapExporter : LegacyArchiveExporter + { + public BeatmapExporter(Storage storage) + : base(storage) + { + } + + protected override string FileExtension => @".osz2"; + } +} From 465cc759f02c84c6c3ae720d715623ac2b7596d6 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 12 Jul 2023 14:49:49 +0200 Subject: [PATCH 04/15] Add xmldoc to clarify the purpose of BeatmapExporter --- osu.Game/Database/BeatmapExporter.cs | 4 ++++ osu.Game/Database/LegacyBeatmapExporter.cs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/osu.Game/Database/BeatmapExporter.cs b/osu.Game/Database/BeatmapExporter.cs index b3a85dd5d9..9377065973 100644 --- a/osu.Game/Database/BeatmapExporter.cs +++ b/osu.Game/Database/BeatmapExporter.cs @@ -6,6 +6,10 @@ using osu.Game.Beatmaps; namespace osu.Game.Database { + /// + /// Exporter for beatmap archives. + /// This is not for legacy purposes and works for lazer only. + /// public class BeatmapExporter : LegacyArchiveExporter { public BeatmapExporter(Storage storage) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 4ee8c0636e..62096f862a 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -6,6 +6,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Database { + /// + /// Exporter for osu!stable legacy beatmap archives. + /// public class LegacyBeatmapExporter : LegacyArchiveExporter { public LegacyBeatmapExporter(Storage storage) From 3052c317e10c801e79a45542bcc247ee59379ce5 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 12 Jul 2023 15:04:06 +0200 Subject: [PATCH 05/15] change .osz2 to .olz (osu lazer zip) --- osu.Game/Beatmaps/BeatmapImporter.cs | 2 +- osu.Game/Database/BeatmapExporter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index a2d74d089f..e500f87984 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps /// public class BeatmapImporter : RealmArchiveModelImporter { - public override IEnumerable HandledExtensions => new[] { ".osz", ".osz2" }; + public override IEnumerable HandledExtensions => new[] { ".osz", ".olz" }; protected override string[] HashableFileTypes => new[] { ".osu" }; diff --git a/osu.Game/Database/BeatmapExporter.cs b/osu.Game/Database/BeatmapExporter.cs index 9377065973..f37c57dea5 100644 --- a/osu.Game/Database/BeatmapExporter.cs +++ b/osu.Game/Database/BeatmapExporter.cs @@ -17,6 +17,6 @@ namespace osu.Game.Database { } - protected override string FileExtension => @".osz2"; + protected override string FileExtension => @".olz"; } } From 8ca801a2240b6749c35dc98a33f73ba229d080fe Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 12 Jul 2023 15:18:16 +0200 Subject: [PATCH 06/15] dispose the streams --- osu.Game/Beatmaps/BeatmapManager.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index ded26f79cd..248e97d6ee 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -432,10 +432,15 @@ namespace osu.Game.Beatmaps if (file == null) continue; - var beatmapContent = new LegacyBeatmapDecoder().Decode(new LineBufferedReader(RealmFileStore.Storage.GetStream(file.File.GetStoragePath()))); + using var contentStream = RealmFileStore.Storage.GetStream(file.File.GetStoragePath()); + using var contentStreamReader = new LineBufferedReader(contentStream); + var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader); + + using var skinStream = RealmFileStore.Storage.GetStream(file.File.GetStoragePath()); + using var skinStreamReader = new LineBufferedReader(contentStream); var beatmapSkin = new LegacySkin(new SkinInfo(), null!) { - Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(RealmFileStore.Storage.GetStream(file.File.GetStoragePath()))) + Configuration = new LegacySkinDecoder().Decode(skinStreamReader) }; Realm.Realm.Write(realm => From 1d837a8725308a7f6e83ced3713a8796ad464930 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Thu, 13 Jul 2023 00:20:01 +0200 Subject: [PATCH 07/15] Move all conversion code to LegacyBeatmapExporter --- osu.Game/Beatmaps/BeatmapManager.cs | 85 +--------------------- osu.Game/Database/LegacyArchiveExporter.cs | 4 +- osu.Game/Database/LegacyBeatmapExporter.cs | 70 ++++++++++++++++++ osu.Game/Database/ModelManager.cs | 8 +- 4 files changed, 78 insertions(+), 89 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 248e97d6ee..ffdff13845 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -18,18 +18,14 @@ using osu.Framework.Platform; using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.Extensions; -using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Models; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; using osu.Game.Utils; -using osuTK; namespace osu.Game.Beatmaps { @@ -411,86 +407,7 @@ namespace osu.Game.Beatmaps public Task Export(BeatmapSetInfo beatmap) => beatmapExporter.ExportAsync(beatmap.ToLive(Realm)); - /// - /// Creates a copy of the and converts all beatmaps to legacy format, then exports it as a legacy package. - /// - /// - /// - public Task ExportLegacy(BeatmapSetInfo beatmapSetInfo) - { - // Create a clone of the original beatmap set which we will convert to legacy format and then export - var clone = new BeatmapSetInfo(beatmapSetInfo.Beatmaps.Select(b => b.Clone())); - clone.Files.AddRange(beatmapSetInfo.Files.Select(f => new RealmNamedFileUsage(f.File, f.Filename))); - - // convert all beatmaps in the cloned beatmap set to legacy format - foreach (var beatmapInfo in clone.Beatmaps) - { - beatmapInfo.BeatmapSet = clone; - beatmapInfo.ID = Guid.NewGuid(); - - var file = beatmapInfo.File; - if (file == null) - continue; - - using var contentStream = RealmFileStore.Storage.GetStream(file.File.GetStoragePath()); - using var contentStreamReader = new LineBufferedReader(contentStream); - var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader); - - using var skinStream = RealmFileStore.Storage.GetStream(file.File.GetStoragePath()); - using var skinStreamReader = new LineBufferedReader(contentStream); - var beatmapSkin = new LegacySkin(new SkinInfo(), null!) - { - Configuration = new LegacySkinDecoder().Decode(skinStreamReader) - }; - - Realm.Realm.Write(realm => - { - using var stream = new MemoryStream(); - convertAndEncodeLegacyBeatmap(beatmapContent, beatmapSkin, stream); - - beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); - beatmapInfo.Hash = stream.ComputeSHA2Hash(); - - file.File = RealmFileStore.Add(stream, realm).Detach(); - }); - } - - return legacyBeatmapExporter.ExportAsync(new RealmLiveUnmanaged(clone)); - } - - private void convertAndEncodeLegacyBeatmap(IBeatmap beatmapContent, ISkin beatmapSkin, Stream stream) - { - // Convert beatmap elements to be compatible with legacy format - // So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves - foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints) - controlPoint.Time = Math.Floor(controlPoint.Time); - - foreach (var hitObject in beatmapContent.HitObjects) - { - hitObject.StartTime = Math.Floor(hitObject.StartTime); - - if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue; - - var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); - - // Truncate control points to integer positions - foreach (var pathControlPoint in newControlPoints) - { - pathControlPoint.Position = new Vector2( - (float)Math.Floor(pathControlPoint.Position.X), - (float)Math.Floor(pathControlPoint.Position.Y)); - } - - hasPath.Path.ControlPoints.Clear(); - hasPath.Path.ControlPoints.AddRange(newControlPoints); - } - - // Encode to legacy format - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); - - stream.Seek(0, SeekOrigin.Begin); - } + public Task ExportLegacy(BeatmapSetInfo beatmap) => legacyBeatmapExporter.ExportAsync(beatmap.ToLive(Realm)); private void updateHashAndMarkDirty(BeatmapSetInfo setInfo) { diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs index 7689ffc13d..9805207591 100644 --- a/osu.Game/Database/LegacyArchiveExporter.cs +++ b/osu.Game/Database/LegacyArchiveExporter.cs @@ -39,7 +39,7 @@ namespace osu.Game.Database { cancellationToken.ThrowIfCancellationRequested(); - using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath())) + using (var stream = GetFileContents(model, file)) { if (stream == null) { @@ -65,5 +65,7 @@ namespace osu.Game.Database } } } + + protected virtual Stream? GetFileContents(TModel model, INamedFileUsage file) => UserFileStorage.GetStream(file.File.GetStoragePath()); } } diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 62096f862a..42d8a72073 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -1,13 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.IO; +using System.Linq; +using System.Text; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.Extensions; +using osu.Game.IO; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; +using osuTK; namespace osu.Game.Database { /// /// Exporter for osu!stable legacy beatmap archives. + /// Converts all beatmaps in the set to legacy format and exports it as a legacy package. /// public class LegacyBeatmapExporter : LegacyArchiveExporter { @@ -16,6 +28,64 @@ namespace osu.Game.Database { } + protected override Stream? GetFileContents(BeatmapSetInfo model, INamedFileUsage file) + { + bool isBeatmap = model.Beatmaps.Any(o => o.Hash == file.File.Hash); + + if (!isBeatmap) + return base.GetFileContents(model, file); + + // Read the beatmap contents and skin + using var contentStream = UserFileStorage.GetStream(file.File.GetStoragePath()); + + if (contentStream == null) + return null; + + using var contentStreamReader = new LineBufferedReader(contentStream); + var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader); + + using var skinStream = UserFileStorage.GetStream(file.File.GetStoragePath()); + using var skinStreamReader = new LineBufferedReader(contentStream); + var beatmapSkin = new LegacySkin(new SkinInfo(), null!) + { + Configuration = new LegacySkinDecoder().Decode(skinStreamReader) + }; + + // Convert beatmap elements to be compatible with legacy format + // So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves + foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints) + controlPoint.Time = Math.Floor(controlPoint.Time); + + foreach (var hitObject in beatmapContent.HitObjects) + { + hitObject.StartTime = Math.Floor(hitObject.StartTime); + + if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue; + + var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); + + // Truncate control points to integer positions + foreach (var pathControlPoint in newControlPoints) + { + pathControlPoint.Position = new Vector2( + (float)Math.Floor(pathControlPoint.Position.X), + (float)Math.Floor(pathControlPoint.Position.Y)); + } + + hasPath.Path.ControlPoints.Clear(); + hasPath.Path.ControlPoints.AddRange(newControlPoints); + } + + // Encode to legacy format + var stream = new MemoryStream(); + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); + + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } + protected override string FileExtension => @".osz"; } } diff --git a/osu.Game/Database/ModelManager.cs b/osu.Game/Database/ModelManager.cs index 172a2df5a3..7d1dc5239a 100644 --- a/osu.Game/Database/ModelManager.cs +++ b/osu.Game/Database/ModelManager.cs @@ -25,11 +25,11 @@ namespace osu.Game.Database protected RealmAccess Realm { get; } - protected readonly RealmFileStore RealmFileStore; + private readonly RealmFileStore realmFileStore; public ModelManager(Storage storage, RealmAccess realm) { - RealmFileStore = new RealmFileStore(realm, storage); + realmFileStore = new RealmFileStore(realm, storage); Realm = realm; } @@ -77,7 +77,7 @@ namespace osu.Game.Database /// public void ReplaceFile(RealmNamedFileUsage file, Stream contents, Realm realm) { - file.File = RealmFileStore.Add(contents, realm); + file.File = realmFileStore.Add(contents, realm); } /// @@ -93,7 +93,7 @@ namespace osu.Game.Database return; } - var file = RealmFileStore.Add(contents, realm); + var file = realmFileStore.Add(contents, realm); var namedUsage = new RealmNamedFileUsage(file, filename); item.Files.Add(namedUsage); From c02684d985e426b8dab1bf175204026cff2845e3 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 18 Jul 2023 12:18:43 +0200 Subject: [PATCH 08/15] truncate hit object end time --- osu.Game/Database/LegacyBeatmapExporter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 42d8a72073..3b2282c234 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -60,6 +60,9 @@ namespace osu.Game.Database { hitObject.StartTime = Math.Floor(hitObject.StartTime); + if (hitObject is IHasDuration hasDuration && hitObject is not IHasPath) + hasDuration.Duration = Math.Floor(hasDuration.Duration); + if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue; var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); From bcdf5310390021d2b5b4996c6d88293846a12004 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 18 Jul 2023 12:28:35 +0200 Subject: [PATCH 09/15] truncate end time before start time --- osu.Game/Database/LegacyBeatmapExporter.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 3b2282c234..983d25a25a 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -58,10 +58,11 @@ namespace osu.Game.Database foreach (var hitObject in beatmapContent.HitObjects) { - hitObject.StartTime = Math.Floor(hitObject.StartTime); - + // Truncate end time before truncating start time because end time is dependent on start time if (hitObject is IHasDuration hasDuration && hitObject is not IHasPath) - hasDuration.Duration = Math.Floor(hasDuration.Duration); + hasDuration.Duration = Math.Floor(hasDuration.EndTime) - Math.Floor(hitObject.StartTime); + + hitObject.StartTime = Math.Floor(hitObject.StartTime); if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue; From 395dd23966d0ae3fb15b13b8e0b8c6628e1a310b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 18 Jul 2023 12:37:37 +0200 Subject: [PATCH 10/15] Put 'Export package' and 'Export legacy package' in one nested menu --- osu.Game/Localisation/EditorStrings.cs | 9 +++++++-- osu.Game/Screens/Edit/Editor.cs | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 70392d5c83..07b4dddc05 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -40,9 +40,14 @@ namespace osu.Game.Localisation public static LocalisableString ExportPackage => new TranslatableString(getKey(@"export_package"), @"Export package"); /// - /// "Export legacy package" + /// "Legacy format (.osz)" /// - public static LocalisableString ExportLegacyPackage => new TranslatableString(getKey(@"export_legacy_package"), @"Export legacy package"); + public static LocalisableString ExportLegacyFormat => new TranslatableString(getKey(@"export_legacy_format"), @"Legacy format (.osz)"); + + /// + /// "New format (.olz)" + /// + public static LocalisableString ExportNewFormat => new TranslatableString(getKey(@"export_new_format"), @"New format (.olz)"); /// /// "Create new difficulty" diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a48c57f991..16cd7768fa 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -998,8 +998,7 @@ namespace osu.Game.Screens.Edit private List createFileMenuItems() => new List { new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), - new EditorMenuItem(EditorStrings.ExportPackage, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, - new EditorMenuItem(EditorStrings.ExportLegacyPackage, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + createExportPackageMenu(), new EditorMenuItemSpacer(), createDifficultyCreationMenu(), createDifficultySwitchMenu(), @@ -1009,6 +1008,17 @@ namespace osu.Game.Screens.Edit new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit) }; + private EditorMenuItem createExportPackageMenu() + { + var exportItems = new List + { + new EditorMenuItem(EditorStrings.ExportNewFormat, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + new EditorMenuItem(EditorStrings.ExportLegacyFormat, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + }; + + return new EditorMenuItem(EditorStrings.ExportPackage) { Items = exportItems }; + } + private void exportBeatmap() { Save(); From 63dd8bd991835344cbbf3e0c88b8f4b749ea1029 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 18 Jul 2023 12:40:48 +0200 Subject: [PATCH 11/15] use base.GetFileContents to get file stream --- osu.Game/Database/LegacyBeatmapExporter.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 983d25a25a..b90ea73244 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -8,7 +8,6 @@ using System.Text; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; -using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -36,7 +35,7 @@ namespace osu.Game.Database return base.GetFileContents(model, file); // Read the beatmap contents and skin - using var contentStream = UserFileStorage.GetStream(file.File.GetStoragePath()); + using var contentStream = base.GetFileContents(model, file); if (contentStream == null) return null; @@ -44,7 +43,7 @@ namespace osu.Game.Database using var contentStreamReader = new LineBufferedReader(contentStream); var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader); - using var skinStream = UserFileStorage.GetStream(file.File.GetStoragePath()); + using var skinStream = base.GetFileContents(model, file); using var skinStreamReader = new LineBufferedReader(contentStream); var beatmapSkin = new LegacySkin(new SkinInfo(), null!) { From e7a9175aeae253a515c8a8ab0d40985853de28ec Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 18 Jul 2023 13:08:05 +0200 Subject: [PATCH 12/15] fix skin using wrong stream --- osu.Game/Database/LegacyBeatmapExporter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index b90ea73244..e054652efa 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -44,7 +44,11 @@ namespace osu.Game.Database var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader); using var skinStream = base.GetFileContents(model, file); - using var skinStreamReader = new LineBufferedReader(contentStream); + + if (skinStream == null) + return null; + + using var skinStreamReader = new LineBufferedReader(skinStream); var beatmapSkin = new LegacySkin(new SkinInfo(), null!) { Configuration = new LegacySkinDecoder().Decode(skinStreamReader) From d11e56b8bb6ade5c4f6e47de5fa288a909f6bc66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 Jul 2023 14:03:35 +0900 Subject: [PATCH 13/15] Add common `Export` string and use in editor --- osu.Game/Localisation/CommonStrings.cs | 5 +++++ osu.Game/Localisation/EditorStrings.cs | 5 ----- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index c9223db246..480d68619d 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -39,6 +39,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Default => new TranslatableString(getKey(@"default"), @"Default"); + /// + /// "Export" + /// + public static LocalisableString Export => new TranslatableString(getKey(@"export"), @"Export"); + /// /// "Width" /// diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 07b4dddc05..7ff4d9a7ca 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -34,11 +34,6 @@ namespace osu.Game.Localisation /// public static LocalisableString SetPreviewPointToCurrent => new TranslatableString(getKey(@"set_preview_point_to_current"), @"Set preview point to current time"); - /// - /// "Export package" - /// - public static LocalisableString ExportPackage => new TranslatableString(getKey(@"export_package"), @"Export package"); - /// /// "Legacy format (.osz)" /// diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index e4ea277756..229cfd2aa6 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -425,7 +425,7 @@ namespace osu.Game.Online.Leaderboards if (Score.Files.Count > 0) { - items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(Score))); + items.Add(new OsuMenuItem(Localisation.CommonStrings.Export, MenuItemType.Standard, () => scoreManager.Export(Score))); items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 16cd7768fa..0ec6abbc63 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1016,7 +1016,7 @@ namespace osu.Game.Screens.Edit new EditorMenuItem(EditorStrings.ExportLegacyFormat, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, }; - return new EditorMenuItem(EditorStrings.ExportPackage) { Items = exportItems }; + return new EditorMenuItem(CommonStrings.Export) { Items = exportItems }; } private void exportBeatmap() From 20359c90bc589b149dc7f827c253b6e28539115a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 Jul 2023 14:05:44 +0900 Subject: [PATCH 14/15] Adjust localisations slightly --- osu.Game/Localisation/EditorStrings.cs | 8 ++++---- osu.Game/Screens/Edit/Editor.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 7ff4d9a7ca..93e52746c5 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -35,14 +35,14 @@ namespace osu.Game.Localisation public static LocalisableString SetPreviewPointToCurrent => new TranslatableString(getKey(@"set_preview_point_to_current"), @"Set preview point to current time"); /// - /// "Legacy format (.osz)" + /// "For editing (.olz)" /// - public static LocalisableString ExportLegacyFormat => new TranslatableString(getKey(@"export_legacy_format"), @"Legacy format (.osz)"); + public static LocalisableString ExportForEditing => new TranslatableString(getKey(@"export_for_editing"), @"For editing (.olz)"); /// - /// "New format (.olz)" + /// "For compatibility (.osz)" /// - public static LocalisableString ExportNewFormat => new TranslatableString(getKey(@"export_new_format"), @"New format (.olz)"); + public static LocalisableString ExportForCompatibility => new TranslatableString(getKey(@"export_for_compatibility"), @"For compatibility (.osz)"); /// /// "Create new difficulty" diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ec6abbc63..6558554b01 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -998,7 +998,7 @@ namespace osu.Game.Screens.Edit private List createFileMenuItems() => new List { new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), - createExportPackageMenu(), + createExportMenu(), new EditorMenuItemSpacer(), createDifficultyCreationMenu(), createDifficultySwitchMenu(), @@ -1008,12 +1008,12 @@ namespace osu.Game.Screens.Edit new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit) }; - private EditorMenuItem createExportPackageMenu() + private EditorMenuItem createExportMenu() { var exportItems = new List { - new EditorMenuItem(EditorStrings.ExportNewFormat, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, - new EditorMenuItem(EditorStrings.ExportLegacyFormat, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, }; return new EditorMenuItem(CommonStrings.Export) { Items = exportItems }; From fb34ee94c2f451c4fa5ffe4749f970da75d4154a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 Jul 2023 14:07:31 +0900 Subject: [PATCH 15/15] Move export/save options to bottom of menu --- osu.Game/Screens/Edit/Editor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 6558554b01..672551e088 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -997,14 +997,14 @@ namespace osu.Game.Screens.Edit private List createFileMenuItems() => new List { - new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), - createExportMenu(), - new EditorMenuItemSpacer(), createDifficultyCreationMenu(), createDifficultySwitchMenu(), new EditorMenuItemSpacer(), new EditorMenuItem(EditorStrings.DeleteDifficulty, MenuItemType.Standard, deleteDifficulty) { Action = { Disabled = Beatmap.Value.BeatmapSetInfo.Beatmaps.Count < 2 } }, new EditorMenuItemSpacer(), + new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), + createExportMenu(), + new EditorMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit) };