mirror of
https://github.com/muskit/H3VR-TNH-Quality-of-Life-Improvements.git
synced 2026-06-03 04:34:26 -07:00
update MeatKit (9a1a68ab68cd0650227af944ffa30d1166b9e056)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This code is from BepInEx's NStrip library, licensed under MIT
|
||||
* https://github.com/BepInEx/NStrip/blob/f1e9887b3eb77c0e02acb5919b5e26a6e7c2c342/NStrip/AssemblyStripper.cs
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NStrip
|
||||
{
|
||||
public static class AssemblyStripper
|
||||
{
|
||||
static IEnumerable<TypeDefinition> GetAllTypeDefinitions(AssemblyDefinition assembly)
|
||||
{
|
||||
var typeQueue = new Queue<TypeDefinition>(assembly.MainModule.Types);
|
||||
|
||||
while (typeQueue.Count > 0)
|
||||
{
|
||||
var type = typeQueue.Dequeue();
|
||||
|
||||
yield return type;
|
||||
|
||||
foreach (var nestedType in type.NestedTypes)
|
||||
typeQueue.Enqueue(nestedType);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CheckCompilerGeneratedAttribute(IMemberDefinition member)
|
||||
{
|
||||
return member.CustomAttributes.Any(x =>
|
||||
x.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute");
|
||||
}
|
||||
|
||||
public static void MakePublic(AssemblyDefinition assembly, IList<string> typeNameBlacklist, bool includeCompilerGenerated, bool excludeCgEvents)
|
||||
{
|
||||
|
||||
foreach (var type in GetAllTypeDefinitions(assembly))
|
||||
{
|
||||
if (typeNameBlacklist.Contains(type.Name))
|
||||
continue;
|
||||
|
||||
if (!includeCompilerGenerated && CheckCompilerGeneratedAttribute(type))
|
||||
continue;
|
||||
|
||||
if (type.IsNested)
|
||||
type.IsNestedPublic = true;
|
||||
else
|
||||
type.IsPublic = true;
|
||||
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
if (!includeCompilerGenerated &&
|
||||
(CheckCompilerGeneratedAttribute(method) || method.IsCompilerControlled))
|
||||
continue;
|
||||
|
||||
method.IsPublic = true;
|
||||
}
|
||||
|
||||
foreach (var field in type.Fields)
|
||||
{
|
||||
if (!includeCompilerGenerated &&
|
||||
(CheckCompilerGeneratedAttribute(field) || field.IsCompilerControlled))
|
||||
continue;
|
||||
|
||||
if (includeCompilerGenerated && excludeCgEvents)
|
||||
{
|
||||
if (type.Events.Any(x => x.Name == field.Name))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field.IsPublic) continue;
|
||||
|
||||
field.IsPublic = true;
|
||||
var attributes = field.CustomAttributes;
|
||||
CustomAttribute isExplicitlySerialized = attributes.FirstOrDefault(x => x.AttributeType.Name == "SerializeField");
|
||||
|
||||
if (isExplicitlySerialized == null)
|
||||
{
|
||||
var nonSerializedAttributeCtor = typeof(NonSerializedAttribute).GetConstructor(Type.EmptyTypes);
|
||||
var nonSerializedAttributeRef = assembly.MainModule.ImportReference(nonSerializedAttributeCtor);
|
||||
attributes.Add(new CustomAttribute(nonSerializedAttributeRef));
|
||||
|
||||
var hideInInspectorCtor = typeof(MeatKit.HideInNormalInspectorAttribute).GetConstructor(Type.EmptyTypes);
|
||||
var hideInInspectorRef = assembly.MainModule.ImportReference(hideInInspectorCtor);
|
||||
attributes.Add(new CustomAttribute(hideInInspectorRef));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c62b8c488f64d298e5bc464fdee9f17
|
||||
timeCreated: 1646326380
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public class NativeHookFunctionOffsets
|
||||
{
|
||||
public long MonoScriptTransferWrite { get; set; }
|
||||
public long MonoScriptTransferRead { get; set; }
|
||||
public long ShutdownManaged { get; set; }
|
||||
public long StringAssign { get; set; }
|
||||
}
|
||||
|
||||
public class EditorVersion
|
||||
{
|
||||
public NativeHookFunctionOffsets FunctionOffsets { get; set; }
|
||||
|
||||
private static bool _hasShownPopup = false;
|
||||
|
||||
public static bool IsSupportedVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
bool supported = SupportedVersions.ContainsKey(Application.unityVersion);
|
||||
|
||||
if (!supported && !_hasShownPopup)
|
||||
{
|
||||
// Show the warning popup about the wrong version if is hasn't come up already.
|
||||
string validVersion = string.Join(", ", SupportedVersions.Keys.ToArray());
|
||||
EditorUtility.DisplayDialog("Wrong editor version",
|
||||
"You are using Unity version " + Application.unityVersion + ", MeatKit requires one of the following: " + validVersion,
|
||||
"I'll go install that.");
|
||||
_hasShownPopup = true;
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
}
|
||||
|
||||
public static EditorVersion Current
|
||||
{
|
||||
get
|
||||
{
|
||||
EditorVersion currentVersion;
|
||||
if (SupportedVersions.TryGetValue(Application.unityVersion, out currentVersion))
|
||||
return currentVersion;
|
||||
throw new NotSupportedException("The current editor version is not in the list of supported versions.");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, EditorVersion> SupportedVersions = new Dictionary<string, EditorVersion>()
|
||||
{
|
||||
{
|
||||
"5.6.3p4", new EditorVersion
|
||||
{
|
||||
FunctionOffsets = new NativeHookFunctionOffsets
|
||||
{
|
||||
MonoScriptTransferWrite = 0xE321E0,
|
||||
MonoScriptTransferRead = 0xE34000,
|
||||
ShutdownManaged = 0x17542D0,
|
||||
StringAssign = 0x1480
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"5.6.7f1", new EditorVersion
|
||||
{
|
||||
FunctionOffsets = new NativeHookFunctionOffsets
|
||||
{
|
||||
MonoScriptTransferWrite = 0xE39BF0,
|
||||
MonoScriptTransferRead = 0xE3BA10,
|
||||
ShutdownManaged = 0x175D2C0,
|
||||
StringAssign = 0x1480
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6222c8b0cb74878820d6fe4c55aae88
|
||||
timeCreated: 1684366513
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static Type[] GetTypesSafe(this Assembly assembly)
|
||||
{
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
return e.Types.Where(t => t != null).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static string ByteArrayToString(byte[] ba)
|
||||
{
|
||||
var hex = new StringBuilder(ba.Length * 2);
|
||||
foreach (var b in ba)
|
||||
hex.AppendFormat("{0:x2}", b);
|
||||
return hex.ToString();
|
||||
}
|
||||
|
||||
// Modified version of http://answers.unity.com/answers/1425776/view.html
|
||||
public static T[] GetAllInstances<T>() where T : ScriptableObject
|
||||
{
|
||||
return AssetDatabase.FindAssets("t:" + typeof(T).FullName)
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.Select(AssetDatabase.LoadAssetAtPath<T>)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static Object[] GetAllInstances(Type t)
|
||||
{
|
||||
return AssetDatabase.FindAssets("t:" + t.FullName)
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.Select(p => AssetDatabase.LoadAssetAtPath(p, t))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/25223884
|
||||
public static string MakeValidFileName(string text, char? replacement = '_')
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return "";
|
||||
|
||||
var invalids = Path.GetInvalidFileNameChars();
|
||||
var sb = new StringBuilder(text.Length);
|
||||
var changed = false;
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
if (invalids.Contains(c))
|
||||
{
|
||||
changed = true;
|
||||
var repl = replacement ?? '\0';
|
||||
if (repl != '\0')
|
||||
sb.Append(repl);
|
||||
}
|
||||
else
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
if (sb.Length == 0)
|
||||
return "_";
|
||||
return changed ? sb.ToString() : text;
|
||||
}
|
||||
|
||||
// https://answers.unity.com/questions/150942/texture-scale.html
|
||||
public static Texture2D ScaleTexture(this Texture2D source, int targetWidth, int targetHeight)
|
||||
{
|
||||
Texture2D result = new Texture2D(targetWidth, targetHeight, TextureFormat.ARGB32, false);
|
||||
for (int i = 0; i < result.height; ++i)
|
||||
{
|
||||
for (int j = 0; j < result.width; ++j)
|
||||
{
|
||||
Color newColor = source.GetPixelBilinear(j / (float) result.width, i / (float) result.height);
|
||||
result.SetPixel(j, i, newColor);
|
||||
}
|
||||
}
|
||||
|
||||
result.Apply();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) {
|
||||
foreach (DirectoryInfo dir in source.GetDirectories())
|
||||
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
|
||||
foreach (FileInfo file in source.GetFiles())
|
||||
file.CopyTo(Path.Combine(target.FullName, file.Name));
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(string source, string target)
|
||||
{
|
||||
CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target));
|
||||
}
|
||||
|
||||
#region TimeSpan formatting https://stackoverflow.com/a/21649465/8809017
|
||||
|
||||
public static string GetReadableTimespan(this TimeSpan ts)
|
||||
{
|
||||
// formats and its cutoffs based on totalseconds
|
||||
var cutoff = new SortedList<long, string>
|
||||
{
|
||||
{59, "{3:S}"},
|
||||
{60, "{2:M}"},
|
||||
{60 * 60 - 1, "{2:M}, {3:S}"},
|
||||
{60 * 60, "{1:H}"},
|
||||
{24 * 60 * 60 - 1, "{1:H}, {2:M}"},
|
||||
{24 * 60 * 60, "{0:D}"},
|
||||
{long.MaxValue, "{0:D}, {1:H}"}
|
||||
};
|
||||
|
||||
// find nearest best match
|
||||
var find = cutoff.Keys.ToList()
|
||||
.BinarySearch((long) ts.TotalSeconds);
|
||||
// negative values indicate a nearest match
|
||||
var near = find < 0 ? Math.Abs(find) - 1 : find;
|
||||
// use custom formatter to get the string
|
||||
return String.Format(
|
||||
new HMSFormatter(),
|
||||
cutoff[cutoff.Keys[near]],
|
||||
ts.Days,
|
||||
ts.Hours,
|
||||
ts.Minutes,
|
||||
ts.Seconds);
|
||||
}
|
||||
|
||||
// formatter for forms of
|
||||
// seconds/hours/day
|
||||
private class HMSFormatter : ICustomFormatter, IFormatProvider
|
||||
{
|
||||
// list of Formats, with a P customformat for pluralization
|
||||
static Dictionary<string, string> timeformats = new Dictionary<string, string>
|
||||
{
|
||||
{"S", "{0:P:s:s}"},
|
||||
{"M", "{0:P:m:m}"},
|
||||
{"H", "{0:P:h:h}"},
|
||||
{"D", "{0:P:d:d}"}
|
||||
};
|
||||
|
||||
public string Format(string format, object arg, IFormatProvider formatProvider)
|
||||
{
|
||||
return String.Format(new PluralFormatter(), timeformats[format], arg);
|
||||
}
|
||||
|
||||
public object GetFormat(Type formatType)
|
||||
{
|
||||
return formatType == typeof(ICustomFormatter) ? this : null;
|
||||
}
|
||||
}
|
||||
|
||||
// formats a numeric value based on a format P:Plural:Singular
|
||||
private class PluralFormatter : ICustomFormatter, IFormatProvider
|
||||
{
|
||||
public string Format(string format, object arg, IFormatProvider formatProvider)
|
||||
{
|
||||
if (arg != null)
|
||||
{
|
||||
var parts = format.Split(':'); // ["P", "Plural", "Singular"]
|
||||
|
||||
if (parts[0] == "P") // correct format?
|
||||
{
|
||||
// which index postion to use
|
||||
int partIndex = (arg.ToString() == "1") ? 2 : 1;
|
||||
// pick string (safe guard for array bounds) and format
|
||||
return String.Format("{0}{1}", arg, (parts.Length > partIndex ? parts[partIndex] : ""));
|
||||
}
|
||||
}
|
||||
|
||||
return String.Format(format, arg);
|
||||
}
|
||||
|
||||
public object GetFormat(Type formatType)
|
||||
{
|
||||
return formatType == typeof(ICustomFormatter) ? this : null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d928d818e3c24bce8c1c938e971da916
|
||||
timeCreated: 1628577239
|
||||
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(HideInNormalInspectorAttribute))]
|
||||
class HideInNormalInspectorDrawer : PropertyDrawer
|
||||
{
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c4b5d56cc4522845a6ca6bb3d6d76f0
|
||||
timeCreated: 1667603705
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using MonoMod.RuntimeDetour;
|
||||
using MonoMod.Utils;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
/// <summary>
|
||||
/// This class helps manage detours into the native code of the Editor.
|
||||
/// Any detours into native code should be registered using this as it will automatically dispose of them
|
||||
/// right before the Editor reloads the mono domain, preventing editor crashes.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public static class NativeHookManager
|
||||
{
|
||||
// Actual name: ShutdownPlatformSupportModulesInManaged(void)
|
||||
private delegate void ShutdownManaged();
|
||||
|
||||
private static readonly ShutdownManaged OrigShutdownManaged;
|
||||
|
||||
// Keep track of all the applied detours so we can quickly undo them before the mono domain is reloaded
|
||||
private static readonly List<NativeDetour> Detours = new List<NativeDetour>();
|
||||
|
||||
static NativeHookManager()
|
||||
{
|
||||
if (!EditorVersion.IsSupportedVersion) return;
|
||||
|
||||
// Apply our detours here and save the trampoline to call the original function
|
||||
OrigShutdownManaged = ApplyEditorDetour<ShutdownManaged>(EditorVersion.Current.FunctionOffsets.ShutdownManaged, new ShutdownManaged(OnShutdownManaged));
|
||||
}
|
||||
|
||||
public static T ApplyEditorDetour<T>(long from, Delegate to) where T : class
|
||||
{
|
||||
// Avoid crashing the editor if we're loaded in the wrong Unity version
|
||||
if (!EditorVersion.IsSupportedVersion) return null;
|
||||
|
||||
// Get the base address of the Unity module and the address in memory of the function
|
||||
IntPtr editorBase = DynDll.OpenLibrary("Unity.exe");
|
||||
IntPtr fromPtr = (IntPtr)(editorBase.ToInt64() + from);
|
||||
|
||||
// Get a function pointer for the managed callback
|
||||
var toPtr = Marshal.GetFunctionPointerForDelegate(to);
|
||||
|
||||
// Make a detour and add it to the list
|
||||
var detour = new NativeDetour(fromPtr, toPtr, new NativeDetourConfig { ManualApply = true });
|
||||
Detours.Add(detour);
|
||||
|
||||
// Apply the detour and generate a trampoline for it, which we return
|
||||
var original = detour.GenerateTrampoline(to.GetType().GetMethod("Invoke")).CreateDelegate(typeof(T)) as T;
|
||||
detour.Apply();
|
||||
return original;
|
||||
}
|
||||
|
||||
public static Delegate GetDelegateForFunctionPointer<T>(long from)
|
||||
{
|
||||
// Avoid crashing the editor if we're loaded in the wrong Unity version
|
||||
if (!EditorVersion.IsSupportedVersion) return null;
|
||||
|
||||
// Get the base address for the Unity module and apply the offset
|
||||
IntPtr editorBase = DynDll.OpenLibrary("Unity.exe");
|
||||
return Marshal.GetDelegateForFunctionPointer((IntPtr)(editorBase.ToInt64() + from), typeof(T));
|
||||
}
|
||||
|
||||
private static void OnShutdownManaged()
|
||||
{
|
||||
// Unity is about to shutdown the mono runtime! Quickly dispose of our detours!
|
||||
OrigShutdownManaged();
|
||||
foreach (var detour in Detours) detour.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a51d43ba1fe4baf8e3b0a90d7f8ec88
|
||||
timeCreated: 1654485214
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static class SteamAppLocator
|
||||
{
|
||||
private const int AppId = 450540;
|
||||
private const string AppFolderName = "H3VR";
|
||||
|
||||
public static string LocateGame()
|
||||
{
|
||||
// Get the main steam installation location via registry.
|
||||
var steamDir = (
|
||||
Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", null) ??
|
||||
Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Valve\Steam", "InstallPath", null))
|
||||
as string;
|
||||
|
||||
// If we can't find it, return. This should really only happen if Steam isn't installed.
|
||||
if (string.IsNullOrEmpty(steamDir)) return null;
|
||||
|
||||
// Check main steamapps library folder for h3 manifest.
|
||||
var manifestFile = @"steamapps\appmanifest_" + AppId + ".acf";
|
||||
var gameFolder = @"steamapps\common\" + AppFolderName + @"\";
|
||||
if (File.Exists(Path.Combine(steamDir, manifestFile)))
|
||||
{
|
||||
return Path.Combine(steamDir, gameFolder);
|
||||
}
|
||||
|
||||
// We didn't find it, look at other library folders by lazily parsing libraryfolders.
|
||||
var libraryFolders = Path.Combine(steamDir, @"steamapps\libraryfolders.vdf");
|
||||
foreach (Match match in Regex.Matches(File.ReadAllText(libraryFolders), @"^\s+\""path\""\s+\""(.+)\""$",
|
||||
RegexOptions.Multiline))
|
||||
{
|
||||
var folder = match.Groups[1].Value;
|
||||
if (!File.Exists(Path.Combine(folder, manifestFile))) continue;
|
||||
return Path.Combine(folder, gameFolder);
|
||||
}
|
||||
|
||||
// Nope. Still can't find it.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c50a485a68d43caa9c30a864c2d8b25
|
||||
timeCreated: 1655693881
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static class UnityNativeHelper
|
||||
{
|
||||
private delegate IntPtr StringAssignType(IntPtr ptr, string str, ulong len, IntPtr nul);
|
||||
|
||||
private static readonly StringAssignType AssignNativeString;
|
||||
|
||||
static UnityNativeHelper()
|
||||
{
|
||||
if (!EditorVersion.IsSupportedVersion) return;
|
||||
|
||||
AssignNativeString = (StringAssignType) NativeHookManager.GetDelegateForFunctionPointer<StringAssignType>(EditorVersion.Current.FunctionOffsets.StringAssign);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a native string structure from unmanaged memory
|
||||
/// </summary>
|
||||
/// <param name="ptr">The pointer to the structure</param>
|
||||
/// <param name="ofs">Offset to the pointer</param>
|
||||
/// <returns>A copy of the structure in managed memory</returns>
|
||||
public static string ReadNativeString(IntPtr ptr, int ofs)
|
||||
{
|
||||
// Apply the offset
|
||||
var real = (IntPtr) (ptr.ToInt64() + ofs);
|
||||
|
||||
// Get the pointer to the string in memory
|
||||
var stringPointer = Marshal.ReadIntPtr(real);
|
||||
if (stringPointer == IntPtr.Zero)
|
||||
{
|
||||
// If the pointer is null, that means it's stored in the struct directly.
|
||||
// In this format, the string is 16 chars or less.
|
||||
var length = Marshal.ReadInt64(real, 24);
|
||||
return Marshal.PtrToStringAnsi((IntPtr) (real.ToInt64() + 8), (int) length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it isn't null, we can just go out into that memory location and read it.
|
||||
var length = Marshal.ReadInt64(real, 24);
|
||||
return Marshal.PtrToStringAnsi(stringPointer, (int) length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a managed string to the unmanaged memory location of a string structure
|
||||
/// </summary>
|
||||
/// <param name="ptr">The pointer to the structure in memory</param>
|
||||
/// <param name="ofs">Offset to the pointer</param>
|
||||
/// <param name="str">The string to write</param>
|
||||
public static void WriteNativeString(IntPtr ptr, int ofs, string str)
|
||||
{
|
||||
// Apply the offset and call the assign function of native Unity code
|
||||
var real = (IntPtr) (ptr.ToInt64() + ofs);
|
||||
AssignNativeString(real, str, (ulong) str.Length, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba90027061ca46f88e2e0cf4fefc0224
|
||||
timeCreated: 1655406646
|
||||
Reference in New Issue
Block a user