Skip to content

Commit

Permalink
scaleform mem
Browse files Browse the repository at this point in the history
  • Loading branch information
Perchik71 committed Aug 12, 2024
1 parent fdc64df commit 5b71a57
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 43 deletions.
2 changes: 1 addition & 1 deletion X-Cell-FO4.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@
<BuildStlModules>false</BuildStlModules>
<CallingConvention>StdCall</CallingConvention>
<ForcedIncludeFiles>xc_common.h</ForcedIncludeFiles>
<DisableSpecificWarnings>4477;4996;6001;26495;28125;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>4477;4996;6001;6250;6333;26495;28125;28160;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
Expand Down
24 changes: 24 additions & 0 deletions include/xc_patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,32 @@

#pragma once

#include <vector>
#include <xc_assertion.h>
#include <initializer_list>

namespace xc
{
class scope_relocate_al
{
public:
inline scope_relocate_al(LPVOID address, SIZE_T size) : _protected(0), _address(address), _size(size)
{
_xc_assert_msg_fmt(VirtualProtect(_address, _size, PAGE_EXECUTE_READWRITE, &_protected),
"Address: %p Size: %X", _address, _size);
}

inline ~scope_relocate_al()
{
// Ignore if this fails, the memory was copied either way
VirtualProtect(_address, _size, _protected, &_protected);
}
private:
LPVOID _address;
SIZE_T _size;
DWORD _protected;
};

class patch
{
public:
Expand All @@ -34,6 +56,8 @@ namespace xc
virtual uintptr_t detour_jump(uintptr_t target, uintptr_t func) const noexcept;
virtual uintptr_t detour_call(uintptr_t target, uintptr_t func) const noexcept;
virtual uint32_t calc_rva(uintptr_t from, uintptr_t target, uint32_t opcode_offset) const noexcept;
virtual uintptr_t find_pattern(uintptr_t start_address, uintptr_t max_size, const char* mask) const noexcept;
virtual std::vector<uintptr_t> find_patterns(uintptr_t start_address, uintptr_t max_size, const char* mask) const noexcept;
private:
bool start_impl() const;
};
Expand Down
3 changes: 2 additions & 1 deletion include/xc_patch_archive_limit.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ namespace xc
virtual bool run() const;
private:
static uint32_t impl_memcpy_hash_from_archive_table(void* archive, void* archive_hash, file_hash_t* hash, size_t read_size);
static void impl_set_index_archive_to_hash();
static void impl_set_index_archive_to_hash_og();
static void impl_set_index_archive_to_hash_ng();
};
}
73 changes: 73 additions & 0 deletions source/xc_patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,79 @@ namespace xc
return (uint32_t)delta;
}

uintptr_t patch::find_pattern(uintptr_t start_address, uintptr_t max_size, const char* mask) const noexcept
{
std::vector<std::pair<uint8_t, bool>> pattern;

for (size_t i = 0; i < strlen(mask);)
{
if (mask[i] != '?')
{
pattern.emplace_back((uint8_t)strtoul(&mask[i], nullptr, 16), false);
i += 3;
}
else
{
pattern.emplace_back(0x00, true);
i += 2;
}
}

const uint8_t* dataStart = (uint8_t*)start_address;
const uint8_t* dataEnd = (uint8_t*)start_address + max_size + 1;

auto ret = std::search(dataStart, dataEnd, pattern.begin(), pattern.end(),
[](uint8_t CurrentByte, std::pair<uint8_t, bool>& Pattern) {
return Pattern.second || (CurrentByte == Pattern.first);
});

if (ret == dataEnd)
return 0;

return std::distance(dataStart, ret) + start_address;
}

std::vector<uintptr_t> patch::find_patterns(uintptr_t start_address, uintptr_t max_size, const char* mask) const noexcept
{
std::vector<uintptr_t> results;
std::vector<std::pair<uint8_t, bool>> pattern;

for (size_t i = 0; i < strlen(mask);)
{
if (mask[i] != '?')
{
pattern.emplace_back((uint8_t)strtoul(&mask[i], nullptr, 16), false);
i += 3;
}
else
{
pattern.emplace_back(0x00, true);
i += 2;
}
}

const uint8_t* dataStart = (uint8_t*)start_address;
const uint8_t* dataEnd = (uint8_t*)start_address + max_size + 1;

for (const uint8_t* i = dataStart;;)
{
auto ret = std::search(i, dataEnd, pattern.begin(), pattern.end(),
[](uint8_t CurrentByte, std::pair<uint8_t, bool>& Pattern) {
return Pattern.second || (CurrentByte == Pattern.first);
});

if (ret == dataEnd)
break;

uintptr_t addr = std::distance(dataStart, ret) + start_address;
results.push_back(addr);

i = (uint8_t*)(addr + 1);
}

return results;
}

bool patch::start_impl() const
{
auto name_patch = get_name();
Expand Down
55 changes: 16 additions & 39 deletions source/xc_patch_archive_limit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include <vmm.h>
#include <xc_patch_archive_limit.h>
#include <xc_assertion.h>
#include <xc_version.h>
#include <xc_plugin.h>

Expand Down Expand Up @@ -34,26 +33,6 @@ namespace xc

tree_db_general_t g_tree_db_general;

class scope_relocate_al
{
public:
scope_relocate_al(LPVOID address, SIZE_T size) : _protected(0), _address(address), _size(size)
{
_xc_assert_msg_fmt(VirtualProtect(_address, _size, PAGE_EXECUTE_READWRITE, &_protected),
"Address: %p Size: %X", _address, _size);
}

~scope_relocate_al()
{
// Ignore if this fails, the memory was copied either way
VirtualProtect(_address, _size, _protected, &_protected);
}
private:
LPVOID _address;
SIZE_T _size;
DWORD _protected;
};

const char* patch_archive_limit::get_name() const noexcept
{
return "archive_limit";
Expand Down Expand Up @@ -646,22 +625,12 @@ namespace xc
// supported 8 version archive
patch_mem_nop(g_plugin->get_base() + 0x1B6FA9F, { 0x8 });




//detour_call(g_plugin->get_base() + 0x1B76AC9, (uintptr_t)&impl_memcpy_hash_from_archive_table);
//memcpy_hash_from_archive_table_orig = g_plugin->get_base() + 0x1B78970;

//detour_call(g_plugin->get_base() + 0x15864B5, (uintptr_t)&impl_set_index_archive_to_hash);

//offset = g_plugin->get_base() + 1587095;
//// Remove useless stuff.
//
//// mov eax, dword ptr ds:[rsi+0xC]
//// mov dword ptr ds:[rdi+0xC], eax
//patch_mem(offset, { 0x8B, 0x46, 0x0C, 0x89, 0x47, 0x0C });
#if 0
detour_call(g_plugin->get_base() + 0x1B76AC9, (uintptr_t)&impl_memcpy_hash_from_archive_table);
memcpy_hash_from_archive_table_orig = g_plugin->get_base() + 0x1B78970;

Sleep(20000);
detour_call(g_plugin->get_base() + 0x1B76AF7, (uintptr_t)&impl_set_index_archive_to_hash_og);
#endif
}
else if (g_plugin->get_runtime_version() == RUNTIME_VERSION_1_10_984)
{
Expand Down Expand Up @@ -1160,7 +1129,7 @@ namespace xc
detour_call(g_plugin->get_base() + 0x158646F, (uintptr_t)&impl_memcpy_hash_from_archive_table);
memcpy_hash_from_archive_table_orig = g_plugin->get_base() + 0x1587BA0;

detour_call(g_plugin->get_base() + 0x15864B5, (uintptr_t)&impl_set_index_archive_to_hash);
detour_call(g_plugin->get_base() + 0x15864B5, (uintptr_t)&impl_set_index_archive_to_hash_ng);

offset = g_plugin->get_base() + 1587095;
// Remove useless stuff.
Expand All @@ -1186,11 +1155,19 @@ namespace xc
return result; // 0 - OK
}

void patch_archive_limit::impl_set_index_archive_to_hash()
void patch_archive_limit::impl_set_index_archive_to_hash_og()
{
// It is necessary to get the stack of the calling function.
auto rsp = (uintptr_t)_AddressOfReturnAddress() + 8;
// Set archive index from stack
*((uint16_t*)(rsp + 0x4C)) = *((uint16_t*)(rsp + 0x250));
}

void patch_archive_limit::impl_set_index_archive_to_hash_ng()
{
// It is necessary to get the stack of the calling function.
auto rsp = (uintptr_t)_AddressOfReturnAddress() + 8;
// Set archive index from stack
*((uint16_t*)(rsp + 0x3C)) = *((uint16_t*)(rsp + 0x1E8));//min(, (uint16_t)255);
*((uint16_t*)(rsp + 0x3C)) = *((uint16_t*)(rsp + 0x1E8));
}
}
62 changes: 61 additions & 1 deletion source/xc_patch_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,38 @@ namespace xc
return nullptr;
}
};

class BSScaleformSysMemMapper
{
public:
constexpr static UInt32 PAGE_SIZE = (size_t)256 * 1024; // 256 Kb
constexpr static UInt32 HEAP_SIZE = (size_t)512 * 1024 * 1024; // 512 Mb

static uint32_t get_page_size(BSScaleformSysMemMapper* _this)
{
return (uint32_t)PAGE_SIZE;
}

static void* init(BSScaleformSysMemMapper* _this, size_t size)
{
return VirtualAlloc(NULL, (SIZE_T)size, MEM_RESERVE, PAGE_READWRITE);
}

static bool release(BSScaleformSysMemMapper* _this, void* address)
{
return VirtualFree((LPVOID)address, (SIZE_T)HEAP_SIZE, MEM_RELEASE);
}

static void* alloc(BSScaleformSysMemMapper* _this, void* address, size_t size)
{
return VirtualAlloc((LPVOID)address, (SIZE_T)size, MEM_COMMIT, PAGE_READWRITE);
}

static bool free(BSScaleformSysMemMapper* _this, void* address, size_t size)
{
return VirtualFree((LPVOID)address, (SIZE_T)size, MEM_DECOMMIT);
}
};
}

const char* patch_memory::get_name() const noexcept
Expand Down Expand Up @@ -269,6 +301,20 @@ namespace xc
detour_jump((g_plugin->get_base() + 0x1B13F70), (uintptr_t)&detail::BGSScrapHeap::alloc);
detour_jump((g_plugin->get_base() + 0x1B14580), (uintptr_t)&detail::BGSScrapHeap::dealloc);
detour_jump((g_plugin->get_base() + 0x1E21B10), (uintptr_t)&detail::bhkThreadMemorySource::__ctor__); // bhkThreadMemorySource init

// BSScaleformSysMemMapper
{
auto vtable = (uintptr_t*)(g_plugin->get_base() + 0x2EB92C8);
scope_relocate_al lock((LPVOID)vtable, 0x40);
vtable[0] = (uintptr_t)detail::BSScaleformSysMemMapper::get_page_size;
vtable[1] = (uintptr_t)detail::BSScaleformSysMemMapper::init;
vtable[2] = (uintptr_t)detail::BSScaleformSysMemMapper::release;
vtable[3] = (uintptr_t)detail::BSScaleformSysMemMapper::alloc;
vtable[4] = (uintptr_t)detail::BSScaleformSysMemMapper::free;

patch_mem((g_plugin->get_base() + 0x211214B), (UInt8*)&detail::BSScaleformSysMemMapper::PAGE_SIZE, 4);
patch_mem((g_plugin->get_base() + 0x2112151), (UInt8*)&detail::BSScaleformSysMemMapper::HEAP_SIZE, 4);
}

// So that it is never called
patch_mem((g_plugin->get_base() + 0xD0C160), { 0xC3, 0x90 }); // MemoryManager - Default/Static/File heaps init
Expand All @@ -287,6 +333,20 @@ namespace xc
detour_jump((g_plugin->get_base() + 0x15425E0), (uintptr_t)&detail::BGSScrapHeap::dealloc);
detour_jump((g_plugin->get_base() + 0x17D9DF0), (uintptr_t)&detail::bhkThreadMemorySource::__ctor__); // bhkThreadMemorySource init

// BSScaleformSysMemMapper
{
auto vtable = (uintptr_t*)(g_plugin->get_base() + 0x25131D8);
scope_relocate_al lock((LPVOID)vtable, 0x40);
vtable[0] = (uintptr_t)detail::BSScaleformSysMemMapper::get_page_size;
vtable[1] = (uintptr_t)detail::BSScaleformSysMemMapper::init;
vtable[2] = (uintptr_t)detail::BSScaleformSysMemMapper::release;
vtable[3] = (uintptr_t)detail::BSScaleformSysMemMapper::alloc;
vtable[4] = (uintptr_t)detail::BSScaleformSysMemMapper::free;

patch_mem((g_plugin->get_base() + 0x19FF5D9), (UInt8*)&detail::BSScaleformSysMemMapper::PAGE_SIZE, 4);
patch_mem((g_plugin->get_base() + 0x19FF5E4), (UInt8*)&detail::BSScaleformSysMemMapper::HEAP_SIZE, 4);
}

// So that it is never called
patch_mem((g_plugin->get_base() + 0xB8DC50), { 0xC3, 0x90 }); // MemoryManager - Default/Static/File heaps init
patch_mem((g_plugin->get_base() + 0x153D5D0), { 0xC3, 0x90 }); // BSSmallBlockAllocator init
Expand All @@ -295,7 +355,7 @@ namespace xc
}
else
_ERROR("The patch has not been fully installed, as the mod does not know the game");

return true;
}

Expand Down
Binary file modified version/build_version.txt
Binary file not shown.
Binary file modified version/resource_version2.h
Binary file not shown.
2 changes: 1 addition & 1 deletion x-cell.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ facegen=true
; - Use OS file cache for less disk access.
io=true
; - Replace old zlib decompression code with optimized libdeflate.
libdeflate=false
libdeflate=true
; - Replacing functions WritePrivateProfileStringA, GetPrivateProfileStringA, GetPrivateProfileIntA
; They are outdated and constantly open and parsing the ini file. Complements Buffout 4, Buffout 4 NG.
; Incompatible with the mod https://www.nexusmods.com/fallout4/mods/33947 PrivateProfileRedirector.
Expand Down

0 comments on commit 5b71a57

Please sign in to comment.