internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(Playlist.PlaylistID)) { throw new Exception("PlaylistID must be provided."); } if (string.IsNullOrEmpty(Playlist.PlaylistName)) { throw new Exception($"PlaylistName must be provided for ID {Playlist.PlaylistName}"); } var songsAssetFile = context.Engine.GetSongsAssetsFile(); BeatmapLevelPackObject levelPack = null; if (context.Cache.PlaylistCache.ContainsKey(Playlist.PlaylistID)) { Log.LogMsg($"Playlist {Playlist.PlaylistID} will be updated"); levelPack = context.Cache.PlaylistCache[Playlist.PlaylistID].Playlist; levelPack.PackName = Playlist.PlaylistName; Playlist.LevelPackObject = levelPack; OpCommon.UpdateCoverImage(Playlist, context, songsAssetFile); } else { levelPack = OpCommon.CreatePlaylist(context, Playlist, songsAssetFile); } }
internal override void PerformOp(OpContext context) { var asset = _locator.Locate(context.Manager, false); string oldName = null; if (asset as IHaveName != null && !_allowOverwriteName) { oldName = (asset as IHaveName).Name; } using (var ms = new MemoryStream(_replaceDataWith)) { using (AssetsReader reader = new AssetsReader(ms)) { asset.ObjectInfo.DataOffset = 0; asset.ObjectInfo.DataSize = _replaceDataWith.Length; asset.Parse(reader); asset.ObjectInfo.DataOffset = -1; asset.ObjectInfo.DataSize = -1; if (oldName != null) { (asset as IHaveName).Name = oldName; } } } }
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(PlaylistID)) { throw new InvalidOperationException("PlaylistID must be provided."); } if (!context.Cache.PlaylistCache.ContainsKey((PlaylistID))) { throw new InvalidOperationException("Playlist ID was not found."); } if (Index < 0 || Index > context.Cache.PlaylistCache.Count) { throw new InvalidOperationException("Index is out of range."); } var mainCol = context.Engine.GetMainLevelPack(); var ptr = mainCol.BeatmapLevelPacks.FirstOrDefault(x => x.Object.PackID == PlaylistID); if (ptr == null) { throw new Exception("Unable to find this playlist in the main level pack! Cache is out of sync!"); } var packIndex = mainCol.BeatmapLevelPacks.IndexOf(ptr); mainCol.BeatmapLevelPacks.Remove(ptr); var newIndex = Index; if (newIndex >= mainCol.BeatmapLevelPacks.Count) { mainCol.BeatmapLevelPacks.Add(ptr); } else { mainCol.BeatmapLevelPacks.Insert(newIndex, ptr); } //just to be on the safe side, operate on the cache separately and reorder it var playlist = context.Cache.PlaylistCache[PlaylistID]; var ordered = context.Cache.PlaylistCache.Values.OrderBy(x => x.Order).ToList(); var oldIndex = ordered.IndexOf(playlist); ordered.Remove(playlist); newIndex = Index; if (newIndex >= ordered.Count) { ordered.Add(playlist); } else { ordered.Insert(newIndex, playlist); } for (int i = 0; i < ordered.Count; i++) { ordered[i].Order = i; } }
public static BeatmapLevelPackObject CreatePlaylist(OpContext context, BeatSaberPlaylist playlist, AssetsFile songsAssetFile) { Log.LogMsg($"Playlist {playlist.PlaylistID} will be created"); var levelPack = new BeatmapLevelPackObject(songsAssetFile) { Enabled = 1, GameObject = null, IsPackAlwaysOwned = true, PackID = playlist.PlaylistID, Name = playlist.PlaylistID + BSConst.NameSuffixes.LevelPack, PackName = playlist.PlaylistName }; songsAssetFile.AddObject(levelPack, true); var col = new BeatmapLevelCollectionObject(songsAssetFile) { Name = playlist.PlaylistID + BSConst.NameSuffixes.LevelCollection }; songsAssetFile.AddObject(col, true); levelPack.BeatmapLevelCollection = col.PtrFrom(levelPack); playlist.LevelPackObject = levelPack; var mainCol = context.Engine.GetMainLevelPack(); var aoPacks = context.Engine.GetAlwaysOwnedModel(); mainCol.BeatmapLevelPacks.Add(levelPack.PtrFrom(mainCol)); aoPacks.AlwaysOwnedPacks.Add(levelPack.PtrFrom(aoPacks)); context.Cache.PlaylistCache.Add(playlist.PlaylistID, new PlaylistAndSongs() { Playlist = levelPack, Order = context.Cache.PlaylistCache.Count }); UpdateCoverImage(playlist, context, songsAssetFile); return(levelPack); }
internal override void PerformOp(OpContext context) { var colorManager = context.Manager.MassFirstOrDefaultAsset <ColorManager>(x => true, false)?.Object; if (colorManager == null) { Log.LogErr($"ChangeColorOp could not get the ColorManager class from assets!"); throw new Exception("Unable to find the color manager asset!"); } SimpleColorSO color = null; switch (_colorType) { case ColorType.LeftColor: color = colorManager.ColorA.Object; break; case ColorType.RightColor: color = colorManager.ColorB.Object; break; } if (color == null) { Log.LogErr($"Color type {_colorType} not found!"); throw new Exception($"Color type {_colorType} not found!"); } color.Color.A = (float)_color.A / (float)255; color.Color.R = (float)_color.R / (float)255; color.Color.G = (float)_color.G / (float)255; color.Color.B = (float)_color.B / (float)255; color.ObjectInfo.ParentFile.HasChanges = true; }
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(SongID)) { throw new InvalidOperationException("SongID must be provided."); } if (!context.Cache.SongCache.ContainsKey(SongID)) { throw new InvalidOperationException("SongID does not exist."); } OpCommon.DeleteSong(context, SongID); }
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(PlaylistID)) { throw new InvalidOperationException("Playlist ID must be provided."); } if (!context.Cache.PlaylistCache.ContainsKey(PlaylistID)) { throw new InvalidOperationException("Playlist ID does not exist."); } if (PlaylistID == "CustomSongs") { throw new InvalidOperationException("Don't delete CustomSongs playlist, it's needed for things."); } OpCommon.DeletePlaylist(context, PlaylistID, DeleteSongsOnPlaylist); }
internal override void PerformOp(OpContext context) { Comparer <SongAndPlaylist> comparer; Func <SongAndPlaylist, string> nameGetter; switch (SortMode) { case PlaylistSortMode.Name: nameGetter = (s) => { if (s.Song.SongName?.Length < 1) { return(""); } return(s.Song.SongName.Substring(0, 1).ToUpper()); }; comparer = Comparer <SongAndPlaylist> .Create((s1, s2) => { try { return(s1.Song.SongName.ToUpper().CompareTo(s2.Song.SongName.ToUpper())); } catch { return(-1); } }); break; case PlaylistSortMode.MaxDifficulty: nameGetter = (s) => { var max = s.Song?.DifficultyBeatmapSets?.SelectMany(x => x?.DifficultyBeatmaps)?.Max(x => x?.Difficulty); if (max == null) { return(Difficulty.Easy.ToString()); } return(max.ToString()); }; comparer = Comparer <SongAndPlaylist> .Create((s1, s2) => { try { return(s1.Song.DifficultyBeatmapSets.SelectMany(x => x.DifficultyBeatmaps).Max(x => x.Difficulty) .CompareTo(s2.Song.DifficultyBeatmapSets.SelectMany(x => x.DifficultyBeatmaps).Max(x => x.Difficulty))); } catch { return(-1); } }); break; case PlaylistSortMode.LevelAuthor: nameGetter = (s) => { return(s.Song?.LevelAuthorName?.ToUpper() ?? ""); }; comparer = Comparer <SongAndPlaylist> .Create((s1, s2) => { try { return(s1.Song.LevelAuthorName.ToUpper().CompareTo(s2.Song.LevelAuthorName.ToUpper())); } catch { return(-1); } }); break; default: throw new NotImplementedException("Unhandled playlist sort mode."); } var songList = context.Cache.SongCache.Values.ToArray(); Array.Sort(songList, comparer); var songsAssetFile = context.Engine.GetSongsAssetsFile(); PlaylistAndSongs currentPlaylist = null; int plCtr = 0; string previousSongName = ""; for (int i = 0; i < songList.Length; i++) { var song = songList[i]; if (BSConst.KnownLevelIDs.Contains(song.Song.LevelID) || BSConst.KnownLevelPackIDs.Contains(song.Playlist.PackID)) { continue; } var curSongName = nameGetter(song); var packID = $"Auto_{curSongName}"; if (currentPlaylist == null || currentPlaylist.Playlist.PackID != packID) { bool newPlaylist = true; if (currentPlaylist != null && SortMode == PlaylistSortMode.Name) { if (plCtr < MaxPerNamePlaylist) { newPlaylist = false; } else { currentPlaylist.Playlist.PackName += " - " + previousSongName; } } if (newPlaylist) { if (!context.Cache.PlaylistCache.ContainsKey(packID)) { OpCommon.CreatePlaylist(context, new Models.BeatSaberPlaylist() { PlaylistID = packID, PlaylistName = curSongName }, songsAssetFile); currentPlaylist = context.Cache.PlaylistCache[packID]; } else { currentPlaylist = context.Cache.PlaylistCache[packID]; currentPlaylist.Playlist.PackName = curSongName; } plCtr = 0; } } //update assets var oldPl = context.Cache.PlaylistCache[song.Playlist.PackID]; var oldPtr = oldPl.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Where(x => x.Object.LevelID == song.Song.LevelID).First(); oldPtr.Dispose(); oldPl.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Remove(oldPtr); currentPlaylist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Add(song.Song.PtrFrom(currentPlaylist.Playlist.BeatmapLevelCollection.Object)); //update cache oldPl.Songs.Remove(song.Song.LevelID); song.Playlist = currentPlaylist.Playlist; currentPlaylist.Songs.Add(song.Song.LevelID, new OrderedSong() { Song = song.Song, Order = plCtr }); plCtr++; previousSongName = curSongName; if (SortMode == PlaylistSortMode.Name && i == songList.Length - 1) { currentPlaylist.Playlist.PackName += " - " + curSongName; } } if (RemoveEmptyPlaylists) { foreach (var pl in context.Cache.PlaylistCache.ToList()) { //leave CustomSongs alone if (pl.Value.Songs.Count < 1 && pl.Key != "CustomSongs") { OpCommon.DeletePlaylist(context, pl.Key, false); } } } }
public static void DeleteSong(OpContext context, string songID) { var songsAssetFile = context.Engine.GetSongsAssetsFile(); var song = context.Cache.SongCache[songID]; var songPtr = song.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.FirstOrDefault(x => x.Object == song.Song); if (songPtr == null) { throw new Exception($"Song pointer is null trying to delete song ID {songID}"); } try { song.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Remove(songPtr); songPtr.Dispose(); //don't delete built in songs if (!BSConst.KnownLevelIDs.Contains(songID)) { if (song.Song.AudioClip?.Target?.Object != null) { songsAssetFile.DeleteObject(song.Song.AudioClip.Object); } else { Log.LogErr($"Trying to delete song ID {songID} and audio clip is null!"); } if (song.Song?.CoverImageTexture2D?.Target?.Object != null) { songsAssetFile.DeleteObject(song.Song.CoverImageTexture2D.Object); } else { Log.LogErr($"Trying to delete song ID {songID} and cover image is null!"); } if (song.Song.CoverImageTexture2D != null) { song.Song.CoverImageTexture2D.Dispose(); } if (song?.Song?.DifficultyBeatmapSets == null) { Log.LogErr($"Trying to delete song id {songID}, but DifficultyBeatmapSets is null!"); } else { song.Song.DifficultyBeatmapSets.ForEach(x => { if (x?.DifficultyBeatmaps == null) { Log.LogErr($"Trying to delete song id {songID} beatmapset, but DifficultyBeatmaps is null!"); } else { x.DifficultyBeatmaps.ForEach(y => { if (y != null && y.BeatmapDataPtr != null) { if (y?.BeatmapDataPtr?.Object != null) { songsAssetFile.DeleteObject(y.BeatmapDataPtr.Object); } else { Log.LogErr($"Trying to delete song id {songID} but BeatmapDataPtr.Object is null!"); } y.BeatmapDataPtr.Dispose(); } else { Log.LogErr($"Trying to delete song id {songID} but BeatmapDataPtr is null!"); } }); } x.BeatmapCharacteristic.Dispose(); }); } songsAssetFile.DeleteObject(song.Song); context.Engine.QueuedFileOperations.Add(new QueuedFileOp() { Tag = song.Song.LevelID, Type = QueuedFileOperationType.DeleteFolder, TargetPath = context.Config.SongsPath.CombineFwdSlash(songID) }); } context.Cache.SongCache.Remove(song.Song.LevelID); context.Cache.PlaylistCache[song.Playlist.PackID].Songs.Remove(song.Song.LevelID); } catch (Exception ex) { Log.LogErr($"Exception deleting song ID {songID}!", ex); } }
internal override void PerformOp(OpContext context) { context.Engine.QueuedFileOperations.Add(this); }
public static void DeletePlaylist(OpContext context, string playlistID, bool deleteSongsOnPlaylist) { var playlist = context.Cache.PlaylistCache[playlistID]; if (deleteSongsOnPlaylist) { try { foreach (var song in playlist.Songs.ToList()) { try { OpCommon.DeleteSong(context, song.Key); } catch (Exception ex) { Log.LogErr($"Exception trying to delete song id {song.Key} while deleting playlist id {playlist.Playlist.PackID}, will be unlinked in cache. This may leave unused data in the assets.", ex); try { context.Cache.SongCache.Remove(song.Value.Song.LevelID); playlist.Songs.Remove(song.Value.Song.LevelID); } catch (Exception ex2) { Log.LogErr($"Exception cleaning up cache for song id {song.Key} in playlist if {playlist.Playlist.PackID} while recovering from a failed delete.", ex2); } } } } catch (Exception ex) { //really this shouldn't ever get hit anymore. Probably can remove it. Log.LogErr($"Deleting songs on playlist ID {playlist?.Playlist?.PackID} failed! Attempting to recover by removing links to songs, although this may leave extra stuff in assets!"); try { var bmCol = playlist?.Playlist?.BeatmapLevelCollection?.Object?.BeatmapLevels?.ToList(); if (bmCol != null) { bmCol.ForEach(x => { playlist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Remove(x); x.Dispose(); }); } } catch (Exception ex2) { Log.LogErr($"Failed to recover by removing song pointers while deleting playlist {playlist?.Playlist?.PackID}! This will definitely leave stuff in assets.", ex2); } } playlist.Songs.Clear(); } //this should be done in song delete already //playlist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.ForEach(x => { x.Target.ParentFile.DeleteObject(x.Object); x.Dispose(); }); var mlp = context.Engine.GetMainLevelPack(); var aop = context.Engine.GetAlwaysOwnedModel(); var mlpptr = mlp.BeatmapLevelPacks.FirstOrDefault(x => x.Object.PackID == playlist.Playlist.PackID); var aopptr = aop.AlwaysOwnedPacks.FirstOrDefault(x => x.Object.PackID == playlist.Playlist.PackID); if (mlpptr != null) { mlp.BeatmapLevelPacks.Remove(mlpptr); mlpptr.Dispose(); } else { Log.LogErr($"The playlist id {playlist.Playlist.PackID} didn't exist in the main level packs"); } if (aopptr != null) { aop.AlwaysOwnedPacks.Remove(aopptr); aopptr.Dispose(); } else { Log.LogErr($"The playlist id {playlist.Playlist.PackID} didn't exist in the always owned level packs"); } //don't delete built in packs assets, just unlink them if (!BSConst.KnownLevelPackIDs.Contains(playlist.Playlist.PackID)) { var plParent = playlist.Playlist.ObjectInfo.ParentFile; plParent.DeleteObject(playlist.Playlist.CoverImage.Object.RenderData.Texture.Object); playlist.Playlist.CoverImage.Object.RenderData.Texture.Dispose(); plParent.DeleteObject(playlist.Playlist.CoverImage.Object); plParent.DeleteObject(playlist.Playlist); playlist.Playlist.CoverImage.Dispose(); } context.Cache.PlaylistCache.Remove(playlistID); }
public static void UpdateCoverImage(BeatSaberPlaylist playlist, OpContext context, AssetsFile songsAssetFile) { CustomLevelLoader loader = new CustomLevelLoader(songsAssetFile, context.Config); if (playlist.CoverImageBytes != null && playlist.CoverImageBytes.Length > 0) { Log.LogMsg($"Loading cover art for playlist ID '{playlist.PlaylistID}'"); var oldCoverImage = playlist?.LevelPackObject?.CoverImage; var oldTex = playlist?.LevelPackObject?.CoverImage?.Object?.RenderData?.Texture; //todo: verify this is a good place to delete stuff try { playlist.CoverArtSprite = loader.LoadPackCover(playlist.PlaylistID, playlist.CoverImageBytes); playlist.LevelPackObject.CoverImage = playlist.CoverArtSprite.PtrFrom(playlist.LevelPackObject); } catch (Exception ex) { Log.LogErr("Exception in step 1!", ex); throw; } try { //don't erase base content from the assets, although this definitely leaks textures if you keep switching the stock level pack picture if (!BSConst.KnownLevelPackIDs.Contains(playlist.PlaylistID)) { if (oldTex != null) { if (oldTex?.Object != null) { songsAssetFile.DeleteObject(oldTex.Object); } oldTex.Dispose(); } if (oldCoverImage != null) { if (oldCoverImage.Object != null) { songsAssetFile.DeleteObject(oldCoverImage.Object); } oldCoverImage.Dispose(); } } } catch (Exception ex) { Log.LogErr("Exception trying to clean up playlist cover art! This may leak cover images!", ex); } } else { try { if (playlist.LevelPackObject.CoverImage != null) { playlist.CoverArtSprite = playlist.LevelPackObject.CoverImage.Object; } else { playlist.CoverArtSprite = loader.LoadPackCover(playlist.PlaylistID, null); } } catch (Exception ex) { Log.LogErr("Exception in the cover art sprite part!", ex); throw; } try { playlist.LevelPackObject.CoverImage = playlist.CoverArtSprite.PtrFrom(playlist.LevelPackObject); } catch (Exception ex) { Log.LogErr("Exception in the final step!", ex); throw; } } //try queueing a file write op to output the playlist cover image try { var tex = playlist.LevelPackObject?.CoverImage?.Object?.RenderData?.Texture?.Object; if (tex == null) { throw new Exception("Texture couldn't be loaded from the playlist even though it should have just been set..."); } var qfo = new QueuedFileOp() { TargetPath = context.Config.PlaylistsPath.CombineFwdSlash(playlist.PlaylistID + ".png"), Type = QueuedFileOperationType.WriteFile, SourceData = Utils.ImageUtils.Instance.TextureToPngBytes(tex) }; context.Engine.QueuedFileOperations.Add(qfo); } catch (Exception ex) { Log.LogErr($"Exception queueing write op for playlist art on {playlist.PlaylistID}!", ex); } }
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(SongID)) { throw new InvalidOperationException("SongID must be provided."); } if (string.IsNullOrEmpty(ToPlaylistID)) { throw new InvalidOperationException("ToPlaylistID must be provided."); } if (!context.Cache.SongCache.ContainsKey(SongID)) { throw new InvalidOperationException("SongID could not be found."); } if (!context.Cache.PlaylistCache.ContainsKey(ToPlaylistID)) { throw new InvalidOperationException("ToPlaylistID could not be found."); } var song = context.Cache.SongCache[SongID]; var toPlaylist = context.Cache.PlaylistCache[ToPlaylistID]; var songPtr = song.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.FirstOrDefault(x => x.Object == song.Song); if (songPtr == null) { throw new Exception($"Unable to find the song pointer for song id {song.Song.LevelID} in the playlist it is moving from ({song.Playlist.PackID})."); } song.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Remove(songPtr); songPtr.Dispose(); bool inserted = false; if (Index.HasValue) { if (Index.Value >= toPlaylist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Count) { Log.LogErr($"Song ID {SongID} moved to playlist {ToPlaylistID} but it specified an out of range index of {Index.Value}, it will be put at the end."); } else { toPlaylist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Insert(Index.Value, song.Song.PtrFrom(toPlaylist.Playlist)); inserted = true; } } if (!inserted) { toPlaylist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Add(song.Song.PtrFrom(toPlaylist.Playlist)); } //keep the cache updated context.Cache.PlaylistCache[song.Playlist.PackID].Songs.Remove(song.Song.LevelID); song.Playlist = toPlaylist.Playlist; toPlaylist.Songs.Add(song.Song.LevelID, new OrderedSong() { Song = song.Song, Order = toPlaylist.Songs.Count }); //if a song's been moved to a playlist, make sure it isn't going to be deleted var qfos = context.Engine.QueuedFileOperations.Where(x => x.Tag == SongID && x.Type == QueuedFileOperationType.DeleteFolder || x.Type == QueuedFileOperationType.DeleteFile).ToList(); foreach (var q in qfos) { context.Engine.QueuedFileOperations.Remove(q); } }
internal abstract void PerformOp(OpContext context);
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(SongID)) { throw new InvalidOperationException("SongID must be provided."); } if (!context.Cache.SongCache.ContainsKey(SongID)) { throw new InvalidOperationException("SongID was not found."); } var songCache = context.Cache.SongCache[SongID]; var playlistCache = context.Cache.PlaylistCache[songCache.Playlist.PackID]; if (Index < 0 || Index > playlistCache.Songs.Count) { throw new InvalidOperationException("Index is out of range."); } var ptr = playlistCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.FirstOrDefault(x => x.Object.LevelID == SongID); if (ptr == null) { throw new Exception("Unable to find song pointer! Cache is out of sync!"); } var songIndex = playlistCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.IndexOf(ptr); playlistCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Remove(ptr); var newIndex = Index; if (newIndex >= playlistCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Count) { playlistCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Add(ptr); } else { playlistCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Insert(newIndex, ptr); } //do the cache separately to be safe about it var ordered = playlistCache.Songs.Values.OrderBy(x => x.Order).ToList(); var sng = ordered.First(x => x.Song.LevelID == SongID); var oldIndex = ordered.IndexOf(sng); ordered.Remove(sng); newIndex = Index; if (newIndex >= ordered.Count) { ordered.Add(sng); } else { ordered.Insert(newIndex, sng); } for (int i = 0; i < ordered.Count; i++) { ordered[i].Order = i; } //if a song's been moved in a playlist, make sure it isn't going to be deleted var qfos = context.Engine.QueuedFileOperations.Where(x => x.Tag == SongID && x.Type == QueuedFileOperationType.DeleteFolder || x.Type == QueuedFileOperationType.DeleteFile).ToList(); foreach (var q in qfos) { context.Engine.QueuedFileOperations.Remove(q); } }
internal override void PerformOp(OpContext context) { context.Engine.ModManager.SetModStatus(Definition, TargetModStatus); }
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(Song.SongID)) { throw new InvalidOperationException("SongID must be set on the song!"); } if (string.IsNullOrEmpty(Song.CustomSongPath)) { throw new InvalidOperationException("CustomSongPath must be set on the song!"); } if (!context.Cache.PlaylistCache.ContainsKey(PlaylistID)) { throw new KeyNotFoundException($"PlaylistID {PlaylistID} not found in the cache!"); } bool exists = context.Cache.SongCache.ContainsKey(Song.SongID); if (exists && !OverwriteIfExists) { throw new AddSongException(AddSongFailType.SongExists, $"SongID {Song.SongID} already exists!"); } if (exists && OverwriteIfExists) { OpCommon.DeleteSong(context, Song.SongID); } if (context.Cache.SongCache.ContainsKey(Song.SongID)) { throw new AddSongException(AddSongFailType.SongExists, $"SongID {Song.SongID} already exists, even though it should have been deleted to be replaced!"); } BeatmapLevelDataObject level = null; try { var songsAssetFile = context.Engine.GetSongsAssetsFile(); CustomLevelLoader loader = new CustomLevelLoader(songsAssetFile, context.Config); var deser = loader.DeserializeFromJson(Song.CustomSongPath, Song.SongID); level = loader.LoadSongToAsset(deser, Song.CustomSongPath, true); } catch (Exception ex) { throw new Exception($"Exception loading custom song folder '{Song.CustomSongPath}' for SongID {Song.SongID}", ex); } if (level == null) { throw new AddSongException(AddSongFailType.InvalidFormat, $"Song at folder '{Song.CustomSongPath}' for SongID {Song.SongID} failed to load"); } Song.LevelData = level; Song.LevelAuthorName = level.LevelAuthorName; Song.SongAuthorName = level.SongAuthorName; Song.SongName = level.SongName; Song.SongSubName = level.SongSubName; var playlist = context.Cache.PlaylistCache[PlaylistID]; playlist.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Add(Song.LevelData.PtrFrom(playlist.Playlist.BeatmapLevelCollection.Object)); playlist.Songs.Add(Song.SongID, new OrderedSong() { Song = Song.LevelData, Order = playlist.Songs.Count }); context.Cache.SongCache.Add(Song.SongID, new SongAndPlaylist() { Playlist = playlist.Playlist, Song = Song.LevelData }); var qfos = context.Engine.QueuedFileOperations.Where(x => x.Tag == Song.SongID && x.Type == QueuedFileOperationType.DeleteFolder || x.Type == QueuedFileOperationType.DeleteFile).ToList(); foreach (var q in qfos) { context.Engine.QueuedFileOperations.Remove(q); } }
public AssetOpManager(OpContext context) { _context = context; }
internal override void PerformOp(OpContext context) { if (string.IsNullOrEmpty(PlaylistID)) { throw new InvalidOperationException("Playlist ID must be provided."); } if (!context.Cache.PlaylistCache.ContainsKey(PlaylistID)) { throw new InvalidOperationException("Playlist ID does not exist."); } var plCache = context.Cache.PlaylistCache[PlaylistID]; var songList = plCache.Songs.Values.ToArray(); Comparer <OrderedSong> comparer; switch (SortMode) { case PlaylistSortMode.Name: comparer = Comparer <OrderedSong> .Create((s1, s2) => { try { return(s1.Song.SongName.ToUpper().CompareTo(s2.Song.SongName.ToUpper())); } catch { return(-1); } }); break; case PlaylistSortMode.MaxDifficulty: comparer = Comparer <OrderedSong> .Create((s1, s2) => { try { return(s1.Song.DifficultyBeatmapSets.SelectMany(x => x.DifficultyBeatmaps).Max(x => x.Difficulty) .CompareTo(s2.Song.DifficultyBeatmapSets.SelectMany(x => x.DifficultyBeatmaps).Max(x => x.Difficulty))); } catch { return(-1); } }); break; case PlaylistSortMode.LevelAuthor: comparer = Comparer <OrderedSong> .Create((s1, s2) => { try { return(s1.Song.LevelAuthorName.ToUpper().CompareTo(s2.Song.LevelAuthorName.ToUpper())); } catch { return(-1); } }); break; default: throw new NotImplementedException("Unhandled playlist sort mode."); } Array.Sort(songList, comparer); if (Reverse) { Array.Reverse(songList); } plCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.ToList().ForEach(x => { plCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Remove(x); x.Dispose(); }); for (int i = 0; i < songList.Count(); i++) { var song = songList[i]; song.Order = i; plCache.Playlist.BeatmapLevelCollection.Object.BeatmapLevels.Add(song.Song.PtrFrom(plCache.Playlist.BeatmapLevelCollection.Object)); Log.LogMsg($"{song.Song.SongName}"); } }