|
|
|
@ -1,12 +1,18 @@ |
|
|
|
using System; |
|
|
|
using System.IO; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
using AOT; |
|
|
|
using UnityEngine; |
|
|
|
|
|
|
|
public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
{ |
|
|
|
private const int MaxFileHandles = 50; |
|
|
|
|
|
|
|
private readonly UniQuake uq; |
|
|
|
private readonly double startTime; |
|
|
|
|
|
|
|
private readonly FileStream[] fileHandles = new FileStream[MaxFileHandles]; |
|
|
|
|
|
|
|
public SysCalls(UniQuake uniQuake) |
|
|
|
{ |
|
|
|
uq = uniQuake; |
|
|
|
@ -15,15 +21,39 @@ public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
var callbacks = new Callbacks |
|
|
|
{ |
|
|
|
target = SelfPtr, |
|
|
|
|
|
|
|
SysPrint = CreateCallback<SysPrintCallback>(Callback_SysPrint), |
|
|
|
SysError = CreateCallback<SysErrorCallback>(Callback_SysError), |
|
|
|
SysQuit = CreateCallback<SysQuitCallback>(Callback_SysQuit), |
|
|
|
SysFloatTime = CreateCallback<SysFloatTimeCallback>(Callback_SysFloatTime), |
|
|
|
|
|
|
|
SysFileOpenRead = CreateCallback<SysFileOpenReadCallback>(Callback_SysFileOpenRead), |
|
|
|
SysFileOpenWrite = CreateCallback<SysFileOpenWriteCallback>(Callback_SysFileOpenWrite), |
|
|
|
SysFileClose = CreateCallback<SysFileCloseCallback>(Callback_SysFileClose), |
|
|
|
SysFileSeek = CreateCallback<SysFileSeekCallback>(Callback_SysFileSeek), |
|
|
|
SysFileRead = CreateCallback<SysFileReadCallback>(Callback_SysFileRead), |
|
|
|
SysFileWrite = CreateCallback<SysFileWriteCallback>(Callback_SysFileWrite), |
|
|
|
SysFileTime = CreateCallback<SysFileTimeCallback>(Callback_SysFileTime), |
|
|
|
SysMkDir = CreateCallback<SysMkDirCallback>(Callback_SysMkDir), |
|
|
|
}; |
|
|
|
|
|
|
|
RegisterCallbacks(callbacks); |
|
|
|
} |
|
|
|
|
|
|
|
public override void Destroy() |
|
|
|
{ |
|
|
|
for (int i = 0; i < MaxFileHandles; ++i) |
|
|
|
{ |
|
|
|
if (fileHandles[i] != null) |
|
|
|
fileHandles[i].Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
base.Destroy(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// This matches struct unity_syscalls_s from uniquake.h in native code.
|
|
|
|
/// </summary>
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 0)] |
|
|
|
private class Callbacks |
|
|
|
{ |
|
|
|
@ -33,12 +63,21 @@ public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
public IntPtr SysError; |
|
|
|
public IntPtr SysQuit; |
|
|
|
public IntPtr SysFloatTime; |
|
|
|
|
|
|
|
public IntPtr SysFileOpenRead; |
|
|
|
public IntPtr SysFileOpenWrite; |
|
|
|
public IntPtr SysFileClose; |
|
|
|
public IntPtr SysFileSeek; |
|
|
|
public IntPtr SysFileRead; |
|
|
|
public IntPtr SysFileWrite; |
|
|
|
public IntPtr SysFileTime; |
|
|
|
public IntPtr SysMkDir; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate void SysPrintCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string message); |
|
|
|
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(SysPrintCallback))] |
|
|
|
[MonoPInvokeCallback(typeof(SysPrintCallback))] |
|
|
|
private static void Callback_SysPrint(IntPtr target, string message) |
|
|
|
{ |
|
|
|
GetSelf(target).Print(message); |
|
|
|
@ -46,13 +85,13 @@ public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
|
|
|
|
private void Print(string message) |
|
|
|
{ |
|
|
|
Debug.Log(message); |
|
|
|
// Debug.Log(message); // TODO: collect logs per frame and print after each Update loop
|
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate void SysErrorCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string message); |
|
|
|
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(SysErrorCallback))] |
|
|
|
[MonoPInvokeCallback(typeof(SysErrorCallback))] |
|
|
|
private static void Callback_SysError(IntPtr target, string message) |
|
|
|
{ |
|
|
|
GetSelf(target).Error(message); |
|
|
|
@ -68,7 +107,7 @@ public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate void SysQuitCallback(IntPtr target); |
|
|
|
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(SysQuitCallback))] |
|
|
|
[MonoPInvokeCallback(typeof(SysQuitCallback))] |
|
|
|
private static void Callback_SysQuit(IntPtr target) |
|
|
|
{ |
|
|
|
GetSelf(target).Quit(); |
|
|
|
@ -83,7 +122,7 @@ public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate double SysFloatTimeCallback(IntPtr target); |
|
|
|
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(SysFloatTimeCallback))] |
|
|
|
[MonoPInvokeCallback(typeof(SysFloatTimeCallback))] |
|
|
|
private static double Callback_SysFloatTime(IntPtr target) |
|
|
|
{ |
|
|
|
return GetSelf(target).FloatTime(); |
|
|
|
@ -93,4 +132,193 @@ public class SysCalls: CallbackHandler<SysCalls> |
|
|
|
{ |
|
|
|
return Time.timeAsDouble - startTime; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate int SysFileOpenReadCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string path, out int handle); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileOpenReadCallback))] |
|
|
|
private static int Callback_SysFileOpenRead(IntPtr target, string path, out int handle) |
|
|
|
{ |
|
|
|
return GetSelf(target).FileOpenRead(path, out handle); |
|
|
|
} |
|
|
|
|
|
|
|
private int FileOpenRead(string path, out int handle) |
|
|
|
{ |
|
|
|
int i = FindFileHandle(); |
|
|
|
try |
|
|
|
{ |
|
|
|
Debug.Log($"Opening file for reading: {path}"); |
|
|
|
FileStream fileStream = File.OpenRead(path); |
|
|
|
fileHandles[i] = fileStream; |
|
|
|
handle = i; |
|
|
|
return (int)fileStream.Length; |
|
|
|
} |
|
|
|
catch |
|
|
|
{ |
|
|
|
handle = -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate int SysFileOpenWriteCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string path); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileOpenWriteCallback))] |
|
|
|
private static int Callback_SysFileOpenWrite(IntPtr target, string path) |
|
|
|
{ |
|
|
|
return GetSelf(target).FileOpenWrite(path); |
|
|
|
} |
|
|
|
|
|
|
|
private int FileOpenWrite(string path) |
|
|
|
{ |
|
|
|
int i = FindFileHandle(); |
|
|
|
try |
|
|
|
{ |
|
|
|
Debug.Log($"Opening file for writing: {path}"); |
|
|
|
FileStream fileStream = File.OpenWrite(path); |
|
|
|
fileHandles[i] = fileStream; |
|
|
|
return i; |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
Error($"Error opening {path}: {ex.Message}"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private int FindFileHandle() |
|
|
|
{ |
|
|
|
for (int i = 1; i < MaxFileHandles; ++i) |
|
|
|
{ |
|
|
|
if (fileHandles[i] == null) |
|
|
|
return i; |
|
|
|
} |
|
|
|
|
|
|
|
Error("Out of handles!"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate void SysFileCloseCallback(IntPtr target, int handle); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileCloseCallback))] |
|
|
|
private static void Callback_SysFileClose(IntPtr target, int handle) |
|
|
|
{ |
|
|
|
GetSelf(target).FileClose(handle); |
|
|
|
} |
|
|
|
|
|
|
|
private void FileClose(int handle) |
|
|
|
{ |
|
|
|
if (handle < 0 || handle >= MaxFileHandles) |
|
|
|
{ |
|
|
|
Debug.LogWarning($"Invalid file handle! {handle}"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
fileHandles[handle].Dispose(); |
|
|
|
fileHandles[handle] = null; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate void SysFileSeekCallback(IntPtr target, int handle, int position); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileSeekCallback))] |
|
|
|
private static void Callback_SysFileSeek(IntPtr target, int handle, int position) |
|
|
|
{ |
|
|
|
GetSelf(target).FileSeek(handle, position); |
|
|
|
} |
|
|
|
|
|
|
|
private void FileSeek(int handle, int position) |
|
|
|
{ |
|
|
|
if (handle < 0 || handle >= MaxFileHandles) |
|
|
|
{ |
|
|
|
Debug.LogWarning($"Invalid file handle! {handle}"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
fileHandles[handle].Seek(position, SeekOrigin.Begin); |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate int SysFileReadCallback(IntPtr target, int handle, IntPtr dest, int count); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileReadCallback))] |
|
|
|
private static int Callback_SysFileRead(IntPtr target, int handle, IntPtr dest, int count) |
|
|
|
{ |
|
|
|
return GetSelf(target).FileRead(handle, dest, count); |
|
|
|
} |
|
|
|
|
|
|
|
private int FileRead(int handle, IntPtr dest, int count) |
|
|
|
{ |
|
|
|
if (handle < 0 || handle >= MaxFileHandles) |
|
|
|
{ |
|
|
|
Debug.LogWarning($"Invalid file handle! {handle}"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
byte[] buf = new byte[count]; // TODO: reuse buffer
|
|
|
|
int numRead = fileHandles[handle].Read(buf, 0, count); |
|
|
|
Marshal.Copy(buf, 0, dest, numRead); |
|
|
|
return numRead; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate int SysFileWriteCallback(IntPtr target, int handle, IntPtr data, int count); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileWriteCallback))] |
|
|
|
private static int Callback_SysFileWrite(IntPtr target, int handle, IntPtr data, int count) |
|
|
|
{ |
|
|
|
return GetSelf(target).FileWrite(handle, data, count); |
|
|
|
} |
|
|
|
|
|
|
|
private int FileWrite(int handle, IntPtr data, int count) |
|
|
|
{ |
|
|
|
if (handle < 0 || handle >= MaxFileHandles) |
|
|
|
{ |
|
|
|
Debug.LogWarning($"Invalid file handle! {handle}"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
byte[] buf = new byte[count]; // TODO: reuse buffer
|
|
|
|
Marshal.Copy(data, buf, 0, count); |
|
|
|
fileHandles[handle].Write(buf, 0, count); |
|
|
|
return count; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate int SysFileTimeCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string path); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysFileTimeCallback))] |
|
|
|
private static int Callback_SysFileTime(IntPtr target, string path) |
|
|
|
{ |
|
|
|
return GetSelf(target).FileTime(path); |
|
|
|
} |
|
|
|
|
|
|
|
private int FileTime(string path) |
|
|
|
{ |
|
|
|
// It would logically make more sense to return an actual file write time here,
|
|
|
|
// but a signed 32-bit int is quite limited and we don't want any Year 2038 problems here.
|
|
|
|
return File.Exists(path) ? 1 : -1; |
|
|
|
} |
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
|
private delegate void SysMkDirCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string path); |
|
|
|
|
|
|
|
[MonoPInvokeCallback(typeof(SysMkDirCallback))] |
|
|
|
private static void Callback_SysMkDir(IntPtr target, string path) |
|
|
|
{ |
|
|
|
GetSelf(target).MkDir(path); |
|
|
|
} |
|
|
|
|
|
|
|
private void MkDir(string path) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
Directory.CreateDirectory(path); |
|
|
|
} |
|
|
|
catch |
|
|
|
{ |
|
|
|
Debug.LogWarning($"Could not create directory: {path}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |