Ptr<T> and MarshalledPtr<T>
Abstraction for a pointer to a value of type T
.
This type was formerly called BlittablePointer<T>
and ArrayPtr<T>
in old library versions.
Zero overhead but not 1:1 codegen in all scenarios. e.g. Multiplying an element in place via Setter can be slower (.NET 7).
A helper to allow you to do two things:
- Use pointer arithmetic on anything implementing
ICanReadWriteMemory
. (e.g. Memory of External Process). - To use in generics.
The type MarshalledPtr<T>
also exists, this is a special type of Ptr<T>
that marshals the elements as they are read/written;
with pointer arithmetic being performed on the marshalled size of the object rather than the raw size.
Examples
Basically, the Ptr<T>
type is just a regular pointer, with all of the operations you would expect from a pointer; except that it can be used in Generics, and also read/write to more than just RAM.
Initialization
The Ptr<T>
type supports implicit conversions to/from raw pointers.
int someValue = 42;
Ptr<int> ptr = &someValue;
int* ptr2 = intPtr;
AsRef
Pointers can be converted for references.
ref int valueFromPtr = ref intPtr.AsRef();
Console.WriteLine($"Value from pointer: {valueFromPtr}");
This can be especially useful for game modding APIs, the end user can for example do this:
ref var playerCount = ref State.NumberOfPlayers.AsRef();
playerCount = 5;
Completely avoiding pointer arithmetic; which is friendly to non-programmers.
Pointer Arithmetic
You can do regular arithmetic on pointers, e.g. add 1 to go to next value.
Ptr<int> arrayIntPtr = arrayPtr;
Ptr<int> offsetArrayIntPtr = arrayIntPtr + 2;
Console.WriteLine($"Value at original pointer: {arrayIntPtr.AsRef()}"); // Output: Value at original pointer: 1
Console.WriteLine($"Value at offset pointer: {offsetArrayIntPtr.AsRef()}"); // Output: Value at offset pointer: 3
Console.WriteLine($"Equal? {offsetArrayIntPtr != arrayIntPtr}"); // Equal? false
// You can also do ++ and --
arrayIntPtr++;
arrayIntPtr++;
// Now arrayIntPtr points to the third element in the array.
Value Read / Write
You can read/write values with an implementation of ICanReadWriteMemory
Reading from RAM:
int valueFromSource = pointer.Get(); // implicit Memory.Instance
Console.WriteLine($"Value from RAM: {valueFromSource}");
Reading from RAM of another process:
// externalMemory = new ExternalMemory(anotherProcess);
int valueFromSource = pointer.Get(externalMemory);
Console.WriteLine($"Value from another process' RAM: {valueFromSource}");
You can also read/write to offsets:
int valueAtOffset2 = pointer.Get(2);
Console.WriteLine($"Value from RAM (Offset 2): {valueAtOffset2}");
Branch on Null Pointer
Just like in C, you can branch into an if statement if a pointer isn't null.
var notNullPointer = new Ptr<int>((int*)0x12345678);
if (notNullPointer)
Console.WriteLine("Pointer is not null!");
From External Libraries
Getting a ref to native memory/pointer:
// Defined in a library for modding a certain hoverboard racing game.
public static readonly Ptr<int> NumberOfRacers = new Ptr<int>((int*)0x64B758);
// End user can do either
int* racers = NumberOfRacers;
// or avoid pointer arithmetic entirely.
ref int racers = ref NumberOfRacers.AsRef();
Hooking a function with Reloaded.Hooks:
// Function pointer declatation (can also use delegate).
[Function(CallingConventions.MicrosoftThiscall)]
public struct OpenFileFnPtr { public FuncPtr<Ptr<byte>, Ptr<byte>, int, Ptr<byte>> Value; }
_openFile = FileSystemFuncs.OpenFile.HookAs<FileSystemFuncs.OpenFileFnPtr>(typeof(FileAccessServer), nameof(OpenBfsFileImpl)).Activate();
SourcedPtr<T>
This is a tuple of Ptr<T>
and TSource
.
Basically, it allows you to assign a TSource
to a Ptr<T>
and skip passing TSource
as a parameter.
Remaining usage is the same.