Skip to content

Commit

Permalink
RDR2: Better and fully portable skip main menu patch
Browse files Browse the repository at this point in the history
  • Loading branch information
illusion0001 committed Jun 14, 2024
1 parent 61c49c0 commit 0528209
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 142 deletions.
4 changes: 1 addition & 3 deletions source/RDR3.Patches/RDR3.Patches.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,12 @@
<ClCompile Include="..\Shared\memory.cpp" />
<ClCompile Include="..\Shared\stdafx.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="install_mod.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\helper.hpp" />
<ClInclude Include="..\..\include\memory.hpp" />
<ClInclude Include="..\..\include\stdafx.h" />
<ClInclude Include="mod_data.hpp" />
<ClInclude Include="mod_enum.h" />
<ClInclude Include="mod_data.h" />
<ClInclude Include="scripthook_sdk\inc\enums.h" />
<ClInclude Include="scripthook_sdk\inc\main.h" />
<ClInclude Include="scripthook_sdk\inc\nativeCaller.h" />
Expand Down
8 changes: 1 addition & 7 deletions source/RDR3.Patches/RDR3.Patches.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
<ClCompile Include="..\Shared\memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="install_mod.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\helper.hpp">
Expand All @@ -37,10 +34,7 @@
<ClInclude Include="..\..\include\memory.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mod_data.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mod_enum.h">
<ClInclude Include="mod_data.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="scripthook_sdk\inc\natives.h">
Expand Down
152 changes: 99 additions & 53 deletions source/RDR3.Patches/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include "nativeCaller.h"
#include "natives.h"

#include "mod_enum.h"

HMODULE baseModule{};

#define wstr(s) L#s
Expand All @@ -31,14 +29,7 @@ static void ReadConfig()
{
// no ini, lets generate one.
std::wstring ini_defaults = L"[Settings]\n"
"; Installs No Legal and Main Menu Mod\n"
"; Mod files:\n"
"; ```\n"
"; /x64/data/startup.meta\n"
"; /x64/main_and_legal_menu_skip_data/legal_screen_patch.ymt\n"
"; /x64/main_and_legal_menu_skip_data/main_menu_patch.ymt\n"
"; ```\n"
"; Source: https://www.rdr2mods.com/downloads/rdr2/other/304-main-and-legal-menu-skip/\n"
"; Thanks to this mod for reference on flowblock: https://www.rdr2mods.com/downloads/rdr2/other/304-main-and-legal-menu-skip/\n"
wstr(bSkipLegalMainMenu)" = true\n"
"; Start benchmark mode after game loads into autosave\n"
wstr(bBenchmarkOnly)" = false\n";
Expand Down Expand Up @@ -86,6 +77,8 @@ static void __attribute__((naked)) Opcode6cAsm()
jnz not_match;
cmp dword ptr[rcx + 0x440], 0x08FA8CCA;
jnz not_match;
cmp dword ptr[rcx + 0x1090], 5;
jz not_match;
mov dword ptr[rcx + 0x1090], 5; // benchmark sequence override
not_match:;
jmp qword ptr[rip + g_nextScrOpcodeReturnAddr];
Expand Down Expand Up @@ -177,9 +170,8 @@ uint32_t makeHash(const char* str)
return makeHash_(nullptr, str);
}

static uint32_t* AfterLoadScreenHook1(void* arg1, uint32_t* arg2)
void showConsole()
{
#ifdef _DEBUG
AllocConsole();
SetConsoleTitleA(PROJECT_NAME " - Debug Console");
freopen_s(reinterpret_cast<FILE**>(stdin), "conin$", "r", stdin);
Expand All @@ -191,20 +183,7 @@ static uint32_t* AfterLoadScreenHook1(void* arg1, uint32_t* arg2)
GetConsoleMode(hConsole, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole, dwMode);
#endif
* arg2 = 0; // not valid flow
return arg2;
}
bool breakLoop = false;
static uint32_t* AfterLoadScreenHook2(void* arg1, uint32_t* arg2)
{
// cannot skip this flow for now, must use mod to workaround it
// <https://www.rdr2mods.com/downloads/rdr2/other/304-main-and-legal-menu-skip/>
*arg2 = makeHash("landing_page/flow/landing_launcher_flow");
return arg2;
}

void Install_NoLogo_MainMenu_Patch(int mode);

uintptr_t CScript_DoScreenFadeInReturn = 0;
uintptr_t CScript_DoScreenFadeOutReturn = 0;
Expand Down Expand Up @@ -267,12 +246,85 @@ static void DO_SCREEN_FADE_OUT_Hook(CScriptFuncArgs* arg)
}
CScript_DoScreenFadeOutAsm(arg);
}

uintptr_t ParseFlowBlockReturn = 19;

typedef struct xml_keys_
{
void* not_useful[9];
char* key;
void* not_useful2[3];
}xml_keys;

static void __attribute__((naked)) ParseFlowBlockAsm(void* arg1, xml_keys* keys, const char* prev_key)
{
__asm
{
MOV RAX, RSP;
MOV qword ptr[RAX + 0x8], RBX;
MOV qword ptr[RAX + 0x10], RBP;
MOV qword ptr[RAX + 0x18], RSI;
MOV qword ptr[RAX + 0x20], RDI;
jmp qword ptr[rip + ParseFlowBlockReturn];
}
}

// https://github.com/PLTytus/various_joaat/blob/e06f51b1cfe95c2690b0fb46adce51c7d1f81d8a/joaat.c
// Hash method doesn't really matter
// Its just to prevent using strcmp
constexpr uint32_t joaat(const char* s)
{
uint32_t hash = 0;
for (; *s; ++s)
{
hash += *s;
hash += (hash << 10);
hash ^= (hash >> 6);
}

hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);

return hash;
}

static void ParseFlowBlockHook(void* arg1, xml_keys* keys, const char* prev_key)
{
if (!keys->key)
{
// the compiler will generate similar code if i used call() + return
goto no_match_or_quit;
}
switch (joaat(keys->key))
{
case joaat("feedback_context_switch.boot_screen_host.sign_in_flow_activity_sentinel.account_picker_activity_sentinel.account_picker_wrapper"):
{
keys->key = (char*)"feedback_context_switch.boot_screen_host.profile_flow_activity_sentinel.wait_for_profile";
break;
}
case joaat("input_context_switch.can_access_sp_check"):
case joaat("input_context_switch.boot_flow_startup"):
case joaat("input_context_switch.boot_flow_invite"):
case joaat("input_context_switch.boot_flow_sign_out"):
case joaat("input_context_switch.mp_direct_dispatch_code_event.mp_direct_scene.mp_direct_host"):
{
keys->key = (char*)"input_context_switch.landing_camera_fader.landing_main.landing_filter.priority_feed_decision_sp.set_feedback_context.from_priority_feed_decision_sp_can_access_sp.sp_menu.launch_sp";
break;
}
default:
{
break;
}
}
no_match_or_quit:
ParseFlowBlockAsm(arg1, keys, prev_key);
}

static void ApplyPatches()
{
Install_NoLogo_MainMenu_Patch((bSkipLegalMainMenu || bBenchmarkOnly) ? ModInstallEnable : ModDisable);
if (bBenchmarkOnly)
{
baseModule = GetModuleHandle(NULL);
void* jumpPattern = (void*)FindAndPrintPatternW(L"cc cc cc cc cc cc cc cc cc cc cc cc cc cc", L"Int3 Array");
void* OpcodePattern = (void*)FindAndPrintPatternW(L"48 83 eb 10 48 ff c7 0f b6 17", L"6c Opcode finder");
g_nextScrOpcodeReturnAddr = FindAndPrintPatternW(L"4c 8b 75 67 4c 8b 7c 24 38 4c 8b c3 48 ff c7", L"nextScrOpcode", +9);
Expand All @@ -295,38 +347,28 @@ static void ApplyPatches()
CScript_DoScreenFadeOutReturn = Cam_DoFadePtrs + 12 + 6;
}
}
}

// failed attempt at skipping logos and main menu
#ifndef _DEBUG
if (false)
#endif
// generates cleaner diff
{
if (bBenchmarkOnly || bSkipLegalMainMenu)
{
uintptr_t* boot_flow_vftable = (uintptr_t*)ReadLEA32(L"48 8d 05 ? ? ? ? 33 d2 48 89 03 48 8d 8b 30 01 00 00", L"boot_flow_vftable", 0, 3, 7);
uintptr_t* landing_launcher_flow_vftable = (uintptr_t*)ReadLEA32(L"48 8d 05 ? ? ? ? 48 8b d9 48 89 01 48 81 c1 a8 06 00 00", L"landing_launcher_flow_vftable", 0, 3, 7);
printf_s("boot_flow_vftable[30] = 0x%p\n", boot_flow_vftable[30]);
printf_s("landing_launcher_flow_vftable[30] = 0x%p\n", landing_launcher_flow_vftable[30]);

if (boot_flow_vftable)
{
void* jumpPattern2 = (void*)FindAndPrintPatternW(L"cc cc cc cc cc cc cc cc cc cc cc cc cc cc", L"Int3 Array");
if (jumpPattern2)
{
BranchFunction32((void*)boot_flow_vftable[30], jumpPattern2, 5);
Memory::DetourFunction64(jumpPattern2, (void*)AfterLoadScreenHook1, 14);
}
}

/*
if (landing_launcher_flow_vftable)
uintptr_t ParseXmlBlockAddr = FindAndPrintPatternW(L"48 8b c4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 56 48 83 ec 20 49 8b f0 48 8b da 4c 8b 41 08", L"ParseXmlBlockAddr");
uintptr_t BogusErrorCheck = FindAndPrintPatternW(L"48 83 ec 28 45 33 c9 e8 ?? ?? ?? ?? cc", L"BogusErrorCheck");
if (ParseXmlBlockAddr && BogusErrorCheck)
{
void* jumpPattern2 = (void*)FindAndPrintPatternW(L"cc cc cc cc cc cc cc cc cc cc cc cc cc cc", L"Int3 Array");
if (jumpPattern2)
{
BranchFunction32((void*)landing_launcher_flow_vftable[30], jumpPattern2, 5);
Memory::DetourFunction64(jumpPattern2, (void*)AfterLoadScreenHook2, 14);
}
ParseFlowBlockReturn += ParseXmlBlockAddr;
Memory::DetourFunction64((void*)ParseXmlBlockAddr, (void*)ParseFlowBlockHook, 14);
// patches sus errors when replacing the pointer in `ParseFlowBlockHook`
// TODO: Check if calling C script from a hook works now
// and also find a c script function that only runs when player is alive and once a frame
// this will hopefully eliminate the need for linking with scriptHook
// just like orbis version
unsigned char ret[] = { 0xc3 };
Memory::PatchBytes(BogusErrorCheck, ret, sizeof(ret));
}
*/
}
}
}
Expand Down Expand Up @@ -367,6 +409,10 @@ static void ScriptMain()

static void Main(HMODULE hModule)
{
#ifdef _DEBUG
showConsole();
#endif
baseModule = GetModuleHandle(NULL);
ReadConfig();
ApplyPatches();
if (bBenchmarkOnly)
Expand Down
72 changes: 0 additions & 72 deletions source/RDR3.Patches/install_mod.cpp

This file was deleted.

File renamed without changes.
7 changes: 0 additions & 7 deletions source/RDR3.Patches/mod_enum.h

This file was deleted.

0 comments on commit 0528209

Please sign in to comment.