From b56025412b09bc00adc96ea795491e2dd05161c1 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 24 Oct 2020 22:51:40 -0400 Subject: [PATCH] Recognize auto-generated inspect methods and create `deftype`s from them (#95) - Recognize new type definitions/parents/type flags in the decompiler - Analyze autogenerated inspect methods and dump guesses at fields to a file - Utility functions for accessing static data by label - Better ordering in the decompiler to go through functions in the order they appeared in the source - Added a decent number of types to `all-types.gc` based on the new field analyzer - Correct a few `int`/`integer` mistakes in `gcommon.gc` (this should really be a warning) - Correct a few type issues in `gcommon` and `gkernel-h` - Option in the decompiler to be strict about `define-extern` redefining a type of a symbol - Add a test to check consistency in types between `all-types.gc` (used by decompiler) and `goal_src` (used by the compiler) --- common/type_system/Type.cpp | 2 + common/type_system/Type.h | 1 + decompiler/CMakeLists.txt | 3 +- decompiler/Function/Function.cpp | 107 +- decompiler/Function/Function.h | 7 +- decompiler/Function/TypeInspector.cpp | 812 ++++++ decompiler/Function/TypeInspector.h | 35 + decompiler/ObjectFile/LinkedObjectFile.cpp | 24 +- decompiler/ObjectFile/LinkedObjectFile.h | 5 +- decompiler/ObjectFile/ObjectFileDB.cpp | 15 +- decompiler/ObjectFile/ObjectFileDB.h | 18 + decompiler/config.cpp | 5 + decompiler/config.h | 1 + decompiler/config/all-types.gc | 2277 ++++++++++++++++- decompiler/config/jak1_ntsc_black_label.jsonc | 10 +- decompiler/util/DecompilerTypeSystem.cpp | 43 + decompiler/util/DecompilerTypeSystem.h | 7 + goal_src/goal-lib.gc | 4 + goal_src/kernel/gcommon.gc | 21 +- goal_src/kernel/gkernel-h.gc | 33 +- goalc/compiler/Compiler.h | 2 + goalc/compiler/compilation/Define.cpp | 4 + test/goalc/test_with_game.cpp | 9 +- 23 files changed, 3363 insertions(+), 82 deletions(-) create mode 100644 decompiler/Function/TypeInspector.cpp create mode 100644 decompiler/Function/TypeInspector.h diff --git a/common/type_system/Type.cpp b/common/type_system/Type.cpp index 55b7bae13..ad1880a97 100644 --- a/common/type_system/Type.cpp +++ b/common/type_system/Type.cpp @@ -39,6 +39,8 @@ std::string MethodInfo::print_one_line() const { } Field::Field(std::string name, TypeSpec ts) : m_name(std::move(name)), m_type(std::move(ts)) {} +Field::Field(std::string name, TypeSpec ts, int offset) + : m_name(std::move(name)), m_type(std::move(ts)), m_offset(offset) {} /*! * Print a one line description of a field. diff --git a/common/type_system/Type.h b/common/type_system/Type.h index ccaf3c0f2..c738a6ae0 100644 --- a/common/type_system/Type.h +++ b/common/type_system/Type.h @@ -162,6 +162,7 @@ class Field { public: Field() = default; Field(std::string name, TypeSpec ts); + Field(std::string name, TypeSpec ts, int offset); void set_dynamic(); void set_array(int size); void set_inline(); diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index a78de4336..c7156e242 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -18,7 +18,8 @@ add_executable(decompiler IR/BasicOpBuilder.cpp IR/CfgBuilder.cpp IR/IR.cpp - IR/IR_TypeAnalysis.cpp) + IR/IR_TypeAnalysis.cpp + Function/TypeInspector.cpp) target_link_libraries(decompiler goos diff --git a/decompiler/Function/Function.cpp b/decompiler/Function/Function.cpp index e93a61840..e09989c8c 100644 --- a/decompiler/Function/Function.cpp +++ b/decompiler/Function/Function.cpp @@ -5,6 +5,7 @@ #include "decompiler/Disasm/InstructionMatching.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/util/DecompilerTypeSystem.h" +#include "TypeInspector.h" namespace { std::vector gpr_backups = {make_gpr(Reg::GP), make_gpr(Reg::S5), make_gpr(Reg::S4), @@ -474,7 +475,8 @@ void Function::find_global_function_defs(LinkedObjectFile& file, DecompilerTypeS * Look through this function to find calls to method-set! which define methods. * Updates the guessed_name of the function and updates type_info. */ -void Function::find_method_defs(LinkedObjectFile& file) { +void Function::find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts) { + (void)dts; int state = 0; int label_id = -1; int method_id = -1; @@ -546,6 +548,11 @@ void Function::find_method_defs(LinkedObjectFile& file) { auto& func = file.get_function_at_label(label_id); assert(func.guessed_name.empty()); func.guessed_name.set_as_method(type_name, method_id); + func.method_of_type = type_name; + if (method_id == GOAL_INSPECT_METHOD) { + func.is_inspect_method = true; + } + state = 0; continue; } @@ -553,6 +560,104 @@ void Function::find_method_defs(LinkedObjectFile& file) { } } +void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts) { + int state = 0; + Register temp_reg; + std::string type_name; + std::string parent_type; + int label_idx = -1; + + for (const auto& instr : instructions) { + // look for lw xx, type(s7) + if (instr.kind == InstructionKind::LW && instr.get_src(0).kind == InstructionAtom::IMM_SYM && + instr.get_src(0).get_sym() == "type" && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { + state = 1; + temp_reg = instr.get_dst(0).get_reg(); + continue; + } + + if (state == 1) { + // look for lwu t9, 16, v1 + if (instr.kind == InstructionKind::LWU && instr.get_dst(0).get_reg() == make_gpr(Reg::T9) && + instr.get_src(0).get_imm() == 16 && instr.get_src(1).get_reg() == temp_reg) { + state = 2; + continue; + } else { + state = 0; + } + } + + if (state == 2) { + // look for daddiu a0, s7, name-of-type + if (instr.kind == InstructionKind::DADDIU && + instr.get_dst(0).get_reg() == make_gpr(Reg::A0) && + instr.get_src(0).get_reg() == make_gpr(Reg::S7) && instr.get_src(1).is_sym()) { + state = 3; + type_name = instr.get_src(1).get_sym(); + continue; + } else { + state = 0; + } + } + + if (state == 3) { + // look for lw a1, parent-type(s7) + if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::A1) && + instr.get_src(0).kind == InstructionAtom::IMM_SYM && + instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { + state = 4; + parent_type = instr.get_src(0).get_sym(); + continue; + } else { + state = 0; + } + } + + if (state == 4) { + // look for ld a2, LXX(fp) + if (instr.kind == InstructionKind::LD && instr.get_dst(0).get_reg() == make_gpr(Reg::A2) && + instr.get_src(0).is_label() && instr.get_src(1).get_reg() == make_gpr(Reg::FP)) { + state = 5; + label_idx = instr.get_src(0).get_label(); + continue; + } else { + state = 0; + } + } + + if (state == 5) { + if (instr.kind == InstructionKind::JALR && instr.get_dst(0).get_reg() == make_gpr(Reg::RA) && + instr.get_src(0).get_reg() == make_gpr(Reg::T9)) { + state = 6; + continue; + } else { + state = 0; + } + } + + if (state == 6) { + // look for sll v0, ra, 0 + if (instr.kind == InstructionKind::SLL && instr.get_dst(0).get_reg() == make_gpr(Reg::V0) && + instr.get_src(0).get_reg() == make_gpr(Reg::RA) && instr.get_src(1).get_imm() == 0) { + // done! + // fmt::print("Got type {} parent {}\n", type_name, parent_type); + dts.add_type_parent(type_name, parent_type); + Label flag_label = file.labels.at(label_idx); + u64 word = file.read_data_word(flag_label); + flag_label.offset += 4; + u64 word2 = file.read_data_word(flag_label); + word |= (word2 << 32); + dts.add_type_flags(type_name, word); + // fmt::print("Flags are 0x{:x}\n", word); + state = 0; + continue; + } + } else { + state = 0; + } + } +} + void Function::add_basic_op(std::shared_ptr op, int start_instr, int end_instr) { op->is_basic_op = true; assert(end_instr > start_instr); diff --git a/decompiler/Function/Function.h b/decompiler/Function/Function.h index 223e56b8a..cfccd4a8e 100644 --- a/decompiler/Function/Function.h +++ b/decompiler/Function/Function.h @@ -70,7 +70,8 @@ class Function { Function(int _start_word, int _end_word); void analyze_prologue(const LinkedObjectFile& file); void find_global_function_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts); - void find_method_defs(LinkedObjectFile& file); + void find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts); + void find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts); void add_basic_op(std::shared_ptr op, int start_instr, int end_instr); bool has_basic_ops() { return !basic_ops.empty(); } bool has_typemaps() { return !basic_op_typemaps.empty(); } @@ -94,6 +95,8 @@ class Function { FunctionName guessed_name; bool suspected_asm = false; + bool is_inspect_method = false; + std::string method_of_type; std::vector instructions; std::vector basic_blocks; @@ -137,10 +140,10 @@ class Function { } prologue; bool uses_fp_register = false; + std::vector> basic_ops; private: void check_epilogue(const LinkedObjectFile& file); - std::vector> basic_ops; std::vector basic_op_typemaps; std::unordered_map instruction_to_basic_op; std::unordered_map basic_op_to_instruction; diff --git a/decompiler/Function/TypeInspector.cpp b/decompiler/Function/TypeInspector.cpp new file mode 100644 index 000000000..a54e130ee --- /dev/null +++ b/decompiler/Function/TypeInspector.cpp @@ -0,0 +1,812 @@ +#include "decompiler/config.h" +#include "decompiler/Disasm/InstructionMatching.h" +#include "TypeInspector.h" +#include "Function.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" +#include "third-party/fmt/format.h" +#include "decompiler/util/DecompilerTypeSystem.h" +#include "common/type_system/deftype.h" + +namespace { +struct FieldPrint { + char format = '\0'; + std::string field_name; + std::string field_type_name; + bool has_array = false; + int array_size = -1; +}; + +FieldPrint get_field_print(const std::string& str) { + int idx = 0; + auto next = [&]() { return str.at(idx++); }; + + auto peek = [&](int off) { return str.at(idx + off); }; + + FieldPrint field_print; + + // first is ~T + char c0 = next(); + assert(c0 == '~'); + char c1 = next(); + assert(c1 == 'T'); + + // next the name: + char name_char = next(); + while (name_char != ':' && name_char != '[') { + field_print.field_name.push_back(name_char); + name_char = next(); + } + + // possibly array thing + if (name_char == '[') { + int size = 0; + char num_char = next(); + while (num_char >= '0' && num_char <= '9') { + size = size * 10 + (num_char - '0'); + num_char = next(); + } + field_print.has_array = true; + field_print.array_size = size; + + assert(num_char == ']'); + char c = next(); + assert(c == ' '); + c = next(); + assert(c == '@'); + c = next(); + assert(c == ' '); + c = next(); + assert(c == '#'); + c = next(); + assert(c == 'x'); + } else { + // next a space + char space_char = next(); + assert(space_char == ' '); + } + + // next the format + char fmt1 = next(); + if (fmt1 == '~' && peek(0) != '`') { // normal ~_~% + char fmt_code = next(); + field_print.format = fmt_code; + char end1 = next(); + assert(end1 == '~'); + char end2 = next(); + assert(end2 == '%'); + assert(idx == (int)str.size()); + } else if (fmt1 == '#' && peek(0) == '<') { // struct #~% + next(); + char type_name_c = next(); + while (type_name_c != ' ') { + field_print.field_type_name += type_name_c; + type_name_c = next(); + } + + std::string expect_end = "@ #x~X>~%"; + for (char i : expect_end) { + char c = next(); + assert(i == c); + } + field_print.format = 'X'; + + assert(idx == (int)str.size()); + } else if (fmt1 == '#' && peek(0) == 'x') { // #x~X~% + next(); + std::string expect_end = "~X~%"; + for (char i : expect_end) { + char c = next(); + assert(i == c); + } + field_print.format = 'X'; + } else if (fmt1 == '~' && peek(0) == '`') { // ~`my-type-with-overriden-print`P~% + next(); + char type_name_c = next(); + while (type_name_c != '`') { + field_print.field_type_name += type_name_c; + type_name_c = next(); + } + + std::string expect_end = "P~%"; + for (char i : expect_end) { + char c = next(); + assert(i == c); + } + field_print.format = 'P'; + + assert(idx == (int)str.size()); + } else if (str.substr(idx - 1) == "(meters ~m)~%") { + field_print.format = 'm'; + } else if (str.substr(idx - 1) == "(deg ~r)~%") { + field_print.format = 'r'; + } else if (str.substr(idx - 1) == "(seconds ~e)~%") { + field_print.format = 'e'; + } + + else { + throw std::runtime_error("other format nyi in get_field_print " + str.substr(idx)); + } + + return field_print; +} + +bool is_int(IR* ir, s64 value) { + auto as_int = dynamic_cast(ir); + return as_int && as_int->value == value; +} + +bool is_reg(IR* ir, Register reg) { + auto as_reg = dynamic_cast(ir); + return as_reg && as_reg->reg == reg; +} + +bool is_math_reg_constant(IR* ir, IR_IntMath2::Kind kind, Register src0, s64 src1) { + auto as_math = dynamic_cast(ir); + return as_math && as_math->kind == kind && is_reg(as_math->arg0.get(), src0) && + is_int(as_math->arg1.get(), src1); +} + +bool is_load_with_offset(IR* ir, IR_Load::Kind kind, int load_size, Register base, s64 offset) { + auto as_load = dynamic_cast(ir); + return as_load && as_load->kind == kind && as_load->size == load_size && + is_math_reg_constant(as_load->location.get(), IR_IntMath2::ADD, base, offset); +} + +bool is_get_load_with_offset(IR* ir, + Register dst, + IR_Load::Kind kind, + int load_size, + Register base, + s64 offset) { + auto as_set = dynamic_cast(ir); + return as_set && is_reg(as_set->dst.get(), dst) && + is_load_with_offset(as_set->src.get(), kind, load_size, base, offset); +} + +struct LoadInfo { + int offset = 0; + int size = 0; + IR_Load::Kind kind; +}; + +LoadInfo get_load_info_from_set(IR* load) { + auto as_set = dynamic_cast(load); + assert(as_set); + auto as_load = dynamic_cast(as_set->src.get()); + assert(as_load); + LoadInfo info; + info.kind = as_load->kind; + info.size = as_load->size; + if (dynamic_cast(as_load->location.get())) { + info.offset = 0; + return info; + } + + auto as_math = dynamic_cast(as_load->location.get()); + assert(as_math); + assert(as_math->kind == IR_IntMath2::ADD); + auto as_int = dynamic_cast(as_math->arg1.get()); + assert(as_int); + info.offset = as_int->value; + return info; +} + +Register get_base_of_load(IR_Load* load) { + auto as_reg = dynamic_cast(load->location.get()); + if (as_reg) { + return as_reg->reg; + } + + auto as_math = dynamic_cast(load->location.get()); + assert(as_math->kind == IR_IntMath2::ADD); + assert(dynamic_cast(as_math->arg1.get())); + auto math_reg = dynamic_cast(as_math->arg0.get()); + if (math_reg) { + return math_reg->reg; + } else { + assert(false); + } + return {}; +} + +bool is_load_with_base(IR* ir, Register base) { + auto as_load = dynamic_cast(ir); + return as_load && base == get_base_of_load(as_load); +} + +bool is_get_load(IR* ir, Register dst, Register base) { + auto as_set = dynamic_cast(ir); + return as_set && is_reg(as_set->dst.get(), dst) && is_load_with_base(as_set->src.get(), base); +} + +bool is_reg_reg_move(IR* ir, Register dst, Register src) { + auto as_set = dynamic_cast(ir); + return as_set && is_reg(as_set->dst.get(), dst) && is_reg(as_set->src.get(), src); +} + +bool is_sym_value(IR* ir, const std::string& sym_name) { + auto as_sym_value = dynamic_cast(ir); + return as_sym_value && as_sym_value->name == sym_name; +} + +bool is_sym(IR* ir, const std::string& sym_name) { + auto as_sym = dynamic_cast(ir); + return as_sym && as_sym->name == sym_name; +} + +bool is_get_sym_value(IR* ir, Register dst, const std::string& sym_name) { + auto as_set = dynamic_cast(ir); + return as_set && is_reg(as_set->dst.get(), dst) && is_sym_value(as_set->src.get(), sym_name); +} + +bool is_get_sym(IR* ir, Register dst, const std::string& sym_name) { + auto as_set = dynamic_cast(ir); + return as_set && is_reg(as_set->dst.get(), dst) && is_sym(as_set->src.get(), sym_name); +} + +bool is_label(IR* ir) { + return dynamic_cast(ir); +} + +bool is_get_label(IR* ir, Register dst) { + auto as_set = dynamic_cast(ir); + return as_set && is_reg(as_set->dst.get(), dst) && is_label(as_set->src.get()); +} + +int get_label_id_of_set(IR* ir) { + return dynamic_cast(dynamic_cast(ir)->src.get())->label_id; +} + +bool is_set_shift(IR* ir) { + auto as_set = dynamic_cast(ir); + if (as_set) { + auto as_math = dynamic_cast(as_set->src.get()); + if (as_math && (as_math->kind == IR_IntMath2::LEFT_SHIFT || + as_math->kind == IR_IntMath2::RIGHT_SHIFT_LOGIC || + as_math->kind == IR_IntMath2::RIGHT_SHIFT_ARITH)) { + return true; + } + } + + auto as_asm = dynamic_cast(ir); + return as_asm && as_asm->name == "sllv"; +} + +bool get_ptr_offset_constant_nonzero(IR_IntMath2* math, Register base, int* result) { + if (!is_reg(math->arg0.get(), base)) { + return false; + } + + auto as_int = dynamic_cast(math->arg1.get()); + if (!as_int) { + return false; + } + + *result = as_int->value; + return true; +} + +bool get_ptr_offset_zero(IR_IntMath2* math, Register base, int* result) { + if (!is_reg(math->arg0.get(), make_gpr(Reg::R0)) || !is_reg(math->arg1.get(), base)) { + return false; + } + *result = 0; + return true; +} + +bool get_ptr_offset(IR* ir, Register dst, Register base, int* result) { + auto as_set = dynamic_cast(ir); + if (!as_set) { + return false; + } + + if (!is_reg(as_set->dst.get(), dst)) { + return false; + } + + auto as_math = dynamic_cast(as_set->src.get()); + if (!as_math) { + return false; + } + return get_ptr_offset_constant_nonzero(as_math, base, result) || + get_ptr_offset_zero(as_math, base, result); +} + +bool is_weird(Function& function, LinkedObjectFile& file, TypeInspectorResult* result) { + if (function.basic_blocks.size() > 1) { + result->warnings += " too many basic blocks"; + return true; + } + + /* + ;; for a basic + or gp, a0, r0 ;; (set! gp a0) + lw t9, format(s7) ;; (set! t9 format) + daddiu a0, s7, #t ;; (set! a0 '#t) + daddiu a1, fp, L362 ;; (set! a1 L362) "[~8x] ~A~%" + or a2, gp, r0 ;; (set! a2 gp) + lwu a3, -4(gp) ;; (set! a3 (l.wu (+.i gp -4))) + jalr ra, t9 ;; (call!) + sll v0, ra, 0 + ;; for a struct + or gp, a0, r0 ;; (set! gp a0) + lw t9, format(s7) ;; (set! t9 format) + daddiu a0, s7, #t ;; (set! a0 '#t) + daddiu a1, fp, L79 ;; (set! a1 L79) "[~8x] ~A~%" + or a2, gp, r0 ;; (set! a2 gp) + daddiu a3, s7, dead-pool-heap-rec;; (set! a3 'dead-pool-heap-rec) + jalr ra, t9 ;; (call!) + */ + + // check size + if (function.basic_ops.size() < 7) { + result->warnings += " not enough basic ops"; + return true; + } + + auto& move_op = function.basic_ops.at(0); + if (!is_reg_reg_move(move_op.get(), make_gpr(Reg::GP), make_gpr(Reg::A0))) { + result->warnings += "bad first move"; + return true; + } + + auto& get_format_op = function.basic_ops.at(1); + if (!is_get_sym_value(get_format_op.get(), make_gpr(Reg::T9), "format")) { + result->warnings += "bad get format"; + return true; + } + + auto& get_true = function.basic_ops.at(2); + if (!is_get_sym(get_true.get(), make_gpr(Reg::A0), "#t")) { + result->warnings += "bad get true"; + return true; + } + + auto& get_str = function.basic_ops.at(3); + if (!is_get_label(get_str.get(), make_gpr(Reg::A1))) { + result->warnings += "bad get label"; + return true; + } + + auto str = file.get_goal_string_by_label(file.labels.at(get_label_id_of_set(get_str.get()))); + if (str != "[~8x] ~A~%") { + result->warnings += "bad type dec string: " + str; + return true; + } + + auto& move2_op = function.basic_ops.at(4); + if (!is_reg_reg_move(move2_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP))) { + result->warnings += "bad second move"; + return true; + } + + auto& load_op = function.basic_ops.at(5); + bool is_basic_load = is_get_load_with_offset(load_op.get(), make_gpr(Reg::A3), IR_Load::UNSIGNED, + 4, make_gpr(Reg::GP), -4); + result->is_basic = is_basic_load; + + bool is_struct_load = is_get_sym(load_op.get(), make_gpr(Reg::A3), function.method_of_type); + + if (!is_basic_load && !is_struct_load) { + result->warnings += "bad load"; + return true; + } + + auto& call = function.basic_ops.at(6); + if (!dynamic_cast(call.get())) { + result->warnings += "bad call"; + return true; + } + + return false; +} + +int identify_basic_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + (void)file; + auto load_info = get_load_info_from_set(function.basic_ops.at(idx++).get()); + assert(load_info.size == 4); + assert(load_info.kind == IR_Load::UNSIGNED || load_info.kind == IR_Load::SIGNED); + + if (load_info.kind == IR_Load::SIGNED) { + result->warnings += "field " + print_info.field_name + " is a basic loaded with a signed load "; + } + + int offset = load_info.offset; + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec("basic"), offset); + result->fields_of_type.push_back(field); + return idx; +} + +int identify_pointer_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + (void)file; + auto load_info = get_load_info_from_set(function.basic_ops.at(idx++).get()); + assert(load_info.size == 4); + assert(load_info.kind == IR_Load::UNSIGNED); + + int offset = load_info.offset; + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec("pointer"), offset); + result->fields_of_type.push_back(field); + return idx; +} + +int identify_array_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + auto& get_op = function.basic_ops.at(idx++); + int offset = 0; + if (!get_ptr_offset(get_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP), &offset)) { + printf("bad get ptr offset %s\n", get_op->print(file).c_str()); + assert(false); + } + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec("UNKNOWN"), offset); + if (print_info.array_size) { + field.set_array(print_info.array_size); + } else { + field.set_dynamic(); + } + result->fields_of_type.push_back(field); + return idx; +} + +int identify_float_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + auto load_info = get_load_info_from_set(function.basic_ops.at(idx++).get()); + assert(load_info.size == 4); + assert(load_info.kind == IR_Load::FLOAT); + + auto& float_move = function.basic_ops.at(idx++); + if (!is_reg_reg_move(float_move.get(), make_gpr(Reg::A2), make_fpr(0))) { + printf("bad float move: %s\n", float_move->print(file).c_str()); + assert(false); + } + + std::string type; + switch (print_info.format) { + case 'f': + type = "float"; + break; + case 'm': + type = "meters"; + break; + case 'r': + type = "deg"; + break; + case 'X': + type = "float"; + result->warnings += "field " + print_info.field_name + " is a float printed as hex? "; + break; + default: + assert(false); + } + int offset = load_info.offset; + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec(type), offset); + result->fields_of_type.push_back(field); + return idx; +} + +int identify_struct_not_inline_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + (void)file; + auto load_info = get_load_info_from_set(function.basic_ops.at(idx++).get()); + + if (!(load_info.size == 4 && load_info.kind == IR_Load::UNSIGNED)) { + result->warnings += "field " + print_info.field_type_name + " is likely a value type"; + } + int offset = load_info.offset; + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec(print_info.field_type_name), offset); + result->fields_of_type.push_back(field); + return idx; +} + +int identify_struct_inline_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + auto& get_op = function.basic_ops.at(idx++); + int offset = 0; + if (!get_ptr_offset(get_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP), &offset)) { + printf("bad get ptr offset %s\n", get_op->print(file).c_str()); + assert(false); + } + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec(print_info.field_type_name), offset); + field.set_inline(); + result->fields_of_type.push_back(field); + return idx; +} + +int identify_int_field(int idx, + Function& function, + LinkedObjectFile& file, + TypeInspectorResult* result, + FieldPrint& print_info) { + (void)file; + auto load_info = get_load_info_from_set(function.basic_ops.at(idx++).get()); + + std::string field_type_name; + if (load_info.kind == IR_Load::UNSIGNED) { + field_type_name += "u"; + } else if (load_info.kind == IR_Load::FLOAT) { + assert(false); // ... + } + field_type_name += "int"; + + switch (load_info.size) { + case 1: + field_type_name += "8"; + break; + case 2: + field_type_name += "16"; + break; + case 4: + field_type_name += "32"; + break; + case 8: + field_type_name += "64"; + break; + case 16: + field_type_name += "128"; + break; + default: + throw std::runtime_error("unknown load op size in identify int field " + + std::to_string((int)load_info.size)); + } + + if (print_info.format == 'e') { + switch (load_info.kind) { + case IR_Load::SIGNED: + field_type_name = "sseconds"; + break; + case IR_Load::UNSIGNED: + field_type_name = "useconds"; + break; + default: + assert(false); + } + assert(load_info.size == 8); + } + + int offset = load_info.offset; + if (result->is_basic) { + offset += BASIC_OFFSET; + } + + Field field(print_info.field_name, TypeSpec(field_type_name), offset); + result->fields_of_type.push_back(field); + + return idx; +} + +int detect(int idx, Function& function, LinkedObjectFile& file, TypeInspectorResult* result) { + auto& get_format_op = function.basic_ops.at(idx++); + if (!is_get_sym_value(get_format_op.get(), make_gpr(Reg::T9), "format")) { + printf("bad get format"); + assert(false); + } + + auto& get_true = function.basic_ops.at(idx++); + if (!is_get_sym(get_true.get(), make_gpr(Reg::A0), "#t")) { + printf("bad get true"); + assert(false); + } + + auto& get_str = function.basic_ops.at(idx++); + if (!is_get_label(get_str.get(), make_gpr(Reg::A1))) { + result->warnings += "bad get label"; + return true; + } + + auto str = file.get_goal_string_by_label(file.labels.at(get_label_id_of_set(get_str.get()))); + auto info = get_field_print(str); + + auto& first_get_op = function.basic_ops.at(idx); + + if (is_get_load(first_get_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP)) && + (info.format == 'D' || info.format == 'X' || info.format == 'e') && !info.has_array && + info.field_type_name.empty()) { + idx = identify_int_field(idx, function, file, result, info); + // it's a load! + } else if (is_get_load(first_get_op.get(), make_fpr(0), make_gpr(Reg::GP)) && + (info.format == 'f' || info.format == 'm' || info.format == 'r' || + info.format == 'X') && + !info.has_array && info.field_type_name.empty()) { + idx = identify_float_field(idx, function, file, result, info); + } else if (is_get_load(first_get_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP)) && + info.format == 'A' && !info.has_array && info.field_type_name.empty()) { + idx = identify_basic_field(idx, function, file, result, info); + } else if (is_get_load(first_get_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP)) && + info.format == 'X' && !info.has_array && info.field_type_name.empty()) { + idx = identify_pointer_field(idx, function, file, result, info); + } else if (info.has_array && (info.format == 'X' || info.format == 'P') && + info.field_type_name.empty()) { + idx = identify_array_field(idx, function, file, result, info); + } else if (!info.has_array && (info.format == 'X' || info.format == 'P') && + !info.field_type_name.empty()) { + // structure. + if (is_get_load(first_get_op.get(), make_gpr(Reg::A2), make_gpr(Reg::GP))) { + // not inline + idx = identify_struct_not_inline_field(idx, function, file, result, info); + } else { + idx = identify_struct_inline_field(idx, function, file, result, info); + } + } + + else if (is_set_shift(first_get_op.get())) { + result->warnings += "likely a bitfield type"; + return -1; + } else { + printf("couldn't do %s, %s\n", str.c_str(), first_get_op->print(file).c_str()); + return -1; + } + + auto& call_op = function.basic_ops.at(idx++); + if (!dynamic_cast(call_op.get())) { + printf("bad call\n"); + assert(false); + } + + return idx; +} +} // namespace + +TypeInspectorResult inspect_inspect_method(Function& inspect, + const std::string& type_name, + DecompilerTypeSystem& dts, + LinkedObjectFile& file) { + TypeInspectorResult result; + TypeFlags flags; + flags.flag = 0; + dts.lookup_flags(type_name, &flags.flag); + result.type_name = type_name; + result.parent_type_name = dts.lookup_parent_from_inspects(type_name); + result.flags = flags.flag; + result.type_size = flags.size; + result.type_method_count = flags.methods; + result.type_heap_base = flags.heap_base; + assert(flags.pad == 0); + + auto& bad_set = get_config().bad_inspect_types; + if (is_weird(inspect, file, &result) || bad_set.find(type_name) != bad_set.end()) { + // printf("was weird: %s\n", result.warnings.c_str()); + return result; + } + int idx = 7; + while (idx < int(inspect.basic_ops.size()) - 1 && idx != -1) { + idx = detect(idx, inspect, file, &result); + } + + // todo, continue to identify fields, then identify the return. + + result.success = true; + return result; +} + +std::string TypeInspectorResult::print_as_deftype() { + std::string result; + + result += fmt::format("(deftype {} ({})\n (", type_name, parent_type_name); + + int longest_field_name = 0; + int longest_type_name = 0; + int longest_mods = 0; + + std::string inline_string = ":inline"; + std::string dynamic_string = ":dynamic"; + + for (auto& field : fields_of_type) { + longest_field_name = std::max(longest_field_name, int(field.name().size())); + longest_type_name = std::max(longest_type_name, int(field.type().print().size())); + + int mods = 0; + // mods are array size, :inline, :dynamic + if (field.is_array() && !field.is_dynamic()) { + mods += std::to_string(field.array_size()).size(); + } + + if (field.is_inline()) { + if (mods) { + mods++; // space + } + mods += inline_string.size(); + } + + if (field.is_dynamic()) { + if (mods) { + mods++; // space + } + mods += dynamic_string.size(); + } + longest_mods = std::max(longest_mods, mods); + } + + for (auto& field : fields_of_type) { + result += "("; + result += field.name(); + result.append(1 + (longest_field_name - int(field.name().size())), ' '); + result += field.type().print(); + result.append(1 + (longest_type_name - int(field.type().print().size())), ' '); + + std::string mods; + if (field.is_array() && !field.is_dynamic()) { + mods += std::to_string(field.array_size()); + mods += " "; + } + + if (field.is_inline()) { + mods += inline_string; + mods += " "; + } + + if (field.is_dynamic()) { + mods += dynamic_string; + mods += " "; + } + result.append(mods); + result.append(longest_mods - int(mods.size() - 1), ' '); + + result.append(":offset-assert "); + result.append(std::to_string(field.offset())); + result.append(")\n "); + } + result.append(")\n"); + + result.append(fmt::format(" :method-count-assert {}\n", type_method_count)); + result.append(fmt::format(" :size-assert #x{:x}\n", type_size)); + result.append(fmt::format(" :flag-assert #x{:x}\n ", flags)); + if (!warnings.empty()) { + result.append(";; "); + result.append(warnings); + result.append("\n "); + } + + if (type_method_count > 9) { + result.append("(:methods\n "); + for (int i = 9; i < type_method_count; i++) { + result.append(fmt::format("(dummy-{} () none {})\n ", i, i)); + } + result.append(")\n "); + } + result.append(")\n"); + + return result; +} \ No newline at end of file diff --git a/decompiler/Function/TypeInspector.h b/decompiler/Function/TypeInspector.h new file mode 100644 index 000000000..a70c14224 --- /dev/null +++ b/decompiler/Function/TypeInspector.h @@ -0,0 +1,35 @@ +#pragma once + +/*! + * @file TypeInspector.h + * Analyze an auto-generated GOAL inspect method to determine the layout of a type in memory. + */ + +#include +#include "common/type_system/Type.h" + +class Function; +class DecompilerTypeSystem; +class LinkedObjectFile; + +struct TypeInspectorResult { + bool success = false; + int type_size = -1; + int type_method_count = -1; + int type_heap_base = -1; + + std::string warnings; + std::vector fields_of_type; + bool is_basic = false; + + std::string type_name; + std::string parent_type_name; + u64 flags = 0; + + std::string print_as_deftype(); +}; + +TypeInspectorResult inspect_inspect_method(Function& inspect, + const std::string& type_name, + DecompilerTypeSystem& dts, + LinkedObjectFile& file); diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index 836d0fe7e..f3ecb7140 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -794,8 +794,11 @@ std::string LinkedObjectFile::print_disassembly() { /*! * Hacky way to get a GOAL string object */ -std::string LinkedObjectFile::get_goal_string(int seg, int word_idx) { - std::string result = "\""; +std::string LinkedObjectFile::get_goal_string(int seg, int word_idx, bool with_quotes) { + std::string result; + if (with_quotes) { + result += "\""; + } // next should be the size if (word_idx + 1 >= int(words_by_seg[seg].size())) { return "invalid string!\n"; @@ -819,7 +822,10 @@ std::string LinkedObjectFile::get_goal_string(int seg, int word_idx) { memcpy(cword, &word.data, 4); result += cword[byte_offset]; } - return result + "\""; + if (with_quotes) { + result += "\""; + } + return result; } /*! @@ -997,4 +1003,16 @@ goos::Object LinkedObjectFile::to_form_script_object(int seg, } return result; +} + +u32 LinkedObjectFile::read_data_word(const Label& label) { + assert(0 == (label.offset % 4)); + auto& word = words_by_seg.at(label.target_segment).at(label.offset / 4); + assert(word.kind == LinkedWord::Kind::PLAIN_DATA); + return word.data; +} + +std::string LinkedObjectFile::get_goal_string_by_label(const Label& label) { + assert(0 == (label.offset % 4)); + return get_goal_string(label.target_segment, (label.offset / 4) - 1, false); } \ No newline at end of file diff --git a/decompiler/ObjectFile/LinkedObjectFile.h b/decompiler/ObjectFile/LinkedObjectFile.h index ea6b1f352..d2bff079f 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.h +++ b/decompiler/ObjectFile/LinkedObjectFile.h @@ -68,6 +68,9 @@ class LinkedObjectFile { const std::string& extra_name); std::string print_asm_function_disassembly(const std::string& my_name); + u32 read_data_word(const Label& label); + std::string get_goal_string_by_label(const Label& label); + struct Stats { uint32_t total_code_bytes = 0; uint32_t total_v2_code_bytes = 0; @@ -134,7 +137,7 @@ class LinkedObjectFile { goos::Object to_form_script_object(int seg, int byte_idx, std::vector& seen); bool is_empty_list(int seg, int byte_idx); bool is_string(int seg, int byte_idx); - std::string get_goal_string(int seg, int word_idx); + std::string get_goal_string(int seg, int word_idx, bool with_quotes = true); std::vector> label_per_seg_by_offset; }; diff --git a/decompiler/ObjectFile/ObjectFileDB.cpp b/decompiler/ObjectFile/ObjectFileDB.cpp index 4bf7a95aa..a5eef6d7b 100644 --- a/decompiler/ObjectFile/ObjectFileDB.cpp +++ b/decompiler/ObjectFile/ObjectFileDB.cpp @@ -20,6 +20,7 @@ #include "decompiler/Function/BasicBlocks.h" #include "decompiler/IR/BasicOpBuilder.h" #include "decompiler/IR/CfgBuilder.h" +#include "decompiler/Function/TypeInspector.h" #include "third-party/spdlog/include/spdlog/spdlog.h" #include "third-party/json.hpp" @@ -625,7 +626,8 @@ void ObjectFileDB::analyze_functions() { assert(func.guessed_name.empty()); func.guessed_name.set_as_top_level(); func.find_global_function_defs(data.linked_data, dts); - func.find_method_defs(data.linked_data); + func.find_type_defs(data.linked_data, dts); + func.find_method_defs(data.linked_data, dts); } }); @@ -687,10 +689,11 @@ void ObjectFileDB::analyze_functions() { int successful_type_analysis = 0; std::map> unresolved_by_length; + if (get_config().find_basic_blocks) { timer.start(); int total_basic_blocks = 0; - for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { + for_each_function_def_order([&](Function& func, int segment_id, ObjectFileData& data) { // printf("in %s from %s\n", func.guessed_name.to_string().c_str(), // data.to_unique_name().c_str()); auto blocks = find_blocks_in_function(data.linked_data, segment_id, func); @@ -718,6 +721,12 @@ void ObjectFileDB::analyze_functions() { total_basic_ops += func.get_basic_op_count(); total_failed_basic_ops += func.get_failed_basic_op_count(); + if (func.is_inspect_method) { + auto result = inspect_inspect_method(func, func.method_of_type, dts, data.linked_data); + all_type_defs += ";; " + data.to_unique_name() + "\n"; + all_type_defs += result.print_as_deftype() + "\n"; + } + // Combine basic ops + CFG to build a nested IR func.ir = build_cfg_ir(func, *func.cfg, data.linked_data); non_asm_funcs++; @@ -744,11 +753,13 @@ void ObjectFileDB::analyze_functions() { } // GOOD! func.type = kv->second; + /* spdlog::info("Type Analysis on {} {}", func.guessed_name.to_string(), kv->second.print()); func.run_type_analysis(kv->second, dts, data.linked_data); */ + if (func.has_typemaps()) { successful_type_analysis++; } diff --git a/decompiler/ObjectFile/ObjectFileDB.h b/decompiler/ObjectFile/ObjectFileDB.h index 2f18d4c4d..962935ce4 100644 --- a/decompiler/ObjectFile/ObjectFileDB.h +++ b/decompiler/ObjectFile/ObjectFileDB.h @@ -57,6 +57,7 @@ class ObjectFileDB { void analyze_functions(); ObjectFileData& lookup_record(const ObjectFileRecord& rec); DecompilerTypeSystem dts; + std::string all_type_defs; private: void load_map_file(const std::string& map_data); @@ -101,6 +102,23 @@ class ObjectFileDB { }); } + template + void for_each_function_def_order(Func f) { + for_each_obj([&](ObjectFileData& data) { + // printf("IN %s\n", data.record.to_unique_name().c_str()); + for (int i = 0; i < int(data.linked_data.segments); i++) { + // printf("seg %d\n", i); + int fn = 0; + // for (auto& goal_func : data.linked_data.functions_by_seg.at(i)) { + for (size_t j = data.linked_data.functions_by_seg.at(i).size(); j-- > 0;) { + // printf("fn %d\n", fn); + f(data.linked_data.functions_by_seg.at(i).at(j), i, data); + fn++; + } + } + }); + } + // Danger: after adding all object files, we assume that the vector never reallocates. std::unordered_map> obj_files_by_name; std::unordered_map> obj_files_by_dgo; diff --git a/decompiler/config.cpp b/decompiler/config.cpp index f270d0762..1af62c9b1 100644 --- a/decompiler/config.cpp +++ b/decompiler/config.cpp @@ -33,4 +33,9 @@ void set_config(const std::string& path_to_config_file) { for (const auto& x : asm_functions_by_name) { gConfig.asm_functions_by_name.insert(x); } + + auto bad_inspect = cfg.at("types_with_bad_inspect_methods").get>(); + for (const auto& x : bad_inspect) { + gConfig.bad_inspect_types.insert(x); + } } diff --git a/decompiler/config.h b/decompiler/config.h index 5310266cf..66312671d 100644 --- a/decompiler/config.h +++ b/decompiler/config.h @@ -10,6 +10,7 @@ struct Config { int game_version = -1; std::vector dgo_names; + std::unordered_set bad_inspect_types; std::string obj_file_name_map_file; bool write_disassembly = false; bool write_hexdump = false; diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index ba13bb2c1..723aa6112 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -6,10 +6,2220 @@ ;; TYPES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;; +;; gcommon +;;;;;;;;;;;;;; + +;; todo array +;; todo vec4s + (deftype bfloat (basic) - ((data float :offset-assert 4)) + ((data float :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 ) +(deftype inline-array-class (basic) + ((length int32 :offset-assert 4) + (allocated-length int32 :offset-assert 8) + (data uint8 :dynamic :offset-assert 12) + (_pad uint8 4) + ) + + (:methods (new (symbol type int) _type_ 0) ;; we will override print later on. This is optional to include + ) + + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;;;;;;;;;;;;;; +;; gkernel-h +;;;;;;;;;;;;;; + +(deftype kernel-context (basic) + ((prevent-from-run uint32 :offset-assert 4) + (require-for-run uint32 :offset-assert 8) + (allow-to-run uint32 :offset-assert 12) + (next-pid int32 :offset-assert 16) + (fast-stack-top uint32 :offset-assert 20) + (current-process basic :offset-assert 24) + (relocating-process basic :offset-assert 28) + (relocating-min int32 :offset-assert 32) + (relocating-max int32 :offset-assert 36) + (relocating-offset int32 :offset-assert 40) + (low-memory-message basic :offset-assert 44) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +(deftype thread (basic) + ((name basic :offset-assert 4) + (process process :offset-assert 8) + (previous thread :offset-assert 12) + (suspend-hook basic :offset-assert 16) + (resume-hook basic :offset-assert 20) + (pc pointer :offset-assert 24) + (sp pointer :offset-assert 28) + (stack-top pointer :offset-assert 32) + (stack-size int32 :offset-assert 36) + ) + + (:methods + (stack-size-set! ((this thread) (stack-size int)) none 9) + (thread-suspend ((this _type_)) none 10) + (thread-resume ((to-resume _type_)) none 11) + ) + + :method-count-assert 12 + :size-assert #x28 + :flag-assert #xc00000028 + ) + +(deftype cpu-thread (thread) + ((rreg uint64 8 :offset-assert 40) + (freg float 6 :offset-assert 104) + (stack uint8 :dynamic :offset-assert 128) + ) + + (:methods + (new ((allocation symbol) (type-to-make type) (parent-process process) (name symbol) (stack-size int) (stack-top pointer)) _type_ 0) + (thread-suspend ((this _type_)) none 10) + (thread-resume ((to-resume _type_)) none 11) + ) + + :method-count-assert 12 + :size-assert #x80 + :flag-assert #xc00000080 + ) + +(deftype process-tree (basic) + ((name basic :offset-assert 4) + (mask uint32 :offset-assert 8) + (parent pointer :offset-assert 12) + (brother pointer :offset-assert 16) + (child pointer :offset-assert 20) + (ppointer pointer :offset-assert 24) + (self basic :offset-assert 28) + ) + + (:methods + (activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) basic 9) + (deactivate ((obj _type_)) basic 10) + (dummy-method-11 () none 11) + (run-logic? ((obj _type_)) symbol 12) + (dummy-method () none 13) + ) + + :size-assert #x20 + :method-count-assert 14 + :no-runtime-type + ) + +(deftype process (process-tree) + ((pool basic :offset-assert #x20) + (status basic :offset-assert #x24) + (pid int32 :offset-assert #x28) + (main-thread thread :offset-assert #x2c) + (top-thread thread :offset-assert #x30) + (entity basic :offset-assert #x34) + (state basic :offset-assert #x38) + (trans-hook function :offset-assert #x3c) + (post-hook function :offset-assert #x40) + (event-hook function :offset-assert #x44) + (allocated-length int32 :offset-assert #x48) + (next-state basic :offset-assert #x4c) + (heap-base pointer :offset-assert #x50) + (heap-top pointer :offset-assert #x54) + (heap-cur pointer :offset-assert #x58) + (stack-frame-top stack-frame :offset-assert #x5c) + (connection-list connectable :inline :offset-assert #x60) + (stack uint8 :dynamic :offset-assert #x70) + ) + + (:methods + (activate ((obj process) (dest process-tree) (name basic) (stack-top pointer)) basic 9) + (deactivate ((obj process)) basic 10) + (dummy-method-11 () none 11) + (run-logic? ((obj process)) symbol 12) + (dummy-method () none 13) + ) + + :size-assert #x70 + :method-count-assert 14 + :no-runtime-type ;; already defined by kscheme. Don't do it again. + ) + +(deftype dead-pool (process-tree) + ( + ;; nothing new! + ) + (:methods + (get-process ((pool dead-pool) (type-to-make type) (stack-size integer)) process 14) + (return-process ((pool dead-pool) (proc process)) process-tree 15) + ) + :size-assert #x20 + :method-count-assert 16 + :flag-assert #x1000000020 + ) + +(deftype dead-pool-heap-rec (structure) + ((process process :offset-assert 0) + (prev dead-pool-heap-rec :offset-assert 4) + (next dead-pool-heap-rec :offset-assert 8) + ) + :pack-me + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +(deftype dead-pool-heap (dead-pool) + ((allocated-length int32 :offset-assert 32) + (compact-time uint32 :offset-assert 36) + (compact-count-targ uint32 :offset-assert 40) + (compact-count uint32 :offset-assert 44) + (fill-percent float :offset-assert 48) + (first-gap dead-pool-heap-rec :offset-assert 52) + (first-shrink dead-pool-heap-rec :offset-assert 56) + (heap kheap :inline :offset-assert 64) + (alive-list dead-pool-heap-rec :inline :offset-assert 80) + (last dead-pool-heap-rec :offset #x54 :offset-assert 84) + (dead-list dead-pool-heap-rec :inline :offset-assert 92) + (process-list dead-pool-heap-rec :inline :dynamic :offset-assert 104) + ) + + (:methods + (compact ((this dead-pool-heap) (count integer)) none 16) + (shrink-heap ((this dead-pool-heap) (proc process)) dead-pool-heap 17) + (churn ((this dead-pool-heap) (count integer)) none 18) + (memory-used ((this dead-pool-heap)) integer 19) + (memory-total ((this dead-pool-heap)) integer 20) + (gap-size ((this dead-pool-heap) (rec dead-pool-heap-rec)) integer 21) + (gap-location ((this dead-pool-heap) (rec dead-pool-heap-rec)) pointer 22) + (find-gap ((this dead-pool-heap) (rec dead-pool-heap-rec)) dead-pool-heap-rec 23) + (find-gap-by-size ((this dead-pool-heap) (size integer)) dead-pool-heap-rec 24) + (memory-free ((this dead-pool-heap)) integer 25) + (compact-time ((this dead-pool-heap)) integer 26) + ) + + :method-count-assert 27 + :size-assert #x68 + :flag-assert #x1b00000068 + ) + +(deftype stack-frame (basic) + ((name basic :offset 4) + (next stack-frame :offset 8) ;; which way does this point? + ) + + :size-assert #xc + :method-count-assert 9 + :flag-assert #x90000000c + ) + +(deftype catch-frame (stack-frame) + ((sp int32 :offset-assert 12) + (ra int32 :offset-assert 16) + (freg float 6 :offset-assert 20) + (rreg uint128 8 :offset-assert 48) + ) + :method-count-assert 9 + :size-assert #xb0 + :flag-assert #x9000000b0 + ) + +(deftype protect-frame (stack-frame) + ((exit function :offset-assert 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) +;; todo handle + +(deftype state (protect-frame) + ((code function :offset-assert 16) + (trans function :offset-assert 20) + (post function :offset-assert 24) + (enter function :offset-assert 28) + (event basic :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x24 + :flag-assert #x900000024 + ) + +(deftype event-message-block (structure) + ((to basic :offset-assert 0) + (from basic :offset-assert 4) + (num-params int32 :offset-assert 8) + (message basic :offset-assert 12) + (param uint64 7 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x48 + :flag-assert #x900000048 + ) + +;;;;;;;;;;;;;; +;; pskernel +;;;;;;;;;;;;;; + +(deftype lowmemmap (structure) + ((irq-info-stack uint32 :offset-assert 0) + (irq2-info-stack uint32 :offset-assert 4) + (kernel-copy-fn uint32 :offset-assert 8) + (kernel-write-fn uint32 :offset-assert 12) + (r1-save uint128 :offset-assert 16) + (last-time uint32 :offset-assert 32) + (high-time uint32 :offset-assert 36) + (dma-status uint32 :offset-assert 40) + (dma-qnext uint32 :offset-assert 44) + (dma-qwc uint32 :offset-assert 48) + (dma-tnext uint32 :offset-assert 52) + (dma-stack0 uint32 :offset-assert 56) + (dma-stack1 uint32 :offset-assert 60) + (kernel-read-fn uint32 :offset-assert 64) + ) + :method-count-assert 9 + :size-assert #x44 + :flag-assert #x900000044 + ) + +;;;;;;;;;;;;;; +;; dgo-h +;;;;;;;;;;;;;; + +(deftype dgo-entry (structure) + ((offset uint32 :offset-assert 0) + (length uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype dgo-file (basic) + ((num-go-files uint32 :offset-assert 4) + (total-length uint32 :offset-assert 8) + (rsvd uint32 :offset-assert 12) + (data uint8 :dynamic :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;;;;;;;;;;;;;; +;; math +;;;;;;;;;;;;;; + +(deftype random-generator (basic) + ((seed uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +;;;;;;;;;;;;;; +;; vector-h +;;;;;;;;;;;;;; + +;; note - this one has two inspect methods +(deftype vector (structure) + ((data float 4 :offset-assert 0) + (x float :offset 0) + (y float :offset 4) + (z float :offset 8) + (w float :offset 12) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype bit-array (basic) + ((length int32 :offset-assert 4) + (allocated-length int32 :offset-assert 8) + (_pad uint8) + ) + :method-count-assert 13 + :size-assert #xd + :flag-assert #xd0000000d + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + ) + ) + +(deftype vector4ub (structure) + ((data uint8 4 :offset-assert 0) + (x uint8 :offset 0) + (y uint8 :offset 1) + (z uint8 :offset 2) + (w uint8 :offset 3) + (clr uint32 :offset 0) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype vector4b (structure) + ((data int8 4 :offset-assert 0) + (x int8 :offset 0) + (y int8 :offset 1) + (z int8 :offset 2) + (w int8 :offset 3) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype vector2h (structure) + ((data int16 2 :offset-assert 0) + (x int16 :offset 0) + (y int16 :offset 2) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype vector2uh (structure) + ((data uint16 2 :offset-assert 0) + (x uint16 :offset 0) + (y uint16 :offset 2) + (val uint32 :offset 0) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype vector3h (structure) + ((data int16 2 :offset-assert 0) ;; probably a bug, should be 3. + (x int16 :offset 0) + (y int16 :offset 2) + (z int16 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x6 + :flag-assert #x900000006 + ) + +(deftype vector2w (structure) + ((data int32 2 :offset-assert 0) + (x int32 :offset 0) + (y int32 :offset 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype vector3w (structure) + ((data int32 3 :offset-assert 0) + (x int32 :offset 0) + (y int32 :offset 4) + (z int32 :offset 8) + ) + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +(deftype vector4w (structure) + ((data int32 4 :offset-assert 0) + (x int32 :offset 0) + (y int32 :offset 4) + (z int32 :offset 8) + (w int32 :offset 12) + (dword uint64 2 :offset 0) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype vector4w-2 (structure) + ((data int32 8 :offset-assert 0) + (quad uint128 2 :offset 0) + (vector vector4w 2 :offset 0) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype vector4w-3 (structure) + ((data int32 12 :offset-assert 0) + (quad uint128 3 :offset 0) + (vector vector4w 3 :offset 0) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +(deftype vector4w-4 (structure) + ((data int32 16 :offset-assert 0) + (quad uint128 4 :offset 0) + (vector vector4w 4 :offset 0) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +(deftype vector4h (structure) + ((data int16 4 :offset-assert 0) + (x int16 :offset 0) + (y int16 :offset 2) + (z int16 :offset 4) + (w int16 :offset 6) + (long uint64 :offset 0) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype vector8h (structure) + ((data int16 8 :offset-assert 0) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype vector16b (structure) + ((data int8 8 :offset-assert 0) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;; what's the s here? +(deftype vector4s-3 (structure) + ((data float 12 :offset-assert 0) ;; guess + (quad uint128 3 :offset 0) + (vector vector4w 3 :offset 0) ;; guess + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +(deftype vector-array (inline-array-class) + ( + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype rgbaf (vector) + ((r float :offset 0) + (g float :offset 4) + (b float :offset 8) + (a float :offset 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype plane (vector) + ((a float :offset 0) + (b float :offset 4) + (c float :offset 8) + (d float :offset 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype sphere (vector) + ((r float :offset 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype box8s (structure) + ((data float 8 :offset-assert 0) + (quad uint128 2 :offset 0) + (vector vector 2 :offset 0) + (min vector :inline :offset 0) + (max vector :inline :offset 16) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype box8s-array (inline-array-class) + () + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype cylinder (structure) + ((origin vector :inline :offset-assert 0) + (axis vector :inline :offset-assert 16) + (radius float :offset-assert 32) + (length float :offset-assert 36) + ) + :method-count-assert 11 + :size-assert #x28 + :flag-assert #xb00000028 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + ) + ) + +(deftype cylinder-flat (structure) + ((origin vector :inline :offset-assert 0) + (axis vector :inline :offset-assert 16) + (radius float :offset-assert 32) + (length float :offset-assert 36) + ) + :method-count-assert 11 + :size-assert #x28 + :flag-assert #xb00000028 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + ) + ) + +(deftype vertical-planes (structure) + ((data uint128 4 :offset-assert 0) ;; probably wrong + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +(deftype vertical-planes-array (basic) + ((length uint32 :offset-assert 4) + (data vertical-planes :dynamic :offset 16) ;; todo, why is this here? + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;; the signs on these are a guess +(deftype qword (structure) + ((data uint32 4 :offset-assert 0) + (byte uint8 16 :offset 0) + (hword uint16 8 :offset 0) + (word uint32 4 :offset 0) + (dword uint64 2 :offset 0) + (quad uint128 :offset 0) + (vector vector :inline :offset 0) + (vector4w vector4w :inline :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype vector3s (structure) + ((data float 3 :offset-assert 0) + (x float :offset 0) + (y float :offset 4) + (z float :offset 8) + ) + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + + + +;;;;;;;;;;;;;;;;;;; +;; bounding-box-h +;;;;;;;;;;;;;;;;;;; + +(deftype bounding-box (structure) + ((min vector :inline :offset-assert 0) + (max vector :inline :offset-assert 16) + ) + :method-count-assert 16 + :size-assert #x20 + :flag-assert #x1000000020 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + ) + ) + +(deftype bounding-box4w (structure) + ((min vector4w :inline :offset-assert 0) + (max vector4w :inline :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype bounding-box-both (structure) + ((box bounding-box :inline :offset-assert 0) + (box4w bounding-box4w :inline :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +;;;;;;;;;;;;;;;;;;; +;; matrix-h +;;;;;;;;;;;;;;;;;;; + +(deftype matrix (structure) + ((data float 16 :offset-assert 0) + (vector vector 4 :offset 0) + (quad uint128 4 :offset 0) + ) + :method-count-assert 10 + :size-assert #x40 + :flag-assert #xa00000040 + (:methods + (dummy-9 () none 9) + ) + ) + +(deftype matrix3 (structure) + ((data float 12 :offset-assert 0) + (vector vector 3 :offset 0) + (quad uint128 3 :offset 0) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +;; guess on signs here +(deftype matrix4h (structure) + ((data int16 16 :offset-assert 0) + (vector4h vector4h 4 :offset 0) + (long int64 4 :offset 0) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +;;;;;;;;;;;;;;;;;;; +;; quaternion-h +;;;;;;;;;;;;;;;;;;; + +(deftype quaternion (structure) + ((data float 4 :offset-assert 0) + (x float :offset 0) + (y float :offset 4) + (z float :offset 8) + (w float :offset 12) + (vec vector :inline :offset 0) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;;;;;;;;;;;;;;;;;;; +;; euler-h +;;;;;;;;;;;;;;;;;;; + +(deftype euler-angles (vector) + () + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;;;;;;;;;;;;;;;;;;; +;; transform-h +;;;;;;;;;;;;;;;;;;; + +(deftype transform (structure) + ((trans vector :inline :offset-assert 0) + (rot vector :inline :offset-assert 16) ;; maybe 3 if euler + (scale vector :inline :offset-assert 32) ;; maybe 3 + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +(deftype trs (basic) + ((trans vector :inline :offset-assert 16) + (rot vector :inline :offset-assert 32) ;; maybe 3 if euler + (scale vector :inline :offset-assert 48) ;; maybe 3 + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +;;;;;;;;;;;;;;;;;;; +;; geometry-h +;;;;;;;;;;;;;;;;;;; + +(deftype curve (structure) + ((cverts uint32 :offset-assert 0) + (num-cverts int32 :offset-assert 4) + (knots uint32 :offset-assert 8) + (num-knots int32 :offset-assert 12) + (length float :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +(deftype border-plane (basic) + ((name basic :offset-assert 4) + (action basic :offset-assert 8) + (slot int8 :offset-assert 12) + (trans vector :inline :offset-assert 16) + (normal vector :inline :offset-assert 32) + ) + :method-count-assert 11 + :size-assert #x30 + :flag-assert #xb00000030 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + ) + ) + +;;;;;;;;;;;;;;;;;;; +;; transformq-h +;;;;;;;;;;;;;;;;;;; + +(deftype transformq (transform) + ((quat quaternion :inline :offset 16) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +(deftype trsq (trs) + ((quat quaternion :inline :offset 32) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +(deftype trsqv (trsq) + ((pause-adjust-distance float :offset 4) ;; todo meters + (nav-radius float :offset 8) ;; todo meters + (transv vector :inline :offset-assert 64) + (rotv vector :inline :offset-assert 80) + (scalev vector :inline :offset-assert 96) + (dir-targ quaternion :inline :offset-assert 112) + (angle-change-time uint64 :offset-assert 128) + (old-y-angle-diff float :offset-assert 136) + ) + :method-count-assert 28 + :size-assert #x8c + :flag-assert #x1c0000008c + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + (dummy-16 () none 16) + (dummy-17 () none 17) + (dummy-18 () none 18) + (dummy-19 () none 19) + (dummy-20 () none 20) + (dummy-21 () none 21) + (dummy-22 () none 22) + (dummy-23 () none 23) + (dummy-24 () none 24) + (dummy-25 () none 25) + (dummy-26 () none 26) + (dummy-27 () none 27) + ) + ) + +;;;;;;;;;;;;;;;;;;; +;; gsound-h +;;;;;;;;;;;;;;;;;;; + +(deftype sound-rpc-cmd (structure) + ((rsvd1 uint16 :offset-assert 0) + (command uint16 :offset-assert 2) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype sound-play-parms (structure) + ((mask uint16 :offset-assert 0) + (pitch-mod int16 :offset-assert 2) + (bend int16 :offset-assert 4) + (fo-min int16 :offset-assert 6) + (fo-max int16 :offset-assert 8) + (fo-curve int8 :offset-assert 10) + (priority int8 :offset-assert 11) + (volume int32 :offset-assert 12) + (trans float 3 :offset-assert 16) ;; guess on type here + (group uint8 :offset-assert 28) + ) + :pack-me + :method-count-assert 9 + :size-assert #x1d + :flag-assert #x90000001d + ) + +(deftype sound-rpc-bank-cmd (sound-rpc-cmd) + ((bank-name uint128 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype sound-rpc-sound-cmd (sound-rpc-cmd) + ((id uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype sound-rpc-group-cmd (sound-rpc-cmd) + ((group uint8 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x5 + :flag-assert #x900000005 + ) + +(deftype sound-rpc-load-bank (sound-rpc-bank-cmd) + () + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype sound-rpc-load-music (sound-rpc-bank-cmd) + () + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype sound-rpc-unload-bank (sound-rpc-bank-cmd) + () + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype sound-rpc-play (sound-rpc-sound-cmd) + ((name uint128 :offset-assert 16) + (parms sound-play-parms :inline :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x3d + :flag-assert #x90000003d + ) + +(deftype sound-rpc-pause-sound (sound-rpc-sound-cmd) + () + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype sound-rpc-stop-sound (sound-rpc-sound-cmd) + () + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype sound-rpc-continue-sound (sound-rpc-sound-cmd) + () + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype sound-rpc-set-param (sound-rpc-sound-cmd) + ((parms sound-play-parms :inline :offset-assert 8) + (auto-time int32 :offset-assert 40) + (auto-from int32 :offset-assert 44) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +(deftype sound-rpc-set-master-volume (sound-rpc-group-cmd) + ((volume int32 :offset-assert 8) + ) + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +(deftype sound-rpc-pause-group (sound-rpc-group-cmd) + () + :method-count-assert 9 + :size-assert #x5 + :flag-assert #x900000005 + ) + +(deftype sound-rpc-stop-group (sound-rpc-group-cmd) + () + :method-count-assert 9 + :size-assert #x5 + :flag-assert #x900000005 + ) + +(deftype sound-rpc-continue-group (sound-rpc-group-cmd) + () + :method-count-assert 9 + :size-assert #x5 + :flag-assert #x900000005 + ) + +(deftype sound-rpc-get-irx-version (sound-rpc-cmd) + ((major uint32 :offset-assert 4) + (minor uint32 :offset-assert 8) + (ee-addr uint32 :offset-assert 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype sound-rpc-set-language (sound-rpc-cmd) + ((lang uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype sound-rpc-set-falloff-curve (sound-rpc-cmd) + ((curve int32 :offset-assert 4) + (falloff int32 :offset-assert 8) + (ease int32 :offset-assert 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype sound-rpc-set-sound-falloff (sound-rpc-cmd) + ((name uint128 :offset-assert 16) + (curve int32 :offset-assert 32) + (min int32 :offset-assert 36) + (max int32 :offset-assert 40) + ) + :method-count-assert 9 + :size-assert #x2c + :flag-assert #x90000002c + ) + +(deftype sound-rpc-reload-info (sound-rpc-cmd) + () + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype sound-rpc-set-reverb (sound-rpc-cmd) + ((core uint8 :offset-assert 4) + (reverb int32 :offset-assert 8) + (left uint32 :offset-assert 12) + (right uint32 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +(deftype sound-rpc-set-ear-trans (sound-rpc-cmd) + ((ear-trans float 3 :offset-assert 4) ;; guess on float, may be fixed point + (cam-trans float 3 :offset-assert 16) + (cam-angle int32 :offset-assert 28) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype sound-rpc-set-flava (sound-rpc-cmd) + ((flava uint8 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x5 + :flag-assert #x900000005 + ) + +(deftype sound-rpc-shutdown (sound-rpc-cmd) + () + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype sound-rpc-list-sounds (sound-rpc-cmd) + () + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype sound-rpc-unload-music (sound-rpc-cmd) + () + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype sound-rpc-union (structure) + ((data uint32 20 :offset-assert 0) + (load-bank sound-rpc-load-bank :offset 0) + (unload-bank sound-rpc-unload-bank :offset 0) + (play sound-rpc-play :offset 0) + (pause-sound sound-rpc-pause-sound :offset 0) + (stop-sound sound-rpc-stop-sound :offset 0) + (continue-sound sound-rpc-continue-sound :offset 0) + (set-param sound-rpc-set-param :offset 0) + (set-master-volume sound-rpc-set-master-volume :offset 0) + (pause-group sound-rpc-pause-group :offset 0) + (stop-group sound-rpc-stop-group :offset 0) + (continue-group sound-rpc-continue-group :offset 0) + (get-irx-version sound-rpc-get-irx-version :offset 0) + (set-falloff-curve sound-rpc-set-falloff-curve :offset 0) + (set-sound-falloff sound-rpc-set-sound-falloff :offset 0) + (reload-info sound-rpc-reload-info :offset 0) + (set-language sound-rpc-set-language :offset 0) + (set-reverb sound-rpc-set-reverb :offset 0) + (set-ear-trans sound-rpc-set-ear-trans :offset 0) + (set-flava sound-rpc-set-flava :offset 0) + (shutdown sound-rpc-shutdown :offset 0) + (list-sounds sound-rpc-list-sounds :offset 0) + (unload-music sound-rpc-unload-music :offset 0) + ) + :method-count-assert 9 + :size-assert #x50 + :flag-assert #x900000050 + ) + +(deftype sound-spec (basic) + ((mask uint16 :offset-assert 4) + (num float :offset-assert 8) + (group uint8 :offset-assert 12) + (sound-name-char uint8 16 :offset 16) + (sound-name uint128 :offset 16) + (trans float 4 :offset-assert 32) ;; guess + (volume int32 :offset-assert 48) + (pitch-mod int32 :offset-assert 52) + (bend int32 :offset-assert 56) + (fo-min int16 :offset-assert 60) + (fo-max int16 :offset-assert 62) + (fo-curve int8 :offset-assert 64) + (priority int8 :offset-assert 65) + (auto-time int32 :offset-assert 68) + (auto-from int32 :offset-assert 72) + ) + :method-count-assert 9 + :size-assert #x4c + :flag-assert #x90000004c + ) + +(deftype ambient-sound (basic) + ((spec basic :offset-assert 4) + (playing-id uint32 :offset-assert 8) + (trans vector :inline :offset-assert 16) + (name uint128 :offset-assert 32) + (play-time uint64 :offset-assert 48) + (time-base uint64 :offset-assert 56) + (time-random uint64 :offset-assert 64) + (volume int32 :offset-assert 72) + (pitch int32 :offset-assert 76) + (falloff-near int32 :offset-assert 80) + (falloff-far int32 :offset-assert 84) + (falloff-mode int32 :offset-assert 88) + (params uint32 :offset-assert 92) + (param-count int32 :offset-assert 96) + (entity basic :offset-assert 100) + (sound-count int32 :offset-assert 104) + ) + :method-count-assert 14 + :size-assert #x6c + :flag-assert #xe0000006c + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + ) + ) + +;;;;;;;;;;;;; +;; timer-h +;;;;;;;;;;;;; + +(deftype timer-bank (structure) + ((count uint32 :offset-assert 0) + (mode uint32 :offset 16) ;; not sure why these are so widely spaced. match hw? + (comp uint32 :offset 32) + ) + :method-count-assert 9 + :size-assert #x24 + :flag-assert #x900000024 + ) + +(deftype timer-hold-bank (timer-bank) + ((hold uint32 :offset 48) ;; not sure why so widely spaced + ) + :method-count-assert 9 + :size-assert #x34 + :flag-assert #x900000034 + ) + +(deftype stopwatch (basic) + ((prev-time-elapsed uint64 :offset-assert 8) + (start-time uint64 :offset-assert 16) + (begin-level int32 :offset-assert 24) + ) + :method-count-assert 9 + :size-assert #x1c + :flag-assert #x90000001c + ) + +(deftype profile-frame (structure) + ((name basic :offset-assert 0) + (time-stamp uint32 :offset-assert 4) + (color uint32 :offset-assert 8) + ) + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +(deftype profile-bar (basic) + ((profile-frame-count int32 :offset-assert 4) + (cache-time uint64 :offset-assert 8) + (data profile-frame 1024 :inline :offset-assert 16) + ) + :method-count-assert 14 + :size-assert #x4010 + :flag-assert #xe00004010 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + ) + ) + +;;;;;;;;;;;;; +;; vif-h +;;;;;;;;;;;;; + +;; todo vif-stat + +;; these are spaced strangely, is this some HW map? +(deftype vif-bank (structure) + ((stat uint32 :offset-assert 0) + (fbrst uint32 :offset 16) + (err uint32 :offset 32) + (mark uint32 :offset 48) + (cycle uint32 :offset 64) + (mode uint32 :offset 80) + (num uint32 :offset 96) + (mask uint32 :offset 112) + (code uint32 :offset 128) + (itops uint32 :offset 144) + (base uint32 :offset 160) + (offset uint32 :offset 176) + (tops uint32 :offset 192) + (itop uint32 :offset 208) + (top uint32 :offset 224) + (r0 uint32 :offset 256) + (r1 uint32 :offset 272) + (r2 uint32 :offset 288) + (r3 uint32 :offset 304) + (c0 uint32 :offset 320) + (c1 uint32 :offset 336) + (c2 uint32 :offset 352) + (c3 uint32 :offset 368) + ) + :method-count-assert 9 + :size-assert #x174 + :flag-assert #x900000174 + ) + +;;;;;;;;;;;;; +;; dma-h +;;;;;;;;;;;;; + +;; todo dma-chcr + +(deftype dma-bank (structure) + ((chcr uint32 :offset 0) + (madr uint32 :offset 16) + (qwc uint32 :offset 32) + ) + :method-count-assert 9 + :size-assert #x24 + :flag-assert #x900000024 + ) + +(deftype dma-bank-source (dma-bank) + ((tadr uint32 :offset 48) + ) + :method-count-assert 9 + :size-assert #x34 + :flag-assert #x900000034 + ) + +(deftype dma-bank-vif (dma-bank-source) + ((as0 uint32 :offset 64) + (as1 uint32 :offset 80) + ) + :method-count-assert 9 + :size-assert #x54 + :flag-assert #x900000054 + ) + +(deftype dma-bank-spr (dma-bank-source) + ((sadr uint32 :offset 128) + ) + :method-count-assert 9 + :size-assert #x84 + :flag-assert #x900000084 + ) + +(deftype dma-bank-control (structure) + ((ctrl uint32 :offset 0) + (stat uint32 :offset 16) + (pcr uint32 :offset 32) + (sqwc uint32 :offset 48) + (rbsr uint32 :offset 64) + (rbor uint32 :offset 80) + (stadr uint32 :offset 96) + (enabler uint32 :offset 5408) + (enablew uint32 :offset 5520) + ) + :method-count-assert 9 + :size-assert #x1594 + :flag-assert #x900001594 + ) + +(deftype vu-code-block (basic) + ((name basic :offset-assert 4) + (code uint32 :offset-assert 8) + (size int32 :offset-assert 12) + (dest-address uint32 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +;; todo dma-tag +(deftype dma-bucket (structure) + ((tag uint64 :offset-assert 0) + (last uint32 :offset-assert 8) + (dummy uint32 :offset-assert 12) + (next uint32 :offset 4) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;; todo vif-tag + +;;;;;;;;;;;;; +;; video-h +;;;;;;;;;;;;; + +(deftype video-parms (structure) + ((set-video-mode basic :offset-assert 0) + (reset-video-mode basic :offset-assert 4) + (screen-sy int32 :offset-assert 8) + (screen-hy int32 :offset-assert 12) + (screen-miny int32 :offset-assert 16) + (screen-maxy int32 :offset-assert 20) + (screen-masky int32 :offset-assert 24) + (display-dx int32 :offset-assert 28) + (display-dy int32 :offset-assert 32) + (screen-pages-high int32 :offset-assert 36) + (_pad int64) + (relative-x-scale float :offset-assert 48) + (relative-y-scale float :offset-assert 52) + (_pad2 int64) + (relative-x-scale-reciprical float :offset-assert 64) + (relative-y-scale-reciprical float :offset-assert 68) + ) + :method-count-assert 9 + :size-assert #x48 + :flag-assert #x900000048 + ) + +;;;;;;;;;;;;;;; +;; vu1-user-h +;;;;;;;;;;;;;;; + +(deftype dma-foreground-sink (basic) + ((bucket int32 :offset-assert 4) + (foreground-texture-page int8 :offset-assert 8) + (foreground-texture-level int8 :offset-assert 9) + (foreground-output-bucket int8 :offset-assert 10) + ) + :method-count-assert 9 + :size-assert #xb + :flag-assert #x90000000b + ) + +(deftype generic-bucket-state (structure) + ((gifbuf-adr uint32 :offset-assert 0) + (inbuf-adr uint32 :offset-assert 4) + ) + :pack-me + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(deftype generic-dma-foreground-sink (dma-foreground-sink) + ((state generic-bucket-state :inline :offset-assert 12) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +(deftype dma-foreground-sink-group (basic) + ((sink basic 3 :offset-assert 4) + (merc-sink basic :offset 4) + (generic-sink basic :offset 8) + (level basic :offset 16) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +;;;;;;;;;;;;;;; +;; dma-buffer +;;;;;;;;;;;;;;; + +(deftype dma-packet (structure) + ((dma uint64 :offset-assert 0) + (vif0 uint32 :offset-assert 8) + (vif1 uint32 :offset-assert 12) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype dma-packet-array (inline-array-class) + () + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(deftype dma-gif-packet (structure) + ((dma-vif dma-packet :inline :offset-assert 0) + (gif uint64 2 :offset-assert 16) ;; guess + (quad uint128 2 :offset 0) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(deftype dma-buffer (basic) + ((allocated-length int32 :offset-assert 4) + (base uint32 :offset-assert 8) + (end uint32 :offset-assert 12) + (data uint64 1 :offset-assert 16) ;; hmmm, seems wrong + ) + :method-count-assert 9 + :size-assert #x18 + :flag-assert #x900000018 + ) + +;;;;;;;;;;;;;;; +;; dma-disasm +;;;;;;;;;;;;;;; + +(deftype vif-disasm-element (structure) + ((mask uint32 :offset-assert 0) + (tag uint32 :offset-assert 4) + (val uint32 :offset-assert 8) + (print uint32 :offset-assert 12) + (string1 basic :offset-assert 16) + (string2 basic :offset-assert 20) + ) + :method-count-assert 9 + :size-assert #x18 + :flag-assert #x900000018 + ) + +;;;;;;;;;;;;;;; +;; pad +;;;;;;;;;;;;;;; + +(deftype hw-cpad (basic) + ((valid uint8 :offset-assert 4) + (status uint8 :offset-assert 5) + (button0 uint16 :offset-assert 6) + (rightx uint8 :offset-assert 8) + (righty uint8 :offset-assert 9) + (leftx uint8 :offset-assert 10) + (lefty uint8 :offset-assert 11) + (abutton uint8 12 :offset-assert 12) + (dummy uint8 12 :offset-assert 24) + ) + :method-count-assert 9 + :size-assert #x24 + :flag-assert #x900000024 + ) + +(deftype cpad-info (hw-cpad) + ((number int32 :offset-assert 36) + (cpad-file int32 :offset-assert 40) + (button0-abs int32 3 :offset-assert 44) ;; guess + (button0-shadow-abs int32 1 :offset-assert 56) ;; guess + (button0-rel int32 3 :offset-assert 60) ;; guess + (stick0-dir float :offset-assert 72) + (stick0-speed float :offset-assert 76) + (new-pad int32 :offset-assert 80) + (state int32 :offset-assert 84) + (align uint8 6 :offset-assert 88) + (direct uint8 6 :offset-assert 94) + (buzz-val uint16 2 :offset-assert 100) + (buzz-time uint64 2 :offset-assert 104) + (buzz basic :offset-assert 120) + (buzz-act int32 :offset-assert 124) + (change-time uint64 :offset-assert 128) + ) + :method-count-assert 9 + :size-assert #x88 + :flag-assert #x900000088 + ) + +(deftype cpad-list (basic) + ((num-cpads int32 :offset-assert 4) + (cpads int32 2 :offset-assert 8) ;; guess + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;;;;;;;;;;;;;;; +;; gs +;;;;;;;;;;;;;;; + +(deftype gs-bank (structure) + ((pmode uint64 :offset-assert 0) + (smode2 uint64 :offset 32) + (dspfb1 uint64 :offset 112) + (display1 uint64 :offset 128) + (dspfb2 uint64 :offset 144) + (display2 uint64 :offset 160) + (extbuf uint64 :offset 176) + (extdata uint64 :offset 192) + (extwrite uint64 :offset 208) + (bgcolor uint64 :offset 224) + (csr uint64 :offset 4096) + (imr uint64 :offset 4112) + (busdir uint64 :offset 4160) + ) + :method-count-assert 9 + :size-assert #x1048 + :flag-assert #x900001048 + ) + +;; todo gs-alpha, gs-fog, gs-fogcol + +(deftype gif-bank (structure) + ((ctrl uint32 :offset 0) + (mode uint32 :offset 16) + (stat uint32 :offset 32) + (tag0 uint32 :offset 64) + (tag1 uint32 :offset 80) + (tag2 uint32 :offset 96) + (tag3 uint32 :offset 112) + (cnt uint32 :offset 128) + (p3cnt uint32 :offset 144) + (p3tag uint32 :offset 160) + ) + :method-count-assert 9 + :size-assert #xa4 + :flag-assert #x9000000a4 + ) + +(deftype gs-gif-tag (structure) + ((qword uint128 :offset-assert 0) ;; is "qword" and inline? in game + (dword uint64 2 :offset 0) + (word uint32 4 :offset 0) + (tag uint64 :offset 0) + (regs uint64 :offset 8) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;; todo gif-tag +(deftype gif-packet (basic) + ((reg-count int32 :offset-assert 4) + (gif-tag0 uint128 :offset-assert 16) + (args uint64 1 :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x28 + :flag-assert #x900000028 + ) + +(deftype draw-context (basic) + ((orgx int32 :offset-assert 4) + (orgy int32 :offset-assert 8) + (orgz int32 :offset-assert 12) + (width int32 :offset-assert 16) + (height int32 :offset-assert 20) + (color int32 4 :offset-assert 24) + ) + :method-count-assert 9 + :size-assert #x28 + :flag-assert #x900000028 + ) + +;;;;;;;;;;;;;;; +;; display-h +;;;;;;;;;;;;;;; + +(deftype display-env (structure) + ((pmode uint64 :offset-assert 0) + (smode2 uint64 :offset-assert 8) + (dspfb uint64 :offset-assert 16) + (display uint64 :offset-assert 24) + (bgcolor uint64 :offset-assert 32) + ) + :pack-me + :method-count-assert 9 + :size-assert #x28 + :flag-assert #x900000028 + ) + +(deftype draw-env (structure) + ((frame1 uint64 :offset-assert 0) + (frame1addr uint64 :offset-assert 8) + (zbuf1 uint64 :offset-assert 16) + (zbuf1addr uint64 :offset-assert 24) + (xyoffset1 uint64 :offset-assert 32) + (xyoffset1addr uint64 :offset-assert 40) + (scissor1 uint64 :offset-assert 48) + (scissor1addr uint64 :offset-assert 56) + (prmodecont uint64 :offset-assert 64) + (prmodecontaddr uint64 :offset-assert 72) + (colclamp uint64 :offset-assert 80) + (colclampaddr uint64 :offset-assert 88) + (dthe uint64 :offset-assert 96) + (dtheaddr uint64 :offset-assert 104) + (test1 uint64 :offset-assert 112) + (test1addr uint64 :offset-assert 120) + ) + :method-count-assert 9 + :size-assert #x80 + :flag-assert #x900000080 + ) + +(deftype display-frame (basic) + ((buffer int32 11 :offset-assert 4) ;; no idea + (calc-buf basic :offset 8) + (vu1-buf basic :offset 8) + (debug-buf basic :offset 36) + (global-buf basic :offset 40) + (bucket-group dma-bucket :offset 44) + (profile-bar basic 2 :offset 48) + (run-time uint64 :offset 56) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +(deftype virtual-frame (structure) + ((display display-env :offset-assert 0) + (display-last display-env :offset-assert 4) + (gif uint32 :offset-assert 8) + (draw draw-env :offset-assert 12) + (frame basic :offset-assert 16) + ) + :pack-me + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +;; todo +; (deftype display (basic) +; ((display-env0 display-env :inline :offset-assert 8) +; (display-env1 display-env :inline :offset-assert 48) +; (display-env2 display-env :inline :offset-assert 88) +; (gif-tag0 uint128 :offset-assert 128) +; (draw0 draw-env :inline :offset-assert 144) +; (gif-tag1 uint128 :offset-assert 272) +; (draw1 draw-env :inline :offset-assert 288) +; (gif-tag2 uint128 :offset-assert 416) +; (draw2 draw-env :inline :offset-assert 432) +; (on-screen int32 :offset-assert 560) +; (last-screen int32 :offset-assert 564) +; (frames UNKNOWN 6 :offset-assert 568) +; (bg-clear-color UNKNOWN 4 :offset-assert 760) +; (real-frame-counter uint64 :offset-assert 776) +; (base-frame-counter uint64 :offset-assert 784) +; (game-frame-counter uint64 :offset-assert 792) +; (integral-frame-counter uint64 :offset-assert 800) +; (real-integral-frame-counter uint64 :offset-assert 808) +; (actual-frame-counter uint64 :offset-assert 816) +; (real-actual-frame-counter uint64 :offset-assert 824) +; (part-frame-counter uint64 :offset-assert 832) +; (old-real-frame-counter uint64 :offset-assert 840) +; (old-base-frame-counter uint64 :offset-assert 848) +; (old-game-frame-counter uint64 :offset-assert 856) +; (old-integral-frame-counter uint64 :offset-assert 864) +; (old-real-integral-frame-counter uint64 :offset-assert 872) +; (old-actual-frame-counter uint64 :offset-assert 880) +; (old-real-actual-frame-counter uint64 :offset-assert 888) +; (old-part-frame-counter uint64 :offset-assert 896) +; (time-ratio float :offset-assert 904) +; (seconds-per-frame float :offset-assert 908) +; (frames-per-second float :offset-assert 912) +; (time-factor float :offset-assert 916) +; (time-adjust-ratio float :offset-assert 920) +; ) +; :method-count-assert 10 +; :size-assert #x39c +; :flag-assert #xa0000039c +; (:methods +; (dummy-9 () none 9) +; ) +; ) + +;;;;;;;;;;;;;;; +;; file-io +;;;;;;;;;;;;;;; + +;; todo, builtin type issue +; (deftype file-stream (basic) +; ((flags uint32 :offset-assert 4) +; (mode basic :offset-assert 8) +; (name basic :offset-assert 12) +; (file uint32 :offset-assert 16) +; ) +; :method-count-assert 9 +; :size-assert #x14 +; :flag-assert #x900000014 +; ) + +(deftype file-info (basic) + ((file-type basic :offset-assert 4) + (file-name basic :offset-assert 8) + (major-version uint32 :offset-assert 12) + (minor-version uint32 :offset-assert 16) + (maya-file-name basic :offset-assert 20) + (tool-debug basic :offset-assert 24) + (mdb-file-name basic :offset-assert 28) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +;;;;;;;;;;;;;;; +;; loader-h +;;;;;;;;;;;;;;; + +(deftype external-art-buffer (basic) + ((index int32 :offset-assert 4) + (other basic :offset-assert 8) + (status basic :offset-assert 12) + (locked? basic :offset-assert 16) + (frame-lock basic :offset-assert 20) + (heap kheap :inline :offset-assert 32) + (pending-load-file basic :offset-assert 48) + (pending-load-file-part int32 :offset-assert 52) + (pending-load-file-owner uint64 :offset-assert 56) + (pending-load-file-priority float :offset-assert 64) + (load-file basic :offset-assert 68) + (load-file-part int32 :offset-assert 72) + (load-file-owner uint64 :offset-assert 80) + (load-file-priority float :offset-assert 88) + (buf uint32 :offset-assert 92) + (len int32 :offset-assert 96) + (art-group basic :offset-assert 100) + ) + :method-count-assert 16 + :size-assert #x68 + :flag-assert #x1000000068 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + ) + ) + +;; loader-h +(deftype spool-anim (basic) + ((name basic :offset 16) ;; why? + (index int32 :offset-assert 20) + (parts int32 :offset-assert 24) + (priority float :offset-assert 28) + (owner uint64 :offset-assert 32) + (command-list basic :offset-assert 40) + ) + :method-count-assert 9 + :size-assert #x2c + :flag-assert #x90000002c + ) + +;; loader-h +; todo inline basics missing offset?? +; (deftype external-art-control (basic) +; ((buffer basic 2 :offset-assert 4) +; ;(rec basic 3 :offset-assert 20) some inline structure +; (_pad uint8 142) +; (spool-lock uint64 :offset-assert 160) +; (reserve-buffer basic :offset-assert 168) +; (reserve-buffer-count int32 :offset-assert 172) +; (active-stream basic :offset-assert 176) +; (preload-stream spool-anim :inline :offset-assert 188) +; (last-preload-stream spool-anim :inline :offset-assert 236) +; ) +; :method-count-assert 17 +; :size-assert #x118 +; :flag-assert #x1100000118 +; (:methods +; (dummy-9 () none 9) +; (dummy-10 () none 10) +; (dummy-11 () none 11) +; (dummy-12 () none 12) +; (dummy-13 () none 13) +; (dummy-14 () none 14) +; (dummy-15 () none 15) +; (dummy-16 () none 16) +; ) +; ) + +;;;;;;;;;;;;;; +;; texture-h +;;;;;;;;;;;;;; + +;; texture-h +;; todo texture-id + +;; texture-h +(deftype texture-pool-segment (structure) + ((dest uint32 :offset-assert 0) + (size uint32 :offset-assert 4) + ) + :pack-me + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +;; texture-h +(deftype texture-pool (basic) + ((top int32 :offset-assert 4) + (cur int32 :offset-assert 8) + (allocate-func basic :offset-assert 12) + (font-palette int32 :offset-assert 16) + (segment texture-pool-segment 4 :inline :offset-assert 20) + (segment-near texture-pool-segment :inline :offset 20) + (segment-common texture-pool-segment :inline :offset 28) + (common-page int32 32 :offset-assert 52) + (common-page-mask int32 :offset-assert 180) + (ids int32 126 :offset-assert 184) + ) + :method-count-assert 23 + :size-assert #x2b0 + :flag-assert #x17000002b0 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + (dummy-16 () none 16) + (dummy-17 () none 17) + (dummy-18 () none 18) + (dummy-19 () none 19) + (dummy-20 () none 20) + (dummy-21 () none 21) + (dummy-22 () none 22) + ) + ) + +;; texture-h +(deftype texture (basic) + ((w int16 :offset-assert 4) + (h int16 :offset-assert 6) + (num-mips uint8 :offset-assert 8) + (tex1-control uint8 :offset-assert 9) + (psm uint8 :offset-assert 10) + (mip-shift uint8 :offset-assert 11) + (clutpsm uint16 :offset-assert 12) + (dest uint16 7 :offset-assert 14) + (clutdest uint16 :offset-assert 28) + (width uint8 7 :offset-assert 30) + (name basic :offset-assert 40) + (size uint32 :offset-assert 44) + (uv-dist float :offset-assert 48) + (masks uint32 3 :offset-assert 52) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +;; texture-h +(deftype texture-page-segment (structure) + ((block-data uint32 :offset-assert 0) + (size uint32 :offset-assert 4) + (dest uint32 :offset-assert 8) + ) + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +;; texture-h +(deftype texture-page (basic) + ((info basic :offset-assert 4) + (name basic :offset-assert 8) + (id uint32 :offset-assert 12) + (length int32 :offset-assert 16) + (mip0-size uint32 :offset-assert 20) + (size uint32 :offset-assert 24) + (segment texture-pool-segment 3 :inline :offset-assert 28) ;; this is wrong + (pad uint32 16 :offset 64) + (data uint8 :dynamic :offset-assert 128) + ) + :method-count-assert 15 + :size-assert #x80 + :flag-assert #xf00000080 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + ) + ) + +;; texture-h +(deftype texture-link (structure) + ((next uint32 :offset-assert 0) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +;; texture-h +(deftype texture-page-dir-entry (structure) + ((length int16 :offset-assert 0) + (status uint16 :offset-assert 2) + (page basic :offset-assert 4) + (link uint32 :offset-assert 8) + ) + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +;; texture-h +(deftype texture-relocate-later (basic) + ((memcpy basic :offset-assert 4) + (dest uint32 :offset-assert 8) + (source uint32 :offset-assert 12) + (move uint32 :offset-assert 16) + (entry texture-page-dir-entry :offset-assert 20) + (page basic :offset-assert 24) + ) + :method-count-assert 9 + :size-assert #x1c + :flag-assert #x90000001c + ) + +;; texture-h +(deftype adgif-shader (structure) + ( + ;;(quad UNKNOWN 5 :offset-assert 0) + ;;(prims UNKNOWN 10 :offset-assert 0) + (tex0 uint64 :offset-assert 0) + (tex1 uint64 :offset 16) + (miptbp1 uint64 :offset 32) + (clamp uint64 :offset 48) + (clamp-reg uint64 :offset 56) + (alpha uint64 :offset 64) + (link-test uint32 :offset 8) + (texture-id uint32 :offset 24) + (next uint32 :offset 40) + (_pad uint64) + ) + :method-count-assert 9 + :size-assert #x50 + :flag-assert #x900000050 + ) + +;; texture-h +(deftype adgif-shader-array (inline-array-class) + () + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +;;;;;;;;;;;;;; +;; level-h +;;;;;;;;;;;;;; + +;; level-h +(deftype level-vis-info (basic) + ((level basic :offset-assert 4) + (from-level basic :offset-assert 8) + (from-bsp basic :offset-assert 12) + (flags uint32 :offset-assert 16) + (length uint32 :offset-assert 20) + (allocated-length uint32 :offset-assert 24) + (dictionary-length uint32 :offset-assert 28) + (dictionary uint32 :offset-assert 32) + (string-block uint32 :offset-assert 36) + (ramdisk uint32 :offset-assert 40) + (vis-bits uint32 :offset-assert 44) + (current-vis-string uint32 :offset-assert 48) + (vis-string uint8 :dynamic :offset-assert 52) + ) + :method-count-assert 9 + :size-assert #x34 + :flag-assert #x900000034 + ) + +;; level-h +(deftype level-load-info (basic) + ((name-list basic 3 :offset-assert 4) + (index int32 :offset-assert 16) + (name basic :offset 4) + (visname basic :offset 8) + (nickname basic :offset 12) + (packages basic :offset-assert 20) + (sound-banks basic :offset-assert 24) + (music-bank basic :offset-assert 28) + (ambient-sounds basic :offset-assert 32) + (mood basic :offset-assert 36) + (mood-func basic :offset-assert 40) + (ocean basic :offset-assert 44) + (sky basic :offset-assert 48) + (sun-fade float :offset-assert 52) + (continues basic :offset-assert 56) + (tasks basic :offset-assert 60) + (priority int32 :offset-assert 64) + (load-commands basic :offset-assert 68) + (alt-load-commands basic :offset-assert 72) + (bsp-mask uint64 :offset-assert 80) + (bsphere sphere :offset-assert 88) + (buzzer int32 :offset-assert 92) + (bottom-height float :offset-assert 96) ;; meters + (run-packages basic :offset-assert 100) + (prev-level basic :offset-assert 104) + (next-level basic :offset-assert 108) + (wait-for-load basic :offset-assert 112) + ) + :method-count-assert 9 + :size-assert #x74 + :flag-assert #x900000074 + ) + +;; level-h +(deftype login-state (basic) + ((state int32 :offset-assert 4) + (pos uint32 :offset-assert 8) + (elts uint32 :offset-assert 12) + (elt uint32 16 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x50 + :flag-assert #x900000050 + ) + +; ;; level-h +; (deftype level (basic) +; ((name basic :offset-assert 4) +; (load-name basic :offset-assert 8) +; (nickname basic :offset-assert 12) +; (index int32 :offset-assert 16) +; (status basic :offset-assert 20) +; (other basic :offset-assert 24) +; (heap kheap :inline :offset-assert 32) +; (bsp basic :offset-assert 48) +; (art-group basic :offset-assert 52) +; (info basic :offset-assert 56) +; (texture-page UNKNOWN 9 :offset-assert 60) +; (loaded-texture-page UNKNOWN 16 :offset-assert 96) +; (loaded-texture-page-count int32 :offset-assert 160) +; (foreground-sink-group UNKNOWN 3 :offset-assert 180) +; (foreground-draw-engine UNKNOWN 3 :offset-assert 272) +; (entity basic :offset-assert 284) +; (ambient basic :offset-assert 288) +; (closest-object UNKNOWN 9 :offset-assert 292) +; (upload-size UNKNOWN 9 :offset-assert 328) +; (level-distance meters :offset-assert 364) +; (inside-sphere? basic :offset-assert 368) +; (inside-boxes? basic :offset-assert 372) +; (display? basic :offset-assert 376) +; (meta-inside? basic :offset-assert 380) +; (mood basic :offset-assert 384) +; (mood-func basic :offset-assert 388) +; (vis-bits uint32 :offset-assert 392) +; (all-visible? basic :offset-assert 396) +; (force-all-visible? basic :offset-assert 400) +; (linking basic :offset-assert 404) +; (vis-info UNKNOWN 8 :offset-assert 408) +; (vis-self-index int32 :offset-assert 440) +; (vis-adj-index int32 :offset-assert 444) +; (vis-buffer UNKNOWN 2048 :offset-assert 448) +; (mem-usage-block basic :offset-assert 2496) +; (mem-usage int32 :offset-assert 2500) +; (code-memory-start uint32 :offset-assert 2504) +; (code-memory-end uint32 :offset-assert 2508) +; (texture-mask UNKNOWN 9 :offset-assert 2512) +; (force-inside? basic :offset-assert 2548) +; ) +; :method-count-assert 29 +; :size-assert #xa30 +; :flag-assert #x1d00000a30 +; (:methods +; (dummy-9 () none 9) +; (dummy-10 () none 10) +; (dummy-11 () none 11) +; (dummy-12 () none 12) +; (dummy-13 () none 13) +; (dummy-14 () none 14) +; (dummy-15 () none 15) +; (dummy-16 () none 16) +; (dummy-17 () none 17) +; (dummy-18 () none 18) +; (dummy-19 () none 19) +; (dummy-20 () none 20) +; (dummy-21 () none 21) +; (dummy-22 () none 22) +; (dummy-23 () none 23) +; (dummy-24 () none 24) +; (dummy-25 () none 25) +; (dummy-26 () none 26) +; (dummy-27 () none 27) +; (dummy-28 () none 28) +; ) +; ) + +; ;; level-h +; (deftype level-group (basic) +; ((length int32 :offset-assert 4) +; (entity-link entity-links :offset-assert 16) +; (border? basic :offset-assert 20) +; (vis? basic :offset-assert 24) +; (want-level basic :offset-assert 28) +; (receiving-level basic :offset-assert 32) +; (load-commands basic :offset-assert 36) +; (play? basic :offset-assert 40) +; (level UNKNOWN 3 :offset-assert 100) +; (data UNKNOWN 3 :offset-assert 100) +; (level0 level :inline :offset-assert 100) +; (level1 level :inline :offset-assert 2708) +; (level-default level :inline :offset-assert 5316) +; ) +; :method-count-assert 27 +; :size-assert #x1ef4 +; :flag-assert #x1b00001ef4 +; (:methods +; (dummy-9 () none 9) +; (dummy-10 () none 10) +; (dummy-11 () none 11) +; (dummy-12 () none 12) +; (dummy-13 () none 13) +; (dummy-14 () none 14) +; (dummy-15 () none 15) +; (dummy-16 () none 16) +; (dummy-17 () none 17) +; (dummy-18 () none 18) +; (dummy-19 () none 19) +; (dummy-20 () none 20) +; (dummy-21 () none 21) +; (dummy-22 () none 22) +; (dummy-23 () none 23) +; (dummy-24 () none 24) +; (dummy-25 () none 25) +; (dummy-26 () none 26) +; ) +; ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SYMBOLS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -36,8 +2246,8 @@ (define-extern lognor (function int int int)) (define-extern logxor (function int int int)) (define-extern lognot (function int int)) -(define-extern false-func (function basic)) -(define-extern true-func (function basic)) +(define-extern false-func (function symbol)) +(define-extern true-func (function symbol)) ;; format ;; vec4s ;; vec4s method 3 @@ -93,70 +2303,51 @@ (define-extern function type) -(define-extern insert-cons! function) -(define-extern / function) -(define-extern 1/ function) -(define-extern lognot function) -(define-extern basic-type? function) +(define-extern insert-cons! (function object object pair)) +(define-extern basic-type? (function basic type symbol)) ;;(define-extern pair object) ;; unknown type (define-extern qmem-copy->! function) -(define-extern mem-set32! function) -(define-extern delete-car! function) -(define-extern logxor function) +(define-extern mem-set32! (function pointer int int pointer)) +(define-extern delete-car! (function object object object)) ;;(define-extern inline-array-class object) ;; unknown type (define-extern mem-print function) ;;(define-extern vec4s object) ;; unknown type (define-extern array type) (define-extern print-tree-bitmask function) -(define-extern logior function) (define-extern quad-copy! function) -(define-extern assoc function) +(define-extern assoc (function object object object)) (define-extern mem-or! function) (define-extern fact function) -(define-extern max function) -(define-extern assoce function) -(define-extern mod function) -(define-extern false-func function) +(define-extern assoce (function object object object)) (define-extern basic type) -(define-extern type-type? function) -(define-extern member function) -(define-extern nmember function) +(define-extern type-type? (function type type symbol)) +(define-extern member (function object object object)) +(define-extern nmember (function basic object object)) (define-extern breakpoint-range-set! function) -(define-extern abs function) (define-extern nassoc function) ;;(define-extern _format object) ;; unknown type -(define-extern logand function) -(define-extern find-parent-method function) -(define-extern true-func function) +(define-extern find-parent-method (function type int function)) (define-extern nassoce function) -(define-extern ref function) +(define-extern ref (function object int object)) ;;(define-extern *print-column* object) ;; unknown type -(define-extern delete! function) +(define-extern delete! (function object object pair)) (define-extern qmem-copy<-! function) -(define-extern rem function) -(define-extern mem-copy! function) -(define-extern inspect function) +(define-extern mem-copy! (function pointer pointer integer pointer)) (define-extern type type) ;;(define-extern format object) ;; unknown type ;;(define-extern uint128 object) ;; unknown type -(define-extern append! function) +(define-extern append! (function object object object)) ;;(define-extern *trace-list* object) ;; unknown type -(define-extern min function) -(define-extern last function) +(define-extern last (function object object)) (define-extern printl function) (define-extern valid? function) ;;(define-extern nothing object) ;; unknown type -(define-extern ash function) -(define-extern lognor function) -(define-extern sort function) +(define-extern sort (function object (function object object object) object)) ;;(define-extern method-set! object) ;; unknown type ;;(define-extern *debug-segment* object) ;; unknown type (define-extern bfloat type) -(define-extern * function) -(define-extern print function) -(define-extern - function) (define-extern string type) ;;(define-extern #t object) ;; unknown type ;;(define-extern integer object) ;; unknown type @@ -170,7 +2361,7 @@ (define-extern object type) ;;(define-extern uint64 object) ;; unknown type ;;(define-extern number object) ;; unknown type -(define-extern name= function) +(define-extern name= (function basic basic symbol)) ;;(define-extern int64 object) ;; unknown type ;;(define-extern global object) ;; unknown type ;;(define-extern structure object) ;; unknown type @@ -212,7 +2403,7 @@ ;;(define-extern *pickup-dead-pool* object) ;; unknown type ;;(define-extern *kernel-packages* object) ;; unknown type ;;(define-extern *camera-master-dead-pool* object) ;; unknown type -(define-extern load-package function) +(define-extern load-package (function string kheap pair)) (define-extern set-to-run function) ;;(define-extern *master-mode* object) ;; unknown type ;;(define-extern default-pool object) ;; unknown type @@ -258,9 +2449,9 @@ ;;(define-extern *last-loado-length* object) ;; unknown type ;;(define-extern *4k-dead-pool* object) ;; unknown type ;;(define-extern *default-dead-pool* object) ;; unknown type -(define-extern kernel-dispatcher function) +(define-extern kernel-dispatcher (function (function object))) ;;(define-extern *8k-dead-pool* object) ;; unknown type -(define-extern unload-package function) +(define-extern unload-package (function string pair)) (define-extern reset-and-call function) (define-extern process-not-name function) ;;(define-extern *display-pool* object) ;; unknown type @@ -338,7 +2529,7 @@ (define-extern catn-string<-charp function) ;;(define-extern *string-tmp-str* object) ;; unknown type (define-extern string-suck-up! function) -(define-extern copy-string<-string function) +(define-extern copy-string<-string (function string string symbol)) (define-extern string-skip-to-char function) (define-extern string-strip-trailing-whitespace! function) (define-extern string>? function) diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index 9df4b4e5f..c8748a67d 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -3,14 +3,14 @@ { "game_version":1, // the order here matters. KERNEL and GAME should go first - "dgo_names":["CGO/KERNEL.CGO", "CGO/GAME.CGO" + "dgo_names":["CGO/KERNEL.CGO","CGO/GAME.CGO"], /* , "CGO/ENGINE.CGO" , "CGO/ART.CGO", "DGO/BEA.DGO", "DGO/CIT.DGO", "CGO/COMMON.CGO", "DGO/DAR.DGO", "DGO/DEM.DGO", "DGO/FIN.DGO", "DGO/INT.DGO", "DGO/JUB.DGO", "DGO/JUN.DGO", "CGO/JUNGLE.CGO", "CGO/L1.CGO", "DGO/FIC.DGO", "DGO/LAV.DGO", "DGO/MAI.DGO", "CGO/MAINCAVE.CGO", "DGO/MIS.DGO", "DGO/OGR.DGO", "CGO/RACERP.CGO", "DGO/ROB.DGO", "DGO/ROL.DGO", "DGO/SNO.DGO", "DGO/SUB.DGO", "DGO/SUN.DGO", "CGO/SUNKEN.CGO", "DGO/SWA.DGO", "DGO/TIT.DGO", "DGO/TRA.DGO", "DGO/VI1.DGO", "DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO" - ], + ],*/ "write_disassembly":true, "write_hex_near_instructions":false, @@ -31,6 +31,12 @@ // Experimental Stuff "find_basic_blocks":true, + "types_with_bad_inspect_methods":[ + "engine", + "bsp-header", + "joint-anim-matrix" + ], + "asm_functions_by_name":[ // gcommon "quad-copy!", diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index e748b3492..f6ffdea4e 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -1,6 +1,7 @@ #include "DecompilerTypeSystem.h" #include "common/goos/Reader.h" #include "common/type_system/deftype.h" +#include "third-party/spdlog/include/spdlog/spdlog.h" DecompilerTypeSystem::DecompilerTypeSystem() { ts.add_builtin_types(); @@ -75,4 +76,46 @@ std::string DecompilerTypeSystem::dump_symbol_types() { } } return result; +} + +void DecompilerTypeSystem::add_type_flags(const std::string& name, u64 flags) { + auto kv = type_flags.find(name); + if (kv != type_flags.end()) { + spdlog::warn("duplicated type flags for {}, was 0x{:x}, now 0x{:x}", name.c_str(), kv->second, + flags); + if (kv->second != flags) { + spdlog::warn("duplicated type flags that are inconsistent!"); + } + } + type_flags[name] = flags; +} + +void DecompilerTypeSystem::add_type_parent(const std::string& child, const std::string& parent) { + auto kv = type_parents.find(child); + if (kv != type_parents.end()) { + spdlog::warn("duplicated type parents for {} was {} now {}", child.c_str(), kv->second.c_str(), + parent.c_str()); + if (kv->second != parent) { + throw std::runtime_error("duplicated type parents that are inconsistent!"); + } + } + type_parents[child] = parent; +} + +std::string DecompilerTypeSystem::lookup_parent_from_inspects(const std::string& child) const { + auto kv_tp = type_parents.find(child); + if (kv_tp != type_parents.end()) { + return kv_tp->second; + } + + return "UNKNOWN"; +} + +bool DecompilerTypeSystem::lookup_flags(const std::string& type, u64* dest) const { + auto kv = type_flags.find(type); + if (kv != type_flags.end()) { + *dest = kv->second; + return true; + } + return false; } \ No newline at end of file diff --git a/decompiler/util/DecompilerTypeSystem.h b/decompiler/util/DecompilerTypeSystem.h index a38d44783..8d72d1741 100644 --- a/decompiler/util/DecompilerTypeSystem.h +++ b/decompiler/util/DecompilerTypeSystem.h @@ -11,6 +11,8 @@ class DecompilerTypeSystem { std::unordered_map symbol_types; std::unordered_set symbols; std::vector symbol_add_order; + std::unordered_map type_flags; + std::unordered_map type_parents; void add_symbol(const std::string& name) { if (symbols.find(name) == symbols.end()) { @@ -40,7 +42,12 @@ class DecompilerTypeSystem { void parse_type_defs(const std::vector& file_path); + void add_type_flags(const std::string& name, u64 flags); + void add_type_parent(const std::string& child, const std::string& parent); + std::string dump_symbol_types(); + std::string lookup_parent_from_inspects(const std::string& child) const; + bool lookup_flags(const std::string& type, u64* dest) const; }; #endif // JAK_DECOMPILERTYPESYSTEM_H diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 9003837d4..60cbf7a14 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -41,6 +41,10 @@ ) ) +(defmacro tc () + `(m "decompiler/config/all-types.gc") + ) + (defmacro e () `(:exit) ) diff --git a/goal_src/kernel/gcommon.gc b/goal_src/kernel/gcommon.gc index 5a3e90117..f1dd260f5 100644 --- a/goal_src/kernel/gcommon.gc +++ b/goal_src/kernel/gcommon.gc @@ -69,7 +69,7 @@ (/ x y) ) -(defun ash ((value integer) (shift-amount integer)) +(defun ash ((value int) (shift-amount int)) "Arithmetic shift value by shift-amount. A positive shift-amount will shift to the left and a negative will shift to the right. " @@ -97,7 +97,7 @@ ) ) -(defun mod ((a integer) (b integer)) +(defun mod ((a int) (b int)) "Compute mod. It does what you expect for positive numbers. For negative numbers, nobody knows what to expect. This is a 32-bit operation. It uses an idiv on x86 and gets the remainder." @@ -107,7 +107,7 @@ ) -(defun rem ((a integer) (b integer)) +(defun rem ((a int) (b int)) "Compute remainder (32-bit). It is identical to mod. It uses a idiv and gets the remainder" ;; The original implementation is div, mfhi @@ -134,7 +134,7 @@ ) ) -(defun min ((a integer) (b integer)) +(defun min ((a int) (b int)) "Compute minimum." ;; The original implementation was inline assembly, to take advantage of branch delay slots: @@ -147,23 +147,23 @@ (if (> a b) b a) ) -(defun max ((a integer) (b integer)) +(defun max ((a int) (b int)) "Compute maximum." (declare (inline)) (if (> a b) a b) ) -(defun logior ((a integer) (b integer)) +(defun logior ((a int) (b int)) "Compute the bitwise inclusive-or" (logior a b) ) -(defun logand ((a integer) (b integer)) +(defun logand ((a int) (b int)) "Compute the bitwise and" (logand a b) ) -(defun lognor ((a integer) (b integer)) +(defun lognor ((a int) (b int)) "Compute not or." ;; Note - MIPS has a 'nor' instruction, but x86 doesn't. ;; the GOAL x86 compiler therefore doesn't have a nor operation, @@ -172,12 +172,12 @@ (lognot (logior a b)) ) -(defun logxor ((a integer) (b integer)) +(defun logxor ((a int) (b int)) "Compute the logical exclusive-or" (logxor a b) ) -(defun lognot ((a integer)) +(defun lognot ((a int)) "Compute the bitwise not" (lognot a) ) @@ -573,6 +573,7 @@ ((length int32 :offset-assert 4) (allocated-length int32 :offset-assert 8) (data uint8 :dynamic) + (_pad uint8 4) ) (:methods (new (symbol type int) _type_ 0) ;; we will override print later on. This is optional to include ) diff --git a/goal_src/kernel/gkernel-h.gc b/goal_src/kernel/gkernel-h.gc index bca8419ea..22fcd7770 100644 --- a/goal_src/kernel/gkernel-h.gc +++ b/goal_src/kernel/gkernel-h.gc @@ -85,17 +85,17 @@ ;; this stores the current state of the kernel. (deftype kernel-context (basic) - ((prevent-from-run int32 :offset-assert 4) ;; actually a process-mask - (require-for-run int32 :offset-assert 8) ;; actually a process-mask, unused? - (allow-to-run int32 :offset-assert 12) ;; actually a process-mask, unused? - (next-pid int32 :offset-assert 16) ;; next PID to give out - (fast-stack-top pointer :offset-assert 20) ;; scratchpad stack (unused?) - (current-process basic :offset-assert 24) ;; process? - (relocating-process basic :offset-assert 28) ;; process? - (relocating-min int32 :offset-assert 32) ;; print hex - (relocating-max int32 :offset-assert 36) ;; print hex - (relocating-offset int32 :offset-assert 40) ;; ? - (low-memory-message basic :offset-assert 44) ;; boolean? + ((prevent-from-run uint32 :offset-assert 4) + (require-for-run uint32 :offset-assert 8) + (allow-to-run uint32 :offset-assert 12) + (next-pid int32 :offset-assert 16) + (fast-stack-top uint32 :offset-assert 20) + (current-process basic :offset-assert 24) + (relocating-process basic :offset-assert 28) + (relocating-min int32 :offset-assert 32) + (relocating-max int32 :offset-assert 36) + (relocating-offset int32 :offset-assert 40) + (low-memory-message basic :offset-assert 44) ) :size-assert #x30 @@ -146,6 +146,7 @@ ) (:methods + (new ((allocation symbol) (type-to-make type) (parent-process process) (name symbol) (stack-size int) (stack-top pointer)) _type_ 0) (thread-suspend ((this _type_)) none 10) (thread-resume ((to-resume _type_)) none 11) ) @@ -160,7 +161,7 @@ ;; (except GOAL is old and it looks like they called them left-child right-brother trees back then) (deftype process-tree (basic) ((name basic :offset-assert 4) - (mask int32 :offset-assert 8) + (mask uint32 :offset-assert 8) (parent pointer :offset-assert 12) (brother pointer :offset-assert 16) (child pointer :offset-assert 20) @@ -302,12 +303,12 @@ ;; A catch frame is a frame you can "throw" to, by name. ;; You can "throw" out of a function and into another function. (deftype catch-frame (stack-frame) - ((sp pointer :offset 12) ;; where to reset the stack when throwing. - (ra pointer :offset 16) ;; where to jump when throwing + ((sp int32 :offset 12) ;; where to reset the stack when throwing. + (ra int32 :offset 16) ;; where to jump when throwing ;; todo - rework for x86-64. - (freg float 6 :offset 20) ;; saved floating point registers from "catch" statement - (rreg uint128 8 :offset 48) ;; saved GPRs from "catch" statement (ugh they are 128s) + (freg float 6 :offset-assert 20) ;; saved floating point registers from "catch" statement + (rreg uint128 8 :offset-assert 48) ;; saved GPRs from "catch" statement (ugh they are 128s) ) :size-assert #xb0 :method-count-assert 9 diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index d0e4a29e1..34aa7cc81 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -31,6 +31,7 @@ class Compiler { std::vector run_test(const std::string& source_code); std::vector run_test_no_load(const std::string& source_code); void shutdown_target(); + void enable_throw_on_redefines() { m_throw_on_define_extern_redefinition = true; } private: void init_logger(); @@ -104,6 +105,7 @@ class Compiler { std::unordered_map, goos::Object> m_global_constants; std::unordered_map, LambdaVal*> m_inlineable_functions; CompilerSettings m_settings; + bool m_throw_on_define_extern_redefinition = false; MathMode get_math_mode(const TypeSpec& ts); bool is_number(const TypeSpec& ts); bool is_float(const TypeSpec& ts); diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index dd83c3c50..1d04f5f41 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -74,6 +74,10 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec "[Warning] define-extern has redefined the type of symbol %s\npreviously: %s\nnow: %s\n", symbol_string(sym).c_str(), existing_type->second.print().c_str(), new_type.print().c_str()); + + if (m_throw_on_define_extern_redefinition) { + throw_compile_error(form, "define-extern redefinition"); + } } if (new_type == m_ts.make_typespec("type")) { diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index 9567c63f9..069576f04 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -104,7 +104,7 @@ TEST_F(WithGameTests, All) { runner.run_static_test(env, testCategory, "test-delete-car.gc", {"((a . b) (e . f))\n#f\n0\n"}); runner.run_static_test(env, testCategory, "test-insert-cons.gc", {"((c . w) (a . b) (e . f))\n0\n"}); - runner.run_static_test(env, testCategory, "test-new-inline-array-class.gc", {"2820\n"}); + runner.run_static_test(env, testCategory, "test-new-inline-array-class.gc", {"2824\n"}); runner.run_static_test(env, testCategory, "test-memcpy.gc", {"13\n"}); runner.run_static_test(env, testCategory, "test-memset.gc", {"11\n"}); runner.run_static_test(env, testCategory, "test-binteger-print.gc", {"-17\n0\n"}); @@ -132,3 +132,10 @@ TEST_F(WithGameTests, All) { runner.run_static_test(env, testCategory, "test-new-static-basic.gc", get_test_pass_string("new-static-basic", 9)); } + +TEST(TypeConsistency, TypeConsistency) { + Compiler compiler; + compiler.enable_throw_on_redefines(); + compiler.run_test_no_load("test/goalc/source_templates/with_game/test-build-game.gc"); + compiler.run_test_no_load("decompiler/config/all-types.gc"); +} \ No newline at end of file