diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index 31738c1b9a..ac74081582 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -55,6 +55,7 @@ add_library( IR2/OpenGoalMapping.cpp level_extractor/BspHeader.cpp + level_extractor/extract_actors.cpp level_extractor/extract_collide_frags.cpp level_extractor/extract_common.cpp level_extractor/extract_level.cpp diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index 44f0c396d2..531fcfc2fc 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -827,7 +827,7 @@ std::string LinkedObjectFile::print_scripts() { if ((label.offset & 7) == 2) { // result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) + // "\n"; - result += pretty_print::to_string(to_form_script(seg, word_idx, already_printed)) + "\n"; + result += pretty_print::to_string(to_form_script(seg, word_idx, &already_printed)) + "\n"; } } } @@ -838,7 +838,7 @@ std::string LinkedObjectFile::print_scripts() { /*! * Is the object pointed to the empty list? */ -bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) { +bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) const { ASSERT((byte_idx % 4) == 0); auto& word = words_by_seg.at(seg).at(byte_idx / 4); return word.kind() == LinkedWord::EMPTY_PTR; @@ -849,7 +849,9 @@ bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) { * Note : this takes the address of the car of the pair. which is perhaps a bit confusing * (in GOAL, this would be (&-> obj car)) */ -goos::Object LinkedObjectFile::to_form_script(int seg, int word_idx, std::vector& seen) { +goos::Object LinkedObjectFile::to_form_script(int seg, + int word_idx, + std::vector* seen) const { // the object to currently print. to start off, create pair from the car address we've been given. int goal_print_obj = word_idx * 4 + 2; @@ -863,11 +865,13 @@ goos::Object LinkedObjectFile::to_form_script(int seg, int word_idx, std::vector // loop until we run out of things to add for (;;) { - // check the thing to print is a a pair. + // check the thing to print is a pair. if ((goal_print_obj & 7) == 2) { // first convert the car (again, with (&-> obj car)) fill.as_pair()->car = to_form_script_object(seg, goal_print_obj - 2, seen); - seen.at(goal_print_obj / 4) = true; + if (seen) { + seen->at(goal_print_obj / 4) = true; + } auto cdr_addr = goal_print_obj + 2; @@ -940,7 +944,7 @@ bool LinkedObjectFile::is_string(int seg, int byte_idx) const { */ goos::Object LinkedObjectFile::to_form_script_object(int seg, int byte_idx, - std::vector& seen) { + std::vector* seen) const { goos::Object result; switch (byte_idx & 7) { diff --git a/decompiler/ObjectFile/LinkedObjectFile.h b/decompiler/ObjectFile/LinkedObjectFile.h index 2a6e1b794a..94336888ad 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.h +++ b/decompiler/ObjectFile/LinkedObjectFile.h @@ -141,11 +141,11 @@ class LinkedObjectFile { std::unique_ptr label_db; GameVersion version; + goos::Object to_form_script(int seg, int word_idx, std::vector* seen) const; private: - goos::Object to_form_script(int seg, int word_idx, std::vector& seen); - goos::Object to_form_script_object(int seg, int byte_idx, std::vector& seen); - bool is_empty_list(int seg, int byte_idx); + goos::Object to_form_script_object(int seg, int byte_idx, std::vector* seen) const; + bool is_empty_list(int seg, int byte_idx) const; std::vector> label_per_seg_by_offset; }; diff --git a/decompiler/level_extractor/BspHeader.cpp b/decompiler/level_extractor/BspHeader.cpp index 118e8600b0..e53ec4e14f 100644 --- a/decompiler/level_extractor/BspHeader.cpp +++ b/decompiler/level_extractor/BspHeader.cpp @@ -1,6 +1,7 @@ #include "BspHeader.h" #include "common/dma/dma.h" +#include "common/goos/PrettyPrinter.h" #include "common/log/log.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" @@ -528,14 +529,6 @@ std::string TieFragment::print(const PrintSettings& /*settings*/, int indent) co return result; } -void DrawableActor::read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats, - GameVersion /*version*/) { - bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); - stats->total_actors++; -} - std::string DrawableActor::print(const PrintSettings& /*settings*/, int indent) const { std::string is(indent, ' '); std::string result; @@ -1899,10 +1892,129 @@ std::string DrawableTreeArray::print(const PrintSettings& settings, int indent) return result; } +template +void fill_res_with_value_types(Res& res_tag, const Ref& data) { + ASSERT(res_tag.inlined); + res_tag.inlined_storage = bytes_from_plain_data(data, sizeof(T) * res_tag.count); +} + +void EntityActor::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* /*stats*/, + GameVersion /*version*/) { + trans.read_from_file(get_field_ref(ref, "trans", dts)); + aid = read_plain_data_field(ref, "aid", dts); + etype = read_type_field(ref, "etype", dts, false); + task = read_plain_data_field(ref, "task", dts); + vis_id = read_plain_data_field(ref, "vis-id", dts); + quat.read_from_file(get_field_ref(ref, "quat", dts)); + + int res_length = read_plain_data_field(ref, "length", dts); + // int res_allocated_length = read_plain_data_field(ref, "allocated-length", dts); + + auto tags = deref_label(get_field_ref(ref, "tag", dts)); + auto data_base = deref_label(get_field_ref(ref, "data-base", dts)); + + for (int i = 0; i < res_length; i++) { + auto& res = res_list.emplace_back(); + res.name = read_symbol(tags); + tags.byte_offset += 4; + res.key_frame = deref_float(tags, 0); + tags.byte_offset += 4; + res.elt_type = read_type(tags); + tags.byte_offset += 4; + const u32 vals = deref_u32(tags, 0); + const u32 offset = vals & 0xffff; // 16 bits + res.count = (vals >> 16) & 0x7fff; // 15 bits + res.inlined = vals & 0x8000'0000; + + Ref data = data_base; + data.byte_offset += offset; + + if (res.elt_type == "string") { + ASSERT(!res.inlined); + for (int j = 0; j < res.count; j++) { + res.strings.push_back(read_string_ref(data)); + data.byte_offset += 4; + } + } else if (res.elt_type == "symbol") { + ASSERT(!res.inlined); + for (int j = 0; j < res.count; j++) { + res.strings.push_back(read_symbol(data)); + data.byte_offset += 4; + } + } else if (res.elt_type == "type") { + ASSERT(!res.inlined); + for (int j = 0; j < res.count; j++) { + res.strings.push_back(read_type(data)); + data.byte_offset += 4; + } + } else if (res.elt_type == "vector") { + ASSERT(res.inlined); + res.inlined_storage = bytes_from_plain_data(data, 16 * res.count); + } else if (res.elt_type == "float") { + fill_res_with_value_types(res, data); + } else if (res.elt_type == "int32") { + fill_res_with_value_types(res, data); + } else if (res.elt_type == "int16") { + fill_res_with_value_types(res, data); + } else if (res.elt_type == "int8") { + fill_res_with_value_types(res, data); + } else if (res.elt_type == "uint32") { + fill_res_with_value_types(res, data); + } else if (res.elt_type == "uint8") { + fill_res_with_value_types(res, data); + } else if (res.elt_type == "actor-group") { + // TODO: unsupported. + } else if (res.elt_type == "pair") { + ASSERT(res.count == 1); + ASSERT(!res.inlined); + data = deref_label(data); + res.script = data.data->to_form_script(data.seg, (data.byte_offset) / 4, nullptr); + } else { + fmt::print("unhandled elt_type: {}\n", res.elt_type); + ASSERT_NOT_REACHED(); + } + + tags.byte_offset += 4; + } +} + +void DrawableActor::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats, + GameVersion version) { + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + actor.read_from_file(get_and_check_ref_to_basic(ref, "actor", "entity-actor", dts), dts, stats, + version); + stats->total_actors++; +} + +void DrawableInlineArrayActor::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats, + GameVersion version) { + int num_actors = read_plain_data_field(ref, "length", dts); + fmt::print("total of {} actors\n", num_actors); + + auto data_ref = get_field_ref(ref, "data", dts); + for (int i = 0; i < num_actors; i++) { + Ref obj_ref = data_ref; + obj_ref.byte_offset += 32 * i; // todo not a constant here + auto type = get_type_of_basic(obj_ref); + if (type != "drawable-actor") { + throw Error("bad drawable-actor type: {}", type); + } + drawable_actors.emplace_back(); + drawable_actors.back().read_from_file(typed_ref_from_basic(obj_ref, dts), dts, stats, version); + } +} + void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats, - GameVersion version) { + GameVersion version, + bool only_read_texture_remap) { TypedRef ref; ref.ref.byte_offset = 0; ref.ref.seg = 0; @@ -1912,6 +2024,24 @@ void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file, file_info.read_from_file(get_and_check_ref_to_basic(ref, "info", "file-info", dts), dts); bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + texture_remap_table.clear(); + s32 tex_remap_len = read_plain_data_field(ref, "texture-remap-table-len", dts); + if (tex_remap_len > 0) { + auto tex_remap_data = deref_label(get_field_ref(ref, "texture-remap-table", dts)); + for (int entry = 0; entry < tex_remap_len; entry++) { + u64 low = deref_u32(tex_remap_data, 2 * entry); + u64 high = deref_u32(tex_remap_data, 2 * entry + 1); + TextureRemap remap; + remap.original_texid = low; + remap.new_texid = high; + texture_remap_table.push_back(remap); + } + } + + if (only_read_texture_remap) { + return; + } + switch (version) { case GameVersion::Jak1: visible_list_length = read_plain_data_field(ref, "visible-list-length", dts); @@ -1927,26 +2057,16 @@ void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file, get_and_check_ref_to_basic(ref, "drawable-trees", "drawable-tree-array", dts), dts, stats, version); - texture_remap_table.clear(); - - s32 tex_remap_len = read_plain_data_field(ref, "texture-remap-table-len", dts); - - if (tex_remap_len > 0) { - auto tex_remap_data = deref_label(get_field_ref(ref, "texture-remap-table", dts)); - for (int entry = 0; entry < tex_remap_len; entry++) { - u64 low = deref_u32(tex_remap_data, 2 * entry); - u64 high = deref_u32(tex_remap_data, 2 * entry + 1); - TextureRemap remap; - remap.original_texid = low; - remap.new_texid = high; - texture_remap_table.push_back(remap); - } - } - if (version > GameVersion::Jak1) { auto ff = get_field_ref(ref, "texture-flags", dts); memcpy_plain_data((u8*)texture_flags, ff, sizeof(u16) * kNumTextureFlags); } + + if (get_word_kind_for_field(ref, "actors", dts) == decompiler::LinkedWord::PTR) { + actors.read_from_file( + get_and_check_ref_to_basic(ref, "actors", "drawable-inline-array-actor", dts), dts, stats, + version); + } } std::string BspHeader::print(const PrintSettings& settings) const { diff --git a/decompiler/level_extractor/BspHeader.h b/decompiler/level_extractor/BspHeader.h index aecc6d191e..518edccd63 100644 --- a/decompiler/level_extractor/BspHeader.h +++ b/decompiler/level_extractor/BspHeader.h @@ -163,10 +163,38 @@ struct DrawableInlineArrayUnknown : public DrawableInlineArray { // there's a tree for actors - but we don't do anything with it yet. -struct EntityActor {}; +constexpr float kDefaultKeyFrame = -1000000000.f; + +struct Res { + std::string name; + float key_frame = -2; + std::string elt_type; + u16 count; + bool inlined = false; + + std::vector inlined_storage; + std::vector strings; + goos::Object script; +}; + +struct EntityActor { + Vector trans; + u32 aid = 0; + // nav mesh + std::string etype; + int task = 0; + u16 vis_id = 0; + Vector quat; + + std::vector res_list; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats, + GameVersion version); +}; struct DrawableActor : public Drawable { - s16 id; Vector bsphere; EntityActor actor; @@ -770,6 +798,14 @@ struct DrawableTreeInstanceShrub : public level_tools::DrawableTree { } // namespace shrub_types +struct DrawableInlineArrayActor { + std::vector drawable_actors; + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats, + GameVersion version); +}; + //////////////////////////////// // Main Level Type (bsp-header) //////////////////////////////// @@ -840,6 +876,7 @@ struct BspHeader { // (nickname symbol :offset-assert 76) // (vis-info level-vis-info 8 :offset-assert 80) // (actors drawable-inline-array-actor :offset-assert 112) + DrawableInlineArrayActor actors; // (cameras (array entity-camera) :offset-assert 116) // (nodes (inline-array bsp-node) :offset-assert 120) // @@ -860,7 +897,8 @@ struct BspHeader { void read_from_file(const decompiler::LinkedObjectFile& file, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats, - GameVersion version); + GameVersion version, + bool only_read_texture_remap = false); std::string print(const PrintSettings& settings) const; }; diff --git a/decompiler/level_extractor/extract_actors.cpp b/decompiler/level_extractor/extract_actors.cpp new file mode 100644 index 0000000000..6c36062863 --- /dev/null +++ b/decompiler/level_extractor/extract_actors.cpp @@ -0,0 +1,149 @@ +#include "extract_actors.h" + +#include "common/goos/PrettyPrinter2.h" + +#include "third-party/fmt/core.h" +#include "third-party/json.hpp" + +namespace decompiler { + +/* + * { +"trans": [-21.6238, 20.0496, 17.1191], // translation +"etype": "fuel-cell", // actor type +"game_task": 0, // associated game task (for powercells, etc) +"quat" : [0, 0, 0, 1], // quaternion +"bsphere": [-21.6238, 19.3496, 17.1191, 10], // bounding sphere +"lump": { + "name":"test-fuel-cell" +} +}, + + */ + +namespace { +nlohmann::json vectorm_json(const level_tools::Vector& v) { + nlohmann::json result; + for (int i = 0; i < 4; i++) { + result.push_back(v.data[i] / 4096.f); + } + return result; +} + +nlohmann::json vector_json(const level_tools::Vector& v) { + nlohmann::json result; + for (int i = 0; i < 4; i++) { + result.push_back(v.data[i]); + } + return result; +} + +nlohmann::json vector_json(const float* data) { + nlohmann::json result; + for (int i = 0; i < 4; i++) { + result.push_back(data[i]); + } + return result; +} + +nlohmann::json strings_json(const std::vector& data, bool prefix_quote) { + if (data.size() == 1) { + if (prefix_quote) { + return fmt::format("'{}", data[0]); + } else { + return data[0]; + } + } else { + nlohmann::json result; + for (const auto& str : data) { + if (prefix_quote) { + result.push_back(fmt::format("'{}", str)); + } else { + result.push_back(str); + } + } + return result; + } +} + +template +nlohmann::json value_json(const std::vector& data, int count) { + ASSERT(count * sizeof(T) == data.size()); + if (count == 1) { + T v; + memcpy(&v, data.data(), sizeof(T)); + return v; + } else { + nlohmann::json ret; + T v; + for (int i = 0; i < count; i++) { + memcpy(&v, data.data() + i * sizeof(T), sizeof(T)); + ret.push_back(v); + } + return ret; + } +} +} // namespace + +std::string extract_actors_to_json(const level_tools::DrawableInlineArrayActor& actors) { + nlohmann::json json; + + for (const auto& dactor : actors.drawable_actors) { + const auto& actor = dactor.actor; + auto& json_actor = json.emplace_back(); + json_actor["bsphere"] = vectorm_json(dactor.bsphere); + // drawable ID? + + json_actor["trans"] = vectorm_json(actor.trans); + json_actor["aid"] = actor.aid; // aid + // nav mesh + json_actor["etype"] = actor.etype; + json_actor["game_task"] = actor.task; + // vis ID? + json_actor["quat"] = vector_json(actor.quat); + auto& json_lump = json_actor["lump"]; + for (const auto& res : actor.res_list) { + if (res.elt_type == "string") { + json_lump[res.name] = strings_json(res.strings, false); + } else if (res.elt_type == "symbol") { + json_lump[res.name] = strings_json(res.strings, true); + } else if (res.elt_type == "type") { + // TODO: confusion with symbols + json_lump[res.name] = strings_json(res.strings, true); + } else if (res.elt_type == "vector") { + const float* data = (const float*)res.inlined_storage.data(); + if (res.count == 1) { + json_lump[res.name] = vector_json(data); + } else { + for (int i = 0; i < res.count; i++) { + json_lump[res.name].push_back(vector_json(data + 4 * i)); + } + } + } else if (res.elt_type == "pair") { + json_lump[res.name] = pretty_print::to_string(res.script); + } else if (res.elt_type == "float") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "int32") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "int16") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "int8") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "uint32") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "uint16") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "uint8") { + json_lump[res.name] = value_json(res.inlined_storage, res.count); + } else if (res.elt_type == "actor-group") { + // not supported. + } else { + ASSERT_NOT_REACHED(); + } + } + } + + return json.dump(2); +} + +} // namespace decompiler \ No newline at end of file diff --git a/decompiler/level_extractor/extract_actors.h b/decompiler/level_extractor/extract_actors.h new file mode 100644 index 0000000000..6066b5cd22 --- /dev/null +++ b/decompiler/level_extractor/extract_actors.h @@ -0,0 +1,9 @@ +#pragma once + +#include "decompiler/level_extractor/BspHeader.h" + +namespace decompiler { + +std::string extract_actors_to_json(const level_tools::DrawableInlineArrayActor& actors); + +} \ No newline at end of file diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 389d1dfd95..b0265490d4 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -10,6 +10,7 @@ #include "common/util/string_util.h" #include "decompiler/level_extractor/BspHeader.h" +#include "decompiler/level_extractor/extract_actors.h" #include "decompiler/level_extractor/extract_collide_frags.h" #include "decompiler/level_extractor/extract_merc.h" #include "decompiler/level_extractor/extract_shrub.h" @@ -147,17 +148,17 @@ std::vector extract_tex_remap(const ObjectFileDB& db, level_tools::DrawStats draw_stats; level_tools::BspHeader bsp_header; - bsp_header.read_from_file(bsp_file.linked_data, db.dts, &draw_stats, db.version()); + bsp_header.read_from_file(bsp_file.linked_data, db.dts, &draw_stats, db.version(), true); return bsp_header.texture_remap_table; } -std::vector extract_bsp_from_level(const ObjectFileDB& db, - const TextureDB& tex_db, - const std::string& dgo_name, - const DecompileHacks& hacks, - bool extract_collision, - tfrag3::Level& level_data) { +level_tools::BspHeader extract_bsp_from_level(const ObjectFileDB& db, + const TextureDB& tex_db, + const std::string& dgo_name, + const DecompileHacks& hacks, + bool extract_collision, + tfrag3::Level& level_data) { auto bsp_rec = get_bsp_file(db.obj_files_by_dgo.at(dgo_name), dgo_name); if (!bsp_rec) { lg::warn("Skipping extract for {} because the BSP file was not found", dgo_name); @@ -240,7 +241,7 @@ std::vector extract_bsp_from_level(const ObjectFileDB } level_data.level_name = level_name; - return bsp_header.texture_remap_table; + return bsp_header; } /*! @@ -326,7 +327,8 @@ void extract_from_level(const ObjectFileDB& db, const Config& config, bool dump_level, bool extract_collision, - const fs::path& output_folder) { + const fs::path& output_folder, + const fs::path& entities_folder) { if (db.obj_files_by_dgo.count(dgo_name) == 0) { lg::warn("Skipping extract for {} because the DGO was not part of the input", dgo_name); return; @@ -335,9 +337,9 @@ void extract_from_level(const ObjectFileDB& db, add_all_textures_from_level(level_data, dgo_name, tex_db); // the bsp header file data - auto tex_remap = + auto bsp_header = extract_bsp_from_level(db, tex_db, dgo_name, config.hacks, extract_collision, level_data); - extract_art_groups_from_level(db, tex_db, tex_remap, dgo_name, level_data); + extract_art_groups_from_level(db, tex_db, bsp_header.texture_remap_table, dgo_name, level_data); Serializer ser; level_data.serialize(ser); @@ -361,6 +363,8 @@ void extract_from_level(const ObjectFileDB& db, file_util::create_dir_if_needed_for_file(fore_file_path); save_level_foreground_as_gltf(level_data, fore_file_path); } + file_util::write_text_file(entities_folder / fmt::format("{}_actors.json", level_data.level_name), + extract_actors_to_json(bsp_header.actors)); } void extract_all_levels(const ObjectFileDB& db, @@ -372,11 +376,14 @@ void extract_all_levels(const ObjectFileDB& db, bool extract_collision, const fs::path& output_path) { extract_common(db, tex_db, common_name, debug_dump_level, output_path, config); + auto entities_dir = file_util::get_jak_project_dir() / "decompiler_out" / + game_version_names[config.game_version] / "entities"; + file_util::create_dir_if_needed(entities_dir); SimpleThreadGroup threads; threads.run( [&](int idx) { extract_from_level(db, tex_db, dgo_names[idx], config, debug_dump_level, extract_collision, - output_path); + output_path, entities_dir); }, dgo_names.size()); threads.join(); diff --git a/decompiler/level_extractor/extract_tfrag.cpp b/decompiler/level_extractor/extract_tfrag.cpp index 8285adf916..69ce3038f4 100644 --- a/decompiler/level_extractor/extract_tfrag.cpp +++ b/decompiler/level_extractor/extract_tfrag.cpp @@ -1018,16 +1018,16 @@ std::vector emulate_tfrag_execution(const level_tools::TFragment& fra // lq.xyzw vf04, 5(vi14) | mulw.xyzw vf16, vf00, vf00 // inputs.vf04_cam_mat_x = load_vector_data(vars.vi14 + 5); - Vector4f vf16_scaled_pos_0 = Vector4f(0, 0, 0, 1); + // Vector4f vf16_scaled_pos_0 = Vector4f(0, 0, 0, 1); // lq.xyzw vf07, 6(vi14) | mulw.xyzw vf17, vf00, vf00 // inputs.vf07_cam_mat_y = load_vector_data(vars.vi14 + 6); - Vector4f vf17_scaled_pos_1 = Vector4f(0, 0, 0, 1); + // Vector4f vf17_scaled_pos_1 = Vector4f(0, 0, 0, 1); // ibne vi00, vi14, L136 | mulw.xyzw vf18, vf00, vf00 - Vector4f vf18_scaled_pos_2 = Vector4f(0, 0, 0, 1); + // Vector4f vf18_scaled_pos_2 = Vector4f(0, 0, 0, 1); // lq.xyzw vf08, 7(vi14) | mulw.xyzw vf19, vf00, vf00 - Vector4f vf19_scaled_pos_3 = Vector4f(0, 0, 0, 1); + // Vector4f vf19_scaled_pos_3 = Vector4f(0, 0, 0, 1); // inputs.vf08_cam_mat_z = load_vector_data(vars.vi14 + 7); //////////////////////////////////////////////////////////////////////////////////////// diff --git a/decompiler/util/goal_data_reader.cpp b/decompiler/util/goal_data_reader.cpp index bdd58bacf2..639c442486 100644 --- a/decompiler/util/goal_data_reader.cpp +++ b/decompiler/util/goal_data_reader.cpp @@ -51,6 +51,49 @@ void read_plain_data_field(const TypedRef& object, } } +void memcpy_from_plain_data(u8* dest, const Ref& source, int size_bytes) { + const auto& words = source.data->words_by_seg.at(source.seg); + for (int byte = 0; byte < size_bytes; byte++) { + int byte_in_words = byte + source.byte_offset; + + int word_idx = byte_in_words / 4; + int byte_in_word = byte_in_words % 4; + + const auto& word = words.at(word_idx); + if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) { + throw Error("Error reading byte {} of (in data, byte {}). Didn't get plain data.", byte, + byte_in_words); + } + + dest[byte] = word.get_byte(byte_in_word); + } +} + +std::vector bytes_from_plain_data(const Ref& source, int size_bytes) { + std::vector ret(size_bytes); + memcpy_from_plain_data(ret.data(), source, size_bytes); + return ret; +} + +decompiler::LinkedWord::Kind get_word_kind_for_field(const TypedRef& object, + const std::string& field_name, + const decompiler::DecompilerTypeSystem& dts) { + FieldLookupInfo field_info = dts.ts.lookup_field_info(object.type->get_name(), field_name); + + if (field_info.field.is_dynamic() || field_info.field.is_array() || + field_info.field.is_inline()) { + throw Error("Field {} is dynamic/array/inline and can't be used with get_word_kind_for_field", + field_name); + } + + int byte_in_words = object.ref.byte_offset + field_info.field.offset(); + if ((byte_in_words % 4) != 0) { + throw Error("Field {} was misaligned.", field_name); + } + + return object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4).kind(); +} + TypedRef get_and_check_ref_to_basic(const TypedRef& object, const std::string& field_name, const std::string& expected_type, @@ -169,6 +212,31 @@ std::string read_type_field(const TypedRef& object, return word.symbol_name(); } +std::string read_symbol(const Ref& object) { + const auto& word = object.data->words_by_seg.at(object.seg).at(object.byte_offset / 4); + if (word.kind() != decompiler::LinkedWord::SYM_PTR) { + throw Error("read_symbol did not get a symbol (offset {} words)", object.byte_offset / 4); + } + return word.symbol_name(); +} + +std::string read_type(const Ref& object) { + const auto& word = object.data->words_by_seg.at(object.seg).at(object.byte_offset / 4); + if (word.kind() != decompiler::LinkedWord::TYPE_PTR) { + throw Error("read_type did not get a type (offset {} words)", object.byte_offset / 4); + } + return word.symbol_name(); +} + +std::string read_string_ref(const Ref& object) { + const auto& word = object.data->words_by_seg.at(object.seg).at(object.byte_offset / 4); + if (word.kind() != decompiler::LinkedWord::PTR) { + throw Error("read_string_ref did not get a pointer (offset {} words)", object.byte_offset / 4); + } + const auto& label = object.data->labels.at(word.label_id()); + return object.data->get_goal_string_by_label(label); +} + std::string read_string_field(const TypedRef& object, const std::string& field_name, const decompiler::DecompilerTypeSystem& dts, @@ -292,6 +360,19 @@ u32 deref_u32(const Ref& ref, int word_offset) { return word.data; } +float deref_float(const Ref& ref, int array_idx) { + if ((ref.byte_offset % 4) != 0) { + throw Error("deref_u32 bad alignment"); + } + const auto& word = ref.data->words_by_seg.at(ref.seg).at(array_idx + (ref.byte_offset / 4)); + if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) { + throw Error("deref_u32 bad kind: {}", (int)word.kind()); + } + float ret; + memcpy(&ret, &word.data, 4); + return ret; +} + u64 deref_u64(const Ref& ref, int dw_offset) { if ((ref.byte_offset % 8) != 0) { throw Error("deref_u64 bad alignment"); diff --git a/decompiler/util/goal_data_reader.h b/decompiler/util/goal_data_reader.h index 2d3f074184..d44e8a0c04 100644 --- a/decompiler/util/goal_data_reader.h +++ b/decompiler/util/goal_data_reader.h @@ -5,6 +5,8 @@ #include "common/common_types.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" + namespace decompiler { class DecompilerTypeSystem; class LinkedObjectFile; @@ -43,6 +45,13 @@ T read_plain_data_field(const TypedRef& object, return result; } +void memcpy_from_plain_data(u8* dest, const Ref& source, int size_bytes); +std::vector bytes_from_plain_data(const Ref& source, int size_bytes); + +decompiler::LinkedWord::Kind get_word_kind_for_field(const TypedRef& object, + const std::string& field_name, + const decompiler::DecompilerTypeSystem& dts); + TypedRef get_and_check_ref_to_basic(const TypedRef& object, const std::string& field_name, const std::string& expected_type, @@ -52,6 +61,10 @@ std::string read_symbol_field(const TypedRef& object, const std::string& field_name, const decompiler::DecompilerTypeSystem& dts); +std::string read_symbol(const Ref& object); +std::string read_type(const Ref& object); +std::string read_string_ref(const Ref& object); + std::string read_type_field(const TypedRef& object, const std::string& field_name, const decompiler::DecompilerTypeSystem& dts, @@ -75,5 +88,6 @@ u32 deref_u32(const Ref& ref, int word_offset); u16 deref_u16(const Ref& ref, int array_idx); s8 deref_s8(const Ref& ref, int byte); u8 deref_u8(const Ref& ref, int byte); +float deref_float(const Ref& ref, int array_idx); u64 deref_u64(const Ref& ref, int dw_offset); std::string inspect_ref(const Ref& ref); diff --git a/goalc/build_level/build_level.cpp b/goalc/build_level/build_level.cpp index 270411aec5..f5f7d69daa 100644 --- a/goalc/build_level/build_level.cpp +++ b/goalc/build_level/build_level.cpp @@ -206,6 +206,9 @@ bool run_build_level(const std::string& input_file, const auto& files = db.obj_files_by_dgo.at(dgo_name); auto art_groups = find_art_groups( processed_art_groups, level_json.at("art_groups").get>(), files); + if (art_groups.empty()) { + continue; + } auto tex_remap = decompiler::extract_tex_remap(db, dgo_name); for (const auto& ag : art_groups) { if (ag.name.length() > 3 && !ag.name.compare(ag.name.length() - 3, 3, "-ag")) {