eliminate using namespace

This commit is contained in:
Martin Michelsen
2026-05-25 16:38:31 -07:00
parent 4503d09c77
commit e9c2ac34a3
98 changed files with 6022 additions and 6531 deletions
+10 -13
View File
@@ -9,10 +9,7 @@
#include "Text.hh"
using namespace std;
AFSArchive::AFSArchive(shared_ptr<const string> data)
: data(data) {
AFSArchive::AFSArchive(std::shared_ptr<const std::string> data) : data(data) {
struct FileHeader {
be_uint32_t magic;
le_uint32_t num_files;
@@ -26,7 +23,7 @@ AFSArchive::AFSArchive(shared_ptr<const string> data)
phosg::StringReader r(*this->data);
const auto& header = r.get<FileHeader>();
if (header.magic != 0x41465300) { // 'AFS\0'
throw runtime_error("file is not an AFS archive");
throw std::runtime_error("file is not an AFS archive");
}
while (this->entries.size() < header.num_files) {
@@ -35,21 +32,21 @@ AFSArchive::AFSArchive(shared_ptr<const string> data)
}
}
pair<const void*, size_t> AFSArchive::get(size_t index) const {
std::pair<const void*, size_t> AFSArchive::get(size_t index) const {
const auto& entry = this->entries.at(index);
if (entry.offset > this->data->size()) {
throw out_of_range("entry begins beyond end of archive");
throw std::out_of_range("entry begins beyond end of archive");
}
if (entry.offset + entry.size > this->data->size()) {
throw out_of_range("entry extends beyond end of archive");
throw std::out_of_range("entry extends beyond end of archive");
}
return make_pair(this->data->data() + entry.offset, entry.size);
return std::make_pair(this->data->data() + entry.offset, entry.size);
}
string AFSArchive::get_copy(size_t index) const {
std::string AFSArchive::get_copy(size_t index) const {
auto ret = this->get(index);
return string(reinterpret_cast<const char*>(ret.first), ret.second);
return std::string(reinterpret_cast<const char*>(ret.first), ret.second);
}
phosg::StringReader AFSArchive::get_reader(size_t index) const {
@@ -57,12 +54,12 @@ phosg::StringReader AFSArchive::get_reader(size_t index) const {
return phosg::StringReader(ret.first, ret.second);
}
string AFSArchive::generate(const vector<string>& files, bool big_endian) {
std::string AFSArchive::generate(const std::vector<std::string>& files, bool big_endian) {
return big_endian ? AFSArchive::generate_t<true>(files) : AFSArchive::generate_t<false>(files);
}
template <bool BE>
string AFSArchive::generate_t(const vector<string>& files) {
std::string AFSArchive::generate_t(const std::vector<std::string>& files) {
phosg::StringWriter w;
w.put_u32b(0x41465300); // 'AFS\0'
w.put<U32T<BE>>(files.size());
+188 -188
View File
@@ -10,23 +10,21 @@
#include "Account.hh"
using namespace std;
shared_ptr<DCNTELicense> DCNTELicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<DCNTELicense>();
std::shared_ptr<DCNTELicense> DCNTELicense::from_json(const phosg::JSON& json) {
auto ret = std::make_shared<DCNTELicense>();
ret->serial_number = json.get_string("SerialNumber");
ret->access_key = json.get_string("AccessKey");
if (ret->serial_number.size() > 16) {
throw runtime_error("serial number is too long");
throw std::runtime_error("serial number is too long");
}
if (ret->serial_number.empty()) {
throw runtime_error("serial number is too short");
throw std::runtime_error("serial number is too short");
}
if (ret->access_key.size() > 16) {
throw runtime_error("access key is too long");
throw std::runtime_error("access key is too long");
}
if (ret->access_key.empty()) {
throw runtime_error("access key is too short");
throw std::runtime_error("access key is too short");
}
return ret;
}
@@ -35,15 +33,15 @@ phosg::JSON DCNTELicense::json() const {
return phosg::JSON::dict({{"SerialNumber", this->serial_number}, {"AccessKey", this->access_key}});
}
shared_ptr<V1V2License> V1V2License::from_json(const phosg::JSON& json) {
auto ret = make_shared<V1V2License>();
std::shared_ptr<V1V2License> V1V2License::from_json(const phosg::JSON& json) {
auto ret = std::make_shared<V1V2License>();
ret->serial_number = json.get_int("SerialNumber");
ret->access_key = json.get_string("AccessKey");
if (ret->serial_number == 0) {
throw runtime_error("serial number is zero");
throw std::runtime_error("serial number is zero");
}
if (ret->access_key.size() != 8) {
throw runtime_error("access key length is incorrect");
throw std::runtime_error("access key length is incorrect");
}
return ret;
}
@@ -52,19 +50,19 @@ phosg::JSON V1V2License::json() const {
return phosg::JSON::dict({{"SerialNumber", this->serial_number}, {"AccessKey", this->access_key}});
}
shared_ptr<GCLicense> GCLicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<GCLicense>();
std::shared_ptr<GCLicense> GCLicense::from_json(const phosg::JSON& json) {
auto ret = std::make_shared<GCLicense>();
ret->serial_number = json.get_int("SerialNumber");
ret->access_key = json.get_string("AccessKey");
ret->password = json.get_string("Password");
if (ret->serial_number == 0) {
throw runtime_error("serial number is zero");
throw std::runtime_error("serial number is zero");
}
if (ret->access_key.size() != 12) {
throw runtime_error("access key length is incorrect");
throw std::runtime_error("access key length is incorrect");
}
if (ret->password.empty()) {
throw runtime_error("password is too short");
throw std::runtime_error("password is too short");
}
return ret;
}
@@ -77,19 +75,19 @@ phosg::JSON GCLicense::json() const {
});
}
shared_ptr<XBLicense> XBLicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<XBLicense>();
std::shared_ptr<XBLicense> XBLicense::from_json(const phosg::JSON& json) {
auto ret = std::make_shared<XBLicense>();
ret->gamertag = json.get_string("GamerTag");
ret->user_id = json.get_int("UserID");
ret->account_id = json.get_int("AccountID");
if (ret->gamertag.empty()) {
throw runtime_error("gamertag is too short");
throw std::runtime_error("gamertag is too short");
}
if (ret->user_id == 0) {
throw runtime_error("user ID is zero");
throw std::runtime_error("user ID is zero");
}
if (ret->account_id == 0) {
throw runtime_error("account ID is zero");
throw std::runtime_error("account ID is zero");
}
return ret;
}
@@ -98,21 +96,21 @@ phosg::JSON XBLicense::json() const {
return phosg::JSON::dict({{"GamerTag", this->gamertag}, {"UserID", this->user_id}, {"AccountID", this->account_id}});
}
shared_ptr<BBLicense> BBLicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<BBLicense>();
std::shared_ptr<BBLicense> BBLicense::from_json(const phosg::JSON& json) {
auto ret = std::make_shared<BBLicense>();
ret->username = json.get_string("UserName");
ret->password = json.get_string("Password");
if (ret->username.size() > 16) {
throw runtime_error("username is too long");
throw std::runtime_error("username is too long");
}
if (ret->username.empty()) {
throw runtime_error("username is too short");
throw std::runtime_error("username is too short");
}
if (ret->password.size() > 16) {
throw runtime_error("password is too long");
throw std::runtime_error("password is too long");
}
if (ret->password.empty()) {
throw runtime_error("password is too short");
throw std::runtime_error("password is too short");
}
return ret;
}
@@ -132,51 +130,51 @@ Account::Account(const phosg::JSON& json)
uint64_t format_version = 0;
try {
format_version = json.get_int("FormatVersion");
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (format_version == 0) {
// Original format - no account ID
this->account_id = json.get_int("SerialNumber");
string access_key = json.get_string("AccessKey", "");
string dc_nte_serial_number = json.get_string("DCNTESerialNumber", "");
string dc_nte_access_key = json.get_string("DCNTEAccessKey", "");
string gc_password = json.get_string("GCPassword", "");
string xb_gamertag = json.get_string("XBGamerTag", "");
std::string access_key = json.get_string("AccessKey", "");
std::string dc_nte_serial_number = json.get_string("DCNTESerialNumber", "");
std::string dc_nte_access_key = json.get_string("DCNTEAccessKey", "");
std::string gc_password = json.get_string("GCPassword", "");
std::string xb_gamertag = json.get_string("XBGamerTag", "");
uint64_t xb_user_id = json.get_int("XBUserID", 0);
uint64_t xb_account_id = json.get_int("XBAccountID", 0);
string bb_username = json.get_string("BBUsername", "");
string bb_password = json.get_string("BBPassword", "");
std::string bb_username = json.get_string("BBUsername", "");
std::string bb_password = json.get_string("BBPassword", "");
if (access_key.size() == 12) {
if (!gc_password.empty()) {
auto lic = make_shared<GCLicense>();
auto lic = std::make_shared<GCLicense>();
lic->serial_number = this->account_id;
lic->access_key = access_key;
lic->password = gc_password;
this->gc_licenses.emplace(lic->serial_number, lic);
}
} else if (access_key.size() >= 8) {
auto lic = make_shared<V1V2License>();
auto lic = std::make_shared<V1V2License>();
lic->serial_number = this->account_id;
lic->access_key = access_key.substr(0, 8);
this->dc_licenses.emplace(lic->serial_number, lic);
this->pc_licenses.emplace(lic->serial_number, lic);
}
if (!dc_nte_serial_number.empty() && !dc_nte_access_key.empty()) {
auto lic = make_shared<DCNTELicense>();
auto lic = std::make_shared<DCNTELicense>();
lic->serial_number = dc_nte_serial_number;
lic->access_key = dc_nte_access_key;
this->dc_nte_licenses.emplace(lic->serial_number, lic);
}
if (!xb_gamertag.empty() && xb_user_id && xb_account_id) {
auto lic = make_shared<XBLicense>();
auto lic = std::make_shared<XBLicense>();
lic->gamertag = xb_gamertag;
lic->user_id = xb_user_id;
lic->account_id = xb_account_id;
this->xb_licenses.emplace(lic->user_id, lic);
}
if (!bb_username.empty() && !bb_password.empty()) {
auto lic = make_shared<BBLicense>();
auto lic = std::make_shared<BBLicense>();
lic->username = bb_username;
lic->password = bb_password;
this->bb_licenses.emplace(lic->username, lic);
@@ -223,7 +221,7 @@ Account::Account(const phosg::JSON& json)
for (const auto& it : json.get_list("AutoPatchesEnabled")) {
this->auto_patches_enabled.emplace(it->as_string());
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
@@ -279,11 +277,11 @@ phosg::JSON Account::json() const {
});
}
string Account::str() const {
std::string Account::str() const {
std::string ret = std::format("Account: {:010}/{:08X}\n", this->account_id, this->account_id);
if (this->flags) {
string flags_str = "";
std::string flags_str = "";
if (this->flags == static_cast<uint32_t>(Flag::ROOT)) {
flags_str = "ROOT";
} else if (this->flags == static_cast<uint32_t>(Flag::ADMINISTRATOR)) {
@@ -334,7 +332,7 @@ string Account::str() const {
}
if (this->user_flags) {
string user_flags_str = "";
std::string user_flags_str = "";
if (this->check_user_flag(UserFlag::DISABLE_DROP_NOTIFICATION_BROADCAST)) {
user_flags_str += "DISABLE_DROP_NOTIFICATION_BROADCAST,";
}
@@ -347,8 +345,7 @@ string Account::str() const {
}
if (this->ban_end_time) {
string time_str = phosg::format_time(this->ban_end_time);
ret += std::format(" Banned until: {} ({})\n", this->ban_end_time, time_str);
ret += std::format(" Banned until: {} ({})\n", this->ban_end_time, phosg::format_time(this->ban_end_time));
}
if (this->ep3_current_meseta || this->ep3_total_meseta_earned) {
ret += std::format(" Episode 3 meseta: {} (total earned: {})\n",
@@ -399,20 +396,19 @@ string Account::str() const {
void Account::save() const {
if (!this->is_temporary) {
auto json = this->json();
string json_data = json.serialize(
std::string json_data = json.serialize(
phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::HEX_INTEGERS);
string filename = std::format("system/licenses/{:010}.json", this->account_id);
std::string filename = std::format("system/licenses/{:010}.json", this->account_id);
phosg::save_file(filename, json_data);
}
}
void Account::delete_file() const {
string filename = std::format("system/licenses/{:010}.json", this->account_id);
remove(filename.c_str());
std::filesystem::remove(std::format("system/licenses/{:010}.json", this->account_id));
}
string Login::str() const {
string ret = std::format("Account:{:08X}", this->account->account_id);
std::string Login::str() const {
std::string ret = std::format("Account:{:08X}", this->account->account_id);
if (this->account_was_created) {
ret += " (new)";
}
@@ -435,21 +431,21 @@ string Login::str() const {
}
size_t AccountIndex::count() const {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->by_account_id.size();
}
shared_ptr<Account> AccountIndex::from_account_id(uint32_t account_id) const {
std::shared_ptr<Account> AccountIndex::from_account_id(uint32_t account_id) const {
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->by_account_id.at(account_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw missing_account();
}
}
shared_ptr<Login> AccountIndex::from_dc_nte_credentials_locked(const string& serial_number, const string& access_key) {
auto login = make_shared<Login>();
std::shared_ptr<Login> AccountIndex::from_dc_nte_credentials_locked(const std::string& serial_number, const std::string& access_key) {
auto login = std::make_shared<Login>();
login->account = this->by_dc_nte_serial_number.at(serial_number);
login->dc_nte_license = login->account->dc_nte_licenses.at(serial_number);
if (login->dc_nte_license->access_key != access_key) {
@@ -461,30 +457,30 @@ shared_ptr<Login> AccountIndex::from_dc_nte_credentials_locked(const string& ser
return login;
}
shared_ptr<Login> AccountIndex::from_dc_nte_credentials(
const string& serial_number, const string& access_key, bool allow_create) {
std::shared_ptr<Login> AccountIndex::from_dc_nte_credentials(
const std::string& serial_number, const std::string& access_key, bool allow_create) {
if (serial_number.empty()) {
throw no_username();
}
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->from_dc_nte_credentials_locked(serial_number, access_key);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
unique_lock g(this->lock);
std::unique_lock g(this->lock);
try {
return this->from_dc_nte_credentials_locked(serial_number, access_key);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (allow_create) {
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = phosg::fnv1a32(serial_number) & 0x7FFFFFFF;
auto lic = make_shared<DCNTELicense>();
auto lic = std::make_shared<DCNTELicense>();
lic->serial_number = serial_number;
lic->access_key = access_key;
login->account->dc_nte_licenses.emplace(lic->serial_number, lic);
@@ -496,9 +492,9 @@ shared_ptr<Login> AccountIndex::from_dc_nte_credentials(
}
}
shared_ptr<Login> AccountIndex::from_dc_credentials_locked(
uint32_t serial_number, const string& access_key, const string& character_name) {
auto login = make_shared<Login>();
std::shared_ptr<Login> AccountIndex::from_dc_credentials_locked(
uint32_t serial_number, const std::string& access_key, const std::string& character_name) {
auto login = std::make_shared<Login>();
login->account = this->by_dc_serial_number.at(serial_number);
login->dc_license = login->account->dc_licenses.at(serial_number);
bool is_shared = login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT);
@@ -514,30 +510,30 @@ shared_ptr<Login> AccountIndex::from_dc_credentials_locked(
return login;
}
shared_ptr<Login> AccountIndex::from_dc_credentials(
uint32_t serial_number, const string& access_key, const string& character_name, bool allow_create) {
std::shared_ptr<Login> AccountIndex::from_dc_credentials(
uint32_t serial_number, const std::string& access_key, const std::string& character_name, bool allow_create) {
if (serial_number == 0) {
throw no_username();
}
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->from_dc_credentials_locked(serial_number, access_key, character_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
unique_lock g(this->lock);
std::unique_lock g(this->lock);
try {
return this->from_dc_credentials_locked(serial_number, access_key, character_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (allow_create) {
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = serial_number;
auto lic = make_shared<V1V2License>();
auto lic = std::make_shared<V1V2License>();
lic->serial_number = serial_number;
lic->access_key = access_key;
login->account->dc_licenses.emplace(lic->serial_number, lic);
@@ -549,19 +545,19 @@ shared_ptr<Login> AccountIndex::from_dc_credentials(
}
}
shared_ptr<Login> AccountIndex::from_pc_nte_credentials(uint32_t guild_card_number, bool allow_create) {
std::shared_ptr<Login> AccountIndex::from_pc_nte_credentials(uint32_t guild_card_number, bool allow_create) {
if (!allow_create) {
throw missing_account();
}
if (guild_card_number == 0xFFFFFFFF) {
guild_card_number = phosg::random_object<uint32_t>() & 0x7FFFFFFF;
}
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = guild_card_number;
login->account->is_temporary = true;
auto lic = make_shared<V1V2License>();
auto lic = std::make_shared<V1V2License>();
lic->serial_number = guild_card_number;
login->account->pc_licenses.emplace(lic->serial_number, lic);
login->pc_license = lic;
@@ -569,9 +565,9 @@ shared_ptr<Login> AccountIndex::from_pc_nte_credentials(uint32_t guild_card_numb
return login;
}
shared_ptr<Login> AccountIndex::from_pc_credentials_locked(
uint32_t serial_number, const string& access_key, const string& character_name) {
auto login = make_shared<Login>();
std::shared_ptr<Login> AccountIndex::from_pc_credentials_locked(
uint32_t serial_number, const std::string& access_key, const std::string& character_name) {
auto login = std::make_shared<Login>();
login->account = this->by_pc_serial_number.at(serial_number);
login->pc_license = login->account->pc_licenses.at(serial_number);
bool is_shared = login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT);
@@ -587,30 +583,30 @@ shared_ptr<Login> AccountIndex::from_pc_credentials_locked(
return login;
}
shared_ptr<Login> AccountIndex::from_pc_credentials(
uint32_t serial_number, const string& access_key, const string& character_name, bool allow_create) {
std::shared_ptr<Login> AccountIndex::from_pc_credentials(
uint32_t serial_number, const std::string& access_key, const std::string& character_name, bool allow_create) {
if (serial_number == 0) {
throw no_username();
}
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->from_pc_credentials_locked(serial_number, access_key, character_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
unique_lock g(this->lock);
std::unique_lock g(this->lock);
try {
return this->from_pc_credentials_locked(serial_number, access_key, character_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (allow_create) {
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = serial_number;
auto lic = make_shared<V1V2License>();
auto lic = std::make_shared<V1V2License>();
lic->serial_number = serial_number;
lic->access_key = access_key;
login->account->pc_licenses.emplace(lic->serial_number, lic);
@@ -622,9 +618,12 @@ shared_ptr<Login> AccountIndex::from_pc_credentials(
}
}
shared_ptr<Login> AccountIndex::from_gc_credentials_locked(
uint32_t serial_number, const string& access_key, const string* password, const string& character_name) {
auto login = make_shared<Login>();
std::shared_ptr<Login> AccountIndex::from_gc_credentials_locked(
uint32_t serial_number,
const std::string& access_key,
const std::string* password,
const std::string& character_name) {
auto login = std::make_shared<Login>();
login->account = this->by_gc_serial_number.at(serial_number);
login->gc_license = login->account->gc_licenses.at(serial_number);
bool is_shared = login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT);
@@ -643,34 +642,34 @@ shared_ptr<Login> AccountIndex::from_gc_credentials_locked(
return login;
}
shared_ptr<Login> AccountIndex::from_gc_credentials(
std::shared_ptr<Login> AccountIndex::from_gc_credentials(
uint32_t serial_number,
const string& access_key,
const string* password,
const string& character_name,
const std::string& access_key,
const std::string* password,
const std::string& character_name,
bool allow_create) {
if (serial_number == 0) {
throw no_username();
}
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->from_gc_credentials_locked(serial_number, access_key, password, character_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
unique_lock g(this->lock);
std::unique_lock g(this->lock);
try {
return this->from_gc_credentials_locked(serial_number, access_key, password, character_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (allow_create && password) {
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = serial_number;
auto lic = make_shared<GCLicense>();
auto lic = std::make_shared<GCLicense>();
lic->serial_number = serial_number;
lic->access_key = access_key;
lic->password = *password;
@@ -683,8 +682,8 @@ shared_ptr<Login> AccountIndex::from_gc_credentials(
}
}
shared_ptr<Login> AccountIndex::from_xb_credentials_locked(uint64_t user_id) {
auto login = make_shared<Login>();
std::shared_ptr<Login> AccountIndex::from_xb_credentials_locked(uint64_t user_id) {
auto login = std::make_shared<Login>();
login->account = this->by_xb_user_id.at(user_id);
login->xb_license = login->account->xb_licenses.at(user_id);
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
@@ -693,30 +692,30 @@ shared_ptr<Login> AccountIndex::from_xb_credentials_locked(uint64_t user_id) {
return login;
}
shared_ptr<Login> AccountIndex::from_xb_credentials(
const string& gamertag, uint64_t user_id, uint64_t account_id, bool allow_create) {
std::shared_ptr<Login> AccountIndex::from_xb_credentials(
const std::string& gamertag, uint64_t user_id, uint64_t account_id, bool allow_create) {
if (user_id == 0 || account_id == 0) {
throw incorrect_access_key();
}
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->from_xb_credentials_locked(user_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
unique_lock g(this->lock);
std::unique_lock g(this->lock);
try {
return this->from_xb_credentials_locked(user_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (allow_create) {
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = phosg::fnv1a32(gamertag) & 0x7FFFFFFF;
auto lic = make_shared<XBLicense>();
auto lic = std::make_shared<XBLicense>();
lic->gamertag = gamertag;
lic->user_id = user_id;
lic->account_id = account_id;
@@ -729,8 +728,9 @@ shared_ptr<Login> AccountIndex::from_xb_credentials(
}
}
shared_ptr<Login> AccountIndex::from_bb_credentials_locked(const string& username, const string* password) {
auto login = make_shared<Login>();
std::shared_ptr<Login> AccountIndex::from_bb_credentials_locked(
const std::string& username, const std::string* password) {
auto login = std::make_shared<Login>();
login->account = this->by_bb_username.at(username);
login->bb_license = login->account->bb_licenses.at(username);
if (password && (login->bb_license->password != *password)) {
@@ -742,30 +742,30 @@ shared_ptr<Login> AccountIndex::from_bb_credentials_locked(const string& usernam
return login;
}
shared_ptr<Login> AccountIndex::from_bb_credentials(
const string& username, const string* password, bool allow_create) {
std::shared_ptr<Login> AccountIndex::from_bb_credentials(
const std::string& username, const std::string* password, bool allow_create) {
if (username.empty() || (password && password->empty())) {
throw no_username();
}
try {
shared_lock g(this->lock);
std::shared_lock g(this->lock);
return this->from_bb_credentials_locked(username, password);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
unique_lock g(this->lock);
std::unique_lock g(this->lock);
try {
return this->from_bb_credentials_locked(username, password);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (allow_create && password) {
auto login = make_shared<Login>();
auto login = std::make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account = std::make_shared<Account>();
login->account->account_id = phosg::fnv1a32(username) & 0x7FFFFFFF;
auto lic = make_shared<BBLicense>();
auto lic = std::make_shared<BBLicense>();
lic->username = username;
lic->password = *password;
login->account->bb_licenses.emplace(lic->username, lic);
@@ -777,9 +777,9 @@ shared_ptr<Login> AccountIndex::from_bb_credentials(
}
}
vector<shared_ptr<Account>> AccountIndex::all() const {
shared_lock g(this->lock);
vector<shared_ptr<Account>> ret;
std::vector<std::shared_ptr<Account>> AccountIndex::all() const {
std::shared_lock g(this->lock);
std::vector<std::shared_ptr<Account>> ret;
ret.reserve(this->by_account_id.size());
for (const auto& it : this->by_account_id) {
ret.emplace_back(it.second);
@@ -787,44 +787,44 @@ vector<shared_ptr<Account>> AccountIndex::all() const {
return ret;
}
void AccountIndex::add(shared_ptr<Account> a) {
unique_lock g(this->lock);
void AccountIndex::add(std::shared_ptr<Account> a) {
std::unique_lock g(this->lock);
this->add_locked(a);
}
void AccountIndex::add_locked(shared_ptr<Account> a) {
void AccountIndex::add_locked(std::shared_ptr<Account> a) {
if (this->force_all_temporary) {
a->is_temporary = true;
}
for (const auto& it : a->dc_nte_licenses) {
if (this->by_dc_nte_serial_number.count(it.second->serial_number)) {
throw runtime_error("account already exists with this DC NTE serial number");
throw std::runtime_error("account already exists with this DC NTE serial number");
}
}
for (const auto& it : a->dc_licenses) {
if (this->by_dc_serial_number.count(it.second->serial_number)) {
throw runtime_error("account already exists with this DC serial number");
throw std::runtime_error("account already exists with this DC serial number");
}
}
for (const auto& it : a->pc_licenses) {
if (this->by_pc_serial_number.count(it.second->serial_number)) {
throw runtime_error("account already exists with this PC NTE serial number");
throw std::runtime_error("account already exists with this PC NTE serial number");
}
}
for (const auto& it : a->gc_licenses) {
if (this->by_gc_serial_number.count(it.second->serial_number)) {
throw runtime_error("account already exists with this GC serial number");
throw std::runtime_error("account already exists with this GC serial number");
}
}
for (const auto& it : a->xb_licenses) {
if (this->by_xb_user_id.count(it.second->user_id)) {
throw runtime_error("account already exists with this XB user ID");
throw std::runtime_error("account already exists with this XB user ID");
}
}
for (const auto& it : a->bb_licenses) {
if (this->by_bb_username.count(it.second->username)) {
throw runtime_error("account already exists with this BB username");
throw std::runtime_error("account already exists with this BB username");
}
}
@@ -854,10 +854,10 @@ void AccountIndex::add_locked(shared_ptr<Account> a) {
}
void AccountIndex::remove(uint32_t account_id) {
unique_lock g(this->lock);
std::unique_lock g(this->lock);
auto acc_it = this->by_account_id.find(account_id);
if (acc_it == this->by_account_id.end()) {
throw out_of_range("account does not exist");
throw std::out_of_range("account does not exist");
}
auto a = std::move(acc_it->second);
this->by_account_id.erase(acc_it);
@@ -882,135 +882,135 @@ void AccountIndex::remove(uint32_t account_id) {
}
}
void AccountIndex::add_dc_nte_license(shared_ptr<Account> account, shared_ptr<DCNTELicense> license) {
void AccountIndex::add_dc_nte_license(std::shared_ptr<Account> account, std::shared_ptr<DCNTELicense> license) {
if (!this->by_dc_nte_serial_number.emplace(license->serial_number, account).second) {
throw runtime_error("serial number already registered");
throw std::runtime_error("serial number already registered");
}
if (!account->dc_nte_licenses.emplace(license->serial_number, license).second) {
this->by_dc_nte_serial_number.erase(license->serial_number);
throw logic_error("serial number registered in account but not in account index");
throw std::logic_error("serial number registered in account but not in account index");
}
}
void AccountIndex::add_dc_license(shared_ptr<Account> account, shared_ptr<V1V2License> license) {
void AccountIndex::add_dc_license(std::shared_ptr<Account> account, std::shared_ptr<V1V2License> license) {
if (!this->by_dc_serial_number.emplace(license->serial_number, account).second) {
throw runtime_error("serial number already registered");
throw std::runtime_error("serial number already registered");
}
if (!account->dc_licenses.emplace(license->serial_number, license).second) {
this->by_dc_serial_number.erase(license->serial_number);
throw logic_error("serial number registered in account but not in account index");
throw std::logic_error("serial number registered in account but not in account index");
}
}
void AccountIndex::add_pc_license(shared_ptr<Account> account, shared_ptr<V1V2License> license) {
void AccountIndex::add_pc_license(std::shared_ptr<Account> account, std::shared_ptr<V1V2License> license) {
if (!this->by_pc_serial_number.emplace(license->serial_number, account).second) {
throw runtime_error("serial number already registered");
throw std::runtime_error("serial number already registered");
}
if (!account->pc_licenses.emplace(license->serial_number, license).second) {
this->by_pc_serial_number.erase(license->serial_number);
throw logic_error("serial number registered in account but not in account index");
throw std::logic_error("serial number registered in account but not in account index");
}
}
void AccountIndex::add_gc_license(shared_ptr<Account> account, shared_ptr<GCLicense> license) {
void AccountIndex::add_gc_license(std::shared_ptr<Account> account, std::shared_ptr<GCLicense> license) {
if (!this->by_gc_serial_number.emplace(license->serial_number, account).second) {
throw runtime_error("serial number already registered");
throw std::runtime_error("serial number already registered");
}
if (!account->gc_licenses.emplace(license->serial_number, license).second) {
this->by_gc_serial_number.erase(license->serial_number);
throw logic_error("serial number registered in account but not in account index");
throw std::logic_error("serial number registered in account but not in account index");
}
}
void AccountIndex::add_xb_license(shared_ptr<Account> account, shared_ptr<XBLicense> license) {
void AccountIndex::add_xb_license(std::shared_ptr<Account> account, std::shared_ptr<XBLicense> license) {
if (!this->by_xb_user_id.emplace(license->user_id, account).second) {
throw runtime_error("user ID already registered");
throw std::runtime_error("user ID already registered");
}
if (!account->xb_licenses.emplace(license->user_id, license).second) {
this->by_xb_user_id.erase(license->user_id);
throw logic_error("user ID registered in account but not in account index");
throw std::logic_error("user ID registered in account but not in account index");
}
}
void AccountIndex::add_bb_license(shared_ptr<Account> account, shared_ptr<BBLicense> license) {
void AccountIndex::add_bb_license(std::shared_ptr<Account> account, std::shared_ptr<BBLicense> license) {
if (!this->by_bb_username.emplace(license->username, account).second) {
throw runtime_error("username already registered");
throw std::runtime_error("username already registered");
}
if (!account->bb_licenses.emplace(license->username, license).second) {
this->by_bb_username.erase(license->username);
throw logic_error("username registered in account but not in account index");
throw std::logic_error("username registered in account but not in account index");
}
}
void AccountIndex::remove_dc_nte_license(shared_ptr<Account> account, const string& serial_number) {
void AccountIndex::remove_dc_nte_license(std::shared_ptr<Account> account, const std::string& serial_number) {
auto it = account->dc_nte_licenses.find(serial_number);
if (it == account->dc_nte_licenses.end()) {
throw runtime_error("license not registered to account");
throw std::runtime_error("license not registered to account");
}
if (!this->by_dc_nte_serial_number.erase(it->second->serial_number)) {
throw runtime_error("license registered in account but not in account index");
throw std::runtime_error("license registered in account but not in account index");
}
account->dc_nte_licenses.erase(it);
}
void AccountIndex::remove_dc_license(shared_ptr<Account> account, uint32_t serial_number) {
void AccountIndex::remove_dc_license(std::shared_ptr<Account> account, uint32_t serial_number) {
auto it = account->dc_licenses.find(serial_number);
if (it == account->dc_licenses.end()) {
throw runtime_error("license not registered to account");
throw std::runtime_error("license not registered to account");
}
if (!this->by_dc_serial_number.erase(it->second->serial_number)) {
throw runtime_error("license registered in account but not in account index");
throw std::runtime_error("license registered in account but not in account index");
}
account->dc_licenses.erase(it);
}
void AccountIndex::remove_pc_license(shared_ptr<Account> account, uint32_t serial_number) {
void AccountIndex::remove_pc_license(std::shared_ptr<Account> account, uint32_t serial_number) {
auto it = account->pc_licenses.find(serial_number);
if (it == account->pc_licenses.end()) {
throw runtime_error("license not registered to account");
throw std::runtime_error("license not registered to account");
}
if (!this->by_pc_serial_number.erase(it->second->serial_number)) {
throw runtime_error("license registered in account but not in account index");
throw std::runtime_error("license registered in account but not in account index");
}
account->pc_licenses.erase(it);
}
void AccountIndex::remove_gc_license(shared_ptr<Account> account, uint32_t serial_number) {
void AccountIndex::remove_gc_license(std::shared_ptr<Account> account, uint32_t serial_number) {
auto it = account->gc_licenses.find(serial_number);
if (it == account->gc_licenses.end()) {
throw runtime_error("license not registered to account");
throw std::runtime_error("license not registered to account");
}
if (!this->by_gc_serial_number.erase(it->second->serial_number)) {
throw runtime_error("license registered in account but not in account index");
throw std::runtime_error("license registered in account but not in account index");
}
account->gc_licenses.erase(it);
}
void AccountIndex::remove_xb_license(shared_ptr<Account> account, uint64_t user_id) {
void AccountIndex::remove_xb_license(std::shared_ptr<Account> account, uint64_t user_id) {
auto it = account->xb_licenses.find(user_id);
if (it == account->xb_licenses.end()) {
throw runtime_error("license not registered to account");
throw std::runtime_error("license not registered to account");
}
if (!this->by_xb_user_id.erase(it->second->user_id)) {
throw runtime_error("license registered in account but not in account index");
throw std::runtime_error("license registered in account but not in account index");
}
account->xb_licenses.erase(it);
}
void AccountIndex::remove_bb_license(shared_ptr<Account> account, const string& username) {
void AccountIndex::remove_bb_license(std::shared_ptr<Account> account, const std::string& username) {
auto it = account->bb_licenses.find(username);
if (it == account->bb_licenses.end()) {
throw runtime_error("license not registered to account");
throw std::runtime_error("license not registered to account");
}
if (!this->by_bb_username.erase(it->second->username)) {
throw runtime_error("license registered in account but not in account index");
throw std::runtime_error("license registered in account but not in account index");
}
account->bb_licenses.erase(it);
}
shared_ptr<Account> AccountIndex::create_temporary_account_for_shared_account(
shared_ptr<const Account> src_a, const string& variation_data) const {
auto ret = make_shared<Account>(*src_a);
std::shared_ptr<Account> AccountIndex::create_temporary_account_for_shared_account(
std::shared_ptr<const Account> src_a, const std::string& variation_data) const {
auto ret = std::make_shared<Account>(*src_a);
ret->is_temporary = true;
ret->account_id = phosg::fnv1a32(&src_a->account_id, sizeof(src_a->account_id));
ret->account_id = phosg::fnv1a32(variation_data, ret->account_id);
@@ -1023,12 +1023,12 @@ AccountIndex::AccountIndex(bool force_all_temporary) : force_all_temporary(force
std::filesystem::create_directories("system/licenses");
} else {
for (const auto& item : std::filesystem::directory_iterator("system/licenses")) {
string filename = item.path().filename().string();
std::string filename = item.path().filename().string();
if (filename.ends_with(".json")) {
try {
phosg::JSON json = phosg::JSON::parse(phosg::load_file("system/licenses/" + filename));
this->add(make_shared<Account>(json));
} catch (const exception& e) {
this->add(std::make_shared<Account>(json));
} catch (const std::exception& e) {
phosg::log_error_f("Failed to index account {}", filename);
throw;
}
+104 -106
View File
@@ -14,8 +14,6 @@
#include "Text.hh"
#include "Types.hh"
using namespace std;
class AddressTranslator {
public:
enum class ExpandMethod {
@@ -63,7 +61,7 @@ public:
case ExpandMethod::RAW_BOTH:
return "RAW_BOTH";
default:
throw logic_error("invalid expand method");
throw std::logic_error("invalid expand method");
}
}
@@ -85,7 +83,7 @@ public:
case ExpandMethod::RAW_BOTH:
return false;
default:
throw logic_error("invalid expand method");
throw std::logic_error("invalid expand method");
}
}
@@ -107,46 +105,46 @@ public:
case ExpandMethod::RAW_BOTH:
return false;
default:
throw logic_error("invalid expand method");
throw std::logic_error("invalid expand method");
}
}
AddressTranslator(const string& directory)
AddressTranslator(const std::string& directory)
: log("[addr-trans] "),
directory(directory) {
while (this->directory.ends_with("/")) {
this->directory.pop_back();
}
for (const auto& item : std::filesystem::directory_iterator(this->directory)) {
string filename = item.path().filename().string();
std::string filename = item.path().filename().string();
if (filename.size() < 4) {
continue;
}
string name = filename.substr(0, filename.size() - 4);
string path = directory + "/" + filename;
std::string name = filename.substr(0, filename.size() - 4);
std::string path = directory + "/" + filename;
if (filename.ends_with(".dol")) {
ResourceDASM::DOLFile dol(path.c_str());
auto mem = make_shared<ResourceDASM::MemoryContext>();
auto mem = std::make_shared<ResourceDASM::MemoryContext>();
dol.load_into(mem);
this->mems.emplace(name, mem);
this->ppc_mems.emplace(mem);
this->log.info_f("Loaded {}", name);
} else if (filename.ends_with(".xbe")) {
ResourceDASM::XBEFile xbe(path.c_str());
auto mem = make_shared<ResourceDASM::MemoryContext>();
auto mem = std::make_shared<ResourceDASM::MemoryContext>();
xbe.load_into(mem);
this->mems.emplace(name, mem);
this->log.info_f("Loaded {}", name);
} else if (filename.ends_with(".exe")) {
ResourceDASM::PEFile pe(path.c_str());
auto mem = make_shared<ResourceDASM::MemoryContext>();
auto mem = std::make_shared<ResourceDASM::MemoryContext>();
pe.load_into(mem);
this->mems.emplace(name, mem);
this->log.info_f("Loaded {}", name);
} else if (filename.ends_with(".bin")) {
string data = phosg::load_file(path);
auto mem = make_shared<ResourceDASM::MemoryContext>();
std::string data = phosg::load_file(path);
auto mem = std::make_shared<ResourceDASM::MemoryContext>();
mem->allocate_at(0x8C010000, data.size());
mem->memcpy(0x8C010000, data.data(), data.size());
this->mems.emplace(name, mem);
@@ -156,10 +154,10 @@ public:
}
~AddressTranslator() = default;
const string& get_source_filename() const {
const std::string& get_source_filename() const {
return this->src_filename;
}
void set_source_file(const string& filename) {
void set_source_file(const std::string& filename) {
this->src_filename = filename;
this->src_mem = this->mems.at(this->src_filename);
}
@@ -178,25 +176,25 @@ public:
uint32_t opcode = r.get_u32b();
if ((opcode & 0xFFFF0000) == 0x3DA00000) {
if (r13_high_found) {
throw runtime_error("multiple values for r13_high");
throw std::runtime_error("multiple values for r13_high");
}
r13_high_found = true;
r13 |= (opcode << 16);
} else if ((opcode & 0xFFFF0000) == 0x3C400000) {
if (r2_high_found) {
throw runtime_error("multiple values for r2_high");
throw std::runtime_error("multiple values for r2_high");
}
r2_high_found = true;
r2 |= (opcode << 16);
} else if ((opcode & 0xFFFF0000) == 0x61AD0000) {
if (r13_low_found) {
throw runtime_error("multiple values for r13_low");
throw std::runtime_error("multiple values for r13_low");
}
r13_low_found = true;
r13 |= (opcode & 0xFFFF);
} else if ((opcode & 0xFFFF0000) == 0x60420000) {
if (r2_low_found) {
throw runtime_error("multiple values for r2_low");
throw std::runtime_error("multiple values for r2_low");
}
r2_low_found = true;
r2 |= (opcode & 0xFFFF);
@@ -217,11 +215,11 @@ public:
}
struct ParseDATConstructorTableSpec {
string src_name;
std::string src_name;
uint32_t index_addr;
size_t num_areas;
bool has_names;
vector<uint32_t> x86_constructor_calls;
std::vector<uint32_t> x86_constructor_calls;
ParseDATConstructorTableSpec(const phosg::JSON& json) {
this->src_name = json.at("SourceName").as_string();
@@ -233,8 +231,8 @@ public:
}
}
static vector<ParseDATConstructorTableSpec> from_json_list(const phosg::JSON& json) {
vector<ParseDATConstructorTableSpec> ret;
static std::vector<ParseDATConstructorTableSpec> from_json_list(const phosg::JSON& json) {
std::vector<ParseDATConstructorTableSpec> ret;
for (const auto& z : json.as_list()) {
ret.emplace_back(*z);
}
@@ -267,25 +265,25 @@ public:
// Returns {type: {constructor_addr: [(start_area, end_area), ...]}}
template <typename EntryT>
map<uint32_t, map<uint32_t, vector<pair<size_t, size_t>>>> parse_dat_constructor_table_t(
shared_ptr<const ResourceDASM::MemoryContext>& mem, const ParseDATConstructorTableSpec& spec) {
std::map<uint32_t, std::map<uint32_t, std::vector<std::pair<size_t, size_t>>>> parse_dat_constructor_table_t(
std::shared_ptr<const ResourceDASM::MemoryContext>& mem, const ParseDATConstructorTableSpec& spec) {
if (!mem) {
throw runtime_error("no file selected");
throw std::runtime_error("no file selected");
}
// On some of the x86 builds of the game (PCv2 and Xbox), the constructor tables aren't entirely static in the data
// sections - some parts are written during static initialization instead. To handle this, we make a copy of the
// immutable MemoryContext and run the static initialization functions using resource_dasm's emulator before
// parsing the constructor table.
shared_ptr<const ResourceDASM::MemoryContext> effective_mem = mem;
std::shared_ptr<const ResourceDASM::MemoryContext> effective_mem = mem;
if (!spec.x86_constructor_calls.empty()) {
auto constructed_mem = make_shared<ResourceDASM::MemoryContext>(mem->duplicate());
auto constructed_mem = std::make_shared<ResourceDASM::MemoryContext>(mem->duplicate());
uint32_t esp = constructed_mem->allocate(0x1000) + 0x1000;
for (uint32_t constructor_addr : spec.x86_constructor_calls) {
ResourceDASM::X86Emulator emu(constructed_mem);
// Uncomment for debugging
// auto debugger = make_shared<ResourceDASM::EmulatorDebugger<ResourceDASM::X86Emulator>>();
// auto debugger = std::make_shared<ResourceDASM::EmulatorDebugger<ResourceDASM::X86Emulator>>();
// debugger->bind(emu);
// debugger->state.mode = ResourceDASM::DebuggerMode::TRACE;
@@ -295,7 +293,7 @@ public:
constructed_mem->write_u32l(esp - 4, 0xFFFFFFFF); // Return addr
try {
emu.execute();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
if (regs.eip != 0xFFFFFFFF) {
throw;
}
@@ -304,7 +302,7 @@ public:
effective_mem = constructed_mem;
}
map<uint32_t, map<uint32_t, vector<pair<size_t, size_t>>>> table;
std::map<uint32_t, std::map<uint32_t, std::vector<std::pair<size_t, size_t>>>> table;
auto index_r = effective_mem->reader(spec.index_addr, spec.num_areas * sizeof(uint32_t));
for (size_t area = 0; area < spec.num_areas; area++) {
@@ -322,18 +320,18 @@ public:
if (!group.empty() && (group.back().second == (area - 1))) {
group.back().second = area;
} else {
group.emplace_back(make_pair(area, area));
group.emplace_back(std::make_pair(area, area));
}
}
if (entries_r.eof()) {
throw runtime_error("did not find end-of-entries marker");
throw std::runtime_error("did not find end-of-entries marker");
}
}
return table;
}
static uint64_t area_mask_for_ranges(const vector<pair<size_t, size_t>>& ranges) {
static uint64_t area_mask_for_ranges(const std::vector<std::pair<size_t, size_t>>& ranges) {
uint64_t ret = 0;
for (const auto& [start, end] : ranges) {
for (size_t z = start; z <= end; z++) {
@@ -344,7 +342,7 @@ public:
}
void parse_dat_constructor_table(const ParseDATConstructorTableSpec& spec) {
map<uint32_t, map<uint32_t, vector<pair<size_t, size_t>>>> table;
std::map<uint32_t, std::map<uint32_t, std::vector<std::pair<size_t, size_t>>>> table;
auto spec_mem = this->mems.at(spec.src_name);
if (this->ppc_mems.count(spec_mem)) {
table = this->parse_dat_constructor_table_t<DATConstructorTableEntry<true>>(spec_mem, spec);
@@ -374,10 +372,10 @@ public:
}
void parse_dat_constructor_table_multi(
const vector<ParseDATConstructorTableSpec>& specs, bool is_enemies, bool print_area_masks) {
map<string, map<uint32_t, map<uint32_t, vector<pair<size_t, size_t>>>>> all_tables;
const std::vector<ParseDATConstructorTableSpec>& specs, bool is_enemies, bool print_area_masks) {
std::map<std::string, std::map<uint32_t, std::map<uint32_t, std::vector<std::pair<size_t, size_t>>>>> all_tables;
for (const auto& spec : specs) {
map<uint32_t, map<uint32_t, vector<pair<size_t, size_t>>>> table;
std::map<uint32_t, std::map<uint32_t, std::vector<std::pair<size_t, size_t>>>> table;
auto spec_mem = this->mems.at(spec.src_name);
if (this->ppc_mems.count(spec_mem)) {
table = this->parse_dat_constructor_table_t<DATConstructorTableEntry<true>>(spec_mem, spec);
@@ -389,14 +387,14 @@ public:
all_tables.emplace(spec.src_name, std::move(table));
}
map<string, size_t> version_widths;
map<uint32_t, map<string, string>> formatted_cells_for_type;
std::map<std::string, size_t> version_widths;
std::map<uint32_t, std::map<std::string, std::string>> formatted_cells_for_type;
for (const auto& spec : specs) {
const auto& table = all_tables.at(spec.src_name);
size_t max_width = 0;
for (const auto& [type, constructor_to_area_ranges] : table) {
string cell_data;
std::string cell_data;
for (const auto& [constructor, area_ranges] : constructor_to_area_ranges) {
if (!cell_data.empty()) {
cell_data.push_back(' ');
@@ -417,14 +415,14 @@ public:
}
}
}
max_width = max<size_t>(max_width, cell_data.size());
max_width = std::max<size_t>(max_width, cell_data.size());
formatted_cells_for_type[type][spec.src_name] = std::move(cell_data);
}
version_widths[spec.src_name] = max_width;
}
vector<string> formatted_lines;
string header_line = "TYPE =>";
std::vector<std::string> formatted_lines;
std::string header_line = "TYPE =>";
for (const auto& spec : specs) {
size_t width = version_widths.at(spec.src_name);
header_line.push_back(' ');
@@ -436,7 +434,7 @@ public:
header_line += " NAME";
for (const auto& [type, formatted_cells] : formatted_cells_for_type) {
string line = std::format("{:04X} =>", type);
std::string line = std::format("{:04X} =>", type);
for (const auto& spec : specs) {
size_t width = version_widths.at(spec.src_name);
try {
@@ -446,7 +444,7 @@ public:
if (width > cell_data.size()) {
line.resize(line.size() + (width - cell_data.size()), ' ');
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
line.resize(line.size() + (width + 1), ' ');
}
}
@@ -466,21 +464,21 @@ public:
}
uint32_t find_match(
shared_ptr<const ResourceDASM::MemoryContext> dest_mem,
std::shared_ptr<const ResourceDASM::MemoryContext> dest_mem,
uint32_t src_addr,
uint32_t src_size,
ExpandMethod expand_method) const {
bool is_ppc = this->is_ppc_expand_method(expand_method);
bool is_ppc_data = this->is_ppc_data_expand_method(expand_method);
if (!this->src_mem) {
throw runtime_error("no source file selected");
throw std::runtime_error("no source file selected");
}
if (src_size == 0) {
src_size = is_ppc ? 4 : 1;
}
pair<uint32_t, uint32_t> src_section = make_pair(0, 0);
std::pair<uint32_t, uint32_t> src_section = std::make_pair(0, 0);
for (const auto& sec : this->src_mem->allocated_blocks()) {
if (src_addr >= sec.first && src_addr + src_size <= sec.first + sec.second) {
src_section = sec;
@@ -488,7 +486,7 @@ public:
}
}
if (!src_section.second) {
throw runtime_error("source address not within any section");
throw std::runtime_error("source address not within any section");
}
const char* method_token = this->name_for_expand_method(expand_method);
@@ -570,7 +568,7 @@ public:
if (num_matches == 1) {
return last_match_address;
} else if (num_matches == 0) {
throw runtime_error("did not find exactly one match");
throw std::runtime_error("did not find exactly one match");
}
bool can_expand_backward = false;
bool can_expand_forward = false;
@@ -614,10 +612,10 @@ public:
can_expand_forward = (src_bytes_available_after > match_bytes_after);
break;
default:
throw logic_error("invalid expand method");
throw std::logic_error("invalid expand method");
}
if (!can_expand_backward && !can_expand_forward) {
throw runtime_error("no further expansion is allowed");
throw std::runtime_error("no further expansion is allowed");
}
if (can_expand_backward) {
match_bytes_before += (is_ppc ? 4 : 1);
@@ -626,7 +624,7 @@ public:
match_bytes_after += (is_ppc ? 4 : 1);
}
}
throw runtime_error("scan field too long; too many matches");
throw std::runtime_error("scan field too long; too many matches");
}
enum class MatchType {
@@ -637,18 +635,18 @@ public:
void find_all_matches(uint32_t src_addr, uint32_t src_size, MatchType type) const {
if (!this->src_mem) {
throw runtime_error("no source file selected");
throw std::runtime_error("no source file selected");
}
map<string, uint32_t> results;
std::map<std::string, uint32_t> results;
for (const auto& it : this->mems) {
if (it.second == this->src_mem) {
log.info_f("({}) {:08X} (from source)", it.first, src_addr);
results.emplace(it.first, src_addr);
} else {
vector<future<uint32_t>> futures;
static const vector<ExpandMethod> ppc_methods = {
std::vector<std::future<uint32_t>> futures;
static const std::vector<ExpandMethod> ppc_methods = {
ExpandMethod::PPC_TEXT_FORWARD,
ExpandMethod::PPC_TEXT_FORWARD_WITH_BARRIER,
ExpandMethod::PPC_TEXT_BACKWARD,
@@ -660,7 +658,7 @@ public:
ExpandMethod::PPC_DATA_BACKWARD,
ExpandMethod::PPC_DATA_BOTH,
};
static const vector<ExpandMethod> ppc_text_methods = {
static const std::vector<ExpandMethod> ppc_text_methods = {
ExpandMethod::PPC_TEXT_FORWARD,
ExpandMethod::PPC_TEXT_FORWARD_WITH_BARRIER,
ExpandMethod::PPC_TEXT_BACKWARD,
@@ -669,18 +667,18 @@ public:
ExpandMethod::PPC_TEXT_BOTH_WITH_BARRIER,
ExpandMethod::PPC_TEXT_BOTH_IGNORE_ORIGIN,
};
static const vector<ExpandMethod> ppc_data_methods = {
static const std::vector<ExpandMethod> ppc_data_methods = {
ExpandMethod::PPC_DATA_FORWARD,
ExpandMethod::PPC_DATA_BACKWARD,
ExpandMethod::PPC_DATA_BOTH,
};
static const vector<ExpandMethod> raw_methods = {
static const std::vector<ExpandMethod> raw_methods = {
ExpandMethod::RAW_FORWARD,
ExpandMethod::RAW_BACKWARD,
ExpandMethod::RAW_BOTH,
};
const vector<ExpandMethod>* methods;
const std::vector<ExpandMethod>* methods;
if (this->ppc_mems.count(it.second)) {
if (type == MatchType::ANY) {
methods = &ppc_methods;
@@ -689,7 +687,7 @@ public:
} else if (type == MatchType::DATA) {
methods = &ppc_data_methods;
} else {
throw logic_error("invalid match type");
throw std::logic_error("invalid match type");
}
} else {
methods = &raw_methods;
@@ -699,14 +697,14 @@ public:
futures.emplace_back(async(&AddressTranslator::find_match, this, it.second, src_addr, src_size, methods->at(z)));
}
unordered_set<uint32_t> match_addrs;
std::unordered_set<uint32_t> match_addrs;
for (size_t z = 0; z < futures.size(); z++) {
const char* method_name = this->name_for_expand_method(methods->at(z));
try {
uint32_t ret = futures[z].get();
log.info_f("({}) ({}) {:08X}", it.first, method_name, ret);
match_addrs.emplace(ret);
} catch (const exception& e) {
} catch (const std::exception& e) {
log.error_f("({}) ({}) failed: {}", it.first, method_name, e.what());
}
}
@@ -726,12 +724,12 @@ public:
}
uint32_t find_be_to_le_data_match(
shared_ptr<const ResourceDASM::MemoryContext> dest_mem, uint32_t src_addr, uint32_t src_size) const {
std::shared_ptr<const ResourceDASM::MemoryContext> dest_mem, uint32_t src_addr, uint32_t src_size) const {
if (src_size == 0) {
src_size = 4;
}
pair<uint32_t, uint32_t> src_section = make_pair(0, 0);
std::pair<uint32_t, uint32_t> src_section = std::make_pair(0, 0);
for (const auto& sec : this->src_mem->allocated_blocks()) {
if (src_addr >= sec.first && src_addr + src_size <= sec.first + sec.second) {
src_section = sec;
@@ -739,7 +737,7 @@ public:
}
}
if (!src_section.second) {
throw runtime_error("source address not within any section");
throw std::runtime_error("source address not within any section");
}
size_t src_offset = src_addr - src_section.first;
@@ -782,12 +780,12 @@ public:
if (num_matches == 1) {
return last_match_address;
} else if (num_matches == 0) {
throw runtime_error("did not find exactly one match");
throw std::runtime_error("did not find exactly one match");
}
bool can_expand_backward = (src_bytes_available_before >= match_bytes_before + 4);
bool can_expand_forward = (src_bytes_available_after >= match_bytes_after + 4);
if (!can_expand_backward && !can_expand_forward) {
throw runtime_error("no further expansion is allowed");
throw std::runtime_error("no further expansion is allowed");
}
if (can_expand_backward) {
match_bytes_before += 4;
@@ -796,15 +794,15 @@ public:
match_bytes_after += 4;
}
}
throw runtime_error("scan field too long; too many matches");
throw std::runtime_error("scan field too long; too many matches");
}
void find_all_be_to_le_data_matches(uint32_t src_addr, uint32_t src_size) const {
if (!this->src_mem) {
throw runtime_error("no source file selected");
throw std::runtime_error("no source file selected");
}
map<string, uint32_t> results;
std::map<std::string, uint32_t> results;
for (const auto& it : this->mems) {
if (it.second == this->src_mem) {
log.info_f("({}) {:08X} (from source)", it.first, src_addr);
@@ -815,7 +813,7 @@ public:
try {
ret = this->find_be_to_le_data_match(it.second, src_addr, src_size);
log.info_f("({}) {:08X}", it.first, ret);
} catch (const exception& e) {
} catch (const std::exception& e) {
log.error_f("({}) failed: {}", it.first, e.what());
}
@@ -831,7 +829,7 @@ public:
}
}
void find_data(const string& data) const {
void find_data(const std::string& data) const {
for (const auto& [name, mem] : this->mems) {
for (const auto& [sec_addr, sec_size] : mem->allocated_blocks()) {
uint32_t last_addr = sec_addr + sec_size - data.size();
@@ -844,10 +842,10 @@ public:
}
}
void handle_command(const string& command) {
void handle_command(const std::string& command) {
auto tokens = phosg::split(command, ' ');
if (tokens.empty()) {
throw runtime_error("no command given");
throw std::runtime_error("no command given");
}
phosg::strip_trailing_whitespace(tokens[tokens.size() - 1]);
@@ -856,7 +854,7 @@ public:
} else if (tokens[0] == "find") {
this->find_data(phosg::parse_data_string(tokens.at(1)));
} else if (tokens[0] == "only") {
unordered_set<string> to_keep{tokens.begin() + 1, tokens.end()};
std::unordered_set<std::string> to_keep{tokens.begin() + 1, tokens.end()};
for (auto it = this->mems.begin(); it != this->mems.end();) {
if (to_keep.count(it->first)) {
it++;
@@ -891,7 +889,7 @@ public:
auto specs = ParseDATConstructorTableSpec::from_json_list(phosg::JSON::parse(phosg::load_file(tokens.at(1))));
this->parse_dat_constructor_table_multi(specs, is_enemies, true);
} else if (!tokens[0].empty()) {
throw runtime_error("unknown command");
throw std::runtime_error("unknown command");
}
}
@@ -904,10 +902,10 @@ public:
}
fflush(stdout);
string command = phosg::fgets(stdin);
std::string command = phosg::fgets(stdin);
try {
this->handle_command(command);
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.error_f("Failed: {}", e.what());
}
}
@@ -916,14 +914,14 @@ public:
private:
phosg::PrefixedLogger log;
string directory;
unordered_map<string, shared_ptr<const ResourceDASM::MemoryContext>> mems;
unordered_set<shared_ptr<const ResourceDASM::MemoryContext>> ppc_mems;
string src_filename;
shared_ptr<const ResourceDASM::MemoryContext> src_mem;
std::string directory;
std::unordered_map<std::string, std::shared_ptr<const ResourceDASM::MemoryContext>> mems;
std::unordered_set<std::shared_ptr<const ResourceDASM::MemoryContext>> ppc_mems;
std::string src_filename;
std::shared_ptr<const ResourceDASM::MemoryContext> src_mem;
};
void run_address_translator(const string& directory, const string& use_filename, const string& command) {
void run_address_translator(const std::string& directory, const std::string& use_filename, const std::string& command) {
AddressTranslator trans(directory);
if (!use_filename.empty()) {
trans.set_source_file(use_filename);
@@ -936,32 +934,32 @@ void run_address_translator(const string& directory, const string& use_filename,
}
}
vector<DiffEntry> diff_dol_files(const string& a_filename, const string& b_filename) {
std::vector<DiffEntry> diff_dol_files(const std::string& a_filename, const std::string& b_filename) {
ResourceDASM::DOLFile a(a_filename.c_str());
ResourceDASM::DOLFile b(b_filename.c_str());
auto a_mem = make_shared<ResourceDASM::MemoryContext>();
auto b_mem = make_shared<ResourceDASM::MemoryContext>();
auto a_mem = std::make_shared<ResourceDASM::MemoryContext>();
auto b_mem = std::make_shared<ResourceDASM::MemoryContext>();
a.load_into(a_mem);
b.load_into(b_mem);
uint32_t min_addr = 0xFFFFFFFF;
uint32_t max_addr = 0x00000000;
for (const auto& sec : a.sections) {
min_addr = min<uint32_t>(min_addr, sec.address);
max_addr = max<uint32_t>(max_addr, sec.address + sec.data.size());
min_addr = std::min<uint32_t>(min_addr, sec.address);
max_addr = std::max<uint32_t>(max_addr, sec.address + sec.data.size());
}
for (const auto& sec : b.sections) {
min_addr = min<uint32_t>(min_addr, sec.address);
max_addr = max<uint32_t>(max_addr, sec.address + sec.data.size());
min_addr = std::min<uint32_t>(min_addr, sec.address);
max_addr = std::max<uint32_t>(max_addr, sec.address + sec.data.size());
}
vector<DiffEntry> ret;
std::vector<DiffEntry> ret;
for (uint32_t addr = min_addr; addr < max_addr; addr += 4) {
bool a_exists = a_mem->exists(addr, 4);
bool b_exists = b_mem->exists(addr, 4);
if (a_exists && b_exists) {
string a_value = a_mem->read(addr, 4);
string b_value = b_mem->read(addr, 4);
std::string a_value = a_mem->read(addr, 4);
std::string b_value = b_mem->read(addr, 4);
if (a_value != b_value) {
if (!ret.empty() && (ret.back().address + ret.back().b_data.size() == addr)) {
ret.back().a_data += a_value;
@@ -975,26 +973,26 @@ vector<DiffEntry> diff_dol_files(const string& a_filename, const string& b_filen
return ret;
}
vector<DiffEntry> diff_xbe_files(const string& a_filename, const string& b_filename) {
std::vector<DiffEntry> diff_xbe_files(const std::string& a_filename, const std::string& b_filename) {
ResourceDASM::XBEFile a(a_filename.c_str());
ResourceDASM::XBEFile b(b_filename.c_str());
auto a_mem = make_shared<ResourceDASM::MemoryContext>();
auto b_mem = make_shared<ResourceDASM::MemoryContext>();
auto a_mem = std::make_shared<ResourceDASM::MemoryContext>();
auto b_mem = std::make_shared<ResourceDASM::MemoryContext>();
a.load_into(a_mem);
b.load_into(b_mem);
uint32_t min_addr = 0xFFFFFFFF;
uint32_t max_addr = 0x00000000;
for (const auto& sec : a.sections) {
min_addr = min<uint32_t>(min_addr, sec.addr);
max_addr = max<uint32_t>(max_addr, sec.addr + sec.size);
min_addr = std::min<uint32_t>(min_addr, sec.addr);
max_addr = std::max<uint32_t>(max_addr, sec.addr + sec.size);
}
for (const auto& sec : b.sections) {
min_addr = min<uint32_t>(min_addr, sec.addr);
max_addr = max<uint32_t>(max_addr, sec.addr + sec.size);
min_addr = std::min<uint32_t>(min_addr, sec.addr);
max_addr = std::max<uint32_t>(max_addr, sec.addr + sec.size);
}
vector<DiffEntry> ret;
std::vector<DiffEntry> ret;
for (uint32_t addr = min_addr; addr < max_addr; addr++) {
bool a_exists = a_mem->exists(addr, 1);
bool b_exists = b_mem->exists(addr, 1);
+19 -21
View File
@@ -14,9 +14,7 @@
#include "Revision.hh"
#include "Server.hh"
using namespace std;
static const unordered_map<int, const char*> explanation_for_response_code{
static const std::unordered_map<int, const char*> explanation_for_response_code{
{100, "Continue"},
{101, "Switching Protocols"},
{102, "Processing"},
@@ -92,7 +90,7 @@ const std::string* HTTPRequest::get_header(const std::string& name) const {
if (its.first == its.second) {
return nullptr;
}
const string* ret = &its.first->second;
const std::string* ret = &its.first->second;
its.first++;
if (its.first != its.second) {
throw std::out_of_range("Header appears multiple times: " + name);
@@ -105,7 +103,7 @@ const std::string* HTTPRequest::get_query_param(const std::string& name) const {
if (its.first == its.second) {
return nullptr;
}
const string* ret = &its.first->second;
const std::string* ret = &its.first->second;
its.first++;
if (its.first != its.second) {
throw std::out_of_range("Query parameter appears multiple times: " + name);
@@ -113,7 +111,7 @@ const std::string* HTTPRequest::get_query_param(const std::string& name) const {
return ret;
}
static void url_decode_inplace(string& s) {
static void url_decode_inplace(std::string& s) {
size_t write_offset = 0, read_offset = 0;
for (; read_offset < s.size(); write_offset++) {
if ((s[read_offset] == '%') && (read_offset < s.size() - 2)) {
@@ -139,7 +137,7 @@ asio::awaitable<HTTPRequest> HTTPClient::recv_http_request(size_t max_line_size,
std::string request_line = co_await this->r.read_line("\r\n", max_line_size);
auto line_tokens = phosg::split(request_line, ' ');
if (line_tokens.size() != 3) {
throw runtime_error("invalid HTTP request line");
throw std::runtime_error("invalid HTTP request line");
}
const auto& method_token = line_tokens[0];
if (method_token == "GET") {
@@ -169,14 +167,14 @@ asio::awaitable<HTTPRequest> HTTPClient::recv_http_request(size_t max_line_size,
req.http_version = std::move(line_tokens[2]);
size_t fragment_start_offset = line_tokens[1].find('#');
if (fragment_start_offset != string::npos) {
if (fragment_start_offset != std::string::npos) {
req.fragment = line_tokens[1].substr(fragment_start_offset + 1);
line_tokens[1].resize(fragment_start_offset);
}
size_t query_start_offset = line_tokens[1].find('?');
string query;
if (query_start_offset != string::npos) {
std::string query;
if (query_start_offset != std::string::npos) {
query = line_tokens[1].substr(query_start_offset + 1);
line_tokens[1].resize(query_start_offset);
}
@@ -189,12 +187,12 @@ asio::awaitable<HTTPRequest> HTTPClient::recv_http_request(size_t max_line_size,
auto query_tokens = phosg::split(query, '&');
for (auto& token : query_tokens) {
size_t equals_pos = token.find('=');
if (equals_pos == string::npos) {
if (equals_pos == std::string::npos) {
url_decode_inplace(token);
req.query_params.emplace(std::move(token), "");
} else {
string key = token.substr(0, equals_pos);
string value = token.substr(equals_pos + 1);
std::string key = token.substr(0, equals_pos);
std::string value = token.substr(equals_pos + 1);
url_decode_inplace(key);
url_decode_inplace(value);
req.query_params.emplace(std::move(key), std::move(value));
@@ -217,11 +215,11 @@ asio::awaitable<HTTPRequest> HTTPClient::recv_http_request(size_t max_line_size,
}
} else {
size_t colon_pos = line.find(':');
if (colon_pos == string::npos) {
throw runtime_error("malformed header line");
if (colon_pos == std::string::npos) {
throw std::runtime_error("malformed header line");
}
string key = line.substr(0, colon_pos);
string value = line.substr(colon_pos + 1);
std::string key = line.substr(0, colon_pos);
std::string value = line.substr(colon_pos + 1);
phosg::strip_whitespace(key);
phosg::strip_whitespace(value);
prev_header_it = req.headers.emplace(phosg::tolower(key), std::move(value));
@@ -230,7 +228,7 @@ asio::awaitable<HTTPRequest> HTTPClient::recv_http_request(size_t max_line_size,
auto transfer_encoding_header = req.get_header("transfer-encoding");
if (transfer_encoding_header && phosg::tolower(*transfer_encoding_header) == "chunked") {
deque<string> chunks;
std::deque<std::string> chunks;
size_t total_data_bytes = 0;
for (;;) {
auto line = co_await this->r.read_line("\r\n", 0x20);
@@ -306,7 +304,7 @@ asio::awaitable<WebSocketMessage> HTTPClient::recv_websocket_message(size_t max_
}
if (payload_size > max_data_size) {
throw runtime_error("Incoming WebSocket message exceeds size limit");
throw std::runtime_error("Incoming WebSocket message exceeds size limit");
}
// Read the masking key if present
@@ -380,7 +378,7 @@ asio::awaitable<WebSocketMessage> HTTPClient::recv_websocket_message(size_t max_
}
}
throw logic_error("failed to receive websocket message");
throw std::logic_error("failed to receive websocket message");
}
asio::awaitable<void> HTTPClient::send_websocket_message(const void* data, size_t size, uint8_t opcode) {
@@ -396,7 +394,7 @@ asio::awaitable<void> HTTPClient::send_websocket_message(const void* data, size_
w.put_u8(size);
}
array<asio::const_buffer, 2> bufs = {asio::const_buffer(w.data(), w.size()), asio::const_buffer(data, size)};
std::array<asio::const_buffer, 2> bufs = {asio::const_buffer(w.data(), w.size()), asio::const_buffer(data, size)};
co_await asio::async_write(this->r.get_socket(), bufs, asio::use_awaitable);
}
+19 -21
View File
@@ -7,15 +7,12 @@
#include <phosg/Strings.hh>
#include <string>
using namespace std;
AsyncEvent::AsyncEvent(asio::any_io_executor ex)
: executor(ex), is_set(false) {}
AsyncEvent::AsyncEvent(asio::any_io_executor ex) : executor(ex), is_set(false) {}
void AsyncEvent::set() {
std::vector<std::unique_ptr<asio::detail::awaitable_handler<asio::any_io_executor>>> waiters_to_resume;
{
lock_guard g(this->lock);
std::lock_guard g(this->lock);
this->is_set = true;
this->waiters.swap(waiters_to_resume);
}
@@ -28,7 +25,7 @@ void AsyncEvent::set() {
}
void AsyncEvent::clear() {
lock_guard g(this->lock);
std::lock_guard g(this->lock);
this->is_set = false;
}
@@ -36,11 +33,12 @@ asio::awaitable<void> AsyncEvent::wait() {
auto token = asio::use_awaitable_t<>{};
co_await asio::async_initiate<asio::use_awaitable_t<>, void()>(
[this](auto&& handler) -> void {
lock_guard g(this->lock);
std::lock_guard g(this->lock);
if (this->is_set) {
handler();
} else {
this->waiters.emplace_back(make_unique<asio::detail::awaitable_handler<asio::any_io_executor>>(std::move(handler)));
this->waiters.emplace_back(
std::make_unique<asio::detail::awaitable_handler<asio::any_io_executor>>(std::move(handler)));
}
},
token);
@@ -49,17 +47,17 @@ asio::awaitable<void> AsyncEvent::wait() {
AsyncSocketReader::AsyncSocketReader(asio::ip::tcp::socket&& sock)
: sock(std::move(sock)) {}
asio::awaitable<string> AsyncSocketReader::read_line(const char* delimiter, size_t max_length) {
asio::awaitable<std::string> AsyncSocketReader::read_line(const char* delimiter, size_t max_length) {
size_t delimiter_size = strlen(delimiter);
if (delimiter_size == 0) {
throw logic_error("delimiter is empty");
throw std::logic_error("delimiter is empty");
}
size_t delimiter_backup_bytes = delimiter_size - 1;
size_t delimiter_pos = this->pending_data.find(delimiter);
while ((delimiter_pos == string::npos) && (!max_length || (this->pending_data.size() < max_length))) {
while ((delimiter_pos == std::string::npos) && (!max_length || (this->pending_data.size() < max_length))) {
size_t pre_size = this->pending_data.size();
this->pending_data.resize(min(max_length, this->pending_data.size() + 0x400));
this->pending_data.resize(std::min(max_length, this->pending_data.size() + 0x400));
auto buf = asio::buffer(this->pending_data.data() + pre_size, this->pending_data.size() - pre_size);
size_t bytes_read = co_await this->sock.async_read_some(buf, asio::use_awaitable);
@@ -69,18 +67,18 @@ asio::awaitable<string> AsyncSocketReader::read_line(const char* delimiter, size
(delimiter_backup_bytes > pre_size) ? 0 : (pre_size - delimiter_backup_bytes));
}
if (delimiter_pos == string::npos) {
throw runtime_error("line exceeds max length");
if (delimiter_pos == std::string::npos) {
throw std::runtime_error("line exceeds max length");
}
// TODO: It's not great that we copy the data here. There's probably a more idiomatic and efficient way to do this.
string ret = this->pending_data.substr(0, delimiter_pos);
std::string ret = this->pending_data.substr(0, delimiter_pos);
this->pending_data = this->pending_data.substr(delimiter_pos + delimiter_size);
co_return ret;
}
asio::awaitable<string> AsyncSocketReader::read_data(size_t size) {
string ret;
asio::awaitable<std::string> AsyncSocketReader::read_data(size_t size) {
std::string ret;
if (this->pending_data.size() == size) {
this->pending_data.swap(ret);
} else if (this->pending_data.size() > size) {
@@ -111,7 +109,7 @@ asio::awaitable<void> AsyncSocketReader::read_data_into(void* data, size_t size)
}
}
void AsyncWriteCollector::add(string&& data) {
void AsyncWriteCollector::add(std::string&& data) {
const auto& item = this->owned_data.emplace_back(std::move(data));
bufs.emplace_back(asio::buffer(item.data(), item.size()));
}
@@ -121,14 +119,14 @@ void AsyncWriteCollector::add_reference(const void* data, size_t size) {
}
asio::awaitable<void> AsyncWriteCollector::write(asio::ip::tcp::socket& sock) {
deque<string> local_owned_data;
std::deque<std::string> local_owned_data;
local_owned_data.swap(this->owned_data);
vector<asio::const_buffer> local_bufs;
std::vector<asio::const_buffer> local_bufs;
local_bufs.swap(this->bufs);
co_await asio::async_write(sock, local_bufs, asio::use_awaitable);
}
asio::awaitable<void> async_sleep(chrono::steady_clock::duration duration) {
asio::awaitable<void> async_sleep(std::chrono::steady_clock::duration duration) {
asio::steady_timer timer(co_await asio::this_coro::executor, duration);
co_await timer.async_wait(asio::use_awaitable);
}
+1 -1
View File
@@ -259,7 +259,7 @@ asio::awaitable<std::invoke_result_t<FnT, ArgTs...>> call_on_thread_pool(asio::t
using ReturnT = std::invoke_result_t<FnT, ArgTs...>;
auto bound = std::bind(std::forward<FnT>(f), std::forward<ArgTs>(args)...);
// We have to use a shared_ptr here in case call_on_thread_pool is canceled (in that case, the posted callback will
// We have to use a std::shared_ptr here in case call_on_thread_pool is canceled (in that case, the posted callback will
// try to use promise after the call_on_thread_pool coroutine has been destroyed)
auto promise = std::make_shared<AsyncPromise<ReturnT>>();
asio::post(pool, [bound = std::move(bound), promise]() mutable {
+18 -21
View File
@@ -7,8 +7,6 @@
#include "Text.hh"
#include "Types.hh"
using namespace std;
template <bool BE>
struct BMLHeaderT {
parray<uint8_t, 0x04> unknown_a1;
@@ -42,13 +40,13 @@ void BMLArchive::load_t() {
const auto& entry = r.get<BMLHeaderEntryT<BE>>();
if (offset + entry.compressed_size > this->data->size()) {
throw runtime_error("BML data entry extends beyond end of data");
throw std::runtime_error("BML data entry extends beyond end of data");
}
size_t data_offset = offset;
offset = (offset + entry.compressed_size + 0x1F) & (~0x1F);
if (offset + entry.compressed_gvm_size > this->data->size()) {
throw runtime_error("BML GVM entry extends beyond end of data");
throw std::runtime_error("BML GVM entry extends beyond end of data");
}
size_t gvm_offset = offset;
offset = (offset + entry.compressed_gvm_size + 0x1F) & (~0x1F);
@@ -57,8 +55,7 @@ void BMLArchive::load_t() {
}
}
BMLArchive::BMLArchive(shared_ptr<const string> data, bool big_endian)
: data(data) {
BMLArchive::BMLArchive(std::shared_ptr<const std::string> data, bool big_endian) : data(data) {
if (big_endian) {
this->load_t<true>();
} else {
@@ -66,42 +63,42 @@ BMLArchive::BMLArchive(shared_ptr<const string> data, bool big_endian)
}
}
const unordered_map<string, BMLArchive::Entry> BMLArchive::all_entries() const {
const std::unordered_map<std::string, BMLArchive::Entry> BMLArchive::all_entries() const {
return this->entries;
}
pair<const void*, size_t> BMLArchive::get(const std::string& name) const {
std::pair<const void*, size_t> BMLArchive::get(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return make_pair(this->data->data() + entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("BML does not contain file: " + name);
return std::make_pair(this->data->data() + entry.offset, entry.size);
} catch (const std::out_of_range&) {
throw std::out_of_range("BML does not contain file: " + name);
}
}
pair<const void*, size_t> BMLArchive::get_gvm(const std::string& name) const {
std::pair<const void*, size_t> BMLArchive::get_gvm(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return make_pair(this->data->data() + entry.gvm_offset, entry.gvm_size);
} catch (const out_of_range&) {
throw out_of_range("BML does not contain file: " + name);
return std::make_pair(this->data->data() + entry.gvm_offset, entry.gvm_size);
} catch (const std::out_of_range&) {
throw std::out_of_range("BML does not contain file: " + name);
}
}
string BMLArchive::get_copy(const string& name) const {
std::string BMLArchive::get_copy(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return this->data->substr(entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("BML does not contain file: " + name);
} catch (const std::out_of_range&) {
throw std::out_of_range("BML does not contain file: " + name);
}
}
phosg::StringReader BMLArchive::get_reader(const string& name) const {
phosg::StringReader BMLArchive::get_reader(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return phosg::StringReader(this->data->data() + entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("BML does not contain file: " + name);
} catch (const std::out_of_range&) {
throw std::out_of_range("BML does not contain file: " + name);
}
}
+10 -12
View File
@@ -7,8 +7,6 @@
#include "PSOEncryption.hh"
#include "StaticGameData.hh"
using namespace std;
BattleParamsIndex::AttackData BattleParamsIndex::AttackData::from_json(const phosg::JSON& json) {
return AttackData{
json.get_int("MinATP"),
@@ -202,7 +200,7 @@ void BattleParamsIndex::Table::print(FILE* stream, Episode episode) const {
for (size_t z = 0; z < 0x60; z++) {
const auto& e = this->stats[static_cast<size_t>(difficulty)][z];
phosg::fwrite_fmt(stream, " {:02X} ", z);
string names_str;
std::string names_str;
for (auto type : enemy_types_for_battle_param_stats_index(episode, z)) {
if (!names_str.empty()) {
names_str += ", ";
@@ -290,17 +288,17 @@ const BattleParamsIndex::Table& JSONBattleParamsIndex::get_table(bool solo, Epis
case Episode::EP4:
return this->tables[!!solo][2];
default:
throw invalid_argument("invalid episode");
throw std::invalid_argument("invalid episode");
}
}
BinaryBattleParamsIndex::BinaryBattleParamsIndex(
shared_ptr<const string> data_on_ep1,
shared_ptr<const string> data_on_ep2,
shared_ptr<const string> data_on_ep4,
shared_ptr<const string> data_off_ep1,
shared_ptr<const string> data_off_ep2,
shared_ptr<const string> data_off_ep4) {
std::shared_ptr<const std::string> data_on_ep1,
std::shared_ptr<const std::string> data_on_ep2,
std::shared_ptr<const std::string> data_on_ep4,
std::shared_ptr<const std::string> data_off_ep1,
std::shared_ptr<const std::string> data_off_ep2,
std::shared_ptr<const std::string> data_off_ep4) {
this->files[0][0].data = data_on_ep1;
this->files[0][1].data = data_on_ep2;
this->files[0][2].data = data_on_ep4;
@@ -312,7 +310,7 @@ BinaryBattleParamsIndex::BinaryBattleParamsIndex(
for (uint8_t episode = 0; episode < 3; episode++) {
auto& file = this->files[is_solo][episode];
if (file.data->size() < sizeof(Table)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"battle params table size is incorrect (expected {:X} bytes, have {:X} bytes; is_solo={}, episode={})",
sizeof(Table), file.data->size(), is_solo, episode));
}
@@ -330,6 +328,6 @@ const BattleParamsIndex::Table& BinaryBattleParamsIndex::get_table(bool solo, Ep
case Episode::EP4:
return *this->files[!!solo][2].table;
default:
throw invalid_argument("invalid episode");
throw std::invalid_argument("invalid episode");
}
}
+20 -22
View File
@@ -12,14 +12,12 @@
#include "StaticGameData.hh"
#include "Version.hh"
using namespace std;
extern bool use_terminal_colors;
Channel::Channel(
Version version,
Language language,
const string& name,
const std::string& name,
phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
@@ -49,7 +47,7 @@ void Channel::send(
size += b.second;
}
string send_data;
std::string send_data;
size_t logical_size;
size_t send_data_size = 0;
switch (this->version) {
@@ -113,12 +111,12 @@ void Channel::send(
}
default:
throw logic_error("unimplemented game version in send_command");
throw std::logic_error("unimplemented game version in send_command");
}
// All versions of PSO I've seen (so far) have a receive buffer 0x7C00 bytes in size
if (send_data_size > 0x7C00) {
throw runtime_error("outbound command too large");
throw std::runtime_error("outbound command too large");
}
send_data.reserve(send_data_size);
@@ -164,10 +162,10 @@ void Channel::send(
}
void Channel::send(uint16_t cmd, uint32_t flag, const void* data, size_t size, bool silent) {
this->send(cmd, flag, {make_pair(data, size)}, silent);
this->send(cmd, flag, {std::make_pair(data, size)}, silent);
}
void Channel::send(uint16_t cmd, uint32_t flag, const string& data, bool silent) {
void Channel::send(uint16_t cmd, uint32_t flag, const std::string& data, bool silent) {
this->send(cmd, flag, data.data(), data.size(), silent);
}
@@ -182,7 +180,7 @@ void Channel::send(const void* data, size_t size, bool silent) {
silent);
}
void Channel::send(const string& data, bool silent) {
void Channel::send(const std::string& data, bool silent) {
this->send(data.data(), data.size(), silent);
}
@@ -196,7 +194,7 @@ asio::awaitable<Channel::Message> Channel::recv() {
size_t command_logical_size = header.size(version);
if (command_logical_size < header_size) {
throw runtime_error("header size field is smaller than header");
throw std::runtime_error("header size field is smaller than header");
}
// If encryption is enabled, BB pads commands to 8-byte boundaries, and this is not reflected in the size field. This
@@ -205,7 +203,7 @@ asio::awaitable<Channel::Message> Channel::recv() {
? ((command_logical_size + 7) & ~7)
: command_logical_size;
string command_data(command_physical_size - header_size, '\0');
std::string command_data(command_physical_size - header_size, '\0');
co_await this->recv_raw(command_data.data(), command_data.size());
if (this->crypt_in.get()) {
@@ -267,17 +265,17 @@ asio::awaitable<Channel::Message> Channel::recv() {
};
}
shared_ptr<SocketChannel> SocketChannel::create(
std::shared_ptr<SocketChannel> SocketChannel::create(
std::shared_ptr<asio::io_context> io_context,
std::unique_ptr<asio::ip::tcp::socket>&& sock,
Version version,
Language language,
const string& name,
const std::string& name,
phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
bool censor_sent_credentials) {
shared_ptr<SocketChannel> ret(new SocketChannel(
std::shared_ptr<SocketChannel> ret(new SocketChannel(
io_context,
std::move(sock),
version,
@@ -296,7 +294,7 @@ SocketChannel::SocketChannel(
std::unique_ptr<asio::ip::tcp::socket>&& sock,
Version version,
Language language,
const string& name,
const std::string& name,
phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color,
bool censor_received_credentials,
@@ -320,7 +318,7 @@ void SocketChannel::disconnect() {
this->send_buffer_nonempty_signal.set();
}
void SocketChannel::send_raw(string&& data) {
void SocketChannel::send_raw(std::string&& data) {
if (this->sock && !this->should_disconnect) {
this->outbound_data.emplace_back(std::move(data));
this->send_buffer_nonempty_signal.set();
@@ -329,7 +327,7 @@ void SocketChannel::send_raw(string&& data) {
asio::awaitable<void> SocketChannel::recv_raw(void* data, size_t size) {
if (!this->sock || this->should_disconnect) {
throw runtime_error("Cannot receive on closed channel");
throw std::runtime_error("Cannot receive on closed channel");
}
co_await asio::async_read(*this->sock, asio::buffer(data, size), asio::use_awaitable);
}
@@ -339,11 +337,11 @@ asio::awaitable<void> SocketChannel::send_task() {
auto this_sh = this->shared_from_this();
while (this->sock->is_open()) {
deque<string> to_send;
std::deque<std::string> to_send;
to_send.swap(this->outbound_data);
if (!to_send.empty()) {
vector<asio::const_buffer> bufs;
std::vector<asio::const_buffer> bufs;
bufs.reserve(to_send.size());
for (const auto& it : to_send) {
bufs.emplace_back(asio::buffer(it.data(), it.size()));
@@ -376,7 +374,7 @@ PeerChannel::PeerChannel(
void PeerChannel::link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2) {
if (peer1->connected() || peer2->connected()) {
throw logic_error("Cannot link already-connected peer channels");
throw std::logic_error("Cannot link already-connected peer channels");
}
peer1->peer = peer2;
peer2->peer = peer1;
@@ -400,7 +398,7 @@ void PeerChannel::disconnect() {
this->send_buffer_nonempty_signal.set();
}
void PeerChannel::send_raw(string&& data) {
void PeerChannel::send_raw(std::string&& data) {
auto peer = this->peer.lock();
if (peer) {
peer->inbound_data.emplace_back(std::move(data));
@@ -428,7 +426,7 @@ asio::awaitable<void> PeerChannel::recv_raw(void* data, size_t size) {
this->inbound_data.pop_front();
}
} else if (!this->peer.lock()) {
throw runtime_error("Channel peer has disconnected");
throw std::runtime_error("Channel peer has disconnected");
}
}
}
+147 -153
View File
@@ -23,13 +23,11 @@
#include "StaticGameData.hh"
#include "Text.hh"
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tools
string str_for_flag_ranges(const vector<bool>& flags) {
string ret;
std::string str_for_flag_ranges(const std::vector<bool>& flags) {
std::string ret;
auto add_result = [&](size_t start, size_t end) {
if (!ret.empty()) {
ret.push_back(',');
@@ -174,19 +172,19 @@ struct ChatCommandDefinition {
std::vector<const char*> names;
Handler handler;
static unordered_map<string, const ChatCommandDefinition*> all_defs;
static std::unordered_map<std::string, const ChatCommandDefinition*> all_defs;
ChatCommandDefinition(std::initializer_list<const char*> names, Handler handler)
: names(names), handler(handler) {
for (const char* name : this->names) {
if (!this->all_defs.emplace(name, this).second) {
throw logic_error("duplicate command definition: " + string(name));
throw std::logic_error("duplicate command definition: " + std::string(name));
}
}
}
};
unordered_map<string, const ChatCommandDefinition*> ChatCommandDefinition::all_defs;
std::unordered_map<std::string, const ChatCommandDefinition*> ChatCommandDefinition::all_defs;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// All commands (in alphabetical order)
@@ -332,7 +330,7 @@ ChatCommandDefinition cc_auction(
co_return;
});
static string name_for_client(shared_ptr<Client> c) {
static std::string name_for_client(std::shared_ptr<Client> c) {
auto player = c->character_file(false);
if (player.get()) {
return escape_player_name(player->disp.name.decode(player->inventory.language));
@@ -355,11 +353,11 @@ ChatCommandDefinition cc_ban(
auto l = a.c->require_lobby();
size_t space_pos = a.text.find(' ');
if (space_pos == string::npos) {
if (space_pos == std::string::npos) {
throw precondition_failed("$C6Incorrect arguments");
}
string identifier = a.text.substr(space_pos + 1);
std::string identifier = a.text.substr(space_pos + 1);
auto target = s->find_client(&identifier);
if (!target->login) {
// This should be impossible, but I'll bet it's not actually
@@ -372,7 +370,7 @@ ChatCommandDefinition cc_ban(
if (a.c == target) {
// This shouldn't be possible because you need BAN_USER to get here, but the target can't have BAN_USER if we
// get here, so if a.c and target are the same, one of the preceding conditions must be false.
throw logic_error("client attempts to ban themself");
throw std::logic_error("client attempts to ban themself");
}
uint64_t usecs = stoull(a.text, nullptr, 0) * 1000000;
@@ -397,9 +395,8 @@ ChatCommandDefinition cc_ban(
target->login->account->ban_end_time = phosg::now() + usecs;
target->login->account->save();
send_message_box(target, "$C6You have been banned.");
string target_name = name_for_client(target);
send_text_message_fmt(a.c, "$C6{} banned", name_for_client(target));
target->channel->disconnect();
send_text_message_fmt(a.c, "$C6{} banned", target_name);
co_return;
});
@@ -409,10 +406,10 @@ ChatCommandDefinition cc_bank(
a.check_is_proxy(false);
a.check_version(Version::BB_V4);
if (a.c->check_flag(Client::Flag::AT_BANK_COUNTER)) {
throw runtime_error("cannot change banks while at the bank counter");
throw std::runtime_error("cannot change banks while at the bank counter");
}
if (a.c->has_overlay()) {
throw runtime_error("cannot change banks while Battle or Challenge is in progress");
throw std::runtime_error("cannot change banks while Battle or Challenge is in progress");
}
ssize_t new_char_index = a.text.empty() ? (a.c->bb_character_index + 1) : stol(a.text, nullptr, 0);
@@ -450,17 +447,17 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
throw precondition_failed("$C6Episode 3 players\ncannot be converted\nto BB format");
}
shared_ptr<Account> dest_account;
shared_ptr<BBLicense> dest_bb_license;
std::shared_ptr<Account> dest_account;
std::shared_ptr<BBLicense> dest_bb_license;
size_t dest_character_index = 0;
if (is_bb_conversion) {
vector<string> tokens = phosg::split(a.text, ' ');
std::vector<std::string> tokens = phosg::split(a.text, ' ');
if (tokens.size() != 3) {
throw precondition_failed("$C6Incorrect argument\ncount");
}
// username/password are tokens[0] and [1]
dest_character_index = stoull(tokens[2]) - 1;
dest_character_index = std::stoull(tokens[2]) - 1;
if (dest_character_index >= 127) {
throw precondition_failed("$C6Player index must\nbe in range 1-127");
}
@@ -469,7 +466,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
auto dest_login = s->account_index->from_bb_credentials(tokens[0], &tokens[1], false);
dest_account = dest_login->account;
dest_bb_license = dest_login->bb_license;
} catch (const exception& e) {
} catch (const std::exception& e) {
throw precondition_failed("$C6Login failed: {}", e.what());
}
@@ -490,7 +487,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
ch = co_await send_get_player_info(a.c, true);
}
string filename = dest_bb_license
std::string filename = dest_bb_license
? Client::character_filename(dest_bb_license->username, dest_character_index)
: Client::backup_character_filename(dest_account->account_id, dest_character_index, is_ep3(a.c->version()));
@@ -500,7 +497,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
try {
Client::save_ep3_character_file(filename, *ch.ep3_character);
send_text_message(a.c, "$C7Character data saved\n(full save file)");
} catch (const exception& e) {
} catch (const std::exception& e) {
send_text_message_fmt(a.c, "$C6Character data could\nnot be saved:\n{}", e.what());
}
}
@@ -508,7 +505,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
try {
Client::save_character_file(filename, a.c->system_file(), ch.character);
send_text_message(a.c, "$C7Character data saved\n(full save file)");
} catch (const exception& e) {
} catch (const std::exception& e) {
send_text_message_fmt(a.c, "$C6Character data could\nnot be saved:\n{}", e.what());
}
}
@@ -553,7 +550,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
try {
Client::save_character_file(filename, a.c->system_file(), bb_player);
send_text_message(a.c, "$C7Character data saved\n(basic only)");
} catch (const exception& e) {
} catch (const std::exception& e) {
send_text_message_fmt(a.c, "$C6Character data could\nnot be saved:\n{}", e.what());
}
}
@@ -608,16 +605,16 @@ ChatCommandDefinition cc_checkchar(
if (a.text.empty()) {
bool is_ep3 = ::is_ep3(a.c->version());
vector<bool> flags;
std::vector<bool> flags;
flags.emplace_back(false);
for (size_t z = 0; z < s->num_backup_character_slots; z++) {
string filename = a.c->backup_character_filename(a.c->login->account->account_id, z, is_ep3);
std::string filename = a.c->backup_character_filename(a.c->login->account->account_id, z, is_ep3);
flags.emplace_back(std::filesystem::is_regular_file(filename));
}
string used_str = str_for_flag_ranges(flags);
std::string used_str = str_for_flag_ranges(flags);
flags.flip();
flags[0] = false;
string free_str = str_for_flag_ranges(flags);
std::string free_str = str_for_flag_ranges(flags);
send_text_message_fmt(a.c, "Used: {}\nFree: {}", used_str, free_str);
} else {
@@ -628,7 +625,7 @@ ChatCommandDefinition cc_checkchar(
try {
if (is_ep3(a.c->version())) {
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, true);
std::string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, true);
auto ch = phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename);
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nCLv: on {}.{}, off {}.{}",
index + 1, ch.disp.visual.name.decode(),
@@ -636,7 +633,7 @@ ChatCommandDefinition cc_checkchar(
(ch.ep3_config.online_clv_exp / 100) + 1, ch.ep3_config.online_clv_exp % 100,
(ch.ep3_config.offline_clv_exp / 100) + 1, ch.ep3_config.offline_clv_exp % 100);
} else {
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, false);
std::string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, false);
auto ch = PSOCHARFile::load_shared(filename, false).character_file;
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nLevel {}",
index + 1, ch->disp.name.decode(),
@@ -673,7 +670,7 @@ ChatCommandDefinition cc_deletechar(
throw precondition_failed("$C6Player index must\nbe in range 1-16");
}
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, is_ep3(a.c->version()));
std::string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, is_ep3(a.c->version()));
if (std::filesystem::is_regular_file(filename)) {
std::filesystem::remove(filename);
send_text_message_fmt(a.c, "Character in slot\n{} deleted", index + 1);
@@ -694,7 +691,7 @@ ChatCommandDefinition cc_dicerange(
auto l = a.c->require_lobby();
if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
throw std::logic_error("non-Ep3 client in Ep3 game");
}
if (!l->ep3_server) {
throw precondition_failed("$C6Episode 3 server\nis not initialized");
@@ -706,7 +703,7 @@ ChatCommandDefinition cc_dicerange(
throw precondition_failed("$C6Cannot override\ndice ranges in a\ntournament");
}
auto parse_dice_range = +[](const string& spec) -> uint8_t {
auto parse_dice_range = +[](const std::string& spec) -> uint8_t {
auto tokens = phosg::split(spec, '-');
if (tokens.size() == 1) {
uint8_t v = stoull(spec);
@@ -714,7 +711,7 @@ ChatCommandDefinition cc_dicerange(
} else if (tokens.size() == 2) {
return (stoull(tokens[0]) << 4) | (stoull(tokens[1]) & 0x0F);
} else {
throw runtime_error("invalid dice spec format");
throw std::runtime_error("invalid dice spec format");
}
};
@@ -891,33 +888,33 @@ ChatCommandDefinition cc_edit(
(s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) ||
a.c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE));
string encoded_args = phosg::tolower(a.text);
vector<string> tokens = phosg::split(encoded_args, ' ');
std::string encoded_args = phosg::tolower(a.text);
std::vector<std::string> tokens = phosg::split(encoded_args, ' ');
using MatType = PSOBBCharacterFile::MaterialType;
try {
auto p = a.c->character_file();
if (tokens.at(0) == "atp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.atp = stoul(tokens.at(1));
p->disp.stats.char_stats.atp = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "mst" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.mst = stoul(tokens.at(1));
p->disp.stats.char_stats.mst = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "evp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.evp = stoul(tokens.at(1));
p->disp.stats.char_stats.evp = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "hp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.hp = stoul(tokens.at(1));
p->disp.stats.char_stats.hp = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "dfp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.dfp = stoul(tokens.at(1));
p->disp.stats.char_stats.dfp = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "ata" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.ata = stoul(tokens.at(1));
p->disp.stats.char_stats.ata = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "lck" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.lck = stoul(tokens.at(1));
p->disp.stats.char_stats.lck = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "meseta" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.meseta = stoul(tokens.at(1));
p->disp.stats.meseta = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "exp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.exp = stoul(tokens.at(1));
p->disp.stats.exp = std::stoul(tokens.at(1));
} else if (tokens.at(0) == "level" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.level = stoul(tokens.at(1)) - 1;
p->disp.stats.level = std::stoul(tokens.at(1)) - 1;
p->recompute_stats(s->level_table(a.c->version()), true);
} else if (((tokens.at(0) == "material") || (tokens.at(0) == "mat")) && !is_v1_or_v2(a.c->version()) && (cheats_allowed || !s->cheat_flags.reset_materials)) {
if (tokens.at(1) == "reset") {
@@ -958,10 +955,10 @@ ChatCommandDefinition cc_edit(
}
p->recompute_stats(s->level_table(a.c->version()), false);
} else if (tokens.at(0) == "namecolor") {
p->disp.visual.name_color = stoul(tokens.at(1), nullptr, 16);
p->disp.visual.name_color = std::stoul(tokens.at(1), nullptr, 16);
} else if (tokens.at(0) == "language" || tokens.at(0) == "lang") {
if (tokens.at(1).size() != 1) {
throw runtime_error("invalid language");
throw std::runtime_error("invalid language");
}
Language new_language = language_for_char(tokens.at(1).at(0));
a.c->channel->language = new_language;
@@ -982,7 +979,7 @@ ChatCommandDefinition cc_edit(
p->disp.visual.section_id = secid;
}
} else if (tokens.at(0) == "name") {
vector<string> orig_tokens = phosg::split(a.text, ' ', 1);
std::vector<std::string> orig_tokens = phosg::split(a.text, ' ', 1);
p->disp.name.encode(orig_tokens.at(1), p->inventory.language);
} else if (tokens.at(0) == "npc") {
if (tokens.at(1) == "none") {
@@ -999,7 +996,7 @@ ChatCommandDefinition cc_edit(
p->disp.visual.validation_flags |= 0x02;
}
} else if (tokens.at(0) == "tech" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
uint8_t level = stoul(tokens.at(2)) - 1;
uint8_t level = std::stoul(tokens.at(2)) - 1;
if (tokens.at(1) == "all") {
for (size_t x = 0; x < 0x14; x++) {
p->set_technique_level(x, level);
@@ -1011,14 +1008,14 @@ ChatCommandDefinition cc_edit(
}
try {
p->set_technique_level(tech_id, level);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("$C6Invalid technique");
}
}
} else {
throw precondition_failed("$C6Unknown field");
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("$C6Not enough arguments");
}
@@ -1111,10 +1108,10 @@ ChatCommandDefinition cc_exit(
a.c->check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) {
co_await prepare_client_for_patches(a.c);
auto s = a.c->require_server_state();
shared_ptr<const ClientFunctionIndex::Function> fn;
std::shared_ptr<const ClientFunctionIndex::Function> fn;
try {
fn = s->client_functions->get("ExitAnywhere", a.c->specific_version);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (fn) {
co_await send_function_call(a.c, fn);
@@ -1182,7 +1179,7 @@ ChatCommandDefinition cc_inftime(
auto l = a.c->require_lobby();
if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
throw std::logic_error("non-Ep3 client in Ep3 game");
}
if (!l->ep3_server) {
throw precondition_failed("$C6Episode 3 server\nis not initialized");
@@ -1258,7 +1255,7 @@ ChatCommandDefinition cc_item(
}
}
string name = s->describe_item(a.c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES);
std::string name = s->describe_item(a.c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES);
if (was_enqueued) {
send_text_message(a.c, "$C7Next item:\n" + name);
} else {
@@ -1307,13 +1304,12 @@ ChatCommandDefinition cc_kick(
if (a.c == target) {
// This shouldn't be possible because you need KICK_USER to get here, but the target can't have KICK_USER if we
// get here, so if a.c and target are the same, one of the preceding conditions must be false.
throw logic_error("client attempts to kick themself off");
throw std::logic_error("client attempts to kick themself off");
}
send_message_box(target, "$C6You have been kicked off the server.");
string target_name = name_for_client(target);
send_text_message_fmt(a.c, "$C6{} kicked off", name_for_client(target));
target->channel->disconnect();
send_text_message_fmt(a.c, "$C6{} kicked off", target_name);
co_return;
});
@@ -1323,7 +1319,7 @@ ChatCommandDefinition cc_killcount(
a.check_is_proxy(false);
auto p = a.c->character_file();
vector<size_t> item_indexes;
std::vector<size_t> item_indexes;
for (size_t z = 0; z < p->inventory.num_items; z++) {
const auto& item = p->inventory.items[z];
if (item.is_equipped() && item.data.has_kill_count()) {
@@ -1348,7 +1344,7 @@ ChatCommandDefinition cc_killcount(
auto s = a.c->require_server_state();
for (size_t z : item_indexes) {
const auto& item = p->inventory.items[z];
string name = s->describe_item(
std::string name = s->describe_item(
a.c->version(), item.data, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES | ItemNameIndex::Flag::NAME_ONLY);
send_text_message_fmt(a.c, "{}$C7: {} kills", name, item.data.get_kill_count());
}
@@ -1360,7 +1356,7 @@ ChatCommandDefinition cc_lobby_info(
{"$li"},
+[](const Args& a) -> asio::awaitable<void> {
if (a.c->proxy_session) {
string msg;
std::string msg;
// On non-masked-GC sessions (BB), there is no remote Guild Card number, so we don't show it. (The user can see
// it in the pause menu, unlike in masked-GC sessions like GC.)
if (a.c->proxy_session->remote_guild_card_number >= 0) {
@@ -1384,7 +1380,7 @@ ChatCommandDefinition cc_lobby_info(
}
}
vector<const char*> cheats_tokens;
std::vector<const char*> cheats_tokens;
if (a.c->check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
cheats_tokens.emplace_back("HP");
}
@@ -1396,7 +1392,7 @@ ChatCommandDefinition cc_lobby_info(
msg += phosg::join(cheats_tokens, ",");
}
vector<const char*> behaviors_tokens;
std::vector<const char*> behaviors_tokens;
if (a.c->check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
behaviors_tokens.emplace_back("SWA");
}
@@ -1419,7 +1415,7 @@ ChatCommandDefinition cc_lobby_info(
send_text_message(a.c->channel, msg);
} else { // Not proxy session
vector<string> lines;
std::vector<std::string> lines;
auto l = a.c->lobby.lock();
if (!l) {
@@ -1476,7 +1472,7 @@ ChatCommandDefinition cc_lobby_info(
lines.emplace_back(std::format("$C7Lobby ID: $C6{:08X}$C7", l->lobby_id));
}
string slots_str = "Slots: ";
std::string slots_str = "Slots: ";
for (size_t z = 0; z < l->clients.size(); z++) {
if (!l->clients[z]) {
slots_str += std::format("$C0{:X}$C7", z);
@@ -1542,7 +1538,7 @@ ChatCommandDefinition cc_loadchar(
throw precondition_failed("$C6Player index must\nbe in range 1-{}", s->num_backup_character_slots);
}
shared_ptr<PSOGCEp3CharacterFile::Character> ep3_char;
std::shared_ptr<PSOGCEp3CharacterFile::Character> ep3_char;
if (is_ep3(a.c->version())) {
ep3_char = a.c->load_ep3_backup_character(a.c->login->account->account_id, index);
} else {
@@ -1578,7 +1574,7 @@ ChatCommandDefinition cc_loadchar(
send_player_leave_notification(l, a.c->lobby_client_id);
s->send_lobby_join_notifications(l, a.c);
}
} catch (const exception& e) {
} catch (const std::exception& e) {
a.c->log.warning_f("Failed to set extended player info: {}", e.what());
throw precondition_failed("Failed to set\nplayer info:\n{}", e.what());
}
@@ -1600,14 +1596,14 @@ ChatCommandDefinition cc_loadchar(
co_await send_set_extended_player_info(*ep3_char);
} else if (a.c->version() == Version::XB_V3) {
if (!a.c->login || !a.c->login->xb_license) {
throw runtime_error("XB client is not logged in");
throw std::runtime_error("XB client is not logged in");
}
PSOXBCharacterFile::Character xb_char = *a.c->character_file();
xb_char.guild_card.xb_user_id_high = (a.c->login->xb_license->user_id >> 32) & 0xFFFFFFFF;
xb_char.guild_card.xb_user_id_low = a.c->login->xb_license->user_id & 0xFFFFFFFF;
co_await send_set_extended_player_info(xb_char);
} else {
throw logic_error("unimplemented extended player info version");
throw std::logic_error("unimplemented extended player info version");
}
} else {
@@ -1627,10 +1623,10 @@ ChatCommandDefinition cc_makeobj(
auto tokens = phosg::split(a.text, ' ');
if (tokens.size() < 1) {
throw runtime_error("not enough arguments");
throw std::runtime_error("not enough arguments");
}
uint32_t base_type_high = stoul(tokens[0], nullptr, 0) << 16;
uint32_t base_type_high = std::stoul(tokens[0], nullptr, 0) << 16;
VectorXYZF pos = a.c->pos;
VectorXYZI angle{0, 0, 0};
VectorXYZF param123{0, 0, 0};
@@ -1638,7 +1634,7 @@ ChatCommandDefinition cc_makeobj(
for (size_t z = 1; z < tokens.size(); z++) {
auto subtokens = phosg::split(tokens[z], ':');
if (subtokens.size() != 2 || subtokens[0].size() != 1) {
throw runtime_error("invalid argument: " + tokens[z]);
throw std::runtime_error("invalid argument: " + tokens[z]);
}
switch (tolower(subtokens[0].front())) {
case 'X':
@@ -1684,11 +1680,11 @@ ChatCommandDefinition cc_makeobj(
param456.z = stol(subtokens[1], nullptr, 0);
break;
default:
throw runtime_error("invalid argument: " + tokens[z]);
throw std::runtime_error("invalid argument: " + tokens[z]);
}
}
unordered_map<string, uint32_t> label_writes{
std::unordered_map<std::string, uint32_t> label_writes{
{"base_type_high", base_type_high},
{"floor_low", a.c->floor},
{"pos_x", std::bit_cast<uint32_t>(pos.x.load())},
@@ -1809,8 +1805,7 @@ ChatCommandDefinition cc_password(
} else {
l->password = a.text;
string escaped = remove_color(l->password);
send_text_message_fmt(l, "$C6Game password:\n{}", escaped);
send_text_message_fmt(l, "$C6Game password:\n{}", remove_color(l->password));
}
co_return;
});
@@ -1820,15 +1815,15 @@ ChatCommandDefinition cc_patch(
+[](const Args& a) -> asio::awaitable<void> {
auto tokens = phosg::split(a.text, ' ');
if (tokens.empty()) {
throw runtime_error("not enough arguments");
throw std::runtime_error("not enough arguments");
}
string patch_name = std::move(tokens[0]);
unordered_map<string, uint32_t> label_writes;
std::string patch_name = std::move(tokens[0]);
std::unordered_map<std::string, uint32_t> label_writes;
for (size_t z = 0; z < tokens.size() - 1; z++) {
const auto& token = tokens[z + 1];
size_t equals_pos = token.find('=');
string key, value;
std::string key, value;
if (equals_pos == std::string::npos) {
key = std::format("arg{}", z);
value = token;
@@ -1837,9 +1832,9 @@ ChatCommandDefinition cc_patch(
value = token.substr(equals_pos + 1);
}
if (value.contains('.')) { // float
label_writes.emplace(std::move(key), std::bit_cast<uint32_t>(stof(value, nullptr)));
label_writes.emplace(std::move(key), std::bit_cast<uint32_t>(std::stof(value, nullptr)));
} else { // int
label_writes.emplace(std::move(key), stoul(value, nullptr, 0));
label_writes.emplace(std::move(key), std::stoul(value, nullptr, 0));
}
}
@@ -1870,7 +1865,7 @@ ChatCommandDefinition cc_patch(
ret.return_value.load(), ret.return_value.load(), std::bit_cast<float>(ret.return_value.load()));
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
send_text_message(a.c, "$C6Invalid function");
}
co_return;
@@ -1911,10 +1906,10 @@ ChatCommandDefinition cc_ping(
co_return;
});
static string file_path_for_recording(const std::string& args, uint32_t account_id, bool compressed) {
static std::string file_path_for_recording(const std::string& args, uint32_t account_id, bool compressed) {
for (char ch : args) {
if (ch <= 0x20 || ch > 0x7E || ch == '/') {
throw runtime_error("invalid recording name");
throw std::runtime_error("invalid recording name");
}
}
return std::format("system/ep3/battle-records/{:010}_{}.mzr{}", account_id, args, compressed ? "" : "d");
@@ -1932,13 +1927,13 @@ ChatCommandDefinition cc_playrec(
} else if (!l->is_game()) {
auto s = a.c->require_server_state();
string filename = a.text;
std::string filename = a.text;
bool start_battle_player_immediately = (filename.at(0) != '!');
if (!start_battle_player_immediately) {
filename = filename.substr(1);
}
string data;
std::string data;
try {
data = phosg::load_file(file_path_for_recording(filename, a.c->login->account->account_id, false));
} catch (const phosg::cannot_open_file&) {
@@ -1949,8 +1944,8 @@ ChatCommandDefinition cc_playrec(
throw precondition_failed("$C4The recording does\nnot exist");
}
}
auto record = make_shared<Episode3::BattleRecord>(data);
auto battle_player = make_shared<Episode3::BattleRecordPlayer>(s->io_context, record);
auto record = std::make_shared<Episode3::BattleRecord>(data);
auto battle_player = std::make_shared<Episode3::BattleRecordPlayer>(s->io_context, record);
auto game = create_game_generic(
s, a.c, filename, "", Episode::EP3, GameMode::NORMAL, Difficulty::NORMAL, false, nullptr, battle_player);
if (game) {
@@ -1975,7 +1970,7 @@ ChatCommandDefinition cc_qcall(
auto l = a.c->require_lobby();
if (l->is_game() && l->quest) {
send_quest_function_call(a.c, stoul(a.text, nullptr, 0));
send_quest_function_call(a.c, std::stoul(a.text, nullptr, 0));
}
co_return;
});
@@ -1986,7 +1981,7 @@ ChatCommandDefinition cc_qcheck(
a.check_is_proxy(false);
auto l = a.c->require_lobby();
uint16_t flag_num = stoul(a.text, nullptr, 0);
uint16_t flag_num = std::stoul(a.text, nullptr, 0);
if (l->is_game()) {
if (!l->quest_flags_known || l->quest_flags_known->get(l->difficulty, flag_num)) {
@@ -2009,7 +2004,7 @@ ChatCommandDefinition cc_qcheck(
static void command_qset_qclear(const Args& a, bool should_set) {
a.check_is_game(true);
uint16_t flag_num = stoul(a.text, nullptr, 0);
uint16_t flag_num = std::stoul(a.text, nullptr, 0);
if (!a.c->proxy_session) {
a.check_debug_enabled();
@@ -2072,11 +2067,11 @@ ChatCommandDefinition cc_qfread(
const auto& def = s->quest_counter_fields.at(a.text);
counter_index = def.first;
mask = def.second;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("$C4Invalid field name");
}
if (mask == 0) {
throw runtime_error("invalid quest counter definition");
throw std::runtime_error("invalid quest counter definition");
}
uint32_t counter_value = a.c->character_file()->quest_counters.at(counter_index) & mask;
@@ -2098,7 +2093,7 @@ ChatCommandDefinition cc_qgread(
{"$qgread"},
+[](const Args& a) -> asio::awaitable<void> {
a.check_is_proxy(false);
uint8_t counter_num = stoul(a.text, nullptr, 0);
uint8_t counter_num = std::stoul(a.text, nullptr, 0);
const auto& counters = a.c->character_file()->quest_counters;
if (counter_num >= counters.size()) {
throw precondition_failed("$C7Counter ID must be\nless than {}", counters.size());
@@ -2126,8 +2121,8 @@ ChatCommandDefinition cc_qgwrite(
throw precondition_failed("$C6Incorrect number\nof arguments");
}
uint8_t counter_num = stoul(tokens[0], nullptr, 0);
uint32_t value = stoul(tokens[1], nullptr, 0);
uint8_t counter_num = std::stoul(tokens[0], nullptr, 0);
uint32_t value = std::stoul(tokens[1], nullptr, 0);
auto& counters = a.c->character_file()->quest_counters;
if (counter_num >= counters.size()) {
throw precondition_failed("$C7Counter ID must be\nless than {}", counters.size());
@@ -2158,10 +2153,10 @@ static void command_qsync_qsyncall(const Args& a, bool send_to_lobby) {
G_SyncQuestRegister_6x77 cmd;
cmd.header = {0x77, 0x03, 0x0000};
cmd.register_number = stoul(tokens[0].substr(1), nullptr, 0);
cmd.register_number = std::stoul(tokens[0].substr(1), nullptr, 0);
cmd.unused = 0;
if (tokens[0][0] == 'r') {
cmd.value.as_int = stoul(tokens[1], nullptr, 0);
cmd.value.as_int = std::stoul(tokens[1], nullptr, 0);
} else if (tokens[0][0] == 'f') {
cmd.value.as_float = stof(tokens[1]);
} else {
@@ -2287,24 +2282,24 @@ ChatCommandDefinition cc_readmem(
+[](const Args& a) -> asio::awaitable<void> {
a.check_debug_enabled();
uint32_t addr = stoul(a.text, nullptr, 16);
uint32_t addr = std::stoul(a.text, nullptr, 16);
if (!console_address_in_range(a.c->version(), addr)) {
throw precondition_failed("$C4Address out of\nrange");
}
co_await prepare_client_for_patches(a.c);
shared_ptr<const ClientFunctionIndex::Function> fn;
std::shared_ptr<const ClientFunctionIndex::Function> fn;
try {
auto s = a.c->require_server_state();
fn = s->client_functions->get("ReadMemoryWord", a.c->specific_version);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("Invalid patch name");
}
unordered_map<string, uint32_t> label_writes{{"address", addr}};
std::unordered_map<std::string, uint32_t> label_writes{{"address", addr}};
auto res = co_await send_function_call(a.c, fn, label_writes);
string data_str;
std::string data_str;
if (is_big_endian(a.c->version())) {
be_uint32_t v = res.return_value.load();
data_str = phosg::format_data_string(&v, sizeof(v));
@@ -2362,9 +2357,9 @@ ChatCommandDefinition cc_saverec(
if (!a.c->ep3_prev_battle_record) {
throw precondition_failed("$C4No finished\nrecording is\npresent");
}
string file_path = file_path_for_recording(a.text, a.c->login->account->account_id, false);
string data = a.c->ep3_prev_battle_record->serialize();
phosg::save_file(file_path, data);
phosg::save_file(
file_path_for_recording(a.text, a.c->login->account->account_id, false),
a.c->ep3_prev_battle_record->serialize());
send_text_message(a.c, "$C7Recording saved");
a.c->ep3_prev_battle_record.reset();
co_return;
@@ -2374,7 +2369,7 @@ static asio::awaitable<void> command_send_command(const Args& a, bool to_client,
if (!a.c->proxy_session) {
a.check_debug_enabled();
}
string data = phosg::parse_data_string(a.text);
std::string data = phosg::parse_data_string(a.text);
data.resize((data.size() + 3) & (~3));
if (to_client) {
if (send_protected) {
@@ -2452,7 +2447,7 @@ ChatCommandDefinition cc_setassist(
auto l = a.c->require_lobby();
if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
throw std::logic_error("non-Ep3 client in Ep3 game");
}
if (!l->ep3_server) {
throw precondition_failed("$C6Episode 3 server\nis not initialized");
@@ -2465,10 +2460,10 @@ ChatCommandDefinition cc_setassist(
}
size_t client_id;
string card_name;
std::string card_name;
if (isdigit(a.text[0])) {
auto tokens = phosg::split(a.text, ' ', 1);
client_id = stoul(tokens.at(0), nullptr, 0) - 1;
client_id = std::stoul(tokens.at(0), nullptr, 0) - 1;
card_name = tokens.at(1);
} else {
client_id = a.c->lobby_client_id;
@@ -2478,10 +2473,10 @@ ChatCommandDefinition cc_setassist(
throw precondition_failed("$C6Invalid client ID");
}
shared_ptr<const Episode3::CardIndex::CardEntry> ce;
std::shared_ptr<const Episode3::CardIndex::CardEntry> ce;
try {
ce = l->ep3_server->options.card_index->definition_for_name_normalized(card_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("$C6Card not found");
}
if (ce->def.type != Episode3::CardType::ASSIST) {
@@ -2495,7 +2490,7 @@ ChatCommandDefinition cc_server_info(
{"$si"},
+[](const Args& a) -> asio::awaitable<void> {
auto s = a.c->require_server_state();
string uptime_str = phosg::format_duration(phosg::now() - s->creation_time);
std::string uptime_str = phosg::format_duration(phosg::now() - s->creation_time);
send_text_message_fmt(a.c,
"Uptime: $C6{}$C7\nLobbies: $C6{}$C7\nClients: $C6{}$C7(g) $C6{}$C7(p)",
uptime_str,
@@ -2523,7 +2518,7 @@ ChatCommandDefinition cc_silence(
}
target->can_chat = !target->can_chat;
string target_name = name_for_client(target);
std::string target_name = name_for_client(target);
send_text_message_fmt(a.c, "$C6{} {}silenced", target_name, target->can_chat ? "un" : "");
co_return;
});
@@ -2538,7 +2533,7 @@ ChatCommandDefinition cc_song(
song = -song;
send_ep3_change_music(a.c->proxy_session->server_channel, song);
}
send_ep3_change_music(a.c->channel, stoul(a.text, nullptr, 0));
send_ep3_change_music(a.c->channel, std::stoul(a.text, nullptr, 0));
co_return;
});
@@ -2546,7 +2541,7 @@ ChatCommandDefinition cc_sound(
{"$sound"},
+[](const Args& a) -> asio::awaitable<void> {
bool echo_to_all = (!a.text.empty() && a.text[0] == '!');
uint32_t sound_id = stoul(echo_to_all ? a.text.substr(1) : a.text, nullptr, 16);
uint32_t sound_id = std::stoul(echo_to_all ? a.text.substr(1) : a.text, nullptr, 16);
auto l = a.c->require_lobby();
uint8_t area = l->is_game() ? l->area_for_floor(a.c->version(), a.c->floor) : 0x0F;
@@ -2571,7 +2566,7 @@ ChatCommandDefinition cc_spec(
a.check_is_ep3(true);
auto l = a.c->require_lobby();
if (!l->is_ep3()) {
throw logic_error("Episode 3 client in non-Episode 3 game");
throw std::logic_error("Episode 3 client in non-Episode 3 game");
}
// In non-tournament games, only the leader can do this; in a tournament match, the players don't have control
@@ -2613,7 +2608,7 @@ ChatCommandDefinition cc_stat(
a.check_is_ep3(true);
auto l = a.c->require_lobby();
if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
throw std::logic_error("non-Ep3 client in Ep3 game");
}
if (!l->ep3_server) {
throw precondition_failed("$C6Episode 3 server\nis not initialized");
@@ -2627,7 +2622,7 @@ ChatCommandDefinition cc_stat(
}
uint8_t team_id = ps->get_team_id();
if (team_id > 1) {
throw logic_error("team ID is incorrect");
throw std::logic_error("team ID is incorrect");
}
if (a.text == "rank") {
@@ -2636,8 +2631,8 @@ ChatCommandDefinition cc_stat(
const char* rank_name = ps->stats.name_for_rank(rank);
send_text_message_fmt(a.c, "$C7Score: {:g}\nRank: {} ({})", score, rank, rank_name);
} else if (a.text == "duration") {
string s = phosg::format_duration(phosg::now() - l->ep3_server->battle_start_usecs);
send_text_message_fmt(a.c, "$C7Duration: {}", s);
send_text_message_fmt(a.c, "$C7Duration: {}",
phosg::format_duration(phosg::now() - l->ep3_server->battle_start_usecs));
} else if (a.text == "fcs-destroyed") {
send_text_message_fmt(a.c, "$C7Team FCs destroyed:\n{}", l->ep3_server->team_num_ally_fcs_destroyed[team_id]);
} else if (a.text == "cards-destroyed") {
@@ -2694,7 +2689,7 @@ ChatCommandDefinition cc_surrender(
a.check_is_ep3(true);
auto l = a.c->require_lobby();
if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
throw std::logic_error("non-Ep3 client in Ep3 game");
}
if (!l->ep3_server) {
throw precondition_failed("$C6Episode 3 server\nis not initialized");
@@ -2706,7 +2701,7 @@ ChatCommandDefinition cc_surrender(
if (!ps || !ps->is_alive()) {
throw precondition_failed("$C6Defeated players\ncannot surrender");
}
string name = remove_color(a.c->character_file()->disp.name.decode(a.c->language()));
std::string name = remove_color(a.c->character_file()->disp.name.decode(a.c->language()));
send_text_message_fmt(l, "$C6{} has\nsurrendered", name);
for (const auto& watcher_l : l->watcher_lobbies) {
send_text_message_fmt(watcher_l, "$C6{} has\nsurrendered", name);
@@ -2736,10 +2731,10 @@ static void command_swset_swclear(const Args& a, bool should_set) {
uint8_t floor, flag_num;
if (tokens.size() == 1) {
floor = a.c->floor;
flag_num = stoul(tokens[0], nullptr, 0);
flag_num = std::stoul(tokens[0], nullptr, 0);
} else if (tokens.size() == 2) {
floor = stoul(tokens[0], nullptr, 0);
flag_num = stoul(tokens[1], nullptr, 0);
floor = std::stoul(tokens[0], nullptr, 0);
flag_num = std::stoul(tokens[1], nullptr, 0);
} else {
throw precondition_failed("$C4Incorrect parameters");
}
@@ -2859,7 +2854,7 @@ ChatCommandDefinition cc_unset(
a.check_cheats_enabled_in_game(s->cheat_flags.ep3_unset_field_character);
auto l = a.c->require_lobby();
if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
throw std::logic_error("non-Ep3 client in Ep3 game");
}
if (!l->ep3_server) {
throw precondition_failed("$C6Episode 3 server\nis not initialized");
@@ -2886,8 +2881,8 @@ ChatCommandDefinition cc_variations(
auto s = a.c->require_server_state();
a.check_cheats_enabled_in_game(s->cheat_flags.override_variations);
a.c->override_variations = make_unique<Variations>();
for (size_t z = 0; z < min<size_t>(a.c->override_variations->entries.size() * 2, a.text.size()); z++) {
a.c->override_variations = std::make_unique<Variations>();
for (size_t z = 0; z < std::min<size_t>(a.c->override_variations->entries.size() * 2, a.text.size()); z++) {
auto& entry = a.c->override_variations->entries.at(z / 2);
if (z & 1) {
entry.entities = a.text[z] - '0';
@@ -2905,7 +2900,7 @@ static void command_warp(const Args& a, bool is_warpall) {
auto s = a.c->require_server_state();
a.check_cheats_enabled_or_allowed(s->cheat_flags.warp);
uint32_t floor = stoul(a.text, nullptr, 0);
uint32_t floor = std::stoul(a.text, nullptr, 0);
if (!is_warpall && (a.c->floor == floor)) {
return;
}
@@ -2959,7 +2954,7 @@ ChatCommandDefinition cc_what(
co_return;
}
shared_ptr<const Lobby::FloorItem> nearest_fi;
std::shared_ptr<const Lobby::FloorItem> nearest_fi;
float min_dist2 = 0.0f;
for (const auto& it : l->floor_item_managers.at(a.c->floor).items) {
if (!it.second->visible_to_client(a.c->lobby_client_id)) {
@@ -2976,9 +2971,8 @@ ChatCommandDefinition cc_what(
throw precondition_failed("$C4No items are near you");
} else {
auto s = a.c->require_server_state();
string name = s->describe_item(
a.c->version(), nearest_fi->data, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES);
send_text_message(a.c, name);
send_text_message(
a.c, s->describe_item(a.c->version(), nearest_fi->data, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES));
}
co_return;
});
@@ -3005,8 +2999,8 @@ static void whatobj_whatene_fn(const Args& a, bool include_objs, bool include_en
double min_dist2 = -1.0;
VectorXYZF nearest_worldspace_pos;
shared_ptr<const MapState::ObjectState> nearest_obj;
shared_ptr<const MapState::EnemyState> nearest_ene;
std::shared_ptr<const MapState::ObjectState> nearest_obj;
std::shared_ptr<const MapState::EnemyState> nearest_ene;
auto check_entity = [&](auto& nearest_entity, auto entity, const auto& def) -> void {
VectorXYZF worldspace_pos;
@@ -3015,7 +3009,7 @@ static void whatobj_whatene_fn(const Args& a, bool include_objs, bool include_en
const auto& room = s->room_layout_index->get_room(area, layout_var, def.set_entry->room);
// This is the order in which the game does the rotations; not sure why
worldspace_pos = def.set_entry->pos.rotate_x(room.angle.x).rotate_z(room.angle.z).rotate_y(room.angle.y) + room.position;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
a.c->log.warning_f("Can't find definition for room {:02X}:{:02X}:{:08X}", area, layout_var, def.set_entry->room);
worldspace_pos = def.set_entry->pos;
}
@@ -3056,7 +3050,7 @@ static void whatobj_whatene_fn(const Args& a, bool include_objs, bool include_en
// we print that if it's set, and print the object if not.
if (nearest_ene) {
const auto* set_entry = nearest_ene->super_ene->version(a.c->version()).set_entry;
string type_name = MapFile::name_for_enemy_type(set_entry->base_type, a.c->version(), area);
std::string type_name = MapFile::name_for_enemy_type(set_entry->base_type, a.c->version(), area);
uint8_t area = l->area_for_floor(a.c->version(), a.c->floor);
send_text_message_fmt(a.c, "$C5E-{:03X}\n$C6{}\n$C2{}\n$C7X:{:.2f} Z:{:.2f}",
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), area, l->difficulty, l->event)),
@@ -3120,7 +3114,7 @@ ChatCommandDefinition cc_where(
if (!a.c->proxy_session && l && l->is_game()) {
for (auto lc : l->clients) {
if (lc && (lc != a.c)) {
string name = lc->character_file()->disp.name.decode(lc->language());
std::string name = lc->character_file()->disp.name.decode(lc->language());
send_text_message_fmt(a.c, "$C6{}$C7 {:X}:{}",
name, lc->floor, FloorDefinition::get(l->episode, lc->floor).short_name);
}
@@ -3139,7 +3133,7 @@ ChatCommandDefinition cc_writemem(
throw precondition_failed("Incorrect arguments");
}
uint32_t addr = stoul(tokens[0], nullptr, 16);
uint32_t addr = std::stoul(tokens[0], nullptr, 16);
if (!console_address_in_range(a.c->version(), addr)) {
throw precondition_failed("$C4Address out of\nrange");
}
@@ -3152,9 +3146,9 @@ ChatCommandDefinition cc_writemem(
try {
auto s = a.c->require_server_state();
auto fn = s->client_functions->get("WriteMemory", a.c->specific_version);
unordered_map<string, uint32_t> label_writes{{"dest_addr", addr}, {"size", data.size()}};
std::unordered_map<std::string, uint32_t> label_writes{{"dest_addr", addr}, {"size", data.size()}};
co_await send_function_call(a.c, fn, label_writes, data.data(), data.size());
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("Invalid patch name");
}
co_return;
@@ -3177,12 +3171,12 @@ ChatCommandDefinition cc_nativecall(
throw precondition_failed("Incorrect arguments");
}
uint32_t addr = stoul(tokens[0], nullptr, 16);
uint32_t addr = std::stoul(tokens[0], nullptr, 16);
if (!console_address_in_range(a.c->version(), addr)) {
throw precondition_failed("$C4Function address\nout of range");
}
unordered_map<string, uint32_t> label_writes{{"call_addr", addr}};
std::unordered_map<std::string, uint32_t> label_writes{{"call_addr", addr}};
for (size_t z = 0; z < tokens.size() - 1; z++) {
label_writes.emplace(std::format("arg{}", z), stoull(tokens[z + 1], nullptr, 16));
}
@@ -3193,7 +3187,7 @@ ChatCommandDefinition cc_nativecall(
auto s = a.c->require_server_state();
auto fn = s->client_functions->get("CallNativeFunction", a.c->specific_version);
co_await send_function_call(a.c, fn, label_writes);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw precondition_failed("Invalid patch name");
}
co_return;
@@ -3203,12 +3197,12 @@ ChatCommandDefinition cc_nativecall(
// Dispatch methods
struct SplitCommand {
string name;
string args;
std::string name;
std::string args;
SplitCommand(const string& text) {
SplitCommand(const std::string& text) {
size_t space_pos = text.find(' ');
if (space_pos != string::npos) {
if (space_pos != std::string::npos) {
this->name = text.substr(0, space_pos);
this->args = text.substr(space_pos + 1);
} else {
@@ -3231,7 +3225,7 @@ asio::awaitable<void> on_chat_command(std::shared_ptr<Client> c, const std::stri
const ChatCommandDefinition* def = nullptr;
try {
def = ChatCommandDefinition::all_defs.at(cmd.name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (!def) {
send_text_message(c, "$C6Unknown command");
@@ -3242,7 +3236,7 @@ asio::awaitable<void> on_chat_command(std::shared_ptr<Client> c, const std::stri
co_await def->handler(Args{cmd.args, check_permissions, c});
} catch (const precondition_failed& e) {
send_text_message(c, e.what());
} catch (const exception& e) {
} catch (const std::exception& e) {
send_text_message(c, "$C6Failed:\n" + remove_color(e.what()));
}
}
+5 -7
View File
@@ -5,9 +5,7 @@
#include "Client.hh"
using namespace std;
const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
ChoiceSearchCategory{
.id = 0x0001,
.name = "Level",
@@ -24,7 +22,7 @@ const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
{0x0009, "Level 121-160"},
{0x000A, "Level 161-200"},
},
.client_matches = +[](shared_ptr<Client> searcher_c, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
.client_matches = +[](std::shared_ptr<Client> searcher_c, std::shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
if (choice_id == 0x0000) {
return true;
}
@@ -75,7 +73,7 @@ const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
{0x0008, "FOnewm"},
{0x0009, "FOnewearl"},
},
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
.client_matches = +[](std::shared_ptr<Client>, std::shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
switch (choice_id) {
case 0x0000:
return true;
@@ -102,7 +100,7 @@ const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
{0x0005, "GC Episode 3"},
{0x0006, "BB"},
},
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
.client_matches = +[](std::shared_ptr<Client>, std::shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
if (choice_id == 0x0000) {
return true;
}
@@ -142,7 +140,7 @@ const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
{0x0005, "Battle"},
{0x0006, "Challenge"},
},
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
.client_matches = +[](std::shared_ptr<Client>, std::shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
uint16_t target_choice_id = target_c->character_file()->choice_search_config.get_setting(0x0204);
return (choice_id == 0) || (target_choice_id == 0) || (choice_id == target_choice_id);
},
+118 -123
View File
@@ -16,11 +16,9 @@
#include "Server.hh"
#include "Version.hh"
using namespace std;
const uint64_t CLIENT_CONFIG_MAGIC = 0x8399AC32;
static atomic<uint64_t> next_id(1);
static std::atomic<uint64_t> next_client_id(1);
void Client::set_flags_for_version(Version version, int64_t sub_version) {
this->set_flag(Flag::PROXY_CHAT_COMMANDS_ENABLED);
@@ -82,7 +80,7 @@ void Client::set_flags_for_version(Version version, int64_t sub_version) {
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
default:
throw logic_error("invalid game version");
throw std::logic_error("invalid game version");
}
break;
@@ -151,7 +149,7 @@ void Client::set_flags_for_version(Version version, int64_t sub_version) {
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break;
default:
throw runtime_error(std::format("unknown sub_version {:X}", sub_version));
throw std::runtime_error(std::format("unknown sub_version {:X}", sub_version));
}
}
@@ -176,11 +174,11 @@ void Client::set_drop_notification_mode(ItemDropNotificationMode new_mode) {
}
Client::Client(
shared_ptr<GameServer> server,
shared_ptr<Channel> channel,
std::shared_ptr<GameServer> server,
std::shared_ptr<Channel> channel,
ServerBehavior server_behavior)
: server(server),
id(next_id++),
id(next_client_id++),
log(std::format("[C-{:X}] ", this->id), client_log.min_level),
channel(channel),
server_behavior(server_behavior),
@@ -239,13 +237,12 @@ Client::~Client() {
}
void Client::update_channel_name() {
string default_name = this->channel->default_name();
std::string default_name = this->channel->default_name();
auto player = this->character_file(false, false);
if (player) {
string name_str = player->disp.name.decode(this->language());
size_t level = player->disp.stats.level + 1;
this->channel->name = std::format("C-{:X} ({} Lv.{}) @ {}", this->id, name_str, level, default_name);
this->channel->name = std::format("C-{:X} ({} Lv.{}) @ {}",
this->id, player->disp.name.decode(this->language()), player->disp.stats.level + 1, default_name);
} else {
this->channel->name = std::format("C-{:X} @ {}", this->id, default_name);
}
@@ -278,7 +275,7 @@ void Client::reschedule_ping_and_timeout_timers() {
// The game doesn't use this timestamp; we only use it for debugging purposes
be_uint64_t timestamp = phosg::now();
this->channel->send(0x1D, 0x00, &timestamp, sizeof(be_uint64_t));
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.warning_f("Failed to send ping: {}", e.what());
}
}
@@ -306,25 +303,25 @@ void Client::convert_account_to_temporary_if_nte() {
}
}
shared_ptr<ServerState> Client::require_server_state() const {
std::shared_ptr<ServerState> Client::require_server_state() const {
auto server = this->server.lock();
if (!server) {
throw logic_error("server is deleted");
throw std::logic_error("server is deleted");
}
return server->get_state();
}
shared_ptr<Lobby> Client::require_lobby() const {
std::shared_ptr<Lobby> Client::require_lobby() const {
auto l = this->lobby.lock();
if (!l) {
throw runtime_error("client not in any lobby");
throw std::runtime_error("client not in any lobby");
}
return l;
}
shared_ptr<const TeamIndex::Team> Client::team() const {
std::shared_ptr<const TeamIndex::Team> Client::team() const {
if (!this->login) {
throw logic_error("Client::team called on client with no account");
throw std::logic_error("Client::team called on client with no account");
}
if (this->login->account->bb_team_id == 0) {
@@ -352,7 +349,7 @@ shared_ptr<const TeamIndex::Team> Client::team() const {
// The team membership is valid, but the player name may be different; update the team membership if needed
if (p) {
auto& m = member_it->second;
string name = p->disp.name.decode(this->language());
std::string name = p->disp.name.decode(this->language());
if (m.name != name) {
this->log.info_f("Updating player name in team config");
s->team_index->update_member_name(this->login->account->account_id, name);
@@ -363,8 +360,8 @@ shared_ptr<const TeamIndex::Team> Client::team() const {
}
bool Client::evaluate_quest_availability_expression(
shared_ptr<const IntegralExpression> expr,
shared_ptr<const Lobby> game,
std::shared_ptr<const IntegralExpression> expr,
std::shared_ptr<const Lobby> game,
uint8_t event,
Difficulty difficulty,
size_t num_players,
@@ -376,7 +373,7 @@ bool Client::evaluate_quest_availability_expression(
return true;
}
if (game && !game->quest_flag_values) {
throw logic_error("quest flags are missing from game");
throw std::logic_error("quest flags are missing from game");
}
auto p = this->character_file();
IntegralExpression::Env env = {
@@ -389,15 +386,14 @@ bool Client::evaluate_quest_availability_expression(
};
int64_t ret = expr->evaluate(env);
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
string expr_str = expr->str();
this->log.info_f("Evaluated integral expression {} => {}", expr_str, ret ? "TRUE" : "FALSE");
this->log.info_f("Evaluated integral expression {} => {}", expr->str(), ret ? "TRUE" : "FALSE");
}
return ret;
}
bool Client::can_see_quest(
shared_ptr<const Quest> q,
shared_ptr<const Lobby> game,
std::shared_ptr<const Quest> q,
std::shared_ptr<const Lobby> game,
uint8_t event,
Difficulty difficulty,
size_t num_players,
@@ -410,8 +406,8 @@ bool Client::can_see_quest(
}
bool Client::can_play_quest(
shared_ptr<const Quest> q,
shared_ptr<const Lobby> game,
std::shared_ptr<const Quest> q,
std::shared_ptr<const Lobby> game,
uint8_t event,
Difficulty difficulty,
size_t num_players,
@@ -436,7 +432,7 @@ bool Client::can_use_chat_commands() const {
return this->require_server_state()->enable_chat_commands;
}
void Client::set_login(shared_ptr<Login> login) {
void Client::set_login(std::shared_ptr<Login> login) {
this->login = login;
auto s = this->require_server_state();
@@ -458,116 +454,116 @@ void Client::set_login(shared_ptr<Login> login) {
// System file
string Client::system_filename(const string& bb_username) {
std::string Client::system_filename(const std::string& bb_username) {
return std::format("system/players/system_{}.psosys", bb_username);
}
string Client::system_filename() const {
std::string Client::system_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have system data");
throw std::logic_error("non-BB players do not have system data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
return this->system_filename(this->login->bb_license->username);
}
shared_ptr<PSOBBBaseSystemFile> Client::system_file(bool allow_load) {
std::shared_ptr<PSOBBBaseSystemFile> Client::system_file(bool allow_load) {
if (!this->system_data && allow_load) {
this->load_all_files();
}
return this->system_data;
}
shared_ptr<const PSOBBBaseSystemFile> Client::system_file(bool throw_if_missing) const {
std::shared_ptr<const PSOBBBaseSystemFile> Client::system_file(bool throw_if_missing) const {
if (!this->system_data.get() && throw_if_missing) {
throw runtime_error("system file is not loaded");
throw std::runtime_error("system file is not loaded");
}
return this->system_data;
}
void Client::save_system_file() const {
if (!this->system_data) {
throw logic_error("no system file loaded");
throw std::logic_error("no system file loaded");
}
string filename = this->system_filename();
std::string filename = this->system_filename();
phosg::save_object_file(filename, *this->system_data);
this->log.info_f("Saved system file {}", filename);
}
// Guild Card file
string Client::guild_card_filename(const string& bb_username) {
std::string Client::guild_card_filename(const std::string& bb_username) {
return std::format("system/players/guild_cards_{}.psocard", bb_username);
}
string Client::guild_card_filename() const {
std::string Client::guild_card_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved Guild Card files");
throw std::logic_error("non-BB players do not have saved Guild Card files");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
return this->guild_card_filename(this->login->bb_license->username);
}
shared_ptr<PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) {
std::shared_ptr<PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) {
if (!this->guild_card_data && allow_load) {
this->load_all_files();
}
return this->guild_card_data;
}
shared_ptr<const PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) const {
std::shared_ptr<const PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) const {
if (!this->guild_card_data && allow_load) {
throw runtime_error("account data is not loaded");
throw std::runtime_error("account data is not loaded");
}
return this->guild_card_data;
}
void Client::save_guild_card_file() const {
if (!this->guild_card_data.get()) {
throw logic_error("no Guild Card file loaded");
throw std::logic_error("no Guild Card file loaded");
}
string filename = this->guild_card_filename();
std::string filename = this->guild_card_filename();
phosg::save_object_file(filename, *this->guild_card_data);
this->log.info_f("Saved Guild Card file {}", filename);
}
// Character file
string Client::character_filename(const std::string& bb_username, ssize_t index) {
std::string Client::character_filename(const std::string& bb_username, ssize_t index) {
if (bb_username.empty()) {
throw logic_error("non-BB players do not have saved character filenames");
throw std::logic_error("non-BB players do not have saved character filenames");
}
if (index < 0) {
throw logic_error("character index is not set");
throw std::logic_error("character index is not set");
}
return std::format("system/players/player_{}_{}.psochar", bb_username, index);
}
string Client::backup_character_filename(uint32_t account_id, size_t index, bool is_ep3) {
std::string Client::backup_character_filename(uint32_t account_id, size_t index, bool is_ep3) {
return std::format("system/players/backup_player_{}_{}.{}",
account_id, index, is_ep3 ? "pso3char" : "psochar");
}
string Client::character_filename() const {
std::string Client::character_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved character filenames");
throw std::logic_error("non-BB players do not have saved character filenames");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
return this->character_filename(this->login->bb_license->username, this->bb_character_index);
}
shared_ptr<PSOBBCharacterFile> Client::character_file(bool allow_load, bool allow_overlay) {
std::shared_ptr<PSOBBCharacterFile> Client::character_file(bool allow_load, bool allow_overlay) {
if (this->overlay_character_data && allow_overlay) {
return this->overlay_character_data;
}
if (!this->character_data && allow_load) {
if ((this->version() == Version::BB_V4) && (this->bb_character_index < 0)) {
throw runtime_error("character index not specified");
throw std::runtime_error("character index not specified");
}
this->load_all_files();
if (!this->character_data) {
@@ -577,35 +573,35 @@ shared_ptr<PSOBBCharacterFile> Client::character_file(bool allow_load, bool allo
return this->character_data;
}
shared_ptr<const PSOBBCharacterFile> Client::character_file(bool throw_if_missing, bool allow_overlay) const {
std::shared_ptr<const PSOBBCharacterFile> Client::character_file(bool throw_if_missing, bool allow_overlay) const {
if (allow_overlay && this->overlay_character_data) {
return this->overlay_character_data;
}
if (!this->character_data && throw_if_missing) {
throw runtime_error("character data is not loaded");
throw std::runtime_error("character data is not loaded");
}
return this->character_data;
}
void Client::save_character_file(
const string& filename,
shared_ptr<const PSOBBBaseSystemFile> system,
shared_ptr<const PSOBBCharacterFile> character) {
const std::string& filename,
std::shared_ptr<const PSOBBBaseSystemFile> system,
std::shared_ptr<const PSOBBCharacterFile> character) {
PSOCHARFile::save(filename, system, character);
}
void Client::save_ep3_character_file(
const string& filename,
const std::string& filename,
const PSOGCEp3CharacterFile::Character& character) {
phosg::save_file(filename, &character, sizeof(character));
}
void Client::save_character_file() {
if (!this->system_data.get()) {
throw logic_error("no system file loaded");
throw std::logic_error("no system file loaded");
}
if (!this->character_data.get()) {
throw logic_error("no character file loaded");
throw std::logic_error("no character file loaded");
}
if (this->should_update_play_time) {
// This is slightly inaccurate, since fractions of a second are truncated off each time we save. I'm lazy, so
@@ -630,7 +626,7 @@ void Client::create_character_file(
uint32_t guild_card_number,
Language language,
const PlayerDispDataBBPreview& preview,
shared_ptr<const LevelTable> level_table) {
std::shared_ptr<const LevelTable> level_table) {
this->log.info_f("Creating new character file");
this->character_data = PSOBBCharacterFile::create_from_preview(guild_card_number, language, preview, level_table);
this->save_character_file();
@@ -639,8 +635,8 @@ void Client::create_character_file(
std::filesystem::remove(this->bank_filename());
}
void Client::create_battle_overlay(shared_ptr<const BattleRules> rules, shared_ptr<const LevelTable> level_table) {
this->overlay_character_data = make_shared<PSOBBCharacterFile>(*this->character_file(true, false));
void Client::create_battle_overlay(std::shared_ptr<const BattleRules> rules, std::shared_ptr<const LevelTable> level_table) {
this->overlay_character_data = std::make_shared<PSOBBCharacterFile>(*this->character_file(true, false));
if (rules->weapon_and_armor_mode != BattleRules::WeaponAndArmorMode::ALLOW) {
this->overlay_character_data->inventory.remove_all_items_of_type(0);
@@ -657,7 +653,7 @@ void Client::create_battle_overlay(shared_ptr<const BattleRules> rules, shared_p
this->overlay_character_data->inventory.hp_from_materials = 0;
this->overlay_character_data->inventory.tp_from_materials = 0;
uint32_t target_level = clamp<uint32_t>(rules->char_level, 0, 199);
uint32_t target_level = std::clamp<uint32_t>(rules->char_level, 0, 199);
uint8_t char_class = this->overlay_character_data->disp.visual.char_class;
auto& stats = this->overlay_character_data->disp.stats;
@@ -689,11 +685,11 @@ void Client::create_battle_overlay(shared_ptr<const BattleRules> rules, shared_p
}
void Client::create_challenge_overlay(
Version version, size_t template_index, shared_ptr<const LevelTable> level_table) {
Version version, size_t template_index, std::shared_ptr<const LevelTable> level_table) {
auto p = this->character_file(true, false);
const auto& tpl = get_challenge_template_definition(version, p->disp.visual.class_flags, template_index);
this->overlay_character_data = make_shared<PSOBBCharacterFile>(*p);
this->overlay_character_data = std::make_shared<PSOBBCharacterFile>(*p);
auto overlay = this->overlay_character_data;
for (size_t z = 0; z < overlay->inventory.items.size(); z++) {
@@ -738,9 +734,9 @@ void Client::create_challenge_overlay(
// Bank file
string Client::bank_filename(const std::string& bb_username, ssize_t index) {
std::string Client::bank_filename(const std::string& bb_username, ssize_t index) {
if (bb_username.empty()) {
throw logic_error("non-BB players do not have saved bank files");
throw std::logic_error("non-BB players do not have saved bank files");
}
if (index < 0) {
return std::format("system/players/shared_bank_{}.psobank", bb_username);
@@ -749,19 +745,19 @@ string Client::bank_filename(const std::string& bb_username, ssize_t index) {
}
}
string Client::bank_filename() const {
std::string Client::bank_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved bank filenames");
throw std::logic_error("non-BB players do not have saved bank filenames");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
return this->bank_filename(this->login->bb_license->username, this->bb_bank_character_index);
}
std::shared_ptr<PlayerBank> Client::bank_file(bool allow_load) {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved bank files");
throw std::logic_error("non-BB players do not have saved bank files");
}
if (this->has_overlay()) {
throw std::runtime_error("bank is inaccessible when overlay is present");
@@ -771,7 +767,7 @@ std::shared_ptr<PlayerBank> Client::bank_file(bool allow_load) {
// If there's a psobank file, load it and ignore the character file bank
auto filename = this->bank_filename();
auto f = phosg::fopen_unique(filename, "rb");
this->bank_data = make_shared<PlayerBank>();
this->bank_data = std::make_shared<PlayerBank>();
this->bank_data->load(f.get());
this->log.info_f("Loaded bank data from {}", filename);
} catch (const phosg::cannot_open_file&) {
@@ -783,15 +779,16 @@ std::shared_ptr<PlayerBank> Client::bank_file(bool allow_load) {
this->log.info_f("Using bank data from loaded character");
} else if (this->bb_bank_character_index >= 0) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
string filename = this->character_filename(this->login->bb_license->username, this->bb_bank_character_index);
std::string filename = this->character_filename(
this->login->bb_license->username, this->bb_bank_character_index);
auto character = PSOCHARFile::load_shared(filename, false).character_file;
this->bank_data = std::make_shared<PlayerBank>(character->bank);
this->log.info_f("Using bank data from {}", filename);
} else {
// The shared bank doesn't exist; make a new one
this->bank_data = make_shared<PlayerBank>();
this->bank_data = std::make_shared<PlayerBank>();
this->log.info_f("Created new shared bank");
}
}
@@ -811,14 +808,14 @@ std::shared_ptr<const PlayerBank> Client::bank_file(bool throw_if_missing) const
return this->bank_data;
}
void Client::save_bank_file(const string& filename, const PlayerBank& bank) {
void Client::save_bank_file(const std::string& filename, const PlayerBank& bank) {
auto f = phosg::fopen_unique(filename, "wb");
bank.save(f.get());
}
void Client::save_bank_file() const {
if (!this->bank_data) {
throw logic_error("no bank file loaded");
throw std::logic_error("no bank file loaded");
}
auto filename = this->bank_filename();
this->save_bank_file(filename, *this->bank_data);
@@ -840,30 +837,28 @@ void Client::change_bank(ssize_t index) {
// Legacy files
string Client::legacy_account_filename() const {
std::string Client::legacy_account_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved account data");
throw std::logic_error("non-BB players do not have saved account data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
return std::format("system/players/account_{}.nsa", this->login->bb_license->username);
}
string Client::legacy_player_filename() const {
std::string Client::legacy_player_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved player files");
throw std::logic_error("non-BB players do not have saved player files");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
throw std::logic_error("client is not logged in");
}
if (this->bb_character_index < 0) {
throw logic_error("character index is not set");
throw std::logic_error("character index is not set");
}
return std::format(
"system/players/player_{}_{}.nsc",
this->login->bb_license->username,
static_cast<ssize_t>(this->bb_character_index + 1));
return std::format("system/players/player_{}_{}.nsc",
this->login->bb_license->username, static_cast<ssize_t>(this->bb_character_index + 1));
}
void Client::import_blocked_senders(const parray<le_uint32_t, 30>& blocked_senders) {
@@ -877,20 +872,20 @@ void Client::import_blocked_senders(const parray<le_uint32_t, 30>& blocked_sende
void Client::load_all_files() {
if (this->version() != Version::BB_V4) {
this->system_data = make_shared<PSOBBBaseSystemFile>();
this->character_data = make_shared<PSOBBCharacterFile>();
this->guild_card_data = make_shared<PSOBBGuildCardFile>();
this->bank_data = make_shared<PlayerBank>();
this->system_data = std::make_shared<PSOBBBaseSystemFile>();
this->character_data = std::make_shared<PSOBBCharacterFile>();
this->guild_card_data = std::make_shared<PSOBBGuildCardFile>();
this->bank_data = std::make_shared<PlayerBank>();
return;
}
if (!this->login || !this->login->bb_license) {
throw logic_error("cannot load BB player data until client is logged in");
throw std::logic_error("cannot load BB player data until client is logged in");
}
if (!this->system_data) {
string sys_filename = this->system_filename();
std::string sys_filename = this->system_filename();
if (std::filesystem::is_regular_file(sys_filename)) {
this->system_data = make_shared<PSOBBBaseSystemFile>(phosg::load_object_file<PSOBBBaseSystemFile>(sys_filename, true));
this->system_data = std::make_shared<PSOBBBaseSystemFile>(phosg::load_object_file<PSOBBBaseSystemFile>(sys_filename, true));
this->log.info_f("Loaded system data from {}", sys_filename);
} else {
this->log.info_f("System file is missing: {}", sys_filename);
@@ -898,7 +893,7 @@ void Client::load_all_files() {
}
if (!this->character_data && (this->bb_character_index >= 0)) {
string char_filename = this->character_filename();
std::string char_filename = this->character_filename();
if (std::filesystem::is_regular_file(char_filename)) {
auto psochar = PSOCHARFile::load_shared(char_filename, !this->system_data);
this->character_data = psochar.character_file;
@@ -907,7 +902,7 @@ void Client::load_all_files() {
// If there was no .psosys file, use the system file from the .psochar file instead
if (!this->system_data) {
if (!psochar.system_file) {
throw logic_error("account system data not present, and also not loaded from psochar file");
throw std::logic_error("account system data not present, and also not loaded from psochar file");
}
this->system_data = psochar.system_file;
this->log.info_f("Loaded system data from {}", char_filename);
@@ -922,9 +917,9 @@ void Client::load_all_files() {
}
if (!this->guild_card_data) {
string card_filename = this->guild_card_filename();
std::string card_filename = this->guild_card_filename();
if (std::filesystem::is_regular_file(card_filename)) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>(phosg::load_object_file<PSOBBGuildCardFile>(card_filename));
this->guild_card_data = std::make_shared<PSOBBGuildCardFile>(phosg::load_object_file<PSOBBGuildCardFile>(card_filename));
this->guild_card_data->delete_duplicates();
this->log.info_f("Loaded Guild Card data from {}", card_filename);
} else {
@@ -934,25 +929,25 @@ void Client::load_all_files() {
// If any of the above files are still missing, try to load from .nsa/.nsc files instead
if (!this->system_data || (!this->character_data && (this->bb_character_index >= 0)) || !this->guild_card_data) {
string nsa_filename = this->legacy_account_filename();
shared_ptr<LegacySavedAccountDataBB> nsa_data;
std::string nsa_filename = this->legacy_account_filename();
std::shared_ptr<LegacySavedAccountDataBB> nsa_data;
if (std::filesystem::is_regular_file(nsa_filename)) {
nsa_data = make_shared<LegacySavedAccountDataBB>(phosg::load_object_file<LegacySavedAccountDataBB>(nsa_filename));
nsa_data = std::make_shared<LegacySavedAccountDataBB>(phosg::load_object_file<LegacySavedAccountDataBB>(nsa_filename));
if (!nsa_data->signature.eq(LegacySavedAccountDataBB::SIGNATURE)) {
throw runtime_error("account data header is incorrect");
throw std::runtime_error("account data header is incorrect");
}
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>(nsa_data->system_file);
this->system_data = std::make_shared<PSOBBBaseSystemFile>(nsa_data->system_file);
this->log.info_f("Loaded legacy system data from {}", nsa_filename);
}
if (!this->guild_card_data) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>(nsa_data->guild_card_file);
this->guild_card_data = std::make_shared<PSOBBGuildCardFile>(nsa_data->guild_card_file);
this->log.info_f("Loaded legacy Guild Card data from {}", nsa_filename);
}
}
if (!this->character_data && (this->bb_character_index >= 0)) {
string nsc_filename = this->legacy_player_filename();
std::string nsc_filename = this->legacy_player_filename();
if (std::filesystem::is_regular_file(nsc_filename)) {
auto nsc_data = phosg::load_object_file<LegacySavedPlayerDataBB>(nsc_filename);
if (nsc_data.signature == LegacySavedPlayerDataBB::SIGNATURE_V0) {
@@ -962,10 +957,10 @@ void Client::load_all_files() {
nsc_data.battle_records.disconnect_count = 0;
nsc_data.battle_records.unknown_a1.clear(0);
} else if (nsc_data.signature != LegacySavedPlayerDataBB::SIGNATURE_V1) {
throw runtime_error("legacy player data has incorrect signature");
throw std::runtime_error("legacy player data has incorrect signature");
}
this->character_data = make_shared<PSOBBCharacterFile>();
this->character_data = std::make_shared<PSOBBCharacterFile>();
this->character_data->inventory = nsc_data.inventory;
this->character_data->disp = nsc_data.disp;
this->character_data->play_time_seconds = 0;
@@ -1001,7 +996,7 @@ void Client::load_all_files() {
// The system and Guild Card files can be auto-created if they can't be loaded. After this, system_data and
// guild_card_data are always non-null, but character_data may still be null
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>();
this->system_data = std::make_shared<PSOBBBaseSystemFile>();
auto s = this->require_server_state();
if (s->bb_default_keyboard_config) {
this->system_data->key_config = *s->bb_default_keyboard_config;
@@ -1012,7 +1007,7 @@ void Client::load_all_files() {
this->log.info_f("Created new system data");
}
if (!this->guild_card_data) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>();
this->guild_card_data = std::make_shared<PSOBBGuildCardFile>();
this->log.info_f("Created new Guild Card data");
}
@@ -1041,7 +1036,7 @@ void Client::load_all_files() {
}
}
void Client::update_character_data_after_load(shared_ptr<PSOBBCharacterFile> charfile) {
void Client::update_character_data_after_load(std::shared_ptr<PSOBBCharacterFile> charfile) {
charfile->import_tethealla_material_usage(this->require_server_state()->level_table(this->version()));
Language lang = this->language();
@@ -1050,7 +1045,7 @@ void Client::update_character_data_after_load(shared_ptr<PSOBBCharacterFile> cha
charfile->guild_card.language = lang;
}
void Client::update_bank_data_after_load(shared_ptr<PlayerBank> bank) {
void Client::update_bank_data_after_load(std::shared_ptr<PlayerBank> bank) {
auto s = this->require_server_state();
auto limits = s->item_stack_limits(this->version());
for (auto& item : bank->items) {
@@ -1083,17 +1078,17 @@ void Client::save_all() {
}
void Client::load_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index, false);
std::string filename = this->backup_character_filename(account_id, index, false);
this->character_data = PSOCHARFile::load_shared(filename, false).character_file;
this->update_character_data_after_load(this->character_data);
this->v1_v2_last_reported_disp.reset();
}
shared_ptr<PSOGCEp3CharacterFile::Character> Client::load_ep3_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index, true);
auto ch = make_shared<PSOGCEp3CharacterFile::Character>(phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename));
std::shared_ptr<PSOGCEp3CharacterFile::Character> Client::load_ep3_backup_character(uint32_t account_id, size_t index) {
std::string filename = this->backup_character_filename(account_id, index, true);
auto ch = std::make_shared<PSOGCEp3CharacterFile::Character>(phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename));
this->character_data = PSOBBCharacterFile::create_from_file(*ch);
this->ep3_config = make_shared<Episode3::PlayerConfig>(ch->ep3_config);
this->ep3_config = std::make_shared<Episode3::PlayerConfig>(ch->ep3_config);
this->update_character_data_after_load(this->character_data);
this->v1_v2_last_reported_disp.reset();
return ch;
+1 -2
View File
@@ -10,7 +10,6 @@
#include "CommandFormats.hh"
#include "Episode3/BattleRecord.hh"
#include "Episode3/Tournament.hh"
#include "FileContentsCache.hh"
#include "PSOEncryption.hh"
#include "PSOProtocol.hh"
#include "PatchFileIndex.hh"
@@ -27,7 +26,7 @@ struct Lobby;
class Parsed6x70Data;
struct GetPlayerInfoResult {
// Exactly one of the following two shared_ptrs is not null
// Exactly one of the following two std::shared_ptrs is not null
std::shared_ptr<PSOBBCharacterFile> character;
std::shared_ptr<PSOGCEp3CharacterFile::Character> ep3_character;
bool is_full_info; // True if the client sent 30; false if it was 61 or 98
+40 -42
View File
@@ -18,8 +18,6 @@
#include "Compression.hh"
#include "Loggers.hh"
using namespace std;
using Arch = ClientFunctionIndex::Function::Architecture;
const char* name_for_architecture(Arch arch) {
@@ -31,7 +29,7 @@ const char* name_for_architecture(Arch arch) {
case Arch::X86:
return "x86";
default:
throw logic_error("invalid architecture");
throw std::logic_error("invalid architecture");
}
}
@@ -44,7 +42,7 @@ uint32_t specific_version_for_architecture(Arch arch) {
case Arch::X86:
return SPECIFIC_VERSION_X86_INDETERMINATE;
default:
throw logic_error("invalid architecture");
throw std::logic_error("invalid architecture");
}
}
@@ -87,8 +85,8 @@ const T& get_with_sv_fallback(
}
template <bool BE>
string ClientFunctionIndex::Function::generate_client_command_t(
const unordered_map<string, uint32_t>& label_writes,
std::string ClientFunctionIndex::Function::generate_client_command_t(
const std::unordered_map<std::string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
uint32_t override_relocations_offset) const {
@@ -102,11 +100,11 @@ string ClientFunctionIndex::Function::generate_client_command_t(
phosg::StringWriter w;
if (!label_writes.empty()) {
string modified_code = this->code;
std::string modified_code = this->code;
for (const auto& it : label_writes) {
size_t offset = this->label_offsets.at(it.first);
if (offset > modified_code.size() - 4) {
throw runtime_error("label out of range");
throw std::runtime_error("label out of range");
}
*reinterpret_cast<U32T<FooterT::IsBE>*>(modified_code.data() + offset) = it.second;
}
@@ -143,8 +141,8 @@ string ClientFunctionIndex::Function::generate_client_command_t(
return std::move(w.str());
}
string ClientFunctionIndex::Function::generate_client_command(
const unordered_map<string, uint32_t>& label_writes,
std::string ClientFunctionIndex::Function::generate_client_command(
const std::unordered_map<std::string, uint32_t>& label_writes,
const void* suffix_data,
size_t suffix_size,
uint32_t override_relocations_offset) const {
@@ -153,11 +151,11 @@ string ClientFunctionIndex::Function::generate_client_command(
} else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) {
return this->generate_client_command_t<false>(label_writes, suffix_data, suffix_size, override_relocations_offset);
} else {
throw logic_error("invalid architecture");
throw std::logic_error("invalid architecture");
}
}
static unordered_map<uint32_t, std::string> preprocess_function_code(const std::string& text) {
static std::unordered_map<uint32_t, std::string> preprocess_function_code(const std::string& text) {
std::unordered_set<uint32_t> all_specific_versions;
struct Line {
std::string text;
@@ -170,7 +168,7 @@ static unordered_map<uint32_t, std::string> preprocess_function_code(const std::
auto& line = lines.emplace_back();
line.text = std::move(line_text);
string stripped_line = line.text;
std::string stripped_line = line.text;
phosg::strip_whitespace(stripped_line);
if (stripped_line == ".all_versions") {
@@ -190,7 +188,7 @@ static unordered_map<uint32_t, std::string> preprocess_function_code(const std::
static const std::string empty_str = "";
unordered_map<uint32_t, std::string> ret;
std::unordered_map<uint32_t, std::string> ret;
for (uint32_t specific_version : all_specific_versions) {
std::deque<std::string> version_lines;
bool include_current_line = true;
@@ -220,15 +218,15 @@ static unordered_map<uint32_t, std::string> preprocess_function_code(const std::
} else {
std::string line_text = line.text;
size_t vers_offset = line_text.find("<VERS ");
while (vers_offset != string::npos) {
while (vers_offset != std::string::npos) {
size_t end_offset = line_text.find('>', vers_offset + 6);
if (end_offset == string::npos) {
throw runtime_error(std::format("(version {}) (line {}) unterminated <VERS> replacement",
if (end_offset == std::string::npos) {
throw std::runtime_error(std::format("(version {}) (line {}) unterminated <VERS> replacement",
str_for_specific_version(specific_version), line_znum + 1));
}
auto tokens = phosg::split(line_text.substr(vers_offset + 6, end_offset - vers_offset - 6), ' ');
if (current_vers_index >= tokens.size()) {
throw runtime_error(std::format("(version {}) (line {}) invalid <VERS> replacement",
throw std::runtime_error(std::format("(version {}) (line {}) invalid <VERS> replacement",
str_for_specific_version(specific_version), line_znum + 1));
}
line_text = line_text.substr(0, vers_offset) + tokens[current_vers_index] + line_text.substr(end_offset + 1);
@@ -243,12 +241,12 @@ static unordered_map<uint32_t, std::string> preprocess_function_code(const std::
return ret;
}
ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_any_failure) {
map<string, string> source_files;
ClientFunctionIndex::ClientFunctionIndex(const std::string& root_dir, bool raise_on_any_failure) {
std::map<std::string, std::string> source_files;
std::function<void(const std::string&)> add_directory = [&](const std::string& dir) -> void {
for (const auto& item : std::filesystem::directory_iterator(dir)) {
string item_name = item.path().filename().string();
string item_path = dir.ends_with("/") ? (dir + item_name) : (dir + "/" + item_name);
std::string item_name = item.path().filename().string();
std::string item_path = dir.ends_with("/") ? (dir + item_name) : (dir + "/" + item_name);
if (std::filesystem::is_directory(item_path)) {
add_directory(item_path);
} else if (item_path.ends_with(".s") && std::filesystem::is_regular_file(item_path)) {
@@ -268,7 +266,7 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
};
add_directory(root_dir);
unordered_map<string, string> include_cache;
std::unordered_map<std::string, std::string> include_cache;
uint32_t last_menu_item_id = 0;
for (const auto& [source_filename, source] : source_files) {
if (!source_filename.ends_with(".s")) {
@@ -288,15 +286,15 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
}
for (const auto& [specific_version, source] : preprocessed) {
shared_ptr<Function> fn = make_shared<Function>();
std::shared_ptr<Function> fn = std::make_shared<Function>();
fn->short_name = source_filename.substr(0, source_filename.size() - 2);
fn->specific_version = specific_version;
fn->menu_item_id = ++last_menu_item_id;
fn->arch = architecture_for_specific_version(fn->specific_version);
try {
unordered_set<string> get_include_stack;
function<string(const string&, uint32_t)> get_include_for_sv = [&include_cache, &source_files, &get_include_stack, &get_include_for_sv](const string& name, uint32_t specific_version) -> string {
std::unordered_set<std::string> get_include_stack;
std::function<std::string(const std::string&, uint32_t)> get_include_for_sv = [&include_cache, &source_files, &get_include_stack, &get_include_for_sv](const std::string& name, uint32_t specific_version) -> std::string {
try {
return get_with_sv_fallback(include_cache, name, specific_version);
} catch (const std::out_of_range&) {
@@ -309,7 +307,7 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
auto it = source_files.find(name + ".inc.s");
if (it != source_files.end()) {
if (!get_include_stack.emplace(name).second) {
throw runtime_error("Mutual recursion between includes: " + name);
throw std::runtime_error("Mutual recursion between includes: " + name);
}
for (const auto& [include_specific_version, include_source] : preprocess_function_code(it->second)) {
ResourceDASM::EmulatorBase::AssembleResult ret;
@@ -325,7 +323,7 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
ret = ResourceDASM::SH4Emulator::assemble(include_source, get_include);
break;
default:
throw runtime_error("unknown architecture");
throw std::runtime_error("unknown architecture");
}
if (client_functions_log.should_log(phosg::LogLevel::L_DEBUG)) {
client_functions_log.debug_f("({}) Compiled include {}-{}",
@@ -347,7 +345,7 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
return get_with_sv_fallback(include_cache, name, specific_version);
} catch (const std::out_of_range&) {
}
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"Data not found for include {} ({})", name, str_for_specific_version(specific_version)));
};
@@ -365,7 +363,7 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
assembled = ResourceDASM::SH4Emulator::assemble(source, get_include);
break;
default:
throw runtime_error("invalid architecture");
throw std::runtime_error("invalid architecture");
}
fn->code = std::move(assembled.code);
@@ -396,17 +394,17 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
} else if (key == "show_return_value") {
fn->show_return_value = true;
} else {
throw runtime_error("unknown metadata key: " + key);
throw std::runtime_error("unknown metadata key: " + key);
}
}
try {
fn->entrypoint_offset_offset = fn->label_offsets.at("entry_ptr");
} catch (const out_of_range&) {
throw runtime_error("code does not contain entry_ptr label");
} catch (const std::out_of_range&) {
throw std::runtime_error("code does not contain entry_ptr label");
}
set<uint32_t> reloc_indexes;
std::set<uint32_t> reloc_indexes;
for (const auto& it : fn->label_offsets) {
if (it.first.starts_with("reloc")) {
reloc_indexes.emplace(it.second / 4);
@@ -416,13 +414,13 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
for (const auto& it : reloc_indexes) {
uint32_t delta = it - prev_index;
if (delta > 0xFFFF) {
throw runtime_error("relocation delta too far away");
throw std::runtime_error("relocation delta too far away");
}
fn->relocation_deltas.emplace_back(delta);
prev_index = it;
}
} catch (const exception& e) {
} catch (const std::exception& e) {
if (raise_on_any_failure) {
throw;
}
@@ -448,18 +446,18 @@ ClientFunctionIndex::ClientFunctionIndex(const string& root_dir, bool raise_on_a
}
}
shared_ptr<const Menu> ClientFunctionIndex::patch_switches_menu(
std::shared_ptr<const Menu> ClientFunctionIndex::patch_switches_menu(
uint32_t specific_version,
const std::unordered_set<std::string>& server_auto_patches_enabled,
const std::unordered_set<std::string>& client_auto_patches_enabled) const {
auto ret = make_shared<Menu>(MenuID::PATCH_SWITCHES, "Patches");
auto ret = std::make_shared<Menu>(MenuID::PATCH_SWITCHES, "Patches");
ret->items.emplace_back(PatchesMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0);
auto map_it = this->functions_by_specific_version.find(specific_version);
if (map_it != this->functions_by_specific_version.end()) {
for (auto [name, fn] : map_it->second) {
if (fn->appears_in_patches_menu() && !server_auto_patches_enabled.count(fn->short_name)) {
string item_text;
std::string item_text;
item_text.push_back(client_auto_patches_enabled.count(fn->short_name) ? '*' : '-');
item_text += fn->long_name.empty() ? fn->short_name : fn->long_name;
ret->items.emplace_back(
@@ -496,7 +494,7 @@ std::shared_ptr<const ClientFunctionIndex::Function> ClientFunctionIndex::get_by
}
uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
static unordered_map<uint32_t, uint32_t> checksum_to_specific_version;
static std::unordered_map<uint32_t, uint32_t> checksum_to_specific_version;
if (checksum_to_specific_version.empty()) {
struct {
char system_code = 'G';
@@ -517,7 +515,7 @@ uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
uint32_t checksum = phosg::crc32(&data, sizeof(data));
uint32_t specific_version = 0x33000030 | (*game_code2 << 16) | (*region_code << 8) | version_code;
if (!checksum_to_specific_version.emplace(checksum, specific_version).second) {
throw logic_error("multiple specific_versions have same header checksum");
throw std::logic_error("multiple specific_versions have same header checksum");
}
}
}
@@ -529,7 +527,7 @@ uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
uint32_t checksum = phosg::crc32(&data, sizeof(data));
uint32_t specific_version = 0x33004A54 | (*game_code2 << 16);
if (!checksum_to_specific_version.emplace(checksum, specific_version).second) {
throw logic_error("multiple specific_versions have same header checksum");
throw std::logic_error("multiple specific_versions have same header checksum");
}
data.system_code = 'G';
}
+42 -44
View File
@@ -6,8 +6,6 @@
#include "StaticGameData.hh"
#include "Types.hh"
using namespace std;
template <typename IntT, size_t Count>
phosg::JSON to_json(const parray<IntT, Count>& v) {
auto ret = phosg::JSON::list();
@@ -20,7 +18,7 @@ phosg::JSON to_json(const parray<IntT, Count>& v) {
template <typename IntT, size_t Count>
void from_json_into(const phosg::JSON& json, parray<IntT, Count>& ret) {
if (json.size() != Count) {
throw runtime_error("incorrect array length");
throw std::runtime_error("incorrect array length");
}
for (size_t z = 0; z < Count; z++) {
ret[z] = json.at(z).as_int();
@@ -39,7 +37,7 @@ phosg::JSON to_json(const parray<CommonItemSet::Table::Range<IntT>, Count>& v) {
template <typename IntT, size_t Count>
void from_json_into(const phosg::JSON& json, parray<CommonItemSet::Table::Range<IntT>, Count>& ret) {
if (json.size() != Count) {
throw runtime_error("incorrect array length");
throw std::runtime_error("incorrect array length");
}
for (size_t z = 0; z < Count; z++) {
from_json_into(json.at(z), ret[z]);
@@ -60,7 +58,7 @@ void from_json_into(const phosg::JSON& json, CommonItemSet::Table::Range<IntT>&
} else {
const auto& l = json.as_list();
if (l.size() != 2) {
throw runtime_error("incorrect range list length");
throw std::runtime_error("incorrect range list length");
}
ret.min = l.at(0)->as_int();
ret.max = l.at(1)->as_int();
@@ -79,7 +77,7 @@ phosg::JSON to_json(const parray<parray<IntT, Count2>, Count1>& v) {
template <typename IntT, size_t Count1, size_t Count2>
void from_json_into(const phosg::JSON& json, parray<parray<IntT, Count2>, Count1>& ret) {
if (json.size() != Count1) {
throw runtime_error("incorrect array length");
throw std::runtime_error("incorrect array length");
}
for (size_t z = 0; z < Count1; z++) {
from_json_into(json.at(z), ret[z]);
@@ -89,7 +87,7 @@ void from_json_into(const phosg::JSON& json, parray<parray<IntT, Count2>, Count1
template <typename IntT, size_t Count1, size_t Count2>
void from_json_into(const phosg::JSON& json, parray<parray<CommonItemSet::Table::Range<IntT>, Count2>, Count1>& ret) {
if (json.size() != Count1) {
throw runtime_error("incorrect array length");
throw std::runtime_error("incorrect array length");
}
for (size_t z = 0; z < Count1; z++) {
from_json_into(json.at(z), ret[z]);
@@ -139,7 +137,7 @@ CommonItemSet::Table::Table(std::shared_ptr<const Table> prev_table, const phosg
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
from_json_into(*dict.at(phosg::name_for_enum(enemy_type)), this->enemy_type_meseta_ranges[enemy_type]);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
} else {
@@ -151,7 +149,7 @@ CommonItemSet::Table::Table(std::shared_ptr<const Table> prev_table, const phosg
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
this->enemy_type_drop_probs[enemy_type] = dict.at(phosg::name_for_enum(enemy_type))->as_int();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
} else {
@@ -163,7 +161,7 @@ CommonItemSet::Table::Table(std::shared_ptr<const Table> prev_table, const phosg
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
this->enemy_type_item_classes[enemy_type] = dict.at(phosg::name_for_enum(enemy_type))->as_int();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
} else {
@@ -208,13 +206,13 @@ void CommonItemSet::Table::print(FILE* stream) const {
def.rt_index, phosg::name_for_enum(enemy_type),
meseta_range.min, meseta_range.max, drop_prob, item_class,
name_for_common_item_class(item_class));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
phosg::fwrite_fmt(stream, " {:02X}:{:<23} ----- ----- ---- --:-------\n",
def.rt_index, phosg::name_for_enum(enemy_type));
}
}
static const array<const char*, 12> base_weapon_type_names = {
static const std::array<const char*, 12> base_weapon_type_names = {
"SABER", "SWORD", "DAGGER", "PARTISAN", "SLICER", "HANDGUN", "RIFLE", "MECHGUN", "SHOT", "CANE", "ROD", "WAND"};
phosg::fwrite_fmt(stream, "Base weapon config:\n");
phosg::fwrite_fmt(stream, " TYPE PROB [SB AL] FLOORS\n");
@@ -295,7 +293,7 @@ void CommonItemSet::Table::print(FILE* stream) const {
fputc('\n', stream);
}
static const array<const char*, 19> technique_names = {
static const std::array<const char*, 19> technique_names = {
"FOIE ",
"GIFOIE ",
"RAFOIE ",
@@ -392,7 +390,7 @@ void CommonItemSet::Table::print_diff(FILE* stream, const Table& other) const {
}
auto format_enemy_range_table = [&](const std::unordered_map<EnemyType, Range<uint16_t>>& table) -> std::string {
string ret = "";
std::string ret = "";
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
const auto& range = table.at(enemy_type);
@@ -400,13 +398,13 @@ void CommonItemSet::Table::print_diff(FILE* stream, const Table& other) const {
ret += ",";
}
ret += std::format("{}=[{},{}]", phosg::name_for_enum(enemy_type), range.min, range.max);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
return ret;
};
auto format_enemy_u8_table = [&](const std::unordered_map<EnemyType, uint8_t>& table) -> std::string {
string ret = "";
std::string ret = "";
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
uint8_t value = table.at(enemy_type);
@@ -414,7 +412,7 @@ void CommonItemSet::Table::print_diff(FILE* stream, const Table& other) const {
ret += ",";
}
ret += std::format("{}={}", phosg::name_for_enum(enemy_type), value);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
return ret;
@@ -620,7 +618,7 @@ phosg::JSON CommonItemSet::json() const {
auto prev_table = this->get_prev_table(episode, mode, difficulty, section_id);
auto table = this->get_table(episode, mode, difficulty, section_id);
ret.emplace(json_key, table->json(prev_table));
} catch (const runtime_error&) {
} catch (const std::runtime_error&) {
}
}
}
@@ -642,7 +640,7 @@ void CommonItemSet::print(FILE* stream) const {
name_for_difficulty(difficulty),
name_for_section_id(section_id));
table->print(stream);
} catch (const runtime_error&) {
} catch (const std::runtime_error&) {
}
}
}
@@ -655,15 +653,15 @@ void CommonItemSet::print_diff(FILE* stream, const CommonItemSet& other) const {
for (const auto& mode : ALL_GAME_MODES_V4) {
for (const auto& difficulty : ALL_DIFFICULTIES_V234) {
for (uint8_t section_id = 0; section_id < 10; section_id++) {
shared_ptr<const Table> this_table;
shared_ptr<const Table> other_table;
std::shared_ptr<const Table> this_table;
std::shared_ptr<const Table> other_table;
try {
this_table = this->get_table(episode, mode, difficulty, section_id);
} catch (const runtime_error&) {
} catch (const std::runtime_error&) {
}
try {
other_table = other.get_table(episode, mode, difficulty, section_id);
} catch (const runtime_error&) {
} catch (const std::runtime_error&) {
}
if (!this_table && !other_table) {
@@ -789,17 +787,17 @@ std::string CommonItemSet::json_key_for_table(
token_name_for_difficulty(difficulty), name_for_section_id(section_id));
}
shared_ptr<const CommonItemSet::Table> CommonItemSet::get_table(
std::shared_ptr<const CommonItemSet::Table> CommonItemSet::get_table(
Episode episode, GameMode mode, Difficulty difficulty, uint8_t section_id) const {
try {
return this->tables.at(this->key_for_table(episode, mode, difficulty, section_id));
} catch (const out_of_range&) {
throw runtime_error(std::format("common item table not available for episode={}, mode={}, difficulty={}, secid={}",
} catch (const std::out_of_range&) {
throw std::runtime_error(std::format("common item table not available for episode={}, mode={}, difficulty={}, secid={}",
name_for_episode(episode), name_for_mode(mode), name_for_difficulty(difficulty), section_id));
}
}
shared_ptr<const CommonItemSet::Table> CommonItemSet::get_prev_table(
std::shared_ptr<const CommonItemSet::Table> CommonItemSet::get_prev_table(
Episode episode, GameMode mode, Difficulty difficulty, uint8_t section_id) const {
if (section_id != 0) {
// All section IDs are based on the previous, except Viridia
@@ -837,7 +835,7 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
for (size_t section_id = 0; section_id < 10; section_id++) {
auto entry = pt_afs.get(static_cast<size_t>(difficulty) * 10 + section_id);
phosg::StringReader r(entry.first, entry.second);
auto table = make_shared<Table>(r, false, false, Episode::EP1);
auto table = std::make_shared<Table>(r, false, false, Episode::EP1);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::NORMAL, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::BATTLE, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::SOLO, difficulty, section_id), table);
@@ -861,7 +859,7 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
continue;
}
auto r = ct_afs.get_reader(static_cast<size_t>(difficulty) * 10);
auto table = make_shared<Table>(r, false, false, Episode::EP1);
auto table = std::make_shared<Table>(r, false, false, Episode::EP1);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::CHALLENGE, difficulty, section_id), table);
}
@@ -872,7 +870,7 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian) {
GSLArchive gsl(gsl_data, is_big_endian);
auto filename_for_table = +[](Episode episode, Difficulty difficulty, uint8_t section_id, bool is_challenge) -> string {
auto filename_for_table = +[](Episode episode, Difficulty difficulty, uint8_t section_id, bool is_challenge) -> std::string {
const char* episode_token = "";
switch (episode) {
case Episode::EP1:
@@ -885,7 +883,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
episode_token = "s";
break;
default:
throw runtime_error("invalid episode");
throw std::runtime_error("invalid episode");
}
return std::format(
"ItemPT{}{}{}{}.rel",
@@ -901,7 +899,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
phosg::StringReader r;
try {
r = gsl.get_reader(filename_for_table(episode, difficulty, section_id, false));
} catch (const exception&) {
} catch (const std::exception&) {
// Fall back to Episode 1 if Episode 4 data is missing
if (episode == Episode::EP4) {
auto ep1_table = this->tables.at(this->key_for_table(Episode::EP1, GameMode::NORMAL, difficulty, section_id));
@@ -913,7 +911,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
throw;
}
}
auto table = make_shared<Table>(r, is_big_endian, true, episode);
auto table = std::make_shared<Table>(r, is_big_endian, true, episode);
this->tables.emplace(this->key_for_table(episode, GameMode::NORMAL, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(episode, GameMode::BATTLE, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(episode, GameMode::SOLO, difficulty, section_id), table);
@@ -923,11 +921,11 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
try {
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
auto table = make_shared<Table>(r, is_big_endian, true, episode);
auto table = std::make_shared<Table>(r, is_big_endian, true, episode);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(episode, GameMode::CHALLENGE, difficulty, section_id), table);
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
// GC NTE doesn't have Ep2 challenge; just skip adding the table
}
}
@@ -943,9 +941,9 @@ JSONCommonItemSet::JSONCommonItemSet(const phosg::JSON& json) {
auto prev_table = this->get_prev_table(episode, mode, difficulty, section_id);
auto json_key = this->json_key_for_table(episode, mode, difficulty, section_id);
auto key = this->key_for_table(episode, mode, difficulty, section_id);
this->tables.emplace(key, make_shared<Table>(prev_table, json.at(json_key), episode));
} catch (const runtime_error&) {
} catch (const out_of_range&) {
this->tables.emplace(key, std::make_shared<Table>(prev_table, json.at(json_key), episode));
} catch (const std::runtime_error&) {
} catch (const std::out_of_range&) {
}
}
}
@@ -985,21 +983,21 @@ ToolRandomSet::ToolRandomSet(std::shared_ptr<const std::string> data) : RELFileS
this->tech_disk_level_table_spec = &r.pget<TableSpec>(r.pget_u32b(specs_offset + 2 * sizeof(uint32_t)));
}
pair<const uint8_t*, size_t> ToolRandomSet::get_common_recovery_table(size_t index) const {
std::pair<const uint8_t*, size_t> ToolRandomSet::get_common_recovery_table(size_t index) const {
return this->get_table<uint8_t>(*this->common_recovery_table_spec, index);
}
pair<const ToolRandomSet::WeightTableEntry8*, size_t>
std::pair<const ToolRandomSet::WeightTableEntry8*, size_t>
ToolRandomSet::get_rare_recovery_table(size_t index) const {
return this->get_table<WeightTableEntry8>(*this->rare_recovery_table_spec, index);
}
pair<const ToolRandomSet::WeightTableEntry8*, size_t>
std::pair<const ToolRandomSet::WeightTableEntry8*, size_t>
ToolRandomSet::get_tech_disk_table(size_t index) const {
return this->get_table<WeightTableEntry8>(*this->tech_disk_table_spec, index);
}
pair<const ToolRandomSet::TechDiskLevelEntry*, size_t>
std::pair<const ToolRandomSet::TechDiskLevelEntry*, size_t>
ToolRandomSet::get_tech_disk_level_table(size_t index) const {
return this->get_table<TechDiskLevelEntry>(*this->tech_disk_level_table_spec, index);
}
@@ -1013,7 +1011,7 @@ std::pair<const WeaponRandomSet::WeightTableEntry8*, size_t>
WeaponRandomSet::get_weapon_type_table(size_t index) const {
const auto& spec = this->r.pget<TableSpec>(this->offsets->weapon_type_table + index * sizeof(TableSpec));
const auto* data = &this->r.pget<WeightTableEntry8>(spec.offset, spec.entries_per_table * sizeof(WeightTableEntry8));
return make_pair(data, spec.entries_per_table);
return std::make_pair(data, spec.entries_per_table);
}
const parray<WeaponRandomSet::WeightTableEntry32, 6>*
@@ -1055,7 +1053,7 @@ const ProbabilityTable<uint8_t, 100>& TekkerAdjustmentSet::get_table(
bool favored,
uint8_t section_id) const {
if (section_id >= 10) {
throw runtime_error("invalid section ID");
throw std::runtime_error("invalid section ID");
}
ProbabilityTable<uint8_t, 100>& table = favored ? tables_favored[section_id] : tables_default[section_id];
if (table.count == 0) {
+45 -48
View File
@@ -11,8 +11,6 @@
#include "Text.hh"
using namespace std;
template <>
const char* phosg::name_for_enum<CompressPhase>(CompressPhase v) {
switch (v) {
@@ -34,13 +32,13 @@ struct WindowIndex {
const uint8_t* data;
size_t size;
size_t offset;
set<size_t, function<bool(size_t, size_t)>> index;
std::set<size_t, std::function<bool(size_t, size_t)>> index;
WindowIndex(const void* data, size_t size)
: data(reinterpret_cast<const uint8_t*>(data)),
size(size),
offset(0),
index(bind(&WindowIndex::set_comparator, this, placeholders::_1, placeholders::_2)) {}
index(std::bind(&WindowIndex::set_comparator, this, std::placeholders::_1, std::placeholders::_2)) {}
void advance() {
if (this->offset >= WindowLength) {
@@ -69,7 +67,7 @@ struct WindowIndex {
// strings far too much. We can solve this by instead storing the offset of each string as keys in a set and using a
// custom comparator to treat them as references to binary strings within the data.
bool set_comparator(size_t a, size_t b) const {
size_t max_length = min<size_t>(MaxMatchLength, this->size - max<size_t>(a, b));
size_t max_length = std::min<size_t>(MaxMatchLength, this->size - std::max<size_t>(a, b));
size_t end_a = a + max_length;
for (; a < end_a; a++, b++) {
uint8_t data_a = static_cast<uint8_t>(this->data[a]);
@@ -83,7 +81,7 @@ struct WindowIndex {
return a < b; // Maximum-length match; order them by offset
};
pair<size_t, size_t> get_best_match() const {
std::pair<size_t, size_t> get_best_match() const {
// Find the best match from the index. It's unlikely that we'll get an exact match, so check the entry before the
// upper_bound result too. Note: We use upper_bound rather than lower_bound because in PRS, a backreference can be
// encoded with fewer bits if it's close to the decompression offset, and this makes us pick the latest match by
@@ -108,7 +106,7 @@ struct WindowIndex {
match_size = new_match_size;
}
}
return make_pair(match_offset, match_size);
return std::make_pair(match_offset, match_size);
}
};
@@ -140,7 +138,7 @@ struct LZSSInterleavedWriter {
void write_control(bool v) {
if (this->next_control_bit == 0) {
throw logic_error("write_control called with no space to write");
throw std::logic_error("write_control called with no space to write");
}
if (v) {
this->buf[0] |= this->next_control_bit;
@@ -208,15 +206,15 @@ struct PRSPathNode {
size_t to_offset = 0;
};
string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
std::string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
const uint8_t* in_data = reinterpret_cast<const uint8_t*>(in_data_v);
vector<PRSPathNode> nodes;
std::vector<PRSPathNode> nodes;
nodes.resize(in_size + 1);
nodes[0].bits_used = 18; // Stop command: 2 control bits and 2 data bytes
size_t copy_progress_max = 3 * in_size;
atomic<size_t> copy_progress = 0;
std::atomic<size_t> copy_progress = 0;
// Populate all possible short copies
std::thread short_window_thread([&]() -> void {
@@ -361,14 +359,14 @@ string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallb
switch (next_node.from_command_type) {
case PRSPathNode::CommandType::LITERAL:
if (copy_size != 1) {
throw logic_error("incorrect size for LITERAL copy type");
throw std::logic_error("incorrect size for LITERAL copy type");
}
w.write_control(true);
w.write_data(in_data[offset]);
break;
case PRSPathNode::CommandType::SHORT_COPY: {
if (copy_size < 2 || copy_size > 5) {
throw logic_error("incorrect size for SHORT_COPY copy type");
throw std::logic_error("incorrect size for SHORT_COPY copy type");
}
uint8_t encoded_size = copy_size - 2;
w.write_control(false);
@@ -383,7 +381,7 @@ string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallb
}
case PRSPathNode::CommandType::LONG_COPY: {
if (copy_size < 2 || copy_size > 9) {
throw logic_error("incorrect size for LONG_COPY copy type");
throw std::logic_error("incorrect size for LONG_COPY copy type");
}
w.write_control(false);
w.flush_if_ready();
@@ -395,7 +393,7 @@ string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallb
}
case PRSPathNode::CommandType::EXTENDED_COPY: {
if (copy_size < 1 || copy_size > 0x100) {
throw logic_error("incorrect size for EXTENDED_COPY copy type");
throw std::logic_error("incorrect size for EXTENDED_COPY copy type");
}
w.write_control(false);
w.flush_if_ready();
@@ -407,7 +405,7 @@ string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallb
break;
}
default:
throw logic_error("invalid copy type in shortest path");
throw std::logic_error("invalid copy type in shortest path");
}
w.flush_if_ready();
@@ -424,11 +422,11 @@ string prs_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallb
return std::move(w.close());
}
string prs_compress_optimal(const string& data, ProgressCallback progress_fn) {
std::string prs_compress_optimal(const std::string& data, ProgressCallback progress_fn) {
return prs_compress_optimal(data.data(), data.size(), progress_fn);
}
string prs_compress_pessimal(const void* vdata, size_t size) {
std::string prs_compress_pessimal(const void* vdata, size_t size) {
const uint8_t* in_data = reinterpret_cast<const uint8_t*>(vdata);
// The worst possible encoding we can do is a literal byte when no byte with the same value is within the window, or
@@ -479,7 +477,7 @@ PRSCompressor::PRSCompressor(
void PRSCompressor::add(const void* data, size_t size) {
if (this->closed) {
throw logic_error("compressor is closed");
throw std::logic_error("compressor is closed");
}
phosg::StringReader r(data, size);
@@ -488,7 +486,7 @@ void PRSCompressor::add(const void* data, size_t size) {
}
}
void PRSCompressor::add(const string& data) {
void PRSCompressor::add(const std::string& data) {
this->add(data.data(), data.size());
}
@@ -573,7 +571,7 @@ void PRSCompressor::advance() {
this->advance_extended_copy(backreference_offset, best_match_size);
} else {
throw logic_error("invalid best match");
throw std::logic_error("invalid best match");
}
}
@@ -621,7 +619,7 @@ void PRSCompressor::advance_extended_copy(ssize_t offset, size_t size) {
this->move_forward_data_to_reverse_log(size);
}
string& PRSCompressor::close() {
std::string& PRSCompressor::close() {
if (!this->closed) {
// Advance until all input is consumed
while (this->reverse_log.end_offset() < this->input_bytes) {
@@ -658,23 +656,23 @@ void PRSCompressor::flush_control() {
this->output.pput_u8(this->control_byte_offset, this->pending_control_bits & 0xFF);
} else {
if (this->control_byte_offset != this->output.size() - 1) {
throw logic_error("data written without control bits");
throw std::logic_error("data written without control bits");
}
this->output.str().resize(this->output.str().size() - 1);
}
}
string prs_compress(const void* vdata, size_t size, ssize_t compression_level, ProgressCallback progress_fn) {
std::string prs_compress(const void* vdata, size_t size, ssize_t compression_level, ProgressCallback progress_fn) {
PRSCompressor prs(compression_level, progress_fn);
prs.add(vdata, size);
return std::move(prs.close());
}
string prs_compress(const string& data, ssize_t compression_level, ProgressCallback progress_fn) {
std::string prs_compress(const std::string& data, ssize_t compression_level, ProgressCallback progress_fn) {
return prs_compress(data.data(), data.size(), compression_level, progress_fn);
}
string prs_compress_indexed(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
std::string prs_compress_indexed(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
const uint8_t* in_data = reinterpret_cast<const uint8_t*>(in_data_v);
LZSSInterleavedWriter w;
@@ -779,12 +777,12 @@ string prs_compress_indexed(const void* in_data_v, size_t in_size, ProgressCallb
}
case PRSPathNode::CommandType::NONE:
default:
throw logic_error("invalid command type");
throw std::logic_error("invalid command type");
}
w.flush_if_ready();
if (bytes_consumed == 0) {
throw logic_error("no input data was consumed");
throw std::logic_error("no input data was consumed");
}
for (size_t z = 0; z < bytes_consumed; z++) {
@@ -804,7 +802,7 @@ string prs_compress_indexed(const void* in_data_v, size_t in_size, ProgressCallb
return std::move(w.close());
}
string prs_compress_indexed(const string& data, ProgressCallback progress_fn) {
std::string prs_compress_indexed(const std::string& data, ProgressCallback progress_fn) {
return prs_compress_indexed(data.data(), data.size(), progress_fn);
}
@@ -846,7 +844,7 @@ PRSDecompressResult prs_decompress_with_meta(
if (allow_unterminated) {
return {std::move(w.str()), r.where()};
} else {
throw runtime_error("maximum output size exceeded");
throw std::runtime_error("maximum output size exceeded");
}
}
w.put_u8(r.get_u8());
@@ -882,14 +880,14 @@ PRSDecompressResult prs_decompress_with_meta(
// support ranges that cover the current end of the output.
size_t read_offset = w.size() + offset;
if (read_offset >= w.size()) {
throw runtime_error("backreference offset beyond beginning of output");
throw std::runtime_error("backreference offset beyond beginning of output");
}
for (size_t z = 0; z < count; z++) {
if (max_output_size && w.size() == max_output_size) {
if (allow_unterminated) {
return {std::move(w.str()), r.where()};
} else {
throw out_of_range("maximum output size exceeded");
throw std::out_of_range("maximum output size exceeded");
}
}
w.put_u8(w.str()[read_offset + z]);
@@ -900,16 +898,16 @@ PRSDecompressResult prs_decompress_with_meta(
return {std::move(w.str()), r.where()};
}
PRSDecompressResult prs_decompress_with_meta(const string& data, size_t max_output_size, bool allow_unterminated) {
PRSDecompressResult prs_decompress_with_meta(const std::string& data, size_t max_output_size, bool allow_unterminated) {
return prs_decompress_with_meta(data.data(), data.size(), max_output_size, allow_unterminated);
}
string prs_decompress(const void* data, size_t size, size_t max_output_size, bool allow_unterminated) {
std::string prs_decompress(const void* data, size_t size, size_t max_output_size, bool allow_unterminated) {
auto ret = prs_decompress_with_meta(data, size, max_output_size, allow_unterminated);
return std::move(ret.data);
}
string prs_decompress(const string& data, size_t max_output_size, bool allow_unterminated) {
std::string prs_decompress(const std::string& data, size_t max_output_size, bool allow_unterminated) {
auto ret = prs_decompress_with_meta(data.data(), data.size(), max_output_size, allow_unterminated);
return std::move(ret.data);
}
@@ -945,7 +943,7 @@ size_t prs_decompress_size(const void* data, size_t size, size_t max_output_size
size_t read_offset = ret + offset;
if (read_offset >= ret) {
throw runtime_error("backreference offset beyond beginning of output");
throw std::runtime_error("backreference offset beyond beginning of output");
}
ret += count;
}
@@ -954,7 +952,7 @@ size_t prs_decompress_size(const void* data, size_t size, size_t max_output_size
if (allow_unterminated) {
return max_output_size;
} else {
throw out_of_range("maximum output size exceeded");
throw std::out_of_range("maximum output size exceeded");
}
}
}
@@ -962,7 +960,7 @@ size_t prs_decompress_size(const void* data, size_t size, size_t max_output_size
return ret;
}
size_t prs_decompress_size(const string& data, size_t max_output_size, bool allow_unterminated) {
size_t prs_decompress_size(const std::string& data, size_t max_output_size, bool allow_unterminated) {
return prs_decompress_size(data.data(), data.size(), max_output_size, allow_unterminated);
}
@@ -1015,7 +1013,7 @@ void prs_disassemble(FILE* stream, const void* data, size_t size) {
}
if (read_offset >= output_bytes) {
throw runtime_error("backreference offset beyond beginning of output");
throw std::runtime_error("backreference offset beyond beginning of output");
}
output_bytes += count;
}
@@ -1043,11 +1041,10 @@ struct BC0PathNode {
size_t to_offset = 0;
};
string bc0_compress_optimal(
const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
std::string bc0_compress_optimal(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
const uint8_t* in_data = reinterpret_cast<const uint8_t*>(in_data_v);
vector<BC0PathNode> nodes;
std::vector<BC0PathNode> nodes;
nodes.resize(in_size + 1);
nodes[0].bits_used = 0;
@@ -1140,11 +1137,11 @@ string bc0_compress_optimal(
return std::move(w.close());
}
string bc0_compress(const string& data, ProgressCallback progress_fn) {
std::string bc0_compress(const std::string& data, ProgressCallback progress_fn) {
return bc0_compress(data.data(), data.size(), progress_fn);
}
string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
std::string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback progress_fn) {
const uint8_t* in_data = reinterpret_cast<const uint8_t*>(in_data_v);
LZSSInterleavedWriter w;
@@ -1180,7 +1177,7 @@ string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback prog
return std::move(w.close());
}
string bc0_encode(const void* in_data_v, size_t in_size) {
std::string bc0_encode(const void* in_data_v, size_t in_size) {
const uint8_t* in_data = reinterpret_cast<const uint8_t*>(in_data_v);
LZSSInterleavedWriter w;
@@ -1197,11 +1194,11 @@ string bc0_encode(const void* in_data_v, size_t in_size) {
// the output buffer. It is unlikely that this can be usefully exploited (e.g. for RCE) because the output pointer is
// loaded from memory before every byte is written, so we cannot change the output pointer to any arbitrary address.
string bc0_decompress(const string& data) {
std::string bc0_decompress(const std::string& data) {
return bc0_decompress(data.data(), data.size());
}
string bc0_decompress(const void* data, size_t size) {
std::string bc0_decompress(const void* data, size_t size) {
phosg::StringReader r(data, size);
phosg::StringWriter w;
@@ -1265,7 +1262,7 @@ string bc0_decompress(const void* data, size_t size) {
return std::move(w.str());
}
void bc0_disassemble(FILE* stream, const string& data) {
void bc0_disassemble(FILE* stream, const std::string& data) {
bc0_disassemble(stream, data.data(), data.size());
}
+27 -28
View File
@@ -9,8 +9,6 @@
#include "PSOEncryption.hh"
using namespace std;
static const uint32_t primes1[] = {
0x65, 0x67, 0x6B, 0x6D, 0x71, 0x7F, 0x83, 0x89, 0x8B, 0x95, 0x97, 0x9D, 0xA3, 0xA7, 0xAD, 0xB3, 0xB5, 0xBF, 0xC1,
0xC5, 0xC7, 0xD3, 0xDF, 0xE3, 0xE5, 0xE9, 0xEF, 0xF1, 0xFB, 0x101, 0x107, 0x10D, 0x10F, 0x115, 0x119, 0x11B, 0x125,
@@ -735,10 +733,10 @@ static const uint32_t primes3[] = {
static constexpr size_t num_primes3 = sizeof(primes3) / sizeof(primes3[0]);
static bool check_prime3(uint64_t prime3) {
static vector<bool> primes3_set;
static mutex primes3_init_mutex;
static std::vector<bool> primes3_set;
static std::mutex primes3_init_mutex;
if (primes3_set.empty()) {
lock_guard g(primes3_init_mutex);
std::lock_guard g(primes3_init_mutex);
if (primes3_set.empty()) {
size_t primes3_set_size = primes3[num_primes3 - 1] - primes3[0] + 1;
primes3_set.resize(primes3_set_size, false);
@@ -790,7 +788,7 @@ static char replace_char_reverse(char ch) {
static constexpr uint64_t INVALID_PRODUCT = 0xFFFFFFFFFFFFFFFF;
static uint64_t decode_dc_serial_number_str(const string& s) {
static uint64_t decode_dc_serial_number_str(const std::string& s) {
if (s.size() != 8) {
return INVALID_PRODUCT;
}
@@ -828,20 +826,20 @@ static uint32_t encode_dc_serial_number_int(uint32_t v) {
(replace_nybble_reverse(v));
}
static pair<size_t, size_t> compute_offset1_and_limit1(uint8_t domain, uint8_t subdomain) {
static std::pair<size_t, size_t> compute_offset1_and_limit1(uint8_t domain, uint8_t subdomain) {
if (domain > 2) {
return make_pair(0, 0);
return std::make_pair(0, 0);
}
size_t domain_base = domain * 30;
if (subdomain != 0xFF) {
size_t subdomain_base = domain_base + (subdomain % 3);
return make_pair(subdomain_base, subdomain_base + 1);
return std::make_pair(subdomain_base, subdomain_base + 1);
} else {
return make_pair(domain_base, domain_base + 3);
return std::make_pair(domain_base, domain_base + 3);
}
}
bool dc_serial_number_is_valid_slow(const string& s, uint8_t domain, uint8_t subdomain) {
bool dc_serial_number_is_valid_slow(const std::string& s, uint8_t domain, uint8_t subdomain) {
uint64_t serial_number = decode_dc_serial_number_str(s);
if (serial_number == INVALID_PRODUCT) {
return false;
@@ -890,7 +888,7 @@ bool decoded_dc_serial_number_is_valid_fast(uint32_t serial_number, uint8_t doma
return false;
}
bool dc_serial_number_is_valid_fast(const string& s, uint8_t domain, uint8_t subdomain) {
bool dc_serial_number_is_valid_fast(const std::string& s, uint8_t domain, uint8_t subdomain) {
uint64_t serial_number = decode_dc_serial_number_str(s);
if (serial_number == INVALID_PRODUCT) {
return false;
@@ -902,7 +900,7 @@ bool dc_serial_number_is_valid_fast(uint32_t serial_number, uint8_t domain, uint
return decoded_dc_serial_number_is_valid_fast(decode_dc_serial_number_int(serial_number), domain, subdomain);
}
string generate_dc_serial_number(uint8_t domain, uint8_t subdomain) {
std::string generate_dc_serial_number(uint8_t domain, uint8_t subdomain) {
size_t offset1, limit1;
if (domain == 0) {
offset1 = 0x00;
@@ -914,7 +912,7 @@ string generate_dc_serial_number(uint8_t domain, uint8_t subdomain) {
offset1 = 0x3C;
limit1 = 0x3F;
} else {
throw runtime_error("invalid domain");
throw std::runtime_error("invalid domain");
}
size_t det1 = (subdomain == 0xFF) ? phosg::random_object<uint32_t>() : subdomain;
@@ -922,23 +920,23 @@ string generate_dc_serial_number(uint8_t domain, uint8_t subdomain) {
size_t index2 = phosg::random_object<uint32_t>() % num_primes2;
size_t index3 = phosg::random_object<uint32_t>() % num_primes3;
uint32_t value = primes1[index1] * primes2[index2] * primes3[index3];
string s = std::format("{:08X}", value);
std::string s = std::format("{:08X}", value);
string ret;
std::string ret;
for (char ch : s) {
ret.push_back(replace_char_reverse(ch));
}
return ret;
}
unordered_map<uint32_t, string> generate_all_dc_serial_numbers(uint8_t domain, uint8_t subdomain) {
std::unordered_map<uint32_t, std::string> generate_all_dc_serial_numbers(uint8_t domain, uint8_t subdomain) {
DCSerialNumberIterator iter;
if (domain < 3) {
iter.domain = domain;
iter.end_domain = domain + 1;
} else if (domain != 0xFF) {
throw runtime_error("invalid domain");
throw std::runtime_error("invalid domain");
}
if (subdomain < 3) {
@@ -946,11 +944,11 @@ unordered_map<uint32_t, string> generate_all_dc_serial_numbers(uint8_t domain, u
iter.start_subdomain = subdomain;
iter.end_subdomain = subdomain + 1;
} else if (subdomain != 0xFF) {
throw runtime_error("invalid subdomain");
throw std::runtime_error("invalid subdomain");
}
uint32_t serial_number;
unordered_map<uint32_t, string> ret;
std::unordered_map<uint32_t, std::string> ret;
while ((serial_number = iter.next()) != 0) {
ret[serial_number].push_back(((iter.domain << 2) & 3) | (iter.subdomain & 3));
if (iter.index3 == 0) {
@@ -1015,7 +1013,7 @@ void dc_serial_number_speed_test(uint64_t seed) {
size_t num_disagreements = 0;
static constexpr size_t count = 0x1000;
for (size_t z = 0; z < count; z++) {
string s = std::format("{:08X}", crypt.next());
std::string s = std::format("{:08X}", crypt.next());
uint64_t start = phosg::now();
bool is_valid_fast = dc_serial_number_is_valid_fast(s, 1, 0xFF);
@@ -1041,7 +1039,8 @@ void dc_serial_number_speed_test(uint64_t seed) {
phosg::fwrite_fmt(stderr, "Disagreements: {}\n", num_disagreements);
}
string decrypt_dp_address_jpn(const string& executable, const string& values, const string& indexes) {
std::string decrypt_dp_address_jpn(
const std::string& executable, const std::string& values, const std::string& indexes) {
phosg::StringReader values_r(values);
phosg::StringReader indexes_r(indexes);
@@ -1056,7 +1055,7 @@ string decrypt_dp_address_jpn(const string& executable, const string& values, co
fixup_offset += (fixup_steps_r.get_u8() << 2);
fixup_steps_r.skip(1);
if (fixup_offset + 4 > decrypted.compressed_data.size()) {
throw runtime_error("fixup beyond end of compressed data");
throw std::runtime_error("fixup beyond end of compressed data");
}
*reinterpret_cast<le_uint32_t*>(decrypted.compressed_data.data() + fixup_offset) = fixup_values_r.get_u32l();
}
@@ -1064,10 +1063,10 @@ string decrypt_dp_address_jpn(const string& executable, const string& values, co
return prs_decompress(decrypted.compressed_data);
}
EncryptedDCv2Executables encrypt_dp_address_jpn(const string& executable, const string& indexes) {
EncryptedDCv2Executables encrypt_dp_address_jpn(const std::string& executable, const std::string& indexes) {
EncryptedDCv2Executables ret;
string compressed = prs_compress(executable);
std::string compressed = prs_compress(executable);
ret.executable = encrypt_pr2_data<false>(compressed, executable.size(), phosg::random_object<uint32_t>() & 0x7FFFFF7F);
phosg::StringReader indexes_r(indexes);
@@ -1079,12 +1078,12 @@ EncryptedDCv2Executables encrypt_dp_address_jpn(const string& executable, const
std::string crypt_dp_address_jpn_simple(const std::string& data, int64_t mask_key) {
if (data.size() & 3) {
throw runtime_error("size is not a multiple of 4");
throw std::runtime_error("size is not a multiple of 4");
}
phosg::StringReader r(data);
if (mask_key < 0) {
unordered_map<uint32_t, size_t> key_freq;
std::unordered_map<uint32_t, size_t> key_freq;
while (!r.eof()) {
key_freq[r.get_u32l()] += 1;
}
@@ -1096,7 +1095,7 @@ std::string crypt_dp_address_jpn_simple(const std::string& data, int64_t mask_ke
}
}
if (mask_key < 0) {
throw runtime_error("cannot determine mask key");
throw std::runtime_error("cannot determine mask key");
}
phosg::log_info_f("Determined {:08X} to be the most likely mask key", mask_key);
r.go(0);
+8 -10
View File
@@ -14,9 +14,7 @@
#include "NetworkAddresses.hh"
#include "ServerState.hh"
using namespace std;
DNSServer::DNSServer(shared_ptr<ServerState> state)
DNSServer::DNSServer(std::shared_ptr<ServerState> state)
: state(state) {}
void DNSServer::listen(const std::string& addr, int port) {
@@ -25,15 +23,15 @@ void DNSServer::listen(const std::string& addr, int port) {
}
asio::ip::address asio_addr = addr.empty() ? asio::ip::address_v4::any() : asio::ip::make_address(addr);
asio::ip::udp::endpoint endpoint(asio_addr, port);
auto sock = make_shared<asio::ip::udp::socket>(*this->state->io_context, endpoint);
auto sock = std::make_shared<asio::ip::udp::socket>(*this->state->io_context, endpoint);
this->sockets.emplace(sock);
asio::co_spawn(*this->state->io_context, this->dns_server_task(sock), asio::detached);
}
string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t resolved_address) {
std::string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t resolved_address) {
if (size < 0x0C) {
throw invalid_argument("query too small");
throw std::invalid_argument("query too small");
}
const char* data = reinterpret_cast<const char*>(vdata);
@@ -41,7 +39,7 @@ string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t re
phosg::be_uint32_t be_resolved_address = resolved_address;
string response;
std::string response;
response.append(data, 2);
response.append("\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00", 10);
response.append(&data[12], name_len);
@@ -50,13 +48,13 @@ string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t re
return response;
}
string DNSServer::response_for_query(const string& query, uint32_t resolved_address) {
std::string DNSServer::response_for_query(const std::string& query, uint32_t resolved_address) {
return DNSServer::response_for_query(query.data(), query.size(), resolved_address);
}
asio::awaitable<void> DNSServer::dns_server_task(std::shared_ptr<asio::ip::udp::socket> sock) {
for (;;) {
string input(2048, 0);
std::string input(2048, 0);
asio::ip::udp::endpoint sender_ep;
size_t bytes = co_await sock->async_receive_from(asio::buffer(input), sender_ep, asio::use_awaitable);
uint32_t sender_addr = ipv4_addr_for_asio_addr(sender_ep.address());
@@ -69,7 +67,7 @@ asio::awaitable<void> DNSServer::dns_server_task(std::shared_ptr<asio::ip::udp::
uint32_t connect_address = is_local_address(sender_addr)
? this->state->local_address
: this->state->external_address;
string response = this->response_for_query(input, connect_address);
std::string response = this->response_for_query(input, connect_address);
co_await sock->async_send_to(asio::buffer(response.data(), response.size()), sender_ep, asio::use_awaitable);
}
}
+11 -13
View File
@@ -18,15 +18,13 @@
#include "Compression.hh"
#include "Loggers.hh"
using namespace std;
DOLFileIndex::DOLFileIndex(const string& directory) {
DOLFileIndex::DOLFileIndex(const std::string& directory) {
if (!std::filesystem::is_directory(directory)) {
client_functions_log.info_f("DOL file directory is missing");
return;
}
auto menu = make_shared<Menu>(MenuID::PROGRAMS, "Programs");
auto menu = std::make_shared<Menu>(MenuID::PROGRAMS, "Programs");
this->menu = menu;
menu->items.emplace_back(ProgramsMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0);
@@ -43,17 +41,17 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
if (!is_dol && !is_compressed_dol) {
continue;
}
string name = filename.substr(0, filename.size() - (is_compressed_dol ? 8 : 4));
std::string name = filename.substr(0, filename.size() - (is_compressed_dol ? 8 : 4));
try {
auto dol = make_shared<File>();
auto dol = std::make_shared<File>();
dol->menu_item_id = next_menu_item_id++;
dol->name = name;
string path = directory + "/" + filename;
string file_data = phosg::load_file(path);
std::string path = directory + "/" + filename;
std::string file_data = phosg::load_file(path);
string description;
std::string description;
if (is_compressed_dol) {
size_t decompressed_size = prs_decompress_size(file_data);
@@ -66,8 +64,8 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
}
dol->data = std::move(w.str());
string compressed_size_str = phosg::format_size(file_data.size());
string decompressed_size_str = phosg::format_size(decompressed_size);
std::string compressed_size_str = phosg::format_size(file_data.size());
std::string decompressed_size_str = phosg::format_size(decompressed_size);
client_functions_log.debug_f("Loaded compressed DOL file {} ({} -> {})",
dol->name, compressed_size_str, decompressed_size_str);
description = std::format("$C6{}$C7\n{}\n{} (orig)", dol->name, compressed_size_str, decompressed_size_str);
@@ -82,7 +80,7 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
}
dol->data = std::move(w.str());
string size_str = phosg::format_size(dol->data.size());
std::string size_str = phosg::format_size(dol->data.size());
client_functions_log.debug_f("Loaded DOL file {} ({})", filename, size_str);
description = std::format("$C6{}$C7\n{}", dol->name, size_str);
}
@@ -90,7 +88,7 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
this->item_id_to_file.emplace_back(dol);
menu->items.emplace_back(dol->menu_item_id, dol->name, description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
} catch (const exception& e) {
} catch (const std::exception& e) {
client_functions_log.warning_f("Failed to load DOL file {}: {}", filename, e.what());
}
}
+42 -44
View File
@@ -22,12 +22,10 @@
#include "ReceiveSubcommands.hh"
#include "SendCommands.hh"
using namespace std;
static string random_name() {
string ret;
static std::string random_name() {
std::string ret;
size_t length = (phosg::random_object<size_t>() % 12) + 4;
static const string alphabet = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890-+<>:\"\',.";
static const std::string alphabet = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890-+<>:\"\',.";
while (ret.size() < length) {
ret.push_back(alphabet[phosg::random_object<size_t>() % alphabet.size()]);
}
@@ -87,40 +85,40 @@ DownloadSession::DownloadSession(
case Version::DC_V1:
case Version::DC_V2:
if (this->serial_number2 == 0 || this->serial_number == 0 || this->access_key.empty()) {
throw runtime_error("missing credentials");
throw std::runtime_error("missing credentials");
}
break;
case Version::PC_V2:
if (this->serial_number == 0 || this->access_key.empty()) {
throw runtime_error("missing credentials");
throw std::runtime_error("missing credentials");
}
break;
case Version::GC_V3:
if (this->serial_number == 0 || this->access_key.empty() || this->password.empty()) {
throw runtime_error("missing credentials");
throw std::runtime_error("missing credentials");
}
break;
case Version::XB_V3:
if (this->xb_gamertag.empty() || this->xb_user_id == 0 || this->xb_account_id == 0) {
throw runtime_error("missing credentials");
throw std::runtime_error("missing credentials");
}
break;
case Version::BB_V4:
if (this->username.empty() || this->password.empty()) {
throw runtime_error("missing credentials");
throw std::runtime_error("missing credentials");
}
break;
default:
throw runtime_error("unsupported version");
throw std::runtime_error("unsupported version");
}
this->character->inventory.language = language;
}
asio::awaitable<void> DownloadSession::run() {
string netloc_str = std::format("{}:{}", this->remote_host, this->remote_port);
std::string netloc_str = std::format("{}:{}", this->remote_host, this->remote_port);
this->log.info_f("Connecting to {}", netloc_str);
auto sock = make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(this->remote_host, this->remote_port));
auto sock = std::make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(this->remote_host, this->remote_port));
this->channel = SocketChannel::create(
this->io_context,
std::move(sock),
@@ -214,7 +212,7 @@ void DownloadSession::send_93_9D_9E(bool extended) {
this->channel->send(0x9E, 0x01, &ret, extended ? sizeof(ret) : sizeof(C_Login_DC_PC_GC_9D));
} else {
throw runtime_error("unsupported version");
throw std::runtime_error("unsupported version");
}
}
@@ -266,7 +264,7 @@ void DownloadSession::send_61_98(bool is_98) {
this->channel->send(command, 0x04, ret);
} else {
throw runtime_error("unsupported version");
throw std::runtime_error("unsupported version");
}
}
@@ -278,16 +276,16 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
switch (msg.command) {
case 0x03: {
if (this->version != Version::BB_V4) {
throw runtime_error("BB server sent non-BB encryption command");
throw std::runtime_error("BB server sent non-BB encryption command");
}
if (!this->bb_key_file) {
throw runtime_error("BB encryption requires a key file");
throw std::runtime_error("BB encryption requires a key file");
}
const auto& cmd = msg.check_size_t<S_ServerInitDefault_BB_03_9B>(0xFFFF);
this->channel->crypt_in = make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key));
this->channel->crypt_out = make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key));
this->channel->crypt_in = std::make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key));
this->channel->crypt_out = std::make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key));
this->log.info_f("Enabled BB encryption");
throw runtime_error("not yet implemented"); // Send 93
throw std::runtime_error("not yet implemented"); // Send 93
break;
}
@@ -297,17 +295,17 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
case 0x9B: {
const auto& cmd = msg.check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(0xFFFF);
if (uses_v3_encryption(this->version)) {
this->channel->crypt_in = make_shared<PSOV3Encryption>(cmd.server_key);
this->channel->crypt_out = make_shared<PSOV3Encryption>(cmd.client_key);
this->channel->crypt_in = std::make_shared<PSOV3Encryption>(cmd.server_key);
this->channel->crypt_out = std::make_shared<PSOV3Encryption>(cmd.client_key);
this->log.info_f("Enabled V3 encryption (server key {:08X}, client key {:08X})",
cmd.server_key, cmd.client_key);
} else if (!uses_v4_encryption(this->version)) {
this->channel->crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
this->channel->crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
this->channel->crypt_in = std::make_shared<PSOV2Encryption>(cmd.server_key);
this->channel->crypt_out = std::make_shared<PSOV2Encryption>(cmd.client_key);
this->log.info_f("Enabled V2 encryption (server key {:08X}, client key {:08X})",
cmd.server_key, cmd.client_key);
} else {
throw runtime_error("BB server sent non-BB encryption command");
throw std::runtime_error("BB server sent non-BB encryption command");
}
if (msg.command == 0x02) {
@@ -346,7 +344,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
this->send_93_9D_9E(true);
} else {
throw runtime_error("unsupported version");
throw std::runtime_error("unsupported version");
}
}
@@ -381,14 +379,14 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
this->channel->send(0x9C, 0x00, ret);
} else {
throw runtime_error("unsupported version");
throw std::runtime_error("unsupported version");
}
} else if (msg.flag == 0 || msg.flag == 2) {
this->send_93_9D_9E(true);
} else {
throw runtime_error("login failed");
throw std::runtime_error("login failed");
}
break;
}
@@ -396,14 +394,14 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
case 0x92:
case 0x9C:
if (msg.flag == 0) {
throw runtime_error("server rejected login credentials");
throw std::runtime_error("server rejected login credentials");
}
this->send_93_9D_9E(true);
break;
case 0x9F: {
if (is_v1_or_v2(this->version)) {
throw runtime_error("invalid command");
throw std::runtime_error("invalid command");
}
this->channel->send(0x9F, 0x00, this->client_config);
break;
@@ -477,11 +475,11 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
if (this->interactive) {
while (item_index == 0 || item_index > msg.flag) {
this->log.info_f("Choose response index:");
string input = phosg::fgets(stdin);
item_index = stoul(input, nullptr, 0);
std::string input = phosg::fgets(stdin);
item_index = std::stoul(input, nullptr, 0);
}
} else {
throw runtime_error("unhandled menu selection");
throw std::runtime_error("unhandled menu selection");
}
}
ret.menu_id = items[item_index].menu_id;
@@ -521,9 +519,9 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
const auto& cmd = msg.check_size_t<S_Reconnect_19>(sizeof(S_Reconnect_19), 0xFFFF);
auto new_ep = make_endpoint_ipv4(cmd.address, cmd.port);
string netloc_str = str_for_endpoint(new_ep);
std::string netloc_str = str_for_endpoint(new_ep);
this->log.info_f("Connecting to {}", netloc_str);
auto sock = make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(new_ep));
auto sock = std::make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(new_ep));
this->channel = SocketChannel::create(
this->io_context,
std::move(sock),
@@ -649,12 +647,12 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
case 0xA6: {
auto handle_command = [&]<typename CmdT>() {
const auto& cmd = msg.check_size_t<CmdT>(0xFFFF);
string internal_name = cmd.filename.decode();
string filtered_name;
std::string internal_name = cmd.filename.decode();
std::string filtered_name;
for (char ch : internal_name) {
filtered_name.push_back((isalnum(ch) || (ch == '-') || (ch == '.') || (ch == '_')) ? ch : '_');
}
string local_filename = std::format(
std::string local_filename = std::format(
"{}/{:016X}_{}_{}_{:c}_{}",
this->output_dir,
this->current_request,
@@ -677,7 +675,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
case 0x13:
case 0xA7: {
const auto& cmd = msg.check_size_t<S_WriteFile_13_A7>();
string internal_filename = cmd.filename.decode();
std::string internal_filename = cmd.filename.decode();
if (!is_v1_or_v2(this->version)) {
C_WriteFileConfirmation_V3_BB_13_A7 ret;
@@ -692,8 +690,8 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
}
auto& f = this->open_files.at(cmd.filename.decode());
size_t block_offset = msg.flag * 0x400;
size_t allowed_block_size = (block_offset < f.total_size) ? min<size_t>(f.total_size - block_offset, 0x400) : 0;
size_t data_size = min<size_t>(cmd.data_size, allowed_block_size);
size_t allowed_block_size = (block_offset < f.total_size) ? std::min<size_t>(f.total_size - block_offset, 0x400) : 0;
size_t data_size = std::min<size_t>(cmd.data_size, allowed_block_size);
size_t block_end_offset = block_offset + data_size;
if (block_end_offset > f.data.size()) {
f.data.resize(block_end_offset);
@@ -720,7 +718,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
}
case 0xAC: {
if (is_v1_or_v2(this->version)) {
throw runtime_error("unsupported version");
throw std::runtime_error("unsupported version");
}
this->on_request_complete();
break;
@@ -745,7 +743,7 @@ void DownloadSession::send_next_request() {
this->log.info_f("{:016X}: {}", it.first, it.second);
}
this->log.info_f("Choose item to expand by ID (q to quit; s to skip to next config):");
string input = phosg::fgets(stdin);
std::string input = phosg::fgets(stdin);
if (input.empty() || (input == "q\n")) {
this->channel->disconnect();
return;
@@ -753,7 +751,7 @@ void DownloadSession::send_next_request() {
this->pending_requests.clear();
this->on_request_complete();
} else {
this->current_request = stoull(input, nullptr, 16);
this->current_request = std::stoull(input, nullptr, 16);
this->done_requests.emplace(this->current_request);
this->pending_requests.erase(this->current_request);
}
+12 -14
View File
@@ -8,8 +8,6 @@
#include "PSOEncryption.hh"
#include "StaticGameData.hh"
using namespace std;
static constexpr uint8_t EP1 = EnemyTypeDefinition::Flag::VALID_EP1;
static constexpr uint8_t EP2 = EnemyTypeDefinition::Flag::VALID_EP2;
static constexpr uint8_t EP4 = EnemyTypeDefinition::Flag::VALID_EP4;
@@ -17,7 +15,7 @@ static constexpr uint8_t RARE = EnemyTypeDefinition::Flag::IS_RARE;
static constexpr uint8_t BOSS = EnemyTypeDefinition::Flag::IS_BOSS;
static constexpr uint8_t NONE = 0xFF;
static const vector<EnemyTypeDefinition> type_defs{
static const std::vector<EnemyTypeDefinition> type_defs{
// clang-format off
// TYPE FLAGS RT OLDRT BP-STATS BP-ATTACK BP-RESIST BP-MOVEMENT ENUM NAME IN-GAME NAME ULTIMATE NAME
{EnemyType::UNKNOWN, 0, NONE, NONE, {}, {}, {}, {}, "UNKNOWN", "__UNKNOWN__", nullptr},
@@ -167,19 +165,19 @@ const char* phosg::name_for_enum<EnemyType>(EnemyType type) {
template <>
EnemyType phosg::enum_for_name<EnemyType>(const char* name) {
static unordered_map<string, EnemyType> index;
static std::unordered_map<std::string, EnemyType> index;
if (index.empty()) {
for (const auto& def : type_defs) {
if (!index.emplace(def.enum_name, def.type).second) {
throw logic_error(std::format("duplicate enemy enum name: {}", def.enum_name));
throw std::logic_error(std::format("duplicate enemy enum name: {}", def.enum_name));
}
}
}
return index.at(name);
}
const vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8_t rt_index) {
static array<vector<vector<EnemyType>>, 5> data;
const std::vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8_t rt_index) {
static std::array<std::vector<std::vector<EnemyType>>, 5> data;
auto& ret = data.at(static_cast<size_t>(episode));
if (ret.empty()) {
for (const auto& def : type_defs) {
@@ -196,8 +194,8 @@ const vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8
}
try {
return ret.at(rt_index);
} catch (const out_of_range&) {
static const vector<EnemyType> empty_vec;
} catch (const std::out_of_range&) {
static const std::vector<EnemyType> empty_vec;
return empty_vec;
}
}
@@ -211,7 +209,7 @@ struct BPIndexCacheEntry {
static const BPIndexCacheEntry& get_bp_index_cache_entry(Episode episode, uint8_t bp_index) {
static bool cache_populated = false;
static array<vector<BPIndexCacheEntry>, 5> data;
static std::array<std::vector<BPIndexCacheEntry>, 5> data;
if (!cache_populated) {
cache_populated = true;
for (const auto& def : type_defs) {
@@ -256,7 +254,7 @@ static const std::set<EnemyType> empty_vec;
const std::set<EnemyType>& enemy_types_for_battle_param_stats_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).stats;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return empty_vec;
}
}
@@ -264,7 +262,7 @@ const std::set<EnemyType>& enemy_types_for_battle_param_stats_index(Episode epis
const std::set<EnemyType>& enemy_types_for_battle_param_attack_data_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).attack_data;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return empty_vec;
}
}
@@ -272,7 +270,7 @@ const std::set<EnemyType>& enemy_types_for_battle_param_attack_data_index(Episod
const std::set<EnemyType>& enemy_types_for_battle_param_resist_data_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).resist_data;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return empty_vec;
}
}
@@ -280,7 +278,7 @@ const std::set<EnemyType>& enemy_types_for_battle_param_resist_data_index(Episod
const std::set<EnemyType>& enemy_types_for_battle_param_movement_data_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).movement_data;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return empty_vec;
}
}
+13 -15
View File
@@ -2,22 +2,20 @@
#include "Server.hh"
using namespace std;
namespace Episode3 {
const vector<uint16_t>& all_assist_card_ids(bool is_nte) {
const std::vector<uint16_t>& all_assist_card_ids(bool is_nte) {
// Note: This order matches the order that the cards are defined in the original
// code. This is relevant for consistency of results when choosing a random card
// (for God Whim).
static const vector<uint16_t> ALL_ASSIST_CARD_IDS_TRIAL = {
static const std::vector<uint16_t> ALL_ASSIST_CARD_IDS_TRIAL = {
0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, 0x0100, 0x0101, 0x0102,
0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, 0x0121,
0x0125, 0x0126, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, 0x0130, 0x0131, 0x0132,
0x0133, 0x0134, 0x0135, 0x0136, 0x0137, 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, 0x0140,
0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0148, 0x014A, 0x014B, 0x014C, 0x014D, 0x014E, 0x023F, 0x0240,
0x0241, 0x0242};
static const vector<uint16_t> ALL_ASSIST_CARD_IDS_FINAL = {
static const std::vector<uint16_t> ALL_ASSIST_CARD_IDS_FINAL = {
0x0018, 0x0019, 0x001A, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D,
0x010E, 0x010F, 0x0121, 0x0125, 0x0126, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F,
@@ -28,12 +26,12 @@ const vector<uint16_t>& all_assist_card_ids(bool is_nte) {
}
AssistEffect assist_effect_number_for_card_id(uint16_t card_id, bool is_nte) {
static const unordered_map<uint16_t, AssistEffect> card_id_to_effect_final_only({
static const std::unordered_map<uint16_t, AssistEffect> card_id_to_effect_final_only({
{0x0018, /* 0x0049 */ AssistEffect::DICE_FEVER_PLUS},
{0x0019, /* 0x004A */ AssistEffect::RICH_PLUS},
{0x001A, /* 0x004B */ AssistEffect::CHARITY_PLUS},
});
static const unordered_map<uint16_t, AssistEffect> card_id_to_effect({
static const std::unordered_map<uint16_t, AssistEffect> card_id_to_effect({
{0x00F5, /* 0x0001 */ AssistEffect::DICE_HALF},
{0x00F6, /* 0x0002 */ AssistEffect::DICE_PLUS_1},
{0x00F7, /* 0x0003 */ AssistEffect::DICE_FEVER},
@@ -109,18 +107,18 @@ AssistEffect assist_effect_number_for_card_id(uint16_t card_id, bool is_nte) {
});
try {
return card_id_to_effect.at(card_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (!is_nte) {
try {
return card_id_to_effect_final_only.at(card_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
return AssistEffect::NONE;
}
AssistServer::AssistServer(shared_ptr<Server> server)
AssistServer::AssistServer(std::shared_ptr<Server> server)
: w_server(server),
assist_effects(AssistEffect::NONE),
num_assist_cards_set(0),
@@ -128,18 +126,18 @@ AssistServer::AssistServer(shared_ptr<Server> server)
active_assist_effects(AssistEffect::NONE),
num_active_assists(0) {}
shared_ptr<Server> AssistServer::server() {
std::shared_ptr<Server> AssistServer::server() {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
shared_ptr<const Server> AssistServer::server() const {
std::shared_ptr<const Server> AssistServer::server() const {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
@@ -148,7 +146,7 @@ uint16_t AssistServer::card_id_for_card_ref(uint16_t card_ref) const {
return this->server()->card_id_for_card_ref(card_ref);
}
shared_ptr<const CardIndex::CardEntry> AssistServer::definition_for_card_id(
std::shared_ptr<const CardIndex::CardEntry> AssistServer::definition_for_card_id(
uint16_t card_id) const {
return this->server()->definition_for_card_id(card_id);
}
+31 -39
View File
@@ -5,8 +5,6 @@
#include "../CommandFormats.hh"
#include "../SendCommands.hh"
using namespace std;
namespace Episode3 {
void BattleRecord::PlayerEntry::print(FILE* stream) const {
@@ -41,7 +39,7 @@ BattleRecord::Event::Event(phosg::StringReader& r) {
this->data = r.read(r.get_u16l());
break;
default:
throw logic_error("unknown event type");
throw std::logic_error("unknown event type");
}
}
@@ -51,7 +49,7 @@ void BattleRecord::Event::serialize(phosg::StringWriter& w) const {
switch (this->type) {
case Event::Type::PLAYER_JOIN:
if (this->players.size() != 1) {
throw logic_error("player join event does not contain 1 player entry");
throw std::logic_error("player join event does not contain 1 player entry");
}
w.put(this->players[0]);
break;
@@ -75,13 +73,12 @@ void BattleRecord::Event::serialize(phosg::StringWriter& w) const {
w.write(this->data);
break;
default:
throw logic_error("unknown event type");
throw std::logic_error("unknown event type");
}
}
void BattleRecord::Event::print(FILE* stream) const {
string time_str = phosg::format_time(this->timestamp);
phosg::fwrite_fmt(stream, "Event @{:016X} ({}) ", this->timestamp, time_str);
phosg::fwrite_fmt(stream, "Event @{:016X} ({}) ", this->timestamp, phosg::format_time(this->timestamp));
switch (this->type) {
case Type::PLAYER_JOIN:
phosg::fwrite_fmt(stream, "PLAYER_JOIN {:02X}\n", this->players[0].lobby_data.client_id);
@@ -121,7 +118,7 @@ void BattleRecord::Event::print(FILE* stream) const {
phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
break;
default:
throw runtime_error("unknown event type in battle record");
throw std::runtime_error("unknown event type in battle record");
}
}
@@ -131,11 +128,8 @@ BattleRecord::BattleRecord(uint32_t behavior_flags)
battle_start_timestamp(0),
battle_end_timestamp(0) {}
BattleRecord::BattleRecord(const string& data)
: is_writable(false),
behavior_flags(0),
battle_start_timestamp(0),
battle_end_timestamp(0) {
BattleRecord::BattleRecord(const std::string& data)
: is_writable(false), behavior_flags(0), battle_start_timestamp(0), battle_end_timestamp(0) {
phosg::StringReader r(data);
uint64_t signature = r.get_u64l();
@@ -145,7 +139,7 @@ BattleRecord::BattleRecord(const string& data)
} else if (signature == this->SIGNATURE_V2) {
has_random_stream = true;
} else {
throw runtime_error("incorrect battle record signature");
throw std::runtime_error("incorrect battle record signature");
}
this->battle_start_timestamp = r.get_u64l();
@@ -159,7 +153,7 @@ BattleRecord::BattleRecord(const string& data)
}
}
string BattleRecord::serialize() const {
std::string BattleRecord::serialize() const {
phosg::StringWriter w;
w.put_u64l(this->SIGNATURE_V2);
w.put_u64l(this->battle_start_timestamp);
@@ -179,10 +173,10 @@ void BattleRecord::add_player(
const PlayerDispDataDCPCV3& disp,
uint32_t level) {
if (!this->is_writable) {
throw logic_error("cannot write to battle record");
throw std::logic_error("cannot write to battle record");
}
if (this->battle_start_timestamp != 0) {
throw runtime_error("cannot add player during battle");
throw std::runtime_error("cannot add player during battle");
}
Event& ev = this->events.emplace_back();
ev.type = Event::Type::PLAYER_JOIN;
@@ -196,7 +190,7 @@ void BattleRecord::add_player(
void BattleRecord::delete_player(uint8_t client_id) {
if (!this->is_writable) {
throw logic_error("cannot write to battle record");
throw std::logic_error("cannot write to battle record");
}
Event& ev = this->events.emplace_back();
ev.type = Event::Type::PLAYER_LEAVE;
@@ -206,7 +200,7 @@ void BattleRecord::delete_player(uint8_t client_id) {
void BattleRecord::add_command(Event::Type type, const void* data, size_t size) {
if (!this->is_writable) {
throw logic_error("cannot write to battle record");
throw std::logic_error("cannot write to battle record");
}
Event& ev = this->events.emplace_back();
ev.type = type;
@@ -214,9 +208,9 @@ void BattleRecord::add_command(Event::Type type, const void* data, size_t size)
ev.data.assign(reinterpret_cast<const char*>(data), size);
}
void BattleRecord::add_command(Event::Type type, string&& data) {
void BattleRecord::add_command(Event::Type type, std::string&& data) {
if (!this->is_writable) {
throw logic_error("cannot write to battle record");
throw std::logic_error("cannot write to battle record");
}
Event& ev = this->events.emplace_back();
ev.type = type;
@@ -224,10 +218,9 @@ void BattleRecord::add_command(Event::Type type, string&& data) {
ev.data = std::move(data);
}
void BattleRecord::add_chat_message(
uint32_t guild_card_number, string&& data) {
void BattleRecord::add_chat_message(uint32_t guild_card_number, std::string&& data) {
if (!this->is_writable) {
throw logic_error("cannot write to battle record");
throw std::logic_error("cannot write to battle record");
}
Event& ev = this->events.emplace_back();
ev.type = Event::Type::CHAT_MESSAGE;
@@ -240,7 +233,7 @@ void BattleRecord::add_random_data(const void* data, size_t size) {
this->random_stream.append(reinterpret_cast<const char*>(data), size);
}
const string& BattleRecord::get_random_stream() const {
const std::string& BattleRecord::get_random_stream() const {
return this->random_stream;
}
@@ -259,7 +252,7 @@ bool BattleRecord::is_map_definition_event(const Event& ev) {
void BattleRecord::set_battle_start_timestamp() {
if (this->battle_start_timestamp != 0) {
throw logic_error("battle start timestamp is already set");
throw std::logic_error("battle start timestamp is already set");
}
this->battle_start_timestamp = phosg::now();
@@ -271,30 +264,30 @@ void BattleRecord::set_battle_start_timestamp() {
for (auto& ev : this->events) {
if (ev.type == Event::Type::PLAYER_JOIN) {
if (ev.players.size() != 1) {
throw logic_error("player join event does not contain 1 player entry");
throw std::logic_error("player join event does not contain 1 player entry");
}
auto& player = ev.players[0];
if (player.lobby_data.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
players[player.lobby_data.client_id] = player;
players_present[player.lobby_data.client_id] = true;
} else if (ev.type == Event::Type::PLAYER_LEAVE) {
if (ev.leaving_client_id >= 4) {
throw logic_error("invalid client ID");
throw std::logic_error("invalid client ID");
}
players_present[ev.leaving_client_id] = false;
} else if (ev.type == Event::Type::SET_INITIAL_PLAYERS) {
throw logic_error("BattleRecord::set_battle_start_timestamp called twice");
throw std::logic_error("BattleRecord::set_battle_start_timestamp called twice");
} else if (this->is_map_definition_event(ev)) {
num_map_events++;
}
}
deque<Event> new_events;
std::deque<Event> new_events;
// Generate the initial players event
Event initial_ev;
@@ -335,32 +328,31 @@ void BattleRecord::set_battle_end_timestamp() {
}
void BattleRecord::print(FILE* stream) const {
string start_str = phosg::format_time(this->battle_start_timestamp);
string end_str = phosg::format_time(this->battle_end_timestamp);
phosg::fwrite_fmt(stream, "BattleRecord {} behavior_flags={:08X} start={:016X} ({}) end={:016X} ({}); {} events\n",
this->is_writable ? "writable" : "read-only",
this->behavior_flags,
this->battle_start_timestamp,
start_str,
phosg::format_time(this->battle_start_timestamp),
this->battle_end_timestamp,
end_str, this->events.size());
phosg::format_time(this->battle_end_timestamp),
this->events.size());
for (const auto& event : this->events) {
event.print(stream);
}
}
BattleRecordPlayer::BattleRecordPlayer(std::shared_ptr<asio::io_context> io_context, shared_ptr<const BattleRecord> rec)
BattleRecordPlayer::BattleRecordPlayer(std::shared_ptr<asio::io_context> io_context, std::shared_ptr<const BattleRecord> rec)
: io_context(io_context),
record(rec),
event_it(this->record->events.begin()),
play_start_timestamp(0),
next_command_timer(*this->io_context) {}
shared_ptr<const BattleRecord> BattleRecordPlayer::get_record() const {
std::shared_ptr<const BattleRecord> BattleRecordPlayer::get_record() const {
return this->record;
}
void BattleRecordPlayer::set_lobby(shared_ptr<Lobby> l) {
void BattleRecordPlayer::set_lobby(std::shared_ptr<Lobby> l) {
this->lobby = l;
}
@@ -406,7 +398,7 @@ asio::awaitable<void> BattleRecordPlayer::play_task() {
switch (ev.type) {
case BattleRecord::Event::Type::PLAYER_JOIN:
// Technically we can support this, but it should never happen
throw runtime_error("player join event during battle replay");
throw std::runtime_error("player join event during battle replay");
case BattleRecord::Event::Type::PLAYER_LEAVE:
send_player_leave_notification(l, ev.leaving_client_id);
break;
+52 -59
View File
@@ -3,11 +3,9 @@
#include "../CommandFormats.hh"
#include "Server.hh"
using namespace std;
namespace Episode3 {
Card::Card(uint16_t card_id, uint16_t card_ref, uint16_t client_id, shared_ptr<Server> server)
Card::Card(uint16_t card_id, uint16_t card_ref, uint16_t client_id, std::shared_ptr<Server> server)
: w_server(server),
w_player_state(server->get_player_state(client_id)),
client_id(client_id),
@@ -39,7 +37,7 @@ void Card::init() {
// Arkz-side. This could break things later on in the battle, and even if it
// doesn't, it certainly isn't behavior that the player would expect, so we
// prevent it instead.
throw runtime_error("card definition is missing");
throw std::runtime_error("card definition is missing");
}
this->sc_card_ref = ps->get_sc_card_ref();
this->sc_def_entry = s->definition_for_card_id(ps->get_sc_card_id());
@@ -56,7 +54,7 @@ void Card::init() {
} else {
int16_t rules_char_hp = s->map_and_rules->rules.char_hp;
int16_t base_char_hp = (rules_char_hp == 0) ? 15 : rules_char_hp;
int16_t hp = clamp<int16_t>(base_char_hp + this->def_entry->def.hp.stat, 1, 99);
int16_t hp = std::clamp<int16_t>(base_char_hp + this->def_entry->def.hp.stat, 1, 99);
this->max_hp = hp;
this->current_hp = hp;
}
@@ -78,34 +76,34 @@ void Card::init() {
}
}
shared_ptr<Server> Card::server() {
std::shared_ptr<Server> Card::server() {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
shared_ptr<const Server> Card::server() const {
std::shared_ptr<const Server> Card::server() const {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
shared_ptr<PlayerState> Card::player_state() {
std::shared_ptr<PlayerState> Card::player_state() {
auto s = this->w_player_state.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
shared_ptr<const PlayerState> Card::player_state() const {
std::shared_ptr<const PlayerState> Card::player_state() const {
auto s = this->w_player_state.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
@@ -158,7 +156,7 @@ ssize_t Card::apply_abnormal_condition(
int16_t existing_cond_value = 0;
auto& cond = this->action_chain.conditions[cond_index];
if ((eff.type == ConditionType::MV_BONUS) && (cond.type == ConditionType::MV_BONUS)) {
existing_cond_value = clamp<int16_t>(cond.value, -99, 99);
existing_cond_value = std::clamp<int16_t>(cond.value, -99, 99);
log.debug_f("MV_BONUS combines => existing_cond_value = {}", existing_cond_value);
}
@@ -176,7 +174,7 @@ ssize_t Card::apply_abnormal_condition(
switch (eff.arg1.at(0)) {
case 'a': {
string s = eff.arg1.decode();
std::string s = eff.arg1.decode();
cond.a_arg_value = atoi(s.c_str() + 1);
break;
}
@@ -190,13 +188,12 @@ ssize_t Card::apply_abnormal_condition(
cond.remaining_turns = 102;
break;
case 't': {
string s = eff.arg1.decode();
std::string s = eff.arg1.decode();
cond.remaining_turns = atoi(s.c_str() + 1);
}
}
string cond_str = cond.str(s);
log.debug_f("wrote condition {} => {}", cond_index, cond_str);
log.debug_f("wrote condition {} => {}", cond_index, cond.str(s));
if (!is_nte) {
s->card_special->update_condition_orders(this->shared_from_this());
@@ -204,8 +201,7 @@ ssize_t Card::apply_abnormal_condition(
if (this->action_chain.conditions[z].type == ConditionType::NONE) {
continue;
}
string cond_str = cond.str(s);
log.debug_f("sorted conditions: [{}] => {}", z, cond_str);
log.debug_f("sorted conditions: [{}] => {}", z, cond.str(s));
}
}
@@ -213,7 +209,7 @@ ssize_t Card::apply_abnormal_condition(
}
void Card::apply_ap_and_tp_adjust_assists_to_attack(
shared_ptr<const Card> attacker_card,
std::shared_ptr<const Card> attacker_card,
int16_t* inout_attacker_ap,
int16_t* in_defense_power,
int16_t* inout_attacker_tp) const {
@@ -226,12 +222,12 @@ void Card::apply_ap_and_tp_adjust_assists_to_attack(
switch (s->assist_server->get_active_assist_by_index(z)) {
case AssistEffect::POWERLESS_RAIN:
if (is_nte) {
*inout_attacker_ap = max<int16_t>(*inout_attacker_ap - 2, 0);
*inout_attacker_ap = std::max<int16_t>(*inout_attacker_ap - 2, 0);
}
break;
case AssistEffect::BRAVE_WIND:
if (is_nte) {
*inout_attacker_ap = max<int16_t>(*inout_attacker_ap + 2, 0);
*inout_attacker_ap = std::max<int16_t>(*inout_attacker_ap + 2, 0);
}
break;
case AssistEffect::SILENT_COLOSSEUM:
@@ -284,7 +280,7 @@ bool Card::check_card_flag(uint32_t flags) const {
void Card::commit_attack(
int16_t damage,
shared_ptr<Card> attacker_card,
std::shared_ptr<Card> attacker_card,
G_ApplyConditionEffect_Ep3_6xB4x06* cmd,
size_t strike_number,
int16_t* out_effective_damage) {
@@ -302,7 +298,7 @@ void Card::commit_attack(
auto eff = s->assist_server->get_active_assist_by_index(z);
if ((eff == AssistEffect::RANSOM) && (attacker_card->action_chain.chain.attack_medium == AttackMedium::PHYSICAL)) {
uint8_t team_id = this->player_state()->get_team_id();
int16_t exp_amount = clamp<int16_t>(s->team_exp[team_id], 0, effective_damage);
int16_t exp_amount = std::clamp<int16_t>(s->team_exp[team_id], 0, effective_damage);
s->team_exp[team_id] -= exp_amount;
effective_damage -= exp_amount;
if (!is_nte) {
@@ -326,7 +322,7 @@ void Card::commit_attack(
this->player_state()->stats.damage_taken += effective_damage;
log.debug_f("updated stats");
this->current_hp = clamp<int16_t>(this->current_hp - effective_damage, 0, this->max_hp);
this->current_hp = std::clamp<int16_t>(this->current_hp - effective_damage, 0, this->max_hp);
log.debug_f("hp set to {}", this->current_hp);
if ((effective_damage > 0) && (attacker_ps->stats.max_attack_damage < effective_damage)) {
@@ -366,7 +362,7 @@ void Card::commit_attack(
}
}
int16_t Card::compute_defense_power_for_attacker_card(shared_ptr<const Card> attacker_card) {
int16_t Card::compute_defense_power_for_attacker_card(std::shared_ptr<const Card> attacker_card) {
if (!attacker_card) {
return 0;
}
@@ -392,7 +388,7 @@ int16_t Card::compute_defense_power_for_attacker_card(shared_ptr<const Card> att
return this->action_metadata.defense_power + this->action_metadata.defense_bonus;
}
void Card::destroy_set_card(shared_ptr<Card> attacker_card) {
void Card::destroy_set_card(std::shared_ptr<Card> attacker_card) {
auto s = this->server();
auto ps = this->player_state();
@@ -490,7 +486,7 @@ int32_t Card::error_code_for_move_to_location(const Location& loc) const {
return 0;
}
void Card::execute_attack(shared_ptr<Card> attacker_card) {
void Card::execute_attack(std::shared_ptr<Card> attacker_card) {
if (!attacker_card) {
return;
}
@@ -518,7 +514,7 @@ void Card::execute_attack(shared_ptr<Card> attacker_card) {
if (attacker_card->action_chain.chain.attack_medium == AttackMedium::UNKNOWN_03) {
// Probably Resta
for (size_t strike_num = 0; strike_num < attacker_card->action_chain.chain.strike_count; strike_num++) {
this->current_hp = min<int16_t>(this->current_hp + attacker_card->action_chain.chain.effective_tp, this->max_hp);
this->current_hp = std::min<int16_t>(this->current_hp + attacker_card->action_chain.chain.effective_tp, this->max_hp);
}
this->propagate_shared_hp_if_needed();
cmd.effect.tp = attacker_card->action_chain.chain.effective_tp;
@@ -545,7 +541,7 @@ void Card::execute_attack(shared_ptr<Card> attacker_card) {
log.debug_f("assist adjusts ap={}, defense={}", attack_ap, defense_power);
int16_t raw_damage = attack_ap - defense_power;
int16_t preliminary_damage = max<int16_t>(raw_damage, 0) - attack_tp;
int16_t preliminary_damage = std::max<int16_t>(raw_damage, 0) - attack_tp;
this->last_attack_preliminary_damage = preliminary_damage;
log.debug_f("raw_damage={}, preliminary_damange={}", raw_damage, preliminary_damage);
@@ -571,8 +567,8 @@ void Card::execute_attack(shared_ptr<Card> attacker_card) {
}
}
cmd.effect.current_hp = is_nte ? attack_ap : min<int16_t>(attack_ap, 99);
cmd.effect.ap = is_nte ? defense_power : min<int16_t>(defense_power, 99);
cmd.effect.current_hp = is_nte ? attack_ap : std::min<int16_t>(attack_ap, 99);
cmd.effect.ap = is_nte ? defense_power : std::min<int16_t>(defense_power, 99);
cmd.effect.tp = attack_tp;
auto ps = this->player_state();
@@ -583,7 +579,7 @@ void Card::execute_attack(shared_ptr<Card> attacker_card) {
for (size_t strike_num = 0; strike_num < attacker_card->action_chain.chain.strike_count; strike_num++) {
int16_t final_effective_damage = 0;
target->commit_attack(preliminary_damage, attacker_card, &cmd, strike_num, &final_effective_damage);
ps->stats.action_card_negated_damage += max<int16_t>(0, this->current_defense_power - final_effective_damage);
ps->stats.action_card_negated_damage += std::max<int16_t>(0, this->current_defense_power - final_effective_damage);
}
} else {
log.debug_f("flag 2 set; committing zero-damage attack");
@@ -618,7 +614,7 @@ const Condition* Card::find_condition(ConditionType cond_type) const {
return const_cast<Card*>(this)->find_condition(cond_type);
}
shared_ptr<const CardIndex::CardEntry> Card::get_definition() const {
std::shared_ptr<const CardIndex::CardEntry> Card::get_definition() const {
return this->def_entry;
}
@@ -839,10 +835,10 @@ void Card::set_current_and_max_hp(int16_t hp) {
void Card::set_current_hp(
uint32_t new_hp, bool propagate_shared_hp, bool enforce_max_hp) {
if (!enforce_max_hp) {
new_hp = max<int16_t>(new_hp, 0);
this->max_hp = max<int16_t>(this->max_hp, new_hp);
new_hp = std::max<int16_t>(new_hp, 0);
this->max_hp = std::max<int16_t>(this->max_hp, new_hp);
} else {
new_hp = clamp<int16_t>(new_hp, 0, this->max_hp);
new_hp = std::clamp<int16_t>(new_hp, 0, this->max_hp);
}
this->current_hp = new_hp;
@@ -955,11 +951,11 @@ void Card::compute_action_chain_results(bool apply_action_conditions, bool ignor
}
if (!this->action_chain.check_flag(0x10)) {
this->action_chain.chain.effective_ap = is_nte ? effective_ap : min<int16_t>(effective_ap, 99);
this->action_chain.chain.effective_ap = is_nte ? effective_ap : std::min<int16_t>(effective_ap, 99);
log.debug_f("set chain effective_ap = {}", this->action_chain.chain.effective_ap);
}
if (!this->action_chain.check_flag(0x20)) {
this->action_chain.chain.effective_tp = is_nte ? effective_tp : min<int16_t>(effective_tp, 99);
this->action_chain.chain.effective_tp = is_nte ? effective_tp : std::min<int16_t>(effective_tp, 99);
log.debug_f("set chain effective_tp = {}", this->action_chain.chain.effective_tp);
}
@@ -1099,7 +1095,7 @@ void Card::compute_action_chain_results(bool apply_action_conditions, bool ignor
this->action_chain.chain.damage = is_nte
? (damage * this->action_chain.chain.damage_multiplier)
: min<int16_t>(damage * this->action_chain.chain.damage_multiplier, 99);
: std::min<int16_t>(damage * this->action_chain.chain.damage_multiplier, 99);
log.debug_f("overall chain damage = {} (base) * {} (mult) = {}",
damage, this->action_chain.chain.damage_multiplier, this->action_chain.chain.damage);
@@ -1108,7 +1104,7 @@ void Card::compute_action_chain_results(bool apply_action_conditions, bool ignor
s->card_special->apply_action_conditions(EffectWhen::BEFORE_ANY_CARD_ATTACK, this_sh, this_sh, 2, nullptr);
log.debug_f("applied action conditions (2)");
if (!is_nte && this->action_chain.check_flag(0x100)) {
this->action_chain.chain.damage = min<int16_t>(this->action_chain.chain.damage + 5, 99);
this->action_chain.chain.damage = std::min<int16_t>(this->action_chain.chain.damage + 5, 99);
log.debug_f("(has flag 0x100) chain damage = {}", this->action_chain.chain.damage);
}
} else {
@@ -1141,8 +1137,7 @@ void Card::compute_action_chain_results(bool apply_action_conditions, bool ignor
}
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string chain_str = this->action_chain.str(s);
log.debug_f("result computed as {}", chain_str);
log.debug_f("result computed as {}", this->action_chain.str(s));
}
}
@@ -1208,20 +1203,19 @@ void Card::move_phase_before() {
this->server()->card_special->move_phase_before_for_card(this->shared_from_this());
}
void Card::unknown_80236374(shared_ptr<Card> other_card, const ActionState* as) {
void Card::unknown_80236374(std::shared_ptr<Card> other_card, const ActionState* as) {
auto s = this->server();
auto log = s->log_stack(std::format("unknown_80236374(@{:04X} #{:04X}, @{:04X} #{:04X}): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id()));
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
if (as) {
string as_str = as->str(s);
log.debug_f("as = {}", as_str);
log.debug_f("as = {}", as->str(s));
} else {
log.debug_f("as = null");
}
}
auto check_card = [&](shared_ptr<Card> card) -> void {
auto check_card = [&](std::shared_ptr<Card> card) -> void {
if (card) {
if (!card->unknown_80236554(other_card, as)) {
log.debug_f("check_card @{:04X} #{:04X} => false", card->get_card_ref(), card->get_card_id());
@@ -1263,7 +1257,7 @@ void Card::unknown_802379DC(const ActionState& pa) {
pa.defense_card_ref, this->shared_from_this(), pa.original_attacker_card_ref);
this->server()->card_special->unknown_8024A9D8(pa, 0xFFFF);
for (size_t z = 0; z < this->action_metadata.target_card_ref_count; z++) {
shared_ptr<Card> card = this->server()->card_for_set_card_ref(this->action_metadata.target_card_refs[z]);
std::shared_ptr<Card> card = this->server()->card_for_set_card_ref(this->action_metadata.target_card_refs[z]);
if (card) {
card->action_chain.set_action_subphase_from_card(this->shared_from_this());
card->send_6xB4x4E_4C_4D_if_needed();
@@ -1302,7 +1296,7 @@ void Card::unknown_80237A90(const ActionState& pa, uint16_t action_card_ref) {
if (this->action_chain.chain.target_card_ref_count == 0) {
for (size_t z = 0; (z < 4 * 9) && (pa.target_card_refs[z] != 0xFFFF); z++) {
this->action_chain.add_target_card_ref(pa.target_card_refs[z]);
shared_ptr<Card> sc_card = s->card_for_set_card_ref(pa.target_card_refs[z]);
std::shared_ptr<Card> sc_card = s->card_for_set_card_ref(pa.target_card_refs[z]);
if (sc_card) {
sc_card->action_metadata.add_target_card_ref(this->card_ref);
sc_card->card_flags |= 8;
@@ -1357,7 +1351,7 @@ bool Card::is_guard_item() const {
return (this->def_entry->def.card_class() == CardClass::GUARD_ITEM);
}
bool Card::unknown_80236554(shared_ptr<Card> other_card, const ActionState* as) {
bool Card::unknown_80236554(std::shared_ptr<Card> other_card, const ActionState* as) {
auto s = this->server();
auto log = s->log_stack(other_card
? std::format("unknown_80236554(@{:04X} #{:04X}, @{:04X} #{:04X}): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id())
@@ -1395,7 +1389,7 @@ bool Card::unknown_80236554(shared_ptr<Card> other_card, const ActionState* as)
}
}
this->action_metadata.attack_bonus = max<int16_t>(attack_bonus, 0);
this->action_metadata.attack_bonus = std::max<int16_t>(attack_bonus, 0);
log.debug_f("attack_bonus = {}", this->action_metadata.attack_bonus);
this->last_attack_preliminary_damage = 0;
this->last_attack_final_damage = 0;
@@ -1421,17 +1415,17 @@ bool Card::unknown_80236554(shared_ptr<Card> other_card, const ActionState* as)
return ret;
}
void Card::execute_attack_on_all_valid_targets(shared_ptr<Card> attacker_card) {
void Card::execute_attack_on_all_valid_targets(std::shared_ptr<Card> attacker_card) {
auto s = this->server();
for (size_t client_id = 0; client_id < 4; client_id++) {
auto ps = s->player_states[client_id];
if (ps) {
shared_ptr<Card> card = ps->get_sc_card();
std::shared_ptr<Card> card = ps->get_sc_card();
if (card) {
card->execute_attack(attacker_card);
}
for (size_t set_index = 0; set_index < 8; set_index++) {
shared_ptr<Card> card = ps->get_set_card(set_index);
std::shared_ptr<Card> card = ps->get_set_card(set_index);
if (card) {
card->execute_attack(attacker_card);
}
@@ -1476,7 +1470,7 @@ void Card::apply_attack_result() {
temp_chain.chain.target_card_ref_count++;
} else if ((target_card->get_definition()->def.type == CardType::ITEM) && !this->action_chain.check_flag(0x02)) {
auto target_ps = target_card->player_state();
shared_ptr<Card> candidate_card;
std::shared_ptr<Card> candidate_card;
for (size_t set_index = 0; set_index < 8; set_index++) {
auto set_card = target_ps->get_set_card(set_index);
if (set_card && (set_card != target_card) && !(set_card->card_flags & 2) && set_card->is_guard_item()) {
@@ -1555,12 +1549,11 @@ void Card::apply_attack_result() {
}
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string as_str = as.str(s);
log.debug_f("as constructed as {}", as_str);
log.debug_f("as constructed as {}", as.str(s));
}
for (size_t z = 0; z < this->action_chain.chain.target_card_ref_count; z++) {
shared_ptr<Card> card = s->card_for_set_card_ref(this->action_chain.chain.target_card_refs[z]);
std::shared_ptr<Card> card = s->card_for_set_card_ref(this->action_chain.chain.target_card_refs[z]);
if (card) {
card->current_defense_power = card->action_metadata.attack_bonus;
if (!this->action_chain.check_flag(0x40)) {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -8
View File
@@ -2,8 +2,6 @@
#include "Server.hh"
using namespace std;
namespace Episode3 {
NameEntry::NameEntry() {
@@ -198,7 +196,7 @@ void DeckState::redraw_initial_hand(bool is_nte) {
auto s = this->server.lock();
if (!s) {
throw runtime_error("server is missing");
throw std::runtime_error("server is missing");
}
// Shuffle the deck, except the first 5 cards (which are about to be drawn).
@@ -260,7 +258,7 @@ void DeckState::shuffle() {
if (this->shuffle_enabled) {
auto s = this->server.lock();
if (!s) {
throw runtime_error("server is missing");
throw std::runtime_error("server is missing");
}
size_t max = this->num_drawable_cards();
@@ -305,17 +303,17 @@ void DeckState::print(FILE* stream, std::shared_ptr<const CardIndex> card_index)
this->loop_enabled ? "true" : "false");
for (size_t z = 0; z < 31; z++) {
const auto& e = this->entries[z];
shared_ptr<const CardIndex::CardEntry> ce;
std::shared_ptr<const CardIndex::CardEntry> ce;
if (card_index) {
try {
ce = card_index->definition_for_id(e.card_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
if (ce) {
string name = ce->def.en_name.decode(Language::ENGLISH);
phosg::fwrite_fmt(stream, " ({:02}) index={:02X} ref=@{:04X} card_id=#{:04X} \"{}\" {}\n",
z, e.deck_index, this->card_refs[z], e.card_id, name, name_for_card_state(e.state));
z, e.deck_index, this->card_refs[z], e.card_id, ce->def.en_name.decode(Language::ENGLISH),
name_for_card_state(e.state));
} else {
phosg::fwrite_fmt(stream, " ({:02}) index={:02X} ref=@{:04X} card_id=#{:04X} {}\n",
z, e.deck_index, this->card_refs[z], e.card_id, name_for_card_state(e.state));
-2
View File
@@ -1,7 +1,5 @@
#include "MapState.hh"
using namespace std;
namespace Episode3 {
MapState::MapState() {
+47 -53
View File
@@ -2,11 +2,9 @@
#include "Server.hh"
using namespace std;
namespace Episode3 {
PlayerState::PlayerState(uint8_t client_id, shared_ptr<Server> server)
PlayerState::PlayerState(uint8_t client_id, std::shared_ptr<Server> server)
: w_server(server),
client_id(client_id),
num_hand_redraws_allowed(1),
@@ -46,10 +44,10 @@ void PlayerState::init() {
if (s->player_states.at(this->client_id).get() != this) {
// Note: The original code handles this, but we don't. This appears not to
// ever happen, so we didn't bother implementing it.
throw logic_error("replacing a player state object is not permitted");
throw std::logic_error("replacing a player state object is not permitted");
}
this->deck_state = make_shared<DeckState>(this->client_id, s->deck_entries[client_id]->card_ids, s);
this->deck_state = std::make_shared<DeckState>(this->client_id, s->deck_entries[client_id]->card_ids, s);
if (s->map_and_rules->rules.disable_deck_shuffle) {
this->deck_state->disable_shuffle();
}
@@ -62,7 +60,7 @@ void PlayerState::init() {
this->team_id = s->deck_entries[this->client_id]->team_id;
auto sc_ce = s->definition_for_card_ref(this->sc_card_ref);
if (!sc_ce) {
throw runtime_error("SC card definition is missing");
throw std::runtime_error("SC card definition is missing");
}
if (sc_ce->def.type == CardType::HUNTERS_SC) {
this->sc_card_type = CardType::HUNTERS_SC;
@@ -72,13 +70,13 @@ void PlayerState::init() {
// In the original code, sc_card_type gets left as 0xFFFFFFFF (yes, it's a
// uint32_t). This probably breaks some things later on, so we instead
// prevent it upfront.
throw runtime_error("SC card is not a Hunters or Arkz SC");
throw std::runtime_error("SC card is not a Hunters or Arkz SC");
}
this->hand_and_equip = make_shared<HandAndEquipState>();
this->card_short_statuses = make_shared<parray<CardShortStatus, 0x10>>();
this->set_card_action_chains = make_shared<parray<ActionChainWithConds, 9>>();
this->set_card_action_metadatas = make_shared<parray<ActionMetadata, 9>>();
this->hand_and_equip = std::make_shared<HandAndEquipState>();
this->card_short_statuses = std::make_shared<parray<CardShortStatus, 0x10>>();
this->set_card_action_chains = std::make_shared<parray<ActionChainWithConds, 9>>();
this->set_card_action_metadatas = std::make_shared<parray<ActionMetadata, 9>>();
this->hand_and_equip->clear_FF();
for (size_t z = 0; z < 0x10; z++) {
@@ -89,7 +87,7 @@ void PlayerState::init() {
this->set_card_action_metadatas->at(z).clear_FF();
}
this->sc_card = make_shared<Card>(this->deck_state->sc_card_id(), this->sc_card_ref, this->client_id, s);
this->sc_card = std::make_shared<Card>(this->deck_state->sc_card_id(), this->sc_card_ref, this->client_id, s);
this->sc_card->init();
this->draw_initial_hand();
if (s->options.is_nte()) {
@@ -114,18 +112,18 @@ void PlayerState::init() {
this->god_whim_can_use_hidden_cards = (s->deck_entries[this->client_id]->god_whim_flag != 3);
}
shared_ptr<Server> PlayerState::server() {
std::shared_ptr<Server> PlayerState::server() {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
shared_ptr<const Server> PlayerState::server() const {
std::shared_ptr<const Server> PlayerState::server() const {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
@@ -151,7 +149,7 @@ bool PlayerState::draw_cards_allowed() const {
return true;
}
void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter_ps) {
void PlayerState::apply_assist_card_effect_on_set(std::shared_ptr<PlayerState> setter_ps) {
auto s = this->server();
uint16_t assist_card_id = this->set_assist_card_id;
@@ -244,7 +242,7 @@ void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter
case AssistEffect::LEGACY: {
uint16_t total_cost = 0;
for (ssize_t z = 7; z >= 0; z--) {
shared_ptr<const Card> card = this->set_cards[z];
std::shared_ptr<const Card> card = this->set_cards[z];
if (card) {
auto ce = card->get_definition();
uint8_t card_cost = ce->def.self_cost;
@@ -258,7 +256,7 @@ void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter
if (!is_nte) {
this->on_cards_destroyed();
}
this->atk_points = min<uint8_t>(9, this->atk_points + (total_cost >> 1));
this->atk_points = std::min<uint8_t>(9, this->atk_points + (total_cost >> 1));
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
if (!is_nte) {
s->send_6xB4x05();
@@ -353,7 +351,7 @@ void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter
if (is_nte
? (other_ps->assist_remaining_turns != 90 && other_ps->assist_remaining_turns != 99)
: (other_ps->assist_remaining_turns < 10)) {
other_ps->assist_remaining_turns = min<uint8_t>(9, other_ps->assist_remaining_turns << 1);
other_ps->assist_remaining_turns = std::min<uint8_t>(9, other_ps->assist_remaining_turns << 1);
}
for (ssize_t set_index = is_nte ? 0 : -1; set_index < 8; set_index++) {
@@ -369,7 +367,7 @@ void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter
cond.remaining_turns <<= 1;
}
} else if (cond.remaining_turns < 10) {
cond.remaining_turns = min<uint8_t>(9, cond.remaining_turns << 1);
cond.remaining_turns = std::min<uint8_t>(9, cond.remaining_turns << 1);
}
}
}
@@ -480,7 +478,7 @@ void PlayerState::apply_dice_effects() {
}
for (size_t die_index = 0; die_index < 2; die_index++) {
this->dice_results[die_index] = min<uint8_t>(this->dice_results[die_index], 9);
this->dice_results[die_index] = std::min<uint8_t>(this->dice_results[die_index], 9);
}
}
@@ -818,15 +816,14 @@ int32_t PlayerState::error_code_for_client_setting_card(
}
}
vector<uint16_t> PlayerState::get_all_cards_within_range(
std::vector<uint16_t> PlayerState::get_all_cards_within_range(
const parray<uint8_t, 9 * 9>& range, const Location& loc, uint8_t target_team_id) const {
auto s = this->server();
auto log = s->log_stack("get_all_cards_within_range: ");
string loc_str = loc.str();
log.debug_f("loc={}, target_team_id={:02X}", loc_str, target_team_id);
log.debug_f("loc={}, target_team_id={:02X}", loc.str(), target_team_id);
vector<uint16_t> ret;
std::vector<uint16_t> ret;
for (size_t client_id = 0; client_id < 4; client_id++) {
auto other_ps = s->player_states[client_id];
if (other_ps && ((target_team_id == 0xFF) || (target_team_id == other_ps->get_team_id()))) {
@@ -845,7 +842,7 @@ void PlayerState::get_short_status_for_card_index_in_hand(size_t hand_index, Car
stat->card_ref = this->card_refs[hand_index - 1];
}
shared_ptr<DeckState> PlayerState::get_deck() {
std::shared_ptr<DeckState> PlayerState::get_deck() {
return this->deck_state;
}
@@ -871,11 +868,11 @@ uint16_t PlayerState::get_sc_card_id() const {
return this->sc_card_id;
}
shared_ptr<Card> PlayerState::get_sc_card() {
std::shared_ptr<Card> PlayerState::get_sc_card() {
return this->sc_card;
}
shared_ptr<const Card> PlayerState::get_sc_card() const {
std::shared_ptr<const Card> PlayerState::get_sc_card() const {
return this->sc_card;
}
@@ -887,11 +884,11 @@ CardType PlayerState::get_sc_card_type() const {
return this->sc_card_type;
}
shared_ptr<Card> PlayerState::get_set_card(size_t set_index) {
std::shared_ptr<Card> PlayerState::get_set_card(size_t set_index) {
return (set_index < 8) ? this->set_cards[set_index] : nullptr;
}
shared_ptr<const Card> PlayerState::get_set_card(size_t set_index) const {
std::shared_ptr<const Card> PlayerState::get_set_card(size_t set_index) const {
return (set_index < 8) ? this->set_cards[set_index] : nullptr;
}
@@ -964,7 +961,7 @@ uint16_t PlayerState::pop_from_discard_log(uint16_t) {
bool PlayerState::move_card_to_location_by_card_index(size_t card_index, const Location& new_loc) {
auto s = this->server();
shared_ptr<Card> card;
std::shared_ptr<Card> card;
if (card_index == 0) {
card = this->sc_card;
} else {
@@ -1010,7 +1007,7 @@ void PlayerState::move_null_hand_refs_to_end() {
void PlayerState::on_cards_destroyed() {
auto s = this->server();
unordered_multimap<uint16_t, bool> card_refs_map; // {card_ref: should_return_to_hand}
std::unordered_multimap<uint16_t, bool> card_refs_map; // {card_ref: should_return_to_hand}
for (size_t z = 0; z < 8; z++) {
auto card = this->set_cards[z];
if (!card || !(card->card_flags & 2)) {
@@ -1333,7 +1330,7 @@ bool PlayerState::set_card_from_hand(
}
this->card_refs[card_index + 1] = card_ref;
// Note: NTE doesn't call the destructor on the existing card, if there is one. Is that a bug?
this->set_cards[card_index - 7] = make_shared<Card>(s->card_id_for_card_ref(card_ref), card_ref, this->client_id, s);
this->set_cards[card_index - 7] = std::make_shared<Card>(s->card_id_for_card_ref(card_ref), card_ref, this->client_id, s);
auto new_card = this->set_cards[card_index - 7];
new_card->init();
@@ -1433,7 +1430,7 @@ void PlayerState::set_initial_location() {
static const uint8_t start_tile_defs_offset_for_team_size[4] = {0, 0, 1, 3};
if (num_team_players >= 4) {
throw logic_error("too many players on team");
throw std::logic_error("too many players on team");
}
size_t start_tile_def_index = start_tile_defs_offset_for_team_size[num_team_players] + player_index_within_team;
uint8_t player_start_tile = mr->map.start_tile_definitions[this->team_id][start_tile_def_index];
@@ -1455,11 +1452,11 @@ void PlayerState::set_initial_location() {
}
}
if (!start_tile_found) {
throw runtime_error("player start location not set");
throw std::runtime_error("player start location not set");
}
}
void PlayerState::set_map_occupied_bit_for_card_on_warp_tile(shared_ptr<const Card> card) {
void PlayerState::set_map_occupied_bit_for_card_on_warp_tile(std::shared_ptr<const Card> card) {
if (!card) {
return;
}
@@ -1524,7 +1521,7 @@ bool PlayerState::subtract_or_check_atk_or_def_points_for_action(const ActionSta
void PlayerState::subtract_atk_points(uint8_t cost) {
this->atk_points -= cost;
this->atk_points2 = min<uint8_t>(this->atk_points, this->atk_points2_max);
this->atk_points2 = std::min<uint8_t>(this->atk_points, this->atk_points2_max);
}
G_UpdateHand_Ep3_6xB4x02 PlayerState::prepare_6xB4x02() const {
@@ -1571,7 +1568,7 @@ void PlayerState::set_random_assist_card_from_hand_for_free() {
auto s = this->server();
bool is_nte = s->options.is_nte();
vector<uint16_t> candidate_card_refs;
std::vector<uint16_t> candidate_card_refs;
for (size_t hand_index = 0; hand_index < 6; hand_index++) {
uint16_t card_ref = this->card_refs[hand_index];
auto ce = s->definition_for_card_ref(card_ref);
@@ -1636,11 +1633,11 @@ void PlayerState::send_6xB4x04_if_needed(bool always_send) {
}
}
vector<uint16_t> PlayerState::get_card_refs_within_range_from_all_players(
std::vector<uint16_t> PlayerState::get_card_refs_within_range_from_all_players(
const parray<uint8_t, 9 * 9>& range, const Location& loc, CardType type) const {
auto s = this->server();
vector<uint16_t> ret;
std::vector<uint16_t> ret;
for (size_t client_id = 0; client_id < 4; client_id++) {
auto other_ps = s->player_states[client_id];
if (other_ps && ((other_ps->get_sc_card_type() == type) || (type == CardType::ITEM))) {
@@ -1697,7 +1694,7 @@ void PlayerState::handle_before_turn_assist_effects() {
break;
case AssistEffect::ATK_DICE_2:
// Note: This behavior doesn't match the card description. Is it supposed to add 2 or multiply by 2?
this->atk_points = min<int16_t>(this->atk_points + 2, 9);
this->atk_points = std::min<int16_t>(this->atk_points + 2, 9);
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
break;
case AssistEffect::SKIP_TURN:
@@ -1756,8 +1753,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
size_t z = 0;
do {
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string ref_str = s->debug_str_for_card_ref(pa.action_card_refs[z]);
log.debug_f("on action card ref {}", ref_str);
log.debug_f("on action card ref {}", s->debug_str_for_card_ref(pa.action_card_refs[z]));
}
card->unknown_80237A90(pa, pa.action_card_refs[z]);
card->unknown_802379BC(pa.action_card_refs[z]);
@@ -1793,8 +1789,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
auto target_card = s->card_for_set_card_ref(pa.target_card_refs[z]);
if (target_card) {
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string ref_str = s->debug_str_for_card_ref(pa.target_card_refs[z]);
log.debug_f("on target card ref {}", ref_str);
log.debug_f("on target card ref {}", s->debug_str_for_card_ref(pa.target_card_refs[z]));
}
target_card->unknown_802379DC(pa);
if (!is_nte) {
@@ -1823,8 +1818,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
}
for (size_t z = 0; (z < pa.action_card_refs.size()) && (pa.action_card_refs[z] != 0xFFFF); z++) {
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string ref_str = s->debug_str_for_card_ref(pa.action_card_refs[z]);
log.debug_f("discarding {} from hand", ref_str);
log.debug_f("discarding {} from hand", s->debug_str_for_card_ref(pa.action_card_refs[z]));
}
this->discard_ref_from_hand(pa.action_card_refs[z]);
}
@@ -1866,7 +1860,7 @@ void PlayerState::dice_phase_before() {
this->send_set_card_updates();
}
void PlayerState::handle_homesick_assist_effect_from_bomb(shared_ptr<Card> card) {
void PlayerState::handle_homesick_assist_effect_from_bomb(std::shared_ptr<Card> card) {
if (!card) {
return;
}
@@ -1998,13 +1992,13 @@ void PlayerState::roll_main_dice_or_apply_after_effects() {
}
this->atk_points += s->team_dice_bonus[this->team_id];
this->def_points += s->team_dice_bonus[this->team_id];
this->atk_points = clamp<uint8_t>(this->atk_points, 1, 9);
this->def_points = clamp<uint8_t>(this->def_points, 1, 9);
this->atk_points = std::clamp<uint8_t>(this->atk_points, 1, 9);
this->def_points = std::clamp<uint8_t>(this->def_points, 1, 9);
if (!s->options.is_nte()) {
this->atk_bonuses = this->atk_points - atk_before_bonuses;
this->def_bonuses = this->def_points - def_before_bonuses;
}
this->atk_points2 = min<uint8_t>(this->atk_points2_max, this->atk_points);
this->atk_points2 = std::min<uint8_t>(this->atk_points2_max, this->atk_points);
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
}
@@ -2036,7 +2030,7 @@ void PlayerState::compute_team_dice_bonus_after_draw_phase() {
uint8_t current_team_turn = s->get_current_team_turn();
uint8_t dice_boost = s->get_team_exp(current_team_turn) / (s->team_client_count[current_team_turn] * 12);
s->card_special->adjust_dice_boost_if_team_has_condition_52(current_team_turn, &dice_boost, 0);
s->team_dice_bonus[current_team_turn] = clamp<int16_t>(dice_boost, 0, 8);
s->team_dice_bonus[current_team_turn] = std::clamp<int16_t>(dice_boost, 0, 8);
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
}
+16 -18
View File
@@ -2,8 +2,6 @@
#include "Server.hh"
using namespace std;
namespace Episode3 {
Condition::Condition() {
@@ -61,7 +59,7 @@ void Condition::clear_FF() {
this->unknown_a8 = 0xFF;
}
std::string Condition::str(shared_ptr<const Server> s) const {
std::string Condition::str(std::shared_ptr<const Server> s) const {
return std::format(
"Condition[type={}, turns={}, a_arg={}, dice={}, flags={:02X}, "
"def_eff_index={}, ref={}, value={}, giver_ref={} "
@@ -98,7 +96,7 @@ void EffectResult::clear() {
this->dice_roll_value = 0;
}
std::string EffectResult::str(shared_ptr<const Server> s) const {
std::string EffectResult::str(std::shared_ptr<const Server> s) const {
return std::format(
"EffectResult[att_ref={}, target_ref={}, value={}, cur_hp={}, ap={}, tp={}, flags={:02X}, op={}, cond_index={}, dice={}]",
s->debug_str_for_card_ref(this->attacker_card_ref),
@@ -130,7 +128,7 @@ bool CardShortStatus::operator!=(const CardShortStatus& other) const {
return !this->operator==(other);
}
std::string CardShortStatus::str(shared_ptr<const Server> s) const {
std::string CardShortStatus::str(std::shared_ptr<const Server> s) const {
return std::format(
"CardShortStatus[ref={}, cur_hp={}, flags={:08X}, loc={}, u1={:04X}, max_hp={}, u2={}]",
s->debug_str_for_card_ref(this->card_ref),
@@ -178,7 +176,7 @@ void ActionState::clear() {
this->unused2 = 0xFFFF;
}
std::string ActionState::str(shared_ptr<const Server> s) const {
std::string ActionState::str(std::shared_ptr<const Server> s) const {
return std::format(
"ActionState[client={:X}, u={}, facing={}, attacker_ref={}, def_ref={}, target_refs={}, action_refs={}, orig_attacker_ref={}]",
this->client_id,
@@ -222,7 +220,7 @@ bool ActionChain::operator!=(const ActionChain& other) const {
return !this->operator==(other);
}
std::string ActionChain::str(shared_ptr<const Server> s) const {
std::string ActionChain::str(std::shared_ptr<const Server> s) const {
return std::format(
"ActionChain[eff_ap={}, eff_tp={}, ap_bonus={}, damage={}, acting_ref={}, unknown_ref_a3={}, attack_action_refs={}, "
"attack_action_ref_count={}, medium={}, target_ref_count={}, subphase={}, strikes={}, damage_mult={}, attack_num={}, "
@@ -309,8 +307,8 @@ bool ActionChainWithConds::operator!=(const ActionChainWithConds& other) const {
return !this->operator==(other);
}
std::string ActionChainWithConds::str(shared_ptr<const Server> s) const {
string ret = "ActionChainWithConds[chain=";
std::string ActionChainWithConds::str(std::shared_ptr<const Server> s) const {
std::string ret = "ActionChainWithConds[chain=";
ret += this->chain.str(s);
ret += ", conds=[";
for (size_t z = 0; z < this->conditions.size(); z++) {
@@ -383,7 +381,7 @@ void ActionChainWithConds::set_flags(uint32_t flags) {
this->chain.flags |= flags;
}
void ActionChainWithConds::add_attack_action_card_ref(uint16_t card_ref, shared_ptr<Server> server) {
void ActionChainWithConds::add_attack_action_card_ref(uint16_t card_ref, std::shared_ptr<Server> server) {
if (card_ref != 0xFFFF) {
this->chain.attack_action_card_refs[this->chain.attack_action_card_ref_count++] = card_ref;
}
@@ -397,7 +395,7 @@ void ActionChainWithConds::add_target_card_ref(uint16_t card_ref) {
}
}
void ActionChainWithConds::compute_attack_medium(shared_ptr<Server> server) {
void ActionChainWithConds::compute_attack_medium(std::shared_ptr<Server> server) {
this->chain.attack_medium = AttackMedium::PHYSICAL;
for (size_t z = 0; z < this->chain.attack_action_card_ref_count; z++) {
uint16_t card_ref = this->chain.attack_action_card_refs[z];
@@ -437,7 +435,7 @@ bool ActionChainWithConds::get_condition_value(
return any_found;
}
void ActionChainWithConds::set_action_subphase_from_card(shared_ptr<const Card> card) {
void ActionChainWithConds::set_action_subphase_from_card(std::shared_ptr<const Card> card) {
this->chain.action_subphase = card->server()->get_current_action_subphase();
}
@@ -545,7 +543,7 @@ bool ActionMetadata::operator!=(const ActionMetadata& other) const {
return !this->operator==(other);
}
std::string ActionMetadata::str(shared_ptr<const Server> s) const {
std::string ActionMetadata::str(std::shared_ptr<const Server> s) const {
return std::format(
"ActionMetadata[ref={}, target_ref_count={}, def_ref_count={}, subphase={}, def_power={}, def_bonus={}, "
"att_bonus={}, flags={:08X}, target_refs={}, defense_refs={}, original_attacker_refs={}]",
@@ -621,7 +619,7 @@ void ActionMetadata::add_target_card_ref(uint16_t card_ref) {
}
void ActionMetadata::add_defense_card_ref(
uint16_t defense_card_ref, shared_ptr<Card> card, uint16_t original_attacker_card_ref) {
uint16_t defense_card_ref, std::shared_ptr<Card> card, uint16_t original_attacker_card_ref) {
if ((defense_card_ref != 0xFFFF) && (this->defense_card_ref_count < 8)) {
this->defense_card_refs[this->defense_card_ref_count] = defense_card_ref;
this->original_attacker_card_refs[this->defense_card_ref_count] = original_attacker_card_ref;
@@ -634,7 +632,7 @@ HandAndEquipState::HandAndEquipState() {
this->clear();
}
std::string HandAndEquipState::str(shared_ptr<const Server> s) const {
std::string HandAndEquipState::str(std::shared_ptr<const Server> s) const {
return std::format(
"HandAndEquipState[dice=[{}, {}], atk={}, def={}, atk2={}, a1={}, total_set_cost={}, is_cpu={}, assist_flags={:08X}, "
"hand_refs={}, assist_ref={}, set_refs={}, sc_ref={}, hand_refs2={}, set_refs2={}, assist_ref2={}, assist_set_num={}, assist_card_id={}, "
@@ -777,7 +775,7 @@ uint8_t PlayerBattleStats::rank_for_score(float score) {
const char* PlayerBattleStats::name_for_rank(uint8_t rank) {
if (rank >= RANK_THRESHOLD_COUNT + 1) {
throw invalid_argument("invalid rank");
throw std::invalid_argument("invalid rank");
}
return RANK_NAMES[rank];
}
@@ -842,12 +840,12 @@ static bool is_card_within_range(
return ret;
}
vector<uint16_t> get_card_refs_within_range(
std::vector<uint16_t> get_card_refs_within_range(
const parray<uint8_t, 9 * 9>& range,
const Location& loc,
const parray<CardShortStatus, 0x10>& short_statuses,
phosg::PrefixedLogger* log) {
vector<uint16_t> ret;
std::vector<uint16_t> ret;
if (is_card_within_range(range, loc, short_statuses[0], log)) {
if (log) {
log->debug_f("get_card_refs_within_range: sc card @{:04X} within range", short_statuses[0].card_ref);
+57 -64
View File
@@ -5,20 +5,17 @@
#include "DataIndexes.hh"
#include "Server.hh"
using namespace std;
namespace Episode3 {
void compute_effective_range(
parray<uint8_t, 9 * 9>& ret,
shared_ptr<const CardIndex> card_index,
std::shared_ptr<const CardIndex> card_index,
uint16_t card_id,
const Location& loc,
shared_ptr<const MapAndRulesState> map_and_rules,
std::shared_ptr<const MapAndRulesState> map_and_rules,
phosg::PrefixedLogger* log) {
if (log && log->should_log(phosg::LogLevel::L_DEBUG)) {
string loc_str = loc.str();
log->debug_f("compute_effective_range: card_id=#{:04X}, loc={}", card_id, loc_str);
log->debug_f("compute_effective_range: card_id=#{:04X}, loc={}", card_id, loc.str());
log->debug_f("compute_effective_range: map_and_rules->map:");
map_and_rules->map.print(stderr);
}
@@ -28,10 +25,10 @@ void compute_effective_range(
if (card_id == 0xFFFE) { // Heavy Fog: one tile directly in front
range_def[3] = 0x00000100;
} else {
shared_ptr<const CardIndex::CardEntry> ce;
std::shared_ptr<const CardIndex::CardEntry> ce;
try {
ce = card_index->definition_for_id(card_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return;
}
for (size_t z = 0; z < 6; z++) {
@@ -95,7 +92,7 @@ void compute_effective_range(
up_y = 9 - y - 1;
break;
default:
throw logic_error("invalid direction");
throw std::logic_error("invalid direction");
}
ret[y * 9 + x] = decoded_range[up_y * 9 + up_x];
if (log) {
@@ -117,9 +114,9 @@ void compute_effective_range(
}
bool card_linkage_is_valid(
shared_ptr<const CardIndex::CardEntry> right_ce,
shared_ptr<const CardIndex::CardEntry> left_ce,
shared_ptr<const CardIndex::CardEntry> sc_ce,
std::shared_ptr<const CardIndex::CardEntry> right_ce,
std::shared_ptr<const CardIndex::CardEntry> left_ce,
std::shared_ptr<const CardIndex::CardEntry> sc_ce,
bool has_permission_effect) {
if (!right_ce) {
return false;
@@ -173,31 +170,27 @@ bool card_linkage_is_valid(
return false;
}
RulerServer::RulerServer(shared_ptr<Server> server)
: w_server(server),
team_id_for_client_id(0xFF),
error_code1(0),
error_code2(0),
error_code3(0) {}
RulerServer::RulerServer(std::shared_ptr<Server> server)
: w_server(server), team_id_for_client_id(0xFF), error_code1(0), error_code2(0), error_code3(0) {}
shared_ptr<Server> RulerServer::server() {
std::shared_ptr<Server> RulerServer::server() {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
shared_ptr<const Server> RulerServer::server() const {
std::shared_ptr<const Server> RulerServer::server() const {
auto s = this->w_server.lock();
if (!s) {
throw runtime_error("server is deleted");
throw std::runtime_error("server is deleted");
}
return s;
}
ActionChainWithConds* RulerServer::action_chain_with_conds_for_card_ref(uint16_t card_ref) {
return const_cast<ActionChainWithConds*>(as_const(*this).action_chain_with_conds_for_card_ref(card_ref));
return const_cast<ActionChainWithConds*>(std::as_const(*this).action_chain_with_conds_for_card_ref(card_ref));
}
const ActionChainWithConds* RulerServer::action_chain_with_conds_for_card_ref(uint16_t card_ref) const {
@@ -398,7 +391,7 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(const ActionState& pa
last_action_card_index = z;
}
auto check_chain = [&]() -> optional<bool> {
auto check_chain = [&]() -> std::optional<bool> {
const auto* chain = this->action_chain_with_conds_for_card_ref(pa.attacker_card_ref);
if (chain) {
for (ssize_t cond_index = 8; cond_index >= 0; cond_index--) {
@@ -415,7 +408,7 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(const ActionState& pa
}
}
}
return nullopt;
return std::nullopt;
};
if (is_nte) {
@@ -767,7 +760,7 @@ bool RulerServer::check_move_path_and_get_cost(
if (max_dist < 1) {
return false;
}
max_dist = min<uint8_t>(max_dist, 9);
max_dist = std::min<uint8_t>(max_dist, 9);
const auto* short_status = this->short_status_for_card_ref(card_ref);
if (!short_status) {
@@ -1030,7 +1023,7 @@ bool RulerServer::check_usability_or_condition_apply(
}
break;
case CriterionCode::HUNTER_NON_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0001, // Orland
0x0002, // Kranz
0x0003, // Ino'lis
@@ -1059,7 +1052,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_HU_CLASS_MALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0001, // Orland
0x0113, // Teifu
0x02AA, // H-HUmar
@@ -1070,7 +1063,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis
0x0004, // Sil'fer
0x0006, // Kylria
@@ -1094,7 +1087,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_NON_RA_CLASS_HUMAN_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0001, // Orland
0x0003, // Ino'lis
0x0004, // Sil'fer
@@ -1117,7 +1110,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_HU_CLASS_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0110, // Saligun
0x0113, // Teifu
0x02AC, // H-HUcast
@@ -1128,7 +1121,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_NON_RA_CLASS_NON_NEWMAN_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0001, // Orland
0x0003, // Ino'lis
0x0110, // Saligun
@@ -1148,7 +1141,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_NON_NEWMAN_NON_FORCE_MALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0001, // Orland
0x0002, // Kranz
0x0005, // Guykild
@@ -1166,7 +1159,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_HUNEWEARL_CLASS_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0004, // Sil'fer
0x02AB, // H-HUnewearl
0x02CF, // H-HUnewearl
@@ -1174,7 +1167,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_RA_CLASS_MALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0002, // Kranz
0x0005, // Guykild
0x02AE, // H-RAmar
@@ -1185,7 +1178,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_RA_CLASS_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0006, // Kylria
0x0114, // Stella
0x02AF, // H-RAmarl
@@ -1196,7 +1189,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_RA_OR_FO_CLASS_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis
0x0006, // Kylria
0x0112, // Viviana
@@ -1213,7 +1206,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_HU_OR_RA_CLASS_HUMAN_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0001, // Orland
0x0002, // Kranz
0x0004, // Sil'fer
@@ -1230,7 +1223,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_RA_CLASS_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0005, // Guykild
0x0114, // Stella
0x02B0, // H-RAcast
@@ -1241,7 +1234,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_FO_CLASS_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis
0x0112, // Viviana
0x02B3, // H-FOmarl
@@ -1252,7 +1245,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_HUMAN_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis
0x0004, // Sil'fer
0x0006, // Kylria
@@ -1269,7 +1262,7 @@ bool RulerServer::check_usability_or_condition_apply(
return ret && card_ids.count(card_id2);
}
case CriterionCode::HUNTER_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = {
static const std::unordered_set<uint16_t> card_ids = {
0x0005, // Guykild
0x0110, // Saligun
0x0113, // Teifu
@@ -1433,14 +1426,14 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
final_cost = 0;
}
} else {
final_cost = max<int16_t>(final_cost, this->hand_and_equip_states[pa.client_id]->atk_points);
final_cost = std::max<int16_t>(final_cost, this->hand_and_equip_states[pa.client_id]->atk_points);
}
}
if (out_ally_cost) {
*out_ally_cost = total_ally_cost;
}
return max<int16_t>(final_cost, total_cost + assist_cost_bias);
return std::max<int16_t>(final_cost, total_cost + assist_cost_bias);
}
bool RulerServer::compute_effective_range_and_target_mode_for_attack(
@@ -1627,7 +1620,7 @@ bool RulerServer::defense_card_can_apply_to_attack(
bool RulerServer::defense_card_matches_any_attack_card_top_color(const ActionState& pa) const {
auto ce = this->definition_for_card_ref(pa.action_card_refs[0]);
if (!ce) {
throw runtime_error("defense card definition is missing");
throw std::runtime_error("defense card definition is missing");
}
const auto* chain = this->action_chain_with_conds_for_card_ref(pa.original_attacker_card_ref);
if (chain->chain.attack_action_card_ref_count < 1) {
@@ -1646,7 +1639,7 @@ bool RulerServer::defense_card_matches_any_attack_card_top_color(const ActionSta
return false;
}
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
std::shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
uint16_t card_id = this->card_id_for_card_ref(card_ref);
if (card_id == 0xFFFF) {
return nullptr;
@@ -1841,7 +1834,7 @@ int32_t RulerServer::error_code_for_client_setting_card(
return -0x7E;
}
} else {
int16_t diff = max<int16_t>(summon_area_size - summon_cost, 0);
int16_t diff = std::max<int16_t>(summon_area_size - summon_cost, 0);
if (x_offset > 0) {
if (loc->x < summon_area_loc.x) {
return -0x7E;
@@ -1860,7 +1853,7 @@ int32_t RulerServer::error_code_for_client_setting_card(
return -0x7E;
}
} else {
int16_t diff = max<int16_t>(summon_area_size - summon_cost, 0);
int16_t diff = std::max<int16_t>(summon_area_size - summon_cost, 0);
if (y_offset > 0) {
if (loc->y < summon_area_loc.y) {
return -0x7E;
@@ -1974,7 +1967,7 @@ bool RulerServer::flood_fill_move_path(
Direction dirs[3] = {direction, turn_left(direction), turn_right(direction)};
for (size_t dir_index = 0; dir_index < 3; dir_index++) {
if (static_cast<uint8_t>(dirs[dir_index]) > 3) {
throw logic_error("invalid direction");
throw std::logic_error("invalid direction");
}
ret |= this->flood_fill_move_path(
chain,
@@ -2017,7 +2010,7 @@ uint16_t RulerServer::get_ally_sc_card_ref(uint16_t card_ref) const {
return 0xFFFF;
}
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_id(uint32_t card_id) const {
std::shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_id(uint32_t card_id) const {
return this->server()->definition_for_card_id(card_id);
}
@@ -2130,11 +2123,11 @@ bool RulerServer::get_creature_summon_area(uint8_t client_id, Location* out_loc,
return true;
}
shared_ptr<HandAndEquipState> RulerServer::get_hand_and_equip_state_for_client_id(uint8_t client_id) {
std::shared_ptr<HandAndEquipState> RulerServer::get_hand_and_equip_state_for_client_id(uint8_t client_id) {
return (client_id < 4) ? this->hand_and_equip_states[client_id] : nullptr;
}
shared_ptr<const HandAndEquipState> RulerServer::get_hand_and_equip_state_for_client_id(uint8_t client_id) const {
std::shared_ptr<const HandAndEquipState> RulerServer::get_hand_and_equip_state_for_client_id(uint8_t client_id) const {
return (client_id < 4) ? this->hand_and_equip_states[client_id] : nullptr;
}
@@ -2171,7 +2164,7 @@ ssize_t RulerServer::get_path_cost(
path_length *= cond.value;
}
}
return clamp<ssize_t>(path_length + cost_penalty, 0, 99);
return std::clamp<ssize_t>(path_length + cost_penalty, 0, 99);
}
ActionType RulerServer::get_pending_action_type(const ActionState& pa) const {
@@ -2440,9 +2433,9 @@ bool RulerServer::is_defense_valid(const ActionState& pa) {
}
void RulerServer::link_objects(
shared_ptr<MapAndRulesState> map_and_rules,
shared_ptr<StateFlags> state_flags,
shared_ptr<AssistServer> assist_server) {
std::shared_ptr<MapAndRulesState> map_and_rules,
std::shared_ptr<StateFlags> state_flags,
std::shared_ptr<AssistServer> assist_server) {
this->map_and_rules = map_and_rules;
this->state_flags = state_flags;
this->assist_server = assist_server;
@@ -2491,7 +2484,7 @@ size_t RulerServer::max_move_distance_for_card_ref(uint32_t card_ref) const {
if (this->find_condition_on_card_ref(card_ref, ConditionType::SET_MV, &cond, nullptr, true)) {
ret = cond.value;
}
ret = max<ssize_t>(0, ret);
ret = std::max<ssize_t>(0, ret);
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(client_id);
bool has_stamina_effect = false;
@@ -2505,7 +2498,7 @@ size_t RulerServer::max_move_distance_for_card_ref(uint32_t card_ref) const {
}
}
return has_stamina_effect ? 9 : min<ssize_t>(9, ret);
return has_stamina_effect ? 9 : std::min<ssize_t>(9, ret);
}
}
@@ -2566,11 +2559,11 @@ void RulerServer::offsets_for_direction(
void RulerServer::register_player(
uint8_t client_id,
shared_ptr<HandAndEquipState> hes,
shared_ptr<parray<CardShortStatus, 0x10>> short_statuses,
shared_ptr<DeckEntry> deck_entry,
shared_ptr<parray<ActionChainWithConds, 9>> set_card_action_chains,
shared_ptr<parray<ActionMetadata, 9>> set_card_action_metadatas) {
std::shared_ptr<HandAndEquipState> hes,
std::shared_ptr<parray<CardShortStatus, 0x10>> short_statuses,
std::shared_ptr<DeckEntry> deck_entry,
std::shared_ptr<parray<ActionChainWithConds, 9>> set_card_action_chains,
std::shared_ptr<parray<ActionMetadata, 9>> set_card_action_metadatas) {
this->hand_and_equip_states[client_id] = hes;
this->short_statuses[client_id] = short_statuses;
this->deck_entries[client_id] = deck_entry;
@@ -2656,7 +2649,7 @@ int32_t RulerServer::set_cost_for_card(uint8_t client_id, uint16_t card_ref) con
// In NTE, Land Price is apparently 2x rather than 1.5x
ret = is_nte ? (ret << 1) : (ret + (ret >> 1));
} else if (eff == AssistEffect::DEFLATION) {
ret = max<int32_t>(0, ret - 1);
ret = std::max<int32_t>(0, ret - 1);
} else if (eff == AssistEffect::INFLATION) {
ret++;
}
+117 -119
View File
@@ -7,11 +7,9 @@
#include "../Revision.hh"
#include "../SendCommands.hh"
using namespace std;
namespace Episode3 {
// This is (obviously) not the original string. The original string is:
// This is (obviously) not the original std::string. The original std::string is:
// NTE: "03/05/29 18:00 by K.Toya"
// Final: "[V1][FINAL2.0] 03/09/13 15:30 by K.Toya"
static const char* VERSION_SIGNATURE = "newserv Ep3 based on [V1][FINAL2.0] 03/09/13 15:30 by K.Toya";
@@ -27,7 +25,7 @@ void Server::PresenceEntry::clear() {
this->is_cpu_player = 0;
}
Server::Server(shared_ptr<Lobby> lobby, Options&& options)
Server::Server(std::shared_ptr<Lobby> lobby, Options&& options)
: lobby(lobby),
battle_record(lobby ? lobby->battle_record : nullptr),
has_lobby(lobby != nullptr),
@@ -81,7 +79,7 @@ Server::Server(shared_ptr<Lobby> lobby, Options&& options)
Server::~Server() noexcept(false) {
if (this->logger_stack.size() != 1) {
throw logic_error(std::format("incorrect logger stack size: expected 1, received {}", this->logger_stack.size()));
throw std::logic_error(std::format("incorrect logger stack size: expected 1, received {}", this->logger_stack.size()));
}
delete this->logger_stack.back();
}
@@ -89,31 +87,31 @@ Server::~Server() noexcept(false) {
void Server::init() {
this->log().info_f("Creating server with random seed {:08X}", this->options.rand_crypt->seed());
this->map_and_rules = make_shared<MapAndRulesState>();
this->map_and_rules = std::make_shared<MapAndRulesState>();
this->num_clients_present = 0;
this->overlay_state.clear();
for (size_t z = 0; z < 4; z++) {
this->presence_entries[z].clear();
this->deck_entries[z] = make_shared<DeckEntry>();
this->deck_entries[z] = std::make_shared<DeckEntry>();
this->name_entries[z].clear();
this->name_entries_valid[z] = false;
}
this->card_special = make_shared<CardSpecial>(this->shared_from_this());
this->card_special = std::make_shared<CardSpecial>(this->shared_from_this());
// Note: The original implementation calls the default PSOV2Encryption constructor for random_crypt, which just uses
// 0 as the seed. It then re-seeds the generator later. We instead expect the caller to provide a seeded generator,
// and we don't re-seed it at all.
// this->random_crypt = make_shared<PSOV2Encryption>(0);
// this->random_crypt = std::make_shared<PSOV2Encryption>(0);
this->state_flags = make_shared<StateFlags>();
this->state_flags = std::make_shared<StateFlags>();
this->clear_player_flags_after_dice_phase();
this->update_battle_state_flags_and_send_6xB4x03_if_needed();
this->assist_server = make_shared<AssistServer>(this->shared_from_this());
this->ruler_server = make_shared<RulerServer>(this->shared_from_this());
this->assist_server = std::make_shared<AssistServer>(this->shared_from_this());
this->ruler_server = std::make_shared<RulerServer>(this->shared_from_this());
this->ruler_server->link_objects(this->map_and_rules, this->state_flags, this->assist_server);
this->send_6xB4x46();
@@ -135,7 +133,7 @@ Server::StackLogger::StackLogger(StackLogger&& other)
: PrefixedLogger(std::move(other)),
server(other.server) {
if (this->server->logger_stack.back() != &other) {
throw logic_error("cannot move StackLogger unless it is the last one");
throw std::logic_error("cannot move StackLogger unless it is the last one");
}
this->server->logger_stack.back() = this;
}
@@ -144,7 +142,7 @@ Server::StackLogger& Server::StackLogger::operator=(StackLogger&& other) {
this->PrefixedLogger::operator=(std::move(other));
this->server = other.server;
if (this->server->logger_stack.back() != &other) {
throw logic_error("cannot move StackLogger unless it is the last one");
throw std::logic_error("cannot move StackLogger unless it is the last one");
}
this->server->logger_stack.back() = this;
return *this;
@@ -152,7 +150,7 @@ Server::StackLogger& Server::StackLogger::operator=(StackLogger&& other) {
Server::StackLogger::~StackLogger() noexcept(false) {
if (this->server->logger_stack.back() != this) {
throw logic_error("incorrect logger stack unwind order");
throw std::logic_error("incorrect logger stack unwind order");
}
this->server->logger_stack.pop_back();
}
@@ -171,8 +169,7 @@ std::string Server::debug_str_for_card_ref(uint16_t card_ref) const {
}
auto ce = this->definition_for_card_ref(card_ref);
if (ce) {
string name = ce->def.en_name.decode();
return std::format("@{:04X} (#{:04X} {})", card_ref, ce->def.card_id, name);
return std::format("@{:04X} (#{:04X} {})", card_ref, ce->def.card_id, ce->def.en_name.decode());
} else {
return std::format("@{:04X} (missing)", card_ref);
}
@@ -184,8 +181,7 @@ std::string Server::debug_str_for_card_id(uint16_t card_id) const {
}
auto ce = this->definition_for_card_id(card_id);
if (ce) {
string name = ce->def.en_name.decode();
return std::format("#{:04X} ({})", card_id, name);
return std::format("#{:04X} ({})", card_id, ce->def.en_name.decode());
} else {
return std::format("#{:04X} (missing)", card_id);
}
@@ -209,17 +205,17 @@ int8_t Server::get_winner_team_id() const {
}
if (!team_player_counts[0] || !team_player_counts[1]) {
throw logic_error("at least one team has no players");
throw std::logic_error("at least one team has no players");
}
if (team_win_flag_counts[0] && team_win_flag_counts[1]) {
throw logic_error("both teams have winning players");
throw std::logic_error("both teams have winning players");
}
for (int8_t z = 0; z < 2; z++) {
if (!team_win_flag_counts[z]) {
continue;
}
if (team_win_flag_counts[z] != team_player_counts[z]) {
throw logic_error("only some players on team have won");
throw std::logic_error("only some players on team have won");
}
return z;
}
@@ -236,10 +232,10 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
if (this->has_lobby) {
auto l = this->lobby.lock();
if (!l) {
throw runtime_error("lobby is deleted");
throw std::runtime_error("lobby is deleted");
}
string masked_data;
std::string masked_data;
if (enable_masking &&
!this->options.is_nte() &&
!(this->options.behavior_flags & BehaviorFlag::DISABLE_MASKING) &&
@@ -267,7 +263,7 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
void Server::send_6xB4x46() const {
// Note: This function is not part of the original implementation; it was factored out from its callsites in this
// file and the strings were changed.
// file and the std::strings were changed.
// NTE doesn't have the date_str2 field, but we send it anyway to make debugging easier.
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
@@ -275,12 +271,13 @@ void Server::send_6xB4x46() const {
cmd.date_str1.encode(
std::format("Card definitions: {:016X}", this->options.card_index->definitions_hash()),
Language::ENGLISH);
string build_date = phosg::format_time(BUILD_TIMESTAMP);
std::string build_date = phosg::format_time(BUILD_TIMESTAMP);
cmd.date_str2.encode(std::format("newserv {} compiled at {}", GIT_REVISION_HASH, build_date), Language::ENGLISH);
this->send(cmd);
}
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::Map> map, Language language, bool is_nte) {
std::string Server::prepare_6xB6x41_map_definition(
std::shared_ptr<const MapIndex::Map> map, Language language, bool is_nte) {
auto vm = map->version(language);
const auto& compressed = vm->compressed(is_nte);
@@ -295,7 +292,8 @@ string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::Map> ma
void Server::send_commands_for_joining_spectator(std::shared_ptr<Channel> ch) const {
if (this->last_chosen_map) {
string data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, ch->language, this->options.is_nte());
std::string data = this->prepare_6xB6x41_map_definition(
this->last_chosen_map, ch->language, this->options.is_nte());
this->log().info_f(
"Sending {} version of map {:08X}", name_for_language(ch->language), this->last_chosen_map->map_number);
ch->send(0x6C, 0x00, data);
@@ -366,11 +364,12 @@ void Server::add_team_exp(uint8_t team_id, int32_t exp) {
}
}
this->team_exp[team_id] = clamp<int16_t>(this->team_exp[team_id] + exp, 0, this->team_client_count[team_id] * 96);
this->team_exp[team_id] = std::clamp<int16_t>(
this->team_exp[team_id] + exp, 0, this->team_client_count[team_id] * 96);
uint8_t dice_boost = this->team_exp[team_id] / (this->team_client_count[team_id] * 12);
this->card_special->adjust_dice_boost_if_team_has_condition_52(team_id, &dice_boost, 0);
this->team_dice_bonus[team_id] = min<uint8_t>(dice_boost, 8);
this->team_dice_bonus[team_id] = std::min<uint8_t>(dice_boost, 8);
}
bool Server::advance_battle_phase() {
@@ -396,7 +395,7 @@ bool Server::advance_battle_phase() {
this->dice_phase_before();
break;
default:
throw logic_error("invalid battle phase");
throw std::logic_error("invalid battle phase");
}
return this->check_for_battle_end();
}
@@ -414,15 +413,15 @@ void Server::draw_phase_before() {
}
}
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
std::shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
try {
return this->options.card_index->definition_for_id(this->card_id_for_card_ref(card_ref));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
shared_ptr<Card> Server::card_for_set_card_ref(uint16_t card_ref) {
std::shared_ptr<Card> Server::card_for_set_card_ref(uint16_t card_ref) {
if (card_ref == 0xFFFF) {
return nullptr;
}
@@ -447,7 +446,7 @@ shared_ptr<Card> Server::card_for_set_card_ref(uint16_t card_ref) {
return nullptr;
}
shared_ptr<const Card> Server::card_for_set_card_ref(uint16_t card_ref) const {
std::shared_ptr<const Card> Server::card_for_set_card_ref(uint16_t card_ref) const {
return const_cast<Server*>(this)->card_for_set_card_ref(card_ref);
}
@@ -584,7 +583,7 @@ bool Server::check_for_battle_end() {
void Server::force_replace_assist_card(uint8_t client_id, uint16_t card_id) {
auto ps = this->player_states.at(client_id);
if (!ps) {
throw runtime_error("player does not exist");
throw std::runtime_error("player does not exist");
}
if (card_id == 0xFFFF) {
ps->discard_set_assist_card();
@@ -599,12 +598,12 @@ void Server::force_replace_assist_card(uint8_t client_id, uint16_t card_id) {
void Server::force_destroy_field_character(uint8_t client_id, size_t visible_index) {
auto ps = this->player_states.at(client_id);
if (!ps) {
throw runtime_error("player does not exist");
throw std::runtime_error("player does not exist");
}
// TODO: Is it possible for there to be gaps in the set cards array? If not, we could just do a direct array lookup
// here instead of this loop
shared_ptr<Card> set_card = nullptr;
std::shared_ptr<Card> set_card = nullptr;
for (size_t set_index = 0; set_index < 8; set_index++) {
if (!ps->set_cards[set_index]) {
continue;
@@ -681,7 +680,7 @@ void Server::compute_all_map_occupied_bits() {
}
void Server::compute_team_dice_bonus(uint8_t team_id) {
this->team_dice_bonus[team_id] = clamp<int16_t>(
this->team_dice_bonus[team_id] = std::clamp<int16_t>(
this->team_exp[team_id] / (this->team_client_count[team_id] * 12), 0, 8);
}
@@ -700,10 +699,10 @@ void Server::copy_player_states_to_prev_states() {
}
}
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
std::shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
try {
return this->options.card_index->definition_for_id(card_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
@@ -731,7 +730,7 @@ void Server::determine_first_team_turn() {
this->team_client_count[0] = this->map_and_rules->num_team0_players;
this->team_client_count[1] = this->map_and_rules->num_players - this->team_client_count[0];
if (this->team_client_count[0] == 0 || this->team_client_count[1] == 0) {
throw runtime_error("one or both teams have no players");
throw std::runtime_error("one or both teams have no players");
}
this->first_team_turn = 0xFF;
while (this->first_team_turn == 0xFF) {
@@ -927,7 +926,7 @@ void Server::end_action_phase() {
bool Server::enqueue_attack_or_defense(uint8_t client_id, ActionState* pa) {
auto log = this->log_stack("enqueue_attack_or_defense: ");
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string s = pa->str(this->shared_from_this());
std::string s = pa->str(this->shared_from_this());
log.debug_f("input: {}", s);
}
@@ -977,8 +976,8 @@ bool Server::enqueue_attack_or_defense(uint8_t client_id, ActionState* pa) {
size_t attack_index = this->num_pending_attacks++;
this->pending_attacks[attack_index] = *pa;
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string pa_str = this->pending_attacks[attack_index].str(this->shared_from_this());
log.debug_f("set pending attack {}: {}", attack_index, pa_str);
log.debug_f("set pending attack {}: {}",
attack_index, this->pending_attacks[attack_index].str(this->shared_from_this()));
}
ps->set_action_cards_for_action_state(*pa);
log.debug_f("set action cards");
@@ -1013,14 +1012,14 @@ uint8_t Server::get_current_team_turn() const {
return this->current_team_turn1;
}
shared_ptr<PlayerState> Server::get_player_state(uint8_t client_id) {
std::shared_ptr<PlayerState> Server::get_player_state(uint8_t client_id) {
if (client_id >= 4) {
return nullptr;
}
return this->player_states[client_id];
}
shared_ptr<const PlayerState> Server::get_player_state(uint8_t client_id) const {
std::shared_ptr<const PlayerState> Server::get_player_state(uint8_t client_id) const {
if (client_id >= 4) {
return nullptr;
}
@@ -1094,19 +1093,19 @@ void Server::move_phase_after() {
continue;
}
static const array<vector<uint16_t>, 5> DEFAULT_TRAP_CARD_IDS = {
static const std::array<std::vector<uint16_t>, 5> DEFAULT_TRAP_CARD_IDS = {
// Red: Dice Fever, Heavy Fog, Muscular, Immortality, Snail Pace
vector<uint16_t>{0x00F7, 0x010F, 0x012E, 0x013B, 0x013C},
std::vector<uint16_t>{0x00F7, 0x010F, 0x012E, 0x013B, 0x013C},
// Blue: Gold Rush, Charity, Requiem
vector<uint16_t>{0x0131, 0x012B, 0x0133},
std::vector<uint16_t>{0x0131, 0x012B, 0x0133},
// Purple: Powerless Rain, Trash 1, Empty Hand, Skip Draw
vector<uint16_t>{0x00FA, 0x0125, 0x0126, 0x0137},
std::vector<uint16_t>{0x00FA, 0x0125, 0x0126, 0x0137},
// Green: Brave Wind, Homesick, Fly
vector<uint16_t>{0x00FB, 0x014E, 0x0107},
std::vector<uint16_t>{0x00FB, 0x014E, 0x0107},
// Yellow: Dice+1, Battle Royale, Reverse Card, Giant Garden, Fix
vector<uint16_t>{0x00F6, 0x0242, 0x014B, 0x0145, 0x012D}};
std::vector<uint16_t>{0x00F6, 0x0242, 0x014B, 0x0145, 0x012D}};
const vector<uint16_t>* trap_card_ids = &this->options.trap_card_ids.at(trap_type);
const std::vector<uint16_t>* trap_card_ids = &this->options.trap_card_ids.at(trap_type);
if (trap_card_ids->empty()) {
trap_card_ids = &DEFAULT_TRAP_CARD_IDS.at(trap_type);
}
@@ -1201,7 +1200,7 @@ int8_t Server::send_6xB4x33_remove_ally_atk_if_needed(const ActionState& pa) {
bool has_ally_cost = false;
uint8_t ally_cost = 0;
uint8_t setter_client_id = 0xFF;
shared_ptr<PlayerState> setter_ps = nullptr;
std::shared_ptr<PlayerState> setter_ps = nullptr;
cmd.card_ref = 0xFFFF;
for (size_t z = 0; (z < 8) && (pa.action_card_refs[z] != 0xFFFF); z++) {
auto ce = this->definition_for_card_ref(pa.action_card_refs[z]);
@@ -1509,7 +1508,7 @@ void Server::setup_and_start_battle() {
this->name_entries[z].clear();
}
} else {
this->player_states[z] = make_shared<PlayerState>(z, this->shared_from_this());
this->player_states[z] = std::make_shared<PlayerState>(z, this->shared_from_this());
this->player_states[z]->init();
}
}
@@ -1523,7 +1522,7 @@ void Server::setup_and_start_battle() {
}
auto card = ps->get_sc_card();
if (card) {
team_hp[ps->get_team_id()] = min<int16_t>(team_hp[ps->get_team_id()], card->get_current_hp());
team_hp[ps->get_team_id()] = std::min<int16_t>(team_hp[ps->get_team_id()], card->get_current_hp());
}
}
@@ -1739,7 +1738,7 @@ bool Server::update_registration_phase() {
return true;
}
const unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers({
const std::unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers({
{0x0B, &Server::handle_CAx0B_redraw_initial_hand},
{0x0C, &Server::handle_CAx0C_end_redraw_initial_hand_phase},
{0x0D, &Server::handle_CAx0D_end_non_action_phase},
@@ -1765,23 +1764,23 @@ const unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers({
{0x49, &Server::handle_CAx49_card_counts},
});
void Server::on_server_data_input(shared_ptr<Client> sender_c, const string& data) {
void Server::on_server_data_input(std::shared_ptr<Client> sender_c, const std::string& data) {
auto header = check_size_t<G_CardBattleCommandHeader>(data, 0xFFFF);
size_t expected_size = header.size * 4;
if (expected_size < data.size()) {
phosg::print_data(stderr, data);
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"command is incomplete: expected {:X} bytes, received {:X} bytes", expected_size, data.size()));
}
if (header.subcommand != 0xB3) {
throw runtime_error("server data command is not 6xB3");
throw std::runtime_error("server data command is not 6xB3");
}
handler_t handler = nullptr;
try {
handler = this->subcommand_handlers.at(header.subsubcommand);
} catch (const out_of_range&) {
throw runtime_error("unknown CAx subsubcommand");
} catch (const std::out_of_range&) {
throw std::runtime_error("unknown CAx subsubcommand");
}
if (this->battle_record && this->battle_record->writable()) {
@@ -1791,17 +1790,17 @@ void Server::on_server_data_input(shared_ptr<Client> sender_c, const string& dat
if ((sender_c && (sender_c->version() == Version::GC_EP3_NTE)) || !header.mask_key) {
(this->*handler)(sender_c, data);
} else {
string unmasked_data = data;
std::string unmasked_data = data;
set_mask_for_ep3_game_command(unmasked_data.data(), unmasked_data.size(), 0);
(this->*handler)(sender_c, unmasked_data);
}
}
void Server::handle_CAx0B_redraw_initial_hand(shared_ptr<Client>, const string& data) {
void Server::handle_CAx0B_redraw_initial_hand(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_RedrawInitialHand_Ep3_CAx0B>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "REDRAW");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -1829,11 +1828,11 @@ void Server::handle_CAx0B_redraw_initial_hand(shared_ptr<Client>, const string&
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, error_code);
}
void Server::handle_CAx0C_end_redraw_initial_hand_phase(shared_ptr<Client>, const string& data) {
void Server::handle_CAx0C_end_redraw_initial_hand_phase(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EndInitialRedrawPhase_Ep3_CAx0C>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "HAND READY");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -1887,11 +1886,11 @@ void Server::handle_CAx0C_end_redraw_initial_hand_phase(shared_ptr<Client>, cons
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, error_code);
}
void Server::handle_CAx0D_end_non_action_phase(shared_ptr<Client>, const string& data) {
void Server::handle_CAx0D_end_non_action_phase(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EndNonAttackPhase_Ep3_CAx0D>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "END PHASE");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
if (!this->options.is_nte()) {
@@ -1909,11 +1908,11 @@ void Server::handle_CAx0D_end_non_action_phase(shared_ptr<Client>, const string&
this->send(out_cmd_fin);
}
void Server::handle_CAx0E_discard_card_from_hand(shared_ptr<Client>, const string& data) {
void Server::handle_CAx0E_discard_card_from_hand(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_DiscardCardFromHand_Ep3_CAx0E>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "DISCARD");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -1948,11 +1947,11 @@ void Server::handle_CAx0E_discard_card_from_hand(shared_ptr<Client>, const strin
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, error_code);
}
void Server::handle_CAx0F_set_card_from_hand(shared_ptr<Client>, const string& data) {
void Server::handle_CAx0F_set_card_from_hand(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_SetCardFromHand_Ep3_CAx0F>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "SET FC");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -1994,11 +1993,11 @@ void Server::handle_CAx0F_set_card_from_hand(shared_ptr<Client>, const string& d
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, this->ruler_server->error_code1);
}
void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string& data) {
void Server::handle_CAx10_move_fc_to_location(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_MoveFieldCharacter_Ep3_CAx10>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "MOVE");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -2034,11 +2033,11 @@ void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string&
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, this->ruler_server->error_code2);
}
void Server::handle_CAx11_enqueue_attack_or_defense(shared_ptr<Client>, const string& data) {
void Server::handle_CAx11_enqueue_attack_or_defense(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EnqueueAttackOrDefense_Ep3_CAx11>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "ENQUEUE ACT");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -2072,11 +2071,11 @@ void Server::handle_CAx11_enqueue_attack_or_defense(shared_ptr<Client>, const st
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, this->ruler_server->error_code3);
}
void Server::handle_CAx12_end_attack_list(shared_ptr<Client>, const string& data) {
void Server::handle_CAx12_end_attack_list(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EndAttackList_Ep3_CAx12>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "END ATK LIST");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
int32_t error_code = 0;
@@ -2097,7 +2096,7 @@ void Server::handle_CAx12_end_attack_list(shared_ptr<Client>, const string& data
}
template <typename CmdT>
void Server::handle_CAx13_update_map_during_setup_t(shared_ptr<Client> c, const string& data) {
void Server::handle_CAx13_update_map_during_setup_t(std::shared_ptr<Client> c, const std::string& data) {
const auto& in_cmd = check_size_t<CmdT>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "UPDATE MAP");
@@ -2157,7 +2156,7 @@ void Server::handle_CAx13_update_map_during_setup_t(shared_ptr<Client> c, const
}
}
void Server::handle_CAx13_update_map_during_setup(shared_ptr<Client> c, const string& data) {
void Server::handle_CAx13_update_map_during_setup(std::shared_ptr<Client> c, const std::string& data) {
if (this->options.is_nte()) {
this->handle_CAx13_update_map_during_setup_t<G_SetMapState_Ep3NTE_CAx13>(c, data);
} else {
@@ -2165,7 +2164,7 @@ void Server::handle_CAx13_update_map_during_setup(shared_ptr<Client> c, const st
}
}
void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const string& data) {
void Server::handle_CAx14_update_deck_during_setup(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_SetPlayerDeck_Ep3_CAx14>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "UPDATE DECK");
@@ -2188,7 +2187,7 @@ void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const str
}
}
if (verify_error) {
throw runtime_error(std::format("invalid deck: -0x{:X}", verify_error));
throw std::runtime_error(std::format("invalid deck: -0x{:X}", verify_error));
}
if (!this->options.is_nte() && !(this->options.behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
this->ruler_server->replace_D1_D2_rank_cards_with_Attack(entry.card_ids);
@@ -2211,7 +2210,7 @@ void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const str
}
}
void Server::handle_CAx15_unused_hard_reset_server_state(shared_ptr<Client>, const string& data) {
void Server::handle_CAx15_unused_hard_reset_server_state(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_HardResetServerState_Ep3_CAx15>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "HARD RESET");
@@ -2224,10 +2223,10 @@ void Server::handle_CAx15_unused_hard_reset_server_state(shared_ptr<Client>, con
// this->send_all_state_updates();
// this->update_registration_phase();
// this->setup_and_start_battle();
throw runtime_error("hard reset command received");
throw std::runtime_error("hard reset command received");
}
void Server::handle_CAx1B_update_player_name(shared_ptr<Client>, const string& data) {
void Server::handle_CAx1B_update_player_name(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_SetPlayerName_Ep3_CAx1B>(data);
this->send_debug_command_received_message(in_cmd.entry.client_id, in_cmd.header.subsubcommand, "UPDATE NAME");
@@ -2258,7 +2257,7 @@ void Server::handle_CAx1B_update_player_name(shared_ptr<Client>, const string& d
this->send(out_cmd);
}
void Server::handle_CAx1D_start_battle(shared_ptr<Client>, const string& data) {
void Server::handle_CAx1D_start_battle(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_StartBattle_Ep3_CAx1D>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "START BATTLE");
@@ -2304,7 +2303,7 @@ void Server::handle_CAx1D_start_battle(shared_ptr<Client>, const string& data) {
}
}
void Server::handle_CAx21_end_battle(shared_ptr<Client>, const string& data) {
void Server::handle_CAx21_end_battle(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EndBattle_Ep3_CAx21>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "END BATTLE");
if (this->setup_phase == SetupPhase::BATTLE_ENDED) {
@@ -2318,11 +2317,11 @@ void Server::handle_CAx21_end_battle(shared_ptr<Client>, const string& data) {
}
}
void Server::handle_CAx28_end_defense_list(shared_ptr<Client>, const string& data) {
void Server::handle_CAx28_end_defense_list(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EndDefenseList_Ep3_CAx28>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "END DEF LIST");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
G_ActionResult_Ep3_6xB4x1E out_cmd_ack;
@@ -2369,13 +2368,13 @@ void Server::handle_CAx28_end_defense_list(shared_ptr<Client>, const string& dat
this->send(out_cmd_fin);
}
void Server::handle_CAx2B_legacy_set_card(shared_ptr<Client>, const string& data) {
void Server::handle_CAx2B_legacy_set_card(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_ExecLegacyCard_Ep3_CAx2B>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "EXEC LEGACY");
// Sega's original implementation does nothing here, so we do nothing as well.
}
void Server::handle_CAx34_subtract_ally_atk_points(shared_ptr<Client>, const string& data) {
void Server::handle_CAx34_subtract_ally_atk_points(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_PhotonBlastRequest_Ep3_CAx34>(data);
uint8_t card_ref_client_id = client_id_for_card_ref(in_cmd.card_ref);
@@ -2450,11 +2449,11 @@ void Server::handle_CAx34_subtract_ally_atk_points(shared_ptr<Client>, const str
}
}
void Server::handle_CAx37_client_ready_to_advance_from_starter_roll_phase(shared_ptr<Client>, const string& data) {
void Server::handle_CAx37_client_ready_to_advance_from_starter_roll_phase(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_AdvanceFromStartingRollsPhase_Ep3_CAx37>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "CHOOSE ORDER");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
auto ps = this->player_states[in_cmd.client_id];
@@ -2479,19 +2478,19 @@ void Server::handle_CAx37_client_ready_to_advance_from_starter_roll_phase(shared
}
}
void Server::handle_CAx3A_time_limit_expired(shared_ptr<Client>, const string& data) {
void Server::handle_CAx3A_time_limit_expired(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_OverallTimeLimitExpired_Ep3_CAx3A>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "TIME EXPIRED");
// We don't need to do anything here because the overall time limit is tracked server-side instead.
}
void Server::handle_CAx40_map_list_request(shared_ptr<Client> sender_c, const string& data) {
void Server::handle_CAx40_map_list_request(std::shared_ptr<Client> sender_c, const std::string& data) {
const auto& in_cmd = check_size_t<G_MapListRequest_Ep3_CAx40>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "MAP LIST");
auto l = this->lobby.lock();
if (!l) {
throw runtime_error("lobby is deleted");
throw std::runtime_error("lobby is deleted");
}
size_t num_players = l ? l->count_clients() : 1;
@@ -2512,13 +2511,13 @@ void Server::handle_CAx40_map_list_request(shared_ptr<Client> sender_c, const st
void Server::send_6xB6x41_to_all_clients() const {
if (!this->last_chosen_map) {
throw logic_error("cannot send 6xB4x41 without a map chosen");
throw std::logic_error("cannot send 6xB4x41 without a map chosen");
}
auto l = this->lobby.lock();
if (l) {
vector<string> map_commands_by_language;
auto send_to_client = [&](shared_ptr<Client> c) -> void {
std::vector<std::string> map_commands_by_language;
auto send_to_client = [&](std::shared_ptr<Client> c) -> void {
if (!c) {
return;
}
@@ -2545,7 +2544,7 @@ void Server::send_6xB6x41_to_all_clients() const {
if (this->battle_record && this->battle_record->writable()) {
// TODO: It's not great that we just pick the first one; ideally we'd put all of them in the recording and send
// the appropriate one to the client in the playback lobby
for (string& data : map_commands_by_language) {
for (std::string& data : map_commands_by_language) {
if (!data.empty()) {
this->battle_record->add_command(BattleRecord::Event::Type::BATTLE_COMMAND, std::move(data));
break;
@@ -2559,7 +2558,7 @@ void Server::send_6xB6x41_to_all_clients() const {
}
}
void Server::handle_CAx41_map_request(shared_ptr<Client>, const string& data) {
void Server::handle_CAx41_map_request(std::shared_ptr<Client>, const std::string& data) {
const auto& cmd = check_size_t<G_MapDataRequest_Ep3_CAx41>(data);
this->send_debug_command_received_message(cmd.header.subsubcommand, "MAP DATA");
if (!this->options.tournament || (this->options.tournament->get_map()->map_number == cmd.map_number)) {
@@ -2568,11 +2567,11 @@ void Server::handle_CAx41_map_request(shared_ptr<Client>, const string& data) {
}
}
void Server::handle_CAx48_end_turn(shared_ptr<Client>, const string& data) {
void Server::handle_CAx48_end_turn(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_EndTurn_Ep3_CAx48>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "END TURN");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
throw std::runtime_error("invalid client ID");
}
auto ps = this->get_player_state(in_cmd.client_id);
@@ -2585,7 +2584,7 @@ void Server::handle_CAx48_end_turn(shared_ptr<Client>, const string& data) {
this->send(out_cmd);
}
void Server::handle_CAx49_card_counts(shared_ptr<Client>, const string& data) {
void Server::handle_CAx49_card_counts(std::shared_ptr<Client>, const std::string& data) {
const auto& in_cmd = check_size_t<G_CardCounts_Ep3_CAx49>(data);
this->send_debug_command_received_message(in_cmd.header.sender_client_id, in_cmd.header.subsubcommand, "CARD COUNTS");
@@ -2612,7 +2611,7 @@ void Server::compute_losing_team_id_and_add_winner_flags(uint32_t flags) {
uint32_t winner_flags = flags | AssistFlag::HAS_WON_BATTLE | AssistFlag::WINNER_DECIDED_BY_DEFEAT;
int8_t losing_team_id = -1;
array<uint32_t, 2> team_counts = {0, 0};
std::array<uint32_t, 2> team_counts{0, 0};
if (!is_nte) {
// First, check which team has more dead SCs
@@ -2747,8 +2746,7 @@ void Server::unknown_8023EEF4() {
ActionState as = this->pending_attacks_with_cards[this->unknown_a14];
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
log.debug_f("card @{:04X} #{:04X} can attack", card->get_card_ref(), card->get_card_id());
string as_str = as.str(this->shared_from_this());
log.debug_f("as: {}", as_str);
log.debug_f("as: {}", as.str(this->shared_from_this()));
}
if (is_nte) {
this->replace_targets_due_to_destruction_nte(&as);
@@ -2756,8 +2754,7 @@ void Server::unknown_8023EEF4() {
this->replace_targets_due_to_destruction_or_conditions(&as);
}
if (log.should_log(phosg::LogLevel::L_DEBUG)) {
string as_str = as.str(this->shared_from_this());
log.debug_f("as after target replacement: {}", as_str);
log.debug_f("as after target replacement: {}", as.str(this->shared_from_this()));
}
if (this->any_target_exists_for_attack(as)) {
log.debug_f("as is valid");
@@ -2837,8 +2834,8 @@ void Server::execute_bomb_assist_effect() {
for (size_t set_index = 0; set_index < 8; set_index++) {
auto card = ps->get_set_card(set_index);
if (card && !(card->card_flags & 2)) {
max_hp = max<int16_t>(max_hp, card->get_current_hp());
min_hp = min<int16_t>(min_hp, card->get_current_hp());
max_hp = std::max<int16_t>(max_hp, card->get_current_hp());
min_hp = std::min<int16_t>(min_hp, card->get_current_hp());
}
}
}
@@ -2875,7 +2872,7 @@ void Server::replace_targets_due_to_destruction_nte(ActionState* as) {
continue;
}
auto ps = target_card->player_state();
shared_ptr<Card> found_guard_item;
std::shared_ptr<Card> found_guard_item;
for (size_t z = 0; z < 8; z++) {
auto set_card = ps->get_set_card(z);
if (set_card && (set_card != target_card) && !(set_card->card_flags & 2) && set_card->is_guard_item()) {
@@ -2915,7 +2912,7 @@ void Server::replace_targets_due_to_destruction_or_conditions(ActionState* as) {
return;
}
vector<uint16_t> phase1_replaced_card_refs;
std::vector<uint16_t> phase1_replaced_card_refs;
for (size_t client_id = 0; client_id < 4; client_id++) {
auto ps = this->get_player_state(client_id);
if (!attacker_card->action_chain.check_flag(0x200 << client_id)) {
@@ -3005,7 +3002,7 @@ void Server::replace_targets_due_to_destruction_or_conditions(ActionState* as) {
}
// as->target_card_refs[phase1_replaced_card_refs.size()] = 0xFFFF;
vector<uint16_t> phase2_replaced_card_refs;
std::vector<uint16_t> phase2_replaced_card_refs;
for (size_t z = 0; (z < 4 * 9) && (as->target_card_refs[z] != 0xFFFF); z++) {
uint16_t target_card_ref = this->send_6xB4x06_if_card_ref_invalid(as->target_card_refs[z], 7);
auto target_card = this->card_for_set_card_ref(target_card_ref);
@@ -3105,13 +3102,14 @@ void Server::unknown_802402F4() {
}
}
vector<shared_ptr<Card>> Server::const_cast_set_cards_v(const vector<shared_ptr<const Card>>& cards) {
std::vector<std::shared_ptr<Card>> Server::const_cast_set_cards_v(
const std::vector<std::shared_ptr<const Card>>& cards) {
// TODO: This is dumb. Figure out a not-dumb way to do this.
vector<shared_ptr<Card>> ret;
std::vector<std::shared_ptr<Card>> ret;
for (auto const_card : cards) {
auto mutable_card = this->card_for_set_card_ref(const_card->get_card_ref());
if (mutable_card.get() != const_card.get()) {
throw logic_error("inconsistent set cards index");
throw std::logic_error("inconsistent set cards index");
}
ret.emplace_back(mutable_card);
}
+1 -1
View File
@@ -37,7 +37,7 @@ namespace Episode3 {
// RulerServer.hh/cc
// Server.hh/cc
// Class ownership levels (classes may contain weak_ptrs but not shared_ptrs to classes at the same or higher level):
// Class ownership levels (classes may contain weak_ptrs but not std::shared_ptrs to classes at the same or higher level):
// - Server
// - - RulerServer
// - - - AssistServer
+99 -115
View File
@@ -7,23 +7,18 @@
#include "../SendCommands.hh"
#include "../ServerState.hh"
using namespace std;
namespace Episode3 {
Tournament::PlayerEntry::PlayerEntry(uint32_t account_id, const string& player_name)
: account_id(account_id),
player_name(player_name) {}
Tournament::PlayerEntry::PlayerEntry(uint32_t account_id, const std::string& player_name)
: account_id(account_id), player_name(player_name) {}
Tournament::PlayerEntry::PlayerEntry(shared_ptr<Client> c)
Tournament::PlayerEntry::PlayerEntry(std::shared_ptr<Client> c)
: account_id(c->login->account->account_id),
client(c),
player_name(c->character_file()->disp.name.decode(c->language())) {}
Tournament::PlayerEntry::PlayerEntry(
shared_ptr<const COMDeckDefinition> com_deck)
: account_id(0),
com_deck(com_deck) {}
Tournament::PlayerEntry::PlayerEntry(std::shared_ptr<const COMDeckDefinition> com_deck)
: account_id(0), com_deck(com_deck) {}
bool Tournament::PlayerEntry::is_com() const {
return (this->com_deck != nullptr);
@@ -33,8 +28,7 @@ bool Tournament::PlayerEntry::is_human() const {
return (this->account_id != 0);
}
Tournament::Team::Team(
shared_ptr<Tournament> tournament, size_t index, size_t max_players)
Tournament::Team::Team(std::shared_ptr<Tournament> tournament, size_t index, size_t max_players)
: tournament(tournament),
index(index),
max_players(max_players),
@@ -43,7 +37,7 @@ Tournament::Team::Team(
num_rounds_cleared(0),
is_active(true) {}
string Tournament::Team::str() const {
std::string Tournament::Team::str() const {
size_t num_human_players = 0;
size_t num_com_players = 0;
for (const auto& player : this->players) {
@@ -51,7 +45,7 @@ string Tournament::Team::str() const {
num_com_players += player.is_com();
}
string ret = std::format("[Team/{} {} {}H/{}C/{}P name={} pass={} rounds={}",
std::string ret = std::format("[Team/{} {} {}H/{}C/{}P name={} pass={} rounds={}",
this->index, this->is_active ? "active" : "inactive",
num_human_players, num_com_players, this->max_players, this->name,
this->password, this->num_rounds_cleared);
@@ -67,26 +61,27 @@ string Tournament::Team::str() const {
return ret + "]";
}
void Tournament::Team::register_player(shared_ptr<Client> c, const string& team_name, const string& password) {
void Tournament::Team::register_player(
std::shared_ptr<Client> c, const std::string& team_name, const std::string& password) {
if (this->players.size() >= this->max_players) {
throw runtime_error("team is full");
throw std::runtime_error("team is full");
}
if (!this->name.empty() && (password != this->password)) {
throw runtime_error("incorrect password");
throw std::runtime_error("incorrect password");
}
auto tournament = this->tournament.lock();
if (!tournament) {
throw runtime_error("tournament has been deleted");
throw std::runtime_error("tournament has been deleted");
}
if (!tournament->all_player_account_ids.emplace(c->login->account->account_id).second) {
throw runtime_error("player already registered in same tournament");
throw std::runtime_error("player already registered in same tournament");
}
for (const auto& player : this->players) {
if (player.is_human() && (player.account_id == c->login->account->account_id)) {
throw logic_error("player already registered in team but not in tournament");
throw std::logic_error("player already registered in team but not in tournament");
}
}
@@ -125,7 +120,7 @@ bool Tournament::Team::unregister_player(uint32_t account_id) {
// Look through the pending matches to see if this team is involved in any of them
for (auto match : tournament->pending_matches) {
if (!match->preceding_a || !match->preceding_b) {
throw logic_error("zero-round match is pending after tournament registration phase");
throw std::logic_error("zero-round match is pending after tournament registration phase");
}
if (match->preceding_a->winner_team.get() == this) {
match->set_winner_team(match->preceding_b->winner_team);
@@ -139,7 +134,7 @@ bool Tournament::Team::unregister_player(uint32_t account_id) {
} else {
// If the tournament has not started yet, just remove the player from the team
if (!tournament->all_player_account_ids.erase(account_id)) {
throw logic_error("player removed from team but not from tournament");
throw std::logic_error("player removed from team but not from tournament");
}
}
@@ -176,27 +171,19 @@ size_t Tournament::Team::num_com_players() const {
}
Tournament::Match::Match(
shared_ptr<Tournament> tournament, shared_ptr<Match> preceding_a, shared_ptr<Match> preceding_b)
: tournament(tournament),
preceding_a(preceding_a),
preceding_b(preceding_b),
winner_team(nullptr),
round_num(0) {
std::shared_ptr<Tournament> tournament, std::shared_ptr<Match> preceding_a, std::shared_ptr<Match> preceding_b)
: tournament(tournament), preceding_a(preceding_a), preceding_b(preceding_b), winner_team(nullptr), round_num(0) {
if (this->preceding_a->round_num != this->preceding_b->round_num) {
throw logic_error("preceding matches have different round numbers");
throw std::logic_error("preceding matches have different round numbers");
}
this->round_num = this->preceding_a->round_num + 1;
}
Tournament::Match::Match(shared_ptr<Tournament> tournament, shared_ptr<Team> winner_team)
: tournament(tournament),
preceding_a(nullptr),
preceding_b(nullptr),
winner_team(winner_team),
round_num(0) {}
Tournament::Match::Match(std::shared_ptr<Tournament> tournament, std::shared_ptr<Team> winner_team)
: tournament(tournament), preceding_a(nullptr), preceding_b(nullptr), winner_team(winner_team), round_num(0) {}
string Tournament::Match::str() const {
string winner_str = this->winner_team ? this->winner_team->str() : "(none)";
std::string Tournament::Match::str() const {
std::string winner_str = this->winner_team ? this->winner_team->str() : "(none)";
return std::format("[Match round={} winner={}]", this->round_num, winner_str);
}
@@ -262,12 +249,12 @@ void Tournament::Match::on_winner_team_set() {
}
}
void Tournament::Match::set_winner_team_without_triggers(shared_ptr<Team> team) {
void Tournament::Match::set_winner_team_without_triggers(std::shared_ptr<Team> team) {
if (!this->preceding_a || !this->preceding_b) {
throw logic_error("set_winner_team called on zero-round match");
throw std::logic_error("set_winner_team called on zero-round match");
}
if ((team != this->preceding_a->winner_team) && (team != this->preceding_b->winner_team)) {
throw logic_error("winner team did not participate in match");
throw std::logic_error("winner team did not participate in match");
}
this->winner_team = team;
@@ -280,29 +267,29 @@ void Tournament::Match::set_winner_team_without_triggers(shared_ptr<Team> team)
}
}
void Tournament::Match::set_winner_team(shared_ptr<Team> team) {
void Tournament::Match::set_winner_team(std::shared_ptr<Team> team) {
this->set_winner_team_without_triggers(team);
this->on_winner_team_set();
}
shared_ptr<Tournament::Team> Tournament::Match::opponent_team_for_team(shared_ptr<Team> team) const {
std::shared_ptr<Tournament::Team> Tournament::Match::opponent_team_for_team(std::shared_ptr<Team> team) const {
if (!this->preceding_a || !this->preceding_b) {
throw logic_error("zero-round matches do not have opponents");
throw std::logic_error("zero-round matches do not have opponents");
}
if (team == this->preceding_a->winner_team) {
return this->preceding_b->winner_team;
} else if (team == this->preceding_b->winner_team) {
return this->preceding_a->winner_team;
} else {
throw logic_error("team is not registered for this match");
throw std::logic_error("team is not registered for this match");
}
}
Tournament::Tournament(
shared_ptr<const MapIndex> map_index,
shared_ptr<const COMDeckIndex> com_deck_index,
const string& name,
shared_ptr<const MapIndex::Map> map,
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
const std::string& name,
std::shared_ptr<const MapIndex::Map> map,
const Rules& rules,
size_t num_teams,
uint8_t flags)
@@ -317,18 +304,20 @@ Tournament::Tournament(
current_state(State::REGISTRATION),
menu_item_id(0xFFFFFFFF) {
if (this->num_teams < 4) {
throw invalid_argument("team count must be 4 or more");
throw std::invalid_argument("team count must be 4 or more");
}
if (this->num_teams > 32) {
throw invalid_argument("team count must be 32 or fewer");
throw std::invalid_argument("team count must be 32 or fewer");
}
if (this->num_teams & (this->num_teams - 1)) {
throw invalid_argument("team count must be a power of 2");
throw std::invalid_argument("team count must be a power of 2");
}
}
Tournament::Tournament(
shared_ptr<const MapIndex> map_index, shared_ptr<const COMDeckIndex> com_deck_index, const phosg::JSON& json)
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
const phosg::JSON& json)
: log(std::format("[Tournament:{}] ", json.get_string("name"))),
map_index(map_index),
com_deck_index(com_deck_index),
@@ -336,7 +325,7 @@ Tournament::Tournament(
current_state(State::REGISTRATION) {}
void Tournament::init() {
vector<size_t> team_index_to_rounds_cleared;
std::vector<size_t> team_index_to_rounds_cleared;
bool is_registration_complete;
if (!this->source_json.is_null()) {
@@ -350,7 +339,7 @@ void Tournament::init() {
is_registration_complete = this->source_json.get_bool("is_registration_complete");
for (const auto& team_json : this->source_json.get_list("teams")) {
auto& team = this->teams.emplace_back(make_shared<Team>(
auto& team = this->teams.emplace_back(std::make_shared<Team>(
this->shared_from_this(), this->teams.size(), team_json->get_int("max_players")));
team->name = team_json->get_string("name");
team->password = team_json->get_string("password");
@@ -367,7 +356,7 @@ void Tournament::init() {
} else if (player_json->is_string()) {
team->players.emplace_back(this->com_deck_index->deck_for_name(player_json->as_string()));
} else {
throw runtime_error("invalid player spec");
throw std::runtime_error("invalid player spec");
}
}
}
@@ -378,7 +367,7 @@ void Tournament::init() {
} else {
// Create empty teams
while (this->teams.size() < this->num_teams) {
auto t = make_shared<Team>(this->shared_from_this(), this->teams.size(), (this->flags & Flag::IS_2V2) ? 2 : 1);
auto t = std::make_shared<Team>(this->shared_from_this(), this->teams.size(), (this->flags & Flag::IS_2V2) ? 2 : 1);
this->teams.emplace_back(t);
}
is_registration_complete = false;
@@ -390,12 +379,12 @@ void Tournament::init() {
this->create_bracket_matches();
// Start with all zero-round matches in the match queue
unordered_set<shared_ptr<Match>> match_queue;
std::unordered_set<std::shared_ptr<Match>> match_queue;
for (auto match : this->zero_round_matches) {
match_queue.emplace(match->following.lock());
}
if (match_queue.count(nullptr)) {
throw logic_error("null match in match queue");
throw std::logic_error("null match in match queue");
}
// For each match in the queue, either resolve it from the previous state or
@@ -406,12 +395,12 @@ void Tournament::init() {
match_queue.erase(match_it);
if (!match->preceding_a->winner_team || !match->preceding_b->winner_team) {
throw logic_error("preceding matches are not resolved");
throw std::logic_error("preceding matches are not resolved");
}
size_t& a_rounds_cleared = team_index_to_rounds_cleared[match->preceding_a->winner_team->index];
size_t& b_rounds_cleared = team_index_to_rounds_cleared[match->preceding_b->winner_team->index];
if (a_rounds_cleared && b_rounds_cleared) {
throw runtime_error("both teams won the same match");
throw std::runtime_error("both teams won the same match");
}
if (!a_rounds_cleared && !b_rounds_cleared) {
this->pending_matches.emplace(match); // Neither team has won yet
@@ -434,7 +423,7 @@ void Tournament::init() {
}
if (!this->final_match->winner_team == this->pending_matches.empty()) {
throw logic_error("there must be pending matches if and only if the final match is not resolved");
throw std::logic_error("there must be pending matches if and only if the final match is not resolved");
}
// If all matches are resolved, then the tournament is complete
@@ -449,19 +438,19 @@ void Tournament::init() {
void Tournament::create_bracket_matches() {
if (this->teams.size() < 4) {
throw logic_error("tournaments must have at least 4 teams");
throw std::logic_error("tournaments must have at least 4 teams");
}
if (this->teams.size() > 32) {
throw logic_error("tournaments must have at most 32 teams");
throw std::logic_error("tournaments must have at most 32 teams");
}
if (this->teams.size() & (this->teams.size() - 1)) {
throw logic_error("tournaments team count is not a power of 2");
throw std::logic_error("tournaments team count is not a power of 2");
}
// Create the zero-round matches, and make them all pending if registration is still open
this->zero_round_matches.clear();
for (const auto& team : this->teams) {
auto m = make_shared<Match>(this->shared_from_this(), team);
auto m = std::make_shared<Match>(this->shared_from_this(), team);
this->zero_round_matches.emplace_back(m);
if (this->current_state == State::REGISTRATION) {
this->pending_matches.emplace(m);
@@ -469,11 +458,11 @@ void Tournament::create_bracket_matches() {
}
// Create the bracket matches
vector<shared_ptr<Match>> current_round_matches = this->zero_round_matches;
std::vector<std::shared_ptr<Match>> current_round_matches = this->zero_round_matches;
while (current_round_matches.size() > 1) {
vector<shared_ptr<Match>> next_round_matches;
std::vector<std::shared_ptr<Match>> next_round_matches;
for (size_t z = 0; z < current_round_matches.size(); z += 2) {
auto m = make_shared<Match>(this->shared_from_this(), current_round_matches[z], current_round_matches[z + 1]);
auto m = std::make_shared<Match>(this->shared_from_this(), current_round_matches[z], current_round_matches[z + 1]);
current_round_matches[z]->following = m;
current_round_matches[z + 1]->following = m;
next_round_matches.emplace_back(std::move(m));
@@ -516,26 +505,26 @@ phosg::JSON Tournament::json() const {
});
}
shared_ptr<Tournament::Team> Tournament::get_winner_team() const {
std::shared_ptr<Tournament::Team> Tournament::get_winner_team() const {
if (this->current_state != State::COMPLETE) {
return nullptr;
}
if (!this->final_match) {
throw logic_error("tournament is complete but final match is missing");
throw std::logic_error("tournament is complete but final match is missing");
}
if (!this->final_match->winner_team) {
throw logic_error("tournament is complete but winner is not set");
throw std::logic_error("tournament is complete but winner is not set");
}
return this->final_match->winner_team;
}
shared_ptr<Tournament::Match> Tournament::next_match_for_team(shared_ptr<Team> team) const {
std::shared_ptr<Tournament::Match> Tournament::next_match_for_team(std::shared_ptr<Team> team) const {
if (this->current_state == Tournament::State::REGISTRATION) {
return nullptr;
}
for (auto match : this->pending_matches) {
if (!match->preceding_a || !match->preceding_b) {
throw logic_error("zero-round match is pending after tournament registration phase");
throw std::logic_error("zero-round match is pending after tournament registration phase");
}
if ((team == match->preceding_a->winner_team) || (team == match->preceding_b->winner_team)) {
return match;
@@ -544,11 +533,11 @@ shared_ptr<Tournament::Match> Tournament::next_match_for_team(shared_ptr<Team> t
return nullptr;
}
shared_ptr<Tournament::Match> Tournament::get_final_match() const {
std::shared_ptr<Tournament::Match> Tournament::get_final_match() const {
return this->final_match;
}
shared_ptr<Tournament::Team> Tournament::team_for_account_id(uint32_t account_id) const {
std::shared_ptr<Tournament::Team> Tournament::team_for_account_id(uint32_t account_id) const {
if (!this->all_player_account_ids.count(account_id)) {
return nullptr;
}
@@ -561,16 +550,16 @@ shared_ptr<Tournament::Team> Tournament::team_for_account_id(uint32_t account_id
}
}
throw logic_error("account ID registered in tournament but not in any team");
throw std::logic_error("account ID registered in tournament but not in any team");
}
const set<uint32_t>& Tournament::get_all_player_account_ids() const {
const std::set<uint32_t>& Tournament::get_all_player_account_ids() const {
return this->all_player_account_ids;
}
void Tournament::start() {
if (this->current_state != State::REGISTRATION) {
throw runtime_error("tournament has already started");
throw std::runtime_error("tournament has already started");
}
bool has_com_teams = (this->flags & Flag::HAS_COM_TEAMS);
@@ -584,7 +573,7 @@ void Tournament::start() {
}
}
if (num_human_teams < (has_com_teams ? 1 : 2)) {
throw runtime_error("not enough registrants to start tournament");
throw std::runtime_error("not enough registrants to start tournament");
}
if ((this->flags & Flag::SHUFFLE_ENTRIES) && (this->flags & Flag::RESIZE_ON_START)) {
@@ -642,11 +631,11 @@ void Tournament::start() {
}
for (const auto& player : t->players) {
if (player.is_com()) {
throw logic_error("non-human player on team before tournament start");
throw std::logic_error("non-human player on team before tournament start");
}
}
if (this->com_deck_index->num_decks() < t->max_players - t->players.size()) {
throw runtime_error("not enough COM decks to complete team");
throw std::runtime_error("not enough COM decks to complete team");
}
// If we allow all-COM teams, or this is a 2v2 tournament and the team has only one human on it, add a COM
if (has_com_teams || !t->players.empty()) {
@@ -687,10 +676,10 @@ void Tournament::send_all_state_updates_on_deletion() const {
}
}
string Tournament::bracket_str() const {
string ret = std::format("Tournament \"{}\"\n", this->name);
std::string Tournament::bracket_str() const {
std::string ret = std::format("Tournament \"{}\"\n", this->name);
function<void(shared_ptr<Match>, size_t)> add_match = [&](shared_ptr<Match> m, size_t indent_level) -> void {
std::function<void(std::shared_ptr<Match>, size_t)> add_match = [&](std::shared_ptr<Match> m, size_t indent_level) -> void {
ret.append(2 * indent_level, ' ');
ret += m->str();
if (this->pending_matches.count(m)) {
@@ -707,13 +696,12 @@ string Tournament::bracket_str() const {
auto en_vm = this->map->version(Language::ENGLISH);
if (en_vm) {
string map_name = en_vm->map->name.decode(en_vm->language);
std::string map_name = en_vm->map->name.decode(en_vm->language);
ret += std::format(" Map: {:08X} ({})\n", this->map->map_number, map_name);
} else {
ret += std::format(" Map: {:08X}\n", this->map->map_number);
}
string rules_str = this->rules.str();
ret += std::format(" Rules: {}\n", rules_str);
ret += std::format(" Rules: {}\n", this->rules.str());
ret += std::format(" Structure: {}, {} entries\n", (this->flags & Flag::IS_2V2) ? "2v2" : "1v1", this->num_teams);
ret += std::format(" COM teams: {}\n", (this->flags & Flag::HAS_COM_TEAMS) ? "allowed" : "forbidden");
ret += std::format(" Shuffle entries: {}\n", (this->flags & Flag::SHUFFLE_ENTRIES) ? "yes" : "no");
@@ -739,14 +727,12 @@ string Tournament::bracket_str() const {
if (this->current_state == State::REGISTRATION) {
ret += " Teams:\n";
for (const auto& team : this->teams) {
string team_str = team->str();
ret += std::format(" {}\n", team_str);
ret += std::format(" {}\n", team->str());
}
} else {
ret += " Pending matches:\n";
for (const auto& match : this->pending_matches) {
string match_str = match->str();
ret += std::format(" {}\n", match_str);
ret += std::format(" {}\n", match->str());
}
}
@@ -755,13 +741,11 @@ string Tournament::bracket_str() const {
}
TournamentIndex::TournamentIndex(
shared_ptr<const MapIndex> map_index,
shared_ptr<const COMDeckIndex> com_deck_index,
const string& state_filename,
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
const std::string& state_filename,
bool skip_load_state)
: map_index(map_index),
com_deck_index(com_deck_index),
state_filename(state_filename) {
: map_index(map_index), com_deck_index(com_deck_index), state_filename(state_filename) {
if (this->state_filename.empty() || skip_load_state) {
return;
}
@@ -775,14 +759,14 @@ TournamentIndex::TournamentIndex(
if (json.is_list()) {
if (json.size() > 0x20) {
throw runtime_error("tournament phosg::JSON list length is incorrect");
throw std::runtime_error("tournament phosg::JSON list length is incorrect");
}
for (size_t z = 0; z < min<size_t>(json.size(), 0x20); z++) {
for (size_t z = 0; z < std::min<size_t>(json.size(), 0x20); z++) {
if (!json.at(z).is_null()) {
auto tourn = make_shared<Tournament>(this->map_index, this->com_deck_index, json.at(z));
auto tourn = std::make_shared<Tournament>(this->map_index, this->com_deck_index, json.at(z));
tourn->init();
if (!this->name_to_tournament.emplace(tourn->get_name(), tourn).second) {
throw runtime_error("multiple tournaments have the same name: " + tourn->get_name());
throw std::runtime_error("multiple tournaments have the same name: " + tourn->get_name());
}
tourn->set_menu_item_id(this->menu_item_id_to_tournament.size());
this->menu_item_id_to_tournament.emplace_back(tourn);
@@ -790,20 +774,20 @@ TournamentIndex::TournamentIndex(
}
} else if (json.is_dict()) {
if (json.size() > 0x20) {
throw runtime_error("tournament phosg::JSON dict length is incorrect");
throw std::runtime_error("tournament phosg::JSON dict length is incorrect");
}
for (const auto& it : json.as_dict()) {
auto tourn = make_shared<Tournament>(this->map_index, this->com_deck_index, *it.second);
auto tourn = std::make_shared<Tournament>(this->map_index, this->com_deck_index, *it.second);
tourn->init();
if (!this->name_to_tournament.emplace(tourn->get_name(), tourn).second) {
// This is logic_error instead of runtime_error because phosg::JSON dicts already have unique keys
throw logic_error("multiple tournaments have the same name: " + tourn->get_name());
throw std::logic_error("multiple tournaments have the same name: " + tourn->get_name());
}
tourn->set_menu_item_id(this->menu_item_id_to_tournament.size());
this->menu_item_id_to_tournament.emplace_back(tourn);
}
} else {
throw runtime_error("tournament state root phosg::JSON is not a list or dict");
throw std::runtime_error("tournament state root phosg::JSON is not a list or dict");
}
}
@@ -819,20 +803,20 @@ void TournamentIndex::save() const {
phosg::save_file(this->state_filename, json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::HEX_INTEGERS | phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
}
shared_ptr<Tournament> TournamentIndex::create_tournament(
const string& name,
shared_ptr<const MapIndex::Map> map,
std::shared_ptr<Tournament> TournamentIndex::create_tournament(
const std::string& name,
std::shared_ptr<const MapIndex::Map> map,
const Rules& rules,
size_t num_teams,
uint8_t flags) {
if (this->name_to_tournament.size() >= 0x20) {
throw runtime_error("there can be at most 32 tournaments at a time");
throw std::runtime_error("there can be at most 32 tournaments at a time");
}
auto t = make_shared<Tournament>(this->map_index, this->com_deck_index, name, map, rules, num_teams, flags);
auto t = std::make_shared<Tournament>(this->map_index, this->com_deck_index, name, map, rules, num_teams, flags);
t->init();
if (!this->name_to_tournament.emplace(t->get_name(), t).second) {
throw runtime_error("a tournament with the same name already exists");
throw std::runtime_error("a tournament with the same name already exists");
}
size_t z;
@@ -852,7 +836,7 @@ shared_ptr<Tournament> TournamentIndex::create_tournament(
return t;
}
bool TournamentIndex::delete_tournament(const string& name) {
bool TournamentIndex::delete_tournament(const std::string& name) {
auto it = this->name_to_tournament.find(name);
if (it == this->name_to_tournament.end()) {
return false;
@@ -869,7 +853,7 @@ bool TournamentIndex::delete_tournament(const string& name) {
return true;
}
shared_ptr<Tournament::Team> TournamentIndex::team_for_account_id(uint32_t account_id) const {
std::shared_ptr<Tournament::Team> TournamentIndex::team_for_account_id(uint32_t account_id) const {
for (const auto& it : this->name_to_tournament) {
const auto& tourn = it.second;
auto team = tourn->team_for_account_id(account_id);
@@ -880,7 +864,7 @@ shared_ptr<Tournament::Team> TournamentIndex::team_for_account_id(uint32_t accou
return nullptr;
}
void TournamentIndex::link_client(shared_ptr<Client> c) {
void TournamentIndex::link_client(std::shared_ptr<Client> c) {
if (!is_ep3(c->version())) {
return;
}
@@ -898,7 +882,7 @@ void TournamentIndex::link_client(shared_ptr<Client> c) {
return;
}
}
throw logic_error("tournament team found for player, but player not found on team");
throw std::logic_error("tournament team found for player, but player not found on team");
} else {
c->ep3_tournament_team.reset();
if (c->version() == Version::GC_EP3) {
-89
View File
@@ -1,89 +0,0 @@
#include "FileContentsCache.hh"
#include <unistd.h>
#include <phosg/Filesystem.hh>
#include <phosg/Time.hh>
using namespace std;
FileContentsCache::FileContentsCache(uint64_t ttl_usecs) : ttl_usecs(ttl_usecs) {}
FileContentsCache::File::File(
const string& name,
string&& data,
uint64_t load_time)
: name(name),
data(make_shared<string>(std::move(data))),
load_time(load_time) {}
shared_ptr<const FileContentsCache::File> FileContentsCache::replace(
const string& name, string&& data, uint64_t t) {
if (t == 0) {
t = phosg::now();
}
auto new_file = make_shared<File>(name, std::move(data), t);
auto emplace_ret = this->name_to_file.emplace(name, new_file);
if (!emplace_ret.second) {
emplace_ret.first->second = new_file;
}
return new_file;
}
shared_ptr<const FileContentsCache::File> FileContentsCache::replace(
const string& name, const void* data, size_t size, uint64_t t) {
string s(reinterpret_cast<const char*>(data), size);
return this->replace(name, std::move(s), t);
}
FileContentsCache::GetResult FileContentsCache::get_or_load(const std::string& name) {
return this->get(name, phosg::load_file);
}
FileContentsCache::GetResult FileContentsCache::get_or_load(const char* name) {
return this->get_or_load(string(name));
}
shared_ptr<const FileContentsCache::File> FileContentsCache::get_or_throw(const std::string& name) {
auto throw_fn = +[](const std::string&) -> string {
throw out_of_range("file missing from cache");
};
return this->get(name, throw_fn).file;
}
shared_ptr<const FileContentsCache::File> FileContentsCache::get_or_throw(const char* name) {
return this->get_or_throw(string(name));
}
FileContentsCache::GetResult FileContentsCache::get(const std::string& name,
std::function<std::string(const std::string&)> generate) {
uint64_t t = phosg::now();
try {
auto& entry = this->name_to_file.at(name);
if (this->ttl_usecs && (t - entry->load_time < this->ttl_usecs)) {
return {entry, false};
}
} catch (const out_of_range& e) {
}
return {this->replace(name, generate(name)), true};
}
FileContentsCache::GetResult FileContentsCache::get(const char* name,
std::function<std::string(const std::string&)> generate) {
return this->get(string(name), generate);
}
shared_ptr<const string> ThreadSafeFileCache::get(
const string& name, std::function<shared_ptr<const string>(const std::string&)> generate) {
try {
shared_lock g(this->lock);
return this->name_to_file.at(name);
} catch (const out_of_range&) {
unique_lock g(this->lock);
auto it = this->name_to_file.find(name);
if (it == this->name_to_file.end()) {
it = this->name_to_file.emplace(name, generate(name)).first;
}
return it->second;
}
}
-124
View File
@@ -1,124 +0,0 @@
#pragma once
#include <functional>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <phosg/Time.hh>
class FileContentsCache {
public:
struct File {
std::string name;
std::shared_ptr<const std::string> data;
uint64_t load_time;
File() = delete;
File(const std::string& name, std::string&& contents, uint64_t load_time);
File(const File&) = delete;
File(File&&) = delete;
File& operator=(const File&) = delete;
File& operator=(File&&) = delete;
~File() = default;
};
explicit FileContentsCache(uint64_t ttl_usecs);
FileContentsCache(const FileContentsCache&) = delete;
FileContentsCache(FileContentsCache&&) = delete;
FileContentsCache& operator=(const FileContentsCache&) = delete;
FileContentsCache& operator=(FileContentsCache&&) = delete;
~FileContentsCache() = default;
template <typename NameT>
bool delete_key(NameT key) {
return this->name_to_file.erase(key);
}
std::shared_ptr<const File> replace(const std::string& name, std::string&& data, uint64_t t = 0);
std::shared_ptr<const File> replace(const std::string& name, const void* data, size_t size, uint64_t t = 0);
struct GetResult {
std::shared_ptr<const File> file;
bool generate_called;
};
GetResult get_or_load(const std::string& name);
GetResult get_or_load(const char* name);
std::shared_ptr<const File> get_or_throw(const std::string& name);
std::shared_ptr<const File> get_or_throw(const char* name);
GetResult get(const std::string& name, std::function<std::string(const std::string&)> generate);
GetResult get(const char* name, std::function<std::string(const std::string&)> generate);
template <typename T>
struct GetObjResult {
const T& obj;
std::shared_ptr<const File> data;
bool generate_called;
};
template <typename T, typename NameT>
GetObjResult<T> get_obj_or_load(NameT name) {
auto res = this->get_or_load(name);
if (res.file->data->size() != sizeof(T)) {
throw std::runtime_error("cached string size is incorrect");
}
return {*reinterpret_cast<const T*>(res.file->data->data()), res.file, res.generate_called};
}
template <typename T, typename NameT>
GetObjResult<T> get_obj_or_throw(NameT name) {
auto res = this->get_or_throw(name);
if (res.file->data->size() != sizeof(T)) {
throw std::runtime_error("cached string size is incorrect");
}
return {*reinterpret_cast<const T*>(res.file->data->data()), res.file, res.generate_called};
}
template <typename T, typename NameT>
GetObjResult<T> get_obj(NameT name, std::function<T(const std::string&)> generate) {
uint64_t t = phosg::now();
try {
auto& f = this->name_to_file.at(name);
if (f->data->size() != sizeof(T)) {
throw std::runtime_error("cached string size is incorrect");
}
if (this->ttl_usecs && (t - f->load_time < this->ttl_usecs)) {
return {*reinterpret_cast<const T*>(f->data->data()), f, false};
}
} catch (const std::out_of_range& e) {
}
T value = generate(name);
auto ret = this->replace_obj(name, value);
ret.generate_called = true;
return ret;
}
template <typename T, typename NameT>
GetObjResult<T> replace_obj(NameT name, const T& value) {
auto cached_value = this->replace(name, &value, sizeof(value));
return {*reinterpret_cast<const T*>(cached_value->data->data()), cached_value, false};
}
private:
std::unordered_map<std::string, std::shared_ptr<File>> name_to_file;
uint64_t ttl_usecs;
};
class ThreadSafeFileCache {
public:
explicit ThreadSafeFileCache() = default;
ThreadSafeFileCache(const ThreadSafeFileCache&) = delete;
ThreadSafeFileCache(ThreadSafeFileCache&&) = delete;
ThreadSafeFileCache& operator=(const ThreadSafeFileCache&) = delete;
ThreadSafeFileCache& operator=(ThreadSafeFileCache&&) = delete;
~ThreadSafeFileCache() = default;
// generate() is called while the lock is held for writing, so it will block other threads.
std::shared_ptr<const std::string> get(
const std::string& name, std::function<std::shared_ptr<const std::string>(const std::string&)> generate);
private:
std::shared_mutex lock;
std::unordered_map<std::string, std::shared_ptr<const std::string>> name_to_file;
};
+15 -17
View File
@@ -7,8 +7,6 @@
#include "Text.hh"
#include "Types.hh"
using namespace std;
template <bool BE>
struct GSLHeaderEntryT {
pstring<TextEncoding::ASCII, 0x20> filename;
@@ -30,13 +28,13 @@ void GSLArchive::load_t() {
}
uint64_t offset = static_cast<uint64_t>(entry.offset) * 0x800;
if (offset + entry.size > this->data->size()) {
throw runtime_error("GSL entry extends beyond end of data");
throw std::runtime_error("GSL entry extends beyond end of data");
}
this->entries.emplace(entry.filename.decode(), Entry{offset, entry.size});
}
}
GSLArchive::GSLArchive(shared_ptr<const string> data, bool big_endian) : data(data) {
GSLArchive::GSLArchive(std::shared_ptr<const std::string> data, bool big_endian) : data(data) {
if (big_endian) {
this->load_t<true>();
} else {
@@ -44,43 +42,43 @@ GSLArchive::GSLArchive(shared_ptr<const string> data, bool big_endian) : data(da
}
}
const unordered_map<string, GSLArchive::Entry> GSLArchive::all_entries() const {
const std::unordered_map<std::string, GSLArchive::Entry> GSLArchive::all_entries() const {
return this->entries;
}
pair<const void*, size_t> GSLArchive::get(const std::string& name) const {
std::pair<const void*, size_t> GSLArchive::get(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return make_pair(this->data->data() + entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("GSL does not contain file: " + name);
return std::make_pair(this->data->data() + entry.offset, entry.size);
} catch (const std::out_of_range&) {
throw std::out_of_range("GSL does not contain file: " + name);
}
}
string GSLArchive::get_copy(const string& name) const {
std::string GSLArchive::get_copy(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return this->data->substr(entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("GSL does not contain file: " + name);
} catch (const std::out_of_range&) {
throw std::out_of_range("GSL does not contain file: " + name);
}
}
phosg::StringReader GSLArchive::get_reader(const string& name) const {
phosg::StringReader GSLArchive::get_reader(const std::string& name) const {
try {
const auto& entry = this->entries.at(name);
return phosg::StringReader(this->data->data() + entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("GSL does not contain file: " + name);
} catch (const std::out_of_range&) {
throw std::out_of_range("GSL does not contain file: " + name);
}
}
string GSLArchive::generate(const unordered_map<string, string>& files, bool big_endian) {
std::string GSLArchive::generate(const std::unordered_map<std::string, std::string>& files, bool big_endian) {
return big_endian ? GSLArchive::generate_t<true>(files) : GSLArchive::generate_t<false>(files);
}
template <bool BE>
string GSLArchive::generate_t(const unordered_map<string, string>& files) {
std::string GSLArchive::generate_t(const std::unordered_map<std::string, std::string>& files) {
phosg::StringWriter w;
// Make sure there's enough space for a blank header entry before any file's data pages begin
+24 -26
View File
@@ -18,14 +18,11 @@
#include "PSOProtocol.hh"
#include "ReceiveCommands.hh"
using namespace std;
using namespace std::placeholders;
GameServer::GameServer(shared_ptr<ServerState> state) : Server(state->io_context, "[GameServer] "), state(state) {}
GameServer::GameServer(std::shared_ptr<ServerState> state) : Server(state->io_context, "[GameServer] "), state(state) {}
void GameServer::listen(
const std::string& name,
const string& addr,
const std::string& addr,
uint16_t port,
Version version,
ServerBehavior behavior) {
@@ -34,7 +31,7 @@ void GameServer::listen(
}
asio::ip::address asio_addr = addr.empty() ? asio::ip::address_v4::any() : asio::ip::make_address(addr);
auto sock = make_shared<GameServerSocket>();
auto sock = std::make_shared<GameServerSocket>();
sock->name = name;
sock->endpoint = asio::ip::tcp::endpoint(asio_addr, port);
sock->version = version;
@@ -42,8 +39,8 @@ void GameServer::listen(
this->add_socket(std::move(sock));
}
shared_ptr<Client> GameServer::connect_channel(shared_ptr<Channel> ch, uint16_t port, ServerBehavior initial_state) {
auto c = make_shared<Client>(this->shared_from_this(), ch, initial_state);
std::shared_ptr<Client> GameServer::connect_channel(std::shared_ptr<Channel> ch, uint16_t port, ServerBehavior initial_state) {
auto c = std::make_shared<Client>(this->shared_from_this(), ch, initial_state);
this->log.info_f("Client connected: C-{:X} via TSI-{}-{}-{}",
c->id, port, phosg::name_for_enum(ch->version), phosg::name_for_enum(initial_state));
@@ -52,31 +49,31 @@ shared_ptr<Client> GameServer::connect_channel(shared_ptr<Channel> ch, uint16_t
return c;
}
shared_ptr<Client> GameServer::get_client() const {
std::shared_ptr<Client> GameServer::get_client() const {
if (this->clients.empty()) {
throw runtime_error("no clients on game server");
throw std::runtime_error("no clients on game server");
}
if (this->clients.size() > 1) {
throw runtime_error("multiple clients on game server");
throw std::runtime_error("multiple clients on game server");
}
return *this->clients.begin();
}
vector<shared_ptr<Client>> GameServer::get_clients_by_identifier(const string& ident) const {
std::vector<std::shared_ptr<Client>> GameServer::get_clients_by_identifier(const std::string& ident) const {
int64_t account_id_hex = -1;
int64_t account_id_dec = -1;
try {
account_id_dec = stoul(ident, nullptr, 10);
} catch (const invalid_argument&) {
} catch (const std::invalid_argument&) {
}
try {
account_id_hex = stoul(ident, nullptr, 16);
} catch (const invalid_argument&) {
} catch (const std::invalid_argument&) {
}
// TODO: It's kind of not great that we do a linear search here, but this is only used in the shell, so it should be
// pretty rare.
vector<shared_ptr<Client>> results;
std::vector<std::shared_ptr<Client>> results;
for (const auto& c : this->clients) {
if (c->login && c->login->account->account_id == account_id_hex) {
results.emplace_back(c);
@@ -114,8 +111,8 @@ vector<shared_ptr<Client>> GameServer::get_clients_by_identifier(const string& i
return results;
}
shared_ptr<Client> GameServer::create_client(
shared_ptr<GameServerSocket> listen_sock, asio::ip::tcp::socket&& client_sock) {
std::shared_ptr<Client> GameServer::create_client(
std::shared_ptr<GameServerSocket> listen_sock, asio::ip::tcp::socket&& client_sock) {
uint32_t addr = ipv4_addr_for_asio_addr(client_sock.remote_endpoint().address());
if (this->state->banned_ipv4_ranges->check(addr)) {
if (client_sock.is_open()) {
@@ -126,7 +123,7 @@ shared_ptr<Client> GameServer::create_client(
auto channel = SocketChannel::create(
this->io_context,
make_unique<asio::ip::tcp::socket>(std::move(client_sock)),
std::make_unique<asio::ip::tcp::socket>(std::move(client_sock)),
listen_sock->version,
Language::ENGLISH,
"",
@@ -134,27 +131,28 @@ shared_ptr<Client> GameServer::create_client(
phosg::TerminalFormat::FG_GREEN,
this->state->censor_credentials,
false);
auto c = make_shared<Client>(this->shared_from_this(), channel, listen_sock->behavior);
auto c = std::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);
return c;
}
asio::awaitable<void> GameServer::handle_client_command(shared_ptr<Client> c, unique_ptr<Channel::Message> msg) {
asio::awaitable<void> GameServer::handle_client_command(
std::shared_ptr<Client> c, std::unique_ptr<Channel::Message> msg) {
try {
co_await on_command(c, std::move(msg));
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.warning_f("Error processing client command: {}", e.what());
c->channel->disconnect();
}
}
asio::awaitable<void> GameServer::handle_client(shared_ptr<Client> c) {
asio::awaitable<void> GameServer::handle_client(std::shared_ptr<Client> c) {
auto g = phosg::on_close_scope(std::bind(&Client::cancel_pending_promises, c.get()));
try {
co_await on_connect(c);
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.warning_f("Error in client initialization: {}", e.what());
c->channel->disconnect();
}
@@ -181,17 +179,17 @@ asio::awaitable<void> GameServer::destroy_client(std::shared_ptr<Client> c) {
try {
co_await on_disconnect(c);
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.warning_f("Error during client disconnect cleanup: {}", e.what());
}
// Note: It's important to move the disconnect hooks out of the client here because the hooks could modify
// c->disconnect_hooks while it's being iterated here, which would invalidate these iterators.
unordered_map<string, function<void()>> hooks = std::move(c->disconnect_hooks);
std::unordered_map<std::string, std::function<void()>> hooks = std::move(c->disconnect_hooks);
for (auto h_it : hooks) {
try {
h_it.second();
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.warning_f("Disconnect hook {} failed: {}", h_it.first, e.what());
}
}
+56 -56
View File
@@ -14,9 +14,7 @@
#include "Server.hh"
#include "ShellCommands.hh"
using namespace std;
HTTPServer::HTTPServer(shared_ptr<ServerState> state)
HTTPServer::HTTPServer(std::shared_ptr<ServerState> state)
: AsyncHTTPServer(state->io_context, "[HTTPServer] "), state(state) {
using RouterRetT = std::variant<RawResponse, std::shared_ptr<const phosg::JSON>>;
using RetT = asio::awaitable<RouterRetT>;
@@ -32,15 +30,15 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
};
this->router.add(HTTPRequest::Method::GET, "/", [generate_server_version_json](ArgsT&&) -> RetT {
co_return make_shared<phosg::JSON>(generate_server_version_json());
co_return std::make_shared<phosg::JSON>(generate_server_version_json());
});
this->router.add(HTTPRequest::Method::POST, "/y/shell-exec", [this](ArgsT&& args) -> RetT {
auto command = args.post_data.get_string("command");
try {
auto dispatch_res = co_await ShellCommand::dispatch_str(this->state, command);
co_return make_shared<phosg::JSON>(phosg::JSON::dict({{"result", phosg::join(dispatch_res, "\n")}}));
} catch (const exception& e) {
co_return std::make_shared<phosg::JSON>(phosg::JSON::dict({{"result", phosg::join(dispatch_res, "\n")}}));
} catch (const std::exception& e) {
throw HTTPError(400, e.what());
}
});
@@ -55,7 +53,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
});
this->router.add(HTTPRequest::Method::GET, "/y/clients", [this](ArgsT&&) -> RetT {
auto res = make_shared<phosg::JSON>(phosg::JSON::list());
auto res = std::make_shared<phosg::JSON>(phosg::JSON::list());
for (const auto& c : this->state->game_server->all_clients()) {
auto item_name_index = this->state->item_name_index_opt(c->version());
@@ -215,7 +213,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
uint8_t minute = p->challenge_records.grave_time & 0xFF;
client_json.emplace("ChallengeGraveTime", std::format("{:04}-{:02}-{:02} {:02}:{:02}:00", year, month, day, hour, minute));
}
string grave_enemy_types;
std::string grave_enemy_types;
if (p->challenge_records.grave_defeated_by_enemy_rt_index) {
for (EnemyType type : enemy_types_for_rare_table_index(
p->challenge_records.grave_is_ep2 ? Episode::EP2 : Episode::EP1,
@@ -297,7 +295,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
});
this->router.add(HTTPRequest::Method::GET, "/y/lobbies", [this](ArgsT&&) -> RetT {
auto res = make_shared<phosg::JSON>(phosg::JSON::list());
auto res = std::make_shared<phosg::JSON>(phosg::JSON::list());
for (const auto& [_, l] : this->state->id_to_lobby) {
auto leader = l->clients[l->leader_id];
Version v = leader ? leader->version() : Version::BB_V4;
@@ -430,7 +428,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
name = ce->def.jp_short_name.decode();
}
cards_json.emplace_back(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
cards_json.emplace_back(deck_entry->card_ids[w].load());
}
}
@@ -505,7 +503,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
});
this->router.add(HTTPRequest::Method::GET, "/y/accounts", [this](ArgsT&&) -> RetT {
auto res = make_shared<phosg::JSON>(phosg::JSON::list());
auto res = std::make_shared<phosg::JSON>(phosg::JSON::list());
for (const auto& it : this->state->account_index->all()) {
res->emplace_back(it->json());
}
@@ -515,14 +513,14 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
this->router.add(HTTPRequest::Method::GET, "/y/account/:account_id", [this](ArgsT&& args) -> RetT {
uint32_t account_id = args.get_param<uint32_t>("account_id");
try {
co_return make_shared<phosg::JSON>(this->state->account_index->from_account_id(account_id)->json());
co_return std::make_shared<phosg::JSON>(this->state->account_index->from_account_id(account_id)->json());
} catch (const AccountIndex::missing_account&) {
throw HTTPError(404, "Account does not exist");
}
});
this->router.add(HTTPRequest::Method::GET, "/y/teams", [this](ArgsT&&) -> RetT {
auto res = make_shared<phosg::JSON>(phosg::JSON::dict());
auto res = std::make_shared<phosg::JSON>(phosg::JSON::dict());
for (const auto& it : this->state->team_index->all()) {
res->emplace(std::format("{}", it->team_id), it->json());
}
@@ -535,7 +533,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
if (!team) {
throw HTTPError(404, "Team does not exist");
}
co_return make_shared<phosg::JSON>(team->json());
co_return std::make_shared<phosg::JSON>(team->json());
});
this->router.add(HTTPRequest::Method::GET, "/y/team/:team_id/flag", [this](ArgsT&& args) -> RetT {
@@ -576,7 +574,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
};
this->router.add(HTTPRequest::Method::GET, "/y/server", [generate_server_info_json](ArgsT&&) -> RetT {
co_return make_shared<phosg::JSON>(generate_server_info_json());
co_return std::make_shared<phosg::JSON>(generate_server_info_json());
});
this->router.add(HTTPRequest::Method::GET, "/y/config", [this](ArgsT&&) -> RetT {
@@ -638,7 +636,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
}
}
co_return make_shared<phosg::JSON>(phosg::JSON::dict({
co_return std::make_shared<phosg::JSON>(phosg::JSON::dict({
{"Clients", std::move(clients_json)},
{"Games", std::move(games_json)},
{"Server", generate_server_info_json()},
@@ -647,8 +645,8 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
this->router.add(HTTPRequest::Method::GET, "/y/data/ep3/cards", [this](ArgsT&& args) -> RetT {
auto& index = args.req.query_params.count("trial") ? this->state->ep3_card_index_trial : this->state->ep3_card_index;
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> shared_ptr<phosg::JSON> {
return make_shared<phosg::JSON>(index->definitions_json());
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::shared_ptr<phosg::JSON> {
return std::make_shared<phosg::JSON>(index->definitions_json());
});
});
@@ -656,15 +654,15 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
auto& index = args.req.query_params.count("trial") ? this->state->ep3_card_index_trial : this->state->ep3_card_index;
uint32_t card_id = args.get_param<uint32_t>("card_id");
try {
co_return make_shared<phosg::JSON>(index->definition_for_id(card_id)->def.json());
co_return std::make_shared<phosg::JSON>(index->definition_for_id(card_id)->def.json());
} catch (const std::out_of_range&) {
throw HTTPError(404, "Card definition does not exist");
}
});
this->router.add(HTTPRequest::Method::GET, "/y/data/ep3/maps", [this](ArgsT&&) -> RetT {
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> shared_ptr<phosg::JSON> {
auto ret = make_shared<phosg::JSON>(phosg::JSON::dict());
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::shared_ptr<phosg::JSON> {
auto ret = std::make_shared<phosg::JSON>(phosg::JSON::dict());
for (const auto& [map_number, map] : this->state->ep3_map_index->all_maps()) {
auto languages_json = phosg::JSON::list();
for (const auto& vm : map->all_versions()) {
@@ -684,11 +682,11 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
});
this->router.add(HTTPRequest::Method::GET, "/y/data/ep3/map/:map_number/:language", [this](ArgsT&& args) -> RetT {
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> shared_ptr<phosg::JSON> {
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::shared_ptr<phosg::JSON> {
try {
auto map = this->state->ep3_map_index->map_for_id(args.get_param<uint32_t>("map_number", true));
auto vm = map->version(language_for_name(args.params.at("language")));
return make_shared<phosg::JSON>(vm->map->json(vm->language));
return std::make_shared<phosg::JSON>(vm->map->json(vm->language));
} catch (const std::out_of_range&) {
throw HTTPError(404, "Map version does not exist");
}
@@ -700,7 +698,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
try {
auto map = this->state->ep3_map_index->map_for_id(args.get_param<uint32_t>("map_number"));
auto vm = map->version(language_for_name(args.params.at("language")));
string data(reinterpret_cast<const char*>(vm->map.get()), sizeof(Episode3::MapDefinition));
std::string data(reinterpret_cast<const char*>(vm->map.get()), sizeof(Episode3::MapDefinition));
return RawResponse{.content_type = "application/octet-stream", .data = std::move(data)};
} catch (const std::out_of_range&) {
throw HTTPError(404, "Map version does not exist");
@@ -709,7 +707,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
});
this->router.add(HTTPRequest::Method::GET, "/y/data/common-tables", [this](ArgsT&&) -> RetT {
auto ret = make_shared<phosg::JSON>(phosg::JSON::list());
auto ret = std::make_shared<phosg::JSON>(phosg::JSON::list());
for (const auto& it : this->state->common_item_sets) {
ret->emplace_back(it.first);
}
@@ -719,16 +717,16 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
this->router.add(HTTPRequest::Method::GET, "/y/data/common-table/:table_name", [this](ArgsT&& args) -> RetT {
try {
const auto& table = this->state->common_item_sets.at(args.params.at("table_name"));
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> shared_ptr<phosg::JSON> {
return make_shared<phosg::JSON>(table->json());
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::shared_ptr<phosg::JSON> {
return std::make_shared<phosg::JSON>(table->json());
});
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw HTTPError(404, "Table does not exist");
}
});
this->router.add(HTTPRequest::Method::GET, "/y/data/rare-tables", [this](ArgsT&&) -> RetT {
auto ret = make_shared<phosg::JSON>(phosg::JSON::list());
auto ret = std::make_shared<phosg::JSON>(phosg::JSON::list());
for (const auto& it : this->state->rare_item_sets) {
ret->emplace_back(it.first);
}
@@ -739,7 +737,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
try {
const auto& table_name = args.params.at("table_name");
const auto& table = this->state->rare_item_sets.at(table_name);
shared_ptr<const ItemNameIndex> name_index;
std::shared_ptr<const ItemNameIndex> name_index;
if (table_name.ends_with("-v1")) {
name_index = this->state->item_name_index_opt(Version::DC_V1);
} else if (table_name.ends_with("-v2")) {
@@ -749,17 +747,17 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
} else if (table_name.ends_with("-v4")) {
name_index = this->state->item_name_index_opt(Version::BB_V4);
}
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> shared_ptr<phosg::JSON> {
return make_shared<phosg::JSON>(table->json(name_index));
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::shared_ptr<phosg::JSON> {
return std::make_shared<phosg::JSON>(table->json(name_index));
});
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
throw HTTPError(404, "Table does not exist");
}
});
this->router.add(HTTPRequest::Method::GET, "/y/data/quests", [this](ArgsT&&) -> RetT {
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> shared_ptr<phosg::JSON> {
return make_shared<phosg::JSON>(this->state->quest_index->json());
co_return co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::shared_ptr<phosg::JSON> {
return std::make_shared<phosg::JSON>(this->state->quest_index->json());
});
});
@@ -769,21 +767,21 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
if (!q) {
throw HTTPError(404, "Quest does not exist");
}
co_return make_shared<phosg::JSON>(q->json());
co_return std::make_shared<phosg::JSON>(q->json());
});
}
asio::awaitable<void> HTTPServer::send_rare_drop_notification(shared_ptr<const phosg::JSON> message) {
asio::awaitable<void> HTTPServer::send_rare_drop_notification(std::shared_ptr<const phosg::JSON> message) {
if (!this->rare_drop_subscribers.empty()) {
string data = message->serialize();
std::string data = message->serialize();
// Make a copy of the rare drop subscribers set, so we can guarantee that the client objects are all valid until
// this coroutine returns
unordered_set<shared_ptr<HTTPClient>> subscribers = this->rare_drop_subscribers;
std::unordered_set<std::shared_ptr<HTTPClient>> subscribers = this->rare_drop_subscribers;
size_t expected_results = subscribers.size();
AsyncPromise<void> complete_promise;
auto fn = [this, &data, &expected_results, &complete_promise](shared_ptr<HTTPClient> c) -> asio::awaitable<void> {
auto fn = [this, &data, &expected_results, &complete_promise](std::shared_ptr<HTTPClient> c) -> asio::awaitable<void> {
try {
co_await c->send_websocket_message(data);
} catch (const std::exception& e) {
@@ -803,14 +801,14 @@ asio::awaitable<void> HTTPServer::send_rare_drop_notification(shared_ptr<const p
co_return;
}
asio::awaitable<std::unique_ptr<HTTPResponse>> HTTPServer::handle_request(shared_ptr<HTTPClient> c, HTTPRequest&& req) {
variant<RawResponse, shared_ptr<const phosg::JSON>> ret;
asio::awaitable<std::unique_ptr<HTTPResponse>> HTTPServer::handle_request(std::shared_ptr<HTTPClient> c, HTTPRequest&& req) {
std::variant<RawResponse, std::shared_ptr<const phosg::JSON>> ret;
uint32_t serialize_options = phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY;
uint64_t start_time = phosg::now();
this->log.info_f("{} ...", req.path);
auto resp = make_unique<HTTPResponse>();
auto resp = std::make_unique<HTTPResponse>();
resp->http_version = req.http_version;
resp->response_code = 200;
resp->headers.emplace("Server", "newserv");
@@ -830,39 +828,41 @@ asio::awaitable<std::unique_ptr<HTTPResponse>> HTTPServer::handle_request(shared
ret = co_await this->router.call_handler(c, req);
} catch (const HTTPError& e) {
ret = make_shared<phosg::JSON>(phosg::JSON::dict({{"Error", true}, {"Message", e.what()}}));
ret = std::make_shared<phosg::JSON>(phosg::JSON::dict({{"Error", true}, {"Message", e.what()}}));
resp->response_code = e.code;
} catch (const exception& e) {
ret = make_shared<phosg::JSON>(phosg::JSON::dict({{"Error", true}, {"Message", e.what()}}));
} catch (const std::exception& e) {
ret = std::make_shared<phosg::JSON>(phosg::JSON::dict({{"Error", true}, {"Message", e.what()}}));
resp->response_code = 500;
}
uint64_t handler_end = phosg::now();
if (holds_alternative<shared_ptr<const phosg::JSON>>(ret)) {
if (holds_alternative<std::shared_ptr<const phosg::JSON>>(ret)) {
// If the handler returns nullptr (not JSON null), assume it called enable_websockets and send no response
auto& json = get<shared_ptr<const phosg::JSON>>(ret);
auto& json = get<std::shared_ptr<const phosg::JSON>>(ret);
if (!json) {
co_return nullptr;
}
resp->headers.emplace("Content-Type", "application/json");
resp->data = co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> string {
resp->data = co_await call_on_thread_pool(*this->state->thread_pool, [&]() -> std::string {
return json->serialize(serialize_options, 0);
});
uint64_t serialize_end = phosg::now();
string handler_time = phosg::format_duration(handler_end - start_time);
string serialize_time = phosg::format_duration(serialize_end - handler_end);
string size_str = phosg::format_size(resp->data.size());
this->log.info_f("{} in [handler: {}, serialize: {}, size: {}]", req.path, handler_time, serialize_time, size_str);
this->log.info_f("{} in [handler: {}, serialize: {}, size: {}]",
req.path,
phosg::format_duration(handler_end - start_time),
phosg::format_duration(serialize_end - handler_end),
phosg::format_size(resp->data.size()));
} else {
auto& raw_resp = get<RawResponse>(ret);
resp->headers.emplace("Content-Type", std::move(raw_resp.content_type));
resp->data = std::move(raw_resp.data);
string handler_time = phosg::format_duration(handler_end - start_time);
string size_str = phosg::format_size(resp->data.size());
this->log.info_f("{} in [handler: {}, size: {}]", req.path, handler_time, size_str);
this->log.info_f("{} in [handler: {}, size: {}]",
req.path,
phosg::format_duration(handler_end - start_time),
phosg::format_size(resp->data.size()));
}
co_return resp;
+14 -16
View File
@@ -4,8 +4,6 @@
#include <phosg/Strings.hh>
using namespace std;
static inline uint16_t collapse_checksum(uint32_t sum) {
// It's impossible for this to be necessary more than twice: the first addition can carry out at most a single bit.
sum = (sum & 0xFFFF) + (sum >> 16);
@@ -65,13 +63,13 @@ FrameInfo::FrameInfo(LinkType link_type, const void* header_start, size_t size)
break;
default:
throw logic_error("invalid link type");
throw std::logic_error("invalid link type");
}
// Parse inner protocol headers
switch (proto) {
case Protocol::NONE:
throw runtime_error("unknown protocol");
throw std::runtime_error("unknown protocol");
case Protocol::LCP:
this->payload_size -= sizeof(LCPHeader);
this->lcp = &r.get<LCPHeader>();
@@ -87,7 +85,7 @@ FrameInfo::FrameInfo(LinkType link_type, const void* header_start, size_t size)
case Protocol::IPV4:
this->ipv4 = &r.get<IPv4Header>();
if (this->payload_size < this->ipv4->size) {
throw invalid_argument("ipv4 header specifies size larger than frame");
throw std::invalid_argument("ipv4 header specifies size larger than frame");
}
this->payload_size = this->ipv4->size - sizeof(IPv4Header);
@@ -95,7 +93,7 @@ FrameInfo::FrameInfo(LinkType link_type, const void* header_start, size_t size)
this->tcp = &r.get<TCPHeader>();
size_t tcp_header_size = (this->tcp->flags >> 12) * 4;
if (tcp_header_size < sizeof(TCPHeader) || tcp_header_size > this->payload_size) {
throw invalid_argument("frame is too small for tcp4 header with options");
throw std::invalid_argument("frame is too small for tcp4 header with options");
}
this->tcp_options_size = tcp_header_size - sizeof(TCPHeader);
this->payload_size -= tcp_header_size;
@@ -115,12 +113,12 @@ FrameInfo::FrameInfo(LinkType link_type, const void* header_start, size_t size)
this->payload = r.getv(this->payload_size);
}
string FrameInfo::header_str() const {
std::string FrameInfo::header_str() const {
if (!this->ether && !this->hdlc) {
return "<invalid-frame-info>";
}
string ret;
std::string ret;
if (this->ether) {
ret = std::format(
"ETHER:{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}->{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
@@ -187,10 +185,10 @@ string FrameInfo::header_str() const {
void FrameInfo::truncate(size_t new_total_size) {
if (new_total_size > this->total_size) {
throw logic_error("truncate call expands frame size");
throw std::logic_error("truncate call expands frame size");
}
if (new_total_size < this->payload_size) {
throw logic_error("truncate call destroys part of header");
throw std::logic_error("truncate call destroys part of header");
}
size_t delta_bytes = this->total_size - new_total_size;
this->total_size -= delta_bytes;
@@ -222,7 +220,7 @@ uint16_t FrameInfo::computed_ipv4_header_checksum(const IPv4Header& ipv4) {
uint16_t FrameInfo::computed_ipv4_header_checksum() const {
if (!this->ipv4) {
throw logic_error("cannot compute ipv4 header checksum for non-ipv4 frame");
throw std::logic_error("cannot compute ipv4 header checksum for non-ipv4 frame");
}
return this->computed_ipv4_header_checksum(*this->ipv4);
}
@@ -252,10 +250,10 @@ uint16_t FrameInfo::computed_udp4_checksum(
uint16_t FrameInfo::computed_udp4_checksum() const {
if (!this->ipv4) {
throw logic_error("cannot compute udp header checksum for non-ipv4 frame");
throw std::logic_error("cannot compute udp header checksum for non-ipv4 frame");
}
if (!this->udp) {
throw logic_error("cannot compute udp header checksum for non-udp frame");
throw std::logic_error("cannot compute udp header checksum for non-udp frame");
}
return this->computed_udp4_checksum(
*this->ipv4, *this->udp, this->payload, this->payload_size);
@@ -293,10 +291,10 @@ uint16_t FrameInfo::computed_tcp4_checksum(
uint16_t FrameInfo::computed_tcp4_checksum() const {
if (!this->ipv4) {
throw logic_error("cannot compute tcp header checksum for non-ipv4 frame");
throw std::logic_error("cannot compute tcp header checksum for non-ipv4 frame");
}
if (!this->tcp) {
throw logic_error("cannot compute tcp header checksum for non-tcp frame");
throw std::logic_error("cannot compute tcp header checksum for non-tcp frame");
}
return this->computed_tcp4_checksum(
*this->ipv4, *this->tcp, this->tcp + 1,
@@ -317,7 +315,7 @@ uint16_t FrameInfo::computed_hdlc_checksum(const void* vdata, size_t size) {
uint16_t FrameInfo::computed_hdlc_checksum() const {
if (!this->hdlc) {
throw logic_error("cannot compute HDLC checksum for non-HDLC frame");
throw std::logic_error("cannot compute HDLC checksum for non-HDLC frame");
}
return this->computed_hdlc_checksum(&this->hdlc->address, this->total_size - 4);
}
+132 -132
View File
@@ -13,18 +13,16 @@
#include "IPFrameInfo.hh"
#include "Loggers.hh"
using namespace std;
static size_t unescape_hdlc_frame_inplace(void* vdata, size_t size) {
uint8_t* data = reinterpret_cast<uint8_t*>(vdata);
if (size < 2) {
throw runtime_error("escaped HDLC frame is too small");
throw std::runtime_error("escaped HDLC frame is too small");
}
if (data[0] != 0x7E) {
throw runtime_error("HDLC frame does not begin with 7E");
throw std::runtime_error("HDLC frame does not begin with 7E");
}
if (data[size - 1] != 0x7E) {
throw runtime_error("HDLC frame does not end with 7E");
throw std::runtime_error("HDLC frame does not end with 7E");
}
size_t read_offset = 1;
@@ -33,33 +31,34 @@ static size_t unescape_hdlc_frame_inplace(void* vdata, size_t size) {
uint8_t ch = data[read_offset++];
if (ch == 0x7D) {
if (read_offset >= size - 1) {
throw runtime_error("abort sequence received");
throw std::runtime_error("abort sequence received");
}
ch = data[read_offset++] ^ 0x20;
}
data[write_offset++] = ch;
}
if (write_offset > size - 1) {
throw logic_error("unescaping HDLC frame resulted in longer data string");
throw std::logic_error("unescaping HDLC frame resulted in longer data string");
}
data[write_offset++] = 0x7E;
return write_offset;
}
static string escape_hdlc_frame(const void* data, size_t size, uint32_t escape_control_character_flags = 0xFFFFFFFF) {
static std::string escape_hdlc_frame(
const void* data, size_t size, uint32_t escape_control_character_flags = 0xFFFFFFFF) {
if (size < 2) {
throw runtime_error("HDLC frame too small for start and end sentinels");
throw std::runtime_error("HDLC frame too small for start and end sentinels");
}
phosg::StringReader r(data, size);
if (r.pget_u8(size - 1) != 0x7E) {
throw runtime_error("HDLC frame does not end with 7E");
throw std::runtime_error("HDLC frame does not end with 7E");
}
r.truncate(size - 1);
if (r.get_u8() != 0x7E) {
throw runtime_error("HDLC frame does not begin with 7E");
throw std::runtime_error("HDLC frame does not begin with 7E");
}
string ret("\x7E", 1);
std::string ret("\x7E", 1);
while (!r.eof()) {
uint8_t ch = r.get_u8();
@@ -74,7 +73,7 @@ static string escape_hdlc_frame(const void* data, size_t size, uint32_t escape_c
return ret;
}
static string escape_hdlc_frame(const string& data, uint32_t escape_control_character_flags = 0xFFFFFFFF) {
static std::string escape_hdlc_frame(const std::string& data, uint32_t escape_control_character_flags = 0xFFFFFFFF) {
return escape_hdlc_frame(data.data(), data.size(), escape_control_character_flags);
}
@@ -114,7 +113,7 @@ void IPSSClient::TCPConnection::drain_outbound_data(size_t size) {
}
}
if (size > 0) {
throw logic_error("attempted to drain more outbound data than was present");
throw std::logic_error("attempted to drain more outbound data than was present");
}
}
@@ -128,7 +127,7 @@ void IPSSClient::TCPConnection::linearize_outbound_data(size_t size) {
}
IPSSClient::IPSSClient(
shared_ptr<IPStackSimulator> sim, uint64_t network_id, VirtualNetworkProtocol protocol, asio::ip::tcp::socket&& sock)
std::shared_ptr<IPStackSimulator> sim, uint64_t network_id, VirtualNetworkProtocol protocol, asio::ip::tcp::socket&& sock)
: io_context(sim->get_io_context()),
sim(sim),
network_id(network_id),
@@ -143,7 +142,7 @@ IPSSClient::IPSSClient(
void IPSSClient::reschedule_idle_timeout() {
auto sim = this->sim.lock();
if (!sim) {
throw runtime_error("cannot reschedule idle timeout when simulator is missing");
throw std::runtime_error("cannot reschedule idle timeout when simulator is missing");
}
this->idle_timeout_timer.cancel();
this->idle_timeout_timer.expires_after(std::chrono::microseconds(sim->get_state()->client_idle_timeout_usecs));
@@ -204,7 +203,7 @@ void IPSSChannel::add_inbound_data(const void* data, size_t size) {
// If recv_buf is not null, there is a coroutine waiting to receive data, and inbound_data must be empty. Copy the
// data directly to the waiting coroutine's buffer, and put the rest in this->inbound_data if needed.
if (this->recv_buf) {
size_t direct_size = min<size_t>(this->recv_buf_size, size);
size_t direct_size = std::min<size_t>(this->recv_buf_size, size);
memcpy(this->recv_buf, data, direct_size);
data = reinterpret_cast<const uint8_t*>(data) + direct_size;
size -= direct_size;
@@ -221,7 +220,7 @@ void IPSSChannel::add_inbound_data(const void* data, size_t size) {
this->data_available_signal.set();
}
void IPSSChannel::send_raw(string&& data) {
void IPSSChannel::send_raw(std::string&& data) {
auto c = this->ipss_client.lock();
if (!c) {
return;
@@ -249,7 +248,7 @@ void IPSSChannel::send_raw(string&& data) {
asio::awaitable<void> IPSSChannel::recv_raw(void* data, size_t size) {
if (this->recv_buf) {
throw logic_error("recv_raw called again when it was already pending");
throw std::logic_error("recv_raw called again when it was already pending");
}
// Receive as much data as possible from the pending inbound data buffer
@@ -274,7 +273,7 @@ asio::awaitable<void> IPSSChannel::recv_raw(void* data, size_t size) {
this->recv_buf_size = size;
while (this->recv_buf) {
if (!this->connected()) {
throw runtime_error("IPSS channel closed");
throw std::runtime_error("IPSS channel closed");
}
this->data_available_signal.clear();
co_await this->data_available_signal.wait();
@@ -282,18 +281,18 @@ asio::awaitable<void> IPSSChannel::recv_raw(void* data, size_t size) {
}
}
IPStackSimulator::IPStackSimulator(shared_ptr<ServerState> state)
IPStackSimulator::IPStackSimulator(std::shared_ptr<ServerState> state)
: Server(state->io_context, "[IPStackSimulator] "), state(state) {
this->host_mac_address_bytes.clear(0x90);
this->broadcast_mac_address_bytes.clear(0xFF);
}
void IPStackSimulator::listen(const std::string& name, const string& addr, int port, VirtualNetworkProtocol protocol) {
void IPStackSimulator::listen(const std::string& name, const std::string& addr, int port, VirtualNetworkProtocol protocol) {
if (port == 0) {
throw std::runtime_error("Listening port cannot be zero");
}
asio::ip::address asio_addr = addr.empty() ? asio::ip::address_v4::any() : asio::ip::make_address(addr);
auto sock = make_shared<IPSSSocket>();
auto sock = std::make_shared<IPSSSocket>();
sock->name = name;
sock->endpoint = asio::ip::tcp::endpoint(asio_addr, port);
sock->protocol = protocol;
@@ -320,12 +319,12 @@ uint64_t IPStackSimulator::tcp_conn_key_for_client_frame(const IPv4Header& ipv4,
uint64_t IPStackSimulator::tcp_conn_key_for_client_frame(const FrameInfo& fi) {
if (!fi.ipv4 || !fi.tcp) {
throw logic_error("tcp_conn_key_for_frame called on non-TCP frame");
throw std::logic_error("tcp_conn_key_for_frame called on non-TCP frame");
}
return IPStackSimulator::tcp_conn_key_for_client_frame(*fi.ipv4, *fi.tcp);
}
string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) {
std::string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) {
be_uint32_t be_addr = addr;
char addr_str[INET_ADDRSTRLEN];
memset(addr_str, 0, sizeof(addr_str));
@@ -336,16 +335,16 @@ string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) {
}
}
string IPStackSimulator::str_for_tcp_connection(
shared_ptr<const IPSSClient> c, std::shared_ptr<const IPSSClient::TCPConnection> conn) {
std::string IPStackSimulator::str_for_tcp_connection(
std::shared_ptr<const IPSSClient> c, std::shared_ptr<const IPSSClient::TCPConnection> conn) {
uint64_t key = IPStackSimulator::tcp_conn_key_for_connection(conn);
string server_netloc_str = str_for_ipv4_netloc(conn->server_addr, conn->server_port);
string client_netloc_str = str_for_ipv4_netloc(c->ipv4_addr, conn->client_port);
std::string server_netloc_str = str_for_ipv4_netloc(conn->server_addr, conn->server_port);
std::string client_netloc_str = str_for_ipv4_netloc(c->ipv4_addr, conn->client_port);
return std::format("{:016X} ({} -> {})", key, client_netloc_str, server_netloc_str);
}
asio::awaitable<void> IPStackSimulator::send_ethernet_tapserver_frame(
shared_ptr<IPSSClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const {
std::shared_ptr<IPSSClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const {
struct TapServerEthernetHeader {
phosg::le_uint16_t frame_size;
@@ -358,9 +357,9 @@ asio::awaitable<void> IPStackSimulator::send_ethernet_tapserver_frame(
header.ether.src_mac = this->host_mac_address_bytes;
switch (proto) {
case FrameInfo::Protocol::NONE:
throw logic_error("layer 3 protocol not specified");
throw std::logic_error("layer 3 protocol not specified");
case FrameInfo::Protocol::LCP:
throw logic_error("cannot send LCP frame over Ethernet");
throw std::logic_error("cannot send LCP frame over Ethernet");
case FrameInfo::Protocol::IPV4:
header.ether.protocol = 0x0800;
break;
@@ -368,16 +367,17 @@ asio::awaitable<void> IPStackSimulator::send_ethernet_tapserver_frame(
header.ether.protocol = 0x0806;
break;
default:
throw logic_error("unknown layer 3 protocol");
throw std::logic_error("unknown layer 3 protocol");
}
header.frame_size = size + sizeof(EthernetHeader);
array<asio::const_buffer, 2> bufs{asio::buffer(static_cast<const void*>(&header), sizeof(header)), asio::buffer(data, size)};
std::array<asio::const_buffer, 2> bufs{
asio::buffer(static_cast<const void*>(&header), sizeof(header)), asio::buffer(data, size)};
co_await asio::async_write(c->sock, bufs, asio::use_awaitable);
}
asio::awaitable<void> IPStackSimulator::send_hdlc_frame(
shared_ptr<IPSSClient> c, FrameInfo::Protocol proto, const void* data, size_t size, bool is_raw) const {
std::shared_ptr<IPSSClient> c, FrameInfo::Protocol proto, const void* data, size_t size, bool is_raw) const {
HDLCHeader hdlc;
hdlc.start_sentinel1 = 0x7E;
@@ -385,7 +385,7 @@ asio::awaitable<void> IPStackSimulator::send_hdlc_frame(
hdlc.control = 0x03;
switch (proto) {
case FrameInfo::Protocol::NONE:
throw logic_error("layer 3 protocol not specified");
throw std::logic_error("layer 3 protocol not specified");
case FrameInfo::Protocol::LCP:
hdlc.protocol = 0xC021;
break;
@@ -399,9 +399,9 @@ asio::awaitable<void> IPStackSimulator::send_hdlc_frame(
hdlc.protocol = 0x0021;
break;
case FrameInfo::Protocol::ARP:
throw runtime_error("cannot send ARP packets over HDLC");
throw std::runtime_error("cannot send ARP packets over HDLC");
default:
throw logic_error("unknown layer 3 protocol");
throw std::logic_error("unknown layer 3 protocol");
}
phosg::StringWriter w;
@@ -410,14 +410,14 @@ asio::awaitable<void> IPStackSimulator::send_hdlc_frame(
w.put_u16l(FrameInfo::computed_hdlc_checksum(w.str().data() + 1, w.size() - 1));
w.put_u8(0x7E);
string escaped = escape_hdlc_frame(w.str(), c->hdlc_escape_control_character_flags);
std::string escaped = escape_hdlc_frame(w.str(), c->hdlc_escape_control_character_flags);
if (this->log.debug_f("Sending HDLC frame to virtual network (escaped to {:X} bytes)", escaped.size())) {
phosg::print_data(stderr, w.str());
}
if (!is_raw) {
phosg::le_uint16_t frame_size = escaped.size();
array<asio::const_buffer, 2> bufs{
std::array<asio::const_buffer, 2> bufs{
asio::buffer(static_cast<const void*>(&frame_size), sizeof(frame_size)),
asio::buffer(escaped.data(), escaped.size())};
co_await asio::async_write(c->sock, bufs, asio::use_awaitable);
@@ -427,7 +427,7 @@ asio::awaitable<void> IPStackSimulator::send_hdlc_frame(
}
asio::awaitable<void> IPStackSimulator::send_layer3_frame(
shared_ptr<IPSSClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const {
std::shared_ptr<IPSSClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const {
switch (c->protocol) {
case VirtualNetworkProtocol::ETHERNET_TAPSERVER:
co_await this->send_ethernet_tapserver_frame(c, proto, data, size);
@@ -439,11 +439,11 @@ asio::awaitable<void> IPStackSimulator::send_layer3_frame(
co_await this->send_hdlc_frame(c, proto, data, size, true);
break;
default:
throw logic_error("unknown link type");
throw std::logic_error("unknown link type");
}
}
asio::awaitable<void> IPStackSimulator::on_client_frame(shared_ptr<IPSSClient> c, const void* data, size_t size) {
asio::awaitable<void> IPStackSimulator::on_client_frame(std::shared_ptr<IPSSClient> c, const void* data, size_t size) {
FrameInfo::LinkType link_type = (c->protocol == VirtualNetworkProtocol::ETHERNET_TAPSERVER)
? FrameInfo::LinkType::ETHERNET
: FrameInfo::LinkType::HDLC;
@@ -461,17 +461,17 @@ asio::awaitable<void> IPStackSimulator::on_client_frame(shared_ptr<IPSSClient> c
if (c->mac_addr.is_filled_with(0)) {
c->mac_addr = fi.ether->src_mac;
} else if ((fi.ether->src_mac != c->mac_addr) && (fi.ether->src_mac != this->broadcast_mac_address_bytes)) {
throw runtime_error("client sent IPv4 packet from different MAC address");
throw std::runtime_error("client sent IPv4 packet from different MAC address");
}
} else if (fi.hdlc) {
uint16_t expected_checksum = fi.computed_hdlc_checksum();
uint16_t stored_checksum = fi.stored_hdlc_checksum();
if (expected_checksum != stored_checksum) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"HDLC checksum is incorrect ({:04X} expected, {:04X} received)", expected_checksum, stored_checksum));
}
} else {
throw runtime_error("frame is not Ethernet or HDLC");
throw std::runtime_error("frame is not Ethernet or HDLC");
}
if (fi.lcp) {
@@ -489,18 +489,18 @@ asio::awaitable<void> IPStackSimulator::on_client_frame(shared_ptr<IPSSClient> c
} else if (fi.ipv4) {
uint16_t expected_ipv4_checksum = fi.computed_ipv4_header_checksum();
if (fi.ipv4->checksum != expected_ipv4_checksum) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"IPv4 header checksum is incorrect ({:04X} expected, {:04X} received)", expected_ipv4_checksum, fi.ipv4->checksum));
}
if ((fi.ipv4->src_addr != c->ipv4_addr) && (fi.ipv4->src_addr != 0)) {
throw runtime_error("client sent IPv4 packet from different IPv4 address");
throw std::runtime_error("client sent IPv4 packet from different IPv4 address");
}
if (fi.udp) {
uint16_t expected_udp_checksum = fi.computed_udp4_checksum();
if (fi.udp->checksum != expected_udp_checksum) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"UDP checksum is incorrect ({:04X} expected, {:04X} received)", expected_udp_checksum, fi.udp->checksum));
}
co_await this->on_client_udp_frame(c, fi);
@@ -508,27 +508,27 @@ asio::awaitable<void> IPStackSimulator::on_client_frame(shared_ptr<IPSSClient> c
} else if (fi.tcp) {
uint16_t expected_tcp_checksum = fi.computed_tcp4_checksum();
if (fi.tcp->checksum != expected_tcp_checksum) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"TCP checksum is incorrect ({:04X} expected, {:04X} received)", expected_tcp_checksum, fi.tcp->checksum));
}
co_await this->on_client_tcp_frame(c, fi);
} else {
throw runtime_error("frame uses unsupported IPv4 protocol");
throw std::runtime_error("frame uses unsupported IPv4 protocol");
}
} else {
throw runtime_error("frame is not IPv4");
throw std::runtime_error("frame is not IPv4");
}
}
asio::awaitable<void> IPStackSimulator::on_client_lcp_frame(shared_ptr<IPSSClient> c, const FrameInfo& fi) {
asio::awaitable<void> IPStackSimulator::on_client_lcp_frame(std::shared_ptr<IPSSClient> c, const FrameInfo& fi) {
switch (fi.lcp->command) {
case 0x01: { // Configure-Request
auto opts_r = fi.read_payload();
while (!opts_r.eof()) {
uint8_t opt = opts_r.get_u8();
string opt_data = opts_r.read(opts_r.get_u8() - 2);
std::string opt_data = opts_r.read(opts_r.get_u8() - 2);
phosg::StringReader opt_data_r(opt_data);
switch (opt) {
case 0x01: // Maximum receive unit
@@ -546,9 +546,9 @@ asio::awaitable<void> IPStackSimulator::on_client_lcp_frame(shared_ptr<IPSSClien
case 0x04: // Quality protocol
case 0x07: // Protocol field compression
case 0x08: // Address and control field compression
throw runtime_error(std::format("unimplemented LCP option {:02X} ({} bytes)", opt, opt_data.size()));
throw std::runtime_error(std::format("unimplemented LCP option {:02X} ({} bytes)", opt, opt_data.size()));
default:
throw runtime_error("unknown LCP option");
throw std::runtime_error("unknown LCP option");
}
}
// Technically, we should implement the LCP state machine, but I'm too lazy to do this right now. In our
@@ -591,14 +591,14 @@ asio::awaitable<void> IPStackSimulator::on_client_lcp_frame(shared_ptr<IPSSClien
case 0x05: { // Terminate-Request
c->ipv4_addr = 0;
c->tcp_connections.clear();
string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
std::string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
response.at(0) = 0x06; // Terminate-Ack
co_await this->send_layer3_frame(c, FrameInfo::Protocol::LCP, response);
break;
}
case 0x09: { // Echo-Request
string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
std::string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
response.at(0) = 0x0A; // Echo-Reply
co_await this->send_layer3_frame(c, FrameInfo::Protocol::LCP, response);
break;
@@ -614,23 +614,23 @@ asio::awaitable<void> IPStackSimulator::on_client_lcp_frame(shared_ptr<IPSSClien
case 0x07: // Code-Reject
case 0x08: // Protocol-Reject
case 0x0A: // Echo-Reply
throw runtime_error("unimplemented LCP command");
throw std::runtime_error("unimplemented LCP command");
default:
throw runtime_error("unknown LCP command");
throw std::runtime_error("unknown LCP command");
}
}
asio::awaitable<void> IPStackSimulator::on_client_pap_frame(shared_ptr<IPSSClient> c, const FrameInfo& fi) {
asio::awaitable<void> IPStackSimulator::on_client_pap_frame(std::shared_ptr<IPSSClient> c, const FrameInfo& fi) {
if (fi.pap->command != 0x01) { // Authenticate-Request
throw runtime_error("client sent incorrect PAP command");
throw std::runtime_error("client sent incorrect PAP command");
}
auto r = fi.read_payload();
string username = r.read(r.get_u8());
string password = r.read(r.get_u8());
std::string username = r.read(r.get_u8());
std::string password = r.read(r.get_u8());
this->log.info_f("Client logged in with username \"{}\" and password", username);
static const string login_message = "newserv PPP simulator";
static const std::string login_message = "newserv PPP simulator";
phosg::StringWriter w;
w.put<PAPHeader>(PAPHeader{
.command = 0x02, // Authenticate-Ack
@@ -642,7 +642,7 @@ asio::awaitable<void> IPStackSimulator::on_client_pap_frame(shared_ptr<IPSSClien
co_await this->send_layer3_frame(c, FrameInfo::Protocol::PAP, w.str());
}
asio::awaitable<void> IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPSSClient> c, const FrameInfo& fi) {
asio::awaitable<void> IPStackSimulator::on_client_ipcp_frame(std::shared_ptr<IPSSClient> c, const FrameInfo& fi) {
switch (fi.ipcp->command) {
case 0x01: { // Configure-Request
auto opts_r = fi.read_payload();
@@ -653,11 +653,11 @@ asio::awaitable<void> IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPSSClie
phosg::StringWriter rejected_opts_w;
while (!opts_r.eof()) {
uint8_t opt = opts_r.get_u8();
string opt_data = opts_r.read(opts_r.get_u8() - 2);
std::string opt_data = opts_r.read(opts_r.get_u8() - 2);
phosg::StringReader opt_data_r(opt_data);
switch (opt) {
case 0x01: // IP addresses (deprecated as of 1992; we don't support it at all)
throw runtime_error("IPCP client sent IP-Addresses option");
throw std::runtime_error("IPCP client sent IP-Addresses option");
case 0x02: // IP compression protocol
rejected_opts_w.put_u8(0x02);
rejected_opts_w.put_u8(opt_data_r.size() + 2);
@@ -675,9 +675,9 @@ asio::awaitable<void> IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPSSClie
case 0x82: // Primary NBNS server address
case 0x84: // Secondary NBNS server address
case 0x04: // Mobile IP address
throw runtime_error(std::format("unimplemented IPCP option {:02X} ({} bytes)", opt, opt_data.size()));
throw std::runtime_error(std::format("unimplemented IPCP option {:02X} ({} bytes)", opt, opt_data.size()));
default:
throw runtime_error("unknown IPCP option");
throw std::runtime_error("unknown IPCP option");
}
}
@@ -755,7 +755,7 @@ asio::awaitable<void> IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPSSClie
case 0x05: { // Terminate-Request
c->ipv4_addr = 0;
c->tcp_connections.clear();
string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
std::string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
response.at(0) = 0x06; // Terminate-Ack
co_await this->send_layer3_frame(c, FrameInfo::Protocol::LCP, response);
break;
@@ -768,21 +768,21 @@ asio::awaitable<void> IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPSSClie
case 0x04: // Configure-Reject
case 0x06: // Terminate-Ack
case 0x07: // Code-Reject
throw runtime_error("unimplemented IPCP command");
throw std::runtime_error("unimplemented IPCP command");
default:
throw runtime_error("unknown LCP command");
throw std::runtime_error("unknown LCP command");
}
}
asio::awaitable<void> IPStackSimulator::on_client_arp_frame(shared_ptr<IPSSClient> c, const FrameInfo& fi) {
asio::awaitable<void> IPStackSimulator::on_client_arp_frame(std::shared_ptr<IPSSClient> c, const FrameInfo& fi) {
if (fi.arp->hwaddr_len != 6 ||
fi.arp->paddr_len != 4 ||
fi.arp->hardware_type != 0x0001 ||
fi.arp->protocol_type != 0x0800) {
throw runtime_error("unsupported ARP parameters");
throw std::runtime_error("unsupported ARP parameters");
}
if (fi.payload_size < 20) {
throw runtime_error("ARP payload too small");
throw std::runtime_error("ARP payload too small");
}
if (c->ipv4_addr == 0) {
@@ -816,7 +816,7 @@ asio::awaitable<void> IPStackSimulator::on_client_arp_frame(shared_ptr<IPSSClien
co_await this->send_layer3_frame(c, FrameInfo::Protocol::ARP, w.str());
}
asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClient> c, const FrameInfo& fi) {
asio::awaitable<void> IPStackSimulator::on_client_udp_frame(std::shared_ptr<IPSSClient> c, const FrameInfo& fi) {
// We only implement DHCP and newserv's DNS server here.
// Every received UDP packet will elicit exactly one UDP response from newserv, so we prepare the headers in advance
@@ -838,24 +838,24 @@ asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClien
// r_udp.size filled in later
// r_udp.checksum filled in later
string r_data;
std::string r_data;
if (fi.udp->dest_port == 67) { // DHCP
auto r = fi.read_payload();
const auto& dhcp = r.get<DHCPHeader>();
if (dhcp.hardware_type != 1) {
throw runtime_error("unknown DHCP hardware type");
throw std::runtime_error("unknown DHCP hardware type");
}
if (dhcp.hardware_address_length != 6) {
throw runtime_error("unknown DHCP hardware address length");
throw std::runtime_error("unknown DHCP hardware address length");
}
if (dhcp.magic != 0x63825363) {
throw runtime_error("incorrect DHCP magic cookie");
throw std::runtime_error("incorrect DHCP magic cookie");
}
if (dhcp.opcode != 1) { // Request
throw runtime_error("DHCP packet is not a request");
throw std::runtime_error("DHCP packet is not a request");
}
unordered_map<uint8_t, string> option_data;
std::unordered_map<uint8_t, std::string> option_data;
for (;;) {
uint8_t option = r.get_u8();
if (option == 0xFF) {
@@ -868,8 +868,8 @@ asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClien
uint8_t command = 0;
try {
command = option_data.at(53).at(0);
} catch (const out_of_range&) {
throw runtime_error("client did not send a DHCP command option");
} catch (const std::out_of_range&) {
throw std::runtime_error("client did not send a DHCP command option");
}
if (command == 7) {
@@ -885,7 +885,7 @@ asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClien
r_ipv4.dest_addr = c->ipv4_addr;
if ((command != 1) && (command != 3)) {
throw runtime_error("client sent unknown DHCP command option");
throw std::runtime_error("client sent unknown DHCP command option");
}
phosg::StringWriter w;
@@ -951,19 +951,19 @@ asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClien
r_data = std::move(w.str());
} else {
throw runtime_error("client sent unknown DHCP command");
throw std::runtime_error("client sent unknown DHCP command");
}
} else if (fi.udp->dest_port == 53) { // DNS
if (fi.payload_size < 0x0C) {
throw runtime_error("DNS payload too small");
throw std::runtime_error("DNS payload too small");
}
uint32_t resolved_address = this->connect_address_for_remote_address(c->ipv4_addr);
r_data = DNSServer::response_for_query(fi.payload, fi.payload_size, resolved_address);
} else { // Not DHCP or DNS
throw runtime_error("UDP packet is not DHCP or DNS");
throw std::runtime_error("UDP packet is not DHCP or DNS");
}
if (!r_data.empty()) {
@@ -973,7 +973,7 @@ asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClien
r_udp.checksum = FrameInfo::computed_udp4_checksum(r_ipv4, r_udp, r_data.data(), r_data.size());
if (this->log.should_log(phosg::LogLevel::L_DEBUG)) {
string remote_str = this->str_for_ipv4_netloc(fi.ipv4->src_addr, fi.udp->src_port);
std::string remote_str = this->str_for_ipv4_netloc(fi.ipv4->src_addr, fi.udp->src_port);
this->log.debug_f("Sending UDP response to {}", remote_str);
phosg::print_data(stderr, r_data);
}
@@ -987,19 +987,19 @@ asio::awaitable<void> IPStackSimulator::on_client_udp_frame(shared_ptr<IPSSClien
}
}
asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClient> c, const FrameInfo& fi) {
asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(std::shared_ptr<IPSSClient> c, const FrameInfo& fi) {
this->log.debug_f("Virtual network sent TCP frame (seq={:08X}, ack={:08X})",
fi.tcp->seq_num, fi.tcp->ack_num);
if (fi.tcp->flags & (TCPHeader::Flag::NS | TCPHeader::Flag::CWR | TCPHeader::Flag::ECE | TCPHeader::Flag::URG)) {
throw runtime_error("unsupported flag in TCP packet");
throw std::runtime_error("unsupported flag in TCP packet");
}
if (fi.tcp->flags & TCPHeader::Flag::SYN) {
// We never make connections back to the client, so we should never receive a SYN+ACK. Essentially, no other flags
// should be set in any received SYN.
if ((fi.tcp->flags & 0x0FFF) != TCPHeader::Flag::SYN) {
throw runtime_error("TCP SYN contains extra flags");
throw std::runtime_error("TCP SYN contains extra flags");
}
phosg::StringReader options_r(fi.tcp + 1, fi.tcp_options_size);
@@ -1015,19 +1015,19 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
break;
case 2: // Max segment size
if (option_size != 4) {
throw runtime_error("incorrect size for TCP max frame size option");
throw std::runtime_error("incorrect size for TCP max frame size option");
}
max_frame_size = options_r.get_u16b();
break;
case 3: // Window scale (ignored)
if (option_size != 3) {
throw runtime_error("incorrect size for TCP window scale option");
throw std::runtime_error("incorrect size for TCP window scale option");
}
options_r.skip(option_size);
break;
case 4: // Selective ACK supported (ignored)
if (option_size != 2) {
throw runtime_error("incorrect size for TCP selective ACK supported option");
throw std::runtime_error("incorrect size for TCP selective ACK supported option");
}
break;
case 5: // Selective ACK (ignored)
@@ -1035,21 +1035,21 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
break;
case 8: // Timestamps (ignored)
if (option_size != 10) {
throw runtime_error("incorrect size for TCP timestamps option");
throw std::runtime_error("incorrect size for TCP timestamps option");
}
options_r.skip(8);
break;
default:
throw runtime_error("invalid TCP option");
throw std::runtime_error("invalid TCP option");
}
}
shared_ptr<IPSSClient::TCPConnection> conn;
string conn_str;
std::shared_ptr<IPSSClient::TCPConnection> conn;
std::string conn_str;
uint64_t key = this->tcp_conn_key_for_client_frame(fi);
auto conn_it = c->tcp_connections.find(key);
if (conn_it == c->tcp_connections.end()) {
conn = make_shared<IPSSClient::TCPConnection>(c);
conn = std::make_shared<IPSSClient::TCPConnection>(c);
c->tcp_connections.emplace(key, conn);
conn->server_addr = fi.ipv4->dest_addr;
conn->server_port = fi.tcp->dest_port;
@@ -1070,7 +1070,7 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
// Connection is NOT new; this is probably a resend of an earlier SYN
if (!conn->awaiting_first_ack) {
throw logic_error("SYN received on already-open connection after initial phase");
throw std::logic_error("SYN received on already-open connection after initial phase");
}
// TODO: We should check the syn/ack numbers here instead of just assuming they're correct
conn_str = this->str_for_tcp_connection(c, conn);
@@ -1100,7 +1100,7 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
this->log.debug_f("Client sent ACK {:08X}", fi.tcp->ack_num);
if (conn->awaiting_first_ack) {
if (fi.tcp->ack_num != conn->acked_server_seq + 1) {
throw runtime_error("first ack_num was not acked_server_seq + 1");
throw std::runtime_error("first ack_num was not acked_server_seq + 1");
}
conn->acked_server_seq++;
conn->awaiting_first_ack = false;
@@ -1111,7 +1111,7 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
this->log.debug_f("Advancing acked_server_seq from {:08X}", conn->acked_server_seq);
uint32_t ack_delta = fi.tcp->ack_num - conn->acked_server_seq;
if (conn->outbound_data_bytes < ack_delta) {
throw runtime_error("client acknowledged beyond end of sent data");
throw std::runtime_error("client acknowledged beyond end of sent data");
}
conn->drain_outbound_data(ack_delta);
@@ -1125,7 +1125,7 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
ack_delta, conn->acked_server_seq);
} else if (seq_num_less(fi.tcp->ack_num, conn->acked_server_seq)) {
throw runtime_error("client sent lower ack num than previous frame");
throw std::runtime_error("client sent lower ack num than previous frame");
}
}
@@ -1137,10 +1137,10 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
if (fi.tcp->flags & (TCPHeader::Flag::RST | TCPHeader::Flag::FIN)) {
bool is_rst = (fi.tcp->flags & TCPHeader::Flag::RST);
if (is_rst && (fi.tcp->flags & TCPHeader::Flag::FIN)) {
throw runtime_error("client sent TCP FIN+RST");
throw std::runtime_error("client sent TCP FIN+RST");
}
string conn_str = this->str_for_tcp_connection(c, conn);
std::string conn_str = this->str_for_tcp_connection(c, conn);
this->log.info_f("Client closed TCP connection {}", conn_str);
if (conn->server_channel) {
conn->server_channel->disconnect();
@@ -1162,7 +1162,7 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
// newserv can handle incomplete commands, so we just ignore the PSH flag and forward any data to the server
// immediately (hence the lack of a flag check in the above condition).
string conn_str = this->log.should_log(phosg::LogLevel::L_WARNING)
std::string conn_str = this->log.should_log(phosg::LogLevel::L_WARNING)
? this->str_for_tcp_connection(c, conn)
: "";
@@ -1190,7 +1190,7 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
}
if (payload_skip_bytes > fi.payload_size) {
throw logic_error("payload skip bytes too large");
throw std::logic_error("payload skip bytes too large");
}
if (payload_skip_bytes < fi.payload_size) {
@@ -1239,9 +1239,9 @@ asio::awaitable<void> IPStackSimulator::on_client_tcp_frame(shared_ptr<IPSSClien
}
}
void IPStackSimulator::schedule_send_pending_push_frame(shared_ptr<IPSSClient::TCPConnection> conn, uint64_t delay_usecs) {
void IPStackSimulator::schedule_send_pending_push_frame(std::shared_ptr<IPSSClient::TCPConnection> conn, uint64_t delay_usecs) {
conn->resend_push_timer.expires_after(std::chrono::microseconds(delay_usecs));
conn->resend_push_timer.async_wait([wconn = weak_ptr<IPSSClient::TCPConnection>(conn)](std::error_code ec) {
conn->resend_push_timer.async_wait([wconn = std::weak_ptr<IPSSClient::TCPConnection>(conn)](std::error_code ec) {
if (ec) {
return;
}
@@ -1262,7 +1262,7 @@ void IPStackSimulator::schedule_send_pending_push_frame(shared_ptr<IPSSClient::T
}
asio::awaitable<void> IPStackSimulator::send_pending_push_frame(
shared_ptr<IPSSClient> c, shared_ptr<IPSSClient::TCPConnection> conn) {
std::shared_ptr<IPSSClient> c, std::shared_ptr<IPSSClient::TCPConnection> conn) {
if (!conn->outbound_data_bytes) {
if (!conn->server_channel || !conn->server_channel->connected()) {
co_await this->close_tcp_connection(c, conn);
@@ -1270,11 +1270,11 @@ asio::awaitable<void> IPStackSimulator::send_pending_push_frame(
co_return;
}
size_t bytes_to_send = min<size_t>(conn->outbound_data_bytes, conn->next_push_max_frame_size);
size_t bytes_to_send = std::min<size_t>(conn->outbound_data_bytes, conn->next_push_max_frame_size);
if (c->protocol == VirtualNetworkProtocol::HDLC_TAPSERVER) {
// There is a bug in Dolphin's modem implementation (which I wrote, so it's my fault) that causes commands to be
// dropped when too much data is sent. To work around this, we only send up to 200 bytes in each push frame.
bytes_to_send = min<size_t>(bytes_to_send, 200);
bytes_to_send = std::min<size_t>(bytes_to_send, 200);
}
this->log.debug_f("Sending PSH frame with seq_num {:08X}, 0x{:X}/0x{:X} data bytes",
@@ -1284,7 +1284,7 @@ asio::awaitable<void> IPStackSimulator::send_pending_push_frame(
if (conn->outbound_data.empty() || conn->outbound_data.front().size() < bytes_to_send) {
// This should never happen because bytes_to_send should always be less than or equal to conn->outbound_data_bytes,
// which itself should be equal to the number of bytes that can be linearized
throw logic_error("failed to linearize enough bytes before sending TCP PSH");
throw std::logic_error("failed to linearize enough bytes before sending TCP PSH");
}
co_await this->send_tcp_frame(c, conn, TCPHeader::Flag::PSH, conn->outbound_data.front().data(), bytes_to_send);
conn->awaiting_ack = true;
@@ -1300,17 +1300,17 @@ asio::awaitable<void> IPStackSimulator::send_pending_push_frame(
if (conn->resend_push_usecs > 5000000) {
conn->resend_push_usecs = 5000000;
}
conn->next_push_max_frame_size = max<size_t>(0x100, conn->next_push_max_frame_size - 0x100);
conn->next_push_max_frame_size = std::max<size_t>(0x100, conn->next_push_max_frame_size - 0x100);
}
asio::awaitable<void> IPStackSimulator::send_tcp_frame(
shared_ptr<IPSSClient> c,
shared_ptr<IPSSClient::TCPConnection> conn,
std::shared_ptr<IPSSClient> c,
std::shared_ptr<IPSSClient::TCPConnection> conn,
uint16_t flags,
const void* payload_data,
size_t payload_size) {
if (!payload_data != !(flags & TCPHeader::Flag::PSH)) {
throw logic_error("data should be given if and only if PSH is given");
throw std::logic_error("data should be given if and only if PSH is given");
}
IPv4Header ipv4;
@@ -1350,12 +1350,12 @@ asio::awaitable<void> IPStackSimulator::send_tcp_frame(
}
asio::awaitable<void> IPStackSimulator::open_server_connection(
shared_ptr<IPSSClient> c, shared_ptr<IPSSClient::TCPConnection> conn) {
std::shared_ptr<IPSSClient> c, std::shared_ptr<IPSSClient::TCPConnection> conn) {
if (conn->server_channel) {
throw logic_error("server connection is already open");
throw std::logic_error("server connection is already open");
}
string conn_str = this->str_for_tcp_connection(c, conn);
std::string conn_str = this->str_for_tcp_connection(c, conn);
// Figure out which logical port the connection should go to
auto port_config_it = this->state->number_to_port_config.find(conn->server_port);
@@ -1366,7 +1366,7 @@ asio::awaitable<void> IPStackSimulator::open_server_connection(
}
const auto& port_config = port_config_it->second;
conn->server_channel = make_shared<IPSSChannel>(
conn->server_channel = std::make_shared<IPSSChannel>(
this->shared_from_this(),
c,
conn,
@@ -1389,13 +1389,13 @@ asio::awaitable<void> IPStackSimulator::open_server_connection(
}
asio::awaitable<void> IPStackSimulator::close_tcp_connection(
shared_ptr<IPSSClient> c, shared_ptr<IPSSClient::TCPConnection> conn) {
std::shared_ptr<IPSSClient> c, std::shared_ptr<IPSSClient::TCPConnection> conn) {
// Send an RST to the client. This is kind of rude (we really should use FIN) but the PSO network stack always sends
// an RST to us when disconnecting, so whatever
co_await this->send_tcp_frame(c, conn, TCPHeader::Flag::RST);
// Delete the connection object
string conn_str = this->str_for_tcp_connection(c, conn);
std::string conn_str = this->str_for_tcp_connection(c, conn);
this->log.info_f("Server closed TCP connection {}", conn_str);
c->tcp_connections.erase(this->tcp_conn_key_for_connection(conn));
}
@@ -1412,14 +1412,14 @@ std::shared_ptr<IPSSClient> IPStackSimulator::create_client(
uint64_t network_id = this->next_network_id++;
this->log.info_f("Virtual network N-{:X} connected via {}", network_id, listen_sock->name);
return make_shared<IPSSClient>(this->shared_from_this(), network_id, listen_sock->protocol, std::move(client_sock));
return std::make_shared<IPSSClient>(this->shared_from_this(), network_id, listen_sock->protocol, std::move(client_sock));
}
asio::awaitable<void> IPStackSimulator::handle_tapserver_client(std::shared_ptr<IPSSClient> c) {
for (;;) {
le_uint16_t frame_size;
co_await asio::async_read(c->sock, asio::buffer(&frame_size, sizeof(frame_size)), asio::use_awaitable);
string frame(frame_size, '\0');
std::string frame(frame_size, '\0');
co_await asio::async_read(c->sock, asio::buffer(frame.data(), frame.size()), asio::use_awaitable);
if (c->protocol == VirtualNetworkProtocol::HDLC_TAPSERVER) {
@@ -1428,7 +1428,7 @@ asio::awaitable<void> IPStackSimulator::handle_tapserver_client(std::shared_ptr<
try {
co_await this->on_client_frame(c, frame.data(), frame.size());
} catch (const exception& e) {
} catch (const std::exception& e) {
if (this->log.warning_f("Failed to process frame: {}", e.what())) {
phosg::print_data(stderr, frame);
}
@@ -1454,10 +1454,10 @@ asio::awaitable<void> IPStackSimulator::handle_hdlc_raw_client(std::shared_ptr<I
size_t frame_start_offset = 0;
while (buffer.size() > frame_start_offset) {
if (buffer[frame_start_offset] != 0x7E) {
throw runtime_error("HDLC frame does not begin with 7E");
throw std::runtime_error("HDLC frame does not begin with 7E");
}
size_t frame_end_offset = buffer.find(0x7E, frame_start_offset + 1);
if (frame_end_offset == string::npos) {
if (frame_end_offset == std::string::npos) {
break;
}
frame_end_offset++;
@@ -1468,7 +1468,7 @@ asio::awaitable<void> IPStackSimulator::handle_hdlc_raw_client(std::shared_ptr<I
try {
co_await this->on_client_frame(c, frame_data, unescaped_size);
} catch (const exception& e) {
} catch (const std::exception& e) {
if (this->log.warning_f("Failed to process frame: {}", e.what())) {
phosg::print_data(stderr, frame_data, unescaped_size);
}
@@ -1479,7 +1479,7 @@ asio::awaitable<void> IPStackSimulator::handle_hdlc_raw_client(std::shared_ptr<I
// Delete the processed packets from the beginning of the buffer
if (frame_start_offset > buffer_bytes) {
throw logic_error("frame start offset is beyond buffer bounds");
throw std::logic_error("frame start offset is beyond buffer bounds");
} else if (frame_start_offset == buffer_bytes) {
buffer_bytes = 0;
} else if (frame_start_offset > 0) {
+4 -6
View File
@@ -1,7 +1,5 @@
#include "IPV4RangeSet.hh"
using namespace std;
IPV4RangeSet::IPV4RangeSet(const phosg::JSON& json) {
for (const auto& it : json.as_list()) {
// String should be of the form a.b.c.d or a.b.c.d/e
@@ -13,22 +11,22 @@ IPV4RangeSet::IPV4RangeSet(const phosg::JSON& json) {
} else if (tokens.size() == 2) {
mask_bits = stoul(tokens[1], nullptr, 10);
if (mask_bits > 32) {
throw runtime_error("invalid IPv4 address range");
throw std::runtime_error("invalid IPv4 address range");
}
} else {
throw runtime_error("invalid IPv4 address range");
throw std::runtime_error("invalid IPv4 address range");
}
auto addr_tokens = phosg::split(tokens[0], '.');
if (addr_tokens.size() != 4) {
throw runtime_error("invalid IPv4 address");
throw std::runtime_error("invalid IPv4 address");
}
uint32_t addr = 0;
for (size_t z = 0; z < 4; z++) {
size_t end_pos = 0;
size_t new_byte = stoul(addr_tokens[z], &end_pos, 10);
if (end_pos != addr_tokens[z].size() || new_byte > 0xFF) {
throw runtime_error("invalid IPv4 address");
throw std::runtime_error("invalid IPv4 address");
}
addr = (addr << 8) | new_byte;
}
+13 -11
View File
@@ -7,8 +7,6 @@
#include "Text.hh"
using namespace std;
struct GVMFileEntry {
be_uint16_t file_num;
pstring<TextEncoding::ASCII, 0x1C> name;
@@ -35,21 +33,25 @@ struct GVRHeader {
be_uint16_t height;
} __packed_ws__(GVRHeader, 0x10);
string encode_gvm(const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, const string& internal_name, uint32_t global_index) {
std::string encode_gvm(
const phosg::ImageRGBA8888N& img,
GVRDataFormat data_format,
const std::string& internal_name,
uint32_t global_index) {
int8_t dimensions_field = -2;
{
size_t h = img.get_height();
size_t w = img.get_width();
if ((h != w) || (w & (w - 1)) || (h & (h - 1))) {
throw runtime_error("image must be square and dimensions must be powers of 2");
throw std::runtime_error("image must be square and dimensions must be powers of 2");
}
for (w >>= 1; w; w >>= 1, dimensions_field++) {
}
if (dimensions_field < 1) {
throw runtime_error("image is too small");
throw std::runtime_error("image is too small");
}
if (dimensions_field > 0xF) {
throw runtime_error("image is too large");
throw std::runtime_error("image is too large");
}
}
@@ -64,7 +66,7 @@ string encode_gvm(const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, c
pixel_bytes = pixel_count * 2;
break;
default:
throw invalid_argument("cannot encode pixel format");
throw std::invalid_argument("cannot encode pixel format");
}
phosg::StringWriter w;
@@ -102,7 +104,7 @@ string encode_gvm(const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, c
w.put_u32b(phosg::argb8888_for_rgba8888(c));
break;
default:
throw logic_error("cannot encode pixel format");
throw std::logic_error("cannot encode pixel format");
}
}
}
@@ -112,9 +114,9 @@ string encode_gvm(const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, c
return std::move(w.str());
}
static const array<uint32_t, 4> fon_colors = {0x000000FF, 0x555555FF, 0xAAAAAAFF, 0xFFFFFFFF};
static const std::array<uint32_t, 4> fon_colors = {0x000000FF, 0x555555FF, 0xAAAAAAFF, 0xFFFFFFFF};
phosg::ImageRGB888 decode_fon(const string& data, size_t width) {
phosg::ImageRGB888 decode_fon(const std::string& data, size_t width) {
size_t num_pixels = data.size() * 4;
size_t height = num_pixels / width;
phosg::ImageRGB888 ret(width, height);
@@ -132,7 +134,7 @@ constexpr size_t uabs(size_t a, size_t b) {
return (a > b) ? (a - b) : (b - a);
}
string encode_fon(const phosg::ImageRGB888& img) {
std::string encode_fon(const phosg::ImageRGB888& img) {
phosg::BitWriter w;
for (size_t y = 0; y < img.get_height(); y++) {
for (size_t x = 0; x < img.get_width(); x++) {
+57 -63
View File
@@ -19,22 +19,17 @@
#include "SaveFileFormats.hh"
#include "Text.hh"
using namespace std;
IntegralExpression::IntegralExpression(const string& text)
: root(this->parse_expr(text)) {}
IntegralExpression::IntegralExpression(const std::string& text) : root(this->parse_expr(text)) {}
IntegralExpression::BinaryOperatorNode::BinaryOperatorNode(
Type type, unique_ptr<const Node>&& left, unique_ptr<const Node>&& right)
: type(type),
left(std::move(left)),
right(std::move(right)) {}
Type type, std::unique_ptr<const Node>&& left, std::unique_ptr<const Node>&& right)
: type(type), left(std::move(left)), right(std::move(right)) {}
bool IntegralExpression::BinaryOperatorNode::operator==(const Node& other) const {
try {
const BinaryOperatorNode& other_bin = dynamic_cast<const BinaryOperatorNode&>(other);
return other_bin.type == this->type && *other_bin.left == *this->left && *other_bin.right == *this->right;
} catch (const bad_cast&) {
} catch (const std::bad_cast&) {
return false;
}
}
@@ -78,11 +73,11 @@ int64_t IntegralExpression::BinaryOperatorNode::evaluate(const Env& env) const {
case Type::MODULUS:
return this->left->evaluate(env) % this->right->evaluate(env);
default:
throw logic_error("invalid binary operator type");
throw std::logic_error("invalid binary operator type");
}
}
string IntegralExpression::BinaryOperatorNode::str() const {
std::string IntegralExpression::BinaryOperatorNode::str() const {
switch (this->type) {
case Type::LOGICAL_OR:
return "(" + this->left->str() + ") || (" + this->right->str() + ")";
@@ -121,19 +116,18 @@ string IntegralExpression::BinaryOperatorNode::str() const {
case Type::MODULUS:
return "(" + this->left->str() + ") % (" + this->right->str() + ")";
default:
throw logic_error("invalid binary operator type");
throw std::logic_error("invalid binary operator type");
}
}
IntegralExpression::UnaryOperatorNode::UnaryOperatorNode(Type type, unique_ptr<const Node>&& sub)
: type(type),
sub(std::move(sub)) {}
IntegralExpression::UnaryOperatorNode::UnaryOperatorNode(Type type, std::unique_ptr<const Node>&& sub)
: type(type), sub(std::move(sub)) {}
bool IntegralExpression::UnaryOperatorNode::operator==(const Node& other) const {
try {
const UnaryOperatorNode& other_un = dynamic_cast<const UnaryOperatorNode&>(other);
return other_un.type == this->type && *other_un.sub == *this->sub;
} catch (const bad_cast&) {
} catch (const std::bad_cast&) {
return false;
}
}
@@ -147,11 +141,11 @@ int64_t IntegralExpression::UnaryOperatorNode::evaluate(const Env& env) const {
case Type::NEGATIVE:
return -this->sub->evaluate(env);
default:
throw logic_error("invalid unary operator type");
throw std::logic_error("invalid unary operator type");
}
}
string IntegralExpression::UnaryOperatorNode::str() const {
std::string IntegralExpression::UnaryOperatorNode::str() const {
switch (this->type) {
case Type::LOGICAL_NOT:
return "!(" + this->sub->str() + ")";
@@ -160,7 +154,7 @@ string IntegralExpression::UnaryOperatorNode::str() const {
case Type::NEGATIVE:
return "-(" + this->sub->str() + ")";
default:
throw logic_error("invalid unary operator type");
throw std::logic_error("invalid unary operator type");
}
}
@@ -170,19 +164,19 @@ bool IntegralExpression::FlagLookupNode::operator==(const Node& other) const {
try {
const FlagLookupNode& other_flag = dynamic_cast<const FlagLookupNode&>(other);
return other_flag.flag_index == this->flag_index;
} catch (const bad_cast&) {
} catch (const std::bad_cast&) {
return false;
}
}
int64_t IntegralExpression::FlagLookupNode::evaluate(const Env& env) const {
if (!env.flags) {
throw runtime_error("quest flags not available");
throw std::runtime_error("quest flags not available");
}
return env.flags->get(this->flag_index) ? 1 : 0;
}
string IntegralExpression::FlagLookupNode::str() const {
std::string IntegralExpression::FlagLookupNode::str() const {
return std::format("F_{:04X}", this->flag_index);
}
@@ -193,14 +187,14 @@ bool IntegralExpression::ChallengeCompletionLookupNode::operator==(const Node& o
try {
const ChallengeCompletionLookupNode& other_cc = dynamic_cast<const ChallengeCompletionLookupNode&>(other);
return other_cc.episode == this->episode && other_cc.stage_index == this->stage_index;
} catch (const bad_cast&) {
} catch (const std::bad_cast&) {
return false;
}
}
int64_t IntegralExpression::ChallengeCompletionLookupNode::evaluate(const Env& env) const {
if (!env.challenge_records) {
throw runtime_error("challenge records not available");
throw std::runtime_error("challenge records not available");
}
if (this->episode == Episode::EP1) {
return env.challenge_records->times_ep1_online.at(this->stage_index).has_value();
@@ -210,17 +204,17 @@ int64_t IntegralExpression::ChallengeCompletionLookupNode::evaluate(const Env& e
return false;
}
string IntegralExpression::ChallengeCompletionLookupNode::str() const {
std::string IntegralExpression::ChallengeCompletionLookupNode::str() const {
return std::format("CC_{}_{}", abbreviation_for_episode(this->episode), static_cast<uint8_t>(this->stage_index + 1));
}
IntegralExpression::TeamRewardLookupNode::TeamRewardLookupNode(const string& reward_name) : reward_name(reward_name) {}
IntegralExpression::TeamRewardLookupNode::TeamRewardLookupNode(const std::string& reward_name) : reward_name(reward_name) {}
bool IntegralExpression::TeamRewardLookupNode::operator==(const Node& other) const {
try {
const TeamRewardLookupNode& other_team_reward = dynamic_cast<const TeamRewardLookupNode&>(other);
return other_team_reward.reward_name == this->reward_name;
} catch (const bad_cast&) {
} catch (const std::bad_cast&) {
return false;
}
}
@@ -229,7 +223,7 @@ int64_t IntegralExpression::TeamRewardLookupNode::evaluate(const Env& env) const
return (env.team && env.team->has_reward(this->reward_name)) ? 1 : 0;
}
string IntegralExpression::TeamRewardLookupNode::str() const {
std::string IntegralExpression::TeamRewardLookupNode::str() const {
return "T_" + this->reward_name;
}
@@ -243,7 +237,7 @@ int64_t IntegralExpression::NumPlayersLookupNode::evaluate(const Env& env) const
return env.num_players;
}
string IntegralExpression::NumPlayersLookupNode::str() const {
std::string IntegralExpression::NumPlayersLookupNode::str() const {
return "V_NumPlayers";
}
@@ -257,7 +251,7 @@ int64_t IntegralExpression::EventLookupNode::evaluate(const Env& env) const {
return env.event;
}
string IntegralExpression::EventLookupNode::str() const {
std::string IntegralExpression::EventLookupNode::str() const {
return "V_Event";
}
@@ -271,7 +265,7 @@ int64_t IntegralExpression::V1PresenceLookupNode::evaluate(const Env& env) const
return env.v1_present ? 1 : 0;
}
string IntegralExpression::V1PresenceLookupNode::str() const {
std::string IntegralExpression::V1PresenceLookupNode::str() const {
return "V_V1Present";
}
@@ -282,7 +276,7 @@ bool IntegralExpression::ConstantNode::operator==(const Node& other) const {
try {
const ConstantNode& other_const = dynamic_cast<const ConstantNode&>(other);
return other_const.value == this->value;
} catch (const bad_cast&) {
} catch (const std::bad_cast&) {
return false;
}
}
@@ -291,11 +285,11 @@ int64_t IntegralExpression::ConstantNode::evaluate(const Env&) const {
return this->value;
}
string IntegralExpression::ConstantNode::str() const {
std::string IntegralExpression::ConstantNode::str() const {
return std::format("{}", this->value);
}
unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string_view text) {
std::unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(std::string_view text) {
// Strip off spaces and fully-enclosing parentheses
for (;;) {
size_t starting_size = text.size();
@@ -329,22 +323,22 @@ unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string
}
}
if (text.empty()) {
throw runtime_error("invalid expression");
throw std::runtime_error("invalid expression");
}
// Check for binary operators at the root level
using BinType = BinaryOperatorNode::Type;
static const vector<vector<pair<std::string, BinaryOperatorNode::Type>>> binary_operator_levels = {
{{make_pair("||", BinType::LOGICAL_OR)}},
{{make_pair("&&", BinType::LOGICAL_AND)}},
{{make_pair("|", BinType::BITWISE_OR)}},
{{make_pair("^", BinType::BITWISE_XOR)}},
{{make_pair("&", BinType::BITWISE_AND)}},
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
static const std::vector<std::vector<std::pair<std::string, BinaryOperatorNode::Type>>> binary_operator_levels = {
{{std::make_pair("||", BinType::LOGICAL_OR)}},
{{std::make_pair("&&", BinType::LOGICAL_AND)}},
{{std::make_pair("|", BinType::BITWISE_OR)}},
{{std::make_pair("^", BinType::BITWISE_XOR)}},
{{std::make_pair("&", BinType::BITWISE_AND)}},
{{std::make_pair("==", BinType::EQUAL)}, {std::make_pair("!=", BinType::NOT_EQUAL)}},
{{std::make_pair("<=", BinType::LESS_OR_EQUAL)}, {std::make_pair(">=", BinType::GREATER_OR_EQUAL)}, {std::make_pair("<", BinType::LESS_THAN)}, {std::make_pair(">", BinType::GREATER_THAN)}},
{{std::make_pair("<<", BinType::LEFT_SHIFT)}, {std::make_pair(">>", BinType::RIGHT_SHIFT)}},
{{std::make_pair("+", BinType::ADD)}, {std::make_pair("-", BinType::SUBTRACT)}},
{{std::make_pair("*", BinType::MULTIPLY)}, {std::make_pair("/", BinType::DIVIDE)}, {std::make_pair("%", BinType::MODULUS)}},
};
for (const auto& operators : binary_operator_levels) {
size_t paren_level = 0;
@@ -390,12 +384,12 @@ unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string
char* endptr = nullptr;
uint64_t flag = strtoul(text.data() + 2, &endptr, 16);
if (endptr != text.data() + text.size()) {
throw runtime_error("invalid flag lookup token");
throw std::runtime_error("invalid flag lookup token");
}
if (flag >= 0x400) {
throw runtime_error("invalid flag index");
throw std::runtime_error("invalid flag index");
}
return make_unique<FlagLookupNode>(flag);
return std::make_unique<FlagLookupNode>(flag);
}
if (text.starts_with("CC_")) {
Episode episode;
@@ -404,45 +398,45 @@ unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string
} else if (text.starts_with("CC_Ep2_")) {
episode = Episode::EP2;
} else {
throw runtime_error("invalid challenge episode");
throw std::runtime_error("invalid challenge episode");
}
char* endptr = nullptr;
uint64_t stage_index = strtoul(text.data() + 7, &endptr, 0) - 1;
if (endptr != text.data() + text.size()) {
throw runtime_error("invalid challenge completion lookup token");
throw std::runtime_error("invalid challenge completion lookup token");
}
if ((episode == Episode::EP1 && stage_index > 8) || (episode == Episode::EP2 && stage_index > 4)) {
throw runtime_error("invalid challenge stage index");
throw std::runtime_error("invalid challenge stage index");
}
return make_unique<ChallengeCompletionLookupNode>(episode, stage_index);
return std::make_unique<ChallengeCompletionLookupNode>(episode, stage_index);
}
if (text.starts_with("T_")) {
return make_unique<TeamRewardLookupNode>(string(text.substr(2)));
return std::make_unique<TeamRewardLookupNode>(std::string(text.substr(2)));
}
if (text == "V_NumPlayers") {
return make_unique<NumPlayersLookupNode>();
return std::make_unique<NumPlayersLookupNode>();
}
if (text == "V_Event") {
return make_unique<EventLookupNode>();
return std::make_unique<EventLookupNode>();
}
if (text == "V_V1Present") {
return make_unique<V1PresenceLookupNode>();
return std::make_unique<V1PresenceLookupNode>();
}
// Check for constants
if (text == "true") {
return make_unique<ConstantNode>(1);
return std::make_unique<ConstantNode>(1);
}
if (text == "false") {
return make_unique<ConstantNode>(0);
return std::make_unique<ConstantNode>(0);
}
try {
size_t endpos;
int64_t v = stoll(string(text), &endpos, 0);
int64_t v = std::stoll(std::string(text), &endpos, 0);
if (endpos == text.size()) {
return make_unique<ConstantNode>(v);
return std::make_unique<ConstantNode>(v);
}
} catch (const exception&) {
} catch (const std::exception&) {
}
throw runtime_error("unparseable expression");
throw std::runtime_error("unparseable expression");
}
+65 -67
View File
@@ -6,8 +6,6 @@
#include "EnemyType.hh"
#include "Loggers.hh"
using namespace std;
// The favored weapon type table is hardcoded in the game client. The table is:
// Viridia shots
// Greennill rifles
@@ -19,23 +17,23 @@ using namespace std;
// Oran daggers
// Yellowboze (none)
// Whitill slicers
static const array<uint8_t, 10> favored_weapon_by_section_id = {
static const std::array<uint8_t, 10> favored_weapon_by_section_id = {
0x09, 0x07, 0x02, 0x04, 0x08, 0x0A, 0xFF, 0x03, 0xFF, 0x05};
ItemCreator::ItemCreator(
shared_ptr<const CommonItemSet> common_item_set,
shared_ptr<const RareItemSet> rare_item_set,
shared_ptr<const ArmorRandomSet> armor_random_set,
shared_ptr<const ToolRandomSet> tool_random_set,
shared_ptr<const WeaponRandomSet> weapon_random_set,
shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set,
shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const CommonItemSet> common_item_set,
std::shared_ptr<const RareItemSet> rare_item_set,
std::shared_ptr<const ArmorRandomSet> armor_random_set,
std::shared_ptr<const ToolRandomSet> tool_random_set,
std::shared_ptr<const WeaponRandomSet> weapon_random_set,
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set,
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const ItemData::StackLimits> stack_limits,
GameMode mode,
Difficulty difficulty,
uint8_t section_id,
std::shared_ptr<RandomGenerator> rand_crypt,
shared_ptr<const BattleRules> restrictions)
std::shared_ptr<const BattleRules> restrictions)
: log(std::format("[ItemCreator:{}/{}/{}/{}] ", phosg::name_for_enum(stack_limits->version), abbreviation_for_mode(mode), abbreviation_for_difficulty(difficulty), section_id), lobby_log.min_level),
logic_version(stack_limits->version),
is_legacy_replay(false),
@@ -175,7 +173,7 @@ ItemCreator::DropResult ItemCreator::on_box_item_drop(uint8_t area, bool force_r
case 6: // Nothing
break;
default:
throw logic_error("this should be impossible");
throw std::logic_error("this should be impossible");
}
if (item_class < 6) {
this->generate_common_item_variances(res.item, area);
@@ -183,7 +181,7 @@ ItemCreator::DropResult ItemCreator::on_box_item_drop(uint8_t area, bool force_r
}
return res;
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.error_f("Exception in item creation: {}", e.what());
return DropResult();
}
@@ -233,13 +231,13 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(EnemyType enemy_type,
case 2:
try {
item_class = pt->enemy_type_item_classes.at(enemy_type);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
this->log.info_f("Item class is not set for this enemy type");
item_class = 0xFF;
}
break;
default:
throw logic_error("invalid item class determinant");
throw std::logic_error("invalid item class determinant");
}
this->log.info_f(
@@ -265,7 +263,7 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(EnemyType enemy_type,
res.item.data1[0] = 0x04;
try {
res.item.data2d = this->choose_meseta_amount(pt->enemy_type_meseta_ranges.at(enemy_type)) & 0xFFFF;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
this->log.info_f("Meseta range is not set for this enemy type");
return DropResult();
}
@@ -281,7 +279,7 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(EnemyType enemy_type,
return res;
} catch (const exception& e) {
} catch (const std::exception& e) {
this->log.error_f("Exception in item creation: {}", e.what());
return DropResult();
}
@@ -400,7 +398,7 @@ ItemData ItemCreator::create_rare_item(const ItemData& drop_item, uint8_t area)
case 4:
break;
default:
throw logic_error("invalid item class");
throw std::logic_error("invalid item class");
}
this->set_item_kill_count_if_unsealable(item);
}
@@ -416,7 +414,7 @@ void ItemCreator::generate_rare_weapon_bonuses(ItemData& item, Episode episode,
auto pt = this->pt(episode);
if (!pt->has_rare_bonus_value_prob_table) {
throw logic_error("generate_rare_weapon_bonuses called for common item table without rare bonus value probability table");
throw std::logic_error("generate_rare_weapon_bonuses called for common item table without rare bonus value probability table");
}
for (size_t z = 0; z < 6; z += 2) {
@@ -549,7 +547,7 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
item.clear();
break;
default:
throw logic_error("invalid weapon and armor mode");
throw std::logic_error("invalid weapon and armor mode");
}
break;
case 2:
@@ -580,7 +578,7 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
}
break;
default:
throw logic_error("invalid tech disk mode");
throw std::logic_error("invalid tech disk mode");
}
} else if ((item.data1[1] == 9) && this->restrictions->forbid_scape_dolls) {
this->log.info_f("Restricted: scape dolls not allowed");
@@ -594,7 +592,7 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
}
break;
default:
throw logic_error("invalid item");
throw std::logic_error("invalid item");
}
}
}
@@ -634,7 +632,7 @@ void ItemCreator::generate_common_item_variances(ItemData& item, uint8_t area) {
// Note: The original code does the following here:
// item.clear();
// item.data1[0] = 0x05;
throw logic_error("invalid item class");
throw std::logic_error("invalid item class");
}
this->clear_item_if_restricted(item);
@@ -698,7 +696,7 @@ void ItemCreator::generate_common_tool_variances(ItemData& item, uint8_t area) {
item.data1[0] = 0x03;
item.data1[1] = data.first;
item.data1[2] = data.second;
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
this->log.info_f("Tool class is missing; skipping item generation");
return;
}
@@ -787,7 +785,7 @@ void ItemCreator::generate_common_weapon_variances(ItemData& item, uint8_t area)
void ItemCreator::generate_common_weapon_grind(ItemData& item, uint8_t area, uint8_t offset_within_subtype_range) {
if (item.data1[0] == 0) {
uint8_t offset = clamp<uint8_t>(offset_within_subtype_range, 0, 3);
uint8_t offset = std::clamp<uint8_t>(offset_within_subtype_range, 0, 3);
item.data1[3] = this->get_rand_from_weighted_tables_2d_vertical(this->pt(area)->grind_prob_table, offset);
this->log.info_f("Generated grind {:02X} from offset within subtype range {:02X}", item.data1[3], offset_within_subtype_range);
}
@@ -852,7 +850,7 @@ void ItemCreator::generate_unit_stars_tables() {
case Version::BB_PATCH:
case Version::GC_EP3_NTE:
case Version::GC_EP3:
throw logic_error("ItemCreator cannot be created for Episode 3 games");
throw std::logic_error("ItemCreator cannot be created for Episode 3 games");
case Version::DC_NTE:
star_base_index = 0x124;
num_units = 0x43;
@@ -882,7 +880,7 @@ void ItemCreator::generate_unit_stars_tables() {
num_units = 0x64;
break;
default:
throw logic_error("invalid game version");
throw std::logic_error("invalid game version");
}
for (auto& vec : this->unit_results_by_star_count) {
@@ -936,7 +934,7 @@ IntT ItemCreator::get_rand_from_weighted_tables(const IntT* tables, size_t offse
rand_max += tables[x * stride + offset];
}
if (rand_max == 0) {
throw runtime_error("weighted table is empty");
throw std::runtime_error("weighted table is empty");
}
uint32_t x = this->rand_int(rand_max);
@@ -947,7 +945,7 @@ IntT ItemCreator::get_rand_from_weighted_tables(const IntT* tables, size_t offse
}
x -= table_value;
}
throw logic_error("selector was not less than rand_max");
throw std::logic_error("selector was not less than rand_max");
}
template <typename IntT, size_t X>
@@ -960,8 +958,8 @@ IntT ItemCreator::get_rand_from_weighted_tables_2d_vertical(const parray<parray<
return ItemCreator::get_rand_from_weighted_tables<IntT>(tables[0].data(), offset, Y, X);
}
vector<ItemData> ItemCreator::generate_armor_shop_contents(Episode episode, size_t player_level) {
vector<ItemData> shop;
std::vector<ItemData> ItemCreator::generate_armor_shop_contents(Episode episode, size_t player_level) {
std::vector<ItemData> shop;
this->generate_armor_shop_armors(shop, episode, player_level);
this->generate_armor_shop_shields(shop, player_level);
this->generate_armor_shop_units(shop, player_level);
@@ -984,7 +982,7 @@ size_t ItemCreator::get_table_index_for_armor_shop(
}
bool ItemCreator::shop_does_not_contain_duplicate_armor(
const vector<ItemData>& shop, const ItemData& item) {
const std::vector<ItemData>& shop, const ItemData& item) {
for (const auto& shop_item : shop) {
if ((shop_item.data1[0] == item.data1[0]) &&
(shop_item.data1[1] == item.data1[1]) &&
@@ -997,7 +995,7 @@ bool ItemCreator::shop_does_not_contain_duplicate_armor(
}
bool ItemCreator::shop_does_not_contain_duplicate_tech_disk(
const vector<ItemData>& shop, const ItemData& item) {
const std::vector<ItemData>& shop, const ItemData& item) {
for (const auto& shop_item : shop) {
if ((shop_item.data1[0] == item.data1[0]) &&
(shop_item.data1[1] == item.data1[1]) &&
@@ -1010,7 +1008,7 @@ bool ItemCreator::shop_does_not_contain_duplicate_tech_disk(
}
bool ItemCreator::shop_does_not_contain_duplicate_or_too_many_similar_weapons(
const vector<ItemData>& shop, const ItemData& item) {
const std::vector<ItemData>& shop, const ItemData& item) {
size_t similar_items = 0;
for (const auto& shop_item : shop) {
// Disallow exact matches
@@ -1029,7 +1027,7 @@ bool ItemCreator::shop_does_not_contain_duplicate_or_too_many_similar_weapons(
}
bool ItemCreator::shop_does_not_contain_duplicate_item_by_data1_0_1_2(
const vector<ItemData>& shop, const ItemData& item) {
const std::vector<ItemData>& shop, const ItemData& item) {
for (const auto& shop_item : shop) {
if ((shop_item.data1[0] == item.data1[0]) &&
(shop_item.data1[1] == item.data1[1]) &&
@@ -1040,7 +1038,7 @@ bool ItemCreator::shop_does_not_contain_duplicate_item_by_data1_0_1_2(
return true;
}
void ItemCreator::generate_armor_shop_armors(vector<ItemData>& shop, Episode episode, size_t player_level) {
void ItemCreator::generate_armor_shop_armors(std::vector<ItemData>& shop, Episode episode, size_t player_level) {
size_t num_items;
if (player_level < 11) {
num_items = 4;
@@ -1084,7 +1082,7 @@ void ItemCreator::generate_armor_shop_armors(vector<ItemData>& shop, Episode epi
}
}
void ItemCreator::generate_armor_shop_shields(vector<ItemData>& shop, size_t player_level) {
void ItemCreator::generate_armor_shop_shields(std::vector<ItemData>& shop, size_t player_level) {
size_t num_items;
if (player_level < 11) {
num_items = 4;
@@ -1127,7 +1125,7 @@ void ItemCreator::generate_armor_shop_shields(vector<ItemData>& shop, size_t pla
}
}
void ItemCreator::generate_armor_shop_units(vector<ItemData>& shop, size_t player_level) {
void ItemCreator::generate_armor_shop_units(std::vector<ItemData>& shop, size_t player_level) {
size_t num_items;
if (player_level < 11) {
return; // num_items = 0
@@ -1161,8 +1159,8 @@ void ItemCreator::generate_armor_shop_units(vector<ItemData>& shop, size_t playe
}
}
vector<ItemData> ItemCreator::generate_tool_shop_contents(size_t player_level) {
vector<ItemData> shop;
std::vector<ItemData> ItemCreator::generate_tool_shop_contents(size_t player_level) {
std::vector<ItemData> shop;
this->generate_common_tool_shop_recovery_items(shop, player_level);
this->generate_rare_tool_shop_recovery_items(shop, player_level);
this->generate_tool_shop_tech_disks(shop, player_level);
@@ -1184,11 +1182,11 @@ size_t ItemCreator::get_table_index_for_tool_shop(size_t player_level) {
}
}
static const vector<pair<uint8_t, uint8_t>> tool_item_defs{
static const std::vector<std::pair<uint8_t, uint8_t>> tool_item_defs{
{0x00, 0x00}, {0x00, 0x01}, {0x00, 0x02}, {0x01, 0x00}, {0x01, 0x01}, {0x01, 0x02}, {0x06, 0x00}, {0x06, 0x01},
{0x03, 0x00}, {0x04, 0x00}, {0x05, 0x00}, {0x07, 0x00}, {0x08, 0x00}, {0x09, 0x00}, {0x0A, 0x00}, {0xFF, 0xFF}};
void ItemCreator::generate_common_tool_shop_recovery_items(vector<ItemData>& shop, size_t player_level) {
void ItemCreator::generate_common_tool_shop_recovery_items(std::vector<ItemData>& shop, size_t player_level) {
size_t table_index;
if (player_level < 11) {
table_index = 0;
@@ -1218,7 +1216,7 @@ void ItemCreator::generate_common_tool_shop_recovery_items(vector<ItemData>& sho
}
}
void ItemCreator::generate_rare_tool_shop_recovery_items(vector<ItemData>& shop, size_t player_level) {
void ItemCreator::generate_rare_tool_shop_recovery_items(std::vector<ItemData>& shop, size_t player_level) {
if (player_level < 11) {
return;
}
@@ -1256,7 +1254,7 @@ void ItemCreator::generate_rare_tool_shop_recovery_items(vector<ItemData>& shop,
}
}
void ItemCreator::generate_tool_shop_tech_disks(vector<ItemData>& shop, size_t player_level) {
void ItemCreator::generate_tool_shop_tech_disks(std::vector<ItemData>& shop, size_t player_level) {
size_t num_items;
if (player_level < 11) {
num_items = 4;
@@ -1278,7 +1276,7 @@ void ItemCreator::generate_tool_shop_tech_disks(vector<ItemData>& shop, size_t p
}
pt.shuffle(this->rand_crypt);
static const array<uint8_t, 0x13> tech_num_map = {
static const std::array<uint8_t, 0x13> tech_num_map = {
0x00, 0x03, 0x06, 0x0F, 0x10, 0x0D, 0x0A, 0x0B, 0x0C, 0x01, 0x04, 0x07,
0x0E, 0x11, 0x02, 0x05, 0x08, 0x09, 0x12};
@@ -1301,7 +1299,7 @@ void ItemCreator::choose_tech_disk_level_for_tool_shop(ItemData& item, size_t pl
size_t table_index = this->get_table_index_for_tool_shop(player_level);
auto table = this->tool_random_set->get_tech_disk_level_table(table_index);
if (tech_num_index >= table.second) {
throw runtime_error("technique number out of range");
throw std::runtime_error("technique number out of range");
}
const auto& e = table.first[tech_num_index];
@@ -1310,23 +1308,23 @@ void ItemCreator::choose_tech_disk_level_for_tool_shop(ItemData& item, size_t pl
item.data1[2] = 0;
break;
case ToolRandomSet::TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR:
item.data1[2] = clamp<ssize_t>(
(min<size_t>(player_level, 99) / e.player_level_divisor_or_min_level) - 1, 0, 14);
item.data1[2] = std::clamp<ssize_t>(
(std::min<size_t>(player_level, 99) / e.player_level_divisor_or_min_level) - 1, 0, 14);
break;
case ToolRandomSet::TechDiskLevelEntry::Mode::RANDOM_IN_RANGE: {
// Note: This logic does not give a uniform distribution - if the minimumlevel is not zero (level 1), then the
// minimum level is more likely than all the other levels. This behavior matches the client's logic, though it's
// unclear if this nonuniformity was intentional.
int16_t min_level = max<int16_t>(e.player_level_divisor_or_min_level - 1, 0);
item.data1[2] = clamp<int16_t>(this->rand_int(e.max_level), min_level, 14);
int16_t min_level = std::max<int16_t>(e.player_level_divisor_or_min_level - 1, 0);
item.data1[2] = std::clamp<int16_t>(this->rand_int(e.max_level), min_level, 14);
break;
}
default:
throw logic_error("invalid tech disk level mode");
throw std::logic_error("invalid tech disk level mode");
}
}
vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level) {
std::vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level) {
size_t num_items;
if (player_level < 11) {
num_items = 10;
@@ -1377,13 +1375,13 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
}
pt.shuffle(this->rand_crypt);
vector<ItemData> shop;
std::vector<ItemData> shop;
while (shop.size() < num_items) {
ItemData item;
uint8_t which = pt.pop();
if (which == 0x39) {
static const vector<pair<uint8_t, uint8_t>> defs{
static const std::vector<std::pair<uint8_t, uint8_t>> defs{
{0x28, 0x00}, {0x2A, 0x00}, {0x2B, 0x00}, {0x35, 0x00}, {0x52, 0x00}, {0x48, 0x00}, {0x64, 0x00},
{0x59, 0x00}, {0x8A, 0x00}, {0x99, 0x00}};
const auto& def = defs.at(this->section_id);
@@ -1392,7 +1390,7 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
item.data1[2] = def.second;
} else if (which == 0x3A) {
static const vector<pair<uint8_t, uint8_t>> defs{
static const std::vector<std::pair<uint8_t, uint8_t>> defs{
{0x99, 0x00}, {0x64, 0x00}, {0x8A, 0x00}, {0x28, 0x00}, {0x59, 0x00}, {0x2B, 0x00}, {0x52, 0x00},
{0x2A, 0x00}, {0x48, 0x00}, {0x35, 0x00}};
const auto& def = defs.at(this->section_id);
@@ -1401,7 +1399,7 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
item.data1[2] = def.second;
} else {
static const vector<pair<uint8_t, uint8_t>> defs({
static const std::vector<std::pair<uint8_t, uint8_t>> defs({
/* 00 */ {0x01, 0x00},
/* 01 */ {0x01, 0x01},
/* 02 */ {0x01, 0x02},
@@ -1520,7 +1518,7 @@ void ItemCreator::generate_weapon_shop_item_grind(ItemData& item, size_t player_
: this->weapon_random_set->get_standard_grind_range(table_index);
const auto& weapon_def = this->item_parameter_table->get_weapon(item.data1[1], item.data1[2]);
item.data1[3] = clamp<uint8_t>(this->rand_int(range->max + 1), range->min, weapon_def.max_grind);
item.data1[3] = std::clamp<uint8_t>(this->rand_int(range->max + 1), range->min, weapon_def.max_grind);
}
void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t player_level) {
@@ -1566,11 +1564,11 @@ void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t playe
item.data1[4] = this->choose_weapon_special(1);
break;
default:
throw runtime_error("invalid special mode");
throw std::runtime_error("invalid special mode");
}
}
static const array<int8_t, 20> bonus_values = {
static const std::array<int8_t, 20> bonus_values = {
-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player_level) {
@@ -1611,7 +1609,7 @@ void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player
item.data1[7] = 0;
} else {
const auto* range = this->weapon_random_set->get_bonus_range(0, table_index);
item.data1[7] = bonus_values.at(max<size_t>(this->rand_int(range->max + 1), range->min));
item.data1[7] = bonus_values.at(std::max<size_t>(this->rand_int(range->max + 1), range->min));
}
}
@@ -1655,7 +1653,7 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player
item.data1[9] = 0;
} else {
const auto* range = this->weapon_random_set->get_bonus_range(1, table_index);
item.data1[9] = bonus_values.at(max<size_t>(this->rand_int(range->max + 1), range->min));
item.data1[9] = bonus_values.at(std::max<size_t>(this->rand_int(range->max + 1), range->min));
}
}
@@ -1707,7 +1705,7 @@ ItemData ItemCreator::base_item_for_specialized_box(uint32_t param4, uint32_t pa
item.data2d = ((param5 >> 0x10) & 0xFFFF) * 10;
break;
default:
throw runtime_error("invalid item class");
throw std::runtime_error("invalid item class");
}
return item;
@@ -1715,10 +1713,10 @@ ItemData ItemCreator::base_item_for_specialized_box(uint32_t param4, uint32_t pa
ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
if (item.data1[0] != 0) {
throw runtime_error("tekker deltas can only be applied to weapons");
throw std::runtime_error("tekker deltas can only be applied to weapons");
}
static const array<int8_t, 11> delta_table = {-10, -5, -3, -2, -1, 0, 1, 2, 3, 5, 10};
static const std::array<int8_t, 11> delta_table = {-10, -5, -3, -2, -1, 0, 1, 2, 3, 5, 10};
bool favored = item.data1[1] == favored_weapon_by_section_id[section_id];
ssize_t luck = 0;
@@ -1751,7 +1749,7 @@ ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
this->log.info_f("(Special) Delta canceled because it would change special category");
}
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
// Invalid special number passed to get_special; just ignore it
}
luck += this->tekker_adjustment_set->get_luck_for_special_upgrade(delta_index);
@@ -1766,7 +1764,7 @@ ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
int8_t delta = delta_table.at(delta_index);
this->log.info_f("(Grind) Delta index {}, delta {}", delta_index, delta);
int16_t new_grind = static_cast<int16_t>(item.data1[3]) + static_cast<int16_t>(delta);
item.data1[3] = clamp<int16_t>(new_grind, 0, weapon_def.max_grind);
item.data1[3] = std::clamp<int16_t>(new_grind, 0, weapon_def.max_grind);
luck += this->tekker_adjustment_set->get_luck_for_grind_delta(delta_index);
this->log.info_f("(Grind) Luck is now {}", luck);
} else {
@@ -1785,7 +1783,7 @@ ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
// to check here if each bonus is actually present.
for (size_t z = 6; z <= 10; z += 2) {
if (item.data1[z] >= 1 && item.data1[z] <= 5) {
item.data1[z + 1] = min<int8_t>(item.data1[z + 1] + delta, 100);
item.data1[z + 1] = std::min<int8_t>(item.data1[z + 1] + delta, 100);
}
}
luck += this->tekker_adjustment_set->get_luck_for_bonus_delta(delta_index);
+30 -32
View File
@@ -6,11 +6,9 @@
#include "ItemParameterTable.hh"
#include "StaticGameData.hh"
using namespace std;
const vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_DC_NTE({10});
const vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V1_V2({10, 10, 1, 10, 10, 10, 10, 10, 10, 1});
const vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V3_V4(
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_DC_NTE({10});
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V1_V2({10, 10, 1, 10, 10, 10, 10, 10, 10, 1});
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V3_V4(
{10, 10, 1, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 99, 1});
const ItemData::StackLimits ItemData::StackLimits::DEFAULT_STACK_LIMITS_DC_NTE(
@@ -21,7 +19,7 @@ const ItemData::StackLimits ItemData::StackLimits::DEFAULT_STACK_LIMITS_V3_V4(
Version::GC_V3, ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V3_V4, 999999);
ItemData::StackLimits::StackLimits(
Version version, const vector<uint8_t>& max_tool_stack_sizes_by_data1_1, uint32_t max_meseta_stack_size)
Version version, const std::vector<uint8_t>& max_tool_stack_sizes_by_data1_1, uint32_t max_meseta_stack_size)
: version(version),
max_tool_stack_sizes_by_data1_1(max_tool_stack_sizes_by_data1_1),
max_meseta_stack_size(max_meseta_stack_size) {}
@@ -40,7 +38,7 @@ uint8_t ItemData::StackLimits::get(uint8_t data1_0, uint8_t data1_1) const {
}
if (data1_0 == 3) {
const auto& vec = this->max_tool_stack_sizes_by_data1_1;
return vec.at(min<size_t>(data1_1, vec.size() - 1));
return vec.at(std::min<size_t>(data1_1, vec.size() - 1));
}
return 1;
}
@@ -181,7 +179,7 @@ bool ItemData::is_wrapped(const StackLimits& limits) const {
case 4:
return false;
default:
throw runtime_error("invalid item data");
throw std::runtime_error("invalid item data");
}
}
@@ -206,7 +204,7 @@ void ItemData::wrap(const StackLimits& limits, uint8_t present_color) {
case 4:
break;
default:
throw runtime_error("invalid item data");
throw std::runtime_error("invalid item data");
}
}
@@ -227,7 +225,7 @@ void ItemData::unwrap(const StackLimits& limits) {
case 4:
break;
default:
throw runtime_error("invalid item data");
throw std::runtime_error("invalid item data");
}
}
@@ -309,7 +307,7 @@ uint16_t ItemData::compute_mag_strength_flags() const {
ret |= 0x020;
}
uint16_t highest = max<uint16_t>(dex, max<uint16_t>(pow, mind));
uint16_t highest = std::max<uint16_t>(dex, std::max<uint16_t>(pow, mind));
if ((pow == highest) + (dex == highest) + (mind == highest) > 1) {
ret |= 0x100;
}
@@ -343,10 +341,10 @@ uint8_t ItemData::mag_photon_blast_for_slot(uint8_t slot) const {
left_pb_num--;
}
}
throw logic_error("failed to find unused photon blast number");
throw std::logic_error("failed to find unused photon blast number");
} else {
throw logic_error("invalid slot index");
throw std::logic_error("invalid slot index");
}
}
@@ -387,7 +385,7 @@ void ItemData::add_mag_photon_blast(uint8_t pb_num) {
pb_num--;
}
if (pb_num >= 4) {
throw runtime_error("left photon blast number is too high");
throw std::runtime_error("left photon blast number is too high");
}
pb_nums |= (pb_num << 6);
flags |= 4;
@@ -471,11 +469,11 @@ void ItemData::decode_for_version(Version from_version) {
break;
default:
throw runtime_error("invalid item class");
throw std::runtime_error("invalid item class");
}
}
void ItemData::encode_for_version(Version to_version, shared_ptr<const ItemParameterTable> item_parameter_table) {
void ItemData::encode_for_version(Version to_version, std::shared_ptr<const ItemParameterTable> item_parameter_table) {
bool should_encode_v2_data = item_parameter_table &&
(is_v1(to_version) || is_v2(to_version)) &&
(to_version != Version::GC_NTE) &&
@@ -501,7 +499,7 @@ void ItemData::encode_for_version(Version to_version, shared_ptr<const ItemParam
break;
case 0x01: {
static const array<uint8_t, 4> armor_limits = {0x00, 0x29, 0x27, 0x44};
static const std::array<uint8_t, 4> armor_limits = {0x00, 0x29, 0x27, 0x44};
if (should_encode_v2_data && (this->data1[2] >= armor_limits[this->data1[1]])) {
this->data1[3] = this->data1[2];
this->data1[2] = 0x00;
@@ -554,7 +552,7 @@ void ItemData::encode_for_version(Version to_version, shared_ptr<const ItemParam
break;
default:
throw runtime_error("invalid item class");
throw std::runtime_error("invalid item class");
}
}
@@ -658,7 +656,7 @@ bool ItemData::has_bonuses() const {
case 3:
return (this->get_unit_bonus() > 0);
default:
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
}
case 2:
if (this->data1[1] < 0x23) {
@@ -670,7 +668,7 @@ bool ItemData::has_bonuses() const {
case 4:
return false;
default:
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
}
}
@@ -703,7 +701,7 @@ EquipSlot ItemData::default_equip_slot() const {
case 0x02:
return EquipSlot::MAG;
}
throw runtime_error("item cannot be equipped");
throw std::runtime_error("item cannot be equipped");
}
bool ItemData::can_be_equipped_in_slot(EquipSlot slot) const {
@@ -722,7 +720,7 @@ bool ItemData::can_be_equipped_in_slot(EquipSlot slot) const {
case EquipSlot::WEAPON:
return (this->data1[0] == 0x00);
default:
throw runtime_error("invalid equip slot");
throw std::runtime_error("invalid equip slot");
}
}
@@ -748,23 +746,23 @@ bool ItemData::compare_for_sort(const ItemData& a, const ItemData& b) {
return false;
}
ItemData ItemData::from_data(const string& data) {
ItemData ItemData::from_data(const std::string& data) {
if (data.size() < 2) {
throw runtime_error("data is too short");
throw std::runtime_error("data is too short");
}
if (data.size() > 0x10) {
throw runtime_error("data is too long");
throw std::runtime_error("data is too long");
}
ItemData ret;
for (size_t z = 0; z < min<size_t>(data.size(), 12); z++) {
for (size_t z = 0; z < std::min<size_t>(data.size(), 12); z++) {
ret.data1[z] = data[z];
}
for (size_t z = 12; z < min<size_t>(data.size(), 16); z++) {
for (size_t z = 12; z < std::min<size_t>(data.size(), 16); z++) {
ret.data2[z - 12] = data[z];
}
if (ret.data1[0] > 4) {
throw runtime_error("invalid item class");
throw std::runtime_error("invalid item class");
}
return ret;
}
@@ -772,7 +770,7 @@ ItemData ItemData::from_data(const string& data) {
ItemData ItemData::from_primary_identifier(const StackLimits& limits, uint32_t primary_identifier) {
ItemData ret;
if (primary_identifier > 0x04000000) {
throw runtime_error("invalid item class");
throw std::runtime_error("invalid item class");
}
ret.data1[0] = (primary_identifier >> 24) & 0xFF;
ret.data1[1] = (primary_identifier >> 16) & 0xFF;
@@ -786,16 +784,16 @@ ItemData ItemData::from_primary_identifier(const StackLimits& limits, uint32_t p
return ret;
}
string ItemData::hex() const {
std::string ItemData::hex() const {
return std::format("{:08X} {:08X} {:08X} ({:08X}) {:08X}",
this->data1db[0], this->data1db[1], this->data1db[2], this->id, this->data2db);
}
string ItemData::short_hex() const {
std::string ItemData::short_hex() const {
auto ret = std::format("{:08X}{:08X}{:08X}{:08X}",
this->data1db[0], this->data1db[1], this->data1db[2], this->data2db);
size_t offset = ret.find_last_not_of('0');
if (offset != string::npos) {
if (offset != std::string::npos) {
offset += (offset & 1) ? 1 : 2;
offset = std::max<size_t>(offset, 6);
if (offset < ret.size()) {
+59 -62
View File
@@ -4,8 +4,6 @@
#include "StaticGameData.hh"
using namespace std;
ItemNameIndex::ItemNameIndex(
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const ItemData::StackLimits> limits,
@@ -19,17 +17,17 @@ ItemNameIndex::ItemNameIndex(
continue;
}
const string* name = nullptr;
const std::string* name = nullptr;
bool is_es_weapon = false;
try {
ItemData item = ItemData::from_primary_identifier(*this->limits, primary_identifier);
is_es_weapon = item.is_s_rank_weapon();
name = &name_coll.at(item_parameter_table->get_item_id(item));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (name) {
auto meta = make_shared<ItemMetadata>();
auto meta = std::make_shared<ItemMetadata>();
meta->primary_identifier = primary_identifier;
meta->name = *name;
this->primary_identifier_index.emplace(meta->primary_identifier, meta);
@@ -44,7 +42,7 @@ ItemNameIndex::ItemNameIndex(
static std::string s_rank_name_characters("\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_", 0x20);
// clang-format off
static const array<const char*, 0x29> name_for_weapon_special = {
static const std::array<const char*, 0x29> name_for_weapon_special = {
nullptr,
"Draw", // Type: 0001, amount: 0005
"Drain", // Type: 0001, amount: 0009
@@ -89,7 +87,7 @@ static const array<const char*, 0x29> name_for_weapon_special = {
};
// clang-format on
const array<const char*, 0x11> name_for_s_rank_special = {
const std::array<const char*, 0x11> name_for_s_rank_special = {
nullptr,
"Jellen",
"Zalure",
@@ -117,7 +115,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
return std::format("{}{} Meseta", include_color_escapes ? "$C7" : "", item.data2d);
}
vector<string> ret_tokens;
std::vector<std::string> ret_tokens;
// For weapons, specials appear before the weapon name
bool is_unidentified = false;
@@ -135,7 +133,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
if (special_id) {
try {
ret_tokens.emplace_back(name_for_weapon_special.at(special_id));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
ret_tokens.emplace_back(std::format("!SP:{:02X}", special_id));
}
}
@@ -143,7 +141,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
if (!name_only && (item.data1[0] == 0x00) && (item.data1[2] != 0x00) && item.is_s_rank_weapon()) {
try {
ret_tokens.emplace_back(name_for_s_rank_special.at(item.data1[2]));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
ret_tokens.emplace_back(std::format("!SSP:{:02X}", item.data1[2]));
}
}
@@ -160,11 +158,11 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
// Add the item name
uint32_t primary_identifier = item.primary_identifier();
if ((primary_identifier & 0xFFFF0000) == 0x03020000) {
string technique_name;
std::string technique_name;
try {
technique_name = tech_id_to_name.at(item.data1[4]);
technique_name[0] = toupper(technique_name[0]);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
technique_name = std::format("!TD:{:02X}", item.data1[4]);
}
// Hide the level for Reverser and Ryuker, unless the level isn't 1
@@ -177,7 +175,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
try {
auto meta = this->primary_identifier_index.at(primary_identifier);
ret_tokens.emplace_back(meta->name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
ret_tokens.emplace_back(std::format("!ID:{:08X}", primary_identifier));
}
}
@@ -208,7 +206,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
static_cast<uint8_t>(be_data1w5 & 0x1F),
};
string name;
std::string name;
for (size_t x = 0; x < 8; x++) {
char ch = s_rank_name_characters.at(char_indexes[x]);
if (ch == 0) {
@@ -306,7 +304,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
uint16_t pow = item.data1w[3];
uint16_t dex = item.data1w[4];
uint16_t mind = item.data1w[5];
auto format_stat = +[](uint16_t stat) -> string {
auto format_stat = +[](uint16_t stat) -> std::string {
uint16_t level = stat / 100;
uint8_t partial = stat % 100;
if (partial == 0) {
@@ -323,7 +321,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
uint8_t flags = item.data2[2];
if (flags & 7) {
static const vector<const char*> pb_shortnames = {"F", "E", "G", "P", "L", "M&Y", "MG", "GR"};
static const std::vector<const char*> pb_shortnames = {"F", "E", "G", "P", "L", "M&Y", "MG", "GR"};
const char* pb_names[3] = {nullptr, nullptr, nullptr};
uint8_t left_pb = item.mag_photon_blast_for_slot(2);
@@ -339,7 +337,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
pb_names[2] = pb_shortnames[right_pb];
}
string token = "PB:";
std::string token = "PB:";
for (size_t x = 0; x < 3; x++) {
if (pb_names[x] == nullptr) {
continue;
@@ -354,7 +352,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
try {
ret_tokens.emplace_back(std::format("({})", name_for_mag_color.at(item.data2[3])));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
ret_tokens.emplace_back(std::format("(!CL:{:02X})", item.data2[3]));
}
@@ -365,7 +363,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) co
}
}
string ret = phosg::join(ret_tokens, " ");
std::string ret = phosg::join(ret_tokens, " ");
if (include_color_escapes) {
if (is_unidentified) {
return "$C3" + ret;
@@ -387,18 +385,18 @@ ItemData ItemNameIndex::parse_item_description(const std::string& desc) const {
ItemData ret;
try {
ret = this->parse_item_description_phase(desc, false);
} catch (const exception& e1) {
} catch (const std::exception& e1) {
try {
ret = this->parse_item_description_phase(desc, true);
} catch (const exception& e2) {
} catch (const std::exception& e2) {
try {
ret = ItemData::from_data(phosg::parse_data_string(desc));
} catch (const exception& ed) {
} catch (const std::exception& ed) {
if (strcmp(e1.what(), e2.what())) {
throw runtime_error(std::format("cannot parse item description \"{}\" (as text 1: {}) (as text 2: {}) (as data: {})",
throw std::runtime_error(std::format("cannot parse item description \"{}\" (as text 1: {}) (as text 2: {}) (as data: {})",
desc, e1.what(), e2.what(), ed.what()));
} else {
throw runtime_error(std::format("cannot parse item description \"{}\" (as text: {}) (as data: {})",
throw std::runtime_error(std::format("cannot parse item description \"{}\" (as text: {}) (as data: {})",
desc, e1.what(), ed.what()));
}
}
@@ -414,17 +412,17 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.id = 0xFFFFFFFF;
ret.data2d = 0;
string desc = phosg::tolower(description);
std::string desc = phosg::tolower(description);
if (desc.ends_with(" meseta")) {
ret.data1[0] = 0x04;
ret.data2d = stol(desc, nullptr, 10);
ret.data2d = std::stol(desc, nullptr, 10);
return ret;
}
if (desc.starts_with("es ")) {
auto parse_name = [&](const std::string& token) -> void {
if (token.size() > 8) {
throw runtime_error("s-rank name too long");
throw std::runtime_error("s-rank name too long");
}
uint8_t char_indexes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@@ -432,7 +430,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
char ch = toupper(token[z]);
size_t pos = s_rank_name_characters.find(ch);
if (pos == std::string::npos) {
throw runtime_error(std::format("s-rank name contains invalid character {:02X} ({})", ch, ch));
throw std::runtime_error(std::format("s-rank name contains invalid character {:02X} ({})", ch, ch));
}
char_indexes[z] = pos;
}
@@ -538,10 +536,10 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.data1[4] = tech;
} else {
if (tokens.size() != 2) {
throw runtime_error("invalid tech disk format");
throw std::runtime_error("invalid tech disk format");
}
if (!tokens[1].starts_with("lv.")) {
throw runtime_error("invalid tech disk level");
throw std::runtime_error("invalid tech disk level");
}
uint8_t tech = technique_for_name(tokens[0]);
uint8_t level = stoul(tokens[1].substr(3), nullptr, 10) - 1;
@@ -574,7 +572,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
if (!name_for_weapon_special[z]) {
continue;
}
string prefix = phosg::tolower(name_for_weapon_special[z]);
std::string prefix = phosg::tolower(name_for_weapon_special[z]);
prefix += ' ';
if (desc.starts_with(prefix)) {
weapon_special = z;
@@ -592,14 +590,14 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
if (name_it != this->name_index.end() && desc.starts_with(name_it->first)) {
break;
} else if (name_it == this->name_index.begin()) {
throw runtime_error("no such item");
throw std::runtime_error("no such item");
} else {
name_it--;
lookback++;
}
}
if (lookback >= 4) {
throw runtime_error("item not found: " + desc);
throw std::runtime_error("item not found: " + desc);
}
desc = desc.substr(name_it->first.size());
@@ -637,7 +635,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
} else {
auto p_tokens = phosg::split(token, '/');
if (p_tokens.size() > 5) {
throw runtime_error("invalid bonuses token");
throw std::runtime_error("invalid bonuses token");
}
uint8_t max_bonuses = this->item_parameter_table->is_unsealable_item(ret) ? 2 : 3;
uint8_t bonus_index = 0;
@@ -647,7 +645,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
continue;
}
if (bonus_index >= max_bonuses) {
throw runtime_error("weapon has too many bonuses");
throw std::runtime_error("weapon has too many bonuses");
}
ret.data1[6 + (2 * bonus_index)] = z + 1;
ret.data1[7 + (2 * bonus_index)] = static_cast<uint8_t>(bonus_value);
@@ -662,7 +660,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
} else if (ret.data1[0] == 0x01) {
if (ret.data1[1] == 0x03) { // Unit
static const unordered_map<string, uint16_t> modifiers({
static const std::unordered_map<std::string, uint16_t> modifiers({
{"--", 0xFFFC},
{"-", 0xFFFE},
{"", 0x0000},
@@ -698,7 +696,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
if (token.empty()) {
continue;
} else if (!token.starts_with("+")) {
throw runtime_error("invalid armor/shield modifier");
throw std::runtime_error("invalid armor/shield modifier");
}
if (token.ends_with("def")) {
ret.data1w[3] = static_cast<uint16_t>(stol(token.substr(1, token.size() - 4), nullptr, 10));
@@ -721,9 +719,9 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
} else if (token.starts_with("pb:")) { // Photon blasts
auto pb_tokens = phosg::split(token.substr(3), ',');
if (pb_tokens.size() > 3) {
throw runtime_error("too many photon blasts specified");
throw std::runtime_error("too many photon blasts specified");
}
static const unordered_map<string, uint8_t> name_to_pb_num(
static const std::unordered_map<std::string, uint8_t> name_to_pb_num(
{{"f", 0}, {"e", 1}, {"g", 2}, {"p", 3}, {"l", 4}, {"m", 5}, {"my", 5}, {"m&y", 5}});
for (const auto& pb_token : pb_tokens) {
ret.add_mag_photon_blast(name_to_pb_num.at(pb_token));
@@ -735,12 +733,12 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
} else if (!token.empty() && isdigit(token[0])) { // Stats
auto s_tokens = phosg::split(token, '/');
if (s_tokens.size() != 4) {
throw runtime_error("incorrect stat count");
throw std::runtime_error("incorrect stat count");
}
for (size_t z = 0; z < 4; z++) {
auto n_tokens = phosg::split(s_tokens[z], '.');
if (n_tokens.size() == 0 || n_tokens.size() > 2) {
throw logic_error("incorrect stats argument format");
throw std::logic_error("incorrect stats argument format");
} else if ((n_tokens.size() == 1) || (n_tokens[1].size() == 0)) {
ret.data1w[z + 2] = stoul(n_tokens[0], nullptr, 10) * 100;
} else if (n_tokens[1].size() == 1) {
@@ -748,7 +746,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
} else if (n_tokens[1].size() == 2) {
ret.data1w[z + 2] = stoul(n_tokens[0], nullptr, 10) * 100 + stoul(n_tokens[1], nullptr, 10);
} else {
throw runtime_error("incorrect stat format");
throw std::runtime_error("incorrect stat format");
}
}
ret.data1[2] = ret.compute_mag_level();
@@ -768,18 +766,18 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.data1[5] = 1;
}
} else if (!desc.empty()) {
throw runtime_error("item cannot be stacked");
throw std::runtime_error("item cannot be stacked");
}
if (is_wrapped) {
if (ret.is_stackable(*this->limits)) {
throw runtime_error("stackable items cannot be wrapped");
throw std::runtime_error("stackable items cannot be wrapped");
} else {
ret.data1[3] |= 0x40;
}
}
} else {
throw logic_error("invalid item class");
throw std::logic_error("invalid item class");
}
return ret;
@@ -793,7 +791,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes(); data1_1++) {
uint8_t weapon_class = pmt->get_weapon_kind(data1_1);
float sale_divisor = pmt->get_sale_divisor(0x00, data1_1);
string divisor_str = std::format("{:g}", sale_divisor);
std::string divisor_str = std::format("{:g}", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_weapons_in_class(data1_1);
@@ -806,11 +804,11 @@ void ItemNameIndex::print_table(FILE* stream) const {
item.data1[0] = 0x00;
item.data1[1] = data1_1;
item.data1[2] = data1_2;
string name = this->describe_item(item);
std::string name = this->describe_item(item);
const auto& stat_boost = pmt->get_stat_boost(w.stat_boost_entry_index);
string tech_boost_str;
std::string tech_boost_str;
if (w.tech_boost_entry_index < pmt->num_tech_boosts()) {
const auto& tech_boost = pmt->get_tech_boost(w.tech_boost_entry_index);
tech_boost_str = std::format("({:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g})",
@@ -874,7 +872,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS -DFP- -EVP- BP BE FLAG LVL EFR ETH EIC EDK ELT DFR EVR SB(S1:AMT1,S2:AMT2) TB(TN:FL:AMOUNT, ... ) FT A4 ST* ---DIVISOR--- NAME\n");
for (size_t data1_1 = 1; data1_1 < 3; data1_1++) {
float sale_divisor = pmt->get_sale_divisor(0x01, data1_1);
string divisor_str = std::format("{:g}", sale_divisor);
std::string divisor_str = std::format("{:g}", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_armors_or_shields_in_class(data1_1);
@@ -886,11 +884,11 @@ void ItemNameIndex::print_table(FILE* stream) const {
item.data1[0] = 0x01;
item.data1[1] = data1_1;
item.data1[2] = data1_2;
string name = this->describe_item(item);
std::string name = this->describe_item(item);
auto& stat_boost = pmt->get_stat_boost(a.stat_boost_entry_index);
string tech_boost_str;
std::string tech_boost_str;
if (a.tech_boost_entry_index < pmt->num_tech_boosts()) {
const auto& tech_boost = pmt->get_tech_boost(a.tech_boost_entry_index);
tech_boost_str = std::format("({:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g})",
@@ -938,7 +936,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS STAT COUNT ST-MOD ST* ---DIVISOR--- NAME\n");
{
float sale_divisor = pmt->get_sale_divisor(0x01, 0x03);
string divisor_str = std::format("{:g}", sale_divisor);
std::string divisor_str = std::format("{:g}", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_units();
@@ -950,7 +948,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
item.data1[0] = 0x01;
item.data1[1] = 0x03;
item.data1[2] = data1_2;
string name = this->describe_item(item);
std::string name = this->describe_item(item);
phosg::fwrite_fmt(stream, " 0103{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:6} {:2}* {} {}\n",
data1_2,
@@ -975,14 +973,14 @@ void ItemNameIndex::print_table(FILE* stream) const {
const auto& m = pmt->get_mag(data1_1);
float sale_divisor = pmt->get_sale_divisor(0x02, data1_1);
string divisor_str = std::format("{:g}", sale_divisor);
std::string divisor_str = std::format("{:g}", sale_divisor);
divisor_str.resize(13, ' ');
ItemData item;
item.data1[0] = 0x02;
item.data1[1] = data1_1;
item.data1[2] = 0x00;
string name = this->describe_item(item);
std::string name = this->describe_item(item);
phosg::fwrite_fmt(stream, " 02{:02X}00 => {:08X} {:04X} {:04X} {:6} {:04X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:04X} {} {}\n",
data1_1,
@@ -1011,7 +1009,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS COUNT TECH -COST- ITEMFLAG ---DIVISOR--- NAME\n");
for (size_t data1_1 = 0; data1_1 < pmt->num_tool_classes(); data1_1++) {
float sale_divisor = pmt->get_sale_divisor(0x03, data1_1);
string divisor_str = std::format("{:g}", sale_divisor);
std::string divisor_str = std::format("{:g}", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_tools_in_class(data1_1);
@@ -1023,7 +1021,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
item.data1[1] = data1_1;
item.data1[(data1_1 == 0x02) ? 4 : 2] = data1_2;
item.set_tool_item_amount(*this->limits, 1);
string name = this->describe_item(item);
std::string name = this->describe_item(item);
phosg::fwrite_fmt(stream, " 03{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:04X} {:6} {:08X} {} {}\n",
data1_1,
@@ -1078,7 +1076,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
if (index) {
try {
name = name_for_weapon_special.at(index);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
phosg::fwrite_fmt(stream, " {:02X} => {:04X} {:5} {:2}* {}\n", index, sp.type, sp.amount, stars, name);
@@ -1191,7 +1189,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
static constexpr std::array<const char*, 0x10> stat_names{
"ATP+", "ATA+", "EVP+", "DFP+", "MST+", "HP+", "LCK+", "ALL+",
"ATP-", "ATA-", "EVP-", "DFP-", "MST-", "HP-", "LCK-", "ALL-"};
string s;
std::string s;
if (sb.stat1 > 0x10) {
s = std::format("[{:02X}:{:04X}]", sb.stat1, sb.amount1);
} else if (sb.stat1 > 0) {
@@ -1244,7 +1242,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
phosg::fwrite_fmt(stream, " ## => BOOSTS\n");
for (size_t z = 0; z < pmt->num_tech_boosts(); z++) {
const auto& tb = pmt->get_tech_boost(z);
string s;
std::string s;
if (tb.amount1) {
s += std::format("{:02X}:{:02X}:{:g}", tb.tech_num1, tb.flags1, tb.amount1);
}
@@ -1269,8 +1267,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
item.data1[0] = item_code >> 16;
item.data1[1] = item_code >> 8;
item.data1[2] = item_code;
string name = this->describe_item(item);
phosg::fwrite_fmt(stream, " {:06X} {}\n", item_code, name);
phosg::fwrite_fmt(stream, " {:06X} {}\n", item_code, this->describe_item(item));
}
phosg::fwrite_fmt(stream, "RANGED SPECIALS\n");
+42 -44
View File
@@ -2,8 +2,6 @@
#include "CommonFileFormats.hh"
using namespace std;
/* General notes on the ItemPMT formats:
*
* Sega apparently serialized the fields in this order, so we do the same in BinaryItemParameterTableT::serialize.
@@ -91,7 +89,7 @@ ServerDropMode phosg::enum_for_name<ServerDropMode>(const char* name) {
} else if (!strcmp(name, "SERVER_DUPLICATE")) {
return ServerDropMode::SERVER_DUPLICATE;
} else {
throw runtime_error("invalid drop mode");
throw std::runtime_error("invalid drop mode");
}
}
@@ -109,7 +107,7 @@ const char* phosg::name_for_enum<ServerDropMode>(ServerDropMode value) {
case ServerDropMode::SERVER_DUPLICATE:
return "SERVER_DUPLICATE";
default:
throw runtime_error("invalid drop mode");
throw std::runtime_error("invalid drop mode");
}
}
@@ -1027,11 +1025,11 @@ public:
case 3:
return this->unit_sale_divisor;
}
throw runtime_error("invalid defensive item type");
throw std::runtime_error("invalid defensive item type");
case 2:
return this->mag_sale_divisor;
default:
throw runtime_error("item type does not have a sale divisor");
throw std::runtime_error("item type does not have a sale divisor");
}
}
@@ -2088,7 +2086,7 @@ struct HeaderV4 : HeaderV3V4Base<false> {
// Reader implementation
std::set<uint32_t> ItemParameterTable::compute_all_valid_primary_identifiers() const {
set<uint32_t> ret;
std::set<uint32_t> ret;
auto find_items_1d = [&](uint64_t data1, size_t position) -> size_t {
ItemData item(data1, 0);
@@ -2096,7 +2094,7 @@ std::set<uint32_t> ItemParameterTable::compute_all_valid_primary_identifiers() c
item.data1[position] = x;
try {
this->get_item_id(item);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return x;
}
ret.emplace(item.primary_identifier());
@@ -2143,7 +2141,7 @@ ItemParameterTable::definition_for_primary_identifier(uint32_t primary_identifie
case 3:
return &this->get_unit(data1_2);
default:
throw runtime_error("invalid primary identifier");
throw std::runtime_error("invalid primary identifier");
}
case 2:
return &this->get_mag(data1_1);
@@ -2152,7 +2150,7 @@ ItemParameterTable::definition_for_primary_identifier(uint32_t primary_identifie
// 0302XXYY here
return &this->get_tool(data1_1, data1_2);
default:
throw runtime_error("invalid primary identifier");
throw std::runtime_error("invalid primary identifier");
}
}
@@ -2166,7 +2164,7 @@ uint32_t ItemParameterTable::get_item_id(const ItemData& item) const {
} else if ((item.data1[1] == 1) || (item.data1[1] == 2)) {
return this->get_armor_or_shield(item.data1[1], item.data1[2]).id;
}
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
case 2:
return this->get_mag(item.data1[1]).id;
case 3:
@@ -2175,11 +2173,11 @@ uint32_t ItemParameterTable::get_item_id(const ItemData& item) const {
} else {
return this->get_tool(item.data1[1], item.data1[2]).id;
}
throw logic_error("this should be impossible");
throw std::logic_error("this should be impossible");
case 4:
throw runtime_error("item is meseta and therefore has no definition");
throw std::runtime_error("item is meseta and therefore has no definition");
default:
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
}
}
@@ -2193,7 +2191,7 @@ uint32_t ItemParameterTable::get_item_team_points(const ItemData& item) const {
} else if ((item.data1[1] == 1) || (item.data1[1] == 2)) {
return this->get_armor_or_shield(item.data1[1], item.data1[2]).team_points;
}
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
case 2:
return this->get_mag(item.data1[1]).team_points;
case 3:
@@ -2202,11 +2200,11 @@ uint32_t ItemParameterTable::get_item_team_points(const ItemData& item) const {
} else {
return this->get_tool(item.data1[1], item.data1[2]).team_points;
}
throw logic_error("this should be impossible");
throw std::logic_error("this should be impossible");
case 4:
throw runtime_error("item is meseta and therefore has no definition");
throw std::runtime_error("item is meseta and therefore has no definition");
default:
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
}
}
@@ -2249,7 +2247,7 @@ uint8_t ItemParameterTable::get_item_adjusted_stars(const ItemData& item, bool i
}
}
}
return min<uint8_t>(ret, 12);
return std::min<uint8_t>(ret, 12);
}
std::string ItemParameterTable::get_star_value_table() const {
@@ -2288,7 +2286,7 @@ std::string ItemParameterTable::get_shield_stat_boost_index_table() const {
bool ItemParameterTable::is_item_rare(const ItemData& item) const {
try {
return (this->get_item_base_stars(item) >= 9);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return false;
}
}
@@ -2317,8 +2315,8 @@ const std::vector<ItemParameterTable::ItemCombination>& ItemParameterTable::all_
try {
return this->item_combinations_index().at(item_code_to_u32(
used_item.data1[0], used_item.data1[1], used_item.data1[2]));
} catch (const out_of_range&) {
static const vector<ItemCombination> ret;
} catch (const std::out_of_range&) {
static const std::vector<ItemCombination> ret;
return ret;
}
}
@@ -2332,7 +2330,7 @@ const ItemParameterTable::ItemCombination& ItemParameterTable::get_item_combinat
return def;
}
}
throw out_of_range("no item combination applies");
throw std::out_of_range("no item combination applies");
}
size_t ItemParameterTable::price_for_item(const ItemData& item) const {
@@ -2347,7 +2345,7 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const {
float sale_divisor = this->get_sale_divisor(0, item.data1[1]);
if (sale_divisor == 0.0) {
throw runtime_error("item sale divisor is zero");
throw std::runtime_error("item sale divisor is zero");
}
const auto& def = this->get_weapon(item.data1[1], item.data1[2]);
@@ -2380,7 +2378,7 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const {
double sale_divisor = (double)this->get_sale_divisor(1, item.data1[1]);
if (sale_divisor == 0.0) {
throw runtime_error("item sale divisor is zero");
throw std::runtime_error("item sale divisor is zero");
}
int16_t def_bonus = item.get_armor_or_shield_defense_bonus();
@@ -2406,9 +2404,9 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const {
return item.data2d;
default:
throw runtime_error("invalid item");
throw std::runtime_error("invalid item");
}
throw logic_error("this should be impossible");
throw std::logic_error("this should be impossible");
}
template <
@@ -2442,7 +2440,7 @@ public:
const T& indirect_lookup_2d(size_t base_offset, size_t co_index, size_t item_index) const {
const auto& co = this->r.pget<ArrayRefT<BE>>(base_offset + sizeof(ArrayRefT<BE>) * co_index);
if (item_index >= (co.count + HasImplicitPlaceholders)) {
throw out_of_range("2-D array index out of range");
throw std::out_of_range("2-D array index out of range");
}
return this->r.pget<T>(co.offset + sizeof(T) * item_index);
}
@@ -2470,14 +2468,14 @@ public:
virtual size_t num_weapons_in_class(uint8_t data1_1) const {
if (data1_1 >= this->num_weapon_classes()) {
throw out_of_range("weapon ID out of range");
throw std::out_of_range("weapon ID out of range");
}
return this->indirect_lookup_2d_count(this->root->weapon_table, data1_1);
}
virtual const Weapon& get_weapon(uint8_t data1_1, uint8_t data1_2) const {
if (data1_1 >= this->num_weapon_classes()) {
throw out_of_range("weapon ID out of range");
throw std::out_of_range("weapon ID out of range");
}
uint16_t key = (data1_1 << 8) | data1_2;
auto it = this->weapons.find(key);
@@ -2490,14 +2488,14 @@ public:
virtual size_t num_armors_or_shields_in_class(uint8_t data1_1) const {
if ((data1_1 < 1) || (data1_1 > 2)) {
throw out_of_range("armor/shield class ID out of range");
throw std::out_of_range("armor/shield class ID out of range");
}
return this->indirect_lookup_2d_count(this->root->armor_table, data1_1 - 1) + HasImplicitPlaceholders;
}
virtual const ArmorOrShield& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const {
if ((data1_1 < 1) || (data1_1 > 2)) {
throw out_of_range("armor/shield class ID out of range");
throw std::out_of_range("armor/shield class ID out of range");
}
auto& vec = (data1_1 == 1) ? this->armors : this->shields;
return this->add_to_vector_cache_2d_indirect<ArmorOrShieldT>(vec, this->root->armor_table, data1_1 - 1, data1_2);
@@ -2517,14 +2515,14 @@ public:
virtual size_t num_tools_in_class(uint8_t data1_1) const {
if (data1_1 >= this->num_tool_classes()) {
throw out_of_range("tool class ID out of range");
throw std::out_of_range("tool class ID out of range");
}
return this->indirect_lookup_2d_count(this->root->tool_table, data1_1);
}
virtual const Tool& get_tool(uint8_t data1_1, uint8_t data1_2) const {
if (data1_1 >= this->num_tool_classes()) {
throw out_of_range("tool class ID out of range");
throw std::out_of_range("tool class ID out of range");
}
uint16_t key = (data1_1 << 8) | data1_2;
auto it = this->tools.find(key);
@@ -2543,11 +2541,11 @@ public:
const auto* defs = &this->r.pget<ToolT>(co.offset, sizeof(ToolT) * co.count);
for (size_t y = 0; y < co.count; y++) {
if (defs[y].base.id == id) {
return make_pair(z, y);
return std::make_pair(z, y);
}
}
}
throw out_of_range(std::format("invalid tool class {:08X}", id));
throw std::out_of_range(std::format("invalid tool class {:08X}", id));
}
virtual size_t num_mags() const {
@@ -2636,10 +2634,10 @@ public:
virtual const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t item_index) const {
if (table_index >= 8) {
throw out_of_range("invalid mag feed table index");
throw std::out_of_range("invalid mag feed table index");
}
if (item_index >= 11) {
throw out_of_range("invalid mag feed item index");
throw std::out_of_range("invalid mag feed item index");
}
const auto& table_offsets = this->r.pget<MagFeedResultsListOffsetsT<BE>>(this->root->mag_feed_table);
return this->r.pget<MagFeedResultsList>(table_offsets[table_index])[item_index];
@@ -2681,7 +2679,7 @@ public:
virtual const Special& get_special(uint8_t special) const {
special &= 0x3F;
if (special >= this->num_specials()) {
throw out_of_range("invalid special index");
throw std::out_of_range("invalid special index");
}
while (this->specials.size() <= special) {
this->specials.emplace_back(this->r.pget<SpecialT<BE>>(
@@ -2772,10 +2770,10 @@ public:
virtual uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const {
if (char_class >= 12) {
throw out_of_range("invalid character class");
throw std::out_of_range("invalid character class");
}
if (tech_num >= 19) {
throw out_of_range("invalid technique number");
throw std::out_of_range("invalid technique number");
}
if constexpr (requires { this->root->max_tech_level_table; }) {
return r.pget_u8(this->root->max_tech_level_table + tech_num * 12 + char_class);
@@ -2862,13 +2860,13 @@ public:
if constexpr (requires { this->root->unwrap_table; }) {
const auto& co = this->r.pget<ArrayRefT<BE>>(this->root->unwrap_table);
if (event_number >= co.count) {
throw out_of_range("invalid event number");
throw std::out_of_range("invalid event number");
}
const auto& event_co = this->r.pget<ArrayRefT<BE>>(co.offset + sizeof(ArrayRefT<BE>) * event_number);
const auto* defs = &this->r.pget<EventItem>(event_co.offset, event_co.count * sizeof(EventItem));
return make_pair(defs, event_co.count);
return std::make_pair(defs, event_co.count);
} else {
return make_pair(nullptr, 0);
return std::make_pair(nullptr, 0);
}
}
+10 -12
View File
@@ -1,7 +1,5 @@
#include "ItemTranslationTable.hh"
using namespace std;
static constexpr bool is_canonical(uint32_t id) {
return !(id & 0x80000000);
}
@@ -23,12 +21,12 @@ ItemTranslationTable::ItemTranslationTable(
if (is_canonical(id)) {
has_any_canonical_id = true;
if (!this->entry_index_for_version[v_s].emplace(id, z).second) {
throw runtime_error(std::format("(row {}) duplicate canonical ID {:08X}", z, id));
throw std::runtime_error(std::format("(row {}) duplicate canonical ID {:08X}", z, id));
}
}
}
if (!has_any_canonical_id) {
throw runtime_error(std::format("(row {}) no canonical ID present in row", z));
throw std::runtime_error(std::format("(row {}) no canonical ID present in row", z));
}
}
@@ -46,27 +44,27 @@ ItemTranslationTable::ItemTranslationTable(
uint32_t e_id = this->entries[z].id_for_version[v_s];
if (is_canonical(e_id)) {
if (!entry_index.count(e_id)) {
throw logic_error(std::format("(row {} version {}) canonical ID {:08X} is missing from the index", z, phosg::name_for_enum(v), e_id));
throw std::logic_error(std::format("(row {} version {}) canonical ID {:08X} is missing from the index", z, phosg::name_for_enum(v), e_id));
}
try {
item_parameter_table->definition_for_primary_identifier(e_id);
} catch (const out_of_range&) {
throw runtime_error(std::format("(row {} version {}) ID {:08X} not defined in item parameter table", z, phosg::name_for_enum(v), e_id));
} catch (const std::out_of_range&) {
throw std::runtime_error(std::format("(row {} version {}) ID {:08X} not defined in item parameter table", z, phosg::name_for_enum(v), e_id));
}
if (!remaining_identifiers.erase(e_id)) {
throw runtime_error(std::format("(row {} version {}) ID {:08X} not in item parameter table's primary identifier list", z, phosg::name_for_enum(v), e_id));
throw std::runtime_error(std::format("(row {} version {}) ID {:08X} not in item parameter table's primary identifier list", z, phosg::name_for_enum(v), e_id));
}
} else if (!entry_index.count(make_canonical(e_id))) {
throw runtime_error(std::format("(row {} version {}) ID {:08X} refers to nonexistent canonical ID", z, phosg::name_for_enum(v), e_id));
throw std::runtime_error(std::format("(row {} version {}) ID {:08X} refers to nonexistent canonical ID", z, phosg::name_for_enum(v), e_id));
}
}
if (!remaining_identifiers.empty()) {
string missing_str = std::format("(version {}) not all identifiers in the item parameter table are defined in the translation table; missing:", phosg::name_for_enum(v));
std::string missing_str = std::format("(version {}) not all identifiers in the item parameter table are defined in the translation table; missing:", phosg::name_for_enum(v));
for (uint32_t id : remaining_identifiers) {
missing_str += std::format(" {:08X}", id);
}
throw runtime_error(missing_str);
throw std::runtime_error(missing_str);
}
}
}
@@ -92,7 +90,7 @@ uint32_t ItemTranslationTable::translate(uint32_t primary_identifier, Version fr
ItemTranslationTable::Entry::Entry(const phosg::JSON& json) {
const auto& l = json.as_list();
if (l.size() != NUM_NON_PATCH_VERSIONS + 1) {
throw runtime_error("list length is incorrect");
throw std::runtime_error("list length is incorrect");
}
for (size_t z = 0; z < NUM_NON_PATCH_VERSIONS; z++) {
this->id_for_version[z] = l[z]->as_int();
+27 -29
View File
@@ -4,9 +4,7 @@
#include "SendCommands.hh"
using namespace std;
void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomGenerator> rand_crypt) {
void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_ptr<RandomGenerator> rand_crypt) {
auto s = c->require_server_state();
// On PC (and presumably DC), the client sends a 6x29 after this to delete the used item. On GC and later versions,
@@ -26,13 +24,13 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
auto item_parameter_table = s->item_parameter_table(c->version());
uint8_t max_level = item_parameter_table->get_max_tech_level(player->disp.visual.char_class, item.data.data1[4]);
if (item.data.data1[2] > max_level) {
throw runtime_error("technique level too high");
throw std::runtime_error("technique level too high");
}
player->set_technique_level(item.data.data1[4], item.data.data1[2]);
} else if ((primary_identifier & 0xFFFF0000) == 0x030A0000) { // Grinder
if (item.data.data1[2] > 2) {
throw runtime_error("incorrect grinder value");
throw std::runtime_error("incorrect grinder value");
}
auto& weapon = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::WEAPON)];
@@ -40,9 +38,9 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
auto item_parameter_table = s->item_parameter_table(c->version());
auto weapon_def = item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]);
if (is_v4 && (weapon.data.data1[3] >= weapon_def.max_grind)) {
throw runtime_error("weapon already at maximum grind");
throw std::runtime_error("weapon already at maximum grind");
}
weapon.data.data1[3] = min<uint8_t>(weapon.data.data1[3] + item.data.data1[2] + 1, weapon_def.max_grind);
weapon.data.data1[3] = std::min<uint8_t>(weapon.data.data1[3] + item.data.data1[2] + 1, weapon_def.max_grind);
} else if ((primary_identifier & 0xFFFF0000) == 0x030B0000) { // Material
auto p = c->character_file();
@@ -85,11 +83,11 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
if (!is_v3_or_later && (c->version() != Version::GC_NTE)) {
p->disp.stats.char_stats.lck += 2;
} else {
throw runtime_error("unknown material used");
throw std::runtime_error("unknown material used");
}
break;
default:
throw runtime_error("unknown material used");
throw std::runtime_error("unknown material used");
}
if (is_v3_or_later || (type == Type::HP) || (type == Type::TP)) {
p->set_material_usage(type, p->get_material_usage(type) + 1);
@@ -98,7 +96,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
} else if ((primary_identifier & 0xFFFF0000) == 0x030F0000) { // AddSlot
auto& armor = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::ARMOR)];
if (armor.data.data1[5] >= 4) {
throw runtime_error("armor already at maximum slot count");
throw std::runtime_error("armor already at maximum slot count");
}
armor.data.data1[5]++;
@@ -155,7 +153,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
mag.data.data1[1] = 0x2B;
break;
default:
throw runtime_error("invalid mag cell used");
throw std::runtime_error("invalid mag cell used");
}
}
@@ -168,7 +166,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
sum += table.first[z].probability;
}
if (sum == 0) {
throw runtime_error("no unwrap results available for event");
throw std::runtime_error("no unwrap results available for event");
}
// TODO: It seems that on non-BB, clients don't synchronize this at all, so they could end up thinking the
// unwrapped item is something completely different. (They don't even use a fixed random seed, like for rares; they
@@ -218,30 +216,30 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
auto item_parameter_table = s->item_parameter_table(c->version());
const auto& combo = item_parameter_table->get_item_combination(item.data, inv_item.data);
if (combo.char_class != 0xFF && combo.char_class != player->disp.visual.char_class) {
throw runtime_error("item combination requires specific char_class");
throw std::runtime_error("item combination requires specific char_class");
}
if (combo.mag_level != 0xFF) {
if (inv_item.data.data1[0] != 2) {
throw runtime_error("item combination applies with mag level requirement, but equipped item is not a mag");
throw std::runtime_error("item combination applies with mag level requirement, but equipped item is not a mag");
}
if (inv_item.data.compute_mag_level() < combo.mag_level) {
throw runtime_error("item combination applies with mag level requirement, but equipped mag level is too low");
throw std::runtime_error("item combination applies with mag level requirement, but equipped mag level is too low");
}
}
if (combo.grind != 0xFF) {
if (inv_item.data.data1[0] != 0) {
throw runtime_error("item combination applies with grind requirement, but equipped item is not a weapon");
throw std::runtime_error("item combination applies with grind requirement, but equipped item is not a weapon");
}
if (inv_item.data.data1[3] < combo.grind) {
throw runtime_error("item combination applies with grind requirement, but equipped weapon grind is too low");
throw std::runtime_error("item combination applies with grind requirement, but equipped weapon grind is too low");
}
}
if (combo.level != 0xFF && player->disp.stats.level + 1 < combo.level) {
throw runtime_error("item combination applies with level requirement, but player level is too low");
throw std::runtime_error("item combination applies with level requirement, but player level is too low");
}
// If we get here, then the combo applies
if (combo_applied) {
throw runtime_error("multiple combinations apply");
throw std::runtime_error("multiple combinations apply");
}
combo_applied = true;
@@ -254,7 +252,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
inv_item.data.data1[4] = 0; // Flags + special
}
inv_item.flags &= (~8); // Unequip it
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
}
@@ -269,13 +267,13 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
void apply_mag_feed_result(
ItemData& mag_item,
const ItemData& fed_item,
shared_ptr<const ItemParameterTable> item_parameter_table,
shared_ptr<const MagEvolutionTable> mag_evolution_table,
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const MagEvolutionTable> mag_evolution_table,
uint8_t char_class,
uint8_t section_id,
bool version_has_rare_mags) {
static const unordered_map<uint32_t, size_t> result_index_for_fed_item({
static const std::unordered_map<uint32_t, size_t> result_index_for_fed_item({
{0x03000000, 0}, // Monomate
{0x03000100, 1}, // Dimate
{0x03000200, 2}, // Trimate
@@ -298,7 +296,7 @@ void apply_mag_feed_result(
if ((delta > 0) || ((delta < 0) && (-delta < existing_stat))) {
uint16_t level = data.compute_mag_level();
if (level > 200) {
throw runtime_error("mag level is too high");
throw std::runtime_error("mag level is too high");
}
if ((level == 200) && ((99 - existing_stat) < delta)) {
delta = 99 - existing_stat;
@@ -311,8 +309,8 @@ void apply_mag_feed_result(
update_stat(mag_item, 3, feed_result.pow);
update_stat(mag_item, 4, feed_result.dex);
update_stat(mag_item, 5, feed_result.mind);
mag_item.data2[0] = clamp<ssize_t>(static_cast<ssize_t>(mag_item.data2[0]) + feed_result.synchro, 0, 120);
mag_item.data2[1] = clamp<ssize_t>(static_cast<ssize_t>(mag_item.data2[1]) + feed_result.iq, 0, 200);
mag_item.data2[0] = std::clamp<ssize_t>(static_cast<ssize_t>(mag_item.data2[0]) + feed_result.synchro, 0, 120);
mag_item.data2[1] = std::clamp<ssize_t>(static_cast<ssize_t>(mag_item.data2[1]) + feed_result.iq, 0, 200);
uint8_t mag_level = mag_item.compute_mag_level();
mag_item.data1[2] = mag_level;
@@ -347,7 +345,7 @@ void apply_mag_feed_result(
mag_item.data1[1] = 0x19; // Vritra
break;
default:
throw runtime_error("invalid character class");
throw std::runtime_error("invalid character class");
}
}
@@ -401,7 +399,7 @@ void apply_mag_feed_result(
} else if (is_ranger) {
table_index += 6;
} else if (!is_hunter) {
throw logic_error("char class is not any of the top-level classes");
throw std::logic_error("char class is not any of the top-level classes");
}
// Note: The original code checks the class (hunter/ranger/force) again here, and goes into 3 branches that
@@ -433,7 +431,7 @@ void apply_mag_feed_result(
bool is_ranger = char_class_is_ranger(char_class);
bool is_force = char_class_is_force(char_class);
if (is_hunter + is_ranger + is_force != 1) {
throw logic_error("char class is not exactly one of the top-level classes");
throw std::logic_error("char class is not exactly one of the top-level classes");
}
if (is_hunter) {
+7 -9
View File
@@ -7,8 +7,6 @@
#include "CommonFileFormats.hh"
#include "StaticGameData.hh"
using namespace std;
void LevelTable::reset_to_base(PlayerStats& stats, uint8_t char_class) const {
stats.level = 0;
stats.exp = 0;
@@ -82,7 +80,7 @@ const LevelStatsDelta& JSONLevelTable::stats_delta_for_level(uint8_t char_class,
return this->level_deltas.at(char_class).at(level);
}
LevelTableV2::LevelTableV2(const string& data) {
LevelTableV2::LevelTableV2(const std::string& data) {
struct Root {
// The overall format of this file on V2 has much more data than we actually use. This table is sorted by the
// offset in the PlayerTable.prs file; note that the offset fields in this structure do not match that order.
@@ -165,7 +163,7 @@ size_t LevelTableV3::num_char_classes() const {
}
const CharacterStats& LevelTableV3::base_stats_for_class(uint8_t char_class) const {
static const array<CharacterStats, 12> data = {
static const std::array<CharacterStats, 12> data = {
// ATP MST EVP HP DFP ATA LCK
CharacterStats{0x0023, 0x001D, 0x002D, 0x0014, 0x0011, 0x001E, 0x000A},
CharacterStats{0x001E, 0x0028, 0x003C, 0x0013, 0x0016, 0x0019, 0x000A},
@@ -183,7 +181,7 @@ const CharacterStats& LevelTableV3::base_stats_for_class(uint8_t char_class) con
return data.at(char_class);
}
static const array<PlayerStats, 12> max_stats_v3_v4 = {
static const std::array<PlayerStats, 12> max_stats_v3_v4 = {
// ATP MST EVP HP DFP ATA LCK ESP PRX PRY L E M
PlayerStats{{0x056B, 0x02DC, 0x02F4, 0x0265, 0x0243, 0x054B, 0x0064}, 0x0064, 0.0f, 0.0f, 0, 0, 0},
PlayerStats{{0x04CB, 0x0499, 0x032B, 0x0254, 0x024D, 0x056C, 0x0064}, 0x0064, 0.0f, 0.0f, 0, 0, 0},
@@ -208,7 +206,7 @@ const LevelStatsDelta& LevelTableV3::stats_delta_for_level(uint8_t char_class, u
}
template <bool BE>
void parse_level_deltas_t(std::array<std::array<LevelStatsDelta, 200>, 12>& deltas, const string& data) {
void parse_level_deltas_t(std::array<std::array<LevelStatsDelta, 200>, 12>& deltas, const std::string& data) {
// The V3 format is very simple:
// root:
// u32 offset:
@@ -225,11 +223,11 @@ void parse_level_deltas_t(std::array<std::array<LevelStatsDelta, 200>, 12>& delt
}
}
LevelTableGC::LevelTableGC(const string& data) {
LevelTableGC::LevelTableGC(const std::string& data) {
parse_level_deltas_t<true>(this->level_deltas, data);
}
LevelTableXB::LevelTableXB(const string& data) {
LevelTableXB::LevelTableXB(const std::string& data) {
parse_level_deltas_t<false>(this->level_deltas, data);
}
@@ -274,7 +272,7 @@ std::string LevelTable::serialize_binary_v4() const {
return rel.finalize(root_offset);
}
LevelTableV4::LevelTableV4(const string& data) {
LevelTableV4::LevelTableV4(const std::string& data) {
phosg::StringReader r(data);
const auto& footer = r.pget<RELFileFooter>(r.size() - sizeof(RELFileFooter));
+49 -51
View File
@@ -10,8 +10,6 @@
#include "ServerState.hh"
#include "Text.hh"
using namespace std;
bool Lobby::FloorItem::visible_to_client(uint8_t client_id) const {
return this->flags & (1 << client_id);
}
@@ -24,17 +22,17 @@ bool Lobby::FloorItemManager::exists(uint32_t item_id) const {
return this->items.count(item_id);
}
shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::find(uint32_t item_id) const {
std::shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::find(uint32_t item_id) const {
return this->items.at(item_id);
}
void Lobby::FloorItemManager::add(
const ItemData& item,
const VectorXZF& pos,
shared_ptr<const MapState::ObjectState> from_obj,
shared_ptr<const MapState::EnemyState> from_ene,
std::shared_ptr<const MapState::ObjectState> from_obj,
std::shared_ptr<const MapState::EnemyState> from_ene,
uint16_t flags) {
auto fi = make_shared<FloorItem>();
auto fi = std::make_shared<FloorItem>();
fi->data = item;
fi->pos = pos;
fi->drop_number = this->next_drop_number++;
@@ -44,14 +42,14 @@ void Lobby::FloorItemManager::add(
this->add(fi);
}
void Lobby::FloorItemManager::add(shared_ptr<Lobby::FloorItem> fi) {
void Lobby::FloorItemManager::add(std::shared_ptr<Lobby::FloorItem> fi) {
if (fi->flags == 0) {
throw logic_error("floor item is not visible to any player");
throw std::logic_error("floor item is not visible to any player");
}
auto emplace_ret = this->items.emplace(fi->data.id, fi);
if (!emplace_ret.second) {
throw runtime_error("floor item already exists with the same ID");
throw std::runtime_error("floor item already exists with the same ID");
}
for (size_t z = 0; z < 12; z++) {
if (fi->visible_to_client(z)) {
@@ -65,15 +63,15 @@ void Lobby::FloorItemManager::add(shared_ptr<Lobby::FloorItem> fi) {
std::shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::remove(uint32_t item_id, uint8_t client_id) {
auto item_it = this->items.find(item_id);
if (item_it == this->items.end()) {
throw out_of_range("item not present");
throw std::out_of_range("item not present");
}
auto fi = item_it->second;
if ((client_id != 0xFF) && !fi->visible_to_client(client_id)) {
throw runtime_error("client does not have access to item");
throw std::runtime_error("client does not have access to item");
}
for (size_t z = 0; z < 12; z++) {
if (fi->visible_to_client(z) && !this->queue_for_client[z].erase(fi->drop_number)) {
throw logic_error("item queue for client is inconsistent");
throw std::logic_error("item queue for client is inconsistent");
}
}
this->items.erase(item_it);
@@ -83,7 +81,7 @@ std::shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::remove(uint32_t item_
}
std::unordered_set<std::shared_ptr<Lobby::FloorItem>> Lobby::FloorItemManager::evict() {
unordered_set<shared_ptr<FloorItem>> ret;
std::unordered_set<std::shared_ptr<FloorItem>> ret;
for (size_t z = 0; z < 12; z++) {
while (this->queue_for_client[z].size() > 48) {
ret.emplace(this->remove(this->queue_for_client[z].begin()->second->data.id, 0xFF));
@@ -94,7 +92,7 @@ std::unordered_set<std::shared_ptr<Lobby::FloorItem>> Lobby::FloorItemManager::e
}
void Lobby::FloorItemManager::clear_inaccessible(uint16_t remaining_clients_mask) {
unordered_set<uint32_t> item_ids_to_delete;
std::unordered_set<uint32_t> item_ids_to_delete;
for (const auto& it : this->items) {
if ((it.second->flags & remaining_clients_mask) == 0) {
item_ids_to_delete.emplace(it.first);
@@ -107,7 +105,7 @@ void Lobby::FloorItemManager::clear_inaccessible(uint16_t remaining_clients_mask
}
void Lobby::FloorItemManager::clear_private() {
unordered_set<uint32_t> item_ids_to_delete;
std::unordered_set<uint32_t> item_ids_to_delete;
for (const auto& it : this->items) {
if ((it.second->flags & 0x00F) != 0x00F) {
item_ids_to_delete.emplace(it.first);
@@ -130,7 +128,7 @@ void Lobby::FloorItemManager::clear() {
}
uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) {
::map<uint32_t, shared_ptr<FloorItem>> old_items;
std::map<uint32_t, std::shared_ptr<FloorItem>> old_items;
old_items.swap(this->items);
for (auto& queue : this->queue_for_client) {
queue.clear();
@@ -142,13 +140,13 @@ uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) {
return next_item_id;
}
Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id, bool is_game)
Lobby::Lobby(std::shared_ptr<ServerState> s, uint32_t id, bool is_game)
: server_state(s),
log(std::format("[{}:{:X}] ", is_game ? "Game" : "Lobby", id), lobby_log.min_level),
creation_time(phosg::now()),
lobby_id(id),
random_seed(phosg::random_object<uint32_t>()),
rand_crypt(make_shared<DisabledRandomGenerator>()),
rand_crypt(std::make_shared<DisabledRandomGenerator>()),
drop_mode(ServerDropMode::CLIENT),
idle_timeout_timer(*s->io_context) {
this->log.info_f("Created");
@@ -178,17 +176,17 @@ uint8_t Lobby::area_for_floor(Version version, uint8_t floor) const {
return sdt->default_floor_to_area(this->episode).at(floor);
}
shared_ptr<ServerState> Lobby::require_server_state() const {
std::shared_ptr<ServerState> Lobby::require_server_state() const {
auto s = this->server_state.lock();
if (!s) {
throw logic_error("server is deleted");
throw std::logic_error("server is deleted");
}
return s;
}
shared_ptr<Lobby::ChallengeParameters> Lobby::require_challenge_params() const {
std::shared_ptr<Lobby::ChallengeParameters> Lobby::require_challenge_params() const {
if (!this->challenge_params) {
throw runtime_error("challenge params are missing");
throw std::runtime_error("challenge params are missing");
}
return this->challenge_params;
}
@@ -206,17 +204,17 @@ void Lobby::create_item_creator(Version logic_version) {
logic_version = leader_c ? leader_c->version() : Version::BB_V4;
}
shared_ptr<RandomGenerator> rand_crypt;
std::shared_ptr<RandomGenerator> rand_crypt;
if (s->use_psov2_rand_crypt) {
rand_crypt = make_shared<PSOV2Encryption>(this->rand_crypt->seed());
rand_crypt = std::make_shared<PSOV2Encryption>(this->rand_crypt->seed());
} else {
rand_crypt = make_shared<MT19937Generator>(this->rand_crypt->seed());
rand_crypt = std::make_shared<MT19937Generator>(this->rand_crypt->seed());
}
uint8_t effective_section_id = this->effective_section_id();
if (effective_section_id >= 10) {
effective_section_id = 0x00;
}
this->item_creator = make_shared<ItemCreator>(
this->item_creator = std::make_shared<ItemCreator>(
s->common_item_set(logic_version, this->quest),
s->rare_item_set(logic_version, this->quest),
s->armor_random_set,
@@ -274,13 +272,13 @@ void Lobby::load_maps() {
if (this->quest) {
this->log.info_f("Loading quest supermap");
auto supermap = this->quest->get_supermap(this->random_seed);
this->map_state = make_shared<MapState>(
this->map_state = std::make_shared<MapState>(
this->lobby_id, this->difficulty, this->event, this->random_seed, this->rare_enemy_rates, this->rand_crypt, supermap);
} else {
this->log.info_f("Loading free play supermaps");
auto s = this->require_server_state();
auto supermaps = s->supermaps_for_variations(this->episode, this->mode, this->difficulty, this->variations);
this->map_state = make_shared<MapState>(
this->map_state = std::make_shared<MapState>(
this->lobby_id, this->difficulty, this->event, this->random_seed, this->rare_enemy_rates, this->rand_crypt, supermaps);
}
@@ -323,7 +321,7 @@ void Lobby::create_ep3_server() {
} else {
options.behavior_flags &= (~Episode3::BehaviorFlag::IS_TRIAL_EDITION);
}
this->ep3_server = make_shared<Episode3::Server>(this->shared_from_this(), std::move(options));
this->ep3_server = std::make_shared<Episode3::Server>(this->shared_from_this(), std::move(options));
this->ep3_server->init();
}
@@ -381,9 +379,9 @@ bool Lobby::any_v1_clients_present() const {
return false;
}
void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
void Lobby::add_client(std::shared_ptr<Client> c, ssize_t required_client_id) {
if (!c->login) {
throw runtime_error("client is not logged in");
throw std::runtime_error("client is not logged in");
}
ssize_t index;
@@ -391,7 +389,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
if (required_client_id >= 0) {
if (this->clients.at(required_client_id).get()) {
throw out_of_range("required slot is in use");
throw std::out_of_range("required slot is in use");
}
this->clients[required_client_id] = c;
index = required_client_id;
@@ -404,7 +402,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
}
}
if (index < min_client_id) {
throw out_of_range("no space left in lobby");
throw std::out_of_range("no space left in lobby");
}
} else {
for (index = min_client_id; index < this->max_clients; index++) {
@@ -414,7 +412,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
}
}
if (index >= this->max_clients) {
throw out_of_range("no space left in lobby");
throw std::out_of_range("no space left in lobby");
}
}
@@ -487,10 +485,10 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
}
}
void Lobby::remove_client(shared_ptr<Client> c) {
void Lobby::remove_client(std::shared_ptr<Client> c) {
if (this->clients.at(c->lobby_client_id) != c) {
auto other_c = this->clients[c->lobby_client_id].get();
throw logic_error(std::format(
throw std::logic_error(std::format(
"client\'s lobby client id ({}) does not match client list ({})",
c->lobby_client_id,
static_cast<uint8_t>(other_c ? other_c->lobby_client_id : 0xFF)));
@@ -563,20 +561,20 @@ void Lobby::remove_client(shared_ptr<Client> c) {
}
}
void Lobby::move_client_to_lobby(shared_ptr<Lobby> dest_lobby, shared_ptr<Client> c, ssize_t required_client_id) {
void Lobby::move_client_to_lobby(std::shared_ptr<Lobby> dest_lobby, std::shared_ptr<Client> c, ssize_t required_client_id) {
if (dest_lobby.get() == this) {
return;
}
if (required_client_id >= 0) {
if (dest_lobby->clients.at(required_client_id)) {
throw out_of_range("required slot is in use");
throw std::out_of_range("required slot is in use");
}
} else {
ssize_t min_client_id = this->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) ? 4 : 0;
size_t available_slots = dest_lobby->max_clients - min_client_id;
if (dest_lobby->count_clients() >= available_slots) {
throw out_of_range("no space left in lobby");
throw std::out_of_range("no space left in lobby");
}
}
@@ -584,7 +582,7 @@ void Lobby::move_client_to_lobby(shared_ptr<Lobby> dest_lobby, shared_ptr<Client
dest_lobby->add_client(c, required_client_id);
}
shared_ptr<Client> Lobby::find_client(const string* identifier, uint64_t account_id) {
std::shared_ptr<Client> Lobby::find_client(const std::string* identifier, uint64_t account_id) {
for (size_t x = 0; x < this->max_clients; x++) {
auto lc = this->clients[x];
if (!lc) {
@@ -598,7 +596,7 @@ shared_ptr<Client> Lobby::find_client(const string* identifier, uint64_t account
}
}
throw out_of_range("client not found");
throw std::out_of_range("client not found");
}
Lobby::JoinError Lobby::join_error_for_client(std::shared_ptr<Client> c, const std::string* password) const {
@@ -662,7 +660,7 @@ bool Lobby::item_exists(uint8_t floor, uint32_t item_id) const {
return this->floor_item_managers.at(floor).exists(item_id);
}
shared_ptr<Lobby::FloorItem> Lobby::find_item(uint8_t floor, uint32_t item_id) const {
std::shared_ptr<Lobby::FloorItem> Lobby::find_item(uint8_t floor, uint32_t item_id) const {
return this->floor_item_managers.at(floor).find(item_id);
}
@@ -678,7 +676,7 @@ void Lobby::add_item(
this->evict_items_from_floor(floor);
}
void Lobby::add_item(uint8_t floor, shared_ptr<FloorItem> fi) {
void Lobby::add_item(uint8_t floor, std::shared_ptr<FloorItem> fi) {
auto& m = this->floor_item_managers.at(floor);
m.add(fi);
this->evict_items_from_floor(floor);
@@ -700,7 +698,7 @@ void Lobby::evict_items_from_floor(uint8_t floor) {
}
}
shared_ptr<Lobby::FloorItem> Lobby::remove_item(uint8_t floor, uint32_t item_id, uint8_t requesting_client_id) {
std::shared_ptr<Lobby::FloorItem> Lobby::remove_item(uint8_t floor, uint32_t item_id, uint8_t requesting_client_id) {
return this->floor_item_managers.at(floor).remove(item_id, requesting_client_id);
}
@@ -721,7 +719,7 @@ void Lobby::on_item_id_generated_externally(uint32_t item_id) {
}
}
void Lobby::assign_inventory_and_bank_item_ids(shared_ptr<Client> c, bool consume_ids) {
void Lobby::assign_inventory_and_bank_item_ids(std::shared_ptr<Client> c, bool consume_ids) {
auto p = c->character_file();
uint32_t orig_next_item_id = this->next_item_id_for_client.at(c->lobby_client_id);
for (size_t z = 0; z < p->inventory.num_items; z++) {
@@ -746,8 +744,8 @@ void Lobby::assign_inventory_and_bank_item_ids(shared_ptr<Client> c, bool consum
}
}
unordered_map<uint32_t, shared_ptr<Client>> Lobby::clients_by_account_id() const {
unordered_map<uint32_t, shared_ptr<Client>> ret;
std::unordered_map<uint32_t, std::shared_ptr<Client>> Lobby::clients_by_account_id() const {
std::unordered_map<uint32_t, std::shared_ptr<Client>> ret;
for (auto c : this->clients) {
if (c && c->login) {
ret.emplace(c->login->account->account_id, c);
@@ -759,7 +757,7 @@ unordered_map<uint32_t, shared_ptr<Client>> Lobby::clients_by_account_id() const
QuestIndex::IncludeCondition Lobby::quest_include_condition() const {
size_t num_players = this->count_clients();
bool v1_present = this->any_v1_clients_present();
return [this, num_players, v1_present](shared_ptr<const Quest> q) -> QuestIndex::IncludeState {
return [this, num_players, v1_present](std::shared_ptr<const Quest> q) -> QuestIndex::IncludeState {
bool is_enabled = true;
for (const auto& lc : this->clients) {
auto this_sh = this->shared_from_this();
@@ -774,7 +772,7 @@ QuestIndex::IncludeCondition Lobby::quest_include_condition() const {
};
}
bool Lobby::compare_shared(const shared_ptr<const Lobby>& a, const shared_ptr<const Lobby>& b) {
bool Lobby::compare_shared(const std::shared_ptr<const Lobby>& a, const std::shared_ptr<const Lobby>& b) {
// Sort keys:
// 1. Priority class: has free space < empty (persistent) < full < non-joinable (in quest/battle)
// 2. Password: public < locked
@@ -782,7 +780,7 @@ bool Lobby::compare_shared(const shared_ptr<const Lobby>& a, const shared_ptr<co
// 4. Episode: 1 < 2 < 4
// 5. Difficulty: Normal < Hard < Very Hard < Ultimate
// 6. Game name
static auto get_priority = +[](const shared_ptr<const Lobby>& l) -> size_t {
static auto get_priority = +[](const std::shared_ptr<const Lobby>& l) -> size_t {
if (l->check_flag(Lobby::Flag::QUEST_SELECTION_IN_PROGRESS) ||
l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) ||
l->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) {
@@ -864,6 +862,6 @@ const char* phosg::name_for_enum<Lobby::JoinError>(Lobby::JoinError value) {
case Lobby::JoinError::NO_ACCESS_TO_QUEST:
return "NO_ACCESS_TO_QUEST";
default:
throw runtime_error("invalid drop mode");
throw std::runtime_error("invalid drop mode");
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
struct FloorItemManager {
phosg::PrefixedLogger log;
uint64_t next_drop_number;
// It's important that this is a map and not an unordered_map. See the comment in send_game_item_state for details.
// It's important that this is a map and not an std::unordered_map. See the comment in send_game_item_state for details.
std::map<uint32_t, std::shared_ptr<FloorItem>> items; // Keyed on item_id
std::array<std::map<uint64_t, std::shared_ptr<FloorItem>>, 12> queue_for_client;
+2 -4
View File
@@ -2,8 +2,6 @@
#include <phosg/Strings.hh>
using namespace std;
phosg::PrefixedLogger channel_exceptions_log("[Channel] ", phosg::LogLevel::L_USE_DEFAULT);
phosg::PrefixedLogger client_functions_log("[ClientFunctionIndex] ", phosg::LogLevel::L_USE_DEFAULT);
phosg::PrefixedLogger client_log("", phosg::LogLevel::L_USE_DEFAULT);
@@ -22,9 +20,9 @@ phosg::PrefixedLogger static_game_data_log("[StaticGameData] ", phosg::LogLevel:
static void set_log_level_from_json(
phosg::PrefixedLogger& log, const phosg::JSON& d, const char* json_key) {
try {
string name = phosg::toupper(d.at(json_key).as_string());
std::string name = phosg::toupper(d.at(json_key).as_string());
log.min_level = phosg::enum_for_name<phosg::LogLevel>(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
+7 -9
View File
@@ -2,8 +2,6 @@
#include "CommonFileFormats.hh"
using namespace std;
template <bool BE>
struct MotionReferenceTables {
// It seems that there are two definition tables, but only the first is used on any version of PSO. On v3 and later,
@@ -125,7 +123,7 @@ static uint8_t get_v1_mag_evolution_number(uint8_t data1_1) {
/* 10 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, 3, 4, 3, 3,
/* 20 */ 3, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4};
if (data1_1 >= v1_evolution_number_table.size()) {
throw runtime_error("invalid mag number");
throw std::runtime_error("invalid mag number");
}
return v1_evolution_number_table[data1_1];
}
@@ -196,7 +194,7 @@ public:
virtual const VectorXYZTF& get_color_rgba(size_t index) const {
if (index >= NumColors) {
throw runtime_error("invalid mag color index");
throw std::runtime_error("invalid mag color index");
}
return this->add_to_vector_cache<ColorEntry<BE>>(this->colors, this->root->color_table, index);
}
@@ -241,29 +239,29 @@ public:
return 0;
}
virtual const MotionReference& get_motion_reference(bool, size_t) const {
throw runtime_error("Mag tables not available on DC NTE");
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t) const {
throw runtime_error("Mag tables not available on DC NTE");
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual size_t num_unknown_a3_entries() const {
return 0;
}
virtual const UnknownA3Entry& get_unknown_a3(size_t) const {
throw runtime_error("Mag tables not available on DC NTE");
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual uint8_t get_unknown_a4(size_t) const {
throw runtime_error("Mag tables not available on DC NTE");
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual size_t num_colors() const {
return 0;
}
virtual const VectorXYZTF& get_color_rgba(size_t) const {
throw runtime_error("Mag tables not available on DC NTE");
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual uint8_t get_evolution_number(uint8_t data1_1) const {
+507 -541
View File
File diff suppressed because it is too large Load Diff
+304 -313
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -456,7 +456,7 @@ public:
return this->has_any_random_sections;
}
// If the map file has no random sections, does nothing and returns a shared_ptr to this. If it has any random
// If the map file has no random sections, does nothing and returns a std::shared_ptr to this. If it has any random
// sections, returns a new map with all non-random sections copied verbatim, and random sections replaced with
// non-random sections according to the challenge mode enemy generation algorithm.
std::shared_ptr<MapFile> materialize_random_sections(uint32_t random_seed);
+5 -20
View File
@@ -1,28 +1,13 @@
#include "Menu.hh"
using namespace std;
MenuItem::MenuItem(uint32_t item_id, const std::string& name, const std::string& description, uint32_t flags)
: item_id(item_id), name(name), description(description), get_description(nullptr), flags(flags) {}
MenuItem::MenuItem(
uint32_t item_id,
const string& name,
const string& description,
uint32_t flags)
: item_id(item_id),
name(name),
description(description),
get_description(nullptr),
flags(flags) {}
MenuItem::MenuItem(uint32_t item_id,
const string& name,
const std::string& name,
std::function<std::string()> get_description,
uint32_t flags)
: item_id(item_id),
name(name),
description(),
get_description(std::move(get_description)),
flags(flags) {}
: item_id(item_id), name(name), description(), get_description(std::move(get_description)), flags(flags) {}
Menu::Menu(uint32_t menu_id, const std::string& name)
: menu_id(menu_id),
name(name) {}
Menu::Menu(uint32_t menu_id, const std::string& name) : menu_id(menu_id), name(name) {}
+6 -8
View File
@@ -17,18 +17,16 @@
#include <phosg/Strings.hh>
#include <stdexcept>
using namespace std;
map<string, uint32_t> get_local_addresses() {
map<string, uint32_t> ret;
std::map<std::string, uint32_t> get_local_addresses() {
std::map<std::string, uint32_t> ret;
#ifndef PHOSG_WINDOWS
struct ifaddrs* ifa_raw;
if (getifaddrs(&ifa_raw)) {
auto s = phosg::string_for_error(errno);
throw runtime_error(std::format("failed to get interface addresses: {}", s));
throw std::runtime_error(std::format("failed to get interface addresses: {}", s));
}
unique_ptr<struct ifaddrs, void (*)(struct ifaddrs*)> ifa(ifa_raw, freeifaddrs);
std::unique_ptr<struct ifaddrs, void (*)(struct ifaddrs*)> ifa(ifa_raw, freeifaddrs);
for (struct ifaddrs* i = ifa.get(); i; i = i->ifa_next) {
if (!i->ifa_addr) {
@@ -56,7 +54,7 @@ map<string, uint32_t> get_local_addresses() {
}
if (result != NO_ERROR) {
throw runtime_error(std::format("GetAdaptersAddresses failed: {}", result));
throw std::runtime_error(std::format("GetAdaptersAddresses failed: {}", result));
}
for (IP_ADAPTER_ADDRESSES* adapter = adapters; adapter != nullptr; adapter = adapter->Next) {
@@ -92,7 +90,7 @@ bool is_local_address(const sockaddr_storage& daddr) {
return is_local_address(ntohl(sin->sin_addr.s_addr));
}
string string_for_address(uint32_t address) {
std::string string_for_address(uint32_t address) {
return std::format("{}.{}.{}.{}",
static_cast<uint8_t>(address >> 24), static_cast<uint8_t>(address >> 16),
static_cast<uint8_t>(address >> 8), static_cast<uint8_t>(address));
+7 -9
View File
@@ -10,8 +10,6 @@
#include "Compression.hh"
#include "Text.hh"
using namespace std;
struct Entry {
pstring<TextEncoding::ASCII, 0x80> filename;
le_uint32_t unknown_a1;
@@ -24,7 +22,7 @@ struct Entry {
static void decrypt_ppk_data(std::string& data, const std::string& filename, const std::string& password) {
if (password.size() > 0xFF) {
throw runtime_error("password is too long");
throw std::runtime_error("password is too long");
}
uint8_t key[0x100];
@@ -47,27 +45,27 @@ std::unordered_map<std::string, std::string> decode_ppk_file(const std::string&
uint32_t signature = r.get_u32b();
if (signature != 0x50503130 && signature != 0x4D5A5000) { // 'PP10' or 'MZP\0'
throw runtime_error("file is not a ppk archive");
throw std::runtime_error("file is not a ppk archive");
}
unordered_map<string, string> ret;
std::unordered_map<std::string, std::string> ret;
for (size_t offset = r.size() - 4; offset >= 4;) {
uint32_t size = r.pget_u32l(offset) ^ 0x12345678;
uint32_t entry_offset = offset - size;
const auto& entry = r.pget<Entry>(entry_offset);
string data = r.pread(entry_offset + sizeof(Entry), entry.compressed_size);
string filename = entry.filename.decode();
std::string data = r.pread(entry_offset + sizeof(Entry), entry.compressed_size);
std::string filename = entry.filename.decode();
decrypt_ppk_data(data, phosg::tolower(filename), password);
uint32_t checksum = phosg::crc32(data.data(), data.size());
if (checksum != entry.checksum) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"incorrect checksum for file {} (expected {:08X}; received {:08X})", filename, entry.checksum, checksum));
}
if (entry.compressed_size < entry.decompressed_size) {
data = prs_decompress(data);
}
if (!ret.emplace(filename, data).second) {
throw runtime_error(std::format("archive contains multiple files with the same name ({})", filename));
throw std::runtime_error(std::format("archive contains multiple files with the same name ({})", filename));
}
offset = entry_offset - 4;
}
+33 -35
View File
@@ -9,8 +9,6 @@
#include <stdexcept>
#include <string>
using namespace std;
RandomGenerator::RandomGenerator(uint32_t seed) : initial_seed(seed) {}
DisabledRandomGenerator::DisabledRandomGenerator() : RandomGenerator(0) {}
@@ -62,7 +60,7 @@ void PSOLFGEncryption::encrypt_big_endian_minus(void* vdata, size_t size) {
void PSOLFGEncryption::encrypt_both_endian(void* le_vdata, void* be_vdata, size_t size) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
throw std::invalid_argument("size must be a multiple of 4");
}
size >>= 2;
@@ -185,7 +183,7 @@ PSOBBEncryption::PSOBBEncryption(const KeyFile& key, const void* original_seed,
void PSOBBEncryption::encrypt(void* vdata, size_t size) {
if (this->state.subtype == Subtype::TFS1) {
if (size & 7) {
throw invalid_argument("size must be a multiple of 8");
throw std::invalid_argument("size must be a multiple of 8");
}
le_uint32_t* dwords = reinterpret_cast<le_uint32_t*>(vdata);
@@ -212,7 +210,7 @@ void PSOBBEncryption::encrypt(void* vdata, size_t size) {
} else if (this->state.subtype == Subtype::JSD1) {
if (size & 1) {
throw invalid_argument("size must be a multiple of 2");
throw std::invalid_argument("size must be a multiple of 2");
}
uint8_t* bytes = reinterpret_cast<uint8_t*>(vdata);
for (size_t z = 0; z < size; z++) {
@@ -230,7 +228,7 @@ void PSOBBEncryption::encrypt(void* vdata, size_t size) {
} else { // STANDARD or MOCB1
if (size & 7) {
throw invalid_argument("size must be a multiple of 8");
throw std::invalid_argument("size must be a multiple of 8");
}
size_t num_dwords = size >> 2;
@@ -274,7 +272,7 @@ void PSOBBEncryption::encrypt(void* vdata, size_t size) {
void PSOBBEncryption::decrypt(void* vdata, size_t size) {
if (this->state.subtype == Subtype::TFS1) {
if (size & 7) {
throw invalid_argument("size must be a multiple of 8");
throw std::invalid_argument("size must be a multiple of 8");
}
le_uint32_t* dwords = reinterpret_cast<le_uint32_t*>(vdata);
@@ -301,7 +299,7 @@ void PSOBBEncryption::decrypt(void* vdata, size_t size) {
} else if (this->state.subtype == Subtype::JSD1) {
if (size & 1) {
throw invalid_argument("size must be a multiple of 2");
throw std::invalid_argument("size must be a multiple of 2");
}
uint8_t* bytes = reinterpret_cast<uint8_t*>(vdata);
for (size_t z = 0; z < size; z += 2) {
@@ -318,7 +316,7 @@ void PSOBBEncryption::decrypt(void* vdata, size_t size) {
} else { // STANDARD or MOCB1
if (size & 7) {
throw invalid_argument("size must be a multiple of 8");
throw std::invalid_argument("size must be a multiple of 8");
}
size_t num_dwords = size >> 2;
le_uint32_t* dwords = reinterpret_cast<le_uint32_t*>(vdata);
@@ -385,7 +383,7 @@ void PSOBBEncryption::tfs1_scramble(uint32_t* out1, uint32_t* out2) const {
void PSOBBEncryption::apply_seed(const void* original_seed, size_t seed_size) {
// Note: This part is done in the 03 command handler in the BB client, and isn't actually part of the encryption
// library. (Why did they do this?)
string seed;
std::string seed;
const uint8_t* original_seed_data = reinterpret_cast<const uint8_t*>(original_seed);
for (size_t x = 0; x < seed_size; x += 3) {
seed.push_back(original_seed_data[x] ^ 0x19);
@@ -431,7 +429,7 @@ void PSOBBEncryption::apply_seed(const void* original_seed, size_t seed_size) {
} else { // STANDARD or MOCB1 (they share most of their logic)
if (seed_size % 3) {
throw invalid_argument("seed size must be divisible by 3");
throw std::invalid_argument("seed size must be divisible by 3");
}
if (this->state.subtype == Subtype::MOCB1) {
@@ -641,27 +639,27 @@ PSOV2OrV3DetectorEncryption::PSOV2OrV3DetectorEncryption(
void PSOV2OrV3DetectorEncryption::encrypt(void* data, size_t size) {
if (!this->active_crypt) {
if (size != 4) {
throw logic_error("initial detector decrypt size must be 4");
throw std::logic_error("initial detector decrypt size must be 4");
}
le_uint32_t encrypted = *reinterpret_cast<le_uint32_t*>(data);
le_uint32_t decrypted_v2 = encrypted;
auto v2_crypt = make_unique<PSOV2Encryption>(this->key);
auto v2_crypt = std::make_unique<PSOV2Encryption>(this->key);
v2_crypt->decrypt(&decrypted_v2, sizeof(decrypted_v2));
le_uint32_t decrypted_v3 = encrypted;
auto v3_crypt = make_unique<PSOV3Encryption>(this->key);
auto v3_crypt = std::make_unique<PSOV3Encryption>(this->key);
v3_crypt->decrypt(&decrypted_v3, sizeof(decrypted_v3));
bool v2_match = this->v2_matches.count(decrypted_v2);
bool v3_match = this->v3_matches.count(decrypted_v3);
if (!v2_match && !v3_match) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"unable to determine crypt version (input={:08X}, v2={:08X}, v3={:08X})",
encrypted, decrypted_v2, decrypted_v3));
} else if (v2_match && v3_match) {
throw runtime_error(std::format("ambiguous crypt version (v2={:08X}, v3={:08X})", decrypted_v2, decrypted_v3));
throw std::runtime_error(std::format("ambiguous crypt version (v2={:08X}, v3={:08X})", decrypted_v2, decrypted_v3));
} else if (v2_match) {
this->active_crypt = std::move(v2_crypt);
*reinterpret_cast<le_uint32_t*>(data) = decrypted_v2;
@@ -676,7 +674,7 @@ void PSOV2OrV3DetectorEncryption::encrypt(void* data, size_t size) {
PSOEncryption::Type PSOV2OrV3DetectorEncryption::type() const {
if (!this->active_crypt) {
throw logic_error("detector encryption state is indeterminate");
throw std::logic_error("detector encryption state is indeterminate");
}
return this->active_crypt->type();
}
@@ -689,11 +687,11 @@ void PSOV2OrV3ImitatorEncryption::encrypt(void* data, size_t size) {
if (!this->active_crypt) {
auto t = this->detector_crypt->type();
if (t == Type::V2) {
this->active_crypt = make_shared<PSOV2Encryption>(this->key);
this->active_crypt = std::make_shared<PSOV2Encryption>(this->key);
} else if (t == Type::V3) {
this->active_crypt = make_shared<PSOV3Encryption>(this->key);
this->active_crypt = std::make_shared<PSOV3Encryption>(this->key);
} else {
throw logic_error("detector crypt is not V2 or V3");
throw std::logic_error("detector crypt is not V2 or V3");
}
}
this->active_crypt->encrypt(data, size);
@@ -707,8 +705,8 @@ PSOEncryption::Type PSOV2OrV3ImitatorEncryption::type() const {
}
PSOBBMultiKeyDetectorEncryption::PSOBBMultiKeyDetectorEncryption(
const vector<shared_ptr<const PSOBBEncryption::KeyFile>>& possible_keys,
const unordered_set<string>& expected_first_data,
const std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>>& possible_keys,
const std::unordered_set<std::string>& expected_first_data,
const void* seed,
size_t seed_size)
: possible_keys(possible_keys),
@@ -717,7 +715,7 @@ PSOBBMultiKeyDetectorEncryption::PSOBBMultiKeyDetectorEncryption(
void PSOBBMultiKeyDetectorEncryption::encrypt(void* data, size_t size) {
if (!this->active_crypt.get()) {
throw logic_error("PSOBB multi-key encryption requires client input first");
throw std::logic_error("PSOBB multi-key encryption requires client input first");
}
this->active_crypt->encrypt(data, size);
}
@@ -729,13 +727,13 @@ void PSOBBMultiKeyDetectorEncryption::decrypt(void* data, size_t size) {
}
if (size != 8) {
throw logic_error("initial decryption size does not match expected first data size");
throw std::logic_error("initial decryption size does not match expected first data size");
}
for (const auto& key : this->possible_keys) {
this->active_key = key;
this->active_crypt = make_shared<PSOBBEncryption>(*this->active_key, this->seed.data(), this->seed.size());
string test_data(reinterpret_cast<const char*>(data), size);
this->active_crypt = std::make_shared<PSOBBEncryption>(*this->active_key, this->seed.data(), this->seed.size());
std::string test_data(reinterpret_cast<const char*>(data), size);
this->active_crypt->decrypt(test_data.data(), test_data.size());
if (this->expected_first_data.count(test_data)) {
memcpy(data, test_data.data(), size);
@@ -744,7 +742,7 @@ void PSOBBMultiKeyDetectorEncryption::decrypt(void* data, size_t size) {
this->active_key.reset();
this->active_crypt.reset();
}
throw runtime_error("none of the registered private keys are valid for this client");
throw std::runtime_error("none of the registered private keys are valid for this client");
}
PSOEncryption::Type PSOBBMultiKeyDetectorEncryption::type() const {
@@ -752,7 +750,7 @@ PSOEncryption::Type PSOBBMultiKeyDetectorEncryption::type() const {
}
PSOBBMultiKeyImitatorEncryption::PSOBBMultiKeyImitatorEncryption(
shared_ptr<const PSOBBMultiKeyDetectorEncryption> detector_crypt,
std::shared_ptr<const PSOBBMultiKeyDetectorEncryption> detector_crypt,
const void* seed,
size_t seed_size,
bool jsd1_use_detector_seed)
@@ -772,19 +770,19 @@ PSOEncryption::Type PSOBBMultiKeyImitatorEncryption::type() const {
return Type::BB;
}
shared_ptr<PSOBBEncryption> PSOBBMultiKeyImitatorEncryption::ensure_crypt() {
std::shared_ptr<PSOBBEncryption> PSOBBMultiKeyImitatorEncryption::ensure_crypt() {
if (!this->active_crypt.get()) {
auto key = this->detector_crypt->get_active_key();
if (!key.get()) {
throw logic_error("server crypt cannot be initialized because client crypt is not ready");
throw std::logic_error("server crypt cannot be initialized because client crypt is not ready");
}
// Hack: JSD1 uses the client seed for both ends of the connection and ignores the server seed (though each end has
// its own state after that). To handle this, we use the other crypt's seed if the type is JSD1.
if ((key->subtype == PSOBBEncryption::Subtype::JSD1) && this->jsd1_use_detector_seed) {
const auto& detector_seed = this->detector_crypt->get_seed();
this->active_crypt = make_shared<PSOBBEncryption>(*key, detector_seed.data(), detector_seed.size());
this->active_crypt = std::make_shared<PSOBBEncryption>(*key, detector_seed.data(), detector_seed.size());
} else {
this->active_crypt = make_shared<PSOBBEncryption>(*key, this->seed.data(), this->seed.size());
this->active_crypt = std::make_shared<PSOBBEncryption>(*key, this->seed.data(), this->seed.size());
}
}
return this->active_crypt;
@@ -836,7 +834,7 @@ static uint8_t count_one_bits(uint16_t v) {
}
uint32_t encrypt_challenge_time(uint16_t value) {
vector<uint8_t> available_bits({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
std::vector<uint8_t> available_bits({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
uint16_t mask = 0;
uint8_t num_one_bits = (phosg::random_object<uint8_t>() % 9) + 4; // Range [4, 12]
@@ -856,8 +854,8 @@ uint16_t decrypt_challenge_time(uint32_t value) {
return ((mask_one_bits < 4) || (mask_one_bits > 12)) ? 0xFFFF : ((mask ^ value) & 0xFFFF);
}
string decrypt_v2_registry_value(const void* data, size_t size) {
string ret(reinterpret_cast<const char*>(data), size);
std::string decrypt_v2_registry_value(const void* data, size_t size) {
std::string ret(reinterpret_cast<const char*>(data), size);
PSOV2Encryption crypt(0x66);
for (size_t z = 0; z < size; z++) {
ret[z] ^= (crypt.next() & 0x7F);
+3 -4
View File
@@ -421,12 +421,11 @@ std::string encrypt_pr2_data(const std::string& data, size_t decompressed_size,
w.put<U32T<BE>>(seed);
w.write(data);
std::string ret = std::move(w.str());
PSOV2Encryption crypt(seed);
if (BE) {
crypt.encrypt_big_endian(ret.data() + 8, ret.size() - 8);
crypt.encrypt_big_endian(w.str().data() + 8, w.str().size() - 8);
} else {
crypt.decrypt(ret.data() + 8, ret.size() - 8);
crypt.decrypt(w.str().data() + 8, w.str().size() - 8);
}
return ret;
return std::move(w.str());
}
+8 -10
View File
@@ -2,8 +2,6 @@
#include "Text.hh"
using namespace std;
struct TObjectVTable {
phosg::be_uint32_t unused_a1;
phosg::be_uint32_t unused_a2;
@@ -24,19 +22,19 @@ struct TObject {
phosg::be_uint32_t vtable_addr;
} __packed_ws__(TObject, 0x1C);
PSOGCObjectGraph::PSOGCObjectGraph(const string& memory_data, uint32_t root_address) {
PSOGCObjectGraph::PSOGCObjectGraph(const std::string& memory_data, uint32_t root_address) {
phosg::StringReader r(memory_data);
this->root = this->parse_object_memo(r, root_address);
}
shared_ptr<PSOGCObjectGraph::VTable> PSOGCObjectGraph::parse_vtable_memo(phosg::StringReader& r, uint32_t addr) {
std::shared_ptr<PSOGCObjectGraph::VTable> PSOGCObjectGraph::parse_vtable_memo(phosg::StringReader& r, uint32_t addr) {
try {
return this->all_vtables.at(addr);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
const auto& vt = r.pget<TObjectVTable>(addr & 0x01FFFFFF);
auto ret = this->all_vtables.emplace(addr, make_shared<VTable>()).first->second;
auto ret = this->all_vtables.emplace(addr, std::make_shared<VTable>()).first->second;
ret->address = addr;
ret->destroy_addr = vt.destroy;
ret->update_addr = vt.update;
@@ -45,16 +43,16 @@ shared_ptr<PSOGCObjectGraph::VTable> PSOGCObjectGraph::parse_vtable_memo(phosg::
return ret;
}
shared_ptr<PSOGCObjectGraph::Object> PSOGCObjectGraph::parse_object_memo(phosg::StringReader& r, uint32_t addr) {
std::shared_ptr<PSOGCObjectGraph::Object> PSOGCObjectGraph::parse_object_memo(phosg::StringReader& r, uint32_t addr) {
try {
return this->all_objects.at(addr);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
const auto& obj = r.pget<TObject>(addr & 0x01FFFFFF);
string type_name = r.pget_cstr(obj.type_name_addr & 0x01FFFFFF);
std::string type_name = r.pget_cstr(obj.type_name_addr & 0x01FFFFFF);
auto ret = this->all_objects.emplace(addr, make_shared<Object>()).first->second;
auto ret = this->all_objects.emplace(addr, std::make_shared<Object>()).first->second;
ret->address = addr;
ret->flags = obj.flags;
ret->type_name = std::move(type_name);
+7 -9
View File
@@ -5,8 +5,6 @@
#include "Text.hh"
using namespace std;
extern bool use_terminal_colors;
PSOCommandHeader::PSOCommandHeader() {
@@ -37,7 +35,7 @@ uint16_t PSOCommandHeader::command(Version version) const {
case Version::BB_V4:
return this->bb.command;
default:
throw logic_error("unknown game version");
throw std::logic_error("unknown game version");
}
}
@@ -68,7 +66,7 @@ void PSOCommandHeader::set_command(Version version, uint16_t command) {
this->bb.command = command;
break;
default:
throw logic_error("unknown game version");
throw std::logic_error("unknown game version");
}
}
@@ -94,7 +92,7 @@ uint16_t PSOCommandHeader::size(Version version) const {
case Version::BB_V4:
return this->bb.size;
default:
throw logic_error("unknown game version");
throw std::logic_error("unknown game version");
}
}
@@ -125,7 +123,7 @@ void PSOCommandHeader::set_size(Version version, uint32_t size) {
this->bb.size = size;
break;
default:
throw logic_error("unknown game version");
throw std::logic_error("unknown game version");
}
}
@@ -151,7 +149,7 @@ uint32_t PSOCommandHeader::flag(Version version) const {
case Version::BB_V4:
return this->bb.flag;
default:
throw logic_error("unknown game version");
throw std::logic_error("unknown game version");
}
}
@@ -182,7 +180,7 @@ void PSOCommandHeader::set_flag(Version version, uint32_t flag) {
this->bb.flag = flag;
break;
default:
throw logic_error("unknown game version");
throw std::logic_error("unknown game version");
}
}
@@ -255,7 +253,7 @@ std::string prepend_command_header(
}
default:
throw logic_error("unimplemented game version in prepend_command_header");
throw std::logic_error("unimplemented game version in prepend_command_header");
}
ret.write(data);
return std::move(ret.str());
+9 -11
View File
@@ -22,8 +22,6 @@
#include "ReceiveSubcommands.hh"
#include "SendCommands.hh"
using namespace std;
PatchDownloadSession::PatchDownloadSession(
std::shared_ptr<asio::io_context> io_context,
const std::string& remote_host,
@@ -54,9 +52,9 @@ PatchDownloadSession::PatchDownloadSession(
}
asio::awaitable<void> PatchDownloadSession::run() {
string netloc_str = std::format("{}:{}", this->remote_host, this->remote_port);
std::string netloc_str = std::format("{}:{}", this->remote_host, this->remote_port);
this->log.info_f("Connecting to {}", netloc_str);
auto sock = make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(this->remote_host, this->remote_port));
auto sock = std::make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(this->remote_host, this->remote_port));
this->channel = SocketChannel::create(
this->io_context,
std::move(sock),
@@ -79,14 +77,14 @@ void PatchDownloadSession::check_path_token(const std::string& token) {
if (token == "..") {
throw std::runtime_error("parent directory token is not allowed");
}
if ((token.find('/') != string::npos) || (token.find('\\') != string::npos)) {
if ((token.find('/') != std::string::npos) || (token.find('\\') != std::string::npos)) {
throw std::runtime_error("directory token contains path separator");
}
}
std::string PatchDownloadSession::resolve_filename(const std::string& filename) const {
check_path_token(filename);
string path = this->output_dir;
std::string path = this->output_dir;
for (const auto& dir_name : this->dir_path) {
path.push_back('/');
path += dir_name;
@@ -105,8 +103,8 @@ asio::awaitable<void> PatchDownloadSession::on_message(Channel::Message& msg) {
if (cmd.copyright.decode() != "Patch Server. Copyright SonicTeam, LTD. 2001") {
throw std::runtime_error("incorrect copyright message");
}
this->channel->crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
this->channel->crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
this->channel->crypt_in = std::make_shared<PSOV2Encryption>(cmd.server_key);
this->channel->crypt_out = std::make_shared<PSOV2Encryption>(cmd.client_key);
this->channel->send(0x02);
this->log.info_f("Enabled encryption");
break;
@@ -181,7 +179,7 @@ asio::awaitable<void> PatchDownloadSession::on_message(Channel::Message& msg) {
}
const auto& cmd = msg.check_size_t<S_EnterDirectory_Patch_09>();
string dirname = cmd.name.decode();
std::string dirname = cmd.name.decode();
check_path_token(dirname);
this->dir_path.emplace_back(std::move(dirname));
std::filesystem::create_directories(this->resolve_filename(""));
@@ -257,9 +255,9 @@ asio::awaitable<void> PatchDownloadSession::on_message(Channel::Message& msg) {
const auto& cmd = msg.check_size_t<S_Reconnect_Patch_14>();
auto new_ep = make_endpoint_ipv4(cmd.address, cmd.port);
string netloc_str = str_for_endpoint(new_ep);
std::string netloc_str = str_for_endpoint(new_ep);
this->log.info_f("Connecting to {}", netloc_str);
auto sock = make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(new_ep));
auto sock = std::make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(new_ep));
auto old_channel = this->channel;
auto new_channel = SocketChannel::create(
+24 -31
View File
@@ -12,8 +12,6 @@
#include "Loggers.hh"
using namespace std;
int64_t file_mtime_int(const std::string& path) {
auto mtime = std::filesystem::last_write_time(path);
auto sctp = std::chrono::time_point_cast<std::chrono::nanoseconds>(
@@ -21,32 +19,27 @@ int64_t file_mtime_int(const std::string& path) {
return sctp.time_since_epoch().count();
}
PatchFileIndex::File::File(PatchFileIndex* index)
: index(index),
crc32(0),
size(0) {}
PatchFileIndex::File::File(PatchFileIndex* index) : index(index), crc32(0), size(0) {}
std::shared_ptr<const std::string> PatchFileIndex::File::load_data() {
if (!this->loaded_data) {
string relative_path = phosg::join(this->path_directories, "/") + "/" + this->name;
string full_path = this->index->root_dir + "/" + relative_path;
std::string relative_path = phosg::join(this->path_directories, "/") + "/" + this->name;
std::string full_path = this->index->root_dir + "/" + relative_path;
patch_index_log.debug_f("Loading data for {}", relative_path);
this->loaded_data = make_shared<string>(phosg::load_file(full_path));
this->loaded_data = std::make_shared<std::string>(phosg::load_file(full_path));
this->size = this->loaded_data->size();
}
return this->loaded_data;
}
PatchFileIndex::PatchFileIndex(const string& root_dir)
: root_dir(root_dir) {
string metadata_cache_filename = root_dir + "/.metadata-cache.json";
PatchFileIndex::PatchFileIndex(const std::string& root_dir) : root_dir(root_dir) {
std::string metadata_cache_filename = root_dir + "/.metadata-cache.json";
phosg::JSON metadata_cache_json;
try {
string metadata_text = phosg::load_file(metadata_cache_filename);
std::string metadata_text = phosg::load_file(metadata_cache_filename);
metadata_cache_json = phosg::JSON::parse(metadata_text);
patch_index_log.debug_f("Loaded patch metadata cache from {}", metadata_cache_filename);
} catch (const exception& e) {
} catch (const std::exception& e) {
metadata_cache_json = phosg::JSON::dict();
patch_index_log.warning_f("Cannot load patch metadata cache from {}: {}", metadata_cache_filename, e.what());
}
@@ -56,45 +49,45 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
bool should_write_metadata_cache = false;
phosg::JSON new_metadata_cache_json = phosg::JSON::dict();
vector<string> path_directories;
function<void(const string&)> collect_dir = [&](const string& dir) -> void {
std::vector<std::string> path_directories;
std::function<void(const std::string&)> collect_dir = [&](const std::string& dir) -> void {
path_directories.emplace_back(dir);
string relative_dirs = phosg::join(path_directories, "/");
string full_dir_path = root_dir + '/' + relative_dirs;
std::string relative_dirs = phosg::join(path_directories, "/");
std::string full_dir_path = root_dir + '/' + relative_dirs;
patch_index_log.debug_f("Listing directory {}", full_dir_path);
for (const auto& dir_item : std::filesystem::directory_iterator(full_dir_path)) {
string item = dir_item.path().filename().string();
std::string item = dir_item.path().filename().string();
// Skip invisible files (e.g. .DS_Store on macOS)
if (item.starts_with(".")) {
continue;
}
string relative_item_path = relative_dirs + '/' + item;
string full_item_path = root_dir + '/' + relative_item_path;
std::string relative_item_path = relative_dirs + '/' + item;
std::string full_item_path = root_dir + '/' + relative_item_path;
if (std::filesystem::is_directory(full_item_path)) {
collect_dir(item);
} else if (std::filesystem::is_regular_file(full_item_path)) {
auto f = make_shared<File>(this);
auto f = std::make_shared<File>(this);
f->path_directories = path_directories;
f->name = item;
int64_t file_mtime = file_mtime_int(full_item_path);
string compute_crc32s_message; // If not empty, should compute crc32s
std::string compute_crc32s_message; // If not empty, should compute crc32s
phosg::JSON cache_item_json;
try {
cache_item_json = metadata_cache_json.at(relative_item_path);
uint64_t cached_size = cache_item_json.get_int(0);
int64_t cached_mtime = cache_item_json.get_int(1);
if (file_mtime != cached_mtime) {
throw runtime_error("file has been modified");
throw std::runtime_error("file has been modified");
}
if (std::filesystem::file_size(full_item_path) != cached_size) {
throw runtime_error("file size has changed");
throw std::runtime_error("file size has changed");
}
f->size = cached_size;
f->crc32 = cache_item_json.get_int(2);
@@ -102,7 +95,7 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
f->chunk_crcs.emplace_back(chunk_crc32_json->as_int());
}
} catch (const exception& e) {
} catch (const std::exception& e) {
compute_crc32s_message = e.what();
}
@@ -110,7 +103,7 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
auto data = f->load_data(); // Sets f->size
f->crc32 = phosg::crc32(data->data(), f->size);
for (size_t x = 0; x < data->size(); x += this->CHUNK_SIZE) {
size_t chunk_bytes = min<size_t>(f->size - x, this->CHUNK_SIZE);
size_t chunk_bytes = std::min<size_t>(f->size - x, this->CHUNK_SIZE);
f->chunk_crcs.emplace_back(phosg::crc32(data->data() + x, chunk_bytes));
}
@@ -149,7 +142,7 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
try {
phosg::save_file(metadata_cache_filename, new_metadata_cache_json.serialize());
patch_index_log.debug_f("Saved patch metadata cache to {}", metadata_cache_filename);
} catch (const exception& e) {
} catch (const std::exception& e) {
patch_index_log.warning_f("Cannot save patch metadata cache to {}: {}", metadata_cache_filename, e.what());
}
} else {
@@ -157,10 +150,10 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
}
}
const vector<shared_ptr<PatchFileIndex::File>>& PatchFileIndex::all_files() const {
const std::vector<std::shared_ptr<PatchFileIndex::File>>& PatchFileIndex::all_files() const {
return this->files_by_patch_order;
}
shared_ptr<PatchFileIndex::File> PatchFileIndex::get(const string& filename) const {
std::shared_ptr<PatchFileIndex::File> PatchFileIndex::get(const std::string& filename) const {
return this->files_by_name.at(filename);
}
-1
View File
@@ -12,7 +12,6 @@
#include <vector>
#include "ChoiceSearch.hh"
#include "FileContentsCache.hh"
#include "ItemData.hh"
#include "PSOEncryption.hh"
#include "Text.hh"
+21 -23
View File
@@ -20,8 +20,6 @@
#include "Text.hh"
#include "Version.hh"
using namespace std;
void PlayerDispDataBB::apply_dressing_room(const PlayerDispDataBBPreview& pre) {
this->visual.name_color = pre.visual.name_color;
this->visual.extra_model = pre.visual.extra_model;
@@ -414,7 +412,7 @@ const char* phosg::name_for_enum<BattleRules::TechDiskMode>(BattleRules::TechDis
case BattleRules::TechDiskMode::LIMIT_LEVEL:
return "LIMIT_LEVEL";
default:
throw invalid_argument("invalid BattleRules::TechDiskMode value");
throw std::invalid_argument("invalid BattleRules::TechDiskMode value");
}
}
template <>
@@ -426,7 +424,7 @@ BattleRules::TechDiskMode phosg::enum_for_name<BattleRules::TechDiskMode>(const
} else if (!strcmp(name, "LIMIT_LEVEL")) {
return BattleRules::TechDiskMode::LIMIT_LEVEL;
} else {
throw invalid_argument("invalid BattleRules::TechDiskMode name");
throw std::invalid_argument("invalid BattleRules::TechDiskMode name");
}
}
@@ -442,7 +440,7 @@ const char* phosg::name_for_enum<BattleRules::WeaponAndArmorMode>(BattleRules::W
case BattleRules::WeaponAndArmorMode::FORBID_RARES:
return "FORBID_RARES";
default:
throw invalid_argument("invalid BattleRules::WeaponAndArmorMode value");
throw std::invalid_argument("invalid BattleRules::WeaponAndArmorMode value");
}
}
template <>
@@ -456,7 +454,7 @@ BattleRules::WeaponAndArmorMode phosg::enum_for_name<BattleRules::WeaponAndArmor
} else if (!strcmp(name, "FORBID_RARES")) {
return BattleRules::WeaponAndArmorMode::FORBID_RARES;
} else {
throw invalid_argument("invalid BattleRules::WeaponAndArmorMode name");
throw std::invalid_argument("invalid BattleRules::WeaponAndArmorMode name");
}
}
@@ -468,7 +466,7 @@ const char* phosg::name_for_enum<BattleRules::MagMode>(BattleRules::MagMode v) {
case BattleRules::MagMode::FORBID_ALL:
return "FORBID_ALL";
default:
throw invalid_argument("invalid BattleRules::MagMode value");
throw std::invalid_argument("invalid BattleRules::MagMode value");
}
}
template <>
@@ -478,7 +476,7 @@ BattleRules::MagMode phosg::enum_for_name<BattleRules::MagMode>(const char* name
} else if (!strcmp(name, "FORBID_ALL")) {
return BattleRules::MagMode::FORBID_ALL;
} else {
throw invalid_argument("invalid BattleRules::MagMode name");
throw std::invalid_argument("invalid BattleRules::MagMode name");
}
}
@@ -492,7 +490,7 @@ const char* phosg::name_for_enum<BattleRules::ToolMode>(BattleRules::ToolMode v)
case BattleRules::ToolMode::FORBID_ALL:
return "FORBID_ALL";
default:
throw invalid_argument("invalid BattleRules::ToolMode value");
throw std::invalid_argument("invalid BattleRules::ToolMode value");
}
}
template <>
@@ -504,7 +502,7 @@ BattleRules::ToolMode phosg::enum_for_name<BattleRules::ToolMode>(const char* na
} else if (!strcmp(name, "FORBID_ALL")) {
return BattleRules::ToolMode::FORBID_ALL;
} else {
throw invalid_argument("invalid BattleRules::ToolMode name");
throw std::invalid_argument("invalid BattleRules::ToolMode name");
}
}
@@ -516,7 +514,7 @@ const char* phosg::name_for_enum<BattleRules::TrapMode>(BattleRules::TrapMode v)
case BattleRules::TrapMode::ALL_PLAYERS:
return "ALL_PLAYERS";
default:
throw invalid_argument("invalid BattleRules::TrapMode value");
throw std::invalid_argument("invalid BattleRules::TrapMode value");
}
}
template <>
@@ -526,7 +524,7 @@ BattleRules::TrapMode phosg::enum_for_name<BattleRules::TrapMode>(const char* na
} else if (!strcmp(name, "ALL_PLAYERS")) {
return BattleRules::TrapMode::ALL_PLAYERS;
} else {
throw invalid_argument("invalid BattleRules::TrapMode name");
throw std::invalid_argument("invalid BattleRules::TrapMode name");
}
}
@@ -540,7 +538,7 @@ const char* phosg::name_for_enum<BattleRules::MesetaMode>(BattleRules::MesetaMod
case BattleRules::MesetaMode::CLEAR_AND_ALLOW:
return "CLEAR_AND_ALLOW";
default:
throw invalid_argument("invalid BattleRules::MesetaDropMode value");
throw std::invalid_argument("invalid BattleRules::MesetaDropMode value");
}
}
template <>
@@ -552,7 +550,7 @@ BattleRules::MesetaMode phosg::enum_for_name<BattleRules::MesetaMode>(const char
} else if (!strcmp(name, "CLEAR_AND_ALLOW")) {
return BattleRules::MesetaMode::CLEAR_AND_ALLOW;
} else {
throw invalid_argument("invalid BattleRules::MesetaDropMode name");
throw std::invalid_argument("invalid BattleRules::MesetaDropMode name");
}
}
@@ -566,7 +564,7 @@ const char* phosg::name_for_enum<BattleRules::RespawnMode>(BattleRules::RespawnM
case BattleRules::RespawnMode::LIMIT_LIVES:
return "LIMIT_LIVES";
default:
throw invalid_argument("invalid BattleRules::MesetaDropMode value");
throw std::invalid_argument("invalid BattleRules::MesetaDropMode value");
}
}
template <>
@@ -578,7 +576,7 @@ BattleRules::RespawnMode phosg::enum_for_name<BattleRules::RespawnMode>(const ch
} else if (!strcmp(name, "LIMIT_LIVES")) {
return BattleRules::RespawnMode::LIMIT_LIVES;
} else {
throw invalid_argument("invalid BattleRules::MesetaDropMode name");
throw std::invalid_argument("invalid BattleRules::MesetaDropMode name");
}
}
@@ -598,7 +596,7 @@ static PlayerInventoryItem v3_item(bool equipped, uint64_t first_data, uint64_t
const ChallengeTemplateDefinition& get_challenge_template_definition(Version version, uint32_t class_flags, size_t index) {
// clang-format off
static const vector<ChallengeTemplateDefinition> v2_hunter_templates({
static const std::vector<ChallengeTemplateDefinition> v2_hunter_templates({
{0, {v2_item(true, 0x0001000000000000, 0x0000000000000000), v2_item(true, 0x0101000000000000, 0x0000000000000000), v2_item(true, 0x02000500F4010100, 0x0100010000002800), v2_item(false, 0x0300000000030000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{4, {v2_item(true, 0x0001000500000000, 0x0000000000000000), v2_item(true, 0x0101010000000000, 0x0000000000000000), v2_item(true, 0x0102000000000000, 0x0000000000000000), v2_item(true, 0x02010D002003F501, 0x0100010000002800), v2_item(false, 0x0300000000060000, 0x0000000000000000), v2_item(false, 0x0306010000030000, 0x0000000000000000), v2_item(false, 0x0306000000030000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{6, {v2_item(true, 0x0002000000000000, 0x0000000000000000), v2_item(true, 0x0101020000000000, 0x0000000000000000), v2_item(true, 0x0102010000000000, 0x0000000000000000), v2_item(true, 0x0201100020032103, 0x0100010000002800), v2_item(false, 0x0300000000060000, 0x0000000000000000), v2_item(false, 0x0306010000030000, 0x0000000000000000), v2_item(false, 0x0306000000030000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
@@ -616,7 +614,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
{50, {v2_item(true, 0x02058200A00F8913, 0xD107D10700002800), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{99, {v2_item(true, 0x0205BE007017591B, 0xB90BB90B00002800), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
});
static const vector<ChallengeTemplateDefinition> v2_ranger_templates({
static const std::vector<ChallengeTemplateDefinition> v2_ranger_templates({
{0, {v2_item(true, 0x0006000000000000, 0x0000000000000000), v2_item(true, 0x0101000000000000, 0x0000000000000000), v2_item(true, 0x02000500F4010100, 0x0100010000002800), v2_item(false, 0x0300000000030000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{4, {v2_item(true, 0x0006000500000000, 0x0000000000000000), v2_item(true, 0x0101010000000000, 0x0000000000000000), v2_item(true, 0x0102000000000000, 0x0000000000000000), v2_item(true, 0x020D0C00F401C900, 0xF501010000002800), v2_item(false, 0x0300000000050000, 0x0000000000000000), v2_item(false, 0x0306010000030000, 0x0000000000000000), v2_item(false, 0x0306000000030000, 0x0000000000000000), v2_item(false, 0x0308000000050000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{5, {v2_item(true, 0x0006000500000000, 0x0000000000000000), v2_item(true, 0x0101010000000000, 0x0000000000000000), v2_item(true, 0x0102010000000000, 0x0000000000000000), v2_item(true, 0x020D0E00F401C900, 0xBD02010000002800), v2_item(false, 0x0300000000050000, 0x0000000000000000), v2_item(false, 0x0306010000030000, 0x0000000000000000), v2_item(false, 0x0306000000030000, 0x0000000000000000), v2_item(false, 0x0308000000050000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
@@ -634,7 +632,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
{50, {v2_item(true, 0x020C8C00B80BC509, 0x7117C50900002800), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{99, {v2_item(true, 0x0206B400B80BB90B, 0x2923B90B00002800), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
});
static const vector<ChallengeTemplateDefinition> v2_force_templates({
static const std::vector<ChallengeTemplateDefinition> v2_force_templates({
{0, {v2_item(true, 0x000A000000000000, 0x0000000000000000), v2_item(true, 0x0101000000000000, 0x0000000000000000), v2_item(true, 0x02000500F4010100, 0x0100010000002800), v2_item(false, 0x0300000000040000, 0x0000000000000000), v2_item(false, 0x0301000000040000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 0}}},
{4, {v2_item(true, 0x000A000500000000, 0x0000000000000000), v2_item(true, 0x0101000000000000, 0x0000000000000000), v2_item(true, 0x0102000000000000, 0x0000000000000000), v2_item(true, 0x02190D0020036500, 0x0100910100002800), v2_item(false, 0x0300000000060000, 0x0000000000000000), v2_item(false, 0x0301000000060000, 0x0000000000000000), v2_item(false, 0x0306010000030000, 0x0000000000000000), v2_item(false, 0x0306000000030000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 2}, {0x03, 2}, {0x0D, 2}, {0x0A, 2}}},
{6, {v2_item(true, 0x000B000000000000, 0x0000000000000000), v2_item(true, 0x0101000000000000, 0x0000000000000000), v2_item(true, 0x0102000000000000, 0x0000000000000000), v2_item(true, 0x02190F002003C900, 0x0100F50100002800), v2_item(false, 0x0300000000060000, 0x0000000000000000), v2_item(false, 0x0301000000060000, 0x0000000000000000), v2_item(false, 0x0306010000030000, 0x0000000000000000), v2_item(false, 0x0306000000030000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 2}, {0x03, 2}, {0x0D, 2}, {0x0A, 2}}},
@@ -653,7 +651,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
{99, {v2_item(true, 0x021CB400AC0DD107, 0xC509112700002800), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000), v2_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 0}} },
});
static const vector<ChallengeTemplateDefinition> v3_hunter_templates({
static const std::vector<ChallengeTemplateDefinition> v3_hunter_templates({
{0, {v3_item(true, 0x0001000000000000, 0x0000000000000000), v3_item(true, 0x0101000000000000, 0x0000000000000000), v3_item(true, 0x02000500F4010000, 0x0000000028000012), v3_item(false, 0x0300000000030000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{4, {v3_item(true, 0x0001000500000000, 0x0000000000000000), v3_item(true, 0x0101010000000000, 0x0000000000000000), v3_item(true, 0x0102000000000000, 0x0000000000000000), v3_item(true, 0x02010D002003F401, 0x0000000028000012), v3_item(false, 0x0300000000060000, 0x0000000000000000), v3_item(false, 0x0306010000030000, 0x0000000000000000), v3_item(false, 0x0306000000030000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{6, {v3_item(true, 0x0002000000000000, 0x0000000000000000), v3_item(true, 0x0101020000000000, 0x0000000000000000), v3_item(true, 0x0102010000000000, 0x0000000000000000), v3_item(true, 0x0201100020032003, 0x0000000028000012), v3_item(false, 0x0300000000060000, 0x0000000000000000), v3_item(false, 0x0306010000030000, 0x0000000000000000), v3_item(false, 0x0306000000030000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
@@ -671,7 +669,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
{50, {v3_item(true, 0x02058200A00F8813, 0xD007D00728000012), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{99, {v3_item(true, 0x0205BE007017581B, 0xB80BB80B28000012), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
});
static const vector<ChallengeTemplateDefinition> v3_ranger_templates({
static const std::vector<ChallengeTemplateDefinition> v3_ranger_templates({
{0, {v3_item(true, 0x0006000000000000, 0x0000000000000000), v3_item(true, 0x0101000000000000, 0x0000000000000000), v3_item(true, 0x02000500F4010000, 0x0000000028000012), v3_item(false, 0x0300000000030000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{4, {v3_item(true, 0x0006000500000000, 0x0000000000000000), v3_item(true, 0x0101010000000000, 0x0000000000000000), v3_item(true, 0x0102000000000000, 0x0000000000000000), v3_item(true, 0x020D0C00F401C800, 0xF401000028000012), v3_item(false, 0x0300000000050000, 0x0000000000000000), v3_item(false, 0x0306010000030000, 0x0000000000000000), v3_item(false, 0x0306000000030000, 0x0000000000000000), v3_item(false, 0x0308000000050000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{5, {v3_item(true, 0x0006000500000000, 0x0000000000000000), v3_item(true, 0x0101010000000000, 0x0000000000000000), v3_item(true, 0x0102010000000000, 0x0000000000000000), v3_item(true, 0x020D0E00F401C800, 0xBC02000028000012), v3_item(false, 0x0300000000050000, 0x0000000000000000), v3_item(false, 0x0306010000030000, 0x0000000000000000), v3_item(false, 0x0306000000030000, 0x0000000000000000), v3_item(false, 0x0308000000050000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
@@ -689,7 +687,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
{50, {v3_item(true, 0x020C8C00B80BC409, 0x7017C40928000012), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
{99, {v3_item(true, 0x0206B400B80BB80B, 0x2823B80B28000012), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {}},
});
static const vector<ChallengeTemplateDefinition> v3_force_templates({
static const std::vector<ChallengeTemplateDefinition> v3_force_templates({
{0, {v3_item(true, 0x000A000000000000, 0x0000000000000000), v3_item(true, 0x0101000000000000, 0x0000000000000000), v3_item(true, 0x02000500F4010000, 0x0000000028000012), v3_item(false, 0x0300000000040000, 0x0000000000000000), v3_item(false, 0x0301000000040000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 0}}},
{4, {v3_item(true, 0x000A000500000000, 0x0000000000000000), v3_item(true, 0x0101000000000000, 0x0000000000000000), v3_item(true, 0x0102000000000000, 0x0000000000000000), v3_item(true, 0x02190D0020036400, 0x0000900128000012), v3_item(false, 0x0300000000060000, 0x0000000000000000), v3_item(false, 0x0301000000060000, 0x0000000000000000), v3_item(false, 0x0306010000030000, 0x0000000000000000), v3_item(false, 0x0306000000030000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 2}, {0x03, 2}, {0x0D, 2}, {0x0A, 2}}},
{6, {v3_item(true, 0x000B000000000000, 0x0000000000000000), v3_item(true, 0x0101000000000000, 0x0000000000000000), v3_item(true, 0x0102000000000000, 0x0000000000000000), v3_item(true, 0x02190F002003C800, 0x0000F40128000012), v3_item(false, 0x0300000000060000, 0x0000000000000000), v3_item(false, 0x0301000000060000, 0x0000000000000000), v3_item(false, 0x0306010000030000, 0x0000000000000000), v3_item(false, 0x0306000000030000, 0x0000000000000000), v3_item(false, 0x0309000000000000, 0x0000000000000000)}, {{0x00, 2}, {0x03, 2}, {0x0D, 2}, {0x0A, 2}}},
@@ -716,7 +714,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
} else if ((class_flags & 0xE0) == 0x80) {
return is_v1_or_v2(version) ? v2_force_templates.at(index) : v3_force_templates.at(index);
} else {
throw runtime_error("invalid class flags on original player");
throw std::runtime_error("invalid class flags on original player");
}
}
-1
View File
@@ -12,7 +12,6 @@
#include "ChoiceSearch.hh"
#include "CommonFileFormats.hh"
#include "FileContentsCache.hh"
#include "ItemData.hh"
#include "LevelTable.hh"
#include "PSOEncryption.hh"
+136 -137
View File
@@ -29,17 +29,15 @@
#include "ReceiveSubcommands.hh"
#include "SendCommands.hh"
using namespace std;
enum class HandlerResult {
FORWARD = 0,
SUPPRESS,
MODIFIED,
};
typedef asio::awaitable<HandlerResult> (*MessageHandler)(shared_ptr<Client> c, Channel::Message& msg);
typedef asio::awaitable<HandlerResult> (*MessageHandler)(std::shared_ptr<Client> c, Channel::Message& msg);
static void forward_command(shared_ptr<Client> c, bool to_server, const Channel::Message& msg, bool print_contents = true) {
static void forward_command(std::shared_ptr<Client> c, bool to_server, const Channel::Message& msg, bool print_contents = true) {
auto ch = to_server ? (c->proxy_session ? c->proxy_session->server_channel : nullptr) : c->channel;
if (!ch || !ch->connected()) {
proxy_server_log.warning_f("No endpoint is present; dropping command");
@@ -55,20 +53,20 @@ static void forward_command(shared_ptr<Client> c, bool to_server, const Channel:
// versions), and COMMAND-NUMBERS are the hexadecimal value in the command header field that this handler is called
// for. If VERSIONS is omitted, the command handler is for all versions (for example, the 97 handler is like this).
static asio::awaitable<HandlerResult> default_handler(shared_ptr<Client>, Channel::Message&) {
static asio::awaitable<HandlerResult> default_handler(std::shared_ptr<Client>, Channel::Message&) {
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_invalid(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_invalid(std::shared_ptr<Client> c, Channel::Message& msg) {
c->log.error_f("Server sent invalid command");
string error_str = is_v4(c->version())
std::string error_str = is_v4(c->version())
? std::format("Server sent invalid\ncommand: {:04X} {:08X}", msg.command, msg.flag)
: std::format("Server sent invalid\ncommand: {:02X} {:02X}", msg.command, msg.flag);
c->proxy_session->server_channel->disconnect();
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> C_1D(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> C_1D(std::shared_ptr<Client> c, Channel::Message&) {
if (c->ping_start_time) {
uint64_t ping_usecs = phosg::now() - c->ping_start_time;
c->ping_start_time = 0;
@@ -80,11 +78,11 @@ static asio::awaitable<HandlerResult> C_1D(shared_ptr<Client> c, Channel::Messag
}
}
static asio::awaitable<HandlerResult> S_1D(shared_ptr<Client>, Channel::Message&) {
static asio::awaitable<HandlerResult> S_1D(std::shared_ptr<Client>, Channel::Message&) {
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_97(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> S_97(std::shared_ptr<Client> c, Channel::Message&) {
// We always assume a 97 has already been received by the client - we should have sent 97 01 before sending the
// client to the proxy server.
c->proxy_session->server_channel->send(0xB1, 0x00);
@@ -204,7 +202,7 @@ static void send_9E_XB_to_server(std::shared_ptr<Client> c) {
c->proxy_session->server_channel->send(0x9E, 0x01, &cmd, sizeof(C_LoginExtended_XB_9E));
}
static asio::awaitable<HandlerResult> S_G_9A(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> S_G_9A(std::shared_ptr<Client> c, Channel::Message&) {
// TODO: Either delete this handler or finish implementing it (flag=00/02 should do the below, 01 should send 9C,
// anything else should end the session)
C_LoginExtended_GC_9E cmd;
@@ -236,9 +234,9 @@ static asio::awaitable<HandlerResult> S_G_9A(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_V123U_02_17(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_V123U_02_17(std::shared_ptr<Client> c, Channel::Message& msg) {
if (is_patch(c->version()) && msg.command == 0x17) {
throw invalid_argument("patch server sent 17 server init");
throw std::invalid_argument("patch server sent 17 server init");
}
// Most servers don't include after_message or have a shorter after_message than newserv does, so don't require it
@@ -246,11 +244,11 @@ static asio::awaitable<HandlerResult> S_V123U_02_17(shared_ptr<Client> c, Channe
// This isn't forwarded to the client, so don't recreate the client's crypts
if (uses_v3_encryption(c->version())) {
c->proxy_session->server_channel->crypt_in = make_shared<PSOV3Encryption>(cmd.server_key);
c->proxy_session->server_channel->crypt_out = make_shared<PSOV3Encryption>(cmd.client_key);
c->proxy_session->server_channel->crypt_in = std::make_shared<PSOV3Encryption>(cmd.server_key);
c->proxy_session->server_channel->crypt_out = std::make_shared<PSOV3Encryption>(cmd.client_key);
} else {
c->proxy_session->server_channel->crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
c->proxy_session->server_channel->crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
c->proxy_session->server_channel->crypt_in = std::make_shared<PSOV2Encryption>(cmd.server_key);
c->proxy_session->server_channel->crypt_out = std::make_shared<PSOV2Encryption>(cmd.client_key);
}
// Respond with an appropriate login command. We don't let the client do this because it believes it already did
@@ -264,7 +262,7 @@ static asio::awaitable<HandlerResult> S_V123U_02_17(shared_ptr<Client> c, Channe
case Version::DC_NTE:
// TODO
throw runtime_error("DC NTE proxy is not implemented");
throw std::runtime_error("DC NTE proxy is not implemented");
case Version::DC_11_2000:
case Version::DC_V1:
@@ -301,7 +299,7 @@ static asio::awaitable<HandlerResult> S_V123U_02_17(shared_ptr<Client> c, Channe
// For command 02, send the same as if we had received 9A from the server
co_return co_await S_G_9A(c, msg);
}
throw logic_error("GC init command not handled");
throw std::logic_error("GC init command not handled");
case Version::XB_V3: {
send_9E_XB_to_server(c);
@@ -309,13 +307,13 @@ static asio::awaitable<HandlerResult> S_V123U_02_17(shared_ptr<Client> c, Channe
}
case Version::BB_V4:
throw logic_error("v1/v2/v3 server init handler should not be called on BB");
throw std::logic_error("v1/v2/v3 server init handler should not be called on BB");
default:
throw logic_error("invalid game version in server init handler");
throw std::logic_error("invalid game version in server init handler");
}
}
static asio::awaitable<HandlerResult> S_U_04(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> S_U_04(std::shared_ptr<Client> c, Channel::Message&) {
C_Login_Patch_04 ret;
ret.username.encode(c->username);
ret.password.encode(c->password);
@@ -324,7 +322,7 @@ static asio::awaitable<HandlerResult> S_U_04(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_B_03(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_03(std::shared_ptr<Client> c, Channel::Message& msg) {
// Most servers don't include after_message or have a shorter after_message than newserv does, so don't require it
const auto& cmd = msg.check_size_t<S_ServerInitDefault_BB_03_9B>(0xFFFF);
@@ -332,11 +330,11 @@ static asio::awaitable<HandlerResult> S_B_03(shared_ptr<Client> c, Channel::Mess
// the server has the luxury of being able to try all the crypts it knows to detect what type the client uses, but
// the client can't do this since it sends the first encrypted data on the connection.
if (!c->bb_detector_crypt) {
throw logic_error("Client proxy session started with missing detector crypt");
throw std::logic_error("Client proxy session started with missing detector crypt");
}
c->proxy_session->server_channel->crypt_in = make_shared<PSOBBMultiKeyImitatorEncryption>(
c->proxy_session->server_channel->crypt_in = std::make_shared<PSOBBMultiKeyImitatorEncryption>(
c->bb_detector_crypt, cmd.server_key.data(), sizeof(cmd.server_key), false);
c->proxy_session->server_channel->crypt_out = make_shared<PSOBBMultiKeyImitatorEncryption>(
c->proxy_session->server_channel->crypt_out = std::make_shared<PSOBBMultiKeyImitatorEncryption>(
c->bb_detector_crypt, cmd.client_key.data(), sizeof(cmd.client_key), false);
C_LoginWithHardwareInfo_BB_93 resp;
@@ -360,7 +358,7 @@ static asio::awaitable<HandlerResult> S_B_03(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_B_E6(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_E6(std::shared_ptr<Client> c, Channel::Message& msg) {
const auto& cmd = msg.check_size_t<S_ClientInit_BB_00E6>(0xFFFF);
c->proxy_session->remote_guild_card_number = cmd.guild_card_number;
c->bb_security_token = cmd.security_token;
@@ -376,7 +374,7 @@ static asio::awaitable<HandlerResult> S_B_E6(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_V123_04(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_V123_04(std::shared_ptr<Client> c, Channel::Message& msg) {
// Suppress extremely short commands from the server instead of disconnecting
if (msg.data.size() < offsetof(S_UpdateClientConfig_V3_04, client_config)) {
le_uint64_t checksum = phosg::random_object<uint64_t>() & 0x0000FFFFFFFFFFFF;
@@ -396,7 +394,7 @@ static asio::awaitable<HandlerResult> S_V123_04(shared_ptr<Client> c, Channel::M
if (c->proxy_session->remote_guild_card_number != cmd.guild_card_number) {
c->proxy_session->remote_guild_card_number = cmd.guild_card_number;
c->log.info_f("Remote guild card number set to {}", c->proxy_session->remote_guild_card_number);
string message = std::format(
std::string message = std::format(
"The remote server\nhas assigned your\nGuild Card number:\n$C6{}", c->proxy_session->remote_guild_card_number);
send_ship_info(c->channel, message);
}
@@ -411,7 +409,7 @@ static asio::awaitable<HandlerResult> S_V123_04(shared_ptr<Client> c, Channel::M
memcpy(c->proxy_session->remote_client_config_data.data(),
had_guild_card_number ? "t Lobby Server. Copyright SEGA E" : "t Port Map. Copyright SEGA Enter", 0x20);
memcpy(c->proxy_session->remote_client_config_data.data(), &cmd.client_config,
min<size_t>(msg.data.size() - offsetof(S_UpdateClientConfig_V3_04, client_config),
std::min<size_t>(msg.data.size() - offsetof(S_UpdateClientConfig_V3_04, client_config),
c->proxy_session->remote_client_config_data.bytes()));
// If the guild card number was not set, pretend (to the server) that this is the first 04 command the client has
@@ -424,7 +422,7 @@ static asio::awaitable<HandlerResult> S_V123_04(shared_ptr<Client> c, Channel::M
co_return c->login ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_V123_06(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_V123_06(std::shared_ptr<Client> c, Channel::Message& msg) {
bool modified = false;
if (c->login && c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
auto& cmd = msg.check_size_t<SC_TextHeader_01_06_11_B0_EE>(0xFFFF);
@@ -449,7 +447,7 @@ static asio::awaitable<HandlerResult> S_V123_06(shared_ptr<Client> c, Channel::M
}
template <typename CmdT>
static asio::awaitable<HandlerResult> S_41(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_41(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->login) {
auto& cmd = msg.check_size_t<CmdT>();
if ((cmd.searcher_guild_card_number == c->proxy_session->remote_guild_card_number) &&
@@ -485,7 +483,7 @@ constexpr MessageHandler S_P_41 = &S_41<S_GuildCardSearchResult_PC_41>;
constexpr MessageHandler S_B_41 = &S_41<S_GuildCardSearchResult_BB_41>;
template <typename CmdT>
static asio::awaitable<HandlerResult> S_81(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_81(std::shared_ptr<Client> c, Channel::Message& msg) {
bool modified = false;
if (c->login && c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
auto& cmd = msg.check_size_t<CmdT>();
@@ -505,7 +503,7 @@ constexpr MessageHandler S_DGX_81 = &S_81<SC_SimpleMail_DC_V3_81>;
constexpr MessageHandler S_P_81 = &S_81<SC_SimpleMail_PC_81>;
constexpr MessageHandler S_B_81 = &S_81<SC_SimpleMail_BB_81>;
static asio::awaitable<HandlerResult> S_88(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_88(std::shared_ptr<Client> c, Channel::Message& msg) {
// If the client isn't in the lobby, suppress the command (Ep3 can crash if it receives this while loading; other
// versions probably also will crash)
if (!c->proxy_session->is_in_lobby) {
@@ -526,19 +524,19 @@ static asio::awaitable<HandlerResult> S_88(shared_ptr<Client> c, Channel::Messag
co_return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_B1(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> S_B1(std::shared_ptr<Client> c, Channel::Message&) {
// Block all time updates from the remote server, so client's time remains consistent
c->proxy_session->server_channel->send(0x99, 0x00);
co_return HandlerResult::SUPPRESS;
}
template <bool BE>
static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B2(std::shared_ptr<Client> c, Channel::Message& msg) {
const auto& cmd = msg.check_size_t<S_ExecuteCode_B2>(0xFFFF);
if (cmd.code_size && c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
uint64_t filename_timestamp = phosg::now();
string code = msg.data.substr(sizeof(S_ExecuteCode_B2));
std::string code = msg.data.substr(sizeof(S_ExecuteCode_B2));
if (c->check_flag(Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL)) {
phosg::StringReader r(code);
@@ -547,7 +545,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
uint32_t key = is_big_endian ? r.get_u32b() : r.get_u32l();
PSOV2Encryption crypt(key);
string decrypted_data;
std::string decrypted_data;
if (is_big_endian) {
phosg::StringWriter w;
while (!r.eof()) {
@@ -563,7 +561,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
if (decompressed_size < code.size()) {
code.resize(decompressed_size);
} else if (decompressed_size > code.size()) {
throw runtime_error("decompressed code smaller than expected");
throw std::runtime_error("decompressed code smaller than expected");
}
} else {
@@ -573,7 +571,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
}
}
string output_filename = std::format("code.{}.bin", filename_timestamp);
std::string output_filename = std::format("code.{}.bin", filename_timestamp);
phosg::save_file(output_filename, msg.data);
c->log.info_f("Wrote code from server to file {}", output_filename);
@@ -585,7 +583,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
if (is_ppc || is_x86 || is_sh4) {
try {
if (code.size() < sizeof(FooterT)) {
throw runtime_error("code section is too small");
throw std::runtime_error("code section is too small");
}
size_t footer_offset = code.size() - sizeof(FooterT);
@@ -593,7 +591,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
phosg::StringReader r(code.data(), code.size());
const auto& footer = r.pget<FooterT>(footer_offset);
multimap<uint32_t, string> labels;
std::multimap<uint32_t, std::string> labels;
r.go(footer.relocations_offset);
uint32_t reloc_offset = 0;
for (size_t x = 0; x < footer.num_relocations; x++) {
@@ -604,7 +602,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
labels.emplace(footer_offset, "footer");
labels.emplace(r.pget<U32T<BE>>(footer.root_offset), "start");
string disassembly;
std::string disassembly;
if (is_ppc) {
disassembly = ResourceDASM::PPC32Emulator::disassemble(&r.pget<uint8_t>(0, code.size()), code.size(), 0, &labels);
} else if (is_x86) {
@@ -613,7 +611,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
disassembly = ResourceDASM::SH4Emulator::disassemble(&r.pget<uint8_t>(0, code.size()), code.size(), 0, &labels);
} else {
// We shouldn't have entered the outer if statement if this happens
throw logic_error("unsupported architecture");
throw std::logic_error("unsupported architecture");
}
output_filename = std::format("code.{}.txt", filename_timestamp);
@@ -626,7 +624,7 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
}
c->log.info_f("Wrote disassembly to file {}", output_filename);
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.info_f("Failed to disassemble code from server: {}", e.what());
}
}
@@ -645,10 +643,10 @@ static asio::awaitable<HandlerResult> S_B2(shared_ptr<Client> c, Channel::Messag
}
}
static asio::awaitable<HandlerResult> C_B3(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_B3(std::shared_ptr<Client> c, Channel::Message& msg) {
auto cmd = msg.check_size_t<C_ExecuteCodeResult_B3>();
shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> promise;
std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> promise;
if (!c->function_call_response_queue.empty()) {
promise = std::move(c->function_call_response_queue.front());
c->function_call_response_queue.pop_front();
@@ -662,18 +660,18 @@ static asio::awaitable<HandlerResult> C_B3(shared_ptr<Client> c, Channel::Messag
}
}
static asio::awaitable<HandlerResult> C_B_E0(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> C_B_E0(std::shared_ptr<Client> c, Channel::Message&) {
auto ret = c->proxy_session->bb_client_sent_E0 ? HandlerResult::FORWARD : HandlerResult::SUPPRESS;
c->proxy_session->bb_client_sent_E0 = true;
co_return ret;
}
static asio::awaitable<HandlerResult> S_B_E2(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_E2(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
const auto& cmd = msg.check_size_t<S_SyncSystemFile_BB_E2>();
uint64_t ts = phosg::now();
string system_filename = std::format("system.{}.psosys", ts);
string team_membership_filename = std::format("system.{}.psosysteam", ts);
std::string system_filename = std::format("system.{}.psosys", ts);
std::string team_membership_filename = std::format("system.{}.psosysteam", ts);
phosg::save_object_file(system_filename, cmd.system_file);
phosg::save_object_file(team_membership_filename, cmd.team_membership);
c->log.info_f("Wrote system file to {}", system_filename);
@@ -682,9 +680,9 @@ static asio::awaitable<HandlerResult> S_B_E2(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_B_E7(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_E7(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
string output_filename = std::format("player.{}.psochar", phosg::now());
std::string output_filename = std::format("player.{}.psochar", phosg::now());
auto f = phosg::fopen_unique(output_filename, "wb");
PSOCommandHeaderBB header = {msg.data.size() + sizeof(PSOCommandHeaderBB), msg.command, msg.flag};
phosg::fwritex(f.get(), &header, sizeof(header));
@@ -694,7 +692,7 @@ static asio::awaitable<HandlerResult> S_B_E7(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_B_DC(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_DC(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES) && (msg.command == 0x02DC)) {
const auto& cmd = msg.check_size_t<S_GuildCardFileChunk_02DC>(8, sizeof(S_GuildCardFileChunk_02DC));
size_t chunk_size = msg.data.size() - 8;
@@ -716,11 +714,11 @@ static asio::awaitable<HandlerResult> S_B_DC(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> C_B_DC(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_B_DC(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES) && (msg.command == 0x03DC)) {
const auto& cmd = msg.check_size_t<C_GuildCardDataRequest_BB_03DC>();
if ((cmd.cont == 0) && c->proxy_session->bb_guild_card_data) {
string output_filename = std::format("guildcard.{}.psocard", phosg::now());
std::string output_filename = std::format("guildcard.{}.psocard", phosg::now());
phosg::save_object_file(output_filename, *c->proxy_session->bb_guild_card_data);
c->log.info_f("Wrote Guild Card data to {}", output_filename);
}
@@ -728,7 +726,7 @@ static asio::awaitable<HandlerResult> C_B_DC(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_B_EB(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_EB(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
if (msg.command == 0x01EB) {
const auto* entries = &msg.check_size_t<S_StreamFileIndexEntry_BB_01EB>(
@@ -758,7 +756,7 @@ static asio::awaitable<HandlerResult> S_B_EB(shared_ptr<Client> c, Channel::Mess
memcpy(c->proxy_session->bb_stream_file_data.data() + chunk_offset, cmd.data.data(), chunk_size);
c->proxy_session->bb_stream_file_data_received += chunk_size;
if (c->proxy_session->bb_stream_file_data_received == c->proxy_session->bb_stream_file_data.size()) {
string output_prefix = std::format("streamfile.{}.", phosg::now());
std::string output_prefix = std::format("streamfile.{}.", phosg::now());
for (const auto& entry : c->proxy_session->bb_stream_file_entries) {
std::string filename = entry.filename.decode();
std::string sanitized_filename = filename;
@@ -785,7 +783,7 @@ static asio::awaitable<HandlerResult> S_B_EB(shared_ptr<Client> c, Channel::Mess
}
template <typename CmdT>
static asio::awaitable<HandlerResult> S_C4(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_C4(std::shared_ptr<Client> c, Channel::Message& msg) {
bool modified = false;
if (c->login && c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
size_t expected_size = sizeof(CmdT) * msg.flag;
@@ -805,7 +803,7 @@ constexpr MessageHandler S_DGX_C4 = &S_C4<S_ChoiceSearchResultEntry_DC_V3_C4>;
constexpr MessageHandler S_P_C4 = &S_C4<S_ChoiceSearchResultEntry_PC_C4>;
constexpr MessageHandler S_B_C4 = &S_C4<S_ChoiceSearchResultEntry_BB_C4>;
static asio::awaitable<HandlerResult> S_G_E4(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_G_E4(std::shared_ptr<Client> c, Channel::Message& msg) {
auto& cmd = msg.check_size_t<S_CardBattleTableState_Ep3_E4>();
bool modified = false;
if (c->login && c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
@@ -819,7 +817,7 @@ static asio::awaitable<HandlerResult> S_G_E4(shared_ptr<Client> c, Channel::Mess
co_return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_B_22(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_B_22(std::shared_ptr<Client> c, Channel::Message& msg) {
// We use this command (which is sent before the init encryption command) to detect a particular server behavior that
// we'll have to work around later. It looks like this command's existence is an anti-proxy measure, since this
// command is 0x34 bytes in total, and the logic that adds padding bytes when the command size isn't a multiple of 8
@@ -833,7 +831,7 @@ static asio::awaitable<HandlerResult> S_B_22(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_19_U_14(std::shared_ptr<Client> c, Channel::Message& msg) {
// If the command is shorter than 6 bytes, use the previous server command to fill it in. This simulates a behavior
// used by some private servers where a longer previous command is used to fill part of the client's receive buffer
// with meaningful data, then an intentionally undersize 19 command is sent which results in the client using the
@@ -869,9 +867,9 @@ static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::M
}
// Replace the server channel with a new channel to the new endpoint
string netloc_str = str_for_endpoint(new_ep);
std::string netloc_str = str_for_endpoint(new_ep);
c->log.info_f("Connecting to {}", netloc_str);
auto sock = make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(new_ep));
auto sock = std::make_unique<asio::ip::tcp::socket>(co_await async_connect_tcp(new_ep));
// Close the old channel only after replacing it with the new one
auto s = c->require_server_state();
@@ -895,7 +893,7 @@ static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::M
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_V3_1A_D5(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> S_V3_1A_D5(std::shared_ptr<Client> c, Channel::Message&) {
// If the client is a version that sends close confirmations and the client has the no-close-confirmation flag set in
// its newserv client config, send a fake confirmation to the remote server immediately.
if (is_v3(c->version()) && c->check_flag(Client::Flag::NO_D6)) {
@@ -904,7 +902,7 @@ static asio::awaitable<HandlerResult> S_V3_1A_D5(shared_ptr<Client> c, Channel::
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_V3_BB_DA(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_V3_BB_DA(std::shared_ptr<Client> c, Channel::Message& msg) {
// This command is supported on all V3 and V4 versions except Ep1&2 Trial
if (c->version() == Version::GC_NTE) {
co_return HandlerResult::SUPPRESS;
@@ -916,7 +914,7 @@ static asio::awaitable<HandlerResult> S_V3_BB_DA(shared_ptr<Client> c, Channel::
}
}
static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> SC_6x60_6xA2(std::shared_ptr<Client> c, Channel::Message& msg) {
if (!c->proxy_session->is_in_game) {
co_return HandlerResult::FORWARD;
}
@@ -944,7 +942,7 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
case ProxyDropMode::INTERCEPT:
break;
default:
throw logic_error("invalid drop mode");
throw std::logic_error("invalid drop mode");
}
if (!c->proxy_session->item_creator) {
@@ -982,7 +980,7 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
c->log.info_f("No item was created");
} else {
auto s = c->require_server_state();
string name = s->describe_item(c->version(), res.item);
std::string name = s->describe_item(c->version(), res.item);
c->log.info_f("Entity {:04X} (area {:02X}) created item {}", cmd.entity_index, cmd.effective_area, name);
res.item.id = c->proxy_session->next_item_id++;
c->log.info_f("Creating item {:08X} at {:02X}:{:g},{:g} for all clients",
@@ -994,7 +992,7 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_6x(std::shared_ptr<Client> c, Channel::Message& msg) {
auto s = c->require_server_state();
if (msg.data.size() < 4) {
@@ -1044,7 +1042,7 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
case 0x46: {
const auto& header = msg.check_size_t<G_AttackFinished_Header_6x46>(0xFFFF);
if (header.target_count > min<size_t>(header.header.size - sizeof(G_AttackFinished_Header_6x46) / 4, 10)) {
if (header.target_count > std::min<size_t>(header.header.size - sizeof(G_AttackFinished_Header_6x46) / 4, 10)) {
c->log.warning_f("Blocking subcommand 6x46 with invalid count");
co_return HandlerResult::SUPPRESS;
}
@@ -1053,7 +1051,7 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
case 0x47: {
const auto& header = msg.check_size_t<G_CastTechnique_Header_6x47>(0xFFFF);
if (header.target_count > min<size_t>(header.header.size - sizeof(G_CastTechnique_Header_6x47) / 4, 10)) {
if (header.target_count > std::min<size_t>(header.header.size - sizeof(G_CastTechnique_Header_6x47) / 4, 10)) {
c->log.warning_f("Blocking subcommand 6x47 with invalid count");
co_return HandlerResult::SUPPRESS;
}
@@ -1062,7 +1060,7 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
case 0x49: {
const auto& header = msg.check_size_t<G_ExecutePhotonBlast_Header_6x49>(0xFFFF);
if (header.target_count > min<size_t>(header.header.size - sizeof(G_ExecutePhotonBlast_Header_6x49) / 4, 10)) {
if (header.target_count > std::min<size_t>(header.header.size - sizeof(G_ExecutePhotonBlast_Header_6x49) / 4, 10)) {
c->log.warning_f("Blocking subcommand 6x49 with invalid count");
co_return HandlerResult::SUPPRESS;
}
@@ -1084,10 +1082,10 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
case 0x6A: {
auto& cmd = msg.check_size_t<G_SetBossWarpFlags_6x6A>();
if (c->proxy_session->map_state) {
shared_ptr<MapState::ObjectState> obj_st;
std::shared_ptr<MapState::ObjectState> obj_st;
try {
obj_st = c->proxy_session->map_state->object_state_for_index(c->version(), cmd.header.entity_id - 0x4000);
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.warning_f("Invalid object reference ({})", e.what());
}
@@ -1203,8 +1201,8 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
const auto& header = msg.check_size_t<G_MapSubsubcommand_Ep3_6xB6>(0xFFFF);
if (header.subsubcommand == 0x00000041) {
const auto& cmd = msg.check_size_t<G_MapData_Ep3_6xB6x41>(0xFFFF);
string filename = std::format("map{:08X}.{}.mnmd", cmd.map_number, phosg::now());
string map_data = prs_decompress(msg.data.data() + sizeof(cmd), msg.data.size() - sizeof(cmd));
std::string filename = std::format("map{:08X}.{}.mnmd", cmd.map_number, phosg::now());
std::string map_data = prs_decompress(msg.data.data() + sizeof(cmd), msg.data.size() - sizeof(cmd));
phosg::save_file(filename, map_data);
if ((map_data.size() != sizeof(Episode3::MapDefinition)) &&
(map_data.size() != sizeof(Episode3::MapDefinitionTrial))) {
@@ -1245,7 +1243,7 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
co_return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> C_GXB_61(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_GXB_61(std::shared_ptr<Client> c, Channel::Message& msg) {
bool modified = false;
// TODO: We should check if the info board text was actually modified and return MODIFIED if so.
@@ -1284,7 +1282,7 @@ static asio::awaitable<HandlerResult> C_GXB_61(shared_ptr<Client> c, Channel::Me
co_return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> C_GX_D9(shared_ptr<Client>, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_GX_D9(std::shared_ptr<Client>, Channel::Message& msg) {
phosg::strip_trailing_zeroes(msg.data);
msg.data = add_color(msg.data);
msg.data.push_back(0);
@@ -1295,19 +1293,19 @@ static asio::awaitable<HandlerResult> C_GX_D9(shared_ptr<Client>, Channel::Messa
co_return HandlerResult::MODIFIED;
}
static asio::awaitable<HandlerResult> C_B_D9(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_B_D9(std::shared_ptr<Client> c, Channel::Message& msg) {
try {
phosg::strip_trailing_zeroes(msg.data);
if (msg.data.size() & 1) {
msg.data.push_back(0);
}
string decoded = tt_utf16_to_utf8(msg.data.data(), msg.data.size());
std::string decoded = tt_utf16_to_utf8(msg.data.data(), msg.data.size());
add_color_inplace(decoded);
msg.data = tt_utf8_to_utf16(decoded.data(), decoded.size());
while (msg.data.size() & 3) {
msg.data.push_back(0);
}
} catch (const runtime_error& e) {
} catch (const std::runtime_error& e) {
c->log.warning_f("Failed to decode and unescape D9 command: {}", e.what());
}
// TODO: We should check if the info board text was actually modified and return HandlerResult::FORWARD if not.
@@ -1315,16 +1313,16 @@ static asio::awaitable<HandlerResult> C_B_D9(shared_ptr<Client> c, Channel::Mess
}
template <typename T>
static asio::awaitable<HandlerResult> S_44_A6(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_44_A6(std::shared_ptr<Client> c, Channel::Message& msg) {
const auto& cmd = msg.check_size_t<T>();
string filename = cmd.filename.decode();
string output_filename;
std::string filename = cmd.filename.decode();
std::string output_filename;
bool is_download = (msg.command == 0xA6);
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
size_t extension_offset = filename.rfind('.');
string basename, extension;
if (extension_offset != string::npos) {
std::string basename, extension;
if (extension_offset != std::string::npos) {
basename = filename.substr(0, extension_offset);
extension = filename.substr(extension_offset);
if (extension == ".bin" && is_ep3(c->version())) {
@@ -1368,15 +1366,15 @@ constexpr MessageHandler S_PG_44_A6 = &S_44_A6<S_OpenFile_PC_GC_44_A6>;
constexpr MessageHandler S_X_44_A6 = &S_44_A6<S_OpenFile_XB_44_A6>;
constexpr MessageHandler S_B_44_A6 = &S_44_A6<S_OpenFile_BB_44_A6>;
static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_13_A7(std::shared_ptr<Client> c, Channel::Message& msg) {
auto& cmd = msg.check_size_t<S_WriteFile_13_A7>();
bool modified = false;
ProxySession::SavingFile* sf = nullptr;
try {
sf = &c->proxy_session->saving_files.at(cmd.filename.decode());
} catch (const out_of_range&) {
string filename = cmd.filename.decode();
} catch (const std::out_of_range&) {
std::string filename = cmd.filename.decode();
c->log.warning_f("Received data for non-open file {}", filename);
}
if (!sf) {
@@ -1386,7 +1384,7 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
bool is_last_block = (cmd.data_size != 0x400);
size_t block_offset = msg.flag * 0x400;
size_t allowed_block_size = (block_offset < sf->total_size)
? min<size_t>(sf->total_size - block_offset, 0x400)
? std::min<size_t>(sf->total_size - block_offset, 0x400)
: 0;
if (cmd.data_size > allowed_block_size) {
@@ -1421,9 +1419,9 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
if (!sf->is_download) {
if (sf->basename.ends_with(".bin")) {
c->proxy_session->last_bin_contents = make_shared<std::string>(prs_decompress(sf->data));
c->proxy_session->last_bin_contents = std::make_shared<std::string>(prs_decompress(sf->data));
} else if (sf->basename.ends_with(".dat")) {
c->proxy_session->last_dat_contents = make_shared<std::string>(prs_decompress(sf->data));
c->proxy_session->last_dat_contents = std::make_shared<std::string>(prs_decompress(sf->data));
}
if (c->proxy_session->last_bin_contents && c->proxy_session->last_dat_contents) {
@@ -1436,23 +1434,23 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
c->version(),
c->language());
auto map_file = make_shared<MapFile>(c->proxy_session->last_dat_contents);
auto map_file = std::make_shared<MapFile>(c->proxy_session->last_dat_contents);
auto materialized_map_file = map_file->materialize_random_sections(c->proxy_session->lobby_random_seed);
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
std::array<std::shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
map_files.at(static_cast<size_t>(c->version())) = materialized_map_file;
auto supermap = make_shared<SuperMap>(map_files, meta.get_floor_to_area());
auto supermap = std::make_shared<SuperMap>(map_files, meta.get_floor_to_area());
c->proxy_session->map_state = make_shared<MapState>(
c->proxy_session->map_state = std::make_shared<MapState>(
c->id,
c->proxy_session->lobby_difficulty,
c->proxy_session->lobby_event,
c->proxy_session->lobby_random_seed,
MapState::DEFAULT_RARE_ENEMIES,
make_shared<MT19937Generator>(c->proxy_session->lobby_random_seed),
std::make_shared<MT19937Generator>(c->proxy_session->lobby_random_seed),
supermap);
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.warning_f("Failed to load quest map: {}", e.what());
c->proxy_session->map_state.reset();
}
@@ -1468,7 +1466,7 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
co_return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_G_B7(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_G_B7(std::shared_ptr<Client> c, Channel::Message& msg) {
if (is_ep3(c->version())) {
if (c->check_flag(Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED)) {
auto& cmd = msg.check_size_t<S_RankUpdate_Ep3_B7>();
@@ -1484,7 +1482,7 @@ static asio::awaitable<HandlerResult> S_G_B7(shared_ptr<Client> c, Channel::Mess
}
}
static asio::awaitable<HandlerResult> S_G_B8(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_G_B8(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
if (msg.data.size() < 4) {
c->log.warning_f("Card list data size is too small; not saving file");
@@ -1498,7 +1496,7 @@ static asio::awaitable<HandlerResult> S_G_B8(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
string output_filename = std::format("card-definitions.{}.mnr", phosg::now());
std::string output_filename = std::format("card-definitions.{}.mnr", phosg::now());
phosg::save_file(output_filename, r.read(size));
c->log.info_f("Wrote {} bytes to {}", size, output_filename);
}
@@ -1510,19 +1508,19 @@ static asio::awaitable<HandlerResult> S_G_B8(shared_ptr<Client> c, Channel::Mess
co_return is_ep3(c->version()) ? HandlerResult::FORWARD : HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_G_B9(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_G_B9(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_SAVE_FILES)) {
try {
const auto& header = msg.check_size_t<S_UpdateMediaHeader_Ep3_B9>(0xFFFF);
if (msg.data.size() - sizeof(header) < header.size) {
throw runtime_error("Media data size extends beyond end of command; not saving file");
throw std::runtime_error("Media data size extends beyond end of command; not saving file");
}
string decompressed_data = prs_decompress(
std::string decompressed_data = prs_decompress(
msg.data.data() + sizeof(header), msg.data.size() - sizeof(header));
string output_filename = std::format("media-update.{}", phosg::now());
std::string output_filename = std::format("media-update.{}", phosg::now());
if (header.type == 1) {
output_filename += ".gvm";
} else if (header.type == 2 || header.type == 3) {
@@ -1532,7 +1530,7 @@ static asio::awaitable<HandlerResult> S_G_B9(shared_ptr<Client> c, Channel::Mess
}
phosg::save_file(output_filename, decompressed_data);
c->log.info_f("Wrote {} bytes to {}", decompressed_data.size(), output_filename);
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.warning_f("Failed to save file: {}", e.what());
}
}
@@ -1541,7 +1539,7 @@ static asio::awaitable<HandlerResult> S_G_B9(shared_ptr<Client> c, Channel::Mess
co_return (c->version() == Version::GC_EP3) ? HandlerResult::FORWARD : HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> C_G_B9(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> C_G_B9(std::shared_ptr<Client> c, Channel::Message&) {
if (c->proxy_session->suppress_next_ep3_media_update_confirmation) {
c->proxy_session->suppress_next_ep3_media_update_confirmation = false;
co_return HandlerResult::SUPPRESS;
@@ -1549,7 +1547,7 @@ static asio::awaitable<HandlerResult> C_G_B9(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_G_EF(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_G_EF(std::shared_ptr<Client> c, Channel::Message& msg) {
if (is_ep3(c->version())) {
if (c->check_flag(Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED)) {
auto& cmd = msg.check_size_t<S_StartCardAuction_Ep3_EF>(offsetof(S_StartCardAuction_Ep3_EF, unused), 0xFFFF);
@@ -1564,12 +1562,12 @@ static asio::awaitable<HandlerResult> S_G_EF(shared_ptr<Client> c, Channel::Mess
}
}
static asio::awaitable<HandlerResult> S_B_EF(shared_ptr<Client>, Channel::Message&) {
static asio::awaitable<HandlerResult> S_B_EF(std::shared_ptr<Client>, Channel::Message&) {
// See the comments on EF in CommandFormats.hh for why we unconditionally suppress these.
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> S_G_BA(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_G_BA(std::shared_ptr<Client> c, Channel::Message& msg) {
if (c->check_flag(Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED)) {
auto& cmd = msg.check_size_t<S_MesetaTransaction_Ep3_BA>();
if (cmd.current_meseta != 1000000) {
@@ -1580,7 +1578,7 @@ static asio::awaitable<HandlerResult> S_G_BA(shared_ptr<Client> c, Channel::Mess
co_return HandlerResult::FORWARD;
}
static void update_leader_id(shared_ptr<Client> c, uint8_t leader_id) {
static void update_leader_id(std::shared_ptr<Client> c, uint8_t leader_id) {
if (c->proxy_session->leader_client_id != leader_id) {
c->proxy_session->leader_client_id = leader_id;
c->log.info_f("Changed room leader to {:X}", c->proxy_session->leader_client_id);
@@ -1592,7 +1590,7 @@ static void update_leader_id(shared_ptr<Client> c, uint8_t leader_id) {
}
template <typename CmdT>
static asio::awaitable<HandlerResult> S_65_67_68_EB(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_65_67_68_EB(std::shared_ptr<Client> c, Channel::Message& msg) {
if (msg.command == 0x67) {
c->proxy_session->clear_lobby_players(12);
c->proxy_session->is_in_lobby = true;
@@ -1625,7 +1623,7 @@ static asio::awaitable<HandlerResult> S_65_67_68_EB(shared_ptr<Client> c, Channe
if (index >= c->proxy_session->lobby_players.size()) {
c->log.warning_f("Ignoring invalid player index {} at position {}", index, x);
} else {
string name = escape_player_name(entry.disp.visual.name.decode(entry.inventory.language));
std::string name = escape_player_name(entry.disp.visual.name.decode(entry.inventory.language));
if (c->login && (entry.lobby_data.guild_card_number == c->proxy_session->remote_guild_card_number)) {
num_replacements++;
if (c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
@@ -1714,7 +1712,7 @@ Episode get_episode<S_JoinGame_Ep3_64>(const S_JoinGame_Ep3_64&) {
}
template <typename CmdT>
static asio::awaitable<HandlerResult> S_64(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_64(std::shared_ptr<Client> c, Channel::Message& msg) {
CmdT* cmd;
S_JoinGame_Ep3_64* cmd_ep3 = nullptr;
if ((c->sub_version >= 0x40) && is_v3(c->version())) {
@@ -1789,13 +1787,13 @@ static asio::awaitable<HandlerResult> S_64(shared_ptr<Client> c, Channel::Messag
c->proxy_session->lobby_mode,
c->proxy_session->lobby_difficulty,
cmd->variations);
c->proxy_session->map_state = make_shared<MapState>(
c->proxy_session->map_state = std::make_shared<MapState>(
c->id,
c->proxy_session->lobby_difficulty,
c->proxy_session->lobby_event,
c->proxy_session->lobby_random_seed,
MapState::DEFAULT_RARE_ENEMIES,
make_shared<MT19937Generator>(c->proxy_session->lobby_random_seed),
std::make_shared<MT19937Generator>(c->proxy_session->lobby_random_seed),
supermaps);
} else {
c->proxy_session->map_state.reset();
@@ -1834,7 +1832,7 @@ constexpr MessageHandler S_G_64 = &S_64<S_JoinGame_GC_64>;
constexpr MessageHandler S_X_64 = &S_64<S_JoinGame_XB_64>;
constexpr MessageHandler S_B_64 = &S_64<S_JoinGame_BB_64>;
static asio::awaitable<HandlerResult> S_E8(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_E8(std::shared_ptr<Client> c, Channel::Message& msg) {
auto& cmd = msg.check_size_t<S_JoinSpectatorTeam_Ep3_E8>();
c->floor = 0;
@@ -1896,7 +1894,7 @@ static asio::awaitable<HandlerResult> S_E8(shared_ptr<Client> c, Channel::Messag
co_return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> S_AC(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> S_AC(std::shared_ptr<Client> c, Channel::Message&) {
if (!c->proxy_session->is_in_game) {
co_return HandlerResult::SUPPRESS;
} else {
@@ -1905,7 +1903,7 @@ static asio::awaitable<HandlerResult> S_AC(shared_ptr<Client> c, Channel::Messag
}
}
static asio::awaitable<HandlerResult> S_66_69_E9(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> S_66_69_E9(std::shared_ptr<Client> c, Channel::Message& msg) {
// Schtserv sends a large command here for unknown reasons. The client ignores the extra data, so we allow the large
// command here.
const auto& cmd = msg.check_size_t<S_LeaveLobby_66_69_Ep3_E9>(0xFFFF);
@@ -1914,7 +1912,7 @@ static asio::awaitable<HandlerResult> S_66_69_E9(shared_ptr<Client> c, Channel::
c->log.warning_f("Lobby leave command references missing position");
} else {
auto& p = c->proxy_session->lobby_players[index];
string name = escape_player_name(p.name);
std::string name = escape_player_name(p.name);
if (c->check_flag(Client::Flag::PROXY_PLAYER_NOTIFICATIONS_ENABLED)) {
send_text_message_fmt(c->channel, "$C4Leave: {}/{}\n{}", index, p.guild_card_number, name);
}
@@ -1926,7 +1924,7 @@ static asio::awaitable<HandlerResult> S_66_69_E9(shared_ptr<Client> c, Channel::
co_return HandlerResult::FORWARD;
}
static asio::awaitable<HandlerResult> C_98(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_98(std::shared_ptr<Client> c, Channel::Message& msg) {
c->floor = 0x0F;
c->proxy_session->is_in_lobby = false;
c->proxy_session->is_in_game = false;
@@ -1948,11 +1946,11 @@ static asio::awaitable<HandlerResult> C_98(shared_ptr<Client> c, Channel::Messag
}
}
static asio::awaitable<HandlerResult> C_06(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_06(std::shared_ptr<Client> c, Channel::Message& msg) {
if (msg.data.size() >= 0x0C) {
const auto& cmd = msg.check_size_t<SC_TextHeader_01_06_11_B0_EE>(0xFFFF);
string text = msg.data.substr(sizeof(cmd));
std::string text = msg.data.substr(sizeof(cmd));
phosg::strip_trailing_zeroes(text);
uint8_t private_flags = 0;
@@ -1968,7 +1966,7 @@ static asio::awaitable<HandlerResult> C_06(shared_ptr<Client> c, Channel::Messag
} else {
text = tt_decode_marked(text, c->language(), false);
}
} catch (const runtime_error& e) {
} catch (const std::runtime_error& e) {
c->log.warning_f("Failed to decode and unescape chat text: {}", e.what());
text.clear();
}
@@ -2002,7 +2000,7 @@ static asio::awaitable<HandlerResult> C_06(shared_ptr<Client> c, Channel::Messag
}
}
static asio::awaitable<HandlerResult> C_40(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_40(std::shared_ptr<Client> c, Channel::Message& msg) {
bool modified = false;
if (c->login && c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
auto& cmd = msg.check_size_t<C_GuildCardSearch_40>();
@@ -2019,7 +2017,7 @@ static asio::awaitable<HandlerResult> C_40(shared_ptr<Client> c, Channel::Messag
}
template <typename CmdT>
static asio::awaitable<HandlerResult> C_81(shared_ptr<Client> c, Channel::Message& msg) {
static asio::awaitable<HandlerResult> C_81(std::shared_ptr<Client> c, Channel::Message& msg) {
auto& cmd = msg.check_size_t<CmdT>();
if (c->login && c->login->account->account_id != c->proxy_session->remote_guild_card_number) {
if (cmd.from_guild_card_number == c->login->account->account_id) {
@@ -2039,7 +2037,7 @@ constexpr MessageHandler C_P_81 = &C_81<SC_SimpleMail_PC_81>;
constexpr MessageHandler C_B_81 = &C_81<SC_SimpleMail_BB_81>;
template <typename SendGuildCardCmdT>
asio::awaitable<HandlerResult> C_6x(shared_ptr<Client> c, Channel::Message& msg) {
asio::awaitable<HandlerResult> C_6x(std::shared_ptr<Client> c, Channel::Message& msg) {
if (msg.data.size() < 4) {
co_return HandlerResult::FORWARD;
}
@@ -2214,7 +2212,7 @@ constexpr MessageHandler C_G_6x = &C_6x<G_SendGuildCard_GC_6x06>;
constexpr MessageHandler C_X_6x = &C_6x<G_SendGuildCard_XB_6x06>;
constexpr MessageHandler C_B_6x = &C_6x<G_SendGuildCard_BB_6x06>;
static asio::awaitable<HandlerResult> C_V123_A0(shared_ptr<Client> c, Channel::Message&) {
static asio::awaitable<HandlerResult> C_V123_A0(std::shared_ptr<Client> c, Channel::Message&) {
// A0 is sent after downloading a quest (either successfully, or by backing out of the menu), and when choosing
// Change Ship from the lobby counter menu. We override the Change Ship action to end the proxy session, but we only
// do so if the player is in a lobby in order to properly handle the download quest case.
@@ -2785,13 +2783,14 @@ static MessageHandler get_handler(Version version, bool from_server, uint8_t com
const auto& handlers = from_server ? server_handlers : client_handlers;
size_t version_index = static_cast<size_t>(version);
if (version_index >= handlers[command].size()) {
throw logic_error("invalid game version on proxy server");
throw std::logic_error("invalid game version on proxy server");
}
auto ret = handlers[command][version_index];
return ret ? ret : default_handler;
}
asio::awaitable<void> on_proxy_command(shared_ptr<Client> c, bool from_server, unique_ptr<Channel::Message> msg) {
asio::awaitable<void> on_proxy_command(
std::shared_ptr<Client> c, bool from_server, std::unique_ptr<Channel::Message> msg) {
auto fn = get_handler(c->version(), from_server, msg->command & 0xFF);
try {
auto res = co_await fn(c, *msg);
@@ -2803,9 +2802,9 @@ asio::awaitable<void> on_proxy_command(shared_ptr<Client> c, bool from_server, u
} else if (res == HandlerResult::SUPPRESS) {
c->log.info_f("The preceding command from the {} was not forwarded", from_server ? "server" : "client");
} else {
throw logic_error("invalid handler result");
throw std::logic_error("invalid handler result");
}
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.error_f("Error in proxy command handler: {}", e.what());
if (c->proxy_session && c->proxy_session->server_channel) {
c->proxy_session->server_channel->disconnect();
@@ -2814,13 +2813,13 @@ asio::awaitable<void> on_proxy_command(shared_ptr<Client> c, bool from_server, u
}
asio::awaitable<void> handle_proxy_server_commands(
shared_ptr<Client> c, shared_ptr<ProxySession> ses, shared_ptr<Channel> channel) {
std::shared_ptr<Client> c, std::shared_ptr<ProxySession> ses, std::shared_ptr<Channel> channel) {
std::string error_str;
// server_channel can be changed by receiving a 19 command, hence the exception handler is inside the loop here
while ((c->proxy_session == ses) && (ses->server_channel == channel) && channel->connected()) {
unique_ptr<Channel::Message> msg;
std::unique_ptr<Channel::Message> msg;
try {
msg = make_unique<Channel::Message>(co_await channel->recv());
msg = std::make_unique<Channel::Message>(co_await channel->recv());
if (c->proxy_session == ses) {
for (size_t z = 0; z < std::min<size_t>(c->proxy_session->prev_server_command_bytes.size(), msg->data.size()); z++) {
c->proxy_session->prev_server_command_bytes[z] = msg->data[z];
@@ -2838,7 +2837,7 @@ asio::awaitable<void> handle_proxy_server_commands(
error_str = e.what();
}
channel->disconnect();
} catch (const exception& e) {
} catch (const std::exception& e) {
c->log.info_f("Error in proxy server channel handler (command {:04X}): {}", msg ? msg->command : 0, e.what());
error_str = e.what();
channel->disconnect();
+4 -6
View File
@@ -2,11 +2,9 @@
#include "ServerState.hh"
using namespace std;
size_t ProxySession::num_proxy_sessions = 0;
ProxySession::ProxySession(shared_ptr<Channel> server_channel, const PersistentConfig* pc)
ProxySession::ProxySession(std::shared_ptr<Channel> server_channel, const PersistentConfig* pc)
: server_channel(server_channel) {
if (pc) {
this->remote_guild_card_number = pc->remote_guild_card_number;
@@ -22,11 +20,11 @@ ProxySession::~ProxySession() {
}
void ProxySession::set_drop_mode(
shared_ptr<ServerState> s, Version version, int64_t override_random_seed, ProxyDropMode new_mode) {
std::shared_ptr<ServerState> s, Version version, int64_t override_random_seed, ProxyDropMode new_mode) {
this->drop_mode = new_mode;
if (this->drop_mode == ProxyDropMode::INTERCEPT) {
auto rand_crypt = make_shared<MT19937Generator>((override_random_seed >= 0) ? override_random_seed : this->lobby_random_seed);
this->item_creator = make_shared<ItemCreator>(
auto rand_crypt = std::make_shared<MT19937Generator>((override_random_seed >= 0) ? override_random_seed : this->lobby_random_seed);
this->item_creator = std::make_shared<ItemCreator>(
s->common_item_set(version, nullptr),
s->rare_item_set(version, nullptr),
s->armor_random_set,
+185 -184
View File
@@ -20,8 +20,6 @@
#include "SaveFileFormats.hh"
#include "Text.hh"
using namespace std;
QuestCategoryIndex::Category::Category(uint32_t category_id, const phosg::JSON& json) : category_id(category_id) {
this->enabled_flags = json.get_int(0);
this->directory_name = json.get_string(1);
@@ -32,11 +30,11 @@ QuestCategoryIndex::Category::Category(uint32_t category_id, const phosg::JSON&
QuestCategoryIndex::QuestCategoryIndex(const phosg::JSON& json) {
uint32_t next_category_id = 1;
for (const auto& it : json.as_list()) {
this->categories.emplace_back(make_shared<Category>(next_category_id++, *it));
this->categories.emplace_back(std::make_shared<Category>(next_category_id++, *it));
}
}
shared_ptr<const QuestCategoryIndex::Category> QuestCategoryIndex::at(uint32_t category_id) const {
std::shared_ptr<const QuestCategoryIndex::Category> QuestCategoryIndex::at(uint32_t category_id) const {
return this->categories.at(category_id - 1);
}
@@ -56,9 +54,9 @@ using PSOVMSDLQFileEncryptedHeader = PSOMemCardDLQFileEncryptedHeaderT<false>;
using PSOGCIDLQFileEncryptedHeader = PSOMemCardDLQFileEncryptedHeaderT<true>;
template <bool BE>
string decrypt_download_quest_data_section(
std::string decrypt_download_quest_data_section(
const void* data_section, size_t size, uint32_t seed, bool skip_checksum = false, bool is_ep3_trial = false) {
string decrypted = decrypt_data_section<BE>(data_section, size, seed);
std::string decrypted = decrypt_data_section<BE>(data_section, size, seed);
size_t orig_size = decrypted.size();
decrypted.resize((decrypted.size() + 3) & (~3));
@@ -74,7 +72,7 @@ string decrypt_download_quest_data_section(
phosg::StringReader r(decrypted);
r.skip(16);
if (r.readx(15) != "SONICTEAM,SEGA.") {
throw runtime_error("Episode 3 GCI file is not a quest");
throw std::runtime_error("Episode 3 GCI file is not a quest");
}
r.skip(9);
@@ -84,7 +82,7 @@ string decrypt_download_quest_data_section(
size_t decompressed_size = prs_decompress_size(
r.getv(r.remaining(), false), r.remaining(), sizeof(Episode3::MapDefinitionTrial), true);
if (decompressed_size < sizeof(Episode3::MapDefinitionTrial)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"decompressed size ({}) does not match expected size ({})",
decompressed_size, sizeof(Episode3::MapDefinitionTrial)));
}
@@ -92,7 +90,7 @@ string decrypt_download_quest_data_section(
} else {
if (header->decompressed_size & 0xFFF00000) {
throw runtime_error(std::format("decompressed_size too large ({:08X})", header->decompressed_size));
throw std::runtime_error(std::format("decompressed_size too large ({:08X})", header->decompressed_size));
}
if (!skip_checksum) {
@@ -101,7 +99,7 @@ string decrypt_download_quest_data_section(
uint32_t actual_crc = phosg::crc32(decrypted.data(), orig_size);
header->checksum = expected_crc;
if (expected_crc != actual_crc && expected_crc != phosg::bswap32(actual_crc)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"incorrect decrypted data section checksum: expected {:08X}; received {:08X}", expected_crc, actual_crc));
}
}
@@ -117,7 +115,7 @@ string decrypt_download_quest_data_section(
decrypted.data() + sizeof(HeaderT), decrypted.size() - sizeof(HeaderT));
size_t expected_decompressed_size = header->decompressed_size;
if ((decompressed_size != expected_decompressed_size) && (decompressed_size != expected_decompressed_size - 8)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"decompressed size ({}) does not match expected size ({})", decompressed_size, expected_decompressed_size));
}
@@ -125,12 +123,12 @@ string decrypt_download_quest_data_section(
}
}
string decrypt_vms_v1_data_section(const void* data_section, size_t size) {
std::string decrypt_vms_v1_data_section(const void* data_section, size_t size) {
phosg::StringReader r(data_section, size);
uint32_t expected_decompressed_size = r.get_u32l();
uint32_t seed = r.get_u32l();
string data = r.read(r.remaining());
std::string data = r.read(r.remaining());
size_t orig_size = data.size();
data.resize((orig_size + 3) & (~3));
@@ -139,7 +137,7 @@ string decrypt_vms_v1_data_section(const void* data_section, size_t size) {
size_t actual_decompressed_size = prs_decompress_size(data);
if (actual_decompressed_size != expected_decompressed_size) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"decompressed size ({}) does not match size in header ({})",
actual_decompressed_size, expected_decompressed_size));
}
@@ -148,17 +146,17 @@ string decrypt_vms_v1_data_section(const void* data_section, size_t size) {
}
template <bool BE>
string find_seed_and_decrypt_download_quest_data_section(
std::string find_seed_and_decrypt_download_quest_data_section(
const void* data_section, size_t size, bool skip_checksum, bool is_ep3_trial, size_t num_threads) {
mutex result_lock;
string result;
std::mutex result_lock;
std::string result;
uint64_t result_seed = phosg::parallel_blocks<uint64_t>([&](uint64_t seed, size_t) {
try {
string ret = decrypt_download_quest_data_section<BE>(data_section, size, seed, skip_checksum, is_ep3_trial);
lock_guard<mutex> g(result_lock);
std::string ret = decrypt_download_quest_data_section<BE>(data_section, size, seed, skip_checksum, is_ep3_trial);
std::lock_guard g(result_lock);
result = std::move(ret);
return true;
} catch (const runtime_error& e) {
} catch (const std::runtime_error& e) {
return false;
}
},
@@ -168,7 +166,7 @@ string find_seed_and_decrypt_download_quest_data_section(
static_game_data_log.info_f("Found seed {:08X}", result_seed);
return result;
} else {
throw runtime_error("no seed found");
throw std::runtime_error("no seed found");
}
}
@@ -179,16 +177,16 @@ struct PSODownloadQuestHeader {
void VersionedQuest::assert_valid() const {
if (this->meta.category_id == 0xFFFFFFFF) {
throw runtime_error("category ID is not set");
throw std::runtime_error("category ID is not set");
}
if (this->meta.quest_number == 0xFFFFFFFF) {
throw runtime_error("quest number is not set");
throw std::runtime_error("quest number is not set");
}
if (this->meta.version == Version::UNKNOWN) {
throw runtime_error("version is not set");
throw std::runtime_error("version is not set");
}
if (this->meta.language == Language::UNKNOWN) {
throw runtime_error("language is not set");
throw std::runtime_error("language is not set");
}
uint8_t num_floors = is_v1(this->meta.version) ? 0x10 : 0x12;
@@ -209,10 +207,10 @@ void VersionedQuest::assert_valid() const {
for (size_t floor = 0; floor < num_floors; floor++) {
const auto& fa = this->meta.floor_assignments[floor];
if (fa.floor != floor) {
throw logic_error("floor assignment is inconsistent");
throw std::logic_error("floor assignment is inconsistent");
}
if ((fa.area != 0xFF) && (fa.area > num_areas)) {
throw runtime_error(std::format("floor assignment 0x{:02X} specifies invalid area 0x{:02X}", floor, fa.area));
throw std::runtime_error(std::format("floor assignment 0x{:02X} specifies invalid area 0x{:02X}", floor, fa.area));
}
}
@@ -221,70 +219,70 @@ void VersionedQuest::assert_valid() const {
break;
case Episode::EP2:
if (is_v1_or_v2(this->meta.version)) {
throw runtime_error("v1 or v2 quest specifies Episode 2");
throw std::runtime_error("v1 or v2 quest specifies Episode 2");
}
break;
case Episode::EP3:
if (!is_ep3(this->meta.version)) {
throw runtime_error("non-Ep3 quest specifies Episode 3");
throw std::runtime_error("non-Ep3 quest specifies Episode 3");
}
break;
case Episode::EP4:
if (!is_v4(this->meta.version)) {
throw runtime_error("non-v4 quest specifies Episode 4");
throw std::runtime_error("non-v4 quest specifies Episode 4");
}
break;
case Episode::NONE:
throw runtime_error("episode is not set");
throw std::runtime_error("episode is not set");
default:
throw runtime_error("episode is not valid");
throw std::runtime_error("episode is not valid");
}
if (!this->bin_contents) {
throw runtime_error("bin file is missing");
throw std::runtime_error("bin file is missing");
}
if (!this->dat_contents) {
throw runtime_error("dat file is missing");
throw std::runtime_error("dat file is missing");
}
if (!this->map_file) {
throw runtime_error("parsed map file is missing");
throw std::runtime_error("parsed map file is missing");
}
if (this->meta.allowed_drop_modes &&
!(this->meta.allowed_drop_modes & (1 << static_cast<size_t>(this->meta.default_drop_mode)))) {
throw runtime_error("default drop mode is not allowed");
throw std::runtime_error("default drop mode is not allowed");
}
}
string VersionedQuest::bin_filename() const {
std::string VersionedQuest::bin_filename() const {
return std::format("quest{}.bin", this->meta.quest_number);
}
string VersionedQuest::dat_filename() const {
std::string VersionedQuest::dat_filename() const {
return std::format("quest{}.dat", this->meta.quest_number);
}
string VersionedQuest::pvr_filename() const {
std::string VersionedQuest::pvr_filename() const {
return std::format("quest{}.pvr", this->meta.quest_number);
}
string VersionedQuest::xb_filename() const {
std::string VersionedQuest::xb_filename() const {
return std::format("quest{}_{}.dat",
this->meta.quest_number, static_cast<char>(tolower(char_for_language(this->meta.language))));
}
string VersionedQuest::encode_qst() const {
unordered_map<string, shared_ptr<const string>> files;
std::string VersionedQuest::encode_qst() const {
std::unordered_map<std::string, std::shared_ptr<const std::string>> files;
files.emplace(std::format("quest{}.bin", this->meta.quest_number), this->bin_contents);
files.emplace(std::format("quest{}.dat", this->meta.quest_number), this->dat_contents);
if (this->pvr_contents) {
files.emplace(std::format("quest{}.pvr", this->meta.quest_number), this->pvr_contents);
}
string xb_filename = std::format("quest{}_{}.dat",
std::string xb_filename = std::format("quest{}_{}.dat",
this->meta.quest_number, static_cast<char>(tolower(char_for_language(this->meta.language))));
return encode_qst_file(files, this->meta.name, this->meta.quest_number, xb_filename, this->meta.version, this->is_dlq_encoded);
}
Quest::Quest(shared_ptr<const VersionedQuest> initial_version) : meta(initial_version->meta), supermap(nullptr) {
Quest::Quest(std::shared_ptr<const VersionedQuest> initial_version) : meta(initial_version->meta), supermap(nullptr) {
this->add_version(initial_version);
}
@@ -310,7 +308,7 @@ uint32_t Quest::versions_key(Version v, Language language) {
return (static_cast<uint32_t>(v) << 8) | static_cast<uint8_t>(language);
}
const string& Quest::name_for_language(Language language) const {
const std::string& Quest::name_for_language(Language language) const {
size_t lang_index = static_cast<size_t>(language);
if (!this->names_by_language.at(lang_index).empty()) {
return this->names_by_language[lang_index];
@@ -319,7 +317,7 @@ const string& Quest::name_for_language(Language language) const {
if (!this->names_by_language[english_lang_index].empty()) {
return this->names_by_language[english_lang_index];
}
for (const string& name : this->names_by_language) {
for (const std::string& name : this->names_by_language) {
if (!name.empty()) {
return name;
}
@@ -327,7 +325,7 @@ const string& Quest::name_for_language(Language language) const {
return this->meta.name;
}
void Quest::add_version(shared_ptr<const VersionedQuest> vq) {
void Quest::add_version(std::shared_ptr<const VersionedQuest> vq) {
this->meta.assert_compatible(vq->meta);
if (this->meta.create_item_mask_entries.empty()) {
this->meta.create_item_mask_entries = vq->meta.create_item_mask_entries;
@@ -348,7 +346,7 @@ std::shared_ptr<const SuperMap> Quest::get_supermap(int64_t random_seed) const {
bool save_to_cache = true;
bool any_map_file_present = false;
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
std::array<std::shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
for (Version v : ALL_NON_PATCH_VERSIONS) {
auto vq = this->version(v, Language::ENGLISH);
if (vq && vq->map_file) {
@@ -369,7 +367,7 @@ std::shared_ptr<const SuperMap> Quest::get_supermap(int64_t random_seed) const {
return nullptr;
}
auto supermap = make_shared<SuperMap>(map_files, this->meta.get_floor_to_area());
auto supermap = std::make_shared<SuperMap>(map_files, this->meta.get_floor_to_area());
if (save_to_cache) {
this->supermap = supermap;
}
@@ -389,17 +387,17 @@ bool Quest::has_version_any_language(Version v) const {
return ((it != this->versions.end()) && ((it->first & 0xFF00) == k));
}
shared_ptr<const VersionedQuest> Quest::version(Version v, Language language) const {
std::shared_ptr<const VersionedQuest> Quest::version(Version v, Language language) const {
// Return the requested version, if it exists
try {
return this->versions.at(this->versions_key(v, language));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
// Return the English version, if it exists
try {
return this->versions.at(this->versions_key(v, Language::ENGLISH));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
// Return the first language, if it exists
@@ -411,36 +409,36 @@ shared_ptr<const VersionedQuest> Quest::version(Version v, Language language) co
}
QuestIndex::QuestIndex(
const string& directory, shared_ptr<const QuestCategoryIndex> category_index, bool raise_on_any_failure)
const std::string& directory, std::shared_ptr<const QuestCategoryIndex> category_index, bool raise_on_any_failure)
: directory(directory), category_index(category_index) {
struct FileData {
string filename;
shared_ptr<const string> data;
std::string filename;
std::shared_ptr<const std::string> data;
};
struct BINFileData {
string filename;
shared_ptr<const AssembledQuestScript> assembled;
shared_ptr<const string> data;
std::string filename;
std::shared_ptr<const AssembledQuestScript> assembled;
std::shared_ptr<const std::string> data;
};
struct DATFileData {
string filename;
shared_ptr<const string> data;
shared_ptr<const MapFile> map_file;
std::string filename;
std::shared_ptr<const std::string> data;
std::shared_ptr<const MapFile> map_file;
};
map<string, BINFileData> bin_files;
map<string, DATFileData> dat_files;
map<string, FileData> pvr_files;
map<string, FileData> json_files;
map<string, uint32_t> categories;
std::map<std::string, BINFileData> bin_files;
std::map<std::string, DATFileData> dat_files;
std::map<std::string, FileData> pvr_files;
std::map<std::string, FileData> json_files;
std::map<std::string, uint32_t> categories;
for (const auto& cat : this->category_index->categories) {
auto add_file = [&](map<string, FileData>& files, const string& basename, const string& filename, string&& value, bool check_chunk_size) {
auto add_file = [&](std::map<std::string, FileData>& files, const std::string& basename, const std::string& filename, std::string&& value, bool check_chunk_size) {
if (categories.emplace(basename, cat->category_id).first->second != cat->category_id) {
throw runtime_error("file " + basename + " exists in multiple categories");
throw std::runtime_error("file " + basename + " exists in multiple categories");
}
auto data_ptr = make_shared<string>(std::move(value));
auto data_ptr = std::make_shared<std::string>(std::move(value));
if (!files.emplace(basename, FileData{filename, data_ptr}).second) {
throw runtime_error("file " + basename + " already exists");
throw std::runtime_error("file " + basename + " already exists");
}
// There is a bug in the client that prevents quests from loading properly if any file's size is a multiple of
// 0x400. See the comments on the 13 command in CommandFormats.hh for more details.
@@ -449,14 +447,14 @@ QuestIndex::QuestIndex(
}
};
auto add_bin_file = [&](const string& basename, const string& filename, string&& data, shared_ptr<AssembledQuestScript> assembled) {
auto add_bin_file = [&](const std::string& basename, const std::string& filename, std::string&& data, std::shared_ptr<AssembledQuestScript> assembled) {
if (categories.emplace(basename, cat->category_id).first->second != cat->category_id) {
throw runtime_error("bin file " + basename + " exists in multiple categories");
throw std::runtime_error("bin file " + basename + " exists in multiple categories");
}
auto data_ptr = make_shared<string>(std::move(data));
auto data_ptr = std::make_shared<std::string>(std::move(data));
auto emplace_ret = bin_files.emplace(basename, BINFileData{});
if (!emplace_ret.second) {
throw runtime_error("bin file " + basename + " already exists");
throw std::runtime_error("bin file " + basename + " already exists");
}
auto& entry = emplace_ret.first->second;
entry.filename = filename;
@@ -466,36 +464,36 @@ QuestIndex::QuestIndex(
data_ptr->push_back(0x00);
}
};
auto add_dat_file = [&](const string& basename, const string& filename, string&& data) {
auto add_dat_file = [&](const std::string& basename, const std::string& filename, std::string&& data) {
if (categories.emplace(basename, cat->category_id).first->second != cat->category_id) {
throw runtime_error("dat file " + basename + " exists in multiple categories");
throw std::runtime_error("dat file " + basename + " exists in multiple categories");
}
auto data_ptr = make_shared<string>(std::move(data));
auto map_file = make_shared<MapFile>(make_shared<string>(prs_decompress(*data_ptr)));
auto data_ptr = std::make_shared<std::string>(std::move(data));
auto map_file = std::make_shared<MapFile>(std::make_shared<std::string>(prs_decompress(*data_ptr)));
if (!dat_files.emplace(basename, DATFileData{filename, data_ptr, map_file}).second) {
throw runtime_error("dat file " + basename + " already exists");
throw std::runtime_error("dat file " + basename + " already exists");
}
if (!(data_ptr->size() & 0x3FF)) {
data_ptr->push_back(0x00);
}
};
string cat_path = directory + "/" + cat->directory_name;
std::string cat_path = directory + "/" + cat->directory_name;
if (!std::filesystem::is_directory(cat_path)) {
static_game_data_log.warning_f("Quest category directory {} is missing; skipping it", cat_path);
continue;
}
for (const auto& item : std::filesystem::directory_iterator(cat_path)) {
string filename = item.path().filename().string();
std::string filename = item.path().filename().string();
if (filename == ".DS_Store") {
continue;
}
string file_path = cat_path + "/" + filename;
shared_ptr<AssembledQuestScript> assembled;
std::string file_path = cat_path + "/" + filename;
std::shared_ptr<AssembledQuestScript> assembled;
try {
string orig_filename = filename;
string file_data;
std::string orig_filename = filename;
std::string file_data;
if (filename.ends_with(".gci")) {
file_data = decode_gci_data(phosg::load_file(file_path));
filename.resize(filename.size() - 4);
@@ -506,8 +504,8 @@ QuestIndex::QuestIndex(
file_data = decode_dlq_data(phosg::load_file(file_path));
filename.resize(filename.size() - 4);
} else if (filename.ends_with(".bin.txt")) {
string include_dir = phosg::dirname(file_path);
assembled = make_shared<AssembledQuestScript>(assemble_quest_script(
std::string include_dir = phosg::dirname(file_path);
assembled = std::make_shared<AssembledQuestScript>(assemble_quest_script(
phosg::load_file(file_path),
{include_dir, "system/quests/includes"},
{include_dir, "system/quests/includes", "system/client-functions/System"}));
@@ -521,9 +519,9 @@ QuestIndex::QuestIndex(
}
size_t dot_pos = filename.rfind('.');
string file_basename;
string extension;
if (dot_pos != string::npos) {
std::string file_basename;
std::string extension;
if (dot_pos != std::string::npos) {
file_basename = phosg::tolower(filename.substr(0, dot_pos));
extension = phosg::tolower(filename.substr(dot_pos + 1));
} else {
@@ -552,14 +550,14 @@ QuestIndex::QuestIndex(
} else if (it.first.ends_with(".pvr")) {
add_file(pvr_files, file_basename, orig_filename, std::move(it.second), true);
} else {
throw runtime_error("qst file contains unsupported file type: " + it.first);
throw std::runtime_error("qst file contains unsupported file type: " + it.first);
}
}
}
} catch (const exception& e) {
} catch (const std::exception& e) {
if (raise_on_any_failure) {
throw runtime_error(format("({}) {}", filename, e.what()));
throw std::runtime_error(std::format("({}) {}", filename, e.what()));
}
static_game_data_log.warning_f("({}) Failed to load quest file: ({})", filename, e.what());
}
@@ -568,10 +566,10 @@ QuestIndex::QuestIndex(
// All quests have a bin file (even in Episode 3, though its format is different), so we use bin_files as the primary
// list of all quests that should be indexed
unordered_map<const FileData*, shared_ptr<const phosg::JSON>> parsed_json_files;
std::unordered_map<const FileData*, std::shared_ptr<const phosg::JSON>> parsed_json_files;
for (auto& [basename, entry] : bin_files) {
try {
auto vq = make_shared<VersionedQuest>();
auto vq = std::make_shared<VersionedQuest>();
// Quest .bin filenames are like K###-VERS-LANG.EXT, where:
// K can be any character (usually it's q)
@@ -580,11 +578,11 @@ QuestIndex::QuestIndex(
// LANG = client language (j, e, g, f, s)
// EXT = file type (bin, bind, bin.dlq, qst, etc.)
// EXT has already been stripped off by the time we get here, so we just parse the remaining fields.
string quest_number_token, version_token, language_token;
std::string quest_number_token, version_token, language_token;
{
vector<string> filename_tokens = phosg::split(basename, '-');
std::vector<std::string> filename_tokens = phosg::split(basename, '-');
if (filename_tokens.size() != 3) {
throw invalid_argument("incorrect filename format");
throw std::invalid_argument("incorrect filename format");
}
quest_number_token = std::move(filename_tokens[0]);
version_token = std::move(filename_tokens[1]);
@@ -599,12 +597,12 @@ QuestIndex::QuestIndex(
} else {
// Get the number from the first token
if (quest_number_token.empty()) {
throw runtime_error("quest number token is missing");
throw std::runtime_error("quest number token is missing");
}
vq->meta.quest_number = strtoull(quest_number_token.c_str() + 1, nullptr, 10);
// Get the version from the second token
static const unordered_map<string, Version> name_to_version({
static const std::unordered_map<std::string, Version> name_to_version({
{"dn", Version::DC_NTE},
{"dp", Version::DC_11_2000},
{"d1", Version::DC_V1},
@@ -622,7 +620,7 @@ QuestIndex::QuestIndex(
// Get the language from the last token
if (language_token.size() != 1) {
throw runtime_error("language token is not a single character");
throw std::runtime_error("language token is not a single character");
}
vq->meta.language = language_for_char(language_token[0]);
}
@@ -636,19 +634,19 @@ QuestIndex::QuestIndex(
const FileData* pvr_filedata = nullptr;
try {
dat_filedata = &dat_files.at(basename);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
try {
dat_filedata = &dat_files.at(quest_number_token + "-" + version_token);
} catch (const out_of_range&) {
throw runtime_error("no dat file found for bin file " + basename);
} catch (const std::out_of_range&) {
throw std::runtime_error("no dat file found for bin file " + basename);
}
}
try {
pvr_filedata = &pvr_files.at(basename);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
try {
pvr_filedata = &pvr_files.at(quest_number_token + "-" + version_token);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
// pvr files aren't required (and most quests do not have them), so don't fail if it's missing
}
}
@@ -665,21 +663,21 @@ QuestIndex::QuestIndex(
const FileData* json_filedata = nullptr;
try {
json_filedata = &json_files.at(basename);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
try {
json_filedata = &json_files.at(quest_number_token + "-" + version_token);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
try {
json_filedata = &json_files.at(quest_number_token);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
}
if (json_filedata) {
try {
vq->json_contents = parsed_json_files.at(json_filedata);
} catch (const out_of_range&) {
vq->json_contents = make_shared<phosg::JSON>(phosg::JSON::parse(*json_filedata->data));
} catch (const std::out_of_range&) {
vq->json_contents = std::make_shared<phosg::JSON>(phosg::JSON::parse(*json_filedata->data));
parsed_json_files.emplace(json_filedata, vq->json_contents);
}
vq->meta.apply_json_overrides(*vq->json_contents);
@@ -688,7 +686,7 @@ QuestIndex::QuestIndex(
vq->assert_valid();
auto category_name = this->category_index->at(vq->meta.category_id)->name;
string filenames_str = entry.filename;
std::string filenames_str = entry.filename;
if (dat_filedata) {
filenames_str += std::format("/{}", dat_filedata->filename);
}
@@ -708,7 +706,7 @@ QuestIndex::QuestIndex(
vq->meta.quest_number,
vq->meta.name);
} else {
auto q = make_shared<Quest>(vq);
auto q = std::make_shared<Quest>(vq);
this->quests_by_number.emplace(vq->meta.quest_number, q);
this->quests_by_name.emplace(vq->meta.name, q);
this->quests_by_category_id_and_number[q->meta.category_id].emplace(vq->meta.quest_number, q);
@@ -723,9 +721,9 @@ QuestIndex::QuestIndex(
vq->meta.category_id,
vq->meta.joinable ? "joinable" : "not joinable");
}
} catch (const exception& e) {
} catch (const std::exception& e) {
if (raise_on_any_failure) {
throw runtime_error(format("({}) {}", basename, e.what()));
throw std::runtime_error(std::format("({}) {}", basename, e.what()));
}
static_game_data_log.warning_f("({}) Failed to index quest file: {}", basename, e.what());
}
@@ -757,25 +755,25 @@ phosg::JSON QuestIndex::json() const {
});
}
shared_ptr<const Quest> QuestIndex::get(uint32_t quest_number) const {
std::shared_ptr<const Quest> QuestIndex::get(uint32_t quest_number) const {
try {
return this->quests_by_number.at(quest_number);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
shared_ptr<const Quest> QuestIndex::get(const std::string& name) const {
std::shared_ptr<const Quest> QuestIndex::get(const std::string& name) const {
try {
return this->quests_by_name.at(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
vector<shared_ptr<const QuestCategoryIndex::Category>> QuestIndex::categories(
std::vector<std::shared_ptr<const QuestCategoryIndex::Category>> QuestIndex::categories(
QuestMenuType menu_type, Episode episode, uint16_t version_flags, IncludeCondition include_condition) const {
vector<shared_ptr<const QuestCategoryIndex::Category>> ret;
std::vector<std::shared_ptr<const QuestCategoryIndex::Category>> ret;
for (const auto& cat : this->category_index->categories) {
if (cat->check_flag(menu_type) && !this->filter(episode, version_flags, cat->category_id, include_condition, 1).empty()) {
ret.emplace_back(cat);
@@ -784,7 +782,7 @@ vector<shared_ptr<const QuestCategoryIndex::Category>> QuestIndex::categories(
return ret;
}
vector<pair<QuestIndex::IncludeState, shared_ptr<const Quest>>> QuestIndex::filter(
std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>> QuestIndex::filter(
Episode episode,
uint16_t version_flags,
uint32_t category_id,
@@ -793,7 +791,7 @@ vector<pair<QuestIndex::IncludeState, shared_ptr<const Quest>>> QuestIndex::filt
auto cat = this->category_index->at(category_id);
Episode effective_episode = cat->enable_episode_filter() ? episode : Episode::NONE;
vector<pair<IncludeState, shared_ptr<const Quest>>> ret;
std::vector<std::pair<IncludeState, std::shared_ptr<const Quest>>> ret;
auto category_it = this->quests_by_category_id_and_number.find(category_id);
if (category_it == this->quests_by_category_id_and_number.end()) {
return ret;
@@ -824,7 +822,8 @@ vector<pair<QuestIndex::IncludeState, shared_ptr<const Quest>>> QuestIndex::filt
return ret;
}
string encode_download_quest_data(const string& compressed_data, size_t decompressed_size, uint32_t encryption_seed) {
std::string encode_download_quest_data(
const std::string& compressed_data, size_t decompressed_size, uint32_t encryption_seed) {
// Download quest files are like normal (PRS-compressed) quest files, but they are encrypted with PSO V2 encryption
// (even on V3 / PSO GC), and a small header (PSODownloadQuestHeader) is prepended to the encrypted data.
@@ -835,7 +834,7 @@ string encode_download_quest_data(const string& compressed_data, size_t decompre
decompressed_size = prs_decompress_size(compressed_data);
}
string data(8, '\0');
std::string data(8, '\0');
auto* header = reinterpret_cast<PSODownloadQuestHeader*>(data.data());
header->size = decompressed_size;
header->encryption_seed = encryption_seed;
@@ -852,18 +851,18 @@ string encode_download_quest_data(const string& compressed_data, size_t decompre
return data;
}
shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(Language override_language) const {
std::shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(Language override_language) const {
// The download flag needs to be set in the bin header, or else the client will ignore it when scanning for download
// quests in an offline game. To set this flag, we need to decompress the quest's .bin file, set the flag, then
// recompress it again.
string decompressed_bin = prs_decompress(*this->bin_contents);
std::string decompressed_bin = prs_decompress(*this->bin_contents);
void* data_ptr = decompressed_bin.data();
switch (this->meta.version) {
case Version::DC_NTE:
if (decompressed_bin.size() < sizeof(PSOQuestHeaderDCNTE)) {
throw runtime_error("bin file is too small for header");
throw std::runtime_error("bin file is too small for header");
}
// There's no known language field in this version, so we don't write anything here
break;
@@ -871,7 +870,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(Language overri
case Version::DC_V1:
case Version::DC_V2:
if (decompressed_bin.size() < sizeof(PSOQuestHeaderDC)) {
throw runtime_error("bin file is too small for header");
throw std::runtime_error("bin file is too small for header");
}
if (override_language != Language::UNKNOWN) {
reinterpret_cast<PSOQuestHeaderDC*>(data_ptr)->language = override_language;
@@ -880,7 +879,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(Language overri
case Version::PC_NTE:
case Version::PC_V2:
if (decompressed_bin.size() < sizeof(PSOQuestHeaderPC)) {
throw runtime_error("bin file is too small for header");
throw std::runtime_error("bin file is too small for header");
}
if (override_language != Language::UNKNOWN) {
reinterpret_cast<PSOQuestHeaderPC*>(data_ptr)->language = override_language;
@@ -890,30 +889,31 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(Language overri
case Version::GC_V3:
case Version::XB_V3:
if (decompressed_bin.size() < sizeof(PSOQuestHeaderV3)) {
throw runtime_error("bin file is too small for header");
throw std::runtime_error("bin file is too small for header");
}
if (override_language != Language::UNKNOWN) {
reinterpret_cast<PSOQuestHeaderV3*>(data_ptr)->language = override_language;
}
break;
case Version::BB_V4:
throw invalid_argument("PSOBB does not support download quests");
throw std::invalid_argument("PSOBB does not support download quests");
default:
throw invalid_argument("unknown game version");
throw std::invalid_argument("unknown game version");
}
string compressed_bin = prs_compress(decompressed_bin);
std::string compressed_bin = prs_compress(decompressed_bin);
// Return a new VersionedQuest object with appropriately-processed .bin and .dat file contents
auto dlq = make_shared<VersionedQuest>(*this);
dlq->bin_contents = make_shared<string>(encode_download_quest_data(compressed_bin, decompressed_bin.size()));
dlq->dat_contents = make_shared<string>(encode_download_quest_data(*this->dat_contents));
auto dlq = std::make_shared<VersionedQuest>(*this);
dlq->bin_contents = std::make_shared<std::string>(encode_download_quest_data(compressed_bin, decompressed_bin.size()));
dlq->dat_contents = std::make_shared<std::string>(encode_download_quest_data(*this->dat_contents));
dlq->pvr_contents = this->pvr_contents;
dlq->is_dlq_encoded = true;
return dlq;
}
string decode_gci_data(const string& data, ssize_t find_seed_num_threads, int64_t known_seed, bool skip_checksum) {
std::string decode_gci_data(
const std::string& data, ssize_t find_seed_num_threads, int64_t known_seed, bool skip_checksum) {
phosg::StringReader r(data);
const auto& header = r.get<PSOGCIFileHeader>();
header.check();
@@ -933,10 +933,10 @@ string decode_gci_data(const string& data, ssize_t find_seed_num_threads, int64_
} else {
if (find_seed_num_threads < 0) {
throw runtime_error("file is encrypted");
throw std::runtime_error("file is encrypted");
}
if (find_seed_num_threads == 0) {
find_seed_num_threads = thread::hardware_concurrency();
find_seed_num_threads = std::thread::hardware_concurrency();
}
return find_seed_and_decrypt_download_quest_data_section<true>(
r.getv(header.data_size), header.data_size, skip_checksum, false, find_seed_num_threads);
@@ -944,12 +944,12 @@ string decode_gci_data(const string& data, ssize_t find_seed_num_threads, int64_
} else { // Unencrypted GCI format
r.skip(sizeof(PSOGCIDLQFileEncryptedHeader));
string compressed_data = r.readx(header.data_size - sizeof(PSOGCIDLQFileEncryptedHeader));
std::string compressed_data = r.readx(header.data_size - sizeof(PSOGCIDLQFileEncryptedHeader));
size_t decompressed_bytes = prs_decompress_size(compressed_data);
size_t expected_decompressed_bytes = dlq_header.decompressed_size - 8;
if (decompressed_bytes < expected_decompressed_bytes) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"GCI decompressed data is smaller than expected size (have 0x{:X} bytes, expected 0x{:X} bytes)",
decompressed_bytes, expected_decompressed_bytes));
}
@@ -964,10 +964,10 @@ string decode_gci_data(const string& data, ssize_t find_seed_num_threads, int64_
r.getv(header.data_size), header.data_size, known_seed, true, true);
} else {
if (find_seed_num_threads < 0) {
throw runtime_error("file is encrypted");
throw std::runtime_error("file is encrypted");
}
if (find_seed_num_threads == 0) {
find_seed_num_threads = thread::hardware_concurrency();
find_seed_num_threads = std::thread::hardware_concurrency();
}
return find_seed_and_decrypt_download_quest_data_section<true>(
r.getv(header.data_size), header.data_size, true, true, find_seed_num_threads);
@@ -982,21 +982,21 @@ string decode_gci_data(const string& data, ssize_t find_seed_num_threads, int64_
// The game treats this field as a 16-byte string (including the \0). The 8 bytes after it appear to be
// completely unused.
if (r.readx(15) != "SONICTEAM,SEGA.") {
throw runtime_error("Episode 3 GCI file is not a quest");
throw std::runtime_error("Episode 3 GCI file is not a quest");
}
r.skip(9);
string decrypted = r.readx(header.data_size - 40);
std::string decrypted = r.readx(header.data_size - 40);
// For some reason, Sega decided not to encrypt Episode 3 quest files in the same way as Episodes 1&2 quest files
// (see above). Instead, they just wrote a fairly trivial XOR loop over the first 0x100 bytes, leaving the
// remaining bytes completely unencrypted (but still compressed).
size_t unscramble_size = min<size_t>(0x100, data.size());
size_t unscramble_size = std::min<size_t>(0x100, data.size());
decrypt_trivial_gci_data(decrypted.data(), unscramble_size, 0);
size_t decompressed_size = prs_decompress_size(decrypted);
if (decompressed_size != sizeof(Episode3::MapDefinition)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"decompressed quest is 0x{:X} bytes; expected 0x{:X} bytes",
decompressed_size, sizeof(Episode3::MapDefinition)));
}
@@ -1004,22 +1004,23 @@ string decode_gci_data(const string& data, ssize_t find_seed_num_threads, int64_
}
} else {
throw runtime_error("unknown game name in GCI header");
throw std::runtime_error("unknown game name in GCI header");
}
}
string decode_vms_data(const string& data, ssize_t find_seed_num_threads, int64_t known_seed, bool skip_checksum) {
std::string decode_vms_data(
const std::string& data, ssize_t find_seed_num_threads, int64_t known_seed, bool skip_checksum) {
phosg::StringReader r(data);
const auto& header = r.get<PSOVMSFileHeader>();
if (!header.checksum_correct()) {
throw runtime_error("VMS file unencrypted header checksum is incorrect");
throw std::runtime_error("VMS file unencrypted header checksum is incorrect");
}
r.skip(header.num_icons * 0x200);
const void* data_section = r.getv(header.data_size);
try {
return decrypt_vms_v1_data_section(data_section, header.data_size);
} catch (const exception& e) {
} catch (const std::exception& e) {
}
if (known_seed >= 0) {
@@ -1027,17 +1028,17 @@ string decode_vms_data(const string& data, ssize_t find_seed_num_threads, int64_
} else {
if (find_seed_num_threads < 0) {
throw runtime_error("file is encrypted");
throw std::runtime_error("file is encrypted");
}
if (find_seed_num_threads == 0) {
find_seed_num_threads = thread::hardware_concurrency();
find_seed_num_threads = std::thread::hardware_concurrency();
}
return find_seed_and_decrypt_download_quest_data_section<false>(
data_section, header.data_size, skip_checksum, 0, find_seed_num_threads);
}
}
string decode_dlq_data(const string& data) {
std::string decode_dlq_data(const std::string& data) {
phosg::StringReader r(data);
uint32_t decompressed_size = r.get_u32l();
uint32_t key = r.get_u32l();
@@ -1045,7 +1046,7 @@ string decode_dlq_data(const string& data) {
// The compressed data size does not need to be a multiple of 4, but the V2 encryption (which is used for all
// download quests, even in V3) requires the data size to be a multiple of 4. We'll just temporarily stick a few
// bytes on the end, then throw them away later if needed.
string decrypted = r.read(r.remaining());
std::string decrypted = r.read(r.remaining());
PSOV2Encryption encr(key);
size_t original_size = data.size();
decrypted.resize((decrypted.size() + 3) & (~3));
@@ -1053,18 +1054,18 @@ string decode_dlq_data(const string& data) {
decrypted.resize(original_size);
if (prs_decompress_size(decrypted) != decompressed_size) {
throw runtime_error("decompressed size does not match size in header");
throw std::runtime_error("decompressed size does not match size in header");
}
return decrypted;
}
template <typename HeaderT, typename OpenFileT>
static unordered_map<string, string> decode_qst_data_t(const string& data) {
static std::unordered_map<std::string, std::string> decode_qst_data_t(const std::string& data) {
phosg::StringReader r(data);
unordered_map<string, string> files;
unordered_map<string, size_t> file_remaining_bytes;
std::unordered_map<std::string, std::string> files;
std::unordered_map<std::string, size_t> file_remaining_bytes;
QuestFileFormat subformat = QuestFileFormat::QST; // Stand-in for unknown
while (!r.eof()) {
// Handle BB's implicit 8-byte command alignment
@@ -1081,62 +1082,62 @@ static unordered_map<string, string> decode_qst_data_t(const string& data) {
if (subformat == QuestFileFormat::QST) {
subformat = QuestFileFormat::BIN_DAT;
} else if (subformat != QuestFileFormat::BIN_DAT) {
throw runtime_error("QST file contains mixed download and non-download commands");
throw std::runtime_error("QST file contains mixed download and non-download commands");
}
} else if (header.command == 0xA6 || header.command == 0xA7) {
if (subformat == QuestFileFormat::QST) {
subformat = QuestFileFormat::BIN_DAT_DLQ;
} else if (subformat != QuestFileFormat::BIN_DAT_DLQ) {
throw runtime_error("QST file contains mixed download and non-download commands");
throw std::runtime_error("QST file contains mixed download and non-download commands");
}
}
if (header.command == 0x44 || header.command == 0xA6) {
if (header.size != sizeof(HeaderT) + sizeof(OpenFileT)) {
throw runtime_error("qst open file command has incorrect size");
throw std::runtime_error("qst open file command has incorrect size");
}
const auto& cmd = r.get<OpenFileT>();
string internal_filename = cmd.filename.decode();
std::string internal_filename = cmd.filename.decode();
if (!files.emplace(internal_filename, "").second) {
throw runtime_error("qst opens the same file multiple times: " + internal_filename);
throw std::runtime_error("qst opens the same file multiple times: " + internal_filename);
}
if (!file_remaining_bytes.emplace(internal_filename, cmd.file_size).second) {
throw runtime_error("qst opens the same file multiple times: " + internal_filename);
throw std::runtime_error("qst opens the same file multiple times: " + internal_filename);
}
} else if (header.command == 0x13 || header.command == 0xA7) {
// We have to allow larger commands here, because it seems some tools encoded QST files with BB's extra 4 padding
// bytes included in the command size.
if (header.size < sizeof(HeaderT) + sizeof(S_WriteFile_13_A7)) {
throw runtime_error("qst write file command has incorrect size");
throw std::runtime_error("qst write file command has incorrect size");
}
const auto& cmd = r.get<S_WriteFile_13_A7>();
if (cmd.data_size > 0x400) {
throw runtime_error("qst contains invalid write command");
throw std::runtime_error("qst contains invalid write command");
}
string filename = cmd.filename.decode();
std::string filename = cmd.filename.decode();
string& file_data = files.at(filename);
std::string& file_data = files.at(filename);
size_t& remaining_bytes = file_remaining_bytes.at(filename);
if (file_data.size() & 0x3FF) {
throw runtime_error("qst contains uneven chunks out of order");
throw std::runtime_error("qst contains uneven chunks out of order");
}
if (header.flag != file_data.size() / 0x400) {
throw runtime_error("qst contains chunks out of order");
throw std::runtime_error("qst contains chunks out of order");
}
file_data.append(reinterpret_cast<const char*>(cmd.data.data()), cmd.data_size);
remaining_bytes -= cmd.data_size;
} else {
throw runtime_error("invalid command in qst file");
throw std::runtime_error("invalid command in qst file");
}
}
for (const auto& it : file_remaining_bytes) {
if (it.second) {
throw runtime_error(std::format("expected {} (0x{:X}) more bytes for file {}", it.second, it.second, it.first));
throw std::runtime_error(std::format("expected {} (0x{:X}) more bytes for file {}", it.second, it.second, it.first));
}
}
@@ -1149,7 +1150,7 @@ static unordered_map<string, string> decode_qst_data_t(const string& data) {
return files;
}
unordered_map<string, string> decode_qst_data(const string& data) {
std::unordered_map<std::string, std::string> decode_qst_data(const std::string& data) {
// QST files start with an open file command, but the format differs depending on the PSO version that the qst file
// is for. We can detect the format from the first 4 bytes in the file:
// - BB: 58 00 44 00 or 58 00 A6 00
@@ -1174,7 +1175,7 @@ unordered_map<string, string> decode_qst_data(const string& data) {
} else if (((signature & 0xFF00FFFF) == 0x44005400) || ((signature & 0xFF00FFFF) == 0xA6005400)) {
return decode_qst_data_t<PSOCommandHeaderDCV3, S_OpenFile_XB_44_A6>(data);
} else {
throw runtime_error("invalid qst file format");
throw std::runtime_error("invalid qst file format");
}
}
@@ -1229,12 +1230,12 @@ void add_open_file_command_t<PSOCommandHeaderDCV3, S_OpenFile_XB_44_A6>(
template <typename HeaderT>
void add_write_file_commands_t(
phosg::StringWriter& w,
const string& filename,
const string& data,
const std::string& filename,
const std::string& data,
bool is_download,
bool bb_alignment) {
for (size_t z = 0; z < data.size(); z += 0x400) {
size_t chunk_size = min<size_t>(data.size() - z, 0x400);
size_t chunk_size = std::min<size_t>(data.size() - z, 0x400);
add_command_header<HeaderT>(w, is_download ? 0xA7 : 0x13, z >> 10, sizeof(S_WriteFile_13_A7));
S_WriteFile_13_A7 cmd;
cmd.filename.encode(filename);
@@ -1249,11 +1250,11 @@ void add_write_file_commands_t(
}
}
string encode_qst_file(
const unordered_map<string, shared_ptr<const string>>& files,
const string& name,
std::string encode_qst_file(
const std::unordered_map<std::string, std::shared_ptr<const std::string>>& files,
const std::string& name,
uint32_t quest_number,
const string& xb_filename,
const std::string& xb_filename,
Version version,
bool is_dlq_encoded) {
phosg::StringWriter w;
@@ -1313,7 +1314,7 @@ string encode_qst_file(
}
break;
default:
throw logic_error("invalid game version");
throw std::logic_error("invalid game version");
}
return std::move(w.str());
+51 -52
View File
@@ -1,7 +1,5 @@
#include "QuestMetadata.hh"
using namespace std;
phosg::JSON QuestMetadata::FloorAssignment::json() const {
return phosg::JSON::dict({
{"Floor", this->floor},
@@ -21,51 +19,51 @@ std::string QuestMetadata::FloorAssignment::str() const {
void QuestMetadata::apply_json_overrides(const phosg::JSON& json) {
try {
this->description_flag = json.at("DescriptionFlag").as_int();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->available_expression = make_shared<IntegralExpression>(json.get_string("AvailableIf"));
} catch (const out_of_range&) {
this->available_expression = std::make_shared<IntegralExpression>(json.get_string("AvailableIf"));
} catch (const std::out_of_range&) {
}
try {
this->enabled_expression = make_shared<IntegralExpression>(json.get_string("EnabledIf"));
} catch (const out_of_range&) {
this->enabled_expression = std::make_shared<IntegralExpression>(json.get_string("EnabledIf"));
} catch (const std::out_of_range&) {
}
try {
this->allow_start_from_chat_command = json.get_bool("AllowStartFromChatCommand");
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->joinable = json.get_bool("Joinable");
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->lock_status_register = json.get_int("LockStatusRegister");
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->enemy_exp_overrides = QuestMetadata::parse_enemy_exp_overrides(json.at("EnemyEXPOverrides"));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->common_item_set_name = json.at("CommonItemSetName").as_string();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->rare_item_set_name = json.at("RareItemSetName").as_string();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->allowed_drop_modes = json.at("AllowedDropModes").as_int();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->default_drop_mode = phosg::enum_for_name<ServerDropMode>(json.at("DefaultDropMode").as_string());
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->enable_schtserv_commands = json.at("EnableSchtservCommands").as_bool();
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
@@ -82,51 +80,51 @@ void QuestMetadata::assign_default_floors() {
void QuestMetadata::assert_compatible(const QuestMetadata& other) const {
if (this->quest_number != other.quest_number) {
throw logic_error(std::format(
throw std::logic_error(std::format(
"incorrect versioned quest number (existing: {:08X}, new: {:08X})", this->quest_number, other.quest_number));
}
if (this->category_id != other.category_id) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version is in a different category (existing: {:08X}, new: {:08X})",
this->category_id, other.category_id));
}
if (this->episode != other.episode) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version is in a different episode (existing: {}, new: {})",
name_for_episode(this->episode), name_for_episode(other.episode)));
}
if (this->allow_start_from_chat_command != other.allow_start_from_chat_command) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has a different allow_start_from_chat_command state (existing: {}, new: {})",
this->allow_start_from_chat_command ? "true" : "false",
other.allow_start_from_chat_command ? "true" : "false"));
}
if (this->joinable != other.joinable) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has a different joinability state (existing: {}, new: {})",
this->joinable ? "true" : "false", other.joinable ? "true" : "false"));
}
bool this_has_player_limit = (this->max_players != 0) && (this->max_players != 4);
bool other_has_player_limit = (other.max_players != 0) && (other.max_players != 4);
if ((this_has_player_limit || other_has_player_limit) && (this->max_players != other.max_players)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has a different maximum player count (existing: {}, new: {})",
this->max_players, other.max_players));
}
if (this->lock_status_register != other.lock_status_register) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has a different lock status register (existing: {:04X}, new: {:04X})",
this->lock_status_register, other.lock_status_register));
}
if (this->enemy_exp_overrides != other.enemy_exp_overrides) {
throw runtime_error("quest version has different enemy EXP overrides");
throw std::runtime_error("quest version has different enemy EXP overrides");
}
if (this->solo_unlock_flags != other.solo_unlock_flags) {
throw runtime_error(std::format("quest version has a different set of solo unlock flags"));
throw std::runtime_error(std::format("quest version has a different set of solo unlock flags"));
}
if (!this->create_item_mask_entries.empty() && !other.create_item_mask_entries.empty() &&
this->create_item_mask_entries != other.create_item_mask_entries) {
string this_str, other_str;
std::string this_str, other_str;
for (const auto& item : this->create_item_mask_entries) {
if (!this_str.empty()) {
this_str += ", ";
@@ -139,32 +137,32 @@ void QuestMetadata::assert_compatible(const QuestMetadata& other) const {
}
other_str += item.str();
}
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has a different set of create item masks (existing: {}, new: {})", this_str, other_str));
}
if (!this->battle_rules != !other.battle_rules) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has a different battle rules presence state (existing: {}, new: {})",
this->battle_rules ? "present" : "absent", other.battle_rules ? "present" : "absent"));
}
if (this->battle_rules && (*this->battle_rules != *other.battle_rules)) {
string existing_str = this->battle_rules->json().serialize();
string new_str = other.battle_rules->json().serialize();
throw runtime_error(std::format(
std::string existing_str = this->battle_rules->json().serialize();
std::string new_str = other.battle_rules->json().serialize();
throw std::runtime_error(std::format(
"quest version has different battle rules (existing: {}, new: {})", existing_str, new_str));
}
if (this->challenge_template_index != other.challenge_template_index) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different challenge template index (existing: {}, new: {})",
this->challenge_template_index, other.challenge_template_index));
}
if (this->challenge_exp_multiplier != other.challenge_exp_multiplier) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different challenge EXP multiplier (existing: {}, new: {})",
this->challenge_exp_multiplier, other.challenge_exp_multiplier));
}
if (this->challenge_difficulty != other.challenge_difficulty) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different challenge difficulty (existing: {}, new: {})",
name_for_difficulty(this->challenge_difficulty), name_for_difficulty(other.challenge_difficulty)));
}
@@ -172,58 +170,59 @@ void QuestMetadata::assert_compatible(const QuestMetadata& other) const {
const auto& this_fa = this->floor_assignments[z];
const auto& other_fa = other.floor_assignments[z];
if (this_fa.area != other_fa.area) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different area on floor 0x{:02X} (existing: {}, new: {})",
z, this_fa.str(), other_fa.str()));
}
}
if (this->description_flag != other.description_flag) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different description flag (existing: {:02X}, new: {:02X})",
this->description_flag, other.description_flag));
}
if (!this->available_expression != !other.available_expression) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has available expression but root quest does not, or vice versa (existing: {}, new: {})",
this->available_expression ? "present" : "absent", other.available_expression ? "present" : "absent"));
}
if (this->available_expression && *this->available_expression != *other.available_expression) {
string existing_str = this->available_expression->str();
string new_str = other.available_expression->str();
throw runtime_error(std::format(
std::string existing_str = this->available_expression->str();
std::string new_str = other.available_expression->str();
throw std::runtime_error(std::format(
"quest version has a different available expression (existing: {}, new: {})", existing_str, new_str));
}
if (!this->enabled_expression != !other.enabled_expression) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has enabled expression but root quest does not, or vice versa (existing: {}, new: {})",
this->enabled_expression ? "present" : "absent", other.enabled_expression ? "present" : "absent"));
}
if (this->enabled_expression && *this->enabled_expression != *other.enabled_expression) {
string existing_str = this->enabled_expression->str();
string new_str = other.enabled_expression->str();
throw runtime_error(std::format(
std::string existing_str = this->enabled_expression->str();
std::string new_str = other.enabled_expression->str();
throw std::runtime_error(std::format(
"quest version has a different enabled expression (existing: {}, new: {})", existing_str, new_str));
}
if (this->common_item_set_name != other.common_item_set_name) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different common table name (existing: {}, new: {})",
this->common_item_set_name, other.common_item_set_name));
}
if (this->rare_item_set_name != other.rare_item_set_name) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"quest version has different rare table name (existing: {}, new: {})",
this->rare_item_set_name, other.rare_item_set_name));
}
if (this->allowed_drop_modes != other.allowed_drop_modes) {
throw runtime_error(format("quest version has different allowed drop modes (existing: {:02X}, new: {:02X})",
throw std::runtime_error(std::format(
"quest version has different allowed drop modes (existing: {:02X}, new: {:02X})",
this->allowed_drop_modes, other.allowed_drop_modes));
}
if (this->default_drop_mode != other.default_drop_mode) {
throw runtime_error(format("quest version has different default drop mode (existing: {}, new: {})",
throw std::runtime_error(std::format("quest version has different default drop mode (existing: {}, new: {})",
phosg::name_for_enum(this->default_drop_mode), phosg::name_for_enum(other.default_drop_mode)));
}
if (this->enable_schtserv_commands != other.enable_schtserv_commands) {
throw runtime_error(format(
throw std::runtime_error(std::format(
"quest version has different value for enable_schtserv_commands (existing: {}, new: {})",
this->enable_schtserv_commands ? "true" : "false", other.enable_schtserv_commands ? "true" : "false"));
}
@@ -354,7 +353,7 @@ std::unordered_map<uint32_t, uint32_t> QuestMetadata::parse_enemy_exp_overrides(
// Key is like "Difficulty:Floor:EnemyType" or "Difficulty:EnemyType"
auto key_tokens = phosg::split(key, ':');
static const unordered_map<string, Difficulty> difficulty_keys(
static const std::unordered_map<std::string, Difficulty> difficulty_keys(
{{"Normal", Difficulty::NORMAL}, {"Hard", Difficulty::HARD}, {"VeryHard", Difficulty::VERY_HARD}, {"Ultimate", Difficulty::ULTIMATE}});
Difficulty difficulty = Difficulty::NORMAL;
@@ -366,7 +365,7 @@ std::unordered_map<uint32_t, uint32_t> QuestMetadata::parse_enemy_exp_overrides(
floor = stoul(key_tokens[1], nullptr, 0);
enemy_type = phosg::enum_for_name<EnemyType>(key_tokens[2]);
} else {
throw runtime_error("malformatted key: " + key);
throw std::runtime_error("malformatted key: " + key);
}
difficulty = difficulty_keys.at(key_tokens[0]);
if (floor == 0xFF) {
@@ -379,7 +378,7 @@ std::unordered_map<uint32_t, uint32_t> QuestMetadata::parse_enemy_exp_overrides(
}
return ret;
} catch (const exception& e) {
} catch (const std::exception& e) {
throw std::runtime_error(std::format("invalid enemy EXP overrides: ", e.what()));
}
}
+199 -200
View File
File diff suppressed because it is too large Load Diff
+66 -71
View File
@@ -9,18 +9,14 @@
#include "ItemData.hh"
#include "StaticGameData.hh"
using namespace std;
string RareItemSet::ExpandedDrop::str() const {
std::string RareItemSet::ExpandedDrop::str() const {
auto frac = phosg::reduce_fraction<uint64_t>(this->probability, 0x100000000);
auto hex = this->data.hex();
return std::format(
"({:08X} => {}/{}) {}",
this->probability, frac.first, frac.second, hex);
return std::format("({:08X} => {}/{}) {}", this->probability, frac.first, frac.second, hex);
}
string RareItemSet::ExpandedDrop::str(shared_ptr<const ItemNameIndex> name_index) const {
string ret = this->str();
std::string RareItemSet::ExpandedDrop::str(std::shared_ptr<const ItemNameIndex> name_index) const {
std::string ret = this->str();
ret += " (";
ret += name_index->describe_item(this->data);
ret += ")";
@@ -74,7 +70,7 @@ uint8_t RareItemSet::compress_rate(uint32_t probability) {
RareItemSet::ParsedRELData::PackedDrop::PackedDrop(const ExpandedDrop& exp)
: probability(RareItemSet::compress_rate(exp.probability)) {
if (!exp.data.can_be_encoded_in_rel_rare_table()) {
throw runtime_error("item " + exp.data.short_hex() + " has extended attributes and cannot be encoded in a REL file");
throw std::runtime_error("item " + exp.data.short_hex() + " has extended attributes and cannot be encoded in a REL file");
}
this->item_code[0] = exp.data.data1[0];
this->item_code[1] = exp.data.data1[1];
@@ -182,7 +178,7 @@ RareItemSet::ParsedRELData::ParsedRELData(const SpecCollection& collection) {
for (const auto& [enemy_type, specs] : collection.enemy_specs) {
const auto& def = type_definition_for_enemy(enemy_type);
if (def.rt_index == 0xFF) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"monster spec for {} has no rt_index and cannot be converted to ItemRT format", def.enum_name));
}
@@ -191,14 +187,14 @@ RareItemSet::ParsedRELData::ParsedRELData(const SpecCollection& collection) {
if (dest_spec.data.empty()) {
dest_spec = spec;
} else if ((dest_spec.probability != spec.probability) || (dest_spec.data != spec.data)) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"monster spec for {} contains multiple drops and cannot be converted to ItemRT format", def.enum_name));
}
}
}
if (collection.box_specs.size() > 0xFF) {
throw runtime_error("area_norm value too high");
throw std::runtime_error("area_norm value too high");
}
for (uint8_t area_norm = 0; area_norm < collection.box_specs.size(); area_norm++) {
for (const auto& spec : collection.box_specs[area_norm]) {
@@ -249,14 +245,14 @@ RareItemSet::RareItemSet(const AFSArchive& afs, bool is_v1) {
ParsedRELData rel(afs.get_reader(index), false, is_v1);
this->collections.emplace(
this->key_for_params(mode, Episode::EP1, difficulty, section_id), rel.as_collection(Episode::EP1));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
}
}
}
string RareItemSet::gsl_entry_name_for_table(
std::string RareItemSet::gsl_entry_name_for_table(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t section_id) {
return std::format("ItemRT{}{}{}{}.rel",
((mode == GameMode::CHALLENGE) ? "c" : ""),
@@ -271,11 +267,11 @@ RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
for (size_t section_id = 0; section_id < 10; section_id++) {
try {
string filename = this->gsl_entry_name_for_table(mode, episode, difficulty, section_id);
std::string filename = this->gsl_entry_name_for_table(mode, episode, difficulty, section_id);
ParsedRELData rel(gsl.get_reader(filename), is_big_endian, false);
this->collections.emplace(
this->key_for_params(mode, episode, difficulty, section_id), rel.as_collection(episode));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
}
@@ -283,7 +279,7 @@ RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
}
}
RareItemSet::RareItemSet(const string& rel_data, bool is_big_endian) {
RareItemSet::RareItemSet(const std::string& rel_data, bool is_big_endian) {
// Tables are 0x280 bytes in size in this format, laid out sequentially
phosg::StringReader r(rel_data);
for (Episode episode : ALL_EPISODES_V4) {
@@ -295,26 +291,26 @@ RareItemSet::RareItemSet(const string& rel_data, bool is_big_endian) {
ParsedRELData rel(r.sub(0x280 * index, 0x280), is_big_endian, false);
this->collections.emplace(
this->key_for_params(GameMode::NORMAL, episode, difficulty, section_id), rel.as_collection(episode));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
}
}
}
RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex> name_index) {
RareItemSet::RareItemSet(const phosg::JSON& json, std::shared_ptr<const ItemNameIndex> name_index) {
for (const auto& mode_it : json.as_dict()) {
static const unordered_map<string, GameMode> mode_keys(
static const std::unordered_map<std::string, GameMode> mode_keys(
{{"Normal", GameMode::NORMAL}, {"Battle", GameMode::BATTLE}, {"Challenge", GameMode::CHALLENGE}, {"Solo", GameMode::SOLO}});
GameMode mode = mode_keys.at(mode_it.first);
for (const auto& episode_it : mode_it.second->as_dict()) {
static const unordered_map<string, Episode> episode_keys(
static const std::unordered_map<std::string, Episode> episode_keys(
{{"Episode1", Episode::EP1}, {"Episode2", Episode::EP2}, {"Episode4", Episode::EP4}});
Episode episode = episode_keys.at(episode_it.first);
for (const auto& difficulty_it : episode_it.second->as_dict()) {
static const unordered_map<string, Difficulty> difficulty_keys(
static const std::unordered_map<std::string, Difficulty> difficulty_keys(
{{"Normal", Difficulty::NORMAL}, {"Hard", Difficulty::HARD}, {"VeryHard", Difficulty::VERY_HARD}, {"Ultimate", Difficulty::ULTIMATE}});
Difficulty difficulty = difficulty_keys.at(difficulty_it.first);
@@ -323,7 +319,7 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
auto& collection = this->collections[this->key_for_params(mode, episode, difficulty, section_id)];
for (const auto& [enemy_type_name, specs_json] : section_id_it.second->as_dict()) {
vector<ExpandedDrop>* target;
std::vector<ExpandedDrop>* target;
if (enemy_type_name.starts_with("Box-")) {
uint8_t area_norm = FloorDefinition::get(episode, enemy_type_name.substr(4)).drop_area_norm;
if (collection.box_specs.size() <= area_norm) {
@@ -343,7 +339,7 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
} else if (prob_desc.is_string()) {
auto tokens = phosg::split(prob_desc.as_string(), '/');
if (tokens.size() != 2) {
throw runtime_error("invalid probability specification");
throw std::runtime_error("invalid probability specification");
}
uint64_t numerator = stoull(tokens[0], nullptr, 0);
uint64_t denominator = stoull(tokens[1], nullptr, 0);
@@ -362,11 +358,11 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
d.data.data1[2] = item_code & 0xFF;
} else if (item_desc.is_string()) {
if (!name_index) {
throw runtime_error("item name index is not available");
throw std::runtime_error("item name index is not available");
}
d.data = name_index->parse_item_description(item_desc.as_string());
} else {
throw runtime_error("invalid item description type");
throw std::runtime_error("invalid item description type");
}
}
}
@@ -377,7 +373,7 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
}
std::string RareItemSet::serialize_afs(bool is_v1) const {
vector<string> files;
std::vector<std::string> files;
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
if (is_v1 && (difficulty == Difficulty::ULTIMATE)) {
continue;
@@ -391,16 +387,16 @@ std::string RareItemSet::serialize_afs(bool is_v1) const {
}
std::string RareItemSet::serialize_gsl(bool big_endian) const {
unordered_map<string, string> files;
std::unordered_map<std::string, std::string> files;
for (Episode episode : ALL_EPISODES_V3) {
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
for (uint8_t section_id = 0; section_id < 10; section_id++) {
try {
string filename = this->gsl_entry_name_for_table(GameMode::NORMAL, episode, difficulty, section_id);
std::string filename = this->gsl_entry_name_for_table(GameMode::NORMAL, episode, difficulty, section_id);
ParsedRELData rel(this->get_collection(GameMode::NORMAL, episode, difficulty, section_id));
files.emplace(filename, rel.serialize(big_endian, false));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
// Collection does not exist; skip it
}
}
@@ -410,10 +406,10 @@ std::string RareItemSet::serialize_gsl(bool big_endian) const {
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
for (uint8_t section_id = 0; section_id < 10; section_id++) {
try {
string filename = this->gsl_entry_name_for_table(GameMode::CHALLENGE, Episode::EP1, difficulty, section_id);
std::string filename = this->gsl_entry_name_for_table(GameMode::CHALLENGE, Episode::EP1, difficulty, section_id);
ParsedRELData rel(this->get_collection(GameMode::CHALLENGE, Episode::EP1, difficulty, section_id));
files.emplace(filename, rel.serialize(big_endian, false));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
// Collection does not exist; skip it
}
}
@@ -421,17 +417,17 @@ std::string RareItemSet::serialize_gsl(bool big_endian) const {
return GSLArchive::generate(files, big_endian);
}
string RareItemSet::serialize_html(
std::string RareItemSet::serialize_html(
GameMode mode,
Episode episode,
Difficulty difficulty,
shared_ptr<const ItemNameIndex> name_index,
shared_ptr<const CommonItemSet> common_item_set) const {
std::shared_ptr<const ItemNameIndex> name_index,
std::shared_ptr<const CommonItemSet> common_item_set) const {
struct ZoneTypes {
const char* name;
vector<uint8_t> floors;
vector<EnemyType> types;
std::vector<uint8_t> floors;
std::vector<EnemyType> types;
};
// clang-format off
@@ -561,7 +557,7 @@ string RareItemSet::serialize_html(
return (((r / 8) & 0xFF) << 16) | (((g / 8) & 0xFF) << 8) | ((b / 8) & 0xFF);
};
deque<string> blocks;
std::deque<std::string> blocks;
blocks.emplace_back(std::format("\
<html>\n\
<head>\n\
@@ -643,7 +639,7 @@ string RareItemSet::serialize_html(
</style>\n\
</head><body>\n");
string mode_token;
std::string mode_token;
switch (mode) {
case GameMode::NORMAL:
mode_token = "";
@@ -658,7 +654,7 @@ string RareItemSet::serialize_html(
mode_token = " (solo mode)";
break;
default:
throw logic_error("invalid game mode");
throw std::logic_error("invalid game mode");
}
blocks.emplace_back(std::format(
@@ -679,7 +675,7 @@ string RareItemSet::serialize_html(
blocks.emplace_back("</tr>");
};
auto add_specs_row = [&](const EnemyTypeDefinition* type_def, const char* loc_name, bool is_box, const array<vector<ExpandedDrop>, 10>& specs_lists) -> void {
auto add_specs_row = [&](const EnemyTypeDefinition* type_def, const char* loc_name, bool is_box, const std::array<std::vector<ExpandedDrop>, 10>& specs_lists) -> void {
bool any_list_nonempty = false;
for (const auto& specs_list : specs_lists) {
any_list_nonempty |= !specs_list.empty();
@@ -691,7 +687,7 @@ string RareItemSet::serialize_html(
blocks.emplace_back(std::format("<tr><td class=\"loc-{}\">{}</td>", is_box ? "box" : "enemy", loc_name));
for (uint8_t section_id = 0; section_id < 10; section_id++) {
blocks.emplace_back(std::format("<td class=\"sec{}-{}\">", section_id, is_box ? "box" : "enemy"));
vector<string> tokens;
std::vector<std::string> tokens;
for (const auto& spec : specs_lists[section_id]) {
if (!tokens.empty()) {
tokens.emplace_back("");
@@ -705,7 +701,7 @@ string RareItemSet::serialize_html(
uint8_t dar = 0;
try {
dar = table->enemy_type_drop_probs.at(type_def->type);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
exact_token += std::format(" (DAR: {}%)", dar);
frac.first *= dar;
@@ -729,12 +725,12 @@ string RareItemSet::serialize_html(
}
}
string hex = example_item.short_hex();
string desc = name_index->describe_item(example_item, ItemNameIndex::Flag::NAME_ONLY);
std::string hex = example_item.short_hex();
std::string desc = name_index->describe_item(example_item, ItemNameIndex::Flag::NAME_ONLY);
tokens.emplace_back(std::format("<span class=\"item\" title=\"Hex: {}\">{}</span>", hex, desc));
float denom = static_cast<float>(frac.second) / static_cast<double>(frac.first);
string denom_token = (floor(denom) == denom)
std::string denom_token = (floor(denom) == denom)
? std::format("1 / {:.0f}", denom)
: std::format("1 / {:.02f}", denom);
tokens.emplace_back(std::format("<span class=\"rate\" title=\"{}\">{}</span>", exact_token, denom_token));
@@ -751,7 +747,7 @@ string RareItemSet::serialize_html(
for (const auto& zone_type : zone_types) {
add_location_header(zone_type.name);
for (EnemyType type : zone_type.types) {
array<vector<ExpandedDrop>, 10> specs_lists;
std::array<std::vector<ExpandedDrop>, 10> specs_lists;
for (uint8_t section_id = 0; section_id < 10; section_id++) {
specs_lists[section_id] = this->get_enemy_specs(mode, episode, difficulty, section_id, type);
}
@@ -762,9 +758,9 @@ string RareItemSet::serialize_html(
for (uint8_t floor : zone_type.floors) {
const auto& floor_def = FloorDefinition::get(episode, floor);
if (floor_def.drop_area_norm == 0xFF) {
throw runtime_error("zone includes floors with no drop area");
throw std::runtime_error("zone includes floors with no drop area");
}
array<vector<ExpandedDrop>, 10> specs_lists;
std::array<std::vector<ExpandedDrop>, 10> specs_lists;
for (uint8_t section_id = 0; section_id < 10; section_id++) {
specs_lists[section_id] = this->get_box_specs(mode, episode, difficulty, section_id, floor_def.drop_area_norm);
}
@@ -777,7 +773,7 @@ string RareItemSet::serialize_html(
return phosg::join(blocks, "");
}
phosg::JSON RareItemSet::json(shared_ptr<const ItemNameIndex> name_index) const {
phosg::JSON RareItemSet::json(std::shared_ptr<const ItemNameIndex> name_index) const {
auto modes_dict = phosg::JSON::dict();
for (const auto& mode : ALL_GAME_MODES_V4) {
auto episodes_dict = phosg::JSON::dict();
@@ -853,12 +849,12 @@ void RareItemSet::multiply_all_rates(double factor) {
for (auto& [_, collection] : this->collections) {
for (auto& [_, specs] : collection.enemy_specs) {
for (auto& spec : specs) {
spec.probability = min<uint64_t>(spec.probability * factor, 0xFFFFFFFF);
spec.probability = std::min<uint64_t>(spec.probability * factor, 0xFFFFFFFF);
}
}
for (auto& specs : collection.box_specs) {
for (auto& spec : specs) {
spec.probability = min<uint64_t>(spec.probability * factor, 0xFFFFFFFF);
spec.probability = std::min<uint64_t>(spec.probability * factor, 0xFFFFFFFF);
}
}
}
@@ -870,11 +866,11 @@ void RareItemSet::print_collection(
Episode episode,
Difficulty difficulty,
uint8_t section_id,
shared_ptr<const ItemNameIndex> name_index) const {
std::shared_ptr<const ItemNameIndex> name_index) const {
const SpecCollection* collection;
try {
collection = &this->get_collection(mode, episode, difficulty, section_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return;
}
@@ -889,18 +885,17 @@ void RareItemSet::print_collection(
try {
const auto& def = type_definition_for_enemy(enemy_type);
for (const auto& spec : collection->enemy_specs.at(enemy_type)) {
string s = name_index ? spec.str(name_index) : spec.str();
phosg::fwrite_fmt(stream, " {:<23} {}\n", def.enum_name, s);
phosg::fwrite_fmt(stream, " {:<23} {}\n", def.enum_name, (name_index ? spec.str(name_index) : spec.str()));
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
phosg::fwrite_fmt(stream, " Box rares:\n");
for (size_t area_norm = 0; area_norm < collection->box_specs.size(); area_norm++) {
for (const auto& spec : collection->box_specs[area_norm]) {
string s = name_index ? spec.str(name_index) : spec.str();
phosg::fwrite_fmt(stream, " (area-norm {:02X}) {}\n", area_norm, s);
phosg::fwrite_fmt(stream, " (area-norm {:02X}) {}\n",
area_norm, (name_index ? spec.str(name_index) : spec.str()));
}
}
}
@@ -912,7 +907,7 @@ void RareItemSet::print_all_collections(FILE* stream, std::shared_ptr<const Item
for (uint8_t section_id = 0; section_id < 10; section_id++) {
try {
this->print_collection(stream, mode, episode, difficulty, section_id, name_index);
} catch (const out_of_range& e) {
} catch (const std::out_of_range& e) {
}
}
}
@@ -938,11 +933,11 @@ void RareItemSet::SpecCollection::print_diff(FILE* stream, const SpecCollection&
const std::vector<ExpandedDrop>* other_specs = &empty_specs;
try {
this_specs = &this->enemy_specs.at(enemy_type);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
other_specs = &other.enemy_specs.at(enemy_type);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (*this_specs != *other_specs) {
phosg::fwrite_fmt(stream, " {}: {} -> {}\n",
@@ -969,11 +964,11 @@ void RareItemSet::print_diff(FILE* stream, const RareItemSet& other) const {
const SpecCollection* other_coll = nullptr;
try {
this_coll = &this->get_collection(mode, episode, difficulty, section_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
other_coll = &other.get_collection(mode, episode, difficulty, section_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (!this_coll && !other_coll) {
@@ -1014,7 +1009,7 @@ std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_enemy_specs(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, EnemyType enemy_type) const {
try {
return this->get_collection(mode, episode, difficulty, secid).enemy_specs.at(enemy_type);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
static const std::vector<ExpandedDrop> empty_vector;
return empty_vector;
}
@@ -1024,7 +1019,7 @@ std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_box_specs(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t area_norm) const {
try {
return this->get_collection(mode, episode, difficulty, secid).box_specs.at(area_norm);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
static const std::vector<ExpandedDrop> empty_vector;
return empty_vector;
}
@@ -1043,7 +1038,7 @@ const RareItemSet::SpecCollection& RareItemSet::get_collection(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) const {
try {
return this->collections.at(this->key_for_params(mode, episode, difficulty, secid));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
if (mode == GameMode::BATTLE || mode == GameMode::SOLO) {
return this->collections.at(this->key_for_params(GameMode::NORMAL, episode, difficulty, secid));
}
@@ -1053,10 +1048,10 @@ const RareItemSet::SpecCollection& RareItemSet::get_collection(
uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) {
if (static_cast<size_t>(difficulty) > 3) {
throw logic_error("incorrect difficulty");
throw std::logic_error("incorrect difficulty");
}
if (secid > 10) {
throw logic_error("incorrect section id");
throw std::logic_error("incorrect section id");
}
uint16_t key = ((static_cast<size_t>(difficulty) & 3) << 4) | (secid & 0x0F);
@@ -1073,7 +1068,7 @@ uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, Difficulty
key |= 0x00C0;
break;
default:
throw logic_error("invalid episode in RareItemSet");
throw std::logic_error("invalid episode in RareItemSet");
}
switch (episode) {
case Episode::EP1:
@@ -1085,7 +1080,7 @@ uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, Difficulty
key |= 0x0200;
break;
default:
throw logic_error("invalid episode in RareItemSet");
throw std::logic_error("invalid episode in RareItemSet");
}
return key;
}
+424 -436
View File
File diff suppressed because it is too large Load Diff
+393 -395
View File
File diff suppressed because it is too large Load Diff
+69 -77
View File
@@ -8,10 +8,8 @@
#include "Loggers.hh"
#include "Server.hh"
using namespace std;
static string encode_chat_message(Version version, const string& message) {
string encoded_message;
static std::string encode_chat_message(Version version, const std::string& message) {
std::string encoded_message;
encoded_message.resize(8, 0);
encoded_message += uses_utf16(version) ? tt_utf8_to_utf16("\tE" + message) : tt_utf8_to_ascii("\tE" + message);
encoded_message.resize((encoded_message.size() + 3) & (~3));
@@ -19,14 +17,10 @@ static string encode_chat_message(Version version, const string& message) {
}
ReplaySession::Event::Event(Type type, uint64_t client_id, size_t line_num)
: type(type),
client_id(client_id),
allow_size_disparity(false),
complete(false),
line_num(line_num) {}
: type(type), client_id(client_id), allow_size_disparity(false), complete(false), line_num(line_num) {}
string ReplaySession::Event::str() const {
string ret;
std::string ReplaySession::Event::str() const {
std::string ret;
if (this->type == Type::CONNECT) {
ret = std::format("Event[{}, CONNECT", this->client_id);
} else if (this->type == Type::DISCONNECT) {
@@ -46,11 +40,11 @@ string ReplaySession::Event::str() const {
return ret;
}
ReplaySession::Client::Client(shared_ptr<asio::io_context> io_context, uint64_t id, uint16_t port, Version version)
ReplaySession::Client::Client(std::shared_ptr<asio::io_context> io_context, uint64_t id, uint16_t port, Version version)
: id(id),
port(port),
version(version),
channel(make_shared<PeerChannel>(
channel(std::make_shared<PeerChannel>(
io_context,
this->version,
Language::ENGLISH,
@@ -60,12 +54,12 @@ ReplaySession::Client::Client(shared_ptr<asio::io_context> io_context, uint64_t
false,
false)) {}
string ReplaySession::Client::str() const {
std::string ReplaySession::Client::str() const {
return std::format("Client[{}, T-{}, {}]", this->id, this->port, phosg::name_for_enum(this->version));
}
shared_ptr<ReplaySession::Event> ReplaySession::create_event(Event::Type type, shared_ptr<Client> c, size_t line_num) {
auto event = make_shared<Event>(type, c->id, line_num);
std::shared_ptr<ReplaySession::Event> ReplaySession::create_event(Event::Type type, std::shared_ptr<Client> c, size_t line_num) {
auto event = std::make_shared<Event>(type, c->id, line_num);
if (!this->last_event.get()) {
this->first_event = event;
} else {
@@ -78,7 +72,7 @@ shared_ptr<ReplaySession::Event> ReplaySession::create_event(Event::Type type, s
return event;
}
void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
void ReplaySession::apply_default_mask(std::shared_ptr<Event> ev) {
auto version = this->clients.at(ev->client_id)->version;
void* cmd_data = ev->data.data() + ((version == Version::BB_V4) ? 8 : 4);
@@ -325,11 +319,11 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
break;
}
default:
throw logic_error("invalid game version");
throw std::logic_error("invalid game version");
}
}
ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
ReplaySession::ReplaySession(std::shared_ptr<ServerState> state, FILE* input_log)
: state(state),
prev_psov2_crypt_enabled(this->state->use_psov2_rand_crypt),
commands_sent(0),
@@ -338,13 +332,13 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
bytes_received(0),
idle_timeout_timer(*this->state->io_context),
run_failed(false) {
shared_ptr<Event> parsing_command = nullptr;
std::shared_ptr<Event> parsing_command = nullptr;
size_t line_num = 0;
size_t num_events = 0;
while (!feof(input_log)) {
line_num++;
string line = phosg::fgets(input_log);
std::string line = phosg::fgets(input_log);
if (line.ends_with("\n")) {
line.resize(line.size() - 1);
}
@@ -353,11 +347,11 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
}
if (parsing_command.get()) {
string expected_start = std::format("{:04X} |", parsing_command->data.size());
std::string expected_start = std::format("{:04X} |", parsing_command->data.size());
if (line.starts_with(expected_start)) {
// Parse out the hex part of the hex/ASCII dump
string mask_bytes;
string data_bytes = phosg::parse_data_string(line.substr(expected_start.size(), 16 * 3 + 1), &mask_bytes);
std::string mask_bytes;
std::string data_bytes = phosg::parse_data_string(line.substr(expected_start.size(), 16 * 3 + 1), &mask_bytes);
parsing_command->data += data_bytes;
parsing_command->mask += mask_bytes;
continue;
@@ -378,35 +372,35 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
if (line.starts_with("### cc ")) {
// ### cc $<chat command>
if (this->clients.size() != 1) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"(ev-line {}) cc shortcut cannot be used with multiple clients connected; use on C-X cc instead",
line_num));
}
shared_ptr<Event> event;
std::shared_ptr<Event> event;
try {
auto c = this->clients.begin()->second;
event = this->create_event(Event::Type::SEND, c, line_num);
event->data = encode_chat_message(c->version, line.substr(7));
num_events++;
} catch (const exception& e) {
throw runtime_error(std::format("(ev-line {}) failed to generate chat message ({})", line_num, e.what()));
} catch (const std::exception& e) {
throw std::runtime_error(std::format("(ev-line {}) failed to generate chat message ({})", line_num, e.what()));
}
continue;
} else if (line.starts_with("### on C-")) {
// ### on C-{} cc <chat command>
shared_ptr<Event> event;
std::shared_ptr<Event> event;
try {
size_t end_offset;
auto c = this->clients.at(stoull(line.substr(9), &end_offset, 16));
if (line.compare(end_offset + 9, 4, " cc ") != 0) {
throw runtime_error("malformed `on C-X cc $...` shortcut command");
throw std::runtime_error("malformed `on C-X cc $...` shortcut command");
}
event = this->create_event(Event::Type::SEND, c, line_num);
event->data = encode_chat_message(c->version, line.substr(end_offset + 13));
num_events++;
} catch (const exception& e) {
throw runtime_error(std::format("(ev-line {}) failed to generate chat message ({})", line_num, e.what()));
} catch (const std::exception& e) {
throw std::runtime_error(std::format("(ev-line {}) failed to generate chat message ({})", line_num, e.what()));
}
continue;
@@ -414,25 +408,25 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
// I <pid/ts> - [GameServer] Client connected: C-1 via TG-9000-GC_V3-gc-jp10-game_server
// I <pid/ts> - [GameServer] Client connected: C-3 via TSI-9000-GC_V3-game_server
size_t offset = line.find(" - [GameServer] Client connected: C-");
if (offset != string::npos) {
if (offset != std::string::npos) {
auto tokens = phosg::split(line, ' ');
if (!tokens[8].starts_with("C-")) {
throw runtime_error(std::format("(ev-line {}) client connection message missing client ID token", line_num));
throw std::runtime_error(std::format("(ev-line {}) client connection message missing client ID token", line_num));
}
uint64_t client_id = stoull(tokens[8].substr(2), nullptr, 16);
auto listen_tokens = phosg::split(tokens[10], '-');
if (listen_tokens.size() < 4) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"(ev-line {}) client connection message listening socket token format is incorrect", line_num));
}
uint16_t port = stoul(listen_tokens[1], nullptr, 10);
Version version = phosg::enum_for_name<Version>(listen_tokens[2]);
auto c = make_shared<Client>(state->io_context, client_id, port, version);
auto c = std::make_shared<Client>(state->io_context, client_id, port, version);
if (!this->clients.emplace(c->id, c).second) {
throw runtime_error(std::format("(ev-line {}) duplicate client ID in input log", line_num));
throw std::runtime_error(std::format("(ev-line {}) duplicate client ID in input log", line_num));
}
this->create_event(Event::Type::CONNECT, c, line_num);
num_events++;
@@ -441,24 +435,24 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
// I <pid/ts> - [GameServer] Running cleanup tasks for C-{}
offset = line.find(" - [GameServer] Running cleanup tasks for C-");
if (offset != string::npos) {
if (offset != std::string::npos) {
auto tokens = phosg::split(line, ' ');
if (tokens.size() < 11) {
throw runtime_error(std::format("(ev-line {}) client disconnection message has incorrect token count", line_num));
throw std::runtime_error(std::format("(ev-line {}) client disconnection message has incorrect token count", line_num));
}
if (!tokens[10].starts_with("C-")) {
throw runtime_error(std::format("(ev-line {}) client disconnection message missing client ID token", line_num));
throw std::runtime_error(std::format("(ev-line {}) client disconnection message missing client ID token", line_num));
}
uint64_t client_id = stoul(tokens[10].substr(2), nullptr, 16);
try {
auto& c = this->clients.at(client_id);
if (c->disconnect_event.get()) {
throw runtime_error(std::format("(ev-line {}) client has multiple disconnect events", line_num));
throw std::runtime_error(std::format("(ev-line {}) client has multiple disconnect events", line_num));
}
c->disconnect_event = this->create_event(Event::Type::DISCONNECT, c, line_num);
num_events++;
} catch (const out_of_range&) {
throw runtime_error(std::format("(ev-line {}) unknown disconnecting client ID in input log", line_num));
} catch (const std::out_of_range&) {
throw std::runtime_error(std::format("(ev-line {}) unknown disconnecting client ID in input log", line_num));
}
continue;
}
@@ -466,13 +460,13 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
// I <pid/ts> - [Commands] Sending to C-{:X} (...)
// I <pid/ts> - [Commands] Received from C-{:X} (...)
offset = line.find(" - [Commands] Sending to C-");
if (offset == string::npos) {
if (offset == std::string::npos) {
offset = line.find(" - [Commands] Received from C-");
}
if (offset != string::npos) {
if (offset != std::string::npos) {
auto tokens = phosg::split(line, ' ');
if (tokens.size() < 10) {
throw runtime_error(std::format("(ev-line {}) command header line too short", line_num));
throw std::runtime_error(std::format("(ev-line {}) command header line too short", line_num));
}
bool from_client = (tokens[6] == "Received");
uint64_t client_id = stoull(tokens[8].substr(2), nullptr, 16);
@@ -480,8 +474,8 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
parsing_command = this->create_event(
from_client ? Event::Type::SEND : Event::Type::RECEIVE, this->clients.at(client_id), line_num);
num_events++;
} catch (const out_of_range&) {
throw runtime_error(std::format("(ev-line {}) input log contains command for missing client", line_num));
} catch (const std::out_of_range&) {
throw std::runtime_error(std::format("(ev-line {}) input log contains command for missing client", line_num));
}
continue;
}
@@ -490,14 +484,12 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
replay_log.debug_f("{} clients in log", this->clients.size());
for (const auto& it : this->clients) {
string client_str = it.second->str();
replay_log.debug_f(" {} => {}", it.first, client_str);
replay_log.debug_f(" {} => {}", it.first, it.second->str());
}
replay_log.debug_f("{} events in replay log", num_events);
for (auto ev = this->first_event; ev != nullptr; ev = ev->next_event) {
string ev_str = ev->str();
replay_log.debug_f(" {}", ev_str);
replay_log.debug_f(" {}", ev->str());
}
}
@@ -512,25 +504,25 @@ asio::awaitable<void> ReplaySession::run() {
switch (this->first_event->type) {
case Event::Type::CONNECT: {
if (c->channel->connected()) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"(ev-line {}) connect event on already-connected client", this->first_event->line_num));
}
shared_ptr<const PortConfiguration> port_config;
std::shared_ptr<const PortConfiguration> port_config;
try {
port_config = this->state->number_to_port_config.at(c->port);
} catch (const out_of_range&) {
throw runtime_error(std::format(
} catch (const std::out_of_range&) {
throw std::runtime_error(std::format(
"(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, "", phosg::TerminalFormat::END, phosg::TerminalFormat::END, false, false);
auto server_channel = std::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);
if (this->state->game_server.get()) {
this->state->game_server->connect_channel(server_channel, c->port, port_config->behavior);
} else {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"(ev-line {}) no server available for connection", this->first_event->line_num));
}
break;
@@ -542,7 +534,7 @@ asio::awaitable<void> ReplaySession::run() {
case Event::Type::SEND:
if (!c->channel->connected()) {
throw runtime_error(std::format(
throw std::runtime_error(std::format(
"(ev-line {}) send event attempted on unconnected client", this->first_event->line_num));
}
c->channel->send(this->first_event->data);
@@ -552,25 +544,25 @@ asio::awaitable<void> ReplaySession::run() {
case Event::Type::RECEIVE: {
if (!c->channel->connected()) {
throw runtime_error(std::format("(ev-line {}) receive event on non-connected client",
throw std::runtime_error(std::format("(ev-line {}) receive event on non-connected client",
this->first_event->line_num));
}
if (c->receive_events.front() != this->first_event) {
throw logic_error("Client receive events are out of order");
throw std::logic_error("Client receive events are out of order");
}
this->reschedule_idle_timeout();
auto msg = co_await c->channel->recv();
// TODO: Use the iovec form of phosg::print_data here instead of prepend_command_header (which copies data)
string full_command = prepend_command_header(
std::string full_command = prepend_command_header(
c->version, (c->channel->crypt_in.get() != nullptr), msg.command, msg.flag, msg.data);
this->commands_received++;
this->bytes_received += full_command.size();
if (c->receive_events.empty()) {
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 std::runtime_error("received unexpected command for client");
}
auto& ev = c->receive_events.front();
@@ -579,15 +571,15 @@ asio::awaitable<void> ReplaySession::run() {
phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
replay_log.error_f("Received command:");
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 std::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 < std::min<size_t>(full_command.size(), ev->data.size()); x++) {
if ((full_command[x] & ev->mask[x]) != (ev->data[x] & ev->mask[x])) {
replay_log.error_f("Expected command:");
phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
replay_log.error_f("Received command:");
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 std::runtime_error(std::format("(ev-line {}) received command data does not match expected data", ev->line_num));
}
}
@@ -600,8 +592,8 @@ asio::awaitable<void> ReplaySession::run() {
case Version::BB_PATCH:
if (msg.command == 0x02) {
auto& cmd = msg.check_size_t<S_ServerInit_Patch_02>();
c->channel->crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
c->channel->crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
c->channel->crypt_in = std::make_shared<PSOV2Encryption>(cmd.server_key);
c->channel->crypt_out = std::make_shared<PSOV2Encryption>(cmd.client_key);
}
break;
case Version::DC_NTE:
@@ -618,11 +610,11 @@ asio::awaitable<void> ReplaySession::run() {
if (msg.command == 0x02 || msg.command == 0x17 || msg.command == 0x91 || msg.command == 0x9B) {
auto& cmd = msg.check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(0xFFFF);
if (is_v1_or_v2(c->version)) {
c->channel->crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
c->channel->crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
c->channel->crypt_in = std::make_shared<PSOV2Encryption>(cmd.server_key);
c->channel->crypt_out = std::make_shared<PSOV2Encryption>(cmd.client_key);
} else { // V3
c->channel->crypt_in = make_shared<PSOV3Encryption>(cmd.server_key);
c->channel->crypt_out = make_shared<PSOV3Encryption>(cmd.client_key);
c->channel->crypt_in = std::make_shared<PSOV3Encryption>(cmd.server_key);
c->channel->crypt_out = std::make_shared<PSOV3Encryption>(cmd.client_key);
}
}
break;
@@ -631,19 +623,19 @@ asio::awaitable<void> ReplaySession::run() {
auto& cmd = msg.check_size_t<S_ServerInitDefault_BB_03_9B>(0xFFFF);
// TODO: At some point it may matter which BB private key file we use. Don't just blindly use the
// first one here.
c->channel->crypt_in = make_shared<PSOBBEncryption>(
c->channel->crypt_in = std::make_shared<PSOBBEncryption>(
*this->state->bb_private_keys[0], cmd.server_key.data(), cmd.server_key.size());
c->channel->crypt_out = make_shared<PSOBBEncryption>(
c->channel->crypt_out = std::make_shared<PSOBBEncryption>(
*this->state->bb_private_keys[0], cmd.client_key.data(), cmd.client_key.size());
}
break;
default:
throw logic_error("unsupported encryption version");
throw std::logic_error("unsupported encryption version");
}
break;
}
default:
throw logic_error("unhandled event type");
throw std::logic_error("unhandled event type");
}
this->first_event->complete = true;
}
@@ -653,7 +645,7 @@ asio::awaitable<void> ReplaySession::run() {
this->last_event = nullptr;
}
}
} catch (const exception& e) {
} catch (const std::exception& e) {
replay_log.error_f("Replay failed: {}", e.what());
if (this->first_event) {
replay_log.error_f("Next pending event: {}", this->first_event->str());
+59 -60
View File
@@ -7,13 +7,11 @@
#include "LevelTable.hh"
#include "PSOProtocol.hh"
using namespace std;
struct DefaultSymbolChatEntry {
array<const char*, 8> language_to_name;
std::array<const char*, 8> language_to_name;
uint32_t spec;
array<uint16_t, 4> corner_objects;
array<SymbolChatFacePart, 12> face_parts;
std::array<uint16_t, 4> corner_objects;
std::array<SymbolChatFacePart, 12> face_parts;
SaveFileSymbolChatEntryBB to_entry(Language language) const {
SaveFileSymbolChatEntryBB ret;
@@ -30,7 +28,7 @@ struct DefaultSymbolChatEntry {
}
};
static const array<DefaultSymbolChatEntry, 6> DEFAULT_SYMBOL_CHATS = {
static const std::array<DefaultSymbolChatEntry, 6> DEFAULT_SYMBOL_CHATS = {
DefaultSymbolChatEntry{{"\tJ\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF", "\tEHello", "\tEHallo", "\tESalut", "\tEHola", "\tB\xE4\xBD\xA0\xE5\xA5\xBD", "\tT\xE4\xBD\xA0\xE5\xA5\xBD", "\tK\xEC\x95\x88\xEB\x85\x95"}, 0x28, {0xFFFF, 0x000D, 0xFFFF, 0xFFFF}, {SymbolChatFacePart{0x05, 0x18, 0x1D, 0x00}, {0x05, 0x28, 0x1D, 0x01}, {0x36, 0x20, 0x2A, 0x00}, {0x3C, 0x00, 0x32, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}}},
DefaultSymbolChatEntry{{"\tJ\xE3\x81\x95\xE3\x82\x88\xE3\x81\x86\xE3\x81\xAA\xE3\x82\x89", "\tEGood-bye", "\tETschus", "\tEAu revoir", "\tEAdios", "\tB\xE5\x86\x8D\xE8\xA7\x81", "\tT\xE5\x86\x8D\xE8\xA6\x8B", "\tK\xEC\x9E\x98\xEA\xB0\x80"}, 0x74, {0x0476, 0x000C, 0xFFFF, 0xFFFF}, {SymbolChatFacePart{0x06, 0x15, 0x14, 0x00}, {0x06, 0x2B, 0x14, 0x01}, {0x05, 0x18, 0x1F, 0x00}, {0x05, 0x28, 0x1F, 0x01}, {0x36, 0x20, 0x2A, 0x00}, {0x3C, 0x00, 0x32, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}}},
DefaultSymbolChatEntry{{"\tJ\xE3\x81\xB0\xE3\x82\x93\xE3\x81\x96\xE3\x83\xBC\xE3\x81\x84", "\tEHurrah!", "\tEHurra!", "\tEHourra !", "\tEHurra", "\tB\xE4\xB8\x87\xE5\xB2\x81", "\tT\xE8\x90\xAC\xE6\xAD\xB2", "\tK\xEB\xA7\x8C\xEC\x84\xB8"}, 0x28, {0x0362, 0x0362, 0xFFFF, 0xFFFF}, {SymbolChatFacePart{0x09, 0x16, 0x1B, 0x00}, {0x09, 0x2B, 0x1B, 0x01}, {0x37, 0x20, 0x2C, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}}},
@@ -39,11 +37,11 @@ static const array<DefaultSymbolChatEntry, 6> DEFAULT_SYMBOL_CHATS = {
DefaultSymbolChatEntry{{"\tJ\xE3\x81\x9F\xE3\x81\x99\xE3\x81\x91\xE3\x81\xA6\xEF\xBC\x81", "\tEHelp me!", "\tEHilf mir!", "\tEAide-moi !", "\tEAyuda", "\tB\xE6\x95\x91\xE5\x91\xBD\xE5\x95\x8A\xEF\xBC\x81", "\tT\xE6\x95\x91\xE5\x91\xBD\xE5\x95\x8A\xEF\xBC\x81", "\tK\xEB\x8F\x84\xEC\x99\x80\xEC\xA4\x98\xEF\xBC\x81"}, 0xEC, {0x065E, 0x0138, 0xFFFF, 0xFFFF}, {SymbolChatFacePart{0x02, 0x17, 0x1B, 0x01}, {0x02, 0x2A, 0x1B, 0x00}, {0x31, 0x20, 0x2C, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}}},
};
static const array<uint16_t, 20> DEFAULT_TECH_MENU_CONFIG = {
static const std::array<uint16_t, 20> DEFAULT_TECH_MENU_CONFIG = {
0x0000, 0x0006, 0x0003, 0x0001, 0x0007, 0x0004, 0x0002, 0x0008, 0x0005, 0x0009,
0x0012, 0x000F, 0x0010, 0x0011, 0x000D, 0x000A, 0x000B, 0x000C, 0x000E, 0x0000};
static const array<uint8_t, 0x016C> DEFAULT_KEY_CONFIG = {
static const std::array<uint8_t, 0x016C> DEFAULT_KEY_CONFIG = {
0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -68,7 +66,7 @@ static const array<uint8_t, 0x016C> DEFAULT_KEY_CONFIG = {
0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
static const array<uint8_t, 0x0038> DEFAULT_JOYSTICK_CONFIG = {
static const std::array<uint8_t, 0x0038> DEFAULT_JOYSTICK_CONFIG = {
0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
@@ -88,7 +86,7 @@ ShuffleTables::ShuffleTables(PSOV2Encryption& crypt) {
while (r28 >= 0) {
uint32_t r3 = this->pseudorand(crypt, r28 + 1);
if (r3 >= 0x100) {
throw logic_error("bad r3");
throw std::logic_error("bad r3");
}
uint8_t t = this->forward_table[r3];
this->forward_table[r3] = *r31;
@@ -144,7 +142,7 @@ bool PSOVMSFileHeader::checksum_correct() const {
void PSOVMSFileHeader::check() const {
if (!this->checksum_correct()) {
throw runtime_error("VMS file unencrypted header checksum is incorrect");
throw std::runtime_error("VMS file unencrypted header checksum is incorrect");
}
}
@@ -165,19 +163,19 @@ bool PSOGCIFileHeader::checksum_correct() const {
void PSOGCIFileHeader::check() const {
if (!this->checksum_correct()) {
throw runtime_error("GCI file unencrypted header checksum is incorrect");
throw std::runtime_error("GCI file unencrypted header checksum is incorrect");
}
if (this->developer_id[0] != '8' || this->developer_id[1] != 'P') {
throw runtime_error("GCI file is not for a Sega game");
throw std::runtime_error("GCI file is not for a Sega game");
}
if ((this->game_id[0] != 'G') && (this->game_id[0] != 'D')) {
throw runtime_error("GCI file is not for a GameCube game");
throw std::runtime_error("GCI file is not for a GameCube game");
}
if (this->game_id[1] != 'P') {
throw runtime_error("GCI file is not for Phantasy Star Online");
throw std::runtime_error("GCI file is not for Phantasy Star Online");
}
if ((this->game_id[2] != 'S') && (this->game_id[2] != 'O')) {
throw runtime_error("GCI file is not for Phantasy Star Online");
throw std::runtime_error("GCI file is not for Phantasy Star Online");
}
}
@@ -215,10 +213,10 @@ phosg::ImageRGB888 PSOGCSnapshotFile::decode_image() const {
size_t width = this->width ? this->width.load() : 256;
size_t height = this->height ? this->height.load() : 192;
if (width != 256) {
throw runtime_error("width is incorrect");
throw std::runtime_error("width is incorrect");
}
if (height != 192) {
throw runtime_error("height is incorrect");
throw std::runtime_error("height is incorrect");
}
// 4x4 blocks of pixels
@@ -307,7 +305,7 @@ bool PSOXBFileHeader::checksum_correct() const {
void PSOXBFileHeader::check() const {
if (!this->checksum_correct()) {
throw runtime_error("Xbox file intermediate header checksum is incorrect");
throw std::runtime_error("Xbox file intermediate header checksum is incorrect");
}
}
@@ -322,7 +320,7 @@ uint32_t PSOBBGuildCardFile::checksum() const {
void PSOBBGuildCardFile::delete_duplicates() {
{
unordered_set<uint32_t> seen;
std::unordered_set<uint32_t> seen;
size_t read_index = 0, write_index = 0;
for (read_index = 0; read_index < this->blocked_senders.size(); read_index++) {
const auto& read_blocked_senders = this->blocked_senders[read_index];
@@ -339,7 +337,7 @@ void PSOBBGuildCardFile::delete_duplicates() {
}
{
unordered_set<uint32_t> seen;
std::unordered_set<uint32_t> seen;
size_t read_index = 0, write_index = 0;
for (read_index = 0; read_index < this->entries.size(); read_index++) {
const auto& read_entry = this->entries[read_index];
@@ -377,13 +375,13 @@ PlayerDispDataBBPreview PSOBBCharacterFile::to_preview() const {
return pre;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
uint32_t guild_card_number,
Language language,
const PlayerVisualConfig& visual,
const std::string& name,
shared_ptr<const LevelTable> level_table) {
static const array<array<PlayerInventoryItem, 5>, 12> initial_inventory{{
std::shared_ptr<const LevelTable> level_table) {
static const std::array<std::array<PlayerInventoryItem, 5>, 12> initial_inventory{{
{
PlayerInventoryItem(ItemData(0x0001000000000000, 0x0000000000000000), true),
PlayerInventoryItem(ItemData(0x0101000000000000, 0x0000000000000000), true),
@@ -470,7 +468,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
},
}};
static const array<uint8_t, 0xE8> config_hunter_ranger{
static const std::array<uint8_t, 0xE8> config_hunter_ranger{
{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -486,7 +484,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static const array<uint8_t, 0xE8> config_force{
static const std::array<uint8_t, 0xE8> config_force{
{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -503,7 +501,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
auto ret = make_shared<PSOBBCharacterFile>();
auto ret = std::make_shared<PSOBBCharacterFile>();
ret->disp.visual = visual;
ret->disp.name.encode(name, language);
@@ -514,7 +512,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
}
// Set mag color based on initial costume
static const array<array<uint8_t, 25>, 12> mag_colors = {{
static const std::array<std::array<uint8_t, 25>, 12> mag_colors = {{
{0x09, 0x01, 0x02, 0x11, 0x0A, 0x05, 0x06, 0x0B, 0x05, 0x00, 0x07, 0x0B, 0x0C, 0x04, 0x05, 0x06, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x01, 0x02, 0x11, 0x04, 0x05, 0x06, 0x08, 0x11, 0x0D, 0x01, 0x02, 0x0C, 0x04, 0x05, 0x06, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x01, 0x02, 0x11, 0x04, 0x0E, 0x06, 0x01, 0x0E, 0x09, 0x07, 0x02, 0x11, 0x04, 0x05, 0x06, 0x04, 0x11, 0x0D, 0x01, 0x0B, 0x11, 0x0D, 0x05, 0x06},
@@ -567,16 +565,16 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
uint32_t guild_card_number,
Language language,
const PlayerDispDataBBPreview& preview,
shared_ptr<const LevelTable> level_table) {
std::shared_ptr<const LevelTable> level_table) {
return PSOBBCharacterFile::create_from_config(
guild_card_number, language, preview.visual, preview.name.decode(language), level_table);
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCNTECharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCNTECharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
Language::JAPANESE,
@@ -609,7 +607,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCN
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODC112000CharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODC112000CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -652,7 +650,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODC1
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV1CharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV1CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -685,7 +683,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV2CharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV2CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -725,7 +723,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCNTECharacterFileCharacter& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCNTECharacterFileCharacter& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -765,7 +763,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCN
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCCharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCCharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -815,7 +813,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCC
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCEp3CharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCEp3CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -863,7 +861,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCE
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOXBCharacterFile::Character& src) {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOXBCharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
@@ -1178,24 +1176,24 @@ PSOBBCharacterFile::operator PSOXBCharacterFile::Character() const {
return ret;
}
PSOCHARFile::LoadSharedResult PSOCHARFile::load_shared(const string& filename, bool load_system) {
PSOCHARFile::LoadSharedResult PSOCHARFile::load_shared(const std::string& filename, bool load_system) {
auto f = phosg::fopen_unique(filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
throw std::runtime_error("incorrect size in character file header");
}
if (header.command != 0x00E7) {
throw runtime_error("incorrect command in character file header");
throw std::runtime_error("incorrect command in character file header");
}
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
throw std::runtime_error("incorrect flag in character file header");
}
static_assert(sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBFullTeamMembership) == 0x3994, ".psochar size is incorrect");
LoadSharedResult ret;
ret.character_file = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
ret.character_file = std::make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
if (load_system) {
ret.system_file = make_shared<PSOBBBaseSystemFile>(phosg::freadx<PSOBBBaseSystemFile>(f.get()));
ret.system_file = std::make_shared<PSOBBBaseSystemFile>(phosg::freadx<PSOBBBaseSystemFile>(f.get()));
}
return ret;
}
@@ -1245,7 +1243,7 @@ void PSOBBCharacterFile::add_item(const ItemData& item, const ItemData::StackLim
if (y < this->inventory.num_items) {
size_t new_stack_size = this->inventory.items[y].data.data1[5] + item.data1[5];
if (new_stack_size > combine_max) {
throw out_of_range("stack is too large");
throw std::out_of_range("stack is too large");
}
this->inventory.items[y].data.data1[5] = new_stack_size;
return;
@@ -1254,7 +1252,7 @@ void PSOBBCharacterFile::add_item(const ItemData& item, const ItemData::StackLim
// If we get here, then it's not meseta and not a combine item, so it needs to go into an empty inventory slot
if (this->inventory.num_items >= 30) {
throw out_of_range("inventory is full");
throw std::out_of_range("inventory is full");
}
auto& inv_item = this->inventory.items[this->inventory.num_items];
inv_item.state = 1;
@@ -1285,7 +1283,7 @@ ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, cons
// if amount is nonzero.
if (amount && (inventory_item.data.stack_size(limits) > 1) && (amount < inventory_item.data.data1[5])) {
if (is_equipped) {
throw runtime_error("character has a combine item equipped");
throw std::runtime_error("character has a combine item equipped");
}
ret = inventory_item.data;
ret.data1[5] = amount;
@@ -1318,7 +1316,7 @@ ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, cons
}
void PSOBBCharacterFile::add_meseta(uint32_t amount) {
this->disp.stats.meseta = min<size_t>(static_cast<size_t>(this->disp.stats.meseta) + amount, 999999);
this->disp.stats.meseta = std::min<size_t>(static_cast<size_t>(this->disp.stats.meseta) + amount, 999999);
}
void PSOBBCharacterFile::remove_meseta(uint32_t amount, bool allow_overdraft) {
@@ -1327,7 +1325,7 @@ void PSOBBCharacterFile::remove_meseta(uint32_t amount, bool allow_overdraft) {
} else if (allow_overdraft) {
this->disp.stats.meseta = 0;
} else {
throw out_of_range("player does not have enough meseta");
throw std::out_of_range("player does not have enough meseta");
}
}
@@ -1363,7 +1361,7 @@ uint8_t PSOBBCharacterFile::get_material_usage(MaterialType which) const {
case MaterialType::LUCK:
return this->inventory.items[8 + static_cast<uint8_t>(which)].extension_data2;
default:
throw logic_error("invalid material type");
throw std::logic_error("invalid material type");
}
}
@@ -1383,7 +1381,7 @@ void PSOBBCharacterFile::set_material_usage(MaterialType which, uint8_t usage) {
this->inventory.items[8 + static_cast<uint8_t>(which)].extension_data2 = usage;
break;
default:
throw logic_error("invalid material type");
throw std::logic_error("invalid material type");
}
}
@@ -1490,29 +1488,30 @@ static uint16_t crc16(const void* data, size_t size) {
return ret ^ 0xFFFF;
}
string encode_psobb_hangame_credentials(const string& user_id, const string& token, const string& unused) {
std::string encode_psobb_hangame_credentials(
const std::string& user_id, const std::string& token, const std::string& unused) {
if (user_id.size() < 4) {
throw runtime_error("user_id must be at least 4 characters");
throw std::runtime_error("user_id must be at least 4 characters");
}
if (user_id.size() > 12) {
throw runtime_error("user_id must be at most 12 characters");
throw std::runtime_error("user_id must be at most 12 characters");
}
if (!user_id.ends_with("@HG")) {
throw runtime_error("user_id must end with \"@HG\"");
throw std::runtime_error("user_id must end with \"@HG\"");
}
if (token.empty()) {
throw runtime_error("token must not be empty");
throw std::runtime_error("token must not be empty");
}
if (token.size() > 8) {
throw runtime_error("token must be at most 8 characters");
throw std::runtime_error("token must be at most 8 characters");
}
for (char ch : token) {
if (!isdigit(ch)) {
throw runtime_error("token must contain only decimal digits");
throw std::runtime_error("token must contain only decimal digits");
}
}
if (unused.size() > 0xFF) {
throw runtime_error("unused must be at most 255 characters");
throw std::runtime_error("unused must be at most 255 characters");
}
// The encoded format is:
@@ -1526,7 +1525,7 @@ string encode_psobb_hangame_credentials(const string& user_id, const string& tok
// uint8_t unused_size;
// char unused[unused_size]; // Ignored (possibly email address?)
// We'll fill in mask_key and checksum after all the other fields.
string data(7, '\0'); // mask_key, checksum, unused
std::string data(7, '\0'); // mask_key, checksum, unused
data.push_back(user_id.size());
data += user_id;
data.push_back(token.size());
+387 -385
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -24,7 +24,7 @@ extern const std::unordered_set<std::string> bb_crypt_initial_client_commands;
constexpr size_t V3_V4_QUEST_LOAD_MAX_CHUNKS_IN_FLIGHT = 4;
// TODO: Many of these functions should take a shared_ptr<Channel> instead of a shared_ptr<Client>. Refactor functions
// TODO: Many of these functions should take a std::shared_ptr<Channel> instead of a std::shared_ptr<Client>. Refactor functions
// appropriately.
// Note: There are so many versions of this function for a few reasons:
+4 -6
View File
@@ -13,9 +13,7 @@
#include "ShellCommands.hh"
#include "StaticGameData.hh"
using namespace std;
ServerShell::ServerShell(shared_ptr<ServerState> state)
ServerShell::ServerShell(std::shared_ptr<ServerState> state)
: state(state), th(&ServerShell::thread_fn, this) {}
ServerShell::~ServerShell() {
@@ -28,7 +26,7 @@ void ServerShell::thread_fn() {
for (;;) {
phosg::fwrite_fmt(stdout, "newserv> ");
fflush(stdout);
string command;
std::string command;
uint64_t read_start_usecs = phosg::now();
try {
command = phosg::fgets(stdin);
@@ -53,7 +51,7 @@ void ServerShell::thread_fn() {
phosg::strip_leading_whitespace(command);
try {
std::promise<deque<string>> promise;
std::promise<std::deque<std::string>> promise;
auto future = promise.get_future();
asio::co_spawn(
@@ -73,7 +71,7 @@ void ServerShell::thread_fn() {
} catch (const exit_shell&) {
this->state->io_context->stop();
return;
} catch (const exception& e) {
} catch (const std::exception& e) {
phosg::fwrite_fmt(stderr, "FAILED: {}\n", e.what());
}
}
+277 -285
View File
File diff suppressed because it is too large Load Diff
+162 -160
View File
@@ -14,48 +14,48 @@
#include "ServerState.hh"
#include "StaticGameData.hh"
using namespace std;
vector<const ShellCommand*> ShellCommand::commands_by_order;
unordered_map<string, const ShellCommand*> ShellCommand::commands_by_name;
std::vector<const ShellCommand*> ShellCommand::commands_by_order;
std::unordered_map<std::string, const ShellCommand*> ShellCommand::commands_by_name;
exit_shell::exit_shell() : runtime_error("shell exited") {}
shared_ptr<Client> ShellCommand::Args::get_client() const {
std::shared_ptr<Client> ShellCommand::Args::get_client() const {
if (!s->game_server) {
throw logic_error("game server is missing");
throw std::logic_error("game server is missing");
}
shared_ptr<Client> c;
std::shared_ptr<Client> c;
if (this->session_name.empty()) {
return this->s->game_server->get_client();
} else {
auto clients = this->s->game_server->get_clients_by_identifier(this->session_name);
if (clients.empty()) {
throw runtime_error("no such client");
throw std::runtime_error("no such client");
}
if (clients.size() > 1) {
throw runtime_error("multiple clients found");
throw std::runtime_error("multiple clients found");
}
return clients[0];
}
}
shared_ptr<Client> ShellCommand::Args::get_proxy_client() const {
std::shared_ptr<Client> ShellCommand::Args::get_proxy_client() const {
auto c = this->get_client();
if (!c->proxy_session) {
throw runtime_error("client is not in a proxy session");
throw std::runtime_error("client is not in a proxy session");
}
return c;
}
ShellCommand::ShellCommand(const char* name, const char* help_text, asio::awaitable<deque<string>> (*run)(Args&))
ShellCommand::ShellCommand(
const char* name, const char* help_text, asio::awaitable<std::deque<std::string>> (*run)(Args&))
: name(name), help_text(help_text), run(run) {
ShellCommand::commands_by_order.emplace_back(this);
ShellCommand::commands_by_name.emplace(this->name, this);
}
asio::awaitable<deque<string>> ShellCommand::dispatch_str(shared_ptr<ServerState> s, const string& command) {
asio::awaitable<std::deque<std::string>> ShellCommand::dispatch_str(
std::shared_ptr<ServerState> s, const std::string& command) {
size_t command_end = phosg::skip_non_whitespace(command, 0);
size_t args_begin = phosg::skip_whitespace(command, command_end);
Args args;
@@ -65,21 +65,21 @@ asio::awaitable<deque<string>> ShellCommand::dispatch_str(shared_ptr<ServerState
co_return co_await ShellCommand::dispatch(args);
}
asio::awaitable<deque<string>> ShellCommand::dispatch(Args& args) {
asio::awaitable<std::deque<std::string>> ShellCommand::dispatch(Args& args) {
const ShellCommand* def = nullptr;
try {
def = commands_by_name.at(args.command);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (!def) {
throw runtime_error("no such command; try 'help'");
throw std::runtime_error("no such command; try 'help'");
} else {
return def->run(args);
}
}
static string get_quoted_string(string& s) {
string ret;
static std::string get_quoted_string(std::string& s) {
std::string ret;
char end_char = (s.at(0) == '\"') ? '\"' : ' ';
size_t z = (s.at(0) == '\"') ? 1 : 0;
for (; (z < s.size()) && (s[z] != end_char); z++) {
@@ -87,7 +87,7 @@ static string get_quoted_string(string& s) {
if (z + 1 < s.size()) {
ret.push_back(s[z + 1]);
} else {
throw runtime_error("incomplete escape sequence");
throw std::runtime_error("incomplete escape sequence");
}
} else {
ret.push_back(s[z]);
@@ -95,7 +95,7 @@ static string get_quoted_string(string& s) {
}
if (end_char != ' ') {
if (z >= s.size()) {
throw runtime_error("unterminated quoted string");
throw std::runtime_error("unterminated quoted string");
}
s = s.substr(phosg::skip_whitespace(s, z + 1));
} else {
@@ -104,8 +104,8 @@ static string get_quoted_string(string& s) {
return ret;
}
static asio::awaitable<deque<string>> empty_handler(ShellCommand::Args&) {
co_return deque<string>();
static asio::awaitable<std::deque<std::string>> empty_handler(ShellCommand::Args&) {
co_return std::deque<std::string>();
}
ShellCommand c_nop1("", nullptr, empty_handler);
@@ -115,8 +115,8 @@ ShellCommand c_nop3("#", nullptr, empty_handler);
ShellCommand c_help(
"help", "help\n\
You\'re reading it now.",
+[](ShellCommand::Args&) -> asio::awaitable<deque<string>> {
deque<string> ret({"Commands:"});
+[](ShellCommand::Args&) -> asio::awaitable<std::deque<std::string>> {
std::deque<std::string> ret({"Commands:"});
for (const auto& def : ShellCommand::commands_by_order) {
if (def->help_text) {
// TODO: It's not great that we copy the text here.
@@ -129,7 +129,7 @@ ShellCommand c_help(
ShellCommand c_exit(
"exit", "exit (or ctrl+d)\n\
Shut down the server.",
+[](ShellCommand::Args&) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args&) -> asio::awaitable<std::deque<std::string>> {
throw exit_shell();
});
ShellCommand c_on(
@@ -141,7 +141,7 @@ ShellCommand c_on(
gamertag, or a BB account username. For proxy commands, SESSION should be\n\
the session ID, which generally is the same as the player\'s account ID\n\
and appears after \"LinkedSession:\" in the log output.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
size_t session_name_end = phosg::skip_non_whitespace(args.args, 0);
size_t command_begin = phosg::skip_whitespace(args.args, session_name_end);
size_t command_end = phosg::skip_non_whitespace(args.args, command_begin);
@@ -181,7 +181,7 @@ ShellCommand c_reload(
disconnect or reload the battle parameters, so if these are changed without\n\
restarting, clients may see (for example) EXP messages inconsistent with\n\
the amounts of EXP actually received.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto types = phosg::split(args.args, ' ');
for (const auto& type : types) {
if (type == "all") {
@@ -231,18 +231,18 @@ ShellCommand c_reload(
} else if (type == "quests") {
args.s->load_quest_index();
} else {
throw runtime_error("invalid data type: " + type);
throw std::runtime_error("invalid data type: " + type);
}
}
co_return deque<string>{};
co_return std::deque<std::string>{};
});
ShellCommand c_list_accounts(
"list-accounts", "list-accounts\n\
List all accounts registered on the server.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
deque<string> ret;
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
std::deque<std::string> ret;
auto accounts = args.s->account_index->all();
if (accounts.empty()) {
ret.emplace_back("No accounts registered");
@@ -254,20 +254,20 @@ ShellCommand c_list_accounts(
co_return ret;
});
uint32_t parse_account_flags(const string& flags_str) {
uint32_t parse_account_flags(const std::string& flags_str) {
try {
size_t end_pos = 0;
uint32_t ret = stoul(flags_str, &end_pos, 16);
uint32_t ret = std::stoul(flags_str, &end_pos, 16);
if (end_pos == flags_str.size()) {
return ret;
}
} catch (const exception&) {
} catch (const std::exception&) {
}
uint32_t ret = 0;
auto tokens = phosg::split(flags_str, ',');
for (const auto& token : tokens) {
string token_upper = phosg::toupper(token);
std::string token_upper = phosg::toupper(token);
if (token_upper == "NONE") {
// Nothing to do
} else if (token_upper == "KICK_USER") {
@@ -299,32 +299,32 @@ uint32_t parse_account_flags(const string& flags_str) {
} else if (token_upper == "IS_SHARED_ACCOUNT") {
ret |= static_cast<uint32_t>(Account::Flag::IS_SHARED_ACCOUNT);
} else {
throw runtime_error("invalid flag name: " + token_upper);
throw std::runtime_error("invalid flag name: " + token_upper);
}
}
return ret;
}
uint32_t parse_account_user_flags(const string& user_flags_str) {
uint32_t parse_account_user_flags(const std::string& user_flags_str) {
try {
size_t end_pos = 0;
uint32_t ret = stoul(user_flags_str, &end_pos, 16);
uint32_t ret = std::stoul(user_flags_str, &end_pos, 16);
if (end_pos == user_flags_str.size()) {
return ret;
}
} catch (const exception&) {
} catch (const std::exception&) {
}
uint32_t ret = 0;
auto tokens = phosg::split(user_flags_str, ',');
for (const auto& token : tokens) {
string token_upper = phosg::toupper(token);
std::string token_upper = phosg::toupper(token);
if (token_upper == "NONE") {
// Nothing to do
} else if (token_upper == "DISABLE_DROP_NOTIFICATION_BROADCAST") {
ret |= static_cast<uint32_t>(Account::UserFlag::DISABLE_DROP_NOTIFICATION_BROADCAST);
} else {
throw runtime_error("invalid user flag name: " + token_upper);
throw std::runtime_error("invalid user flag name: " + token_upper);
}
}
return ret;
@@ -360,15 +360,15 @@ ShellCommand c_add_account(
IS_SHARED_ACCOUNT: Account is a shared serial (disables Access Key and\n\
password checks; players will get Guild Cards based on their player\n\
names)",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
auto account = make_shared<Account>();
for (const string& token : phosg::split(args.args, ' ')) {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto account = std::make_shared<Account>();
for (const std::string& token : phosg::split(args.args, ' ')) {
if (token.starts_with("id=")) {
account->account_id = stoul(token.substr(3), nullptr, 16);
account->account_id = std::stoul(token.substr(3), nullptr, 16);
} else if (token.starts_with("ep3-current-meseta=")) {
account->ep3_current_meseta = stoul(token.substr(19), nullptr, 0);
account->ep3_current_meseta = std::stoul(token.substr(19), nullptr, 0);
} else if (token.starts_with("ep3-total-meseta=")) {
account->ep3_total_meseta_earned = stoul(token.substr(17), nullptr, 0);
account->ep3_total_meseta_earned = std::stoul(token.substr(17), nullptr, 0);
} else if (token == "temporary") {
account->is_temporary = true;
} else if (token.starts_with("flags=")) {
@@ -376,12 +376,12 @@ ShellCommand c_add_account(
} else if (token.starts_with("user-flags=")) {
account->user_flags = parse_account_user_flags(token.substr(11));
} else {
throw invalid_argument("invalid account field: " + token);
throw std::invalid_argument("invalid account field: " + token);
}
}
args.s->account_index->add(account);
account->save();
co_return deque<string>{format("Account {:08X} added", account->account_id)};
co_return std::deque<std::string>{std::format("Account {:08X} added", account->account_id)};
});
ShellCommand c_update_account(
"update-account", "update-account ACCOUNT-ID PARAMETERS...\n\
@@ -400,10 +400,10 @@ ShellCommand c_update_account(
temporary: Marks the account as temporary; it is not saved to disk and\n\
therefore will be deleted when the server shuts down.\n\
permanent: If the account was temporary, makes it non-temporary.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto tokens = phosg::split(args.args, ' ');
if (tokens.size() < 2) {
throw runtime_error("not enough arguments");
throw std::runtime_error("not enough arguments");
}
auto account = args.s->account_index->from_account_id(stoul(tokens[0], nullptr, 16));
tokens.erase(tokens.begin());
@@ -416,11 +416,11 @@ ShellCommand c_update_account(
int64_t new_user_flags = -1;
uint8_t new_is_temporary = 0xFF;
int64_t new_ban_duration = -1;
for (const string& token : tokens) {
for (const std::string& token : tokens) {
if (token.starts_with("ep3-current-meseta=")) {
new_ep3_current_meseta = stoul(token.substr(19), nullptr, 0);
new_ep3_current_meseta = std::stoul(token.substr(19), nullptr, 0);
} else if (token.starts_with("ep3-total-meseta=")) {
new_ep3_total_meseta = stoul(token.substr(17), nullptr, 0);
new_ep3_total_meseta = std::stoul(token.substr(17), nullptr, 0);
} else if (token == "temporary") {
new_is_temporary = 1;
} else if (token == "permanent") {
@@ -448,10 +448,10 @@ ShellCommand c_update_account(
} else if (duration_str.ends_with("y")) {
new_ban_duration = stoull(duration_str.substr(0, duration_str.size() - 1)) * 31536000000000LL;
} else {
throw runtime_error("invalid time unit");
throw std::runtime_error("invalid time unit");
}
} else {
throw invalid_argument("invalid account field: " + token);
throw std::invalid_argument("invalid account field: " + token);
}
}
@@ -479,18 +479,18 @@ ShellCommand c_update_account(
args.s->disconnect_all_banned_clients();
}
co_return deque<string>{format("Account {:08X} updated", account->account_id)};
co_return std::deque<std::string>{std::format("Account {:08X} updated", account->account_id)};
});
ShellCommand c_delete_account(
"delete-account", "delete-account ACCOUNT-ID\n\
Delete an account from the server. If a player is online with the deleted\n\
account, they will not be automatically disconnected.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto account = args.s->account_index->from_account_id(stoul(args.args, nullptr, 16));
args.s->account_index->remove(account->account_id);
account->is_temporary = true;
account->delete_file();
co_return deque<string>{"Account deleted"};
co_return std::deque<std::string>{"Account deleted"};
});
ShellCommand c_add_license(
@@ -509,57 +509,57 @@ ShellCommand c_add_license(
add-license 385A92C4 DC 107862F9 d38XTu2p\n\
add-license 385A92C4 GC 0418572923 282949185033 hunter2\n\
add-license 385A92C4 BB user1 trustno1",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto tokens = phosg::split(args.args, ' ');
if (tokens.size() < 3) {
throw runtime_error("not enough arguments");
throw std::runtime_error("not enough arguments");
}
auto account = args.s->account_index->from_account_id(stoul(tokens[0], nullptr, 16));
string type_str = phosg::toupper(tokens[1]);
std::string type_str = phosg::toupper(tokens[1]);
if (type_str == "DC-NTE") {
if (tokens.size() != 4) {
throw runtime_error("incorrect number of parameters");
throw std::runtime_error("incorrect number of parameters");
}
auto license = make_shared<DCNTELicense>();
auto license = std::make_shared<DCNTELicense>();
license->serial_number = std::move(tokens[2]);
license->access_key = std::move(tokens[3]);
args.s->account_index->add_dc_nte_license(account, license);
} else if (type_str == "DC") {
if (tokens.size() != 4) {
throw runtime_error("incorrect number of parameters");
throw std::runtime_error("incorrect number of parameters");
}
auto license = make_shared<V1V2License>();
license->serial_number = stoul(tokens[2], nullptr, 16);
auto license = std::make_shared<V1V2License>();
license->serial_number = std::stoul(tokens[2], nullptr, 16);
license->access_key = std::move(tokens[3]);
args.s->account_index->add_dc_license(account, license);
} else if (type_str == "PC") {
if (tokens.size() != 4) {
throw runtime_error("incorrect number of parameters");
throw std::runtime_error("incorrect number of parameters");
}
auto license = make_shared<V1V2License>();
license->serial_number = stoul(tokens[2], nullptr, 16);
auto license = std::make_shared<V1V2License>();
license->serial_number = std::stoul(tokens[2], nullptr, 16);
license->access_key = std::move(tokens[3]);
args.s->account_index->add_pc_license(account, license);
} else if (type_str == "GC") {
if (tokens.size() != 5) {
throw runtime_error("incorrect number of parameters");
throw std::runtime_error("incorrect number of parameters");
}
auto license = make_shared<GCLicense>();
license->serial_number = stoul(tokens[2], nullptr, 10);
auto license = std::make_shared<GCLicense>();
license->serial_number = std::stoul(tokens[2], nullptr, 10);
license->access_key = std::move(tokens[3]);
license->password = std::move(tokens[4]);
args.s->account_index->add_gc_license(account, license);
} else if (type_str == "XB") {
if (tokens.size() != 5) {
throw runtime_error("incorrect number of parameters");
throw std::runtime_error("incorrect number of parameters");
}
auto license = make_shared<XBLicense>();
auto license = std::make_shared<XBLicense>();
license->gamertag = std::move(tokens[2]);
license->user_id = stoull(tokens[3], nullptr, 16);
license->account_id = stoull(tokens[4], nullptr, 16);
@@ -567,19 +567,19 @@ ShellCommand c_add_license(
} else if (type_str == "BB") {
if (tokens.size() != 4) {
throw runtime_error("incorrect number of parameters");
throw std::runtime_error("incorrect number of parameters");
}
auto license = make_shared<BBLicense>();
auto license = std::make_shared<BBLicense>();
license->username = std::move(tokens[2]);
license->password = std::move(tokens[3]);
args.s->account_index->add_bb_license(account, license);
} else {
throw runtime_error("invalid license type");
throw std::runtime_error("invalid license type");
}
account->save();
co_return deque<string>{format("Account {:08X} updated", account->account_id)};
co_return std::deque<std::string>{std::format("Account {:08X} updated", account->account_id)};
});
ShellCommand c_delete_license(
"delete-license", "delete-license ACCOUNT-ID TYPE PRIMARY-CREDENTIAL\n\
@@ -598,46 +598,46 @@ ShellCommand c_delete_license(
delete-license 385A92C4 GC 0418572923\n\
delete-license 385A92C4 XB 7E29A2950019EB20\n\
delete-license 385A92C4 BB user1",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto tokens = phosg::split(args.args, ' ');
if (tokens.size() != 3) {
throw runtime_error("incorrect argument count");
throw std::runtime_error("incorrect argument count");
}
auto account = args.s->account_index->from_account_id(stoul(tokens[0], nullptr, 16));
string type_str = phosg::toupper(tokens[1]);
std::string type_str = phosg::toupper(tokens[1]);
if (type_str == "DC-NTE") {
args.s->account_index->remove_dc_nte_license(account, tokens[2]);
} else if (type_str == "DC") {
args.s->account_index->remove_dc_license(account, stoul(tokens[2], nullptr, 16));
args.s->account_index->remove_dc_license(account, std::stoul(tokens[2], nullptr, 16));
} else if (type_str == "PC") {
args.s->account_index->remove_pc_license(account, stoul(tokens[2], nullptr, 16));
args.s->account_index->remove_pc_license(account, std::stoul(tokens[2], nullptr, 16));
} else if (type_str == "GC") {
args.s->account_index->remove_gc_license(account, stoul(tokens[2], nullptr, 0));
args.s->account_index->remove_gc_license(account, std::stoul(tokens[2], nullptr, 0));
} else if (type_str == "XB") {
args.s->account_index->remove_xb_license(account, stoull(tokens[2], nullptr, 16));
} else if (type_str == "BB") {
args.s->account_index->remove_bb_license(account, tokens[2]);
} else {
throw runtime_error("invalid license type");
throw std::runtime_error("invalid license type");
}
account->save();
co_return deque<string>{format("Account {:08X} updated", account->account_id)};
co_return std::deque<std::string>{std::format("Account {:08X} updated", account->account_id)};
});
ShellCommand c_lookup(
"lookup", "lookup USER\n\
Find the account for a logged-in user.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto target = args.s->find_client(&args.args);
if (target->login) {
co_return deque<string>{format("Found client {} with account ID {:08X}",
co_return std::deque<std::string>{format("Found client {} with account ID {:08X}",
target->channel->name, target->login->account->account_id)};
} else {
// This should be impossible
throw logic_error("find_client found user who is not logged in");
throw std::logic_error("find_client found user who is not logged in");
}
});
ShellCommand c_kick(
@@ -645,26 +645,26 @@ ShellCommand c_kick(
Disconnect a user from the server. USER may be an account ID, player name,\n\
or client ID (beginning with \"C-\"). This does not ban the user; they are\n\
free to reconnect after doing this.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto target = args.s->find_client(&args.args);
send_message_box(target, "$C6You have been kicked off the server.");
target->channel->disconnect();
co_return deque<string>{format("Client C-{:X} disconnected from server", target->id)};
co_return std::deque<std::string>{std::format("Client C-{:X} disconnected from server", target->id)};
});
ShellCommand c_announce(
"announce", "announce MESSAGE\n\
Send an announcement message to all players.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
send_text_or_scrolling_message(args.s, args.args, args.args);
co_return deque<string>{};
co_return std::deque<std::string>{};
});
ShellCommand c_announce_mail(
"announce-mail", "announce-mail MESSAGE\n\
Send an announcement message via Simple Mail to all players.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
send_simple_mail(args.s, 0, args.s->name, args.args);
co_return deque<string>{};
co_return std::deque<std::string>{};
});
ShellCommand c_create_tournament(
@@ -695,11 +695,11 @@ ShellCommand c_create_tournament(
dialogue=ON/OFF: Enable/disable dialogue\n\
dice-exchange=ATK/DEF/NONE: Set dice exchange mode\n\
dice-boost=ON/OFF: Enable/disable dice boost",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
string name = get_quoted_string(args.args);
string map_name = get_quoted_string(args.args);
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
std::string name = get_quoted_string(args.args);
std::string map_name = get_quoted_string(args.args);
auto map = args.s->ep3_map_index->map_for_name(map_name);
uint32_t num_teams = stoul(get_quoted_string(args.args), nullptr, 0);
uint32_t num_teams = std::stoul(get_quoted_string(args.args), nullptr, 0);
Episode3::Rules rules;
rules.set_defaults();
uint8_t flags = Episode3::Tournament::Flag::HAS_COM_TEAMS;
@@ -716,24 +716,24 @@ ShellCommand c_create_tournament(
} else if (token == "resize") {
flags |= Episode3::Tournament::Flag::RESIZE_ON_START;
} else if (token.starts_with("dice=")) {
auto parse_range_c = +[](const string& s) -> uint8_t {
auto parse_range_c = +[](const std::string& s) -> uint8_t {
auto tokens = phosg::split(s, '-');
if (tokens.size() != 2) {
throw runtime_error("dice spec must be of the form MIN-MAX");
throw std::runtime_error("dice spec must be of the form MIN-MAX");
}
return (stoul(tokens[0]) << 4) | (stoul(tokens[1]) & 0x0F);
};
auto parse_range_p = +[](const string& s) -> pair<uint8_t, uint8_t> {
auto parse_range_p = +[](const std::string& s) -> std::pair<uint8_t, uint8_t> {
auto tokens = phosg::split(s, '-');
if (tokens.size() != 2) {
throw runtime_error("dice spec must be of the form MIN-MAX");
throw std::runtime_error("dice spec must be of the form MIN-MAX");
}
return make_pair(stoul(tokens[0]), stoul(tokens[1]));
return std::make_pair(stoul(tokens[0]), std::stoul(tokens[1]));
};
auto subtokens = phosg::split(token.substr(5), ':');
if (subtokens.size() < 1) {
throw runtime_error("no dice ranges specified in dice= option");
throw std::runtime_error("no dice ranges specified in dice= option");
}
auto atk_range = parse_range_p(subtokens[0]);
rules.min_dice_value = atk_range.first;
@@ -747,7 +747,7 @@ ShellCommand c_create_tournament(
} else if (subtokens.size() == 4) {
rules.def_dice_value_range_2v1 = parse_range_c(subtokens[3]);
} else {
throw runtime_error("too many range specs given");
throw std::runtime_error("too many range specs given");
}
} else {
rules.atk_dice_value_range_2v1 = 0;
@@ -759,18 +759,18 @@ ShellCommand c_create_tournament(
rules.def_dice_value_range_2v1 = 0;
}
} else if (token.starts_with("overall-time-limit=")) {
uint32_t limit = stoul(token.substr(19));
uint32_t limit = std::stoul(token.substr(19));
if (limit > 600) {
throw runtime_error("overall-time-limit must be 600 or fewer minutes");
throw std::runtime_error("overall-time-limit must be 600 or fewer minutes");
}
if (limit % 5) {
throw runtime_error("overall-time-limit must be a multiple of 5 minutes");
throw std::runtime_error("overall-time-limit must be a multiple of 5 minutes");
}
rules.overall_time_limit = limit;
} else if (token.starts_with("phase-time-limit=")) {
rules.phase_time_limit = stoul(token.substr(17));
rules.phase_time_limit = std::stoul(token.substr(17));
} else if (token.starts_with("hp=")) {
rules.char_hp = stoul(token.substr(3));
rules.char_hp = std::stoul(token.substr(3));
} else if (token == "allowed-cards=all") {
rules.allowed_cards = Episode3::AllowedCards::ALL;
} else if (token == "allowed-cards=n") {
@@ -812,11 +812,11 @@ ShellCommand c_create_tournament(
} else if (token == "dice-exchange=none") {
rules.dice_exchange_mode = Episode3::DiceExchangeMode::NONE;
} else {
throw runtime_error("invalid rules option: " + token);
throw std::runtime_error("invalid rules option: " + token);
}
}
}
deque<string> ret;
std::deque<std::string> ret;
if (rules.check_and_reset_invalid_fields()) {
ret.emplace_back("Warning: Some rules were invalid and reset to defaults");
}
@@ -829,19 +829,19 @@ ShellCommand c_delete_tournament(
"delete-tournament", "delete-tournament TOURNAMENT-NAME\n\
Delete a tournament. Quotes are required around the tournament name unless\n\
the name contains no spaces.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
string name = get_quoted_string(args.args);
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
std::string name = get_quoted_string(args.args);
if (args.s->ep3_tournament_index->delete_tournament(name)) {
co_return deque<string>{"Deleted tournament"};
co_return std::deque<std::string>{"Deleted tournament"};
} else {
throw runtime_error("tournament does not exist");
throw std::runtime_error("tournament does not exist");
}
});
ShellCommand c_list_tournaments(
"list-tournaments", "list-tournaments\n\
List the names and numbers of all existing tournaments.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
deque<string> ret;
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
std::deque<std::string> ret;
for (const auto& it : args.s->ep3_tournament_index->all_tournaments()) {
ret.emplace_back(" " + it.second->get_name());
}
@@ -851,30 +851,30 @@ ShellCommand c_start_tournament(
"start-tournament", "start-tournament TOURNAMENT-NAME\n\
End registration for a tournament and allow matches to begin. Quotes are\n\
required around the tournament name unless the name contains no spaces.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
string name = get_quoted_string(args.args);
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
std::string name = get_quoted_string(args.args);
auto tourn = args.s->ep3_tournament_index->get_tournament(name);
if (tourn) {
tourn->start();
args.s->ep3_tournament_index->save();
tourn->send_all_state_updates();
send_ep3_text_message_fmt(args.s, "$C7The tournament\n$C6{}$C7\nhas begun", tourn->get_name());
co_return deque<string>{"Tournament started"};
co_return std::deque<std::string>{"Tournament started"};
} else {
throw runtime_error("tournament does not exist");
throw std::runtime_error("tournament does not exist");
}
});
ShellCommand c_describe_tournament(
"describe-tournament", "describe-tournament TOURNAMENT-NAME\n\
Show the current state of a tournament. Quotes are required around the\n\
tournament name unless the name contains no spaces.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
string name = get_quoted_string(args.args);
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
std::string name = get_quoted_string(args.args);
auto tourn = args.s->ep3_tournament_index->get_tournament(name);
if (tourn) {
co_return deque<string>{tourn->bracket_str()};
co_return std::deque<std::string>{tourn->bracket_str()};
} else {
throw runtime_error("tournament does not exist");
throw std::runtime_error("tournament does not exist");
}
});
@@ -888,16 +888,16 @@ ShellCommand c_cc(
via this command are exempt from permission checks, so commands that\n\
require cheat mode or debug mode are always available via cc even if the\n\
player cannot normamlly use them.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto c = args.get_client();
co_await on_chat_command(c, args.args, false);
co_return deque<string>{};
co_return std::deque<std::string>{};
});
asio::awaitable<deque<string>> f_sc_ss(ShellCommand::Args& args) {
string data = phosg::parse_data_string(args.args, nullptr, phosg::ParseDataFlags::ALLOW_FILES);
asio::awaitable<std::deque<std::string>> f_sc_ss(ShellCommand::Args& args) {
std::string data = phosg::parse_data_string(args.args, nullptr, phosg::ParseDataFlags::ALLOW_FILES);
if (data.size() == 0) {
throw invalid_argument("no data given");
throw std::invalid_argument("no data given");
}
data.resize((data.size() + 3) & (~3));
@@ -912,7 +912,7 @@ asio::awaitable<deque<string>> f_sc_ss(ShellCommand::Args& args) {
send_command_with_header(c->channel, data.data(), data.size());
}
co_return deque<string>{};
co_return std::deque<std::string>{};
}
ShellCommand c_sc("sc", "sc DATA\n\
@@ -926,10 +926,10 @@ ShellCommand c_show_slots(
"show-slots", "show-slots\n\
Show the player names, Guild Card numbers, and client IDs of all players in\n\
the current lobby or game.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto c = args.get_proxy_client();
deque<string> ret;
std::deque<std::string> ret;
for (size_t z = 0; z < c->proxy_session->lobby_players.size(); z++) {
const auto& player = c->proxy_session->lobby_players[z];
if (player.guild_card_number) {
@@ -939,13 +939,13 @@ ShellCommand c_show_slots(
name_for_char_class(player.char_class),
name_for_section_id(player.section_id)));
} else {
ret.emplace_back(format(" {}: (no player)", z));
ret.emplace_back(std::format(" {}: (no player)", z));
}
}
co_return ret;
});
asio::awaitable<deque<string>> fn_chat(ShellCommand::Args& args) {
asio::awaitable<std::deque<std::string>> fn_chat(ShellCommand::Args& args) {
auto c = args.get_client();
bool is_dchat = (args.command == "dchat");
@@ -953,7 +953,7 @@ asio::awaitable<deque<string>> fn_chat(ShellCommand::Args& args) {
if (!is_dchat && uses_utf16(c->version())) {
send_chat_message_from_client(c->proxy_session->server_channel, args.args, 0);
} else {
string data(8, '\0');
std::string data(8, '\0');
data.push_back('\x09');
data.push_back('E');
if (is_dchat) {
@@ -966,7 +966,9 @@ asio::awaitable<deque<string>> fn_chat(ShellCommand::Args& args) {
c->proxy_session->server_channel->send(0x06, 0x00, data);
}
} else if (c->login) {
string text = is_dchat ? phosg::parse_data_string(args.args, nullptr, phosg::ParseDataFlags::ALLOW_FILES) : args.args;
std::string text = is_dchat
? phosg::parse_data_string(args.args, nullptr, phosg::ParseDataFlags::ALLOW_FILES)
: args.args;
auto l = c->require_lobby();
for (auto& lc : l->clients) {
if (lc) {
@@ -975,7 +977,7 @@ asio::awaitable<deque<string>> fn_chat(ShellCommand::Args& args) {
}
}
co_return deque<string>{};
co_return std::deque<std::string>{};
}
ShellCommand c_c("c", "c TEXT", fn_chat);
ShellCommand c_chat("chat", "chat TEXT\n\
@@ -985,13 +987,13 @@ ShellCommand c_dchat("dchat", "dchat DATA\n\
Send a chat message to the server with arbitrary data in it.",
fn_chat);
asio::awaitable<deque<string>> fn_wchat(ShellCommand::Args& args) {
asio::awaitable<std::deque<std::string>> fn_wchat(ShellCommand::Args& args) {
auto c = args.get_client();
if (!is_ep3(c->version())) {
throw runtime_error("wchat can only be used on Episode 3");
throw std::runtime_error("wchat can only be used on Episode 3");
}
if (c->proxy_session) {
string data(8, '\0');
std::string data(8, '\0');
data.push_back('\x40'); // private_flags: visible to all
data.push_back('\x09');
data.push_back('E');
@@ -1008,7 +1010,7 @@ asio::awaitable<deque<string>> fn_wchat(ShellCommand::Args& args) {
}
}
}
co_return deque<string>{};
co_return std::deque<std::string>{};
}
ShellCommand c_wc("wc", "wc TEXT", fn_wchat);
ShellCommand c_wchat("wchat", "wchat TEXT\n\
@@ -1018,20 +1020,20 @@ ShellCommand c_wchat("wchat", "wchat TEXT\n\
ShellCommand c_marker(
"marker", "marker COLOR-ID\n\
Change your lobby marker color.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto c = args.get_proxy_client();
c->proxy_session->server_channel->send(0x89, stoul(args.args));
co_return deque<string>{};
c->proxy_session->server_channel->send(0x89, std::stoul(args.args));
co_return std::deque<std::string>{};
});
asio::awaitable<deque<string>> fn_warp(ShellCommand::Args& args) {
asio::awaitable<std::deque<std::string>> fn_warp(ShellCommand::Args& args) {
auto c = args.get_proxy_client();
uint8_t floor = stoul(args.args);
uint8_t floor = std::stoul(args.args);
send_warp(c->channel, c->lobby_client_id, floor, true);
if (args.command == "warpall") {
send_warp(c->proxy_session->server_channel, c->lobby_client_id, floor, false);
}
co_return deque<string>{};
co_return std::deque<std::string>{};
}
ShellCommand c_warp("warp", "warp FLOOR-ID", fn_warp);
ShellCommand c_warpme("warpme", "warpme FLOOR-ID\n\
@@ -1041,10 +1043,10 @@ ShellCommand c_warpall("warpall", "warpall FLOOR-ID\n\
Send everyone to a specific floor.",
fn_warp);
asio::awaitable<deque<string>> fn_info_board(ShellCommand::Args& args) {
asio::awaitable<std::deque<std::string>> fn_info_board(ShellCommand::Args& args) {
auto c = args.get_proxy_client();
string data;
std::string data;
if (args.command == "info-board-data") {
data += phosg::parse_data_string(args.args, nullptr, phosg::ParseDataFlags::ALLOW_FILES);
} else {
@@ -1054,7 +1056,7 @@ asio::awaitable<deque<string>> fn_info_board(ShellCommand::Args& args) {
data.resize((data.size() + 3) & (~3));
c->proxy_session->server_channel->send(0xD9, 0x00, data);
co_return deque<string>{};
co_return std::deque<std::string>{};
}
ShellCommand c_info_board("info-board", "info-board TEXT\n\
Set your info board contents. This will affect the current session only,\n\
@@ -1069,17 +1071,17 @@ ShellCommand c_info_board_data("info-board-data", "info-board-data DATA\n\
ShellCommand c_create_item(
"create-item", "create-item DATA\n\
Create an item as if the client had run the $item command.",
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
+[](ShellCommand::Args& args) -> asio::awaitable<std::deque<std::string>> {
auto c = args.get_proxy_client();
if (c->version() == Version::BB_V4) {
throw runtime_error("proxy session is BB");
throw std::runtime_error("proxy session is BB");
}
if (!c->proxy_session->is_in_game) {
throw runtime_error("proxy session is not in a game");
throw std::runtime_error("proxy session is not in a game");
}
if (c->lobby_client_id != c->proxy_session->leader_client_id) {
throw runtime_error("proxy session is not game leader");
throw std::runtime_error("proxy session is not game leader");
}
ItemData item = args.s->parse_item_description(c->version(), args.args);
@@ -1088,7 +1090,7 @@ ShellCommand c_create_item(
send_drop_stacked_item_to_channel(args.s, c->channel, item, c->floor, c->pos);
send_drop_stacked_item_to_channel(args.s, c->proxy_session->server_channel, item, c->floor, c->pos);
string name = args.s->describe_item(c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES);
std::string name = args.s->describe_item(c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES);
send_text_message(c->channel, "$C7Item created:\n" + name);
co_return deque<string>{};
co_return std::deque<std::string>{};
});
+4 -8
View File
@@ -12,12 +12,8 @@
#include "ServerState.hh"
#include "StaticGameData.hh"
using namespace std;
SignalWatcher::SignalWatcher(shared_ptr<ServerState> state)
: log("[SignalWatcher] "),
state(state),
signals(*this->state->io_context) {
SignalWatcher::SignalWatcher(std::shared_ptr<ServerState> state)
: log("[SignalWatcher] "), state(state), signals(*this->state->io_context) {
asio::co_spawn(*this->state->io_context, this->signal_handler_task(), asio::detached);
}
@@ -34,7 +30,7 @@ asio::awaitable<void> SignalWatcher::signal_handler_task() {
this->state->load_config_early();
this->state->load_config_late();
phosg::fwrite_fmt(stderr, "Configuration update complete\n");
} catch (const exception& e) {
} catch (const std::exception& e) {
phosg::fwrite_fmt(stderr, "FAILED: {}\n", e.what());
phosg::fwrite_fmt(stderr, "Some configuration may have been reloaded. Fix the underlying issue and try again.\n");
}
@@ -44,7 +40,7 @@ asio::awaitable<void> SignalWatcher::signal_handler_task() {
try {
this->state->load_all(true);
phosg::fwrite_fmt(stderr, "Configuration update complete\n");
} catch (const exception& e) {
} catch (const std::exception& e) {
phosg::fwrite_fmt(stderr, "FAILED: {}\n", e.what());
phosg::fwrite_fmt(stderr, "Some configuration may have been reloaded. Fix the underlying issue and try again.\n");
}
+89 -91
View File
@@ -4,8 +4,6 @@
#include "Text.hh"
using namespace std;
bool episode_has_arpg_semantics(Episode ep) {
return (ep == Episode::EP1) || (ep == Episode::EP2) || (ep == Episode::EP4);
}
@@ -38,11 +36,11 @@ const char* token_name_for_episode(Episode ep) {
case Episode::EP4:
return "Episode4";
default:
throw logic_error("invalid episode");
throw std::logic_error("invalid episode");
}
}
Episode episode_for_token_name(const string& name) {
Episode episode_for_token_name(const std::string& name) {
if (name == "Episode1") {
return Episode::EP1;
}
@@ -55,7 +53,7 @@ Episode episode_for_token_name(const string& name) {
if (name == "Episode4") {
return Episode::EP4;
}
throw runtime_error("unknown episode");
throw std::runtime_error("unknown episode");
}
Episode episode_for_area(uint8_t area) {
@@ -117,13 +115,13 @@ const char* abbreviation_for_mode(GameMode mode) {
}
}
static const array<const char*, 10> section_id_to_name = {
static const std::array<const char*, 10> section_id_to_name = {
"Viridia", "Greennill", "Skyly", "Bluefull", "Purplenum", "Pinkal", "Redria", "Oran", "Yellowboze", "Whitill"};
static const array<const char*, 10> section_id_to_abbreviation = {
static const std::array<const char*, 10> section_id_to_abbreviation = {
"Vir", "Grn", "Sky", "Blu", "Prp", "Pnk", "Red", "Orn", "Ylw", "Wht"};
const unordered_map<string, uint8_t> name_to_section_id({{"viridia", 0},
const std::unordered_map<std::string, uint8_t> name_to_section_id({{"viridia", 0},
// Greennill is spelled Greenill in some places, so we accept both spellings
{"greennill", 1}, {"greenill", 1}, {"skyly", 2}, {"bluefull", 3}, {"purplenum", 4}, {"pinkal", 5}, {"redria", 6},
{"oran", 7}, {"yellowboze", 8}, {"whitill", 9},
@@ -131,32 +129,32 @@ const unordered_map<string, uint8_t> name_to_section_id({{"viridia", 0},
// Shortcuts for chat commands
{"b", 3}, {"g", 1}, {"o", 7}, {"pi", 5}, {"pu", 4}, {"r", 6}, {"s", 2}, {"v", 0}, {"w", 9}, {"y", 8}});
const vector<string> lobby_event_to_name = {
const std::vector<std::string> lobby_event_to_name = {
"none", "xmas", "none", "val", "easter", "hallo", "sonic", "newyear",
"summer", "white", "wedding", "fall", "s-spring", "s-summer", "spring"};
const unordered_map<string, uint8_t> name_to_lobby_event = {
const std::unordered_map<std::string, uint8_t> name_to_lobby_event = {
{"none", 0}, {"xmas", 1}, {"val", 3}, {"easter", 4}, {"hallo", 5}, {"sonic", 6}, {"newyear", 7}, {"summer", 8},
{"white", 9}, {"wedding", 10}, {"fall", 11}, {"s-spring", 12}, {"s-summer", 13}, {"spring", 14}};
const unordered_map<uint8_t, string> lobby_type_to_name = {
const std::unordered_map<uint8_t, std::string> lobby_type_to_name = {
{0x00, "normal"}, {0x0F, "inormal"}, {0x10, "ipc"}, {0x11, "iball"}, {0x67, "cave2u"}, {0xD4, "cave1"},
{0xE9, "planet"}, {0xEA, "clouds"}, {0xED, "cave"}, {0xEE, "jungle"}, {0xEF, "forest2-2"}, {0xF0, "forest2-1"},
{0xF1, "windpower"}, {0xF2, "overview"}, {0xF3, "seaside"}, {0xF4, "fons"}, {0xF5, "dmorgue"}, {0xF6, "caelum"},
{0xF8, "cyber"}, {0xF9, "boss1"}, {0xFA, "boss2"}, {0xFB, "dolor"}, {0xFC, "dragon"}, {0xFD, "derolle"},
{0xFE, "volopt"}, {0xFF, "darkfalz"}};
const unordered_map<string, uint8_t> name_to_lobby_type = {
const std::unordered_map<std::string, uint8_t> name_to_lobby_type = {
{"normal", 0x00}, {"inormal", 0x0F}, {"ipc", 0x10}, {"iball", 0x11}, {"cave1", 0xD4}, {"cave2u", 0x67},
{"dragon", 0xFC}, {"derolle", 0xFD}, {"volopt", 0xFE}, {"darkfalz", 0xFF}, {"planet", 0xE9}, {"clouds", 0xEA},
{"cave", 0xED}, {"jungle", 0xEE}, {"forest2-2", 0xEF}, {"forest2-1", 0xF0}, {"windpower", 0xF1},
{"overview", 0xF2}, {"seaside", 0xF3}, {"fons", 0xF4}, {"dmorgue", 0xF5}, {"caelum", 0xF6}, {"cyber", 0xF8},
{"boss1", 0xF9}, {"boss2", 0xFA}, {"dolor", 0xFB}, {"ravum", 0xFC}, {"sky", 0xFE}, {"morgue", 0xFF}};
const vector<string> npc_id_to_name = {
const std::vector<std::string> npc_id_to_name = {
"ninja", "rico", "sonic", "knuckles", "tails", "flowen", "elly", "momoka", "irene", "guild", "nurse"};
const unordered_map<string, uint8_t> name_to_npc_id = {
const std::unordered_map<std::string, uint8_t> name_to_npc_id = {
{"ninja", 0}, {"rico", 1}, {"sonic", 2}, {"knuckles", 3}, {"tails", 4}, {"flowen", 5}, {"elly", 6}, {"momoka", 7},
{"irene", 8}, {"guild", 9}, {"nurse", 10}};
@@ -199,100 +197,100 @@ const char* name_for_section_id(uint8_t section_id) {
}
}
uint8_t section_id_for_name(const string& name) {
string lower_name = phosg::tolower(name);
uint8_t section_id_for_name(const std::string& name) {
std::string lower_name = phosg::tolower(name);
try {
return name_to_section_id.at(lower_name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
uint64_t x = stoul(name);
uint64_t x = std::stoul(name);
if (x < section_id_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
} catch (const std::invalid_argument&) {
} catch (const std::out_of_range&) {
}
return 0xFF;
}
const string& name_for_event(uint8_t event) {
const std::string& name_for_event(uint8_t event) {
if (event < lobby_event_to_name.size()) {
return lobby_event_to_name[event];
} else {
static const string ret = "Unknown lobby event";
static const std::string ret = "Unknown lobby event";
return ret;
}
}
uint8_t event_for_name(const string& name) {
uint8_t event_for_name(const std::string& name) {
try {
return name_to_lobby_event.at(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
uint64_t x = stoul(name);
if (x < lobby_event_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
} catch (const std::invalid_argument&) {
} catch (const std::out_of_range&) {
}
return 0xFF;
}
const string& name_for_lobby_type(uint8_t type) {
const std::string& name_for_lobby_type(uint8_t type) {
try {
return lobby_type_to_name.at(type);
} catch (const out_of_range&) {
static const string ret = "Unknown lobby type";
} catch (const std::out_of_range&) {
static const std::string ret = "Unknown lobby type";
return ret;
}
}
uint8_t lobby_type_for_name(const string& name) {
uint8_t lobby_type_for_name(const std::string& name) {
try {
return name_to_lobby_type.at(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
uint64_t x = stoul(name, nullptr, 0);
uint64_t x = std::stoul(name, nullptr, 0);
if (lobby_type_to_name.count(x)) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
} catch (const std::invalid_argument&) {
} catch (const std::out_of_range&) {
}
return 0x80;
}
const string& name_for_npc(uint8_t npc) {
const std::string& name_for_npc(uint8_t npc) {
try {
return npc_id_to_name.at(npc);
} catch (const out_of_range&) {
static const string ret = "Unknown NPC";
} catch (const std::out_of_range&) {
static const std::string ret = "Unknown NPC";
return ret;
}
}
uint8_t npc_for_name(const string& name, Version version) {
uint8_t npc_for_name(const std::string& name, Version version) {
uint8_t npc_id = 0xFF;
try {
npc_id = name_to_npc_id.at(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
if (npc_id == 0xFF) {
try {
npc_id = stoul(name);
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
npc_id = std::stoul(name);
} catch (const std::invalid_argument&) {
} catch (const std::out_of_range&) {
}
}
return npc_valid_for_version(npc_id, version) ? npc_id : 0xFF;
}
const char* name_for_char_class(uint8_t cls) {
static const array<const char*, 12> names = {
static const std::array<const char*, 12> names = {
/* 00 */ "HUmar",
/* 01 */ "HUnewearl",
/* 02 */ "HUcast",
@@ -307,17 +305,17 @@ const char* name_for_char_class(uint8_t cls) {
/* 0B */ "RAmarl"};
try {
return names.at(cls);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return "Unknown";
}
}
const char* abbreviation_for_char_class(uint8_t cls) {
static const array<const char*, 12> names = {
static const std::array<const char*, 12> names = {
"HUmr", "HUnl", "HUct", "RAmr", "RAct", "RAcl", "FOml", "FOnm", "FOnl", "HUcl", "FOmr", "RAml"};
try {
return names.at(cls);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return "???";
}
}
@@ -332,7 +330,7 @@ enum ClassFlag {
FORCE = 0x40,
};
static array<uint8_t, 12> class_flags = {
static std::array<uint8_t, 12> class_flags = {
ClassFlag::HUNTER | ClassFlag::HUMAN | ClassFlag::MALE, // HUmar
ClassFlag::HUNTER | ClassFlag::NEWMAN, // HUnewearl
ClassFlag::HUNTER | ClassFlag::ANDROID | ClassFlag::MALE, // HUcast
@@ -376,34 +374,34 @@ bool char_class_is_force(uint8_t cls) {
}
const char* name_for_difficulty(Difficulty difficulty) {
static const array<const char*, 4> names = {"Normal", "Hard", "Very Hard", "Ultimate"};
static const std::array<const char*, 4> names = {"Normal", "Hard", "Very Hard", "Ultimate"};
try {
return names.at(static_cast<size_t>(difficulty));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return "Unknown";
}
}
const char* token_name_for_difficulty(Difficulty difficulty) {
static const array<const char*, 4> names = {"Normal", "Hard", "VeryHard", "Ultimate"};
static const std::array<const char*, 4> names = {"Normal", "Hard", "VeryHard", "Ultimate"};
try {
return names.at(static_cast<size_t>(difficulty));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return "Unknown";
}
}
char abbreviation_for_difficulty(Difficulty difficulty) {
static const array<char, 4> names = {'N', 'H', 'V', 'U'};
static const std::array<char, 4> names = {'N', 'H', 'V', 'U'};
try {
return names.at(static_cast<size_t>(difficulty));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return '?';
}
}
const char* name_for_language(Language language) {
array<const char*, 8> names = {
std::array<const char*, 8> names = {
"Japanese", "English", "German", "French", "Spanish", "Simplified Chinese", "Traditional Chinese", "Korean"};
size_t lang_index = static_cast<size_t>(language);
return (lang_index < 8) ? names[lang_index] : "Unknown";
@@ -441,15 +439,15 @@ Language language_for_char(char language_char) {
case 'k':
return Language::KOREAN;
default:
throw runtime_error("unknown language");
throw std::runtime_error("unknown language");
}
}
Language language_for_name(const string& name) {
Language language_for_name(const std::string& name) {
if (name.size() == 1) {
return language_for_char(name[0]);
}
string lower_name = phosg::tolower(name);
std::string lower_name = phosg::tolower(name);
if (lower_name == "japanese") {
return Language::JAPANESE;
}
@@ -474,48 +472,48 @@ Language language_for_name(const string& name) {
if (lower_name == "korean") {
return Language::KOREAN;
}
throw runtime_error("unknown language");
throw std::runtime_error("unknown language");
}
const vector<string> tech_id_to_name = {
const std::vector<std::string> tech_id_to_name = {
"Foie", "Gifoie", "Rafoie", "Barta", "Gibarta", "Rabarta", "Zonde", "Gizonde", "Razonde", "Grants", "Deband",
"Jellen", "Zalure", "Shifta", "Ryuker", "Resta", "Anti", "Reverser", "Megid"};
const unordered_map<string, uint8_t> name_to_tech_id = {
const std::unordered_map<std::string, uint8_t> name_to_tech_id = {
{"foie", 0}, {"gifoie", 1}, {"rafoie", 2}, {"barta", 3}, {"gibarta", 4}, {"rabarta", 5}, {"zonde", 6},
{"gizonde", 7}, {"razonde", 8}, {"grants", 9}, {"deband", 10}, {"jellen", 11}, {"zalure", 12}, {"shifta", 13},
{"ryuker", 14}, {"resta", 15}, {"anti", 16}, {"reverser", 17}, {"megid", 18}};
const string& name_for_technique(uint8_t tech) {
const std::string& name_for_technique(uint8_t tech) {
try {
return tech_id_to_name.at(tech);
} catch (const out_of_range&) {
static const string ret = "Unknown technique";
} catch (const std::out_of_range&) {
static const std::string ret = "Unknown technique";
return ret;
}
}
uint8_t technique_for_name(const string& name) {
uint8_t technique_for_name(const std::string& name) {
try {
return name_to_tech_id.at(phosg::tolower(name));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
uint64_t x = stoul(name);
uint64_t x = std::stoul(name);
if (x < tech_id_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
} catch (const std::invalid_argument&) {
} catch (const std::out_of_range&) {
}
return 0xFF;
}
const vector<const char*> name_for_mag_color = {
const std::vector<const char*> name_for_mag_color = {
"red", "blue", "yellow", "green", "purple", "black", "white", "cyan", "brown", "orange", "light-blue", "olive",
"turquoise", "fuchsia", "grey", "cream", "pink", "dark-green", "costume"};
const unordered_map<string, uint8_t> mag_color_for_name = {
const std::unordered_map<std::string, uint8_t> mag_color_for_name = {
{"red", 0x00}, {"blue", 0x01}, {"yellow", 0x02}, {"green", 0x03}, {"purple", 0x04}, {"black", 0x05},
{"white", 0x06}, {"cyan", 0x07}, {"brown", 0x08}, {"orange", 0x09}, {"light-blue", 0x0A}, {"olive", 0x0B},
{"turquoise", 0x0C}, {"fuchsia", 0x0D}, {"grey", 0x0E}, {"cream", 0x0F}, {"pink", 0x10}, {"dark-green", 0x11},
@@ -586,7 +584,7 @@ static const std::vector<FloorDefinition> floor_defs{
const FloorDefinition& FloorDefinition::get(Episode episode, uint8_t floor) {
if (floor >= FloorDefinition::limit_for_episode(episode)) {
throw runtime_error("invalid floor number");
throw std::runtime_error("invalid floor number");
}
switch (episode) {
case Episode::EP1:
@@ -596,7 +594,7 @@ const FloorDefinition& FloorDefinition::get(Episode episode, uint8_t floor) {
case Episode::EP4:
return floor_defs.at(floor + 0x24);
default:
throw logic_error("invalid episode");
throw std::logic_error("invalid episode");
}
}
@@ -609,16 +607,16 @@ const FloorDefinition& FloorDefinition::get_by_drop_area_norm(Episode episode, u
case Episode::EP4:
return floor_defs[area_norm + 0x24];
default:
throw logic_error("invalid episode number");
throw std::logic_error("invalid episode number");
}
}
const FloorDefinition& FloorDefinition::get(Episode episode, const std::string& name) {
static unordered_map<std::string, size_t> index_ep1;
static unordered_map<std::string, size_t> index_ep2;
static unordered_map<std::string, size_t> index_ep4;
static std::unordered_map<std::string, size_t> index_ep1;
static std::unordered_map<std::string, size_t> index_ep2;
static std::unordered_map<std::string, size_t> index_ep4;
unordered_map<std::string, size_t>* index;
std::unordered_map<std::string, size_t>* index;
switch (episode) {
case Episode::EP1:
index = &index_ep1;
@@ -630,7 +628,7 @@ const FloorDefinition& FloorDefinition::get(Episode episode, const std::string&
index = &index_ep4;
break;
default:
throw logic_error("invalid episode");
throw std::logic_error("invalid episode");
}
if (index->empty()) {
@@ -663,7 +661,7 @@ size_t FloorDefinition::limit_for_episode(Episode ep) {
uint32_t class_flags_for_class(uint8_t char_class) {
static constexpr uint8_t flags[12] = {0x25, 0x2A, 0x31, 0x45, 0x51, 0x52, 0x86, 0x89, 0x8A, 0x32, 0x85, 0x46};
if (char_class >= 12) {
throw runtime_error("invalid character class");
throw std::runtime_error("invalid character class");
}
return flags[char_class];
}
@@ -675,16 +673,16 @@ char char_for_challenge_rank(uint8_t rank) {
return "BAS"[rank];
}
const array<size_t, 4> DEFAULT_MIN_LEVELS_V123({0, 19, 39, 79});
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP1({0, 19, 39, 79});
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2({0, 29, 49, 89});
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4({0, 39, 79, 109});
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V123({0, 19, 39, 79});
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP1({0, 19, 39, 79});
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2({0, 29, 49, 89});
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4({0, 39, 79, 109});
const array<GameMode, 2> ALL_GAME_MODES_V1 = {GameMode::NORMAL, GameMode::BATTLE};
const array<GameMode, 3> ALL_GAME_MODES_V23 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE};
const array<GameMode, 4> ALL_GAME_MODES_V4 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
const array<Episode, 1> ALL_EPISODES_V12 = {Episode::EP1};
const array<Episode, 2> ALL_EPISODES_V3 = {Episode::EP1, Episode::EP2};
const array<Episode, 3> ALL_EPISODES_V4 = {Episode::EP1, Episode::EP2, Episode::EP4};
const array<Difficulty, 3> ALL_DIFFICULTIES_V1 = {Difficulty::NORMAL, Difficulty::HARD, Difficulty::VERY_HARD};
const array<Difficulty, 4> ALL_DIFFICULTIES_V234 = {Difficulty::NORMAL, Difficulty::HARD, Difficulty::VERY_HARD, Difficulty::ULTIMATE};
const std::array<GameMode, 2> ALL_GAME_MODES_V1 = {GameMode::NORMAL, GameMode::BATTLE};
const std::array<GameMode, 3> ALL_GAME_MODES_V23 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE};
const std::array<GameMode, 4> ALL_GAME_MODES_V4 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
const std::array<Episode, 1> ALL_EPISODES_V12 = {Episode::EP1};
const std::array<Episode, 2> ALL_EPISODES_V3 = {Episode::EP1, Episode::EP2};
const std::array<Episode, 3> ALL_EPISODES_V4 = {Episode::EP1, Episode::EP2, Episode::EP4};
const std::array<Difficulty, 3> ALL_DIFFICULTIES_V1 = {Difficulty::NORMAL, Difficulty::HARD, Difficulty::VERY_HARD};
const std::array<Difficulty, 4> ALL_DIFFICULTIES_V234 = {Difficulty::NORMAL, Difficulty::HARD, Difficulty::VERY_HARD, Difficulty::ULTIMATE};
-1
View File
@@ -6,7 +6,6 @@
#include <unordered_map>
#include <vector>
#include "FileContentsCache.hh"
#include "Version.hh"
enum class Episode {
+42 -46
View File
@@ -11,13 +11,11 @@
#include "Loggers.hh"
#include "StaticGameData.hh"
using namespace std;
TeamIndex::Team::Member::Member(const phosg::JSON& json)
: flags(json.get_int("Flags", 0)), points(json.get_int("Points", 0)), name(json.get_string("Name", "")) {
try {
this->account_id = json.get_int("AccountID");
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
// Old format
this->account_id = json.get_int("SerialNumber");
}
@@ -42,11 +40,11 @@ TeamIndex::Team::Team(uint32_t team_id) : Team() {
this->team_id = team_id;
}
string TeamIndex::Team::json_filename() const {
std::string TeamIndex::Team::json_filename() const {
return std::format("system/teams/{:08X}.json", this->team_id);
}
string TeamIndex::Team::flag_filename() const {
std::string TeamIndex::Team::flag_filename() const {
return std::format("system/teams/{:08X}.bmp", this->team_id);
}
@@ -68,7 +66,7 @@ void TeamIndex::Team::load_config() {
for (const auto& it : json.get_list("RewardKeys")) {
this->reward_keys.emplace(it->as_string());
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
this->reward_flags = json.get_int("RewardFlags");
}
@@ -98,7 +96,7 @@ void TeamIndex::Team::save_config() const {
void TeamIndex::Team::load_flag() {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(this->flag_filename()));
if (img.get_width() != 32 || img.get_height() != 32) {
throw runtime_error("incorrect flag image dimensions");
throw std::runtime_error("incorrect flag image dimensions");
}
this->flag_data.reset(new parray<le_uint16_t, 0x20 * 0x20>());
for (size_t y = 0; y < 32; y++) {
@@ -127,10 +125,8 @@ void TeamIndex::Team::save_flag() const {
}
void TeamIndex::Team::delete_files() const {
string json_filename = this->json_filename();
string flag_filename = this->flag_filename();
remove(json_filename.c_str());
remove(flag_filename.c_str());
std::filesystem::remove(this->json_filename());
std::filesystem::remove(this->flag_filename());
}
PSOBBBaseTeamMembership TeamIndex::Team::base_membership_for_member(uint32_t account_id) const {
@@ -161,7 +157,7 @@ PSOBBFullTeamMembership TeamIndex::Team::full_membership_for_member(uint32_t acc
return ret;
}
bool TeamIndex::Team::has_reward(const string& key) const {
bool TeamIndex::Team::has_reward(const std::string& key) const {
return this->reward_keys.count(key);
}
@@ -226,21 +222,20 @@ TeamIndex::Reward::Reward(uint32_t menu_item_id, const phosg::JSON& def_json)
for (const auto& it : def_json.get_list("PrerequisiteKeys")) {
this->prerequisite_keys.emplace(it->as_string());
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->reward_flag = static_cast<Team::RewardFlag>(def_json.get_int("RewardFlag"));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
try {
this->reward_item = ItemData::from_data(phosg::parse_data_string(def_json.get_string("RewardItem")));
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
TeamIndex::TeamIndex(const string& directory, const phosg::JSON& reward_defs_json)
: directory(directory),
next_team_id(1) {
TeamIndex::TeamIndex(const std::string& directory, const phosg::JSON& reward_defs_json)
: directory(directory), next_team_id(1) {
uint32_t reward_menu_item_id = 0;
for (const auto& it : reward_defs_json.as_list()) {
this->reward_defs.emplace_back(reward_menu_item_id++, *it);
@@ -251,24 +246,24 @@ TeamIndex::TeamIndex(const string& directory, const phosg::JSON& reward_defs_jso
return;
}
for (const auto& item : std::filesystem::directory_iterator(this->directory)) {
string filename = item.path().filename().string();
string file_path = this->directory + "/" + filename;
std::string filename = item.path().filename().string();
std::string file_path = this->directory + "/" + filename;
if (filename == "base.json") {
auto json = phosg::JSON::parse(phosg::load_file(file_path));
this->next_team_id = json.get_int("NextTeamID");
} else if (filename.ends_with(".json")) {
try {
uint32_t team_id = stoul(filename.substr(0, filename.size() - 5), nullptr, 16);
auto team = make_shared<Team>(team_id);
auto team = std::make_shared<Team>(team_id);
team->load_config();
try {
team->load_flag();
} catch (const exception& e) {
} catch (const std::exception& e) {
static_game_data_log.warning_f("Failed to load flag for team {:08X}: {}", team_id, e.what());
}
this->add_to_indexes(team);
static_game_data_log.info_f("Indexed team {:08X} ({}) ({} members)", team_id, team->name, team->num_members());
} catch (const exception& e) {
} catch (const std::exception& e) {
static_game_data_log.warning_f("Failed to index team from {}: {}", filename, e.what());
}
}
@@ -279,40 +274,41 @@ size_t TeamIndex::count() const {
return this->id_to_team.size();
}
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_id(uint32_t team_id) const {
std::shared_ptr<const TeamIndex::Team> TeamIndex::get_by_id(uint32_t team_id) const {
try {
return this->id_to_team.at(team_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_name(const string& name) const {
std::shared_ptr<const TeamIndex::Team> TeamIndex::get_by_name(const std::string& name) const {
try {
return this->name_to_team.at(name);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_account_id(uint32_t account_id) const {
std::shared_ptr<const TeamIndex::Team> TeamIndex::get_by_account_id(uint32_t account_id) const {
try {
return this->account_id_to_team.at(account_id);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
return nullptr;
}
}
vector<shared_ptr<const TeamIndex::Team>> TeamIndex::all() const {
vector<shared_ptr<const Team>> ret;
std::vector<std::shared_ptr<const TeamIndex::Team>> TeamIndex::all() const {
std::vector<std::shared_ptr<const Team>> ret;
for (const auto& it : this->id_to_team) {
ret.emplace_back(it.second);
}
return ret;
}
shared_ptr<const TeamIndex::Team> TeamIndex::create(const string& name, uint32_t master_account_id, const string& master_name) {
auto team = make_shared<Team>(this->next_team_id++);
std::shared_ptr<const TeamIndex::Team> TeamIndex::create(
const std::string& name, uint32_t master_account_id, const std::string& master_name) {
auto team = std::make_shared<Team>(this->next_team_id++);
phosg::save_file(this->directory + "/base.json", phosg::JSON::dict({{"NextTeamID", this->next_team_id}}).serialize());
Team::Member m;
@@ -338,17 +334,17 @@ void TeamIndex::disband(uint32_t team_id) {
void TeamIndex::rename(uint32_t team_id, const std::string& new_team_name) {
auto team = this->id_to_team.at(team_id);
if (!this->name_to_team.emplace(new_team_name, team).second) {
throw runtime_error("team name is already in use");
throw std::runtime_error("team name is already in use");
}
this->name_to_team.erase(team->name);
team->name = new_team_name;
team->save_config();
}
void TeamIndex::add_member(uint32_t team_id, uint32_t account_id, const string& name) {
void TeamIndex::add_member(uint32_t team_id, uint32_t account_id, const std::string& name) {
auto team = this->id_to_team.at(team_id);
if (!this->account_id_to_team.emplace(account_id, team).second) {
throw runtime_error("user is already in a different team");
throw std::runtime_error("user is already in a different team");
}
Team::Member m;
@@ -364,7 +360,7 @@ void TeamIndex::add_member(uint32_t team_id, uint32_t account_id, const string&
void TeamIndex::remove_member(uint32_t account_id) {
auto team_it = this->account_id_to_team.find(account_id);
if (team_it == this->account_id_to_team.end()) {
throw runtime_error("client is not in any team");
throw std::runtime_error("client is not in any team");
}
auto team = std::move(team_it->second);
this->account_id_to_team.erase(team_it);
@@ -401,7 +397,7 @@ bool TeamIndex::promote_leader(uint32_t master_account_id, uint32_t leader_accou
auto team = this->account_id_to_team.at(master_account_id);
auto& master_m = team->members.at(master_account_id);
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
throw runtime_error("incorrect master account ID");
throw std::runtime_error("incorrect master account ID");
}
auto& other_m = team->members.at(leader_account_id);
@@ -417,7 +413,7 @@ bool TeamIndex::demote_leader(uint32_t master_account_id, uint32_t leader_accoun
auto team = this->account_id_to_team.at(master_account_id);
auto& master_m = team->members.at(master_account_id);
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
throw runtime_error("incorrect master account ID");
throw std::runtime_error("incorrect master account ID");
}
auto& other_m = team->members.at(leader_account_id);
@@ -433,7 +429,7 @@ void TeamIndex::change_master(uint32_t master_account_id, uint32_t new_master_ac
auto team = this->account_id_to_team.at(master_account_id);
auto& master_m = team->members.at(master_account_id);
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
throw runtime_error("incorrect master account ID");
throw std::runtime_error("incorrect master account ID");
}
auto& new_master_m = team->members.at(new_master_account_id);
@@ -445,10 +441,10 @@ void TeamIndex::change_master(uint32_t master_account_id, uint32_t new_master_ac
team->save_config();
}
void TeamIndex::buy_reward(uint32_t team_id, const string& key, uint32_t points, Team::RewardFlag reward_flag) {
void TeamIndex::buy_reward(uint32_t team_id, const std::string& key, uint32_t points, Team::RewardFlag reward_flag) {
auto team = this->id_to_team.at(team_id);
if (team->spent_points + points > team->points) {
throw runtime_error("not enough points available");
throw std::runtime_error("not enough points available");
}
team->reward_keys.emplace(key);
team->spent_points += points;
@@ -458,13 +454,13 @@ void TeamIndex::buy_reward(uint32_t team_id, const string& key, uint32_t points,
team->save_config();
}
void TeamIndex::add_to_indexes(shared_ptr<Team> team) {
void TeamIndex::add_to_indexes(std::shared_ptr<Team> team) {
if (!this->id_to_team.emplace(team->team_id, team).second) {
throw runtime_error("team ID is already in use");
throw std::runtime_error("team ID is already in use");
}
if (!this->name_to_team.emplace(team->name, team).second) {
this->id_to_team.erase(team->team_id);
throw runtime_error("team name is already in use");
throw std::runtime_error("team name is already in use");
}
for (const auto& [_, member] : team->members) {
if (!this->account_id_to_team.emplace(member.account_id, team).second) {
@@ -474,7 +470,7 @@ void TeamIndex::add_to_indexes(shared_ptr<Team> team) {
}
}
void TeamIndex::remove_from_indexes(shared_ptr<Team> team) {
void TeamIndex::remove_from_indexes(std::shared_ptr<Team> team) {
this->id_to_team.erase(team->team_id);
this->name_to_team.erase(team->name);
for (const auto& it : team->members) {
+47 -49
View File
@@ -9,15 +9,13 @@
#include <phosg/Strings.hh>
#include <vector>
using namespace std;
const iconv_t TextTranscoder::INVALID_IC = (iconv_t)(-1);
const size_t TextTranscoder::FAILURE_RESULT = static_cast<size_t>(-1);
TextTranscoder::TextTranscoder(const char* to, const char* from) : ic(iconv_open(to, from)) {
if (ic == this->INVALID_IC) {
string error_str = phosg::string_for_error(errno);
throw runtime_error(std::format("failed to initialize {} -> {} text converter: {}", from, to, error_str));
throw std::runtime_error(std::format("failed to initialize {} -> {} text converter: {}",
from, to, phosg::string_for_error(errno)));
}
}
@@ -45,32 +43,32 @@ TextTranscoder::Result TextTranscoder::operator()(
if (ret == this->FAILURE_RESULT) {
switch (errno) {
case EILSEQ: {
string custom_result = this->on_untranslatable(&src, &src_bytes);
std::string custom_result = this->on_untranslatable(&src, &src_bytes);
if (custom_result.empty()) {
throw runtime_error(std::format("untranslatable character at position 0x{:X}", bytes_read));
throw std::runtime_error(std::format("untranslatable character at position 0x{:X}", bytes_read));
} else if (custom_result.size() <= dest_bytes) {
memcpy(dest, custom_result.data(), custom_result.size());
dest = reinterpret_cast<char*>(dest) + custom_result.size();
dest_bytes -= custom_result.size();
} else if (!truncate_oversize_result) {
throw runtime_error("string does not fit in buffer");
throw std::runtime_error("string does not fit in buffer");
}
break;
}
case EINVAL:
throw runtime_error(std::format("incomplete multibyte sequence at position 0x{:X}", bytes_read));
throw std::runtime_error(std::format("incomplete multibyte sequence at position 0x{:X}", bytes_read));
case E2BIG:
if (!truncate_oversize_result) {
throw runtime_error("string does not fit in buffer");
throw std::runtime_error("string does not fit in buffer");
} else {
src_bytes = 0;
break;
}
default:
throw runtime_error("transcoding failed: " + phosg::string_for_error(errno));
throw std::runtime_error("transcoding failed: " + phosg::string_for_error(errno));
}
} else if (src_bytes_before == src_bytes) {
throw runtime_error("could not transcode any characters");
throw std::runtime_error("could not transcode any characters");
}
}
@@ -79,15 +77,15 @@ TextTranscoder::Result TextTranscoder::operator()(
return Result{.bytes_read = bytes_read, .bytes_written = bytes_written};
}
string TextTranscoder::operator()(const void* src, size_t src_bytes) {
std::string TextTranscoder::operator()(const void* src, size_t src_bytes) {
// Clear any conversion state left over from the previous call
iconv(this->ic, nullptr, nullptr, nullptr, nullptr);
const void* orig_src = src;
deque<string> blocks;
std::deque<std::string> blocks;
while (src_bytes > 0) {
// Assume 2x input size on average, but always allocate at least 8 bytes
string& block = blocks.emplace_back(max<size_t>((src_bytes << 1), 8), '\0');
std::string& block = blocks.emplace_back(std::max<size_t>((src_bytes << 1), 8), '\0');
char* dest = block.data();
size_t dest_size = block.size();
size_t src_bytes_before = src_bytes;
@@ -103,29 +101,29 @@ string TextTranscoder::operator()(const void* src, size_t src_bytes) {
if (ret == this->FAILURE_RESULT) {
switch (errno) {
case EILSEQ: {
string custom_result = this->on_untranslatable(&src, &src_bytes);
std::string custom_result = this->on_untranslatable(&src, &src_bytes);
if (custom_result.empty()) {
throw runtime_error(std::format("untranslatable character at position {}", bytes_read));
throw std::runtime_error(std::format("untranslatable character at position {}", bytes_read));
}
blocks.emplace_back(std::move(custom_result));
break;
}
case EINVAL:
throw runtime_error(std::format("incomplete multibyte sequence at position {}", bytes_read));
throw std::runtime_error(std::format("incomplete multibyte sequence at position {}", bytes_read));
case E2BIG:
break;
default:
throw runtime_error("transcoding failed: " + phosg::string_for_error(errno));
throw std::runtime_error("transcoding failed: " + phosg::string_for_error(errno));
}
} else if (src_bytes_before == src_bytes) {
throw runtime_error("could not transcode any characters");
throw std::runtime_error("could not transcode any characters");
}
}
return phosg::join(blocks, "");
}
string TextTranscoder::operator()(const string& data) {
std::string TextTranscoder::operator()(const std::string& data) {
return this->operator()(data.data(), data.size());
}
@@ -136,7 +134,7 @@ std::string TextTranscoder::on_untranslatable(const void**, size_t*) const {
TextTranscoderCustomSJISToUTF8::TextTranscoderCustomSJISToUTF8() : TextTranscoder("UTF-8", "SHIFT_JIS") {}
std::string encode_utf8_char(uint32_t ch) {
string ret;
std::string ret;
if (ch < 0x80) {
ret.push_back(ch);
} else if (ch < 0x800) {
@@ -152,14 +150,14 @@ std::string encode_utf8_char(uint32_t ch) {
ret.push_back(0x80 | ((ch >> 6) & 0x3F));
ret.push_back(0x80 | (ch & 0x3F));
} else {
throw runtime_error("unencodable Unicode code point");
throw std::runtime_error("unencodable Unicode code point");
}
return ret;
}
uint32_t decode_utf8_char(const void** vdata, size_t* size) {
if (*size == 0) {
throw runtime_error("incomplete UTF-8 character");
throw std::runtime_error("incomplete UTF-8 character");
}
const uint8_t* data = reinterpret_cast<const uint8_t*>(*vdata);
@@ -169,27 +167,27 @@ uint32_t decode_utf8_char(const void** vdata, size_t* size) {
return *data;
} else if ((data[0] & 0xE0) == 0xC0) {
if ((*size < 2) || ((data[1] & 0xC0) != 0x80)) {
throw runtime_error("incomplete UTF-8 character");
throw std::runtime_error("incomplete UTF-8 character");
}
(*size) -= 2;
*vdata = data + 2;
return ((data[0] & 0x1F) << 6) | (data[1] & 0x3F);
} else if ((data[0] & 0xF0) == 0xE0) {
if ((*size < 3) || ((data[1] & 0xC0) != 0x80) || ((data[2] & 0xC0) != 0x80)) {
throw runtime_error("incomplete UTF-8 character");
throw std::runtime_error("incomplete UTF-8 character");
}
(*size) -= 3;
*vdata = data + 3;
return ((data[0] & 0x0F) << 12) | ((data[1] & 0x3F) << 6) | (data[2] & 0x3F);
} else if ((data[0] & 0xF8) == 0xF0) {
if ((*size < 4) || ((data[1] & 0xC0) != 0x80) || ((data[2] & 0xC0) != 0x80) || ((data[3] & 0xC0) != 0x80)) {
throw runtime_error("incomplete UTF-8 character");
throw std::runtime_error("incomplete UTF-8 character");
}
(*size) -= 4;
*vdata = data + 4;
return ((data[0] & 0x07) << 18) | ((data[1] & 0x3F) << 12) | ((data[2] & 0x3F) << 6) | (data[3] & 0x3F);
} else {
throw runtime_error("invalid UTF-8 character");
throw std::runtime_error("invalid UTF-8 character");
}
}
@@ -208,7 +206,7 @@ std::string TextTranscoderCustomSJISToUTF8::on_untranslatable(const void** vsrc,
return "";
}
string ret;
std::string ret;
if (src[1] < 0x40) {
return "";
} else if (src[1] == 0x40) { // F040 -> U+2665
@@ -236,7 +234,7 @@ std::string TextTranscoderUTF8ToCustomSJIS::on_untranslatable(const void** src,
uint32_t ch;
try {
ch = decode_utf8_char(src, size);
} catch (const runtime_error&) {
} catch (const std::runtime_error&) {
return "";
}
@@ -245,11 +243,11 @@ std::string TextTranscoderUTF8ToCustomSJIS::on_untranslatable(const void** src,
} else if (ch == 0x24EA) { // U+24EA -> F041
return "\xF0\x41";
} else if (ch >= 0x2460 && ch <= 0x2468) { // U+2460-U+2468 -> F042-F04A
string ret("\xF0");
std::string ret("\xF0");
ret.push_back(0x42 + (ch - 0x2460));
return ret;
} else if (ch >= 0x1D4D0 && ch <= 0x1D4E9) { // U+1D4D0-U+1D4E9 -> F04B-F064
string ret("\xF0");
std::string ret("\xF0");
ret.push_back(0x4B + (ch - 0x1D4D0));
return ret;
} else {
@@ -270,29 +268,29 @@ TextTranscoder tt_utf8_to_utf16("UTF-16LE", "UTF-8");
TextTranscoder tt_ascii_to_utf8("UTF-8", "ASCII");
TextTranscoder tt_utf8_to_ascii("ASCII", "UTF-8");
string tt_encode_marked_optional(const string& utf8, Language default_language, bool is_utf16) {
std::string tt_encode_marked_optional(const std::string& utf8, Language default_language, bool is_utf16) {
if (is_utf16) {
return tt_utf8_to_utf16(utf8);
} else {
if (default_language == Language::JAPANESE) {
try {
return tt_utf8_to_sega_sjis(utf8);
} catch (const exception& e) {
} catch (const std::exception& e) {
return "\tE" + tt_utf8_to_8859(utf8);
}
} else {
try {
return tt_utf8_to_8859(utf8);
} catch (const exception& e) {
} catch (const std::exception& e) {
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
}
}
}
}
string tt_encode_marked(const string& utf8, Language default_language, bool is_utf16) {
std::string tt_encode_marked(const std::string& utf8, Language default_language, bool is_utf16) {
if (is_utf16) {
string to_encode = "\t";
std::string to_encode = "\t";
to_encode += marker_for_language(default_language);
to_encode += utf8;
return tt_utf8_to_utf16(to_encode);
@@ -300,22 +298,22 @@ string tt_encode_marked(const string& utf8, Language default_language, bool is_u
if (default_language == Language::JAPANESE) {
try {
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
} catch (const exception& e) {
} catch (const std::exception& e) {
return "\tE" + tt_utf8_to_8859(utf8);
}
} else {
try {
return "\tE" + tt_utf8_to_8859(utf8);
} catch (const exception& e) {
} catch (const std::exception& e) {
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
}
}
}
}
string tt_decode_marked(const string& data, Language default_language, bool is_utf16) {
std::string tt_decode_marked(const std::string& data, Language default_language, bool is_utf16) {
if (is_utf16) {
string ret = tt_utf16_to_utf8(data);
std::string ret = tt_utf16_to_utf8(data);
if (ret.size() >= 2 && ret[0] == '\t' && is_language_marker_utf16(ret[1])) {
ret = ret.substr(2);
}
@@ -332,19 +330,19 @@ string tt_decode_marked(const string& data, Language default_language, bool is_u
}
}
string add_language_marker(const string& s, char marker) {
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;
std::string ret;
ret.push_back('\t');
ret.push_back(marker);
ret += s;
return ret;
}
string remove_language_marker(const string& s) {
std::string remove_language_marker(const std::string& s) {
if ((s.size() < 2) || (s[0] != '\t') || (s[1] == 'C')) {
return s;
}
@@ -394,7 +392,7 @@ size_t add_color_inplace(char* a, size_t max_chars) {
return d - orig_d;
}
void add_color_inplace(string& s) {
void add_color_inplace(std::string& s) {
s.resize(add_color_inplace(s.data(), s.size()));
}
@@ -425,7 +423,7 @@ void add_color(phosg::StringWriter& w, const char* src, size_t max_input_chars)
}
}
string add_color(const string& s) {
std::string add_color(const std::string& s) {
phosg::StringWriter w;
add_color(w, s.data(), s.size());
return std::move(w.str());
@@ -453,14 +451,14 @@ void remove_color(phosg::StringWriter& w, const char* src, size_t max_input_char
}
}
string remove_color(const string& s) {
std::string remove_color(const std::string& s) {
phosg::StringWriter w;
remove_color(w, s.data(), s.size());
return std::move(w.str());
}
string strip_color(const string& s) {
string ret;
std::string strip_color(const std::string& s) {
std::string ret;
for (size_t r = 0; r < s.size(); r++) {
if ((s[r] == '$' || s[r] == '\t') &&
(s[r + 1] == 'C') && (((s[r + 2] >= '0') && (s[r + 2] <= '9')) || (s[r + 2] == 'G') || (s[r + 2] == 'a'))) {
@@ -472,7 +470,7 @@ string strip_color(const string& s) {
return ret;
}
string escape_player_name(const string& name) {
std::string escape_player_name(const std::string& name) {
if (name.size() > 2 && name[0] == '\t' && name[1] != 'C') {
return remove_color(name.substr(2));
} else {
+47 -49
View File
@@ -14,8 +14,6 @@
#include "StaticGameData.hh"
#include "Text.hh"
using namespace std;
TextSet::TextSet(const phosg::JSON& json) {
for (const auto& coll_json : json.as_list()) {
auto& collection = this->collections.emplace_back();
@@ -57,7 +55,7 @@ const std::string& TextSet::get(size_t collection_index, size_t string_index) co
return this->get(collection_index).at(string_index);
}
const vector<string>& TextSet::get(size_t collection_index) const {
const std::vector<std::string>& TextSet::get(size_t collection_index) const {
return this->collections.at(collection_index);
}
@@ -106,12 +104,12 @@ void TextSet::ensure_collection_exists(size_t collection_index) {
}
}
UnicodeTextSet::UnicodeTextSet(const string& prs_data) {
string data = prs_decompress(prs_data);
UnicodeTextSet::UnicodeTextSet(const std::string& prs_data) {
std::string data = prs_decompress(prs_data);
phosg::StringReader r(data);
uint32_t num_collections = r.get_u32l();
deque<uint32_t> collection_sizes;
std::deque<uint32_t> collection_sizes;
while (collection_sizes.size() < num_collections) {
collection_sizes.emplace_back(r.get_u32l());
}
@@ -134,7 +132,7 @@ UnicodeTextSet::UnicodeTextSet(const string& prs_data) {
}
}
string UnicodeTextSet::serialize() const {
std::string UnicodeTextSet::serialize() const {
phosg::StringWriter header_w;
phosg::StringWriter data_w;
@@ -145,7 +143,7 @@ string UnicodeTextSet::serialize() const {
total_num_strings += collection.size();
}
unordered_map<string, uint32_t> encoded;
std::unordered_map<std::string, uint32_t> encoded;
size_t data_base_offset = (total_num_strings * 4) + header_w.size();
for (const auto& collection : this->collections) {
@@ -154,8 +152,7 @@ string UnicodeTextSet::serialize() const {
if (encoded_it == encoded.end()) {
uint32_t offset = data_base_offset + data_w.size();
encoded_it = encoded.emplace(s, offset).first;
string s_utf16 = tt_utf8_to_utf16(s);
data_w.write(s_utf16.data(), s_utf16.size());
data_w.write(tt_utf8_to_utf16(s));
data_w.put_u16(0);
while (data_w.size() & 3) {
data_w.put_u8(0);
@@ -177,7 +174,7 @@ BinaryTextSet::BinaryTextSet(const std::string& pr2_data, size_t collection_coun
// Annoyingly, there doesn't appear to be any bounds-checking on the language functions, so there are no counts of
// strings in each collection. We have to figure out where each collection ends by collecting all the relevant
// offsets in the file instead.
::set<uint32_t> used_offsets;
std::set<uint32_t> used_offsets;
size_t root_offset = has_rel_footer
? r.pget<RELFileFooter>(r.size() - 0x20).root_offset.load()
: (r.size() - collection_count * sizeof(le_uint32_t));
@@ -202,12 +199,12 @@ BinaryTextSet::BinaryTextSet(const std::string& pr2_data, size_t collection_coun
string_offset_offset += 4) {
collection.emplace_back(tt(r.pget_cstr(r.pget_u32l(string_offset_offset))));
}
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
}
}
}
BinaryTextAndKeyboardsSet::BinaryTextAndKeyboardsSet(const string& pr2_data, bool big_endian, bool is_sjis) {
BinaryTextAndKeyboardsSet::BinaryTextAndKeyboardsSet(const std::string& pr2_data, bool big_endian, bool is_sjis) {
if (big_endian) {
this->parse_t<true>(pr2_data, is_sjis);
} else {
@@ -224,7 +221,7 @@ BinaryTextAndKeyboardsSet::BinaryTextAndKeyboardsSet(const phosg::JSON& json) {
}
for (const auto& keyboard_json : json.at("keyboards").as_list()) {
auto& keyboard = this->keyboards.emplace_back(make_unique<Keyboard>());
auto& keyboard = this->keyboards.emplace_back(std::make_unique<Keyboard>());
for (size_t y = 0; y < keyboard->size(); y++) {
auto& row = keyboard->at(y);
const auto& row_json = keyboard_json->at(y);
@@ -267,14 +264,14 @@ void BinaryTextAndKeyboardsSet::set_keyboard(size_t kb_index, const Keyboard& kb
if (kb_index >= this->keyboards.size()) {
this->keyboards.resize(kb_index + 1);
}
this->keyboards[kb_index] = make_unique<Keyboard>(kb);
this->keyboards[kb_index] = std::make_unique<Keyboard>(kb);
}
void BinaryTextAndKeyboardsSet::resize_keyboards(size_t num_keyboards) {
this->keyboards.resize(num_keyboards);
}
pair<string, string> BinaryTextAndKeyboardsSet::serialize(bool big_endian, bool is_sjis) const {
std::pair<std::string, std::string> BinaryTextAndKeyboardsSet::serialize(bool big_endian, bool is_sjis) const {
if (big_endian) {
return this->serialize_t<true>(is_sjis);
} else {
@@ -283,7 +280,7 @@ pair<string, string> BinaryTextAndKeyboardsSet::serialize(bool big_endian, bool
}
template <bool BE>
void BinaryTextAndKeyboardsSet::parse_t(const string& pr2_data, bool is_sjis) {
void BinaryTextAndKeyboardsSet::parse_t(const std::string& pr2_data, bool is_sjis) {
auto& tt = is_sjis ? tt_sega_sjis_to_utf8 : tt_8859_to_utf8;
// The structure is as follows:
@@ -308,7 +305,7 @@ void BinaryTextAndKeyboardsSet::parse_t(const string& pr2_data, bool is_sjis) {
// Annoyingly, there doesn't appear to be any bounds-checking on the language functions, so there are no counts of
// strings in each collection. We have to figure out where each collection ends by collecting all the relevant
// offsets in the file instead.
::set<uint32_t> used_offsets;
std::set<uint32_t> used_offsets;
used_offsets.emplace(r.size() - 8);
uint32_t keyboard_index_offset = r.pget<U32T<BE>>(r.size() - 8);
@@ -320,7 +317,7 @@ void BinaryTextAndKeyboardsSet::parse_t(const string& pr2_data, bool is_sjis) {
while (this->keyboards.size() < num_keyboards) {
uint32_t keyboard_offset = r.pget<U32T<BE>>(keyboards_offset + 4 * this->keyboards.size());
used_offsets.emplace(keyboard_offset);
auto& kb = this->keyboards.emplace_back(make_unique<Keyboard>());
auto& kb = this->keyboards.emplace_back(std::make_unique<Keyboard>());
auto key_r = r.sub(keyboard_offset, sizeof(Keyboard));
for (size_t y = 0; y < kb->size(); y++) {
auto& row = kb->at(y);
@@ -348,11 +345,11 @@ void BinaryTextAndKeyboardsSet::parse_t(const string& pr2_data, bool is_sjis) {
}
template <bool BE>
pair<string, string> BinaryTextAndKeyboardsSet::serialize_t(bool is_sjis) const {
std::pair<std::string, std::string> BinaryTextAndKeyboardsSet::serialize_t(bool is_sjis) const {
auto& tt = is_sjis ? tt_utf8_to_sega_sjis : tt_utf8_to_8859;
phosg::StringWriter w;
::set<size_t> relocation_offsets;
std::set<size_t> relocation_offsets;
auto put_offset_u32 = [&](uint32_t v) {
relocation_offsets.emplace(w.size());
w.put<U32T<BE>>(v);
@@ -360,7 +357,7 @@ pair<string, string> BinaryTextAndKeyboardsSet::serialize_t(bool is_sjis) const
uint32_t collections_offset;
{
unordered_map<string, uint32_t> string_to_offset;
std::unordered_map<std::string, uint32_t> string_to_offset;
for (const auto& collection : this->collections) {
for (const auto& s : collection) {
if (string_to_offset.emplace(s, w.size()).second) {
@@ -373,7 +370,7 @@ pair<string, string> BinaryTextAndKeyboardsSet::serialize_t(bool is_sjis) const
}
}
vector<uint32_t> collection_offsets;
std::vector<uint32_t> collection_offsets;
for (const auto& collection : this->collections) {
collection_offsets.emplace_back(w.size());
for (const auto& s : collection) {
@@ -389,7 +386,7 @@ pair<string, string> BinaryTextAndKeyboardsSet::serialize_t(bool is_sjis) const
uint32_t keyboard_index_offset;
{
vector<uint32_t> keyboard_offsets;
std::vector<uint32_t> keyboard_offsets;
for (const auto& keyboard : this->keyboards) {
keyboard_offsets.emplace_back(w.size());
for (size_t y = 0; y < keyboard->size(); y++) {
@@ -426,39 +423,40 @@ pair<string, string> BinaryTextAndKeyboardsSet::serialize_t(bool is_sjis) const
size_t offset = 0;
for (size_t reloc_offset : relocation_offsets) {
if (reloc_offset & 3) {
throw logic_error("misaligned relocation");
throw std::logic_error("misaligned relocation");
}
size_t num_words = (reloc_offset - offset) >> 2;
if (num_words > 0xFFFF) {
throw runtime_error("relocation offset too far away");
throw std::runtime_error("relocation offset too far away");
}
reloc_w.put<U16T<BE>>(num_words);
offset = reloc_offset;
}
}
const string& pr2_data = w.str();
const string& pr3_data = reloc_w.str();
string pr2_compressed = prs_compress_optimal(pr2_data.data(), pr2_data.size());
string pr3_compressed = prs_compress_optimal(pr3_data.data(), pr3_data.size());
string pr2_ret = encrypt_pr2_data<BE>(pr2_compressed, pr2_data.size(), phosg::random_object<uint32_t>());
string pr3_ret = encrypt_pr2_data<BE>(pr3_compressed, pr3_data.size(), phosg::random_object<uint32_t>());
return make_pair(std::move(pr2_ret), std::move(pr3_ret));
const std::string& pr2_data = w.str();
const std::string& pr3_data = reloc_w.str();
std::string pr2_compressed = prs_compress_optimal(pr2_data.data(), pr2_data.size());
std::string pr3_compressed = prs_compress_optimal(pr3_data.data(), pr3_data.size());
std::string pr2_ret = encrypt_pr2_data<BE>(pr2_compressed, pr2_data.size(), phosg::random_object<uint32_t>());
std::string pr3_ret = encrypt_pr2_data<BE>(pr3_compressed, pr3_data.size(), phosg::random_object<uint32_t>());
return std::make_pair(std::move(pr2_ret), std::move(pr3_ret));
}
TextIndex::TextIndex(
const string& directory, function<shared_ptr<const string>(Version, const string&)> get_patch_file)
const std::string& directory,
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_patch_file)
: log("[TextIndex] ", static_game_data_log.min_level) {
if (!directory.empty()) {
auto add_version = [&](Version version, const string& subdirectory, function<shared_ptr<TextSet>(const string&, bool)> make_set) -> void {
static const map<string, Language> bintext_filenames({
auto add_version = [&](Version version, const std::string& subdirectory, std::function<std::shared_ptr<TextSet>(const std::string&, bool)> make_set) -> void {
static const std::map<std::string, Language> bintext_filenames({
{"TextJapanese.pr2", Language::JAPANESE},
{"TextEnglish.pr2", Language::ENGLISH},
{"TextGerman.pr2", Language::GERMAN},
{"TextFrench.pr2", Language::FRENCH},
{"TextSpanish.pr2", Language::SPANISH},
});
static const map<string, Language> unitext_filenames({
static const std::map<std::string, Language> unitext_filenames({
{"unitxt_j.prs", Language::JAPANESE}, // PC/BB Japanese
{"unitxt_e.prs", Language::ENGLISH}, // PC/BB English
{"unitxt_g.prs", Language::GERMAN}, // PC/BB German
@@ -473,11 +471,11 @@ TextIndex::TextIndex(
});
if (!uses_utf16(version)) {
for (const auto& [base_filename, language] : bintext_filenames) {
string file_path = directory + "/" + subdirectory + "/" + base_filename;
string json_path = file_path + ".json";
std::string file_path = directory + "/" + subdirectory + "/" + base_filename;
std::string json_path = file_path + ".json";
if (std::filesystem::is_regular_file(json_path)) {
this->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), name_for_language(language), json_path);
this->add_set(version, language, make_shared<BinaryTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
this->add_set(version, language, std::make_shared<BinaryTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
} else if (std::filesystem::is_regular_file(file_path)) {
this->log.debug_f("Loading {} {} binary text set from {}", phosg::name_for_enum(version), name_for_language(language), file_path);
this->add_set(version, language, make_set(phosg::load_file(file_path), language == Language::JAPANESE));
@@ -485,11 +483,11 @@ TextIndex::TextIndex(
}
} else {
for (const auto& [base_filename, language] : unitext_filenames) {
string file_path = directory + "/" + subdirectory + "/" + base_filename;
string json_path = file_path + ".json";
std::string file_path = directory + "/" + subdirectory + "/" + base_filename;
std::string json_path = file_path + ".json";
if (std::filesystem::is_regular_file(json_path)) {
this->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), name_for_language(language), json_path);
this->add_set(version, language, make_shared<UnicodeTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
this->add_set(version, language, std::make_shared<UnicodeTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
} else {
auto patch_file = get_patch_file ? get_patch_file(version, base_filename) : nullptr;
if (patch_file) {
@@ -506,12 +504,12 @@ TextIndex::TextIndex(
}
};
auto make_binary_dc112000 = +[](const string& data, bool is_sjis) { return make_shared<BinaryTextSet>(data, 21, true, is_sjis); };
auto make_binary_dcnte_dcv1 = +[](const string& data, bool is_sjis) { return make_shared<BinaryTextSet>(data, 26, true, is_sjis); };
auto make_binary_dcv2 = +[](const string& data, bool is_sjis) { return make_shared<BinaryTextSet>(data, 37, false, is_sjis); };
auto make_binary_gc = +[](const string& data, bool is_sjis) { return make_shared<BinaryTextAndKeyboardsSet>(data, true, is_sjis); };
auto make_binary_xb = +[](const string& data, bool is_sjis) { return make_shared<BinaryTextAndKeyboardsSet>(data, false, is_sjis); };
auto make_unitxt = +[](const string& data, bool) { return make_shared<UnicodeTextSet>(data); };
auto make_binary_dc112000 = +[](const std::string& data, bool is_sjis) { return std::make_shared<BinaryTextSet>(data, 21, true, is_sjis); };
auto make_binary_dcnte_dcv1 = +[](const std::string& data, bool is_sjis) { return std::make_shared<BinaryTextSet>(data, 26, true, is_sjis); };
auto make_binary_dcv2 = +[](const std::string& data, bool is_sjis) { return std::make_shared<BinaryTextSet>(data, 37, false, is_sjis); };
auto make_binary_gc = +[](const std::string& data, bool is_sjis) { return std::make_shared<BinaryTextAndKeyboardsSet>(data, true, is_sjis); };
auto make_binary_xb = +[](const std::string& data, bool is_sjis) { return std::make_shared<BinaryTextAndKeyboardsSet>(data, false, is_sjis); };
auto make_unitxt = +[](const std::string& data, bool) { return std::make_shared<UnicodeTextSet>(data); };
add_version(Version::DC_NTE, "dc-nte", make_binary_dcnte_dcv1);
add_version(Version::DC_11_2000, "dc-11-2000", make_binary_dc112000);
+9 -11
View File
@@ -6,8 +6,6 @@
#include "Client.hh"
using namespace std;
template <>
const char* phosg::name_for_enum<Version>(Version v) {
switch (v) {
@@ -40,7 +38,7 @@ const char* phosg::name_for_enum<Version>(Version v) {
case Version::BB_V4:
return "BB_V4";
default:
throw runtime_error("unknown version");
throw std::runtime_error("unknown version");
}
}
@@ -75,7 +73,7 @@ Version phosg::enum_for_name<Version>(const char* name) {
} else if (!strcmp(name, "BB_V4") || !strcasecmp(name, "bb")) {
return Version::BB_V4;
} else {
throw invalid_argument("incorrect version name");
throw std::invalid_argument("incorrect version name");
}
}
@@ -91,7 +89,7 @@ const char* phosg::name_for_enum<ServerBehavior>(ServerBehavior behavior) {
case ServerBehavior::PATCH_SERVER_BB:
return "patch_server_bb";
}
throw logic_error("invalid server behavior");
throw std::logic_error("invalid server behavior");
}
template <>
@@ -105,7 +103,7 @@ ServerBehavior phosg::enum_for_name<ServerBehavior>(const char* name) {
} else if (!strcasecmp(name, "patch_server_bb") || !strcasecmp(name, "patch_bb")) {
return ServerBehavior::PATCH_SERVER_BB;
} else {
throw invalid_argument(std::format("incorrect server behavior name: {}", name));
throw std::invalid_argument(std::format("incorrect server behavior name: {}", name));
}
}
@@ -186,7 +184,7 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi
default:
return SPECIFIC_VERSION_GC_V3_INDETERMINATE; // 3O__; need to send VersionDetect
}
throw logic_error("this should be impossible");
throw std::logic_error("this should be impossible");
case Version::GC_EP3_NTE:
return SPECIFIC_VERSION_GC_EP3_NTE; // 3SJT
case Version::GC_EP3:
@@ -261,8 +259,8 @@ uint32_t specific_version_for_str(const std::string& s) {
}
}
string str_for_specific_version(uint32_t specific_version) {
string ret;
std::string str_for_specific_version(uint32_t specific_version) {
std::string ret;
for (size_t z = 0; z < 4; z++) {
char ch = specific_version >> (24 - (z << 3));
ret.push_back(isalnum(ch) ? ch : '_');
@@ -301,7 +299,7 @@ const char* file_path_token_for_version(Version version) {
case Version::BB_V4:
return "bb-v4";
default:
throw runtime_error("invalid game version");
throw std::runtime_error("invalid game version");
}
}
@@ -325,6 +323,6 @@ uint64_t generate_random_hardware_id(Version version) {
case Version::BB_V4:
return 0;
default:
throw runtime_error("invalid game version");
throw std::runtime_error("invalid game version");
}
}
+29 -30
View File
@@ -7,11 +7,9 @@
#include "Compression.hh"
using namespace std;
template <typename RetT, typename ReadT>
static vector<RetT> read_direct_table(const phosg::StringReader& base_r, size_t offset, size_t count) {
vector<RetT> ret;
static std::vector<RetT> read_direct_table(const phosg::StringReader& base_r, size_t offset, size_t count) {
std::vector<RetT> ret;
auto entries_r = base_r.sub(offset, count * sizeof(ReadT));
while (!entries_r.eof()) {
ret.emplace_back(entries_r.get<ReadT>());
@@ -20,8 +18,8 @@ static vector<RetT> read_direct_table(const phosg::StringReader& base_r, size_t
}
template <typename RetT, typename ReadT, typename OffsetT>
static vector<vector<RetT>> read_indirect_table(const phosg::StringReader& base_r, size_t offset, size_t count) {
vector<vector<RetT>> ret;
static std::vector<std::vector<RetT>> read_indirect_table(const phosg::StringReader& base_r, size_t offset, size_t count) {
std::vector<std::vector<RetT>> ret;
auto pointers_r = base_r.sub(offset, sizeof(OffsetT) * 2 * count);
while (!pointers_r.eof()) {
uint32_t sub_offset = pointers_r.get<OffsetT>();
@@ -73,7 +71,7 @@ void WordSelectSet::parse_non_windows_t(const std::string& data, bool use_sjis)
{
auto string_offset_r = r.sub(root.strings_table, sizeof(U32T<BE>) * StringTableCount);
while (!string_offset_r.eof()) {
string raw_s = r.pget_cstr(string_offset_r.template get<U32T<BE>>());
std::string raw_s = r.pget_cstr(string_offset_r.template get<U32T<BE>>());
this->strings.emplace_back(use_sjis ? tt_sega_sjis_to_utf8(raw_s) : tt_8859_to_utf8(raw_s));
}
}
@@ -89,7 +87,7 @@ void WordSelectSet::parse_non_windows_t(const std::string& data, bool use_sjis)
template <typename RootT, size_t TokenCount>
void WordSelectSet::parse_windows_t(const std::string& data, const std::vector<std::string>* unitxt_collection) {
if (!unitxt_collection) {
throw runtime_error("a unitxt collection is required");
throw std::runtime_error("a unitxt collection is required");
}
phosg::StringReader r(data);
@@ -104,13 +102,14 @@ void WordSelectSet::parse_windows_t(const std::string& data, const std::vector<s
// this->table6 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table6, Table6Count);
}
WordSelectSet::WordSelectSet(const string& data, Version version, const vector<string>* unitxt_collection, bool use_sjis) {
WordSelectSet::WordSelectSet(
const std::string& data, Version version, const std::vector<std::string>* unitxt_collection, bool use_sjis) {
switch (version) {
case Version::DC_NTE: {
if (data.size() < 4) {
throw runtime_error("data is too small");
throw std::runtime_error("data is too small");
}
string decrypted = data.substr(0, data.size() - 4);
std::string decrypted = data.substr(0, data.size() - 4);
uint32_t seed = *reinterpret_cast<const le_uint32_t*>(data.data() + data.size() - 4);
PSOV2Encryption crypt(seed);
crypt.decrypt(decrypted);
@@ -145,11 +144,11 @@ WordSelectSet::WordSelectSet(const string& data, Version version, const vector<s
this->parse_windows_t<BBRoot, 0x68C>(decrypt_and_decompress_pr2_data<false>(data), unitxt_collection);
break;
default:
throw runtime_error("unsupported word select data version");
throw std::runtime_error("unsupported word select data version");
}
}
const string& WordSelectSet::string_for_token(uint16_t token_id) const {
const std::string& WordSelectSet::string_for_token(uint16_t token_id) const {
return this->strings.at(this->token_id_to_string_id.at(token_id));
}
@@ -179,9 +178,9 @@ WordSelectTable::WordSelectTable(
const WordSelectSet& gc_ep3_ws,
const WordSelectSet& xb_v3_ws,
const WordSelectSet& bb_v4_ws,
const vector<vector<string>>& name_alias_lists) {
const std::vector<std::vector<std::string>>& name_alias_lists) {
unordered_map<string, string> name_to_canonical_name;
std::unordered_map<std::string, std::string> name_to_canonical_name;
for (const auto& alias_list : name_alias_lists) {
if (alias_list.size() < 2) {
continue;
@@ -193,20 +192,20 @@ WordSelectTable::WordSelectTable(
}
}
vector<shared_ptr<Token>> dynamic_tokens;
std::vector<std::shared_ptr<Token>> dynamic_tokens;
{
for (size_t z = 0; z < 12; z++) {
auto& token = dynamic_tokens.emplace_back(make_shared<Token>());
auto& token = dynamic_tokens.emplace_back(std::make_shared<Token>());
token->canonical_name = std::format("__PLAYER_{}_NAME__", z);
this->name_to_token.emplace(token->canonical_name, token);
}
auto& token = dynamic_tokens.emplace_back(make_shared<Token>());
auto& token = dynamic_tokens.emplace_back(std::make_shared<Token>());
token->canonical_name = "__BLANK__";
this->name_to_token.emplace(token->canonical_name, token);
}
static_assert(NUM_NON_PATCH_VERSIONS == 12, "Don\'t forget to update the WordSelectTable constructor");
array<const WordSelectSet*, NUM_NON_PATCH_VERSIONS> ws_sets = {
std::array<const WordSelectSet*, NUM_NON_PATCH_VERSIONS> ws_sets = {
&dc_nte_ws, &dc_112000_ws, &dc_v1_ws, &dc_v2_ws, &pc_nte_ws, &pc_v2_ws, &gc_nte_ws, &gc_v3_ws, &gc_ep3_nte_ws,
&gc_ep3_ws, &xb_v3_ws, &bb_v4_ws};
@@ -217,18 +216,18 @@ WordSelectTable::WordSelectTable(
index.reserve(ws_set.num_tokens());
for (size_t token_id = 0; token_id < ws_set.num_tokens(); token_id++) {
const string& str = ws_set.string_for_token(token_id);
const std::string& str = ws_set.string_for_token(token_id);
string canonical_name;
std::string canonical_name;
try {
canonical_name = name_to_canonical_name.at(str);
} catch (const out_of_range&) {
} catch (const std::out_of_range&) {
canonical_name = str;
}
auto token_it = this->name_to_token.find(canonical_name);
if (token_it == this->name_to_token.end()) {
token_it = this->name_to_token.emplace(canonical_name, make_shared<Token>()).first;
token_it = this->name_to_token.emplace(canonical_name, std::make_shared<Token>()).first;
token_it->second->canonical_name = std::move(canonical_name);
}
token_it->second->slot_for_version(version) = token_id;
@@ -255,8 +254,8 @@ void WordSelectTable::print(FILE* stream) const {
phosg::fwrite_fmt(stream, "{:04X} ", token->values_by_version[z]);
}
}
string serialized = phosg::JSON(token->canonical_name).serialize(phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY);
phosg::fwrite_fmt(stream, "{}\n", serialized);
phosg::fwrite_fmt(stream, "{}\n",
phosg::JSON(token->canonical_name).serialize(phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
}
}
@@ -273,8 +272,8 @@ void WordSelectTable::print_index(FILE* stream, Version v) const {
phosg::fwrite_fmt(stream, "{:04X} ", token->values_by_version[z]);
}
}
string serialized = phosg::JSON(token->canonical_name).serialize(phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY);
phosg::fwrite_fmt(stream, "{}\n", serialized);
phosg::fwrite_fmt(stream, "{}\n",
phosg::JSON(token->canonical_name).serialize(phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
}
}
@@ -287,7 +286,7 @@ void WordSelectTable::validate(const WordSelectMessage& msg, Version version) co
}
const auto& token = index.at(msg.tokens[z]);
if (!token) {
throw runtime_error(std::format("token {:04X} does not exist in the index", msg.tokens[z]));
throw std::runtime_error(std::format("token {:04X} does not exist in the index", msg.tokens[z]));
}
}
}
@@ -303,11 +302,11 @@ WordSelectMessage WordSelectTable::translate(
} else {
const auto& token = index.at(msg.tokens[z]);
if (!token) {
throw runtime_error(std::format("token {:04X} does not exist in the index", msg.tokens[z]));
throw std::runtime_error(std::format("token {:04X} does not exist in the index", msg.tokens[z]));
}
ret.tokens[z] = token->slot_for_version(to_version);
if (ret.tokens[z] == 0xFFFF) {
throw runtime_error(std::format("token {:04X} has no translation", msg.tokens[z]));
throw std::runtime_error(std::format("token {:04X} has no translation", msg.tokens[z]));
}
}
}