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\
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\
--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) {
string data = read_input_data(args);
auto version = get_cli_version(args);
@@ -1036,7 +1038,8 @@ Action a_disassemble_quest_script(
data = prs_decompress(data);
}
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");
});
Action a_disassemble_quest_map(
+52 -24
View File
@@ -34,6 +34,11 @@ ToT as_type(const FromT& v) {
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) {
return (language ? TextEncoding::ISO8859 : TextEncoding::SJIS);
}
@@ -892,7 +897,7 @@ opcodes_by_name_for_version(Version v) {
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);
deque<string> lines;
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(".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(".short_desc " + escape_string(header.short_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;
function_table_offset = header.function_table_offset;
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));
if (header.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()) {
try {
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();
auto l = make_shared<Label>(name, offset, function_id);
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)
dasm_line.resize(0x20, ' ');
if (reassembly_mode) {
dasm_line += "...";
} else {
dasm_line += "... ";
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)) {
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);
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);
if (hex_data.size() > 14) {
hex_data.resize(12);
hex_data += "...";
}
hex_data.resize(16, ' ');
dasm_lines.emplace(
opcode_start_offset,
DisassemblyLine(
string_printf(" %04zX %s %s", opcode_start_offset, hex_data.c_str(), dasm_line.c_str()), cmd_r.where()));
line_text = string_printf(" %04zX %s %s", opcode_start_offset, hex_data.c_str(), dasm_line.c_str());
}
dasm_lines.emplace(opcode_start_offset, DisassemblyLine(std::move(line_text), cmd_r.where()));
}
}
@@ -1386,10 +1397,13 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
if (size > 0) {
lines.emplace_back();
}
if (l->function_id == 0) {
if ((l->function_id == 0) && !reassembly_mode) {
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) {
lines.emplace_back(string_printf(" // Referenced by instruction at %04zX", *l->references.begin()));
} 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, ", "));
}
}
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) {
if (l->has_data_type(data_type)) {
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)) {
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));
@@ -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)) {
for (size_t z = l->offset; z < 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;
add_disassembly_lines(l->offset, size);
}
}
}
+1 -1
View File
@@ -82,7 +82,7 @@ struct PSOQuestHeaderBB {
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);
Episode find_quest_episode_from_script(const void* data, size_t size, Version version);