procmod-layout 1.0.1

Struct mapping with pointer chain traversal via derive macros
Documentation
<p align="center">
<img src="logo.svg" width="256" height="256" alt="procmod-layout">
</p>

<h1 align="center">procmod-layout</h1>

<p align="center">Struct mapping with pointer chain traversal via derive macros.</p>

---

Map remote process memory into Rust structs. Declare byte offsets on each field, optionally follow pointer chains through multiple indirections, and read the entire struct in one call. Built on [procmod-core](https://github.com/procmod/procmod-core).

## Install

```toml
[dependencies]
procmod-layout = "1"
```

## Quick start

Read a game's player state from a known base address:

```rust
use procmod_layout::{GameStruct, Process};

#[derive(GameStruct)]
struct Player {
    #[offset(0x100)]
    health: f32,
    #[offset(0x104)]
    max_health: f32,
    #[offset(0x108)]
    position: [f32; 3],
}

fn main() -> procmod_layout::Result<()> {
    let game = Process::attach(pid)?;
    let player = Player::read(&game, player_base)?;
    println!("hp: {}/{}", player.health, player.max_health);
    println!("pos: {:?}", player.position);
    Ok(())
}
```

## Usage

### Basic struct mapping

Every field needs an `#[offset(N)]` attribute specifying its byte offset from the base address. The derive macro generates a `read(process, base) -> Result<Self>` method.

```rust
use procmod_layout::{GameStruct, Process};

#[derive(GameStruct)]
struct GameSettings {
    #[offset(0x00)]
    difficulty: u32,
    #[offset(0x04)]
    volume: f32,
    #[offset(0x08)]
    fov: f32,
    #[offset(0x10)]
    mouse_sensitivity: f64,
}
```

### Pointer chains

When a value is behind one or more pointer indirections, use `#[pointer_chain(...)]` to follow the chain automatically. The offsets list the intermediate dereference offsets before reading the final value.

For example, reading a damage multiplier stored behind two pointer hops:

```rust
use procmod_layout::{GameStruct, Process};

#[derive(GameStruct)]
struct CombatState {
    #[offset(0x50)]
    is_attacking: u8,
    #[offset(0x54)]
    combo_count: u32,
    #[offset(0x60)]
    #[pointer_chain(0x10, 0x08)]
    damage_mult: f32,
}

// The pointer chain for damage_mult reads:
//   1. read pointer at (base + 0x60)
//   2. read pointer at (ptr + 0x10)
//   3. read f32 at (ptr + 0x08)
```

### Composing with procmod-scan

Use [procmod-scan](https://github.com/procmod/procmod-scan) to find a structure's base address after a game update, then read it with a layout:

```rust
use procmod_layout::{GameStruct, Process};
use procmod_scan::Pattern;

#[derive(GameStruct)]
struct Inventory {
    #[offset(0x00)]
    slot_count: u32,
    #[offset(0x04)]
    gold: u32,
    #[offset(0x10)]
    weight: f32,
}

fn find_inventory(process: &Process, module: &[u8], module_base: usize) -> procmod_layout::Result<Inventory> {
    let sig = Pattern::from_ida("48 8D 0D ? ? ? ? E8 ? ? ? ? 48 8B D8").unwrap();
    let offset = sig.scan_first(module).expect("inventory signature not found");
    let base = module_base + offset;
    Inventory::read(process, base)
}
```

## Supported types

All field types must be `Copy` and valid for any bit pattern. Types with validity invariants (`bool`, `char`, enums) must not be used - read them as their underlying integer type instead (e.g., `u8` for booleans).

- Numeric primitives: `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`, `f32`, `f64`, `usize`
- Fixed-size arrays: `[f32; 3]`, `[u8; 16]`, etc.
- Any `#[repr(C)]` struct that is `Copy` and valid for any bit pattern

## License

MIT