Last active
January 18, 2020 07:01
-
-
Save mukaschultze/748ca5e4e017a8c249ffb4d49f795205 to your computer and use it in GitHub Desktop.
AssetStoreToolsCallbacks (self contained)
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
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Reflection; | |
using UnityEditor; | |
public class AssetStoreToolsCallbacks { | |
public static Action<object, string> afterAssetsUploaded = (sender, err) => { }; | |
public static Action<object, string> afterAssetsBundleUploaded = (sender, err) => { }; | |
public static Action<object, string> beforePackageExport = (sender, packagePath) => { }; | |
public static Action<object, string> afterPackageExport = (sender, packagePath) => { }; | |
public static Action<object> beforeUpload = (sender) => { }; | |
public static Action<object> afterUpload = (sender) => { }; | |
public static Action<object> beforeBundleUpload = (sender) => { }; | |
public static Action<object> afterBundleUpload = (sender) => { }; | |
public static Action<object, double, double> onUploading = (sender, pctUp, pctDown) => { }; | |
public static Action<object> uploadSucessfull = (sender) => { }; | |
public static Action<object> uploadFailed = (sender) => { }; | |
// [InitializeOnLoadMethod] | |
// private static void AddLogs() { | |
// afterAssetsUploaded += (sender, err) => UnityEngine.Debug.LogFormat("afterAssetsUploaded: {0}", err); | |
// afterAssetsBundleUploaded += (sender, err) => UnityEngine.Debug.LogFormat("afterAssetsBundleUploaded: {0}", err); | |
// beforePackageExport += (sender, packagePath) => UnityEngine.Debug.LogFormat("beforePackageExport: {0}", packagePath); | |
// afterPackageExport += (sender, packagePath) => UnityEngine.Debug.LogFormat("afterPackageExport: {0}", packagePath); | |
// beforeUpload += (sender) => UnityEngine.Debug.Log("beforeUpload"); | |
// afterUpload += (sender) => UnityEngine.Debug.Log("afterUpload"); | |
// beforeBundleUpload += (sender) => UnityEngine.Debug.Log("beforeBundleUpload"); | |
// afterBundleUpload += (sender) => UnityEngine.Debug.Log("afterBundleUpload"); | |
// onUploading += (sender, pctUp, pctDown) => UnityEngine.Debug.LogFormat("onUploading {0:00.00} {1:00.00}", pctUp, pctDown); | |
// uploadSucessfull += (sender) => UnityEngine.Debug.Log("uploadSucessfull"); | |
// uploadFailed += (sender) => UnityEngine.Debug.Log("uploadFailed"); | |
// } | |
public const BindingFlags FULL_BINDING = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; | |
private static Assembly[] cachedAssemblies; | |
private static readonly Dictionary<string, Patcher> patchers = new Dictionary<string, Patcher>(); | |
public static Type FindClass(string name) { | |
var result = FindTypeInAssembly(name, typeof(Editor).Assembly); | |
if (result != null) | |
return result; | |
if (cachedAssemblies == null) | |
cachedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); | |
for (var i = 0; i < cachedAssemblies.Length; i++) { | |
result = FindTypeInAssembly(name, cachedAssemblies[i]); | |
if (result != null) | |
return result; | |
} | |
return result; | |
} | |
private static Type FindTypeInAssembly(string name, Assembly assembly) { | |
return assembly == null ? | |
null : | |
assembly.GetType(name, false, true); | |
} | |
public static MethodInfo FindMethod(Type type, string methodName) { | |
return type.GetMethod(methodName, FULL_BINDING); | |
} | |
static AssetStoreToolsCallbacks() { | |
var AssetStorePackageController = FindClass("AssetStorePackageController"); | |
var methods = new [] { | |
"OnAssetsUploaded", | |
"OnUploadAssetBundlesFinished", | |
"Export", | |
"Upload", | |
"UploadAssetBundles", | |
"OnAssetsUploading", | |
"OnUploadSuccessfull", | |
"OnSubmitionFail", | |
}; | |
foreach (var method in methods) { | |
patchers[method] = new Patcher( | |
FindMethod(AssetStorePackageController, method), | |
FindMethod(typeof(AssetStoreToolsCallbacks), method) | |
); | |
patchers[method].SwapMethods(); | |
} | |
} | |
private static Patcher GetCurrentPatcher() { | |
var frame = new StackFrame(1, true); | |
var methodName = frame.GetMethod().Name; | |
return patchers[methodName]; | |
} | |
private void OnAssetsUploaded(string errorMessage) { | |
GetCurrentPatcher().InvokeOriginal(this, errorMessage); | |
afterAssetsUploaded(this, errorMessage); | |
} | |
private void OnUploadAssetBundlesFinished(string errorMessage) { | |
GetCurrentPatcher().InvokeOriginal(this, errorMessage); | |
afterAssetsBundleUploaded(this, errorMessage); | |
} | |
private void Export(string packagePath) { | |
beforePackageExport(this, packagePath); | |
GetCurrentPatcher().InvokeOriginal(this, packagePath); | |
afterPackageExport(this, packagePath); | |
} | |
private void Upload() { | |
beforeUpload(this); | |
GetCurrentPatcher().InvokeOriginal(this, null); | |
afterUpload(this); | |
} | |
private void UploadAssetBundles() { | |
beforeBundleUpload(this); | |
GetCurrentPatcher().InvokeOriginal(this, null); | |
afterBundleUpload(this); | |
} | |
private void OnAssetsUploading(double pctUp, double pctDown) { | |
onUploading(this, pctUp, pctDown); | |
GetCurrentPatcher().InvokeOriginal(this, pctUp, pctDown); | |
} | |
private void OnUploadSuccessfull() { | |
GetCurrentPatcher().InvokeOriginal(this, null); | |
uploadSucessfull(this); | |
} | |
private void OnSubmitionFail() { | |
GetCurrentPatcher().InvokeOriginal(this, null); | |
uploadFailed(this); | |
} | |
} |
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
using System; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
// error CS0702: A constraint cannot be special class `System.Delegate' | |
// Unity 2018.3.11f1 - .NET 3.5 equivalent | |
// public class Patcher<T> : Patcher where T : Delegate { | |
// public Patcher(T method, T replacement) : base(method.GetMethodInfo(), replacement.GetMethodInfo()) { } | |
// public Patcher(MethodBase method, T replacement) : base(method, replacement.GetMethodInfo()) { } | |
// } | |
public unsafe class Patcher { | |
private bool swapped; | |
private MethodBase method; | |
private MethodInfo replacement; | |
private byte[] backup = new byte[25]; | |
private IntPtr pBody; | |
private IntPtr pBorrowed; | |
public Patcher(MethodBase method, MethodInfo replacement) { | |
this.method = method; | |
this.replacement = replacement; | |
} | |
public Patcher(MethodBase method, Delegate replacement) { | |
this.method = method; | |
this.replacement = replacement.GetMethodInfo(); | |
} | |
public bool IsPatched() { | |
// var cursor = (byte * )pBody.ToPointer(); | |
// var isOriginal = backup.All(b => * (cursor++) == b); | |
return swapped; | |
} | |
public void Revert() { | |
if (!swapped) { | |
throw new Exception("Methods is not patched"); | |
} | |
swapped = false; | |
unsafe { | |
var cursor = (byte * )pBody.ToPointer(); | |
for (var i = 0; i < backup.Length; i++) { | |
*(cursor++) = backup[i]; | |
} | |
} | |
} | |
public void InvokeOriginal(object obj, params object[] parameters) { | |
try { | |
if (IsPatched()) | |
Revert(); | |
method.Invoke(obj, parameters); | |
} finally { | |
if (!IsPatched()) | |
SwapMethods(); | |
} | |
} | |
public void SwapMethods() { | |
if (swapped) { | |
throw new Exception("Methods already patched"); | |
} | |
swapped = true; | |
RuntimeHelpers.PrepareMethod(method.MethodHandle); | |
RuntimeHelpers.PrepareMethod(replacement.MethodHandle); | |
pBody = method.MethodHandle.GetFunctionPointer(); | |
pBorrowed = replacement.MethodHandle.GetFunctionPointer(); | |
unsafe { | |
var ptr = (byte * )pBody.ToPointer(); | |
var ptr2 = (byte * )pBorrowed.ToPointer(); | |
var ptrDiff = ptr2 - ptr - 5; | |
// Backup orignal opcodes so we can revert it later | |
for (var i = 0; i < backup.Length; i++) { | |
backup[i] = * (ptr + i); | |
} | |
if (ptrDiff < (long)0xFFFFFFFF && ptrDiff > (long) - 0xFFFFFFFF) { | |
// 32-bit relative jump, available on both 32 and 64 bit arch. | |
// Debug.Trace($"diff is {ptrDiff} doing relative jmp"); | |
// Debug.Trace("patching on {0:X}, target: {1:X}", (ulong)ptr, (ulong)ptr2); | |
* ptr = 0xE9; // JMP | |
*((uint * )(ptr + 1)) = (uint)ptrDiff; | |
} else { | |
// Debug.Trace($"diff is {ptrDiff} doing push+ret trampoline"); | |
// Debug.Trace("patching on {0:X}, target: {1:X}", (ulong)ptr, (ulong)ptr2); | |
if (sizeof(IntPtr) == 8) { | |
// For 64bit arch and likely 64bit pointers, do: | |
// PUSH bits 0 - 32 of addr | |
// MOV [RSP+4] bits 32 - 64 of addr | |
// RET | |
var cursor = ptr; | |
*(cursor++) = 0x68; // PUSH | |
*((uint * )cursor) = (uint)ptr2; | |
cursor += 4; | |
*(cursor++) = 0xC7; // MOV [RSP+4] | |
*(cursor++) = 0x44; | |
*(cursor++) = 0x24; | |
*(cursor++) = 0x04; | |
*((uint * )cursor) = (uint)((ulong)ptr2 >> 32); | |
cursor += 4; | |
*(cursor++) = 0xC3; // RET | |
} else { | |
// For 32bit arch and 32bit pointers, do: PUSH addr, RET. | |
* ptr = 0x68; | |
*((uint * )(ptr + 1)) = (uint)ptr2; | |
*(ptr + 5) = 0xC3; | |
} | |
} | |
// Logger.Debug("Patched 0x{0:X} to 0x{1:X}.", (ulong)ptr, (ulong)ptr2); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment