@@ -26,12 +26,59 @@ class Flatout2(ProtocolBase):
26
26
PACKET_END = b"\x2e \x55 \x19 \xb4 \xe1 \x4f \x81 \x4a "
27
27
28
28
# Car Type Identifiers (byte at position -8 from end)
29
+ # Based on comprehensive analysis: Car Type + Upgrade Setting combinations
29
30
CAR_TYPE_IDENTIFIERS = {
30
- 0x08 : "Jeder" , # Alle Wagentypen erlaubt
31
- 0x18 : "Derby" , # Derby-Wagen
32
- 0x28 : "Rennen" , # Rennwagen
33
- 0x38 : "Strasse" , # Straßenwagen
34
- 0xE8 : "Wie Host" , # Wagentyp wie Host-Einstellung
31
+ # Jeder (Alle Wagentypen erlaubt)
32
+ 0x00 : "Jeder (0% Upgrades)" ,
33
+ 0x04 : "Jeder (50% Upgrades)" ,
34
+ 0x08 : "Jeder (100% Upgrades)" ,
35
+ 0x0C : "Jeder (Wählbare Upgrades)" ,
36
+
37
+ # Derby-Wagen
38
+ 0x10 : "Derby (0% Upgrades)" ,
39
+ 0x14 : "Derby (50% Upgrades)" ,
40
+ 0x18 : "Derby (100% Upgrades)" ,
41
+ 0x1C : "Derby (Wählbare Upgrades)" ,
42
+
43
+ # Rennwagen
44
+ 0x20 : "Rennen (0% Upgrades)" ,
45
+ 0x24 : "Rennen (50% Upgrades)" ,
46
+ 0x28 : "Rennen (100% Upgrades)" ,
47
+ 0x2C : "Rennen (Wählbare Upgrades)" ,
48
+
49
+ # Straßenwagen
50
+ 0x30 : "Strasse (0% Upgrades)" ,
51
+ 0x34 : "Strasse (50% Upgrades)" ,
52
+ 0x38 : "Strasse (100% Upgrades)" ,
53
+ 0x3C : "Strasse (Wählbare Upgrades)" ,
54
+
55
+ # Wie Host
56
+ 0xD0 : "Wie Host (0% Upgrades)" ,
57
+ 0xD4 : "Wie Host (50% Upgrades)" ,
58
+ 0xD8 : "Wie Host (100% Upgrades)" ,
59
+ 0xDC : "Wie Host (Wählbare Upgrades)" ,
60
+
61
+ # Legacy support for old single-byte car type detection
62
+ 0xE8 : "Wie Host" , # Old identifier, kept for backward compatibility
63
+ }
64
+
65
+ # Separate mappings for detailed analysis
66
+ CAR_TYPE_BASE = {
67
+ 0x00 : "Jeder" , 0x04 : "Jeder" , 0x08 : "Jeder" , 0x0C : "Jeder" ,
68
+ 0x10 : "Derby" , 0x14 : "Derby" , 0x18 : "Derby" , 0x1C : "Derby" ,
69
+ 0x20 : "Rennen" , 0x24 : "Rennen" , 0x28 : "Rennen" , 0x2C : "Rennen" ,
70
+ 0x30 : "Strasse" , 0x34 : "Strasse" , 0x38 : "Strasse" , 0x3C : "Strasse" ,
71
+ 0xD0 : "Wie Host" , 0xD4 : "Wie Host" , 0xD8 : "Wie Host" , 0xDC : "Wie Host" ,
72
+ 0xE8 : "Wie Host" , # Legacy
73
+ }
74
+
75
+ UPGRADE_SETTINGS = {
76
+ 0x00 : "0%" , 0x04 : "50%" , 0x08 : "100%" , 0x0C : "Wählbar" ,
77
+ 0x10 : "0%" , 0x14 : "50%" , 0x18 : "100%" , 0x1C : "Wählbar" ,
78
+ 0x20 : "0%" , 0x24 : "50%" , 0x28 : "100%" , 0x2C : "Wählbar" ,
79
+ 0x30 : "0%" , 0x34 : "50%" , 0x38 : "100%" , 0x3C : "Wählbar" ,
80
+ 0xD0 : "0%" , 0xD4 : "50%" , 0xD8 : "100%" , 0xDC : "Wählbar" ,
81
+ 0xE8 : "Unknown" , # Legacy
35
82
}
36
83
37
84
# Game Mode Identifiers (byte at position -7 from end)
@@ -245,6 +292,41 @@ def _extract_car_type(self, data: bytes) -> str:
245
292
print (f"Error extracting car type: { e } " )
246
293
return "Unknown"
247
294
295
+ def _extract_car_type_base (self , data : bytes ) -> str :
296
+ """
297
+ Extracts the base car type (without upgrade info) from the payload data.
298
+
299
+ :param data: The complete response data
300
+ :return: The base car type name or "Unknown" if not found
301
+ """
302
+ try :
303
+ if len (data ) >= 8 :
304
+ car_type_id = data [- 8 ] # 8 bytes from end
305
+ return self .CAR_TYPE_BASE .get (car_type_id , f"Unknown (0x{ car_type_id :02X} )" )
306
+ else :
307
+ return "Unknown"
308
+ except Exception as e :
309
+ print (f"Error extracting base car type: { e } " )
310
+ return "Unknown"
311
+
312
+ def _extract_upgrade_setting (self , data : bytes ) -> str :
313
+ """
314
+ Extracts the upgrade setting from the payload data.
315
+ Upgrade setting is encoded in the car type byte at offset -8.
316
+
317
+ :param data: The complete response data
318
+ :return: The upgrade setting or "Unknown" if not found
319
+ """
320
+ try :
321
+ if len (data ) >= 8 :
322
+ car_type_id = data [- 8 ] # 8 bytes from end
323
+ return self .UPGRADE_SETTINGS .get (car_type_id , f"Unknown (0x{ car_type_id :02X} )" )
324
+ else :
325
+ return "Unknown"
326
+ except Exception as e :
327
+ print (f"Error extracting upgrade setting: { e } " )
328
+ return "Unknown"
329
+
248
330
def _extract_game_mode (self , data : bytes ) -> str :
249
331
"""
250
332
Extracts the game mode from the payload data.
@@ -371,10 +453,15 @@ def _parse_response(self, br: BinaryReader, original_data: bytes) -> Status:
371
453
server_name = self ._read_utf16_string (br )
372
454
info ["hostname" ] = server_name
373
455
374
- # Extract car type from the payload
375
- # Car type identifier at offset -8
376
- car_type = self ._extract_car_type (original_data )
377
- info ["car_type" ] = car_type
456
+ # Extract car type and upgrade information from the payload
457
+ # Car type identifier at offset -8 (includes upgrade settings)
458
+ car_type_full = self ._extract_car_type (original_data )
459
+ car_type_base = self ._extract_car_type_base (original_data )
460
+ upgrade_setting = self ._extract_upgrade_setting (original_data )
461
+
462
+ info ["car_type" ] = car_type_full # Full description with upgrades
463
+ info ["car_type_base" ] = car_type_base # Base car type only
464
+ info ["upgrade_setting" ] = upgrade_setting # Upgrade setting only
378
465
379
466
# Extract game mode from the payload
380
467
# Game mode identifier at offset -7
@@ -449,6 +536,8 @@ def _parse_response(self, br: BinaryReader, original_data: bytes) -> Status:
449
536
# Set defaults on error
450
537
info .setdefault ("hostname" , "Unknown Server" )
451
538
info .setdefault ("car_type" , "Unknown" )
539
+ info .setdefault ("car_type_base" , "Unknown" )
540
+ info .setdefault ("upgrade_setting" , "Unknown" )
452
541
info .setdefault ("game_mode" , "Unknown" )
453
542
info .setdefault ("map" , "Unknown Map" )
454
543
info .setdefault ("lap_count" , None )
0 commit comments