handle quest loading client bug
This commit is contained in:
@@ -709,7 +709,14 @@ struct C_MenuSelection_PC_BB_10_Flag03 : C_MenuSelection_10_Flag03<TextEncoding:
|
|||||||
// memory card), use A7 instead.
|
// memory card), use A7 instead.
|
||||||
// All chunks except the last must have 0x400 data bytes. When downloading an
|
// 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
|
// 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)
|
// header.flag = file chunk index (start offset / 0x400)
|
||||||
struct S_WriteFile_13_A7 {
|
struct S_WriteFile_13_A7 {
|
||||||
|
|||||||
+16
-10
@@ -490,7 +490,7 @@ QuestIndex::QuestIndex(
|
|||||||
continue;
|
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) {
|
if (categories.emplace(basename, cat->category_id).first->second != cat->category_id) {
|
||||||
throw runtime_error("file " + basename + " exists in multiple categories");
|
throw runtime_error("file " + basename + " exists in multiple categories");
|
||||||
}
|
}
|
||||||
@@ -498,6 +498,12 @@ QuestIndex::QuestIndex(
|
|||||||
if (!files.emplace(basename, FileData{filename, data_ptr}).second) {
|
if (!files.emplace(basename, FileData{filename, data_ptr}).second) {
|
||||||
throw runtime_error("file " + basename + " already exists");
|
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;
|
string cat_path = directory + "/" + cat->directory_name;
|
||||||
@@ -544,26 +550,26 @@ QuestIndex::QuestIndex(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (extension == "json") {
|
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") {
|
} 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") {
|
} 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") {
|
} 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") {
|
} 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") {
|
} 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") {
|
} else if (extension == "qst") {
|
||||||
auto files = decode_qst_data(file_data);
|
auto files = decode_qst_data(file_data);
|
||||||
for (auto& it : files) {
|
for (auto& it : files) {
|
||||||
if (ends_with(it.first, ".bin")) {
|
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")) {
|
} 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")) {
|
} 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 {
|
} else {
|
||||||
throw runtime_error("qst file contains unsupported file type: " + it.first);
|
throw runtime_error("qst file contains unsupported file type: " + it.first);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user