Skip to content

Instantly share code, notes, and snippets.

@emoose
Last active July 9, 2022 15:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emoose/9a374004d80af699df8e91ff875a454b to your computer and use it in GitHub Desktop.
Save emoose/9a374004d80af699df8e91ff875a454b to your computer and use it in GitHub Desktop.
Improved Lighting Shaders Plugin for FNV (https://www.nexusmods.com/newvegas/mods/69833)
// Improved Lighting Shaders for FNV, 1.5beta4, by emoose (stoker25)
// Released under MIT License
#include "nvse/PluginAPI.h"
#include "nvse/SafeWrite.h"
#include "nvse/GameObjects.h"
#include "nvse/ParamInfos.h"
#include "NiTypes.h"
#include <string>
// TODO:
// - do pip-light priority stuff inside RenderList_Add_Hook instead? (pip-light will always go through this func)
// - support for FO3, OB?
// - port SM2 shaders to SM3, allowing 200+ lights at once? (seems possible!)
// Number of lights used in the shader
// 13 is maximum we can use in SM2
#define TRUE_MAX_LIGHTS 13
// This string will replace the shaderpackage path the game uses
// Allowing vanilla game + ILS-enabled game to use seperate shader sets
const char* g_shaderPackagePath = "Data\\Shaders\\ILS\\shaderpackage%03i.sdp";
const char* g_iniPath = "Data\\NVSE\\Plugins\\ImprovedLightingShaders.ini";
const char* g_iniApp = "ImprovedLightingShaders";
// Configurable INI settings:
int g_iMaxLightCount = 8;
bool g_bPrioritisePipLight = true;
bool g_bIncreaseLightBuffers = false;
// Replacement buffers:
NiVector4 g_LightData[32]; // g_LightPos...
volatile NiVector4 g_LightSpacer[32]; // in case something overflows
NiVector4 g_LightRadius[32];
volatile NiVector4 g_LightSpacer2[32]; // in case something overflows
NiVector4 g_AltLightData[32];
volatile NiVector4 g_LightSpacer3[32]; // in case something overflows
// g_LightBacking =
// NiVector4[32] g_LightBacking unknown backing data
// NiVector4[1] g_LightColorAmb ambient light color
// NiVector4[32] g_LightColor light colors
NiVector4 g_LightBacking[32 + 1 + 32];
extern "C" {
bool NVSEPlugin_Query(const NVSEInterface * nvse, PluginInfo * info)
{
// fill out the info structure
info->infoVersion = PluginInfo::kInfoVersion;
info->name = "Improved Lighting Shaders for FNV";
info->version = 8;
// version checks
if (nvse->nvseVersion < NVSE_VERSION_INTEGER)
{
_ERROR("NVSE version too old (got %08X expected at least %08X)", nvse->nvseVersion, NVSE_VERSION_INTEGER);
return false;
}
if (nvse->isEditor)
return false; // game only
if (nvse->runtimeVersion < RUNTIME_VERSION_1_4_0_525)
{
_ERROR("incorrect runtime version (got %08X need at least %08X)", nvse->runtimeVersion, RUNTIME_VERSION_1_4_0_525);
return false;
}
#ifdef NOGORE
if (!nvse->isNogore)
{
_ERROR("incorrect runtime edition (got %08X need %08X (nogore))", nvse->isNogore, 1);
return false;
}
#else
if (nvse->isNogore)
{
_ERROR("incorrect runtime edition (got %08X need %08X (standard))", nvse->isNogore, 0);
return false;
}
#endif
// version checks pass
return true;
}
typedef void(__cdecl* fn_BA8C50)(
BSShaderProperty::RenderPass* a1,
BYTE numLights,
void* light0,
void* light1, void* light2, void* light3,
void* light4, void* light5, void* light6,
void* light7, void* light8, void* light9,
void* light10, void* light11, void* light12
);
fn_BA8C50 sub_BA8C50 = (fn_BA8C50)0xBA8C50;
typedef void* (__thiscall* fn_GameHeapAllocate)(DWORD a1, DWORD a2);
fn_GameHeapAllocate GameHeapAllocate = (fn_GameHeapAllocate)0xAA3E40;
typedef void* (__cdecl* fn_BA8EC0)(
void* a1,
void* triStrips, WORD renderPassNum,
BYTE enable, BYTE numLights,
void* light0,
void* light1, void* light2, void* light3,
void* light4, void* light5, void* light6,
void* light7, void* light8, void* light9,
void* light10, void* light11, void* light12
);
fn_BA8EC0 sub_BA8EC0 = (fn_BA8EC0)0xBA8EC0;
typedef void(__thiscall* fn_sub_BA8D30)(BSShaderProperty::RenderPassList*, unsigned int);
fn_sub_BA8D30 sub_BA8D30 = (fn_sub_BA8D30)0xBA8D30;
typedef DWORD(__thiscall* fn_sub_BA3AA0)(BSShaderProperty::RenderPassList*, unsigned int, void**);
fn_sub_BA3AA0 sub_BA3AA0 = (fn_sub_BA3AA0)0xBA3AA0;
// Custom version of 0xBA9EE0 which allows adding more than 4 lights
DWORD __fastcall RenderList_Add_Custom(
BSShaderProperty::RenderPassList* _this, DWORD unused_edx,
void* triStrips, WORD renderPassNum,
BYTE enable, BYTE numLights,
void* light0, // base/ambient light?
void* light1, void* light2, void* light3,
void* light4, void* light5, void* light6,
void* light7, void* light8, void* light9,
void* light10, void* light11, void* light12
)
{
if (_this->iNumPasses < _this->m_usESize)
{
auto* v18 = _this->m_pBase[_this->iNumPasses];
sub_BA8C50(v18, numLights, light0,
light1, light2, light3,
light4, light5, light6,
light7, light8, light9,
light10, light11, light12
);
v18->iPassNum = renderPassNum;
v18->bEnabled = enable;
v18->spTriStrips = triStrips;
v18->unk8 = 0;
v18->unk6 = 0;
v18->unkB = 0;
_this->iNumPasses++;
return 0;
}
void* v11 = GameHeapAllocate(0x011F6238, 0x10u);
void* v14 = 0;
if (v11)
{
v14 = sub_BA8EC0(v11, triStrips, renderPassNum, enable, numLights, light0,
light1, light2, light3,
light4, light5, light6,
light7, light8, light9,
light10, light11, light12
);
}
if (_this->m_usSize >= _this->m_usMaxSize)
sub_BA8D30(_this, _this->m_usSize + _this->m_usGrowBy);
auto res = sub_BA3AA0(_this, _this->m_usSize, &v14);
_this->iNumPasses++;
return res;
}
typedef ShadowSceneLight* (__fastcall* fn_LightList_Iterate)(BSShaderProperty*, int, DListNode<ShadowSceneLight*>**);
fn_LightList_Iterate LightList_Begin = (fn_LightList_Iterate)0xB70600;
fn_LightList_Iterate LightList_Next = (fn_LightList_Iterate)0xB70700;
DWORD __fastcall RenderList_Add_Hook(
BSShaderProperty* _this, DWORD unused_edx,
void* triStrips, DWORD renderPassNum,
BYTE enable, BYTE numLights,
void* light0,
ShadowSceneLight* oldlight1, ShadowSceneLight* oldlight2, ShadowSceneLight* oldlight3
)
{
ShadowSceneLight* lights[TRUE_MAX_LIGHTS - 1] = {
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr
};
DListNode<ShadowSceneLight*>* cur_node;
// Game filters lights based on this for some reason
NiPoint3* unknown_purpose = (NiPoint3*)0x11F4998;
ShadowSceneLight* next_light = LightList_Begin(_this, 0, &cur_node);
for (int i = 0; i < g_iMaxLightCount - 1; i++)
{
lights[i] = next_light;
for (; lights[i]; lights[i] = LightList_Next(_this, 0, &cur_node))
{
auto light = lights[i]->spBaseLight;
if (unknown_purpose->x != light->m_kDiff.r)
break;
if (unknown_purpose->y != light->m_kDiff.g)
break;
if (unknown_purpose->z != light->m_kDiff.b)
break;
}
next_light = LightList_Next(_this, 0, &cur_node);
}
// Add the lights to the render pass
// The light count passed to this should equal the number of lights used in the shader
// That way any unset lights will be cleared and the shader won't try using any invalid data
return RenderList_Add_Custom(_this->pRenderPassList, 0, triStrips, renderPassNum, enable, TRUE_MAX_LIGHTS, light0,
lights[0], lights[1], lights[2],
lights[3], lights[4], lights[5],
lights[6], lights[7], lights[8],
lights[9], lights[10], lights[11]
);
}
void __fastcall AddLightingConstants_Hook(BYTE* _this, DWORD unused)
{
typedef void(__thiscall* fn_eax_8)(BYTE*, const char*, signed int, DWORD, signed int, signed int, const CHAR*, signed int, signed int, float*, DWORD);
//auto vftable = *(DWORD*)_this;
//auto eax_8 = (fn_eax_8)(*(DWORD*)(vftable + 8));
auto eax_8 = (fn_eax_8)unused; // edx was set to the fn we need to call, noice
// the code we patched over: (adds c22 register to use data from 0x11FD9E8?)
if (g_iMaxLightCount > 3)
eax_8(_this, "Light Position3", 0x10000007, 0, 22, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[4].x, 0);
// new adds (makes c23 thru c28 registers point to the extra lights)
if (g_iMaxLightCount > 4)
eax_8(_this, "Light Position4", 0x10000007, 0, 23, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[5].x, 0);
if (g_iMaxLightCount > 5)
eax_8(_this, "Light Position5", 0x10000007, 0, 24, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[6].x, 0);
if (g_iMaxLightCount > 6)
eax_8(_this, "Light Position6", 0x10000007, 0, 25, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[7].x, 0);
if (g_iMaxLightCount > 7)
eax_8(_this, "Light Position7", 0x10000007, 0, 26, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[8].x, 0);
if (g_iMaxLightCount > 8)
eax_8(_this, "Light Position8", 0x10000007, 0, 27, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[9].x, 0);
if (g_iMaxLightCount > 9)
eax_8(_this, "Light Position9", 0x10000007, 0, 28, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[10].x, 0);
if (g_iMaxLightCount > 10)
eax_8(_this, "Light Position10", 0x10000007, 0, 29, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[11].x, 0);
if (g_iMaxLightCount > 11)
eax_8(_this, "Light Position11", 0x10000007, 0, 30, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[12].x, 0);
if (g_iMaxLightCount > 12)
eax_8(_this, "Light Position12", 0x10000007, 0, 31, 1, (const CHAR*)0x1011584, 0x10, 4, &g_LightData[13].x, 0);
}
#ifdef PRINT_LIGHT_LIST
LightingDataNode* prev_head = nullptr;
int prev_list_count = 0;
#endif
// store replacement here instead of stack, seems more stable(?)
// IteratesLights doesn't seem to be multithreaded so should be safe
LightingDataNode replacement_node;
typedef BYTE* (__fastcall* IteratesLights_fn)(BSShaderPPLightingProperty* _this, DWORD unused, int a2, int a3, DWORD a4, char a5, BYTE* a6, int a7, char a8);
IteratesLights_fn IteratesLights = (IteratesLights_fn)0xBDF3E0;
BYTE* __fastcall IteratesLights_hook(BSShaderPPLightingProperty* _this, DWORD unused, int a2, int a3, DWORD a4, char a5, BYTE* a6, int a7, char a8)
{
LightingDataList* list = &_this->lights;
auto* list_head = list->first;
auto* list_tail = list->last;
// return early if there's nothing to do (3 lights or less = no need to change anything)
if (!list_head || list->count <= g_iMaxLightCount)
return IteratesLights(_this, unused, a2, a3, a4, a5, a6, a7, a8);
#ifdef PRINT_LIGHT_LIST
auto light_list_count = list->count;
if (prev_head != list_head || prev_list_count != light_list_count)
{
prev_head = list_head;
prev_list_count = light_list_count;
int i = 0;
LightListNode* light_prev = 0;
while (list_head)
{
bool pip_light = false;
auto* light_obj = list_head->data->object;
char* light_name = 0;
if (light_obj)
{
light_name = light_obj->name;
pip_light = list_head->data->is_pip_light();
}
float distance = 0;
if (light_prev)
distance = list_head->data->distance(light_prev->data);
if (light_name)
Console_Print("%s%d 0x%p (%s) dist %f", pip_light ? "> " : " ", i, list_head->data, light_name, distance);
else
Console_Print("%s%d 0x%p dist %f", pip_light ? "> " : " ", i, list_head->data, distance);
i++;
light_prev = list_head;
list_head = list_head->next;
}
}
list_head = list->first;
#endif
// Search for pip-light inside lights list
// (iterate backwards since pip-light is usually near end)
// If found, 'move' it to the beginning of the list by unlinking it, and linking in new node (entry_replacement) as the start of the list
LightingDataNode* entry_cur = list_tail;
LightingDataNode* entry_prev = 0;
bool has_pip_light = false;
while (entry_cur)
{
has_pip_light = false;
entry_prev = entry_cur->prev;
// check if there's a prev entry before this
// (we only want to move the light if it's not already at the front)
if (entry_prev)
{
has_pip_light = entry_cur->data->is_pip_light();
if (has_pip_light)
{
// unlink pip-light from light_list
entry_prev->next = entry_cur->next;
if (entry_cur->next)
entry_cur->next->prev = entry_prev;
// entry_replacement setup
// (this used to be outside the loop, based on has_pip_light, but moved it here to try reducing number of jumps)
{
replacement_node.prev = 0;
// copy pip-light data pointer to entry_replacement
replacement_node.data = entry_cur->data;
// Link entry_replacement to first entry in light_list
replacement_node.next = list_head;
list_head->prev = &replacement_node;
// Update linked-list pointer to our replacement list entry
list->first = &replacement_node;
// if pip-light was the last node in the list, update list-end ptr to the node before it
if (list_tail == entry_cur)
list->last = entry_prev;
}
break;
}
}
entry_cur = entry_prev;
}
// Call OG function
BYTE* retval = IteratesLights(_this, unused, a2, a3, a4, a5, a6, a7, a8);
// Revert our changes before returning
if (has_pip_light)
{
// Relink pip-light with light-list entries
entry_prev->next = entry_cur;
if (entry_cur->next)
entry_cur->next->prev = entry_cur;
// Set linked-list pointers back to normal
replacement_node.next->prev = 0;
list->first = replacement_node.next;
list->last = list_tail;
}
return retval;
}
// Wrapper around IteratesLights_hook that'll print a msg to console the first time it's called, to let user know that it's active
// Re-hooks target after running to point to main hook, so we can skip pointless checks every time this is ran (per-frame?)
BYTE* __fastcall IteratesLights_hook_msg(BSShaderPPLightingProperty* _this, DWORD unused, int a2, int a3, DWORD a4, char a5, BYTE* a6, int a7, char a8)
{
static bool hookedIndicator = false;
if (!hookedIndicator)
{
Console_Print("[ImprovedLightingShaders-v1.5beta4] hooked successfully");
//iMaxLightCount = % d, bPrioritisePipLight = % d, bIncreaseLightBuffers = % d", g_iMaxLightCount, g_bPrioritisePipLight ? 1 : 0, g_bIncreaseLightBuffers ? 1 : 0);
hookedIndicator = true;
}
// Re-hook target to use main hook code and skip the message check above
if (g_bPrioritisePipLight)
WriteRelCall(0xBE0680, (UInt32)IteratesLights_hook);
else
WriteRelCall(0xBE0680, (UInt32)IteratesLights); // unhook call..
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBE0680, 5);
if(g_bPrioritisePipLight)
return IteratesLights_hook(_this, unused, a2, a3, a4, a5, a6, a7, a8);
return IteratesLights(_this, unused, a2, a3, a4, a5, a6, a7, a8);
}
void* __cdecl UpdateShaderConstant_LightRadius_Hook(int constIdx, float x, float y, float z, float w)
{
int idx = constIdx - 19; // caller added 19 to light idx
g_LightRadius[idx].x = x;
g_LightRadius[idx].y = y;
g_LightRadius[idx].z = z;
g_LightRadius[idx].w = w;
return &g_LightRadius[idx];
}
void* __cdecl UpdateShaderConstant_AltLightData_Hook(int constIdx, float x, float y, float z, float w)
{
int idx = constIdx - 11; // caller added 11 to light idx
g_AltLightData[idx].x = x;
g_AltLightData[idx].y = y;
g_AltLightData[idx].z = z;
g_AltLightData[idx].w = w;
return &g_AltLightData[idx];
}
void ClearLightData()
{
for (int i = 0; i < TRUE_MAX_LIGHTS; i++)
{
g_LightData[i].x = g_LightData[i].y = g_LightData[i].z = g_LightData[i].w = 0.0f;
}
}
void ClearLightData_SetWto1()
{
for (int i = 0; i < TRUE_MAX_LIGHTS; i++)
{
g_LightData[i].x = g_LightData[i].y = g_LightData[i].z = 0.0f;
g_LightData[i].w = 1.0f;
}
}
// Clears lights? Unsure if this is ever used...
void __cdecl Hook_B704C0(DWORD lightIdx)
{
// These two only get used in this func?
DWORD* dword_11FA578 = (DWORD*)0x11FA578;
NiVector4* vect_11FA568 = (NiVector4*)0x11FA568;
if (!(*dword_11FA578 & 1))
{
*dword_11FA578 |= 1u;
vect_11FA568->x = 0.001;
vect_11FA568->y = 0.0;
vect_11FA568->z = 0.0;
vect_11FA568->w = 0.0;
}
if (lightIdx >= TRUE_MAX_LIGHTS)
return;
// this sets g_LightBacking[0] + lightIdx, DWORDs? (might be vectors...)
memset(&g_LightBacking[lightIdx], 0, 4 * (TRUE_MAX_LIGHTS - lightIdx));
// g_LightColor starts at g_LightBacking[33]
NiVector4* light = &g_LightBacking[33 + lightIdx];
do
{
NiVector4* colorBlack = (NiVector4*)0x11A9BD0;
light->x = colorBlack->x;
light->y = colorBlack->y;
light->z = colorBlack->z;
light->w = colorBlack->w;
g_LightRadius[lightIdx].x = vect_11FA568->x;
g_LightRadius[lightIdx].y = vect_11FA568->y;
g_LightRadius[lightIdx].z = vect_11FA568->z;
g_LightRadius[lightIdx].w = vect_11FA568->w;
++light;
} while (light < &g_LightBacking[33 + TRUE_MAX_LIGHTS]);
}
BOOL FileExists(LPCTSTR szPath, bool dir)
{
DWORD dwAttrib = GetFileAttributes(szPath);
if (dir)
return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
else
return dwAttrib != INVALID_FILE_ATTRIBUTES;
}
void SetMaxLightCount(int count)
{
// minimum 4 lights
if (count < 4)
count = 4;
// max 10 lights if g_bIncreaseLightBuffers = false, otherwise max is 13
if (!g_bIncreaseLightBuffers && count > 10)
count = 10;
if (g_bIncreaseLightBuffers && count > TRUE_MAX_LIGHTS)
count = TRUE_MAX_LIGHTS;
if (g_iMaxLightCount == count)
return;
g_iMaxLightCount = count;
// Patch light coloring(?) func to handle more lights
SafeWrite8(0xB7082D, g_iMaxLightCount + 1);
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xB7082B, 3);
// Patch main light handling func to handle more lights
SafeWrite8(0xB78B46, g_iMaxLightCount + 1);
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xB78B44, 3);
}
bool Cmd_GetMaxLights_Execute(COMMAND_ARGS)
{
*result = g_iMaxLightCount;
Console_Print("g_iMaxLightCount -> %d", g_iMaxLightCount);
Console_Print("g_bIncreaseLightBuffers -> %d", g_bIncreaseLightBuffers);
Console_Print("g_bPrioritisePipLight -> %d", g_bPrioritisePipLight);
return true;
}
bool Cmd_SetMaxLights_Execute(COMMAND_ARGS)
{
UInt32 newCount = 0;
*result = 0;
if (ExtractArgs(EXTRACT_ARGS, &newCount))
SetMaxLightCount(newCount);
Console_Print("g_iMaxLightCount -> %d", g_iMaxLightCount);
// can't change g_bIncreaseLightBuffers & g_bPrioritisePipLight as they affect hooks
return true;
}
DEFINE_COMMAND_PLUGIN(GetMaxLights, "Gets ImprovedLightingShaders settings", 0, 0, kParams_OneInt)
DEFINE_COMMAND_PLUGIN(SetMaxLights, "Sets ImprovedLightingShaders g_iMaxLightCount", 0, 1, kParams_OneInt)
bool NVSEPlugin_Load(const NVSEInterface* nvse)
{
nvse->SetOpcodeBase(0x3900);
nvse->RegisterCommand(&kCommandInfo_GetMaxLights);
nvse->RegisterCommand(&kCommandInfo_SetMaxLights);
g_bIncreaseLightBuffers = GetPrivateProfileIntA(g_iniApp, "bIncreaseLightBuffers", 0, g_iniPath) == 1;
int maxLightCount = GetPrivateProfileIntA(g_iniApp, "iMaxLightCount", 8, g_iniPath);
// Set g_iMaxLightCount from INI
// (SetMaxLightCount patches 2 spots in the EXE with the light count!)
g_iMaxLightCount = 0;
SetMaxLightCount(maxLightCount);
// Patch offset game uses to build ShaderPackage path, so we can redirect it where we like
// This way vanilla shaders can be left untouched, for GECK or other uses.
if (FileExists("Data\\Shaders\\ILS\\", true))
{
// Check that all shader packages exist
bool ilsShadersExist = true;
char temp[128];
for (int i = 2; i < 20; i++)
{
if (i == 5 || i == 8)
continue;
sprintf_s(temp, g_shaderPackagePath, i);
ilsShadersExist = FileExists(temp, false);
if (!ilsShadersExist)
break;
}
if(ilsShadersExist)
SafeWrite32(0xB7FFDA + 1, (UInt32)g_shaderPackagePath);
}
g_bPrioritisePipLight = GetPrivateProfileIntA(g_iniApp, "bPrioritisePipLight", 1, g_iniPath) == 1;
// Hook that allows adding more than 3 lights to the render pass
{
WriteRelCall(0xBDF5D0, (UInt32)RenderList_Add_Hook);
// TODO: patch that function and remove the 3 loops (since our hook does the exact same loops)
// Change asm just before jump to our hook so the class ptr gets passed along to us
// mov ecx, esi, nop
SafeWrite8(0xBDF5C7, 0x8B);
SafeWrite8(0xBDF5C8, 0xCE);
SafeWrite8(0xBDF5C9, 0x90);
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBDF5C7, 0xE);
// Skip code that tries finding lights 2 & 3, since our func will handle that
WriteRelJump(0xBDF457, 0xBDF537);
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBDF457, 0x5);
}
// Add extra shader constant registers for any additional lights:
{
WriteRelCall(0xB7F039, (UInt32)AddLightingConstants_Hook);
// Nop out the code we overwrote since AddLightingConstants_Hook will perform it for us
for (UInt32 i = 0xB7F03E; i < 0xB7F05B; i++)
SafeWrite8(i, 0x90);
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xB7F039, 0x22);
}
// Hook to move Pip-Light to the front of the light list
// TODO: should probably handle this inside RenderList_Add_Hook instead!
WriteRelCall(0xBE0680, (UInt32)IteratesLights_hook_msg);
FlushInstructionCache(GetCurrentProcess(), (LPCVOID)0xBE0680, 5);
#ifdef _DEBUG
//Wait for debugger to attach
//while (!::IsDebuggerPresent())
// ::Sleep(100); // to avoid 100% CPU load
#endif
// Update pointers for light data to use our larger buffers instead
if (g_bIncreaseLightBuffers)
{
// Update g_LightRadius xrefs, seems to fix midrange when using > 8 lights
{
WriteRelCall(0xB7094E, (UInt32)UpdateShaderConstant_LightRadius_Hook);
SafeWrite32(0xB70D9B + 2, (UInt32)&g_LightRadius[0].x);
SafeWrite32(0xB70DA9 + 2, (UInt32)&g_LightRadius[0].y);
SafeWrite32(0xB70DB3 + 2, (UInt32)&g_LightRadius[0].z);
SafeWrite32(0xB70DB9 + 2, (UInt32)&g_LightRadius[0].w);
SafeWrite32(0xB78C4F + 2, (UInt32)&g_LightRadius[0].x);
SafeWrite32(0xB78CEE + 2, (UInt32)&g_LightRadius[0].y);
SafeWrite32(0xB78D00 + 2, (UInt32)&g_LightRadius[0].z);
SafeWrite32(0xB78D3E + 2, (UInt32)&g_LightRadius[0].w);
SafeWrite32(0xB78B81 + 2, (UInt32)&g_LightRadius[0].x);
SafeWrite32(0xB78CD9 + 2, (UInt32)&g_LightRadius[0].x);
SafeWrite32(0xBB1963 + 2, (UInt32)&g_LightRadius[0].x);
SafeWrite32(0xBB1975 + 2, (UInt32)&g_LightRadius[0].y);
SafeWrite32(0xBB198A + 2, (UInt32)&g_LightRadius[0].z);
// Update LightRadius constant xref
SafeWrite32(0xBD319C + 1, (UInt32)&g_LightRadius[0].x);
// Update weird use of g_LightRadius[1]...
SafeWrite32(0xBB1A4A + 2, (UInt32)&g_LightRadius[1].x);
}
// g_AltLightData xrefs...
{
WriteRelCall(0xB70AE4, (UInt32)UpdateShaderConstant_AltLightData_Hook);
SafeWrite32(0xB78B7B + 2, (UInt32)&g_AltLightData[0].x);
SafeWrite32(0xB78B87 + 2, (UInt32)&g_AltLightData[0].y);
SafeWrite32(0xB78B8D + 2, (UInt32)&g_AltLightData[0].z);
SafeWrite32(0xB78B99 + 2, (UInt32)&g_AltLightData[0].w);
SafeWrite32(0xB78BCE + 2, (UInt32)&g_AltLightData[0].x);
SafeWrite32(0xB78BDD + 2, (UInt32)&g_AltLightData[0].y);
SafeWrite32(0xB78BE7 + 2, (UInt32)&g_AltLightData[0].z);
SafeWrite32(0xBB19FE + 2, (UInt32)&g_AltLightData[1].x);
SafeWrite32(0xBB1A0D + 2, (UInt32)&g_AltLightData[1].y);
SafeWrite32(0xBB1A1C + 2, (UInt32)&g_AltLightData[1].z);
}
// Light-data clearing hooks
{
// Edit some vftable so we can clear our lights?
SafeWrite32(0x1010118, (UInt32)ClearLightData);
// Hook data clear code so we can clear the added lights
WriteRelCall(0xB7B6C9, (UInt32)ClearLightData_SetWto1);
WriteRelJump(0xB7B6C9 + 5, 0xB7B725);
}
// Point the shader constants to new buffer
{
SafeWrite32(0xB7E610 + 1, (UInt32)&g_LightData[0]);
SafeWrite32(0xB7EF93 + 1, (UInt32)&g_LightData[0]);
SafeWrite32(0xB7EFBD + 1, (UInt32)&g_LightData[1]);
SafeWrite32(0xB7EFE4 + 1, (UInt32)&g_LightData[2]);
SafeWrite32(0xB7F011 + 1, (UInt32)&g_LightData[3]);
// Handled by AddLightingConstants_Hook
//SafeWrite32(0xB7F03B + 1, (UInt32)&g_LightData[4]);
}
// Update light xrefs
{
SafeWrite32(0xB78B93 + 2, (UInt32)&g_LightData[0].x);
SafeWrite32(0xB78B9F + 2, (UInt32)&g_LightData[0].y);
SafeWrite32(0xB78BA5 + 2, (UInt32)&g_LightData[0].z);
SafeWrite32(0xB78BAB + 2, (UInt32)&g_LightData[0].w);
SafeWrite32(0xB78CB9 + 2, (UInt32)&g_LightData[0].x);
SafeWrite32(0xB78CC3 + 2, (UInt32)&g_LightData[0].y);
SafeWrite32(0xB78CCD + 2, (UInt32)&g_LightData[0].z);
SafeWrite32(0xB78D24 + 2, (UInt32)&g_LightData[0].x);
SafeWrite32(0xB78D2E + 2, (UInt32)&g_LightData[0].y);
SafeWrite32(0xB78D38 + 2, (UInt32)&g_LightData[0].z);
SafeWrite32(0xB78D44 + 2, (UInt32)&g_LightData[0].w);
SafeWrite32(0xB7C9A5 + 2, (UInt32)&g_LightData[0].x);
SafeWrite32(0xB7C9C0 + 2, (UInt32)&g_LightData[0].y);
SafeWrite32(0xB7C9CA + 2, (UInt32)&g_LightData[0].z);
SafeWrite32(0xB7C9D4 + 2, (UInt32)&g_LightData[0].w);
SafeWrite32(0xB7CC78 + 2, (UInt32)&g_LightData[0].x);
SafeWrite32(0xB7CC93 + 2, (UInt32)&g_LightData[0].y);
SafeWrite32(0xB7CC9D + 2, (UInt32)&g_LightData[0].z);
SafeWrite32(0xB7CCA7 + 2, (UInt32)&g_LightData[0].w);
}
// Redirect g_LightBacking / 011FA070
{
// handled by hook SafeWrite32(0xB70515 + 3, (UInt32)&g_LightBacking[0]);
SafeWrite32(0xB70DCD + 3, (UInt32)&g_LightBacking[0]);
}
// g_LightColorAmb
{
// UpdateShaderConstant - besides the hooked ones above this only has 2 usages, neither seem light related, so we'll leave this as-is
//SafeWrite32(0x4E20C9 + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0xB70B69 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0xB70B7B + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0xB70B8B + 1, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0xB70B98 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0xB78AB1 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0xB78ABD + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0xB78AC9 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0xB78AD5 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B78E52 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B78E58 + 1, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B78E5D + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B78E69 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0xB794B1 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0xB7955B + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7BA76 + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7BA7B + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7BA89 + 1, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7BA9A + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BAC7 + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7BAD0 + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7BADA + 1, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7BAE8 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BB8B + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7BB95 + 1, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7BB9A + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7BBA0 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BD25 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BD2B + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BD99 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7BDAB + 1, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7BDB8 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7BDBE + 1, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BDEB + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7BDF1 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7C5D5 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7C5DF + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7C5E9 + 1, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7C5EE + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7CAAB + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7CAC1 + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7CAD3 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7CADF + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00B7CD7E + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00B7CD90 + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00B7CDA6 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00B7CDC5 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0xB7EF3C + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BACA41 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BACA4B + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00BACA51 + 1, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00BACA56 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00BB20B5 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BB20C3 + 1, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00BB20C8 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00BB20CE + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00BB282E + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BB283B + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00BB2841 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00BB2847 + 1, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00BB2DD6 + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BD30C6 + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BD9497 + 1, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00BD949C + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00BD94A2 + 2, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00BD94AC + 1, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00C030D7 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00C030DD + 1, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00C030E8 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00C030F9 + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00C03149 + 2, (UInt32)&g_LightBacking[32].x);
SafeWrite32(0x00C03153 + 1, (UInt32)&g_LightBacking[32].y);
SafeWrite32(0x00C03158 + 2, (UInt32)&g_LightBacking[32].z);
SafeWrite32(0x00C0315E + 2, (UInt32)&g_LightBacking[32].w);
SafeWrite32(0x00FB99F7 + 1, (UInt32)&g_LightBacking[32].z);
}
// g_LightColor0
{
SafeWrite32(0xB633ED + 1, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB633F7 + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB633FD + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB63403 + 1, (UInt32)&g_LightBacking[33].w);
WriteRelJump(0xB704C0, (UInt32)Hook_B704C0);
SafeWrite32(0xB7084B + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB70D31 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB70D43 + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB70D51 + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB70D57 + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0xB78D8E + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB78D94 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB78DA0 + 1, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB78DAB + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB78DB9 + 1, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0xB78DD4 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB78DE6 + 1, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB78DF5 + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB78E07 + 1, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0xB78E63 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB78E6F + 1, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB78E74 + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB78E7A + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0xB78EC2 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB78ECC + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB78ED6 + 1, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB78EDB + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0xB7C722 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0xB7C72C + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0xB7C732 + 1, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0xB7C737 + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0xB7EF66 + 1, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BB20EC + 1, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BB2101 + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BB2107 + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BB210D + 1, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BB21DA + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BB21E4 + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BB21EA + 1, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BB21EF + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BB2860 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BB2876 + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BB287C + 1, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BB2881 + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BB28A1 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BB28AB + 1, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BB28B0 + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BB28B6 + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BB2E00 + 1, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BBC153 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BBC159 + 1, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BBC164 + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BBC16A + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BBC178 + 1, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BBC199 + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BBC1AB + 2, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BBC1BB + 1, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BBC1CC + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BBC1EC + 2, (UInt32)&g_LightBacking[33].x);
SafeWrite32(0x00BBC1F8 + 1, (UInt32)&g_LightBacking[33].y);
SafeWrite32(0x00BBC1FD + 2, (UInt32)&g_LightBacking[33].z);
SafeWrite32(0x00BBC203 + 2, (UInt32)&g_LightBacking[33].w);
SafeWrite32(0x00BD30F0 + 1, (UInt32)&g_LightBacking[33].x);
}
// g_LightColor1
{
SafeWrite32(0xB7CE09 + 1, (UInt32)&g_LightBacking[34].x);
SafeWrite32(0xB7CE12 + 2, (UInt32)&g_LightBacking[34].y);
SafeWrite32(0xB7CE18 + 2, (UInt32)&g_LightBacking[34].z);
SafeWrite32(0xB7CE1E + 1, (UInt32)&g_LightBacking[34].w);
SafeWrite32(0xBB2E2A + 1, (UInt32)&g_LightBacking[34].x);
SafeWrite32(0xBD311A + 1, (UInt32)&g_LightBacking[34].x);
}
}
return true;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment