Addresses versus runtime wrappers
The address layer records the native locations needed by RPS's checked operations and additional FO4VR evidence. A catalog entry alone does not make a raw native call safe.
CommonLibF4VR and CommonFramework
RPS keeps its FO4VR-specific contracts and checked operations. Standalone address aliases already supplied by CommonLibF4VR or F4VR-CommonFramework belong to those libraries.
The catalog at source revision a92d0d5 contains 197 symbols. The
2026-09-11 cleanup removed 33 catalog-only aliases: 28 supplied by
CommonLibF4VR and five by CommonFramework. Those aliases had no RPS runtime
callers. The checked runtime APIs, package version 0.1.0, and static-library
dependency requirements are unchanged.
A matching address is only one part of an integration contract. RPS retains bindings where the call signature, native layout, failure reporting, physics phase, or ownership requirements differ. Keep the RPS implementation when its FO4VR evidence establishes a correction to a shared declaration.
Removed aliases
Use the following tables to locate the existing shared declarations when
migrating a tool that referenced a removed RPS::Addresses::Symbol entry.
Each shared API still requires its own argument, lifetime, and execution
contract. The tables identify where the same addresses are declared; they do
not establish that every surrounding wrapper or object layout is interchangeable.
CommonLibF4VR — 28 aliases
| Removed RPS symbol | Existing shared declaration |
|---|---|
Physics_SetBodyVelocity | RE::hknpWorld::SetBodyVelocity |
Physics_SetBodyMotionProperties | RE::hknpWorld::SetBodyMotionProperties |
Physics_SetBodyMotion | RE::hknpWorld::SetBodyMotion |
Shape_ConvexPolytopeVtable | RE::VTABLE::hknpConvexPolytopeShape |
Body_CollisionObjectSetTransform | RE::bhkNPCollisionObject::SetTransform |
Body_CollisionObjectSetVelocity | RE::bhkNPCollisionObject::SetVelocity |
Body_CollisionObjectSetLinearVelocity | RE::bhkNPCollisionObject::SetLinearVelocity |
Body_CollisionObjectSetAngularVelocity | RE::bhkNPCollisionObject::SetAngularVelocity |
Body_CollisionObjectApplyLinearImpulse | RE::bhkNPCollisionObject::ApplyLinearImpulse |
Body_CollisionObjectApplyPointImpulse | RE::bhkNPCollisionObject::ApplyPointImpulseAt |
Body_CollisionObjectSetMass | RE::bhkNPCollisionObject::SetMass |
Body_CollisionObjectGetFilterInfo | RE::bhkNPCollisionObject::GetCollisionFilterInfo |
Body_CollisionObjectGetCenterOfMassWorld | RE::bhkNPCollisionObject::GetCenterOfMassInWorld |
Body_CollisionObjectGetShape | RE::bhkNPCollisionObject::GetShape |
World_AddStepListener | RE::bhkWorld::AddPhysicsStepListener |
World_CastRay | RE::hknpWorld::CastRay |
World_GetClosestPoints | RE::hknpWorld::GetClosestPoints |
World_QueryAabb | RE::hknpWorld::QueryAabb (collector overload) |
World_QueryAabbBroadphaseOnly | RE::hknpWorld::QueryAabb (body-ID array overload) |
Scene_NiNodeVtable | RE::VTABLE::NiNode |
Constraint_RagdollDataVtable | RE::VTABLE::hkpRagdollConstraintData |
Animation_PlayerControlsDoAction | RE::PlayerControls::DoAction |
Animation_GraphManagerVtable | RE::VTABLE::BSAnimationGraphManager |
Animation_ClipGeneratorVtable | RE::VTABLE::hkbClipGenerator |
Animation_BehaviorGraphVtable | RE::VTABLE::hkbBehaviorGraph |
Animation_GraphVtable | RE::VTABLE::BShkbAnimationGraph |
Runtime_EquippedWeaponAttachManager | RE::TaskQueueInterface::GetSingleton |
Render_TargetManager | RE::BSGraphics::RenderTargetManager::GetSingleton |
CommonFramework — five aliases
| Removed RPS symbol | Existing shared declaration |
|---|---|
Animation_GetCurrentWeapon | f4cf::f4vr::Actor_GetCurrentWeapon |
Runtime_MainLoopHookSite | f4cf::f4vr::hookSomeRandomFunc |
Runtime_LateCullingHookSite | f4cf::f4vr::hookBoneTreeUpdate |
Render_PrimaryDrawCallsite | f4cf::f4vr::hookMainDrawCandidate |
Render_PrimaryDrawTarget | f4cf::f4vr::hookedMainDrawCandidateFunc |
Rebuild consumers with matching RPS headers and libraries after updating
removed enum references. In particular, Physics_SetBodyVelocityDeferred
remains in RPS for the checked deferred operation; it is not a rename for the
removed immediate-velocity alias.
RE-backed differences retained in RPS
The source comments record targeted FO4VR disassembly checks from 2026-09-11:
| Operation | RPS contract retained | Shared declaration or helper differs |
|---|---|---|
| Recursive scene motion/collision | The scene root is the first argument; the native success byte is preserved in the command result. | CommonLib's bhkWorld member wrappers insert an extra world argument. CommonFramework's free motion binding discards the success byte. |
| Scene world-bound update | Virtual slot 0x36 dispatches the bound updater. | CommonLib selects 0x33, which reaches the three-argument downward pass in the verified NiNode table. |
| Scene application culling | Virtual slot 0x30 calls the native setter, which also marks ancestors dirty. | CommonFramework's visibility helper changes only the object's cull bit. |
The witnesses and native locations are recorded beside the contracts in
RPS/Addresses/Fallout4Vr_1_2_72.inc and RPS/Addresses/Layouts.h.
Use the scene guide for the checked
call patterns.
Inspect catalog evidence
#include <RPS/Addresses/Catalog.h>
using namespace RPS::Addresses;
const SymbolRecord& entry = record(Symbol::Physics_SetBodyVelocityDeferred);
const auto name = entry.name;
const auto rva = entry.rva;
const auto kind = entry.kind;
const auto source = entry.source;
const auto evidence = entry.evidence;
const auto runtime = entry.runtime;
Every record contains:
- a typed symbol name;
- an RVA, never an absolute process address;
- symbol kind: function, data, vtable, callsite, hook site, or patch site;
- subsystem and source project;
- in-game-proven evidence classification;
- the exact FO4VR runtime version.
String tools can use find(name) or findByRva(rva). Runtime integrations
should prefer the typed Symbol enum.
Prefer a runtime wrapper
This direct resolution:
const auto address = module.resolve(
RPS::Addresses::Symbol::Physics_SetBodyVelocityDeferred);
only proves that the symbol's RVA fits inside the supported module image. It does not prove that your world is current, the body exists, the current thread owns a write epoch, the vectors are finite, the call is legal in this phase, or the postcondition succeeded.
The corresponding wrapper owns those checks:
RPS::Runtime::Physics::WorldWriteGuard write{module, hknpWorld};
RPS::Runtime::Physics::Api physics{module, hknpWorld};
const bool changed = physics.setVelocityDeferred(
write,
bodyId,
linearVelocity,
angularVelocity);
Use the first form only when no wrapper exists and your component explicitly owns the missing lifetime, synchronization, and validation boundary.
Resolve only after executable detection
const auto module = RPS::Runtime::RuntimeModule::detect();
if (!module) {
return false;
}
using Function = void (*)(
void*,
std::uint32_t,
const float*,
const float*);
const Function function = module.resolveFunction<Function>(
RPS::Addresses::Symbol::Physics_SetBodyVelocityDeferred);
if (!function) {
return false;
}
The function type and calling contract must come from the RPS source/evidence for that exact symbol. Do not infer a signature from the symbol name or a flat Fallout 4 declaration.
Verify identity before hooks and patches
RuntimeModule::matches() compares a masked byte pattern at a cataloged symbol.
Hooks::inspectExecutableSite() combines runtime resolution, symbol-kind
checking, executable memory validation, and entry-pattern comparison.
Direct-call preflight additionally proves that the site starts with E8, its
relative target decodes safely, and the target matches the expected native
function:
#include <RPS/Runtime/HookPatch.h>
using namespace RPS::Runtime::Hooks;
const SiteInspection site = inspectDirectCall(
callsiteAddress,
expectedNativeTarget);
if (!site) {
return false;
}
Patch a related group transactionally:
std::array<DirectCallPatch, 2> patches{
DirectCallPatch{siteA, expectedA, replacementA},
DirectCallPatch{siteB, expectedB, replacementB},
};
std::array<std::uintptr_t, 2> originals{};
const auto patched = patchDirectCallsTransactional(patches, originals);
if (!patched) {
reportPatchFailure(patched.status, patched.failedIndex);
return false;
}
The transaction preflights the group, rejects duplicate sites, captures original targets, commits the writes, and attempts rollback after a partial failure.
What HookPatch does not provide
It does not:
- suspend or quiesce threads;
- allocate executable trampolines;
- decode arbitrary overwritten instruction sequences;
- chain multiple hook owners;
- own callback or original-function storage;
- wait for in-flight callbacks at shutdown;
- decide when it is safe to restore a site.
Direct E8 rewriting is not instruction-stream atomic. One explicit component
must own the complete install/use/uninstall lifecycle and prevent other threads
from executing the site while it is being rewritten.
Catalog-only does not mean stable callable API
Some evidence is intentionally catalog-only because a general ownership boundary is not complete. Examples include animation preharvest loading, passive clip lifecycle functions, physics contact subscription, actor path request submission, character physics hooks, global collision matrix ownership, and the collision-audio global.
Using those symbols requires a consumer design that supplies process-global
ownership, registration/unregistration, callback quiescence, generation
protection, and deterministic restore. Do not present a catalog record alone as
a supported high-level feature. Physics-step listener registration is supplied
by CommonLibF4VR's bhkWorld::AddPhysicsStepListener; its former RPS alias was
removed. RPS still provides copied timing samples, while the consumer owns any
listener registration and teardown.
Withheld paths
The stable runtime intentionally excludes:
- direct ragdoll constraint/pivot mutation;
- live world-from-model compensation;
- the single-witness graph active-index field;
- custom six-axis constraint shellcode/vtable construction;
- weak legacy REL-ID-only candidates.
Those exclusions prevent an address collection from becoming a false promise of safe multi-consumer behavior.