namespace XFEWarframe.Bridge;
internal static unsafe partial class GameTestLua
{
internal readonly record struct Ref(int Index);
// Used by fixed production HUD calls as well as the opt-in test adapter.
// Each scope owns its stack values; Lua tables own all retained resources.
internal sealed class Scope : IDisposable
{
private readonly nint state;
private readonly int saved;
private bool returnsValue;
internal Scope(nint scriptState = 0)
{
state = scriptState != 0 ? scriptState : *(nint*)(image + 0x2734D40);
if (state == 0) throw new InvalidOperationException("Game scripts are not initialized.");
saved = Top(state);
((delegate* unmanaged<nint, int, void>)(image + 0x11E1C40))(state, 1024);
}
internal int Count => Top(state);
internal Ref At(int index) => new(index);
internal Ref NativeFunction(nint function, Ref? captured = null)
{
if (captured is { } value) Copy(state, value.Index);
((delegate* unmanaged<nint, nint, nint, int, nint, void>)(image + 0x80DFE0))(state, function, 0, captured is null ? 0 : 1, 0);
return new(Top(state));
}
internal Ref Captured(int index) { Copy(state, -10002 - index); return new(Top(state)); }
internal void AssignRaw(nint target, Ref source) => new ReadOnlySpan<byte>((void*)Value(state, source.Index), 16).CopyTo(new Span<byte>((void*)target, 16));
internal Ref RawValue(nint source)
{
var destination = *(nint*)(state + 8);
new ReadOnlySpan<byte>((void*)source, 16).CopyTo(new Span<byte>((void*)destination, 16));
*(nint*)(state + 8) = destination + 16;
return new(Top(state));
}
internal void Return(Ref result)
{
new ReadOnlySpan<byte>((void*)Value(state, result.Index), 16).CopyTo(new Span<byte>((void*)Value(state, saved + 1), 16));
returnsValue = true;
}
internal Ref Global(string name) { Get(state, -10002, name); return new(Top(state)); }
internal Ref Field(Ref owner, string name, bool literal = true)
{ Get(state, owner.Index, name, literal); return new(Top(state)); }
internal int Kind(Ref value) => Type(state, value.Index);
internal nint Object(Ref value)
{
if (Kind(value) != 9) return 0;
var reference = *(nint*)(*(nint*)Value(state, value.Index) + 0x18);
return reference == 0 ? 0 : *(nint*)reference;
}
internal nint ResourceType(Ref value) { var instance = Object(value); return instance == 0 ? 0 : *(nint*)(instance + 8); }
internal nint Identity(Ref value) => *(nint*)Value(state, value.Index);
internal float Number(Ref value) => Kind(value) == 3 ? *(float*)Value(state, value.Index) : 0;
internal string? Text(Ref value) => Kind(value) == 6 ? Describe(Value(state, value.Index))["text"]!.GetValue<string>() : null;
internal bool Boolean(Ref value) => Kind(value) == 1 && *(uint*)Value(state, value.Index) != 0;
internal Ref Table(int array = 0, int fields = 0)
{
((delegate* unmanaged<nint, int, int, void>)(image + 0xFADDA0))(state, array, fields);
return new(Top(state));
}
internal Ref Call(Ref function, params object?[] arguments)
{
if (Kind(function) != 8) throw new InvalidOperationException("HUD function is unavailable.");
Copy(state, function.Index);
foreach (var argument in arguments) Push(argument);
GameTestLua.Call(state, arguments.Length);
return new(Top(state));
}
internal Ref Method(Ref owner, string name, params object?[] arguments)
{
var before = Top(state);
Get(state, owner.Index, name, false);
if (Type(state, Top(state)) != 8) throw new InvalidOperationException("HUD method is unavailable: " + name);
Copy(state, owner.Index);
foreach (var argument in arguments) Push(argument);
try { GameTestLua.Call(state, arguments.Length + 1); }
catch (Exception ex) { throw new InvalidOperationException("HUD method " + name + ": " + ex.Message, ex); }
if (Top(state) != before + 1) throw new InvalidOperationException("Unexpected HUD stack result.");
return new(Top(state));
}
internal Ref Raw(Ref table, object key) => Call(Global("rawget"), table, key);
internal void Set(Ref table, object key, object? value)
{
var before = Top(state);
Call(Global("rawset"), table, key, value);
SetTop(state, before);
}
internal Ref Load(string path) => Call(Global("Type"), path);
internal Ref Require(string module) => Call(Global("require"), module);
private void Push(object? argument)
{
if (argument is Ref reference) { Copy(state, reference.Index); return; }
if (argument is string text) { PushString(state, text); return; }
var value = *(nint*)(state + 8); new Span<byte>((void*)value, 16).Clear();
if (argument is bool flag) { *(uint*)value = flag ? 1u : 0u; *(int*)(value + 12) = 1; }
else if (argument is not null) { *(float*)value = Convert.ToSingle(argument, System.Globalization.CultureInfo.InvariantCulture); *(int*)(value + 12) = 3; }
*(nint*)(state + 8) = value + 16;
}
public void Dispose() => SetTop(state, saved + (returnsValue ? 1 : 0));
}
}
namespace XFEWarframe.Bridge;
internal static unsafe partial class GameTestLua
{
internal readonly record struct Ref(int Index);
// Used by fixed production HUD calls as well as the opt-in test adapter.
// Each scope owns its stack values; Lua tables own all retained resources.
internal sealed class Scope : IDisposable
{
private readonly nint state;
private readonly int saved;
private bool returnsValue;
internal Scope(nint scriptState = 0)
{
state = scriptState != 0 ? scriptState : *(nint*)(image + 0x2734D40);
if (state == 0) throw new InvalidOperationException("Game scripts are not initialized.");
saved = Top(state);
((delegate* unmanaged<nint, int, void>)(image + 0x11E1C40))(state, 1024);
}
internal int Count => Top(state);
internal Ref At(int index) => new(index);
internal Ref NativeFunction(nint function, Ref? captured = null)
{
if (captured is { } value) Copy(state, value.Index);
((delegate* unmanaged<nint, nint, nint, int, nint, void>)(image + 0x80DFE0))(state, function, 0, captured is null ? 0 : 1, 0);
return new(Top(state));
}
internal Ref Captured(int index) { Copy(state, -10002 - index); return new(Top(state)); }
internal void AssignRaw(nint target, Ref source) => new ReadOnlySpan<byte>((void*)Value(state, source.Index), 16).CopyTo(new Span<byte>((void*)target, 16));
internal Ref RawValue(nint source)
{
var destination = *(nint*)(state + 8);
new ReadOnlySpan<byte>((void*)source, 16).CopyTo(new Span<byte>((void*)destination, 16));
*(nint*)(state + 8) = destination + 16;
return new(Top(state));
}
internal void Return(Ref result)
{
new ReadOnlySpan<byte>((void*)Value(state, result.Index), 16).CopyTo(new Span<byte>((void*)Value(state, saved + 1), 16));
returnsValue = true;
}
internal Ref Global(string name) { Get(state, -10002, name); return new(Top(state)); }
internal Ref Field(Ref owner, string name, bool literal = true)
{ Get(state, owner.Index, name, literal); return new(Top(state)); }
internal int Kind(Ref value) => Type(state, value.Index);
internal nint Object(Ref value)
{
if (Kind(value) != 9) return 0;
var reference = *(nint*)(*(nint*)Value(state, value.Index) + 0x18);
return reference == 0 ? 0 : *(nint*)reference;
}
internal nint ResourceType(Ref value) { var instance = Object(value); return instance == 0 ? 0 : *(nint*)(instance + 8); }
internal nint Identity(Ref value) => *(nint*)Value(state, value.Index);
internal float Number(Ref value) => Kind(value) == 3 ? *(float*)Value(state, value.Index) : 0;
internal string? Text(Ref value) => Kind(value) == 6 ? Describe(Value(state, value.Index))["text"]!.GetValue<string>() : null;
internal bool Boolean(Ref value) => Kind(value) == 1 && *(uint*)Value(state, value.Index) != 0;
internal Ref Table(int array = 0, int fields = 0)
{
((delegate* unmanaged<nint, int, int, void>)(image + 0xFADDA0))(state, array, fields);
return new(Top(state));
}
internal Ref Call(Ref function, params object?[] arguments)
{
if (Kind(function) != 8) throw new InvalidOperationException("HUD function is unavailable.");
Copy(state, function.Index);
foreach (var argument in arguments) Push(argument);
GameTestLua.Call(state, arguments.Length);
return new(Top(state));
}
internal Ref Method(Ref owner, string name, params object?[] arguments)
{
var before = Top(state);
Get(state, owner.Index, name, false);
if (Type(state, Top(state)) != 8) throw new InvalidOperationException("HUD method is unavailable: " + name);
Copy(state, owner.Index);
foreach (var argument in arguments) Push(argument);
try { GameTestLua.Call(state, arguments.Length + 1); }
catch (Exception ex) { throw new InvalidOperationException("HUD method " + name + ": " + ex.Message, ex); }
if (Top(state) != before + 1) throw new InvalidOperationException("Unexpected HUD stack result.");
return new(Top(state));
}
internal Ref Raw(Ref table, object key) => Call(Global("rawget"), table, key);
internal void Set(Ref table, object key, object? value)
{
var before = Top(state);
Call(Global("rawset"), table, key, value);
SetTop(state, before);
}
internal Ref Load(string path) => Call(Global("Type"), path);
internal Ref Require(string module) => Call(Global("require"), module);
private void Push(object? argument)
{
if (argument is Ref reference) { Copy(state, reference.Index); return; }
if (argument is string text) { PushString(state, text); return; }
var value = *(nint*)(state + 8); new Span<byte>((void*)value, 16).Clear();
if (argument is bool flag) { *(uint*)value = flag ? 1u : 0u; *(int*)(value + 12) = 1; }
else if (argument is not null) { *(float*)value = Convert.ToSingle(argument, System.Globalization.CultureInfo.InvariantCulture); *(int*)(value + 12) = 3; }
*(nint*)(state + 8) = value + 16;
}
public void Dispose() => SetTop(state, saved + (returnsValue ? 1 : 0));
}
}