use psopacket::pso_packet; use crate::{PSOPacket, PacketParseError, PSOPacketData}; use std::io::Read; pub const PATCH_FILE_CHUNK_SIZE: u16 = 0x6000; // 24kb #[allow(non_camel_case_types)] type u8_str = u8; #[pso_packet(0x02, no_flag)] pub struct PatchWelcome { copyright: [u8; 44], padding: [u8; 20], server_key: u32, client_key: u32, } impl PatchWelcome { pub fn new(server_key: u32, client_key: u32) -> PatchWelcome { PatchWelcome { copyright: *b"Patch Server. Copyright SonicTeam, LTD. 2001", padding: [0; 20], server_key, client_key, } } } #[pso_packet(0x02, no_flag)] pub struct PatchWelcomeReply { } #[pso_packet(0x04, no_flag)] pub struct RequestLogin { } #[pso_packet(0x04, no_flag)] pub struct LoginReply { unused: [u8; 12], username: [u8_str; 16], password: [u8_str; 16], unused2: [u8; 64], } #[pso_packet(0x06, no_flag)] pub struct StartFileSend { id: u32, size: u32, filename: [u8_str; 48], } impl StartFileSend { pub fn new(filename: &str, size: u32, id: u32) -> StartFileSend { let mut f = [0u8; 48]; for (src, dst) in filename.as_bytes().iter().zip(f.iter_mut()) { *dst = *src } StartFileSend { id, size, filename: f, } } } //#[pso_packet(0x07)] pub struct FileSend { pub chunk_num: u32, pub checksum: u32, pub chunk_size: u32, pub buffer: [u8; PATCH_FILE_CHUNK_SIZE as usize], } impl PSOPacket for FileSend { fn from_bytes(_data: &[u8]) -> Result { // TODO: implement this? it shouldn't be called on the server side ever... unimplemented!(); } fn as_bytes(&self) -> Vec { let mut buf: Vec = Vec::new(); buf.extend_from_slice(&u32::to_le_bytes(self.chunk_num)); buf.extend_from_slice(&u32::to_le_bytes(self.checksum)); buf.extend_from_slice(&u32::to_le_bytes(self.chunk_size)); buf.extend_from_slice(&self.buffer[0..self.chunk_size as usize]); while buf.len() % 4 != 0 { buf.push(0); } //buf //buf.extend_from_slice(&u16::to_le_bytes((4 * 4) as u16 + self.chunk_size as u16)); //buf.extend_from_slice(&u16::to_le_bytes(0x07)); let pkt_len = (buf.len() + 4) as u16; let mut prebuf: Vec = Vec::new(); prebuf.extend_from_slice(&u16::to_le_bytes(pkt_len)); prebuf.extend_from_slice(&u16::to_le_bytes(0x07)); prebuf.append(&mut buf); prebuf } } impl std::fmt::Debug for FileSend { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { writeln!(f, "packet FileSend {{").unwrap(); writeln!(f, " chunk_num: {:?}", self.chunk_num).unwrap(); writeln!(f, " checksum: {:X?}", self.checksum).unwrap(); writeln!(f, " chunk_size: {:X?}", self.chunk_size).unwrap(); writeln!(f, " buffer: [...a large array ...]").unwrap(); writeln!(f, "}}") } } #[derive(Default)] #[pso_packet(0x08, no_flag)] pub struct EndFileSend { padding: u32, } #[pso_packet(0x0B, no_flag)] pub struct PatchStartList { } #[pso_packet(0x09, no_flag)] pub struct ChangeDirectory { dirname: [u8_str; 64] } impl ChangeDirectory { pub fn new(dirname: &str) -> ChangeDirectory { let mut d = [0u8; 64]; for (src, dst) in dirname.as_bytes().iter().zip(d.iter_mut()) { *dst = *src } ChangeDirectory { dirname: d, } } } #[pso_packet(0x0A, no_flag)] pub struct UpOneDirectory { } #[pso_packet(0x0C, no_flag)] pub struct FileInfo { id: u32, filename: [u8_str; 32], } impl FileInfo { pub fn new(filename: &str, id: u32) -> FileInfo { let mut f = [0u8; 32]; for (src, dst) in filename.as_bytes().iter().zip(f.iter_mut()) { *dst = *src }; FileInfo { id, filename: f, } } } #[pso_packet(0x0D, no_flag)] pub struct PatchEndList { } #[pso_packet(0x0F, no_flag)] pub struct FileInfoReply { pub id: u32, pub checksum: u32, pub size: u32, } #[pso_packet(0x10, no_flag)] pub struct FileInfoListEnd { } #[pso_packet(0x11, no_flag)] pub struct FilesToPatchMetadata { data_size: u32, file_count: u32, } impl FilesToPatchMetadata { pub fn new(data_size: u32, file_count: u32) -> FilesToPatchMetadata { FilesToPatchMetadata { data_size, file_count, } } } #[pso_packet(0x12, no_flag)] pub struct FinalizePatching { } #[pso_packet(0x13, no_flag)] pub struct Message { msg: String, } impl Message { pub fn new(mut msg: String) -> Message { msg.push('\0'); Message { msg, } } } #[pso_packet(0x14, no_flag)] pub struct RedirectClient { ip: u32, port: u16, padding: u16, } impl RedirectClient { pub fn new(ip: u32, port: u16) -> RedirectClient { RedirectClient { ip, port, padding: 0, } } } #[cfg(test)] mod tests { #[test] fn patch_welcome() { use super::PSOPacket; let pkt = super::PatchWelcome::new(123, 456); assert!(pkt.as_bytes() == vec![0x4C, 0x00, 0x02, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2E, 0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x53, 0x6F, 0x6E, 0x69, 0x63, 0x54, 0x65, 0x61, 0x6D, 0x2C, 0x20, 0x4C, 0x54, 0x44, 0x2E, 0x20, 0x32, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, ]); let mut bytes = pkt.as_bytes(); bytes.splice(28..37, b"Elsewhere".iter().cloned()); let new_pkt = super::PatchWelcome::from_bytes(&bytes); assert!(new_pkt == Ok(super::PatchWelcome { copyright: b"Patch Server. Copyright Elsewhere, LTD. 2001".clone(), padding: [0; 20], server_key: 123, client_key: 456, })); if let Ok(p) = new_pkt { println!("{:?}", p); } } #[test] fn test_message() { use super::PSOPacket; let msg = super::Message::new("hello this is an arbitrary message?!!".to_string()); assert!(msg.as_bytes() == vec![0x50, 0x00, 0x13, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x61, 0x00, 0x72, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x20, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x3F, 0x00, 0x21, 0x00, 0x21, 0x00, 0x00, 0x00]); let mut bytes = Vec::new(); bytes.extend_from_slice(&70u16.to_le_bytes()); bytes.extend_from_slice(&19u16.to_le_bytes()); for c in "this is a cool string of letters!".encode_utf16() { bytes.extend_from_slice(&c.to_le_bytes()); } let msg = super::Message::from_bytes(&bytes); let b = msg.unwrap().as_bytes(); assert!(b == vec![0x48, 0x00, 0x13, 0x00, 0x74, 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x21, 0x00, 0x00, 0x00]) } }