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
+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());