From 24edee654844c9576ea8a0fec7cfcc5af8d50a55 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 4 Jul 2025 15:01:27 -0400 Subject: [PATCH] Fix various size issues with lobby IPC packets Some of these were the wrong size, and were affecting both the server and packet analyzer alike. This should now more accurately reflect retail. --- resources/opcodes.json | 2 +- src/bin/kawari-lobby.rs | 2 + src/ipc/lobby/chara_make.rs | 7 +- src/ipc/lobby/mod.rs | 127 ++++++++++++++++++++++++++++-------- 4 files changed, 105 insertions(+), 33 deletions(-) diff --git a/resources/opcodes.json b/resources/opcodes.json index d2fb1a6..b3563a6 100644 --- a/resources/opcodes.json +++ b/resources/opcodes.json @@ -412,7 +412,7 @@ { "name": "DistRetainerInfo", "opcode": 23, - "size": 210 + "size": 536 } ], "ClientLobbyIpcType": [ diff --git a/src/bin/kawari-lobby.rs b/src/bin/kawari-lobby.rs index 9aef22a..a26a3cb 100644 --- a/src/bin/kawari-lobby.rs +++ b/src/bin/kawari-lobby.rs @@ -216,6 +216,7 @@ async fn main() { sequence, session_id, version_info, + .. } => { tracing::info!( "Client {session_id} ({version_info}) logging in!" @@ -294,6 +295,7 @@ async fn main() { ClientLobbyIpcData::GameLogin { sequence, content_id, + .. } => { tracing::info!("Client is joining the world with {content_id}"); diff --git a/src/ipc/lobby/chara_make.rs b/src/ipc/lobby/chara_make.rs index 2aef761..d790a72 100644 --- a/src/ipc/lobby/chara_make.rs +++ b/src/ipc/lobby/chara_make.rs @@ -5,8 +5,9 @@ use crate::common::CHAR_NAME_MAX_LENGTH; use super::{read_string, write_string}; #[binrw] -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Debug, Default)] pub enum LobbyCharacterActionKind { + #[default] #[brw(magic = 0x1u8)] ReserveName, #[brw(magic = 0x2u8)] @@ -34,11 +35,11 @@ pub enum LobbyCharacterActionKind { } #[binrw] -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Debug, Default)] pub struct CharaMake { pub sequence: u64, pub content_id: u64, - #[br(pad_before = 8)] + #[brw(pad_before = 8)] pub character_index: u8, pub action: LobbyCharacterActionKind, pub world_id: u16, diff --git a/src/ipc/lobby/mod.rs b/src/ipc/lobby/mod.rs index c12b824..ea3bcdb 100644 --- a/src/ipc/lobby/mod.rs +++ b/src/ipc/lobby/mod.rs @@ -47,6 +47,7 @@ impl Default for ClientLobbyIpcSegment { sequence: 0, session_id: String::new(), version_info: String::new(), + unk1: 0, }, } } @@ -105,6 +106,8 @@ pub enum ClientLobbyIpcData { sequence: u64, content_id: u64, // TODO: what else is in here? + unk1: u64, + unk2: u64, }, /// Sent by the client after exchanging encryption information with the lobby server. #[br(pre_assert(*magic == ClientLobbyIpcType::LoginEx))] @@ -113,15 +116,19 @@ pub enum ClientLobbyIpcData { #[brw(pad_before = 10)] // full of nonsense i don't understand yet #[br(count = 64)] + #[bw(pad_size_to = 64)] #[br(map = read_string)] - #[bw(ignore)] + #[bw(map = write_string)] session_id: String, #[br(count = 144)] + #[bw(pad_size_to = 144)] #[br(map = read_string)] - #[bw(ignore)] + #[bw(map = write_string)] version_info: String, - // unknown stuff at the end, it's not completely empty + + #[brw(pad_before = 910)] // empty + unk1: u64, }, #[br(pre_assert(*magic == ClientLobbyIpcType::ShandaLogin))] ShandaLogin { @@ -199,7 +206,7 @@ pub enum ServerLobbyIpcData { DistRetainerInfo { // TODO: what is in here? #[brw(pad_before = 7)] - #[brw(pad_after = 202)] + #[brw(pad_after = 528)] unk1: u8, }, Unknown { @@ -218,24 +225,36 @@ mod tests { /// Ensure that the IPC data size as reported matches up with what we write #[test] - fn lobby_ipc_sizes() { + fn server_lobby_ipc_sizes() { let ipc_types = [ + ( + ServerLobbyIpcType::NackReply, + ServerLobbyIpcData::NackReply { + sequence: 0, + error: 0, + value: 0, + exd_error_id: 0, + unk1: 0, + }, + ), ( ServerLobbyIpcType::LoginReply, ServerLobbyIpcData::LoginReply(LoginReply::default()), ), - ( - ServerLobbyIpcType::DistWorldInfo, - ServerLobbyIpcData::DistWorldInfo(DistWorldInfo::default()), - ), - ( - ServerLobbyIpcType::DistRetainerInfo, - ServerLobbyIpcData::DistRetainerInfo { unk1: 0 }, - ), ( ServerLobbyIpcType::ServiceLoginReply, ServerLobbyIpcData::ServiceLoginReply(ServiceLoginReply::default()), ), + ( + ServerLobbyIpcType::CharaMakeReply, + ServerLobbyIpcData::CharaMakeReply { + sequence: 0, + unk1: 0, + unk2: 0, + action: LobbyCharacterActionKind::ReserveName, + details: CharacterDetails::default(), + }, + ), ( ServerLobbyIpcType::GameLoginReply, ServerLobbyIpcData::GameLoginReply { @@ -248,24 +267,12 @@ mod tests { }, ), ( - ServerLobbyIpcType::NackReply, - ServerLobbyIpcData::NackReply { - sequence: 0, - error: 0, - value: 0, - exd_error_id: 0, - unk1: 0, - }, + ServerLobbyIpcType::DistWorldInfo, + ServerLobbyIpcData::DistWorldInfo(DistWorldInfo::default()), ), ( - ServerLobbyIpcType::CharaMakeReply, - ServerLobbyIpcData::CharaMakeReply { - sequence: 0, - unk1: 0, - unk2: 0, - action: LobbyCharacterActionKind::ReserveName, - details: CharacterDetails::default(), - }, + ServerLobbyIpcType::DistRetainerInfo, + ServerLobbyIpcData::DistRetainerInfo { unk1: 0 }, ), ]; @@ -292,4 +299,66 @@ mod tests { ); } } + + /// Ensure that the IPC data size as reported matches up with what we write + #[test] + fn client_lobby_ipc_sizes() { + let ipc_types = [ + ( + ClientLobbyIpcType::ServiceLogin, + ClientLobbyIpcData::ServiceLogin { sequence: 0 }, + ), + ( + ClientLobbyIpcType::GameLogin, + ClientLobbyIpcData::GameLogin { + sequence: 0, + content_id: 0, + unk1: 0, + unk2: 0, + }, + ), + ( + ClientLobbyIpcType::LoginEx, + ClientLobbyIpcData::LoginEx { + sequence: 0, + session_id: String::default(), + version_info: String::default(), + unk1: 0, + }, + ), + ( + ClientLobbyIpcType::ShandaLogin, + ClientLobbyIpcData::ShandaLogin { + unk: Vec::default(), + }, + ), + ( + ClientLobbyIpcType::CharaMake, + ClientLobbyIpcData::CharaMake(CharaMake::default()), + ), + ]; + + for (opcode, ipc) in &ipc_types { + let mut cursor = Cursor::new(Vec::new()); + + let ipc_segment = ClientLobbyIpcSegment { + unk1: 0, + unk2: 0, + op_code: opcode.clone(), + option: 0, + timestamp: 0, + data: ipc.clone(), + }; + ipc_segment.write_le(&mut cursor).unwrap(); + + let buffer = cursor.into_inner(); + + assert_eq!( + buffer.len(), + ipc_segment.calc_size() as usize, + "{:#?} did not match size!", + opcode + ); + } + } }