# Table of Contents - [Enma - Overview | Perception - Enma | Perception AngelScript & Lua API](#enma-overview-perception-enma-perception-angelscript-lua-api) - [Render API | Perception - Enma | Perception AngelScript & Lua API](#render-api-perception-enma-perception-angelscript-lua-api) - [Lifecycle and Routines | Perception - Enma | Perception AngelScript & Lua API](#lifecycle-and-routines-perception-enma-perception-angelscript-lua-api) - [Proc API | Perception - Enma | Perception AngelScript & Lua API](#proc-api-perception-enma-perception-angelscript-lua-api) - [Net API | Perception - Enma | Perception AngelScript & Lua API](#net-api-perception-enma-perception-angelscript-lua-api) - [Input API | Perception - Enma | Perception AngelScript & Lua API](#input-api-perception-enma-perception-angelscript-lua-api) - [Filesystem API | Perception - Enma | Perception AngelScript & Lua API](#filesystem-api-perception-enma-perception-angelscript-lua-api) - [GUI API | Perception - Enma | Perception AngelScript & Lua API](#gui-api-perception-enma-perception-angelscript-lua-api) - [CPU API | Perception - Enma | Perception AngelScript & Lua API](#cpu-api-perception-enma-perception-angelscript-lua-api) - [Sound API | Perception - Enma | Perception AngelScript & Lua API](#sound-api-perception-enma-perception-angelscript-lua-api) - [Win API | Perception - Enma | Perception AngelScript & Lua API](#win-api-perception-enma-perception-angelscript-lua-api) - [Unicorn API | Perception - Enma | Perception AngelScript & Lua API](#unicorn-api-perception-enma-perception-angelscript-lua-api) - [Zydis API | Perception - Enma | Perception AngelScript & Lua API](#zydis-api-perception-enma-perception-angelscript-lua-api) - [MCP API | Perception - Enma | Perception AngelScript & Lua API](#mcp-api-perception-enma-perception-angelscript-lua-api) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) --- # Enma - Overview | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/readme.md) . Enma is perception's proprietary full-module AOT and JIT-compiled scripting language. This site covers the APIs perception registers on top of Enma. For the language itself see [enma docs](https://enma-1.gitbook.io/enma) . [](https://docs.perception.cx/perception/enma#whats-registered) What's registered -------------------------------------------------------------------------------------- #### [](https://docs.perception.cx/perception/enma#enma-pre-shipped) Enma Pre-Shipped * [Core](https://enma-1.gitbook.io/enma/addons/core) * [String](https://enma-1.gitbook.io/enma/addons/strings) * [Arrays](https://enma-1.gitbook.io/enma/addons/arrays) * [Maps](https://enma-1.gitbook.io/enma/addons/maps) * [Math](https://enma-1.gitbook.io/enma/addons/math) * [3D Math (quat + mat4)](https://enma-1.gitbook.io/enma/addons/math3d) * [SIMD](https://enma-1.gitbook.io/enma/addons/simd) * [Variant](https://enma-1.gitbook.io/enma/addons/variant) * [Atomic](https://enma-1.gitbook.io/enma/addons/atomic) * [Bits](https://enma-1.gitbook.io/enma/addons/bits) * [Time](https://enma-1.gitbook.io/enma/addons/time) * [Regex](https://enma-1.gitbook.io/enma/addons/regex) * [Thread](https://enma-1.gitbook.io/enma/addons/thread) * [Vectors](https://enma-1.gitbook.io/enma/addons/vec) * [Hash Set](https://enma-1.gitbook.io/enma/addons/hash_set) * [Sorted Map](https://enma-1.gitbook.io/enma/addons/sorted_map) * [List](https://enma-1.gitbook.io/enma/addons/list) * [JSON](https://enma-1.gitbook.io/enma/addons/json) #### [](https://docs.perception.cx/perception/enma#perception-api) **Perception API** * [Lifecycle and Routines](https://docs.perception.cx/perception/enma/lifecycle-and-routines) * [Render](https://docs.perception.cx/perception/enma/render-api) * [Proc](https://docs.perception.cx/perception/enma/proc-api) * [CPU](https://docs.perception.cx/perception/enma/cpu-api) * [Filesystem](https://docs.perception.cx/perception/enma/filesystem-api) * [Sound](https://docs.perception.cx/perception/enma/sound-api) * [Zydis](https://docs.perception.cx/perception/enma/zydis-api) * [Win](https://docs.perception.cx/perception/enma/win-api) * [Input](https://docs.perception.cx/perception/enma/input-api) * [Unicorn](https://docs.perception.cx/perception/enma/unicorn-api) * [Net](https://docs.perception.cx/perception/enma/net-api) * [GUI](https://docs.perception.cx/perception/enma/gui-api) #### [](https://docs.perception.cx/perception/enma#ai-agent-surface) AI agent surface * [MCP](https://docs.perception.cx/perception/enma/mcp-api) — JSON-RPC over local TCP / HTTP for Claude Code, Cline, etc. [](https://docs.perception.cx/perception/enma#minimal-example) Minimal example ----------------------------------------------------------------------------------- Copy int64 g_tick; void my_draw(int64 data) { g_tick = g_tick + 1; color white = color(255, 255, 255, 255); color noeffect = color(0, 0, 0, 0); string text = "tick=" + cast(g_tick); draw_text(text, vec2(40.0, 40.0), white, get_font20(), 0, noeffect, 0.0); } int64 main() { g_tick = 0; register_routine(cast(my_draw), 0); return 1; } See [Lifecycle and Routines](https://docs.perception.cx/perception/enma/lifecycle-and-routines) for the entry point, return-value semantics, and how routines tick. [](https://docs.perception.cx/perception/enma#conventions) Conventions --------------------------------------------------------------------------- * **Colors and positions**: always wrap. `color(255, 255, 255, 255)`, `vec2(10.0, 20.0)`. Freshly constructed each frame is fine; Enma drops the temporaries at scope exit. * **Float32 literals**: `0.2f`, not `cast(0.2)`. Required for vertex buffers. * **Handles**: all `create_*` / `load_*` natives return an encrypted `int64`. Pass it back into draw / bind / destroy. Don't inspect. [](https://docs.perception.cx/perception/enma#sdk) SDK ----------------------------------------------------------- Perception's Enma SDK is not public yet. [NextLifecycle and Routines](https://docs.perception.cx/perception/enma/lifecycle-and-routines) Last updated 23 days ago --- # Render API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/render-api.md) . All render natives are auto-registered into every loaded script. Handles (`int64`) are encrypted pointers. Pass them back into other render calls. Don't dereference or arithmetic them. [](https://docs.perception.cx/perception/enma/render-api#color-type) `color` type -------------------------------------------------------------------------------------- `color` is a source-level module. Opt in with `import "color";`: Copy import "vec"; import "color"; int64 main() { color red = color(255, 0, 0, 255); draw_rect_filled(vec2(10.0, 10.0), vec2(100.0, 50.0), red, 4.0, 15); return 0; } `color` is a `[[packed]]` 4-byte struct with `r` / `g` / `b` / `a` fields plus `with_alpha(uint8 _a)` to copy with a different alpha. Non-escaping locals are stack-allocated; the byte layout matches the native `pixelcolor4` so every `draw_*` call reads them directly. Read fields directly: `c.r`, `c.g`, `c.b`, `c.a` (each returns `uint8`). [](https://docs.perception.cx/perception/enma/render-api#id-2d-primitives) 2D primitives --------------------------------------------------------------------------------------------- Copy int64 draw_rect(vec2 pos, vec2 size, color c, float64 thickness, float64 rounding, uint8 rounding_flags); int64 draw_rect_filled(vec2 pos, vec2 size, color c, float64 rounding, uint8 rounding_flags); int64 draw_line(vec2 a, vec2 b, color c, float64 thickness); int64 draw_circle(vec2 center, float64 radius, color c, float64 thickness, bool filled); int64 draw_arc(vec2 center, vec2 radii, float64 start_deg, float64 sweep_deg, color c, float64 thickness, bool filled); int64 draw_triangle(vec2 a, vec2 b, vec2 c, color col, float64 thickness, bool filled); int64 draw_four_corner_gradient(vec2 pos, vec2 size, color tl, color tr, color bl, color br, float64 rounding); int64 draw_polygon(array xy_pairs, uint32 count_pairs, color c, float64 thickness, bool filled); int64 draw_bitmap(int64 bmp, vec2 pos, vec2 size, color tint, bool rounded); int64 draw_text(string text, vec2 pos, color c, int64 font, int32 effect, color effect_color, float64 effect_amount); `effect`: 0=none, 1=shadow, 2=outline. `rounding_flags`: bitmask of which corners to round (ImGui-style, `15` = all corners). [](https://docs.perception.cx/perception/enma/render-api#text-and-fonts) Text and fonts -------------------------------------------------------------------------------------------- Copy float64 get_text_width(int64 font, string text, int32 maxw, int32 maxh); float64 get_text_height(int64 font, string text, int32 maxw, int32 maxh); int32 get_char_advance(int64 font, uint32 wchar32); int64 create_font(string path, float64 size, bool antialias, bool load_color, array glyph_ranges); int64 create_font_mem(string label, float64 size, array buf, bool antialias, bool load_color, array glyph_ranges); int64 create_bitmap(array data); int64 get_font18(); int64 get_font20(); int64 get_font24(); int64 get_font28(); `create_font` first tries the path as-is, then retries under perception's main dir. `glyph_ranges` may be an empty array. [](https://docs.perception.cx/perception/enma/render-api#clipping) Clipping -------------------------------------------------------------------------------- Copy int64 clip_push(vec2 pos, vec2 size); int64 clip_pop(); [](https://docs.perception.cx/perception/enma/render-api#viewport) Viewport -------------------------------------------------------------------------------- Copy float64 get_view_width(); float64 get_view_height(); float64 get_view_scale(); float64 get_fps(); [](https://docs.perception.cx/perception/enma/render-api#shaders) Shaders ------------------------------------------------------------------------------ Copy int64 create_shader(string vs_source, string ps_source, string layout); int64 destroy_shader(int64 shader); int64 create_compute_shader(string cs_source); int64 destroy_compute_shader(int64 cs); Layout format: `"SEMANTIC:INDEX:TYPE, ..."`. Example: `"POSITION:0:FLOAT2, COLOR:0:FLOAT4"`. Types: `FLOAT1`, `FLOAT2`, `FLOAT3`, `FLOAT4`, `BYTE4` (unorm), `UINT1`. [](https://docs.perception.cx/perception/enma/render-api#buffers) Buffers ------------------------------------------------------------------------------ Copy int64 create_vertex_buffer(uint32 stride, uint32 max_vertices, bool dynamic); int64 destroy_vertex_buffer(int64 vb); int64 create_index_buffer(uint32 max_indices, bool use_32bit, bool dynamic); int64 destroy_index_buffer(int64 ib); int64 create_constant_buffer(uint32 size); int64 destroy_constant_buffer(int64 cb); int64 create_structured_buffer(uint32 element_size, uint32 element_count, bool cpu_write, bool gpu_write); int64 destroy_structured_buffer(int64 sb); [](https://docs.perception.cx/perception/enma/render-api#pipeline-state) Pipeline state -------------------------------------------------------------------------------------------- Copy int64 create_blend_state(int32 src, int32 dst, int32 op, int32 src_alpha, int32 dst_alpha, int32 op_alpha); int64 destroy_blend_state(int64 bs); int64 create_sampler(int32 filter, int32 address_u, int32 address_v); int64 destroy_sampler(int64 s); int64 create_depth_stencil_state(bool depth_enable, bool depth_write, int32 compare_func); int64 destroy_depth_stencil_state(int64 ds); int64 create_rasterizer_state(int32 cull_mode, int32 fill_mode, bool scissor_enable); int64 destroy_rasterizer_state(int64 rs); Enum values (all `int32`): * `blend_factor`: 0=ZERO, 1=ONE, 2=SRC\_ALPHA, 3=INV\_SRC\_ALPHA, 4=DEST\_ALPHA, 5=INV\_DEST\_ALPHA, 6=SRC\_COLOR, 7=INV\_SRC\_COLOR, 8=DEST\_COLOR, 9=INV\_DEST\_COLOR. * `blend_op`: 0=ADD, 1=SUBTRACT, 2=REV\_SUBTRACT, 3=MIN, 4=MAX. * `filter`: 0=POINT, 1=LINEAR, 2=ANISOTROPIC. * `address`: 0=WRAP, 1=CLAMP, 2=MIRROR, 3=BORDER. * `compare_func`: 0=NEVER, 1=LESS, 2=EQUAL, 3=LESS\_EQUAL, 4=GREATER, 5=NOT\_EQUAL, 6=GREATER\_EQUAL, 7=ALWAYS. [](https://docs.perception.cx/perception/enma/render-api#render-targets-and-textures) Render targets and textures ---------------------------------------------------------------------------------------------------------------------- Copy int64 create_render_target(uint32 width, uint32 height); int64 destroy_render_target(int64 rt); int64 create_depth_buffer(uint32 width, uint32 height); int64 destroy_depth_buffer(int64 db); int64 create_texture(uint32 width, uint32 height, array rgba_data); int64 destroy_texture(int64 tex); int64 load_texture(string path); int64 load_texture_mem(array data); float64 get_texture_width(int64 tex); float64 get_texture_height(int64 tex); `create_texture` wants `width * height * 4` bytes of RGBA. [](https://docs.perception.cx/perception/enma/render-api#meshes) Meshes ---------------------------------------------------------------------------- Copy int64 create_mesh_raw(array vertex_data, uint32 vertex_count, uint32 stride, array index_data, uint32 index_count, bool use_32bit); int64 load_mesh(string path); int64 load_mesh_mem(array data); int64 destroy_mesh(int64 mesh); int64 get_mesh_vert_count(int64 mesh); int64 get_mesh_index_count(int64 mesh); float64 get_mesh_stride(int64 mesh); float64 get_mesh_bounds_min_x(int64 mesh); float64 get_mesh_bounds_min_y(int64 mesh); float64 get_mesh_bounds_min_z(int64 mesh); float64 get_mesh_bounds_max_x(int64 mesh); float64 get_mesh_bounds_max_y(int64 mesh); float64 get_mesh_bounds_max_z(int64 mesh); [](https://docs.perception.cx/perception/enma/render-api#custom-draw) Custom draw -------------------------------------------------------------------------------------- Copy int64 custom_draw(int64 shader, int64 vb, array vertex_data, uint32 vertex_count, int32 topology, int64 blend, int64 sampler, int64 texture, int32 tex_slot, int64 cb, array cb_data, int32 cb_slot); int64 custom_draw_indexed(int64 shader, int64 vb, array vertex_data, uint32 vertex_count, int64 ib, array index_data, uint32 index_count, int32 topology, int64 blend, int64 sampler, int64 texture, int32 tex_slot, int64 cb, array cb_data, int32 cb_slot); int64 draw_mesh(int64 mesh, int64 shader, int32 topology, int64 blend, int64 sampler, int64 texture, int32 tex_slot, int64 cb, array cb_data, int32 cb_slot); int64 dispatch_compute(int64 cs, uint32 x, uint32 y, uint32 z); `topology`: 0=TRIANGLE\_LIST, 1=TRIANGLE\_STRIP, 2=LINE\_LIST, 3=LINE\_STRIP, 4=POINT\_LIST. Any of `blend` / `sampler` / `texture` / `cb` can be `0` to skip binding. `cb_data` may be an empty array. [](https://docs.perception.cx/perception/enma/render-api#binding-and-state) Binding and state -------------------------------------------------------------------------------------------------- Copy int64 custom_set_render_target(int64 rt); int64 custom_set_render_target_ext(int64 rt, int64 depth_buffer); int64 custom_reset_render_target(); int64 custom_bind_rt_as_texture(int64 rt, int32 slot); int64 custom_restore_state(); int64 custom_set_depth_stencil_state(int64 ds); int64 custom_set_rasterizer_state(int64 rs); int64 custom_set_viewport(float64 x, float64 y, float64 w, float64 h); int64 custom_reset_viewport(); int64 custom_bind_texture(int64 texture, int64 sampler, int32 slot); int64 custom_bind_constant_buffer(int64 cb, array data, int32 slot, int32 stage); int64 custom_update_texture(int64 tex, uint32 x, uint32 y, uint32 w, uint32 h, array rgba_data); int64 custom_clear_render_target(int64 rt, float64 r, float64 g, float64 b, float64 a); int64 custom_clear_depth_buffer(int64 db); int64 bind_structured_buffer(int64 sb, int32 slot, int32 stage); int64 update_structured_buffer(int64 sb, array data); int64 capture_backbuffer(int32 slot); `stage`: 0=VS, 1=PS, 2=CS (matches D3D11 shader stages). Call `custom_restore_state()` after any custom-pipeline sequence before returning control to the 2D layer. [](https://docs.perception.cx/perception/enma/render-api#minimal-triangle) Minimal triangle ------------------------------------------------------------------------------------------------ Copy int64 g_shader; int64 g_vb; int64 main() { string vs = "struct VSIn { float2 pos : POSITION; float4 color : COLOR; };\nstruct VSOut { float4 pos : SV_Position; float4 color : COLOR; };\nVSOut main(VSIn i) { VSOut o; o.pos = float4(i.pos, 0.0, 1.0); o.color = i.color; return o; }\n"; string ps = "struct VSOut { float4 pos : SV_Position; float4 color : COLOR; };\nfloat4 main(VSOut i) : SV_Target { return i.color; }\n"; g_shader = create_shader(vs, ps, "POSITION:0:FLOAT2, COLOR:0:FLOAT4"); g_vb = create_vertex_buffer(24, 3, true); // 2*4 + 4*4 = 24 bytes per vertex register_routine(cast(my_draw), 0); return 1; } void my_draw(int64 data) { float32[] verts; // vertex 0: pos(-0.5, -0.5) color(1, 0, 0, 1) verts.push(-0.5f); verts.push(-0.5f); verts.push(1.0f); verts.push(0.0f); verts.push(0.0f); verts.push(1.0f); // vertex 1: pos(0.5, -0.5) color(0, 1, 0, 1) verts.push(0.5f); verts.push(-0.5f); verts.push(0.0f); verts.push(1.0f); verts.push(0.0f); verts.push(1.0f); // vertex 2: pos(0, 0.5) color(0, 0, 1, 1) verts.push(0.0f); verts.push(0.5f); verts.push(0.0f); verts.push(0.0f); verts.push(1.0f); verts.push(1.0f); float32[] no_cb; custom_draw(g_shader, g_vb, verts, 3, 0, 0, 0, 0, 0, 0, no_cb, 0); } [](https://docs.perception.cx/perception/enma/render-api#cleanup) Cleanup ------------------------------------------------------------------------------ On script unload, every handle returned by `create_*` / `load_*` is destroyed automatically. Explicit `destroy_*` is optional and only needed if you want to free a resource mid-script. [PreviousNet API](https://docs.perception.cx/perception/enma/net-api) [NextSound API](https://docs.perception.cx/perception/enma/sound-api) Last updated 23 days ago --- # Lifecycle and Routines | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/lifecycle-and-routines.md) . [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#entry-point) Entry point -------------------------------------------------------------------------------------------------- Every script needs a `main()` function. It runs once when the script is loaded. Copy int64 main() { // setup state, load resources, register routines return 1; } `main()`'s return value decides what happens next: Return Behavior `> 0` Script stays loaded. `<= 0` Script unloads immediately after `main()` returns. Use `return 1;` for any normal long-lived script. Return `0` for one-shot scripts that just wanted to do work in `main()` and exit. [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#routines) Routines -------------------------------------------------------------------------------------------- A routine is a script function that runs continuously after `main()` returns. Routines are how your script keeps doing work over time. Copy int64 register_routine(int64 fn_handle, int64 data); bool unregister_routine(int64 routine_handle); ### [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#callback-shape) Callback shape The function you register takes one `int64` parameter: Copy void my_callback(int64 data) { // data: the value you passed as the second arg to register_routine } Pass the function as a closure handle via `cast(fn_name)`: Copy int64 main() { int64 r = register_routine(cast(my_callback), 42); return 1; } `register_routine` returns a handle. Keep it if you intend to unregister later, or discard. ### [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#multiple-routines) Multiple routines Register as many as you need. Copy void on_render(int64 data) { /* draw */ } void on_tick(int64 data) { /* update logic */ } int64 main() { register_routine(cast(on_render), 0); register_routine(cast(on_tick), 0); return 1; } ### [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#unregistering) Unregistering Copy unregister_routine(my_handle); A routine can also unregister itself from inside its own callback: Copy int64 g_handle; void my_callback(int64 data) { if (should_stop()) { unregister_routine(g_handle); return; } // normal work } int64 main() { g_handle = register_routine(cast(my_callback), 0); return 1; } [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#unload) Unload ---------------------------------------------------------------------------------------- A script unloads when `main()` returns `<= 0`, when the user unloads it from the UI, or when the host shuts down. On unload all routines stop and any GPU resources you created via the render API are destroyed automatically. [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#exceptions) Exceptions ------------------------------------------------------------------------------------------------ Routines automatically catch uncaught throws and faults. The error is logged to `\exceptions\enma.log` with a timestamp, the routine id, the thrown value, and the source line where it happened. The script keeps running. [](https://docs.perception.cx/perception/enma/lifecycle-and-routines#diagnostic-helpers) Diagnostic helpers ---------------------------------------------------------------------------------------------------------------- Quick tracing without touching the renderer: Copy void heartbeat(); // log "heartbeat called" void take_int(int64 x); // log an int value void take_ptr(int64 p); // log a pointer in hex void test_3arg(int64 a, int64 b, int64 c); // log three ints Useful for confirming a code path is reached or sanity-checking a value. [PreviousEnma - Overview](https://docs.perception.cx/perception/enma) [NextProc API](https://docs.perception.cx/perception/enma/proc-api) Last updated 1 month ago --- # Proc API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/proc-api.md) . All proc natives are auto-registered into every loaded script. `proc_t` is a value-type handle. Construct it via `ref_process(...)`; the host ref is released automatically when the variable goes out of scope. Some natives are gated by permission flags toggled host-side. Gated calls log and return 0 / false when blocked. See [Permissions](https://docs.perception.cx/perception/enma/proc-api#permissions) . **Address type:** all addresses are `uint64`. Pick `uint64` for any locals that hold an address — `uint64 base = p.base_address();` — and the rest of the chain stays cast-free. Mixing `int64` addresses requires `cast(...)`. [](https://docs.perception.cx/perception/enma/proc-api#proc_t) `proc_t` ---------------------------------------------------------------------------- Copy proc_t ref_process(uint32 pid); proc_t ref_process(string name); Returns an alive handle on success, a null one on failure. Verify with `.alive()`. [](https://docs.perception.cx/perception/enma/proc-api#identity) Identity ------------------------------------------------------------------------------ Copy uint64 proc.base_address(); uint64 proc.peb(); uint32 proc.pid(); bool proc.alive(); bool proc.is_valid_address(uint64 addr); uint64 proc.get_eprocess(); // gated: kernel_rw_access — see below `get_eprocess` returns the target's EPROCESS kernel address. Gated behind the `kernel_rw_access` permission — returns `0` and logs when the script doesn't hold it. Use cases: passing the EPROCESS to a custom kernel routine, walking kernel structures the proc API doesn't already expose, etc. [](https://docs.perception.cx/perception/enma/proc-api#read-primitives) Read primitives -------------------------------------------------------------------------------------------- Copy uint8/16/32/64 proc.ru8/ru16/ru32/ru64(uint64 addr); int8/16/32/64 proc.r8/r16/r32/r64 (uint64 addr); float32 proc.rf32(uint64 addr); float64 proc.rf64(uint64 addr); string proc.rs (uint64 addr, int32 max_chars); // null-terminated UTF-8, cap 8192 string proc.rws(uint64 addr, int32 max_chars); // UTF-16, returns UTF-8, cap 8192 All return 0 / empty on failure or out-of-range address. By default, addresses must be usermode. When the script holds `kernel_rw_access`, _safe_ kernel addresses are also accepted — see [Permissions](https://docs.perception.cx/perception/enma/proc-api#permissions) . [](https://docs.perception.cx/perception/enma/proc-api#write-primitives-gated-write_memory) Write primitives (gated: `write_memory`) ----------------------------------------------------------------------------------------------------------------------------------------- Copy bool proc.wu8/wu16/wu32/wu64(uint64 addr, uintN v); bool proc.w8/w16/w32/w64 (uint64 addr, intN v); bool proc.wf32(uint64 addr, float32 v); bool proc.wf64(uint64 addr, float64 v); bool proc.ws (uint64 addr, string text); // UTF-8 bytes bool proc.wws(uint64 addr, string text); // converts UTF-8 to UTF-16 [](https://docs.perception.cx/perception/enma/proc-api#bulk-read-write) Bulk read/write -------------------------------------------------------------------------------------------- Copy array proc.rvm(uint64 addr, uint64 size); // length = bytes actually read bool proc.wvm(uint64 addr, array bytes); // gated: write_memory [](https://docs.perception.cx/perception/enma/proc-api#typed-reads-writes-vec-quat-mat) Typed reads / writes (vec / quat / mat) ------------------------------------------------------------------------------------------------------------------------------------ Read a `vec2`/`vec3`/`vec4`/`quat`/`mat4` directly from process memory. `_fl32` reads source bytes as float32 (promoted to float64 in the result); `_fl64` reads source bytes as float64. `mat4` is a row-major 4x4. `quat` is `x, y, z, w` packed. Copy vec2 proc.read_vec2_fl32(uint64 addr); vec2 proc.read_vec2_fl64(uint64 addr); vec3 proc.read_vec3_fl32(uint64 addr); vec3 proc.read_vec3_fl64(uint64 addr); vec4 proc.read_vec4_fl32(uint64 addr); vec4 proc.read_vec4_fl64(uint64 addr); quat proc.read_quat_fl32(uint64 addr); quat proc.read_quat_fl64(uint64 addr); mat4 proc.read_mat4_fl32(uint64 addr); mat4 proc.read_mat4_fl64(uint64 addr); Writes mirror the reads (gated: `write_memory`): Copy bool proc.write_vec2_fl32(uint64 addr, vec2 v); bool proc.write_vec2_fl64(uint64 addr, vec2 v); bool proc.write_vec3_fl32(uint64 addr, vec3 v); bool proc.write_vec3_fl64(uint64 addr, vec3 v); bool proc.write_vec4_fl32(uint64 addr, vec4 v); bool proc.write_vec4_fl64(uint64 addr, vec4 v); bool proc.write_quat_fl32(uint64 addr, quat q); bool proc.write_quat_fl64(uint64 addr, quat q); bool proc.write_mat4_fl32(uint64 addr, mat4 m); bool proc.write_mat4_fl64(uint64 addr, mat4 m); Reads return the value directly: Copy proc_t p = ref_process("game.exe"); vec3 cam_pos = p.read_vec3_fl32(p.base_address() + 0x10A4830); println("camera at " + cast(cam_pos.x) + "," + cast(cam_pos.y)); Failed reads (bad address, kernel-RW gate denial, dead proc handle) return a zero-initialized value of the right type — chained `.x` / `.m[i]` stays safe instead of AVing through null. Writes return `false` on the same failure cases. Same kernel-RW gate semantics as the rest of the proc API — see [Permissions](https://docs.perception.cx/perception/enma/proc-api#permissions) . [](https://docs.perception.cx/perception/enma/proc-api#simd-width-reads-writes) SIMD-width reads/writes ------------------------------------------------------------------------------------------------------------ Copy array proc.r128(uint64 addr); // 16 bytes array proc.r256(uint64 addr); // 32 bytes array proc.r512(uint64 addr); // 64 bytes bool proc.w128(uint64 addr, array bytes); // gated: write_memory bool proc.w256(uint64 addr, array bytes); // gated: write_memory bool proc.w512(uint64 addr, array bytes); // gated: write_memory [](https://docs.perception.cx/perception/enma/proc-api#modules-and-exports) Modules and exports ---------------------------------------------------------------------------------------------------- Copy uint64 proc.get_module_base(string name); // 0 if missing uint64 proc.get_module_size(string name); // 0 if missing array proc.get_module_list(); // every loaded module uint64 proc.get_proc_address(uint64 module_base, string export_name); uint64 proc.get_import_rdata_address(uint64 module_base, string import_name); `module_info_t` methods: Copy string m.name(); // base DLL filename, e.g. "kernel32.dll" uint64 m.base(); // DllBase uint64 m.size(); // SizeOfImage Example — list every module loaded in the target: Copy array mods = p.get_module_list(); for (module_info_t m : mods) { println(format("{s} base=0x{x} size=0x{x}", m.name(), m.base(), m.size())); } [](https://docs.perception.cx/perception/enma/proc-api#pattern-scanning) Pattern scanning ---------------------------------------------------------------------------------------------- Copy uint64 proc.find_code_pattern (uint64 search_start, uint64 search_size, string sig); array proc.find_all_code_patterns(uint64 search_start, uint64 search_size, string sig); Sig syntax: hex bytes separated by spaces, `??` is a wildcard. Example: `"48 8B ?? ?? 48 89"`. [](https://docs.perception.cx/perception/enma/proc-api#threads) Threads ---------------------------------------------------------------------------- Copy array proc.get_all_tebs(); [](https://docs.perception.cx/perception/enma/proc-api#pointer-arrays) Pointer arrays ------------------------------------------------------------------------------------------ Copy array proc.read_pointer_array(uint64 base, int64 count, int64 offset_delta); Reads `count` consecutive `uint64`s starting at `base`. `offset_delta` is added to each value before storing (useful when the target stores relative offsets). [](https://docs.perception.cx/perception/enma/proc-api#vad-virtual_query) VAD / virtual\_query --------------------------------------------------------------------------------------------------- Both calls **exclude PE-image regions** (modules, exes). Use `get_module_base/size` for those. Copy vad_region_t proc.virtual_query(uint64 address); array proc.get_vad_snapshot(bool heap_likely_only); `virtual_query` returns a `vad_region_t` handle on hit, `0` on miss. ### [](https://docs.perception.cx/perception/enma/proc-api#vad_region_t) `vad_region_t` Copy uint64 region.start(); uint64 region.size(); uint64 region.protection(); // host page-protection bits (PAGE_READWRITE, PAGE_EXECUTE, etc.) bool region.heap_likely(); // host's heuristic for heap allocations Copy array snap = p.get_vad_snapshot(false); for (int64 i = 0; i < snap.length(); i = i + 1) { vad_region_t r = snap.get(i); uint64 start = r.start(); uint64 size = r.size(); uint64 prot = r.protection(); bool heap = r.heap_likely(); } [](https://docs.perception.cx/perception/enma/proc-api#memory-scans) Memory scans -------------------------------------------------------------------------------------- All scans walk the VAD snapshot (so module memory is excluded — same caveat as above). `heap_only=true` restricts to heap-likely regions. Copy array proc.scan_string (string text, bool heap_only); array proc.scan_wstring(string text, bool heap_only); // text is UTF-8, converted to UTF-16 array proc.scan_pointer(uint64 target, bool heap_only); array proc.scan_u64 (uint64 value, bool heap_only); array proc.scan_u32 (uint32 value, bool heap_only); array proc.scan_float (float32 value, bool heap_only); array proc.scan_double (float64 value, bool heap_only); [](https://docs.perception.cx/perception/enma/proc-api#vm-alloc-free-gated-virtual_memory_operations) VM alloc / free (gated: `virtual_memory_operations`) --------------------------------------------------------------------------------------------------------------------------------------------------------------- Copy uint64 proc.alloc_vm(uint64 size); // 0 on failure bool proc.free_vm (uint64 address); Allocation itself is safe. To execute code from the returned page, the target must have Control Flow Guard (CFG) disabled — CFG kills the process on indirect calls/jumps to non-bitmap addresses. Reads + writes are unaffected. [](https://docs.perception.cx/perception/enma/proc-api#permissions) Permissions ------------------------------------------------------------------------------------ Three flags gate sensitive operations. All default to off; the user grants them per script via the host UI. Flag Gates `write_memory` `wu*`, `w*`, `wf*`, `ws`, `wws`, `wvm`, `w128/256/512` `virtual_memory_operations` `alloc_vm`, `free_vm` `kernel_rw_access` `get_eprocess`; expands every other read/write to also accept _safe_ kernel addresses When a gated call runs without permission it logs `[ENMA] ... blocked: '' permission not granted` and returns 0 / false. ### [](https://docs.perception.cx/perception/enma/proc-api#kernel_rw_access-semantics) `kernel_rw_access` semantics Without it, every read/write address must pass `is_usermode_address` — i.e. canonical user-range, non-null, non-tiny. This is the default and matches the original behavior. With it, addresses are accepted when **either**: * The address is a valid usermode address (same check as before), **or** * The address is a _safe kernel address_ — canonical kernel range AND not in any host-protected critical region (the host's own EPROCESS / ETHREAD / kernel state used for privilege escalation). The "safe kernel" denylist is enforced by `is_safe_kernel_address` in the host. Scripts can't bypass it: a kernel write to a denied address returns `false` and logs, just like any other refused op. Use this flag when a script genuinely needs to inspect or modify kernel structures of the target process (Win32 thread state, KPCR fields, driver-side game state, etc.). Don't grant it casually — kernel writes to the wrong address bugcheck the box. [](https://docs.perception.cx/perception/enma/proc-api#lifetime-and-cleanup) Lifetime and cleanup ------------------------------------------------------------------------------------------------------ `proc_t` releases its host ref via the destructor when the variable goes out of scope. If a script forgets (e.g. leaks a `proc_t*` heap-allocation), the host sweeps remaining refs at script unload — no permanent leak. Copy int64 main() { proc_t p = ref_process("notepad.exe"); if (!p.alive()) return 0; uint64 base = p.base_address(); println(cast(p.r32(base + 0x3C))); // e_lfanew return 0; // p drops here; host ref released } [](https://docs.perception.cx/perception/enma/proc-api#conventions) Conventions ------------------------------------------------------------------------------------ * **Addresses are** `**uint64**`**.** Use `uint64` for any local that holds an address — hex literals like `0x7FF000000000` work directly. Mixing in an `int64` requires `cast(...)`. * **Failed reads return 0**, not an exception. Check `is_valid_address` first if you need certainty. * **Strings returned by** `**rs**`**/**`**rws**` are heap strings — drop normally at scope exit. * **Array returns are length-correct.** `arr.length()` is the actual element count, not a max. [PreviousLifecycle and Routines](https://docs.perception.cx/perception/enma/lifecycle-and-routines) [NextCPU API](https://docs.perception.cx/perception/enma/cpu-api) Last updated 23 days ago --- # Net API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/net-api.md) . All net natives are auto-registered into every loaded script. All network calls are gated by the `network_access` permission. Without it, calls return a transport-failure value (`status=0` / null handle / empty array). [](https://docs.perception.cx/perception/enma/net-api#http-sync-with-timeout) HTTP — sync, with timeout ------------------------------------------------------------------------------------------------------------ Copy http_response_t http_get (string url, int64 timeout_ms); http_response_t http_get (string url, map headers, int64 timeout_ms); http_response_t http_post(string url, string content_type, string body, int64 timeout_ms); http_response_t http_post(string url, string content_type, string body, map headers, int64 timeout_ms); Both always return a non-null `http_response_t`. Read via methods: Copy int64 response.status(); // 0 on transport failure / permission denied string response.body(); bool response.ok(); // true if status is 200..299 `content_type` may be empty for `http_post`. The 3-arg `http_get` / 5-arg `http_post` overloads take a `map` of extra request headers — useful for `Authorization: Bearer ...`, `X-API-Key`, `Accept`, custom protocol headers, etc. Pass `null` or an empty map to skip. ### [](https://docs.perception.cx/perception/enma/net-api#headers-example) Headers example Copy map headers; headers.set("Authorization", "Bearer " + g_token); headers.set("Accept", "application/json"); http_response_t r = http_get("https://api.example.com/me", headers, 5000); if (r.ok()) println(r.body()); Copy map headers; headers.set("X-API-Key", "abc123"); http_response_t r = http_post( "https://api.example.com/events", "application/json", "{\"event\":\"login\"}", headers, 5000); [](https://docs.perception.cx/perception/enma/net-api#websocket) WebSocket ------------------------------------------------------------------------------- Copy ws_t ws_connect(string url, int64 timeout_ms); Connects to `ws://`, `wss://` (also `http://` / `https://` accepted). Spawns a background recv thread. Returns a null handle on failure or permission denied. ### [](https://docs.perception.cx/perception/enma/net-api#ws_t-methods) `ws_t` methods Copy bool ws.is_open(); bool ws.send_text (string msg); bool ws.send_binary(array data); ws_message_t ws.recv(); // blocks until a message arrives or the connection closes ws_message_t ws.poll(); // non-blocking void ws.close(int64 code); // standard WS close codes (1000 = normal) ### [](https://docs.perception.cx/perception/enma/net-api#ws_message_t-methods) `ws_message_t` methods Copy bool msg.ok(); // true if a message was returned bool msg.is_text(); // payload framing bool msg.is_closed(); // peer / local close has fired string msg.payload(); [](https://docs.perception.cx/perception/enma/net-api#udp-raw-datagrams) UDP — raw datagrams ------------------------------------------------------------------------------------------------- Copy udp_t udp_create(); Creates a fresh UDP socket. Returns a null handle on failure / permission denied. Send-only sockets can skip `bind()`; sockets that receive must `bind()` to a local port first. ### [](https://docs.perception.cx/perception/enma/net-api#udp_t-methods) `udp_t` methods Copy bool udp.bind(string addr, int64 port); // "0.0.0.0" / port — port 0 = OS-picked bool udp.send_to(array data, string addr, int64 port); array udp.recv(int64 timeout_ms); // blocking with timeout; empty on timeout/error string udp.last_sender_addr(); // IP of the most recent successful recv() int64 udp.last_sender_port(); // port of the most recent successful recv() void udp.close(); `recv` returns up to one full UDP datagram (max 65535 bytes). Timeout is in milliseconds — `timeout_ms = 0` means block indefinitely. After a successful `recv`, `last_sender_addr()` / `last_sender_port()` give you the peer to reply to. ### [](https://docs.perception.cx/perception/enma/net-api#udp-example-source-query-protocol-a2s_info) UDP example — Source Query Protocol (A2S\_INFO) Copy udp_t s = udp_create(); if (cast(s) == 0) return 0; // A2S_INFO request: FF FF FF FF 54 "Source Engine Query" 00 array q; q.push(0xFF); q.push(0xFF); q.push(0xFF); q.push(0xFF); q.push(0x54); string banner = "Source Engine Query"; for (int32 ch : banner) q.push(cast(ch)); q.push(0x00); if (!s.send_to(q, "1.2.3.4", 27015)) { println("send failed"); return 0; } array reply = s.recv(2000); // 2-second timeout if (reply.length() == 0) { println("no reply (timeout)"); } else { println(format("got {d} bytes from {s}:{d}", reply.length(), s.last_sender_addr(), s.last_sender_port())); } ### [](https://docs.perception.cx/perception/enma/net-api#udp-example-listener) UDP example — listener Copy udp_t s = udp_create(); s.bind("0.0.0.0", 9999); for (int32 i = 0; i < 10; i = i + 1) { array pkt = s.recv(1000); if (pkt.length() == 0) continue; println(format("from {s}:{d} ({d} bytes)", s.last_sender_addr(), s.last_sender_port(), pkt.length())); } [](https://docs.perception.cx/perception/enma/net-api#http-example) HTTP example ------------------------------------------------------------------------------------- Copy http_response_t r = http_get("https://api.example.com/status", 5000); if (r.ok()) { println("got: " + r.body()); } else if (r.status() == 0) { println("transport failed or permission denied"); } else { println("server returned " + cast(r.status())); } [](https://docs.perception.cx/perception/enma/net-api#websocket-example) WebSocket example ----------------------------------------------------------------------------------------------- Copy ws_t ws = ws_connect("wss://echo.example.com/", 5000); if (cast(ws) == 0) return 0; ws.send_text("hello"); ws_message_t m = ws.recv(); if (m.ok()) { println("got: " + m.payload()); } ws.close(1000); [](https://docs.perception.cx/perception/enma/net-api#permission) Permission --------------------------------------------------------------------------------- `network_access` gates every native in this file (HTTP, WebSocket, UDP). When off, every call returns a transport-failure value. [](https://docs.perception.cx/perception/enma/net-api#lifetime) Lifetime ----------------------------------------------------------------------------- `ws_t` and `udp_t` close + free via the destructor at scope exit. If the script forgets, the host sweeps remaining sockets at unload — connections closed, threads joined, no permanent leak. UDP packets in flight are not buffered host-side; once you close, anything still on the wire is dropped by the OS. [PreviousInput API](https://docs.perception.cx/perception/enma/input-api) [NextRender API](https://docs.perception.cx/perception/enma/render-api) Last updated 23 days ago --- # Input API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/input-api.md) . All input natives are auto-registered into every loaded script. Read-only complement to [Win API](https://docs.perception.cx/perception/enma/win-api) — Win API **sends** input, this **reads** state. Pollable per-frame from `my_draw` or routine callbacks. Virtual-key codes follow Win32 VK\_\* convention. The `vk` enum bundles the common ones so no `#include` is needed. [](https://docs.perception.cx/perception/enma/input-api#mouse) Mouse ------------------------------------------------------------------------- Copy vec2 get_mouse_pos(); // render-window pixels vec2 get_mouse_pos_desktop(); // desktop pixels (full screen) vec2 get_mouse_delta(); // raw movement this frame vec2 get_mouse_delta_desktop(); // desktop-space delta this frame bool mouse_movement_received(); // any movement this frame bool is_hovered(vec2 pos, vec2 size); // mouse inside rect at pos with given size float64 get_scroll_delta(); // wheel ticks; positive = up [](https://docs.perception.cx/perception/enma/input-api#keyboard-single-flag-queries) Keyboard — single-flag queries ------------------------------------------------------------------------------------------------------------------------- Flag Meaning `down` currently pressed (host-debounced) `raw_down` OS-level pressed state `fired` up→down transition this frame `toggle` caps-lock-style toggle (flips on each press) `singlepress` fired but suppressed when modifiers are held `prev_down` down state from previous frame Copy bool key_down (int64 vk); bool key_raw_down (int64 vk); bool key_fired (int64 vk); bool key_toggle (int64 vk); bool key_singlepress(int64 vk); bool key_prev_down (int64 vk); [](https://docs.perception.cx/perception/enma/input-api#bulk-ergonomic-queries) Bulk / ergonomic queries ------------------------------------------------------------------------------------------------------------- Copy key_state_t get_key_state(int64 vk); // atomic snapshot of all 6 flags array get_keys_down(); // virtual-key codes currently pressed string get_recent_key_input(); // buffered text input (UTF-8) since last poll string get_key_name(int64 vk); // localized key name (e.g. "F1", "Left Arrow"); empty on invalid ### [](https://docs.perception.cx/perception/enma/input-api#key_state_t) `key_state_t` Copy bool ks.raw_down(); // OS-level pressed state bool ks.down(); // host-debounced pressed state bool ks.fired(); // up->down this frame (one-shot) bool ks.toggle(); // caps-lock-style toggle (flips on each press) bool ks.singlepress(); // fired but suppressed if modifiers held bool ks.prev_down(); // down state from previous frame Use `get_key_state(vk)` when you need consistency across multiple flag reads in the same frame — the per-flag fns above each take a separate lock and can race. [](https://docs.perception.cx/perception/enma/input-api#vk-enum-common-win32-virtual-keys) `vk` enum — common Win32 virtual keys ------------------------------------------------------------------------------------------------------------------------------------- Copy vk::backspace vk::tab vk::enter vk::shift vk::ctrl vk::alt vk::pause vk::caps_lock vk::escape vk::space vk::page_up vk::page_down vk::end vk::home vk::left vk::up vk::right vk::down vk::insert vk::delete vk::k0 .. vk::k9 // top-row digits vk::a .. vk::z // letters vk::lwin vk::rwin vk::numpad0 .. vk::numpad9 vk::multiply vk::add vk::subtract vk::decimal vk::divide vk::f1 .. vk::f12 vk::num_lock vk::scroll_lock vk::lshift vk::rshift vk::lctrl vk::rctrl vk::lalt vk::ralt // Mouse buttons (Win32 puts these in the same VK space): vk::lbutton vk::rbutton vk::mbutton vk::xbutton1 vk::xbutton2 [](https://docs.perception.cx/perception/enma/input-api#example-trigger-an-action-on-f1-press) Example: trigger an action on F1 press ------------------------------------------------------------------------------------------------------------------------------------------ Copy void my_tick(int64 data) { if (key_fired(vk::f1)) { println("F1 pressed"); } } int64 main() { register_routine(cast(my_tick), 0); return 1; } [PreviousGUI API](https://docs.perception.cx/perception/enma/gui-api) [NextNet API](https://docs.perception.cx/perception/enma/net-api) Last updated 23 days ago --- # Filesystem API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/filesystem-api.md) . Sandboxed filesystem access for enma scripts. Every call is gated by the `file_system_access` permission. Without it, calls return a failure value (`false` / `0` / empty array) and never throw. [](https://docs.perception.cx/perception/enma/filesystem-api#sandbox) Sandbox ---------------------------------------------------------------------------------- Paths are interpreted relative to the script's per-user data directory. Scripts cannot reach outside that root. Rejected at the path-validation step (returns failure without touching disk): * absolute paths — `C:\config`, `/etc/hosts` * UNC paths — `\\server\share` * parent traversals — `..\..\elsewhere` * leading slashes — `/foo`, `\foo` * embedded `:`, `\n`, `\r`, `\0` Forward and backslashes are both accepted internally; pick one and stay consistent. [](https://docs.perception.cx/perception/enma/filesystem-api#quick-reference) Quick reference -------------------------------------------------------------------------------------------------- Operation Native Create / overwrite a text file `fs_create_file(path, data)` Create a directory `fs_create_directory(path)` Test existence `fs_file_exists(path)` / `fs_dir_exists(path)` Delete `fs_delete_file(path)` / `fs_delete_directory(path)` File size `fs_file_size(path)` Read text `fs_read_file(path)` Write / append text `fs_write_file(path, data)` / `fs_append_file(path, data)` Read binary `fs_read_file_binary(path)` Write / append binary `fs_write_file_binary(path, bytes)` / `fs_append_file_binary(path, bytes)` List entries `fs_list_files(path)` / `fs_list_dirs(path)` / `fs_list_all(path)` [](https://docs.perception.cx/perception/enma/filesystem-api#file-operations) File operations -------------------------------------------------------------------------------------------------- Copy bool fs_create_file(string path, string data); bool fs_create_directory(string path); bool fs_file_exists(string path); bool fs_dir_exists(string path); bool fs_delete_file(string path); bool fs_delete_directory(string path); int64 fs_file_size(string path); * `fs_create_file` writes `data` as the file's complete contents (UTF-8). Overwrites if the file already exists. Empty `data` creates a zero-byte file. * `fs_create_directory` creates the directory and any missing parents. * `fs_delete_directory` succeeds only when the target directory is empty. * `fs_file_size` returns `0` for missing files (indistinguishable from a real zero-byte file — use `fs_file_exists` first if you need to disambiguate). Copy if (!fs_dir_exists("configs")) fs_create_directory("configs"); if (!fs_create_file("configs/active.json", "{\"version\":1}")) println("[fs] write failed (permission?)"); [](https://docs.perception.cx/perception/enma/filesystem-api#text-i-o) Text I/O ------------------------------------------------------------------------------------ Copy string fs_read_file(string path); bool fs_write_file(string path, string data); bool fs_append_file(string path, string data); * `fs_read_file` returns the file's bytes interpreted as UTF-8. Returns an **empty string** on missing file / read failure / permission denied — distinguish from a real empty file via `fs_file_exists` if it matters. * `fs_write_file` overwrites; `fs_append_file` appends. Both return `true` on success, `false` on failure. Copy fs_write_file("state/last_target.txt", "weapon_t1_assault"); string saved = fs_read_file("state/last_target.txt"); if (saved.length() > 0) set_target(saved); [](https://docs.perception.cx/perception/enma/filesystem-api#binary-i-o) Binary I/O ---------------------------------------------------------------------------------------- Copy array fs_read_file_binary(string path); bool fs_write_file_binary(string path, array bytes); bool fs_append_file_binary(string path, array bytes); Use these for opaque blobs (saved offsets, screenshots, packet captures). A missing or unreadable file yields an empty array. Empty-input writes succeed and produce a zero-byte file; empty-input appends are a no-op (still return `true`). Copy array header; header.push(0x4D); header.push(0x5A); // "MZ" fs_write_file_binary("dumps/probe.bin", header); array back = fs_read_file_binary("dumps/probe.bin"); print("read " + cast(back.length()) + " bytes"); [](https://docs.perception.cx/perception/enma/filesystem-api#directory-listing) Directory listing ------------------------------------------------------------------------------------------------------ Copy array fs_list_files(string path); array fs_list_dirs(string path); array fs_list_all(string path); Returns the **basenames** of entries (no path prefixes) in the requested directory. No recursion — descend manually by calling again with the joined relative path. A missing directory or permission denial yields an empty array. Entry order is filesystem-dependent. Copy array configs = fs_list_files("configs"); for (string name : configs) { string body = fs_read_file("configs/" + name); // ... } [](https://docs.perception.cx/perception/enma/filesystem-api#failure-modes) Failure modes ---------------------------------------------------------------------------------------------- Every native handles every failure the same way: returns a falsy value (`false` / `0` / empty). The script never sees an exception from the FS layer. Cause Return `file_system_access` permission off `false` / `0` / empty Path validation fails (absolute, UNC, `..`, control char) `false` / `0` / empty Target file/directory missing `false` / `0` / empty (writes that need a parent will fail if the parent is missing — `fs_create_directory` first) Underlying I/O error (disk full, locked file, etc.) `false` / `0` / empty [](https://docs.perception.cx/perception/enma/filesystem-api#sandbox-tests) Sandbox tests ---------------------------------------------------------------------------------------------- Every escape attempt below returns `false` / empty without touching disk: Copy fs_create_file("C:\\evil.txt", "x"); // false: absolute path fs_create_file("/etc/passwd", "x"); // false: absolute / fs_create_file("\\\\server\\f", "x"); // false: UNC fs_create_file("../escape.txt", "x"); // false: parent traversal fs_create_file("ok/../escape.txt", "x"); // false: nested traversal [](https://docs.perception.cx/perception/enma/filesystem-api#permissions) Permissions ------------------------------------------------------------------------------------------ Enabled via the script's `permissions` block. The flag is `file_system_access`. With it off, every call short-circuits before touching disk — reads return empty, writes silently no-op. Check with `fs_file_exists` after a write if your logic depends on the operation having succeeded. [PreviousCPU API](https://docs.perception.cx/perception/enma/cpu-api) [NextGUI API](https://docs.perception.cx/perception/enma/gui-api) Last updated 23 days ago --- # GUI API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/gui-api.md) . All GUI natives are auto-registered into every loaded script. The API is in two parts: * **Part 1 — sidebar sections + widgets.** A `sidebar_section_t` is a select-button in the host sidebar plus a content panel auto-attached to the main frame. The panel renders only when the section's button is selected. Widgets are created on the section directly. * **Part 2 — frames, layers, custom widgets, menus, file pickers.** Lower-level primitives for floating windows and custom drawing. All GUI handles are `int64`\-backed. The script doesn't own the underlying resources — calling a destructor on a handle is a noop. At script unload, every handle the script created gets cleaned up automatically. [](https://docs.perception.cx/perception/enma/gui-api#sidebar-sections) Sidebar sections --------------------------------------------------------------------------------------------- Copy sidebar_section_t create_sidebar_section(string name, string icon); void create_sidebar_separator(); `name` renders as the sidebar label. `icon` accepts a codicon string (e.g. `"\xEE\xAC\xA3"` for file-code U+EB23) or `""` for no icon. Each section is a radio-style sidebar entry: clicking one auto-deselects siblings and shows that section's panel. Copy void section.set_active(bool active); // toggle selection programmatically ### [](https://docs.perception.cx/perception/enma/gui-api#widget-builders-on-sidebar_section_t) Widget builders on `sidebar_section_t` Every widget builder returns a typed handle. Each is also `int64`\-backed; pass to other natives via the typed name. Copy label_t section.create_label(string text, ui_align align); void section.create_separator(); button_t section.create_button(string label, ui_align align); checkbox_t section.create_checkbox(string label, bool initial); slider_t section.create_slider(string label, float64 initial, float64 minv, float64 maxv, float64 step); slider_icon_t section.create_slider_icon(string icon, float64 initial, float64 minv, float64 maxv, float64 step); value_input_t section.create_value_input(string label, float64 initial, float64 minv, float64 maxv, float64 step); options_t section.create_options(string label, array items, int64 selected); multi_options_t section.create_multi_options(string label, array items, int64 selected_mask); dropdown_t section.create_dropdown(string label, array items, int64 selected); multi_dropdown_t section.create_multi_dropdown(string label, array items, int64 selected_mask); list_t section.create_list(string label, array info1, array info2, bool selectable, int64 selected, int64 visible_rows, bool filterable); inline_button_t section.create_inline_button(string label, float64 width, string icon); inline_text_input_t section.create_inline_text_input(string initial, float64 width, string placeholder); tabs_t section.create_tabs(array items, int64 selected); keybind_t section.create_keybind(string label); progress_bar_t section.create_progress_bar(string label, float64 initial, float64 minv, float64 maxv, bool show_pct); spinner_t section.create_spinner(string label); range_slider_t section.create_range_slider(string label, float64 minv, float64 maxv, float64 lo, float64 hi, float64 step); table_t section.create_table(string label, array col_names, array col_widths, int64 visible_rows); text_input_t section.create_text_input(string label, string initial, int64 max_lines); text_editor_t section.create_text_editor(string label, string initial, int64 visible_lines, string lexer); colorpicker_t section.create_colorpicker(string label, color initial); [](https://docs.perception.cx/perception/enma/gui-api#common-widget-operations) Common widget operations ------------------------------------------------------------------------------------------------------------- Every widget type (except `text_editor_t`, which has no `set_active`) supports: Copy void widget.set_active(bool active); void widget.set_tooltip(string s); void widget.on_change(int64 fn_handle); // closure: void cb(int64 widget_handle) `on_change` fires on `CALLBACK_VALUE_CHANGED` — which means click for buttons, value mutation for sliders / checkboxes / dropdowns / etc. Pass the closure via `cast(my_callback)`. [](https://docs.perception.cx/perception/enma/gui-api#per-widget-typed-get-set) Per-widget typed get / set --------------------------------------------------------------------------------------------------------------- Copy // label void label.set_text(string s); // button void button.attach_to(button_t other); // group buttons into one row // checkbox bool checkbox.get(); void checkbox.set(bool v); // slider, slider_icon, value_input float64 X.get(); void X.set(float64 v); // options, dropdown, tabs int64 X.get(); void X.set(int64 i); // multi_options, multi_dropdown int64 X.get_mask(); void X.set_mask(int64 m); // list int64 list.get_selected(); void list.set_selected(int64 i); void list.set_items(array info1, array info2); int64 list.size(); // inline_text_input, text_input, text_editor string X.get(); void X.set(string s); // keybind void keybind.bind(int64 vk, bool ctrl, bool shift, bool alt, keybind_mode mode); bool keybind.is_active(); // true when any binding is currently active per its mode; poll to react to activation int64 keybind.binding_count(); // number of bindings on this row // progress_bar void progress_bar.set(float64 v); // range_slider — split lo/hi getters since there's no natural pair type float64 range_slider.get_lo(); float64 range_slider.get_hi(); void range_slider.set(float64 lo, float64 hi); // table void table.add_row(array cells); void table.clear(); int64 table.size(); // colorpicker — uses the registered `color` type void colorpicker.attach_to(colorpicker_t other); color colorpicker.get(); void colorpicker.set(color c); [](https://docs.perception.cx/perception/enma/gui-api#frames-part-2) Frames (Part 2) ----------------------------------------------------------------------------------------- `frame_t` wraps any of four host frame kinds — distinguished by which factory you call: Copy frame_t create_frame(string name, vec2 pos, vec2 size, layer_t layer); // raw frame, no chrome. Pass 0 for layer to use the default layer. frame_t create_default_frame(string name, vec2 pos, vec2 size, layer_t layer); // frame with title bar / logo / drag chrome. frame_t create_draggable_frame(string name, vec2 pos, vec2 size, layer_t layer); frame_t create_popup(string name, vec2 pos, vec2 size, layer_t layer); Copy void frame.set_pos(vec2 pos); void frame.set_size(vec2 size); vec2 frame.get_pos(); vec2 frame.get_size(); void frame.set_visible(bool v); bool frame.is_visible(); void frame.set_anchors(int64 mask); // ui_anchor::* OR'd void frame.attach(frame_t parent); void frame.set_float(int64 hash, float64 v); // widget_attr::* keys void frame.install_hook(int64 hook_id, int64 fn_handle); void frame.remove_hook(int64 hook_id); void frame.set_focused(); frame_t get_focused_frame(); bool ui_is_focused(); [](https://docs.perception.cx/perception/enma/gui-api#layers) Layers ------------------------------------------------------------------------- A layer is a z-stacked frame group; frames in higher layers paint over lower ones. Copy layer_t create_layer(string name, bool input_passthrough, bool force_topmost); layer_t get_default_layer(); int64 layer_count(); void layer.promote_to_top(); void layer.set_visible(bool v); int64 layer.frame_count(); [](https://docs.perception.cx/perception/enma/gui-api#custom-widgets-on-a-script-owned-frame) Custom widgets on a script-owned frame ----------------------------------------------------------------------------------------------------------------------------------------- Drop a `widget_t` into one of your `frame_t`s for a custom render callback that fires every tick during the frame's render pass: Copy widget_t create_widget(frame_t parent, string name, int64 execute_cb_handle, bool consume_input); // execute_cb shape: void cb(int64 widget_handle) — called every tick. void widget.set_pos(vec2 pos); void widget.set_size(vec2 size); void widget.set_active(bool v); void widget.set_tooltip(string s); void widget.set_float(int64 hash, float64 v); void widget.set_anchors(int64 mask); void widget.install_hook(int64 hook_id, int64 fn_handle); void widget.remove_hook(int64 hook_id); [](https://docs.perception.cx/perception/enma/gui-api#menus) Menus ----------------------------------------------------------------------- A `menu_t` is a context menu — a popup list of items. Attach it to any widget to make right-click on that widget open it. Copy menu_t create_menu(); void menu.add_item(string label, int64 on_click_cb, string shortcut, string icon); // shortcut: visible label only (e.g. "Ctrl+C"); not bound by add_item itself. // icon: codicon string or "" for none. // on_click_cb shape: void cb(int64 menu_user_data). void menu.add_separator(); `menu_t.attach_to_widget` is split per widget type because enma's overloading is by arity, not by parameter type. Use the variant matching the widget you're attaching to: Copy void menu.attach_to_widget(widget_t target); void menu.attach_to_button(button_t target); void menu.attach_to_label(label_t target); void menu.attach_to_checkbox(checkbox_t target); void menu.attach_to_slider(slider_t target); void menu.attach_to_slider_icon(slider_icon_t target); void menu.attach_to_value_input(value_input_t target); void menu.attach_to_options(options_t target); void menu.attach_to_multi_options(multi_options_t target); void menu.attach_to_dropdown(dropdown_t target); void menu.attach_to_multi_dropdown(multi_dropdown_t target); void menu.attach_to_list(list_t target); void menu.attach_to_inline_button(inline_button_t target); void menu.attach_to_inline_text_input(inline_text_input_t target); void menu.attach_to_tabs(tabs_t target); void menu.attach_to_keybind(keybind_t target); void menu.attach_to_progress_bar(progress_bar_t target); void menu.attach_to_spinner(spinner_t target); void menu.attach_to_range_slider(range_slider_t target); void menu.attach_to_table(table_t target); void menu.attach_to_text_input(text_input_t target); void menu.attach_to_text_editor(text_editor_t target); void menu.attach_to_colorpicker(colorpicker_t target); [](https://docs.perception.cx/perception/enma/gui-api#file-picker) File picker ----------------------------------------------------------------------------------- Copy file_picker_t create_file_picker(string title, string start_path, string filter_extension, bool folder_mode); void picker.open(); void picker.close(); string picker.get_selected(); [](https://docs.perception.cx/perception/enma/gui-api#theme) Theme ----------------------------------------------------------------------- Copy bool is_dark_theme(); void set_dark_theme(bool dark); color get_theme_color(int64 color_hash); void set_theme_color(int64 color_hash, color c); `color_hash` is a value from the `ui_color` enum. [](https://docs.perception.cx/perception/enma/gui-api#toasts-and-queries) Toasts and queries ------------------------------------------------------------------------------------------------- Copy void show_toast(toast_kind kind, string title, string msg); bool gui_active(); [](https://docs.perception.cx/perception/enma/gui-api#enums) Enums ----------------------------------------------------------------------- All exposed without needing a header import: Enum Values `ui_anchor` `none`, `left`, `right`, `top`, `bottom`, `all` `ui_edge` `left`, `top`, `right`, `bottom` `ui_align` `left`, `center`, `right` `ui_layout` `none`, `vertical`, `horizontal` `ui_hook` 33 hook IDs incl. `pre_execute`, `post_execute`, `clicked`, `right_clicked`, `should_render`, `editor_*`, `widget_execute` `ui_callback` `value_changed`, `item_activated` `widget_attr` well-known position / size / scroll / rounding hashes `ui_color` 35 color hashes (`bg`, `text`, `accent`, `frame_bg`, `sidebar_bg`, `element_button_bg`, etc.) `keybind_mode` `off`, `on`, `single`, `toggle`, `always_on` `toast_kind` `info`, `success`, `warning`, `error` [](https://docs.perception.cx/perception/enma/gui-api#lifecycle-and-cleanup) Lifecycle and cleanup ------------------------------------------------------------------------------------------------------- GUI resources you create (sections, frames, layers, custom widgets, menus, file pickers) are tracked per-script and torn down automatically at script unload. You don't need to destroy them manually — the destructor on each handle is a noop. Caveats: * **Sidebar slots persist.** Sections you create occupy a sidebar slot for the lifetime of the host. Hot-reloading scripts that create many sections will leave stale slots in the sidebar. * **Separators** stay visible after unload (no remove path). Hook callbacks fire on the UI thread. Heavy work inside a `pre_execute` or `on_change` (running every tick on every widget) shows up in profile — keep them lightweight. [](https://docs.perception.cx/perception/enma/gui-api#example) Example --------------------------------------------------------------------------- A comprehensive script exercising most of the widget builders, `on_change` plumbing through typed handles, an attached-button row, an attached colorpicker chain, a context menu, a tabs widget with per-tab content, and a routine polling `keybind.is_active()`. Copy sidebar_section_t g_sec; menu_t g_menu; keybind_t g_kb_aim; keybind_t g_kb_esp; bool g_aim_was_active = false; bool g_esp_was_active = false; int64 g_kb_routine = 0; tabs_t g_tabs; label_t g_t0_label; slider_t g_t0_slider; label_t g_t1_label; checkbox_t g_t1_check; void on_apply(int64 _) { print_console("[demo] Apply clicked"); } void on_cancel(int64 _) { print_console("[demo] Cancel clicked"); } void on_volume(int64 self) { slider_t s = cast(self); print_console("Volume -> " + cast(s.get())); } void on_features(int64 self) { multi_options_t mo = cast(self); print_console("features mask -> " + cast(mo.get_mask())); } void on_accent(int64 self) { colorpicker_t cp = cast(self); color c = cp.get(); print_console("accent -> " + cast(c.r()) + "," + cast(c.g()) + "," + cast(c.b()) + "," + cast(c.a())); } void on_view_tabs(int64 self) { tabs_t t = cast(self); int64 sel = t.get(); // selected tab's widgets active, others inactive g_t0_label.set_active(sel == 0); g_t0_slider.set_active(sel == 0); g_t1_label.set_active(sel == 1); g_t1_check.set_active(sel == 1); } void on_kb_aim_changed(int64 self) { keybind_t kb = cast(self); print_console("aim bindings -> " + cast(kb.binding_count())); } // keybinds don't fire a callback on hardware-key activation — // poll keybind.is_active() to react. void kb_poll_routine(int64 _data) { bool now = g_kb_aim.is_active(); if (now != g_aim_was_active) { print_console(now ? "Aim ACTIVE" : "Aim inactive"); g_aim_was_active = now; } bool esp = g_kb_esp.is_active(); if (esp != g_esp_was_active) { print_console(esp ? "ESP ACTIVE" : "ESP inactive"); g_esp_was_active = esp; } } int32 main() { g_sec = create_sidebar_section("demo", ""); g_sec.create_label("Settings panel demo.", ui_align::left); g_sec.create_separator(); // Attached button row — children share the primary's row. button_t apply = g_sec.create_button("Apply", ui_align::right); button_t cancel = g_sec.create_button("Cancel", ui_align::right); cancel.attach_to(apply); apply.on_change(cast(on_apply)); cancel.on_change(cast(on_cancel)); g_sec.create_separator(); g_sec.create_checkbox("Notifications", true); slider_t vol = g_sec.create_slider("Volume", 0.6, 0.0, 1.0, 0.0); vol.on_change(cast(on_volume)); g_sec.create_value_input("Port", 8080.0, 1.0, 65535.0, 1.0); // Codicon UTF-8 byte sequence — `\xHH` lexer escape required. g_sec.create_slider_icon("\xEE\xA9\xB0", 0.75, 0.0, 1.0, 0.0); g_sec.create_separator(); array features; features.push("Autosave"); features.push("Spell check"); features.push("Auto-complete"); features.push("Line numbers"); multi_options_t mo = g_sec.create_multi_options("Editor features", features, 13); mo.on_change(cast(on_features)); g_sec.create_separator(); array tab_items; tab_items.push("Overview"); tab_items.push("Logs"); g_tabs = g_sec.create_tabs(tab_items, 0); g_tabs.on_change(cast(on_view_tabs)); g_t0_label = g_sec.create_label("Overview content.", ui_align::left); g_t0_slider = g_sec.create_slider("FOV", 90.0, 60.0, 120.0, 1.0); g_t1_label = g_sec.create_label("Logs content.", ui_align::left); g_t1_check = g_sec.create_checkbox("Verbose logging", false); g_t1_label.set_active(false); g_t1_check.set_active(false); g_sec.create_separator(); g_kb_aim = g_sec.create_keybind("Aimbot"); g_kb_aim.bind(0x01, false, false, false, keybind_mode::on); // VK_LBUTTON g_kb_aim.on_change(cast(on_kb_aim_changed)); g_kb_esp = g_sec.create_keybind("ESP toggle"); g_kb_esp.bind(0x45, false, false, false, keybind_mode::toggle); // 'E' g_sec.create_separator(); colorpicker_t accent = g_sec.create_colorpicker("Accent", color(180, 180, 180, 255)); accent.on_change(cast(on_accent)); // Attached colorpicker chain — children render as swatches in the parent's popup. colorpicker_t theme_cp = g_sec.create_colorpicker("Theme", color(120, 120, 120, 255)); colorpicker_t primary = g_sec.create_colorpicker("Primary", color( 80, 80, 80, 255)); colorpicker_t secondary = g_sec.create_colorpicker("Secondary", color(200, 200, 200, 255)); primary.attach_to(theme_cp); secondary.attach_to(theme_cp); // Context menu attached to a button — opens on the host's right-click path. button_t actions = g_sec.create_button("Actions", ui_align::center); g_menu = create_menu(); g_menu.add_item("Reset", cast(on_apply), "Ctrl+R", ""); g_menu.add_separator(); g_menu.add_item("About...", cast(on_cancel), "", ""); g_menu.attach_to_button(actions); g_kb_routine = register_routine(cast(kb_poll_routine), 0); return 1; // keep loaded so the section stays interactive } [PreviousFilesystem API](https://docs.perception.cx/perception/enma/filesystem-api) [NextInput API](https://docs.perception.cx/perception/enma/input-api) Last updated 16 days ago --- # CPU API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/cpu-api.md) . All CPU natives are auto-registered into every loaded script. Stuff that doesn't fit cleanly into other host APIs and isn't already in Enma's preshipped addons. For wall-clock time, ISO formatting, and `unix_seconds()` see the preshipped [Time](https://enma-1.gitbook.io/enma/addons/time) addon. For `popcount`/`clz`/`bswap` etc., the preshipped [Bits](https://enma-1.gitbook.io/enma/addons/bits) addon. [](https://docs.perception.cx/perception/enma/cpu-api#cpu-identification) CPU identification ------------------------------------------------------------------------------------------------- Copy string cpu_vendor(); // CPUID leaf 0, e.g. "GenuineIntel" string cpu_brand(); // CPUID leaves 0x80000002..4, e.g. "Intel(R) Core(TM) i9-..." [](https://docs.perception.cx/perception/enma/cpu-api#timing) Timing ------------------------------------------------------------------------- Copy int64 rdtsc(); // raw cycle counter; not stable across cores or sleep int64 perf_time(); // QueryPerformanceCounter int64 perf_frequency(); // counter ticks per second int64 get_tickcount64(); // ms since system boot (monotonic, 64-bit safe) `perf_time / perf_frequency` together give sub-microsecond timestamps: Copy int64 t0 = perf_time(); do_work(); float64 secs = cast(perf_time() - t0) / cast(perf_frequency()); [](https://docs.perception.cx/perception/enma/cpu-api#datetime-helpers) Datetime helpers --------------------------------------------------------------------------------------------- Companions to the preshipped `time` addon's `year`/`month`/`day`/`hour`/`day_of_week`/etc. decoders. The `time` addon takes a unix timestamp; these convert intermediate fields: Copy int64 now_millisecond(); // 0..999, current local time string day_name(int64 dow); // 0..6 -> "Sunday".."Saturday"; "Unknown" out of range string month_name(int64 month); // 1..12 -> "January".."December"; "Unknown" out of range int64 hour12(int64 hour24); // 0..23 -> 1..12 (12-hour wall format) string ampm(int64 hour24); // 0..23 -> "AM" / "PM" [](https://docs.perception.cx/perception/enma/cpu-api#bitcasts-float-int) Bitcasts (float ↔ int) ----------------------------------------------------------------------------------------------------- Use the language built-in `reinterpret_cast(val)`. Reinterprets the bit pattern; not a value conversion. Source and target must be the same byte size; emits a compile error otherwise. Copy uint32 u = reinterpret_cast(1.5f); // 0x3FC00000 float32 f = reinterpret_cast(0x3FC00000u); // 1.5 uint64 bits = reinterpret_cast(3.14); // IEEE 64-bit pattern uint32 sign = reinterpret_cast(-3.14f) >> 31; // 1 Compiles to at most 2 mov instructions (`narrow_f32` + `cast` at the f32 boundary, plain `cast` elsewhere) — zero call overhead. Generalizes to any same-size pair: pointers ↔ `int64`, mixed signed/unsigned narrow ints, etc. For narrow-int → wider-int (no float involved), `cast(some_int8)` etc. works directly — Enma keeps narrow ints zero/sign-extended in 64-bit slots, so the cast is a free rename. [](https://docs.perception.cx/perception/enma/cpu-api#thread-priority) Thread priority ------------------------------------------------------------------------------------------- Affects whatever thread invokes the call. Routine callbacks run each tick on their own ticker thread, so calling from a routine adjusts that ticker thread (NOT the script's main thread). Copy bool set_thread_priority(thread_priority p); `thread_priority` enum values: `lowest`, `below_normal`, `normal`, `above_normal`, `highest`. Copy set_thread_priority(thread_priority::highest); [PreviousProc API](https://docs.perception.cx/perception/enma/proc-api) [NextFilesystem API](https://docs.perception.cx/perception/enma/filesystem-api) Last updated 23 days ago --- # Sound API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/sound-api.md) . All sound natives are auto-registered into every loaded script. Two handle types: * `sound_t` — a loaded resource. Multiple instances can play from one resource concurrently. * `sound_inst_t` — a live playback instance. Returned by `sound.play(...)`. Both are `int64`\-backed handles with auto-cleanup destructors. Resources are tracked per-script; an unload sweep frees anything the user forgot to drop. [](https://docs.perception.cx/perception/enma/sound-api#load-unload) Load / unload --------------------------------------------------------------------------------------- Copy sound_t load_sound(string relative_path); Loads `/`. Path is validated: * No `..` segments. * No `:` (drive letters), `\n`, `\r`. * Cannot start with `/` or `\` (must be relative). Returns a null handle on validation failure or read failure. The destructor frees the resource. [](https://docs.perception.cx/perception/enma/sound-api#playback) Playback ------------------------------------------------------------------------------- Copy sound_inst_t sound.play(float64 volume, float64 pan, bool loop); * `volume`: 0.0 .. 1.0 (clamped) * `pan`: -1.0 (full left) .. 1.0 (full right) (clamped) * `loop`: repeat forever until stopped [](https://docs.perception.cx/perception/enma/sound-api#instance-control) Instance control ----------------------------------------------------------------------------------------------- Copy bool sound_inst.is_playing(); void sound_inst.stop(); void sound_inst.set_volume(float64 v); // 0..1 void sound_inst.set_pan(float64 p); // -1..1 [](https://docs.perception.cx/perception/enma/sound-api#globals) Globals ----------------------------------------------------------------------------- Copy void stop_all_sounds(); // halts every instance globally [](https://docs.perception.cx/perception/enma/sound-api#example) Example ----------------------------------------------------------------------------- Copy int64 main() { sound_t snd = load_sound("sounds/notification.wav"); if (cast(snd) == 0) return 0; sound_inst_t inst = snd.play(0.5, 0.0, false); while (inst.is_playing()) sleep_ms(50); return 1; // snd / inst drop here; resource + instance freed } [](https://docs.perception.cx/perception/enma/sound-api#lifetime) Lifetime ------------------------------------------------------------------------------- `sound_t` and `sound_inst_t` both release at scope exit. If the script forgets, the host sweeps remaining handles at unload. No permanent leak. [PreviousRender API](https://docs.perception.cx/perception/enma/render-api) [NextUnicorn API](https://docs.perception.cx/perception/enma/unicorn-api) Last updated 23 days ago --- # Win API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/win-api.md) . All win natives are auto-registered into every loaded script. This API **sends** input and reads window state. For state polling (mouse position, key down/up etc.), see [Input API](https://docs.perception.cx/perception/enma/input-api) . `HWND` is exposed as raw `int64`. OS-owned; if the window disappears, subsequent calls reject via `IsWindow()`. [](https://docs.perception.cx/perception/enma/win-api#window_info_t) `window_info_t` ----------------------------------------------------------------------------------------- Snapshot of a window at enumeration time. Heap-allocated; fields read via methods. Copy int64 info.hwnd(); int64 info.pid(); int64 info.tid(); string info.process_name(); // exe basename string info.title(); // window title at snapshot time string info.class_name(); [](https://docs.perception.cx/perception/enma/win-api#enumerate-find) Enumerate / find ------------------------------------------------------------------------------------------- Copy array get_all_hwnds(); int64 find_window(string title); int64 find_window(string title, string class_name); `find_window` returns 0 when no match. [](https://docs.perception.cx/perception/enma/win-api#window-queries) Window queries ----------------------------------------------------------------------------------------- Geometry is split per axis (no array tuples). Combine pos + size for a rect. Copy int64 get_window_width(int64 hwnd); // 0 on invalid hwnd int64 get_window_height(int64 hwnd); // 0 on invalid hwnd vec2 get_window_pos(int64 hwnd); // screen coords; (0,0) on invalid vec2 get_window_size(int64 hwnd); // (width, height) as vec2 bool is_foreground_window(int64 hwnd); bool is_window_active(int64 hwnd); // visible AND not minimized string get_window_title(int64 hwnd); string get_window_class(int64 hwnd); bool set_foreground_window(int64 hwnd); int64 get_window_thread_id(int64 hwnd); // 0 on invalid hwnd int64 get_window_process_id(int64 hwnd); // 0 on invalid hwnd bool post_message(int64 hwnd, int64 msg, int64 wparam, int64 lparam); [](https://docs.perception.cx/perception/enma/win-api#clipboard) Clipboard ------------------------------------------------------------------------------- Copy bool copy_to_clipboard(string text); string copy_from_clipboard(); // empty string when nothing or wrong format `copy_to_clipboard` is gated by perception's restricted-string filter (returns false + logs when blocked). [](https://docs.perception.cx/perception/enma/win-api#keyboard-send) Keyboard SEND --------------------------------------------------------------------------------------- Synthesized via `SendInput`. Restricted virtual keys (set host-side) are blocked + logged. Copy void win_key_down (int64 vk); void win_key_up (int64 vk); void win_key_press(int64 vk, int64 delay_ms); // down + sleep + up; delay capped at 1000ms bool send_char(int64 hwnd, string text); // PostMessageW(WM_CHAR), first wide char only bool send_key (int64 hwnd, int64 vk); // PostMessageW(WM_KEYDOWN+WM_KEYUP) targeted at hwnd [](https://docs.perception.cx/perception/enma/win-api#mouse-send) Mouse SEND --------------------------------------------------------------------------------- Copy void mouse_move (int64 x, int64 y); // absolute screen coords void mouse_move_relative(int64 dx, int64 dy); void mouse_left_click (); // down + 10ms + up void mouse_right_click (); void mouse_middle_click (); void mouse_scroll (int64 amount); // multiples of WHEEL_DELTA void send_mouse_input (int64 dx, int64 dy, int64 flags, int64 mouse_data); // raw SendInput [](https://docs.perception.cx/perception/enma/win-api#example-focus-a-window-and-click-in-it) Example: focus a window and click in it ------------------------------------------------------------------------------------------------------------------------------------------ Copy int64 hwnd = find_window("Notepad"); if (hwnd == 0) return 0; set_foreground_window(hwnd); sleep_ms(50); vec2 pos = get_window_pos(hwnd); vec2 sz = get_window_size(hwnd); mouse_move(pos.x() + sz.x() / 2.0, pos.y() + sz.y() / 2.0); mouse_left_click(); [PreviousUnicorn API](https://docs.perception.cx/perception/enma/unicorn-api) [NextZydis API](https://docs.perception.cx/perception/enma/zydis-api) Last updated 23 days ago --- # Unicorn API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/unicorn-api.md) . All unicorn natives are auto-registered into every loaded script. `cpu_t` is the emulator handle. RAII destructor closes the engine + frees user hooks. Standalone or process-bound (process-bound: unmapped reads pull from `proc.rvm`, writes go to `proc.wvm`). [](https://docs.perception.cx/perception/enma/unicorn-api#constants-exposed-as-enums-no-header-import-needed) Constants — exposed as enums (no header import needed) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Copy uc_reg::rax / rbx / rcx / rdx / rsi / rdi / rbp / rsp uc_reg::r8 .. r15 uc_reg::rip / eflags uc_reg::cs / ds / es / fs / gs / ss uc_reg::fs_base / gs_base / mxcsr uc_reg::xmm0 .. xmm15 uc_reg::ymm0 .. ymm15 uc_prot::none / read / write / exec / rw / rx / rwx / all uc_hook::code // fires per instruction uc_hook::mem_unmapped // fires on read/write/fetch to unmapped page [](https://docs.perception.cx/perception/enma/unicorn-api#construction) Construction ----------------------------------------------------------------------------------------- Copy cpu_t cpu_create(); cpu_t cpu_create_process(proc_t proc, bool allow_writes); * `cpu_create` — standalone emulator, no process backing. * `cpu_create_process` — unmapped reads pull pages from `proc.rvm`; writes go to `proc.wvm` if `allow_writes=true`. Auto-installs read/write/fetch/unmapped hooks plus invalid-instruction + interrupt handlers. The destructor closes the engine and frees any user hooks. [](https://docs.perception.cx/perception/enma/unicorn-api#memory) Memory ----------------------------------------------------------------------------- Copy bool cpu.mem_map (int64 addr, int64 size, uc_prot perms); bool cpu.mem_write(int64 addr, array bytes); array cpu.mem_read (int64 addr, int64 size); // empty array on read failure `addr` and `size` should be page-aligned. `mem_map` returns true on success or already-mapped. `perms` accepts `uc_prot::*` values OR'd together. [](https://docs.perception.cx/perception/enma/unicorn-api#registers) Registers ----------------------------------------------------------------------------------- Copy bool cpu.reg_write64(uc_reg reg, int64 value); int64 cpu.reg_read64 (uc_reg reg); bool cpu.reg_write128(uc_reg reg, array bytes); // XMM, must be exactly 16 bytes array cpu.reg_read128 (uc_reg reg); bool cpu.reg_write256(uc_reg reg, array bytes); // YMM, must be exactly 32 bytes array cpu.reg_read256 (uc_reg reg); [](https://docs.perception.cx/perception/enma/unicorn-api#execution) Execution ----------------------------------------------------------------------------------- Copy int64 cpu.start(int64 begin, int64 end, int64 timeout, int64 count); Emulates from `begin` to `end`. `timeout=0` and `count=0` are unbounded. Returns a `uc_err` code (0 = OK). Copy void cpu.emu_stop(); // halt emulation from inside a hook bool cpu.flush_code(); // drop translation cache (after self-modifying writes) bool cpu.setup_stack(int64 base, int64 size, int64 stop_addr); `setup_stack` maps stack pages, plants a NOP page at `stop_addr` (so a `RET` out lands somewhere mapped), and sets `RSP`. [](https://docs.perception.cx/perception/enma/unicorn-api#hooks-script-callbacks) Hooks (script callbacks) --------------------------------------------------------------------------------------------------------------- Copy bool cpu.hook_add(uc_hook hook_kind, int64 fn_handle); * `fn_handle` = `cast(my_callback)` closure handle. * Callback shape: `int64 cb(int64 addr)`. **Return 0 to stop emulation; non-zero to continue.** The currently-emulating CPU is available as `cpu_active()` from inside the callback — useful for reading/writing state without capturing the handle. Copy cpu_t cpu_active(); // null outside a hook [](https://docs.perception.cx/perception/enma/unicorn-api#exception-inspection) Exception inspection --------------------------------------------------------------------------------------------------------- Copy int64 cpu.get_last_exception(); // NTSTATUS-shaped: 0 = none, 0xC000001D = invalid insn, 0xC0000005 = AV int64 cpu.get_exception_address(); // RIP at the faulting instruction Set when emulation stops due to invalid instruction, AV-style unmapped access, or interrupt. [](https://docs.perception.cx/perception/enma/unicorn-api#example-emulate-mov-rax-0x42-hlt) Example: emulate `mov rax, 0x42; hlt` -------------------------------------------------------------------------------------------------------------------------------------- Copy cpu_t cpu = cpu_create(); cpu.mem_map(0x10000, 0x1000, uc_prot::rwx); cpu.mem_map(0x20000, 0x1000, uc_prot::rw); // Build code: mov rax, 0x42 + hlt (0xF4) zydis_req_t r; r.set_mnemonic(zydis_mnemonic_from_string("mov")); r.set_operand_count(2); r.set_operand_reg(0, zydis_register_from_string("rax")); r.set_operand_imm(1, 0x42); array code = zydis_encode(r); code.push(cast(0xF4)); cpu.mem_write(0x10000, code); cpu.reg_write64(uc_reg::rsp, 0x21000 - 8); cpu.start(0x10000, 0x10000 + code.length(), 1000000, 0); println(cast(cpu.reg_read64(uc_reg::rax))); // "66" (= 0x42) [](https://docs.perception.cx/perception/enma/unicorn-api#lifetime) Lifetime --------------------------------------------------------------------------------- `cpu_t` releases its host resources via the destructor at scope exit. If a script forgets, the host sweeps remaining cpus at unload — engine closed, hooks freed, no permanent leak. [](https://docs.perception.cx/perception/enma/unicorn-api#notes) Notes --------------------------------------------------------------------------- * Hook callbacks fire on the emulating thread (whichever thread called `cpu.start`). Enma's TLS is already set up in that context — heap-alloc, string concat etc. all work. * `cpu_create_process` automatically maps a fake TEB at `0x101000` and a fake / real PEB so guest code reading FS/GS doesn't fault. `gs_base` and `fs_base` are set to the fake TEB. [PreviousSound API](https://docs.perception.cx/perception/enma/sound-api) [NextWin API](https://docs.perception.cx/perception/enma/win-api) Last updated 23 days ago --- # Zydis API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/zydis-api.md) . All Zydis natives are auto-registered into every loaded script. Two handle types: * `zydis_req_t` — single-instruction encoder request (mnemonic + operands). * `zydis_builder_t` — sequence of requests + raw byte chunks; encodes to a flat byte buffer with absolute addressing. Constants are exposed as enums (`zydis_machine_mode::long_64` etc.) so no header import is needed. [](https://docs.perception.cx/perception/enma/zydis-api#zydis_req_t) `zydis_req_t` --------------------------------------------------------------------------------------- Copy zydis_req_t r; // factory: defaults to MODE_LONG_64 r.set_mnemonic(int64 mnemonic); // ZydisMnemonic value (use zydis_mnemonic_from_string) r.set_machine_mode(zydis_machine_mode mode); r.set_operand_count(int64 count); // 0..4 r.set_branch_type (zydis_branch_type type); r.set_branch_width(zydis_branch_width width); r.set_operand_reg(int64 idx, int64 reg); // ZydisRegister value r.set_operand_imm(int64 idx, int64 imm); r.set_operand_mem(int64 idx, int64 base, int64 idx_reg, int64 scale, int64 disp, int64 size); r.set_operand_ptr(int64 idx, int64 segment, int64 offset); int64 r.get_mnemonic(); zydis_machine_mode r.get_machine_mode(); int64 r.get_operand_count(); [](https://docs.perception.cx/perception/enma/zydis-api#encoding) Encoding ------------------------------------------------------------------------------- Copy array zydis_encode (zydis_req_t req); // empty array on failure array zydis_encode_absolute(zydis_req_t req, int64 runtime_rip); // bakes RIP-relative immediates array zydis_nop_fill (int64 length); // minimal NOP padding zydis_req_t zydis_decoded_to_request(array bytes, int64 runtime_rip); `zydis_decoded_to_request` decodes the bytes and returns a fresh request you can mutate and re-encode (useful for instruction patching). [](https://docs.perception.cx/perception/enma/zydis-api#mnemonic-register-name-lookup) Mnemonic / register name lookup --------------------------------------------------------------------------------------------------------------------------- Copy int64 zydis_mnemonic_from_string(string name); // case-insensitive; 0 (INVALID) if no match string zydis_mnemonic_to_string (int64 mnemonic); int64 zydis_register_from_string(string name); // case-insensitive; 0 (NONE) if no match string zydis_register_to_string (int64 reg); [](https://docs.perception.cx/perception/enma/zydis-api#disassembly-textual) Disassembly (textual) ------------------------------------------------------------------------------------------------------- Copy array zydis_disasm(array bytes, int64 runtime_rip); One element per decoded instruction, formatted as Zydis's intel syntax (e.g. `"mov rax, 0x1234"`). Decoding stops at the first invalid byte. For per-operand structure, decode + convert to a `zydis_req_t` via `zydis_decoded_to_request` and read the request fields. [](https://docs.perception.cx/perception/enma/zydis-api#zydis_builder_t) `zydis_builder_t` ----------------------------------------------------------------------------------------------- Builds a sequence of instructions (and raw bytes) into one flat output buffer. Tracks a base address so RIP-relative encoding produces correct offsets. Copy zydis_builder_t b; b.set_machine_mode(zydis_machine_mode mode); b.set_base_address(int64 addr); b.clear(); b.push (zydis_req_t req); b.push_bytes (array bytes); b.push_byte (uint8 b); b.push_u16 (uint16 v); // little-endian b.push_u32 (uint32 v); // little-endian b.push_u64 (uint64 v); // little-endian b.push_nop (int64 count); b.push_int3 (); b.push_ret (); array b.build(); // encode every entry in order int64 b.get_count(); // number of entries [](https://docs.perception.cx/perception/enma/zydis-api#enums-no-header-needed) Enums (no header needed) ------------------------------------------------------------------------------------------------------------- Copy zydis_machine_mode::long_64 / long_compat_32 / long_compat_16 / legacy_32 / legacy_16 / real_16 zydis_branch_type::none / short / near / far zydis_branch_width::none / w8 / w16 / w32 / w64 [](https://docs.perception.cx/perception/enma/zydis-api#example-encode-mov-rax-0x42-then-disasm) Example: encode `mov rax, 0x42`, then disasm -------------------------------------------------------------------------------------------------------------------------------------------------- Copy int64 mov_id = zydis_mnemonic_from_string("mov"); int64 rax_id = zydis_register_from_string("rax"); zydis_req_t r; r.set_mnemonic(mov_id); r.set_operand_count(2); r.set_operand_reg(0, rax_id); r.set_operand_imm(1, 0x42); array bytes = zydis_encode(r); array texts = zydis_disasm(bytes, 0); println(texts.get(0)); // "mov rax, 0x42" [PreviousWin API](https://docs.perception.cx/perception/enma/win-api) [NextMCP API](https://docs.perception.cx/perception/enma/mcp-api) Last updated 23 days ago --- # MCP API | Perception - Enma | Perception AngelScript & Lua API For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt) . This page is also available as [Markdown](https://docs.perception.cx/perception/enma/mcp-api.md) . Perception's MCP server exposes the proc-API surface as JSON-RPC tools that any [Model Context Protocol](https://modelcontextprotocol.io/) client (Claude Code, Cline, Continue, ...) can call. Writing an Enma script? Use [Proc](https://docs.perception.cx/perception/enma/proc-api) / [CPU](https://docs.perception.cx/perception/enma/cpu-api) / [Zydis](https://docs.perception.cx/perception/enma/zydis-api) directly. Driving perception from an AI agent? Enable MCP. [](https://docs.perception.cx/perception/enma/mcp-api#enable) Enable ------------------------------------------------------------------------- In perception, **Settings → Perception MCP**: 1. Type a **Bind port** (1024..65535), or leave blank for OS-pick. 2. Toggle **Enable MCP server** on. 3. Copy the **Bound URL** that appears. The server is loopback-only. ### [](https://docs.perception.cx/perception/enma/mcp-api#other-toggles-in-the-same-panel) Other toggles in the same panel * **Auto-start on perception load** — persisted; on next launch the server starts automatically using the saved port (config loads before the autostart fires, so the saved port is reused rather than regenerated). * **Heap-only scans by default** — controls the default of the `heap_only` flag on `scan_value` / `scan_string` / `scan_pointer_to` / `find_string_refs` when an MCP caller omits it. **On by default.** Flipping it off makes those tools walk the entire user-space when callers don't supply `heap_only`, which can OOM or hang on targets with multi-GiB heaps (Forza-class). [](https://docs.perception.cx/perception/enma/mcp-api#connect) Connect --------------------------------------------------------------------------- **Claude Code:** Copy claude mcp add --transport http perception http://127.0.0.1:/mcp Add `--scope user` for global registration. Other clients (Cline, Continue, ...) accept the same URL via their Streamable HTTP transport. [](https://docs.perception.cx/perception/enma/mcp-api#transport) Transport ------------------------------------------------------------------------------- The server auto-detects two framings on the same port: First bytes Framing Used by `POST` / `GET` / `OPTIONS` / `DELETE` HTTP/1.1 streamable MCP clients anything else Line-delimited JSON-RPC The cpp example below, raw debugging Both carry JSON-RPC 2.0. Real MCP clients use the 5 protocol methods (`initialize` / `notifications/initialized` / `tools/list` / `tools/call` / `ping`); raw clients can call tool methods directly (`"method": "process/list"`). [](https://docs.perception.cx/perception/enma/mcp-api#handles) Handles --------------------------------------------------------------------------- Most tools need a `handle` from `process/reference_by_pid` / `_by_name`. Handles are **per-connection**: * Other connections can't use yours. * Disconnecting releases everything automatically. * Manual release: `process/dereference` (one) or `process/cleanup_references` (all). [](https://docs.perception.cx/perception/enma/mcp-api#permissions) Permissions ----------------------------------------------------------------------------------- Shared with [enma](https://docs.perception.cx/perception/enma/proc-api#permissions) . Toggle in **Scripting → API permissions**: Flag Gates `kernel_rw_access` Kernel-mode addresses in any read / write / disasm / `query_memory_region` / `find_pattern*` call; the `eprocess` field in `process/list` + `info_by_*`; the `ethread` field in `get_threads`; `system/list_drivers` `write_memory` Every tool that writes target memory: `write_virtual_memory`, `write_typed_value`, `write_string`, `copy_memory`, `fill_memory` `virtual_memory_operations` `allocate_memory`, `free_memory` Blocked calls return `-32001` with the missing permission named. [](https://docs.perception.cx/perception/enma/mcp-api#error-codes) Error codes ----------------------------------------------------------------------------------- Code Meaning `-32700` Parse error `-32600` Invalid request `-32601` Method not found `-32602` Invalid params `-32603` Internal `-32001` Permission denied `-32002` Stale / cross-connection handle `-32003` Target not found `-32004` Operation failed [](https://docs.perception.cx/perception/enma/mcp-api#tools) Tools ----------------------------------------------------------------------- 59 tools. Addresses + handles are **hex strings** (`"0x7ff7..."`) — JSON numbers lose precision past 2^53. Required params are listed plain, optional params get `?`. Every "take a handle" tool below has `handle` as its first param — omitted from the params column to keep things readable. The tools that don't take a handle are called out per-section. ### [](https://docs.perception.cx/perception/enma/mcp-api#discovery--reference-lifecycle) Discovery + reference lifecycle Params shown literally (no implicit `handle` — `process/dereference` explicitly takes the handle it's about to release). Tool Params `process/list` — Snapshot of every active process. `process/info_by_pid` `pid` One process by PID. `process/info_by_name` `name` One process by image name. `process/reference_by_pid` `pid` Take a per-connection handle. Returns hex string. `process/reference_by_name` `name` Same, by image name. `process/dereference` `handle` Release one handle. `process/cleanup_references` — Release every handle this connection holds. `process/list_references` — What this connection currently holds. ### [](https://docs.perception.cx/perception/enma/mcp-api#memory-i-o) Memory I/O Tool Params `process/read_virtual_memory` `address`, `size` Raw bytes as hex. Max 16 MiB. `process/write_virtual_memory` `address`, `data` Gated `write_memory`. `data` is hex. `process/is_valid_address` `address` Does the address resolve? `process/read_typed_value` `address`, `type` `type` ∈ `u8..u64 / i8..i64 / f32 / f64 / ptr / bool`. `process/write_typed_value` `address`, `type`, `value` Gated `write_memory`. Use hex string for `value` when type is u64/i64/ptr. `process/read_string` `address`, `max_len?`, `encoding?` `max_len` 1024 default. `encoding` ∈ `auto / ascii / utf16` (default auto-sniff). `process/write_string` `address`, `text`, `encoding?`, `null_terminate?` Gated `write_memory`. `encoding` ∈ `ascii / utf16`. `process/copy_memory` `src_address`, `dst_address`, `size` In-target memcpy. Gated `write_memory`. Max 64 MiB, 1 MiB chunks. `process/fill_memory` `address`, `size`, `byte` Memset. `byte` 0..255 (0x90 = NOP, 0xCC = int3). Gated `write_memory`. `process/read_pointer_chain` `base_address`, `offsets` `offsets` is an int array, max 64. `process/disassemble` `address`, `max_bytes?`, `max_instructions?` Zydis. Defaults 256 / 32. ### [](https://docs.perception.cx/perception/enma/mcp-api#modules-threads-pe) Modules / threads / PE Tool Params `process/get_modules` — All loaded modules. `process/get_threads` — All threads. `process/get_module_by_name` `name` One module by name. `process/get_export_address` `module_base`, `export_name` Single resolve. `process/get_import_address` `module_base`, `import_name` Resolve IAT slot VA. `process/get_module_imports` `module_base` Full IAT walk. `process/list_module_exports` `module_base` Full EAT walk. `process/get_module_sections` `module_base` PE sections. `process/get_pe_header` `module_base` NT/optional header summary. `process/get_module_strings` `module_base`, `min_length?`, `encoding?` `min_length` default 4. `encoding` ∈ `ascii / utf16 / both` (default both). `process/get_exception_table` `module_base`, `max_entries?` x64 RUNTIME\_FUNCTION entries from `.pdata`. Precise function bounds. `process/get_data_directory` `module_base`, `directory` One PE data-dir entry. `directory` ∈ `export / import / resource / exception / security / basereloc / debug / architecture / globalptr / tls / load_config / bound_import / iat / delay_import / com_descriptor` or 0..15. ### [](https://docs.perception.cx/perception/enma/mcp-api#memory-regions--allocation) Memory regions + allocation Tool Params `process/query_memory_region` `address` VirtualQuery-style. `process/enumerate_memory_regions` `heap_only?` All committed regions. `heap_only` default false. `process/allocate_memory` `size` Gated `virtual_memory_operations`. Max 256 MiB. Allocation itself is safe. To execute code from the returned VA the target must have Control Flow Guard (CFG) off; reads + writes are unaffected. `process/free_memory` `address` Same gate. ### [](https://docs.perception.cx/perception/enma/mcp-api#pattern--scanner--xrefs--signature) Pattern + scanner + xrefs + signature Tool Params `process/find_pattern` `start`, `size`, `signature` IDA-style `"AB CD ?? EF"`. `process/find_all_patterns` `start`, `size`, `signature` Same, all hits (cap 1024). `process/scan_value` `type`, `value`, `aligned?`, `heap_only?` `type` ∈ `u8..u64 / i8..i64 / f32 / f64`. Use hex string for `value` when u64/i64. Defaults: aligned true. `heap_only` defaults to the MCP UI's "Heap-only by default" toggle (on by default — skips code/module regions); pass `heap_only=false` to walk full user-space. `process/scan_next` `compare`, `value?`, `min?`, `max?` `compare` ∈ `exact / range / unchanged / changed / increased / decreased`. `value` for `exact`, `min`+`max` for `range`. `process/scan_string` `text`, `encoding?`, `heap_only?` `encoding` ∈ `ascii / utf16`, default ascii. `heap_only` default = UI toggle (see `scan_value`). `process/scan_pointer_to` `target_address`, `heap_only?` Aligned QWORDs pointing at `target_address`. `heap_only` default = UI toggle. `process/find_xrefs` `module_base`, `target_address` Decode `.text`, return refs. `process/find_string_refs` `module_base`, `text`, `encoding?`, `heap_only?`, `string_module?` Combo: scan for the string, then decode `module_base`'s `.text` for code refs to each hit. Phase 1 (string search) defaults to a heap-only VAD walk (`heap_only` follows the UI toggle) and is **pre-capped at 1 GiB** so the listener never crashes on huge targets — if the cap fires the call returns an error asking you to pass `heap_only=true` or set `string_module` (hex VA of the module that owns the string, usually the same as `module_base`) for a fast bounded scan of just that module's image. Phase 2 caps code hits at 4096; response includes a `truncated` flag. `process/generate_signature` `address`, `max_length?` Default 32. `is_unique=false` if length exhausted. `process/diff_memory` `addr_a`, `addr_b`, `size` Cap 1 MiB. ### [](https://docs.perception.cx/perception/enma/mcp-api#code-analysis) Code analysis Tool Params `process/find_function_bounds` `address`, `scan_back?`, `scan_forward?` Defaults 4096 / 65536. Heuristic — use `get_exception_table` for precision. `process/find_function_by_signature` `module_base`, `signature` AOB-scan a module's `.text` + run bounds walk on each hit. `process/analyze_vtable` `vtable_address`, `max_entries?` Default 64. Classifies entries as code/data per loaded modules. `process/read_rtti` `vtable_address` Win64 RTTI: class name + base classes. ### [](https://docs.perception.cx/perception/enma/mcp-api#symbol-function-lookup) Symbol / function lookup Tool Params `process/lookup_symbol` `address` VA → `{module_base, module_name, module_offset, section, nearest_export}`. `process/find_function_by_name` `pattern`, `case_sensitive?`, `max_results?` Substring match across all modules' export tables. Default case-insensitive, 64 results. ### [](https://docs.perception.cx/perception/enma/mcp-api#handles-1) Handles Tool Params `process/enum_handles` `max_entries?` Default 8192. `NtQuerySystemInformation(SystemExtendedHandleInformation)`. ### [](https://docs.perception.cx/perception/enma/mcp-api#system-environment) System / environment Tool Params `system/info` — Build number, page size, processor count + arch. `is_24h2_or_later` flag for build-keyed offsets. No handle. `system/list_drivers` `max_entries?` Kernel modules via `NtQuerySystemInformation(SystemModuleInformation)`. Gated `kernel_rw_access`. No handle. `process/get_command_line` — Reads `PEB.ProcessParameters.CommandLine`. x64 only. `process/list_environment` `max_bytes?` Reads `PEB.ProcessParameters.Environment`. Returns `[{key, value}]`. ### [](https://docs.perception.cx/perception/enma/mcp-api#enma-scripting-bridge) Enma scripting bridge None of these takes a `handle` — they run a script (or return reference text) with its own permissions, independent of any referenced process. Tool Params `script/get_context` — Returns the full enma language + Perception API reference as a single `context` string. **Call this once per session before generating any script** — enma is proprietary and its addon surface can't be inferred from training data. Covers language grammar, all 17 pre-shipped enma addons, and all 12 Perception API surfaces. `script/validate` `source` Compile-only. **All** addons registered (render / proc / cpu / zydis / sound / win / unicorn / net / input / **gui** / **thread** / filesystem). Returns `{ ok, errors:[] }`. `script/execute` `source` Compile + run `main()` once. **GUI and thread addons are NOT registered** — those resources would outlive a one-shot script and leak. For long-lived scripts use the in-app script editor. Returns `{ ok, logs:[] }`. [](https://docs.perception.cx/perception/enma/mcp-api#example-minimal-c-client) Example — minimal C++ client ----------------------------------------------------------------------------------------------------------------- Build with the VS Developer Command Prompt: Copy cl /EHsc /std:c++17 minimal_mcp.cpp /link Ws2_32.lib Copy #define WIN32_LEAN_AND_MEAN #include #include #include #pragma comment(lib, "Ws2_32.lib") int main(int argc, char** argv) { if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } WSADATA wd; WSAStartup(MAKEWORD(2, 2), &wd); SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in a{}; a.sin_family = AF_INET; a.sin_port = htons((u_short)atoi(argv[1])); inet_pton(AF_INET, "127.0.0.1", &a.sin_addr); if (connect(s, (sockaddr*)&a, sizeof(a)) != 0) { printf("connect failed\n"); return 1; } auto call = [&](const char* line) { send(s, line, (int)strlen(line), 0); char buf[8192]; int n = recv(s, buf, sizeof(buf) - 1, 0); if (n > 0) { buf[n] = 0; printf("%s", buf); } }; // 1. List processes. call("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"process/list\",\"params\":{}}\n"); // 2. Reference notepad.exe. call("{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"process/reference_by_name\"," "\"params\":{\"name\":\"notepad.exe\"}}\n"); // 3. Run a one-shot enma script. call("{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"script/execute\"," "\"params\":{\"source\":\"fn main() { println(\\\"hello from mcp\\\"); }\"}}\n"); closesocket(s); WSACleanup(); return 0; } Plain line-delimited JSON-RPC — the server's auto-detect routes us to that framing because we don't open with `POST` / `GET`. For the HTTP path, wrap each request in `POST /mcp HTTP/1.1\r\nContent-Length: N\r\n\r\n` — that's what Claude Code does for you. [PreviousZydis API](https://docs.perception.cx/perception/enma/zydis-api) Last updated 23 days ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/readme.md). # Enma - Overview Enma is perception's proprietary full-module AOT and JIT-compiled scripting language. This site covers the APIs perception registers on top of Enma. For the language itself see \[enma docs\](https://enma-1.gitbook.io/enma). ## What's registered #### Enma Pre-Shipped \* \[Core\](https://enma-1.gitbook.io/enma/addons/core) \* \[String\](https://enma-1.gitbook.io/enma/addons/strings) \* \[Arrays\](https://enma-1.gitbook.io/enma/addons/arrays) \* \[Maps\](https://enma-1.gitbook.io/enma/addons/maps) \* \[Math\](https://enma-1.gitbook.io/enma/addons/math) \* \[3D Math (quat + mat4)\](https://enma-1.gitbook.io/enma/addons/math3d) \* \[SIMD\](https://enma-1.gitbook.io/enma/addons/simd) \* \[Variant\](https://enma-1.gitbook.io/enma/addons/variant) \* \[Atomic\](https://enma-1.gitbook.io/enma/addons/atomic) \* \[Bits\](https://enma-1.gitbook.io/enma/addons/bits) \* \[Time\](https://enma-1.gitbook.io/enma/addons/time) \* \[Regex\](https://enma-1.gitbook.io/enma/addons/regex) \* \[Thread\](https://enma-1.gitbook.io/enma/addons/thread) \* \[Vectors\](https://enma-1.gitbook.io/enma/addons/vec) \* \[Hash Set\](https://enma-1.gitbook.io/enma/addons/hash\_set) \* \[Sorted Map\](https://enma-1.gitbook.io/enma/addons/sorted\_map) \* \[List\](https://enma-1.gitbook.io/enma/addons/list) \* \[JSON\](https://enma-1.gitbook.io/enma/addons/json) #### \*\*Perception API\*\* \* \[Lifecycle and Routines\](/perception/enma/lifecycle-and-routines.md) \* \[Render\](/perception/enma/render-api.md) \* \[Proc\](/perception/enma/proc-api.md) \* \[CPU\](/perception/enma/cpu-api.md) \* \[Filesystem\](/perception/enma/filesystem-api.md) \* \[Sound\](/perception/enma/sound-api.md) \* \[Zydis\](/perception/enma/zydis-api.md) \* \[Win\](/perception/enma/win-api.md) \* \[Input\](/perception/enma/input-api.md) \* \[Unicorn\](/perception/enma/unicorn-api.md) \* \[Net\](/perception/enma/net-api.md) \* \[GUI\](/perception/enma/gui-api.md) #### AI agent surface \* \[MCP\](/perception/enma/mcp-api.md) — JSON-RPC over local TCP / HTTP for Claude Code, Cline, etc. ## Minimal example \`\`\`cpp int64 g\_tick; void my\_draw(int64 data) { g\_tick = g\_tick + 1; color white = color(255, 255, 255, 255); color noeffect = color(0, 0, 0, 0); string text = "tick=" + cast(g\_tick); draw\_text(text, vec2(40.0, 40.0), white, get\_font20(), 0, noeffect, 0.0); } int64 main() { g\_tick = 0; register\_routine(cast(my\_draw), 0); return 1; } \`\`\` See \[Lifecycle and Routines\](/perception/enma/lifecycle-and-routines.md) for the entry point, return-value semantics, and how routines tick. ## Conventions \* \*\*Colors and positions\*\*: always wrap. \`color(255, 255, 255, 255)\`, \`vec2(10.0, 20.0)\`. Freshly constructed each frame is fine; Enma drops the temporaries at scope exit. \* \*\*Float32 literals\*\*: \`0.2f\`, not \`cast(0.2)\`. Required for vertex buffers. \* \*\*Handles\*\*: all \`create\_\*\` / \`load\_\*\` natives return an encrypted \`int64\`. Pass it back into draw / bind / destroy. Don't inspect. ## SDK Perception's Enma SDK is not public yet. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/readme.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/zydis-api.md). # Zydis API All Zydis natives are auto-registered into every loaded script. Two handle types: \* \`zydis\_req\_t\` — single-instruction encoder request (mnemonic + operands). \* \`zydis\_builder\_t\` — sequence of requests + raw byte chunks; encodes to a flat byte buffer with absolute addressing. Constants are exposed as enums (\`zydis\_machine\_mode::long\_64\` etc.) so no header import is needed. ## \`zydis\_req\_t\` \`\`\`cpp zydis\_req\_t r; // factory: defaults to MODE\_LONG\_64 r.set\_mnemonic(int64 mnemonic); // ZydisMnemonic value (use zydis\_mnemonic\_from\_string) r.set\_machine\_mode(zydis\_machine\_mode mode); r.set\_operand\_count(int64 count); // 0..4 r.set\_branch\_type (zydis\_branch\_type type); r.set\_branch\_width(zydis\_branch\_width width); r.set\_operand\_reg(int64 idx, int64 reg); // ZydisRegister value r.set\_operand\_imm(int64 idx, int64 imm); r.set\_operand\_mem(int64 idx, int64 base, int64 idx\_reg, int64 scale, int64 disp, int64 size); r.set\_operand\_ptr(int64 idx, int64 segment, int64 offset); int64 r.get\_mnemonic(); zydis\_machine\_mode r.get\_machine\_mode(); int64 r.get\_operand\_count(); \`\`\` ## Encoding \`\`\`cpp array zydis\_encode (zydis\_req\_t req); // empty array on failure array zydis\_encode\_absolute(zydis\_req\_t req, int64 runtime\_rip); // bakes RIP-relative immediates array zydis\_nop\_fill (int64 length); // minimal NOP padding zydis\_req\_t zydis\_decoded\_to\_request(array bytes, int64 runtime\_rip); \`\`\` \`zydis\_decoded\_to\_request\` decodes the bytes and returns a fresh request you can mutate and re-encode (useful for instruction patching). ## Mnemonic / register name lookup \`\`\`cpp int64 zydis\_mnemonic\_from\_string(string name); // case-insensitive; 0 (INVALID) if no match string zydis\_mnemonic\_to\_string (int64 mnemonic); int64 zydis\_register\_from\_string(string name); // case-insensitive; 0 (NONE) if no match string zydis\_register\_to\_string (int64 reg); \`\`\` ## Disassembly (textual) \`\`\`cpp array zydis\_disasm(array bytes, int64 runtime\_rip); \`\`\` One element per decoded instruction, formatted as Zydis's intel syntax (e.g. \`"mov rax, 0x1234"\`). Decoding stops at the first invalid byte. For per-operand structure, decode + convert to a \`zydis\_req\_t\` via \`zydis\_decoded\_to\_request\` and read the request fields. ## \`zydis\_builder\_t\` Builds a sequence of instructions (and raw bytes) into one flat output buffer. Tracks a base address so RIP-relative encoding produces correct offsets. \`\`\`cpp zydis\_builder\_t b; b.set\_machine\_mode(zydis\_machine\_mode mode); b.set\_base\_address(int64 addr); b.clear(); b.push (zydis\_req\_t req); b.push\_bytes (array bytes); b.push\_byte (uint8 b); b.push\_u16 (uint16 v); // little-endian b.push\_u32 (uint32 v); // little-endian b.push\_u64 (uint64 v); // little-endian b.push\_nop (int64 count); b.push\_int3 (); b.push\_ret (); array b.build(); // encode every entry in order int64 b.get\_count(); // number of entries \`\`\` ## Enums (no header needed) \`\`\`cpp zydis\_machine\_mode::long\_64 / long\_compat\_32 / long\_compat\_16 / legacy\_32 / legacy\_16 / real\_16 zydis\_branch\_type::none / short / near / far zydis\_branch\_width::none / w8 / w16 / w32 / w64 \`\`\` ## Example: encode \`mov rax, 0x42\`, then disasm \`\`\`cpp int64 mov\_id = zydis\_mnemonic\_from\_string("mov"); int64 rax\_id = zydis\_register\_from\_string("rax"); zydis\_req\_t r; r.set\_mnemonic(mov\_id); r.set\_operand\_count(2); r.set\_operand\_reg(0, rax\_id); r.set\_operand\_imm(1, 0x42); array bytes = zydis\_encode(r); array texts = zydis\_disasm(bytes, 0); println(texts.get(0)); // "mov rax, 0x42" \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/zydis-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/unicorn-api.md). # Unicorn API All unicorn natives are auto-registered into every loaded script. \`cpu\_t\` is the emulator handle. RAII destructor closes the engine + frees user hooks. Standalone or process-bound (process-bound: unmapped reads pull from \`proc.rvm\`, writes go to \`proc.wvm\`). ## Constants — exposed as enums (no header import needed) \`\`\`cpp uc\_reg::rax / rbx / rcx / rdx / rsi / rdi / rbp / rsp uc\_reg::r8 .. r15 uc\_reg::rip / eflags uc\_reg::cs / ds / es / fs / gs / ss uc\_reg::fs\_base / gs\_base / mxcsr uc\_reg::xmm0 .. xmm15 uc\_reg::ymm0 .. ymm15 uc\_prot::none / read / write / exec / rw / rx / rwx / all uc\_hook::code // fires per instruction uc\_hook::mem\_unmapped // fires on read/write/fetch to unmapped page \`\`\` ## Construction \`\`\`cpp cpu\_t cpu\_create(); cpu\_t cpu\_create\_process(proc\_t proc, bool allow\_writes); \`\`\` \* \`cpu\_create\` — standalone emulator, no process backing. \* \`cpu\_create\_process\` — unmapped reads pull pages from \`proc.rvm\`; writes go to \`proc.wvm\` if \`allow\_writes=true\`. Auto-installs read/write/fetch/unmapped hooks plus invalid-instruction + interrupt handlers. The destructor closes the engine and frees any user hooks. ## Memory \`\`\`cpp bool cpu.mem\_map (int64 addr, int64 size, uc\_prot perms); bool cpu.mem\_write(int64 addr, array bytes); array cpu.mem\_read (int64 addr, int64 size); // empty array on read failure \`\`\` \`addr\` and \`size\` should be page-aligned. \`mem\_map\` returns true on success or already-mapped. \`perms\` accepts \`uc\_prot::\*\` values OR'd together. ## Registers \`\`\`cpp bool cpu.reg\_write64(uc\_reg reg, int64 value); int64 cpu.reg\_read64 (uc\_reg reg); bool cpu.reg\_write128(uc\_reg reg, array bytes); // XMM, must be exactly 16 bytes array cpu.reg\_read128 (uc\_reg reg); bool cpu.reg\_write256(uc\_reg reg, array bytes); // YMM, must be exactly 32 bytes array cpu.reg\_read256 (uc\_reg reg); \`\`\` ## Execution \`\`\`cpp int64 cpu.start(int64 begin, int64 end, int64 timeout, int64 count); \`\`\` Emulates from \`begin\` to \`end\`. \`timeout=0\` and \`count=0\` are unbounded. Returns a \`uc\_err\` code (0 = OK). \`\`\`cpp void cpu.emu\_stop(); // halt emulation from inside a hook bool cpu.flush\_code(); // drop translation cache (after self-modifying writes) bool cpu.setup\_stack(int64 base, int64 size, int64 stop\_addr); \`\`\` \`setup\_stack\` maps stack pages, plants a NOP page at \`stop\_addr\` (so a \`RET\` out lands somewhere mapped), and sets \`RSP\`. ## Hooks (script callbacks) \`\`\`cpp bool cpu.hook\_add(uc\_hook hook\_kind, int64 fn\_handle); \`\`\` \* \`fn\_handle\` = \`cast(my\_callback)\` closure handle. \* Callback shape: \`int64 cb(int64 addr)\`. \*\*Return 0 to stop emulation; non-zero to continue.\*\* The currently-emulating CPU is available as \`cpu\_active()\` from inside the callback — useful for reading/writing state without capturing the handle. \`\`\`cpp cpu\_t cpu\_active(); // null outside a hook \`\`\` ## Exception inspection \`\`\`cpp int64 cpu.get\_last\_exception(); // NTSTATUS-shaped: 0 = none, 0xC000001D = invalid insn, 0xC0000005 = AV int64 cpu.get\_exception\_address(); // RIP at the faulting instruction \`\`\` Set when emulation stops due to invalid instruction, AV-style unmapped access, or interrupt. ## Example: emulate \`mov rax, 0x42; hlt\` \`\`\`cpp cpu\_t cpu = cpu\_create(); cpu.mem\_map(0x10000, 0x1000, uc\_prot::rwx); cpu.mem\_map(0x20000, 0x1000, uc\_prot::rw); // Build code: mov rax, 0x42 + hlt (0xF4) zydis\_req\_t r; r.set\_mnemonic(zydis\_mnemonic\_from\_string("mov")); r.set\_operand\_count(2); r.set\_operand\_reg(0, zydis\_register\_from\_string("rax")); r.set\_operand\_imm(1, 0x42); array code = zydis\_encode(r); code.push(cast(0xF4)); cpu.mem\_write(0x10000, code); cpu.reg\_write64(uc\_reg::rsp, 0x21000 - 8); cpu.start(0x10000, 0x10000 + code.length(), 1000000, 0); println(cast(cpu.reg\_read64(uc\_reg::rax))); // "66" (= 0x42) \`\`\` ## Lifetime \`cpu\_t\` releases its host resources via the destructor at scope exit. If a script forgets, the host sweeps remaining cpus at unload — engine closed, hooks freed, no permanent leak. ## Notes \* Hook callbacks fire on the emulating thread (whichever thread called \`cpu.start\`). Enma's TLS is already set up in that context — heap-alloc, string concat etc. all work. \* \`cpu\_create\_process\` automatically maps a fake TEB at \`0x101000\` and a fake / real PEB so guest code reading FS/GS doesn't fault. \`gs\_base\` and \`fs\_base\` are set to the fake TEB. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/unicorn-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/proc-api.md). # Proc API All proc natives are auto-registered into every loaded script. \`proc\_t\` is a value-type handle. Construct it via \`ref\_process(...)\`; the host ref is released automatically when the variable goes out of scope. Some natives are gated by permission flags toggled host-side. Gated calls log and return 0 / false when blocked. See \[Permissions\](#permissions). \*\*Address type:\*\* all addresses are \`uint64\`. Pick \`uint64\` for any locals that hold an address — \`uint64 base = p.base\_address();\` — and the rest of the chain stays cast-free. Mixing \`int64\` addresses requires \`cast(...)\`. ## \`proc\_t\` \`\`\`cpp proc\_t ref\_process(uint32 pid); proc\_t ref\_process(string name); \`\`\` Returns an alive handle on success, a null one on failure. Verify with \`.alive()\`. ## Identity \`\`\`cpp uint64 proc.base\_address(); uint64 proc.peb(); uint32 proc.pid(); bool proc.alive(); bool proc.is\_valid\_address(uint64 addr); uint64 proc.get\_eprocess(); // gated: kernel\_rw\_access — see below \`\`\` \`get\_eprocess\` returns the target's EPROCESS kernel address. Gated behind the \`kernel\_rw\_access\` permission — returns \`0\` and logs when the script doesn't hold it. Use cases: passing the EPROCESS to a custom kernel routine, walking kernel structures the proc API doesn't already expose, etc. ## Read primitives \`\`\`cpp uint8/16/32/64 proc.ru8/ru16/ru32/ru64(uint64 addr); int8/16/32/64 proc.r8/r16/r32/r64 (uint64 addr); float32 proc.rf32(uint64 addr); float64 proc.rf64(uint64 addr); string proc.rs (uint64 addr, int32 max\_chars); // null-terminated UTF-8, cap 8192 string proc.rws(uint64 addr, int32 max\_chars); // UTF-16, returns UTF-8, cap 8192 \`\`\` All return 0 / empty on failure or out-of-range address. By default, addresses must be usermode. When the script holds \`kernel\_rw\_access\`, \*safe\* kernel addresses are also accepted — see \[Permissions\](#permissions). ## Write primitives (gated: \`write\_memory\`) \`\`\`cpp bool proc.wu8/wu16/wu32/wu64(uint64 addr, uintN v); bool proc.w8/w16/w32/w64 (uint64 addr, intN v); bool proc.wf32(uint64 addr, float32 v); bool proc.wf64(uint64 addr, float64 v); bool proc.ws (uint64 addr, string text); // UTF-8 bytes bool proc.wws(uint64 addr, string text); // converts UTF-8 to UTF-16 \`\`\` ## Bulk read/write \`\`\`cpp array proc.rvm(uint64 addr, uint64 size); // length = bytes actually read bool proc.wvm(uint64 addr, array bytes); // gated: write\_memory \`\`\` ## Typed reads / writes (vec / quat / mat) Read a \`vec2\`/\`vec3\`/\`vec4\`/\`quat\`/\`mat4\` directly from process memory. \`\_fl32\` reads source bytes as float32 (promoted to float64 in the result); \`\_fl64\` reads source bytes as float64. \`mat4\` is a row-major 4x4. \`quat\` is \`x, y, z, w\` packed. \`\`\`cpp vec2 proc.read\_vec2\_fl32(uint64 addr); vec2 proc.read\_vec2\_fl64(uint64 addr); vec3 proc.read\_vec3\_fl32(uint64 addr); vec3 proc.read\_vec3\_fl64(uint64 addr); vec4 proc.read\_vec4\_fl32(uint64 addr); vec4 proc.read\_vec4\_fl64(uint64 addr); quat proc.read\_quat\_fl32(uint64 addr); quat proc.read\_quat\_fl64(uint64 addr); mat4 proc.read\_mat4\_fl32(uint64 addr); mat4 proc.read\_mat4\_fl64(uint64 addr); \`\`\` Writes mirror the reads (gated: \`write\_memory\`): \`\`\`cpp bool proc.write\_vec2\_fl32(uint64 addr, vec2 v); bool proc.write\_vec2\_fl64(uint64 addr, vec2 v); bool proc.write\_vec3\_fl32(uint64 addr, vec3 v); bool proc.write\_vec3\_fl64(uint64 addr, vec3 v); bool proc.write\_vec4\_fl32(uint64 addr, vec4 v); bool proc.write\_vec4\_fl64(uint64 addr, vec4 v); bool proc.write\_quat\_fl32(uint64 addr, quat q); bool proc.write\_quat\_fl64(uint64 addr, quat q); bool proc.write\_mat4\_fl32(uint64 addr, mat4 m); bool proc.write\_mat4\_fl64(uint64 addr, mat4 m); \`\`\` Reads return the value directly: \`\`\`cpp proc\_t p = ref\_process("game.exe"); vec3 cam\_pos = p.read\_vec3\_fl32(p.base\_address() + 0x10A4830); println("camera at " + cast(cam\_pos.x) + "," + cast(cam\_pos.y)); \`\`\` Failed reads (bad address, kernel-RW gate denial, dead proc handle) return a zero-initialized value of the right type — chained \`.x\` / \`.m\[i\]\` stays safe instead of AVing through null. Writes return \`false\` on the same failure cases. Same kernel-RW gate semantics as the rest of the proc API — see \[Permissions\](#permissions). ## SIMD-width reads/writes \`\`\`cpp array proc.r128(uint64 addr); // 16 bytes array proc.r256(uint64 addr); // 32 bytes array proc.r512(uint64 addr); // 64 bytes bool proc.w128(uint64 addr, array bytes); // gated: write\_memory bool proc.w256(uint64 addr, array bytes); // gated: write\_memory bool proc.w512(uint64 addr, array bytes); // gated: write\_memory \`\`\` ## Modules and exports \`\`\`cpp uint64 proc.get\_module\_base(string name); // 0 if missing uint64 proc.get\_module\_size(string name); // 0 if missing array proc.get\_module\_list(); // every loaded module uint64 proc.get\_proc\_address(uint64 module\_base, string export\_name); uint64 proc.get\_import\_rdata\_address(uint64 module\_base, string import\_name); \`\`\` \`module\_info\_t\` methods: \`\`\`cpp string m.name(); // base DLL filename, e.g. "kernel32.dll" uint64 m.base(); // DllBase uint64 m.size(); // SizeOfImage \`\`\` Example — list every module loaded in the target: \`\`\`cpp array mods = p.get\_module\_list(); for (module\_info\_t m : mods) { println(format("{s} base=0x{x} size=0x{x}", m.name(), m.base(), m.size())); } \`\`\` ## Pattern scanning \`\`\`cpp uint64 proc.find\_code\_pattern (uint64 search\_start, uint64 search\_size, string sig); array proc.find\_all\_code\_patterns(uint64 search\_start, uint64 search\_size, string sig); \`\`\` Sig syntax: hex bytes separated by spaces, \`??\` is a wildcard. Example: \`"48 8B ?? ?? 48 89"\`. ## Threads \`\`\`cpp array proc.get\_all\_tebs(); \`\`\` ## Pointer arrays \`\`\`cpp array proc.read\_pointer\_array(uint64 base, int64 count, int64 offset\_delta); \`\`\` Reads \`count\` consecutive \`uint64\`s starting at \`base\`. \`offset\_delta\` is added to each value before storing (useful when the target stores relative offsets). ## VAD / virtual\\\_query Both calls \*\*exclude PE-image regions\*\* (modules, exes). Use \`get\_module\_base/size\` for those. \`\`\`cpp vad\_region\_t proc.virtual\_query(uint64 address); array proc.get\_vad\_snapshot(bool heap\_likely\_only); \`\`\` \`virtual\_query\` returns a \`vad\_region\_t\` handle on hit, \`0\` on miss. ### \`vad\_region\_t\` \`\`\`cpp uint64 region.start(); uint64 region.size(); uint64 region.protection(); // host page-protection bits (PAGE\_READWRITE, PAGE\_EXECUTE, etc.) bool region.heap\_likely(); // host's heuristic for heap allocations \`\`\` \`\`\`cpp array snap = p.get\_vad\_snapshot(false); for (int64 i = 0; i < snap.length(); i = i + 1) { vad\_region\_t r = snap.get(i); uint64 start = r.start(); uint64 size = r.size(); uint64 prot = r.protection(); bool heap = r.heap\_likely(); } \`\`\` ## Memory scans All scans walk the VAD snapshot (so module memory is excluded — same caveat as above). \`heap\_only=true\` restricts to heap-likely regions. \`\`\`cpp array proc.scan\_string (string text, bool heap\_only); array proc.scan\_wstring(string text, bool heap\_only); // text is UTF-8, converted to UTF-16 array proc.scan\_pointer(uint64 target, bool heap\_only); array proc.scan\_u64 (uint64 value, bool heap\_only); array proc.scan\_u32 (uint32 value, bool heap\_only); array proc.scan\_float (float32 value, bool heap\_only); array proc.scan\_double (float64 value, bool heap\_only); \`\`\` ## VM alloc / free (gated: \`virtual\_memory\_operations\`) \`\`\`cpp uint64 proc.alloc\_vm(uint64 size); // 0 on failure bool proc.free\_vm (uint64 address); \`\`\` Allocation itself is safe. To execute code from the returned page, the target must have Control Flow Guard (CFG) disabled — CFG kills the process on indirect calls/jumps to non-bitmap addresses. Reads + writes are unaffected. ## Permissions Three flags gate sensitive operations. All default to off; the user grants them per script via the host UI. | Flag | Gates | | --------------------------- | ------------------------------------------------------------------------------------- | | \`write\_memory\` | \`wu\*\`, \`w\*\`, \`wf\*\`, \`ws\`, \`wws\`, \`wvm\`, \`w128/256/512\` | | \`virtual\_memory\_operations\` | \`alloc\_vm\`, \`free\_vm\` | | \`kernel\_rw\_access\` | \`get\_eprocess\`; expands every other read/write to also accept \*safe\* kernel addresses | When a gated call runs without permission it logs \`\[ENMA\] ... blocked: '' permission not granted\` and returns 0 / false. ### \`kernel\_rw\_access\` semantics Without it, every read/write address must pass \`is\_usermode\_address\` — i.e. canonical user-range, non-null, non-tiny. This is the default and matches the original behavior. With it, addresses are accepted when \*\*either\*\*: \* The address is a valid usermode address (same check as before), \*\*or\*\* \* The address is a \*safe kernel address\* — canonical kernel range AND not in any host-protected critical region (the host's own EPROCESS / ETHREAD / kernel state used for privilege escalation). The "safe kernel" denylist is enforced by \`is\_safe\_kernel\_address\` in the host. Scripts can't bypass it: a kernel write to a denied address returns \`false\` and logs, just like any other refused op. Use this flag when a script genuinely needs to inspect or modify kernel structures of the target process (Win32 thread state, KPCR fields, driver-side game state, etc.). Don't grant it casually — kernel writes to the wrong address bugcheck the box. ## Lifetime and cleanup \`proc\_t\` releases its host ref via the destructor when the variable goes out of scope. If a script forgets (e.g. leaks a \`proc\_t\*\` heap-allocation), the host sweeps remaining refs at script unload — no permanent leak. \`\`\`cpp int64 main() { proc\_t p = ref\_process("notepad.exe"); if (!p.alive()) return 0; uint64 base = p.base\_address(); println(cast(p.r32(base + 0x3C))); // e\_lfanew return 0; // p drops here; host ref released } \`\`\` ## Conventions \* \*\*Addresses are \`uint64\`.\*\* Use \`uint64\` for any local that holds an address — hex literals like \`0x7FF000000000\` work directly. Mixing in an \`int64\` requires \`cast(...)\`. \* \*\*Failed reads return 0\*\*, not an exception. Check \`is\_valid\_address\` first if you need certainty. \* \*\*Strings returned by \`rs\`/\`rws\`\*\* are heap strings — drop normally at scope exit. \* \*\*Array returns are length-correct.\*\* \`arr.length()\` is the actual element count, not a max. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/proc-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/sound-api.md). # Sound API All sound natives are auto-registered into every loaded script. Two handle types: \* \`sound\_t\` — a loaded resource. Multiple instances can play from one resource concurrently. \* \`sound\_inst\_t\` — a live playback instance. Returned by \`sound.play(...)\`. Both are \`int64\`-backed handles with auto-cleanup destructors. Resources are tracked per-script; an unload sweep frees anything the user forgot to drop. ## Load / unload \`\`\`cpp sound\_t load\_sound(string relative\_path); \`\`\` Loads \`/\`. Path is validated: \* No \`..\` segments. \* No \`:\` (drive letters), \`\\n\`, \`\\r\`. \* Cannot start with \`/\` or \`\\\` (must be relative). Returns a null handle on validation failure or read failure. The destructor frees the resource. ## Playback \`\`\`cpp sound\_inst\_t sound.play(float64 volume, float64 pan, bool loop); \`\`\` \* \`volume\`: 0.0 .. 1.0 (clamped) \* \`pan\`: -1.0 (full left) .. 1.0 (full right) (clamped) \* \`loop\`: repeat forever until stopped ## Instance control \`\`\`cpp bool sound\_inst.is\_playing(); void sound\_inst.stop(); void sound\_inst.set\_volume(float64 v); // 0..1 void sound\_inst.set\_pan(float64 p); // -1..1 \`\`\` ## Globals \`\`\`cpp void stop\_all\_sounds(); // halts every instance globally \`\`\` ## Example \`\`\`cpp int64 main() { sound\_t snd = load\_sound("sounds/notification.wav"); if (cast(snd) == 0) return 0; sound\_inst\_t inst = snd.play(0.5, 0.0, false); while (inst.is\_playing()) sleep\_ms(50); return 1; // snd / inst drop here; resource + instance freed } \`\`\` ## Lifetime \`sound\_t\` and \`sound\_inst\_t\` both release at scope exit. If the script forgets, the host sweeps remaining handles at unload. No permanent leak. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/sound-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/render-api.md). # Render API All render natives are auto-registered into every loaded script. Handles (\`int64\`) are encrypted pointers. Pass them back into other render calls. Don't dereference or arithmetic them. ## \`color\` type \`color\` is a source-level module. Opt in with \`import "color";\`: \`\`\`enma import "vec"; import "color"; int64 main() { color red = color(255, 0, 0, 255); draw\_rect\_filled(vec2(10.0, 10.0), vec2(100.0, 50.0), red, 4.0, 15); return 0; } \`\`\` \`color\` is a \`\[\[packed\]\]\` 4-byte struct with \`r\` / \`g\` / \`b\` / \`a\` fields plus \`with\_alpha(uint8 \_a)\` to copy with a different alpha. Non-escaping locals are stack-allocated; the byte layout matches the native \`pixelcolor4\` so every \`draw\_\*\` call reads them directly. Read fields directly: \`c.r\`, \`c.g\`, \`c.b\`, \`c.a\` (each returns \`uint8\`). ## 2D primitives \`\`\`cpp int64 draw\_rect(vec2 pos, vec2 size, color c, float64 thickness, float64 rounding, uint8 rounding\_flags); int64 draw\_rect\_filled(vec2 pos, vec2 size, color c, float64 rounding, uint8 rounding\_flags); int64 draw\_line(vec2 a, vec2 b, color c, float64 thickness); int64 draw\_circle(vec2 center, float64 radius, color c, float64 thickness, bool filled); int64 draw\_arc(vec2 center, vec2 radii, float64 start\_deg, float64 sweep\_deg, color c, float64 thickness, bool filled); int64 draw\_triangle(vec2 a, vec2 b, vec2 c, color col, float64 thickness, bool filled); int64 draw\_four\_corner\_gradient(vec2 pos, vec2 size, color tl, color tr, color bl, color br, float64 rounding); int64 draw\_polygon(array xy\_pairs, uint32 count\_pairs, color c, float64 thickness, bool filled); int64 draw\_bitmap(int64 bmp, vec2 pos, vec2 size, color tint, bool rounded); int64 draw\_text(string text, vec2 pos, color c, int64 font, int32 effect, color effect\_color, float64 effect\_amount); \`\`\` \`effect\`: 0=none, 1=shadow, 2=outline. \`rounding\_flags\`: bitmask of which corners to round (ImGui-style, \`15\` = all corners). ## Text and fonts \`\`\`cpp float64 get\_text\_width(int64 font, string text, int32 maxw, int32 maxh); float64 get\_text\_height(int64 font, string text, int32 maxw, int32 maxh); int32 get\_char\_advance(int64 font, uint32 wchar32); int64 create\_font(string path, float64 size, bool antialias, bool load\_color, array glyph\_ranges); int64 create\_font\_mem(string label, float64 size, array buf, bool antialias, bool load\_color, array glyph\_ranges); int64 create\_bitmap(array data); int64 get\_font18(); int64 get\_font20(); int64 get\_font24(); int64 get\_font28(); \`\`\` \`create\_font\` first tries the path as-is, then retries under perception's main dir. \`glyph\_ranges\` may be an empty array. ## Clipping \`\`\`cpp int64 clip\_push(vec2 pos, vec2 size); int64 clip\_pop(); \`\`\` ## Viewport \`\`\`cpp float64 get\_view\_width(); float64 get\_view\_height(); float64 get\_view\_scale(); float64 get\_fps(); \`\`\` ## Shaders \`\`\`cpp int64 create\_shader(string vs\_source, string ps\_source, string layout); int64 destroy\_shader(int64 shader); int64 create\_compute\_shader(string cs\_source); int64 destroy\_compute\_shader(int64 cs); \`\`\` Layout format: \`"SEMANTIC:INDEX:TYPE, ..."\`. Example: \`"POSITION:0:FLOAT2, COLOR:0:FLOAT4"\`. Types: \`FLOAT1\`, \`FLOAT2\`, \`FLOAT3\`, \`FLOAT4\`, \`BYTE4\` (unorm), \`UINT1\`. ## Buffers \`\`\`cpp int64 create\_vertex\_buffer(uint32 stride, uint32 max\_vertices, bool dynamic); int64 destroy\_vertex\_buffer(int64 vb); int64 create\_index\_buffer(uint32 max\_indices, bool use\_32bit, bool dynamic); int64 destroy\_index\_buffer(int64 ib); int64 create\_constant\_buffer(uint32 size); int64 destroy\_constant\_buffer(int64 cb); int64 create\_structured\_buffer(uint32 element\_size, uint32 element\_count, bool cpu\_write, bool gpu\_write); int64 destroy\_structured\_buffer(int64 sb); \`\`\` ## Pipeline state \`\`\`cpp int64 create\_blend\_state(int32 src, int32 dst, int32 op, int32 src\_alpha, int32 dst\_alpha, int32 op\_alpha); int64 destroy\_blend\_state(int64 bs); int64 create\_sampler(int32 filter, int32 address\_u, int32 address\_v); int64 destroy\_sampler(int64 s); int64 create\_depth\_stencil\_state(bool depth\_enable, bool depth\_write, int32 compare\_func); int64 destroy\_depth\_stencil\_state(int64 ds); int64 create\_rasterizer\_state(int32 cull\_mode, int32 fill\_mode, bool scissor\_enable); int64 destroy\_rasterizer\_state(int64 rs); \`\`\` Enum values (all \`int32\`): \* \`blend\_factor\`: 0=ZERO, 1=ONE, 2=SRC\\\_ALPHA, 3=INV\\\_SRC\\\_ALPHA, 4=DEST\\\_ALPHA, 5=INV\\\_DEST\\\_ALPHA, 6=SRC\\\_COLOR, 7=INV\\\_SRC\\\_COLOR, 8=DEST\\\_COLOR, 9=INV\\\_DEST\\\_COLOR. \* \`blend\_op\`: 0=ADD, 1=SUBTRACT, 2=REV\\\_SUBTRACT, 3=MIN, 4=MAX. \* \`filter\`: 0=POINT, 1=LINEAR, 2=ANISOTROPIC. \* \`address\`: 0=WRAP, 1=CLAMP, 2=MIRROR, 3=BORDER. \* \`compare\_func\`: 0=NEVER, 1=LESS, 2=EQUAL, 3=LESS\\\_EQUAL, 4=GREATER, 5=NOT\\\_EQUAL, 6=GREATER\\\_EQUAL, 7=ALWAYS. ## Render targets and textures \`\`\`cpp int64 create\_render\_target(uint32 width, uint32 height); int64 destroy\_render\_target(int64 rt); int64 create\_depth\_buffer(uint32 width, uint32 height); int64 destroy\_depth\_buffer(int64 db); int64 create\_texture(uint32 width, uint32 height, array rgba\_data); int64 destroy\_texture(int64 tex); int64 load\_texture(string path); int64 load\_texture\_mem(array data); float64 get\_texture\_width(int64 tex); float64 get\_texture\_height(int64 tex); \`\`\` \`create\_texture\` wants \`width \* height \* 4\` bytes of RGBA. ## Meshes \`\`\`cpp int64 create\_mesh\_raw(array vertex\_data, uint32 vertex\_count, uint32 stride, array index\_data, uint32 index\_count, bool use\_32bit); int64 load\_mesh(string path); int64 load\_mesh\_mem(array data); int64 destroy\_mesh(int64 mesh); int64 get\_mesh\_vert\_count(int64 mesh); int64 get\_mesh\_index\_count(int64 mesh); float64 get\_mesh\_stride(int64 mesh); float64 get\_mesh\_bounds\_min\_x(int64 mesh); float64 get\_mesh\_bounds\_min\_y(int64 mesh); float64 get\_mesh\_bounds\_min\_z(int64 mesh); float64 get\_mesh\_bounds\_max\_x(int64 mesh); float64 get\_mesh\_bounds\_max\_y(int64 mesh); float64 get\_mesh\_bounds\_max\_z(int64 mesh); \`\`\` ## Custom draw \`\`\`cpp int64 custom\_draw(int64 shader, int64 vb, array vertex\_data, uint32 vertex\_count, int32 topology, int64 blend, int64 sampler, int64 texture, int32 tex\_slot, int64 cb, array cb\_data, int32 cb\_slot); int64 custom\_draw\_indexed(int64 shader, int64 vb, array vertex\_data, uint32 vertex\_count, int64 ib, array index\_data, uint32 index\_count, int32 topology, int64 blend, int64 sampler, int64 texture, int32 tex\_slot, int64 cb, array cb\_data, int32 cb\_slot); int64 draw\_mesh(int64 mesh, int64 shader, int32 topology, int64 blend, int64 sampler, int64 texture, int32 tex\_slot, int64 cb, array cb\_data, int32 cb\_slot); int64 dispatch\_compute(int64 cs, uint32 x, uint32 y, uint32 z); \`\`\` \`topology\`: 0=TRIANGLE\\\_LIST, 1=TRIANGLE\\\_STRIP, 2=LINE\\\_LIST, 3=LINE\\\_STRIP, 4=POINT\\\_LIST. Any of \`blend\` / \`sampler\` / \`texture\` / \`cb\` can be \`0\` to skip binding. \`cb\_data\` may be an empty array. ## Binding and state \`\`\`cpp int64 custom\_set\_render\_target(int64 rt); int64 custom\_set\_render\_target\_ext(int64 rt, int64 depth\_buffer); int64 custom\_reset\_render\_target(); int64 custom\_bind\_rt\_as\_texture(int64 rt, int32 slot); int64 custom\_restore\_state(); int64 custom\_set\_depth\_stencil\_state(int64 ds); int64 custom\_set\_rasterizer\_state(int64 rs); int64 custom\_set\_viewport(float64 x, float64 y, float64 w, float64 h); int64 custom\_reset\_viewport(); int64 custom\_bind\_texture(int64 texture, int64 sampler, int32 slot); int64 custom\_bind\_constant\_buffer(int64 cb, array data, int32 slot, int32 stage); int64 custom\_update\_texture(int64 tex, uint32 x, uint32 y, uint32 w, uint32 h, array rgba\_data); int64 custom\_clear\_render\_target(int64 rt, float64 r, float64 g, float64 b, float64 a); int64 custom\_clear\_depth\_buffer(int64 db); int64 bind\_structured\_buffer(int64 sb, int32 slot, int32 stage); int64 update\_structured\_buffer(int64 sb, array data); int64 capture\_backbuffer(int32 slot); \`\`\` \`stage\`: 0=VS, 1=PS, 2=CS (matches D3D11 shader stages). Call \`custom\_restore\_state()\` after any custom-pipeline sequence before returning control to the 2D layer. ## Minimal triangle \`\`\`cpp int64 g\_shader; int64 g\_vb; int64 main() { string vs = "struct VSIn { float2 pos : POSITION; float4 color : COLOR; };\\nstruct VSOut { float4 pos : SV\_Position; float4 color : COLOR; };\\nVSOut main(VSIn i) { VSOut o; o.pos = float4(i.pos, 0.0, 1.0); o.color = i.color; return o; }\\n"; string ps = "struct VSOut { float4 pos : SV\_Position; float4 color : COLOR; };\\nfloat4 main(VSOut i) : SV\_Target { return i.color; }\\n"; g\_shader = create\_shader(vs, ps, "POSITION:0:FLOAT2, COLOR:0:FLOAT4"); g\_vb = create\_vertex\_buffer(24, 3, true); // 2\*4 + 4\*4 = 24 bytes per vertex register\_routine(cast(my\_draw), 0); return 1; } void my\_draw(int64 data) { float32\[\] verts; // vertex 0: pos(-0.5, -0.5) color(1, 0, 0, 1) verts.push(-0.5f); verts.push(-0.5f); verts.push(1.0f); verts.push(0.0f); verts.push(0.0f); verts.push(1.0f); // vertex 1: pos(0.5, -0.5) color(0, 1, 0, 1) verts.push(0.5f); verts.push(-0.5f); verts.push(0.0f); verts.push(1.0f); verts.push(0.0f); verts.push(1.0f); // vertex 2: pos(0, 0.5) color(0, 0, 1, 1) verts.push(0.0f); verts.push(0.5f); verts.push(0.0f); verts.push(0.0f); verts.push(1.0f); verts.push(1.0f); float32\[\] no\_cb; custom\_draw(g\_shader, g\_vb, verts, 3, 0, 0, 0, 0, 0, 0, no\_cb, 0); } \`\`\` ## Cleanup On script unload, every handle returned by \`create\_\*\` / \`load\_\*\` is destroyed automatically. Explicit \`destroy\_\*\` is optional and only needed if you want to free a resource mid-script. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/render-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/gui-api.md). # GUI API All GUI natives are auto-registered into every loaded script. The API is in two parts: \* \*\*Part 1 — sidebar sections + widgets.\*\* A \`sidebar\_section\_t\` is a select-button in the host sidebar plus a content panel auto-attached to the main frame. The panel renders only when the section's button is selected. Widgets are created on the section directly. \* \*\*Part 2 — frames, layers, custom widgets, menus, file pickers.\*\* Lower-level primitives for floating windows and custom drawing. All GUI handles are \`int64\`-backed. The script doesn't own the underlying resources — calling a destructor on a handle is a noop. At script unload, every handle the script created gets cleaned up automatically. ## Sidebar sections \`\`\`cpp sidebar\_section\_t create\_sidebar\_section(string name, string icon); void create\_sidebar\_separator(); \`\`\` \`name\` renders as the sidebar label. \`icon\` accepts a codicon string (e.g. \`"\\xEE\\xAC\\xA3"\` for file-code U+EB23) or \`""\` for no icon. Each section is a radio-style sidebar entry: clicking one auto-deselects siblings and shows that section's panel. \`\`\`cpp void section.set\_active(bool active); // toggle selection programmatically \`\`\` ### Widget builders on \`sidebar\_section\_t\` Every widget builder returns a typed handle. Each is also \`int64\`-backed; pass to other natives via the typed name. \`\`\`cpp label\_t section.create\_label(string text, ui\_align align); void section.create\_separator(); button\_t section.create\_button(string label, ui\_align align); checkbox\_t section.create\_checkbox(string label, bool initial); slider\_t section.create\_slider(string label, float64 initial, float64 minv, float64 maxv, float64 step); slider\_icon\_t section.create\_slider\_icon(string icon, float64 initial, float64 minv, float64 maxv, float64 step); value\_input\_t section.create\_value\_input(string label, float64 initial, float64 minv, float64 maxv, float64 step); options\_t section.create\_options(string label, array items, int64 selected); multi\_options\_t section.create\_multi\_options(string label, array items, int64 selected\_mask); dropdown\_t section.create\_dropdown(string label, array items, int64 selected); multi\_dropdown\_t section.create\_multi\_dropdown(string label, array items, int64 selected\_mask); list\_t section.create\_list(string label, array info1, array info2, bool selectable, int64 selected, int64 visible\_rows, bool filterable); inline\_button\_t section.create\_inline\_button(string label, float64 width, string icon); inline\_text\_input\_t section.create\_inline\_text\_input(string initial, float64 width, string placeholder); tabs\_t section.create\_tabs(array items, int64 selected); keybind\_t section.create\_keybind(string label); progress\_bar\_t section.create\_progress\_bar(string label, float64 initial, float64 minv, float64 maxv, bool show\_pct); spinner\_t section.create\_spinner(string label); range\_slider\_t section.create\_range\_slider(string label, float64 minv, float64 maxv, float64 lo, float64 hi, float64 step); table\_t section.create\_table(string label, array col\_names, array col\_widths, int64 visible\_rows); text\_input\_t section.create\_text\_input(string label, string initial, int64 max\_lines); text\_editor\_t section.create\_text\_editor(string label, string initial, int64 visible\_lines, string lexer); colorpicker\_t section.create\_colorpicker(string label, color initial); \`\`\` ## Common widget operations Every widget type (except \`text\_editor\_t\`, which has no \`set\_active\`) supports: \`\`\`cpp void widget.set\_active(bool active); void widget.set\_tooltip(string s); void widget.on\_change(int64 fn\_handle); // closure: void cb(int64 widget\_handle) \`\`\` \`on\_change\` fires on \`CALLBACK\_VALUE\_CHANGED\` — which means click for buttons, value mutation for sliders / checkboxes / dropdowns / etc. Pass the closure via \`cast(my\_callback)\`. ## Per-widget typed get / set \`\`\`cpp // label void label.set\_text(string s); // button void button.attach\_to(button\_t other); // group buttons into one row // checkbox bool checkbox.get(); void checkbox.set(bool v); // slider, slider\_icon, value\_input float64 X.get(); void X.set(float64 v); // options, dropdown, tabs int64 X.get(); void X.set(int64 i); // multi\_options, multi\_dropdown int64 X.get\_mask(); void X.set\_mask(int64 m); // list int64 list.get\_selected(); void list.set\_selected(int64 i); void list.set\_items(array info1, array info2); int64 list.size(); // inline\_text\_input, text\_input, text\_editor string X.get(); void X.set(string s); // keybind void keybind.bind(int64 vk, bool ctrl, bool shift, bool alt, keybind\_mode mode); bool keybind.is\_active(); // true when any binding is currently active per its mode; poll to react to activation int64 keybind.binding\_count(); // number of bindings on this row // progress\_bar void progress\_bar.set(float64 v); // range\_slider — split lo/hi getters since there's no natural pair type float64 range\_slider.get\_lo(); float64 range\_slider.get\_hi(); void range\_slider.set(float64 lo, float64 hi); // table void table.add\_row(array cells); void table.clear(); int64 table.size(); // colorpicker — uses the registered \`color\` type void colorpicker.attach\_to(colorpicker\_t other); color colorpicker.get(); void colorpicker.set(color c); \`\`\` ## Frames (Part 2) \`frame\_t\` wraps any of four host frame kinds — distinguished by which factory you call: \`\`\`cpp frame\_t create\_frame(string name, vec2 pos, vec2 size, layer\_t layer); // raw frame, no chrome. Pass 0 for layer to use the default layer. frame\_t create\_default\_frame(string name, vec2 pos, vec2 size, layer\_t layer); // frame with title bar / logo / drag chrome. frame\_t create\_draggable\_frame(string name, vec2 pos, vec2 size, layer\_t layer); frame\_t create\_popup(string name, vec2 pos, vec2 size, layer\_t layer); \`\`\` \`\`\`cpp void frame.set\_pos(vec2 pos); void frame.set\_size(vec2 size); vec2 frame.get\_pos(); vec2 frame.get\_size(); void frame.set\_visible(bool v); bool frame.is\_visible(); void frame.set\_anchors(int64 mask); // ui\_anchor::\* OR'd void frame.attach(frame\_t parent); void frame.set\_float(int64 hash, float64 v); // widget\_attr::\* keys void frame.install\_hook(int64 hook\_id, int64 fn\_handle); void frame.remove\_hook(int64 hook\_id); void frame.set\_focused(); frame\_t get\_focused\_frame(); bool ui\_is\_focused(); \`\`\` ## Layers A layer is a z-stacked frame group; frames in higher layers paint over lower ones. \`\`\`cpp layer\_t create\_layer(string name, bool input\_passthrough, bool force\_topmost); layer\_t get\_default\_layer(); int64 layer\_count(); void layer.promote\_to\_top(); void layer.set\_visible(bool v); int64 layer.frame\_count(); \`\`\` ## Custom widgets on a script-owned frame Drop a \`widget\_t\` into one of your \`frame\_t\`s for a custom render callback that fires every tick during the frame's render pass: \`\`\`cpp widget\_t create\_widget(frame\_t parent, string name, int64 execute\_cb\_handle, bool consume\_input); // execute\_cb shape: void cb(int64 widget\_handle) — called every tick. void widget.set\_pos(vec2 pos); void widget.set\_size(vec2 size); void widget.set\_active(bool v); void widget.set\_tooltip(string s); void widget.set\_float(int64 hash, float64 v); void widget.set\_anchors(int64 mask); void widget.install\_hook(int64 hook\_id, int64 fn\_handle); void widget.remove\_hook(int64 hook\_id); \`\`\` ## Menus A \`menu\_t\` is a context menu — a popup list of items. Attach it to any widget to make right-click on that widget open it. \`\`\`cpp menu\_t create\_menu(); void menu.add\_item(string label, int64 on\_click\_cb, string shortcut, string icon); // shortcut: visible label only (e.g. "Ctrl+C"); not bound by add\_item itself. // icon: codicon string or "" for none. // on\_click\_cb shape: void cb(int64 menu\_user\_data). void menu.add\_separator(); \`\`\` \`menu\_t.attach\_to\_widget\` is split per widget type because enma's overloading is by arity, not by parameter type. Use the variant matching the widget you're attaching to: \`\`\`cpp void menu.attach\_to\_widget(widget\_t target); void menu.attach\_to\_button(button\_t target); void menu.attach\_to\_label(label\_t target); void menu.attach\_to\_checkbox(checkbox\_t target); void menu.attach\_to\_slider(slider\_t target); void menu.attach\_to\_slider\_icon(slider\_icon\_t target); void menu.attach\_to\_value\_input(value\_input\_t target); void menu.attach\_to\_options(options\_t target); void menu.attach\_to\_multi\_options(multi\_options\_t target); void menu.attach\_to\_dropdown(dropdown\_t target); void menu.attach\_to\_multi\_dropdown(multi\_dropdown\_t target); void menu.attach\_to\_list(list\_t target); void menu.attach\_to\_inline\_button(inline\_button\_t target); void menu.attach\_to\_inline\_text\_input(inline\_text\_input\_t target); void menu.attach\_to\_tabs(tabs\_t target); void menu.attach\_to\_keybind(keybind\_t target); void menu.attach\_to\_progress\_bar(progress\_bar\_t target); void menu.attach\_to\_spinner(spinner\_t target); void menu.attach\_to\_range\_slider(range\_slider\_t target); void menu.attach\_to\_table(table\_t target); void menu.attach\_to\_text\_input(text\_input\_t target); void menu.attach\_to\_text\_editor(text\_editor\_t target); void menu.attach\_to\_colorpicker(colorpicker\_t target); \`\`\` ## File picker \`\`\`cpp file\_picker\_t create\_file\_picker(string title, string start\_path, string filter\_extension, bool folder\_mode); void picker.open(); void picker.close(); string picker.get\_selected(); \`\`\` ## Theme \`\`\`cpp bool is\_dark\_theme(); void set\_dark\_theme(bool dark); color get\_theme\_color(int64 color\_hash); void set\_theme\_color(int64 color\_hash, color c); \`\`\` \`color\_hash\` is a value from the \`ui\_color\` enum. ## Toasts and queries \`\`\`cpp void show\_toast(toast\_kind kind, string title, string msg); bool gui\_active(); \`\`\` ## Enums All exposed without needing a header import: | Enum | Values | | -------------- | -------------------------------------------------------------------------------------------------------------------------- | | \`ui\_anchor\` | \`none\`, \`left\`, \`right\`, \`top\`, \`bottom\`, \`all\` | | \`ui\_edge\` | \`left\`, \`top\`, \`right\`, \`bottom\` | | \`ui\_align\` | \`left\`, \`center\`, \`right\` | | \`ui\_layout\` | \`none\`, \`vertical\`, \`horizontal\` | | \`ui\_hook\` | 33 hook IDs incl. \`pre\_execute\`, \`post\_execute\`, \`clicked\`, \`right\_clicked\`, \`should\_render\`, \`editor\_\*\`, \`widget\_execute\` | | \`ui\_callback\` | \`value\_changed\`, \`item\_activated\` | | \`widget\_attr\` | well-known position / size / scroll / rounding hashes | | \`ui\_color\` | 35 color hashes (\`bg\`, \`text\`, \`accent\`, \`frame\_bg\`, \`sidebar\_bg\`, \`element\_button\_bg\`, etc.) | | \`keybind\_mode\` | \`off\`, \`on\`, \`single\`, \`toggle\`, \`always\_on\` | | \`toast\_kind\` | \`info\`, \`success\`, \`warning\`, \`error\` | ## Lifecycle and cleanup GUI resources you create (sections, frames, layers, custom widgets, menus, file pickers) are tracked per-script and torn down automatically at script unload. You don't need to destroy them manually — the destructor on each handle is a noop. Caveats: \* \*\*Sidebar slots persist.\*\* Sections you create occupy a sidebar slot for the lifetime of the host. Hot-reloading scripts that create many sections will leave stale slots in the sidebar. \* \*\*Separators\*\* stay visible after unload (no remove path). Hook callbacks fire on the UI thread. Heavy work inside a \`pre\_execute\` or \`on\_change\` (running every tick on every widget) shows up in profile — keep them lightweight. ## Example A comprehensive script exercising most of the widget builders, \`on\_change\` plumbing through typed handles, an attached-button row, an attached colorpicker chain, a context menu, a tabs widget with per-tab content, and a routine polling \`keybind.is\_active()\`. \`\`\`cpp sidebar\_section\_t g\_sec; menu\_t g\_menu; keybind\_t g\_kb\_aim; keybind\_t g\_kb\_esp; bool g\_aim\_was\_active = false; bool g\_esp\_was\_active = false; int64 g\_kb\_routine = 0; tabs\_t g\_tabs; label\_t g\_t0\_label; slider\_t g\_t0\_slider; label\_t g\_t1\_label; checkbox\_t g\_t1\_check; void on\_apply(int64 \_) { print\_console("\[demo\] Apply clicked"); } void on\_cancel(int64 \_) { print\_console("\[demo\] Cancel clicked"); } void on\_volume(int64 self) { slider\_t s = cast(self); print\_console("Volume -> " + cast(s.get())); } void on\_features(int64 self) { multi\_options\_t mo = cast(self); print\_console("features mask -> " + cast(mo.get\_mask())); } void on\_accent(int64 self) { colorpicker\_t cp = cast(self); color c = cp.get(); print\_console("accent -> " + cast(c.r()) + "," + cast(c.g()) + "," + cast(c.b()) + "," + cast(c.a())); } void on\_view\_tabs(int64 self) { tabs\_t t = cast(self); int64 sel = t.get(); // selected tab's widgets active, others inactive g\_t0\_label.set\_active(sel == 0); g\_t0\_slider.set\_active(sel == 0); g\_t1\_label.set\_active(sel == 1); g\_t1\_check.set\_active(sel == 1); } void on\_kb\_aim\_changed(int64 self) { keybind\_t kb = cast(self); print\_console("aim bindings -> " + cast(kb.binding\_count())); } // keybinds don't fire a callback on hardware-key activation — // poll keybind.is\_active() to react. void kb\_poll\_routine(int64 \_data) { bool now = g\_kb\_aim.is\_active(); if (now != g\_aim\_was\_active) { print\_console(now ? "Aim ACTIVE" : "Aim inactive"); g\_aim\_was\_active = now; } bool esp = g\_kb\_esp.is\_active(); if (esp != g\_esp\_was\_active) { print\_console(esp ? "ESP ACTIVE" : "ESP inactive"); g\_esp\_was\_active = esp; } } int32 main() { g\_sec = create\_sidebar\_section("demo", ""); g\_sec.create\_label("Settings panel demo.", ui\_align::left); g\_sec.create\_separator(); // Attached button row — children share the primary's row. button\_t apply = g\_sec.create\_button("Apply", ui\_align::right); button\_t cancel = g\_sec.create\_button("Cancel", ui\_align::right); cancel.attach\_to(apply); apply.on\_change(cast(on\_apply)); cancel.on\_change(cast(on\_cancel)); g\_sec.create\_separator(); g\_sec.create\_checkbox("Notifications", true); slider\_t vol = g\_sec.create\_slider("Volume", 0.6, 0.0, 1.0, 0.0); vol.on\_change(cast(on\_volume)); g\_sec.create\_value\_input("Port", 8080.0, 1.0, 65535.0, 1.0); // Codicon UTF-8 byte sequence — \`\\xHH\` lexer escape required. g\_sec.create\_slider\_icon("\\xEE\\xA9\\xB0", 0.75, 0.0, 1.0, 0.0); g\_sec.create\_separator(); array features; features.push("Autosave"); features.push("Spell check"); features.push("Auto-complete"); features.push("Line numbers"); multi\_options\_t mo = g\_sec.create\_multi\_options("Editor features", features, 13); mo.on\_change(cast(on\_features)); g\_sec.create\_separator(); array tab\_items; tab\_items.push("Overview"); tab\_items.push("Logs"); g\_tabs = g\_sec.create\_tabs(tab\_items, 0); g\_tabs.on\_change(cast(on\_view\_tabs)); g\_t0\_label = g\_sec.create\_label("Overview content.", ui\_align::left); g\_t0\_slider = g\_sec.create\_slider("FOV", 90.0, 60.0, 120.0, 1.0); g\_t1\_label = g\_sec.create\_label("Logs content.", ui\_align::left); g\_t1\_check = g\_sec.create\_checkbox("Verbose logging", false); g\_t1\_label.set\_active(false); g\_t1\_check.set\_active(false); g\_sec.create\_separator(); g\_kb\_aim = g\_sec.create\_keybind("Aimbot"); g\_kb\_aim.bind(0x01, false, false, false, keybind\_mode::on); // VK\_LBUTTON g\_kb\_aim.on\_change(cast(on\_kb\_aim\_changed)); g\_kb\_esp = g\_sec.create\_keybind("ESP toggle"); g\_kb\_esp.bind(0x45, false, false, false, keybind\_mode::toggle); // 'E' g\_sec.create\_separator(); colorpicker\_t accent = g\_sec.create\_colorpicker("Accent", color(180, 180, 180, 255)); accent.on\_change(cast(on\_accent)); // Attached colorpicker chain — children render as swatches in the parent's popup. colorpicker\_t theme\_cp = g\_sec.create\_colorpicker("Theme", color(120, 120, 120, 255)); colorpicker\_t primary = g\_sec.create\_colorpicker("Primary", color( 80, 80, 80, 255)); colorpicker\_t secondary = g\_sec.create\_colorpicker("Secondary", color(200, 200, 200, 255)); primary.attach\_to(theme\_cp); secondary.attach\_to(theme\_cp); // Context menu attached to a button — opens on the host's right-click path. button\_t actions = g\_sec.create\_button("Actions", ui\_align::center); g\_menu = create\_menu(); g\_menu.add\_item("Reset", cast(on\_apply), "Ctrl+R", ""); g\_menu.add\_separator(); g\_menu.add\_item("About...", cast(on\_cancel), "", ""); g\_menu.attach\_to\_button(actions); g\_kb\_routine = register\_routine(cast(kb\_poll\_routine), 0); return 1; // keep loaded so the section stays interactive } \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/gui-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/win-api.md). # Win API All win natives are auto-registered into every loaded script. This API \*\*sends\*\* input and reads window state. For state polling (mouse position, key down/up etc.), see \[Input API\](/perception/enma/input-api.md). \`HWND\` is exposed as raw \`int64\`. OS-owned; if the window disappears, subsequent calls reject via \`IsWindow()\`. ## \`window\_info\_t\` Snapshot of a window at enumeration time. Heap-allocated; fields read via methods. \`\`\`cpp int64 info.hwnd(); int64 info.pid(); int64 info.tid(); string info.process\_name(); // exe basename string info.title(); // window title at snapshot time string info.class\_name(); \`\`\` ## Enumerate / find \`\`\`cpp array get\_all\_hwnds(); int64 find\_window(string title); int64 find\_window(string title, string class\_name); \`\`\` \`find\_window\` returns 0 when no match. ## Window queries Geometry is split per axis (no array tuples). Combine pos + size for a rect. \`\`\`cpp int64 get\_window\_width(int64 hwnd); // 0 on invalid hwnd int64 get\_window\_height(int64 hwnd); // 0 on invalid hwnd vec2 get\_window\_pos(int64 hwnd); // screen coords; (0,0) on invalid vec2 get\_window\_size(int64 hwnd); // (width, height) as vec2 bool is\_foreground\_window(int64 hwnd); bool is\_window\_active(int64 hwnd); // visible AND not minimized string get\_window\_title(int64 hwnd); string get\_window\_class(int64 hwnd); bool set\_foreground\_window(int64 hwnd); int64 get\_window\_thread\_id(int64 hwnd); // 0 on invalid hwnd int64 get\_window\_process\_id(int64 hwnd); // 0 on invalid hwnd bool post\_message(int64 hwnd, int64 msg, int64 wparam, int64 lparam); \`\`\` ## Clipboard \`\`\`cpp bool copy\_to\_clipboard(string text); string copy\_from\_clipboard(); // empty string when nothing or wrong format \`\`\` \`copy\_to\_clipboard\` is gated by perception's restricted-string filter (returns false + logs when blocked). ## Keyboard SEND Synthesized via \`SendInput\`. Restricted virtual keys (set host-side) are blocked + logged. \`\`\`cpp void win\_key\_down (int64 vk); void win\_key\_up (int64 vk); void win\_key\_press(int64 vk, int64 delay\_ms); // down + sleep + up; delay capped at 1000ms bool send\_char(int64 hwnd, string text); // PostMessageW(WM\_CHAR), first wide char only bool send\_key (int64 hwnd, int64 vk); // PostMessageW(WM\_KEYDOWN+WM\_KEYUP) targeted at hwnd \`\`\` ## Mouse SEND \`\`\`cpp void mouse\_move (int64 x, int64 y); // absolute screen coords void mouse\_move\_relative(int64 dx, int64 dy); void mouse\_left\_click (); // down + 10ms + up void mouse\_right\_click (); void mouse\_middle\_click (); void mouse\_scroll (int64 amount); // multiples of WHEEL\_DELTA void send\_mouse\_input (int64 dx, int64 dy, int64 flags, int64 mouse\_data); // raw SendInput \`\`\` ## Example: focus a window and click in it \`\`\`cpp int64 hwnd = find\_window("Notepad"); if (hwnd == 0) return 0; set\_foreground\_window(hwnd); sleep\_ms(50); vec2 pos = get\_window\_pos(hwnd); vec2 sz = get\_window\_size(hwnd); mouse\_move(pos.x() + sz.x() / 2.0, pos.y() + sz.y() / 2.0); mouse\_left\_click(); \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/win-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/mcp-api.md). # MCP API Perception's MCP server exposes the proc-API surface as JSON-RPC tools that any \[Model Context Protocol\](https://modelcontextprotocol.io/) client (Claude Code, Cline, Continue, ...) can call. Writing an Enma script? Use \[Proc\](/perception/enma/proc-api.md) / \[CPU\](/perception/enma/cpu-api.md) / \[Zydis\](/perception/enma/zydis-api.md) directly. Driving perception from an AI agent? Enable MCP. ## Enable In perception, \*\*Settings → Perception MCP\*\*: 1. Type a \*\*Bind port\*\* (1024..65535), or leave blank for OS-pick. 2. Toggle \*\*Enable MCP server\*\* on. 3. Copy the \*\*Bound URL\*\* that appears. The server is loopback-only. ### Other toggles in the same panel \* \*\*Auto-start on perception load\*\* — persisted; on next launch the server starts automatically using the saved port (config loads before the autostart fires, so the saved port is reused rather than regenerated). \* \*\*Heap-only scans by default\*\* — controls the default of the \`heap\_only\` flag on \`scan\_value\` / \`scan\_string\` / \`scan\_pointer\_to\` / \`find\_string\_refs\` when an MCP caller omits it. \*\*On by default.\*\* Flipping it off makes those tools walk the entire user-space when callers don't supply \`heap\_only\`, which can OOM or hang on targets with multi-GiB heaps (Forza-class). ## Connect \*\*Claude Code:\*\* \`\`\` claude mcp add --transport http perception http://127.0.0.1:/mcp \`\`\` Add \`--scope user\` for global registration. Other clients (Cline, Continue, ...) accept the same URL via their Streamable HTTP transport. ## Transport The server auto-detects two framings on the same port: | First bytes | Framing | Used by | | ------------------------------------- | ----------------------- | ------------------------------------ | | \`POST\` / \`GET\` / \`OPTIONS\` / \`DELETE\` | HTTP/1.1 streamable | MCP clients | | anything else | Line-delimited JSON-RPC | The cpp example below, raw debugging | Both carry JSON-RPC 2.0. Real MCP clients use the 5 protocol methods (\`initialize\` / \`notifications/initialized\` / \`tools/list\` / \`tools/call\` / \`ping\`); raw clients can call tool methods directly (\`"method": "process/list"\`). ## Handles Most tools need a \`handle\` from \`process/reference\_by\_pid\` / \`\_by\_name\`. Handles are \*\*per-connection\*\*: \* Other connections can't use yours. \* Disconnecting releases everything automatically. \* Manual release: \`process/dereference\` (one) or \`process/cleanup\_references\` (all). ## Permissions Shared with \[enma\](/perception/enma/proc-api.md#permissions). Toggle in \*\*Scripting → API permissions\*\*: | Flag | Gates | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`kernel\_rw\_access\` | Kernel-mode addresses in any read / write / disasm / \`query\_memory\_region\` / \`find\_pattern\*\` call; the \`eprocess\` field in \`process/list\` + \`info\_by\_\*\`; the \`ethread\` field in \`get\_threads\`; \`system/list\_drivers\` | | \`write\_memory\` | Every tool that writes target memory: \`write\_virtual\_memory\`, \`write\_typed\_value\`, \`write\_string\`, \`copy\_memory\`, \`fill\_memory\` | | \`virtual\_memory\_operations\` | \`allocate\_memory\`, \`free\_memory\` | Blocked calls return \`-32001\` with the missing permission named. ## Error codes | Code | Meaning | | -------- | ------------------------------- | | \`-32700\` | Parse error | | \`-32600\` | Invalid request | | \`-32601\` | Method not found | | \`-32602\` | Invalid params | | \`-32603\` | Internal | | \`-32001\` | Permission denied | | \`-32002\` | Stale / cross-connection handle | | \`-32003\` | Target not found | | \`-32004\` | Operation failed | ## Tools 59 tools. Addresses + handles are \*\*hex strings\*\* (\`"0x7ff7..."\`) — JSON numbers lose precision past 2^53. Required params are listed plain, optional params get \`?\`. Every "take a handle" tool below has \`handle\` as its first param — omitted from the params column to keep things readable. The tools that don't take a handle are called out per-section. ### Discovery + reference lifecycle Params shown literally (no implicit \`handle\` — \`process/dereference\` explicitly takes the handle it's about to release). | Tool | Params | | | ---------------------------- | -------- | ------------------------------------------------- | | \`process/list\` | — | Snapshot of every active process. | | \`process/info\_by\_pid\` | \`pid\` | One process by PID. | | \`process/info\_by\_name\` | \`name\` | One process by image name. | | \`process/reference\_by\_pid\` | \`pid\` | Take a per-connection handle. Returns hex string. | | \`process/reference\_by\_name\` | \`name\` | Same, by image name. | | \`process/dereference\` | \`handle\` | Release one handle. | | \`process/cleanup\_references\` | — | Release every handle this connection holds. | | \`process/list\_references\` | — | What this connection currently holds. | ### Memory I/O | Tool | Params | | | ------------------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------- | | \`process/read\_virtual\_memory\` | \`address\`, \`size\` | Raw bytes as hex. Max 16 MiB. | | \`process/write\_virtual\_memory\` | \`address\`, \`data\` | Gated \`write\_memory\`. \`data\` is hex. | | \`process/is\_valid\_address\` | \`address\` | Does the address resolve? | | \`process/read\_typed\_value\` | \`address\`, \`type\` | \`type\` ∈ \`u8..u64 / i8..i64 / f32 / f64 / ptr / bool\`. | | \`process/write\_typed\_value\` | \`address\`, \`type\`, \`value\` | Gated \`write\_memory\`. Use hex string for \`value\` when type is u64/i64/ptr. | | \`process/read\_string\` | \`address\`, \`max\_len?\`, \`encoding?\` | \`max\_len\` 1024 default. \`encoding\` ∈ \`auto / ascii / utf16\` (default auto-sniff). | | \`process/write\_string\` | \`address\`, \`text\`, \`encoding?\`, \`null\_terminate?\` | Gated \`write\_memory\`. \`encoding\` ∈ \`ascii / utf16\`. | | \`process/copy\_memory\` | \`src\_address\`, \`dst\_address\`, \`size\` | In-target memcpy. Gated \`write\_memory\`. Max 64 MiB, 1 MiB chunks. | | \`process/fill\_memory\` | \`address\`, \`size\`, \`byte\` | Memset. \`byte\` 0..255 (0x90 = NOP, 0xCC = int3). Gated \`write\_memory\`. | | \`process/read\_pointer\_chain\` | \`base\_address\`, \`offsets\` | \`offsets\` is an int array, max 64. | | \`process/disassemble\` | \`address\`, \`max\_bytes?\`, \`max\_instructions?\` | Zydis. Defaults 256 / 32. | ### Modules / threads / PE | Tool | Params | | | ----------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`process/get\_modules\` | — | All loaded modules. | | \`process/get\_threads\` | — | All threads. | | \`process/get\_module\_by\_name\` | \`name\` | One module by name. | | \`process/get\_export\_address\` | \`module\_base\`, \`export\_name\` | Single resolve. | | \`process/get\_import\_address\` | \`module\_base\`, \`import\_name\` | Resolve IAT slot VA. | | \`process/get\_module\_imports\` | \`module\_base\` | Full IAT walk. | | \`process/list\_module\_exports\` | \`module\_base\` | Full EAT walk. | | \`process/get\_module\_sections\` | \`module\_base\` | PE sections. | | \`process/get\_pe\_header\` | \`module\_base\` | NT/optional header summary. | | \`process/get\_module\_strings\` | \`module\_base\`, \`min\_length?\`, \`encoding?\` | \`min\_length\` default 4. \`encoding\` ∈ \`ascii / utf16 / both\` (default both). | | \`process/get\_exception\_table\` | \`module\_base\`, \`max\_entries?\` | x64 RUNTIME\\\_FUNCTION entries from \`.pdata\`. Precise function bounds. | | \`process/get\_data\_directory\` | \`module\_base\`, \`directory\` | One PE data-dir entry. \`directory\` ∈ \`export / import / resource / exception / security / basereloc / debug / architecture / globalptr / tls / load\_config / bound\_import / iat / delay\_import / com\_descriptor\` or 0..15. | ### Memory regions + allocation | Tool | Params | | | ---------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`process/query\_memory\_region\` | \`address\` | VirtualQuery-style. | | \`process/enumerate\_memory\_regions\` | \`heap\_only?\` | All committed regions. \`heap\_only\` default false. | | \`process/allocate\_memory\` | \`size\` | Gated \`virtual\_memory\_operations\`. Max 256 MiB. Allocation itself is safe. To execute code from the returned VA the target must have Control Flow Guard (CFG) off; reads + writes are unaffected. | | \`process/free\_memory\` | \`address\` | Same gate. | ### Pattern + scanner + xrefs + signature | Tool | Params | | | ---------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`process/find\_pattern\` | \`start\`, \`size\`, \`signature\` | IDA-style \`"AB CD ?? EF"\`. | | \`process/find\_all\_patterns\` | \`start\`, \`size\`, \`signature\` | Same, all hits (cap 1024). | | \`process/scan\_value\` | \`type\`, \`value\`, \`aligned?\`, \`heap\_only?\` | \`type\` ∈ \`u8..u64 / i8..i64 / f32 / f64\`. Use hex string for \`value\` when u64/i64. Defaults: aligned true. \`heap\_only\` defaults to the MCP UI's "Heap-only by default" toggle (on by default — skips code/module regions); pass \`heap\_only=false\` to walk full user-space. | | \`process/scan\_next\` | \`compare\`, \`value?\`, \`min?\`, \`max?\` | \`compare\` ∈ \`exact / range / unchanged / changed / increased / decreased\`. \`value\` for \`exact\`, \`min\`+\`max\` for \`range\`. | | \`process/scan\_string\` | \`text\`, \`encoding?\`, \`heap\_only?\` | \`encoding\` ∈ \`ascii / utf16\`, default ascii. \`heap\_only\` default = UI toggle (see \`scan\_value\`). | | \`process/scan\_pointer\_to\` | \`target\_address\`, \`heap\_only?\` | Aligned QWORDs pointing at \`target\_address\`. \`heap\_only\` default = UI toggle. | | \`process/find\_xrefs\` | \`module\_base\`, \`target\_address\` | Decode \`.text\`, return refs. | | \`process/find\_string\_refs\` | \`module\_base\`, \`text\`, \`encoding?\`, \`heap\_only?\`, \`string\_module?\` | Combo: scan for the string, then decode \`module\_base\`'s \`.text\` for code refs to each hit. Phase 1 (string search) defaults to a heap-only VAD walk (\`heap\_only\` follows the UI toggle) and is \*\*pre-capped at 1 GiB\*\* so the listener never crashes on huge targets — if the cap fires the call returns an error asking you to pass \`heap\_only=true\` or set \`string\_module\` (hex VA of the module that owns the string, usually the same as \`module\_base\`) for a fast bounded scan of just that module's image. Phase 2 caps code hits at 4096; response includes a \`truncated\` flag. | | \`process/generate\_signature\` | \`address\`, \`max\_length?\` | Default 32. \`is\_unique=false\` if length exhausted. | | \`process/diff\_memory\` | \`addr\_a\`, \`addr\_b\`, \`size\` | Cap 1 MiB. | ### Code analysis | Tool | Params | | | ------------------------------------ | ---------------------------------------- | --------------------------------------------------------------------------- | | \`process/find\_function\_bounds\` | \`address\`, \`scan\_back?\`, \`scan\_forward?\` | Defaults 4096 / 65536. Heuristic — use \`get\_exception\_table\` for precision. | | \`process/find\_function\_by\_signature\` | \`module\_base\`, \`signature\` | AOB-scan a module's \`.text\` + run bounds walk on each hit. | | \`process/analyze\_vtable\` | \`vtable\_address\`, \`max\_entries?\` | Default 64. Classifies entries as code/data per loaded modules. | | \`process/read\_rtti\` | \`vtable\_address\` | Win64 RTTI: class name + base classes. | ### Symbol / function lookup | Tool | Params | | | ------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------- | | \`process/lookup\_symbol\` | \`address\` | VA → \`{module\_base, module\_name, module\_offset, section, nearest\_export}\`. | | \`process/find\_function\_by\_name\` | \`pattern\`, \`case\_sensitive?\`, \`max\_results?\` | Substring match across all modules' export tables. Default case-insensitive, 64 results. | ### Handles | Tool | Params | | | ---------------------- | -------------- | -------------------------------------------------------------------------- | | \`process/enum\_handles\` | \`max\_entries?\` | Default 8192. \`NtQuerySystemInformation(SystemExtendedHandleInformation)\`. | ### System / environment | Tool | Params | | | -------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------ | | \`system/info\` | — | Build number, page size, processor count + arch. \`is\_24h2\_or\_later\` flag for build-keyed offsets. No handle. | | \`system/list\_drivers\` | \`max\_entries?\` | Kernel modules via \`NtQuerySystemInformation(SystemModuleInformation)\`. Gated \`kernel\_rw\_access\`. No handle. | | \`process/get\_command\_line\` | — | Reads \`PEB.ProcessParameters.CommandLine\`. x64 only. | | \`process/list\_environment\` | \`max\_bytes?\` | Reads \`PEB.ProcessParameters.Environment\`. Returns \`\[{key, value}\]\`. | ### Enma scripting bridge None of these takes a \`handle\` — they run a script (or return reference text) with its own permissions, independent of any referenced process. | Tool | Params | | | -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`script/get\_context\` | — | Returns the full enma language + Perception API reference as a single \`context\` string. \*\*Call this once per session before generating any script\*\* — enma is proprietary and its addon surface can't be inferred from training data. Covers language grammar, all 17 pre-shipped enma addons, and all 12 Perception API surfaces. | | \`script/validate\` | \`source\` | Compile-only. \*\*All\*\* addons registered (render / proc / cpu / zydis / sound / win / unicorn / net / input / \*\*gui\*\* / \*\*thread\*\* / filesystem). Returns \`{ ok, errors:\[\] }\`. | | \`script/execute\` | \`source\` | Compile + run \`main()\` once. \*\*GUI and thread addons are NOT registered\*\* — those resources would outlive a one-shot script and leak. For long-lived scripts use the in-app script editor. Returns \`{ ok, logs:\[\] }\`. | ## Example — minimal C++ client Build with the VS Developer Command Prompt: \`\`\` cl /EHsc /std:c++17 minimal\_mcp.cpp /link Ws2\_32.lib \`\`\` \`\`\`cpp #define WIN32\_LEAN\_AND\_MEAN #include #include #include #pragma comment(lib, "Ws2\_32.lib") int main(int argc, char\*\* argv) { if (argc < 2) { printf("usage: %s \\n", argv\[0\]); return 1; } WSADATA wd; WSAStartup(MAKEWORD(2, 2), &wd); SOCKET s = socket(AF\_INET, SOCK\_STREAM, IPPROTO\_TCP); sockaddr\_in a{}; a.sin\_family = AF\_INET; a.sin\_port = htons((u\_short)atoi(argv\[1\])); inet\_pton(AF\_INET, "127.0.0.1", &a.sin\_addr); if (connect(s, (sockaddr\*)&a, sizeof(a)) != 0) { printf("connect failed\\n"); return 1; } auto call = \[&\](const char\* line) { send(s, line, (int)strlen(line), 0); char buf\[8192\]; int n = recv(s, buf, sizeof(buf) - 1, 0); if (n > 0) { buf\[n\] = 0; printf("%s", buf); } }; // 1. List processes. call("{\\"jsonrpc\\":\\"2.0\\",\\"id\\":1,\\"method\\":\\"process/list\\",\\"params\\":{}}\\n"); // 2. Reference notepad.exe. call("{\\"jsonrpc\\":\\"2.0\\",\\"id\\":2,\\"method\\":\\"process/reference\_by\_name\\"," "\\"params\\":{\\"name\\":\\"notepad.exe\\"}}\\n"); // 3. Run a one-shot enma script. call("{\\"jsonrpc\\":\\"2.0\\",\\"id\\":3,\\"method\\":\\"script/execute\\"," "\\"params\\":{\\"source\\":\\"fn main() { println(\\\\\\"hello from mcp\\\\\\"); }\\"}}\\n"); closesocket(s); WSACleanup(); return 0; } \`\`\` Plain line-delimited JSON-RPC — the server's auto-detect routes us to that framing because we don't open with \`POST\` / \`GET\`. For the HTTP path, wrap each request in \`POST /mcp HTTP/1.1\\r\\nContent-Length: N\\r\\n\\r\\n\` — that's what Claude Code does for you. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/mcp-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/lifecycle-and-routines.md). # Lifecycle and Routines ## Entry point Every script needs a \`main()\` function. It runs once when the script is loaded. \`\`\`cpp int64 main() { // setup state, load resources, register routines return 1; } \`\`\` \`main()\`'s return value decides what happens next: | Return | Behavior | | ------ | -------------------------------------------------- | | \`> 0\` | Script stays loaded. | | \`<= 0\` | Script unloads immediately after \`main()\` returns. | Use \`return 1;\` for any normal long-lived script. Return \`0\` for one-shot scripts that just wanted to do work in \`main()\` and exit. ## Routines A routine is a script function that runs continuously after \`main()\` returns. Routines are how your script keeps doing work over time. \`\`\`cpp int64 register\_routine(int64 fn\_handle, int64 data); bool unregister\_routine(int64 routine\_handle); \`\`\` ### Callback shape The function you register takes one \`int64\` parameter: \`\`\`cpp void my\_callback(int64 data) { // data: the value you passed as the second arg to register\_routine } \`\`\` Pass the function as a closure handle via \`cast(fn\_name)\`: \`\`\`cpp int64 main() { int64 r = register\_routine(cast(my\_callback), 42); return 1; } \`\`\` \`register\_routine\` returns a handle. Keep it if you intend to unregister later, or discard. ### Multiple routines Register as many as you need. \`\`\`cpp void on\_render(int64 data) { /\* draw \*/ } void on\_tick(int64 data) { /\* update logic \*/ } int64 main() { register\_routine(cast(on\_render), 0); register\_routine(cast(on\_tick), 0); return 1; } \`\`\` ### Unregistering \`\`\`cpp unregister\_routine(my\_handle); \`\`\` A routine can also unregister itself from inside its own callback: \`\`\`cpp int64 g\_handle; void my\_callback(int64 data) { if (should\_stop()) { unregister\_routine(g\_handle); return; } // normal work } int64 main() { g\_handle = register\_routine(cast(my\_callback), 0); return 1; } \`\`\` ## Unload A script unloads when \`main()\` returns \`<= 0\`, when the user unloads it from the UI, or when the host shuts down. On unload all routines stop and any GPU resources you created via the render API are destroyed automatically. ## Exceptions Routines automatically catch uncaught throws and faults. The error is logged to \`\\exceptions\\enma.log\` with a timestamp, the routine id, the thrown value, and the source line where it happened. The script keeps running. ## Diagnostic helpers Quick tracing without touching the renderer: \`\`\`cpp void heartbeat(); // log "heartbeat called" void take\_int(int64 x); // log an int value void take\_ptr(int64 p); // log a pointer in hex void test\_3arg(int64 a, int64 b, int64 c); // log three ints \`\`\` Useful for confirming a code path is reached or sanity-checking a value. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/lifecycle-and-routines.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/cpu-api.md). # CPU API All CPU natives are auto-registered into every loaded script. Stuff that doesn't fit cleanly into other host APIs and isn't already in Enma's preshipped addons. For wall-clock time, ISO formatting, and \`unix\_seconds()\` see the preshipped \[Time\](https://enma-1.gitbook.io/enma/addons/time) addon. For \`popcount\`/\`clz\`/\`bswap\` etc., the preshipped \[Bits\](https://enma-1.gitbook.io/enma/addons/bits) addon. ## CPU identification \`\`\`cpp string cpu\_vendor(); // CPUID leaf 0, e.g. "GenuineIntel" string cpu\_brand(); // CPUID leaves 0x80000002..4, e.g. "Intel(R) Core(TM) i9-..." \`\`\` ## Timing \`\`\`cpp int64 rdtsc(); // raw cycle counter; not stable across cores or sleep int64 perf\_time(); // QueryPerformanceCounter int64 perf\_frequency(); // counter ticks per second int64 get\_tickcount64(); // ms since system boot (monotonic, 64-bit safe) \`\`\` \`perf\_time / perf\_frequency\` together give sub-microsecond timestamps: \`\`\`cpp int64 t0 = perf\_time(); do\_work(); float64 secs = cast(perf\_time() - t0) / cast(perf\_frequency()); \`\`\` ## Datetime helpers Companions to the preshipped \`time\` addon's \`year\`/\`month\`/\`day\`/\`hour\`/\`day\_of\_week\`/etc. decoders. The \`time\` addon takes a unix timestamp; these convert intermediate fields: \`\`\`cpp int64 now\_millisecond(); // 0..999, current local time string day\_name(int64 dow); // 0..6 -> "Sunday".."Saturday"; "Unknown" out of range string month\_name(int64 month); // 1..12 -> "January".."December"; "Unknown" out of range int64 hour12(int64 hour24); // 0..23 -> 1..12 (12-hour wall format) string ampm(int64 hour24); // 0..23 -> "AM" / "PM" \`\`\` ## Bitcasts (float ↔ int) Use the language built-in \`reinterpret\_cast(val)\`. Reinterprets the bit pattern; not a value conversion. Source and target must be the same byte size; emits a compile error otherwise. \`\`\`cpp uint32 u = reinterpret\_cast(1.5f); // 0x3FC00000 float32 f = reinterpret\_cast(0x3FC00000u); // 1.5 uint64 bits = reinterpret\_cast(3.14); // IEEE 64-bit pattern uint32 sign = reinterpret\_cast(-3.14f) >> 31; // 1 \`\`\` Compiles to at most 2 mov instructions (\`narrow\_f32\` + \`cast\` at the f32 boundary, plain \`cast\` elsewhere) — zero call overhead. Generalizes to any same-size pair: pointers ↔ \`int64\`, mixed signed/unsigned narrow ints, etc. For narrow-int → wider-int (no float involved), \`cast(some\_int8)\` etc. works directly — Enma keeps narrow ints zero/sign-extended in 64-bit slots, so the cast is a free rename. ## Thread priority Affects whatever thread invokes the call. Routine callbacks run each tick on their own ticker thread, so calling from a routine adjusts that ticker thread (NOT the script's main thread). \`\`\`cpp bool set\_thread\_priority(thread\_priority p); \`\`\` \`thread\_priority\` enum values: \`lowest\`, \`below\_normal\`, \`normal\`, \`above\_normal\`, \`highest\`. \`\`\`cpp set\_thread\_priority(thread\_priority::highest); \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/cpu-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/filesystem-api.md). # Filesystem API Sandboxed filesystem access for enma scripts. Every call is gated by the \`file\_system\_access\` permission. Without it, calls return a failure value (\`false\` / \`0\` / empty array) and never throw. ## Sandbox Paths are interpreted relative to the script's per-user data directory. Scripts cannot reach outside that root. Rejected at the path-validation step (returns failure without touching disk): \* absolute paths — \`C:\\config\`, \`/etc/hosts\` \* UNC paths — \`\\\\server\\share\` \* parent traversals — \`..\\..\\elsewhere\` \* leading slashes — \`/foo\`, \`\\foo\` \* embedded \`:\`, \`\\n\`, \`\\r\`, \`\\0\` Forward and backslashes are both accepted internally; pick one and stay consistent. ## Quick reference | Operation | Native | | ------------------------------ | -------------------------------------------------------------------------- | | Create / overwrite a text file | \`fs\_create\_file(path, data)\` | | Create a directory | \`fs\_create\_directory(path)\` | | Test existence | \`fs\_file\_exists(path)\` / \`fs\_dir\_exists(path)\` | | Delete | \`fs\_delete\_file(path)\` / \`fs\_delete\_directory(path)\` | | File size | \`fs\_file\_size(path)\` | | Read text | \`fs\_read\_file(path)\` | | Write / append text | \`fs\_write\_file(path, data)\` / \`fs\_append\_file(path, data)\` | | Read binary | \`fs\_read\_file\_binary(path)\` | | Write / append binary | \`fs\_write\_file\_binary(path, bytes)\` / \`fs\_append\_file\_binary(path, bytes)\` | | List entries | \`fs\_list\_files(path)\` / \`fs\_list\_dirs(path)\` / \`fs\_list\_all(path)\` | ## File operations \`\`\`cpp bool fs\_create\_file(string path, string data); bool fs\_create\_directory(string path); bool fs\_file\_exists(string path); bool fs\_dir\_exists(string path); bool fs\_delete\_file(string path); bool fs\_delete\_directory(string path); int64 fs\_file\_size(string path); \`\`\` \* \`fs\_create\_file\` writes \`data\` as the file's complete contents (UTF-8). Overwrites if the file already exists. Empty \`data\` creates a zero-byte file. \* \`fs\_create\_directory\` creates the directory and any missing parents. \* \`fs\_delete\_directory\` succeeds only when the target directory is empty. \* \`fs\_file\_size\` returns \`0\` for missing files (indistinguishable from a real zero-byte file — use \`fs\_file\_exists\` first if you need to disambiguate). \`\`\`cpp if (!fs\_dir\_exists("configs")) fs\_create\_directory("configs"); if (!fs\_create\_file("configs/active.json", "{\\"version\\":1}")) println("\[fs\] write failed (permission?)"); \`\`\` ## Text I/O \`\`\`cpp string fs\_read\_file(string path); bool fs\_write\_file(string path, string data); bool fs\_append\_file(string path, string data); \`\`\` \* \`fs\_read\_file\` returns the file's bytes interpreted as UTF-8. Returns an \*\*empty string\*\* on missing file / read failure / permission denied — distinguish from a real empty file via \`fs\_file\_exists\` if it matters. \* \`fs\_write\_file\` overwrites; \`fs\_append\_file\` appends. Both return \`true\` on success, \`false\` on failure. \`\`\`cpp fs\_write\_file("state/last\_target.txt", "weapon\_t1\_assault"); string saved = fs\_read\_file("state/last\_target.txt"); if (saved.length() > 0) set\_target(saved); \`\`\` ## Binary I/O \`\`\`cpp array fs\_read\_file\_binary(string path); bool fs\_write\_file\_binary(string path, array bytes); bool fs\_append\_file\_binary(string path, array bytes); \`\`\` Use these for opaque blobs (saved offsets, screenshots, packet captures). A missing or unreadable file yields an empty array. Empty-input writes succeed and produce a zero-byte file; empty-input appends are a no-op (still return \`true\`). \`\`\`cpp array header; header.push(0x4D); header.push(0x5A); // "MZ" fs\_write\_file\_binary("dumps/probe.bin", header); array back = fs\_read\_file\_binary("dumps/probe.bin"); print("read " + cast(back.length()) + " bytes"); \`\`\` ## Directory listing \`\`\`cpp array fs\_list\_files(string path); array fs\_list\_dirs(string path); array fs\_list\_all(string path); \`\`\` Returns the \*\*basenames\*\* of entries (no path prefixes) in the requested directory. No recursion — descend manually by calling again with the joined relative path. A missing directory or permission denial yields an empty array. Entry order is filesystem-dependent. \`\`\`cpp array configs = fs\_list\_files("configs"); for (string name : configs) { string body = fs\_read\_file("configs/" + name); // ... } \`\`\` ## Failure modes Every native handles every failure the same way: returns a falsy value (\`false\` / \`0\` / empty). The script never sees an exception from the FS layer. | Cause | Return | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | \`file\_system\_access\` permission off | \`false\` / \`0\` / empty | | Path validation fails (absolute, UNC, \`..\`, control char) | \`false\` / \`0\` / empty | | Target file/directory missing | \`false\` / \`0\` / empty (writes that need a parent will fail if the parent is missing — \`fs\_create\_directory\` first) | | Underlying I/O error (disk full, locked file, etc.) | \`false\` / \`0\` / empty | ## Sandbox tests Every escape attempt below returns \`false\` / empty without touching disk: \`\`\`cpp fs\_create\_file("C:\\\\evil.txt", "x"); // false: absolute path fs\_create\_file("/etc/passwd", "x"); // false: absolute / fs\_create\_file("\\\\\\\\server\\\\f", "x"); // false: UNC fs\_create\_file("../escape.txt", "x"); // false: parent traversal fs\_create\_file("ok/../escape.txt", "x"); // false: nested traversal \`\`\` ## Permissions Enabled via the script's \`permissions\` block. The flag is \`file\_system\_access\`. With it off, every call short-circuits before touching disk — reads return empty, writes silently no-op. Check with \`fs\_file\_exists\` after a write if your logic depends on the operation having succeeded. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/filesystem-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/net-api.md). # Net API All net natives are auto-registered into every loaded script. All network calls are gated by the \`network\_access\` permission. Without it, calls return a transport-failure value (\`status=0\` / null handle / empty array). ## HTTP — sync, with timeout \`\`\`cpp http\_response\_t http\_get (string url, int64 timeout\_ms); http\_response\_t http\_get (string url, map headers, int64 timeout\_ms); http\_response\_t http\_post(string url, string content\_type, string body, int64 timeout\_ms); http\_response\_t http\_post(string url, string content\_type, string body, map headers, int64 timeout\_ms); \`\`\` Both always return a non-null \`http\_response\_t\`. Read via methods: \`\`\`cpp int64 response.status(); // 0 on transport failure / permission denied string response.body(); bool response.ok(); // true if status is 200..299 \`\`\` \`content\_type\` may be empty for \`http\_post\`. The 3-arg \`http\_get\` / 5-arg \`http\_post\` overloads take a \`map\` of extra request headers — useful for \`Authorization: Bearer ...\`, \`X-API-Key\`, \`Accept\`, custom protocol headers, etc. Pass \`null\` or an empty map to skip. ### Headers example \`\`\`cpp map headers; headers.set("Authorization", "Bearer " + g\_token); headers.set("Accept", "application/json"); http\_response\_t r = http\_get("https://api.example.com/me", headers, 5000); if (r.ok()) println(r.body()); \`\`\` \`\`\`cpp map headers; headers.set("X-API-Key", "abc123"); http\_response\_t r = http\_post( "https://api.example.com/events", "application/json", "{\\"event\\":\\"login\\"}", headers, 5000); \`\`\` ## WebSocket \`\`\`cpp ws\_t ws\_connect(string url, int64 timeout\_ms); \`\`\` Connects to \`ws://\`, \`wss://\` (also \`http://\` / \`https://\` accepted). Spawns a background recv thread. Returns a null handle on failure or permission denied. ### \`ws\_t\` methods \`\`\`cpp bool ws.is\_open(); bool ws.send\_text (string msg); bool ws.send\_binary(array data); ws\_message\_t ws.recv(); // blocks until a message arrives or the connection closes ws\_message\_t ws.poll(); // non-blocking void ws.close(int64 code); // standard WS close codes (1000 = normal) \`\`\` ### \`ws\_message\_t\` methods \`\`\`cpp bool msg.ok(); // true if a message was returned bool msg.is\_text(); // payload framing bool msg.is\_closed(); // peer / local close has fired string msg.payload(); \`\`\` ## UDP — raw datagrams \`\`\`cpp udp\_t udp\_create(); \`\`\` Creates a fresh UDP socket. Returns a null handle on failure / permission denied. Send-only sockets can skip \`bind()\`; sockets that receive must \`bind()\` to a local port first. ### \`udp\_t\` methods \`\`\`cpp bool udp.bind(string addr, int64 port); // "0.0.0.0" / port — port 0 = OS-picked bool udp.send\_to(array data, string addr, int64 port); array udp.recv(int64 timeout\_ms); // blocking with timeout; empty on timeout/error string udp.last\_sender\_addr(); // IP of the most recent successful recv() int64 udp.last\_sender\_port(); // port of the most recent successful recv() void udp.close(); \`\`\` \`recv\` returns up to one full UDP datagram (max 65535 bytes). Timeout is in milliseconds — \`timeout\_ms = 0\` means block indefinitely. After a successful \`recv\`, \`last\_sender\_addr()\` / \`last\_sender\_port()\` give you the peer to reply to. ### UDP example — Source Query Protocol (A2S\\\_INFO) \`\`\`cpp udp\_t s = udp\_create(); if (cast(s) == 0) return 0; // A2S\_INFO request: FF FF FF FF 54 "Source Engine Query" 00 array q; q.push(0xFF); q.push(0xFF); q.push(0xFF); q.push(0xFF); q.push(0x54); string banner = "Source Engine Query"; for (int32 ch : banner) q.push(cast(ch)); q.push(0x00); if (!s.send\_to(q, "1.2.3.4", 27015)) { println("send failed"); return 0; } array reply = s.recv(2000); // 2-second timeout if (reply.length() == 0) { println("no reply (timeout)"); } else { println(format("got {d} bytes from {s}:{d}", reply.length(), s.last\_sender\_addr(), s.last\_sender\_port())); } \`\`\` ### UDP example — listener \`\`\`cpp udp\_t s = udp\_create(); s.bind("0.0.0.0", 9999); for (int32 i = 0; i < 10; i = i + 1) { array pkt = s.recv(1000); if (pkt.length() == 0) continue; println(format("from {s}:{d} ({d} bytes)", s.last\_sender\_addr(), s.last\_sender\_port(), pkt.length())); } \`\`\` ## HTTP example \`\`\`cpp http\_response\_t r = http\_get("https://api.example.com/status", 5000); if (r.ok()) { println("got: " + r.body()); } else if (r.status() == 0) { println("transport failed or permission denied"); } else { println("server returned " + cast(r.status())); } \`\`\` ## WebSocket example \`\`\`cpp ws\_t ws = ws\_connect("wss://echo.example.com/", 5000); if (cast(ws) == 0) return 0; ws.send\_text("hello"); ws\_message\_t m = ws.recv(); if (m.ok()) { println("got: " + m.payload()); } ws.close(1000); \`\`\` ## Permission \`network\_access\` gates every native in this file (HTTP, WebSocket, UDP). When off, every call returns a transport-failure value. ## Lifetime \`ws\_t\` and \`udp\_t\` close + free via the destructor at scope exit. If the script forgets, the host sweeps remaining sockets at unload — connections closed, threads joined, no permanent leak. UDP packets in flight are not buffered host-side; once you close, anything still on the wire is dropped by the OS. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/net-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.perception.cx/perception/enma/input-api.md). # Input API All input natives are auto-registered into every loaded script. Read-only complement to \[Win API\](/perception/enma/win-api.md) — Win API \*\*sends\*\* input, this \*\*reads\*\* state. Pollable per-frame from \`my\_draw\` or routine callbacks. Virtual-key codes follow Win32 VK\\\_\\\* convention. The \`vk\` enum bundles the common ones so no \`#include\` is needed. ## Mouse \`\`\`cpp vec2 get\_mouse\_pos(); // render-window pixels vec2 get\_mouse\_pos\_desktop(); // desktop pixels (full screen) vec2 get\_mouse\_delta(); // raw movement this frame vec2 get\_mouse\_delta\_desktop(); // desktop-space delta this frame bool mouse\_movement\_received(); // any movement this frame bool is\_hovered(vec2 pos, vec2 size); // mouse inside rect at pos with given size float64 get\_scroll\_delta(); // wheel ticks; positive = up \`\`\` ## Keyboard — single-flag queries | Flag | Meaning | | ------------- | -------------------------------------------- | | \`down\` | currently pressed (host-debounced) | | \`raw\_down\` | OS-level pressed state | | \`fired\` | up→down transition this frame | | \`toggle\` | caps-lock-style toggle (flips on each press) | | \`singlepress\` | fired but suppressed when modifiers are held | | \`prev\_down\` | down state from previous frame | \`\`\`cpp bool key\_down (int64 vk); bool key\_raw\_down (int64 vk); bool key\_fired (int64 vk); bool key\_toggle (int64 vk); bool key\_singlepress(int64 vk); bool key\_prev\_down (int64 vk); \`\`\` ## Bulk / ergonomic queries \`\`\`cpp key\_state\_t get\_key\_state(int64 vk); // atomic snapshot of all 6 flags array get\_keys\_down(); // virtual-key codes currently pressed string get\_recent\_key\_input(); // buffered text input (UTF-8) since last poll string get\_key\_name(int64 vk); // localized key name (e.g. "F1", "Left Arrow"); empty on invalid \`\`\` ### \`key\_state\_t\` \`\`\`cpp bool ks.raw\_down(); // OS-level pressed state bool ks.down(); // host-debounced pressed state bool ks.fired(); // up->down this frame (one-shot) bool ks.toggle(); // caps-lock-style toggle (flips on each press) bool ks.singlepress(); // fired but suppressed if modifiers held bool ks.prev\_down(); // down state from previous frame \`\`\` Use \`get\_key\_state(vk)\` when you need consistency across multiple flag reads in the same frame — the per-flag fns above each take a separate lock and can race. ## \`vk\` enum — common Win32 virtual keys \`\`\`cpp vk::backspace vk::tab vk::enter vk::shift vk::ctrl vk::alt vk::pause vk::caps\_lock vk::escape vk::space vk::page\_up vk::page\_down vk::end vk::home vk::left vk::up vk::right vk::down vk::insert vk::delete vk::k0 .. vk::k9 // top-row digits vk::a .. vk::z // letters vk::lwin vk::rwin vk::numpad0 .. vk::numpad9 vk::multiply vk::add vk::subtract vk::decimal vk::divide vk::f1 .. vk::f12 vk::num\_lock vk::scroll\_lock vk::lshift vk::rshift vk::lctrl vk::rctrl vk::lalt vk::ralt // Mouse buttons (Win32 puts these in the same VK space): vk::lbutton vk::rbutton vk::mbutton vk::xbutton1 vk::xbutton2 \`\`\` ## Example: trigger an action on F1 press \`\`\`cpp void my\_tick(int64 data) { if (key\_fired(vk::f1)) { println("F1 pressed"); } } int64 main() { register\_routine(cast(my\_tick), 0); return 1; } \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter: \`\`\` GET https://docs.perception.cx/perception/enma/input-api.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. ---