Skip to content

Commit 9d31584

Browse files
committed
Use simpler parser logic.
1 parent 93f086a commit 9d31584

File tree

9 files changed

+637
-247
lines changed

9 files changed

+637
-247
lines changed

ReClass.NET/AddressParser/DynamicCompiler.cs

Lines changed: 76 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,91 +9,104 @@ namespace ReClassNET.AddressParser
99
{
1010
public class DynamicCompiler : IExecuter
1111
{
12-
public IntPtr Execute(IOperation operation, RemoteProcess process)
12+
public IntPtr Execute(IExpression operation, RemoteProcess process)
1313
{
1414
Contract.Requires(operation != null);
1515
Contract.Requires(process != null);
1616

1717
return CompileAddressFormula(operation)(process);
1818
}
1919

20-
public Func<RemoteProcess, IntPtr> CompileAddressFormula(IOperation operation)
20+
public static Func<RemoteProcess, IntPtr> CompileAddressFormula(IExpression expression)
2121
{
22-
Contract.Requires(operation != null);
22+
Contract.Requires(expression != null);
2323

2424
var processParameter = Expression.Parameter(typeof(RemoteProcess));
2525

2626
return Expression.Lambda<Func<RemoteProcess, IntPtr>>(
27-
GenerateMethodBody(operation, processParameter),
27+
GenerateMethodBody(expression, processParameter),
2828
processParameter
2929
).Compile();
3030
}
3131

32-
private Expression GenerateMethodBody(IOperation operation, ParameterExpression processParameter)
32+
private static Expression GenerateMethodBody(IExpression operation, ParameterExpression processParameter)
3333
{
3434
Contract.Requires(operation != null);
3535
Contract.Requires(processParameter != null);
3636

3737
switch (operation)
3838
{
39-
case AdditionOperation additionOperation:
40-
{
41-
var argument1 = GenerateMethodBody(additionOperation.Argument1, processParameter);
42-
var argument2 = GenerateMethodBody(additionOperation.Argument2, processParameter);
43-
44-
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Add)), argument1, argument2);
45-
}
46-
case SubtractionOperation subtractionOperation:
47-
{
48-
var argument1 = GenerateMethodBody(subtractionOperation.Argument1, processParameter);
49-
var argument2 = GenerateMethodBody(subtractionOperation.Argument2, processParameter);
50-
51-
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Sub)), argument1, argument2);
52-
}
53-
case MultiplicationOperation multiplicationOperation:
54-
{
55-
var argument1 = GenerateMethodBody(multiplicationOperation.Argument1, processParameter);
56-
var argument2 = GenerateMethodBody(multiplicationOperation.Argument2, processParameter);
57-
58-
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Mul)), argument1, argument2);
59-
}
60-
case DivisionOperation divisionOperation:
61-
{
62-
var argument1 = GenerateMethodBody(divisionOperation.Dividend, processParameter);
63-
var argument2 = GenerateMethodBody(divisionOperation.Divisor, processParameter);
64-
65-
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Div)), argument1, argument2);
66-
}
67-
case ModuleOffsetOperation moduleOffsetOperation:
68-
{
69-
var getModuleByNameFunc = typeof(RemoteProcess).GetRuntimeMethod(nameof(RemoteProcess.GetModuleByName), new[] { typeof(string) });
70-
var moduleNameConstant = Expression.Constant(moduleOffsetOperation.Name);
71-
72-
var moduleVariable = Expression.Variable(typeof(Memory.Module));
73-
var assignExpression = Expression.Assign(moduleVariable, Expression.Call(processParameter, getModuleByNameFunc, moduleNameConstant));
74-
75-
return Expression.Block(
76-
new[] { moduleVariable },
77-
assignExpression,
78-
Expression.Condition(
79-
Expression.Equal(moduleVariable, Expression.Constant(null)),
80-
Expression.Constant(IntPtr.Zero),
81-
Expression.MakeMemberAccess(moduleVariable, typeof(Memory.Module).GetProperty(nameof(Memory.Module.Start)))
82-
)
83-
);
84-
}
85-
case OffsetOperation offsetOperation:
86-
{
87-
return Expression.Constant(offsetOperation.Value);
88-
}
89-
case ReadPointerOperation readPointerOperation:
90-
{
91-
var argument = GenerateMethodBody(readPointerOperation.Argument, processParameter);
92-
93-
var readRemoteIntPtrFunc = typeof(RemoteProcess).GetRuntimeMethod(nameof(RemoteProcess.ReadRemoteIntPtr), new[] { typeof(IntPtr) });
94-
95-
return Expression.Call(processParameter, readRemoteIntPtrFunc, argument);
96-
}
39+
case AddExpression addExpression:
40+
{
41+
var argument1 = GenerateMethodBody(addExpression.Lhs, processParameter);
42+
var argument2 = GenerateMethodBody(addExpression.Rhs, processParameter);
43+
44+
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Add)), argument1, argument2);
45+
}
46+
case SubtractExpression subtractExpression:
47+
{
48+
var argument1 = GenerateMethodBody(subtractExpression.Lhs, processParameter);
49+
var argument2 = GenerateMethodBody(subtractExpression.Rhs, processParameter);
50+
51+
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Sub)), argument1, argument2);
52+
}
53+
case MultiplyExpression multiplyExpression:
54+
{
55+
var argument1 = GenerateMethodBody(multiplyExpression.Lhs, processParameter);
56+
var argument2 = GenerateMethodBody(multiplyExpression.Rhs, processParameter);
57+
58+
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Mul)), argument1, argument2);
59+
}
60+
case DivideExpression divideExpression:
61+
{
62+
var argument1 = GenerateMethodBody(divideExpression.Lhs, processParameter);
63+
var argument2 = GenerateMethodBody(divideExpression.Rhs, processParameter);
64+
65+
return Expression.Call(null, GetIntPtrExtension(nameof(IntPtrExtension.Div)), argument1, argument2);
66+
}
67+
case ModuleExpression moduleExpression:
68+
{
69+
var getModuleByNameFunc = typeof(RemoteProcess).GetRuntimeMethod(nameof(RemoteProcess.GetModuleByName), new[] { typeof(string) });
70+
var moduleNameConstant = Expression.Constant(moduleExpression.Name);
71+
72+
var moduleVariable = Expression.Variable(typeof(Memory.Module));
73+
var assignExpression = Expression.Assign(moduleVariable, Expression.Call(processParameter, getModuleByNameFunc, moduleNameConstant));
74+
75+
return Expression.Block(
76+
new[] { moduleVariable },
77+
assignExpression,
78+
Expression.Condition(
79+
Expression.Equal(moduleVariable, Expression.Constant(null)),
80+
Expression.Constant(IntPtr.Zero),
81+
Expression.MakeMemberAccess(moduleVariable, typeof(Memory.Module).GetProperty(nameof(Memory.Module.Start)))
82+
)
83+
);
84+
}
85+
case ConstantExpression constantExpression:
86+
{
87+
#if RECLASSNET64
88+
// long -> IntPtr
89+
return Expression.Convert(Expression.Constant(nodeNumber.Value), typeof(IntPtr));
90+
#else
91+
// long -> int -> IntPtr
92+
return Expression.Convert(Expression.Convert(Expression.Constant(constantExpression.Value), typeof(int)), typeof(IntPtr));
93+
#endif
94+
}
95+
case ReadMemoryExpression readMemoryExpression:
96+
{
97+
var argument = GenerateMethodBody(readMemoryExpression.Expression, processParameter);
98+
99+
var functionName = readMemoryExpression.ByteCount == 4 ? nameof(RemoteProcess.ReadRemoteInt32) : nameof(RemoteProcess.ReadRemoteInt64);
100+
var readRemoteIntFn = typeof(RemoteProcess).GetRuntimeMethod(functionName, new[] { typeof(IntPtr) });
101+
102+
var callExpression = Expression.Call(processParameter, readRemoteIntFn, argument);
103+
104+
#if RECLASSNET64
105+
return Expression.Convert(callExpression, typeof(IntPtr));
106+
#else
107+
return Expression.Convert(Expression.Convert(callExpression, typeof(int)), typeof(IntPtr));
108+
#endif
109+
}
97110
}
98111

99112
throw new ArgumentException($"Unsupported operation '{operation.GetType().FullName}'.");
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace ReClassNET.AddressParser
5+
{
6+
public class ExpressionParser
7+
{
8+
private readonly Tokenizer tokenizer;
9+
10+
public ExpressionParser(Tokenizer tokenizer)
11+
{
12+
this.tokenizer = tokenizer;
13+
}
14+
15+
public IExpression ParseExpression()
16+
{
17+
var expr = ParseAddSubtract();
18+
19+
if (tokenizer.Token != Token.None)
20+
{
21+
throw new ParseException("Unexpected characters at end of expression");
22+
}
23+
24+
return expr;
25+
}
26+
27+
private IExpression ParseAddSubtract()
28+
{
29+
var lhs = ParseMultiplyDivide();
30+
31+
while (true)
32+
{
33+
if (tokenizer.Token == Token.Add || tokenizer.Token == Token.Subtract)
34+
{
35+
tokenizer.ReadNextToken();
36+
37+
var rhs = ParseMultiplyDivide();
38+
39+
if (tokenizer.Token == Token.Add)
40+
{
41+
lhs = new AddExpression(lhs, rhs);
42+
}
43+
else
44+
{
45+
lhs = new SubtractExpression(lhs, rhs);
46+
}
47+
}
48+
else
49+
{
50+
return lhs;
51+
}
52+
}
53+
}
54+
55+
private IExpression ParseMultiplyDivide()
56+
{
57+
var lhs = ParseUnary();
58+
59+
while (true)
60+
{
61+
if (tokenizer.Token == Token.Multiply || tokenizer.Token == Token.Divide)
62+
{
63+
tokenizer.ReadNextToken();
64+
65+
var rhs = ParseUnary();
66+
67+
if (tokenizer.Token == Token.Multiply)
68+
{
69+
lhs = new MultiplyExpression(lhs, rhs);
70+
}
71+
else
72+
{
73+
lhs = new DivideExpression(lhs, rhs);
74+
}
75+
}
76+
else
77+
{
78+
return lhs;
79+
}
80+
}
81+
}
82+
83+
private IExpression ParseUnary()
84+
{
85+
while (true)
86+
{
87+
if (tokenizer.Token == Token.Add)
88+
{
89+
tokenizer.ReadNextToken();
90+
91+
continue;
92+
}
93+
94+
if (tokenizer.Token == Token.Subtract)
95+
{
96+
tokenizer.ReadNextToken();
97+
98+
var rhs = ParseUnary();
99+
100+
return new NegateExpression(rhs);
101+
}
102+
103+
return ParseLeaf();
104+
}
105+
}
106+
107+
private IExpression ParseLeaf()
108+
{
109+
switch (tokenizer.Token)
110+
{
111+
case Token.Number:
112+
{
113+
var node = new ConstantExpression(tokenizer.Number);
114+
115+
tokenizer.ReadNextToken();
116+
117+
return node;
118+
}
119+
case Token.OpenParenthesis:
120+
{
121+
tokenizer.ReadNextToken();
122+
123+
var node = ParseAddSubtract();
124+
125+
if (tokenizer.Token != Token.CloseParenthesis)
126+
{
127+
throw new ParseException("Missing close parenthesis");
128+
}
129+
130+
tokenizer.ReadNextToken();
131+
132+
return node;
133+
}
134+
case Token.OpenBrackets:
135+
{
136+
tokenizer.ReadNextToken();
137+
138+
var node = ParseAddSubtract();
139+
140+
var byteCount = IntPtr.Size;
141+
if (tokenizer.Token == Token.Comma)
142+
{
143+
tokenizer.ReadNextToken();
144+
145+
if (tokenizer.Token != Token.Number)
146+
{
147+
throw new ParseException("Missing read byte count");
148+
}
149+
150+
byteCount = (int)tokenizer.Number;
151+
152+
tokenizer.ReadNextToken();
153+
}
154+
155+
if (tokenizer.Token != Token.CloseBrackets)
156+
{
157+
throw new ParseException("Missing close bracket");
158+
}
159+
160+
tokenizer.ReadNextToken();
161+
162+
return new ReadMemoryExpression(node, byteCount);
163+
}
164+
case Token.Identifier:
165+
{
166+
var node = new ModuleExpression(tokenizer.Identifier);
167+
168+
tokenizer.ReadNextToken();
169+
170+
return node;
171+
}
172+
default:
173+
throw new ParseException($"Unexpect token: {tokenizer.Token}");
174+
}
175+
}
176+
177+
#region Convenience Helpers
178+
179+
public static IExpression Parse(string str)
180+
{
181+
using (var sr = new StringReader(str))
182+
{
183+
return Parse(new Tokenizer(sr));
184+
}
185+
}
186+
187+
public static IExpression Parse(Tokenizer tokenizer)
188+
{
189+
var parser = new ExpressionParser(tokenizer);
190+
return parser.ParseExpression();
191+
}
192+
193+
#endregion
194+
}
195+
}

0 commit comments

Comments
 (0)