diff --git a/tooling/cmd_dump.cpp b/tooling/cmd_dump.cpp index 2132d590..8e1512ad 100644 --- a/tooling/cmd_dump.cpp +++ b/tooling/cmd_dump.cpp @@ -78,9 +78,8 @@ bool dumpTreeFile(const std::string &filepath) { void register_cmd_dump(CLI::App &app) { auto cmd = app.add_subcommand("dump-tree", "Dump the tree-sitter AST for a file"); - cmd->add_option("-f,--filepath", filepath, - "File to dump the tree-sitter AST for") - ->required(); + cmd->add_option("file", filepath, + "Input C++ file to parse")->required(); cmd->final_callback([]() { spdlog::info("=== Processing: {} ===", filepath); dumpTreeFile(filepath); diff --git a/tooling/cmd_hooks.cpp b/tooling/cmd_hooks.cpp index f7b5d47c..31c8d2a4 100644 --- a/tooling/cmd_hooks.cpp +++ b/tooling/cmd_hooks.cpp @@ -24,10 +24,11 @@ bool generateHooksFile(const std::string &output_filepath) { for (const auto &func : fix_functions) { // Extract just the filename from the full path std::string filename = std::filesystem::path(func.filepath).filename().string(); + std::string conv_str = callingConventionToString(func.calling_convention); - output_stream << "HOOK(0x" << func.address << ", " << func.name << ") // " << filename << std::endl; + output_stream << "HOOK(0x" << func.address << ", " << func.name << ", " << conv_str << ") // " << filename << std::endl; - spdlog::debug("Added hook: {} {} from {}", func.address, func.name, filename); + spdlog::debug("Added hook: {} {} {} from {}", func.address, func.name, conv_str, filename); } output_stream.close(); diff --git a/tooling/database.cpp b/tooling/database.cpp index 40c1668b..597471ab 100644 --- a/tooling/database.cpp +++ b/tooling/database.cpp @@ -27,11 +27,11 @@ public: &delete_imports_stmt, "Failed to prepare delete imports statement"); prepareStatement("INSERT OR REPLACE INTO Functions (filepath, name, " - "address, type) VALUES (?, ?, ?, ?)", + "address, type, calling_convention) VALUES (?, ?, ?, ?, ?)", &insert_functions_stmt, "Failed to prepare insert functions statement"); prepareStatement("INSERT OR REPLACE INTO Imports (filepath, name, address, " - "type) VALUES (?, ?, ?, ?)", + "type, calling_convention) VALUES (?, ?, ?, ?, ?)", &insert_imports_stmt, "Failed to prepare insert imports statement"); prepareStatement("DELETE FROM Globals WHERE filepath = ?", @@ -80,8 +80,8 @@ DatabaseManager::DatabaseManager(const std::string &db_path) : db(nullptr) { } const char *create_tables = R"( - CREATE TABLE IF NOT EXISTS Functions (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, PRIMARY KEY (name, filepath)); - CREATE TABLE IF NOT EXISTS Imports (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, PRIMARY KEY (name, filepath)); + CREATE TABLE IF NOT EXISTS Functions (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, PRIMARY KEY (name, filepath)); + CREATE TABLE IF NOT EXISTS Imports (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, PRIMARY KEY (name, filepath)); CREATE TABLE IF NOT EXISTS Globals (filepath TEXT, name TEXT, address TEXT); )"; @@ -118,6 +118,7 @@ void DatabaseManager::insertFunction(const FunctionInfo &func) { sqlite3_bind_text(stmt, 2, func.name.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 3, func.address.c_str(), -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 4, static_cast(func.type)); + sqlite3_bind_int(stmt, 5, static_cast(func.calling_convention)); sqlite3_step(stmt); } @@ -240,7 +241,7 @@ std::vector DatabaseManager::getFunctionsByType(FileType type) { std::vector functions; const char *sql = R"( - SELECT name, address, filepath + SELECT name, address, filepath, calling_convention FROM Functions WHERE type = ? AND address != '' ORDER BY address; @@ -259,6 +260,7 @@ std::vector DatabaseManager::getFunctionsByType(FileType type) { func.name = (const char *)sqlite3_column_text(stmt, 0); func.address = (const char *)sqlite3_column_text(stmt, 1); func.filepath = (const char *)sqlite3_column_text(stmt, 2); + func.calling_convention = static_cast(sqlite3_column_int(stmt, 3)); func.type = type; func.is_import = false; // Functions table contains non-imports diff --git a/tooling/files.sh b/tooling/files.sh index 34dee96c..78b10a0d 100644 --- a/tooling/files.sh +++ b/tooling/files.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -tool=build/clang-x86_64-pc-windows-msvc/Release/r3_gh_tool -cmake --build build/clang-x86_64-pc-windows-msvc/Release --target r3_gh_tool +tool=build/clang-x86_64-pc-windows-msvc/Release/gh_tool +cmake --build build/clang-x86_64-pc-windows-msvc/Release --target gh_tool types=(auto ref fix stub) for type in "${types[@]}"; do @@ -12,9 +12,9 @@ for type in "${types[@]}"; do for file in "tmps/gh_${type}"/*.cxx; do echo "$file" >>"$file_list" done - $tool "@$file_list" -v --type=$type --log-file=log-functions.txt + $tool -v --log-file=log-functions.txt functions "@$file_list" --type=$type fi done -$tool tmps/gh_global.h -mglobals -v --log-file=log-globals.txt -$tool -mduplicates -v --log-file=log-duplicates.txt +$tool -v --log-file=log-globals.txt globals tmps/gh_global.h +$tool -v --log-file=log-duplicates.txt verify diff --git a/tooling/parser.cpp b/tooling/parser.cpp index 01d27d2c..e8c19045 100644 --- a/tooling/parser.cpp +++ b/tooling/parser.cpp @@ -209,6 +209,47 @@ bool hasFunctionBody(TSNode node) { return false; } +// Add this helper function to detect calling convention from a function declaration +CallingConvention getCallingConvention(TSNode node, const char *source_code) { + uint32_t child_count = ts_node_child_count(node); + + for (uint32_t i = 0; i < child_count; i++) { + TSNode child = ts_node_child(node, i); + const char *type = ts_node_type(child); + + // Look for identifiers that might be calling conventions + if (strcmp(type, "identifier") == 0) { + std::string text = extractNodeText(child, source_code); + if (text == "__fastcall" || text == "__FASTCALL") { + return CallingConvention::Fastcall; + } else if (text == "__stdcall" || text == "__STDCALL") { + return CallingConvention::Stdcall; + } else if (text == "__cdecl" || text == "__CDECL") { + return CallingConvention::Cdecl; + } + } + // Also check in type specifiers and declaration specifiers + else if (strcmp(type, "type_specifier") == 0 || + strcmp(type, "declaration_specifier") == 0 || + strcmp(type, "specifier_qualifier_list") == 0) { + CallingConvention conv = getCallingConvention(child, source_code); + if (conv != CallingConvention::Cdecl) { + return conv; + } + } + // Check in declarators as well (calling convention can appear in various positions) + else if (strcmp(type, "function_declarator") == 0 || + strcmp(type, "pointer_declarator") == 0) { + CallingConvention conv = getCallingConvention(child, source_code); + if (conv != CallingConvention::Cdecl) { + return conv; + } + } + } + + return CallingConvention::Cdecl; // Default +} + void findFunctions(TSNode node, const char *source_code, uint32_t source_length, std::vector &functions, FileType file_type) { const char *type = ts_node_type(node); @@ -226,11 +267,19 @@ void findFunctions(TSNode node, const char *source_code, uint32_t source_length, } if (!address.empty()) { + // Detect calling convention + CallingConvention calling_conv = getCallingConvention(node, source_code); + FunctionInfo func{func_name, address, "", strcmp(type, "function_definition") == 0 ? !hasFunctionBody(node) : true, file_type}; // Add file_type parameter + func.calling_convention = calling_conv; // Set the calling convention + + spdlog::debug("Found function: {} at {} with calling convention: {}", + func_name, address, callingConventionToString(calling_conv)); + functions.push_back(func); } // We'll never nest function declarations diff --git a/tooling/tool.cpp b/tooling/tool.cpp index cd55e0c7..269b6ab7 100644 --- a/tooling/tool.cpp +++ b/tooling/tool.cpp @@ -1,5 +1,6 @@ #include "tool.hpp" #include +#include #include #include #include @@ -45,3 +46,28 @@ int main(int argc, char *argv[]) { return 0; } + +CallingConvention stringToCallingConvention(const std::string &conv_str) { + std::string lower_conv = conv_str; + std::transform(lower_conv.begin(), lower_conv.end(), lower_conv.begin(), ::tolower); + + if (lower_conv == "fastcall" || lower_conv == "__fastcall") { + return CallingConvention::Fastcall; + } else if (lower_conv == "stdcall" || lower_conv == "__stdcall") { + return CallingConvention::Stdcall; + } else { + return CallingConvention::Cdecl; // Default + } +} + +std::string callingConventionToString(CallingConvention conv) { + switch (conv) { + case CallingConvention::Fastcall: + return "fastcall"; + case CallingConvention::Stdcall: + return "stdcall"; + case CallingConvention::Cdecl: + default: + return "cdecl"; + } +} diff --git a/tooling/tool.hpp b/tooling/tool.hpp index 856e3a71..d0552a90 100644 --- a/tooling/tool.hpp +++ b/tooling/tool.hpp @@ -14,6 +14,9 @@ extern const std::regex ADDRESS_REGEX; // Enums enum class FileType { Auto, Fix, Stub, Ref }; +// Add calling convention enum +enum class CallingConvention { Cdecl, Stdcall, Fastcall }; + // Data structures struct FunctionInfo { std::string name; @@ -21,6 +24,7 @@ struct FunctionInfo { std::string filepath; bool is_import; FileType type; + CallingConvention calling_convention = CallingConvention::Cdecl; // Default to cdecl }; struct GlobalInfo { @@ -35,6 +39,10 @@ std::string fileTypeToString(FileType type); bool hasAddressPattern(const std::string &comment); std::string extractAddress(const std::string &comment); +// Add utility functions for calling convention +CallingConvention stringToCallingConvention(const std::string &conv_str); +std::string callingConventionToString(CallingConvention conv); + // Tree-sitter parsing functions std::string extractNodeText(TSNode node, const char *source_code); std::string findIdentifierInNode(TSNode node, const char *source_code);