censor user credentials in logs by default

This commit is contained in:
Martin Michelsen
2026-05-16 16:58:27 -07:00
parent ecc61b7d1f
commit 0d5cfc6ccc
25 changed files with 445 additions and 105 deletions
+1
View File
@@ -61,6 +61,7 @@ set(SOURCES
src/ChoiceSearch.cc src/ChoiceSearch.cc
src/Client.cc src/Client.cc
src/ClientFunctionIndex.cc src/ClientFunctionIndex.cc
src/CommandCensorData.cc
src/CommonItemSet.cc src/CommonItemSet.cc
src/Compression.cc src/Compression.cc
src/DCSerialNumbers.cc src/DCSerialNumbers.cc
+57 -14
View File
@@ -7,6 +7,7 @@
#include <phosg/Network.hh> #include <phosg/Network.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include "CommandCensorData.hh"
#include "Loggers.hh" #include "Loggers.hh"
#include "StaticGameData.hh" #include "StaticGameData.hh"
#include "Version.hh" #include "Version.hh"
@@ -20,12 +21,16 @@ Channel::Channel(
Language language, Language language,
const string& name, const string& name,
phosg::TerminalFormat terminal_send_color, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color) phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials)
: version(version), : version(version),
language(language), language(language),
name(name), name(name),
terminal_send_color(terminal_send_color), terminal_send_color(terminal_send_color),
terminal_recv_color(terminal_recv_color) { terminal_recv_color(terminal_recv_color),
censor_received_credentials(censor_received_credentials),
censor_sent_credentials(censor_sent_credentials) {
} }
void Channel::send(uint16_t cmd, uint32_t flag, bool silent) { void Channel::send(uint16_t cmd, uint32_t flag, bool silent) {
@@ -132,7 +137,20 @@ void Channel::send(
command_data_log.info_f("Sending to {} (version={} command={:02X} flag={:02X})", command_data_log.info_f("Sending to {} (version={} command={:02X} flag={:02X})",
this->name, phosg::name_for_enum(version), cmd, flag); this->name, phosg::name_for_enum(version), cmd, flag);
} }
phosg::print_data(stderr, send_data.data(), logical_size, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
struct iovec iov{.iov_base = send_data.data(), .iov_len = send_data.size()};
if (this->censor_sent_credentials) {
auto [censor_data, censor_size] = censor_data_for_client_command(this->version, cmd);
struct iovec censor_iovs[2] = {
// const_casts are OK here because print_data does not modify the buffers
{.iov_base = const_cast<char*>("\0\0\0\0\0\0\0\0"), .iov_len = static_cast<size_t>(is_v4(this->version) ? 8 : 4)},
{.iov_base = const_cast<void*>(censor_data), .iov_len = censor_size}};
phosg::print_data(stderr, &iov, 1, 0, nullptr, 0, censor_iovs, 2, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
} else {
phosg::print_data(stderr, &iov, 1, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
}
if (use_terminal_colors && this->terminal_send_color != phosg::TerminalFormat::NORMAL) { if (use_terminal_colors && this->terminal_send_color != phosg::TerminalFormat::NORMAL) {
print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END); print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
} }
@@ -201,6 +219,7 @@ asio::awaitable<Channel::Message> Channel::recv() {
} }
command_data.resize(command_logical_size - header_size); command_data.resize(command_logical_size - header_size);
uint16_t command = header.command(this->version);
if (command_data_log.should_log(phosg::LogLevel::L_INFO) && (this->terminal_recv_color != phosg::TerminalFormat::END)) { if (command_data_log.should_log(phosg::LogLevel::L_INFO) && (this->terminal_recv_color != phosg::TerminalFormat::END)) {
if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) { if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) {
print_color_escape(stderr, this->terminal_recv_color, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::END); print_color_escape(stderr, this->terminal_recv_color, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::END);
@@ -221,10 +240,20 @@ asio::awaitable<Channel::Message> Channel::recv() {
header.flag(this->version)); header.flag(this->version));
} }
vector<struct iovec> iovs; struct iovec iovs[2] = {
iovs.emplace_back(iovec{.iov_base = &header, .iov_len = header_size}); {.iov_base = &header, .iov_len = header_size},
iovs.emplace_back(iovec{.iov_base = command_data.data(), .iov_len = command_data.size()}); {.iov_base = command_data.data(), .iov_len = command_data.size()}};
phosg::print_data(stderr, iovs, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
if (this->censor_received_credentials) {
auto [censor_data, censor_size] = censor_data_for_client_command(this->version, command);
struct iovec censor_iovs[2] = {
// const_casts are OK here because print_data does not modify the buffers
{.iov_base = const_cast<char*>("\0\0\0\0\0\0\0\0"), .iov_len = header_size},
{.iov_base = const_cast<void*>(censor_data), .iov_len = censor_size}};
phosg::print_data(stderr, iovs, 2, 0, nullptr, 0, censor_iovs, 2, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
} else {
phosg::print_data(stderr, iovs, 2, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
}
if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) { if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) {
phosg::print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END); phosg::print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
@@ -232,7 +261,7 @@ asio::awaitable<Channel::Message> Channel::recv() {
} }
co_return Message{ co_return Message{
.command = header.command(this->version), .command = command,
.flag = header.flag(this->version), .flag = header.flag(this->version),
.data = std::move(command_data), .data = std::move(command_data),
}; };
@@ -245,9 +274,19 @@ shared_ptr<SocketChannel> SocketChannel::create(
Language language, Language language,
const string& name, const string& name,
phosg::TerminalFormat terminal_send_color, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color) { phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials) {
shared_ptr<SocketChannel> ret(new SocketChannel( shared_ptr<SocketChannel> ret(new SocketChannel(
io_context, std::move(sock), version, language, name, terminal_send_color, terminal_recv_color)); io_context,
std::move(sock),
version,
language,
name,
terminal_send_color,
terminal_recv_color,
censor_received_credentials,
censor_sent_credentials));
asio::co_spawn(*io_context, ret->send_task(), asio::detached); asio::co_spawn(*io_context, ret->send_task(), asio::detached);
return ret; return ret;
} }
@@ -259,8 +298,10 @@ SocketChannel::SocketChannel(
Language language, Language language,
const string& name, const string& name,
phosg::TerminalFormat terminal_send_color, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color) phosg::TerminalFormat terminal_recv_color,
: Channel(version, language, name, terminal_send_color, terminal_recv_color), bool censor_received_credentials,
bool censor_sent_credentials)
: Channel(version, language, name, terminal_send_color, terminal_recv_color, censor_received_credentials, censor_sent_credentials),
sock(std::move(sock)), sock(std::move(sock)),
local_addr(this->sock->local_endpoint()), local_addr(this->sock->local_endpoint()),
remote_addr(this->sock->remote_endpoint()), remote_addr(this->sock->remote_endpoint()),
@@ -327,8 +368,10 @@ PeerChannel::PeerChannel(
Language language, Language language,
const std::string& name, const std::string& name,
phosg::TerminalFormat terminal_send_color, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color) phosg::TerminalFormat terminal_recv_color,
: Channel(version, language, name, terminal_send_color, terminal_recv_color), bool censor_received_credentials,
bool censor_sent_credentials)
: Channel(version, language, name, terminal_send_color, terminal_recv_color, censor_received_credentials, censor_sent_credentials),
send_buffer_nonempty_signal(io_context->get_executor()) {} send_buffer_nonempty_signal(io_context->get_executor()) {}
void PeerChannel::link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2) { void PeerChannel::link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2) {
+19 -9
View File
@@ -19,6 +19,8 @@ public:
std::string name; std::string name;
phosg::TerminalFormat terminal_send_color; phosg::TerminalFormat terminal_send_color;
phosg::TerminalFormat terminal_recv_color; phosg::TerminalFormat terminal_recv_color;
bool censor_received_credentials;
bool censor_sent_credentials;
struct Message { struct Message {
uint16_t command; uint16_t command;
@@ -87,8 +89,10 @@ protected:
Version version, Version version,
Language language, Language language,
const std::string& name, const std::string& name,
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END); phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials);
Channel(const Channel& other) = delete; Channel(const Channel& other) = delete;
Channel(Channel&& other) = delete; Channel(Channel&& other) = delete;
Channel& operator=(const Channel& other) = delete; Channel& operator=(const Channel& other) = delete;
@@ -114,9 +118,11 @@ public:
std::unique_ptr<asio::ip::tcp::socket>&& sock, std::unique_ptr<asio::ip::tcp::socket>&& sock,
Version version, Version version,
Language language, Language language,
const std::string& name = "", const std::string& name,
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END); phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials);
virtual std::string default_name() const; virtual std::string default_name() const;
@@ -134,7 +140,9 @@ private:
Language language, Language language,
const std::string& name, const std::string& name,
phosg::TerminalFormat terminal_send_color, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color); phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials);
std::deque<std::string> outbound_data; std::deque<std::string> outbound_data;
bool should_disconnect = false; bool should_disconnect = false;
@@ -152,9 +160,11 @@ public:
std::shared_ptr<asio::io_context> io_context, std::shared_ptr<asio::io_context> io_context,
Version version, Version version,
Language language, Language language,
const std::string& name = "", const std::string& name,
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END); phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials);
static void link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2); static void link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2);
+241
View File
@@ -0,0 +1,241 @@
#include "CommandCensorData.hh"
#include "CommandFormats.hh"
std::pair<const void*, size_t> censor_data_for_client_command(Version version, uint16_t command) {
switch (command) {
case 0x03: {
static const C_LegacyLogin_PC_V3_03 ret{
.hardware_id = 0,
.sub_version = 0,
.is_extended = 0,
.language = Language::JAPANESE,
.unused = 0,
.serial_number2 = 1,
.access_key2 = 1,
};
return std::make_pair(&ret, sizeof(ret));
}
case 0x04:
if (is_patch(version)) {
static const C_Login_Patch_04 ret{.unused{0}, .username{1}, .password{1}, .email_address{1}};
return std::make_pair(&ret, sizeof(ret));
} else if (!is_v4(version)) {
static const C_LegacyLogin_PC_V3_04 ret{
.hardware_id = 0,
.sub_version = 0,
.is_extended = 0,
.language = Language::JAPANESE,
.unused = 0,
.serial_number = 1,
.access_key = 1,
};
return std::make_pair(&ret, sizeof(ret));
} else {
static const C_LegacyLogin_BB_04 ret{
.sub_version = 0,
.is_extended = 0,
.language = Language::JAPANESE,
.unused = 0,
.username = 1,
.password = 1};
return std::make_pair(&ret, sizeof(ret));
}
case 0x88:
if (version == Version::DC_NTE) {
static const C_Login_DCNTE_88 ret{.serial_number{1}, .access_key{1}};
return std::make_pair(&ret, sizeof(ret));
} else {
return std::make_pair(nullptr, 0);
}
case 0x8A:
if (version == Version::DC_NTE) {
static const C_ConnectionInfo_DCNTE_8A ret{
.hardware_id = 0, .sub_version = 0, .unused = 0, .username = 1, .password = 1, .email_address = 1};
return std::make_pair(&ret, sizeof(ret));
} else {
return std::make_pair(nullptr, 0);
}
case 0x8B:
if (version == Version::DC_NTE) {
static const C_Login_DCNTE_8B ret{
.player_tag = 0,
.guild_card_number = 0,
.hardware_id = 0,
.sub_version = 0,
.is_extended = 0,
.language = Language::JAPANESE,
.unused1 = 0,
.serial_number = 1,
.access_key = 1,
.username = 1,
.password = 1,
.login_character_name = 0,
.unused = 0,
};
return std::make_pair(&ret, sizeof(ret));
} else {
return std::make_pair(nullptr, 0);
}
case 0x90: {
static const C_LoginV1_DC_PC_V3_90 ret{.serial_number = 1, .access_key = 1};
return std::make_pair(&ret, sizeof(ret));
}
case 0x92: {
static const C_RegisterV1_DC_92 ret{
.hardware_id = 0,
.sub_version = 0,
.unused1 = 0,
.language = Language::JAPANESE,
.unused2 = 0,
.serial_number2 = 1,
.access_key2 = 1,
.email_address = 1,
};
return std::make_pair(&ret, sizeof(ret));
}
case 0x93:
if (!is_v4(version)) {
static const C_LoginV1_DC_93 ret{
.player_tag = 0,
.guild_card_number = 0,
.hardware_id = 0,
.sub_version = 0,
.is_extended = 0,
.language = Language::JAPANESE,
.unused1 = 0,
.serial_number = 1,
.access_key = 1,
.serial_number2 = 1,
.access_key2 = 1,
.login_character_name = 0,
.unused2 = 0,
};
return std::make_pair(&ret, sizeof(ret));
} else {
static const C_LoginBase_BB_93 ret{
.player_tag = 0,
.guild_card_number = 0,
.sub_version = 0,
.language = Language::JAPANESE,
.character_slot = 0,
.connection_phase = 0,
.client_code = 0,
.security_token = 0,
.username = 1,
.password = 1,
.menu_id = 0,
.preferred_lobby_id = 0,
};
return std::make_pair(&ret, sizeof(ret));
}
case 0x9A: {
static const C_Login_DC_PC_V3_9A ret{
.v1_serial_number = 1,
.v1_access_key = 1,
.serial_number = 1,
.access_key = 1,
.player_tag = 0,
.guild_card_number = 0,
.sub_version = 0,
.serial_number2 = 1,
.access_key2 = 1,
.email_address = 1,
};
return std::make_pair(&ret, sizeof(ret));
}
case 0x9C:
if (!is_v4(version)) {
static const C_Register_DC_PC_V3_9C ret{
.hardware_id = 0,
.sub_version = 0,
.unused1 = 0,
.language = Language::JAPANESE,
.unused2 = 0,
.serial_number = 1,
.access_key = 1,
.password = 1,
};
return std::make_pair(&ret, sizeof(ret));
} else {
static const C_Register_BB_9C ret{
.sub_version = 0,
.unused1 = 0,
.language = Language::JAPANESE,
.unused2 = 0,
.username = 1,
.password = 1,
.game_tag = 0,
};
return std::make_pair(&ret, sizeof(ret));
}
case 0x9D:
case 0x9E:
if (!is_v4(version)) {
static const C_Login_DC_PC_GC_9D ret{
.player_tag = 0,
.guild_card_number = 0,
.hardware_id = 0,
.sub_version = 0,
.is_extended = 0,
.language = Language::JAPANESE,
.unused3 = 0,
.v1_serial_number = 1,
.v1_access_key = 1,
.serial_number = 1,
.access_key = 1,
.serial_number2 = 1,
.access_key2 = 1,
.login_character_name = 0,
};
return std::make_pair(&ret, sizeof(ret));
} else {
static const C_LoginExtended_BB_9E ret{
.player_tag = 0,
.guild_card_number = 0,
.sub_version = 0,
.language32 = 0,
.unknown_a2 = 0,
.v1_serial_number = 1,
.v1_access_key = 1,
.serial_number = 1,
.access_key = 1,
.username = 1,
.password = 1,
.guild_card_number_str = 0,
.client_config = 0,
.extension{},
};
return std::make_pair(&ret, sizeof(ret));
}
case 0xDB:
if (!is_v4(version)) {
static const C_VerifyAccount_V3_DB ret{
.v1_serial_number = 1,
.v1_access_key = 1,
.serial_number = 1,
.access_key = 1,
.hardware_id = 0,
.sub_version = 0,
.serial_number2 = 1,
.access_key2 = 1,
.password = 1,
};
return std::make_pair(&ret, sizeof(ret));
} else {
static const C_VerifyAccount_BB_DB ret{
.v1_serial_number = 1,
.v1_access_key = 1,
.serial_number = 1,
.access_key = 1,
.sub_version = 0,
.username = 1,
.password = 1,
.game_tag = 0,
};
return std::make_pair(&ret, sizeof(ret));
}
default:
return std::make_pair(nullptr, 0);
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
#include <utility>
#include "Version.hh"
std::pair<const void*, size_t> censor_data_for_client_command(Version version, uint16_t command);
+9 -9
View File
@@ -318,7 +318,7 @@ struct S_ServerInitWithAfterMessageT_DC_PC_V3_02_17_91_9B {
// Internal name: SndRegist // Internal name: SndRegist
struct C_LegacyLogin_PC_V3_03 { struct C_LegacyLogin_PC_V3_03 {
/* 00 */ be_uint64_t hardware_id; /* 00 */ be_uint64_t hardware_id = 0;
/* 08 */ le_uint32_t sub_version = 0; /* 08 */ le_uint32_t sub_version = 0;
/* 0C */ uint8_t is_extended = 0; /* 0C */ uint8_t is_extended = 0;
/* 0D */ Language language = Language::JAPANESE; /* 0D */ Language language = Language::JAPANESE;
@@ -364,7 +364,7 @@ struct S_ServerInitWithAfterMessageT_BB_03_9B {
// likely a relic of an older, now-unused sequence. Like 03, this command isn't used by any known PSO version. // likely a relic of an older, now-unused sequence. Like 03, this command isn't used by any known PSO version.
struct C_LegacyLogin_PC_V3_04 { struct C_LegacyLogin_PC_V3_04 {
/* 00 */ be_uint64_t hardware_id; /* 00 */ be_uint64_t hardware_id = 0;
/* 08 */ le_uint32_t sub_version = 0; /* 08 */ le_uint32_t sub_version = 0;
/* 0C */ uint8_t is_extended = 0; /* 0C */ uint8_t is_extended = 0;
/* 0D */ Language language = Language::JAPANESE; /* 0D */ Language language = Language::JAPANESE;
@@ -1509,7 +1509,7 @@ struct S_ArrowUpdateEntry_88 {
// The server should respond with an 8A command. // The server should respond with an 8A command.
struct C_ConnectionInfo_DCNTE_8A { struct C_ConnectionInfo_DCNTE_8A {
be_uint64_t hardware_id; be_uint64_t hardware_id = 0;
le_uint32_t sub_version = 0x20; le_uint32_t sub_version = 0x20;
le_uint32_t unused = 0; le_uint32_t unused = 0;
pstring<TextEncoding::ASCII, 0x30> username; pstring<TextEncoding::ASCII, 0x30> username;
@@ -1536,7 +1536,7 @@ struct C_ConnectionInfo_DCNTE_8A {
struct C_Login_DCNTE_8B { struct C_Login_DCNTE_8B {
le_uint32_t player_tag = 0x00010000; le_uint32_t player_tag = 0x00010000;
le_uint32_t guild_card_number = 0; le_uint32_t guild_card_number = 0;
be_uint64_t hardware_id; be_uint64_t hardware_id = 0;
le_uint32_t sub_version = 0x20; le_uint32_t sub_version = 0x20;
uint8_t is_extended = 0; uint8_t is_extended = 0;
Language language = Language::JAPANESE; Language language = Language::JAPANESE;
@@ -1589,7 +1589,7 @@ struct C_LoginV1_DC_PC_V3_90 {
// 92 (C->S): Register (DC) // 92 (C->S): Register (DC)
struct C_RegisterV1_DC_92 { struct C_RegisterV1_DC_92 {
be_uint64_t hardware_id; be_uint64_t hardware_id = 0;
le_uint32_t sub_version; le_uint32_t sub_version;
uint8_t unused1 = 0; uint8_t unused1 = 0;
Language language = Language::JAPANESE; Language language = Language::JAPANESE;
@@ -1608,7 +1608,7 @@ struct C_RegisterV1_DC_92 {
struct C_LoginV1_DC_93 { struct C_LoginV1_DC_93 {
/* 00 */ le_uint32_t player_tag = 0x00010000; /* 00 */ le_uint32_t player_tag = 0x00010000;
/* 04 */ le_uint32_t guild_card_number = 0; /* 04 */ le_uint32_t guild_card_number = 0;
/* 08 */ be_uint64_t hardware_id; /* 08 */ be_uint64_t hardware_id = 0;
/* 10 */ le_uint32_t sub_version = 0; /* 10 */ le_uint32_t sub_version = 0;
/* 14 */ uint8_t is_extended = 0; /* 14 */ uint8_t is_extended = 0;
/* 15 */ Language language = Language::JAPANESE; /* 15 */ Language language = Language::JAPANESE;
@@ -1662,7 +1662,7 @@ struct C_LoginWithoutHardwareInfo_BB_93 : C_LoginBase_BB_93 {
struct C_LoginWithHardwareInfo_BB_93 : C_LoginBase_BB_93 { struct C_LoginWithHardwareInfo_BB_93 : C_LoginBase_BB_93 {
// See the comment in the above structure. This format is used on newer client versions. // See the comment in the above structure. This format is used on newer client versions.
/* 7C */ be_uint64_t hardware_id; /* 7C */ be_uint64_t hardware_id = 0;
/* 84 */ parray<uint8_t, 0x28> client_config; /* 84 */ parray<uint8_t, 0x28> client_config;
/* AC */ /* AC */
} __packed_ws__(C_LoginWithHardwareInfo_BB_93, 0xAC); } __packed_ws__(C_LoginWithHardwareInfo_BB_93, 0xAC);
@@ -1773,7 +1773,7 @@ struct C_Login_DC_PC_V3_9A {
// It appears PSO GC sends uninitialized data in the header.flag field here. // It appears PSO GC sends uninitialized data in the header.flag field here.
struct C_Register_DC_PC_V3_9C { struct C_Register_DC_PC_V3_9C {
/* 00 */ be_uint64_t hardware_id; /* 00 */ be_uint64_t hardware_id = 0;
/* 08 */ le_uint32_t sub_version = 0; /* 08 */ le_uint32_t sub_version = 0;
/* 0C */ uint8_t unused1 = 0; /* 0C */ uint8_t unused1 = 0;
/* 0D */ Language language = Language::JAPANESE; /* 0D */ Language language = Language::JAPANESE;
@@ -1819,7 +1819,7 @@ struct C_Login_DC_PC_GC_9D {
// other bytes are all zeroes. // other bytes are all zeroes.
// - V3: the hardware ID is all zeroes. // - V3: the hardware ID is all zeroes.
// On the client, this is actually an array of 8 bytes, but we treat it as a single integer for simplicity. // On the client, this is actually an array of 8 bytes, but we treat it as a single integer for simplicity.
/* 08 */ be_uint64_t hardware_id; /* 08 */ be_uint64_t hardware_id = 0;
/* 10 */ le_uint32_t sub_version = 0; /* 10 */ le_uint32_t sub_version = 0;
/* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format /* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format
/* 15 */ Language language = Language::JAPANESE; /* 15 */ Language language = Language::JAPANESE;
+6 -2
View File
@@ -128,7 +128,9 @@ asio::awaitable<void> DownloadSession::run() {
this->language, this->language,
netloc_str, netloc_str,
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END, this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END); this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END,
false,
false);
this->log.info_f("Server channel connected"); this->log.info_f("Server channel connected");
while (this->channel->connected()) { while (this->channel->connected()) {
@@ -529,7 +531,9 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
this->language, this->language,
netloc_str, netloc_str,
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END, this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END); this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END,
false,
false);
this->log.info_f("Server channel connected"); this->log.info_f("Server channel connected");
break; break;
} }
+7 -8
View File
@@ -10,9 +10,8 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
void BattleRecord::PlayerEntry::print(FILE* stream) const { void BattleRecord::PlayerEntry::print(FILE* stream) const {
// TODO: Format this nicely somehow. Maybe factor out the functions in // TODO: Format this nicely somehow. Maybe factor out the functions in QuestScript that format some of these structs
// QuestScript that format some of these structures phosg::print_data(stream, this, sizeof(*this), 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this, sizeof(*this), 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
} }
BattleRecord::Event::Event(phosg::StringReader& r) { BattleRecord::Event::Event(phosg::StringReader& r) {
@@ -103,23 +102,23 @@ void BattleRecord::Event::print(FILE* stream) const {
break; break;
case Type::BATTLE_COMMAND: case Type::BATTLE_COMMAND:
phosg::fwrite_fmt(stream, "BATTLE_COMMAND\n"); phosg::fwrite_fmt(stream, "BATTLE_COMMAND\n");
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
break; break;
case Type::GAME_COMMAND: case Type::GAME_COMMAND:
phosg::fwrite_fmt(stream, "GAME_COMMAND\n"); phosg::fwrite_fmt(stream, "GAME_COMMAND\n");
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
break; break;
case Type::EP3_GAME_COMMAND: case Type::EP3_GAME_COMMAND:
phosg::fwrite_fmt(stream, "EP3_GAME_COMMAND\n"); phosg::fwrite_fmt(stream, "EP3_GAME_COMMAND\n");
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
break; break;
case Type::CHAT_MESSAGE: case Type::CHAT_MESSAGE:
phosg::fwrite_fmt(stream, "CHAT_MESSAGE {:08X}\n", this->guild_card_number); phosg::fwrite_fmt(stream, "CHAT_MESSAGE {:08X}\n", this->guild_card_number);
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
break; break;
case Type::SERVER_DATA_COMMAND: case Type::SERVER_DATA_COMMAND:
phosg::fwrite_fmt(stream, "SERVER_DATA_COMMAND\n"); phosg::fwrite_fmt(stream, "SERVER_DATA_COMMAND\n");
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
break; break;
default: default:
throw runtime_error("unknown event type in battle record"); throw runtime_error("unknown event type in battle record");
+1 -1
View File
@@ -261,7 +261,7 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
} else if ((this->options.behavior_flags & BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING) && } else if ((this->options.behavior_flags & BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING) &&
this->log().info_f("Generated command")) { this->log().info_f("Generated command")) {
phosg::print_data(stderr, data, size, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stderr, data, size, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
} }
} }
+3 -1
View File
@@ -131,7 +131,9 @@ shared_ptr<Client> GameServer::create_client(
Language::ENGLISH, Language::ENGLISH,
"", "",
phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::FG_YELLOW,
phosg::TerminalFormat::FG_GREEN); phosg::TerminalFormat::FG_GREEN,
this->state->censor_credentials,
false);
auto c = make_shared<Client>(this->shared_from_this(), channel, listen_sock->behavior); auto c = make_shared<Client>(this->shared_from_this(), channel, listen_sock->behavior);
this->log.info_f("Client connected: C-{:X} via {}", c->id, listen_sock->name); this->log.info_f("Client connected: C-{:X} via {}", c->id, listen_sock->name);
+15 -3
View File
@@ -165,8 +165,10 @@ IPSSChannel::IPSSChannel(
Language language, Language language,
const std::string& name, const std::string& name,
phosg::TerminalFormat terminal_send_color, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color) phosg::TerminalFormat terminal_recv_color,
: Channel(version, language, name, terminal_send_color, terminal_recv_color), bool censor_received_credentials,
bool censor_sent_credentials)
: Channel(version, language, name, terminal_send_color, terminal_recv_color, censor_received_credentials, censor_sent_credentials),
sim(sim), sim(sim),
ipss_client(ipss_client), ipss_client(ipss_client),
tcp_conn(tcp_conn), tcp_conn(tcp_conn),
@@ -1364,7 +1366,17 @@ asio::awaitable<void> IPStackSimulator::open_server_connection(
} }
const auto& port_config = port_config_it->second; const auto& port_config = port_config_it->second;
conn->server_channel = make_shared<IPSSChannel>(this->shared_from_this(), c, conn, port_config->version, Language::ENGLISH); conn->server_channel = make_shared<IPSSChannel>(
this->shared_from_this(),
c,
conn,
port_config->version,
Language::ENGLISH,
"",
phosg::TerminalFormat::END,
phosg::TerminalFormat::END,
false,
this->state->censor_credentials);
if (!this->state->game_server.get()) { if (!this->state->game_server.get()) {
this->log.error_f("No server available for TCP connection {}", conn_str); this->log.error_f("No server available for TCP connection {}", conn_str);
+10 -13
View File
@@ -94,10 +94,8 @@ struct IPSSClient : std::enable_shared_from_this<IPSSClient> {
void reschedule_idle_timeout(); void reschedule_idle_timeout();
}; };
// IPSSChannel provides an "unwrapped" connection to the rest of the server. It // IPSSChannel provides an "unwrapped" connection to the rest of the server. It implements the Channel interface and
// implements the Channel interface and can be used in place of an // can be used in place of an SocketChannel, so the rest of the server doesn't have to know about IPStackSimulator.
// SocketChannel, so the rest of the server doesn't have to know about
// IPStackSimulator.
class IPSSChannel : public Channel { class IPSSChannel : public Channel {
public: public:
std::shared_ptr<IPStackSimulator> sim; std::shared_ptr<IPStackSimulator> sim;
@@ -110,18 +108,19 @@ public:
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn, std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
Version version, Version version,
Language language, Language language,
const std::string& name = "", const std::string& name,
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END, phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END); phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials);
virtual std::string default_name() const; virtual std::string default_name() const;
virtual bool connected() const; virtual bool connected() const;
virtual void disconnect(); virtual void disconnect();
// Adds inbound data, which will then be available via recv_raw(). This // Adds inbound data, which will then be available via recv_raw(). This function is called by IPStackSimulator to
// function is called by IPStackSimulator to forward "unwrapped" data to // forward "unwrapped" data to the game/proxy servers.
// the game/proxy servers.
void add_inbound_data(const void* data, size_t size); void add_inbound_data(const void* data, size_t size);
virtual void send_raw(std::string&& data); virtual void send_raw(std::string&& data);
@@ -134,9 +133,7 @@ private:
size_t recv_buf_size = 0; size_t recv_buf_size = 0;
}; };
class IPStackSimulator class IPStackSimulator : public Server<IPSSClient, IPSSSocket>, public std::enable_shared_from_this<IPStackSimulator> {
: public Server<IPSSClient, IPSSSocket>,
public std::enable_shared_from_this<IPStackSimulator> {
public: public:
IPStackSimulator(std::shared_ptr<ServerState> state); IPStackSimulator(std::shared_ptr<ServerState> state);
~IPStackSimulator() = default; ~IPStackSimulator() = default;
+6 -7
View File
@@ -3579,7 +3579,7 @@ Action a_check_quests(
phosg::fwritex(stdout, vq->map_file->disassemble(false, vq->meta.version)); phosg::fwritex(stdout, vq->map_file->disassemble(false, vq->meta.version));
phosg::log_info_f("... BINDIFF:"); phosg::log_info_f("... BINDIFF:");
phosg::print_binary_diff( phosg::print_binary_diff(
stdout, dat.data(), dat.size(), serialized.data(), serialized.size(), isatty(fileno(stdout)), 3); stdout, dat.data(), dat.size(), serialized.data(), serialized.size(), isatty(fileno(stdout)));
phosg::log_info_f("... {} {} {} ({}) MAP FAILED", phosg::log_info_f("... {} {} {} ({}) MAP FAILED",
phosg::name_for_enum(vq->meta.version), phosg::name_for_enum(vq->meta.version),
name_for_language(vq->meta.language), name_for_language(vq->meta.language),
@@ -3821,9 +3821,9 @@ Action a_diff_executables(
throw runtime_error("the two files are not the same type of executable, or are neither dol nor xbe"); throw runtime_error("the two files are not the same type of executable, or are neither dol nor xbe");
} }
for (const auto& it : result) { for (const auto& it : result) {
string b_str = phosg::format_data_string(it.b_data, nullptr, phosg::FormatDataFlags::HEX_ONLY); string b_str = phosg::format_data_string(it.b_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
if (show_pre) { if (show_pre) {
string a_str = phosg::format_data_string(it.a_data, nullptr, phosg::FormatDataFlags::HEX_ONLY); string a_str = phosg::format_data_string(it.a_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
phosg::fwrite_fmt(stdout, "{:08X}: {} => {}\n", it.address, a_str, b_str); phosg::fwrite_fmt(stdout, "{:08X}: {} => {}\n", it.address, a_str, b_str);
} else { } else {
phosg::fwrite_fmt(stdout, "{:08X} {}\n", it.address, b_str); phosg::fwrite_fmt(stdout, "{:08X} {}\n", it.address, b_str);
@@ -3962,7 +3962,7 @@ Action a_replay_ep3_battle_record(
} }
if (output_queue->empty()) { if (output_queue->empty()) {
phosg::fwrite_fmt(stderr, "Output queue is empty, but expected battle command:\n"); phosg::fwrite_fmt(stderr, "Output queue is empty, but expected battle command:\n");
phosg::print_data(stderr, ev.data, 0, nullptr, phosg::PrintDataFlags::OFFSET_16_BITS | phosg::PrintDataFlags::PRINT_ASCII); phosg::print_data(stderr, ev.data, 0, phosg::FormatDataFlags::OFFSET_16_BITS | phosg::FormatDataFlags::PRINT_ASCII);
throw std::runtime_error("Output did not match expectations"); throw std::runtime_error("Output did not match expectations");
} }
// Hack: don't check the last field in 6xB4x46 since it contains a timestamp on non-NTE // Hack: don't check the last field in 6xB4x46 since it contains a timestamp on non-NTE
@@ -3977,11 +3977,10 @@ Action a_replay_ep3_battle_record(
matched = (output_queue->front() == ev.data); matched = (output_queue->front() == ev.data);
} }
if (!matched) { if (!matched) {
const void* prev = (ev.data.size() == output_queue->front().size()) ? ev.data.data() : nullptr;
phosg::fwrite_fmt(stderr, "Output queue front did not match expected command; expected:\n"); phosg::fwrite_fmt(stderr, "Output queue front did not match expected command; expected:\n");
phosg::print_data(stderr, ev.data, 0, nullptr, phosg::PrintDataFlags::OFFSET_16_BITS | phosg::PrintDataFlags::PRINT_ASCII); phosg::print_data(stderr, ev.data, 0, phosg::FormatDataFlags::OFFSET_16_BITS | phosg::FormatDataFlags::PRINT_ASCII);
phosg::fwrite_fmt(stderr, "Received:\n"); phosg::fwrite_fmt(stderr, "Received:\n");
phosg::print_data(stderr, output_queue->front(), 0, prev, phosg::PrintDataFlags::OFFSET_16_BITS | phosg::PrintDataFlags::PRINT_ASCII); phosg::print_data(stderr, output_queue->front(), 0, ev.data, phosg::FormatDataFlags::OFFSET_16_BITS | phosg::FormatDataFlags::PRINT_ASCII);
throw std::runtime_error("Output did not match expectations"); throw std::runtime_error("Output did not match expectations");
} }
output_queue->pop_front(); output_queue->pop_front();
+6 -6
View File
@@ -6573,7 +6573,7 @@ void MapState::import_object_states_from_sync(
throw logic_error("super object link is incorrect"); throw logic_error("super object link is incorrect");
} }
if (obj_st->game_flags != entry.flags) { if (obj_st->game_flags != entry.flags) {
this->log.warning_f("({:04X} => K-{:03X}) Game flags from client ({:04X}) do not match game flags from map ({:04X})", this->log.info_f("({:04X} => K-{:03X}) Game flags from client ({:04X}) do not match game flags from map ({:04X})",
object_index, obj_st->k_id, entry.flags, obj_st->game_flags); object_index, obj_st->k_id, entry.flags, obj_st->game_flags);
obj_st->game_flags = entry.flags; obj_st->game_flags = entry.flags;
} }
@@ -6608,12 +6608,12 @@ void MapState::import_enemy_states_from_sync(Version from_version, const SyncEne
// Only set the state if it's not an alias // Only set the state if it's not an alias
if (ene_st->super_ene == ene) { if (ene_st->super_ene == ene) {
if (ene_st->game_flags != entry.flags) { if (ene_st->game_flags != entry.flags) {
this->log.warning_f("({:04X} => E-{:03X}) Game flags from client ({:08X}) do not match game flags from map ({:08X})", this->log.info_f("({:04X} => E-{:03X}) Game flags from client ({:08X}) do not match game flags from map ({:08X})",
enemy_index, ene_st->e_id, entry.flags, ene_st->game_flags); enemy_index, ene_st->e_id, entry.flags, ene_st->game_flags);
ene_st->game_flags = entry.flags; ene_st->game_flags = entry.flags;
} }
if (ene_st->total_damage != entry.total_damage) { if (ene_st->total_damage != entry.total_damage) {
this->log.warning_f("({:04X} => E-{:03X}) Total damage from client ({}) does not match total damage from map ({})", this->log.info_f("({:04X} => E-{:03X}) Total damage from client ({}) does not match total damage from map ({})",
enemy_index, ene_st->e_id, entry.total_damage, ene_st->total_damage); enemy_index, ene_st->e_id, entry.total_damage, ene_st->total_damage);
ene_st->total_damage = entry.total_damage; ene_st->total_damage = entry.total_damage;
} }
@@ -6665,7 +6665,7 @@ void MapState::import_flag_states_from_sync(
throw logic_error("super object link is incorrect"); throw logic_error("super object link is incorrect");
} }
if (obj_st->set_flags != set_flags) { if (obj_st->set_flags != set_flags) {
this->log.warning_f("({:04X} => K-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})", this->log.info_f("({:04X} => K-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})",
object_index, obj_st->k_id, set_flags, obj_st->set_flags); object_index, obj_st->k_id, set_flags, obj_st->set_flags);
obj_st->set_flags = set_flags; obj_st->set_flags = set_flags;
} }
@@ -6701,7 +6701,7 @@ void MapState::import_flag_states_from_sync(
throw logic_error("super enemy link is incorrect"); throw logic_error("super enemy link is incorrect");
} }
if (ene_st->set_flags != set_flags) { if (ene_st->set_flags != set_flags) {
this->log.warning_f("({:04X} => E-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})", this->log.info_f("({:04X} => E-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})",
enemy_set_index, ene_st->e_id, set_flags, ene_st->set_flags); enemy_set_index, ene_st->e_id, set_flags, ene_st->set_flags);
ene_st->set_flags = set_flags; ene_st->set_flags = set_flags;
} }
@@ -6733,7 +6733,7 @@ void MapState::import_flag_states_from_sync(
const auto& ev = entities.events.at(event_index - base_indexes.base_event_index); const auto& ev = entities.events.at(event_index - base_indexes.base_event_index);
auto& ev_st = this->event_states.at(fc.base_super_ids.base_event_index + ev->super_id); auto& ev_st = this->event_states.at(fc.base_super_ids.base_event_index + ev->super_id);
if (ev_st->flags != flags) { if (ev_st->flags != flags) {
this->log.warning_f("({:04X} => W-{:03X}) Set flags from client ({:04X}) do not match flags from map ({:04X})", this->log.info_f("({:04X} => W-{:03X}) Set flags from client ({:04X}) do not match flags from map ({:04X})",
event_index, ev_st->w_id, flags, ev_st->flags); event_index, ev_st->w_id, flags, ev_st->flags);
ev_st->flags = flags; ev_st->flags = flags;
} }
+6 -2
View File
@@ -64,7 +64,9 @@ asio::awaitable<void> PatchDownloadSession::run() {
Language::ENGLISH, Language::ENGLISH,
netloc_str, netloc_str,
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END, this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END); this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END,
false,
false);
this->log.info_f("Server channel connected"); this->log.info_f("Server channel connected");
while (this->channel->connected()) { while (this->channel->connected()) {
@@ -267,7 +269,9 @@ asio::awaitable<void> PatchDownloadSession::on_message(Channel::Message& msg) {
this->channel->language, this->channel->language,
netloc_str, netloc_str,
this->channel->terminal_send_color, this->channel->terminal_send_color,
this->channel->terminal_recv_color); this->channel->terminal_recv_color,
false,
false);
this->channel = new_channel; this->channel = new_channel;
old_channel->disconnect(); old_channel->disconnect();
this->log.info_f("Server channel connected"); this->log.info_f("Server channel connected");
+3 -1
View File
@@ -883,7 +883,9 @@ static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::M
old_channel->language, old_channel->language,
std::format("C-{} proxy remote server at {}", c->id, netloc_str), std::format("C-{} proxy remote server at {}", c->id, netloc_str),
old_channel->terminal_send_color, old_channel->terminal_send_color,
old_channel->terminal_recv_color); old_channel->terminal_recv_color,
old_channel->censor_received_credentials,
old_channel->censor_sent_credentials);
c->proxy_session->server_channel = new_channel; c->proxy_session->server_channel = new_channel;
asio::co_spawn(*s->io_context, handle_proxy_server_commands(c, c->proxy_session, new_channel), asio::detached); asio::co_spawn(*s->io_context, handle_proxy_server_commands(c, c->proxy_session, new_channel), asio::detached);
c->log.info_f("Server channel connected"); c->log.info_f("Server channel connected");
+12 -18
View File
@@ -107,24 +107,18 @@ static string escape_string(const string& data, TextEncoding encoding = TextEnco
} }
static string format_and_indent_data(const void* data, size_t size, uint64_t start_address) { static string format_and_indent_data(const void* data, size_t size, uint64_t start_address) {
struct iovec iov;
iov.iov_base = const_cast<void*>(data);
iov.iov_len = size;
string ret = " "; string ret = " ";
phosg::format_data( auto write_fn = [&ret](const void* vdata, size_t size) -> void {
[&ret](const void* vdata, size_t size) -> void { const char* data = reinterpret_cast<const char*>(vdata);
const char* data = reinterpret_cast<const char*>(vdata); for (size_t z = 0; z < size; z++) {
for (size_t z = 0; z < size; z++) { if (data[z] == '\n') {
if (data[z] == '\n') { ret += "\n ";
ret += "\n "; } else {
} else { ret.push_back(data[z]);
ret.push_back(data[z]); }
} }
} };
}, phosg::format_data_custom(write_fn, data, size, start_address, phosg::FormatDataFlags::PRINT_ASCII);
&iov, 1, start_address, nullptr, 0, phosg::PrintDataFlags::PRINT_ASCII);
phosg::strip_trailing_whitespace(ret); phosg::strip_trailing_whitespace(ret);
return ret; return ret;
} }
@@ -3533,7 +3527,7 @@ std::string disassemble_quest_script(
line_text = std::format(" {}", dasm_line); line_text = std::format(" {}", dasm_line);
} else { } else {
size_t opcode_size = label_r.where() - opcode_start_offset; size_t opcode_size = label_r.where() - opcode_start_offset;
string hex_data = phosg::format_data_string(label_r.preadx(opcode_start_offset, opcode_size), nullptr, phosg::FormatDataFlags::HEX_ONLY); string hex_data = phosg::format_data_string(label_r.preadx(opcode_start_offset, opcode_size), nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
if (hex_data.size() > 14) { if (hex_data.size() > 14) {
hex_data.resize(12); hex_data.resize(12);
hex_data += "..."; hex_data += "...";
+3 -1
View File
@@ -480,7 +480,9 @@ asio::awaitable<void> start_proxy_session(shared_ptr<Client> c, const string& ho
c->channel->language, c->channel->language,
std::format("C-{} proxy remote server at {}", c->id, netloc_str), std::format("C-{} proxy remote server at {}", c->id, netloc_str),
phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::FG_YELLOW,
phosg::TerminalFormat::FG_RED); phosg::TerminalFormat::FG_RED,
false,
s->censor_credentials);
c->proxy_session = make_shared<ProxySession>(channel, pc); c->proxy_session = make_shared<ProxySession>(channel, pc);
if (c->version() == Version::GC_EP3) { if (c->version() == Version::GC_EP3) {
+1 -1
View File
@@ -2247,7 +2247,7 @@ static asio::awaitable<void> on_pick_up_item_generic(
if (!l->item_exists(floor, item_id)) { if (!l->item_exists(floor, item_id)) {
// This can happen if the network is slow, and the client tries to pick up the same item multiple times. Or // This can happen if the network is slow, and the client tries to pick up the same item multiple times. Or
// multiple clients could try to pick up the same item at approximately the same time; only one should get it. // multiple clients could try to pick up the same item at approximately the same time; only one should get it.
l->log.warning_f("Player {} requests to pick up {:08X}, but the item does not exist; dropping command", client_id, item_id); l->log.info_f("Player {} requests to pick up {:08X}, but the item does not exist; dropping command", client_id, item_id);
} else { } else {
// This is handled by the server on BB, and by the leader on other versions. However, the client's logic is to // This is handled by the server on BB, and by the leader on other versions. However, the client's logic is to
+15 -7
View File
@@ -50,7 +50,15 @@ ReplaySession::Client::Client(shared_ptr<asio::io_context> io_context, uint64_t
: id(id), : id(id),
port(port), port(port),
version(version), version(version),
channel(make_shared<PeerChannel>(io_context, this->version, Language::ENGLISH, std::format("R-{:X}", this->id))) {} channel(make_shared<PeerChannel>(
io_context,
this->version,
Language::ENGLISH,
std::format("R-{:X}", this->id),
phosg::TerminalFormat::END,
phosg::TerminalFormat::END,
false,
false)) {}
string ReplaySession::Client::str() const { string ReplaySession::Client::str() const {
return std::format("Client[{}, T-{}, {}]", this->id, this->port, phosg::name_for_enum(this->version)); return std::format("Client[{}, T-{}, {}]", this->id, this->port, phosg::name_for_enum(this->version));
@@ -516,7 +524,7 @@ asio::awaitable<void> ReplaySession::run() {
"(ev-line {}) client connected to port missing from configuration", this->first_event->line_num)); "(ev-line {}) client connected to port missing from configuration", this->first_event->line_num));
} }
auto server_channel = make_shared<PeerChannel>(this->state->io_context, port_config->version, c->channel->language); auto server_channel = make_shared<PeerChannel>(this->state->io_context, port_config->version, c->channel->language, "", phosg::TerminalFormat::END, phosg::TerminalFormat::END, false, false);
PeerChannel::link_peers(c->channel, server_channel); PeerChannel::link_peers(c->channel, server_channel);
if (this->state->game_server.get()) { if (this->state->game_server.get()) {
@@ -561,24 +569,24 @@ asio::awaitable<void> ReplaySession::run() {
this->bytes_received += full_command.size(); this->bytes_received += full_command.size();
if (c->receive_events.empty()) { if (c->receive_events.empty()) {
phosg::print_data(stderr, full_command, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stderr, full_command, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
throw runtime_error("received unexpected command for client"); throw runtime_error("received unexpected command for client");
} }
auto& ev = c->receive_events.front(); auto& ev = c->receive_events.front();
if ((full_command.size() != ev->data.size()) && !ev->allow_size_disparity) { if ((full_command.size() != ev->data.size()) && !ev->allow_size_disparity) {
replay_log.error_f("Expected command:"); replay_log.error_f("Expected command:");
phosg::print_data(stderr, ev->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
replay_log.error_f("Received command:"); replay_log.error_f("Received command:");
phosg::print_data(stderr, full_command, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stderr, full_command, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
throw runtime_error(std::format("(ev-line {}) received command sizes do not match", ev->line_num)); throw runtime_error(std::format("(ev-line {}) received command sizes do not match", ev->line_num));
} }
for (size_t x = 0; x < min<size_t>(full_command.size(), ev->data.size()); x++) { for (size_t x = 0; x < min<size_t>(full_command.size(), ev->data.size()); x++) {
if ((full_command[x] & ev->mask[x]) != (ev->data[x] & ev->mask[x])) { if ((full_command[x] & ev->mask[x]) != (ev->data[x] & ev->mask[x])) {
replay_log.error_f("Expected command:"); replay_log.error_f("Expected command:");
phosg::print_data(stderr, ev->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
replay_log.error_f("Received command:"); replay_log.error_f("Received command:");
phosg::print_data(stderr, full_command, 0, ev->data.data(), phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS); phosg::print_data(stderr, full_command, 0, ev->data, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
throw runtime_error(std::format("(ev-line {}) received command data does not match expected data", ev->line_num)); throw runtime_error(std::format("(ev-line {}) received command data does not match expected data", ev->line_num));
} }
} }
+1
View File
@@ -976,6 +976,7 @@ void ServerState::load_config_early() {
this->ep3_behavior_flags = this->config_json->get_int("Episode3BehaviorFlags", 0); this->ep3_behavior_flags = this->config_json->get_int("Episode3BehaviorFlags", 0);
this->ep3_card_auction_points = this->config_json->get_int("CardAuctionPoints", 0); this->ep3_card_auction_points = this->config_json->get_int("CardAuctionPoints", 0);
this->hide_download_commands = this->config_json->get_bool("HideDownloadCommands", true); this->hide_download_commands = this->config_json->get_bool("HideDownloadCommands", true);
this->censor_credentials = this->config_json->get_bool("CensorCredentials", true);
this->proxy_allow_save_files = this->config_json->get_bool("ProxyAllowSaveFiles", true); this->proxy_allow_save_files = this->config_json->get_bool("ProxyAllowSaveFiles", true);
try { try {
+1
View File
@@ -165,6 +165,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
bool ep3_jukebox_is_free = false; bool ep3_jukebox_is_free = false;
uint32_t ep3_behavior_flags = 0; uint32_t ep3_behavior_flags = 0;
bool hide_download_commands = true; bool hide_download_commands = true;
bool censor_credentials = true;
RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT; RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT;
BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT; BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT;
bool default_switch_assist_enabled = false; bool default_switch_assist_enabled = false;
+8 -2
View File
@@ -486,8 +486,14 @@ struct pstring {
uint8_t data[Bytes]; uint8_t data[Bytes];
pstring() { pstring(uint8_t v = 0) {
memset(this->data, 0, Bytes); memset(this->data, v, Bytes);
}
pstring(const void* data, size_t size) {
memcpy(this->data, data, std::min<size_t>(size, Bytes));
if (size < Bytes) {
memset(this->data + size, 0, Bytes - size);
}
} }
pstring(const pstring<Encoding, Chars, BytesPerChar>& other) { pstring(const pstring<Encoding, Chars, BytesPerChar>& other) {
memcpy(this->data, other.data, Bytes); memcpy(this->data, other.data, Bytes);
+4
View File
@@ -204,6 +204,10 @@
// default. If you're investigating or submitting a bug report that occurs on BB clients, set this to false to get a // default. If you're investigating or submitting a bug report that occurs on BB clients, set this to false to get a
// full session log before submitting your report. // full session log before submitting your report.
"HideDownloadCommands": true, "HideDownloadCommands": true,
// Some commands include user information such as passwords and access keys. By default, these are not shown in the
// log, but in certain debugging situations you may need to log them. Set this to false to show credentials in the
// command log.
"CensorCredentials": true,
// If this option is disabled, the server only allows users who have accounts on the server to connect. If this is // If this option is disabled, the server only allows users who have accounts on the server to connect. If this is
// enabled, all users will be allowed to connect even if they don't have accounts. When a user connects with an // enabled, all users will be allowed to connect even if they don't have accounts. When a user connects with an
+1
View File
@@ -185,6 +185,7 @@
"StaticGameData": "WARNING", "StaticGameData": "WARNING",
}, },
"HideDownloadCommands": true, "HideDownloadCommands": true,
"CensorCredentials": true,
"AllowUnregisteredUsers": true, "AllowUnregisteredUsers": true,
"UseTemporaryAccountsForPrototypes": true, "UseTemporaryAccountsForPrototypes": true,