document contents of BugFixes patch

This commit is contained in:
Martin Michelsen
2025-10-12 23:37:42 -07:00
parent d9d33c2d65
commit 90de571457
6 changed files with 1000 additions and 1221 deletions
+107 -70
View File
@@ -108,6 +108,111 @@ bool CompiledFunctionCode::is_big_endian() const {
return this->arch == Architecture::POWERPC;
}
static unordered_map<uint32_t, std::string> preprocess_function_code(const std::string& text) {
auto parse_specific_version_list = +[](std::string&& text) -> vector<uint32_t> {
phosg::strip_whitespace(text);
vector<uint32_t> ret;
for (auto& vers_token : phosg::split(text, ' ')) {
phosg::strip_whitespace(vers_token);
if (vers_token.empty()) {
continue;
}
if (vers_token.size() != 4) {
throw std::runtime_error("invalid specific_version: " + vers_token);
}
ret.emplace_back(*reinterpret_cast<const be_uint32_t*>(vers_token.data()));
}
return ret;
};
// Find a .versions directive and populate specific_versions
vector<uint32_t> specific_versions;
auto lines = phosg::split(text, '\n');
for (auto& line : lines) {
if (line.starts_with(".versions ")) {
if (!specific_versions.empty()) {
throw std::runtime_error("multiple .versions directives in file");
}
specific_versions = parse_specific_version_list(line.substr(10));
if (specific_versions.empty()) {
throw std::runtime_error(".versions directive does not specify any versions");
}
line.clear();
}
}
// If there's no .versions directive, just return the text as-is
if (specific_versions.empty()) {
return {{0, std::move(text)}};
}
vector<deque<string>> version_lines;
version_lines.resize(specific_versions.size());
size_t line_num = 1;
vector<uint32_t> current_only_versions;
unordered_set<uint32_t> current_only_versions_set;
for (auto& line : lines) {
phosg::strip_whitespace(line);
if (line.starts_with(".only_versions ")) {
current_only_versions = parse_specific_version_list(line.substr(15));
current_only_versions_set.clear();
for (uint32_t specific_version : current_only_versions) {
current_only_versions_set.emplace(specific_version);
}
} else if (line == ".all_versions") {
current_only_versions.clear();
current_only_versions_set.clear();
} else {
size_t vers_offset = line.find("<VERS ");
if (vers_offset == string::npos) {
for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) {
if (current_only_versions.empty() || current_only_versions_set.count(specific_versions[vers_index])) {
version_lines[vers_index].emplace_back(line);
} else {
version_lines[vers_index].emplace_back("");
}
}
} else {
size_t token_index = 0;
for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) {
if (current_only_versions.empty() || current_only_versions_set.count(specific_versions[vers_index])) {
string version_line = line;
size_t vers_offset = line.find("<VERS ");
while (vers_offset != string::npos) {
size_t end_offset = version_line.find('>', vers_offset + 6);
if (end_offset == string::npos) {
throw runtime_error(std::format("(line {}) unterminated <VERS> replacement", line_num));
}
auto tokens = phosg::split(version_line.substr(vers_offset + 6, end_offset - vers_offset - 6), ' ');
if (tokens.size() <= token_index) {
throw runtime_error(std::format("(line {}) invalid <VERS> replacement", line_num));
}
version_line = version_line.substr(0, vers_offset) + tokens.at(token_index) + version_line.substr(end_offset + 1);
vers_offset = version_line.find("<VERS ");
}
version_lines[vers_index].emplace_back(version_line);
token_index++;
} else {
version_lines[vers_index].emplace_back("");
}
}
}
}
line_num++;
}
unordered_map<uint32_t, string> ret;
for (size_t z = 0; z < specific_versions.size(); z++) {
ret.emplace(specific_versions[z], phosg::join(version_lines.at(z), "\n"));
}
return ret;
}
static vector<shared_ptr<CompiledFunctionCode>> compile_function_code(
CompiledFunctionCode::Architecture arch,
const string& function_directory,
@@ -169,78 +274,10 @@ static vector<shared_ptr<CompiledFunctionCode>> compile_function_code(
throw runtime_error("data not found for include: " + name + " (from " + asm_filename + " or " + bin_filename + ")");
};
// Handle VERS tokens
vector<uint32_t> specific_versions;
auto lines = phosg::split(text, '\n');
for (auto& line : lines) {
if (line.starts_with(".versions ")) {
if (!specific_versions.empty()) {
throw std::runtime_error("multiple .versions directives in file");
}
for (auto& vers_token : phosg::split(line.substr(10), ' ')) {
phosg::strip_whitespace(vers_token);
if (vers_token.empty()) {
continue;
}
if (vers_token.size() != 4) {
throw std::runtime_error("invalid token in .version directive: " + vers_token);
}
specific_versions.emplace_back(*reinterpret_cast<const be_uint32_t*>(vers_token.data()));
}
line.clear();
}
}
// Preprocess <VERS> tokens in the text if a .versions directive was given
vector<string> version_texts;
if (specific_versions.empty()) {
specific_versions.emplace_back(0);
version_texts.emplace_back(text);
} else {
vector<deque<string>> version_lines;
version_lines.resize(specific_versions.size());
size_t line_num = 1;
for (const auto& line : lines) {
size_t vers_offset = line.find("<VERS ");
if (vers_offset == string::npos) {
for (auto& lines : version_lines) {
lines.emplace_back(line);
}
} else {
for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) {
string version_line = line;
size_t vers_offset = line.find("<VERS ");
while (vers_offset != string::npos) {
size_t end_offset = version_line.find('>', vers_offset + 6);
if (end_offset == string::npos) {
throw runtime_error(std::format("(line {}) unterminated <VERS> replacement", line_num));
}
auto tokens = phosg::split(version_line.substr(vers_offset + 6, end_offset - vers_offset - 6), ' ');
if (tokens.size() != specific_versions.size()) {
throw runtime_error(std::format("(line {}) invalid <VERS> replacement", line_num));
}
version_line = version_line.substr(0, vers_offset) + tokens.at(vers_index) + version_line.substr(end_offset + 1);
vers_offset = version_line.find("<VERS ");
}
version_lines[vers_index].emplace_back(version_line);
}
}
line_num++;
}
for (const auto& lines : version_lines) {
version_texts.emplace_back(phosg::join(lines, "\n"));
}
}
auto version_texts = preprocess_function_code(text);
vector<shared_ptr<CompiledFunctionCode>> ret;
for (size_t vers_index = 0; vers_index < specific_versions.size(); vers_index++) {
uint32_t specific_version = specific_versions[vers_index];
const auto& version_text = version_texts.at(vers_index);
for (const auto& [specific_version, version_text] : version_texts) {
try {
ResourceDASM::EmulatorBase::AssembleResult assembled;
if (arch == CompiledFunctionCode::Architecture::POWERPC) {
+10 -6
View File
@@ -1714,19 +1714,23 @@ Action a_assemble_quest_script(
Action a_assemble_all_patches(
"assemble-all-patches", "\
assemble-all-patches\n\
assemble-all-patches [--skip-encrypted]\n\
Assemble all patches in the system/client-functions directory, and produce\n\
two compiled .bin files for each patch (one unencrypted, for most PSO\n\
versions, and one encrypted, for PSO GC JP v1.4, JP Ep3, and Ep3 Trial\n\
Edition). The output files are saved in system/client-functions.\n",
+[](phosg::Arguments&) {
+[](phosg::Arguments& args) {
auto fci = make_shared<FunctionCodeIndex>("system/client-functions");
auto process_code = +[](shared_ptr<const CompiledFunctionCode> code,
uint32_t checksum_addr,
uint32_t checksum_size,
uint32_t override_start_addr) -> void {
bool skip_encrypted = args.get<bool>("skip-encrypted");
auto process_code = [&](shared_ptr<const CompiledFunctionCode> code,
uint32_t checksum_addr,
uint32_t checksum_size,
uint32_t override_start_addr) -> void {
for (uint8_t encrypted = 0; encrypted < 2; encrypted++) {
if (encrypted && skip_encrypted) {
continue;
}
phosg::StringWriter w;
string data = prepare_send_function_call_data(
code, {}, nullptr, 0, checksum_addr, checksum_size, override_start_addr, encrypted);