using System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 0)] public class QuakeParms { private const int MaxArgs = 50; // Corresponds with MAX_NUM_ARGVS in engine [MarshalAs(UnmanagedType.LPStr)] public string baseDir; [MarshalAs(UnmanagedType.LPStr)] public string userDir; public int argc; [MarshalAs(UnmanagedType.LPArray, SizeConst = MaxArgs)] public IntPtr[] argv; public IntPtr memBase; public int memSize; public int numCpus; public int errState; private IntPtr nativeBlock; public IntPtr ToNativePtr() { if (nativeBlock == IntPtr.Zero) nativeBlock = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(this, nativeBlock, false); return nativeBlock; } public void SetArguments(string[] arguments) { argc = arguments.Length; argv = new IntPtr[MaxArgs]; for (int i = 0; i < arguments.Length && i < MaxArgs; ++i) { argv[i] = Marshal.StringToHGlobalAnsi(arguments[i]); } } public void AllocateMemory(int memorySize) { memSize = memorySize; memBase = Marshal.AllocHGlobal(memorySize); if (memBase == IntPtr.Zero) { throw new OutOfMemoryException($"Could not allocate {memorySize / 1024 / 1024} MB of heap memory!"); } } public void Destroy() { if (nativeBlock != IntPtr.Zero) { Marshal.FreeHGlobal(nativeBlock); nativeBlock = IntPtr.Zero; } if (memBase != IntPtr.Zero) { Marshal.FreeHGlobal(memBase); memBase = IntPtr.Zero; } if (argv != null) { for (int i = 0; i < argv.Length; ++i) { if (argv[i] != IntPtr.Zero) { Marshal.FreeHGlobal(argv[i]); argv[i] = IntPtr.Zero; } } } } }