using HarmonyLib; using OWML.Common; using QSB.Utility; using System; using System.Collections.Generic; using System.Linq; namespace QSB.Patches; public static class QSBPatchManager { public static event Action OnPatchType; public static event Action OnUnpatchType; private static readonly List _patchList = new(); private static readonly List _patchedTypes = new(); private static PatchVendor _vendor = PatchVendor.None; public static Dictionary TypeToInstance = new(); public static void Init() { foreach (var type in typeof(QSBPatch).GetDerivedTypes()) { _patchList.Add((QSBPatch)Activator.CreateInstance(type)); } foreach (QSBPatchTypes type in Enum.GetValues(typeof(QSBPatchTypes))) { TypeToInstance.Add(type, new Harmony(type.ToString())); } var gameAssemblyTypes = typeof(AstroObject).Assembly.GetTypes(); var isEpic = gameAssemblyTypes.Any(x => x.Name == "EpicEntitlementRetriever"); var isSteam = gameAssemblyTypes.Any(x => x.Name == "SteamEntitlementRetriever"); var isUWP = gameAssemblyTypes.Any(x => x.Name == "MSStoreEntitlementRetriever"); if (isEpic && !isSteam && !isUWP) { _vendor = PatchVendor.Epic; } else if (!isEpic && isSteam && !isUWP) { _vendor = PatchVendor.Steam; } else if (!isEpic && !isSteam && isUWP) { _vendor = PatchVendor.Gamepass; } else { // ??? DebugLog.ToConsole($"FATAL - Could not determine game vendor.", MessageType.Fatal); } DebugLog.DebugWrite($"Determined game vendor as {_vendor}", MessageType.Info); DebugLog.DebugWrite("Patch Manager ready.", MessageType.Success); } public static void DoPatchType(QSBPatchTypes type) { if (_patchedTypes.Contains(type)) { DebugLog.ToConsole($"Warning - Tried to patch type {type}, when it has already been patched!", MessageType.Warning); return; } OnPatchType?.SafeInvoke(type); //DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); foreach (var patch in _patchList.Where(x => x.Type == type && x.PatchVendor.HasFlag(_vendor))) { //DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info); try { patch.DoPatches(TypeToInstance[type]); } catch (Exception ex) { DebugLog.ToConsole($"Error while patching {patch.GetType().Name} :\r\n{ex}", MessageType.Error); } } _patchedTypes.Add(type); } public static void DoUnpatchType(QSBPatchTypes type) { if (!_patchedTypes.Contains(type)) { DebugLog.ToConsole($"Warning - Tried to unpatch type {type}, when it is either unpatched or was never patched.", MessageType.Warning); return; } OnUnpatchType?.SafeInvoke(type); //DebugLog.DebugWrite($"Unpatch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); TypeToInstance[type].UnpatchSelf(); _patchedTypes.Remove(type); } }