Created
July 3, 2020 12:14
-
-
Save Slaynash/b75bbc7de6110a575ee472233ba039f7 to your computer and use it in GitHub Desktop.
Custom Unity render loop, drawing a magenta quad at 0,0 of 400x400px (hardcoded offsets for Unity 2018.4.23f1)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private delegate void PerformMainLoop(/* HWND__* */ IntPtr param_1, uint param_2, uint param_3, ulong param_4); // These parameters may be inaccurate | |
private delegate GfxDevice GetGfxDevice(); | |
private delegate IntPtr GetIVRDevice(); | |
private delegate void SetupPixelCorrectCoordinates(bool _0); | |
private static IntPtr GetFunctionPointerFromMethod(string methodName) => | |
typeof(CustomUnityRender).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); | |
internal static void Init() | |
{ | |
unityPlayerModule = Externs.GetModuleHandle("UnityPlayer.dll"); | |
string unityVersion = MelonLoader.Main.UnityVersion; | |
IntPtr performMainLoop = | |
unityVersion.StartsWith("2017") | |
? CppUtils.Sigscan(unityPlayerModule, "48 83 ec 28 80 ?? ?? ?? ?? ?? 00 0f 85 ?? ?? ?? ?? e8 ?? ?? ?? ?? 80 b8 4d 01 00 00 00 74 ?? 80") | |
: unityVersion.StartsWith("2018") | |
? CppUtils.Sigscan(unityPlayerModule, "48 83 ec 28 80 ?? ?? ?? ?? ?? 00 0f 85 ?? ?? ?? ?? 48 89 5c 24 30 e8 ?? ?? ?? ?? 80 b8 4d 01 00 00 00 74 ?? e8") | |
: CppUtils.Sigscan(unityPlayerModule, "48 83 ec 28 80 ?? ?? ?? ?? ?? 00 0f 85 ?? ?? ?? ?? 48 89 5c 24 30 e8 ?? ?? ?? ?? 80 b8 55 01 00 00 00 74 ?? e8"); | |
CreateDetourHook("PerformMainLoopDetour", ref originalPerformMainLoop, performMainLoop); | |
IntPtr getIVRDevicePtr = CppUtils.ResolvePtrOffset(drawSplashScreenPtr.Add(0xd), drawSplashScreenPtr.Add(0x11)); | |
getIVRDevice = (GetIVRDevice)Marshal.GetDelegateForFunctionPointer(getIVRDevicePtr, typeof(GetIVRDevice)); | |
IntPtr drawsplashscreen_postIvrCheckPtr = drawSplashScreenPtr.Add(0x16).Add(drawSplashScreenPtr.Add(0x15).GetByte()); | |
IntPtr getGfxDevicePtr = CppUtils.ResolvePtrOffset(drawsplashscreen_postIvrCheckPtr.Add(0x16), drawsplashscreen_postIvrCheckPtr.Add(0x1a)); | |
getGfxDevice = (GetGfxDevice)Marshal.GetDelegateForFunctionPointer(getGfxDevicePtr, typeof(GetGfxDevice)); | |
setupPixelCorrectCoordinates = (SetupPixelCorrectCoordinates)Marshal.GetDelegateForFunctionPointer(unityPlayerModule.Add(0x69f940), typeof(SetupPixelCorrectCoordinates)); | |
} | |
// These parameters may be inaccurate | |
private static unsafe void PerformMainLoopDetour(/* HWND__* */ IntPtr window, uint param_2, uint param_3, ulong param_4) | |
{ | |
//MelonModLogger.Log("PerformMainLoop called"); | |
if (inPerformMainLoop) // Invalid call | |
return; | |
inPerformMainLoop = true; | |
// So uh yeah, we do some test here, and uhhh, we may break everything but whatever. Let`s do this! | |
Externs.NativeMessage lpMsg; | |
Externs.PeekMessage(out lpMsg, IntPtr.Zero, 0, 0, 0); | |
IntPtr eventHandle = Externs.CreateEvent(IntPtr.Zero, false, false, null); | |
Externs.timeBeginPeriod(1); | |
while (lpMsg.msg != 0x12) // WM_QUIT | |
{ | |
if (!Externs.PeekMessage(out lpMsg, IntPtr.Zero, 0, 0, 1)) // If there is no pending message | |
{ | |
//updateTimer(); | |
GfxDevice gfxDevice = getGfxDevice(); | |
bool currentlyRendering = gfxDevice.isCurrentlyRendering; | |
if (!currentlyRendering) | |
gfxDevice.BeginFrame(); | |
setupPixelCorrectCoordinates(false); // Set MVP matrices | |
Texture texture = new Texture(unityPlayerModule.Add(0x15acf60).GetValue()); // whiteTexture | |
DrawQuad(0, new UnityEngine.Rect(0, 0, 400, 400), texture, UnityEngine.Color.magenta, new UnityEngine.Rect(0, 0, 1, 1)); | |
if (!currentlyRendering) | |
gfxDevice.EndFrame(); | |
gfxDevice.SetInvertProjectionMatrix(false); | |
ScreenManager screenManager = ScreenManager.GetScreenManagerPtr(); | |
IntPtr shaderChannelMask = default; | |
screenManager.SetBlitMaterial((IntPtr)(&shaderChannelMask)); | |
gfxDevice.PresentFrame(shaderChannelMask); | |
Thread.Sleep(16); // ~60fps | |
} | |
else | |
{ | |
Externs.TranslateMessage(ref lpMsg); | |
Externs.DispatchMessage(ref lpMsg); | |
} | |
} | |
MelonModLogger.Log("QUIT"); | |
inPerformMainLoop = false; | |
return; | |
} | |
private static unsafe void DrawQuad(float depth, UnityEngine.Rect rect, Texture texture, UnityEngine.Color color, UnityEngine.Rect textureRect) | |
{ | |
//The splash material is the same as the UI material, so I guess we can use it for everything | |
Material splashUnityLogoMaterial = new Material(unityPlayerModule.Add(0x15acf38).GetValue()); | |
FastPropertyName _MainTexFPName = new FastPropertyName(unityPlayerModule.Add(0x15184e0)); | |
if (_MainTexFPName.ptr.GetUInt() == 0xffffffff) | |
_MainTexFPName.Init("_MainTex"); | |
SharedPassContext g_SharedPassContext = new SharedPassContext(unityPlayerModule.Add(0x15b87d8)); | |
splashUnityLogoMaterial.SetTexture(_MainTexFPName, texture); | |
IntPtr shaderChannelMask = default; | |
splashUnityLogoMaterial.SetPassSlow((IntPtr)(&shaderChannelMask), 0, g_SharedPassContext, 0, 0); | |
GfxDevice gfxDevice = getGfxDevice(); | |
gfxDevice.ImmediateBegin(2, shaderChannelMask); | |
gfxDevice.ImmediateColor(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, color.a * 0.5f); | |
float halfWidth = rect.width * 0.5f; | |
float halfHeight = rect.height * 0.5f; | |
float centerX = halfWidth + rect.x; | |
float centerY = halfHeight + rect.y; | |
float minX = centerX - halfWidth; | |
float minY = centerY - halfHeight; | |
float maxX = centerX + halfWidth; | |
float maxY = centerY + halfHeight; | |
gfxDevice.ImmediateTexCoordAll(textureRect.x, textureRect.y, 0); | |
gfxDevice.ImmediateVertex(minX, minY, depth); | |
gfxDevice.ImmediateTexCoordAll(textureRect.x, textureRect.y + textureRect.height, 0); | |
gfxDevice.ImmediateVertex(minX, maxY, depth); | |
gfxDevice.ImmediateTexCoordAll(textureRect.x + textureRect.width, textureRect.y + textureRect.height, 0); | |
gfxDevice.ImmediateVertex(maxX, maxY, depth); | |
gfxDevice.ImmediateTexCoordAll(textureRect.x + textureRect.width, textureRect.y, 0); | |
gfxDevice.ImmediateVertex(maxX, minY, depth); | |
gfxDevice.ImmediateEnd(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal struct FastPropertyName | |
{ | |
private delegate void Void_CharPtr_Function(IntPtr _this, string _1); | |
public IntPtr ptr; | |
public FastPropertyName(IntPtr ptr) | |
{ | |
this.ptr = ptr; | |
} | |
internal void Init(string name) | |
{ | |
((Void_CharPtr_Function)Marshal.GetDelegateForFunctionPointer(CustomUnityRender.unityPlayerModule.Add(0x8628f0), typeof(Void_CharPtr_Function))) | |
(ptr, name); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal struct GfxDevice | |
{ | |
private delegate void Void_Function(IntPtr _this); | |
private delegate void Void_Bool_Function(IntPtr _this, bool _0); | |
private delegate void Void_GfxPrimitiveType_VertexInputMasks_Function(IntPtr _this, int primitiveType, IntPtr vertexInputMasks); | |
private delegate void Void_Float_Float_Float_Function(IntPtr _this, float _1, float _2, float _3); | |
private delegate void Void_Float_Float_Float_Float_Function(IntPtr _this, float _1, float _2, float _3, float _4); | |
private delegate void PresentFrame_delegate(IntPtr _this, IntPtr shaderChannelMask); | |
public IntPtr ptr; | |
public bool isCurrentlyRendering => ptr.Add(0x21d4).GetBool(); | |
internal IntPtr GetPointerFromVTable(int offset) => | |
ptr.GetValue().Add(offset).GetValue(); // this->vtable(@0)->(@offset) | |
internal void SetInvertProjectionMatrix(bool _0) => | |
((Void_Bool_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x40), typeof(Void_Bool_Function))) | |
(ptr, _0); | |
internal void BeginFrame() => | |
((Void_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x508), typeof(Void_Function)))(ptr); | |
internal void EndFrame() => | |
((Void_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x510), typeof(Void_Function)))(ptr); | |
internal void PresentFrame(IntPtr shaderChannelMask) => | |
((PresentFrame_delegate)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x520), typeof(PresentFrame_delegate))) | |
(ptr, shaderChannelMask); | |
internal void ImmediateVertex(float x, float y, float z) => | |
((Void_Float_Float_Float_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x578), typeof(Void_Float_Float_Float_Function))) | |
(ptr, x, y, z); | |
internal void ImmediateColor(float a, float r, float g, float b) => | |
((Void_Float_Float_Float_Float_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x588), typeof(Void_Float_Float_Float_Float_Function))) | |
(ptr, a, r, g, b); | |
internal void ImmediateTexCoordAll(float u, float v, float w) => | |
((Void_Float_Float_Float_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x590), typeof(Void_Float_Float_Float_Function))) | |
(ptr, u, v, w); | |
internal void ImmediateBegin(int primitiveType, IntPtr vertexInputMasks) => | |
((Void_GfxPrimitiveType_VertexInputMasks_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x5a0), typeof(Void_GfxPrimitiveType_VertexInputMasks_Function))) | |
(ptr, primitiveType, vertexInputMasks); | |
internal void ImmediateEnd() => | |
((Void_Function)Marshal.GetDelegateForFunctionPointer(GetPointerFromVTable(0x5a8), typeof(Void_Function)))(ptr); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal struct Material | |
{ | |
private struct SetSlowPass_stack | |
{ | |
public int _0; | |
public byte _1; | |
} | |
private delegate void SetTexture_delegate(IntPtr _this, uint _1, Texture _2); | |
private delegate int SetPassSlow_delegate(IntPtr _this, IntPtr sharedPassContext, int _3, SharedPassContext _4, SetSlowPass_stack stack); | |
public IntPtr ptr; | |
public Material(IntPtr ptr) | |
{ | |
this.ptr = ptr; | |
} | |
public void SetTexture(FastPropertyName property, Texture texture) | |
{ | |
((SetTexture_delegate)Marshal.GetDelegateForFunctionPointer(CustomUnityRender.unityPlayerModule.Add(0x859710), typeof(SetTexture_delegate))) | |
(ptr, property.ptr.GetUInt(), texture); | |
} | |
internal int SetPassSlow(IntPtr _1, int _2, SharedPassContext sharedPassContext, int _4, byte _5) | |
{ | |
return ((SetPassSlow_delegate)Marshal.GetDelegateForFunctionPointer(CustomUnityRender.unityPlayerModule.Add(0x8592b0), typeof(SetPassSlow_delegate))) | |
(ptr, _1, _2, sharedPassContext, new SetSlowPass_stack { _0 = _4, _1 = _5 }); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal struct ScreenManager | |
{ | |
private delegate ScreenManager GetScreenManagerPtr_delegate(); | |
private delegate IntPtr SetBlitMaterial_delegate(ScreenManager _this, IntPtr sharedMaterialData); | |
public IntPtr ptr; | |
public static ScreenManager GetScreenManagerPtr() => | |
((GetScreenManagerPtr_delegate) Marshal.GetDelegateForFunctionPointer(CustomUnityRender.unityPlayerModule.Add(0x726d30), typeof(GetScreenManagerPtr_delegate)))(); | |
public IntPtr SetBlitMaterial(IntPtr sharedMaterialData) => | |
((SetBlitMaterial_delegate)Marshal.GetDelegateForFunctionPointer(CustomUnityRender.unityPlayerModule.Add(0x728a50), typeof(SetBlitMaterial_delegate))) | |
(this, sharedMaterialData); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal struct Texture | |
{ | |
public IntPtr ptr; | |
internal Texture(IntPtr ptr) | |
{ | |
this.ptr = ptr; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment