dethrace/tools/dethrace.lua

938 lines
34 KiB
Lua

-- Basic Dethrace Wireshark dissector
--
-- Installation instructions:
-- 1. Open Wireshark GUI
-- 2. Navigate to "Help -> About Wireshark" menu
-- 3. Click on "Folder" tab and click in the list of paths on "Personal Lua Plugins".
-- If the folder does not yet exist, a dialog will ask you to create it.
-- 4. Paste this lua script in the "Personal Lua Plugins" directory, or create a symlink.
-- 5. When modifying this script, reload all plug-ins with CTRL+SHIFT+L
dethrace_protocol = Proto("dethrace", "Dethrace Protocol")
DETHRACE_PORT = 12286
broadcast_types = {
[0x31] = "PING Request",
[0x32] = "PONG Response",
}
content_types = {
[0x00] = "SendMeDetails",
[0x01] = "Details",
[0x02] = "Join",
[0x03] = "NewPlayerList",
[0x04] = "GuaranteeReply",
[0x05] = "CarDetailsReq",
[0x06] = "CarDetails",
[0x07] = "Leave",
[0x08] = "Hosticide",
[0x09] = "RaceOver",
[0x0a] = "StatusReport",
[0x0b] = "StartRace",
[0x0c] = "HeadUp",
[0x0d] = "HostQuery",
[0x0e] = "HostReply",
[0x0f] = "Mechanics",
[0x10] = "NonCar_Info",
[0x11] = "TimeSync",
[0x12] = "Confirm",
[0x13] = "DisableCar",
[0x14] = "EnableCar",
[0x15] = "PowerUp",
[0x16] = "Recover",
[0x17] = "Scores",
[0x18] = "Wasted",
[0x19] = "Pedestrian",
[0x1a] = "Gameplay",
[0x1b] = "NonCarPosition",
[0x1c] = "CopInfo",
[0x1d] = "GameScores",
[0x1e] = "OilSpill",
[0x1f] = "CrushPoint",
[0x20] = "None",
}
-- FIXME: missing table: index -> car name
f_checksum = ProtoField.uint32("dethrace.checksum", "Checksum")
f_broadcast_magic = ProtoField.string("dethrace.broadcast_magic", "Broadcast Magic", base.ASCII)
f_broadcast_type = ProtoField.char("dethrace.broadcast_type", "Broadcast Type", base.NONE, broadcast_types)
f_terminator = ProtoField.char("dethrace.terminator", "Terminator")
f_message_magic = ProtoField.uint32("dethrace.magic", "Magic", base.HEX)
f_message_guarantee = ProtoField.uint32("dethrace.guarantee", "Guarantee Number")
f_message_sender = ProtoField.uint32("dethrace.sender", "Sender Player ID")
f_message_version = ProtoField.int32("dethrace.version", "Version")
f_message_timestamp = ProtoField.uint32("dethrace.time", "Sender Time Stamp")
f_message_count = ProtoField.uint16("dethrace.count", "Count Contents")
f_message_size = ProtoField.uint16("dethrace.size", "Overall Size")
f_contents_size = ProtoField.uint8("dethrace.content_size", "Content Size")
f_contents_type = ProtoField.uint8("dethrace.content_type", "Content Type", base.DEC, content_types)
player_statuses = {
[0] = "Unknown",
[1] = "Ready",
[2] = "Loading",
[3] = "Wrecks Gallery",
[4] = "Summary",
[5] = "Not Responding",
[6] = "Racing",
[7] = "Main Menu",
[8] = "Recovering",
[9] = "Action Replay",
}
f_player_status = ProtoField.uint32("dethrace.player_status", "Player Status", base.DEC, player_statuses)
function create_message_size_type(parent, message, name)
local message_type = message(1, 1):uint()
local tree = parent:add(dethrace_protocol, message(), string.format("%s (%d)", name, message_type))
tree:add_le(f_contents_size, message(0, 1))
tree:add_le(f_contents_type, message(1, 1))
result = {}
result["tree"] = tree
result["size"] = message(0, 1):le_uint()
result["type"] = message(1, 1):le_uint()
return result
end
f_pd_player_info = ProtoField.bytes("dethrace.pd_info", "PD Net Info")
function dissect_pd_player_info(tree, data)
tree:add_le(f_pd_player_info, data(0, 16))
return 16
end
f_float = ProtoField.float("dethrace.float", "Floating value")
function dissect_br_matrix34(parent, data, name)
local matrix_tree = parent:add(name, data(0, 48))
local offset = 0
for row_i = 0, 3 do
local row_tree = matrix_tree:add(string.format("m[%d]", row_i), data(offset, 12))
for col_i = 0, 2 do
local f = data(offset, 4):le_float()
row_tree:add_le(f_float, data(offset, 4)):set_text(string.format("m[%d][%d] = %f", row_i, col_i, f))
offset = offset + 4
end
end
return offset
end
function dissect_reduced_matrix(parent, data, name)
local matrix_tree = parent:add(name, data(0, 36))
local offset = 0
for row_i = 0, 2 do
local row_tree = matrix_tree:add(string.format("m[%d]", row_i), data(offset, 12))
for col_i = 0, 2 do
local f = data(offset, 4):le_float()
row_tree:add_le(f_float, data(offset, 4)):set_text(string.format("m[%d][%d] = %f", row_i, col_i, f))
offset = offset + 4
end
end
return offset
end
function dissect_br_vector3(parent, data, name)
local tree = parent:add(name, data(0, 12))
local offset = 0
for i = 0, 2 do
local f = data(offset, 4):le_float()
tree:add_le(f_float, data(offset, 4)):set_text(string.format("v[%d] = %f", i, f))
offset = offset + 4
end
return offset
end
function dissect_message_sendmedetails(tree, message)
-- tNet_message_send_me_details (0)
local t = create_message_size_type(tree, message, "Send Me Details")["tree"]
-- finished
end
race_tracks = {
[0] = "Sumo",
[1] = "Coliseum",
[2] = "Maim Street",
[3] = "Coastal Carnage",
[4] = "Fridge Racer",
[5] = "Death Valley",
[6] = "Off Quay",
[7] = "Industrial Injury",
[8] = "Avenue of Atrocities",
[9] = "Terror on the Trails",
[10] = "MagnaChem Mayhem",
[11] = "Downtown Devastation",
}
game_stages = {
[0] = "Starting",
[1] = "Ready",
[2] = "Playing",
}
game_types = {
[0] = "Driven To Destruction",
[1] = "Car Crusher",
[2] = "Carnage",
[3] = "Checkpoint Stampede",
[4] = "Sudden Death",
[5] = "Terminal Tag",
[6] = "Fox 'n' Hounds",
}
details_options_start_types = {
[0] = "Random Start",
[1] = "Grid Start",
}
details_options_starting_money = {
[0] = "$0",
[1] = "$2,000",
[2] = "$5,000",
[3] = "$10,000",
[4] = "$20,000",
}
details_options_car_choices = {
[0] = "Eagle",
[1] = "Hawk",
[2] = "Both",
[3] = "All",
}
f_details_host_name = ProtoField.stringz("dethrace.details.host_name", "Host Name")
f_details_host_id = ProtoField.uint32("dethrace.details.host_id", "Host ID")
f_details_num_players = ProtoField.uint32("dethrace.details.num_players", "Number Players")
f_details_start_race = ProtoField.uint32("dethrace.details.start_race", "Start Race", base.DEC, race_tracks)
f_details_no_races_yet = ProtoField.bool("dethrace.details.no_races_yet", "No Races Yet")
f_details_status = ProtoField.uint32("dethrace.details.game_status", "Status", base.DEC, game_stages)
f_details_type = ProtoField.uint32("dethrace.details.type", "Type", base.DEC, game_types)
f_details_option_show_players_on_map = ProtoField.bool("dethrace.details.options.players_on_map", "Show Players On Map")
f_details_option_show_peds_on_map = ProtoField.bool("dethrace.details.options.peds_on_map", "Show Peds On Map")
f_details_option_enable_text_messages = ProtoField.bool("dethrace.details.options.text_messages", "Enable Text Messages")
f_details_option_show_powerups_on_map = ProtoField.bool("dethrace.details.options.powerups_on_map", "Show Powerups On Map")
f_details_option_powerup_respawn = ProtoField.bool("dethrace.details.options.powerup_respawn", "Powerup Respawn")
f_details_option_open_game = ProtoField.bool("dethrace.details.options.open_game", "Open Game")
f_details_option_starting_money_index = ProtoField.uint32("dethrace.details.options.starting_money", "Starting Money", base.DEC, details_options_starting_money)
f_details_option_start_type = ProtoField.uint32("dethrace.details.options.start_Type", "Start Type", base.DEC, details_options_start_types)
f_details_option_race_end_target = ProtoField.uint32("dethrace.details.options.end_target", "Race End Target")
f_details_option_random_car_choice = ProtoField.bool("dethrace.details.options.random_car", "Random Cars")
f_details_option_race_sequence_type = ProtoField.uint32("dethrace.details.options.race_sequence_type", "Race Sequence Type")
f_details_option_car_choice = ProtoField.uint32("dethrace.details.options.car_choice", "Car Choice", base.DEC, details_options_car_choices)
function dissect_game_details(parent, data)
-- tNet_game_options
local tree = parent:add("Options", data(48))
tree:add_le(f_details_option_show_players_on_map, data(0, 4))
tree:add_le(f_details_option_show_peds_on_map, data(4, 4))
tree:add_le(f_details_option_enable_text_messages, data(8, 4))
tree:add_le(f_details_option_show_powerups_on_map, data(12, 4))
tree:add_le(f_details_option_powerup_respawn, data(16, 4))
tree:add_le(f_details_option_open_game, data(20, 4))
tree:add_le(f_details_option_starting_money_index, data(24, 4))
tree:add_le(f_details_option_start_type, data(28, 4))
tree:add_le(f_details_option_race_end_target, data(32, 4))
tree:add_le(f_details_option_random_car_choice, data(36, 4))
tree:add_le(f_details_option_race_sequence_type, data(40, 4))
tree:add_le(f_details_option_car_choice, data(44, 4))
return 48
end
function dissect_message_details(tree, message)
-- tNet_message_my_details (1)
local t = create_message_size_type(tree, message, "Details")["tree"]
local offset = 2
offset = offset + dissect_pd_player_info(t, message(offset))
t:add(f_details_host_name, message(offset, 32))
offset = offset + 4
t:add_le(f_details_host_id, message(offset, 4))
offset = offset + 4
t:add_le(f_details_num_players, message(offset, 4))
offset = offset + 4
t:add_le(f_details_start_race, message(offset, 4))
offset = offset + 4
t:add_le(f_details_no_races_yet, message(offset, 4))
offset = offset + 4
t:add_le(f_details_status, message(offset, 4))
offset = offset + 4
offset = offset + dissect_game_details(t, message(offset))
t:add_le(f_details_type, message(offset, 4))
offset = offset + 4
end
f_join_timestamp = ProtoField.uint32("dethrace.join.timestamp", "Timestamp")
f_join_last_heard = ProtoField.uint32("dethrace.join.time_last_heard", "Last heard timestamp")
f_join_reposition_time = ProtoField.uint32("dethrace.join.time_reposition", "Reposition timestamp")
f_join_last_waste_time = ProtoField.uint32("dethrace.join.time_waste", "Last waste message timestamp")
f_join_host = ProtoField.bool("dethrace.join.host", "Host")
f_join_playerid = ProtoField.uint32("dethrace.join.playerid", "Player ID")
f_join_player_name = ProtoField.stringz("dethrace.join.player_name", "Player Name")
f_join_car = ProtoField.uint32("dethrace.join.car", "Car") -- FIXME: use table index -> car name
f_join_grid = ProtoField.uint32("dethrace.join.grid", "Grid Index")
f_join_grid_set = ProtoField.bool("dethrace.join.grid_set", "Grid Position Set")
f_join_opponent_index = ProtoField.uint32("dethrace.join.opponent_index", "Opponent List Index")
f_join_wait_confirm = ProtoField.uint32("dethrace.join.wait_confirm", "Awaiting Confirmation") -- FIXME: find out values
f_join_score = ProtoField.uint32("dethrace.join.score", "Score")
f_join_credits = ProtoField.uint32("dethrace.join.credits", "Credits")
f_join_wasted = ProtoField.bool("dethrace.join.wasted", "Wasted")
f_join_wasteage_attributed = ProtoField.bool("dethrace.join.wasteage_attributed", "Wasteage Attributed") -- FIXME: find out what this is
f_join_name_not_clipped = ProtoField.bool("dethrace.join.name_not_clipped", "Name Not Clipped") -- FIXME: find out what this is
f_join_race_stuff_initialized = ProtoField.bool("dethrace.join.race_stuff_initialized", "Race Stuff Initialized")
f_join_played = ProtoField.bool("dethrace.join.played", "Played")
f_join_won = ProtoField.bool("dethrace.join.won", "Won")
f_join_next_car_index = ProtoField.uint32("dethrace.join.next_car_index", "Next Car Index") -- FIXME: find out what this is
f_join_games_score = ProtoField.uint32("dethrace.join.games_score", "Games Score")
f_join_last_score_index = ProtoField.uint32("dethrace.join.last_score_index", "Last Score Index")
f_join_last_score_index = ProtoField.uint32("dethrace.join.last_score_index", "Last Score Index")
f_join_car_ptr = ProtoField.uint64("dethrace.join.car_ptr", "Car Spec (pointer)")
function dissect_game_player_info(tree, data)
-- tNet_game_player_info
local offset = dissect_pd_player_info(tree, data)
tree:add_le(f_join_timestamp, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_last_heard, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_reposition_time, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_last_waste_time, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_host, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_playerid, data(offset, 4))
offset = offset + 4
tree:add(f_join_player_name, data(offset, 32))
offset = offset + 32
tree:add_le(f_player_status, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_car, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_grid, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_grid_set, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_opponent_index, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_wait_confirm, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_score, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_credits, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_wasted, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_wasteage_attributed, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_name_not_clipped, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_race_stuff_initialized, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_played, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_won, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_next_car_index, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_games_score, data(offset, 4))
offset = offset + 4
tree:add_le(f_join_last_score_index, data(offset, 4))
offset = offset + 4
offset = offset + dissect_br_matrix34(tree, data(offset), "Initial Position")
assert(offset == 0xbc, "offset of car spec ptr")
tree:add_le(f_join_car_ptr, data(offset, 4))
tree:set_len(offset)
end
function dissect_message_join(tree, message)
-- tNet_message_join (2)
local t = create_message_size_type(tree, message, "Join")["tree"]
local offset = 4
dissect_game_player_info(t:add("Player Info", message(offset)), message(offset))
end
newerplayerlist_count_magics = {
[-1] = "-1 (Selected Car Unavailable)",
[0] = "0 (Game Already Started)",
}
f_newplayerlist_count = ProtoField.int32("dethrace.newplayerlist.count", "Number Of Players")--, base.DEC, newerplayerlist_count_magics)
f_newplayerlist_index = ProtoField.int32("dethrace.newplayerlist.index", "This Index")
f_newplayerlist_batch_number = ProtoField.int32("dethrace.newplayerlist.batch_number", "Batch Number")
function dissect_message_newplayerlist(tree, message)
-- tNet_message_new_player_list (3)
local t = create_message_size_type(tree, message, "New Player List")["tree"]
local offset = 4
local t_count = t:add_le(f_newplayerlist_count, message(offset, 4))
if message(offset, 4):int() == 0 then
t_count:set_text("Number Of Players: Game Already Started (0)")
elseif message(offset, 4):int() == -1 then
t_count:set_text("Number Of Players: Car Unavailable (-1)")
end
offset = offset + 4
t:add_le(f_newplayerlist_index, message(offset, 4))
offset = offset + 4
t:add_le(f_newplayerlist_batch_number, message(offset, 4))
offset = offset + 4
dissect_game_player_info(t:add("Player", message(offset)), message(offset))
-- finished
end
f_guaranteereply_number = ProtoField.uint32("dethrace.guaranteereply.number", "Guarantee Number")
function dissect_message_guaranteereply(tree, message)
-- tNet_message_guarantee_reply (4)
local t = create_message_size_type(tree, message, "Guarantee Reply")["tree"]
t:add_le(f_guaranteereply_number, message(4, 4))
-- FIXME: add reference to packet with this reply number
-- finished
end
function dissect_message_cardetailsreq(tree, message)
-- tNet_message_car_details_req (5)
local t = create_message_size_type(tree, message, "Car Details Req")["tree"]
-- finished
end
f_cardetails_count = ProtoField.uint32("dethrace.cardetails.count", "Car Count")
f_cardetails_car = ProtoField.uint32("dethrace.cardetails.car_index", "Car") -- FIXME: use table index -> car name
f_cardetails_owner = ProtoField.stringz("dethrace.cardetails.owner", "Owner")
function dissect_message_cardetails(tree, message)
-- tNet_message_car_details (6)
local t = create_message_size_type(tree, message, "Car Details")["tree"]
local offset = 4
t:add_le(f_cardetails_count, message(4, 4))
offset = offset + 4
for i = 0, 5 do
local car_detail = message(offset, 20)
local subtree = t:add(car_detail, string.format("Car[%d]", i))
subtree:add_le(f_cardetails_car, car_detail(0, 4))
subtree:add(f_cardetails_owner, car_detail(4, 16))
offset = offset + 20
end
-- finished
end
function dissect_message_leave(tree, message)
-- tNet_message_leave (7)
local t = create_message_size_type(tree, message, "Leave")["tree"]
-- finished
end
function dissect_message_hosticide(tree, message)
-- tNet_message_host_pissing_off (8)
local t = create_message_size_type(tree, message, "Hosticide")["tree"]
-- finished
end
raceover_reasons = {
[-1] = eRace_not_over_yet,
[ 0] = eRace_over_laps,
[ 1] = eRace_over_peds,
[ 2] = eRace_over_opponents,
[ 3] = eRace_over_abandoned,
[ 4] = eRace_over_out_of_time,
[ 5] = eRace_over_demo,
[ 6] = eRace_over_network_victory,
[ 7] = eRace_over_network_loss,
}
f_raceover_reason = ProtoField.uint32("dethrace.raceover.reason", "Reason", base.DEC, raceover_reasons)
function dissect_message_raceover(tree, message)
-- tNet_message_race_over (9)
local t = create_message_size_type(tree, message, "Race Over (UNTESTED)")["tree"]
t:add_le(f_raceover_reason, message(4, 4))
-- finished
end
function dissect_message_statusreport(tree, message)
-- tNet_message_status_report (10)
local t = create_message_size_type(tree, message, "Status Report")["tree"]
t:add_le(f_player_status, message(4, 4))
-- finished
end
f_startrace_count = ProtoField.uint32("dethrace.startrace.count", "Car Count", base.DEC)
f_startrace_racing = ProtoField.bool("dethrace.startrace.racing", "Racing")
f_startrace_next = ProtoField.uint32("dethrace.startrace.next", "Next Race", base.DEC, race_tracks)
f_startrace_grid_index = ProtoField.uint32("dethrace.startrace.grid.index", "Index", base.DEC)
f_startrace_grid_next_car_index = ProtoField.uint32("dethrace.startrace.grid.next_car_index", "Next Car Index", base.DEC)
function dissect_message_startrace(tree, message)
-- tNet_message_start_race (11)
local t = create_message_size_type(tree, message, "Start Race")["tree"]
local offset = 4
t:add_le(f_startrace_count, message(offset, 4))
offset = offset + 4
t:add_le(f_startrace_racing, message(offset, 4))
offset = offset + 4
t:add_le(f_startrace_next, message(offset, 4))
offset = offset + 4
for i = 0, 5 do
local start = offset
local subtree = t:add(message(offset), string.format("Grid Car[%d]", i))
subtree:add_le(f_startrace_grid_index, message(offset, 4))
offset = offset + 4
subtree:add_le(f_startrace_grid_next_car_index, message(offset, 4))
offset = offset + 4
offset = offset + dissect_br_matrix34(subtree, message(offset), "Matrix")
subtree:set_len(offset - start)
end
-- FIXME
end
f_headup_text = ProtoField.stringz("dethrace.headup.message", "Message")
function dissect_message_headup(tree, message)
-- tNet_message_headup (12)
local t = create_message_size_type(tree, message, "Head-Up (UNTESTED)")["tree"]
t:add(f_headup_text, message(4, 128))
-- finished
end
function dissect_message_hostquery(tree, message)
-- tNet_message_host_query (13)
local t = create_message_size_type(tree, message, "Host Query")["tree"]
-- finished
end
f_hostreply_started = ProtoField.bool("dethrace.hostreply.started", "Race Started")
f_hostreply_race = ProtoField.uint32("dethrace.hostreply.race", "Race", base.DEC, race_tracks)
f_hostreply_pending = ProtoField.uint32("dethrace.hostreply.pending", "Pending Race", base.DEC, race_tracks)
function dissect_message_hostreply(tree, message)
-- tNet_message_host_reply (14)
local t = create_message_size_type(tree, message, "Host Reply (UNTESTED)")["tree"]
t:add(f_hostreply_started, message(4, 4))
t:add(f_hostreply_race, message(8, 4))
t:add(f_hostreply_pending, message(12, 4))
-- finished
end
f_mechanics_id = ProtoField.uint32("dethrace.mechanics.id", "ID")
f_mechanics_time = ProtoField.uint32("dethrace.mechanics.time", "Time")
f_mechanics_d = ProtoField.uint8("dethrace.mechanics.d", "D")
f_mechanics_carcontrols = ProtoField.uint32("dethrace.mechanics.carcontrols", "Car Controls") -- FIXME: bitmask
f_mechanics_coll_time = ProtoField.uint32("dethrace.mechanics.coll_time", "Car Control Collision Time")
f_mechanics_curvature = ProtoField.int16("dethrace.mechanics.curvature", "Curvature")
f_mechanics_revs = ProtoField.uint16("dethrace.mechanics.revs", "Revs")
f_mechanics_front = ProtoField.float("dethrace.mechanics.front", "Front")
f_mechanics_back = ProtoField.float("dethrace.mechanics.back", "Back")
f_mechanics_repair_time = ProtoField.uint32("dethrace.mechanics.repair_time", "Repair Time")
f_mechanics_powerups = ProtoField.int16("dethrace.mechanics.powerups", "Powerups")
f_mechanics_damage = ProtoField.uint8("dethrace.mechanics.damage", "Damage")
function dissect_message_mechanics(tree, message)
-- tNet_message_mechanics_info (15)
local t = create_message_size_type(tree, message, "Mechanics")["tree"]
local offset = 4
t:add_le(f_mechanics_id, message(offset, 4))
offset = offset + 4
t:add_le(f_mechanics_time, message(offset, 4))
offset = offset + 4
offset = offset + dissect_reduced_matrix(t, message(offset), "Matrix")
offset = offset + dissect_br_vector3(t, message(offset), "Velocity")
offset = offset + dissect_br_vector3(t, message(offset), "Omega")
do
local sub = t:add("d", message(offset, 4))
for i = 0, 3 do
local item = sub:add_le(f_mechanics_d, message(offset, 1))
item:set_text(string.format("d[%d] = %d", i, message(offset, 1):le_uint()))
offset = offset + 1
end
end
t:add_le(f_mechanics_carcontrols, message(offset, 4))
offset = offset + 4
t:add_le(f_mechanics_coll_time, message(offset, 4))
offset = offset + 4
t:add_le(f_mechanics_curvature, message(offset, 2))
offset = offset + 2
t:add_le(f_mechanics_revs, message(offset, 2))
offset = offset + 2
t:add_le(f_mechanics_front, message(offset, 4))
offset = offset + 4
t:add_le(f_mechanics_back, message(offset, 4))
offset = offset + 4
t:add_le(f_mechanics_repair_time, message(offset, 4))
offset = offset + 4
t:add_le(f_mechanics_powerups, message(offset, 2))
offset = offset + 2
do
local sub = t:add("Damage", message(offset, 4))
-- FIXME: use names of damage system (in addition to index)
for i = 0, 11 do
local item = sub:add_le(f_mechanics_damage, message(offset, 1))
item:set_text(string.format("damage[%d] = %d", i, message(offset, 1):le_uint()))
offset = offset + 1
end
end
t:add_le(f_mechanics_powerups, message(offset, 2))
offset = offset + 2
-- omit 2 bytes of padding
offset = offset + 2
if offset < message:len() then
do
local sub = t:add("Wheel Damage Offset", message(offset, 4))
for i = 0, 3 do
local item = sub:add_le(f_float, message(offset, 4))
item:set_text(string.format("offset[%d] = %d", i, message(offset, 4):le_float()))
offset = offset + 4
end
end
end
end
function dissect_message_noncar_info(tree, message)
-- tNet_message_non_car_info (16)
local t = create_message_size_type(tree, message, "Non-Car Info (INCOMPLETE)")["tree"]
-- FIXME
end
f_timesync_time = ProtoField.uint32("dethrace.timesync.start_time", "Race Start Time")
function dissect_message_timesync(tree, message)
-- tNet_message_time_sync (17)
local t = create_message_size_type(tree, message, "Time Sync")["tree"]
local offset = 4
t:add_le(f_timesync_time, message(offset, 4))
offset = offset + 4
end
function dissect_message_confirm(tree, message)
-- tNet_message_players_confirm (18)
local t = create_message_size_type(tree, message, "Confirm (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_disablecar(tree, message)
-- tNet_message_disable_car (19)
local t = create_message_size_type(tree, message, "Disable Car (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_enablecar(tree, message)
-- tNet_message_enable_car (20)
local t = create_message_size_type(tree, message, "Enable Car (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_powerup(tree, message)
-- tNet_message_powerup (21)
local t = create_message_size_type(tree, message, "Power-Up (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_recover(tree, message)
-- tNet_message_recover (22)
local t = create_message_size_type(tree, message, "Recover (INCOMPLETE)")["tree"]
-- FIXME
end
f_scores_played = ProtoField.bool("dethrace.score.played", "Played")
f_scores_won = ProtoField.bool("dethrace.scored.won", "Won")
f_scores_score = ProtoField.int32("dethrace.scored.score", "Score")
function dissect_message_scores(tree, message)
-- tNet_message_game_scores (23)
local header = create_message_size_type(tree, message, "Scores")
local t = header["tree"]
local offset = 4
for i = 0, 5 do
local subtree = t:add(string.format("Scores[%d]", i), message(offset, 12))
subtree:add_le(f_scores_played, message(offset, 4))
offset = offset + 4
subtree:add_le(f_scores_won, message(offset, 4))
offset = offset + 4
subtree:add_le(f_scores_score, message(offset, 4))
offset = offset + 4
end
end
function dissect_message_wasted(tree, message)
-- tNet_message_wasted (24)
local t = create_message_size_type(tree, message, "Wasted (INCOMPLETE)")["tree"]
-- FIXME
end
f_ped_instruction = ProtoField.int8("dethrace.ped.instruction", "Action Instruction")
f_ped_flags = ProtoField.int8("dethrace.ped.flags", "Flags")
f_ped_index = ProtoField.int16("dethrace.ped.index", "Index")
function dissect_message_pedestrian(tree, message)
-- tNet_message_pedestrian (25)
local t = create_message_size_type(tree, message, "Pedestrian (INCOMPLETE)")["tree"]
local offset = 2
t:add_le(f_ped_instruction, message(offset, 1))
offset = offset + 1
t:add_le(f_ped_flags, message(offset, 1))
offset = offset + 1
t:add_le(f_ped_index, message(offset, 2))
offset = offset + 2
-- padding
offset = offset + 2
offset = offset + dissect_br_vector3(t, message(offset, 12), "Position")
t:add_le(f_float, message(offset, 4)):set_text(string.format("Speed: %f", message(offset, 4):le_float()))
offset = offset + 4
--offset = offset + dissect_br_vector3(t, message(offset, 12), "to_pos")
-- FIXME
end
function dissect_message_gameplay(tree, message)
-- tNet_message_gameplay (26)
local t = create_message_size_type(tree, message, "Gameplay (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_noncarposition(tree, message)
-- tNet_message_non_car_position (27)
local t = create_message_size_type(tree, message, "Non-Car Position (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_copinfo(tree, message)
-- tNet_message_cop_info (28)
local t = create_message_size_type(tree, message, "Cop Info (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_gamescores(tree, message)
-- tNet_message_oil_spill (29)
local t = create_message_size_type(tree, message, "Game Scores (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_oilspill(tree, message)
-- tNet_message_oil_spill (30)
local t = create_message_size_type(tree, message, "Oil Spill (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_crushpoint(tree, message)
-- tNet_message_crush_point (31)
local t = create_message_size_type(tree, message, "Crush Point (INCOMPLETE)")["tree"]
-- FIXME
end
function dissect_message_none(tree, message)
-- none (32)
local t = create_message_size_type(tree, message, "None")["tree"]
-- finished
end
dethrace_protocol.fields = {
f_checksum, f_broadcast_magic, f_broadcast_type, f_terminator,
f_float,
f_pd_player_info,
f_message_magic, f_message_guarantee, f_message_sender, f_message_version, f_message_timestamp, f_message_count, f_message_size,
f_contents_size, f_contents_type,
-- 2 (join)
f_join_timestamp, f_join_last_heard, f_join_reposition_time, f_join_last_waste_time,
f_join_host, f_join_playerid, f_join_player_name, f_join_car, f_join_grid, f_join_grid_set,
f_join_opponent_index, f_join_wait_confirm, f_join_score, f_join_credits, f_join_wasted,
f_join_wasteage_attributed, f_join_name_not_clipped, f_join_race_stuff_initialized, f_join_played,
f_join_won, f_join_next_car_index, f_join_games_score, f_join_last_score_index, f_join_car_ptr,
-- 3 (newplayerlist)
f_newplayerlist_count, f_newplayerlist_index, f_newplayerlist_batch_number,
f_details_host_name, f_details_host_id, f_details_num_players, f_details_start_race, f_details_no_races_yet, f_details_status,
f_details_option_show_players_on_map, f_details_option_show_peds_on_map, f_details_option_enable_text_messages, f_details_option_show_powerups_on_map, f_details_option_powerup_respawn, f_details_option_open_game, f_details_option_starting_money_index, f_details_option_start_type, f_details_option_race_end_target, f_details_option_random_car_choice, f_details_option_race_sequence_type, f_details_option_car_choice,
f_details_type,
f_guaranteereply_number,
f_cardetails_count, f_cardetails_car, f_cardetails_owner,
f_raceover_reason,
f_statusreport_status,
f_startrace_count, f_startrace_racing, f_startrace_next, f_startrace_grid_index, f_startrace_grid_next_car_index,
f_headup_text,
f_hostreply_started, f_hostreply_race, f_hostreply_pending,
f_player_status,
f_mechanics_id, f_mechanics_time, f_mechanics_d, f_mechanics_carcontrols, f_mechanics_coll_time,
f_mechanics_curvature, f_mechanics_revs, f_mechanics_front, f_mechanics_back, f_mechanics_repair_time,
f_mechanics_powerups, f_mechanics_damage,
f_timesync_time,
f_scores_played, f_scores_won, f_scores_score,
f_ped_instruction, f_ped_flags, f_ped_index,
}
dethrace_message_dissectors = {
[ 0] = dissect_message_sendmedetails,
[ 1] = dissect_message_details,
[ 2] = dissect_message_join,
[ 3] = dissect_message_newplayerlist,
[ 4] = dissect_message_guaranteereply,
[ 5] = dissect_message_cardetailsreq,
[ 6] = dissect_message_cardetails,
[ 7] = dissect_message_leave,
[ 8] = dissect_message_hosticide,
[ 9] = dissect_message_raceover,
[10] = dissect_message_statusreport,
[11] = dissect_message_startrace,
[12] = dissect_message_headup,
[13] = dissect_message_hostquery,
[14] = dissect_message_hostreply,
[15] = dissect_message_mechanics,
[16] = dissect_message_noncar_info,
[17] = dissect_message_timesync,
[18] = dissect_message_confirm,
[19] = dissect_message_disablecar,
[20] = dissect_message_enablecar,
[21] = dissect_message_powerup,
[22] = dissect_message_recover,
[23] = dissect_message_scores,
[24] = dissect_message_wasted,
[25] = dissect_message_pedestrian,
[26] = dissect_message_gameplay,
[27] = dissect_message_noncarposition,
[28] = dissect_message_copinfo,
[29] = dissect_message_gamescores,
[30] = dissect_message_oilspill,
[31] = dissect_message_crushpoint,
[32] = dissect_message_none,
}
function dethrace_protocol.dissector(buffer, pinfo, tree)
if buffer:bytes(4, 7) == ByteArray.new("43 57 39 35 4d 53 47") then -- "CW95MSG"
local t = tree:add(dethrace_protocol, buffer(), "DethRace Broadcast Protocol")
pinfo.cols.protocol = "DRBC"
pinfo.cols.info = string.format("Broadcast: %s", broadcast_types[buffer(11, 1):uint()])
t:add(f_checksum, buffer(0, 4))
t:add(f_broadcast_magic, buffer(4, 7))
t:add(f_broadcast_type, buffer(11, 1))
t:add(f_terminator, buffer(12, 1))
elseif buffer(4, 4):le_uint() == 0x763a5058 then -- "XP:v"
pinfo.cols.protocol = "DR";
do
local t = tree:add(dethrace_protocol, buffer(0, 28), "DethRace Message Protocol")
t:add(f_checksum, buffer(0, 4))
t:add_le(f_message_magic, buffer(4, 4))
t:add_le(f_message_guarantee, buffer(8, 4))
t:add_le(f_message_sender, buffer(12, 4))
t:add_le(f_message_version, buffer(16, 4))
t:add_le(f_message_timestamp, buffer(20, 4))
t:add_le(f_message_count, buffer(24, 2))
t:add_le(f_message_size, buffer(26, 2))
end
local sender_type_str
if (pinfo.src_port == DETHRACE_PORT) then
sender_type_str = "HOST"
else
sender_type_str = "CLIENT"
end
local message = buffer(28)
local message_type = message(1, 1):uint()
pinfo.cols.info = string.format("[%s] %s (%d)", sender_type_str, content_types[message_type], message_type)
dethrace_message_dissectors[message_type](tree, message)
end
end
-- load the udp.port table
udp_table = DissectorTable.get("udp.port")
-- register our protocol to handle udp port 39456
udp_table:add(DETHRACE_PORT, dethrace_protocol)