From d1e309cc121c26d9d88649654e55d45deed6d8dd Mon Sep 17 00:00:00 2001 From: Thomas Mathieson Date: Fri, 15 Apr 2022 22:57:31 +0100 Subject: [PATCH] + Added custom marshalling for pointers to OmsiObject and arrays of structs and OmsiObjects in structs + Added TicketPack to OmsiHook + Added a couple more bits to OmsiMap + Added skeletons for D3DMeshFileObject and its parents * Refactoring --- OmsiExtensionsCLI/Program.cs | 2 +- OmsiHook/CustomAttributes.cs | 84 ++++++++++++++++++++++++++++++++++ OmsiHook/D3DMeshFileObject.cs | 8 ++++ OmsiHook/D3DMeshObject.cs | 8 ++++ OmsiHook/D3DObject.cs | 8 ++++ OmsiHook/D3DTransformObject.cs | 8 ++++ OmsiHook/Memory.cs | 36 +++++++++++++-- OmsiHook/OmsiHook.cs | 2 + OmsiHook/OmsiMap.cs | 18 +++++++- OmsiHook/OmsiStructs.cs | 52 ++++++++++++++++++++- 10 files changed, 217 insertions(+), 9 deletions(-) create mode 100644 OmsiHook/D3DMeshFileObject.cs create mode 100644 OmsiHook/D3DMeshObject.cs create mode 100644 OmsiHook/D3DObject.cs create mode 100644 OmsiHook/D3DTransformObject.cs diff --git a/OmsiExtensionsCLI/Program.cs b/OmsiExtensionsCLI/Program.cs index 53ae656..8de0d3e 100644 --- a/OmsiExtensionsCLI/Program.cs +++ b/OmsiExtensionsCLI/Program.cs @@ -18,7 +18,7 @@ static void Main(string[] args) var pos = omsi.PlayerVehicle.Position; var map = omsi.Map; var weather = omsi.Weather; - var water = map.Water_Matl; + var ticketPack = omsi.TicketPack; Console.WriteLine($"Read data: x:{pos.x:F3}\ty:{pos.y:F3}\tz:{pos.z:F3}\t\t" + $"tile:{0}\trow45:{0:F3}\trow47:{0:F3}"); diff --git a/OmsiHook/CustomAttributes.cs b/OmsiHook/CustomAttributes.cs index 3c94b25..ff8e983 100644 --- a/OmsiHook/CustomAttributes.cs +++ b/OmsiHook/CustomAttributes.cs @@ -31,4 +31,88 @@ sealed class OmsiPtrAttribute : Attribute { } + + /// + /// Marks a field to be converted from an int to an OmsiObject. + /// Used by Memory.MarshalStruct() + /// + [System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + sealed class OmsiObjPtrAttribute : Attribute + { + // See the attribute guidelines at + // http://go.microsoft.com/fwlink/?LinkId=85236 + readonly Type objType; + + // This is a positional argument + public OmsiObjPtrAttribute(Type objType) + { + if (!objType.IsSubclassOf(typeof(OmsiObject))) + throw new ArgumentException("OmsiObjPtr must be a pointer to an object deriving from " + nameof(OmsiObject) + "!"); + + this.objType = objType; + } + + public Type ObjType => objType; + } + + /// + /// Marks a field to be converted from an int to an OmsiObject[]. + /// Used by Memory.MarshalStruct() + /// + [System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + sealed class OmsiObjArrayPtrAttribute : Attribute + { + // See the attribute guidelines at + // http://go.microsoft.com/fwlink/?LinkId=85236 + readonly Type objType; + + // This is a positional argument + public OmsiObjArrayPtrAttribute(Type objType) + { + if (objType.IsSubclassOf(typeof(OmsiObject))) + throw new ArgumentException("OmsiObjArrayPtr must be a pointer to an object deriving from " + nameof(OmsiObject) + "!"); + + this.objType = objType; + } + + public Type ObjType => objType; + } + + /// + /// Marks a field to be converted from an int to an OmsiObject[]. + /// Used by Memory.MarshalStruct() + /// + [System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + sealed class OmsiStructArrayPtrAttribute : Attribute + { + // See the attribute guidelines at + // http://go.microsoft.com/fwlink/?LinkId=85236 + readonly Type objType; + readonly Type internalType; + readonly bool requiresExtraMarshalling; + + // This is a positional argument + /// + /// + /// + /// The type of the object to convert to + /// The intermidiate type to convert through + /// (in case Marshal.PtrToStruct doesn't support all the fields in objType). + /// Leave null to default to objType + public OmsiStructArrayPtrAttribute(Type objType, Type internalType = null) + { + if (!objType.IsValueType) + throw new ArgumentException("OmsiStructArrayPtr must be a pointer to a struct/value!"); + if ((!internalType?.IsValueType) ?? false) + throw new ArgumentException("OmsiStructArrayPtr must be a pointer to a struct/value!"); + + this.objType = objType; + this.requiresExtraMarshalling = internalType != null; + this.internalType = internalType ?? objType; + } + + public Type ObjType => objType; + public Type InternalType => internalType; + public bool RequiresExtraMarshalling => requiresExtraMarshalling; + } } diff --git a/OmsiHook/D3DMeshFileObject.cs b/OmsiHook/D3DMeshFileObject.cs new file mode 100644 index 0000000..0e1ae9b --- /dev/null +++ b/OmsiHook/D3DMeshFileObject.cs @@ -0,0 +1,8 @@ +namespace OmsiHook +{ + public class D3DMeshFileObject : D3DMeshObject + { + internal D3DMeshFileObject(Memory omsiMemory, int baseAddress) : base(omsiMemory, baseAddress) { } + internal D3DMeshFileObject() : base() { } + } +} \ No newline at end of file diff --git a/OmsiHook/D3DMeshObject.cs b/OmsiHook/D3DMeshObject.cs new file mode 100644 index 0000000..4443dde --- /dev/null +++ b/OmsiHook/D3DMeshObject.cs @@ -0,0 +1,8 @@ +namespace OmsiHook +{ + public class D3DMeshObject : D3DTransformObject + { + internal D3DMeshObject(Memory omsiMemory, int baseAddress) : base(omsiMemory, baseAddress) { } + internal D3DMeshObject() : base() { } + } +} \ No newline at end of file diff --git a/OmsiHook/D3DObject.cs b/OmsiHook/D3DObject.cs new file mode 100644 index 0000000..7804072 --- /dev/null +++ b/OmsiHook/D3DObject.cs @@ -0,0 +1,8 @@ +namespace OmsiHook +{ + public class D3DObject : OmsiObject + { + internal D3DObject(Memory omsiMemory, int baseAddress) : base(omsiMemory, baseAddress) { } + internal D3DObject() : base() { } + } +} \ No newline at end of file diff --git a/OmsiHook/D3DTransformObject.cs b/OmsiHook/D3DTransformObject.cs new file mode 100644 index 0000000..a634017 --- /dev/null +++ b/OmsiHook/D3DTransformObject.cs @@ -0,0 +1,8 @@ +namespace OmsiHook +{ + public class D3DTransformObject : D3DObject + { + internal D3DTransformObject(Memory omsiMemory, int baseAddress) : base(omsiMemory, baseAddress) { } + internal D3DTransformObject() : base() { } + } +} \ No newline at end of file diff --git a/OmsiHook/Memory.cs b/OmsiHook/Memory.cs index 4a1ebb2..46ecbda 100644 --- a/OmsiHook/Memory.cs +++ b/OmsiHook/Memory.cs @@ -223,19 +223,45 @@ public OutStruct MarshalStruct(InStruct obj) foreach (var field in obj.GetType().GetFields()) { object val = field.GetValue(obj); - foreach (var attr in field.CustomAttributes) + foreach (var attr in field.GetCustomAttributes(false)) { - switch (attr.AttributeType.Name) + // Based on which kind of attribute the field has, perform special marshalling operations + switch (attr) { - case nameof(OmsiStrPtrAttribute): - val = ReadMemoryString((int)val, (bool)attr.ConstructorArguments[0].Value); + case OmsiStrPtrAttribute a: + val = ReadMemoryString((int)val, a.Wide); break; - case nameof(OmsiPtrAttribute): + + case OmsiPtrAttribute: val = new IntPtr((int)val); break; + + case OmsiObjPtrAttribute a: + int addr = (int)val; + val = Activator.CreateInstance(a.ObjType, true); + ((OmsiObject)val).InitObject(this, addr); + break; + + case OmsiStructArrayPtrAttribute a: + val = typeof(Memory).GetMethod(nameof(ReadMemoryStructArray)) + .MakeGenericMethod(a.InternalType) + .Invoke(this, new object[] { val }); + // Perform extra marshalling if needed + if(a.RequiresExtraMarshalling) + val = typeof(Memory).GetMethod(nameof(MarshalStructs)) + .MakeGenericMethod(a.ObjType, a.InternalType) + .Invoke(this, new object[] { val }); + break; + + case OmsiObjArrayPtrAttribute a: + val = typeof(Memory).GetMethod(nameof(ReadMemoryObjArray)) + .MakeGenericMethod(a.ObjType) + .Invoke(this, new object[] { val }); + break; } } + // Match fields by name, setting the destination fields to the corresponding source fields typeof(OutStruct).GetField(field.Name).SetValue(ret, val); } diff --git a/OmsiHook/OmsiHook.cs b/OmsiHook/OmsiHook.cs index 90b88a3..44faf8b 100644 --- a/OmsiHook/OmsiHook.cs +++ b/OmsiHook/OmsiHook.cs @@ -20,6 +20,8 @@ public class OmsiHook public int PlayerVehicleIndex => omsiMemory.ReadMemory(0x00861740); public OmsiWeather Weather => new(omsiMemory, omsiMemory.ReadMemory(0x008617D0)); public OmsiMap Map => new(omsiMemory, omsiMemory.ReadMemory(0x861588)); + public OmsiTicketPack TicketPack => omsiMemory.MarshalStruct( + omsiMemory.ReadMemory(0x008611fc)); /// /// Attaches the hooking application to OMSI.exe. diff --git a/OmsiHook/OmsiMap.cs b/OmsiHook/OmsiMap.cs index d01a257..13b7c18 100644 --- a/OmsiHook/OmsiMap.cs +++ b/OmsiHook/OmsiMap.cs @@ -267,6 +267,22 @@ public bool Tile_Aerial_Visible set => Memory.WriteMemory(Address + 0x184, value); } - //TODO: Many more... + public int GridTexture + { + get => Memory.ReadMemory(Address + 0x188); + set => Memory.WriteMemory(Address + 0x188, value); + } + + public float WellenAnimation + { + get => Memory.ReadMemory(Address + 0x18c); + set => Memory.WriteMemory(Address + 0x18c, value); + } + + /*public float[] WellenAnimation_P + { + get => Memory.ReadMemoryStructArray(Address + 0x184); + set => Memory.WriteMemory(Address + 0x184, value); + }*/ } } diff --git a/OmsiHook/OmsiStructs.cs b/OmsiHook/OmsiStructs.cs index 96a0f6a..1ddcec0 100644 --- a/OmsiHook/OmsiStructs.cs +++ b/OmsiHook/OmsiStructs.cs @@ -279,7 +279,7 @@ public struct OmsiWeatherProp public bool schneeAufStrassen; } - internal struct CloudTypeInternal + internal struct OmsiCloudTypeInternal { [OmsiStrPtr] public int name; // ANSI String [OmsiStrPtr] public int texFile; // ANSI String @@ -287,7 +287,7 @@ internal struct CloudTypeInternal public bool ovc; } - public struct CloudType + public struct OmsiCloudType { public string name; // ANSI String public string texFile; // ANSI String @@ -297,4 +297,52 @@ public struct CloudType /// public bool ovc; } + + public struct OmsiTicketPackInternal + { + [OmsiStrPtr] public int filename; + [OmsiStrPtr(true)] public int voicepath; + [OmsiStructArrayPtr(typeof(OmsiTicket), typeof(OmsiTicketInternal))] + public int tickets; + public float stamper_prop; + public float ticketBuy_prop; + public float chattiness; + public float whinge_prop; + } + + public struct OmsiTicketPack + { + public string filename; + public string voicepath; + public OmsiTicket[] tickets; + public float stamper_prop; + public float ticketBuy_prop; + public float chattiness; + public float whinge_prop; + } + + public struct OmsiTicketInternal + { + //TODO: I don't think these are decoding correctly (I saw nothing when I looked), check this actually works. + [OmsiStrPtr] public int name, name_english, name_display; + public int max_stations; + public int age_min, age_max; + public float value; + public bool dayTicket; + public float propability; + [OmsiObjPtr(typeof(D3DMeshFileObject))] public int mesh_block; + [OmsiObjPtr(typeof(D3DMeshFileObject))] public int mesh_single; + } + + public struct OmsiTicket + { + public string name, name_english, name_display; + public int max_stations; + public int age_min, age_max; + public float value; + public bool dayTicket; + public float propability; + public D3DMeshFileObject mesh_block; + public D3DMeshFileObject mesh_single; + } }