diff --git a/OmsiExtensionsCLI/Program.cs b/OmsiExtensionsCLI/Program.cs index 9a59d3e..53af334 100644 --- a/OmsiExtensionsCLI/Program.cs +++ b/OmsiExtensionsCLI/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using System.Numerics; using System.Reflection; @@ -54,37 +55,54 @@ static void Main(string[] args) Console.WriteLine($"Read data: map:{map?.Name} path:{map?.Filename} friendly:{map?.FriendlyName}".PadRight(Console.WindowWidth - 1)); Console.WriteLine($"Time: {omsi.Globals.Time.Day}/{omsi.Globals.Time.Month}/{omsi.Globals.Time.Year} - {omsi.Globals.Time.Hour}:{omsi.Globals.Time.Minute}:{omsi.Globals.Time.Second:F2} "); Console.WriteLine($"Camera pos: {cam.Pos} ".PadRight(Console.WindowWidth - 1)); - Console.WriteLine($"{omsi.Globals.Drivers}".PadRight(Console.WindowWidth - 1)); + //Console.WriteLine($"{omsi.Globals.Drivers}".PadRight(Console.WindowWidth - 1)); - /*if(!dXTests.IsReady) - dXTests.CreateTexture(); - if(dXTests.IsReady) - dXTests.UpdateTexture();*/ + ///*if(!dXTests.IsReady) + // dXTests.CreateTexture(); + //if(dXTests.IsReady) + // dXTests.UpdateTexture();*/ - Console.WriteLine($"[MOUSE] pos: {progMan.MausPos}".PadRight(Console.WindowWidth - 1)); - Console.WriteLine($"[MOUSE] ray_pos: {progMan.MausLine3DPos} ray_dir: {progMan.MausLine3DDir}".PadRight(Console.WindowWidth - 1)); - Console.WriteLine($"[MOUSE] mouse_mesh_event: {progMan.Maus_MeshEvent}".PadRight(Console.WindowWidth - 1)); - CheckClickPos(progMan, meshes, meshInsts); + //Console.WriteLine($"[MOUSE] pos: {progMan.MausPos}".PadRight(Console.WindowWidth - 1)); + //Console.WriteLine($"[MOUSE] ray_pos: {progMan.MausLine3DPos} ray_dir: {progMan.MausLine3DDir}".PadRight(Console.WindowWidth - 1)); + //Console.WriteLine($"[MOUSE] mouse_mesh_event: {progMan.Maus_MeshEvent}".PadRight(Console.WindowWidth - 1)); + //CheckClickPos(progMan, meshes, meshInsts); - Console.WriteLine($"Loaded textures: {textures.Count}"); - Console.WriteLine($"Path id: {playerVehicle.PathInfo.path.path}"); + //Console.WriteLine($"Loaded textures: {textures.Count}"); + //Console.WriteLine($"Path id: {playerVehicle.PathInfo.path.path}"); - /*Console.WriteLine("".PadRight(Console.WindowWidth-1)); - try - { - if (omsi.Globals.PlayerVehicle != null) - { - Console.WriteLine($"INEO_PS_Matricule: {omsi.Globals.PlayerVehicle.GetStringVariable("INEO_Login")}".PadRight(Console.WindowWidth - 1)); + ///*Console.WriteLine("".PadRight(Console.WindowWidth-1)); + //try + //{ + // if (omsi.Globals.PlayerVehicle != null) + // { + // Console.WriteLine($"INEO_PS_Matricule: {omsi.Globals.PlayerVehicle.GetStringVariable("INEO_Login")}".PadRight(Console.WindowWidth - 1)); - omsi.Globals.PlayerVehicle.SetStringVariable("INEO_Login", toggle ? "thomas" : "01234"); - toggle = !toggle; - } - } - catch (Exception e) { Console.WriteLine(e.Message); }*/ + // omsi.Globals.PlayerVehicle.SetStringVariable("INEO_Login", toggle ? "thomas" : "01234"); + // toggle = !toggle; + // } + //} + //catch (Exception e) { Console.WriteLine(e.Message); }*/ + + + var OMSIRM = omsi.RemoteMethods; + //OMSIRM.PlaceRandomBus(); + Console.WriteLine("Placed"); + + OMSIRM.OmsiSetCriticalSectionLock(omsi.Globals.ProgamManager.CS_MakeVehiclePtr).ContinueWith((_) => + { + OMSIRM.MakeVehicle(@"Vehicles\GPM_MAN_LionsCity_M\MAN_A47.bus", __copyToMainList: true).ContinueWith((id) => + { + Console.WriteLine($"Spawned Vehicle ID: {id.Result}"); + OMSIRM.OmsiReleaseCriticalSectionLock(omsi.Globals.ProgamManager.CS_MakeVehiclePtr).ContinueWith((_)=>Console.WriteLine($"Unlock")); + }); + }); + break; + Debugger.Break(); Thread.Sleep(20); } + Console.ReadLine(); } private static void CheckClickPos(OmsiProgMan progMan, MemArrayList meshes, MemArrayList meshInsts) diff --git a/OmsiHook/FastBinaryWriter.cs b/OmsiHook/FastBinaryWriter.cs new file mode 100644 index 0000000..626d314 --- /dev/null +++ b/OmsiHook/FastBinaryWriter.cs @@ -0,0 +1,64 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OmsiHook; + +internal static class FastBinaryWriter +{ + public static void Write(Span buffer, ref int pos, int data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 4; + } + + public static void Write(Span buffer, ref int pos, uint data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 4; + } + + public static void Write(Span buffer, ref int pos, short data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 2; + } + public static void Write(Span buffer, ref int pos, ushort data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 2; + } + + public static void Write(Span buffer, ref int pos, byte data, int advance = 1) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += advance; + } + + public static void Write(Span buffer, ref int pos, sbyte data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 1; + } + + public static void Write(Span buffer, ref int pos, bool data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 1; + } + + public static void Write(Span buffer, ref int pos, float data) + { + BitConverter.TryWriteBytes(buffer[pos..], data); + pos += 4; + } + + public static void Write(byte[] buffer, ref int pos, int data) + { + BitConverter.TryWriteBytes(buffer.AsSpan()[pos..], data); + pos += 4; + } +} diff --git a/OmsiHook/OmsiHookRPCMethods.cs b/OmsiHook/OmsiHookRPCMethods.cs index d7ddf48..6098f1b 100644 --- a/OmsiHook/OmsiHookRPCMethods.cs +++ b/OmsiHook/OmsiHookRPCMethods.cs @@ -12,9 +12,11 @@ internal static class OmsiHookRPCMethods [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum RemoteMethod : int { + None, CloseRPCConnection, TProgManMakeVehicle, TTempRVListCreate, + CopyTempListIntoMainList, TProgManPlaceRandomBus, GetMem, FreeMem, @@ -26,14 +28,18 @@ internal enum RemoteMethod : int GetTextureLevelCount, IsTexture, RVTriggerXML, - SoundTrigger + SoundTrigger, + SetCriticalSectionLock, + ReleaseCriticalSectionLock } internal static readonly ReadOnlyDictionary RemoteMethodsArgsSizes = new(new Dictionary() { + { RemoteMethod.None, 0 }, { RemoteMethod.CloseRPCConnection, 4 }, { RemoteMethod.TProgManMakeVehicle, 61 }, { RemoteMethod.TTempRVListCreate, 8 }, + { RemoteMethod.CopyTempListIntoMainList, 8 }, { RemoteMethod.TProgManPlaceRandomBus, 35 }, { RemoteMethod.GetMem, 4 }, { RemoteMethod.FreeMem, 4 }, @@ -46,6 +52,8 @@ internal enum RemoteMethod : int { RemoteMethod.IsTexture, 4 }, { RemoteMethod.RVTriggerXML, 12 }, { RemoteMethod.SoundTrigger, 12 }, + { RemoteMethod.SetCriticalSectionLock, 4 }, + { RemoteMethod.ReleaseCriticalSectionLock, 4 }, }); } } diff --git a/OmsiHook/OmsiRemoteMethods.cs b/OmsiHook/OmsiRemoteMethods.cs index ac99ec3..fca82e4 100644 --- a/OmsiHook/OmsiRemoteMethods.cs +++ b/OmsiHook/OmsiRemoteMethods.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using OmsiHook.WrappedOmsiClasses; using OmsiHookRPCPlugin; namespace OmsiHook @@ -110,19 +111,135 @@ private void ResultReaderTask() } } - [Obsolete] - public int MakeVehicle() + private async Task TempRVListCreate(uint classAddr, int capacity) { - int vehList = TTempRVListCreate(0x0074802C, 1); - string path = @"Vehicles\GPM_MAN_LionsCity_M\MAN_A47.bus"; - int mem = memory.AllocateString(path, false).Result; - - return TProgManMakeVehicle(memory.ReadMemory(0x00862f28), vehList, - memory.ReadMemory(0x008615A8), false, false, - 0, false, false, false, false, - -1, true, 0, (byte)3, false, - 0, 0, 0, 0, 0, false, - false, true, true, mem); + if (localPlugin) + { + return TTempRVListCreate(unchecked((int)classAddr), capacity); + } + else + { + if (!IsInitialised) + throw new NotInitialisedException("OmsiHook RPC plugin is not connected! Did you make sure to call OmsiRemoteMethods.InitRemoteMethods() before this call?"); + + int argPos = 0; + var method = OmsiHookRPCMethods.RemoteMethod.TTempRVListCreate; + byte[] writeBuffer = asyncWriteBuff.Value; + //Span readBuffer = stackalloc byte[4]; + (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, classAddr); + FastBinaryWriter.Write(writeBuffer, ref argPos, capacity); + int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; + lock (pipeTX) + pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); + return await promise.Task; + } + } + + private async Task CopyTempListIntoMainList_(uint rvList, uint tmpList) + { + if (localPlugin) + { + return CopyTempListIntoMainList(unchecked((int)rvList), unchecked((int)tmpList)); + } + else + { + if (!IsInitialised) + throw new NotInitialisedException("OmsiHook RPC plugin is not connected! Did you make sure to call OmsiRemoteMethods.InitRemoteMethods() before this call?"); + + int argPos = 0; + var method = OmsiHookRPCMethods.RemoteMethod.CopyTempListIntoMainList; + byte[] writeBuffer = asyncWriteBuff.Value; + //Span readBuffer = stackalloc byte[4]; + (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, rvList); + FastBinaryWriter.Write(writeBuffer, ref argPos, tmpList); + int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; + lock (pipeTX) + pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); + return await promise.Task; + } + } + + public async Task MakeVehicle(string path, int licensePlateIndex = -1, bool randomLicensePlate = false, + int type = 0, int groupHof = 0, int tour = 0, int line = 0, bool scheduled = false, bool aiRoadVehicle = false, + int farbschema = -1, bool randomFarbschema = false, + bool __copyToMainList = true) + { + var vehListTask = __copyToMainList ? TempRVListCreate(0x0074802C, 1) : Task.FromResult(0); + //string path = @"Vehicles\GPM_MAN_LionsCity_M\MAN_A47.bus"; + var pathAddrTask = memory.AllocateString(path, false); + await Task.WhenAll(vehListTask, pathAddrTask); + + var vehList = vehListTask.Result; + var pathAddr = pathAddrTask.Result; + if (!__copyToMainList) + vehList = memory.ReadMemory(0x0086171c); + int progManAddr = memory.ReadMemory(0x00862f28); + int roadVehicleTypes = memory.ReadMemory(0x008615A8); + + if (localPlugin) + { + var ret = TProgManMakeVehicle(progManAddr, vehList, + roadVehicleTypes, false, false, + 0, false, false, false/*true*/, false, + licensePlateIndex, true, 0, (byte)2/*3*/, false, + groupHof, type, tour, line, farbschema, scheduled, + aiRoadVehicle, randomLicensePlate, randomFarbschema, pathAddr); + + if (__copyToMainList) + await CopyTempListIntoMainList_(memory.ReadMemory(0x00861508), unchecked((uint)vehList)); + return ret; + } + else + { + if (!IsInitialised) + throw new NotInitialisedException("OmsiHook RPC plugin is not connected! Did you make sure to call OmsiRemoteMethods.InitRemoteMethods() before this call?"); + + int argPos = 0; + var method = OmsiHookRPCMethods.RemoteMethod.TProgManMakeVehicle; + byte[] writeBuffer = asyncWriteBuff.Value; + (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, progManAddr); + FastBinaryWriter.Write(writeBuffer, ref argPos, vehList); + FastBinaryWriter.Write(writeBuffer, ref argPos, roadVehicleTypes); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, 0f); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, licensePlateIndex); + FastBinaryWriter.Write(writeBuffer, ref argPos, true); + FastBinaryWriter.Write(writeBuffer, ref argPos, 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, 3, 1); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, groupHof); + FastBinaryWriter.Write(writeBuffer, ref argPos, type); + FastBinaryWriter.Write(writeBuffer, ref argPos, tour); + FastBinaryWriter.Write(writeBuffer, ref argPos, line); + FastBinaryWriter.Write(writeBuffer, ref argPos, farbschema); + FastBinaryWriter.Write(writeBuffer, ref argPos, scheduled); + FastBinaryWriter.Write(writeBuffer, ref argPos, aiRoadVehicle); + FastBinaryWriter.Write(writeBuffer, ref argPos, randomLicensePlate); + FastBinaryWriter.Write(writeBuffer, ref argPos, randomFarbschema); + FastBinaryWriter.Write(writeBuffer, ref argPos, pathAddr); + int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; + lock (pipeTX) + pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); + var ret = await promise.Task; + + if (__copyToMainList) + await CopyTempListIntoMainList_(memory.ReadMemory(0x00861508), unchecked((uint)vehList)); + return ret; + } } public async Task CloseRPCSession(bool killAllConnections) @@ -138,9 +255,9 @@ public async Task CloseRPCSession(bool killAllConnections) int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], 0); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], killAllConnections ? 1 : 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, killAllConnections ? 1 : 0); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); await promise.Task; @@ -174,19 +291,19 @@ public int PlaceRandomBus(int aiType = 0, int group = 1, int type = -1, bool sch Span writeBuffer = stackalloc byte[OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8]; //Span readBuffer = stackalloc byte[4]; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], aiType); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], group); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], 0); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 1)..], false); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 1)..], true); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], type); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 1)..], scheduled); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], 0); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], aiType); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], tour); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], line); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, memory.ReadMemory(0x00862f28)); + FastBinaryWriter.Write(writeBuffer, ref argPos, aiType); + FastBinaryWriter.Write(writeBuffer, ref argPos, group); + FastBinaryWriter.Write(writeBuffer, ref argPos, 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, false); + FastBinaryWriter.Write(writeBuffer, ref argPos, true); + FastBinaryWriter.Write(writeBuffer, ref argPos, type); + FastBinaryWriter.Write(writeBuffer, ref argPos, scheduled); + FastBinaryWriter.Write(writeBuffer, ref argPos, 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, tour); + FastBinaryWriter.Write(writeBuffer, ref argPos, line); lock (pipeTX) pipeTX.Write(writeBuffer); return promise.Task.Result; @@ -218,9 +335,9 @@ public async Task OmsiGetMem(int length) // This should be thread safe as the asyncWriteBuff is thread local byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], length); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, length); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); return (uint)await promise.Task; @@ -248,9 +365,9 @@ public void OmsiFreeMemAsync(int addr) var method = OmsiHookRPCMethods.RemoteMethod.FreeMem; Span writeBuffer = stackalloc byte[OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8]; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], addr); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, addr); lock (pipeTX) pipeTX.Write(writeBuffer); //promise.AsTask().Wait(); @@ -272,8 +389,8 @@ public bool OmsiHookD3D() int argPos = 0; Span writeBuffer = stackalloc byte[8]; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer[(argPos)..], (int)OmsiHookRPCMethods.RemoteMethod.HookD3D); - BitConverter.TryWriteBytes(writeBuffer[(argPos += 4)..], resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)OmsiHookRPCMethods.RemoteMethod.HookD3D); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); lock (pipeTX) pipeTX.Write(writeBuffer); var res = promise.Task.Result; @@ -307,13 +424,13 @@ public bool OmsiHookD3D() int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], width); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], height); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], (uint)format); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], levels); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], ppTexture); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, width); + FastBinaryWriter.Write(writeBuffer, ref argPos, height); + FastBinaryWriter.Write(writeBuffer, ref argPos, (uint)format); + FastBinaryWriter.Write(writeBuffer, ref argPos, levels); + FastBinaryWriter.Write(writeBuffer, ref argPos, ppTexture); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); HRESULT result = (HRESULT)await promise.Task; @@ -352,18 +469,18 @@ public async Task OmsiUpdateTextureAsync(uint texturePtr, uint textureD int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], texturePtr); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], textureDataPtr); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], width); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], height); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], updateRect.HasValue ? 1 : 0); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], updateRect?.left ?? 0); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], updateRect?.top ?? 0); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], updateRect?.right ?? 0); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], updateRect?.bottom ?? 0); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], level); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, texturePtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, textureDataPtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, width); + FastBinaryWriter.Write(writeBuffer, ref argPos, height); + FastBinaryWriter.Write(writeBuffer, ref argPos, updateRect.HasValue ? 1 : 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, updateRect?.left ?? 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, updateRect?.top ?? 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, updateRect?.right ?? 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, updateRect?.bottom ?? 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, level); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); return (HRESULT)await promise.Task; @@ -391,9 +508,9 @@ public async Task OmsiReleaseTextureAsync(uint texturePtr) int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], texturePtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, texturePtr); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); return (HRESULT)await promise.Task; @@ -432,13 +549,13 @@ public async Task OmsiReleaseTextureAsync(uint texturePtr) int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], texturePtr); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], level); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], descPtr); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], descPtr + 4); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], descPtr + 8); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, texturePtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, level); + FastBinaryWriter.Write(writeBuffer, ref argPos, descPtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, descPtr + 4); + FastBinaryWriter.Write(writeBuffer, ref argPos, descPtr + 8); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); @@ -473,9 +590,9 @@ public async Task OmsiGetTextureLevelCountAsync(uint texturePtr) int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], texturePtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, texturePtr); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); return unchecked((uint)await promise.Task); @@ -505,9 +622,9 @@ public async Task OmsiIsTextureAsync(uint texturePtr) int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], texturePtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, texturePtr); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); return !HRESULTFailed((HRESULT)await promise.Task); @@ -550,11 +667,11 @@ public async Task OmsiSetTrigger(OmsiRoadVehicleInst roadVehicle, int triggerPtr // This should be thread safe as the asyncWriteBuff is thread local byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], roadVehicle.Address); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], triggerPtr); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], enabled ? 1 : 0); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, roadVehicle.Address); + FastBinaryWriter.Write(writeBuffer, ref argPos, triggerPtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, enabled ? 1 : 0); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); await promise.Task; @@ -599,11 +716,67 @@ public async Task OmsiSoundTrigger(OmsiComplMapObjInst mapObj, int triggerPtr, i // This should be thread safe as the asyncWriteBuff is thread local byte[] writeBuffer = asyncWriteBuff.Value; (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos)..], (int)method); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], resultPromise); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], mapObj.Address); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], triggerPtr); - BitConverter.TryWriteBytes(writeBuffer.AsSpan()[(argPos += 4)..], filenamePtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, mapObj.Address); + FastBinaryWriter.Write(writeBuffer, ref argPos, triggerPtr); + FastBinaryWriter.Write(writeBuffer, ref argPos, filenamePtr); + lock (pipeTX) + pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); + await promise.Task; + } + } + + + public async Task OmsiSetCriticalSectionLock(IntPtr CS) + { + if (localPlugin) + { + SetCriticalSectionLock((int)CS); + return; + } + else + { + if (!IsInitialised) + throw new NotInitialisedException("OmsiHook RPC plugin is not connected! Did you make sure to call OmsiRemoteMethods.InitRemoteMethods() before this call?"); + + int argPos = 0; + var method = OmsiHookRPCMethods.RemoteMethod.SetCriticalSectionLock; + int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; + // This should be thread safe as the asyncWriteBuff is thread local + byte[] writeBuffer = asyncWriteBuff.Value; + (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)CS); + lock (pipeTX) + pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); + await promise.Task; + } + } + + + public async Task OmsiReleaseCriticalSectionLock(IntPtr CS) + { + if (localPlugin) + { + ReleaseCriticalSectionLock((int)CS); + return; + } + else + { + if (!IsInitialised) + throw new NotInitialisedException("OmsiHook RPC plugin is not connected! Did you make sure to call OmsiRemoteMethods.InitRemoteMethods() before this call?"); + + int argPos = 0; + var method = OmsiHookRPCMethods.RemoteMethod.ReleaseCriticalSectionLock; + int writeBufferSize = OmsiHookRPCMethods.RemoteMethodsArgsSizes[method] + 8; + // This should be thread safe as the asyncWriteBuff is thread local + byte[] writeBuffer = asyncWriteBuff.Value; + (int resultPromise, TaskCompletionSource promise) = CreateResultPromise(); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)method); + FastBinaryWriter.Write(writeBuffer, ref argPos, resultPromise); + FastBinaryWriter.Write(writeBuffer, ref argPos, (int)CS); lock (pipeTX) pipeTX.Write(writeBuffer.AsSpan()[..writeBufferSize]); await promise.Task; @@ -619,6 +792,8 @@ private static extern int TProgManMakeVehicle(int progMan, int vehList, int _Roa [DllImport("OmsiHookInvoker.dll")] private static extern int TTempRVListCreate(int classAddr, int capacity); [DllImport("OmsiHookInvoker.dll")] + private static extern int CopyTempListIntoMainList(int rvList, int tmpList); + [DllImport("OmsiHookInvoker.dll")] private static extern int TProgManPlaceRandomBus(int progMan, int aityp, int group, float TTtime, bool thread, bool instantCopy, int _typ, bool scheduled, int startDay, int tour, int line); @@ -644,6 +819,10 @@ private static extern int TProgManPlaceRandomBus(int progMan, int aityp, private static extern void RVTriggerXML(int roadVehicle, int trigger, int value); [DllImport("OmsiHookInvoker.dll")] internal static extern void SoundTrigger(int complMapObj, int trigger, int filename); + [DllImport("OmsiHookInvoker.dll")] + internal static extern void SetCriticalSectionLock(int CS); + [DllImport("OmsiHookInvoker.dll")] + internal static extern void ReleaseCriticalSectionLock(int CS); public enum D3DFORMAT : uint { diff --git a/OmsiHook/WrappedOmsiClasses/OmsiCriticalSection.cs b/OmsiHook/WrappedOmsiClasses/OmsiCriticalSection.cs new file mode 100644 index 0000000..d22b022 --- /dev/null +++ b/OmsiHook/WrappedOmsiClasses/OmsiCriticalSection.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OmsiHook.WrappedOmsiClasses +{ + public class OmsiCriticalSectionObject : OmsiObject + { + internal OmsiCriticalSectionObject(Memory omsiMemory, int baseAddress) : base(omsiMemory, baseAddress) { } + public OmsiCriticalSectionObject() : base() { } + + public RTL_CRITICAL_SECTION cs + { + get => Memory.ReadMemory(Address + 0x0); + } + public string name + { + get => Memory.ReadMemoryString(Address + 0x18); + } + public uint ident + { + get => Memory.ReadMemory(Address + 0x1c); + } + } +} diff --git a/OmsiHook/WrappedOmsiClasses/OmsiProgMan.cs b/OmsiHook/WrappedOmsiClasses/OmsiProgMan.cs index 8c6128a..5dc27fa 100644 --- a/OmsiHook/WrappedOmsiClasses/OmsiProgMan.cs +++ b/OmsiHook/WrappedOmsiClasses/OmsiProgMan.cs @@ -1,4 +1,5 @@ -using System; +using OmsiHook.WrappedOmsiClasses; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -347,22 +348,18 @@ public OmsiObjBlackRect ScreenRect { get => Memory.ReadMemoryObject(Address, 0x1b0, false); } - // TODO: Write internal structs for these so that they can be marhsalled - /*public OmsiCriticalSection CS_MakeVehicle + public IntPtr CS_MakeVehiclePtr { - get => Memory.ReadMemory(Address + 0x1b4); - set => Memory.WriteMemory(Address + 0x1b4, value); + get => (IntPtr)(Address + 0x1b4); } - public OmsiCriticalSection CS_TexUse + public IntPtr CS_TexUsePtr { - get => Memory.ReadMemory(Address + 0x1d4); - set => Memory.WriteMemory(Address + 0x1d4, value); + get => (IntPtr)(Address + 0x1d4); } - public OmsiCriticalSection CS_ODE + public IntPtr CS_ODEPtr { - get => Memory.ReadMemory(Address + 0x1f4); - set => Memory.WriteMemory(Address + 0x1f4, value); - }*/ + get => (IntPtr)(Address + 0x1f4); + } public bool FPS_ShowedAllReady { get => Memory.ReadMemory(Address + 0x214); diff --git a/OmsiHookInvoker/dllmain.cpp b/OmsiHookInvoker/dllmain.cpp index 6ab87be..ad87a69 100644 --- a/OmsiHookInvoker/dllmain.cpp +++ b/OmsiHookInvoker/dllmain.cpp @@ -154,6 +154,12 @@ extern "C" __declspec(dllexport) int TTempRVListCreate(int classAddr, int capaci classAddr, capacity); } +extern "C" __declspec(dllexport) int CopyTempListIntoMainList(int rvList, int tmpList) +{ + return BorlandFastCall(0x0074a240, 2, 2, + rvList, tmpList); +} + extern "C" __declspec(dllexport) int TProgManPlaceRandomBus(int progMan, int aityp, int group, int TTtime, bool thread, bool instantCopy, int _typ, bool scheduled, int startDay, int tour, int line) @@ -187,6 +193,18 @@ extern "C" __declspec(dllexport) void SoundTrigger(int complMapObj, int trigger, complMapObj, trigger, filename); } +extern "C" __declspec(dllexport) void SetCriticalSectionLock(int addr) +{ + BorlandFastCall(0x00562af8, 1, 1, + addr); +} + +extern "C" __declspec(dllexport) void ReleaseCriticalSectionLock(int addr) +{ + BorlandFastCall(0x00562B30, 1, 1, + addr); +} + extern "C" __declspec(dllexport) BOOL HookD3D() { if (!m_dxHook) diff --git a/OmsiHookRPCPlugin/FastBinaryReader.cs b/OmsiHookRPCPlugin/FastBinaryReader.cs new file mode 100644 index 0000000..98493be --- /dev/null +++ b/OmsiHookRPCPlugin/FastBinaryReader.cs @@ -0,0 +1,67 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OmsiHookRPCPlugin; + +internal static class FastBinaryReader +{ + public static int ReadI32(Span buffer, ref int pos) + { + var ret = BitConverter.ToInt32(buffer[pos..]); + pos += 4; + return ret; + } + + public static uint ReadU32(Span buffer, ref int pos) + { + var ret = BitConverter.ToUInt32(buffer[pos..]); + pos += 4; + return ret; + } + + public static short ReadI16(Span buffer, ref int pos) + { + var ret = BitConverter.ToInt16(buffer[pos..]); + pos += 2; + return ret; + } + + public static ushort ReadU16(Span buffer, ref int pos) + { + var ret = BitConverter.ToUInt16(buffer[pos..]); + pos += 2; + return ret; + } + + public static sbyte ReadI8(Span buffer, ref int pos) + { + var ret = (sbyte)buffer[pos]; + pos += 1; + return ret; + } + + public static byte ReadU8(Span buffer, ref int pos) + { + var ret = buffer[pos]; + pos += 1; + return ret; + } + + public static bool ReadBool(Span buffer, ref int pos) + { + var ret = BitConverter.ToBoolean(buffer[pos..]); + pos += 1; + return ret; + } + + public static float ReadFloat(Span buffer, ref int pos) + { + var ret = BitConverter.ToSingle(buffer[pos..]); + pos += 4; + return ret; + } +} diff --git a/OmsiHookRPCPlugin/NativeImports.cs b/OmsiHookRPCPlugin/NativeImports.cs index 89facff..c5febbc 100644 --- a/OmsiHookRPCPlugin/NativeImports.cs +++ b/OmsiHookRPCPlugin/NativeImports.cs @@ -1,42 +1,47 @@ using System.Runtime.InteropServices; -namespace OmsiHookRPCPlugin +namespace OmsiHookRPCPlugin; + +internal static class NativeImports { - internal static class NativeImports - { - [DllImport("OmsiHookInvoker.dll")] - internal static extern int TProgManMakeVehicle(int progMan, int vehList, int _RoadVehicleTypes, bool onlyvehlist, bool CS, - float TTtime, bool situationload, bool dialog, bool setdriver, bool thread, - int kennzeichen_index, bool initcall, int startday, byte trainbuilddir, bool reverse, - int grouphof, int typ, int tour, int line, int farbschema, bool Scheduled, - bool AIRoadVehicle, bool kennzeichen_random, bool farbschema_random, int filename); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int TTempRVListCreate(int classAddr, int capacity); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int TProgManPlaceRandomBus(int progMan, int aityp, - int group, float TTtime, bool thread, bool instantCopy, int _typ, - bool scheduled, int startDay, int tour, int line); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int GetMem(int length); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int FreeMem(int addr); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int HookD3D(); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int CreateTexture(uint Width, uint Height, uint Format, uint Levels, uint ppTexture); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int UpdateSubresource(uint Texture, uint TextureData, uint Width, uint Height, int UseRect, uint Left, uint Top, uint Right, uint Bottom, uint Level); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int ReleaseTexture(uint Texture); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int GetTextureDesc(uint Texture, uint Level, uint pWidth, uint pHeight, uint pFormat); - [DllImport("OmsiHookInvoker.dll")] - internal static extern uint GetTextureLevelCount(uint Texture); - [DllImport("OmsiHookInvoker.dll")] - internal static extern int IsTexture(uint Texture); - [DllImport("OmsiHookInvoker.dll")] - internal static extern void RVTriggerXML(int roadVehicle, int trigger, int value); - [DllImport("OmsiHookInvoker.dll")] - internal static extern void SoundTrigger(int complMapObj, int trigger, int filename); - } + [DllImport("OmsiHookInvoker.dll")] + internal static extern int TProgManMakeVehicle(int progMan, int vehList, int _RoadVehicleTypes, bool onlyvehlist, bool CS, + float TTtime, bool situationload, bool dialog, bool setdriver, bool thread, + int kennzeichen_index, bool initcall, int startday, byte trainbuilddir, bool reverse, + int grouphof, int typ, int tour, int line, int farbschema, bool Scheduled, + bool AIRoadVehicle, bool kennzeichen_random, bool farbschema_random, int filename); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int TTempRVListCreate(int classAddr, int capacity); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int CopyTempListIntoMainList(int rvList, int tmpList); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int TProgManPlaceRandomBus(int progMan, int aityp, + int group, float TTtime, bool thread, bool instantCopy, int _typ, + bool scheduled, int startDay, int tour, int line); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int GetMem(int length); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int FreeMem(int addr); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int HookD3D(); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int CreateTexture(uint Width, uint Height, uint Format, uint Levels, uint ppTexture); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int UpdateSubresource(uint Texture, uint TextureData, uint Width, uint Height, int UseRect, uint Left, uint Top, uint Right, uint Bottom, uint Level); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int ReleaseTexture(uint Texture); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int GetTextureDesc(uint Texture, uint Level, uint pWidth, uint pHeight, uint pFormat); + [DllImport("OmsiHookInvoker.dll")] + internal static extern uint GetTextureLevelCount(uint Texture); + [DllImport("OmsiHookInvoker.dll")] + internal static extern int IsTexture(uint Texture); + [DllImport("OmsiHookInvoker.dll")] + internal static extern void RVTriggerXML(int roadVehicle, int trigger, int value); + [DllImport("OmsiHookInvoker.dll")] + internal static extern void SoundTrigger(int complMapObj, int trigger, int filename); + [DllImport("OmsiHookInvoker.dll")] + internal static extern void SetCriticalSectionLock(int addr); + [DllImport("OmsiHookInvoker.dll")] + internal static extern void ReleaseCriticalSectionLock(int addr); } diff --git a/OmsiHookRPCPlugin/OmsiHookRPCPlugin.cs b/OmsiHookRPCPlugin/OmsiHookRPCPlugin.cs index 326475e..6171c7b 100644 --- a/OmsiHookRPCPlugin/OmsiHookRPCPlugin.cs +++ b/OmsiHookRPCPlugin/OmsiHookRPCPlugin.cs @@ -12,6 +12,7 @@ using System.Buffers; using System.Threading.Tasks; using System.Reflection; +using System.Linq; namespace OmsiHookRPCPlugin { @@ -21,7 +22,7 @@ public class OmsiHookRPCPlugin /// /// Forces all RPC to occur in the main thread. Has a slight performance impact but is usually much more stable. /// - public const bool SINGLE_THREADED_EXECTION = true; + public const bool SINGLE_THREADED_EXECUTION = true; private static List threadPool; private static ConcurrentQueue callQueue; @@ -80,20 +81,22 @@ public static void PluginStart(IntPtr aOwner) try { File.Delete("omsiHookRPCPluginLog.txt"); - } catch { } - Log("############## Omsi Hook RPC Plugin ##############"); - Log($" version: {Assembly.GetExecutingAssembly().GetName().Version}"); - Log($" copyright Thomas Mathieson 2023"); + } + catch { } + Log($"################################# Omsi Hook RPC Plugin ##################################"); + Log($"~ version: {Assembly.GetExecutingAssembly().GetName().Version}"); + Log($"~ copyright Thomas Mathieson 2023-2024"); Log($"~ Omsi Hook RPC plugin is a simple plugin allowing OMSI mods which use the OmsiHook SDK ~"); Log($"~ to interact with OMSI from an external process. The source code is available at: ~"); Log($"~ https://github.com/space928/Omsi-Extensions ~"); + Log($"#########################################################################################"); Log($""); Log($@"Starting RPC server on named pipe: \\.\pipe\{PIPE_NAME_RX} and \\.\pipe\{PIPE_NAME_TX} with {MAX_CLIENTS} threads..."); - argumentArrayPool = ArrayPool.Create(256,8); + argumentArrayPool = ArrayPool.Create(256, 8); pipes = new(); - if (SINGLE_THREADED_EXECTION) + if (SINGLE_THREADED_EXECUTION) { callQueue = new(); returnPool = new(MAX_CLIENTS); @@ -101,11 +104,11 @@ public static void PluginStart(IntPtr aOwner) // We expect server threads to have a relatively long lifetime so for now, no need to have a complicated thread pool implementation. threadPool = new(); - for(int i = 0; i < MAX_CLIENTS; i++) + for (int i = 0; i < MAX_CLIENTS; i++) { - Thread thread = new(() => ServerThreadStart(i-1)); + Thread thread = new(() => ServerThreadStart(i - 1)); threadPool.Add(thread); - lock(returnPool) + lock (returnPool) returnPool.Add(new()); thread.Start(); } @@ -135,12 +138,14 @@ private static void ServerThreadStart(int threadId) Task.WaitAll(readTask, writeTask); Thread.Sleep(50); - } + } catch (AggregateException ex) { // Pipe closed normally, probably... - if (ex.InnerException.GetType() != typeof(EndOfStreamException)) - Log(ex.InnerException); + // if (ex.InnerException.GetType() != typeof(EndOfStreamException)) + // Log(ex.InnerException); + if (!ex.InnerExceptions.All(x => x.GetType() == typeof(EndOfStreamException))) + Log(ex); } catch (Exception ex) { @@ -152,63 +157,91 @@ private static void ServerThreadStart(int threadId) private static void WriteTask(int threadId, NamedPipeServerStream pipeTX) { - using BinaryWriter writer = new(pipeTX); - while (pipeTX.IsConnected) + try { - returnPool[threadId].isReady.Wait(); - lock (writeMutex) + using BinaryWriter writer = new(pipeTX); + while (pipeTX.IsConnected) { - if (returnPool[threadId].values.TryDequeue(out ReturnPromise ret)) + returnPool[threadId].isReady.Wait(); + lock (writeMutex) { - //Log($"[RPC Server {threadId}] writing: {ret.Val:X} for promise: 0x{ret.Promise:X8}"); - writer.Write(ret.Promise); - writer.Write(ret.Val); + if (returnPool[threadId].values.TryDequeue(out ReturnPromise ret)) + { + //Log($"[RPC Server {threadId}] writing: {ret.Val:X} for promise: 0x{ret.Promise:X8}"); + writer.Write(ret.Promise); + writer.Write(ret.Val); + } + else + Log($"[RPC Server {threadId}] tried to return a result, but no result was available!"); } - else - Log($"[RPC Server {threadId}] tried to return a result, but no result was available!"); + //Log($"[RPC Server {threadId}] Done!"); } - //Log($"[RPC Server {threadId}] Done!"); + } + catch (Exception ex) + { + Log($"[RPC Server {threadId}] Couldn't write to pipe: \n{ex}"); + throw; } } private static void ReadTask(int threadId, NamedPipeServerStream pipeRX, NamedPipeServerStream pipeTX) { - using BinaryReader reader = new(pipeRX); - while (pipeRX.IsConnected) + try { - // Read the message type from the pipe - RemoteMethod method = (RemoteMethod)reader.ReadInt32(); - int returnPromise = reader.ReadInt32(); + using BinaryReader reader = new(pipeRX); + while (pipeRX.IsConnected) + { + // Read the message type from the pipe + RemoteMethod method = (RemoteMethod)reader.ReadInt32(); + int returnPromise = reader.ReadInt32(); - int argBytes = RemoteMethodsArgsSizes[method]; - byte[] args = argumentArrayPool.Rent(argBytes); - //Log($"[RPC Server {threadId}] Remote method execute: '{method}' promise: 0x{returnPromise:X8}; reading {argBytes} bytes of arguments..."); + int argBytes = RemoteMethodsArgsSizes[method]; + byte[] args = argumentArrayPool.Rent(argBytes); +#if DEBUG + Log($"[RPC Server {threadId}] Remote method execute: '{method}' promise: 0x{returnPromise:X8}; reading {argBytes} bytes of arguments..."); +#endif - // Read all the arguments into a byte array - int read = reader.Read(args, 0, argBytes); - if (read < argBytes) - { - Log($"Only read {read} out of {argBytes} bytes of arguments for {method} call!"); - continue; - } + // Read all the arguments into a byte array + int read = reader.Read(args, 0, argBytes); + if (read < argBytes) + { + Log($"Only read {read} out of {argBytes} bytes of arguments for {method} call!"); + argumentArrayPool.Return(args); + continue; + } - if(method == RemoteMethod.CloseRPCConnection) - { - if (BitConverter.ToUInt32(args, 0) != 0) + if (method == RemoteMethod.None) { - foreach(var pipe in pipes) - pipe.Close(); + argumentArrayPool.Return(args); + continue; } - else + + if (method == RemoteMethod.CloseRPCConnection) { - pipeRX.Close(); - pipeTX.Close(); + if (BitConverter.ToUInt32(args, 0) != 0) + { + foreach (var pipe in pipes) + pipe.Close(); + } + else + { + pipeRX.Close(); + pipeTX.Close(); + } + argumentArrayPool.Return(args); + return; } - return; - } - callQueue.Enqueue(new(method, args, threadId, returnPromise)); - //Log($"[RPC Server {threadId}] method enqueued..."); + callQueue.Enqueue(new(method, args, threadId, returnPromise)); +#if DEBUG + Log($"[RPC Server {threadId}] method enqueued..."); +#endif + } + } + catch (Exception ex) + { + Log($"[RPC Server {threadId}] Couldn't read from pipe: \n{ex}"); + throw; } } @@ -220,67 +253,76 @@ private static void ProcessCall(MethodData methodData) { int ret = -1; int argInd = 0; +#if DEBUG + Log($"Processing call: {methodData.method} args[{methodData.args.Length}] promise:{methodData.returnPromise}"); +#endif switch (methodData.method) { case RemoteMethod.TProgManMakeVehicle: ret = NativeImports.TProgManMakeVehicle( - BitConverter.ToInt32(methodData.args, argInd), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToSingle(methodData.args, argInd+=4), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToInt32(methodData.args, argInd+=4), - methodData.args[argInd+=1], - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToInt32(methodData.args, argInd+=4), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToBoolean(methodData.args, argInd+=1), - BitConverter.ToInt32(methodData.args, argInd+=4) + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadFloat(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadU8(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; case RemoteMethod.TTempRVListCreate: ret = NativeImports.TTempRVListCreate( - BitConverter.ToInt32(methodData.args, argInd), - BitConverter.ToInt32(methodData.args, argInd += 4) + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd) + ); + break; + case RemoteMethod.CopyTempListIntoMainList: + ret = NativeImports.CopyTempListIntoMainList( + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; case RemoteMethod.TProgManPlaceRandomBus: ret = NativeImports.TProgManPlaceRandomBus( - BitConverter.ToInt32(methodData.args, argInd), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToSingle(methodData.args, argInd += 4), - BitConverter.ToBoolean(methodData.args, argInd += 1), - BitConverter.ToBoolean(methodData.args, argInd += 1), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToBoolean(methodData.args, argInd += 1), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToInt32(methodData.args, argInd += 4) + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadFloat(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadBool(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; case RemoteMethod.GetMem: //Log($"[RPC Main Thread] Executing GetMem(size={BitConverter.ToInt32(methodData.args, argInd)})"); ret = NativeImports.GetMem( - BitConverter.ToInt32(methodData.args, argInd) + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; case RemoteMethod.FreeMem: ret = NativeImports.FreeMem( - BitConverter.ToInt32(methodData.args, argInd) + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; case RemoteMethod.HookD3D: @@ -288,73 +330,88 @@ private static void ProcessCall(MethodData methodData) break; case RemoteMethod.CreateTexture: ret = NativeImports.CreateTexture( - BitConverter.ToUInt32(methodData.args, argInd), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4) + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd) ); break; case RemoteMethod.UpdateSubresource: ret = NativeImports.UpdateSubresource( - BitConverter.ToUInt32(methodData.args, argInd), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4) - ); + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd) + ); break; case RemoteMethod.ReleaseTexture: ret = NativeImports.ReleaseTexture( - BitConverter.ToUInt32(methodData.args, argInd) + FastBinaryReader.ReadU32(methodData.args, ref argInd) ); break; case RemoteMethod.GetTextureDesc: ret = NativeImports.GetTextureDesc( - BitConverter.ToUInt32(methodData.args, argInd), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4), - BitConverter.ToUInt32(methodData.args, argInd += 4) - ); + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd), + FastBinaryReader.ReadU32(methodData.args, ref argInd) + ); break; case RemoteMethod.GetTextureLevelCount: ret = unchecked((int)NativeImports.GetTextureLevelCount( - BitConverter.ToUInt32(methodData.args, argInd) + FastBinaryReader.ReadU32(methodData.args, ref argInd) )); break; case RemoteMethod.IsTexture: ret = NativeImports.IsTexture( - BitConverter.ToUInt32(methodData.args, argInd) + FastBinaryReader.ReadU32(methodData.args, ref argInd) ); break; case RemoteMethod.RVTriggerXML: ret = 0; NativeImports.RVTriggerXML( - BitConverter.ToInt32(methodData.args, argInd), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToInt32(methodData.args, argInd += 4) + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; case RemoteMethod.SoundTrigger: ret = 0; NativeImports.SoundTrigger( - BitConverter.ToInt32(methodData.args, argInd), - BitConverter.ToInt32(methodData.args, argInd += 4), - BitConverter.ToInt32(methodData.args, argInd += 4) + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd), + FastBinaryReader.ReadI32(methodData.args, ref argInd) + ); + break; + case RemoteMethod.SetCriticalSectionLock: + ret = 0; + NativeImports.SetCriticalSectionLock( + FastBinaryReader.ReadI32(methodData.args, ref argInd) + ); + break; + case RemoteMethod.ReleaseCriticalSectionLock: + ret = 0; + NativeImports.ReleaseCriticalSectionLock( + FastBinaryReader.ReadI32(methodData.args, ref argInd) ); break; default: Log($"Unknown message type: {methodData.method} encountered!"); break; } +#if DEBUG + Log($" returning {ret} to {methodData.returnPromise}..."); +#endif - returnPool[methodData.threadId].values.Enqueue(new (ret, methodData.returnPromise)); + returnPool[methodData.threadId].values.Enqueue(new(ret, methodData.returnPromise)); returnPool[methodData.threadId].isReady.Release(); argumentArrayPool.Return(methodData.args); } @@ -366,17 +423,24 @@ public static void PluginFinalize() Log("Exiting OmsiHookRPCPlugin..."); } - [UnmanagedCallersOnly(CallConvs = new[] {typeof(CallConvCdecl)}, EntryPoint = nameof(AccessVariable))] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) }, EntryPoint = nameof(AccessVariable))] public static void AccessVariable(ushort variableIndex, [C99Type("float*")] IntPtr value, [C99Type("__crt_bool*")] IntPtr writeValue) { - if (SINGLE_THREADED_EXECTION) + if (SINGLE_THREADED_EXECUTION) { while (!callQueue.IsEmpty) { if (callQueue.TryDequeue(out MethodData call)) { - ProcessCall(call); + try + { + ProcessCall(call); + } + catch (Exception ex) + { + Log($"Exception while processing call {call.returnPromise} for thread {call.threadId} method={call.method} args={call.args.Length}:\n{ex}"); + } } } } diff --git a/OmsiHookRPCPlugin/OmsiHookRPCPlugin.csproj b/OmsiHookRPCPlugin/OmsiHookRPCPlugin.csproj index 45b5597..a9ac0fa 100644 --- a/OmsiHookRPCPlugin/OmsiHookRPCPlugin.csproj +++ b/OmsiHookRPCPlugin/OmsiHookRPCPlugin.csproj @@ -5,7 +5,7 @@ Thomas Mathieson - 1.3.6 + 1.3.8 Thomas Mathieson