Skip to content

Commit 75bfec8

Browse files
committed
Update hookcheck
Added ability to ignore symbols by source file path, and functions in output are sorted now
1 parent 91fd24c commit 75bfec8

File tree

7 files changed

+132
-36
lines changed

7 files changed

+132
-36
lines changed

Client/game_sa/premake5.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ project "Game SA"
4343
}
4444

4545
postbuildcommands {
46-
"%[%{!wks.location}/../utils/hookcheck.exe] %[$(TargetPath)]"
46+
"%[%{!wks.location}/../utils/hookcheck.exe] -dll:%[$(TargetPath)] -ignore:\\vendor\\"
4747
}
4848

4949
filter "architecture:not x86"

Client/multiplayer_sa/premake5.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ project "Multiplayer SA"
3434
}
3535

3636
postbuildcommands {
37-
"%[%{!wks.location}/../utils/hookcheck.exe] %[$(TargetPath)]"
37+
"%[%{!wks.location}/../utils/hookcheck.exe] -dll:%[$(TargetPath)] -ignore:\\vendor\\"
3838
}
3939

4040
filter "architecture:not x86"

utils/hookcheck.exe

14 KB
Binary file not shown.

utils/src/hookcheck/hookcheck.cpp

Lines changed: 106 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <algorithm>
2626
#include <array>
2727
#include <format>
28+
#include <vector>
29+
#include <optional>
2830

2931
#define NOMINMAX
3032
#define WIN32_LEAN_AND_MEAN
@@ -36,35 +38,79 @@ int wmain(int argc, wchar_t* argv[])
3638
{
3739
SetConsoleOutputCP(CP_UTF8);
3840

39-
if (argc < 2)
41+
struct Arguments
4042
{
41-
PrintLn(L"Usage: " TOOL_NAME L" <dll_path> [<label:" DEFAULT_LABEL L">]");
42-
return 1;
43-
}
43+
std::optional<std::wstring> dll;
44+
std::optional<std::wstring> label;
45+
std::vector<std::wstring> ignore;
46+
} arguments;
4447

45-
const fs::path dllFilePath = fs::absolute(argv[1]);
46-
std::wstring labelName = (argc >= 3) ? argv[2] : (L"" DEFAULT_LABEL);
48+
for (int i = 1; i < argc; ++i)
49+
{
50+
std::wstring_view arg = argv[i];
4751

48-
if (!fs::exists(dllFilePath) || !fs::is_regular_file(dllFilePath))
52+
if (arg.length() < 2 || !arg.starts_with(L'-'))
53+
continue;
54+
55+
std::wstring key;
56+
std::wstring value;
57+
58+
if (const size_t pos = arg.find_first_of(L':', 1); pos != std::wstring_view::npos && pos > 1)
59+
{
60+
key = arg.substr(1, pos - 1);
61+
value = arg.substr(pos + 1);
62+
}
63+
else
64+
{
65+
key = arg.substr(1);
66+
}
67+
68+
Trim(key);
69+
Trim(value);
70+
71+
if (key.empty())
72+
continue;
73+
74+
if (!_wcsnicmp(key.c_str(), L"dll", key.size()))
75+
{
76+
if (!value.empty() && !arguments.dll.has_value())
77+
arguments.dll = value;
78+
79+
continue;
80+
}
81+
82+
if (!_wcsnicmp(key.c_str(), L"label", key.size()))
83+
{
84+
if (!value.empty() && !arguments.label.has_value())
85+
arguments.label = value;
86+
87+
continue;
88+
}
89+
90+
if (!_wcsnicmp(key.c_str(), L"ignore", key.size()))
91+
{
92+
if (!value.empty())
93+
arguments.ignore.emplace_back(value);
94+
95+
continue;
96+
}
97+
}
98+
99+
if (!arguments.dll.has_value())
49100
{
50-
PrintErrorLn(L"{} is not a regular file", dllFilePath.wstring());
101+
PrintLn(L"Usage: " TOOL_NAME L" -dll:<path> [-label:" DEFAULT_LABEL L"] [-ignore:<path>]");
51102
return 1;
52103
}
53104

54-
Trim(labelName);
105+
const fs::path dllFilePath = fs::absolute(arguments.dll.value());
106+
std::wstring labelName = arguments.label.value_or(L"" DEFAULT_LABEL);
55107

56-
if (labelName.empty())
108+
if (!fs::exists(dllFilePath) || !fs::is_regular_file(dllFilePath))
57109
{
58-
PrintWarningLn(L"'label' program argument is empty, using " DEFAULT_LABEL);
59-
labelName = (L"" DEFAULT_LABEL);
110+
PrintErrorLn(L"DLL file '{}' does not exist or is not readable", dllFilePath.wstring());
111+
return 1;
60112
}
61113

62-
#ifdef MTA_DEBUG
63-
PrintLn(L"[~] " TOOL_NAME L" " TOOL_VERSION);
64-
PrintLn(L"[~] Label: '{}'", labelName);
65-
PrintLn(L"[~] DLL: '{}'", dllFilePath.wstring());
66-
#endif
67-
68114
DllAnalyzer dll;
69115

70116
if (auto [errorCode, hr] = dll.LoadDll(dllFilePath.wstring()); errorCode != DllAnalyzer::LoadError::None)
@@ -133,7 +179,13 @@ int wmain(int argc, wchar_t* argv[])
133179
}
134180

135181
#ifdef MTA_DEBUG
136-
PrintLn(L"[~] PDB: '{}'", pdbFilePath.wstring());
182+
PrintLn(L"[~] " TOOL_NAME L" " TOOL_VERSION);
183+
PrintLn(L"[~] Label: '{}'", labelName);
184+
PrintLn(L"[~] DLL: '{}'", dllFilePath.wstring());
185+
PrintLn(L"[~] PDB: '{}'", pdbFilePath.wstring());
186+
187+
for (const std::wstring& ignore : arguments.ignore)
188+
PrintLn(L"[~] Ignore: '{}'", ignore);
137189
#endif
138190

139191
if (!fs::exists(pdbFilePath) || !fs::is_regular_file(pdbFilePath))
@@ -184,11 +236,40 @@ int wmain(int argc, wchar_t* argv[])
184236
return 1;
185237
}
186238

187-
const std::vector<PdbAnalyzer::Function>& functions = pdb.GetFunctions();
239+
std::vector<PdbAnalyzer::Function>& functions = pdb.GetFunctions();
188240

189241
if (functions.empty())
190242
return 0;
191243

244+
const auto ContainsIgnore = [&ignored = arguments.ignore](const PdbAnalyzer::Function& f)
245+
{
246+
for (const std::wstring& ignore : ignored)
247+
{
248+
if (StrStrIW(f.SourceFile.c_str(), ignore.c_str()) != nullptr)
249+
return true;
250+
}
251+
252+
return false;
253+
};
254+
255+
functions.erase(std::remove_if(std::begin(functions), std::end(functions), ContainsIgnore), std::end(functions));
256+
257+
const auto SortBySource = [](const PdbAnalyzer::Function& lhs, const PdbAnalyzer::Function& rhs)
258+
{
259+
const auto compare =
260+
std::lexicographical_compare_three_way(std::begin(lhs.SourceFile), std::end(lhs.SourceFile), std::begin(rhs.SourceFile), std::end(rhs.SourceFile),
261+
[](wchar_t a, wchar_t b) { return std::tolower(a) <=> std::tolower(b); });
262+
263+
if (std::is_lt(compare))
264+
return true;
265+
if (std::is_gt(compare))
266+
return false;
267+
268+
return lhs.SourceLine < rhs.SourceLine;
269+
};
270+
271+
std::sort(std::begin(functions), std::end(functions), SortBySource);
272+
192273
int exitCode = 0;
193274

194275
const auto isOurLabel = [&labelName](const PdbAnalyzer::Label& l) { return l.Name == labelName; };
@@ -200,6 +281,9 @@ int wmain(int argc, wchar_t* argv[])
200281

201282
std::vector<std::wstring> problems;
202283

284+
if (!hasLabel && !fs::is_regular_file(function.SourceFile))
285+
continue;
286+
203287
if (function.Name.starts_with(L"std::") || function.Name.starts_with(L'_'))
204288
{
205289
if (!hasLabel)
@@ -246,19 +330,8 @@ int wmain(int argc, wchar_t* argv[])
246330
if (problems.empty())
247331
continue;
248332

249-
if (function.SourceFile.empty())
250-
{
251-
PrintLn(L"");
252-
PrintLn(L"[>] {}", function.Name);
253-
254-
for (const std::wstring& problem : problems)
255-
PrintLn(L"[-] {}", problem);
256-
}
257-
else
258-
{
259-
for (const std::wstring& problem : problems)
260-
PrintLn(L"{}({}): {}", function.SourceFile, function.SourceLine, problem);
261-
}
333+
for (const std::wstring& problem : problems)
334+
PrintLn(L"{}({}): {}", function.SourceFile, function.SourceLine, problem);
262335
}
263336

264337
return exitCode;

utils/src/hookcheck/pdb-analyzer.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,15 @@ void PdbAnalyzer::AnalyzeFunction(IDiaSymbol* functionSymbol)
245245
newFunction.HasInlineAssembly = hasInlineAssembly;
246246

247247
AttachLocation(functionSymbol, newFunction);
248+
NormalizePath(newFunction.SourceFile);
249+
250+
if (newFunction.SourceFile.empty())
251+
{
252+
m_functions.pop_back();
253+
return;
254+
}
255+
248256
AttachLabels(functionSymbol, newFunction);
249-
SysFreeString(name);
250257
}
251258

252259
void PdbAnalyzer::AttachLocation(IDiaSymbol* functionSymbol, Function& attachTo)

utils/src/hookcheck/utility.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*****************************************************************************/
1111

1212
#include "utility.h"
13+
#include <filesystem>
1314

1415
void Trim(std::wstring& input)
1516
{
@@ -22,3 +23,16 @@ void Trim(std::wstring& input)
2223
if (size_t position = input.find_last_not_of(L" \r\n\t\0"); position != std::string_view::npos)
2324
input = input.substr(0, position + 1);
2425
}
26+
27+
void NormalizePath(std::wstring& input)
28+
{
29+
if (input.empty())
30+
return;
31+
32+
std::error_code ec{};
33+
34+
if (const auto canon = std::filesystem::canonical(input, ec); !ec)
35+
input = canon.wstring();
36+
37+
std::replace(input.begin(), input.end(), L'/', L'\\');
38+
}

utils/src/hookcheck/utility.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ using HRESULT = long;
1919

2020
void Trim(std::wstring& input);
2121

22+
void NormalizePath(std::wstring& input);
23+
2224
template <typename... Args>
2325
void PrintLn(const std::wformat_string<Args...> fmt, Args&&... args)
2426
{

0 commit comments

Comments
 (0)