add reassembly mode in quest script disassembler

This commit is contained in:
Martin Michelsen
2023-12-20 12:33:09 -08:00
parent 2ee4249f89
commit f048a4f5fb
3 changed files with 313 additions and 282 deletions
+5 -2
View File
@@ -1028,7 +1028,9 @@ Action a_disassemble_quest_script(
Disassemble the input quest script (.bin file) into a text representation\n\ Disassemble the input quest script (.bin file) into a text representation\n\
of the commands and metadata it contains. Specify the quest\'s game version\n\ of the commands and metadata it contains. Specify the quest\'s game version\n\
with one of the --dc-nte, --dc-v1, --dc-v2, --pc, --gc-nte, --gc, --gc-ep3,\n\ with one of the --dc-nte, --dc-v1, --dc-v2, --pc, --gc-nte, --gc, --gc-ep3,\n\
--xb, or --bb options.\n", --xb, or --bb options. If you intend to edit and reassemble the script, use\n\
the --reassembly option to add explicit label numbers and remove offsets\n\
and data in code sections.\n",
+[](Arguments& args) { +[](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);
@@ -1036,7 +1038,8 @@ Action a_disassemble_quest_script(
data = prs_decompress(data); data = prs_decompress(data);
} }
uint8_t language = args.get<bool>("japanese") ? 0 : 1; uint8_t language = args.get<bool>("japanese") ? 0 : 1;
string result = disassemble_quest_script(data.data(), data.size(), version, language); bool reassembly_mode = args.get<bool>("reassembly");
string result = disassemble_quest_script(data.data(), data.size(), version, language, reassembly_mode);
write_output_data(args, result.data(), result.size(), "txt"); write_output_data(args, result.data(), result.size(), "txt");
}); });
Action a_disassemble_quest_map( Action a_disassemble_quest_map(
+52 -24
View File
@@ -34,6 +34,11 @@ ToT as_type(const FromT& v) {
return ret; return ret;
} }
static const char* name_for_header_episode_number(uint8_t episode) {
static const array<const char*, 3> names = {"Episode1", "Episode2", "Episode4"};
return names.at(episode);
}
static TextEncoding encoding_for_language(uint8_t language) { static TextEncoding encoding_for_language(uint8_t language) {
return (language ? TextEncoding::ISO8859 : TextEncoding::SJIS); return (language ? TextEncoding::ISO8859 : TextEncoding::SJIS);
} }
@@ -892,7 +897,7 @@ opcodes_by_name_for_version(Version v) {
return index; return index;
} }
std::string disassemble_quest_script(const void* data, size_t size, Version version, uint8_t language) { std::string disassemble_quest_script(const void* data, size_t size, Version version, uint8_t language, bool reassembly_mode) {
StringReader r(data, size); StringReader r(data, size);
deque<string> lines; deque<string> lines;
lines.emplace_back(string_printf(".version %s", name_for_enum(version))); lines.emplace_back(string_printf(".version %s", name_for_enum(version)));
@@ -954,7 +959,7 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} }
lines.emplace_back(string_printf(".quest_num %hhu", header.quest_number)); lines.emplace_back(string_printf(".quest_num %hhu", header.quest_number));
lines.emplace_back(string_printf(".language %hhu", header.language)); lines.emplace_back(string_printf(".language %hhu", header.language));
lines.emplace_back(string_printf(".episode %hhu", header.episode)); lines.emplace_back(string_printf(".episode %s", name_for_header_episode_number(header.episode)));
lines.emplace_back(".name " + escape_string(header.name.decode(language))); lines.emplace_back(".name " + escape_string(header.name.decode(language)));
lines.emplace_back(".short_desc " + escape_string(header.short_description.decode(language))); lines.emplace_back(".short_desc " + escape_string(header.short_description.decode(language)));
lines.emplace_back(".long_desc " + escape_string(header.long_description.decode(language))); lines.emplace_back(".long_desc " + escape_string(header.long_description.decode(language)));
@@ -966,7 +971,7 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
code_offset = header.code_offset; code_offset = header.code_offset;
function_table_offset = header.function_table_offset; function_table_offset = header.function_table_offset;
lines.emplace_back(string_printf(".quest_num %hu", header.quest_number.load())); lines.emplace_back(string_printf(".quest_num %hu", header.quest_number.load()));
lines.emplace_back(string_printf(".episode %hhu", header.episode)); lines.emplace_back(string_printf(".episode %s", name_for_header_episode_number(header.episode)));
lines.emplace_back(string_printf(".max_players %hhu", header.episode)); lines.emplace_back(string_printf(".max_players %hhu", header.episode));
if (header.joinable) { if (header.joinable) {
lines.emplace_back(".joinable"); lines.emplace_back(".joinable");
@@ -1009,7 +1014,7 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
while (!function_table_r.eof()) { while (!function_table_r.eof()) {
try { try {
uint32_t function_id = function_table.size(); uint32_t function_id = function_table.size();
string name = string_printf("label%04" PRIX32, function_id); string name = (function_id == 0) ? "start" : string_printf("label%04" PRIX32, function_id);
uint32_t offset = function_table_r.get_u32l(); uint32_t offset = function_table_r.get_u32l();
auto l = make_shared<Label>(name, offset, function_id); auto l = make_shared<Label>(name, offset, function_id);
if (function_id == 0) { if (function_id == 0) {
@@ -1244,6 +1249,9 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} else { // (def->flags & F_ARGS) } else { // (def->flags & F_ARGS)
dasm_line.resize(0x20, ' '); dasm_line.resize(0x20, ' ');
if (reassembly_mode) {
dasm_line += "...";
} else {
dasm_line += "... "; dasm_line += "... ";
if (def->args.size() != arg_stack_values.size()) { if (def->args.size() != arg_stack_values.size()) {
@@ -1354,6 +1362,7 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} }
} }
} }
}
if (!(def->flags & F_PASS)) { if (!(def->flags & F_PASS)) {
arg_stack_values.clear(); arg_stack_values.clear();
@@ -1364,17 +1373,19 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} }
strip_trailing_whitespace(dasm_line); strip_trailing_whitespace(dasm_line);
string line_text;
if (reassembly_mode) {
line_text = string_printf(" %s", dasm_line.c_str());
} else {
string hex_data = format_data_string(cmd_r.preadx(opcode_start_offset, cmd_r.where() - opcode_start_offset), nullptr, FormatDataFlags::HEX_ONLY); string hex_data = format_data_string(cmd_r.preadx(opcode_start_offset, cmd_r.where() - opcode_start_offset), nullptr, FormatDataFlags::HEX_ONLY);
if (hex_data.size() > 14) { if (hex_data.size() > 14) {
hex_data.resize(12); hex_data.resize(12);
hex_data += "..."; hex_data += "...";
} }
hex_data.resize(16, ' '); hex_data.resize(16, ' ');
line_text = string_printf(" %04zX %s %s", opcode_start_offset, hex_data.c_str(), dasm_line.c_str());
dasm_lines.emplace( }
opcode_start_offset, dasm_lines.emplace(opcode_start_offset, DisassemblyLine(std::move(line_text), cmd_r.where()));
DisassemblyLine(
string_printf(" %04zX %s %s", opcode_start_offset, hex_data.c_str(), dasm_line.c_str()), cmd_r.where()));
} }
} }
@@ -1386,10 +1397,13 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
if (size > 0) { if (size > 0) {
lines.emplace_back(); lines.emplace_back();
} }
if (l->function_id == 0) { if ((l->function_id == 0) && !reassembly_mode) {
lines.emplace_back("start:"); lines.emplace_back("start:");
} }
lines.emplace_back(string_printf("label%04" PRIX32 ":", l->function_id)); if (reassembly_mode) {
lines.emplace_back(string_printf("%s@0x%04" PRIX32 ":", l->name.c_str(), l->function_id));
} else {
lines.emplace_back(string_printf("%s:", l->name.c_str()));
if (l->references.size() == 1) { if (l->references.size() == 1) {
lines.emplace_back(string_printf(" // Referenced by instruction at %04zX", *l->references.begin())); lines.emplace_back(string_printf(" // Referenced by instruction at %04zX", *l->references.begin()));
} else if (!l->references.empty()) { } else if (!l->references.empty()) {
@@ -1400,7 +1414,33 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} }
lines.emplace_back(" // Referenced by instructions at " + join(tokens, ", ")); lines.emplace_back(" // Referenced by instructions at " + join(tokens, ", "));
} }
}
if (l->type_flags == 0) {
lines.emplace_back(string_printf(" // Could not determine data type; disassembling as code"));
l->add_data_type(Arg::DataType::SCRIPT);
}
auto add_disassembly_lines = [&](size_t start_offset, size_t size) -> void {
for (size_t z = start_offset; z < start_offset + size;) {
const auto& l = dasm_lines.at(z);
lines.emplace_back(l.line);
if (l.next_offset <= z) {
throw logic_error("line points backward or to itself");
}
z = l.next_offset;
}
};
// Print data interpretations of the label (if any)
if (reassembly_mode) {
if (l->has_data_type(Arg::DataType::SCRIPT)) {
add_disassembly_lines(l->offset, size);
} else {
lines.emplace_back(".data " + format_data_string(cmd_r.pgetv(l->offset, size), size));
}
} else {
auto print_as_struct = [&]<Arg::DataType data_type, typename StructT>(function<void(const StructT&)> print_fn) { auto print_as_struct = [&]<Arg::DataType data_type, typename StructT>(function<void(const StructT&)> print_fn) {
if (l->has_data_type(data_type)) { if (l->has_data_type(data_type)) {
if (size >= sizeof(StructT)) { if (size >= sizeof(StructT)) {
@@ -1418,12 +1458,6 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} }
}; };
if (l->type_flags == 0) {
lines.emplace_back(string_printf(" // Could not determine data type; disassembling as code"));
l->add_data_type(Arg::DataType::SCRIPT);
}
// Print data interpretations of the label (if any)
if (l->has_data_type(Arg::DataType::DATA)) { if (l->has_data_type(Arg::DataType::DATA)) {
lines.emplace_back(string_printf(" // As raw data (0x%zX bytes)", size)); lines.emplace_back(string_printf(" // As raw data (0x%zX bytes)", size));
lines.emplace_back(format_and_indent_data(cmd_r.pgetv(l->offset, size), size, l->offset)); lines.emplace_back(format_and_indent_data(cmd_r.pgetv(l->offset, size), size, l->offset));
@@ -1557,13 +1591,7 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
} }
} }
if (l->has_data_type(Arg::DataType::SCRIPT)) { if (l->has_data_type(Arg::DataType::SCRIPT)) {
for (size_t z = l->offset; z < l->offset + size;) { add_disassembly_lines(l->offset, size);
const auto& l = dasm_lines.at(z);
lines.emplace_back(l.line);
if (l.next_offset <= z) {
throw logic_error("line points backward or to itself");
}
z = l.next_offset;
} }
} }
} }
+1 -1
View File
@@ -82,7 +82,7 @@ struct PSOQuestHeaderBB {
Episode episode_for_quest_episode_number(uint8_t episode_number); Episode episode_for_quest_episode_number(uint8_t episode_number);
std::string disassemble_quest_script(const void* data, size_t size, Version version, uint8_t language); std::string disassemble_quest_script(const void* data, size_t size, Version version, uint8_t language, bool reassembly_mode);
std::string assemble_quest_script(const std::string& text); std::string assemble_quest_script(const std::string& text);
Episode find_quest_episode_from_script(const void* data, size_t size, Version version); Episode find_quest_episode_from_script(const void* data, size_t size, Version version);