mirror of
https://github.com/NuclearPowered/Reactor.git
synced 2026-10-19 15:51:54 +00:00
Initial commit
This commit is contained in:
44 files changed
+3673
No files matched your search
@@ -0,0 +1,3 @@
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
@@ -0,0 +1,7 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
.idea
|
||||
work
|
||||
@@ -0,0 +1,76 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<!-- Among Us -->
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Hazel">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\Hazel.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
|
||||
<!-- BepInEx -->
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>$(AmongUs)\BepInEx\core\0Harmony.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="BepInEx">
|
||||
<HintPath>$(AmongUs)\BepInEx\core\BepInEx.Core.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="BepInEx.IL2CPP">
|
||||
<HintPath>$(AmongUs)\BepInEx\core\BepInEx.IL2CPP.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="SemVer">
|
||||
<HintPath>$(AmongUs)\BepInEx\core\SemVer.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
|
||||
<!-- IL2CPP Unhollower -->
|
||||
<Reference Include="UnhollowerBaseLib">
|
||||
<HintPath>$(AmongUs)\BepInEx\core\UnhollowerBaseLib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Il2Cppmscorlib">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\Il2Cppmscorlib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Il2CppSystem.Core">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\Il2CppSystem.Core.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
|
||||
<!-- UnityEngine -->
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\UnityEngine.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.InputLegacyModule">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\UnityEngine.InputLegacyModule.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.IMGUIModule">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\UnityEngine.IMGUIModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UI">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\UnityEngine.UI.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UIModule">
|
||||
<HintPath>$(AmongUs)\BepInEx\unhollowed\UnityEngine.UIModule.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Copy" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<OutputFiles Include="$(OutputPath)$(AssemblyName).dll" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(AmongUs)/BepInEx/plugins/" Condition="'$(Configuration)' == 'Debug'" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,18 @@
|
||||
# Reactor
|
||||
|
||||
Core (BepInEx) mod and API for Among Us.
|
||||
|
||||
Featuring amazing features like:
|
||||
|
||||
- modded handshake
|
||||
- *partially* unstripped IMGUI and UI
|
||||
- a lot of utility methods
|
||||
|
||||
## Reactor.OxygenFilter
|
||||
|
||||
Library for mapping output of Il2cppDumper, used in
|
||||
[NuclearPowered/Il2CppAssemblyUnhollower](https://github.com/NuclearPowered/Il2CppAssemblyUnhollower)
|
||||
|
||||
## Reactor.Greenhouse
|
||||
|
||||
Standalone program which generates mappings for OxygenFilter
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using BepInEx;
|
||||
using BepInEx.IL2CPP;
|
||||
using HarmonyLib;
|
||||
using Reactor.Extensions;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerRuntimeLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reactor.Debugger
|
||||
{
|
||||
[BepInPlugin(Id)]
|
||||
[BepInProcess("Among Us.exe")]
|
||||
[BepInDependency(ReactorPlugin.Id, "^0.2.0")]
|
||||
[ReactorPluginSide(PluginSide.Client)]
|
||||
public class DebuggerPlugin : BasePlugin
|
||||
{
|
||||
public const string Id = "gg.reactor.debugger";
|
||||
|
||||
public Harmony Harmony { get; } = new Harmony(Id);
|
||||
public DebuggerComponent Component { get; private set; }
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
ClassInjector.RegisterTypeInIl2Cpp<DebuggerComponent>();
|
||||
|
||||
var gameObject = new GameObject(nameof(DebuggerPlugin)).DontDestroy();
|
||||
Component = gameObject.AddComponent<DebuggerComponent>();
|
||||
|
||||
Harmony.PatchAll();
|
||||
|
||||
Extensions.Extensions.once_sceneLoaded((_, _) =>
|
||||
{
|
||||
Dumping.Dump();
|
||||
});
|
||||
}
|
||||
|
||||
public class DebuggerComponent : MonoBehaviour
|
||||
{
|
||||
public bool DisableGameEnd { get; set; }
|
||||
|
||||
public DragWindow TestWindow { get; }
|
||||
|
||||
public DebuggerComponent(IntPtr ptr) : base(ptr)
|
||||
{
|
||||
TestWindow = new DragWindow(new Rect(20, 20, 120, 120), "Debugger", () =>
|
||||
{
|
||||
GUILayout.Label("Name: " + SaveManager.PlayerName, new Il2CppReferenceArray<GUILayoutOption>(0));
|
||||
DisableGameEnd = GUILayout.Toggle(DisableGameEnd, "Disable game end", new Il2CppReferenceArray<GUILayoutOption>(0));
|
||||
|
||||
if (AmongUsClient.Instance.AmHost && ShipStatus.Instance && GUILayout.Button("Force game end", new Il2CppReferenceArray<GUILayoutOption>(0)))
|
||||
{
|
||||
ShipStatus.Instance.enabled = false;
|
||||
ShipStatus.RpcEndGame(GameOverReason.ImpostorDisconnect, false);
|
||||
}
|
||||
|
||||
if (PlayerControl.LocalPlayer)
|
||||
{
|
||||
var position = PlayerControl.LocalPlayer.transform.position;
|
||||
GUILayout.Label($"x: {position.x}", new Il2CppReferenceArray<GUILayoutOption>(0));
|
||||
GUILayout.Label($"y: {position.y}", new Il2CppReferenceArray<GUILayoutOption>(0));
|
||||
}
|
||||
})
|
||||
{
|
||||
Enabled = false
|
||||
};
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.F1))
|
||||
{
|
||||
TestWindow.Enabled = !TestWindow.Enabled;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
TestWindow.OnGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [HarmonyPatch]
|
||||
// public static class TracePatch
|
||||
// {
|
||||
// public static IEnumerable<MethodBase> TargetMethods()
|
||||
// {
|
||||
// return typeof(AmongUsClient).GetMethods(AccessTools.all).Where(x => x.ReturnType == typeof(Il2CppStructArray<byte>) && !x.GetParameters().Any());
|
||||
// }
|
||||
//
|
||||
// public static void Postfix(MethodBase __originalMethod)
|
||||
// {
|
||||
// PluginSingleton<DebuggerPlugin>.Instance.Log.LogInfo(__originalMethod.FullDescription());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BepInEx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reactor.Debugger
|
||||
{
|
||||
public static class Dumping
|
||||
{
|
||||
public static string DumpPath { get; } = Path.Combine(Paths.PluginPath, "dump");
|
||||
|
||||
public static void Dump()
|
||||
{
|
||||
Directory.CreateDirectory(DumpPath);
|
||||
|
||||
WritePalette();
|
||||
WriteEnum("Hat", HatManager.Instance.AllHats.ToArray().Select(hat => hat.name));
|
||||
WriteEnum("Pet", HatManager.Instance.AllPets.ToArray().Select(hat => hat.name));
|
||||
WriteEnum("Skin", HatManager.Instance.AllSkins.ToArray().Select(hat => hat.name));
|
||||
}
|
||||
|
||||
private static void WritePalette()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("public static class Palette");
|
||||
builder.AppendLine("{");
|
||||
|
||||
foreach (var propertyInfo in typeof(Palette).GetProperties())
|
||||
{
|
||||
if (propertyInfo.GetMethod == null || !propertyInfo.GetMethod.IsStatic)
|
||||
continue;
|
||||
|
||||
var value = propertyInfo.GetValue(null);
|
||||
|
||||
if (value is Color f)
|
||||
{
|
||||
value = (Color32) f;
|
||||
}
|
||||
|
||||
if (value is Color32 color)
|
||||
{
|
||||
builder.AppendLine($" public static readonly Color {propertyInfo.Name} = Color.FromArgb({(color.a != byte.MaxValue ? $"{color.a}, " : string.Empty)}{color.r}, {color.g}, {color.b});");
|
||||
}
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
|
||||
File.WriteAllText(Path.Combine(DumpPath, $"Palette.cs"), builder.ToString());
|
||||
}
|
||||
|
||||
private static void WriteEnum(string name, IEnumerable<string> values)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("public enum " + name);
|
||||
builder.AppendLine("{");
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
builder.AppendLine(" " + value.Replace(" ", "").Replace("'", "") + ",");
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
|
||||
File.WriteAllText(Path.Combine(DumpPath, $"{name}.cs"), builder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace Reactor.Debugger.Patches
|
||||
{
|
||||
public static class Test
|
||||
{
|
||||
static Test()
|
||||
{
|
||||
GameOptionsData.MaxImpostors = GameOptionsData.RecommendedImpostors = Enumerable.Repeat((int) byte.MaxValue, byte.MaxValue).ToArray();
|
||||
GameOptionsData.MinPlayers = Enumerable.Repeat(1, 4).ToArray();
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Update))]
|
||||
public static class UpdatePatch
|
||||
{
|
||||
public static void Prefix(GameStartManager __instance)
|
||||
{
|
||||
__instance.MinPlayers = 1;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Update))]
|
||||
public static class CountdownPatch
|
||||
{
|
||||
public static void Prefix(GameStartManager __instance)
|
||||
{
|
||||
__instance.countDownTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(StatsManager), nameof(StatsManager.AmBanned), MethodType.Getter)]
|
||||
public static class AmBannedPatch
|
||||
{
|
||||
public static bool Prefix(ref bool __result)
|
||||
{
|
||||
return __result = false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CheckEndCriteria))]
|
||||
[HarmonyPriority(Priority.First)]
|
||||
public static class CheckEndCriteriaPatch
|
||||
{
|
||||
public static bool Prefix()
|
||||
{
|
||||
return !PluginSingleton<DebuggerPlugin>.Instance.Component.DisableGameEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
|
||||
<Version>0.1.0</Version>
|
||||
<Description>Debugging mod for Reactor</Description>
|
||||
<Authors>NuclearPowered</Authors>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Reactor\Reactor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../AmongUs.props" />
|
||||
</Project>
|
||||
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Mono.Cecil;
|
||||
using Reactor.OxygenFilter;
|
||||
|
||||
namespace Reactor.Greenhouse
|
||||
{
|
||||
public static class Compiler
|
||||
{
|
||||
public static void Apply(this Mappings current, Mappings mappings)
|
||||
{
|
||||
static void ApplyType(List<MappedType> types, MappedType newType)
|
||||
{
|
||||
var type = types.SingleOrDefault(x => newType.Equals(x));
|
||||
if (type == null)
|
||||
{
|
||||
types.Add(newType);
|
||||
}
|
||||
else
|
||||
{
|
||||
type.Mapped = newType.Mapped;
|
||||
|
||||
void ApplyMembers(List<MappedMember> members, List<MappedMember> newMembers)
|
||||
{
|
||||
foreach (var newMember in newMembers)
|
||||
{
|
||||
var member = members.SingleOrDefault(x => newMember.Equals(x, false));
|
||||
|
||||
if (member == null)
|
||||
{
|
||||
members.Add(newMember);
|
||||
}
|
||||
else
|
||||
{
|
||||
member.Mapped = newMember.Mapped;
|
||||
member.Original = newMember.Original;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyMembers(type.Fields, newType.Fields);
|
||||
ApplyMembers(type.Properties, newType.Properties);
|
||||
|
||||
foreach (var newMember in newType.Methods)
|
||||
{
|
||||
var member = type.Methods.SingleOrDefault(x => newMember.Equals(x, false));
|
||||
|
||||
if (member == null)
|
||||
{
|
||||
type.Methods.Add(newMember);
|
||||
}
|
||||
else
|
||||
{
|
||||
member.Mapped = newMember.Mapped;
|
||||
member.Original = newMember.Original;
|
||||
member.Parameters = newMember.Parameters;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var nestedType in newType.Nested)
|
||||
{
|
||||
ApplyType(type.Nested, nestedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var newType in mappings.Types)
|
||||
{
|
||||
ApplyType(current.Types, newType);
|
||||
}
|
||||
}
|
||||
|
||||
private static Regex Regex { get; } = new Regex(@"{{(?<expression>[\w\.]+)}}", RegexOptions.Compiled);
|
||||
|
||||
public static string MapSignature(this OriginalDescriptor original, Mappings mappings)
|
||||
{
|
||||
var signature = original.Signature;
|
||||
|
||||
var matches = Regex.Matches(signature);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
var group = match.Groups["expression"];
|
||||
if (group.Success)
|
||||
{
|
||||
signature = signature.Replace(match.Value, mappings.FindByMapped(group.Value).Original.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
private static bool TestField(MappedMember field, TypeDefinition typeDef, FieldDefinition fieldDef, Mappings mappings)
|
||||
{
|
||||
return (field.Original.Name == null || field.Original.Name == fieldDef.Name) &&
|
||||
(field.Original.Index == null || field.Original.Index == typeDef.Fields.IndexOf(fieldDef)) &&
|
||||
(field.Original.Signature == null || field.Original.MapSignature(mappings) == fieldDef.GetSignature()) &&
|
||||
(field.Original.Const == null || !typeDef.IsEnum && field.Original.Const.Value.Equals(fieldDef.Constant));
|
||||
}
|
||||
|
||||
private static bool TestMethod(MappedMember method, TypeDefinition typeDef, MethodDefinition methodDef, Mappings mappings)
|
||||
{
|
||||
return (method.Original.Index == null || method.Original.Index == typeDef.Methods.IndexOf(methodDef)) &&
|
||||
(method.Original.Signature == null || method.Original.MapSignature(mappings) == methodDef.GetSignature()) &&
|
||||
(method.Original.Name == null || method.Original.Name == methodDef.Name);
|
||||
}
|
||||
|
||||
private static bool TestProperty(MappedMember property, TypeDefinition typeDef, PropertyDefinition propertyDef, Mappings mappings)
|
||||
{
|
||||
return (property.Original.Name == null || property.Original.Name == propertyDef.Name) &&
|
||||
(property.Original.Index == null || property.Original.Index == typeDef.Properties.IndexOf(propertyDef)) &&
|
||||
(property.Original.Signature == null || property.Original.MapSignature(mappings) == propertyDef.GetSignature());
|
||||
}
|
||||
|
||||
private static bool TestType(MappedType type, TypeDefinition typeDef, Mappings mappings)
|
||||
{
|
||||
return (type.Original.Name == null || type.Original.Name == typeDef.Name) &&
|
||||
type.Methods.All(method => typeDef.Methods.Any(m => TestMethod(method, typeDef, m, mappings))) &&
|
||||
type.Fields.All(field => typeDef.Fields.Any(f => TestField(field, typeDef, f, mappings))) &&
|
||||
type.Properties.All(property => typeDef.Properties.Any(p => TestProperty(property, typeDef, p, mappings)));
|
||||
}
|
||||
|
||||
private static void Compile(this MappedType type, TypeDefinition typeDef, Mappings mappings)
|
||||
{
|
||||
type.Original = new OriginalDescriptor { Name = typeDef.Name };
|
||||
|
||||
foreach (var nested in type.Nested)
|
||||
{
|
||||
var nestedDef = typeDef.NestedTypes.Single(t =>
|
||||
TestType(type, t, mappings) &&
|
||||
nested.Original.Index == null || typeDef.NestedTypes.IndexOf(t) == nested.Original.Index
|
||||
);
|
||||
|
||||
nested.Compile(nestedDef, mappings);
|
||||
}
|
||||
|
||||
foreach (var property in type.Properties)
|
||||
{
|
||||
try
|
||||
{
|
||||
var propertyDef = typeDef.Properties.Single(p => TestProperty(property, typeDef, p, mappings));
|
||||
|
||||
property.Original = new OriginalDescriptor { Name = propertyDef.Name };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Compilation of {property} failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var field in type.Fields)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fieldDef = typeDef.Fields.Single(f => TestField(field, typeDef, f, mappings));
|
||||
|
||||
field.Original = new OriginalDescriptor { Name = fieldDef.Name };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Compilation of {field} failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
try
|
||||
{
|
||||
var methodDef = typeDef.Methods.Single(m => TestMethod(method, typeDef, m, mappings));
|
||||
|
||||
method.Original = new OriginalDescriptor { Name = methodDef.Name, Signature = methodDef.GetSignature() };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Compilation of {method} failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Compile(this Mappings mappings, ModuleDefinition moduleDef)
|
||||
{
|
||||
foreach (var type in mappings.Types)
|
||||
{
|
||||
try
|
||||
{
|
||||
var typeDef = moduleDef.Types.Single(t => TestType(type, t, mappings));
|
||||
|
||||
type.Compile(typeDef, mappings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Compilation of {type} failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Reactor.Greenhouse
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider cap, string attribute)
|
||||
{
|
||||
if (!cap.HasCustomAttributes)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return cap.CustomAttributes.FirstOrDefault(attrib => attrib.AttributeType.FullName == attribute);
|
||||
}
|
||||
|
||||
public static uint? GetOffset(this MethodDefinition methodDef)
|
||||
{
|
||||
var attribute = methodDef.GetCustomAttribute("Il2CppDummyDll.AddressAttribute");
|
||||
if (attribute == null)
|
||||
return null;
|
||||
|
||||
var offset = attribute.Fields.Single(x => x.Name == "Offset");
|
||||
return new System.ComponentModel.UInt32Converter().ConvertFrom(offset) as uint?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Reactor.OxygenFilter;
|
||||
|
||||
namespace Reactor.Greenhouse
|
||||
{
|
||||
public static class Generator
|
||||
{
|
||||
public static Mappings Generate(ModuleDefinition old, ModuleDefinition latest)
|
||||
{
|
||||
var result = new Mappings();
|
||||
|
||||
foreach (var oldType in old.Types)
|
||||
{
|
||||
if (oldType.Name.StartsWith("<") || oldType.Namespace.StartsWith("GoogleMobileAds"))
|
||||
continue;
|
||||
|
||||
void AddType(TypeDefinition type)
|
||||
{
|
||||
var mapped = new MappedType(new OriginalDescriptor { Name = type.Name }, oldType.Name);
|
||||
|
||||
var i = 0;
|
||||
foreach (var field in type.Fields)
|
||||
{
|
||||
var j = 0;
|
||||
var i1 = i;
|
||||
var oldFields = oldType.Fields.Where(x => j++ == i1 && x.GetSignature().ToString() == field.GetSignature().ToString()).ToArray();
|
||||
if (oldFields.Length == 1)
|
||||
{
|
||||
var oldField = oldFields.First();
|
||||
if (oldField.Name == field.Name || !field.Name.IsObfuscated())
|
||||
continue;
|
||||
|
||||
mapped.Fields.Add(new MappedMember(new OriginalDescriptor { Index = i1 }, oldField.Name));
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
if (!method.HasParameters || method.IsSetter || method.IsGetter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var oldMethods = oldType.Methods.Where(x => x.Name == method.Name && x.Parameters.Count == method.Parameters.Count).ToArray();
|
||||
if (oldMethods.Length != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var oldMethod = oldMethods.Single();
|
||||
|
||||
var oldParameters = oldMethod.Parameters.Select(x => x.Name).ToList();
|
||||
|
||||
if (method.Parameters.Select(x => x.Name).SequenceEqual(oldParameters))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mapped.Methods.Add(new MappedMethod(new OriginalDescriptor { Name = method.Name, Signature = method.GetSignature() }, null)
|
||||
{
|
||||
Parameters = oldParameters
|
||||
});
|
||||
}
|
||||
|
||||
if (type.Name == oldType.Name || (!type.Name.IsObfuscated() && !mapped.Fields.Any() && !mapped.Methods.Any()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
result.Types.Add(mapped);
|
||||
}
|
||||
|
||||
var exact = latest.Types.FirstOrDefault(x => x.FullName == oldType.FullName);
|
||||
if (exact != null)
|
||||
{
|
||||
AddType(exact);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldType.IsEnum)
|
||||
{
|
||||
var first = oldType.Fields.Select(x => x.Name).ToArray();
|
||||
var type = latest.Types.SingleOrDefault(x => x.IsEnum && x.Fields.Select(f => f.Name).SequenceEqual(first));
|
||||
if (type != null)
|
||||
{
|
||||
AddType(type);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
static bool Test(TypeReference typeReference)
|
||||
{
|
||||
return typeReference.IsGenericParameter || typeReference.Namespace != string.Empty || !typeReference.Name.IsObfuscated();
|
||||
}
|
||||
|
||||
var methods = oldType.Methods.Where(x => Test(x.ReturnType) && x.Parameters.All(p => Test(p.ParameterType))).Select(x => x.GetSignature()).ToArray();
|
||||
var fields = oldType.Fields.Where(x => Test(x.FieldType) && (!x.FieldType.HasGenericParameters || x.FieldType.GenericParameters.All(Test))).Select(x => x.GetSignature()).ToArray();
|
||||
var properties = oldType.Properties.Select(x => x.Name).ToArray();
|
||||
|
||||
var types = latest.Types
|
||||
.Where(t => t.Attributes == oldType.Attributes)
|
||||
.ToArray();
|
||||
|
||||
TypeDefinition winner = null;
|
||||
var winnerPoints = -1;
|
||||
|
||||
foreach (var t in types)
|
||||
{
|
||||
var points = 0;
|
||||
points += t.Properties.Count(p => properties.Contains((p.GetMethod?.Name ?? p.SetMethod.Name).Substring(4)));
|
||||
points += fields.Count(s => t.Fields.Any(f => f.GetSignature().ToString() == s.ToString()));
|
||||
points += methods.Count(s => t.Methods.Any(m => m.GetSignature().ToString() == s.ToString()));
|
||||
|
||||
if (points > winnerPoints)
|
||||
{
|
||||
winnerPoints = points;
|
||||
winner = t;
|
||||
}
|
||||
else if (points == winnerPoints)
|
||||
{
|
||||
winner = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (winner != null && winnerPoints > 0)
|
||||
{
|
||||
AddType(winner);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Reactor.Greenhouse.Setup;
|
||||
using Reactor.OxygenFilter;
|
||||
|
||||
namespace Reactor.Greenhouse
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static Task<int> Main(string[] args)
|
||||
{
|
||||
var rootCommand = new RootCommand();
|
||||
|
||||
rootCommand.Handler = CommandHandler.Create(SearchAsync);
|
||||
|
||||
return rootCommand.InvokeAsync(args);
|
||||
}
|
||||
|
||||
private static async Task SearchAsync()
|
||||
{
|
||||
var gameManager = new GameManager();
|
||||
await gameManager.SetupAsync();
|
||||
|
||||
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
DefaultValueHandling = DefaultValueHandling.Ignore,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
ContractResolver = ShouldSerializeContractResolver.Instance,
|
||||
};
|
||||
|
||||
Console.WriteLine($"Generating mappings from {gameManager.PreObfuscation.Name} ({gameManager.PreObfuscation.Version})");
|
||||
using var old = ModuleDefinition.ReadModule(File.OpenRead(gameManager.PreObfuscation.Dll));
|
||||
|
||||
await GenerateAsync(gameManager.Steam, old);
|
||||
await GenerateAsync(gameManager.Itch, old);
|
||||
}
|
||||
|
||||
private static async Task GenerateAsync(Game game, ModuleDefinition old)
|
||||
{
|
||||
Console.WriteLine($"Compiling mappings for {game.Name} ({game.Version})");
|
||||
|
||||
using var moduleDef = ModuleDefinition.ReadModule(File.OpenRead(game.Dll));
|
||||
var version = game.Version;
|
||||
var postfix = game.Postfix;
|
||||
|
||||
var generated = Generator.Generate(old, moduleDef);
|
||||
|
||||
await File.WriteAllTextAsync(Path.Combine("work", version + postfix + ".generated.json"), JsonConvert.SerializeObject(generated, Formatting.Indented));
|
||||
|
||||
Apply(generated, Path.Combine("mappings", version + ".json"));
|
||||
Apply(generated, Path.Combine("mappings", version + postfix + ".json"));
|
||||
|
||||
generated.Compile(moduleDef);
|
||||
|
||||
await File.WriteAllTextAsync(Path.Combine("work", version + postfix + ".json"), JsonConvert.SerializeObject(generated));
|
||||
}
|
||||
|
||||
private static void Apply(Mappings generated, string file)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
var mappings = JsonConvert.DeserializeObject<Mappings>(File.ReadAllText(file));
|
||||
generated.Apply(mappings);
|
||||
}
|
||||
}
|
||||
|
||||
public class ShouldSerializeContractResolver : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
public static ShouldSerializeContractResolver Instance { get; } = new ShouldSerializeContractResolver();
|
||||
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (property.PropertyType != null && property.PropertyType != typeof(string))
|
||||
{
|
||||
if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null)
|
||||
{
|
||||
property.ShouldSerialize = instance => (instance?.GetType().GetProperty(property.UnderlyingName!)!.GetValue(instance) as IEnumerable<object>)?.Count() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="0.14.0" />
|
||||
<PackageReference Include="DepotDownloader.Library" Version="0.1.2" />
|
||||
<PackageReference Include="Iced" Version="1.6.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20478.1" />
|
||||
<ProjectReference Include="..\Reactor.OxygenFilter\Reactor.OxygenFilter.csproj" />
|
||||
<Reference Include="Il2CppDumper">
|
||||
<HintPath>lib\Il2CppDumper.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Il2CppDumper;
|
||||
using Reactor.Greenhouse.Setup.Provider;
|
||||
using IOPath = System.IO.Path;
|
||||
|
||||
namespace Reactor.Greenhouse.Setup
|
||||
{
|
||||
public class Game
|
||||
{
|
||||
public BaseProvider Provider { get; }
|
||||
public string Name { get; }
|
||||
public char Postfix { get; }
|
||||
public string Path { get; }
|
||||
public string Dll { get; }
|
||||
public string Version { get; private set; }
|
||||
|
||||
public Game(BaseProvider provider, string name, string path)
|
||||
{
|
||||
Provider = provider;
|
||||
provider.Game = this;
|
||||
Name = name;
|
||||
Postfix = char.ToLowerInvariant(name[0]);
|
||||
Path = path;
|
||||
Dll = IOPath.Combine(Path, "DummyDll", "Assembly-CSharp.dll");
|
||||
}
|
||||
|
||||
public async Task DownloadAsync()
|
||||
{
|
||||
Provider.Setup();
|
||||
await Provider.DownloadAsync();
|
||||
UpdateVersion();
|
||||
}
|
||||
|
||||
public void UpdateVersion()
|
||||
{
|
||||
if (Version != null)
|
||||
return;
|
||||
|
||||
Version = GameVersionParser.Parse(System.IO.Path.Combine(Path, "Among Us_Data", "globalgamemanagers"));
|
||||
}
|
||||
|
||||
public void Dump()
|
||||
{
|
||||
Console.WriteLine($"Dumping {Name}");
|
||||
|
||||
var hash = ComputeHash(IOPath.Combine(Path, "GameAssembly.dll"));
|
||||
var hashFile = IOPath.Combine(Path, "Assembly-CSharp.dll.md5");
|
||||
|
||||
if (File.Exists(hashFile) && File.ReadAllText(hashFile) == hash)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Il2CppDumper.Il2CppDumper.PerformDump(
|
||||
IOPath.Combine(Path, "GameAssembly.dll"),
|
||||
IOPath.Combine(Path, "Among Us_Data", "il2cpp_data", "Metadata", "global-metadata.dat"),
|
||||
Path,
|
||||
new Config
|
||||
{
|
||||
RequireAnyKey = false,
|
||||
GenerateScript = false,
|
||||
DumpProperty = true,
|
||||
DumpAttribute = true
|
||||
},
|
||||
Console.WriteLine
|
||||
))
|
||||
{
|
||||
throw new Exception("Il2CppDumper failed");
|
||||
}
|
||||
|
||||
File.WriteAllText(hashFile, hash);
|
||||
}
|
||||
|
||||
private static string ComputeHash(string file)
|
||||
{
|
||||
using var md5 = MD5.Create();
|
||||
using var assemblyStream = File.OpenRead(file);
|
||||
|
||||
var hash = md5.ComputeHash(assemblyStream);
|
||||
|
||||
return Encoding.UTF8.GetString(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using DepotDownloader;
|
||||
using Reactor.Greenhouse.Setup.Provider;
|
||||
|
||||
namespace Reactor.Greenhouse.Setup
|
||||
{
|
||||
public class GameManager
|
||||
{
|
||||
public string WorkPath { get; }
|
||||
|
||||
public Game PreObfuscation { get; }
|
||||
public Game Steam { get; }
|
||||
public Game Itch { get; }
|
||||
|
||||
public GameManager()
|
||||
{
|
||||
WorkPath = Path.GetFullPath("work");
|
||||
PreObfuscation = new Game(new SteamProvider(true), "preobfuscation", Path.Combine(WorkPath, "preobfuscation"));
|
||||
Steam = new Game(new SteamProvider(false), "steam", Path.Combine(WorkPath, "steam"));
|
||||
Itch = new Game(new ItchProvider(), "itch", Path.Combine(WorkPath, "itch"));
|
||||
}
|
||||
|
||||
public async Task SetupAsync()
|
||||
{
|
||||
var preObfuscation = PreObfuscation.Provider.IsUpdateNeeded();
|
||||
var steam = Steam.Provider.IsUpdateNeeded();
|
||||
var itch = Itch.Provider.IsUpdateNeeded();
|
||||
|
||||
if (preObfuscation || steam || itch)
|
||||
{
|
||||
ContentDownloader.ShutdownSteam3();
|
||||
|
||||
if (preObfuscation)
|
||||
{
|
||||
await PreObfuscation.DownloadAsync();
|
||||
Console.WriteLine($"Downloaded {nameof(PreObfuscation)} ({PreObfuscation.Version})");
|
||||
}
|
||||
|
||||
if (steam)
|
||||
{
|
||||
await Steam.DownloadAsync();
|
||||
Console.WriteLine($"Downloaded {nameof(Steam)} ({Steam.Version})");
|
||||
}
|
||||
|
||||
if (itch)
|
||||
{
|
||||
await Itch.DownloadAsync();
|
||||
Console.WriteLine($"Downloaded {nameof(Itch)} ({Itch.Version})");
|
||||
}
|
||||
}
|
||||
|
||||
ContentDownloader.ShutdownSteam3();
|
||||
|
||||
PreObfuscation.UpdateVersion();
|
||||
Steam.UpdateVersion();
|
||||
Itch.UpdateVersion();
|
||||
|
||||
if (PreObfuscation.Version != "2020.9.9")
|
||||
{
|
||||
throw new ArgumentException("Pre obfuscation version is invalid");
|
||||
}
|
||||
|
||||
PreObfuscation.Dump();
|
||||
Steam.Dump();
|
||||
Itch.Dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Reactor.Greenhouse.Setup
|
||||
{
|
||||
public static class GameVersionParser
|
||||
{
|
||||
public static int IndexOf(this byte[] source, byte[] pattern)
|
||||
{
|
||||
for (var i = 0; i < source.Length; i++)
|
||||
{
|
||||
if (source.Skip(i).Take(pattern.Length).SequenceEqual(pattern))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static string Parse(string file)
|
||||
{
|
||||
var bytes = File.ReadAllBytes(file);
|
||||
|
||||
var pattern = Encoding.UTF8.GetBytes("public.app-category.games");
|
||||
var index = bytes.IndexOf(pattern) + pattern.Length + 127;
|
||||
|
||||
return Encoding.UTF8.GetString(bytes.Skip(index).TakeWhile(x => x != 0).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Reactor.Greenhouse.Setup.Provider
|
||||
{
|
||||
public abstract class BaseProvider
|
||||
{
|
||||
public Game Game { get; internal set; }
|
||||
|
||||
public abstract void Setup();
|
||||
public abstract Task DownloadAsync();
|
||||
public abstract bool IsUpdateNeeded();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using DepotDownloader;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Reactor.Greenhouse.Setup.Provider
|
||||
{
|
||||
public class ItchProvider : BaseProvider
|
||||
{
|
||||
private HttpClient HttpClient { get; } = new HttpClient(new HttpClientHandler
|
||||
{
|
||||
CookieContainer = new CookieContainer()
|
||||
});
|
||||
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
Console.Write("itch.io username: ");
|
||||
Username = Console.ReadLine();
|
||||
|
||||
Console.Write("itch.io password: ");
|
||||
Password = Util.ReadPassword();
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public override async Task DownloadAsync()
|
||||
{
|
||||
var htmlParser = new HtmlParser();
|
||||
|
||||
var csrfResponse = await HttpClient.GetAsync("https://itch.io/login");
|
||||
|
||||
var csrfDocument = htmlParser.ParseDocument(await csrfResponse.Content.ReadAsStreamAsync());
|
||||
|
||||
var loginResponse = await HttpClient.PostAsync("https://itch.io/login", new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("csrf_token", ((IHtmlInputElement) csrfDocument.QuerySelector("input[name=csrf_token]")).Value),
|
||||
new KeyValuePair<string, string>("username", Username),
|
||||
new KeyValuePair<string, string>("password", Password)
|
||||
}));
|
||||
|
||||
if (!loginResponse.IsSuccessStatusCode || (await loginResponse.Content.ReadAsStringAsync()).Contains("Errors"))
|
||||
{
|
||||
throw new Exception("itch.io authentication failed");
|
||||
}
|
||||
|
||||
Console.WriteLine("Logged into itch.io");
|
||||
|
||||
var pageDocument = htmlParser.ParseDocument(await HttpClient.GetStringAsync("https://innersloth.itch.io/among-us"));
|
||||
var downloadPageUrl = ((IHtmlAnchorElement) pageDocument.QuerySelector("a[class=button]")).Href;
|
||||
|
||||
var downloadDocument = htmlParser.ParseDocument(await HttpClient.GetStringAsync(downloadPageUrl));
|
||||
var uploadId = ((IHtmlAnchorElement) downloadDocument.QuerySelector("a[class='button download_btn']")).Dataset["upload_id"];
|
||||
|
||||
var keyRegex = new Regex("key\":\"(.+)\",");
|
||||
var key = downloadDocument.QuerySelectorAll("script[type='text/javascript']")
|
||||
.Cast<IHtmlScriptElement>()
|
||||
.Select(x => keyRegex.Match(x.InnerHtml))
|
||||
.Single(x => x.Success)
|
||||
.Groups[1].Value;
|
||||
|
||||
var json = await HttpClient.PostAsync($"https://innersloth.itch.io/among-us/file/{uploadId}?key={key}", null);
|
||||
var response = JsonConvert.DeserializeObject<DownloadResponse>(await json.Content.ReadAsStringAsync());
|
||||
|
||||
Console.WriteLine($"Downloading {response.Url}");
|
||||
|
||||
var files = new[] { "GameAssembly.dll", "global-metadata.dat", "globalgamemanagers" };
|
||||
using var zipArchive = new ZipArchive(await HttpClient.GetStreamAsync(response.Url), ZipArchiveMode.Read);
|
||||
foreach (var entry in zipArchive.Entries)
|
||||
{
|
||||
if (files.Contains(entry.Name))
|
||||
{
|
||||
var path = Path.Combine("work", "itch", entry.FullName.Substring(entry.FullName.IndexOf("/", StringComparison.Ordinal) + 1));
|
||||
|
||||
Directory.GetParent(path)!.Create();
|
||||
entry.ExtractToFile(path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsUpdateNeeded()
|
||||
{
|
||||
return !Directory.Exists(Game.Path);
|
||||
}
|
||||
|
||||
public class DownloadResponse
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public bool External { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using DepotDownloader;
|
||||
using SteamKit2;
|
||||
|
||||
namespace Reactor.Greenhouse.Setup.Provider
|
||||
{
|
||||
public class SteamProvider : BaseProvider
|
||||
{
|
||||
private const uint AppId = 945360;
|
||||
private const uint DepotId = 945361;
|
||||
private const ulong PreObfuscationManifest = 3596575937380717449;
|
||||
|
||||
public bool IsPreObfuscation { get; }
|
||||
|
||||
public SteamProvider(bool isPreObfuscation)
|
||||
{
|
||||
IsPreObfuscation = isPreObfuscation;
|
||||
}
|
||||
|
||||
public override bool IsUpdateNeeded()
|
||||
{
|
||||
DepotConfigStore.LoadFromFile(Path.Combine(Game.Path, ".DepotDownloader", "depot.config"));
|
||||
if (DepotConfigStore.Instance.InstalledManifestIDs.TryGetValue(DepotId, out var installedManifest))
|
||||
{
|
||||
if (IsPreObfuscation)
|
||||
{
|
||||
if (installedManifest == PreObfuscationManifest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ContentDownloader.steam3 == null)
|
||||
{
|
||||
ContentDownloader.InitializeSteam3();
|
||||
}
|
||||
|
||||
ContentDownloader.steam3!.RequestAppInfo(AppId);
|
||||
|
||||
var depots = ContentDownloader.GetSteam3AppSection(AppId, EAppInfoSection.Depots);
|
||||
if (installedManifest == depots[DepotId.ToString()]["manifests"][ContentDownloader.DEFAULT_BRANCH].AsUnsignedLong())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
if (ContentDownloader.steam3 != null && ContentDownloader.steam3.bConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AccountSettingsStore.LoadFromFile("account.config");
|
||||
|
||||
ContentDownloader.Config.RememberPassword = true;
|
||||
|
||||
Console.Write("Steam username: ");
|
||||
var username = Console.ReadLine();
|
||||
|
||||
string password = null;
|
||||
|
||||
if (!AccountSettingsStore.Instance.LoginKeys.ContainsKey(username))
|
||||
{
|
||||
Console.Write("Steam password: ");
|
||||
password = ContentDownloader.Config.SuppliedPassword = Util.ReadPassword();
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
ContentDownloader.InitializeSteam3(username, password);
|
||||
|
||||
ContentDownloader.Config.UsingFileList = true;
|
||||
ContentDownloader.Config.FilesToDownload = new List<string>
|
||||
{
|
||||
"GameAssembly.dll"
|
||||
};
|
||||
ContentDownloader.Config.FilesToDownloadRegex = new List<Regex>
|
||||
{
|
||||
new Regex("^Among Us_Data/il2cpp_data/Metadata/global-metadata.dat$".Replace("/", "[\\\\|/]"), RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex("^Among Us_Data/globalgamemanagers$".Replace("/", "[\\\\|/]"), RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
};
|
||||
}
|
||||
|
||||
public override Task DownloadAsync()
|
||||
{
|
||||
ContentDownloader.Config.InstallDirectory = Game.Path;
|
||||
return ContentDownloader.DownloadAppAsync(AppId, DepotId, IsPreObfuscation ? PreObfuscationManifest : ContentDownloader.INVALID_MANIFEST_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://reactor.gg/schemas/mappings.schema.json",
|
||||
"title": "Reactor.OxygenFilter - Mappings",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"original": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "number"
|
||||
},
|
||||
"signature": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"const": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mapped": {
|
||||
"type": "string"
|
||||
},
|
||||
"notes": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"required": [
|
||||
"original"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/member",
|
||||
"properties": {
|
||||
"nested": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/type"
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/member"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/member"
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/member",
|
||||
"properties": {
|
||||
"parameters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/type"
|
||||
},
|
||||
"required": [
|
||||
"original"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"types"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
2020.12.9.json
|
||||
@@ -0,0 +1,809 @@
|
||||
{
|
||||
"$schema": "../mappings.schema.json",
|
||||
"types": [
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "Constants",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Byte[] ()"
|
||||
},
|
||||
"mapped": "GetBroadcastVersion"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "GamePlayPort"
|
||||
},
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "AnnouncementPort"
|
||||
},
|
||||
{
|
||||
"original": 2,
|
||||
"mapped": "ShipOnlyMask"
|
||||
},
|
||||
{
|
||||
"original": 3,
|
||||
"mapped": "ShipAndObjectsMask"
|
||||
},
|
||||
{
|
||||
"original": 4,
|
||||
"mapped": "ShipAndAllObjectsMask"
|
||||
},
|
||||
{
|
||||
"original": 5,
|
||||
"mapped": "NotShipMask"
|
||||
},
|
||||
{
|
||||
"original": 6,
|
||||
"mapped": "Usables"
|
||||
},
|
||||
{
|
||||
"original": 7,
|
||||
"mapped": "PlayersOnlyMask"
|
||||
},
|
||||
{
|
||||
"original": 8,
|
||||
"mapped": "ShadowMask"
|
||||
},
|
||||
{
|
||||
"original": 9,
|
||||
"mapped": "CompatVersions"
|
||||
},
|
||||
{
|
||||
"original": 10,
|
||||
"mapped": "Year"
|
||||
},
|
||||
{
|
||||
"original": 11,
|
||||
"mapped": "Month"
|
||||
},
|
||||
{
|
||||
"original": 12,
|
||||
"mapped": "Day"
|
||||
},
|
||||
{
|
||||
"original": 13,
|
||||
"mapped": "Revision"
|
||||
},
|
||||
{
|
||||
"original": 14,
|
||||
"mapped": "field_14"
|
||||
},
|
||||
{
|
||||
"original": 15,
|
||||
"mapped": "LocalNetAddress"
|
||||
},
|
||||
{
|
||||
"original": 16,
|
||||
"mapped": "InfinitySymbol"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "SaveManager",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Collections.Generic.HashSet`1<System.String>",
|
||||
"index": 23
|
||||
},
|
||||
"mapped": "purchases"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"mapped": "LastAnnouncement",
|
||||
"original": 0
|
||||
},
|
||||
{
|
||||
"mapped": "BoughtNoAds",
|
||||
"original": 1
|
||||
},
|
||||
{
|
||||
"mapped": "VSync",
|
||||
"original": 2
|
||||
},
|
||||
{
|
||||
"mapped": "CensorChat",
|
||||
"original": 3
|
||||
},
|
||||
{
|
||||
"mapped": "ShowAdsScreen",
|
||||
"original": 4
|
||||
},
|
||||
{
|
||||
"mapped": "ShowMinPlayerWarning",
|
||||
"original": 6
|
||||
},
|
||||
{
|
||||
"mapped": "ShowOnlineHelp",
|
||||
"original": 7
|
||||
},
|
||||
{
|
||||
"mapped": "SfxVolume",
|
||||
"original": 8
|
||||
},
|
||||
{
|
||||
"mapped": "MusicVolume",
|
||||
"original": 9
|
||||
},
|
||||
{
|
||||
"mapped": "TouchConfig",
|
||||
"original": 10
|
||||
},
|
||||
{
|
||||
"mapped": "JoystickSize",
|
||||
"original": 11
|
||||
},
|
||||
{
|
||||
"mapped": "PlayerName",
|
||||
"original": 12
|
||||
},
|
||||
{
|
||||
"mapped": "LastPet",
|
||||
"original": 13
|
||||
},
|
||||
{
|
||||
"mapped": "LastHat",
|
||||
"original": 14
|
||||
},
|
||||
{
|
||||
"mapped": "LastSkin",
|
||||
"original": 15
|
||||
},
|
||||
{
|
||||
"mapped": "LastLanguage",
|
||||
"original": 16
|
||||
},
|
||||
{
|
||||
"mapped": "BodyColor",
|
||||
"original": 17
|
||||
},
|
||||
{
|
||||
"mapped": "GameHostOptions",
|
||||
"original": 18
|
||||
},
|
||||
{
|
||||
"mapped": "GameSearchOptions",
|
||||
"original": 19
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "InnerNetClient",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Byte[] ()"
|
||||
},
|
||||
"mapped": "GetConnectionData"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Void ({{GameOverReason}},System.Boolean)"
|
||||
},
|
||||
"mapped": "OnGameEnd"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "GameOptionsData",
|
||||
"fields": [
|
||||
{
|
||||
"original": 22,
|
||||
"mapped": "isDefaults"
|
||||
},
|
||||
{
|
||||
"original": 23,
|
||||
"mapped": "ConfirmEjects"
|
||||
},
|
||||
{
|
||||
"original": 24,
|
||||
"mapped": "VisualTasks"
|
||||
},
|
||||
{
|
||||
"original": 25,
|
||||
"mapped": "TaskBarUpdate"
|
||||
},
|
||||
{
|
||||
"original": 26,
|
||||
"mapped": "AnonymousVotes"
|
||||
},
|
||||
{
|
||||
"original": 27,
|
||||
"mapped": "RecommendedKillCooldown"
|
||||
},
|
||||
{
|
||||
"original": 28,
|
||||
"mapped": "RecommendedImpostors"
|
||||
},
|
||||
{
|
||||
"original": 29,
|
||||
"mapped": "MaxImpostors"
|
||||
},
|
||||
{
|
||||
"original": 30,
|
||||
"mapped": "MinPlayers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "VersionShower",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"name": "Start",
|
||||
"signature": "System.Void ()"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "PingTracker",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"name": "Update",
|
||||
"signature": "System.Void ()"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "GameStartManager",
|
||||
"fields": [
|
||||
{
|
||||
"original": 3,
|
||||
"mapped": "LastPlayerCount"
|
||||
},
|
||||
{
|
||||
"original": 12,
|
||||
"mapped": "startState"
|
||||
},
|
||||
{
|
||||
"original": 13,
|
||||
"mapped": "countDownTimer"
|
||||
}
|
||||
],
|
||||
"nested": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "StartingStates"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "GameData",
|
||||
"nested": [
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "PlayerInfo",
|
||||
"fields": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "PlayerId"
|
||||
},
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "PlayerName"
|
||||
},
|
||||
{
|
||||
"original": 2,
|
||||
"mapped": "ColorId"
|
||||
},
|
||||
{
|
||||
"original": 3,
|
||||
"mapped": "HatId"
|
||||
},
|
||||
{
|
||||
"original": 4,
|
||||
"mapped": "PetId"
|
||||
},
|
||||
{
|
||||
"original": 5,
|
||||
"mapped": "SkinId"
|
||||
},
|
||||
{
|
||||
"original": 6,
|
||||
"mapped": "Disconnected"
|
||||
},
|
||||
{
|
||||
"original": 7,
|
||||
"mapped": "Tasks"
|
||||
},
|
||||
{
|
||||
"original": 8,
|
||||
"mapped": "IsImpostor"
|
||||
},
|
||||
{
|
||||
"original": 9,
|
||||
"mapped": "IsDead"
|
||||
},
|
||||
{
|
||||
"original": 10,
|
||||
"mapped": "_object"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "Object"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "RegionInfo",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"signature": "System.String"
|
||||
},
|
||||
"mapped": "Name"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 1,
|
||||
"signature": "System.String"
|
||||
},
|
||||
"mapped": "PingServer"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 2,
|
||||
"signature": "{{ServerInfo}}[]"
|
||||
},
|
||||
"mapped": "Servers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "PlayerPhysics",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "UnityEngine.Rigidbody2D"
|
||||
},
|
||||
"mapped": "body"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "PlayerControl",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "UnityEngine.SpriteRenderer"
|
||||
},
|
||||
"mapped": "myRend"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "MapBehaviour",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"name": "ShowInfectedMap",
|
||||
"signature": "System.Void ()"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "IntroCutscene",
|
||||
"nested": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "CoBegin__d"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"name": "IntroStinger",
|
||||
"signature": "UnityEngine.AudioClip"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "GameOptionsMenu",
|
||||
"fields": [
|
||||
{
|
||||
"original": 2,
|
||||
"mapped": "Children"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"name": "ResetButton",
|
||||
"signature": "UnityEngine.GameObject"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "GameSettingMenu",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"name": "HideForOnline",
|
||||
"signature": "UnityEngine.Transform[]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "AmongUsClient",
|
||||
"nested": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "CoStartGame__d"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "Minigame",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"name": "ForceClose",
|
||||
"signature": "System.Void ()"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "KeyboardJoystick",
|
||||
"methods": [
|
||||
{
|
||||
"original": "Update"
|
||||
},
|
||||
{
|
||||
"original": "get_Delta"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "PlayerVoteArea",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"name": "CoAnimateOverlay",
|
||||
"signature": "System.Collections.IEnumerator ()"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "PlayerTab",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Collections.Generic.HashSet`1<System.Int32>"
|
||||
},
|
||||
"mapped": "AvailableColors"
|
||||
}
|
||||
],
|
||||
"nested": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "OnEnable__0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "MeetingHud",
|
||||
"nested": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "VoteStates"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "ResultsTime"
|
||||
},
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "Depth"
|
||||
},
|
||||
{
|
||||
"original": 7,
|
||||
"mapped": "CounterOrigin"
|
||||
},
|
||||
{
|
||||
"original": 8,
|
||||
"mapped": "CounterOffsets"
|
||||
},
|
||||
{
|
||||
"original": 10,
|
||||
"mapped": "playerStates"
|
||||
},
|
||||
{
|
||||
"original": 19,
|
||||
"mapped": "state"
|
||||
},
|
||||
{
|
||||
"original": 23,
|
||||
"mapped": "exiledPlayer"
|
||||
},
|
||||
{
|
||||
"original": 24,
|
||||
"mapped": "wasTie"
|
||||
},
|
||||
{
|
||||
"original": 27,
|
||||
"mapped": "reporterId"
|
||||
},
|
||||
{
|
||||
"original": 28,
|
||||
"mapped": "amDead"
|
||||
},
|
||||
{
|
||||
"original": 29,
|
||||
"mapped": "resultsStartedAt"
|
||||
},
|
||||
{
|
||||
"original": 30,
|
||||
"mapped": "lastSecond"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "MessageExtensions",
|
||||
"methods": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "WriteNetObject"
|
||||
},
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "ReadNetObject"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "PhysicsHelpers",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Boolean (UnityEngine.Vector2,UnityEngine.Vector2,System.Single,System.Int32)"
|
||||
},
|
||||
"mapped": "AnyNonTriggersBetween"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "AspectPosition",
|
||||
"nested": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "EdgeAlignments"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "GameCode",
|
||||
"methods": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "IntToGameName"
|
||||
},
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "GameNameToInt"
|
||||
},
|
||||
{
|
||||
"original": 2,
|
||||
"mapped": "CreateGameId"
|
||||
},
|
||||
{
|
||||
"original": 3,
|
||||
"mapped": "IntToGameNameV2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "Effects",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"signature": "System.Collections.IEnumerator (System.Single,UnityEngine.Transform,System.Single,System.Single)"
|
||||
},
|
||||
"mapped": "Bloop"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "StringOption",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"name": "Values",
|
||||
"signature": "StringNames[]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "PlainShipRoom",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"name": "RoomId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 1,
|
||||
"name": "survCamera"
|
||||
}
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 2,
|
||||
"name": "roomArea"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "CooldownHelpers",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"signature": "System.Void (UnityEngine.SpriteRenderer)"
|
||||
},
|
||||
"mapped": "SetCooldownNormalizedUvs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "WinningPlayerData",
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"signature": "System.String"
|
||||
},
|
||||
"mapped": "Name"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 1,
|
||||
"signature": "System.Boolean"
|
||||
},
|
||||
"mapped": "IsDead"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 2,
|
||||
"signature": "System.Boolean"
|
||||
},
|
||||
"mapped": "IsImpostor"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 3,
|
||||
"signature": "System.Int32"
|
||||
},
|
||||
"mapped": "ColorId"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 4,
|
||||
"signature": "System.UInt32"
|
||||
},
|
||||
"mapped": "SkinId"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 5,
|
||||
"signature": "System.UInt32"
|
||||
},
|
||||
"mapped": "HatId"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 6,
|
||||
"signature": "System.UInt32"
|
||||
},
|
||||
"mapped": "PetId"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"index": 7,
|
||||
"signature": "System.Boolean"
|
||||
},
|
||||
"mapped": "IsYou"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "TempData",
|
||||
"methods": [
|
||||
{
|
||||
"original": {
|
||||
"index": 0,
|
||||
"signature": "System.Boolean ({{GameOverReason}})"
|
||||
},
|
||||
"mapped": "DidHumansWin"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": 0,
|
||||
"mapped": "LastDeathReason"
|
||||
},
|
||||
{
|
||||
"original": 1,
|
||||
"mapped": "EndReason"
|
||||
},
|
||||
{
|
||||
"original": 2,
|
||||
"mapped": "showAd"
|
||||
},
|
||||
{
|
||||
"original": 3,
|
||||
"mapped": "winners"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"$schema": "../mappings.schema.json",
|
||||
"types": [
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "MeetingHud",
|
||||
"methods": [
|
||||
{
|
||||
"notes": "MeetingHud#CastVote",
|
||||
"original": "EIGGEKBEOEA",
|
||||
"mapped": "CheckForEndVoting"
|
||||
},
|
||||
{
|
||||
"notes": "void (byte[], GameData.PlayerInfo, bool)",
|
||||
"original": "KGIPOIOFBJH",
|
||||
"mapped": "VotingComplete"
|
||||
},
|
||||
{
|
||||
"notes": "MeetingHud#VotingComplete",
|
||||
"original": "FBNKEAEKKJE",
|
||||
"mapped": "PopulateResults"
|
||||
},
|
||||
{
|
||||
"notes": "MeetingHud#VotingComplete",
|
||||
"original": "ECLBANBAFCN",
|
||||
"mapped": "SetupProceedButton"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "ShipStatus",
|
||||
"methods": [
|
||||
{
|
||||
"notes": "ShipStatus#FixedUpdate",
|
||||
"original": "ECNNLBKGIKE",
|
||||
"mapped": "CheckEndCriteria"
|
||||
},
|
||||
{
|
||||
"notes": "CheckEndCriteria",
|
||||
"original": "PLBGOMIEONF",
|
||||
"mapped": "RpcEndGame"
|
||||
},
|
||||
{
|
||||
"original": "NJOJCFDGEAE",
|
||||
"mapped": "SelectInfected"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "ServerManager",
|
||||
"methods": [
|
||||
{
|
||||
"notes": "Last call in ServerManager#Awake",
|
||||
"original": "BBIDOJKFGPM",
|
||||
"mapped": "LoadServers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": {},
|
||||
"mapped": "StatsManager",
|
||||
"properties": [
|
||||
{
|
||||
"notes": "Second call in *GameButton#OnClick",
|
||||
"original": "IDEHLLDOODH",
|
||||
"mapped": "AmBanned"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"original": {
|
||||
"const": {
|
||||
"type": "System.Byte",
|
||||
"value": 3
|
||||
}
|
||||
},
|
||||
"mapped": "StatsVersion"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"const": {
|
||||
"type": "System.Int32",
|
||||
"value": 2
|
||||
}
|
||||
},
|
||||
"mapped": "PointsUntilBanStarts"
|
||||
},
|
||||
{
|
||||
"original": {
|
||||
"const": {
|
||||
"type": "System.Int32",
|
||||
"value": 5
|
||||
}
|
||||
},
|
||||
"mapped": "MinutesPerBanPoint"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "PlayerControl",
|
||||
"methods": [
|
||||
{
|
||||
"notes": "Only method with [CallerCount(2)] and PlayerControl ()",
|
||||
"original": "OAFBLFBAOJJ",
|
||||
"mapped": "FindClosestTarget"
|
||||
},
|
||||
{
|
||||
"notes": "first call in CmdReportDeadBody",
|
||||
"original": "MKAIDPJOJFP",
|
||||
"mapped": "ReportDeadBody"
|
||||
},
|
||||
{
|
||||
"notes": "Only method with [CallerCount(1)] and void (byte[])",
|
||||
"original": "OJPCECLPCCF",
|
||||
"mapped": "SetInfected"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"original": "^",
|
||||
"mapped": "EndGameManager",
|
||||
"methods": [
|
||||
{
|
||||
"notes": "first call in EndGameManager#Start",
|
||||
"original": "MFIBPNBCMKM",
|
||||
"mapped": "SetEverythingUp"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Reactor.OxygenFilter
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsObfuscated(this string text)
|
||||
{
|
||||
return text.Length == 11 && text.All(char.IsUpper);
|
||||
}
|
||||
|
||||
public static string GetSignature(this MethodDefinition methodDefinition)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append(methodDefinition.ReturnType.FullName);
|
||||
sb.Append(" ");
|
||||
|
||||
sb.Append("(");
|
||||
if (methodDefinition.HasParameters)
|
||||
{
|
||||
for (var i = 0; i < methodDefinition.Parameters.Count; i++)
|
||||
{
|
||||
var parameterType = methodDefinition.Parameters[i].ParameterType;
|
||||
|
||||
if (i > 0)
|
||||
sb.Append(",");
|
||||
|
||||
if (parameterType is SentinelType)
|
||||
sb.Append("...,");
|
||||
|
||||
sb.Append(parameterType.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append(")");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetSignature(this FieldDefinition fieldDefinition)
|
||||
{
|
||||
return fieldDefinition.FieldType.FullName;
|
||||
}
|
||||
|
||||
public static string GetSignature(this PropertyDefinition propertyDefinition)
|
||||
{
|
||||
return propertyDefinition.PropertyType.FullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Reactor.OxygenFilter
|
||||
{
|
||||
public class Mappings
|
||||
{
|
||||
public List<MappedType> Types { get; set; } = new List<MappedType>();
|
||||
|
||||
public MappedType Find(string name, Func<MappedType, string> predicate)
|
||||
{
|
||||
var split = name.Split('.').SelectMany(x => x.Split('+')).ToArray();
|
||||
|
||||
if (split.Length > 1)
|
||||
{
|
||||
MappedType type = null;
|
||||
|
||||
foreach (var s in split)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
type = Find(s, predicate);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = type.Nested.SingleOrDefault(x => predicate(x) == s);
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
return Types.SingleOrDefault(x => predicate(x) == name);
|
||||
}
|
||||
|
||||
public MappedType FindByMapped(string name)
|
||||
{
|
||||
return Find(name, x => x.Mapped);
|
||||
}
|
||||
|
||||
public MappedType FindByOriginal(string name)
|
||||
{
|
||||
return Find(name, x => x.Original.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public class OriginalDescriptor
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int? Index { get; set; }
|
||||
public string Signature { get; set; }
|
||||
public Constant Const { get; set; }
|
||||
|
||||
public bool Equals(OriginalDescriptor obj)
|
||||
{
|
||||
return Name == obj.Name && Index == obj.Index && Signature == obj.Signature && Const == obj.Const;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return Name == null && Index == null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name ?? Index?.ToString();
|
||||
}
|
||||
|
||||
public class Constant
|
||||
{
|
||||
private object _value;
|
||||
|
||||
public object Value
|
||||
{
|
||||
get => Convert.ChangeType(_value, Type);
|
||||
set => _value = value;
|
||||
}
|
||||
|
||||
public Type Type { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class OriginalDescriptorConverter : JsonConverter<OriginalDescriptor>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, OriginalDescriptor value, JsonSerializer serializer)
|
||||
{
|
||||
if (value.Name != null && value.Index == null && value.Signature == null)
|
||||
{
|
||||
writer.WriteValue(value.Name);
|
||||
}
|
||||
else if (value.Index != null && value.Name == null && value.Signature == null)
|
||||
{
|
||||
writer.WriteValue(value.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
serializer.Serialize(writer, value);
|
||||
}
|
||||
}
|
||||
|
||||
public override OriginalDescriptor ReadJson(JsonReader reader, Type objectType, OriginalDescriptor existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value is string name)
|
||||
{
|
||||
return new OriginalDescriptor { Name = name };
|
||||
}
|
||||
|
||||
if (reader.Value is long index)
|
||||
{
|
||||
return new OriginalDescriptor { Index = (int?) index };
|
||||
}
|
||||
|
||||
if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
return serializer.Deserialize<OriginalDescriptor>(reader);
|
||||
}
|
||||
|
||||
throw new JsonSerializationException();
|
||||
}
|
||||
}
|
||||
|
||||
public class MappedMember
|
||||
{
|
||||
[JsonProperty(Order = -3)]
|
||||
[JsonConverter(typeof(OriginalDescriptorConverter))]
|
||||
public OriginalDescriptor Original { get; set; }
|
||||
|
||||
[JsonProperty(Order = -2)]
|
||||
public string Mapped { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = Original.ToString();
|
||||
|
||||
if (Mapped != null)
|
||||
{
|
||||
result += $" (name: {Mapped})";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public MappedMember()
|
||||
{
|
||||
}
|
||||
|
||||
public MappedMember(OriginalDescriptor original, string mapped)
|
||||
{
|
||||
Original = original;
|
||||
|
||||
if (original.Name != mapped)
|
||||
{
|
||||
Mapped = mapped;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(MappedMember obj, bool compareOriginal = true)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Mapped == obj.Mapped && (!compareOriginal || (Original.Name == "^" && obj.Mapped != null) || Original.Equals(obj.Original));
|
||||
}
|
||||
}
|
||||
|
||||
public class MappedMethod : MappedMember
|
||||
{
|
||||
public List<string> Parameters { get; set; } = new List<string>();
|
||||
|
||||
public MappedMethod()
|
||||
{
|
||||
}
|
||||
|
||||
public MappedMethod(OriginalDescriptor original, string mapped) : base(original, mapped)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MappedType : MappedMember
|
||||
{
|
||||
public List<MappedMember> Properties { get; set; } = new List<MappedMember>();
|
||||
public List<MappedMember> Fields { get; set; } = new List<MappedMember>();
|
||||
public List<MappedMethod> Methods { get; set; } = new List<MappedMethod>();
|
||||
|
||||
public List<MappedType> Nested { get; set; } = new List<MappedType>();
|
||||
|
||||
public MappedType()
|
||||
{
|
||||
}
|
||||
|
||||
public MappedType(OriginalDescriptor original, string mapped) : base(original, mapped)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Reactor.OxygenFilter
|
||||
{
|
||||
public class ObfuscationMapper
|
||||
{
|
||||
public ModuleDefinition ModuleDef { get; }
|
||||
public Mappings Mappings { get; private set; }
|
||||
|
||||
public ObfuscationMapper(ModuleDefinition moduleDef)
|
||||
{
|
||||
ModuleDef = moduleDef;
|
||||
}
|
||||
|
||||
public void Map()
|
||||
{
|
||||
var mappedAttribute = new TypeDefinition("", "MappedAttribute", TypeAttributes.Public, ModuleDef.ImportReference(typeof(Attribute)))
|
||||
{
|
||||
Attributes = TypeAttributes.Class & TypeAttributes.Public
|
||||
};
|
||||
|
||||
ModuleDef.Types.Add(mappedAttribute);
|
||||
|
||||
var mappedAttributeCtor = new MethodReference(".ctor", ModuleDef.ImportReference(typeof(void)), mappedAttribute)
|
||||
{
|
||||
HasThis = true,
|
||||
Parameters = { new ParameterDefinition(ModuleDef.ImportReference(typeof(string))) }
|
||||
};
|
||||
|
||||
void MapMember(ICustomAttributeProvider attributes, string mapped)
|
||||
{
|
||||
attributes.CustomAttributes.Add(new CustomAttribute(mappedAttributeCtor)
|
||||
{
|
||||
ConstructorArguments = { new CustomAttributeArgument(ModuleDef.ImportReference(typeof(string)), mapped) }
|
||||
});
|
||||
}
|
||||
|
||||
// beebyte 1 iq?
|
||||
foreach (var propertyDef in ModuleDef.Types.SelectMany(x => x.NestedTypes.Prepend(x)).SelectMany(x => x.Properties))
|
||||
{
|
||||
const string getPrefix = "get_";
|
||||
const string setPrefix = "set_";
|
||||
|
||||
var getName = propertyDef.GetMethod != null && propertyDef.GetMethod.Name.StartsWith(getPrefix) ? propertyDef.GetMethod.Name.Substring(getPrefix.Length) : null;
|
||||
var setName = propertyDef.SetMethod != null && propertyDef.SetMethod.Name.StartsWith(setPrefix) ? propertyDef.SetMethod.Name.Substring(setPrefix.Length) : null;
|
||||
|
||||
if (getName != null && setName != null && getName != setName)
|
||||
{
|
||||
throw new Exception($"{propertyDef.FullName} has 2 different accessor names");
|
||||
}
|
||||
|
||||
if (getName != null || setName != null)
|
||||
{
|
||||
var name = getName ?? setName;
|
||||
|
||||
MapMember(propertyDef, name);
|
||||
|
||||
if (propertyDef.GetMethod != null)
|
||||
MapMember(propertyDef.GetMethod, "get_" + name);
|
||||
|
||||
if (propertyDef.SetMethod != null)
|
||||
MapMember(propertyDef.SetMethod, "set_" + name);
|
||||
}
|
||||
}
|
||||
|
||||
void MapType(MappedType type, TypeDefinition typeDef)
|
||||
{
|
||||
if (typeDef == null)
|
||||
{
|
||||
throw new NullReferenceException($"Type {type} was not found!");
|
||||
}
|
||||
|
||||
if (type.Mapped != null)
|
||||
{
|
||||
MapMember(typeDef, type.Mapped);
|
||||
}
|
||||
|
||||
foreach (var property in type.Properties)
|
||||
{
|
||||
var propertyDef = typeDef.Properties.SingleOrDefault(x => x.Name == property.Original.Name);
|
||||
|
||||
if (propertyDef == null)
|
||||
{
|
||||
throw new NullReferenceException($"Property {property} was not found in {type}!");
|
||||
}
|
||||
|
||||
if (property.Mapped != null)
|
||||
{
|
||||
MapMember(propertyDef, property.Mapped);
|
||||
|
||||
if (propertyDef.GetMethod != null)
|
||||
MapMember(propertyDef.GetMethod, "get_" + property.Mapped);
|
||||
|
||||
if (propertyDef.SetMethod != null)
|
||||
MapMember(propertyDef.SetMethod, "set_" + property.Mapped);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var field in type.Fields)
|
||||
{
|
||||
var fieldDef = typeDef.Fields.SingleOrDefault(x => x.Name == field.Original.Name);
|
||||
|
||||
if (fieldDef == null)
|
||||
{
|
||||
throw new NullReferenceException($"Field {field} was not found in {type}!");
|
||||
}
|
||||
|
||||
if (field.Mapped != null)
|
||||
{
|
||||
MapMember(fieldDef, field.Mapped);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
var methodDef = typeDef.Methods
|
||||
.SingleOrDefault(x => x.Name == method.Original.Name && (method.Original.Signature == null || x.GetSignature().ToString() == method.Original.Signature));
|
||||
|
||||
if (methodDef == null)
|
||||
{
|
||||
throw new NullReferenceException($"Method {method} was not found in {type}!");
|
||||
}
|
||||
|
||||
foreach (var methodDef2 in ModuleDef.Types
|
||||
.Where(x => x.BaseType == typeDef)
|
||||
.Select(x => x.Methods.SingleOrDefault(m => m.Name == method.Original.Name && (method.Original.Signature == null || m.GetSignature().ToString() == method.Original.Signature)))
|
||||
.Where(x => x != null)
|
||||
.Prepend(methodDef)
|
||||
)
|
||||
{
|
||||
if (method.Mapped != null)
|
||||
{
|
||||
MapMember(methodDef2, method.Mapped);
|
||||
}
|
||||
|
||||
for (var i = 0; i < method.Parameters.Count; i++)
|
||||
{
|
||||
MapMember(methodDef2.Parameters.ElementAt(i), method.Parameters[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var nested in type.Nested)
|
||||
{
|
||||
MapType(nested, typeDef.NestedTypes.SingleOrDefault(x => x.Name == nested.Original.Name));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var type in Mappings.Types)
|
||||
{
|
||||
MapType(type, ModuleDef.GetType(type.Original.Name));
|
||||
}
|
||||
|
||||
foreach (var typeDef in ModuleDef.Types)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
foreach (var member in typeDef.Properties)
|
||||
{
|
||||
if (member.Name.IsObfuscated() && member.CustomAttributes.All(x => x.AttributeType != mappedAttribute))
|
||||
{
|
||||
MapMember(member, $"Property_{i}");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
foreach (var member in typeDef.Fields)
|
||||
{
|
||||
if (member.Name.IsObfuscated() && member.CustomAttributes.All(x => x.AttributeType != mappedAttribute))
|
||||
{
|
||||
MapMember(member, $"Field_{i}");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
foreach (var member in typeDef.Methods)
|
||||
{
|
||||
if (member.Name.IsObfuscated() && member.CustomAttributes.All(x => x.AttributeType != mappedAttribute))
|
||||
{
|
||||
MapMember(member, $"Method_{i}");
|
||||
}
|
||||
|
||||
var j = 0;
|
||||
foreach (var parameter in member.Parameters)
|
||||
{
|
||||
if (parameter.Name.IsObfuscated() && parameter.CustomAttributes.All(x => x.AttributeType != mappedAttribute))
|
||||
{
|
||||
MapMember(parameter, $"Parameter_{j}");
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
foreach (var member in typeDef.NestedTypes)
|
||||
{
|
||||
if (member.Name.IsObfuscated() && member.CustomAttributes.All(x => x.AttributeType != mappedAttribute))
|
||||
{
|
||||
MapMember(member, $"Nested_{i}");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadMappings(FileInfo file)
|
||||
{
|
||||
if (!file.Exists)
|
||||
{
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
Mappings = JsonConvert.DeserializeObject<Mappings>(File.ReadAllText(file.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.IO;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Reactor.OxygenFilter
|
||||
{
|
||||
public class OxygenFilter
|
||||
{
|
||||
public void Start(FileInfo mappingsFile, FileInfo dumpedDll, FileInfo outputDll)
|
||||
{
|
||||
using var inputStream = dumpedDll.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
|
||||
using var outputStream = dumpedDll == outputDll ? inputStream : dumpedDll.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
|
||||
|
||||
var resolver = new DefaultAssemblyResolver();
|
||||
resolver.AddSearchDirectory(dumpedDll.DirectoryName);
|
||||
|
||||
using var module = ModuleDefinition.ReadModule(inputStream, new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = resolver });
|
||||
var mapper = new ObfuscationMapper(module);
|
||||
mapper.LoadMappings(mappingsFile);
|
||||
mapper.Map();
|
||||
module.Dispose();
|
||||
module.Write(outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;netstandard2.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor", "Reactor\Reactor.csproj", "{91906039-9753-472B-ACC3-12C0561FFAD8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.OxygenFilter", "Reactor.OxygenFilter\Reactor.OxygenFilter.csproj", "{154C08E6-8F9F-43A3-86C3-A3A606316D2B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.Debugger", "Reactor.Debugger\Reactor.Debugger.csproj", "{68589B4D-9EF5-40DD-8D6D-10977ED07259}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.Greenhouse", "Reactor.Greenhouse\Reactor.Greenhouse.csproj", "{00BF8959-652F-46B8-AA0F-891B4DA1112B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{91906039-9753-472B-ACC3-12C0561FFAD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91906039-9753-472B-ACC3-12C0561FFAD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91906039-9753-472B-ACC3-12C0561FFAD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{91906039-9753-472B-ACC3-12C0561FFAD8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{154C08E6-8F9F-43A3-86C3-A3A606316D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{154C08E6-8F9F-43A3-86C3-A3A606316D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{154C08E6-8F9F-43A3-86C3-A3A606316D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{154C08E6-8F9F-43A3-86C3-A3A606316D2B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{68589B4D-9EF5-40DD-8D6D-10977ED07259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{68589B4D-9EF5-40DD-8D6D-10977ED07259}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{68589B4D-9EF5-40DD-8D6D-10977ED07259}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{68589B4D-9EF5-40DD-8D6D-10977ED07259}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00BF8959-652F-46B8-AA0F-891B4DA1112B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00BF8959-652F-46B8-AA0F-891B4DA1112B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00BF8959-652F-46B8-AA0F-891B4DA1112B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00BF8959-652F-46B8-AA0F-891B4DA1112B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Binary file not shown.
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Reactor.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// General utilities
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stops <paramref name="obj"/> from being destroyed
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to stop from being destroyed</param>
|
||||
/// <returns>Passed <paramref name="obj"/></returns>
|
||||
public static T DontDestroy<T>(this T obj) where T : Object
|
||||
{
|
||||
obj.hideFlags |= HideFlags.HideAndDontSave;
|
||||
Object.DontDestroyOnLoad(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a delegate to get one notification when a scene has loaded.
|
||||
/// </summary>
|
||||
public static void once_sceneLoaded(Action<Scene, LoadSceneMode> value)
|
||||
{
|
||||
UnityAction<Scene, LoadSceneMode> unityAction = null;
|
||||
|
||||
unityAction = (Action<Scene, LoadSceneMode>) ((scene, loadMode) =>
|
||||
{
|
||||
SceneManager.remove_sceneLoaded(unityAction);
|
||||
value.Invoke(scene, loadMode);
|
||||
});
|
||||
|
||||
SceneManager.add_sceneLoaded(unityAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns random <typeparamref name="T"/> from <paramref name="input"/>
|
||||
/// </summary>
|
||||
public static T Random<T>(this IEnumerable<T> input)
|
||||
{
|
||||
var list = input as IList<T> ?? input.ToList();
|
||||
return list.Count == 0 ? default : list[UnityEngine.Random.Range(0, list.Count)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the color as a hexadecimal string in the format "RRGGBBAA".
|
||||
/// </summary>
|
||||
/// <param name="color">The color to be converted.</param>
|
||||
/// <returns>Hexadecimal string representing the color.</returns>
|
||||
/// <remarks>https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html</remarks>
|
||||
public static string ToHtmlStringRGBA(this Color32 color)
|
||||
{
|
||||
return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}";
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToHtmlStringRGBA(UnityEngine.Color32)"/>
|
||||
public static string ToHtmlStringRGBA(this Color color)
|
||||
{
|
||||
return ((Color32) color).ToHtmlStringRGBA();
|
||||
}
|
||||
|
||||
public static byte[] ReadFully(this Stream input)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
input.CopyTo(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Reactor.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// GUI utilities
|
||||
/// </summary>
|
||||
public static class GUIExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Shortcut for empty texture
|
||||
/// </summary>
|
||||
public static Texture2D CreateEmptyTexture(int width = 0, int height = 0)
|
||||
{
|
||||
return new Texture2D(width, height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamp Rect to screen size
|
||||
/// </summary>
|
||||
public static Rect ClampScreen(this Rect rect)
|
||||
{
|
||||
rect.x = Mathf.Clamp(rect.x, 0, Screen.width - rect.width);
|
||||
rect.y = Mathf.Clamp(rect.y, 0, Screen.height - rect.height);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="Sprite"/> from <paramref name="tex"/>
|
||||
/// </summary>
|
||||
public static Sprite CreateSprite(this Texture2D tex)
|
||||
{
|
||||
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100f);
|
||||
}
|
||||
|
||||
public static DefaultControls.Resources StandardResources { get; internal set; }
|
||||
|
||||
public static GameObject CreateCanvas()
|
||||
{
|
||||
var gameObject = new GameObject("Canvas");
|
||||
var canvas = gameObject.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
|
||||
var canvasScaler = gameObject.AddComponent<CanvasScaler>();
|
||||
|
||||
canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
|
||||
canvasScaler.referencePixelsPerUnit = 100f;
|
||||
|
||||
gameObject.AddComponent<GraphicRaycaster>();
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public static GameObject CreateEventSystem()
|
||||
{
|
||||
var gameObject = new GameObject("EventSystem");
|
||||
gameObject.AddComponent<EventSystem>();
|
||||
gameObject.AddComponent<StandaloneInputModule>();
|
||||
gameObject.AddComponent<BaseInput>();
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public static void SetSize(this RectTransform rectTransform, float width, float height)
|
||||
{
|
||||
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
|
||||
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
|
||||
}
|
||||
}
|
||||
|
||||
public class Window
|
||||
{
|
||||
private static int _lastWindowId;
|
||||
|
||||
public static int NextWindowId()
|
||||
{
|
||||
return _lastWindowId++;
|
||||
}
|
||||
|
||||
public int Id { get; set; } = NextWindowId();
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
public Rect Rect { get; set; }
|
||||
public Action<int> Func { get; set; }
|
||||
public string Title { get; set; }
|
||||
|
||||
public Window(Rect rect, string title, Action<int> func)
|
||||
{
|
||||
Rect = rect;
|
||||
Title = title;
|
||||
Func = func;
|
||||
}
|
||||
|
||||
public Window(Rect rect, string title, Action func) : this(rect, title, id => func())
|
||||
{
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (Enabled)
|
||||
{
|
||||
Rect = GUI.Window(Id, Rect, Func, Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DragWindow : Window
|
||||
{
|
||||
public DragWindow(Rect rect, string title, Action<int> func) : base(rect, title, func)
|
||||
{
|
||||
Func = id =>
|
||||
{
|
||||
func(id);
|
||||
|
||||
GUI.DragWindow(new Rect(0, 0, 10000, 20));
|
||||
Rect = Rect.ClampScreen();
|
||||
};
|
||||
}
|
||||
|
||||
public DragWindow(Rect rect, string title, Action func) : this(rect, title, id => func())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx.IL2CPP;
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
using UnhollowerBaseLib;
|
||||
|
||||
namespace Reactor.Patches
|
||||
{
|
||||
internal static class ModdedHandshake
|
||||
{
|
||||
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.GetConnectionData))]
|
||||
public static class HandshakePatch
|
||||
{
|
||||
public static bool Prefix(out Il2CppStructArray<byte> __result)
|
||||
{
|
||||
var handshake = MessageWriter.Get(SendOption.Reliable);
|
||||
|
||||
var plugins = Preloader.Chainloader.Plugins;
|
||||
|
||||
if (plugins.Values
|
||||
.Select(pluginInfo => pluginInfo.Instance.GetType().GetCustomAttribute<ReactorPluginSideAttribute>())
|
||||
.Any(attribute => attribute == null || attribute.Side == PluginSide.Both))
|
||||
{
|
||||
handshake.Write(-1);
|
||||
}
|
||||
|
||||
handshake.Write(Constants.GetBroadcastVersion());
|
||||
handshake.Write(SaveManager.PlayerName);
|
||||
|
||||
handshake.WritePacked(plugins.Count);
|
||||
|
||||
foreach (var plugin in plugins)
|
||||
{
|
||||
handshake.Write(plugin.Key);
|
||||
handshake.Write(plugin.Value.Metadata.Version.ToString());
|
||||
}
|
||||
|
||||
__result = handshake.ToByteArray(false);
|
||||
handshake.Recycle();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using BepInEx;
|
||||
using BepInEx.IL2CPP;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Reactor.Patches
|
||||
{
|
||||
public static class ReactorVersionShower
|
||||
{
|
||||
public static TextRenderer Text { get; private set; }
|
||||
|
||||
public static event TextUpdatedHandler TextUpdated;
|
||||
|
||||
public delegate void TextUpdatedHandler(TextRenderer text);
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
SceneManager.add_sceneLoaded((Action<Scene, LoadSceneMode>) ((scene, loadMode) =>
|
||||
{
|
||||
var original = UnityEngine.Object.FindObjectOfType<VersionShower>();
|
||||
if (!original)
|
||||
return;
|
||||
|
||||
var gameObject = UnityEngine.Object.Instantiate(original.gameObject);
|
||||
gameObject.name = "ReactorVersion";
|
||||
|
||||
var versionShower = gameObject.GetComponent<VersionShower>();
|
||||
if (versionShower)
|
||||
{
|
||||
versionShower.enabled = false;
|
||||
}
|
||||
|
||||
var aspectPosition = gameObject.GetComponent<AspectPosition>();
|
||||
|
||||
var position = aspectPosition.DistanceFromEdge;
|
||||
position.y += 0.2f;
|
||||
aspectPosition.DistanceFromEdge = position;
|
||||
|
||||
aspectPosition.AdjustPosition();
|
||||
|
||||
Text = gameObject.GetComponentInChildren<TextRenderer>();
|
||||
UpdateText();
|
||||
}));
|
||||
}
|
||||
|
||||
public static void UpdateText()
|
||||
{
|
||||
Text.Text = "Reactor " + typeof(ReactorPlugin).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||
Text.Text += "\nBepInEx: " + Paths.BepInExVersion;
|
||||
Text.Text += "\nMods: " + Preloader.Chainloader.Plugins.Count;
|
||||
TextUpdated?.Invoke(Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reactor.Patches
|
||||
{
|
||||
internal class RegionInfoWatcher : IDisposable
|
||||
{
|
||||
private FileSystemWatcher Watcher { get; }
|
||||
public bool Reload { get; set; }
|
||||
|
||||
internal RegionInfoWatcher()
|
||||
{
|
||||
Watcher = new FileSystemWatcher(
|
||||
string.IsNullOrEmpty(Application.persistentDataPath) ? Directory.GetCurrentDirectory() : Application.persistentDataPath,
|
||||
"regionInfo.dat"
|
||||
);
|
||||
|
||||
Watcher.Changed += (s, e) =>
|
||||
{
|
||||
if (new FileInfo(e.Name).Length > 0)
|
||||
{
|
||||
Reload = true;
|
||||
}
|
||||
};
|
||||
|
||||
Watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Watcher?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BepInEx.IL2CPP;
|
||||
|
||||
namespace Reactor
|
||||
{
|
||||
public class PluginSingleton<T> where T : BasePlugin
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = Preloader.Chainloader.Plugins.Values.Select(x => x.Instance).OfType<T>().Single();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
throw new Exception($"Instance for {typeof(T).FullName} is already set");
|
||||
}
|
||||
|
||||
_instance = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<Version>0.2.0</Version>
|
||||
<Description>Core mod and API for Among Us</Description>
|
||||
<Authors>NuclearPowered</Authors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Assets\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../AmongUs.props" />
|
||||
</Project>
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using BepInEx;
|
||||
using BepInEx.IL2CPP;
|
||||
using HarmonyLib;
|
||||
using Reactor.Extensions;
|
||||
using Reactor.Patches;
|
||||
using Reactor.Unstrip;
|
||||
using UnhollowerRuntimeLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reactor
|
||||
{
|
||||
[BepInPlugin(Id)]
|
||||
[BepInProcess("Among Us.exe")]
|
||||
[ReactorPluginSide(PluginSide.Client)]
|
||||
public class ReactorPlugin : BasePlugin
|
||||
{
|
||||
public const string Id = "gg.reactor.api";
|
||||
|
||||
public Harmony Harmony { get; } = new Harmony(Id);
|
||||
private RegionInfoWatcher RegionInfoWatcher { get; } = new RegionInfoWatcher();
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ReactorComponent>();
|
||||
ClassInjector.RegisterTypeInIl2Cpp<Unstrip.Patches.GUIWordWrapSizer>();
|
||||
|
||||
var gameObject = new GameObject(nameof(ReactorPlugin)).DontDestroy();
|
||||
gameObject.AddComponent<ReactorComponent>();
|
||||
|
||||
Harmony.PatchAll();
|
||||
ReactorVersionShower.Initialize();
|
||||
DefaultBundle.Load();
|
||||
}
|
||||
|
||||
public override bool Unload()
|
||||
{
|
||||
RegionInfoWatcher?.Dispose();
|
||||
|
||||
return base.Unload();
|
||||
}
|
||||
|
||||
public class ReactorComponent : MonoBehaviour
|
||||
{
|
||||
public ReactorComponent(IntPtr ptr) : base(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var watcher = PluginSingleton<ReactorPlugin>.Instance.RegionInfoWatcher;
|
||||
if (watcher.Reload)
|
||||
{
|
||||
watcher.Reload = false;
|
||||
ServerManager.Instance.LoadServers();
|
||||
PluginSingleton<ReactorPlugin>.Instance.Log.LogInfo("Region file reloaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace Reactor
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute specifies plugin's side
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ReactorPluginSideAttribute : Attribute
|
||||
{
|
||||
public PluginSide Side { get; }
|
||||
|
||||
public ReactorPluginSideAttribute(PluginSide side)
|
||||
{
|
||||
if (side == PluginSide.Server)
|
||||
{
|
||||
throw new ArgumentException("BepInEx plugin can't be server only!");
|
||||
}
|
||||
|
||||
Side = side;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plugin side used in modded handshake
|
||||
/// </summary>
|
||||
public enum PluginSide
|
||||
{
|
||||
/// <summary>
|
||||
/// Required by both sides, reject connection if missing on the other side
|
||||
/// </summary>
|
||||
Both,
|
||||
|
||||
/// <summary>
|
||||
/// Required only by client
|
||||
/// </summary>
|
||||
Client,
|
||||
|
||||
/// <summary>
|
||||
/// Required only by server
|
||||
/// </summary>
|
||||
Server
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// https://github.com/sinai-dev/UnityExplorer/blob/master/src/Unstrip/AssetBundleUnstrip.cs
|
||||
|
||||
using System;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerRuntimeLib;
|
||||
|
||||
namespace Reactor.Unstrip
|
||||
{
|
||||
public class AssetBundle
|
||||
{
|
||||
private delegate IntPtr d_LoadFromFile(IntPtr path, uint crc, ulong offset);
|
||||
|
||||
private static readonly d_LoadFromFile i_LoadFromFile = IL2CPP.ResolveICall<d_LoadFromFile>("UnityEngine.AssetBundle::LoadFromFile_Internal");
|
||||
|
||||
public static AssetBundle LoadFromFile(string path, uint crc = 0, ulong offset = 0)
|
||||
{
|
||||
return new AssetBundle(i_LoadFromFile.Invoke(IL2CPP.ManagedStringToIl2Cpp(path), crc, offset));
|
||||
}
|
||||
|
||||
private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc);
|
||||
|
||||
private static readonly d_LoadFromMemory i_LoadFromMemory = IL2CPP.ResolveICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal");
|
||||
|
||||
public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0)
|
||||
{
|
||||
return new AssetBundle(i_LoadFromMemory(((Il2CppStructArray<byte>) binary).Pointer, crc));
|
||||
}
|
||||
|
||||
public IntPtr Pointer { get; }
|
||||
|
||||
public AssetBundle(IntPtr ptr)
|
||||
{
|
||||
Pointer = ptr;
|
||||
}
|
||||
|
||||
private delegate IntPtr d_LoadAssetWithSubAssets_Internal(IntPtr __instance, IntPtr name, IntPtr type);
|
||||
|
||||
private static readonly d_LoadAssetWithSubAssets_Internal i_LoadAssetWithSubAssets_Internal = IL2CPP.ResolveICall<d_LoadAssetWithSubAssets_Internal>("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal");
|
||||
|
||||
public UnityEngine.Object[] LoadAllAssets()
|
||||
{
|
||||
var ptr = i_LoadAssetWithSubAssets_Internal.Invoke(Pointer, IL2CPP.ManagedStringToIl2Cpp(string.Empty), Il2CppType.Of<UnityEngine.Object>().Pointer);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
return new UnityEngine.Object[0];
|
||||
}
|
||||
|
||||
return new Il2CppReferenceArray<UnityEngine.Object>(ptr);
|
||||
}
|
||||
|
||||
private delegate IntPtr d_LoadAsset_Internal(IntPtr __instance, IntPtr name, IntPtr type);
|
||||
|
||||
private static readonly d_LoadAsset_Internal i_LoadAsset_Internal = IL2CPP.ResolveICall<d_LoadAsset_Internal>("UnityEngine.AssetBundle::LoadAsset_Internal");
|
||||
|
||||
public T LoadAsset<T>(string name) where T : UnityEngine.Object
|
||||
{
|
||||
var ptr = i_LoadAsset_Internal.Invoke(Pointer, IL2CPP.ManagedStringToIl2Cpp(name), Il2CppType.Of<T>().Pointer);
|
||||
|
||||
return ptr == IntPtr.Zero ? null : new UnityEngine.Object(ptr).TryCast<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Reactor.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Reactor.Unstrip
|
||||
{
|
||||
internal static class DefaultBundle
|
||||
{
|
||||
public static void Load()
|
||||
{
|
||||
var bundle = AssetBundle.LoadFromMemory(typeof(ReactorPlugin).Assembly.GetManifestResourceStream("Reactor.Assets.default.bundle").ReadFully());
|
||||
|
||||
var backupShader = bundle.LoadAsset<Shader>("UI-Default");
|
||||
|
||||
if (!Graphic.defaultGraphicMaterial.shader || Graphic.defaultGraphicMaterial.shader.name != "UI/Default")
|
||||
{
|
||||
Graphic.defaultGraphicMaterial.shader = backupShader;
|
||||
}
|
||||
|
||||
var tex = new Texture2D(1, 1, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
|
||||
tex.SetPixel(0, 0, Color.red);
|
||||
|
||||
var sprite = tex.CreateSprite();
|
||||
|
||||
GUIExtensions.StandardResources = new DefaultControls.Resources
|
||||
{
|
||||
background = sprite,
|
||||
checkmark = sprite,
|
||||
dropdown = sprite,
|
||||
knob = sprite,
|
||||
mask = sprite,
|
||||
standard = sprite,
|
||||
inputField = sprite
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using UnhollowerBaseLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reactor.Unstrip
|
||||
{
|
||||
public static class ImageConversion
|
||||
{
|
||||
private delegate bool d_LoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
|
||||
|
||||
private static readonly d_LoadImage i_LoadImage = IL2CPP.ResolveICall<d_LoadImage>("UnityEngine.ImageConversion::LoadImage");
|
||||
|
||||
/// <summary>
|
||||
/// Load image from <paramref name="data"/> to <paramref name="tex"/>
|
||||
/// </summary>
|
||||
public static bool LoadImage(this Texture2D tex, byte[] data, bool markNonReadable = false)
|
||||
{
|
||||
return i_LoadImage(tex.Pointer, ((Il2CppStructArray<byte>) data).Pointer, markNonReadable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using UnhollowerBaseLib;
|
||||
using UnityEngine;
|
||||
using Object = Il2CppSystem.Object;
|
||||
using Type = Il2CppSystem.Type;
|
||||
|
||||
namespace Reactor.Unstrip
|
||||
{
|
||||
internal static class Patches
|
||||
{
|
||||
[HarmonyPatch(typeof(GUIUtility), nameof(GUIUtility.HasKeyFocus))]
|
||||
public static class HasKeyFocusPatch
|
||||
{
|
||||
public static bool Prefix(int controlID, ref bool __result)
|
||||
{
|
||||
__result = controlID == GUIUtility.keyboardControl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GUILayoutGroup), nameof(GUILayoutGroup.Add))]
|
||||
public static class AddPatch
|
||||
{
|
||||
public static bool Prefix(GUILayoutGroup __instance, GUILayoutEntry e)
|
||||
{
|
||||
__instance.entries.Add(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GUILayoutGroup), nameof(GUILayoutGroup.GetNext))]
|
||||
public static class GetNextPatch
|
||||
{
|
||||
public static bool Prefix(GUILayoutGroup __instance, ref GUILayoutEntry __result)
|
||||
{
|
||||
if (__instance.m_Cursor < __instance.entries.Count)
|
||||
{
|
||||
__result = __instance.entries[__instance.m_Cursor];
|
||||
__instance.m_Cursor++;
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new ArgumentException(string.Concat("Getting control ", __instance.m_Cursor, "'s position in a group with only ", __instance.entries.Count, " controls when doing ", Event.current.rawType, "\nAborting"));
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GUILayoutEntry), nameof(GUILayoutEntry.CalcWidth))]
|
||||
public static class CalcWidthPatch
|
||||
{
|
||||
public static bool Prefix(GUILayoutEntry __instance)
|
||||
{
|
||||
var cast = __instance.TryCast<GUIWordWrapSizer>();
|
||||
if (cast != null)
|
||||
{
|
||||
cast.CalcWidth();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GUILayoutEntry), nameof(GUILayoutEntry.CalcHeight))]
|
||||
public static class CalcHeightPatch
|
||||
{
|
||||
public static bool Prefix(GUILayoutEntry __instance)
|
||||
{
|
||||
var cast = __instance.TryCast<GUIWordWrapSizer>();
|
||||
if (cast != null)
|
||||
{
|
||||
cast.CalcHeight();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GUIWordWrapSizer : GUILayoutEntry
|
||||
{
|
||||
private readonly GUIContent m_Content;
|
||||
private readonly float m_ForcedMinHeight;
|
||||
private readonly float m_ForcedMaxHeight;
|
||||
|
||||
public GUIWordWrapSizer(IntPtr ptr) : base(ptr)
|
||||
{
|
||||
CalcWidth();
|
||||
CalcHeight();
|
||||
}
|
||||
|
||||
public GUIWordWrapSizer(GUIStyle style, GUIContent content, GUILayoutOption[] options)
|
||||
: base(0.0f, 0.0f, 0.0f, 0.0f, style)
|
||||
{
|
||||
m_Content = new GUIContent(content.text, content.image, content.tooltip);
|
||||
ApplyOptions(options);
|
||||
m_ForcedMinHeight = minHeight;
|
||||
m_ForcedMaxHeight = maxHeight;
|
||||
|
||||
CalcWidth();
|
||||
CalcHeight();
|
||||
}
|
||||
|
||||
public new void CalcWidth()
|
||||
{
|
||||
if (this.minWidth != 0.0 && this.maxWidth != 0.0)
|
||||
return;
|
||||
float minWidth;
|
||||
float maxWidth;
|
||||
style.CalcMinMaxWidth(m_Content, out minWidth, out maxWidth);
|
||||
minWidth = Mathf.Ceil(minWidth);
|
||||
maxWidth = Mathf.Ceil(maxWidth);
|
||||
if (this.minWidth == 0.0)
|
||||
this.minWidth = minWidth;
|
||||
if (this.maxWidth == 0.0)
|
||||
this.maxWidth = maxWidth;
|
||||
}
|
||||
|
||||
public new void CalcHeight()
|
||||
{
|
||||
if (m_ForcedMinHeight != 0.0 && m_ForcedMaxHeight != 0.0)
|
||||
return;
|
||||
var num = style.CalcHeight(m_Content, rect.width);
|
||||
if (m_ForcedMinHeight == 0.0)
|
||||
minHeight = num;
|
||||
else
|
||||
minHeight = m_ForcedMinHeight;
|
||||
if (m_ForcedMaxHeight == 0.0)
|
||||
maxHeight = num;
|
||||
else
|
||||
maxHeight = m_ForcedMaxHeight;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GUILayoutUtility), nameof(GUILayoutUtility.DoGetRect), typeof(GUIContent), typeof(GUIStyle), typeof(Il2CppReferenceArray<GUILayoutOption>))]
|
||||
public static class DoGetRectPatch
|
||||
{
|
||||
public static bool Prefix(GUIContent content, GUIStyle style, Il2CppReferenceArray<GUILayoutOption> options, ref Rect __result)
|
||||
{
|
||||
GUIUtility.CheckOnGUI();
|
||||
var type = Event.current.type;
|
||||
if (type != EventType.Layout)
|
||||
{
|
||||
if (type != EventType.Used)
|
||||
{
|
||||
var next = GUILayoutUtility.current.topLevel.GetNext();
|
||||
__result = next.rect;
|
||||
}
|
||||
else
|
||||
{
|
||||
__result = GUILayoutUtility.kDummyRect;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var isHeightDependantOnWidth = style.isHeightDependantOnWidth;
|
||||
if (isHeightDependantOnWidth)
|
||||
{
|
||||
GUILayoutUtility.current.topLevel.Add(new GUIWordWrapSizer(style, content, options));
|
||||
}
|
||||
else
|
||||
{
|
||||
var constraints = new Vector2(0f, 0f);
|
||||
if (options != null)
|
||||
{
|
||||
foreach (var guilayoutOption in options)
|
||||
{
|
||||
var type2 = guilayoutOption.type;
|
||||
if (type2 != GUILayoutOption.Type.maxWidth)
|
||||
{
|
||||
if (type2 == GUILayoutOption.Type.maxHeight)
|
||||
{
|
||||
constraints.y = guilayoutOption.value.Unbox<float>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
constraints.x = guilayoutOption.value.Unbox<float>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vector = style.CalcSizeWithConstraints(content, constraints);
|
||||
vector.x = Mathf.Ceil(vector.x);
|
||||
vector.y = Mathf.Ceil(vector.y);
|
||||
GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(vector.x, vector.x, vector.y, vector.y, style));
|
||||
}
|
||||
|
||||
__result = GUILayoutUtility.kDummyRect;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GUIUtility), nameof(GUIUtility.GetStateObject))]
|
||||
public static class GetStateObjectPatch
|
||||
{
|
||||
private static Dictionary<int, Object> s_StateCache = new Dictionary<int, Object>();
|
||||
|
||||
public static bool Prefix(Type t, int controlID, out Object __result)
|
||||
{
|
||||
if (!s_StateCache.TryGetValue(controlID, out __result) || __result.GetIl2CppType() != t)
|
||||
{
|
||||
s_StateCache[controlID] = __result = t.GetConstructor(new Il2CppReferenceArray<Type>(0)).Invoke(null, new Il2CppReferenceArray<Object>(0));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user