diff --git a/osu-framework b/osu-framework index 41e2a0a430..cc39713fbf 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 41e2a0a4304544fb67779c21cad1435c105982d5 +Subproject commit cc39713fbf9427aa53df91e27ecd09d15661089f diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 1bb67f9e75..74b7d0272e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Formats private void handleGeneral(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) @@ -155,7 +155,7 @@ namespace osu.Game.Beatmaps.Formats private void handleEditor(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); switch (pair.Key) { @@ -179,7 +179,7 @@ namespace osu.Game.Beatmaps.Formats private void handleMetadata(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) @@ -220,7 +220,7 @@ namespace osu.Game.Beatmaps.Formats private void handleDifficulty(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index e4aa9f5091..131c010c5c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using osu.Framework.Logging; using OpenTK.Graphics; namespace osu.Game.Beatmaps.Formats @@ -31,7 +32,11 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith(@"[") && line.EndsWith(@"]")) { if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); + { + Logger.Log($"Unknown section \"{line}\" in {beatmap}"); + section = Section.None; + } + continue; } @@ -55,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats private void handleColours(T output, string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); bool isCombo = pair.Key.StartsWith(@"Combo"); @@ -89,7 +94,7 @@ namespace osu.Game.Beatmaps.Formats } } - protected KeyValuePair SplitKeyVal(string line, char separator) + protected KeyValuePair SplitKeyVal(string line, char separator = ':') { var split = line.Trim().Split(new[] { separator }, 2); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a65593ff82..f0e67a7185 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -13,6 +13,7 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.IPC; using osu.Game.Overlays.Notifications; +using SharpCompress.Common; using FileInfo = osu.Game.IO.FileInfo; namespace osu.Game.Database @@ -79,7 +80,6 @@ namespace osu.Game.Database var notification = new ProgressNotification { Text = "Import is initialising...", - CompletionText = "Import successful!", Progress = 0, State = ProgressNotificationState.Active, }; @@ -88,7 +88,8 @@ namespace osu.Game.Database List imported = new List(); - int i = 0; + int current = 0; + int errors = 0; foreach (string path in paths) { if (notification.State == ProgressNotificationState.Cancelled) @@ -97,11 +98,11 @@ namespace osu.Game.Database try { - notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}"; + notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; using (ArchiveReader reader = getReaderFrom(path)) imported.Add(Import(reader)); - notification.Progress = (float)++i / paths.Length; + notification.Progress = (float)current / paths.Length; // We may or may not want to delete the file depending on where it is stored. // e.g. reconstructing/repairing database with items from default storage. @@ -121,9 +122,11 @@ namespace osu.Game.Database { e = e.InnerException ?? e; Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + errors++; } } + notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; notification.State = ProgressNotificationState.Completed; } @@ -218,9 +221,11 @@ namespace osu.Game.Database // user requested abort return; - notification.Text = $"Deleting ({i} of {items.Count})"; - notification.Progress = (float)++i / items.Count; + notification.Text = $"Deleting ({++i} of {items.Count})"; + Delete(b); + + notification.Progress = (float)i / items.Count; } } @@ -254,9 +259,11 @@ namespace osu.Game.Database // user requested abort return; - notification.Text = $"Restoring ({i} of {items.Count})"; - notification.Progress = (float)++i / items.Count; + notification.Text = $"Restoring ({++i} of {items.Count})"; + Undelete(item); + + notification.Progress = (float)i / items.Count; } } @@ -331,7 +338,9 @@ namespace osu.Game.Database { if (ZipFile.IsZipFile(path)) return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); - return new LegacyFilesystemReader(path); + if (Directory.Exists(path)) + return new LegacyFilesystemReader(path); + throw new InvalidFormatException($"{path} is not a valid archive"); } } } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 6f9d83473f..89ed8044e6 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -242,7 +242,7 @@ namespace osu.Game.Graphics.Backgrounds triangle, colourInfo, null, - Shared.VertexBatch.Add, + Shared.VertexBatch.AddAction, Vector2.Divide(localInflationAmount, size)); } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index bc0b8b4aaa..5df5304751 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.Name, s.ID)); + void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); skins.ItemAdded += _ => reloadSkins(); skins.ItemRemoved += _ => reloadSkins(); diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 3a3f3d4650..fc747acbb4 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Menu rectangle, colourInfo, null, - Shared.VertexBatch.Add, + Shared.VertexBatch.AddAction, //barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. Vector2.Divide(inflation, barSize.Yx)); } diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 3fcb885655..b7d2ed2e1f 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -337,12 +337,10 @@ namespace osu.Game.Screens.Menu } } - private bool interactive => Action != null && Alpha > 0.2f; + public override bool HandleMouseInput => base.HandleMouseInput && Action != null && Alpha > 0.2f; protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - if (!interactive) return false; - logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out); return true; } @@ -355,8 +353,6 @@ namespace osu.Game.Screens.Menu protected override bool OnClick(InputState state) { - if (!interactive) return false; - if (Action?.Invoke() ?? true) sampleClick.Play(); @@ -368,8 +364,6 @@ namespace osu.Game.Screens.Menu protected override bool OnHover(InputState state) { - if (!interactive) return false; - logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic); return true; } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index e40a43d400..c469e91250 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -11,6 +11,7 @@ namespace osu.Game.Skinning public DefaultSkin() : base(SkinInfo.Default) { + Configuration = new SkinConfiguration(); } public override Drawable GetDrawableComponent(string componentName) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5525cc483e..b531d791b0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -25,6 +25,14 @@ namespace osu.Game.Skinning storage = new LegacySkinResourceStore(skin, storage); samples = audioManager.GetSampleManager(storage); textures = new TextureStore(new RawTextureLoaderStore(storage)); + + Stream stream = storage.GetStream("skin.ini"); + + if (stream != null) + using (StreamReader reader = new StreamReader(stream)) + Configuration = new LegacySkinDecoder().Decode(reader); + else + Configuration = new SkinConfiguration(); } public override Drawable GetDrawableComponent(string componentName) @@ -60,10 +68,12 @@ namespace osu.Game.Skinning private string getPathForFile(string filename) { + bool hasExtension = filename.Contains('.'); + string lastPiece = filename.Split('/').Last(); var file = skin.Files.FirstOrDefault(f => - string.Equals(Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); + string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } @@ -73,9 +83,17 @@ namespace osu.Game.Skinning this.underlyingStore = underlyingStore; } - public Stream GetStream(string name) => underlyingStore.GetStream(getPathForFile(name)); + public Stream GetStream(string name) + { + string path = getPathForFile(name); + return path == null ? null : underlyingStore.GetStream(path); + } - byte[] IResourceStore.Get(string name) => underlyingStore.Get(getPathForFile(name)); + byte[] IResourceStore.Get(string name) + { + string path = getPathForFile(name); + return path == null ? null : underlyingStore.Get(path); + } } } } diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs new file mode 100644 index 0000000000..853abceddf --- /dev/null +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.Formats; + +namespace osu.Game.Skinning +{ + public class LegacySkinDecoder : LegacyDecoder + { + public LegacySkinDecoder() + : base(1) + { + } + + protected override void ParseLine(SkinConfiguration output, Section section, string line) + { + switch (section) + { + case Section.General: + var pair = SplitKeyVal(line); + + switch (pair.Key) + { + case @"Name": + output.SkinInfo.Name = pair.Value; + break; + case @"Author": + output.SkinInfo.Creator = pair.Value; + break; + } + + break; + } + + base.ParseLine(output, section, line); + } + } +} diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index fafbdec8f0..7b4e894dfd 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -10,6 +10,8 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo; + public virtual SkinConfiguration Configuration { get; protected set; } + public abstract Drawable GetDrawableComponent(string componentName); public abstract SampleChannel GetSample(string sampleName); diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs new file mode 100644 index 0000000000..eac77ae753 --- /dev/null +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps.Formats; +using OpenTK.Graphics; + +namespace osu.Game.Skinning +{ + public class SkinConfiguration : IHasComboColours, IHasCustomColours + { + public readonly SkinInfo SkinInfo = new SkinInfo(); + + public List ComboColours { get; set; } = new List(); + + public Dictionary CustomColours { get; set; } = new Dictionary(); + } +} diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 45c8b97f63..5080b65a37 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -24,5 +24,7 @@ namespace osu.Game.Skinning public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; + + public override string ToString() => $"\"{Name}\" by {Creator}"; } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 88d51eca10..fa65b923fb 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -39,6 +39,31 @@ namespace osu.Game.Skinning Name = archive.Name }; + protected override void Populate(SkinInfo model, ArchiveReader archive) + { + base.Populate(model, archive); + populate(model); + } + + /// + /// Populate a from its (if possible). + /// + /// + private void populate(SkinInfo model) + { + Skin reference = GetSkin(model); + if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) + { + model.Name = reference.Configuration.SkinInfo.Name; + model.Creator = reference.Configuration.SkinInfo.Creator; + } + else + { + model.Name = model.Name.Replace(".osk", ""); + model.Creator = "Unknown"; + } + } + /// /// Retrieve a instance for the provided /// @@ -65,6 +90,16 @@ namespace osu.Game.Skinning if (skin.SkinInfo != CurrentSkinInfo.Value) throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead."); }; + + // migrate older imports which didn't have access to skin.ini + using (ContextFactory.GetForWrite()) + { + foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk"))) + { + populate(skinInfo); + Update(skinInfo); + } + } } /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 091ec3f7ac..b325e52ed1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -873,7 +873,9 @@ + +