Appearance
aowlspt_debugui.h
Source: abi/aowlspt_debugui.h — 1105 lines, 69 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_debugui.h -- the C half of the in-game, UNITY-NATIVE debug overlay:
a Minecraft-F3-style info panel and in-world markers over AI bots.
===========================================================================
WHY THIS IS NOT A D3D OVERLAY
===========================================================================
The host already HAS a D3D11 Present overlay (`aowlspt_overlay.h`), and it is
the wrong tool for this job: it draws into the swapchain after Unity is done,
so it knows nothing about the game's canvas, its scaling, its fonts, or --
decisively -- where a bot is on screen. What is wanted here is a panel the
GAME renders: real Unity UI, in the real canvas, in the game's own font,
scaling with the game's own resolution.
Post-1.0 that used to be impossible, because IL2CPP reflection on this build
is dead (`il2cpp_object_get_class`, `il2cpp_class_get_name`, `il2cpp_value_box`
and field iteration all FAULT -- the P2-P5 verdict). It stopped being
impossible when `abi/aowlspt_invoke2.h` established that IL2CPP AOT-compiles
every managed method into an ordinary native function which can be CALLED
DIRECTLY at its RVA, with a plain Win64 convention plus a hidden trailing
`const MethodInfo*`. This header is the first real FEATURE built on that.
===========================================================================
THE UI-CREATION PATH: CLONE, NOT NEW
===========================================================================
`il2cpp_object_new` + `GameObject::.ctor` + `AddComponent<T>` is a chain of
three things, two of which are unproven on this build (the generic
`MethodInfo*` for `AddComponent<T>` is read from a LAZY .data cache slot that
is NULL until TMP's own default-control code has run, and the
`AddComponent(Type)` route needs `il2cpp_class_get_type` /
`il2cpp_type_get_object` -- reflection surface).
`UnityEngine.Object::Instantiate(Object)` @ 0x52ADBE0 is ONE static call with
ONE reference argument, needs neither a `System.Type` nor a generic
`MethodInfo*`, and hands back a live clone of whatever it was given. So every
label this overlay draws is a CLONE of a label the game already built, and
the feature has no dependency on object creation at all.
The clone anchor is deliberately the most persistent label in the client:
EFT.UI.PreloaderUI (the root, whole-session UI)
+ 0x020 _alphaVersionLabel -> EFT.UI.LocalizedText
+ 0x078 _labels -> List<TMPro.TextMeshProUGUI>
+ 0x010 _items -> TMP[] + 0x020 elems -> [0]
-- i.e. the bottom-left version label. It exists in the menu AND in a raid,
it is a TextMeshProUGUI, and the host has ALREADY proven live that it can
write that component's text by raw field store (the version brand). Cloning
the COMPONENT rather than its GameObject is deliberate: Unity clones the whole
GameObject either way, but cloning the component hands back the CLONED
COMPONENT, which is the pointer the raw `m_text` write needs. Cloning the
GameObject would hand back a GameObject and leave us needing `GetComponent`,
which needs a Type again.
Parenting: `Transform::get_root` on the anchor's transform reaches the Canvas
root, whose RectTransform spans the screen -- so a corner anchor means a
SCREEN corner, which is what a layout config expects. Then
`TMP_DefaultControls::SetParentAndAlign(cloneGO, rootGO)` (static, two
GameObjects, no Type, no generic) puts the clone in the live hierarchy.
===========================================================================
THE TARGETS ADDED HERE, AND HOW THEY WERE RESOLVED
===========================================================================
Every RVA below is for `GameAssembly.dll` imagebase 0x180000000, build
1.1.0.1.46777, taken from the 185k-entry name->RVA map produced by
`tools/il2cpp_resolve.py`, and each one was then DISASSEMBLED to confirm its
argument shape before being written down. The shapes are not guesses:
* `UnityEngine.Camera::get_main` @ 0x5260400 -- STATIC, 0 args -> Camera.
5260400: sub rsp,0x28 ; mov rax,[rip+..] ; test rax,rax ; jne ..
5260428: add rsp,0x28 ; jmp rax <- lazy icall thunk, RCX unread
Identical shape to `Screen::get_width`, which the invoke2 ladder already
calls. RCX carries only the hidden MethodInfo*, and the body never reads it.
* `UnityEngine.Camera::WorldToScreenPoint(Vector3)` @ 0x525F940 -- INSTANCE,
one 12-byte struct argument and a 12-byte struct return, so Win64 puts the
return buffer FIRST and passes the argument BY ADDRESS:
525f940: movsd xmm0,[r8] <- R8 = &position (arg0, by address)
525f951: mov [rcx],rax <- RCX = &retbuf (sret, hidden arg)
525f954: mov rdi,rdx <- RDX = this (the Camera)
525f99f: mov r8d,2 <- eye = Mono, hardcoded: this IS the
525f9ad: call rax one-argument overload
so the call is (retbuf, this, &pos, MethodInfo*) and R9 (the MethodInfo) is
used as scratch by the body -- never read. NOTE the sibling at 0x525F700 is
the TWO-argument overload (`..., MonoOrStereoscopicEye`), whose R9 is the
EYE and whose MethodInfo goes on the stack; calling that one by mistake
would pass a garbage eye, which is exactly why both were disassembled.
* `UnityEngine.RectTransform::set_anchoredPosition` @ 0x52B5490,
`set_anchorMin` @ 0x52B5310, `set_anchorMax` @ 0x52B53D0,
`set_pivot` @ 0x52B5610 -- INSTANCE, one Vector2. A Vector2 is EIGHT bytes,
so Win64 passes it BY VALUE in an integer register, and all four bodies
agree:
52b549d: mov rbx,rcx <- RCX = this
52b54a0: mov [rsp+0x20],rdx <- RDX = the Vector2, packed
52b54c2: lea rdx,[rsp+0x20] <- spilled, then passed by address to
52b54ca: call rax the icall
so the call is (this, <two floats packed into one uint64>, MethodInfo*).
`aowl_du_call_setvec2` builds that uint64 with memcpy rather than a union
cast, so it is well-defined and endianness-explicit: x in the LOW half.
* `UnityEngine.Component::get_transform` @ 0x73B0F0 and
`UnityEngine.Transform::get_root` @ 0x52B9C50 -- INSTANCE, 0 args ->
reference. The exact shape of `Component::get_gameObject` @ 0x11F57E0 that
invoke2 STEP 1 already proved live, so they reuse its thunk.
* `EFT.UI.PreloaderUI::Update` @ 0x1569F20 -- the per-frame Unity-thread
anchor this whole feature hangs off. PreloaderUI is the ROOT UI
MonoBehaviour: it owns the version label, the FPS counter, the console and
the notifier, it is alive from the preloader through the menu and through a
raid, and its `Update` is an ordinary MonoBehaviour tick. Its prologue is
`mov rax,rsp ; mov [rax+0x10],rbx ; mov [rax+0x20],rsi ; mov [rax+8],rcx ;
push rdi` -- sixteen bytes with no RIP-relative operand, so the detour
engine's copier relocates it cleanly. RCX = the live PreloaderUI, which is
simultaneously the frame tick AND the clone anchor.
It is used in preference to `TarkovApplication::Update` @ 0x977B10
deliberately: that one has a live crash in its history (see the host
internals note), and it does not hand us a UI object.
===========================================================================
FIELD OFFSETS
===========================================================================
Resolved OFFLINE from `Il2CppMetadataRegistration.fieldOffsets` in
GameAssembly.dll indexed by type, against the decrypted global-metadata --
the same technique that produced the botdiag and settings-UI offsets, and
self-checked on `System.String` (`_stringLength` @ 0x10, `_firstChar` @ 0x14)
before anything else was believed. NOT from live `il2cpp_field_get_offset`,
which is part of the faulting reflection API.
===========================================================================
SAFETY
===========================================================================
Nothing here calls anything on its own; it is a table, a set of thunks and a
set of guarded stores. `host/Aowlspt.Host.Il2Cpp/debugui.nim` drives it, from
inside the PreloaderUI::Update detour (Unity thread), with the entire body
under the `aowl_p_p_seh` VEH/setjmp guard, every pointer hop
`aowl_is_readable`-guarded, every iteration capped, and both features
flag-gated default-OFF. A fault is caught, logged and skipped; it can never
take the client down.Constants
AOWLSPT_DEBUGUI_HAOWL_DU_ARR_ELEMSAOWL_DU_CAMERA_MAINAOWL_DU_CANVAS_MODEAOWL_DU_CANVAS_ORDERAOWL_DU_CANVAS_SCALEAOWL_DU_GET_ANCHOREDPOSAOWL_DU_GET_ANCHORMINAOWL_DU_GET_LOCALPOSAOWL_DU_GET_LOCALSCALEAOWL_DU_GET_PARENTAOWL_DU_GET_PIVOTAOWL_DU_GET_RECTAOWL_DU_GET_ROOTAOWL_DU_GET_SIZEDELTAAOWL_DU_GET_TRANSFORMAOWL_DU_GO_ACTIVEINHIERAOWL_DU_GO_ACTIVESELFAOWL_DU_GO_LAYERAOWL_DU_GW_LOCATIONIDAOWL_DU_KEYSLOTSAOWL_DU_LIST_ITEMSAOWL_DU_LIST_SIZEAOWL_DU_LOC_LABELSAOWL_DU_MC_PREVPOSAOWL_DU_MC_ROTATIONAOWL_DU_PRELOADER_UPDATE_RVAAOWL_DU_PRE_VERSIONLABELAOWL_DU_REVERIFY_MSAOWL_DU_SET_ANCHOREDPOSAOWL_DU_SET_ANCHORMAXAOWL_DU_SET_ANCHORMINAOWL_DU_SET_LOCALPOSAOWL_DU_SET_LOCALSCALEAOWL_DU_SET_PIVOTAOWL_DU_SET_SIZEDELTAAOWL_DU_STR_CHARSAOWL_DU_STR_LENAOWL_DU_TARGET_COUNTAOWL_DU_TMP_AUTOSIZEAOWL_DU_TMP_DIRTYAOWL_DU_TMP_FIRSTVISAOWL_DU_TMP_FONTCOLORAOWL_DU_TMP_FONTSIZEAOWL_DU_TMP_GCANVASAOWL_DU_TMP_GCOLORAOWL_DU_TMP_GRECTAOWL_DU_TMP_HALIGNAOWL_DU_TMP_MARGINAOWL_DU_TMP_MAXVISCHARSAOWL_DU_TMP_MAXVISLINESAOWL_DU_TMP_MAXVISWORDSAOWL_DU_TMP_OVERFLOWAOWL_DU_TMP_PAGEAOWL_DU_TMP_RECTAOWL_DU_TMP_TEXTAOWL_DU_TMP_TEXTALIGNAOWL_DU_TMP_VALIGNAOWL_DU_TMP_WORDWRAPAOWL_DU_WORLD_TO_SCREEN
Types
struct AowlDuTarget
Functions
| Signature | Line |
|---|---|
int32_t aowl_du_off_pre_versionlabel(void) | 236 |
int32_t aowl_du_off_loc_labels(void) | 237 |
int32_t aowl_du_off_list_items(void) | 238 |
int32_t aowl_du_off_list_size(void) | 239 |
int32_t aowl_du_off_arr_elems(void) | 240 |
int32_t aowl_du_off_tmp_text(void) | 241 |
int32_t aowl_du_off_tmp_fontsize(void) | 242 |
int32_t aowl_du_off_tmp_fontcolor(void) | 243 |
int32_t aowl_du_off_tmp_dirty(void) | 244 |
int32_t aowl_du_off_tmp_rect(void) | 245 |
int32_t aowl_du_off_mc_prevpos(void) | 246 |
int32_t aowl_du_off_mc_rotation(void) | 247 |
int32_t aowl_du_off_gw_locationid(void) | 248 |
int32_t aowl_du_off_tmp_gcolor(void) | 249 |
int32_t aowl_du_off_tmp_gcanvas(void) | 250 |
int32_t aowl_du_off_tmp_autosize(void) | 251 |
int32_t aowl_du_off_tmp_halign(void) | 252 |
int32_t aowl_du_off_tmp_valign(void) | 253 |
int32_t aowl_du_off_tmp_textalign(void) | 254 |
int32_t aowl_du_off_tmp_wordwrap(void) | 255 |
int32_t aowl_du_off_tmp_overflow(void) | 256 |
int32_t aowl_du_off_tmp_firstvis(void) | 257 |
int32_t aowl_du_off_tmp_maxvischars(void) | 258 |
int32_t aowl_du_off_tmp_maxviswords(void) | 259 |
int32_t aowl_du_off_tmp_maxvislines(void) | 260 |
int32_t aowl_du_off_tmp_page(void) | 261 |
int32_t aowl_du_off_tmp_margin(void) | 262 |
void aowl_du_fn_full(int32_t i) | 605 |
void aowl_du_fn(int32_t i) | 648 |
char aowl_du_name(int32_t i) | 661 |
uint32_t aowl_du_rva(int32_t i) | 665 |
int32_t aowl_du_target_count(void) | 669 |
int32_t aowl_du_profull_count(void) | 670 |
int32_t aowl_du_ok_count(void) | 675 |
int32_t aowl_du_bad_count(void) | 676 |
int32_t aowl_du_tried_count(void) | 680 |
void aowl_du_preloader_update_target(void) | 701 |
uint32_t aowl_du_preloader_update_rva(void) | 733 |
void aowl_du_call_v_pb(void* fn, void* self, int32_t b) | 750 |
void aowl_du_call_p_v(void* fn) | 756 |
void aowl_du_call_p_p(void* fn, void* self) | 765 |
void aowl_du_call_setvec2(void* fn, void* self, double x, double y) | 776 |
int32_t aowl_du_world_to_screen(void* fn, void* cam, double wx, double wy, double wz) | 808 |
uint64_t aowl_du_call_u_p(void* fn, void* self) | 832 |
double aowl_du_vec2_x(uint64_t packed) | 836 |
double aowl_du_vec2_y(uint64_t packed) | 839 |
int32_t aowl_du_call_i_p(void* fn, void* self) | 845 |
double aowl_du_call_f_p(void* fn, void* self) | 850 |
int32_t aowl_du_call_sret_p(void* fn, void* self, int32_t nfloats) | 865 |
int32_t aowl_du_call_sret_p_v3(void* fn, void* self, double ax, double ay, double az, int32_t nfloats) | 890 |
double aowl_du_sret_0(void) | 909 |
double aowl_du_sret_1(void) | 910 |
double aowl_du_sret_2(void) | 911 |
double aowl_du_sret_3(void) | 912 |
void aowl_du_call_setvec3(void* fn, void* self, double x, double y, double z) | 919 |
int32_t aowl_du_string_len(void* s) | 930 |
double aowl_du_screen_x(void) | 942 |
double aowl_du_screen_y(void) | 943 |
double aowl_du_screen_z(void) | 944 |
int32_t aowl_du_writable(void* p, int32_t off, size_t n) | 957 |
int32_t aowl_du_write_i32(void* p, int32_t off, int32_t v) | 976 |
int32_t aowl_du_write_u8(void* p, int32_t off, int32_t v) | 981 |
int32_t aowl_du_write_f32(void* p, int32_t off, double v) | 987 |
int32_t aowl_du_write_color(void* p, int32_t off, double r, double g, double b, double a) | 995 |
int32_t aowl_du_key_edge(int32_t vk) | 1021 |
int32_t aowl_du_foreground(void) | 1047 |
void aowl_du_fps_sample(void) | 1076 |
double aowl_du_fps(void) | 1101 |
int64_t aowl_du_frame_no(void) | 1102 |

