You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

711 lines
13 KiB

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<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(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<BlockEntry>
}
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<BankItem>,
}
#[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<PlayerInfo>,
}
#[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,
}
}
}
// this is literally the same struct as 0x01.
#[pso_packet(0xB0)]
pub struct RightText {
padding: [u32; 0x02],
msg: String,
}
impl RightText {
pub fn new(mut msg: String) -> RightText {
if !msg.ends_with('\0') {
msg.push('\0');
}
RightText {
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(0x1A)]
pub struct LargeDialog {
padding: [u32; 0x02],
msg: String,
}
impl LargeDialog {
pub fn new(mut msg: String) -> LargeDialog {
if !msg.ends_with('\0') {
msg.push('\0');
}
LargeDialog {
padding: [0, 0x45000000],
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(0x6ED)]
pub struct UpdateTechMenu {
pub config: [u8; 0x28],
}
#[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<InfoboardResponse>,
}
#[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<RoomList>,
}
#[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<QuestCategory>,
}
#[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<QuestEntry>,
}
#[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],
#[nodebug]
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,
}