/// <summary>
 /// Patches a method manually with a transpiler.
 /// </summary>
 /// <param name="instance">The Harmony instance.</param>
 /// <param name="type">The class to modify.</param>
 /// <param name="methodName">The method to patch.</param>
 /// <param name="transpiler">The transpiler to apply.</param>
 public static void PatchTranspile(this Harmony instance, Type type,
                                   string methodName, HarmonyMethod transpiler)
 {
     if (type == null)
     {
         throw new ArgumentNullException(nameof(type));
     }
     if (string.IsNullOrEmpty(methodName))
     {
         throw new ArgumentNullException(nameof(methodName));
     }
     // Fetch the method
     try {
         var method = type.GetMethod(methodName, PPatchTools.BASE_FLAGS |
                                     BindingFlags.Static | BindingFlags.Instance);
         if (method != null)
         {
             instance.Patch(method, null, null, transpiler);
         }
         else
         {
             PUtil.LogWarning("Unable to find method {0} on type {1}".F(methodName,
                                                                        type.FullName));
         }
     } catch (AmbiguousMatchException e) {
         PUtil.LogException(e);
     } catch (FormatException e) {
         PUtil.LogWarning("Unable to transpile method {0}: {1}".F(methodName,
                                                                  e.Message));
     }
 }
Exemple #2
0
        /// <summary>
        /// Localizes the PLib strings.
        /// </summary>
        /// <param name="locale">The locale to use.</param>
        internal static void LocalizeItself(Localization.Locale locale)
        {
            if (locale == null)
            {
                throw new ArgumentNullException(nameof(locale));
            }
            Localization.RegisterForTranslation(typeof(PLibStrings));
            var    assembly = Assembly.GetExecutingAssembly();
            string locCode  = locale.Code;

            if (string.IsNullOrEmpty(locCode))
            {
                locCode = Localization.GetCurrentLanguageCode();
            }
            try {
                using (var stream = assembly.GetManifestResourceStream(
                           TRANSLATIONS_RES_PATH + locCode + TRANSLATIONS_EXT)) {
                    if (stream != null)
                    {
                        // File.ReadAllLines does not work on streams unfortunately
                        var lines = new List <string>(128);
                        using (var reader = new StreamReader(stream, Encoding.UTF8)) {
                            string line;
                            while ((line = reader.ReadLine()) != null)
                            {
                                lines.Add(line);
                            }
                        }
                        Localization.OverloadStrings(Localization.ExtractTranslatedStrings(
                                                         lines.ToArray()));
#if DEBUG
                        PUtil.LogDebug("Localizing PLib Core to locale {0}".F(locCode));
#endif
                    }
                }
            } catch (Exception e) {
                PUtil.LogWarning("Failed to load {0} localization for PLib Core:".F(locCode));
                PUtil.LogExcWarn(e);
            }
        }
        /// <summary>
        /// Retrieves a type using its full name (including namespace). However, the assembly
        /// name is optional, as this method searches all assemblies in the current
        /// AppDomain if it is null or empty.
        /// </summary>
        /// <param name="name">The type name to retrieve.</param>
        /// <param name="assemblyName">If specified, the name of the assembly that contains
        /// the type. No other assembly name will be searched if this parameter is not null
        /// or empty. The assembly name might not match the DLL name, use a decompiler to
        /// make sure.</param>
        /// <returns>The type, or null if the type is not found or cannot be loaded.</returns>
        public static Type GetTypeSafe(string name, string assemblyName = null)
        {
            Type type = null;

            if (string.IsNullOrEmpty(assemblyName))
            {
                foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    try {
                        type = assembly.GetType(name, false);
                    } catch (System.IO.IOException) {
                        // The common parent of exceptions when the type requires another type
                        // that cannot be loaded
                    } catch (BadImageFormatException) { }
                    if (type != null)
                    {
                        break;
                    }
                }
            }
            else
            {
                try {
                    type = Type.GetType(name + ", " + assemblyName, false);
                } catch (TargetInvocationException e) {
                    PUtil.LogWarning("Unable to load type {0} from assembly {1}:".F(name,
                                                                                    assemblyName));
                    PUtil.LogExcWarn(e);
                } catch (ArgumentException e) {
                    // A generic type is loaded with bad arguments
                    PUtil.LogWarning("Unable to load type {0} from assembly {1}:".F(name,
                                                                                    assemblyName));
                    PUtil.LogExcWarn(e);
                } catch (System.IO.IOException) {
                    // The common parent of exceptions when the type requires another type that
                    // cannot be loaded
                } catch (BadImageFormatException) { }
            }
            return(type);
        }
Exemple #4
0
        /// <summary>
        /// Copies the sounds from one animation to another animation.
        /// </summary>
        /// <param name="dstAnim">The destination anim file name.</param>
        /// <param name="srcAnim">The source anim file name.</param>
        public static void CopySoundsToAnim(string dstAnim, string srcAnim)
        {
            if (string.IsNullOrEmpty(dstAnim))
            {
                throw new ArgumentNullException(nameof(dstAnim));
            }
            if (string.IsNullOrEmpty(srcAnim))
            {
                throw new ArgumentNullException(nameof(srcAnim));
            }
            var anim = Assets.GetAnim(dstAnim);

            if (anim != null)
            {
                var audioSheet = GameAudioSheets.Get();
                var animData   = anim.GetData();
                // For each anim in the kanim, look for existing sound events under the old
                // anim's file name
                for (int i = 0; i < animData.animCount; i++)
                {
                    string animName = animData.GetAnim(i)?.name ?? "";
                    var    events   = audioSheet.GetEvents(srcAnim + "." + animName);
                    if (events != null)
                    {
#if DEBUG
                        PUtil.LogDebug("Adding {0:D} audio event(s) to anim {1}.{2}".F(events.
                                                                                       Count, dstAnim, animName));
#endif
                        audioSheet.events[dstAnim + "." + animName] = events;
                    }
                }
            }
            else
            {
                PUtil.LogWarning("Destination animation \"{0}\" not found!".F(dstAnim));
            }
        }
 /// <summary>
 /// Patches a constructor manually.
 /// </summary>
 /// <param name="instance">The Harmony instance.</param>
 /// <param name="type">The class to modify.</param>
 /// <param name="arguments">The constructor's argument types.</param>
 /// <param name="prefix">The prefix to apply, or null if none.</param>
 /// <param name="postfix">The postfix to apply, or null if none.</param>
 public static void PatchConstructor(this Harmony instance, Type type,
                                     Type[] arguments, HarmonyMethod prefix = null, HarmonyMethod postfix = null)
 {
     if (type == null)
     {
         throw new ArgumentNullException(nameof(type));
     }
     // Fetch the constructor
     try {
         var cons = type.GetConstructor(PPatchTools.BASE_FLAGS | BindingFlags.Static |
                                        BindingFlags.Instance, null, arguments, null);
         if (cons != null)
         {
             instance.Patch(cons, prefix, postfix);
         }
         else
         {
             PUtil.LogWarning("Unable to find constructor on type {0}".F(type.
                                                                         FullName));
         }
     } catch (ArgumentException e) {
         PUtil.LogException(e);
     }
 }
        /// <summary>
        /// Transpiles a method to replace calls to the specified victim methods with
        /// replacement methods, altering the call type if necessary.
        ///
        /// Each key to value pair must meet the criteria defined in
        /// ReplaceMethodCall(TranspiledMethod, MethodInfo, MethodInfo).
        /// </summary>
        /// <param name="method">The method to patch.</param>
        /// <param name="translation">A mapping from the old method calls to replace, to the
        /// new method calls to use instead.</param>
        /// <returns>A transpiled version of that method that replaces or removes all calls
        /// to the specified methods.</returns>
        /// <exception cref="ArgumentException">If any of the new methods' argument types do
        /// not exactly match the old methods' argument types.</exception>
        public static TranspiledMethod ReplaceMethodCall(TranspiledMethod method,
                                                         IDictionary <MethodInfo, MethodInfo> translation)
        {
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }
            if (translation == null)
            {
                throw new ArgumentNullException(nameof(translation));
            }
            // Sanity check arguments
            int replaced = 0;

            foreach (var pair in translation)
            {
                var victim    = pair.Key;
                var newMethod = pair.Value;
                if (victim == null)
                {
                    throw new ArgumentNullException(nameof(victim));
                }
                if (newMethod != null)
                {
                    PTranspilerTools.CompareMethodParams(victim, victim.GetParameterTypes(),
                                                         newMethod);
                }
                else if (victim.ReturnType != typeof(void))
                {
                    throw new ArgumentException("Cannot remove method {0} with a return value".
                                                F(victim.Name));
                }
            }
            foreach (var instruction in method)
            {
                var        opcode = instruction.opcode;
                MethodInfo target;
                if ((opcode == OpCodes.Call || opcode == OpCodes.Calli || opcode == OpCodes.
                     Callvirt) && translation.TryGetValue(target = instruction.operand as
                                                                   MethodInfo, out MethodInfo newMethod))
                {
                    if (newMethod != null)
                    {
                        // Replace with new method
                        instruction.opcode = newMethod.IsStatic ? OpCodes.Call :
                                             OpCodes.Callvirt;
                        instruction.operand = newMethod;
                        yield return(instruction);
                    }
                    else
                    {
                        // Pop "this" if needed
                        int n = target.GetParameters().Length;
                        if (!target.IsStatic)
                        {
                            n++;
                        }
                        // Pop the arguments off the stack
                        instruction.opcode  = (n == 0) ? OpCodes.Nop : OpCodes.Pop;
                        instruction.operand = null;
                        yield return(instruction);

                        for (int i = 0; i < n - 1; i++)
                        {
                            yield return(new CodeInstruction(OpCodes.Pop));
                        }
                    }
                    replaced++;
                }
                else
                {
                    yield return(instruction);
                }
            }
#if DEBUG
            if (replaced == 0)
            {
                if (translation.Count == 1)
                {
                    // Diagnose the method that could not be replaced
                    var items = new KeyValuePair <MethodInfo, MethodInfo> [1];
                    translation.CopyTo(items, 0);
                    MethodInfo from = items[0].Key, to = items[0].Value;
                    PUtil.LogWarning("No method calls replaced: {0}.{1} to {2}.{3}".F(
                                         from.DeclaringType.FullName, from.Name, to.DeclaringType.FullName,
                                         to.Name));
                }
                else
                {
                    PUtil.LogWarning("No method calls replaced (multiple replacements)");
                }
            }
#endif
        }
 public static void LogAllFailedAsserts()
 {
     PUtil.LogWarning("PLib in mod " + Assembly.GetCallingAssembly().GetName()?.Name +
                      " is logging ALL failed assertions!");
     PTranspilerTools.LogAllFailedAsserts();
 }
 public static void LogAllExceptions()
 {
     PUtil.LogWarning("PLib in mod " + Assembly.GetCallingAssembly().GetName()?.Name +
                      " is logging ALL unhandled exceptions!");
     PTranspilerTools.LogAllExceptions();
 }