Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Shaders/Granblue Fantasy Relink/Includes/Tonemap.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,9 @@ float3 ApplyUserGradingAndToneMap(float3 color_bt709, float3 bloom, float2 grain
true);
const float peakRatioCorrected = gammaCorrectedPeakRatio.x;

bloom = ApplyCustomBloom(color_bt709, bloom, 0.5f);
// bloom = ApplyCustomBloom(color_bt709, bloom, 0.5f);
if (BLOOM_TYPE == 0 && LumaSettings.DisplayMode != 0)
bloom = FakeHDR(bloom, 0.05, 0.33f);
// blow out and hue shift

float3 purity_and_hue_source = Reinhard::ReinhardPiecewise(color_bt709, 8.f, 0.18f);
Expand Down
39 changes: 24 additions & 15 deletions Source/Games/Granblue Fantasy Relink/includes/hooks.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
#include "..\..\Core\core.hpp"

Check failure on line 1 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:1:10 [clang-diagnostic-error]

'..\..\Core\core.hpp' file not found
#include "cbuffers.h"
#include "hooks.hpp"
#include "common.hpp"

bool TryReadCameraJitter(float2& out_jitter)

Check warning on line 6 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:6:6 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
const uintptr_t mod_base = reinterpret_cast<uintptr_t>(GetModuleHandleA(NULL));
if (mod_base == 0)
const uintptr_t camera = ResolveGBFRDataOrFallback(

Check warning on line 8 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:8:10 [misc-include-cleaner]

no header providing "uintptr_t" is directly included
g_resolved_addresses.camera_global,
kCameraGlobal_RVA);
if (camera == 0)

Check warning on line 11 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:11:20 [google-readability-braces-around-statements]

statement should be inside braces
return false;

const uintptr_t camera = mod_base + kCameraGlobal_RVA;
const uintptr_t projection_ptr = *reinterpret_cast<const uintptr_t*>(camera + kCameraProjectionDataOffset);

Check warning on line 14 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:14:38 [performance-no-int-to-ptr]

integer to pointer cast pessimizes optimization opportunities

Check warning on line 14 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:14:38 [cppcoreguidelines-pro-type-reinterpret-cast]

do not use reinterpret_cast
if (projection_ptr == 0)

Check warning on line 15 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:15:28 [google-readability-braces-around-statements]

statement should be inside braces
return false;

out_jitter.x = *reinterpret_cast<const float*>(projection_ptr + kProjectionJitterXOffset);

Check warning on line 18 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:18:20 [performance-no-int-to-ptr]

integer to pointer cast pessimizes optimization opportunities

Check warning on line 18 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:18:20 [cppcoreguidelines-pro-type-reinterpret-cast]

do not use reinterpret_cast
out_jitter.y = *reinterpret_cast<const float*>(projection_ptr + kProjectionJitterYOffset);

Check warning on line 19 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:19:20 [performance-no-int-to-ptr]

integer to pointer cast pessimizes optimization opportunities

Check warning on line 19 in Source/Games/Granblue Fantasy Relink/includes/hooks.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/hooks.cpp:19:20 [cppcoreguidelines-pro-type-reinterpret-cast]

do not use reinterpret_cast
return true;
}

Expand Down Expand Up @@ -82,17 +83,15 @@
static_assert(JITTER_PHASES >= 1 && JITTER_PHASES <= 64, "JITTER_PHASES must be between 1 and 64");

#ifndef PATCH_JITTER_TABLE_INIT
const uintptr_t base_addr = reinterpret_cast<uintptr_t>(GetModuleHandleA(NULL));
if (base_addr == 0)
return;

constexpr uint8_t mask = static_cast<uint8_t>(JITTER_PHASES - 1);
const uintptr_t patch_addrs[2] = {
base_addr + kJitterPhaseMask_CL_RVA,
base_addr + kJitterPhaseMask_EAX_RVA,
ResolveGBFRDataOrFallback(g_resolved_addresses.jitter_phase_mask_cl_imm, kJitterPhaseMask_CL_RVA),
ResolveGBFRDataOrFallback(g_resolved_addresses.jitter_phase_mask_eax_imm, kJitterPhaseMask_EAX_RVA),
};
for (uintptr_t addr : patch_addrs)
{
if (addr == 0)
continue;
auto* byte_ptr = reinterpret_cast<uint8_t*>(addr);
DWORD old_protect;
VirtualProtect(byte_ptr, 1, PAGE_EXECUTE_READWRITE, &old_protect);
Expand All @@ -109,13 +108,15 @@
static std::atomic<bool> s_last_taa_running{false};

const bool last_known = s_last_taa_running.load(std::memory_order_acquire);
const uintptr_t mod_base = reinterpret_cast<uintptr_t>(GetModuleHandleA(NULL));
if (mod_base == 0)
const uintptr_t settings_ptr_addr = ResolveGBFRDataOrFallback(
g_resolved_addresses.taa_settings_global,
kTAASettingsGlobal_RVA);
if (settings_ptr_addr == 0)
return last_known;

__try
{
const uintptr_t settings_obj = *reinterpret_cast<const uintptr_t*>(mod_base + kTAASettingsGlobal_RVA);
const uintptr_t settings_obj = *reinterpret_cast<const uintptr_t*>(settings_ptr_addr);
if (settings_obj == 0)
return last_known;

Expand Down Expand Up @@ -173,9 +174,17 @@
// CreateRenderTargets initialises these from g_outputWidth/g_outputHeight (always output
// dims) and never applies a scale, so without this write the frame graph sees
// render == output and skips the temporal upscale path every frame.
const uintptr_t mod_base = reinterpret_cast<uintptr_t>(GetModuleHandleA(NULL));
*reinterpret_cast<int*>(mod_base + kRenderWidth_RVA) = render_w;
*reinterpret_cast<int*>(mod_base + kRenderHeight_RVA) = render_h;
const uintptr_t render_w_addr = ResolveGBFRDataOrFallback(
g_resolved_addresses.render_width,
kRenderWidth_RVA);
const uintptr_t render_h_addr = ResolveGBFRDataOrFallback(
g_resolved_addresses.render_height,
kRenderHeight_RVA);
if (render_w_addr != 0 && render_h_addr != 0)
{
*reinterpret_cast<int*>(render_w_addr) = render_w;
*reinterpret_cast<int*>(render_h_addr) = render_h;
}
}

// Pass render dims to the game — g_outputWidth/g_outputHeight are not touched.
Expand Down
33 changes: 29 additions & 4 deletions Source/Games/Granblue Fantasy Relink/includes/hooks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

#include "safetyhook.hpp"

struct GBFRResolvedAddresses
{
void* initialize_dx11_rendering_pipeline = nullptr;
void* dispatch_render_pass_viewport = nullptr;
void* ui_render_orchestrator = nullptr;
void* jitter_write_site = nullptr;
#ifdef PATCH_JITTER_TABLE_INIT
void* temporal_aa_component_init = nullptr;
#endif

uintptr_t output_width = 0;
uintptr_t output_height = 0;
uintptr_t render_width = 0;
uintptr_t render_height = 0;
uintptr_t camera_global = 0;
uintptr_t taa_settings_global = 0;
uintptr_t jitter_phase_counter = 0;
uintptr_t jitter_phase_mask_cl_imm = 0;
uintptr_t jitter_phase_mask_eax_imm = 0;

bool resolve_attempted = false;
bool ready = false;
};

struct GBFRHookGlobals
{
SafetyHookInline rt_creation_hook;
Expand Down Expand Up @@ -45,10 +69,6 @@ constexpr uintptr_t kCameraProjectionDataOffset = 0x60;
constexpr uintptr_t kProjectionJitterXOffset = 0x940;
constexpr uintptr_t kProjectionJitterYOffset = 0x944;
constexpr uintptr_t kTAASettingsGlobal_RVA = 0x05E55EA0;
constexpr uintptr_t kPauseCandidate_GlobalBit_RVA = 0x061720A4;
constexpr uintptr_t kPauseCandidate_TonemapGate_RVA = 0x05E5CABD;
constexpr uintptr_t kPauseCandidate_DofGateA_RVA = 0x06130C5C;
constexpr uintptr_t kPauseCandidate_DofGateB_RVA = 0x06130E13;
constexpr uintptr_t kJitterPhaseCounter_RVA = 0x05E61790;
constexpr uintptr_t kJitterPhaseMask_CL_RVA = 0x01A9EB76;
constexpr uintptr_t kJitterPhaseMask_EAX_RVA = 0x01A9EB7C;
Expand All @@ -71,6 +91,11 @@ inline auto& g_taa_init_hook = g_hook_globals.taa_init_hook;
#endif
inline auto& g_device_data_ptr = g_hook_globals.device_data_ptr;
inline auto& g_native_device_ptr = g_hook_globals.native_device_ptr;
inline GBFRResolvedAddresses g_resolved_addresses;

bool ResolveGBFRAddresses();
uintptr_t ResolveGBFRDataOrFallback(uintptr_t resolved_absolute, uintptr_t fallback_rva);
void* ResolveGBFRCodeOrFallback(void* resolved_absolute, uintptr_t fallback_rva);

bool TryReadCameraJitter(float2& out_jitter);
void OnJitterWrite(safetyhook::Context& ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@
}
#endif

static bool CreateOrRecreateOutlineTextureIfNeeded(GameDeviceDataGBFR& game_device_data, ID3D11Device* native_device, D3D11_TEXTURE2D_DESC desc)

Check failure on line 12 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:12:119 [clang-diagnostic-error]

unknown type name 'D3D11_TEXTURE2D_DESC'

Check failure on line 12 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:12:90 [clang-diagnostic-error]

unknown type name 'ID3D11Device'

Check failure on line 12 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:12:52 [clang-diagnostic-error]

unknown type name 'GameDeviceDataGBFR'
{
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;

Check failure on line 14 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:14:50 [clang-diagnostic-error]

use of undeclared identifier 'D3D11_BIND_UNORDERED_ACCESS'

Check failure on line 14 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:14:21 [clang-diagnostic-error]

use of undeclared identifier 'D3D11_BIND_SHADER_RESOURCE'
desc.MiscFlags = 0;
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;

Check failure on line 17 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:17:17 [clang-diagnostic-error]

use of undeclared identifier 'D3D11_USAGE_DEFAULT'

if (CreateOrRecreateTextureIfNeeded(game_device_data, native_device, desc, game_device_data.outline_resource))
{
game_device_data.outline_srv = nullptr;
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};

Check failure on line 22 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:22:7 [clang-diagnostic-error]

unknown type name 'D3D11_SHADER_RESOURCE_VIEW_DESC'
srv_desc.Format = desc.Format;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

Check failure on line 24 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:24:32 [clang-diagnostic-error]

use of undeclared identifier 'D3D11_SRV_DIMENSION_TEXTURE2D'
srv_desc.Texture2D.MipLevels = 1;
srv_desc.Texture2D.MostDetailedMip = 0;
HRESULT hr = native_device->CreateShaderResourceView(game_device_data.outline_resource.get(), &srv_desc, game_device_data.outline_srv.put());

Check failure on line 27 in Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp

View workflow job for this annotation

GitHub Actions / Run linter

Source/Games/Granblue Fantasy Relink/includes/postprocess.cpp:27:7 [clang-diagnostic-error]

unknown type name 'HRESULT'
if (FAILED(hr) || game_device_data.outline_srv.get() == nullptr)
{
ASSERT_ONCE_MSG(false, "CreateOrRecreateOutlineTextureIfNeeded: failed to create SRV");
Expand Down Expand Up @@ -1079,7 +1079,7 @@
settings_data.render_width = static_cast<uint>(device_data.render_resolution.x);
settings_data.render_height = static_cast<uint>(device_data.render_resolution.y);
settings_data.dynamic_resolution = false;
settings_data.hdr = cb_luma_global_settings.DisplayMode == DisplayModeType::HDR ? true : false;
settings_data.hdr = cb_luma_global_settings.DisplayMode == DisplayModeType::HDR ? true : tonemap_after_taa;
settings_data.auto_exposure = true;
settings_data.inverted_depth = false;
// Granblue MVs are unjittered (g_ProjectionOffset cancels jitter in the PS)
Expand Down
178 changes: 178 additions & 0 deletions Source/Games/Granblue Fantasy Relink/includes/sig_helper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#pragma once

// License: MIT License

// Copyright (c) 2024 Lyall

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// Source: https://github.com/Lyall/GBFRelinkFix/blob/master/src/helper.hpp

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <string_view>
#include <vector>
#include <Windows.h>

namespace Memory
{
inline std::vector<int> PatternToByte(const char* pattern)
{
std::vector<int> bytes;
const char* current = pattern;
const char* end = pattern + std::strlen(pattern);

while (current < end)
{
if (*current == ' ')
{
++current;
continue;
}

if (*current == '?')
{
++current;
if (current < end && *current == '?')
++current;
bytes.push_back(-1);
continue;
}

char* next = nullptr;
bytes.push_back(static_cast<int>(std::strtoul(current, &next, 16)));
current = next;
}

return bytes;
}

inline bool GetSectionRange(void* module, std::string_view section_name, std::uint8_t*& begin, std::uint8_t*& end)
{
if (!module)
return false;

auto* dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(module);
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
return false;

auto* nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<std::uint8_t*>(module) + dos_header->e_lfanew);
if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
return false;

IMAGE_SECTION_HEADER* section = IMAGE_FIRST_SECTION(nt_headers);
for (unsigned i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section)
{
char name[9] = {};
std::memcpy(name, section->Name, sizeof(section->Name));
if (section_name == name)
{
begin = reinterpret_cast<std::uint8_t*>(module) + section->VirtualAddress;
end = begin + section->Misc.VirtualSize;
return true;
}
}

return false;
}

inline std::uint8_t* PatternScan(void* module, const char* signature, std::string_view section_name = ".text")
{
std::uint8_t* section_begin = nullptr;
std::uint8_t* section_end = nullptr;
if (!GetSectionRange(module, section_name, section_begin, section_end))
return nullptr;

const auto pattern_bytes = PatternToByte(signature);
if (pattern_bytes.empty())
return nullptr;

const std::size_t section_size = static_cast<std::size_t>(section_end - section_begin);
if (section_size < pattern_bytes.size())
return nullptr;

const std::size_t pattern_size = pattern_bytes.size();
for (std::size_t i = 0; i <= section_size - pattern_size; ++i)
{
bool found = true;
for (std::size_t j = 0; j < pattern_size; ++j)
{
const int want = pattern_bytes[j];
if (want != -1 && section_begin[i + j] != static_cast<std::uint8_t>(want))
{
found = false;
break;
}
}

if (found)
return &section_begin[i];
}

return nullptr;
}

inline std::uint8_t* PatternScanUnique(void* module, const char* signature, std::string_view section_name, std::size_t& out_match_count)
{
out_match_count = 0;

std::uint8_t* section_begin = nullptr;
std::uint8_t* section_end = nullptr;
if (!GetSectionRange(module, section_name, section_begin, section_end))
return nullptr;

const auto pattern_bytes = PatternToByte(signature);
if (pattern_bytes.empty())
return nullptr;

const std::size_t section_size = static_cast<std::size_t>(section_end - section_begin);
if (section_size < pattern_bytes.size())
return nullptr;

const std::size_t pattern_size = pattern_bytes.size();
std::uint8_t* first_match = nullptr;
for (std::size_t i = 0; i <= section_size - pattern_size; ++i)
{
bool found = true;
for (std::size_t j = 0; j < pattern_size; ++j)
{
const int want = pattern_bytes[j];
if (want != -1 && section_begin[i + j] != static_cast<std::uint8_t>(want))
{
found = false;
break;
}
}

if (found)
{
++out_match_count;
if (!first_match)
first_match = &section_begin[i];
}
}

return (out_match_count == 1) ? first_match : nullptr;
}

inline std::uintptr_t GetAbsolute64(std::uintptr_t address) noexcept
{
return address + 4 + static_cast<std::intptr_t>(*reinterpret_cast<std::int32_t*>(address));
}

inline std::uintptr_t GetAbsolute64(std::uintptr_t instruction_address, std::size_t disp_offset, std::size_t instruction_size) noexcept
{
const auto disp = *reinterpret_cast<std::int32_t*>(instruction_address + disp_offset);
return instruction_address + instruction_size + static_cast<std::intptr_t>(disp);
}

}
Loading
Loading