#pragma once #include #include #include #include #include #include "Version.hh" #include "Text.hh" // raw item data // TODO: use parray for the fields here struct ItemData { union { uint8_t item_data1[12]; le_uint16_t item_data1w[6]; le_uint32_t item_data1d[3]; } __attribute__((packed)); le_uint32_t item_id; union { uint8_t item_data2[4]; le_uint16_t item_data2w[2]; le_uint32_t item_data2d; } __attribute__((packed)); uint32_t primary_identifier() const; } __attribute__((packed)); struct PlayerBankItem; // an item in a player's inventory struct PlayerInventoryItem { le_uint16_t equip_flags; le_uint16_t tech_flag; le_uint32_t game_flags; ItemData data; PlayerBankItem to_bank_item() const; } __attribute__((packed)); // an item in a player's bank struct PlayerBankItem { ItemData data; le_uint16_t amount; le_uint16_t show_flags; PlayerInventoryItem to_inventory_item() const; } __attribute__((packed)); // a player's inventory (remarkably, the format is the same in all versions of PSO) struct PlayerInventory { uint8_t num_items; uint8_t hp_materials_used; uint8_t tp_materials_used; uint8_t language; PlayerInventoryItem items[30]; size_t find_item(uint32_t item_id); } __attribute__((packed)); // a player's bank struct PlayerBank { le_uint32_t num_items; le_uint32_t meseta; PlayerBankItem items[200]; void load(const std::string& filename); void save(const std::string& filename) const; bool switch_with_file(const std::string& save_filename, const std::string& load_filename); void add_item(const PlayerBankItem& item); void remove_item(uint32_t item_id, uint32_t amount, PlayerBankItem* item); size_t find_item(uint32_t item_id); } __attribute__((packed)); // simple player stats struct PlayerStats { le_uint16_t atp; le_uint16_t mst; le_uint16_t evp; le_uint16_t hp; le_uint16_t dfp; le_uint16_t ata; le_uint16_t lck; PlayerStats() noexcept; } __attribute__((packed)); struct PlayerDispDataBB; // PC/GC player appearance and stats data struct PlayerDispDataPCGC { // 0xD0 in size PlayerStats stats; parray unknown_a1; le_uint32_t level; le_uint32_t experience; le_uint32_t meseta; ptext name; uint64_t unknown_a2; le_uint32_t name_color; uint8_t extra_model; parray unused; le_uint32_t name_color_checksum; uint8_t section_id; uint8_t char_class; uint8_t v2_flags; uint8_t version; le_uint32_t v1_flags; le_uint16_t costume; le_uint16_t skin; le_uint16_t face; le_uint16_t head; le_uint16_t hair; le_uint16_t hair_r; le_uint16_t hair_g; le_uint16_t hair_b; le_float proportion_x; le_float proportion_y; parray config; parray technique_levels; // Note: This struct has a default constructor because it's used in a command // that has a fixed-size array. If we didn't define this constructor, the // trivial fields in that array's members would be uninitialized, and we could // send uninitialized memory to the client. PlayerDispDataPCGC() noexcept; void enforce_pc_limits(); PlayerDispDataBB to_bb() const; } __attribute__((packed)); // BB player preview format struct PlayerDispDataBBPreview { le_uint32_t experience; le_uint32_t level; ptext guild_card; uint64_t unknown_a2; le_uint32_t name_color; uint8_t extra_model; parray unused; le_uint32_t name_color_checksum; uint8_t section_id; uint8_t char_class; uint8_t v2_flags; uint8_t version; le_uint32_t v1_flags; le_uint16_t costume; le_uint16_t skin; le_uint16_t face; le_uint16_t head; le_uint16_t hair; le_uint16_t hair_r; le_uint16_t hair_g; le_uint16_t hair_b; le_float proportion_x; le_float proportion_y; ptext name; uint32_t play_time; PlayerDispDataBBPreview() noexcept; } __attribute__((packed)); // BB player appearance and stats data struct PlayerDispDataBB { PlayerStats stats; parray unknown_a1; le_uint32_t level; le_uint32_t experience; le_uint32_t meseta; ptext guild_card; uint64_t unknown_a2; le_uint32_t name_color; uint8_t extra_model; parray unused; le_uint32_t name_color_checksum; uint8_t section_id; uint8_t char_class; uint8_t v2_flags; uint8_t version; le_uint32_t v1_flags; le_uint16_t costume; le_uint16_t skin; le_uint16_t face; le_uint16_t head; le_uint16_t hair; le_uint16_t hair_r; le_uint16_t hair_g; le_uint16_t hair_b; le_float proportion_x; le_float proportion_y; ptext name; parray config; parray technique_levels; PlayerDispDataBB() noexcept; inline void enforce_pc_limits() { } PlayerDispDataPCGC to_pcgc() const; PlayerDispDataBBPreview to_preview() const; void apply_preview(const PlayerDispDataBBPreview&); } __attribute__((packed)); struct GuildCardGC { le_uint32_t player_tag; le_uint32_t serial_number; ptext name; ptext desc; uint8_t reserved1; // should be 1 uint8_t reserved2; // should be 1 uint8_t section_id; uint8_t char_class; GuildCardGC() noexcept; } __attribute__((packed)); // BB guild card format struct GuildCardBB { le_uint32_t serial_number; ptext name; ptext teamname; ptext desc; uint8_t reserved1; // should be 1 uint8_t reserved2; // should be 1 uint8_t section_id; uint8_t char_class; GuildCardBB() noexcept; } __attribute__((packed)); // an entry in the BB guild card file struct GuildCardEntryBB { GuildCardBB data; parray unknown; } __attribute__((packed)); // the format of the BB guild card file struct GuildCardFileBB { parray unknown_a1; GuildCardEntryBB entry[0x0068]; // that's 104 of them in decimal parray unknown_a2; } __attribute__((packed)); // PSOBB key config and team info struct KeyAndTeamConfigBB { parray unknown_a1; // 0000 parray key_config; // 0114 parray joystick_config; // 0280 le_uint32_t serial_number; // 02B8 le_uint32_t team_id; // 02BC le_uint64_t team_info; // 02C0 le_uint16_t team_privilege_level; // 02C8 le_uint16_t reserved; // 02CA ptext team_name; // 02CC parray team_flag; // 02EC le_uint64_t team_rewards; // 0AEC } __attribute__((packed)); // BB account data struct PlayerAccountDataBB { parray symbol_chats; KeyAndTeamConfigBB key_config; GuildCardFileBB guild_cards; le_uint32_t options; parray shortcuts; // chat shortcuts (@1FB4 in E7 command) } __attribute__((packed)); struct PlayerLobbyDataPC { le_uint32_t player_tag; le_uint32_t guild_card; be_uint32_t ip_address; le_uint32_t client_id; ptext name; PlayerLobbyDataPC() noexcept; } __attribute__((packed)); struct PlayerLobbyDataGC { le_uint32_t player_tag; le_uint32_t guild_card; be_uint32_t ip_address; le_uint32_t client_id; ptext name; PlayerLobbyDataGC() noexcept; } __attribute__((packed)); struct PlayerLobbyDataBB { le_uint32_t player_tag; le_uint32_t guild_card; be_uint32_t ip_address; // Guess - the official builds didn't use this, but all other versions have it parray unknown_a1; le_uint32_t client_id; ptext name; le_uint32_t unknown2; PlayerLobbyDataBB() noexcept; } __attribute__((packed)); struct PSOPlayerDataPC { // for command 0x61 PlayerInventory inventory; PlayerDispDataPCGC disp; } __attribute__((packed)); struct PSOPlayerDataGC { // for command 0x61 PlayerInventory inventory; PlayerDispDataPCGC disp; parray unknown; ptext info_board; parray blocked_senders; le_uint32_t auto_reply_enabled; char auto_reply[0]; } __attribute__((packed)); struct PSOPlayerDataBB { // for command 0x61 PlayerInventory inventory; PlayerDispDataBB disp; ptext unused; ptext info_board; parray blocked_senders; le_uint32_t auto_reply_enabled; char16_t auto_reply[0]; } __attribute__((packed)); // complete BB player data format (used in E7 command) struct PlayerBB { PlayerInventory inventory; // 0000 // player PlayerDispDataBB disp; // 034C // player parray unknown; // 04DC // le_uint32_t option_flags; // 04EC // account parray quest_data1; // 04F0 // player PlayerBank bank; // 06F8 // player le_uint32_t serial_number; // 19C0 // player ptext name; // 19C4 // player ptext team_name; // 19C4 // player ptext guild_card_desc; // 1A14 // player uint8_t reserved1; // 1AC4 // player uint8_t reserved2; // 1AC5 // player uint8_t section_id; // 1AC6 // player uint8_t char_class; // 1AC7 // player le_uint32_t unknown3; // 1AC8 // parray symbol_chats; // 1ACC // account parray shortcuts; // 1FAC // account ptext auto_reply; // 29EC // player ptext info_board; // 2B44 // player parray unknown5; // 2C9C // parray challenge_data; // 2CB8 // player parray tech_menu_config; // 2DF8 // player parray unknown6; // 2E20 // parray quest_data2; // 2E4C // player KeyAndTeamConfigBB key_config; // 2EA4 // account } __attribute__((packed)); // total size: 39A0 struct SavedPlayerBB { // .nsc file format ptext signature; PlayerDispDataBBPreview preview; ptext auto_reply; PlayerBank bank; parray challenge_data; PlayerDispDataBB disp; ptext guild_card_desc; ptext info_board; PlayerInventory inventory; parray quest_data1; parray quest_data2; parray tech_menu_config; } __attribute__((packed)); struct SavedAccountBB { // .nsa file format ptext signature; parray blocked_senders; GuildCardFileBB guild_cards; KeyAndTeamConfigBB key_config; le_uint32_t option_flags; parray shortcuts; parray symbol_chats; ptext team_name; } __attribute__((packed)); // complete player info stored by the server struct Player { le_uint32_t loaded_from_shipgate_time; ptext auto_reply; // player PlayerBank bank; // player ptext bank_name; // not saved parray blocked_senders; // account parray challenge_data; // player PlayerDispDataBB disp; // player parray ep3_config; // not saved ptext guild_card_desc; // player GuildCardFileBB guild_cards; // account PlayerInventoryItem identify_result; // not saved ptext info_board; // player PlayerInventory inventory; // player KeyAndTeamConfigBB key_config; // account le_uint32_t option_flags; // account parray quest_data1; // player parray quest_data2; // player le_uint32_t serial_number; // account identifier std::vector current_shop_contents; // not saved parray shortcuts; // account parray symbol_chats; // account ptext team_name; // account parray tech_menu_config; // player void load_player_data(const std::string& filename); void save_player_data(const std::string& filename) const; void load_account_data(const std::string& filename); void save_account_data(const std::string& filename) const; void import(const PSOPlayerDataPC& pd); void import(const PSOPlayerDataGC& pd); void import(const PSOPlayerDataBB& pd); PlayerBB export_bb_player_data() const; void add_item(const PlayerInventoryItem& item); void remove_item(uint32_t item_id, uint32_t amount, PlayerInventoryItem* item); size_t find_item(uint32_t item_id); }; uint32_t compute_guild_card_checksum(const void* data, size_t size); std::string filename_for_player_bb(const std::string& username, uint8_t player_index); std::string filename_for_bank_bb(const std::string& username, const std::string& bank_name); std::string filename_for_class_template_bb(uint8_t char_class); std::string filename_for_account_bb(const std::string& username); template DestT convert_player_disp_data(const SrcT&) { static_assert(always_false::v, "unspecialized strcpy_t should never be called"); } template <> inline PlayerDispDataPCGC convert_player_disp_data( const PlayerDispDataPCGC& src) { return src; } template <> inline PlayerDispDataPCGC convert_player_disp_data( const PlayerDispDataBB& src) { return src.to_pcgc(); } template <> inline PlayerDispDataBB convert_player_disp_data( const PlayerDispDataPCGC& src) { return src.to_bb(); } template <> inline PlayerDispDataBB convert_player_disp_data( const PlayerDispDataBB& src) { return src; }