Skip to content

aowlspt_unitypp.h

Source: abi/aowlspt_unitypp.h — 504 lines, 11 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_unitypp.h -- Unity Post Processing Stack v2, READ-ONLY DISCOVERY.

===========================================================================
WHY THIS FILE EXISTS
===========================================================================

`mods/graphics` renders nothing itself. The renderer is native D3D11 in
`abi/aowlspt_graphics.h`, hooked off the overlay's Present: it copies the
FINISHED back buffer and grades it. That is post-hoc -- it fights the game's
own rendering, it is ordered after everything including the UI, and it has
no honest access to depth or motion vectors. The standing instruction is to
stop doing that and drive the game's OWN post-processing instead.

So the first question is not "how do we write the grade", it is "what post
processing does THIS build actually have". That was answered OFFLINE, before
a line of this file was written, with `tools/il2cpp_resolve.py` and
`tools/fldoff.py` against D:/Aowlspt/GameAssembly.dll and the decrypted
global-metadata. The answer:

  Unity.Postprocessing.Runtime.dll IS PRESENT AND UNSTRIPPED.

118 types in namespace `UnityEngine.Rendering.PostProcessing`, including
every one that matters here:

  PostProcessLayer  PostProcessVolume  PostProcessProfile  PostProcessManager
  PostProcessEffectSettings  PostProcessBundle  PostProcessRenderContext
  ColorGrading  Bloom  Vignette  Grain  ChromaticAberration  AmbientOcclusion
  DepthOfField  MotionBlur  LensDistortion  AutoExposure  ScreenSpaceReflections
  TemporalAntialiasing  SubpixelMorphologicalAntialiasing  Fog  Dithering

plus the whole ParameterOverride family (FloatParameter, BoolParameter,
ColorParameter, Vector2/3/4Parameter, IntParameter, SplineParameter,
TextureParameter, and the enum-typed TonemapperParameter / GradingModeParameter
/ VignetteModeParameter / KernelSizeParameter).

That is the real answer to "does the stack exist", and it means the D3D11
path can eventually be retired rather than merely wrapped.

===========================================================================
THE OFFSETS -- AND THE ONE THAT IS NOT KNOWABLE OFFLINE
===========================================================================

All of these come from `Il2CppMetadataRegistration.fieldOffsets` via
`tools/fldoff.py fields <Type>`, whose mandatory self-check
(System.String._stringLength@0x10, _firstChar@0x14) passed. None is guessed.

  PostProcessVolume            sharedProfile        @0x20   PostProcessProfile
                               isGlobal             @0x28   bool
                               blendDistance        @0x2C   float
                               weight               @0x30   float
                               priority             @0x34   float
                               m_InternalProfile    @0x48   PostProcessProfile

  PostProcessProfile           settings             @0x18   List<PostProcessEffectSettings>
                               isDirty              @0x20   bool

  PostProcessEffectSettings    active               @0x18   bool
                               enabled              @0x20   BoolParameter
                               parameters           @0x28   ReadOnlyCollection<ParameterOverride>

  ColorGrading (: EffectSettings)
                               gradingMode          @0x30   temperature @0x88
                               tonemapper           @0x40   tint        @0x90
                               toneCurveToeStrength @0x48   colorFilter @0x98
                               ldrLutContribution   @0x80   hueShift    @0xA0
                               saturation           @0xA8   brightness  @0xB0
                               postExposure         @0xB8   contrast    @0xC0
                               lift @0x110  gamma @0x118  gain @0x120

  Vignette (: EffectSettings)  mode      @0x30   color     @0x38  center @0x40
                               intensity @0x48   smoothness@0x50  roundness@0x58
                               rounded   @0x60   mask      @0x68  opacity @0x70

  ParameterOverride (NON-generic base)
                               overrideState        @0x10   bool   <-- REAL

---- THE HOLE, STATED PLAINLY -------------------------------------------

`FloatParameter` declares NO fields of its own. Its value lives in
`ParameterOverride<T>.value`, and `fldoff.py` reports that field as

     GENERIC -- NO LAYOUT: uninstantiated generic type definition

which is CLAUDE.md 5's three-state offset rule biting exactly where it says
it will. IL2CPP writes an all-zero fieldOffsets array for `ParameterOverride`1`;
the concrete layout of `ParameterOverride<float>` is built at runtime and
every `Il2CppGenericClass.cached_class` in GameAssembly.dll is null. It is
NOT reachable offline, and no amount of reading the file will change that.

The obvious inference is `value @ 0x14` (bool at 0x10, float padded to its
natural 4-byte alignment). It is PROBABLY right. It is still an INFERENCE,
and a wrong offset here does not fault -- it reads a plausible float and then
a WRITE at that offset corrupts a live managed object every frame. That is
precisely the failure this project keeps paying for, so:

  THIS FILE DOES NOT WRITE. NOT ONE BYTE.

It PROBES: it walks to the live profile, names every effect it finds by
reading the object's own klass, and for ColorGrading it dumps the raw bytes
of `saturation` and `postExposure` at 0x10..0x20 into the host log. Two
FloatParameters with different, known-different authored values settle the
`value` offset from the log in ONE run, empirically, with no guess surviving.
Only after that run may a write path be built -- in a follow-up.

===========================================================================
BORROWED LAYOUTS (said out loud, as required)
===========================================================================

  List<T>   _items @0x10 -> T[],  _size @0x18 (int32)
  T[]       element 0 @0x20

BORROWED from `abi/aowlspt_botdiag.h` and `abi/aowlspt_debugui.h`, which have
both used this shape against live objects in this client. It is an
instantiated generic layout and therefore, per the rule, not offline-derivable
either. Every element read below is bounds-checked against `_size` AND capped,
and every element pointer is klass-named before anything is read through it,
so a wrong borrow produces a refusal in the log rather than a fabricated tree.

===========================================================================
TYPE CONFUSION IS THE REAL ENEMY, NOT UNREADABILITY
===========================================================================

`aowl_admin_readable()` proves a page is mapped. It does not prove the object
is what you think, and Unity's FAKE NULL means a live-looking managed pointer
can wrap a destroyed native object. So every hop here is checked TWICE:
readable, and then IDENTIFIED by `aowl_comp_klass_fullname()` -- the same
klass->name/namespace reader the inspector's `component` verb uses. A pointer
whose klass does not name a PostProcessing type is refused by name in the log.
That is the check that can actually fail, which is the only kind worth having.

===========================================================================
THE EIGHT RULES
===========================================================================

 1. Prologue byte-verify -- inherited. The only game code called is
    `Component::GetComponent(String)` @0x52A48E0, bound and 16-byte verified
    by `abi/aowlspt_navui.h`, and the camera acquisition in
    `abi/aowlspt_admin.h` (AOWL_ADM_SIG_CAM_MAIN / _CAM_MGR). No new RVA is
    introduced and nothing new is bound.
 2. VirtualQuery on EVERY hop -- `aowl_admin_readable` before each read.
 3. ONE `aowl_p_p_seh`, opened by `aowl_upp_tick` around the whole body.
    `aowl_upp_scan_body` opens NONE. Nesting would disarm the outer guard.
 4. Capped -- AOWL_UPP_MAX_EFFECTS effects, AOWL_UPP_NAME_MAX name bytes.
 5. Flag-gated, default OFF -- `unityPostProbe`.
 6. Self-disable after AOWL_UPP_MAXFAULT faults.
 7. No managed allocation. Ever. No `il2cpp_string_new` in the tick either:
    the GetComponent argument string is created ONCE, on the first tick, and
    cached in a static.
 8. Never blind-write -- vacuously, since it never writes.

The probe runs ONCE and latches. It is a survey, not a per-frame feature.

===========================================================================
PREREQUISITES
===========================================================================

Same translation unit as `aowlspt_admin.h` (for `aowl_admin_cam_object`,
`aowl_admin_readable`) and `aowlspt_components.h` (for the klass namer), and
after `aowlspt_region.h` for `aowl_region_sayf`. That is region.nim, and only
region.nim.

Constants

  • AOWLSPT_UNITYPP_H
  • AOWL_UPP_ARR_ELEM0
  • AOWL_UPP_CG_CONTRAST
  • AOWL_UPP_CG_POSTEXPOSURE
  • AOWL_UPP_CG_SATURATION
  • AOWL_UPP_LIST_ITEMS
  • AOWL_UPP_LIST_SIZE
  • AOWL_UPP_MAXFAULT
  • AOWL_UPP_MAX_EFFECTS
  • AOWL_UPP_PARAM_OVERRIDESTATE
  • AOWL_UPP_PROF_SETTINGS
  • AOWL_UPP_RVA_GETCOMPONENT
  • AOWL_UPP_SET_ACTIVE
  • AOWL_UPP_SET_ENABLED
  • AOWL_UPP_VOL_INTERNALPROFILE
  • AOWL_UPP_VOL_ISGLOBAL
  • AOWL_UPP_VOL_PRIORITY
  • AOWL_UPP_VOL_SHAREDPROFILE
  • AOWL_UPP_VOL_WEIGHT

Functions

SignatureLine
void aowl_upp_bind(void)232
int32_t aowl_upp_is_done(void)266
int32_t aowl_upp_is_disabled(void)267
int32_t aowl_upp_effect_count(void)268
int32_t aowl_upp_has_grading(void)269
int32_t aowl_upp_fault_count(void)270
int32_t aowl_upp_identify(const void *obj)275
void aowl_upp_dump_param(const char *what, const void *p)289
int32_t aowl_upp_walk_profile(const void *profile, const char *origin)322
void aowl_upp_scan_body(void *unused)400
void aowl_upp_tick(void)485

aoughwl — self-hosted platform for things n stuff. Contact / Support on Discord for access to the private backends.