Skip to content

Commit 3b83d7a

Browse files
author
sourcehold
committed
improve function decompilation and mcp tooling to search src folder
1 parent 3fd66d1 commit 3b83d7a

3 files changed

Lines changed: 397 additions & 238 deletions

File tree

tools/mcp/decomphelper.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import subprocess
2121
import tempfile
2222
from pathlib import Path
23-
from typing import Any, Sequence
23+
from typing import Any, List, Sequence
2424
import difflib
2525
import re
2626
import sys
@@ -119,9 +119,10 @@ def function_name_to_cpp_path(function_name: str, base_path = Path("src")) -> tu
119119
def compile_cpp_code_for_function(function_name: str, contents: str) -> tuple[bool, str, str]:
120120
"""
121121
Write and compile cpp code for function identified by fully namespaced function name.
122+
Includes the generated cpp file automatically in the cmake sources list.
122123
123124
Args:
124-
function_name: Name of the function to extract, fully namespaced using '::'
125+
function_name: Name of the function to compile, fully namespaced using '::'
125126
contents: New contents of the file
126127
127128
Returns:
@@ -149,6 +150,45 @@ def compile_cpp_code_for_function(function_name: str, contents: str) -> tuple[bo
149150
# Compile the project and return the resulting state
150151
return compile_project()
151152

153+
@mcp.tool()
154+
def exclude_function_cpp_code_from_compilation(function_name: str) -> tuple[bool, str, str]:
155+
"""
156+
Excludes the generated cpp file from the cmake sources list, avoiding its compilation.
157+
158+
Args:
159+
function_name: Name of the function to exclude, fully namespaced using '::'
160+
161+
Returns:
162+
Tuple of (success, stdout, stderr)
163+
"""
164+
# Translate the function name into a path
165+
rstate, rresult, rerr = function_name_to_cpp_path(function_name=function_name)
166+
if not rstate:
167+
return rstate, "", f"could not resolve function name to file path: {rerr}"
168+
path = Path(rresult)
169+
if not path.exists():
170+
return False, "", f"could not resolve function name to file path, file does not exist: {rerr}"
171+
172+
# Ensure the cpp file is included in the build
173+
csentry = str(path).replace("\\", "/")
174+
if not csentry.startswith("src/"):
175+
return False, "", f"invalid cmake/openshc-sources.txt entry: {csentry}"
176+
177+
# Store a backup
178+
i = 1
179+
pcandidate = PATH_CMAKE_OPENSHC_SOURCES.with_name(PATH_CMAKE_OPENSHC_SOURCES.name + f".{i:{0}>3}")
180+
while pcandidate.exists():
181+
i += 1
182+
pcandidate = PATH_CMAKE_OPENSHC_SOURCES.with_name(PATH_CMAKE_OPENSHC_SOURCES.name + f".{i:{0}>3}")
183+
pcandidate.write_text(PATH_CMAKE_OPENSHC_SOURCES.read_text())
184+
185+
# Produce the new file without the line
186+
lines = PATH_CMAKE_OPENSHC_SOURCES.read_text().splitlines(False)
187+
lines = [line for line in lines if not line.startswith(csentry)]
188+
PATH_CMAKE_OPENSHC_SOURCES.write_text('\n'.join(lines) + '\n', newline='\n')
189+
190+
return True, "cpp file excluded", ""
191+
152192
def read_function(function_name: str, base_path: Path = Path("src")):
153193
rstate, rresult, rerr = function_name_to_cpp_path(function_name=function_name, base_path=base_path)
154194
if not rstate:
@@ -232,5 +272,25 @@ def fetch_cached_ghidra_function_decompilation(function_name: str) -> tuple[bool
232272
"""
233273
return read_function(function_name=function_name, base_path=Path("tools") / "mcp" / "ghidra_scripts" / "decompilation")
234274

275+
@mcp.tool()
276+
def find_source_file_containing_text(text: str, glob: str = "**/*") -> List[str]:
277+
"""
278+
Returns a list of files in which 'text' can be found. The default file glob pattern is '**/*'.
279+
280+
Args:
281+
text: the text to find, no regex is supported
282+
glob: the glob to use, useful to look in hpp or cpp files specifically
283+
284+
Returns:
285+
list of file path strings
286+
"""
287+
results: List[str] = []
288+
src = Path("src")
289+
for f in src.glob(pattern=glob):
290+
if f.is_file():
291+
if text in f.read_text(encoding='UTF-8'):
292+
results.append(str(f.relative_to(src)))
293+
return results
294+
235295
if __name__ == "__main__":
236296
mcp.run(transport="stdio")

0 commit comments

Comments
 (0)