Skip to content

Commit 895ed6e

Browse files
committed
Fix Apollo Justice evidence thumbnails in VR
1 parent 7794c75 commit 895ed6e

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

src/mods/VR.cpp

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,82 @@ std::unique_ptr<FunctionHook> g_wwise_listener_update_hook{};
7979
std::optional<regenny::via::Size> g_previous_size{};
8080
#endif
8181

82+
bool is_apollo_justice_exe() {
83+
static const bool value = utility::get_module_path(utility::get_executable())->find("GS456.exe") != std::string::npos;
84+
return value;
85+
}
86+
87+
enum class ApolloEpisodeFamily {
88+
Unknown,
89+
GS4,
90+
GS5,
91+
GS6,
92+
};
93+
94+
ApolloEpisodeFamily get_apollo_visible_episode_family() {
95+
if (!is_apollo_justice_exe() || !g_framework->is_ready()) {
96+
return ApolloEpisodeFamily::Unknown;
97+
}
98+
99+
const auto gui_manager = sdk::get_managed_singleton<REManagedObject>("app.GUIManager");
100+
101+
if (gui_manager == nullptr) {
102+
return ApolloEpisodeFamily::Unknown;
103+
}
104+
105+
const auto all_gui = sdk::get_object_field<sdk::SystemArray*>(gui_manager, "_AllGUI");
106+
107+
if (all_gui == nullptr || *all_gui == nullptr) {
108+
return ApolloEpisodeFamily::Unknown;
109+
}
110+
111+
int gs4_visible{};
112+
int gs5_visible{};
113+
int gs6_visible{};
114+
115+
for (size_t i = 0; i < (*all_gui)->get_size(); ++i) {
116+
const auto gui = (REManagedObject*)(*all_gui)->get_element((int32_t)i);
117+
118+
if (gui == nullptr || !sdk::call_object_func_easy<bool>(gui, "get_IsVisible")) {
119+
continue;
120+
}
121+
122+
const auto type_definition = utility::re_managed_object::get_type_definition(gui);
123+
124+
if (type_definition == nullptr) {
125+
continue;
126+
}
127+
128+
const auto full_name = type_definition->get_full_name();
129+
130+
if (full_name.rfind("app.gs4.", 0) == 0) {
131+
++gs4_visible;
132+
} else if (full_name.rfind("app.gs5.", 0) == 0) {
133+
++gs5_visible;
134+
} else if (full_name.rfind("app.gs6.", 0) == 0) {
135+
if (full_name == "app.gs6.uThumbnailGUI") {
136+
continue;
137+
}
138+
139+
++gs6_visible;
140+
}
141+
}
142+
143+
if (gs4_visible >= gs5_visible && gs4_visible >= gs6_visible && gs4_visible > 0) {
144+
return ApolloEpisodeFamily::GS4;
145+
}
146+
147+
if (gs5_visible >= gs6_visible && gs5_visible > 0) {
148+
return ApolloEpisodeFamily::GS5;
149+
}
150+
151+
if (gs6_visible > 0) {
152+
return ApolloEpisodeFamily::GS6;
153+
}
154+
155+
return ApolloEpisodeFamily::Unknown;
156+
}
157+
82158
// Purpose: spoof the render target size to the size of the HMD displays
83159
void VR::on_view_get_size(REManagedObject* scene_view, float* result) {
84160
// There are some very dumb optimizations that cause set_DisplayType
@@ -2386,6 +2462,7 @@ struct GUIRestoreData {
23862462
thread_local std::vector<std::unique_ptr<GUIRestoreData>> g_elements_to_reset{};
23872463
thread_local std::vector<GUIRestoreData::ApolloNitroRootRestore> g_apollo_nitro_roots_to_reset{};
23882464
thread_local bool g_apollo_nitro_roots_applied{};
2465+
thread_local bool g_apollo_thumbnail_rebound_this_frame{};
23892466

23902467
static void apply_apollo_nitro_root_sync(const Matrix4x4f& gui_matrix) {
23912468
if (g_apollo_nitro_roots_applied) {
@@ -2541,6 +2618,94 @@ static void apply_apollo_cell_animation_rtt_workaround() {
25412618
}
25422619
}
25432620

2621+
static void apply_apollo_thumbnail_texture_workaround() {
2622+
static auto thumbnail_manager_t = sdk::find_type_definition("app.ThumbnailTextureManager");
2623+
static auto thumbnail_gui_t = sdk::find_type_definition("app.gs6.uThumbnailGUI");
2624+
2625+
static auto manager_thumbnail_gui_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("mpThumbnailGUI") : nullptr;
2626+
static auto evidence_gs4_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("_EvidenceTextureListGS4") : nullptr;
2627+
static auto evidence_gs5_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("_EvidenceTextureListGS5") : nullptr;
2628+
static auto evidence_gs6_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("_EvidenceTextureListGS6") : nullptr;
2629+
static auto profile_gs4_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("_ProfileTextureListGS4") : nullptr;
2630+
static auto profile_gs5_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("_ProfileTextureListGS5") : nullptr;
2631+
static auto profile_gs6_field = thumbnail_manager_t != nullptr ? thumbnail_manager_t->get_field("_ProfileTextureListGS6") : nullptr;
2632+
2633+
static auto thumbnail_texture_field = thumbnail_gui_t != nullptr ? thumbnail_gui_t->get_field("_TextureImage") : nullptr;
2634+
static auto thumbnail_type_field = thumbnail_gui_t != nullptr ? thumbnail_gui_t->get_field("mType") : nullptr;
2635+
static auto thumbnail_item_id_field = thumbnail_gui_t != nullptr ? thumbnail_gui_t->get_field("mItemID") : nullptr;
2636+
2637+
if (manager_thumbnail_gui_field == nullptr || evidence_gs4_field == nullptr || evidence_gs5_field == nullptr || evidence_gs6_field == nullptr ||
2638+
profile_gs4_field == nullptr || profile_gs5_field == nullptr || profile_gs6_field == nullptr || thumbnail_texture_field == nullptr ||
2639+
thumbnail_type_field == nullptr || thumbnail_item_id_field == nullptr) {
2640+
return;
2641+
}
2642+
2643+
const auto thumbnail_manager = sdk::get_managed_singleton<REManagedObject>("app.ThumbnailTextureManager");
2644+
2645+
if (thumbnail_manager == nullptr) {
2646+
return;
2647+
}
2648+
2649+
const auto thumbnail_gui = manager_thumbnail_gui_field->get_data<REManagedObject*>(thumbnail_manager);
2650+
2651+
if (thumbnail_gui == nullptr || !sdk::call_object_func_easy<bool>(thumbnail_gui, "get_IsVisible")) {
2652+
return;
2653+
}
2654+
2655+
const auto thumbnail_texture = thumbnail_texture_field->get_data<REManagedObject*>(thumbnail_gui);
2656+
2657+
if (thumbnail_texture == nullptr) {
2658+
return;
2659+
}
2660+
2661+
const auto episode_family = get_apollo_visible_episode_family();
2662+
2663+
if (episode_family == ApolloEpisodeFamily::Unknown) {
2664+
return;
2665+
}
2666+
2667+
const auto thumbnail_type = thumbnail_type_field->get_data<int32_t>(thumbnail_gui);
2668+
const auto thumbnail_item_id = thumbnail_item_id_field->get_data<uint32_t>(thumbnail_gui);
2669+
const auto texture_list_field = [&]() -> sdk::REField* {
2670+
const auto is_evidence_thumbnail = thumbnail_type == 0;
2671+
2672+
switch (episode_family) {
2673+
case ApolloEpisodeFamily::GS4:
2674+
return is_evidence_thumbnail ? evidence_gs4_field : profile_gs4_field;
2675+
case ApolloEpisodeFamily::GS5:
2676+
return is_evidence_thumbnail ? evidence_gs5_field : profile_gs5_field;
2677+
case ApolloEpisodeFamily::GS6:
2678+
return is_evidence_thumbnail ? evidence_gs6_field : profile_gs6_field;
2679+
default:
2680+
return nullptr;
2681+
}
2682+
}();
2683+
2684+
if (texture_list_field == nullptr) {
2685+
return;
2686+
}
2687+
2688+
const auto texture_list = texture_list_field->get_data<sdk::SystemArray*>(thumbnail_manager);
2689+
2690+
if (texture_list == nullptr || thumbnail_item_id >= texture_list->get_size()) {
2691+
return;
2692+
}
2693+
2694+
const auto target_texture_holder = (REManagedObject*)texture_list->get_element((int32_t)thumbnail_item_id);
2695+
2696+
if (target_texture_holder == nullptr) {
2697+
return;
2698+
}
2699+
2700+
const auto current_texture_holder = sdk::call_object_func_easy<REManagedObject*>(thumbnail_texture, "getTexture");
2701+
2702+
if (current_texture_holder == target_texture_holder) {
2703+
return;
2704+
}
2705+
2706+
sdk::call_object_func<void*>(thumbnail_texture, "setTexture", sdk::get_thread_context(), thumbnail_texture, target_texture_holder);
2707+
}
2708+
25442709
static void restore_pending_gui_elements() {
25452710
if (g_elements_to_reset.empty() && g_apollo_nitro_roots_to_reset.empty()) {
25462711
inside_gui_draw = false;
@@ -2602,6 +2767,11 @@ bool VR::on_pre_gui_draw_element(REComponent* gui_element, void* primitive_conte
26022767
if (game_object != nullptr && game_object_transform != nullptr) {
26032768
auto context = sdk::get_thread_context();
26042769

2770+
if (is_apollo_justice_exe() && !g_apollo_thumbnail_rebound_this_frame) {
2771+
apply_apollo_thumbnail_texture_workaround();
2772+
g_apollo_thumbnail_rebound_this_frame = true;
2773+
}
2774+
26052775
const auto name = utility::re_game_object::get_name(game_object);
26062776
const auto name_hash = utility::hash(name);
26072777

@@ -3203,7 +3373,9 @@ void VR::on_pre_begin_rendering(void* entry) {
32033373
static const auto is_apollo_justice = utility::get_module_path(utility::get_executable())->find("GS456.exe") != std::string::npos;
32043374

32053375
if (is_apollo_justice) {
3376+
g_apollo_thumbnail_rebound_this_frame = false;
32063377
apply_apollo_cell_animation_rtt_workaround();
3378+
apply_apollo_thumbnail_texture_workaround();
32073379
}
32083380

32093381
// Apollo's GS4 GUI can render additional portrait/evidence layers after the

0 commit comments

Comments
 (0)