add DC v2 save file format
This commit is contained in:
+5
-5
@@ -904,7 +904,7 @@ void Client::save_all() {
|
||||
}
|
||||
if (this->external_bank) {
|
||||
string filename = this->shared_bank_filename();
|
||||
save_object_file<PlayerBank>(filename, *this->external_bank);
|
||||
save_object_file<PlayerBank200>(filename, *this->external_bank);
|
||||
player_data_log.info("Saved shared bank file %s", filename.c_str());
|
||||
}
|
||||
if (this->external_bank_character) {
|
||||
@@ -1002,7 +1002,7 @@ void Client::save_and_unload_character() {
|
||||
}
|
||||
}
|
||||
|
||||
PlayerBank& Client::current_bank() {
|
||||
PlayerBank200& Client::current_bank() {
|
||||
if (this->external_bank) {
|
||||
return *this->external_bank;
|
||||
} else if (this->external_bank_character) {
|
||||
@@ -1018,7 +1018,7 @@ std::shared_ptr<PSOBBCharacterFile> Client::current_bank_character() {
|
||||
void Client::use_default_bank() {
|
||||
if (this->external_bank) {
|
||||
string filename = this->shared_bank_filename();
|
||||
save_object_file<PlayerBank>(filename, *this->external_bank);
|
||||
save_object_file<PlayerBank200>(filename, *this->external_bank);
|
||||
this->external_bank.reset();
|
||||
player_data_log.info("Detached shared bank %s", filename.c_str());
|
||||
}
|
||||
@@ -1040,12 +1040,12 @@ bool Client::use_shared_bank() {
|
||||
player_data_log.info("Using loaded shared bank %s", filename.c_str());
|
||||
return true;
|
||||
} else if (isfile(filename)) {
|
||||
this->external_bank = make_shared<PlayerBank>(load_object_file<PlayerBank>(filename));
|
||||
this->external_bank = make_shared<PlayerBank200>(load_object_file<PlayerBank200>(filename));
|
||||
files_manager->set_bank(filename, this->external_bank);
|
||||
player_data_log.info("Loaded shared bank %s", filename.c_str());
|
||||
return true;
|
||||
} else {
|
||||
this->external_bank = make_shared<PlayerBank>();
|
||||
this->external_bank = make_shared<PlayerBank200>();
|
||||
files_manager->set_bank(filename, this->external_bank);
|
||||
player_data_log.info("Created shared bank for %s", filename.c_str());
|
||||
return false;
|
||||
|
||||
+2
-2
@@ -381,7 +381,7 @@ public:
|
||||
void load_backup_character(uint32_t account_id, size_t index);
|
||||
void save_and_unload_character();
|
||||
|
||||
PlayerBank& current_bank();
|
||||
PlayerBank200& current_bank();
|
||||
std::shared_ptr<PSOBBCharacterFile> current_bank_character();
|
||||
bool use_shared_bank(); // Returns true if the bank exists; false if it was created
|
||||
void use_character_bank(int8_t bb_character_index);
|
||||
@@ -398,7 +398,7 @@ private:
|
||||
std::shared_ptr<PSOBBCharacterFile> overlay_character_data;
|
||||
std::shared_ptr<PSOBBCharacterFile> character_data;
|
||||
std::shared_ptr<PSOBBGuildCardFile> guild_card_data;
|
||||
std::shared_ptr<PlayerBank> external_bank;
|
||||
std::shared_ptr<PlayerBank200> external_bank;
|
||||
std::shared_ptr<PSOBBCharacterFile> external_bank_character;
|
||||
int8_t external_bank_character_index;
|
||||
uint64_t last_play_time_update;
|
||||
|
||||
@@ -7215,5 +7215,7 @@ struct G_RejectBattleStartRequest_Ep3_6xB4x53 {
|
||||
// implement extended functionality.
|
||||
|
||||
// 30 (C->S): Extended player info
|
||||
// Requested with the GetExtendedPlayerInfo patch.
|
||||
// Format is PSOGCCharacterFile::Character or PSOXBCharacterFileCharacter.
|
||||
// Requested with the GetExtendedPlayerInfo patch. Format depends on version:
|
||||
// DC v2: PSODCV2CharacterFile
|
||||
// GC v3: PSOGCCharacterFile::Character
|
||||
// XB v3: PSOXBCharacterFileCharacter
|
||||
|
||||
@@ -66,7 +66,7 @@ std::shared_ptr<PSOBBGuildCardFile> PlayerFilesManager::get_guild_card(const std
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PlayerBank> PlayerFilesManager::get_bank(const std::string& filename) {
|
||||
std::shared_ptr<PlayerBank200> PlayerFilesManager::get_bank(const std::string& filename) {
|
||||
try {
|
||||
return this->loaded_bank_files.at(filename);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -92,7 +92,7 @@ void PlayerFilesManager::set_guild_card(const std::string& filename, std::shared
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerFilesManager::set_bank(const std::string& filename, std::shared_ptr<PlayerBank> file) {
|
||||
void PlayerFilesManager::set_bank(const std::string& filename, std::shared_ptr<PlayerBank200> file) {
|
||||
if (!this->loaded_bank_files.emplace(filename, file).second) {
|
||||
throw runtime_error("bank file already loaded: " + filename);
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ public:
|
||||
std::shared_ptr<PSOBBBaseSystemFile> get_system(const std::string& filename);
|
||||
std::shared_ptr<PSOBBCharacterFile> get_character(const std::string& filename);
|
||||
std::shared_ptr<PSOBBGuildCardFile> get_guild_card(const std::string& filename);
|
||||
std::shared_ptr<PlayerBank> get_bank(const std::string& filename);
|
||||
std::shared_ptr<PlayerBank200> get_bank(const std::string& filename);
|
||||
|
||||
void set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file);
|
||||
void set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file);
|
||||
void set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file);
|
||||
void set_bank(const std::string& filename, std::shared_ptr<PlayerBank> file);
|
||||
void set_bank(const std::string& filename, std::shared_ptr<PlayerBank200> file);
|
||||
|
||||
private:
|
||||
std::shared_ptr<struct event_base> base;
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
std::unordered_map<std::string, std::shared_ptr<PSOBBBaseSystemFile>> loaded_system_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PSOBBCharacterFile>> loaded_character_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PSOBBGuildCardFile>> loaded_guild_card_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PlayerBank>> loaded_bank_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PlayerBank200>> loaded_bank_files;
|
||||
|
||||
static void clear_expired_files(evutil_socket_t fd, short events, void* ctx);
|
||||
};
|
||||
|
||||
+18
-11
@@ -234,6 +234,7 @@ struct PlayerInventoryT {
|
||||
}
|
||||
|
||||
size_t remove_all_items_of_type(uint8_t data1_0, int16_t data1_1 = -1) {
|
||||
|
||||
size_t write_offset = 0;
|
||||
for (size_t read_offset = 0; read_offset < this->num_items; read_offset++) {
|
||||
bool should_delete = ((this->items[read_offset].data.data1[0] == data1_0) &&
|
||||
@@ -287,6 +288,7 @@ struct PlayerInventoryT {
|
||||
}
|
||||
|
||||
operator PlayerInventoryT<!IsBigEndian>() const {
|
||||
|
||||
PlayerInventoryT<!IsBigEndian> ret;
|
||||
ret.num_items = this->num_items;
|
||||
ret.hp_from_materials = this->hp_from_materials;
|
||||
@@ -301,14 +303,14 @@ using PlayerInventoryBE = PlayerInventoryT<true>;
|
||||
check_struct_size(PlayerInventory, 0x34C);
|
||||
check_struct_size(PlayerInventoryBE, 0x34C);
|
||||
|
||||
template <bool IsBigEndian>
|
||||
template <size_t SlotCount, bool IsBigEndian>
|
||||
struct PlayerBankT {
|
||||
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
|
||||
|
||||
/* 0000 */ U32T num_items = 0;
|
||||
/* 0004 */ U32T meseta = 0;
|
||||
/* 0008 */ parray<PlayerBankItemT<IsBigEndian>, 200> items;
|
||||
/* 12C8 */
|
||||
/* 0008 */ parray<PlayerBankItemT<IsBigEndian>, SlotCount> items;
|
||||
/* 05A8 for 60 items (v1/v2), 12C8 for 200 items (v3/v4) */
|
||||
|
||||
void add_item(const ItemData& item, const ItemData::StackLimits& limits) {
|
||||
uint32_t primary_identifier = item.primary_identifier();
|
||||
@@ -341,7 +343,7 @@ struct PlayerBankT {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->num_items >= 200) {
|
||||
if (this->num_items >= SlotCount) {
|
||||
throw std::runtime_error("no free space in bank");
|
||||
}
|
||||
auto& last_item = this->items[this->num_items];
|
||||
@@ -352,6 +354,7 @@ struct PlayerBankT {
|
||||
}
|
||||
|
||||
ItemData remove_item(uint32_t item_id, uint32_t amount, const ItemData::StackLimits& limits) {
|
||||
|
||||
size_t index = this->find_item(item_id);
|
||||
auto& bank_item = this->items[index];
|
||||
|
||||
@@ -395,17 +398,21 @@ struct PlayerBankT {
|
||||
}
|
||||
}
|
||||
|
||||
operator PlayerBankT<!IsBigEndian>() const {
|
||||
PlayerBankT<!IsBigEndian> ret;
|
||||
template <size_t DestSlotCount, bool DestIsBigEndian>
|
||||
operator PlayerBankT<DestSlotCount, DestIsBigEndian>() const {
|
||||
|
||||
PlayerBankT<DestSlotCount, DestIsBigEndian> ret;
|
||||
ret.num_items = this->num_items.load();
|
||||
ret.meseta = this->meseta.load();
|
||||
for (size_t z = 0; z < this->items.size(); z++) {
|
||||
for (size_t z = 0; z < std::min<size_t>(ret.items.size(), this->items.size()); z++) {
|
||||
ret.items[z] = this->items[z];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} __packed__;
|
||||
using PlayerBank = PlayerBankT<false>;
|
||||
using PlayerBankBE = PlayerBankT<true>;
|
||||
check_struct_size(PlayerBank, 0x12C8);
|
||||
check_struct_size(PlayerBankBE, 0x12C8);
|
||||
using PlayerBank60 = PlayerBankT<60, false>;
|
||||
using PlayerBank200 = PlayerBankT<200, false>;
|
||||
using PlayerBank200BE = PlayerBankT<200, true>;
|
||||
check_struct_size(PlayerBank60, 0x05A8);
|
||||
check_struct_size(PlayerBank200, 0x12C8);
|
||||
check_struct_size(PlayerBank200BE, 0x12C8);
|
||||
|
||||
@@ -3323,6 +3323,9 @@ static void on_30(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> bb_char;
|
||||
switch (c->version()) {
|
||||
case Version::DC_V2:
|
||||
bb_char = PSOBBCharacterFile::create_from_dc_v2(check_size_t<PSODCV2CharacterFile>(data));
|
||||
break;
|
||||
case Version::GC_V3:
|
||||
bb_char = PSOBBCharacterFile::create_from_gc(check_size_t<PSOGCCharacterFile::Character>(data));
|
||||
break;
|
||||
@@ -3332,7 +3335,6 @@ static void on_30(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
case Version::DC_NTE:
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
case Version::DC_V1:
|
||||
case Version::DC_V2:
|
||||
case Version::PC_NTE:
|
||||
case Version::PC_V2:
|
||||
case Version::GC_NTE:
|
||||
|
||||
+39
-6
@@ -418,6 +418,39 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
guild_card_number, language, preview.visual, preview.name.decode(language), level_table);
|
||||
}
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_dc_v2(const PSODCV2CharacterFile& dc) {
|
||||
auto ret = make_shared<PSOBBCharacterFile>();
|
||||
ret->inventory = dc.inventory;
|
||||
ret->inventory.decode_from_client(Version::DC_V2);
|
||||
uint8_t language = ret->inventory.language;
|
||||
ret->disp = dc.disp.to_bb(language, language);
|
||||
ret->creation_timestamp = dc.creation_timestamp;
|
||||
ret->play_time_seconds = dc.play_time_seconds;
|
||||
ret->option_flags = dc.option_flags;
|
||||
ret->save_count = dc.save_count;
|
||||
ret->quest_flags = dc.quest_flags;
|
||||
ret->bank = dc.bank;
|
||||
ret->guild_card = dc.guild_card;
|
||||
for (size_t z = 0; z < std::min<size_t>(ret->symbol_chats.size(), dc.symbol_chats.size()); z++) {
|
||||
auto& ret_sc = ret->symbol_chats[z];
|
||||
const auto& dc_sc = dc.symbol_chats[z];
|
||||
ret_sc.present = dc_sc.present.load();
|
||||
ret_sc.name.encode(dc_sc.name.decode(language), language);
|
||||
ret_sc.spec = dc_sc.spec;
|
||||
}
|
||||
for (size_t z = 0; z < std::min<size_t>(ret->shortcuts.size(), dc.shortcuts.size()); z++) {
|
||||
ret->shortcuts[z] = dc.shortcuts[z].convert<false, TextEncoding::UTF16, 0x50>(language);
|
||||
}
|
||||
ret->battle_records = dc.battle_records;
|
||||
ret->challenge_records = dc.challenge_records;
|
||||
ret->tech_menu_shortcut_entries = dc.tech_menu_shortcut_entries;
|
||||
for (size_t z = 0; z < 5; z++) {
|
||||
ret->choice_search_config.entries[z].parent_choice_id = dc.choice_search_config[z * 2].load();
|
||||
ret->choice_search_config.entries[z].choice_id = dc.choice_search_config[z * 2 + 1].load();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_gc(const PSOGCCharacterFile::Character& gc) {
|
||||
auto ret = make_shared<PSOBBCharacterFile>();
|
||||
ret->inventory = gc.inventory;
|
||||
@@ -440,7 +473,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_gc(const PSOGCCha
|
||||
ret_sc.spec = gc_sc.spec;
|
||||
}
|
||||
for (size_t z = 0; z < std::min<size_t>(ret->shortcuts.size(), gc.shortcuts.size()); z++) {
|
||||
ret->shortcuts[z] = gc.shortcuts[z].convert<false, TextEncoding::UTF16>(language);
|
||||
ret->shortcuts[z] = gc.shortcuts[z].convert<false, TextEncoding::UTF16, 0x50>(language);
|
||||
}
|
||||
ret->auto_reply.encode(gc.auto_reply.decode(language), language);
|
||||
ret->info_board.encode(gc.info_board.decode(language), language);
|
||||
@@ -482,7 +515,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_xb(const PSOXBCha
|
||||
ret_sc.spec = xb_sc.spec;
|
||||
}
|
||||
for (size_t z = 0; z < std::min<size_t>(ret->shortcuts.size(), xb.shortcuts.size()); z++) {
|
||||
ret->shortcuts[z] = xb.shortcuts[z].convert<false, TextEncoding::UTF16>(language);
|
||||
ret->shortcuts[z] = xb.shortcuts[z].convert<false, TextEncoding::UTF16, 0x50>(language);
|
||||
}
|
||||
ret->auto_reply.encode(xb.auto_reply.decode(language), language);
|
||||
ret->info_board.encode(xb.info_board.decode(language), language);
|
||||
@@ -525,7 +558,7 @@ PSOGCCharacterFile::Character PSOBBCharacterFile::to_gc() const {
|
||||
ret_sc.spec = gc_sc.spec;
|
||||
}
|
||||
for (size_t z = 0; z < std::min<size_t>(ret.shortcuts.size(), this->shortcuts.size()); z++) {
|
||||
ret.shortcuts[z] = this->shortcuts[z].convert<true, TextEncoding::MARKED>(language);
|
||||
ret.shortcuts[z] = this->shortcuts[z].convert<true, TextEncoding::MARKED, 0x50>(language);
|
||||
}
|
||||
ret.auto_reply.encode(this->auto_reply.decode(language), language);
|
||||
ret.info_board.encode(this->info_board.decode(language), language);
|
||||
@@ -570,7 +603,7 @@ PSOXBCharacterFileCharacter PSOBBCharacterFile::to_xb(uint64_t xb_user_id) const
|
||||
ret_sc.spec = gc_sc.spec;
|
||||
}
|
||||
for (size_t z = 0; z < std::min<size_t>(ret.shortcuts.size(), this->shortcuts.size()); z++) {
|
||||
ret.shortcuts[z] = this->shortcuts[z].convert<false, TextEncoding::MARKED>(language);
|
||||
ret.shortcuts[z] = this->shortcuts[z].convert<false, TextEncoding::MARKED, 0x50>(language);
|
||||
}
|
||||
ret.auto_reply.encode(this->auto_reply.decode(language), language);
|
||||
ret.info_board.encode(this->info_board.decode(language), language);
|
||||
@@ -605,7 +638,7 @@ SaveFileSymbolChatEntryBB PSOBBCharacterFile::DefaultSymbolChatEntry::to_entry(u
|
||||
}
|
||||
|
||||
// TODO: Eliminate duplication between this function and the parallel function
|
||||
// in PlayerBank
|
||||
// in PlayerBankT
|
||||
void PSOBBCharacterFile::add_item(const ItemData& item, const ItemData::StackLimits& limits) {
|
||||
uint32_t primary_identifier = item.primary_identifier();
|
||||
|
||||
@@ -653,7 +686,7 @@ void PSOBBCharacterFile::add_item(const ItemData& item, const ItemData::StackLim
|
||||
}
|
||||
|
||||
// TODO: Eliminate code duplication between this function and the parallel
|
||||
// function in PlayerBank
|
||||
// function in PlayerBankT
|
||||
ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, const ItemData::StackLimits& limits) {
|
||||
ItemData ret;
|
||||
|
||||
|
||||
+68
-33
@@ -132,11 +132,11 @@ struct SaveFileSymbolChatEntryT {
|
||||
} __packed__;
|
||||
using SaveFileSymbolChatEntryPC = SaveFileSymbolChatEntryT<false, TextEncoding::UTF16, 0x18>;
|
||||
using SaveFileSymbolChatEntryGC = SaveFileSymbolChatEntryT<true, TextEncoding::MARKED, 0x18>;
|
||||
using SaveFileSymbolChatEntryXB = SaveFileSymbolChatEntryT<false, TextEncoding::MARKED, 0x18>;
|
||||
using SaveFileSymbolChatEntryDCXB = SaveFileSymbolChatEntryT<false, TextEncoding::MARKED, 0x18>;
|
||||
using SaveFileSymbolChatEntryBB = SaveFileSymbolChatEntryT<false, TextEncoding::UTF16, 0x14>;
|
||||
check_struct_size(SaveFileSymbolChatEntryPC, 0x70);
|
||||
check_struct_size(SaveFileSymbolChatEntryGC, 0x58);
|
||||
check_struct_size(SaveFileSymbolChatEntryXB, 0x58);
|
||||
check_struct_size(SaveFileSymbolChatEntryDCXB, 0x58);
|
||||
check_struct_size(SaveFileSymbolChatEntryBB, 0x68);
|
||||
|
||||
template <bool IsBigEndian>
|
||||
@@ -167,12 +167,12 @@ using WordSelectMessageBE = WordSelectMessageT<true>;
|
||||
check_struct_size(WordSelectMessage, 0x1C);
|
||||
check_struct_size(WordSelectMessageBE, 0x1C);
|
||||
|
||||
template <bool IsBigEndian, TextEncoding Encoding>
|
||||
template <bool IsBigEndian, TextEncoding Encoding, size_t MaxChars>
|
||||
struct SaveFileChatShortcutEntryT {
|
||||
using U32T = std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t>;
|
||||
|
||||
union Definition {
|
||||
pstring<Encoding, 0x50> text;
|
||||
pstring<Encoding, MaxChars> text;
|
||||
WordSelectMessageT<IsBigEndian> word_select;
|
||||
SymbolChatT<IsBigEndian> symbol_chat;
|
||||
|
||||
@@ -184,14 +184,14 @@ struct SaveFileChatShortcutEntryT {
|
||||
}
|
||||
} __packed__;
|
||||
|
||||
/* GC:BB */
|
||||
/* 00:00 */ U32T type; // 1 = text, 2 = word select, 3 = symbol chat
|
||||
/* 04:04 */ Definition definition;
|
||||
/* 54:A4 */
|
||||
/* DC:GC:BB */
|
||||
/* 00:00:00 */ U32T type; // 1 = text, 2 = word select, 3 = symbol chat
|
||||
/* 04:04:04 */ Definition definition;
|
||||
/* 40:54:A4 */
|
||||
|
||||
template <bool RetIsBigEndian, TextEncoding RetEncoding>
|
||||
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding> convert(uint8_t language) const {
|
||||
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding> ret;
|
||||
template <bool RetIsBigEndian, TextEncoding RetEncoding, size_t RetMaxSize>
|
||||
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding, RetMaxSize> convert(uint8_t language) const {
|
||||
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding, RetMaxSize> ret;
|
||||
ret.type = this->type.load();
|
||||
switch (ret.type) {
|
||||
case 1:
|
||||
@@ -209,13 +209,49 @@ struct SaveFileChatShortcutEntryT {
|
||||
return ret;
|
||||
}
|
||||
} __packed__;
|
||||
using SaveFileShortcutEntryGC = SaveFileChatShortcutEntryT<true, TextEncoding::MARKED>;
|
||||
using SaveFileShortcutEntryXB = SaveFileChatShortcutEntryT<false, TextEncoding::MARKED>;
|
||||
using SaveFileShortcutEntryBB = SaveFileChatShortcutEntryT<false, TextEncoding::UTF16>;
|
||||
using SaveFileShortcutEntryDC = SaveFileChatShortcutEntryT<false, TextEncoding::MARKED, 0x3C>;
|
||||
using SaveFileShortcutEntryGC = SaveFileChatShortcutEntryT<true, TextEncoding::MARKED, 0x50>;
|
||||
using SaveFileShortcutEntryXB = SaveFileChatShortcutEntryT<false, TextEncoding::MARKED, 0x50>;
|
||||
using SaveFileShortcutEntryBB = SaveFileChatShortcutEntryT<false, TextEncoding::UTF16, 0x50>;
|
||||
check_struct_size(SaveFileShortcutEntryDC, 0x40);
|
||||
check_struct_size(SaveFileShortcutEntryGC, 0x54);
|
||||
check_struct_size(SaveFileShortcutEntryXB, 0x54);
|
||||
check_struct_size(SaveFileShortcutEntryBB, 0xA4);
|
||||
|
||||
struct PSODCV2CharacterFile {
|
||||
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
|
||||
/* 0000:---- */ PlayerInventory inventory;
|
||||
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
|
||||
/* 041C:0000 */ le_uint32_t flags = 0;
|
||||
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
|
||||
/* 0424:0008 */ le_uint32_t signature = 0xA205B064;
|
||||
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
|
||||
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
|
||||
/* 0430:0014 */ le_uint32_t save_count = 0;
|
||||
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
|
||||
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
|
||||
/* 0460:0044 */ QuestFlags quest_flags;
|
||||
/* 0660:0244 */ PlayerBank60 bank;
|
||||
/* 0C08:07EC */ GuildCardDC guild_card;
|
||||
/* 0C85:0869 */ parray<uint8_t, 3> unknown_s1; // Probably actually unused
|
||||
/* 0C88:086C */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
|
||||
/* 10A8:0C8C */ parray<SaveFileShortcutEntryDC, 20> shortcuts;
|
||||
/* 15A8:118C */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
|
||||
/* 15B8:119C */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
|
||||
/* 15C8:11AC */ PlayerRecordsBattle battle_records;
|
||||
/* 15E0:11C4 */ PlayerRecordsChallengeDC challenge_records;
|
||||
/* 1680:1264 */ parray<le_uint16_t, 20> tech_menu_shortcut_entries;
|
||||
// The Choice Search config is stored here as 32-bit integers, even though
|
||||
// it's always represented with 16-bit integers in the C0 command. The order
|
||||
// of the entries here is the same (that is, the first two of these ints are
|
||||
// entries[0], the second two are entries[1], etc.).
|
||||
/* 16A8:128C */ parray<le_uint32_t, 10> choice_search_config;
|
||||
/* 16D0:12B4 */ parray<uint8_t, 4> unknown_a2;
|
||||
/* 16D4:12B8 */ pstring<TextEncoding::ASCII, 0x10> v2_serial_number;
|
||||
/* 16E4:12C8 */ pstring<TextEncoding::ASCII, 0x10> v2_access_key;
|
||||
/* 16F4:12D8 */
|
||||
} __packed_ws__(PSODCV2CharacterFile, 0x16F4);
|
||||
|
||||
struct PSOGCCharacterFile {
|
||||
/* 00000 */ be_uint32_t checksum;
|
||||
struct Character {
|
||||
@@ -257,11 +293,11 @@ struct PSOGCCharacterFile {
|
||||
// R = Map direction (0 = non-fixed; 1 = fixed)
|
||||
/* 042C:0010 */ be_uint32_t option_flags = 0x00040058;
|
||||
/* 0430:0014 */ be_uint32_t save_count = 0;
|
||||
/* 0434:0018 */ parray<uint8_t, 0x1C> unknown_a2;
|
||||
/* 0450:0034 */ parray<uint8_t, 0x10> unknown_a3;
|
||||
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
|
||||
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
|
||||
/* 0460:0044 */ QuestFlags quest_flags;
|
||||
/* 0660:0244 */ be_uint32_t death_count = 0;
|
||||
/* 0664:0248 */ PlayerBankBE bank;
|
||||
/* 0664:0248 */ PlayerBank200BE bank;
|
||||
/* 192C:1510 */ GuildCardGCBE guild_card;
|
||||
/* 19BC:15A0 */ parray<SaveFileSymbolChatEntryGC, 12> symbol_chats;
|
||||
/* 1DDC:19C0 */ parray<SaveFileShortcutEntryGC, 20> shortcuts;
|
||||
@@ -307,19 +343,17 @@ struct PSOGCEp3CharacterFile {
|
||||
// this field mean.
|
||||
/* 042C:0010 */ be_uint32_t option_flags;
|
||||
/* 0430:0014 */ be_uint32_t save_count;
|
||||
/* 0434:0018 */ parray<uint8_t, 0x1C> unknown_a2;
|
||||
/* 0450:0034 */ parray<uint8_t, 0x10> unknown_a3;
|
||||
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
|
||||
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
|
||||
// seq_vars is an array of 8192 bits, which contain all the Episode 3 quest
|
||||
// progress flags. This includes things like which maps are unlocked, which
|
||||
// NPC decks are unlocked, and whether the player has a VIP card or not.
|
||||
/* 0460:0044 */ parray<uint8_t, 0x400> seq_vars;
|
||||
/* 0860:0444 */ be_uint32_t death_count;
|
||||
// The following three fields appear to actually be a PlayerBank structure
|
||||
// with only 4 item slots instead of 200. They presumably didn't completely
|
||||
// remove the bank in Ep3 because they would have to change too much code.
|
||||
/* 0864:0448 */ be_uint32_t num_bank_items;
|
||||
/* 0868:044C */ be_uint32_t bank_meseta;
|
||||
/* 086C:0450 */ parray<PlayerBankItemBE, 4> bank_items;
|
||||
// Curiously, Episode 3 characters do have item banks, but there are only 4
|
||||
// item slots. Sega presumably didn't completely remove the bank in Ep3
|
||||
// because they would have to change too much code.
|
||||
/* 0864:0448 */ PlayerBankT<4, true> bank;
|
||||
/* 08CC:04B0 */ GuildCardGCBE guild_card;
|
||||
/* 095C:0540 */ parray<SaveFileSymbolChatEntryGC, 12> symbol_chats;
|
||||
/* 0D7C:0960 */ parray<SaveFileShortcutEntryGC, 20> chat_shortcuts;
|
||||
@@ -410,13 +444,13 @@ struct PSOXBCharacterFileCharacter {
|
||||
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
|
||||
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
|
||||
/* 0430:0014 */ le_uint32_t save_count = 0;
|
||||
/* 0434:0018 */ parray<uint8_t, 0x1C> unknown_a2;
|
||||
/* 0450:0034 */ parray<uint8_t, 0x10> unknown_a3;
|
||||
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
|
||||
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
|
||||
/* 0460:0044 */ QuestFlags quest_flags;
|
||||
/* 0660:0244 */ le_uint32_t death_count = 0;
|
||||
/* 0664:0248 */ PlayerBank bank;
|
||||
/* 0664:0248 */ PlayerBank200 bank;
|
||||
/* 192C:1510 */ GuildCardXB guild_card;
|
||||
/* 1B58:173C */ parray<SaveFileSymbolChatEntryXB, 12> symbol_chats;
|
||||
/* 1B58:173C */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
|
||||
/* 1F78:1B5C */ parray<SaveFileShortcutEntryXB, 16> shortcuts;
|
||||
/* 24B8:209C */ pstring<TextEncoding::MARKED, 0xAC> auto_reply;
|
||||
/* 2518:20FC */ pstring<TextEncoding::MARKED, 0xAC> info_board;
|
||||
@@ -759,7 +793,7 @@ struct PSOBBCharacterFile {
|
||||
/* 04F0 */ le_uint32_t save_count = 0;
|
||||
/* 04F4 */ QuestFlags quest_flags;
|
||||
/* 06F4 */ le_uint32_t death_count = 0;
|
||||
/* 06F8 */ PlayerBank bank;
|
||||
/* 06F8 */ PlayerBank200 bank;
|
||||
/* 19C0 */ GuildCardBB guild_card;
|
||||
/* 1AC8 */ le_uint32_t unknown_a3 = 0;
|
||||
/* 1ACC */ parray<SaveFileSymbolChatEntryBB, 0x0C> symbol_chats;
|
||||
@@ -795,8 +829,9 @@ struct PSOBBCharacterFile {
|
||||
uint8_t language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_gc(const PSOGCCharacterFile::Character& gc_char);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_xb(const PSOXBCharacterFileCharacter& xb_char);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_dc_v2(const PSODCV2CharacterFile& dc);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_gc(const PSOGCCharacterFile::Character& gc);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_xb(const PSOXBCharacterFileCharacter& xb);
|
||||
|
||||
void add_item(const ItemData& item, const ItemData::StackLimits& limits);
|
||||
ItemData remove_item(uint32_t item_id, uint32_t amount, const ItemData::StackLimits& limits);
|
||||
@@ -856,7 +891,7 @@ struct LegacySavedPlayerDataBB { // .nsc file format
|
||||
/* 0028 */ PlayerRecordsBattle battle_records;
|
||||
/* 0040 */ PlayerDispDataBBPreview preview;
|
||||
/* 00BC */ pstring<TextEncoding::UTF16, 0x00AC> auto_reply;
|
||||
/* 0214 */ PlayerBank bank;
|
||||
/* 0214 */ PlayerBank200 bank;
|
||||
/* 14DC */ PlayerRecordsChallengeBB challenge_records;
|
||||
/* 161C */ PlayerDispDataBB disp;
|
||||
/* 17AC */ pstring<TextEncoding::UTF16, 0x0058> guild_card_description;
|
||||
|
||||
+2
-2
@@ -2338,11 +2338,11 @@ void send_self_leave_notification(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
void send_get_player_info(shared_ptr<Client> c, bool request_extended) {
|
||||
// TODO: Support extended player info on other versions.
|
||||
// TODO: Support extended player info on Ep3 JP and NTE
|
||||
if (request_extended &&
|
||||
!c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) &&
|
||||
!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY) &&
|
||||
((c->version() == Version::GC_V3) || (c->version() == Version::XB_V3))) {
|
||||
((c->version() == Version::DC_V2) || (c->version() == Version::GC_V3) || (c->version() == Version::XB_V3))) {
|
||||
auto s = c->require_server_state();
|
||||
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c)]() {
|
||||
auto c = wc.lock();
|
||||
|
||||
Reference in New Issue
Block a user