Skip to content

Commit 394f57d

Browse files
committed
Add GT7 Simulator Interface tool
1 parent 7f86016 commit 394f57d

File tree

10 files changed

+497
-4
lines changed

10 files changed

+497
-4
lines changed

PDTools.Crypto/Salsa20.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public Salsa20(byte[] key, int keyLength)
3131
m_state = new uint[0x10];
3232

3333
// memcpy(vector, key, keyLength)
34-
var keyUints = MemoryMarshal.Cast<byte, uint>(key);
34+
var keyUints = MemoryMarshal.Cast<byte, uint>(key.AsSpan(0, keyLength));
3535
keyUints.CopyTo(m_state.AsSpan(1, keyLength / 4));
3636

3737
byte[] constants = keyLength == 32 ? c_sigma : c_tau;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace PDTools.Crypto.SimulationInterface
8+
{
9+
public interface ISimulationInterfaceCryptor
10+
{
11+
public void Decrypt(Span<byte> bytes);
12+
}
13+
}

PDTools.Crypto/SimulatorInterfaceCryptor.cs renamed to PDTools.Crypto/SimulationInterface/SimulatorInterfaceCryptor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77

88
using System.Buffers.Binary;
99

10-
namespace PDTools.Crypto
10+
namespace PDTools.Crypto.SimulationInterface
1111
{
1212
/// <summary>
1313
/// Used to decrypt packets from GT6's Simulator Interface.
1414
/// </summary>
15-
public class SimulatorInterfaceCryptor
15+
public class SimulatorInterfaceCryptor : ISimulationInterfaceCryptor
1616
{
1717
private Salsa20 _salsa;
1818

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.IO;
7+
8+
using System.Buffers.Binary;
9+
10+
namespace PDTools.Crypto.SimulationInterface
11+
{
12+
/// <summary>
13+
/// Used to decrypt packets from GT7's Simulator Interface.
14+
/// </summary>
15+
public class SimulatorInterfaceCryptorGT7 : ISimulationInterfaceCryptor
16+
{
17+
private Salsa20 _salsa;
18+
19+
public const string Key = "Simulator Interface Packet GT7 ver 0.0";
20+
21+
public SimulatorInterfaceCryptorGT7()
22+
{
23+
_salsa = new Salsa20(Encoding.ASCII.GetBytes(Key), 0x26);
24+
}
25+
26+
public void Decrypt(Span<byte> bytes)
27+
{
28+
// Input should be 0x94 (or 0x128 in certain cases?)
29+
_salsa.Set(0);
30+
31+
int iv1 = BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(0x40)); // Seed IV is always located there
32+
int iv2 = (int)(iv1 ^ 0xDEADBEAF); // Notice DEADBEAF, not DEADBEEF
33+
34+
Span<byte> iv = stackalloc byte[8];
35+
BinaryPrimitives.WriteInt32LittleEndian(iv, iv2);
36+
BinaryPrimitives.WriteInt32LittleEndian(iv[4..], iv1);
37+
_salsa.SetIV(iv);
38+
39+
_salsa.Decrypt(bytes, bytes.Length);
40+
// Magic should be "G7S0" when decrypted
41+
}
42+
}
43+
}

PDTools.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDTools.GrimPFS", "PDTools.
2121
EndProject
2222
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDTools.Files", "PDTools.Files\PDTools.Files.csproj", "{72D26FBA-E068-43AE-B5C1-492E45586C13}"
2323
EndProject
24-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDTools.RText", "PDTools.RText\PDTools.RText.csproj", "{6A553661-9746-4CC4-8DF8-DA036E8031C7}"
24+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDTools.RText", "PDTools.RText\PDTools.RText.csproj", "{6A553661-9746-4CC4-8DF8-DA036E8031C7}"
25+
EndProject
26+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimulatorInterface", "SimulatorInterface\SimulatorInterface.csproj", "{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}"
2527
EndProject
2628
Global
2729
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -69,6 +71,10 @@ Global
6971
{6A553661-9746-4CC4-8DF8-DA036E8031C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
7072
{6A553661-9746-4CC4-8DF8-DA036E8031C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
7173
{6A553661-9746-4CC4-8DF8-DA036E8031C7}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75+
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Debug|Any CPU.Build.0 = Debug|Any CPU
76+
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Release|Any CPU.ActiveCfg = Release|Any CPU
77+
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Release|Any CPU.Build.0 = Release|Any CPU
7278
EndGlobalSection
7379
GlobalSection(SolutionProperties) = preSolution
7480
HideSolutionNode = FALSE

SimulatorInterface/Program.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+

2+
using System.Net.Sockets;
3+
using System.Net;
4+
5+
namespace SimulatorInterface
6+
{
7+
internal class Program
8+
{
9+
static void Main(string[] args)
10+
{
11+
Console.WriteLine("Simulator Interface GT7 - by Nenkai#9075");
12+
Console.WriteLine();
13+
14+
if (args.Length == 0)
15+
{
16+
Console.WriteLine("Usage: SimulatorInterface.exe <IP Address of PS4/PS5>");
17+
return;
18+
}
19+
20+
21+
SimulatorInterface simInterface = new SimulatorInterface(args[0]);
22+
simInterface.Start();
23+
}
24+
}
25+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"profiles": {
3+
"SimulatorInterface": {
4+
"commandName": "Project",
5+
"commandLineArgs": "192.168.0.25"
6+
}
7+
}
8+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Net;
7+
using System.Net.Sockets;
8+
using Syroot.BinaryData.Memory;
9+
using System.Numerics;
10+
11+
using PDTools.Crypto.SimulationInterface;
12+
13+
namespace SimulatorInterface
14+
{
15+
public class SimulatorInterface
16+
{
17+
private ISimulationInterfaceCryptor _cryptor;
18+
private IPEndPoint _endpoint;
19+
20+
public const int SendDelaySeconds = 10;
21+
22+
public const int ReceivePort = 33739;
23+
public const int BindPort = 33740;
24+
25+
public SimulatorInterface(string address)
26+
{
27+
if (!IPAddress.TryParse(address, out IPAddress addr))
28+
throw new ArgumentException("Could not parse IP Address.");
29+
30+
_endpoint = new IPEndPoint(addr, ReceivePort);
31+
_cryptor = new SimulatorInterfaceCryptorGT7();
32+
}
33+
34+
public bool Start()
35+
{
36+
Console.WriteLine($"- Starting Simulator Interface to connect at endpoint: {_endpoint}");
37+
const int SendDelaySeconds = 10;
38+
39+
UdpClient udpClient;
40+
try
41+
{
42+
Console.WriteLine($"- Attempting to bind port: {BindPort}");
43+
udpClient = new UdpClient(BindPort);
44+
45+
Console.WriteLine("- Sending heartbeat packet..");
46+
udpClient.Send(new byte[1] { (byte)'A' }, _endpoint);
47+
}
48+
catch (Exception e)
49+
{
50+
Console.WriteLine($"Error: {e.Message}");
51+
return false;
52+
}
53+
54+
DateTime lastSent = DateTime.UtcNow;
55+
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
56+
57+
Console.WriteLine("- Done. Waiting on packets.. (If this gets stuck, it failed to connect.)");
58+
59+
// Will send a packet per tick - 60fps
60+
while (true)
61+
{
62+
if ((DateTime.UtcNow - lastSent).TotalSeconds > SendDelaySeconds)
63+
udpClient.Send(new byte[1] { (byte)'A' }, _endpoint);
64+
65+
byte[] data = udpClient.Receive(ref RemoteIpEndPoint);
66+
if (data.Length != 0x128)
67+
throw new InvalidDataException($"Expected packet size to be 0x128. Was {data.Length:X4} bytes.");
68+
69+
_cryptor.Decrypt(data);
70+
71+
SpanReader sr = new SpanReader(data);
72+
int magic = sr.ReadInt32();
73+
if (magic != 0x47375330) // 0S7G - G7S0
74+
throw new InvalidDataException($"Unexpected packet magic '{magic}'.");
75+
76+
var dataPacket = new SimulatorPacketGT7();
77+
78+
dataPacket.Position = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle()); // Coords to track
79+
dataPacket.Acceleration = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle()); // Accel in track pixels
80+
dataPacket.Rotation = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle()); // Pitch/Yaw/Roll all -1 to 1
81+
dataPacket.Unknown_0x28 = sr.ReadSingle();
82+
dataPacket.Unknown_0x2C = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle());
83+
dataPacket.Unknown_0x38 = sr.ReadSingle();
84+
dataPacket.RPM = sr.ReadSingle();
85+
86+
// Skip IV
87+
sr.Position += 8;
88+
89+
dataPacket.Unknown_0x48 = sr.ReadSingle();
90+
dataPacket.MetersPerSecond = sr.ReadSingle();
91+
dataPacket.TurboBoost = sr.ReadSingle();
92+
dataPacket.Unknown_0x54 = sr.ReadSingle();
93+
dataPacket.Unknown_Always85_0x58 = sr.ReadSingle();
94+
dataPacket.Unknown_Always110_0x5C = sr.ReadSingle();
95+
dataPacket.TireSurfaceTemperatureFL = sr.ReadSingle();
96+
dataPacket.TireSurfaceTemperatureFR = sr.ReadSingle();
97+
dataPacket.TireSurfaceTemperatureRL = sr.ReadSingle();
98+
dataPacket.TireSurfaceTemperatureRR = sr.ReadSingle();
99+
dataPacket.TotalTimeTicks = sr.ReadInt32(); // can't be more than MAX_LAPTIME1000 - which is 1209599999, or else it's set to -1
100+
dataPacket.CurrentLap = sr.ReadInt32();
101+
dataPacket.BestLapTime = TimeSpan.FromMilliseconds(sr.ReadInt32());
102+
dataPacket.LastLapTime = TimeSpan.FromMilliseconds(sr.ReadInt32());
103+
dataPacket.DayProgressionMS = sr.ReadInt32();
104+
dataPacket.PreRaceStartPositionOrQualiPos = sr.ReadInt16();
105+
dataPacket.NumCarsAtPreRace = sr.ReadInt16();
106+
dataPacket.MinAlertRPM = sr.ReadInt16();
107+
dataPacket.MaxAlertRPM = sr.ReadInt16();
108+
dataPacket.CalculatedMaxSpeed = sr.ReadInt16();
109+
dataPacket.Flags = (SimulatorFlags)sr.ReadInt16();
110+
111+
int bits = sr.ReadByte();
112+
dataPacket.CurrentGear = (byte)(bits & 0b1111);
113+
dataPacket.SuggestedGear = (byte)(bits >> 4);
114+
115+
dataPacket.Throttle = sr.ReadByte();
116+
dataPacket.Brake = sr.ReadByte();
117+
118+
//short throttleAndBrake = sr.ReadInt16();
119+
byte unknown = sr.ReadByte();
120+
121+
dataPacket.TireFL_Unknown0x94_0 = sr.ReadSingle();
122+
dataPacket.TireFR_Unknown0x94_1 = sr.ReadSingle();
123+
dataPacket.TireRL_Unknown0x94_2 = sr.ReadSingle();
124+
dataPacket.TireRR_Unknown0x94_3 = sr.ReadSingle();
125+
dataPacket.TireFL_Accel = sr.ReadSingle();
126+
dataPacket.TireFR_Accel = sr.ReadSingle();
127+
dataPacket.TireRL_Accel = sr.ReadSingle();
128+
dataPacket.TireRR_Accel = sr.ReadSingle();
129+
dataPacket.TireFL_UnknownB4 = sr.ReadSingle();
130+
dataPacket.TireFR_UnknownB4 = sr.ReadSingle();
131+
dataPacket.TireRL_UnknownB4 = sr.ReadSingle();
132+
dataPacket.TireRR_UnknownB4 = sr.ReadSingle();
133+
dataPacket.TireFL_UnknownC4 = sr.ReadSingle();
134+
dataPacket.TireFR_UnknownC4 = sr.ReadSingle();
135+
dataPacket.TireRL_UnknownC4 = sr.ReadSingle();
136+
dataPacket.TireRR_UnknownC4 = sr.ReadSingle();
137+
138+
sr.Position += sizeof(int) * 8; // Seems to be reserved - server does not set that
139+
140+
dataPacket.Unknown_0xF4 = sr.ReadSingle();
141+
dataPacket.Unknown_0xF8 = sr.ReadSingle();
142+
dataPacket.RPMUnknown_0xFC = sr.ReadSingle();
143+
144+
for (var i = 0; i < 7; i++)
145+
dataPacket.Unknown_0x100[i] = sr.ReadSingle();
146+
147+
sr.Position += 8;
148+
dataPacket.CarCode = sr.ReadInt32();
149+
150+
PrintStatus(dataPacket);
151+
}
152+
}
153+
154+
private static void PrintStatus(SimulatorPacketGT7 packet)
155+
{
156+
Console.Clear();
157+
Console.WriteLine($"Simulator Interface Packet");
158+
Console.WriteLine("[Car Data]");
159+
Console.WriteLine($"- Car Code: {packet.CarCode}");
160+
Console.WriteLine($"- Throttle: {packet.Throttle}");
161+
Console.WriteLine($"- Brake: {packet.Brake}");
162+
Console.WriteLine($"- RPM: {packet.RPM} - KPH: {Math.Round(packet.MetersPerSecond * 3.6, 2)}");
163+
Console.WriteLine($"- Turbo Boost: {packet.TurboBoost}");
164+
165+
if (packet.SuggestedGear == 15)
166+
Console.WriteLine($"- Gear: {packet.CurrentGear}");
167+
else
168+
Console.WriteLine($"- Gear: {packet.CurrentGear} (Suggested: {packet.SuggestedGear})");
169+
170+
Console.WriteLine($"- Flags: {packet.Flags}");
171+
Console.WriteLine($"- Tires");
172+
Console.WriteLine($" FL:{Math.Round(packet.TireSurfaceTemperatureFL, 2)} FR:{Math.Round(packet.TireSurfaceTemperatureFR, 2)}");
173+
Console.WriteLine($" RL:{Math.Round(packet.TireSurfaceTemperatureRL, 2)} RR:{Math.Round(packet.TireSurfaceTemperatureRR, 2)}");
174+
175+
Console.WriteLine();
176+
Console.WriteLine("[Race Data]");
177+
178+
int a = (int)(packet.TotalTimeTicks * 0.16667);
179+
TimeSpan.FromSeconds(a / 10);
180+
Console.WriteLine($"- Total Session Time: {TimeSpan.FromSeconds(packet.TotalTimeTicks / 60)}");
181+
Console.WriteLine($"- Current Lap: {packet.CurrentLap}");
182+
Console.WriteLine($"- Best: {packet.BestLapTime}");
183+
Console.WriteLine($"- Last: {packet.LastLapTime}");
184+
Console.WriteLine($"- Time of Day: {TimeSpan.FromMilliseconds(packet.DayProgressionMS)}");
185+
186+
Console.WriteLine();
187+
Console.WriteLine("[Positional Information]");
188+
Console.WriteLine($"- Position: {packet.Position}");
189+
Console.WriteLine($"- Accel: {packet.Acceleration}");
190+
Console.WriteLine($"- Rotation: {packet.Rotation}");
191+
}
192+
}
193+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Syroot.BinaryData" Version="5.2.2" />
12+
<PackageReference Include="Syroot.BinaryData.Memory" Version="5.2.2" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\PDTools.Crypto\PDTools.Crypto.csproj" />
17+
</ItemGroup>
18+
19+
</Project>

0 commit comments

Comments
 (0)