Skip to content

REFramework v2#1609

Open
praydog wants to merge 28 commits intomasterfrom
v2
Open

REFramework v2#1609
praydog wants to merge 28 commits intomasterfrom
v2

Conversation

@praydog
Copy link
Owner

@praydog praydog commented Mar 7, 2026

Experimental branch to convert REFramework into one monolithic DLL covering all the games, getting rid of the need for multiple builds via runtime dispatch. Should be easier to maintain, takes up less space for nightlies and is less straining on CI.

Game TDB Status Notes
DMC5 67 ✅ Working
RE8 69 ✅ Working Canonical ReClass layout
RE7 70 ✅ Working VR crash fixed (ViaDispatch)
RE2 70 ✅ Working Head shrinks, gameplay functional
RE3 70 ✅ Working MAINCAM rotation fix pending verify
RE4 71 ✅ Working IL2CPP JSON dump crash separate issue (might affect all games)
SF6 71 ✅ Working
MHRISE 71 ✅ Working
DD2 73 ❌ D3D12 freeze
MHWILDS 74 ✅ Working
MHSTORIES3 82 ✅ Working Game seems blurry (ultrawide bugs?)
RE9 83 ✅ Working
PRAGMATA 83 ❌Broken Crashes with RE Engine id > 0 && id < header.typeImplCount assertion popup

Old (non-modern) builds of RE2, RE3, and RE7 are not tested and will need additional work.

Full VR functionality (motion controls) is not yet tested on RE2, RE3, RE7, RE8.

Type inspection/lookup seems broken in some games (maybe across the C# boundary? needs more investigation)

praydog added 26 commits March 5, 2026 12:48
- Add GameIdentity.hpp/cpp: Runtime game detection from exe name,
  TDB version mapping, engine parameter derivation (replaces compile-time
  RE2/RE3/RE4/.../REENGINE_PACKED/REENGINE_AT/TDB_VER macros)
- Restructure cmake.toml: Single REFrameworkSDK + REFramework targets
  replace ~30 per-game SDK+DLL targets. Define REFRAMEWORK_UNIVERSAL.
- Add PLAN_V2_MONOLITHIC.md documenting architecture decisions.
…runtime dispatch

- Mods.cpp: All conditional mod registration uses GameIdentity runtime checks
- REFramework.hpp: get_game_name() delegates to GameIdentity
- Main.cpp: Startup guards use GameIdentity; initialize() called early
- PluginLoader.cpp: game_name set at runtime in initialize_plugins()
- ReClass.hpp: REFRAMEWORK_UNIVERSAL includes RE8 layout as canonical base
- TDBVer.hpp: REFRAMEWORK_UNIVERSAL sets TDB_VER=84 so all code paths compile
…in src/

Batch conversion of game-specific preprocessor guards across 25 source files:

Camera, FreeCam, FirstPerson, ManualFlashlight:
- #ifdef RE8, RE2, RE3, RE4 → runtime gi.is_re8() etc.

Graphics, Hooks, REFrameworkConfig, ExceptionHandler:
- #ifdef SF6, MHWILDS, RE4, RE7 → runtime checks
- Member declarations gated with REFRAMEWORK_UNIVERSAL

IntegrityCheckBypass:
- Heavy per-game byte pattern ifdefs → runtime if/else if chains

RE8VR:
- Removed file-level #if defined(RE7)||defined(RE8) guard
- Added runtime early return in on_initialize()
- Internal RE7/RE8 guards → runtime checks

VR.cpp:
- Regenny include chain → REFRAMEWORK_UNIVERSAL block
- ~35 game guards → runtime checks

ObjectExplorer, ChainViewer, LooseFileLoader:
- TDB_VER and game guards → runtime checks

REFramework.cpp:
- DD2/MHRISE/TDB_VER >= 74 guards → runtime checks
…mbers, legacy TDB guards

- GameIdentity.hpp: Move static inline members to .cpp (incomplete type error)
- REFramework.cpp: Fix brace nesting in ldr_notification_callback
- FirstPerson.cpp: Fix missing closing braces in constructor and on_lua_state_created
- Graphics.cpp: Fix missing closing braces in on_pre/on_application_entry
- ObjectExplorer.cpp: Guard legacy TDB struct member access with #ifndef REFRAMEWORK_UNIVERSAL
- CMakeLists.txt: Regenerated by cmkr from updated cmake.toml (single unified target)
REType is 0x60 in most games but 0x68 in MHWILDS/RE9, with fields shifted
by 8 bytes after offset 0x28 (super, childType, chainType, fields, classInfo,
size, typeCRC all at different offsets).

- Add RETypeLayouts.hpp: defines reclass_mhwilds::REType (0x68 layout) and
  accessor functions in utility::re_type_accessor namespace that check
  GameIdentity at runtime and cast to the correct layout
- Replace all direct REType field accesses in shared/sdk/ and src/ with
  accessor calls (get_super, get_classInfo, get_fields, get_size, get_typeCRC,
  get_childType, get_chainType)
- REType.cpp, REManagedObject.cpp, REArray.cpp, RETypeDefinition.cpp,
  PluginLoader.cpp, ObjectExplorer.cpp updated
- REObjectInfo->classInfo and ParsedType->super accesses left untouched
  (different structs, same offset in all layouts)
Games with TDB < 73 (RE2, RE3, RE8, RE7, MHRISE) use 18-bit
TYPE_INDEX_BITS, but the universal build compiles with 19-bit.
The bitfield packing differs, causing all RETypeDefinition field
reads to return garbage on those games.

- Add RETypeDefDispatch.hpp: defines tdb_bits18::RETypeDefVersion69
  with hardcoded 18-bit field widths, and TDEF_FIELD/TDEF_FIELD_SET
  macros for runtime dispatch based on GameIdentity::tdb_ver()
- Replace ~33 bitfield accesses in RETypeDefinition.cpp with
  TDEF_FIELD macro (impl_index, declaring_typeid, parent_typeid,
  generics, index, object_type, member_method, member_field,
  member_prop, num_member_prop, type, managed_vt)
- Non-bitfield fields at same offset (type_flags, size, fqn_hash,
  type_crc) left as direct access

Note: REField and REMethodDefinition also use TYPE_INDEX_BITS
bitfields and will need similar treatment for full functionality.
…ld dispatch

- RETypeDB: All pointer field accesses (types, methods, fields, etc.) now dispatch
  through get_*_ptr() helpers that cast to tdb70::TDB for games with TDB < 73
- RETypeDB: Count accessors (get_num_types, etc.) dispatch similarly
- REMethodDefinition: get_method() uses byte-offset arithmetic with correct stride
  (16 bytes for tdb69, 12 bytes for tdb84)
- MethodIterator: Refactored to index-based iteration to handle variable stride
- REMethodDefinition: get_function() returns direct function pointer for tdb69
- REMethodDefinition: declaring_typeid, impl_id accessed via TMETH_FIELD dispatch
- REMethodDefinition: get_param_index() dispatches params vs params_lo/params_hi
- REField: declaring_typeid, impl_id accessed via TFIELD_FIELD dispatch
- REField: get_type() casts to tdb69::REFieldImpl for field_typeid
- REField: get_init_data_index() casts to tdb69::REFieldImpl for init_data split
- REField: get_offset_from_fieldptr() reads from tdb69 REField69::offset
- ObjectExplorer: All direct tdb-> accesses replaced with dispatched accessors
- RETypeDefDispatch.hpp: Added REMethodDef69, REField69, TMETH_FIELD, TFIELD_FIELD
…ect, Renderer, REContext

- RETypeDefDispatch.hpp: Added REParamDef69 (18-bit type_id) and TPARAM_FIELD macro
- RETypeDB.cpp: All p.type_id accesses use TPARAM_FIELD dispatch for 18 vs 19 bit
- RETypeDB.cpp: REField::get_data_raw vtable optimization gated to tdb_ver >= 81
- RETypes.cpp: game_namespace() returns correct prefix per game at runtime
- REManagedObject.cpp: is_managed_object/get_type/get_vm_type dispatch tdb_ver >= 71
- Renderer.hpp: Struct offsets (d3d12 resource, scene layers, output state) runtime dispatch
- REContext.cpp: Static table fixup gated to tdb_ver >= 71
…matrix dispatch

- Application.hpp: Function struct gets accessor methods (get_description, get_priority,
  get_type_val) that read from correct offsets (TDB < 74: +8 byte shift from extra void*)
- Application.cpp: get_function_stride() returns 0xD0 for TDB < 74, 0xC8 for TDB >= 74
- Application.cpp: All Function array iteration uses get_function_at() stride-aware indexing
- Application.cpp: All field accesses use accessor methods
- Hooks.cpp: entry->description -> entry->get_description()
- VR.cpp: func->description -> func->get_description()
- RETransform.hpp: Joint matrix access dispatches between RE2/RE3 (jointMatrices at 0xC0)
  and RE8+ (joints.matrices at 0xE0) via get_joint_matrices() helper
- RETypeDefinition.cpp: get_fieldptr_offset() uses direct managed_vt for TDB < 81
- RETypeDefinition.cpp: has_fieldptr_offset() checks TDEF_FIELD(managed_vt) for TDB < 81
- RETypeDefinition.cpp: get_managed_vt() skips abstract-type parent walk for TDB < 81
Runtime dispatch conversions:
- ResourceManager.cpp: TDB_VER < 81/73 byte-pattern scans for create_userdata
- Hooks.cpp: TDB_VER < 74 hook_update_before_lock_scene, >= 73 PrimitiveSystem guard
- Graphics.cpp: regenny header selection, TDB_VER < 73 backbuffer write
- Renderer.hpp: DRAW/UPDATE_VTABLE_INDEX, NUM_PRIORITY_OFFSETS as runtime fns,
  DirectXResource offset, TargetState::Desc pad, RenderLayer layout, RE4 lod_bias
- Renderer.cpp: NUM_PRIORITY_OFFSETS loop
- RETypes.cpp: >= 73 type-list finder
- VR.hpp: m_allow_engine_overlays, m_enable_asynchronous_rendering defaults
- Graphics.hpp: m_ultrawide_custom_fov default
- RETypeDB.cpp: REModule get_methods/instantiated/member_references via tdb74 cast
- RETypeDB.hpp: REModule stride-aware get_module_at(), get_module_stride()

Bug fixes:
- REModule array indexing stride mismatch: tdb81 is 0x40 bytes, tdb74 is 0x58.
  get_module() now uses stride-aware access for universal builds.

Cleanup:
- Application.cpp: removed temporary debug logging from get()
Window and SceneView structs have different field offsets per game
(RE2/RE3/RE8/RE4 etc. all differ). The universal build previously
always used RE9 headers, giving wrong offsets for other games.

New ViaDispatch.hpp provides runtime accessor functions that dispatch
by GameID. Graphics.cpp and VR.cpp updated to use these accessors
under REFRAMEWORK_UNIVERSAL.
…lity

Instead of hardcoded offset tables, each game's regenny Window/SceneView
headers are included inside distinct outer namespaces (ns_re7, ns_re3,
ns_re2, etc.). Dispatch functions cast to the correct namespaced struct
type at runtime. When Regenny regenerates a header, offsets update
automatically through the struct layout — no manual table maintenance.

re9 headers use the globally-included versions (already present via
Graphics.cpp/VR.cpp) to avoid #pragma once conflicts.
Two bugs causing RE4/SF6/MHRISE/DD2 to fail (Renderer type not found):

1. needs_18bit() threshold was tdb_ver < 73, but TDB 71+ (RE4/SF6/MHRISE/DD2)
   uses 19-bit TYPE_INDEX_BITS. Changed to tdb_ver < 71.

2. get_type(index) used sizeof(RETypeDefVersion84) = 0x50 as stride, but
   TDB 71-73 games have sizeof(RETypeDefVersion71) = 0x48 (no
   unk_new_tdb74_uint64 field). Added get_typedef_stride() and stride-
   aware access in get_type().

Stride map:
  TDB 69-70 (RE2/RE3/RE7/RE8): 0x50 (18-bit bitfields, V69 layout)
  TDB 71-73 (RE4/SF6/MHRISE/DD2): 0x48 (19-bit, no extra uint64)
  TDB 74+ (MHWILDS/RE9/PRAGMATA): 0x50 (19-bit, has unk_new_tdb74_uint64)
…r strides

Architecture change: every TDB version struct now has hardcoded bit
widths (no dependency on TYPE_INDEX_BITS / FIELD_BITS macros). This
makes each struct self-contained and correct regardless of compile-time
macro values.

Changes:
- V84/V83/V82/V74: hardcoded 19-bit TYPE_INDEX, 20-bit FIELD
- V71: hardcoded 19-bit TYPE_INDEX, 19-bit FIELD (no unk_new_tdb74_uint64)
- V69: hardcoded 18-bit (was in tdb_bits18 namespace, now the main struct)
- V67: split into V67 (DMC5, bitfield variant) and V67_RE3 (plain uint32)
- V66/V49: hardcoded 16-bit

Dispatch:
- TDEF_FIELD: 3-way dispatch (V69 for <71, V71 for 71-73, V84 for 74+)
- get_typedef_stride(): sizeof(RETypeDefVersion*) instead of 0x48/0x50
- get_method_stride(): sizeof(tdb69/tdb84::REMethodDefinition)
- tdb_bits18 namespace reduced to backward-compat aliases
…nitions

Every tdb namespace struct now has hardcoded bit widths:
- tdb84 through tdb71: 19-bit TYPE_INDEX (was macro, same value)
- tdb69: 18-bit (BUG FIX — was evaluating to 19 under universal build)
- tdb67: 17-bit (BUG FIX — was evaluating to 19 under universal build)
- tdb66: 16-bit (BUG FIX — was evaluating to 19 under universal build)

The tdb69/67/66 fixes are correctness bugs: under REFRAMEWORK_UNIVERSAL
TYPE_INDEX_BITS=19, so REField/GenericListData/REMethodDefinition structs
in those namespaces had wrong bitfield widths. This caused silent
corruption when reading older-format TDB data via dispatch macros.
TDB header accessors (get_num_types, get_num_methods, get_types_ptr,
get_modules_ptr, etc.) now switch on tdb_ver() with explicit casts to
tdb70/tdb71/tdb73/tdb74/tdb81/tdb82/tdb83/tdb84 TDB structs. Replaces
the old needs_18bit()-only two-branch dispatch that was wrong for
TDB 71-73 (e.g. RE4 reading 'initialized' instead of numTypes).

RETypeImpl field accesses (num_member_methods, num_member_fields) in
RETypeDefinition.cpp now use TIMPL_DISPATCH which dispatches to the
correct tdb69/tdb71/.../tdb84 RETypeImpl struct. Fixes the RE2 bug
where offset 0x18 in tdb69 data read interface_id (0xFFFF=65535)
instead of num_member_methods at offset 0x12.
tdb69::TDB has no 'void* unk' field between initData and attributes2,
so stringPool/bytePool/internStrings are at different offsets than tdb70.
Casting RE8's TDB to tdb70::TDB read garbage stringPool pointer causing
infinite loop during initialization.
Root cause: find_type() set map_populated=true BEFORE iterating
types. On TDB 67, get_full_name() throws for ~66% of type indices
(corrupt/unresolvable names in pre-impl TDB layout). The first
exception propagated out, leaving map_populated=true with an empty
map. All subsequent find_type() calls returned nullptr immediately.

Fix:
- Move map_populated=true AFTER the iteration loop completes
- Wrap individual type name resolution in try/catch so corrupt
  indices are skipped without aborting the entire map build
- ~21K of 62K types populate successfully, including all critical
  types (via.Application, via.io.file, etc.)

Verified: DMC5 now fully initializes (hooks, D3D11, config save).
RE2 regression test passes.
…onversion

- Add tmeth_declaring_typeid() / tfield_declaring_typeid() inline functions
  for 3-tier dispatch: tdb67 (17-bit) / tdb69 (18-bit) / tdb84 (19-bit)
- Replace TMETH_FIELD/TFIELD_FIELD(this, declaring_typeid) with new functions
  in RETypeDB.cpp and ObjectExplorer.cpp
- Convert all #ifndef REFRAMEWORK_UNIVERSAL guards in ObjectExplorer.cpp to
  runtime tdb67:: casts for method params, fields, properties
- Fixes ~66% type name resolution failures on DMC5 (TDB 67) caused by reading
  declaring_typeid with wrong bit width
RE3 has an extra 8-byte pad after cameraControllerInfos (0x58) in
RopewayCameraSystem, shifting all subsequent fields +8 vs RE2/RE8:
  cameraController: 0x98 (RE8) vs 0xA0 (RE3)
  mainCamera:       0xC0 (RE8) vs 0xC8 (RE3)
  playerJoint:      0xE0 (RE8) vs 0xE8 (RE3)
  mainCameraController: 0xE8 (RE8) vs 0xF0 (RE3)

Added CameraSystemDispatch.hpp with CAMSYS() macro that dispatches
to the correct struct layout at runtime via GameIdentity::is_re3().
Replaced all 58 shifted field accesses in FirstPerson.cpp.

Fixes access violation (c0000005) in FirstPerson::update_pointers_from_camera_system
that occurred ~6s after RE3 startup.
…troller + RETransform joints

RE2 RopewayPlayerCameraController has fields shifted -0x10 vs RE8/RE3:
  activeCamera: 0xA8 (RE2) vs 0xB8 (RE8) — confirmed via TDB reflection
  cameraParam:  0xB8 (RE2) vs 0xC8 (RE8)
  pitch/yaw:    0x108/0x10C vs 0x118/0x11C

Added CAMCTRL() dispatch macro and sdk::re2::RopewayPlayerCameraController_RE2
struct in CameraSystemDispatch.hpp. Replaced 18 field accesses in FirstPerson.cpp.

RE2 RETransform has REJointArray at 0xD0 (vs 0xD8 in RE8). The get_joint()
and get_all_children() functions read transform.joints.data at the RE8 offset,
causing joint lookups to fail on RE2 — head bone never found, never zeroed.

Added get_joint_array_data() dispatcher in RETransform.hpp and rewrote
get_joint()/get_all_children() to use it for REFRAMEWORK_UNIVERSAL builds.
RE3's RopewayMainCameraController has extra cameraDampingCameraPosition field
at 0x90, shifting all subsequent fields +0x10 vs RE2/RE8:
  cameraRotation:       0x90 (RE8) vs 0xA0 (RE3)
  mainCamera:           0xD0 (RE8) vs 0xE0 (RE3)
  switchInterpolationTime: 0xB0 (RE8) vs 0xC0 (RE3)

Added MAINCAM() dispatch macro and RopewayMainCameraController_RE3 struct
in CameraSystemDispatch.hpp. Replaced 8 field accesses in FirstPerson.cpp.
@praydog praydog added enhancement New feature or request experimental wip labels Mar 7, 2026
PRAGMATA_SKETCHBOOK.exe uses TDB 83 (confirmed from runtime TDB header).
GameIdentity hardcoded 84 (matching the unreleased full game), causing
dispatch macros to hit the tdb84 default path instead of case 83.

TODO: Read TDB version from binary at runtime and override the hardcoded
value, making the universal build resilient to demo/trial TDB mismatches.

asdf
…size at 0xa0)

MH Stories 3 (TDB 82) uses the DD2 Window struct layout where
borderless_size is at offset 0xa0, not the RE9 layout (0xa8).
Confirmed via live memory probing. ViaDispatch.hpp was sending
MHSTORIES3 to default->RE9, reading borderless_size 8 bytes late.

Added GameID::MHSTORIES3 fallthrough to DD2 cases in VIA_WIN_FIELD,
VIA_WIN_BORDERLESS, and sv_window dispatchers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant