mirror of
https://github.com/open-goal/jak-project
synced 2026-07-08 22:45:00 -04:00
Start a debug tool for looking through level data (#954)
* temp * temp * level inspection tool * docs * windows fix
This commit is contained in:
@@ -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,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/graphics/dma/dma.h"
|
||||
#include "common/dma/dma.h"
|
||||
|
||||
/*!
|
||||
* @file gs.h
|
||||
@@ -22,4 +22,5 @@
|
||||
- [Register Handling](/registers.md)
|
||||
|
||||
- PC Port Documentation
|
||||
- [Graphics](/graphics.md)
|
||||
- [Graphics](/graphics.md)
|
||||
- [Drawable and TFRAG](/drawable_and_tfrag.md)
|
||||
@@ -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.
|
||||
@@ -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,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;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "common/util/Timer.h"
|
||||
#include "game/graphics/dma/dma.h"
|
||||
#include "common/dma/dma.h"
|
||||
|
||||
class FrameTimeRecorder {
|
||||
public:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)...)) {}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user