use safe packed string types

This commit is contained in:
Martin Michelsen
2022-03-31 23:23:02 -07:00
parent 832135a505
commit 8a9e1a2049
22 changed files with 1040 additions and 879 deletions
+9 -7
View File
@@ -402,7 +402,8 @@ static void command_lobby_info(shared_ptr<ServerState>, shared_ptr<Lobby> l,
level_string = string_printf("Levels: %d-%d", l->min_level + 1, l->max_level + 1); level_string = string_printf("Levels: %d-%d", l->min_level + 1, l->max_level + 1);
} }
send_text_message_printf(c, "$C6Game ID: %08X\n%s\nSection ID: %s\nCheat mode: %s", send_text_message_printf(c,
"$C6Game ID: %08X\n%s\nSection ID: %s\nCheat mode: %s",
l->lobby_id, level_string.c_str(), l->lobby_id, level_string.c_str(),
name_for_section_id(l->section_id).c_str(), name_for_section_id(l->section_id).c_str(),
(l->flags & LobbyFlag::CHEATS_ENABLED) ? "on" : "off"); (l->flags & LobbyFlag::CHEATS_ENABLED) ? "on" : "off");
@@ -547,7 +548,7 @@ static void command_password(shared_ptr<ServerState>, shared_ptr<Lobby> l,
send_text_message(l, u"$C6Game unlocked"); send_text_message(l, u"$C6Game unlocked");
} else { } else {
strncpy_t(l->password, args, countof(l->password)); l->password = args;
auto encoded = encode_sjis(l->password); auto encoded = encode_sjis(l->password);
send_text_message_printf(l, "$C6Game password:\n%s", send_text_message_printf(l, "$C6Game password:\n%s",
encoded.c_str()); encoded.c_str());
@@ -619,7 +620,9 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
} else if (tokens[0] == "level") { } else if (tokens[0] == "level") {
c->player.disp.level = stoul(tokens[1]) - 1; c->player.disp.level = stoul(tokens[1]) - 1;
} else if (tokens[0] == "namecolor") { } else if (tokens[0] == "namecolor") {
sscanf(tokens[1].c_str(), "%8X", &c->player.disp.name_color); uint32_t new_color;
sscanf(tokens[1].c_str(), "%8X", &new_color);
c->player.disp.name_color = new_color;
} else if (tokens[0] == "secid") { } else if (tokens[0] == "secid") {
uint8_t secid = section_id_for_name(decode_sjis(tokens[1])); uint8_t secid = section_id_for_name(decode_sjis(tokens[1]));
if (secid == 0xFF) { if (secid == 0xFF) {
@@ -629,8 +632,7 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
c->player.disp.section_id = secid; c->player.disp.section_id = secid;
} }
} else if (tokens[0] == "name") { } else if (tokens[0] == "name") {
decode_sjis(c->player.disp.name, tokens[1].c_str(), 0x10); c->player.disp.name = add_language_marker(tokens[1], 'J');
add_language_marker_inplace(c->player.disp.name, u'J', 0x10);
} else if (tokens[0] == "npc") { } else if (tokens[0] == "npc") {
if (tokens[1] == "none") { if (tokens[1] == "none") {
c->player.disp.extra_model = 0; c->player.disp.extra_model = 0;
@@ -648,7 +650,7 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
uint8_t level = stoul(tokens[2]) - 1; uint8_t level = stoul(tokens[2]) - 1;
if (tokens[1] == "all") { if (tokens[1] == "all") {
for (size_t x = 0; x < 0x14; x++) { for (size_t x = 0; x < 0x14; x++) {
c->player.disp.technique_levels[x] = level; c->player.disp.technique_levels.data()[x] = level;
} }
} else { } else {
uint8_t tech_id = technique_for_name(decode_sjis(tokens[1])); uint8_t tech_id = technique_for_name(decode_sjis(tokens[1]));
@@ -656,7 +658,7 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
send_text_message(c, u"$C6No such technique."); send_text_message(c, u"$C6No such technique.");
return; return;
} }
c->player.disp.technique_levels[tech_id] = level; c->player.disp.technique_levels.data()[tech_id] = level;
} }
} else { } else {
send_text_message(c, u"$C6Unknown field."); send_text_message(c, u"$C6Unknown field.");
+2 -2
View File
@@ -63,8 +63,8 @@ ClientConfig Client::export_config() const {
cc.flags = this->flags; cc.flags = this->flags;
cc.proxy_destination_address = this->proxy_destination_address; cc.proxy_destination_address = this->proxy_destination_address;
cc.proxy_destination_port = this->proxy_destination_port; cc.proxy_destination_port = this->proxy_destination_port;
memset(cc.unused, 0xFF, sizeof(cc.unused)); cc.unused.clear(0xFF);
memset(cc.unused_bb_only, 0xFF, sizeof(cc.unused_bb_only)); cc.unused_bb_only.clear(0xFF);
return cc; return cc;
} }
+4 -2
View File
@@ -8,6 +8,8 @@
#include "Player.hh" #include "Player.hh"
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "Text.hh"
extern const uint64_t CLIENT_CONFIG_MAGIC; extern const uint64_t CLIENT_CONFIG_MAGIC;
@@ -30,8 +32,8 @@ struct ClientConfig {
uint16_t flags; uint16_t flags;
uint32_t proxy_destination_address; uint32_t proxy_destination_address;
uint16_t proxy_destination_port; uint16_t proxy_destination_port;
uint8_t unused[0x0E]; parray<uint8_t, 0x0E> unused;
uint8_t unused_bb_only[0x08]; parray<uint8_t, 0x08> unused_bb_only;
} __attribute__((packed)); } __attribute__((packed));
struct Client { struct Client {
+88 -86
View File
@@ -33,14 +33,14 @@ struct SC_TextHeader_01_06_11 {
// Client will respond with an (encrypted) 9A, 9D, or 9E command // Client will respond with an (encrypted) 9A, 9D, or 9E command
struct S_ServerInit_DC_GC_02_17 { struct S_ServerInit_DC_GC_02_17 {
char copyright[0x40]; ptext<char, 0x40> copyright;
le_uint32_t server_key; // Key for data sent by server le_uint32_t server_key; // Key for data sent by server
le_uint32_t client_key; // Key for data sent by client le_uint32_t client_key; // Key for data sent by client
char after_message[200]; ptext<char, 0xBC> after_message;
}; };
struct S_ServerInit_Patch_02 { struct S_ServerInit_Patch_02 {
char copyright[0x40]; ptext<char, 0x40> copyright;
le_uint32_t server_key; le_uint32_t server_key;
le_uint32_t client_key; le_uint32_t client_key;
// BB rejects the command if it's not exactly this size, so we can't add the // BB rejects the command if it's not exactly this size, so we can't add the
@@ -51,10 +51,10 @@ struct S_ServerInit_Patch_02 {
// Client will respond with a 93 command // Client will respond with a 93 command
struct S_ServerInit_BB_03 { struct S_ServerInit_BB_03 {
char copyright[0x60]; ptext<char, 0x60> copyright;
uint8_t server_key[0x30]; parray<uint8_t, 0x30> server_key;
uint8_t client_key[0x30]; parray<uint8_t, 0x30> client_key;
char after_message[200]; ptext<char, 0xBC> after_message;
}; };
// 04 (S->C): Set guild card number and update client config ("security data") // 04 (S->C): Set guild card number and update client config ("security data")
@@ -74,9 +74,9 @@ struct S_UpdateClientConfig_DC_PC_GC_04 {
// 04 (C->S): Log in (patch server) // 04 (C->S): Log in (patch server)
struct C_Login_Patch_04 { struct C_Login_Patch_04 {
le_uint32_t unused[3]; parray<le_uint32_t, 3> unused;
char username[0x10]; ptext<char, 0x10> username;
char password[0x10]; ptext<char, 0x10> password;
}; };
// 05: Disconnect // 05: Disconnect
@@ -89,7 +89,7 @@ struct C_Login_Patch_04 {
// The guild_card_number field is used only in server->client commands // The guild_card_number field is used only in server->client commands
struct C_Chat_06 { struct C_Chat_06 {
le_uint32_t unused[2]; uint64_t unused;
union { union {
char dcgc[0]; char dcgc[0];
char16_t pcbb[0]; char16_t pcbb[0];
@@ -107,7 +107,7 @@ struct S_MenuEntry {
le_uint32_t menu_id; le_uint32_t menu_id;
le_uint32_t item_id; le_uint32_t item_id;
le_uint16_t flags; // should be 0x0F04 le_uint16_t flags; // should be 0x0F04
CharT text[EntryLength]; ptext<CharT, EntryLength> text;
}; };
struct S_MenuEntry_PC_BB_07 : S_MenuEntry<char16_t, 17> { }; struct S_MenuEntry_PC_BB_07 : S_MenuEntry<char16_t, 17> { };
struct S_MenuEntry_DC_GC_07 : S_MenuEntry<char, 18> { }; struct S_MenuEntry_DC_GC_07 : S_MenuEntry<char, 18> { };
@@ -126,7 +126,7 @@ struct S_GameMenuEntry {
le_uint32_t game_id; le_uint32_t game_id;
uint8_t difficulty_tag; // 0x0A = Ep3; else difficulty + 0x22 (so 0x25 = Ult) uint8_t difficulty_tag; // 0x0A = Ep3; else difficulty + 0x22 (so 0x25 = Ult)
uint8_t num_players; uint8_t num_players;
CharT name[0x10]; ptext<CharT, 0x10> name;
uint8_t episode; // 40 = Ep1, 41 = Ep2, 43 = Ep4. Ignored on Ep3 uint8_t episode; // 40 = Ep1, 41 = Ep2, 43 = Ep4. Ignored on Ep3
uint8_t flags; // 02 = locked, 04 = disabled (BB), 10 = battle, 20 = challenge uint8_t flags; // 02 = locked, 04 = disabled (BB), 10 = battle, 20 = challenge
}; };
@@ -136,7 +136,7 @@ struct S_GameMenuEntry_GC_08 : S_GameMenuEntry<char> { };
// 09 (S->C): Check directory (patch server) // 09 (S->C): Check directory (patch server)
struct S_CheckDirectory_Patch_09 { struct S_CheckDirectory_Patch_09 {
char name[0x40]; ptext<char, 0x40> name;
}; };
// 09 (C->S): Menu item info request // 09 (C->S): Menu item info request
@@ -189,7 +189,7 @@ struct C_MenuSelection {
// Header flag = file chunk index // Header flag = file chunk index
struct S_WriteFile_13_A7 { struct S_WriteFile_13_A7 {
char filename[0x10]; ptext<char, 0x10> filename;
uint8_t data[0x400]; uint8_t data[0x400];
le_uint32_t data_size; le_uint32_t data_size;
}; };
@@ -220,13 +220,13 @@ struct S_Reconnect_19 {
struct S_ReconnectSplit_19 { struct S_ReconnectSplit_19 {
be_uint32_t pc_address; be_uint32_t pc_address;
le_uint16_t pc_port; le_uint16_t pc_port;
uint8_t unused1[0x0F]; parray<uint8_t, 0x0F> unused1;
uint8_t gc_command; uint8_t gc_command;
uint8_t gc_flag; uint8_t gc_flag;
le_uint16_t gc_size; le_uint16_t gc_size;
be_uint32_t gc_address; be_uint32_t gc_address;
le_uint16_t gc_port; le_uint16_t gc_port;
uint8_t unused2[0xB0 - 0x23]; parray<uint8_t, 0xB0 - 0x23> unused2;
}; };
// 1A: Large message box // 1A: Large message box
@@ -256,7 +256,7 @@ struct S_ReconnectSplit_19 {
// command had no payload). The latest version uses this 16-byte challenge. // command had no payload). The latest version uses this 16-byte challenge.
struct SC_GameCardCheck_BB_22 { struct SC_GameCardCheck_BB_22 {
uint8_t data[0x10]; parray<uint8_t, 0x10> data;
}; };
// 23: Invalid command // 23: Invalid command
@@ -304,11 +304,11 @@ struct S_GuildCardSearchResult {
le_uint32_t result_serial_number; le_uint32_t result_serial_number;
HeaderT reconnect_command_header; HeaderT reconnect_command_header;
S_Reconnect_19 reconnect_command; S_Reconnect_19 reconnect_command;
char location_string[0x44]; ptext<char, 0x44> location_string;
le_uint32_t menu_id; le_uint32_t menu_id;
le_uint32_t lobby_id; le_uint32_t lobby_id;
char unused[0x3C]; ptext<char, 0x3C> unused;
CharT name[0x20]; ptext<CharT, 0x20> name;
}; };
struct S_GuildCardSearchResult_PC_40 : S_GuildCardSearchResult<PSOCommandHeaderPC, char16_t> { }; struct S_GuildCardSearchResult_PC_40 : S_GuildCardSearchResult<PSOCommandHeaderPC, char16_t> { };
struct S_GuildCardSearchResult_DC_GC_40 : S_GuildCardSearchResult<PSOCommandHeaderDCGC, char> { }; struct S_GuildCardSearchResult_DC_GC_40 : S_GuildCardSearchResult<PSOCommandHeaderDCGC, char> { };
@@ -326,19 +326,19 @@ struct S_GuildCardSearchResult_BB_40 : S_GuildCardSearchResult<PSOCommandHeaderB
// Used for downloading online quests // Used for downloading online quests
struct S_OpenFile_PC_GC_44_A6 { struct S_OpenFile_PC_GC_44_A6 {
char name[0x20]; ptext<char, 0x20> name;
le_uint16_t unused; le_uint16_t unused;
le_uint16_t flags; le_uint16_t flags;
char filename[0x10]; ptext<char, 0x10> filename;
le_uint32_t file_size; le_uint32_t file_size;
}; };
struct S_OpenFile_BB_44_A6 { struct S_OpenFile_BB_44_A6 {
uint8_t unused[0x22]; parray<uint8_t, 0x22> unused;
le_uint16_t flags; le_uint16_t flags;
char filename[0x10]; ptext<char, 0x10> filename;
le_uint32_t file_size; le_uint32_t file_size;
char name[0x18]; ptext<char, 0x18> name;
}; };
// 45: Invalid command // 45: Invalid command
@@ -391,7 +391,7 @@ struct S_OpenFile_BB_44_A6 {
// Header flag = entry count // Header flag = entry count
template <typename LobbyDataT, typename DispDataT> template <typename LobbyDataT, typename DispDataT>
struct S_JoinGame { struct S_JoinGame {
le_uint32_t variations[0x20]; parray<le_uint32_t, 0x20> variations;
// Unlike lobby join commands, these are filled in in their slot positions. // Unlike lobby join commands, these are filled in in their slot positions.
// That is, if there's one player in a game with ID 2, then the first two of // That is, if there's one player in a game with ID 2, then the first two of
// these are blank and the player's data is in the third entry here. // these are blank and the player's data is in the third entry here.
@@ -514,9 +514,9 @@ struct S_LeaveLobby_66_69 {
struct SC_SimpleMail_GC_81 { struct SC_SimpleMail_GC_81 {
le_uint32_t player_tag; le_uint32_t player_tag;
le_uint32_t from_serial_number; le_uint32_t from_serial_number;
char from_name[0x10]; ptext<char, 0x10> from_name;
le_uint32_t to_serial_number; le_uint32_t to_serial_number;
char text[0x200]; ptext<char, 0x200> text;
}; };
// 82: Invalid command // 82: Invalid command
@@ -574,11 +574,11 @@ struct S_ArrowUpdateEntry_88 {
// 93 (C->S): Log in (BB) // 93 (C->S): Log in (BB)
struct C_Login_BB_93 { struct C_Login_BB_93 {
char unused[0x14]; ptext<char, 0x14> unused;
char username[0x10]; ptext<char, 0x10> username;
char unused2[0x20]; ptext<char, 0x20> unused2;
char password[0x10]; ptext<char, 0x10> password;
char unused3[0x30]; ptext<char, 0x30> unused3;
ClientConfig cfg; ClientConfig cfg;
}; };
@@ -611,9 +611,9 @@ struct C_ClientChecksum_GC_96 {
// 9A (C->S): Initial login (no password or client config) // 9A (C->S): Initial login (no password or client config)
struct C_Login_DC_PC_GC_9A { struct C_Login_DC_PC_GC_9A {
char unused[0x20]; ptext<char, 0x20> unused;
char serial_number[0x10]; ptext<char, 0x10> serial_number;
char access_key[0x10]; ptext<char, 0x10> access_key;
}; };
// 9B: Invalid command // 9B: Invalid command
@@ -625,12 +625,12 @@ struct C_Login_DC_PC_GC_9A {
// 9C (C->S): Register // 9C (C->S): Register
struct C_Register_DC_PC_GC_9C { struct C_Register_DC_PC_GC_9C {
char unused[8]; ptext<char, 8> unused;
le_uint32_t sub_version; le_uint32_t sub_version;
le_uint32_t unused2; le_uint32_t unused2;
char serial_number[0x30]; ptext<char, 0x30> serial_number;
char access_key[0x30]; ptext<char, 0x30> access_key;
char password[0x30]; ptext<char, 0x30> password;
}; };
// 9D (C->S): Log in with client config // 9D (C->S): Log in with client config
@@ -641,19 +641,19 @@ struct C_Register_DC_PC_GC_9C {
struct C_Login_PC_GC_9D_9E { struct C_Login_PC_GC_9D_9E {
le_uint32_t player_tag; // 00 00 01 00 if guild card is set (via 04) le_uint32_t player_tag; // 00 00 01 00 if guild card is set (via 04)
le_uint32_t guild_card_number; // FF FF FF FF if not set le_uint32_t guild_card_number; // FF FF FF FF if not set
le_uint32_t unused1[2]; le_uint64_t unused;
le_uint32_t sub_version; le_uint32_t sub_version;
uint8_t unused2[0x24]; // 00 01 00 00 ... (rest is 00) parray<uint8_t, 0x24> unused2; // 00 01 00 00 ... (rest is 00)
char serial_number[0x10]; ptext<char, 0x10> serial_number;
char access_key[0x10]; ptext<char, 0x10> access_key;
char serial_number2[0x30]; ptext<char, 0x30> serial_number2;
char access_key2[0x30]; ptext<char, 0x30> access_key2;
char name[0x10]; ptext<char, 0x10> name;
// Note: there are 8 bytes at the end of cfg that are technically not // Note: there are 8 bytes at the end of cfg that are technically not
// included in the client config on GC, but the field after it is // included in the client config on GC, but the field after it is
// sufficiently large and unused anyway // sufficiently large and unused anyway
ClientConfig cfg; ClientConfig cfg;
uint8_t unused4[0x5C]; parray<uint8_t, 0x5C> unused4;
}; };
// 9F: Invalid command // 9F: Invalid command
@@ -679,8 +679,8 @@ template <typename CharT>
struct S_QuestMenuEntry { struct S_QuestMenuEntry {
le_uint32_t menu_id; le_uint32_t menu_id;
le_uint32_t item_id; le_uint32_t item_id;
CharT name[0x20]; ptext<CharT, 0x20> name;
CharT short_desc[0x70]; ptext<CharT, 0x70> short_desc;
}; };
struct S_QuestMenuEntry_PC_A2_A4 : S_QuestMenuEntry<char16_t> { }; struct S_QuestMenuEntry_PC_A2_A4 : S_QuestMenuEntry<char16_t> { };
struct S_QuestMenuEntry_GC_A2_A4 : S_QuestMenuEntry<char> { }; struct S_QuestMenuEntry_GC_A2_A4 : S_QuestMenuEntry<char> { };
@@ -688,8 +688,9 @@ struct S_QuestMenuEntry_GC_A2_A4 : S_QuestMenuEntry<char> { };
struct S_QuestMenuEntry_BB_A2_A4 { struct S_QuestMenuEntry_BB_A2_A4 {
le_uint32_t menu_id; le_uint32_t menu_id;
le_uint32_t item_id; le_uint32_t item_id;
char16_t name[0x20]; ptext<char16_t, 0x20> name;
char16_t short_desc[0x7A]; // Why is this not the same as PC? Mysteries of SEGA's engineering... // Why is this 10 characters longer than on other versions...?
ptext<char16_t, 0x7A> short_desc;
}; };
// A3 (S->C): Quest information // A3 (S->C): Quest information
@@ -755,7 +756,7 @@ struct S_QuestMenuEntry_BB_A2_A4 {
struct S_RankUpdate_GC_Ep3_B7 { struct S_RankUpdate_GC_Ep3_B7 {
le_uint32_t rank; le_uint32_t rank;
char rank_text[0x0C]; ptext<char, 0x0C> rank_text;
le_uint32_t meseta; le_uint32_t meseta;
le_uint32_t max_meseta; le_uint32_t max_meseta;
le_uint32_t jukebox_songs_unlocked; le_uint32_t jukebox_songs_unlocked;
@@ -799,7 +800,7 @@ struct S_ChoiceSearchEntry {
// be set by the user at any time; otherwise it can't. // be set by the user at any time; otherwise it can't.
le_uint16_t parent_category_id; // 0 for top-level categories le_uint16_t parent_category_id; // 0 for top-level categories
le_uint16_t category_id; le_uint16_t category_id;
CharT text[0x1C]; ptext<CharT, 0x1C> text;
}; };
struct S_ChoiceSearchEntry_DC_GC_C0 : S_ChoiceSearchEntry<char> { }; struct S_ChoiceSearchEntry_DC_GC_C0 : S_ChoiceSearchEntry<char> { };
struct S_ChoiceSearchEntry_PC_BB_C0 : S_ChoiceSearchEntry<char16_t> { }; struct S_ChoiceSearchEntry_PC_BB_C0 : S_ChoiceSearchEntry<char16_t> { };
@@ -821,9 +822,9 @@ struct S_ChoiceSearchEntry_PC_BB_C0 : S_ChoiceSearchEntry<char16_t> { };
template <typename CharT> template <typename CharT>
struct C_CreateGame { struct C_CreateGame {
le_uint32_t unused[2]; le_uint64_t unused;
CharT name[0x10]; ptext<CharT, 0x10> name;
CharT password[0x10]; ptext<CharT, 0x10> password;
uint8_t difficulty; uint8_t difficulty;
uint8_t battle_mode; uint8_t battle_mode;
uint8_t challenge_mode; uint8_t challenge_mode;
@@ -831,6 +832,7 @@ struct C_CreateGame {
}; };
struct C_CreateGame_DC_GC_C1_EC : C_CreateGame<char> { }; struct C_CreateGame_DC_GC_C1_EC : C_CreateGame<char> { };
struct C_CreateGame_PC_C1 : C_CreateGame<char16_t> { }; struct C_CreateGame_PC_C1 : C_CreateGame<char16_t> { };
struct C_CreateGame_BB_C1 : C_CreateGame<char16_t> { struct C_CreateGame_BB_C1 : C_CreateGame<char16_t> {
uint8_t solo_mode; uint8_t solo_mode;
uint8_t unused2[3]; uint8_t unused2[3];
@@ -864,12 +866,12 @@ struct C_ExecuteChoiceSearch_C3 {
// Command is a list of these; header.flag is the entry count // Command is a list of these; header.flag is the entry count
struct S_ChoiceSearchResultEntry_GC_C4 { struct S_ChoiceSearchResultEntry_GC_C4 {
le_uint32_t guild_card_number; le_uint32_t guild_card_number;
char name[0x10]; // No language marker, as usual on GC ptext<char, 0x10> name; // No language marker, as usual on GC
char info_string[0x20]; // Usually something like "<class> Lvl <level>" ptext<char, 0x20> info_string; // Usually something like "<class> Lvl <level>"
// Format is stricter here; this is "LOBBYNAME,BLOCKNUM,SHIPNAME" // Format is stricter here; this is "LOBBYNAME,BLOCKNUM,SHIPNAME"
// If target is in game, for example, "Game Name,BLOCK01,Alexandria" // If target is in game, for example, "Game Name,BLOCK01,Alexandria"
// If target is in lobby, for example, "BLOCK01-1,BLOCK01,Alexandria" // If target is in lobby, for example, "BLOCK01-1,BLOCK01,Alexandria"
char locator_string[0x34]; ptext<char, 0x34> locator_string;
// Server IP and port for "meet user" option // Server IP and port for "meet user" option
le_uint32_t server_ip; le_uint32_t server_ip;
le_uint16_t server_port; le_uint16_t server_port;
@@ -877,7 +879,7 @@ struct S_ChoiceSearchResultEntry_GC_C4 {
le_uint32_t menu_id; le_uint32_t menu_id;
le_uint32_t lobby_id; // These two are guesses le_uint32_t lobby_id; // These two are guesses
le_uint32_t game_id; // Zero if target is in a lobby rather than a game le_uint32_t game_id; // Zero if target is in a lobby rather than a game
uint8_t unused2[0x58]; parray<uint8_t, 0x58> unused2;
}; };
// C5 (S->C): Challenge rank update // C5 (S->C): Challenge rank update
@@ -886,7 +888,7 @@ struct S_ChoiceSearchResultEntry_GC_C4 {
// C6 (C->S): Set blocked senders list // C6 (C->S): Set blocked senders list
struct C_SetBlockedSenders_C6 { struct C_SetBlockedSenders_C6 {
le_uint32_t blocked_senders[30]; parray<le_uint32_t, 30> blocked_senders;
}; };
// C7 (C->S): Enable simple mail auto-reply // C7 (C->S): Enable simple mail auto-reply
@@ -936,7 +938,7 @@ struct C_SetBlockedSenders_C6 {
// D7 (C->S): Request GBA game file // D7 (C->S): Request GBA game file
struct C_GBAGameRequest_GC_D7 { struct C_GBAGameRequest_GC_D7 {
char filename[0x10]; ptext<char, 0x10> filename;
}; };
// D8 (S->C): Info board // D8 (S->C): Info board
@@ -944,8 +946,8 @@ struct C_GBAGameRequest_GC_D7 {
// Command is a list of these; header.flag is the entry count // Command is a list of these; header.flag is the entry count
template <typename CharT> template <typename CharT>
struct S_InfoBoardEntry_D8 { struct S_InfoBoardEntry_D8 {
CharT name[0x10]; ptext<CharT, 0x10> name;
CharT message[0xAC]; ptext<CharT, 0xAC> message;
}; };
struct S_InfoBoardEntry_PC_BB_D8 : S_InfoBoardEntry_D8<char16_t> { }; struct S_InfoBoardEntry_PC_BB_D8 : S_InfoBoardEntry_D8<char16_t> { };
struct S_InfoBoardEntry_DC_GC_D8 : S_InfoBoardEntry_D8<char> { }; struct S_InfoBoardEntry_DC_GC_D8 : S_InfoBoardEntry_D8<char> { };
@@ -959,14 +961,14 @@ struct S_InfoBoardEntry_DC_GC_D8 : S_InfoBoardEntry_D8<char> { };
// DB (S->C): Verify license (GC) // DB (S->C): Verify license (GC)
struct C_VerifyLicense_GC_DB { struct C_VerifyLicense_GC_DB {
char unused[0x20]; ptext<char, 0x20> unused;
char serial_number[0x10]; ptext<char, 0x10> serial_number;
char access_key[0x10]; ptext<char, 0x10> access_key;
char unused2[0x08]; ptext<char, 0x08> unused2;
le_uint32_t sub_version; le_uint32_t sub_version;
char serial_number2[0x30]; ptext<char, 0x30> serial_number2;
char access_key2[0x30]; ptext<char, 0x30> access_key2;
char password[0x30]; ptext<char, 0x30> password;
}; };
// DC: Player menu state (Episode 3) // DC: Player menu state (Episode 3)
@@ -1002,7 +1004,7 @@ struct S_GuildCardHeader_BB_01DC {
struct S_TournamentEntry_GC_Ep3_E0 { struct S_TournamentEntry_GC_Ep3_E0 {
le_uint32_t menu_id; le_uint32_t menu_id;
le_uint32_t item_id; le_uint32_t item_id;
uint8_t unknown[0x30]; parray<uint8_t, 0x30> unknown;
}; };
// E0 (C->S): Request team and key config (BB) // E0 (C->S): Request team and key config (BB)
@@ -1104,7 +1106,7 @@ struct S_StreamFileIndexEntry_BB_01EB {
le_uint32_t size; le_uint32_t size;
le_uint32_t checksum; le_uint32_t checksum;
le_uint32_t offset; le_uint32_t offset;
char filename[0x40]; ptext<char, 0x40> filename;
} __attribute__((packed)); } __attribute__((packed));
struct S_StreamFileChunk_BB_02EB { struct S_StreamFileChunk_BB_02EB {
@@ -1123,12 +1125,12 @@ struct S_StreamFileChunk_BB_02EB {
union C_UpdateAccountData_BB_ED { union C_UpdateAccountData_BB_ED {
le_uint32_t option; // 01ED le_uint32_t option; // 01ED
uint8_t symbol_chats[0x4E0]; // 02ED parray<uint8_t, 0x4E0> symbol_chats; // 02ED
uint8_t chat_shortcuts[0xA40]; // 03ED parray<uint8_t, 0xA40> chat_shortcuts; // 03ED
uint8_t key_config[0x16C]; // 04ED parray<uint8_t, 0x16C> key_config; // 04ED
uint8_t pad_config[0x38]; // 05ED parray<uint8_t, 0x38> pad_config; // 05ED
uint8_t tech_menu[0x28]; // 06ED parray<uint8_t, 0x28> tech_menu; // 06ED
uint8_t customize[0xE8]; // 07ED parray<uint8_t, 0xE8> customize; // 07ED
} __attribute__((packed)); } __attribute__((packed));
// EE: Scrolling message (BB) // EE: Scrolling message (BB)
@@ -1162,8 +1164,8 @@ struct S_SendGuildCard_GC {
le_uint16_t unused; le_uint16_t unused;
le_uint32_t player_tag; le_uint32_t player_tag;
le_uint32_t serial_number; le_uint32_t serial_number;
char name[0x18]; ptext<char, 0x18> name;
char desc[0x6C]; ptext<char, 0x6C> desc;
uint8_t reserved1; uint8_t reserved1;
uint8_t reserved2; uint8_t reserved2;
uint8_t section_id; uint8_t section_id;
@@ -1175,9 +1177,9 @@ struct S_SendGuildCard_BB {
uint8_t subsize; uint8_t subsize;
le_uint16_t unused; le_uint16_t unused;
le_uint32_t serial_number; le_uint32_t serial_number;
char16_t name[0x18]; ptext<char16_t, 0x18> name;
char16_t team_name[0x10]; ptext<char16_t, 0x10> team_name;
char16_t desc[0x58]; ptext<char16_t, 0x58> desc;
uint8_t reserved1; uint8_t reserved1;
uint8_t reserved2; uint8_t reserved2;
uint8_t section_id; uint8_t section_id;
+1 -1
View File
@@ -168,7 +168,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index) {
auto& item = c->player.inventory.items[item_index]; auto& item = c->player.inventory.items[item_index];
if (item.data.item_data1w[0] == 0x0203) { // technique disk if (item.data.item_data1w[0] == 0x0203) { // technique disk
c->player.disp.technique_levels[item.data.item_data1[4]] = item.data.item_data1[2]; c->player.disp.technique_levels.data()[item.data.item_data1[4]] = item.data.item_data1[2];
} else if (item.data.item_data1w[0] == 0x0A03) { // grinder } else if (item.data.item_data1w[0] == 0x0A03) { // grinder
if (equipped_weapon < 0) { if (equipped_weapon < 0) {
+18 -28
View File
@@ -11,31 +11,23 @@ using namespace std;
License::License() { License::License() : serial_number(0), privileges(0), ban_end_time(0) { }
memset(this->username, 0, 20);
memset(this->bb_password, 0, 20);
this->serial_number = 0;
memset(this->access_key, 0, 16);
memset(this->gc_password, 0, 12);
this->privileges = 0;
this->ban_end_time = 0;
}
string License::str() const { string License::str() const {
string ret = string_printf("License(serial_number=%" PRIu32, this->serial_number); string ret = string_printf("License(serial_number=%" PRIu32, this->serial_number);
if (this->username[0]) { if (!this->username.empty()) {
ret += ", username="; ret += ", username=";
ret += this->username; ret += this->username;
} }
if (this->bb_password[0]) { if (!this->bb_password.empty()) {
ret += ", bb-password="; ret += ", bb-password=";
ret += this->bb_password; ret += this->bb_password;
} }
if (this->access_key[0]) { if (!this->access_key.empty()) {
ret += ", access-key="; ret += ", access-key=";
ret += this->access_key; ret += this->access_key;
} }
if (this->gc_password[0]) { if (!this->gc_password.empty()) {
ret += ", gc-password="; ret += ", gc-password=";
ret += this->gc_password; ret += this->gc_password;
} }
@@ -83,10 +75,10 @@ void LicenseManager::save() const {
shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number, shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number,
const char* access_key, const char* password) const { const char* access_key, const char* password) const {
auto& license = this->serial_number_to_license.at(serial_number); auto& license = this->serial_number_to_license.at(serial_number);
if (strncmp(license->access_key, access_key, 8)) { if (!license->access_key.eq_n(access_key, 8)) {
throw invalid_argument("incorrect access key"); throw invalid_argument("incorrect access key");
} }
if (password && (strcmp(license->gc_password, password))) { if (password && (license->gc_password != password)) {
throw invalid_argument("incorrect password"); throw invalid_argument("incorrect password");
} }
@@ -99,10 +91,10 @@ shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number,
shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number, shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
const char* access_key, const char* password) const { const char* access_key, const char* password) const {
auto& license = this->serial_number_to_license.at(serial_number); auto& license = this->serial_number_to_license.at(serial_number);
if (strncmp(license->access_key, access_key, 12)) { if (!license->access_key.eq_n(access_key, 12)) {
throw invalid_argument("incorrect access key"); throw invalid_argument("incorrect access key");
} }
if (password && (strcmp(license->gc_password, password))) { if (password && (license->gc_password != password)) {
throw invalid_argument("incorrect password"); throw invalid_argument("incorrect password");
} }
@@ -115,7 +107,7 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
shared_ptr<const License> LicenseManager::verify_bb(const char* username, shared_ptr<const License> LicenseManager::verify_bb(const char* username,
const char* password) const { const char* password) const {
auto& license = this->bb_username_to_license.at(username); auto& license = this->bb_username_to_license.at(username);
if (password && strcmp(license->bb_password, password)) { if (password && (license->bb_password != password)) {
throw invalid_argument("incorrect password"); throw invalid_argument("incorrect password");
} }
@@ -137,20 +129,18 @@ void LicenseManager::ban_until(uint32_t serial_number, uint64_t end_time) {
void LicenseManager::add(shared_ptr<License> l) { void LicenseManager::add(shared_ptr<License> l) {
uint32_t serial_number = l->serial_number; uint32_t serial_number = l->serial_number;
this->serial_number_to_license.emplace(serial_number, l); this->serial_number_to_license.emplace(serial_number, l);
if (l->username[0]) { if (!l->username.empty()) {
this->bb_username_to_license.emplace(l->username, l); this->bb_username_to_license.emplace(l->username, l);
} }
this->save(); this->save();
} }
void LicenseManager::remove(uint32_t serial_number) { void LicenseManager::remove(uint32_t serial_number) {
auto l = this->serial_number_to_license.at(serial_number); auto l = this->serial_number_to_license.at(serial_number);
this->serial_number_to_license.erase(l->serial_number); this->serial_number_to_license.erase(l->serial_number);
if (l->username[0]) { if (!l->username.empty()) {
this->bb_username_to_license.erase(l->username); this->bb_username_to_license.erase(l->username);
} }
this->save(); this->save();
} }
@@ -168,9 +158,9 @@ shared_ptr<License> LicenseManager::create_license_pc(
uint32_t serial_number,const char* access_key, const char* password, bool temporary) { uint32_t serial_number,const char* access_key, const char* password, bool temporary) {
shared_ptr<License> l(new License()); shared_ptr<License> l(new License());
l->serial_number = serial_number; l->serial_number = serial_number;
strncpy(l->access_key, access_key, 8); l->access_key = access_key;
if (password) { if (password) {
strncpy(l->gc_password, password, 8); l->gc_password = password;
} }
if (temporary) { if (temporary) {
l->privileges |= Privilege::TEMPORARY; l->privileges |= Privilege::TEMPORARY;
@@ -182,9 +172,9 @@ shared_ptr<License> LicenseManager::create_license_gc(
uint32_t serial_number, const char* access_key, const char* password, bool temporary) { uint32_t serial_number, const char* access_key, const char* password, bool temporary) {
shared_ptr<License> l(new License()); shared_ptr<License> l(new License());
l->serial_number = serial_number; l->serial_number = serial_number;
strncpy(l->access_key, access_key, 12); l->access_key = access_key;
if (password) { if (password) {
strncpy(l->gc_password, password, 8); l->gc_password = password;
} }
if (temporary) { if (temporary) {
l->privileges |= Privilege::TEMPORARY; l->privileges |= Privilege::TEMPORARY;
@@ -196,8 +186,8 @@ shared_ptr<License> LicenseManager::create_license_bb(
uint32_t serial_number, const char* username, const char* password, bool temporary) { uint32_t serial_number, const char* username, const char* password, bool temporary) {
shared_ptr<License> l(new License()); shared_ptr<License> l(new License());
l->serial_number = serial_number; l->serial_number = serial_number;
strncpy(l->username, username, 19); l->username = username;
strncpy(l->bb_password, password, 19); l->bb_password = password;
if (temporary) { if (temporary) {
l->privileges |= Privilege::TEMPORARY; l->privileges |= Privilege::TEMPORARY;
} }
+6 -4
View File
@@ -5,6 +5,8 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "Text.hh"
enum Privilege { enum Privilege {
KICK_USER = 0x00000001, KICK_USER = 0x00000001,
BAN_USER = 0x00000002, BAN_USER = 0x00000002,
@@ -30,11 +32,11 @@ enum LicenseVerifyAction {
}; };
struct License { struct License {
char username[20]; // BB username (max. 16 chars; should technically be Unicode) ptext<char, 0x14> username; // BB username (max. 16 chars; should technically be Unicode)
char bb_password[20]; // BB password (max. 16 chars) ptext<char, 0x14> bb_password; // BB password (max. 16 chars)
uint32_t serial_number; // PC/GC serial number. MUST BE PRESENT FOR BB LICENSES TOO; this is also the player's guild card number. uint32_t serial_number; // PC/GC serial number. MUST BE PRESENT FOR BB LICENSES TOO; this is also the player's guild card number.
char access_key[16]; // PC/GC access key. (to log in using PC on a GC license, just enter the first 8 characters of the GC access key) ptext<char, 0x10> access_key; // PC/GC access key. (to log in using PC on a GC license, just enter the first 8 characters of the GC access key)
char gc_password[12]; // GC password ptext<char, 0x0C> gc_password; // GC password
uint32_t privileges; // privilege level uint32_t privileges; // privilege level
uint64_t ban_end_time; // end time of ban (zero = not banned) uint64_t ban_end_time; // end time of ban (zero = not banned)
+1 -4
View File
@@ -21,9 +21,6 @@ Lobby::Lobby() : lobby_id(0), min_level(0), max_level(0xFFFFFFFF),
this->next_item_id[x] = 0; this->next_item_id[x] = 0;
} }
memset(&this->next_drop_item, 0, sizeof(this->next_drop_item)); memset(&this->next_drop_item, 0, sizeof(this->next_drop_item));
memset(this->variations, 0, 0x20 * sizeof(this->variations[0]));
memset(this->password, 0, 36 * sizeof(this->password[0]));
memset(this->name, 0, 36 * sizeof(this->name[0]));
} }
bool Lobby::is_game() const { bool Lobby::is_game() const {
@@ -152,7 +149,7 @@ shared_ptr<Client> Lobby::find_client(const char16_t* identifier,
(this->clients[x]->license->serial_number == serial_number)) { (this->clients[x]->license->serial_number == serial_number)) {
return this->clients[x]; return this->clients[x];
} }
if (identifier && !char16ncmp(this->clients[x]->player.disp.name, identifier, 0x10)) { if (identifier && (this->clients[x]->player.disp.name == identifier)) {
return this->clients[x]; return this->clients[x];
} }
} }
+3 -3
View File
@@ -33,7 +33,7 @@ struct Lobby {
uint32_t next_game_item_id; uint32_t next_game_item_id;
PlayerInventoryItem next_drop_item; PlayerInventoryItem next_drop_item;
std::unordered_map<uint32_t, PlayerInventoryItem> item_id_to_floor_item; std::unordered_map<uint32_t, PlayerInventoryItem> item_id_to_floor_item;
uint32_t variations[0x20]; parray<le_uint32_t, 0x20> variations;
// game config // game config
GameVersion version; GameVersion version;
@@ -41,8 +41,8 @@ struct Lobby {
uint8_t episode; uint8_t episode;
uint8_t difficulty; uint8_t difficulty;
uint8_t mode; uint8_t mode;
char16_t password[0x24]; std::u16string password;
char16_t name[0x24]; std::u16string name;
uint32_t rare_seed; uint32_t rare_seed;
//EP3_GAME_CONFIG* ep3; // only present if this is an Episode 3 game //EP3_GAME_CONFIG* ep3; // only present if this is an Episode 3 game
+2 -2
View File
@@ -10,11 +10,11 @@ struct BattleParams {
uint16_t atp; // attack power uint16_t atp; // attack power
uint16_t psv; // perseverance (intelligence?) uint16_t psv; // perseverance (intelligence?)
uint16_t evp; // evasion uint16_t evp; // evasion
uint16_t hp; // hit points uint16_t hp; // hit points
uint16_t dfp; // defense uint16_t dfp; // defense
uint16_t ata; // accuracy uint16_t ata; // accuracy
uint16_t lck; // luck uint16_t lck; // luck
uint8_t unknown[14]; uint8_t unknown_a1[0x0E];
uint32_t experience; uint32_t experience;
uint32_t difficulty; uint32_t difficulty;
} __attribute__((packed)); } __attribute__((packed));
+77 -126
View File
@@ -49,19 +49,15 @@ PlayerDispDataBB PlayerDispDataPCGC::to_bb() const {
bb.stats.dfp = this->stats.dfp; bb.stats.dfp = this->stats.dfp;
bb.stats.ata = this->stats.ata; bb.stats.ata = this->stats.ata;
bb.stats.lck = this->stats.lck; bb.stats.lck = this->stats.lck;
bb.unknown1 = this->unknown1; bb.unknown_a1 = this->unknown_a1;
bb.unknown2[0] = this->unknown2[0];
bb.unknown2[1] = this->unknown2[1];
bb.level = this->level; bb.level = this->level;
bb.experience = this->experience; bb.experience = this->experience;
bb.meseta = this->meseta; bb.meseta = this->meseta;
memset(bb.guild_card, 0, sizeof(bb.guild_card)); bb.guild_card = " 0";
strncpy(bb.guild_card, " 0", 0x10); bb.unknown_a2 = this->unknown_a2;
bb.unknown3[0] = this->unknown3[0];
bb.unknown3[1] = this->unknown3[1];
bb.name_color = this->name_color; bb.name_color = this->name_color;
bb.extra_model = this->extra_model; bb.extra_model = this->extra_model;
memcpy(&bb.unused, &this->unused, 15); bb.unused = this->unused;
bb.name_color_checksum = this->name_color_checksum; bb.name_color_checksum = this->name_color_checksum;
bb.section_id = this->section_id; bb.section_id = this->section_id;
bb.char_class = this->char_class; bb.char_class = this->char_class;
@@ -78,11 +74,9 @@ PlayerDispDataBB PlayerDispDataPCGC::to_bb() const {
bb.hair_b = this->hair_b; bb.hair_b = this->hair_b;
bb.proportion_x = this->proportion_x; bb.proportion_x = this->proportion_x;
bb.proportion_y = this->proportion_y; bb.proportion_y = this->proportion_y;
memset(bb.name, 0, sizeof(bb.name)); bb.name = add_language_marker(this->name, 'J');
decode_sjis(bb.name, this->name, 0x10); bb.config = this->config;
add_language_marker_inplace(bb.name, 'J', 0x10); bb.technique_levels = this->technique_levels;
memcpy(&bb.config, &this->config, 0x48);
memcpy(&bb.technique_levels, &this->technique_levels, 0x14);
return bb; return bb;
} }
@@ -96,17 +90,14 @@ PlayerDispDataPCGC PlayerDispDataBB::to_pcgc() const {
pcgc.stats.dfp = this->stats.dfp; pcgc.stats.dfp = this->stats.dfp;
pcgc.stats.ata = this->stats.ata; pcgc.stats.ata = this->stats.ata;
pcgc.stats.lck = this->stats.lck; pcgc.stats.lck = this->stats.lck;
pcgc.unknown1 = this->unknown1; pcgc.unknown_a1 = this->unknown_a1;
pcgc.unknown2[0] = this->unknown2[0];
pcgc.unknown2[1] = this->unknown2[1];
pcgc.level = this->level; pcgc.level = this->level;
pcgc.experience = this->experience; pcgc.experience = this->experience;
pcgc.meseta = this->meseta; pcgc.meseta = this->meseta;
pcgc.unknown3[0] = this->unknown3[0]; pcgc.unknown_a2 = this->unknown_a2;
pcgc.unknown3[1] = this->unknown3[1];
pcgc.name_color = this->name_color; pcgc.name_color = this->name_color;
pcgc.extra_model = this->extra_model; pcgc.extra_model = this->extra_model;
memcpy(&pcgc.unused, &this->unused, 15); pcgc.unused = this->unused;
pcgc.name_color_checksum = this->name_color_checksum; pcgc.name_color_checksum = this->name_color_checksum;
pcgc.section_id = this->section_id; pcgc.section_id = this->section_id;
pcgc.char_class = this->char_class; pcgc.char_class = this->char_class;
@@ -123,11 +114,9 @@ PlayerDispDataPCGC PlayerDispDataBB::to_pcgc() const {
pcgc.hair_b = this->hair_b; pcgc.hair_b = this->hair_b;
pcgc.proportion_x = this->proportion_x; pcgc.proportion_x = this->proportion_x;
pcgc.proportion_y = this->proportion_y; pcgc.proportion_y = this->proportion_y;
memset(pcgc.name, 0, sizeof(pcgc.name)); pcgc.name = remove_language_marker(this->name);
encode_sjis(pcgc.name, this->name, 0x10); pcgc.config = this->config;
remove_language_marker_inplace(pcgc.name); pcgc.technique_levels = this->technique_levels;
memcpy(&pcgc.config, &this->config, 0x48);
memcpy(&pcgc.technique_levels, &this->technique_levels, 0x14);
return pcgc; return pcgc;
} }
@@ -136,13 +125,11 @@ PlayerDispDataBBPreview PlayerDispDataBB::to_preview() const {
PlayerDispDataBBPreview pre; PlayerDispDataBBPreview pre;
pre.level = this->level; pre.level = this->level;
pre.experience = this->experience; pre.experience = this->experience;
memset(pre.guild_card, 0, sizeof(pre.guild_card)); pre.guild_card = this->guild_card;
strncpy(pre.guild_card, this->guild_card, 0x10); pre.unknown_a2 = this->unknown_a2;
pre.unknown3[0] = this->unknown3[0];
pre.unknown3[1] = this->unknown3[1];
pre.name_color = this->name_color; pre.name_color = this->name_color;
pre.extra_model = this->extra_model; pre.extra_model = this->extra_model;
memcpy(&pre.unused, &this->unused, 11); pre.unused = this->unused;
pre.name_color_checksum = this->name_color_checksum; pre.name_color_checksum = this->name_color_checksum;
pre.section_id = this->section_id; pre.section_id = this->section_id;
pre.char_class = this->char_class; pre.char_class = this->char_class;
@@ -159,22 +146,19 @@ PlayerDispDataBBPreview PlayerDispDataBB::to_preview() const {
pre.hair_b = this->hair_b; pre.hair_b = this->hair_b;
pre.proportion_x = this->proportion_x; pre.proportion_x = this->proportion_x;
pre.proportion_y = this->proportion_y; pre.proportion_y = this->proportion_y;
memset(pre.name, 0, sizeof(pre.name)); pre.name = this->name;
strcpy_z(pre.name, this->name, 16); pre.play_time = 0; // TODO: Store this somewhere and return it here
pre.play_time = this->play_time;
return pre; return pre;
} }
void PlayerDispDataBB::apply_preview(const PlayerDispDataBBPreview& pre) { void PlayerDispDataBB::apply_preview(const PlayerDispDataBBPreview& pre) {
this->level = pre.level; this->level = pre.level;
this->experience = pre.experience; this->experience = pre.experience;
memset(this->guild_card, 0, sizeof(this->guild_card)); this->guild_card = pre.guild_card;
strncpy(this->guild_card, pre.guild_card, 0x10); this->unknown_a2 = pre.unknown_a2;
this->unknown3[0] = pre.unknown3[0];
this->unknown3[1] = pre.unknown3[1];
this->name_color = pre.name_color; this->name_color = pre.name_color;
this->extra_model = pre.extra_model; this->extra_model = pre.extra_model;
memcpy(&this->unused, &pre.unused, 11); this->unused = pre.unused;
this->name_color_checksum = pre.name_color_checksum; this->name_color_checksum = pre.name_color_checksum;
this->section_id = pre.section_id; this->section_id = pre.section_id;
this->char_class = pre.char_class; this->char_class = pre.char_class;
@@ -191,9 +175,7 @@ void PlayerDispDataBB::apply_preview(const PlayerDispDataBBPreview& pre) {
this->hair_b = pre.hair_b; this->hair_b = pre.hair_b;
this->proportion_x = pre.proportion_x; this->proportion_x = pre.proportion_x;
this->proportion_y = pre.proportion_y; this->proportion_y = pre.proportion_y;
memset(this->name, 0, sizeof(this->name)); this->name = pre.name;
strcpy_z(this->name, pre.name, 0x10);
this->play_time = 0;
} }
@@ -214,43 +196,33 @@ void PlayerBank::save(const string& filename) const {
void Player::import(const PSOPlayerDataPC& pc) { void Player::import(const PSOPlayerDataPC& pc) {
this->inventory = pc.inventory; this->inventory = pc.inventory;
this->disp = pc.disp.to_bb(); this->disp = pc.disp.to_bb();
/* TODO: fix and re-enable this functionality // TODO: Add these fields to the existing structure so we can parse them
memset(this->info_board, 0, sizeof(this->info_board)); // this->info_board = pc.info_board;
decode_sjis(this->info_board, pc->info_board); // this->blocked_senders = pc.blocked_senders;
memcpy(&this->blocked, pc->blocked, sizeof(uint32_t) * 30); // this->auto_reply = pc.auto_reply;
memset(this->auto_reply, 0, sizeof(this->auto_reply));
if (pc->auto_reply_enabled) {
decode_sjis(this->auto_reply, pc->auto_reply);
} else {*/
this->auto_reply[0] = 0;
//}
} }
void Player::import(const PSOPlayerDataGC& gc) { void Player::import(const PSOPlayerDataGC& gc) {
this->inventory = gc.inventory; this->inventory = gc.inventory;
this->disp = gc.disp.to_bb(); this->disp = gc.disp.to_bb();
memset(this->info_board, 0, sizeof(this->info_board)); this->info_board = gc.info_board;
decode_sjis(this->info_board, gc.info_board, 0xAC); this->blocked_senders = gc.blocked_senders;
memcpy(&this->blocked, gc.blocked, sizeof(uint32_t) * 30);
memset(this->auto_reply, 0, sizeof(this->auto_reply));
if (gc.auto_reply_enabled) { if (gc.auto_reply_enabled) {
decode_sjis(this->auto_reply, gc.auto_reply, 0xAC); this->auto_reply = gc.auto_reply;
} else { } else {
this->auto_reply[0] = 0; this->auto_reply.clear();
} }
} }
void Player::import(const PSOPlayerDataBB& bb) { void Player::import(const PSOPlayerDataBB& bb) {
// note: we don't copy the inventory and disp here because we already have // Note: we don't copy the inventory and disp here because we already have
// it (we sent the player data to the client in the first place) // it (we sent the player data to the client in the first place)
memset(this->info_board, 0, sizeof(this->info_board)); this->info_board = bb.info_board;
strcpy_z(this->info_board, bb.info_board, 0xAC); this->blocked_senders = bb.blocked_senders;
memcpy(&this->blocked, bb.blocked, sizeof(uint32_t) * 30);
memset(this->auto_reply, 0, sizeof(this->auto_reply));
if (bb.auto_reply_enabled) { if (bb.auto_reply_enabled) {
strcpy_z(this->auto_reply, bb.auto_reply, 0xAC); this->auto_reply = bb.auto_reply;
} else { } else {
this->auto_reply[0] = 0; this->auto_reply.clear();
} }
} }
@@ -258,33 +230,28 @@ PlayerBB Player::export_bb_player_data() const {
PlayerBB bb; PlayerBB bb;
bb.inventory = this->inventory; bb.inventory = this->inventory;
bb.disp = this->disp; bb.disp = this->disp;
memset(bb.unknown, 0, 0x10); bb.unknown.clear();
bb.option_flags = this->option_flags; bb.option_flags = this->option_flags;
memcpy(bb.quest_data1, &this->quest_data1, 0x0208); bb.quest_data1 = this->quest_data1;
bb.bank = this->bank; bb.bank = this->bank;
bb.serial_number = this->serial_number; bb.serial_number = this->serial_number;
memset(bb.name, 0, sizeof(bb.name)); bb.name = this->disp.name;
strcpy_z(bb.name, this->disp.name, 24); bb.team_name = this->team_name;
memset(bb.team_name, 0, sizeof(bb.team_name)); bb.guild_card_desc = this->guild_card_desc;
strcpy_z(bb.team_name, this->team_name, 16);
memset(bb.guild_card_desc, 0, sizeof(bb.guild_card_desc));
strcpy_z(bb.guild_card_desc, this->guild_card_desc, 0x58);
bb.reserved1 = 0; bb.reserved1 = 0;
bb.reserved2 = 0; bb.reserved2 = 0;
bb.section_id = this->disp.section_id; bb.section_id = this->disp.section_id;
bb.char_class = this->disp.char_class; bb.char_class = this->disp.char_class;
bb.unknown3 = 0; bb.unknown3 = 0;
memcpy(bb.symbol_chats, this->symbol_chats, 0x04E0); bb.symbol_chats = this->symbol_chats;
memcpy(bb.shortcuts, this->shortcuts, 0x0A40); bb.shortcuts = this->shortcuts;
memset(bb.auto_reply, 0, sizeof(bb.auto_reply)); bb.auto_reply = this->auto_reply;
strcpy_z(bb.auto_reply, this->auto_reply, 0xAC); bb.info_board = this->info_board;
memset(bb.info_board, 0, sizeof(bb.info_board)); bb.unknown5.clear();
strcpy_z(bb.info_board, this->info_board, 0xAC); bb.challenge_data = this->challenge_data;
memset(bb.unknown5, 0, 0x1C); bb.tech_menu_config = this->tech_menu_config;
memcpy(bb.challenge_data, this->challenge_data, 0x0140); bb.unknown6.clear();
memcpy(bb.tech_menu_config, this->tech_menu_config, 0x0028); bb.quest_data2 = this->quest_data2;
memset(bb.unknown6, 0, 0x2C);
memcpy(bb.quest_data2, &this->quest_data2, 0x0058);
bb.key_config = this->key_config; bb.key_config = this->key_config;
return bb; return bb;
} }
@@ -311,78 +278,62 @@ uint32_t compute_guild_card_checksum(const void* vdata, size_t size) {
void Player::load_account_data(const string& filename) { void Player::load_account_data(const string& filename) {
SavedAccountBB account = load_object_file<SavedAccountBB>(filename); SavedAccountBB account = load_object_file<SavedAccountBB>(filename);
if (account.signature != ACCOUNT_FILE_SIGNATURE) {
if (strcmp(account.signature, ACCOUNT_FILE_SIGNATURE)) {
throw runtime_error("account data header is incorrect"); throw runtime_error("account data header is incorrect");
} }
this->blocked_senders = account.blocked_senders;
memcpy(&this->blocked, &account.blocked, sizeof(uint32_t) * 30);
this->guild_cards = account.guild_cards; this->guild_cards = account.guild_cards;
this->key_config = account.key_config; this->key_config = account.key_config;
this->option_flags = account.option_flags; this->option_flags = account.option_flags;
memcpy(&this->shortcuts, &account.shortcuts, 0x0A40); this->shortcuts = account.shortcuts;
memcpy(&this->symbol_chats, &account.symbol_chats, 0x04E0); this->symbol_chats = account.symbol_chats;
memset(this->team_name, 0, sizeof(this->team_name)); this->team_name = account.team_name;
strcpy_z(this->team_name, account.team_name, 16);
} }
void Player::save_account_data(const string& filename) const { void Player::save_account_data(const string& filename) const {
SavedAccountBB account; SavedAccountBB account;
account.signature = ACCOUNT_FILE_SIGNATURE;
strncpy(account.signature, ACCOUNT_FILE_SIGNATURE, sizeof(account.signature)); account.blocked_senders = this->blocked_senders;
memcpy(&account.blocked, &this->blocked, sizeof(uint32_t) * 30);
account.guild_cards = this->guild_cards; account.guild_cards = this->guild_cards;
account.key_config = this->key_config; account.key_config = this->key_config;
account.option_flags = this->option_flags; account.option_flags = this->option_flags;
memcpy(&account.shortcuts, &this->shortcuts, 0x0A40); account.shortcuts = this->shortcuts;
memcpy(&account.symbol_chats, &this->symbol_chats, 0x04E0); account.symbol_chats = this->symbol_chats;
memset(account.team_name, 0, sizeof(account.team_name)); account.team_name = this->team_name;
strcpy_z(account.team_name, this->team_name, 16);
save_file(filename, &account, sizeof(account)); save_file(filename, &account, sizeof(account));
} }
void Player::load_player_data(const string& filename) { void Player::load_player_data(const string& filename) {
SavedPlayerBB player = load_object_file<SavedPlayerBB>(filename); SavedPlayerBB player = load_object_file<SavedPlayerBB>(filename);
if (player.signature != PLAYER_FILE_SIGNATURE) {
if (strcmp(player.signature, PLAYER_FILE_SIGNATURE)) {
throw runtime_error("account data header is incorrect"); throw runtime_error("account data header is incorrect");
} }
this->auto_reply = player.auto_reply;
memset(this->auto_reply, 0, sizeof(this->auto_reply));
strcpy_z(this->auto_reply, player.auto_reply, 0xAC);
this->bank = player.bank; this->bank = player.bank;
memcpy(&this->challenge_data, &player.challenge_data, 0x0140); this->challenge_data = player.challenge_data;
this->disp = player.disp; this->disp = player.disp;
memset(this->guild_card_desc, 0, sizeof(this->guild_card_desc)); this->guild_card_desc = player.guild_card_desc;
strcpy_z(this->guild_card_desc, player.guild_card_desc, 0x58); this->info_board = player.info_board;
memset(this->info_board, 0, sizeof(this->info_board));
strcpy_z(this->info_board, player.info_board, 0xAC);
this->inventory = player.inventory; this->inventory = player.inventory;
memcpy(&this->quest_data1, &player.quest_data1, 0x0208); this->quest_data1 = player.quest_data1;
memcpy(&this->quest_data2, &player.quest_data2, 0x0058); this->quest_data2 = player.quest_data2;
memcpy(&this->tech_menu_config, &player.tech_menu_config, 0x0028); this->tech_menu_config = player.tech_menu_config;
} }
void Player::save_player_data(const string& filename) const { void Player::save_player_data(const string& filename) const {
SavedPlayerBB player; SavedPlayerBB player;
player.signature = PLAYER_FILE_SIGNATURE;
strncpy(player.signature, PLAYER_FILE_SIGNATURE, sizeof(player.signature));
player.preview = this->disp.to_preview(); player.preview = this->disp.to_preview();
memset(player.auto_reply, 0, sizeof(player.auto_reply)); player.auto_reply = this->auto_reply;
strcpy_z(player.auto_reply, this->auto_reply, 0xAC);
player.bank = this->bank; player.bank = this->bank;
memcpy(&player.challenge_data, &this->challenge_data, 0x0140); player.challenge_data = this->challenge_data;
player.disp = this->disp; player.disp = this->disp;
memset(player.guild_card_desc, 0, sizeof(player.guild_card_desc)); player.guild_card_desc = this->guild_card_desc;
strcpy_z(player.guild_card_desc,this->guild_card_desc, 0x58); player.info_board = this->info_board;
memset(player.info_board, 0, sizeof(player.info_board));
strcpy_z(player.info_board, this->info_board, 0xAC);
player.inventory = this->inventory; player.inventory = this->inventory;
memcpy(&player.quest_data1, &this->quest_data1, 0x0208); player.quest_data1 = this->quest_data1;
memcpy(&player.quest_data2, &this->quest_data2, 0x0058); player.quest_data2 = this->quest_data2;
memcpy(&player.tech_menu_config, &this->tech_menu_config, 0x0028); player.tech_menu_config = this->tech_menu_config;
save_file(filename, &player, sizeof(player)); save_file(filename, &player, sizeof(player));
} }
@@ -639,9 +590,9 @@ string filename_for_player_bb(const string& username, uint8_t player_index) {
static_cast<uint8_t>(player_index + 1)); static_cast<uint8_t>(player_index + 1));
} }
string filename_for_bank_bb(const string& username, const char* bank_name) { string filename_for_bank_bb(const string& username, const std::string& bank_name) {
return string_printf("system/players/bank_%s_%s.nsb", username.c_str(), return string_printf("system/players/bank_%s_%s.nsb", username.c_str(),
bank_name); bank_name.c_str());
} }
string filename_for_class_template_bb(uint8_t char_class) { string filename_for_class_template_bb(uint8_t char_class) {
+200 -203
View File
@@ -8,21 +8,23 @@
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include "Version.hh" #include "Version.hh"
#include "Text.hh"
// raw item data // raw item data
// TODO: use parray for the fields here
struct ItemData { struct ItemData {
union { union {
uint8_t item_data1[12]; uint8_t item_data1[12];
uint16_t item_data1w[6]; le_uint16_t item_data1w[6];
uint32_t item_data1d[3]; le_uint32_t item_data1d[3];
} __attribute__((packed)); } __attribute__((packed));
uint32_t item_id; uint32_t item_id;
union { union {
uint8_t item_data2[4]; uint8_t item_data2[4];
uint16_t item_data2w[2]; le_uint16_t item_data2w[2];
uint32_t item_data2d; le_uint32_t item_data2d;
} __attribute__((packed)); } __attribute__((packed));
uint32_t primary_identifier() const; uint32_t primary_identifier() const;
@@ -32,9 +34,9 @@ struct PlayerBankItem;
// an item in a player's inventory // an item in a player's inventory
struct PlayerInventoryItem { struct PlayerInventoryItem {
uint16_t equip_flags; le_uint16_t equip_flags;
uint16_t tech_flag; le_uint16_t tech_flag;
uint32_t game_flags; le_uint32_t game_flags;
ItemData data; ItemData data;
PlayerBankItem to_bank_item() const; PlayerBankItem to_bank_item() const;
@@ -43,8 +45,8 @@ struct PlayerInventoryItem {
// an item in a player's bank // an item in a player's bank
struct PlayerBankItem { struct PlayerBankItem {
ItemData data; ItemData data;
uint16_t amount; le_uint16_t amount;
uint16_t show_flags; le_uint16_t show_flags;
PlayerInventoryItem to_inventory_item() const; PlayerInventoryItem to_inventory_item() const;
} __attribute__((packed)); } __attribute__((packed));
@@ -62,8 +64,8 @@ struct PlayerInventory {
// a player's bank // a player's bank
struct PlayerBank { struct PlayerBank {
uint32_t num_items; le_uint32_t num_items;
uint32_t meseta; le_uint32_t meseta;
PlayerBankItem items[200]; PlayerBankItem items[200];
void load(const std::string& filename); void load(const std::string& filename);
@@ -80,13 +82,13 @@ struct PlayerBank {
// simple player stats // simple player stats
struct PlayerStats { struct PlayerStats {
uint16_t atp; le_uint16_t atp;
uint16_t mst; le_uint16_t mst;
uint16_t evp; le_uint16_t evp;
uint16_t hp; le_uint16_t hp;
uint16_t dfp; le_uint16_t dfp;
uint16_t ata; le_uint16_t ata;
uint16_t lck; le_uint16_t lck;
} __attribute__((packed)); } __attribute__((packed));
struct PlayerDispDataBB; struct PlayerDispDataBB;
@@ -94,34 +96,33 @@ struct PlayerDispDataBB;
// PC/GC player appearance and stats data // PC/GC player appearance and stats data
struct PlayerDispDataPCGC { // 0xD0 in size struct PlayerDispDataPCGC { // 0xD0 in size
PlayerStats stats; PlayerStats stats;
uint16_t unknown1; parray<uint8_t, 0x0A> unknown_a1;
uint32_t unknown2[2]; le_uint32_t level;
uint32_t level; le_uint32_t experience;
uint32_t experience; le_uint32_t meseta;
uint32_t meseta; ptext<char, 0x10> name;
char name[16]; uint64_t unknown_a2;
uint32_t unknown3[2]; le_uint32_t name_color;
uint32_t name_color;
uint8_t extra_model; uint8_t extra_model;
uint8_t unused[15]; parray<uint8_t, 0x0F> unused;
uint32_t name_color_checksum; le_uint32_t name_color_checksum;
uint8_t section_id; uint8_t section_id;
uint8_t char_class; uint8_t char_class;
uint8_t v2_flags; uint8_t v2_flags;
uint8_t version; uint8_t version;
uint32_t v1_flags; le_uint32_t v1_flags;
uint16_t costume; le_uint16_t costume;
uint16_t skin; le_uint16_t skin;
uint16_t face; le_uint16_t face;
uint16_t head; le_uint16_t head;
uint16_t hair; le_uint16_t hair;
uint16_t hair_r; le_uint16_t hair_r;
uint16_t hair_g; le_uint16_t hair_g;
uint16_t hair_b; le_uint16_t hair_b;
float proportion_x; le_float proportion_x;
float proportion_y; le_float proportion_y;
uint8_t config[0x48]; parray<uint8_t, 0x48> config;
uint8_t technique_levels[0x14]; parray<uint8_t, 0x14> technique_levels;
void enforce_pc_limits(); void enforce_pc_limits();
PlayerDispDataBB to_bb() const; PlayerDispDataBB to_bb() const;
@@ -129,66 +130,64 @@ struct PlayerDispDataPCGC { // 0xD0 in size
// BB player preview format // BB player preview format
struct PlayerDispDataBBPreview { struct PlayerDispDataBBPreview {
uint32_t experience; le_uint32_t experience;
uint32_t level; le_uint32_t level;
char guild_card[16]; ptext<char, 0x10> guild_card;
uint32_t unknown3[2]; uint64_t unknown_a2;
uint32_t name_color; le_uint32_t name_color;
uint8_t extra_model; uint8_t extra_model;
uint8_t unused[15]; parray<uint8_t, 0x0F> unused;
uint32_t name_color_checksum; le_uint32_t name_color_checksum;
uint8_t section_id; uint8_t section_id;
uint8_t char_class; uint8_t char_class;
uint8_t v2_flags; uint8_t v2_flags;
uint8_t version; uint8_t version;
uint32_t v1_flags; le_uint32_t v1_flags;
uint16_t costume; le_uint16_t costume;
uint16_t skin; le_uint16_t skin;
uint16_t face; le_uint16_t face;
uint16_t head; le_uint16_t head;
uint16_t hair; le_uint16_t hair;
uint16_t hair_r; le_uint16_t hair_r;
uint16_t hair_g; le_uint16_t hair_g;
uint16_t hair_b; le_uint16_t hair_b;
float proportion_x; le_float proportion_x;
float proportion_y; le_float proportion_y;
char16_t name[16]; ptext<char16_t, 0x10> name;
uint32_t play_time; uint32_t play_time;
} __attribute__((packed)); } __attribute__((packed));
// BB player appearance and stats data // BB player appearance and stats data
struct PlayerDispDataBB { struct PlayerDispDataBB {
PlayerStats stats; PlayerStats stats;
uint16_t unknown1; parray<uint8_t, 0x0A> unknown_a1;
uint32_t unknown2[2]; le_uint32_t level;
uint32_t level; le_uint32_t experience;
uint32_t experience; le_uint32_t meseta;
uint32_t meseta; ptext<char, 0x10> guild_card;
char guild_card[0x10]; uint64_t unknown_a2;
uint32_t unknown3[2]; le_uint32_t name_color;
uint32_t name_color;
uint8_t extra_model; uint8_t extra_model;
uint8_t unused[11]; parray<uint8_t, 0x0F> unused;
uint32_t play_time; // not actually a game field; used only by my server le_uint32_t name_color_checksum;
uint32_t name_color_checksum;
uint8_t section_id; uint8_t section_id;
uint8_t char_class; uint8_t char_class;
uint8_t v2_flags; uint8_t v2_flags;
uint8_t version; uint8_t version;
uint32_t v1_flags; le_uint32_t v1_flags;
uint16_t costume; le_uint16_t costume;
uint16_t skin; le_uint16_t skin;
uint16_t face; le_uint16_t face;
uint16_t head; le_uint16_t head;
uint16_t hair; le_uint16_t hair;
uint16_t hair_r; le_uint16_t hair_r;
uint16_t hair_g; le_uint16_t hair_g;
uint16_t hair_b; le_uint16_t hair_b;
float proportion_x; le_float proportion_x;
float proportion_y; le_float proportion_y;
char16_t name[0x10]; ptext<char16_t, 0x10> name;
uint8_t config[0xE8]; parray<uint8_t, 0xE8> config;
uint8_t technique_levels[0x14]; parray<uint8_t, 0x14> technique_levels;
inline void enforce_pc_limits() { } inline void enforce_pc_limits() { }
PlayerDispDataPCGC to_pcgc() const; PlayerDispDataPCGC to_pcgc() const;
@@ -199,10 +198,10 @@ struct PlayerDispDataBB {
struct GuildCardGC { struct GuildCardGC {
uint32_t player_tag; le_uint32_t player_tag;
uint32_t serial_number; le_uint32_t serial_number;
char name[0x18]; ptext<char, 0x18> name;
char desc[0x6C]; ptext<char, 0x6C> desc;
uint8_t reserved1; // should be 1 uint8_t reserved1; // should be 1
uint8_t reserved2; // should be 1 uint8_t reserved2; // should be 1
uint8_t section_id; uint8_t section_id;
@@ -211,10 +210,10 @@ struct GuildCardGC {
// BB guild card format // BB guild card format
struct GuildCardBB { struct GuildCardBB {
uint32_t serial_number; le_uint32_t serial_number;
char16_t name[0x18]; ptext<char16_t, 0x18> name;
char16_t teamname[0x10]; ptext<char16_t, 0x10> teamname;
char16_t desc[0x58]; ptext<char16_t, 0x58> desc;
uint8_t reserved1; // should be 1 uint8_t reserved1; // should be 1
uint8_t reserved2; // should be 1 uint8_t reserved2; // should be 1
uint8_t section_id; uint8_t section_id;
@@ -224,66 +223,66 @@ 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;
uint8_t unknown[0xB4]; parray<uint8_t, 0xB4> unknown;
} __attribute__((packed)); } __attribute__((packed));
// the format of the BB guild card file // the format of the BB guild card file
struct GuildCardFileBB { struct GuildCardFileBB {
uint8_t unknown[0x1F84]; parray<uint8_t, 0x1F84> unknown_a1;
GuildCardEntryBB entry[0x0068]; // that's 104 of them in decimal GuildCardEntryBB entry[0x0068]; // that's 104 of them in decimal
uint8_t unknown2[0x01AC]; parray<uint8_t, 0x01AC> unknown_a2;
} __attribute__((packed)); } __attribute__((packed));
// PSOBB key config and team info // PSOBB key config and team info
struct KeyAndTeamConfigBB { struct KeyAndTeamConfigBB {
uint8_t unknown[0x0114]; // 0000 parray<uint8_t, 0x0114> unknown_a1; // 0000
uint8_t key_config[0x016C]; // 0114 parray<uint8_t, 0x016C> key_config; // 0114
uint8_t joystick_config[0x0038]; // 0280 parray<uint8_t, 0x0038> joystick_config; // 0280
uint32_t serial_number; // 02B8 le_uint32_t serial_number; // 02B8
uint32_t team_id; // 02BC le_uint32_t team_id; // 02BC
uint32_t team_info[2]; // 02C0 le_uint64_t team_info; // 02C0
uint16_t team_privilege_level; // 02C8 le_uint16_t team_privilege_level; // 02C8
uint16_t reserved; // 02CA le_uint16_t reserved; // 02CA
char16_t team_name[0x0010]; // 02CC ptext<char16_t, 0x0010> team_name; // 02CC
uint8_t team_flag[0x0800]; // 02EC parray<uint8_t, 0x0800> team_flag; // 02EC
uint32_t team_rewards[2]; // 0AEC le_uint64_t team_rewards; // 0AEC
} __attribute__((packed)); } __attribute__((packed));
// BB account data // BB account data
struct PlayerAccountDataBB { struct PlayerAccountDataBB {
uint8_t symbol_chats[0x04E0]; parray<uint8_t, 0x04E0> symbol_chats;
KeyAndTeamConfigBB key_config; KeyAndTeamConfigBB key_config;
GuildCardFileBB guild_cards; GuildCardFileBB guild_cards;
uint32_t options; le_uint32_t options;
uint8_t shortcuts[0x0A40]; // chat shortcuts (@1FB4 in E7 command) parray<uint8_t, 0x0A40> shortcuts; // chat shortcuts (@1FB4 in E7 command)
} __attribute__((packed)); } __attribute__((packed));
struct PlayerLobbyDataPC { struct PlayerLobbyDataPC {
uint32_t player_tag; le_uint32_t player_tag;
uint32_t guild_card; le_uint32_t guild_card;
be_uint32_t ip_address; be_uint32_t ip_address;
uint32_t client_id; le_uint32_t client_id;
char16_t name[16]; ptext<char16_t, 0x10> name;
} __attribute__((packed)); } __attribute__((packed));
struct PlayerLobbyDataGC { struct PlayerLobbyDataGC {
uint32_t player_tag; le_uint32_t player_tag;
uint32_t guild_card; le_uint32_t guild_card;
be_uint32_t ip_address; be_uint32_t ip_address;
uint32_t client_id; le_uint32_t client_id;
char name[16]; ptext<char, 0x10> name;
} __attribute__((packed)); } __attribute__((packed));
struct PlayerLobbyDataBB { struct PlayerLobbyDataBB {
uint32_t player_tag; le_uint32_t player_tag;
uint32_t guild_card; le_uint32_t guild_card;
be_uint32_t ip_address; // Guess - the official builds didn't use this, but all other versions have it be_uint32_t ip_address; // Guess - the official builds didn't use this, but all other versions have it
uint32_t unknown1[4]; parray<uint8_t, 0x10> unknown_a1;
uint32_t client_id; le_uint32_t client_id;
char16_t name[16]; ptext<char16_t, 0x10> name;
uint32_t unknown2; le_uint32_t unknown2;
} __attribute__((packed)); } __attribute__((packed));
@@ -296,107 +295,105 @@ struct PSOPlayerDataPC { // for command 0x61
struct PSOPlayerDataGC { // for command 0x61 struct PSOPlayerDataGC { // for command 0x61
PlayerInventory inventory; PlayerInventory inventory;
PlayerDispDataPCGC disp; PlayerDispDataPCGC disp;
char unknown[0x134]; parray<uint8_t, 0x134> unknown;
char info_board[0xAC]; ptext<char, 0xAC> info_board;
uint32_t blocked[0x1E]; parray<le_uint32_t, 0x1E> blocked_senders;
uint32_t auto_reply_enabled; le_uint32_t auto_reply_enabled;
char auto_reply[0]; char auto_reply[0];
} __attribute__((packed)); } __attribute__((packed));
struct PSOPlayerDataBB { // for command 0x61 struct PSOPlayerDataBB { // for command 0x61
PlayerInventory inventory; PlayerInventory inventory;
PlayerDispDataBB disp; PlayerDispDataBB disp;
char unused[0x174]; ptext<char, 0x174> unused;
char16_t info_board[0xAC]; ptext<char16_t, 0xAC> info_board;
uint32_t blocked[0x1E]; parray<le_uint32_t, 0x1E> blocked_senders;
uint32_t auto_reply_enabled; le_uint32_t auto_reply_enabled;
char16_t auto_reply[0]; char16_t auto_reply[0];
} __attribute__((packed)); } __attribute__((packed));
// complete BB player data format (used in E7 command) // complete BB player data format (used in E7 command)
struct PlayerBB { struct PlayerBB {
PlayerInventory inventory; // 0000 // player PlayerInventory inventory; // 0000 // player
PlayerDispDataBB disp; // 034C // player PlayerDispDataBB disp; // 034C // player
uint8_t unknown[0x0010]; // 04DC // parray<uint8_t, 0x0010> unknown; // 04DC //
uint32_t option_flags; // 04EC // account le_uint32_t option_flags; // 04EC // account
uint8_t quest_data1[0x0208]; // 04F0 // player parray<uint8_t, 0x0208> quest_data1; // 04F0 // player
PlayerBank bank; // 06F8 // player PlayerBank bank; // 06F8 // player
uint32_t serial_number; // 19C0 // player le_uint32_t serial_number; // 19C0 // player
char16_t name[0x18]; // 19C4 // player ptext<char16_t, 0x18> name; // 19C4 // player
char16_t team_name[0x10]; // 19C4 // player ptext<char16_t, 0x10> team_name; // 19C4 // player
char16_t guild_card_desc[0x58]; // 1A14 // player ptext<char16_t, 0x58> guild_card_desc; // 1A14 // player
uint8_t reserved1; // 1AC4 // player uint8_t reserved1; // 1AC4 // player
uint8_t reserved2; // 1AC5 // player uint8_t reserved2; // 1AC5 // player
uint8_t section_id; // 1AC6 // player uint8_t section_id; // 1AC6 // player
uint8_t char_class; // 1AC7 // player uint8_t char_class; // 1AC7 // player
uint32_t unknown3; // 1AC8 // le_uint32_t unknown3; // 1AC8 //
uint8_t symbol_chats[0x04E0]; // 1ACC // account parray<uint8_t, 0x04E0> symbol_chats; // 1ACC // account
uint8_t shortcuts[0x0A40]; // 1FAC // account parray<uint8_t, 0x0A40> shortcuts; // 1FAC // account
char16_t auto_reply[0x00AC]; // 29EC // player ptext<char16_t, 0x00AC> auto_reply; // 29EC // player
char16_t info_board[0x00AC]; // 2B44 // player ptext<char16_t, 0x00AC> info_board; // 2B44 // player
uint8_t unknown5[0x001C]; // 2C9C // parray<uint8_t, 0x001C> unknown5; // 2C9C //
uint8_t challenge_data[0x0140]; // 2CB8 // player parray<uint8_t, 0x0140> challenge_data; // 2CB8 // player
uint8_t tech_menu_config[0x0028]; // 2DF8 // player parray<uint8_t, 0x0028> tech_menu_config; // 2DF8 // player
uint8_t unknown6[0x002C]; // 2E20 // parray<uint8_t, 0x002C> unknown6; // 2E20 //
uint8_t quest_data2[0x0058]; // 2E4C // player parray<uint8_t, 0x0058> quest_data2; // 2E4C // player
KeyAndTeamConfigBB key_config; // 2EA4 // account KeyAndTeamConfigBB key_config; // 2EA4 // account
} __attribute__((packed)); // total size: 39A0 } __attribute__((packed)); // total size: 39A0
struct SavedPlayerBB { // .nsc file format struct SavedPlayerBB { // .nsc file format
char signature[0x40]; ptext<char, 0x40> signature;
PlayerDispDataBBPreview preview; PlayerDispDataBBPreview preview;
ptext<char16_t, 0x00AC> auto_reply;
char16_t auto_reply[0x00AC]; PlayerBank bank;
PlayerBank bank; parray<uint8_t, 0x0140> challenge_data;
uint8_t challenge_data[0x0140]; PlayerDispDataBB disp;
PlayerDispDataBB disp; ptext<char16_t, 0x58> guild_card_desc;
char16_t guild_card_desc[0x58]; ptext<char16_t, 0x00AC> info_board;
char16_t info_board[0x00AC]; PlayerInventory inventory;
PlayerInventory inventory; parray<uint8_t, 0x0208> quest_data1;
uint8_t quest_data1[0x0208]; parray<uint8_t, 0x0058> quest_data2;
uint8_t quest_data2[0x0058]; parray<uint8_t, 0x0028> tech_menu_config;
uint8_t tech_menu_config[0x0028];
} __attribute__((packed)); } __attribute__((packed));
struct SavedAccountBB { // .nsa file format struct SavedAccountBB { // .nsa file format
char signature[0x40]; ptext<char, 0x40> signature;
uint32_t blocked[0x001E]; parray<le_uint32_t, 0x001E> blocked_senders;
GuildCardFileBB guild_cards; GuildCardFileBB guild_cards;
KeyAndTeamConfigBB key_config; KeyAndTeamConfigBB key_config;
uint32_t option_flags; le_uint32_t option_flags;
uint8_t shortcuts[0x0A40]; parray<uint8_t, 0x0A40> shortcuts;
uint8_t symbol_chats[0x04E0]; parray<uint8_t, 0x04E0> symbol_chats;
char16_t team_name[0x0010]; ptext<char16_t, 0x0010> team_name;
} __attribute__((packed)); } __attribute__((packed));
// complete player info stored by the server // complete player info stored by the server
struct Player { struct Player {
uint32_t loaded_from_shipgate_time; le_uint32_t loaded_from_shipgate_time;
ptext<char16_t, 0x00AC> auto_reply; // player
char16_t auto_reply[0x00AC]; // player PlayerBank bank; // player
PlayerBank bank; // player ptext<char, 0x0020> bank_name; // not saved
char bank_name[0x20]; parray<le_uint32_t, 0x001E> blocked_senders; // account
uint32_t blocked[0x001E]; // account parray<uint8_t, 0x0140> challenge_data; // player
uint8_t challenge_data[0x0140]; // player PlayerDispDataBB disp; // player
PlayerDispDataBB disp; // player parray<uint8_t, 0x2408> ep3_config; // not saved
uint8_t ep3_config[0x2408]; ptext<char16_t, 0x0058> guild_card_desc; // player
char16_t guild_card_desc[0x58]; // player GuildCardFileBB guild_cards; // account
GuildCardFileBB guild_cards; // account PlayerInventoryItem identify_result; // not saved
PlayerInventoryItem identify_result; ptext<char16_t, 0x00AC> info_board; // player
char16_t info_board[0x00AC]; // player PlayerInventory inventory; // player
PlayerInventory inventory; // player KeyAndTeamConfigBB key_config; // account
KeyAndTeamConfigBB key_config; // account le_uint32_t option_flags; // account
uint32_t option_flags; // account parray<uint8_t, 0x0208> quest_data1; // player
uint8_t quest_data1[0x0208]; // player parray<uint8_t, 0x0058> quest_data2; // player
uint8_t quest_data2[0x0058]; // player le_uint32_t serial_number; // account identifier
uint32_t serial_number; std::vector<ItemData> current_shop_contents; // not saved
std::vector<ItemData> current_shop_contents; parray<uint8_t, 0x0A40> shortcuts; // account
uint8_t shortcuts[0x0A40]; // account parray<uint8_t, 0x04E0> symbol_chats; // account
uint8_t symbol_chats[0x04E0]; // account ptext<char16_t, 0x0010> team_name; // account
char16_t team_name[0x0010]; // account parray<uint8_t, 0x0028> tech_menu_config; // player
uint8_t tech_menu_config[0x0028]; // player
void load_player_data(const std::string& filename); void load_player_data(const std::string& filename);
void save_player_data(const std::string& filename) const; void save_player_data(const std::string& filename) const;
@@ -419,7 +416,7 @@ struct Player {
uint32_t compute_guild_card_checksum(const void* data, size_t size); 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_player_bb(const std::string& username, uint8_t player_index);
std::string filename_for_bank_bb(const std::string& username, const char* bank_name); 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_class_template_bb(uint8_t char_class);
std::string filename_for_account_bb(const std::string& username); std::string filename_for_account_bb(const std::string& username);
+23 -25
View File
@@ -198,14 +198,14 @@ void ProxyServer::UnlinkedSession::on_client_input() {
should_close_unlinked_session = true; should_close_unlinked_session = true;
} else { } else {
const auto* cmd = reinterpret_cast<const C_Login_PC_GC_9D_9E*>(data.data()); const auto* cmd = reinterpret_cast<const C_Login_PC_GC_9D_9E*>(data.data());
uint32_t serial_number = strtoul(cmd->serial_number, nullptr, 16); uint32_t serial_number = strtoul(cmd->serial_number.c_str(), nullptr, 16);
try { try {
license = this->server->state->license_manager->verify_gc( license = this->server->state->license_manager->verify_gc(
serial_number, cmd->access_key, nullptr); serial_number, cmd->access_key.c_str(), nullptr);
sub_version = cmd->sub_version; sub_version = cmd->sub_version;
character_name = cmd->name; character_name = cmd->name;
memcpy(&client_config, &cmd->cfg, offsetof(ClientConfig, unused_bb_only)); memcpy(&client_config, &cmd->cfg, offsetof(ClientConfig, unused_bb_only));
memset(client_config.unused_bb_only, 0xFF, sizeof(client_config.unused_bb_only)); client_config.unused_bb_only.clear(0xFF);
} catch (const exception& e) { } catch (const exception& e) {
log(ERROR, "[ProxyServer] Unlinked client has no valid license"); log(ERROR, "[ProxyServer] Unlinked client has no valid license");
should_close_unlinked_session = true; should_close_unlinked_session = true;
@@ -306,7 +306,6 @@ ProxyServer::LinkedSession::LinkedSession(
enable_chat_filter(true), enable_chat_filter(true),
lobby_players(12), lobby_players(12),
lobby_client_id(0) { lobby_client_id(0) {
memset(this->remote_client_config_data, 0, 0x20);
memset(&this->next_destination, 0, sizeof(this->next_destination)); memset(&this->next_destination, 0, sizeof(this->next_destination));
struct sockaddr_in* dest_sin = reinterpret_cast<struct sockaddr_in*>(&this->next_destination); struct sockaddr_in* dest_sin = reinterpret_cast<struct sockaddr_in*>(&this->next_destination);
dest_sin->sin_family = AF_INET; dest_sin->sin_family = AF_INET;
@@ -625,14 +624,13 @@ void ProxyServer::LinkedSession::on_server_input() {
if (command == 0x17) { if (command == 0x17) {
C_VerifyLicense_GC_DB cmd; C_VerifyLicense_GC_DB cmd;
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
snprintf(cmd.serial_number, sizeof(cmd.serial_number), "%08" PRIX32 "", cmd.serial_number = string_printf("%08" PRIX32 "",
this->license->serial_number); this->license->serial_number);
memcpy(cmd.access_key, this->license->access_key, 0x10); cmd.access_key = this->license->access_key;
cmd.sub_version = this->sub_version; cmd.sub_version = this->sub_version;
snprintf(cmd.serial_number2, sizeof(cmd.serial_number2), "%08" PRIX32 "", cmd.serial_number2 = cmd.serial_number;
this->license->serial_number); cmd.access_key2 = cmd.access_key;
memcpy(cmd.access_key2, this->license->access_key, 0x10); cmd.password = this->license->gc_password;
memcpy(cmd.password, this->license->gc_password, 0x0C);
send_command(this->server_bev.get(), this->version, send_command(this->server_bev.get(), this->version,
this->server_output_crypt.get(), 0xDB, 0, &cmd, sizeof(cmd), this->server_output_crypt.get(), 0xDB, 0, &cmd, sizeof(cmd),
name.c_str()); name.c_str());
@@ -656,15 +654,14 @@ void ProxyServer::LinkedSession::on_server_input() {
cmd.guild_card_number = this->guild_card_number; cmd.guild_card_number = this->guild_card_number;
} }
cmd.sub_version = this->sub_version; cmd.sub_version = this->sub_version;
cmd.unused2[1] = 1; cmd.unused2.data()[1] = 1;
snprintf(cmd.serial_number, sizeof(cmd.serial_number), "%08" PRIX32 "", cmd.serial_number = string_printf("%08" PRIX32 "",
this->license->serial_number); this->license->serial_number);
memcpy(cmd.access_key, this->license->access_key, 0x10); cmd.access_key = this->license->access_key;
snprintf(cmd.serial_number2, sizeof(cmd.serial_number2), "%08" PRIX32 "", cmd.serial_number2 = cmd.serial_number;
this->license->serial_number); cmd.access_key2 = cmd.access_key;
memcpy(cmd.access_key2, this->license->access_key, 0x10); cmd.name = this->character_name;
strncpy(cmd.name, this->character_name.c_str(), sizeof(cmd.name) - 1); memcpy(&cmd.cfg, this->remote_client_config_data.data(), 0x20);
memcpy(&cmd.cfg, this->remote_client_config_data, 0x20);
// If there's a guild card number, a shorter 9E is sent that ends // If there's a guild card number, a shorter 9E is sent that ends
// right after the client config data // right after the client config data
@@ -701,14 +698,14 @@ void ProxyServer::LinkedSession::on_server_input() {
// server init command). We simulate that bug here. // server init command). We simulate that bug here.
// If there was previously a guild card number, assume we got the // If there was previously a guild card number, assume we got the
// lobby server init text instead of the port map init text. // lobby server init text instead of the port map init text.
memcpy( memcpy(this->remote_client_config_data.data(),
this->remote_client_config_data,
had_guild_card_number had_guild_card_number
? "t Lobby Server. Copyright SEGA E" ? "t Lobby Server. Copyright SEGA E"
: "t Port Map. Copyright SEGA Enter", : "t Port Map. Copyright SEGA Enter",
0x20); this->remote_client_config_data.bytes());
memcpy(this->remote_client_config_data, &cmd->cfg, memcpy(this->remote_client_config_data.data(), &cmd->cfg,
min<size_t>(data.size() - sizeof(S_UpdateClientConfig_DC_PC_GC_04), 0x20)); min<size_t>(data.size() - sizeof(S_UpdateClientConfig_DC_PC_GC_04),
this->remote_client_config_data.bytes()));
// If the guild card number was not set, pretend (to the server) // If the guild card number was not set, pretend (to the server)
// that this is the first 04 command the client has received. The // that this is the first 04 command the client has received. The
@@ -798,7 +795,8 @@ void ProxyServer::LinkedSession::on_server_input() {
const auto* cmd = reinterpret_cast<const S_OpenFile_PC_GC_44_A6*>(data.data()); const auto* cmd = reinterpret_cast<const S_OpenFile_PC_GC_44_A6*>(data.data());
string output_filename = string_printf("%s.%s.%" PRIu64, string output_filename = string_printf("%s.%s.%" PRIu64,
cmd->filename, is_download_quest ? "download" : "online", now()); cmd->filename.c_str(),
is_download_quest ? "download" : "online", now());
for (size_t x = 0; x < output_filename.size(); x++) { for (size_t x = 0; x < output_filename.size(); x++) {
if (output_filename[x] < 0x20 || output_filename[x] > 0x7E || output_filename[x] == '/') { if (output_filename[x] < 0x20 || output_filename[x] > 0x7E || output_filename[x] == '/') {
output_filename[x] = '_'; output_filename[x] = '_';
@@ -833,7 +831,7 @@ void ProxyServer::LinkedSession::on_server_input() {
sf = &this->saving_files.at(cmd->filename); sf = &this->saving_files.at(cmd->filename);
} catch (const out_of_range&) { } catch (const out_of_range&) {
log(WARNING, "[ProxyServer/%08" PRIX32 "] Received data for non-open file %s", log(WARNING, "[ProxyServer/%08" PRIX32 "] Received data for non-open file %s",
this->license->serial_number, cmd->filename); this->license->serial_number, cmd->filename.c_str());
break; break;
} }
+1 -1
View File
@@ -46,7 +46,7 @@ public:
std::string character_name; std::string character_name;
uint32_t guild_card_number; uint32_t guild_card_number;
uint8_t remote_client_config_data[0x20]; parray<uint8_t, 0x20> remote_client_config_data;
ClientConfig newserv_client_config; ClientConfig newserv_client_config;
bool suppress_newserv_commands; bool suppress_newserv_commands;
bool enable_chat_filter; bool enable_chat_filter;
+18 -18
View File
@@ -77,9 +77,9 @@ struct PSOQuestHeaderDC { // same for dc v1 and v2, thankfully
uint8_t is_download; uint8_t is_download;
uint8_t unknown1; uint8_t unknown1;
uint16_t quest_number; // 0xFFFF for challenge quests uint16_t quest_number; // 0xFFFF for challenge quests
char name[0x20]; ptext<char, 0x20> name;
char short_description[0x80]; ptext<char, 0x80> short_description;
char long_description[0x120]; ptext<char, 0x120> long_description;
} __attribute__((packed)); } __attribute__((packed));
struct PSOQuestHeaderPC { struct PSOQuestHeaderPC {
@@ -90,9 +90,9 @@ struct PSOQuestHeaderPC {
uint8_t is_download; uint8_t is_download;
uint8_t unknown1; uint8_t unknown1;
uint16_t quest_number; // 0xFFFF for challenge quests uint16_t quest_number; // 0xFFFF for challenge quests
char16_t name[0x20]; ptext<char16_t, 0x20> name;
char16_t short_description[0x80]; ptext<char16_t, 0x80> short_description;
char16_t long_description[0x120]; ptext<char16_t, 0x120> long_description;
} __attribute__((packed)); } __attribute__((packed));
struct PSOQuestHeaderGC { struct PSOQuestHeaderGC {
@@ -104,21 +104,21 @@ struct PSOQuestHeaderGC {
uint8_t unknown1; uint8_t unknown1;
uint8_t quest_number; uint8_t quest_number;
uint8_t episode; // 1 = ep2. apparently some quests have 0xFF here, which means ep1 (?) uint8_t episode; // 1 = ep2. apparently some quests have 0xFF here, which means ep1 (?)
char name[0x20]; ptext<char, 0x20> name;
char short_description[0x80]; ptext<char, 0x80> short_description;
char long_description[0x120]; ptext<char, 0x120> long_description;
} __attribute__((packed)); } __attribute__((packed));
struct PSOQuestHeaderGCEpisode3 { struct PSOQuestHeaderGCEpisode3 {
// there's actually a lot of other important stuff in here but I'm lazy. it // there's actually a lot of other important stuff in here but I'm lazy. it
// looks like map data, cutscene data, and maybe special cards used during // looks like map data, cutscene data, and maybe special cards used during
// the quest // the quest
uint8_t unused[0x1DF0]; parray<uint8_t, 0x1DF0> unknown_a1;
char name[0x14]; ptext<char, 0x14> name;
char location[0x14]; ptext<char, 0x14> location;
char location2[0x3C]; ptext<char, 0x3C> location2;
char description[0x190]; ptext<char, 0x190> description;
uint8_t unused2[0x3A34]; parray<uint8_t, 0x3A34> unknown_a2;
} __attribute__((packed)); } __attribute__((packed));
struct PSOQuestHeaderBB { struct PSOQuestHeaderBB {
@@ -132,9 +132,9 @@ struct PSOQuestHeaderBB {
uint8_t max_players; uint8_t max_players;
uint8_t joinable_in_progress; uint8_t joinable_in_progress;
uint8_t unknown; uint8_t unknown;
char16_t name[0x20]; ptext<char16_t, 0x20> name;
char16_t short_description[0x80]; ptext<char16_t, 0x80> short_description;
char16_t long_description[0x120]; ptext<char16_t, 0x120> long_description;
} __attribute__((packed)); } __attribute__((packed));
+81 -80
View File
@@ -159,7 +159,7 @@ void process_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
c->player.load_account_data("system/blueburst/default.nsa"); c->player.load_account_data("system/blueburst/default.nsa");
} }
sprintf(c->player.bank_name, "player%d", c->bb_player_index + 1); c->player.bank_name = string_printf("player%d", c->bb_player_index + 1);
string player_filename = filename_for_player_bb(c->license->username, string player_filename = filename_for_player_bb(c->license->username,
c->bb_player_index); c->bb_player_index);
@@ -198,7 +198,10 @@ void process_disconnect(shared_ptr<ServerState> s, shared_ptr<Client> c) {
} }
if (c->version == GameVersion::BB) { if (c->version == GameVersion::BB) {
c->player.disp.play_time += ((now() - c->play_time_begin) / 1000000); // TODO: Make a timer event for each connected player that saves their data
// periodically, not only when they disconnect
// TODO: Track play time somewhere
// c->player.disp.play_time += ((now() - c->play_time_begin) / 1000000);
string account_filename = filename_for_account_bb(c->license->username); string account_filename = filename_for_account_bb(c->license->username);
string player_filename = filename_for_player_bb(c->license->username, string player_filename = filename_for_player_bb(c->license->username,
c->bb_player_index); c->bb_player_index);
@@ -218,10 +221,10 @@ void process_verify_license_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // DB uint16_t, uint32_t, const string& data) { // DB
const auto& cmd = check_size_t<C_VerifyLicense_GC_DB>(data); const auto& cmd = check_size_t<C_VerifyLicense_GC_DB>(data);
uint32_t serial_number = strtoul(cmd.serial_number, nullptr, 16); uint32_t serial_number = strtoul(cmd.serial_number.c_str(), nullptr, 16);
try { try {
c->license = s->license_manager->verify_gc(serial_number, cmd.access_key, c->license = s->license_manager->verify_gc(serial_number,
cmd.password); cmd.access_key.c_str(), cmd.password.c_str());
} catch (const exception& e) { } catch (const exception& e) {
if (!s->allow_unregistered_users) { if (!s->allow_unregistered_users) {
u16string message = u"Login failed: " + decode_sjis(e.what()); u16string message = u"Login failed: " + decode_sjis(e.what());
@@ -230,7 +233,7 @@ void process_verify_license_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
return; return;
} else { } else {
auto l = LicenseManager::create_license_gc(serial_number, auto l = LicenseManager::create_license_gc(serial_number,
cmd.access_key, cmd.password, true); cmd.access_key.c_str(), cmd.password.c_str(), true);
s->license_manager->add(l); s->license_manager->add(l);
c->license = l; c->license = l;
} }
@@ -244,14 +247,14 @@ void process_login_a_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // 9A uint16_t, uint32_t, const string& data) { // 9A
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9A>(data); const auto& cmd = check_size_t<C_Login_DC_PC_GC_9A>(data);
uint32_t serial_number = strtoul(cmd.serial_number, nullptr, 16); uint32_t serial_number = strtoul(cmd.serial_number.c_str(), nullptr, 16);
try { try {
if (c->version == GameVersion::GC) { if (c->version == GameVersion::GC) {
c->license = s->license_manager->verify_gc(serial_number, cmd.access_key, c->license = s->license_manager->verify_gc(serial_number,
nullptr); cmd.access_key.c_str(), nullptr);
} else { } else {
c->license = s->license_manager->verify_pc(serial_number, cmd.access_key, c->license = s->license_manager->verify_pc(serial_number,
nullptr); cmd.access_key.c_str(), nullptr);
} }
} catch (const exception& e) { } catch (const exception& e) {
// The client should have sent a different command containing the password // The client should have sent a different command containing the password
@@ -273,14 +276,14 @@ void process_login_c_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
c->flags |= flags_for_version(c->version, cmd.sub_version); c->flags |= flags_for_version(c->version, cmd.sub_version);
uint32_t serial_number = strtoul(cmd.serial_number, nullptr, 16); uint32_t serial_number = strtoul(cmd.serial_number.c_str(), nullptr, 16);
try { try {
if (c->version == GameVersion::GC) { if (c->version == GameVersion::GC) {
c->license = s->license_manager->verify_gc(serial_number, cmd.access_key, c->license = s->license_manager->verify_gc(serial_number,
cmd.password); cmd.access_key.c_str(), cmd.password.c_str());
} else { } else {
c->license = s->license_manager->verify_pc(serial_number, cmd.access_key, c->license = s->license_manager->verify_pc(serial_number,
cmd.password); cmd.access_key.c_str(), cmd.password.c_str());
} }
} catch (const exception& e) { } catch (const exception& e) {
if (!s->allow_unregistered_users) { if (!s->allow_unregistered_users) {
@@ -292,10 +295,10 @@ void process_login_c_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
shared_ptr<License> l; shared_ptr<License> l;
if (c->version == GameVersion::GC) { if (c->version == GameVersion::GC) {
l = LicenseManager::create_license_gc(serial_number, l = LicenseManager::create_license_gc(serial_number,
cmd.access_key, cmd.password, true); cmd.access_key.c_str(), cmd.password.c_str(), true);
} else { } else {
l = LicenseManager::create_license_pc(serial_number, l = LicenseManager::create_license_pc(serial_number,
cmd.access_key, cmd.password, true); cmd.access_key.c_str(), cmd.password.c_str(), true);
} }
s->license_manager->add(l); s->license_manager->add(l);
c->license = l; c->license = l;
@@ -313,14 +316,14 @@ void process_login_d_e_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
c->flags |= flags_for_version(c->version, cmd.sub_version); c->flags |= flags_for_version(c->version, cmd.sub_version);
uint32_t serial_number = strtoul(cmd.serial_number, nullptr, 16); uint32_t serial_number = strtoul(cmd.serial_number.c_str(), nullptr, 16);
try { try {
if (c->version == GameVersion::GC) { if (c->version == GameVersion::GC) {
c->license = s->license_manager->verify_gc(serial_number, cmd.access_key, c->license = s->license_manager->verify_gc(serial_number,
nullptr); cmd.access_key.c_str(), nullptr);
} else { } else {
c->license = s->license_manager->verify_pc(serial_number, cmd.access_key, c->license = s->license_manager->verify_pc(serial_number,
nullptr); cmd.access_key.c_str(), nullptr);
} }
} catch (const exception& e) { } catch (const exception& e) {
// See comment in 9A handler about why we do this even if unregistered users // See comment in 9A handler about why we do this even if unregistered users
@@ -357,7 +360,8 @@ void process_login_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
c->flags |= flags_for_version(c->version, 0); c->flags |= flags_for_version(c->version, 0);
try { try {
c->license = s->license_manager->verify_bb(cmd.username, cmd.password); c->license = s->license_manager->verify_bb(
cmd.username.c_str(), cmd.password.c_str());
} catch (const exception& e) { } catch (const exception& e) {
u16string message = u"Login failed: " + decode_sjis(e.what()); u16string message = u"Login failed: " + decode_sjis(e.what());
send_message_box(c, message.c_str()); send_message_box(c, message.c_str());
@@ -770,21 +774,18 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
} }
if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES)) { if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES)) {
char16_t password[0x10]; ptext<char16_t, 0x10> password;
memset(password, 0, sizeof(password));
if (data.size() > sizeof(C_MenuSelection)) { if (data.size() > sizeof(C_MenuSelection)) {
if (uses_unicode) { if (uses_unicode) {
size_t max_chars = (data.size() - sizeof(C_MenuSelection)) / sizeof(char16_t); size_t max_chars = (data.size() - sizeof(C_MenuSelection)) / sizeof(char16_t);
strcpy_z(password, cmd.password.pcbb, password.assign(cmd.password.pcbb, max_chars);
min<size_t>(max_chars, countof(password)));
} else { } else {
size_t max_chars = (data.size() - sizeof(C_MenuSelection)) / sizeof(char); size_t max_chars = (data.size() - sizeof(C_MenuSelection)) / sizeof(char);
decode_sjis(password, cmd.password.dcgc, password = decode_sjis(cmd.password.dcgc, max_chars);
min<size_t>(max_chars, countof(password)));
} }
} }
if (game->password[0] && char16ncmp(game->password, password, 0x10)) { if (!game->password.empty() && (game->password != password)) {
send_message_box(c, u"$C6Incorrect password."); send_message_box(c, u"$C6Incorrect password.");
break; break;
} }
@@ -1036,7 +1037,7 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
switch (c->version) { switch (c->version) {
case GameVersion::PC: case GameVersion::PC:
check_size(data.size(), sizeof(PSOPlayerDataPC), check_size(data.size(), sizeof(PSOPlayerDataPC),
sizeof(PSOPlayerDataPC) + sizeof(char16_t) * countof(c->player.auto_reply)); sizeof(PSOPlayerDataPC) + sizeof(char16_t) * c->player.auto_reply.size());
c->player.import(*reinterpret_cast<const PSOPlayerDataPC*>(data.data())); c->player.import(*reinterpret_cast<const PSOPlayerDataPC*>(data.data()));
break; break;
case GameVersion::GC: case GameVersion::GC:
@@ -1045,13 +1046,13 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
// TODO: import Episode 3 data somewhere // TODO: import Episode 3 data somewhere
} else { } else {
check_size(data.size(), sizeof(PSOPlayerDataGC), check_size(data.size(), sizeof(PSOPlayerDataGC),
sizeof(PSOPlayerDataGC) + sizeof(char) * countof(c->player.auto_reply)); sizeof(PSOPlayerDataGC) + sizeof(char) * c->player.auto_reply.size());
} }
c->player.import(*reinterpret_cast<const PSOPlayerDataGC*>(data.data())); c->player.import(*reinterpret_cast<const PSOPlayerDataGC*>(data.data()));
break; break;
case GameVersion::BB: case GameVersion::BB:
check_size(data.size(), sizeof(PSOPlayerDataBB), check_size(data.size(), sizeof(PSOPlayerDataBB),
sizeof(PSOPlayerDataBB) + sizeof(char16_t) * countof(c->player.auto_reply)); sizeof(PSOPlayerDataBB) + sizeof(char16_t) * c->player.auto_reply.size());
c->player.import(*reinterpret_cast<const PSOPlayerDataBB*>(data.data())); c->player.import(*reinterpret_cast<const PSOPlayerDataBB*>(data.data()));
break; break;
default: default:
@@ -1148,7 +1149,7 @@ void process_chat_generic(shared_ptr<ServerState> s, shared_ptr<Client> c,
continue; continue;
} }
send_chat_message(l->clients[x], c->license->serial_number, send_chat_message(l->clients[x], c->license->serial_number,
c->player.disp.name, processed_text.c_str()); c->player.disp.name.data(), processed_text.c_str());
} }
} }
} }
@@ -1157,7 +1158,7 @@ void process_chat_pc_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // 06 uint16_t, uint32_t, const string& data) { // 06
const auto& cmd = check_size_t<C_Chat_06>(data, sizeof(C_Chat_06), 0xFFFF); const auto& cmd = check_size_t<C_Chat_06>(data, sizeof(C_Chat_06), 0xFFFF);
u16string text(cmd.text.pcbb, (data.size() - sizeof(C_Chat_06)) / sizeof(char16_t)); u16string text(cmd.text.pcbb, (data.size() - sizeof(C_Chat_06)) / sizeof(char16_t));
text.resize(char16len(text.c_str())); strip_trailing_zeroes(text);
process_chat_generic(s, c, text); process_chat_generic(s, c, text);
} }
@@ -1248,13 +1249,13 @@ void process_create_character_bb(shared_ptr<ServerState> s, shared_ptr<Client> c
send_message_box(c, u"$C6You are not logged in."); send_message_box(c, u"$C6You are not logged in.");
return; return;
} }
if (c->player.disp.name[0]) { if (!c->player.disp.name.empty()) {
send_message_box(c, u"$C6You have already loaded a character."); send_message_box(c, u"$C6You have already loaded a character.");
return; return;
} }
c->bb_player_index = cmd.player_index; c->bb_player_index = cmd.player_index;
snprintf(c->player.bank_name, 0x20, "player%" PRIu32, cmd.player_index + 1); c->player.bank_name = string_printf("player%" PRIu32, cmd.player_index + 1);
string player_filename = filename_for_player_bb(c->license->username, cmd.player_index); string player_filename = filename_for_player_bb(c->license->username, cmd.player_index);
string bank_filename = filename_for_bank_bb(c->license->username, c->player.bank_name); string bank_filename = filename_for_bank_bb(c->license->username, c->player.bank_name);
string template_filename = filename_for_class_template_bb(cmd.preview.char_class); string template_filename = filename_for_class_template_bb(cmd.preview.char_class);
@@ -1305,27 +1306,27 @@ void process_change_account_data_bb(shared_ptr<ServerState>, shared_ptr<Client>
break; break;
case 0x02ED: case 0x02ED:
check_size(data.size(), sizeof(cmd->symbol_chats)); check_size(data.size(), sizeof(cmd->symbol_chats));
memcpy(c->player.symbol_chats, cmd->symbol_chats, 0x04E0); c->player.symbol_chats = cmd->symbol_chats;
break; break;
case 0x03ED: case 0x03ED:
check_size(data.size(), sizeof(cmd->chat_shortcuts)); check_size(data.size(), sizeof(cmd->chat_shortcuts));
memcpy(c->player.shortcuts, cmd->chat_shortcuts, 0x0A40); c->player.shortcuts = cmd->chat_shortcuts;
break; break;
case 0x04ED: case 0x04ED:
check_size(data.size(), sizeof(cmd->key_config)); check_size(data.size(), sizeof(cmd->key_config));
memcpy(&c->player.key_config.key_config, cmd->key_config, 0x016C); c->player.key_config.key_config = cmd->key_config;
break; break;
case 0x05ED: case 0x05ED:
check_size(data.size(), sizeof(cmd->pad_config)); check_size(data.size(), sizeof(cmd->pad_config));
memcpy(&c->player.key_config.joystick_config, cmd->pad_config, 0x0038); c->player.key_config.joystick_config = cmd->pad_config;
break; break;
case 0x06ED: case 0x06ED:
check_size(data.size(), sizeof(cmd->tech_menu)); check_size(data.size(), sizeof(cmd->tech_menu));
memcpy(&c->player.tech_menu_config, cmd->tech_menu, 0x0028); c->player.tech_menu_config = cmd->tech_menu;
break; break;
case 0x07ED: case 0x07ED:
check_size(data.size(), sizeof(cmd->customize)); check_size(data.size(), sizeof(cmd->customize));
memcpy(c->player.disp.config, cmd->customize, 0xE8); c->player.disp.config = cmd->customize;
break; break;
default: default:
throw invalid_argument("unknown account command"); throw invalid_argument("unknown account command");
@@ -1386,22 +1387,21 @@ void process_simple_mail(shared_ptr<ServerState> s, shared_ptr<Client> c,
// If the sender is blocked, don't forward the mail // If the sender is blocked, don't forward the mail
for (size_t y = 0; y < 30; y++) { for (size_t y = 0; y < 30; y++) {
if (target->player.blocked[y] == c->license->serial_number) { if (target->player.blocked_senders.data()[y] == c->license->serial_number) {
return; return;
} }
} }
// If the target has auto-reply enabled, send the autoreply // If the target has auto-reply enabled, send the autoreply
if (target->player.auto_reply[0]) { if (!target->player.auto_reply.empty()) {
send_simple_mail(c, target->license->serial_number, send_simple_mail(c, target->license->serial_number,
target->player.disp.name, target->player.auto_reply); target->player.disp.name.c_str(), target->player.auto_reply.c_str());
} }
// Forward the message // Forward the message
string message(cmd.text, strnlen(cmd.text, sizeof(cmd.text) / sizeof(cmd.text[0]))); u16string u16message = decode_sjis(cmd.text);
u16string u16message = decode_sjis(message); send_simple_mail(target, c->license->serial_number,
send_simple_mail(target, c->license->serial_number, c->player.disp.name, c->player.disp.name.c_str(), u16message.c_str());
u16message.data());
} }
@@ -1418,33 +1418,31 @@ void process_info_board_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
template <typename CharT> template <typename CharT>
void process_write_info_board_t(shared_ptr<ServerState>, shared_ptr<Client> c, void process_write_info_board_t(shared_ptr<ServerState>, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // D9 uint16_t, uint32_t, const string& data) { // D9
check_size(data.size(), 0, countof(c->player.info_board) * sizeof(CharT)); check_size(data.size(), 0, c->player.info_board.size() * sizeof(CharT));
memset(c->player.info_board, 0, sizeof(c->player.info_board)); c->player.info_board.assign(
strncpy_t(c->player.info_board, reinterpret_cast<const CharT*>(data.data()), reinterpret_cast<const CharT*>(data.data()),
min<size_t>(countof(c->player.info_board), data.size() / sizeof(CharT))); data.size() / sizeof(CharT));
} }
template <typename CharT> template <typename CharT>
void process_set_auto_reply_t(shared_ptr<ServerState>, shared_ptr<Client> c, void process_set_auto_reply_t(shared_ptr<ServerState>, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // C7 uint16_t, uint32_t, const string& data) { // C7
check_size(data.size(), 0, countof(c->player.auto_reply) * sizeof(CharT)); check_size(data.size(), 0, c->player.auto_reply.size() * sizeof(CharT));
memset(c->player.auto_reply, 0, sizeof(c->player.auto_reply)); c->player.auto_reply.assign(
strncpy_t(c->player.auto_reply, reinterpret_cast<const CharT*>(data.data()), reinterpret_cast<const CharT*>(data.data()),
min<size_t>(countof(c->player.auto_reply), data.size() / sizeof(CharT))); data.size() / sizeof(CharT));
} }
void process_disable_auto_reply(shared_ptr<ServerState>, shared_ptr<Client> c, void process_disable_auto_reply(shared_ptr<ServerState>, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // C8 uint16_t, uint32_t, const string& data) { // C8
check_size(data.size(), 0); check_size(data.size(), 0);
memset(c->player.auto_reply, 0, sizeof(c->player.auto_reply)); c->player.auto_reply.clear();
} }
void process_set_blocked_list(shared_ptr<ServerState>, shared_ptr<Client> c, void process_set_blocked_senders_list(shared_ptr<ServerState>, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // C6 uint16_t, uint32_t, const string& data) { // C6
const auto& cmd = check_size_t<C_SetBlockedSenders_C6>(data); const auto& cmd = check_size_t<C_SetBlockedSenders_C6>(data);
for (size_t x = 0; x < countof(cmd.blocked_senders); x++) { c->player.blocked_senders = cmd.blocked_senders;
c->player.blocked[x] = cmd.blocked_senders[x];
}
} }
@@ -1501,8 +1499,8 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
} }
shared_ptr<Lobby> game(new Lobby()); shared_ptr<Lobby> game(new Lobby());
strcpy_z(game->name, name, countof(game->name)); game->name = name;
strcpy_z(game->password, password, countof(game->name)); game->password = password;
game->version = c->version; game->version = c->version;
game->section_id = c->override_section_id >= 0 game->section_id = c->override_section_id >= 0
? c->override_section_id : c->player.disp.section_id; ? c->override_section_id : c->player.disp.section_id;
@@ -1548,8 +1546,9 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
try { try {
auto filename = string_printf( auto filename = string_printf(
"system/blueburst/map/%c%hhu%zu%" PRIu32 "%" PRIu32 ".dat", "system/blueburst/map/%c%hhu%zu%" PRIu32 "%" PRIu32 ".dat",
*type_char, game->episode, x, game->variations[x * 2], *type_char, game->episode, x,
game->variations[(x * 2) + 1]); game->variations.data()[x * 2].load(),
game->variations.data()[(x * 2) + 1].load());
game->enemies = load_map(filename.c_str(), game->episode, game->enemies = load_map(filename.c_str(), game->episode,
game->difficulty, bp_subtable, false); game->difficulty, bp_subtable, false);
break; break;
@@ -1566,8 +1565,9 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
for (size_t x = 0; x < 0x10; x++) { for (size_t x = 0; x < 0x10; x++) {
auto filename = string_printf( auto filename = string_printf(
"system/blueburst/map/m%hhu%zu%" PRIu32 "%" PRIu32 ".dat", "system/blueburst/map/m%hhu%zu%" PRIu32 "%" PRIu32 ".dat",
game->episode, x, game->variations[x * 2], game->episode, x,
game->variations[(x * 2) + 1]); game->variations.data()[x * 2].load(),
game->variations.data()[(x * 2) + 1].load());
game->enemies = load_map(filename.c_str(), game->episode, game->enemies = load_map(filename.c_str(), game->episode,
game->difficulty, bp_subtable, false); game->difficulty, bp_subtable, false);
} }
@@ -1583,11 +1583,11 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
if (variation_maxes) { if (variation_maxes) {
for (size_t x = 0; x < 0x20; x++) { for (size_t x = 0; x < 0x20; x++) {
game->variations[x] = random_int(0, variation_maxes[x] - 1); game->variations.data()[x] = random_int(0, variation_maxes[x] - 1);
} }
} else { } else {
for (size_t x = 0; x < 0x20; x++) { for (size_t x = 0; x < 0x20; x++) {
game->variations[x] = 0; game->variations.data()[x] = 0;
} }
} }
@@ -1598,7 +1598,7 @@ void process_create_game_pc(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // C1 uint16_t, uint32_t, const string& data) { // C1
const auto& cmd = check_size_t<C_CreateGame_PC_C1>(data); const auto& cmd = check_size_t<C_CreateGame_PC_C1>(data);
auto game = create_game_generic(s, c, cmd.name, cmd.password, 1, auto game = create_game_generic(s, c, cmd.name.c_str(), cmd.password.c_str(), 1,
cmd.difficulty, cmd.battle_mode, cmd.challenge_mode, 0); cmd.difficulty, cmd.battle_mode, cmd.challenge_mode, 0);
s->add_lobby(game); s->add_lobby(game);
@@ -1639,7 +1639,7 @@ void process_create_game_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // C1 uint16_t, uint32_t, const string& data) { // C1
const auto& cmd = check_size_t<C_CreateGame_BB_C1>(data); const auto& cmd = check_size_t<C_CreateGame_BB_C1>(data);
auto game = create_game_generic(s, c, cmd.name, cmd.password, auto game = create_game_generic(s, c, cmd.name.c_str(), cmd.password.c_str(),
cmd.episode, cmd.difficulty, cmd.battle_mode, cmd.challenge_mode, cmd.episode, cmd.difficulty, cmd.battle_mode, cmd.challenge_mode,
cmd.solo_mode); cmd.solo_mode);
@@ -1657,7 +1657,7 @@ void process_lobby_name_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
if (!l) { if (!l) {
throw invalid_argument("client not in any lobby"); throw invalid_argument("client not in any lobby");
} }
send_lobby_name(c, l->name); send_lobby_name(c, l->name.c_str());
} }
void process_client_ready(shared_ptr<ServerState> s, shared_ptr<Client> c, void process_client_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
@@ -1712,7 +1712,8 @@ and operated completely independently.\n\
\n\ \n\
License check: "; License check: ";
try { try {
c->license = s->license_manager->verify_bb(cmd.username, cmd.password); c->license = s->license_manager->verify_bb(
cmd.username.c_str(), cmd.password.c_str());
message += u"OK"; message += u"OK";
} catch (const exception& e) { } catch (const exception& e) {
message += decode_sjis(e.what()); message += decode_sjis(e.what());
@@ -1817,7 +1818,7 @@ static process_command_t dc_handlers[0x100] = {
// C0 // C0
nullptr, process_create_game_dc_gc, nullptr, nullptr, nullptr, process_create_game_dc_gc, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_list, process_set_auto_reply_t<char>, nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char>,
process_disable_auto_reply, nullptr, nullptr, nullptr, process_disable_auto_reply, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
@@ -1900,7 +1901,7 @@ static process_command_t pc_handlers[0x100] = {
// C0 // C0
nullptr, process_create_game_pc, nullptr, nullptr, nullptr, process_create_game_pc, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_list, process_set_auto_reply_t<char16_t>, nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char16_t>,
process_disable_auto_reply, nullptr, nullptr, nullptr, process_disable_auto_reply, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
@@ -1985,7 +1986,7 @@ static process_command_t gc_handlers[0x100] = {
// C0 // C0
process_choice_search, process_create_game_dc_gc, nullptr, nullptr, process_choice_search, process_create_game_dc_gc, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_list, process_set_auto_reply_t<char>, nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char>,
process_disable_auto_reply, process_game_command, process_ep3_server_data_request, process_game_command, process_disable_auto_reply, process_game_command, process_ep3_server_data_request, process_game_command,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
@@ -2072,7 +2073,7 @@ static process_command_t bb_handlers[0x100] = {
// C0 // C0
nullptr, process_create_game_bb, nullptr, nullptr, nullptr, process_create_game_bb, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_list, process_set_auto_reply_t<char16_t>, nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char16_t>,
process_disable_auto_reply, nullptr, nullptr, nullptr, process_disable_auto_reply, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+1 -1
View File
@@ -114,7 +114,7 @@ static void process_subcommand_send_guild_card(shared_ptr<ServerState>,
if (count < 0x25) { if (count < 0x25) {
return; return;
} }
decode_sjis(c->player.guild_card_desc, c->player.guild_card_desc = decode_sjis(
reinterpret_cast<const char*>(&p[9].byte[0]), 0x58); reinterpret_cast<const char*>(&p[9].byte[0]), 0x58);
} }
+69 -115
View File
@@ -156,12 +156,11 @@ S_ServerInit_DC_GC_02_17 prepare_server_init_contents_dc_pc_gc(
uint32_t server_key, uint32_t server_key,
uint32_t client_key) { uint32_t client_key) {
S_ServerInit_DC_GC_02_17 cmd; S_ServerInit_DC_GC_02_17 cmd;
memset(&cmd, 0, sizeof(cmd)); cmd.copyright = initial_connection
? dc_port_map_copyright : dc_lobby_server_copyright;
strcpy(cmd.copyright, initial_connection ? dc_port_map_copyright : dc_lobby_server_copyright);
cmd.server_key = server_key; cmd.server_key = server_key;
cmd.client_key = client_key; cmd.client_key = client_key;
strcpy(cmd.after_message, anti_copyright); cmd.after_message = anti_copyright;
return cmd; return cmd;
} }
@@ -194,17 +193,16 @@ void send_server_init_dc_pc_gc(shared_ptr<Client> c,
void send_server_init_bb(shared_ptr<ServerState> s, shared_ptr<Client> c) { void send_server_init_bb(shared_ptr<ServerState> s, shared_ptr<Client> c) {
S_ServerInit_BB_03 cmd; S_ServerInit_BB_03 cmd;
memset(&cmd, 0, sizeof(cmd)); cmd.copyright = bb_game_server_copyright;
strcpy(cmd.copyright, bb_game_server_copyright); random_data(cmd.server_key.data(), cmd.server_key.bytes());
random_data(cmd.server_key, 0x30); random_data(cmd.client_key.data(), cmd.client_key.bytes());
random_data(cmd.client_key, 0x30); cmd.after_message = anti_copyright;
strcpy(cmd.after_message, anti_copyright);
send_command(c, 0x03, 0x00, cmd); send_command(c, 0x03, 0x00, cmd);
c->crypt_out.reset(new PSOBBEncryption(s->default_key_file, cmd.server_key, c->crypt_out.reset(new PSOBBEncryption(s->default_key_file,
sizeof(cmd.server_key))); cmd.server_key.data(), cmd.server_key.bytes()));
c->crypt_in.reset(new PSOBBEncryption(s->default_key_file, cmd.client_key, c->crypt_in.reset(new PSOBBEncryption(s->default_key_file,
sizeof(cmd.client_key))); cmd.client_key.data(), cmd.client_key.bytes()));
} }
void send_server_init_patch(shared_ptr<Client> c) { void send_server_init_patch(shared_ptr<Client> c) {
@@ -212,8 +210,7 @@ void send_server_init_patch(shared_ptr<Client> c) {
uint32_t client_key = random_object<uint32_t>(); uint32_t client_key = random_object<uint32_t>();
S_ServerInit_Patch_02 cmd; S_ServerInit_Patch_02 cmd;
memset(&cmd, 0, sizeof(cmd)); cmd.copyright = patch_server_copyright;
strcpy(cmd.copyright, patch_server_copyright);
cmd.server_key = server_key; cmd.server_key = server_key;
cmd.client_key = client_key; cmd.client_key = client_key;
send_command(c, 0x02, 0x00, cmd); send_command(c, 0x02, 0x00, cmd);
@@ -246,7 +243,6 @@ void send_server_init(shared_ptr<ServerState> s, shared_ptr<Client> c,
// for non-BB clients, updates the client's guild card and security data // for non-BB clients, updates the client's guild card and security data
void send_update_client_config(shared_ptr<Client> c) { void send_update_client_config(shared_ptr<Client> c) {
S_UpdateClientConfig_DC_PC_GC_04 cmd; S_UpdateClientConfig_DC_PC_GC_04 cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.player_tag = 0x00010000; cmd.player_tag = 0x00010000;
cmd.guild_card_number = c->license->serial_number; cmd.guild_card_number = c->license->serial_number;
cmd.cfg = c->export_config(); cmd.cfg = c->export_config();
@@ -265,7 +261,6 @@ void send_reconnect(shared_ptr<Client> c, uint32_t address, uint16_t port) {
void send_pc_gc_split_reconnect(shared_ptr<Client> c, uint32_t address, void send_pc_gc_split_reconnect(shared_ptr<Client> c, uint32_t address,
uint16_t pc_port, uint16_t gc_port) { uint16_t pc_port, uint16_t gc_port) {
S_ReconnectSplit_19 cmd; S_ReconnectSplit_19 cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.pc_address = address; cmd.pc_address = address;
cmd.pc_port = pc_port; cmd.pc_port = pc_port;
cmd.gc_command = 0x19; cmd.gc_command = 0x19;
@@ -352,7 +347,7 @@ void send_stream_file_bb(shared_ptr<Client> c) {
uint32_t buffer_offset = 0; uint32_t buffer_offset = 0;
for (size_t x = 0; x < entry_count; x++) { for (size_t x = 0; x < entry_count; x++) {
auto filename = string_printf("system/blueburst/%s", entries[x].filename); auto filename = string_printf("system/blueburst/%s", entries[x].filename.c_str());
auto file_data = file_cache.get(filename); auto file_data = file_cache.get(filename);
size_t file_data_remaining = file_data->size(); size_t file_data_remaining = file_data->size();
@@ -366,11 +361,12 @@ void send_stream_file_bb(shared_ptr<Client> c) {
} }
memcpy(&chunk_cmd.data[buffer_offset], memcpy(&chunk_cmd.data[buffer_offset],
file_data->data() + file_data->size() - file_data_remaining, read_size); file_data->data() + file_data->size() - file_data_remaining, read_size);
// TODO: We probably should clear the rest of the buffer on the last chunk
buffer_offset += read_size; buffer_offset += read_size;
file_data_remaining -= read_size; file_data_remaining -= read_size;
if (buffer_offset == 0x6800) { if (buffer_offset == 0x6800) {
// note: the client sends 0x03EB in response to these, but we'll just // Note: the client sends 0x03EB in response to these, but we'll just
// ignore them because we don't need any of the contents // ignore them because we don't need any of the contents
send_command(c, 0x02EB, 0x00000000, chunk_cmd); send_command(c, 0x02EB, 0x00000000, chunk_cmd);
buffer_offset = 0; buffer_offset = 0;
@@ -399,9 +395,7 @@ void send_complete_player_bb(shared_ptr<Client> c) {
// patch functions // patch functions
void send_check_directory_patch(shared_ptr<Client> c, const char* dir) { void send_check_directory_patch(shared_ptr<Client> c, const char* dir) {
S_CheckDirectory_Patch_09 cmd; S_CheckDirectory_Patch_09 cmd = {dir};
memset(&cmd, 0, sizeof(cmd));
strncpy_t(cmd.name, dir, countof(cmd.name));
send_command(c, 0x09, 0x00, cmd); send_command(c, 0x09, 0x00, cmd);
} }
@@ -416,7 +410,7 @@ void send_text(shared_ptr<Client> c, StringWriter& w, uint16_t command,
string data = encode_sjis(text); string data = encode_sjis(text);
add_color(w, data.c_str(), data.size()); add_color(w, data.c_str(), data.size());
} else { } else {
add_color(w, text, char16len(text)); add_color(w, text, text_strlen_t(text));
} }
while (w.str().size() & 3) { while (w.str().size() & 3) {
w.put_u8(0); w.put_u8(0);
@@ -493,12 +487,11 @@ void send_chat_message(shared_ptr<Client> c, uint32_t from_serial_number,
void send_simple_mail_gc(std::shared_ptr<Client> c, uint32_t from_serial_number, void send_simple_mail_gc(std::shared_ptr<Client> c, uint32_t from_serial_number,
const char16_t* from_name, const char16_t* text) { const char16_t* from_name, const char16_t* text) {
SC_SimpleMail_GC_81 cmd; SC_SimpleMail_GC_81 cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.player_tag = 0x00010000; cmd.player_tag = 0x00010000;
cmd.from_serial_number = from_serial_number; cmd.from_serial_number = from_serial_number;
encode_sjis(cmd.from_name, from_name, sizeof(cmd.from_name) / sizeof(cmd.from_name[0])); cmd.from_name = from_name;
cmd.to_serial_number = c->license->serial_number; cmd.to_serial_number = c->license->serial_number;
encode_sjis(cmd.text, text, sizeof(cmd.text) / sizeof(cmd.text[0])); cmd.text = text;
send_command(c, 0x81, 0x00, cmd); send_command(c, 0x81, 0x00, cmd);
} }
@@ -523,12 +516,10 @@ void send_info_board_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
if (!c.get()) { if (!c.get()) {
continue; continue;
} }
entries.emplace_back(); auto& e = entries.emplace_back();
auto& e = entries.back(); e.name = c->player.disp.name;
memset(&e, 0, sizeof(e)); e.message = c->player.info_board;
strncpy_t(e.name, c->player.disp.name, countof(e.name)); add_color_inplace(e.message);
strncpy_t(e.message, c->player.info_board, countof(e.message));
add_color_inplace(e.message, countof(e.message));
} }
send_command(c, 0xD8, entries.size(), entries); send_command(c, 0xD8, entries.size(), entries);
} }
@@ -554,7 +545,6 @@ void send_card_search_result_t(
shared_ptr<Client> result, shared_ptr<Client> result,
shared_ptr<Lobby> result_lobby) { shared_ptr<Lobby> result_lobby) {
S_GuildCardSearchResult<CommandHeaderT, CharT> cmd; S_GuildCardSearchResult<CommandHeaderT, CharT> cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.player_tag = 0x00010000; cmd.player_tag = 0x00010000;
cmd.searcher_serial_number = c->license->serial_number; cmd.searcher_serial_number = c->license->serial_number;
cmd.result_serial_number = result->license->serial_number; cmd.result_serial_number = result->license->serial_number;
@@ -570,18 +560,18 @@ void send_card_search_result_t(
cmd.reconnect_command.unused = 0; cmd.reconnect_command.unused = 0;
auto encoded_server_name = encode_sjis(s->name); auto encoded_server_name = encode_sjis(s->name);
string location_string;
if (result_lobby->is_game()) { if (result_lobby->is_game()) {
string encoded_lobby_name = encode_sjis(result_lobby->name); string encoded_lobby_name = encode_sjis(result_lobby->name);
snprintf(cmd.location_string, sizeof(cmd.location_string), location_string = string_printf("%s,Block 00,,%s",
"%s,Block 00,,%s", encoded_lobby_name.c_str(), encoded_server_name.c_str()); encoded_lobby_name.c_str(), encoded_server_name.c_str());
} else { } else {
snprintf(cmd.location_string, sizeof(cmd.location_string), "Block 00,,%s", location_string = string_printf("Block 00,,%s", encoded_server_name.c_str());
encoded_server_name.c_str());
} }
cmd.location_string = location_string;
cmd.menu_id = LOBBY_MENU_ID; cmd.menu_id = LOBBY_MENU_ID;
cmd.lobby_id = result->lobby_id; cmd.lobby_id = result->lobby_id;
memset(cmd.unused, 0, sizeof(cmd.unused)); cmd.name = result->player.disp.name;
strncpy_t(cmd.name, result->player.disp.name, countof(cmd.name));
send_command(c, 0x41, 0x00, cmd); send_command(c, 0x41, 0x00, cmd);
} }
@@ -605,26 +595,22 @@ void send_card_search_result(
} }
} }
////////////////////////////////////////////////////////////////////////////////
// CommandSendGuildCard: generates a guild card for the source player and sends it to the destination player
void send_guild_card_gc(shared_ptr<Client> c, shared_ptr<Client> source) { void send_guild_card_gc(shared_ptr<Client> c, shared_ptr<Client> source) {
S_SendGuildCard_GC cmd; S_SendGuildCard_GC cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.subcommand = 0x06; cmd.subcommand = 0x06;
cmd.subsize = 0x25; cmd.subsize = 0x25;
cmd.unused = 0x0000; cmd.unused = 0x0000;
cmd.player_tag = 0x00010000; cmd.player_tag = 0x00010000;
cmd.reserved1 = 1; cmd.reserved1 = 1;
cmd.reserved2 = 1; cmd.reserved2 = 1;
cmd.serial_number = source->license->serial_number; cmd.serial_number = source->license->serial_number;
encode_sjis(cmd.name, source->player.disp.name, countof(cmd.name)); cmd.name = source->player.disp.name;
remove_language_marker_inplace(cmd.name); remove_language_marker_inplace(cmd.name);
encode_sjis(cmd.desc, source->player.guild_card_desc, countof(cmd.desc)); cmd.desc = source->player.guild_card_desc;
cmd.section_id = source->player.disp.section_id; cmd.section_id = source->player.disp.section_id;
cmd.char_class = source->player.disp.char_class; cmd.char_class = source->player.disp.char_class;
send_command(c, 0x62, c->lobby_client_id, cmd); send_command(c, 0x62, c->lobby_client_id, cmd);
} }
@@ -636,16 +622,12 @@ void send_guild_card_bb(shared_ptr<Client> c, shared_ptr<Client> source) {
cmd.unused = 0x0000; cmd.unused = 0x0000;
cmd.reserved1 = 1; cmd.reserved1 = 1;
cmd.reserved2 = 1; cmd.reserved2 = 1;
cmd.serial_number = source->license->serial_number; cmd.serial_number = source->license->serial_number;
strcpy_z(cmd.name, source->player.disp.name, countof(cmd.name)); cmd.name = remove_language_marker(source->player.disp.name);
remove_language_marker_inplace(cmd.name); cmd.team_name = remove_language_marker(source->player.team_name);
strcpy_z(cmd.team_name, source->player.team_name, countof(cmd.team_name)); cmd.desc = source->player.guild_card_desc;
remove_language_marker_inplace(cmd.team_name);
strcpy_z(cmd.desc, source->player.guild_card_desc, countof(cmd.desc));
cmd.section_id = source->player.disp.section_id; cmd.section_id = source->player.disp.section_id;
cmd.char_class = source->player.disp.char_class; cmd.char_class = source->player.disp.char_class;
send_command(c, 0x62, c->lobby_client_id, cmd); send_command(c, 0x62, c->lobby_client_id, cmd);
} }
@@ -673,14 +655,12 @@ void send_menu_t(
bool is_info_menu) { bool is_info_menu) {
vector<EntryT> entries; vector<EntryT> entries;
entries.emplace_back();
{ {
auto& e = entries.back(); auto& e = entries.emplace_back();
memset(&e, 0, sizeof(e));
e.menu_id = menu_id; e.menu_id = menu_id;
e.item_id = 0xFFFFFFFF; e.item_id = 0xFFFFFFFF;
e.flags = 0x0004; e.flags = 0x0004;
strncpy_t(e.text, menu_name, countof(e.text)); e.text = menu_name;
} }
for (const auto& item : items) { for (const auto& item : items) {
@@ -692,14 +672,11 @@ void send_menu_t(
((item.flags & MenuItemFlag::REQUIRES_MESSAGE_BOXES) && (c->flags & ClientFlag::NO_MESSAGE_BOX_CLOSE_CONFIRMATION))) { ((item.flags & MenuItemFlag::REQUIRES_MESSAGE_BOXES) && (c->flags & ClientFlag::NO_MESSAGE_BOX_CLOSE_CONFIRMATION))) {
continue; continue;
} }
auto& e = entries.emplace_back();
entries.emplace_back();
auto& e = entries.back();
memset(&e, 0, sizeof(e));
e.menu_id = menu_id; e.menu_id = menu_id;
e.item_id = item.item_id; e.item_id = item.item_id;
e.flags = (c->version == GameVersion::BB) ? 0x0004 : 0x0F04; e.flags = (c->version == GameVersion::BB) ? 0x0004 : 0x0F04;
strncpy_t(e.text, item.name.c_str(), countof(e.text)); e.text = item.name;
} }
send_command(c, is_info_menu ? 0x1F : 0x07, entries.size() - 1, entries); send_command(c, is_info_menu ? 0x1F : 0x07, entries.size() - 1, entries);
@@ -722,43 +699,38 @@ template <typename CharT>
void send_game_menu_t(shared_ptr<Client> c, shared_ptr<ServerState> s) { void send_game_menu_t(shared_ptr<Client> c, shared_ptr<ServerState> s) {
vector<S_GameMenuEntry<CharT>> entries; vector<S_GameMenuEntry<CharT>> entries;
{ {
entries.emplace_back(); auto& e = entries.emplace_back();
auto& e = entries.back();
memset(&e, 0, sizeof(e));
e.menu_id = GAME_MENU_ID; e.menu_id = GAME_MENU_ID;
e.game_id = 0x00000000; e.game_id = 0x00000000;
e.difficulty_tag = 0x00; e.difficulty_tag = 0x00;
e.num_players = 0x00; e.num_players = 0x00;
strncpy_t(e.name, s->name.c_str(), countof(e.name)); e.name = s->name;
e.episode = 0x00; e.episode = 0x00;
e.flags = 0x04; e.flags = 0x04;
} }
for (shared_ptr<Lobby> l : s->all_lobbies()) { for (shared_ptr<Lobby> l : s->all_lobbies()) {
if (!l->is_game()) { if (!l->is_game() || (l->version != c->version)) {
continue; continue;
} }
if (l->version != c->version) { bool l_is_ep3 = !!(l->flags & LobbyFlag::EPISODE_3);
continue; bool c_is_ep3 = !!(c->flags & ClientFlag::EPISODE_3_GAMES);
} if (l_is_ep3 != c_is_ep3) {
if (!(l->flags & LobbyFlag::EPISODE_3) != !(c->flags & ClientFlag::EPISODE_3_GAMES)) {
continue; continue;
} }
entries.emplace_back(); auto& e = entries.emplace_back();
auto& e = entries.back();
memset(&e, 0, sizeof(e)); memset(&e, 0, sizeof(e));
e.menu_id = GAME_MENU_ID; e.menu_id = GAME_MENU_ID;
e.game_id = l->lobby_id; e.game_id = l->lobby_id;
e.difficulty_tag = ((l->flags & LobbyFlag::EPISODE_3) e.difficulty_tag = (l_is_ep3 ? 0x0A : (l->difficulty + 0x22));
? 0x0A : (l->difficulty + 0x22));
e.num_players = l->count_clients(); e.num_players = l->count_clients();
e.episode = ((c->version == GameVersion::BB) ? (l->max_clients << 4) : 0) | l->episode; e.episode = ((c->version == GameVersion::BB) ? (l->max_clients << 4) : 0) | l->episode;
if (l->flags & LobbyFlag::EPISODE_3) { if (l->flags & LobbyFlag::EPISODE_3) {
e.flags = (l->password[0] ? 2 : 0); e.flags = (l->password.empty() ? 0 : 2);
} else { } else {
e.flags = ((l->episode << 6) | ((l->mode % 3) << 4) | (l->password[0] ? 2 : 0)) | ((l->mode == 3) ? 4 : 0); e.flags = ((l->episode << 6) | ((l->mode % 3) << 4) | (l->password.empty() ? 0 : 2)) | ((l->mode == 3) ? 4 : 0);
} }
strncpy_t(e.name, l->name, countof(e.name)); e.name = l->name;
} }
send_command(c, 0x08, entries.size() - 1, entries); send_command(c, 0x08, entries.size() - 1, entries);
@@ -782,14 +754,12 @@ void send_quest_menu_t(
bool is_download_menu) { bool is_download_menu) {
vector<EntryT> entries; vector<EntryT> entries;
for (const auto& quest : quests) { for (const auto& quest : quests) {
entries.emplace_back(); auto& e = entries.emplace_back();
auto& e = entries.back();
memset(&e, 0, sizeof(e));
e.menu_id = menu_id; e.menu_id = menu_id;
e.item_id = quest->quest_id; e.item_id = quest->quest_id;
strncpy_t(e.name, quest->name.c_str(), countof(e.name)); e.name = quest->name;
strncpy_t(e.short_desc, quest->short_description.c_str(), countof(e.short_desc)); e.short_desc = quest->short_description;
add_color_inplace(e.short_desc, countof(e.short_desc)); add_color_inplace(e.short_desc);
} }
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries); send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
} }
@@ -802,14 +772,12 @@ void send_quest_menu_t(
bool is_download_menu) { bool is_download_menu) {
vector<EntryT> entries; vector<EntryT> entries;
for (const auto& item : items) { for (const auto& item : items) {
entries.emplace_back(); auto& e = entries.emplace_back();
auto& e = entries.back();
memset(&e, 0, sizeof(e));
e.menu_id = menu_id; e.menu_id = menu_id;
e.item_id = item.item_id; e.item_id = item.item_id;
strncpy_t(e.name, item.name.c_str(), 0x20); e.name = item.name;
strncpy_t(e.short_desc, item.description.c_str(), 0x70); e.short_desc = item.description;
add_color_inplace(e.short_desc, 0x70); add_color_inplace(e.short_desc);
} }
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries); send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
} }
@@ -853,9 +821,7 @@ void send_lobby_list(shared_ptr<Client> c, shared_ptr<ServerState> s) {
if ((l->flags & LobbyFlag::EPISODE_3) && !(c->flags & ClientFlag::EPISODE_3_GAMES)) { if ((l->flags & LobbyFlag::EPISODE_3) && !(c->flags & ClientFlag::EPISODE_3_GAMES)) {
continue; continue;
} }
auto& e = entries.emplace_back();
entries.emplace_back();
auto& e = entries.back();
e.menu_id = LOBBY_MENU_ID; e.menu_id = LOBBY_MENU_ID;
e.item_id = l->lobby_id; e.item_id = l->lobby_id;
e.unused = 0; e.unused = 0;
@@ -872,10 +838,10 @@ void send_lobby_list(shared_ptr<Client> c, shared_ptr<ServerState> s) {
template <typename LobbyDataT, typename DispDataT> template <typename LobbyDataT, typename DispDataT>
void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) { void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
S_JoinGame<LobbyDataT, DispDataT> cmd; S_JoinGame<LobbyDataT, DispDataT> cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.variations = l->variations;
size_t player_count = 0; size_t player_count = 0;
memcpy(cmd.variations, l->variations, sizeof(cmd.variations));
for (size_t x = 0; x < 4; x++) { for (size_t x = 0; x < 4; x++) {
if (l->clients[x]) { if (l->clients[x]) {
cmd.lobby_data[x].player_tag = 0x00010000; cmd.lobby_data[x].player_tag = 0x00010000;
@@ -883,8 +849,7 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
// See comment in send_join_lobby_t about Episode III behavior here // See comment in send_join_lobby_t about Episode III behavior here
cmd.lobby_data[x].ip_address = 0x7F000001; cmd.lobby_data[x].ip_address = 0x7F000001;
cmd.lobby_data[x].client_id = c->lobby_client_id; cmd.lobby_data[x].client_id = c->lobby_client_id;
strncpy_t(cmd.lobby_data[x].name, l->clients[x]->player.disp.name, cmd.lobby_data[x].name = l->clients[x]->player.disp.name;
countof(cmd.lobby_data[x].name));
if (l->flags & LobbyFlag::EPISODE_3) { if (l->flags & LobbyFlag::EPISODE_3) {
cmd.players_ep3[x].inventory = l->clients[x]->player.inventory; cmd.players_ep3[x].inventory = l->clients[x]->player.inventory;
cmd.players_ep3[x].disp = convert_player_disp_data<DispDataT>( cmd.players_ep3[x].disp = convert_player_disp_data<DispDataT>(
@@ -927,7 +892,6 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
} }
} else { } else {
command = joining_client ? 0x68 : 0x67; command = joining_client ? 0x68 : 0x67;
} }
uint8_t lobby_type = (l->type > 14) ? (l->block - 1) : l->type; uint8_t lobby_type = (l->type > 14) ? (l->block - 1) : l->type;
@@ -949,7 +913,6 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
} }
S_JoinLobby<LobbyDataT, DispDataT> cmd; S_JoinLobby<LobbyDataT, DispDataT> cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.client_id = c->lobby_client_id; cmd.client_id = c->lobby_client_id;
cmd.leader_id = l->leader_id; cmd.leader_id = l->leader_id;
cmd.disable_udp = 0x01; cmd.disable_udp = 0x01;
@@ -972,7 +935,6 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
size_t used_entries = 0; size_t used_entries = 0;
for (const auto& lc : lobby_clients) { for (const auto& lc : lobby_clients) {
auto& e = cmd.entries[used_entries++]; auto& e = cmd.entries[used_entries++];
memset(&e.lobby_data, 0, sizeof(e.lobby_data));
e.lobby_data.player_tag = 0x00010000; e.lobby_data.player_tag = 0x00010000;
e.lobby_data.guild_card = lc->license->serial_number; e.lobby_data.guild_card = lc->license->serial_number;
// There's a strange behavior (bug? "feature"?) in Episode 3 where the start // There's a strange behavior (bug? "feature"?) in Episode 3 where the start
@@ -981,8 +943,7 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
// to avoid this behavior. // to avoid this behavior.
e.lobby_data.ip_address = 0x7F000001; e.lobby_data.ip_address = 0x7F000001;
e.lobby_data.client_id = lc->lobby_client_id; e.lobby_data.client_id = lc->lobby_client_id;
strncpy_t(e.lobby_data.name, lc->player.disp.name, e.lobby_data.name = lc->player.disp.name;
countof(e.lobby_data.name));
e.inventory = lc->player.inventory; e.inventory = lc->player.inventory;
e.disp = convert_player_disp_data<DispDataT>(lc->player.disp); e.disp = convert_player_disp_data<DispDataT>(lc->player.disp);
if (c->version == GameVersion::PC) { if (c->version == GameVersion::PC) {
@@ -1059,9 +1020,7 @@ void send_arrow_update(shared_ptr<Lobby> l) {
if (!l->clients[x]) { if (!l->clients[x]) {
continue; continue;
} }
auto& e = entries.emplace_back();
entries.emplace_back();
auto& e = entries.back();
e.player_tag = 0x00010000; e.player_tag = 0x00010000;
e.serial_number = l->clients[x]->license->serial_number; e.serial_number = l->clients[x]->license->serial_number;
e.arrow_color = l->clients[x]->lobby_arrow_color; e.arrow_color = l->clients[x]->lobby_arrow_color;
@@ -1092,16 +1051,14 @@ void send_player_stats_change(shared_ptr<Lobby> l, shared_ptr<Client> c,
vector<PSOSubcommand> subs; vector<PSOSubcommand> subs;
while (amount > 0) { while (amount > 0) {
{ {
subs.emplace_back(); auto& sub = subs.emplace_back();
auto& sub = subs.back();
sub.byte[0] = 0x9A; sub.byte[0] = 0x9A;
sub.byte[1] = 0x02; sub.byte[1] = 0x02;
sub.byte[2] = c->lobby_client_id; sub.byte[2] = c->lobby_client_id;
sub.byte[3] = 0x00; sub.byte[3] = 0x00;
} }
{ {
subs.emplace_back(); auto& sub = subs.emplace_back();
auto& sub = subs.back();
sub.byte[0] = 0x00; sub.byte[0] = 0x00;
sub.byte[1] = 0x00; sub.byte[1] = 0x00;
sub.byte[2] = stat; sub.byte[2] = stat;
@@ -1288,7 +1245,6 @@ void send_ep3_card_list_update(shared_ptr<Client> c) {
StringWriter w; StringWriter w;
w.put_u32l(file_data->size()); w.put_u32l(file_data->size());
w.write(*file_data); w.write(*file_data);
w.str().resize((w.str().size() + 3) & (~3));
send_command(c, 0xB8, 0x00, w.str()); send_command(c, 0xB8, 0x00, w.str());
} }
@@ -1340,11 +1296,10 @@ void send_quest_open_file_t(
bool is_download_quest, bool is_download_quest,
bool is_ep3_quest) { bool is_ep3_quest) {
CommandT cmd; CommandT cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.flags = 2 + is_ep3_quest; cmd.flags = 2 + is_ep3_quest;
cmd.file_size = file_size; cmd.file_size = file_size;
strncpy_t(cmd.name, filename.c_str(), countof(cmd.name)); cmd.name = filename.c_str();
strncpy_t(cmd.filename, filename.c_str(), countof(cmd.filename)); cmd.filename = filename.c_str();
send_command(c, is_download_quest ? 0xA6 : 0x44, 0x00, cmd); send_command(c, is_download_quest ? 0xA6 : 0x44, 0x00, cmd);
} }
@@ -1360,8 +1315,7 @@ void send_quest_file_chunk(
} }
S_WriteFile_13_A7 cmd; S_WriteFile_13_A7 cmd;
memset(cmd.filename, 0, countof(cmd.filename)); cmd.filename = filename;
strncpy_t(cmd.filename, filename, countof(cmd.filename));
memcpy(cmd.data, data, size); memcpy(cmd.data, data, size);
if (size < 0x400) { if (size < 0x400) {
memset(&cmd.data[size], 0, 0x400 - size); memset(&cmd.data[size], 0, 0x400 - size);
+4 -4
View File
@@ -149,25 +149,25 @@ Proxy commands (these will only work when exactly one client is connected):\n\
if (token.size() >= 29) { if (token.size() >= 29) {
throw invalid_argument("username too long"); throw invalid_argument("username too long");
} }
strcpy(l->username, token.c_str() + 9); l->username = token.substr(9);
} else if (starts_with(token, "bb-password=")) { } else if (starts_with(token, "bb-password=")) {
if (token.size() >= 32) { if (token.size() >= 32) {
throw invalid_argument("bb-password too long"); throw invalid_argument("bb-password too long");
} }
strcpy(l->bb_password, token.c_str() + 12); l->bb_password = token.substr(12);
} else if (starts_with(token, "gc-password=")) { } else if (starts_with(token, "gc-password=")) {
if (token.size() > 20) { if (token.size() > 20) {
throw invalid_argument("gc-password too long"); throw invalid_argument("gc-password too long");
} }
strcpy(l->gc_password, token.c_str() + 12); l->gc_password = token.substr(12);
} else if (starts_with(token, "access-key=")) { } else if (starts_with(token, "access-key=")) {
if (token.size() > 23) { if (token.size() > 23) {
throw invalid_argument("access-key is too long"); throw invalid_argument("access-key is too long");
} }
strcpy(l->access_key, token.c_str() + 11); l->access_key = token.substr(11);
} else if (starts_with(token, "serial=")) { } else if (starts_with(token, "serial=")) {
l->serial_number = stoul(token.substr(7)); l->serial_number = stoul(token.substr(7));
+1 -1
View File
@@ -33,7 +33,7 @@ ServerState::ServerState()
(is_ep3_only ? LobbyFlag::EPISODE_3 : 0); (is_ep3_only ? LobbyFlag::EPISODE_3 : 0);
l->block = x + 1; l->block = x + 1;
l->type = x; l->type = x;
strcpy_z(l->name, lobby_name.c_str(), 0x24); l->name = lobby_name;
l->max_clients = 12; l->max_clients = 12;
this->add_lobby(l); this->add_lobby(l);
+75 -140
View File
@@ -30,20 +30,14 @@ int char16ncmp(const char16_t* s1, const char16_t* s2, size_t count) {
return 0; return 0;
} }
size_t char16len(const char16_t* s) {
size_t x;
for (x = 0; s[x] != 0; x++);
return x;
}
static vector<char16_t> unicode_to_sjis_table_data; static vector<char16_t> unicode_to_sjis_table_data;
static vector<char16_t> sjis_to_unicode_table_data; static vector<char16_t> sjis_to_unicode_table_data;
static void load_sjis_tables() { static void load_sjis_tables() {
unicode_to_sjis_table_data.resize(0x10000); unicode_to_sjis_table_data.resize(0x10000, 0);
sjis_to_unicode_table_data.resize(0x10000); sjis_to_unicode_table_data.resize(0x10000, 0);
// TODO: this is inefficient; it makes multiple copies of the string // TODO: this is inefficient; it makes multiple copies of the string
auto file_contents = load_file("system/sjis-table.ini"); auto file_contents = load_file("system/sjis-table.ini");
@@ -75,166 +69,107 @@ static const vector<char16_t>& unicode_to_sjis_table() {
return unicode_to_sjis_table_data; return unicode_to_sjis_table_data;
} }
// TODO: It looks like these functions are probably wrong. Specifically, we
// don't write the high byte when encoding non-ASCII chars, do we?
void encode_sjis(char* dest, const char16_t* source, size_t max) {
const auto& table = unicode_to_sjis_table();
while (*source && (--max)) {
*(dest++) = table[*(source++)];
};
*dest = 0;
}
void decode_sjis(char16_t* dest, const char* source, size_t max) {
const auto& table = sjis_to_unicode_table();
while (*source && (--max)) {
char16_t src_char = *(source++);
if (src_char & 0x80) {
src_char = (src_char << 8) | *(source++);
if ((src_char & 0xFF) == 0) {
return;
}
}
*(dest++) = table[src_char];
};
*dest = 0;
}
std::string encode_sjis(const char16_t* src, size_t src_count) { std::string encode_sjis(const char16_t* src, size_t src_count) {
const auto& table = unicode_to_sjis_table(); const auto& table = unicode_to_sjis_table();
const char16_t* src_end = src + src_count;
string ret; string ret;
for (; *src && (src_count > 0); src_count--) { while ((src != src_end) && *src) {
ret.push_back(table[*(src++)]); uint16_t ch = *(src++);
uint16_t translated_c = table[ch];
if (translated_c == 0) {
throw runtime_error("untranslatable unicode character");
} else if (translated_c & 0xFF00) {
ret.push_back((translated_c >> 8) & 0xFF);
ret.push_back(translated_c & 0xFF);
} else {
ret.push_back(translated_c & 0xFF);
}
}; };
return ret; return ret;
} }
void encode_sjis(
char* dest,
size_t dest_count,
const char16_t* src,
size_t src_count) {
const auto& table = unicode_to_sjis_table();
if (dest_count == 0) {
throw logic_error("cannot encode into zero-length buffer");
}
const char16_t* src_end = src + src_count;
const char* dest_end = dest + (dest_count - 1);
while ((dest != dest_end) && (src != src_end) && *src) {
uint16_t ch = *(src++);
uint16_t translated_c = table[ch];
if (translated_c == 0) {
throw runtime_error("untranslatable unicode character");
} else if (translated_c & 0xFF00) {
*(dest++) = (translated_c >> 8) & 0xFF;
// If the second byte of this character would cause the null to overrun
// the buffer, erase the first byte instead and return early
if (dest == dest_end) {
*(dest - 1) = 0;
} else {
*(dest++) = translated_c & 0xFF;
}
} else {
*(dest++) = translated_c & 0xFF;
}
}
*dest = 0;
}
std::u16string decode_sjis(const char* src, size_t src_count) { std::u16string decode_sjis(const char* src, size_t src_count) {
const auto& table = sjis_to_unicode_table(); const auto& table = sjis_to_unicode_table();
const char* src_end = src + src_count;
u16string ret; u16string ret;
while (*src && (src_count > 0)) { while ((src != src_end) && *src) {
char16_t src_char = *(src++); uint16_t src_char = *(src++);
src_count--;
if (src_char & 0x80) { if (src_char & 0x80) {
if (src_count == 0) { if (src == src_end) {
return ret; throw runtime_error("incomplete extended character");
} }
src_char = (src_char << 8) | *(src++); src_char = (src_char << 8) | *(src++);
if ((src_char & 0xFF) == 0) { if ((src_char & 0xFF) == 0) {
return ret; throw runtime_error("incomplete extended character");
} }
src_count--;
} }
ret.push_back(table[src_char]); ret.push_back(table[src_char]);
}; };
return ret; return ret;
} }
std::string encode_sjis(const std::u16string& source) { void decode_sjis(
const auto& table = unicode_to_sjis_table(); char16_t* dest,
string ret; size_t dest_count,
for (char16_t ch : source) { const char* src,
ret.push_back(table[ch]); size_t src_count) {
};
return ret;
}
std::u16string decode_sjis(const std::string& source) {
const auto& table = sjis_to_unicode_table(); const auto& table = sjis_to_unicode_table();
u16string ret;
for (size_t x = 0; x < source.size();) { if (dest_count == 0) {
char16_t src_char = source[x++]; throw logic_error("cannot decode into zero-length buffer");
}
const char* src_end = src + src_count;
const char16_t* dest_end = dest + (dest_count - 1);
while ((dest != dest_end) && (src != src_end) && *src) {
uint16_t src_char = *(src++);
if (src_char & 0x80) { if (src_char & 0x80) {
if (x == source.size()) { if (src == src_end) {
return ret; throw runtime_error("incomplete extended character");
} }
src_char = (src_char << 8) | source[x++]; src_char = (src_char << 8) | *(src++);
if ((src_char & 0xFF) == 0) { if ((src_char & 0xFF) == 0) {
return ret; throw runtime_error("incomplete extended character");
} }
} }
ret.push_back(table[src_char]); *(dest++) = table[src_char];
}; };
return ret;
}
void add_language_marker_inplace(char* a, char e, size_t dest_count) {
if ((a[0] == '\t') && (a[1] != 'C')) {
return;
}
size_t existing_count = strlen(a);
if (existing_count > dest_count - 3) {
existing_count = dest_count - 3;
}
memmove(&a[2], a, (existing_count + 1) * sizeof(char));
a[0] = '\t';
a[1] = e;
a[existing_count + 2] = 0;
}
void add_language_marker_inplace(char16_t* a, char16_t e, size_t dest_count) {
if ((a[0] == '\t') && (a[1] != 'C')) {
return;
}
size_t existing_count = char16len(a);
if (existing_count > dest_count - 3) {
existing_count = dest_count - 3;
}
memmove(&a[2], a, (existing_count + 1) * sizeof(char16_t));
a[0] = '\t';
a[1] = e;
a[existing_count + 2] = 0;
}
void remove_language_marker_inplace(char* a) {
if ((a[0] == '\t') && (a[1] != 'C')) {
strcpy(a, &a[2]);
}
}
void remove_language_marker_inplace(char16_t* a) {
if ((a[0] == '\t') && (a[1] != 'C')) {
strcpy_z(a, &a[2], char16len(a) - 2);
}
}
std::string add_language_marker(const std::string& s, char marker) {
if ((s.size() >= 2) && (s[0] == '\t') && (s[1] != 'C')) {
return s;
}
string ret;
ret.push_back('\t');
ret.push_back(marker);
return ret + s;
}
std::u16string add_language_marker(const std::u16string& s, char16_t marker) {
if ((s.size() >= 2) && (s[0] == L'\t') && (s[1] != L'C')) {
return s;
}
u16string ret;
ret.push_back(L'\t');
ret.push_back(marker);
return ret + s;
}
std::string remove_language_marker(const std::string& s) {
if ((s.size() < 2) || (s[0] != '\t') || (s[1] == 'C')) {
return s;
}
return s.substr(2);
}
std::u16string remove_language_marker(const std::u16string& s) {
if ((s.size() < 2) || (s[0] != L'\t') || (s[1] == L'C')) {
return s;
}
return s.substr(2);
} }
+356 -26
View File
@@ -10,26 +10,74 @@
#define countof(F) (sizeof(F) / sizeof(F[0])) // TODO: delete these if not needed
// int char16ncmp(const char16_t* s1, const char16_t* s2, size_t count);
// size_t char16len(const char16_t* s);
int char16ncmp(const char16_t* s1, const char16_t* s2, size_t count); // (1a) Conversion functions
size_t char16len(const char16_t* s);
void encode_sjis(
char* dest, size_t dest_count,
const char16_t* src, size_t src_count);
void decode_sjis(
char16_t* dest, size_t dest_count,
const char* src, size_t src_count);
void encode_sjis(char* dest, const char16_t* source, size_t dest_count);
void decode_sjis(char16_t* dest, const char* source, size_t dest_count);
std::string encode_sjis(const char16_t* source, size_t src_count); std::string encode_sjis(const char16_t* source, size_t src_count);
std::u16string decode_sjis(const char* source, size_t src_count); std::u16string decode_sjis(const char* source, size_t src_count);
std::string encode_sjis(const std::u16string& source);
std::u16string decode_sjis(const std::string& source);
inline std::string encode_sjis(const std::u16string& s) {
return encode_sjis(s.data(), s.size());
}
inline std::u16string decode_sjis(const std::string& s) {
return decode_sjis(s.data(), s.size());
}
// (1b) Type-independent utility functions
template <typename T>
size_t text_strlen_t(const T* s) {
size_t ret = 0;
for (; s[ret] != 0; ret++) { }
return ret;
}
template <typename T>
size_t text_streq_t(const T* a, const T* b) {
for (;;) {
if (*a != *b) {
return false;
}
if (*a == 0) {
return true;
}
a++;
b++;
}
}
template <typename T>
size_t text_strneq_t(const T* a, const T* b, size_t count) {
for (; count; count--) {
if (*a != *b) {
return false;
}
if (*a == 0) {
return true;
}
a++;
b++;
}
return true;
}
// Like strncpy, but *always* null-terminates the string, even if it has to // Like strncpy, but *always* null-terminates the string, even if it has to
// truncate it. // truncate it.
template <typename T> template <typename T>
void strcpy_z(T* dest, const T* src, size_t count) { void text_strnzcpy_t(T* dest, const T* src, size_t count) {
size_t x; size_t x;
for (x = 0; x < count - 1 && src[x] != 0; x++) { for (x = 0; x < count - 1 && src[x] != 0; x++) {
dest[x] = src[x]; dest[x] = src[x];
@@ -39,44 +87,294 @@ void strcpy_z(T* dest, const T* src, size_t count) {
// (2) Type conversion functions
template <typename DestT, typename SrcT = DestT> template <typename DestT, typename SrcT = DestT>
void strncpy_t(DestT*, const SrcT*, size_t) { void text_strnzcpy_t(DestT*, size_t, const SrcT*, size_t) {
static_assert(always_false<DestT, SrcT>::v, static_assert(always_false<DestT, SrcT>::v,
"unspecialized strcpy_t should never be called"); "unspecialized text_strnzcpy_t should never be called");
} }
template <> template <>
inline void strncpy_t<char>(char* dest, const char* src, size_t count) { inline void text_strnzcpy_t<char>(
strcpy_z(dest, src, count); char* dest, size_t dest_count, const char* src, size_t src_count) {
size_t count = std::min<size_t>(dest_count, src_count);
text_strnzcpy_t(dest, src, count);
} }
template <> template <>
inline void strncpy_t<char, char16_t>(char* dest, const char16_t* src, size_t count) { inline void text_strnzcpy_t<char, char16_t>(
encode_sjis(dest, src, count); char* dest, size_t dest_count, const char16_t* src, size_t src_count) {
encode_sjis(dest, dest_count, src, src_count);
} }
template <> template <>
inline void strncpy_t<char16_t, char>(char16_t* dest, const char* src, size_t count) { inline void text_strnzcpy_t<char16_t, char>(
decode_sjis(dest, src, count); char16_t* dest, size_t dest_count, const char* src, size_t src_count) {
decode_sjis(dest, dest_count, src, src_count);
} }
template <> template <>
inline void strncpy_t<char16_t>(char16_t* dest, const char16_t* src, size_t count) { inline void text_strnzcpy_t<char16_t>(
strcpy_z(dest, src, count); char16_t* dest, size_t dest_count, const char16_t* src, size_t src_count) {
size_t count = std::min<size_t>(dest_count, src_count);
text_strnzcpy_t(dest, src, count);
} }
void add_language_marker_inplace(char* s, char marker, size_t dest_count); // (3) Packed text object for use in protocol structs
void add_language_marker_inplace(char16_t* s, char16_t marker, size_t dest_count);
void remove_language_marker_inplace(char* s); template <typename ItemT, size_t Count>
void remove_language_marker_inplace(char16_t* s); struct parray {
std::string add_language_marker(const std::string& s, char marker); ItemT items[Count];
std::u16string add_language_marker(const std::u16string& s, char16_t marker);
std::string remove_language_marker(const std::string& s); parray() {
std::u16string remove_language_marker(const std::u16string& s); this->clear();
}
parray(const parray& other) {
this->operator=(other);
}
parray(parray&& s) = delete;
template <size_t OtherCount>
parray(const parray<ItemT, OtherCount>& s) {
this->operator=(s);
}
constexpr size_t size() {
return Count;
}
constexpr size_t bytes() {
return Count * sizeof(ItemT);
}
ItemT* data() {
return this->items;
}
const ItemT* data() const {
return this->items;
}
// TODO: These can be made faster by only clearing the unused space after the
// strncpy_t (if any) instead of clearing all the space every time
parray& operator=(const parray& s) {
for (size_t x = 0; x < Count; x++) {
this->items[x] = s.items[x];
}
return *this;
}
parray& operator=(parray&& s) = delete;
template <size_t OtherCount>
parray& operator=(const parray<ItemT, OtherCount>& s) {
if (OtherCount <= Count) {
size_t x;
for (x = 0; x < OtherCount; x++) {
this->items[x] = s.items[x];
}
for (; x < Count; x++) {
this->items[x] = 0;
}
} else {
for (size_t x = 0; x < Count; x++) {
this->items[x] = s.items[x];
}
}
return *this;
}
parray& operator=(const ItemT* s) {
for (size_t x = 0; x < Count; x++) {
this->items[x] = s[x];
}
return *this;
}
bool operator==(const parray& s) const {
for (size_t x = 0; x < Count; x++) {
if (this->items[x] != s.items[x]) {
return false;
}
}
return true;
}
void clear(ItemT v = 0) {
for (size_t x = 0; x < Count; x++) {
this->items[x] = v;
}
}
} __attribute__((packed));
template <typename CharT, size_t Count>
struct ptext : parray<CharT, Count> {
ptext() {
this->clear();
}
ptext(const ptext& other) {
this->operator=(other);
}
ptext(ptext&& s) = delete;
template <typename OtherCharT>
ptext(const OtherCharT* s) {
this->operator=(s);
}
template <typename OtherCharT>
ptext(const OtherCharT* s, size_t count) {
this->assign(s, count);
}
template <typename OtherCharT>
ptext(const std::basic_string<OtherCharT>& s) {
this->operator=(s);
}
template <typename OtherCharT, size_t OtherCount>
ptext(const ptext<OtherCharT, OtherCount>& s) {
this->operator=(s);
}
size_t len() const {
return text_strlen_t(this->items);
}
const CharT* c_str() const {
return this->data();
}
// TODO: These can be made faster by only clearing the unused space after the
// strncpy_t (if any) instead of clearing all the space every time
ptext& operator=(const ptext& s) {
this->clear();
text_strnzcpy_t(this->items, Count, s.items, Count);
return *this;
}
ptext& operator=(ptext&& s) = delete;
template <typename OtherCharT>
ptext& operator=(const OtherCharT* s) {
this->clear();
text_strnzcpy_t(this->items, Count, s, Count);
return *this;
}
template <typename OtherCharT>
ptext& assign(const OtherCharT* s, size_t s_count) {
this->clear();
text_strnzcpy_t(this->items, Count, s, s_count);
return *this;
}
template <typename OtherCharT>
ptext& operator=(const std::basic_string<OtherCharT>& s) {
this->clear();
text_strnzcpy_t(this->items, Count, s.c_str(), s.size() + 1);
return *this;
}
template <typename OtherCharT, size_t OtherCount>
ptext& operator=(const ptext<OtherCharT, OtherCount>& s) {
this->clear();
text_strnzcpy_t(this->items, Count, s.items, OtherCount);
return *this;
}
template <typename OtherCharT>
bool operator==(const OtherCharT* s) const {
return text_streq_t(this->items, s);
}
template <typename OtherCharT>
bool operator==(const std::basic_string<OtherCharT>& s) const {
return text_streq_t(this->items, s.c_str());
}
template <typename OtherCharT, size_t OtherCount>
bool operator==(const ptext<OtherCharT, OtherCount>& s) const {
return text_streq_t(this->items, s.items);
}
template <typename OtherCharT>
bool eq_n(const OtherCharT* s, size_t count) const {
return text_strneq_t(this->items, s, count);
}
template <typename OtherCharT>
bool eq_n(const std::basic_string<OtherCharT>& s, size_t count) const {
return text_strneq_t(this->items, s.c_str(), count);
}
template <typename OtherCharT, size_t OtherCount>
bool eq_n(const ptext<OtherCharT, OtherCount>& s, size_t count) const {
return text_strneq_t(this->items, s.items, count);
}
operator std::basic_string<CharT>() const {
std::basic_string<CharT> ret(this->items, Count);
strip_trailing_zeroes(ret);
return ret;
}
bool empty() const {
return (this->items[0] == 0);
}
} __attribute__((packed));
// (4) Markers and character replacement
template <typename CharT>
std::basic_string<CharT> add_language_marker(
const std::basic_string<CharT>& s, CharT marker) {
if ((s.size() >= 2) && (s[0] == '\t') && (s[1] != 'C')) {
return s;
}
std::basic_string<CharT> ret;
ret.push_back('\t');
ret.push_back(marker);
ret += s;
return ret;
}
template <typename CharT, size_t Count>
std::basic_string<CharT> add_language_marker(
const ptext<CharT, Count>& s, CharT marker) {
if ((s.items[0] == '\t') && (s.items[1] != 'C')) {
return s;
}
std::basic_string<CharT> ret;
ret.push_back('\t');
ret.push_back(marker);
ret += s;
return ret;
}
template <typename CharT>
const CharT* remove_language_marker(const CharT* s) {
if ((s[0] != '\t') || (s[1] == 'C')) {
return s;
}
return s + 2;
}
template <typename CharT, size_t Count>
std::basic_string<CharT> remove_language_marker(const ptext<CharT, Count>& s) {
if ((s.items[0] != '\t') || (s.items[1] == L'C')) {
return s;
}
return &s.items[2];
}
template <typename CharT>
std::basic_string<CharT> remove_language_marker(
const std::basic_string<CharT>& s) {
if ((s.size() < 2) || (s[0] != L'\t') || (s[1] == L'C')) {
return s;
}
return s.substr(2);
}
template <typename CharT, size_t Count>
void remove_language_marker_inplace(ptext<CharT, Count>& a) {
if ((a.items[0] == '\t') && (a.items[1] != 'C')) {
text_strnzcpy_t(a.items, Count, &a.items[2], Count);
}
}
template <typename T> template <typename T>
void replace_char_inplace(T* a, T f, T r) { void replace_char_inplace(T* a, T f, T r) {
@@ -150,3 +448,35 @@ void add_color(StringWriter& w, const T* src, size_t max_input_chars) {
} }
w.put<T>(0); w.put<T>(0);
} }
template <typename CharT, size_t Count>
void add_color_inplace(ptext<CharT, Count>& t) {
size_t sx = 0;
size_t dx = 0;
for (; (sx < Count - 1) && t.items[sx]; sx++) {
if (t.items[sx] == '$') {
t.items[dx] = '\t';
} else if (t.items[sx] == '#') {
t.items[dx] = '\n';
} else if (t.items[sx] == '%') {
sx++;
if ((sx == Count - 1) || (t.items[sx] == '\0')) {
break;
} else if (t.items[sx] == 's') {
t.items[dx] = '$';
} else if (t.items[sx] == '%') {
t.items[dx] = '%';
} else if (t.items[sx] == 'n') {
t.items[dx] = '#';
} else {
t.items[dx] = t.items[sx];
}
} else {
t.items[dx] = t.items[sx];
}
dx++;
}
for (; dx < Count; dx++) {
t.items[dx] = 0;
}
}