From 53082a3b503090d255e6b7dc6fee95f111f3ae01 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Fri, 4 Oct 2024 20:47:24 +1000 Subject: [PATCH 01/49] Add v2 and v4 Info classes for vanilla properties --- Assets/Plugins/SimpleJSONExtension.cs | 14 + Assets/TestsEditMode/InfoTest.cs | 414 ++++++++++++++++++ Assets/TestsEditMode/InfoTest.cs.meta | 3 + Assets/__Scripts/Beatmap/Info/BaseInfo.cs | 114 +++++ .../__Scripts/Beatmap/Info/BaseInfo.cs.meta | 3 + Assets/__Scripts/Beatmap/Info/V2Info.cs | 171 ++++++++ Assets/__Scripts/Beatmap/Info/V2Info.cs.meta | 3 + Assets/__Scripts/Beatmap/Info/V4Info.cs | 184 ++++++++ Assets/__Scripts/Beatmap/Info/V4Info.cs.meta | 3 + 9 files changed, 909 insertions(+) create mode 100644 Assets/TestsEditMode/InfoTest.cs create mode 100644 Assets/TestsEditMode/InfoTest.cs.meta create mode 100644 Assets/__Scripts/Beatmap/Info/BaseInfo.cs create mode 100644 Assets/__Scripts/Beatmap/Info/BaseInfo.cs.meta create mode 100644 Assets/__Scripts/Beatmap/Info/V2Info.cs create mode 100644 Assets/__Scripts/Beatmap/Info/V2Info.cs.meta create mode 100644 Assets/__Scripts/Beatmap/Info/V4Info.cs create mode 100644 Assets/__Scripts/Beatmap/Info/V4Info.cs.meta diff --git a/Assets/Plugins/SimpleJSONExtension.cs b/Assets/Plugins/SimpleJSONExtension.cs index bfc4f93a6..47fe6d501 100644 --- a/Assets/Plugins/SimpleJSONExtension.cs +++ b/Assets/Plugins/SimpleJSONExtension.cs @@ -1,9 +1,23 @@ using System; using System.Globalization; using System.Text; +using UnityEngine; namespace SimpleJSON { + public partial class JSONNode + { + public Color ReadHtmlStringColor() + { + if (IsString && ColorUtility.TryParseHtmlString(Value, out var color)) + { + return color; + } + + return Color.white; + } + } + public class JSONNumberWithOverridenRounding : JSONNumber { private int precision; diff --git a/Assets/TestsEditMode/InfoTest.cs b/Assets/TestsEditMode/InfoTest.cs new file mode 100644 index 000000000..3459637c0 --- /dev/null +++ b/Assets/TestsEditMode/InfoTest.cs @@ -0,0 +1,414 @@ +using System.Linq; +using Beatmap.Info; +using NUnit.Framework; +using SimpleJSON; +using UnityEngine; + +namespace TestsEditMode +{ + public class InfoTest + { + private const string v2FileInfo = @" +{ + ""_version"": ""2.1.0"", + ""_songName"": ""Magic"", + ""_songSubName"": ""ft. Meredith Bull"", + ""_songAuthorName"": ""Jaroslav Beck"", + ""_levelAuthorName"": ""Freeek"", + ""_beatsPerMinute"": 208, + ""_songTimeOffset"": 0, + ""_shuffle"": 0, + ""_shufflePeriod"": 0, + ""_previewStartTime"": 0, + ""_previewDuration"": 0, + ""_songFilename"": ""Magic.wav"", + ""_coverImageFilename"": ""cover.png"", + ""_environmentName"": ""WeaveEnvironment"", + ""_allDirectionsEnvironmentName"": ""GlassDesertEnvironment"", + ""_environmentNames"": [""WeaveEnvironment"", ""GlassDesertEnvironment""], + ""_colorSchemes"": [ + { + ""useOverride"": true, + ""colorScheme"": { + ""colorSchemeId"": ""Weave"", + ""saberAColor"": { + ""r"": 0.78431370, + ""g"": 0.07843138, + ""b"": 0.07843138, + ""a"": 1.00000000 + }, + ""saberBColor"": { + ""r"": 0.1568627, + ""g"": 0.5568627, + ""b"": 0.8235294, + ""a"": 1.0000000 + }, + ""environmentColor0"": { + ""r"": 0.85000000, + ""g"": 0.08499997, + ""b"": 0.08499997, + ""a"": 1.00000000 + }, + ""environmentColor1"": { + ""r"": 0.1882353, + ""g"": 0.6752940, + ""b"": 1.0000000, + ""a"": 1.0000000 + }, + ""obstaclesColor"": { + ""r"": 1.0000000, + ""g"": 0.1882353, + ""b"": 0.1882353, + ""a"": 1.0000000 + }, + ""environmentColor0Boost"": { + ""r"": 0.82184090, + ""g"": 0.08627451, + ""b"": 0.85098040, + ""a"": 1.00000000 + }, + ""environmentColor1Boost"": { + ""r"": 0.5320754, + ""g"": 0.5320754, + ""b"": 0.5320754, + ""a"": 1.0000000 + } + } + } + ], + ""_difficultyBeatmapSets"": [ + { + ""_beatmapCharacteristicName"": ""Standard"", + ""_difficultyBeatmaps"": [ + { + ""_difficulty"": ""Easy"", + ""_difficultyRank"": 1, + ""_beatmapFilename"": ""Easy.dat"", + ""_noteJumpMovementSpeed"": 10, + ""_noteJumpStartBeatOffset"": 0, + ""_beatmapColorSchemeIdx"": 0, + ""_environmentNameIdx"": 0 + }, + { + ""_difficulty"": ""Normal"", + ""_difficultyRank"": 3, + ""_beatmapFilename"": ""Normal.dat"", + ""_noteJumpMovementSpeed"": 10, + ""_noteJumpStartBeatOffset"": 0, + ""_beatmapColorSchemeIdx"": 0, + ""_environmentNameIdx"": 0 + }, + { + ""_difficulty"": ""Hard"", + ""_difficultyRank"": 5, + ""_beatmapFilename"": ""Hard.dat"", + ""_noteJumpMovementSpeed"": 10, + ""_noteJumpStartBeatOffset"": 0, + ""_beatmapColorSchemeIdx"": 0, + ""_environmentNameIdx"": 0 + }, + { + ""_difficulty"": ""Expert"", + ""_difficultyRank"": 7, + ""_beatmapFilename"": ""Expert.dat"", + ""_noteJumpMovementSpeed"": 16, + ""_noteJumpStartBeatOffset"": 1, + ""_beatmapColorSchemeIdx"": 0, + ""_environmentNameIdx"": 0 + }, + { + ""_difficulty"": ""ExpertPlus"", + ""_difficultyRank"": 9, + ""_beatmapFilename"": ""ExpertPlus.dat"", + ""_noteJumpMovementSpeed"": 18, + ""_noteJumpStartBeatOffset"": 0.5, + ""_beatmapColorSchemeIdx"": 0, + ""_environmentNameIdx"": 0 + } + ] + } + ] +} +"; + + private const string v4FileInfo = @" +{ + ""version"": ""4.0.0"", + ""song"": { + ""title"" : ""Magic"" , + ""subTitle"": ""ft. Meredith Bull"", + ""author"" : ""Jaroslav Beck"" + }, + ""audio"": { + ""songFilename"": ""song.ogg"", + ""songDuration"": 202, + ""audioDataFilename"": ""BPMInfo.dat"", + ""bpm"": 208, + ""lufs"": 0, + ""previewStartTime"": 0, + ""previewDuration"": 0 + }, + ""songPreviewFilename"": ""song.ogg"", + ""coverImageFilename"": ""cover.png"", + ""environmentNames"": [""WeaveEnvironment"", ""GlassDesertEnvironment""], + ""colorSchemes"": [ + { + ""useOverride"": true, + ""colorSchemeName"": ""Weave"", + ""saberAColor"": ""#C81414FF"", + ""saberBColor"": ""#288ED2FF"", + ""obstaclesColor"": ""#FF3030FF"", + ""environmentColor0"": ""#D91616FF"", + ""environmentColor1"": ""#30ACFFFF"", + ""environmentColor0Boost"": ""#D216D9FF"", + ""environmentColor1Boost"": ""#888888FF"" + // ^ This is modified from the wiki as this was different from v2 example + } + ], + ""difficultyBeatmaps"": [ + { + ""characteristic"": ""Standard"", + ""difficulty"": ""Easy"", + ""beatmapAuthors"": { + ""mappers"" : [""Freeek""], + ""lighters"": [""Freeek""] + }, + ""environmentNameIdx"": 0, + ""beatmapColorSchemeIdx"": 0, + ""noteJumpMovementSpeed"": 10, + ""noteJumpStartBeatOffset"": 0, + ""beatmapDataFilename"": ""Easy.dat"", + ""lightshowDataFilename"": ""Lightshow.dat"" + }, + { + ""characteristic"": ""Standard"", + ""difficulty"": ""Normal"", + ""beatmapAuthors"": { + ""mappers"" : [""Freeek""], + ""lighters"": [""Freeek""] + }, + ""environmentNameIdx"": 0, + ""beatmapColorSchemeIdx"": 0, + ""noteJumpMovementSpeed"": 10, + ""noteJumpStartBeatOffset"": 0, + ""beatmapDataFilename"": ""Normal.dat"", + ""lightshowDataFilename"": ""Lightshow.dat"" + }, + { + ""characteristic"": ""Standard"", + ""difficulty"": ""Hard"", + ""beatmapAuthors"": { + ""mappers"" : [""Freeek""], + ""lighters"": [""Freeek""] + }, + ""environmentNameIdx"": 0, + ""beatmapColorSchemeIdx"": 0, + ""noteJumpMovementSpeed"": 10, + ""noteJumpStartBeatOffset"": 0, + ""beatmapDataFilename"": ""Hard.dat"", + ""lightshowDataFilename"": ""Lightshow.dat"" + }, + { + ""characteristic"": ""Standard"", + ""difficulty"": ""Expert"", + ""beatmapAuthors"": { + ""mappers"" : [""Freeek""], + ""lighters"": [""Freeek""] + }, + ""environmentNameIdx"": 0, + ""beatmapColorSchemeIdx"": 0, + ""noteJumpMovementSpeed"": 16, + ""noteJumpStartBeatOffset"": 1, + ""beatmapDataFilename"": ""Expert.dat"", + ""lightshowDataFilename"": ""Lightshow.dat"" + }, + { + ""characteristic"": ""Standard"", + ""difficulty"": ""ExpertPlus"", + ""beatmapAuthors"": { + ""mappers"" : [""Freeek""], + ""lighters"": [""Freeek""] + }, + ""environmentNameIdx"": 0, + ""beatmapColorSchemeIdx"": 0, + ""noteJumpMovementSpeed"": 18, + ""noteJumpStartBeatOffset"": 0.5, + ""beatmapDataFilename"": ""ExpertPlus.dat"", + ""lightshowDataFilename"": ""LightshowPlus.dat"" + } + ] +} +"; + + [Test] + public void V2_GetFromJson_VanillaProperties() + { + var info = V2Info.GetFromJson(JSONNode.Parse(v2FileInfo)); + AssertV2Info(info); + } + + [Test] + public void V2_GetOutputJson_VanillaProperties() + { + var info = V2Info.GetFromJson(JSONNode.Parse(v2FileInfo)); + var output = V2Info.GetOutputJson(info); + var reparsed = V2Info.GetFromJson(output); + + AssertV2Info(reparsed); // This should have the same stuff + } + + [Test] + public void V4_GetFromJson_VanillaProperties() + { + var info = V4Info.GetFromJson(JSONNode.Parse(v4FileInfo)); + AssertV4Info(info); + } + + [Test] + public void V4_GetOutputJson_VanillaProperties() + { + var info = V4Info.GetFromJson(JSONNode.Parse(v4FileInfo)); + var output = V4Info.GetOutputJson(info); + var reparsed = V4Info.GetFromJson(output); + + AssertV4Info(reparsed); // This should have the same stuff + } + + private void AssertV2Info(BaseInfo info) + { + Assert.AreEqual("2.1.0", info.Version); + + AssertCommonInfo(info); + + Assert.AreEqual("Freeek", info.LevelAuthorName); + + Assert.AreEqual("Magic.wav", info.SongFilename); + + Assert.AreEqual("WeaveEnvironment", info.EnvironmentName); + Assert.AreEqual("GlassDesertEnvironment", info.AllDirectionsEnvironmentName); + } + + private void AssertV4Info(BaseInfo info) + { + Assert.AreEqual("4.0.0", info.Version); + + AssertCommonInfo(info); + + Assert.AreEqual("BPMInfo.dat", info.AudioDataFilename); + Assert.AreEqual(0, info.Lufs); + + Assert.AreEqual("song.ogg", info.SongFilename); + Assert.AreEqual("song.ogg", info.SongPreviewFilename); + + foreach (var difficulty in info.DifficultySets.SelectMany(x => x.Difficulties)) + { + Assert.AreEqual(1, difficulty.Mappers.Count); + Assert.AreEqual("Freeek", difficulty.Mappers[0]); + + Assert.AreEqual(1, difficulty.Lighters.Count); + Assert.AreEqual("Freeek", difficulty.Lighters[0]); + + var lightshowFileName = difficulty.Difficulty == "ExpertPlus" ? "LightshowPlus.dat" : "Lightshow.dat"; + Assert.AreEqual(lightshowFileName, difficulty.LightshowFileName); + } + } + + private void AssertCommonInfo(BaseInfo info) + { + Assert.AreEqual("Magic", info.SongName); + Assert.AreEqual("ft. Meredith Bull", info.SongSubName); + Assert.AreEqual("Jaroslav Beck", info.SongAuthorName); + + Assert.AreEqual(208f, info.BeatsPerMinute); + + Assert.AreEqual(0f, info.SongTimeOffset); + Assert.AreEqual(0f, info.Shuffle); + Assert.AreEqual(0f, info.ShufflePeriod); + + Assert.AreEqual(0f, info.PreviewStartTime); + Assert.AreEqual(0f, info.PreviewDuration); + + Assert.AreEqual("cover.png", info.CoverImageFilename); + + Assert.AreEqual(2, info.EnvironmentNames.Count); + Assert.AreEqual("WeaveEnvironment", info.EnvironmentNames[0]); + Assert.AreEqual("GlassDesertEnvironment", info.EnvironmentNames[1]); + + Assert.AreEqual(1, info.ColorSchemes.Count); + + var colorScheme = info.ColorSchemes[0]; + Assert.AreEqual(true, colorScheme.UseOverride); + Assert.AreEqual("Weave", colorScheme.ColorSchemeName); + AssertColorsAreEqual(new Color(0.7843137f, 0.07843138f, 0.07843138f), colorScheme.SaberAColor); + AssertColorsAreEqual(new Color(0.1568627f, 0.55686270f, 0.82352940f), colorScheme.SaberBColor); + AssertColorsAreEqual(new Color(0.8500000f, 0.08499997f, 0.08499997f), colorScheme.EnvironmentColor0); + AssertColorsAreEqual(new Color(0.1882353f, 0.67529400f, 1.00000000f), colorScheme.EnvironmentColor1); + AssertColorsAreEqual(new Color(1.0000000f, 0.18823530f, 0.18823530f), colorScheme.ObstaclesColor); + AssertColorsAreEqual(new Color(0.8218409f, 0.08627451f, 0.85098040f), colorScheme.EnvironmentColor0Boost); + AssertColorsAreEqual(new Color(0.5320754f, 0.53207540f, 0.53207540f), colorScheme.EnvironmentColor1Boost); + + Assert.AreEqual(1, info.DifficultySets.Count); + + var difficultySet = info.DifficultySets[0]; + Assert.AreEqual("Standard", difficultySet.Characteristic); + Assert.AreEqual(5, difficultySet.Difficulties.Count); + + var easyDifficulty = difficultySet.Difficulties[0]; + Assert.AreEqual("Easy", easyDifficulty.Difficulty); + Assert.AreEqual(1, easyDifficulty.DifficultyRank); + Assert.AreEqual("Easy.dat", easyDifficulty.BeatmapFileName); + Assert.AreEqual(10, easyDifficulty.NoteJumpSpeed); + Assert.AreEqual(0, easyDifficulty.NoteStartBeatOffset); + Assert.AreEqual(0, easyDifficulty.ColorSchemeIndex); + Assert.AreEqual(0, easyDifficulty.EnvironmentNameIndex); + + var normalDifficulty = difficultySet.Difficulties[1]; + Assert.AreEqual("Normal", normalDifficulty.Difficulty); + Assert.AreEqual(3, normalDifficulty.DifficultyRank); + Assert.AreEqual("Normal.dat", normalDifficulty.BeatmapFileName); + Assert.AreEqual(10, normalDifficulty.NoteJumpSpeed); + Assert.AreEqual(0, normalDifficulty.NoteStartBeatOffset); + Assert.AreEqual(0, normalDifficulty.ColorSchemeIndex); + Assert.AreEqual(0, normalDifficulty.EnvironmentNameIndex); + + var hardDifficulty = difficultySet.Difficulties[2]; + Assert.AreEqual("Hard", hardDifficulty.Difficulty); + Assert.AreEqual(5, hardDifficulty.DifficultyRank); + Assert.AreEqual("Hard.dat", hardDifficulty.BeatmapFileName); + Assert.AreEqual(10, hardDifficulty.NoteJumpSpeed); + Assert.AreEqual(0, hardDifficulty.NoteStartBeatOffset); + Assert.AreEqual(0, hardDifficulty.ColorSchemeIndex); + Assert.AreEqual(0, hardDifficulty.EnvironmentNameIndex); + + var expertDifficulty = difficultySet.Difficulties[3]; + Assert.AreEqual("Expert", expertDifficulty.Difficulty); + Assert.AreEqual(7, expertDifficulty.DifficultyRank); + Assert.AreEqual("Expert.dat", expertDifficulty.BeatmapFileName); + Assert.AreEqual(16, expertDifficulty.NoteJumpSpeed); + Assert.AreEqual(1, expertDifficulty.NoteStartBeatOffset); + Assert.AreEqual(0, expertDifficulty.ColorSchemeIndex); + Assert.AreEqual(0, expertDifficulty.EnvironmentNameIndex); + + var expertPlusDifficulty = difficultySet.Difficulties[4]; + Assert.AreEqual("ExpertPlus", expertPlusDifficulty.Difficulty); + Assert.AreEqual(9, expertPlusDifficulty.DifficultyRank); + Assert.AreEqual("ExpertPlus.dat", expertPlusDifficulty.BeatmapFileName); + Assert.AreEqual(18, expertPlusDifficulty.NoteJumpSpeed); + Assert.AreEqual(0.5f, expertPlusDifficulty.NoteStartBeatOffset); + Assert.AreEqual(0, expertPlusDifficulty.ColorSchemeIndex); + Assert.AreEqual(0, expertPlusDifficulty.EnvironmentNameIndex); + } + + private void AssertColorsAreEqual(Color expected, Color actual) + { + // Because v4 saves color schemes as a Html string, we can expect an error of ±0.5 / 255; + var delta = 0.5f / 255; + + Assert.AreEqual(expected.r, actual.r, delta, "red component"); + Assert.AreEqual(expected.g, actual.g, delta, "green component"); + Assert.AreEqual(expected.b, actual.b, delta, "blue component"); + Assert.AreEqual(expected.a, actual.a, delta, "alpha component"); + } + + } +} \ No newline at end of file diff --git a/Assets/TestsEditMode/InfoTest.cs.meta b/Assets/TestsEditMode/InfoTest.cs.meta new file mode 100644 index 000000000..1eb5251af --- /dev/null +++ b/Assets/TestsEditMode/InfoTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae89345ab3024ad4be99717ae0458542 +timeCreated: 1728032161 \ No newline at end of file diff --git a/Assets/__Scripts/Beatmap/Info/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/BaseInfo.cs new file mode 100644 index 000000000..b076493ae --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/BaseInfo.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using Beatmap.Base.Customs; +using SimpleJSON; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Beatmap.Info +{ + // TODO: Parse CustomData for supported properties *and* preserve unknown fields + public class BaseInfo + { + // Editor Properties + public string Directory { get; set; } + public bool IsFavourite; + + // Vanilla Properties + public string Version { get; set; } = "4.0.0"; + + public string SongName { get; set; } = "New Song"; + public string SongSubName { get; set; } = ""; + public string SongAuthorName { get; set; } = ""; + + public string LevelAuthorName { get; set; } = ""; + + public float SongTimeOffset { get; set; } + public float Shuffle { get; set; } + public float ShufflePeriod { get; set; } + + public float BeatsPerMinute { get; set; } = 100; + public float PreviewStartTime { get; set; } = 12; + public float PreviewDuration { get; set; } = 10; + public string SongFilename { get; set; } = + "song.ogg"; // .egg file extension is a problem solely beat saver deals with, work with .ogg for the mapper + public string SongPreviewFilename { get; set; } = "song.ogg"; // Default same as SongFilename + public string AudioDataFilename { get; set; } = "AudioData.dat"; + public float SongDurationMetadata { get; set; } + public float Lufs { get; set; } // Some normalisation thing + + public string CoverImageFilename { get; set; } = "cover.png"; + + // TODO: Could probably convert this to EnvironmentNames.FirstOrDefault() ?? "DefaultEnvironment"; + public string EnvironmentName { get; set; } = "DefaultEnvironment"; + + // TODO: This might be better as just a constant. No other all directions environment exists in vanilla. + public string AllDirectionsEnvironmentName { get; set; } = "GlassDesertEnvironment"; + + public List EnvironmentNames { get; set; } = new(); + + public List ColorSchemes { get; set; } = new(); + + public List DifficultySets { get; set; } = new(); + + + // CustomData Properties + public JSONNode CustomData = new JSONObject(); + } + + public class InfoDifficultySet + { + public string Characteristic { get; set; } + public List Difficulties { get; set; } = new(); + + public JSONObject CustomData { get; set; } = new(); // Pretty much just for v2 SongCore parsing + } + + public class InfoDifficulty + { + public InfoDifficulty(InfoDifficultySet parentSet) => ParentSet = parentSet; + public InfoDifficultySet ParentSet { get; set; } + public string Characteristic => ParentSet.Characteristic; + + public string BeatmapFileName { get; set; } + public string LightshowFileName { get; set; } + public string Difficulty { get; set; } + + public int DifficultyRank => Difficulty switch + { + "ExpertPlus" => 9, + "Expert+" => 9, // Yes this is valid + "Expert" => 7, + "Hard" => 5, + "Normal" => 3, + "Easy" => 1, + _ => -1 + }; + + public int EnvironmentNameIndex { get; set; } + public int ColorSchemeIndex { get; set; } + + public float NoteJumpSpeed { get; set; } + public float NoteStartBeatOffset { get; set; } + + + public List Mappers { get; set; } + public List Lighters { get; set; } + + + // CustomData Properties + public JSONObject CustomData { get; set; } = new(); + } + + public class InfoColorScheme + { + public bool UseOverride { get; set; } + public string ColorSchemeName { get; set; } + public Color SaberAColor { get; set; } + public Color SaberBColor { get; set; } + public Color ObstaclesColor { get; set; } + public Color EnvironmentColor0 { get; set; } + public Color EnvironmentColor1 { get; set; } + public Color EnvironmentColor0Boost { get; set; } + public Color EnvironmentColor1Boost { get; set; } + } +} diff --git a/Assets/__Scripts/Beatmap/Info/BaseInfo.cs.meta b/Assets/__Scripts/Beatmap/Info/BaseInfo.cs.meta new file mode 100644 index 000000000..03e0b29af --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/BaseInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 43ea7d8d5bd44126a428c96d541a0656 +timeCreated: 1727702122 \ No newline at end of file diff --git a/Assets/__Scripts/Beatmap/Info/V2Info.cs b/Assets/__Scripts/Beatmap/Info/V2Info.cs new file mode 100644 index 000000000..a3d0064ff --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V2Info.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using System.Linq; +using SimpleJSON; + +namespace Beatmap.Info +{ + public static class V2Info + { + public static BaseInfo GetFromJson(JSONNode node) + { + var info = new BaseInfo(); + + info.Version = node["_version"].Value; + info.SongName = node["_songName"].Value; + info.SongSubName = node["_songSubName"].Value; + info.SongAuthorName = node["_songAuthorName"].Value; + + info.LevelAuthorName = node["_levelAuthorName"].Value; + + info.BeatsPerMinute = node["_beatsPerMinute"].AsFloat; + + // Deprecated fields + info.SongTimeOffset = node["_songTimeOffset"].AsFloat; + info.Shuffle = node["_shuffle"].AsFloat; + info.ShufflePeriod = node["_shufflePeriod"].AsFloat; + + info.PreviewStartTime = node["_previewStartTime"].AsFloat; + info.PreviewDuration = node["_previewDuration"].AsFloat; + + info.SongFilename = node["_songFilename"].Value; + info.CoverImageFilename = node["_coverImageFilename"].Value; + + info.EnvironmentName = node["_environmentName"].Value; + info.EnvironmentNames = node["_environmentNames"].AsArray.Children.Select(x => x.Value).ToList(); + info.AllDirectionsEnvironmentName = node["_allDirectionsEnvironmentName"].Value; + + var colorSchemes = new List(); + var colorSchemeNodes = node["_colorSchemes"].AsArray.Children.Select(x => x.AsObject); + foreach (var colorSchemeNode in colorSchemeNodes) + { + var colorScheme = new InfoColorScheme(); + colorScheme.UseOverride = colorSchemeNode["useOverride"].AsBool; + colorScheme.ColorSchemeName = colorSchemeNode["colorScheme"]["colorSchemeId"]; + colorScheme.SaberAColor = colorSchemeNode["colorScheme"]["saberAColor"].ReadColor(); + colorScheme.SaberBColor = colorSchemeNode["colorScheme"]["saberBColor"].ReadColor(); + colorScheme.ObstaclesColor = colorSchemeNode["colorScheme"]["obstaclesColor"].ReadColor(); + colorScheme.EnvironmentColor0 = colorSchemeNode["colorScheme"]["environmentColor0"].ReadColor(); + colorScheme.EnvironmentColor1 = colorSchemeNode["colorScheme"]["environmentColor1"].ReadColor(); + colorScheme.EnvironmentColor0Boost = colorSchemeNode["colorScheme"]["environmentColor0Boost"].ReadColor(); + colorScheme.EnvironmentColor1Boost = colorSchemeNode["colorScheme"]["environmentColor1Boost"].ReadColor(); + colorSchemes.Add(colorScheme); + } + info.ColorSchemes = colorSchemes; + + var beatmapSets = new List(); + var beatmapSetsNode = node["_difficultyBeatmapSets"].AsArray; + foreach (var beatmapSetNode in beatmapSetsNode.Children) + { + var beatmapSet = new InfoDifficultySet(); + beatmapSet.Characteristic = beatmapSetNode["_beatmapCharacteristicName"].Value; + + var difficulties = new List(); + + var beatmapsNode = beatmapSetNode["_difficultyBeatmaps"].AsArray; + foreach (var beatmapNode in beatmapsNode.Children) + { + var infoDifficulty = new InfoDifficulty(beatmapSet); + infoDifficulty.BeatmapFileName = beatmapNode["_beatmapFilename"].Value; + infoDifficulty.Difficulty = beatmapNode["_difficulty"].Value; + infoDifficulty.EnvironmentNameIndex = beatmapNode["_environmentNameIdx"].AsInt; + infoDifficulty.ColorSchemeIndex = beatmapNode["_beatmapColorSchemeIdx"].AsInt; + infoDifficulty.NoteJumpSpeed = beatmapNode["_noteJumpMovementSpeed"].AsFloat; + infoDifficulty.NoteStartBeatOffset = beatmapNode["_noteJumpStartBeatOffset"].AsFloat; + + infoDifficulty.CustomData = beatmapNode["_customData"].AsObject; + + difficulties.Add(infoDifficulty); + } + + beatmapSet.Difficulties = difficulties; + beatmapSet.CustomData = beatmapSetNode["_customData"].AsObject; + + beatmapSets.Add(beatmapSet); + } + info.DifficultySets = beatmapSets; + + info.CustomData = node["_customData"]; + + return info; + } + + public static JSONNode GetOutputJson(BaseInfo info) + { + var json = new JSONObject(); + + json["_version"] = "2.1.0"; + + json["_songName"] = info.SongName; + json["_songSubName"] = info.SongSubName; + json["_songSubName"] = info.SongSubName; + json["_songAuthorName"] = info.SongAuthorName; + json["_levelAuthorName"] = info.LevelAuthorName; + json["_beatsPerMinute"] = info.BeatsPerMinute; + json["_songTimeOffset"] = info.SongTimeOffset; + json["_shuffle"] = info.Shuffle; + json["_shufflePeriod"] = info.ShufflePeriod; + json["_previewStartTime"] = info.PreviewStartTime; + json["_previewDuration"] = info.PreviewDuration; + json["_songFilename"] = info.SongFilename; + json["_coverImageFilename"] = info.CoverImageFilename; + json["_environmentName"] = info.EnvironmentName; + json["_allDirectionsEnvironmentName"] = info.AllDirectionsEnvironmentName; + + var environmentNames = new JSONArray(); + foreach (var environmentName in info.EnvironmentNames) + { + environmentNames.Add(environmentName); + } + json["_environmentNames"] = environmentNames; + + var colorSchemes = new JSONArray(); + foreach (var colorScheme in info.ColorSchemes) + { + var node = new JSONObject(); + node["useOverride"] = colorScheme.UseOverride; + node["colorScheme"]["colorSchemeId"] = colorScheme.ColorSchemeName; + node["colorScheme"]["saberAColor"] = colorScheme.SaberAColor; + node["colorScheme"]["saberBColor"] = colorScheme.SaberBColor; + node["colorScheme"]["obstaclesColor"] = colorScheme.ObstaclesColor; + node["colorScheme"]["environmentColor0"] = colorScheme.EnvironmentColor0; + node["colorScheme"]["environmentColor1"] = colorScheme.EnvironmentColor1; + node["colorScheme"]["environmentColor0Boost"] = colorScheme.EnvironmentColor0Boost; + node["colorScheme"]["environmentColor1Boost"] = colorScheme.EnvironmentColor1Boost; + + colorSchemes.Add(node); + } + json["_colorSchemes"] = colorSchemes; + + var beatmapSetArray = new JSONArray(); + foreach (var beatmapSet in info.DifficultySets) + { + var setNode = new JSONObject { ["_beatmapCharacteristicName"] = beatmapSet.Characteristic }; + var difficultyBeatmapsArray = new JSONArray(); + + foreach (var difficulty in beatmapSet.Difficulties) + { + var node = new JSONObject(); + node["_difficulty"] = difficulty.Difficulty; + node["_difficultyRank"] = difficulty.DifficultyRank; + node["_beatmapFilename"] = difficulty.BeatmapFileName; + node["_noteJumpMovementSpeed"] = difficulty.NoteJumpSpeed; + node["_noteJumpStartBeatOffset"] = difficulty.NoteStartBeatOffset; + node["_beatmapColorSchemeIdx"] = difficulty.ColorSchemeIndex; + node["_environmentNameIdx"] = difficulty.EnvironmentNameIndex; + node["_customData"] = difficulty.CustomData; + difficultyBeatmapsArray.Add(node); + } + + setNode["_difficultyBeatmaps"] = difficultyBeatmapsArray; + setNode["_customData"] = beatmapSet.CustomData; + beatmapSetArray.Add(setNode); + } + + json["_difficultyBeatmapSets"] = beatmapSetArray; + + json["_customData"] = info.CustomData; + + return json; + } + } +} diff --git a/Assets/__Scripts/Beatmap/Info/V2Info.cs.meta b/Assets/__Scripts/Beatmap/Info/V2Info.cs.meta new file mode 100644 index 000000000..2a9c61ce5 --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V2Info.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 10cfdaa88660411a914995f22247a227 +timeCreated: 1727702138 \ No newline at end of file diff --git a/Assets/__Scripts/Beatmap/Info/V4Info.cs b/Assets/__Scripts/Beatmap/Info/V4Info.cs new file mode 100644 index 000000000..5ae44eb1d --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V4Info.cs @@ -0,0 +1,184 @@ +using System.Collections.Generic; +using System.Linq; +using SimpleJSON; +using UnityEngine; + +namespace Beatmap.Info +{ + public static class V4Info + { + public static BaseInfo GetFromJson(JSONNode node) + { + var info = new BaseInfo(); + + info.Version = node["version"].Value; + + var songNode = node["song"].AsObject; + info.SongName = songNode["title"].Value; + info.SongSubName = songNode["subTitle"].Value; + info.SongAuthorName = songNode["author"].Value; + + var audioNode = node["audio"].AsObject; + info.SongFilename = audioNode["songFilename"].Value; + info.SongDurationMetadata = audioNode["songDuration"].AsFloat; + info.AudioDataFilename = audioNode["audioDataFilename"].Value; + info.BeatsPerMinute = audioNode["bpm"].AsFloat; + info.PreviewStartTime = audioNode["previewStartTime"].AsFloat; + info.PreviewDuration = audioNode["previewDuration"].AsFloat; + info.Lufs = audioNode["lufs"].AsFloat; + + info.SongPreviewFilename = node["songPreviewFilename"].Value; + info.CoverImageFilename = node["coverImageFilename"].Value; + + info.EnvironmentNames = node["environmentNames"].AsArray.Children.Select(x => x.Value).ToList(); + info.EnvironmentName = info.EnvironmentNames.First(); + info.AllDirectionsEnvironmentName = node["allDirectionsEnvironmentName"].Value; + + var colorSchemes = new List(); + var colorSchemeNodes = node["colorSchemes"].AsArray.Children.Select(x => x.AsObject); + foreach (var colorSchemeNode in colorSchemeNodes) + { + var colorScheme = new InfoColorScheme(); + colorScheme.UseOverride = colorSchemeNode["useOverride"].AsBool; + colorScheme.ColorSchemeName = colorSchemeNode["colorSchemeName"]; + colorScheme.SaberAColor = colorSchemeNode["saberAColor"].ReadHtmlStringColor(); + colorScheme.SaberBColor = colorSchemeNode["saberBColor"].ReadHtmlStringColor(); + colorScheme.ObstaclesColor = colorSchemeNode["obstaclesColor"].ReadHtmlStringColor(); + colorScheme.EnvironmentColor0 = colorSchemeNode["environmentColor0"].ReadHtmlStringColor(); + colorScheme.EnvironmentColor1 = colorSchemeNode["environmentColor1"].ReadHtmlStringColor(); + colorScheme.EnvironmentColor0Boost = colorSchemeNode["environmentColor0Boost"].ReadHtmlStringColor(); + colorScheme.EnvironmentColor1Boost = colorSchemeNode["environmentColor1Boost"].ReadHtmlStringColor(); + colorSchemes.Add(colorScheme); + } + info.ColorSchemes = colorSchemes; + + var beatmapsNode = node["difficultyBeatmaps"].AsArray; + var difficultySets = new List(); + foreach (var beatmapCharacteristicSet in beatmapsNode.Children.GroupBy(x => x["characteristic"].Value)) + { + var beatmapSet = new InfoDifficultySet { Characteristic = beatmapCharacteristicSet.Key }; + var infoDifficultys = new List(); + + foreach (var beatmap in beatmapCharacteristicSet) + { + var infoDifficulty = new InfoDifficulty(beatmapSet); + infoDifficulty.Difficulty = beatmap["difficulty"].Value; + infoDifficulty.EnvironmentNameIndex = beatmap["environmentNameIdx"].AsInt; + infoDifficulty.ColorSchemeIndex = beatmap["beatmapColorSchemeIdx"].AsInt; + infoDifficulty.NoteJumpSpeed = beatmap["noteJumpMovementSpeed"].AsFloat; + infoDifficulty.NoteStartBeatOffset = beatmap["noteJumpStartBeatOffset"].AsFloat; + + infoDifficulty.BeatmapFileName = beatmap["beatmapDataFilename"].Value; + infoDifficulty.LightshowFileName = beatmap["lightshowDataFilename"].Value; + + var authorsNode = beatmap["beatmapAuthors"].AsObject; + infoDifficulty.Mappers = authorsNode["mappers"].AsArray.Children.Select(x => x.Value).ToList(); + infoDifficulty.Lighters = authorsNode["lighters"].AsArray.Children.Select(x => x.Value).ToList(); + + infoDifficultys.Add(infoDifficulty); + } + + beatmapSet.Difficulties = infoDifficultys; + difficultySets.Add(beatmapSet); + } + + info.DifficultySets = difficultySets; + + info.CustomData = node["customData"]; + + return info; + } + + public static JSONNode GetOutputJson(BaseInfo info) + { + var json = new JSONObject(); + + json["version"] = "4.0.0"; + + var songNode = new JSONObject(); + songNode["title"] = info.SongName; + songNode["subTitle"] = info.SongSubName; + songNode["author"] = info.SongAuthorName; + json["song"] = songNode; + + var audioNode = new JSONObject(); + audioNode["songFilename"] = info.SongFilename; + audioNode["songDuration"] = info.SongDurationMetadata; // Probably insert loaded audio duration here + audioNode["audioDataFilename"] = info.AudioDataFilename; + audioNode["bpm"] = info.BeatsPerMinute; + audioNode["lufs"] = info.Lufs; + audioNode["previewStartTime"] = info.PreviewStartTime; + audioNode["previewDuration"] = info.PreviewDuration; + json["audio"] = audioNode; + + json["songPreviewFilename"] = info.SongPreviewFilename; // Why isn't this part of the audio node??? + json["coverImageFilename"] = info.CoverImageFilename; + + var environmentNames = new JSONArray(); + foreach (var environmentName in info.EnvironmentNames) + { + environmentNames.Add(environmentName); + } + json["environmentNames"] = environmentNames; + + var colorSchemes = new JSONArray(); + foreach (var colorScheme in info.ColorSchemes) + { + var node = new JSONObject(); + + node["useOverride"] = colorScheme.UseOverride; + node["colorSchemeName"] = colorScheme.ColorSchemeName; + node["saberAColor"] = ToHashPrefixedHtmlStringRGBA(colorScheme.SaberAColor); + node["saberBColor"] = ToHashPrefixedHtmlStringRGBA(colorScheme.SaberBColor); + node["obstaclesColor"] = ToHashPrefixedHtmlStringRGBA(colorScheme.ObstaclesColor); + node["environmentColor0"] = ToHashPrefixedHtmlStringRGBA(colorScheme.EnvironmentColor0); + node["environmentColor1"] = ToHashPrefixedHtmlStringRGBA(colorScheme.EnvironmentColor1); + node["environmentColor0Boost"] = ToHashPrefixedHtmlStringRGBA(colorScheme.EnvironmentColor0Boost); + node["environmentColor1Boost"] = ToHashPrefixedHtmlStringRGBA(colorScheme.EnvironmentColor1Boost); + + colorSchemes.Add(node); + } + json["colorSchemes"] = colorSchemes; + + var difficultyBeatmaps = new JSONArray(); + foreach (var difficulty in info.DifficultySets.SelectMany(x => x.Difficulties)) + { + var node = new JSONObject(); + + node["characteristic"] = difficulty.Characteristic; + node["difficulty"] = difficulty.Difficulty; + + var authorsNode = new JSONObject(); + + var mappers = new JSONArray(); + foreach (var mapper in difficulty.Mappers) mappers.Add(mapper); + authorsNode["mappers"] = mappers; + + var lighters = new JSONArray(); + foreach (var lighter in difficulty.Lighters) lighters.Add(lighter); + authorsNode["lighters"] = lighters; + + node["beatmapAuthors"] = authorsNode; + + node["environmentNameIdx"] = difficulty.EnvironmentNameIndex; + node["beatmapColorSchemeIdx"] = difficulty.ColorSchemeIndex; + node["noteJumpMovementSpeed"] = difficulty.NoteJumpSpeed; + node["noteJumpStartBeatOffset"] = difficulty.NoteStartBeatOffset; + node["beatmapDataFilename"] = difficulty.BeatmapFileName; + node["lightshowDataFilename"] = difficulty.LightshowFileName; + + node["customData"] = difficulty.CustomData; + + difficultyBeatmaps.Add(node); + } + + json["difficultyBeatmaps"] = difficultyBeatmaps; + + json["customData"] = info.CustomData; + + return json; + } + + private static string ToHashPrefixedHtmlStringRGBA(Color color) => $"#{ColorUtility.ToHtmlStringRGBA(color)}"; + } +} diff --git a/Assets/__Scripts/Beatmap/Info/V4Info.cs.meta b/Assets/__Scripts/Beatmap/Info/V4Info.cs.meta new file mode 100644 index 000000000..e23c74a99 --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V4Info.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dcd789ddc10a43808c8332ce5146d433 +timeCreated: 1727702143 \ No newline at end of file From 295c2349be2eacb69db079b745cb866bce34d775 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:08:40 +1000 Subject: [PATCH 02/49] Move Info classes into separate folder --- Assets/__Scripts/Beatmap/Info/Base.meta | 3 +++ Assets/__Scripts/Beatmap/Info/{ => Base}/BaseBpmInfo.cs | 0 Assets/__Scripts/Beatmap/Info/{ => Base}/BaseBpmInfo.cs.meta | 0 Assets/__Scripts/Beatmap/Info/{ => Base}/BaseInfo.cs | 0 Assets/__Scripts/Beatmap/Info/{ => Base}/BaseInfo.cs.meta | 0 Assets/__Scripts/Beatmap/Info/V2.meta | 3 +++ Assets/__Scripts/Beatmap/Info/{ => V2}/V2BpmInfo.cs | 0 Assets/__Scripts/Beatmap/Info/{ => V2}/V2BpmInfo.cs.meta | 0 Assets/__Scripts/Beatmap/Info/{ => V2}/V2Info.cs | 0 Assets/__Scripts/Beatmap/Info/{ => V2}/V2Info.cs.meta | 0 Assets/__Scripts/Beatmap/Info/V4.meta | 3 +++ Assets/__Scripts/Beatmap/Info/{ => V4}/V4AudioData.cs | 0 Assets/__Scripts/Beatmap/Info/{ => V4}/V4AudioData.cs.meta | 0 Assets/__Scripts/Beatmap/Info/{ => V4}/V4Info.cs | 0 Assets/__Scripts/Beatmap/Info/{ => V4}/V4Info.cs.meta | 0 15 files changed, 9 insertions(+) create mode 100644 Assets/__Scripts/Beatmap/Info/Base.meta rename Assets/__Scripts/Beatmap/Info/{ => Base}/BaseBpmInfo.cs (100%) rename Assets/__Scripts/Beatmap/Info/{ => Base}/BaseBpmInfo.cs.meta (100%) rename Assets/__Scripts/Beatmap/Info/{ => Base}/BaseInfo.cs (100%) rename Assets/__Scripts/Beatmap/Info/{ => Base}/BaseInfo.cs.meta (100%) create mode 100644 Assets/__Scripts/Beatmap/Info/V2.meta rename Assets/__Scripts/Beatmap/Info/{ => V2}/V2BpmInfo.cs (100%) rename Assets/__Scripts/Beatmap/Info/{ => V2}/V2BpmInfo.cs.meta (100%) rename Assets/__Scripts/Beatmap/Info/{ => V2}/V2Info.cs (100%) rename Assets/__Scripts/Beatmap/Info/{ => V2}/V2Info.cs.meta (100%) create mode 100644 Assets/__Scripts/Beatmap/Info/V4.meta rename Assets/__Scripts/Beatmap/Info/{ => V4}/V4AudioData.cs (100%) rename Assets/__Scripts/Beatmap/Info/{ => V4}/V4AudioData.cs.meta (100%) rename Assets/__Scripts/Beatmap/Info/{ => V4}/V4Info.cs (100%) rename Assets/__Scripts/Beatmap/Info/{ => V4}/V4Info.cs.meta (100%) diff --git a/Assets/__Scripts/Beatmap/Info/Base.meta b/Assets/__Scripts/Beatmap/Info/Base.meta new file mode 100644 index 000000000..35e7c66f4 --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/Base.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e24548c555d40d296f49d24c75dfa0b +timeCreated: 1728043626 \ No newline at end of file diff --git a/Assets/__Scripts/Beatmap/Info/BaseBpmInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Info/BaseBpmInfo.cs rename to Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs diff --git a/Assets/__Scripts/Beatmap/Info/BaseBpmInfo.cs.meta b/Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Info/BaseBpmInfo.cs.meta rename to Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs.meta diff --git a/Assets/__Scripts/Beatmap/Info/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Info/BaseInfo.cs rename to Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs diff --git a/Assets/__Scripts/Beatmap/Info/BaseInfo.cs.meta b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Info/BaseInfo.cs.meta rename to Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs.meta diff --git a/Assets/__Scripts/Beatmap/Info/V2.meta b/Assets/__Scripts/Beatmap/Info/V2.meta new file mode 100644 index 000000000..b90e15ea5 --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V2.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5d1bee42fabf40c0ad933dcd4c220050 +timeCreated: 1728043632 \ No newline at end of file diff --git a/Assets/__Scripts/Beatmap/Info/V2BpmInfo.cs b/Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V2BpmInfo.cs rename to Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs diff --git a/Assets/__Scripts/Beatmap/Info/V2BpmInfo.cs.meta b/Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V2BpmInfo.cs.meta rename to Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs.meta diff --git a/Assets/__Scripts/Beatmap/Info/V2Info.cs b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V2Info.cs rename to Assets/__Scripts/Beatmap/Info/V2/V2Info.cs diff --git a/Assets/__Scripts/Beatmap/Info/V2Info.cs.meta b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V2Info.cs.meta rename to Assets/__Scripts/Beatmap/Info/V2/V2Info.cs.meta diff --git a/Assets/__Scripts/Beatmap/Info/V4.meta b/Assets/__Scripts/Beatmap/Info/V4.meta new file mode 100644 index 000000000..d0463bc2c --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V4.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e4e45662388f496e95e40968659e9661 +timeCreated: 1728043636 \ No newline at end of file diff --git a/Assets/__Scripts/Beatmap/Info/V4AudioData.cs b/Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V4AudioData.cs rename to Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs diff --git a/Assets/__Scripts/Beatmap/Info/V4AudioData.cs.meta b/Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V4AudioData.cs.meta rename to Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs.meta diff --git a/Assets/__Scripts/Beatmap/Info/V4Info.cs b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V4Info.cs rename to Assets/__Scripts/Beatmap/Info/V4/V4Info.cs diff --git a/Assets/__Scripts/Beatmap/Info/V4Info.cs.meta b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Info/V4Info.cs.meta rename to Assets/__Scripts/Beatmap/Info/V4/V4Info.cs.meta From 07309f0891e62747e95ee8eeaf7cf1bda149f4e7 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:23:24 +1000 Subject: [PATCH 03/49] Move BaseContributor from Beatmap Base to Info Base --- .../Beatmap/{Base/Customs => Info/Base}/BaseContributor.cs | 0 .../Beatmap/{Base/Customs => Info/Base}/BaseContributor.cs.meta | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Assets/__Scripts/Beatmap/{Base/Customs => Info/Base}/BaseContributor.cs (100%) rename Assets/__Scripts/Beatmap/{Base/Customs => Info/Base}/BaseContributor.cs.meta (100%) diff --git a/Assets/__Scripts/Beatmap/Base/Customs/BaseContributor.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs similarity index 100% rename from Assets/__Scripts/Beatmap/Base/Customs/BaseContributor.cs rename to Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs diff --git a/Assets/__Scripts/Beatmap/Base/Customs/BaseContributor.cs.meta b/Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs.meta similarity index 100% rename from Assets/__Scripts/Beatmap/Base/Customs/BaseContributor.cs.meta rename to Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs.meta From 7b2b1560b158227a3cb59310402ac4d46b1f595c Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:41:15 +1000 Subject: [PATCH 04/49] Implement V2Contributor --- Assets/__Scripts/BeatSaberSong.cs | 4 +-- .../Beatmap/Info/Base/BaseContributor.cs | 14 ++++++++-- .../__Scripts/Beatmap/Info/InfoContributor.cs | 28 ------------------- .../Beatmap/Info/V2/V2Contributor.cs | 26 +++++++++++++++++ .../V2Contributor.cs.meta} | 2 +- .../UI/Contributors/ContributorListItem.cs | 2 +- .../UI/Contributors/ContributorsController.cs | 2 +- 7 files changed, 43 insertions(+), 35 deletions(-) delete mode 100644 Assets/__Scripts/Beatmap/Info/InfoContributor.cs create mode 100644 Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs rename Assets/__Scripts/Beatmap/Info/{InfoContributor.cs.meta => V2/V2Contributor.cs.meta} (83%) diff --git a/Assets/__Scripts/BeatSaberSong.cs b/Assets/__Scripts/BeatSaberSong.cs index a8ef1cda9..16b287310 100644 --- a/Assets/__Scripts/BeatSaberSong.cs +++ b/Assets/__Scripts/BeatSaberSong.cs @@ -166,7 +166,7 @@ public void SaveSong() if (Contributors.Any()) { var contributorArray = new JSONArray(); - Contributors.ForEach(x => contributorArray.Add(x.ToJson())); + Contributors.ForEach(x => contributorArray.Add(V2Contributor.ToJson(x))); if (Contributors.Any()) Json["_customData"]["_contributors"] = contributorArray; } @@ -410,7 +410,7 @@ public static BeatSaberSong GetSongFromFolder(string directory) if (node.HasKey("_contributors")) { foreach (JSONNode contributor in song.CustomData["_contributors"]) - song.Contributors.Add(new InfoContributor(contributor)); + song.Contributors.Add(V2Contributor.GetFromJson(contributor)); } song.Editors = new EditorsObject(node["_editors"]); diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs index c8b0d7aee..a4453ae3c 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseContributor.cs @@ -1,9 +1,19 @@ -namespace Beatmap.Base.Customs +namespace Beatmap.Info { - public abstract class BaseContributor : BaseItem + public class BaseContributor { public string LocalImageLocation { get; set; } public string Name { get; set; } public string Role { get; set; } + + + public BaseContributor() { } + + public BaseContributor(string localImageLocation, string name, string role) + { + LocalImageLocation = localImageLocation; + Name = name; + Role = role; + } } } diff --git a/Assets/__Scripts/Beatmap/Info/InfoContributor.cs b/Assets/__Scripts/Beatmap/Info/InfoContributor.cs deleted file mode 100644 index 4b31f4b17..000000000 --- a/Assets/__Scripts/Beatmap/Info/InfoContributor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Beatmap.Base; -using Beatmap.Base.Customs; -using SimpleJSON; - -namespace Beatmap.Info -{ - public class InfoContributor : BaseContributor - { - public InfoContributor(JSONNode node) - { - Name = node["_name"]?.Value; - Role = node["_role"]?.Value; - LocalImageLocation = node["_iconPath"]?.Value; - } - - public InfoContributor(string name, string role, string iconPath) - { - Name = name; - Role = role; - LocalImageLocation = iconPath; - } - - public override JSONNode ToJson() => - new JSONObject { ["_name"] = Name, ["_role"] = Role, ["_iconPath"] = LocalImageLocation }; - - public override BaseItem Clone() => new InfoContributor(Name, Role, LocalImageLocation); - } -} diff --git a/Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs b/Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs new file mode 100644 index 000000000..ace9a3c78 --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs @@ -0,0 +1,26 @@ +using SimpleJSON; + +namespace Beatmap.Info +{ + public static class V2Contributor + { + public static BaseContributor GetFromJson(JSONNode node) + { + var contributor = new BaseContributor(); + + contributor.Name = node["_name"]?.Value; + contributor.Role = node["_role"]?.Value; + contributor.LocalImageLocation = node["_iconPath"]?.Value; + + return contributor; + } + + public static JSONObject ToJson(BaseContributor contributor) => + new JSONObject + { + ["_name"] = contributor.Name, + ["_role"] = contributor.Role, + ["_iconPath"] = contributor.LocalImageLocation + }; + } +} diff --git a/Assets/__Scripts/Beatmap/Info/InfoContributor.cs.meta b/Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs.meta similarity index 83% rename from Assets/__Scripts/Beatmap/Info/InfoContributor.cs.meta rename to Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs.meta index c8e1994dc..3b2b2a49e 100644 --- a/Assets/__Scripts/Beatmap/Info/InfoContributor.cs.meta +++ b/Assets/__Scripts/Beatmap/Info/V2/V2Contributor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f96164335cb03cc45869fab9bc42a33b +guid: 022a78c2497decc47a7d5dd6e769fbcb MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/__Scripts/UI/Contributors/ContributorListItem.cs b/Assets/__Scripts/UI/Contributors/ContributorListItem.cs index 22ba40268..6103b2c39 100644 --- a/Assets/__Scripts/UI/Contributors/ContributorListItem.cs +++ b/Assets/__Scripts/UI/Contributors/ContributorListItem.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Globalization; using System.IO; -using Beatmap.Base.Customs; +using Beatmap.Info; using SFB; using TMPro; using UnityEngine; diff --git a/Assets/__Scripts/UI/Contributors/ContributorsController.cs b/Assets/__Scripts/UI/Contributors/ContributorsController.cs index 31b6150c5..bc22fb57f 100644 --- a/Assets/__Scripts/UI/Contributors/ContributorsController.cs +++ b/Assets/__Scripts/UI/Contributors/ContributorsController.cs @@ -61,7 +61,7 @@ public void RemoveAllContributors() => public void AddNewContributor() { - var contributor = new InfoContributor("", "", ""); + var contributor = new BaseContributor("", "", ""); var listItem = Instantiate(listItemPrefab, listContainer.transform).GetComponent(); listItem.Setup(contributor, this, true); Contributors.Add(contributor); From 7e73006ef1aa09857a5897a347b72889a75fd520 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:41:26 +1000 Subject: [PATCH 05/49] Add V4Contributor --- .../Beatmap/Info/V4/V4Contributor.cs | 26 +++++++++++++++++++ .../Beatmap/Info/V4/V4Contributor.cs.meta | 3 +++ 2 files changed, 29 insertions(+) create mode 100644 Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs create mode 100644 Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs.meta diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs b/Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs new file mode 100644 index 000000000..b0e3461c6 --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs @@ -0,0 +1,26 @@ +using SimpleJSON; + +namespace Beatmap.Info +{ + public static class V4Contributor + { + public static BaseContributor GetFromJson(JSONNode node) + { + var contributor = new BaseContributor(); + + contributor.Name = node["name"]?.Value; + contributor.Role = node["role"]?.Value; + contributor.LocalImageLocation = node["iconPath"]?.Value; + + return contributor; + } + + public static JSONObject ToJson(BaseContributor contributor) => + new JSONObject + { + ["name"] = contributor.Name, + ["role"] = contributor.Role, + ["iconPath"] = contributor.LocalImageLocation + }; + } +} diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs.meta b/Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs.meta new file mode 100644 index 000000000..6f2ef2adb --- /dev/null +++ b/Assets/__Scripts/Beatmap/Info/V4/V4Contributor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 04d223374b63465f8f405b6a8cd8b3e1 +timeCreated: 1728045089 \ No newline at end of file From 4a399f10f59a50f23b79be4414165884a6dcdd54 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Fri, 4 Oct 2024 23:10:09 +1000 Subject: [PATCH 06/49] Implement CustomContributors --- Assets/TestsEditMode/InfoTest.cs | 30 +++++++++++++++++-- .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 2 ++ Assets/__Scripts/Beatmap/Info/V2/V2Info.cs | 27 +++++++++++++++-- Assets/__Scripts/Beatmap/Info/V4/V4Info.cs | 24 ++++++++++++++- 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/Assets/TestsEditMode/InfoTest.cs b/Assets/TestsEditMode/InfoTest.cs index 3459637c0..664a51831 100644 --- a/Assets/TestsEditMode/InfoTest.cs +++ b/Assets/TestsEditMode/InfoTest.cs @@ -127,7 +127,16 @@ public class InfoTest } ] } - ] + ], + ""_customData"": { + ""_contributors"": [ + { + ""_name"": ""Bullet"", + ""_role"": ""Everything"", + ""_iconPath"": ""Bullet.png"" + } + ] + } } "; @@ -236,7 +245,16 @@ public class InfoTest ""beatmapDataFilename"": ""ExpertPlus.dat"", ""lightshowDataFilename"": ""LightshowPlus.dat"" } - ] + ], + ""customData"": { + ""contributors"": [ + { + ""name"": ""Bullet"", + ""role"": ""Everything"", + ""iconPath"": ""Bullet.png"" + } + ] + } } "; @@ -397,6 +415,14 @@ private void AssertCommonInfo(BaseInfo info) Assert.AreEqual(0.5f, expertPlusDifficulty.NoteStartBeatOffset); Assert.AreEqual(0, expertPlusDifficulty.ColorSchemeIndex); Assert.AreEqual(0, expertPlusDifficulty.EnvironmentNameIndex); + + // CustomData properties + Assert.AreEqual(1, info.CustomContributors.Count); + + var contributor = info.CustomContributors[0]; + Assert.AreEqual("Bullet", contributor.Name); + Assert.AreEqual("Everything", contributor.Role); + Assert.AreEqual("Bullet.png", contributor.LocalImageLocation); } private void AssertColorsAreEqual(Color expected, Color actual) diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index b076493ae..8bd4d92e0 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -53,6 +53,8 @@ public class BaseInfo // CustomData Properties public JSONNode CustomData = new JSONObject(); + + public List CustomContributors = new(); } public class InfoDifficultySet diff --git a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs index a3d0064ff..388e3ed0e 100644 --- a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs @@ -83,8 +83,19 @@ public static BaseInfo GetFromJson(JSONNode node) beatmapSets.Add(beatmapSet); } info.DifficultySets = beatmapSets; - - info.CustomData = node["_customData"]; + + // CustomData Parsing + if (node["_customData"].IsObject) + { + var customData = node["_customData"]; + + if (customData["_contributors"].IsArray) + { + info.CustomContributors = customData["_contributors"].AsArray.Children.Select(V2Contributor.GetFromJson).ToList(); + } + + info.CustomData = customData; + } return info; } @@ -163,6 +174,18 @@ public static JSONNode GetOutputJson(BaseInfo info) json["_difficultyBeatmapSets"] = beatmapSetArray; + // CustomData writing + if (info.CustomContributors.Any()) + { + var customContributors = new JSONArray(); + foreach (var customContributor in info.CustomContributors) + { + customContributors.Add(V2Contributor.ToJson(customContributor)); + } + + info.CustomData["_contributors"] = customContributors; + } + json["_customData"] = info.CustomData; return json; diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs index 5ae44eb1d..2d2a5d403 100644 --- a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs @@ -84,7 +84,18 @@ public static BaseInfo GetFromJson(JSONNode node) info.DifficultySets = difficultySets; - info.CustomData = node["customData"]; + // CustomData Parsing + if (node["customData"].IsObject) + { + var customData = node["customData"]; + + if (customData["contributors"].IsArray) + { + info.CustomContributors = customData["contributors"].AsArray.Children.Select(V4Contributor.GetFromJson).ToList(); + } + + info.CustomData = customData; + } return info; } @@ -174,6 +185,17 @@ public static JSONNode GetOutputJson(BaseInfo info) json["difficultyBeatmaps"] = difficultyBeatmaps; + // CustomData writing + if (info.CustomContributors.Any()) + { + var customContributors = new JSONArray(); + foreach (var customContributor in info.CustomContributors) + { + customContributors.Add(V4Contributor.ToJson(customContributor)); + } + info.CustomData["contributors"] = customContributors; + } + json["customData"] = info.CustomData; return json; From 17eaac7246400994096416bde1948313d990f5e6 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:54:19 +1000 Subject: [PATCH 07/49] Implement difficulty custom properties --- Assets/Plugins/SimpleJSONHelper.cs | 14 +++ Assets/TestsEditMode/InfoTest.cs | 73 ++++++++++++-- .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 19 ++-- Assets/__Scripts/Beatmap/Info/V2/V2Info.cs | 94 ++++++++++++++++++- Assets/__Scripts/Beatmap/Info/V4/V4Info.cs | 92 ++++++++++++++++++ 5 files changed, 279 insertions(+), 13 deletions(-) diff --git a/Assets/Plugins/SimpleJSONHelper.cs b/Assets/Plugins/SimpleJSONHelper.cs index 25c8068ef..947806f9b 100644 --- a/Assets/Plugins/SimpleJSONHelper.cs +++ b/Assets/Plugins/SimpleJSONHelper.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace SimpleJSON @@ -6,6 +7,19 @@ public static class SimpleJSONHelper { private const string v2CustomData = "_customData"; private const string v3CustomData = "customData"; + + public static JSONArray MapSequenceToJSONArray(IEnumerable source, Func func) + { + var array = new JSONArray(); + + foreach (var element in source) + { + array.Add(func(element)); + } + + return array; + } + public static void RemovePropertiesWithDefaultValues(JSONNode node) { diff --git a/Assets/TestsEditMode/InfoTest.cs b/Assets/TestsEditMode/InfoTest.cs index 664a51831..f0442551e 100644 --- a/Assets/TestsEditMode/InfoTest.cs +++ b/Assets/TestsEditMode/InfoTest.cs @@ -87,7 +87,25 @@ public class InfoTest ""_noteJumpMovementSpeed"": 10, ""_noteJumpStartBeatOffset"": 0, ""_beatmapColorSchemeIdx"": 0, - ""_environmentNameIdx"": 0 + ""_environmentNameIdx"": 0, + ""_customData"": { + ""_oneSaber"" : true, + ""_showRotationNoteSpawnLines"" : true, + ""_difficultyLabel"": ""ACustomLabel"", + ""_warnings"": [ + ""Warning1"", + ""Warning2"" + ], + ""_information"": [ + ""Info"" + ], + ""_suggestions"": [ + ""Chroma"" + ], + ""_requirements"": [ + ""Noodle Extensions"" + ] + } }, { ""_difficulty"": ""Normal"", @@ -187,7 +205,25 @@ public class InfoTest ""noteJumpMovementSpeed"": 10, ""noteJumpStartBeatOffset"": 0, ""beatmapDataFilename"": ""Easy.dat"", - ""lightshowDataFilename"": ""Lightshow.dat"" + ""lightshowDataFilename"": ""Lightshow.dat"", + ""customData"": { + ""oneSaber"" : true, + ""showRotationNoteSpawnLines"" : true, + ""difficultyLabel"": ""ACustomLabel"", + ""warnings"": [ + ""Warning1"", + ""Warning2"" + ], + ""information"": [ + ""Info"" + ], + ""suggestions"": [ + ""Chroma"" + ], + ""requirements"": [ + ""Noodle Extensions"" + ] + } }, { ""characteristic"": ""Standard"", @@ -259,14 +295,14 @@ public class InfoTest "; [Test] - public void V2_GetFromJson_VanillaProperties() + public void V2_GetFromJson() { var info = V2Info.GetFromJson(JSONNode.Parse(v2FileInfo)); AssertV2Info(info); } [Test] - public void V2_GetOutputJson_VanillaProperties() + public void V2_GetOutputJson() { var info = V2Info.GetFromJson(JSONNode.Parse(v2FileInfo)); var output = V2Info.GetOutputJson(info); @@ -276,14 +312,14 @@ public void V2_GetOutputJson_VanillaProperties() } [Test] - public void V4_GetFromJson_VanillaProperties() + public void V4_GetFromJson() { var info = V4Info.GetFromJson(JSONNode.Parse(v4FileInfo)); AssertV4Info(info); } [Test] - public void V4_GetOutputJson_VanillaProperties() + public void V4_GetOutputJson() { var info = V4Info.GetFromJson(JSONNode.Parse(v4FileInfo)); var output = V4Info.GetOutputJson(info); @@ -380,6 +416,21 @@ private void AssertCommonInfo(BaseInfo info) Assert.AreEqual(0, easyDifficulty.ColorSchemeIndex); Assert.AreEqual(0, easyDifficulty.EnvironmentNameIndex); + // Custom properties for Easy + Assert.IsTrue(easyDifficulty.CustomOneSaberFlag); + Assert.IsTrue(easyDifficulty.CustomShowRotationNoteSpawnLinesFlag); + Assert.AreEqual("ACustomLabel", easyDifficulty.CustomLabel); + + Assert.AreEqual(1, easyDifficulty.CustomInformation.Count); + Assert.AreEqual("Info", easyDifficulty.CustomInformation[0]); + Assert.AreEqual(2, easyDifficulty.CustomWarnings.Count); + Assert.AreEqual("Warning1", easyDifficulty.CustomWarnings[0]); + Assert.AreEqual("Warning2", easyDifficulty.CustomWarnings[1]); + Assert.AreEqual(1, easyDifficulty.CustomSuggestions.Count); + Assert.AreEqual("Chroma", easyDifficulty.CustomSuggestions[0]); + Assert.AreEqual(1, easyDifficulty.CustomRequirements.Count); + Assert.AreEqual("Noodle Extensions", easyDifficulty.CustomRequirements[0]); + var normalDifficulty = difficultySet.Difficulties[1]; Assert.AreEqual("Normal", normalDifficulty.Difficulty); Assert.AreEqual(3, normalDifficulty.DifficultyRank); @@ -388,6 +439,16 @@ private void AssertCommonInfo(BaseInfo info) Assert.AreEqual(0, normalDifficulty.NoteStartBeatOffset); Assert.AreEqual(0, normalDifficulty.ColorSchemeIndex); Assert.AreEqual(0, normalDifficulty.EnvironmentNameIndex); + + // Non-existent custom properties for normal + Assert.IsNull(normalDifficulty.CustomOneSaberFlag); + Assert.IsNull(normalDifficulty.CustomShowRotationNoteSpawnLinesFlag); + Assert.IsTrue(string.IsNullOrWhiteSpace(normalDifficulty.CustomLabel)); + + Assert.AreEqual(0, normalDifficulty.CustomInformation.Count); + Assert.AreEqual(0, normalDifficulty.CustomWarnings.Count); + Assert.AreEqual(0, normalDifficulty.CustomSuggestions.Count); + Assert.AreEqual(0, normalDifficulty.CustomRequirements.Count); var hardDifficulty = difficultySet.Difficulties[2]; Assert.AreEqual("Hard", hardDifficulty.Difficulty); diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index 8bd4d92e0..2003490db 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -64,13 +64,13 @@ public class InfoDifficultySet public JSONObject CustomData { get; set; } = new(); // Pretty much just for v2 SongCore parsing } - + public class InfoDifficulty { public InfoDifficulty(InfoDifficultySet parentSet) => ParentSet = parentSet; public InfoDifficultySet ParentSet { get; set; } public string Characteristic => ParentSet.Characteristic; - + public string BeatmapFileName { get; set; } public string LightshowFileName { get; set; } public string Difficulty { get; set; } @@ -85,20 +85,27 @@ public class InfoDifficulty "Easy" => 1, _ => -1 }; - + public int EnvironmentNameIndex { get; set; } public int ColorSchemeIndex { get; set; } public float NoteJumpSpeed { get; set; } public float NoteStartBeatOffset { get; set; } - public List Mappers { get; set; } public List Lighters { get; set; } - - + // CustomData Properties public JSONObject CustomData { get; set; } = new(); + + public string CustomLabel { get; set; } + public bool? CustomOneSaberFlag { get; set; } + public bool? CustomShowRotationNoteSpawnLinesFlag { get; set; } + + public List CustomInformation { get; set; } = new(); + public List CustomWarnings { get; set; } = new(); + public List CustomSuggestions { get; set; } = new(); + public List CustomRequirements { get; set; } = new(); } public class InfoColorScheme diff --git a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs index 388e3ed0e..9373843cd 100644 --- a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs @@ -72,7 +72,11 @@ public static BaseInfo GetFromJson(JSONNode node) infoDifficulty.NoteJumpSpeed = beatmapNode["_noteJumpMovementSpeed"].AsFloat; infoDifficulty.NoteStartBeatOffset = beatmapNode["_noteJumpStartBeatOffset"].AsFloat; - infoDifficulty.CustomData = beatmapNode["_customData"].AsObject; + var customData = beatmapNode["_customData"].AsObject; + + ParseDifficultyCustomData(customData, infoDifficulty); + + infoDifficulty.CustomData = customData; difficulties.Add(infoDifficulty); } @@ -163,6 +167,9 @@ public static JSONNode GetOutputJson(BaseInfo info) node["_noteJumpStartBeatOffset"] = difficulty.NoteStartBeatOffset; node["_beatmapColorSchemeIdx"] = difficulty.ColorSchemeIndex; node["_environmentNameIdx"] = difficulty.EnvironmentNameIndex; + + PopulateDifficultyCustomData(difficulty); + node["_customData"] = difficulty.CustomData; difficultyBeatmapsArray.Add(node); } @@ -190,5 +197,90 @@ public static JSONNode GetOutputJson(BaseInfo info) return json; } + + private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficulty difficulty) + { + if (customData["_oneSaber"].IsBoolean) + { + difficulty.CustomOneSaberFlag = customData["_oneSaber"].AsBool; + } + + if (customData["_showRotationNoteSpawnLines"].IsBoolean) + { + difficulty.CustomShowRotationNoteSpawnLinesFlag = customData["_showRotationNoteSpawnLines"].AsBool; + } + + if (customData["_difficultyLabel"].IsString) + { + difficulty.CustomLabel = customData["_difficultyLabel"].Value; + } + + if (customData["_information"].IsArray) + { + difficulty.CustomInformation = + customData["_information"].AsArray.Children.Select(x => x.Value).ToList(); + } + + if (customData["_warnings"].IsArray) + { + difficulty.CustomWarnings = + customData["_warnings"].AsArray.Children.Select(x => x.Value).ToList(); + } + + if (customData["_suggestions"].IsArray) + { + difficulty.CustomSuggestions = + customData["_suggestions"].AsArray.Children.Select(x => x.Value).ToList(); + } + + if (customData["_requirements"].IsArray) + { + difficulty.CustomRequirements = + customData["_requirements"].AsArray.Children.Select(x => x.Value).ToList(); + } + } + + + private static void PopulateDifficultyCustomData(InfoDifficulty difficulty) + { + if (difficulty.CustomOneSaberFlag != null) + { + difficulty.CustomData["_oneSaber"] = difficulty.CustomOneSaberFlag.Value; + } + + if (difficulty.CustomShowRotationNoteSpawnLinesFlag != null) + { + difficulty.CustomData["_showRotationNoteSpawnLines"] = difficulty.CustomShowRotationNoteSpawnLinesFlag.Value; + } + + if (!string.IsNullOrWhiteSpace(difficulty.CustomLabel)) + { + difficulty.CustomData["_difficultyLabel"] = difficulty.CustomLabel; + } + + if (difficulty.CustomInformation.Any()) + { + difficulty.CustomData["_information"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomInformation, s => s); + } + + if (difficulty.CustomWarnings.Any()) + { + difficulty.CustomData["_warnings"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomWarnings, s => s); + } + + if (difficulty.CustomSuggestions.Any()) + { + difficulty.CustomData["_suggestions"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomSuggestions, s => s); + } + + if (difficulty.CustomRequirements.Any()) + { + difficulty.CustomData["_requirements"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomRequirements, s => s); + } + } } } diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs index 2d2a5d403..ae73126af 100644 --- a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs @@ -75,6 +75,12 @@ public static BaseInfo GetFromJson(JSONNode node) infoDifficulty.Mappers = authorsNode["mappers"].AsArray.Children.Select(x => x.Value).ToList(); infoDifficulty.Lighters = authorsNode["lighters"].AsArray.Children.Select(x => x.Value).ToList(); + var customData = beatmap["customData"].AsObject; + + ParseDifficultyCustomData(customData, infoDifficulty); + + infoDifficulty.CustomData = customData; + infoDifficultys.Add(infoDifficulty); } @@ -177,6 +183,8 @@ public static JSONNode GetOutputJson(BaseInfo info) node["noteJumpStartBeatOffset"] = difficulty.NoteStartBeatOffset; node["beatmapDataFilename"] = difficulty.BeatmapFileName; node["lightshowDataFilename"] = difficulty.LightshowFileName; + + PopulateDifficultyCustomData(difficulty); node["customData"] = difficulty.CustomData; @@ -202,5 +210,89 @@ public static JSONNode GetOutputJson(BaseInfo info) } private static string ToHashPrefixedHtmlStringRGBA(Color color) => $"#{ColorUtility.ToHtmlStringRGBA(color)}"; + + private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficulty difficulty) + { + if (customData["oneSaber"].IsBoolean) + { + difficulty.CustomOneSaberFlag = customData["oneSaber"].AsBool; + } + + if (customData["showRotationNoteSpawnLines"].IsBoolean) + { + difficulty.CustomShowRotationNoteSpawnLinesFlag = customData["showRotationNoteSpawnLines"].AsBool; + } + + if (customData["difficultyLabel"].IsString) + { + difficulty.CustomLabel = customData["difficultyLabel"].Value; + } + + if (customData["information"].IsArray) + { + difficulty.CustomInformation = + customData["information"].AsArray.Children.Select(x => x.Value).ToList(); + } + + if (customData["warnings"].IsArray) + { + difficulty.CustomWarnings = + customData["warnings"].AsArray.Children.Select(x => x.Value).ToList(); + } + + if (customData["suggestions"].IsArray) + { + difficulty.CustomSuggestions = + customData["suggestions"].AsArray.Children.Select(x => x.Value).ToList(); + } + + if (customData["requirements"].IsArray) + { + difficulty.CustomRequirements = + customData["requirements"].AsArray.Children.Select(x => x.Value).ToList(); + } + } + + private static void PopulateDifficultyCustomData(InfoDifficulty difficulty) + { + if (difficulty.CustomOneSaberFlag != null) + { + difficulty.CustomData["oneSaber"] = difficulty.CustomOneSaberFlag.Value; + } + + if (difficulty.CustomShowRotationNoteSpawnLinesFlag != null) + { + difficulty.CustomData["showRotationNoteSpawnLines"] = difficulty.CustomShowRotationNoteSpawnLinesFlag.Value; + } + + if (!string.IsNullOrWhiteSpace(difficulty.CustomLabel)) + { + difficulty.CustomData["difficultyLabel"] = difficulty.CustomLabel; + } + + if (difficulty.CustomInformation.Any()) + { + difficulty.CustomData["information"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomInformation, s => s); + } + + if (difficulty.CustomWarnings.Any()) + { + difficulty.CustomData["warnings"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomWarnings, s => s); + } + + if (difficulty.CustomSuggestions.Any()) + { + difficulty.CustomData["suggestions"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomSuggestions, s => s); + } + + if (difficulty.CustomRequirements.Any()) + { + difficulty.CustomData["requirements"] = + SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomRequirements, s => s); + } + } } } From 35938c824567c7c91080668dc59eaa09f05afe36 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Sun, 6 Oct 2024 12:03:26 +1000 Subject: [PATCH 08/49] Implement difficulty custom colors --- Assets/TestsEditMode/InfoTest.cs | 114 +++++++++++++++++- .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 10 ++ Assets/__Scripts/Beatmap/Info/V2/V2Info.cs | 97 ++++++++++++++- Assets/__Scripts/Beatmap/Info/V4/V4Info.cs | 95 +++++++++++++++ 4 files changed, 313 insertions(+), 3 deletions(-) diff --git a/Assets/TestsEditMode/InfoTest.cs b/Assets/TestsEditMode/InfoTest.cs index f0442551e..390d846d2 100644 --- a/Assets/TestsEditMode/InfoTest.cs +++ b/Assets/TestsEditMode/InfoTest.cs @@ -104,7 +104,52 @@ public class InfoTest ], ""_requirements"": [ ""Noodle Extensions"" - ] + ], + ""_colorLeft"": { + ""r"": 0.111, + ""g"": 0.111, + ""b"": 0.111 + }, + ""_colorRight"": { + ""r"": 0.222, + ""g"": 0.222, + ""b"": 0.222 + }, + ""_obstacleColor"": { + ""r"": 0.333, + ""g"": 0.333, + ""b"": 0.333 + }, + ""_envColorLeft"": { + ""r"": 0.444, + ""g"": 0.444, + ""b"": 0.444 + }, + ""_envColorRight"": { + ""r"": 0.555, + ""g"": 0.555, + ""b"": 0.555 + }, + ""_envColorWhite"": { + ""r"": 0.666, + ""g"": 0.666, + ""b"": 0.666 + }, + ""_envColorLeftBoost"": { + ""r"": 0.777, + ""g"": 0.777, + ""b"": 0.777 + }, + ""_envColorRightBoost"": { + ""r"": 0.888, + ""g"": 0.888, + ""b"": 0.888 + }, + ""_envColorWhiteBoost"": { + ""r"": 0.999, + ""g"": 0.999, + ""b"": 0.999 + } } }, { @@ -222,7 +267,52 @@ public class InfoTest ], ""requirements"": [ ""Noodle Extensions"" - ] + ], + ""colorLeft"": { + ""r"": 0.111, + ""g"": 0.111, + ""b"": 0.111 + }, + ""colorRight"": { + ""r"": 0.222, + ""g"": 0.222, + ""b"": 0.222 + }, + ""obstacleColor"": { + ""r"": 0.333, + ""g"": 0.333, + ""b"": 0.333 + }, + ""envColorLeft"": { + ""r"": 0.444, + ""g"": 0.444, + ""b"": 0.444 + }, + ""envColorRight"": { + ""r"": 0.555, + ""g"": 0.555, + ""b"": 0.555 + }, + ""envColorWhite"": { + ""r"": 0.666, + ""g"": 0.666, + ""b"": 0.666 + }, + ""envColorLeftBoost"": { + ""r"": 0.777, + ""g"": 0.777, + ""b"": 0.777 + }, + ""envColorRightBoost"": { + ""r"": 0.888, + ""g"": 0.888, + ""b"": 0.888 + }, + ""envColorWhiteBoost"": { + ""r"": 0.999, + ""g"": 0.999, + ""b"": 0.999 + } } }, { @@ -431,6 +521,16 @@ private void AssertCommonInfo(BaseInfo info) Assert.AreEqual(1, easyDifficulty.CustomRequirements.Count); Assert.AreEqual("Noodle Extensions", easyDifficulty.CustomRequirements[0]); + AssertColorsAreEqual(new Color(0.111f, 0.111f, 0.111f),easyDifficulty.CustomColorLeft!.Value); + AssertColorsAreEqual(new Color(0.222f, 0.222f, 0.222f),easyDifficulty.CustomColorRight!.Value); + AssertColorsAreEqual(new Color(0.333f, 0.333f, 0.333f),easyDifficulty.CustomColorObstacle!.Value); + AssertColorsAreEqual(new Color(0.444f, 0.444f, 0.444f),easyDifficulty.CustomEnvColorLeft!.Value); + AssertColorsAreEqual(new Color(0.555f, 0.555f, 0.555f),easyDifficulty.CustomEnvColorRight!.Value); + AssertColorsAreEqual(new Color(0.666f, 0.666f, 0.666f),easyDifficulty.CustomEnvColorWhite!.Value); + AssertColorsAreEqual(new Color(0.777f, 0.777f, 0.777f),easyDifficulty.CustomEnvColorBoostLeft!.Value); + AssertColorsAreEqual(new Color(0.888f, 0.888f, 0.888f),easyDifficulty.CustomEnvColorBoostRight!.Value); + AssertColorsAreEqual(new Color(0.999f, 0.999f, 0.999f),easyDifficulty.CustomEnvColorBoostWhite!.Value); + var normalDifficulty = difficultySet.Difficulties[1]; Assert.AreEqual("Normal", normalDifficulty.Difficulty); Assert.AreEqual(3, normalDifficulty.DifficultyRank); @@ -449,6 +549,16 @@ private void AssertCommonInfo(BaseInfo info) Assert.AreEqual(0, normalDifficulty.CustomWarnings.Count); Assert.AreEqual(0, normalDifficulty.CustomSuggestions.Count); Assert.AreEqual(0, normalDifficulty.CustomRequirements.Count); + + Assert.IsNull(normalDifficulty.CustomColorLeft); + Assert.IsNull(normalDifficulty.CustomColorRight); + Assert.IsNull(normalDifficulty.CustomColorObstacle); + Assert.IsNull(normalDifficulty.CustomEnvColorLeft); + Assert.IsNull(normalDifficulty.CustomEnvColorRight); + Assert.IsNull(normalDifficulty.CustomEnvColorWhite); + Assert.IsNull(normalDifficulty.CustomEnvColorBoostLeft); + Assert.IsNull(normalDifficulty.CustomEnvColorBoostRight); + Assert.IsNull(normalDifficulty.CustomEnvColorBoostWhite); var hardDifficulty = difficultySet.Difficulties[2]; Assert.AreEqual("Hard", hardDifficulty.Difficulty); diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index 2003490db..99ccd9cff 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -106,6 +106,16 @@ public class InfoDifficulty public List CustomWarnings { get; set; } = new(); public List CustomSuggestions { get; set; } = new(); public List CustomRequirements { get; set; } = new(); + + public Color? CustomColorLeft { get; set; } + public Color? CustomColorRight { get; set; } + public Color? CustomColorObstacle { get; set; } + public Color? CustomEnvColorLeft { get; set; } + public Color? CustomEnvColorRight { get; set; } + public Color? CustomEnvColorWhite { get; set; } + public Color? CustomEnvColorBoostLeft { get; set; } + public Color? CustomEnvColorBoostRight { get; set; } + public Color? CustomEnvColorBoostWhite { get; set; } } public class InfoColorScheme diff --git a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs index 9373843cd..306cc6fac 100644 --- a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using SimpleJSON; @@ -238,6 +238,51 @@ private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficult difficulty.CustomRequirements = customData["_requirements"].AsArray.Children.Select(x => x.Value).ToList(); } + + if (customData["_colorLeft"].IsObject) + { + difficulty.CustomColorLeft = customData["_colorLeft"].ReadColor(); + } + + if (customData["_colorRight"].IsObject) + { + difficulty.CustomColorRight = customData["_colorRight"].ReadColor(); + } + + if (customData["_obstacleColor"].IsObject) + { + difficulty.CustomColorObstacle = customData["_obstacleColor"].ReadColor(); + } + + if (customData["_envColorLeft"].IsObject) + { + difficulty.CustomEnvColorLeft = customData["_envColorLeft"].ReadColor(); + } + + if (customData["_envColorRight"].IsObject) + { + difficulty.CustomEnvColorRight = customData["_envColorRight"].ReadColor(); + } + + if (customData["_envColorWhite"].IsObject) + { + difficulty.CustomEnvColorWhite = customData["_envColorWhite"].ReadColor(); + } + + if (customData["_envColorLeftBoost"].IsObject) + { + difficulty.CustomEnvColorBoostLeft = customData["_envColorLeftBoost"].ReadColor(); + } + + if (customData["_envColorRightBoost"].IsObject) + { + difficulty.CustomEnvColorBoostRight = customData["_envColorRightBoost"].ReadColor(); + } + + if (customData["_envColorWhiteBoost"].IsObject) + { + difficulty.CustomEnvColorBoostWhite = customData["_envColorWhiteBoost"].ReadColor(); + } } @@ -281,6 +326,56 @@ private static void PopulateDifficultyCustomData(InfoDifficulty difficulty) difficulty.CustomData["_requirements"] = SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomRequirements, s => s); } + + // SongCore saves colors in Object format so temporarily change container type + JSONNode.ColorContainerType = JSONContainerType.Object; + + if (difficulty.CustomColorLeft != null) + { + difficulty.CustomData["_colorLeft"] = difficulty.CustomColorLeft.Value; + } + + if (difficulty.CustomColorRight != null) + { + difficulty.CustomData["_colorRight"] = difficulty.CustomColorRight.Value; + } + + if (difficulty.CustomColorObstacle != null) + { + difficulty.CustomData["_obstacleColor"] = difficulty.CustomColorObstacle.Value; + } + + if (difficulty.CustomEnvColorLeft != null) + { + difficulty.CustomData["_envColorLeft"] = difficulty.CustomEnvColorLeft.Value; + } + + if (difficulty.CustomEnvColorRight != null) + { + difficulty.CustomData["_envColorRight"] = difficulty.CustomEnvColorRight.Value; + } + + if (difficulty.CustomEnvColorWhite != null) + { + difficulty.CustomData["_envColorWhite"] = difficulty.CustomEnvColorWhite.Value; + } + + if (difficulty.CustomEnvColorBoostLeft != null) + { + difficulty.CustomData["_envColorLeftBoost"] = difficulty.CustomEnvColorBoostLeft.Value; + } + + if (difficulty.CustomEnvColorBoostRight != null) + { + difficulty.CustomData["_envColorRightBoost"] = difficulty.CustomEnvColorBoostRight.Value; + } + + if (difficulty.CustomEnvColorBoostWhite != null) + { + difficulty.CustomData["_envColorWhiteBoost"] = difficulty.CustomEnvColorBoostWhite.Value; + } + + JSONNode.ColorContainerType = JSONContainerType.Array; } } } diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs index ae73126af..e4eeb07e1 100644 --- a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs @@ -251,6 +251,51 @@ private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficult difficulty.CustomRequirements = customData["requirements"].AsArray.Children.Select(x => x.Value).ToList(); } + + if (customData["colorLeft"].IsObject) + { + difficulty.CustomColorLeft = customData["colorLeft"].ReadColor(); + } + + if (customData["colorRight"].IsObject) + { + difficulty.CustomColorRight = customData["colorRight"].ReadColor(); + } + + if (customData["obstacleColor"].IsObject) + { + difficulty.CustomColorObstacle = customData["obstacleColor"].ReadColor(); + } + + if (customData["envColorLeft"].IsObject) + { + difficulty.CustomEnvColorLeft = customData["envColorLeft"].ReadColor(); + } + + if (customData["envColorRight"].IsObject) + { + difficulty.CustomEnvColorRight = customData["envColorRight"].ReadColor(); + } + + if (customData["envColorWhite"].IsObject) + { + difficulty.CustomEnvColorWhite = customData["envColorWhite"].ReadColor(); + } + + if (customData["envColorLeftBoost"].IsObject) + { + difficulty.CustomEnvColorBoostLeft = customData["envColorLeftBoost"].ReadColor(); + } + + if (customData["envColorRightBoost"].IsObject) + { + difficulty.CustomEnvColorBoostRight = customData["envColorRightBoost"].ReadColor(); + } + + if (customData["envColorWhiteBoost"].IsObject) + { + difficulty.CustomEnvColorBoostWhite = customData["envColorWhiteBoost"].ReadColor(); + } } private static void PopulateDifficultyCustomData(InfoDifficulty difficulty) @@ -293,6 +338,56 @@ private static void PopulateDifficultyCustomData(InfoDifficulty difficulty) difficulty.CustomData["requirements"] = SimpleJSONHelper.MapSequenceToJSONArray(difficulty.CustomRequirements, s => s); } + + // SongCore saves colors in Object format so temporarily change container type + JSONNode.ColorContainerType = JSONContainerType.Object; + + if (difficulty.CustomColorLeft != null) + { + difficulty.CustomData["colorLeft"] = difficulty.CustomColorLeft.Value; + } + + if (difficulty.CustomColorRight != null) + { + difficulty.CustomData["colorRight"] = difficulty.CustomColorRight.Value; + } + + if (difficulty.CustomColorObstacle != null) + { + difficulty.CustomData["obstacleColor"] = difficulty.CustomColorObstacle.Value; + } + + if (difficulty.CustomEnvColorLeft != null) + { + difficulty.CustomData["envColorLeft"] = difficulty.CustomEnvColorLeft.Value; + } + + if (difficulty.CustomEnvColorRight != null) + { + difficulty.CustomData["envColorRight"] = difficulty.CustomEnvColorRight.Value; + } + + if (difficulty.CustomEnvColorWhite != null) + { + difficulty.CustomData["envColorWhite"] = difficulty.CustomEnvColorWhite.Value; + } + + if (difficulty.CustomEnvColorBoostLeft != null) + { + difficulty.CustomData["envColorLeftBoost"] = difficulty.CustomEnvColorBoostLeft.Value; + } + + if (difficulty.CustomEnvColorBoostRight != null) + { + difficulty.CustomData["envColorRightBoost"] = difficulty.CustomEnvColorBoostRight.Value; + } + + if (difficulty.CustomEnvColorBoostWhite != null) + { + difficulty.CustomData["envColorWhiteBoost"] = difficulty.CustomEnvColorBoostWhite.Value; + } + + JSONNode.ColorContainerType = JSONContainerType.Array; } } } From 66c4af6da07df1ccd83d2ea8da1bdb29044dd689 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Sun, 6 Oct 2024 12:42:26 +1000 Subject: [PATCH 09/49] Implement custom characteristic data --- Assets/TestsEditMode/InfoTest.cs | 15 ++++++ .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 7 ++- Assets/__Scripts/Beatmap/Info/V2/V2Info.cs | 35 ++++++++++++- Assets/__Scripts/Beatmap/Info/V4/V4Info.cs | 50 ++++++++++++++++++- 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/Assets/TestsEditMode/InfoTest.cs b/Assets/TestsEditMode/InfoTest.cs index 390d846d2..6f658a58e 100644 --- a/Assets/TestsEditMode/InfoTest.cs +++ b/Assets/TestsEditMode/InfoTest.cs @@ -78,6 +78,10 @@ public class InfoTest ], ""_difficultyBeatmapSets"": [ { + ""_customData"": { + ""_characteristicLabel"" : ""A Custom Characteristic"", + ""_characteristicIconImageFilename"" : ""customCharacteristic.png"" + } ""_beatmapCharacteristicName"": ""Standard"", ""_difficultyBeatmaps"": [ { @@ -379,6 +383,13 @@ public class InfoTest ""role"": ""Everything"", ""iconPath"": ""Bullet.png"" } + ], + ""characteristicData"": [ + { + ""characteristic"": ""Standard"", + ""label"" : ""A Custom Characteristic"", + ""iconPath"" : ""customCharacteristic.png"" + } ] } } @@ -496,6 +507,10 @@ private void AssertCommonInfo(BaseInfo info) var difficultySet = info.DifficultySets[0]; Assert.AreEqual("Standard", difficultySet.Characteristic); Assert.AreEqual(5, difficultySet.Difficulties.Count); + + // Custom properties for Set + Assert.AreEqual("A Custom Characteristic", difficultySet.CustomCharacteristicLabel); + Assert.AreEqual("customCharacteristic.png", difficultySet.CustomCharacteristicIconImageFileName); var easyDifficulty = difficultySet.Difficulties[0]; Assert.AreEqual("Easy", easyDifficulty.Difficulty); diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index 99ccd9cff..a94c976f4 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -63,13 +63,18 @@ public class InfoDifficultySet public List Difficulties { get; set; } = new(); public JSONObject CustomData { get; set; } = new(); // Pretty much just for v2 SongCore parsing + + public string CustomCharacteristicLabel { get; set; } + public string CustomCharacteristicIconImageFileName { get; set; } } public class InfoDifficulty { public InfoDifficulty(InfoDifficultySet parentSet) => ParentSet = parentSet; - public InfoDifficultySet ParentSet { get; set; } + private InfoDifficultySet ParentSet { get; } public string Characteristic => ParentSet.Characteristic; + public string CustomCharacteristicLabel => ParentSet.CustomCharacteristicLabel; + public string CustomCharacteristicIconImageFileName => ParentSet.CustomCharacteristicIconImageFileName; public string BeatmapFileName { get; set; } public string LightshowFileName { get; set; } diff --git a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs index 306cc6fac..b8dc5669c 100644 --- a/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V2/V2Info.cs @@ -82,7 +82,12 @@ public static BaseInfo GetFromJson(JSONNode node) } beatmapSet.Difficulties = difficulties; - beatmapSet.CustomData = beatmapSetNode["_customData"].AsObject; + + var setCustomData = beatmapSetNode["_customData"].AsObject; + + ParseDifficultySetCustomData(setCustomData, beatmapSet); + + beatmapSet.CustomData = setCustomData; beatmapSets.Add(beatmapSet); } @@ -175,6 +180,9 @@ public static JSONNode GetOutputJson(BaseInfo info) } setNode["_difficultyBeatmaps"] = difficultyBeatmapsArray; + + PopulateDifficultySetCustomData(beatmapSet); + setNode["_customData"] = beatmapSet.CustomData; beatmapSetArray.Add(setNode); } @@ -197,6 +205,19 @@ public static JSONNode GetOutputJson(BaseInfo info) return json; } + + private static void ParseDifficultySetCustomData(JSONNode customData, InfoDifficultySet difficultySet) + { + if (customData["_characteristicLabel"].IsString) + { + difficultySet.CustomCharacteristicLabel = customData["_characteristicLabel"].Value; + } + + if (customData["_characteristicIconImageFilename"].IsString) + { + difficultySet.CustomCharacteristicIconImageFileName = customData["_characteristicIconImageFilename"].Value; + } + } private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficulty difficulty) { @@ -285,6 +306,18 @@ private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficult } } + private static void PopulateDifficultySetCustomData(InfoDifficultySet difficultySet) + { + if (!string.IsNullOrWhiteSpace(difficultySet.CustomCharacteristicLabel)) + { + difficultySet.CustomData["_characteristicLabel"] = difficultySet.CustomCharacteristicLabel; + } + + if (!string.IsNullOrWhiteSpace(difficultySet.CustomCharacteristicIconImageFileName)) + { + difficultySet.CustomData["_characteristicIconImageFilename"] = difficultySet.CustomCharacteristicIconImageFileName; + } + } private static void PopulateDifficultyCustomData(InfoDifficulty difficulty) { diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs index e4eeb07e1..5931e3a90 100644 --- a/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs +++ b/Assets/__Scripts/Beatmap/Info/V4/V4Info.cs @@ -100,6 +100,20 @@ public static BaseInfo GetFromJson(JSONNode node) info.CustomContributors = customData["contributors"].AsArray.Children.Select(V4Contributor.GetFromJson).ToList(); } + // v4 no longer has difficulty sets so the customData for custom characteristics is done here + if (customData["characteristicData"].IsArray) + { + foreach (var characteristicNode in customData["characteristicData"].AsArray.Children) + { + var characteristic = characteristicNode["characteristic"].Value; + var difficultySet = info.DifficultySets.FirstOrDefault(x => x.Characteristic == characteristic); + if (difficultySet != null) + { + ParseDifficultySetCustomData(characteristicNode, difficultySet); + } + } + } + info.CustomData = customData; } @@ -202,8 +216,29 @@ public static JSONNode GetOutputJson(BaseInfo info) customContributors.Add(V4Contributor.ToJson(customContributor)); } info.CustomData["contributors"] = customContributors; + + var customCharacteristics = new JSONArray(); + foreach (var difficultySet in info.DifficultySets) + { + var customCharacteristic = new JSONObject { ["characteristic"] = difficultySet.Characteristic }; + if (!string.IsNullOrWhiteSpace(difficultySet.CustomCharacteristicLabel)) + { + customCharacteristic["label"] = difficultySet.CustomCharacteristicLabel; + } + + if (!string.IsNullOrWhiteSpace(difficultySet.CustomCharacteristicIconImageFileName)) + { + customCharacteristic["iconPath"] = difficultySet.CustomCharacteristicIconImageFileName; + } + + if (customCharacteristic.Count > 1) + { + customCharacteristics.Add(customCharacteristic); + } + } + info.CustomData["characteristicData"] = customCharacteristics; } - + json["customData"] = info.CustomData; return json; @@ -211,6 +246,19 @@ public static JSONNode GetOutputJson(BaseInfo info) private static string ToHashPrefixedHtmlStringRGBA(Color color) => $"#{ColorUtility.ToHtmlStringRGBA(color)}"; + private static void ParseDifficultySetCustomData(JSONNode customData, InfoDifficultySet difficultySet) + { + if (customData["label"].IsString) + { + difficultySet.CustomCharacteristicLabel = customData["label"].Value; + } + + if (customData["iconPath"].IsString) + { + difficultySet.CustomCharacteristicIconImageFileName = customData["iconPath"].Value; + } + } + private static void ParseDifficultyCustomData(JSONNode customData, InfoDifficulty difficulty) { if (customData["oneSaber"].IsBoolean) From d14de0e5c4f20dda89f31e194831034617786af7 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:02:37 +1000 Subject: [PATCH 10/49] wip: SongList displays v4 now! Nothing else works :D --- Assets/__Scripts/BeatSaberSong.cs | 76 ++++++++++++++++++- Assets/__Scripts/BeatSaberSongContainer.cs | 29 ++++--- .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 61 ++++++++++++++- .../Extensions/BeatSaberSongExtensions.cs | 7 +- Assets/__Scripts/UI/SongInfoEditUI.cs | 2 +- .../UI/SongSelectMenu/CreateNewSong.cs | 2 +- .../__Scripts/UI/SongSelectMenu/SongList.cs | 39 +++++----- .../UI/SongSelectMenu/SongListItem.cs | 41 +++++----- .../UI/SongSelectMenu/TempLoaderController.cs | 6 +- 9 files changed, 201 insertions(+), 62 deletions(-) diff --git a/Assets/__Scripts/BeatSaberSong.cs b/Assets/__Scripts/BeatSaberSong.cs index 16b287310..edd559800 100644 --- a/Assets/__Scripts/BeatSaberSong.cs +++ b/Assets/__Scripts/BeatSaberSong.cs @@ -12,7 +12,7 @@ using UnityEngine; using UnityEngine.Serialization; -[Serializable] +[Serializable][Obsolete] public class BeatSaberSong { public static readonly Color DefaultLeftColor = Color.red; @@ -328,6 +328,56 @@ public static JSONNode CleanObject(JSONNode obj) return obj; } + public static BaseInfo GetInfoFromFolder(string directory) + { + try + { + var mainNode = GetNodeFromFile(directory + "/Info.dat"); + if (mainNode == null) + { + //Virgin "info.dat" VS chad "Info.dat" + mainNode = GetNodeFromFile(directory + "/info.dat"); + if (mainNode == null) return null; + File.Move(directory + "/info.dat", directory + "/Info.dat"); + } + + var version = -1; + + if (mainNode.HasKey("_version")) + { + version = 2; + } + else if (mainNode.HasKey("version")) + { + version = mainNode["version"].Value[0] == '4' ? 4 : -1; + } + + var info = version switch + { + 2 => V2Info.GetFromJson(mainNode), + 4 => V4Info.GetFromJson(mainNode), + _ => null + }; + + if (info != null) + { + info.Directory = directory; + } + else + { + Debug.LogWarning($"Could not parse Info.dat in {directory}"); + } + + return info; + } + catch (Exception e) + { + Debug.LogError(e); + return null; + } + } + + [Obsolete("Use GetInfoFromFolder", true)] public static BeatSaberSong GetSongFromFolder(string directory) { try @@ -492,6 +542,26 @@ public static BeatSaberSong GetSongFromFolder(string directory) } } + public static BaseDifficulty GetMapFromInfoFiles(BaseInfo info, InfoDifficulty difficultyData) + { + if (!System.IO.Directory.Exists(info.Directory)) + { + Debug.LogWarning("Failed to get difficulty json file."); + return null; + } + var fullPath = Path.Combine(info.Directory, difficultyData.BeatmapFileName); + + var mainNode = GetNodeFromFile(fullPath); + if (mainNode == null) + { + Debug.LogWarning("Failed to get difficulty json file " + fullPath); + return null; + } + + return BeatmapFactory.GetDifficultyFromJson(mainNode, fullPath); + + } + public BaseDifficulty GetMapFromDifficultyBeatmap(DifficultyBeatmap data) { if (!System.IO.Directory.Exists(Directory)) @@ -530,7 +600,7 @@ private static JSONNode GetNodeFromFile(string file) return null; } - [Serializable] + [Serializable][Obsolete] public class DifficultyBeatmap { [FormerlySerializedAs("difficulty")] public string Difficulty = "Easy"; @@ -619,7 +689,7 @@ public JSONNode GetOrCreateCustomData() } } - [Serializable] + [Serializable][Obsolete] public class DifficultyBeatmapSet { [FormerlySerializedAs("beatmapCharacteristicName")] public string BeatmapCharacteristicName = "Standard"; diff --git a/Assets/__Scripts/BeatSaberSongContainer.cs b/Assets/__Scripts/BeatSaberSongContainer.cs index 1508e704a..9a3af3d93 100644 --- a/Assets/__Scripts/BeatSaberSongContainer.cs +++ b/Assets/__Scripts/BeatSaberSongContainer.cs @@ -6,12 +6,17 @@ using UnityEngine.Localization.Settings; using UnityEngine.Serialization; using Beatmap.Base; +using Beatmap.Info; public class BeatSaberSongContainer : MonoBehaviour { + [Obsolete("",false)] [FormerlySerializedAs("song")] public BeatSaberSong Song; [FormerlySerializedAs("difficultyData")] public BeatSaberSong.DifficultyBeatmap DifficultyData; [FormerlySerializedAs("loadedSong")] public AudioClip LoadedSong; + + public BaseInfo Info; + public InfoDifficulty MapDifficultyInfo; [FormerlySerializedAs("map")] public BaseDifficulty Map; [NonSerialized] public MultiClientNetListener? MultiMapperConnection; @@ -29,6 +34,12 @@ private void Awake() Instance = this; DontDestroyOnLoad(gameObject); } + + public void SelectSongForEditing(BaseInfo info) + { + Info = info; + SceneTransitionManager.Instance.LoadScene("02_SongEditMenu"); + } public void SelectSongForEditing(BeatSaberSong song) { @@ -70,22 +81,22 @@ private IEnumerator DownloadAndLaunchMap() archive.ExtractToDirectory(directory); // Try and get a BeatSaberSong out of what we've downloaded. - var song = BeatSaberSong.GetSongFromFolder(directory); - if (song != null) + var mapInfo = BeatSaberSong.GetInfoFromFolder(directory); + if (mapInfo != null) { PersistentUI.Instance.LevelLoadSliderLabel.text = LocalizationSettings.StringDatabase.GetLocalizedString("MultiMapping", "multi.session.loading"); - Song = song; + Info = mapInfo; // Find characteristic and difficulty - DifficultyData = Song.DifficultyBeatmapSets - .Find(set => set.BeatmapCharacteristicName == MultiMapperConnection.MapData.Characteristic) - .DifficultyBeatmaps.Find(diff => diff.Difficulty == MultiMapperConnection.MapData.Diff); - - Map = song.GetMapFromDifficultyBeatmap(DifficultyData); + MapDifficultyInfo = mapInfo.DifficultySets + .Find(set => set.Characteristic == MultiMapperConnection.MapData.Characteristic) + .Difficulties.Find(diff => diff.Difficulty == MultiMapperConnection.MapData.Diff); + + Map = BeatSaberSong.GetMapFromInfoFiles(mapInfo, MapDifficultyInfo); Settings.Instance.MapVersion = Map.Version[0] == '3' ? 3 : 2; - yield return Song.LoadAudio((clip) => + yield return BeatSaberSongExtensions.LoadAudio(mapInfo, (clip) => { LoadedSong = clip; LoadedSongSamples = clip.samples; diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index a94c976f4..1c5af33c8 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using Beatmap.Base.Customs; using SimpleJSON; using UnityEngine; @@ -10,13 +13,65 @@ namespace Beatmap.Info public class BaseInfo { // Editor Properties - public string Directory { get; set; } - public bool IsFavourite; + private string directory; + + public string Directory + { + get => directory; + set + { + LastWriteTime = System.IO.Directory.GetLastWriteTime(value); + isFavourite = File.Exists(Path.Combine(value, ".favourite")); + directory = value; + } + } + + public DateTime LastWriteTime { get; private set; } + + // These values piggy back off of Application.productName and Application.version here. + // It's so that anyone maintaining a ChroMapper fork, but wants its identity to be separate, can easily just change + // product name and the version from Project Settings, and have it automatically apply to the metadata. + // But it's in their own fields because Unity cries like a little blyat when you access them directly from another thread. + private static string editorName; + private static string editorVersion; + + private bool isFavourite; + + public bool IsFavourite + { + get => isFavourite; + set + { + var path = Path.Combine(Directory, ".favourite"); + lock (this) + { + if (value) + { + File.Create(path).Dispose(); + File.SetAttributes(path, FileAttributes.Hidden); + } + else + { + File.Delete(path); + } + } + + isFavourite = value; + } + } + + public BaseInfo() + { + if (string.IsNullOrEmpty(editorName)) editorName = Application.productName; + if (string.IsNullOrEmpty(editorVersion)) editorVersion = Application.version; + } // Vanilla Properties public string Version { get; set; } = "4.0.0"; public string SongName { get; set; } = "New Song"; + public string CleanSongName => Path.GetInvalidFileNameChars() + .Aggregate(SongName, (res, el) => res.Replace(el.ToString(), string.Empty)); public string SongSubName { get; set; } = ""; public string SongAuthorName { get; set; } = ""; diff --git a/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs b/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs index faae860bd..010e048ad 100644 --- a/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs +++ b/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Beatmap.Info; using JetBrains.Annotations; using UnityEngine; using UnityEngine.Networking; @@ -20,11 +21,11 @@ public static class BeatSaberSongExtensions /// /// Should we load the song the user has updated in the UI or from the saved song data /// Coroutine IEnumerator - public static IEnumerator LoadAudio(this BeatSaberSong song, Action onClipLoaded, float songTimeOffset = 0, string overrideLocalPath = null) + public static IEnumerator LoadAudio(BaseInfo mapInfo, Action onClipLoaded, float songTimeOffset = 0, string overrideLocalPath = null) { - if (!Directory.Exists(song.Directory)) yield break; + if (!Directory.Exists(mapInfo.Directory)) yield break; - var fullPath = Path.Combine(song.Directory, overrideLocalPath ?? song.SongFilename); + var fullPath = Path.Combine(mapInfo.Directory, overrideLocalPath ?? mapInfo.SongFilename); // Commented out since Song Time Offset changes need to reload the song, even if its the same file //if (fullPath == loadedSong) diff --git a/Assets/__Scripts/UI/SongInfoEditUI.cs b/Assets/__Scripts/UI/SongInfoEditUI.cs index 119d32607..f0c9e26f8 100644 --- a/Assets/__Scripts/UI/SongInfoEditUI.cs +++ b/Assets/__Scripts/UI/SongInfoEditUI.cs @@ -297,7 +297,7 @@ private IEnumerator LoadAudio(bool useTemp = true, bool applySongTimeOffset = fa Debug.Log("Loading audio"); if (File.Exists(fullPath)) { - yield return Song.LoadAudio((clip) => + yield return BeatSaberSongExtensions.LoadAudio(null,(clip) => { previewAudio.clip = clip; BeatSaberSongContainer.Instance.LoadedSong = clip; diff --git a/Assets/__Scripts/UI/SongSelectMenu/CreateNewSong.cs b/Assets/__Scripts/UI/SongSelectMenu/CreateNewSong.cs index b29c4f517..e23d76712 100644 --- a/Assets/__Scripts/UI/SongSelectMenu/CreateNewSong.cs +++ b/Assets/__Scripts/UI/SongSelectMenu/CreateNewSong.cs @@ -23,7 +23,7 @@ private void HandleNewSongName(string res) return; } - if (list.Songs.Any(x => Path.GetFullPath(x.Directory).Equals( + if (list.SongInfos.Any(x => Path.GetFullPath(x.Directory).Equals( Path.GetFullPath(Path.Combine( list.SelectedFolderPath, song.CleanSongName)), diff --git a/Assets/__Scripts/UI/SongSelectMenu/SongList.cs b/Assets/__Scripts/UI/SongSelectMenu/SongList.cs index e74637e19..74f9d5ac0 100644 --- a/Assets/__Scripts/UI/SongSelectMenu/SongList.cs +++ b/Assets/__Scripts/UI/SongSelectMenu/SongList.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Xml.Linq; +using Beatmap.Info; using TMPro; using UnityEngine; using UnityEngine.Localization.Components; @@ -16,18 +17,18 @@ public enum SongSortType Name, Modified, Artist } - private static readonly IComparer sortName = + private static readonly IComparer sortName = new WithFavouriteComparer((a, b) => string.Compare(a.SongName, b.SongName, StringComparison.InvariantCultureIgnoreCase)); - private static readonly IComparer sortModified = + private static readonly IComparer sortModified = new WithFavouriteComparer((a, b) => b.LastWriteTime.CompareTo(a.LastWriteTime)); - private static readonly IComparer sortArtist = + private static readonly IComparer sortArtist = new WithFavouriteComparer((a, b) => string.Compare(a.SongAuthorName, b.SongAuthorName, StringComparison.InvariantCultureIgnoreCase)); - public SortedSet Songs = new(sortName); + public SortedSet SongInfos = new(sortName); public bool FilteredBySearch; [SerializeField] private TMP_InputField searchField; @@ -42,7 +43,7 @@ public enum SongSortType [SerializeField] private RecyclingListView newList; private SongSortType currentSort = SongSortType.Name; - private List filteredSongs = new(); + private List filteredSongs = new(); public string SelectedFolderPath => songFolderPaths[selectedFolder]; private static int selectedFolder; @@ -133,10 +134,10 @@ private void InitFolderObject(string tabName, string folderPath) public event Action SortTypeChanged; - private void SwitchSort(IComparer newSort, Sprite sprite) + private void SwitchSort(IComparer newSort, Sprite sprite) { sortImage.sprite = sprite; - Songs = new SortedSet(Songs, newSort); + SongInfos = new SortedSet(SongInfos, newSort); UpdateSongList(); } @@ -195,7 +196,7 @@ public IEnumerator RefreshSongList() var directories = new DirectoryInfo(songFolderPaths[selectedFolder]) .GetDirectories() .Where(dir => !dir.Attributes.HasFlag(FileAttributes.Hidden)); - Songs.Clear(); + SongInfos.Clear(); newList.Clear(); var iterBeginTime = Time.realtimeSinceStartup; foreach (var dir in directories) @@ -207,8 +208,8 @@ public IEnumerator RefreshSongList() iterBeginTime = Time.realtimeSinceStartup; } - var song = BeatSaberSong.GetSongFromFolder(dir.FullName); - if (song == null) + var mapInfo = BeatSaberSong.GetInfoFromFolder(dir.FullName); + if (mapInfo == null) Debug.LogWarning($"No song at location {dir} exists! Is it in a subfolder?"); /* * Subfolder loading support has been removed for the following: @@ -224,7 +225,7 @@ public IEnumerator RefreshSongList() if (song != null) songs.Add(song); }*/ else - Songs.Add(song); + SongInfos.Add(mapInfo); } UpdateSongList(); @@ -239,14 +240,14 @@ public void UpdateSongList() } else { - filteredSongs = Songs.ToList(); + filteredSongs = SongInfos.ToList(); ReloadListItems(); } } public void FilterBySearch() { - filteredSongs = Songs.Where(x => + filteredSongs = SongInfos.Where(x => x.SongName.IndexOf(searchField.text, StringComparison.InvariantCultureIgnoreCase) >= 0 || x.SongAuthorName.IndexOf(searchField.text, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList(); ReloadListItems(); @@ -260,11 +261,11 @@ private void ReloadListItems() newList.Refresh(); } - public void RemoveSong(BeatSaberSong song) => Songs.Remove(song); + public void RemoveSong(BaseInfo mapInfo) => SongInfos.Remove(mapInfo); - public void AddSong(BeatSaberSong song) + public void AddSong(BaseInfo song) { - Songs.Add(song); + SongInfos.Add(song); UpdateSongList(); } @@ -281,11 +282,11 @@ public virtual int Compare(T x, T y) } } - private class WithFavouriteComparer : FuncComparer + private class WithFavouriteComparer : FuncComparer { - public WithFavouriteComparer(Comparison comparison) : base(comparison) { } + public WithFavouriteComparer(Comparison comparison) : base(comparison) { } - public override int Compare(BeatSaberSong a, BeatSaberSong b) => + public override int Compare(BaseInfo a, BaseInfo b) => a?.IsFavourite != b?.IsFavourite ? a?.IsFavourite == true ? -1 : 1 : base.Compare(a, b); } } diff --git a/Assets/__Scripts/UI/SongSelectMenu/SongListItem.cs b/Assets/__Scripts/UI/SongSelectMenu/SongListItem.cs index 744a7c67f..5d16bf743 100644 --- a/Assets/__Scripts/UI/SongSelectMenu/SongListItem.cs +++ b/Assets/__Scripts/UI/SongSelectMenu/SongListItem.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using Beatmap.Info; using SimpleJSON; using TMPro; using UnityEngine; @@ -44,7 +45,7 @@ public class SongListItem : RecyclingListViewItem, IPointerEnterHandler, IPointe private bool ignoreToggle; private string previousSearch = ""; - private BeatSaberSong song; + private BaseInfo mapInfo; private SongList songList; @@ -88,8 +89,8 @@ private static void InitCache() public void OnPointerClick(PointerEventData eventData) { - if (BeatSaberSongContainer.Instance != null && song != null) - BeatSaberSongContainer.Instance.SelectSongForEditing(song); + if (BeatSaberSongContainer.Instance != null && mapInfo != null) + BeatSaberSongContainer.Instance.SelectSongForEditing(mapInfo); } public void OnPointerEnter(PointerEventData eventData) @@ -114,28 +115,28 @@ private string HighlightSubstring(string s, string search) : stripped; } - public void AssignSong(BeatSaberSong song, string searchFieldText) + public void AssignSong(BaseInfo mapInfo, string searchFieldText) { - if (this.song == song && previousSearch == searchFieldText) return; + if (this.mapInfo == mapInfo && previousSearch == searchFieldText) return; StopCoroutine(nameof(LoadImage)); StopCoroutine(nameof(LoadDuration)); previousSearch = searchFieldText; - this.song = song; - var songName = HighlightSubstring(song.SongName, searchFieldText); - var artistName = HighlightSubstring(song.SongAuthorName, searchFieldText); + this.mapInfo = mapInfo; + var songName = HighlightSubstring(mapInfo.SongName, searchFieldText); + var artistName = HighlightSubstring(mapInfo.SongAuthorName, searchFieldText); - title.text = $"{songName} {song.SongSubName.StripTMPTags()}"; + title.text = $"{songName} {mapInfo.SongSubName.StripTMPTags()}"; artist.text = artistName; - folder.text = song.Directory; + folder.text = mapInfo.Directory; duration.text = "-:--"; - bpm.text = $"{song.BeatsPerMinute:N0}"; + bpm.text = $"{mapInfo.BeatsPerMinute:N0}"; ignoreToggle = true; - favouriteToggle.isOn = this.song.IsFavourite; - favouritePreviewImage.gameObject.SetActive(this.song.IsFavourite); + favouriteToggle.isOn = this.mapInfo.IsFavourite; + favouritePreviewImage.gameObject.SetActive(this.mapInfo.IsFavourite); ignoreToggle = false; StartCoroutine(nameof(LoadImage)); @@ -144,7 +145,7 @@ public void AssignSong(BeatSaberSong song, string searchFieldText) private IEnumerator LoadImage() { - var fullPath = Path.Combine(song.Directory, song.CoverImageFilename); + var fullPath = Path.Combine(mapInfo.Directory, mapInfo.CoverImageFilename); if (cache.TryGetValue(fullPath, out var spriteRef) && spriteRef.TryGetTarget(out var existingSprite)) { @@ -231,8 +232,8 @@ private void SetDuration(float length) private IEnumerator LoadDuration() { - var cacheKey = Path.GetFullPath(song.Directory); - var fullPath = Path.Combine(song.Directory, song.SongFilename); + var cacheKey = Path.GetFullPath(mapInfo.Directory); + var fullPath = Path.Combine(mapInfo.Directory, mapInfo.SongFilename); if (!File.Exists(fullPath)) yield break; @@ -251,7 +252,7 @@ private IEnumerator LoadDuration() yield break; } - yield return song.LoadAudio((clip) => SetDuration(cacheKey, clip.length), 0, null); + yield return BeatSaberSongExtensions.LoadAudio(mapInfo, (clip) => SetDuration(cacheKey, clip.length), 0, null); } private static bool FindBytes(Stream fs, BinaryReader br, byte[] bytes, int searchLength) @@ -338,9 +339,9 @@ public void OnFavourite(bool isFavourite) { if (ignoreToggle) return; - songList.RemoveSong(song); - song.IsFavourite = isFavourite; + songList.RemoveSong(mapInfo); + mapInfo.IsFavourite = isFavourite; favouritePreviewImage.gameObject.SetActive(isFavourite); - songList.AddSong(song); + songList.AddSong(mapInfo); } } diff --git a/Assets/__Scripts/UI/SongSelectMenu/TempLoaderController.cs b/Assets/__Scripts/UI/SongSelectMenu/TempLoaderController.cs index 0fc8bfeed..dd488cf0b 100644 --- a/Assets/__Scripts/UI/SongSelectMenu/TempLoaderController.cs +++ b/Assets/__Scripts/UI/SongSelectMenu/TempLoaderController.cs @@ -133,11 +133,11 @@ private IEnumerator GetBeatmapFromLocation(Uri uri) downloadHandler.Dispose(); // Try and get a BeatSaberSong out of what we've downloaded. - var song = BeatSaberSong.GetSongFromFolder(directory); - if (song != null) + var mapInfo = BeatSaberSong.GetInfoFromFolder(directory); + if (mapInfo != null) { PersistentUI.Instance.LevelLoadSliderLabel.text = "Loading song..."; - BeatSaberSongContainer.Instance.Song = song; + BeatSaberSongContainer.Instance.Info = mapInfo; } else { From a8f79e5441c709591c59f5913663ba82271b506d Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Mon, 7 Oct 2024 00:16:17 +1000 Subject: [PATCH 11/49] wip: ok the left side of the song edit works --- .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 24 ++++ .../Extensions/BeatSaberSongExtensions.cs | 26 ++-- .../__Scripts/MapEditor/AutoSaveController.cs | 2 +- Assets/__Scripts/MapEditor/MapExporter.cs | 21 +-- .../Multi Mapping/MultiServerNetListener.cs | 6 +- .../UI/Contributors/ContributorListItem.cs | 4 +- .../UI/Contributors/ContributorsController.cs | 2 +- Assets/__Scripts/UI/ImageList/ImageList.cs | 13 +- Assets/__Scripts/UI/PersistentUI.cs | 5 +- Assets/__Scripts/UI/SongInfoEditUI.cs | 121 +++++++++--------- 10 files changed, 129 insertions(+), 95 deletions(-) diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index 1c5af33c8..c162a928f 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -110,6 +110,30 @@ public BaseInfo() public JSONNode CustomData = new JSONObject(); public List CustomContributors = new(); + + + public bool Save() + { + // Create map folder + if (!System.IO.Directory.Exists(Directory)) System.IO.Directory.CreateDirectory(Directory); + + var outputJson = Version[0] switch + { + '2' => V2Info.GetOutputJson(this), + '4' => V4Info.GetOutputJson(this), + _ => null + }; + + if (outputJson == null) + return false; + + // Write difficulty file + File.WriteAllText(Path.Combine(Directory, "Info.dat"), Settings.Instance.FormatJson + ? outputJson.ToString(2) + : outputJson.ToString()); + + return true; + } } public class InfoDifficultySet diff --git a/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs b/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs index 010e048ad..f285c6422 100644 --- a/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs +++ b/Assets/__Scripts/Extensions/BeatSaberSongExtensions.cs @@ -99,15 +99,15 @@ public static IEnumerator LoadAudio(BaseInfo mapInfo, Action onClipLo } [CanBeNull] - public static Dictionary GetFilesForArchiving(this BeatSaberSong song) + public static Dictionary GetFilesForArchiving(BaseInfo info) { // path:entry_name var exportedFiles = new Dictionary(); var infoFileLocation = ""; - if (Directory.Exists(song.Directory)) + if (Directory.Exists(info.Directory)) { - infoFileLocation = Path.Combine(song.Directory, "Info.dat"); + infoFileLocation = Path.Combine(info.Directory, "Info.dat"); } if (!File.Exists(infoFileLocation)) @@ -118,24 +118,26 @@ public static Dictionary GetFilesForArchiving(this BeatSaberSong } exportedFiles.Add(infoFileLocation, "Info.dat"); - TryAddToFileDictionary(exportedFiles, song.Directory, song.CoverImageFilename); - TryAddToFileDictionary(exportedFiles, song.Directory, song.SongFilename); - TryAddToFileDictionary(exportedFiles, song.Directory, "cinema-video.json"); - TryAddToFileDictionary(exportedFiles, song.Directory, "BPMInfo.dat"); + TryAddToFileDictionary(exportedFiles, info.Directory, info.CoverImageFilename); + TryAddToFileDictionary(exportedFiles, info.Directory, info.SongFilename); + TryAddToFileDictionary(exportedFiles, info.Directory, info.SongPreviewFilename); + TryAddToFileDictionary(exportedFiles, info.Directory, "cinema-video.json"); + TryAddToFileDictionary(exportedFiles, info.Directory, info.Version[0] == '4' ? info.AudioDataFilename: "BPMInfo.dat"); - foreach (var contributor in song.Contributors.DistinctBy(it => it.LocalImageLocation)) + foreach (var contributor in info.CustomContributors.DistinctBy(it => it.LocalImageLocation)) { - var imageLocation = Path.Combine(song.Directory!, contributor.LocalImageLocation); - if (contributor.LocalImageLocation != song.CoverImageFilename && + var imageLocation = Path.Combine(info.Directory!, contributor.LocalImageLocation); + if (contributor.LocalImageLocation != info.CoverImageFilename && File.Exists(imageLocation) && !File.GetAttributes(imageLocation).HasFlag(FileAttributes.Directory)) { exportedFiles.Add(imageLocation, contributor.LocalImageLocation); } } - foreach (var map in song.DifficultyBeatmapSets.SelectMany(set => set.DifficultyBeatmaps)) + foreach (var map in info.DifficultySets.SelectMany(set => set.Difficulties)) { - TryAddToFileDictionary(exportedFiles, song.Directory, map.BeatmapFilename); + TryAddToFileDictionary(exportedFiles, info.Directory, map.BeatmapFileName); + TryAddToFileDictionary(exportedFiles, info.Directory, map.LightshowFileName); } // Don't package to zip if any paths are absolute or rooted diff --git a/Assets/__Scripts/MapEditor/AutoSaveController.cs b/Assets/__Scripts/MapEditor/AutoSaveController.cs index 0fecf5a9d..b51724d5d 100644 --- a/Assets/__Scripts/MapEditor/AutoSaveController.cs +++ b/Assets/__Scripts/MapEditor/AutoSaveController.cs @@ -38,7 +38,7 @@ public class AutoSaveController : MonoBehaviour, CMInput.ISavingActions private string saveWarningMessage; - private static MapExporter Exporter => new MapExporter(BeatSaberSongContainer.Instance.Song); + private static MapExporter Exporter => new(BeatSaberSongContainer.Instance.Info); public enum SaveType { diff --git a/Assets/__Scripts/MapEditor/MapExporter.cs b/Assets/__Scripts/MapEditor/MapExporter.cs index 8e2062670..f3cebf8c7 100644 --- a/Assets/__Scripts/MapEditor/MapExporter.cs +++ b/Assets/__Scripts/MapEditor/MapExporter.cs @@ -4,6 +4,7 @@ using System.IO.Compression; using System.Linq; using System.Threading.Tasks; +using Beatmap.Info; using QuestDumper; using UnityEngine.Localization.Settings; using Debug = UnityEngine.Debug; @@ -26,9 +27,9 @@ public struct MapExporter "sdcard/ModData/com.beatgames.beatsaber/Mods/SongLoader/CustomWIPLevels"; - private readonly BeatSaberSong song; + private readonly BaseInfo info; - public MapExporter(BeatSaberSong song) => this.song = song; + public MapExporter(BaseInfo info) => this.info = info; /// /// Exports the files to the Quest using adb @@ -68,8 +69,8 @@ public async Task ExportToQuest() dialog.Open(); // We should always be exporting to WIP Levels. CustomLevels are for downloaded BeatSaver songs. - var songExportPath = Path.Combine(QUEST_CUSTOM_SONGS_WIP_LOCATION, song.CleanSongName).Replace("\\", @"/"); - var exportedFiles = song.GetFilesForArchiving(); + var songExportPath = Path.Combine(QUEST_CUSTOM_SONGS_WIP_LOCATION, info.CleanSongName).Replace("\\", @"/"); + var exportedFiles = BeatSaberSongExtensions.GetFilesForArchiving(info); if (exportedFiles == null) return; @@ -115,13 +116,13 @@ public bool PackageZip() { var infoFileLocation = ""; var zipPath = ""; - if (Directory.Exists(song.Directory)) + if (Directory.Exists(info.Directory)) { - zipPath = Path.Combine(song.Directory, song.CleanSongName + ".zip"); + zipPath = Path.Combine(info.Directory, info.CleanSongName + ".zip"); // Mac doesn't seem to like overwriting existing zips, so delete the old one first File.Delete(zipPath); - infoFileLocation = Path.Combine(song.Directory, "Info.dat"); + infoFileLocation = Path.Combine(info.Directory, "Info.dat"); } if (!File.Exists(infoFileLocation)) @@ -132,7 +133,7 @@ public bool PackageZip() return false; } - var exportedFiles = song.GetFilesForArchiving(); + var exportedFiles = BeatSaberSongExtensions.GetFilesForArchiving(info); if (exportedFiles == null) { return false; @@ -154,14 +155,14 @@ public bool PackageZip() /// public void OpenSelectedMapInFileBrowser() { - if (!Directory.Exists(song.Directory)) + if (!Directory.Exists(info.Directory)) { PersistentUI.Instance.ShowDialogBox("SongEditMenu", "explorer.warning", null, PersistentUI.DialogBoxPresetType.Ok); return; } - var path = song.Directory; + var path = info.Directory; OSTools.OpenFileBrowser(path); } } diff --git a/Assets/__Scripts/MapEditor/Multi Mapping/MultiServerNetListener.cs b/Assets/__Scripts/MapEditor/Multi Mapping/MultiServerNetListener.cs index 84471c3e1..546bf4b94 100644 --- a/Assets/__Scripts/MapEditor/Multi Mapping/MultiServerNetListener.cs +++ b/Assets/__Scripts/MapEditor/Multi Mapping/MultiServerNetListener.cs @@ -189,14 +189,14 @@ internal static IEnumerator SaveAndSendMapToPeer(MultiNetListener listener, Auto // Zip the song in its current state, then send to the player. // I'm aware that there's a little bit of time in between saving and zipping where changes made *arent* sent to the client, // but I'm hoping its small enough to not be a big worry. - var song = BeatSaberSongContainer.Instance.Song; + var mapInfo = BeatSaberSongContainer.Instance.Info; var diff = BeatSaberSongContainer.Instance.DifficultyData; var characteristic = BeatSaberSongContainer.Instance.DifficultyData.ParentBeatmapSet; - var zipPath = Path.Combine(song.Directory, song.CleanSongName + ".zip"); + var zipPath = Path.Combine(mapInfo.Directory, mapInfo.CleanSongName + ".zip"); File.Delete(zipPath); - var exportedFiles = song.GetFilesForArchiving(); + var exportedFiles = BeatSaberSongExtensions.GetFilesForArchiving(mapInfo); using (var archive = ZipFile.Open(zipPath, ZipArchiveMode.Create)) { diff --git a/Assets/__Scripts/UI/Contributors/ContributorListItem.cs b/Assets/__Scripts/UI/Contributors/ContributorListItem.cs index 6103b2c39..542f4ac3a 100644 --- a/Assets/__Scripts/UI/Contributors/ContributorListItem.cs +++ b/Assets/__Scripts/UI/Contributors/ContributorListItem.cs @@ -67,7 +67,7 @@ public void BrowseForImage() new ExtensionFilter("Image Files", "png", "jpg", "jpeg"), new ExtensionFilter("All Files", "*") }; - var songDir = BeatSaberSongContainer.Instance.Song.Directory; + var songDir = BeatSaberSongContainer.Instance.Info.Directory; CMInputCallbackInstaller.DisableActionMaps(typeof(ContributorListItem), new[] { typeof(CMInput.IMenusExtendedActions) }); var paths = StandaloneFileBrowser.OpenFilePanel("Open File", songDir, extensions, false); @@ -136,7 +136,7 @@ private void SetImageLocation(string path) private IEnumerator LoadImage() { - var location = Path.Combine(BeatSaberSongContainer.Instance.Song.Directory, imagePath); + var location = Path.Combine(BeatSaberSongContainer.Instance.Info.Directory, imagePath); var uriPath = Application.platform is RuntimePlatform.WindowsPlayer or RuntimePlatform.WindowsEditor ? Uri.EscapeDataString(location) diff --git a/Assets/__Scripts/UI/Contributors/ContributorsController.cs b/Assets/__Scripts/UI/Contributors/ContributorsController.cs index bc22fb57f..280efe234 100644 --- a/Assets/__Scripts/UI/Contributors/ContributorsController.cs +++ b/Assets/__Scripts/UI/Contributors/ContributorsController.cs @@ -39,7 +39,7 @@ public void UndoChanges() { HandleRemoveAllContributors(0); - foreach (var item in BeatSaberSongContainer.Instance.Song.Contributors) + foreach (var item in BeatSaberSongContainer.Instance.Info.CustomContributors) { var listItem = Instantiate(listItemPrefab, listContainer.transform).GetComponent(); listItem.Setup(item, this); diff --git a/Assets/__Scripts/UI/ImageList/ImageList.cs b/Assets/__Scripts/UI/ImageList/ImageList.cs index 55ec5388b..e86d5513b 100644 --- a/Assets/__Scripts/UI/ImageList/ImageList.cs +++ b/Assets/__Scripts/UI/ImageList/ImageList.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using Beatmap.Info; +using UnityEngine; using UnityEngine.Serialization; [CreateAssetMenu(fileName = "ImageList", menuName = "ImageList")] @@ -22,14 +23,14 @@ public class ImageList : ScriptableObject public Sprite GetRandomSprite() => Settings.Instance.DarkTheme ? DarkSprite : Sprites[Random.Range(0, Sprites.Length)]; - public Sprite GetBgSprite(BeatSaberSong song) + public Sprite GetBgSprite(BaseInfo mapInfo) { if (Settings.Instance.DarkTheme) return DarkSprite; - if (song.CustomData != null) + if (mapInfo.CustomData != null) { - if (song.CustomData && !string.IsNullOrEmpty(song.CustomData["_customEnvironment"])) + if (mapInfo.CustomData && !string.IsNullOrEmpty(mapInfo.CustomData["_customEnvironment"])) { - switch (song.CustomData["_customEnvironment"].Value) + switch (mapInfo.CustomData["_customEnvironment"].Value) { case "Vapor Frame": return VaporFramePlatform; case "Big Mirror V2": return BigMirrorV2Platform; @@ -37,7 +38,7 @@ public Sprite GetBgSprite(BeatSaberSong song) } } - return song.EnvironmentName switch + return mapInfo.EnvironmentName switch { "DefaultEnvironment" => DefaultPlatform, "TriangleEnvironment" => TrianglePlatform, diff --git a/Assets/__Scripts/UI/PersistentUI.cs b/Assets/__Scripts/UI/PersistentUI.cs index 50625b00d..d94febda3 100644 --- a/Assets/__Scripts/UI/PersistentUI.cs +++ b/Assets/__Scripts/UI/PersistentUI.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Beatmap.Info; using TMPro; using UnityEditor; using UnityEngine; @@ -269,11 +270,11 @@ public IEnumerator LoadMessage() #region loading - public static void UpdateBackground(BeatSaberSong song) + public static void UpdateBackground(BaseInfo info) { if (Instance.editorLoadingBackground.gameObject.activeSelf == false) Instance.editorLoadingBackground.gameObject.SetActive(true); - Instance.editorLoadingBackground.sprite = Instance.editorImageList.GetBgSprite(song); + Instance.editorLoadingBackground.sprite = Instance.editorImageList.GetBgSprite(info); } public Coroutine FadeInLoadingScreen() diff --git a/Assets/__Scripts/UI/SongInfoEditUI.cs b/Assets/__Scripts/UI/SongInfoEditUI.cs index f0c9e26f8..e2c2bf193 100644 --- a/Assets/__Scripts/UI/SongInfoEditUI.cs +++ b/Assets/__Scripts/UI/SongInfoEditUI.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Linq; +using Beatmap.Info; using QuestDumper; using SimpleJSON; using TMPro; @@ -82,11 +83,15 @@ public class SongInfoEditUI : MenuBase private Coroutine reloadSongDataCoroutine; public Action TempSongLoadedEvent; + [Obsolete("pain",true)] private BeatSaberSong Song => BeatSaberSongContainer.Instance.Song; + + private BaseInfo Info => BeatSaberSongContainer.Instance.Info; + private GameObject ContributorWrapper => contributorController.transform.parent.gameObject; [SerializeField] private GameObject questExportButton; - private MapExporter exporter => new(Song); + private MapExporter exporter => new(Info); private void Start() { @@ -142,19 +147,19 @@ public override void OnLeaveMenu(CallbackContext context) /// public void SaveToSong() { - Song.SongName = nameField.text; - Song.SongSubName = subNameField.text; - Song.SongAuthorName = songAuthorField.text; - Song.LevelAuthorName = authorField.text; - Song.CoverImageFilename = coverImageField.text; - Song.SongFilename = audioPath.text; - - Song.BeatsPerMinute = GetTextValue(bpmField); - Song.PreviewStartTime = GetTextValue(prevStartField); - Song.PreviewDuration = GetTextValue(prevDurField); - Song.SongTimeOffset = GetTextValue(offset); - - if (Song.SongTimeOffset != 0) + Info.SongName = nameField.text; + Info.SongSubName = subNameField.text; + Info.SongAuthorName = songAuthorField.text; + Info.LevelAuthorName = authorField.text; + Info.CoverImageFilename = coverImageField.text; + Info.SongFilename = audioPath.text; + + Info.BeatsPerMinute = GetTextValue(bpmField); + Info.PreviewStartTime = GetTextValue(prevStartField); + Info.PreviewDuration = GetTextValue(prevDurField); + Info.SongTimeOffset = GetTextValue(offset); + + if (Info.SongTimeOffset != 0) { PersistentUI.Instance.ShowDialogBox("SongEditMenu", "songtimeoffset.warning", null, PersistentUI.DialogBoxPresetType.Ok); @@ -166,35 +171,35 @@ public void SaveToSong() if (TryGetEnvironmentNameFromID(environmentDropdown.value, out var environmentName)) { - Song.EnvironmentName = environmentName; + Info.EnvironmentName = environmentName; } - if (Song.CustomData == null) Song.CustomData = new JSONObject(); + if (Info.CustomData == null) Info.CustomData = new JSONObject(); if (customPlatformsDropdown.value > 0) { - Song.CustomData["_customEnvironment"] = customPlatformsDropdown.captionText.text; + Info.CustomData["_customEnvironment"] = customPlatformsDropdown.captionText.text; if (CustomPlatformsLoader.Instance.GetAllEnvironments() .TryGetValue(customPlatformsDropdown.captionText.text, out var info)) { - Song.CustomData["_customEnvironmentHash"] = info.Md5Hash; + Info.CustomData["_customEnvironmentHash"] = info.Md5Hash; } } else { - Song.CustomData.Remove("_customEnvironment"); - Song.CustomData.Remove("_customEnvironmentHash"); + Info.CustomData.Remove("_customEnvironment"); + Info.CustomData.Remove("_customEnvironmentHash"); } contributorController.Commit(); - Song.Contributors = contributorController.Contributors; + Info.CustomContributors = contributorController.Contributors; - Song.SaveSong(); + Info.Save(); // Update duration cache (This needs to be beneath SaveSong so that the directory is guaranteed to be created) // also dont forget to null check please thanks if (previewAudio.clip != null) - SongListItem.SetDuration(this, Path.GetFullPath(Song.Directory), previewAudio.clip.length); + SongListItem.SetDuration(this, Path.GetFullPath(Info.Directory), previewAudio.clip.length); // Trigger validation checks, if this is the first save they will not have been done yet coverImageField.GetComponent().OnUpdate(); @@ -209,18 +214,18 @@ public void SaveToSong() /// public void LoadFromSong() { - nameField.text = Song.SongName; - subNameField.text = Song.SongSubName; - songAuthorField.text = Song.SongAuthorName; - authorField.text = Song.LevelAuthorName; + nameField.text = Info.SongName; + subNameField.text = Info.SongSubName; + songAuthorField.text = Info.SongAuthorName; + authorField.text = Info.LevelAuthorName; BroadcastMessage("OnValidate"); // god unity why are you so dumb - coverImageField.text = Song.CoverImageFilename; - audioPath.text = Song.SongFilename; + coverImageField.text = Info.CoverImageFilename; + audioPath.text = Info.SongFilename; - offset.text = Song.SongTimeOffset.ToString(CultureInfo.InvariantCulture); - if (Song.SongTimeOffset != 0) + offset.text = Info.SongTimeOffset.ToString(CultureInfo.InvariantCulture); + if (Info.SongTimeOffset != 0) { PersistentUI.Instance.ShowDialogBox("SongEditMenu", "songtimeoffset.warning", null, PersistentUI.DialogBoxPresetType.Ok); @@ -230,20 +235,20 @@ public void LoadFromSong() offset.interactable = false; } - bpmField.text = Song.BeatsPerMinute.ToString(CultureInfo.InvariantCulture); - prevStartField.text = Song.PreviewStartTime.ToString(CultureInfo.InvariantCulture); - prevDurField.text = Song.PreviewDuration.ToString(CultureInfo.InvariantCulture); + bpmField.text = Info.BeatsPerMinute.ToString(CultureInfo.InvariantCulture); + prevStartField.text = Info.PreviewStartTime.ToString(CultureInfo.InvariantCulture); + prevDurField.text = Info.PreviewDuration.ToString(CultureInfo.InvariantCulture); environmentDropdown.ClearOptions(); environmentDropdown.AddOptions(VanillaEnvironments.Select(it => it.HumanName).ToList()); // Handle unsupported environment - if (!VanillaEnvironments.Any(env => env.JsonName == Song.EnvironmentName)) + if (!VanillaEnvironments.Any(env => env.JsonName == Info.EnvironmentName)) { - environmentDropdown.AddOptions(new List { Song.EnvironmentName }); + environmentDropdown.AddOptions(new List { Info.EnvironmentName }); } - environmentDropdown.value = GetEnvironmentIDFromString(Song.EnvironmentName); + environmentDropdown.value = GetEnvironmentIDFromString(Info.EnvironmentName); customPlatformsDropdown.ClearOptions(); customPlatformsDropdown.AddOptions(new List { "None" }); @@ -263,12 +268,12 @@ public void LoadFromSong() /// Custom platform index private int CustomPlatformFromSong() { - if (Song.CustomData != null) + if (Info.CustomData != null) { - if (Song.CustomData["_customEnvironment"] != null && Song.CustomData["_customEnvironment"] != "") + if (Info.CustomData["_customEnvironment"] != null && Info.CustomData["_customEnvironment"] != "") { return CustomPlatformsLoader.Instance.GetAllEnvironmentIds() - .IndexOf(Song.CustomData["_customEnvironment"]) + 1; + .IndexOf(Info.CustomData["_customEnvironment"]) + 1; } return 0; @@ -290,14 +295,14 @@ private int CustomPlatformFromSong() /// Coroutine IEnumerator private IEnumerator LoadAudio(bool useTemp = true, bool applySongTimeOffset = false) { - if (!Directory.Exists(Song.Directory)) yield break; + if (!Directory.Exists(Info.Directory)) yield break; - var fullPath = Path.Combine(Song.Directory, useTemp ? audioPath.text : Song.SongFilename); + var fullPath = Path.Combine(Info.Directory, useTemp ? audioPath.text : Info.SongFilename); Debug.Log("Loading audio"); if (File.Exists(fullPath)) { - yield return BeatSaberSongExtensions.LoadAudio(null,(clip) => + yield return BeatSaberSongExtensions.LoadAudio(Info,(clip) => { previewAudio.clip = clip; BeatSaberSongContainer.Instance.LoadedSong = clip; @@ -321,7 +326,7 @@ private IEnumerator LoadAudio(bool useTemp = true, bool applySongTimeOffset = fa /// public void DeleteMap() => PersistentUI.Instance.ShowDialogBox("SongEditMenu", "delete.dialog", HandleDeleteMap, - PersistentUI.DialogBoxPresetType.YesNo, new object[] { Song.SongName }); + PersistentUI.DialogBoxPresetType.YesNo, new object[] { Info.SongName }); /// /// Delete the map, it's still recoverable externally @@ -331,7 +336,7 @@ private void HandleDeleteMap(int result) { if (result == 0) //Left button (ID 0) pressed; the user wants to delete the map. { - FileOperationAPIWrapper.MoveToRecycleBin(Song.Directory); + FileOperationAPIWrapper.MoveToRecycleBin(Info.Directory); ReturnToSongList(); } //Middle button (ID 1) would be pressed; the user doesn't want to delete the map, so we do nothing. } @@ -437,7 +442,7 @@ private void HandleEditMapButtonPressed(int r) if (r != 2) { var map = difficultySelect.CurrentDiff; - PersistentUI.UpdateBackground(Song); + PersistentUI.UpdateBackground(Info); if (map == null) { @@ -448,7 +453,7 @@ private void HandleEditMapButtonPressed(int r) } Debug.Log("Transitioning..."); - Settings.Instance.LastLoadedMap = Song.Directory; + Settings.Instance.LastLoadedMap = Info.Directory; Settings.Instance.LastLoadedChar = BeatSaberSongContainer.Instance.DifficultyData.ParentBeatmapSet .BeatmapCharacteristicName; Settings.Instance.LastLoadedDiff = BeatSaberSongContainer.Instance.DifficultyData.Difficulty; @@ -577,17 +582,17 @@ private static float GetTextValue(TMP_InputField inputfield) /// /// True if user has made changes, false otherwise private bool IsDirty() => - Song.SongName != nameField.text || - Song.SongSubName != subNameField.text || - Song.SongAuthorName != songAuthorField.text || - Song.LevelAuthorName != authorField.text || - Song.CoverImageFilename != coverImageField.text || - Song.SongFilename != audioPath.text || - !NearlyEqual(Song.BeatsPerMinute, GetTextValue(bpmField)) || - !NearlyEqual(Song.PreviewStartTime, GetTextValue(prevStartField)) || - !NearlyEqual(Song.PreviewDuration, GetTextValue(prevDurField)) || - !NearlyEqual(Song.SongTimeOffset, GetTextValue(offset)) || - environmentDropdown.value != GetEnvironmentIDFromString(Song.EnvironmentName) || + Info.SongName != nameField.text || + Info.SongSubName != subNameField.text || + Info.SongAuthorName != songAuthorField.text || + Info.LevelAuthorName != authorField.text || + Info.CoverImageFilename != coverImageField.text || + Info.SongFilename != audioPath.text || + !NearlyEqual(Info.BeatsPerMinute, GetTextValue(bpmField)) || + !NearlyEqual(Info.PreviewStartTime, GetTextValue(prevStartField)) || + !NearlyEqual(Info.PreviewDuration, GetTextValue(prevDurField)) || + !NearlyEqual(Info.SongTimeOffset, GetTextValue(offset)) || + environmentDropdown.value != GetEnvironmentIDFromString(Info.EnvironmentName) || customPlatformsDropdown.value != CustomPlatformFromSong(); private static bool NearlyEqual(float a, float b, float epsilon = 0.01f) => From ab9444d62c86fe9f9765f779f25a231c910d9a96 Mon Sep 17 00:00:00 2001 From: Bullet <68104413+XAce1337manX@users.noreply.github.com> Date: Mon, 7 Oct 2024 20:03:16 +1000 Subject: [PATCH 12/49] wip: the right side probably works --- Assets/__Scripts/BeatSaberSong.cs | 4 +- Assets/__Scripts/BeatSaberSongContainer.cs | 3 +- .../__Scripts/Beatmap/Base/BaseDifficulty.cs | 10 +- Assets/__Scripts/Beatmap/Base/BaseGrid.cs | 6 +- .../Beatmap/Containers/EventContainer.cs | 2 +- .../Beatmap/Helper/EnvironmentInfoHelper.cs | 8 +- .../Beatmap/Info/Base/BaseBpmInfo.cs | 20 ++- .../__Scripts/Beatmap/Info/Base/BaseInfo.cs | 4 +- Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs | 2 +- .../__Scripts/Beatmap/Info/V4/V4AudioData.cs | 2 +- Assets/__Scripts/Beatmap/V2/V2Difficulty.cs | 2 +- Assets/__Scripts/Beatmap/V3/V3Difficulty.cs | 2 +- .../MapEditor/AudioTimeSyncController.cs | 12 +- .../__Scripts/MapEditor/AutoSaveController.cs | 35 ++--- Assets/__Scripts/Requirements/CinemaReq.cs | 2 +- Assets/__Scripts/UI/InputBoxFileValidator.cs | 14 +- .../UI/SongEditMenu/CharacteristicSelect.cs | 5 +- .../__Scripts/UI/SongEditMenu/CopySource.cs | 8 +- .../UI/SongEditMenu/DifficultySelect.cs | 122 +++++++++--------- .../UI/SongEditMenu/DifficultySettings.cs | 51 ++++---- Assets/__Scripts/UI/SongInfoEditUI.cs | 10 +- 21 files changed, 175 insertions(+), 149 deletions(-) diff --git a/Assets/__Scripts/BeatSaberSong.cs b/Assets/__Scripts/BeatSaberSong.cs index edd559800..c94b429b6 100644 --- a/Assets/__Scripts/BeatSaberSong.cs +++ b/Assets/__Scripts/BeatSaberSong.cs @@ -600,7 +600,7 @@ private static JSONNode GetNodeFromFile(string file) return null; } - [Serializable][Obsolete] + [Serializable][Obsolete("Use InfoDifficulty")] public class DifficultyBeatmap { [FormerlySerializedAs("difficulty")] public string Difficulty = "Easy"; @@ -689,7 +689,7 @@ public JSONNode GetOrCreateCustomData() } } - [Serializable][Obsolete] + [Serializable][Obsolete("Use InfoDifficultySet")] public class DifficultyBeatmapSet { [FormerlySerializedAs("beatmapCharacteristicName")] public string BeatmapCharacteristicName = "Standard"; diff --git a/Assets/__Scripts/BeatSaberSongContainer.cs b/Assets/__Scripts/BeatSaberSongContainer.cs index 9a3af3d93..c298fd516 100644 --- a/Assets/__Scripts/BeatSaberSongContainer.cs +++ b/Assets/__Scripts/BeatSaberSongContainer.cs @@ -10,8 +10,9 @@ public class BeatSaberSongContainer : MonoBehaviour { - [Obsolete("",false)] + [Obsolete("Use Info",false)] [FormerlySerializedAs("song")] public BeatSaberSong Song; + [Obsolete("Use MapDifficultyInfo")] [FormerlySerializedAs("difficultyData")] public BeatSaberSong.DifficultyBeatmap DifficultyData; [FormerlySerializedAs("loadedSong")] public AudioClip LoadedSong; diff --git a/Assets/__Scripts/Beatmap/Base/BaseDifficulty.cs b/Assets/__Scripts/Beatmap/Base/BaseDifficulty.cs index 69f991417..d7dc68abe 100644 --- a/Assets/__Scripts/Beatmap/Base/BaseDifficulty.cs +++ b/Assets/__Scripts/Beatmap/Base/BaseDifficulty.cs @@ -78,8 +78,8 @@ public List> public void ConvertCustomBpmToOfficial() { - var songBpm = BeatSaberSongContainer.Instance.Song.BeatsPerMinute; - var customData = BeatSaberSongContainer.Instance.DifficultyData.CustomData; + var songBpm = BeatSaberSongContainer.Instance.Info.BeatsPerMinute; + var customData = BeatSaberSongContainer.Instance.MapDifficultyInfo.CustomData; // Replace editor offset with equivalent bpm change if it exists if ((customData?.HasKey("_editorOffset") ?? false) && customData["_editorOffset"] > 0f) @@ -268,13 +268,13 @@ public bool Save() // Write Bpm file var songContainer = BeatSaberSongContainer.Instance; - var bpmRegions = BaseBpmInfo.GetBpmInfoRegions(BpmEvents, songContainer.Song.BeatsPerMinute, + var bpmRegions = BaseBpmInfo.GetBpmInfoRegions(BpmEvents, songContainer.Info.BeatsPerMinute, songContainer.LoadedSongSamples, songContainer.LoadedSongFrequency); var bpmInfo = new BaseBpmInfo { BpmRegions = bpmRegions }.InitWithSongContainerInstance(); var bpmOutputJson = V2BpmInfo.GetOutputJson(bpmInfo); - var bpmOutputFileName = BaseBpmInfo.GetOutputFileName(songContainer.Song); + var bpmOutputFileName = BaseBpmInfo.GetOutputFileName(songContainer.Info); - File.WriteAllText(Path.Combine(songContainer.Song.Directory, bpmOutputFileName), + File.WriteAllText(Path.Combine(songContainer.Info.Directory, bpmOutputFileName), bpmOutputJson.ToString(2)); return true; diff --git a/Assets/__Scripts/Beatmap/Base/BaseGrid.cs b/Assets/__Scripts/Beatmap/Base/BaseGrid.cs index a5f6101bc..db0b303fa 100644 --- a/Assets/__Scripts/Beatmap/Base/BaseGrid.cs +++ b/Assets/__Scripts/Beatmap/Base/BaseGrid.cs @@ -99,10 +99,10 @@ public override void Apply(BaseObject originalData) public override void RecomputeSongBpmTime() { var njs = CustomNoteJumpMovementSpeed?.AsFloat - ?? BeatSaberSongContainer.Instance.DifficultyData.NoteJumpMovementSpeed; + ?? BeatSaberSongContainer.Instance.MapDifficultyInfo.NoteJumpSpeed; var offset = CustomNoteJumpStartBeatOffset?.AsFloat - ?? BeatSaberSongContainer.Instance.DifficultyData.NoteJumpStartBeatOffset; - var bpm = BeatSaberSongContainer.Instance.Song.BeatsPerMinute; + ?? BeatSaberSongContainer.Instance.MapDifficultyInfo.NoteStartBeatOffset; + var bpm = BeatSaberSongContainer.Instance.Info.BeatsPerMinute; Hjd = SpawnParameterHelper.CalculateHalfJumpDuration(njs, offset, bpm); // (5 / 3) * njs * (60 / bpm) = 100 EditorScale = 100f * njs / bpm; diff --git a/Assets/__Scripts/Beatmap/Containers/EventContainer.cs b/Assets/__Scripts/Beatmap/Containers/EventContainer.cs index 6c6a36c65..48dfb39b9 100644 --- a/Assets/__Scripts/Beatmap/Containers/EventContainer.cs +++ b/Assets/__Scripts/Beatmap/Containers/EventContainer.cs @@ -185,7 +185,7 @@ private float GetHeight() public void UpdateGradientRendering(Color? startColor = null, Color? endColor = null, string easing = "easeLinear") { - if (!EventData.IsLightEvent(BeatSaberSongContainer.Instance.Song.EnvironmentName)) + if (!EventData.IsLightEvent(BeatSaberSongContainer.Instance.Info.EnvironmentName)) { lightGradientController.SetVisible(false); return; diff --git a/Assets/__Scripts/Beatmap/Helper/EnvironmentInfoHelper.cs b/Assets/__Scripts/Beatmap/Helper/EnvironmentInfoHelper.cs index a8aacf8ce..6dc4161cc 100644 --- a/Assets/__Scripts/Beatmap/Helper/EnvironmentInfoHelper.cs +++ b/Assets/__Scripts/Beatmap/Helper/EnvironmentInfoHelper.cs @@ -1,8 +1,8 @@ public static class EnvironmentInfoHelper { public static string GetName() => - BeatSaberSongContainer.Instance.DifficultyData.ParentBeatmapSet.BeatmapCharacteristicName == "90Degree" || - BeatSaberSongContainer.Instance.DifficultyData.ParentBeatmapSet.BeatmapCharacteristicName == "360Degree" - ? BeatSaberSongContainer.Instance.Song.AllDirectionsEnvironmentName - : BeatSaberSongContainer.Instance.Song.EnvironmentName; + BeatSaberSongContainer.Instance.MapDifficultyInfo.Characteristic == "90Degree" || + BeatSaberSongContainer.Instance.MapDifficultyInfo.Characteristic == "360Degree" + ? BeatSaberSongContainer.Instance.Info.AllDirectionsEnvironmentName + : BeatSaberSongContainer.Instance.Info.EnvironmentName; } diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs index a0de70746..662df8cd4 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseBpmInfo.cs @@ -13,17 +13,25 @@ public class BaseBpmInfo public BaseBpmInfo InitWithSongContainerInstance() { - Version = BeatSaberSongContainer.Instance.Song.Version[0] == '4' ? "4.0.0" : "2.0.0"; + Version = BeatSaberSongContainer.Instance.Info.Version[0] == '4' ? "4.0.0" : "2.0.0"; AudioSamples = BeatSaberSongContainer.Instance.LoadedSongSamples; AudioFrequency = BeatSaberSongContainer.Instance.LoadedSongFrequency; return this; } - - public static string GetOutputFileName(BeatSaberSong info) => info.Version[0] == '4' - ? V4AudioData.FileName - : V2BpmInfo.FileName; - + + public static string GetOutputFileName(BaseInfo info) + { + if (info.Version[0] == '4') + { + return !string.IsNullOrWhiteSpace(info.AudioDataFilename) + ? info.AudioDataFilename + : V4AudioData.FileName; + } + + return V2BpmInfo.FileName; + } + public static List GetBpmEvents(List bpmRegions, int audioFrequency) { var bpmEvents = new List(); diff --git a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs index c162a928f..a2358cae7 100644 --- a/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/Base/BaseInfo.cs @@ -150,7 +150,7 @@ public class InfoDifficultySet public class InfoDifficulty { public InfoDifficulty(InfoDifficultySet parentSet) => ParentSet = parentSet; - private InfoDifficultySet ParentSet { get; } + public InfoDifficultySet ParentSet { get; } public string Characteristic => ParentSet.Characteristic; public string CustomCharacteristicLabel => ParentSet.CustomCharacteristicLabel; public string CustomCharacteristicIconImageFileName => ParentSet.CustomCharacteristicIconImageFileName; @@ -200,6 +200,8 @@ public class InfoDifficulty public Color? CustomEnvColorBoostLeft { get; set; } public Color? CustomEnvColorBoostRight { get; set; } public Color? CustomEnvColorBoostWhite { get; set; } + + public void SetBeatmapFileNameToDefault() => BeatmapFileName = $"{Difficulty}{Characteristic}.dat"; } public class InfoColorScheme diff --git a/Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs b/Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs index a43bcbf05..7b015be23 100644 --- a/Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs +++ b/Assets/__Scripts/Beatmap/Info/V2/V2BpmInfo.cs @@ -45,7 +45,7 @@ public static JSONNode GetOutputJson(BaseBpmInfo bpmInfo) json["_songSampleCount"] = bpmInfo.AudioSamples; json["_songFrequency"] = bpmInfo.AudioFrequency; - var songBpm = BeatSaberSongContainer.Instance.Song.BeatsPerMinute; + var songBpm = BeatSaberSongContainer.Instance.Info.BeatsPerMinute; var precision = Settings.Instance.BpmTimeValueDecimalPrecision; var regions = new JSONArray(); diff --git a/Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs b/Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs index e008eb2f5..eaf91b912 100644 --- a/Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs +++ b/Assets/__Scripts/Beatmap/Info/V4/V4AudioData.cs @@ -59,7 +59,7 @@ public static JSONNode GetOutputJson(BaseBpmInfo bpmInfo) json["songSampleCount"] = bpmInfo.AudioSamples; json["songFrequency"] = bpmInfo.AudioFrequency; - var songBpm = BeatSaberSongContainer.Instance.Song.BeatsPerMinute; + var songBpm = BeatSaberSongContainer.Instance.Info.BeatsPerMinute; var precision = Settings.Instance.BpmTimeValueDecimalPrecision; var bpmData = new JSONArray(); diff --git a/Assets/__Scripts/Beatmap/V2/V2Difficulty.cs b/Assets/__Scripts/Beatmap/V2/V2Difficulty.cs index ac158e641..9794dfa49 100644 --- a/Assets/__Scripts/Beatmap/V2/V2Difficulty.cs +++ b/Assets/__Scripts/Beatmap/V2/V2Difficulty.cs @@ -32,7 +32,7 @@ public static JSONNode GetOutputJson(BaseDifficulty difficulty) if (difficulty.BpmEvents.Count > 0 && difficulty.BpmEvents.First().JsonTime != 0) { var insertedBpm = (BeatSaberSongContainer.Instance != null) - ? BeatSaberSongContainer.Instance.Song.BeatsPerMinute + ? BeatSaberSongContainer.Instance.Info.BeatsPerMinute : 100; // This path only appears in tests allEvents.Add(new BaseBpmEvent { diff --git a/Assets/__Scripts/Beatmap/V3/V3Difficulty.cs b/Assets/__Scripts/Beatmap/V3/V3Difficulty.cs index f69f2c894..915c7e691 100644 --- a/Assets/__Scripts/Beatmap/V3/V3Difficulty.cs +++ b/Assets/__Scripts/Beatmap/V3/V3Difficulty.cs @@ -29,7 +29,7 @@ public static JSONNode GetOutputJson(BaseDifficulty difficulty) if (difficulty.BpmEvents.Count > 0 && difficulty.BpmEvents.First().JsonTime != 0) { var insertedBpm = (BeatSaberSongContainer.Instance != null) - ? BeatSaberSongContainer.Instance.Song.BeatsPerMinute + ? BeatSaberSongContainer.Instance.Info.BeatsPerMinute : 100; // This path only appears in tests difficulty.BpmEvents.Insert(0, new BaseBpmEvent { diff --git a/Assets/__Scripts/MapEditor/AudioTimeSyncController.cs b/Assets/__Scripts/MapEditor/AudioTimeSyncController.cs index 03e26fd7b..09e0719a5 100644 --- a/Assets/__Scripts/MapEditor/AudioTimeSyncController.cs +++ b/Assets/__Scripts/MapEditor/AudioTimeSyncController.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Runtime.CompilerServices; +using Beatmap.Info; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.Serialization; @@ -26,8 +27,12 @@ public class AudioTimeSyncController : MonoBehaviour, CMInput.IPlaybackActions, [FormerlySerializedAs("bpmChangesContainer")][SerializeField] private BPMChangeGridContainer bpmChangeGridContainer; [SerializeField] private GridRenderingController gridRenderingController; [SerializeField] private CustomStandaloneInputModule customStandaloneInputModule; + + [Obsolete("BaseInfo", true)] [FormerlySerializedAs("song")][HideInInspector] public BeatSaberSong Song; + public BaseInfo MapInfo; + [SerializeField] private float currentSeconds; [FormerlySerializedAs("stopScheduled")] public bool StopScheduled; [FormerlySerializedAs("initialized")] public bool Initialized; @@ -116,7 +121,8 @@ private void Start() { //Init dat stuff clip = BeatSaberSongContainer.Instance.LoadedSong; - Song = BeatSaberSongContainer.Instance.Song; + // Song = BeatSaberSongContainer.Instance.Song; + MapInfo = BeatSaberSongContainer.Instance.Info; ResetTime(); IsPlaying = false; SongAudioSource.clip = clip; @@ -438,10 +444,10 @@ public void MoveToJsonTime(float jsonTime) public float FindRoundedBeatTime(float beat, float snap = -1) => bpmChangeGridContainer.FindRoundedBpmTime(beat, snap); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float GetBeatFromSeconds(float seconds) => Song.BeatsPerMinute / 60 * seconds; + public float GetBeatFromSeconds(float seconds) => MapInfo.BeatsPerMinute / 60 * seconds; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float GetSecondsFromBeat(float beat) => 60 / Song.BeatsPerMinute * beat; + public float GetSecondsFromBeat(float beat) => 60 / MapInfo.BeatsPerMinute * beat; private void ValidatePosition() { diff --git a/Assets/__Scripts/MapEditor/AutoSaveController.cs b/Assets/__Scripts/MapEditor/AutoSaveController.cs index b51724d5d..ef3990233 100644 --- a/Assets/__Scripts/MapEditor/AutoSaveController.cs +++ b/Assets/__Scripts/MapEditor/AutoSaveController.cs @@ -53,14 +53,14 @@ private void Start() autoSaveToggle.isOn = Settings.Instance.AutoSave; t = 0; - var autoSavesDir = Path.Combine(BeatSaberSongContainer.Instance.Song.Directory, "autosaves"); + var autoSavesDir = Path.Combine(BeatSaberSongContainer.Instance.Info.Directory, "autosaves"); if (Directory.Exists(autoSavesDir)) { foreach (var dir in Directory.EnumerateDirectories(autoSavesDir)) currentAutoSaves.Add(new DirectoryInfo(dir)); } - maxSongBpmTime = BeatSaberSongContainer.Instance.LoadedSong.length * BeatSaberSongContainer.Instance.Song.BeatsPerMinute / 60; + maxSongBpmTime = BeatSaberSongContainer.Instance.LoadedSong.length * BeatSaberSongContainer.Instance.Info.BeatsPerMinute / 60; CleanAutosaves(); } @@ -364,20 +364,20 @@ public void Save(bool auto = false) Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; //Saving Map Data var originalMap = BeatSaberSongContainer.Instance.Map.DirectoryAndFile; - var originalSong = BeatSaberSongContainer.Instance.Song.Directory; + var originalInfo = BeatSaberSongContainer.Instance.Info.Directory; try { if (auto) { - var autoSaveDir = Path.Combine(originalSong, "autosaves", $"{DateTime.Now:dd-MM-yyyy_HH-mm-ss}"); + var autoSaveDir = Path.Combine(originalInfo, "autosaves", $"{DateTime.Now:dd-MM-yyyy_HH-mm-ss}"); Debug.Log($"Auto saved to: {autoSaveDir}"); //We need to create the autosave directory before we can save the .dat difficulty into it. Directory.CreateDirectory(autoSaveDir); BeatSaberSongContainer.Instance.Map.DirectoryAndFile = Path.Combine(autoSaveDir, - BeatSaberSongContainer.Instance.DifficultyData.BeatmapFilename); - BeatSaberSongContainer.Instance.Song.Directory = autoSaveDir; + BeatSaberSongContainer.Instance.MapDifficultyInfo.BeatmapFileName); + BeatSaberSongContainer.Instance.Info.Directory = autoSaveDir; var newDirectoryInfo = new DirectoryInfo(autoSaveDir); currentAutoSaves.Add(newDirectoryInfo); @@ -391,20 +391,21 @@ public void Save(bool auto = false) BeatSaberSongContainer.Instance.Map.Save(); - var set = BeatSaberSongContainer.Instance.DifficultyData.ParentBeatmapSet; //Grab our set - BeatSaberSongContainer.Instance.Song.DifficultyBeatmapSets.Remove(set); //Yeet it out - var data = BeatSaberSongContainer.Instance.DifficultyData; //Grab our diff data - set.DifficultyBeatmaps.Remove(data); //Yeet out our difficulty data - if (BeatSaberSongContainer.Instance.DifficultyData.CustomData == + var set = BeatSaberSongContainer.Instance.MapDifficultyInfo.ParentSet; //Grab our set + BeatSaberSongContainer.Instance.Info.DifficultySets.Remove(set); //Yeet it out + var data = BeatSaberSongContainer.Instance.MapDifficultyInfo; //Grab our diff data + set.Difficulties.Remove(data); //Yeet out our difficulty data + if (BeatSaberSongContainer.Instance.MapDifficultyInfo.CustomData == null) //if for some reason this be null, make new customdata { - BeatSaberSongContainer.Instance.DifficultyData.CustomData = new JSONObject(); + BeatSaberSongContainer.Instance.MapDifficultyInfo.CustomData = new JSONObject(); } - set.DifficultyBeatmaps.Add(BeatSaberSongContainer.Instance - .DifficultyData); //Add back our difficulty data - BeatSaberSongContainer.Instance.Song.DifficultyBeatmapSets.Add(set); //Add back our difficulty set - BeatSaberSongContainer.Instance.Song.SaveSong(); //Save + set.Difficulties.Add(BeatSaberSongContainer.Instance + .MapDifficultyInfo); //Add back our difficulty data + BeatSaberSongContainer.Instance.Info.DifficultySets.Add(set); //Add back our difficulty set + BeatSaberSongContainer.Instance.Info.Save(); //Save + // TODO: Make sure this works for v4 } catch (Exception ex) @@ -414,7 +415,7 @@ public void Save(bool auto = false) } // Revert directory if it was changed by autosave - BeatSaberSongContainer.Instance.Song.Directory = originalSong; + BeatSaberSongContainer.Instance.Info.Directory = originalInfo; BeatSaberSongContainer.Instance.Map.DirectoryAndFile = originalMap; notification.SkipDisplay = true; diff --git a/Assets/__Scripts/Requirements/CinemaReq.cs b/Assets/__Scripts/Requirements/CinemaReq.cs index 7616c436c..8f580ad4a 100644 --- a/Assets/__Scripts/Requirements/CinemaReq.cs +++ b/Assets/__Scripts/Requirements/CinemaReq.cs @@ -7,7 +7,7 @@ public class CinemaReq : RequirementCheck public override RequirementType IsRequiredOrSuggested(BeatSaberSong.DifficultyBeatmap mapInfo, BaseDifficulty map) { - var path = Path.Combine(BeatSaberSongContainer.Instance.Song.Directory, "cinema-video.json"); + var path = Path.Combine(BeatSaberSongContainer.Instance.Info.Directory, "cinema-video.json"); return File.Exists(path) ? RequirementType.Suggestion : RequirementType.None; } } diff --git a/Assets/__Scripts/UI/InputBoxFileValidator.cs b/Assets/__Scripts/UI/InputBoxFileValidator.cs index 2cdf8201b..3f7d4e15e 100644 --- a/Assets/__Scripts/UI/InputBoxFileValidator.cs +++ b/Assets/__Scripts/UI/InputBoxFileValidator.cs @@ -32,9 +32,9 @@ public void Awake() startOffset = transform.offsetMax; // This will get un-done on start, but will stop negative text scroll // Shouldn't really be in awake but it needs to run before SongInfoEditUI sets the text value - var song = BeatSaberSongContainer.Instance != null ? BeatSaberSongContainer.Instance.Song : null; + var info = BeatSaberSongContainer.Instance != null ? BeatSaberSongContainer.Instance.Info : null; - if (forceStartupValidationAlign || (enableValidation && Directory.Exists(song?.Directory))) + if (forceStartupValidationAlign || (enableValidation && Directory.Exists(info?.Directory))) transform.offsetMax = new Vector2(startOffset.x - 36, startOffset.y); } @@ -42,17 +42,17 @@ public void Awake() public void OnUpdate() { - var song = BeatSaberSongContainer.Instance != null ? BeatSaberSongContainer.Instance.Song : null; + var info = BeatSaberSongContainer.Instance != null ? BeatSaberSongContainer.Instance.Info : null; var filename = input.text; - if (!enableValidation || filename.Length == 0 || !Directory.Exists(song?.Directory)) + if (!enableValidation || filename.Length == 0 || !Directory.Exists(info?.Directory)) { if (!forceStartupValidationAlign) SetValidationState(false); return; } - var path = Path.Combine(song.Directory, filename); + var path = Path.Combine(info.Directory, filename); SetValidationState(true, File.Exists(path)); } @@ -84,7 +84,7 @@ public void BrowserForFile() { var exts = new[] { new ExtensionFilter(filetypeName, extensions), new ExtensionFilter("All Files", "*") }; - if (BeatSaberSongContainer.Instance.Song is null || BeatSaberSongContainer.Instance.Song.Directory is null) + if (BeatSaberSongContainer.Instance.Info is null || BeatSaberSongContainer.Instance.Info.Directory is null) { PersistentUI.Instance.ShowDialogBox("Cannot locate song directory. Did you forget to save your map?", null, PersistentUI.DialogBoxPresetType.Ok); @@ -92,7 +92,7 @@ public void BrowserForFile() return; } - var songDir = BeatSaberSongContainer.Instance.Song.Directory; + var songDir = BeatSaberSongContainer.Instance.Info.Directory; CMInputCallbackInstaller.DisableActionMaps(typeof(InputBoxFileValidator), new[] { typeof(CMInput.IMenusExtendedActions) }); string[] paths; diff --git a/Assets/__Scripts/UI/SongEditMenu/CharacteristicSelect.cs b/Assets/__Scripts/UI/SongEditMenu/CharacteristicSelect.cs index af9266f2e..70af474f0 100644 --- a/Assets/__Scripts/UI/SongEditMenu/CharacteristicSelect.cs +++ b/Assets/__Scripts/UI/SongEditMenu/CharacteristicSelect.cs @@ -1,3 +1,4 @@ +using Beatmap.Info; using TMPro; using UnityEngine; using UnityEngine.UI; @@ -9,7 +10,7 @@ public class CharacteristicSelect : MonoBehaviour [SerializeField] private DifficultySelect difficultySelect; private Transform selected; - private BeatSaberSong Song => BeatSaberSongContainer.Instance != null ? BeatSaberSongContainer.Instance.Song : null; + private static BaseInfo MapInfo => BeatSaberSongContainer.Instance != null ? BeatSaberSongContainer.Instance.Info : null; public void Start() { @@ -20,7 +21,7 @@ public void Start() var button = child.GetComponent