From d71ad576afdae6079ceaca230ffc9368187bc264 Mon Sep 17 00:00:00 2001 From: Super Balls Date: Thu, 26 Sep 2024 13:42:19 -0700 Subject: [PATCH 01/13] CAM-60 Added pack functionality, refactored some documentation --- Data/Album.cs | 15 ++++++++--- Data/Pack.cs | 14 ++++++++++ Data/Setlist.cs | 13 --------- Managers/AlbumManager.cs | 51 ++++++++++++++++++++++++++++++++--- Managers/PackManager.cs | 33 +++++++++++++++++++++++ Patches/AssetPatch.cs | 14 +++++----- Patches/PackPatch.cs | 40 +++++++++++++++++++++++++++ Patches/SavePatch.cs | 15 ++++++----- Utilities/Json.cs | 4 +-- Utilities/SaveExtensions.cs | 2 +- Utilities/StringExtensions.cs | 14 ++++++++++ 11 files changed, 179 insertions(+), 36 deletions(-) create mode 100644 Data/Pack.cs delete mode 100644 Data/Setlist.cs create mode 100644 Managers/PackManager.cs create mode 100644 Patches/PackPatch.cs create mode 100644 Utilities/StringExtensions.cs diff --git a/Data/Album.cs b/Data/Album.cs index 2f5058f..0fae3f4 100644 --- a/Data/Album.cs +++ b/Data/Album.cs @@ -10,9 +10,10 @@ public class Album { private static readonly Logger Logger = new(nameof(Album)); - public Album(string path, int index) + public Album(string path, int index, string packName = null) { - if (Directory.Exists(path)) + // If packName is not null then it's a file path, not a folder chart + if (Directory.Exists(path) && packName == null) { // Load album from directory if (!File.Exists($"{path}\\info.json")) @@ -27,6 +28,7 @@ public Album(string path, int index) else if (File.Exists(path)) { // Load album from package + PackName = packName; using var zip = ZipFile.OpenRead(path); var info = zip.GetEntry("info.json"); if (info == null) @@ -38,6 +40,9 @@ public Album(string path, int index) using var stream = info.Open(); Info = Json.Deserialize(stream); IsPackaged = true; + + // CurrentPack will always be null if album is not in a pack + IsPack = AlbumManager.CurrentPack != null; } else { @@ -54,6 +59,8 @@ public Album(string path, int index) public int Index { get; } public string Path { get; } public bool IsPackaged { get; } + public bool IsPack { get; } + public string PackName { get; } public AlbumInfo Info { get; } public Sprite Cover => this.GetCover(); public AnimatedCover AnimatedCover => this.GetAnimatedCover(); @@ -61,7 +68,9 @@ public Album(string path, int index) public AudioClip Demo => this.GetAudio("demo"); public Dictionary Sheets { get; } = new(); public string AlbumName => - IsPackaged ? $"album_{System.IO.Path.GetFileNameWithoutExtension(Path)}" : $"album_{System.IO.Path.GetFileName(Path)}_folder"; + IsPackaged ? + $"album_{System.IO.Path.GetFileNameWithoutExtension(Path)}{(PackName != null ? $"_{PackName}" : string.Empty)}" + : $"album_{System.IO.Path.GetFileName(Path)}_folder"; public string Uid => $"{AlbumManager.Uid}-{Index}"; public bool HasFile(string name) diff --git a/Data/Pack.cs b/Data/Pack.cs new file mode 100644 index 0000000..b6957c7 --- /dev/null +++ b/Data/Pack.cs @@ -0,0 +1,14 @@ +using CustomAlbums.Managers; + +namespace CustomAlbums.Data +{ + public class Pack + { + public string Title { get; set; } = AlbumManager.GetCustomAlbumsTitle(); + public string TitleColorHex { get; set; } = "#ffffff"; + public bool LongTextScroll { get; set; } = false; + + internal int StartIndex; + internal int Length; + } +} diff --git a/Data/Setlist.cs b/Data/Setlist.cs deleted file mode 100644 index f1cfb9f..0000000 --- a/Data/Setlist.cs +++ /dev/null @@ -1,13 +0,0 @@ -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/Managers/AlbumManager.cs b/Managers/AlbumManager.cs index e4e3974..7335b60 100644 --- a/Managers/AlbumManager.cs +++ b/Managers/AlbumManager.cs @@ -1,6 +1,8 @@ using CustomAlbums.Data; using CustomAlbums.ModExtensions; using CustomAlbums.Utilities; +using Il2CppAssets.Scripts.PeroTools.Commons; +using Il2CppAssets.Scripts.PeroTools.GeneralLocalization; using Il2CppPeroTools2.Resources; using UnityEngine; using Logger = CustomAlbums.Utilities.Logger; @@ -29,20 +31,55 @@ public static class AlbumManager internal static Events.LoadAlbumEvent OnAlbumLoaded; private static int MaxCount { get; set; } + internal static string CurrentPack { get; set; } = null; public static Dictionary LoadedAlbums { get; } = new(); + + public static void LoadMany(string directory) + { + // Get the files from the directory + var files = Directory.EnumerateFiles(directory); + + // Filter for .mdm files and find the pack.json file + var mdms = files.Where(file => Path.GetExtension(file).EqualsCaseInsensitive(".mdm")).ToList(); + var json = files.FirstOrDefault(file => Path.GetFileName(file).EqualsCaseInsensitive("pack.json")); + + // Initialize pack and variables + var pack = PackManager.CreatePack(json); + CurrentPack = pack.Title; + pack.StartIndex = MaxCount; + + // Count successfully loaded .mdm files + pack.Length = mdms.Count(file => LoadOne(file) != null); + + // Set the current pack to null and add the pack to the pack list + CurrentPack = null; + PackManager.AddPack(pack); + } + public static Album LoadOne(string path) { MaxCount = Math.Max(LoadedAlbums.Count, MaxCount); - var fileName = File.GetAttributes(path).HasFlag(FileAttributes.Directory) ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); + var isDirectory = File.GetAttributes(path).HasFlag(FileAttributes.Directory); + var fileName = isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); + if (LoadedAlbums.ContainsKey(fileName)) return null; - + try { - var album = new Album(path, MaxCount); + if (isDirectory && Directory.EnumerateFiles(path) + .Any(file => Path.GetFileName(file) + .EqualsCaseInsensitive("pack.json"))) + { + LoadMany(path); + return null; + } + + var album = new Album(path, MaxCount, CurrentPack); if (album.Info is null) return null; var albumName = album.AlbumName; + LoadedAlbums.Add(albumName, album); if (album.HasFile("cover.png") || album.HasFile("cover.gif")) @@ -94,5 +131,13 @@ public static IEnumerable GetAlbumUidsFromNames(this IEnumerable return albumNames.Where(name => LoadedAlbums.ContainsKey(name)) .Select(name => $"{Uid}-{LoadedAlbums[name].Index}"); } + + public static string GetCustomAlbumsTitle() + { + return Languages.GetValueOrDefault( + SingletonScriptableObject + .instance? + .GetActiveOption("Language") ?? "English"); + } } } \ No newline at end of file diff --git a/Managers/PackManager.cs b/Managers/PackManager.cs new file mode 100644 index 0000000..71b2841 --- /dev/null +++ b/Managers/PackManager.cs @@ -0,0 +1,33 @@ +using CustomAlbums.Data; +using CustomAlbums.Utilities; + +namespace CustomAlbums.Managers +{ + internal class PackManager + { + private static List Packs = new(); + internal static Pack GetPackFromUid(string uid) + { + // If the uid is not custom or parsing the index fails + if (!uid.StartsWith($"{AlbumManager.Uid}-") || + !uid[4..].TryParseAsInt(out var uidIndex)) return null; + + // Retrieve the pack that the uid belongs to + var pack = Packs.FirstOrDefault(pack => + uidIndex >= pack.StartIndex && uidIndex < pack.StartIndex + pack.Length); + + // If the pack has no albums in it return null, otherwise return pack (will be null if it doesn't exist) + return pack.Length == 0 ? null : pack; + } + + internal static Pack CreatePack(string file) + { + return Json.Deserialize(File.OpenRead(file)); + } + + internal static void AddPack(Pack pack) + { + Packs.Add(pack); + } + } +} diff --git a/Patches/AssetPatch.cs b/Patches/AssetPatch.cs index f8d674c..1818ec2 100644 --- a/Patches/AssetPatch.cs +++ b/Patches/AssetPatch.cs @@ -39,7 +39,7 @@ internal class AssetPatch /// /// The old assetName. /// The new assetName where the value should be bound. - /// true if the key was successfully bound; otherwise, false. + /// if the key was successfully bound; otherwise, . internal static bool ModifyCacheKey(string oldAssetName, string newAssetName) { var success = AssetCache.Remove(oldAssetName, out var asset); @@ -50,7 +50,7 @@ internal static bool ModifyCacheKey(string oldAssetName, string newAssetName) /// Removes a KeyValuePair from the asset cache. /// /// The key corresponding to the value to be removed. - /// true if the entry was successfully removed; otherwise, false. + /// if the entry was successfully removed; otherwise, . internal static bool RemoveFromCache(string key) { return AssetCache.Remove(key); @@ -240,8 +240,8 @@ internal static void InitializeHandler() } /// - /// Gets LoadFromName<TextAsset> and detours it using a - /// NativeHook<LoadFromNameDelegate> to LoadFromNamePatch. + /// Gets where T : and detours it using a + /// where T : to LoadFromNamePatch. /// internal static unsafe void AttachHook() { @@ -277,11 +277,11 @@ internal static unsafe void AttachHook() /// Modifies certain game data as it get loaded. /// The game data that is modified directly adds support for custom albums. /// - /// The instance of ResourceManager calling LoadFromName. + /// The instance of ResourceManager calling LoadFromName. /// The pointer to the string assetName. /// Method info used by the original method. /// - /// A pointer of either a newly created asset if it exists or the original asset pointer if a new one was not + /// An of either a newly created asset if it exists or the original asset if a new one was not /// created. /// [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] @@ -348,7 +348,7 @@ private static IntPtr LoadFromNamePatch(IntPtr instance, IntPtr assetNamePtr, In /// /// /// - /// A new TextAsset initialized with the parameters. + /// A new initialized with the parameters. private static TextAsset CreateTextAsset(string name, string text) { return new TextAsset(text) diff --git a/Patches/PackPatch.cs b/Patches/PackPatch.cs new file mode 100644 index 0000000..a2f8995 --- /dev/null +++ b/Patches/PackPatch.cs @@ -0,0 +1,40 @@ +using CustomAlbums.Data; +using CustomAlbums.Managers; +using CustomAlbums.Utilities; +using HarmonyLib; +using Il2Cpp; +using Il2CppAssets.Scripts.Database; + +namespace CustomAlbums.Patches +{ + internal class PackPatch + { + private static readonly Logger Logger = new(nameof(PackPatch)); + + [HarmonyPatch(typeof(LongSongNameController), nameof(LongSongNameController.Refresh), new[] { typeof(string), typeof(bool), typeof(float) })] + internal class RefreshPatch { + private static void SetColor(string colorHex, LongSongNameController instance) + { + var fixedColor = UnityEngine.ColorUtility.TryParseHtmlString(colorHex, out var color) ? color : UnityEngine.Color.white; + if (instance.m_MidSimpleName != null) instance.m_MidSimpleName.color = fixedColor; + if (instance.m_TxtBackupName != null) instance.m_TxtBackupName.color = fixedColor; + if (instance.m_TxtSimpleName != null) instance.m_TxtSimpleName.color = fixedColor; + } + + private static void Prefix(ref string text, ref Pack __state) + { + var currentUid = DataHelper.selectedMusicUid; + if (currentUid == null || !currentUid.StartsWith($"{AlbumManager.Uid}-") || text != AlbumManager.GetCustomAlbumsTitle()) return; + + __state = PackManager.GetPackFromUid(currentUid); + text = __state?.Title ?? text; + } + + private static void Postfix(ref Pack __state, LongSongNameController __instance) + { + if (__instance == null) return; + SetColor(__state?.TitleColorHex, __instance); + } + } + } +} diff --git a/Patches/SavePatch.cs b/Patches/SavePatch.cs index bf0393b..035398e 100644 --- a/Patches/SavePatch.cs +++ b/Patches/SavePatch.cs @@ -52,9 +52,9 @@ internal static class SavePatch // /// - /// Sets the PnlRecord (score, combo, accuracy) to the custom chart data. + /// Sets the (score, combo, accuracy) to the custom chart data. /// - /// The PnlRecord instance to set. + /// The instance to set. /// The custom chart data. /// If the selected chart has been FCed. private static void SetPanelWithData(PnlRecord panel, CustomChartSave data, bool isFullCombo) @@ -79,9 +79,10 @@ private static void SetPanelWithData(PnlRecord panel, CustomChartSave data, bool /// /// Clears the current pnlRecord and refreshes the panel if needed. - /// The PnlPreparation instance. - /// Whether panelPreparation should force reload the leaderboards. - /// + /// + /// The instance. + /// Whether panelPreparation should force reload the leaderboards. + /// private static void ClearAndRefreshPanels(PnlPreparation panelPreparation, bool reload) { panelPreparation.pnlRecord.Clear(); @@ -111,8 +112,8 @@ private static int GetDifficulty(string uid) /// /// Grabs the custom chart data and injects the PnlRecord with the chart data. /// - /// The PnlPreparation instance. - /// Whether the PnlPreparation instance should force reload the leaderboards. + /// The instance. + /// Whether the instance should force reload the leaderboards. /// private static bool InjectPnlPreparation(PnlPreparation __instance, bool forceReload) { diff --git a/Utilities/Json.cs b/Utilities/Json.cs index cc0b2f0..c8feddd 100644 --- a/Utilities/Json.cs +++ b/Utilities/Json.cs @@ -49,8 +49,8 @@ public static T Il2CppJsonDeserialize(string text) /// /// Fixes strange issue where getting a single as a decimal does not work. /// - /// A JsonNode - /// The decimal value + /// A + /// The parsed value public static decimal GetValueAsDecimal(this JsonNode node) { return node.ToString().TryParseAsDecimal(out var result) ? result : 0M; diff --git a/Utilities/SaveExtensions.cs b/Utilities/SaveExtensions.cs index 2bcf1f3..d956636 100644 --- a/Utilities/SaveExtensions.cs +++ b/Utilities/SaveExtensions.cs @@ -20,7 +20,7 @@ public class SaveData /// /// The save file data class. /// The chart UID. - /// A JsonObject consisting of score information from the current chart's UID. + /// A object consisting of score information from the current chart's UID. public static SaveData GetChartSaveDataFromUid(this CustomAlbumsSave save, string uid) { var album = AlbumManager.GetByUid(uid); diff --git a/Utilities/StringExtensions.cs b/Utilities/StringExtensions.cs new file mode 100644 index 0000000..bcd4449 --- /dev/null +++ b/Utilities/StringExtensions.cs @@ -0,0 +1,14 @@ +namespace CustomAlbums.Utilities +{ + public static class StringExtensions + { + /// + /// Compares two strings using the comparison type. + /// + /// First string to compare + /// + /// if the value of the is the same as this string; otherwise, . + public static bool EqualsCaseInsensitive(this string str1, string str2) + => str1.Equals(str2, StringComparison.OrdinalIgnoreCase); + } +} From c05aa941633bee3639c2ab971bc9a04b235442ff Mon Sep 17 00:00:00 2001 From: Super Balls Date: Thu, 26 Sep 2024 21:54:22 -0700 Subject: [PATCH 02/13] Fix PackManager crash causing Refresh to not run, which resulted in the title getting stuck. --- Managers/PackManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Managers/PackManager.cs b/Managers/PackManager.cs index 71b2841..37526f1 100644 --- a/Managers/PackManager.cs +++ b/Managers/PackManager.cs @@ -17,7 +17,7 @@ internal static Pack GetPackFromUid(string uid) uidIndex >= pack.StartIndex && uidIndex < pack.StartIndex + pack.Length); // If the pack has no albums in it return null, otherwise return pack (will be null if it doesn't exist) - return pack.Length == 0 ? null : pack; + return pack?.Length == 0 ? null : pack; } internal static Pack CreatePack(string file) From efd5be775575290618c10765338bae6ab860fb5b Mon Sep 17 00:00:00 2001 From: Super Balls Date: Sat, 12 Oct 2024 00:50:57 -0700 Subject: [PATCH 03/13] Added readme --- Managers/PackManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Managers/PackManager.cs b/Managers/PackManager.cs index 37526f1..87b636e 100644 --- a/Managers/PackManager.cs +++ b/Managers/PackManager.cs @@ -5,7 +5,7 @@ namespace CustomAlbums.Managers { internal class PackManager { - private static List Packs = new(); + private static readonly List Packs = new(); internal static Pack GetPackFromUid(string uid) { // If the uid is not custom or parsing the index fails From 8d9c13b8a5775f91909984e7c1108b4debb8c66d Mon Sep 17 00:00:00 2001 From: Super Balls Date: Thu, 26 Sep 2024 13:42:19 -0700 Subject: [PATCH 04/13] CAM-60 Added pack functionality, refactored some documentation --- Data/Album.cs | 15 ++++++++--- Data/Pack.cs | 14 ++++++++++ Data/Setlist.cs | 13 --------- Managers/AlbumManager.cs | 51 ++++++++++++++++++++++++++++++++--- Managers/PackManager.cs | 33 +++++++++++++++++++++++ Patches/AssetPatch.cs | 14 +++++----- Patches/PackPatch.cs | 40 +++++++++++++++++++++++++++ Patches/SavePatch.cs | 15 ++++++----- Utilities/Json.cs | 4 +-- Utilities/SaveExtensions.cs | 2 +- Utilities/StringExtensions.cs | 14 ++++++++++ 11 files changed, 179 insertions(+), 36 deletions(-) create mode 100644 Data/Pack.cs delete mode 100644 Data/Setlist.cs create mode 100644 Managers/PackManager.cs create mode 100644 Patches/PackPatch.cs create mode 100644 Utilities/StringExtensions.cs diff --git a/Data/Album.cs b/Data/Album.cs index 2f5058f..0fae3f4 100644 --- a/Data/Album.cs +++ b/Data/Album.cs @@ -10,9 +10,10 @@ public class Album { private static readonly Logger Logger = new(nameof(Album)); - public Album(string path, int index) + public Album(string path, int index, string packName = null) { - if (Directory.Exists(path)) + // If packName is not null then it's a file path, not a folder chart + if (Directory.Exists(path) && packName == null) { // Load album from directory if (!File.Exists($"{path}\\info.json")) @@ -27,6 +28,7 @@ public Album(string path, int index) else if (File.Exists(path)) { // Load album from package + PackName = packName; using var zip = ZipFile.OpenRead(path); var info = zip.GetEntry("info.json"); if (info == null) @@ -38,6 +40,9 @@ public Album(string path, int index) using var stream = info.Open(); Info = Json.Deserialize(stream); IsPackaged = true; + + // CurrentPack will always be null if album is not in a pack + IsPack = AlbumManager.CurrentPack != null; } else { @@ -54,6 +59,8 @@ public Album(string path, int index) public int Index { get; } public string Path { get; } public bool IsPackaged { get; } + public bool IsPack { get; } + public string PackName { get; } public AlbumInfo Info { get; } public Sprite Cover => this.GetCover(); public AnimatedCover AnimatedCover => this.GetAnimatedCover(); @@ -61,7 +68,9 @@ public Album(string path, int index) public AudioClip Demo => this.GetAudio("demo"); public Dictionary Sheets { get; } = new(); public string AlbumName => - IsPackaged ? $"album_{System.IO.Path.GetFileNameWithoutExtension(Path)}" : $"album_{System.IO.Path.GetFileName(Path)}_folder"; + IsPackaged ? + $"album_{System.IO.Path.GetFileNameWithoutExtension(Path)}{(PackName != null ? $"_{PackName}" : string.Empty)}" + : $"album_{System.IO.Path.GetFileName(Path)}_folder"; public string Uid => $"{AlbumManager.Uid}-{Index}"; public bool HasFile(string name) diff --git a/Data/Pack.cs b/Data/Pack.cs new file mode 100644 index 0000000..b6957c7 --- /dev/null +++ b/Data/Pack.cs @@ -0,0 +1,14 @@ +using CustomAlbums.Managers; + +namespace CustomAlbums.Data +{ + public class Pack + { + public string Title { get; set; } = AlbumManager.GetCustomAlbumsTitle(); + public string TitleColorHex { get; set; } = "#ffffff"; + public bool LongTextScroll { get; set; } = false; + + internal int StartIndex; + internal int Length; + } +} diff --git a/Data/Setlist.cs b/Data/Setlist.cs deleted file mode 100644 index f1cfb9f..0000000 --- a/Data/Setlist.cs +++ /dev/null @@ -1,13 +0,0 @@ -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/Managers/AlbumManager.cs b/Managers/AlbumManager.cs index e4e3974..7335b60 100644 --- a/Managers/AlbumManager.cs +++ b/Managers/AlbumManager.cs @@ -1,6 +1,8 @@ using CustomAlbums.Data; using CustomAlbums.ModExtensions; using CustomAlbums.Utilities; +using Il2CppAssets.Scripts.PeroTools.Commons; +using Il2CppAssets.Scripts.PeroTools.GeneralLocalization; using Il2CppPeroTools2.Resources; using UnityEngine; using Logger = CustomAlbums.Utilities.Logger; @@ -29,20 +31,55 @@ public static class AlbumManager internal static Events.LoadAlbumEvent OnAlbumLoaded; private static int MaxCount { get; set; } + internal static string CurrentPack { get; set; } = null; public static Dictionary LoadedAlbums { get; } = new(); + + public static void LoadMany(string directory) + { + // Get the files from the directory + var files = Directory.EnumerateFiles(directory); + + // Filter for .mdm files and find the pack.json file + var mdms = files.Where(file => Path.GetExtension(file).EqualsCaseInsensitive(".mdm")).ToList(); + var json = files.FirstOrDefault(file => Path.GetFileName(file).EqualsCaseInsensitive("pack.json")); + + // Initialize pack and variables + var pack = PackManager.CreatePack(json); + CurrentPack = pack.Title; + pack.StartIndex = MaxCount; + + // Count successfully loaded .mdm files + pack.Length = mdms.Count(file => LoadOne(file) != null); + + // Set the current pack to null and add the pack to the pack list + CurrentPack = null; + PackManager.AddPack(pack); + } + public static Album LoadOne(string path) { MaxCount = Math.Max(LoadedAlbums.Count, MaxCount); - var fileName = File.GetAttributes(path).HasFlag(FileAttributes.Directory) ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); + var isDirectory = File.GetAttributes(path).HasFlag(FileAttributes.Directory); + var fileName = isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); + if (LoadedAlbums.ContainsKey(fileName)) return null; - + try { - var album = new Album(path, MaxCount); + if (isDirectory && Directory.EnumerateFiles(path) + .Any(file => Path.GetFileName(file) + .EqualsCaseInsensitive("pack.json"))) + { + LoadMany(path); + return null; + } + + var album = new Album(path, MaxCount, CurrentPack); if (album.Info is null) return null; var albumName = album.AlbumName; + LoadedAlbums.Add(albumName, album); if (album.HasFile("cover.png") || album.HasFile("cover.gif")) @@ -94,5 +131,13 @@ public static IEnumerable GetAlbumUidsFromNames(this IEnumerable return albumNames.Where(name => LoadedAlbums.ContainsKey(name)) .Select(name => $"{Uid}-{LoadedAlbums[name].Index}"); } + + public static string GetCustomAlbumsTitle() + { + return Languages.GetValueOrDefault( + SingletonScriptableObject + .instance? + .GetActiveOption("Language") ?? "English"); + } } } \ No newline at end of file diff --git a/Managers/PackManager.cs b/Managers/PackManager.cs new file mode 100644 index 0000000..71b2841 --- /dev/null +++ b/Managers/PackManager.cs @@ -0,0 +1,33 @@ +using CustomAlbums.Data; +using CustomAlbums.Utilities; + +namespace CustomAlbums.Managers +{ + internal class PackManager + { + private static List Packs = new(); + internal static Pack GetPackFromUid(string uid) + { + // If the uid is not custom or parsing the index fails + if (!uid.StartsWith($"{AlbumManager.Uid}-") || + !uid[4..].TryParseAsInt(out var uidIndex)) return null; + + // Retrieve the pack that the uid belongs to + var pack = Packs.FirstOrDefault(pack => + uidIndex >= pack.StartIndex && uidIndex < pack.StartIndex + pack.Length); + + // If the pack has no albums in it return null, otherwise return pack (will be null if it doesn't exist) + return pack.Length == 0 ? null : pack; + } + + internal static Pack CreatePack(string file) + { + return Json.Deserialize(File.OpenRead(file)); + } + + internal static void AddPack(Pack pack) + { + Packs.Add(pack); + } + } +} diff --git a/Patches/AssetPatch.cs b/Patches/AssetPatch.cs index f8d674c..1818ec2 100644 --- a/Patches/AssetPatch.cs +++ b/Patches/AssetPatch.cs @@ -39,7 +39,7 @@ internal class AssetPatch /// /// The old assetName. /// The new assetName where the value should be bound. - /// true if the key was successfully bound; otherwise, false. + /// if the key was successfully bound; otherwise, . internal static bool ModifyCacheKey(string oldAssetName, string newAssetName) { var success = AssetCache.Remove(oldAssetName, out var asset); @@ -50,7 +50,7 @@ internal static bool ModifyCacheKey(string oldAssetName, string newAssetName) /// Removes a KeyValuePair from the asset cache. /// /// The key corresponding to the value to be removed. - /// true if the entry was successfully removed; otherwise, false. + /// if the entry was successfully removed; otherwise, . internal static bool RemoveFromCache(string key) { return AssetCache.Remove(key); @@ -240,8 +240,8 @@ internal static void InitializeHandler() } /// - /// Gets LoadFromName<TextAsset> and detours it using a - /// NativeHook<LoadFromNameDelegate> to LoadFromNamePatch. + /// Gets where T : and detours it using a + /// where T : to LoadFromNamePatch. /// internal static unsafe void AttachHook() { @@ -277,11 +277,11 @@ internal static unsafe void AttachHook() /// Modifies certain game data as it get loaded. /// The game data that is modified directly adds support for custom albums. /// - /// The instance of ResourceManager calling LoadFromName. + /// The instance of ResourceManager calling LoadFromName. /// The pointer to the string assetName. /// Method info used by the original method. /// - /// A pointer of either a newly created asset if it exists or the original asset pointer if a new one was not + /// An of either a newly created asset if it exists or the original asset if a new one was not /// created. /// [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] @@ -348,7 +348,7 @@ private static IntPtr LoadFromNamePatch(IntPtr instance, IntPtr assetNamePtr, In /// /// /// - /// A new TextAsset initialized with the parameters. + /// A new initialized with the parameters. private static TextAsset CreateTextAsset(string name, string text) { return new TextAsset(text) diff --git a/Patches/PackPatch.cs b/Patches/PackPatch.cs new file mode 100644 index 0000000..a2f8995 --- /dev/null +++ b/Patches/PackPatch.cs @@ -0,0 +1,40 @@ +using CustomAlbums.Data; +using CustomAlbums.Managers; +using CustomAlbums.Utilities; +using HarmonyLib; +using Il2Cpp; +using Il2CppAssets.Scripts.Database; + +namespace CustomAlbums.Patches +{ + internal class PackPatch + { + private static readonly Logger Logger = new(nameof(PackPatch)); + + [HarmonyPatch(typeof(LongSongNameController), nameof(LongSongNameController.Refresh), new[] { typeof(string), typeof(bool), typeof(float) })] + internal class RefreshPatch { + private static void SetColor(string colorHex, LongSongNameController instance) + { + var fixedColor = UnityEngine.ColorUtility.TryParseHtmlString(colorHex, out var color) ? color : UnityEngine.Color.white; + if (instance.m_MidSimpleName != null) instance.m_MidSimpleName.color = fixedColor; + if (instance.m_TxtBackupName != null) instance.m_TxtBackupName.color = fixedColor; + if (instance.m_TxtSimpleName != null) instance.m_TxtSimpleName.color = fixedColor; + } + + private static void Prefix(ref string text, ref Pack __state) + { + var currentUid = DataHelper.selectedMusicUid; + if (currentUid == null || !currentUid.StartsWith($"{AlbumManager.Uid}-") || text != AlbumManager.GetCustomAlbumsTitle()) return; + + __state = PackManager.GetPackFromUid(currentUid); + text = __state?.Title ?? text; + } + + private static void Postfix(ref Pack __state, LongSongNameController __instance) + { + if (__instance == null) return; + SetColor(__state?.TitleColorHex, __instance); + } + } + } +} diff --git a/Patches/SavePatch.cs b/Patches/SavePatch.cs index e052389..85db929 100644 --- a/Patches/SavePatch.cs +++ b/Patches/SavePatch.cs @@ -52,9 +52,9 @@ internal static class SavePatch // /// - /// Sets the PnlRecord (score, combo, accuracy) to the custom chart data. + /// Sets the (score, combo, accuracy) to the custom chart data. /// - /// The PnlRecord instance to set. + /// The instance to set. /// The custom chart data. /// If the selected chart has been FCed. private static void SetPanelWithData(PnlRecord panel, CustomChartSave data, bool isFullCombo) @@ -79,9 +79,10 @@ private static void SetPanelWithData(PnlRecord panel, CustomChartSave data, bool /// /// Clears the current pnlRecord and refreshes the panel if needed. - /// The PnlPreparation instance. - /// Whether panelPreparation should force reload the leaderboards. - /// + /// + /// The instance. + /// Whether panelPreparation should force reload the leaderboards. + /// private static void ClearAndRefreshPanels(PnlPreparation panelPreparation, bool reload) { panelPreparation.pnlRecord.Clear(); @@ -111,8 +112,8 @@ private static int GetDifficulty(string uid) /// /// Grabs the custom chart data and injects the PnlRecord with the chart data. /// - /// The PnlPreparation instance. - /// Whether the PnlPreparation instance should force reload the leaderboards. + /// The instance. + /// Whether the instance should force reload the leaderboards. /// private static bool InjectPnlPreparation(PnlPreparation __instance, bool forceReload) { diff --git a/Utilities/Json.cs b/Utilities/Json.cs index cc0b2f0..c8feddd 100644 --- a/Utilities/Json.cs +++ b/Utilities/Json.cs @@ -49,8 +49,8 @@ public static T Il2CppJsonDeserialize(string text) /// /// Fixes strange issue where getting a single as a decimal does not work. /// - /// A JsonNode - /// The decimal value + /// A + /// The parsed value public static decimal GetValueAsDecimal(this JsonNode node) { return node.ToString().TryParseAsDecimal(out var result) ? result : 0M; diff --git a/Utilities/SaveExtensions.cs b/Utilities/SaveExtensions.cs index 2bcf1f3..d956636 100644 --- a/Utilities/SaveExtensions.cs +++ b/Utilities/SaveExtensions.cs @@ -20,7 +20,7 @@ public class SaveData /// /// The save file data class. /// The chart UID. - /// A JsonObject consisting of score information from the current chart's UID. + /// A object consisting of score information from the current chart's UID. public static SaveData GetChartSaveDataFromUid(this CustomAlbumsSave save, string uid) { var album = AlbumManager.GetByUid(uid); diff --git a/Utilities/StringExtensions.cs b/Utilities/StringExtensions.cs new file mode 100644 index 0000000..bcd4449 --- /dev/null +++ b/Utilities/StringExtensions.cs @@ -0,0 +1,14 @@ +namespace CustomAlbums.Utilities +{ + public static class StringExtensions + { + /// + /// Compares two strings using the comparison type. + /// + /// First string to compare + /// + /// if the value of the is the same as this string; otherwise, . + public static bool EqualsCaseInsensitive(this string str1, string str2) + => str1.Equals(str2, StringComparison.OrdinalIgnoreCase); + } +} From 78a7db59e276616494cbfafbe7b88d7ad1d8939a Mon Sep 17 00:00:00 2001 From: Super Balls Date: Thu, 26 Sep 2024 21:54:22 -0700 Subject: [PATCH 05/13] Fix PackManager crash causing Refresh to not run, which resulted in the title getting stuck. --- Managers/PackManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Managers/PackManager.cs b/Managers/PackManager.cs index 71b2841..37526f1 100644 --- a/Managers/PackManager.cs +++ b/Managers/PackManager.cs @@ -17,7 +17,7 @@ internal static Pack GetPackFromUid(string uid) uidIndex >= pack.StartIndex && uidIndex < pack.StartIndex + pack.Length); // If the pack has no albums in it return null, otherwise return pack (will be null if it doesn't exist) - return pack.Length == 0 ? null : pack; + return pack?.Length == 0 ? null : pack; } internal static Pack CreatePack(string file) From c2617964a04e5fc7c12eb10b8e257fc8868cabe0 Mon Sep 17 00:00:00 2001 From: Super Balls Date: Sat, 12 Oct 2024 00:50:57 -0700 Subject: [PATCH 06/13] Added readme --- Managers/PackManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Managers/PackManager.cs b/Managers/PackManager.cs index 37526f1..87b636e 100644 --- a/Managers/PackManager.cs +++ b/Managers/PackManager.cs @@ -5,7 +5,7 @@ namespace CustomAlbums.Managers { internal class PackManager { - private static List Packs = new(); + private static readonly List Packs = new(); internal static Pack GetPackFromUid(string uid) { // If the uid is not custom or parsing the index fails From 03d6a88f2aafae0fc8798f0a52d70c5b821d0cd0 Mon Sep 17 00:00:00 2001 From: Super Balls Date: Sat, 12 Oct 2024 01:01:12 -0700 Subject: [PATCH 07/13] Rebased, added documentation snippet to show intent with this commit --- Managers/AlbumManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Managers/AlbumManager.cs b/Managers/AlbumManager.cs index 7335b60..03a2522 100644 --- a/Managers/AlbumManager.cs +++ b/Managers/AlbumManager.cs @@ -132,6 +132,10 @@ public static IEnumerable GetAlbumUidsFromNames(this IEnumerable .Select(name => $"{Uid}-{LoadedAlbums[name].Index}"); } + /// + /// Gets the current "Custom Albums" title based on language. + /// + /// The current "Custom Albums" title based on language. public static string GetCustomAlbumsTitle() { return Languages.GetValueOrDefault( From c23230a31d259cd7ddb236025b150f59ad111905 Mon Sep 17 00:00:00 2001 From: Super Balls Date: Sat, 3 May 2025 11:12:15 -0700 Subject: [PATCH 08/13] CAM-69 Added support for BA SceneEgg, merged branch with master --- Data/SceneEggs.cs | 3 ++- Patches/SceneEggPatch.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Data/SceneEggs.cs b/Data/SceneEggs.cs index f0a2af8..e29a451 100644 --- a/Data/SceneEggs.cs +++ b/Data/SceneEggs.cs @@ -11,7 +11,8 @@ public enum SceneEggs Touhou = 3, Arknights = 4, Miku = 5, - RinLen = 6, + RinLen = 6, + BlueArchive = 7, BadApple = 129, Christmas = 999 } diff --git a/Patches/SceneEggPatch.cs b/Patches/SceneEggPatch.cs index fd98a97..735d741 100644 --- a/Patches/SceneEggPatch.cs +++ b/Patches/SceneEggPatch.cs @@ -39,6 +39,7 @@ internal class ControllerPatch { private static void Prefix(Il2CppSystem.Collections.Generic.List sceneEggIdsBuffer) { + // Return if there are no scene eggs or if the scene eggs have special logic if (IgnoreSceneEggs(out var album, SceneEggs.None, SceneEggs.Christmas, SceneEggs.BadApple)) return; // Adds the scene egg to the buffer @@ -52,18 +53,17 @@ private static void Prefix(Il2CppSystem.Collections.Generic.List sceneEggId } /// - /// Makes scene_05 be scene_05_christmas if the Christmas SceneEgg is enabled or scene_08 be scene_touhou_black if - /// BadApple is enabled. + /// Makes scene_05 be scene_05_christmas if the Christmas SceneEgg is enabled. /// [HarmonyPatch(typeof(GameMusicScene), nameof(GameMusicScene.SceneFestival))] internal class SceneFestivalPatch { private static bool Prefix(string sceneFestivalName, ref string __result) { - // If the scene is not scene_05 or scene_08 then there is no Christmas or Bad Apple + // If the scene is not scene_05 or scene_08 then there is no Christmas if (sceneFestivalName != "scene_05") return true; - // Ignore the actual SceneEggs (and BadApple) + // Ignore the actual SceneEggs if (IgnoreSceneEggs(out _, SceneEggs.Arknights, SceneEggs.Cytus, SceneEggs.None, SceneEggs.Queen, SceneEggs.Touhou, SceneEggs.Wacca, SceneEggs.Miku, SceneEggs.BadApple, SceneEggs.RinLen)) return true; @@ -97,10 +97,10 @@ private static bool Prefix(string bossFestivalName, ref string __result) [HarmonyPatch(typeof(DBTouhou), nameof(DBTouhou.AwakeInit))] internal class BadApplePatch { - private static void Postfix(ref string __state) + private static void Postfix() { if (IgnoreSceneEggs(out _, SceneEggs.Arknights, SceneEggs.Cytus, SceneEggs.None, - SceneEggs.Queen, SceneEggs.Touhou, SceneEggs.Wacca, SceneEggs.Miku, SceneEggs.Christmas, SceneEggs.RinLen)) return; + SceneEggs.Queen, SceneEggs.Touhou, SceneEggs.Wacca, SceneEggs.Miku, SceneEggs.Christmas, SceneEggs.RinLen, SceneEggs.BlueArchive)) return; GlobalDataBase.dbTouhou.isBadApple = true; From fffcc4492f492d396ea9e47db0164f9833e57a5c Mon Sep 17 00:00:00 2001 From: Super Balls Date: Sat, 3 May 2025 11:43:38 -0700 Subject: [PATCH 09/13] CAM-68 Add support for S logos on the menu for custom charts --- Patches/DifficultyUIPatch.cs | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Patches/DifficultyUIPatch.cs diff --git a/Patches/DifficultyUIPatch.cs b/Patches/DifficultyUIPatch.cs new file mode 100644 index 0000000..b45d0f5 --- /dev/null +++ b/Patches/DifficultyUIPatch.cs @@ -0,0 +1,46 @@ +using HarmonyLib; +using Il2CppAssets.Scripts.UI.Panels; +using CustomAlbums.Utilities; +using Il2CppAssets.Scripts.Database; +using CustomAlbums.Managers; +using Il2CppAssets.Scripts.PeroTools.Commons; +using Il2Cpp; + +namespace CustomAlbums.Patches +{ + internal class DifficultyUIPatch + { + + /// + /// Enables the S logos on the difficulties for custom charts. + /// + [HarmonyPatch(typeof(PnlStage), nameof(PnlStage.RefreshDiffUI))] + internal class DiffUIFix + { + private static int GetEvaluate(Dictionary highest, int diff) => highest.GetValueOrDefault(diff)?.Evaluate ?? -1; + + private static void Postfix(MusicInfo musicInfo, PnlStage __instance) + { + + // Return if chart is not custom + var uid = musicInfo?.uid; + if (uid is null || !uid.StartsWith($"{AlbumManager.Uid}-")) return; + + // Gets the highest data from save data + var customHighest = SaveManager.SaveData.GetChartSaveDataFromUid(uid).Highest; + + // Get the Evaluate value from each, set diff3 to diff4 if hidden is invoked + var diff1 = GetEvaluate(customHighest, 1); + var diff2 = GetEvaluate(customHighest, 2); + var diff3 = Singleton.instance.IsInvokeHideBms(uid) + ? GetEvaluate(customHighest, 4) + : GetEvaluate(customHighest, 3); + + // Set the S logos for each difficulty + __instance.m_Diff1Item.ChangeSchemeByEvalue(diff1, __instance.globalData); + __instance.m_Diff2Item.ChangeSchemeByEvalue(diff2, __instance.globalData); + __instance.m_Diff3Item.ChangeSchemeByEvalue(diff3, __instance.globalData); + } + } + } +} From 669cfa696923adb109962cfdce51d728cd8712ff Mon Sep 17 00:00:00 2001 From: ALLMarvelous <53633401+ALLMarvelous@users.noreply.github.com> Date: Sun, 4 May 2025 01:12:46 -0500 Subject: [PATCH 10/13] CAM-70 Inject custom IData into the DataHelper.highest list --- Managers/SaveManager.cs | 13 +++- Patches/DataInjectPatch.cs | 115 +++++++++++++++++++++++++++++++++++ Patches/DifficultyUIPatch.cs | 46 -------------- Patches/ReportCardPatch.cs | 76 ----------------------- Utilities/DataExtensions.cs | 43 +++++++++++++ 5 files changed, 170 insertions(+), 123 deletions(-) create mode 100644 Patches/DataInjectPatch.cs delete mode 100644 Patches/DifficultyUIPatch.cs delete mode 100644 Patches/ReportCardPatch.cs create mode 100644 Utilities/DataExtensions.cs diff --git a/Managers/SaveManager.cs b/Managers/SaveManager.cs index 8959ab2..350ac8c 100644 --- a/Managers/SaveManager.cs +++ b/Managers/SaveManager.cs @@ -3,6 +3,7 @@ using CustomAlbums.Data; using CustomAlbums.Utilities; using System.IO.Compression; +using CustomAlbums.Patches; namespace CustomAlbums.Managers { @@ -216,9 +217,19 @@ internal static void SaveScore(string uid, int musicDifficulty, int score, float if (musicDifficulty is 2 && AlbumManager.LoadedAlbums[albumName].HasDifficulty(3) && newScore.Evaluate >= 4) SaveData.UnlockedMasters.Add(albumName); - if (miss != 0) return; + // Update the IData for the played chart + var dataIndex = DataInjectPatch.DataList.GetIndexByUid(album.Uid, musicDifficulty); + if (dataIndex != -1) + { + DataInjectPatch.DataList.RemoveAt(dataIndex); + } + + var newIData = DataInjectPatch.CreateIData(album, musicDifficulty, newScore); + DataInjectPatch.DataList.Add(newIData); // If there were no misses then add the chart/difficulty to the FullCombo list + if (miss != 0) return; + SaveData.FullCombo.TryAdd(albumName, new List()); if (!SaveData.FullCombo[albumName].Contains(musicDifficulty)) diff --git a/Patches/DataInjectPatch.cs b/Patches/DataInjectPatch.cs new file mode 100644 index 0000000..be86ecb --- /dev/null +++ b/Patches/DataInjectPatch.cs @@ -0,0 +1,115 @@ +using CustomAlbums.Data; +using CustomAlbums.Managers; +using CustomAlbums.Utilities; +using HarmonyLib; +using Il2CppAssets.Scripts.Database; +using Il2CppAssets.Scripts.PeroTools.Nice.Interface; +using Il2CppAssets.Scripts.PeroTools.Nice.Variables; +using IDataList = Il2CppSystem.Collections.Generic.List; + +namespace CustomAlbums.Patches +{ + /// + /// This patch modifies the highest property of DataHelper to include custom charts. + /// + [HarmonyPatch(typeof(DataHelper), nameof(DataHelper.highest), MethodType.Getter)] + internal class DataInjectPatch + { + internal static readonly IDataList DataList = new(); + private static readonly Logger Logger = new(nameof(DataInjectPatch)); + + // ReSharper disable once InconsistentNaming + private static void Postfix(ref IDataList __result) + { + var highest = DataHelper.s_DataManager["Achievement"]["highest"].GetResult(); + if (highest == null) return; + + if (DataList.Count > 0) + { + var combined1 = new IDataList(); + foreach (var item in highest) + { + combined1.Add(item); + } + foreach (var item in DataList) + { + combined1.Add(item); + } + + __result = combined1; + return; + } + + var customsHighest = SaveManager.SaveData.Highest; + + foreach (var (albumName, albumDic) in customsHighest) + { + foreach (var (difficulty, save) in albumDic) + { + if (!AlbumManager.LoadedAlbums.TryGetValue(albumName, out var album)) + { + Logger.Warning($"No album found for key {albumName}."); + continue; + } + + var data = CreateIData(album, difficulty, save); + DataList.Add(data); + } + } + + // combine the two lists highest and data + var combined = new IDataList(); + foreach (var item in highest) + { + combined.Add(item); + } + foreach (var item in DataList) + { + combined.Add(item); + } + + // set the result to the combined list + __result = combined; + } + + /// + /// Creates a Muse Dash-compatible IData object from a CustomChartSave object. + /// + /// The album of the data to be created. + /// The difficulty of the chart. + /// The CustomChartSave object containing the save data. + /// + internal static IData CreateIData(Album album, int difficulty, CustomChartSave save) + { + var data = new Il2CppAssets.Scripts.PeroTools.Nice.Datas.Data(); + + data.fields.Add("uid", CreateIVariable($"{album.Uid}_{difficulty}")); + data.fields.Add("evaluate", CreateIVariable(save.Evaluate)); + data.fields.Add("score", CreateIVariable(save.Score)); + data.fields.Add("combo", CreateIVariable(save.Combo)); + data.fields.Add("accuracy", CreateIVariable(save.Accuracy)); + data.fields.Add("accuracyStr", CreateIVariable(save.AccuracyStr)); + data.fields.Add("clear", CreateIVariable(save.Clear)); + data.fields.Add("failCount", CreateIVariable(save.FailCount)); + data.fields.Add("passed", CreateIVariable(save.Passed)); + + Logger.Msg("Yippee! IData made for " + album.Info.Name, false); + return data.Cast(); + } + + /// + /// Creates a Muse Dash-compatible IVariable object from an Il2CppSystem.Object. + /// + /// The object (Il2Cpp) to be converted to IVariable. + /// + internal static IVariable CreateIVariable(Il2CppSystem.Object obj) + { + var constance = new Constance + { + result = obj + }; + + return constance.Cast(); + } + } +} diff --git a/Patches/DifficultyUIPatch.cs b/Patches/DifficultyUIPatch.cs deleted file mode 100644 index b45d0f5..0000000 --- a/Patches/DifficultyUIPatch.cs +++ /dev/null @@ -1,46 +0,0 @@ -using HarmonyLib; -using Il2CppAssets.Scripts.UI.Panels; -using CustomAlbums.Utilities; -using Il2CppAssets.Scripts.Database; -using CustomAlbums.Managers; -using Il2CppAssets.Scripts.PeroTools.Commons; -using Il2Cpp; - -namespace CustomAlbums.Patches -{ - internal class DifficultyUIPatch - { - - /// - /// Enables the S logos on the difficulties for custom charts. - /// - [HarmonyPatch(typeof(PnlStage), nameof(PnlStage.RefreshDiffUI))] - internal class DiffUIFix - { - private static int GetEvaluate(Dictionary highest, int diff) => highest.GetValueOrDefault(diff)?.Evaluate ?? -1; - - private static void Postfix(MusicInfo musicInfo, PnlStage __instance) - { - - // Return if chart is not custom - var uid = musicInfo?.uid; - if (uid is null || !uid.StartsWith($"{AlbumManager.Uid}-")) return; - - // Gets the highest data from save data - var customHighest = SaveManager.SaveData.GetChartSaveDataFromUid(uid).Highest; - - // Get the Evaluate value from each, set diff3 to diff4 if hidden is invoked - var diff1 = GetEvaluate(customHighest, 1); - var diff2 = GetEvaluate(customHighest, 2); - var diff3 = Singleton.instance.IsInvokeHideBms(uid) - ? GetEvaluate(customHighest, 4) - : GetEvaluate(customHighest, 3); - - // Set the S logos for each difficulty - __instance.m_Diff1Item.ChangeSchemeByEvalue(diff1, __instance.globalData); - __instance.m_Diff2Item.ChangeSchemeByEvalue(diff2, __instance.globalData); - __instance.m_Diff3Item.ChangeSchemeByEvalue(diff3, __instance.globalData); - } - } - } -} diff --git a/Patches/ReportCardPatch.cs b/Patches/ReportCardPatch.cs deleted file mode 100644 index 5c42eea..0000000 --- a/Patches/ReportCardPatch.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Globalization; -using System.Reflection; -using CustomAlbums.Managers; -using HarmonyLib; -using Il2Cpp; -using Il2CppAssets.Scripts.Database; -using Il2CppAssets.Scripts.GameCore.Managers; -using Il2CppAssets.Scripts.PeroTools.Commons; -using Il2CppAssets.Scripts.UI.Panels; - -namespace CustomAlbums.Patches -{ - [HarmonyPatch(typeof(PnlReportCard), nameof(PnlReportCard.RefreshBestRecord))] - internal static class ReportCardPatch - { - // ReSharper disable once InconsistentNaming - private static bool Prefix(PnlReportCard __instance) - { - var musicInfo = GlobalDataBase.s_DbMusicTag.CurMusicInfo(); - if (musicInfo.albumIndex != 999) return true; - - var mapDifficulty = GlobalDataBase.s_DbBattleStage.m_MapDifficulty; - var curMusicBestRankOrder = Singleton.instance.GetCurMusicBestRankOrder(); - - var album = AlbumManager.GetByUid(musicInfo.uid); - if (album == null) return false; - - var save = SaveManager.SaveData.Highest[album.AlbumName][mapDifficulty]; - if (save == null) return false; - - __instance.RefreshRecord(musicInfo, mapDifficulty, save.Score, save.Combo, save.AccuracyStr, save.Evaluate, save.Clear.ToString(CultureInfo.InvariantCulture), curMusicBestRankOrder); - return false; - } - } - - [HarmonyPatch] - internal static class TogglePatch - { - private static IEnumerable TargetMethods() - { - return new[] { nameof(PnlPreparation.OnDiffTglChanged), nameof(PnlPreparation.OnEnable) } - .Select(methodName => typeof(PnlPreparation).GetMethod(methodName)) - .ToArray(); - } - // ReSharper disable once InconsistentNaming - private static void Postfix(PnlPreparation __instance) - { - var musicInfo = GlobalDataBase.s_DbMusicTag.CurMusicInfo(); - if (musicInfo.albumIndex != 999) return; - - var mapDifficulty = GlobalDataBase.s_DbBattleStage.m_MapDifficulty; - var gameObject = __instance.btnDownloadReport.gameObject; - - var album = AlbumManager.GetByUid(musicInfo.uid); - if (album == null) - { - gameObject.SetActive(false); - return; - } - - if (!SaveManager.SaveData.Highest.TryGetValue(album.AlbumName, out var chart)) - { - gameObject.SetActive(false); - return; - } - - if (!chart.ContainsKey(mapDifficulty)) - { - gameObject.SetActive(false); - return; - } - - gameObject.SetActive(true); - } - } -} diff --git a/Utilities/DataExtensions.cs b/Utilities/DataExtensions.cs new file mode 100644 index 0000000..4448e82 --- /dev/null +++ b/Utilities/DataExtensions.cs @@ -0,0 +1,43 @@ +using Il2CppAssets.Scripts.PeroTools.Nice.Interface; + +namespace CustomAlbums.Utilities +{ + internal static class DataExtensions + { + /// + /// Gets the uid field of the IData object. + /// + /// The IData object. + /// The uid or an empty string if not found. + public static string GetUid(this IData data) + { + var uidField = data.fields["uid"]; + return uidField == null ? string.Empty : uidField.GetResult(); + } + + /// + /// Gets the index of a chart in an IData list by its uid and difficulty. + /// + /// The IData list. + /// The uid of the chart. + /// The difficulty of the chart. + /// The index of the chart in the list, or -1 if not found. + public static int GetIndexByUid(this Il2CppSystem.Collections.Generic.List dataList, string uid, int difficulty) + { + var i = 0; + + // For loop doesn't work here + foreach (var data in dataList) + { + if (data.GetUid() == $"{uid}_{difficulty}") + { + return i; + } + + i++; + } + + return -1; + } + } +} From ed2e3b281c2bbb0d03ef15c4912c78145e9ca124 Mon Sep 17 00:00:00 2001 From: ALLMarvelous <53633401+ALLMarvelous@users.noreply.github.com> Date: Sun, 4 May 2025 01:13:12 -0500 Subject: [PATCH 11/13] CAM-70 Clean CustomAlbums Highest from official save file. --- Patches/SavePatch.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Patches/SavePatch.cs b/Patches/SavePatch.cs index 080655a..76489f4 100644 --- a/Patches/SavePatch.cs +++ b/Patches/SavePatch.cs @@ -10,6 +10,7 @@ using Il2CppAssets.Scripts.GameCore.HostComponent; using Il2CppAssets.Scripts.GameCore.Managers; using Il2CppAssets.Scripts.PeroTools.Commons; +using Il2CppAssets.Scripts.PeroTools.Nice.Interface; using Il2CppAssets.Scripts.PeroTools.Platforms.Steam; using Il2CppAssets.Scripts.Structs; using Il2CppAssets.Scripts.UI.Panels; @@ -232,6 +233,8 @@ private static void CleanCustomData() (Il2CppSystem.Predicate)(uid => uid.StartsWith($"{AlbumManager.Uid}-"))); DataHelper.collections.RemoveAll( (Il2CppSystem.Predicate)(uid => uid.StartsWith($"{AlbumManager.Uid}-"))); + DataHelper.highest.RemoveAll( + (Il2CppSystem.Predicate)(data => data.GetUid().StartsWith($"{AlbumManager.Uid}-"))); if (DataHelper.selectedAlbumUid == "music_package_999") DataHelper.selectedAlbumUid = "music_package_0"; From ddea9ba48163d6865d26e14a6cb82454912d96e7 Mon Sep 17 00:00:00 2001 From: ALLMarvelous <53633401+ALLMarvelous@users.noreply.github.com> Date: Sun, 4 May 2025 01:17:50 -0500 Subject: [PATCH 12/13] Bump version for release --- Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main.cs b/Main.cs index f324a21..281d040 100644 --- a/Main.cs +++ b/Main.cs @@ -12,7 +12,7 @@ public class Main : MelonMod public const string MelonName = "CustomAlbums"; public const string MelonAuthor = "Two Fellas"; - public const string MelonVersion = "4.1.6"; + public const string MelonVersion = "4.1.7"; public override void OnInitializeMelon() { From 579aa5b977740a56e3c96d5bf7d80ec20326e497 Mon Sep 17 00:00:00 2001 From: ALLMarvelous <53633401+ALLMarvelous@users.noreply.github.com> Date: Sun, 4 May 2025 01:19:20 -0500 Subject: [PATCH 13/13] CAM-70 Remove debug message --- Patches/DataInjectPatch.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Patches/DataInjectPatch.cs b/Patches/DataInjectPatch.cs index be86ecb..54cb197 100644 --- a/Patches/DataInjectPatch.cs +++ b/Patches/DataInjectPatch.cs @@ -93,7 +93,6 @@ internal static IData CreateIData(Album album, int difficulty, CustomChartSave s data.fields.Add("failCount", CreateIVariable(save.FailCount)); data.fields.Add("passed", CreateIVariable(save.Passed)); - Logger.Msg("Yippee! IData made for " + album.Info.Name, false); return data.Cast(); }