public void CreateSong() { BeatSaberSong songe = new BeatSaberSong(list.WIPLevels); BeatSaberSongContainer.Instance.SelectSongForEditing(songe); PersistentUI.Instance.DisplayMessage("Be sure to save info.dat before editing!", PersistentUI.DisplayMessageType.BOTTOM); }
public Sprite GetBGSprite(BeatSaberSong song) { if (Settings.Instance.DarkTheme) { return(DarkSprite); } if (song.customData != null) { if (song.customData && !string.IsNullOrEmpty(song.customData["_customEnvironment"])) { switch (song.customData["_customEnvironment"].Value) { case "Vapor Frame": return(VaporFramePlatform); case "Big Mirror V2": return(BigMirrorV2Platform); } } } switch (song.environmentName) { case "DefaultEnvironment": return(DefaultPlatform); case "TriangleEnvironment": return(TrianglePlatform); case "BigMirrorEnvironment": return(BigMirrorPlatform); case "NiceEnvironment": return(NicePlatform); default: return(FailsafeBackground); //In case someone does a fucky wucky } }
public void DoesNotDuplicateSong() { SetupPlaylistsWithSongs(2, 2); BeatSaberQuestomConfig oldConfig = null; using (var fp = GetProvider()) { var q = GetQaeConfig(fp); using (QuestomAssetsEngine qae = new QuestomAssetsEngine(q)) { oldConfig = qae.GetCurrentConfig(); var config = CopyIDs(oldConfig); var song = new BeatSaberSong() { SongID = config.Playlists[0].SongList[0].SongID, CustomSongPath = MakeTestSongDir() }; oldConfig.Playlists[0].SongList.Add(song); qae.UpdateConfig(config); } } using (var fp = GetProvider()) { var q = GetQaeConfig(fp); using (QuestomAssetsEngine qae = new QuestomAssetsEngine(q)) { var testConfig = qae.GetCurrentConfig(); Assert.AreEqual(2, testConfig.Playlists[0].SongList.Count()); Assert.AreEqual(2, testConfig.Playlists[1].SongList.Count()); } } Assert.Pass(); }
protected void SetupPlaylistsWithSongs(int playlistCount, int songCount) { BeatSaberQuestomConfig config = null; using (var fp = GetProvider()) { var q = GetQaeConfig(fp); using (QuestomAssetsEngine qae = new QuestomAssetsEngine(q)) { config = qae.GetCurrentConfig(); for (int p = 0; p < playlistCount; p++) { var playlist = new BeatSaberPlaylist() { PlaylistID = string.Format(PlaylistIDFormat, p), PlaylistName = string.Format(PlaylistNameFormat, p) }; for (int i = 0; i < songCount; i++) { var song = new BeatSaberSong() { SongID = string.Format(SongIDFormat, p, i), CustomSongPath = MakeTestSongDir() }; playlist.SongList.Add(song); } config.Playlists.Add(playlist); } qae.UpdateConfig(config); qae.Save(); } } }
private void SetupPlaylistsWithSongs(int playlistCount, int songCount) { BeatSaberQuestomConfig config = null; using (var apk = new ApkAssetsFileProvider(_apkFile, FileCacheMode.Memory)) { QuestomAssetsEngine qae = new QuestomAssetsEngine(apk, "assets/bin/Data/"); config = qae.GetCurrentConfig(); for (int p = 0; p < playlistCount; p++) { var playlist = new BeatSaberPlaylist() { PlaylistID = string.Format(PlaylistIDFormat, p), PlaylistName = string.Format(PlaylistNameFormat, p), CoverArtBytes = File.ReadAllBytes(COVER_ART_FILE) }; for (int i = 0; i < songCount; i++) { var song = new BeatSaberSong() { SongID = string.Format(SongIDFormat, p, i), CustomSongFolder = TEST_SONG_FOLDER }; playlist.SongList.Add(song); } config.Playlists.Add(playlist); } qae.UpdateConfig(config); } }
public void DoesNotDuplicateSong() { SetupPlaylistsWithSongs(2, 2); BeatSaberQuestomConfig oldConfig = null; using (var apk = new ApkAssetsFileProvider(_apkFile, FileCacheMode.Memory, false)) { QuestomAssetsEngine qae = new QuestomAssetsEngine(apk, "assets/bin/Data/", false); oldConfig = qae.GetCurrentConfig(false); var config = CopyIDs(oldConfig); var song = new BeatSaberSong() { SongID = config.Playlists[0].SongList[0].SongID, CustomSongFolder = TEST_SONG_FOLDER }; oldConfig.Playlists[0].SongList.Add(song); qae.UpdateConfig(config); } using (var apk = new ApkAssetsFileProvider(_apkFile, FileCacheMode.Memory, true)) { QuestomAssetsEngine qae = new QuestomAssetsEngine(apk, "assets/bin/Data/", true); var testConfig = qae.GetCurrentConfig(false); Assert.AreEqual(2, testConfig.Playlists[0].SongList.Count()); Assert.AreEqual(2, testConfig.Playlists[1].SongList.Count()); } Assert.Pass(); }
public void AssignSong(BeatSaberSong song) { this.song = song; button.onClick.RemoveAllListeners(); button.onClick.AddListener(ButtonClicked); text.text = $"{song.songName.StripTMPTags()} <size=50%><i>{song.songSubName.StripTMPTags()}</i></size>"; }
public void RefreshSongList(bool search) { FilteredBySearch = search; string[] directories; directories = Directory.GetDirectories(WIPLevels ? Settings.Instance.CustomWIPSongsFolder : Settings.Instance.CustomSongsFolder); songs.Clear(); foreach (var dir in directories) { BeatSaberSong song = BeatSaberSong.GetSongFromFolder(dir); if (song == null) { //Get songs from subdirectories string[] subDirectories = Directory.GetDirectories(dir); foreach (var subDir in subDirectories) { song = BeatSaberSong.GetSongFromFolder(subDir); } } if (song != null) { songs.Add(song); } } //Sort by song name, and filter by search text. if (FilteredBySearch) { songs = songs.Where(x => searchField.text != "" ? x.songName.AllIndexOf(searchField.text).Any() : true).ToList(); } songs = songs.OrderBy(x => x.songName).ToList(); maxPage = Mathf.Max(0, Mathf.CeilToInt(songs.Count / (items.Length + 1))); SetPage(0); }
private void SetupPlaylistsWithSongs(int playlistCount, int songCount) { BeatSaberQuestomConfig config = null; QuestomAssetsEngine qae = new QuestomAssetsEngine(_apkFile); config = qae.GetCurrentConfig(); for (int p = 0; p < playlistCount; p++) { var playlist = new BeatSaberPlaylist() { PlaylistID = string.Format(PlaylistIDFormat, p), PlaylistName = string.Format(PlaylistNameFormat, p), CoverArt = new System.Drawing.Bitmap(COVER_ART_FILE) }; for (int i = 0; i < songCount; i++) { var song = new BeatSaberSong() { SongID = string.Format(SongIDFormat, p, i), CustomSongFolder = TEST_SONG_FOLDER }; playlist.SongList.Add(song); } config.Playlists.Add(playlist); } qae.UpdateConfig(config); }
public void DoesNotDuplicateSong() { SetupPlaylistsWithSongs(2, 2); BeatSaberQuestomConfig oldConfig = null; QuestomAssetsEngine qae = new QuestomAssetsEngine(_apkFile); oldConfig = qae.GetCurrentConfig(false); var config = CopyIDs(oldConfig); var song = new BeatSaberSong() { SongID = config.Playlists[0].SongList[0].SongID, CustomSongFolder = TEST_SONG_FOLDER }; oldConfig.Playlists[0].SongList.Add(song); qae.UpdateConfig(config); qae = new QuestomAssetsEngine(_apkFile, true); var testConfig = qae.GetCurrentConfig(false); Assert.AreEqual(2, testConfig.Playlists[0].SongList.Count()); Assert.AreEqual(2, testConfig.Playlists[1].SongList.Count()); Assert.Pass(); }
public static void UpdateBackground(BeatSaberSong song) { if (Instance.editorLoadingBackground.gameObject.activeSelf == false) { Instance.editorLoadingBackground.gameObject.SetActive(true); } Instance.editorLoadingBackground.sprite = Instance.editorImageList.GetBGSprite(song); }
private void HandleNewSongName(string res) { if (res is null) { return; } BeatSaberSong songe = new BeatSaberSong(list.WIPLevels, res); BeatSaberSongContainer.Instance.SelectSongForEditing(songe); PersistentUI.Instance.DisplayMessage("Be sure to save info.dat before editing!", PersistentUI.DisplayMessageType.BOTTOM); }
public void BasicAddNewSongOpWorks() { SetupPlaylistsWithSongs(1, 1); using (var fp = GetProvider()) { var q = GetQaeConfig(fp); using (QuestomAssetsEngine qae = new QuestomAssetsEngine(q)) { var song = new BeatSaberSong() { SongID = "TESTSONG2", CustomSongPath = MakeTestSongDir() }; bool calledStatusChangeStarted = false; bool calledStatusChangeComplete = false; var newSongOp = new AddNewSongToPlaylistOp(song, "someplaylist0", false); qae.OpManager.OpStatusChanged += (sender, op) => { if (op.Status == OpStatus.Started) { calledStatusChangeStarted = true; } if (op.Status == OpStatus.Complete) { calledStatusChangeComplete = true; } }; qae.OpManager.QueueOp(newSongOp); while (qae.OpManager.IsProcessing) { System.Threading.Thread.Sleep(100); } //give it an extra bit of time to make sure events get fired System.Threading.Thread.Sleep(200); Assert.IsTrue(calledStatusChangeStarted, "Did not get OpStatusChanged event for status Started!"); Assert.IsTrue(calledStatusChangeComplete, "Did not get OpStatusChanged event for status Complete!"); qae.Save(); } using (QuestomAssetsEngine qae = new QuestomAssetsEngine(q)) { var cfg = qae.GetCurrentConfig(); var song = cfg.Playlists.FirstOrDefault(x => x.PlaylistID == "someplaylist0")?.SongList?.FirstOrDefault(x => x.SongID == "TESTSONG2"); Assert.NotNull(song, "Couldn't find the song the op was supposed to add!"); //todo: more tests on this } Assert.Pass(); } }
public void RefreshSongList() { songLocationToggleText.StringReference.TableEntryReference = WIPLevels ? "custom" : "wip"; FilteredBySearch = !string.IsNullOrEmpty(searchField.text); string[] directories; directories = Directory.GetDirectories(WIPLevels ? Settings.Instance.CustomWIPSongsFolder : Settings.Instance.CustomSongsFolder); songs.Clear(); foreach (var dir in directories) { BeatSaberSong song = BeatSaberSong.GetSongFromFolder(dir); if (song == null) { Debug.LogWarning($"No song at location {dir} exists! Is it in a subfolder?"); /* * Subfolder loading support has been removed for the following: * A) SongCore does not natively support loading from subfolders, only through editing a config file * B) OneClick no longer saves to a subfolder */ /*if (dir.ToUpper() == "CACHE") continue; //Ignore the cache folder * //Get songs from subdirectories * string[] subDirectories = Directory.GetDirectories(dir); * foreach (var subDir in subDirectories) * { * song = BeatSaberSong.GetSongFromFolder(subDir); * if (song != null) songs.Add(song); * }*/ } else { songs.Add(song); } } //Sort by song name, and filter by search text. songs = songs.OrderBy(x => x.songName).ToList(); maxPage = Mathf.Max(0, Mathf.CeilToInt((songs.Count - 1) / items.Length)); if (FilteredBySearch) { FilterBySearch(); } else { filteredSongs = songs; SetPage(lastVisitedPage); } }
public void LoadPage() { int offset = currentPage * items.Length; for (int i = 0; i < items.Count(); i++) // && (i + offset) < songs.Count; i++) { { if (i + offset < filteredSongs.Count()) { BeatSaberSong song = filteredSongs.ElementAt(i + offset); items[i].gameObject.SetActive(true); items[i].AssignSong(song); } else { items[i].gameObject.SetActive(false); } } }
private void HandleNewSongName(string res) { if (res is null) { return; } if (list.songs.Any(x => x.songName == res)) { PersistentUI.Instance.ShowInputBox("There already exists a beatmap with that name.\n\n" + "Please enter the name for the new beatmap.", HandleNewSongName, "New Beatmap"); return; } BeatSaberSong song = new BeatSaberSong(list.WIPLevels, res); BeatSaberSong.DifficultyBeatmapSet standardSet = new BeatSaberSong.DifficultyBeatmapSet(); song.difficultyBeatmapSets.Add(standardSet); BeatSaberSongContainer.Instance.SelectSongForEditing(song); PersistentUI.Instance.DisplayMessage("Be sure to save info.dat before editing!", PersistentUI.DisplayMessageType.BOTTOM); }
public void OnUpdate() { BeatSaberSong song = BeatSaberSongContainer.Instance?.song; string filename = input.text; if (!enableValidation || filename.Length == 0 || song?.directory == null) { if (!forceStartupValidationAlign) { SetValidationState(false); } return; } string path = Path.Combine(song.directory, filename); SetValidationState(true, File.Exists(path)); }
// Use this for initialization void Start() { try { //Init dat stuff clip = BeatSaberSongContainer.Instance.loadedSong; song = BeatSaberSongContainer.Instance.song; offsetMS = song.songTimeOffset / 1000; ResetTime(); offsetBeat = currentBeat; gridStartPosition = currentBeat * EditorScaleController.EditorScale; IsPlaying = false; songAudioSource.clip = clip; waveformSource.clip = clip; UpdateMovables(); } catch (Exception e) { Debug.LogException(e); } }
private BeatSaberQuestomConfig CopyIDs(BeatSaberQuestomConfig config) { var newConfig = new BeatSaberQuestomConfig(); foreach (var p in config.Playlists) { var playlist = new BeatSaberPlaylist() { PlaylistID = p.PlaylistID }; foreach (var s in p.SongList) { var song = new BeatSaberSong() { SongID = s.SongID }; playlist.SongList.Add(song); } newConfig.Playlists.Add(playlist); } return(newConfig); }
public void RefreshSongList(bool search) { FilteredBySearch = search; string[] directories = new string[] { }; if (WIPLevels) //Grabs songs from CustomWIPLevels or CustomLevels { directories = Directory.GetDirectories(Settings.Instance.CustomWIPSongsFolder); } else { directories = Directory.GetDirectories(Settings.Instance.CustomSongsFolder); } songs.Clear(); for (int i = 0; i < directories.Length; i++) { BeatSaberSong song = BeatSaberSong.GetSongFromFolder(directories[i]); if (song == null) { //Get songs from subdirectories string[] subDirectories = Directory.GetDirectories(directories[i]); for (int e = 0; e < subDirectories.Length; e++) { song = BeatSaberSong.GetSongFromFolder(subDirectories[e]); } } if (song != null) { songs.Add(song); } } //Sort by song name, and filter by search text. if (FilteredBySearch) { songs = songs.Where(x => searchField.text != "" ? x.songName.AllIndexOf(searchField.text).Any() : true).ToList(); } songs = songs.OrderBy(x => x.songName).ToList(); maxPage = Mathf.Max(0, Mathf.CeilToInt(songs.Count / items.Length)); SetPage(0); }
private void HandleNewSongName(string res) { if (res is null) { return; } var song = new BeatSaberSong(list.WIPLevels, res); if (list.songs.Any(x => Path.GetFullPath(x.directory).Equals( Path.GetFullPath(Path.Combine(list.WIPLevels ? Settings.Instance.CustomWIPSongsFolder : Settings.Instance.CustomSongsFolder, song.cleanSongName)), StringComparison.CurrentCultureIgnoreCase ))) { PersistentUI.Instance.ShowInputBox("SongSelectMenu", "newmap.dialog.duplicate", HandleNewSongName, "newmap.dialog.default"); return; } var standardSet = new BeatSaberSong.DifficultyBeatmapSet(); song.difficultyBeatmapSets.Add(standardSet); BeatSaberSongContainer.Instance.SelectSongForEditing(song); PersistentUI.Instance.DisplayMessage("SongSelectMenu", "newmap.message", PersistentUI.DisplayMessageType.BOTTOM); }
public static BeatSaberSong GetSongFromFolder(string directory) { try { JSONNode mainNode = GetNodeFromFile(directory + "/info.dat"); if (mainNode == null) { return(null); } BeatSaberSong song = new BeatSaberSong(directory, mainNode); JSONNode.Enumerator nodeEnum = mainNode.GetEnumerator(); while (nodeEnum.MoveNext()) { string key = nodeEnum.Current.Key; JSONNode node = nodeEnum.Current.Value; switch (key) { case "_songName": song.songName = node.Value; break; case "_songSubName": song.songSubName = node.Value; break; case "_songAuthorName": song.songAuthorName = node.Value; break; case "_levelAuthorName": song.levelAuthorName = node.Value; break; case "_beatsPerMinute": song.beatsPerMinute = node.AsFloat; break; case "_songTimeOffset": song.songTimeOffset = node.AsFloat; break; case "_previewStartTime": song.previewStartTime = node.AsFloat; break; case "_previewDuration": song.previewDuration = node.AsFloat; break; case "_shuffle": song.shuffle = node.AsFloat; break; case "_shufflePeriod": song.shufflePeriod = node.AsFloat; break; case "_coverImageFilename": song.coverImageFilename = node.Value; break; case "_songFilename": song.songFilename = node.Value; break; case "_environmentName": song.environmentName = node.Value; break; //Because there is only one option, I wont load from file. //case "_allDirectionsEnvironmentName": song.allDirectionsEnvironmentName = node.Value; break; case "_customData": song.customData = node; foreach (JSONNode n in node) { if (n["_contributors"]?.AsArray != null) { foreach (JSONNode contributor in n["_contributors"].AsArray) { song.contributors.Add(new MapContributor(contributor)); } } if (n["_editor"]?.Value != null) { song.editor = n["_editor"].Value; } } break; case "_difficultyBeatmapSets": foreach (JSONNode n in node) { DifficultyBeatmapSet set = new DifficultyBeatmapSet(); set.beatmapCharacteristicName = n["_beatmapCharacteristicName"]; foreach (JSONNode d in n["_difficultyBeatmaps"]) { DifficultyBeatmap beatmap = new DifficultyBeatmap(set) { difficulty = d["_difficulty"].Value, difficultyRank = d["_difficultyRank"].AsInt, noteJumpMovementSpeed = d["_noteJumpMovementSpeed"].AsFloat, noteJumpStartBeatOffset = d["_noteJumpStartBeatOffset"].AsFloat, customData = d["_customData"], }; if (d["_customData"]["_colorLeft"] != null) { beatmap.colorLeft = GetColorFromJSONNode(d["_customData"]["_colorLeft"]); } if (d["_customData"]["_colorRight"] != null) { beatmap.colorRight = GetColorFromJSONNode(d["_customData"]["_colorRight"]); } if (d["_customData"]["_envColorLeft"] != null) { beatmap.envColorLeft = GetColorFromJSONNode(d["_customData"]["_envColorLeft"]); } else if (d["_customData"]["_colorLeft"] != null) { beatmap.envColorLeft = beatmap.colorLeft; } if (d["_customData"]["_envColorRight"] != null) { beatmap.envColorRight = GetColorFromJSONNode(d["_customData"]["_envColorRight"]); } else if (d["_customData"]["_colorRight"] != null) { beatmap.envColorRight = beatmap.colorRight; } if (d["_customData"]["_obstacleColor"] != null) { beatmap.obstacleColor = GetColorFromJSONNode(d["_customData"]["_obstacleColor"]); } beatmap.UpdateName(d["_beatmapFilename"]); set.difficultyBeatmaps.Add(beatmap); } set.difficultyBeatmaps = set.difficultyBeatmaps.OrderBy(x => x.difficultyRank).ToList(); song.difficultyBeatmapSets.Add(set); } song.difficultyBeatmapSets = song.difficultyBeatmapSets.OrderBy(x => SongInfoEditUI.CharacteristicDropdownToBeatmapName.IndexOf(x.beatmapCharacteristicName)).ToList(); break; } } return(song); } catch (Exception e) { Debug.LogError(e); return(null); } }
private IEnumerator GetBeatmapFromLocation(Uri uri) { // We will extract the contents of the zip to the temp directory, so we will save the zip in memory. DownloadHandlerBuffer downloadHandler = new DownloadHandlerBuffer(); // Create our web request, and set our handler. UnityWebRequest request = UnityWebRequest.Get(uri); request.downloadHandler = downloadHandler; // Change our User-Agent so BeatSaver can download our map request.SetRequestHeader("User-Agent", $"{Application.productName}/{Application.version}"); // Set progress bar state. PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true); PersistentUI.Instance.LevelLoadSlider.value = 0; PersistentUI.Instance.LevelLoadSliderLabel.text = $"Downloading file... Starting download..."; var operation = request.SendWebRequest(); while (!request.isDone) { // Grab Content-Length, which is the length of the downloading file, to use for progress bar. if (int.TryParse(request.GetResponseHeader("Content-Length"), out int length)) { float progress = downloadHandler.data.Length / (float)length; PersistentUI.Instance.LevelLoadSlider.value = progress; float percent = progress * 100; PersistentUI.Instance.LevelLoadSliderLabel.text = $"Downloading file... {percent:F2}% complete."; } else { // Just gives the bar something to do until we get the content length. PersistentUI.Instance.LevelLoadSlider.value = (Mathf.Sin(Time.time) / 2) + 0.5f; } // Cancel loading if an error has occurred. if (request.isHttpError || request.isNetworkError) { CancelTempLoader(request.error); yield break; } yield return(new WaitForEndOfFrame()); } // Check one more time to be safe. if (request.isHttpError || request.isNetworkError) { CancelTempLoader(request.error); yield break; } // Wahoo! We are done. Let's grab our downloaded data. byte[] downloaded = downloadHandler.data; // If the request failed, our downloaded bytes will be null. Let's check that. if (downloaded != null) { PersistentUI.Instance.LevelLoadSlider.value = 1; PersistentUI.Instance.LevelLoadSliderLabel.text = "Extracting contents..."; yield return(new WaitForEndOfFrame()); try { // Slap our downloaded bytes into a memory stream and slap that into a ZipArchive. MemoryStream stream = new MemoryStream(downloaded); ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read); // Create the directory for our song to go to. // Path.GetTempPath() should be compatible with Windows and UNIX. // See Microsoft docs on it. string directory = $"{Path.GetTempPath()}ChroMapper Temp Loader\\{request.GetHashCode()}"; if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // Extract our zipped file into this directory. archive.ExtractToDirectory(directory); // Dispose our downloaded bytes, we don't need them. downloadHandler.Dispose(); // Try and get a BeatSaberSong out of what we've downloaded. BeatSaberSong song = BeatSaberSong.GetSongFromFolder(directory); if (song != null) { PersistentUI.Instance.LevelLoadSliderLabel.text = "Loading song..."; BeatSaberSongContainer.Instance.song = song; } else { CancelTempLoader("Could not obtain a valid Beatmap from the downloaded content."); } } catch (Exception e) { // Uh oh, an error occurred. // Let's see if it is due to user error, or a genuine error on ChroMapper's part. switch (e.GetType().Name) { // InvalidDataException means that the ZipArchive cannot be created. // ChroMapper tried to download something that is not a zip. case nameof(InvalidDataException): CancelTempLoader($"Downloaded content was not a valid zip."); break; // Default case is a genuine error, let's print what it has to say. default: CancelTempLoader($"An unknown error ({e.GetType().Name}) has occurred:\n\n{e.Message}"); break; } } } else { CancelTempLoader("Downloaded bytes is somehow null, yet the request was successfully completed. WTF!?"); } }
private void Start() { song = BeatSaberSongContainer.Instance.song; diff = BeatSaberSongContainer.Instance.difficultyData; map = BeatSaberSongContainer.Instance.map; }
public IEnumerator LoadMap() { if (BeatSaberSongContainer.Instance == null) { yield break; } PersistentUI.Instance.LevelLoadSliderLabel.text = ""; yield return(new WaitUntil(() => atsc.gridStartPosition != -1)); //I need a way to find out when Start has been called. song = BeatSaberSongContainer.Instance.song; //Grab songe data diff = BeatSaberSongContainer.Instance.difficultyData; //Set up some local variables int environmentID = 0; bool customPlat = false; bool directional = false; environmentID = SongInfoEditUI.GetEnvironmentIDFromString(song.environmentName); //Grab platform by name (Official or Custom) if (song.customData != null && ((song.customData["_customEnvironment"] != null && song.customData["_customEnvironment"].Value != ""))) { if (CustomPlatformsLoader.Instance.GetAllEnvironmentIds().IndexOf(song.customData["_customEnvironment"] ?? "") >= 0) { customPlat = true; } } if (rotationController.IsActive && diff.parentBeatmapSet.beatmapCharacteristicName != "Lawless") { environmentID = SongInfoEditUI.GetDirectionalEnvironmentIDFromString(song.allDirectionsEnvironmentName); customPlat = false; directional = true; } //Instantiate platform, grab descriptor GameObject platform = (customPlat ? CustomPlatformsLoader.Instance.LoadPlatform(song.customData["_customEnvironment"], (PlatformPrefabs[environmentID]) ?? PlatformPrefabs[0], null) : PlatformPrefabs[environmentID]) ?? PlatformPrefabs[0]; if (directional) { platform = DirectionalPlatformPrefabs[environmentID]; } GameObject instantiate = null; if (customPlat) { instantiate = platform; } else { Debug.Log("Instanciate nonCustomPlat"); instantiate = Instantiate(platform, PlatformOffset, Quaternion.identity) as GameObject; } PlatformDescriptor descriptor = instantiate.GetComponent <PlatformDescriptor>(); BeatmapEventContainer.ModifyTypeMode = descriptor.SortMode; //Change sort mode //Update Colors Color leftNote = BeatSaberSong.DEFAULT_LEFTNOTE; //Have default note as base if (descriptor.RedColor != BeatSaberSong.DEFAULT_LEFTCOLOR) { leftNote = descriptor.RedColor; //Prioritize platforms } if (diff.colorLeft != BeatSaberSong.DEFAULT_LEFTNOTE) { leftNote = diff.colorLeft; //Then prioritize custom colors } Color rightNote = BeatSaberSong.DEFAULT_RIGHTNOTE; if (descriptor.BlueColor != BeatSaberSong.DEFAULT_RIGHTCOLOR) { rightNote = descriptor.BlueColor; } if (diff.colorRight != BeatSaberSong.DEFAULT_RIGHTNOTE) { rightNote = diff.colorRight; } notesContainer.UpdateColor(leftNote, rightNote); obstaclesContainer.UpdateColor(diff.obstacleColor); if (diff.colorLeft != BeatSaberSong.DEFAULT_LEFTNOTE) { descriptor.RedColor = diff.colorLeft; } if (diff.colorRight != BeatSaberSong.DEFAULT_RIGHTNOTE) { descriptor.BlueColor = diff.colorRight; } if (diff.envColorLeft != BeatSaberSong.DEFAULT_LEFTCOLOR) { descriptor.RedColor = diff.envColorLeft; } if (diff.envColorRight != BeatSaberSong.DEFAULT_RIGHTCOLOR) { descriptor.BlueColor = diff.envColorRight; } PlatformLoadedEvent.Invoke(descriptor); //Trigger event for classes that use the platform loader.UpdateMapData(BeatSaberSongContainer.Instance.map); yield return(StartCoroutine(loader.HardRefresh())); LevelLoadedEvent?.Invoke(); }
public void SelectSongForEditing(BeatSaberSong song) { this.song = song; SceneTransitionManager.Instance.LoadScene(2); }
public IEnumerator LoadMap() { if (BeatSaberSongContainer.Instance == null) { yield break; } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true); PersistentUI.Instance.LevelLoadSliderLabel.text = ""; yield return(new WaitUntil(() => atsc.gridStartPosition != -1)); //I need a way to find out when Start has been called. song = BeatSaberSongContainer.Instance.song; //Grab songe data diff = BeatSaberSongContainer.Instance.difficultyData; //Set up some local variables int environmentID = 0; int batchSize = Settings.Instance.InitialLoadBatchSize; bool customPlat = false; environmentID = SongInfoEditUI.GetEnvironmentIDFromString(song.environmentName); //Grab platform by name (Official or Custom) if (song.customData != null && song.customData["_customEnvironment"] != null && song.customData["_customEnvironment"].Value != "") { if (SongInfoEditUI.GetCustomPlatformsIndexFromString(song.customData["_customEnvironment"]) >= 0) { environmentID = SongInfoEditUI.GetCustomPlatformsIndexFromString(song.customData["_customEnvironment"]); customPlat = true; } } //Instantiate platform, grab descriptor GameObject platform = (customPlat ? CustomPlatformPrefabs[environmentID] : PlatformPrefabs[environmentID]) ?? PlatformPrefabs[0]; GameObject instantiate = Instantiate(platform, new Vector3(0, -0.5f, -1.5f), Quaternion.identity); PlatformDescriptor descriptor = instantiate.GetComponent <PlatformDescriptor>(); BeatmapEventContainer.ModifyTypeMode = descriptor.SortMode; //Change sort mode //Update Colors Color leftNote = BeatSaberSong.DEFAULT_LEFTNOTE; //Have default note as base if (descriptor.RedColor != BeatSaberSong.DEFAULT_LEFTCOLOR) { leftNote = descriptor.RedColor; //Prioritize platforms } if (diff.colorLeft != BeatSaberSong.DEFAULT_LEFTNOTE) { leftNote = diff.colorLeft; //Then prioritize custom colors } Color rightNote = BeatSaberSong.DEFAULT_RIGHTNOTE; if (descriptor.BlueColor != BeatSaberSong.DEFAULT_RIGHTCOLOR) { rightNote = descriptor.BlueColor; } if (diff.colorRight != BeatSaberSong.DEFAULT_RIGHTNOTE) { rightNote = diff.colorRight; } notesContainer.UpdateColor(leftNote, rightNote); obstaclesContainer.UpdateColor(diff.obstacleColor); if (diff.colorLeft != BeatSaberSong.DEFAULT_LEFTNOTE) { descriptor.RedColor = diff.colorLeft; } if (diff.colorRight != BeatSaberSong.DEFAULT_RIGHTNOTE) { descriptor.BlueColor = diff.colorRight; } if (diff.envColorLeft != BeatSaberSong.DEFAULT_LEFTCOLOR) { descriptor.RedColor = diff.envColorLeft; } if (diff.envColorRight != BeatSaberSong.DEFAULT_RIGHTCOLOR) { descriptor.BlueColor = diff.envColorRight; } PlatformLoadedEvent.Invoke(descriptor); //Trigger event for classes that use the platform map = BeatSaberSongContainer.Instance.map; //Grab map info, do some stuff int noteLaneSize = 2; //Half of it, anyways int noteLayerSize = 3; Queue <BeatmapObject> queuedData = new Queue <BeatmapObject>( //Take all of our object data and combine them for batch loading. map._notes.Concat <BeatmapObject>(map._obstacles).Concat(map._events).Concat(map._BPMChanges).Concat(map._customEvents)); totalObjectsToLoad = queuedData.Count; if (map != null) { while (queuedData.Count > 0) { //Batch loading is loading a certain amount of objects (Batch Size) every frame, so at least ChroMapper remains active. for (int i = 0; i < batchSize; i++) { if (queuedData.Count == 0) { break; } BeatmapObject data = queuedData.Dequeue(); //Dequeue and load them into ChroMapper. if (data is BeatmapNote noteData) { BeatmapNoteContainer beatmapNote = notesContainer.SpawnObject(noteData, out _) as BeatmapNoteContainer; if (noteData._lineIndex >= 1000 || noteData._lineIndex <= -1000 || noteData._lineLayer >= 1000 || noteData._lineLayer <= -1000) { continue; } if (2 - noteData._lineIndex > noteLaneSize) { noteLaneSize = 2 - noteData._lineIndex; } if (noteData._lineIndex - 1 > noteLaneSize) { noteLaneSize = noteData._lineIndex - 1; } if (noteData._lineLayer + 1 > noteLayerSize) { noteLayerSize = noteData._lineLayer + 1; } } else if (data is BeatmapObstacle obstacleData) { BeatmapObstacleContainer beatmapObstacle = obstaclesContainer.SpawnObject(obstacleData, out _) as BeatmapObstacleContainer; if (obstacleData._lineIndex >= 1000 || obstacleData._lineIndex <= -1000) { continue; } if (2 - obstacleData._lineIndex > noteLaneSize) { noteLaneSize = 2 - obstacleData._lineIndex; } if (obstacleData._lineIndex - 1 > noteLaneSize) { noteLaneSize = obstacleData._lineIndex - 1; } } else if (data is MapEvent eventData) { eventsContainer.SpawnObject(eventData, out _); } else if (data is BeatmapBPMChange bpmData) { bpmContainer.SpawnObject(bpmData, out _); } else if (data is BeatmapCustomEvent customData) { customEventsContainer.SpawnObject(customData, out _); } } UpdateSlider(batchSize); yield return(new WaitForEndOfFrame()); } notesContainer.SortObjects(); //Sort these boyes. obstaclesContainer.SortObjects(); eventsContainer.SortObjects(); bpmContainer.SortObjects(); customEventsContainer.SortObjects(); noteGrid.localScale = new Vector3((float)(noteLaneSize * 2) / 10 + 0.01f, 1, 1); //Set note lanes appropriately } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(false); //Disable progress bar LevelLoadedEvent?.Invoke(); }
public bool Save() { try { /* * LISTS */ //Just in case, I'm moving this up here System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; _events = _events.OrderBy(x => x._time).ToList(); _notes = _notes.OrderBy(x => x._time).ToList(); _obstacles = _obstacles.OrderBy(x => x._time).ToList(); _BPMChanges = _BPMChanges.OrderBy(x => x._time).ToList(); _bookmarks = _bookmarks.OrderBy(x => x._time).ToList(); _customEvents = _customEvents.OrderBy(x => x._time).ThenBy(x => x._type).ToList(); if (mainNode is null) { mainNode = new JSONObject(); } mainNode["_version"] = _version; JSONArray events = new JSONArray(); foreach (MapEvent e in _events) { events.Add(e.ConvertToJSON()); } JSONArray notes = new JSONArray(); foreach (BeatmapNote n in _notes) { notes.Add(n.ConvertToJSON()); } JSONArray obstacles = new JSONArray(); foreach (BeatmapObstacle o in _obstacles) { obstacles.Add(o.ConvertToJSON()); } JSONArray bpm = new JSONArray(); foreach (BeatmapBPMChange b in _BPMChanges) { bpm.Add(b.ConvertToJSON()); } JSONArray bookmarks = new JSONArray(); foreach (BeatmapBookmark b in _bookmarks) { bookmarks.Add(b.ConvertToJSON()); } JSONArray customEvents = new JSONArray(); foreach (BeatmapCustomEvent c in _customEvents) { customEvents.Add(c.ConvertToJSON()); } mainNode["_notes"] = notes; mainNode["_obstacles"] = obstacles; mainNode["_events"] = events; /* * According to new the new BeatSaver schema, which will be enforced sometime soon™, * Bookmarks, Custom Events, and BPM Changes are now pushed to _customData instead of being on top level. * * Private MM should already has this updated, however public MM will need a PR by someone, or maybe squeaksies if he * wants to go against his own words and go back to that. * * Since these are editor only things, it's fine if I implement them now. Besides, CM reads both versions anyways. */ mainNode["_customData"] = new JSONObject(); if (_BPMChanges.Any()) { mainNode["_customData"]["_BPMChanges"] = bpm; } if (_bookmarks.Any()) { mainNode["_customData"]["_bookmarks"] = bookmarks; } if (_customEvents.Any()) { mainNode["_customEvents"] = customEvents; } if (_time > 0) { mainNode["_customData"]["_time"] = Math.Round(_time, 3); } BeatSaberSong.CleanObject(mainNode["_customData"]); if (!mainNode["_customData"].Children.Any()) { mainNode.Remove("_customData"); } using (StreamWriter writer = new StreamWriter(directoryAndFile, false)) { //Advanced users might want human readable JSON to perform easy modifications and reload them on the fly. //Thus, ChroMapper "beautifies" the JSON if you are in advanced mode. if (Settings.Instance.AdvancedShit) { writer.Write(mainNode.ToString(2)); } else { writer.Write(mainNode.ToString()); } } return(true); } catch (Exception e) { Debug.LogException(e); return(false); } }
/// <summary> /// Queues an operation for adding a song to a playlist /// </summary> /// <param name="songID">The song ID to use for the imported song</param> /// <param name="songPath">The path, RELATIVE TO THE BEATONDATA ROOT, of where the custom song exists</param> /// <param name="playlist">The playlist to add the song to</param> private AssetOp QueueAddSongToPlaylistOp(string songID, string songPath, BeatSaberPlaylist playlist, Action completionCallback = null, bool suppressToast = false) { var qae = _getEngine(); var bsSong = new BeatSaberSong() { SongID = songID, //ref: was Path.GetFileName(toInst.DownloadPath), CustomSongPath = songPath //ref: was toInst.DownloadPath }; var addOp = new AddNewSongToPlaylistOp(bsSong, playlist.PlaylistID); addOp.OpFinished += (s, op) => { //TODO: i'd like for this to come back out of the config rather than being added here if (!playlist.SongList.Any(x => x.SongID == bsSong.SongID)) { playlist.SongList.Add(bsSong); } if (op.Status == OpStatus.Complete) { if (!suppressToast) { _showToast($"Song Added", $"{songID} was downloaded and added successfully", ClientModels.ToastType.Success); } } else if (op.Status == OpStatus.Failed) { if (op.Exception as AddSongException != null) { var ex = op.Exception as AddSongException; if (ex.FailType == AddSongFailType.SongExists) { //don't show toast for a song already existing } else if (ex.FailType == AddSongFailType.InvalidFormat) { if (!suppressToast) { _showToast($"Song Invalid", $"{songID} failed to import, it wasn't in a valid format.", ClientModels.ToastType.Error); } } else { if (!suppressToast) { _showToast($"Song Failed", $"{songID} failed to import!", ClientModels.ToastType.Error); } } } else { if (!suppressToast) { _showToast($"Song Failed", $"{songID} failed to import!", ClientModels.ToastType.Error); } } } completionCallback?.Invoke(); }; qae.OpManager.QueueOp(addOp); return(addOp); }
public IEnumerator LoadMap() { if (BeatSaberSongContainer.Instance == null) { yield break; } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true); yield return(new WaitUntil(() => atsc.gridStartPosition != -1)); //I need a way to find out when Start has been called. song = BeatSaberSongContainer.Instance.song; float offset = 0; int environmentID = 0; int batchSize = Settings.Instance.InitialLoadBatchSize; bool customPlat = false; environmentID = SongInfoEditUI.GetEnvironmentIDFromString(song.environmentName); if (song.customData != null && song.customData["_customEnvironment"] != null && song.customData["_customEnvironment"].Value != "") { environmentID = SongInfoEditUI.GetCustomPlatformsIndexFromString(song.customData["_customEnvironment"]); customPlat = true; } GameObject platform = (customPlat ? CustomPlatformPrefabs[environmentID] : PlatformPrefabs[environmentID]) ?? PlatformPrefabs[0]; GameObject instantiate = Instantiate(platform, new Vector3(0, -0.5f, -1.5f), Quaternion.identity); PlatformDescriptor descriptor = instantiate.GetComponent <PlatformDescriptor>(); BeatmapEventContainer.ModifyTypeMode = descriptor.SortMode; PlatformLoadedEvent.Invoke(descriptor); descriptor.RedColor = BeatSaberSongContainer.Instance.difficultyData.colorLeft; descriptor.BlueColor = BeatSaberSongContainer.Instance.difficultyData.colorRight; map = BeatSaberSongContainer.Instance.map; offset = (song.beatsPerMinute / 60) * (BeatSaberSongContainer.Instance.song.songTimeOffset / 1000); int noteLaneSize = 2; //Half of it, anyways int noteLayerSize = 3; Queue <BeatmapObject> queuedData = new Queue <BeatmapObject>( //Take all of our object data and combine them for batch loading. map._notes.Concat <BeatmapObject>(map._obstacles).Concat(map._events).Concat(map._BPMChanges)); totalObjectsToLoad = queuedData.Count; if (map != null) { while (queuedData.Count > 0) { for (int i = 0; i < batchSize; i++) { if (queuedData.Count == 0) { break; } BeatmapObject data = queuedData.Dequeue(); if (data is BeatmapNote noteData) { BeatmapNoteContainer beatmapNote = notesContainer.SpawnObject(noteData) as BeatmapNoteContainer; if (noteData._lineIndex >= 1000 || noteData._lineIndex <= -1000 || noteData._lineLayer >= 1000 || noteData._lineLayer <= -1000) { continue; } if (2 - noteData._lineIndex > noteLaneSize) { noteLaneSize = 2 - noteData._lineIndex; } if (noteData._lineIndex - 1 > noteLaneSize) { noteLaneSize = noteData._lineIndex - 1; } if (noteData._lineLayer + 1 > noteLayerSize) { noteLayerSize = noteData._lineLayer + 1; } } else if (data is BeatmapObstacle obstacleData) { BeatmapObstacleContainer beatmapObstacle = obstaclesContainer.SpawnObject(obstacleData) as BeatmapObstacleContainer; if (obstacleData._lineIndex >= 1000 || obstacleData._lineIndex <= -1000) { continue; } if (2 - obstacleData._lineIndex > noteLaneSize) { noteLaneSize = 2 - obstacleData._lineIndex; } if (obstacleData._lineIndex - 1 > noteLaneSize) { noteLaneSize = obstacleData._lineIndex - 1; } } else if (data is MapEvent eventData) { eventsContainer.SpawnObject(eventData); } else if (data is BeatmapBPMChange bpmData) { bpmContainer.SpawnObject(bpmData); } } UpdateSlider(batchSize); yield return(new WaitForEndOfFrame()); } notesContainer.SortObjects(); obstaclesContainer.SortObjects(); eventsContainer.SortObjects(); bpmContainer.SortObjects(); noteGrid.localScale = new Vector3((float)(noteLaneSize * 2) / 10 + 0.01f, 1, 1); } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(false); }