2019-11-16 23:12:29 -08:00
|
|
|
use psopacket::{pso_packet, PSOPacketData};
|
2019-11-04 20:31:57 -08:00
|
|
|
use crate::{PSOPacket, PacketParseError, PSOPacketData};
|
|
|
|
use crate::utf8_to_utf16_array;
|
2019-11-16 23:12:29 -08:00
|
|
|
use crate::packet::messages::GameMessage;
|
|
|
|
//use character::character::FullCharacter;
|
|
|
|
use crate::character::character as character;
|
2019-11-04 20:31:57 -08:00
|
|
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
|
|
|
const BLOCK_MENU_ID: u32 = 1;
|
|
|
|
|
|
|
|
#[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<R: Read>(_cursor: &mut R) -> Result<Self, PacketParseError> {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
|
|
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(0xA1)]
|
|
|
|
pub struct ShipBlockList {
|
|
|
|
shipblock: BlockEntry,
|
|
|
|
blocks: Vec<BlockEntry>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShipBlockList {
|
2019-12-03 22:50:41 -08:00
|
|
|
pub fn new(shipname: &str, num_blocks: usize) -> ShipBlockList {
|
2019-11-04 20:31:57 -08:00
|
|
|
ShipBlockList {
|
|
|
|
shipblock: BlockEntry {
|
|
|
|
menu: BLOCK_MENU_ID,
|
|
|
|
item: 0,
|
|
|
|
flags: 0,
|
2019-12-03 22:50:41 -08:00
|
|
|
name: utf8_to_utf16_array!(shipname, 0x11)
|
2019-11-04 20:31:57 -08:00
|
|
|
},
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
2019-11-16 23:12:29 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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(0xE7)]
|
|
|
|
pub struct FullCharacter {
|
|
|
|
#[no_debug]
|
|
|
|
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 Message {
|
|
|
|
msg: GameMessage,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Message {
|
|
|
|
pub fn new(msg: GameMessage) -> Message {
|
|
|
|
Message {
|
|
|
|
msg: msg,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x62, manual_flag)]
|
2019-12-15 23:14:40 -08:00
|
|
|
pub struct DirectMessage {
|
2019-11-16 23:12:29 -08:00
|
|
|
flag: u32,
|
|
|
|
msg: GameMessage
|
|
|
|
}
|
|
|
|
|
2019-12-15 23:14:40 -08:00
|
|
|
impl DirectMessage {
|
|
|
|
pub fn new(target: u32, msg: GameMessage) -> DirectMessage {
|
|
|
|
DirectMessage {
|
2019-11-16 23:12:29 -08:00
|
|
|
flag: target,
|
|
|
|
msg: msg,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-12-23 13:14:05 -08:00
|
|
|
#[derive(PSOPacketData, Clone, Copy, Default)]
|
2019-11-16 23:12:29 -08:00
|
|
|
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<PlayerInfo>,
|
2019-11-04 20:31:57 -08:00
|
|
|
}
|
2019-12-11 23:32:32 -08:00
|
|
|
|
|
|
|
#[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,
|
|
|
|
}
|
2019-12-23 13:14:33 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[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 {
|
|
|
|
msg: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SmallDialog {
|
|
|
|
pub fn new(msg: String) -> SmallDialog {
|
|
|
|
SmallDialog {
|
|
|
|
msg: msg,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x64, manual_flag)]
|
|
|
|
pub struct JoinRoom {
|
|
|
|
flag: u32, // # of elements in players
|
|
|
|
maps: [u32; 0x20],
|
|
|
|
players: [PlayerHeader; 4],
|
|
|
|
client_id: u8,
|
|
|
|
leader_id: u8,
|
|
|
|
one: u8,
|
|
|
|
difficulty: u8, // TODO: enum
|
|
|
|
battle: u8,
|
|
|
|
event: u8,
|
|
|
|
section: u8,
|
|
|
|
challenge: u8,
|
|
|
|
random_seed: u32,
|
|
|
|
episode: u8,
|
|
|
|
one2: u8,
|
|
|
|
single_player: u8,
|
|
|
|
unknown: u8,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl JoinRoom {
|
|
|
|
/*fn new() -> JoinRoom {
|
|
|
|
JoinRoom {
|
|
|
|
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x65, manual_flag)]
|
|
|
|
pub struct AddToRoom {
|
|
|
|
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,
|
|
|
|
}
|
2019-12-26 19:00:51 -04:00
|
|
|
|
|
|
|
#[pso_packet(0x06)]
|
|
|
|
pub struct PlayerChat {
|
|
|
|
pub padding: u32,
|
|
|
|
pub guildcard: u32,
|
|
|
|
pub message: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PlayerChat {
|
2019-12-31 00:01:49 -04:00
|
|
|
pub fn new(padding: u32, guildcard: u32, mut message: String) -> PlayerChat {
|
|
|
|
let mut mlen = (message.len() * 2) + 0x12;
|
|
|
|
while mlen & 0x07 != 0 {
|
|
|
|
message.push('\0');
|
|
|
|
mlen += 1;
|
|
|
|
}
|
2019-12-26 19:00:51 -04:00
|
|
|
PlayerChat {
|
|
|
|
padding: padding,
|
|
|
|
guildcard: guildcard,
|
|
|
|
message: message,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|