add decoder/encoder for AdEnding.rel
This commit is contained in:
+31
-2
@@ -1628,7 +1628,7 @@ Action a_disassemble_quest_script(
|
|||||||
QEdit. If you intend to reassemble the script, after editing it, use the\n\
|
QEdit. If you intend to reassemble the script, after editing it, use the\n\
|
||||||
--reassembly option to add explicit label numbers and remove offsets and\n\
|
--reassembly option to add explicit label numbers and remove offsets and\n\
|
||||||
data in code sections. To include script references from the map, use the\n\
|
data in code sections. To include script references from the map, use the\n\
|
||||||
--map-file=FILENAME option.",
|
--map-file=FILENAME option.\n",
|
||||||
+[](phosg::Arguments& args) {
|
+[](phosg::Arguments& args) {
|
||||||
string data = read_input_data(args);
|
string data = read_input_data(args);
|
||||||
auto version = get_cli_version(args);
|
auto version = get_cli_version(args);
|
||||||
@@ -1720,7 +1720,7 @@ Action a_assemble_quest_script(
|
|||||||
Assemble the input quest script (.txt file) into a compressed .bin file\n\
|
Assemble the input quest script (.txt file) into a compressed .bin file\n\
|
||||||
usable as an online quest script. If --decompressed is given, produces an\n\
|
usable as an online quest script. If --decompressed is given, produces an\n\
|
||||||
uncompressed .bind file instead. If --disable-strict is given, allows some\n\
|
uncompressed .bind file instead. If --disable-strict is given, allows some\n\
|
||||||
invalid behaviors (e.g. calling an undefined label by number).",
|
invalid behaviors (e.g. calling an undefined label by number).\n",
|
||||||
+[](phosg::Arguments& args) {
|
+[](phosg::Arguments& args) {
|
||||||
string text = read_input_data(args);
|
string text = read_input_data(args);
|
||||||
|
|
||||||
@@ -2035,6 +2035,35 @@ Action a_encode_unicode_text_set(
|
|||||||
write_output_data(args, encoded.data(), encoded.size(), "prs");
|
write_output_data(args, encoded.data(), encoded.size(), "prs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Action a_decode_credits_text_archive(
|
||||||
|
"decode-credits-text-archive", "\
|
||||||
|
decode-credits-text-archive [OPTIONS] [INPUT-FILENAME [OUTPUT-FILENAME]]\n\
|
||||||
|
Decode a credits text archive (AdEnding.rel) to JSON. Use the --big-endian\n\
|
||||||
|
option if the file is for PSO GC.\n",
|
||||||
|
+[](phosg::Arguments& args) {
|
||||||
|
auto ret = decode_credits_text_set(read_input_data(args), args.get<bool>("big-endian"));
|
||||||
|
auto json = phosg::JSON::list();
|
||||||
|
for (const auto& it : ret) {
|
||||||
|
json.emplace_back(it);
|
||||||
|
}
|
||||||
|
string out_data = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY | phosg::JSON::SerializeOption::EXPAND_LEAF_CONTAINERS);
|
||||||
|
write_output_data(args, out_data.data(), out_data.size(), "json");
|
||||||
|
});
|
||||||
|
Action a_encode_credits_text_archive(
|
||||||
|
"encode-credits-text-archive", "\
|
||||||
|
encode-credits-text-archive [OPTIONS] [INPUT-FILENAME [OUTPUT-FILENAME]]\n\
|
||||||
|
Encode a credits text archive (AdEnding.rel) from JSON. Use the\n\
|
||||||
|
--big-endian option if the file is for PSO GC.\n",
|
||||||
|
+[](phosg::Arguments& args) {
|
||||||
|
auto json = phosg::JSON::parse(read_input_data(args));
|
||||||
|
std::vector<std::string> data;
|
||||||
|
for (const auto& it : json.as_list()) {
|
||||||
|
data.emplace_back(it->as_string());
|
||||||
|
}
|
||||||
|
auto ret = encode_credits_text_set(data, args.get<bool>("big-endian"));
|
||||||
|
write_output_data(args, ret.data(), ret.size(), "rel");
|
||||||
|
});
|
||||||
|
|
||||||
Action a_decode_word_select_set(
|
Action a_decode_word_select_set(
|
||||||
"decode-word-select-set", "\
|
"decode-word-select-set", "\
|
||||||
decode-word-select-set [INPUT-FILENAME]\n\
|
decode-word-select-set [INPUT-FILENAME]\n\
|
||||||
|
|||||||
@@ -551,3 +551,79 @@ std::shared_ptr<const TextSet> TextIndex::get(Version version, Language language
|
|||||||
uint32_t TextIndex::key_for_set(Version version, Language language) {
|
uint32_t TextIndex::key_for_set(Version version, Language language) {
|
||||||
return (static_cast<uint32_t>(version) << 8) | static_cast<size_t>(language);
|
return (static_cast<uint32_t>(version) << 8) | static_cast<size_t>(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool BE>
|
||||||
|
std::vector<std::string> decode_credits_text_set_t(const std::string& data) {
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
phosg::StringReader r(data);
|
||||||
|
const auto& footer = r.pget<RELFileFooterT<BE>>(r.size() - sizeof(RELFileFooterT<BE>));
|
||||||
|
r.go(footer.root_offset);
|
||||||
|
for (;;) {
|
||||||
|
ret.emplace_back(tt_sega_sjis_to_utf8(r.pget_cstr(r.get<U32T<BE>>())));
|
||||||
|
if (!ret.back().empty() && (ret.back()[0] == '*')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> decode_credits_text_set(const std::string& data, bool big_endian) {
|
||||||
|
if (big_endian) {
|
||||||
|
return decode_credits_text_set_t<true>(data);
|
||||||
|
} else {
|
||||||
|
return decode_credits_text_set_t<false>(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool BE>
|
||||||
|
std::string encode_credits_text_set_t(const std::vector<std::string>& data) {
|
||||||
|
if (data.empty() || (data.back() != "*")) {
|
||||||
|
throw std::runtime_error("the last string in a credits text set must be \"*\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
phosg::StringWriter strings_w;
|
||||||
|
phosg::StringWriter offsets_w;
|
||||||
|
std::unordered_map<std::string, uint32_t> existing_offsets;
|
||||||
|
for (const auto& str : data) {
|
||||||
|
try {
|
||||||
|
offsets_w.put<U32T<BE>>(existing_offsets.at(str));
|
||||||
|
} catch (const std::out_of_range&) {
|
||||||
|
existing_offsets.emplace(str, strings_w.size());
|
||||||
|
offsets_w.put<U32T<BE>>(strings_w.size());
|
||||||
|
strings_w.write(tt_utf8_to_sega_sjis(str));
|
||||||
|
strings_w.put_u8(0);
|
||||||
|
while (strings_w.size() & 3) {
|
||||||
|
strings_w.put_u8(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
phosg::StringWriter w;
|
||||||
|
RELFileFooterT<BE> footer;
|
||||||
|
w.write(strings_w.str());
|
||||||
|
footer.root_offset = w.size();
|
||||||
|
w.write(offsets_w.str());
|
||||||
|
while (w.size() & 0x1F) {
|
||||||
|
w.put_u8(0);
|
||||||
|
}
|
||||||
|
footer.relocations_offset = w.size();
|
||||||
|
footer.num_relocations = data.size();
|
||||||
|
w.put<U16T<BE>>(strings_w.size() / 4);
|
||||||
|
for (size_t z = 1; z < data.size(); z++) {
|
||||||
|
w.put<U16T<BE>>(1);
|
||||||
|
}
|
||||||
|
while (w.size() & 0x1F) {
|
||||||
|
w.put_u8(0);
|
||||||
|
}
|
||||||
|
w.put(footer);
|
||||||
|
|
||||||
|
return std::move(w.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string encode_credits_text_set(const std::vector<std::string>& data, bool big_endian) {
|
||||||
|
if (big_endian) {
|
||||||
|
return encode_credits_text_set_t<true>(data);
|
||||||
|
} else {
|
||||||
|
return encode_credits_text_set_t<false>(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -109,3 +109,6 @@ protected:
|
|||||||
phosg::PrefixedLogger log;
|
phosg::PrefixedLogger log;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<const TextSet>> sets;
|
std::unordered_map<uint32_t, std::shared_ptr<const TextSet>> sets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> decode_credits_text_set(const std::string& data, bool big_endian);
|
||||||
|
std::string encode_credits_text_set(const std::vector<std::string>& data, bool big_endian);
|
||||||
|
|||||||
Reference in New Issue
Block a user