Appearance
aowlspt_camera.h
Source: abi/aowlspt_camera.h — 455 lines, 22 file-scope functions.
What this header owns
Reproduced verbatim from the header's own banner comment — these notes are frequently the only written record of why the subsystem is shaped the way it is.
text
aowlspt_camera.h -- the C half of the host CAMERA API.
===========================================================================
WHAT THIS IS
===========================================================================
A guarded, byte-verified surface for READING and WRITING the live camera:
position, rotation (quaternion and euler), field of view, near/far clip, and
the three basis vectors. It is the foundation the free camera in
`host/Aowlspt.Host.Il2Cpp/camera.nim` is built on, and it is deliberately a
plain API rather than one feature's private plumbing -- mods and the live
inspector call the same entry points.
===========================================================================
WHAT IS NOT HERE, AND WHY
===========================================================================
There is NO camera-acquisition target in this table. `UnityEngine.Camera::
get_main` @0x5260400 already lives in `aowlspt_debugui.h`'s verified table
(index AOWL_DU_CAMERA_MAIN) and is already the deploy latch's signal S5 in
`raidphase.nim`. A second acquisition path would be a second answer to the
question "which camera?", and the two would drift. `camera.nim` calls
`aowl_du_fn(AOWL_DU_CAMERA_MAIN)` / `aowl_du_call_p_v`, and reaches the
camera's Transform through `aowl_du_fn(AOWL_DU_GET_TRANSFORM)`
(`UnityEngine.Component::get_transform` @0x73B0F0) -- also already verified
there.
`EFT.CameraControl.CameraManager::SetFov` @0x1268D20 is NOT here and must not
be added. MEASURED by disassembly (and re-stated in `mods/fov/fov.nim`): it
opens `mov rdi,[rbx+0x70]; test rdi,rdi; je <epilogue>` and `<Camera>@0x70`
reads null on this build, so every call through it is a silent no-op that
still returns success. Writing `Camera::set_fieldOfView` on the camera we
actually hold is the only route that lands.
===========================================================================
THE ABI, MEASURED -- NOT ASSUMED
===========================================================================
Every claim below was read out of `GameAssembly.dll` with
`tools/il2cpp_resolve.py ... type UnityEngine.Camera|UnityEngine.Transform`
plus a 32-56 byte code dump at each RVA. The bytes are quoted in the comment
beside each row.
A Vector3 is 12 bytes -- neither 1/2/4/8 -- so Win64 cannot pass or return it
in a register. The build agrees, and says so twice:
RETURN (hidden buffer / sret). `Transform::get_position` @0x52B70E0 opens
33 C0 xor eax,eax
48 8B FA mov rdi,rdx <- `this` arrives in RDX
48 89 01 mov [rcx],rax <- 8 bytes of the retbuf at RCX
48 8B D9 mov rbx,rcx
89 41 08 mov [rcx+8],eax <- the 12th byte
i.e. exactly 12 bytes zeroed through RCX before the icall. So the shape is
(retbuf in RCX, this in RDX, MethodInfo* in R8) -- the SAME shape the
already-live `RectTransform::get_rect` sret path uses. `get_eulerAngles`,
`get_forward`, `get_right` and `get_up` open with the identical
`33 C0 / 0F 57 C0 / 48 89 01 / 48 8B FA / 89 41 08`.
ARGUMENT (by address). `Transform::set_eulerAngles` @0x52B7320 opens
F2 0F 10 02 movsd xmm0,[rdx] <- x,y read THROUGH rdx
48 8B D9 mov rbx,rcx <- rcx is `this`
...
F3 0F 10 4A 08 movss xmm1,[rdx+8] <- z
RDX is dereferenced, so it is a POINTER to the Vector3, not the value.
`set_position`/`set_rotation` carry RDX through untouched
(`48 8B DA` ... `48 8B D3`) into the icall, which is the same conclusion.
A Quaternion is 16 bytes and takes the same two treatments: `get_rotation`
@0x52B7F60 zeroes the retbuf with `0F 57 C0 / 0F 11 01` (movups [rcx],xmm0 --
all 16 bytes at once), and `set_rotation` passes RDX through by address.
A float argument is ordinary: `Camera::set_fieldOfView` @0x525DD30 does
`0F 28 F1 movaps xmm6,xmm1`, so the value is in XMM1 with `this` in RCX.
This differs from the Vector2 case only because of size: a Vector2 is 8 bytes
and comes back PACKED IN RAX (`aowl_du_call_u_p`). Three sizes, three
conventions, all measured.
===========================================================================
SHAREDNESS
===========================================================================
`tools/il2cpp_resolve.py ... shared <rva>` over every row below reports
`unique` (owners=1) for ALL FIFTEEN. Nothing here is detoured in any case --
this file only CALLS -- but the check was run because 28.3% of by-name
lookups land on a shared RVA and "we only call it" is not a reason to skip a
measurement that is free.
For completeness and because it is the one exception worth stating:
`Component::get_transform` @0x73B0F0, which `camera.nim` reuses out of the
debugui table, is SHARED (15 owners). Calling a shared address is correct
code for the receiver passed; DETOURING one would not be. It is called here
and never patched.
===========================================================================
SAFETY
===========================================================================
`aowl_cam_fn` is a byte-for-byte copy of `aowl_du_fn`'s discipline:
GameAssembly.dll must be mapped (through `aowl_cam_ga`, the single
never-cached accessor -- and if it is absent the caller must RETRY rather
than record a verdict; see the comment on that function), the address must
land in COMMITTED
EXECUTABLE memory (checked before the memcmp, because a stale RVA on another
build can point at an uncommitted page and memcmp there faults), and the
prologue is compared against the STARTUP SNAPSHOT (`aowl_pro_verify`) rather
than live memory -- so a feature that binds second still verifies instead of
reading someone else's trampoline and self-rejecting.
Per-target state is recorded once (`aowl_cam_state`), so the verified/rejected
counters are counts of DISTINCT TARGETS and not of successful calls -- the
defect that once printed "36 of 28 managed targets verified".
Every thunk refuses a NULL function pointer and a NULL receiver rather than
calling through it, and every sret read rejects a NaN or an absurd magnitude
and leaves the output statics alone, because a garbage coordinate that still
looks like a number is the failure this whole file exists to stop.
This header opens NO `aowl_p_p_seh` guard of its own. `camera.nim` opens
exactly one around its whole body; that guard is not re-entrant, so a nested
one here would DISARM it.
------------------------------------------------------------------Constants
AOWLSPT_CAMERA_HAOWL_CAM_GET_EULERAOWL_CAM_GET_FARAOWL_CAM_GET_FORWARDAOWL_CAM_GET_FOVAOWL_CAM_GET_NEARAOWL_CAM_GET_POSITIONAOWL_CAM_GET_RIGHTAOWL_CAM_GET_ROTATIONAOWL_CAM_GET_UPAOWL_CAM_SET_EULERAOWL_CAM_SET_FARAOWL_CAM_SET_FOVAOWL_CAM_SET_NEARAOWL_CAM_SET_POSITIONAOWL_CAM_SET_ROTATIONAOWL_CAM_TARGET_COUNT
Types
struct AowlCamTarget
Functions
| Signature | Line |
|---|---|
HMODULE aowl_cam_ga(void) | 279 |
int32_t aowl_cam_module_ready(void) | 282 |
int32_t aowl_cam_wait_count(void) | 283 |
void aowl_cam_fn(int32_t i) | 285 |
char aowl_cam_name(int32_t i) | 322 |
uint32_t aowl_cam_rva(int32_t i) | 326 |
int32_t aowl_cam_target_count(void) | 330 |
int32_t aowl_cam_ok_count(void) | 331 |
int32_t aowl_cam_bad_count(void) | 332 |
int32_t aowl_cam_reason(void) | 333 |
int32_t aowl_cam_profull_count(void) | 334 |
char aowl_cam_reason_text(void) | 337 |
double aowl_cam_get_f(void* fn, void* self) | 364 |
int32_t aowl_cam_get_f_ok(void* fn, void* self) | 375 |
int32_t aowl_cam_set_f(void* fn, void* self, double v) | 386 |
int32_t aowl_cam_get_v(void* fn, void* self, int32_t nfloats) | 400 |
double aowl_cam_v0(void) | 418 |
double aowl_cam_v1(void) | 419 |
double aowl_cam_v2(void) | 420 |
double aowl_cam_v3(void) | 421 |
int32_t aowl_cam_set_v(void* fn, void* self, double x, double y, double z, double w) | 427 |
int32_t aowl_cam_key_down(int32_t vk) | 449 |

