mirror of
https://github.com/open-goal/jak-project
synced 2026-09-08 11:56:11 -04:00
[decompiler] Several small fixes (#775)
* fix assorted bugs * stricter return types * only look for functions in code files
This commit is contained in:
@@ -1121,6 +1121,21 @@ void StoreOp::collect_vars(RegAccessSet& vars) const {
|
||||
// LoadVarOp
|
||||
/////////////////////////////
|
||||
|
||||
std::string load_kind_to_string(LoadVarOp::Kind kind) {
|
||||
switch (kind) {
|
||||
case LoadVarOp::Kind::FLOAT:
|
||||
return "float";
|
||||
case LoadVarOp::Kind::VECTOR_FLOAT:
|
||||
return "vector-float";
|
||||
case LoadVarOp::Kind::SIGNED:
|
||||
return "signed";
|
||||
case LoadVarOp::Kind::UNSIGNED:
|
||||
return "unsigned";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
LoadVarOp::LoadVarOp(Kind kind, int size, RegisterAccess dst, SimpleExpression src, int my_idx)
|
||||
: AtomicOp(my_idx), m_kind(kind), m_size(size), m_dst(dst), m_src(std::move(src)) {}
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ class StoreOp : public AtomicOp {
|
||||
*/
|
||||
class LoadVarOp : public AtomicOp {
|
||||
public:
|
||||
enum class Kind { UNSIGNED, SIGNED, FLOAT, VECTOR_FLOAT };
|
||||
enum class Kind { UNSIGNED, SIGNED, FLOAT, VECTOR_FLOAT, INVALID };
|
||||
LoadVarOp(Kind kind, int size, RegisterAccess dst, SimpleExpression src, int my_idx);
|
||||
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
|
||||
bool operator==(const AtomicOp& other) const override;
|
||||
@@ -490,6 +490,13 @@ class LoadVarOp : public AtomicOp {
|
||||
std::optional<TypeSpec> m_type;
|
||||
};
|
||||
|
||||
std::string load_kind_to_string(LoadVarOp::Kind kind);
|
||||
FormElement* make_label_load(int label_idx,
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
int load_size,
|
||||
LoadVarOp::Kind load_kind);
|
||||
|
||||
/*!
|
||||
* This represents one of the possible instructions that can go in a branch delay slot.
|
||||
* These will be "absorbed" into higher level structures, but for the purpose of printing AtomicOps,
|
||||
|
||||
@@ -476,6 +476,82 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
return pool.alloc_element<StoreElement>(this);
|
||||
}
|
||||
|
||||
FormElement* make_label_load(int label_idx,
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
int load_size,
|
||||
LoadVarOp::Kind load_kind) {
|
||||
auto label = env.file->labels.at(label_idx);
|
||||
auto label_name = label.name;
|
||||
auto hint = env.label_types().find(label_name);
|
||||
if (hint != env.label_types().end()) {
|
||||
if (hint->second.is_const) {
|
||||
if ((load_kind == LoadVarOp::Kind::FLOAT || load_kind == LoadVarOp::Kind::SIGNED) &&
|
||||
load_size == 4 && hint->second.type_name == "float") {
|
||||
assert((label.offset % 4) == 0);
|
||||
auto word = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
float value;
|
||||
memcpy(&value, &word.data, 4);
|
||||
return pool.alloc_element<ConstantFloatElement>(value);
|
||||
} else if (hint->second.type_name == "uint64" && load_kind != LoadVarOp::Kind::FLOAT &&
|
||||
load_size == 8) {
|
||||
assert((label.offset % 8) == 0);
|
||||
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
|
||||
assert(word0.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind == LinkedWord::PLAIN_DATA);
|
||||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
return pool.alloc_element<CastElement>(TypeSpec("uint"),
|
||||
pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(value)));
|
||||
}
|
||||
|
||||
// is it a constant bitfield?
|
||||
auto& ts = env.dts->ts;
|
||||
auto as_bitfield = dynamic_cast<BitFieldType*>(ts.lookup_type(hint->second.type_name));
|
||||
if (as_bitfield && load_kind != LoadVarOp::Kind::FLOAT && load_size == 8) {
|
||||
// get the data
|
||||
assert((label.offset % 8) == 0);
|
||||
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
|
||||
assert(word0.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind == LinkedWord::PLAIN_DATA);
|
||||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
// for some reason, GOAL would use a 64-bit constant for all bitfields, even if they are
|
||||
// smaller. We should check that the higher bits are all zero.
|
||||
int bits = as_bitfield->get_size_in_memory() * 8;
|
||||
assert(bits <= 64);
|
||||
if (bits < 64) {
|
||||
assert((value >> bits) == 0);
|
||||
// technically ub if bits == 64.
|
||||
}
|
||||
TypeSpec typespec(hint->second.type_name);
|
||||
auto defs = decompile_bitfield_from_int(typespec, ts, value);
|
||||
return pool.alloc_element<BitfieldStaticDefElement>(typespec, defs, pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (load_kind == LoadVarOp::Kind::FLOAT && load_size == 4) {
|
||||
assert((label.offset % 4) == 0);
|
||||
const auto& words = env.file->words_by_seg.at(label.target_segment);
|
||||
if ((int)words.size() > label.offset / 4) {
|
||||
auto word = words.at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
float value;
|
||||
memcpy(&value, &word.data, 4);
|
||||
return pool.alloc_element<ConstantFloatElement>(value);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
|
||||
if (env.has_type_analysis()) {
|
||||
IR2_RegOffset ro;
|
||||
@@ -584,6 +660,12 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
|
||||
return pool.alloc_single_element_form<DerefElement>(nullptr, source, rd.addr_of, tokens);
|
||||
}
|
||||
|
||||
if (ro.offset == 0 && input_type.kind == TP_Type::Kind::LABEL_ADDR) {
|
||||
// we no longer resolve label stuff here because sometimes we need expressions for this
|
||||
return pool.alloc_single_element_form<LabelDerefElement>(nullptr, input_type.label_id(),
|
||||
m_size, m_kind, ro.var);
|
||||
}
|
||||
|
||||
if (ro.offset == 0 && (input_type.typespec() == TypeSpec("pointer") ||
|
||||
input_type.kind == TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT)) {
|
||||
std::string cast_type;
|
||||
@@ -623,74 +705,9 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
|
||||
}
|
||||
|
||||
if (m_src.is_identity() && m_src.get_arg(0).is_label()) {
|
||||
// try to see if we're loading a constant
|
||||
auto label = env.file->labels.at(m_src.get_arg(0).label());
|
||||
auto label_name = label.name;
|
||||
auto hint = env.label_types().find(label_name);
|
||||
if (hint != env.label_types().end()) {
|
||||
if (hint->second.is_const) {
|
||||
if ((m_kind == Kind::FLOAT || m_kind == Kind::SIGNED) && m_size == 4 &&
|
||||
hint->second.type_name == "float") {
|
||||
assert((label.offset % 4) == 0);
|
||||
auto word = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
float value;
|
||||
memcpy(&value, &word.data, 4);
|
||||
return pool.alloc_single_element_form<ConstantFloatElement>(nullptr, value);
|
||||
} else if (hint->second.type_name == "uint64" && m_kind != Kind::FLOAT && m_size == 8) {
|
||||
assert((label.offset % 8) == 0);
|
||||
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
|
||||
assert(word0.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind == LinkedWord::PLAIN_DATA);
|
||||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
return pool.alloc_single_element_form<CastElement>(
|
||||
nullptr, TypeSpec("uint"),
|
||||
pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(value)));
|
||||
}
|
||||
|
||||
// is it a constant bitfield?
|
||||
auto& ts = env.dts->ts;
|
||||
auto as_bitfield = dynamic_cast<BitFieldType*>(ts.lookup_type(hint->second.type_name));
|
||||
if (as_bitfield && m_kind != Kind::FLOAT && m_size == 8) {
|
||||
// get the data
|
||||
assert((label.offset % 8) == 0);
|
||||
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
|
||||
assert(word0.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind == LinkedWord::PLAIN_DATA);
|
||||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
// for some reason, GOAL would use a 64-bit constant for all bitfields, even if they are
|
||||
// smaller. We should check that the higher bits are all zero.
|
||||
int bits = as_bitfield->get_size_in_memory() * 8;
|
||||
assert(bits <= 64);
|
||||
if (bits < 64) {
|
||||
assert((value >> bits) == 0);
|
||||
// technically ub if bits == 64.
|
||||
}
|
||||
TypeSpec typespec(hint->second.type_name);
|
||||
auto defs = decompile_bitfield_from_int(typespec, ts, value);
|
||||
return pool.alloc_single_element_form<BitfieldStaticDefElement>(nullptr, typespec, defs,
|
||||
pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_kind == Kind::FLOAT && m_size == 4) {
|
||||
assert((label.offset % 4) == 0);
|
||||
const auto& words = env.file->words_by_seg.at(label.target_segment);
|
||||
if ((int)words.size() > label.offset / 4) {
|
||||
auto word = words.at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
float value;
|
||||
memcpy(&value, &word.data, 4);
|
||||
return pool.alloc_single_element_form<ConstantFloatElement>(nullptr, value);
|
||||
}
|
||||
auto label_load_element = make_label_load(m_src.get_arg(0).label(), env, pool, m_size, m_kind);
|
||||
if (label_load_element) {
|
||||
return pool.alloc_single_form(nullptr, label_load_element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
|
||||
}
|
||||
// todo: should we take out this warning?
|
||||
lg::warn("IR_StaticAddress does not know the type of {}", label.name);
|
||||
return TP_Type::make_label_addr();
|
||||
return TP_Type::make_label_addr(m_int);
|
||||
}
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
|
||||
@@ -2776,6 +2776,32 @@ void LabelElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
void LabelElement::collect_vars(RegAccessSet&, bool) const {}
|
||||
void LabelElement::get_modified_regs(RegSet&) const {}
|
||||
|
||||
////////////////////////////////
|
||||
// LabelDerefElement
|
||||
///////////////////////////////
|
||||
|
||||
LabelDerefElement::LabelDerefElement(int lid,
|
||||
int size,
|
||||
LoadVarOp::Kind load_kind,
|
||||
RegisterAccess var)
|
||||
: m_lid(lid), m_size(size), m_load_kind(load_kind), m_var(var) {}
|
||||
|
||||
goos::Object LabelDerefElement::to_form_internal(const Env& env) const {
|
||||
return pretty_print::build_list(fmt::format("label-deref {} :label {} :size {} :kind {}",
|
||||
m_var.to_string(env), env.file->labels.at(m_lid).name,
|
||||
m_size, load_kind_to_string(m_load_kind)));
|
||||
}
|
||||
|
||||
void LabelDerefElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
}
|
||||
|
||||
void LabelDerefElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
void LabelDerefElement::collect_vars(RegAccessSet& regs, bool) const {
|
||||
regs.insert(m_var);
|
||||
}
|
||||
void LabelDerefElement::get_modified_regs(RegSet&) const {}
|
||||
|
||||
////////////////////////////////
|
||||
// GetSymbolStringPointer
|
||||
//////////////////////////////
|
||||
|
||||
@@ -632,6 +632,7 @@ class ReturnElement : public FormElement {
|
||||
void collect_vars(RegAccessSet& vars, bool recursive) const override;
|
||||
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
std::optional<TypeSpec> return_type;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -1549,6 +1550,27 @@ class LabelElement : public FormElement {
|
||||
int m_lid = -1;
|
||||
};
|
||||
|
||||
class LabelDerefElement : public FormElement {
|
||||
public:
|
||||
LabelDerefElement(int lid, int size, LoadVarOp::Kind load_kind, RegisterAccess var);
|
||||
goos::Object to_form_internal(const Env& env) const override;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(RegAccessSet& vars, bool recursive) const override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) override;
|
||||
|
||||
private:
|
||||
int m_lid = -1;
|
||||
int m_size = -1;
|
||||
LoadVarOp::Kind m_load_kind = LoadVarOp::Kind::INVALID;
|
||||
RegisterAccess m_var;
|
||||
};
|
||||
|
||||
class GetSymbolStringPointer : public FormElement {
|
||||
public:
|
||||
GetSymbolStringPointer(Form* src);
|
||||
|
||||
@@ -3549,6 +3549,24 @@ FormElement* ConditionElement::make_zero_check_generic(const Env& env,
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_compare(m_kind), source_forms);
|
||||
}
|
||||
|
||||
FormElement* try_make_nonzero_logtest(Form* in, FormPool& pool) {
|
||||
/*
|
||||
(defmacro logtest? (a b)
|
||||
"does a have any of the bits in b?"
|
||||
`(nonzero? (logand ,a ,b))
|
||||
)
|
||||
*/
|
||||
auto logand_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::LOGAND),
|
||||
{Matcher::any(0), Matcher::any(1)});
|
||||
auto mr_logand = match(logand_matcher, in);
|
||||
if (mr_logand.matched) {
|
||||
return pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::LOGTEST), mr_logand.maps.forms.at(0),
|
||||
mr_logand.maps.forms.at(1));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
@@ -3578,19 +3596,9 @@ FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
|
||||
std::vector<Form*>{mr.maps.forms.at(0), value_form});
|
||||
}
|
||||
|
||||
/*
|
||||
(defmacro logtest? (a b)
|
||||
"does a have any of the bits in b?"
|
||||
`(nonzero? (logand ,a ,b))
|
||||
)
|
||||
*/
|
||||
auto logand_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::LOGAND),
|
||||
{Matcher::any(0), Matcher::any(1)});
|
||||
auto mr_logand = match(logand_matcher, source_forms.at(0));
|
||||
if (mr_logand.matched) {
|
||||
return pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::LOGTEST), mr_logand.maps.forms.at(0),
|
||||
mr_logand.maps.forms.at(1));
|
||||
auto as_logand = try_make_nonzero_logtest(source_forms.at(0), pool);
|
||||
if (as_logand) {
|
||||
return as_logand;
|
||||
}
|
||||
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_compare(m_kind), source_forms);
|
||||
@@ -3967,7 +3975,8 @@ void ReturnElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
|
||||
return_code->push_back(new_entries.back());
|
||||
if (var) {
|
||||
const auto& func_type = env.func->type.last_arg();
|
||||
if (!env.dts->ts.tc(func_type, env.get_variable_type(*var, false))) {
|
||||
return_type = env.get_variable_type(*var, false);
|
||||
if (func_type != return_type) {
|
||||
auto as_cast = return_code->try_as_element<CastElement>();
|
||||
if (as_cast) {
|
||||
return_code->clear();
|
||||
@@ -4643,13 +4652,25 @@ void ConditionalMoveFalseElement::push_to_stack(const Env& env, FormPool& pool,
|
||||
stack.push_form_element(this, true);
|
||||
return;
|
||||
}
|
||||
stack.push_value_to_reg(dest,
|
||||
pool.alloc_single_element_form<GenericElement>(
|
||||
nullptr,
|
||||
GenericOperator::make_compare(on_zero ? IR2_Condition::Kind::NONZERO
|
||||
: IR2_Condition::Kind::ZERO),
|
||||
std::vector<Form*>{popped.at(1)}),
|
||||
true, TypeSpec("symbol"));
|
||||
|
||||
Form* val = nullptr;
|
||||
|
||||
if (!val && on_zero) {
|
||||
auto as_logtest = try_make_nonzero_logtest(popped.at(1), pool);
|
||||
if (as_logtest) {
|
||||
val = pool.alloc_single_form(nullptr, as_logtest);
|
||||
}
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
val = pool.alloc_single_element_form<GenericElement>(
|
||||
nullptr,
|
||||
GenericOperator::make_compare(on_zero ? IR2_Condition::Kind::NONZERO
|
||||
: IR2_Condition::Kind::ZERO),
|
||||
std::vector<Form*>{popped.at(1)});
|
||||
}
|
||||
|
||||
stack.push_value_to_reg(dest, val, true, TypeSpec("symbol"));
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
@@ -4951,6 +4972,32 @@ void DefstateElement::update_from_stack(const Env&,
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void LabelDerefElement::update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
mark_popped();
|
||||
auto label_var = pop_to_forms({m_var}, env, pool, stack, allow_side_effects).at(0);
|
||||
auto atom = form_as_atom(label_var);
|
||||
if (!atom || !atom->is_label()) {
|
||||
throw std::runtime_error(fmt::format("LabelDerefElement didn't get a label, got {} instead",
|
||||
label_var->to_string(env)));
|
||||
}
|
||||
|
||||
if (atom->label() != m_lid) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Label ID error in LabelDerefElement: {} vs {}", atom->label(), m_lid));
|
||||
}
|
||||
|
||||
auto as_label = make_label_load(m_lid, env, pool, m_size, m_load_kind);
|
||||
if (!as_label) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Unable to figure out label load for {}\n", env.file->labels.at(m_lid).name));
|
||||
}
|
||||
result->push_back(as_label);
|
||||
}
|
||||
|
||||
void LabelElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
|
||||
mark_popped();
|
||||
stack.push_form_element(this, true);
|
||||
|
||||
@@ -158,9 +158,11 @@ class ObjectFileDB {
|
||||
void for_each_function_in_seg(int seg, Func f) {
|
||||
for_each_obj([&](ObjectFileData& data) {
|
||||
int fn = 0;
|
||||
for (size_t j = data.linked_data.functions_by_seg.at(seg).size(); j-- > 0;) {
|
||||
f(data.linked_data.functions_by_seg.at(seg).at(j), data);
|
||||
fn++;
|
||||
if (data.linked_data.segments == 3) {
|
||||
for (size_t j = data.linked_data.functions_by_seg.at(seg).size(); j-- > 0;) {
|
||||
f(data.linked_data.functions_by_seg.at(seg).at(j), data);
|
||||
fn++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,8 +84,31 @@ bool convert_to_expressions(
|
||||
}
|
||||
}
|
||||
|
||||
bool needs_cast = false;
|
||||
if (!dts.ts.tc(f.type.last_arg(), return_type)) {
|
||||
// we need to cast the final value.
|
||||
needs_cast = true;
|
||||
|
||||
} else {
|
||||
bool found_early_return = false;
|
||||
for (auto e : new_entries) {
|
||||
e->apply([&](FormElement* elt) {
|
||||
auto as_ret = dynamic_cast<ReturnElement*>(elt);
|
||||
if (as_ret) {
|
||||
found_early_return = true;
|
||||
}
|
||||
});
|
||||
if (found_early_return) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_early_return && f.type.last_arg() != return_type) {
|
||||
needs_cast = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_cast) {
|
||||
auto to_cast = new_entries.back();
|
||||
auto as_cast = dynamic_cast<CastElement*>(to_cast);
|
||||
if (as_cast) {
|
||||
|
||||
@@ -640,7 +640,6 @@
|
||||
:bitfield #t
|
||||
:type uint32
|
||||
(display-marks 0)
|
||||
(bit0 0) ;; TODO - nav-enemy::45
|
||||
(bit1 1)
|
||||
(bit2 2)
|
||||
(bit3 3) ;; TODO - nav-enemy::45
|
||||
@@ -14752,7 +14751,7 @@
|
||||
(define-extern show-iop-info (function dma-buffer int))
|
||||
(define-extern show-iop-memory (function dma-buffer int))
|
||||
(define-extern make-sqrt-table (function int))
|
||||
(define-extern flava-lookup (function symbol int integer))
|
||||
(define-extern flava-lookup (function symbol int int))
|
||||
|
||||
;; - Symbols
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ std::string TP_Type::print() const {
|
||||
case Kind::PCPYUD_BITFIELD_AND:
|
||||
return fmt::format("<pcpyud-and {}>", m_ts.print());
|
||||
case Kind::LABEL_ADDR:
|
||||
return "<label-addr>";
|
||||
return fmt::format("<label-{}>", m_int);
|
||||
case Kind::ENTER_STATE_FUNCTION:
|
||||
return "<enter-state-func>";
|
||||
case Kind::INVALID:
|
||||
|
||||
@@ -262,9 +262,10 @@ class TP_Type {
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_label_addr() {
|
||||
static TP_Type make_label_addr(int label_id) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::LABEL_ADDR;
|
||||
result.m_int = label_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -342,6 +343,11 @@ class TP_Type {
|
||||
return m_flipped_order;
|
||||
}
|
||||
|
||||
int label_id() const {
|
||||
assert(kind == Kind::LABEL_ADDR);
|
||||
return m_int;
|
||||
}
|
||||
|
||||
private:
|
||||
TypeSpec m_ts;
|
||||
TypeSpec m_method_from_type;
|
||||
|
||||
@@ -1255,11 +1255,22 @@ std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
|
||||
return type_info->entries().at(a) < type_info->entries().at(b);
|
||||
});
|
||||
|
||||
for (auto& field_name : bit_sorted_names) {
|
||||
u64 mask = ((u64)1) << type_info->entries().at(field_name);
|
||||
for (auto& kv : type_info->entries()) {
|
||||
u64 mask = ((u64)1) << kv.second;
|
||||
if (value & mask) {
|
||||
reconstructed |= mask;
|
||||
result.push_back(field_name);
|
||||
result.push_back(kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
int bit_count = 0;
|
||||
{
|
||||
u64 x = value;
|
||||
while (x) {
|
||||
if (x & 1) {
|
||||
bit_count++;
|
||||
}
|
||||
x >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1270,10 +1281,18 @@ std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
|
||||
type.print(), value, reconstructed));
|
||||
}
|
||||
|
||||
// unordered map will give us these fields in a weird order, let's order them explicitly.
|
||||
std::sort(result.begin(), result.end(), [&](const std::string& a, const std::string& b) {
|
||||
return type_info->entries().at(a) < type_info->entries().at(b);
|
||||
});
|
||||
if (bit_count == (int)result.size()) {
|
||||
// unordered map will give us these fields in a weird order, let's order them explicitly.
|
||||
// because we have exactly one name per bit, we can just order them in bit order.
|
||||
std::sort(result.begin(), result.end(), [&](const std::string& a, const std::string& b) {
|
||||
return type_info->entries().at(a) < type_info->entries().at(b);
|
||||
});
|
||||
} else {
|
||||
// we have multiple. Just sort alphabetically and complain.
|
||||
lg::warn("Enum type {} has multiple entries with the same value.", type_info->get_name());
|
||||
std::sort(result.begin(), result.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user