Skip to content

Commit 33023f3

Browse files
committed
lazy hooking
1 parent 95feb33 commit 33023f3

File tree

5 files changed

+86
-65
lines changed

5 files changed

+86
-65
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ In order to update the camera transform, send a POST request to `127.0.0.1:6723`
3636

3737
In order to get the camera transform, send a GET request to `127.0.0.1:6723`. The response will be json in the same format as the above.
3838

39+
## Development
40+
41+
Run with `--noupdateconfig` to use a modified local config without fetching one from the internet.
42+
3943
## Sample Project
4044

4145
The repository includes a sample project that makes use of the API to move the camera. It is a .NET Core WPF project that interpolates the camera over a user defined path.

WriteAPI/GameInterface.cs

Lines changed: 80 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Globalization;
25
using System.Numerics;
6+
using System.Threading.Tasks;
37
using Memory;
48

59
namespace WriteAPI
610
{
711
public static class GameInterface
812
{
9-
private static string gameExe = "echovr.exe";
10-
private static string gameName = "Echo VR";
11-
1213
public static CameraTransform CameraTransform
1314
{
1415
get => GetTransform();
@@ -18,82 +19,97 @@ public static CameraTransform CameraTransform
1819
public static float LoneEchoSpeed => GetLoneEchoSpeed();
1920
public static float LoneEcho2Speed => GetLoneEcho2Speed();
2021

21-
private static readonly Mem echoVRMem = new Mem();
22-
private static readonly Mem loneEchoMem = new Mem();
23-
private static readonly Mem loneEcho2Mem = new Mem();
22+
private enum Game
23+
{
24+
EchoVR,
25+
LoneEcho,
26+
LoneEcho2
27+
}
2428

25-
public static void Hook()
29+
private static readonly Dictionary<Game, Mem> mems = new Dictionary<Game, Mem>()
2630
{
27-
gameName = Program.game switch
28-
{
29-
"echovr" => "Echo VR",
30-
"loneecho" => "Lone Echo",
31-
"loneecho2" => "Lone Echo 2",
32-
};
33-
gameExe = Program.game switch
34-
{
35-
"echovr" => "echovr.exe",
36-
"loneecho" => "loneecho.exe",
37-
"loneecho2" => "loneecho2.exe",
38-
};
39-
40-
Console.WriteLine($"Hooking {gameName}...");
31+
{ Game.EchoVR, new Mem() },
32+
{ Game.LoneEcho, new Mem() },
33+
{ Game.LoneEcho2, new Mem() },
34+
};
35+
36+
private static readonly Dictionary<Game, bool> hooked = new Dictionary<Game, bool>()
37+
{
38+
{ Game.EchoVR, false },
39+
{ Game.LoneEcho, false },
40+
{ Game.LoneEcho2, false },
41+
};
42+
43+
private static readonly Dictionary<Game, string> exes = new Dictionary<Game, string>()
44+
{
45+
{ Game.EchoVR, "echovr.exe" },
46+
{ Game.LoneEcho, "loneecho.exe" },
47+
{ Game.LoneEcho2, "loneecho2.exe" },
48+
};
49+
50+
private static void HookIfNotHooked(Game game)
51+
{
52+
if (!hooked[game]) Hook(game);
53+
}
4154

42-
bool hooked = false;
43-
while (true)
55+
private static void Hook(Game game)
56+
{
57+
bool hookedNow = mems[game].OpenProcess(exes[game]);
58+
if (hookedNow && !hooked[game])
4459
{
45-
if (!echoVRMem.OpenProcess(gameExe))
46-
{
47-
if (hooked)
48-
{
49-
Console.WriteLine($"Could not find {gameName} process, make sure {gameName} is running");
50-
}
51-
52-
hooked = false;
53-
}
54-
else
55-
{
56-
if (!hooked)
57-
{
58-
Console.WriteLine($"Found {gameName} Process");
59-
}
60-
hooked = true;
61-
}
62-
System.Threading.Thread.Sleep(1000);
60+
Console.WriteLine($"Found {game} Process");
6361
}
62+
63+
hooked[game] = hookedNow;
6464
}
6565

6666
private static void UpdateTransform(CameraTransform transform)
6767
{
68+
HookIfNotHooked(Game.EchoVR);
69+
6870
transform.rotation = Quaternion.Normalize(transform.rotation);
69-
WriteVector(echoVRMem, ConfigurationManager.config.cameraPositionAddress, transform.position);
70-
WriteQuaternion(echoVRMem, ConfigurationManager.config.cameraRotationAddress, transform.rotation);
71+
WriteVector(mems[Game.EchoVR], ConfigurationManager.config.cameraPositionAddress, transform.position);
72+
WriteQuaternion(mems[Game.EchoVR], ConfigurationManager.config.cameraRotationAddress, transform.rotation);
7173
#if DEBUG
7274
Console.WriteLine($"Wrote new camera transform: {transform}");
7375
#endif
76+
77+
// if we became unhooked, rehook for the next request
78+
Task.Run(() => { Hook(Game.EchoVR); });
7479
}
7580

76-
static CameraTransform GetTransform()
81+
private static CameraTransform GetTransform()
7782
{
78-
Vector3 position = ReadVector(echoVRMem, ConfigurationManager.config.cameraPositionAddress);
79-
Quaternion rotation = ReadQuaternion(echoVRMem, ConfigurationManager.config.cameraRotationAddress);
83+
HookIfNotHooked(Game.EchoVR);
84+
85+
Vector3 position = ReadVector(mems[Game.EchoVR], ConfigurationManager.config.cameraPositionAddress);
86+
Quaternion rotation = ReadQuaternion(mems[Game.EchoVR], ConfigurationManager.config.cameraRotationAddress);
87+
88+
// if we became unhooked, rehook for the next request
89+
Task.Run(() => { Hook(Game.EchoVR); });
90+
8091
return new CameraTransform(position, rotation);
8192
}
8293

8394
private static float GetLoneEchoSpeed()
8495
{
85-
if (loneEchoMem.OpenProcess("loneecho"))
96+
HookIfNotHooked(Game.LoneEcho);
97+
98+
if (mems[Game.LoneEcho].OpenProcess("loneecho"))
8699
{
87-
return loneEchoMem.ReadFloat(ConfigurationManager.config.loneEchoSpeedAddress, round: false);
100+
return mems[Game.LoneEcho].ReadFloat(ConfigurationManager.config.loneEchoSpeedAddress, round: false);
88101
}
89102

90103
return -1;
91104
}
105+
92106
private static float GetLoneEcho2Speed()
93107
{
94-
if (loneEcho2Mem.OpenProcess("loneecho2"))
108+
HookIfNotHooked(Game.LoneEcho2);
109+
110+
if (mems[Game.LoneEcho2].OpenProcess("loneecho2"))
95111
{
96-
return loneEcho2Mem.ReadFloat(ConfigurationManager.config.loneEcho2SpeedAddress, round: false);
112+
return mems[Game.LoneEcho2].ReadFloat(ConfigurationManager.config.loneEcho2SpeedAddress, round: false);
97113
}
98114

99115
return -1;
@@ -102,23 +118,26 @@ private static float GetLoneEcho2Speed()
102118

103119
private static string AddressWithOffset(string address, int offset)
104120
{
105-
int preOffset = Convert.ToInt32(address, 16);
106-
return address[..^3] + (preOffset + offset).ToString("X2");
121+
string baseString = address[..^3];
122+
string end = address[^2..];
123+
124+
int preOffset = Convert.ToInt32($"0x{end}", 16);
125+
return baseString + (preOffset + offset).ToString("X2");
107126
}
108-
127+
109128
private static void WriteVector(Mem mem, string address, Vector3 vector)
110129
{
111-
mem.WriteMemory(AddressWithOffset(address, 0), "float", vector.X.ToString());
112-
mem.WriteMemory(AddressWithOffset(address, 4), "float", vector.Y.ToString());
113-
mem.WriteMemory(AddressWithOffset(address, 8), "float", vector.Z.ToString());
130+
mem.WriteMemory(AddressWithOffset(address, 0), "float", vector.X.ToString(CultureInfo.InvariantCulture));
131+
mem.WriteMemory(AddressWithOffset(address, 4), "float", vector.Y.ToString(CultureInfo.InvariantCulture));
132+
mem.WriteMemory(AddressWithOffset(address, 8), "float", vector.Z.ToString(CultureInfo.InvariantCulture));
114133
}
115134

116135
private static void WriteQuaternion(Mem mem, string address, Quaternion quaternion)
117136
{
118-
mem.WriteMemory(AddressWithOffset(address, 0), "float", quaternion.X.ToString());
119-
mem.WriteMemory(AddressWithOffset(address, 4), "float", quaternion.Y.ToString());
120-
mem.WriteMemory(AddressWithOffset(address, 8), "float", quaternion.Z.ToString());
121-
mem.WriteMemory(AddressWithOffset(address, 12), "float", quaternion.W.ToString());
137+
mem.WriteMemory(AddressWithOffset(address, 0), "float", quaternion.X.ToString(CultureInfo.InvariantCulture));
138+
mem.WriteMemory(AddressWithOffset(address, 4), "float", quaternion.Y.ToString(CultureInfo.InvariantCulture));
139+
mem.WriteMemory(AddressWithOffset(address, 8), "float", quaternion.Z.ToString(CultureInfo.InvariantCulture));
140+
mem.WriteMemory(AddressWithOffset(address, 12), "float", quaternion.W.ToString(CultureInfo.InvariantCulture));
122141
}
123142

124143
private static Vector3 ReadVector(Mem mem, string address)
@@ -138,4 +157,4 @@ private static Quaternion ReadQuaternion(Mem mem, string address)
138157
return new Quaternion(x, y, z, w);
139158
}
140159
}
141-
}
160+
}

WriteAPI/Listener.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private static void ProcessGet(HttpListenerContext context)
7272

7373
HttpListenerResponse response = context.Response;
7474
response.AddHeader("Content-Type", "application/json; charset=utf-8");
75+
response.AddHeader("Access-Control-Allow-Origin", "*");
7576

7677
byte[] buffer = Encoding.UTF8.GetBytes(data);
7778
response.ContentLength64 = buffer.Length;

WriteAPI/Program.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ private static void Main(string[] args)
3535
ConfigurationManager.UpdateConfig(updateConfig);
3636
Console.WriteLine();
3737

38-
// Constantly checks to see if still hooked
39-
new Thread(GameInterface.Hook).Start();
40-
4138
Listener.Start();
4239
}
4340
}

WriteAPI/WriteAPI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net5.0</TargetFramework>
5+
<TargetFramework>net6.0</TargetFramework>
66
<ApplicationManifest>app.manifest</ApplicationManifest>
77
<StartupObject>WriteAPI.Program</StartupObject>
88
<PackageVersion>1.1.0</PackageVersion>

0 commit comments

Comments
 (0)