add reassembly mode in quest script disassembler
This commit is contained in:
+5
-2
@@ -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
@@ -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
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user