Skip to content

Commit

Permalink
Add GT7 Simulator Interface tool
Browse files Browse the repository at this point in the history
  • Loading branch information
Nenkai committed Jul 25, 2022
1 parent 7f86016 commit 394f57d
Show file tree
Hide file tree
Showing 10 changed files with 497 additions and 4 deletions.
2 changes: 1 addition & 1 deletion PDTools.Crypto/Salsa20.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Salsa20(byte[] key, int keyLength)
m_state = new uint[0x10];

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

byte[] constants = keyLength == 32 ? c_sigma : c_tau;
Expand Down
13 changes: 13 additions & 0 deletions PDTools.Crypto/SimulationInterface/ISimulationInterfaceCryptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PDTools.Crypto.SimulationInterface
{
public interface ISimulationInterfaceCryptor
{
public void Decrypt(Span<byte> bytes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

using System.Buffers.Binary;

namespace PDTools.Crypto
namespace PDTools.Crypto.SimulationInterface
{
/// <summary>
/// Used to decrypt packets from GT6's Simulator Interface.
/// </summary>
public class SimulatorInterfaceCryptor
public class SimulatorInterfaceCryptor : ISimulationInterfaceCryptor
{
private Salsa20 _salsa;

Expand Down
43 changes: 43 additions & 0 deletions PDTools.Crypto/SimulationInterface/SimulatorInterfaceCryptorGT7.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using System.Buffers.Binary;

namespace PDTools.Crypto.SimulationInterface
{
/// <summary>
/// Used to decrypt packets from GT7's Simulator Interface.
/// </summary>
public class SimulatorInterfaceCryptorGT7 : ISimulationInterfaceCryptor
{
private Salsa20 _salsa;

public const string Key = "Simulator Interface Packet GT7 ver 0.0";

public SimulatorInterfaceCryptorGT7()
{
_salsa = new Salsa20(Encoding.ASCII.GetBytes(Key), 0x26);
}

public void Decrypt(Span<byte> bytes)
{
// Input should be 0x94 (or 0x128 in certain cases?)
_salsa.Set(0);

int iv1 = BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(0x40)); // Seed IV is always located there
int iv2 = (int)(iv1 ^ 0xDEADBEAF); // Notice DEADBEAF, not DEADBEEF

Span<byte> iv = stackalloc byte[8];
BinaryPrimitives.WriteInt32LittleEndian(iv, iv2);
BinaryPrimitives.WriteInt32LittleEndian(iv[4..], iv1);
_salsa.SetIV(iv);

_salsa.Decrypt(bytes, bytes.Length);
// Magic should be "G7S0" when decrypted
}
}
}
8 changes: 7 additions & 1 deletion PDTools.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDTools.GrimPFS", "PDTools.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDTools.Files", "PDTools.Files\PDTools.Files.csproj", "{72D26FBA-E068-43AE-B5C1-492E45586C13}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDTools.RText", "PDTools.RText\PDTools.RText.csproj", "{6A553661-9746-4CC4-8DF8-DA036E8031C7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDTools.RText", "PDTools.RText\PDTools.RText.csproj", "{6A553661-9746-4CC4-8DF8-DA036E8031C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimulatorInterface", "SimulatorInterface\SimulatorInterface.csproj", "{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -69,6 +71,10 @@ Global
{6A553661-9746-4CC4-8DF8-DA036E8031C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A553661-9746-4CC4-8DF8-DA036E8031C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A553661-9746-4CC4-8DF8-DA036E8031C7}.Release|Any CPU.Build.0 = Release|Any CPU
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5733B05-0BE0-4B61-B8CB-4C88E77B5443}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
25 changes: 25 additions & 0 deletions SimulatorInterface/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

using System.Net.Sockets;
using System.Net;

namespace SimulatorInterface
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Simulator Interface GT7 - by Nenkai#9075");
Console.WriteLine();

if (args.Length == 0)
{
Console.WriteLine("Usage: SimulatorInterface.exe <IP Address of PS4/PS5>");
return;
}


SimulatorInterface simInterface = new SimulatorInterface(args[0]);
simInterface.Start();
}
}
}
8 changes: 8 additions & 0 deletions SimulatorInterface/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"SimulatorInterface": {
"commandName": "Project",
"commandLineArgs": "192.168.0.25"
}
}
}
193 changes: 193 additions & 0 deletions SimulatorInterface/SimulatorInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using Syroot.BinaryData.Memory;
using System.Numerics;

using PDTools.Crypto.SimulationInterface;

namespace SimulatorInterface
{
public class SimulatorInterface
{
private ISimulationInterfaceCryptor _cryptor;
private IPEndPoint _endpoint;

public const int SendDelaySeconds = 10;

public const int ReceivePort = 33739;
public const int BindPort = 33740;

public SimulatorInterface(string address)
{
if (!IPAddress.TryParse(address, out IPAddress addr))
throw new ArgumentException("Could not parse IP Address.");

_endpoint = new IPEndPoint(addr, ReceivePort);
_cryptor = new SimulatorInterfaceCryptorGT7();
}

public bool Start()
{
Console.WriteLine($"- Starting Simulator Interface to connect at endpoint: {_endpoint}");
const int SendDelaySeconds = 10;

UdpClient udpClient;
try
{
Console.WriteLine($"- Attempting to bind port: {BindPort}");
udpClient = new UdpClient(BindPort);

Console.WriteLine("- Sending heartbeat packet..");
udpClient.Send(new byte[1] { (byte)'A' }, _endpoint);
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
return false;
}

DateTime lastSent = DateTime.UtcNow;
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

Console.WriteLine("- Done. Waiting on packets.. (If this gets stuck, it failed to connect.)");

// Will send a packet per tick - 60fps
while (true)
{
if ((DateTime.UtcNow - lastSent).TotalSeconds > SendDelaySeconds)
udpClient.Send(new byte[1] { (byte)'A' }, _endpoint);

byte[] data = udpClient.Receive(ref RemoteIpEndPoint);
if (data.Length != 0x128)
throw new InvalidDataException($"Expected packet size to be 0x128. Was {data.Length:X4} bytes.");

_cryptor.Decrypt(data);

SpanReader sr = new SpanReader(data);
int magic = sr.ReadInt32();
if (magic != 0x47375330) // 0S7G - G7S0
throw new InvalidDataException($"Unexpected packet magic '{magic}'.");

var dataPacket = new SimulatorPacketGT7();

dataPacket.Position = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle()); // Coords to track
dataPacket.Acceleration = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle()); // Accel in track pixels
dataPacket.Rotation = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle()); // Pitch/Yaw/Roll all -1 to 1
dataPacket.Unknown_0x28 = sr.ReadSingle();
dataPacket.Unknown_0x2C = new Vector3(sr.ReadSingle(), sr.ReadSingle(), sr.ReadSingle());
dataPacket.Unknown_0x38 = sr.ReadSingle();
dataPacket.RPM = sr.ReadSingle();

// Skip IV
sr.Position += 8;

dataPacket.Unknown_0x48 = sr.ReadSingle();
dataPacket.MetersPerSecond = sr.ReadSingle();
dataPacket.TurboBoost = sr.ReadSingle();
dataPacket.Unknown_0x54 = sr.ReadSingle();
dataPacket.Unknown_Always85_0x58 = sr.ReadSingle();
dataPacket.Unknown_Always110_0x5C = sr.ReadSingle();
dataPacket.TireSurfaceTemperatureFL = sr.ReadSingle();
dataPacket.TireSurfaceTemperatureFR = sr.ReadSingle();
dataPacket.TireSurfaceTemperatureRL = sr.ReadSingle();
dataPacket.TireSurfaceTemperatureRR = sr.ReadSingle();
dataPacket.TotalTimeTicks = sr.ReadInt32(); // can't be more than MAX_LAPTIME1000 - which is 1209599999, or else it's set to -1
dataPacket.CurrentLap = sr.ReadInt32();
dataPacket.BestLapTime = TimeSpan.FromMilliseconds(sr.ReadInt32());
dataPacket.LastLapTime = TimeSpan.FromMilliseconds(sr.ReadInt32());
dataPacket.DayProgressionMS = sr.ReadInt32();
dataPacket.PreRaceStartPositionOrQualiPos = sr.ReadInt16();
dataPacket.NumCarsAtPreRace = sr.ReadInt16();
dataPacket.MinAlertRPM = sr.ReadInt16();
dataPacket.MaxAlertRPM = sr.ReadInt16();
dataPacket.CalculatedMaxSpeed = sr.ReadInt16();
dataPacket.Flags = (SimulatorFlags)sr.ReadInt16();

int bits = sr.ReadByte();
dataPacket.CurrentGear = (byte)(bits & 0b1111);
dataPacket.SuggestedGear = (byte)(bits >> 4);

dataPacket.Throttle = sr.ReadByte();
dataPacket.Brake = sr.ReadByte();

//short throttleAndBrake = sr.ReadInt16();
byte unknown = sr.ReadByte();

dataPacket.TireFL_Unknown0x94_0 = sr.ReadSingle();
dataPacket.TireFR_Unknown0x94_1 = sr.ReadSingle();
dataPacket.TireRL_Unknown0x94_2 = sr.ReadSingle();
dataPacket.TireRR_Unknown0x94_3 = sr.ReadSingle();
dataPacket.TireFL_Accel = sr.ReadSingle();
dataPacket.TireFR_Accel = sr.ReadSingle();
dataPacket.TireRL_Accel = sr.ReadSingle();
dataPacket.TireRR_Accel = sr.ReadSingle();
dataPacket.TireFL_UnknownB4 = sr.ReadSingle();
dataPacket.TireFR_UnknownB4 = sr.ReadSingle();
dataPacket.TireRL_UnknownB4 = sr.ReadSingle();
dataPacket.TireRR_UnknownB4 = sr.ReadSingle();
dataPacket.TireFL_UnknownC4 = sr.ReadSingle();
dataPacket.TireFR_UnknownC4 = sr.ReadSingle();
dataPacket.TireRL_UnknownC4 = sr.ReadSingle();
dataPacket.TireRR_UnknownC4 = sr.ReadSingle();

sr.Position += sizeof(int) * 8; // Seems to be reserved - server does not set that

dataPacket.Unknown_0xF4 = sr.ReadSingle();
dataPacket.Unknown_0xF8 = sr.ReadSingle();
dataPacket.RPMUnknown_0xFC = sr.ReadSingle();

for (var i = 0; i < 7; i++)
dataPacket.Unknown_0x100[i] = sr.ReadSingle();

sr.Position += 8;
dataPacket.CarCode = sr.ReadInt32();

PrintStatus(dataPacket);
}
}

private static void PrintStatus(SimulatorPacketGT7 packet)
{
Console.Clear();
Console.WriteLine($"Simulator Interface Packet");
Console.WriteLine("[Car Data]");
Console.WriteLine($"- Car Code: {packet.CarCode}");
Console.WriteLine($"- Throttle: {packet.Throttle}");
Console.WriteLine($"- Brake: {packet.Brake}");
Console.WriteLine($"- RPM: {packet.RPM} - KPH: {Math.Round(packet.MetersPerSecond * 3.6, 2)}");
Console.WriteLine($"- Turbo Boost: {packet.TurboBoost}");

if (packet.SuggestedGear == 15)
Console.WriteLine($"- Gear: {packet.CurrentGear}");
else
Console.WriteLine($"- Gear: {packet.CurrentGear} (Suggested: {packet.SuggestedGear})");

Console.WriteLine($"- Flags: {packet.Flags}");
Console.WriteLine($"- Tires");
Console.WriteLine($" FL:{Math.Round(packet.TireSurfaceTemperatureFL, 2)} FR:{Math.Round(packet.TireSurfaceTemperatureFR, 2)}");
Console.WriteLine($" RL:{Math.Round(packet.TireSurfaceTemperatureRL, 2)} RR:{Math.Round(packet.TireSurfaceTemperatureRR, 2)}");

Console.WriteLine();
Console.WriteLine("[Race Data]");

int a = (int)(packet.TotalTimeTicks * 0.16667);
TimeSpan.FromSeconds(a / 10);
Console.WriteLine($"- Total Session Time: {TimeSpan.FromSeconds(packet.TotalTimeTicks / 60)}");
Console.WriteLine($"- Current Lap: {packet.CurrentLap}");
Console.WriteLine($"- Best: {packet.BestLapTime}");
Console.WriteLine($"- Last: {packet.LastLapTime}");
Console.WriteLine($"- Time of Day: {TimeSpan.FromMilliseconds(packet.DayProgressionMS)}");

Console.WriteLine();
Console.WriteLine("[Positional Information]");
Console.WriteLine($"- Position: {packet.Position}");
Console.WriteLine($"- Accel: {packet.Acceleration}");
Console.WriteLine($"- Rotation: {packet.Rotation}");
}
}
}
19 changes: 19 additions & 0 deletions SimulatorInterface/SimulatorInterface.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Syroot.BinaryData" Version="5.2.2" />
<PackageReference Include="Syroot.BinaryData.Memory" Version="5.2.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PDTools.Crypto\PDTools.Crypto.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit 394f57d

Please sign in to comment.