using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PowerPriority")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PowerPriority")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7dbe0ca4-66d4-43a8-ae90-762a7c6b0d22")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DSPPowerPriority;
[BepInPlugin("lee.dsp.powerpriority", "DSP Power Priority", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "lee.dsp.powerpriority";
public const string PluginName = "DSP Power Priority";
public const string PluginVersion = "1.0.0";
internal static ManualLogSource Log;
private ConfigEntry<int> nonFuelPriority;
private ConfigEntry<int> exchangerPriority;
private ConfigEntry<int> highGradeFuelPriority;
private ConfigEntry<int> lowGradeThermalPriority;
private ConfigEntry<string> highGradeThermalFuelIds;
private ConfigEntry<int> exchangerChargingMaxSourcePriority;
private void Awake()
{
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
nonFuelPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "NonFuelGeneration", 1, "Priority bucket for wind, solar, ray receivers and geothermal. Lower numbers are used first. Equal numbers share load proportionally.");
exchangerPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "EnergyExchangers", 2, "Priority bucket for energy exchangers while discharging.");
highGradeFuelPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "HighGradeFuelGeneration", 3, "Priority bucket for thermal plants burning configured high-grade fuels, mini fusion plants and artificial stars.");
lowGradeThermalPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "LowGradeThermalGeneration", 4, "Priority bucket for ordinary thermal generation such as coal, energetic graphite, oil and hydrogen.");
highGradeThermalFuelIds = ((BaseUnityPlugin)this).Config.Bind<string>("Classification", "HighGradeThermalFuelIds", "1801", "Comma-separated item IDs that make a thermal power plant use the HighGradeFuelGeneration priority. 1801 is the vanilla Hydrogen Fuel Rod.");
exchangerChargingMaxSourcePriority = ((BaseUnityPlugin)this).Config.Bind<int>("EnergyExchanger", "ChargingMaxSourcePriority", 0, "Highest-numbered priority bucket that may be brought online solely to charge energy exchangers. 0 = unrestricted vanilla charging behaviour. Example: 3 allows charging from priorities 1-3 but will not start priority 4 generation just to charge exchangers.");
RebuildConfigSnapshot();
nonFuelPriority.SettingChanged += OnConfigChanged;
exchangerPriority.SettingChanged += OnConfigChanged;
highGradeFuelPriority.SettingChanged += OnConfigChanged;
lowGradeThermalPriority.SettingChanged += OnConfigChanged;
highGradeThermalFuelIds.SettingChanged += OnConfigChanged;
exchangerChargingMaxSourcePriority.SettingChanged += OnConfigChanged;
new Harmony("lee.dsp.powerpriority").PatchAll();
((BaseUnityPlugin)this).Logger.LogInfo((object)"DSP Power Priority 1.0.0 loaded.");
}
private void OnConfigChanged(object sender, EventArgs e)
{
RebuildConfigSnapshot();
}
private void RebuildConfigSnapshot()
{
int num = NormalizePriority(nonFuelPriority.Value);
int num2 = NormalizePriority(exchangerPriority.Value);
int num3 = NormalizePriority(highGradeFuelPriority.Value);
int num4 = NormalizePriority(lowGradeThermalPriority.Value);
int[] array = new int[4] { num, num2, num3, num4 };
int[] array2 = new int[4];
int[] array3 = new int[4];
int num5 = 0;
for (int i = 0; i < 4; i++)
{
int num6 = array[i];
int num7 = -1;
for (int j = 0; j < num5; j++)
{
if (array2[j] == num6)
{
num7 = j;
break;
}
}
if (num7 < 0)
{
num7 = num5++;
array2[num7] = num6;
}
array3[num7] |= 1 << i;
}
for (int k = 1; k < num5; k++)
{
int num8 = array2[k];
int num9 = array3[k];
int num10 = k - 1;
while (num10 >= 0 && array2[num10] > num8)
{
array2[num10 + 1] = array2[num10];
array3[num10 + 1] = array3[num10];
num10--;
}
array2[num10 + 1] = num8;
array3[num10 + 1] = num9;
}
int invalidCount;
int[] array4 = ParseItemIds(highGradeThermalFuelIds.Value, out invalidCount);
int num11 = exchangerChargingMaxSourcePriority.Value;
if (num11 < 0)
{
num11 = 0;
}
PriorityDispatch.SetConfig(new DispatchConfig(array3, num5, array, array4, num11));
if (nonFuelPriority.Value < 1 || exchangerPriority.Value < 1 || highGradeFuelPriority.Value < 1 || lowGradeThermalPriority.Value < 1)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Priority values below 1 are treated as priority 1.");
}
if (invalidCount > 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)string.Format("Ignored {0} invalid HighGradeThermalFuelIds entr{1}.", invalidCount, (invalidCount == 1) ? "y" : "ies"));
}
if (exchangerChargingMaxSourcePriority.Value < 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"ChargingMaxSourcePriority values below 0 are treated as 0 (unrestricted vanilla charging).");
}
}
private static int NormalizePriority(int value)
{
if (value >= 1)
{
return value;
}
return 1;
}
private static int[] ParseItemIds(string text, out int invalidCount)
{
invalidCount = 0;
if (string.IsNullOrWhiteSpace(text))
{
return new int[0];
}
string[] array = text.Split(new char[1] { ',' });
List<int> list = new List<int>(array.Length);
for (int i = 0; i < array.Length; i++)
{
string text2 = array[i].Trim();
if (!int.TryParse(text2, out var result) || result <= 0)
{
if (text2.Length > 0)
{
invalidCount++;
}
}
else if (!list.Contains(result))
{
list.Add(result);
}
}
list.Sort();
return list.ToArray();
}
}
internal enum GeneratorCategory
{
NonFuel,
Exchanger,
HighGradeFuel,
LowGradeThermal
}
internal sealed class DispatchConfig
{
internal readonly int[] GroupMasks;
internal readonly int GroupCount;
internal readonly int[] CategoryPriorities;
internal readonly int[] HighGradeThermalFuelIds;
internal readonly int ChargingMaxSourcePriority;
internal DispatchConfig(int[] groupMasks, int groupCount, int[] categoryPriorities, int[] highGradeThermalFuelIds, int chargingMaxSourcePriority)
{
GroupMasks = groupMasks;
GroupCount = groupCount;
CategoryPriorities = categoryPriorities;
HighGradeThermalFuelIds = highGradeThermalFuelIds;
ChargingMaxSourcePriority = chargingMaxSourcePriority;
}
}
internal static class PriorityDispatch
{
private sealed class DispatchState
{
internal PowerSystem PowerSystem;
internal long PendingDemand;
internal int PlanNetworkId = -1;
internal bool PlanValid;
internal int ScanNetworkId = -1;
internal bool ScanValid;
internal long ChargingCapacity;
internal DispatchConfig ConfigSnapshot;
internal readonly long[] Capacities = new long[4];
internal readonly double[] Ratios = new double[4];
}
private static DispatchConfig config = new DispatchConfig(new int[4] { 1, 2, 4, 8 }, 4, new int[4] { 1, 2, 3, 4 }, new int[1] { 1801 }, 0);
[ThreadStatic]
private static DispatchState threadState;
internal static void SetConfig(DispatchConfig newConfig)
{
config = newConfig;
}
internal static void BeginTick(PowerSystem powerSystem)
{
DispatchState dispatchState = threadState;
if (dispatchState == null)
{
dispatchState = (threadState = new DispatchState());
}
dispatchState.PowerSystem = powerSystem;
dispatchState.PendingDemand = 0L;
dispatchState.PlanNetworkId = -1;
dispatchState.PlanValid = false;
dispatchState.ScanNetworkId = -1;
dispatchState.ScanValid = false;
dispatchState.ConfigSnapshot = null;
}
internal static void SetPendingDemand(long demand)
{
DispatchState dispatchState = threadState;
if (dispatchState != null)
{
dispatchState.PendingDemand = ((demand > 0) ? demand : 0);
dispatchState.PlanNetworkId = -1;
dispatchState.PlanValid = false;
}
}
internal static double GetChargingRatio(double vanillaRatio, PowerSystem powerSystem, ref PowerExchangerComponent exchanger, long vanillaSurplus)
{
if (powerSystem == null || exchanger.networkId <= 0 || vanillaSurplus <= 0)
{
return vanillaRatio;
}
DispatchConfig dispatchConfig = config;
int chargingMaxSourcePriority = dispatchConfig.ChargingMaxSourcePriority;
if (chargingMaxSourcePriority <= 0)
{
return vanillaRatio;
}
DispatchState dispatchState = EnsureNetworkScan(powerSystem, exchanger.networkId, dispatchConfig);
if (dispatchState == null || dispatchState.ChargingCapacity <= 0)
{
return vanillaRatio;
}
long num = 0L;
long num2 = 0L;
for (int i = 0; i < 4; i++)
{
long num3 = dispatchState.Capacities[i];
num += num3;
if (dispatchConfig.CategoryPriorities[i] <= chargingMaxSourcePriority)
{
num2 += num3;
}
}
long num4 = num - vanillaSurplus;
if (num4 < 0)
{
num4 = 0L;
}
long num5 = num2 - num4;
if (num5 <= 0)
{
return 0.0;
}
long num6 = ((num5 < vanillaSurplus) ? num5 : vanillaSurplus);
if (num6 >= dispatchState.ChargingCapacity)
{
return 1.0;
}
return (double)num6 / (double)dispatchState.ChargingCapacity;
}
internal static double GetExchangerRatio(double vanillaRatio, PowerSystem powerSystem, ref PowerExchangerComponent exchanger)
{
if (powerSystem == null || exchanger.networkId <= 0)
{
return vanillaRatio;
}
DispatchState dispatchState = EnsurePlan(powerSystem, exchanger.networkId);
if (dispatchState != null)
{
return dispatchState.Ratios[1];
}
return vanillaRatio;
}
internal static double GetGeneratorRatio(double vanillaRatio, PowerSystem powerSystem, ref PowerGeneratorComponent generator)
{
if (powerSystem == null || generator.networkId <= 0)
{
return vanillaRatio;
}
DispatchState dispatchState = EnsurePlan(powerSystem, generator.networkId);
if (dispatchState == null || dispatchState.ConfigSnapshot == null)
{
return vanillaRatio;
}
GeneratorCategory generatorCategory = ClassifyGenerator(ref generator, dispatchState.ConfigSnapshot);
return dispatchState.Ratios[(int)generatorCategory];
}
private static DispatchState EnsurePlan(PowerSystem powerSystem, int networkId)
{
DispatchState dispatchState = threadState;
if (dispatchState == null || dispatchState.PowerSystem != powerSystem)
{
return null;
}
if (dispatchState.PlanValid && dispatchState.PlanNetworkId == networkId)
{
return dispatchState;
}
if (networkId <= 0 || networkId >= powerSystem.netPool.Length)
{
return null;
}
PowerNetwork val = powerSystem.netPool[networkId];
if (val == null || val.id != networkId)
{
return null;
}
DispatchConfig dispatchConfig = config;
dispatchState = EnsureNetworkScan(powerSystem, networkId, dispatchConfig);
if (dispatchState == null)
{
return null;
}
Array.Clear(dispatchState.Ratios, 0, dispatchState.Ratios.Length);
long num = dispatchState.PendingDemand;
for (int i = 0; i < dispatchConfig.GroupCount; i++)
{
int num2 = dispatchConfig.GroupMasks[i];
long num3 = 0L;
for (int j = 0; j < 4; j++)
{
if ((num2 & (1 << j)) != 0)
{
num3 += dispatchState.Capacities[j];
}
}
double num4;
if (num3 <= 0 || num <= 0)
{
num4 = 0.0;
}
else if (num >= num3)
{
num4 = 1.0;
num -= num3;
}
else
{
num4 = (double)num / (double)num3;
num = 0L;
}
for (int k = 0; k < 4; k++)
{
if ((num2 & (1 << k)) != 0)
{
dispatchState.Ratios[k] = num4;
}
}
}
dispatchState.PlanNetworkId = networkId;
dispatchState.PlanValid = true;
return dispatchState;
}
private static DispatchState EnsureNetworkScan(PowerSystem powerSystem, int networkId, DispatchConfig snapshot)
{
DispatchState dispatchState = threadState;
if (dispatchState == null || dispatchState.PowerSystem != powerSystem)
{
return null;
}
if (dispatchState.ScanValid && dispatchState.ScanNetworkId == networkId && dispatchState.ConfigSnapshot == snapshot)
{
return dispatchState;
}
if (networkId <= 0 || networkId >= powerSystem.netPool.Length)
{
return null;
}
PowerNetwork val = powerSystem.netPool[networkId];
if (val == null || val.id != networkId)
{
return null;
}
Array.Clear(dispatchState.Capacities, 0, dispatchState.Capacities.Length);
dispatchState.ChargingCapacity = 0L;
dispatchState.ConfigSnapshot = snapshot;
List<int> generators = val.generators;
PowerGeneratorComponent[] genPool = powerSystem.genPool;
for (int i = 0; i < generators.Count; i++)
{
int num = generators[i];
if (num <= 0 || num >= genPool.Length)
{
continue;
}
ref PowerGeneratorComponent reference = ref genPool[num];
if (reference.id == num && reference.productId == 0)
{
long capacityCurrentTick = reference.capacityCurrentTick;
if (capacityCurrentTick > 0)
{
GeneratorCategory generatorCategory = ClassifyGenerator(ref reference, snapshot);
dispatchState.Capacities[(int)generatorCategory] += capacityCurrentTick;
}
}
}
List<int> exchangers = val.exchangers;
PowerExchangerComponent[] excPool = powerSystem.excPool;
for (int j = 0; j < exchangers.Count; j++)
{
int num2 = exchangers[j];
if (num2 <= 0 || num2 >= excPool.Length)
{
continue;
}
ref PowerExchangerComponent reference2 = ref excPool[num2];
if (reference2.id != num2)
{
continue;
}
long capsCurrentTick = reference2.capsCurrentTick;
if (capsCurrentTick > 0)
{
if (reference2.state <= -1f)
{
dispatchState.Capacities[1] += capsCurrentTick;
}
else if (reference2.state >= 1f)
{
dispatchState.ChargingCapacity += capsCurrentTick;
}
}
}
dispatchState.ScanNetworkId = networkId;
dispatchState.ScanValid = true;
return dispatchState;
}
private static GeneratorCategory ClassifyGenerator(ref PowerGeneratorComponent generator, DispatchConfig snapshot)
{
if (generator.wind || generator.photovoltaic || generator.gamma || generator.geothermal || generator.fuelMask <= 0)
{
return GeneratorCategory.NonFuel;
}
if (generator.fuelMask == 2 || generator.fuelMask == 4)
{
return GeneratorCategory.HighGradeFuel;
}
int num = 0;
if (generator.fuelEnergy > 0 && generator.curFuelId > 0)
{
num = generator.curFuelId;
}
else if (generator.fuelId > 0)
{
num = generator.fuelId;
}
else if (generator.curFuelId > 0)
{
num = generator.curFuelId;
}
if (num > 0 && IsHighGradeThermalFuel(num, snapshot.HighGradeThermalFuelIds))
{
return GeneratorCategory.HighGradeFuel;
}
return GeneratorCategory.LowGradeThermal;
}
private static bool IsHighGradeThermalFuel(int fuelId, int[] ids)
{
foreach (int num in ids)
{
if (num == fuelId)
{
return true;
}
if (num > fuelId)
{
break;
}
}
return false;
}
}
[HarmonyPatch(typeof(PowerSystem), "GameTick")]
internal static class PowerSystem_GameTick_Patch
{
private struct PatchSite
{
internal int RatioLoadIndex;
internal int RatioStoreIndex;
internal CodeInstruction ComponentLocalLoad;
internal CodeInstruction DemandLocalLoad;
}
private static readonly FieldInfo ExcPoolField = AccessTools.Field(typeof(PowerSystem), "excPool");
private static readonly FieldInfo ExchangerCapsField = AccessTools.Field(typeof(PowerExchangerComponent), "capsCurrentTick");
private static readonly FieldInfo GeneratorCapacityField = AccessTools.Field(typeof(PowerGeneratorComponent), "capacityCurrentTick");
private static readonly MethodInfo ExchangerInputUpdateMethod = AccessTools.Method(typeof(PowerExchangerComponent), "InputUpdate", (Type[])null, (Type[])null);
private static readonly MethodInfo ExchangerOutputUpdateMethod = AccessTools.Method(typeof(PowerExchangerComponent), "OutputUpdate", (Type[])null, (Type[])null);
private static readonly MethodInfo GeneratorFuelMethod = AccessTools.Method(typeof(PowerGeneratorComponent), "GenEnergyByFuel", (Type[])null, (Type[])null);
private static readonly MethodInfo SetPendingDemandMethod = AccessTools.Method(typeof(PriorityDispatch), "SetPendingDemand", (Type[])null, (Type[])null);
private static readonly MethodInfo GetChargingRatioMethod = AccessTools.Method(typeof(PriorityDispatch), "GetChargingRatio", (Type[])null, (Type[])null);
private static readonly MethodInfo GetExchangerRatioMethod = AccessTools.Method(typeof(PriorityDispatch), "GetExchangerRatio", (Type[])null, (Type[])null);
private static readonly MethodInfo GetGeneratorRatioMethod = AccessTools.Method(typeof(PriorityDispatch), "GetGeneratorRatio", (Type[])null, (Type[])null);
[HarmonyPrefix]
private static void Prefix(PowerSystem __instance)
{
PriorityDispatch.BeginTick(__instance);
}
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
MethodBody methodBody = original.GetMethodBody();
if (methodBody == null)
{
Plugin.Log.LogError((object)"Power priority patch disabled: PowerSystem.GameTick has no method body.");
return list;
}
if (!TryFindChargingSite(list, methodBody, out var site) || !TryFindExchangerSite(list, methodBody, out var site2) || !TryFindGeneratorSite(list, methodBody, out var site3))
{
Plugin.Log.LogError((object)"Power priority patch disabled: expected 0.10.34 PowerSystem.GameTick dispatch pattern was not found. Vanilla power dispatch will be used.");
return list;
}
ReplaceGeneratorRatio(list, site3);
ReplaceExchangerRatio(list, site2);
InsertDemandCapture(list, site2);
ReplaceChargingRatio(list, site);
Plugin.Log.LogInfo((object)"PowerSystem.GameTick priority patch applied: exchanger charging limit, demand capture, exchanger dispatch ratio and generator dispatch ratio patched.");
return list;
}
private static bool TryFindChargingSite(List<CodeInstruction> code, MethodBody body, out PatchSite site)
{
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Expected O, but got Unknown
//IL_0136: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Expected O, but got Unknown
site = default(PatchSite);
int num = FindUniqueCall(code, ExchangerInputUpdateMethod);
if (num < 0)
{
return false;
}
int num2 = FindPreviousFieldLoad(code, num, ExchangerCapsField);
if (num2 < 0)
{
return false;
}
int num3 = FindPreviousLocalLoadOfType(code, body, num2, typeof(double));
if (num3 < 0)
{
return false;
}
int num4 = FindPreviousOpcode(code, num2, OpCodes.Ldelema);
if (num4 < 1 || code[num4].operand as Type != typeof(PowerExchangerComponent))
{
return false;
}
int index = num4 - 1;
if (!IsLoadLocal(code[index]))
{
return false;
}
int num5 = -1;
for (int i = num2 + 1; i < num; i++)
{
if (IsLoadLocal(code[i]))
{
int localIndex = GetLocalIndex(code[i]);
if (localIndex >= 0 && localIndex < body.LocalVariables.Count && body.LocalVariables[localIndex].LocalType == typeof(long))
{
num5 = i;
break;
}
}
}
if (num5 < 0)
{
return false;
}
site = new PatchSite
{
RatioLoadIndex = num3,
ComponentLocalLoad = new CodeInstruction(code[index]),
DemandLocalLoad = new CodeInstruction(code[num5])
};
return true;
}
private static bool TryFindExchangerSite(List<CodeInstruction> code, MethodBody body, out PatchSite site)
{
//IL_0144: Unknown result type (might be due to invalid IL or missing references)
//IL_014e: Expected O, but got Unknown
//IL_0158: Unknown result type (might be due to invalid IL or missing references)
//IL_0162: Expected O, but got Unknown
site = default(PatchSite);
int num = FindUniqueCall(code, ExchangerOutputUpdateMethod);
if (num < 0)
{
return false;
}
int num2 = FindPreviousFieldLoad(code, num, ExchangerCapsField);
if (num2 < 0)
{
return false;
}
int num3 = FindPreviousLocalLoadOfType(code, body, num2, typeof(double));
if (num3 < 0)
{
return false;
}
int localIndex = GetLocalIndex(code[num3]);
if (localIndex < 0)
{
return false;
}
int num4 = FindPreviousStoreToLocal(code, num3, localIndex);
if (num4 < 0)
{
return false;
}
int num5 = FindPreviousOpcode(code, num2, OpCodes.Ldelema);
if (num5 < 1 || code[num5].operand as Type != typeof(PowerExchangerComponent))
{
return false;
}
int index = num5 - 1;
if (!IsLoadLocal(code[index]))
{
return false;
}
int num6 = -1;
for (int i = num2 + 1; i < num; i++)
{
if (IsLoadLocal(code[i]))
{
int localIndex2 = GetLocalIndex(code[i]);
if (localIndex2 >= 0 && body.LocalVariables[localIndex2].LocalType == typeof(long))
{
num6 = i;
break;
}
}
}
if (num6 < 0)
{
return false;
}
site = new PatchSite
{
RatioLoadIndex = num3,
RatioStoreIndex = num4,
ComponentLocalLoad = new CodeInstruction(code[index]),
DemandLocalLoad = new CodeInstruction(code[num6])
};
return true;
}
private static bool TryFindGeneratorSite(List<CodeInstruction> code, MethodBody body, out PatchSite site)
{
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Expected O, but got Unknown
site = default(PatchSite);
int num = FindUniqueCall(code, GeneratorFuelMethod);
if (num < 0)
{
return false;
}
int num2 = FindPreviousFieldLoad(code, num, GeneratorCapacityField);
if (num2 < 0)
{
return false;
}
int num3 = FindPreviousLocalLoadOfType(code, body, num2, typeof(double));
if (num3 < 0)
{
return false;
}
int num4 = num2 - 1;
if (num4 < 0 || !IsLoadLocal(code[num4]))
{
return false;
}
int localIndex = GetLocalIndex(code[num4]);
if (localIndex < 0)
{
return false;
}
Type localType = body.LocalVariables[localIndex].LocalType;
if (!localType.IsByRef || localType.GetElementType() != typeof(PowerGeneratorComponent))
{
return false;
}
site = new PatchSite
{
RatioLoadIndex = num3,
ComponentLocalLoad = new CodeInstruction(code[num4])
};
return true;
}
private static void InsertDemandCapture(List<CodeInstruction> code, PatchSite site)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Expected O, but got Unknown
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
CodeInstruction val = new CodeInstruction(site.DemandLocalLoad);
CodeInstruction val2 = new CodeInstruction(OpCodes.Call, (object)SetPendingDemandMethod);
code.InsertRange(site.RatioStoreIndex + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2] { val, val2 });
}
private static void ReplaceChargingRatio(List<CodeInstruction> code, PatchSite site)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected O, but got Unknown
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Expected O, but got Unknown
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Expected O, but got Unknown
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected O, but got Unknown
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Expected O, but got Unknown
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Expected O, but got Unknown
CodeInstruction val = code[site.RatioLoadIndex];
List<CodeInstruction> list = new List<CodeInstruction>
{
new CodeInstruction(val.opcode, val.operand),
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)ExcPoolField),
new CodeInstruction(site.ComponentLocalLoad),
new CodeInstruction(OpCodes.Ldelema, (object)typeof(PowerExchangerComponent)),
new CodeInstruction(site.DemandLocalLoad),
new CodeInstruction(OpCodes.Call, (object)GetChargingRatioMethod)
};
MoveLabelsAndBlocks(val, list[0]);
code.RemoveAt(site.RatioLoadIndex);
code.InsertRange(site.RatioLoadIndex, list);
}
private static void ReplaceExchangerRatio(List<CodeInstruction> code, PatchSite site)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected O, but got Unknown
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Expected O, but got Unknown
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Expected O, but got Unknown
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected O, but got Unknown
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
CodeInstruction val = code[site.RatioLoadIndex];
List<CodeInstruction> list = new List<CodeInstruction>
{
new CodeInstruction(val.opcode, val.operand),
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)ExcPoolField),
new CodeInstruction(site.ComponentLocalLoad),
new CodeInstruction(OpCodes.Ldelema, (object)typeof(PowerExchangerComponent)),
new CodeInstruction(OpCodes.Call, (object)GetExchangerRatioMethod)
};
MoveLabelsAndBlocks(val, list[0]);
code.RemoveAt(site.RatioLoadIndex);
code.InsertRange(site.RatioLoadIndex, list);
}
private static void ReplaceGeneratorRatio(List<CodeInstruction> code, PatchSite site)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected O, but got Unknown
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Expected O, but got Unknown
CodeInstruction val = code[site.RatioLoadIndex];
List<CodeInstruction> list = new List<CodeInstruction>
{
new CodeInstruction(val.opcode, val.operand),
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(site.ComponentLocalLoad),
new CodeInstruction(OpCodes.Call, (object)GetGeneratorRatioMethod)
};
MoveLabelsAndBlocks(val, list[0]);
code.RemoveAt(site.RatioLoadIndex);
code.InsertRange(site.RatioLoadIndex, list);
}
private static int FindUniqueCall(List<CodeInstruction> code, MethodInfo method)
{
int num = -1;
for (int i = 0; i < code.Count; i++)
{
if ((code[i].opcode == OpCodes.Call || code[i].opcode == OpCodes.Callvirt) && object.Equals(code[i].operand, method))
{
if (num >= 0)
{
return -1;
}
num = i;
}
}
return num;
}
private static int FindPreviousFieldLoad(List<CodeInstruction> code, int startExclusive, FieldInfo field)
{
for (int num = startExclusive - 1; num >= 0; num--)
{
if (code[num].opcode == OpCodes.Ldfld && object.Equals(code[num].operand, field))
{
return num;
}
}
return -1;
}
private static int FindPreviousLocalLoadOfType(List<CodeInstruction> code, MethodBody body, int startExclusive, Type type)
{
int num = startExclusive - 1;
while (num >= 0 && num >= startExclusive - 20)
{
if (IsLoadLocal(code[num]))
{
int localIndex = GetLocalIndex(code[num]);
if (localIndex >= 0 && localIndex < body.LocalVariables.Count && body.LocalVariables[localIndex].LocalType == type)
{
return num;
}
}
num--;
}
return -1;
}
private static int FindPreviousStoreToLocal(List<CodeInstruction> code, int startExclusive, int localIndex)
{
for (int num = startExclusive - 1; num >= 0; num--)
{
if (IsStoreLocal(code[num]) && GetLocalIndex(code[num]) == localIndex)
{
return num;
}
}
return -1;
}
private static int FindPreviousOpcode(List<CodeInstruction> code, int startExclusive, OpCode opcode)
{
int num = startExclusive - 1;
while (num >= 0 && num >= startExclusive - 10)
{
if (code[num].opcode == opcode)
{
return num;
}
num--;
}
return -1;
}
private static bool IsLoadLocal(CodeInstruction instruction)
{
OpCode opcode = instruction.opcode;
if (!(opcode == OpCodes.Ldloc) && !(opcode == OpCodes.Ldloc_S) && !(opcode == OpCodes.Ldloc_0) && !(opcode == OpCodes.Ldloc_1) && !(opcode == OpCodes.Ldloc_2))
{
return opcode == OpCodes.Ldloc_3;
}
return true;
}
private static bool IsStoreLocal(CodeInstruction instruction)
{
OpCode opcode = instruction.opcode;
if (!(opcode == OpCodes.Stloc) && !(opcode == OpCodes.Stloc_S) && !(opcode == OpCodes.Stloc_0) && !(opcode == OpCodes.Stloc_1) && !(opcode == OpCodes.Stloc_2))
{
return opcode == OpCodes.Stloc_3;
}
return true;
}
private static int GetLocalIndex(CodeInstruction instruction)
{
if (instruction.opcode == OpCodes.Ldloc_0 || instruction.opcode == OpCodes.Stloc_0)
{
return 0;
}
if (instruction.opcode == OpCodes.Ldloc_1 || instruction.opcode == OpCodes.Stloc_1)
{
return 1;
}
if (instruction.opcode == OpCodes.Ldloc_2 || instruction.opcode == OpCodes.Stloc_2)
{
return 2;
}
if (instruction.opcode == OpCodes.Ldloc_3 || instruction.opcode == OpCodes.Stloc_3)
{
return 3;
}
if (instruction.operand is LocalBuilder localBuilder)
{
return localBuilder.LocalIndex;
}
object operand = instruction.operand;
if (operand is byte)
{
return (byte)operand;
}
operand = instruction.operand;
if (operand is int)
{
return (int)operand;
}
return -1;
}
private static void MoveLabelsAndBlocks(CodeInstruction from, CodeInstruction to)
{
if (from.labels != null && from.labels.Count > 0)
{
to.labels.AddRange(from.labels);
from.labels.Clear();
}
if (from.blocks != null && from.blocks.Count > 0)
{
to.blocks.AddRange(from.blocks);
from.blocks.Clear();
}
}
}