split team membership struct from base BB system file

This commit is contained in:
Martin Michelsen
2024-09-17 21:54:56 -07:00
parent 16a8f91822
commit b13e67d491
7 changed files with 89 additions and 82 deletions
+1 -1
View File
@@ -1418,7 +1418,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
p->guild_card.language = new_language;
auto sys = c->system_file(false);
if (sys) {
sys->base.language = new_language;
sys->language = new_language;
}
} else if (tokens.at(0) == "secid" && cheats_allowed) {
uint8_t secid = section_id_for_name(tokens.at(1));
+12 -58
View File
@@ -759,32 +759,24 @@ void Client::load_all_files() {
if (this->character_data) {
player_data_log.info("Using loaded character file %s", char_filename.c_str());
} else if (phosg::isfile(char_filename)) {
auto f = phosg::fopen_unique(char_filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
if (header.command != 0x00E7) {
throw runtime_error("incorrect command in character file header");
}
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
}
static_assert(sizeof(PSOBBCharacterFile) + sizeof(PSOBBFullSystemFile) == 0x3994, ".psochar size is incorrect");
this->character_data = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
auto psochar = load_psochar(char_filename, !this->system_data);
this->character_data = psochar.character_file;
files_manager->set_character(char_filename, this->character_data);
player_data_log.info("Loaded character data from %s", char_filename.c_str());
// If there was no .psosys file, load the system file from the .psochar
// If there was no .psosys file, use the system file from the .psochar
// file instead
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>(phosg::freadx<PSOBBBaseSystemFile>(f.get()));
if (!psochar.system_file) {
throw logic_error("account system data not present, and also not loaded from psochar file");
}
this->system_data = psochar.system_file;
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info("Loaded system data from %s", char_filename.c_str());
}
this->update_character_data_after_load(this->character_data);
this->system_data->base.language = this->language();
this->system_data->language = this->language();
} else {
player_data_log.info("Character file is missing: %s", char_filename.c_str());
@@ -813,7 +805,7 @@ void Client::load_all_files() {
throw runtime_error("account data header is incorrect");
}
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>(nsa_data->system_file.base);
this->system_data = make_shared<PSOBBBaseSystemFile>(nsa_data->system_file);
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info("Loaded legacy system data from %s", nsa_filename.c_str());
}
@@ -949,23 +941,7 @@ void Client::save_character_file(
const string& filename,
shared_ptr<const PSOBBBaseSystemFile> system,
shared_ptr<const PSOBBCharacterFile> character) {
auto f = phosg::fopen_unique(filename, "wb");
PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership), 0x00E7, 0x00000000};
phosg::fwritex(f.get(), header);
phosg::fwritex(f.get(), *character);
phosg::fwritex(f.get(), *system);
// TODO: Technically, we should write the actual team membership struct to the
// file here, but that would cause Client to depend on Account, which
// it currently does not. This data doesn't matter at all for correctness
// within newserv, since it ignores this data entirely and instead generates
// the membership struct from the team ID in the Account and the team's state.
// So, writing correct data here would mostly be for compatibility with other
// PSO servers. But if the other server is newserv, then this data would be
// used anyway, and if it's not, then it would presumably have a different set
// of teams with a different set of team IDs anyway, so the membership struct
// here would be useless either way.
static const PSOBBTeamMembership empty_membership;
phosg::fwritex(f.get(), empty_membership);
save_psochar(filename, system, character);
player_data_log.info("Saved character file %s", filename.c_str());
}
@@ -1007,18 +983,7 @@ void Client::save_guild_card_file() const {
void Client::load_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index, false);
auto f = phosg::fopen_unique(filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
if (header.command != 0x00E7) {
throw runtime_error("incorrect command in character file header");
}
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
}
this->character_data = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
this->character_data = load_psochar(filename, false).character_file;
this->update_character_data_after_load(this->character_data);
this->v1_v2_last_reported_disp.reset();
}
@@ -1106,18 +1071,7 @@ void Client::use_character_bank(int8_t index) {
this->external_bank_character_index = index;
player_data_log.info("Using loaded character file %s for external bank", filename.c_str());
} else if (phosg::isfile(filename)) {
auto f = phosg::fopen_unique(filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
if (header.command != 0x00E7) {
throw runtime_error("incorrect command in character file header");
}
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
}
this->external_bank_character = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
this->external_bank_character = load_psochar(filename, false).character_file;
this->update_character_data_after_load(this->external_bank_character);
this->external_bank_character_index = index;
files_manager->set_character(filename, this->external_bank_character);
+7 -2
View File
@@ -3017,7 +3017,11 @@ struct S_TournamentEntryList_Ep3_E2 {
} __packed_ws__(S_TournamentEntryList_Ep3_E2, 0x584);
// E2 (S->C): Set system file contents (BB)
// See PSOBBFullSystemFile in SaveFileFormats.hh for format
struct S_SyncSystemFile_BB_E2 {
PSOBBBaseSystemFile system_file;
PSOBBTeamMembership team_membership;
} __packed_ws__(S_SyncSystemFile_BB_E2, 0xAF0);
// E3 (S->C): Game or tournament info (Episode 3)
// The header.flag argument determines which fields are valid (and which panes
@@ -3201,7 +3205,8 @@ struct C_CreateSpectatorTeam_Ep3_E7 {
struct SC_SyncSaveFiles_BB_E7 {
/* 0000 */ PSOBBCharacterFile char_file;
/* 2EA4 */ PSOBBFullSystemFile system_file;
/* 2EA4 */ PSOBBBaseSystemFile system_file;
/* 30DC */ PSOBBTeamMembership team_membership;
/* 3994 */
} __packed_ws__(SC_SyncSaveFiles_BB_E7, 0x3994);
+3 -4
View File
@@ -3798,13 +3798,12 @@ static void on_E7_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
p->challenge_records = cmd.char_file.challenge_records;
p->battle_records = cmd.char_file.battle_records;
p->death_count = cmd.char_file.death_count;
*c->system_file() = cmd.system_file.base;
*c->system_file() = cmd.system_file;
}
static void on_E2_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
auto& cmd = check_size_t<PSOBBFullSystemFile>(data);
auto sys = c->system_file();
*sys = cmd.base;
const auto& cmd = check_size_t<S_SyncSystemFile_BB_E2>(data);
*c->system_file() = cmd.system_file;
c->save_system_file();
S_SystemFileCreated_00E1_BB out_cmd = {1};
+46 -1
View File
@@ -327,7 +327,7 @@ uint32_t PSOBBGuildCardFile::checksum() const {
PSOBBBaseSystemFile::PSOBBBaseSystemFile() {
// This field is based on 1/1/2000, not 1/1/1970, so adjust appropriately
this->base.creation_timestamp = (phosg::now() - 946684800000000ULL) / 1000000;
this->creation_timestamp = (phosg::now() - 946684800000000ULL) / 1000000;
for (size_t z = 0; z < DEFAULT_KEY_CONFIG.size(); z++) {
this->key_config[z] = DEFAULT_KEY_CONFIG[z];
}
@@ -742,6 +742,51 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_xb(const PSOXBCha
return ret;
}
LoadedPSOCHARFile load_psochar(const string& filename, bool load_system) {
auto f = phosg::fopen_unique(filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
if (header.command != 0x00E7) {
throw runtime_error("incorrect command in character file header");
}
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
}
static_assert(sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership) == 0x3994, ".psochar size is incorrect");
LoadedPSOCHARFile ret;
ret.character_file = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
if (load_system) {
ret.system_file = make_shared<PSOBBBaseSystemFile>(phosg::freadx<PSOBBBaseSystemFile>(f.get()));
}
return ret;
}
void save_psochar(
const std::string& filename,
std::shared_ptr<const PSOBBBaseSystemFile> system,
std::shared_ptr<const PSOBBCharacterFile> character) {
auto f = phosg::fopen_unique(filename, "wb");
PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership), 0x00E7, 0x00000000};
phosg::fwritex(f.get(), header);
phosg::fwritex(f.get(), *character);
phosg::fwritex(f.get(), *system);
// TODO: Technically, we should write the actual team membership struct to
// the file here, but that would cause Client to depend on Account, which it
// currently does not. This data doesn't matter at all for correctness within
// newserv, since it ignores this data entirely and instead generates the
// membership struct from the team ID in the Account and the team's state.
// So, writing correct data here would mostly be for compatibility with other
// PSO servers. But if the other server is newserv, then this data wouldn't
// be used anyway, and if it's not, then it would presumably have a different
// set of teams with a different set of team IDs anyway, so the membership
// struct here would be useless either way.
static const PSOBBTeamMembership empty_membership;
phosg::fwritex(f.get(), empty_membership);
}
PSODCV2CharacterFile PSOBBCharacterFile::to_dc_v2() const {
uint8_t language = this->inventory.language;
+15 -11
View File
@@ -279,8 +279,7 @@ struct PSOBBMinimalSystemFile {
/* 0114 */
} __packed_ws__(PSOBBMinimalSystemFile, 0x114);
struct PSOBBBaseSystemFile {
/* 0000 */ PSOBBMinimalSystemFile base;
struct PSOBBBaseSystemFile : PSOBBMinimalSystemFile {
/* 0114 */ parray<uint8_t, 0x016C> key_config;
/* 0280 */ parray<uint8_t, 0x0038> joystick_config;
/* 02B8 */
@@ -288,14 +287,6 @@ struct PSOBBBaseSystemFile {
PSOBBBaseSystemFile();
} __packed_ws__(PSOBBBaseSystemFile, 0x2B8);
struct PSOBBFullSystemFile {
/* 0000 */ PSOBBBaseSystemFile base;
/* 02B8 */ PSOBBTeamMembership team_membership;
/* 0AF0 */
PSOBBFullSystemFile() = default;
} __packed_ws__(PSOBBFullSystemFile, 0xAF0);
////////////////////////////////////////////////////////////////////////////////
// Character files
@@ -757,6 +748,18 @@ struct PSOBBCharacterFile {
void recompute_stats(std::shared_ptr<const LevelTable> level_table);
} __packed_ws__(PSOBBCharacterFile, 0x2EA4);
struct LoadedPSOCHARFile {
std::shared_ptr<PSOBBBaseSystemFile> system_file; // Null if load_system is false
std::shared_ptr<PSOBBCharacterFile> character_file; // Never null
// Team membership is present in the file, but ignored by newserv
};
LoadedPSOCHARFile load_psochar(const std::string& filename, bool load_system);
void save_psochar(
const std::string& filename,
std::shared_ptr<const PSOBBBaseSystemFile> system,
std::shared_ptr<const PSOBBCharacterFile> character);
////////////////////////////////////////////////////////////////////////////////
// Guild Card files
@@ -864,7 +867,8 @@ struct LegacySavedAccountDataBB { // .nsa file format
/* 0000 */ pstring<TextEncoding::ASCII, 0x40> signature;
/* 0040 */ parray<le_uint32_t, 0x001E> blocked_senders;
/* 00B8 */ PSOBBGuildCardFile guild_card_file;
/* D648 */ PSOBBFullSystemFile system_file;
/* D648 */ PSOBBBaseSystemFile system_file;
/* D880 */ PSOBBTeamMembership team_membership;
/* E138 */ le_uint32_t unused = 0;
/* E13C */ le_uint32_t option_flags = 0x00040058;
/* E140 */ parray<SaveFileShortcutEntryBB, 0x10> shortcuts;
+5 -5
View File
@@ -619,8 +619,8 @@ void send_client_init_bb(shared_ptr<Client> c, uint32_t error_code) {
void send_system_file_bb(shared_ptr<Client> c) {
auto team = c->team();
PSOBBFullSystemFile cmd;
cmd.base = *c->system_file();
S_SyncSystemFile_BB_E2 cmd;
cmd.system_file = *c->system_file();
if (team) {
cmd.team_membership = team->membership_for_member(c->login->account->account_id);
}
@@ -754,14 +754,14 @@ void send_complete_player_bb(shared_ptr<Client> c) {
if (c->config.check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB)) {
p->inventory.language = 1;
p->guild_card.language = 1;
sys->base.language = 1;
sys->language = 1;
}
SC_SyncSaveFiles_BB_E7 cmd;
cmd.char_file = *p;
cmd.system_file.base = *sys;
cmd.system_file = *sys;
if (team) {
cmd.system_file.team_membership = team->membership_for_member(c->login->account->account_id);
cmd.team_membership = team->membership_for_member(c->login->account->account_id);
}
send_command_t(c, 0x00E7, 0x00000000, cmd);
}