Пример #1
0
        internal static IEnumerator DelayedStart(BeatmapObjectSpawnController beatmapObjectSpawnController)
        {
            yield return(new WaitForEndOfFrame());

            BeatmapObjectSpawnController = beatmapObjectSpawnController;

            // prone to breaking if anything else implements these interfaces
            BeatmapObjectManager            beatmapObjectManager = _beatmapObjectSpawnAccessor(ref beatmapObjectSpawnController) as BeatmapObjectManager;
            BeatmapObjectCallbackController coreSetup            = _callbackControllerAccessor(ref beatmapObjectSpawnController) as BeatmapObjectCallbackController;

            IAudioTimeSource = _audioTimeSourceAccessor(ref coreSetup);
            IReadonlyBeatmapData beatmapData = _beatmapDataAccessor(ref coreSetup);

            beatmapObjectManager.noteWasCutEvent -= NoteColorizer.ColorizeSaber;
            beatmapObjectManager.noteWasCutEvent += NoteColorizer.ColorizeSaber;

            if (Harmony.HasAnyPatches(HARMONYID))
            {
                if (beatmapData is CustomBeatmapData customBeatmap)
                {
                    if (ChromaConfig.Instance.EnvironmentEnhancementsEnabled)
                    {
                        EnvironmentEnhancementManager.Init(customBeatmap, beatmapObjectSpawnController.noteLinesDistance);
                    }
                }

                // please let me kill legacy
                LegacyLightHelper.Activate(beatmapData.beatmapEventsData);
            }
        }
Пример #2
0
        public override void Load()
        {
            try {
                Logger = Log;
                GameOptionsData.RecommendedImpostors = GameOptionsData.MaxImpostors = Enumerable.Repeat <int>(255, 255).ToArray <int>();
                Logger.LogMessage("Patching harmony...");
                Logger.LogInfo("Succesfully loaded CorsacHats");

                Attribute[] attrs = Attribute.GetCustomAttributes(typeof(MyHats.HatManagerHatsPatch));

                foreach (Attribute attr in attrs)
                {
                    Logger.LogMessage($"Looking {attr.GetType()}");
                }
                if (attrs.Length == 0)
                {
                    Logger.LogError("HarmonyPatchers not found.");
                }
                _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly());
                Logger.LogMessage($"Is patched? {Harmony.HasAnyPatches(_harmony.Id)}");
            }
            catch (Exception e) {
                Log.LogError(e);
                throw e;
            }
        }
Пример #3
0
        static CrossPromotion()
        {
            if (Harmony.HasAnyPatches(_crosspromotion))
            {
                return;
            }

            var instance = new Harmony(_crosspromotion);

            _ = instance.Patch(
                SymbolExtensions.GetMethodInfo(() => ModLister.RebuildModList()),
                postfix: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => ModLister_RebuildModList_Postfix()))
                );

            _ = instance.Patch(
                AccessTools.DeclaredMethod(typeof(Page_ModsConfig), nameof(Page_ModsConfig.PostClose)),
                postfix: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => Page_ModsConfig_PostClose_Postfix()))
                );

            _ = instance.Patch(
                AccessTools.DeclaredMethod(typeof(WorkshopItems), "Notify_Subscribed"),
                postfix: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => WorkshopItems_Notify_Subscribed_Postfix(new PublishedFileId_t(0))))
                );

            _ = instance.Patch(
                AccessTools.DeclaredMethod(typeof(Page_ModsConfig), nameof(Page_ModsConfig.DoWindowContents)),
                transpiler: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => Page_ModsConfig_DoWindowContents_Transpiler(null, null)))
                );
        }
Пример #4
0
        public static void ToggleNoodlePatches(bool value, BeatmapData beatmapData, float defaultNoteJumpMovementSpeed, float defaultNoteJumpStartBeatOffset)
        {
            if (value)
            {
                if (!Harmony.HasAnyPatches(HARMONYID))
                {
                    NoodlePatches.ForEach(n => harmony.Patch(n.originalMethod,
                                                             n.prefix != null ? new HarmonyMethod(n.prefix) : null,
                                                             n.postfix != null ? new HarmonyMethod(n.postfix) : null,
                                                             n.transpiler != null ? new HarmonyMethod(n.transpiler) : null));
                }

                // var njs/spawn offset stuff below

                // there is some ambiguity with these variables but who frikkin cares
                float _startHalfJumpDurationInBeats = 4;
                float _maxHalfJumpDistance          = 18;
                float _moveDuration = 0.5f;

                foreach (BeatmapLineData beatmapLineData in beatmapData.beatmapLinesData)
                {
                    foreach (BeatmapObjectData beatmapObjectData in beatmapLineData.beatmapObjectsData)
                    {
                        dynamic customData;
                        if (beatmapObjectData is CustomObstacleData || beatmapObjectData is CustomNoteData)
                        {
                            customData = beatmapObjectData;
                        }
                        else
                        {
                            return;
                        }
                        dynamic dynData = customData.customData;
                        float   noteJumpMovementSpeed   = (float?)Trees.at(dynData, NOTEJUMPSPEED) ?? defaultNoteJumpMovementSpeed;
                        float   noteJumpStartBeatOffset = (float?)Trees.at(dynData, SPAWNOFFSET) ?? defaultNoteJumpStartBeatOffset;

                        // how do i not repeat this in a reasonable way
                        float num  = 60f / (float)Trees.at(dynData, "bpm");
                        float num2 = _startHalfJumpDurationInBeats;
                        while (noteJumpMovementSpeed * num * num2 > _maxHalfJumpDistance)
                        {
                            num2 /= 2f;
                        }
                        num2 += noteJumpStartBeatOffset;
                        if (num2 < 1f)
                        {
                            num2 = 1f;
                        }
                        float _jumpDuration = num * num2 * 2f;
                        dynData.aheadTime = _moveDuration + _jumpDuration * 0.5f;
                    }
                    beatmapLineData.beatmapObjectsData = beatmapLineData.beatmapObjectsData.OrderBy(n => n.time - (float)((dynamic)n).customData.aheadTime).ToArray();
                }
            }
            else
            {
                harmony.UnpatchAll(HARMONYID);
            }
        }
Пример #5
0
 static bool Prefix(string modName)
 {
     if (Harmony.HasAnyPatches("com." + modName + ".patch"))
     {
         MPLog.Log(modName + " is not being reinstalled", LogLevel.Info);
         return(false);
     }
     return(true);
 }
Пример #6
0
        private void PatchIfCheatsExist()
        {
            // Only patch on state change
            var cfg = Configs.zCheat;

            if (Configs.MagPalm.Enable.Value && (cfg.CursedPalms.Value || cfg.SizeLimit.Value > FVRPhysicalObject.FVRPhysicalObjectSize.Medium))
            {
                Logger.LogDebug("TNH score submission disabled");
                _harmonyCheat.PatchAll(typeof(ScorePatches));
            }
            else if (Harmony.HasAnyPatches(HARMONY_GUID_CHEAT))
            {
                Logger.LogDebug("TNH score submission enabled");
                _harmonyCheat.UnpatchSelf();
            }
        }
Пример #7
0
 static void Prepare()
 {
     if (Harmony.HasAnyPatches("com.MoreLuaPower.patch"))
     {
         return;
     }
     Debug.Log("MoreLuaPower Version 2.1");
     LuaPowerData.Setup();
     LuaPowerCustomEnumsSetup.Setup();
     if (S.I.GetComponent <PowerMonoBehavior>() == null)
     {
         S.I.gameObject.AddComponent <PowerMonoBehavior>();
     }
     if (!LuaPowerData.customEnums[typeof(Effect)].Contains("Lua"))
     {
         LuaPowerData.customEnums[typeof(Effect)].Add("Lua");
     }
     //MoreLuaPowerTesting.Test();
 }
Пример #8
0
        public WorkManagerMod(ModContentPack content) : base(content)
        {
            GetSettings <Settings>();
            var harmony = new Harmony("LordKuper.WorkManager");

            harmony.PatchAll(Assembly.GetExecutingAssembly());
            if (Harmony.HasAnyPatches("fluffy.worktab"))
            {
                ApplyWorkTabPatch(harmony);
            }
            if (Harmony.HasAnyPatches("rimworld.moreThanCapable"))
            {
                #if DEBUG
                Log.Message("Work Manager: MoreThanCapable detected.", true);
                #endif
                Settings.IsBadWorkMethod =
                    AccessTools.Method(AccessTools.TypeByName("MoreThanCapable.MoreThanCapableMod"), "IsBadWork");
            }
        }
Пример #9
0
        internal static void ToggleTechniPatches(bool value, TechniPatchType patchType)
        {
            if (value)
            {
                if (!Harmony.HasAnyPatches(HARMONYID + Enum.GetName(typeof(TechniPatchType), patchType)))
                {
                    List <TechniPatchData> list = null;
                    switch (patchType)
                    {
                    case TechniPatchType.LIGHTS:
                        list = _lightPatches;
                        break;

                    case TechniPatchType.OBSTACLES:
                        list = _obstaclePatches;
                        break;

                    case TechniPatchType.NOTES:
                        list = _notePatches;
                        break;

                    case TechniPatchType.BOMBS:
                        list = _bombPatches;
                        break;
                    }

                    if (list != null)
                    {
                        Harmony harmony = _techniPatchInstances[patchType];
                        list.ForEach(n => harmony.Patch(
                                         n.OriginalMethod,
                                         n.Prefix != null ? new HarmonyMethod(n.Prefix) : null,
                                         n.Postfix != null ? new HarmonyMethod(n.Postfix) : null,
                                         n.Transpiler != null ? new HarmonyMethod(n.Transpiler) : null));
                    }
                }
            }
            else
            {
                _techniPatchInstances[patchType].UnpatchAll(HARMONYID + Enum.GetName(typeof(TechniPatchType), patchType));
            }
        }
        public void Init(HttpApplication app)
        {
            try
            {
                var patchId = GetType().FullName;
                var harmony = new Harmony(patchId);
                if (!Harmony.HasAnyPatches(patchId))
                {
                    harmony.PatchAll();
                    Console.WriteLine($"Module {GetType().FullName} initialized");
                }
            }
            catch (Exception e)
            {
                Console.Error.WriteLine($"Module {GetType().FullName} failed to initialized");
                Console.Error.WriteLine(e);
            }

            app.Error += OnError;
        }
Пример #11
0
        public static void ToggleChromaPatches(bool value)
        {
            ChromaIsActive = value;

            if (value)
            {
                if (!Harmony.HasAnyPatches(HARMONYID))
                {
                    _chromaPatches.ForEach(n => _harmonyInstance.Patch(
                                               n.OriginalMethod,
                                               n.Prefix != null ? new HarmonyMethod(n.Prefix) : null,
                                               n.Postfix != null ? new HarmonyMethod(n.Postfix) : null,
                                               n.Transpiler != null ? new HarmonyMethod(n.Transpiler) : null));
                }
            }
            else
            {
                _harmonyInstance.UnpatchAll(HARMONYID);
            }
        }
Пример #12
0
        public void ApplyHarmonyPatches(bool enabled)
        {
            try
            {
                if (enabled && !Harmony.HasAnyPatches(HarmonyId))
                {
                    Log.Info("Applying Harmony patches...");
                    _harmony.PatchAll(Assembly.GetExecutingAssembly());
                }
                else if (!enabled && Harmony.HasAnyPatches(HarmonyId))
                {
                    Log.Info("Removing Harmony patches...");
                    _harmony.UnpatchAll(HarmonyId);

                    SetDataFromLevelAsync.OnUnpatch();
                }
            }
            catch (Exception ex)
            {
                Log.Error("Error while applying Harmony patches.\n" + ex.ToString());
            }
        }
Пример #13
0
        public static void ToggleNoodlePatches(bool value)
        {
            if (value)
            {
                if (!Harmony.HasAnyPatches(HARMONYID))
                {
                    _noodlePatches.ForEach(n => _harmonyInstance.Patch(
                                               n.OriginalMethod,
                                               n.Prefix != null ? new HarmonyMethod(n.Prefix) : null,
                                               n.Postfix != null ? new HarmonyMethod(n.Postfix) : null,
                                               n.Transpiler != null ? new HarmonyMethod(n.Transpiler) : null));

                    CustomJSONData.CustomEventCallbackController.customEventCallbackControllerInit += Animation.AnimationController.CustomEventCallbackInit;
                }
            }
            else
            {
                _harmonyInstance.UnpatchAll(HARMONYID);

                CustomJSONData.CustomEventCallbackController.customEventCallbackControllerInit -= Animation.AnimationController.CustomEventCallbackInit;
            }
        }
Пример #14
0
        internal static IEnumerator DelayedStart(BeatmapObjectSpawnController beatmapObjectSpawnController)
        {
            yield return(new WaitForEndOfFrame());

            BeatmapObjectSpawnController = beatmapObjectSpawnController;

            // prone to breaking if anything else implements these interfaces
            BeatmapObjectManager            beatmapObjectManager = _beatmapObjectSpawnAccessor(ref beatmapObjectSpawnController) as BeatmapObjectManager;
            BeatmapObjectCallbackController coreSetup            = _callbackControllerAccessor(ref beatmapObjectSpawnController) as BeatmapObjectCallbackController;

            IAudioTimeSource = _audioTimeSourceAccessor(ref coreSetup);
            IReadonlyBeatmapData beatmapData = _beatmapDataAccessor(ref coreSetup);

            beatmapObjectManager.noteWasCutEvent -= NoteColorizer.ColorizeSaber;
            beatmapObjectManager.noteWasCutEvent += NoteColorizer.ColorizeSaber;

            if (Harmony.HasAnyPatches(HARMONYID))
            {
                if (beatmapData is CustomBeatmapData customBeatmap)
                {
                    if (ChromaConfig.Instance.EnvironmentEnhancementsEnabled)
                    {
                        // Spaghetti code below until I can figure out a better way of doing this
                        dynamic       dynData       = customBeatmap.beatmapCustomData;
                        List <object> objectsToKill = Trees.at(dynData, ENVIRONMENTREMOVAL);

                        // seriously what the f**k beat games
                        // GradientBackground permanently yeeted because it looks awful and can ruin multi-colored chroma maps
                        if (objectsToKill == null)
                        {
                            objectsToKill = new List <object>();
                        }

                        objectsToKill.Add("GradientBackground");

                        if (objectsToKill != null)
                        {
                            IEnumerable <GameObject> gameObjects = Resources.FindObjectsOfTypeAll <GameObject>();
                            foreach (string s in objectsToKill.Cast <string>())
                            {
                                if (s == "TrackLaneRing" || s == "BigTrackLaneRing")
                                {
                                    foreach (GameObject n in gameObjects.Where(obj => obj.name.Contains(s)))
                                    {
                                        if (s == "TrackLaneRing" && n.name.Contains("Big"))
                                        {
                                            continue;
                                        }

                                        n.SetActive(false);
                                    }
                                }
                                else
                                {
                                    foreach (GameObject n in gameObjects
                                             .Where(obj => obj.name.Contains(s) && (obj.scene.name?.Contains("Environment") ?? false) && (!obj.scene.name?.Contains("Menu") ?? false)))
                                    {
                                        n.SetActive(false);
                                    }
                                }
                            }
                        }
                    }
                }

                // please let me kill legacy
                LegacyLightHelper.Activate(beatmapData.beatmapEventsData);
            }
        }
Пример #15
0
 public bool HasAnyPatches(string harmonyId) =>
 Harmony.HasAnyPatches(harmonyId);
Пример #16
0
        internal static IEnumerator DelayedStart()
        {
            yield return(new WaitForEndOfFrame());

            AudioTimeSyncController = Resources.FindObjectsOfTypeAll <AudioTimeSyncController>().First();
            BeatmapObjectSpawnController beatmapObjectSpawnController = Resources.FindObjectsOfTypeAll <BeatmapObjectSpawnController>().First();
            BeatmapObjectManager         beatmapObjectManager         = _beatmapObjectSpawnAccessor(ref beatmapObjectSpawnController) as BeatmapObjectManager;

            SongBPM = beatmapObjectSpawnController.currentBpm;
            BeatmapObjectCallbackController coreSetup   = Resources.FindObjectsOfTypeAll <BeatmapObjectCallbackController>().First();
            IReadonlyBeatmapData            beatmapData = coreSetup.GetField <IReadonlyBeatmapData, BeatmapObjectCallbackController>("_beatmapData");

            beatmapObjectManager.noteWasCutEvent -= NoteColorizer.ColorizeSaber;
            beatmapObjectManager.noteWasCutEvent += NoteColorizer.ColorizeSaber;

            if (ChromaConfig.Instance.LightshowModifier)
            {
                foreach (BeatmapLineData b in beatmapData.beatmapLinesData)
                {
                    BeatmapLineData refBeatmapLineData = b;
                    _beatmapObjectsDataAccessor(ref refBeatmapLineData) = b.beatmapObjectsData.Where((source, index) => b.beatmapObjectsData[index].beatmapObjectType != BeatmapObjectType.Note).ToList();
                }

                foreach (Saber saber in Resources.FindObjectsOfTypeAll <Saber>())
                {
                    saber.gameObject.SetActive(false);
                }

                BS_Utils.Gameplay.ScoreSubmission.DisableSubmission("Chroma");

                if (ChromaConfig.Instance.PlayersPlace)
                {
                    GameObject.Find("PlayersPlace")?.SetActive(false);
                }

                if (ChromaConfig.Instance.Spectrograms)
                {
                    GameObject.Find("Spectrograms")?.SetActive(false);
                }

                if (ChromaConfig.Instance.BackColumns)
                {
                    GameObject.Find("BackColumns")?.SetActive(false);
                }

                if (ChromaConfig.Instance.Buildings)
                {
                    GameObject.Find("Buildings")?.SetActive(false);
                }
            }

            if (Harmony.HasAnyPatches(HARMONYID))
            {
                if (beatmapData is CustomBeatmapData customBeatmap)
                {
                    if (ChromaConfig.Instance.EnvironmentEnhancementsEnabled)
                    {
                        // Spaghetti code below until I can figure out a better way of doing this
                        dynamic       dynData       = customBeatmap.beatmapCustomData;
                        List <object> objectsToKill = Trees.at(dynData, "_environmentRemoval");
                        if (objectsToKill != null)
                        {
                            IEnumerable <GameObject> gameObjects = Resources.FindObjectsOfTypeAll <GameObject>();
                            foreach (string s in objectsToKill.Cast <string>())
                            {
                                if (s == "TrackLaneRing" || s == "BigTrackLaneRing")
                                {
                                    foreach (GameObject n in gameObjects.Where(obj => obj.name.Contains(s)))
                                    {
                                        if (s == "TrackLaneRing" && n.name.Contains("Big"))
                                        {
                                            continue;
                                        }

                                        n.SetActive(false);
                                    }
                                }
                                else
                                {
                                    foreach (GameObject n in gameObjects
                                             .Where(obj => obj.name.Contains(s) && (obj.scene.name?.Contains("Environment") ?? false) && (!obj.scene.name?.Contains("Menu") ?? false)))
                                    {
                                        n.SetActive(false);
                                    }
                                }
                            }
                        }
                    }
                }

                // please let me kill legacy
                LegacyLightHelper.Activate(beatmapData.beatmapEventsData);
            }
        }
Пример #17
0
        static void Main_Example()
        {
            // <create>
            var harmony = new Harmony("com.company.project.product");

            // </create>

            // <debug>
            Harmony.DEBUG = true;
            // </debug>

            // <log>
            FileLog.Log("something");
            // or buffered:
            FileLog.LogBuffered("A");
            FileLog.LogBuffered("B");
            FileLog.FlushBuffer();             /* don't forget to flush */
            // </log>

            // <patch_annotation>
            var assembly = Assembly.GetExecutingAssembly();

            harmony.PatchAll(assembly);

            // or implying current assembly:
            harmony.PatchAll();
            // </patch_annotation>

            void PatchManual()
            {
                // <patch_manual>
                // add null checks to the following lines, they are omitted for clarity
                var original = typeof(TheClass).GetMethod("TheMethod");
                var prefix   = typeof(MyPatchClass1).GetMethod("SomeMethod");
                var postfix  = typeof(MyPatchClass2).GetMethod("SomeMethod");

                harmony.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix));

                // You can use named arguments to specify certain patch types only:
                harmony.Patch(original, postfix: new HarmonyMethod(postfix));
                harmony.Patch(original, prefix: new HarmonyMethod(prefix), transpiler: new HarmonyMethod(transpiler));
                // </patch_manual>

                // <patch_method>
                var harmonyPostfix = new HarmonyMethod(postfix)
                {
                    priority = Priority.Low,
                    before   = new[] { "that.other.harmony.user" }
                };
                // </patch_method>
            }

            PatchManual();

            // <patch_getall>
            var originalMethods = Harmony.GetAllPatchedMethods();

            foreach (var method in originalMethods)
            {
            }
            // </patch_getall>

            // <patch_get>
            var myOriginalMethods = harmony.GetPatchedMethods();

            foreach (var method in myOriginalMethods)
            {
            }
            // </patch_get>

            void PatchInfo()
            {
                // <patch_info>
                // get the MethodBase of the original
                var original = typeof(TheClass).GetMethod("TheMethod");

                // retrieve all patches
                var patches = Harmony.GetPatchInfo(original);

                if (patches is null)
                {
                    return;                                  // not patched
                }
                // get a summary of all different Harmony ids involved
                FileLog.Log("all owners: " + patches.Owners);

                // get info about all Prefixes/Postfixes/Transpilers
                foreach (var patch in patches.Prefixes)
                {
                    FileLog.Log("index: " + patch.index);
                    FileLog.Log("owner: " + patch.owner);
                    FileLog.Log("patch method: " + patch.PatchMethod);
                    FileLog.Log("priority: " + patch.priority);
                    FileLog.Log("before: " + patch.before);
                    FileLog.Log("after: " + patch.after);
                }
                // </patch_info>
            }

            PatchInfo();

            // <patch_has>
            if (Harmony.HasAnyPatches("their.harmony.id"))
            {
            }
            // </patch_has>

            // <version>
            var dict = Harmony.VersionInfo(out var myVersion);

            FileLog.Log("My version: " + myVersion);
            foreach (var entry in dict)
            {
                var id      = entry.Key;
                var version = entry.Value;
                FileLog.Log("Mod " + id + " uses Harmony version " + version);
            }
            // </version>
        }
Пример #18
0
        private void SetOneColorMode(bool value)
        {
            if (value)
            {
                Logger.log.Debug("Applying One Color patches...");
                var original = typeof(NoteBasicCutInfo).GetMethod("GetBasicCutInfo");
                var patch    = typeof(HarmonyPatches.NoteBasicCutInfo_GetBasicCutInfo).GetMethod("Postfix");
                Plugin.HarmonyInstance.Patch(original, postfix: new HarmonyMethod(patch));

                original = typeof(GameNoteController).GetMethod("HandleCut");
                patch    = typeof(HarmonyPatches.GameNoteController_HandleCut).GetMethod("Postfix");
                Plugin.HarmonyInstance.Patch(original, postfix: new HarmonyMethod(patch));

                original = typeof(ColorManager).GetMethod("ColorForSaberType");
                patch    = typeof(HarmonyPatches.ColorManager_ColorForType).GetMethod("Prefix");
                Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch));
                original = typeof(ColorManager).GetMethod("ColorForNoteType");
                Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch));

                original = typeof(BeatmapObjectSpawnController).GetMethod("SpawnNote");
                patch    = typeof(HarmonyPatches.BeatmapObjectSpawnController_SpawnNote).GetMethod("Prefix");
                Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch));

                original = typeof(NoteCutSoundEffectManager).GetMethod("BeatmapObjectCallback");
                patch    = typeof(HarmonyPatches.NoteCutSoundEffectManager_BeatmapObjectCallback).GetMethod("Prefix");
                Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch));

                parserParams.EmitEvent("show-colors");
            }
            else
            {
                if (Harmony.HasAnyPatches("com.steven.BeatSaber.MoreModifiers"))
                {
                    Logger.log.Debug("Removing One Color patches...");
                    var original = typeof(NoteBasicCutInfo).GetMethod("GetBasicCutInfo");
                    var patch    = typeof(HarmonyPatches.NoteBasicCutInfo_GetBasicCutInfo).GetMethod("Postfix");
                    Plugin.HarmonyInstance.Unpatch(original, patch);

                    original = typeof(GameNoteController).GetMethod("HandleCut");
                    patch    = typeof(HarmonyPatches.GameNoteController_HandleCut).GetMethod("Postfix");
                    Plugin.HarmonyInstance.Unpatch(original, patch);

                    original = typeof(ColorManager).GetMethod("ColorForSaberType");
                    patch    = typeof(HarmonyPatches.ColorManager_ColorForType).GetMethod("Prefix");
                    Plugin.HarmonyInstance.Unpatch(original, patch);
                    original = typeof(ColorManager).GetMethod("ColorForNoteType");
                    Plugin.HarmonyInstance.Unpatch(original, patch);

                    original = typeof(BeatmapObjectSpawnController).GetMethod("SpawnNote");
                    patch    = typeof(HarmonyPatches.BeatmapObjectSpawnController_SpawnNote).GetMethod("Prefix");
                    Plugin.HarmonyInstance.Unpatch(original, patch);

                    original = typeof(NoteCutSoundEffectManager).GetMethod("BeatmapObjectCallback");
                    patch    = typeof(HarmonyPatches.NoteCutSoundEffectManager_BeatmapObjectCallback).GetMethod("Prefix");
                    Plugin.HarmonyInstance.Unpatch(original, patch);

                    HarmonyPatches.GameNoteController_HandleCut.sabers  = null;
                    HarmonyPatches.GameNoteController_HandleCut.spawner = null;
                }
            }
        }
Пример #19
0
    public void Awake()
    {
        Instance = this;

        MainThreadQueue.Initialize();

        GameInfo            = GetComponent <KMGameInfo>();
        SettingWarning      = gameObject.Traverse("UI", "SettingWarning");
        AdvantageousWarning = gameObject.Traverse("UI", "AdvantageousWarning");
        Tips.TipMessage     = gameObject.Traverse("UI", "TipMessage");
        BetterCasePicker.BombCaseGenerator = GetComponentInChildren <BombCaseGenerator>();
        DemandBasedLoading.LoadingScreen   = gameObject.Traverse <CanvasGroup>("UI", "LoadingModules");

        CaseGeneratorWarning = MakeSettingWarning("CaseGenerator");
        DBMLWarning          = MakeSettingWarning("DemandBasedModLoading");
        HoldablesWarning     = MakeSettingWarning("Holdables");

        modConfig = new ModConfig <TweakSettings>("TweakSettings", OnReadError);
        UpdateSettings();
        StartCoroutine(Modes.LoadDefaultSettings());

        if (!Harmony.HasAnyPatches("qkrisi.harmonymod"))
        {
            HarmonyPatchInfo.HarmonyTexture = GetComponent <TweaksStorage>().HarmonyTexture;
            Patching.EnsurePatch("Harmony", typeof(ModInfoPatch), typeof(WorkshopPatch), typeof(ReloadPatch), typeof(ContinueButtonPatch),
                                 typeof(ManualButtonPatch), typeof(InstructionPatch), typeof(ModManagerPatch), typeof(ChangeButtonText));
        }

        DemandBasedLoading.EverLoadedModules = !settings.DemandBasedModLoading;
        DemandBasedSettingCache = settings.DemandBasedModLoading;

        HoldablesSettingCache = settings.Holdables;

        bool changeFadeTime = settings.FadeTime >= 0;

        FreeplayDevice.MAX_SECONDS_TO_SOLVE = float.MaxValue;
        FreeplayDevice.MIN_MODULE_COUNT     = 1;

        // Force the graphics quality to "Mod Quality", otherwise features might not render correctly.
        QualitySettings.SetQualityLevel(2);
        AbstractPlatformUtil.Instance.OriginalQualityLevel = 2;

        if (settings.EnableModsOnlyKey)
        {
            var lastFreeplaySettings = FreeplaySettings.CreateDefaultFreeplaySettings();
            lastFreeplaySettings.OnlyMods = true;
            ProgressionManager.Instance.RecordLastFreeplaySettings(lastFreeplaySettings);
        }

        UpdateSettingWarnings();
        AdvantageousWarning.SetActive(false);

        // Setup API/properties other mods to interact with
        GameObject infoObject = new GameObject("Tweaks_Info", typeof(TweaksProperties));

        infoObject.transform.parent = gameObject.transform;

        TweaksAPI.Setup();

        // Watch the TweakSettings file for Time Mode state being changed in the office.
        FileSystemWatcher watcher = new FileSystemWatcher(Path.Combine(Application.persistentDataPath, "Modsettings"), "TweakSettings.json")
        {
            NotifyFilter = NotifyFilters.LastWrite
        };

        watcher.Changed += (object source, FileSystemEventArgs e) =>
        {
            if (ModConfig <TweakSettings> .SerializeSettings(userSettings) == ModConfig <TweakSettings> .SerializeSettings(modConfig.Read()))
            {
                return;
            }

            UpdateSettings();
            UpdateSettingWarnings();

            MainThreadQueue.Enqueue(() => StartCoroutine(ModifyFreeplayDevice(false)));
        };

        // Setup the leaderboard controller to block the leaderboard submission requests.
        LeaderboardController.Install();

        GetTweaks();

        // Create a fake case with a bunch of anchors to trick the game when using CaseGenerator.
        TweaksCaseGeneratorCase = new GameObject("TweaksCaseGenerator");
        TweaksCaseGeneratorCase.transform.SetParent(transform);
        var kmBomb = TweaksCaseGeneratorCase.AddComponent <KMBomb>();

        kmBomb.IsHoldable      = false;
        kmBomb.WidgetAreas     = new List <GameObject>();
        kmBomb.visualTransform = transform;
        kmBomb.Faces           = new List <KMBombFace>();

        TweaksCaseGeneratorCase.AddComponent <ModBomb>();

        var kmBombFace = TweaksCaseGeneratorCase.AddComponent <KMBombFace>();

        kmBombFace.Anchors = new List <Transform>();
        kmBomb.Faces.Add(kmBombFace);

        for (int i = 0; i <= 9001; i++)
        {
            kmBombFace.Anchors.Add(transform);
        }

        // Handle scene changes
        UnityEngine.SceneManagement.SceneManager.sceneLoaded += (Scene scene, LoadSceneMode _) =>
        {
            UpdateSettings();
            UpdateSettingWarnings();

            Modes.settings = Modes.modConfig.Read();
            Modes.modConfig.Write(Modes.settings);

            if ((scene.name == "mainScene" || scene.name == "gameplayScene") && changeFadeTime)
            {
                SceneManager.Instance.RapidFadeInTime = settings.FadeTime;
            }

            switch (scene.name)
            {
            case "mainScene":
                if (changeFadeTime)
                {
                    SceneManager.Instance.SetupState.FadeInTime          =
                        SceneManager.Instance.SetupState.FadeOutTime     =
                            SceneManager.Instance.UnlockState.FadeInTime = settings.FadeTime;
                }

                break;

            case "gameplayLoadingScene":
                var gameplayLoadingManager = FindObjectOfType <GameplayLoadingManager>();
                if (settings.InstantSkip)
                {
                    gameplayLoadingManager.MinTotalLoadTime = 0;
                }
                if (changeFadeTime)
                {
                    gameplayLoadingManager.FadeInTime      =
                        gameplayLoadingManager.FadeOutTime = settings.FadeTime;
                }

                ReflectedTypes.UpdateTypes();

                ReflectedTypes.CurrencyAPIEndpointField?.SetValue(null, settings.FixFER ? "https://api.ratesapi.io/api" : "http://api.fixer.io");

                if (
                    AdvantageousFeaturesEnabled &&
                    GameplayState.MissionToLoad != Assets.Scripts.Missions.FreeplayMissionGenerator.FREEPLAY_MISSION_ID &&
                    GameplayState.MissionToLoad != ModMission.CUSTOM_MISSION_ID
                    )
                {
                    StartCoroutine(ShowAdvantageousWarning());
                }

                break;

            case "gameplayScene":
                if (changeFadeTime)
                {
                    SceneManager.Instance.GameplayState.FadeInTime      =
                        SceneManager.Instance.GameplayState.FadeOutTime = settings.FadeTime;
                }

                break;
            }
        };

        // Handle state changes
        GameInfo.OnStateChange += (KMGameInfo.State state) =>
        {
            OnStateChanged(CurrentState, state);

            // Transitioning away from another state
            if (state == KMGameInfo.State.Transitioning)
            {
                if (CurrentState == KMGameInfo.State.Setup)
                {
                    DemandBasedLoading.DisabledModsCount = 0;
                }

                if (CurrentState != KMGameInfo.State.Gameplay)
                {
                    DemandBasedLoading.HandleTransitioning();
                }
            }

            CurrentState = state;
            watcher.EnableRaisingEvents = state == KMGameInfo.State.Setup;

            if (state == KMGameInfo.State.Gameplay)
            {
                if (AdvantageousFeaturesEnabled)
                {
                    LeaderboardController.DisableLeaderboards();
                }

                TwitchPlaysActiveCache = TwitchPlaysActive;
                CurrentModeCache       = CurrentMode;

                BombStatus.Instance.widgetsActivated = false;
                BombStatus.Instance.HUD.SetActive(settings.BombHUD);
                BombStatus.Instance.ConfidencePrefab.gameObject.SetActive(CurrentMode != Mode.Zen);
                BombStatus.Instance.StrikesPrefab.color = CurrentMode == Mode.Time ? Color.yellow : Color.red;

                Modes.Multiplier = Modes.settings.TimeModeStartingMultiplier;
                BombStatus.Instance.UpdateMultiplier();
                bombWrappers.Clear();
                StartCoroutine(CheckForBombs());

                if (GameplayState.BombSeedToUse == -1)
                {
                    GameplayState.BombSeedToUse = settings.MissionSeed;
                }
            }
            else if (state == KMGameInfo.State.Setup)
            {
                ReloadPatch.ResetDict();
                if (ReflectedTypes.LoadedModsField.GetValue(ModManager.Instance) is Dictionary <string, Mod> loadedMods)
                {
                    Mod tweaksMod = loadedMods.Values.FirstOrDefault(mod => mod.ModID == "Tweaks");
                    if (tweaksMod != null && CaseGeneratorSettingCache != settings.CaseGenerator)
                    {
                        if (settings.CaseGenerator)
                        {
                            tweaksMod.ModObjects.Add(TweaksCaseGeneratorCase);
                        }
                        else
                        {
                            tweaksMod.ModObjects.Remove(TweaksCaseGeneratorCase);
                        }

                        CaseGeneratorSettingCache = settings.CaseGenerator;
                    }
                }

                StartCoroutine(Tips.ShowTip());
                StartCoroutine(ModifyFreeplayDevice(true));
                StartCoroutine(ModifyHoldables());
                GetComponentInChildren <ModSelectorExtension>().FindAPI();
                TweaksAPI.SetTPProperties(!TwitchPlaysActive);

                GameplayState.BombSeedToUse = -1;

                UpdateSettingWarnings();

                UpdateBombCreator();
            }
            else if (state == KMGameInfo.State.Transitioning)
            {
                // Because the settings are checked on a scene change and there is no scene change from exiting the gameplay room,
                // we need to update the settings here in case the user changed their HideTOC settings.
                UpdateSettings();

                bool modified       = false;
                var  ModMissionToCs = ModManager.Instance.ModMissionToCs;
                foreach (var metaData in ModMissionToCs)
                {
                    modified |= ModToCMetaData.Add(metaData);
                }

                var unloadedMods = (Dictionary <string, Mod>)ReflectedTypes.UnloadedModsField.GetValue(ModManager.Instance);
                if (unloadedMods != null)
                {
                    foreach (var unloadedMod in unloadedMods.Values)
                    {
                        var tocs = (List <ModTableOfContentsMetaData>)ReflectedTypes.TocsField.GetValue(unloadedMod);
                        if (tocs != null)
                        {
                            foreach (var metaData in tocs)
                            {
                                modified |= ModToCMetaData.Remove(metaData);
                            }
                        }
                    }
                }

                var newToCs = ModToCMetaData.Where(metaData => !settings.HideTOC.Any(pattern => Localization.GetLocalizedString(metaData.DisplayNameTerm).Like(pattern)));
                modified |= newToCs.Count() != ModMissionToCs.Count || !newToCs.All(ModMissionToCs.Contains);
                ModMissionToCs.Clear();
                ModMissionToCs.AddRange(newToCs);

                if (modified)
                {
                    SetupState.LastBombBinderTOCIndex = 0;
                    SetupState.LastBombBinderTOCPage  = 0;
                }
            }
        };
    }
Пример #20
0
        public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            if (Harmony.HasAnyPatches("rimworld.aRandomKiwi.RimTheme")) {
                Log.Warning("ModCheck: RimTheme is active, will not be able to apply all patches.");
                foreach(var instruction in instructions)
                {
                    yield return instruction;
                }
                yield break;
            }

            List<CodeInstruction> iList = new List<CodeInstruction>(instructions);

#if false
            // debug output
            // This will generate the assumedMethod filling code, complete with indexes for each line
            {
                string output = "";
                for (int i = 0; i < iList.Count; ++i)
                {
                    string code = iList[i].ToString();
                    code = code.Replace("\"", "\\\"");

                    output += "\n // ";
                    if (i < 10) output += " ";
                    output += i.ToString();
                    output += "\nassumedMethod.Add(\"" + code + "\");";
                }
                Log.Warning(output);
            }
#endif

            // first step is to verify that vanilla didn't change
            List<string> assumedMethod = new List<string>();

            // 0
            assumedMethod.Add("ldsfld System.Collections.Generic.List`1<Verse.ModContentPack> Verse.LoadedModManager::runningMods");
            // 1
            assumedMethod.Add("ldsfld System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> Verse.<>c::<>9__16_0");
            // 2
            assumedMethod.Add("dup NULL");
            // 3
            assumedMethod.Add("brtrue.s Label2");
            // 4
            assumedMethod.Add("pop NULL");
            // 5
            assumedMethod.Add("ldsfld Verse.<>c Verse.<>c::<>9");
            // 6
            assumedMethod.Add("ldftn System.Collections.Generic.IEnumerable`1<Verse.PatchOperation> Verse.<>c::<ApplyPatches>b__16_0(Verse.ModContentPack rm)");
            // 7
            assumedMethod.Add("newobj System.Void System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>>::.ctor(System.Object object, System.IntPtr method)");
            // 8
            assumedMethod.Add("dup NULL");
            // 9
            assumedMethod.Add("stsfld System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> Verse.<>c::<>9__16_0");
            // 10
            assumedMethod.Add("call static System.Collections.Generic.IEnumerable`1<Verse.PatchOperation> System.Linq.Enumerable::SelectMany(System.Collections.Generic.IEnumerable`1<Verse.ModContentPack> source, System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> selector) [Label2]");
            // 11
            assumedMethod.Add("callvirt abstract virtual System.Collections.Generic.IEnumerator`1<Verse.PatchOperation> System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>::GetEnumerator()");
            // 12
            assumedMethod.Add("stloc.0 NULL");
            // 13
            assumedMethod.Add("br.s Label3 [EX_BeginException]");
            // 14
            assumedMethod.Add("ldloc.0 NULL [Label6]");
            // 15
            assumedMethod.Add("callvirt abstract virtual Verse.PatchOperation System.Collections.Generic.IEnumerator`1<Verse.PatchOperation>::get_Current()");
            // 16
            assumedMethod.Add("stloc.1 NULL");
            // 17
            assumedMethod.Add("ldloc.1 NULL [EX_BeginException]");
            // 18
            assumedMethod.Add("ldarg.0 NULL");
            // 19
            assumedMethod.Add("callvirt System.Boolean Verse.PatchOperation::Apply(System.Xml.XmlDocument xml)");
            // 20
            assumedMethod.Add("pop NULL");
            // 21
            assumedMethod.Add("leave.s Label4");
            // 22
            assumedMethod.Add("stloc.2 NULL [EX_BeginCatch]");
            // 23
            assumedMethod.Add("ldstr \"Error in patch.Apply(): \"");
            // 24
            assumedMethod.Add("ldloc.2 NULL");
            // 25
            assumedMethod.Add("call static System.String System.String::Concat(System.Object arg0, System.Object arg1)");
            // 26
            assumedMethod.Add("ldc.i4.0 NULL");
            // 27
            assumedMethod.Add("call static System.Void Verse.Log::Error(System.String text, System.Boolean ignoreStopLoggingLimit)");
            // 28
            assumedMethod.Add("leave.s Label5 [EX_EndException]");
            // 29
            assumedMethod.Add("ldloc.0 NULL [Label3, Label4, Label5]");
            // 30
            assumedMethod.Add("callvirt abstract virtual System.Boolean System.Collections.IEnumerator::MoveNext()");
            // 31
            assumedMethod.Add("brtrue.s Label6");
            // 32
            assumedMethod.Add("leave.s Label7");
            // 33
            assumedMethod.Add("ldloc.0 NULL [EX_BeginFinally]");
            // 34
            assumedMethod.Add("brfalse.s Label8");
            // 35
            assumedMethod.Add("ldloc.0 NULL");
            // 36
            assumedMethod.Add("callvirt abstract virtual System.Void System.IDisposable::Dispose()");
            // 37
            assumedMethod.Add("endfinally NULL [Label8, EX_EndException]");
            // 38
            assumedMethod.Add("ret NULL [Label7]");

            ////  0
            //assumedMethod.Add("ldsfld System.Collections.Generic.List`1<Verse.ModContentPack> Verse.LoadedModManager::runningMods");
            ////  1
            //assumedMethod.Add("ldsfld System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> Verse.<>c::<>9__16_0");
            ////  2
            //assumedMethod.Add("dup NULL");
            ////  3
            //assumedMethod.Add("brtrue.s Label2");
            ////  4
            //assumedMethod.Add("pop NULL");
            ////  5
            //assumedMethod.Add("newobj Void .ctor(Object, IntPtr)");
            ////  6
            //assumedMethod.Add("stsfld System.Func`2[Verse.ModContentPack,System.Collections.Generic.IEnumerable`1[Verse.PatchOperation]] <>f__am$cache0");
            ////  7
            //assumedMethod.Add("ldsfld System.Func`2[Verse.ModContentPack,System.Collections.Generic.IEnumerable`1[Verse.PatchOperation]] <>f__am$cache0 [Label1]");
            ////  8
            //assumedMethod.Add("call IEnumerable`1 SelectMany[ModContentPack,PatchOperation](IEnumerable`1, System.Func`2[Verse.ModContentPack,System.Collections.Generic.IEnumerable`1[Verse.PatchOperation]])");
            ////  9
            //assumedMethod.Add("callvirt IEnumerator`1 GetEnumerator()");
            //// 10
            //assumedMethod.Add("stloc.1 NULL");
            //// 11
            //assumedMethod.Add("br Label2 [EX_BeginException]");
            //// 12
            //assumedMethod.Add("ldloc.1 NULL [Label5]");
            //// 13
            //assumedMethod.Add("callvirt Verse.PatchOperation get_Current()");
            //// 14
            //assumedMethod.Add("stloc.0 NULL");
            //// 15
            //assumedMethod.Add("ldloc.0 NULL [EX_BeginException]");
            //// 16
            //assumedMethod.Add("ldarg.0 NULL");
            //// 17
            //assumedMethod.Add("callvirt Boolean Apply(System.Xml.XmlDocument)");
            //// 18
            //assumedMethod.Add("pop NULL");
            //// 19
            //assumedMethod.Add("leave Label3");
            //// 20
            //assumedMethod.Add("stloc.2 NULL [EX_BeginCatch]");
            //// 21
            //assumedMethod.Add("ldstr \"Error in patch.Apply(): \"");
            //// 22
            //assumedMethod.Add("ldloc.2 NULL");
            //// 23
            //assumedMethod.Add("call System.String Concat(System.Object, System.Object)");
            //// 24
            //assumedMethod.Add("ldc.i4.0 NULL");
            //// 25
            //assumedMethod.Add("call Void Error(System.String, Boolean)");
            //// 26
            //assumedMethod.Add("leave Label4 [EX_EndException]");
            //// 27
            //assumedMethod.Add("ldloc.1 NULL [Label2, Label3, Label4]");
            //// 28
            //assumedMethod.Add("callvirt Boolean MoveNext()");
            //// 29
            //assumedMethod.Add("brtrue Label5");
            //// 30
            //assumedMethod.Add("leave Label6");
            //// 31
            //assumedMethod.Add("ldloc.1 NULL [EX_BeginFinally]");
            //// 32
            //assumedMethod.Add("brfalse Label7");
            //// 33
            //assumedMethod.Add("ldloc.1 NULL");
            //// 34
            //assumedMethod.Add("callvirt Void Dispose()");
            //// 35
            //assumedMethod.Add("endfinally NULL [Label7, EX_EndException]");
            //// 36
            //assumedMethod.Add("ret NULL [Label6]");


            // test if the loaded method is identical to the expected method
            bool bFoundCorrectMethod = iList.Count == assumedMethod.Count;
            if(!bFoundCorrectMethod && Prefs.DevMode)
            {
                Log.Message($"current method has {iList.Count} rows, assumed {assumedMethod.Count} rows");
            }

            // verify that each line is the same
            for (int i = 0; i < iList.Count && bFoundCorrectMethod; ++i)
            {
                if (iList[i].ToString() != assumedMethod[i])
                {
                    // line mismatch. Test that it isn't something silly like f__am$cache0 != f__am$cache2
                    bool match = false;
                    int offset = -1;

                    // lines with a known changing number
                    switch (i)
                    {
                        case 1:
                        case 6:
                        case 7:
                            offset = 119;
                            break;
                        case 4:
                            offset = 37;
                            break;
                    }

                    if (offset != -1)
                    {
                        try
                        {
                            // the mismatch could be a number in a generated var name
                            // replace the char in question with the new one
                            if (assumedMethod[i].Length > offset && assumedMethod[i].Length == iList[i].ToString().Length)
                            {
                                StringBuilder sb = new StringBuilder(assumedMethod[i]);
                                sb[offset] = iList[i].ToString().ToCharArray()[offset];

                                // make the comparison again, this time ignoring the char, which can modify itself
                                match = sb.ToString() == iList[i].ToString();
                            }
                        }
                        catch
                        {
                            // possibly unneeded try-catch. However it's here just to be safe. We really don't want to kill vanilla xml patching.
                        }
                    }

                    if (!match)
                    {
                        // still no match. The source is indeed modified.
                        bFoundCorrectMethod = false;
                        if (Prefs.DevMode) Log.Message($"source-row {iList[i].ToString()} differ from assumed row {assumedMethod[i]}");
                    }

                }
            }

            if (!bFoundCorrectMethod)
            {
                Log.Error("[ModCheck] Internal failure patching Verse.LoadedModManager.ApplyPatches");
                for (int i = 0; i < iList.Count; ++i)
                {
                    // return the unmodified vanilla code
                    //if (Prefs.DevMode) Log.Message(iList[i].ToString());
                    yield return iList[i];
                }
            }
            else
            {
                // keep the method intact, but add calls to Memory to tell when patching is started or stopped.
                for (int i = 0; i < iList.Count; ++i)
                {
                    // using indexes is perfectly safe because it works as long as vanilla is unchanged and that's already verified at this point
                    if (i == 17)
                    {
                        CodeInstruction instruction = new CodeInstruction(OpCodes.Call);
                        if (Memory.profilingEnabled)
                        {
                            // start the timer as well as incrementing the counter
                            instruction.operand = typeof(ModCheck.Memory).GetMethod(nameof(ModCheck.Memory.startPatchingWithTimer));
                        }
                        else
                        {
                            // no profiling, avoid the timer overhead
                            instruction.operand = typeof(ModCheck.Memory).GetMethod(nameof(ModCheck.Memory.startPatching));
                        }
                        yield return instruction;
                    }
                    else if (i == 21)
                    {
                        if (Memory.profilingEnabled)
                        {
                            CodeInstruction instruction = new CodeInstruction(OpCodes.Call);
                            instruction.operand = typeof(ModCheck.Memory).GetMethod(nameof(ModCheck.Memory.endPatchingWithTimer));
                            yield return instruction;
                        }
                    }
                    yield return iList[i];
                }
            }
        }