-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTrueShaderAntiCrash.cs
153 lines (124 loc) · 6.23 KB
/
TrueShaderAntiCrash.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using MelonLoader;
using ReMod.Core;
using ReMod.Loader;
using UnityEngine.SceneManagement;
using VRC.Core;
namespace ReMod.TSAC
{
internal class TrueShaderAntiCrash : ModComponent
{
private static readonly Dictionary<string, int> OurOffsets = new()
{
{ "aCEmIwSIcjYriBQDFjQlpTNNW1/kA8Wlbkqelmt1USOMB09cnKwK7QWyOulz9d7DEYJh4+vO0Ldv8gdH+dZCrg==", 0x819130 }, // U2018.4.20 non-dev
{ "5dkhl/dWeTREXhHCIkZK17mzZkbjhTKlxb+IUSk+YaWzZrrV+G+M0ekTOEGjZ4dJuB4O3nU/oE3dycXWeJq9uA==", 0x79B3F0 }, // U2019.4.28 non-dev
{ "MV6xP7theydao4ENbGi6BbiBxdZsgGOBo/WrPSeIqh6A/E00NImjUNZn+gL+ZxzpVbJms7nUb6zluLL3+aIcfg==", 0x79C060 }, // U2019.4.29 non-dev
{ "ccZ4F7iE7a78kWdXdMekJzP7/ktzS5jOOS8IOITxa1C5Jg2TKxC0/ywY8F0o9I1vZHsxAO4eh7G2sOGzsR/+uQ==", 0x79CEE0 }, // U2019.4.30 non-dev
{ "sgZUlX3+LSHKnTiTC+nXNcdtLOTrAB1fNjBLOwDdKzCyndlFLAdL0udR4S1szTC/q5pnFhG3Kdspsj5jvwLY1A==", 0x79F070 }, // U2019.4.31 non-dev
};
[UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate void FindAndLoadUnityPlugin(IntPtr name, out IntPtr loadedModule, byte bEnableSomeDebug);
private readonly ShaderFilterApi _filterApi;
public TrueShaderAntiCrash()
{
string unityPlayerHash;
{
using var sha = SHA512.Create();
using var unityPlayerStream = File.OpenRead("UnityPlayer.dll");
unityPlayerHash = Convert.ToBase64String(sha.ComputeHash(unityPlayerStream));
}
if (!OurOffsets.TryGetValue(unityPlayerHash, out var offset))
{
ReLogger.Error($"Unknown UnityPlayer hash: {unityPlayerHash}");
ReLogger.Error("The mod will not work");
return;
}
var pluginsPath = MelonUtils.GetGameDataDirectory() + "/Plugins";
var deeperPluginsPath = Path.Combine(pluginsPath, "x86_64");
if (Directory.Exists(deeperPluginsPath))
{
pluginsPath = deeperPluginsPath;
}
const string dllName = ShaderFilterApi.DllName + ".dll";
try
{
using var resourceStream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(typeof(TrueShaderAntiCrash), dllName);
using var fileStream = File.Open(pluginsPath + "/" + dllName, FileMode.Create, FileAccess.Write);
resourceStream.CopyTo(fileStream);
}
catch (IOException ex)
{
ReLogger.Warning("Failed to write native unity plugin; will attempt loading it anyway. This is normal if you're running multiple instances of VRChat");
MelonDebug.Msg(ex.ToString());
}
var process = Process.GetCurrentProcess();
foreach (ProcessModule module in process.Modules)
{
if (!module.FileName.Contains("UnityPlayer"))
continue;
var loadLibraryAddress = module.BaseAddress + offset;
var findAndLoadUnityPlugin = Marshal.GetDelegateForFunctionPointer<FindAndLoadUnityPlugin>(loadLibraryAddress);
var strPtr = Marshal.StringToHGlobalAnsi(ShaderFilterApi.DllName);
findAndLoadUnityPlugin(strPtr, out var loaded, 1);
if (loaded == IntPtr.Zero)
{
ReLogger.Error("Module load failed");
return;
}
_filterApi = new ShaderFilterApi(loaded);
Marshal.FreeHGlobal(strPtr);
break;
}
var category = MelonPreferences.CreateCategory("True Shader Anticrash");
var loopsEnabled = category.CreateEntry("LimitLoops", true, "Limit loops");
var geometryEnabled = category.CreateEntry("LimitGeometry", true, "Limit geometry shaders");
var tessEnabled = category.CreateEntry("LimitTesselation", true, "Limit tesselation");
IEnumerator WaitForRoomManagerAndUpdate()
{
while (RoomManager.field_Internal_Static_ApiWorldInstance_0 == null)
yield return null;
UpdateLimiters();
}
void UpdateLimiters()
{
var room = RoomManager.field_Internal_Static_ApiWorldInstance_0;
if (room == null)
{
MelonCoroutines.Start(WaitForRoomManagerAndUpdate());
return;
}
_filterApi.SetFilteringState(loopsEnabled.Value, geometryEnabled.Value, tessEnabled.Value);
}
loopsEnabled.OnValueChanged += (_, _) => UpdateLimiters();
geometryEnabled.OnValueChanged += (_, _) => UpdateLimiters();
tessEnabled.OnValueChanged += (_, _) => UpdateLimiters();
var maxLoopIterations = category.CreateEntry("MaxLoopIterations", 128, "Max loop iterations");
maxLoopIterations.OnValueChanged += (_, value) => _filterApi.SetLoopLimit(value);
var maxGeometry = category.CreateEntry("MaxGeometryOutputs", 60, "Max geometry shader outputs");
maxGeometry.OnValueChanged += (_, value) => _filterApi.SetLoopLimit(value);
var maxTess = category.CreateEntry("MaxTesselation", 5f, "Max tesselation power");
maxTess.OnValueChanged += (_, value) => _filterApi.SetMaxTesselationPower(value);
SceneManager.add_sceneLoaded(new Action<Scene, LoadSceneMode>((sc, _) =>
{
if (sc.buildIndex == -1)
UpdateLimiters();
}));
SceneManager.add_sceneUnloaded(new Action<Scene>(_ =>
{
_filterApi.SetFilteringState(false, false, false);
}));
UpdateLimiters();
_filterApi.SetMaxTesselationPower(maxTess.Value);
_filterApi.SetLoopLimit(maxLoopIterations.Value);
_filterApi.SetGeometryLimit(maxGeometry.Value);
}
}
}