모종의 음모/force feedback

forza horizon 4 telemetry 수정

구차니 2024. 12. 7. 22:58

빌드하려고 vs2022 부터 다시 깔고 쑈하긴 했는데

아무튼 기어가 올라오긴 한다.

 

서버는 자기 자신의 아이피를 넣고(0.0.0.0을 넣으면 되려나?) 포트를 게임에서 설정한대로 하면 끝

 

이 녀석의 구조체를 뜯어 봐야 겠구만?

[링크 : https://github.com/geeooff/forza-data-web]

 

 

다시 원점으로 돌아가서 하나하나 길이 맞추면서 보니까 먼가 12바이트가 추가되어 있었다.

01 00 00 00 // S32 IsRaceOn;

87 65 A8 14 // U32 TimestampMS;
FB 6F 14 46 // F32 EngineMaxRpm;
F8 FF 47 44 // F32 EngineIdleRpm;
D9 18 0A 45 // F32 CurrentEngineRpm;

C0 FC 1B BD // F32 AccelerationX;
90 AB 60 3D 
6C 00 D9 40 

88 EA A9 BD // F32 VelocityX;
E8 EF 9D BC 
83 62 9B 40 

5D F5 9C 3B // F32 AngularVelocityX;
16 7C 65 BC 
E9 09 D0 BC 

7E D0 B0 BF // F32 Yaw;
F2 E8 B1 BE 
AA F7 69 3E 

FA 31 DB 3E // F32 NormalizedSuspensionTravelFrontLeft
CE 72 B3 3E 
A4 DD F8 3E 
3F 6E 03 3F 

FB 1B 71 3F // F32 TireSlipRatioFrontLeft
07 85 59 3F 
A2 BE 10 3F 
4F 86 29 3F 

39 74 A3 41 // F32 WheelRotationSpeedFrontLeft
CC 09 8C 41 
BC CC 81 41 
C2 F8 85 41 

00 00 00 00 // S32 WheelOnRumbleStripFrontLeft
00 00 00 00 
00 00 00 00 
00 00 00 00 

00 00 00 00 // F32 WheelInPuddleDepthFrontLeft
00 00 00 00 
00 00 00 00 
00 00 00 00 

99 99 19 3F // F32 SurfaceRumbleFrontLeft
99 99 19 3F 
99 99 19 3F
99 99 19 3F

34 3F DD 3D // F32 TireSlipAngleFrontLeft
34 C6 E3 3D 
0F 9A 97 3D 
35 FA 99 3D 

B2 B0 72 3F  // F32 TireCombinedSlipFrontLeft
06 60 5B 3F 
DA FA 11 3F 
20 9D 2A 3F 

1E A2 B0 BB // F32 SuspensionTravelMetersFrontLeft
5C 44 3D BC 
00 E9 9A 3A 
80 C3 54 3B 

89 0D 00 00 // S32 CarOrdinal
04 00 00 00 // S32 CarClass
84 03 00 00 // S32 CarPerformanceIndex
02 00 00 00 // S32 DrivetrainType - // 0 = FWD, 1 = RWD, 2 = AWD

0C 00 00 00 // S32 NumCylinders
0D 00 00 00 // ?? 
00 00 00 00 // ??
00 00 00 00 // ??

EC C6 18 C4 // F32 PositionX; 
33 08 20 43 // F32 PositionY; 
40 A0 1C C5 // F32 PositionZ; 
A1 68 9B 40 // F32 Speed; 
F4 5F 03 48 // F32 Power; 
23 DF 11 44 // F32 Torque; 
20 4C 9C 42 // F32 TireTempFrontLeft; 
0D 43 9D 42 // F32 TireTempFrontRight; 
9B FD A9 42 // F32 TireTempRearLeft; 
9B FD A9 42 // F32 TireTempRearRight; 
00 00 00 00 // F32 Boost; 
00 00 80 3F // F32 Fuel; 
00 00 00 00 // F32 DistanceTraveled; 
00 00 00 00 // F32 BestLap; 
00 00 00 00 // F32 LastLap;
00 00 00 00 // F32 CurrentLap;
C8 AF FB 43 // F32 CurrentRaceTime;
00 00       // U16 LapNumber;
00          // U8 RacePosition;
FF          // U8 Accel;
00          // U8 Brake;
00          // U8 Clutch;
00          // U8 HandBrake;
01          // U8 Gear;
00          // S8 Steer;
00          // S8 NormalizedDrivingLine;
00          // S8 NormalizedAIBrakeDifference;
00          // ???

 

sled 구조체에서 Steer 이후에 24bit는 원래의 구조체와는 다른 것 같네

아무튼 아래와 같이 값들이 정상적으로 출력되는 것 확인!

 

소스코드

더보기
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <ncurses.h>

 

#define BUF_SIZE 500

 

typedef char S8;
typedef unsigned char U8;
typedef unsigned short U16;
typedef int S32;
typedef unsigned int U32;
typedef float F32;

 

typedef struct _sled_
{
// = 1 when race is on. = 0 when in menus/race stopped …
S32 IsRaceOn;

 

// Can overflow to 0 eventually
U32 TimestampMS;
F32 EngineMaxRpm;
F32 EngineIdleRpm;
F32 CurrentEngineRpm;

 

// In the car's local space; X = right, Y = up, Z = forward
F32 AccelerationX;
F32 AccelerationY;
F32 AccelerationZ;

 

// In the car's local space; X = right, Y = up, Z = forward
F32 VelocityX;
F32 VelocityY;
F32 VelocityZ;

 

// In the car's local space; X = pitch, Y = yaw, Z = roll
F32 AngularVelocityX;
F32 AngularVelocityY;
F32 AngularVelocityZ;

 

F32 Yaw;
F32 Pitch;
F32 Roll;

 

// Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
F32 NormalizedSuspensionTravelFrontLeft;
F32 NormalizedSuspensionTravelFrontRight;
F32 NormalizedSuspensionTravelRearLeft;
F32 NormalizedSuspensionTravelRearRight;

 

// Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
F32 TireSlipRatioFrontLeft;
F32 TireSlipRatioFrontRight;
F32 TireSlipRatioRearLeft;
F32 TireSlipRatioRearRight;

 

// Wheels rotation speed radians/sec.
F32 WheelRotationSpeedFrontLeft;
F32 WheelRotationSpeedFrontRight;
F32 WheelRotationSpeedRearLeft;
F32 WheelRotationSpeedRearRight;

 

// = 1 when wheel is on rumble strip, = 0 when off.
S32 WheelOnRumbleStripFrontLeft;
S32 WheelOnRumbleStripFrontRight;
S32 WheelOnRumbleStripRearLeft;
S32 heelOnRumbleStripRearRight;

 

// = from 0 to 1, where 1 is the deepest puddle
F32 WheelInPuddleDepthFrontLeft;
F32 WheelInPuddleDepthFrontRight;
F32 WheelInPuddleDepthRearLeft;
F32 WheelInPuddleDepthRearRight;

 

// Non-dimensional surface rumble values passed to controller force feedback
F32 SurfaceRumbleFrontLeft;
F32 SurfaceRumbleFrontRight;
F32 SurfaceRumbleRearLeft;
F32 SurfaceRumbleRearRight;

 

// Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
F32 TireSlipAngleFrontLeft;
F32 TireSlipAngleFrontRight;
F32 TireSlipAngleRearLeft;
F32 TireSlipAngleRearRight;

 

// Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
F32 TireCombinedSlipFrontLeft;
F32 TireCombinedSlipFrontRight;
F32 TireCombinedSlipRearLeft;
F32 TireCombinedSlipRearRight;

 

// Actual suspension travel in meters
F32 SuspensionTravelMetersFrontLeft;
F32 SuspensionTravelMetersFrontRight;
F32 SuspensionTravelMetersRearLeft;
F32 SuspensionTravelMetersRearRight;

 

// Unique ID of the car make/model
S32 CarOrdinal;

 

// Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive
S32 CarClass;

 

// Between 100 (worst car) and 999 (best car) inclusive
S32 CarPerformanceIndex;

 

// 0 = FWD, 1 = RWD, 2 = AWD
S32 DrivetrainType;

 

// Number of cylinders in the engine
S32 NumCylinders;
} __attribute__((packed)) FORZA_SLED;

 

typedef struct _dash_
{
// = 1 when race is on. = 0 when in menus/race stopped …
S32 IsRaceOn;

 

// Can overflow to 0 eventually
U32 TimestampMS;
F32 EngineMaxRpm;
F32 EngineIdleRpm;
F32 CurrentEngineRpm;

 

// In the car's local space; X = right, Y = up, Z = forward
F32 AccelerationX;
F32 AccelerationY;
F32 AccelerationZ;

 

// In the car's local space; X = right, Y = up, Z = forward
F32 VelocityX;
F32 VelocityY;
F32 VelocityZ;

 

// In the car's local space; X = pitch, Y = yaw, Z = roll
F32 AngularVelocityX;
F32 AngularVelocityY;
F32 AngularVelocityZ;

 

F32 Yaw;
F32 Pitch;
F32 Roll;

 

// Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
F32 NormalizedSuspensionTravelFrontLeft;
F32 NormalizedSuspensionTravelFrontRight;
F32 NormalizedSuspensionTravelRearLeft;
F32 NormalizedSuspensionTravelRearRight;

 

// Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
F32 TireSlipRatioFrontLeft;
F32 TireSlipRatioFrontRight;
F32 TireSlipRatioRearLeft;
F32 TireSlipRatioRearRight;

 

// Wheels rotation speed radians/sec.
F32 WheelRotationSpeedFrontLeft;
F32 WheelRotationSpeedFrontRight;
F32 WheelRotationSpeedRearLeft;
F32 WheelRotationSpeedRearRight;

 

// = 1 when wheel is on rumble strip, = 0 when off.
S32 WheelOnRumbleStripFrontLeft;
S32 WheelOnRumbleStripFrontRight;
S32 WheelOnRumbleStripRearLeft;
S32 heelOnRumbleStripRearRight;

 

// = from 0 to 1, where 1 is the deepest puddle
F32 WheelInPuddleDepthFrontLeft;
F32 WheelInPuddleDepthFrontRight;
F32 WheelInPuddleDepthRearLeft;
F32 WheelInPuddleDepthRearRight;

 

// Non-dimensional surface rumble values passed to controller force feedback
F32 SurfaceRumbleFrontLeft;
F32 SurfaceRumbleFrontRight;
F32 SurfaceRumbleRearLeft;
F32 SurfaceRumbleRearRight;

 

// Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
F32 TireSlipAngleFrontLeft;
F32 TireSlipAngleFrontRight;
F32 TireSlipAngleRearLeft;
F32 TireSlipAngleRearRight;

 

// Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
F32 TireCombinedSlipFrontLeft;
F32 TireCombinedSlipFrontRight;
F32 TireCombinedSlipRearLeft;
F32 TireCombinedSlipRearRight;

 

// Actual suspension travel in meters
F32 SuspensionTravelMetersFrontLeft;
F32 SuspensionTravelMetersFrontRight;
F32 SuspensionTravelMetersRearLeft;
F32 SuspensionTravelMetersRearRight;

 

// Unique ID of the car make/model
S32 CarOrdinal;

 

// Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive
S32 CarClass;

 

// Between 100 (worst car) and 999 (best car) inclusive
S32 CarPerformanceIndex;

 

// 0 = FWD, 1 = RWD, 2 = AWD
S32 DrivetrainType;

 

// Number of cylinders in the engine
S32 NumCylinders;

 

S32 reserved1;
S32 reserved2;
S32 reserved3;

 

// add for DASH
F32 PositionX;
F32 PositionY;
F32 PositionZ;
F32 Speed;
F32 Power;
F32 Torque;
F32 TireTempFrontLeft;
F32 TireTempFrontRight;
F32 TireTempRearLeft;
F32 TireTempRearRight;
F32 Boost;
F32 Fuel;
F32 DistanceTraveled;
F32 BestLap;
F32 LastLap;
F32 CurrentLap;
F32 CurrentRaceTime;
U16 LapNumber;
U8 RacePosition;
U8 Accel;
U8 Brake;
U8 Clutch;
U8 HandBrake;
U8 Gear;
S8 Steer;
S8 NormalizedDrivingLine;
S8 NormalizedAIBrakeDifference;

 

F32 TireWearFrontLeft;
F32 TireWearFrontRight;
F32 TireWearRearLeft;
F32 TireWearRearRight;

 

// ID for track
S32 TrackOrdinal;
} __attribute__((packed)) FORZA_DASH;

 

int last_raceon = 0;
void parse_forza_dbg(unsigned char *message)
{
int row = 0;
FORZA_DASH forza;
memcpy(&forza, message, sizeof(FORZA_DASH));

 

for(int i = 0; i < 324; i++)
{
if(i % 16 == 0)
printf("\n");
printf("%02X ",message[i]);
}
printf("\n");

 

printf("NumCylinders %d\n", forza.NumCylinders);
 
printf("PositionX %f\n", forza.PositionX);
printf("PositionY %f\n", forza.PositionY);
printf("PositionZ %f\n", forza.PositionZ);

 

printf("speed %f\n", forza.Speed);

 

printf("clutch %d\n", forza.Clutch);
printf("brake %d\n", forza.Brake);
printf("accel %d\n", forza.Accel);
printf("HandBrake %d\n", forza.HandBrake);
}

 

void parse_forza(unsigned char *message)
{
int row = 0;
FORZA_DASH forza;
memcpy(&forza, message, sizeof(FORZA_DASH));

 

static unsigned int pack_cnt = 0;

 

if(last_raceon != forza.IsRaceOn)
{
clear();
last_raceon = forza.IsRaceOn;
}

 

if(forza.IsRaceOn)
{
mvprintw(row++, 0, "let's fly %d\n",pack_cnt++);

 

mvprintw(row++, 0, "RPM %.0f / %.0f", forza.CurrentEngineRpm, forza.EngineMaxRpm);

 

mvprintw(row++, 0, "acc X %f", forza.AccelerationX);
mvprintw(row++, 0, "acc Y %f", forza.AccelerationY);
mvprintw(row++, 0, "acc Z %f", forza.AccelerationZ);

 

mvprintw(row++, 0, "vel X %f", forza.VelocityX);
mvprintw(row++, 0, "vel Y %f", forza.VelocityY);
mvprintw(row++, 0, "vel Z %f", forza.VelocityZ);

 

mvprintw(row++, 0, "ang vel X %f", forza.AngularVelocityX);
mvprintw(row++, 0, "ang vel Y %f", forza.AngularVelocityY);
mvprintw(row++, 0, "ang vel Z %f", forza.AngularVelocityZ);
 
mvprintw(row++, 0, "yaw %f", forza.Yaw);
mvprintw(row++, 0, "roll %f", forza.Pitch);
mvprintw(row++, 0, "pitch %f", forza.Roll);

 

mvprintw(row++, 0, "CarOrdinal %5d", forza.CarOrdinal);
mvprintw(row++, 0, "CarClass %5d", forza.CarClass);
mvprintw(row++, 0, "CarPerformanceIndex %5d", forza.CarPerformanceIndex);
mvprintw(row++, 0, "DrivetrainType %5d", forza.DrivetrainType);
mvprintw(row++, 0, "NumCylinders %5d", forza.NumCylinders);

 

mvprintw(row++, 0, "clutch[%3d] brake[%3d] accel[%3d] handbrake[%3d] gear[%2d] Steer[%3d]"
, forza.Clutch
, forza.Brake
, forza.Accel
, forza.HandBrake
, forza.Gear
, forza.Steer
);

 

mvprintw(row++, 0, "Speed[%5.0f] Power[%7.0f] Torque[%5.0f] Boost[%5.0f] Fuel[%5.0f]"
, forza.Speed
, forza.Power
, forza.Torque
, forza.Boost
, forza.Fuel
);

 

mvprintw(row++, 0, "Position %f , %f , %f"
, forza.PositionX
, forza.PositionY
, forza.PositionZ
);
}
else mvprintw(row++, 0, "pause");

 

refresh();
}

 

void error_handling(char *message);

 

int main(int argc, char *argv[]){
int serv_sock;
char message[BUF_SIZE];
int str_len;
socklen_t clnt_adr_sz;

 

struct sockaddr_in serv_adr, clnt_adr;

 

printf("%d sizeof(FORZA_SLED)\n", sizeof(FORZA_SLED));
printf("%d sizeof(FORZA_DASH)\n", sizeof(FORZA_DASH));

 

if(argc!=2){
printf("Usage:%s <port>\n", argv[0]);
exit(1);
}

 

serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if(serv_sock == -1)
error_handling("UDP socket creation error");

 

memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));

 

if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
error_handling("bind() error");
 
initscr();

 

while(1){
clnt_adr_sz = sizeof(clnt_adr);
str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
message[str_len] = 0x00;
// printf("%d received [%s]\n", str_len, message);
// printf("%d received, %d sizeof(FORZA_DASH)\n", str_len, sizeof(FORZA_DASH));
parse_forza(message);
// parse_forza_dbg(message);
sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz);
}
close(serv_sock);
return 0;
}

 

void error_handling(char *message){
fputs(message, stderr);
fputc('\n', stderr);

 

endwin();
exit(1);
}