Start a debug tool for looking through level data (#954)

* temp

* temp

* level inspection tool

* docs

* windows fix
This commit is contained in:
water111
2021-10-31 13:12:50 -04:00
committed by GitHub
parent 51ab0c04b6
commit 6a606d7222
28 changed files with 2053 additions and 19 deletions
+4
View File
@@ -3,6 +3,10 @@ add_library(common
audio/audio_formats.cpp
cross_os_debug/xdbg.cpp
cross_sockets/xsocket.cpp
dma/dma.cpp
dma/dma_copy.cpp
dma/gs.cpp
goos/Interpreter.cpp
goos/Object.cpp
goos/ParseHelpers.cpp
@@ -35,7 +35,7 @@ std::string VifCode::print() {
result = "ITOP";
break;
case Kind::STMOD:
result = "STMOD";
result = fmt::format("STMOD 0b{:b}", immediate);
break;
case Kind::MSK3PATH:
result = "MSK3PATH";
@@ -79,6 +79,34 @@ std::string VifCode::print() {
case Kind::DIRECTHL:
result = "DIRECTHL";
break;
case Kind::UNPACK_V4_8: {
VifCodeUnpack up(*this);
result = fmt::format("UNPACK-V4-8: {} addr: {} us: {} tops: {}", num, up.addr_qw,
up.is_unsigned, up.use_tops_flag);
break;
}
case Kind::UNPACK_V4_16: {
VifCodeUnpack up(*this);
result = fmt::format("UNPACK-V4-16: {} addr: {} us: {} tops: {}", num, up.addr_qw,
up.is_unsigned, up.use_tops_flag);
break;
}
case Kind::UNPACK_V4_32: {
VifCodeUnpack up(*this);
result = fmt::format("UNPACK-V4-32: {} addr: {} us: {} tops: {}", num, up.addr_qw,
up.is_unsigned, up.use_tops_flag);
break;
}
case Kind::UNPACK_V3_32: {
VifCodeUnpack up(*this);
result = fmt::format("UNPACK-V3-32: {} addr: {} us: {} tops: {}", num, up.addr_qw,
up.is_unsigned, up.use_tops_flag);
break;
}
default:
fmt::print("Unhandled vif code {}", (int)kind);
result = "???";
@@ -71,6 +71,9 @@ struct VifCode {
DIRECTHL = 0b1010001,
UNPACK_MASK = 0b1100000, // unpack is a bunch of commands.
UNPACK_V4_32 = 0b1101100,
UNPACK_V4_16 = 0b1101101,
UNPACK_V3_32 = 0b1101000,
UNPACK_V4_8 = 0b1101110,
};
VifCode(u32 value) {
@@ -1,7 +1,7 @@
#pragma once
#include <cstring>
#include "game/graphics/dma/dma.h"
#include "common/dma/dma.h"
#include "common/util/assert.h"
/*!
@@ -1,6 +1,6 @@
#include "common/goal_constants.h"
#include "game/graphics/dma/dma_chain_read.h"
#include "common/dma/dma_chain_read.h"
#include "dma_copy.h"
#include "third-party/fmt/core.h"
#include "common/util/Timer.h"
@@ -3,7 +3,7 @@
#include <vector>
#include "common/common_types.h"
#include "game/graphics/dma/dma_chain_read.h"
#include "common/dma/dma_chain_read.h"
#include "common/util/Serializer.h"
struct DmaData {
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "game/graphics/dma/dma.h"
#include "common/dma/dma.h"
/*!
* @file gs.h
+2 -1
View File
@@ -22,4 +22,5 @@
- [Register Handling](/registers.md)
- PC Port Documentation
- [Graphics](/graphics.md)
- [Graphics](/graphics.md)
- [Drawable and TFRAG](/drawable_and_tfrag.md)
+38
View File
@@ -0,0 +1,38 @@
## Drawable Trees
At the highest level is the level file itself, which is a `bsp-header`. It contains a `drawable-tree-array`, which contains a small number of `drawable-tree`s. (~1 to 15?).
Each of these `drawable-tree`s is a different kind, distinguished by its type. Different types of trees go to different renderers. For example, there is a `drawable-tree-lowres-tfrag`, a `drawable-tree-instance-tie`, and even a `drawable-tree-actor`. It is possible to have multiple trees of the same type, as some trees seem to have a maximum size - for example a `drawable-tree-instance-tie` can have only 43 different prototypes, so it is common to see a few of these. These trees are the thing passed to the appropriate renderers. All the trees for all the levels get put in the `background-work` structure, which is then read by the renderers.
The `drawable-tree-tfrag` contains all the "normal" tfrag data. There are other trees like `trans`, `dirt`, `ice`, `lowres`, and `lowres-trans`, which are only present if the level actually has this type of geometry. As far as I can tell, the special ones follow the same format, but I haven't checked this in detail.
The `drawable-tree-tfrag` contains a small number (1 to 6?) of `drawable-inline-array`. They all refer to the same set of `tfragment`s (the actual things to draw), but have a different tree structure.
The `drawable-inline-array` contains `draw-node`s. Each `draw-node` contains between 0 and 8 children. The children are represented by a pointer to an inline array of children. The children can be either `draw-node`s or `tfragment`s. All the children are always the same type.
The first `drawable-inline-array` in the `drawable-tree-tfrag` is the full tree structure. It starts with some number of children that is smaller than 8. And from there on, all children are `draw-node` (always 8 or fewer children) or a `tfragment` directly. So this is the deepest possible tree. I believe the max depth seen is 6?
The next `drawable-inline-array` starts with a list of all nodes at with a depth of 1 from one of the top-level nodes of the first. So this has at most 64 entries. Like the previous tree, all the children from here on have 8 or fewer children.
This pattern continues. The n-th `drawable-inline-array` is a list of all nodes at depth n in the "real" tree.
There are two tricks to this:
First, if the `drawable-inline-array` contains `draw-node`s, the type is actually `drawable-inline-array-node`. Unlike a `draw-node`, which can only contain 8 children, a `drawable-inline-array` can contain a huge number of children. The final `drawable-inline-array` is a list of a all children at the final depth, so it's always an array of `tfragment`. In this case, the type of the `drawable-inline-array` is a `drawable-inline-array-tfrag`, and it's just a giant list of all the `tfragment`s in the level.
The second trick is that the `draw-node`s and `tfragment`s are stored only once, even if they appear in multiple `drawable-inline-array`s. They used the weird "node = length + pointer to inline array of children" format and sorted nodes by depth to enable this.
## Tfrag renderers
The `tfrag-methods.gc` file has the methods of `drawable` that call the actual "draw" implementations in `tfrag.gc`. There is a near and "normal" version of the renderer. So far, it seems like trans/low-res, ice, dirt, etc don't have separate rendering code (or are part of the main `tfrag` program).
It looks possible for `tfragment`s to be drawn using the generic renderer, but so far I can't find the code where this happens.
## Tfrag data
Tfrag also uses the `adgif-shader` idea. I believe the shaders are per-`tfragment` (though some may share, or there may be tricks to avoid resending the shader if consecutive `tfragment`s use the same settings).
I don't know if the `adgif-shader` is always 5 quadwords, like for sprite. It seems possible to use the `adgif-shader` just to set up texturing, but also have some other stuff. I believe that we currently log in these shaders and link to textures, so we can probably learn something from inspecting these.
There are 4 sets of data in `tfragment`: base, common, level0, level1. Each has its own DMA transfer (consisting of a address to data, plus a length). The details of which goes where is not clear yet. I _think_ that sometimes not all 4 are valid. There are only 3 start addresses stored, and the three DMA chains may be the same, or overlap.
The DMA data itself seems to only be loading data. It uses `unpack` (V4-16, V4-32, V3-32, V4-8) and `STROW`, `STMOD`, `STCYCL` to set up fancy unpacking tricks. No other VIFcodes have been found in any level.
Additionally, there are some color palettes that use the time-of-day system.
-3
View File
@@ -80,12 +80,9 @@ set(RUNTIME_SOURCE
overlord/srpc.cpp
overlord/ssound.cpp
overlord/stream.cpp
graphics/dma/dma.cpp
graphics/gfx.cpp
graphics/display.cpp
graphics/sceGraphicsInterface.cpp
graphics/dma/dma_copy.cpp
graphics/dma/gs.cpp
graphics/opengl_renderer/BucketRenderer.cpp
graphics/opengl_renderer/debug_gui.cpp
graphics/opengl_renderer/DirectRenderer.cpp
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "game/graphics/dma/gs.h"
#include "common/dma/gs.h"
class AdgifHelper {
public:
@@ -2,7 +2,7 @@
#include <string>
#include <memory>
#include "game/graphics/dma/dma_chain_read.h"
#include "common/dma/dma_chain_read.h"
#include "game/graphics/opengl_renderer/Shader.h"
#include "game/graphics/texture/TexturePool.h"
#include "game/graphics/opengl_renderer/Profiler.h"
@@ -1,5 +1,5 @@
#include "DirectRenderer.h"
#include "game/graphics/dma/gs.h"
#include "common/dma/gs.h"
#include "common/log/log.h"
#include "third-party/fmt/core.h"
#include "game/graphics/pipelines/opengl.h"
@@ -2,7 +2,7 @@
#include <vector>
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/dma/gs.h"
#include "common/dma/gs.h"
#include "common/math/Vector.h"
#include "common/util/SmallVector.h"
#include "game/graphics/pipelines/opengl.h"
@@ -3,7 +3,7 @@
#include <array>
#include <memory>
#include "game/graphics/dma/dma_chain_read.h"
#include "common/dma/dma_chain_read.h"
#include "game/graphics/opengl_renderer/Shader.h"
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/Profiler.h"
@@ -2,7 +2,7 @@
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/DirectRenderer.h"
#include "game/graphics/dma/gs.h"
#include "common/dma/gs.h"
#include "common/math/Vector.h"
using math::Matrix4f;
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "common/util/Timer.h"
#include "game/graphics/dma/dma.h"
#include "common/dma/dma.h"
class FrameTimeRecorder {
public:
+1 -1
View File
@@ -17,7 +17,7 @@
#include "game/graphics/display.h"
#include "game/graphics/opengl_renderer/OpenGLRenderer.h"
#include "game/graphics/texture/TexturePool.h"
#include "game/graphics/dma/dma_copy.h"
#include "common/dma/dma_copy.h"
#include "game/system/newpad.h"
#include "common/log/log.h"
#include "common/goal_constants.h"
+2 -2
View File
@@ -32,8 +32,8 @@
#include "common/util/Timer.h"
#include "game/graphics/sceGraphicsInterface.h"
#include "game/graphics/gfx.h"
#include "game/graphics/dma/dma_chain_read.h"
#include "game/graphics/dma/dma_copy.h"
#include "common/dma/dma_chain_read.h"
#include "common/dma/dma_copy.h"
#include "game/mips2c/mips2c_table.h"
#include "game/system/vm/vm.h"
#include "game/system/newpad.h"
+2
View File
@@ -1,3 +1,5 @@
add_subdirectory(level_tools)
add_executable(dgo_unpacker
dgo_unpacker.cpp)
target_link_libraries(dgo_unpacker common)
File diff suppressed because it is too large Load Diff
+412
View File
@@ -0,0 +1,412 @@
#pragma once
#include "common/common_types.h"
#include <string>
#include <memory>
#include <vector>
#include "tools/level_tools/goal_data_reader.h"
namespace decompiler {
class LinkedObjectFile;
class DecompilerTypeSystem;
} // namespace decompiler
namespace level_tools {
struct Vector {
float data[4];
void read_from_file(Ref ref);
std::string print(int indent = 0) const;
std::string print_meters(int indent = 0) const;
};
struct FileInfo {
std::string file_type;
std::string file_name;
u32 major_version;
u32 minor_version;
std::string maya_file_name;
std::string tool_debug;
std::string mdb_file_name;
void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts);
std::string print(int indent = 0) const;
};
struct PrintSettings {
bool print_tfrag = true;
bool expand_draw_node = true;
bool expand_drawable_tree_tfrag = true;
bool expand_drawable_tree_trans_tfrag = false;
bool expand_drawable_tree_tie_proto = false;
bool expand_drawable_tree_tie_proto_data = false;
bool expand_drawable_tree_instance_tie = false;
bool expand_drawable_tree_actor = false;
};
struct DrawStats {
int total_tfrag_tris = 0;
int total_tie_prototype_tris = 0;
int total_actors = 0;
int total_tie_instances = 0;
int total_tfragments = 0;
bool debug_print_dma_data = false;
std::string print() const;
};
struct Drawable {
virtual void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) = 0;
virtual std::string print(const PrintSettings& settings, int indent) const = 0;
virtual std::string my_type() const = 0;
virtual ~Drawable() = default;
};
struct DrawableInlineArray : public Drawable {};
struct DrawNode : public Drawable {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
s16 id;
Vector bsphere;
u8 child_count = 0;
u8 flags = 0;
std::vector<std::unique_ptr<Drawable>> children;
float distance = 0;
};
struct EntityActor {};
struct DrawableActor : public Drawable {
s16 id;
Vector bsphere;
EntityActor actor;
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override { return "drawable-actor"; }
};
struct TFragmentDebugData {
u16 num_tris[4];
u16 num_dverts[4];
bool has_debug_lines;
std::string print(int indent) const;
void read_from_file(Ref ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
};
struct TFragment : public Drawable {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override { return "tfragment"; }
// 0 - 4 type tag
s16 id; // 4 - 6
u16 color_index; // 6 - 8
TFragmentDebugData debug_data;
// u32 color_indices; // 12 - 16 (or colors?)
Vector bsphere; // 16 - 32
// dma common/level0 // 32 - 36
// dma base // 36 - 40
// dma level 1 // 40 - 44
u8 dma_qwc[4];
// shader // 48 - 52
u8 num_shaders; // 52
u8 num_base_colors; // 53
u8 num_level0_colors; // 54
u8 num_level1_colors; // 55
u8 color_offset; // 56
u8 color_count; // 57
// u8 pad0; // 58
// u8 pad1; // 59
// generic // 60 - 64
};
struct TieFragment : public Drawable {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override { return "tie-fragment"; }
Vector bsphere;
u16 num_tris;
u16 num_dverts;
// todo, lots more
};
struct InstanceTie : public Drawable {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override { return "instance-tie"; }
// (bucket-index uint16 :offset 6)
u16 bucket_index;
Vector bsphere;
// todo, lots more
};
struct DrawableInlineArrayNode : public DrawableInlineArray {
s16 id;
s16 length;
Vector bsphere;
std::vector<DrawNode> draw_nodes;
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
};
struct DrawableInlineArrayTFrag : public DrawableInlineArray {
s16 id;
s16 length;
Vector bsphere;
std::vector<TFragment> tfragments;
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
};
struct DrawableInlineArrayTie : public DrawableInlineArray {
s16 id;
s16 length;
Vector bsphere;
std::vector<InstanceTie> instances;
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
};
struct DrawableInlineArrayTransTFrag : public DrawableInlineArrayTFrag {
std::string my_type() const override { return "drawable-inline-array-trans-tfrag"; }
};
struct DrawableInlineArrayUnknown : public DrawableInlineArray {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
std::string type_name;
};
struct DrawableTree : public Drawable {};
struct DrawableTreeTfrag : public DrawableTree {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
s16 id;
s16 length;
// todo time of day stuff
Vector bsphere;
std::vector<std::unique_ptr<DrawableInlineArray>> arrays;
};
struct DrawableTreeActor : public DrawableTree {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
s16 id;
s16 length;
// todo time of day stuff
Vector bsphere;
std::vector<std::unique_ptr<DrawableInlineArray>> arrays;
};
struct PrototypeTie : public DrawableInlineArray {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
s16 id;
s16 length;
Vector bsphere;
std::vector<TieFragment> tie_fragments;
};
struct PrototypeBucketTie {
std::string name; // 4 - 8
u32 flags; // 8 - 12
u16 in_level; // 12 - 14
u16 utextures; // 14 - 16
PrototypeTie geometry[4];
Vector dists;
Vector rdists;
u32 next[4];
u16 count[4];
u16 generic_count[4];
u32 generic_next[4];
u8 frag_count[4];
u8 index_start[4];
u16 base_qw[4];
float envmap_rfade;
float envmap_fade_far;
// todo envmap shader
// todo collide-frag
// todo tie-colors
// todo data
void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
std::string print(const PrintSettings& settings, int indent) const;
};
struct PrototypeArrayTie {
u32 length;
u32 allocated_length;
std::string content_type;
std::vector<PrototypeBucketTie> data;
void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
std::string print(const PrintSettings& settings, int indent) const;
};
struct ProxyPrototypeArrayTie {
void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
std::string print(const PrintSettings& settings, int indent) const;
PrototypeArrayTie prototype_array_tie;
// todo wind vectors.
};
struct DrawableTreeInstanceTie : public DrawableTree {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
s16 id;
s16 length;
ProxyPrototypeArrayTie prototypes;
Vector bsphere;
std::vector<std::unique_ptr<DrawableInlineArray>> arrays;
};
struct DrawableTreeTransTfrag : public DrawableTreeTfrag {
std::string my_type() const override { return "drawable-tree-trans-tfrag"; }
};
struct DrawableTreeUnknown : public DrawableTree {
void read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) override;
std::string print(const PrintSettings& settings, int indent) const override;
std::string my_type() const override;
std::string type_name;
};
struct DrawableTreeArray {
s16 id;
s16 length;
void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
std::string print(const PrintSettings& settings, int indent) const;
std::vector<std::unique_ptr<DrawableTree>> trees;
};
struct BspHeader {
// (info file-info :offset 4)
FileInfo file_info;
// (bsphere vector :inline :offset-assert 16)
Vector bsphere;
// (all-visible-list (pointer uint16) :offset-assert 32)
// (visible-list-length int32 :offset-assert 36)
s32 visible_list_length = -1;
// (drawable-trees drawable-tree-array :offset-assert 40)
DrawableTreeArray drawable_tree_array;
// (pat pointer :offset-assert 44)
// (pat-length int32 :offset-assert 48)
//
// ;; some osrt of texture remapping info
// (texture-remap-table (pointer uint64) :offset-assert 52)
// (texture-remap-table-len int32 :offset-assert 56)
//
// (texture-ids (pointer texture-id) :offset-assert 60)
// (texture-page-count int32 :offset-assert 64)
//
// (unk-zero-0 basic :offset-assert 68)
//
// (name symbol :offset-assert 72)
// (nickname symbol :offset-assert 76)
// (vis-info level-vis-info 8 :offset-assert 80)
// (actors drawable-inline-array-actor :offset-assert 112)
// (cameras (array entity-camera) :offset-assert 116)
// (nodes (inline-array bsp-node) :offset-assert 120)
//
// (level level :offset-assert 124)
// (current-leaf-idx uint16 :offset-assert 128)
// (unk-data-2 uint16 9 :offset-assert 130)
//
// (boxes box8s-array :offset-assert 148)
// (current-bsp-back-flags uint32 :offset-assert 152)
// (ambients drawable-inline-array-ambient :offset-assert 156)
// (unk-data-4 float :offset-assert 160)
// (unk-data-5 float :offset-assert 164)
// (adgifs adgif-shader-array :offset-assert 168)
// (actor-birth-order (pointer uint32) :offset-assert 172)
// (split-box-indices (pointer uint16) :offset-assert 176)
// (unk-data-8 uint32 55 :offset-assert 180)
void read_from_file(const decompiler::LinkedObjectFile& file,
const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
std::string print(const PrintSettings& settings) const;
};
} // namespace level_tools
+7
View File
@@ -0,0 +1,7 @@
add_library(level_tools goal_data_reader.cpp BspHeader.cpp)
target_link_libraries(level_tools fmt common decomp)
add_executable(level_dump level_dump/main.cpp)
target_link_libraries(level_dump fmt common decomp level_tools)
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <stdexcept>
#include "third-party/fmt/core.h"
class Error : public std::runtime_error {
public:
template <typename... Args>
Error(const std::string& format, Args&&... args)
: std::runtime_error(fmt::format(format, std::forward<Args>(args)...)) {}
};
+281
View File
@@ -0,0 +1,281 @@
#include "goal_data_reader.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "tools/level_tools/Error.h"
void read_plain_data_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts,
int size_bytes,
u8* out) {
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 read_plain_data_field",
field_name);
}
auto field_type = dts.ts.lookup_type(field_info.type);
if (field_type->is_reference()) {
throw Error("Field {} is a reference and can't be used with read_plain_data_field", field_name);
}
if (field_type->is_boxed()) {
throw Error("Field {} is boxed and can't be used with read_plain_data_field", field_name);
}
int field_size_bytes = field_type->get_size_in_memory();
if (field_size_bytes != size_bytes) {
throw Error(
"Field {} size mismatch. Type system thinks it is {} bytes, but tried to load as {} bytes.",
field_name, field_size_bytes, size_bytes);
}
const auto& words = object.ref.data->words_by_seg.at(object.ref.seg);
for (int byte = 0; byte < size_bytes; byte++) {
int byte_in_words = byte + object.ref.byte_offset + field_info.field.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 field {} (in data, byte {}). Didn't get plain data.",
byte, field_name, byte_in_words);
}
out[byte] = word.get_byte(byte_in_word);
}
}
TypedRef get_and_check_ref_to_basic(const TypedRef& object,
const std::string& field_name,
const std::string& expected_type,
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_and_check_ref_to_basic",
field_name);
}
auto field_type = dts.ts.lookup_type(field_info.type);
auto basic_type = dynamic_cast<BasicType*>(field_type);
if (!basic_type) {
throw Error("Field {} is not a basic and can't be used with read_plain_data_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);
}
const auto& word = object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::PTR) {
throw Error("get_and_check_ref_to_basic did not get a label");
}
const auto& label = object.ref.data->labels.at(word.label_id);
Ref ref;
ref.data = object.ref.data;
ref.seg = label.target_segment;
ref.byte_offset = label.offset - 4; // basic offset.
if ((ref.byte_offset % 4) != 0) {
throw Error("get_and_check_ref_to_basic had a misaligned basic");
}
const auto& type_tag = object.ref.data->words_by_seg.at(ref.seg).at(ref.byte_offset / 4);
if (type_tag.kind != decompiler::LinkedWord::TYPE_PTR) {
throw Error("get_and_check_ref_to_basic did not find a type tag");
}
if (type_tag.symbol_name != expected_type) {
throw Error("get_and_check_ref_to_basic found type {} for field {}, expected {}",
type_tag.symbol_name, field_name, expected_type);
}
TypedRef tr;
tr.ref = ref;
tr.type = dts.ts.lookup_type(type_tag.symbol_name);
return tr;
}
std::string read_symbol_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 read_symbol_field",
field_name);
}
if (field_info.type != TypeSpec("symbol")) {
throw Error("Field {} has type {} and can't be used with read_symbol_field", field_name,
field_info.type.print());
}
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);
}
const auto& word = object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::SYM_PTR) {
throw Error("read_symbol_field did not get a symbol (offset {} words)", byte_in_words / 4);
}
return word.symbol_name;
}
std::string read_type_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts,
bool ignore_field_type) {
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 read_type_field",
field_name);
}
if (!ignore_field_type && field_info.type != TypeSpec("type")) {
throw Error("Field {} has type {} and can't be used with read_type_field", field_name,
field_info.type.print());
}
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);
}
const auto& word = object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::TYPE_PTR) {
throw Error("read_type_field did not get a symbol (offset {} words)", byte_in_words / 4);
}
return word.symbol_name;
}
std::string read_string_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts,
bool ignore_field_type) {
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 read_string_field",
field_name);
}
if (!ignore_field_type && field_info.type != TypeSpec("string")) {
throw Error("Field {} has type {} and can't be used with read_string_field", field_name,
field_info.type.print());
}
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);
}
const auto& word = object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::PTR) {
throw Error("read_string_field did not get a label (offset {} words)", byte_in_words / 4);
}
const auto& label = object.ref.data->labels.at(word.label_id);
return object.ref.data->get_goal_string_by_label(label);
}
Ref get_field_ref(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);
Ref result = object.ref;
result.byte_offset += field_info.field.offset();
return result;
}
std::string get_type_of_basic(const Ref& object) {
int byte_in_words = object.byte_offset;
if ((byte_in_words % 4) != 0) {
throw Error("Basic in get_type_of_basic was misaligned.");
}
const auto& word = object.data->words_by_seg.at(object.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::TYPE_PTR) {
throw Error("get_type_of_basic did not get a type tag (offset {} words)", byte_in_words / 4);
}
return word.symbol_name;
}
TypedRef typed_ref_from_basic(const Ref& object, const decompiler::DecompilerTypeSystem& dts) {
int byte_in_words = object.byte_offset;
if ((byte_in_words % 4) != 0) {
throw Error("Basic in typed_ref_from_basic was misaligned.");
}
const auto& word = object.data->words_by_seg.at(object.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::TYPE_PTR) {
throw Error("typed_ref_from_basic did not get a type tag (offset {} words)", byte_in_words / 4);
}
TypedRef result;
result.ref = object;
result.type = dts.ts.lookup_type(word.symbol_name);
return result;
}
Ref deref_label(const Ref& object) {
int byte_in_words = object.byte_offset;
if ((byte_in_words % 4) != 0) {
throw Error("ptr in deref_label was misaligned.");
}
const auto& word = object.data->words_by_seg.at(object.seg).at(byte_in_words / 4);
if (word.kind != decompiler::LinkedWord::PTR) {
throw Error("deref_label did not get a label (offset {} words)", byte_in_words / 4);
}
const auto& lab = object.data->labels.at(word.label_id);
Ref result;
result.byte_offset = lab.offset;
result.seg = lab.target_segment;
result.data = object.data;
return result;
}
std::string inspect_ref(const Ref& ref) {
auto& word = ref.data->words_by_seg.at(ref.seg).at(ref.byte_offset / 4);
switch (word.kind) {
case decompiler::LinkedWord::PLAIN_DATA:
return fmt::format("[0x{:08x} (offset {} bytes)]", word.data, ref.byte_offset % 4);
case decompiler::LinkedWord::PTR:
return fmt::format("[{}]", ref.data->labels.at(word.label_id).name);
case decompiler::LinkedWord::SYM_PTR:
return fmt::format("['{}]", word.symbol_name);
case decompiler::LinkedWord::TYPE_PTR:
return fmt::format("[t'{}]", word.symbol_name);
default:
assert(false);
}
}
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include <string>
#include <cstring>
#include "common/common_types.h"
namespace decompiler {
class DecompilerTypeSystem;
class LinkedObjectFile;
} // namespace decompiler
class Type;
struct Ref {
const decompiler::LinkedObjectFile* data = nullptr;
int seg = -1;
int byte_offset = 1;
};
struct TypedRef {
Ref ref;
Type* type = nullptr;
};
void read_plain_data_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts,
int size_bytes,
u8* out);
template <typename T>
T read_plain_data_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts) {
u8 data[sizeof(T)];
read_plain_data_field(object, field_name, dts, sizeof(T), data);
T result;
memcpy(&result, data, sizeof(T));
return result;
}
TypedRef get_and_check_ref_to_basic(const TypedRef& object,
const std::string& field_name,
const std::string& expected_type,
const decompiler::DecompilerTypeSystem& dts);
std::string read_symbol_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts);
std::string read_type_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts,
bool ignore_field_type);
std::string read_string_field(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts,
bool ignore_field_type);
Ref get_field_ref(const TypedRef& object,
const std::string& field_name,
const decompiler::DecompilerTypeSystem& dts);
std::string get_type_of_basic(const Ref& object);
TypedRef typed_ref_from_basic(const Ref& object, const decompiler::DecompilerTypeSystem& dts);
Ref deref_label(const Ref& object);
std::string inspect_ref(const Ref& ref);
+93
View File
@@ -0,0 +1,93 @@
#include "third-party/fmt/core.h"
#include "common/util/FileUtil.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/ObjectFile/LinkedObjectFileCreation.h"
#include "common/util/DgoReader.h"
#include "tools/level_tools/goal_data_reader.h"
#include "tools/level_tools/BspHeader.h"
#include "common/util/assert.h"
/*!
* Get the level data from a DGO File.
* Will ignore all the other things in the level DGO and just return the bsp file.
*/
decompiler::LinkedObjectFile load_bsp_from_dgo(const std::string& file_name,
decompiler::DecompilerTypeSystem& dts) {
std::string short_name = file_util::base_name(file_name);
fmt::print("Loading DGO file: {}\n", short_name);
auto dgo_file_data = file_util::read_binary_file(file_name);
auto dgo = DgoReader(short_name, dgo_file_data);
auto entries = dgo.entries();
assert(entries.size() > 0);
const auto& level_file = entries.back();
fmt::print("Using level file: {}, size {} kB\n", level_file.internal_name,
level_file.data.size() / 1024);
return decompiler::to_linked_object_file(level_file.data, level_file.internal_name, dts, 1);
}
bool is_valid_bsp(const decompiler::LinkedObjectFile& file) {
if (file.segments != 1) {
fmt::print("Got {} segments, but expected 1\n", file.segments);
return false;
}
auto& first_word = file.words_by_seg.at(0).at(0);
if (first_word.kind != decompiler::LinkedWord::TYPE_PTR) {
fmt::print("Expected the first word to be a type pointer, but it wasn't.\n");
return false;
}
if (first_word.symbol_name != "bsp-header") {
fmt::print("Expected to get a bsp-header, but got {} instead.\n", first_word.symbol_name);
return false;
}
return true;
}
int main(int argc, char** argv) {
try {
fmt::print("Level Dump Tool\n");
if (argc != 2) {
fmt::print("Usage: level_dump <path-to-dgo>\n");
return 1;
}
fmt::print("Setting up types...\n");
decompiler::DecompilerTypeSystem dts;
dts.parse_type_defs({"decompiler", "config", "all-types.gc"});
std::string file_name = argv[1];
fmt::print("Loading data...\n");
auto data = load_bsp_from_dgo(file_name, dts);
data.set_ordered_label_names();
if (!is_valid_bsp(data)) {
fmt::print("Invalid level file.\n");
return 1;
}
level_tools::DrawStats draw_stats;
// draw_stats.debug_print_dma_data = true;
level_tools::BspHeader bsp_header;
bsp_header.read_from_file(data, dts, &draw_stats);
level_tools::PrintSettings settings;
fmt::print("{}\n", bsp_header.print(settings));
fmt::print("Stats:\n{}\n", draw_stats.print());
} catch (const std::exception& e) {
fmt::print("Error: {}\n", e.what());
}
return 0;
}