iflow-mcp_bethington-cheat-engine-server-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/METADATA +16 -0
- iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/RECORD +40 -0
- iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/WHEEL +5 -0
- iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/top_level.txt +1 -0
- server/cheatengine/__init__.py +19 -0
- server/cheatengine/ce_bridge.py +1670 -0
- server/cheatengine/lua_interface.py +460 -0
- server/cheatengine/table_parser.py +1221 -0
- server/config/__init__.py +20 -0
- server/config/settings.py +347 -0
- server/config/whitelist.py +378 -0
- server/gui_automation/__init__.py +43 -0
- server/gui_automation/core/__init__.py +8 -0
- server/gui_automation/core/integration.py +951 -0
- server/gui_automation/demos/__init__.py +8 -0
- server/gui_automation/demos/basic_demo.py +754 -0
- server/gui_automation/demos/notepad_demo.py +460 -0
- server/gui_automation/demos/simple_demo.py +319 -0
- server/gui_automation/tools/__init__.py +8 -0
- server/gui_automation/tools/mcp_tools.py +974 -0
- server/main.py +519 -0
- server/memory/__init__.py +0 -0
- server/memory/analyzer.py +0 -0
- server/memory/reader.py +0 -0
- server/memory/scanner.py +0 -0
- server/memory/symbols.py +0 -0
- server/process/__init__.py +16 -0
- server/process/launcher.py +608 -0
- server/process/manager.py +185 -0
- server/process/monitors.py +202 -0
- server/process/permissions.py +131 -0
- server/process_whitelist.json +119 -0
- server/pyautogui/__init__.py +0 -0
- server/utils/__init__.py +37 -0
- server/utils/data_types.py +368 -0
- server/utils/formatters.py +430 -0
- server/utils/validators.py +340 -0
- server/window_automation/__init__.py +59 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lua Interface Module
|
|
3
|
+
Handles Cheat Engine Lua script integration and execution
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import re
|
|
8
|
+
import subprocess
|
|
9
|
+
import tempfile
|
|
10
|
+
import os
|
|
11
|
+
from typing import Dict, List, Optional, Any, Tuple
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class LuaScript:
|
|
18
|
+
"""Represents a Lua script from Cheat Engine"""
|
|
19
|
+
name: str
|
|
20
|
+
content: str
|
|
21
|
+
variables: List[str]
|
|
22
|
+
functions: List[str]
|
|
23
|
+
dependencies: List[str]
|
|
24
|
+
safe_to_execute: bool = False
|
|
25
|
+
|
|
26
|
+
class LuaInterface:
|
|
27
|
+
"""Interface for Cheat Engine Lua script analysis and limited execution"""
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self.lua_available = self._check_lua_availability()
|
|
31
|
+
self.safe_functions = self._get_safe_functions()
|
|
32
|
+
self.dangerous_patterns = self._get_dangerous_patterns()
|
|
33
|
+
|
|
34
|
+
def _check_lua_availability(self) -> bool:
|
|
35
|
+
"""Check if Lua interpreter is available"""
|
|
36
|
+
try:
|
|
37
|
+
result = subprocess.run(['lua', '-v'], capture_output=True, text=True, timeout=5)
|
|
38
|
+
return result.returncode == 0
|
|
39
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
40
|
+
logger.info("Lua interpreter not available - script execution disabled")
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
def _get_safe_functions(self) -> set:
|
|
44
|
+
"""Get list of safe Lua functions that can be executed"""
|
|
45
|
+
return {
|
|
46
|
+
# Basic Lua functions
|
|
47
|
+
'print', 'type', 'tostring', 'tonumber', 'pairs', 'ipairs',
|
|
48
|
+
'next', 'select', 'unpack', 'rawget', 'rawset', 'rawlen',
|
|
49
|
+
'getmetatable', 'setmetatable',
|
|
50
|
+
|
|
51
|
+
# Math functions
|
|
52
|
+
'math.abs', 'math.acos', 'math.asin', 'math.atan', 'math.atan2',
|
|
53
|
+
'math.ceil', 'math.cos', 'math.cosh', 'math.deg', 'math.exp',
|
|
54
|
+
'math.floor', 'math.fmod', 'math.frexp', 'math.huge', 'math.ldexp',
|
|
55
|
+
'math.log', 'math.log10', 'math.max', 'math.min', 'math.modf',
|
|
56
|
+
'math.pi', 'math.pow', 'math.rad', 'math.random', 'math.randomseed',
|
|
57
|
+
'math.sin', 'math.sinh', 'math.sqrt', 'math.tan', 'math.tanh',
|
|
58
|
+
|
|
59
|
+
# String functions
|
|
60
|
+
'string.byte', 'string.char', 'string.dump', 'string.find',
|
|
61
|
+
'string.format', 'string.gmatch', 'string.gsub', 'string.len',
|
|
62
|
+
'string.lower', 'string.match', 'string.rep', 'string.reverse',
|
|
63
|
+
'string.sub', 'string.upper',
|
|
64
|
+
|
|
65
|
+
# Table functions
|
|
66
|
+
'table.concat', 'table.insert', 'table.maxn', 'table.remove',
|
|
67
|
+
'table.sort', 'table.unpack'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def _get_dangerous_patterns(self) -> List[str]:
|
|
71
|
+
"""Get patterns that indicate dangerous operations"""
|
|
72
|
+
return [
|
|
73
|
+
# File operations
|
|
74
|
+
r'\bio\b', r'\bfile\b', r'dofile', r'loadfile',
|
|
75
|
+
|
|
76
|
+
# Process/system operations
|
|
77
|
+
r'\bos\.\w+', r'\bexecute\b', r'\bsystem\b',
|
|
78
|
+
|
|
79
|
+
# Loading/requiring modules
|
|
80
|
+
r'\brequire\b', r'\bloadstring\b', r'\bload\b',
|
|
81
|
+
|
|
82
|
+
# Memory operations (CE specific)
|
|
83
|
+
r'\breadBytes\b', r'\bwriteBytes\b', r'\bgetAddress\b',
|
|
84
|
+
r'\bgetAddressList\b', r'\bmemoryrecord\b',
|
|
85
|
+
|
|
86
|
+
# Process manipulation
|
|
87
|
+
r'\bopenProcess\b', r'\bcloseHandle\b', r'\bVirtualAlloc\b',
|
|
88
|
+
|
|
89
|
+
# Registry operations
|
|
90
|
+
r'\bregistry\b', r'\breg_\w+',
|
|
91
|
+
|
|
92
|
+
# Network operations
|
|
93
|
+
r'\bsocket\b', r'\bhttp\b', r'\bftp\b',
|
|
94
|
+
|
|
95
|
+
# DLL operations
|
|
96
|
+
r'\bloadLibrary\b', r'\bgetProcAddress\b', r'\bcall\b'
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
def analyze_script(self, script_content: str, script_name: str = "unknown") -> LuaScript:
|
|
100
|
+
"""Analyze a Lua script for safety and extract information
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
script_content: The Lua script content
|
|
104
|
+
script_name: Name of the script
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
LuaScript object with analysis results
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
# Extract variables
|
|
111
|
+
variables = self._extract_variables(script_content)
|
|
112
|
+
|
|
113
|
+
# Extract functions
|
|
114
|
+
functions = self._extract_functions(script_content)
|
|
115
|
+
|
|
116
|
+
# Extract dependencies/requires
|
|
117
|
+
dependencies = self._extract_dependencies(script_content)
|
|
118
|
+
|
|
119
|
+
# Check safety
|
|
120
|
+
safe_to_execute = self._check_script_safety(script_content)
|
|
121
|
+
|
|
122
|
+
return LuaScript(
|
|
123
|
+
name=script_name,
|
|
124
|
+
content=script_content,
|
|
125
|
+
variables=variables,
|
|
126
|
+
functions=functions,
|
|
127
|
+
dependencies=dependencies,
|
|
128
|
+
safe_to_execute=safe_to_execute
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.error(f"Error analyzing script {script_name}: {e}")
|
|
133
|
+
return LuaScript(
|
|
134
|
+
name=script_name,
|
|
135
|
+
content=script_content,
|
|
136
|
+
variables=[],
|
|
137
|
+
functions=[],
|
|
138
|
+
dependencies=[],
|
|
139
|
+
safe_to_execute=False
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def _extract_variables(self, script_content: str) -> List[str]:
|
|
143
|
+
"""Extract variable declarations from script"""
|
|
144
|
+
variables = []
|
|
145
|
+
|
|
146
|
+
# Look for local variable declarations
|
|
147
|
+
local_pattern = r'\blocal\s+([a-zA-Z_]\w*)'
|
|
148
|
+
matches = re.finditer(local_pattern, script_content)
|
|
149
|
+
for match in matches:
|
|
150
|
+
variables.append(match.group(1))
|
|
151
|
+
|
|
152
|
+
# Look for global variable assignments
|
|
153
|
+
global_pattern = r'^([a-zA-Z_]\w*)\s*='
|
|
154
|
+
matches = re.finditer(global_pattern, script_content, re.MULTILINE)
|
|
155
|
+
for match in matches:
|
|
156
|
+
var_name = match.group(1)
|
|
157
|
+
if var_name not in ['if', 'for', 'while', 'function', 'local', 'return']:
|
|
158
|
+
variables.append(var_name)
|
|
159
|
+
|
|
160
|
+
return list(set(variables)) # Remove duplicates
|
|
161
|
+
|
|
162
|
+
def _extract_functions(self, script_content: str) -> List[str]:
|
|
163
|
+
"""Extract function definitions from script"""
|
|
164
|
+
functions = []
|
|
165
|
+
|
|
166
|
+
# Look for function definitions
|
|
167
|
+
function_pattern = r'\bfunction\s+([a-zA-Z_]\w*)\s*\('
|
|
168
|
+
matches = re.finditer(function_pattern, script_content)
|
|
169
|
+
for match in matches:
|
|
170
|
+
functions.append(match.group(1))
|
|
171
|
+
|
|
172
|
+
# Look for anonymous function assignments
|
|
173
|
+
anon_pattern = r'([a-zA-Z_]\w*)\s*=\s*function\s*\('
|
|
174
|
+
matches = re.finditer(anon_pattern, script_content)
|
|
175
|
+
for match in matches:
|
|
176
|
+
functions.append(match.group(1))
|
|
177
|
+
|
|
178
|
+
return functions
|
|
179
|
+
|
|
180
|
+
def _extract_dependencies(self, script_content: str) -> List[str]:
|
|
181
|
+
"""Extract require statements and dependencies"""
|
|
182
|
+
dependencies = []
|
|
183
|
+
|
|
184
|
+
# Look for require statements
|
|
185
|
+
require_pattern = r'\brequire\s*\(\s*["\']([^"\']+)["\']\s*\)'
|
|
186
|
+
matches = re.finditer(require_pattern, script_content)
|
|
187
|
+
for match in matches:
|
|
188
|
+
dependencies.append(match.group(1))
|
|
189
|
+
|
|
190
|
+
return dependencies
|
|
191
|
+
|
|
192
|
+
def _check_script_safety(self, script_content: str) -> bool:
|
|
193
|
+
"""Check if script is safe to execute"""
|
|
194
|
+
|
|
195
|
+
# Check for dangerous patterns
|
|
196
|
+
for pattern in self.dangerous_patterns:
|
|
197
|
+
if re.search(pattern, script_content, re.IGNORECASE):
|
|
198
|
+
logger.warning(f"Dangerous pattern found: {pattern}")
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
# Check for suspicious function calls
|
|
202
|
+
suspicious_calls = [
|
|
203
|
+
'os.execute', 'os.remove', 'os.rename', 'os.exit',
|
|
204
|
+
'io.open', 'io.close', 'io.read', 'io.write',
|
|
205
|
+
'debug.debug', 'debug.getfenv', 'debug.setfenv'
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
for call in suspicious_calls:
|
|
209
|
+
if call in script_content:
|
|
210
|
+
logger.warning(f"Suspicious function call found: {call}")
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
def execute_safe_script(self, script: LuaScript, context: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
216
|
+
"""Execute a safe Lua script in a sandboxed environment
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
script: LuaScript object to execute
|
|
220
|
+
context: Optional context variables to provide to script
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Dictionary with execution results
|
|
224
|
+
"""
|
|
225
|
+
if not self.lua_available:
|
|
226
|
+
return {
|
|
227
|
+
'success': False,
|
|
228
|
+
'error': 'Lua interpreter not available',
|
|
229
|
+
'output': '',
|
|
230
|
+
'variables': {}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if not script.safe_to_execute:
|
|
234
|
+
return {
|
|
235
|
+
'success': False,
|
|
236
|
+
'error': 'Script marked as unsafe for execution',
|
|
237
|
+
'output': '',
|
|
238
|
+
'variables': {}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
# Create sandboxed script
|
|
243
|
+
sandboxed_script = self._create_sandboxed_script(script.content, context)
|
|
244
|
+
|
|
245
|
+
# Execute in temporary file
|
|
246
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.lua', delete=False) as f:
|
|
247
|
+
f.write(sandboxed_script)
|
|
248
|
+
temp_file = f.name
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
# Execute with timeout
|
|
252
|
+
result = subprocess.run(
|
|
253
|
+
['lua', temp_file],
|
|
254
|
+
capture_output=True,
|
|
255
|
+
text=True,
|
|
256
|
+
timeout=10 # 10 second timeout
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
'success': result.returncode == 0,
|
|
261
|
+
'error': result.stderr if result.returncode != 0 else '',
|
|
262
|
+
'output': result.stdout,
|
|
263
|
+
'variables': self._extract_output_variables(result.stdout)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
finally:
|
|
267
|
+
# Clean up temp file
|
|
268
|
+
try:
|
|
269
|
+
os.unlink(temp_file)
|
|
270
|
+
except OSError:
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
except subprocess.TimeoutExpired:
|
|
274
|
+
return {
|
|
275
|
+
'success': False,
|
|
276
|
+
'error': 'Script execution timed out',
|
|
277
|
+
'output': '',
|
|
278
|
+
'variables': {}
|
|
279
|
+
}
|
|
280
|
+
except Exception as e:
|
|
281
|
+
return {
|
|
282
|
+
'success': False,
|
|
283
|
+
'error': f'Execution error: {e}',
|
|
284
|
+
'output': '',
|
|
285
|
+
'variables': {}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
def _create_sandboxed_script(self, script_content: str, context: Dict[str, Any] = None) -> str:
|
|
289
|
+
"""Create a sandboxed version of the script"""
|
|
290
|
+
|
|
291
|
+
sandbox_header = """
|
|
292
|
+
-- Sandboxed Lua execution environment
|
|
293
|
+
-- Disable dangerous functions
|
|
294
|
+
os = nil
|
|
295
|
+
io = nil
|
|
296
|
+
debug = nil
|
|
297
|
+
dofile = nil
|
|
298
|
+
loadfile = nil
|
|
299
|
+
loadstring = nil
|
|
300
|
+
load = nil
|
|
301
|
+
|
|
302
|
+
-- Provide safe context variables
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
# Add context variables if provided
|
|
306
|
+
context_vars = ""
|
|
307
|
+
if context:
|
|
308
|
+
for key, value in context.items():
|
|
309
|
+
if isinstance(value, str):
|
|
310
|
+
context_vars += f'{key} = "{value}"\n'
|
|
311
|
+
elif isinstance(value, (int, float)):
|
|
312
|
+
context_vars += f'{key} = {value}\n'
|
|
313
|
+
elif isinstance(value, bool):
|
|
314
|
+
context_vars += f'{key} = {str(value).lower()}\n'
|
|
315
|
+
|
|
316
|
+
return sandbox_header + context_vars + "\n-- Original script:\n" + script_content
|
|
317
|
+
|
|
318
|
+
def _extract_output_variables(self, output: str) -> Dict[str, Any]:
|
|
319
|
+
"""Extract variables from script output"""
|
|
320
|
+
variables = {}
|
|
321
|
+
|
|
322
|
+
# Look for variable = value patterns in output
|
|
323
|
+
var_pattern = r'^(\w+)\s*=\s*(.+)$'
|
|
324
|
+
|
|
325
|
+
for line in output.split('\n'):
|
|
326
|
+
match = re.match(var_pattern, line.strip())
|
|
327
|
+
if match:
|
|
328
|
+
var_name = match.group(1)
|
|
329
|
+
var_value = match.group(2).strip()
|
|
330
|
+
|
|
331
|
+
# Try to parse the value
|
|
332
|
+
try:
|
|
333
|
+
if var_value.startswith('"') and var_value.endswith('"'):
|
|
334
|
+
variables[var_name] = var_value[1:-1] # String
|
|
335
|
+
elif var_value in ['true', 'false']:
|
|
336
|
+
variables[var_name] = var_value == 'true' # Boolean
|
|
337
|
+
elif '.' in var_value:
|
|
338
|
+
variables[var_name] = float(var_value) # Float
|
|
339
|
+
else:
|
|
340
|
+
variables[var_name] = int(var_value) # Integer
|
|
341
|
+
except ValueError:
|
|
342
|
+
variables[var_name] = var_value # Keep as string
|
|
343
|
+
|
|
344
|
+
return variables
|
|
345
|
+
|
|
346
|
+
def convert_ce_script_to_mcp(self, script: LuaScript) -> Dict[str, Any]:
|
|
347
|
+
"""Convert a CE Lua script to MCP-compatible operations
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
script: LuaScript object to convert
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Dictionary with MCP-compatible operations
|
|
354
|
+
"""
|
|
355
|
+
operations = []
|
|
356
|
+
|
|
357
|
+
# Analyze script content for memory operations
|
|
358
|
+
memory_reads = self._extract_memory_reads(script.content)
|
|
359
|
+
memory_writes = self._extract_memory_writes(script.content)
|
|
360
|
+
address_calculations = self._extract_address_calculations(script.content)
|
|
361
|
+
|
|
362
|
+
# Convert to MCP operations
|
|
363
|
+
for read_op in memory_reads:
|
|
364
|
+
operations.append({
|
|
365
|
+
'type': 'read_memory',
|
|
366
|
+
'address': read_op.get('address'),
|
|
367
|
+
'size': read_op.get('size', 4),
|
|
368
|
+
'data_type': read_op.get('type', 'uint32')
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
# Note: Write operations are not included for safety
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
'script_name': script.name,
|
|
375
|
+
'safe_operations': operations,
|
|
376
|
+
'variables': script.variables,
|
|
377
|
+
'functions': script.functions,
|
|
378
|
+
'original_safe': script.safe_to_execute
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
def _extract_memory_reads(self, script_content: str) -> List[Dict[str, Any]]:
|
|
382
|
+
"""Extract memory read operations from script"""
|
|
383
|
+
reads = []
|
|
384
|
+
|
|
385
|
+
# Look for common CE memory read patterns
|
|
386
|
+
patterns = [
|
|
387
|
+
r'readBytes\(([^,]+),\s*(\d+)\)',
|
|
388
|
+
r'readInteger\(([^)]+)\)',
|
|
389
|
+
r'readFloat\(([^)]+)\)',
|
|
390
|
+
r'readString\(([^,]+),\s*(\d+)\)'
|
|
391
|
+
]
|
|
392
|
+
|
|
393
|
+
for pattern in patterns:
|
|
394
|
+
matches = re.finditer(pattern, script_content)
|
|
395
|
+
for match in matches:
|
|
396
|
+
reads.append({
|
|
397
|
+
'address': match.group(1).strip(),
|
|
398
|
+
'size': int(match.group(2)) if len(match.groups()) > 1 else 4,
|
|
399
|
+
'type': 'bytes' if 'Bytes' in pattern else 'integer'
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
return reads
|
|
403
|
+
|
|
404
|
+
def _extract_memory_writes(self, script_content: str) -> List[Dict[str, Any]]:
|
|
405
|
+
"""Extract memory write operations from script"""
|
|
406
|
+
writes = []
|
|
407
|
+
|
|
408
|
+
# Look for write operations (for analysis only - not executed)
|
|
409
|
+
patterns = [
|
|
410
|
+
r'writeBytes\(([^,]+),\s*([^)]+)\)',
|
|
411
|
+
r'writeInteger\(([^,]+),\s*([^)]+)\)',
|
|
412
|
+
r'writeFloat\(([^,]+),\s*([^)]+)\)'
|
|
413
|
+
]
|
|
414
|
+
|
|
415
|
+
for pattern in patterns:
|
|
416
|
+
matches = re.finditer(pattern, script_content)
|
|
417
|
+
for match in matches:
|
|
418
|
+
writes.append({
|
|
419
|
+
'address': match.group(1).strip(),
|
|
420
|
+
'value': match.group(2).strip(),
|
|
421
|
+
'type': 'bytes' if 'Bytes' in pattern else 'integer'
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
return writes
|
|
425
|
+
|
|
426
|
+
def _extract_address_calculations(self, script_content: str) -> List[Dict[str, Any]]:
|
|
427
|
+
"""Extract address calculation patterns"""
|
|
428
|
+
calculations = []
|
|
429
|
+
|
|
430
|
+
# Look for pointer arithmetic patterns
|
|
431
|
+
pointer_patterns = [
|
|
432
|
+
r'getAddress\(([^)]+)\)',
|
|
433
|
+
r'(\w+)\s*\+\s*0x([0-9A-Fa-f]+)',
|
|
434
|
+
r'\[\[([^\]]+)\]\s*\+\s*0x([0-9A-Fa-f]+)\]'
|
|
435
|
+
]
|
|
436
|
+
|
|
437
|
+
for pattern in pointer_patterns:
|
|
438
|
+
matches = re.finditer(pattern, script_content)
|
|
439
|
+
for match in matches:
|
|
440
|
+
calculations.append({
|
|
441
|
+
'expression': match.group(0),
|
|
442
|
+
'base': match.group(1) if len(match.groups()) > 0 else None,
|
|
443
|
+
'offset': match.group(2) if len(match.groups()) > 1 else None
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
return calculations
|
|
447
|
+
|
|
448
|
+
def get_script_summary(self, script: LuaScript) -> Dict[str, Any]:
|
|
449
|
+
"""Get a summary of the script analysis"""
|
|
450
|
+
return {
|
|
451
|
+
'name': script.name,
|
|
452
|
+
'safe_to_execute': script.safe_to_execute,
|
|
453
|
+
'variable_count': len(script.variables),
|
|
454
|
+
'function_count': len(script.functions),
|
|
455
|
+
'dependency_count': len(script.dependencies),
|
|
456
|
+
'content_length': len(script.content),
|
|
457
|
+
'variables': script.variables[:10], # First 10 variables
|
|
458
|
+
'functions': script.functions,
|
|
459
|
+
'dependencies': script.dependencies
|
|
460
|
+
}
|