diff --git a/Data/Album.cs b/Data/Album.cs index 868dace..2f5058f 100644 --- a/Data/Album.cs +++ b/Data/Album.cs @@ -8,7 +8,7 @@ namespace CustomAlbums.Data { public class Album { - private readonly Logger _logger = new(nameof(Album)); + private static readonly Logger Logger = new(nameof(Album)); public Album(string path, int index) { @@ -17,7 +17,7 @@ public Album(string path, int index) // Load album from directory if (!File.Exists($"{path}\\info.json")) { - _logger.Error($"Could not find info.json at: {path}\\info.json"); + Logger.Error($"Could not find info.json at: {path}\\info.json"); throw new FileNotFoundException(); } @@ -31,7 +31,7 @@ public Album(string path, int index) var info = zip.GetEntry("info.json"); if (info == null) { - _logger.Error($"Could not find info.json in package: {path}"); + Logger.Error($"Could not find info.json in package: {path}"); throw new FileNotFoundException(); } @@ -41,7 +41,7 @@ public Album(string path, int index) } else { - _logger.Error($"Could not find album at: {path}"); + Logger.Error($"Could not find album at: {path}"); throw new FileNotFoundException(); } @@ -97,7 +97,7 @@ public Stream OpenFileStreamIfPossible(string file) return entry.Open().ToMemoryStream(); } - _logger.Error($"Could not find file in package: {file}"); + Logger.Error($"Could not find file in package: {file}"); throw new FileNotFoundException(); } @@ -105,10 +105,32 @@ public Stream OpenFileStreamIfPossible(string file) if (File.Exists(path)) return File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - _logger.Error($"Could not find file: {path}"); + Logger.Error($"Could not find file: {path}"); throw new FileNotFoundException(); } + public Stream OpenNullableStream(string file) + { + if (IsPackaged) + { + using var zip = ZipFile.OpenRead(Path); + var entry = zip.GetEntry(file); + + if (entry != null) + { + return entry.Open().ToMemoryStream(); + } + + return null; + } + + var path = $"{Path}\\{file}"; + if (File.Exists(path)) + return File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + + return null; + } + public MemoryStream OpenMemoryStream(string file) { if (IsPackaged) @@ -121,7 +143,7 @@ public MemoryStream OpenMemoryStream(string file) return entry.Open().ToMemoryStream(); } - _logger.Error($"Could not find file in package: {file}"); + Logger.Error($"Could not find file in package: {file}"); throw new FileNotFoundException(); } @@ -129,7 +151,7 @@ public MemoryStream OpenMemoryStream(string file) if (File.Exists(path)) return File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite).ToMemoryStream(); - _logger.Error($"Could not find file: {path}"); + Logger.Error($"Could not find file: {path}"); throw new FileNotFoundException(); } private void GetSheets() diff --git a/Data/Setlist.cs b/Data/Setlist.cs new file mode 100644 index 0000000..f1cfb9f --- /dev/null +++ b/Data/Setlist.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CustomAlbums.Data +{ + public class Setlist + { + public string Title { get; set; } + } +} diff --git a/Data/Sheet.cs b/Data/Sheet.cs index 665d48e..9fcb073 100644 --- a/Data/Sheet.cs +++ b/Data/Sheet.cs @@ -8,7 +8,7 @@ namespace CustomAlbums.Data { public class Sheet { - private readonly Logger _logger = new(nameof(Sheet)); + private static readonly Logger Logger = new(nameof(Sheet)); public Sheet(Album parentAlbum, int difficulty) { @@ -58,7 +58,7 @@ public StageInfo GetStage() var talkFile = Json.Deserialize(talkStream); if (talkFile.TryGetPropertyValue("version", out var node) && node?.GetValue() == 2) { - _logger.Msg("Version 2 talk file!"); + Logger.Msg("Version 2 talk file!"); TalkFileVersion2 = true; } diff --git a/Main.cs b/Main.cs index 7059310..9fc2056 100644 --- a/Main.cs +++ b/Main.cs @@ -49,5 +49,11 @@ public override void OnFixedUpdate() // TODO: Actually write HotReload // HotReloadManager.FixedUpdate(); } + + public override void OnSceneWasLoaded(int buildIndex, string sceneName) + { + base.OnSceneWasLoaded(buildIndex, sceneName); + MusicStageCellPatch.CurrentScene = sceneName; + } } } \ No newline at end of file diff --git a/Managers/AlbumManager.cs b/Managers/AlbumManager.cs index 2c3bd71..e4e3974 100644 --- a/Managers/AlbumManager.cs +++ b/Managers/AlbumManager.cs @@ -1,4 +1,5 @@ using CustomAlbums.Data; +using CustomAlbums.ModExtensions; using CustomAlbums.Utilities; using Il2CppPeroTools2.Resources; using UnityEngine; @@ -25,6 +26,7 @@ public static class AlbumManager private static readonly Logger Logger = new(nameof(AlbumManager)); internal static readonly FileSystemWatcher AlbumWatcher = new(); + internal static Events.LoadAlbumEvent OnAlbumLoaded; private static int MaxCount { get; set; } public static Dictionary LoadedAlbums { get; } = new(); @@ -48,6 +50,7 @@ public static Album LoadOne(string path) HideFlags.DontUnloadUnusedAsset; Logger.Msg($"Loaded {albumName}: {album.Info.Name}"); + OnAlbumLoaded?.Invoke(typeof(AlbumManager), new AlbumEventArgs(album)); return album; } catch (Exception ex) diff --git a/Managers/SaveManager.cs b/Managers/SaveManager.cs index d3b187e..8959ab2 100644 --- a/Managers/SaveManager.cs +++ b/Managers/SaveManager.cs @@ -193,14 +193,12 @@ internal static void SaveScore(string uid, int musicDifficulty, int score, float var albumName = album.AlbumName; // Create new album save - if (!SaveData.Highest.ContainsKey(albumName)) - SaveData.Highest.Add(albumName, new Dictionary()); + SaveData.Highest.TryAdd(albumName, new Dictionary()); var currChartScore = SaveData.Highest[albumName]; // Create new save data if the difficulty doesn't exist - if (!currChartScore.ContainsKey(musicDifficulty)) - currChartScore.Add(musicDifficulty, new CustomChartSave()); + currChartScore.TryAdd(musicDifficulty, new CustomChartSave()); // Set previous score for PnlVictory logic var newScore = currChartScore[musicDifficulty]; @@ -221,8 +219,7 @@ internal static void SaveScore(string uid, int musicDifficulty, int score, float if (miss != 0) return; // If there were no misses then add the chart/difficulty to the FullCombo list - if (!SaveData.FullCombo.ContainsKey(albumName)) - SaveData.FullCombo.Add(albumName, new List()); + SaveData.FullCombo.TryAdd(albumName, new List()); if (!SaveData.FullCombo[albumName].Contains(musicDifficulty)) SaveData.FullCombo[albumName].Add(musicDifficulty); diff --git a/ModExtensions/AlbumEventArgs.cs b/ModExtensions/AlbumEventArgs.cs new file mode 100644 index 0000000..f407596 --- /dev/null +++ b/ModExtensions/AlbumEventArgs.cs @@ -0,0 +1,14 @@ +using CustomAlbums.Data; + +namespace CustomAlbums.ModExtensions +{ + public class AlbumEventArgs : EventArgs + { + public Album Album; + + public AlbumEventArgs(Album album) + { + Album = album; + } + } +} diff --git a/ModExtensions/AssetEventArgs.cs b/ModExtensions/AssetEventArgs.cs new file mode 100644 index 0000000..bb2b084 --- /dev/null +++ b/ModExtensions/AssetEventArgs.cs @@ -0,0 +1,14 @@ +namespace CustomAlbums.ModExtensions +{ + public class AssetEventArgs : EventArgs + { + public string AssetName; + public IntPtr AssetPtr; + + public AssetEventArgs(string assetName, IntPtr assetPtr) + { + AssetName = assetName; + AssetPtr = assetPtr; + } + } +} diff --git a/ModExtensions/Events.cs b/ModExtensions/Events.cs new file mode 100644 index 0000000..97fb71d --- /dev/null +++ b/ModExtensions/Events.cs @@ -0,0 +1,25 @@ +using CustomAlbums.Managers; +using CustomAlbums.Patches; + +namespace CustomAlbums.ModExtensions +{ + public static class Events + { + public delegate void LoadAssetEvent(object s, AssetEventArgs e); + + public static event LoadAssetEvent OnAssetLoaded + { + add => AssetPatch.OnAssetLoaded += value; + remove => AssetPatch.OnAssetLoaded -= value; + } + + public delegate void LoadAlbumEvent(object s, AlbumEventArgs e); + + public static event LoadAlbumEvent OnAlbumLoaded + { + add => AlbumManager.OnAlbumLoaded += value; + remove => AlbumManager.OnAlbumLoaded -= value; + } + + } +} diff --git a/Patches/AnalyticsPatch.cs b/Patches/AnalyticsPatch.cs index 2b4c317..994f7c3 100644 --- a/Patches/AnalyticsPatch.cs +++ b/Patches/AnalyticsPatch.cs @@ -1,7 +1,6 @@ using CustomAlbums.Managers; using HarmonyLib; using Il2CppAssets.Scripts.Database; -using Il2CppAssets.Scripts.Structs.Modules; using Il2CppPeroPeroGames.DataStatistics; using System.Reflection; using UnityEngine; @@ -102,10 +101,8 @@ internal class GetSearchResultInfoPatch private static bool Prefix(MusicInfo musicInfo) { var runCond = !musicInfo.uid.StartsWith($"{AlbumManager.Uid}-"); - if (runCond) return true; - - Logger.Msg("Blocking custom album from being added to search analytics."); - return false; + if (!runCond) Logger.Msg("Blocking custom album from being added to search analytics."); + return runCond; } } } diff --git a/Patches/AnimatedCoverPatch.cs b/Patches/AnimatedCoverPatch.cs index d49552b..4ab74b5 100644 --- a/Patches/AnimatedCoverPatch.cs +++ b/Patches/AnimatedCoverPatch.cs @@ -18,9 +18,11 @@ internal static class MusicStageCellPatch { private static readonly Logger Logger = new(nameof(MusicStageCellPatch)); private static readonly LinkedList Cells = new(); + internal static string CurrentScene { get; set; } public static void AnimateCoversUpdate() { + if (CurrentScene is not "UISystem_PC") return; var dbMusicTag = GlobalDataBase.dbMusicTag; if (dbMusicTag == null) return; diff --git a/Patches/AssetPatch.cs b/Patches/AssetPatch.cs index df2fb76..f8d674c 100644 --- a/Patches/AssetPatch.cs +++ b/Patches/AssetPatch.cs @@ -4,6 +4,7 @@ using System.Text.Json.Nodes; using CustomAlbums.Data; using CustomAlbums.Managers; +using CustomAlbums.ModExtensions; using CustomAlbums.Utilities; using HarmonyLib; using Il2CppAssets.Scripts.Database; @@ -31,6 +32,7 @@ internal class AssetPatch private static int _latestAlbumNum; private static bool _doneLoadingAlbumsFlag; + internal static Events.LoadAssetEvent OnAssetLoaded; /// /// Binds the asset to a new key, while removing the old asset. @@ -291,6 +293,8 @@ private static IntPtr LoadFromNamePatch(IntPtr instance, IntPtr assetNamePtr, In Logger.Msg($"Loading {assetName}!"); + OnAssetLoaded?.Invoke(typeof(AssetPatch), new AssetEventArgs(assetName, assetPtr)); + if (assetName.StartsWith("ALBUM") && assetName[5..].TryParseAsInt(out var albumNum) && albumNum != AlbumManager.Uid + 1) { diff --git a/Patches/SavePatch.cs b/Patches/SavePatch.cs index 59992dd..e052389 100644 --- a/Patches/SavePatch.cs +++ b/Patches/SavePatch.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CustomAlbums.Data; using CustomAlbums.Managers; @@ -13,8 +12,9 @@ using Il2CppAssets.Scripts.PeroTools.Commons; using Il2CppAssets.Scripts.PeroTools.Platforms.Steam; using Il2CppAssets.Scripts.Structs; +using Il2CppAssets.Scripts.UI.Panels; using Il2CppInterop.Common; -using Il2CppPeroPeroGames.DataStatistics; +using MelonLoader; using MelonLoader.NativeUtils; using static CustomAlbums.Managers.SaveManager; using ArgumentOutOfRangeException = System.ArgumentOutOfRangeException; @@ -26,6 +26,11 @@ namespace CustomAlbums.Patches { internal static class SavePatch { + internal static bool? _hqPresent = null; + internal static bool HQPresent => _hqPresent ??= MelonBase.FindMelon("Headquarters", "AshtonMemer") is not null; + + private static string OriginalNoNetText; + internal static readonly Logger Logger = new(nameof(SavePatch)); // A mapping of Evaluate->letter grade @@ -119,7 +124,7 @@ private static bool InjectPnlPreparation(PnlPreparation __instance, bool forceRe // Reset the panel to its default ClearAndRefreshPanels(__instance, forceReload); __instance.designerLongNameController?.Refresh(); - + if (!ModSettings.SavingEnabled) return false; var recordPanel = __instance.pnlRecord; @@ -161,15 +166,15 @@ private static bool Prefix(MusicInfo __instance, ref string __result, int index) __result = index switch { - 1 => __instance.m_MaskValue.ContainsKey("levelDesigner1") ? __instance.m_MaskValue["levelDesigner1"].ToString() : __instance.levelDesigner1, - 2 => __instance.m_MaskValue.ContainsKey("levelDesigner2") ? __instance.m_MaskValue["levelDesigner2"].ToString() : __instance.levelDesigner2, - 3 => __instance.m_MaskValue.ContainsKey("levelDesigner3") ? __instance.m_MaskValue["levelDesigner3"].ToString() : __instance.levelDesigner3, - 4 => __instance.m_MaskValue.ContainsKey("levelDesigner4") ? __instance.m_MaskValue["levelDesigner4"].ToString() : __instance.levelDesigner4, - 5 => __instance.m_MaskValue.ContainsKey("levelDesigner5") ? __instance.m_MaskValue["levelDesigner5"].ToString() : __instance.levelDesigner5, + 1 => __instance.m_MaskValue.TryGetValue("levelDesigner1", out var d1) ? d1.ToString() : __instance.levelDesigner1, + 2 => __instance.m_MaskValue.TryGetValue("levelDesigner2", out var d2) ? d2.ToString() : __instance.levelDesigner2, + 3 => __instance.m_MaskValue.TryGetValue("levelDesigner3", out var d3) ? d3.ToString() : __instance.levelDesigner3, + 4 => __instance.m_MaskValue.TryGetValue("levelDesigner4", out var d4) ? d4.ToString() : __instance.levelDesigner4, + 5 => __instance.m_MaskValue.TryGetValue("levelDesigner5", out var d5) ? d5.ToString() : __instance.levelDesigner5, _ => throw new ArgumentOutOfRangeException(nameof(index), index, "The difficulty is not a valid index.") }; - if (string.IsNullOrEmpty(__result) || __result == "?") __result = __instance.m_MaskValue.ContainsKey("levelDesigner") ? __instance.m_MaskValue["levelDesigner"].ToString() : __instance.levelDesigner; + if (string.IsNullOrEmpty(__result) || __result == "?") __result = __instance.m_MaskValue.TryGetValue("levelDesigner", out var d) ? d.ToString() : __instance.levelDesigner; if (!string.IsNullOrEmpty(__result)) return false; __result = "?????"; @@ -178,6 +183,41 @@ private static bool Prefix(MusicInfo __instance, ref string __result, int index) } } + /// + /// Stops the game from loading leaderboards on custom charts if Headquarters is not installed. + /// + [HarmonyPatch(typeof(PnlRank), nameof(PnlRank.UIRefresh))] + internal class PnlRankPatch + { + private static bool FirstRun = true; + private static bool Prefix(string uid, PnlRank __instance) + { + var noNetComp = __instance.noNet.GetComponent(); + if (FirstRun) + { + OriginalNoNetText = noNetComp.text; + FirstRun = false; + } + // Check first run case when on a custom and HQ is not present + if (uid.StartsWith($"{AlbumManager.Uid}-") && _hqPresent == null && !HQPresent) + { + Logger.Warning("Headquarters is not installed! Custom chart leaderboards will not function."); + } + + // Vanilla chart or HQ present + if (!uid.StartsWith($"{AlbumManager.Uid}-") || HQPresent) + { + noNetComp.text = OriginalNoNetText; + return true; + } + + // Custom and HQ not present + noNetComp.text = "Headquarters mod is not loaded! ~(*´Д`)"; + __instance.noNet.SetActive(true); + return false; + } + } + // // HACK SECTION // @@ -218,7 +258,7 @@ private static void InjectCustomData() DataHelper.selectedMusicUidFromInfoList = AlbumManager.LoadedAlbums.TryGetValue(SaveData.SelectedAlbum, out var album) ? album.Uid : "0-0"; } - [HarmonyPatch(typeof(DataHelper), nameof(DataHelper.CheckMusicUnlockMaster))] + [HarmonyPatch(typeof(DataHelper), nameof(DataHelper.CheckMusicUnlockMaster), new Type[] { typeof(MusicInfo), typeof(bool) })] internal class CheckUnlockMasterPatch { private static bool Prefix(MusicInfo musicInfo, ref bool __result) diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 827e489..2da9ef2 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -29,8 +29,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.1.2.0")] -[assembly: AssemblyFileVersion("4.1.2.0")] -[assembly: MelonInfo(typeof(CustomAlbums.Main), "CustomAlbums", "4.1.2", "Team Baller")] +[assembly: AssemblyVersion("4.1.3.0")] +[assembly: AssemblyFileVersion("4.1.3.0")] +[assembly: MelonInfo(typeof(CustomAlbums.Main), "CustomAlbums", "4.1.3", "Team Baller")] [assembly: MelonGame("PeroPeroGames", "MuseDash")] [assembly: MelonColor(255, 0, 255, 150)] diff --git a/Utilities/Interop.cs b/Utilities/Interop.cs index d2c7b3a..806bf25 100644 --- a/Utilities/Interop.cs +++ b/Utilities/Interop.cs @@ -2,7 +2,7 @@ namespace CustomAlbums.Utilities { - internal class Interop + public class Interop { /// /// A workaround to a memory corruption issue of creating Il2CppSystem.TypeValue types using C#'s new operator. diff --git a/Utilities/StreamExtensions.cs b/Utilities/StreamExtensions.cs index 08b5757..38f01d0 100644 --- a/Utilities/StreamExtensions.cs +++ b/Utilities/StreamExtensions.cs @@ -2,7 +2,7 @@ namespace CustomAlbums.Utilities { - internal static class StreamExtensions + public static class StreamExtensions { private static readonly Logger Logger = new(nameof(StreamExtensions)); public static string GetHash(this Stream stream)