write Quest::export_qst

This commit is contained in:
Martin Michelsen
2023-03-23 22:22:21 -07:00
parent c48a998750
commit dcebc61b13
3 changed files with 97 additions and 2 deletions
+92 -1
View File
@@ -393,7 +393,8 @@ Quest::Quest(const string& bin_filename)
is_dcv1(false),
joinable(false),
file_format(FileFormat::BIN_DAT),
has_mnm_extension(false) {
has_mnm_extension(false),
is_dlq_encoded(false) {
if (ends_with(bin_filename, ".bin.gci") || ends_with(bin_filename, ".mnm.gci")) {
this->file_format = FileFormat::BIN_DAT_GCI;
@@ -981,6 +982,95 @@ pair<string, string> Quest::decode_qst(const string& filename) {
}
}
template <typename HeaderT>
void add_command_header(
StringWriter& w, uint8_t command, uint8_t flag, uint16_t size) {
HeaderT header;
header.command = command;
header.flag = flag;
header.size = sizeof(HeaderT) + size;
w.put(header);
}
template <typename HeaderT, typename CmdT>
void add_open_file_command(StringWriter& w, const Quest& q, bool is_bin) {
add_command_header<HeaderT>(
w, q.is_dlq_encoded ? 0xA6 : 0x44, q.internal_id,
sizeof(S_OpenFile_DC_44_A6));
CmdT cmd;
cmd.name = "PSO/" + encode_sjis(q.name);
cmd.filename = q.file_basename + (is_bin ? ".bin" : ".dat");
cmd.flags = q.is_dlq_encoded ? 0 : 2;
// TODO: It'd be nice to have something like w.emplace(...) to avoid copying
// the command structs into the StringWriter.
w.put(cmd);
}
template <typename HeaderT>
void add_write_file_commands(
StringWriter& w,
const string& filename,
const string& data,
bool is_dlq_encoded) {
for (size_t z = 0; z < data.size(); z += 0x400) {
size_t chunk_size = min<size_t>(data.size() - z, 0x400);
add_command_header<HeaderT>(w, is_dlq_encoded ? 0xA7 : 0x13, z >> 10, sizeof(S_WriteFile_13_A7));
S_WriteFile_13_A7 cmd;
cmd.filename = filename;
memcpy(cmd.data.data(), &data[z], chunk_size);
cmd.data_size = chunk_size;
w.put(cmd);
}
}
string Quest::export_qst(GameVersion version) const {
if (this->category == QuestCategory::EPISODE_3) {
throw runtime_error("Episode 3 quests cannot be encoded in QST format");
}
StringWriter w;
switch (version) {
case GameVersion::DC:
add_open_file_command<PSOCommandHeaderDCV3, S_OpenFile_DC_44_A6>(w, *this, true);
add_open_file_command<PSOCommandHeaderDCV3, S_OpenFile_DC_44_A6>(w, *this, false);
add_write_file_commands<PSOCommandHeaderDCV3>(
w, this->file_basename + ".bin", *this->bin_contents(), this->is_dlq_encoded);
add_write_file_commands<PSOCommandHeaderDCV3>(
w, this->file_basename + ".dat", *this->dat_contents(), this->is_dlq_encoded);
break;
case GameVersion::PC:
add_open_file_command<PSOCommandHeaderPC, S_OpenFile_PC_V3_44_A6>(w, *this, true);
add_open_file_command<PSOCommandHeaderPC, S_OpenFile_PC_V3_44_A6>(w, *this, false);
add_write_file_commands<PSOCommandHeaderPC>(
w, this->file_basename + ".bin", *this->bin_contents(), this->is_dlq_encoded);
add_write_file_commands<PSOCommandHeaderPC>(
w, this->file_basename + ".dat", *this->dat_contents(), this->is_dlq_encoded);
break;
case GameVersion::GC:
case GameVersion::XB:
add_open_file_command<PSOCommandHeaderDCV3, S_OpenFile_PC_V3_44_A6>(w, *this, true);
add_open_file_command<PSOCommandHeaderDCV3, S_OpenFile_PC_V3_44_A6>(w, *this, false);
add_write_file_commands<PSOCommandHeaderDCV3>(
w, this->file_basename + ".bin", *this->bin_contents(), this->is_dlq_encoded);
add_write_file_commands<PSOCommandHeaderDCV3>(
w, this->file_basename + ".dat", *this->dat_contents(), this->is_dlq_encoded);
break;
case GameVersion::BB:
add_open_file_command<PSOCommandHeaderBB, S_OpenFile_BB_44_A6>(w, *this, true);
add_open_file_command<PSOCommandHeaderBB, S_OpenFile_BB_44_A6>(w, *this, false);
add_write_file_commands<PSOCommandHeaderBB>(
w, this->file_basename + ".bin", *this->bin_contents(), this->is_dlq_encoded);
add_write_file_commands<PSOCommandHeaderBB>(
w, this->file_basename + ".dat", *this->dat_contents(), this->is_dlq_encoded);
break;
default:
throw logic_error("invalid game version");
}
return move(w.str());
}
QuestIndex::QuestIndex(const string& directory) : directory(directory) {
@@ -1139,5 +1229,6 @@ shared_ptr<Quest> Quest::create_download_quest() const {
compressed_bin, decompressed_bin.size())));
dlq->dat_contents_ptr.reset(new string(create_download_quest_file(
*this->dat_contents(), prs_decompress_size(*this->dat_contents()))));
dlq->is_dlq_encoded = true;
return dlq;
}
+3
View File
@@ -55,6 +55,7 @@ public:
std::string file_basename; // we append -<version>.<bin/dat> when reading
FileFormat file_format;
bool has_mnm_extension;
bool is_dlq_encoded;
std::u16string name;
std::u16string short_description;
std::u16string long_description;
@@ -84,6 +85,8 @@ public:
static std::string decode_dlq(const std::string& filename);
static std::pair<std::string, std::string> decode_qst(const std::string& filename);
std::string export_qst(GameVersion version) const;
private:
// these are populated when requested
mutable std::shared_ptr<std::string> bin_contents_ptr;