From 843d7aba8a3e9e558547fec7f164142f6f8df76d Mon Sep 17 00:00:00 2001 From: Fluto Date: Sun, 30 Jan 2022 16:28:02 +1100 Subject: [PATCH] Added more mods including a song loader --- DisableScreenChangeOnFocus.cs | 21 + Folder.DotSettings | 1 + MusicPatch.cs | 1151 +++++++++++++++++++++++++++++++++ Plugin.cs | 84 ++- SignInPatch.cs | 17 +- SkipSplashScreen.cs | 20 + TaikoMods.csproj | 6 +- 7 files changed, 1291 insertions(+), 9 deletions(-) create mode 100644 DisableScreenChangeOnFocus.cs create mode 100644 MusicPatch.cs create mode 100644 SkipSplashScreen.cs diff --git a/DisableScreenChangeOnFocus.cs b/DisableScreenChangeOnFocus.cs new file mode 100644 index 0000000..2634bc4 --- /dev/null +++ b/DisableScreenChangeOnFocus.cs @@ -0,0 +1,21 @@ +using HarmonyLib; + +namespace TaikoMods; + +[HarmonyPatch] +public class DisableScreenChangeOnFocus +{ + [HarmonyPatch(typeof(FocusManager), "OnApplicationFocus")] + [HarmonyPrefix] + private static bool OnApplicationFocus_Prefix(bool focusStatus) + { + return false; + } + + [HarmonyPatch(typeof(FocusManager), "UpdateFocusManager")] + [HarmonyPrefix] + private static bool UpdateFocusManager_Prefix() + { + return false; + } +} \ No newline at end of file diff --git a/Folder.DotSettings b/Folder.DotSettings index 78850e8..31b9bd1 100644 --- a/Folder.DotSettings +++ b/Folder.DotSettings @@ -1,3 +1,4 @@  True + True True \ No newline at end of file diff --git a/MusicPatch.cs b/MusicPatch.cs new file mode 100644 index 0000000..c6eb3a3 --- /dev/null +++ b/MusicPatch.cs @@ -0,0 +1,1151 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text.RegularExpressions; +using BepInEx.Logging; +using HarmonyLib; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine; + +namespace TaikoMods; + +/// +/// This will allow custom songs to be read in +/// +[HarmonyPatch] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class MusicPatch +{ + public static int SaveDataMax => DataConst.MusicMax; + + public static string MusicTrackDirectory => Plugin.Instance.ConfigSongDirectory.Value; + public static string SaveFilePath => $"{Plugin.Instance.ConfigSaveDirectory.Value}/save.json"; + private const string SongDataFileName = "data.json"; + + public static ManualLogSource Log => Plugin.Log; + + public static void Setup(Harmony harmony) + { + CreateDirectoryIfNotExist(Path.GetDirectoryName(SaveFilePath)); + CreateDirectoryIfNotExist(MusicTrackDirectory); + + PatchManual(harmony); + + void CreateDirectoryIfNotExist(string path) + { + path = Path.GetFullPath(path); + if (!Directory.Exists(path)) + { + Log.LogInfo($"Creating path at {path}"); + Directory.CreateDirectory(path); + } + } + } + + private static void PatchManual(Harmony harmony) + { + var original = typeof(FumenLoader).GetNestedType("PlayerData", BindingFlags.NonPublic).GetMethod("Read"); + var prefix = typeof(MusicPatch).GetMethod(nameof(Read_Prefix), BindingFlags.Static | BindingFlags.NonPublic); + + harmony.Patch(original, new HarmonyMethod(prefix)); + } + + #region Custom Save Data + + private static CustomMusicSaveDataBody _customSaveData; + private static readonly DataContractJsonSerializer saveDataSerializer = new(typeof(CustomMusicSaveDataBody)); + + private static CustomMusicSaveDataBody GetCustomSaveData() + { + if (_customSaveData != null) + return _customSaveData; + + Log.LogInfo("Loading custom save data"); + var savePath = SaveFilePath; + try + { + CustomMusicSaveDataBody data; + if (!File.Exists(savePath)) + { + data = new CustomMusicSaveDataBody(); + using var fileStream = File.OpenWrite(savePath); + saveDataSerializer.WriteObject(fileStream, data); + } + else + { + using var fileStream = File.OpenRead(savePath); + data = (CustomMusicSaveDataBody) saveDataSerializer.ReadObject(fileStream); + + data.CustomTrackToEnsoRecordInfo ??= new Dictionary(); + data.CustomTrackToMusicInfoEx ??= new Dictionary(); + } + + _customSaveData = data; + return data; + } + catch (Exception e) + { + Log.LogError($"Could not load custom data, creating a fresh one\n {e}"); + } + + try + { + var data = new CustomMusicSaveDataBody(); + using var fileStream = File.OpenWrite(savePath); + saveDataSerializer.WriteObject(fileStream, data); + } + catch (Exception e) + { + Log.LogError($"Cannot save data at path {savePath}\n {e}"); + } + + return new CustomMusicSaveDataBody(); + } + + private static void SaveCustomData() + { + if (_customSaveData == null) + return; + + Log.LogInfo("Saving custom save data"); + try + { + var data = GetCustomSaveData(); + var savePath = SaveFilePath; + using var fileStream = File.OpenWrite(savePath); + saveDataSerializer.WriteObject(fileStream, data); + } + catch (Exception e) + { + Log.LogError($"Could not save custom data \n {e}"); + } + } + + #endregion + + #region Load Custom Songs + + private static readonly DataContractJsonSerializer customSongSerializer = new(typeof(CustomSong)); + private static List customSongsList; + private static readonly Dictionary idToSong = new Dictionary(); + private static readonly Dictionary uniqueIdToSong = new Dictionary(); + + public static List GetCustomSongs() + { + if (customSongsList != null) + return customSongsList; + + if (!Directory.Exists(MusicTrackDirectory)) + { + Log.LogError($"Cannot find {MusicTrackDirectory}"); + customSongsList = new List(); + return customSongsList; + } + + customSongsList = new List(); + + foreach (var musicDirectory in Directory.GetDirectories(MusicTrackDirectory)) + { + var files = Directory.GetFiles(musicDirectory); + var customSongPath = files.FirstOrDefault(x => Path.GetFileName(x) == SongDataFileName); + if (string.IsNullOrWhiteSpace(customSongPath)) + continue; + + using var fileStream = File.OpenRead(customSongPath); + var song = (CustomSong) customSongSerializer.ReadObject(fileStream); + if (song == null) + { + Log.LogError($"Cannot read {customSongPath}"); + continue; + } + + if (idToSong.ContainsKey(song.id)) + { + Log.LogError($"Cannot load song {song.songName.text} with ID {song.uniqueId} as it clashes with another, skipping it..."); + continue; + } + + if (uniqueIdToSong.ContainsKey(song.uniqueId)) + { + var uniqueIdTest = song.id.GetHashCode(); + while (uniqueIdToSong.ContainsKey(uniqueIdTest)) + uniqueIdTest++; + + Log.LogWarning($"Found song {song.songName.text} with an existing ID {song.uniqueId}, changing it to {uniqueIdTest}"); + song.uniqueId = uniqueIdTest; + } + + customSongsList.Add(song); + idToSong[song.id] = song; + uniqueIdToSong[song.uniqueId] = song; + Log.LogInfo($"Added Song {song.songName.text}:{song.id}:{song.uniqueId}"); + } + + if (customSongsList.Count == 0) + Log.LogInfo($"No tracks found"); + + return customSongsList; + } + + #endregion + + #region Loading and Initializing Data + + /// + /// This will handle loading the meta data of tracks + /// + [HarmonyPatch(typeof(MusicDataInterface))] + [HarmonyPatch(MethodType.Constructor)] + [HarmonyPatch(new[] {typeof(string)})] + [HarmonyPostfix] + private static void MusicDataInterface_Postfix(MusicDataInterface __instance, string path) + { + // This is where the metadata for tracks are read in our attempt to allow custom tracks will be to add additional metadata to the list that is created + Log.LogInfo("Injecting custom songs"); + + var customSongs = GetCustomSongs(); + + if (customSongs.Count == 0) + return; + // now that we have loaded this json, inject it into the existing `musicInfoAccessers` + var musicInfoAccessors = __instance.musicInfoAccessers; + + #region Logic from the original constructor + + for (int i = 0; i < customSongs.Count; i++) + { + if (!customSongs[i].debug || !(customSongs[i].id != "kakunin")) + { + musicInfoAccessors.Add(new MusicDataInterface.MusicInfoAccesser(customSongs[i].uniqueId, customSongs[i].id, customSongs[i].songFileName, customSongs[i].order, customSongs[i].genreNo, + customSongs[i].isDLC, customSongs[i].debug, customSongs[i].price, customSongs[i].isCap, customSongs[i].rankmatchNeedHasPlayer, new bool[5] + { + customSongs[i].branchEasy, + customSongs[i].branchNormal, + customSongs[i].branchHard, + customSongs[i].branchMania, + customSongs[i].branchUra + }, new int[5] + { + customSongs[i].starEasy, + customSongs[i].starNormal, + customSongs[i].starHard, + customSongs[i].starMania, + customSongs[i].starUra + }, new int[5] + { + customSongs[i].shinutiEasy, + customSongs[i].shinutiNormal, + customSongs[i].shinutiHard, + customSongs[i].shinutiMania, + customSongs[i].shinutiUra + }, new int[5] + { + customSongs[i].shinutiEasyDuet, + customSongs[i].shinutiNormalDuet, + customSongs[i].shinutiHardDuet, + customSongs[i].shinutiManiaDuet, + customSongs[i].shinutiUraDuet + }, new int[5] + { + customSongs[i].scoreEasy, + customSongs[i].scoreNormal, + customSongs[i].scoreHard, + customSongs[i].scoreMania, + customSongs[i].scoreUra + })); + } + } + + #endregion + + // sort this + musicInfoAccessors.Sort((MusicDataInterface.MusicInfoAccesser a, MusicDataInterface.MusicInfoAccesser b) => a.Order - b.Order); + } + + + /// + /// This will handle loading the preview data of tracks + /// + [HarmonyPatch(typeof(SongDataInterface))] + [HarmonyPatch(MethodType.Constructor)] + [HarmonyPatch(new[] {typeof(string)})] + [HarmonyPostfix] + private static void SongDataInterface_Postfix(SongDataInterface __instance, string path) + { + // This is where the metadata for tracks are read in our attempt to allow custom tracks will be to add additional metadata to the list that is created + Log.LogInfo("Injecting custom song preview data"); + var customSongs = GetCustomSongs(); + + if (customSongs.Count == 0) + return; + + // now that we have loaded this json, inject it into the existing `songInfoAccessers` + var musicInfoAccessors = __instance.songInfoAccessers; + + foreach (var customTrack in customSongs) + { + musicInfoAccessors.Add(new SongDataInterface.SongInfoAccesser(customTrack.id, customTrack.previewPos, customTrack.fumenOffsetPos)); + } + } + + + /// + /// This will handle loading the localisation of tracks + /// + [HarmonyPatch(typeof(WordDataInterface))] + [HarmonyPatch(MethodType.Constructor)] + [HarmonyPatch(new[] {typeof(string), typeof(string)})] + [HarmonyPostfix] + private static void WordDataInterface_Postfix(WordDataInterface __instance, string path, string language) + { + // This is where the metadata for tracks are read in our attempt to allow custom tracks will be to add additional metadata to the list that is created + var customSongs = GetCustomSongs(); + + if (customSongs.Count == 0) + return; + + // now that we have loaded this json, inject it into the existing `songInfoAccessers` + var musicInfoAccessors = __instance.wordListInfoAccessers; + + foreach (var customTrack in customSongs) + { + musicInfoAccessors.Add(new WordDataInterface.WordListInfoAccesser($"song_{customTrack.id}", customTrack.songName.text, customTrack.songName.font)); + musicInfoAccessors.Add(new WordDataInterface.WordListInfoAccesser($"song_sub_{customTrack.id}", customTrack.songSubtitle.text, customTrack.songSubtitle.font)); + musicInfoAccessors.Add(new WordDataInterface.WordListInfoAccesser($"song_detail_{customTrack.id}", customTrack.songDetail.text, customTrack.songDetail.font)); + } + } + + #endregion + + #region Loading / Save Custom Save Data + + /// + /// When loading, make sure to ignore custom tracks, as their IDs will be different + /// + [HarmonyPatch(typeof(SongSelectManager), "LoadSongList")] + [HarmonyPrefix] + private static bool LoadSongList_Prefix(SongSelectManager __instance) + { + #region Edited Code + + Log.LogInfo("Loading custom save"); + var customData = GetCustomSaveData(); + + #endregion + + #region Setup instanced variables / methods + + var playDataMgr = (PlayDataManager) typeof(SongSelectManager).GetField("playDataMgr", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(__instance); + var musicInfoAccess = (MusicDataInterface.MusicInfoAccesser[]) typeof(SongSelectManager).GetField("musicInfoAccess", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(__instance); + var enableKakuninSong = (bool) (typeof(SongSelectManager).GetField("enableKakuninSong", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(__instance) ?? false); + + var getLocalizedTextMethodInfo = typeof(SongSelectManager).GetMethod("GetLocalizedText", BindingFlags.NonPublic | BindingFlags.Instance); + var getLocalizedText = (string x) => (string) getLocalizedTextMethodInfo?.Invoke(__instance, new object[] {x, string.Empty}); + + var updateSortCategoryInfoMethodInfo = typeof(SongSelectManager).GetMethod("UpdateSortCategoryInfo", BindingFlags.NonPublic | BindingFlags.Instance); + var updateSortCategoryInfo = (DataConst.SongSortType x) => updateSortCategoryInfoMethodInfo?.Invoke(__instance, new object[] {x}); + + #endregion + + if (playDataMgr == null) + { + Log.LogError("Could not find playDataMgr"); + return true; + } + + __instance.UnsortedSongList.Clear(); + playDataMgr.GetMusicInfoExAll(0, out var dst); + playDataMgr.GetPlayerInfo(0, out var _); + _ = TaikoSingletonMonoBehaviour.Instance.newFriends.Count; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < musicInfoAccess.Length; j++) + { + bool flag = false; + playDataMgr.GetUnlockInfo(0, DataConst.ItemType.Music, musicInfoAccess[j].UniqueId, out var dst3); + if (!dst3.isUnlock && musicInfoAccess[j].Price != 0) + { + flag = true; + } + + if (!enableKakuninSong && musicInfoAccess[j].IsKakuninSong()) + { + flag = true; + } + + if (flag || musicInfoAccess[j].GenreNo != i) + { + continue; + } + + SongSelectManager.Song song2 = new SongSelectManager.Song(); + song2.PreviewIndex = j; + song2.Id = musicInfoAccess[j].Id; + song2.TitleKey = "song_" + musicInfoAccess[j].Id; + song2.SubKey = "song_sub_" + musicInfoAccess[j].Id; + song2.RubyKey = "song_detail_" + musicInfoAccess[j].Id; + song2.UniqueId = musicInfoAccess[j].UniqueId; + song2.SongGenre = musicInfoAccess[j].GenreNo; + song2.ListGenre = i; + song2.Order = musicInfoAccess[j].Order; + song2.TitleText = getLocalizedText("song_" + song2.Id); + song2.SubText = getLocalizedText("song_sub_" + song2.Id); + song2.DetailText = getLocalizedText("song_detail_" + song2.Id); + song2.Stars = musicInfoAccess[j].Stars; + song2.Branches = musicInfoAccess[j].Branches; + song2.HighScores = new SongSelectManager.Score[5]; + song2.HighScores2P = new SongSelectManager.Score[5]; + song2.DLC = musicInfoAccess[j].IsDLC; + song2.Price = musicInfoAccess[j].Price; + song2.IsCap = musicInfoAccess[j].IsCap; + if (TaikoSingletonMonoBehaviour.Instance.MyDataManager.SongData.GetInfo(song2.Id) != null) + { + song2.AudioStartMS = TaikoSingletonMonoBehaviour.Instance.MyDataManager.SongData.GetInfo(song2.Id).PreviewPos; + } + else + { + song2.AudioStartMS = 0; + } + + if (dst != null) + { + #region Edited Code + + MusicInfoEx data; + + if (musicInfoAccess[j].UniqueId >= SaveDataMax) + customData.CustomTrackToMusicInfoEx.TryGetValue(musicInfoAccess[j].UniqueId, out data); + else + data = dst[musicInfoAccess[j].UniqueId]; + song2.Favorite = data.favorite; + song2.NotPlayed = new bool[5]; + song2.NotCleared = new bool[5]; + song2.NotFullCombo = new bool[5]; + song2.NotDondaFullCombo = new bool[5]; + song2.NotPlayed2P = new bool[5]; + song2.NotCleared2P = new bool[5]; + song2.NotFullCombo2P = new bool[5]; + song2.NotDondaFullCombo2P = new bool[5]; + bool isNew = data.isNew; + + #endregion + + for (int k = 0; k < 5; k++) + { + playDataMgr.GetPlayerRecordInfo(0, musicInfoAccess[j].UniqueId, (EnsoData.EnsoLevelType) k, out var dst4); + song2.NotPlayed[k] = dst4.playCount <= 0; + song2.NotCleared[k] = dst4.crown < DataConst.CrownType.Silver; + song2.NotFullCombo[k] = dst4.crown < DataConst.CrownType.Gold; + song2.NotDondaFullCombo[k] = dst4.crown < DataConst.CrownType.Rainbow; + song2.HighScores[k].hiScoreRecordInfos = dst4.normalHiScore; + song2.HighScores[k].crown = dst4.crown; + playDataMgr.GetPlayerRecordInfo(1, musicInfoAccess[j].UniqueId, (EnsoData.EnsoLevelType) k, out var dst5); + song2.NotPlayed2P[k] = dst5.playCount <= 0; + song2.NotCleared2P[k] = dst4.crown < DataConst.CrownType.Silver; + song2.NotFullCombo2P[k] = dst5.crown < DataConst.CrownType.Gold; + song2.NotDondaFullCombo2P[k] = dst5.crown < DataConst.CrownType.Rainbow; + song2.HighScores2P[k].hiScoreRecordInfos = dst5.normalHiScore; + song2.HighScores2P[k].crown = dst5.crown; + } + + song2.NewSong = isNew && (song2.DLC || song2.Price > 0); + } + + __instance.UnsortedSongList.Add(song2); + } + } + + var unsortedSongList = (from song in __instance.UnsortedSongList + orderby song.SongGenre, song.Order + select song).ToList(); + typeof(SongSelectManager).GetProperty(nameof(SongSelectManager.UnsortedSongList), BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)?.SetValue(__instance, unsortedSongList); + + var songList = new List(__instance.UnsortedSongList); + typeof(SongSelectManager).GetProperty(nameof(SongSelectManager.SongList), BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)?.SetValue(__instance, songList); + + updateSortCategoryInfo(DataConst.SongSortType.Genre); + return false; + } + + /// + /// When saving favourite tracks, save the custom ones too + /// + [HarmonyPatch(typeof(SongSelectManager), "SaveFavotiteSongs")] + [HarmonyPrefix] + private static bool SaveFavotiteSongs_Prefix(SongSelectManager __instance) + { + var playDataMgr = (PlayDataManager) typeof(SongSelectManager).GetField("playDataMgr", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(__instance); + + playDataMgr.GetMusicInfoExAll(0, out var dst); + var customSaveData = GetCustomSaveData(); + + bool saveCustomData = false; + int num = 0; + foreach (SongSelectManager.Song unsortedSong in __instance.UnsortedSongList) + { + num++; + if (unsortedSong.UniqueId < SaveDataMax) + { + dst[unsortedSong.UniqueId].favorite = unsortedSong.Favorite; + playDataMgr.SetMusicInfoEx(0, unsortedSong.UniqueId, ref dst[unsortedSong.UniqueId], num >= __instance.UnsortedSongList.Count); + } + else + { + customSaveData.CustomTrackToMusicInfoEx.TryGetValue(unsortedSong.UniqueId, out var data); + saveCustomData |= data.favorite != unsortedSong.Favorite; + data.favorite = unsortedSong.Favorite; + customSaveData.CustomTrackToMusicInfoEx[unsortedSong.UniqueId] = data; + } + } + + if (saveCustomData) + SaveCustomData(); + + return false; + } + + /// + /// When loading the song, mark the custom song as not new + /// + [HarmonyPatch(typeof(CourseSelect), "EnsoConfigSubmit")] + [HarmonyPrefix] + private static bool EnsoConfigSubmit_Prefix(CourseSelect __instance) + { + var songInfoType = typeof(CourseSelect).GetNestedType("SongInfo", BindingFlags.NonPublic); + var scoreType = typeof(CourseSelect).GetNestedType("Score", BindingFlags.NonPublic); + var playerTypeEnumType = typeof(CourseSelect).GetNestedType("PlayerType", BindingFlags.NonPublic); + + var settings = (EnsoData.Settings) typeof(CourseSelect).GetField("settings", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var playDataManager = (PlayDataManager) typeof(CourseSelect).GetField("playDataManager", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var ensoDataManager = (EnsoDataManager) typeof(CourseSelect).GetField("ensoDataManager", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + + var selectedSongInfo = typeof(CourseSelect).GetField("selectedSongInfo", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var ensoMode = (EnsoMode) typeof(CourseSelect).GetField("ensoMode", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var ensoMode2P = (EnsoMode) typeof(CourseSelect).GetField("ensoMode2P", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var selectedCourse = (int) typeof(CourseSelect).GetField("selectedCourse", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var selectedCourse2P = (int) typeof(CourseSelect).GetField("selectedCourse2P", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var status = (SongSelectStatus) typeof(CourseSelect).GetField("status", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + + var SetSaveDataEnsoModeMethodInfo = typeof(CourseSelect).GetMethod("SetSaveDataEnsoMode", BindingFlags.NonPublic | BindingFlags.Instance); + var SetSaveDataEnsoMode = (object x) => (string) SetSaveDataEnsoModeMethodInfo?.Invoke(__instance, new object[] {x}); + + var songUniqueId = (int) songInfoType.GetField("UniqueId").GetValue(selectedSongInfo); + + void SetSettings() => typeof(CourseSelect).GetField("settings", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, settings); + + settings.ensoType = EnsoData.EnsoType.Normal; + settings.rankMatchType = EnsoData.RankMatchType.None; + settings.musicuid = (string) songInfoType.GetField("Id").GetValue(selectedSongInfo); + settings.musicUniqueId = songUniqueId; + settings.genre = (EnsoData.SongGenre) songInfoType.GetField("SongGenre").GetValue(selectedSongInfo); + settings.playerNum = 1; + settings.ensoPlayerSettings[0].neiroId = ensoMode.neiro; + settings.ensoPlayerSettings[0].courseType = (EnsoData.EnsoLevelType) selectedCourse; + settings.ensoPlayerSettings[0].speed = ensoMode.speed; + settings.ensoPlayerSettings[0].dron = ensoMode.dron; + settings.ensoPlayerSettings[0].reverse = ensoMode.reverse; + settings.ensoPlayerSettings[0].randomlv = ensoMode.randomlv; + settings.ensoPlayerSettings[0].special = ensoMode.special; + + var array = (Array) songInfoType.GetField("HighScores").GetValue(selectedSongInfo); + settings.ensoPlayerSettings[0].hiScore = ((HiScoreRecordInfo) scoreType.GetField("hiScoreRecordInfos").GetValue(array.GetValue(selectedCourse))).score; + + SetSettings(); + if (status.Is2PActive) + { + settings.ensoPlayerSettings[1].neiroId = ensoMode2P.neiro; + settings.ensoPlayerSettings[1].courseType = (EnsoData.EnsoLevelType) selectedCourse2P; + settings.ensoPlayerSettings[1].speed = ensoMode2P.speed; + settings.ensoPlayerSettings[1].dron = ensoMode2P.dron; + settings.ensoPlayerSettings[1].reverse = ensoMode2P.reverse; + settings.ensoPlayerSettings[1].randomlv = ensoMode2P.randomlv; + settings.ensoPlayerSettings[1].special = ensoMode2P.special; + TaikoSingletonMonoBehaviour.Instance.MyDataManager.PlayData.GetPlayerRecordInfo(1, songUniqueId, (EnsoData.EnsoLevelType) selectedCourse2P, out var dst); + settings.ensoPlayerSettings[1].hiScore = dst.normalHiScore.score; + settings.playerNum = 2; + } + + settings.debugSettings.isTestMenu = false; + settings.rankMatchType = EnsoData.RankMatchType.None; + settings.isRandomSelect = (bool) songInfoType.GetField("IsRandomSelect").GetValue(selectedSongInfo); + settings.isDailyBonus = (bool) songInfoType.GetField("IsDailyBonus").GetValue(selectedSongInfo); + ensoMode.songUniqueId = settings.musicUniqueId; + ensoMode.level = (EnsoData.EnsoLevelType) selectedCourse; + + SetSettings(); + typeof(CourseSelect).GetField("ensoMode", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, ensoMode); + SetSaveDataEnsoMode(Enum.Parse(playerTypeEnumType, "Player1")); + ensoMode2P.songUniqueId = settings.musicUniqueId; + ensoMode2P.level = (EnsoData.EnsoLevelType) selectedCourse2P; + typeof(CourseSelect).GetField("ensoMode2P", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, ensoMode2P); + SetSaveDataEnsoMode(Enum.Parse(playerTypeEnumType, "Player2")); + playDataManager.GetSystemOption(out var dst2); + int deviceTypeIndex = EnsoDataManager.GetDeviceTypeIndex(settings.ensoPlayerSettings[0].inputDevice); + settings.noteDispOffset = dst2.onpuDispLevels[deviceTypeIndex]; + settings.noteDelay = dst2.onpuHitLevels[deviceTypeIndex]; + settings.songVolume = TaikoSingletonMonoBehaviour.Instance.MySoundManager.GetVolume(SoundManager.SoundType.InGameSong); + settings.seVolume = TaikoSingletonMonoBehaviour.Instance.MySoundManager.GetVolume(SoundManager.SoundType.Se); + settings.voiceVolume = TaikoSingletonMonoBehaviour.Instance.MySoundManager.GetVolume(SoundManager.SoundType.Voice); + settings.bgmVolume = TaikoSingletonMonoBehaviour.Instance.MySoundManager.GetVolume(SoundManager.SoundType.Bgm); + settings.neiroVolume = TaikoSingletonMonoBehaviour.Instance.MySoundManager.GetVolume(SoundManager.SoundType.InGameNeiro); + settings.effectLevel = (EnsoData.EffectLevel) dst2.qualityLevel; + SetSettings(); + ensoDataManager.SetSettings(ref settings); + ensoDataManager.DecideSetting(); + if (status.Is2PActive) + { + dst2.controlType[1] = dst2.controlType[0]; + playDataManager.SetSystemOption(ref dst2); + } + + var customSaveData = GetCustomSaveData(); + + if (songUniqueId < SaveDataMax) + { + playDataManager.GetMusicInfoExAll(0, out var dst3); + dst3[songUniqueId].isNew = false; + playDataManager.SetMusicInfoEx(0, songUniqueId, ref dst3[songUniqueId]); + } + else + { + customSaveData.CustomTrackToMusicInfoEx.TryGetValue(songUniqueId, out var data); + data.isNew = false; + customSaveData.CustomTrackToMusicInfoEx[songUniqueId] = data; + SaveCustomData(); + } + + UnityEngine.Debug.Log($"p1 is {ensoMode}"); + return false; + } + + /// + /// When loading the song obtain isfavourite correctly + /// + [HarmonyPatch(typeof(KpiListCommon.MusicKpiInfo), "GetEnsoSettings")] + [HarmonyPrefix] + private static bool GetEnsoSettings_Prefix(KpiListCommon.MusicKpiInfo __instance) + { + TaikoSingletonMonoBehaviour.Instance.MyDataManager.EnsoData.CopySettings(out var dst); + __instance.music_id = dst.musicuid; + __instance.genre = (int) dst.genre; + __instance.course_type = (int) dst.ensoPlayerSettings[0].courseType; + __instance.neiro_id = dst.ensoPlayerSettings[0].neiroId; + __instance.speed = (int) dst.ensoPlayerSettings[0].speed; + __instance.dron = (int) dst.ensoPlayerSettings[0].dron; + __instance.reverse = (int) dst.ensoPlayerSettings[0].reverse; + __instance.randomlv = (int) dst.ensoPlayerSettings[0].randomlv; + __instance.special = (int) dst.ensoPlayerSettings[0].special; + PlayDataManager playData = TaikoSingletonMonoBehaviour.Instance.MyDataManager.PlayData; + playData.GetEnsoMode(out var dst2); + __instance.sort_course = (int) dst2.songSortCourse; + __instance.sort_type = (int) dst2.songSortType; + __instance.sort_filter = (int) dst2.songFilterType; + __instance.sort_favorite = (int) dst2.songFilterTypeFavorite; + MusicDataInterface.MusicInfoAccesser[] array = TaikoSingletonMonoBehaviour.Instance.MyDataManager.MusicData.musicInfoAccessers.ToArray(); + playData.GetMusicInfoExAll(0, out var dst3); + + #region edited code + + for (int i = 0; i < array.Length; i++) + { + var id = array[i].UniqueId; + if (id == dst.musicUniqueId && dst3 != null) + { + if (id < SaveDataMax) + { + __instance.is_favorite = dst3[id].favorite; + } + else + { + GetCustomSaveData().CustomTrackToMusicInfoEx.TryGetValue(id, out var data); + __instance.is_favorite = data.favorite; + } + } + } + + #endregion + + playData.GetPlayerInfo(0, out var dst4); + __instance.current_coins_num = dst4.donCoin; + __instance.total_coins_num = dst4.getCoinsInTotal; + playData.GetRankMatchSeasonRecordInfo(0, 0, out var dst5); + __instance.rank_point = dst5.rankPointMax; + + return false; + } + + /// + /// Load scores from custom save data + /// + [HarmonyPatch(typeof(PlayDataManager), "GetPlayerRecordInfo")] + [HarmonyPrefix] + public static bool GetPlayerRecordInfo_Prefix(int playerId, int uniqueId, EnsoData.EnsoLevelType levelType, out EnsoRecordInfo dst, PlayDataManager __instance) + { + if (uniqueId < SaveDataMax) + { + dst = new EnsoRecordInfo(); + return true; + } + + int num = (int) levelType; + if (num is < 0 or >= 5) + num = 0; + + // load our custom save, this will combine the scores of player1 and player2 + var saveData = GetCustomSaveData().CustomTrackToEnsoRecordInfo; + if (!saveData.TryGetValue(uniqueId, out var ensoData)) + { + ensoData = new EnsoRecordInfo[(int) EnsoData.EnsoLevelType.Num]; + saveData[uniqueId] = ensoData; + } + + dst = ensoData[num]; + return false; + } + + /// + /// Save scores to custom save data + /// + [HarmonyPatch(typeof(PlayDataManager), "UpdatePlayerScoreRecordInfo", + new Type[] {typeof(int), typeof(int), typeof(int), typeof(EnsoData.EnsoLevelType), typeof(bool), typeof(DataConst.SpecialTypes), typeof(HiScoreRecordInfo), typeof(DataConst.ResultType), typeof(bool), typeof(DataConst.CrownType)})] + [HarmonyPrefix] + public static bool UpdatePlayerScoreRecordInfo(PlayDataManager __instance, int playerId, int charaIndex, int uniqueId, EnsoData.EnsoLevelType levelType, bool isSinuchi, DataConst.SpecialTypes spTypes, HiScoreRecordInfo record, + DataConst.ResultType resultType, bool savemode, DataConst.CrownType crownType) + { + if (uniqueId < SaveDataMax) + return true; + + int num = (int) levelType; + if (num is < 0 or >= 5) + num = 0; + + var saveData = GetCustomSaveData().CustomTrackToEnsoRecordInfo; + if (!saveData.TryGetValue(uniqueId, out var ensoData)) + { + ensoData = new EnsoRecordInfo[(int) EnsoData.EnsoLevelType.Num]; + saveData[uniqueId] = ensoData; + } + + EnsoRecordInfo ensoRecordInfo = ensoData[(int) levelType]; +#pragma warning disable Harmony003 + if (ensoRecordInfo.normalHiScore.score <= record.score) + { + ensoRecordInfo.normalHiScore.score = record.score; + ensoRecordInfo.normalHiScore.combo = record.combo; + ensoRecordInfo.normalHiScore.excellent = record.excellent; + ensoRecordInfo.normalHiScore.good = record.good; + ensoRecordInfo.normalHiScore.bad = record.bad; + ensoRecordInfo.normalHiScore.renda = record.renda; + } +#pragma warning restore Harmony003 + + if (crownType != DataConst.CrownType.Off) + { + if (IsValueInRange((int) crownType, 0, 5) && ensoRecordInfo.crown <= crownType) + { + ensoRecordInfo.crown = crownType; + ensoRecordInfo.cleared = crownType >= DataConst.CrownType.Silver; + } + } + + ensoData[(int) levelType] = ensoRecordInfo; + + if (savemode && playerId == 0) + SaveCustomData(); + + return false; + + bool IsValueInRange(int myValue, int minValue, int maxValue) + { + if (myValue >= minValue && myValue < maxValue) + return true; + return false; + } + } + + /// + /// Allow for a song id > 400 + /// + [HarmonyPatch(typeof(EnsoMode), "IsValid")] + [HarmonyPrefix] + public static bool IsValid_Prefix(ref bool __result, EnsoMode __instance) + { +#pragma warning disable Harmony003 + __result = Validate(); + return false; + bool Validate() + { + // commented out this code + // if (songUniqueId < DataConst.InvalidId || songUniqueId > DataConst.MusicMax) + // { + // return false; + // } + if (!Enum.IsDefined(typeof(EnsoData.SongGenre), __instance.listGenre)) + { + return false; + } + if (__instance.neiro < 0 || __instance.neiro > DataConst.NeiroMax) + { + return false; + } + if (!Enum.IsDefined(typeof(EnsoData.EnsoLevelType), __instance.level)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.SpeedTypes), __instance.speed)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.OptionOnOff), __instance.dron)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.OptionOnOff), __instance.reverse)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.RandomLevel), __instance.randomlv)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.SpecialTypes), __instance.special)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.SongSortType), __instance.songSortType)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.SongSortCourse), __instance.songSortCourse)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.SongFilterType), __instance.songFilterType)) + { + return false; + } + if (!Enum.IsDefined(typeof(DataConst.SongFilterTypeFavorite), __instance.songFilterTypeFavorite)) + { + return false; + } + return true; + } +#pragma warning restore Harmony003 + } + + #endregion + + #region Read Fumen + + private static readonly Regex fumenFilePathRegex = new Regex("(?.*?)_(?[ehmn])(?_[12])?.bin"); + + private static readonly Dictionary playerToFumenData = new Dictionary(); + + /// + /// Read unencrypted Fumen files, save them to + /// todo dispose old fumens? + /// + /// + private static unsafe bool Read_Prefix(string filePath, ref bool __result, object __instance) + { + var type = typeof(FumenLoader).GetNestedType("PlayerData", BindingFlags.NonPublic); + + if (File.Exists(filePath)) + return true; + + // if the file doesn't exist, perhaps it's a custom song? + var fileName = Path.GetFileName(filePath); + var match = fumenFilePathRegex.Match(fileName); + if (!match.Success) + { + Log.LogError($"Cannot interpret {fileName}"); + return true; + } + + // get song id + var songId = match.Groups["songID"]; + var difficulty = match.Groups["difficulty"]; + var songIndex = match.Groups["songIndex"]; + + var newPath = Path.Combine(MusicTrackDirectory, $"{songId}\\{fileName}"); + Log.LogInfo($"Redirecting file from {filePath} to {newPath}"); + + type.GetMethod("Dispose").Invoke(__instance, new object[] { }); + type.GetField("fumenPath").SetValue(__instance, newPath); + + byte[] array = File.ReadAllBytes(newPath); + var fumenSize = array.Length; + type.GetField("fumenSize").SetValue(__instance, fumenSize); + + var fumenData = UnsafeUtility.Malloc(fumenSize, 16, Allocator.Persistent); + type.GetField("fumenData").SetValue(__instance, (IntPtr) fumenData); + + Marshal.Copy(array, 0, (IntPtr) fumenData, fumenSize); + + type.GetField("isReadEnd").SetValue(__instance, true); + type.GetField("isReadSucceed").SetValue(__instance, true); + __result = true; + + playerToFumenData[__instance] = (IntPtr) fumenData; + return false; + } + + /// + /// When asking to get a Fumen, used the ones we stored above + /// + [HarmonyPatch(typeof(FumenLoader), "GetFumenData")] + [HarmonyPrefix] + public static unsafe bool GetFumenData_Prefix(int player, ref void* __result, FumenLoader __instance) + { + var settings = (EnsoData.Settings) typeof(FumenLoader).GetField("settings", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var playerData = (Array) typeof(FumenLoader).GetField("playerData", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + + if (player >= 0 && player < settings.playerNum) + { + if (playerToFumenData.TryGetValue(playerData.GetValue(player), out var data)) + { + __result = (void*) data; + return false; + } + } + + // try loading the actual data + return true; + } + + #endregion + + #region Read Song + + private static readonly Regex musicFilePathRegex = new Regex("^song_(?.*?)$"); + + /// + /// Read an unencrypted song "asynchronously" (it does it instantly, we should have fast enough PCs right?) + /// + /// + [HarmonyPatch(typeof(CriPlayer), "LoadAsync")] + [HarmonyPostfix] + public static void LoadAsync_Postfix(CriPlayer __instance) + { + // Run this on the next frame + Plugin.Instance.StartCustomCoroutine(LoadAsync()); + + IEnumerator LoadAsync() + { + yield return null; + var sheetName = __instance.CueSheetName; + var path = Application.streamingAssetsPath + "/sound/" + sheetName + ".bin"; + + if (File.Exists(path)) + yield break; + + typeof(CriPlayer).GetField("isLoadingAsync", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, true); + typeof(CriPlayer).GetField("isCancelLoadingAsync", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, false); + typeof(CriPlayer).GetProperty("IsPrepared").SetValue(__instance, false); + typeof(CriPlayer).GetProperty("IsLoadSucceed").SetValue(__instance, false); + typeof(CriPlayer).GetProperty("LoadingState").SetValue(__instance, CriPlayer.LoadingStates.Loading); + typeof(CriPlayer).GetProperty("LoadTime").SetValue(__instance, -1f); + typeof(CriPlayer).GetField("loadStartTime", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, Time.time); + + var match = musicFilePathRegex.Match(sheetName); + if (!match.Success) + { + Log.LogError($"Cannot interpret {sheetName}"); + yield break; + } + + var songName = match.Groups["songName"]; + var newPath = Path.Combine(MusicTrackDirectory, $"{songName}\\{sheetName}.bin"); + Log.LogInfo($"Redirecting file from {path} to {newPath}"); + + var cueSheet = CriAtom.AddCueSheetAsync(sheetName, File.ReadAllBytes(newPath), null); + typeof(CriPlayer).GetProperty("CueSheet").SetValue(__instance, cueSheet); + + if (cueSheet != null) + { + typeof(CriPlayer).GetField("isLoadingAsync", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, false); + typeof(CriPlayer).GetProperty("IsLoadSucceed").SetValue(__instance, true); + + typeof(CriPlayer).GetProperty("LoadingState").SetValue(__instance, CriPlayer.LoadingStates.Finished); + typeof(CriPlayer).GetProperty("LoadTime").SetValue(__instance, 0); + yield break; + } + + Log.LogError($"Could not load music"); + typeof(CriPlayer).GetProperty("LoadingState").SetValue(__instance, CriPlayer.LoadingStates.Finished); + typeof(CriPlayer).GetField("isLoadingAsync", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, false); + } + } + + /// + /// Read an unencrypted song + /// + [HarmonyPatch(typeof(CriPlayer), "Load")] + [HarmonyPrefix] + private static bool Load_Prefix(ref bool __result, CriPlayer __instance) + { + var sheetName = __instance.CueSheetName; + var path = Application.streamingAssetsPath + "/sound/" + sheetName + ".bin"; + + if (File.Exists(path)) + return true; + + var match = musicFilePathRegex.Match(sheetName); + if (!match.Success) + { + Log.LogError($"Cannot interpret {sheetName}"); + return true; + } + + var songName = match.Groups["songName"]; + + var newPath = Path.Combine(MusicTrackDirectory, $"{songName}\\{sheetName}.bin"); + Log.LogInfo($"Redirecting file from {path} to {newPath}"); + + // load custom song + typeof(CriPlayer).GetProperty("IsPrepared").SetValue(__instance, false); + typeof(CriPlayer).GetProperty("LoadingState").SetValue(__instance, CriPlayer.LoadingStates.Loading); + typeof(CriPlayer).GetProperty("IsLoadSucceed").SetValue(__instance, false); + typeof(CriPlayer).GetProperty("LoadTime").SetValue(__instance, -1f); + typeof(CriPlayer).GetField("loadStartTime", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, Time.time); + + if (sheetName == "") + { + typeof(CriPlayer).GetProperty("LoadingState").SetValue(__instance, CriPlayer.LoadingStates.Finished); + __result = false; + return false; + } + + // + var cueSheet = CriAtom.AddCueSheetAsync(sheetName, File.ReadAllBytes(newPath), null); + typeof(CriPlayer).GetProperty("CueSheet").SetValue(__instance, cueSheet); + + if (cueSheet != null) + { + __result = true; + return false; + } + + typeof(CriPlayer).GetProperty("LoadingState").SetValue(__instance, CriPlayer.LoadingStates.Finished); + __result = false; + return false; + } + + #endregion + + #region Data Structures + + [Serializable] + [DataContract(Name = "CustomSaveData")] + public class CustomMusicSaveDataBody + { + [DataMember] public Dictionary CustomTrackToMusicInfoEx = new(); + + [DataMember] public Dictionary CustomTrackToEnsoRecordInfo = new(); + } + + // ReSharper disable once ClassNeverInstantiated.Global + [DataContract(Name = "CustomSong")] + [Serializable] + public class CustomSong + { + // Song Details + [DataMember] public int uniqueId; + [DataMember] public string id; + [DataMember] public string songFileName; + [DataMember] public int order; + [DataMember] public int genreNo; + [DataMember] public bool isDLC; + [DataMember] public bool debug; + [DataMember] public int price; + [DataMember] public bool isCap; + [DataMember] public int rankmatchNeedHasPlayer; + [DataMember] public bool branchEasy; + [DataMember] public bool branchNormal; + [DataMember] public bool branchHard; + [DataMember] public bool branchMania; + [DataMember] public bool branchUra; + [DataMember] public int starEasy; + [DataMember] public int starNormal; + [DataMember] public int starHard; + [DataMember] public int starMania; + [DataMember] public int starUra; + [DataMember] public int shinutiEasy; + [DataMember] public int shinutiNormal; + [DataMember] public int shinutiHard; + [DataMember] public int shinutiMania; + [DataMember] public int shinutiUra; + [DataMember] public int shinutiEasyDuet; + [DataMember] public int shinutiNormalDuet; + [DataMember] public int shinutiHardDuet; + [DataMember] public int shinutiManiaDuet; + [DataMember] public int shinutiUraDuet; + [DataMember] public int scoreEasy; + [DataMember] public int scoreNormal; + [DataMember] public int scoreHard; + [DataMember] public int scoreMania; + [DataMember] public int scoreUra; + + // Preview Details + [DataMember] public int previewPos; + [DataMember] public int fumenOffsetPos; + + // LocalisationDetails + /// + /// Song Title + /// + /// A Cruel Angel's Thesis + /// + /// + [DataMember] public TextEntry songName; + + /// + /// Origin of the song + /// + /// From \" Neon Genesis EVANGELION \" + /// + /// + [DataMember] public TextEntry songSubtitle; + + /// + /// Extra details for the track, sometimes used to say it's Japanese name + /// + /// 残酷な天使のテーゼ + /// + /// + [DataMember] public TextEntry songDetail; + + /// + /// What font to use for this titles font + /// + [DataMember] public int songFontType; + } + + [Serializable] + public class TextEntry + { + /// + /// The text to display + /// + public string text; + + /// + /// 0 == Japanese + /// 1 == English + /// 2 == Traditional Chinese + /// 3 == Simplified Chinese + /// 4 == Korean + /// + public int font; + } + + #endregion +} \ No newline at end of file diff --git a/Plugin.cs b/Plugin.cs index bf78b2b..e43b8fe 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,19 +1,99 @@ -using BepInEx; +using System; +using System.Collections; +using BepInEx; +using BepInEx.Configuration; +using BepInEx.Logging; +using FlutoTaikoMods; using HarmonyLib; +using HarmonyLib.Tools; namespace TaikoMods { [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] public class Plugin : BaseUnityPlugin { + public ConfigEntry ConfigSkipSplashScreen; + public ConfigEntry ConfigDisableScreenChangeOnFocus; + public ConfigEntry ConfigFixSignInScreen; + public ConfigEntry ConfigEnableCustomSongs; + public ConfigEntry ConfigSongDirectory; + public ConfigEntry ConfigSaveDirectory; + + public static Plugin Instance; private Harmony _harmony; + public static ManualLogSource Log; + private void Awake() { + Instance = this; + Log = Logger; + Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!"); + SetupConfig(); + + SetupHarmony(); + } + + private void SetupConfig() + { + var userFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + + ConfigFixSignInScreen = Config.Bind("General", + "FixSignInScreen", + true, + "When true this will apply the patch to fix signing into Xbox Live"); + + ConfigSkipSplashScreen = Config.Bind("General", + "SkipSplashScreen", + true, + "When true this will skip the intro"); + + ConfigDisableScreenChangeOnFocus = Config.Bind("General", + "DisableScreenChangeOnFocus", + false, + "When focusing this wont do anything jank, I thnk"); + + ConfigEnableCustomSongs = Config.Bind("CustomSongs", + "EnableCustomSongs", + true, + "When true this will load custom mods"); + + ConfigSongDirectory = Config.Bind("CustomSongs", + "SongDirectory", + $"{userFolder}/Documents/TaikoTheDrumMasterMods/customSongs", + "The directory where custom tracks are stored"); + + ConfigSaveDirectory = Config.Bind("CustomSongs", + "SaveDirectory", + $"{userFolder}/Documents/TaikoTheDrumMasterMods/saves", + "The directory where saves are stored"); + } + + private void SetupHarmony() + { // Patch methods _harmony = new Harmony(PluginInfo.PLUGIN_GUID); - _harmony.PatchAll(); + + if (ConfigSkipSplashScreen.Value) + _harmony.PatchAll(typeof(SkipSplashScreen)); + + if (ConfigFixSignInScreen.Value) + _harmony.PatchAll(typeof(SignInPatch)); + + if (ConfigDisableScreenChangeOnFocus.Value) + _harmony.PatchAll(typeof(DisableScreenChangeOnFocus)); + + if (ConfigEnableCustomSongs.Value) + { + _harmony.PatchAll(typeof(MusicPatch)); + MusicPatch.Setup(_harmony); + } + } + + public void StartCustomCoroutine(IEnumerator enumerator) + { + StartCoroutine(enumerator); } } } diff --git a/SignInPatch.cs b/SignInPatch.cs index 7de6465..415857e 100644 --- a/SignInPatch.cs +++ b/SignInPatch.cs @@ -1,6 +1,7 @@ using System.Reflection; using HarmonyLib; using Microsoft.Xbox; +using TaikoMods; using UnityEngine; namespace FlutoTaikoMods; @@ -12,19 +13,23 @@ namespace FlutoTaikoMods; [HarmonyPatch("SignIn")] public static class SignInPatch { + // ReSharper disable once InconsistentNaming private static bool Prefix(GdkHelpers __instance) { // Only apply this patch if we're on version 1.0.0 if (Application.version != "1.0.0") return false; - - UnityEngine.Debug.Log("Patching sign in to force the user to be prompted to sign in"); + + Plugin.Log.LogInfo("Patching sign in to force the user to be prompted to sign in"); var methodInfo = typeof(GdkHelpers).GetMethod("SignInImpl", BindingFlags.NonPublic | BindingFlags.Instance); if (methodInfo == null) - return false; - - methodInfo.Invoke(__instance, new object[] { false}); - return true; + { + Plugin.Log.LogError("Failed to patch"); + return true; + } + + methodInfo.Invoke(__instance, new object[] {true}); + return false; } } \ No newline at end of file diff --git a/SkipSplashScreen.cs b/SkipSplashScreen.cs new file mode 100644 index 0000000..66eed61 --- /dev/null +++ b/SkipSplashScreen.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; + +namespace TaikoMods; + +[HarmonyPatch] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class SkipSplashScreen +{ + /// + /// Simply load the next scene, I don't think this scene does anything specific? + /// + [HarmonyPatch(typeof(BootManager), "Start")] + [HarmonyPostfix] + private static void BootManager_Postfix(BootManager __instance) + { + TaikoSingletonMonoBehaviour.Instance.MySceneManager.ChangeScene("Title", false); + __instance.gameObject.SetActive(false); + } +} \ No newline at end of file diff --git a/TaikoMods.csproj b/TaikoMods.csproj index db7bf72..5b72c99 100644 --- a/TaikoMods.csproj +++ b/TaikoMods.csproj @@ -3,12 +3,13 @@ net48 com.fluto.taikomods - Cool Taiko mods + Fixes Taiko issues and allows custom songs 0.0.1 true latest TaikoMods com.fluto.taikomods + 1.0.0 @@ -26,6 +27,9 @@ D:\XboxGames\T Tablet\Content\Taiko no Tatsujin_Data\Managed\Assembly-CSharp.dll + + D:\XboxGames\T Tablet\Content\Taiko no Tatsujin_Data\Managed\Assembly-CSharp-firstpass.dll +