コード例 #1
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);
            }
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        /// <summary>
        /// Goes through the forwarded components, and picks the latest version of each to
        /// instantiate.
        /// </summary>
        public void Instantiate()
        {
            foreach (var pair in forwardedComponents)
            {
                // Sort value by version
                var versions = pair.Value.Components;
                int n        = versions.Count;
                if (n > 0)
                {
                    string id = pair.Key;
                    versions.Sort();
                    var component = versions[n - 1];
                    latestComponents.GetOrAdd(id, component);
#if DEBUG
                    PRegistry.LogPatchDebug("Instantiating component {0} using version {1} from assembly {2}".F(
                                                id, component.Version, component.GetOwningAssembly().FullName));
#endif
                    try {
                        instantiatedComponents.GetOrAdd(id, component?.DoInitialize(
                                                            PLibInstance));
                    } catch (Exception e) {
                        PRegistry.LogPatchWarning("Error when instantiating component " + id +
                                                  ":");
                        PUtil.LogException(e);
                    }
                }
            }
            // Post initialize for component compatibility
            foreach (var pair in latestComponents)
            {
                try {
                    pair.Value.PostInitialize(PLibInstance);
                } catch (Exception e) {
                    PRegistry.LogPatchWarning("Error when instantiating component " +
                                              pair.Key + ":");
                    PUtil.LogException(e);
                }
            }
        }
コード例 #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));
            }
        }
コード例 #5
0
        /// <summary>
        /// Gets the shared data between components with this ID, serialized to the specified
        /// type. The shared data is retrieved, serialized with JSON, and reconstituted as type
        /// T in the memory space of the caller.
        ///
        /// The target type must exist and be a [JsonObject] in both this assembly and the
        /// target component's assembly.
        ///
        /// This method is somewhat slow and memory intensive, and should be used sparingly.
        /// </summary>
        /// <typeparam name="T">The data type to retrieve and into which to convert.</typeparam>
        /// <param name="defValue">The default value if the shared data is unset.</param>
        /// <returns>The data, or defValue if the shared data has not been set or cannot be serialized.</returns>
        public T GetSharedDataSerialized <T>(T defValue = default)
        {
            var remoteData = PRegistry.Instance.GetSharedData(ID);
            T   result     = defValue;

            using (var buffer = new MemoryStream(1024)) {
                try {
                    SerializationSettings.Serialize(new StreamWriter(buffer, Encoding.UTF8),
                                                    remoteData);
                    buffer.Position = 0L;
                    if (SerializationSettings.Deserialize(new StreamReader(buffer,
                                                                           Encoding.UTF8), typeof(T)) is T decoded)
                    {
                        result = decoded;
                    }
                } catch (JsonException e) {
                    PUtil.LogError("Unable to serialize shared data for component " + ID +
                                   ":");
                    PUtil.LogException(e);
                    result = defValue;
                }
            }
            return(result);
        }
コード例 #6
0
 /// <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);
     }
 }
コード例 #7
0
        /// <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
        }
コード例 #8
0
 public static void LogAllFailedAsserts()
 {
     PUtil.LogWarning("PLib in mod " + Assembly.GetCallingAssembly().GetName()?.Name +
                      " is logging ALL failed assertions!");
     PTranspilerTools.LogAllFailedAsserts();
 }
コード例 #9
0
 public static void LogAllExceptions()
 {
     PUtil.LogWarning("PLib in mod " + Assembly.GetCallingAssembly().GetName()?.Name +
                      " is logging ALL unhandled exceptions!");
     PTranspilerTools.LogAllExceptions();
 }