Browse Source

Reworked library loading so that the native DLL and its functions are now loaded dynamically using native LoadLibrary/GetProcAddress calls and can be freed again with FreeLibrary. This allows the library to be unloaded while in the Unity Editor, making iteration on native code easier, and it's a first step on the way to loading multiple Quake instances simultaneously.

console
Nico de Poel 5 years ago
parent
commit
b19df802c7
  1. 30
      Assets/Scripts/SystemLibrary.cs
  2. 11
      Assets/Scripts/SystemLibrary.cs.meta
  3. 77
      Assets/Scripts/UniQuake.cs

30
Assets/Scripts/SystemLibrary.cs

@ -0,0 +1,30 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
public static class SystemLibrary
{
#if UNITY_STANDALONE_WIN
[DllImport("kernel32", EntryPoint = "LoadLibrary", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibraryNative(string lpFileName);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetDllDirectory(string lpPathName);
public static IntPtr LoadLibrary(string lpFileName)
{
string lpPathName = Path.GetDirectoryName(lpFileName);
lpFileName = Path.GetFileName(lpFileName);
SetDllDirectory(lpPathName.Replace('/', '\\'));
return LoadLibraryNative(lpFileName);
}
[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
#endif
}

11
Assets/Scripts/SystemLibrary.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d583e6fe28a9299438ec95a379db0527
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

77
Assets/Scripts/UniQuake.cs

@ -1,12 +1,24 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
public class UniQuake: MonoBehaviour
{
private const string DllName = "uniquake.dll";
{
#if UNITY_EDITOR
private const string DllPath = "Plugins/windows/x86_64/uniquake.dll";
#elif UNITY_STANDALONE_WIN
#if UNITY_64
private const string DllPath = "Plugins/x86_64/uniquake.dll";
#else
private const string DllPath = "Plugins/x86/uniquake.dll";
#endif
#endif
private const int MemSize = 0x4000000; // 64 MB of heap space
private IntPtr libraryHandle;
private QuakeParms quakeParms;
private SysCalls sysCalls;
private ModCalls modCalls;
@ -20,7 +32,12 @@ public class UniQuake: MonoBehaviour
public double CurrentTime => Time.timeAsDouble - startTime;
void Start()
{
{
sysCalls = new SysCalls(this);
modCalls = new ModCalls(this);
LoadLibrary();
string[] arguments =
{
"",
@ -38,9 +55,6 @@ public class UniQuake: MonoBehaviour
quakeParms.SetArguments(arguments);
quakeParms.AllocateMemory(MemSize);
sysCalls = new SysCalls(this);
modCalls = new ModCalls(this);
startTime = Time.timeAsDouble;
try
@ -106,16 +120,55 @@ public class UniQuake: MonoBehaviour
quakeParms.Destroy();
quakeParms = null;
}
FreeLibrary();
}
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
private static extern void UniQuake_Init(QuakeParms parms, IntPtr sysCalls, IntPtr modCalls);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void UniQuake_InitFunc(QuakeParms parms, IntPtr sysCalls, IntPtr modCalls);
private UniQuake_InitFunc UniQuake_Init;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void UniQuake_UpdateFunc(float deltaTime);
private UniQuake_UpdateFunc UniQuake_Update;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void UniQuake_ShutdownFunc();
private UniQuake_ShutdownFunc UniQuake_Shutdown;
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
private static extern void UniQuake_Update(float deltaTime);
private void LoadLibrary()
{
string dllFile = Path.Combine(Application.dataPath, DllPath);
libraryHandle = SystemLibrary.LoadLibrary(dllFile);
if (libraryHandle == IntPtr.Zero)
{
throw new DllNotFoundException($"Failed to load UniQuake library from path: {dllFile}, last error = {Marshal.GetLastWin32Error()}");
}
UniQuake_Init = LoadLibraryFunction<UniQuake_InitFunc>("UniQuake_Init");
UniQuake_Update = LoadLibraryFunction<UniQuake_UpdateFunc>("UniQuake_Update");
UniQuake_Shutdown = LoadLibraryFunction<UniQuake_ShutdownFunc>("UniQuake_Shutdown");
}
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
private static extern void UniQuake_Shutdown();
private TDelegate LoadLibraryFunction<TDelegate>(string name)
{
IntPtr procAddress = SystemLibrary.GetProcAddress(libraryHandle, name);
if (procAddress == IntPtr.Zero)
{
throw new DllNotFoundException($"Could not find library function: {name}");
}
return Marshal.GetDelegateForFunctionPointer<TDelegate>(procAddress);
}
private void FreeLibrary()
{
if (libraryHandle != IntPtr.Zero)
{
SystemLibrary.FreeLibrary(libraryHandle);
libraryHandle = IntPtr.Zero;
}
}
}
public class QuakeException: Exception

Loading…
Cancel
Save