diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..647e587 --- /dev/null +++ b/404.html @@ -0,0 +1,829 @@ + + + +
+ + + + + + + + + + + + + +Info
+Reloaded.Memory
provides various abstractions that can be used to wrap around contiguous regions of memory.
All APIs listed here are zero overhead.
+Stuff listed here isn't that impressive, but it's the basic building block for what follows next.
+Info
+The Memory
and ExternalMemory
classes are the most basic abstractions provided by Reloaded.Memory
.
+They allow you to access memory either within the current or a target process
.
while (ptr < maxAddress)
+{
+ result += *ptr;
+ ptr += 1;
+}
+
if (Polyfills.IsWindows())
+ return Kernel32.ReadProcessMemory(_processHandle, location, (nuint)buffer, numBytes, out _);
+
+if (Polyfills.IsLinux())
+ return Posix.process_vm_readv_k32(_processHandle, location, (nuint)buffer, numBytes);
+
+// And other cases!
+
// memory = Memory.Instance;
+while (ptr < maxAddress)
+{
+ result += memory.Read<nuint>((UIntPtr)ptr);
+ ptr += 1;
+}
+
// memory = new ExternalMemory(process);
+while (ptr < maxAddress)
+{
+ result += memory.Read<nuint>((UIntPtr)ptr);
+ ptr += 1;
+}
+
As you can see, with the library and its ICanReadWriteMemory
interface; usage is unified across all sources. Instead
+of having to write different code for different sources (first 2 examples), you can now write the same code for all sources.
And of course, various different utility methods are provided to make your life easier.
+while (ptr < maxAddress)
+ Marshal.StructureToPtr(items[x++], (nint)offset, false);
+
byte* bufferPtr = new byte[structSize];
+bool succeeded = ReadProcessMemory(offset, bufferPtr, (nuint)structSize);
+if (!succeeded)
+ ThrowHelpers.ThrowReadExternalMemoryExceptionWindows(offset, structSize);
+
+Marshal.PtrToStructure((nint)bufferPtr, value);
+
while (ptr < maxAddress)
+ memory.WriteWithMarshalling(ptr, items[x++]);
+
while (ptr < maxAddress)
+ memory.WriteWithMarshalling(ptr, items[x++]);
+
All silly boilerplate needed to manipulate different sources is gone; and this is all done with zero-overhead.
+Info
+Structs like Memory
and ExternalMemory
employ ICanAllocateMemory
API to make memory allocations convenient.
var allocation = NativeMemory.Alloc(100);
+
var allocation = Marshal.AllocHGlobal(100);
+
var allocation = memory.Allocate(100);
+
var allocation = memory.Allocate(100);
+
Now you can allocate in another process in a consistent manner. Useful?
+ICanAllocateMemory
for ExternalMemory
currently implemented in Windows only; PRs for Linux and OSX.
Info
+Structs like Memory
and ExternalMemory
employ ICanChangeMemoryProtection
API to allow you to change memory permissions.
This allows you to make existing code etc. in memory writable for editing.
+if (Polyfills.IsWindows())
+{
+ bool result = Kernel32.VirtualProtect(memoryAddress, (nuint)size, (Kernel32.MEM_PROTECTION)newProtection,
+ out Kernel32.MEM_PROTECTION oldPermissions);
+ if (!result)
+ ThrowHelpers.ThrowMemoryPermissionExceptionWindows(memoryAddress, size, newProtection);
+
+ return (nuint)oldPermissions;
+}
+
+if (Polyfills.IsLinux() || Polyfills.IsMacOS())
+{
+ // ... lot more boilerplate
+}
+
var oldPermissions = source.ChangeProtection(address, length, MemoryProtection.READ);
+
var oldPermissions = source.ChangeProtection(address, length, MemoryProtection.READ);
+
Pretty useful huh?
+ICanChangeMemoryProtection
for ExternalMemory
currently implemented in Windows only; PRs for Linux and OSX are welcome.
Info
+These interfaces, and combinations of them allow for some very useful utility methods to be made.
+Temporary allocate a buffer:
+// Automatically disposed, even on exception
+using var alloc = memory.AllocateDisposable(DataSize);
+
Temporary change memory permission:
+using var alloc = memory.ChangeProtectionDisposable(DataSize);
+
Temporary change memory permission, write data, and restore:
+memory.SafeWrite(Alloc.Address, Data.AsSpanFast());
+
In most cases, the abstractions generate 1:1 code that matches exactly the same performance as working with raw pointers.
+| Method | Mean | Error | StdDev | Code Size | Allocated |
+|------------------------------ |---------:|--------:|--------:|----------:|----------:|
+| ReadViaPointer | 130.7 ns | 0.77 ns | 0.69 ns | 49 B | - |
+| ReadViaMemory | 132.4 ns | 0.71 ns | 0.66 ns | 52 B | - |
+| ReadViaMemory_ViaOutParameter | 132.2 ns | 1.81 ns | 1.70 ns | 52 B | - |
+| WriteViaPointer | 117.4 ns | 1.55 ns | 1.45 ns | 48 B | - |
+| WriteViaMemory | 117.8 ns | 1.67 ns | 1.57 ns | 48 B | - |
+
ReadViaPointer
and WriteViaPointer
are using raw pointers; remaining tests are using the abstractions.
Partially forked from Community Toolkit.
+Utility class providing high performance extension methods for working with arrays.
+ArrayExtensions is a utility class that provides extension methods for arrays, including methods for getting references +without bounds checks, counting occurrences of a value, checking covariance, and converting arrays to spans.
+public static ref T DangerousGetReference<T>(this T[] array)
+
Returns a reference to the first element within a given T[]
array, with no bounds checks. The caller is responsible
+for performing checks in case the returned value is dereferenced.
public static ref T DangerousGetReferenceAt<T>(this T[] array, int i)
+
Returns a reference to an element at a specified index within a given T[]
array, with no bounds checks.
+The caller is responsible for ensuring the i
parameter is valid.
public static int Count<T>(this T[] array, T value) where T : IEquatable<T>
+
Counts the number of occurrences of a given value in a target T[]
array instance.
public static bool IsCovariant<T>(this T[] array)
+
Checks whether or not a given T[]
array is covariant.
Only supported on .NET 5 and above. Older runtimes will use default AsSpan
.
public static Span<T> AsSpanFast<T>(this T[] data)
+
Converts a byte array to a Span without doing a null check.
+int[] array = new int[] { 1, 2, 3 };
+ref int firstElement = ref array.DangerousGetReference();
+
int[] array = new int[] { 1, 2, 3 };
+ref int elementAtTwo = ref array.DangerousGetReferenceAt(2);
+
int[] array = new int[] { 1, 2, 2, 3, 2 };
+int count = array.Count(2);
+
object[] array = new object[] { "a", "b", "c" };
+bool isCovariant = array.IsCovariant<string>();
+
int[] array = new int[] { 1, 2, 3 };
+Span<int> span = array.AsSpanFast();
+
Forked from Community Toolkit.
+Utility class providing high performance extension methods for working with the bool
type.
BoolExtensions is a utility class that provides extension methods for the bool
type, including converting bool
values
+to byte
, int
, and long
masks.
public static unsafe byte ToByte(this bool flag)
+
Converts the given bool
value into a byte
. Returns 1 if flag
is true
, 0 otherwise.
+This method does not contain branching instructions.
public static unsafe int ToBitwiseMask32(this bool flag)
+
Converts the given bool
value to an int
mask with all bits representing the value of the input flag
+(either 0xFFFFFFFF or 0x00000000). This method does not contain branching instructions.
public static unsafe long ToBitwiseMask64(this bool flag)
+
Converts the given bool
value to a long
mask with all bits representing the value of the input flag
+(either all 1s or 0s). This method does not contain branching instructions.
bool flag = true;
+byte result = flag.ToByte();
+
bool flag = true;
+int mask = flag.ToBitwiseMask32();
+
bool flag = true;
+long mask = flag.ToBitwiseMask64();
+
Provides extension methods for converting between endianness.
+EndianExtensions
is a static class that offers methods to convert primitive data types and any structure implementing the ICanReverseEndian
interface to big or little endian format.
The conversions check the system's endianness and only perform byte-swapping if necessary, making the operation efficient by avoiding redundant processing on systems with matching endianness.
+The JIT will eliminate no-operations here, so e.g. calling AsLittleEndian
on a Little Endian machine has 0 overhead.
public static byte AsLittleEndian(this byte value)
+public static sbyte AsLittleEndian(this sbyte value)
+public static short AsLittleEndian(this short value)
+public static ushort AsLittleEndian(this ushort value)
+public static int AsLittleEndian(this int value)
+public static uint AsLittleEndian(this uint value)
+public static long AsLittleEndian(this long value)
+public static ulong AsLittleEndian(this ulong value)
+public static float AsLittleEndian(this float value)
+public static double AsLittleEndian(this double value)
+public static T AsLittleEndian<T>(this T value) where T : struct, ICanReverseEndian
+
Converts the given value to little endian format. If the system is already little endian, no conversion is performed.
+value
: The value to convert to little endian.The value in little endian format.
+public static byte AsBigEndian(this byte value)
+public static sbyte AsBigEndian(this sbyte value)
+public static short AsBigEndian(this short value)
+public static ushort AsBigEndian(this ushort value)
+public static int AsBigEndian(this int value)
+public static uint AsBigEndian(this uint value)
+public static long AsBigEndian(this long value)
+public static ulong AsBigEndian(this ulong value)
+public static float AsBigEndian(this float value)
+public static double AsBigEndian(this double value)
+public static T AsBigEndian<T>(this T value) where T : struct, ICanReverseEndian
+
Converts the given value to big endian format. If the system is already big endian, no conversion is performed.
+value
: The value to convert to big endian.The value in big endian format.
+int myValue = 12345678;
+int littleEndianValue = myValue.AsLittleEndian();
+
double myValue = 123.456;
+double bigEndianValue = myValue.AsBigEndian();
+
For structs which implement ICanReverseEndian
var myStruct = new MyStruct { /* ... */ };
+var asBig = myStruct.AsBigEndian();
+
Provides high-performance extension methods for working with enums.
+EnumExtensions is a utility class that , including checking for the presence of a specified flag in an enum value using unsafe code for faster execution.
+public static bool HasFlagFast<T>(this T value, T flag) where T : unmanaged, Enum
+
Determines if the given enum has a specified flag. This method uses unsafe code for faster execution and skips type check.
+value
: The value to check.flag
: The flag to check.true
if the enum is contained in the value, false
otherwise.
T
: The type to check the flag of.NotSupportedException
: This type of enum is not supported.[Flags]
+enum MyEnum : int
+{
+ None = 0,
+ Flag1 = 1,
+ Flag2 = 2,
+ Flag3 = 4,
+}
+
+MyEnum value = MyEnum.Flag1 | MyEnum.Flag3;
+bool hasFlag1 = value.HasFlagFast(MyEnum.Flag1); // True
+bool hasFlag2 = value.HasFlagFast(MyEnum.Flag2); // False
+bool hasFlag3 = value.HasFlagFast(MyEnum.Flag3); // True
+
Partially forked from Community Toolkit.
+Utility class providing high performance extension methods for working with spans.
+SpanExtensions is a utility class that provides extension methods for spans, including methods for casting, slicing, +replacing elements, and finding offsets.
+Not accelerated on .NET Framework and Standard 2.0
+public static Span<TTo> CastFast<TFrom, TTo>(this Span<TFrom> data) where TFrom : struct where TTo : struct
+
Span<TFrom>
to a Span<TTo>
without copying the underlying data.
+public static ref T DangerousGetReference<T>(this Span<T> span)
+
Span<T>
, with no bounds checks.
+public static ref T DangerousGetReferenceAt<T>(this Span<T> span, int i)
+
Span<T>
, with no bounds checks.i
parameter is valid.
+public static Span<byte> AsBytes<T>(this Span<T> span) where T : unmanaged
+
Span<T>
to a Span<byte>
without copying the underlying data.
+public static Span<TTo> Cast<TFrom, TTo>(this Span<TFrom> span) where TFrom : unmanaged where TTo : unmanaged
+
Span<TFrom>
to a Span<TTo>
without copying the underlying data, when both types are unmanaged.
+public static unsafe int IndexOf<T>(this Span<T> span, ref T value)
+
Gets the index of an element within given Span<T>
based on a reference to an element inside the Span<T>
.
public static int Count<T>(this Span<T> span, T value) where T : IEquatable<T>
+
Span<T>
instance.
+Not accelerated on .NET Framework and Standard 2.0
+public static Span<T> SliceFast<T>(this Span<T> data, int start, int length)
+public static ReadOnlySpan<T> SliceFast<T>(this ReadOnlySpan<T> data, int start, int length)
+public static ReadOnlySpan<T> SliceFast<T>(this ReadOnlySpan<T> data, Range range)
+public static Span<T> SliceFast<T>(this Span<T> data, Range range)
+public static Span<T> SliceFast<T>(this Span<T> data, int start)
+public static ReadOnlySpan<T> SliceFast<T>(this ReadOnlySpan<T> data, int start)
+
Span<T>
or ReadOnlySpan<T>
without performing bounds checks.
+This supports ranges, so span.SliceFast(1..3)
is valid.
+public static Span<char> Replace(this Span<char> data, char oldValue, char newValue, Span<char> buffer)
+public static unsafe Span<T> Replace<T>(this Span<T> data, T oldValue, T newValue, Span<T> buffer) where T : unmanaged, IEquatable<T>
+
Span<T>
.
+Missing SIMD path for non-x86/x64 platforms. PRs are welcome.
+public static List<int> FindAllOffsetsOfByte(this ReadOnlySpan<byte> data, byte value)
+public static List<int> FindAllOffsetsOfByte(this ReadOnlySpan<byte> data, byte value, int offsetCountHint)
+
ReadOnlySpan<byte>
instance. offsetCountHint
parameter to preallocate the list capacity.
+Span<int> span = new int[] { 1, 2, 3 };
+ref int firstElement = ref span.DangerousGetReference();
+
Span<int> span = new int[] { 1, 2, 3 };
+ref int elementAtTwo = ref span.DangerousGetReferenceAt(2);
+
Span<int> intSpan = new int[] { 1, 2, 3 };
+Span<byte> byteSpan = intSpan.AsBytes();
+
Span<byte> byteSpan = new byte[] { 1, 2, 3, 4 };
+Span<int> intSpan = byteSpan.Cast<byte, int>();
+
Span<int> span = new int[] { 1, 2, 3, 4, 5 };
+ref int value = ref span[3];
+int index = span.IndexOf(ref value); // index = 3
+
Span<int> span = new int[] { 1, 2, 2, 3, 2 };
+int count = span.Count(2); // 3
+
Span<int> span = new int[] { 1, 2, 3, 4, 5 };
+Span<int> slicedSpan = span.SliceFast(1, 3);
+
Span<char> span = "hello world".ToCharArray();
+Span<char> buffer = new char[span.Length];
+Span<char> replacedSpan = span.Replace('l', 'x', buffer);
+
byte[] data = new byte[] { 1, 2, 3, 2, 4, 2 };
+ReadOnlySpan<byte> span = data;
+List<int> offsets = span.FindAllOffsetsOfByte(2);
+
byte[] data = new byte[] { 1, 2, 3, 2, 4, 2 };
+ReadOnlySpan<byte> span = data;
+List<int> offsets = span.FindAllOffsetsOfByte(2, 3);
+
Utility class providing high performance extension methods for working with Stream
objects.
StreamExtensions is a utility class that provides extension methods for the Stream
type, including padding, writing, and reading unmanaged and marshalled structures.
public static void AddPadding<TStream>(this TStream stream, int alignment)
+
Stream.SetLength
.
+public static void AddPadding<TStream>(this TStream stream, byte value, int alignment = 2048)
+
value
bytes until it is aligned to the specified alignment.
+public static void Write<TStream, T>(this TStream stream, Span<T> structure) where T : unmanaged
+
public static void Write<TStream, T>(this TStream stream, in T structure) where T : unmanaged
+
public static void WriteMarshalled<TStream, T>(this TStream stream, T item)
+
public static void WriteMarshalled<TStream, T>(this TStream stream, T[] item)
+
public static void WriteMarshalled<TStream, T>(this TStream stream, Span<T> item)
+
public static void Read<TStream, T>(this TStream stream, out T result) where T : unmanaged
+
public static void Read<TStream, T>(this TStream stream, T[] output) where T : unmanaged
+
public static void Read<TStream, T>(this TStream stream, Span<T> output) where T : unmanaged
+
public static void ReadMarshalled<TStream, T>(this TStream stream, out T result)
+
public static void ReadMarshalled<TStream, T>(this TStream stream, T[] output)
+
public static void ReadMarshalled<TStream, T>(this TStream stream, Span<T> output)
+
using var stream = new MemoryStream();
+stream.AddPadding(4096);
+
using var stream = new MemoryStream();
+stream.AddPadding(0xFF, 4096);
+
using var stream = new MemoryStream();
+Vector2 structure = new Vector2(1, 2);
+stream.Write(structure);
+
using var stream = new MemoryStream();
+MyStruct item = new MyStruct { Value1 = 1, Value2 = "Hello" };
+stream.WriteMarshalled(item);
+
using var stream = new MemoryStream();
+stream.Read(out Vector2 result);
+
using var stream = new MemoryStream();
+stream.ReadMarshalled(out MyStruct result);
+
using var stream = new MemoryStream();
+Vector2[] output = new Vector2[10];
+stream.Read(output);
+
using var stream = new MemoryStream();
+MyStruct[] output = new MyStruct[10];
+stream.ReadMarshalled(output);
+
Partially forked from Community Toolkit.
+Utility class providing high performance extension methods for working with the string
type.
StringExtensions is a utility class that provides extension methods for the string
type, including getting references to string elements, and counting the number of occurrences of a character in a string.
public static ref char DangerousGetReference(this string text)
+
Returns a reference to the first element within a given string
, with no bounds checks. It is the caller's responsibility to perform bounds checks when dereferencing the returned value.
public static ref char DangerousGetReferenceAt(this string text, int i)
+
Returns a reference to an element at a specified index within a given string
, with no bounds checks. It is the caller's responsibility to ensure the i
parameter is valid.
public static int Count(this string text, char c)
+
Counts the number of occurrences of a given character in a target string
instance.
SIMD method currently restricted to .NET 7+. PRs for backports are welcome.
+Will produce different hashes depending on runtime or CPU.
+Optimised for File Paths specifically
+public static nuint GetHashCodeFast(string text)
+public static unsafe nuint GetHashCodeFast(this ReadOnlySpan<char> text)
+
Faster hashcode for strings; but does not randomize between application runs.
+Use this method if and only if 'Denial of Service' attacks are not a concern +(i.e. never used for free-form user input), or are otherwise mitigated.
+This method does not provide guarantees about producing the same hash across different machines or library versions, +or runtime; only for the current process. Instead, it prioritises speed over all.
+SIMD method currently restricted to .NET 7+. PRs for backports are welcome.
+Will produce different hashes depending on runtime or CPU.
+Optimised for File Paths specifically
+public static nuint GetHashCodeLowerFast(this string text)
+public static unsafe nuint GetHashCodeLowerFast(this ReadOnlySpan<char> text)
+
Faster hashcode for strings, hashed in lower (invariant) case; does not randomize between application runs.
+Use this method if and only if 'Denial of Service' attacks are not a concern +(i.e. never used for free-form user input), or are otherwise mitigated.
+This method does not provide guarantees about producing the same hash across different machines or library versions, +or runtime; only for the current process. Instead, it prioritises speed over all.
+public static string ToLowerInvariantFast(this string text)
+public static unsafe void ToLowerInvariantFast(this ReadOnlySpan<char> text, Span<char> target)
+
Converts the given string to lower case (invariant casing) using the fastest possible implementation. +This method is optimized for performance but currently has limitations for short non-ASCII inputs.
+public static string ToUpperInvariantFast(this string text)
+public static unsafe void ToUpperInvariantFast(this ReadOnlySpan<char> text, Span<char> target)
+
Converts the given string to upper case (invariant casing) using the fastest possible implementation. +This method is optimized for performance but currently has limitations for short non-ASCII inputs.
+string text = "Hello, world!";
+ref char firstCharRef = ref text.DangerousGetReference();
+
string text = "Hello, world!";
+int index = 4;
+ref char charAtIndexRef = ref text.DangerousGetReferenceAt(index);
+
string text = "Hello, world!";
+char targetChar = 'l';
+int count = text.Count(targetChar);
+
string text = "Hello, world!";
+nuint fastHashCode = text.GetHashCodeFast();
+
string text = "Hello, World!";
+nuint lowerCaseHashCode = text.GetHashCodeLowerFast();
+
string text = "Hello, WORLD!";
+string lowerInvariant = text.ToLowerInvariantFast(); // hello, world!
+
string text = "hello, world!";
+string upperInvariant = text.ToUpperInvariantFast(); // HELLO, WORLD!
+
string text = "Hello, WORLD!";
+Span<char> target = stackalloc char[textSpan.Length]; // Careful with string length!
+text.AsSpan().ToLowerInvariantFast(target); // hello, world! (on stack)
+
string text = "hello, world!";
+Span<char> target = stackalloc char[textSpan.Length]; // Careful with string length!
+text.AsSpan().ToLowerInvariantFast(target); // HELLO, WORLD! (on stack)
+
Utility class providing high performance extension methods for working with the Vector
struct.
VectorExtensions is a utility class that provides extension methods for the Vector
struct.
internal static Vector<T> LoadUnsafe<T>(ref T source, nuint elementOffset) where T : struct
+
Loads an element at a specified offset into a vector. Unsafe operations are used for performance reasons.
+internal static void StoreUnsafe<T>(this Vector<T> source, ref T destination, nuint elementOffset) where T : struct
+
Stores an element from a vector into the destination + offset. Unsafe operations are used for performance reasons.
+int[] array = new int[] { 1, 2, 3, 4, 5 };
+ref int source = ref array[0];
+nuint elementOffset = 2;
+Vector<int> vector = VectorExtensions.LoadUnsafe(ref source, elementOffset);
+
Vector<int> vector = new Vector<int>(new int[] { 1, 2, 3, 4 });
+int[] destinationArray = new int[vector.Count];
+ref int destination = ref destinationArray[0];
+nuint elementOffset = 1;
+vector.StoreUnsafe(ref destination, elementOffset);
+
Abstraction for a pointer to a value of type T
with a known length.
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 manage a collection of items in unmanaged, static memory; for example, find an item.
+Pointer
: A PtrCount
: The number of elements in the fixed-size array. ArraySize
: The total size in bytes of the fixed-size array. var ptr = new FixedArrayPtr<int>(address, count);
+
Address
: The memory address of the first element in the fixed-size array.Count
: The number of elements in the fixed-size array.The following methods should be used with in memory/RAM addresses. See Memory Read/Write for APIs that work with ICanReadWriteMemory
implementations.
ref T AsRef(int index);
+T Get(int index);
+void Get(int index, out T value);
+void Set(int index, in T value);
+
These methods provide access to individual elements in the fixed-size array. The AsRef()
method returns a reference to
+an element, allowing for direct modification of the element's value. The Get()
and Set()
methods retrieve and update
+the values of elements at specified indices.
T Get<TSource>(TSource source, int index);
+void Get<TSource>(TSource source, int index, out T value);
+void Set<TSource>(TSource source, int index, in T value);
+
ICanReadWriteMemory
. This can be useful when working with external processes or
+other memory sources.
+void CopyFrom(Span<T> sourceArray, int length);
+void CopyFrom(Span<T> sourceArray, int length, int sourceIndex, int destinationIndex);
+void CopyTo(Span<T> destinationArray, int length);
+void CopyTo(Span<T> destinationArray, int length, int sourceIndex, int destinationIndex);
+
These methods provide copying functionality between the fixed-size array and managed arrays. CopyFrom()
copies elements
+from a managed array to the fixed-size array, and CopyTo()
copies elements from the fixed-size array to a managed array.
Both methods support copying a specified number of elements, as well as specifying source and destination indices for +the copy operation.
+bool Contains(in T item);
+bool Contains<TSource>(TSource source, in T item);
+int IndexOf(in T item);
+int IndexOf<TSource>(TSource source, in T item);
+
These methods provide search functionality for the fixed-size array, allowing you to check for the existence of an element
+or find its index. The Contains()
methods return true if the specified item is found in the array, and the IndexOf()
methods
+return the index of the first occurrence of the specified item or -1 if the item is not found.
This is a tuple of FixedArrayPtr<T>
and TSource
.
API surface is the same, all methods are delegated to underlying FixedArrayPtr<T>
.
Notably however, this API enables the use of LINQ; i.e. the following are now valid:
+foreach (int value in sourcedFixedArrayPtr)
+{
+ // ...
+}
+
sourcedFixedArrayPtr.Max(x => x);
+
etc.
+ + + + + + +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:
+ICanReadWriteMemory
. (e.g. Memory of External Process). 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.
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.
The Ptr<T>
type supports implicit conversions to/from raw pointers.
int someValue = 42;
+Ptr<int> ptr = &someValue;
+int* ptr2 = intPtr;
+
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.
+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.
+
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}");
+
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!");
+
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();
+
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.
+ + + + + + +Info
+This page shows you how to contribute to any documentation page or wiki +based on this template.
+Note
+This theme is forked from my theme for Nexus Docs; +and this page is synced with that.
+Note
+If you are editing the repository with the theme itself on Windows, it might be a good idea to run
+git config core.symlinks true
first to allow git to create symlinks on clone.
You should learn the basics of git
, an easy way is to give GitHub Desktop (Tutorial) a go.
+It's only 15 minutes 😀.
Fork this repository:
+ +This will create a copy of the repository on your own user account, which you will be able to edit.
+Clone this repository.
+For example, using GitHub Desktop: +
+Make changes inside the docs
folder.
Consider using a Markdown Cheat Sheet if you are new to markdown.
+I recommend using a markdown editor such as Typora
.
+Personally I just work from inside Rider
.
Commit the changes and push to GitHub.
+Open a Pull Request
.
Opening a Pull Request
will allow us to review your changes before adding them with the main official page. If everything's good, we'll hit the merge button and add your changes to the official repository.
If you are working on the wiki locally, you can generate a live preview the full website.
+Here's a quick guide of how you could do it from your command prompt
(cmd).
Install Python 3
+If you have winget
installed, or Windows 11, you can do this from the command prompt.
+
winget install Python.Python.3
+
Otherwise download Python 3 from the official website or package manager.
+Install Material for MkDocs and Plugins (Python package) +
# Restart your command prompt before running this command.
+pip install mkdocs-material
+pip install mkdocs-redirects
+
Open a command prompt in the folder containing mkdocs.yml
. and run the site locally.
+
# Move to project folder.
+cd <Replace this with full path to folder containing `mkdocs.yml`>
+mkdocs serve
+
Copy the address to your web browser and enjoy the live preview; any changes you save will be shown instantly.
+This it the NexusMods theme for Material-MkDocs, inspired by the look of Reloaded-II.
+The overall wiki theme should look fairly close to the actual launcher appearance.
+docs/Reloaded
.mkdocs.yml
in your repository root.site_name: Reloaded MkDocs Theme
+site_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2
+
+repo_name: Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2
+repo_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2
+
+extra:
+ social:
+ - icon: fontawesome/brands/github
+ link: https://github.com/Reloaded-Project
+ - icon: fontawesome/brands/twitter
+ link: https://twitter.com/thesewer56?lang=en-GB
+
+extra_css:
+ - Reloaded/Stylesheets/extra.css
+
+markdown_extensions:
+ - admonition
+ - tables
+ - pymdownx.details
+ - pymdownx.highlight
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+ - pymdownx.tasklist
+ - def_list
+ - meta
+ - md_in_html
+ - attr_list
+ - footnotes
+ - pymdownx.tabbed:
+ alternate_style: true
+ - pymdownx.emoji:
+ emoji_index: !!python/name:materialx.emoji.twemoji
+ emoji_generator: !!python/name:materialx.emoji.to_svg
+
+theme:
+ name: material
+ palette:
+ scheme: reloaded-slate
+ features:
+ - navigation.instant
+
+plugins:
+ - search
+
+nav:
+ - Home: index.md
+
.github/workflows/DeployMkDocs.yml
.name: DeployMkDocs
+
+# Controls when the action will run.
+on:
+ # Triggers the workflow on push on the master branch
+ push:
+ branches: [ main ]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ build:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - name: Checkout Branch
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+
+ # Deploy MkDocs
+ - name: Deploy MkDocs
+ # You may pin to the exact commit or the version.
+ # uses: mhausenblas/mkdocs-deploy-gh-pages@66340182cb2a1a63f8a3783e3e2146b7d151a0bb
+ uses: mhausenblas/mkdocs-deploy-gh-pages@master
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REQUIREMENTS: ./docs/requirements.txt
+
Settings -> Pages
in your repo and select gh-pages
branch to enable GitHub pages. Your page should then be live.
+Tip
+Refer to Contributing for instructions on how to locally edit and modify the wiki.
+Note
+For Reloaded3 theme use reloaded3-slate
instead of reloaded-slate
.
Info
+Most documentation pages will also include additional plugins; some which are used in the pages here.
+Here is a sample complete mkdocs.yml you can copy to your project for reference.
If you have questions/bug reports/etc. feel free to Open an Issue.
+Happy Documenting ❤️
+ + + + + + + + +Most components of the Reloaded are governed by the GPLv3 license.
+In some, albeit rare scenarios, certain libraries might be licensed under LGPLv3 instead.
+This is a FAQ meant to clarify the licensing choice and its implications. +Please note, though, that the full license text is the final legal authority.
+The primary objective is to prevent closed-source, commercial exploitation of the project.
+We want to ensure that the project isn't used within a proprietary environment for +profit-making purposes such as:
+The Reloaded Project is a labour of love from unpaid hobbyist volunteers.
+Exploiting that work for profit feels fundamentally unfair.
+While the GPLv3 license doesn't prohibit commercial use outright, it does prevent commercial +exploitation by requiring that contributions are given back to the open-source community.
+In that fashion, everyone can benefit from the projects under the Reloaded label.
+You can as long as the resulting produce is also licensed under GPLv3, and thus open source.
+The license terms do not permit this.
+However, if your software is completely non-commercial, meaning it's neither +sold for profit, funded in development, nor hidden behind a paywall (like Patreon), +we probably just look the other way.
+This often applies to non-professional programmers, learners, or those +with no intent to exploit the project. We believe in understanding and +leniency for those who might not know better.
+GPL v3 exists to protect the project and its contributors. If you're not exploiting the project for commercial +gain, you're not hurting us; and we will not enforce the terms of the GPL.
+If you are interested in obtaining a commercial license, or want an explicit written exemption, +please get in touch with the repository owners.
+Yes, as long as you adhere to the GPLv3 license terms, you're permitted to statically +link Reloaded Libraries into your project, for instance, through the use of NativeAOT or ILMerge.
+We support and encourage the non-commercial use of Reloaded Libraries. +Non-commercial use generally refers to the usage of our libraries for personal projects, +educational purposes, academic research, or use by non-profit organizations.
+You're free to use our libraries for projects that you undertake +for your own learning, hobby or personal enjoyment. This includes creating mods for your +favorite games or building your own applications for personal use.
+Teachers and students are welcome to use our libraries as a learning +resource. You can incorporate them into your teaching materials, student projects, coding +bootcamps, workshops, etc.
+Researchers may use our libraries for academic and scholarly research. +We'd appreciate if you cite our work in any publications that result from research involving our libraries.
+If you're part of a registered non-profit organization, +you can use our libraries in your projects. However, any derivative work that uses our +libraries must also be released under the GPL.
+Please remember, if your usage of our libraries evolves from non-commercial to commercial, +you must ensure compliance with the terms of the GPL v3 license.
+As Reloaded Project is a labor of love, done purely out of passion and with an aim to contribute +to the broader community, we highly appreciate your support in providing attribution when using +our libraries.
+While not legally mandatory under GPL v3, it is a simple act that can go a long +way in recognizing the efforts of our contributors and fostering an open and collaborative atmosphere.
+If you choose to provide attribution (and we hope you do!), here are some guidelines:
+Acknowledge the Use of Reloaded Libraries: Mention that your project uses or is based on Reloaded libraries. + This could be in your project's readme, a credits page on a website, a manual, or within the software itself.
+Link to the Project: If possible, provide a link back to the Reloaded Project. + This allows others to explore and potentially benefit from our work.
+Remember, attribution is more than just giving credit,,, it's a way of saying thank you 👉👈, fostering reciprocal +respect, and acknowledging the power of collaborative open-source development.
+We appreciate your support and look forward to seeing what amazing projects you create using Reloaded libraries!
+In some rare instances, code from more permissively licensed projects, such as those under the
+MIT
or BSD
licenses, may be referenced, incorporated, or slightly modified within the Reloaded Project.
It's important to us to respect the terms and intentions of these permissive licenses, +which often allow their code to be used in a wide variety of contexts, including in GPL-licensed projects like ours.
+In these cases, the Reloaded Project is committed to clearly disclosing the usage of such code:
+Method-Level Disclosure: For individual methods or small code snippets, we use appropriate
+ attribution methods, like programming language attributes. For example, methods borrowed or adapted
+ from MIT-licensed projects might be marked with a [MITLicense]
attribute.
File-Level Disclosure: For larger amounts of code, such as entire files or modules, we'll include + the original license text at the top of the file and clearly indicate which portions of the code originate + from a differently-licensed project.
+Project-Level Disclosure: If an entire library or significant portion of a project under a more permissive + license is used, we will include an acknowledgment in a prominent location, such as the readme file or the + project's license documentation.
+This approach ensures we honor the contributions of the open source community at large, respect the original +licenses, and maintain transparency with our users about where code originates from.
+Any files/methods or snippets marked with those attributes may be consumed using their original license terms.
+i.e. If a method is marked with [MITLicense]
, you may use it under the terms of the MIT license.
We welcome and appreciate contributions to the Reloaded Project! +By contributing, you agree to share your changes under the same GPLv3 license, +helping to make the project better for everyone.
+ + + + + + +Info
+This is a dummy page with various Material MkDocs controls and features scattered throughout for testing.
+Reloaded Admonition
+An admonition featuring a Reloaded logo.
+My source is in Stylesheets/extra.css as Custom 'reloaded' admonition
.
Heart Admonition
+An admonition featuring a heart; because we want to contribute back to the open source community.
+My source is in Stylesheets/extra.css as Custom 'reloaded heart' admonition
.
Nexus Admonition
+An admonition featuring a Nexus logo.
+My source is in Stylesheets/extra.css as Custom 'nexus' admonition
.
Heart Admonition
+An admonition featuring a heart; because we want to contribute back to the open source community.
+My source is in Stylesheets/extra.css as Custom 'nexus heart' admonition
.
Flowchart (Source: Nexus Archive Library):
+flowchart TD
+ subgraph Block 2
+ BigFile1.bin
+ end
+
+ subgraph Block 1
+ BigFile0.bin
+ end
+
+ subgraph Block 0
+ ModConfig.json -.-> Updates.json
+ Updates.json -.-> more["... more .json files"]
+ end
+Sequence Diagram (Source: Reloaded3 Specification):
+sequenceDiagram
+
+ % Define Items
+ participant Mod Loader
+ participant Virtual FileSystem (VFS)
+ participant CRI CPK Archive Support
+ participant Persona 5 Royal Support
+ participant Joker Costume
+
+ % Define Actions
+ Mod Loader->>Persona 5 Royal Support: Load Mod
+ Persona 5 Royal Support->>Mod Loader: Request CRI CPK Archive Support API
+ Mod Loader->>Persona 5 Royal Support: Receive CRI CPK Archive Support Instance
+
+ Mod Loader->>Joker Costume: Load Mod
+ Mod Loader-->Persona 5 Royal Support: Notification: 'Loaded Joker Costume'
+ Persona 5 Royal Support->>CRI CPK Archive Support: Add Files from 'Joker Costume' to CPK Archive (via API)
+State Diagram (Source: Mermaid Docs):
+stateDiagram-v2
+ [*] --> Still
+ Still --> [*]
+
+ Still --> Moving
+ Moving --> Still
+ Moving --> Crash
+ Crash --> [*]
+Class Diagram (Arbitrary)
+classDiagram
+ class Animal
+ `NexusMobile™` <|-- Car
+Note
+At time of writing, version of Mermaid is a bit outdated here; and other diagrams might not render correctly +(even on unmodified theme); thus certain diagrams have been omitted from here.
+Snippet from C# version of Sewer's Virtual FileSystem (VFS):
+/// <summary>
+/// Tries to get files for a specific folder, assuming the input path is already in upper case.
+/// </summary>
+/// <param name="folderPath">The folder to find. Already lowercase.</param>
+/// <param name="value">The returned folder instance.</param>
+/// <returns>True if found, else false.</returns>
+[MethodImpl(MethodImplOptions.AggressiveInlining)]
+public bool TryGetFolderUpper(ReadOnlySpan<char> folderPath, out SpanOfCharDict<TTarget> value)
+{
+ // Must be O(1)
+ value = default!;
+
+ // Compare equality.
+ // Note to devs: Do not invert branches, we optimise for hot paths here.
+ if (folderPath.StartsWith(Prefix))
+ {
+ // Check for subfolder in branchless way.
+ // In CLR, bool is length 1, so conversion to byte should be safe.
+ // Even suppose it is not; as long as code is little endian; truncating int/4 bytes to byte still results
+ // in correct answer.
+ var hasSubfolder = Prefix.Length != folderPath.Length;
+ var hasSubfolderByte = Unsafe.As<bool, byte>(ref hasSubfolder);
+ var nextFolder = folderPath.SliceFast(Prefix.Length + hasSubfolderByte);
+
+ return SubfolderToFiles.TryGetValue(nextFolder, out value!);
+ }
+
+ return false;
+}
+
Something more number heavy, Fast Inverse Square Root from Quake III Arena (unmodified). +
float Q_rsqrt( float number )
+{
+ long i;
+ float x2, y;
+ const float threehalfs = 1.5F;
+
+ x2 = number * 0.5F;
+ y = number;
+ i = * ( long * ) &y; // evil floating point bit level hacking
+ i = 0x5f3759df - ( i >> 1 ); // what the fuck?
+ y = * ( float * ) &i;
+ y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
+// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
+
+ return y;
+}
+
Note
+Test
+Abstract
+Test
+Info
+Test
+Tip
+Test
+Success
+Test
+Question
+Test
+Warning
+Test
+Failure
+Test
+Danger
+Test
+Bug
+Test
+Example
+Test
+Quote
+Test
+Method | +Description | +
---|---|
GET |
+Fetch resource | +
PUT |
+Update resource | +
DELETE |
+Delete resource | +
Please visit the documentation site for usage instructions & more.
+ + + + + + +Info
+This page shows you how to contribute to any documentation page or wiki +based on this template.
+Note
+This theme is forked from my theme for Nexus Docs; +and this page is synced with that.
+Note
+If you are editing the repository with the theme itself on Windows, it might be a good idea to run
+git config core.symlinks true
first to allow git to create symlinks on clone.
You should learn the basics of git
, an easy way is to give GitHub Desktop (Tutorial) a go.
+It's only 15 minutes 😀.
Fork this repository:
+ +This will create a copy of the repository on your own user account, which you will be able to edit.
+Clone this repository.
+For example, using GitHub Desktop: +
+Make changes inside the docs
folder.
Consider using a Markdown Cheat Sheet if you are new to markdown.
+I recommend using a markdown editor such as Typora
.
+Personally I just work from inside Rider
.
Commit the changes and push to GitHub.
+Open a Pull Request
.
Opening a Pull Request
will allow us to review your changes before adding them with the main official page. If everything's good, we'll hit the merge button and add your changes to the official repository.
If you are working on the wiki locally, you can generate a live preview the full website.
+Here's a quick guide of how you could do it from your command prompt
(cmd).
Install Python 3
+If you have winget
installed, or Windows 11, you can do this from the command prompt.
+
winget install Python.Python.3
+
Otherwise download Python 3 from the official website or package manager.
+Install Material for MkDocs and Plugins (Python package) +
# Restart your command prompt before running this command.
+pip install mkdocs-material
+pip install mkdocs-redirects
+
Open a command prompt in the folder containing mkdocs.yml
. and run the site locally.
+
# Move to project folder.
+cd <Replace this with full path to folder containing `mkdocs.yml`>
+mkdocs serve
+
Copy the address to your web browser and enjoy the live preview; any changes you save will be shown instantly.
+This it the NexusMods theme for Material-MkDocs, inspired by the look of Reloaded-II.
+The overall wiki theme should look fairly close to the actual launcher appearance.
+docs/Reloaded
.mkdocs.yml
in your repository root.site_name: Reloaded MkDocs Theme
+site_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2
+
+repo_name: Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2
+repo_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2
+
+extra:
+ social:
+ - icon: fontawesome/brands/github
+ link: https://github.com/Reloaded-Project
+ - icon: fontawesome/brands/twitter
+ link: https://twitter.com/thesewer56?lang=en-GB
+
+extra_css:
+ - Reloaded/Stylesheets/extra.css
+
+markdown_extensions:
+ - admonition
+ - tables
+ - pymdownx.details
+ - pymdownx.highlight
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+ - pymdownx.tasklist
+ - def_list
+ - meta
+ - md_in_html
+ - attr_list
+ - footnotes
+ - pymdownx.tabbed:
+ alternate_style: true
+ - pymdownx.emoji:
+ emoji_index: !!python/name:materialx.emoji.twemoji
+ emoji_generator: !!python/name:materialx.emoji.to_svg
+
+theme:
+ name: material
+ palette:
+ scheme: reloaded-slate
+ features:
+ - navigation.instant
+
+plugins:
+ - search
+
+nav:
+ - Home: index.md
+
.github/workflows/DeployMkDocs.yml
.name: DeployMkDocs
+
+# Controls when the action will run.
+on:
+ # Triggers the workflow on push on the master branch
+ push:
+ branches: [ main ]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ build:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - name: Checkout Branch
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+
+ # Deploy MkDocs
+ - name: Deploy MkDocs
+ # You may pin to the exact commit or the version.
+ # uses: mhausenblas/mkdocs-deploy-gh-pages@66340182cb2a1a63f8a3783e3e2146b7d151a0bb
+ uses: mhausenblas/mkdocs-deploy-gh-pages@master
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REQUIREMENTS: ./docs/requirements.txt
+
Settings -> Pages
in your repo and select gh-pages
branch to enable GitHub pages. Your page should then be live.
+Tip
+Refer to Contributing for instructions on how to locally edit and modify the wiki.
+Note
+For Reloaded3 theme use reloaded3-slate
instead of reloaded-slate
.
Info
+Most documentation pages will also include additional plugins; some which are used in the pages here.
+Here is a sample complete mkdocs.yml you can copy to your project for reference.
If you have questions/bug reports/etc. feel free to Open an Issue.
+Happy Documenting ❤️
+ + + + + + + + +Most components of the Reloaded are governed by the GPLv3 license.
+In some, albeit rare scenarios, certain libraries might be licensed under LGPLv3 instead.
+This is a FAQ meant to clarify the licensing choice and its implications. +Please note, though, that the full license text is the final legal authority.
+The primary objective is to prevent closed-source, commercial exploitation of the project.
+We want to ensure that the project isn't used within a proprietary environment for +profit-making purposes such as:
+The Reloaded Project is a labour of love from unpaid hobbyist volunteers.
+Exploiting that work for profit feels fundamentally unfair.
+While the GPLv3 license doesn't prohibit commercial use outright, it does prevent commercial +exploitation by requiring that contributions are given back to the open-source community.
+In that fashion, everyone can benefit from the projects under the Reloaded label.
+You can as long as the resulting produce is also licensed under GPLv3, and thus open source.
+The license terms do not permit this.
+However, if your software is completely non-commercial, meaning it's neither +sold for profit, funded in development, nor hidden behind a paywall (like Patreon), +we probably just look the other way.
+This often applies to non-professional programmers, learners, or those +with no intent to exploit the project. We believe in understanding and +leniency for those who might not know better.
+GPL v3 exists to protect the project and its contributors. If you're not exploiting the project for commercial +gain, you're not hurting us; and we will not enforce the terms of the GPL.
+If you are interested in obtaining a commercial license, or want an explicit written exemption, +please get in touch with the repository owners.
+Yes, as long as you adhere to the GPLv3 license terms, you're permitted to statically +link Reloaded Libraries into your project, for instance, through the use of NativeAOT or ILMerge.
+We support and encourage the non-commercial use of Reloaded Libraries. +Non-commercial use generally refers to the usage of our libraries for personal projects, +educational purposes, academic research, or use by non-profit organizations.
+You're free to use our libraries for projects that you undertake +for your own learning, hobby or personal enjoyment. This includes creating mods for your +favorite games or building your own applications for personal use.
+Teachers and students are welcome to use our libraries as a learning +resource. You can incorporate them into your teaching materials, student projects, coding +bootcamps, workshops, etc.
+Researchers may use our libraries for academic and scholarly research. +We'd appreciate if you cite our work in any publications that result from research involving our libraries.
+If you're part of a registered non-profit organization, +you can use our libraries in your projects. However, any derivative work that uses our +libraries must also be released under the GPL.
+Please remember, if your usage of our libraries evolves from non-commercial to commercial, +you must ensure compliance with the terms of the GPL v3 license.
+As Reloaded Project is a labor of love, done purely out of passion and with an aim to contribute +to the broader community, we highly appreciate your support in providing attribution when using +our libraries.
+While not legally mandatory under GPL v3, it is a simple act that can go a long +way in recognizing the efforts of our contributors and fostering an open and collaborative atmosphere.
+If you choose to provide attribution (and we hope you do!), here are some guidelines:
+Acknowledge the Use of Reloaded Libraries: Mention that your project uses or is based on Reloaded libraries. + This could be in your project's readme, a credits page on a website, a manual, or within the software itself.
+Link to the Project: If possible, provide a link back to the Reloaded Project. + This allows others to explore and potentially benefit from our work.
+Remember, attribution is more than just giving credit,,, it's a way of saying thank you 👉👈, fostering reciprocal +respect, and acknowledging the power of collaborative open-source development.
+We appreciate your support and look forward to seeing what amazing projects you create using Reloaded libraries!
+In some rare instances, code from more permissively licensed projects, such as those under the
+MIT
or BSD
licenses, may be referenced, incorporated, or slightly modified within the Reloaded Project.
It's important to us to respect the terms and intentions of these permissive licenses, +which often allow their code to be used in a wide variety of contexts, including in GPL-licensed projects like ours.
+In these cases, the Reloaded Project is committed to clearly disclosing the usage of such code:
+Method-Level Disclosure: For individual methods or small code snippets, we use appropriate
+ attribution methods, like programming language attributes. For example, methods borrowed or adapted
+ from MIT-licensed projects might be marked with a [MITLicense]
attribute.
File-Level Disclosure: For larger amounts of code, such as entire files or modules, we'll include + the original license text at the top of the file and clearly indicate which portions of the code originate + from a differently-licensed project.
+Project-Level Disclosure: If an entire library or significant portion of a project under a more permissive + license is used, we will include an acknowledgment in a prominent location, such as the readme file or the + project's license documentation.
+This approach ensures we honor the contributions of the open source community at large, respect the original +licenses, and maintain transparency with our users about where code originates from.
+Any files/methods or snippets marked with those attributes may be consumed using their original license terms.
+i.e. If a method is marked with [MITLicense]
, you may use it under the terms of the MIT license.
We welcome and appreciate contributions to the Reloaded Project! +By contributing, you agree to share your changes under the same GPLv3 license, +helping to make the project better for everyone.
+ + + + + + +Info
+This is a dummy page with various Material MkDocs controls and features scattered throughout for testing.
+Reloaded Admonition
+An admonition featuring a Reloaded logo.
+My source is in Stylesheets/extra.css as Custom 'reloaded' admonition
.
Heart Admonition
+An admonition featuring a heart; because we want to contribute back to the open source community.
+My source is in Stylesheets/extra.css as Custom 'reloaded heart' admonition
.
Nexus Admonition
+An admonition featuring a Nexus logo.
+My source is in Stylesheets/extra.css as Custom 'nexus' admonition
.
Heart Admonition
+An admonition featuring a heart; because we want to contribute back to the open source community.
+My source is in Stylesheets/extra.css as Custom 'nexus heart' admonition
.
Flowchart (Source: Nexus Archive Library):
+flowchart TD
+ subgraph Block 2
+ BigFile1.bin
+ end
+
+ subgraph Block 1
+ BigFile0.bin
+ end
+
+ subgraph Block 0
+ ModConfig.json -.-> Updates.json
+ Updates.json -.-> more["... more .json files"]
+ end
+Sequence Diagram (Source: Reloaded3 Specification):
+sequenceDiagram
+
+ % Define Items
+ participant Mod Loader
+ participant Virtual FileSystem (VFS)
+ participant CRI CPK Archive Support
+ participant Persona 5 Royal Support
+ participant Joker Costume
+
+ % Define Actions
+ Mod Loader->>Persona 5 Royal Support: Load Mod
+ Persona 5 Royal Support->>Mod Loader: Request CRI CPK Archive Support API
+ Mod Loader->>Persona 5 Royal Support: Receive CRI CPK Archive Support Instance
+
+ Mod Loader->>Joker Costume: Load Mod
+ Mod Loader-->Persona 5 Royal Support: Notification: 'Loaded Joker Costume'
+ Persona 5 Royal Support->>CRI CPK Archive Support: Add Files from 'Joker Costume' to CPK Archive (via API)
+State Diagram (Source: Mermaid Docs):
+stateDiagram-v2
+ [*] --> Still
+ Still --> [*]
+
+ Still --> Moving
+ Moving --> Still
+ Moving --> Crash
+ Crash --> [*]
+Class Diagram (Arbitrary)
+classDiagram
+ class Animal
+ `NexusMobile™` <|-- Car
+Note
+At time of writing, version of Mermaid is a bit outdated here; and other diagrams might not render correctly +(even on unmodified theme); thus certain diagrams have been omitted from here.
+Snippet from C# version of Sewer's Virtual FileSystem (VFS):
+/// <summary>
+/// Tries to get files for a specific folder, assuming the input path is already in upper case.
+/// </summary>
+/// <param name="folderPath">The folder to find. Already lowercase.</param>
+/// <param name="value">The returned folder instance.</param>
+/// <returns>True if found, else false.</returns>
+[MethodImpl(MethodImplOptions.AggressiveInlining)]
+public bool TryGetFolderUpper(ReadOnlySpan<char> folderPath, out SpanOfCharDict<TTarget> value)
+{
+ // Must be O(1)
+ value = default!;
+
+ // Compare equality.
+ // Note to devs: Do not invert branches, we optimise for hot paths here.
+ if (folderPath.StartsWith(Prefix))
+ {
+ // Check for subfolder in branchless way.
+ // In CLR, bool is length 1, so conversion to byte should be safe.
+ // Even suppose it is not; as long as code is little endian; truncating int/4 bytes to byte still results
+ // in correct answer.
+ var hasSubfolder = Prefix.Length != folderPath.Length;
+ var hasSubfolderByte = Unsafe.As<bool, byte>(ref hasSubfolder);
+ var nextFolder = folderPath.SliceFast(Prefix.Length + hasSubfolderByte);
+
+ return SubfolderToFiles.TryGetValue(nextFolder, out value!);
+ }
+
+ return false;
+}
+
Something more number heavy, Fast Inverse Square Root from Quake III Arena (unmodified). +
float Q_rsqrt( float number )
+{
+ long i;
+ float x2, y;
+ const float threehalfs = 1.5F;
+
+ x2 = number * 0.5F;
+ y = number;
+ i = * ( long * ) &y; // evil floating point bit level hacking
+ i = 0x5f3759df - ( i >> 1 ); // what the fuck?
+ y = * ( float * ) &i;
+ y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
+// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
+
+ return y;
+}
+
Note
+Test
+Abstract
+Test
+Info
+Test
+Tip
+Test
+Success
+Test
+Question
+Test
+Warning
+Test
+Failure
+Test
+Danger
+Test
+Bug
+Test
+Example
+Test
+Quote
+Test
+Method | +Description | +
---|---|
GET |
+Fetch resource | +
PUT |
+Update resource | +
DELETE |
+Delete resource | +
The BufferedStreamReader
is a buffering mechanism for reading data from streams, allowing for fast reading of data while preserving stream-like semantics.
BufferedStreamReader
is a high-performance replacement for BinaryReader with minimal error checking.
BufferedStreamReader
is not thread-safe.
BufferedStreamReader
cannot read values larger than buffer size it was initialised with.
BufferedStreamReader
has minimal error checking at runtime.
Tip
+Remember, always ensure the stream and reader are properly disposed of after use.
+The best practice is to use the using
statement or try-finally
block in C# to ensure resources are correctly released.
Amount of time taken to read 8 MiB of data (library version 9.0.0):
+Legend:
+- BinaryReader
read via BinaryReader.
+- BufferedStreamReader
read via BufferedStreamReader.
+- BufferedStreamReader
read via BufferedStreamReader (ReadRaw method).
+- NativePointer
no stream; no copy; access existing data from array (baseline reference).
Benchmarks on MemoryStream (near zero overhead) allow us to compare the performance against BinaryReader
and
+array access (baseline reference).
Byte
:
Method | +Mean | +Code Size | +
---|---|---|
BinaryReader | +15,452.7 us | +171 B | +
BufferedStreamReader | +6,298.7 us | +703 B | +
BufferedStreamReader Raw | +1,997.3 us | +669 B | +
NativePointer | +1,845.2 us | +38 B | +
Int
:
Method | +Mean | +Code Size | +
---|---|---|
BinaryReader | +3,253.2 us | +637 B | +
BufferedStreamReader | +1,708.7 us | +981 B | +
BufferedStreamReader Raw | +606.8 us | +760 B | +
NativePointer | +464.1 us | +54 B | +
Long
:
Method | +Mean | +Code Size | +
---|---|---|
BinaryReader | +1,665.2 us | +639 B | +
BufferedStreamReader | +890.8 us | +709 B | +
BufferedStreamReader Raw | +370.7 us | +761 B | +
NativePointer | +227.3 us | +55 B | +
Benchmarks on FileStream allow us to compare the performance against BinaryReader
more closely.
Byte
:
Method | +Mean | +Code Size | +
---|---|---|
BinaryReader | +21,309.7 us | +169 B | +
BufferedStreamReader | +6,731.9 us | +1,052 B | +
BufferedStreamReader Raw | +2,763.3 us | +661 B | +
Int
:
Method | +Mean | +Code Size | +
---|---|---|
BinaryReader | +22,279.2 us | +640 B | +
BufferedStreamReader | +2,219.4 us | +1,487 B | +
BufferedStreamReader Raw | +1,310.4 us | +753 B | +
Long
:
Method | +Mean | +Code Size | +
---|---|---|
BinaryReader | +13,025.8 us | +642 B | +
BufferedStreamReader | +1,425.6 us | +1,068 B | +
BufferedStreamReader Raw | +943.7 us | +754 B | +
BaseStream
: The stream this class was instantiated with.BufferBytesAvailable
: The remaining number of bytes that are currently buffered.CurrentBufferSize
: The total size of the current buffered data at this moment in time.IsEndOfStream
: This is true if end of stream was reached while refilling the internal buffer.OnEndOfStream
: This method is executed if a buffer refill does not fill the whole buffer, indicating end of stream was reached.BufferedStreamReader(TStream stream, int bufferSize = 65536)
: Constructs a BufferedStreamReader
.Just like with regular Stream APIs, less data can be returned than requested if end of stream was reached. Please note BufferedStreamReader
does not throw in these scenarios.
public void Seek(long offset, SeekOrigin origin)
+
Seeks the underlying stream to a specified position.
+public void Advance(long offset)
+
Advances the underlying stream by a specified number of bytes.
+(This is equivalent to Seek(offset, SeekOrigin.Current)
)
public T Read<T>() where T : unmanaged
+public void Read<T>(out T value) where T : unmanaged
+
Reads an unmanaged, generic type from the stream.
+The returned values point to internal buffers. DO NOT MODIFY THE DATA!
+public byte* ReadRaw(int length, out int available)
+public T* ReadRaw<T>(int numItems, out int available) where T : unmanaged
+
Provides a pointer to the buffered data; buffering sufficient data if needed.
+Variant of ReadRaw that copies the data to user's own destination.
+public int ReadRaw<T>(Span<T> buffer) where T : unmanaged
+public int ReadRaw<T>(T* buffer, int numItems) where T : unmanaged
+
Reads raw data from the stream, without conversion. The output is written to the supplied pointer or span.
+This method is useful for reading large blobs (e.g. a compressed file from an archive) without discarding the buffered data.
+public int ReadBytesUnbuffered(long offset, Span<byte> data)
+
Reads a specified amount of bytes at a specific offset from the underlying stream without resetting the buffers or advancing the read pointer.
+public T ReadMarshalled<T>()
+
Reads a value that requires marshalling from the stream.
+public T Peek<T>() where T : unmanaged
+public void Peek<T>(out T value) where T : unmanaged
+
Reads an unmanaged, generic type from the stream without incrementing the position.
+public T PeekMarshalled<T>()
+public void PeekMarshalled<T>(out T value)
+
Reads a value that requires marshalling from the stream without advancing the position.
+public Int16 PeekLittleEndianInt16()
+public Int16 PeekLittleEndian(out Int16 value)
+public UInt16 PeekLittleEndianUInt16()
+public UInt16 PeekLittleEndian(out UInt16 value)
+public Int32 PeekLittleEndianInt32()
+public Int32 PeekLittleEndian(out Int32 value)
+public UInt32 PeekLittleEndianUInt32()
+public UInt32 PeekLittleEndian(out UInt32 value)
+public Int64 PeekLittleEndianInt64()
+public Int64 PeekLittleEndian(out Int64 value)
+public UInt64 PeekLittleEndianUInt64()
+public UInt64 PeekLittleEndian(out UInt64 value)
+public Single PeekLittleEndianSingle()
+public Single PeekLittleEndian(out Single value)
+public Double PeekLittleEndianDouble()
+public Double PeekLittleEndian(out Double value)
+
Peeks a little endian value of the specified type from the stream without incrementing the position.
+public Int16 PeekBigEndianInt16()
+public Int16 PeekBigEndian(out Int16 value)
+public UInt16 PeekBigEndianUInt16()
+public UInt16 PeekBigEndian(out UInt16 value)
+public Int32 PeekBigEndianInt32()
+public Int32 PeekBigEndian(out Int32 value)
+public UInt32 PeekBigEndianUInt32()
+public UInt32 PeekBigEndian(out UInt32 value)
+public Int64 PeekBigEndianInt64()
+public Int64 PeekBigEndian(out Int64 value)
+public UInt64 PeekBigEndianUInt64()
+public UInt64 PeekBigEndian(out UInt64 value)
+public Single PeekBigEndianSingle()
+public Single PeekBigEndian(out Single value)
+public Double PeekBigEndianDouble()
+public Double PeekBigEndian(out Double value)
+
Peeks a big endian value of the specified type from the stream without incrementing the position.
+public Int16 ReadLittleEndianInt16()
+public Int16 ReadLittleEndian(out Int16 value)
+public UInt16 ReadLittleEndianUInt16()
+public UInt16 ReadLittleEndian(out UInt16 value)
+public Int32 ReadLittleEndianInt32()
+public Int32 ReadLittleEndian(out Int32 value)
+public UInt32 ReadLittleEndianUInt32()
+public UInt32 ReadLittleEndian(out UInt32 value)
+public Int64 ReadLittleEndianInt64()
+public Int64 ReadLittleEndian(out Int64 value)
+public UInt64 ReadLittleEndianUInt64()
+public UInt64 ReadLittleEndian(out UInt64 value)
+public Single ReadLittleEndianSingle()
+public Single ReadLittleEndian(out Single value)
+public Double ReadLittleEndianDouble()
+public Double ReadLittleEndian(out Double value)
+
Reads a little endian value of the specified type from the stream and advances the position by the size of the type.
+public Int16 ReadBigEndianInt16()
+public Int16 ReadBigEndian(out Int16 value)
+public UInt16 ReadBigEndianUInt16()
+public UInt16 ReadBigEndian(out UInt16 value)
+public Int32 ReadBigEndianInt32()
+public Int32 ReadBigEndian(out Int32 value)
+public UInt32 ReadBigEndianUInt32()
+public UInt32 ReadBigEndian(out UInt32 value)
+public Int64 ReadBigEndianInt64()
+public Int64 ReadBigEndian(out Int64 value)
+public UInt64 ReadBigEndianUInt64()
+public UInt64 ReadBigEndian(out UInt64 value)
+public Single ReadBigEndianSingle()
+public Single ReadBigEndian(out Single value)
+public Double ReadBigEndianDouble()
+public Double ReadBigEndian(out Double value)
+
Reads a big endian value of the specified type from the stream and advances the position by the size of the type.
+Implements, IEndianedBufferStreamReader<TStream>
. Use constraint where T : IEndianedBufferStreamReader<TStream>
to write endian agnostic code without any overhead.
public LittleEndianBufferedStreamReader<TStream> AsLittleEndian();
+
Returns a reader that can be used to read little endian values from the stream.
+Implements, IEndianedBufferStreamReader<TStream>
. Use constraint where T : IEndianedBufferStreamReader<TStream>
to write endian agnostic code without any overhead.
public BigEndianBufferedStreamReader<TStream> AsBigEndian();
+
Returns a reader that can be used to read big endian values from the stream.
+The following methods are valid for structs which implement ICanReverseEndian
public T PeekLittleEndianStruct<T>() where T : unmanaged, ICanReverseEndian
+public void PeekLittleEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian
+
Peeks a little endian unmanaged, generic type from the stream without incrementing the position.
+public T PeekBigEndianStruct<T>() where T : unmanaged, ICanReverseEndian
+public void PeekBigEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian
+
Peeks a big endian unmanaged, generic type from the stream without incrementing the position.
+public T ReadLittleEndianStruct<T>() where T : unmanaged, ICanReverseEndian
+public void ReadLittleEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian
+
Reads a little endian unmanaged, generic type from the stream.
+public T ReadBigEndianStruct<T>() where T : unmanaged, ICanReverseEndian
+public void ReadBigEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian
+
Reads a big endian unmanaged, generic type from the stream.
+var stream = File.OpenRead("myFile.txt");
+using var reader = new BufferedStreamReader<FileStream>(stream);
+
int value = reader.Read<int>();
+Console.WriteLine($"Read value: {value}");
+
Do not modify the returned data! Copy it elsewhere first!
+int length = 10;
+int available;
+byte* rawBytes = reader.ReadRaw(length, out available);
+
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+Span<Vector3> span = stackalloc Vector3[10];
+int readItems = reader.ReadRaw(span);
+
+Console.WriteLine($"{readItems} Vector3 items were read from the stream.");
+
In this example, we create a BufferedStreamReader
using a FileStream
and then declare a Span<Vector3>
where Vector3
+is a struct representing a 3D vector. We then read from the stream directly into the Span<Vector3>
. After reading, we
+print out how many Vector3
items were read from the stream.
var reader = new BufferedStreamReader<FileStream>(fileStream);
+reader.Seek(100, SeekOrigin.Begin); // Seek to 100 bytes from the start
+reader.Advance(50); // Advance 50 bytes from the current position
+
+Console.WriteLine($"Current stream position is {reader.Position()}");
+
In this example, we first seek to 100 bytes from the start of the stream. Then we advance 50 bytes from the current position. After that, we print out the current stream position.
+using var reader = new BufferedStreamReader<FileStream>(fileStream);
+int value = reader.Read<int>(); // Reads an integer from the stream
+
+Console.WriteLine($"The read integer value is {value}");
+
In this example, we read an integer directly from the stream and print it out.
+using var reader = new BufferedStreamReader<FileStream>(fileStream);
+Span<byte> dataSpan = stackalloc byte[1024];
+int bytesRead = reader.ReadBytesUnbuffered(200, dataSpan);
+
+Console.WriteLine($"{bytesRead} bytes were read from the stream.");
+
In this example, we read 1024 bytes starting from 200 bytes offset into a Span<byte>
without resetting the buffers or
+advancing the read pointer. After reading, we print out how many bytes were read from the stream.
Value of bytesRead
may be less than length of dataSpan
if end of stream was reached.
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+int value = reader.Peek<int>(); // Peeks an integer from the stream
+
+Console.WriteLine($"The peeked integer value is {value}. Stream did not advance.");
+
The default buffer size of 64KBytes should be sufficient for most use cases, including file reads.
+int bufferSize = 8192; // 8 KB
+using var reader = new BufferedStreamReader<FileStream>(fileStream, bufferSize);
+
+Console.WriteLine($"Custom buffer size of {bufferSize} bytes is used for the reader.");
+
In this example, a custom buffer size is set when creating a BufferedStreamReader
. This allows you to tune the buffer
+size to match your specific use case, potentially improving performance.
var reader = new BufferedStreamReader<FileStream>(fileStream);
+reader.Seek(100, SeekOrigin.Begin);
+int value = reader.Read<int>();
+reader.Advance(50);
+Vector3 vector = reader.Read<Vector3>();
+
+Console.WriteLine($"Read integer value: {value}, Vector3: {vector.X}, {vector.Y}, {vector.Z}.");
+
In this final example, multiple operations are combined. First, the reader seeks to a specific position in the stream.
+Then, an integer is read, the reader advances a certain number of bytes, and finally, a Vector3
struct is read.
+The results are then printed to the console.
Shows you how to write code that works with both Big and Little Endian data.
+using var reader = new BufferedStreamReader<FileStream>(fileStream);
+
+// Read in Big Endian
+Read(reader.AsBigEndian());
+// or... as Little Endian
+Read(reader.AsLittleEndian());
+
+private void Read<TReader>(TReader reader) where TReader : IEndianedBufferStreamReader
+{
+ // Some neat parsing code here.
+ var i16 = reader.ReadInt16();
+ var i32 = reader.PeekInt32();
+}
+
This approach allows you to write code which uses the same logic for both big and little endian.
+You can either pass reader.AsLittleEndian()
or reader.AsBigEndian()
to your parsing code. Either way, your reader
+will be devirtualized and you'll be able to parse away with 0 overhead.
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+
+// Little Endian
+Int16 littleEndianInt16 = reader.PeekLittleEndianInt16();
+Console.WriteLine($"The read Int16 value at offset 0 in Little Endian is {littleEndianInt16}");
+
+// Big Endian
+Int16 bigEndianInt16 = reader.PeekBigEndianInt16();
+Console.WriteLine($"The read Int16 value at offset 0 in Big Endian is {bigEndianInt16}");
+
CustomStruct must implement ICanReverseEndian
Example:
+public struct CustomStruct : ICanReverseEndian
+{
+ public int Value1;
+ public int Value2;
+ public int Value3;
+
+ // ICanReverseEndian
+ public void ReverseEndian()
+ {
+ Value1 = Endian.Reverse(Value1);
+ Value2 = Endian.Reverse(Value2);
+ Value3 = Endian.Reverse(Value3);
+ }
+}
+
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+var value = reader.ReadLittleEndianStruct<CustomStruct>();
+
+Console.WriteLine($"The read CustomStruct value is {value}");
+
In this example, a CustomStruct
is read directly from the stream using little endian byte order.
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+var value = reader.ReadBigEndianStruct<CustomStruct>();
+
+Console.WriteLine($"The read CustomStruct value is {value}");
+
In this example, a CustomStruct
is read directly from the stream using big endian byte order.
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+var value = reader.PeekLittleEndianStruct<CustomStruct>();
+
+Console.WriteLine($"The peeked CustomStruct value is {value}. Stream did not advance.");
+
In this example, a CustomStruct
is peeked from the stream without advancing the stream position, using little endian byte order.
using var reader = new BufferedStreamReader<FileStream>(fileStream);
+var value = reader.PeekBigEndianStruct<CustomStruct>();
+
+Console.WriteLine($"The peeked CustomStruct value is {value}. Stream did not advance.");
+
In this example, a CustomStruct
is peeked from the stream without advancing the stream position, using big endian byte order.
CustomStruct
must be an unmanaged type and implement ICanReverseEndian
interface.
Common Info About Endian Readers and Writers
+Structs can implement ICanBeReadByAnEndianReader
and ICanWriteToAnEndianWriter
to allow for easy reading and writing.
Example:
+The structs provide methods named WriteAtOffset
/ ReadAtOffset
to operate on an offset of the current pointer without advancing the pointer itself.
These methods offer some minor performance advantages.
+By reducing the dependency of future instructions on earlier instructions, these offset methods allow for better pipelining.
+For example, a future read operation does not need to wait for the Ptr
value to be updated from a previous operation.
The Just-In-Time (JIT) compiler can recognize when the offset
parameters are specified as constants and can optimize
+the instructions accordingly. This can lead to more efficient code execution.
writer.WriteAtOffset(Hash, 0);
+writer.WriteAtOffset((int)DecompressedSize, 8);
+writer.WriteAtOffset(new OffsetPathIndexTuple(DecompressedBlockOffset, FilePathIndex, FirstBlockIndex).Data, 12);
+writer.Seek(NativeFileEntryV0.SizeBytes);
+
Because write on line 1
, does not depend on modified pointer after line 0
, execution is faster, as the CPU can
+better pipeline the instructions as there is no dependency on the ptr result of the previous method call.
A struct for reading from a pointer in Big Endian.
+BigEndianReader is preferred over BufferedStreamReader when all data is already in memory.
+The BigEndianReader
struct provides utility methods for reading various data types from a pointer in Big Endian format.
These methods read the specified data type from the current pointer in Big Endian format and advance the pointer.
+public byte ReadByte()
+public sbyte ReadSByte()
+public short ReadShort()
+public ushort ReadUShort()
+public uint ReadUInt()
+public int ReadInt()
+public ulong ReadULong()
+public long ReadLong()
+public float ReadFloat()
+public double ReadDouble()
+
These methods read the specified data type from the specified offset in Big Endian format without advancing the pointer.
+public byte ReadByteAtOffset(int offset)
+public sbyte ReadSByteAtOffset(int offset)
+public short ReadShortAtOffset(int offset)
+public ushort ReadUShortAtOffset(int offset)
+public uint ReadUIntAtOffset(int offset)
+public int ReadIntAtOffset(int offset)
+public ulong ReadULongAtOffset(int offset)
+public long ReadLongAtOffset(int offset)
+public float ReadFloatAtOffset(int offset)
+public double ReadDoubleAtOffset(int offset)
+
This method advances the pointer by a specified number of bytes.
+public void Seek(int offset)
+
The BigEndianReader
struct provides several methods that read from a specific offset without advancing the pointer.
+These methods include ReadShortAtOffset
, ReadIntAtOffset
, ReadLongAtOffset
, and ReadUlongAtOffset
.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, +you can read more about this in About Section
+BigEndianReader reader = new BigEndianReader(GetPointer());
+
+byte byteValue = reader.ReadByte();
+uint uintValue = reader.ReadUInt();
+double doubleValue = reader.ReadDouble();
+
BigEndianReader reader = new BigEndianReader(GetPointer());
+
+byte byteValue = reader.ReadByteAtOffset(5);
+uint uintValue = reader.ReadUIntAtOffset(10);
+double doubleValue = reader.ReadDoubleAtOffset(15);
+
BigEndianReader reader = new BigEndianReader(GetPointer(););
+
+reader.Seek(10); // Advances the pointer by 10 bytes.
+
Utility struct for writing to a pointer in Big Endian.
+BigEndianWriter
is a struct providing methods for writing various types of data to a memory location, with the data
+being written in Big Endian
byte order. This includes writing integers, floating point values, and arrays of bytes,
+either advancing the pointer after each write or writing at a specific offset without advancing the pointer.
public byte* Ptr;
+
Current pointer being written to.
+public BigEndianWriter(byte* ptr)
+
Creates a simple wrapper around a pointer that writes in Big Endian. ptr
is the pointer to the item behind the writer.
public void Write(sbyte value)
+public void Write(byte value)
+public void Write(short value)
+public void Write(ushort value)
+public void Write(int value)
+public void Write(uint value)
+public void Write(long value)
+public void Write(ulong value)
+public void Write(float value)
+public void Write(double value)
+public void Write(Span<byte> data)
+
Writes a value to the current pointer and advances the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
, and Span<byte>
.
public void WriteAtOffset(sbyte value, int offset)
+public void WriteAtOffset(byte value, int offset)
+public void WriteAtOffset(short value, int offset)
+public void WriteAtOffset(ushort value, int offset)
+public void WriteAtOffset(int value, int offset)
+public void WriteAtOffset(uint value, int offset)
+public void WriteAtOffset(long value, int offset)
+public void WriteAtOffset(ulong value, int offset)
+public void WriteAtOffset(float value, int offset)
+public void WriteAtOffset(double value, int offset)
+
Writes a value to the specified offset without advancing the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
.
public void Seek(int offset)
+
Advances the stream by a specified number of bytes. offset
is the number of bytes to advance by.
The BigEndianWriter
struct provides several methods that operate on a specific offset without advancing the pointer.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, +you can read more about this in About Section
+Example usage of the BigEndianWriter
struct:
var writer = new BigEndianWriter(ptr);
+
+writer.Write((int)12345); // Write an int
+writer.Write((byte)65); // Write a byte
+writer.WriteAtOffset((long)9876543210, 5); // Write a long at offset 5 from current pointer
+
A struct for reading from a pointer in Big Endian.
+LittleEndianReader is preferred over BufferedStreamReader when all data is already in memory.
+The LittleEndianReader
struct provides utility methods for reading various data types from a pointer in Big Endian format.
These methods read the specified data type from the current pointer in Big Endian format and advance the pointer.
+public byte ReadByte()
+public sbyte ReadSByte()
+public short ReadShort()
+public ushort ReadUShort()
+public uint ReadUInt()
+public int ReadInt()
+public ulong ReadULong()
+public long ReadLong()
+public float ReadFloat()
+public double ReadDouble()
+
These methods read the specified data type from the specified offset in Big Endian format without advancing the pointer.
+public byte ReadByteAtOffset(int offset)
+public sbyte ReadSByteAtOffset(int offset)
+public short ReadShortAtOffset(int offset)
+public ushort ReadUShortAtOffset(int offset)
+public uint ReadUIntAtOffset(int offset)
+public int ReadIntAtOffset(int offset)
+public ulong ReadULongAtOffset(int offset)
+public long ReadLongAtOffset(int offset)
+public float ReadFloatAtOffset(int offset)
+public double ReadDoubleAtOffset(int offset)
+
This method advances the pointer by a specified number of bytes.
+public void Seek(int offset)
+
The LittleEndianReader
struct provides several methods that read from a specific offset without advancing the pointer.
+These methods include ReadShortAtOffset
, ReadIntAtOffset
, ReadLongAtOffset
, and ReadUlongAtOffset
.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, +you can read more about this in About Section
+LittleEndianReader reader = new LittleEndianReader(GetPointer());
+
+byte byteValue = reader.ReadByte();
+uint uintValue = reader.ReadUInt();
+double doubleValue = reader.ReadDouble();
+
LittleEndianReader reader = new LittleEndianReader(GetPointer());
+
+byte byteValue = reader.ReadByteAtOffset(5);
+uint uintValue = reader.ReadUIntAtOffset(10);
+double doubleValue = reader.ReadDoubleAtOffset(15);
+
LittleEndianReader reader = new LittleEndianReader(GetPointer(););
+
+reader.Seek(10); // Advances the pointer by 10 bytes.
+
Utility struct for writing to a pointer in Big Endian.
+LittleEndianWriter
is a struct providing methods for writing various types of data to a memory location, with the data
+being written in Big Endian
byte order. This includes writing integers, floating point values, and arrays of bytes,
+either advancing the pointer after each write or writing at a specific offset without advancing the pointer.
public byte* Ptr;
+
Current pointer being written to.
+public LittleEndianWriter(byte* ptr)
+
Creates a simple wrapper around a pointer that writes in Big Endian. ptr
is the pointer to the item behind the writer.
public void Write(sbyte value)
+public void Write(byte value)
+public void Write(short value)
+public void Write(ushort value)
+public void Write(int value)
+public void Write(uint value)
+public void Write(long value)
+public void Write(ulong value)
+public void Write(float value)
+public void Write(double value)
+public void Write(Span<byte> data)
+
Writes a value to the current pointer and advances the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
, and Span<byte>
.
public void WriteAtOffset(sbyte value, int offset)
+public void WriteAtOffset(byte value, int offset)
+public void WriteAtOffset(short value, int offset)
+public void WriteAtOffset(ushort value, int offset)
+public void WriteAtOffset(int value, int offset)
+public void WriteAtOffset(uint value, int offset)
+public void WriteAtOffset(long value, int offset)
+public void WriteAtOffset(ulong value, int offset)
+public void WriteAtOffset(float value, int offset)
+public void WriteAtOffset(double value, int offset)
+
Writes a value to the specified offset without advancing the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
.
public void Seek(int offset)
+
Advances the stream by a specified number of bytes. offset
is the number of bytes to advance by.
The LittleEndianWriter
struct provides several methods that operate on a specific offset without advancing the pointer.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, +you can read more about this in About Section
+Example usage of the LittleEndianWriter
struct:
var writer = new LittleEndianWriter(ptr);
+
+writer.Write((int)12345); // Write an int
+writer.Write((byte)65); // Write a byte
+writer.WriteAtOffset((long)9876543210, 5); // Write a long at offset 5 from current pointer
+
ArrayRental
is a struct that represents an instance of a rented array. It should be disposed of after use with the using
statement.
If you need a generic version, use ArrayRental<T>
.
The underlying array is managed by the shared ArrayPool
.
Array
: The underlying array for this rental. Span
: Span for the underlying array. May be larger than requested length.public ArrayRental(int numBytes)
+
Rents a requested minimum number of bytes. Amount of data rented might be larger.
+ArrayRentalSlice
represents a slice of an ArrayRental
. This API is meant to be used as a return value from methods,
+and transfers ownership of the rental from the internal ArrayRental
.
public ArrayRentalSlice(ArrayRental rental, int length)
+
Represents a slice of the array rental.
+Make sure to dispose with using
statement or explicit dispose.
// Will create a rental of at least 4096 bytes.
+using var rental = new ArrayRental(4096);
+
When you create an ArrayRentalSlice
, the responsibility of disposing the rental is transferred to the slice. Make sure to not double dispose.
// Some Method
+ArrayRentalSlice CompressData(byte* data, int length)
+{
+ var rental = new ArrayRental(numBytes);
+ // Compress into rental....
+ // And return a slice with just the info needed.
+ return new ArrayRentalSlice(rental, sliceLength);
+}
+
+// Method consumer
+using var compressed = CompressData(data, length);
+
Forked from Community Toolkit.
+A class representing a boxed value type, providing build-time validation and automatic unboxing.
+Box
public static Box<T> GetFrom(object obj)
+
Returns a Box<T>
reference from the input object
instance, representing a boxed T
value.
public static Box<T> DangerousGetFrom(object obj)
+
Returns a Box<T>
reference from the input object
instance, representing a boxed T
value. This method doesn't check the actual type of obj
, so it is the responsibility of the caller to ensure it actually represents a boxed T
value and not some other instance.
public static bool TryGetFrom(object obj, out Box<T>? box)
+
Tries to get a Box<T>
reference from an input object
representing a boxed T
value. Returns true
if a Box<T>
instance was retrieved correctly, false
otherwise.
public static implicit operator T(Box<T> box)
+
Implicitly gets the T
value from a given Box<T>
instance.
public static implicit operator Box<T>(T value)
+
Implicitly creates a new Box<T>
instance from a given T
value.
Box<int> box = 42;
+int sum = box.Value + 1;
+
Box<MyStruct> box = new MyStruct { Field1 = 1, Field2 = 2 };
+ref MyStruct myStructRef = ref box.GetReference();
+myStructRef.Field1 = 3;
+
public static ref T GetReference<T>(this Box<T> box) where T : struct
+
Gets a T
reference from a Box<T>
instance.
Box<MyStruct> box = new MyStruct { Field1 = 1, Field2 = 2 };
+ref MyStruct myStructRef = ref box.GetReference();
+
The CircularBuffer
is a writable buffer useful for temporary storage of data. It's a buffer whereby once you reach the end of the buffer, it loops back over to the beginning of the buffer, overwriting old elements.
Start
: The address of the CircularBuffer
.End
: The address of the CircularBuffer
.Current
: Address of the current item in the buffer.Remaining
: Remaining space in the buffer.Size
: The overall size of the buffer.CircularBuffer(nuint start, int size)
: Creates a CircularBuffer
within the target memory source.public nuint Add(byte* data, uint length)
+public nuint Add<TSource>(TSource source, byte* data, uint length)
+public nuint Add<TSource, T>(TSource source, T value)
+public nuint Add<T>(T value)
+
Adds a new item onto the circular buffer. Returns a pointer to the recently added item to the buffer, or zero if the item cannot fit.
+public ItemFit CanItemFit(uint itemSize)
+public ItemFit CanItemFit<T>()
+
Returns an enumerable describing if an item can fit into the buffer.
+Describes whether an item can fit into a given buffer.
+Yes
: The item can fit into the buffer.StartOfBuffer
: The item can fit into the buffer, but not in the remaining space (will be placed at start of buffer).No
: The item is too large to fit into the buffer.var circularBuffer = new CircularBuffer((nuint)bufferStartPtr, bufferSize);
+nuint pointerToValue = circularBuffer.Add(42);
+
+if (pointerToValue != UIntPtr.Zero)
+ Console.WriteLine("Value added successfully!");
+else
+ Console.WriteLine("Failed to add value to the buffer.");
+
var circularBuffer = new CircularBuffer((nuint)bufferStart, bufferSize);
+var valueToAdd = new Vector2 { X = 1, Y = 2 };
+nuint pointerToValue = circularBuffer.Add(valueToAdd);
+
+if (pointerToValue != UIntPtr.Zero)
+ Console.WriteLine("Value added successfully!");
+else
+ Console.WriteLine("Failed to add value to the buffer.");
+
var circularBuffer = new CircularBuffer((nuint)bufferStart, bufferSize);
+var result = circularBuffer.CanItemFit<double>();
+
+switch (result)
+{
+ case CircularBuffer.ItemFit.Yes:
+ // "A double can fit in the buffer."
+ break;
+ case CircularBuffer.ItemFit.StartOfBuffer:
+ // "A double can fit in the buffer, but it will be placed at the start of the buffer."
+ break;
+ case CircularBuffer.ItemFit.No:
+ // "A double cannot fit in the buffer."
+ break;
+}
+
The Endian
class provides various utilities for converting primitives and structures between endians.
public static byte Reverse(byte value)
+public static sbyte Reverse(sbyte value)
+public static short Reverse(short value)
+public static ushort Reverse(ushort value)
+public static int Reverse(int value)
+public static uint Reverse(uint value)
+public static long Reverse(long value)
+public static ulong Reverse(ulong value)
+public static float Reverse(float value)
+public static double Reverse(double value)
+
Reverses the byte order of the specified value.
+Info
+Utility method for structs with single values.
+Warning
+This method is provided for convenience.
+If the item size is not 1/2/4/8 bytes, this method will throw.
public static T Reverse<T>(T value) where T : unmanaged
+
Reverses the endian of a primitive value such as int, short, float, double etc.
+int value = 42;
+int reversedValue = Endian.Reverse(value);
+
+Console.WriteLine($"Original Value: {value}, Reversed Value: {reversedValue}");
+
This will print something like:
+Original Value: 42, Reversed Value: 704643072
+
Warning
+This will only work if the struct is a blittable type.
+struct NamedOffset
+{
+ public int Offset;
+}
+
+MyStruct value = new NamedOffset { Offset = 1 };
+MyStruct reversedValue = Endian.Reverse(value);
+
+Console.WriteLine($"Original Value: {value.X}, Reversed Value: {reversedValue.X}");
+
This will print something like:
+Original Value: 1 Reversed Value: 16777216
+
Forked from Community Toolkit.
+Utility class providing methods for working with object
instances.
ObjectMarshal is a utility class that provides methods for calculating byte offsets to specific fields within objects, retrieving references to data within objects at specific offsets, and unboxing values from objects.
+public static IntPtr DangerousGetObjectDataByteOffset<T>(object obj, ref T data)
+
Calculates the byte offset to a specific field within a given object
. The input parameters are not validated, and it's
+the responsibility of the caller to ensure that the data
reference is actually pointing to a memory location within obj
.
public static ref T DangerousGetObjectDataReferenceAt<T>(object obj, IntPtr offset)
+
Gets a T
reference to data within a given object at a specified offset. None of the input arguments is validated, and
+it is the responsibility of the caller to ensure they are valid.
public static bool TryUnbox<T>(this object obj, out T value) where T : struct
+
Tries to get a boxed T
value from an input object
instance. Returns true if a T
value was retrieved correctly,
+false
otherwise.
public static ref T DangerousUnbox<T>(object obj) where T : struct
+
Unboxes a T
value from an input object instance. Throws an InvalidCastException
when obj is not of type T
.
MyStruct myStruct = new MyStruct();
+ref int fieldRef = ref myStruct.SomeIntField;
+IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(myStruct, ref fieldRef);
+
MyStruct myStruct = new MyStruct();
+IntPtr fieldOffset = /* calculated offset */;
+ref int fieldRef = ref ObjectMarshal.DangerousGetObjectDataReferenceAt<MyStruct>(myStruct, fieldOffset);
+
object boxedInt = 42;
+int unboxedInt;
+
+if (boxedInt.TryUnbox(out unboxedInt))
+ Console.WriteLine($"Unboxed value: {unboxedInt}");
+
object boxedInt = 42;
+ref int unboxedInt = ref ObjectMarshal.DangerousUnbox<int>(boxedInt);
+
Allows you to pin an unmanaged object in a static location in memory, to be later accessible from native code.
+Assume value is copied; modify the Pinnable instance to read/write values once created.
+The Pinnable<T>
class provides a way to pin native unmanaged objects in memory, ensuring that their memory addresses
+remain constant during the lifetime of the Pinnable<T>
instance. This can be useful when working with native
+code that requires static memory addresses.
On newer runtimes, the memory is allocated into the Pinned Object Heap (POH)
; thus has no impact on effectiveness
+of regular garbage collection.
Value
: The value pointed to by the Pointer
. If the class was instantiated using an array, this is the first element of the array.Pointer
: Pointer to the native value in question. If the class was instantiated using an array, this is the pointer to the first element of the array.public Pinnable(T[] value); // Pins an array of values to the heap.
+public Pinnable(in T value); // Pins a single value to the heap.
+
The Pinnable<T>
class implements the IDisposable
interface, which means you should dispose of the instance when you are done with it.
This can be done using the using
statement, or by explicitly calling the Dispose()
method.
int value = 42;
+
+using var pinnable = new Pinnable<int>(value);
+// Access the pinned value through pinnable.Value
+// Access the memory address of the pinned value through pinnable.Pointer
+
int[] array = new int[] { 1, 2, 3, 4, 5 };
+
+using var pinnable = new Pinnable<int>(value);
+// Access array element via `pinnable[x]`.
+// Access the first element of the pinned array through pinnable.Value
+// Access the memory address of the first element of the pinned array through pinnable.Pointer
+
Utility class providing methods for obtaining information about different types.
+public static bool ApproximateIsBlittable<T>()
+
true
if a type is blittable, else false
. This method uses an approximation, and may not work with generic types with blittable (unmanaged) constraints.
+public static bool ApproximateIsBlittable(Type type)
+
true
if a type is blittable, else false
. This method uses an approximation, and may not work with generic types with blittable (unmanaged) constraints.
+public static int MarshalledSizeOf<T>()
+
bool isBlittable = TypeInfo.ApproximateIsBlittable<int>();
+
bool isBlittable = TypeInfo.ApproximateIsBlittable(typeof(int));
+
int size = TypeInfo.MarshalledSizeOf<int>();
+
Reloaded.Memory is a high performance library which provides zero-cost abstractions
for memory manipulation in C#.
+It is designed to be as fast as possible, with no overhead, while providing useful functionality to the user.
The library has the following characteristics.
+This project guarantees binary backwards compatibility; meaning you can substitute the library with any newer version +without recompiling the source code. Should the need to introduce any breaking changes occur; much like the runtime.
+Common Classes within this Package Include
+Memory Manipulation:
+Action | +Description | +
---|---|
Memory | +Allows you to Read, Write, Allocate & Change Memory Protection for Current Process. | +
ExternalMemory | +Read, Write, Allocate & Change Memory Protection but for Another Process. | +
Streams Management:
+Action | +Description | +
---|---|
BigEndian(Reader/Writer) | +Read/write raw data in memory as Big Endian. | +
LittleEndian(Reader/Writer) | +Read/write raw data in memory as Little Endian. | +
BufferedStreamReader | +High performance alternative to BinaryReader . |
+
Extensions:
+Action | +Description | +
---|---|
(Array/Span)Extensions | +Unsafe slicing, references without bounds checks and SIMD accelerated extensions. | +
StreamExtensions | +Extensions for reading and writing from/to generics. | +
StringExtensions | +Custom Hash Function(s) and unsafe character references. | +
Utilities:
+Action | +Description | +
---|---|
ArrayRental & ArrayRentalSlice | +Safe wrapper around ArrayPool<T> rentals. |
+
Box |
+Represents a boxed value type, providing build-time validation and automatic unboxing. | +
CircularBuffer | +Basic high-performance circular buffer. | +
Pinnable |
+Utility for pinning C# objects for access from native code. | +
Base building blocks:
+Action | +Description | +
---|---|
Ptr<T> / MarshalledPtr<T> | +Abstraction over a pointer to arbitrary source. | +
FixedArrayPtr<T> & MarshalledFixedArrayPtr<T> | +Abstraction over a pointer with known length. | +
(This list is not exhaustive, please see the API Documentation for complete API)
+If you have questions/bug reports/etc. feel free to Open an Issue.
+Contributions are welcome and encouraged. Feel free to implement new features, make bug fixes or suggestions so long as +they meet the quality standards set by the existing code in the repository.
+For an idea as to how things are set up, see Reloaded Project Configurations.
+Happy Hacking 💜
+ + + + + + + + +Reloaded.Memory is a high performance library which provides zero-cost abstractions
for memory manipulation in C#. It is designed to be as fast as possible, with no overhead, while providing useful functionality to the user.
The library has the following characteristics.
This project guarantees binary backwards compatibility; meaning you can substitute the library with any newer version without recompiling the source code. Should the need to introduce any breaking changes occur; much like the runtime.
"},{"location":"#common-utilities","title":"Common Utilities","text":"Common Classes within this Package Include
Memory Manipulation:
Action Description Memory Allows you to Read, Write, Allocate & Change Memory Protection for Current Process. ExternalMemory Read, Write, Allocate & Change Memory Protection but for Another Process.Streams Management:
Action Description BigEndian(Reader/Writer) Read/write raw data in memory as Big Endian. LittleEndian(Reader/Writer) Read/write raw data in memory as Little Endian. BufferedStreamReader High performance alternative toBinaryReader
. Extensions:
Action Description (Array/Span)Extensions Unsafe slicing, references without bounds checks and SIMD accelerated extensions. StreamExtensions Extensions for reading and writing from/to generics. StringExtensions Custom Hash Function(s) and unsafe character references.Utilities:
Action Description ArrayRental & ArrayRentalSlice Safe wrapper aroundArrayPool<T>
rentals. Box Represents a boxed value type, providing build-time validation and automatic unboxing. CircularBuffer Basic high-performance circular buffer. Pinnable Utility for pinning C# objects for access from native code. Base building blocks:
Action Description Ptr<T> / MarshalledPtr<T> Abstraction over a pointer to arbitrary source. FixedArrayPtr<T> & MarshalledFixedArrayPtr<T> Abstraction over a pointer with known length.(This list is not exhaustive, please see the API Documentation for complete API)
"},{"location":"#community-feedback","title":"Community Feedback","text":"If you have questions/bug reports/etc. feel free to Open an Issue.
Contributions are welcome and encouraged. Feel free to implement new features, make bug fixes or suggestions so long as they meet the quality standards set by the existing code in the repository.
For an idea as to how things are set up, see Reloaded Project Configurations.
Happy Hacking \ud83d\udc9c
"},{"location":"About-Memory/","title":"Memory API","text":"Info
Reloaded.Memory
provides various abstractions that can be used to wrap around contiguous regions of memory.
All APIs listed here are zero overhead.
Stuff listed here isn't that impressive, but it's the basic building block for what follows next.
"},{"location":"About-Memory/#memory-externalmemory","title":"Memory & ExternalMemory","text":"Info
The Memory
and ExternalMemory
classes are the most basic abstractions provided by Reloaded.Memory
. They allow you to access memory either within the current or a target process
.
Memory
w/ ExternalMemory
while (ptr < maxAddress)\n{\nresult += *ptr;\nptr += 1;\n}\n
if (Polyfills.IsWindows())\nreturn Kernel32.ReadProcessMemory(_processHandle, location, (nuint)buffer, numBytes, out _);\n\nif (Polyfills.IsLinux())\nreturn Posix.process_vm_readv_k32(_processHandle, location, (nuint)buffer, numBytes);\n\n// And other cases!\n
// memory = Memory.Instance;\nwhile (ptr < maxAddress)\n{\nresult += memory.Read<nuint>((UIntPtr)ptr);\nptr += 1;\n}\n
// memory = new ExternalMemory(process);\nwhile (ptr < maxAddress)\n{\nresult += memory.Read<nuint>((UIntPtr)ptr);\nptr += 1;\n}\n
As you can see, with the library and its ICanReadWriteMemory
interface; usage is unified across all sources. Instead of having to write different code for different sources (first 2 examples), you can now write the same code for all sources.
And of course, various different utility methods are provided to make your life easier.
Raw Pointer MarshallingAnother Processw/Memory
w/ ExternalMemory
while (ptr < maxAddress)\nMarshal.StructureToPtr(items[x++], (nint)offset, false);\n
byte* bufferPtr = new byte[structSize];\nbool succeeded = ReadProcessMemory(offset, bufferPtr, (nuint)structSize);\nif (!succeeded)\nThrowHelpers.ThrowReadExternalMemoryExceptionWindows(offset, structSize);\n\nMarshal.PtrToStructure((nint)bufferPtr, value);\n
while (ptr < maxAddress)\nmemory.WriteWithMarshalling(ptr, items[x++]);\n
while (ptr < maxAddress)\nmemory.WriteWithMarshalling(ptr, items[x++]);\n
All silly boilerplate needed to manipulate different sources is gone; and this is all done with zero-overhead.
"},{"location":"About-Memory/#unified-memory-allocation-api","title":"Unified Memory Allocation API","text":"Info
Structs like Memory
and ExternalMemory
employ ICanAllocateMemory
API to make memory allocations convenient.
Memory
w/ ExternalMemory
var allocation = NativeMemory.Alloc(100);\n
var allocation = Marshal.AllocHGlobal(100);\n
var allocation = memory.Allocate(100);\n
var allocation = memory.Allocate(100);\n
Now you can allocate in another process in a consistent manner. Useful?
ICanAllocateMemory
for ExternalMemory
currently implemented in Windows only; PRs for Linux and OSX.
Info
Structs like Memory
and ExternalMemory
employ ICanChangeMemoryProtection
API to allow you to change memory permissions.
This allows you to make existing code etc. in memory writable for editing.
This Processw/Memory
w/ ExternalMemory
if (Polyfills.IsWindows())\n{\nbool result = Kernel32.VirtualProtect(memoryAddress, (nuint)size, (Kernel32.MEM_PROTECTION)newProtection,\nout Kernel32.MEM_PROTECTION oldPermissions);\nif (!result)\nThrowHelpers.ThrowMemoryPermissionExceptionWindows(memoryAddress, size, newProtection);\n\nreturn (nuint)oldPermissions;\n}\n\nif (Polyfills.IsLinux() || Polyfills.IsMacOS())\n{\n// ... lot more boilerplate\n}\n
var oldPermissions = source.ChangeProtection(address, length, MemoryProtection.READ);\n
var oldPermissions = source.ChangeProtection(address, length, MemoryProtection.READ);\n
Pretty useful huh?
ICanChangeMemoryProtection
for ExternalMemory
currently implemented in Windows only; PRs for Linux and OSX are welcome.
Info
These interfaces, and combinations of them allow for some very useful utility methods to be made.
Temporary allocate a buffer:
// Automatically disposed, even on exception\nusing var alloc = memory.AllocateDisposable(DataSize);\n
Temporary change memory permission:
using var alloc = memory.ChangeProtectionDisposable(DataSize);\n
Temporary change memory permission, write data, and restore:
memory.SafeWrite(Alloc.Address, Data.AsSpanFast());\n
"},{"location":"About-Memory/#reference-benchmarks","title":"Reference Benchmarks","text":"In most cases, the abstractions generate 1:1 code that matches exactly the same performance as working with raw pointers.
| Method | Mean | Error | StdDev | Code Size | Allocated |\n|------------------------------ |---------:|--------:|--------:|----------:|----------:|\n| ReadViaPointer | 130.7 ns | 0.77 ns | 0.69 ns | 49 B | - |\n| ReadViaMemory | 132.4 ns | 0.71 ns | 0.66 ns | 52 B | - |\n| ReadViaMemory_ViaOutParameter | 132.2 ns | 1.81 ns | 1.70 ns | 52 B | - |\n| WriteViaPointer | 117.4 ns | 1.55 ns | 1.45 ns | 48 B | - |\n| WriteViaMemory | 117.8 ns | 1.67 ns | 1.57 ns | 48 B | - |\n
ReadViaPointer
and WriteViaPointer
are using raw pointers; remaining tests are using the abstractions.
Partially forked from Community Toolkit.
Utility class providing high performance extension methods for working with arrays.
ArrayExtensions is a utility class that provides extension methods for arrays, including methods for getting references without bounds checks, counting occurrences of a value, checking covariance, and converting arrays to spans.
"},{"location":"Extensions/ArrayExtensions/#methods","title":"Methods","text":""},{"location":"Extensions/ArrayExtensions/#dangerousgetreference","title":"DangerousGetReference","text":"public static ref T DangerousGetReference<T>(this T[] array)\n
Returns a reference to the first element within a given T[]
array, with no bounds checks. The caller is responsible for performing checks in case the returned value is dereferenced.
public static ref T DangerousGetReferenceAt<T>(this T[] array, int i)\n
Returns a reference to an element at a specified index within a given T[]
array, with no bounds checks. The caller is responsible for ensuring the i
parameter is valid.
public static int Count<T>(this T[] array, T value) where T : IEquatable<T>\n
Counts the number of occurrences of a given value in a target T[]
array instance.
public static bool IsCovariant<T>(this T[] array)\n
Checks whether or not a given T[]
array is covariant.
Only supported on .NET 5 and above. Older runtimes will use default AsSpan
.
public static Span<T> AsSpanFast<T>(this T[] data)\n
Converts a byte array to a Span without doing a null check.
"},{"location":"Extensions/ArrayExtensions/#usage","title":"Usage","text":""},{"location":"Extensions/ArrayExtensions/#get-reference-without-bounds-checks","title":"Get Reference without Bounds Checks","text":"int[] array = new int[] { 1, 2, 3 };\nref int firstElement = ref array.DangerousGetReference();\n
"},{"location":"Extensions/ArrayExtensions/#get-reference-at-specific-index-without-bounds-checks","title":"Get Reference at Specific Index without Bounds Checks","text":"int[] array = new int[] { 1, 2, 3 };\nref int elementAtTwo = ref array.DangerousGetReferenceAt(2);\n
"},{"location":"Extensions/ArrayExtensions/#count-occurrences-of-a-value","title":"Count Occurrences of a Value","text":"int[] array = new int[] { 1, 2, 2, 3, 2 };\nint count = array.Count(2);\n
"},{"location":"Extensions/ArrayExtensions/#check-covariance","title":"Check Covariance","text":"object[] array = new object[] { \"a\", \"b\", \"c\" };\nbool isCovariant = array.IsCovariant<string>();\n
"},{"location":"Extensions/ArrayExtensions/#convert-array-to-span-without-null-check","title":"Convert Array to Span without Null Check","text":"int[] array = new int[] { 1, 2, 3 };\nSpan<int> span = array.AsSpanFast();\n
"},{"location":"Extensions/BoolExtensions/","title":"BoolExtensions","text":"Forked from Community Toolkit.
Utility class providing high performance extension methods for working with the bool
type.
BoolExtensions is a utility class that provides extension methods for the bool
type, including converting bool
values to byte
, int
, and long
masks.
public static unsafe byte ToByte(this bool flag)\n
Converts the given bool
value into a byte
. Returns 1 if flag
is true
, 0 otherwise. This method does not contain branching instructions.
public static unsafe int ToBitwiseMask32(this bool flag)\n
Converts the given bool
value to an int
mask with all bits representing the value of the input flag (either 0xFFFFFFFF or 0x00000000). This method does not contain branching instructions.
public static unsafe long ToBitwiseMask64(this bool flag)\n
Converts the given bool
value to a long
mask with all bits representing the value of the input flag (either all 1s or 0s). This method does not contain branching instructions.
bool flag = true;\nbyte result = flag.ToByte();\n
"},{"location":"Extensions/BoolExtensions/#convert-bool-to-int-mask","title":"Convert Bool to Int Mask","text":"bool flag = true;\nint mask = flag.ToBitwiseMask32();\n
"},{"location":"Extensions/BoolExtensions/#convert-bool-to-long-mask","title":"Convert Bool to Long Mask","text":"bool flag = true;\nlong mask = flag.ToBitwiseMask64();\n
"},{"location":"Extensions/EndianExtensions/","title":"EndianExtensions","text":"Provides extension methods for converting between endianness.
EndianExtensions
is a static class that offers methods to convert primitive data types and any structure implementing the ICanReverseEndian
interface to big or little endian format.
The conversions check the system's endianness and only perform byte-swapping if necessary, making the operation efficient by avoiding redundant processing on systems with matching endianness.
The JIT will eliminate no-operations here, so e.g. calling AsLittleEndian
on a Little Endian machine has 0 overhead.
public static byte AsLittleEndian(this byte value)\npublic static sbyte AsLittleEndian(this sbyte value)\npublic static short AsLittleEndian(this short value)\npublic static ushort AsLittleEndian(this ushort value)\npublic static int AsLittleEndian(this int value)\npublic static uint AsLittleEndian(this uint value)\npublic static long AsLittleEndian(this long value)\npublic static ulong AsLittleEndian(this ulong value)\npublic static float AsLittleEndian(this float value)\npublic static double AsLittleEndian(this double value)\npublic static T AsLittleEndian<T>(this T value) where T : struct, ICanReverseEndian\n
Converts the given value to little endian format. If the system is already little endian, no conversion is performed.
"},{"location":"Extensions/EndianExtensions/#parameters","title":"Parameters","text":"value
: The value to convert to little endian.The value in little endian format.
"},{"location":"Extensions/EndianExtensions/#asbigendian-overloads-for-various-types","title":"AsBigEndian (Overloads for various types)","text":"public static byte AsBigEndian(this byte value)\npublic static sbyte AsBigEndian(this sbyte value)\npublic static short AsBigEndian(this short value)\npublic static ushort AsBigEndian(this ushort value)\npublic static int AsBigEndian(this int value)\npublic static uint AsBigEndian(this uint value)\npublic static long AsBigEndian(this long value)\npublic static ulong AsBigEndian(this ulong value)\npublic static float AsBigEndian(this float value)\npublic static double AsBigEndian(this double value)\npublic static T AsBigEndian<T>(this T value) where T : struct, ICanReverseEndian\n
Converts the given value to big endian format. If the system is already big endian, no conversion is performed.
"},{"location":"Extensions/EndianExtensions/#parameters_1","title":"Parameters","text":"value
: The value to convert to big endian.The value in big endian format.
"},{"location":"Extensions/EndianExtensions/#usage","title":"Usage","text":""},{"location":"Extensions/EndianExtensions/#convert-an-integer-to-little-endian-format","title":"Convert an Integer to Little Endian Format","text":"int myValue = 12345678;\nint littleEndianValue = myValue.AsLittleEndian();\n
"},{"location":"Extensions/EndianExtensions/#convert-a-double-to-big-endian-format","title":"Convert a Double to Big Endian Format","text":"double myValue = 123.456;\ndouble bigEndianValue = myValue.AsBigEndian();\n
"},{"location":"Extensions/EndianExtensions/#convert-a-custom-structure-to-big-endian-format","title":"Convert a Custom Structure to Big Endian Format","text":"For structs which implement ICanReverseEndian
var myStruct = new MyStruct { /* ... */ };\nvar asBig = myStruct.AsBigEndian();\n
"},{"location":"Extensions/EnumExtensions/","title":"EnumExtensions","text":"Provides high-performance extension methods for working with enums.
EnumExtensions is a utility class that , including checking for the presence of a specified flag in an enum value using unsafe code for faster execution.
"},{"location":"Extensions/EnumExtensions/#methods","title":"Methods","text":""},{"location":"Extensions/EnumExtensions/#hasflagfast","title":"HasFlagFast","text":"public static bool HasFlagFast<T>(this T value, T flag) where T : unmanaged, Enum\n
Determines if the given enum has a specified flag. This method uses unsafe code for faster execution and skips type check.
"},{"location":"Extensions/EnumExtensions/#parameters","title":"Parameters","text":"value
: The value to check.flag
: The flag to check.true
if the enum is contained in the value, false
otherwise.
T
: The type to check the flag of.NotSupportedException
: This type of enum is not supported.[Flags]\nenum MyEnum : int\n{\nNone = 0,\nFlag1 = 1,\nFlag2 = 2,\nFlag3 = 4,\n}\n\nMyEnum value = MyEnum.Flag1 | MyEnum.Flag3;\nbool hasFlag1 = value.HasFlagFast(MyEnum.Flag1); // True\nbool hasFlag2 = value.HasFlagFast(MyEnum.Flag2); // False\nbool hasFlag3 = value.HasFlagFast(MyEnum.Flag3); // True\n
"},{"location":"Extensions/SpanExtensions/","title":"SpanExtensions","text":"Partially forked from Community Toolkit.
Utility class providing high performance extension methods for working with spans.
SpanExtensions is a utility class that provides extension methods for spans, including methods for casting, slicing, replacing elements, and finding offsets.
"},{"location":"Extensions/SpanExtensions/#methods","title":"Methods","text":""},{"location":"Extensions/SpanExtensions/#castfast","title":"CastFast","text":"Not accelerated on .NET Framework and Standard 2.0
public static Span<TTo> CastFast<TFrom, TTo>(this Span<TFrom> data) where TFrom : struct where TTo : struct\n
Casts a Span<TFrom>
to a Span<TTo>
without copying the underlying data."},{"location":"Extensions/SpanExtensions/#dangerousgetreference","title":"DangerousGetReference","text":"public static ref T DangerousGetReference<T>(this Span<T> span)\n
Returns a reference to the first element within a given Span<T>
, with no bounds checks. "},{"location":"Extensions/SpanExtensions/#dangerousgetreferenceat","title":"DangerousGetReferenceAt","text":"public static ref T DangerousGetReferenceAt<T>(this Span<T> span, int i)\n
Returns a reference to an element at a specified index within a given Span<T>
, with no bounds checks. The caller is responsible for ensuring the i
parameter is valid. "},{"location":"Extensions/SpanExtensions/#asbytes","title":"AsBytes","text":"public static Span<byte> AsBytes<T>(this Span<T> span) where T : unmanaged\n
Converts a Span<T>
to a Span<byte>
without copying the underlying data."},{"location":"Extensions/SpanExtensions/#cast","title":"Cast","text":"public static Span<TTo> Cast<TFrom, TTo>(this Span<TFrom> span) where TFrom : unmanaged where TTo : unmanaged\n
Casts a Span<TFrom>
to a Span<TTo>
without copying the underlying data, when both types are unmanaged."},{"location":"Extensions/SpanExtensions/#indexof","title":"IndexOf","text":"public static unsafe int IndexOf<T>(this Span<T> span, ref T value)\n
Gets the index of an element within given Span<T>
based on a reference to an element inside the Span<T>
.
public static int Count<T>(this Span<T> span, T value) where T : IEquatable<T>\n
Counts the number of occurrences of a given value in a target Span<T>
instance."},{"location":"Extensions/SpanExtensions/#slicefast","title":"SliceFast","text":"Not accelerated on .NET Framework and Standard 2.0
public static Span<T> SliceFast<T>(this Span<T> data, int start, int length)\npublic static ReadOnlySpan<T> SliceFast<T>(this ReadOnlySpan<T> data, int start, int length)\npublic static ReadOnlySpan<T> SliceFast<T>(this ReadOnlySpan<T> data, Range range)\npublic static Span<T> SliceFast<T>(this Span<T> data, Range range)\npublic static Span<T> SliceFast<T>(this Span<T> data, int start)\npublic static ReadOnlySpan<T> SliceFast<T>(this ReadOnlySpan<T> data, int start)\n
Performs a slice operation on a Span<T>
or ReadOnlySpan<T>
without performing bounds checks. This supports ranges, so span.SliceFast(1..3)
is valid. "},{"location":"Extensions/SpanExtensions/#replace","title":"Replace","text":"public static Span<char> Replace(this Span<char> data, char oldValue, char newValue, Span<char> buffer)\npublic static unsafe Span<T> Replace<T>(this Span<T> data, T oldValue, T newValue, Span<T> buffer) where T : unmanaged, IEquatable<T>\n
Replaces all occurrences of a specified value with another value in a given Span<T>
."},{"location":"Extensions/SpanExtensions/#findalloffsetsofbyte","title":"FindAllOffsetsOfByte","text":"Missing SIMD path for non-x86/x64 platforms. PRs are welcome.
public static List<int> FindAllOffsetsOfByte(this ReadOnlySpan<byte> data, byte value)\npublic static List<int> FindAllOffsetsOfByte(this ReadOnlySpan<byte> data, byte value, int offsetCountHint)\n
Finds all the offsets of a given byte value in a target ReadOnlySpan<byte>
instance. The second overload allows specifying an optional offsetCountHint
parameter to preallocate the list capacity. "},{"location":"Extensions/SpanExtensions/#usage","title":"Usage","text":""},{"location":"Extensions/SpanExtensions/#get-reference-without-bounds-checks","title":"Get Reference without Bounds Checks","text":"Span<int> span = new int[] { 1, 2, 3 };\nref int firstElement = ref span.DangerousGetReference();\n
"},{"location":"Extensions/SpanExtensions/#get-reference-at-specific-index-without-bounds-checks","title":"Get Reference at Specific Index without Bounds Checks","text":"Span<int> span = new int[] { 1, 2, 3 };\nref int elementAtTwo = ref span.DangerousGetReferenceAt(2);\n
"},{"location":"Extensions/SpanExtensions/#convert-span-to-byte-span","title":"Convert Span to Byte Span","text":"Span<int> intSpan = new int[] { 1, 2, 3 };\nSpan<byte> byteSpan = intSpan.AsBytes();\n
"},{"location":"Extensions/SpanExtensions/#cast-span","title":"Cast Span","text":"Span<byte> byteSpan = new byte[] { 1, 2, 3, 4 };\nSpan<int> intSpan = byteSpan.Cast<byte, int>();\n
"},{"location":"Extensions/SpanExtensions/#find-index-of-an-element","title":"Find Index of an Element","text":"Span<int> span = new int[] { 1, 2, 3, 4, 5 };\nref int value = ref span[3];\nint index = span.IndexOf(ref value); // index = 3\n
"},{"location":"Extensions/SpanExtensions/#count-occurrences-of-a-value","title":"Count Occurrences of a Value","text":"Span<int> span = new int[] { 1, 2, 2, 3, 2 };\nint count = span.Count(2); // 3\n
"},{"location":"Extensions/SpanExtensions/#slice-span-without-bounds-checks","title":"Slice Span Without Bounds Checks","text":"Span<int> span = new int[] { 1, 2, 3, 4, 5 };\nSpan<int> slicedSpan = span.SliceFast(1, 3);\n
"},{"location":"Extensions/SpanExtensions/#replace-elements-in-a-span","title":"Replace Elements in a Span","text":"Span<char> span = \"hello world\".ToCharArray();\nSpan<char> buffer = new char[span.Length];\nSpan<char> replacedSpan = span.Replace('l', 'x', buffer);\n
"},{"location":"Extensions/SpanExtensions/#find-all-offsets-of-a-byte","title":"Find All Offsets of a Byte","text":"byte[] data = new byte[] { 1, 2, 3, 2, 4, 2 };\nReadOnlySpan<byte> span = data;\nList<int> offsets = span.FindAllOffsetsOfByte(2);\n
"},{"location":"Extensions/SpanExtensions/#find-all-offsets-of-a-byte-with-offset-count-hint","title":"Find All Offsets of a Byte with Offset Count Hint","text":"byte[] data = new byte[] { 1, 2, 3, 2, 4, 2 };\nReadOnlySpan<byte> span = data;\nList<int> offsets = span.FindAllOffsetsOfByte(2, 3);\n
"},{"location":"Extensions/StreamExtensions/","title":"StreamExtensions","text":"Utility class providing high performance extension methods for working with Stream
objects.
StreamExtensions is a utility class that provides extension methods for the Stream
type, including padding, writing, and reading unmanaged and marshalled structures.
public static void AddPadding<TStream>(this TStream stream, int alignment)\n
Pads the stream with any/random bytes until it is aligned to the specified alignment. The padding bytes depend on the implementation of Stream.SetLength
."},{"location":"Extensions/StreamExtensions/#addpadding_1","title":"AddPadding","text":"public static void AddPadding<TStream>(this TStream stream, byte value, int alignment = 2048)\n
Pads the stream with the specified value
bytes until it is aligned to the specified alignment."},{"location":"Extensions/StreamExtensions/#write","title":"Write","text":"public static void Write<TStream, T>(this TStream stream, Span<T> structure) where T : unmanaged\n
Appends an unmanaged structure onto the given stream and advances the position."},{"location":"Extensions/StreamExtensions/#write_1","title":"Write","text":"public static void Write<TStream, T>(this TStream stream, in T structure) where T : unmanaged\n
Appends an unmanaged structure onto the given stream and advances the position."},{"location":"Extensions/StreamExtensions/#writemarshalled","title":"WriteMarshalled","text":"public static void WriteMarshalled<TStream, T>(this TStream stream, T item)\n
Appends a managed/marshalled structure onto the given stream and advances the position."},{"location":"Extensions/StreamExtensions/#writemarshalled_1","title":"WriteMarshalled","text":"public static void WriteMarshalled<TStream, T>(this TStream stream, T[] item)\n
Appends an array of managed/marshalled structures onto the given stream and advances the position."},{"location":"Extensions/StreamExtensions/#writemarshalled_2","title":"WriteMarshalled","text":"public static void WriteMarshalled<TStream, T>(this TStream stream, Span<T> item)\n
Appends a span of managed/marshalled structures onto the given stream and advances the position."},{"location":"Extensions/StreamExtensions/#read","title":"Read","text":"public static void Read<TStream, T>(this TStream stream, out T result) where T : unmanaged\n
Reads a single unmanaged structure of type T from the stream."},{"location":"Extensions/StreamExtensions/#read_1","title":"Read","text":"public static void Read<TStream, T>(this TStream stream, T[] output) where T : unmanaged\n
Reads a span of unmanaged structures of type T from the stream into the provided output array."},{"location":"Extensions/StreamExtensions/#read_2","title":"Read","text":"public static void Read<TStream, T>(this TStream stream, Span<T> output) where T : unmanaged\n
Reads a span of unmanaged structures of type T from the stream into the provided output span."},{"location":"Extensions/StreamExtensions/#readmarshalled","title":"ReadMarshalled","text":"public static void ReadMarshalled<TStream, T>(this TStream stream, out T result)\n
Reads a single marshalled structure of type T from the stream."},{"location":"Extensions/StreamExtensions/#readmarshalled_1","title":"ReadMarshalled","text":"public static void ReadMarshalled<TStream, T>(this TStream stream, T[] output)\n
Reads a span of marshalled structures of type T from the stream into the provided output array."},{"location":"Extensions/StreamExtensions/#readmarshalled_2","title":"ReadMarshalled","text":"public static void ReadMarshalled<TStream, T>(this TStream stream, Span<T> output)\n
Reads a span of marshalled structures of type T from the stream into the provided output span."},{"location":"Extensions/StreamExtensions/#usage","title":"Usage","text":""},{"location":"Extensions/StreamExtensions/#pad-stream-with-random-bytes","title":"Pad Stream with Random Bytes","text":"using var stream = new MemoryStream();\nstream.AddPadding(4096);\n
"},{"location":"Extensions/StreamExtensions/#pad-stream-with-specific-byte-value","title":"Pad Stream with Specific Byte Value","text":"using var stream = new MemoryStream();\nstream.AddPadding(0xFF, 4096);\n
"},{"location":"Extensions/StreamExtensions/#write-unmanaged-structure","title":"Write Unmanaged Structure","text":"using var stream = new MemoryStream();\nVector2 structure = new Vector2(1, 2);\nstream.Write(structure);\n
"},{"location":"Extensions/StreamExtensions/#write-marshalled-structure","title":"Write Marshalled Structure","text":"using var stream = new MemoryStream();\nMyStruct item = new MyStruct { Value1 = 1, Value2 = \"Hello\" };\nstream.WriteMarshalled(item);\n
"},{"location":"Extensions/StreamExtensions/#read-unmanaged-structure","title":"Read Unmanaged Structure","text":"using var stream = new MemoryStream();\nstream.Read(out Vector2 result);\n
"},{"location":"Extensions/StreamExtensions/#read-marshalled-structure","title":"Read Marshalled Structure","text":"using var stream = new MemoryStream();\nstream.ReadMarshalled(out MyStruct result);\n
"},{"location":"Extensions/StreamExtensions/#read-span-of-unmanaged-structures","title":"Read Span of Unmanaged Structures","text":"using var stream = new MemoryStream();\nVector2[] output = new Vector2[10];\nstream.Read(output);\n
"},{"location":"Extensions/StreamExtensions/#read-span-of-marshalled-structures","title":"Read Span of Marshalled Structures","text":"using var stream = new MemoryStream();\nMyStruct[] output = new MyStruct[10];\nstream.ReadMarshalled(output);\n
"},{"location":"Extensions/StringExtensions/","title":"StringExtensions","text":"Partially forked from Community Toolkit.
Utility class providing high performance extension methods for working with the string
type.
StringExtensions is a utility class that provides extension methods for the string
type, including getting references to string elements, and counting the number of occurrences of a character in a string.
public static ref char DangerousGetReference(this string text)\n
Returns a reference to the first element within a given string
, with no bounds checks. It is the caller's responsibility to perform bounds checks when dereferencing the returned value.
public static ref char DangerousGetReferenceAt(this string text, int i)\n
Returns a reference to an element at a specified index within a given string
, with no bounds checks. It is the caller's responsibility to ensure the i
parameter is valid.
public static int Count(this string text, char c)\n
Counts the number of occurrences of a given character in a target string
instance.
SIMD method currently restricted to .NET 7+. PRs for backports are welcome.
Will produce different hashes depending on runtime or CPU.
Optimised for File Paths specifically
public static nuint GetHashCodeFast(string text)\npublic static unsafe nuint GetHashCodeFast(this ReadOnlySpan<char> text)\n
Faster hashcode for strings; but does not randomize between application runs.
Use this method if and only if 'Denial of Service' attacks are not a concern (i.e. never used for free-form user input), or are otherwise mitigated.
This method does not provide guarantees about producing the same hash across different machines or library versions, or runtime; only for the current process. Instead, it prioritises speed over all.
"},{"location":"Extensions/StringExtensions/#gethashcodelowerfast","title":"GetHashCodeLowerFast","text":"SIMD method currently restricted to .NET 7+. PRs for backports are welcome.
Will produce different hashes depending on runtime or CPU.
Optimised for File Paths specifically
public static nuint GetHashCodeLowerFast(this string text)\npublic static unsafe nuint GetHashCodeLowerFast(this ReadOnlySpan<char> text)\n
Faster hashcode for strings, hashed in lower (invariant) case; does not randomize between application runs.
Use this method if and only if 'Denial of Service' attacks are not a concern (i.e. never used for free-form user input), or are otherwise mitigated.
This method does not provide guarantees about producing the same hash across different machines or library versions, or runtime; only for the current process. Instead, it prioritises speed over all.
"},{"location":"Extensions/StringExtensions/#tolowerinvariantfast","title":"ToLowerInvariantFast","text":"public static string ToLowerInvariantFast(this string text)\npublic static unsafe void ToLowerInvariantFast(this ReadOnlySpan<char> text, Span<char> target)\n
Converts the given string to lower case (invariant casing) using the fastest possible implementation. This method is optimized for performance but currently has limitations for short non-ASCII inputs.
"},{"location":"Extensions/StringExtensions/#toupperinvariantfast","title":"ToUpperInvariantFast","text":"public static string ToUpperInvariantFast(this string text)\npublic static unsafe void ToUpperInvariantFast(this ReadOnlySpan<char> text, Span<char> target)\n
Converts the given string to upper case (invariant casing) using the fastest possible implementation. This method is optimized for performance but currently has limitations for short non-ASCII inputs.
"},{"location":"Extensions/StringExtensions/#usage","title":"Usage","text":""},{"location":"Extensions/StringExtensions/#get-reference-to-first-element-in-string","title":"Get Reference to First Element in String","text":"string text = \"Hello, world!\";\nref char firstCharRef = ref text.DangerousGetReference();\n
"},{"location":"Extensions/StringExtensions/#get-reference-to-element-at-index-in-string","title":"Get Reference to Element at Index in String","text":"string text = \"Hello, world!\";\nint index = 4;\nref char charAtIndexRef = ref text.DangerousGetReferenceAt(index);\n
"},{"location":"Extensions/StringExtensions/#count-character-occurrences-in-string","title":"Count Character Occurrences in String","text":"string text = \"Hello, world!\";\nchar targetChar = 'l';\nint count = text.Count(targetChar);\n
"},{"location":"Extensions/StringExtensions/#get-fast-hash-code","title":"Get Fast Hash Code","text":"string text = \"Hello, world!\";\nnuint fastHashCode = text.GetHashCodeFast();\n
"},{"location":"Extensions/StringExtensions/#get-lower-case-hash-code","title":"Get Lower Case Hash Code","text":"string text = \"Hello, World!\";\nnuint lowerCaseHashCode = text.GetHashCodeLowerFast();\n
"},{"location":"Extensions/StringExtensions/#convert-string-to-lower-case-invariant-fast","title":"Convert String to Lower Case Invariant Fast","text":"string text = \"Hello, WORLD!\";\nstring lowerInvariant = text.ToLowerInvariantFast(); // hello, world!\n
"},{"location":"Extensions/StringExtensions/#convert-string-to-upper-case-invariant-fast","title":"Convert String to Upper Case Invariant Fast","text":"string text = \"hello, world!\";\nstring upperInvariant = text.ToUpperInvariantFast(); // HELLO, WORLD!\n
"},{"location":"Extensions/StringExtensions/#convert-readonlyspan-to-lower-case-invariant-fast","title":"Convert ReadOnlySpan to Lower Case Invariant Fast","text":"string text = \"Hello, WORLD!\";\nSpan<char> target = stackalloc char[textSpan.Length]; // Careful with string length!\ntext.AsSpan().ToLowerInvariantFast(target); // hello, world! (on stack)\n
"},{"location":"Extensions/StringExtensions/#convert-readonlyspan-to-upper-case-invariant-fast","title":"Convert ReadOnlySpan to Upper Case Invariant Fast","text":"string text = \"hello, world!\";\nSpan<char> target = stackalloc char[textSpan.Length]; // Careful with string length!\ntext.AsSpan().ToLowerInvariantFast(target); // HELLO, WORLD! (on stack)\n
"},{"location":"Extensions/VectorExtensions/","title":"VectorExtensions","text":"Utility class providing high performance extension methods for working with the Vector
struct.
VectorExtensions is a utility class that provides extension methods for the Vector
struct.
internal static Vector<T> LoadUnsafe<T>(ref T source, nuint elementOffset) where T : struct\n
Loads an element at a specified offset into a vector. Unsafe operations are used for performance reasons.
"},{"location":"Extensions/VectorExtensions/#storeunsafe","title":"StoreUnsafe","text":"internal static void StoreUnsafe<T>(this Vector<T> source, ref T destination, nuint elementOffset) where T : struct\n
Stores an element from a vector into the destination + offset. Unsafe operations are used for performance reasons.
"},{"location":"Extensions/VectorExtensions/#usage","title":"Usage","text":""},{"location":"Extensions/VectorExtensions/#load-element-into-a-vector-at-an-offset","title":"Load Element into a Vector at an Offset","text":"int[] array = new int[] { 1, 2, 3, 4, 5 };\nref int source = ref array[0];\nnuint elementOffset = 2;\nVector<int> vector = VectorExtensions.LoadUnsafe(ref source, elementOffset);\n
"},{"location":"Extensions/VectorExtensions/#store-element-from-a-vector-to-destination-offset","title":"Store Element from a Vector to Destination + Offset","text":"Vector<int> vector = new Vector<int>(new int[] { 1, 2, 3, 4 });\nint[] destinationArray = new int[vector.Count];\nref int destination = ref destinationArray[0];\nnuint elementOffset = 1;\nvector.StoreUnsafe(ref destination, elementOffset);\n
"},{"location":"Pointers/FixedArrayPtr/","title":"FixedArrayPtr<T> and MarshalledFixedArrayPtr<T>","text":"Abstraction for a pointer to a value of type T
with a known length.
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 manage a collection of items in unmanaged, static memory; for example, find an item.
"},{"location":"Pointers/FixedArrayPtr/#properties","title":"Properties","text":"Pointer
: A Ptr representing the memory address of the first element in the fixed-size array. Count
: The number of elements in the fixed-size array. ArraySize
: The total size in bytes of the fixed-size array. var ptr = new FixedArrayPtr<int>(address, count);\n
Address
: The memory address of the first element in the fixed-size array.Count
: The number of elements in the fixed-size array.The following methods should be used with in memory/RAM addresses. See Memory Read/Write for APIs that work with ICanReadWriteMemory
implementations.
ref T AsRef(int index);\nT Get(int index);\nvoid Get(int index, out T value);\nvoid Set(int index, in T value);\n
These methods provide access to individual elements in the fixed-size array. The AsRef()
method returns a reference to an element, allowing for direct modification of the element's value. The Get()
and Set()
methods retrieve and update the values of elements at specified indices.
T Get<TSource>(TSource source, int index);\nvoid Get<TSource>(TSource source, int index, out T value);\nvoid Set<TSource>(TSource source, int index, in T value);\n
These methods provide access to individual elements in the fixed-size array, allowing read and write operations to be performed on an implementation of ICanReadWriteMemory
. This can be useful when working with external processes or other memory sources."},{"location":"Pointers/FixedArrayPtr/#copying","title":"Copying","text":"void CopyFrom(Span<T> sourceArray, int length);\nvoid CopyFrom(Span<T> sourceArray, int length, int sourceIndex, int destinationIndex);\nvoid CopyTo(Span<T> destinationArray, int length);\nvoid CopyTo(Span<T> destinationArray, int length, int sourceIndex, int destinationIndex);\n
These methods provide copying functionality between the fixed-size array and managed arrays. CopyFrom()
copies elements from a managed array to the fixed-size array, and CopyTo()
copies elements from the fixed-size array to a managed array.
Both methods support copying a specified number of elements, as well as specifying source and destination indices for the copy operation.
"},{"location":"Pointers/FixedArrayPtr/#search","title":"Search","text":"bool Contains(in T item);\nbool Contains<TSource>(TSource source, in T item);\nint IndexOf(in T item);\nint IndexOf<TSource>(TSource source, in T item);\n
These methods provide search functionality for the fixed-size array, allowing you to check for the existence of an element or find its index. The Contains()
methods return true if the specified item is found in the array, and the IndexOf()
methods return the index of the first occurrence of the specified item or -1 if the item is not found.
This is a tuple of FixedArrayPtr<T>
and TSource
.
API surface is the same, all methods are delegated to underlying FixedArrayPtr<T>
.
Notably however, this API enables the use of LINQ; i.e. the following are now valid:
foreach (int value in sourcedFixedArrayPtr)\n{\n// ...\n}\n
sourcedFixedArrayPtr.Max(x => x);\n
etc.
"},{"location":"Pointers/Ptr/","title":"Ptr<T> and MarshalledPtr<T>","text":"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:
ICanReadWriteMemory
. (e.g. Memory of External Process). 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.
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.
The Ptr<T>
type supports implicit conversions to/from raw pointers.
int someValue = 42;\nPtr<int> ptr = &someValue;\nint* ptr2 = intPtr;\n
"},{"location":"Pointers/Ptr/#asref","title":"AsRef","text":"Pointers can be converted for references.
ref int valueFromPtr = ref intPtr.AsRef();\nConsole.WriteLine($\"Value from pointer: {valueFromPtr}\");\n
This can be especially useful for game modding APIs, the end user can for example do this:
ref var playerCount = ref State.NumberOfPlayers.AsRef();\nplayerCount = 5;\n
Completely avoiding pointer arithmetic; which is friendly to non-programmers.
"},{"location":"Pointers/Ptr/#pointer-arithmetic","title":"Pointer Arithmetic","text":"You can do regular arithmetic on pointers, e.g. add 1 to go to next value.
Ptr<int> arrayIntPtr = arrayPtr;\nPtr<int> offsetArrayIntPtr = arrayIntPtr + 2;\n\nConsole.WriteLine($\"Value at original pointer: {arrayIntPtr.AsRef()}\"); // Output: Value at original pointer: 1\nConsole.WriteLine($\"Value at offset pointer: {offsetArrayIntPtr.AsRef()}\"); // Output: Value at offset pointer: 3\nConsole.WriteLine($\"Equal? {offsetArrayIntPtr != arrayIntPtr}\"); // Equal? false\n\n// You can also do ++ and --\narrayIntPtr++;\narrayIntPtr++;\n\n// Now arrayIntPtr points to the third element in the array.\n
"},{"location":"Pointers/Ptr/#value-read-write","title":"Value Read / Write","text":"You can read/write values with an implementation of ICanReadWriteMemory
Reading from RAM:
int valueFromSource = pointer.Get(); // implicit Memory.Instance\nConsole.WriteLine($\"Value from RAM: {valueFromSource}\");\n
Reading from RAM of another process:
// externalMemory = new ExternalMemory(anotherProcess);\nint valueFromSource = pointer.Get(externalMemory);\nConsole.WriteLine($\"Value from another process' RAM: {valueFromSource}\");\n
You can also read/write to offsets:
int valueAtOffset2 = pointer.Get(2);\nConsole.WriteLine($\"Value from RAM (Offset 2): {valueAtOffset2}\");\n
"},{"location":"Pointers/Ptr/#branch-on-null-pointer","title":"Branch on Null Pointer","text":"Just like in C, you can branch into an if statement if a pointer isn't null.
var notNullPointer = new Ptr<int>((int*)0x12345678);\nif (notNullPointer)\nConsole.WriteLine(\"Pointer is not null!\");\n
"},{"location":"Pointers/Ptr/#from-external-libraries","title":"From External Libraries","text":"Getting a ref to native memory/pointer:
// Defined in a library for modding a certain hoverboard racing game.\npublic static readonly Ptr<int> NumberOfRacers = new Ptr<int>((int*)0x64B758);\n\n// End user can do either\nint* racers = NumberOfRacers;\n\n// or avoid pointer arithmetic entirely.\nref int racers = ref NumberOfRacers.AsRef();\n
Hooking a function with Reloaded.Hooks:
// Function pointer declatation (can also use delegate).\n[Function(CallingConventions.MicrosoftThiscall)]\npublic struct OpenFileFnPtr { public FuncPtr<Ptr<byte>, Ptr<byte>, int, Ptr<byte>> Value; }\n\n_openFile = FileSystemFuncs.OpenFile.HookAs<FileSystemFuncs.OpenFileFnPtr>(typeof(FileAccessServer), nameof(OpenBfsFileImpl)).Activate();\n
"},{"location":"Pointers/Ptr/#sourcedptrt","title":"SourcedPtr<T>","text":"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.
"},{"location":"Reloaded/Readme/","title":"Readme","text":"Please visit the documentation site for usage instructions & more.
"},{"location":"Reloaded/Pages/","title":"Index","text":"The Reloaded MkDocs Theme A Theme for MkDocs Material. That resembles the look of Reloaded."},{"location":"Reloaded/Pages/#about","title":"About","text":"This it the NexusMods theme for Material-MkDocs, inspired by the look of Reloaded-II.
The overall wiki theme should look fairly close to the actual launcher appearance.
"},{"location":"Reloaded/Pages/#setup-from-scratch","title":"Setup From Scratch","text":"docs/Reloaded
.mkdocs.yml
in your repository root.site_name: Reloaded MkDocs Theme\nsite_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2\n\nrepo_name: Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2\nrepo_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2\n\nextra:\nsocial:\n- icon: fontawesome/brands/github\nlink: https://github.com/Reloaded-Project\n- icon: fontawesome/brands/twitter\nlink: https://twitter.com/thesewer56?lang=en-GB\n\nextra_css:\n- Reloaded/Stylesheets/extra.css\n\nmarkdown_extensions:\n- admonition\n- tables\n- pymdownx.details\n- pymdownx.highlight\n- pymdownx.superfences:\ncustom_fences:\n- name: mermaid\nclass: mermaid\nformat: !!python/name:pymdownx.superfences.fence_code_format\n- pymdownx.tasklist\n- def_list\n- meta\n- md_in_html\n- attr_list\n- footnotes\n- pymdownx.tabbed:\nalternate_style: true\n- pymdownx.emoji:\nemoji_index: !!python/name:materialx.emoji.twemoji\nemoji_generator: !!python/name:materialx.emoji.to_svg\n\ntheme:\nname: material\npalette:\nscheme: reloaded-slate\nfeatures:\n- navigation.instant\n\nplugins:\n- search\n\nnav:\n- Home: index.md\n
.github/workflows/DeployMkDocs.yml
.name: DeployMkDocs\n\n# Controls when the action will run. \non:\n# Triggers the workflow on push on the master branch\npush:\nbranches: [ main ]\n\n# Allows you to run this workflow manually from the Actions tab\nworkflow_dispatch:\n\n# A workflow run is made up of one or more jobs that can run sequentially or in parallel\njobs:\n# This workflow contains a single job called \"build\"\nbuild:\n# The type of runner that the job will run on\nruns-on: ubuntu-latest\n\n# Steps represent a sequence of tasks that will be executed as part of the job\nsteps:\n\n# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it\n- name: Checkout Branch\nuses: actions/checkout@v2\nwith:\nsubmodules: recursive\n\n# Deploy MkDocs\n- name: Deploy MkDocs\n# You may pin to the exact commit or the version.\n# uses: mhausenblas/mkdocs-deploy-gh-pages@66340182cb2a1a63f8a3783e3e2146b7d151a0bb\nuses: mhausenblas/mkdocs-deploy-gh-pages@master\nenv:\nGITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\nREQUIREMENTS: ./docs/requirements.txt\n
Settings -> Pages
in your repo and select gh-pages
branch to enable GitHub pages. Your page should then be live.
Tip
Refer to Contributing for instructions on how to locally edit and modify the wiki.
Note
For Reloaded3 theme use reloaded3-slate
instead of reloaded-slate
.
Info
Most documentation pages will also include additional plugins; some which are used in the pages here. Here is a sample complete mkdocs.yml you can copy to your project for reference.
"},{"location":"Reloaded/Pages/#technical-questions","title":"Technical Questions","text":"If you have questions/bug reports/etc. feel free to Open an Issue.
Happy Documenting \u2764\ufe0f
"},{"location":"Reloaded/Pages/contributing/","title":"Contributing to the Wiki: Locally","text":"Info
This page shows you how to contribute to any documentation page or wiki based on this template.
Note
This theme is forked from my theme for Nexus Docs; and this page is synced with that.
"},{"location":"Reloaded/Pages/contributing/#tutorial","title":"Tutorial","text":"Note
If you are editing the repository with the theme itself on Windows, it might be a good idea to run git config core.symlinks true
first to allow git to create symlinks on clone.
You should learn the basics of git
, an easy way is to give GitHub Desktop (Tutorial) a go. It's only 15 minutes \ud83d\ude00.
Fork this repository:
This will create a copy of the repository on your own user account, which you will be able to edit.
Clone this repository.
For example, using GitHub Desktop:
Make changes inside the docs
folder.
Consider using a Markdown Cheat Sheet if you are new to markdown.
I recommend using a markdown editor such as Typora
. Personally I just work from inside Rider
.
Commit the changes and push to GitHub.
Open a Pull Request
.
Opening a Pull Request
will allow us to review your changes before adding them with the main official page. If everything's good, we'll hit the merge button and add your changes to the official repository.
If you are working on the wiki locally, you can generate a live preview the full website. Here's a quick guide of how you could do it from your command prompt
(cmd).
Install Python 3
If you have winget
installed, or Windows 11, you can do this from the command prompt.
winget install Python.Python.3\n
Otherwise download Python 3 from the official website or package manager.
Install Material for MkDocs and Plugins (Python package)
# Restart your command prompt before running this command.\npip install mkdocs-material\npip install mkdocs-redirects\n
Open a command prompt in the folder containing mkdocs.yml
. and run the site locally.
# Move to project folder.\ncd <Replace this with full path to folder containing `mkdocs.yml`>\nmkdocs serve\n
Copy the address to your web browser and enjoy the live preview; any changes you save will be shown instantly.
Most components of the Reloaded are governed by the GPLv3 license.
In some, albeit rare scenarios, certain libraries might be licensed under LGPLv3 instead.
This is a FAQ meant to clarify the licensing choice and its implications. Please note, though, that the full license text is the final legal authority.
"},{"location":"Reloaded/Pages/license/#why-was-gpl-v3-chosen","title":"Why was GPL v3 chosen?","text":"The primary objective is to prevent closed-source, commercial exploitation of the project.
We want to ensure that the project isn't used within a proprietary environment for profit-making purposes such as:
The Reloaded Project is a labour of love from unpaid hobbyist volunteers.
Exploiting that work for profit feels fundamentally unfair.
While the GPLv3 license doesn't prohibit commercial use outright, it does prevent commercial exploitation by requiring that contributions are given back to the open-source community.
In that fashion, everyone can benefit from the projects under the Reloaded label.
"},{"location":"Reloaded/Pages/license/#can-i-use-reloaded-libraries-commercially","title":"Can I use Reloaded Libraries Commercially?","text":"You can as long as the resulting produce is also licensed under GPLv3, and thus open source.
"},{"location":"Reloaded/Pages/license/#can-i-use-reloaded-libraries-in-a-closed-source-application","title":"Can I use Reloaded Libraries in a closed-source application?","text":"The license terms do not permit this.
However, if your software is completely non-commercial, meaning it's neither sold for profit, funded in development, nor hidden behind a paywall (like Patreon), we probably just look the other way.
This often applies to non-professional programmers, learners, or those with no intent to exploit the project. We believe in understanding and leniency for those who might not know better.
GPL v3 exists to protect the project and its contributors. If you're not exploiting the project for commercial gain, you're not hurting us; and we will not enforce the terms of the GPL.
If you are interested in obtaining a commercial license, or want an explicit written exemption, please get in touch with the repository owners.
"},{"location":"Reloaded/Pages/license/#can-i-link-reloaded-libraries-staticallydynamically","title":"Can I link Reloaded Libraries statically/dynamically?","text":"Yes, as long as you adhere to the GPLv3 license terms, you're permitted to statically link Reloaded Libraries into your project, for instance, through the use of NativeAOT or ILMerge.
"},{"location":"Reloaded/Pages/license/#guidelines-for-non-commercial-use","title":"Guidelines for Non-Commercial Use","text":"We support and encourage the non-commercial use of Reloaded Libraries. Non-commercial use generally refers to the usage of our libraries for personal projects, educational purposes, academic research, or use by non-profit organizations.
"},{"location":"Reloaded/Pages/license/#personal-projects","title":"Personal Projects","text":"You're free to use our libraries for projects that you undertake for your own learning, hobby or personal enjoyment. This includes creating mods for your favorite games or building your own applications for personal use.
"},{"location":"Reloaded/Pages/license/#educational-use","title":"Educational Use","text":"Teachers and students are welcome to use our libraries as a learning resource. You can incorporate them into your teaching materials, student projects, coding bootcamps, workshops, etc.
"},{"location":"Reloaded/Pages/license/#academic-research","title":"Academic Research","text":"Researchers may use our libraries for academic and scholarly research. We'd appreciate if you cite our work in any publications that result from research involving our libraries.
"},{"location":"Reloaded/Pages/license/#non-profit-organizations","title":"Non-profit Organizations","text":"If you're part of a registered non-profit organization, you can use our libraries in your projects. However, any derivative work that uses our libraries must also be released under the GPL.
Please remember, if your usage of our libraries evolves from non-commercial to commercial, you must ensure compliance with the terms of the GPL v3 license.
"},{"location":"Reloaded/Pages/license/#attribution-requirements","title":"Attribution Requirements","text":"As Reloaded Project is a labor of love, done purely out of passion and with an aim to contribute to the broader community, we highly appreciate your support in providing attribution when using our libraries.
While not legally mandatory under GPL v3, it is a simple act that can go a long way in recognizing the efforts of our contributors and fostering an open and collaborative atmosphere.
If you choose to provide attribution (and we hope you do!), here are some guidelines:
Acknowledge the Use of Reloaded Libraries: Mention that your project uses or is based on Reloaded libraries. This could be in your project's readme, a credits page on a website, a manual, or within the software itself.
Link to the Project: If possible, provide a link back to the Reloaded Project. This allows others to explore and potentially benefit from our work.
Remember, attribution is more than just giving credit,,, it's a way of saying thank you \ud83d\udc49\ud83d\udc48, fostering reciprocal respect, and acknowledging the power of collaborative open-source development.
We appreciate your support and look forward to seeing what amazing projects you create using Reloaded libraries!
"},{"location":"Reloaded/Pages/license/#code-from-mitbsd-licensed-projects","title":"Code from MIT/BSD Licensed Projects","text":"In some rare instances, code from more permissively licensed projects, such as those under the MIT
or BSD
licenses, may be referenced, incorporated, or slightly modified within the Reloaded Project.
It's important to us to respect the terms and intentions of these permissive licenses, which often allow their code to be used in a wide variety of contexts, including in GPL-licensed projects like ours.
In these cases, the Reloaded Project is committed to clearly disclosing the usage of such code:
Method-Level Disclosure: For individual methods or small code snippets, we use appropriate attribution methods, like programming language attributes. For example, methods borrowed or adapted from MIT-licensed projects might be marked with a [MITLicense]
attribute.
File-Level Disclosure: For larger amounts of code, such as entire files or modules, we'll include the original license text at the top of the file and clearly indicate which portions of the code originate from a differently-licensed project.
Project-Level Disclosure: If an entire library or significant portion of a project under a more permissive license is used, we will include an acknowledgment in a prominent location, such as the readme file or the project's license documentation.
This approach ensures we honor the contributions of the open source community at large, respect the original licenses, and maintain transparency with our users about where code originates from.
Any files/methods or snippets marked with those attributes may be consumed using their original license terms.
i.e. If a method is marked with [MITLicense]
, you may use it under the terms of the MIT license.
We welcome and appreciate contributions to the Reloaded Project! By contributing, you agree to share your changes under the same GPLv3 license, helping to make the project better for everyone.
"},{"location":"Reloaded/Pages/testing-zone/","title":"Testing Zone","text":"Info
This is a dummy page with various Material MkDocs controls and features scattered throughout for testing.
"},{"location":"Reloaded/Pages/testing-zone/#custom-admonitions","title":"Custom Admonitions","text":"Reloaded Admonition
An admonition featuring a Reloaded logo. My source is in Stylesheets/extra.css as Custom 'reloaded' admonition
.
Heart Admonition
An admonition featuring a heart; because we want to contribute back to the open source community. My source is in Stylesheets/extra.css as Custom 'reloaded heart' admonition
.
Nexus Admonition
An admonition featuring a Nexus logo. My source is in Stylesheets/extra.css as Custom 'nexus' admonition
.
Heart Admonition
An admonition featuring a heart; because we want to contribute back to the open source community. My source is in Stylesheets/extra.css as Custom 'nexus heart' admonition
.
Flowchart (Source: Nexus Archive Library):
flowchart TD\n subgraph Block 2\n BigFile1.bin\n end\n\n subgraph Block 1\n BigFile0.bin\n end\n\n subgraph Block 0\n ModConfig.json -.-> Updates.json \n Updates.json -.-> more[\"... more .json files\"] \n end
Sequence Diagram (Source: Reloaded3 Specification):
sequenceDiagram\n\n % Define Items\n participant Mod Loader\n participant Virtual FileSystem (VFS)\n participant CRI CPK Archive Support\n participant Persona 5 Royal Support\n participant Joker Costume\n\n % Define Actions\n Mod Loader->>Persona 5 Royal Support: Load Mod\n Persona 5 Royal Support->>Mod Loader: Request CRI CPK Archive Support API\n Mod Loader->>Persona 5 Royal Support: Receive CRI CPK Archive Support Instance\n\n Mod Loader->>Joker Costume: Load Mod\n Mod Loader-->Persona 5 Royal Support: Notification: 'Loaded Joker Costume'\n Persona 5 Royal Support->>CRI CPK Archive Support: Add Files from 'Joker Costume' to CPK Archive (via API)
State Diagram (Source: Mermaid Docs):
stateDiagram-v2\n [*] --> Still\n Still --> [*]\n\n Still --> Moving\n Moving --> Still\n Moving --> Crash\n Crash --> [*]
Class Diagram (Arbitrary)
classDiagram\n class Animal\n `NexusMobile\u2122` <|-- Car
Note
At time of writing, version of Mermaid is a bit outdated here; and other diagrams might not render correctly (even on unmodified theme); thus certain diagrams have been omitted from here.
"},{"location":"Reloaded/Pages/testing-zone/#code-block","title":"Code Block","text":"Snippet from C# version of Sewer's Virtual FileSystem (VFS):
/// <summary>\n/// Tries to get files for a specific folder, assuming the input path is already in upper case.\n/// </summary>\n/// <param name=\"folderPath\">The folder to find. Already lowercase.</param>\n/// <param name=\"value\">The returned folder instance.</param>\n/// <returns>True if found, else false.</returns>\n[MethodImpl(MethodImplOptions.AggressiveInlining)]\npublic bool TryGetFolderUpper(ReadOnlySpan<char> folderPath, out SpanOfCharDict<TTarget> value)\n{\n// Must be O(1)\nvalue = default!; // Compare equality.\n// Note to devs: Do not invert branches, we optimise for hot paths here.\nif (folderPath.StartsWith(Prefix))\n{\n// Check for subfolder in branchless way.\n// In CLR, bool is length 1, so conversion to byte should be safe.\n// Even suppose it is not; as long as code is little endian; truncating int/4 bytes to byte still results \n// in correct answer.\nvar hasSubfolder = Prefix.Length != folderPath.Length;\nvar hasSubfolderByte = Unsafe.As<bool, byte>(ref hasSubfolder);\nvar nextFolder = folderPath.SliceFast(Prefix.Length + hasSubfolderByte);\n\nreturn SubfolderToFiles.TryGetValue(nextFolder, out value!);\n}\n\nreturn false;\n}\n
Something more number heavy, Fast Inverse Square Root from Quake III Arena (unmodified).
float Q_rsqrt( float number )\n{\nlong i;\nfloat x2, y;\nconst float threehalfs = 1.5F;\n\nx2 = number * 0.5F;\ny = number;\ni = * ( long * ) &y; // evil floating point bit level hacking\ni = 0x5f3759df - ( i >> 1 ); // what the fuck? \ny = * ( float * ) &i;\ny = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration\n// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed\n\nreturn y;\n}\n
"},{"location":"Reloaded/Pages/testing-zone/#default-admonitions","title":"Default Admonitions","text":"Note
Test
Abstract
Test
Info
Test
Tip
Test
Success
Test
Question
Test
Warning
Test
Failure
Test
Danger
Test
Bug
Test
Example
Test
Quote
Test
"},{"location":"Reloaded/Pages/testing-zone/#tables","title":"Tables","text":"Method DescriptionGET
Fetch resource PUT
Update resource DELETE
Delete resource"},{"location":"Reloaded/docs/Pages/","title":"Index","text":"The Reloaded MkDocs Theme A Theme for MkDocs Material. That resembles the look of Reloaded."},{"location":"Reloaded/docs/Pages/#about","title":"About","text":"This it the NexusMods theme for Material-MkDocs, inspired by the look of Reloaded-II.
The overall wiki theme should look fairly close to the actual launcher appearance.
"},{"location":"Reloaded/docs/Pages/#setup-from-scratch","title":"Setup From Scratch","text":"docs/Reloaded
.mkdocs.yml
in your repository root.site_name: Reloaded MkDocs Theme\nsite_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2\n\nrepo_name: Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2\nrepo_url: https://github.com/Reloaded-Project/Reloaded.MkDocsMaterial.Themes.R2\n\nextra:\nsocial:\n- icon: fontawesome/brands/github\nlink: https://github.com/Reloaded-Project\n- icon: fontawesome/brands/twitter\nlink: https://twitter.com/thesewer56?lang=en-GB\n\nextra_css:\n- Reloaded/Stylesheets/extra.css\n\nmarkdown_extensions:\n- admonition\n- tables\n- pymdownx.details\n- pymdownx.highlight\n- pymdownx.superfences:\ncustom_fences:\n- name: mermaid\nclass: mermaid\nformat: !!python/name:pymdownx.superfences.fence_code_format\n- pymdownx.tasklist\n- def_list\n- meta\n- md_in_html\n- attr_list\n- footnotes\n- pymdownx.tabbed:\nalternate_style: true\n- pymdownx.emoji:\nemoji_index: !!python/name:materialx.emoji.twemoji\nemoji_generator: !!python/name:materialx.emoji.to_svg\n\ntheme:\nname: material\npalette:\nscheme: reloaded-slate\nfeatures:\n- navigation.instant\n\nplugins:\n- search\n\nnav:\n- Home: index.md\n
.github/workflows/DeployMkDocs.yml
.name: DeployMkDocs\n\n# Controls when the action will run. \non:\n# Triggers the workflow on push on the master branch\npush:\nbranches: [ main ]\n\n# Allows you to run this workflow manually from the Actions tab\nworkflow_dispatch:\n\n# A workflow run is made up of one or more jobs that can run sequentially or in parallel\njobs:\n# This workflow contains a single job called \"build\"\nbuild:\n# The type of runner that the job will run on\nruns-on: ubuntu-latest\n\n# Steps represent a sequence of tasks that will be executed as part of the job\nsteps:\n\n# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it\n- name: Checkout Branch\nuses: actions/checkout@v2\nwith:\nsubmodules: recursive\n\n# Deploy MkDocs\n- name: Deploy MkDocs\n# You may pin to the exact commit or the version.\n# uses: mhausenblas/mkdocs-deploy-gh-pages@66340182cb2a1a63f8a3783e3e2146b7d151a0bb\nuses: mhausenblas/mkdocs-deploy-gh-pages@master\nenv:\nGITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\nREQUIREMENTS: ./docs/requirements.txt\n
Settings -> Pages
in your repo and select gh-pages
branch to enable GitHub pages. Your page should then be live.
Tip
Refer to Contributing for instructions on how to locally edit and modify the wiki.
Note
For Reloaded3 theme use reloaded3-slate
instead of reloaded-slate
.
Info
Most documentation pages will also include additional plugins; some which are used in the pages here. Here is a sample complete mkdocs.yml you can copy to your project for reference.
"},{"location":"Reloaded/docs/Pages/#technical-questions","title":"Technical Questions","text":"If you have questions/bug reports/etc. feel free to Open an Issue.
Happy Documenting \u2764\ufe0f
"},{"location":"Reloaded/docs/Pages/contributing/","title":"Contributing to the Wiki: Locally","text":"Info
This page shows you how to contribute to any documentation page or wiki based on this template.
Note
This theme is forked from my theme for Nexus Docs; and this page is synced with that.
"},{"location":"Reloaded/docs/Pages/contributing/#tutorial","title":"Tutorial","text":"Note
If you are editing the repository with the theme itself on Windows, it might be a good idea to run git config core.symlinks true
first to allow git to create symlinks on clone.
You should learn the basics of git
, an easy way is to give GitHub Desktop (Tutorial) a go. It's only 15 minutes \ud83d\ude00.
Fork this repository:
This will create a copy of the repository on your own user account, which you will be able to edit.
Clone this repository.
For example, using GitHub Desktop:
Make changes inside the docs
folder.
Consider using a Markdown Cheat Sheet if you are new to markdown.
I recommend using a markdown editor such as Typora
. Personally I just work from inside Rider
.
Commit the changes and push to GitHub.
Open a Pull Request
.
Opening a Pull Request
will allow us to review your changes before adding them with the main official page. If everything's good, we'll hit the merge button and add your changes to the official repository.
If you are working on the wiki locally, you can generate a live preview the full website. Here's a quick guide of how you could do it from your command prompt
(cmd).
Install Python 3
If you have winget
installed, or Windows 11, you can do this from the command prompt.
winget install Python.Python.3\n
Otherwise download Python 3 from the official website or package manager.
Install Material for MkDocs and Plugins (Python package)
# Restart your command prompt before running this command.\npip install mkdocs-material\npip install mkdocs-redirects\n
Open a command prompt in the folder containing mkdocs.yml
. and run the site locally.
# Move to project folder.\ncd <Replace this with full path to folder containing `mkdocs.yml`>\nmkdocs serve\n
Copy the address to your web browser and enjoy the live preview; any changes you save will be shown instantly.
Most components of the Reloaded are governed by the GPLv3 license.
In some, albeit rare scenarios, certain libraries might be licensed under LGPLv3 instead.
This is a FAQ meant to clarify the licensing choice and its implications. Please note, though, that the full license text is the final legal authority.
"},{"location":"Reloaded/docs/Pages/license/#why-was-gpl-v3-chosen","title":"Why was GPL v3 chosen?","text":"The primary objective is to prevent closed-source, commercial exploitation of the project.
We want to ensure that the project isn't used within a proprietary environment for profit-making purposes such as:
The Reloaded Project is a labour of love from unpaid hobbyist volunteers.
Exploiting that work for profit feels fundamentally unfair.
While the GPLv3 license doesn't prohibit commercial use outright, it does prevent commercial exploitation by requiring that contributions are given back to the open-source community.
In that fashion, everyone can benefit from the projects under the Reloaded label.
"},{"location":"Reloaded/docs/Pages/license/#can-i-use-reloaded-libraries-commercially","title":"Can I use Reloaded Libraries Commercially?","text":"You can as long as the resulting produce is also licensed under GPLv3, and thus open source.
"},{"location":"Reloaded/docs/Pages/license/#can-i-use-reloaded-libraries-in-a-closed-source-application","title":"Can I use Reloaded Libraries in a closed-source application?","text":"The license terms do not permit this.
However, if your software is completely non-commercial, meaning it's neither sold for profit, funded in development, nor hidden behind a paywall (like Patreon), we probably just look the other way.
This often applies to non-professional programmers, learners, or those with no intent to exploit the project. We believe in understanding and leniency for those who might not know better.
GPL v3 exists to protect the project and its contributors. If you're not exploiting the project for commercial gain, you're not hurting us; and we will not enforce the terms of the GPL.
If you are interested in obtaining a commercial license, or want an explicit written exemption, please get in touch with the repository owners.
"},{"location":"Reloaded/docs/Pages/license/#can-i-link-reloaded-libraries-staticallydynamically","title":"Can I link Reloaded Libraries statically/dynamically?","text":"Yes, as long as you adhere to the GPLv3 license terms, you're permitted to statically link Reloaded Libraries into your project, for instance, through the use of NativeAOT or ILMerge.
"},{"location":"Reloaded/docs/Pages/license/#guidelines-for-non-commercial-use","title":"Guidelines for Non-Commercial Use","text":"We support and encourage the non-commercial use of Reloaded Libraries. Non-commercial use generally refers to the usage of our libraries for personal projects, educational purposes, academic research, or use by non-profit organizations.
"},{"location":"Reloaded/docs/Pages/license/#personal-projects","title":"Personal Projects","text":"You're free to use our libraries for projects that you undertake for your own learning, hobby or personal enjoyment. This includes creating mods for your favorite games or building your own applications for personal use.
"},{"location":"Reloaded/docs/Pages/license/#educational-use","title":"Educational Use","text":"Teachers and students are welcome to use our libraries as a learning resource. You can incorporate them into your teaching materials, student projects, coding bootcamps, workshops, etc.
"},{"location":"Reloaded/docs/Pages/license/#academic-research","title":"Academic Research","text":"Researchers may use our libraries for academic and scholarly research. We'd appreciate if you cite our work in any publications that result from research involving our libraries.
"},{"location":"Reloaded/docs/Pages/license/#non-profit-organizations","title":"Non-profit Organizations","text":"If you're part of a registered non-profit organization, you can use our libraries in your projects. However, any derivative work that uses our libraries must also be released under the GPL.
Please remember, if your usage of our libraries evolves from non-commercial to commercial, you must ensure compliance with the terms of the GPL v3 license.
"},{"location":"Reloaded/docs/Pages/license/#attribution-requirements","title":"Attribution Requirements","text":"As Reloaded Project is a labor of love, done purely out of passion and with an aim to contribute to the broader community, we highly appreciate your support in providing attribution when using our libraries.
While not legally mandatory under GPL v3, it is a simple act that can go a long way in recognizing the efforts of our contributors and fostering an open and collaborative atmosphere.
If you choose to provide attribution (and we hope you do!), here are some guidelines:
Acknowledge the Use of Reloaded Libraries: Mention that your project uses or is based on Reloaded libraries. This could be in your project's readme, a credits page on a website, a manual, or within the software itself.
Link to the Project: If possible, provide a link back to the Reloaded Project. This allows others to explore and potentially benefit from our work.
Remember, attribution is more than just giving credit,,, it's a way of saying thank you \ud83d\udc49\ud83d\udc48, fostering reciprocal respect, and acknowledging the power of collaborative open-source development.
We appreciate your support and look forward to seeing what amazing projects you create using Reloaded libraries!
"},{"location":"Reloaded/docs/Pages/license/#code-from-mitbsd-licensed-projects","title":"Code from MIT/BSD Licensed Projects","text":"In some rare instances, code from more permissively licensed projects, such as those under the MIT
or BSD
licenses, may be referenced, incorporated, or slightly modified within the Reloaded Project.
It's important to us to respect the terms and intentions of these permissive licenses, which often allow their code to be used in a wide variety of contexts, including in GPL-licensed projects like ours.
In these cases, the Reloaded Project is committed to clearly disclosing the usage of such code:
Method-Level Disclosure: For individual methods or small code snippets, we use appropriate attribution methods, like programming language attributes. For example, methods borrowed or adapted from MIT-licensed projects might be marked with a [MITLicense]
attribute.
File-Level Disclosure: For larger amounts of code, such as entire files or modules, we'll include the original license text at the top of the file and clearly indicate which portions of the code originate from a differently-licensed project.
Project-Level Disclosure: If an entire library or significant portion of a project under a more permissive license is used, we will include an acknowledgment in a prominent location, such as the readme file or the project's license documentation.
This approach ensures we honor the contributions of the open source community at large, respect the original licenses, and maintain transparency with our users about where code originates from.
Any files/methods or snippets marked with those attributes may be consumed using their original license terms.
i.e. If a method is marked with [MITLicense]
, you may use it under the terms of the MIT license.
We welcome and appreciate contributions to the Reloaded Project! By contributing, you agree to share your changes under the same GPLv3 license, helping to make the project better for everyone.
"},{"location":"Reloaded/docs/Pages/testing-zone/","title":"Testing Zone","text":"Info
This is a dummy page with various Material MkDocs controls and features scattered throughout for testing.
"},{"location":"Reloaded/docs/Pages/testing-zone/#custom-admonitions","title":"Custom Admonitions","text":"Reloaded Admonition
An admonition featuring a Reloaded logo. My source is in Stylesheets/extra.css as Custom 'reloaded' admonition
.
Heart Admonition
An admonition featuring a heart; because we want to contribute back to the open source community. My source is in Stylesheets/extra.css as Custom 'reloaded heart' admonition
.
Nexus Admonition
An admonition featuring a Nexus logo. My source is in Stylesheets/extra.css as Custom 'nexus' admonition
.
Heart Admonition
An admonition featuring a heart; because we want to contribute back to the open source community. My source is in Stylesheets/extra.css as Custom 'nexus heart' admonition
.
Flowchart (Source: Nexus Archive Library):
flowchart TD\n subgraph Block 2\n BigFile1.bin\n end\n\n subgraph Block 1\n BigFile0.bin\n end\n\n subgraph Block 0\n ModConfig.json -.-> Updates.json \n Updates.json -.-> more[\"... more .json files\"] \n end
Sequence Diagram (Source: Reloaded3 Specification):
sequenceDiagram\n\n % Define Items\n participant Mod Loader\n participant Virtual FileSystem (VFS)\n participant CRI CPK Archive Support\n participant Persona 5 Royal Support\n participant Joker Costume\n\n % Define Actions\n Mod Loader->>Persona 5 Royal Support: Load Mod\n Persona 5 Royal Support->>Mod Loader: Request CRI CPK Archive Support API\n Mod Loader->>Persona 5 Royal Support: Receive CRI CPK Archive Support Instance\n\n Mod Loader->>Joker Costume: Load Mod\n Mod Loader-->Persona 5 Royal Support: Notification: 'Loaded Joker Costume'\n Persona 5 Royal Support->>CRI CPK Archive Support: Add Files from 'Joker Costume' to CPK Archive (via API)
State Diagram (Source: Mermaid Docs):
stateDiagram-v2\n [*] --> Still\n Still --> [*]\n\n Still --> Moving\n Moving --> Still\n Moving --> Crash\n Crash --> [*]
Class Diagram (Arbitrary)
classDiagram\n class Animal\n `NexusMobile\u2122` <|-- Car
Note
At time of writing, version of Mermaid is a bit outdated here; and other diagrams might not render correctly (even on unmodified theme); thus certain diagrams have been omitted from here.
"},{"location":"Reloaded/docs/Pages/testing-zone/#code-block","title":"Code Block","text":"Snippet from C# version of Sewer's Virtual FileSystem (VFS):
/// <summary>\n/// Tries to get files for a specific folder, assuming the input path is already in upper case.\n/// </summary>\n/// <param name=\"folderPath\">The folder to find. Already lowercase.</param>\n/// <param name=\"value\">The returned folder instance.</param>\n/// <returns>True if found, else false.</returns>\n[MethodImpl(MethodImplOptions.AggressiveInlining)]\npublic bool TryGetFolderUpper(ReadOnlySpan<char> folderPath, out SpanOfCharDict<TTarget> value)\n{\n// Must be O(1)\nvalue = default!; // Compare equality.\n// Note to devs: Do not invert branches, we optimise for hot paths here.\nif (folderPath.StartsWith(Prefix))\n{\n// Check for subfolder in branchless way.\n// In CLR, bool is length 1, so conversion to byte should be safe.\n// Even suppose it is not; as long as code is little endian; truncating int/4 bytes to byte still results \n// in correct answer.\nvar hasSubfolder = Prefix.Length != folderPath.Length;\nvar hasSubfolderByte = Unsafe.As<bool, byte>(ref hasSubfolder);\nvar nextFolder = folderPath.SliceFast(Prefix.Length + hasSubfolderByte);\n\nreturn SubfolderToFiles.TryGetValue(nextFolder, out value!);\n}\n\nreturn false;\n}\n
Something more number heavy, Fast Inverse Square Root from Quake III Arena (unmodified).
float Q_rsqrt( float number )\n{\nlong i;\nfloat x2, y;\nconst float threehalfs = 1.5F;\n\nx2 = number * 0.5F;\ny = number;\ni = * ( long * ) &y; // evil floating point bit level hacking\ni = 0x5f3759df - ( i >> 1 ); // what the fuck? \ny = * ( float * ) &i;\ny = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration\n// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed\n\nreturn y;\n}\n
"},{"location":"Reloaded/docs/Pages/testing-zone/#default-admonitions","title":"Default Admonitions","text":"Note
Test
Abstract
Test
Info
Test
Tip
Test
Success
Test
Question
Test
Warning
Test
Failure
Test
Danger
Test
Bug
Test
Example
Test
Quote
Test
"},{"location":"Reloaded/docs/Pages/testing-zone/#tables","title":"Tables","text":"Method DescriptionGET
Fetch resource PUT
Update resource DELETE
Delete resource"},{"location":"Streams/BufferedStreamReader/","title":"BufferedStreamReader Class","text":"The BufferedStreamReader
is a buffering mechanism for reading data from streams, allowing for fast reading of data while preserving stream-like semantics.
BufferedStreamReader
is a high-performance replacement for BinaryReader with minimal error checking.
BufferedStreamReader
is not thread-safe.
BufferedStreamReader
cannot read values larger than buffer size it was initialised with.
BufferedStreamReader
has minimal error checking at runtime.
Tip
Remember, always ensure the stream and reader are properly disposed of after use. The best practice is to use the using
statement or try-finally
block in C# to ensure resources are correctly released.
Amount of time taken to read 8 MiB of data (library version 9.0.0):
Legend: - BinaryReader
read via BinaryReader. - BufferedStreamReader
read via BufferedStreamReader. - BufferedStreamReader
read via BufferedStreamReader (ReadRaw method). - NativePointer
no stream; no copy; access existing data from array (baseline reference).
Benchmarks on MemoryStream (near zero overhead) allow us to compare the performance against BinaryReader
and array access (baseline reference).
Byte
:
Int
:
Long
:
Benchmarks on FileStream allow us to compare the performance against BinaryReader
more closely.
Byte
:
Int
:
Long
:
BaseStream
: The stream this class was instantiated with.BufferBytesAvailable
: The remaining number of bytes that are currently buffered.CurrentBufferSize
: The total size of the current buffered data at this moment in time.IsEndOfStream
: This is true if end of stream was reached while refilling the internal buffer.OnEndOfStream
: This method is executed if a buffer refill does not fill the whole buffer, indicating end of stream was reached.BufferedStreamReader(TStream stream, int bufferSize = 65536)
: Constructs a BufferedStreamReader
.Just like with regular Stream APIs, less data can be returned than requested if end of stream was reached. Please note BufferedStreamReader
does not throw in these scenarios.
public void Seek(long offset, SeekOrigin origin)\n
Seeks the underlying stream to a specified position.
"},{"location":"Streams/BufferedStreamReader/#advance","title":"Advance","text":"public void Advance(long offset)\n
Advances the underlying stream by a specified number of bytes. (This is equivalent to Seek(offset, SeekOrigin.Current)
)
public T Read<T>() where T : unmanaged\npublic void Read<T>(out T value) where T : unmanaged\n
Reads an unmanaged, generic type from the stream.
"},{"location":"Streams/BufferedStreamReader/#readraw","title":"ReadRaw","text":"The returned values point to internal buffers. DO NOT MODIFY THE DATA!
public byte* ReadRaw(int length, out int available)\npublic T* ReadRaw<T>(int numItems, out int available) where T : unmanaged\n
Provides a pointer to the buffered data; buffering sufficient data if needed.
"},{"location":"Streams/BufferedStreamReader/#readraw-with-output","title":"ReadRaw (with Output)","text":"Variant of ReadRaw that copies the data to user's own destination.
public int ReadRaw<T>(Span<T> buffer) where T : unmanaged\npublic int ReadRaw<T>(T* buffer, int numItems) where T : unmanaged\n
Reads raw data from the stream, without conversion. The output is written to the supplied pointer or span.
"},{"location":"Streams/BufferedStreamReader/#readbytesunbuffered","title":"ReadBytesUnbuffered","text":"This method is useful for reading large blobs (e.g. a compressed file from an archive) without discarding the buffered data.
public int ReadBytesUnbuffered(long offset, Span<byte> data)\n
Reads a specified amount of bytes at a specific offset from the underlying stream without resetting the buffers or advancing the read pointer.
"},{"location":"Streams/BufferedStreamReader/#readmarshalled","title":"ReadMarshalled","text":"public T ReadMarshalled<T>()\n
Reads a value that requires marshalling from the stream.
"},{"location":"Streams/BufferedStreamReader/#peek","title":"Peek","text":"public T Peek<T>() where T : unmanaged\npublic void Peek<T>(out T value) where T : unmanaged\n
Reads an unmanaged, generic type from the stream without incrementing the position.
"},{"location":"Streams/BufferedStreamReader/#peekmarshalled","title":"PeekMarshalled","text":"public T PeekMarshalled<T>()\npublic void PeekMarshalled<T>(out T value)\n
Reads a value that requires marshalling from the stream without advancing the position.
"},{"location":"Streams/BufferedStreamReader/#methods-endian-extensions","title":"Methods (Endian Extensions)","text":""},{"location":"Streams/BufferedStreamReader/#peeklittleendian","title":"PeekLittleEndian","text":"public Int16 PeekLittleEndianInt16()\npublic Int16 PeekLittleEndian(out Int16 value)\npublic UInt16 PeekLittleEndianUInt16()\npublic UInt16 PeekLittleEndian(out UInt16 value)\npublic Int32 PeekLittleEndianInt32()\npublic Int32 PeekLittleEndian(out Int32 value)\npublic UInt32 PeekLittleEndianUInt32()\npublic UInt32 PeekLittleEndian(out UInt32 value)\npublic Int64 PeekLittleEndianInt64()\npublic Int64 PeekLittleEndian(out Int64 value)\npublic UInt64 PeekLittleEndianUInt64()\npublic UInt64 PeekLittleEndian(out UInt64 value)\npublic Single PeekLittleEndianSingle()\npublic Single PeekLittleEndian(out Single value)\npublic Double PeekLittleEndianDouble()\npublic Double PeekLittleEndian(out Double value)\n
Peeks a little endian value of the specified type from the stream without incrementing the position.
"},{"location":"Streams/BufferedStreamReader/#peekbigendian","title":"PeekBigEndian","text":"public Int16 PeekBigEndianInt16()\npublic Int16 PeekBigEndian(out Int16 value)\npublic UInt16 PeekBigEndianUInt16()\npublic UInt16 PeekBigEndian(out UInt16 value)\npublic Int32 PeekBigEndianInt32()\npublic Int32 PeekBigEndian(out Int32 value)\npublic UInt32 PeekBigEndianUInt32()\npublic UInt32 PeekBigEndian(out UInt32 value)\npublic Int64 PeekBigEndianInt64()\npublic Int64 PeekBigEndian(out Int64 value)\npublic UInt64 PeekBigEndianUInt64()\npublic UInt64 PeekBigEndian(out UInt64 value)\npublic Single PeekBigEndianSingle()\npublic Single PeekBigEndian(out Single value)\npublic Double PeekBigEndianDouble()\npublic Double PeekBigEndian(out Double value)\n
Peeks a big endian value of the specified type from the stream without incrementing the position.
"},{"location":"Streams/BufferedStreamReader/#readlittleendian","title":"ReadLittleEndian","text":"public Int16 ReadLittleEndianInt16()\npublic Int16 ReadLittleEndian(out Int16 value)\npublic UInt16 ReadLittleEndianUInt16()\npublic UInt16 ReadLittleEndian(out UInt16 value)\npublic Int32 ReadLittleEndianInt32()\npublic Int32 ReadLittleEndian(out Int32 value)\npublic UInt32 ReadLittleEndianUInt32()\npublic UInt32 ReadLittleEndian(out UInt32 value)\npublic Int64 ReadLittleEndianInt64()\npublic Int64 ReadLittleEndian(out Int64 value)\npublic UInt64 ReadLittleEndianUInt64()\npublic UInt64 ReadLittleEndian(out UInt64 value)\npublic Single ReadLittleEndianSingle()\npublic Single ReadLittleEndian(out Single value)\npublic Double ReadLittleEndianDouble()\npublic Double ReadLittleEndian(out Double value)\n
Reads a little endian value of the specified type from the stream and advances the position by the size of the type.
"},{"location":"Streams/BufferedStreamReader/#readbigendian","title":"ReadBigEndian","text":"public Int16 ReadBigEndianInt16()\npublic Int16 ReadBigEndian(out Int16 value)\npublic UInt16 ReadBigEndianUInt16()\npublic UInt16 ReadBigEndian(out UInt16 value)\npublic Int32 ReadBigEndianInt32()\npublic Int32 ReadBigEndian(out Int32 value)\npublic UInt32 ReadBigEndianUInt32()\npublic UInt32 ReadBigEndian(out UInt32 value)\npublic Int64 ReadBigEndianInt64()\npublic Int64 ReadBigEndian(out Int64 value)\npublic UInt64 ReadBigEndianUInt64()\npublic UInt64 ReadBigEndian(out UInt64 value)\npublic Single ReadBigEndianSingle()\npublic Single ReadBigEndian(out Single value)\npublic Double ReadBigEndianDouble()\npublic Double ReadBigEndian(out Double value)\n
Reads a big endian value of the specified type from the stream and advances the position by the size of the type.
"},{"location":"Streams/BufferedStreamReader/#aslittleendian","title":"AsLittleEndian","text":"Implements, IEndianedBufferStreamReader<TStream>
. Use constraint where T : IEndianedBufferStreamReader<TStream>
to write endian agnostic code without any overhead.
public LittleEndianBufferedStreamReader<TStream> AsLittleEndian();\n
Returns a reader that can be used to read little endian values from the stream.
"},{"location":"Streams/BufferedStreamReader/#asbigendian","title":"AsBigEndian","text":"Implements, IEndianedBufferStreamReader<TStream>
. Use constraint where T : IEndianedBufferStreamReader<TStream>
to write endian agnostic code without any overhead.
public BigEndianBufferedStreamReader<TStream> AsBigEndian();\n
Returns a reader that can be used to read big endian values from the stream.
"},{"location":"Streams/BufferedStreamReader/#methods-endian-struct-extensions","title":"Methods (Endian Struct Extensions)","text":"The following methods are valid for structs which implement ICanReverseEndian
public T PeekLittleEndianStruct<T>() where T : unmanaged, ICanReverseEndian\npublic void PeekLittleEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian\n
Peeks a little endian unmanaged, generic type from the stream without incrementing the position.
"},{"location":"Streams/BufferedStreamReader/#peekbigendianstruct","title":"PeekBigEndianStruct","text":"public T PeekBigEndianStruct<T>() where T : unmanaged, ICanReverseEndian\npublic void PeekBigEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian\n
Peeks a big endian unmanaged, generic type from the stream without incrementing the position.
"},{"location":"Streams/BufferedStreamReader/#readlittleendianstruct","title":"ReadLittleEndianStruct","text":"public T ReadLittleEndianStruct<T>() where T : unmanaged, ICanReverseEndian\npublic void ReadLittleEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian\n
Reads a little endian unmanaged, generic type from the stream.
"},{"location":"Streams/BufferedStreamReader/#readbigendianstruct","title":"ReadBigEndianStruct","text":"public T ReadBigEndianStruct<T>() where T : unmanaged, ICanReverseEndian\npublic void ReadBigEndianStruct<T>(out T value) where T : unmanaged, ICanReverseEndian\n
Reads a big endian unmanaged, generic type from the stream.
"},{"location":"Streams/BufferedStreamReader/#examples","title":"Examples","text":""},{"location":"Streams/BufferedStreamReader/#creating-a-bufferedstreamreader","title":"Creating a BufferedStreamReader","text":"var stream = File.OpenRead(\"myFile.txt\");\nusing var reader = new BufferedStreamReader<FileStream>(stream);\n
"},{"location":"Streams/BufferedStreamReader/#reading-an-integer-from-the-stream","title":"Reading an Integer from the Stream","text":"int value = reader.Read<int>();\nConsole.WriteLine($\"Read value: {value}\");\n
"},{"location":"Streams/BufferedStreamReader/#reading-raw-bytes","title":"Reading Raw Bytes","text":"Do not modify the returned data! Copy it elsewhere first!
int length = 10;\nint available;\nbyte* rawBytes = reader.ReadRaw(length, out available);\n
"},{"location":"Streams/BufferedStreamReader/#reading-raw-structs","title":"Reading Raw Structs","text":"using var reader = new BufferedStreamReader<FileStream>(fileStream);\nSpan<Vector3> span = stackalloc Vector3[10];\nint readItems = reader.ReadRaw(span);\n\nConsole.WriteLine($\"{readItems} Vector3 items were read from the stream.\");\n
In this example, we create a BufferedStreamReader
using a FileStream
and then declare a Span<Vector3>
where Vector3
is a struct representing a 3D vector. We then read from the stream directly into the Span<Vector3>
. After reading, we print out how many Vector3
items were read from the stream.
var reader = new BufferedStreamReader<FileStream>(fileStream);\nreader.Seek(100, SeekOrigin.Begin); // Seek to 100 bytes from the start\nreader.Advance(50); // Advance 50 bytes from the current position\n\nConsole.WriteLine($\"Current stream position is {reader.Position()}\");\n
In this example, we first seek to 100 bytes from the start of the stream. Then we advance 50 bytes from the current position. After that, we print out the current stream position.
"},{"location":"Streams/BufferedStreamReader/#reading-unmanaged-types","title":"Reading Unmanaged Types","text":"using var reader = new BufferedStreamReader<FileStream>(fileStream);\nint value = reader.Read<int>(); // Reads an integer from the stream\n\nConsole.WriteLine($\"The read integer value is {value}\");\n
In this example, we read an integer directly from the stream and print it out.
"},{"location":"Streams/BufferedStreamReader/#reading-large-raw-data","title":"Reading Large Raw Data","text":"using var reader = new BufferedStreamReader<FileStream>(fileStream);\nSpan<byte> dataSpan = stackalloc byte[1024];\nint bytesRead = reader.ReadBytesUnbuffered(200, dataSpan);\n\nConsole.WriteLine($\"{bytesRead} bytes were read from the stream.\");\n
In this example, we read 1024 bytes starting from 200 bytes offset into a Span<byte>
without resetting the buffers or advancing the read pointer. After reading, we print out how many bytes were read from the stream.
Value of bytesRead
may be less than length of dataSpan
if end of stream was reached.
using var reader = new BufferedStreamReader<FileStream>(fileStream);\nint value = reader.Peek<int>(); // Peeks an integer from the stream\n\nConsole.WriteLine($\"The peeked integer value is {value}. Stream did not advance.\");\n
"},{"location":"Streams/BufferedStreamReader/#using-customized-buffer-size","title":"Using Customized Buffer Size","text":"The default buffer size of 64KBytes should be sufficient for most use cases, including file reads.
int bufferSize = 8192; // 8 KB\nusing var reader = new BufferedStreamReader<FileStream>(fileStream, bufferSize);\n\nConsole.WriteLine($\"Custom buffer size of {bufferSize} bytes is used for the reader.\");\n
In this example, a custom buffer size is set when creating a BufferedStreamReader
. This allows you to tune the buffer size to match your specific use case, potentially improving performance.
var reader = new BufferedStreamReader<FileStream>(fileStream);\nreader.Seek(100, SeekOrigin.Begin); int value = reader.Read<int>(); reader.Advance(50); Vector3 vector = reader.Read<Vector3>();\n\nConsole.WriteLine($\"Read integer value: {value}, Vector3: {vector.X}, {vector.Y}, {vector.Z}.\");\n
In this final example, multiple operations are combined. First, the reader seeks to a specific position in the stream. Then, an integer is read, the reader advances a certain number of bytes, and finally, a Vector3
struct is read. The results are then printed to the console.
Shows you how to write code that works with both Big and Little Endian data.
using var reader = new BufferedStreamReader<FileStream>(fileStream);\n\n// Read in Big Endian\nRead(reader.AsBigEndian());\n// or... as Little Endian\nRead(reader.AsLittleEndian());\n\nprivate void Read<TReader>(TReader reader) where TReader : IEndianedBufferStreamReader\n{\n// Some neat parsing code here.\nvar i16 = reader.ReadInt16();\nvar i32 = reader.PeekInt32();\n}\n
This approach allows you to write code which uses the same logic for both big and little endian. You can either pass reader.AsLittleEndian()
or reader.AsBigEndian()
to your parsing code. Either way, your reader will be devirtualized and you'll be able to parse away with 0 overhead.
using var reader = new BufferedStreamReader<FileStream>(fileStream);\n\n// Little Endian\nInt16 littleEndianInt16 = reader.PeekLittleEndianInt16();\nConsole.WriteLine($\"The read Int16 value at offset 0 in Little Endian is {littleEndianInt16}\");\n\n// Big Endian\nInt16 bigEndianInt16 = reader.PeekBigEndianInt16();\nConsole.WriteLine($\"The read Int16 value at offset 0 in Big Endian is {bigEndianInt16}\");\n
"},{"location":"Streams/BufferedStreamReader/#examples-endian-struct-extensions","title":"Examples (Endian Struct Extensions)","text":"CustomStruct must implement ICanReverseEndian
Example:
public struct CustomStruct : ICanReverseEndian\n{\npublic int Value1;\npublic int Value2;\npublic int Value3;\n\n// ICanReverseEndian\npublic void ReverseEndian()\n{\nValue1 = Endian.Reverse(Value1);\nValue2 = Endian.Reverse(Value2);\nValue3 = Endian.Reverse(Value3);\n}\n}\n
"},{"location":"Streams/BufferedStreamReader/#reading-a-little-endian-struct","title":"Reading a Little Endian Struct","text":"using var reader = new BufferedStreamReader<FileStream>(fileStream);\nvar value = reader.ReadLittleEndianStruct<CustomStruct>();\n\nConsole.WriteLine($\"The read CustomStruct value is {value}\");\n
In this example, a CustomStruct
is read directly from the stream using little endian byte order.
using var reader = new BufferedStreamReader<FileStream>(fileStream);\nvar value = reader.ReadBigEndianStruct<CustomStruct>();\n\nConsole.WriteLine($\"The read CustomStruct value is {value}\");\n
In this example, a CustomStruct
is read directly from the stream using big endian byte order.
using var reader = new BufferedStreamReader<FileStream>(fileStream);\nvar value = reader.PeekLittleEndianStruct<CustomStruct>();\n\nConsole.WriteLine($\"The peeked CustomStruct value is {value}. Stream did not advance.\");\n
In this example, a CustomStruct
is peeked from the stream without advancing the stream position, using little endian byte order.
using var reader = new BufferedStreamReader<FileStream>(fileStream);\nvar value = reader.PeekBigEndianStruct<CustomStruct>();\n\nConsole.WriteLine($\"The peeked CustomStruct value is {value}. Stream did not advance.\");\n
In this example, a CustomStruct
is peeked from the stream without advancing the stream position, using big endian byte order.
CustomStruct
must be an unmanaged type and implement ICanReverseEndian
interface.
Common Info About Endian Readers and Writers
"},{"location":"Streams/EndianReaders/About/#interfaces","title":"Interfaces","text":"Structs can implement ICanBeReadByAnEndianReader
and ICanWriteToAnEndianWriter
to allow for easy reading and writing.
Example:
"},{"location":"Streams/EndianReaders/About/#about-the-offset-methods","title":"About the Offset Methods","text":"The structs provide methods named WriteAtOffset
/ ReadAtOffset
to operate on an offset of the current pointer without advancing the pointer itself.
These methods offer some minor performance advantages.
"},{"location":"Streams/EndianReaders/About/#improved-pipelining","title":"Improved Pipelining","text":"By reducing the dependency of future instructions on earlier instructions, these offset methods allow for better pipelining. For example, a future read operation does not need to wait for the Ptr
value to be updated from a previous operation.
The Just-In-Time (JIT) compiler can recognize when the offset
parameters are specified as constants and can optimize the instructions accordingly. This can lead to more efficient code execution.
writer.WriteAtOffset(Hash, 0);\nwriter.WriteAtOffset((int)DecompressedSize, 8);\nwriter.WriteAtOffset(new OffsetPathIndexTuple(DecompressedBlockOffset, FilePathIndex, FirstBlockIndex).Data, 12);\nwriter.Seek(NativeFileEntryV0.SizeBytes);\n
Because write on line 1
, does not depend on modified pointer after line 0
, execution is faster, as the CPU can better pipeline the instructions as there is no dependency on the ptr result of the previous method call.
A struct for reading from a pointer in Big Endian.
BigEndianReader is preferred over BufferedStreamReader when all data is already in memory.
The BigEndianReader
struct provides utility methods for reading various data types from a pointer in Big Endian format.
These methods read the specified data type from the current pointer in Big Endian format and advance the pointer.
public byte ReadByte()\npublic sbyte ReadSByte()\npublic short ReadShort()\npublic ushort ReadUShort()\npublic uint ReadUInt()\npublic int ReadInt()\npublic ulong ReadULong()\npublic long ReadLong()\npublic float ReadFloat()\npublic double ReadDouble()\n
"},{"location":"Streams/EndianReaders/BigEndianReader/#readatoffset-methods","title":"ReadAtOffset Methods","text":"These methods read the specified data type from the specified offset in Big Endian format without advancing the pointer.
public byte ReadByteAtOffset(int offset)\npublic sbyte ReadSByteAtOffset(int offset)\npublic short ReadShortAtOffset(int offset)\npublic ushort ReadUShortAtOffset(int offset)\npublic uint ReadUIntAtOffset(int offset)\npublic int ReadIntAtOffset(int offset)\npublic ulong ReadULongAtOffset(int offset)\npublic long ReadLongAtOffset(int offset)\npublic float ReadFloatAtOffset(int offset)\npublic double ReadDoubleAtOffset(int offset)\n
"},{"location":"Streams/EndianReaders/BigEndianReader/#seek-method","title":"Seek Method","text":"This method advances the pointer by a specified number of bytes.
public void Seek(int offset)\n
"},{"location":"Streams/EndianReaders/BigEndianReader/#about-the-offset-methods","title":"About the Offset Methods","text":"The BigEndianReader
struct provides several methods that read from a specific offset without advancing the pointer. These methods include ReadShortAtOffset
, ReadIntAtOffset
, ReadLongAtOffset
, and ReadUlongAtOffset
.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, you can read more about this in About Section
"},{"location":"Streams/EndianReaders/BigEndianReader/#usage","title":"Usage","text":""},{"location":"Streams/EndianReaders/BigEndianReader/#reading-from-pointer","title":"Reading from Pointer","text":"BigEndianReader reader = new BigEndianReader(GetPointer());\n\nbyte byteValue = reader.ReadByte();\nuint uintValue = reader.ReadUInt();\ndouble doubleValue = reader.ReadDouble();\n
"},{"location":"Streams/EndianReaders/BigEndianReader/#reading-from-offset","title":"Reading from Offset","text":"BigEndianReader reader = new BigEndianReader(GetPointer());\n\nbyte byteValue = reader.ReadByteAtOffset(5);\nuint uintValue = reader.ReadUIntAtOffset(10);\ndouble doubleValue = reader.ReadDoubleAtOffset(15);\n
"},{"location":"Streams/EndianReaders/BigEndianReader/#advancing-the-pointer","title":"Advancing the Pointer","text":"BigEndianReader reader = new BigEndianReader(GetPointer(););\n\nreader.Seek(10); // Advances the pointer by 10 bytes.\n
"},{"location":"Streams/EndianReaders/BigEndianWriter/","title":"BigEndianWriter","text":"Utility struct for writing to a pointer in Big Endian.
BigEndianWriter
is a struct providing methods for writing various types of data to a memory location, with the data being written in Big Endian
byte order. This includes writing integers, floating point values, and arrays of bytes, either advancing the pointer after each write or writing at a specific offset without advancing the pointer.
public byte* Ptr;\n
Current pointer being written to.
"},{"location":"Streams/EndianReaders/BigEndianWriter/#constructors","title":"Constructors","text":""},{"location":"Streams/EndianReaders/BigEndianWriter/#bigendianwriter_1","title":"BigEndianWriter","text":"public BigEndianWriter(byte* ptr)\n
Creates a simple wrapper around a pointer that writes in Big Endian. ptr
is the pointer to the item behind the writer.
public void Write(sbyte value)\npublic void Write(byte value)\npublic void Write(short value)\npublic void Write(ushort value)\npublic void Write(int value)\npublic void Write(uint value)\npublic void Write(long value)\npublic void Write(ulong value)\npublic void Write(float value)\npublic void Write(double value)\npublic void Write(Span<byte> data)\n
Writes a value to the current pointer and advances the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
, and Span<byte>
.
public void WriteAtOffset(sbyte value, int offset)\npublic void WriteAtOffset(byte value, int offset)\npublic void WriteAtOffset(short value, int offset)\npublic void WriteAtOffset(ushort value, int offset)\npublic void WriteAtOffset(int value, int offset)\npublic void WriteAtOffset(uint value, int offset)\npublic void WriteAtOffset(long value, int offset)\npublic void WriteAtOffset(ulong value, int offset)\npublic void WriteAtOffset(float value, int offset)\npublic void WriteAtOffset(double value, int offset)\n
Writes a value to the specified offset without advancing the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
.
public void Seek(int offset)\n
Advances the stream by a specified number of bytes. offset
is the number of bytes to advance by.
The BigEndianWriter
struct provides several methods that operate on a specific offset without advancing the pointer.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, you can read more about this in About Section
"},{"location":"Streams/EndianReaders/BigEndianWriter/#usage","title":"Usage","text":"Example usage of the BigEndianWriter
struct:
var writer = new BigEndianWriter(ptr);\n\nwriter.Write((int)12345); // Write an int\nwriter.Write((byte)65); // Write a byte\nwriter.WriteAtOffset((long)9876543210, 5); // Write a long at offset 5 from current pointer\n
"},{"location":"Streams/EndianReaders/LittleEndianReader/","title":"LittleEndianReader","text":"A struct for reading from a pointer in Big Endian.
LittleEndianReader is preferred over BufferedStreamReader when all data is already in memory.
The LittleEndianReader
struct provides utility methods for reading various data types from a pointer in Big Endian format.
These methods read the specified data type from the current pointer in Big Endian format and advance the pointer.
public byte ReadByte()\npublic sbyte ReadSByte()\npublic short ReadShort()\npublic ushort ReadUShort()\npublic uint ReadUInt()\npublic int ReadInt()\npublic ulong ReadULong()\npublic long ReadLong()\npublic float ReadFloat()\npublic double ReadDouble()\n
"},{"location":"Streams/EndianReaders/LittleEndianReader/#readatoffset-methods","title":"ReadAtOffset Methods","text":"These methods read the specified data type from the specified offset in Big Endian format without advancing the pointer.
public byte ReadByteAtOffset(int offset)\npublic sbyte ReadSByteAtOffset(int offset)\npublic short ReadShortAtOffset(int offset)\npublic ushort ReadUShortAtOffset(int offset)\npublic uint ReadUIntAtOffset(int offset)\npublic int ReadIntAtOffset(int offset)\npublic ulong ReadULongAtOffset(int offset)\npublic long ReadLongAtOffset(int offset)\npublic float ReadFloatAtOffset(int offset)\npublic double ReadDoubleAtOffset(int offset)\n
"},{"location":"Streams/EndianReaders/LittleEndianReader/#seek-method","title":"Seek Method","text":"This method advances the pointer by a specified number of bytes.
public void Seek(int offset)\n
"},{"location":"Streams/EndianReaders/LittleEndianReader/#about-the-offset-methods","title":"About the Offset Methods","text":"The LittleEndianReader
struct provides several methods that read from a specific offset without advancing the pointer. These methods include ReadShortAtOffset
, ReadIntAtOffset
, ReadLongAtOffset
, and ReadUlongAtOffset
.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, you can read more about this in About Section
"},{"location":"Streams/EndianReaders/LittleEndianReader/#usage","title":"Usage","text":""},{"location":"Streams/EndianReaders/LittleEndianReader/#reading-from-pointer","title":"Reading from Pointer","text":"LittleEndianReader reader = new LittleEndianReader(GetPointer());\n\nbyte byteValue = reader.ReadByte();\nuint uintValue = reader.ReadUInt();\ndouble doubleValue = reader.ReadDouble();\n
"},{"location":"Streams/EndianReaders/LittleEndianReader/#reading-from-offset","title":"Reading from Offset","text":"LittleEndianReader reader = new LittleEndianReader(GetPointer());\n\nbyte byteValue = reader.ReadByteAtOffset(5);\nuint uintValue = reader.ReadUIntAtOffset(10);\ndouble doubleValue = reader.ReadDoubleAtOffset(15);\n
"},{"location":"Streams/EndianReaders/LittleEndianReader/#advancing-the-pointer","title":"Advancing the Pointer","text":"LittleEndianReader reader = new LittleEndianReader(GetPointer(););\n\nreader.Seek(10); // Advances the pointer by 10 bytes.\n
"},{"location":"Streams/EndianReaders/LittleEndianWriter/","title":"LittleEndianWriter","text":"Utility struct for writing to a pointer in Big Endian.
LittleEndianWriter
is a struct providing methods for writing various types of data to a memory location, with the data being written in Big Endian
byte order. This includes writing integers, floating point values, and arrays of bytes, either advancing the pointer after each write or writing at a specific offset without advancing the pointer.
public byte* Ptr;\n
Current pointer being written to.
"},{"location":"Streams/EndianReaders/LittleEndianWriter/#constructors","title":"Constructors","text":""},{"location":"Streams/EndianReaders/LittleEndianWriter/#littleendianwriter_1","title":"LittleEndianWriter","text":"public LittleEndianWriter(byte* ptr)\n
Creates a simple wrapper around a pointer that writes in Big Endian. ptr
is the pointer to the item behind the writer.
public void Write(sbyte value)\npublic void Write(byte value)\npublic void Write(short value)\npublic void Write(ushort value)\npublic void Write(int value)\npublic void Write(uint value)\npublic void Write(long value)\npublic void Write(ulong value)\npublic void Write(float value)\npublic void Write(double value)\npublic void Write(Span<byte> data)\n
Writes a value to the current pointer and advances the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
, and Span<byte>
.
public void WriteAtOffset(sbyte value, int offset)\npublic void WriteAtOffset(byte value, int offset)\npublic void WriteAtOffset(short value, int offset)\npublic void WriteAtOffset(ushort value, int offset)\npublic void WriteAtOffset(int value, int offset)\npublic void WriteAtOffset(uint value, int offset)\npublic void WriteAtOffset(long value, int offset)\npublic void WriteAtOffset(ulong value, int offset)\npublic void WriteAtOffset(float value, int offset)\npublic void WriteAtOffset(double value, int offset)\n
Writes a value to the specified offset without advancing the pointer. Overloads exist for various data types including sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
.
public void Seek(int offset)\n
Advances the stream by a specified number of bytes. offset
is the number of bytes to advance by.
The LittleEndianWriter
struct provides several methods that operate on a specific offset without advancing the pointer.
While these methods do not significantly reduce the instruction count, they offer some minor performance advantages, you can read more about this in About Section
"},{"location":"Streams/EndianReaders/LittleEndianWriter/#usage","title":"Usage","text":"Example usage of the LittleEndianWriter
struct:
var writer = new LittleEndianWriter(ptr);\n\nwriter.Write((int)12345); // Write an int\nwriter.Write((byte)65); // Write a byte\nwriter.WriteAtOffset((long)9876543210, 5); // Write a long at offset 5 from current pointer\n
"},{"location":"Utilities/ArrayRental/","title":"ArrayRental","text":"ArrayRental
is a struct that represents an instance of a rented array. It should be disposed of after use with the using
statement.
If you need a generic version, use ArrayRental<T>
.
The underlying array is managed by the shared ArrayPool
.
Array
: The underlying array for this rental. Span
: Span for the underlying array. May be larger than requested length.public ArrayRental(int numBytes)\n
Rents a requested minimum number of bytes. Amount of data rented might be larger.
"},{"location":"Utilities/ArrayRental/#slices","title":"Slices","text":"ArrayRentalSlice
represents a slice of an ArrayRental
. This API is meant to be used as a return value from methods, and transfers ownership of the rental from the internal ArrayRental
.
public ArrayRentalSlice(ArrayRental rental, int length)\n
Represents a slice of the array rental.
"},{"location":"Utilities/ArrayRental/#usage","title":"Usage","text":""},{"location":"Utilities/ArrayRental/#rent-an-array-and-create-a-slice","title":"Rent an Array and Create a Slice","text":"Make sure to dispose with using
statement or explicit dispose.
// Will create a rental of at least 4096 bytes.\nusing var rental = new ArrayRental(4096);\n
"},{"location":"Utilities/ArrayRental/#rent-an-array-and-create-a-slice_1","title":"Rent an Array and Create a Slice","text":"When you create an ArrayRentalSlice
, the responsibility of disposing the rental is transferred to the slice. Make sure to not double dispose.
// Some Method\nArrayRentalSlice CompressData(byte* data, int length) {\nvar rental = new ArrayRental(numBytes);\n// Compress into rental....\n// And return a slice with just the info needed.\nreturn new ArrayRentalSlice(rental, sliceLength);\n}\n\n// Method consumer\nusing var compressed = CompressData(data, length);\n
"},{"location":"Utilities/Box/","title":"Box Forked from Community Toolkit.
A class representing a boxed value type, providing build-time validation and automatic unboxing.
Box is a utility class that represents a boxed value type on the managed heap. It can be used in place of a non-generic object reference to a boxed value type, making the code more expressive and reducing the chances of errors.","text":""},{"location":"Utilities/Box/#methods","title":"Methods","text":""},{"location":"Utilities/Box/#getfrom","title":"GetFrom","text":"
public static Box<T> GetFrom(object obj)\n
Returns a Box<T>
reference from the input object
instance, representing a boxed T
value.
public static Box<T> DangerousGetFrom(object obj)\n
Returns a Box<T>
reference from the input object
instance, representing a boxed T
value. This method doesn't check the actual type of obj
, so it is the responsibility of the caller to ensure it actually represents a boxed T
value and not some other instance.
public static bool TryGetFrom(object obj, out Box<T>? box)\n
Tries to get a Box<T>
reference from an input object
representing a boxed T
value. Returns true
if a Box<T>
instance was retrieved correctly, false
otherwise.
public static implicit operator T(Box<T> box)\n
Implicitly gets the T
value from a given Box<T>
instance.
public static implicit operator Box<T>(T value)\n
Implicitly creates a new Box<T>
instance from a given T
value.
Box<int> box = 42;\nint sum = box.Value + 1;\n
","text":""},{"location":"Utilities/Box/#retrieve-a-mutable-reference-to-a-boxed-value","title":"Retrieve a Mutable Reference to a Boxed Value Box<MyStruct> box = new MyStruct { Field1 = 1, Field2 = 2 };\nref MyStruct myStructRef = ref box.GetReference();\nmyStructRef.Field1 = 3;\n
","text":""},{"location":"Utilities/Box/#extension-methods","title":"Extension Methods","text":""},{"location":"Utilities/Box/#getreference","title":"GetReference public static ref T GetReference<T>(this Box<T> box) where T : struct\n
Gets a T
reference from a Box<T>
instance.
Box<MyStruct> box = new MyStruct { Field1 = 1, Field2 = 2 };\nref MyStruct myStructRef = ref box.GetReference();\n
","text":""},{"location":"Utilities/CircularBuffer/","title":"CircularBuffer Class","text":"The CircularBuffer
is a writable buffer useful for temporary storage of data. It's a buffer whereby once you reach the end of the buffer, it loops back over to the beginning of the buffer, overwriting old elements.
Start
: The address of the CircularBuffer
.End
: The address of the CircularBuffer
.Current
: Address of the current item in the buffer.Remaining
: Remaining space in the buffer.Size
: The overall size of the buffer.CircularBuffer(nuint start, int size)
: Creates a CircularBuffer
within the target memory source.public nuint Add(byte* data, uint length)\npublic nuint Add<TSource>(TSource source, byte* data, uint length)\npublic nuint Add<TSource, T>(TSource source, T value)\npublic nuint Add<T>(T value)\n
Adds a new item onto the circular buffer. Returns a pointer to the recently added item to the buffer, or zero if the item cannot fit.
"},{"location":"Utilities/CircularBuffer/#canitemfit","title":"CanItemFit","text":"public ItemFit CanItemFit(uint itemSize)\npublic ItemFit CanItemFit<T>()\n
Returns an enumerable describing if an item can fit into the buffer.
"},{"location":"Utilities/CircularBuffer/#itemfit-enum","title":"ItemFit Enum","text":"Describes whether an item can fit into a given buffer.
Yes
: The item can fit into the buffer.StartOfBuffer
: The item can fit into the buffer, but not in the remaining space (will be placed at start of buffer).No
: The item is too large to fit into the buffer.var circularBuffer = new CircularBuffer((nuint)bufferStartPtr, bufferSize);\nnuint pointerToValue = circularBuffer.Add(42);\n\nif (pointerToValue != UIntPtr.Zero)\nConsole.WriteLine(\"Value added successfully!\");\nelse\nConsole.WriteLine(\"Failed to add value to the buffer.\");\n
"},{"location":"Utilities/CircularBuffer/#adding-a-custom-struct-to-the-buffer","title":"Adding a Custom Struct to The Buffer","text":"var circularBuffer = new CircularBuffer((nuint)bufferStart, bufferSize);\nvar valueToAdd = new Vector2 { X = 1, Y = 2 };\nnuint pointerToValue = circularBuffer.Add(valueToAdd);\n\nif (pointerToValue != UIntPtr.Zero)\nConsole.WriteLine(\"Value added successfully!\");\nelse\nConsole.WriteLine(\"Failed to add value to the buffer.\");\n
"},{"location":"Utilities/CircularBuffer/#checking-if-an-item-can-fit-in-the-buffer","title":"Checking if an item can fit in the buffer.","text":"var circularBuffer = new CircularBuffer((nuint)bufferStart, bufferSize);\nvar result = circularBuffer.CanItemFit<double>();\n\nswitch (result)\n{\ncase CircularBuffer.ItemFit.Yes:\n// \"A double can fit in the buffer.\"\nbreak;\ncase CircularBuffer.ItemFit.StartOfBuffer:\n// \"A double can fit in the buffer, but it will be placed at the start of the buffer.\"\nbreak;\ncase CircularBuffer.ItemFit.No:\n// \"A double cannot fit in the buffer.\"\nbreak;\n}\n
"},{"location":"Utilities/Endian/","title":"Endian Class","text":"The Endian
class provides various utilities for converting primitives and structures between endians.
public static byte Reverse(byte value)\npublic static sbyte Reverse(sbyte value)\npublic static short Reverse(short value)\npublic static ushort Reverse(ushort value)\npublic static int Reverse(int value)\npublic static uint Reverse(uint value)\npublic static long Reverse(long value)\npublic static ulong Reverse(ulong value)\npublic static float Reverse(float value)\npublic static double Reverse(double value)\n
Reverses the byte order of the specified value.
"},{"location":"Utilities/Endian/#reverse-generic","title":"Reverse (Generic)","text":"Info
Utility method for structs with single values.
Warning
This method is provided for convenience. If the item size is not 1/2/4/8 bytes, this method will throw.
public static T Reverse<T>(T value) where T : unmanaged\n
Reverses the endian of a primitive value such as int, short, float, double etc.
"},{"location":"Utilities/Endian/#examples","title":"Examples","text":""},{"location":"Utilities/Endian/#reversing-an-integers-endian","title":"Reversing an Integer's Endian","text":"int value = 42;\nint reversedValue = Endian.Reverse(value);\n\nConsole.WriteLine($\"Original Value: {value}, Reversed Value: {reversedValue}\");\n
This will print something like:
Original Value: 42, Reversed Value: 704643072\n
"},{"location":"Utilities/Endian/#reversing-a-custom-structs-endian","title":"Reversing a Custom Struct's Endian","text":"Warning
This will only work if the struct is a blittable type.
struct NamedOffset\n{\npublic int Offset;\n}\n\nMyStruct value = new NamedOffset { Offset = 1 };\nMyStruct reversedValue = Endian.Reverse(value);\n\nConsole.WriteLine($\"Original Value: {value.X}, Reversed Value: {reversedValue.X}\");\n
This will print something like:
Original Value: 1 Reversed Value: 16777216\n
"},{"location":"Utilities/ObjectMarshal/","title":"ObjectMarshal","text":"Forked from Community Toolkit.
Utility class providing methods for working with object
instances.
ObjectMarshal is a utility class that provides methods for calculating byte offsets to specific fields within objects, retrieving references to data within objects at specific offsets, and unboxing values from objects.
"},{"location":"Utilities/ObjectMarshal/#methods","title":"Methods","text":""},{"location":"Utilities/ObjectMarshal/#dangerousgetobjectdatabyteoffset","title":"DangerousGetObjectDataByteOffset","text":"public static IntPtr DangerousGetObjectDataByteOffset<T>(object obj, ref T data)\n
Calculates the byte offset to a specific field within a given object
. The input parameters are not validated, and it's the responsibility of the caller to ensure that the data
reference is actually pointing to a memory location within obj
.
public static ref T DangerousGetObjectDataReferenceAt<T>(object obj, IntPtr offset)\n
Gets a T
reference to data within a given object at a specified offset. None of the input arguments is validated, and it is the responsibility of the caller to ensure they are valid.
public static bool TryUnbox<T>(this object obj, out T value) where T : struct\n
Tries to get a boxed T
value from an input object
instance. Returns true if a T
value was retrieved correctly, false
otherwise.
public static ref T DangerousUnbox<T>(object obj) where T : struct\n
Unboxes a T
value from an input object instance. Throws an InvalidCastException
when obj is not of type T
.
MyStruct myStruct = new MyStruct();\nref int fieldRef = ref myStruct.SomeIntField;\nIntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(myStruct, ref fieldRef);\n
"},{"location":"Utilities/ObjectMarshal/#get-reference-at-specific-offset","title":"Get Reference At Specific Offset","text":"MyStruct myStruct = new MyStruct();\nIntPtr fieldOffset = /* calculated offset */;\nref int fieldRef = ref ObjectMarshal.DangerousGetObjectDataReferenceAt<MyStruct>(myStruct, fieldOffset);\n
"},{"location":"Utilities/ObjectMarshal/#try-to-unbox-a-value","title":"Try to Unbox a value","text":"object boxedInt = 42;\nint unboxedInt;\n\nif (boxedInt.TryUnbox(out unboxedInt))\nConsole.WriteLine($\"Unboxed value: {unboxedInt}\");\n
"},{"location":"Utilities/ObjectMarshal/#unbox-a-value","title":"Unbox a value","text":"object boxedInt = 42;\nref int unboxedInt = ref ObjectMarshal.DangerousUnbox<int>(boxedInt);\n
"},{"location":"Utilities/Pinnable/","title":"Pinnable<T>","text":"Allows you to pin an unmanaged object in a static location in memory, to be later accessible from native code.
Assume value is copied; modify the Pinnable instance to read/write values once created.
The Pinnable<T>
class provides a way to pin native unmanaged objects in memory, ensuring that their memory addresses remain constant during the lifetime of the Pinnable<T>
instance. This can be useful when working with native code that requires static memory addresses.
On newer runtimes, the memory is allocated into the Pinned Object Heap (POH)
; thus has no impact on effectiveness of regular garbage collection.
Value
: The value pointed to by the Pointer
. If the class was instantiated using an array, this is the first element of the array.Pointer
: Pointer to the native value in question. If the class was instantiated using an array, this is the pointer to the first element of the array.public Pinnable(T[] value); // Pins an array of values to the heap.\npublic Pinnable(in T value); // Pins a single value to the heap.\n
"},{"location":"Utilities/Pinnable/#disposal","title":"Disposal","text":"The Pinnable<T>
class implements the IDisposable
interface, which means you should dispose of the instance when you are done with it.
This can be done using the using
statement, or by explicitly calling the Dispose()
method.
int value = 42;\n\nusing var pinnable = new Pinnable<int>(value);\n// Access the pinned value through pinnable.Value\n// Access the memory address of the pinned value through pinnable.Pointer\n
"},{"location":"Utilities/Pinnable/#pinning-an-array","title":"Pinning an Array","text":"int[] array = new int[] { 1, 2, 3, 4, 5 };\n\nusing var pinnable = new Pinnable<int>(value);\n// Access array element via `pinnable[x]`.\n// Access the first element of the pinned array through pinnable.Value\n// Access the memory address of the first element of the pinned array through pinnable.Pointer\n
"},{"location":"Utilities/TypeInfo/","title":"TypeInfo","text":"Utility class providing methods for obtaining information about different types.
"},{"location":"Utilities/TypeInfo/#methods","title":"Methods","text":""},{"location":"Utilities/TypeInfo/#approximateisblittable","title":"ApproximateIsBlittable","text":"public static bool ApproximateIsBlittable<T>()\n
Returns true
if a type is blittable, else false
. This method uses an approximation, and may not work with generic types with blittable (unmanaged) constraints."},{"location":"Utilities/TypeInfo/#approximateisblittable_1","title":"ApproximateIsBlittable","text":"public static bool ApproximateIsBlittable(Type type)\n
Returns true
if a type is blittable, else false
. This method uses an approximation, and may not work with generic types with blittable (unmanaged) constraints."},{"location":"Utilities/TypeInfo/#marshalledsizeof","title":"MarshalledSizeOf","text":"public static int MarshalledSizeOf<T>()\n
Returns size of item after marshalling is performed. This caches the value, thus is faster to access."},{"location":"Utilities/TypeInfo/#usage","title":"Usage","text":""},{"location":"Utilities/TypeInfo/#check-if-a-type-is-blittable-generic","title":"Check if a Type is Blittable (Generic)","text":"bool isBlittable = TypeInfo.ApproximateIsBlittable<int>();\n
"},{"location":"Utilities/TypeInfo/#check-if-a-type-is-blittable-non-generic","title":"Check if a Type is Blittable (Non-Generic)","text":"bool isBlittable = TypeInfo.ApproximateIsBlittable(typeof(int));\n
"},{"location":"Utilities/TypeInfo/#check-size-of-type-after-marshalling","title":"Check size of Type After Marshalling","text":"int size = TypeInfo.MarshalledSizeOf<int>();\n
"}]}
\ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
new file mode 100644
index 0000000..f5fd22b
--- /dev/null
+++ b/sitemap.xml
@@ -0,0 +1,173 @@
+
+