use psopacket::{pso_packet, PSOPacketData}; use crate::{PSOPacket, PacketParseError, PSOPacketData}; use crate::utf8_to_utf16_array; use crate::packet::messages::GameMessage; //use character::character::FullCharacter; use crate::character::character as character; use crate::character::character::BankItem; use crate::ConsumingBlob; use std::io::Read; pub const BLOCK_MENU_ID: u32 = 2; pub const ROOM_MENU_ID: u32 = 3; pub const LOBBY_MENU_ID: u32 = 4; #[pso_packet(0x03)] pub struct ShipWelcome { #[utf8] copyright: [u8; 0x60], server_key: [u8; 48], client_key: [u8; 48], } impl ShipWelcome { pub fn new(server_key: [u8; 48], client_key: [u8; 48]) -> ShipWelcome { let mut copyright = [0u8; 0x60]; copyright[..0x4B].clone_from_slice(b"Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM."); ShipWelcome { copyright: copyright, server_key: server_key, client_key: client_key, } } } #[derive(Debug, PartialEq, Clone)] pub struct BlockEntry { menu: u32, item: u32, flags: u16, name: [u16; 0x11], } impl PSOPacketData for BlockEntry { fn from_bytes(_cursor: &mut R) -> Result { unimplemented!(); } fn as_bytes(&self) -> Vec { let mut bytes = Vec::new(); bytes.extend_from_slice(&u32::to_le_bytes(self.menu)); bytes.extend_from_slice(&u32::to_le_bytes(self.item)); bytes.extend_from_slice(&u16::to_le_bytes(self.flags)); bytes.extend_from_slice(&unsafe { std::mem::transmute::<[u16; 0x11], [u8; 0x11*2]>(self.name) }); bytes } } #[pso_packet(0xA0)] pub struct RequestShipList { unknown: [u8; 24], } #[pso_packet(0xA1)] pub struct RequestShipBlockList { unknown: [u8; 24], } #[pso_packet(0xA1)] pub struct ShipBlockList { shipblock: BlockEntry, blocks: Vec } impl ShipBlockList { pub fn new(shipname: &str, num_blocks: usize) -> ShipBlockList { ShipBlockList { shipblock: BlockEntry { menu: BLOCK_MENU_ID, item: 0, flags: 0, name: utf8_to_utf16_array!(shipname, 0x11) }, blocks: (0..num_blocks).map(|i| BlockEntry { menu: BLOCK_MENU_ID, item: i as u32 + 1, flags: 0, name: utf8_to_utf16_array!(format!("Block {}", i+1), 0x11) }).collect() } } } // TODO: menu should be an enum // TODO: or perhaps MenuSelect should be broken up into different structs based on menu // TODO: i.e. ShipMenuSelect, BlockMenuSelect, etc #[pso_packet(0x10)] pub struct MenuSelect { pub menu: u32, pub item: u32, } #[pso_packet(0x09)] pub struct MenuDetail { pub menu: u32, pub item: u32, } #[pso_packet(0x10)] pub struct RoomPasswordReq { pub menu: u32, pub item: u32, pub password: [u16; 16], } #[pso_packet(0x84)] pub struct LobbySelect { pub menu: u32, pub lobby: u32, } #[pso_packet(0xE7)] pub struct FullCharacter { #[nodebug] character: character::FullCharacter, } #[pso_packet(0x95)] pub struct CharDataRequest { } // TODO: what does this even do? #[pso_packet(0x61)] pub struct CharData { _unknown: [u8; 0x828] } #[pso_packet(0x60)] pub struct BurstDone72 { msg: u8, len: u8, client: u8, target: u8, } impl BurstDone72 { pub fn new() -> BurstDone72 { BurstDone72 { msg: 0x72, len: 3, client: 0x18, target: 0x08, } } } #[pso_packet(0x60)] pub struct Message { msg: GameMessage, } impl Message { pub fn new(msg: GameMessage) -> Message { Message { msg: msg, } } } #[pso_packet(0x62, manual_flag)] pub struct DirectMessage { flag: u32, msg: GameMessage } impl DirectMessage { pub fn new(target: u32, msg: GameMessage) -> DirectMessage { DirectMessage { flag: target, msg: msg, } } } // this is a bit of a weird packet, 0x6C is in the 0x60 family in terms of function // but this is the only packet I could find that uses it #[pso_packet(0x6C, no_flag)] struct BankItemList { pub aflag: u32, pub cmd: u8, // 0xBC pub unknown: [u8; 3], pub size: u32, pub checksum: u32, pub item_count: u32, pub meseta: u32, pub items: Vec, } #[pso_packet(0x6D, manual_flag)] struct Like62ButCooler { pub flag: u32, pub blob: ConsumingBlob, } #[derive(PSOPacketData, Clone, Copy, Default)] pub struct PlayerHeader { pub tag: u32, pub guildcard: u32, pub _unknown1: [u32; 5], pub client_id: u32, pub name: [u16; 16], pub _unknown2: u32, } #[derive(PSOPacketData, Clone)] pub struct PlayerInfo { pub header: PlayerHeader, pub inventory: character::Inventory, pub character: character::Character, } #[pso_packet(0x67)] pub struct JoinLobby { pub client: u8, pub leader: u8, pub one: u8, pub lobby: u8, pub block: u16, pub event: u16, pub padding: u32, pub playerinfo: Vec, } #[pso_packet(0x68, manual_flag)] pub struct AddToLobby { flag: u32, pub client: u8, pub leader: u8, pub one: u8, pub lobby: u8, pub block: u16, pub event: u16, pub padding: u32, pub playerinfo: PlayerInfo, } #[pso_packet(0xC1)] pub struct CreateRoom { unknown: [u32; 2], name: [u16; 16], password: [u16; 16], difficulty: u8, battle: u8, challenge: u8, episode: u8, single_player: u8, padding: [u8; 3], } #[pso_packet(0x01)] pub struct SmallDialog { padding: [u32; 0x02], msg: String, } impl SmallDialog { pub fn new(mut msg: String) -> SmallDialog { if !msg.ends_with('\0') { msg.push('\0'); } SmallDialog { padding: [0; 0x02], msg: msg, } } } #[pso_packet(0x11)] pub struct SmallLeftDialog { padding: [u32; 0x02], msg: String, } impl SmallLeftDialog { pub fn new(mut msg: String) -> SmallLeftDialog { if !msg.ends_with('\0') { msg.push('\0'); } SmallLeftDialog { padding: [0x00004500, 0x45004500], msg: msg, } } } #[pso_packet(0x64, manual_flag)] pub struct JoinRoom { pub flag: u32, // # of elements in players pub maps: [u32; 0x20], pub players: [PlayerHeader; 4], pub client: u8, pub leader: u8, pub one: u8, pub difficulty: u8, // TODO: enum pub battle: u8, pub event: u8, pub section: u8, pub challenge: u8, pub random_seed: u32, pub episode: u8, pub one2: u8, pub single_player: u8, pub unknown: u8, } #[pso_packet(0x65, manual_flag)] pub struct AddToRoom { pub flag: u32, pub client: u8, pub leader: u8, pub one: u8, pub lobby: u8, pub block: u16, pub event: u16, pub padding: u32, pub playerinfo: PlayerInfo, } #[pso_packet(0x69)] pub struct LeaveLobby { client: u8, leader: u8, _padding: u16, } impl LeaveLobby { pub fn new(client: u8, leader: u8) -> LeaveLobby { LeaveLobby { client: client, leader: leader, _padding: 0, } } } #[pso_packet(0x66)] pub struct LeaveRoom { client: u8, leader: u8, _padding: u16, } impl LeaveRoom { pub fn new(client: u8, leader: u8) -> LeaveRoom { LeaveRoom { client: client, leader: leader, _padding: 0, } } } #[pso_packet(0x06)] pub struct PlayerChat { pub unknown: u32, pub guildcard: u32, pub message: String, } impl PlayerChat { pub fn new(guildcard: u32, message: String) -> PlayerChat { PlayerChat { unknown: 0x00010000, guildcard: guildcard, message: message, } } } #[pso_packet(0x8A)] pub struct RoomNameRequest { } #[pso_packet(0x8A)] pub struct RoomNameResponse { pub name: String, } #[pso_packet(0x7ED)] pub struct UpdateConfig{ pub config: [u8; 0xE8], } #[pso_packet(0xD8)] pub struct ViewInfoboardRequest { } #[pso_packet(0xDE)] pub struct RareMonsterList{ pub ids: [u16; 16], } #[derive(PSOPacketData, Clone)] pub struct InfoboardResponse { pub name: [u16; 16], pub message: [u16; 172], } #[pso_packet(0xD8)] pub struct ViewInfoboardResponse { pub response: Vec, } #[pso_packet(0xD9)] pub struct WriteInfoboard { pub message: String, } #[pso_packet(0x08)] pub struct RoomListRequest { } #[derive(PSOPacketData, Clone)] pub struct RoomList { pub menu_id: u32, pub item_id: u32, pub difficulty: u8, pub players: u8, pub name: [u16; 16], pub episode: u8, pub flags: u8, } #[pso_packet(0x08)] pub struct RoomListResponse { pub baseroom: RoomList, pub rooms: Vec, } #[derive(PSOPacketData, Clone, Copy, Default)] pub struct LobbyEntry { menu_id: u32, item_id: u32, padding: u32, } impl LobbyEntry { pub fn new(menu_id: u32, lobby_id: u32) -> LobbyEntry { LobbyEntry { menu_id: menu_id, item_id: lobby_id, padding: 0, } } } #[pso_packet(0x83, manual_flag)] pub struct LobbyList { flag: u32, entries: [LobbyEntry; 16], } impl LobbyList { pub fn new() -> LobbyList { let lobbies = (0..16).fold([LobbyEntry::default(); 16], |mut acc, index| { acc[index].menu_id = LOBBY_MENU_ID; acc[index].item_id = index as u32; acc }); LobbyList { flag: 0x0F, entries: lobbies, } } } #[pso_packet(0x6F)] pub struct DoneBursting { } #[pso_packet(0x16F)] pub struct DoneBursting2 { } #[pso_packet(0x98)] pub struct ClientCharacterData { pub data: [u8; 2088], } #[pso_packet(0xA2, manual_flag)] pub struct RequestQuestList { pub flag: u32, } #[derive(PSOPacketData, Clone, Copy)] pub struct QuestCategory { pub menu_id: u32, pub option_id: u32, pub name: [u16; 32], pub description: [u16; 122], } #[pso_packet(0xA2)] pub struct QuestCategoryList { pub quest_categories: Vec, } #[derive(PSOPacketData, Clone, Copy)] pub struct QuestEntry { pub menu_id: u32, pub category_id: u16, pub quest_id: u16, pub name: [u16; 32], pub description: [u16; 122], } #[pso_packet(0xA2)] pub struct QuestOptionList { pub quests: Vec, } #[pso_packet(0xA3)] pub struct QuestDetail { description: [u16; 288] } #[pso_packet(0x09)] pub struct QuestDetailRequest { pub menu: u32, pub category: u16, pub quest: u16, } #[pso_packet(0x10)] pub struct QuestMenuSelect { pub menu: u32, pub category: u16, pub quest: u16, } #[pso_packet(0x44)] pub struct QuestHeader { pub unknown1: [u8; 0x24], pub filename: [u8; 16], pub length: u32, pub name: [u8; 16], pub unknown2: [u8; 8], } #[pso_packet(0x44)] pub struct QuestFileRequest { pub filename: [u8; 16], } #[pso_packet(0x13, no_flag)] pub struct QuestChunk { pub chunk_num: u32, pub filename: [u8; 16], pub blob: [u8; 0x400], pub blob_length: u32, pub unknown: u32, } #[pso_packet(0x13, no_flag)] pub struct QuestChunkAck { pub chunk_num: u32, filename: [u8; 16], } #[pso_packet(0xAC)] pub struct DoneLoadingQuest { } #[pso_packet(0xE7)] pub struct FullCharacterData { pub character: character::FullCharacter } #[pso_packet(0x1ED)] pub struct SaveOptions { pub options: u32, } #[derive(PSOPacketData, Clone, Copy, Default)] pub struct TradeItem { pub item_data: [u8; 12], pub item_id: u32, pub item_data2: [u8; 4], } #[pso_packet(0xD0)] pub struct ItemsToTrade { pub trade_target: u8, pub unknown2: u8, pub count: u16, pub items: [TradeItem; 32], } #[pso_packet(0xD1)] pub struct AcknowledgeTrade { } #[pso_packet(0xD2)] pub struct TradeConfirmed { } #[pso_packet(0xD4)] pub struct CancelTrade { } #[pso_packet(0xD4, manual_flag)] pub struct TradeSuccessful { flag: u32, } impl std::default::Default for TradeSuccessful { fn default() -> TradeSuccessful { TradeSuccessful { flag: 1, } } } #[pso_packet(0xDA, no_flag)] pub struct LobbyEvent { pub event: u32, } #[pso_packet(0x4ED)] pub struct KeyboardConfig { pub keyboard_config: [u8; 364], } #[pso_packet(0x5ED)] pub struct GamepadConfig { pub gamepad_config: [u8; 56], } // same struct as libpso::packet::messages::GuildcardRecv #[pso_packet(0x4E8)] pub struct GuildcardAccept { id: u32, name: [u16; 0x18], team: [u16; 0x10], desc: [u16; 0x58], one: u8, language: u8, section_id: u8, class: u8, }