mirror of
https://github.com/open-goal/jak-project
synced 2026-09-09 20:21:28 -04:00
[Decompiler] Static Data Decomp (#280)
* update all-types * begin work on static data decompiler * working for vif disasm array * mostly working * finish static data decompilation
This commit is contained in:
@@ -7,11 +7,29 @@
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
#include "PrettyPrinter.h"
|
||||
#include "Reader.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace pretty_print {
|
||||
|
||||
/*!
|
||||
* Print a float in a nice representation if possibly, or an exact 32-bit integer constant to
|
||||
* be reinterpreted.
|
||||
*/
|
||||
goos::Object float_representation(float value) {
|
||||
int rounded = value;
|
||||
bool exact_int = ((float)rounded) == value;
|
||||
if (value == 0.5 || value == -0.5 || value == 0.0 || value == 1.0 || value == -1.0 || exact_int) {
|
||||
return goos::Object::make_float(value);
|
||||
} else {
|
||||
u32 int_value;
|
||||
memcpy(&int_value, &value, 4);
|
||||
return pretty_print::build_list("the-as", "float", fmt::format("#x{:x}", int_value));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* A single token which cannot be split between lines.
|
||||
*/
|
||||
|
||||
@@ -51,4 +51,6 @@ goos::Object build_list(const goos::Object& car, Args... rest) {
|
||||
|
||||
goos::Reader& get_pretty_printer_reader();
|
||||
|
||||
goos::Object float_representation(float value);
|
||||
|
||||
} // namespace pretty_print
|
||||
|
||||
@@ -51,8 +51,10 @@ Field::Field(std::string name, TypeSpec ts, int offset)
|
||||
*/
|
||||
std::string Field::print() const {
|
||||
return fmt::format(
|
||||
"Field: ({} {} :offset {}) inline: {:5}, dynamic: {:5}, array: {:5}, array size {:3}", m_name,
|
||||
m_type.print(), m_offset, m_inline, m_dynamic, m_array, m_array_size);
|
||||
"Field: ({} {} :offset {}) inline: {:5}, dynamic: {:5}, array: {:5}, array size {:3}, align "
|
||||
"{:2}, skip {}",
|
||||
m_name, m_type.print(), m_offset, m_inline, m_dynamic, m_array, m_array_size, m_alignment,
|
||||
m_skip_in_static_decomp);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -90,7 +92,8 @@ bool Field::operator==(const Field& other) const {
|
||||
m_dynamic == other.m_dynamic &&
|
||||
m_array == other.m_array &&
|
||||
m_array_size == other.m_array_size &&
|
||||
m_alignment == other.m_alignment;
|
||||
m_alignment == other.m_alignment &&
|
||||
m_skip_in_static_decomp == other.m_skip_in_static_decomp;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
@@ -480,8 +483,9 @@ StructureType::StructureType(std::string parent,
|
||||
|
||||
std::string StructureType::print() const {
|
||||
std::string result = fmt::format(
|
||||
"[StructureType] {}\n parent: {}\n boxed: {}\n dynamic: {}\n size: {}\n pack: {}\n fields:\n",
|
||||
m_name, m_parent, m_is_boxed, m_dynamic, m_size_in_mem, m_pack);
|
||||
"[StructureType] {}\n parent: {}\n boxed: {}\n dynamic: {}\n size: {}\n pack: {}\n misalign: "
|
||||
"{}\n fields:\n",
|
||||
m_name, m_parent, m_is_boxed, m_dynamic, m_size_in_mem, m_pack, m_allow_misalign);
|
||||
for (auto& x : m_fields) {
|
||||
result += " " + x.print() + "\n";
|
||||
}
|
||||
@@ -507,7 +511,8 @@ bool StructureType::operator==(const Type& other) const {
|
||||
m_fields == p_other->m_fields &&
|
||||
m_dynamic == p_other->m_dynamic &&
|
||||
m_size_in_mem == p_other->m_size_in_mem &&
|
||||
m_pack == p_other->m_pack;
|
||||
m_pack == p_other->m_pack &&
|
||||
m_allow_misalign == p_other->m_allow_misalign;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +192,7 @@ class Field {
|
||||
bool is_dynamic() const { return m_dynamic; }
|
||||
const std::string& name() const { return m_name; }
|
||||
int offset() const { return m_offset; }
|
||||
bool skip_in_decomp() const { return m_skip_in_static_decomp; }
|
||||
bool operator==(const Field& other) const;
|
||||
|
||||
int alignment() const {
|
||||
@@ -208,6 +209,7 @@ class Field {
|
||||
friend class TypeSystem;
|
||||
void set_alignment(int alignment) { m_alignment = alignment; }
|
||||
void set_offset(int offset) { m_offset = offset; }
|
||||
void set_skip_in_static_decomp() { m_skip_in_static_decomp = true; }
|
||||
|
||||
std::string m_name;
|
||||
TypeSpec m_type;
|
||||
@@ -218,6 +220,7 @@ class Field {
|
||||
bool m_array = false;
|
||||
int m_array_size = 0;
|
||||
int m_alignment = -1;
|
||||
bool m_skip_in_static_decomp = false;
|
||||
};
|
||||
|
||||
class StructureType : public ReferenceType {
|
||||
|
||||
@@ -616,7 +616,8 @@ int TypeSystem::add_field_to_type(StructureType* type,
|
||||
bool is_inline,
|
||||
bool is_dynamic,
|
||||
int array_size,
|
||||
int offset_override) {
|
||||
int offset_override,
|
||||
bool skip_in_static_decomp) {
|
||||
if (type->lookup_field(field_name, nullptr)) {
|
||||
fmt::print("[TypeSystem] Type {} already has a field named {}\n", type->get_name(), field_name);
|
||||
throw std::runtime_error("add_field_to_type duplicate field names");
|
||||
@@ -656,6 +657,9 @@ int TypeSystem::add_field_to_type(StructureType* type,
|
||||
|
||||
field.set_offset(offset);
|
||||
field.set_alignment(field_alignment);
|
||||
if (skip_in_static_decomp) {
|
||||
field.set_skip_in_static_decomp();
|
||||
}
|
||||
|
||||
int after_field = offset + get_size_in_type(field);
|
||||
if (type->get_size_in_memory() < after_field) {
|
||||
|
||||
@@ -145,7 +145,8 @@ class TypeSystem {
|
||||
bool is_inline = false,
|
||||
bool is_dynamic = false,
|
||||
int array_size = -1,
|
||||
int offset_override = -1);
|
||||
int offset_override = -1,
|
||||
bool skip_in_static_decomp = false);
|
||||
|
||||
void add_builtin_types();
|
||||
|
||||
@@ -182,6 +183,8 @@ class TypeSystem {
|
||||
TypeSpec lowest_common_ancestor_reg(const TypeSpec& a, const TypeSpec& b) const;
|
||||
TypeSpec lowest_common_ancestor(const std::vector<TypeSpec>& types) const;
|
||||
|
||||
int get_size_in_type(const Field& field) const;
|
||||
|
||||
private:
|
||||
bool try_reverse_lookup(const FieldReverseLookupInput& input,
|
||||
std::vector<FieldReverseLookupOutput::Token>* path,
|
||||
@@ -205,7 +208,6 @@ class TypeSystem {
|
||||
TypeSpec* result_type) const;
|
||||
std::string lca_base(const std::string& a, const std::string& b) const;
|
||||
bool typecheck_base_types(const std::string& expected, const std::string& actual) const;
|
||||
int get_size_in_type(const Field& field) const;
|
||||
int get_alignment_in_type(const Field& field);
|
||||
Field lookup_field(const std::string& type_name, const std::string& field_name) const;
|
||||
StructureType* add_builtin_structure(const std::string& parent,
|
||||
|
||||
@@ -98,6 +98,7 @@ void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def
|
||||
bool is_dynamic = false;
|
||||
int offset_override = -1;
|
||||
int offset_assert = -1;
|
||||
bool skip_in_decomp = false;
|
||||
|
||||
if (!rest->is_empty_list()) {
|
||||
if (car(rest).is_int()) {
|
||||
@@ -122,6 +123,8 @@ void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def
|
||||
throw std::runtime_error("Cannot use -1 as offset-assert");
|
||||
}
|
||||
rest = cdr(rest);
|
||||
} else if (opt_name == ":do-not-decompile") {
|
||||
skip_in_decomp = true;
|
||||
} else {
|
||||
throw std::runtime_error("Invalid option in field specification: " + opt_name);
|
||||
}
|
||||
@@ -129,7 +132,7 @@ void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def
|
||||
}
|
||||
|
||||
int actual_offset = ts->add_field_to_type(structure, name, type, is_inline, is_dynamic,
|
||||
array_size, offset_override);
|
||||
array_size, offset_override, skip_in_decomp);
|
||||
if (offset_assert != -1 && actual_offset != offset_assert) {
|
||||
throw std::runtime_error("Field " + name + " was placed at " + std::to_string(actual_offset) +
|
||||
" but offset-assert was set to " + std::to_string(offset_assert));
|
||||
|
||||
Reference in New Issue
Block a user