Skip to main content

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 symbolExisting shared declaration
Physics_SetBodyVelocityRE::hknpWorld::SetBodyVelocity
Physics_SetBodyMotionPropertiesRE::hknpWorld::SetBodyMotionProperties
Physics_SetBodyMotionRE::hknpWorld::SetBodyMotion
Shape_ConvexPolytopeVtableRE::VTABLE::hknpConvexPolytopeShape
Body_CollisionObjectSetTransformRE::bhkNPCollisionObject::SetTransform
Body_CollisionObjectSetVelocityRE::bhkNPCollisionObject::SetVelocity
Body_CollisionObjectSetLinearVelocityRE::bhkNPCollisionObject::SetLinearVelocity
Body_CollisionObjectSetAngularVelocityRE::bhkNPCollisionObject::SetAngularVelocity
Body_CollisionObjectApplyLinearImpulseRE::bhkNPCollisionObject::ApplyLinearImpulse
Body_CollisionObjectApplyPointImpulseRE::bhkNPCollisionObject::ApplyPointImpulseAt
Body_CollisionObjectSetMassRE::bhkNPCollisionObject::SetMass
Body_CollisionObjectGetFilterInfoRE::bhkNPCollisionObject::GetCollisionFilterInfo
Body_CollisionObjectGetCenterOfMassWorldRE::bhkNPCollisionObject::GetCenterOfMassInWorld
Body_CollisionObjectGetShapeRE::bhkNPCollisionObject::GetShape
World_AddStepListenerRE::bhkWorld::AddPhysicsStepListener
World_CastRayRE::hknpWorld::CastRay
World_GetClosestPointsRE::hknpWorld::GetClosestPoints
World_QueryAabbRE::hknpWorld::QueryAabb (collector overload)
World_QueryAabbBroadphaseOnlyRE::hknpWorld::QueryAabb (body-ID array overload)
Scene_NiNodeVtableRE::VTABLE::NiNode
Constraint_RagdollDataVtableRE::VTABLE::hkpRagdollConstraintData
Animation_PlayerControlsDoActionRE::PlayerControls::DoAction
Animation_GraphManagerVtableRE::VTABLE::BSAnimationGraphManager
Animation_ClipGeneratorVtableRE::VTABLE::hkbClipGenerator
Animation_BehaviorGraphVtableRE::VTABLE::hkbBehaviorGraph
Animation_GraphVtableRE::VTABLE::BShkbAnimationGraph
Runtime_EquippedWeaponAttachManagerRE::TaskQueueInterface::GetSingleton
Render_TargetManagerRE::BSGraphics::RenderTargetManager::GetSingleton

CommonFramework — five aliases

Removed RPS symbolExisting shared declaration
Animation_GetCurrentWeaponf4cf::f4vr::Actor_GetCurrentWeapon
Runtime_MainLoopHookSitef4cf::f4vr::hookSomeRandomFunc
Runtime_LateCullingHookSitef4cf::f4vr::hookBoneTreeUpdate
Render_PrimaryDrawCallsitef4cf::f4vr::hookMainDrawCandidate
Render_PrimaryDrawTargetf4cf::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:

OperationRPS contract retainedShared declaration or helper differs
Recursive scene motion/collisionThe 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 updateVirtual slot 0x36 dispatches the bound updater.CommonLib selects 0x33, which reaches the three-argument downward pass in the verified NiNode table.
Scene application cullingVirtual 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.