/// <summary> /// Transpiles LoadDLLs to grab the exception information when a mod fails to load. /// </summary> private static IEnumerable <CodeInstruction> Transpiler( IEnumerable <CodeInstruction> method) { var instructions = new List <CodeInstruction>(method); // HarmonyInstance.Create and Assembly.LoadFrom will be wrapped var harmonyCreate = typeof(HarmonyInstance).GetMethodSafe(nameof( HarmonyInstance.Create), true, typeof(string)); var loadFrom = typeof(Assembly).GetMethodSafe(nameof(Assembly.LoadFrom), true, typeof(string)); bool patchException = PUtil.GameVersion >= 397125u, patchAssembly = false, patchCreate = false; // Add call to our handler in exception block, and wrap harmony instances to // have more information on each mod for (int i = instructions.Count - 1; i > 0; i--) { var instr = instructions[i]; if (instr.opcode == OpCodes.Pop && !patchException) { instr.opcode = OpCodes.Call; // Call our method instead instr.operand = typeof(ModLoadHandler).GetMethodSafe(nameof( ModLoadHandler.HandleModException), true, typeof(object)); patchException = true; } else if (instr.opcode == OpCodes.Call) { var target = instr.operand as MethodInfo; if (target == harmonyCreate && harmonyCreate != null) { // Reroute HarmonyInstance.Create instr.operand = typeof(ModLoadHandler).GetMethodSafe(nameof( ModLoadHandler.CreateHarmonyInstance), true, typeof(string)); patchCreate = true; } else if (target == loadFrom && loadFrom != null) { // Reroute Assembly.LoadFrom instr.operand = typeof(ModLoadHandler).GetMethodSafe(nameof( ModLoadHandler.LoadAssembly), true, typeof(string)); patchAssembly = true; } } } if (!patchException) { DebugLogger.LogError("Unable to transpile LoadDLLs: Could not find exception handler"); } if (!patchAssembly) { DebugLogger.LogWarning("Unable to transpile LoadDLLs: No calls to Assembly.LoadFrom found"); } if (!patchCreate) { DebugLogger.LogWarning("Unable to transpile LoadDLLs: No calls to HarmonyInstance.Create found"); } return(instructions); }
/// <summary> /// Applied before OnPrefabInit runs. /// </summary> internal static MethodBase TargetMethod() { MethodBase target = null; #if DEBUG DebugLogger.LogDebug("Transpiling LoadDLLs()"); #endif try { target = typeof(Mod).Assembly.GetType("KMod.DLLLoader", false)?. GetMethodSafe("LoadDLLs", true, PPatchTools.AnyArguments); if (target == null) { DebugLogger.LogError("Unable to transpile LoadDLLs: Method not found"); } } catch (IOException e) { // This should theoretically be impossible since the type is loaded DebugLogger.BaseLogException(e, null); } return(target); }
/// <summary> /// Logs exceptions where some types in an assembly fail to load, and adds the types /// that did load to the list anyways. /// </summary> /// <param name="exception">The exception thrown.</param> /// <param name="assembly">The assembly that failed to fully load.</param> /// <param name="types">The location to store types that did load.</param> private static void HandleTypeLoadExceptions(ReflectionTypeLoadException exception, Assembly assembly, ICollection <Type> types) { var failedTypes = exception?.Types; DebugLogger.LogError("Error when loading types from " + assembly.FullName); if (failedTypes != null) { int n = failedTypes.Length; for (int i = 0; i < n; i++) { var type = failedTypes[i]; var thrown = exception.LoaderExceptions[i]; if (type != null) { types.Add(type); } else if (thrown != null) { DebugLogger.LogException(thrown); } } } }
/// <summary> /// Applied before LogException runs. /// </summary> internal static bool Prefix(Exception e, string errorMessage) { DebugLogger.LogError(errorMessage); DebugLogger.LogException(e); return(false); }