diff --git a/RNSReloaded.Interfaces/IBattlePatterns.cs b/RNSReloaded.Interfaces/IBattlePatterns.cs index 1d047e0..7ae2422 100644 --- a/RNSReloaded.Interfaces/IBattlePatterns.cs +++ b/RNSReloaded.Interfaces/IBattlePatterns.cs @@ -109,6 +109,8 @@ public void displaynumbers( public void enrage_deco(CInstance* self, CInstance* other, int? warningDelay = null, int? spawnDelay = null); + public void erase_patterns_network(CInstance* self, CInstance* other); + // (x, y) refers to the center of the field. Element is which color (purple, yellow, red, blue) public void fieldlimit_rectangle( CInstance* self, CInstance* other, Position? position = null, int? width = null, int? height = null, int? color = null, int? targetMask = null @@ -136,7 +138,7 @@ public void gravity_fall_temporary( ); public void gravity_pull( - CInstance* self, CInstance* other, double? mult = null + CInstance* self, CInstance* other, double? mult = null, Position? position = null ); public void gravity_pull_temporary( @@ -243,6 +245,10 @@ public void prsline_v( public void setgamespeed(CInstance* self, CInstance* other, int? spawnDelay = null, double? timeMult = null); + public void setgamespeed_temp(CInstance* self, CInstance* other, int? spawnDelay = null, int? eraseDelay = null, double? timeMult = null); + + public void setzoom(CInstance* self, CInstance* other, double? zoom = null); + public void showgroups( CInstance* self, CInstance* other, int? spawnDelay = null, int? eraseDelay = null, (int, int, int, int)? groupMasks = null ); @@ -252,11 +258,15 @@ public void showorder( ); public void tailwind( - CInstance* self, CInstance* other, int? eraseDelay = null + CInstance* self, CInstance* other, int? eraseDelay = null, int? spawnDelay = null, int? trgBinary = null, double? mult = null ); public void tailwind_permanent( - CInstance* self, CInstance* other + CInstance* self, CInstance* other, int? spawnDelay = null, int? trgBinary = null, double? mult = null + ); + + public void teleport_dist( + CInstance* self, CInstance* other, int? spawnDelay = null, int? eraseDelay = null, int? type = null, Position[]? offsets = null ); public void tether( diff --git a/RNSReloaded.Interfaces/IRNSReloaded.cs b/RNSReloaded.Interfaces/IRNSReloaded.cs index 4929795..9b2d2d1 100644 --- a/RNSReloaded.Interfaces/IRNSReloaded.cs +++ b/RNSReloaded.Interfaces/IRNSReloaded.cs @@ -20,6 +20,7 @@ public unsafe interface IRNSReloaded { public RFunctionStringRef GetTheFunction(int id); public CInstance* GetGlobalInstance(); public RValue* FindValue(CInstance* instance, string name); + public RValue* FindGlobalValue(string name); public RValue* ArrayGetEntry(RValue* array, int index); public RValue? ArrayGetLength(RValue* array); public string GetString(RValue* value); diff --git a/RNSReloaded.Interfaces/Structs.cs b/RNSReloaded.Interfaces/Structs.cs index 289d978..59f3b2e 100644 --- a/RNSReloaded.Interfaces/Structs.cs +++ b/RNSReloaded.Interfaces/Structs.cs @@ -3,6 +3,11 @@ // ReSharper disable InconsistentNaming namespace RNSReloaded.Interfaces.Structs; +public unsafe interface ILinkedListElement where T : unmanaged { + T* GetNext(); + T* GetPrev(); +} + public unsafe delegate RValue* ScriptDelegate( CInstance* self, CInstance* other, RValue* returnValue, int argc, RValue** argv ); @@ -69,9 +74,94 @@ public unsafe struct RValue { return IRNSReloaded.Instance.FindValue(this.Object, key); } + // TODO: what happens if index out of bounds? + public void Set(int index, RValue value) { + RValue[] args = [this, index, value]; + IRNSReloaded.Instance.ExecuteCodeFunction("array_set", null, null, args); + } + + public bool Set(string key, RValue value) { + if (this.Type != RValueType.Object) return false; + RValue[] args = [this.Object->ID, new RValue(key), value]; + IRNSReloaded.Instance.ExecuteCodeFunction("variable_instance_set", null, null, args); + return true; + } + // Thanks C# for making indexing a pointer with a number impossible lol - public RValue* this[int index] => this.Get(index); - public RValue* this[string key] => this.Get(key); + public RValue* this[int index] { + get => this.Get(index); + set => this.Set(index, *value); + } + public RValue* this[string key] { + get => this.Get(key); + set => this.Set(key, *value); + } + + /// Typesafe pointers I guess + // public ref RValue Get(int index) { + // if (this.Type != RValueType.Array) { + // throw new NotSupportedException("Only RValues of type Array may be accessed via ints"); + // } + // fixed (RValue* ptr = &this) { + // var value = IRNSReloaded.Instance.ArrayGetEntry(ptr, index); + // if (value == null) throw new IndexOutOfRangeException($"Failed to find RValue data at index {index}"); + // return ref *value; + // } + // } + + // public ref RValue Get(string key) { + // if (this.Type != RValueType.Object) { + // throw new NotSupportedException("Only RValues of type Object may be accessed via strings"); + // } + // var value = IRNSReloaded.Instance.FindValue(this.Object, key); + // if (value == null) { + // throw new KeyNotFoundException(); + // } + // return ref *value; + // } + + + // public ref RValue this[int index] => ref this.Get(index); + // public ref RValue this[string key] { + // get => ref this.Get(key); + // // Why no set access on refs ;-; + // // set { + // // try { + // // ref var holder = ref this.Get(key); + // // holder = value; + // // } catch (KeyNotFoundException) { + // // this.Set(key, value); + // // } + // // } + // } + + public int ArrayLength() { + var length = IRNSReloaded.Instance.ExecuteCodeFunction("array_length", null, null, [this]) ?? 0; + return (int)length; + } + + public static RValue? ArrayCreate(int size = 0, RValue? defaultVal = null) { + RValue[] args = [size]; + if (defaultVal != null) args = [..args, defaultVal.Value]; + var array = IRNSReloaded.Instance.ExecuteCodeFunction("array_create", null, null, args); + return array; + } + + public void ArrayPush(params RValue[] values) { + if (values.Count() == 0) { + return; + } + RValue[] args = [this, values[0]]; + if (values.Count() > 1) { + args = [..args, ..values[1..]]; + } + IRNSReloaded.Instance.ExecuteCodeFunction("array_push", null, null, args); + } + + public RValue ArrayPop() { + var ret = IRNSReloaded.Instance.ExecuteCodeFunction("array_push", null, null, [this]); + return ret ?? new RValue(); + } public override string ToString() { fixed (RValue* ptr = &this) { @@ -79,6 +169,75 @@ public override string ToString() { } } + // Technically this should cast ints to uint and return long + public int ToInt(int defaultValue = 0) { + return this.Type switch { + RValueType.Real => (int) this.Real, + RValueType.Int32 => this.Int32, + RValueType.Int64 => this.Int32, + RValueType.Bool => (int) this.Real, + _ => defaultValue, + }; + } + + // See INT64_RValue in a YYC decompilation (might need debug symbols) + private static long RealToLong(double value) { + if (Double.IsNaN(value)) { + return 0; + } else if (Double.IsPositiveInfinity(value)) { + return long.MaxValue; + } else if (Double.IsNegativeInfinity(value)) { + return long.MinValue; + } else { + return (int)value; + } + } + + public long ToLong(long defaultValue = 0) { + return this.Type switch { + RValueType.Real => RealToLong(this.Real), + RValueType.Int32 => this.Int32, + RValueType.Int64 => this.Int64, + RValueType.Bool => RealToLong(this.Real), + _ => defaultValue, + }; + } + + public double ToDouble(double defaultValue = 0) { + return this.Type switch { + RValueType.Real => this.Real, + RValueType.Int32 => (double) this.Int32, + RValueType.Int64 => (double)(int) this.Int64, + RValueType.Bool => this.Real, + _ => defaultValue, + }; + } + + public bool ToBool(bool defaultValue = false) { + return this.Type switch { + RValueType.Real => this.Real > 0.5, + RValueType.Int32 => this.Int32 > 0, + RValueType.Int64 => this.Int64 > 0, + RValueType.Bool => this.Real > 0.5, + _ => defaultValue, + }; + } + + public Span AsSpan() { + if (this.Type != RValueType.Array) return Span.Empty; + var length = this.ArrayLength(); + unsafe { + var arrPtr = this.Pointer + 8; + var arr = (RValue*) *(nint*) arrPtr; + return new Span(arr, length); + } + } + + public RValue[] ToArray() { + if (this.Type != RValueType.Array) return []; + return this.AsSpan().ToArray(); + } + // Constructors public RValue(CInstance* obj) { this.Type = RValueType.Object; @@ -92,6 +251,12 @@ public RValue(int value) { this.Flags = 0; } + public RValue(long value) { + this.Type = RValueType.Int64; + this.Int64 = value; + this.Flags = 0; + } + public RValue(double value) { this.Type = RValueType.Real; this.Real = value; @@ -104,13 +269,114 @@ public RValue(bool value) { this.Flags = 0; } - public static implicit operator RValue(CInstance* obj) => new(obj); + public RValue() { + this.Type = RValueType.Undefined; + this.Int64 = 0; + this.Flags = 0; + } + + public RValue(RValue[] array) { + this = ArrayCreate() ?? new RValue(); + this.ArrayPush(array); + } + + public RValue(string value) { + this = IRNSReloaded.Instance.utils.CreateString(value) ?? new RValue(); + } - // TODO: creating other primitives, new objects, and new arrays + public static implicit operator RValue(CInstance* obj) => new(obj); + public static implicit operator RValue(int value) => new(value); + public static implicit operator RValue(long value) => new(value); + public static implicit operator RValue(double value) => new(value); + public static implicit operator RValue(bool value) => new(value); + public static implicit operator RValue(RValue[] value) => new(value); + + public static explicit operator int(RValue value) => value.ToInt(); + public static explicit operator long(RValue value) => value.ToLong(); + public static explicit operator double(RValue value) => value.ToDouble(); + public static explicit operator bool(RValue value) => value.ToBool(); + + // TODO: creating other primitives, new objects + // TODO: instance enumerator } -[StructLayout(LayoutKind.Sequential, Pack = 8)] -public unsafe struct CInstance; // TODO +// CInstances from functions are laid out in 512 byte chunks at least +[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 512)] +public unsafe struct CInstance : ILinkedListElement { + // TODO + // 3 pointers related to vtables (see pdb types) + fixed byte Padding[176]; + // Alarms probably + // Should also contain the following at some point + // CLayer* layer; + // float collision_space; + + /// CInstance "internals" likely start here + + // Overview of the flags: + // 0. ? + // 1. persistent + // 2. solid? + // 3. visible + // 4. not-solid? + // 5-7. ? + // 8. solid? + // 9-31. ? + // Should have on_ui_layer + public int instanceFlags; + public int ID; + public int objectIndex; + public int spriteIndex; + public float sequencePosition; + public float lastSequencePosition; + public float sequenceDirection; + public float imageIndex; + public float time; + public float imageSpeed; + public float scaleX; + public float scaleY; + public float rotation; + public float alpha; + public int color; // or blend + public float x; + public float y; + public float startx; + public float starty; + public float xprevious; + public float yprevious; + public float direction; + public float speed; + public float friction; + public float gravity_direction; + public float gravity; + public float hspeed; + public float vspeed; + // Bounding box + public float BoxLeft; + public float BoxTop; + public float BoxRight; + public float BoxBottom; + public fixed int timers[12]; + public Int16 rollbackFrameKilled; + // 2 padding bytes + public void* TimelinePath; + public CCode* initCode; + public CCode* precreateCode; + public CObjectGM* oldObject; + public int layerID; + public int maskIndex; + public Int16 mouseOverCount; + // 2 padding bytes + public CInstance* Flink; + public CInstance* Blink; + fixed long pointers[9]; + public float depth; + public float Padding6; + public int Padding7; + + public CInstance* GetNext() => throw new NotImplementedException(); + public CInstance* GetPrev() => throw new NotImplementedException(); +} [StructLayout(LayoutKind.Sequential, Pack = 8)] public unsafe struct CScript { @@ -134,12 +400,42 @@ public unsafe struct RFunctionStringRef { } [StructLayout(LayoutKind.Sequential, Pack = 8)] -public unsafe struct LinkedList where T : unmanaged { +public unsafe struct LinkedList where T : unmanaged, ILinkedListElement { public T* First; public T* Last; public int Count; - // TODO: enumerator + public Enumerator GetEnumerator() => new Enumerator(this); + + public struct Enumerator { + private LinkedList list; + private T* curr; + private bool first; + + internal Enumerator(LinkedList list) { + this.list = list; + this.curr = list.First; + this.first = true; + } + + public T* Current => this.curr; + + // Contract says it should be run once before it points to first element + public bool MoveNext() { + if (!this.first) { + this.curr = this.curr->GetNext(); + } + this.first = false; + return this.curr != null; + } + + public void Reset() { + this.curr = this.list.First; + this.first = true; + } + + public void Dispose() { } + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] @@ -222,7 +518,7 @@ public unsafe struct CRoom { } [StructLayout(LayoutKind.Sequential, Pack = 8)] -public unsafe struct CLayer { +public unsafe struct CLayer : ILinkedListElement { public int ID; public int Depth; public float XOffset; @@ -251,6 +547,26 @@ public unsafe struct CLayer { public CLayer* Next; public CLayer* Previous; public nint GCProxy; + + public CLayer* GetNext() => this.Next; + public CLayer* GetPrev() => this.Previous; + + public List GetInstances() { + RValue[] instances = []; + foreach (var element in this.Elements) { + if (element->Type != LayerElementType.Instance) { continue; } + var instance = (CLayerInstanceElement*) element; + instances = [..instances, instance->Instance]; + } + return instances.ToList(); + } + + public string GetName() { + if (this.Name == null) { + return "Unnamed"; + } + return Marshal.PtrToStringAnsi((nint) this.Name) ?? "Unnamed"; + } } public enum LayerElementType { @@ -258,7 +574,7 @@ public enum LayerElementType { } [StructLayout(LayoutKind.Sequential, Pack = 8)] -public unsafe struct CLayerElementBase { +public unsafe struct CLayerElementBase : ILinkedListElement { public LayerElementType Type; public int ID; public byte RuntimeDataInitialized; @@ -269,6 +585,16 @@ public unsafe struct CLayerElementBase { public CLayerElementBase* Next; public CLayerElementBase* Previous; // Random 3 bytes of data, then 5 of pack, then another pointer. Who knows what that is though. + + public CLayerElementBase* GetNext() => this.Next; + public CLayerElementBase* GetPrev() => this.Previous; + + public string GetName() { + if (this.Name == null) { + return "Unnamed"; + } + return Marshal.PtrToStringAnsi((nint) this.Name) ?? "Unnamed"; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] @@ -325,6 +651,28 @@ public unsafe struct YYRoomInstances; [StructLayout(LayoutKind.Sequential, Pack = 8)] public unsafe struct CLayerEffectInfo; +[StructLayout(LayoutKind.Sequential, Pack = 8)] +public unsafe struct CObjectGM { + public readonly char* name; // const in struct.h + public CObjectGM* parentObject; + public CHashMap* childrenMap; + public CHashMap* eventsMap; + public LinkedList instances; + public LinkedList instancesRecursive; + public int flags; + public int spriteIndex; + public int depth; + public int parent; + public int mask; + public int ID; +}; + +[StructLayout(LayoutKind.Sequential, Pack = 8)] +public unsafe struct CEvent { + public CCode* code; + public int ownerObjectID; +} + [StructLayout(LayoutKind.Sequential, Pack = 8)] public unsafe struct RToken { public int Kind; diff --git a/RNSReloaded/BattlePatterns.cs b/RNSReloaded/BattlePatterns.cs index 6a35b31..e88682a 100644 --- a/RNSReloaded/BattlePatterns.cs +++ b/RNSReloaded/BattlePatterns.cs @@ -500,6 +500,10 @@ public void enrage_deco(CInstance* self, CInstance* other, int? warningDelay = n this.execute_pattern(self, other, "bp_enrage_deco", args); } + public void erase_patterns_network(CInstance* self, CInstance* other) { + this.execute_pattern(self, other, "bp_erase_patterns_network", []); + } + // (x, y) refers to the center of the field. Element is which color (purple, yellow, red, blue) public void fieldlimit_rectangle( CInstance* self, CInstance* other, Position? position = null, int? width = null, int? height = null, int? color = null, int? targetMask = null @@ -614,11 +618,16 @@ public void gravity_fall_temporary( } public void gravity_pull( - CInstance* self, CInstance* other, double? mult = null + CInstance* self, CInstance* other, double? mult = null, Position? position = null ) { RValue[] args = []; args = this.add_if_not_null(args, "mult", mult); + if (position != null) { + args = args.Concat([this.utils.CreateString("x")!.Value, new RValue(position.Value.x)]).ToArray(); + args = args.Concat([this.utils.CreateString("y")!.Value, new RValue(position.Value.y)]).ToArray(); + } + this.execute_pattern(self, other, "bp_gravity_pull", args); } @@ -1080,6 +1089,22 @@ public void setgamespeed(CInstance* self, CInstance* other, int? spawnDelay = nu this.execute_pattern(self, other, "bp_setgamespeed", args); } + public void setgamespeed_temp(CInstance* self, CInstance* other, int? spawnDelay = null, int? eraseDelay = null, double? timeMult = null) { + RValue[] args = []; + args = this.add_if_not_null(args, "timeMult", timeMult); + args = this.add_if_not_null(args, "spawnDelay", spawnDelay); + args = this.add_if_not_null(args, "eraseDelay", eraseDelay); + + this.execute_pattern(self, other, "bp_setgamespeed_temp", args); + } + + public void setzoom(CInstance* self, CInstance* other, double? zoom = null) { + RValue[] args = []; + args = this.add_if_not_null(args, "scale", zoom); + + this.execute_pattern(self, other, "bp_setzoom", args); + } + public void showgroups( CInstance* self, CInstance* other, int? spawnDelay = null, int? eraseDelay = null, (int, int, int, int)? groupMasks = null ) { @@ -1093,7 +1118,6 @@ public void showgroups( args = args.Concat([this.utils.CreateString("orderBin_2")!.Value, new RValue(groupMasks.Value.Item3)]).ToArray(); args = args.Concat([this.utils.CreateString("orderBin_3")!.Value, new RValue(groupMasks.Value.Item4)]).ToArray(); } - this.execute_pattern(self, other, "bp_showgroups", args); } @@ -1113,25 +1137,55 @@ public void showorder( args = args.Concat([this.utils.CreateString("orderBin_3")!.Value, new RValue(orderMasks.Value.Item4)]).ToArray(); } - this.execute_pattern(self, other, "bp_showorder", args); } public void tailwind( - CInstance* self, CInstance* other, int? eraseDelay = null + CInstance* self, CInstance* other, int? eraseDelay = null, int? spawnDelay = null, int? trgBinary = null, double? mult = null ) { RValue[] args = []; args = this.add_if_not_null(args, "eraseDelay", eraseDelay); + args = this.add_if_not_null(args, "spawnDelay", spawnDelay); + args = this.add_if_not_null(args, "trgBinary", trgBinary); + args = this.add_if_not_null(args, "mult", mult); + this.execute_pattern(self, other, "bp_tailwind", args); } public void tailwind_permanent( - CInstance* self, CInstance* other + CInstance* self, CInstance* other, int? spawnDelay = null, int? trgBinary = null, double? mult = null ) { RValue[] args = []; + args = this.add_if_not_null(args, "spawnDelay", spawnDelay); + args = this.add_if_not_null(args, "trgBinary", trgBinary); + args = this.add_if_not_null(args, "mult", mult); + this.execute_pattern(self, other, "bp_tailwind_permanent", args); } + public void teleport_dist( + CInstance* self, CInstance* other, int? spawnDelay = null, int? eraseDelay = null, int? type = null, Position[]? offsets = null + ) { + RValue[] args = []; + args = this.add_if_not_null(args, "spawnDelay", spawnDelay); + args = this.add_if_not_null(args, "eraseDelay", eraseDelay); + args = this.add_if_not_null(args, "type", type); + + if (offsets == null) { + this.execute_pattern(self, other, "bp_teleport_dist", args); + return; + } + + var i = 0; + foreach (var pos in offsets) { + args = this.add_if_not_null(args, "offX_" + i, pos.x); + args = this.add_if_not_null(args, "offY_" + i, pos.y); + i += 1; + } + + this.execute_pattern(self, other, "bp_teleport_dist", args); + } + public void tether( CInstance* self, CInstance* other, int? warningDelay = null, int? spawnDelay = null, int? eraseDelay = null, double? radius = null, int? targetMask = null ) { @@ -1268,7 +1322,7 @@ public void water2_line( if (position != null) { args = args.Concat([this.utils.CreateString("x")!.Value, new RValue(position.Value.x)]).ToArray(); args = args.Concat([this.utils.CreateString("y")!.Value, new RValue(position.Value.y)]).ToArray(); - } + } this.execute_pattern(self, other, "bp_water2_line", args); } diff --git a/RNSReloaded/Functions.cs b/RNSReloaded/Functions.cs index e4f68e5..d7e6fb6 100644 --- a/RNSReloaded/Functions.cs +++ b/RNSReloaded/Functions.cs @@ -13,7 +13,6 @@ public unsafe class Functions { public delegate RValue* FindValueDelegate(CInstance* instance, char* name); public delegate RValue* ArrayGetEntryDelegate(nint array, int index); public delegate char* YYGetStringDelegate(RValue* value, int unk); - public delegate int StructGetKeysDelegate(RValue* value, char** keys, int* count); public delegate int YYCreateStringDelegate(RValue* value, char* str); public delegate RValue* CallScriptFunctionDelegate(CInstance* self, CInstance* other, RValue* result, int num_args, int id, RValue** args, long _, long __); @@ -24,7 +23,6 @@ public unsafe class Functions { public FindValueDelegate FindValue = null!; public ArrayGetEntryDelegate ArrayGetEntry = null!; public YYGetStringDelegate YYGetString = null!; - public StructGetKeysDelegate StructGetKeys = null!; public YYCreateStringDelegate YYCreateString = null!; public CallScriptFunctionDelegate callScriptFunction = null!; @@ -47,8 +45,6 @@ public Functions(ScanUtils utils, WeakReference scannerRef) { addr => { this.FindValue = Marshal.GetDelegateForFunctionPointer(addr); }); this.utils.Scan("40 53 56 48 83 EC ?? 48 63 F2", addr => { this.YYGetString = Marshal.GetDelegateForFunctionPointer(addr); }); - this.utils.Scan("48 83 EC 38 48 89 74 24 ??", - addr => { this.StructGetKeys = Marshal.GetDelegateForFunctionPointer(addr); }); this.utils.Scan("E8 ?? ?? ?? ?? 48 8B 5C 24 ?? 48 83 C4 ?? 5F C3 4C 3B 05", addr => { this.YYCreateString = Marshal.GetDelegateForFunctionPointer(addr); }); this.utils.Scan("40 55 41 54 41 55 41 56 41 57 48 83 EC ?? 48 8D 6C 24 ?? 48 89 5D ?? 48 89 75 ?? 48 89 7D ?? 48 8B 05 ?? ?? ?? ?? 48 33 C5 48 89 45 ?? 48 63 45", addr => { diff --git a/RNSReloaded/ModConfig.json b/RNSReloaded/ModConfig.json index 750d8d4..f21f901 100644 --- a/RNSReloaded/ModConfig.json +++ b/RNSReloaded/ModConfig.json @@ -2,7 +2,7 @@ "ModId": "RNSReloaded", "ModName": "RNSReloaded", "ModAuthor": "NotNite", - "ModVersion": "1.1.22", + "ModVersion": "1.1.23", "ModDescription": "Helper library for Rabbit and Steel. Can be adapted to general GameMaker.", "ModDll": "RNSReloaded.dll", "ModIcon": "", diff --git a/RNSReloaded/RNSReloaded.cs b/RNSReloaded/RNSReloaded.cs index 3fb0ec7..639c422 100644 --- a/RNSReloaded/RNSReloaded.cs +++ b/RNSReloaded/RNSReloaded.cs @@ -110,6 +110,10 @@ public RFunctionStringRef GetTheFunction(int id) { return ret; } + public RValue* FindGlobalValue(string name) { + return this.FindValue(this.GetGlobalInstance(), name); + } + public RValue* ArrayGetEntry(RValue* array, int index) { if (array == null) return null; if (array->Type != RValueType.Array) return null; @@ -140,17 +144,13 @@ public string GetString(RValue* value) { } public List GetStructKeys(RValue* value) { - var ret = new List(); - var count = this.functions.StructGetKeys(value, null, null); - var keys = new char*[count]; - - fixed (char** keysPtr = keys) { - this.functions.StructGetKeys(value, keysPtr, &count); - for (var i = 0; i < count; i++) { - ret.Add(Marshal.PtrToStringAnsi((nint) keys[i])!); - } + if (value->Type != RValueType.Object) return []; + RValue[] args = [value->Object]; + RValue arr = IRNSReloaded.Instance.ExecuteCodeFunction("variable_instance_get_names", null, null, args) ?? new RValue([]); + List ret = []; + foreach (var key in arr.AsSpan()) { + ret.Add(key.ToString()); } - return ret; } diff --git a/RNSReloaded/Util.cs b/RNSReloaded/Util.cs index 1f91a5c..26b0dc3 100644 --- a/RNSReloaded/Util.cs +++ b/RNSReloaded/Util.cs @@ -1,5 +1,3 @@ -using System.Drawing; -using System.Runtime.InteropServices; using Reloaded.Mod.Interfaces.Internal; using RNSReloaded.Interfaces; using RNSReloaded.Interfaces.Structs; @@ -128,5 +126,4 @@ public void setHallway(List hallway, CInstance* self, IRNSReloaded rnsRel notchNum->Real = hallway.Count; notchNum->Type = RValueType.Real; } - }