> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spurdoverse.app/llms.txt
> Use this file to discover all available pages before exploring further.

# FFI

> Load DLLs, call their exports, lay out C structs and pass JS callbacks to native code.

Load DLLs and call their exports.

```ts theme={null}
import ffi from "@native/ffi";
```

<Note>
  Needs the `ffi` [permission](/api/manifest#permissions). Addresses here are addresses in the cheat's
  own process: `bigint`, with `null` for NULL. Game memory is [Pointer](/api/types/pointer).
</Note>

## Overview

<CardGroup cols={2}>
  <Card title="Libraries" icon="book" href="#libraries">
    `open`, `func`, `funcAsync`, `funcs`, `symbol`, `dispose`.
  </Card>

  <Card title="Signatures" icon="code" href="#signatures">
    Type names, strings, out params, variadics.
  </Card>

  <Card title="Structs" icon="table-cells" href="#structs">
    `struct`, `union`, `typedef`, `sizeOf`, views.
  </Card>

  <Card title="Callbacks" icon="reply" href="#callbacks">
    `callback`.
  </Card>

  <Card title="Memory helpers" icon="memory" href="#memory-helpers">
    `alloc`, `free`, `readString`, `lastError`, …
  </Card>
</CardGroup>

***

## Libraries

### open

<br />

```ts theme={null}
ffi.open(path: string): Library
```

Loads a DLL. Bare names use the normal DLL search order. Throws `ENOENT` when it isn't found.

```ts theme={null}
const k32 = ffi.open("kernel32.dll");   // loaded until k32.dispose() or unload
```

### func

<br />

```ts theme={null}
lib.func<F>(signature: string): F
lib.func<F>(name: string, returns: CType, params?: CType[]): F
```

Binds an export as a JS function. Throws `ENOENT` for a missing export. The type parameter only types
the result; the signature decides the conversion. A crash inside the native call throws `EFAULT`.

```ts theme={null}
const beep = k32.func<(freq: number, ms: number) => boolean>("BOOL Beep(DWORD dwFreq, DWORD dwDuration)");
beep(440, 200);

const sleep = k32.func<(ms: number) => void>("Sleep", "void", ["DWORD"]);
```

### funcAsync

<br />

```ts theme={null}
lib.funcAsync<F>(signature: string): (...args) => Promise<ReturnType<F>>
lib.funcAsync<F>(name: string, returns: CType, params?: CType[]): (...args) => Promise<ReturnType<F>>
```

Like `func`, but each call runs in the background and returns a Promise. `_Out_` holders are filled
before it resolves. `ffi.lastError()` doesn't see async calls.

```ts theme={null}
const sleepAsync = k32.funcAsync<(ms: number) => void>("void Sleep(DWORD)");
await sleepAsync(1000);   // the script keeps running
```

### funcs

<br />

```ts theme={null}
lib.funcs<T>(defs: { [K in keyof T]: string }): T
lib.funcsAsync<T>(defs: { [K in keyof T]: string }): { [K in keyof T]: AsyncNativeFunction<T[K]> }
```

Binds several exports at once. The export name is the one in the signature, or the key when the signature
has none. Throws on the first one that fails.

```ts theme={null}
const time = k32.funcs<{ Sleep(ms: number): void; GetTickCount(): number }>({
    Sleep: "void(DWORD)",
    GetTickCount: "DWORD GetTickCount(void)",
});
```

### symbol

<br />

```ts theme={null}
lib.symbol(name: string): bigint | null
```

The address of an export, or `null` when there isn't one.

```ts theme={null}
const addr = k32.symbol("GetProcAddress");
```

### dispose

<br />

```ts theme={null}
lib.dispose(): void
```

Unloads the library. Functions bound from it throw `ESTALE` afterwards. `using` does it at the end of
the block.

```ts theme={null}
function tickCount(): number {
    using k32 = ffi.open("kernel32.dll");
    return k32.func<() => number>("DWORD GetTickCount(void)")();
}
```

| Property | Type | |
| :- | :- | :- |
| `path` | `string` | as passed to `open` |
| `alive` | `boolean` | `false` once disposed |

***

## Signatures

```
<return type> [WINAPI] <Name>(<param>, ...)
param:  [_In_ | _Out_ | _Inout_] <type> [name]
```

Parameter names are optional. Calling conventions are accepted and ignored.

| Type names | JS value |
| :- | :- |
| `int8` … `uint32`, `int`, `long`, `BYTE`, `WORD`, `DWORD`, `HRESULT`, `float`, `double` | `number` |
| `int64`, `uint64`, `size_t`, `INT_PTR`, `LPARAM`, `WPARAM`, … | `bigint` (a safe-integer `number` is accepted as input) |
| `bool` (1 byte), `BOOL` / `bool32` (4 bytes) | `boolean` |
| `char*`, `const char*`, `LPCSTR`, `string` | `string`, returns `string \| null` |
| `wchar_t*`, `LPCWSTR`, `wstring` | same, UTF-16 |
| `void*`, `HANDLE`, `HWND`, `ptr`, any other `T*` | `bigint \| null` |
| `vector2`, `vector`, `qangle`, `quaternion`, `color` | the value class |

`long` is 32-bit (Windows). `const`, `struct X`, `enum X` (an `int`), the Win32 pointer aliases
(`LPDWORD`, `PHANDLE`, …) and your own `typedef`/struct names work too. Unknown names throw `TypeError`.

**Strings.** A JS string passed to a string parameter is only valid during the call. String parameters
also take a buffer the function writes into. Numbers, bigints and booleans aren't coerced.

**Pointer arguments** take a `bigint`, `null`, a `BufferSource` or struct view, a `Pointer` or a `Callback`.

### Out parameters

`_Out_` / `_Inout_` pointers to a scalar or pointer take a `{ value }` holder. `_Inout_` sends `value`,
both set it after the call. A `T*` to a struct also takes a plain object; with `_Out_` / `_Inout_` its
fields are written back.

```ts theme={null}
import ffi, { type OutParam } from "@native/ffi";

const k32 = ffi.open("kernel32.dll");
const getCurrentProcess = k32.func<() => bigint | null>("HANDLE GetCurrentProcess(void)");
const getExitCode = k32.func("BOOL GetExitCodeProcess(HANDLE, _Out_ DWORD*)");

const code: OutParam<number> = { value: 0 };
getExitCode(getCurrentProcess(), code);   // code.value = 259 (STILL_ACTIVE)

ffi.struct("POINT", { x: "int32", y: "int32" });
const getCursorPos = ffi.open("user32.dll").func("BOOL GetCursorPos(_Out_ POINT*)");
const cursor = { x: 0, y: 0 };
getCursorPos(cursor);                     // cursor.x, cursor.y filled in
```

### Variadics

A trailing `...` makes the function variadic. Pass each extra argument as a `(typeName, value)` pair;
`float` is promoted to `double`.

```ts theme={null}
const wsprintf = user32.func<(out: Uint8Array, fmt: string, ...rest: unknown[]) => number>(
    "int wsprintfA(LPSTR, LPCSTR, ...)");

const buf = new Uint8Array(64);
wsprintf(buf, "%d/%s", "int", 42, "const char*", "hi");
ffi.readString(buf);   // "42/hi"
```

### Structs by value

A struct name without `*` passes the struct by value. A by-value return is a new [struct view](#views).

```ts theme={null}
const POINT = ffi.struct("POINT", { x: "int32", y: "int32" });
const RECT = ffi.struct("RECT", { left: "int32", top: "int32", right: "int32", bottom: "int32" });
const ptInRect = user32.func<(rc: object, pt: object) => boolean>("BOOL PtInRect(const RECT*, POINT)");

ptInRect(RECT.from({ right: 100, bottom: 100 }), { x: 50, y: 50 });   // true
```

***

## Structs

### struct

<br />

```ts theme={null}
ffi.struct(name: string, fields: FieldDefs): StructType
ffi.struct(fields: FieldDefs): StructType
```

Defines a C struct, laid out in field order with C alignment. A named struct can then be used by name
in this script's signatures, fields and typedefs.

```ts theme={null}
const Vec3 = ffi.struct("Vec3", { x: "float", y: "float", z: "float" });
const Player = ffi.struct("Player", {
    hp: "int32",
    pos: Vec3,             // nested struct: a view
    name: "char[32]",      // inline text: a string
    ammo: "uint16[4]",     // a live Uint16Array
    owner: "ptr",          // bigint | null
});
```

In struct fields `char*` is a raw pointer, never a string; inline text is `char[N]` (UTF-8) or
`wchar_t[N]` (UTF-16). Numeric arrays are typed arrays, pointer arrays a `BigUint64Array`.

The same `StructType` objects come from [memory.struct](/api/memory) without the `ffi` permission,
for reading game memory.

### union, typedef, sizeOf

| Function | |
| :- | :- |
| `ffi.union(name?, fields): StructType` | all fields at offset 0, `kind: "union"` |
| `ffi.typedef(alias, type): void` | names a type for this script |
| `ffi.sizeOf(type): number` | size in bytes, `"T[N]"` included |

```ts theme={null}
const IntOrFloat = ffi.union("IntOrFloat", { i: "int32", f: "float" });
ffi.typedef("PPOINT", "POINT*");
ffi.sizeOf("wchar_t[260]");   // 520
```

### StructType

| Member | |
| :- | :- |
| `name` | `string \| null` (anonymous) |
| `kind` | `"struct"` or `"union"` |
| `size`, `alignment` | bytes |
| `alloc()` | a zeroed struct in a new buffer |
| `from(init?)` | a new struct from any subset of fields (the rest are zero), or a copy of a view |
| `view(source, offset?)` | a live view over existing memory, not a copy |
| `decode(source, offset?)` | a plain-object copy |
| `array(count)` | `count` zeroed structs in one buffer |
| `offsetOf(field)` | byte offset, or `null` for an unknown field |

`view` and `decode` take a `bigint` address, a `BufferSource` or another view. `offset` must be a
multiple of `alignment`.

```ts theme={null}
const Point = ffi.struct("Point", { x: "int32", y: "int32" });

const p = Point.from({ x: 10, y: 20 });
const pts = Point.array(16);
pts[0].x = 1;

const header = Point.view(new Uint8Array(64), 8);
```

<Warning>
  A `bigint` address is trusted as is: `view(address)` doesn't copy, so the memory must stay valid
  while you use the view.
</Warning>

### Views

Fields of a view read and write the memory directly. Besides its fields a view has:

| Property | |
| :- | :- |
| `$buffer` | the `ArrayBuffer` behind it (shared by all views of one `array()`) |
| `$offset` | byte offset inside `$buffer` |
| `$size` | size of the struct |
| `$address` | address of the first byte |

***

## Callbacks

### callback

<br />

```ts theme={null}
ffi.callback(signature: string, fn: (...args) => unknown): Callback
ffi.callback(returns: CType, params: CType[], fn: (...args) => unknown): Callback
```

Exposes `fn` as a native function pointer. Pass the `Callback` (or its `address`) wherever a function
pointer goes. String arguments arrive as `string | null`. A callback can't return a string (return
`ptr`) and can't be variadic.

```ts theme={null}
const enumWindows = user32.func("BOOL EnumWindows(void*, LPARAM)");

function listWindows(): (bigint | null)[] {
    const windows: (bigint | null)[] = [];
    using proc = ffi.callback("BOOL(HWND hwnd, LPARAM lParam)", (hwnd: bigint | null) => {
        windows.push(hwnd);
        return true;
    });
    enumWindows(proc, 0n);   // calls back synchronously, so `using` is safe here
    return windows;
}
```

| Property | |
| :- | :- |
| `address` | `bigint`, `null` once disposed |
| `alive` | `false` once disposed |
| `dispose()` | releases `fn`; native code must not call the pointer afterwards |

If `fn` throws, native code gets 0 and the error goes to the global `error` event.

<Warning>
  `fn` only runs synchronously when native code calls it during one of your own synchronous FFI calls
  (like `EnumWindows`). Called any other time, such as from another thread, a `void` callback runs on the
  next tick and any other callback returns 0.
</Warning>

***

## Memory helpers

| Function | |
| :- | :- |
| `ffi.alloc(size): ArrayBuffer` | zeroed, garbage-collected |
| `ffi.free(ptr: bigint \| null): void` | frees memory a library allocated with `malloc` |
| `ffi.addressOf(value): bigint \| null` | address of a buffer's or view's first byte, `null` when empty; valid while the buffer lives |
| `ffi.readString(ptr, maxBytes?): string \| null` | NUL-terminated UTF-8; `null` for NULL |
| `ffi.readWString(ptr, maxUnits?): string \| null` | same for UTF-16 |
| `ffi.writeString(buffer, text): number` | writes NUL-terminated UTF-8, returns bytes written without the NUL; `RangeError` if it doesn't fit |
| `ffi.lastError(): number` | `GetLastError()` after the last synchronous FFI call |

```ts theme={null}
const buf = ffi.alloc(260);
ffi.writeString(buf, "hello");
ffi.readString(buf);   // "hello"

const getModule = k32.func<(name: string) => bigint | null>("HMODULE GetModuleHandleA(LPCSTR)");
if (getModule("nope.dll") === null) console.log("error", ffi.lastError());   // 126
```

***

## Example

Titles of all visible top-level windows.

```ts theme={null}
import ffi, { type Callback } from "@native/ffi";

const user32 = ffi.open("user32.dll");
const api = user32.funcs<{
    EnumWindows(proc: Callback, lParam: bigint): boolean;
    IsWindowVisible(hwnd: bigint | null): boolean;
    GetWindowTextW(hwnd: bigint | null, buf: Uint16Array, max: number): number;
}>({
    EnumWindows: "BOOL(void*, LPARAM)",
    IsWindowVisible: "BOOL(HWND)",
    GetWindowTextW: "int(HWND, LPWSTR, int)",
});

function windowTitles(): string[] {
    const titles: string[] = [];
    const text = new Uint16Array(256);
    using proc = ffi.callback("BOOL(HWND, LPARAM)", (hwnd: bigint | null) => {
        if (api.IsWindowVisible(hwnd) && api.GetWindowTextW(hwnd, text, text.length) > 0)
            titles.push(ffi.readWString(text) ?? "");
        return true;
    });
    api.EnumWindows(proc, 0n);
    return titles;
}

console.log(windowTitles());
```
