handle quest loading client bug

This commit is contained in:
Martin Michelsen
2024-03-24 15:43:35 -07:00
parent a762c0f8f8
commit cb05dce764
2 changed files with 24 additions and 11 deletions
+8 -1
View File
@@ -709,7 +709,14 @@ struct C_MenuSelection_PC_BB_10_Flag03 : C_MenuSelection_10_Flag03<TextEncoding:
// memory card), use A7 instead.
// All chunks except the last must have 0x400 data bytes. When downloading an
// online quest, the .bin and .dat chunks may be interleaved (although newserv
// currently sends them sequentially).
// currently sends them sequentially). There is a client bug in BB (and
// probably all other versions) where if the quest file's size is a multiple
// of 0x400, the last chunk will have size 0x400, and the client will never
// consider the download complete since it only checks if the last chunk has
// size < 0x400; it does not check if all expected bytes have been received.
// To work around this, newserv appends an extra zero byte if the quest file's
// size is a multiple of 0x400; this byte will be ignored since the PRS
// decompression algorithm contains a stop command, so it will never read it.
// header.flag = file chunk index (start offset / 0x400)
struct S_WriteFile_13_A7 {
+16 -10
View File
@@ -490,7 +490,7 @@ QuestIndex::QuestIndex(
continue;
}
auto add_file = [&](map<string, FileData>& files, const string& basename, const string& filename, string&& value) {
auto add_file = [&](map<string, FileData>& files, const string& basename, const string& filename, 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");
}
@@ -498,6 +498,12 @@ QuestIndex::QuestIndex(
if (!files.emplace(basename, FileData{filename, data_ptr}).second) {
throw 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.
if (check_chunk_size && !(data_ptr->size() & 0x3FF)) {
data_ptr->push_back(0x00);
}
};
string cat_path = directory + "/" + cat->directory_name;
@@ -544,26 +550,26 @@ QuestIndex::QuestIndex(
}
if (extension == "json") {
add_file(json_files, file_basename, orig_filename, std::move(file_data));
add_file(json_files, file_basename, orig_filename, std::move(file_data), false);
} else if (extension == "bin" || extension == "mnm") {
add_file(bin_files, file_basename, orig_filename, std::move(file_data));
add_file(bin_files, file_basename, orig_filename, std::move(file_data), true);
} else if (extension == "bind" || extension == "mnmd") {
add_file(bin_files, file_basename, orig_filename, prs_compress_optimal(file_data));
add_file(bin_files, file_basename, orig_filename, prs_compress_optimal(file_data), true);
} else if (extension == "dat") {
add_file(dat_files, file_basename, orig_filename, std::move(file_data));
add_file(dat_files, file_basename, orig_filename, std::move(file_data), true);
} else if (extension == "datd") {
add_file(dat_files, file_basename, orig_filename, prs_compress_optimal(file_data));
add_file(dat_files, file_basename, orig_filename, prs_compress_optimal(file_data), true);
} else if (extension == "pvr") {
add_file(pvr_files, file_basename, orig_filename, std::move(file_data));
add_file(pvr_files, file_basename, orig_filename, std::move(file_data), true);
} else if (extension == "qst") {
auto files = decode_qst_data(file_data);
for (auto& it : files) {
if (ends_with(it.first, ".bin")) {
add_file(bin_files, file_basename, orig_filename, std::move(it.second));
add_file(bin_files, file_basename, orig_filename, std::move(it.second), true);
} else if (ends_with(it.first, ".dat")) {
add_file(dat_files, file_basename, orig_filename, std::move(it.second));
add_file(dat_files, file_basename, orig_filename, std::move(it.second), true);
} else if (ends_with(it.first, ".pvr")) {
add_file(pvr_files, file_basename, orig_filename, std::move(it.second));
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);
}