From 0e7e1f807854395f29d88469f9483d80db51c0e1 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 25 Mar 2025 18:15:41 -0400 Subject: [PATCH] More various 7.2 packet fixes, attempt to check recieved packet size --- resources/tests/player_setup.bin | Bin 2784 -> 2808 bytes resources/tests/player_spawn.bin | Bin 656 -> 664 bytes src/lobby/ipc/mod.rs | 12 +++++++- src/packet/compression.rs | 11 +++++++ src/packet/ipc.rs | 5 ++- src/packet/parsing.rs | 6 ++-- src/world/ipc/mod.rs | 51 +++++++++++++++++++++++-------- 7 files changed, 66 insertions(+), 19 deletions(-) diff --git a/resources/tests/player_setup.bin b/resources/tests/player_setup.bin index 602220c6c91e817b6b275070ab6dd7bcd658a33c..57101b7da7222addbf6e30eb9a858213c4638cb6 100644 GIT binary patch literal 2808 zcmd-L3-A$Va9}_O&&x^#U}C;72G!{lR-loLOpJ_-9E^<2oDlO_7#LWVVp_#&!~j&s zh$%4$MJRRU0;r#u2BF`m<)kxyj@*viXZ%k4QOiWi%<)MqOU+A6R0vNjDoV|x%q-$` zI}oRARMlt*5ETM!M5&^-B4BZi3OK-A9#p|m-mnV+<^l2%AJkqI255qZ){^`XaRmlw zI|AxHs3HXhNV;Tz*3D3!0t2WC0t8SBZV1#+XzGTxg%rR_MDe+^hqpB%}Y#F2v003O3kB( zTO8Z|TBjZGgES71`3=A7vIv7(J82k$rofsGz z9C&U?9G=R}&whZL7X?oN@)4L1%m$kUC+vbV!V`jng*<~(Qo<8HVSvebOiC;u)c_Ac BE_VO` literal 656 zcmccWc7}-o2zbK+e8d?XKuiXIAOrzsFv;)$LP5xr4B8Bg$O6Q%9{`m-m=7dCn1O)> zhy~OI)In^X2_O;-7#SE@Vi_11p%$pX#7Akmgn*HVk`n_%gTt8+vBMw>Lm8M*Fe8wU zz`)1?@=<|LVp(clVxmHLVo_0Q9;zIO$H>U2$HY{{z|ffl(%s3*%Fw)$fgy#F6{Z#d DrX(08 diff --git a/src/lobby/ipc/mod.rs b/src/lobby/ipc/mod.rs index df94cea..1b10542 100644 --- a/src/lobby/ipc/mod.rs +++ b/src/lobby/ipc/mod.rs @@ -21,7 +21,17 @@ use crate::{ pub type ClientLobbyIpcSegment = IpcSegment; -impl ReadWriteIpcSegment for ClientLobbyIpcSegment {} +impl ReadWriteIpcSegment for ClientLobbyIpcSegment { + fn calc_size(&self) -> u32 { + // 16 is the size of the IPC header + 16 + match self.op_code { + ClientLobbyIpcType::RequestCharacterList => 24, + ClientLobbyIpcType::RequestEnterWorld => 32, + ClientLobbyIpcType::ClientVersionInfo => 1144, + ClientLobbyIpcType::LobbyCharacterAction => 496, + } + } +} // TODO: make generic impl Default for ClientLobbyIpcSegment { diff --git a/src/packet/compression.rs b/src/packet/compression.rs index 6258e30..4b7565c 100644 --- a/src/packet/compression.rs +++ b/src/packet/compression.rs @@ -47,11 +47,22 @@ pub(crate) fn decompress( let mut cursor = Cursor::new(&data); for _ in 0..header.segment_count { + let current_position = cursor.position(); segments.push(PacketSegment::read_options( &mut cursor, endian, (encryption_key,), )?); + let new_position = cursor.position(); + let expected_size = segments.last().unwrap().calc_size() as u64; + let actual_size = new_position - current_position; + + if expected_size != actual_size { + tracing::warn!( + "The segment {:#?} does not match the size in calc_size()! (expected {expected_size} got {actual_size}", + segments.last() + ); + } } Ok(segments) diff --git a/src/packet/ipc.rs b/src/packet/ipc.rs index d1c6025..47c02df 100644 --- a/src/packet/ipc.rs +++ b/src/packet/ipc.rs @@ -6,9 +6,7 @@ pub trait ReadWriteIpcSegment: { /// Calculate the size of this Ipc segment *including* the 16 byte header. /// When implementing this, please use the size seen in retail instead of guessing. - fn calc_size(&self) -> u32 { - unimplemented!() - } + fn calc_size(&self) -> u32; } /// An IPC packet segment. @@ -47,6 +45,7 @@ where /// Unknown purpose, but usually 0. pub unk2: u8, /// The opcode for this segment. + #[br(dbg)] pub op_code: OpCode, #[brw(pad_before = 2)] // empty /// Unknown purpose, but safe to keep 0. diff --git a/src/packet/parsing.rs b/src/packet/parsing.rs index 0b37d75..53e9d32 100644 --- a/src/packet/parsing.rs +++ b/src/packet/parsing.rs @@ -15,7 +15,7 @@ use super::{ #[binrw] #[brw(repr = u16)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ConnectionType { None = 0x0, Zone = 0x1, @@ -108,7 +108,7 @@ pub struct PacketSegment { } impl PacketSegment { - fn calc_size(&self) -> u32 { + pub fn calc_size(&self) -> u32 { let header = std::mem::size_of::() * 4; header as u32 + match &self.segment_type { @@ -118,7 +118,7 @@ impl PacketSegment { SegmentType::KeepAlive { .. } => 0x8, SegmentType::KeepAliveResponse { .. } => 0x8, SegmentType::ZoneInitialize { .. } => 40, - SegmentType::InitializeSession { .. } => todo!(), + SegmentType::InitializeSession { .. } => 40, SegmentType::CustomIpc { data } => data.calc_size(), } } diff --git a/src/world/ipc/mod.rs b/src/world/ipc/mod.rs index 5b4d754..587b100 100644 --- a/src/world/ipc/mod.rs +++ b/src/world/ipc/mod.rs @@ -61,7 +61,34 @@ use crate::packet::ReadWriteIpcSegment; pub type ClientZoneIpcSegment = IpcSegment; -impl ReadWriteIpcSegment for ClientZoneIpcSegment {} +impl ReadWriteIpcSegment for ClientZoneIpcSegment { + fn calc_size(&self) -> u32 { + // 16 is the size of the IPC header + 16 + match self.op_code { + ClientZoneIpcType::InitRequest => 120, + ClientZoneIpcType::FinishLoading => 72, + ClientZoneIpcType::Unk1 => 32, + ClientZoneIpcType::Unk2 => 16, + ClientZoneIpcType::Unk3 => 8, + ClientZoneIpcType::Unk4 => 8, + ClientZoneIpcType::SetSearchInfoHandler => 8, + ClientZoneIpcType::Unk5 => 8, + ClientZoneIpcType::SocialListRequest => 16, + ClientZoneIpcType::Unk7 => 32, + ClientZoneIpcType::UpdatePositionHandler => 24, + ClientZoneIpcType::LogOut => 8, + ClientZoneIpcType::Disconnected => 8, + ClientZoneIpcType::ChatMessage => todo!(), + ClientZoneIpcType::GameMasterCommand => todo!(), + ClientZoneIpcType::Unk12 => todo!(), + ClientZoneIpcType::EnterZoneLine => todo!(), + ClientZoneIpcType::Unk13 => todo!(), + ClientZoneIpcType::Unk14 => todo!(), + ClientZoneIpcType::ActionRequest => todo!(), + ClientZoneIpcType::Unk15 => todo!(), + } + } +} // TODO: make generic impl Default for ClientZoneIpcSegment { @@ -72,7 +99,7 @@ impl Default for ClientZoneIpcSegment { op_code: ClientZoneIpcType::InitRequest, server_id: 0, timestamp: 0, - data: ClientZoneIpcData::InitRequest { unk: [0; 105] }, + data: ClientZoneIpcData::InitRequest { unk: [0; 120] }, } } } @@ -166,7 +193,7 @@ pub enum ServerZoneIpcType { // Sent by the server to spawn the player in PlayerSpawn = 0x331, /// Sent by the server as response to ZoneInitRequest. - InitResponse = 0x2D0, + InitResponse = 0x223, // Sent by the server to indicate the log out is complete LogOutComplete = 0x69, // Sent by the server to modify the client's position @@ -212,20 +239,20 @@ pub enum ServerZoneIpcType { #[derive(Clone, PartialEq, Debug)] pub enum ClientZoneIpcType { /// Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load) - InitRequest = 0x2ED, + InitRequest = 0x2AB, // Sent by the client when they're done loading and they need to be spawned in - FinishLoading = 0x397, // TODO: assumed + FinishLoading = 0x1AB, // FIXME: 32 bytes of something from the client, not sure what yet - Unk1 = 0x37C, + Unk1 = 0x364, // FIXME: 16 bytes of something from the client, not sure what yet - Unk2 = 0x2E5, + Unk2 = 0x1D8, // FIXME: 8 bytes of something from the client, not sure what yet - Unk3 = 0x326, + Unk3 = 0x2AF, // FIXME: 8 bytes of something from the client, not sure what yet - Unk4 = 0x143, + Unk4 = 0x178, SetSearchInfoHandler = 0x20A, // FIXME: 8 bytes of something from the client, not sure what yet - Unk5 = 0x2D0, + Unk5 = 0x223, // Sent by the client when it requests the friends list and other related info SocialListRequest = 0x1A1, // FIXME: 32 bytes of something from the client, not sure what yet @@ -332,7 +359,7 @@ pub enum ClientZoneIpcData { #[br(pre_assert(*magic == ClientZoneIpcType::InitRequest))] InitRequest { // TODO: full of possibly interesting information - unk: [u8; 105], + unk: [u8; 120], }, #[br(pre_assert(*magic == ClientZoneIpcType::FinishLoading))] FinishLoading { @@ -347,7 +374,7 @@ pub enum ClientZoneIpcData { #[br(pre_assert(*magic == ClientZoneIpcType::Unk2))] Unk2 { // TODO: full of possibly interesting information - unk: [u8; 8], + unk: [u8; 16], }, #[br(pre_assert(*magic == ClientZoneIpcType::Unk3))] Unk3 {