@@ -779,6 +779,36 @@ bool is_ue_5_1_dx_backend() {
779779 return disk_version.dwFileVersionMS >= 0x50001 && disk_version.dwFileVersionMS < 0x50002;
780780}
781781
782+ bool is_ue_5_2_dx_backend() {
783+ if (g_framework == nullptr || (!g_framework->is_dx12() && !g_framework->is_dx11())) {
784+ return false;
785+ }
786+
787+ static const auto disk_version = sdk::get_file_version_info();
788+ static const auto str_version = utility::narrow(sdk::search_for_version(utility::get_executable()).value_or(L"0.00"));
789+
790+ if (str_version != "0.00") {
791+ return str_version.starts_with("5.2");
792+ }
793+
794+ return disk_version.dwFileVersionMS >= 0x50002 && disk_version.dwFileVersionMS < 0x50003;
795+ }
796+
797+ bool is_ue_5_3_dx_backend() {
798+ if (g_framework == nullptr || (!g_framework->is_dx12() && !g_framework->is_dx11())) {
799+ return false;
800+ }
801+
802+ static const auto disk_version = sdk::get_file_version_info();
803+ static const auto str_version = utility::narrow(sdk::search_for_version(utility::get_executable()).value_or(L"0.00"));
804+
805+ if (str_version != "0.00") {
806+ return str_version.starts_with("5.3");
807+ }
808+
809+ return disk_version.dwFileVersionMS >= 0x50003 && disk_version.dwFileVersionMS < 0x50004;
810+ }
811+
782812bool is_ue_5_4_runtime() {
783813 static const auto disk_version = sdk::get_file_version_info();
784814 static const auto str_version = utility::narrow(sdk::search_for_version(utility::get_executable()).value_or(L"0.00"));
@@ -1843,6 +1873,28 @@ std::optional<uint32_t> resolve_post_init_properties_index_from_uobject(uintptr_
18431873 return std::nullopt;
18441874 }
18451875
1876+ // UE5.2.1/5.3.2 source plus The Complex Expedition PDB place
1877+ // UObject::PostInitProperties at slot 9 in shipped layouts. The older
1878+ // legacy body scan sees the function but cannot identify it because this
1879+ // implementation does not reference GEngine.
1880+ if (is_ue_5_2_dx_backend() || is_ue_5_3_dx_backend()) {
1881+ constexpr uint32_t UE52_53_POST_INIT_PROPERTIES_SLOT = 9;
1882+
1883+ if (validate_source_informed_post_init_slot(
1884+ object_vtable,
1885+ localplayer_vtable,
1886+ UE52_53_POST_INIT_PROPERTIES_SLOT,
1887+ is_ue_5_3_dx_backend() ? "UE5.3 UObject::PostInitProperties" : "UE5.2 UObject::PostInitProperties",
1888+ false,
1889+ true))
1890+ {
1891+ return UE52_53_POST_INIT_PROPERTIES_SLOT;
1892+ }
1893+
1894+ SPDLOG_WARN("[PostInitProperties] UE5.2/5.3 slot 9 did not validate; skipping LocalPlayer bootstrap");
1895+ return std::nullopt;
1896+ }
1897+
18461898 // UE 5.4.4, 5.5.4 and 5.6.1 source/PDB put UObject::PostInitProperties at slot 10
18471899 // for shipped game layouts:
18481900 // UObjectBase has 4 virtuals, UObjectBaseUtility has 5, then UObject adds
@@ -9269,13 +9321,15 @@ void FFakeStereoRenderingHook::post_init_properties(uintptr_t localplayer) {
92699321 }
92709322
92719323 const auto ue51_post_init = is_ue_5_1_dx_backend();
9272- const auto needs_source_informed_post_init = is_ue_5_7_or_newer() || is_ue_5_6_dx12_backend() || is_ue_5_5_dx_backend() || is_ue_5_4_dx_backend() || ue51_post_init;
9324+ const auto ue52_post_init = is_ue_5_2_dx_backend();
9325+ const auto ue53_post_init = is_ue_5_3_dx_backend();
9326+ const auto needs_source_informed_post_init = is_ue_5_7_or_newer() || is_ue_5_6_dx12_backend() || is_ue_5_5_dx_backend() || is_ue_5_4_dx_backend() || ue53_post_init || ue52_post_init || ue51_post_init;
92739327
92749328 if (needs_source_informed_post_init) {
92759329 idx = resolve_post_init_properties_index_from_uobject(localplayer);
92769330 }
92779331
9278- if (ue51_post_init && !idx) {
9332+ if (( ue51_post_init || ue52_post_init || ue53_post_init) && !idx) {
92799333 g_hook->m_sceneview_data.known_scene_states.clear();
92809334 g_hook->m_fixed_localplayer_view_count = true;
92819335 return;
0 commit comments