fix bb guild card save; implement comments
This commit is contained in:
@@ -13,7 +13,6 @@ Feel free to submit GitHub issues if you find bugs or have feature requests. I'd
|
|||||||
This project is primarily for my own nostalgia; I offer no guarantees on how or when this project will advance.
|
This project is primarily for my own nostalgia; I offer no guarantees on how or when this project will advance.
|
||||||
|
|
||||||
Current known issues / missing features:
|
Current known issues / missing features:
|
||||||
- Guild card file modifications don't save on BB.
|
|
||||||
- Episode 3 battles aren't implemented.
|
- Episode 3 battles aren't implemented.
|
||||||
- PSOBB is not well-tested and likely will disconnect or misbehave when clients try to use unimplemented features. GC is known to be stable and mostly complete; PC is not well-tested but is likely stable and complete as well.
|
- PSOBB is not well-tested and likely will disconnect or misbehave when clients try to use unimplemented features. GC is known to be stable and mostly complete; PC is not well-tested but is likely stable and complete as well.
|
||||||
- Patches currently are platform-specific but not version-specific. This makes them quite a bit harder to write and use properly.
|
- Patches currently are platform-specific but not version-specific. This makes them quite a bit harder to write and use properly.
|
||||||
|
|||||||
@@ -2607,8 +2607,8 @@ struct G_SendGuildCard_PC_V3 {
|
|||||||
ptext<CharT, 0x18> name;
|
ptext<CharT, 0x18> name;
|
||||||
ptext<CharT, 0x48> description;
|
ptext<CharT, 0x48> description;
|
||||||
parray<uint8_t, 0x24> unused2;
|
parray<uint8_t, 0x24> unused2;
|
||||||
uint8_t reserved1;
|
uint8_t present;
|
||||||
uint8_t reserved2;
|
uint8_t present2;
|
||||||
uint8_t section_id;
|
uint8_t section_id;
|
||||||
uint8_t char_class;
|
uint8_t char_class;
|
||||||
};
|
};
|
||||||
@@ -2623,8 +2623,8 @@ struct G_SendGuildCard_BB_6x06 {
|
|||||||
ptext<char16_t, 0x18> name;
|
ptext<char16_t, 0x18> name;
|
||||||
ptext<char16_t, 0x10> team_name;
|
ptext<char16_t, 0x10> team_name;
|
||||||
ptext<char16_t, 0x58> description;
|
ptext<char16_t, 0x58> description;
|
||||||
uint8_t reserved1;
|
uint8_t present;
|
||||||
uint8_t reserved2;
|
uint8_t present2;
|
||||||
uint8_t section_id;
|
uint8_t section_id;
|
||||||
uint8_t char_class;
|
uint8_t char_class;
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-7
@@ -269,16 +269,16 @@ PlayerDispDataBBPreview::PlayerDispDataBBPreview() noexcept
|
|||||||
|
|
||||||
GuildCardV3::GuildCardV3() noexcept
|
GuildCardV3::GuildCardV3() noexcept
|
||||||
: player_tag(0),
|
: player_tag(0),
|
||||||
serial_number(0),
|
guild_card_number(0),
|
||||||
reserved1(1),
|
present(0),
|
||||||
reserved2(1),
|
present2(0),
|
||||||
section_id(0),
|
section_id(0),
|
||||||
char_class(0) { }
|
char_class(0) { }
|
||||||
|
|
||||||
GuildCardBB::GuildCardBB() noexcept
|
GuildCardBB::GuildCardBB() noexcept
|
||||||
: guild_card_number(0),
|
: guild_card_number(0),
|
||||||
reserved1(1),
|
present(0),
|
||||||
reserved2(1),
|
present2(0),
|
||||||
section_id(0),
|
section_id(0),
|
||||||
char_class(0) { }
|
char_class(0) { }
|
||||||
|
|
||||||
@@ -287,8 +287,8 @@ void GuildCardBB::clear() {
|
|||||||
this->name.clear();
|
this->name.clear();
|
||||||
this->team_name.clear();
|
this->team_name.clear();
|
||||||
this->description.clear();
|
this->description.clear();
|
||||||
this->reserved1 = 1;
|
this->present = 0;
|
||||||
this->reserved2 = 1;
|
this->present2 = 0;
|
||||||
this->section_id = 0;
|
this->section_id = 0;
|
||||||
this->char_class = 0;
|
this->char_class = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-14
@@ -211,11 +211,11 @@ struct PlayerDispDataBB {
|
|||||||
// GC format)
|
// GC format)
|
||||||
struct GuildCardV3 {
|
struct GuildCardV3 {
|
||||||
le_uint32_t player_tag;
|
le_uint32_t player_tag;
|
||||||
le_uint32_t serial_number;
|
le_uint32_t guild_card_number;
|
||||||
ptext<char, 0x18> name;
|
ptext<char, 0x18> name;
|
||||||
ptext<char, 0x6C> description;
|
ptext<char, 0x6C> description;
|
||||||
uint8_t reserved1; // should be 1
|
uint8_t present; // should be 1
|
||||||
uint8_t reserved2; // should be 1
|
uint8_t present2; // should be 1
|
||||||
uint8_t section_id;
|
uint8_t section_id;
|
||||||
uint8_t char_class;
|
uint8_t char_class;
|
||||||
|
|
||||||
@@ -228,8 +228,8 @@ struct GuildCardBB {
|
|||||||
ptext<char16_t, 0x18> name;
|
ptext<char16_t, 0x18> name;
|
||||||
ptext<char16_t, 0x10> team_name;
|
ptext<char16_t, 0x10> team_name;
|
||||||
ptext<char16_t, 0x58> description;
|
ptext<char16_t, 0x58> description;
|
||||||
uint8_t reserved1; // should be 1
|
uint8_t present; // should be 1 if guild card entry exists
|
||||||
uint8_t reserved2; // should be 1
|
uint8_t present2; // should be 1 if guild card entry exists
|
||||||
uint8_t section_id;
|
uint8_t section_id;
|
||||||
uint8_t char_class;
|
uint8_t char_class;
|
||||||
|
|
||||||
@@ -240,21 +240,16 @@ struct GuildCardBB {
|
|||||||
// an entry in the BB guild card file
|
// an entry in the BB guild card file
|
||||||
struct GuildCardEntryBB {
|
struct GuildCardEntryBB {
|
||||||
GuildCardBB data;
|
GuildCardBB data;
|
||||||
// TODO: Almost all of this space (0x58 char16_ts) is probably the comment,
|
ptext<char16_t, 0x58> comment;
|
||||||
// but is the unknown 4 bytes at the beginning or end of this space?
|
parray<uint8_t, 0x4> unknown_a1;
|
||||||
parray<uint8_t, 0xB4> unknown_a1;
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
// the format of the BB guild card file
|
// the format of the BB guild card file
|
||||||
struct GuildCardFileBB {
|
struct GuildCardFileBB {
|
||||||
parray<uint8_t, 6> unknown_a1;
|
parray<uint8_t, 0x1F74> unknown_a3;
|
||||||
uint8_t num_guild_cards; // TODO: This is a guess. Verify this.
|
GuildCardEntryBB entries[0x0069]; // that's 105 of them in decimal
|
||||||
uint8_t unknown_a2;
|
|
||||||
parray<uint8_t, 0x1F7C> unknown_a3;
|
|
||||||
GuildCardEntryBB entries[0x0068]; // that's 104 of them in decimal
|
|
||||||
parray<uint8_t, 0x01AC> unknown_a4;
|
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct KeyAndTeamConfigBB {
|
struct KeyAndTeamConfigBB {
|
||||||
|
|||||||
+27
-27
@@ -1583,6 +1583,7 @@ void process_player_preview_request_bb(shared_ptr<ServerState>, shared_ptr<Clien
|
|||||||
|
|
||||||
void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t command, uint32_t, const string& data) {
|
uint16_t command, uint32_t, const string& data) {
|
||||||
|
constexpr size_t max_count = sizeof(GuildCardFileBB) / sizeof(GuildCardEntryBB);
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 0x01E8: { // Check guild card file checksum
|
case 0x01E8: { // Check guild card file checksum
|
||||||
check_size_v(data.size(), sizeof(C_GuildCardChecksum_01E8));
|
check_size_v(data.size(), sizeof(C_GuildCardChecksum_01E8));
|
||||||
@@ -1600,12 +1601,14 @@ void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
case 0x04E8: { // Add guild card
|
case 0x04E8: { // Add guild card
|
||||||
auto& new_gc = check_size_t<GuildCardBB>(data);
|
auto& new_gc = check_size_t<GuildCardBB>(data);
|
||||||
auto& gcf = c->game_data.account()->guild_cards;
|
auto& gcf = c->game_data.account()->guild_cards;
|
||||||
if (gcf.num_guild_cards < sizeof(gcf.entries) / sizeof(gcf.entries[0])) {
|
for (size_t z = 0; z < max_count; z++) {
|
||||||
gcf.entries[gcf.num_guild_cards].data = new_gc;
|
if (!gcf.entries[z].data.present) {
|
||||||
gcf.entries[gcf.num_guild_cards].unknown_a1.clear();
|
gcf.entries[z].data = new_gc;
|
||||||
c->log.info("Added guild card %" PRIu32 " at position %hhu",
|
gcf.entries[z].unknown_a1.clear();
|
||||||
new_gc.guild_card_number.load(), gcf.num_guild_cards);
|
c->log.info("Added guild card %" PRIu32 " at position %zu",
|
||||||
gcf.num_guild_cards++;
|
new_gc.guild_card_number.load(), z);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1613,25 +1616,25 @@ void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
auto& cmd = check_size_t<C_DeleteGuildCard_BB_05E8_08E8>(data);
|
auto& cmd = check_size_t<C_DeleteGuildCard_BB_05E8_08E8>(data);
|
||||||
auto& gcf = c->game_data.account()->guild_cards;
|
auto& gcf = c->game_data.account()->guild_cards;
|
||||||
size_t z;
|
size_t z;
|
||||||
for (z = 0; z < gcf.num_guild_cards; z++) {
|
for (z = 0; z < max_count; z++) {
|
||||||
if (gcf.entries[z].data.guild_card_number == cmd.guild_card_number) {
|
if (gcf.entries[z].data.guild_card_number == cmd.guild_card_number) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (z < gcf.num_guild_cards) {
|
if (z < max_count) {
|
||||||
c->log.info("Deleted guild card %" PRIu32 " at position %zu",
|
c->log.info("Deleted guild card %" PRIu32 " at position %zu",
|
||||||
cmd.guild_card_number.load(), z);
|
cmd.guild_card_number.load(), z);
|
||||||
gcf.num_guild_cards--;
|
for (z = 0; z < max_count - 1; z++) {
|
||||||
for (z = 0; z < gcf.num_guild_cards; z++) {
|
|
||||||
gcf.entries[z] = gcf.entries[z + 1];
|
gcf.entries[z] = gcf.entries[z + 1];
|
||||||
}
|
}
|
||||||
|
gcf.entries[max_count - 1].clear();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x06E8: { // Update guild card
|
case 0x06E8: { // Update guild card
|
||||||
auto& new_gc = check_size_t<GuildCardBB>(data);
|
auto& new_gc = check_size_t<GuildCardBB>(data);
|
||||||
auto& gcf = c->game_data.account()->guild_cards;
|
auto& gcf = c->game_data.account()->guild_cards;
|
||||||
for (size_t z = 0; z < gcf.num_guild_cards; z++) {
|
for (size_t z = 0; z < max_count; z++) {
|
||||||
if (gcf.entries[z].data.guild_card_number == new_gc.guild_card_number) {
|
if (gcf.entries[z].data.guild_card_number == new_gc.guild_card_number) {
|
||||||
gcf.entries[z].data = new_gc;
|
gcf.entries[z].data = new_gc;
|
||||||
c->log.info("Updated guild card %" PRIu32 " at position %zu",
|
c->log.info("Updated guild card %" PRIu32 " at position %zu",
|
||||||
@@ -1653,34 +1656,31 @@ void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x09E8: { // Write comment
|
case 0x09E8: { // Write comment
|
||||||
// TODO: We need to know the comment storage format in the guild card file
|
auto& cmd = check_size_t<C_WriteGuildCardComment_BB_09E8>(data);
|
||||||
// in order to implement this (see comment in Player.hh). The
|
auto& gcf = c->game_data.account()->guild_cards;
|
||||||
// implementation should go very much like this:
|
for (size_t z = 0; z < max_count; z++) {
|
||||||
// auto& cmd = check_size_t<C_WriteGuildCardComment_BB_09E8>(data);
|
if (gcf.entries[z].data.guild_card_number == cmd.guild_card_number) {
|
||||||
// auto& gcf = c->game_data.account()->guild_cards;
|
gcf.entries[z].comment = cmd.comment;
|
||||||
// for (size_t z = 0; z < gcf.num_guild_cards; z++) {
|
c->log.info("Updated comment on guild card %" PRIu32 " at position %zu",
|
||||||
// if (gcf.entries[z].data.guild_card_number == cmd.guild_card_number) {
|
cmd.guild_card_number.load(), z);
|
||||||
// gcf.entries[z].comment = cmd.comment;
|
break;
|
||||||
// c->log.info("Updated comment on guild card %" PRIu32 " at position %zu",
|
}
|
||||||
// cmd.guild_card_number.load(), z);
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
throw runtime_error("guild card comments are not yet implemented on BB");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x0AE8: { // Move guild card in list
|
case 0x0AE8: { // Move guild card in list
|
||||||
auto& cmd = check_size_t<C_MoveGuildCard_BB_0AE8>(data);
|
auto& cmd = check_size_t<C_MoveGuildCard_BB_0AE8>(data);
|
||||||
auto& gcf = c->game_data.account()->guild_cards;
|
auto& gcf = c->game_data.account()->guild_cards;
|
||||||
if (cmd.position >= gcf.num_guild_cards) {
|
if (cmd.position >= max_count) {
|
||||||
throw invalid_argument("invalid new position");
|
throw invalid_argument("invalid new position");
|
||||||
}
|
}
|
||||||
size_t index;
|
size_t index;
|
||||||
for (index = 0; index < gcf.num_guild_cards; index++) {
|
for (index = 0; index < max_count; index++) {
|
||||||
if (gcf.entries[index].data.guild_card_number == cmd.guild_card_number) {
|
if (gcf.entries[index].data.guild_card_number == cmd.guild_card_number) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index >= gcf.num_guild_cards) {
|
if (index >= max_count) {
|
||||||
throw invalid_argument("player does not have requested guild card");
|
throw invalid_argument("player does not have requested guild card");
|
||||||
}
|
}
|
||||||
auto moved_gc = gcf.entries[index];
|
auto moved_gc = gcf.entries[index];
|
||||||
|
|||||||
+4
-4
@@ -706,8 +706,8 @@ void send_guild_card_pc_v3_t(shared_ptr<Client> c, shared_ptr<Client> source) {
|
|||||||
cmd.name = source->game_data.player()->disp.name;
|
cmd.name = source->game_data.player()->disp.name;
|
||||||
remove_language_marker_inplace(cmd.name);
|
remove_language_marker_inplace(cmd.name);
|
||||||
cmd.description = source->game_data.player()->guild_card_description;
|
cmd.description = source->game_data.player()->guild_card_description;
|
||||||
cmd.reserved1 = 1;
|
cmd.present = 1;
|
||||||
cmd.reserved2 = 1;
|
cmd.present2 = 1;
|
||||||
cmd.section_id = source->game_data.player()->disp.section_id;
|
cmd.section_id = source->game_data.player()->disp.section_id;
|
||||||
cmd.char_class = source->game_data.player()->disp.char_class;
|
cmd.char_class = source->game_data.player()->disp.char_class;
|
||||||
send_command_t(c, 0x62, c->lobby_client_id, cmd);
|
send_command_t(c, 0x62, c->lobby_client_id, cmd);
|
||||||
@@ -722,8 +722,8 @@ void send_guild_card_bb(shared_ptr<Client> c, shared_ptr<Client> source) {
|
|||||||
cmd.name = remove_language_marker(source->game_data.player()->disp.name);
|
cmd.name = remove_language_marker(source->game_data.player()->disp.name);
|
||||||
cmd.team_name = remove_language_marker(source->game_data.account()->team_name);
|
cmd.team_name = remove_language_marker(source->game_data.account()->team_name);
|
||||||
cmd.description = source->game_data.player()->guild_card_description;
|
cmd.description = source->game_data.player()->guild_card_description;
|
||||||
cmd.reserved1 = 1;
|
cmd.present = 1;
|
||||||
cmd.reserved2 = 1;
|
cmd.present2 = 1;
|
||||||
cmd.section_id = source->game_data.player()->disp.section_id;
|
cmd.section_id = source->game_data.player()->disp.section_id;
|
||||||
cmd.char_class = source->game_data.player()->disp.char_class;
|
cmd.char_class = source->game_data.player()->disp.char_class;
|
||||||
send_command_t(c, 0x62, c->lobby_client_id, cmd);
|
send_command_t(c, 0x62, c->lobby_client_id, cmd);
|
||||||
|
|||||||
Reference in New Issue
Block a user