diff --git a/src/bin/kawari-lobby.rs b/src/bin/kawari-lobby.rs index 4fd978a..61a4379 100644 --- a/src/bin/kawari-lobby.rs +++ b/src/bin/kawari-lobby.rs @@ -7,7 +7,7 @@ use kawari::encryption::generate_encryption_key; use kawari::ipc::{CharacterDetails, IPCOpCode, IPCSegment, IPCStructData, Server, ServiceAccount}; use kawari::oodle::FFXIVOodle; use kawari::packet::{ - PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, + CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, }; use kawari::{CONTENT_ID, WORLD_ID, WORLD_NAME, ZONE_ID}; use tokio::io::{AsyncReadExt, WriteHalf}; @@ -28,7 +28,8 @@ async fn main() { let mut state = State { client_key: None, session_id: None, - oodle: FFXIVOodle::new(), + clientbound_oodle: FFXIVOodle::new(), + serverbound_oodle: FFXIVOodle::new(), player_id: None, }; @@ -114,7 +115,13 @@ async fn initialize_encryption( target_actor: 0, segment_type: SegmentType::InitializationEncryptionResponse { data }, }; - send_packet(socket, &[response_packet], state).await; + send_packet( + socket, + &[response_packet], + state, + CompressionType::Uncompressed, + ) + .await; } async fn send_account_list(socket: &mut WriteHalf, state: &mut State) { @@ -156,7 +163,13 @@ async fn send_account_list(socket: &mut WriteHalf, state: &mut State) target_actor: 0, segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(socket, &[response_packet], state).await; + send_packet( + socket, + &[response_packet], + state, + CompressionType::Uncompressed, + ) + .await; } async fn send_lobby_info(socket: &mut WriteHalf, state: &mut State, sequence: u64) { @@ -227,7 +240,7 @@ async fn send_lobby_info(socket: &mut WriteHalf, state: &mut State, s packets.push(response_packet); } - send_packet(socket, &packets, state).await; + send_packet(socket, &packets, state, CompressionType::Uncompressed).await; // now send them the character list { @@ -367,7 +380,13 @@ async fn send_lobby_info(socket: &mut WriteHalf, state: &mut State, s target_actor: 0, segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(socket, &[response_packet], state).await; + send_packet( + socket, + &[response_packet], + state, + CompressionType::Uncompressed, + ) + .await; } } } @@ -412,5 +431,11 @@ async fn send_enter_world( target_actor: 0, segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(socket, &[response_packet], state).await; + send_packet( + socket, + &[response_packet], + state, + CompressionType::Uncompressed, + ) + .await; } diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 0aac8e6..f285cab 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -3,7 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use kawari::ipc::{ActorControlType, IPCOpCode, IPCSegment, IPCStructData, Position}; use kawari::oodle::FFXIVOodle; use kawari::packet::{ - PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, + CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, }; use kawari::{CONTENT_ID, WORLD_ID, ZONE_ID}; use tokio::io::AsyncReadExt; @@ -24,7 +24,8 @@ async fn main() { let mut state = State { client_key: None, session_id: None, - oodle: FFXIVOodle::new(), + clientbound_oodle: FFXIVOodle::new(), + serverbound_oodle: FFXIVOodle::new(), player_id: None, }; @@ -58,7 +59,13 @@ async fn main() { timestamp, }, }; - send_packet(&mut write, &[response_packet], &mut state).await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } match connection_type { @@ -74,8 +81,13 @@ async fn main() { player_id: *player_id, }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } kawari::packet::ConnectionType::Chat => { tracing::info!( @@ -90,8 +102,13 @@ async fn main() { player_id: *player_id, }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } { @@ -101,9 +118,7 @@ async fn main() { op_code: IPCOpCode::InitializeChat, server_id: 0, timestamp: 0, - data: IPCStructData::InitializeChat { - unk: [0; 24], - }, + data: IPCStructData::InitializeChat { unk: [0; 8] }, }; let response_packet = PacketSegment { @@ -111,8 +126,13 @@ async fn main() { target_actor: *player_id, segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } } _ => panic!( @@ -156,8 +176,13 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } // Control Data @@ -184,8 +209,13 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } // Stats @@ -236,8 +266,13 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } // Player Setup @@ -376,8 +411,13 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } // Player Class Info @@ -403,8 +443,13 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } // Init Zone @@ -449,9 +494,38 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state) - .await; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; } + + // ????? + /*{ + let ipc = IPCSegment { + unk1: 0, + unk2: 0, + op_code: IPCOpCode::InitRequest, + server_id: 0, + timestamp: timestamp_secs(), + data: IPCStructData::InitResponse { + unk1: 0, + character_id: state.player_id.unwrap(), + unk2: 0, + }, + }; + + let response_packet = PacketSegment { + source_actor: state.player_id.unwrap(), + target_actor: state.player_id.unwrap(), + segment_type: SegmentType::Ipc { data: ipc }, + }; + send_packet(&mut write, &[response_packet], &mut state, CompressionType::Oodle) + .await; + }*/ } _ => panic!( "The server is recieving a IPC response or unknown packet!" diff --git a/src/compression.rs b/src/compression.rs index 1bf2b69..48230ae 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -33,6 +33,14 @@ pub(crate) fn decompress( crate::packet::CompressionType::Oodle => oodle.decode(data, header.uncompressed_size), }; + if header.compression_type == crate::packet::CompressionType::Oodle { + assert_eq!( + data.len(), + header.uncompressed_size as usize, + "Decompressed data does not match the expected length!" + ); + } + write("decompressed.bin", &data).unwrap(); let mut cursor = Cursor::new(&data); diff --git a/src/ipc.rs b/src/ipc.rs index 9b5a515..469b3ed 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -32,7 +32,7 @@ pub enum IPCOpCode { /// 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, /// Sent by the server as response to ZoneInitRequest. - InitResponse = 280, // TODO: probably wrong! + InitResponse = 0x1EF, /// Sent by the server that tells the client which zone to load InitZone = 0x0311, /// Sent by the server for... something @@ -276,7 +276,7 @@ pub enum IPCStructData { host: String, }, #[br(pre_assert(false))] - InitializeChat { unk: [u8; 24] }, + InitializeChat { unk: [u8; 8] }, #[br(pre_assert(false))] InitResponse { unk1: u64, @@ -521,8 +521,8 @@ impl IPCSegment { IPCStructData::LobbyCharacterAction { .. } => todo!(), IPCStructData::LobbyEnterWorld { .. } => 160, IPCStructData::RequestEnterWorld { .. } => todo!(), - IPCStructData::InitializeChat { .. } => 24, - IPCStructData::InitRequest { .. } => todo!(), + IPCStructData::InitializeChat { .. } => 8, + IPCStructData::InitRequest { .. } => 16, IPCStructData::InitResponse { .. } => 16, IPCStructData::InitZone { .. } => 103, IPCStructData::ActorControlSelf { .. } => 32, @@ -571,7 +571,17 @@ mod tests { unk8: 0, entitled_expansion: 0, characters: Vec::new(), - } + }, + IPCStructData::ActorControlSelf { + category: ActorControlType::SetCharaGearParamUI, + param1: 0, + param2: 0, + param3: 0, + param4: 0, + param5: 0, + param6: 0, + }, + IPCStructData::InitializeChat { unk: [0; 8] }, ]; for ipc in &ipc_types { diff --git a/src/oodle/mod.rs b/src/oodle/mod.rs index d4027fe..a07d0fc 100644 --- a/src/oodle/mod.rs +++ b/src/oodle/mod.rs @@ -151,4 +151,21 @@ impl FFXIVOodle { out_buf } } + + pub fn encode(&mut self, input: Vec) -> Vec { + unsafe { + let mut out_buf: Vec = vec![0u8; input.len()]; + let mut in_buf = input.to_vec(); + let len = OodleNetwork1TCP_Encode( + self.state.as_mut_ptr() as *mut c_void, + self.shared.as_mut_ptr() as *mut c_void, + in_buf.as_mut_ptr() as *const c_void, + in_buf.len().try_into().unwrap(), + out_buf.as_mut_ptr() as *mut c_void, + ); + + out_buf.truncate(len as usize); + out_buf + } + } } diff --git a/src/packet.rs b/src/packet.rs index 58d43c9..aada8f8 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -79,7 +79,7 @@ pub enum SegmentType { #[binrw] #[brw(repr = u8)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum CompressionType { Uncompressed = 0, Oodle = 2, @@ -153,6 +153,7 @@ pub async fn send_packet( socket: &mut WriteHalf, segments: &[PacketSegment], state: &mut State, + compression_type: CompressionType, ) { let timestamp: u64 = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -161,39 +162,45 @@ pub async fn send_packet( .try_into() .unwrap(); - let mut total_segment_size = 0; + let mut segments_buffer = Cursor::new(Vec::new()); for segment in segments { - total_segment_size += segment.calc_size(); + segment + .write_le_args( + &mut segments_buffer, + (state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),), + ) + .unwrap(); } + let segments_buffer = segments_buffer.into_inner(); + + let mut uncompressed_size = 0; + let data = match compression_type { + CompressionType::Uncompressed => segments_buffer, + CompressionType::Oodle => { + uncompressed_size = segments_buffer.len(); + state.clientbound_oodle.encode(segments_buffer) + } + }; + + let size = std::mem::size_of::() + data.len(); + let header = PacketHeader { unk1: 0xE2465DFF41A05252, // wtf? unk2: 0x75C4997B4D642A7F, // wtf? x2 timestamp, - size: std::mem::size_of::() as u32 + total_segment_size, + size: size as u32, connection_type: ConnectionType::Lobby, segment_count: segments.len() as u16, unk3: 0, - compression_type: CompressionType::Uncompressed, + compression_type, unk4: 0, - uncompressed_size: 0, - }; - - let packet = Packet { - header, - segments: segments.to_vec(), + uncompressed_size: uncompressed_size as u32, }; let mut cursor = Cursor::new(Vec::new()); - packet - .write_le_args( - &mut cursor, - ( - &mut state.oodle, - state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()), - ), - ) - .unwrap(); + header.write_le(&mut cursor).unwrap(); + std::io::Write::write_all(&mut cursor, &data).unwrap(); let buffer = cursor.into_inner(); @@ -210,7 +217,8 @@ pub async fn send_packet( pub struct State { pub client_key: Option<[u8; 16]>, pub session_id: Option, - pub oodle: FFXIVOodle, + pub serverbound_oodle: FFXIVOodle, + pub clientbound_oodle: FFXIVOodle, pub player_id: Option, } @@ -220,7 +228,7 @@ pub async fn parse_packet(data: &[u8], state: &mut State) -> (Vec match Packet::read_le_args( &mut cursor, ( - &mut state.oodle, + &mut state.serverbound_oodle, state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()), ), ) { @@ -256,7 +264,13 @@ pub async fn send_keep_alive( target_actor: 0, segment_type: SegmentType::KeepAliveResponse { id, timestamp }, }; - send_packet(socket, &[response_packet], state).await; + send_packet( + socket, + &[response_packet], + state, + CompressionType::Uncompressed, + ) + .await; } #[cfg(test)]