Skip to content

Commit 1cc768a

Browse files
committed
[PR] add generate function for fakepdb
1 parent fb22cb3 commit 1cc768a

File tree

6 files changed

+149
-5
lines changed

6 files changed

+149
-5
lines changed

UE4SS/include/UE4SSProgram.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ namespace RC
9090
constexpr static CharType m_settings_file_name[] = STR("UE4SS-settings.ini");
9191
constexpr static CharType m_log_file_name[] = STR("UE4SS.log");
9292
constexpr static CharType m_object_dumper_file_name[] = STR("UE4SS_ObjectDump.txt");
93+
constexpr static CharType m_func_dumper_file_name[] = STR("UE4SS_FuncDump.json");
9394

9495
public:
9596
RC_UE4SS_API static SettingsManager settings_manager;
@@ -277,6 +278,7 @@ namespace RC
277278
bool is_below_425,
278279
std::unordered_set<Unreal::UFunction*>* in_dumped_functions = nullptr) -> void;
279280
RC_UE4SS_API static auto dump_all_objects_and_properties(const File::StringType& output_path_and_file_name) -> void;
281+
RC_UE4SS_API static auto dump_all_funcs(const File::StringType& output_path_and_file_name) -> void;
280282

281283
template <typename T>
282284
static auto find_mod_by_name(StringViewType mod_name, IsInstalled = IsInstalled::No, IsStarted = IsStarted::No) -> T*

UE4SS/src/GUI/GUI.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ namespace RC::GUI
109109
{
110110
ImGui::BeginDisabled(true);
111111
}
112+
113+
ImGui::SameLine();
114+
if (ImGui::Button(ICON_FA_ARCHWAY " Dump funcs to json"))
115+
{
116+
m_event_thread_busy = true;
117+
UE4SSProgram::get_program().queue_event(
118+
[](void* data) {
119+
UE4SSProgram::dump_all_funcs(UE4SSProgram::get_program().get_object_dumper_output_directory() + STR("\\") +
120+
UE4SSProgram::m_func_dumper_file_name);
121+
static_cast<GUI::DebuggingGUI*>(data)->m_event_thread_busy = false;
122+
},
123+
this);
124+
}
112125
ImGui::SameLine();
113126
if (ImGui::Button(ICON_FA_SYNC " Restart All Mods"))
114127
{

UE4SS/src/GUI/LiveView.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,12 @@ namespace RC::GUI
20932093
auto render_property_value_context_menu = [&](std::string_view id_override = "") {
20942094
if (ImGui::BeginPopupContextItem(id_override.empty() ? property_name.c_str() : fmt::format("context-menu-{}", id_override).c_str()))
20952095
{
2096+
if (ImGui::MenuItem("Copy##Obj"))
2097+
{
2098+
auto _ = to_string(property_text.GetCharArray());
2099+
_ = _.substr(_.find_first_of("'") + 1); _.resize(_.size() - 1);
2100+
ImGui::SetClipboardText(_.c_str());
2101+
}
20962102
if (ImGui::MenuItem("Copy name"))
20972103
{
20982104
ImGui::SetClipboardText(property_name.c_str());
@@ -3240,6 +3246,18 @@ namespace RC::GUI
32403246
{
32413247
ImGui::Checkbox(ICON_FA_EYE " Watch value", &function_watcher_it->second->enabled);
32423248
}
3249+
3250+
if (ImGui::MenuItem(ICON_FA_COPY " Copy Address"))
3251+
{
3252+
if (UFunction* func = Cast<UFunction>(object))
3253+
{
3254+
auto func_ptr = func->GetFuncPtr();
3255+
std::string s = (fmt::format("{}", std::bit_cast<void*>(func_ptr)));
3256+
3257+
Output::send(STR("Copy Address: {}\n"), object->GetFullName());
3258+
ImGui::SetClipboardText(s.c_str());
3259+
}
3260+
}
32433261
}
32443262
ImGui::EndPopup();
32453263
}
@@ -3890,6 +3908,18 @@ namespace RC::GUI
38903908
}
38913909
collapse_all_except(m_currently_opened_tree_node);
38923910
render_context_menu(tree_node_name, object);
3911+
3912+
if (UFunction* func = Cast<UFunction>(object))
3913+
{
3914+
auto func_ptr = func->GetFuncPtr();
3915+
3916+
if (func_ptr != Unreal::UObject::ProcessInternalInternal.get_function_address() &&
3917+
func->HasAnyFunctionFlags(Unreal::EFunctionFlags::FUNC_Native))
3918+
{
3919+
tree_node_name.append(fmt::format(" {}", std::bit_cast<void*>(func_ptr)));
3920+
}
3921+
object->GetName();
3922+
}
38933923
});
38943924
}
38953925
else

UE4SS/src/Mod/LuaMod.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3605,10 +3605,11 @@ No overload found for function 'RegisterHook'.
36053605
generic_pre_id = m_last_generic_hook_id;
36063606
m_generic_hook_id_to_native_hook_id.emplace(++m_last_generic_hook_id, post_id);
36073607
generic_post_id = m_last_generic_hook_id;
3608-
Output::send<LogLevel::Verbose>(STR("[RegisterHook] Registered native hook ({}, {}) for {}\n"),
3608+
Output::send<LogLevel::Verbose>(STR("[RegisterHook] Registered native hook ({}, {}) for {} {}\n"),
36093609
generic_pre_id,
36103610
generic_post_id,
3611-
unreal_function->GetFullName());
3611+
unreal_function->GetFullName(),
3612+
std::bit_cast<void*>(func_ptr));
36123613
}
36133614
else if (func_ptr && func_ptr == Unreal::UObject::ProcessInternalInternal.get_function_address() &&
36143615
!unreal_function->HasAnyFunctionFlags(Unreal::EFunctionFlags::FUNC_Native))
@@ -3623,10 +3624,11 @@ No overload found for function 'RegisterHook'.
36233624
callback_data.registry_indexes.emplace_back(hook_lua, LuaCallbackData::RegistryIndex{lua_callback_registry_index, m_last_generic_hook_id});
36243625
generic_pre_id = m_last_generic_hook_id;
36253626
generic_post_id = m_last_generic_hook_id;
3626-
Output::send<LogLevel::Verbose>(STR("[RegisterHook] Registered script hook ({}, {}) for {}\n"),
3627+
Output::send<LogLevel::Verbose>(STR("[RegisterHook] Registered script hook ({}, {}) for {} {}\n"),
36273628
generic_pre_id,
36283629
generic_post_id,
3629-
unreal_function->GetFullName());
3630+
unreal_function->GetFullName(),
3631+
std::bit_cast<void*>(func_ptr));
36303632
}
36313633
else
36323634
{

UE4SS/src/UE4SSProgram.cpp

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#define NOMINMAX
2-
2+
extern int hCalc(char*);
33
#include <Windows.h>
44

55
#ifdef TEXT
@@ -1895,6 +1895,101 @@ namespace RC
18951895
Output::send(STR("Dumping GUObjectArray took {} seconds\n"), dumper_duration);
18961896
// Object & Property Dumper -> END
18971897
}
1898+
auto UE4SSProgram::dump_all_funcs(const File::StringType& output_path_and_file_name) -> void
1899+
{
1900+
// Object & Property Dumper -> START
1901+
if (settings_manager.ObjectDumper.LoadAllAssetsBeforeDumpingObjects)
1902+
{
1903+
Output::send(STR("Loading all assets...\n"));
1904+
double asset_loading_duration{};
1905+
{
1906+
ScopedTimer loading_timer{&asset_loading_duration};
1907+
1908+
UAssetRegistry::LoadAllAssets();
1909+
}
1910+
Output::send(STR("Loading all assets took {} seconds\n"), asset_loading_duration);
1911+
}
1912+
1913+
double dumper_duration{};
1914+
{
1915+
ScopedTimer dumper_timer{&dumper_duration};
1916+
1917+
std::unordered_set<FField*> dumped_fields;
1918+
// There will be tons of dumped fields so lets just reserve tons in order to speed things up a bit
1919+
dumped_fields.reserve(100000);
1920+
1921+
bool is_below_425 = Unreal::Version::IsBelow(4, 25);
1922+
1923+
// The final outputted string shouldn't need be reformatted just to put a new line at the end
1924+
// Instead the object/property implementations should add a new line in the last format that they do
1925+
//
1926+
// Optimizations done:
1927+
// 1. The entire code-base has been changed to use 'wchar_t' instead of 'char'.
1928+
// The effect of this is that there is no need to ever convert between types.
1929+
// There's also no thinking about which type should be used since 'wchar_t' is now the standard for UE4SS.
1930+
// The downside with wchar_t is that all files that get output to will be doubled in size.
1931+
1932+
using ObjectDumperOutputDevice = Output::NewFileDevice;
1933+
Output::Targets<ObjectDumperOutputDevice> scoped_dumper_out;
1934+
auto& file_device = scoped_dumper_out.get_device<ObjectDumperOutputDevice>();
1935+
file_device.set_file_name_and_path(output_path_and_file_name);
1936+
file_device.set_formatter([](File::StringViewType string) -> File::StringType {
1937+
return File::StringType{string};
1938+
});
1939+
1940+
// Make string & reserve massive amounts of space to hopefully not reach the end of the string and require more
1941+
// dynamic allocations
1942+
StringType out_line;
1943+
out_line.reserve(200000000);
1944+
1945+
Output::send(STR("Dumping all objects & properties in GUObjectArray\n"));
1946+
1947+
out_line.append(STR("{\"general\":{\"filename\":\"sample.exe\",\"architecture\":\"x86\",\"bitness\":64},\"segments\":[{\"name\":\".text\",\"start_rva\":4096,\"type\":\"CODE\",\"selector\":1}],\"exports\":[],\"functions\":[],\"names\":[\n"));
1948+
1949+
UObjectGlobals::ForEachUObject([&](void* object, [[maybe_unused]] int32_t chunk_index, [[maybe_unused]] int32_t object_index) {
1950+
1951+
if (static_cast<UObject*>(object)->IsA<UFunction>())
1952+
{
1953+
int32_t ii;
1954+
UFunction* f = (UFunction*)object;
1955+
if ((f->GetFunctionFlags() & 0x00000400) != 0) // FUNC_Native
1956+
{
1957+
if ( (ii = hCalc((char*)f->GetFuncPtr())) )
1958+
{
1959+
std::wstring a, ws = f->GetFullName();
1960+
size_t pos = ws.rfind(L'.');
1961+
if (pos != std::wstring::npos)
1962+
{
1963+
a = ws.substr(pos + 1);
1964+
pos = a.find(L":");
1965+
if (pos != std::wstring::npos)
1966+
{
1967+
a.replace(pos, 1, L"__");
1968+
}
1969+
}
1970+
out_line.append( // "path": "{}",, p_typed_this->GetFullName()
1971+
fmt::format(STR("{{\"rva\": {},\"name\": \"{}\",\"is_public\": false,\"is_func\": false}},\n"), ii, a));
1972+
}
1973+
}
1974+
}
1975+
return LoopAction::Continue;
1976+
});
1977+
1978+
out_line[out_line.size() - 2] = ']';
1979+
out_line[out_line.size() - 1] = '}';
1980+
1981+
// save to file
1982+
scoped_dumper_out.send(out_line);
1983+
1984+
// Reset the dumped_fields set, otherwise no fields will be dumped in subsequent dumps
1985+
dumped_fields.clear();
1986+
Output::send(STR("Done iterating GUObjectArray\n"));
1987+
}
1988+
1989+
UAssetRegistry::FreeAllForcefullyLoadedAssets();
1990+
Output::send(STR("Dumping GUObjectArray took {} seconds\n"), dumper_duration);
1991+
// Object & Property Dumper -> END
1992+
}
18981993

18991994
auto UE4SSProgram::static_cleanup() -> void
19001995
{

UE4SS/src/main_ue4ss_rewritten.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ auto dll_process_attached(HMODULE moduleHandle) -> void
115115
}
116116
}
117117

118+
char* hGame, *hGameEnd; int hCalc(char* p){ return (p > hGame && p < hGameEnd) ? (int)(p - hGame) : 0; }
118119
auto WIN_API_FUNCTION_NAME(HMODULE hModule, DWORD ul_reason_for_call, [[maybe_unused]] LPVOID lpReserved) -> BOOL
119120
{
120121
switch (ul_reason_for_call)
121122
{
122123
case DLL_PROCESS_ATTACH:
124+
hGame = (PSTR)GetModuleHandleW(0); hGameEnd = hGame + PIMAGE_NT_HEADERS(hGame + PIMAGE_DOS_HEADER(hGame)->e_lfanew)->OptionalHeader.SizeOfImage;
123125
dll_process_attached(hModule);
124126
break;
125127
case DLL_THREAD_ATTACH:

0 commit comments

Comments
 (0)