|
|
@ -1,7 +1,8 @@ |
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
|
|
|
|
use psopacket::pso_packet;
|
|
|
|
use crate::{PSOPacket, PacketParseError};
|
|
|
|
//use psopacket::pso_packet;
|
|
|
|
use psopacket::pso_packet2 as pso_packet;
|
|
|
|
use crate::{PSOPacket, PacketParseError, PSOPacketData};
|
|
|
|
|
|
|
|
use crate::character::character::SelectScreenCharacter;
|
|
|
|
|
|
|
@ -11,13 +12,10 @@ pub const PATCH_FILE_CHUNK_SIZE: u16 = 0x8000; // 32kb |
|
|
|
pub const GUILD_CARD_CHUNK_SIZE: usize = 0x6800;
|
|
|
|
pub const PARAM_DATA_CHUNK_SIZE: usize = 0x6800;
|
|
|
|
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
type u8_str = u8;
|
|
|
|
|
|
|
|
#[pso_packet(0x03)]
|
|
|
|
pub struct LoginWelcome {
|
|
|
|
flag: u32,
|
|
|
|
copyright: [u8_str; 0x60],
|
|
|
|
#[utf8]
|
|
|
|
copyright: [u8; 0x60],
|
|
|
|
server_key: [u8; 48],
|
|
|
|
client_key: [u8; 48],
|
|
|
|
}
|
|
|
@ -27,7 +25,6 @@ impl LoginWelcome { |
|
|
|
let mut copyright = [0u8; 0x60];
|
|
|
|
copyright[..0x4B].clone_from_slice(b"Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM.");
|
|
|
|
LoginWelcome {
|
|
|
|
flag: 0,
|
|
|
|
copyright: copyright,
|
|
|
|
server_key: server_key,
|
|
|
|
client_key: client_key,
|
|
|
@ -37,15 +34,16 @@ impl LoginWelcome { |
|
|
|
|
|
|
|
#[pso_packet(0x93)]
|
|
|
|
pub struct Login {
|
|
|
|
pub flag: u32,
|
|
|
|
pub tag: u32,
|
|
|
|
pub guildcard: u32,
|
|
|
|
pub version: u16,
|
|
|
|
pub unknown1: [u8; 6],
|
|
|
|
pub team: u32,
|
|
|
|
pub username: [u8_str; 16],
|
|
|
|
#[utf8]
|
|
|
|
pub username: [u8; 16],
|
|
|
|
pub unknown2: [u8; 32],
|
|
|
|
pub password: [u8_str; 16],
|
|
|
|
#[utf8]
|
|
|
|
pub password: [u8; 16],
|
|
|
|
pub unknown3: [u8; 40],
|
|
|
|
pub hwinfo: [u8; 8],
|
|
|
|
pub security_data: [u8; 40],
|
|
|
@ -67,27 +65,10 @@ pub enum AccountStatus { |
|
|
|
BadVersion,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountStatus {
|
|
|
|
const SIZE: usize = 4;
|
|
|
|
|
|
|
|
fn to_le_bytes(&self) -> [u8; 4] {
|
|
|
|
[match self {
|
|
|
|
AccountStatus::Ok => 0,
|
|
|
|
AccountStatus::Error => 1,
|
|
|
|
AccountStatus::InvalidPassword => 2,
|
|
|
|
AccountStatus::InvalidPassword2 => 3,
|
|
|
|
AccountStatus::Maintenance => 4,
|
|
|
|
AccountStatus::AlreadyOnline => 5,
|
|
|
|
AccountStatus::Banned => 6,
|
|
|
|
AccountStatus::Banned2 => 7,
|
|
|
|
AccountStatus::InvalidUser => 8,
|
|
|
|
AccountStatus::PayUp => 9,
|
|
|
|
AccountStatus::Locked => 10,
|
|
|
|
AccountStatus::BadVersion => 11,
|
|
|
|
},0,0,0]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_le_bytes(bytes: [u8; 4]) -> Result<AccountStatus, PacketParseError> {
|
|
|
|
impl PSOPacketData for AccountStatus {
|
|
|
|
fn from_bytes<R: Read>(cursor: &mut R) -> Result<Self, PacketParseError> {
|
|
|
|
let mut bytes = [0u8; 4];
|
|
|
|
cursor.read(&mut bytes).map_err(|_| PacketParseError::ReadError)?;
|
|
|
|
match bytes[0] {
|
|
|
|
0 => Ok(AccountStatus::Ok),
|
|
|
|
1 => Ok(AccountStatus::Error),
|
|
|
@ -104,12 +85,28 @@ impl AccountStatus { |
|
|
|
_ => Err(PacketParseError::InvalidValue),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
|
|
vec![match self {
|
|
|
|
AccountStatus::Ok => 0,
|
|
|
|
AccountStatus::Error => 1,
|
|
|
|
AccountStatus::InvalidPassword => 2,
|
|
|
|
AccountStatus::InvalidPassword2 => 3,
|
|
|
|
AccountStatus::Maintenance => 4,
|
|
|
|
AccountStatus::AlreadyOnline => 5,
|
|
|
|
AccountStatus::Banned => 6,
|
|
|
|
AccountStatus::Banned2 => 7,
|
|
|
|
AccountStatus::InvalidUser => 8,
|
|
|
|
AccountStatus::PayUp => 9,
|
|
|
|
AccountStatus::Locked => 10,
|
|
|
|
AccountStatus::BadVersion => 11,
|
|
|
|
},0,0,0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[pso_packet(0xE6)]
|
|
|
|
pub struct LoginResponse {
|
|
|
|
pub flag: u32,
|
|
|
|
pub status: AccountStatus,
|
|
|
|
pub tag: u32,
|
|
|
|
pub guildcard: u32,
|
|
|
@ -121,7 +118,6 @@ pub struct LoginResponse { |
|
|
|
impl LoginResponse {
|
|
|
|
pub fn by_status(status: AccountStatus, security_data: [u8; 40]) -> LoginResponse {
|
|
|
|
LoginResponse {
|
|
|
|
flag: 0,
|
|
|
|
status: status,
|
|
|
|
tag: 0x00010000,
|
|
|
|
//tag: 0x00000100,
|
|
|
@ -133,7 +129,6 @@ impl LoginResponse { |
|
|
|
}
|
|
|
|
pub fn by_char_select(guildcard: u32, team_id: u32, security_data: [u8; 40]) -> LoginResponse {
|
|
|
|
LoginResponse {
|
|
|
|
flag: 0,
|
|
|
|
status: AccountStatus::Ok,
|
|
|
|
tag: 0x00010000,
|
|
|
|
//tag: 0x00000100,
|
|
|
@ -148,12 +143,10 @@ impl LoginResponse { |
|
|
|
|
|
|
|
#[pso_packet(0xE0)]
|
|
|
|
pub struct RequestSettings {
|
|
|
|
pub flag: u32 |
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0xE2)]
|
|
|
|
pub struct SendKeyAndTeamSettings {
|
|
|
|
flag: u32,
|
|
|
|
unknown: [u8; 0x114],
|
|
|
|
key_config: [u8; 0x16C],
|
|
|
|
joystick_config: [u8; 0x38],
|
|
|
@ -165,6 +158,7 @@ pub struct SendKeyAndTeamSettings { |
|
|
|
unknown2: u16,
|
|
|
|
//team_name: [u16; 16],
|
|
|
|
team_name: [u8; 32],
|
|
|
|
#[nodebug]
|
|
|
|
team_flag: [u8; 2048],
|
|
|
|
team_rewards: [u8; 8],
|
|
|
|
}
|
|
|
@ -172,7 +166,6 @@ pub struct SendKeyAndTeamSettings { |
|
|
|
impl SendKeyAndTeamSettings {
|
|
|
|
pub fn new(key_config: [u8; 0x16C], joystick_config: [u8; 0x38], guildcard: u32, team_id: u32) -> SendKeyAndTeamSettings {
|
|
|
|
SendKeyAndTeamSettings {
|
|
|
|
flag: 0,
|
|
|
|
unknown: [0; 0x114],
|
|
|
|
key_config: key_config,
|
|
|
|
joystick_config: joystick_config,
|
|
|
@ -192,7 +185,6 @@ impl SendKeyAndTeamSettings { |
|
|
|
|
|
|
|
#[pso_packet(0x19)]
|
|
|
|
pub struct RedirectClient {
|
|
|
|
pub flag: u32,
|
|
|
|
pub ip: u32,
|
|
|
|
pub port: u16,
|
|
|
|
pub padding: u16,
|
|
|
@ -201,7 +193,6 @@ pub struct RedirectClient { |
|
|
|
impl RedirectClient {
|
|
|
|
pub fn new(ip: u32, port: u16) -> RedirectClient {
|
|
|
|
RedirectClient {
|
|
|
|
flag: 0,
|
|
|
|
ip: ip,
|
|
|
|
port: port,
|
|
|
|
padding: 0,
|
|
|
@ -211,21 +202,18 @@ impl RedirectClient { |
|
|
|
|
|
|
|
#[pso_packet(0x1E8)]
|
|
|
|
pub struct Checksum {
|
|
|
|
pub flag: u32,
|
|
|
|
pub checksum: u32,
|
|
|
|
pub padding: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x2E8)]
|
|
|
|
pub struct ChecksumAck {
|
|
|
|
pub flag: u32,
|
|
|
|
pub ack: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChecksumAck {
|
|
|
|
pub fn new(ack: u32) -> ChecksumAck {
|
|
|
|
ChecksumAck {
|
|
|
|
flag: 0,
|
|
|
|
ack: ack,
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -233,34 +221,40 @@ impl ChecksumAck { |
|
|
|
|
|
|
|
#[pso_packet(0xE3)]
|
|
|
|
pub struct CharSelect {
|
|
|
|
pub flag: u32,
|
|
|
|
pub slot: u32,
|
|
|
|
pub reason: u32, // TODO: enum?
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0xE4)]
|
|
|
|
pub struct CharAck {
|
|
|
|
pub flag: u32,
|
|
|
|
pub slot: u32,
|
|
|
|
pub code: u32, // TODO: enum?
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PSOPacketData for SelectScreenCharacter {
|
|
|
|
fn from_bytes<R: Read>(cursor: &mut R) -> Result<Self, PacketParseError> {
|
|
|
|
let mut buf = [0u8; SelectScreenCharacter::SIZE];
|
|
|
|
cursor.read(&mut buf).map_err(|_| PacketParseError::ReadError)?;
|
|
|
|
SelectScreenCharacter::from_le_bytes(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
|
|
self.to_le_bytes().to_vec()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0xE5)]
|
|
|
|
pub struct CharacterPreview {
|
|
|
|
pub flag: u32,
|
|
|
|
pub slot: u32,
|
|
|
|
pub character: SelectScreenCharacter,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x3E8)]
|
|
|
|
pub struct GuildcardDataRequest {
|
|
|
|
flag: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x1DC)]
|
|
|
|
pub struct GuildcardDataHeader {
|
|
|
|
flag: u32,
|
|
|
|
one: u32,
|
|
|
|
len: u32,
|
|
|
|
checksum: u32,
|
|
|
@ -269,7 +263,6 @@ pub struct GuildcardDataHeader { |
|
|
|
impl GuildcardDataHeader {
|
|
|
|
pub fn new(len: usize, checksum: u32) -> GuildcardDataHeader {
|
|
|
|
GuildcardDataHeader {
|
|
|
|
flag: 0,
|
|
|
|
one: 1,
|
|
|
|
len: len as u32,
|
|
|
|
checksum: checksum
|
|
|
@ -279,14 +272,12 @@ impl GuildcardDataHeader { |
|
|
|
|
|
|
|
#[pso_packet(0x3DC)]
|
|
|
|
pub struct GuildcardDataChunkRequest {
|
|
|
|
flag: u32,
|
|
|
|
_unknown: u32,
|
|
|
|
pub chunk: u32,
|
|
|
|
pub again: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GuildcardDataChunk {
|
|
|
|
flag: u32,
|
|
|
|
_unknown: u32,
|
|
|
|
chunk: u32,
|
|
|
|
pub buffer: [u8; GUILD_CARD_CHUNK_SIZE],
|
|
|
@ -297,7 +288,6 @@ pub struct GuildcardDataChunk { |
|
|
|
impl GuildcardDataChunk {
|
|
|
|
pub fn new(chunk: u32, buffer: [u8; GUILD_CARD_CHUNK_SIZE], len: usize) -> GuildcardDataChunk {
|
|
|
|
GuildcardDataChunk {
|
|
|
|
flag: 0,
|
|
|
|
_unknown: 0,
|
|
|
|
chunk: chunk as u32,
|
|
|
|
buffer: buffer,
|
|
|
@ -313,7 +303,7 @@ impl PSOPacket for GuildcardDataChunk { |
|
|
|
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
|
|
let mut buf: Vec<u8> = Vec::new();
|
|
|
|
buf.extend_from_slice(&u32::to_le_bytes(self.flag));
|
|
|
|
buf.extend_from_slice(&u32::to_le_bytes(0));
|
|
|
|
buf.extend_from_slice(&u32::to_le_bytes(self._unknown));
|
|
|
|
buf.extend_from_slice(&u32::to_le_bytes(self.chunk));
|
|
|
|
buf.extend_from_slice(&self.buffer[0..self.len]);
|
|
|
@ -333,7 +323,7 @@ impl PSOPacket for GuildcardDataChunk { |
|
|
|
impl std::fmt::Debug for GuildcardDataChunk {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
write!(f, "packet GuildcardDataChunk {{\n").unwrap();
|
|
|
|
write!(f, " flag: {:?}\n", self.flag).unwrap();
|
|
|
|
write!(f, " flag: {:?}\n", 0).unwrap();
|
|
|
|
write!(f, " _unknown: {:X?}\n", self._unknown).unwrap();
|
|
|
|
write!(f, " chunk: {:X?}\n", self.chunk).unwrap();
|
|
|
|
write!(f, " buffer: [0..{:X}]\n", self.len).unwrap();
|
|
|
@ -344,7 +334,6 @@ impl std::fmt::Debug for GuildcardDataChunk { |
|
|
|
|
|
|
|
#[pso_packet(0x4EB)]
|
|
|
|
pub struct ParamDataRequest {
|
|
|
|
flag: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
@ -398,27 +387,25 @@ impl std::fmt::Debug for ParamDataHeader { |
|
|
|
|
|
|
|
#[pso_packet(0x3EB)]
|
|
|
|
pub struct ParamDataChunkRequest {
|
|
|
|
flag: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pso_packet(0x2EB)]
|
|
|
|
pub struct ParamDataChunk {
|
|
|
|
pub flag: u32,
|
|
|
|
pub chunk: u32,
|
|
|
|
#[nodebug]
|
|
|
|
pub data: [u8; 0x6800], // TODO: why wont the const work here? (blame macros?)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[pso_packet(0xEC)]
|
|
|
|
pub struct SetFlag {
|
|
|
|
flag: u32,
|
|
|
|
pub flags: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[pso_packet(0xB1)]
|
|
|
|
pub struct Timestamp {
|
|
|
|
flag: u32,
|
|
|
|
#[utf8]
|
|
|
|
timestamp: [u8; 28],
|
|
|
|
}
|
|
|
|
|
|
|
@ -429,7 +416,6 @@ impl Timestamp { |
|
|
|
let mut timebuf = [0u8; 28];
|
|
|
|
timebuf[..timebytes.len()].clone_from_slice(timebytes);
|
|
|
|
Timestamp {
|
|
|
|
flag: 0,
|
|
|
|
timestamp: timebuf
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -444,8 +430,41 @@ pub struct ShipListEntry { |
|
|
|
pub name: [u16; 0x11],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PSOPacketData for ShipListEntry {
|
|
|
|
fn from_bytes<R: Read>(cursor: &mut R) -> Result<Self, PacketParseError> {
|
|
|
|
let mut buf4 = [0u8; 4];
|
|
|
|
cursor.read(&mut buf4).map_err(|_| PacketParseError::ReadError)?;
|
|
|
|
let menu = u32::from_le_bytes(buf4);
|
|
|
|
cursor.read(&mut buf4).map_err(|_| PacketParseError::ReadError)?;
|
|
|
|
let item = u32::from_le_bytes(buf4);
|
|
|
|
|
|
|
|
let mut buf2 = [0u8; 2];
|
|
|
|
cursor.read(&mut buf2).map_err(|_| PacketParseError::ReadError)?;
|
|
|
|
let flags = u16::from_le_bytes(buf2);
|
|
|
|
|
|
|
|
let mut name = [0u8; 0x11 * 2];
|
|
|
|
cursor.read(&mut name).map_err(|_| PacketParseError::ReadError)?;
|
|
|
|
Ok(ShipListEntry {
|
|
|
|
menu: menu,
|
|
|
|
item: item,
|
|
|
|
flags: flags,
|
|
|
|
name: unsafe { std::mem::transmute(name)}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
//self.to_le_bytes().to_vec()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub struct ShipList {
|
|
|
|
pub flag: u32,
|
|
|
|
pub ships: Vec<ShipListEntry>,
|
|
|
|
}
|
|
|
|
|
|
|
@ -490,7 +509,7 @@ impl PSOPacket for ShipList { |
|
|
|
impl std::fmt::Debug for ShipList {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
write!(f, "packet ShipList {{\n").unwrap();
|
|
|
|
write!(f, " flag: {:?}\n", self.flag).unwrap();
|
|
|
|
write!(f, " flag: {:?}\n", 0).unwrap();
|
|
|
|
write!(f, " ships: {:?}]\n", self.ships).unwrap();
|
|
|
|
write!(f, "}}")
|
|
|
|
}
|
|
|
@ -499,7 +518,6 @@ impl std::fmt::Debug for ShipList { |
|
|
|
|
|
|
|
#[pso_packet(0x10)]
|
|
|
|
pub struct MenuSelect {
|
|
|
|
pub flag: u32,
|
|
|
|
pub menu: u32,
|
|
|
|
pub item: u32,
|
|
|
|
}
|
|
|
@ -511,7 +529,6 @@ mod tests { |
|
|
|
fn test_account_status_enum() {
|
|
|
|
use super::PSOPacket;
|
|
|
|
let pkt = super::LoginResponse {
|
|
|
|
flag: 0,
|
|
|
|
status: super::AccountStatus::InvalidPassword,
|
|
|
|
tag: 0,
|
|
|
|
guildcard: 0,
|
|
|
|