mirror of
https://github.com/open-goal/jak-project
synced 2026-07-11 23:30:16 -04:00
[graphics] tfrag3 renderer (#978)
* begin work * work * working objs * exporting * it works * before some time of day fixes * add time of day interp and also fix zbuffer * some small blending fixes * improve randomess * clean up extraction and missing blend mode * culling, time of day, more level fixes * more cleanup * cleanup memory usage * windows fix
This commit is contained in:
@@ -3,6 +3,7 @@ add_library(common
|
||||
audio/audio_formats.cpp
|
||||
cross_os_debug/xdbg.cpp
|
||||
cross_sockets/xsocket.cpp
|
||||
custom_data/TFrag3Data.cpp
|
||||
dma/dma.cpp
|
||||
dma/dma_copy.cpp
|
||||
dma/gs.cpp
|
||||
@@ -32,6 +33,7 @@ add_library(common
|
||||
util/FileUtil.cpp
|
||||
util/json_util.cpp
|
||||
util/Timer.cpp
|
||||
util/os.cpp
|
||||
util/print_float.cpp
|
||||
util/FontUtils.cpp
|
||||
util/image_loading.cpp)
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "Tfrag3Data.h"
|
||||
#include "common/util/assert.h"
|
||||
|
||||
namespace tfrag3 {
|
||||
|
||||
void Draw::serialize(Serializer& ser) {
|
||||
ser.from_ptr(&mode);
|
||||
ser.from_ptr(&tree_tex_id);
|
||||
ser.from_pod_vector(&vertex_index_stream);
|
||||
ser.from_pod_vector(&vis_groups);
|
||||
ser.from_ptr(&num_triangles);
|
||||
}
|
||||
|
||||
void Tree::serialize(Serializer& ser) {
|
||||
ser.from_ptr(&kind);
|
||||
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(draws.size());
|
||||
} else {
|
||||
draws.resize(ser.load<size_t>());
|
||||
}
|
||||
for (auto& draw : draws) {
|
||||
draw.serialize(ser);
|
||||
}
|
||||
|
||||
ser.from_pod_vector(&vertices);
|
||||
ser.from_pod_vector(&color_indices_per_vertex);
|
||||
ser.from_pod_vector(&vis_nodes);
|
||||
ser.from_pod_vector(&colors);
|
||||
ser.from_ptr(&first_leaf_node);
|
||||
ser.from_ptr(&last_leaf_node);
|
||||
ser.from_ptr(&first_root);
|
||||
ser.from_ptr(&num_roots);
|
||||
ser.from_ptr(&only_children);
|
||||
}
|
||||
|
||||
void Texture::serialize(Serializer& ser) {
|
||||
ser.from_ptr(&w);
|
||||
ser.from_ptr(&h);
|
||||
ser.from_pod_vector(&data);
|
||||
ser.from_str(&debug_name);
|
||||
ser.from_str(&debug_tpage_name);
|
||||
}
|
||||
|
||||
void Level::serialize(Serializer& ser) {
|
||||
ser.from_ptr(&version);
|
||||
if (ser.is_loading() && version != TFRAG3_VERSION) {
|
||||
fmt::print("version mismatch when loading tfrag3 data. Got {}, expected {}\n", version,
|
||||
TFRAG3_VERSION);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
ser.from_str(&level_name);
|
||||
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(textures.size());
|
||||
} else {
|
||||
textures.resize(ser.load<size_t>());
|
||||
}
|
||||
for (auto& tex : textures) {
|
||||
tex.serialize(ser);
|
||||
}
|
||||
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(trees.size());
|
||||
} else {
|
||||
trees.resize(ser.load<size_t>());
|
||||
}
|
||||
for (auto& tree : trees) {
|
||||
tree.serialize(ser);
|
||||
}
|
||||
|
||||
ser.from_ptr(&version2);
|
||||
if (ser.is_loading() && version2 != TFRAG3_VERSION) {
|
||||
fmt::print("version mismatch when loading tfrag3 data (at end). Got {}, expected {}\n",
|
||||
version2, TFRAG3_VERSION);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tfrag3
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
// Data format for the tfrag3 renderer.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/assert.h"
|
||||
#include "common/dma/gs.h"
|
||||
#include "common/util/Serializer.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
namespace tfrag3 {
|
||||
|
||||
constexpr int TFRAG3_VERSION = 6;
|
||||
|
||||
// These vertices should be uploaded to the GPU at load time and don't change
|
||||
struct PreloadedVertex {
|
||||
// the vertex position
|
||||
float x, y, z;
|
||||
// texture coordinates
|
||||
float s, t, q;
|
||||
// currently unused, color table indices.
|
||||
u16 color_index;
|
||||
u16 pad[3];
|
||||
};
|
||||
static_assert(sizeof(PreloadedVertex) == 32, "PreloadedVertex size");
|
||||
|
||||
// Settings for an OpenGL draw
|
||||
struct Draw {
|
||||
DrawMode mode; // the OpenGL draw settings.
|
||||
u32 tree_tex_id = 0; // the texture that should be bound for the draw
|
||||
|
||||
// the list of vertices in the draw.
|
||||
std::vector<u32> vertex_index_stream;
|
||||
|
||||
// to do culling, the above vertex stream is grouped.
|
||||
// by following the visgroups and checking the visibility of the tfrag_idx, you can leave out
|
||||
// invisible vertices.
|
||||
struct VisGroup {
|
||||
u32 num = 0;
|
||||
u32 tfrag_idx = 0;
|
||||
};
|
||||
std::vector<VisGroup> vis_groups;
|
||||
u32 num_triangles = 0;
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
struct VisNode {
|
||||
math::Vector<float, 4> bsphere; // the bounding sphere, in meters (4096 = 1 game meter). w = rad
|
||||
u16 child_id = 0xffff; // the ID of our first child.
|
||||
u8 num_kids = 0xff; // number of children. The children are consecutive in memory
|
||||
u8 flags = 0; // flags. If 1, we have a DrawVisNode child, otherwise a Tfrag.
|
||||
};
|
||||
|
||||
enum class TFragmentTreeKind { NORMAL, TRANS, DIRT, ICE, LOWRES, LOWRES_TRANS, INVALID };
|
||||
|
||||
constexpr const char* tfrag_tree_names[] = {"normal", "trans", "dirt", "ice",
|
||||
"lowres", "lowres-trans", "invalid"};
|
||||
|
||||
struct TimeOfDayColor {
|
||||
math::Vector<u8, 4> rgba[8];
|
||||
};
|
||||
|
||||
struct Tree {
|
||||
TFragmentTreeKind kind;
|
||||
std::vector<Draw> draws;
|
||||
std::vector<u16> color_indices_per_vertex;
|
||||
std::vector<VisNode> vis_nodes;
|
||||
std::vector<PreloadedVertex> vertices;
|
||||
std::vector<TimeOfDayColor> colors;
|
||||
u16 first_leaf_node = 0;
|
||||
u16 last_leaf_node = 0;
|
||||
u16 first_root = 0;
|
||||
u16 num_roots = 0;
|
||||
bool only_children = false;
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
u16 w, h;
|
||||
u32 combo_id = 0;
|
||||
std::vector<u32> data;
|
||||
std::string debug_name;
|
||||
std::string debug_tpage_name;
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
struct Level {
|
||||
u16 version = TFRAG3_VERSION;
|
||||
std::string level_name;
|
||||
std::vector<Texture> textures;
|
||||
std::vector<Tree> trees;
|
||||
u16 version2 = TFRAG3_VERSION;
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
} // namespace tfrag3
|
||||
@@ -356,4 +356,79 @@ std::string GsTex0::print() const {
|
||||
|
||||
std::string GsPrim::print() const {
|
||||
return fmt::format("0x{:x}, kind {}\n", data, kind());
|
||||
}
|
||||
|
||||
std::string DrawMode::to_string() const {
|
||||
std::string result;
|
||||
result += fmt::format(" depth-write: {}\n", get_depth_write_enable());
|
||||
result += fmt::format(" depth-test: ");
|
||||
switch (get_depth_test()) {
|
||||
case GsTest::ZTest::NEVER:
|
||||
result += "never\n";
|
||||
break;
|
||||
case GsTest::ZTest::GEQUAL:
|
||||
result += "gequal\n";
|
||||
break;
|
||||
case GsTest::ZTest::ALWAYS:
|
||||
result += "always\n";
|
||||
break;
|
||||
case GsTest::ZTest::GREATER:
|
||||
result += "greater\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
result += fmt::format(" alpha: ");
|
||||
switch (get_alpha_blend()) {
|
||||
case AlphaBlend::SRC_0_SRC_DST:
|
||||
result += "src, 0, src, dst\n";
|
||||
break;
|
||||
case AlphaBlend::SRC_DST_SRC_DST:
|
||||
result += "src, dst, src, dst\n";
|
||||
break;
|
||||
case AlphaBlend::DISABLED:
|
||||
result += "disabled\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
result += fmt::format(" clamp: s {} t {}\n", get_clamp_s_enable(), get_clamp_t_enable());
|
||||
result += fmt::format(" filt: {}\n", get_filt_enable());
|
||||
result += fmt::format(" tcc: {}\n", get_tcc_enable());
|
||||
result += fmt::format(" aref: {}\n", get_aref());
|
||||
result += fmt::format(" ate: {}\n", get_at_enable());
|
||||
result += fmt::format(" atst: ");
|
||||
switch (get_alpha_test()) {
|
||||
case AlphaTest::ALWAYS:
|
||||
result += "always\n";
|
||||
break;
|
||||
case AlphaTest::GEQUAL:
|
||||
result += "gequal\n";
|
||||
break;
|
||||
case AlphaTest::NEVER:
|
||||
result += "never\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
result += fmt::format(" zte: {}\n", get_zt_enable());
|
||||
result += fmt::format(" abe: {}\n", get_ab_enable());
|
||||
result += fmt::format(" afail: ");
|
||||
switch (get_alpha_fail()) {
|
||||
case GsTest::AlphaFail::KEEP:
|
||||
result += "keep\n";
|
||||
break;
|
||||
case GsTest::AlphaFail::FB_ONLY:
|
||||
result += "fb-only\n";
|
||||
break;
|
||||
case GsTest::AlphaFail::RGB_ONLY:
|
||||
result += "rgb-only\n";
|
||||
break;
|
||||
case GsTest::AlphaFail::ZB_ONLY:
|
||||
result += "zb-only\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
+137
-1
@@ -343,4 +343,140 @@ struct AdGifData {
|
||||
u64 clamp_addr;
|
||||
u64 alpha_data;
|
||||
u64 alpha_addr;
|
||||
};
|
||||
};
|
||||
|
||||
// this represents all of the drawing state, stored as an integer.
|
||||
// it can also represent "invalid".
|
||||
class DrawMode {
|
||||
public:
|
||||
enum class AlphaBlend { DISABLED = 0, SRC_DST_SRC_DST = 1, SRC_0_SRC_DST = 2, SRC_0_FIX_DST = 3 };
|
||||
|
||||
enum class AlphaTest {
|
||||
NEVER = 0,
|
||||
ALWAYS = 1,
|
||||
GEQUAL = 2,
|
||||
};
|
||||
|
||||
bool get_depth_write_enable() const { return m_val & 0b1; }
|
||||
void enable_depth_write() { m_val = m_val | 0b1; }
|
||||
void disable_depth_write() { m_val = m_val & ~(0b1); }
|
||||
|
||||
GsTest::ZTest get_depth_test() const { return (GsTest::ZTest)((m_val >> 1) & 0b11); }
|
||||
void set_depth_test(GsTest::ZTest dt) { m_val = (m_val & ~(0b110)) | ((u32)(dt) << 1); }
|
||||
|
||||
AlphaBlend get_alpha_blend() const { return (AlphaBlend)((m_val >> 3) & 0b11); }
|
||||
void set_alpha_blend(AlphaBlend ab) { m_val = (m_val & ~(0b11000)) | ((u32)(ab) << 3); }
|
||||
|
||||
u8 get_aref() const { return m_val >> 8; }
|
||||
void set_aref(u8 val) { m_val = (m_val & ~(0xff00)) | (val << 8); }
|
||||
|
||||
AlphaTest get_alpha_test() const { return (AlphaTest)((m_val >> 16) & 0b11); }
|
||||
void set_alpha_test(AlphaTest ab) { m_val = (m_val & ~(0b11 << 16)) | ((u32)(ab) << 16); }
|
||||
|
||||
GsTest::AlphaFail get_alpha_fail() const { return (GsTest::AlphaFail)((m_val >> 21) & 0b11); }
|
||||
void set_alpha_fail(GsTest::AlphaFail ab) { m_val = (m_val & ~(0b11 << 21)) | ((u32)(ab) << 21); }
|
||||
|
||||
bool is_invalid() const { return m_val == UINT32_MAX; }
|
||||
bool is_valid() const { return !is_invalid(); }
|
||||
void set_invalid() { m_val = UINT32_MAX; }
|
||||
|
||||
bool get_clamp_s_enable() const { return m_val & (1 << 5); }
|
||||
void set_clamp_s_enable(bool en) {
|
||||
if (en) {
|
||||
enable_s_clamp();
|
||||
} else {
|
||||
disable_s_clamp();
|
||||
}
|
||||
}
|
||||
void enable_s_clamp() { m_val = m_val | (1 << 5); }
|
||||
void disable_s_clamp() { m_val = m_val & (~(1 << 5)); }
|
||||
|
||||
bool get_filt_enable() const { return m_val & (1 << 6); }
|
||||
void enable_filt() { m_val = m_val | (1 << 6); }
|
||||
void disable_filt() { m_val = m_val & (~(1 << 6)); }
|
||||
void set_filt_enable(bool en) {
|
||||
if (en) {
|
||||
enable_filt();
|
||||
} else {
|
||||
disable_filt();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_tcc_enable() const { return m_val & (1 << 6); }
|
||||
void enable_tcc() { m_val = m_val | (1 << 7); }
|
||||
void disable_tcc() { m_val = m_val & (~(1 << 7)); }
|
||||
void set_tcc(bool en) {
|
||||
if (en) {
|
||||
enable_tcc();
|
||||
} else {
|
||||
disable_tcc();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_at_enable() const { return m_val & (1 << 18); }
|
||||
void enable_at() { m_val = m_val | (1 << 18); }
|
||||
void disable_at() { m_val = m_val & (~(1 << 18)); }
|
||||
void set_at(bool en) {
|
||||
if (en) {
|
||||
enable_at();
|
||||
} else {
|
||||
disable_at();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_zt_enable() const { return m_val & (1 << 19); }
|
||||
void enable_zt() { m_val = m_val | (1 << 19); }
|
||||
void disable_zt() { m_val = m_val & (~(1 << 19)); }
|
||||
void set_zt(bool en) {
|
||||
if (en) {
|
||||
enable_zt();
|
||||
} else {
|
||||
disable_zt();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_ab_enable() const { return m_val & (1 << 20); }
|
||||
void enable_ab() { m_val = m_val | (1 << 20); }
|
||||
void disable_ab() { m_val = m_val & (~(1 << 20)); }
|
||||
void set_ab(bool en) {
|
||||
if (en) {
|
||||
enable_ab();
|
||||
} else {
|
||||
disable_ab();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_clamp_t_enable() const { return m_val & (1 << 23); }
|
||||
void set_clamp_t_enable(bool en) {
|
||||
if (en) {
|
||||
enable_t_clamp();
|
||||
} else {
|
||||
disable_t_clamp();
|
||||
}
|
||||
}
|
||||
void enable_t_clamp() { m_val = m_val | (1 << 23); }
|
||||
void disable_t_clamp() { m_val = m_val & (~(1 << 23)); }
|
||||
|
||||
u32& as_int() { return m_val; }
|
||||
|
||||
bool operator==(const DrawMode& other) const { return m_val == other.m_val; }
|
||||
bool operator!=(const DrawMode& other) const { return m_val != other.m_val; }
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
private:
|
||||
// 0 - depth write enable
|
||||
// 1, 2 - test: never, always, gequal, greater
|
||||
// 3, 4 - alpha: disable, [src,dst,src,dst], [src,0,src,dst], XX
|
||||
// 5 - clamp enable
|
||||
// 6 - filt enable
|
||||
// 7 - tcc enable
|
||||
// 8,9,10,11,12,14,14,15 - aref
|
||||
// 16, 17 - atest
|
||||
// 18 - ate
|
||||
// 19 - zte
|
||||
// 20 - abe
|
||||
// 21, 22 - afail
|
||||
// 23 t clamp
|
||||
u32 m_val = UINT32_MAX;
|
||||
};
|
||||
|
||||
+17
-1
@@ -175,7 +175,7 @@ class Vector {
|
||||
const T* data() const { return m_data; }
|
||||
|
||||
template <typename U>
|
||||
Vector<U, Size> cast() {
|
||||
Vector<U, Size> cast() const {
|
||||
Vector<U, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = (U)m_data[i];
|
||||
@@ -183,6 +183,19 @@ class Vector {
|
||||
return result;
|
||||
}
|
||||
|
||||
template <int len>
|
||||
Vector<T, len> head() const {
|
||||
static_assert(len < Size);
|
||||
Vector<T, len> result;
|
||||
for (int i = 0; i < len; i++) {
|
||||
result[i] = m_data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<T, 3> xyz() const { return head<3>(); }
|
||||
Vector<T, 3> xy() const { return head<2>(); }
|
||||
|
||||
private:
|
||||
T m_data[Size];
|
||||
};
|
||||
@@ -211,6 +224,9 @@ struct Matrix {
|
||||
return result;
|
||||
}
|
||||
|
||||
T* data() { return m_data; }
|
||||
const T* data() const { return m_data; }
|
||||
|
||||
std::string to_string_aligned() const {
|
||||
std::string result;
|
||||
for (int row = 0; row < Rows; row++) {
|
||||
|
||||
@@ -151,7 +151,7 @@ class Serializer {
|
||||
} else {
|
||||
vec->resize(load<size_t>());
|
||||
}
|
||||
from_raw_data(vec->data(), vec->size());
|
||||
from_raw_data(vec->data(), sizeof(T) * vec->size());
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "os.h"
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
size_t get_peak_rss() {
|
||||
rusage x;
|
||||
getrusage(RUSAGE_SELF, &x);
|
||||
return x.ru_maxrss * 1024;
|
||||
}
|
||||
|
||||
#else
|
||||
size_t get_peak_rss() {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// Note: these are not implemented on windows and will return zero.
|
||||
size_t get_peak_rss();
|
||||
@@ -24,6 +24,7 @@ add_library(
|
||||
data/game_text.cpp
|
||||
data/streamed_audio.cpp
|
||||
data/StrFileReader.cpp
|
||||
data/TextureDB.cpp
|
||||
data/tpage.cpp
|
||||
|
||||
Disasm/Instruction.cpp
|
||||
@@ -54,6 +55,10 @@ add_library(
|
||||
IR2/LabelDB.cpp
|
||||
IR2/OpenGoalMapping.cpp
|
||||
|
||||
level_extractor/extract_level.cpp
|
||||
level_extractor/extract_tfrag.cpp
|
||||
level_extractor/BspHeader.cpp
|
||||
|
||||
ObjectFile/LinkedObjectFile.cpp
|
||||
ObjectFile/LinkedObjectFileCreation.cpp
|
||||
ObjectFile/ObjectFileDB.cpp
|
||||
@@ -63,6 +68,7 @@ add_library(
|
||||
util/data_decompile.cpp
|
||||
util/DataParser.cpp
|
||||
util/DecompilerTypeSystem.cpp
|
||||
util/goal_data_reader.cpp
|
||||
util/sparticle_decompile.cpp
|
||||
util/TP_Type.cpp
|
||||
|
||||
|
||||
@@ -1135,36 +1135,36 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg
|
||||
}
|
||||
}
|
||||
|
||||
if (word.kind == LinkedWord::SYM_OFFSET) {
|
||||
if (word.kind() == LinkedWord::SYM_OFFSET) {
|
||||
bool fixed = false;
|
||||
for (int j = 0; j < i.n_src; j++) {
|
||||
if (i.src[j].kind == InstructionAtom::IMM) {
|
||||
fixed = true;
|
||||
i.src[j].set_sym(word.symbol_name);
|
||||
i.src[j].set_sym(word.symbol_name());
|
||||
}
|
||||
}
|
||||
assert(fixed);
|
||||
}
|
||||
|
||||
if (word.kind == LinkedWord::HI_PTR) {
|
||||
if (word.kind() == LinkedWord::HI_PTR) {
|
||||
assert(i.kind == InstructionKind::LUI);
|
||||
bool fixed = false;
|
||||
for (int j = 0; j < i.n_src; j++) {
|
||||
if (i.src[j].kind == InstructionAtom::IMM) {
|
||||
fixed = true;
|
||||
i.src[j].set_label(word.label_id);
|
||||
i.src[j].set_label(word.label_id());
|
||||
}
|
||||
}
|
||||
assert(fixed);
|
||||
}
|
||||
|
||||
if (word.kind == LinkedWord::LO_PTR) {
|
||||
if (word.kind() == LinkedWord::LO_PTR) {
|
||||
assert(i.kind == InstructionKind::ORI);
|
||||
bool fixed = false;
|
||||
for (int j = 0; j < i.n_src; j++) {
|
||||
if (i.src[j].kind == InstructionAtom::IMM) {
|
||||
fixed = true;
|
||||
i.src[j].set_label(word.label_id);
|
||||
i.src[j].set_label(word.label_id());
|
||||
}
|
||||
}
|
||||
assert(fixed);
|
||||
|
||||
@@ -499,7 +499,7 @@ FormElement* make_label_load(int label_idx,
|
||||
load_size == 4 && hint.result_type == TypeSpec("float")) {
|
||||
assert((label.offset % 4) == 0);
|
||||
auto word = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word.kind() == LinkedWord::PLAIN_DATA);
|
||||
float value;
|
||||
memcpy(&value, &word.data, 4);
|
||||
return pool.alloc_element<ConstantFloatElement>(value);
|
||||
@@ -508,8 +508,8 @@ FormElement* make_label_load(int label_idx,
|
||||
assert((label.offset % 8) == 0);
|
||||
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
|
||||
assert(word0.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word0.kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind() == LinkedWord::PLAIN_DATA);
|
||||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
@@ -526,8 +526,8 @@ FormElement* make_label_load(int label_idx,
|
||||
assert((label.offset % 8) == 0);
|
||||
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
|
||||
assert(word0.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word0.kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(word1.kind() == LinkedWord::PLAIN_DATA);
|
||||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
|
||||
+17
-30
@@ -9,15 +9,22 @@
|
||||
#include "common/util/math_util.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
constexpr const char* reg_names[] = {"a0-0", "a1-0", "a2-0", "a3-0",
|
||||
"t0-0", "t1-0", "t2-0", "t3-0"};
|
||||
|
||||
const char* get_reg_name(int idx) {
|
||||
if (idx >= 8) {
|
||||
return "INVALID";
|
||||
} else {
|
||||
return reg_names[idx];
|
||||
}
|
||||
}
|
||||
|
||||
void Env::set_remap_for_function(const TypeSpec& ts) {
|
||||
int nargs = ts.arg_count() - 1;
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
std::string var_name;
|
||||
var_name.push_back(i >= 4 ? 't' : 'a');
|
||||
var_name.push_back('0' + (i % 4));
|
||||
var_name.push_back('-');
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = ("arg" + std::to_string(i));
|
||||
m_var_remap[get_reg_name(i)] = ("arg" + std::to_string(i));
|
||||
}
|
||||
if (ts.try_get_tag("behavior")) {
|
||||
m_var_remap["s6-0"] = "self";
|
||||
@@ -32,12 +39,7 @@ void Env::set_remap_for_new_method(const TypeSpec& ts) {
|
||||
m_var_remap["a0-0"] = "allocation";
|
||||
m_var_remap["a1-0"] = "type-to-make";
|
||||
for (int i = 2; i < nargs; i++) {
|
||||
std::string var_name;
|
||||
var_name.push_back(i >= 4 ? 't' : 'a');
|
||||
var_name.push_back('0' + (i % 4));
|
||||
var_name.push_back('-');
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = ("arg" + std::to_string(i - 2));
|
||||
m_var_remap[get_reg_name(i)] = ("arg" + std::to_string(i - 2));
|
||||
}
|
||||
if (ts.try_get_tag("behavior")) {
|
||||
m_var_remap["s6-0"] = "self";
|
||||
@@ -51,12 +53,7 @@ void Env::set_remap_for_method(const TypeSpec& ts) {
|
||||
int nargs = ts.arg_count() - 1;
|
||||
m_var_remap["a0-0"] = "obj";
|
||||
for (int i = 1; i < nargs; i++) {
|
||||
std::string var_name;
|
||||
var_name.push_back(i >= 4 ? 't' : 'a');
|
||||
var_name.push_back('0' + (i % 4));
|
||||
var_name.push_back('-');
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = ("arg" + std::to_string(i - 1));
|
||||
m_var_remap[get_reg_name(i)] = ("arg" + std::to_string(i - 1));
|
||||
}
|
||||
if (ts.try_get_tag("behavior")) {
|
||||
m_var_remap["s6-0"] = "self";
|
||||
@@ -69,12 +66,7 @@ void Env::set_remap_for_method(const TypeSpec& ts) {
|
||||
void Env::map_args_from_config(const std::vector<std::string>& args_names,
|
||||
const std::unordered_map<std::string, std::string>& var_names) {
|
||||
for (size_t i = 0; i < args_names.size(); i++) {
|
||||
std::string var_name;
|
||||
var_name.push_back(i >= 4 ? 't' : 'a');
|
||||
var_name.push_back('0' + (i % 4));
|
||||
var_name.push_back('-');
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = args_names[i];
|
||||
m_var_remap[get_reg_name(i)] = args_names[i];
|
||||
}
|
||||
|
||||
for (auto& x : var_names) {
|
||||
@@ -86,12 +78,7 @@ void Env::map_args_from_config(
|
||||
const std::vector<std::string>& args_names,
|
||||
const std::unordered_map<std::string, LocalVarOverride>& var_overrides) {
|
||||
for (size_t i = 0; i < args_names.size(); i++) {
|
||||
std::string var_name;
|
||||
var_name.push_back(i >= 4 ? 't' : 'a');
|
||||
var_name.push_back('0' + (i % 4));
|
||||
var_name.push_back('-');
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = args_names[i];
|
||||
m_var_remap[get_reg_name(i)] = args_names[i];
|
||||
}
|
||||
|
||||
for (auto& x : var_overrides) {
|
||||
|
||||
@@ -150,7 +150,7 @@ bool LinkedObjectFile::pointer_link_word(int source_segment,
|
||||
assert((source_offset % 4) == 0);
|
||||
|
||||
auto& word = words_by_seg.at(source_segment).at(source_offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word.kind() == LinkedWord::PLAIN_DATA);
|
||||
|
||||
if (dest_offset / 4 > (int)words_by_seg.at(dest_segment).size()) {
|
||||
// printf("HACK bad link ignored!\n");
|
||||
@@ -158,8 +158,7 @@ bool LinkedObjectFile::pointer_link_word(int source_segment,
|
||||
}
|
||||
assert(dest_offset / 4 <= (int)words_by_seg.at(dest_segment).size());
|
||||
|
||||
word.kind = LinkedWord::PTR;
|
||||
word.label_id = get_label_id_for(dest_segment, dest_offset);
|
||||
word.set_to_pointer(LinkedWord::PTR, get_label_id_for(dest_segment, dest_offset));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -173,11 +172,10 @@ void LinkedObjectFile::symbol_link_word(int source_segment,
|
||||
assert((source_offset % 4) == 0);
|
||||
auto& word = words_by_seg.at(source_segment).at(source_offset / 4);
|
||||
// assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
printf("bad symbol link word\n");
|
||||
}
|
||||
word.kind = kind;
|
||||
word.symbol_name = name;
|
||||
word.set_to_symbol(kind, name);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -187,9 +185,8 @@ void LinkedObjectFile::symbol_link_word(int source_segment,
|
||||
void LinkedObjectFile::symbol_link_offset(int source_segment, int source_offset, const char* name) {
|
||||
assert((source_offset % 4) == 0);
|
||||
auto& word = words_by_seg.at(source_segment).at(source_offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
word.kind = LinkedWord::SYM_OFFSET;
|
||||
word.symbol_name = name;
|
||||
assert(word.kind() == LinkedWord::PLAIN_DATA);
|
||||
word.set_to_symbol(LinkedWord::SYM_OFFSET, name);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -207,14 +204,11 @@ void LinkedObjectFile::pointer_link_split_word(int source_segment,
|
||||
auto& lo_word = words_by_seg.at(source_segment).at(source_lo_offset / 4);
|
||||
|
||||
// assert(dest_offset / 4 <= (int)words_by_seg.at(dest_segment).size());
|
||||
assert(hi_word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(lo_word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(hi_word.kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(lo_word.kind() == LinkedWord::PLAIN_DATA);
|
||||
|
||||
hi_word.kind = LinkedWord::HI_PTR;
|
||||
hi_word.label_id = get_label_id_for(dest_segment, dest_offset);
|
||||
|
||||
lo_word.kind = LinkedWord::LO_PTR;
|
||||
lo_word.label_id = hi_word.label_id;
|
||||
hi_word.set_to_pointer(LinkedWord::HI_PTR, get_label_id_for(dest_segment, dest_offset));
|
||||
lo_word.set_to_pointer(LinkedWord::LO_PTR, hi_word.label_id());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -284,32 +278,32 @@ std::string LinkedObjectFile::print_words() {
|
||||
void LinkedObjectFile::append_word_to_string(std::string& dest, const LinkedWord& word) const {
|
||||
char buff[128];
|
||||
|
||||
switch (word.kind) {
|
||||
switch (word.kind()) {
|
||||
case LinkedWord::PLAIN_DATA:
|
||||
sprintf(buff, " .word 0x%x\n", word.data);
|
||||
break;
|
||||
case LinkedWord::PTR:
|
||||
sprintf(buff, " .word %s\n", labels.at(word.label_id).name.c_str());
|
||||
sprintf(buff, " .word %s\n", labels.at(word.label_id()).name.c_str());
|
||||
break;
|
||||
case LinkedWord::SYM_PTR:
|
||||
sprintf(buff, " .symbol %s\n", word.symbol_name.c_str());
|
||||
sprintf(buff, " .symbol %s\n", word.symbol_name().c_str());
|
||||
break;
|
||||
case LinkedWord::TYPE_PTR:
|
||||
sprintf(buff, " .type %s\n", word.symbol_name.c_str());
|
||||
sprintf(buff, " .type %s\n", word.symbol_name().c_str());
|
||||
break;
|
||||
case LinkedWord::EMPTY_PTR:
|
||||
sprintf(buff, " .empty-list\n"); // ?
|
||||
break;
|
||||
case LinkedWord::HI_PTR:
|
||||
sprintf(buff, " .ptr-hi 0x%x %s\n", word.data >> 16,
|
||||
labels.at(word.label_id).name.c_str());
|
||||
labels.at(word.label_id()).name.c_str());
|
||||
break;
|
||||
case LinkedWord::LO_PTR:
|
||||
sprintf(buff, " .ptr-lo 0x%x %s\n", word.data >> 16,
|
||||
labels.at(word.label_id).name.c_str());
|
||||
labels.at(word.label_id()).name.c_str());
|
||||
break;
|
||||
case LinkedWord::SYM_OFFSET:
|
||||
sprintf(buff, " .sym-off 0x%x %s\n", word.data >> 16, word.symbol_name.c_str());
|
||||
sprintf(buff, " .sym-off 0x%x %s\n", word.data >> 16, word.symbol_name().c_str());
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("nyi");
|
||||
@@ -326,8 +320,8 @@ void LinkedObjectFile::find_code() {
|
||||
// single segment object files should never have any code.
|
||||
auto& seg = words_by_seg.front();
|
||||
for (auto& word : seg) {
|
||||
if (!word.symbol_name.empty()) {
|
||||
assert(word.symbol_name != "function");
|
||||
if (word.kind() == LinkedWord::TYPE_PTR) {
|
||||
assert(word.symbol_name() != "function");
|
||||
}
|
||||
}
|
||||
offset_of_data_zone_by_seg.at(0) = 0;
|
||||
@@ -346,7 +340,7 @@ void LinkedObjectFile::find_code() {
|
||||
size_t function_loc = -1;
|
||||
for (size_t j = words_by_seg.at(i).size(); j-- > 0;) {
|
||||
auto& word = words_by_seg.at(i).at(j);
|
||||
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name == "function") {
|
||||
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "function") {
|
||||
function_loc = j;
|
||||
found_function = true;
|
||||
break;
|
||||
@@ -361,7 +355,7 @@ void LinkedObjectFile::find_code() {
|
||||
|
||||
for (size_t j = function_loc; j < words_by_seg.at(i).size(); j++) {
|
||||
auto& word = words_by_seg.at(i).at(j);
|
||||
if (word.kind == LinkedWord::PLAIN_DATA && word.data == jr_ra) {
|
||||
if (word.kind() == LinkedWord::PLAIN_DATA && word.data == jr_ra) {
|
||||
found_jr_ra = true;
|
||||
jr_ra_loc = j;
|
||||
}
|
||||
@@ -385,7 +379,7 @@ void LinkedObjectFile::find_code() {
|
||||
// verify there are no functions after the data section starts
|
||||
for (size_t j = offset_of_data_zone_by_seg.at(i); j < words_by_seg.at(i).size(); j++) {
|
||||
auto& word = words_by_seg.at(i).at(j);
|
||||
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name == "function") {
|
||||
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "function") {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
@@ -421,7 +415,7 @@ void LinkedObjectFile::find_functions() {
|
||||
bool found_function_tag_loc = false;
|
||||
for (; function_tag_loc-- > 0;) {
|
||||
auto& word = words_by_seg.at(seg).at(function_tag_loc);
|
||||
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name == "function") {
|
||||
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "function") {
|
||||
found_function_tag_loc = true;
|
||||
break;
|
||||
}
|
||||
@@ -729,7 +723,7 @@ std::string LinkedObjectFile::print_disassembly(bool write_hex) {
|
||||
auto& word = words_by_seg[seg][i];
|
||||
append_word_to_string(result, word);
|
||||
|
||||
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name == "string") {
|
||||
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "string") {
|
||||
result += "; " + get_goal_string(seg, i) + "\n";
|
||||
}
|
||||
}
|
||||
@@ -751,7 +745,7 @@ std::string LinkedObjectFile::get_goal_string(int seg, int word_idx, bool with_q
|
||||
return "invalid string!\n";
|
||||
}
|
||||
const LinkedWord& size_word = words_by_seg[seg].at(word_idx + 1);
|
||||
if (size_word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (size_word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
// sometimes an array of string pointer triggers this!
|
||||
return "invalid string!\n";
|
||||
}
|
||||
@@ -762,7 +756,7 @@ std::string LinkedObjectFile::get_goal_string(int seg, int word_idx, bool with_q
|
||||
int word_offset = word_idx + 2 + (i / 4);
|
||||
int byte_offset = i % 4;
|
||||
auto& word = words_by_seg[seg].at(word_offset);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
return "invalid string! (check me!)\n";
|
||||
}
|
||||
char cword[4];
|
||||
@@ -827,7 +821,7 @@ std::string LinkedObjectFile::print_scripts() {
|
||||
bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) {
|
||||
assert((byte_idx % 4) == 0);
|
||||
auto& word = words_by_seg.at(seg).at(byte_idx / 4);
|
||||
return word.kind == LinkedWord::EMPTY_PTR;
|
||||
return word.kind() == LinkedWord::EMPTY_PTR;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -866,9 +860,10 @@ goos::Object LinkedObjectFile::to_form_script(int seg, int word_idx, std::vector
|
||||
assert((cdr_addr % 4) == 0);
|
||||
auto& cdr_word = words_by_seg.at(seg).at(cdr_addr / 4);
|
||||
// check for proper list
|
||||
if (cdr_word.kind == LinkedWord::PTR && (labels.at(cdr_word.label_id).offset & 7) == 2) {
|
||||
if (cdr_word.kind() == LinkedWord::PTR &&
|
||||
(labels.at(cdr_word.label_id()).offset & 7) == 2) {
|
||||
// yes, proper list. add another pair and link it in to the list.
|
||||
goal_print_obj = labels.at(cdr_word.label_id).offset;
|
||||
goal_print_obj = labels.at(cdr_word.label_id()).offset;
|
||||
fill.as_pair()->cdr = goos::PairObject::make_new(goos::Object::make_empty_list(),
|
||||
goos::Object::make_empty_list());
|
||||
fill = fill.as_pair()->cdr;
|
||||
@@ -900,7 +895,7 @@ bool LinkedObjectFile::is_string(int seg, int byte_idx) const {
|
||||
return false;
|
||||
}
|
||||
auto& type_word = words_by_seg.at(seg).at(type_tag_ptr / 4);
|
||||
return type_word.kind == LinkedWord::TYPE_PTR && type_word.symbol_name == "string";
|
||||
return type_word.kind() == LinkedWord::TYPE_PTR && type_word.symbol_name() == "string";
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -915,15 +910,15 @@ goos::Object LinkedObjectFile::to_form_script_object(int seg,
|
||||
case 0:
|
||||
case 4: {
|
||||
auto& word = words_by_seg.at(seg).at(byte_idx / 4);
|
||||
if (word.kind == LinkedWord::SYM_PTR) {
|
||||
if (word.kind() == LinkedWord::SYM_PTR) {
|
||||
// .symbol xxxx
|
||||
result = pretty_print::to_symbol(word.symbol_name);
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA) {
|
||||
result = pretty_print::to_symbol(word.symbol_name());
|
||||
} else if (word.kind() == LinkedWord::PLAIN_DATA) {
|
||||
// .word xxxxx
|
||||
result = pretty_print::to_symbol(std::to_string(word.data));
|
||||
} else if (word.kind == LinkedWord::PTR) {
|
||||
} else if (word.kind() == LinkedWord::PTR) {
|
||||
// might be a sub-list, or some other random pointer
|
||||
auto offset = labels.at(word.label_id).offset;
|
||||
auto offset = labels.at(word.label_id()).offset;
|
||||
if ((offset & 7) == 2) {
|
||||
// list!
|
||||
result = to_form_script(seg, offset / 4, seen);
|
||||
@@ -932,10 +927,10 @@ goos::Object LinkedObjectFile::to_form_script_object(int seg,
|
||||
result = pretty_print::to_symbol(get_goal_string(seg, offset / 4 - 1));
|
||||
} else {
|
||||
// some random pointer, just print the label.
|
||||
result = pretty_print::to_symbol(labels.at(word.label_id).name);
|
||||
result = pretty_print::to_symbol(labels.at(word.label_id()).name);
|
||||
}
|
||||
}
|
||||
} else if (word.kind == LinkedWord::EMPTY_PTR) {
|
||||
} else if (word.kind() == LinkedWord::EMPTY_PTR) {
|
||||
result = goos::Object::make_empty_list();
|
||||
} else {
|
||||
std::string debug;
|
||||
@@ -958,7 +953,7 @@ goos::Object LinkedObjectFile::to_form_script_object(int seg,
|
||||
u32 LinkedObjectFile::read_data_word(const DecompilerLabel& label) {
|
||||
assert(0 == (label.offset % 4));
|
||||
auto& word = words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::Kind::PLAIN_DATA);
|
||||
assert(word.kind() == LinkedWord::Kind::PLAIN_DATA);
|
||||
return word.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include "common/util/assert.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace decompiler {
|
||||
class LinkedWord {
|
||||
public:
|
||||
explicit LinkedWord(uint32_t _data) : data(_data) {}
|
||||
|
||||
enum Kind {
|
||||
enum Kind : u8 {
|
||||
PLAIN_DATA, // just plain data
|
||||
PTR, // pointer to a location
|
||||
HI_PTR, // lower 16-bits of this data are the upper 16 bits of a pointer
|
||||
@@ -25,15 +23,98 @@ class LinkedWord {
|
||||
EMPTY_PTR, // this is a pointer to the empty list
|
||||
SYM_OFFSET, // this is an offset of a symbol in the symbol table
|
||||
TYPE_PTR // this is a pointer to a type
|
||||
} kind = PLAIN_DATA;
|
||||
};
|
||||
|
||||
uint32_t data = 0;
|
||||
private:
|
||||
uintptr_t m_data_ptr = 0; // 8 bytes
|
||||
public:
|
||||
u32 data;
|
||||
|
||||
int label_id = -1;
|
||||
std::string symbol_name;
|
||||
private:
|
||||
Kind m_kind = PLAIN_DATA;
|
||||
|
||||
public:
|
||||
explicit LinkedWord(uint32_t _data) : data(_data) {}
|
||||
|
||||
bool holds_string() const {
|
||||
return m_kind == SYM_PTR || m_kind == SYM_OFFSET || m_kind == TYPE_PTR;
|
||||
}
|
||||
|
||||
LinkedWord(const LinkedWord& other) {
|
||||
if (&other != this) {
|
||||
data = other.data;
|
||||
m_kind = other.m_kind;
|
||||
if (holds_string()) {
|
||||
const char* other_str = (const char*)(other.m_data_ptr);
|
||||
char* str = new char[strlen(other_str) + 1];
|
||||
strcpy(str, other_str);
|
||||
m_data_ptr = (uintptr_t)str;
|
||||
} else {
|
||||
m_data_ptr = other.m_data_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LinkedWord& operator=(const LinkedWord& other) {
|
||||
if (&other != this) {
|
||||
data = other.data;
|
||||
if (holds_string()) {
|
||||
delete[]((char*)m_data_ptr);
|
||||
}
|
||||
m_kind = other.m_kind;
|
||||
|
||||
if (holds_string()) {
|
||||
const char* other_str = (const char*)(other.m_data_ptr);
|
||||
char* str = new char[strlen(other_str) + 1];
|
||||
strcpy(str, other_str);
|
||||
m_data_ptr = (uintptr_t)str;
|
||||
} else {
|
||||
m_data_ptr = other.m_data_ptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~LinkedWord() {
|
||||
if (holds_string()) {
|
||||
delete[]((char*)m_data_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void set_to_empty_ptr() {
|
||||
if (holds_string()) {
|
||||
delete[]((char*)m_data_ptr);
|
||||
}
|
||||
m_kind = EMPTY_PTR;
|
||||
}
|
||||
|
||||
void set_to_symbol(Kind kind, const std::string& name) {
|
||||
if (holds_string()) {
|
||||
delete[]((char*)m_data_ptr);
|
||||
}
|
||||
m_kind = kind;
|
||||
char* str = new char[name.size() + 1];
|
||||
strcpy(str, name.c_str());
|
||||
m_data_ptr = (uintptr_t)str;
|
||||
}
|
||||
|
||||
void set_to_pointer(Kind kind, u32 label_id) {
|
||||
if (holds_string()) {
|
||||
delete[]((char*)m_data_ptr);
|
||||
}
|
||||
m_data_ptr = label_id;
|
||||
m_kind = kind;
|
||||
}
|
||||
|
||||
void set_to_plain_data() {
|
||||
if (holds_string()) {
|
||||
delete[]((char*)m_data_ptr);
|
||||
}
|
||||
m_kind = PLAIN_DATA;
|
||||
}
|
||||
|
||||
u8 get_byte(int idx) const {
|
||||
assert(kind == PLAIN_DATA);
|
||||
assert(kind() == PLAIN_DATA);
|
||||
switch (idx) {
|
||||
case 0:
|
||||
return data & 0xff;
|
||||
@@ -48,5 +129,25 @@ class LinkedWord {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// kind, label_id, symbol_name
|
||||
Kind kind() const { return m_kind; }
|
||||
|
||||
u32 label_id() const {
|
||||
assert(m_kind == PTR || m_kind == LO_PTR || m_kind == HI_PTR);
|
||||
return m_data_ptr;
|
||||
}
|
||||
|
||||
std::string symbol_name() const {
|
||||
assert(holds_string());
|
||||
return (const char*)(m_data_ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
// if plain data, this is empty.
|
||||
// otherwise:
|
||||
// u8 (kind)
|
||||
// u32 label id | actual string.
|
||||
};
|
||||
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -428,7 +428,7 @@ void ObjectFileDB::process_link_data(const Config& config) {
|
||||
});
|
||||
|
||||
lg::info("Processed Link Data");
|
||||
lg::info(" Total {} ms\n", process_link_timer.getMs());
|
||||
lg::info(" Total {:.2f} ms\n", process_link_timer.getMs());
|
||||
// printf("\n");
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ void ObjectFileDB::process_labels() {
|
||||
|
||||
lg::info("Processed Labels:");
|
||||
lg::info(" Total {} labels", total);
|
||||
lg::info(" Total {} ms\n", process_label_timer.getMs());
|
||||
lg::info(" Total {:.2f} ms\n", process_label_timer.getMs());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -579,7 +579,7 @@ void ObjectFileDB::find_and_write_scripts(const std::string& output_dir) {
|
||||
lg::info(" Total {:.3f} ms\n", timer.getMs());
|
||||
}
|
||||
|
||||
std::string ObjectFileDB::process_tpages() {
|
||||
std::string ObjectFileDB::process_tpages(TextureDB& tex_db) {
|
||||
lg::info("- Finding textures in tpages...");
|
||||
std::string tpage_string = "tpage-";
|
||||
int total = 0, success = 0;
|
||||
@@ -590,7 +590,7 @@ std::string ObjectFileDB::process_tpages() {
|
||||
std::string result;
|
||||
for_each_obj([&](ObjectFileData& data) {
|
||||
if (data.name_in_dgo.substr(0, tpage_string.length()) == tpage_string) {
|
||||
auto statistics = process_tpage(data);
|
||||
auto statistics = process_tpage(data, tex_db);
|
||||
total += statistics.total_textures;
|
||||
success += statistics.successful_textures;
|
||||
total_px += statistics.num_px;
|
||||
@@ -602,13 +602,13 @@ std::string ObjectFileDB::process_tpages() {
|
||||
|
||||
assert(tpage_dir_count <= 1);
|
||||
|
||||
lg::info("Processed {} / {} textures ({} px) {:.2f}% in {:.2f} ms", success, total, total_px,
|
||||
100.f * float(success) / float(total), timer.getMs());
|
||||
|
||||
if (tpage_dir_count == 0) {
|
||||
lg::warn("Did not find tpage-dir.");
|
||||
return {};
|
||||
}
|
||||
|
||||
lg::info("Processed {} / {} textures ({} px) {:.2f}% in {:.2f} ms", success, total, total_px,
|
||||
100.f * float(success) / float(total), timer.getMs());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "LinkedObjectFile.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "common/common_types.h"
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
#include "decompiler/analysis/symbol_def_map.h"
|
||||
|
||||
namespace decompiler {
|
||||
@@ -96,7 +97,7 @@ class ObjectFileDB {
|
||||
std::string ir2_final_out(ObjectFileData& data,
|
||||
const std::unordered_set<std::string>& skip_functions = {});
|
||||
|
||||
std::string process_tpages();
|
||||
std::string process_tpages(TextureDB& tex_db);
|
||||
std::string process_game_count_file();
|
||||
std::string process_game_text_files();
|
||||
|
||||
|
||||
@@ -706,7 +706,7 @@ std::string ObjectFileDB::ir2_to_file(ObjectFileData& data, const Config& config
|
||||
auto& word = data.linked_data.words_by_seg[seg][i];
|
||||
data.linked_data.append_word_to_string(result, word);
|
||||
|
||||
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name == "string") {
|
||||
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "string") {
|
||||
result += "; " + data.linked_data.get_goal_string(seg, i) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,24 +230,24 @@ std::string verify_empty_state_and_get_name(DecompiledDataElement* state, const
|
||||
auto& words = env.file->words_by_seg.at(lab.target_segment);
|
||||
|
||||
auto first_word = words.at(start_word_idx);
|
||||
if (first_word.kind != LinkedWord::TYPE_PTR || first_word.symbol_name != "state") {
|
||||
if (first_word.kind() != LinkedWord::TYPE_PTR || first_word.symbol_name() != "state") {
|
||||
env.func->warnings.warn_and_throw("Reference to state bad: invalid type pointer");
|
||||
}
|
||||
|
||||
auto name_word = words.at(start_word_idx + 1);
|
||||
if (name_word.kind != LinkedWord::SYM_PTR) {
|
||||
if (name_word.kind() != LinkedWord::SYM_PTR) {
|
||||
env.func->warnings.warn_and_throw("Reference to state bad: invalid name");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
auto& word = words.at(start_word_idx + 2 + i);
|
||||
if (word.kind != LinkedWord::SYM_PTR || word.symbol_name != "#f") {
|
||||
if (word.kind() != LinkedWord::SYM_PTR || word.symbol_name() != "#f") {
|
||||
env.func->warnings.warn_and_throw(
|
||||
"Reference to state bad: got a non #f in the initial fields");
|
||||
}
|
||||
}
|
||||
|
||||
return name_word.symbol_name;
|
||||
return name_word.symbol_name();
|
||||
}
|
||||
|
||||
FormElement* rewrite_virtual_defstate(
|
||||
|
||||
@@ -61,34 +61,34 @@ DefskelgroupElement::StaticInfo inspect_skel_group_data(DecompiledDataElement* s
|
||||
auto& words = env.file->words_by_seg.at(lab.target_segment);
|
||||
|
||||
auto& type_word = words.at(start_word_idx - 1);
|
||||
if (type_word.kind != LinkedWord::TYPE_PTR || type_word.symbol_name != "skeleton-group") {
|
||||
if (type_word.kind() != LinkedWord::TYPE_PTR || type_word.symbol_name() != "skeleton-group") {
|
||||
env.func->warnings.warn_and_throw("Reference to skelgroup bad: invalid type pointer");
|
||||
}
|
||||
auto& string_word = words.at(start_word_idx);
|
||||
if (string_word.kind != LinkedWord::PTR) {
|
||||
if (string_word.kind() != LinkedWord::PTR) {
|
||||
env.func->warnings.warn_and_throw("Reference to skelgroup bad: invalid name label");
|
||||
}
|
||||
result.art_name = env.file->get_goal_string_by_label(
|
||||
env.file->get_label_by_name(env.file->get_label_name(string_word.label_id)));
|
||||
env.file->get_label_by_name(env.file->get_label_name(string_word.label_id())));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto& word = words.at(start_word_idx + 3 + i);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
env.func->warnings.warn_and_throw("Reference to skelgroup bad: invalid bounds");
|
||||
}
|
||||
result.bounds[i] = *reinterpret_cast<float*>(&word.data);
|
||||
}
|
||||
auto& lod_word = words.at(start_word_idx + 9);
|
||||
if (lod_word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (lod_word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
env.func->warnings.warn_and_throw("Reference to skelgroup bad: invalid max-lod");
|
||||
}
|
||||
result.max_lod = lod_word.data;
|
||||
auto& edge_word = words.at(start_word_idx + 14);
|
||||
if (edge_word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (edge_word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
env.func->warnings.warn_and_throw("Reference to skelgroup bad: invalid longest-edge");
|
||||
}
|
||||
result.longest_edge = *reinterpret_cast<float*>(&edge_word.data);
|
||||
auto& other_word = words.at(start_word_idx + 15);
|
||||
if (other_word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (other_word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
env.func->warnings.warn_and_throw("Reference to skelgroup bad: invalid other data");
|
||||
}
|
||||
result.tex_level = other_word.get_byte(0);
|
||||
|
||||
@@ -80,8 +80,8 @@ void find_boxed(LabelDB* db, LinkedObjectFile* file) {
|
||||
const auto& word = file->words_by_seg.at(lab.target_segment).at((lab.offset - 4) / 4);
|
||||
// the snowball-bank is a weird basic with no fields other than the built-in type.
|
||||
// so it can actually share a label with something else.
|
||||
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name != "snowball-bank") {
|
||||
TypeSpec basic_type(word.symbol_name);
|
||||
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() != "snowball-bank") {
|
||||
TypeSpec basic_type(word.symbol_name());
|
||||
const auto& existing = db->lookup(lab.name);
|
||||
if (existing.known) {
|
||||
if (existing.result_type.base_type() != basic_type.base_type()) {
|
||||
|
||||
@@ -516,7 +516,7 @@ Mips2C_Line handle_lwc1(const Instruction& i0,
|
||||
if (i0.get_src(0).is_label() && i0.get_src(1).is_reg(Register(Reg::GPR, Reg::FP))) {
|
||||
auto& label = file->labels.at(i0.get_src(0).get_label());
|
||||
auto& word = file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word.kind() == LinkedWord::PLAIN_DATA);
|
||||
float f;
|
||||
memcpy(&f, &word.data, 4);
|
||||
return {fmt::format("c->fprs[{}] = {};", reg_to_name(i0.get_dst(0)), float_to_string(f)),
|
||||
|
||||
@@ -192,8 +192,15 @@ Config read_config_file(const std::string& path_to_config_file) {
|
||||
max_len;
|
||||
}
|
||||
|
||||
for (auto& entry : hacks_json.at("missing_textures")) {
|
||||
int tpage = entry.at(1).get<int>();
|
||||
int idx = entry.at(2).get<int>();
|
||||
config.hacks.missing_textures_by_level[entry.at(0).get<std::string>()].emplace_back(tpage, idx);
|
||||
}
|
||||
|
||||
config.bad_format_strings =
|
||||
hacks_json.at("bad_format_strings").get<std::unordered_map<std::string, int>>();
|
||||
config.levels_to_extract = cfg.at("levels_to_extract").get<std::vector<std::string>>();
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ struct DecompileHacks {
|
||||
std::unordered_map<std::string, std::vector<std::vector<int>>>
|
||||
format_ops_with_dynamic_string_by_func_name;
|
||||
std::unordered_set<std::string> mips2c_functions_by_name;
|
||||
std::unordered_map<std::string, std::vector<std::pair<int, int>>> missing_textures_by_level;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
@@ -125,6 +126,8 @@ struct Config {
|
||||
|
||||
std::unordered_map<std::string, int> bad_format_strings;
|
||||
|
||||
std::vector<std::string> levels_to_extract;
|
||||
|
||||
DecompileHacks hacks;
|
||||
};
|
||||
|
||||
|
||||
@@ -12933,6 +12933,7 @@
|
||||
(pad0 uint8 :offset 58)
|
||||
(pad1 uint8 :offset 59)
|
||||
(generic generic-tfragment :offset-assert 60)
|
||||
(generic-u32 uint32 :offset 60)
|
||||
)
|
||||
:method-count-assert 18
|
||||
:size-assert #x40
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"disassemble_code": false,
|
||||
|
||||
// Run the decompiler
|
||||
"decompile_code": true,
|
||||
"decompile_code": false,
|
||||
|
||||
////////////////////////////
|
||||
// DATA ANALYSIS OPTIONS
|
||||
@@ -77,5 +77,36 @@
|
||||
|
||||
// optional: a predetermined object file name map from a file.
|
||||
// this will make decompilation naming consistent even if you only run on some objects.
|
||||
"obj_file_name_map_file": "goal_src/build/all_objs.json"
|
||||
"obj_file_name_map_file": "goal_src/build/all_objs.json",
|
||||
|
||||
////////////////////////////
|
||||
// LEVEL EXTRACTION
|
||||
////////////////////////////
|
||||
|
||||
"levels_to_extract":[
|
||||
"BEA.DGO",
|
||||
"CIT.DGO",
|
||||
"DAR.DGO",
|
||||
"DEM.DGO",
|
||||
"FIN.DGO",
|
||||
"INT.DGO",
|
||||
"JUB.DGO",
|
||||
"JUN.DGO",
|
||||
"FIC.DGO",
|
||||
"LAV.DGO",
|
||||
"MAI.DGO",
|
||||
"MIS.DGO",
|
||||
"OGR.DGO",
|
||||
"ROB.DGO",
|
||||
"ROL.DGO",
|
||||
"SNO.DGO",
|
||||
"SUB.DGO",
|
||||
"SUN.DGO",
|
||||
"SWA.DGO",
|
||||
"TIT.DGO",
|
||||
"TRA.DGO",
|
||||
"VI1.DGO",
|
||||
"VI2.DGO",
|
||||
"VI3.DGO"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -533,6 +533,12 @@
|
||||
"draw-inline-array-tfrag",
|
||||
"stats-tfrag-asm",
|
||||
"time-of-day-interp-colors-scratch"
|
||||
],
|
||||
|
||||
// there are some missing textures. I don't know what the game actually does here.
|
||||
// the format for entries is [level, tpage, index]
|
||||
"missing_textures": [
|
||||
["finalboss", 1419, 3]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace decompiler {
|
||||
class LinkedWordReader {
|
||||
public:
|
||||
explicit LinkedWordReader(const std::vector<LinkedWord>* words) : m_words(words) {}
|
||||
const std::string& get_type_tag() {
|
||||
if (m_words->at(m_offset).kind == LinkedWord::TYPE_PTR) {
|
||||
auto& result = m_words->at(m_offset).symbol_name;
|
||||
std::string get_type_tag() {
|
||||
if (m_words->at(m_offset).kind() == LinkedWord::TYPE_PTR) {
|
||||
auto result = m_words->at(m_offset).symbol_name();
|
||||
m_offset++;
|
||||
return result;
|
||||
} else {
|
||||
@@ -26,7 +26,7 @@ class LinkedWordReader {
|
||||
T get_word() {
|
||||
static_assert(sizeof(T) == 4, "size of word in get_word");
|
||||
T result;
|
||||
assert(m_words->at(m_offset).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(m_words->at(m_offset).kind() == LinkedWord::PLAIN_DATA);
|
||||
memcpy(&result, &m_words->at(m_offset).data, 4);
|
||||
m_offset++;
|
||||
return result;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "TextureDB.h"
|
||||
|
||||
#include "common/util/assert.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
void TextureDB::add_texture(u32 tpage,
|
||||
u32 texid,
|
||||
const std::vector<u32>& data,
|
||||
u16 w,
|
||||
u16 h,
|
||||
const std::string& tex_name,
|
||||
const std::string& tpage_name) {
|
||||
auto existing_tpage_name = tpage_names.find(tpage);
|
||||
if (existing_tpage_name == tpage_names.end()) {
|
||||
tpage_names[tpage] = tpage_name;
|
||||
} else {
|
||||
assert(existing_tpage_name->second == tpage_name);
|
||||
}
|
||||
|
||||
u32 combo_id = (tpage << 16) | texid;
|
||||
auto existing_tex = textures.find(combo_id);
|
||||
if (existing_tex != textures.end()) {
|
||||
assert(existing_tex->second.name == tex_name);
|
||||
assert(existing_tex->second.w == w);
|
||||
assert(existing_tex->second.h == h);
|
||||
assert(existing_tex->second.rgba_bytes == data);
|
||||
assert(existing_tex->second.page == tpage);
|
||||
} else {
|
||||
auto& new_tex = textures[combo_id];
|
||||
new_tex.rgba_bytes = data;
|
||||
new_tex.name = tex_name;
|
||||
new_tex.w = w;
|
||||
new_tex.h = h;
|
||||
new_tex.page = tpage;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace decompiler {
|
||||
struct TextureDB {
|
||||
struct TextureData {
|
||||
u16 w, h;
|
||||
std::string name;
|
||||
u32 page;
|
||||
std::vector<u32> rgba_bytes;
|
||||
};
|
||||
|
||||
std::unordered_map<u32, TextureData> textures;
|
||||
std::unordered_map<u32, std::string> tpage_names;
|
||||
|
||||
void add_texture(u32 tpage,
|
||||
u32 texid,
|
||||
const std::vector<u32>& data,
|
||||
u16 w,
|
||||
u16 h,
|
||||
const std::string& tex_name,
|
||||
const std::string& tpage_name);
|
||||
};
|
||||
} // namespace decompiler
|
||||
@@ -20,27 +20,26 @@ DirTpageResult process_dir_tpages(ObjectFileData& data) {
|
||||
|
||||
int word_idx = 0;
|
||||
// first is type
|
||||
assert(words.at(word_idx).kind == LinkedWord::TYPE_PTR);
|
||||
assert(words.at(word_idx).symbol_name == "texture-page-dir");
|
||||
assert(words.at(word_idx).kind() == LinkedWord::TYPE_PTR);
|
||||
assert(words.at(word_idx).symbol_name() == "texture-page-dir");
|
||||
word_idx++;
|
||||
// next is length
|
||||
assert(words.at(word_idx).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(word_idx).kind() == LinkedWord::PLAIN_DATA);
|
||||
int dir_length = words.at(word_idx).data;
|
||||
fmt::print("length: {}\n", dir_length);
|
||||
word_idx++;
|
||||
|
||||
for (int i = 0; i < dir_length; i++) {
|
||||
assert(words.at(word_idx).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(word_idx).kind() == LinkedWord::PLAIN_DATA);
|
||||
u32 entry = words.at(word_idx).data;
|
||||
assert((entry & 0xffff7000) == 0); // 7 checks for sign bit.
|
||||
word_idx++;
|
||||
result.lengths.push_back(entry & 0xffff);
|
||||
|
||||
assert(words.at(word_idx).kind == LinkedWord::SYM_PTR);
|
||||
assert(words.at(word_idx).symbol_name == "#f");
|
||||
assert(words.at(word_idx).kind() == LinkedWord::SYM_PTR);
|
||||
assert(words.at(word_idx).symbol_name() == "#f");
|
||||
word_idx++;
|
||||
assert(words.at(word_idx).kind == LinkedWord::SYM_PTR);
|
||||
assert(words.at(word_idx).symbol_name == "#f");
|
||||
assert(words.at(word_idx).kind() == LinkedWord::SYM_PTR);
|
||||
assert(words.at(word_idx).symbol_name() == "#f");
|
||||
word_idx++;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@ namespace {
|
||||
template <typename T>
|
||||
T get_word(const LinkedWord& word) {
|
||||
T result;
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word.kind() == LinkedWord::PLAIN_DATA);
|
||||
static_assert(sizeof(T) == 4, "bad get_word size");
|
||||
memcpy(&result, &word.data, 4);
|
||||
return result;
|
||||
}
|
||||
|
||||
DecompilerLabel get_label(ObjectFileData& data, const LinkedWord& word) {
|
||||
assert(word.kind == LinkedWord::PTR);
|
||||
return data.linked_data.labels.at(word.label_id);
|
||||
assert(word.kind() == LinkedWord::PTR);
|
||||
return data.linked_data.labels.at(word.label_id());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -51,8 +51,8 @@ GameTextResult process_game_text(ObjectFileData& data) {
|
||||
int offset = 0;
|
||||
|
||||
// type tage for game-text-info
|
||||
if (words.at(offset).kind != LinkedWord::TYPE_PTR ||
|
||||
words.front().symbol_name != "game-text-info") {
|
||||
if (words.at(offset).kind() != LinkedWord::TYPE_PTR ||
|
||||
words.front().symbol_name() != "game-text-info") {
|
||||
assert(false);
|
||||
}
|
||||
read_words.at(offset)++;
|
||||
|
||||
+22
-10
@@ -223,23 +223,23 @@ int label_to_word_offset(DecompilerLabel l, bool basic) {
|
||||
}
|
||||
|
||||
std::string get_type_tag(const LinkedWord& word) {
|
||||
assert(word.kind == LinkedWord::TYPE_PTR);
|
||||
return word.symbol_name;
|
||||
assert(word.kind() == LinkedWord::TYPE_PTR);
|
||||
return word.symbol_name();
|
||||
}
|
||||
|
||||
bool is_type_tag(const LinkedWord& word, const std::string& type) {
|
||||
return word.kind == LinkedWord::TYPE_PTR && word.symbol_name == type;
|
||||
return word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == type;
|
||||
}
|
||||
|
||||
DecompilerLabel get_label(ObjectFileData& data, const LinkedWord& word) {
|
||||
assert(word.kind == LinkedWord::PTR);
|
||||
return data.linked_data.labels.at(word.label_id);
|
||||
assert(word.kind() == LinkedWord::PTR);
|
||||
return data.linked_data.labels.at(word.label_id());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_word(const LinkedWord& word) {
|
||||
T result;
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(word.kind() == LinkedWord::PLAIN_DATA);
|
||||
static_assert(sizeof(T) == 4, "bad get_word size");
|
||||
memcpy(&result, &word.data, 4);
|
||||
return result;
|
||||
@@ -385,8 +385,8 @@ TexturePage read_texture_page(ObjectFileData& data,
|
||||
}
|
||||
|
||||
for (int i = 0; i < tpage.length; i++) {
|
||||
if (words.at(offset).kind == LinkedWord::SYM_PTR) {
|
||||
if (words.at(offset).symbol_name == "#f") {
|
||||
if (words.at(offset).kind() == LinkedWord::SYM_PTR) {
|
||||
if (words.at(offset).symbol_name() == "#f") {
|
||||
tpage.data.emplace_back();
|
||||
Texture null_tex;
|
||||
null_tex.null_texture = true;
|
||||
@@ -415,7 +415,7 @@ TexturePage read_texture_page(ObjectFileData& data,
|
||||
* Process a texture page.
|
||||
* TODO - document
|
||||
*/
|
||||
TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
|
||||
TPageResultStats stats;
|
||||
auto& words = data.linked_data.words_by_seg.at(0);
|
||||
|
||||
@@ -464,7 +464,9 @@ TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
}
|
||||
|
||||
// get all textures in the tpage
|
||||
for (auto& tex : texture_page.textures) {
|
||||
for (u32 tex_id = 0; tex_id < texture_page.textures.size(); tex_id++) {
|
||||
auto& tex = texture_page.textures.at(tex_id);
|
||||
|
||||
// I think these get inserted for CLUTs, but I'm not sure.
|
||||
if (tex.null_texture) {
|
||||
continue;
|
||||
@@ -520,6 +522,8 @@ TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
|
||||
data.name_in_dgo, tex.name, tex.w, tex.h),
|
||||
out.data(), tex.w, tex.h);
|
||||
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
|
||||
texture_page.name);
|
||||
stats.successful_textures++;
|
||||
} else if (tex.psm == int(PSM::PSMT8) && tex.clutpsm == int(CPSM::PSMCT16)) {
|
||||
// will store output pixels, rgba (8888)
|
||||
@@ -566,6 +570,8 @@ TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
|
||||
data.name_in_dgo, tex.name, tex.w, tex.h),
|
||||
out.data(), tex.w, tex.h);
|
||||
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
|
||||
texture_page.name);
|
||||
stats.successful_textures++;
|
||||
} else if (tex.psm == int(PSM::PSMCT16) && tex.clutpsm == 0) {
|
||||
// not a clut.
|
||||
@@ -594,6 +600,8 @@ TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
|
||||
data.name_in_dgo, tex.name, tex.w, tex.h),
|
||||
out.data(), tex.w, tex.h);
|
||||
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
|
||||
texture_page.name);
|
||||
stats.successful_textures++;
|
||||
} else if (tex.psm == int(PSM::PSMT4) && tex.clutpsm == int(CPSM::PSMCT16)) {
|
||||
// will store output pixels, rgba (8888)
|
||||
@@ -638,6 +646,8 @@ TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
|
||||
data.name_in_dgo, tex.name, tex.w, tex.h),
|
||||
out.data(), tex.w, tex.h);
|
||||
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
|
||||
texture_page.name);
|
||||
stats.successful_textures++;
|
||||
} else if (tex.psm == int(PSM::PSMT4) && tex.clutpsm == int(CPSM::PSMCT32)) {
|
||||
// will store output pixels, rgba (8888)
|
||||
@@ -682,6 +692,8 @@ TPageResultStats process_tpage(ObjectFileData& data) {
|
||||
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
|
||||
data.name_in_dgo, tex.name, tex.w, tex.h),
|
||||
out.data(), tex.w, tex.h);
|
||||
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
|
||||
texture_page.name);
|
||||
stats.successful_textures++;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
|
||||
namespace decompiler {
|
||||
struct ObjectFileData;
|
||||
|
||||
@@ -9,5 +11,5 @@ struct TPageResultStats {
|
||||
int num_px = 0;
|
||||
};
|
||||
|
||||
TPageResultStats process_tpage(ObjectFileData& data);
|
||||
TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db);
|
||||
} // namespace decompiler
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "tools/level_tools/goal_data_reader.h"
|
||||
#include "tools/level_tools/Error.h"
|
||||
#include "decompiler/util/goal_data_reader.h"
|
||||
#include "decompiler/util/Error.h"
|
||||
#include "common/dma/dma.h"
|
||||
|
||||
namespace level_tools {
|
||||
@@ -24,7 +24,7 @@ void Vector::read_from_file(Ref ref) {
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const auto& word = ref.data->words_by_seg.at(ref.seg).at((ref.byte_offset / 4) + i);
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("vector didn't get plain data.");
|
||||
}
|
||||
memcpy(data + i, &word.data, 4);
|
||||
@@ -148,8 +148,8 @@ void TFragmentDebugData::read_from_file(Ref ref,
|
||||
auto& words = ref.data->words_by_seg.at(ref.seg);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto& word = words.at((ref.byte_offset / 4) + i);
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("debug data word type: {}\n", (int)word.kind);
|
||||
if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("debug data word type: {}\n", (int)word.kind());
|
||||
}
|
||||
data[i] = word.data;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ void TFragmentDebugData::read_from_file(Ref ref,
|
||||
stats->total_tfrag_tris += tris;
|
||||
|
||||
auto& debug_word = words.at(4 + (ref.byte_offset / 4));
|
||||
if (debug_word.kind != decompiler::LinkedWord::PLAIN_DATA || debug_word.data != 0) {
|
||||
if (debug_word.kind() != decompiler::LinkedWord::PLAIN_DATA || debug_word.data != 0) {
|
||||
throw Error("got debug word.");
|
||||
}
|
||||
}
|
||||
@@ -184,8 +184,8 @@ u32 deref_u32(const Ref& ref, int word_offset) {
|
||||
throw Error("deref_u32 bad alignment");
|
||||
}
|
||||
const auto& word = ref.data->words_by_seg.at(ref.seg).at(word_offset + (ref.byte_offset / 4));
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("deref_u32 bad kind");
|
||||
if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("deref_u32 bad kind: {}", (int)word.kind());
|
||||
}
|
||||
return word.data;
|
||||
}
|
||||
@@ -286,6 +286,16 @@ void tfrag_debug_print_unpack(Ref start, int qwc_total) {
|
||||
fmt::print("-------------------------------------------\n");
|
||||
}
|
||||
|
||||
std::vector<u8> read_dma_chain(Ref& start, u32 qwc) {
|
||||
std::vector<u8> result;
|
||||
result.resize(qwc * 16);
|
||||
for (u32 word = 0; word < qwc * 4; word++) {
|
||||
u32 x = deref_u32(start, word);
|
||||
memcpy(result.data() + (word * 4), &x, 4);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TFragment::read_from_file(TypedRef ref,
|
||||
const decompiler::DecompilerTypeSystem& dts,
|
||||
DrawStats* stats) {
|
||||
@@ -298,7 +308,7 @@ void TFragment::read_from_file(TypedRef ref,
|
||||
|
||||
auto dma_field = get_field_ref(ref, "dma-qwc", dts);
|
||||
auto& dma_word = ref.ref.data->words_by_seg.at(dma_field.seg).at(dma_field.byte_offset / 4);
|
||||
if (dma_word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
if (dma_word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("bad dma qwc word");
|
||||
}
|
||||
memcpy(dma_qwc, &dma_word.data, 4);
|
||||
@@ -313,13 +323,12 @@ void TFragment::read_from_file(TypedRef ref,
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto& word = dma_slot.data->words_by_seg.at(dma_slot.seg).at((dma_slot.byte_offset / 4));
|
||||
dmas[i].ref = deref_label(dma_slot);
|
||||
dmas[i].label_name = dma_slot.data->labels.at(word.label_id).name;
|
||||
dmas[i].label_name = dma_slot.data->labels.at(word.label_id()).name;
|
||||
|
||||
dma_slot.byte_offset += 4;
|
||||
}
|
||||
|
||||
if (stats->debug_print_dma_data) {
|
||||
fmt::print("qwc's: {} {} {} {}\n", dma_qwc[0], dma_qwc[1], dma_qwc[2], dma_qwc[3]);
|
||||
// first, common
|
||||
fmt::print("DMA COMMON {}, {} qwc:\n", dmas[0].label_name, dma_qwc[0]);
|
||||
tfrag_debug_print_unpack(dmas[0].ref, dma_qwc[0]);
|
||||
@@ -335,22 +344,50 @@ void TFragment::read_from_file(TypedRef ref,
|
||||
// next "level1"
|
||||
fmt::print("DMA LEVEL1 {}, {} qwc:\n", dmas[2].label_name, dma_qwc[2]);
|
||||
tfrag_debug_print_unpack(dmas[2].ref, dma_qwc[2]);
|
||||
|
||||
fmt::print("qwc's: {} {} {} {}\n", dma_qwc[0], dma_qwc[1], dma_qwc[2], dma_qwc[3]);
|
||||
}
|
||||
|
||||
// todo dma
|
||||
// todo dma
|
||||
// todo dma
|
||||
|
||||
// todo shader
|
||||
num_shaders = read_plain_data_field<u8>(ref, "num-shaders", dts);
|
||||
num_base_colors = read_plain_data_field<u8>(ref, "num-base-colors", dts);
|
||||
num_level0_colors = read_plain_data_field<u8>(ref, "num-level0-colors", dts);
|
||||
num_level1_colors = read_plain_data_field<u8>(ref, "num-level1-colors", dts);
|
||||
num_shaders = read_plain_data_field<u8>(ref, "num-shaders", dts);
|
||||
color_offset = read_plain_data_field<u8>(ref, "color-offset", dts);
|
||||
color_count = read_plain_data_field<u8>(ref, "color-count", dts);
|
||||
dma_base = read_dma_chain(dmas[1].ref, dma_qwc[1]);
|
||||
|
||||
if (num_level0_colors > 0 || num_level1_colors > 0) {
|
||||
// if we're base only, this has junk in it, and it wouldn't be used anyway.
|
||||
dma_common_and_level0 = read_dma_chain(dmas[0].ref, std::max(dma_qwc[3], dma_qwc[0]));
|
||||
}
|
||||
|
||||
dma_level1 = read_dma_chain(dmas[2].ref, dma_qwc[2]);
|
||||
|
||||
// color indices
|
||||
int num_actual_colors = std::max(num_base_colors, std::max(num_level0_colors, num_level1_colors));
|
||||
int num_colors = ((num_actual_colors + 3) / 4) * 4;
|
||||
// each color is a u64 (4x u16 indices)
|
||||
// color_indices.resize(4 * num_colors);
|
||||
|
||||
// 24 = 12 * u32
|
||||
auto color_idx_start = deref_label(get_field_ref(ref, "color-indices", dts));
|
||||
for (int i = 0; i < num_colors / 4; i++) {
|
||||
u32 low = deref_u32(color_idx_start, i * 2);
|
||||
u32 high = deref_u32(color_idx_start, i * 2 + 1);
|
||||
color_indices.push_back(low & 0xffff);
|
||||
color_indices.push_back(low >> 16);
|
||||
color_indices.push_back(high & 0xffff);
|
||||
color_indices.push_back(high >> 16);
|
||||
}
|
||||
assert((int)color_indices.size() == num_colors);
|
||||
|
||||
// todo shader
|
||||
|
||||
assert(num_colors / 4 == color_count);
|
||||
// fmt::print("colors: {} {} {}\n", num_base_colors, num_level0_colors, num_level1_colors);
|
||||
assert(read_plain_data_field<u8>(ref, "pad0", dts) == 0);
|
||||
assert(read_plain_data_field<u8>(ref, "pad1", dts) == 0);
|
||||
// todo generic
|
||||
assert(read_plain_data_field<u32>(ref, "generic-u32", dts) == 0);
|
||||
}
|
||||
|
||||
std::string TFragment::print(const PrintSettings& settings, int indent) const {
|
||||
@@ -687,6 +724,17 @@ void DrawableTreeTfrag::read_from_file(TypedRef ref,
|
||||
throw Error("misaligned data array");
|
||||
}
|
||||
|
||||
auto palette = deref_label(get_field_ref(ref, "time-of-day-pal", dts));
|
||||
time_of_day.width = deref_u32(palette, 0);
|
||||
|
||||
assert(time_of_day.width == 8);
|
||||
time_of_day.height = deref_u32(palette, 1);
|
||||
time_of_day.pad = deref_u32(palette, 2);
|
||||
assert(time_of_day.pad == 0);
|
||||
for (int i = 0; i < int(8 * time_of_day.height); i++) {
|
||||
time_of_day.colors.push_back(deref_u32(palette, 3 + i));
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < length; idx++) {
|
||||
Ref array_slot_ref = data_ref;
|
||||
array_slot_ref.byte_offset += idx * 4;
|
||||
@@ -785,7 +833,7 @@ void PrototypeBucketTie::read_from_file(TypedRef ref,
|
||||
auto next_slot = get_field_ref(ref, "next", dts);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto& word = ref.ref.data->words_by_seg.at(next_slot.seg).at(i + (next_slot.byte_offset / 4));
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("bad word type in PrototypeBucketTie next");
|
||||
}
|
||||
next[i] = word.data;
|
||||
@@ -794,7 +842,7 @@ void PrototypeBucketTie::read_from_file(TypedRef ref,
|
||||
auto count_slot = get_field_ref(ref, "count", dts);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
auto& word = ref.ref.data->words_by_seg.at(count_slot.seg).at(i + (count_slot.byte_offset / 4));
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("bad word type in PrototypeBucketTie count");
|
||||
}
|
||||
memcpy(count + 2 * i, &word.data, 4);
|
||||
@@ -804,7 +852,7 @@ void PrototypeBucketTie::read_from_file(TypedRef ref,
|
||||
u8* block_start = (u8*)generic_count;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
auto& word = ref.ref.data->words_by_seg.at(block_slot.seg).at(i + (block_slot.byte_offset / 4));
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
throw Error("bad word type in PrototypeBucketTie slot");
|
||||
}
|
||||
memcpy(block_start + 4 * i, &word.data, 4);
|
||||
@@ -995,6 +1043,24 @@ std::unique_ptr<DrawableTree> make_drawable_tree(TypedRef ref,
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (ref.type->get_name() == "drawable-tree-lowres-tfrag") {
|
||||
auto tree = std::make_unique<DrawableTreeLowresTfrag>();
|
||||
tree->read_from_file(ref, dts, stats);
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (ref.type->get_name() == "drawable-tree-dirt-tfrag") {
|
||||
auto tree = std::make_unique<DrawableTreeDirtTfrag>();
|
||||
tree->read_from_file(ref, dts, stats);
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (ref.type->get_name() == "drawable-tree-ice-tfrag") {
|
||||
auto tree = std::make_unique<DrawableTreeIceTfrag>();
|
||||
tree->read_from_file(ref, dts, stats);
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (ref.type->get_name() == "drawable-tree-instance-tie") {
|
||||
auto tree = std::make_unique<DrawableTreeInstanceTie>();
|
||||
tree->read_from_file(ref, dts, stats);
|
||||
@@ -1064,6 +1130,22 @@ void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file,
|
||||
|
||||
drawable_tree_array.read_from_file(
|
||||
get_and_check_ref_to_basic(ref, "drawable-trees", "drawable-tree-array", dts), dts, stats);
|
||||
|
||||
texture_remap_table.clear();
|
||||
|
||||
s32 tex_remap_len = read_plain_data_field<s32>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string BspHeader::print(const PrintSettings& settings) const {
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "tools/level_tools/goal_data_reader.h"
|
||||
#include "decompiler/util/goal_data_reader.h"
|
||||
|
||||
namespace decompiler {
|
||||
class LinkedObjectFile;
|
||||
@@ -126,8 +126,12 @@ struct TFragment : public Drawable {
|
||||
// u32 color_indices; // 12 - 16 (or colors?)
|
||||
Vector bsphere; // 16 - 32
|
||||
// dma common/level0 // 32 - 36
|
||||
std::vector<u8> dma_common_and_level0;
|
||||
// dma base // 36 - 40
|
||||
std::vector<u8> dma_base;
|
||||
// dma level 1 // 40 - 44
|
||||
std::vector<u8> dma_level1;
|
||||
std::vector<u16> color_indices;
|
||||
u8 dma_qwc[4];
|
||||
// shader // 48 - 52
|
||||
u8 num_shaders; // 52
|
||||
@@ -226,6 +230,13 @@ struct DrawableInlineArrayUnknown : public DrawableInlineArray {
|
||||
|
||||
struct DrawableTree : public Drawable {};
|
||||
|
||||
struct TimeOfDayPalette {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 pad;
|
||||
std::vector<u32> colors;
|
||||
};
|
||||
|
||||
struct DrawableTreeTfrag : public DrawableTree {
|
||||
void read_from_file(TypedRef ref,
|
||||
const decompiler::DecompilerTypeSystem& dts,
|
||||
@@ -236,6 +247,7 @@ struct DrawableTreeTfrag : public DrawableTree {
|
||||
s16 id;
|
||||
s16 length;
|
||||
// todo time of day stuff
|
||||
TimeOfDayPalette time_of_day;
|
||||
Vector bsphere;
|
||||
|
||||
std::vector<std::unique_ptr<DrawableInlineArray>> arrays;
|
||||
@@ -336,6 +348,18 @@ struct DrawableTreeTransTfrag : public DrawableTreeTfrag {
|
||||
std::string my_type() const override { return "drawable-tree-trans-tfrag"; }
|
||||
};
|
||||
|
||||
struct DrawableTreeLowresTfrag : public DrawableTreeTfrag {
|
||||
std::string my_type() const override { return "drawable-tree-lowres-tfrag"; }
|
||||
};
|
||||
|
||||
struct DrawableTreeDirtTfrag : public DrawableTreeTfrag {
|
||||
std::string my_type() const override { return "drawable-tree-dirt-tfrag"; }
|
||||
};
|
||||
|
||||
struct DrawableTreeIceTfrag : public DrawableTreeTfrag {
|
||||
std::string my_type() const override { return "drawable-tree-ice-tfrag"; }
|
||||
};
|
||||
|
||||
struct DrawableTreeUnknown : public DrawableTree {
|
||||
void read_from_file(TypedRef ref,
|
||||
const decompiler::DecompilerTypeSystem& dts,
|
||||
@@ -356,6 +380,11 @@ struct DrawableTreeArray {
|
||||
std::vector<std::unique_ptr<DrawableTree>> trees;
|
||||
};
|
||||
|
||||
struct TextureRemap {
|
||||
u32 original_texid;
|
||||
u32 new_texid;
|
||||
};
|
||||
|
||||
struct BspHeader {
|
||||
// (info file-info :offset 4)
|
||||
FileInfo file_info;
|
||||
@@ -377,6 +406,7 @@ struct BspHeader {
|
||||
// ;; some osrt of texture remapping info
|
||||
// (texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
// (texture-remap-table-len int32 :offset-assert 56)
|
||||
std::vector<TextureRemap> texture_remap_table;
|
||||
//
|
||||
// (texture-ids (pointer texture-id) :offset-assert 60)
|
||||
// (texture-page-count int32 :offset-assert 64)
|
||||
@@ -405,7 +435,8 @@ struct BspHeader {
|
||||
// (unk-data-8 uint32 55 :offset-assert 180)
|
||||
|
||||
void read_from_file(const decompiler::LinkedObjectFile& file,
|
||||
const decompiler::DecompilerTypeSystem& dts, DrawStats* stats);
|
||||
const decompiler::DecompilerTypeSystem& dts,
|
||||
DrawStats* stats);
|
||||
|
||||
std::string print(const PrintSettings& settings) const;
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <set>
|
||||
|
||||
#include "extract_level.h"
|
||||
#include "decompiler/level_extractor/BspHeader.h"
|
||||
#include "decompiler/level_extractor/extract_tfrag.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
/*!
|
||||
* Look through files in a DGO and find the bsp-header file (the level)
|
||||
*/
|
||||
ObjectFileRecord get_bsp_file(const std::vector<ObjectFileRecord>& records) {
|
||||
ObjectFileRecord result;
|
||||
bool found = false;
|
||||
for (auto& file : records) {
|
||||
if (file.name.length() > 4 && file.name.substr(file.name.length() - 4) == "-vis") {
|
||||
assert(!found);
|
||||
found = true;
|
||||
result = file;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Make sure a file is a valid bsp-header.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
void extract_from_level(ObjectFileDB& db,
|
||||
TextureDB& tex_db,
|
||||
const std::string& dgo_name,
|
||||
const DecompileHacks& hacks) {
|
||||
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;
|
||||
}
|
||||
|
||||
auto bsp_rec = get_bsp_file(db.obj_files_by_dgo.at(dgo_name));
|
||||
std::string level_name = bsp_rec.name.substr(0, bsp_rec.name.length() - 4);
|
||||
|
||||
fmt::print("Processing level {} ({})\n", dgo_name, level_name);
|
||||
auto& bsp_file = db.lookup_record(bsp_rec);
|
||||
bool ok = is_valid_bsp(bsp_file.linked_data);
|
||||
assert(ok);
|
||||
|
||||
level_tools::DrawStats draw_stats;
|
||||
// draw_stats.debug_print_dma_data = true;
|
||||
level_tools::BspHeader bsp_header;
|
||||
bsp_header.read_from_file(bsp_file.linked_data, db.dts, &draw_stats);
|
||||
assert((int)bsp_header.drawable_tree_array.trees.size() == bsp_header.drawable_tree_array.length);
|
||||
|
||||
const std::set<std::string> tfrag_trees = {
|
||||
"drawable-tree-tfrag", "drawable-tree-trans-tfrag", "drawable-tree-dirt-tfrag",
|
||||
"drawable-tree-ice-tfrag", "drawable-tree-lowres-tfrag", "drawable-tree-lowres-trans-tfrag"};
|
||||
int i = 0;
|
||||
tfrag3::Level tfrag_level;
|
||||
|
||||
for (auto& draw_tree : bsp_header.drawable_tree_array.trees) {
|
||||
if (tfrag_trees.count(draw_tree->my_type())) {
|
||||
auto as_tfrag_tree = dynamic_cast<level_tools::DrawableTreeTfrag*>(draw_tree.get());
|
||||
fmt::print(" extracting tree {}\n", draw_tree->my_type());
|
||||
assert(as_tfrag_tree);
|
||||
std::vector<std::pair<int, int>> expected_missing_textures;
|
||||
auto it = hacks.missing_textures_by_level.find(level_name);
|
||||
if (it != hacks.missing_textures_by_level.end()) {
|
||||
expected_missing_textures = it->second;
|
||||
}
|
||||
extract_tfrag(as_tfrag_tree, fmt::format("{}-{}", dgo_name, i++),
|
||||
bsp_header.texture_remap_table, tex_db, expected_missing_textures, tfrag_level);
|
||||
} else {
|
||||
fmt::print(" unsupported tree {}\n", draw_tree->my_type());
|
||||
tfrag_level.trees.emplace_back();
|
||||
tfrag_level.trees.back().kind = tfrag3::TFragmentTreeKind::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
Serializer ser;
|
||||
tfrag_level.serialize(ser);
|
||||
file_util::write_binary_file(file_util::get_file_path({fmt::format(
|
||||
"assets/{}.fr3", dgo_name.substr(0, dgo_name.length() - 4))}),
|
||||
ser.get_save_result().first, ser.get_save_result().second);
|
||||
}
|
||||
} // namespace decompiler
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/math/Vector.h"
|
||||
#include "decompiler/ObjectFile/ObjectFileDB.h"
|
||||
|
||||
namespace decompiler {
|
||||
void extract_from_level(ObjectFileDB& db,
|
||||
TextureDB& tex_db,
|
||||
const std::string& dgo_name,
|
||||
const DecompileHacks& hacks);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "decompiler/level_extractor/BspHeader.h"
|
||||
#include "common/math/Vector.h"
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
// the different "kinds" of tfrag. The actual renderers are almost identical and the only different
|
||||
// is in GS setup (alpha blending) and in how the closest object is used.
|
||||
|
||||
// This is the actual tree data, minus the tfrags themselves.
|
||||
struct VisNodeTree {
|
||||
std::vector<tfrag3::VisNode> vis_nodes;
|
||||
u16 first_child_node = 0;
|
||||
u16 last_child_node = 0;
|
||||
u16 first_root = 0;
|
||||
u16 num_roots = 0;
|
||||
bool only_children = false;
|
||||
};
|
||||
|
||||
// The final result
|
||||
struct ExtractedTFragmentTree {
|
||||
// TFragmentKind kind = TFragmentKind::INVALID;
|
||||
VisNodeTree vis_nodes;
|
||||
|
||||
u16 num_tfrags = 0;
|
||||
u16 tfrag_base_idx = 0;
|
||||
};
|
||||
|
||||
// will pool textures with others already in out.
|
||||
void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
|
||||
const std::string& debug_name,
|
||||
const std::vector<level_tools::TextureRemap>& map,
|
||||
const TextureDB& tex_db,
|
||||
const std::vector<std::pair<int, int>>& expected_missing_textures,
|
||||
tfrag3::Level& out);
|
||||
|
||||
} // namespace decompiler
|
||||
@@ -0,0 +1,748 @@
|
||||
|
||||
|
||||
// The actual "tfrag" type. This only lives on the EE and gets converted to DMA data.
|
||||
// some of the DMA data is pointers to chains that live in the static level data.
|
||||
// other is colors that are looked up from the palette (on the EE!) then thrown in the
|
||||
// double-buffered frame global buffer.
|
||||
|
||||
(deftype tfragment (drawable)
|
||||
(
|
||||
(color-index uint16 :offset 6)
|
||||
(debug-data tfragment-debug-data :offset 8)
|
||||
(color-indices uint32 :offset 12)
|
||||
(colors uint32 :offset 12)
|
||||
(dma-chain uint32 3 :offset-assert 32)
|
||||
(dma-common uint32 :offset 32)
|
||||
(dma-level-0 uint32 :offset 32)
|
||||
(dma-base uint32 :offset 36)
|
||||
(dma-level-1 uint32 :offset 40)
|
||||
(dma-qwc uint8 4 :offset 44)
|
||||
(shader (inline-array adgif-shader) :offset 48)
|
||||
(num-shaders uint8 :offset 52)
|
||||
(num-base-colors uint8 :offset 53)
|
||||
(num-level0-colors uint8 :offset 54)
|
||||
(num-level1-colors uint8 :offset 55)
|
||||
(color-offset uint8 :offset 56)
|
||||
(color-count uint8 :offset 57)
|
||||
(pad0 uint8 :offset 58)
|
||||
(pad1 uint8 :offset 59)
|
||||
(generic generic-tfragment :offset-assert 60)
|
||||
(generic-u32 uint32 :offset 60) ;; added
|
||||
)
|
||||
:method-count-assert 18
|
||||
:size-assert #x40
|
||||
:flag-assert #x1200000040
|
||||
)
|
||||
|
||||
// This is the temp/debug structure used for the EE code
|
||||
|
||||
(deftype tfrag-work (structure)
|
||||
((base-tmpl dma-packet :inline :offset-assert 0)
|
||||
(level-0-tmpl dma-packet :inline :offset-assert 16)
|
||||
(common-tmpl dma-packet :inline :offset-assert 32)
|
||||
(level-1-tmpl dma-packet :inline :offset-assert 48)
|
||||
(color-tmpl dma-packet :inline :offset-assert 64)
|
||||
(frag-dists vector :inline :offset-assert 80)
|
||||
(max-dist vector :inline :offset-assert 96)
|
||||
(min-dist vector :inline :offset-assert 112)
|
||||
(color-ptr vector4w :inline :offset-assert 128)
|
||||
(tr-stat-tfrag tr-stat :offset-assert 144)
|
||||
(tr-stat-tfrag-near tr-stat :offset-assert 148)
|
||||
(vu1-enable-tfrag int32 :offset-assert 152)
|
||||
(vu1-enable-tfrag-near int32 :offset-assert 156)
|
||||
(cur-vis-bits uint32 :offset-assert 160)
|
||||
(end-vis-bits uint32 :offset-assert 164)
|
||||
(src-ptr uint32 :offset-assert 168)
|
||||
(last-call uint32 :offset-assert 172)
|
||||
(dma-buffer basic :offset-assert 176)
|
||||
(test-id uint32 :offset-assert 180)
|
||||
(wait-from-spr uint32 :offset-assert 184)
|
||||
(wait-to-spr uint32 :offset-assert 188)
|
||||
(near-wait-from-spr uint32 :offset-assert 192)
|
||||
(near-wait-to-spr uint32 :offset-assert 196)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #xc8
|
||||
:flag-assert #x9000000c8
|
||||
)
|
||||
*/
|
||||
|
||||
// base vifs:
|
||||
// ??
|
||||
// t3
|
||||
|
||||
// l0:
|
||||
// ??
|
||||
// t3
|
||||
|
||||
// common:
|
||||
// ??
|
||||
// t3
|
||||
|
||||
// color
|
||||
// ??
|
||||
// 12 sb color-offset
|
||||
// 14 sb num colors, 4 aligned.
|
||||
|
||||
/*
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; .function draw-inline-array-tfrag
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; there's two double-buffered spad buffers + culling data
|
||||
|
||||
;; arguments:
|
||||
;; a0 - occlusion cull list (on spad)
|
||||
;; a1 - tfrags
|
||||
;; a2 - num tfrags
|
||||
;; a3 - dma buf
|
||||
|
||||
;; constants:
|
||||
;; t0 = *tfrag-work*
|
||||
;; t1 = SPR FROM
|
||||
;; t2 = 0x14000000 ??
|
||||
;; t4 = SPR TO
|
||||
|
||||
;; vars:
|
||||
;; v1 = ptr to dma buffer data
|
||||
;; t5 = SPR BUFFER 0 (tfrags)
|
||||
;; a3 = SPR BUFFER 1
|
||||
;; t3 = ?? (init to 0)
|
||||
;; t6 = spr buffer 1 use (qwc)
|
||||
|
||||
;; vf3 = frag-dists
|
||||
;; vf1 = (1, 1, 1, 1)
|
||||
;; vf2 = bsphere
|
||||
B0:
|
||||
L40:
|
||||
TFRAG INITIALIZE
|
||||
;; set up constants
|
||||
daddiu sp, sp, -128
|
||||
sd ra, 0(sp)
|
||||
sq s0, 16(sp)
|
||||
sq s1, 32(sp)
|
||||
sq s2, 48(sp)
|
||||
sq s3, 64(sp)
|
||||
sq s4, 80(sp)
|
||||
sq s5, 96(sp)
|
||||
sq gp, 112(sp)
|
||||
lui t2, 5120 = (0x14000000), a constant (mscal)
|
||||
lw v1, 4(a3) (-> dma-buf base)
|
||||
lui t3, 4096 = (0x10000000)
|
||||
lui t1, 4096 = (0x10000000)
|
||||
sync.l
|
||||
cache dxwbin v1, 0
|
||||
sync.l
|
||||
cache dxwbin v1, 1
|
||||
sync.l
|
||||
lw t0, *tfrag-work*(s7)
|
||||
ori t4, t3, 54272 = (0x1000D400) SPR TO
|
||||
ori t1, t1, 53248 = (0x1000D000) SPR FROM
|
||||
lui t5, 28672 = (0x70000000)
|
||||
lqc2 vf3, 80(t0) = (-> *tfrag-work* frag-dists)
|
||||
sw a3, 176(t0) (set! (-> *tfrag-work* dma-buffer) dma-ptr)
|
||||
ori a3, t5, 2064 setup buffer 1
|
||||
addiu t3, r0, 0 t3 = 0
|
||||
ori t5, t5, 1040 setup buffer 0
|
||||
vmaxw.xyzw vf1, vf0, vf0 vf1 = (1, 1, 1, 1)
|
||||
lh t7, 0(a0) vis cull load
|
||||
lqc2 vf4, 96(t0) max-dist
|
||||
addiu a1, a1, -4 remove basic offset
|
||||
addiu t6, r0, 0 t6 = 0
|
||||
or ra, a3, r0 ra = SPAD BUFFER 1
|
||||
|
||||
|
||||
SKIP TO FIRST VISIBLE
|
||||
;; skips ahead until we find some visible tfrags.
|
||||
B1:
|
||||
L41:
|
||||
bne t7, r0, L42
|
||||
sll r0, r0, 0
|
||||
|
||||
B2:
|
||||
addiu a0, a0, 2 ;; + 16 bits in the vis list
|
||||
addiu a1, a1, 1024 ;; 16 * 0x40 = 1024 bytes in tfrag list
|
||||
daddiu a2, a2, -16 ;; num tfrags -= 16
|
||||
lh t7, 0(a0) ;; next vis
|
||||
blez a2, L69_CLEANUP ;; no visible tfrags, abort!
|
||||
sll r0, r0, 0
|
||||
|
||||
B3:
|
||||
beq r0, r0, L41 ;; keep looking
|
||||
sll r0, r0, 0
|
||||
|
||||
WAIT_FOR_PREV_DMA
|
||||
;; waits for any previously running spad dma transfer to end
|
||||
B4:
|
||||
L42:
|
||||
lw t7, 0(t4)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi t7, t7, 256
|
||||
sll r0, r0, 0
|
||||
bne t7, r0, L42
|
||||
sll r0, r0, 0
|
||||
|
||||
INIT_FIRST_SPAD_TO
|
||||
;; initializes the first scratchpad upload of tfrags
|
||||
B5:
|
||||
sw a1, 16(t4) ;; madr = a1
|
||||
xori t7, t5, 1024 ;; t7 = upload addr of tfrags double buffer
|
||||
sw t7, 128(t4) ;; sadr
|
||||
addiu t7, r0, 64 ;; 64 qw = 16 tfrags
|
||||
sw t7, 32(t4) ;; qwc
|
||||
addiu t7, r0, 256 ;; go
|
||||
sw t7, 0(t4) ;; go!
|
||||
sll r0, r0, 0
|
||||
|
||||
TFRAG MAIN LOOP TOP
|
||||
B6:
|
||||
L43:
|
||||
or gp, a0, r0 ;; gp = temp addr of vis list
|
||||
xori t5, t5, 1024 ;; toggle to addr of upload tfrags
|
||||
daddiu a0, a0, 2 ;; advance vis list ptr (16 tfrags)
|
||||
or t9, a0, r0 ;; t9 = temp addr of next vis list
|
||||
or t8, t5, r0 ;; t8 = tfrags to use
|
||||
|
||||
;; next, let's find next block of visible tfrags so we can start it's dma early
|
||||
daddiu t7, a2, -16 ;; t7 = tfrags left after this loop
|
||||
bgtz t7, L45 ;; if we have them left, jump
|
||||
lh t7, 0(a0) ;; and load their vis
|
||||
|
||||
B7:
|
||||
beq r0, r0, L48 ;; none left, skip dma kickoff.
|
||||
sll r0, r0, 0
|
||||
|
||||
B8:
|
||||
L44:
|
||||
daddiu a2, a2, -16 ;; skip invisible block (dec tfrag counter)
|
||||
addiu a0, a0, 2 ;; increment vis list
|
||||
blez a2, L48 ;; did we get to the end of the tfrag list?
|
||||
lh t7, 0(a0) ;; check vis again.
|
||||
|
||||
B9:
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
B10:
|
||||
L45:
|
||||
beq t7, r0, L44 ;; we have tfrags left. if 0, they are all hidden, so loop
|
||||
addiu a1, a1, 1024 ;; and advance upload pointer (not done at all yet)
|
||||
|
||||
;; we reach here if we have tfrags left after this block.
|
||||
;; so let's upload the next ones to the scratchpad so they are ready by next time.
|
||||
B11:
|
||||
L46:
|
||||
lw t7, 0(t4) ;; make sure to-spr is done
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi t7, t7, 256
|
||||
sll r0, r0, 0
|
||||
beq t7, r0, L47
|
||||
sll r0, r0, 0
|
||||
|
||||
B12:
|
||||
sll r0, r0, 0
|
||||
lw t7, 188(t0)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
daddiu t7, t7, 1 ;; counting how many times we wait
|
||||
sll r0, r0, 0
|
||||
sw t7, 188(t0)
|
||||
beq r0, r0, L46
|
||||
sll r0, r0, 0
|
||||
|
||||
B13:
|
||||
L47:
|
||||
sw a1, 16(t4) ;; start the to!
|
||||
xori t7, t5, 1024
|
||||
sw t7, 128(t4)
|
||||
addiu t7, r0, 64
|
||||
sw t7, 32(t4)
|
||||
addiu t7, r0, 256
|
||||
beq r0, r0, L49 ;; skip ahead
|
||||
sw t7, 0(t4)
|
||||
|
||||
;; only reach here if we dont have any more spr to's
|
||||
;; still need to sync the to for the block we're about to process
|
||||
B14:
|
||||
L48:
|
||||
lw t7, 0(t4)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi t7, t7, 256
|
||||
sll r0, r0, 0
|
||||
beq t7, r0, L49
|
||||
sll r0, r0, 0
|
||||
|
||||
B15:
|
||||
sll r0, r0, 0
|
||||
lw t7, 188(t0)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
daddiu t7, t7, 1
|
||||
sll r0, r0, 0
|
||||
sw t7, 188(t0)
|
||||
beq r0, r0, L48
|
||||
sll r0, r0, 0
|
||||
|
||||
;; common op start
|
||||
;; at this point:
|
||||
;; t8 is our spad tfrag buffer, with 16 tfrags. at least 1 is visible.
|
||||
;; gp is our vis-list pointer
|
||||
;; we run through this loop 2x, each time doing 8 tfrags.
|
||||
|
||||
B16:
|
||||
L49:
|
||||
lb t7, 0(gp) ;; load first 8 frag vis bits
|
||||
addiu gp, gp, 1 ;; inc vis bit ptr.
|
||||
sll r0, r0, 0
|
||||
sw gp, 160(t0) ;; store cur-vis-bits
|
||||
bne t7, r0, L50 ;; are any visible in the first 8?
|
||||
sw t9, 164(t0) ;; set end-vis-bits (why?)
|
||||
|
||||
B17: ;; none are visible
|
||||
daddiu a2, a2, -8 ;; dec tfrags
|
||||
addiu t8, t8, 512 ;; skip tfrags
|
||||
beq r0, r0, L65 ;; skip ahead!
|
||||
sll r0, r0, 0
|
||||
|
||||
B18:
|
||||
L50:
|
||||
addiu t9, r0, 128 ;; vis mask init (gets shifted in each run of the 8-loop)
|
||||
lqc2 vf2, 16(t8) ;; bsphere load
|
||||
B19:
|
||||
L51:
|
||||
daddiu gp, t6, -124 ;; are we full of stuff in buffer 1?
|
||||
sll r0, r0, 0
|
||||
blez gp, L54
|
||||
sll r0, r0, 0
|
||||
|
||||
B20:
|
||||
L52:
|
||||
lw ra, 0(t1) ;; wait for spr-from
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi ra, ra, 256
|
||||
sll r0, r0, 0
|
||||
beq ra, r0, L53
|
||||
sll r0, r0, 0
|
||||
|
||||
B21:
|
||||
sll r0, r0, 0 ;; count it
|
||||
lw ra, 184(t0)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
daddiu ra, ra, 1
|
||||
sll r0, r0, 0
|
||||
sw ra, 184(t0)
|
||||
beq r0, r0, L52
|
||||
sll r0, r0, 0
|
||||
|
||||
B22:
|
||||
L53:
|
||||
sw a3, 128(t1) ;; kick off the next spr-from.
|
||||
xori a3, a3, 6144
|
||||
sw v1, 16(t1) ;; to the dma-buf
|
||||
sll ra, t6, 4
|
||||
addu v1, v1, ra ;; add qwc
|
||||
or ra, a3, r0 ;; ra is the spad-side dma buffer to write to
|
||||
sw t6, 32(t1) ;; qwc
|
||||
addiu t6, r0, 256
|
||||
sw t6, 0(t1) ;; go!
|
||||
addiu t6, r0, 0 ;; reset use.
|
||||
|
||||
;; actually building dma.
|
||||
B23:
|
||||
L54:
|
||||
and gp, t7, t9 ;; vis check
|
||||
vmulax.xyzw acc, vf16, vf2 ;; plane ?
|
||||
beq gp, r0, L64_8loop_reject ;; vis check failed, reject!
|
||||
lwu gp, 36(t8) ;; DMA BASE (chain) -------------
|
||||
|
||||
B24:
|
||||
vmadday.xyzw acc, vf17, vf2 ;; plane
|
||||
lbu s5, 45(t8) ;; DMA QWC1
|
||||
vmaddaz.xyzw acc, vf18, vf2 ;; plane
|
||||
sw gp, 4(t0) ;; base tmpl set addr
|
||||
vmsubaw.xyzw acc, vf19, vf0 ;; plane
|
||||
sh s5, 0(t0) ;; base tmpl set qwc
|
||||
vmaddw.xyzw vf5, vf1, vf2 ;; plane
|
||||
lwu gp, 32(t8) ;; DMA level0 -------------
|
||||
vmulaw.xyzw acc, vf27, vf0 ;; camrot
|
||||
lbu s5, 47(t8) ;; DMA QWC3
|
||||
vmaddax.xyzw acc, vf24, vf2 ;; camrot
|
||||
sw gp, 20(t0) ;; l0 tmpl set addr
|
||||
vmadday.xyzw acc, vf25, vf2 ;; camrot
|
||||
sh s5, 16(t0) ;; l0 tmpl set qwc
|
||||
vmaddaz.xyzw acc, vf26, vf2 ;; camrot
|
||||
lwu gp, 32(t8) ;; DMA common --------------
|
||||
qmfc2.i s5, vf5 ;; plane
|
||||
lbu s4, 44(t8) ;; DMA QWC0
|
||||
vmaddw.xyzw vf6, vf1, vf2 ;; ??
|
||||
sw gp, 36(t0) ;; common tmpl set addr
|
||||
vmsubw.xyzw vf8, vf1, vf2 ;; ??
|
||||
sh s4, 32(t0) ;; common tmpl set qwc
|
||||
pcgtw s5, r0, s5 ;; plane check
|
||||
lwu gp, 40(t8) ;; DMA level1 -------------
|
||||
ppach s5, r0, s5 ;; plane check
|
||||
lbu s4, 46(t8) ;; DMA QWC2
|
||||
vaddz.xyzw vf6, vf3, vf6 ;; dist
|
||||
sw gp, 52(t0) ;; l1 tmpl addr
|
||||
vaddz.xyzw vf7, vf3, vf8 ;; dist
|
||||
sw t3, 12(t0) ;; !!! set a vif on base, 0 on the first round, at least.
|
||||
bne s5, r0, L63_8loop_reject_tog_vis
|
||||
sh s4, 48(t0) ;; l1 tmpl qwc
|
||||
|
||||
B25:
|
||||
vmini.xyzw vf4, vf4, vf8 ;; max dist
|
||||
sw t3, 28(t0) ;; !!! set a vif on l0
|
||||
sll r0, r0, 0
|
||||
lbu s5, 53(t8) ;; s5 = num-base-colors
|
||||
qmfc2.i gp, vf6 ;; dist
|
||||
sw t3, 44(t0) ;; !!! set a vif on common
|
||||
qmfc2.i s3, vf7 ;; dist
|
||||
lbu s4, 56(t8) ;; s4 = color-offset
|
||||
pcgtw s2, r0, gp ;; dist
|
||||
lw gp, 12(t8) ;; gp = colors-indices
|
||||
pcgtw s3, r0, s3 ;; dist
|
||||
sb s4, 76(t0) ;; store color-offset
|
||||
pinteh s4, s2, s3 ;; dist
|
||||
lbu s2, 54(t8) ;; s2 = num-level0-colors
|
||||
ppacb s3, r0, s4 ;; dist
|
||||
lbu s1, 55(t8) ;; s1 = num-level1-colors
|
||||
beq s3, r0, L56 ;; jump if dist fails?
|
||||
dsrl32 s4, s3, 8 ;; s4 is the level or something?
|
||||
|
||||
B26:
|
||||
beq s2, r0, L56 ;; if we have no level0 colors, use base
|
||||
sll r0, r0, 0
|
||||
|
||||
B27:
|
||||
beq s1, r0, L55 ;; if we have no level1 colors, use level0
|
||||
dsrl s5, s3, 16
|
||||
|
||||
B28:
|
||||
beq s5, r0, L55 ;; possible l1 skip based on lod
|
||||
dsrl32 s5, s3, 24
|
||||
|
||||
B29:
|
||||
bne s5, r0, L64_8loop_reject ;; possible all skip based on lod.
|
||||
addiu s5, s1, 3 ;; s5 = num-level1-colors + 3
|
||||
|
||||
B30: ;; level 1 color setup
|
||||
sra s4, s5, 2 ;; s4 = (num_color + 3) >> 4
|
||||
or s5, s1, r0 ;; s5 = (num_color)
|
||||
sll t3, s4, 2 ;; t3 = num colors, 4 aligned
|
||||
sh s4, 64(t0) ;; color-tmpl qwc.
|
||||
sll r0, r0, 0
|
||||
sb t3, 78(t0) ;; vif store
|
||||
daddiu t6, t6, 3 ;; use 3 qw's of global dma.
|
||||
lq s2, 32(t0) ;; load the common-tmpl!
|
||||
sll r0, r0, 0
|
||||
lq s1, 48(t0) ;; load the l1 tmpl!
|
||||
sll r0, r0, 0
|
||||
lq t3, 64(t0) ;; load the color tmpl!
|
||||
sq s2, 0(ra) ;; store the common!
|
||||
sll r0, r0, 0
|
||||
sq s1, 16(ra) ;; store the l1!
|
||||
dsrl32 s2, s3, 16
|
||||
sq t3, 32(ra) ;; store the color
|
||||
daddiu ra, ra, 48 ;; advance the dma buffer pointer
|
||||
bne s2, r0, L57
|
||||
ori t3, t2, 18 ;; is this.. program 18?
|
||||
|
||||
B31:
|
||||
dsrl32 t3, s3, 8
|
||||
sll r0, r0, 0
|
||||
bne t3, r0, L57
|
||||
ori t3, t2, 16
|
||||
|
||||
B32:
|
||||
beq r0, r0, L57
|
||||
ori t3, t2, 14
|
||||
|
||||
B33:
|
||||
L55:
|
||||
bne s4, r0, L64_8loop_reject
|
||||
addiu s5, s2, 3 ;; l0 colors + 3
|
||||
|
||||
B34: l0 color setup
|
||||
sra s4, s5, 2 ;; >> 2
|
||||
or s5, s2, r0 ;; s5 = qwc
|
||||
sll t3, s4, 2 ;; << 2
|
||||
sh s4, 64(t0) ;; color-tmpl qwc
|
||||
sll r0, r0, 0
|
||||
sb t3, 78(t0) ;; vif store for unpack??
|
||||
daddiu t6, t6, 2 ;; only 2 qw's
|
||||
lq s2, 16(t0) ;; l0 tmpl
|
||||
sll r0, r0, 0
|
||||
lq t3, 64(t0) ;; color tmp
|
||||
sq s2, 0(ra)
|
||||
dsrl s3, s3, 8
|
||||
sq t3, 16(ra)
|
||||
daddiu ra, ra, 32
|
||||
bne s3, r0, L57
|
||||
ori t3, t2, 10
|
||||
|
||||
B35:
|
||||
beq r0, r0, L57
|
||||
ori t3, t2, 8
|
||||
|
||||
B36:
|
||||
L56:
|
||||
bne s4, r0, L64_8loop_reject
|
||||
addiu s4, s5, 3 ;; base colors + 3
|
||||
|
||||
B37:
|
||||
sra s4, s4, 2
|
||||
sll r0, r0, 0
|
||||
sll t3, s4, 2
|
||||
sh s4, 64(t0)
|
||||
sll r0, r0, 0
|
||||
sb t3, 78(t0)
|
||||
ori t3, t2, 6
|
||||
lq s3, 0(t0) ;; base
|
||||
daddiu t6, t6, 2
|
||||
lq s2, 64(t0) ;; color
|
||||
sq s3, 0(ra)
|
||||
sll r0, r0, 0
|
||||
sq s2, 16(ra)
|
||||
daddiu ra, ra, 32
|
||||
|
||||
;; END of the color setup
|
||||
B38:
|
||||
L57: ;; another opportunity to do some spad swappin
|
||||
addiu s3, r0, 127 ;; s3 = 127
|
||||
daddu s2, t6, s4 ;; s2 = dma-use + color qwc
|
||||
dsubu s3, s3, s2
|
||||
sll r0, r0, 0
|
||||
bgez s3, L60
|
||||
sll r0, r0, 0
|
||||
|
||||
B39:
|
||||
L58:
|
||||
lw ra, 0(t1)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi ra, ra, 256
|
||||
sll r0, r0, 0
|
||||
beq ra, r0, L59
|
||||
sll r0, r0, 0
|
||||
|
||||
B40:
|
||||
sll r0, r0, 0
|
||||
lw ra, 184(t0)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
daddiu ra, ra, 1
|
||||
sll r0, r0, 0
|
||||
sw ra, 184(t0)
|
||||
beq r0, r0, L58
|
||||
sll r0, r0, 0
|
||||
|
||||
B41:
|
||||
L59:
|
||||
sw a3, 128(t1)
|
||||
xori a3, a3, 6144
|
||||
sw v1, 16(t1)
|
||||
sll ra, t6, 4
|
||||
addu v1, v1, ra
|
||||
or ra, a3, r0
|
||||
sw t6, 32(t1)
|
||||
addiu t6, r0, 256
|
||||
sw t6, 0(t1)
|
||||
addiu t6, r0, 0
|
||||
B42:
|
||||
|
||||
L60:
|
||||
daddu t6, t6, s4 ;; add color imm's to dma buffer length
|
||||
sw t8, 168(t0) ;; back up tfrag... not enough regs
|
||||
ld s4, 0(gp) ;; load color-indices (u64 = u16 x 4)
|
||||
daddiu t8, gp, 8 ;; inc colors ptr
|
||||
daddiu gp, s5, -4 ;; gp is color counter. we're using the rounded up to 4 color count.
|
||||
lq s5, 128(t0) ;; color-ptr x4
|
||||
pextlh s4, r0, s4 ;; expand packed u16's to u32's
|
||||
mfc1 r0, f31 ;; nop
|
||||
paddw s2, s4, s5 ;; add to color pointers
|
||||
mfc1 r0, f31
|
||||
lw s4, 0(s2) ;; s4 = colors[0]
|
||||
dsra32 s3, s2, 0
|
||||
lw s3, 0(s3) ;; s5 = colors[1]
|
||||
pcpyud s1, s2, s2
|
||||
lw s2, 0(s1) ;; s2 = colors[2]
|
||||
dsra32 s1, s1, 0
|
||||
blez gp, L62
|
||||
lw s1, 0(s1) ;; s1 = colors[3]
|
||||
|
||||
B43:
|
||||
L61:
|
||||
ld s0, 0(t8)
|
||||
daddiu ra, ra, 16
|
||||
daddiu t8, t8, 8
|
||||
sw s4, -16(ra)
|
||||
daddiu gp, gp, -4
|
||||
sw s3, -12(ra)
|
||||
pextlh s4, r0, s0
|
||||
sw s2, -8(ra)
|
||||
paddw s2, s4, s5
|
||||
sw s1, -4(ra)
|
||||
lw s4, 0(s2)
|
||||
dsra32 s3, s2, 0
|
||||
lw s3, 0(s3)
|
||||
pcpyud s1, s2, s2
|
||||
lw s2, 0(s1)
|
||||
dsra32 s1, s1, 0
|
||||
bgtz gp, L61
|
||||
lw s1, 0(s1)
|
||||
|
||||
B44:
|
||||
L62:
|
||||
daddiu ra, ra, 16
|
||||
lw t8, 168(t0)
|
||||
sll r0, r0, 0
|
||||
sw s4, -16(ra)
|
||||
sll r0, r0, 0
|
||||
sw s3, -12(ra)
|
||||
sll r0, r0, 0
|
||||
sw s2, -8(ra)
|
||||
sll r0, r0, 0
|
||||
sw s1, -4(ra)
|
||||
B45:
|
||||
L63_8loop_reject_tog_vis:
|
||||
xor t7, t7, t9 ;; update vis
|
||||
sll r0, r0, 0
|
||||
B46:
|
||||
L64_8loop_reject:
|
||||
daddiu t8, t8, 64
|
||||
srl t9, t9, 1
|
||||
addiu a2, a2, -1
|
||||
sll r0, r0, 0
|
||||
bne t9, r0, L51
|
||||
lqc2 vf2, 16(t8)
|
||||
|
||||
B47:
|
||||
L65:
|
||||
sll r0, r0, 0
|
||||
lw gp, 160(t0)
|
||||
sll r0, r0, 0
|
||||
lw t9, 164(t0)
|
||||
bne gp, t9, L49
|
||||
sb t7, -1(gp)
|
||||
|
||||
B48:
|
||||
bgtz a2, L43_MAIN_LOOP_TOP
|
||||
sll r0, r0, 0
|
||||
|
||||
B49:
|
||||
beq t6, r0, L68
|
||||
sll r0, r0, 0
|
||||
|
||||
B50:
|
||||
L66:
|
||||
lw a0, 0(t1)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi a0, a0, 256
|
||||
sll r0, r0, 0
|
||||
beq a0, r0, L67
|
||||
sll r0, r0, 0
|
||||
|
||||
B51:
|
||||
sll r0, r0, 0
|
||||
lw a0, 184(t0)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
daddiu a0, a0, 1
|
||||
sll r0, r0, 0
|
||||
sw a0, 184(t0)
|
||||
beq r0, r0, L66
|
||||
sll r0, r0, 0
|
||||
|
||||
B52:
|
||||
L67:
|
||||
sw a3, 128(t1)
|
||||
xori a0, a3, 6144
|
||||
sw v1, 16(t1)
|
||||
sll a1, t6, 4
|
||||
addu v1, v1, a1
|
||||
or a0, a0, r0
|
||||
sw t6, 32(t1)
|
||||
addiu a0, r0, 256
|
||||
sw a0, 0(t1)
|
||||
addiu a0, r0, 0
|
||||
B53:
|
||||
L68:
|
||||
lw a0, 0(t1)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
andi a0, a0, 256
|
||||
sll r0, r0, 0
|
||||
beq a0, r0, L69_CLEANUP
|
||||
sll r0, r0, 0
|
||||
|
||||
B54:
|
||||
sll r0, r0, 0
|
||||
lw a0, 184(t0)
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
daddiu a0, a0, 1
|
||||
sll r0, r0, 0
|
||||
sw a0, 184(t0)
|
||||
beq r0, r0, L68
|
||||
sll r0, r0, 0
|
||||
|
||||
B55:
|
||||
L69_CLEANUP:
|
||||
lw a0, 176(t0)
|
||||
sll r0, r0, 0
|
||||
sw t3, 172(t0)
|
||||
sll r0, r0, 0
|
||||
sqc2 vf4, 112(t0)
|
||||
sll r0, r0, 0
|
||||
sw v1, 4(a0)
|
||||
sll r0, r0, 0
|
||||
or v0, r0, r0
|
||||
ld ra, 0(sp)
|
||||
lq gp, 112(sp)
|
||||
lq s5, 96(sp)
|
||||
lq s4, 80(sp)
|
||||
lq s3, 64(sp)
|
||||
lq s2, 48(sp)
|
||||
lq s1, 32(sp)
|
||||
lq s0, 16(sp)
|
||||
jr ra
|
||||
daddiu sp, sp, 128
|
||||
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
|
||||
|
||||
Notes on the VU program
|
||||
vi03 is a pointer to an "address book" - a sequence of addresses
|
||||
vi02 contains addresses in this book
|
||||
from these xyw are loaded for vf28 (v3-32, with 2, 1)
|
||||
xy are floats. w is address of next vertex data.
|
||||
|
||||
vi08 is a pointer to adgifs?
|
||||
vi09 is a pointer to some data like [vi12, ?, ?, vi13] ??
|
||||
|
||||
vi12 counter, started negative?
|
||||
vi13 is adgif offset?
|
||||
|
||||
vi04 is a pointer to tri-data:
|
||||
- vertex (w = 128.0?)
|
||||
- ?? (vf20)
|
||||
+27
-2
@@ -7,8 +7,13 @@
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/versions.h"
|
||||
#include "decompiler/data/streamed_audio.h"
|
||||
#include "decompiler/level_extractor/extract_level.h"
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
#include "common/util/os.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
fmt::print("[Mem] Size of linked word: {}\n", sizeof(decompiler::LinkedWord));
|
||||
fmt::print("[Mem] Top of main: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
using namespace decompiler;
|
||||
lg::set_file(file_util::get_file_path({"log/decompiler.txt"}));
|
||||
lg::set_file_level(lg::level::info);
|
||||
@@ -24,13 +29,14 @@ int main(int argc, char** argv) {
|
||||
printf("Usage: decompiler <config_file> <in_folder> <out_folder>\n");
|
||||
return 1;
|
||||
}
|
||||
fmt::print("[Mem] After init: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
// collect all files to process
|
||||
Config config;
|
||||
try {
|
||||
config = read_config_file(argv[1]);
|
||||
} catch (const std::exception& e) {
|
||||
lg::error("Failed to parse config");
|
||||
lg::error("Failed to parse config: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -52,10 +58,14 @@ int main(int argc, char** argv) {
|
||||
|
||||
file_util::create_dir_if_needed(out_folder);
|
||||
|
||||
fmt::print("[Mem] After config read: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
// build file database
|
||||
lg::info("Setting up object file DB...");
|
||||
ObjectFileDB db(dgos, config.obj_file_name_map_file, objs, strs, config);
|
||||
|
||||
fmt::print("[Mem] After DB setup: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
// write out DGO file info
|
||||
file_util::write_text_file(file_util::combine_path(out_folder, "dgo.txt"),
|
||||
db.generate_dgo_listing());
|
||||
@@ -72,8 +82,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
// process files (required for all analysis)
|
||||
db.process_link_data(config);
|
||||
fmt::print("[Mem] After link data: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
db.find_code(config);
|
||||
db.process_labels();
|
||||
fmt::print("[Mem] After code: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
// print disassembly
|
||||
if (config.disassemble_code || config.disassemble_data) {
|
||||
@@ -93,6 +105,8 @@ int main(int argc, char** argv) {
|
||||
db.analyze_functions_ir2(out_folder, config, {});
|
||||
}
|
||||
|
||||
fmt::print("[Mem] After decomp: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
// write out all symbols
|
||||
file_util::write_text_file(file_util::combine_path(out_folder, "all-syms.gc"),
|
||||
db.dts.dump_symbol_types());
|
||||
@@ -113,13 +127,18 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("[Mem] After text: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
decompiler::TextureDB tex_db;
|
||||
if (config.process_tpages) {
|
||||
auto result = db.process_tpages();
|
||||
auto result = db.process_tpages(tex_db);
|
||||
if (!result.empty()) {
|
||||
file_util::write_text_file(file_util::get_file_path({"assets", "tpage-dir.txt"}), result);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("[Mem] After textures: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
if (config.process_game_count) {
|
||||
auto result = db.process_game_count_file();
|
||||
if (!result.empty()) {
|
||||
@@ -127,6 +146,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& lev : config.levels_to_extract) {
|
||||
extract_from_level(db, tex_db, lev, config.hacks);
|
||||
}
|
||||
|
||||
fmt::print("[Mem] After extraction: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
if (!config.audio_dir_file_name.empty()) {
|
||||
process_streamed_audio(config.audio_dir_file_name, config.streamed_audio_file_names);
|
||||
}
|
||||
|
||||
@@ -116,8 +116,7 @@ ParsedData parse_data(const std::string& str) {
|
||||
// try as .type
|
||||
if (first_thing == ".type") {
|
||||
LinkedWord word(0);
|
||||
word.kind = LinkedWord::TYPE_PTR;
|
||||
word.symbol_name = line;
|
||||
word.set_to_symbol(LinkedWord::TYPE_PTR, line);
|
||||
result.words.push_back(word);
|
||||
byte_offset += 4;
|
||||
continue;
|
||||
@@ -125,8 +124,7 @@ ParsedData parse_data(const std::string& str) {
|
||||
|
||||
if (first_thing == ".symbol") {
|
||||
LinkedWord word(0);
|
||||
word.kind = LinkedWord::SYM_PTR;
|
||||
word.symbol_name = line;
|
||||
word.set_to_symbol(LinkedWord::SYM_PTR, line);
|
||||
result.words.push_back(word);
|
||||
byte_offset += 4;
|
||||
continue;
|
||||
@@ -137,7 +135,7 @@ ParsedData parse_data(const std::string& str) {
|
||||
throw std::runtime_error("Got something after .empty-list, this is not allowed");
|
||||
}
|
||||
LinkedWord word(0);
|
||||
word.kind = LinkedWord::EMPTY_PTR;
|
||||
word.set_to_empty_ptr();
|
||||
result.words.push_back(word);
|
||||
byte_offset += 4;
|
||||
continue;
|
||||
@@ -151,8 +149,7 @@ ParsedData parse_data(const std::string& str) {
|
||||
result.labels.emplace_back();
|
||||
}
|
||||
LinkedWord word(0);
|
||||
word.kind = LinkedWord::PTR;
|
||||
word.label_id = l.idx;
|
||||
word.set_to_pointer(LinkedWord::PTR, l.idx);
|
||||
result.words.push_back(word);
|
||||
byte_offset += 4;
|
||||
continue;
|
||||
@@ -160,7 +157,7 @@ ParsedData parse_data(const std::string& str) {
|
||||
auto val = std::stoull(line, nullptr, 16);
|
||||
assert(val <= UINT32_MAX);
|
||||
LinkedWord word(val);
|
||||
word.kind = LinkedWord::PLAIN_DATA;
|
||||
word.set_to_plain_data();
|
||||
result.words.push_back(word);
|
||||
byte_offset += 4;
|
||||
continue;
|
||||
@@ -197,18 +194,18 @@ std::string ParsedData::print() const {
|
||||
|
||||
// print word
|
||||
auto& word = words.at(idx);
|
||||
switch (word.kind) {
|
||||
switch (word.kind()) {
|
||||
case LinkedWord::PLAIN_DATA:
|
||||
result += fmt::format(" .word 0x{:x}\n", word.data);
|
||||
break;
|
||||
case LinkedWord::PTR:
|
||||
result += fmt::format(" .word {}\n", labels.at(word.label_id).name);
|
||||
result += fmt::format(" .word {}\n", labels.at(word.label_id()).name);
|
||||
break;
|
||||
case LinkedWord::SYM_PTR:
|
||||
result += fmt::format(" .symbol {}\n", word.symbol_name);
|
||||
result += fmt::format(" .symbol {}\n", word.symbol_name());
|
||||
break;
|
||||
case LinkedWord::TYPE_PTR:
|
||||
result += fmt::format(" .type {}\n", word.symbol_name);
|
||||
result += fmt::format(" .type {}\n", word.symbol_name());
|
||||
break;
|
||||
case LinkedWord::EMPTY_PTR:
|
||||
result += " .empty-list\n";
|
||||
|
||||
@@ -116,18 +116,18 @@ std::optional<TypeSpec> get_type_of_label(const DecompilerLabel& label,
|
||||
if ((label.offset % 8) == 4) {
|
||||
auto type_ptr_word_idx = (label.offset / 4) - 1;
|
||||
auto& type_ptr = words.at(label.target_segment).at(type_ptr_word_idx);
|
||||
if (type_ptr.kind != LinkedWord::TYPE_PTR) {
|
||||
if (type_ptr.kind() != LinkedWord::TYPE_PTR) {
|
||||
return {};
|
||||
}
|
||||
if (type_ptr.symbol_name == "array") {
|
||||
if (type_ptr.symbol_name() == "array") {
|
||||
auto content_type_ptr_word_idx = type_ptr_word_idx + 3;
|
||||
auto& content_type_ptr = words.at(label.target_segment).at(content_type_ptr_word_idx);
|
||||
if (content_type_ptr.kind != LinkedWord::TYPE_PTR) {
|
||||
if (content_type_ptr.kind() != LinkedWord::TYPE_PTR) {
|
||||
return {};
|
||||
}
|
||||
return TypeSpec("array", {TypeSpec(content_type_ptr.symbol_name)});
|
||||
return TypeSpec("array", {TypeSpec(content_type_ptr.symbol_name())});
|
||||
}
|
||||
return TypeSpec(type_ptr.symbol_name);
|
||||
return TypeSpec(type_ptr.symbol_name());
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
@@ -211,14 +211,14 @@ goos::Object decompile_string_at_label(const DecompilerLabel& label,
|
||||
assert(label.offset >= 4);
|
||||
|
||||
const auto& type_ptr = words.at(label.target_segment).at((label.offset - 4) / 4);
|
||||
if (type_ptr.kind != LinkedWord::TYPE_PTR) {
|
||||
if (type_ptr.kind() != LinkedWord::TYPE_PTR) {
|
||||
throw std::runtime_error(fmt::format(
|
||||
"Cannot get string at label {}, word before is not a type pointer.", label.name));
|
||||
}
|
||||
|
||||
if (type_ptr.symbol_name != "string") {
|
||||
if (type_ptr.symbol_name() != "string") {
|
||||
throw std::runtime_error(fmt::format("Cannot get string at label {}, type pointer is for a {}.",
|
||||
label.name, type_ptr.symbol_name));
|
||||
label.name, type_ptr.symbol_name()));
|
||||
}
|
||||
|
||||
std::string result;
|
||||
@@ -230,7 +230,7 @@ goos::Object decompile_string_at_label(const DecompilerLabel& label,
|
||||
fmt::format("Cannot get string at label {}, not enough room", label.name));
|
||||
}
|
||||
const LinkedWord& size_word = words.at(label.target_segment).at(word_idx + 1);
|
||||
if (size_word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (size_word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
// sometimes an array of string pointer triggers this!
|
||||
throw std::runtime_error(
|
||||
fmt::format("Cannot get string at label {}, size is not plain data.", label.name));
|
||||
@@ -241,7 +241,7 @@ goos::Object decompile_string_at_label(const DecompilerLabel& label,
|
||||
int word_offset = word_idx + 2 + (i / 4);
|
||||
int byte_offset = i % 4;
|
||||
auto& word = words.at(label.target_segment).at(word_offset);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Cannot get string at label {}, character is not plain data.", label.name));
|
||||
}
|
||||
@@ -269,7 +269,7 @@ goos::Object decompile_value_array(const TypeSpec& elt_type,
|
||||
std::vector<u8> elt_bytes;
|
||||
for (int j = start; j < end; j++) {
|
||||
auto& word = obj_words.at(j / 4);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
throw std::runtime_error("Got bad word in kind in array of values");
|
||||
}
|
||||
elt_bytes.push_back(word.get_byte(j % 4));
|
||||
@@ -343,11 +343,11 @@ goos::Object decomp_ref_to_inline_array_guess_size(
|
||||
// we expect that to be a label:
|
||||
assert((field_location % 4) == 0);
|
||||
auto pointer_to_data = words.at(field_location / 4);
|
||||
assert(pointer_to_data.kind == LinkedWord::PTR);
|
||||
assert(pointer_to_data.kind() == LinkedWord::PTR);
|
||||
|
||||
// the data shouldn't have any labels in the middle of it, so we can find the end of the array
|
||||
// by searching for the label after the start label.
|
||||
const auto& start_label = labels.at(pointer_to_data.label_id);
|
||||
const auto& start_label = labels.at(pointer_to_data.label_id());
|
||||
int end_label_idx =
|
||||
index_of_closest_following_label_in_segment(start_label.offset, my_seg, labels);
|
||||
|
||||
@@ -381,7 +381,7 @@ goos::Object decomp_ref_to_inline_array_guess_size(
|
||||
int padding_end = end_offset;
|
||||
for (int pad_byte_idx = padding_start; pad_byte_idx < padding_end; pad_byte_idx++) {
|
||||
auto& word = all_words.at(my_seg).at(pad_byte_idx / 4);
|
||||
switch (word.kind) {
|
||||
switch (word.kind()) {
|
||||
case LinkedWord::PLAIN_DATA:
|
||||
assert(word.get_byte(pad_byte_idx) == 0);
|
||||
break;
|
||||
@@ -489,13 +489,13 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
|
||||
if (is_basic) {
|
||||
const auto& word = words.at(label.target_segment).at((offset_location / 4));
|
||||
if (word.kind != LinkedWord::TYPE_PTR) {
|
||||
if (word.kind() != LinkedWord::TYPE_PTR) {
|
||||
throw std::runtime_error("Basic does not start with type pointer");
|
||||
}
|
||||
|
||||
if (word.symbol_name != actual_type.base_type()) {
|
||||
if (word.symbol_name() != actual_type.base_type()) {
|
||||
// we can specify a more specific type.
|
||||
auto got_type = TypeSpec(word.symbol_name);
|
||||
auto got_type = TypeSpec(word.symbol_name());
|
||||
if (ts.tc(actual_type, got_type)) {
|
||||
actual_type = got_type;
|
||||
|
||||
@@ -511,7 +511,7 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Basic has the wrong type pointer, got {} expected {} at label {}:{}",
|
||||
word.symbol_name, actual_type.base_type(), label.name, label.offset));
|
||||
word.symbol_name(), actual_type.base_type(), label.name, label.offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -552,7 +552,7 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
std::vector<int> field_status_per_byte;
|
||||
for (int i = 0; i < word_count; i++) {
|
||||
auto& w = obj_words.at(i);
|
||||
switch (w.kind) {
|
||||
switch (w.kind()) {
|
||||
case LinkedWord::TYPE_PTR:
|
||||
case LinkedWord::PTR:
|
||||
case LinkedWord::SYM_PTR:
|
||||
@@ -585,11 +585,11 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
if (is_basic && idx == 0) {
|
||||
assert(field.name() == "type" && field.offset() == 0);
|
||||
auto& word = obj_words.at(0);
|
||||
if (word.kind != LinkedWord::TYPE_PTR) {
|
||||
if (word.kind() != LinkedWord::TYPE_PTR) {
|
||||
throw std::runtime_error("Basic does not start with type pointer");
|
||||
}
|
||||
|
||||
if (word.symbol_name != actual_type.base_type()) {
|
||||
if (word.symbol_name() != actual_type.base_type()) {
|
||||
// the check above should have caught this.
|
||||
assert(false);
|
||||
}
|
||||
@@ -682,20 +682,20 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
field_start, ts, words, file));
|
||||
} else {
|
||||
if (field.type().base_type() == "pointer") {
|
||||
if (obj_words.at(field_start / 4).kind != LinkedWord::SYM_PTR) {
|
||||
if (obj_words.at(field_start / 4).kind() != LinkedWord::SYM_PTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (obj_words.at(field_start / 4).symbol_name != "#f") {
|
||||
if (obj_words.at(field_start / 4).symbol_name() != "#f") {
|
||||
lg::warn("Got a weird symbol in a pointer field: {}",
|
||||
obj_words.at(field_start / 4).symbol_name);
|
||||
obj_words.at(field_start / 4).symbol_name());
|
||||
continue;
|
||||
}
|
||||
|
||||
field_defs_out.emplace_back(field.name(), pretty_print::to_symbol("#f"));
|
||||
|
||||
} else {
|
||||
if (obj_words.at(field_start / 4).kind != LinkedWord::PLAIN_DATA) {
|
||||
if (obj_words.at(field_start / 4).kind() != LinkedWord::PLAIN_DATA) {
|
||||
continue;
|
||||
}
|
||||
std::vector<u8> bytes_out;
|
||||
@@ -753,7 +753,7 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
int end_elt = 0;
|
||||
for (int elt = len; elt-- > 0;) {
|
||||
auto& word = obj_words.at((field_start / 4) + elt);
|
||||
if (word.kind == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
if (word.kind() == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
continue;
|
||||
}
|
||||
end_elt = elt + 1;
|
||||
@@ -763,25 +763,25 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
for (int elt = 0; elt < end_elt; elt++) {
|
||||
auto& word = obj_words.at((field_start / 4) + elt);
|
||||
|
||||
if (word.kind == LinkedWord::PTR) {
|
||||
array_def.push_back(decompile_at_label(field.type(), labels.at(word.label_id), labels,
|
||||
if (word.kind() == LinkedWord::PTR) {
|
||||
array_def.push_back(decompile_at_label(field.type(), labels.at(word.label_id()), labels,
|
||||
words, ts, file));
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
} else if (word.kind() == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
// do nothing, the default is zero?
|
||||
array_def.push_back(pretty_print::to_symbol("0"));
|
||||
} else if (word.kind == LinkedWord::SYM_PTR) {
|
||||
if (word.symbol_name == "#f" || word.symbol_name == "#t") {
|
||||
array_def.push_back(pretty_print::to_symbol(fmt::format("{}", word.symbol_name)));
|
||||
} else if (word.kind() == LinkedWord::SYM_PTR) {
|
||||
if (word.symbol_name() == "#f" || word.symbol_name() == "#t") {
|
||||
array_def.push_back(pretty_print::to_symbol(fmt::format("{}", word.symbol_name())));
|
||||
} else {
|
||||
array_def.push_back(pretty_print::to_symbol(fmt::format("'{}", word.symbol_name)));
|
||||
array_def.push_back(pretty_print::to_symbol(fmt::format("'{}", word.symbol_name())));
|
||||
}
|
||||
} else if (word.kind == LinkedWord::EMPTY_PTR) {
|
||||
} else if (word.kind() == LinkedWord::EMPTY_PTR) {
|
||||
array_def.push_back(pretty_print::to_symbol("'()"));
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format(
|
||||
"Field {} in type {} offset {} did not have a proper reference for "
|
||||
"array element {} k = {}",
|
||||
field.name(), actual_type.print(), field.offset(), elt, (int)word.kind));
|
||||
field.name(), actual_type.print(), field.offset(), elt, (int)word.kind()));
|
||||
}
|
||||
}
|
||||
field_defs_out.emplace_back(field.name(), pretty_print::build_list(array_def));
|
||||
@@ -795,37 +795,37 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
assert(field_end - field_start == 4);
|
||||
auto& word = obj_words.at(field_start / 4);
|
||||
|
||||
if (word.kind == LinkedWord::PTR) {
|
||||
if (word.kind() == LinkedWord::PTR) {
|
||||
if (field.type() == TypeSpec("symbol")) {
|
||||
continue;
|
||||
}
|
||||
field_defs_out.emplace_back(
|
||||
field.name(),
|
||||
decompile_at_label(field.type(), labels.at(word.label_id), labels, words, ts, file));
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
field.name(), decompile_at_label(field.type(), labels.at(word.label_id()), labels,
|
||||
words, ts, file));
|
||||
} else if (word.kind() == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
// do nothing, the default is zero?
|
||||
field_defs_out.emplace_back(field.name(), pretty_print::to_symbol("0"));
|
||||
} else if (word.kind == LinkedWord::SYM_PTR) {
|
||||
if (word.symbol_name == "#f" || word.symbol_name == "#t") {
|
||||
} else if (word.kind() == LinkedWord::SYM_PTR) {
|
||||
if (word.symbol_name() == "#f" || word.symbol_name() == "#t") {
|
||||
field_defs_out.emplace_back(
|
||||
field.name(), pretty_print::to_symbol(fmt::format("{}", word.symbol_name)));
|
||||
field.name(), pretty_print::to_symbol(fmt::format("{}", word.symbol_name())));
|
||||
} else {
|
||||
field_defs_out.emplace_back(
|
||||
field.name(), pretty_print::to_symbol(fmt::format("'{}", word.symbol_name)));
|
||||
field.name(), pretty_print::to_symbol(fmt::format("'{}", word.symbol_name())));
|
||||
}
|
||||
} else if (word.kind == LinkedWord::EMPTY_PTR) {
|
||||
} else if (word.kind() == LinkedWord::EMPTY_PTR) {
|
||||
field_defs_out.emplace_back(field.name(), pretty_print::to_symbol("'()"));
|
||||
} else if (word.kind == LinkedWord::TYPE_PTR) {
|
||||
} else if (word.kind() == LinkedWord::TYPE_PTR) {
|
||||
if (field.type() != TypeSpec("type")) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Field {} in type {} offset {} had a reference to type {}, but the "
|
||||
"type of the field is not type.",
|
||||
field.name(), actual_type.print(), field.offset(), word.symbol_name));
|
||||
field.name(), actual_type.print(), field.offset(), word.symbol_name()));
|
||||
}
|
||||
int method_count = ts.get_type_method_count(word.symbol_name);
|
||||
int method_count = ts.get_type_method_count(word.symbol_name());
|
||||
field_defs_out.emplace_back(
|
||||
field.name(), pretty_print::to_symbol(fmt::format("(type-ref {} :method-count {})",
|
||||
word.symbol_name, method_count)));
|
||||
word.symbol_name(), method_count)));
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Field {} in type {} offset {} did not have a proper reference",
|
||||
@@ -1048,16 +1048,16 @@ goos::Object decompile_boxed_array(const DecompilerLabel& label,
|
||||
auto type_ptr_word_idx = (label.offset / 4) - 1;
|
||||
if ((label.offset % 8) == 4) {
|
||||
auto& type_ptr = words.at(label.target_segment).at(type_ptr_word_idx);
|
||||
if (type_ptr.kind != LinkedWord::TYPE_PTR) {
|
||||
if (type_ptr.kind() != LinkedWord::TYPE_PTR) {
|
||||
throw std::runtime_error("Invalid basic in decompile_boxed_array");
|
||||
}
|
||||
if (type_ptr.symbol_name == "array") {
|
||||
if (type_ptr.symbol_name() == "array") {
|
||||
auto content_type_ptr_word_idx = type_ptr_word_idx + 3;
|
||||
auto& content_type_ptr = words.at(label.target_segment).at(content_type_ptr_word_idx);
|
||||
if (content_type_ptr.kind != LinkedWord::TYPE_PTR) {
|
||||
if (content_type_ptr.kind() != LinkedWord::TYPE_PTR) {
|
||||
throw std::runtime_error("Invalid content in decompile_boxed_array");
|
||||
}
|
||||
content_type = TypeSpec(content_type_ptr.symbol_name);
|
||||
content_type = TypeSpec(content_type_ptr.symbol_name());
|
||||
} else {
|
||||
throw std::runtime_error("Wrong basic type in decompile_boxed_array");
|
||||
}
|
||||
@@ -1074,7 +1074,8 @@ goos::Object decompile_boxed_array(const DecompilerLabel& label,
|
||||
auto& size_word_2 = words.at(label.target_segment).at(type_ptr_word_idx + 2);
|
||||
auto first_elt_word_idx = type_ptr_word_idx + 4;
|
||||
|
||||
if (size_word_1.kind != LinkedWord::PLAIN_DATA || size_word_2.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (size_word_1.kind() != LinkedWord::PLAIN_DATA ||
|
||||
size_word_2.kind() != LinkedWord::PLAIN_DATA) {
|
||||
throw std::runtime_error("Invalid size in decompile_boxed_array");
|
||||
}
|
||||
|
||||
@@ -1093,20 +1094,20 @@ goos::Object decompile_boxed_array(const DecompilerLabel& label,
|
||||
|
||||
for (int elt = 0; elt < array_length; elt++) {
|
||||
auto& word = words.at(label.target_segment).at(first_elt_word_idx + elt);
|
||||
if (word.kind == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
if (word.kind() == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
result.push_back(pretty_print::to_symbol("0"));
|
||||
} else if (word.kind == LinkedWord::PTR) {
|
||||
} else if (word.kind() == LinkedWord::PTR) {
|
||||
if (content_type == TypeSpec("object")) {
|
||||
result.push_back(
|
||||
decompile_at_label_guess_type(labels.at(word.label_id), labels, words, ts, file));
|
||||
decompile_at_label_guess_type(labels.at(word.label_id()), labels, words, ts, file));
|
||||
} else {
|
||||
result.push_back(
|
||||
decompile_at_label(content_type, labels.at(word.label_id), labels, words, ts, file));
|
||||
result.push_back(decompile_at_label(content_type, labels.at(word.label_id()), labels,
|
||||
words, ts, file));
|
||||
}
|
||||
} else if (word.kind == LinkedWord::SYM_PTR) {
|
||||
result.push_back(pretty_print::to_symbol(fmt::format("'{}", word.symbol_name)));
|
||||
} else if (word.kind() == LinkedWord::SYM_PTR) {
|
||||
result.push_back(pretty_print::to_symbol(fmt::format("'{}", word.symbol_name())));
|
||||
} else {
|
||||
if (content_type == TypeSpec("object") && word.kind == LinkedWord::PLAIN_DATA &&
|
||||
if (content_type == TypeSpec("object") && word.kind() == LinkedWord::PLAIN_DATA &&
|
||||
(word.data & 0b111) == 0) {
|
||||
s32 val = word.data;
|
||||
result.push_back(pretty_print::to_symbol(fmt::format("(the binteger {})", val / 8)));
|
||||
@@ -1129,7 +1130,7 @@ goos::Object decompile_boxed_array(const DecompilerLabel& label,
|
||||
|
||||
for (int elt = 0; elt < array_length; elt++) {
|
||||
auto& word = words.at(label.target_segment).at(first_elt_word_idx + elt);
|
||||
auto segment = labels.at(word.label_id).target_segment;
|
||||
auto segment = labels.at(word.label_id()).target_segment;
|
||||
result.push_back(decomp_ref_to_inline_array_guess_size(
|
||||
words.at(segment), labels, segment, (first_elt_word_idx + elt) * 4, ts, words, file,
|
||||
content_type.get_single_arg(), ts.get_deref_info(content_type).stride));
|
||||
@@ -1152,9 +1153,9 @@ goos::Object decompile_boxed_array(const DecompilerLabel& label,
|
||||
std::vector<u8> elt_bytes;
|
||||
for (int j = start; j < end; j++) {
|
||||
auto& word = words.at(label.target_segment).at(j / 4);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Got bad word of kind {} in boxed array of values", word.kind));
|
||||
fmt::format("Got bad word of kind {} in boxed array of values", word.kind()));
|
||||
}
|
||||
elt_bytes.push_back(word.get_byte(j % 4));
|
||||
}
|
||||
@@ -1171,8 +1172,8 @@ goos::Object decompile_pair_elt(const LinkedWord& word,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file) {
|
||||
if (word.kind == LinkedWord::PTR) {
|
||||
auto& label = labels.at(word.label_id);
|
||||
if (word.kind() == LinkedWord::PTR) {
|
||||
auto& label = labels.at(word.label_id());
|
||||
auto guessed_type = get_type_of_label(label, words);
|
||||
if (!guessed_type.has_value()) {
|
||||
throw std::runtime_error("Could not guess the type of " + label.name);
|
||||
@@ -1183,21 +1184,21 @@ goos::Object decompile_pair_elt(const LinkedWord& word,
|
||||
}
|
||||
|
||||
return decompile_at_label(*guessed_type, label, labels, words, ts, file);
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
} else if (word.kind() == LinkedWord::PLAIN_DATA && word.data == 0) {
|
||||
// do nothing, the default is zero?
|
||||
return pretty_print::to_symbol("0");
|
||||
} else if (word.kind == LinkedWord::SYM_PTR) {
|
||||
} else if (word.kind() == LinkedWord::SYM_PTR) {
|
||||
// never quote symbols in a list.
|
||||
return pretty_print::to_symbol(fmt::format("{}", word.symbol_name));
|
||||
} else if (word.kind == LinkedWord::EMPTY_PTR) {
|
||||
return pretty_print::to_symbol(fmt::format("{}", word.symbol_name()));
|
||||
} else if (word.kind() == LinkedWord::EMPTY_PTR) {
|
||||
return pretty_print::to_symbol("'()");
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA && (word.data & 0b111) == 0) {
|
||||
} else if (word.kind() == LinkedWord::PLAIN_DATA && (word.data & 0b111) == 0) {
|
||||
return pretty_print::to_symbol(fmt::format("(the binteger {})", ((s32)word.data) >> 3));
|
||||
} else if (word.kind == LinkedWord::PLAIN_DATA) {
|
||||
} else if (word.kind() == LinkedWord::PLAIN_DATA) {
|
||||
return pretty_print::to_symbol(fmt::format("#x{:x}", word.data));
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("Pair elt did not have a good word kind: k {} d {}",
|
||||
(int)word.kind, word.data));
|
||||
(int)word.kind(), word.data));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -1213,7 +1214,7 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
throw std::runtime_error(fmt::format("Invalid alignment for pair {}\n", label.offset % 16));
|
||||
} else {
|
||||
auto& word = words.at(label.target_segment).at(label.offset / 4);
|
||||
if (word.kind != LinkedWord::EMPTY_PTR) {
|
||||
if (word.kind() != LinkedWord::EMPTY_PTR) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Based on alignment, expected to get empty list for pair, but didn't"));
|
||||
}
|
||||
@@ -1238,7 +1239,7 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
|
||||
auto cdr_word = words.at(to_print.target_segment).at((to_print.offset + 2) / 4);
|
||||
// if empty
|
||||
if (cdr_word.kind == LinkedWord::EMPTY_PTR) {
|
||||
if (cdr_word.kind() == LinkedWord::EMPTY_PTR) {
|
||||
if (add_quote) {
|
||||
return pretty_print::build_list("quote", pretty_print::build_list(list_tokens));
|
||||
} else {
|
||||
@@ -1246,8 +1247,8 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
}
|
||||
}
|
||||
// if pointer
|
||||
if (cdr_word.kind == LinkedWord::PTR) {
|
||||
to_print = labels.at(cdr_word.label_id);
|
||||
if (cdr_word.kind() == LinkedWord::PTR) {
|
||||
to_print = labels.at(cdr_word.label_id());
|
||||
continue;
|
||||
}
|
||||
// invalid.
|
||||
@@ -1268,7 +1269,7 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
fmt::format("Invalid alignment for pair {}\n", to_print.offset % 16));
|
||||
} else {
|
||||
auto& word = words.at(to_print.target_segment).at(to_print.offset / 4);
|
||||
if (word.kind != LinkedWord::EMPTY_PTR) {
|
||||
if (word.kind() != LinkedWord::EMPTY_PTR) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Based on alignment, expected to get empty list for pair, but didn't"));
|
||||
}
|
||||
@@ -1302,7 +1303,7 @@ goos::Object decompile_bitfield(const TypeSpec& type,
|
||||
std::vector<u8> elt_bytes;
|
||||
for (int j = start_byte; j < end_byte; j++) {
|
||||
auto& word = words.at(label.target_segment).at(j / 4);
|
||||
if (word.kind != LinkedWord::PLAIN_DATA) {
|
||||
if (word.kind() != LinkedWord::PLAIN_DATA) {
|
||||
throw std::runtime_error("Got bad word in static bitfield");
|
||||
}
|
||||
elt_bytes.push_back(word.get_byte(j % 4));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
#include "tools/level_tools/Error.h"
|
||||
#include "decompiler/util/Error.h"
|
||||
|
||||
void read_plain_data_field(const TypedRef& object,
|
||||
const std::string& field_name,
|
||||
@@ -43,7 +43,7 @@ void read_plain_data_field(const TypedRef& object,
|
||||
int byte_in_word = byte_in_words % 4;
|
||||
|
||||
const auto& word = words.at(word_idx);
|
||||
if (word.kind != decompiler::LinkedWord::PLAIN_DATA) {
|
||||
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);
|
||||
}
|
||||
@@ -78,11 +78,11 @@ TypedRef get_and_check_ref_to_basic(const TypedRef& object,
|
||||
|
||||
const auto& word = object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4);
|
||||
|
||||
if (word.kind != decompiler::LinkedWord::PTR) {
|
||||
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);
|
||||
const auto& label = object.ref.data->labels.at(word.label_id());
|
||||
|
||||
Ref ref;
|
||||
ref.data = object.ref.data;
|
||||
@@ -94,18 +94,18 @@ TypedRef get_and_check_ref_to_basic(const TypedRef& object,
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
type_tag.symbol_name(), field_name, expected_type);
|
||||
}
|
||||
|
||||
TypedRef tr;
|
||||
tr.ref = ref;
|
||||
tr.type = dts.ts.lookup_type(type_tag.symbol_name);
|
||||
tr.type = dts.ts.lookup_type(type_tag.symbol_name());
|
||||
return tr;
|
||||
}
|
||||
|
||||
@@ -132,11 +132,11 @@ std::string read_symbol_field(const TypedRef& object,
|
||||
|
||||
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) {
|
||||
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;
|
||||
return word.symbol_name();
|
||||
}
|
||||
|
||||
std::string read_type_field(const TypedRef& object,
|
||||
@@ -163,11 +163,11 @@ std::string read_type_field(const TypedRef& object,
|
||||
|
||||
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) {
|
||||
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;
|
||||
return word.symbol_name();
|
||||
}
|
||||
|
||||
std::string read_string_field(const TypedRef& object,
|
||||
@@ -193,11 +193,11 @@ std::string read_string_field(const TypedRef& object,
|
||||
}
|
||||
const auto& word = object.ref.data->words_by_seg.at(object.ref.seg).at(byte_in_words / 4);
|
||||
|
||||
if (word.kind != decompiler::LinkedWord::PTR) {
|
||||
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);
|
||||
const auto& label = object.ref.data->labels.at(word.label_id());
|
||||
return object.ref.data->get_goal_string_by_label(label);
|
||||
}
|
||||
|
||||
@@ -218,11 +218,11 @@ std::string get_type_of_basic(const Ref& object) {
|
||||
|
||||
const auto& word = object.data->words_by_seg.at(object.seg).at(byte_in_words / 4);
|
||||
|
||||
if (word.kind != decompiler::LinkedWord::TYPE_PTR) {
|
||||
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;
|
||||
return word.symbol_name();
|
||||
}
|
||||
|
||||
TypedRef typed_ref_from_basic(const Ref& object, const decompiler::DecompilerTypeSystem& dts) {
|
||||
@@ -233,13 +233,13 @@ TypedRef typed_ref_from_basic(const Ref& object, const decompiler::DecompilerTyp
|
||||
|
||||
const auto& word = object.data->words_by_seg.at(object.seg).at(byte_in_words / 4);
|
||||
|
||||
if (word.kind != decompiler::LinkedWord::TYPE_PTR) {
|
||||
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);
|
||||
result.type = dts.ts.lookup_type(word.symbol_name());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -251,11 +251,11 @@ Ref deref_label(const Ref& object) {
|
||||
|
||||
const auto& word = object.data->words_by_seg.at(object.seg).at(byte_in_words / 4);
|
||||
|
||||
if (word.kind != decompiler::LinkedWord::PTR) {
|
||||
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);
|
||||
const auto& lab = object.data->labels.at(word.label_id());
|
||||
|
||||
Ref result;
|
||||
result.byte_offset = lab.offset;
|
||||
@@ -266,15 +266,15 @@ Ref deref_label(const Ref& object) {
|
||||
|
||||
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) {
|
||||
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);
|
||||
return fmt::format("[{}]", ref.data->labels.at(word.label_id()).name);
|
||||
case decompiler::LinkedWord::SYM_PTR:
|
||||
return fmt::format("['{}]", word.symbol_name);
|
||||
return fmt::format("['{}]", word.symbol_name());
|
||||
case decompiler::LinkedWord::TYPE_PTR:
|
||||
return fmt::format("[t'{}]", word.symbol_name);
|
||||
return fmt::format("[t'{}]", word.symbol_name());
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@@ -69,4 +69,4 @@ TypedRef typed_ref_from_basic(const Ref& object, const decompiler::DecompilerTyp
|
||||
|
||||
Ref deref_label(const Ref& object);
|
||||
|
||||
std::string inspect_ref(const Ref& ref);
|
||||
std::string inspect_ref(const Ref& ref);
|
||||
@@ -202,10 +202,10 @@ goos::Object decompile_sparticle_tex_field_init(const std::vector<LinkedWord>& w
|
||||
const TypeSystem& ts,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(words.at(1).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 0);
|
||||
assert(flag_name == "plain-v1");
|
||||
|
||||
@@ -217,38 +217,38 @@ goos::Object decompile_sparticle_tex_field_init(const std::vector<LinkedWord>& w
|
||||
}
|
||||
|
||||
float word_as_float(const LinkedWord& w) {
|
||||
assert(w.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(w.kind() == LinkedWord::PLAIN_DATA);
|
||||
float v;
|
||||
memcpy(&v, &w.data, 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
s32 word_as_s32(const LinkedWord& w) {
|
||||
assert(w.kind == LinkedWord::PLAIN_DATA);
|
||||
assert(w.kind() == LinkedWord::PLAIN_DATA);
|
||||
return w.data;
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_func(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(words.at(1).kind == LinkedWord::SYM_PTR);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).kind() == LinkedWord::SYM_PTR);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 0);
|
||||
assert(flag_name == "from-pointer");
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-func {} '{})", field_name, words.at(1).symbol_name));
|
||||
fmt::format("(sp-func {} '{})", field_name, words.at(1).symbol_name()));
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_end(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(words.at(1).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).data == 0);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 0);
|
||||
assert(flag_name == "plain-v1");
|
||||
assert(field_name == "spt-end");
|
||||
@@ -300,10 +300,10 @@ goos::Object decompile_sparticle_userdata(const std::vector<LinkedWord>& words,
|
||||
goos::Object decompile_sparticle_int_init(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(words.at(1).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 1);
|
||||
assert(flag_name == "plain-v1");
|
||||
return pretty_print::to_symbol(
|
||||
@@ -338,10 +338,10 @@ goos::Object decompile_sparticle_int_with_rand_init(const std::vector<LinkedWord
|
||||
goos::Object decompile_sparticle_launcher_by_id(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(words.at(1).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 0);
|
||||
assert(flag_name == "part-by-id");
|
||||
return pretty_print::to_symbol(
|
||||
@@ -354,9 +354,9 @@ goos::Object decompile_sparticle_flags(const std::vector<LinkedWord>& words,
|
||||
const std::string& flag_name) {
|
||||
assert(flag_name == "plain-v1");
|
||||
assert(field_name == "spt-flags");
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 1);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
auto flag_def =
|
||||
decompile_bitfield_enum_from_int(TypeSpec("sp-cpuinfo-flag"), ts, word_as_s32(words.at(1)));
|
||||
@@ -372,10 +372,10 @@ goos::Object decompile_sparticle_flags(const std::vector<LinkedWord>& words,
|
||||
goos::Object decompile_sparticle_from_other(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(words.at(1).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(1).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).kind() == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 1);
|
||||
assert(flag_name == "copy-from-other-field");
|
||||
return pretty_print::to_symbol(
|
||||
@@ -547,7 +547,7 @@ goos::Object decompile_sparticle_field_init(const TypeSpec& type,
|
||||
words.at(label.target_segment).begin() + (offset_location / 4),
|
||||
words.at(label.target_segment).begin() + (offset_location / 4) + word_count);
|
||||
|
||||
assert(obj_words.at(0).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(obj_words.at(0).kind() == LinkedWord::PLAIN_DATA);
|
||||
u16 field_id = obj_words.at(0).data & 0xffff;
|
||||
u16 flags = obj_words.at(0).data >> 16;
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ set(RUNTIME_SOURCE
|
||||
graphics/opengl_renderer/debug_gui.cpp
|
||||
graphics/opengl_renderer/DirectRenderer.cpp
|
||||
graphics/opengl_renderer/dma_helpers.cpp
|
||||
graphics/opengl_renderer/Loader.cpp
|
||||
graphics/opengl_renderer/OpenGLRenderer.cpp
|
||||
graphics/opengl_renderer/Profiler.cpp
|
||||
graphics/opengl_renderer/Shader.cpp
|
||||
@@ -97,6 +98,7 @@ set(RUNTIME_SOURCE
|
||||
graphics/opengl_renderer/TextureUploadHandler.cpp
|
||||
graphics/opengl_renderer/tfrag/BufferedRenderer.cpp
|
||||
graphics/opengl_renderer/tfrag/program6_cpu.cpp
|
||||
graphics/opengl_renderer/tfrag/Tfrag3.cpp
|
||||
graphics/opengl_renderer/tfrag/tfrag_unpack.cpp
|
||||
graphics/opengl_renderer/tfrag/TFragment.cpp
|
||||
graphics/texture/TextureConverter.cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "game/graphics/opengl_renderer/Shader.h"
|
||||
#include "game/graphics/texture/TexturePool.h"
|
||||
#include "game/graphics/opengl_renderer/Profiler.h"
|
||||
#include "game/graphics/opengl_renderer/Loader.h"
|
||||
|
||||
/*!
|
||||
* Matches the bucket-id enum in GOAL
|
||||
@@ -47,6 +48,8 @@ struct SharedRenderState {
|
||||
: texture_pool(_texture_pool) {}
|
||||
ShaderLibrary shaders;
|
||||
std::shared_ptr<TexturePool> texture_pool;
|
||||
Loader loader;
|
||||
|
||||
u32 buckets_base = 0; // address of buckets array.
|
||||
u32 next_bucket = 0; // address of next bucket that we haven't started rendering in buckets
|
||||
u32 default_regs_buffer = 0; // address of the default regs chain.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "Loader.h"
|
||||
#include "common/util/Timer.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace {
|
||||
std::string uppercase_string(const std::string& s) {
|
||||
std::string result;
|
||||
for (auto c : s) {
|
||||
result.push_back(toupper(c));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
tfrag3::Level* Loader::get_tfrag3_level(const std::string& level_name) {
|
||||
const auto& existing = m_tfrag3_levels.find(level_name);
|
||||
if (existing == m_tfrag3_levels.end()) {
|
||||
fmt::print("Loader needs to load tfrag3 level: {}\n", level_name);
|
||||
Timer disk_timer;
|
||||
auto data = file_util::read_binary_file(
|
||||
file_util::get_file_path({fmt::format("assets/{}.fr3", uppercase_string(level_name))}));
|
||||
double disk_load_time = disk_timer.getSeconds();
|
||||
|
||||
Timer import_timer;
|
||||
auto& result = m_tfrag3_levels[level_name];
|
||||
Serializer ser(data.data(), data.size());
|
||||
result.serialize(ser);
|
||||
double import_time = import_timer.getSeconds();
|
||||
fmt::print("Load from file: {:.3f}s, import {:.3f}s\n", disk_load_time, import_time);
|
||||
return &result;
|
||||
} else {
|
||||
return &existing->second;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
|
||||
class Loader {
|
||||
public:
|
||||
tfrag3::Level* get_tfrag3_level(const std::string& level_name);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, tfrag3::Level> m_tfrag3_levels;
|
||||
};
|
||||
@@ -59,15 +59,22 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool)
|
||||
* Construct bucket renderers. We can specify different renderers for different buckets
|
||||
*/
|
||||
void OpenGLRenderer::init_bucket_renderers() {
|
||||
// temp
|
||||
std::vector<tfrag3::TFragmentTreeKind> normal_tfrags = {tfrag3::TFragmentTreeKind::NORMAL,
|
||||
tfrag3::TFragmentTreeKind::LOWRES};
|
||||
std::vector<tfrag3::TFragmentTreeKind> dirt_tfrags = {tfrag3::TFragmentTreeKind::DIRT};
|
||||
// TODO ice
|
||||
// std::vector<tfrag3::TFragmentTreeKind> ice_tfrags = {tfrag3::TFragmentTreeKind::ICE};
|
||||
|
||||
// std::vector<tfrag3::TFragmentTreeKind> trans_tfrags = {tfrag3::TFragmentTreeKind::TRANS,
|
||||
// tfrag3::TFragmentTreeKind::LOWRES_TRANS};
|
||||
|
||||
init_bucket_renderer<EmptyBucketRenderer>("bucket0", BucketId::BUCKET0);
|
||||
init_bucket_renderer<SkyRenderer>("sky", BucketId::SKY_DRAW);
|
||||
|
||||
init_bucket_renderer<TextureUploadHandler>("tfrag-tex-0", BucketId::TFRAG_TEX_LEVEL0);
|
||||
init_bucket_renderer<TFragment>("tfrag-0", BucketId::TFRAG_LEVEL0, false);
|
||||
init_bucket_renderer<TFragment>("tfrag-0", BucketId::TFRAG_LEVEL0, normal_tfrags, false);
|
||||
init_bucket_renderer<TextureUploadHandler>("tfrag-tex-1", BucketId::TFRAG_TEX_LEVEL1);
|
||||
init_bucket_renderer<TFragment>("tfrag-1", BucketId::TFRAG_LEVEL1, false);
|
||||
init_bucket_renderer<TFragment>("tfrag-1", BucketId::TFRAG_LEVEL1, normal_tfrags, false);
|
||||
init_bucket_renderer<TextureUploadHandler>("shrub-tex-0", BucketId::SHRUB_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("shrub-tex-1", BucketId::SHRUB_TEX_LEVEL1);
|
||||
init_bucket_renderer<TextureUploadHandler>("alpha-tex-0", BucketId::ALPHA_TEX_LEVEL0);
|
||||
@@ -75,10 +82,10 @@ void OpenGLRenderer::init_bucket_renderers() {
|
||||
auto sky_blender = std::make_shared<SkyBlender>();
|
||||
init_bucket_renderer<SkyBlendHandler>("sky-blend-and-tfrag-trans-0",
|
||||
BucketId::TFRAG_TRANS0_AND_SKY_BLEND_LEVEL0, sky_blender);
|
||||
init_bucket_renderer<TFragment>("tfrag-dirt-0", BucketId::TFRAG_DIRT_LEVEL0, false);
|
||||
init_bucket_renderer<TFragment>("tfrag-dirt-0", BucketId::TFRAG_DIRT_LEVEL0, dirt_tfrags, false);
|
||||
init_bucket_renderer<SkyBlendHandler>("sky-blend-and-tfrag-trans-1",
|
||||
BucketId::TFRAG_TRANS1_AND_SKY_BLEND_LEVEL1, sky_blender);
|
||||
init_bucket_renderer<TFragment>("tfrag-dirt-1", BucketId::TFRAG_DIRT_LEVEL1, false);
|
||||
init_bucket_renderer<TFragment>("tfrag-dirt-1", BucketId::TFRAG_DIRT_LEVEL1, dirt_tfrags, false);
|
||||
init_bucket_renderer<TextureUploadHandler>("pris-tex-0", BucketId::PRIS_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("pris-tex-1", BucketId::PRIS_TEX_LEVEL1);
|
||||
init_bucket_renderer<TextureUploadHandler>("water-tex-0", BucketId::WATER_TEX_LEVEL0);
|
||||
|
||||
@@ -99,7 +99,7 @@ void Profiler::draw_node(ProfilerNode& node, bool expand, int depth, float start
|
||||
constexpr int origin_x = 40;
|
||||
constexpr int origin_y = 60;
|
||||
constexpr int row_height = 15;
|
||||
constexpr int px_per_ms = 200;
|
||||
constexpr int px_per_ms = 75;
|
||||
|
||||
if (node.m_stats.duration > 0.00001) {
|
||||
color = name_to_color(node.m_name);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Shader.h"
|
||||
#include "common/util/assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/log/log.h"
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
|
||||
Shader::Shader(const std::string& shader_name) {
|
||||
@@ -22,7 +23,7 @@ Shader::Shader(const std::string& shader_name) {
|
||||
glGetShaderiv(m_vert_shader, GL_COMPILE_STATUS, &compile_ok);
|
||||
if (!compile_ok) {
|
||||
glGetShaderInfoLog(m_vert_shader, len, nullptr, err);
|
||||
printf("Failed to compile vertex shader %s:\n%s\n", shader_name.c_str(), err);
|
||||
lg::error("Failed to compile vertex shader {}:\n{}\n", shader_name.c_str(), err);
|
||||
m_is_okay = false;
|
||||
return;
|
||||
}
|
||||
@@ -35,7 +36,7 @@ Shader::Shader(const std::string& shader_name) {
|
||||
glGetShaderiv(m_frag_shader, GL_COMPILE_STATUS, &compile_ok);
|
||||
if (!compile_ok) {
|
||||
glGetShaderInfoLog(m_frag_shader, len, nullptr, err);
|
||||
printf("Failed to compile fragment shader %s:\n%s\n", shader_name.c_str(), err);
|
||||
lg::error("Failed to compile fragment shader {}:\n{}\n", shader_name.c_str(), err);
|
||||
m_is_okay = false;
|
||||
return;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ Shader::Shader(const std::string& shader_name) {
|
||||
glGetProgramiv(m_program, GL_LINK_STATUS, &compile_ok);
|
||||
if (!compile_ok) {
|
||||
glGetProgramInfoLog(m_program, len, nullptr, err);
|
||||
printf("Failed to link shader %s:\n%s\n", shader_name.c_str(), err);
|
||||
lg::error("Failed to link shader {}:\n{}\n", shader_name.c_str(), err);
|
||||
m_is_okay = false;
|
||||
return;
|
||||
}
|
||||
@@ -76,4 +77,6 @@ ShaderLibrary::ShaderLibrary() {
|
||||
at(ShaderId::DEBUG_BUFFERED) = {"debug_buffered"};
|
||||
at(ShaderId::BUFFERED_TCC0) = {"buffered_tcc0"};
|
||||
at(ShaderId::BUFFERED_TCC1) = {"buffered_tcc1"};
|
||||
at(ShaderId::TFRAG3) = {"tfrag3"};
|
||||
at(ShaderId::TFRAG3_NO_TEX) = {"tfrag3_no_tex"};
|
||||
}
|
||||
@@ -34,6 +34,8 @@ enum class ShaderId {
|
||||
DEBUG_BUFFERED = 9,
|
||||
BUFFERED_TCC0 = 10,
|
||||
BUFFERED_TCC1 = 11,
|
||||
TFRAG3 = 12,
|
||||
TFRAG3_NO_TEX = 13,
|
||||
MAX_SHADERS
|
||||
};
|
||||
|
||||
|
||||
@@ -243,7 +243,10 @@ SkyBlendHandler::SkyBlendHandler(const std::string& name,
|
||||
std::shared_ptr<SkyBlender> shared_blender)
|
||||
: BucketRenderer(name, my_id),
|
||||
m_shared_blender(shared_blender),
|
||||
m_tfrag_renderer(fmt::format("tfrag-{}", name), my_id, true) {}
|
||||
m_tfrag_renderer(fmt::format("tfrag-{}", name),
|
||||
my_id,
|
||||
{tfrag3::TFragmentTreeKind::TRANS, tfrag3::TFragmentTreeKind::LOWRES_TRANS},
|
||||
true) {}
|
||||
|
||||
void SkyBlendHandler::handle_sky_copies(DmaFollower& dma,
|
||||
SharedRenderState* render_state,
|
||||
@@ -278,6 +281,12 @@ void SkyBlendHandler::render(DmaFollower& dma,
|
||||
return;
|
||||
}
|
||||
|
||||
if (dma.current_tag().qwc != 8) {
|
||||
auto tfrag_prof = prof.make_scoped_child("tfrag-trans");
|
||||
m_tfrag_renderer.render(dma, render_state, tfrag_prof);
|
||||
return;
|
||||
}
|
||||
|
||||
// first is the set-display-gs-state
|
||||
auto set_display = dma.read_and_advance();
|
||||
assert(set_display.size_bytes == 8 * 16);
|
||||
|
||||
@@ -32,7 +32,7 @@ void FrameTimeRecorder::draw_window(const DmaStats& dma_stats) {
|
||||
window_pos_pivot.y = 1.0f;
|
||||
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
|
||||
ImGui::SetNextWindowBgAlpha(0.85f); // Transparent background
|
||||
if (ImGui::Begin("Frame Timing", p_open, window_flags)) {
|
||||
ImGui::Text("DMA: sync ms %.1f, tc %4d, sz %3d KB, ch %d", dma_stats.sync_time_ms,
|
||||
dma_stats.num_tags, (dma_stats.num_data_bytes) / (1 << 10), dma_stats.num_chunks);
|
||||
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
out vec3 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.a * 2);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
out vec3 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ out vec4 fragment_color;
|
||||
|
||||
void main() {
|
||||
// Note: position.y is multiplied by 32 instead of 16 to undo the half-height for interlacing stuff.
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w + 0.5);
|
||||
//fragment_color = vec4(1.0, 0, 0, 0.7);
|
||||
}
|
||||
@@ -7,6 +7,6 @@ layout (location = 0) in vec3 position_in;
|
||||
out vec4 fragment_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(1.0, 0, 0, 0.7);
|
||||
}
|
||||
@@ -9,6 +9,6 @@ out vec4 fragment_color;
|
||||
|
||||
void main() {
|
||||
// Note: position.y is multiplied by 32 instead of 16 to undo the half-height for interlacing stuff.
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w + 0.5);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
out vec3 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
out vec3 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.a * 2);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
noperspective out vec3 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.a * 2);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ out vec4 fragment_color;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
|
||||
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
in vec4 fragment_color;
|
||||
in vec3 tex_coord;
|
||||
uniform sampler2D tex_T0;
|
||||
|
||||
uniform float alpha_min;
|
||||
uniform float alpha_max;
|
||||
|
||||
void main() {
|
||||
//vec4 T0 = texture(tex_T0, tex_coord);
|
||||
vec4 T0 = texture(tex_T0, tex_coord.xy / tex_coord.z);
|
||||
color = fragment_color * T0 * 2.0;
|
||||
|
||||
if (color.a <= alpha_min) {
|
||||
discard;
|
||||
}
|
||||
|
||||
if (color.a > alpha_max) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 position_in;
|
||||
layout (location = 1) in vec3 tex_coord_in;
|
||||
layout (location = 2) in float time_of_day_index;
|
||||
|
||||
uniform vec4 hvdf_offset;
|
||||
uniform mat4 camera;
|
||||
uniform float fog_constant;
|
||||
uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose.
|
||||
|
||||
out vec4 fragment_color;
|
||||
out vec3 tex_coord;
|
||||
|
||||
void main() {
|
||||
|
||||
|
||||
// old system:
|
||||
// - load vf12
|
||||
// - itof0 vf12
|
||||
// - multiply with camera matrix (add trans)
|
||||
// - let Q = fogx / vf12.w
|
||||
// - xyz *= Q
|
||||
// - xyzw += hvdf_offset
|
||||
// - clip w.
|
||||
// - ftoi4 vf12
|
||||
// use in gs.
|
||||
// gs is 12.4 fixed point, set up with 2048.0 as the center.
|
||||
|
||||
// the itof0 is done in the preprocessing step. now we have floats.
|
||||
|
||||
// Step 3, the camera transform
|
||||
vec4 transformed = -camera[3].xyzw;
|
||||
transformed += -camera[0] * position_in.x;
|
||||
transformed += -camera[1] * position_in.y;
|
||||
transformed += -camera[2] * position_in.z;
|
||||
|
||||
// compute Q
|
||||
float Q = fog_constant / transformed[3];
|
||||
|
||||
// perspective divide!
|
||||
transformed.xyz *= Q;
|
||||
|
||||
// offset
|
||||
transformed.xyz += hvdf_offset.xyz;
|
||||
|
||||
// ftoi4
|
||||
//transformed.xyzw *= 16;
|
||||
|
||||
// correct xy offset
|
||||
transformed.xy -= (2048.);
|
||||
|
||||
// correct z scale
|
||||
transformed.z /= (16777216);
|
||||
transformed.z *= 2;
|
||||
transformed.z -= 1;
|
||||
|
||||
// correct xy scale
|
||||
transformed.x /= (256);
|
||||
transformed.y /= -(128);
|
||||
|
||||
// hack
|
||||
transformed.xyz *= transformed.w;
|
||||
|
||||
gl_Position = transformed;
|
||||
|
||||
// time of day lookup
|
||||
fragment_color = texture(tex_T1, time_of_day_index / 8192.f);
|
||||
fragment_color.w *= 2;
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
in vec4 fragment_color;
|
||||
in vec3 tex_coord;
|
||||
uniform sampler2D tex_T0;
|
||||
|
||||
uniform float alpha_min;
|
||||
uniform float alpha_max;
|
||||
|
||||
void main() {
|
||||
color = fragment_color;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 position_in;
|
||||
layout (location = 1) in vec4 rgba_in;
|
||||
|
||||
uniform vec4 hvdf_offset;
|
||||
uniform mat4 camera;
|
||||
uniform float fog_constant;
|
||||
|
||||
out vec4 fragment_color;
|
||||
|
||||
// this is just for debugging.
|
||||
void main() {
|
||||
vec4 transformed = -camera[3].xyzw;
|
||||
transformed += -camera[0] * position_in.x;
|
||||
transformed += -camera[1] * position_in.y;
|
||||
transformed += -camera[2] * position_in.z;
|
||||
|
||||
// compute Q
|
||||
float Q = fog_constant / transformed[3];
|
||||
|
||||
// perspective divide!
|
||||
transformed.xyz *= Q;
|
||||
|
||||
// offset
|
||||
transformed.xyz += hvdf_offset.xyz;
|
||||
|
||||
// ftoi4
|
||||
//transformed.xyzw *= 16;
|
||||
|
||||
// correct xy offset
|
||||
transformed.xy -= (2048.);
|
||||
|
||||
// correct z scale
|
||||
transformed.z /= (16777216);
|
||||
transformed.z *= 2;
|
||||
transformed.z -= 1;
|
||||
|
||||
// correct xy scale
|
||||
transformed.x /= (256);
|
||||
transformed.y /= -(128);
|
||||
|
||||
// hack
|
||||
transformed.xyz *= transformed.w;
|
||||
|
||||
gl_Position = transformed;
|
||||
|
||||
// time of day lookup
|
||||
fragment_color = rgba_in;
|
||||
}
|
||||
@@ -5,81 +5,6 @@
|
||||
|
||||
namespace BufferedRenderer {
|
||||
|
||||
std::string DrawMode::to_string() const {
|
||||
std::string result;
|
||||
result += fmt::format(" depth-write: {}\n", get_depth_write_enable());
|
||||
result += fmt::format(" depth-test: ");
|
||||
switch (get_depth_test()) {
|
||||
case GsTest::ZTest::NEVER:
|
||||
result += "never\n";
|
||||
break;
|
||||
case GsTest::ZTest::GEQUAL:
|
||||
result += "gequal\n";
|
||||
break;
|
||||
case GsTest::ZTest::ALWAYS:
|
||||
result += "always\n";
|
||||
break;
|
||||
case GsTest::ZTest::GREATER:
|
||||
result += "greater\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
result += fmt::format(" alpha: ");
|
||||
switch (get_alpha_blend()) {
|
||||
case AlphaBlend::SRC_0_SRC_DST:
|
||||
result += "src, 0, src, dst\n";
|
||||
break;
|
||||
case AlphaBlend::SRC_DST_SRC_DST:
|
||||
result += "src, dst, src, dst\n";
|
||||
break;
|
||||
case AlphaBlend::DISABLED:
|
||||
result += "disabled\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
result += fmt::format(" clamp: {}\n", get_clamp_enable());
|
||||
result += fmt::format(" filt: {}\n", get_filt_enable());
|
||||
result += fmt::format(" tcc: {}\n", get_tcc_enable());
|
||||
result += fmt::format(" aref: {}\n", get_aref());
|
||||
result += fmt::format(" ate: {}\n", get_at_enable());
|
||||
result += fmt::format(" atst: ");
|
||||
switch (get_alpha_test()) {
|
||||
case AlphaTest::ALWAYS:
|
||||
result += "always\n";
|
||||
break;
|
||||
case AlphaTest::GEQUAL:
|
||||
result += "gequal\n";
|
||||
break;
|
||||
case AlphaTest::NEVER:
|
||||
result += "never\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
result += fmt::format(" zte: {}\n", get_zt_enable());
|
||||
result += fmt::format(" abe: {}\n", get_ab_enable());
|
||||
result += fmt::format(" afail: ");
|
||||
switch (get_alpha_fail()) {
|
||||
case GsTest::AlphaFail::KEEP:
|
||||
result += "keep\n";
|
||||
break;
|
||||
case GsTest::AlphaFail::FB_ONLY:
|
||||
result += "fb-only\n";
|
||||
break;
|
||||
case GsTest::AlphaFail::RGB_ONLY:
|
||||
result += "rgb-only\n";
|
||||
break;
|
||||
case GsTest::AlphaFail::ZB_ONLY:
|
||||
result += "zb-only\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Renderer::Renderer(BucketId my_id) : m_my_id(my_id) {
|
||||
glGenBuffers(1, &m_ogl.vertex_buffer);
|
||||
glGenBuffers(1, &m_ogl.index_buffer);
|
||||
@@ -203,13 +128,19 @@ void Renderer::setup_opengl_excluding_textures(SharedRenderState* render_state,
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
if (mode.get_clamp_enable()) {
|
||||
if (mode.get_clamp_s_enable()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
}
|
||||
|
||||
if (mode.get_clamp_t_enable()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
}
|
||||
|
||||
@@ -245,6 +176,7 @@ void Renderer::setup_opengl_excluding_textures(SharedRenderState* render_state,
|
||||
alpha_reject);
|
||||
glUniform1i(glGetUniformLocation(render_state->shaders[ShaderId::BUFFERED_TCC1].id(), "T0"), 0);
|
||||
} else {
|
||||
fmt::print("tcc off!\n");
|
||||
render_state->shaders[ShaderId::BUFFERED_TCC0].activate();
|
||||
glUniform1f(
|
||||
glGetUniformLocation(render_state->shaders[ShaderId::BUFFERED_TCC0].id(), "alpha_reject"),
|
||||
@@ -563,8 +495,8 @@ void Builder::handle_clamp1(u64 val) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// this isn't quite right, but I'm hoping it's enough!
|
||||
m_current_mode.set_clamp_enable(val == 0b101);
|
||||
m_current_mode.set_clamp_s_enable(val & 0b1);
|
||||
m_current_mode.set_clamp_t_enable(val & 0b100);
|
||||
}
|
||||
|
||||
void Builder::handle_alpha1(u64 val) {
|
||||
|
||||
@@ -41,134 +41,6 @@ struct Triangle {
|
||||
u32 verts[3];
|
||||
};
|
||||
|
||||
// this represents all of the drawing state, stored as an integer.
|
||||
// it can also represent "invalid".
|
||||
class DrawMode {
|
||||
public:
|
||||
enum class AlphaBlend {
|
||||
DISABLED = 0,
|
||||
SRC_DST_SRC_DST = 1,
|
||||
SRC_0_SRC_DST = 2,
|
||||
};
|
||||
|
||||
enum class AlphaTest {
|
||||
NEVER = 0,
|
||||
ALWAYS = 1,
|
||||
GEQUAL = 2,
|
||||
};
|
||||
|
||||
bool get_depth_write_enable() const { return m_val & 0b1; }
|
||||
void enable_depth_write() { m_val = m_val | 0b1; }
|
||||
void disable_depth_write() { m_val = m_val & ~(0b1); }
|
||||
|
||||
GsTest::ZTest get_depth_test() const { return (GsTest::ZTest)((m_val >> 1) & 0b11); }
|
||||
void set_depth_test(GsTest::ZTest dt) { m_val = (m_val & ~(0b110)) | ((u32)(dt) << 1); }
|
||||
|
||||
AlphaBlend get_alpha_blend() const { return (AlphaBlend)((m_val >> 3) & 0b11); }
|
||||
void set_alpha_blend(AlphaBlend ab) { m_val = (m_val & ~(0b11000)) | ((u32)(ab) << 3); }
|
||||
|
||||
u8 get_aref() const { return m_val >> 8; }
|
||||
void set_aref(u8 val) { m_val = (m_val & ~(0xff00)) | (val << 8); }
|
||||
|
||||
AlphaTest get_alpha_test() const { return (AlphaTest)((m_val >> 16) & 0b11); }
|
||||
void set_alpha_test(AlphaTest ab) { m_val = (m_val & ~(0b11 << 16)) | ((u32)(ab) << 16); }
|
||||
|
||||
GsTest::AlphaFail get_alpha_fail() const { return (GsTest::AlphaFail)((m_val >> 21) & 0b11); }
|
||||
void set_alpha_fail(GsTest::AlphaFail ab) { m_val = (m_val & ~(0b11 << 21)) | ((u32)(ab) << 21); }
|
||||
|
||||
bool is_invalid() const { return m_val == UINT32_MAX; }
|
||||
bool is_valid() const { return !is_invalid(); }
|
||||
void set_invalid() { m_val = UINT32_MAX; }
|
||||
|
||||
bool get_clamp_enable() const { return m_val & (1 << 5); }
|
||||
void set_clamp_enable(bool en) {
|
||||
if (en) {
|
||||
enable_clamp();
|
||||
} else {
|
||||
disable_clamp();
|
||||
}
|
||||
}
|
||||
void enable_clamp() { m_val = m_val | (1 << 5); }
|
||||
void disable_clamp() { m_val = m_val & (~(1 << 5)); }
|
||||
|
||||
bool get_filt_enable() const { return m_val & (1 << 6); }
|
||||
void enable_filt() { m_val = m_val | (1 << 6); }
|
||||
void disable_filt() { m_val = m_val & (~(1 << 6)); }
|
||||
void set_filt_enable(bool en) {
|
||||
if (en) {
|
||||
enable_filt();
|
||||
} else {
|
||||
disable_filt();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_tcc_enable() const { return m_val & (1 << 6); }
|
||||
void enable_tcc() { m_val = m_val | (1 << 7); }
|
||||
void disable_tcc() { m_val = m_val & (~(1 << 7)); }
|
||||
void set_tcc(bool en) {
|
||||
if (en) {
|
||||
enable_tcc();
|
||||
} else {
|
||||
disable_tcc();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_at_enable() const { return m_val & (1 << 18); }
|
||||
void enable_at() { m_val = m_val | (1 << 18); }
|
||||
void disable_at() { m_val = m_val & (~(1 << 18)); }
|
||||
void set_at(bool en) {
|
||||
if (en) {
|
||||
enable_at();
|
||||
} else {
|
||||
disable_at();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_zt_enable() const { return m_val & (1 << 19); }
|
||||
void enable_zt() { m_val = m_val | (1 << 19); }
|
||||
void disable_zt() { m_val = m_val & (~(1 << 19)); }
|
||||
void set_zt(bool en) {
|
||||
if (en) {
|
||||
enable_zt();
|
||||
} else {
|
||||
disable_zt();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_ab_enable() const { return m_val & (1 << 20); }
|
||||
void enable_ab() { m_val = m_val | (1 << 20); }
|
||||
void disable_ab() { m_val = m_val & (~(1 << 20)); }
|
||||
void set_ab(bool en) {
|
||||
if (en) {
|
||||
enable_ab();
|
||||
} else {
|
||||
disable_ab();
|
||||
}
|
||||
}
|
||||
|
||||
u32& as_int() { return m_val; }
|
||||
|
||||
bool operator==(const DrawMode& other) const { return m_val == other.m_val; }
|
||||
bool operator!=(const DrawMode& other) const { return m_val != other.m_val; }
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
private:
|
||||
// 0 - depth write enable
|
||||
// 1, 2 - test: never, always, gequal, greater
|
||||
// 3, 4 - alpha: disable, [src,dst,src,dst], [src,0,src,dst], XX
|
||||
// 5 - clamp enable
|
||||
// 6 - filt enable
|
||||
// 7 - tcc enable
|
||||
// 8,9,10,11,12,14,14,15 - aref
|
||||
// 16, 17 - atest
|
||||
// 18 - ate
|
||||
// 19 - zte
|
||||
// 20 - abe
|
||||
// 21, 22 - afail
|
||||
u32 m_val = UINT32_MAX;
|
||||
};
|
||||
|
||||
struct Draw {
|
||||
DrawMode mode;
|
||||
std::vector<Triangle> triangles; // just indices
|
||||
|
||||
@@ -15,11 +15,15 @@ bool looks_like_tfrag_init(const DmaFollower& follow) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TFragment::TFragment(const std::string& name, BucketId my_id, bool child_mode)
|
||||
TFragment::TFragment(const std::string& name,
|
||||
BucketId my_id,
|
||||
const std::vector<tfrag3::TFragmentTreeKind>& trees,
|
||||
bool child_mode)
|
||||
: BucketRenderer(name, my_id),
|
||||
m_child_mode(child_mode),
|
||||
m_direct_renderer(fmt::format("{}.direct", name), my_id, 1024, DirectRenderer::Mode::NORMAL),
|
||||
m_buffered_renderer(my_id) {
|
||||
m_buffered_renderer(my_id),
|
||||
m_tree_kinds(trees) {
|
||||
for (auto& buf : m_buffered_data) {
|
||||
for (auto& x : buf.pad) {
|
||||
x = 0xff;
|
||||
@@ -31,6 +35,10 @@ TFragment::TFragment(const std::string& name, BucketId my_id, bool child_mode)
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const char* level_names[] = {"bea", "cit", "dar", "fin", "int", "jub", "jun", "fic",
|
||||
"lav", "mai", "mis", "ogr", "rob", "rol", "sno", "sub",
|
||||
"sun", "swa", "tit", "tra", "vi1", "vi2", "vi3"};
|
||||
|
||||
void TFragment::render(DmaFollower& dma,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
@@ -73,32 +81,80 @@ void TFragment::render(DmaFollower& dma,
|
||||
ImGui::Begin(fmt::format("{} extra", m_name).c_str());
|
||||
}
|
||||
|
||||
while (looks_like_tfrag_init(dma)) {
|
||||
m_debug_string += "------------- START!\n";
|
||||
handle_initialization(dma, render_state, prof);
|
||||
int count = 0;
|
||||
// fmt::print("---------------------------------------START\n");
|
||||
|
||||
while (looks_like_tfragment_dma(dma)) {
|
||||
m_stats.tfrag_dma_packets++;
|
||||
auto frag = dma.read_and_advance();
|
||||
m_stats.tfrag_bytes += frag.size_bytes;
|
||||
|
||||
if (m_extra_debug) {
|
||||
handle_tfrag<true>(frag, render_state, prof);
|
||||
} else {
|
||||
handle_tfrag<false>(frag, render_state, prof);
|
||||
if (m_use_tfrag3) {
|
||||
std::string level_name;
|
||||
while (looks_like_tfrag_init(dma)) {
|
||||
handle_initialization(dma, render_state, prof);
|
||||
if (level_name.empty()) {
|
||||
level_name = m_pc_port_data.level_name;
|
||||
} else if (level_name != m_pc_port_data.level_name) {
|
||||
assert(false);
|
||||
}
|
||||
if (m_max_draw >= 0 && count++ > m_max_draw) {
|
||||
break;
|
||||
|
||||
while (looks_like_tfragment_dma(dma)) {
|
||||
dma.read_and_advance();
|
||||
}
|
||||
}
|
||||
|
||||
if (dma.current_tag().qwc == 3) {
|
||||
while (dma.current_tag_offset() != render_state->next_bucket) {
|
||||
dma.read_and_advance();
|
||||
}
|
||||
if (dma.current_tag().qwc == 0) {
|
||||
dma.read_and_advance();
|
||||
|
||||
assert(!level_name.empty());
|
||||
m_tfrag3.setup_for_level(level_name, render_state);
|
||||
Tfrag3::RenderSettings settings;
|
||||
settings.hvdf_offset = m_tfrag_data.hvdf_offset;
|
||||
settings.fog_x = m_tfrag_data.fog.x();
|
||||
memcpy(settings.math_camera.data(), &m_buffered_data[0].pad[TFragDataMem::TFragMatrix0 * 16],
|
||||
64);
|
||||
settings.tree_idx = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
settings.planes[i] = m_pc_port_data.planes[i];
|
||||
}
|
||||
|
||||
if (m_override_time_of_day) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
settings.time_of_day_weights[i] = m_time_of_days[i];
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
settings.time_of_day_weights[i] =
|
||||
2 * (0xff & m_pc_port_data.itimes[i / 2].data()[2 * (i % 2)]) / 127.f;
|
||||
}
|
||||
}
|
||||
|
||||
auto t3prof = prof.make_scoped_child("t3");
|
||||
m_tfrag3.render_matching_trees(m_tree_kinds, settings, render_state, t3prof);
|
||||
|
||||
} else {
|
||||
while (looks_like_tfrag_init(dma)) {
|
||||
m_debug_string += "------------- START!\n";
|
||||
handle_initialization(dma, render_state, prof);
|
||||
int count = 0;
|
||||
// fmt::print("---------------------------------------START\n");
|
||||
|
||||
while (looks_like_tfragment_dma(dma)) {
|
||||
m_stats.tfrag_dma_packets++;
|
||||
auto frag = dma.read_and_advance();
|
||||
m_stats.tfrag_bytes += frag.size_bytes;
|
||||
|
||||
if (m_extra_debug) {
|
||||
handle_tfrag<true>(frag, render_state, prof);
|
||||
} else {
|
||||
handle_tfrag<false>(frag, render_state, prof);
|
||||
}
|
||||
if (m_max_draw >= 0 && count++ > m_max_draw) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dma.current_tag().qwc == 3) {
|
||||
dma.read_and_advance();
|
||||
}
|
||||
if (dma.current_tag().qwc == 0) {
|
||||
dma.read_and_advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +176,28 @@ void TFragment::render(DmaFollower& dma,
|
||||
m_debug_string +=
|
||||
fmt::format("DMA {} {} bytes, {}\n", tag, data.size_bytes, data.vifcode0().print());
|
||||
}
|
||||
|
||||
if (m_hack_test_many_levels) {
|
||||
for (int i = 0; i < HackManyLevels::NUM_LEVELS; i++) {
|
||||
if (m_many_level_render.level_enables[i]) {
|
||||
m_many_level_render.level_renderers[i].setup_for_level(level_names[i], render_state);
|
||||
Tfrag3::RenderSettings settings;
|
||||
settings.hvdf_offset = m_tfrag_data.hvdf_offset;
|
||||
settings.fog_x = m_tfrag_data.fog.x();
|
||||
memcpy(settings.math_camera.data(),
|
||||
&m_buffered_data[0].pad[TFragDataMem::TFragMatrix0 * 16], 64);
|
||||
settings.tree_idx = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
settings.time_of_day_weights[j] = m_time_of_days[j];
|
||||
}
|
||||
|
||||
auto t3prof = prof.make_scoped_child(level_names[i]);
|
||||
|
||||
m_many_level_render.level_renderers[i].debug_render_all_trees_nolores(settings,
|
||||
render_state, t3prof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void TFragment::draw_debug_window() {
|
||||
ImGui::Separator();
|
||||
@@ -129,28 +207,47 @@ void TFragment::draw_debug_window() {
|
||||
if (ImGui::Button("All")) {
|
||||
m_max_draw = -1;
|
||||
}
|
||||
ImGui::Checkbox("Skip MSCAL", &m_skip_mscals);
|
||||
ImGui::Checkbox("Skip XGKICK", &m_skip_xgkick);
|
||||
ImGui::Checkbox("Prog8 hack", &m_prog8_with_prog6);
|
||||
ImGui::Checkbox("Prog10 hack", &m_prog10_with_prog6);
|
||||
ImGui::Checkbox("Prog18 hack", &m_prog18_with_prog6);
|
||||
ImGui::Checkbox("Others with prog6", &m_all_with_prog6);
|
||||
ImGui::Checkbox("Use Buffered Renderer", &m_use_buffered_renderer);
|
||||
ImGui::Text("packets: %d", m_stats.tfrag_dma_packets);
|
||||
ImGui::Text("frag bytes: %d", m_stats.tfrag_bytes);
|
||||
ImGui::Text("errors: %d", m_stats.error_packets);
|
||||
for (int prog = 0; prog < 12; prog++) {
|
||||
ImGui::Text(" prog %d: %d calls\n", prog, m_stats.per_program[prog].calls);
|
||||
ImGui::Checkbox("Manual Time of Day", &m_override_time_of_day);
|
||||
if (m_override_time_of_day) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ImGui::SliderFloat(fmt::format("{}", i).c_str(), m_time_of_days + i, 0.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_use_buffered_renderer && ImGui::TreeNode("direct")) {
|
||||
m_direct_renderer.draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
ImGui::Checkbox("Hack Test Many (danger)", &m_hack_test_many_levels);
|
||||
if (m_hack_test_many_levels) {
|
||||
for (int i = 0; i < HackManyLevels::NUM_LEVELS; i++) {
|
||||
ImGui::Checkbox(level_names[i], &m_many_level_render.level_enables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_use_buffered_renderer && ImGui::TreeNode("buffered")) {
|
||||
m_buffered_renderer.draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
ImGui::Checkbox("Use TFRAG3", &m_use_tfrag3);
|
||||
if (!m_use_tfrag3) {
|
||||
ImGui::Checkbox("Use Buffered Renderer", &m_use_buffered_renderer);
|
||||
ImGui::Checkbox("Skip MSCAL", &m_skip_mscals);
|
||||
ImGui::Checkbox("Skip XGKICK", &m_skip_xgkick);
|
||||
ImGui::Checkbox("Prog8 hack", &m_prog8_with_prog6);
|
||||
ImGui::Checkbox("Prog10 hack", &m_prog10_with_prog6);
|
||||
ImGui::Checkbox("Prog18 hack", &m_prog18_with_prog6);
|
||||
ImGui::Checkbox("Others with prog6", &m_all_with_prog6);
|
||||
ImGui::Text("packets: %d", m_stats.tfrag_dma_packets);
|
||||
ImGui::Text("frag bytes: %d", m_stats.tfrag_bytes);
|
||||
ImGui::Text("errors: %d", m_stats.error_packets);
|
||||
for (int prog = 0; prog < 12; prog++) {
|
||||
ImGui::Text(" prog %d: %d calls\n", prog, m_stats.per_program[prog].calls);
|
||||
}
|
||||
|
||||
if (!m_use_buffered_renderer && ImGui::TreeNode("direct")) {
|
||||
m_direct_renderer.draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (m_use_buffered_renderer && ImGui::TreeNode("buffered")) {
|
||||
m_buffered_renderer.draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else {
|
||||
m_tfrag3.draw_debug_window();
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted(m_debug_string.data());
|
||||
@@ -209,6 +306,24 @@ void TFragment::handle_initialization(DmaFollower& dma,
|
||||
// lq.xyzw vf04, 664(vi00) | nop
|
||||
m_globals.vf04_ambient = m_tfrag_data.ambient; // TODO get rid?
|
||||
|
||||
auto pc_port_data = dma.read_and_advance();
|
||||
assert(pc_port_data.size_bytes == sizeof(PcPortData));
|
||||
memcpy(&m_pc_port_data, pc_port_data.data, sizeof(PcPortData));
|
||||
m_pc_port_data.level_name[11] = '\0';
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
m_debug_string += fmt::format("p[{}]: {}\n", i, m_pc_port_data.planes[i].to_string_aligned());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
m_debug_string += fmt::format("t[{}]: {:x} {:x} {:x} {:x}\n", i, m_pc_port_data.itimes[i].x(),
|
||||
m_pc_port_data.itimes[i].y(), m_pc_port_data.itimes[i].z(),
|
||||
m_pc_port_data.itimes[i].w());
|
||||
}
|
||||
|
||||
m_debug_string +=
|
||||
fmt::format("level: {}, tree: {}\n", m_pc_port_data.level_name, m_pc_port_data.tree_idx);
|
||||
|
||||
// setup double buffering.
|
||||
auto db_setup = dma.read_and_advance();
|
||||
assert(db_setup.size_bytes == 0);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/DirectRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/tfrag/BufferedRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/tfrag/Tfrag3.h"
|
||||
#include "common/dma/gs.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
@@ -40,7 +41,10 @@ struct TFragKickZone {
|
||||
|
||||
class TFragment : public BucketRenderer {
|
||||
public:
|
||||
TFragment(const std::string& name, BucketId my_id, bool child_mode);
|
||||
TFragment(const std::string& name,
|
||||
BucketId my_id,
|
||||
const std::vector<tfrag3::TFragmentTreeKind>& trees,
|
||||
bool child_mode);
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
|
||||
void draw_debug_window() override;
|
||||
|
||||
@@ -193,6 +197,10 @@ class TFragment : public BucketRenderer {
|
||||
bool m_prog18_with_prog6 = true;
|
||||
bool m_all_with_prog6 = false;
|
||||
bool m_use_buffered_renderer = true;
|
||||
bool m_use_tfrag3 = true;
|
||||
bool m_hack_test_many_levels = false;
|
||||
bool m_override_time_of_day = false;
|
||||
float m_time_of_days[8] = {0};
|
||||
std::string m_frag_debug;
|
||||
|
||||
// GS setup data
|
||||
@@ -204,6 +212,13 @@ class TFragment : public BucketRenderer {
|
||||
TFragData m_tfrag_data;
|
||||
TFragKickZone m_kick_data;
|
||||
|
||||
struct PcPortData {
|
||||
Vector4f planes[4];
|
||||
math::Vector<s32, 4> itimes[4];
|
||||
char level_name[12];
|
||||
u32 tree_idx;
|
||||
} m_pc_port_data;
|
||||
|
||||
// buffers
|
||||
TFragBufferedData m_buffered_data[2];
|
||||
int m_uploading_buffer = 0;
|
||||
@@ -281,4 +296,12 @@ class TFragment : public BucketRenderer {
|
||||
|
||||
DirectRenderer m_direct_renderer;
|
||||
BufferedRenderer::Builder m_buffered_renderer;
|
||||
Tfrag3 m_tfrag3;
|
||||
std::vector<tfrag3::TFragmentTreeKind> m_tree_kinds;
|
||||
|
||||
struct HackManyLevels {
|
||||
static constexpr int NUM_LEVELS = 23;
|
||||
Tfrag3 level_renderers[NUM_LEVELS];
|
||||
bool level_enables[NUM_LEVELS] = {0};
|
||||
} m_many_level_render;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,666 @@
|
||||
#include "Tfrag3.h"
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
Tfrag3::Tfrag3() {
|
||||
glGenVertexArrays(1, &m_debug_vao);
|
||||
glBindVertexArray(m_debug_vao);
|
||||
glGenBuffers(1, &m_debug_verts);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_debug_verts);
|
||||
glBufferData(GL_ARRAY_BUFFER, DEBUG_TRI_COUNT * 3 * sizeof(DebugVertex), nullptr,
|
||||
GL_DYNAMIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(0, // location 0 in the shader
|
||||
3, // 3 values per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized
|
||||
sizeof(DebugVertex), // stride
|
||||
(void*)offsetof(DebugVertex, position) // offset (0)
|
||||
);
|
||||
|
||||
glVertexAttribPointer(1, // location 1 in the shader
|
||||
4, // 4 values per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized
|
||||
sizeof(DebugVertex), // stride
|
||||
(void*)offsetof(DebugVertex, rgba) // offset (0)
|
||||
);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
Tfrag3::~Tfrag3() {
|
||||
discard_tree_cache();
|
||||
glDeleteVertexArrays(1, &m_debug_vao);
|
||||
}
|
||||
|
||||
void Tfrag3::setup_for_level(const std::string& level, SharedRenderState* render_state) {
|
||||
// make sure we have the level data.
|
||||
auto lev_data = render_state->loader.get_tfrag3_level(level);
|
||||
if (m_level_name != level) {
|
||||
fmt::print("new level for tfrag3: {} -> {}\n", m_level_name, level);
|
||||
fmt::print("discarding old stuff\n");
|
||||
discard_tree_cache();
|
||||
fmt::print("level has {} trees\n", lev_data->trees.size());
|
||||
m_cached_trees.resize(lev_data->trees.size());
|
||||
|
||||
size_t idx_buffer_len = 0;
|
||||
size_t time_of_day_count = 0;
|
||||
|
||||
for (size_t tree_idx = 0; tree_idx < lev_data->trees.size(); tree_idx++) {
|
||||
const auto& tree = lev_data->trees[tree_idx];
|
||||
m_cached_trees[tree_idx].kind = tree.kind;
|
||||
if (tree.kind != tfrag3::TFragmentTreeKind::INVALID) {
|
||||
for (auto& draw : tree.draws) {
|
||||
idx_buffer_len = std::max(idx_buffer_len, draw.vertex_index_stream.size());
|
||||
}
|
||||
time_of_day_count = std::max(tree.colors.size(), time_of_day_count);
|
||||
u32 verts = tree.vertices.size();
|
||||
fmt::print(" tree {} has {} verts ({} kB) and {} draws\n", tree_idx, verts,
|
||||
verts * sizeof(tfrag3::PreloadedVertex) / 1024.f, tree.draws.size());
|
||||
glGenVertexArrays(1, &m_cached_trees[tree_idx].vao);
|
||||
glBindVertexArray(m_cached_trees[tree_idx].vao);
|
||||
glGenBuffers(1, &m_cached_trees[tree_idx].vertex_buffer);
|
||||
m_cached_trees[tree_idx].vert_count = verts;
|
||||
m_cached_trees[tree_idx].draws = &tree.draws; // todo - should we just copy this?
|
||||
m_cached_trees[tree_idx].colors = &tree.colors;
|
||||
m_cached_trees[tree_idx].vis = &tree.vis_nodes;
|
||||
// don't bother with vis if we only have children.
|
||||
m_cached_trees[tree_idx].num_vis_tree_roots = tree.only_children ? 0 : tree.num_roots;
|
||||
m_cached_trees[tree_idx].vis_tree_root = tree.first_root;
|
||||
m_cached_trees[tree_idx].vis_temp.resize(tree.vis_nodes.size());
|
||||
m_cached_trees[tree_idx].culled_indices.resize(idx_buffer_len);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_cached_trees[tree_idx].vertex_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, verts * sizeof(tfrag3::PreloadedVertex), nullptr,
|
||||
GL_DYNAMIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, verts * sizeof(tfrag3::PreloadedVertex),
|
||||
tree.vertices.data());
|
||||
|
||||
glVertexAttribPointer(0, // location 0 in the shader
|
||||
3, // 3 values per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized
|
||||
sizeof(tfrag3::PreloadedVertex), // stride
|
||||
(void*)offsetof(tfrag3::PreloadedVertex, x) // offset (0)
|
||||
);
|
||||
|
||||
glVertexAttribPointer(1, // location 1 in the shader
|
||||
3, // 3 values per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized
|
||||
sizeof(tfrag3::PreloadedVertex), // stride
|
||||
(void*)offsetof(tfrag3::PreloadedVertex, s) // offset (0)
|
||||
);
|
||||
|
||||
glVertexAttribPointer(2, // location 2 in the shader
|
||||
1, // 1 values per vert
|
||||
GL_UNSIGNED_SHORT, // u16
|
||||
GL_FALSE, // don't normalize
|
||||
sizeof(tfrag3::PreloadedVertex), // stride
|
||||
(void*)offsetof(tfrag3::PreloadedVertex, color_index) // offset (0)
|
||||
);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("level has {} textures\n", lev_data->textures.size());
|
||||
for (auto& tex : lev_data->textures) {
|
||||
GLuint gl_tex;
|
||||
// fmt::print(" tex: {} x {} {} {}\n", tex.w, tex.h, tex.debug_name, tex.debug_tpage_name);
|
||||
glGenTextures(1, &gl_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
tex.data.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
float aniso = 0.0f;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, aniso);
|
||||
m_textures.push_back(gl_tex);
|
||||
}
|
||||
|
||||
fmt::print("level max index stream: {}\n", idx_buffer_len);
|
||||
m_has_index_buffer = true;
|
||||
glGenBuffers(1, &m_index_buffer);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_len * sizeof(u32), nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
fmt::print("level max time of day: {}\n", time_of_day_count);
|
||||
assert(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT);
|
||||
// regardless of how many we use some fixed max
|
||||
// we won't actually interp or upload to gpu the unused ones, but we need a fixed maximum so
|
||||
// indexing works properly.
|
||||
m_color_result.resize(TIME_OF_DAY_COLOR_COUNT);
|
||||
glGenTextures(1, &m_time_of_day_texture);
|
||||
m_has_time_of_day_texture = true;
|
||||
glBindTexture(GL_TEXTURE_1D, m_time_of_day_texture);
|
||||
// just fill with zeros. this lets use use the faster texsubimage later
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA,
|
||||
GL_UNSIGNED_INT_8_8_8_8, m_color_result.data());
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
m_level_name = level;
|
||||
}
|
||||
}
|
||||
|
||||
void Tfrag3::first_draw_setup(const RenderSettings& settings, SharedRenderState* render_state) {
|
||||
render_state->shaders[ShaderId::TFRAG3].activate();
|
||||
glUniform1i(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "tex_T0"), 0);
|
||||
glUniform1i(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "tex_T1"), 1);
|
||||
glUniformMatrix4fv(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "camera"),
|
||||
1, GL_FALSE, settings.math_camera.data());
|
||||
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "hvdf_offset"),
|
||||
settings.hvdf_offset[0], settings.hvdf_offset[1], settings.hvdf_offset[2],
|
||||
settings.hvdf_offset[3]);
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "fog_constant"),
|
||||
settings.fog_x);
|
||||
}
|
||||
|
||||
Tfrag3::DoubleDraw Tfrag3::setup_shader(const RenderSettings& /*settings*/,
|
||||
SharedRenderState* render_state,
|
||||
DrawMode mode) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
if (mode.get_zt_enable()) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
switch (mode.get_depth_test()) {
|
||||
case GsTest::ZTest::NEVER:
|
||||
glDepthFunc(GL_NEVER);
|
||||
break;
|
||||
case GsTest::ZTest::ALWAYS:
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
break;
|
||||
case GsTest::ZTest::GEQUAL:
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
break;
|
||||
case GsTest::ZTest::GREATER:
|
||||
glDepthFunc(GL_GREATER);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
if (mode.get_ab_enable() && mode.get_alpha_blend() != DrawMode::AlphaBlend::DISABLED) {
|
||||
glEnable(GL_BLEND);
|
||||
switch (mode.get_alpha_blend()) {
|
||||
case DrawMode::AlphaBlend::SRC_DST_SRC_DST:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
case DrawMode::AlphaBlend::SRC_0_SRC_DST:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
break;
|
||||
case DrawMode::AlphaBlend::SRC_0_FIX_DST:
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
if (mode.get_clamp_s_enable()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
}
|
||||
|
||||
if (mode.get_clamp_t_enable()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
}
|
||||
|
||||
if (mode.get_filt_enable()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
// for some reason, they set atest NEVER + FB_ONLY to disable depth writes
|
||||
bool alpha_hack_to_disable_z_write = false;
|
||||
DoubleDraw double_draw;
|
||||
|
||||
float alpha_min = 0.;
|
||||
if (mode.get_at_enable()) {
|
||||
switch (mode.get_alpha_test()) {
|
||||
case DrawMode::AlphaTest::ALWAYS:
|
||||
break;
|
||||
case DrawMode::AlphaTest::GEQUAL:
|
||||
alpha_min = mode.get_aref() / 127.f;
|
||||
switch (mode.get_alpha_fail()) {
|
||||
case GsTest::AlphaFail::KEEP:
|
||||
// ok, no need for double draw
|
||||
break;
|
||||
case GsTest::AlphaFail::FB_ONLY:
|
||||
// darn, we need to draw twice
|
||||
double_draw.kind = DoubleDrawKind::AFAIL_NO_DEPTH_WRITE;
|
||||
double_draw.aref = alpha_min;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
case DrawMode::AlphaTest::NEVER:
|
||||
if (mode.get_alpha_fail() == GsTest::AlphaFail::FB_ONLY) {
|
||||
alpha_hack_to_disable_z_write = true;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.get_depth_write_enable()) {
|
||||
glDepthMask(GL_TRUE);
|
||||
} else {
|
||||
glDepthMask(GL_FALSE);
|
||||
}
|
||||
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_min"),
|
||||
alpha_min);
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_max"),
|
||||
10.f);
|
||||
|
||||
return double_draw;
|
||||
}
|
||||
|
||||
void Tfrag3::render_tree(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof,
|
||||
bool use_vis) {
|
||||
auto& tree = m_cached_trees.at(settings.tree_idx);
|
||||
assert(tree.kind != tfrag3::TFragmentTreeKind::INVALID);
|
||||
|
||||
if (m_color_result.size() < tree.colors->size()) {
|
||||
m_color_result.resize(tree.colors->size());
|
||||
}
|
||||
interp_time_of_day_slow(settings.time_of_day_weights, *tree.colors, m_color_result.data());
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_1D, m_time_of_day_texture);
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, tree.colors->size(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
m_color_result.data());
|
||||
|
||||
first_draw_setup(settings, render_state);
|
||||
|
||||
glBindVertexArray(tree.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tree.vertex_buffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glEnable(GL_PRIMITIVE_RESTART);
|
||||
glPrimitiveRestartIndex(UINT32_MAX);
|
||||
|
||||
for (const auto& draw : *tree.draws) {
|
||||
glBindTexture(GL_TEXTURE_2D, m_textures.at(draw.tree_tex_id));
|
||||
auto double_draw = setup_shader(settings, render_state, draw.mode);
|
||||
tree.tris_this_frame += draw.num_triangles;
|
||||
tree.draws_this_frame++;
|
||||
int draw_size = draw.vertex_index_stream.size();
|
||||
if (use_vis) {
|
||||
int vtx_idx = 0;
|
||||
int out_idx = 0;
|
||||
for (auto& grp : draw.vis_groups) {
|
||||
if (grp.tfrag_idx == 0xffffffff || tree.vis_temp.at(grp.tfrag_idx)) {
|
||||
memcpy(&tree.culled_indices[out_idx], &draw.vertex_index_stream[vtx_idx],
|
||||
grp.num * sizeof(u32));
|
||||
out_idx += grp.num;
|
||||
}
|
||||
|
||||
vtx_idx += grp.num;
|
||||
}
|
||||
|
||||
draw_size = out_idx;
|
||||
if (draw_size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
prof.add_draw_call();
|
||||
prof.add_tri(draw.num_triangles * (float)out_idx / draw.vertex_index_stream.size());
|
||||
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, out_idx * sizeof(u32),
|
||||
tree.culled_indices.data());
|
||||
} else {
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, draw.vertex_index_stream.size() * sizeof(u32),
|
||||
draw.vertex_index_stream.data());
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLE_STRIP, draw_size, GL_UNSIGNED_INT, (void*)0);
|
||||
|
||||
switch (double_draw.kind) {
|
||||
case DoubleDrawKind::NONE:
|
||||
break;
|
||||
case DoubleDrawKind::AFAIL_NO_DEPTH_WRITE:
|
||||
prof.add_draw_call();
|
||||
prof.add_tri(draw_size);
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_min"),
|
||||
-10.f);
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_max"),
|
||||
double_draw.aref);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDrawElements(GL_TRIANGLE_STRIP, draw_size, GL_UNSIGNED_INT, (void*)0);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
bool sphere_in_view_ref(const math::Vector4f& sphere, const math::Vector4f* planes) {
|
||||
/*
|
||||
*(let ((v1-0 *math-camera*))
|
||||
(.lvf vf6 (&-> arg0 quad))
|
||||
(.lvf vf1 (&-> v1-0 plane 0 quad))
|
||||
(.lvf vf2 (&-> v1-0 plane 1 quad))
|
||||
(.lvf vf3 (&-> v1-0 plane 2 quad))
|
||||
(.lvf vf4 (&-> v1-0 plane 3 quad))
|
||||
)
|
||||
(.mul.x.vf acc vf1 vf6)
|
||||
(.add.mul.y.vf acc vf2 vf6 acc)
|
||||
(.add.mul.z.vf acc vf3 vf6 acc)
|
||||
(.sub.mul.w.vf vf5 vf4 vf0 acc)
|
||||
(.add.w.vf vf5 vf5 vf6)
|
||||
(.mov v1-1 vf5)
|
||||
(.pcgtw v1-2 r0-0 v1-1)
|
||||
(.ppach v1-3 r0-0 v1-2)
|
||||
(zero? (the-as int v1-3))
|
||||
*/
|
||||
|
||||
math::Vector4f acc =
|
||||
planes[0] * sphere.x() + planes[1] * sphere.y() + planes[2] * sphere.z() - planes[3];
|
||||
|
||||
return acc.x() > -sphere.w() && acc.y() > -sphere.w() && acc.z() > -sphere.w() &&
|
||||
acc.w() > -sphere.w();
|
||||
}
|
||||
|
||||
void cull_ref_all(const math::Vector4f* planes,
|
||||
const std::vector<tfrag3::VisNode>& nodes,
|
||||
u8* out) {
|
||||
for (size_t i = 0; i < nodes.size(); i++) {
|
||||
out[i] = sphere_in_view_ref(nodes[i].bsphere, planes);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Render all trees with settings for the given tree.
|
||||
* This is intended to be used only for debugging when we can't easily get commands for all trees
|
||||
* working.
|
||||
*/
|
||||
void Tfrag3::render_all_trees(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
RenderSettings settings_copy = settings;
|
||||
for (size_t i = 0; i < m_cached_trees.size(); i++) {
|
||||
if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID) {
|
||||
settings_copy.tree_idx = i;
|
||||
render_tree(settings_copy, render_state, prof, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tfrag3::render_matching_trees(const std::vector<tfrag3::TFragmentTreeKind>& trees,
|
||||
const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
RenderSettings settings_copy = settings;
|
||||
for (size_t i = 0; i < m_cached_trees.size(); i++) {
|
||||
m_cached_trees[i].reset_stats();
|
||||
if (!m_cached_trees[i].allowed) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(trees.begin(), trees.end(), m_cached_trees[i].kind) != trees.end() ||
|
||||
m_cached_trees[i].forced) {
|
||||
m_cached_trees[i].rendered_this_frame = true;
|
||||
settings_copy.tree_idx = i;
|
||||
cull_ref_all(settings.planes, *m_cached_trees[i].vis, m_cached_trees[i].vis_temp.data());
|
||||
render_tree(settings_copy, render_state, prof, true);
|
||||
if (m_cached_trees[i].cull_debug) {
|
||||
render_tree_cull_debug(settings_copy, render_state, prof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tfrag3::debug_render_all_trees_nolores(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
RenderSettings settings_copy = settings;
|
||||
for (size_t i = 0; i < m_cached_trees.size(); i++) {
|
||||
if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID &&
|
||||
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES_TRANS &&
|
||||
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES) {
|
||||
settings_copy.tree_idx = i;
|
||||
render_tree(settings_copy, render_state, prof, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_cached_trees.size(); i++) {
|
||||
if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID &&
|
||||
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES_TRANS &&
|
||||
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES) {
|
||||
settings_copy.tree_idx = i;
|
||||
render_tree_cull_debug(settings_copy, render_state, prof);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tfrag3::draw_debug_window() {
|
||||
for (int i = 0; i < (int)m_cached_trees.size(); i++) {
|
||||
auto& tree = m_cached_trees[i];
|
||||
if (tree.kind == tfrag3::TFragmentTreeKind::INVALID) {
|
||||
continue;
|
||||
}
|
||||
ImGui::PushID(i);
|
||||
ImGui::Text("[%d] %10s", i, tfrag3::tfrag_tree_names[(int)m_cached_trees[i].kind]);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Allow?", &tree.allowed);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Force?", &tree.forced);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("cull debug (slow)", &tree.cull_debug);
|
||||
ImGui::PopID();
|
||||
if (tree.rendered_this_frame) {
|
||||
ImGui::Text(" tris: %d draws: %d", tree.tris_this_frame, tree.draws_this_frame);
|
||||
int vis = 0;
|
||||
for (auto x : tree.vis_temp) {
|
||||
if (x) {
|
||||
vis++;
|
||||
}
|
||||
}
|
||||
ImGui::Text(" cull: %d vis out of %d", vis, (int)tree.vis_temp.size());
|
||||
}
|
||||
ImGui::Text("root: %d, roots: %d, nodes %d", tree.vis_tree_root, tree.num_vis_tree_roots,
|
||||
(int)tree.vis->size());
|
||||
}
|
||||
}
|
||||
|
||||
void Tfrag3::discard_tree_cache() {
|
||||
for (auto tex : m_textures) {
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
m_textures.clear();
|
||||
|
||||
for (auto& tree : m_cached_trees) {
|
||||
if (tree.kind != tfrag3::TFragmentTreeKind::INVALID) {
|
||||
glDeleteBuffers(1, &tree.vertex_buffer);
|
||||
glDeleteVertexArrays(1, &tree.vao);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_has_index_buffer) {
|
||||
glDeleteBuffers(1, &m_index_buffer);
|
||||
m_has_index_buffer = false;
|
||||
}
|
||||
|
||||
if (m_has_time_of_day_texture) {
|
||||
glBindTexture(GL_TEXTURE_1D, m_time_of_day_texture);
|
||||
glDeleteTextures(1, &m_time_of_day_texture);
|
||||
m_has_time_of_day_texture = false;
|
||||
}
|
||||
|
||||
// delete textures and stuff.
|
||||
m_cached_trees.clear();
|
||||
}
|
||||
|
||||
void Tfrag3::interp_time_of_day_slow(const float weights[8],
|
||||
const std::vector<tfrag3::TimeOfDayColor>& in,
|
||||
math::Vector<u8, 4>* out) {
|
||||
// Timer interp_timer;
|
||||
for (size_t color = 0; color < in.size(); color++) {
|
||||
math::Vector4f result = math::Vector4f::zero();
|
||||
for (int component = 0; component < 8; component++) {
|
||||
result += in[color].rgba[component].cast<float>() * weights[component];
|
||||
}
|
||||
result[0] = std::min(result[0], 255.f);
|
||||
result[1] = std::min(result[1], 255.f);
|
||||
result[2] = std::min(result[2], 255.f);
|
||||
result[3] = std::min(result[3], 128.f); // note: different for alpha!
|
||||
out[color] = result.cast<u8>();
|
||||
}
|
||||
// about 70 us, not bad.
|
||||
// fmt::print("interp {} colors {:.2f} ms\n", in.size(), interp_timer.getMs());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
float frac(float in) {
|
||||
return in - (int)in;
|
||||
}
|
||||
|
||||
void debug_vis_draw(int first_root,
|
||||
int tree,
|
||||
int num,
|
||||
int depth,
|
||||
const std::vector<tfrag3::VisNode>& nodes,
|
||||
std::vector<Tfrag3::DebugVertex>& verts_out) {
|
||||
for (int ki = 0; ki < num; ki++) {
|
||||
auto& node = nodes.at(ki + tree - first_root);
|
||||
assert(node.child_id != 0xffff);
|
||||
math::Vector4f rgba{frac(0.4 * depth), frac(0.7 * depth), frac(0.2 * depth), 0.06};
|
||||
math::Vector3f center = node.bsphere.xyz();
|
||||
float rad = node.bsphere.w();
|
||||
math::Vector3f corners[8] = {center, center, center, center};
|
||||
corners[0].x() += rad;
|
||||
corners[1].x() += rad;
|
||||
corners[2].x() -= rad;
|
||||
corners[3].x() -= rad;
|
||||
|
||||
corners[0].y() += rad;
|
||||
corners[1].y() -= rad;
|
||||
corners[2].y() += rad;
|
||||
corners[3].y() -= rad;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
corners[i + 4] = corners[i];
|
||||
corners[i].z() += rad;
|
||||
corners[i + 4].z() -= rad;
|
||||
}
|
||||
|
||||
if (true) {
|
||||
for (int i : {0, 4}) {
|
||||
verts_out.push_back({corners[0 + i], rgba});
|
||||
verts_out.push_back({corners[1 + i], rgba});
|
||||
verts_out.push_back({corners[2 + i], rgba});
|
||||
|
||||
verts_out.push_back({corners[1 + i], rgba}); // 0
|
||||
verts_out.push_back({corners[3 + i], rgba});
|
||||
verts_out.push_back({corners[2 + i], rgba});
|
||||
}
|
||||
|
||||
for (int i : {2, 6, 7, 2, 3, 7, 0, 4, 5, 0, 5, 1, 0, 6, 4, 0, 6, 2, 1, 3, 7, 1, 5, 7}) {
|
||||
verts_out.push_back({corners[i], rgba});
|
||||
}
|
||||
|
||||
constexpr int border0[12] = {0, 4, 6, 2, 2, 6, 3, 7, 0, 1, 2, 3};
|
||||
constexpr int border1[12] = {1, 5, 7, 3, 0, 4, 1, 5, 4, 5, 6, 7};
|
||||
rgba.w() = 1.0;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
auto p0 = corners[border0[i]];
|
||||
auto p1 = corners[border1[i]];
|
||||
auto diff = (p1 - p0).normalized();
|
||||
math::Vector3f px = diff.z() == 0 ? math::Vector3f{1, 0, 1} : math::Vector3f{0, 1, 1};
|
||||
auto off = diff.cross(px) * 2000;
|
||||
|
||||
verts_out.push_back({p0 + off, rgba});
|
||||
verts_out.push_back({p0 - off, rgba});
|
||||
verts_out.push_back({p1 - off, rgba});
|
||||
|
||||
verts_out.push_back({p0 + off, rgba});
|
||||
verts_out.push_back({p1 + off, rgba});
|
||||
verts_out.push_back({p1 - off, rgba});
|
||||
}
|
||||
}
|
||||
|
||||
if (node.flags) {
|
||||
debug_vis_draw(first_root, node.child_id, node.num_kids, depth + 1, nodes, verts_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Tfrag3::render_tree_cull_debug(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
// generate debug verts:
|
||||
m_debug_vert_data.clear();
|
||||
auto& tree = m_cached_trees.at(settings.tree_idx);
|
||||
|
||||
debug_vis_draw(tree.vis_tree_root, tree.vis_tree_root, tree.num_vis_tree_roots, 1, *tree.vis,
|
||||
m_debug_vert_data);
|
||||
|
||||
render_state->shaders[ShaderId::TFRAG3_NO_TEX].activate();
|
||||
glUniformMatrix4fv(
|
||||
glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3_NO_TEX].id(), "camera"), 1,
|
||||
GL_FALSE, settings.math_camera.data());
|
||||
glUniform4f(
|
||||
glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3_NO_TEX].id(), "hvdf_offset"),
|
||||
settings.hvdf_offset[0], settings.hvdf_offset[1], settings.hvdf_offset[2],
|
||||
settings.hvdf_offset[3]);
|
||||
glUniform1f(
|
||||
glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3_NO_TEX].id(), "fog_constant"),
|
||||
settings.fog_x);
|
||||
// glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ?
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glBindVertexArray(m_debug_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_debug_verts);
|
||||
|
||||
int remaining = m_debug_vert_data.size();
|
||||
int start = 0;
|
||||
|
||||
while (remaining > 0) {
|
||||
int to_do = std::min(DEBUG_TRI_COUNT * 3, remaining);
|
||||
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, to_do * sizeof(DebugVertex),
|
||||
m_debug_vert_data.data() + start);
|
||||
glDrawArrays(GL_TRIANGLES, 0, to_do);
|
||||
prof.add_draw_call();
|
||||
prof.add_tri(to_do / 3);
|
||||
|
||||
remaining -= to_do;
|
||||
start += to_do;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "common/math/Vector.h"
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
|
||||
class Tfrag3 {
|
||||
public:
|
||||
struct RenderSettings {
|
||||
math::Matrix4f math_camera;
|
||||
math::Vector4f hvdf_offset;
|
||||
float fog_x;
|
||||
const u8* rgba_data;
|
||||
int tree_idx;
|
||||
float time_of_day_weights[8] = {0};
|
||||
math::Vector4f planes[4];
|
||||
bool do_culling = false;
|
||||
bool debug_culling = false;
|
||||
// todo culling planes
|
||||
// todo occlusion culling string.
|
||||
};
|
||||
|
||||
Tfrag3();
|
||||
~Tfrag3();
|
||||
|
||||
void debug_render_all_trees_nolores(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
|
||||
void render_all_trees(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
|
||||
void render_matching_trees(const std::vector<tfrag3::TFragmentTreeKind>& trees,
|
||||
const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
|
||||
void render_tree(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof,
|
||||
bool use_vis);
|
||||
|
||||
void setup_for_level(const std::string& level, SharedRenderState* render_state);
|
||||
void discard_tree_cache();
|
||||
|
||||
void render_tree_cull_debug(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
|
||||
void draw_debug_window();
|
||||
struct DebugVertex {
|
||||
math::Vector3f position;
|
||||
math::Vector4f rgba;
|
||||
};
|
||||
|
||||
private:
|
||||
void first_draw_setup(const RenderSettings& settings, SharedRenderState* render_state);
|
||||
enum class DoubleDrawKind { NONE, AFAIL_NO_DEPTH_WRITE };
|
||||
struct DoubleDraw {
|
||||
DoubleDrawKind kind = DoubleDrawKind::NONE;
|
||||
float aref = 0.;
|
||||
};
|
||||
|
||||
DoubleDraw setup_shader(const RenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
DrawMode mode);
|
||||
void interp_time_of_day_slow(const float weights[8],
|
||||
const std::vector<tfrag3::TimeOfDayColor>& in,
|
||||
math::Vector<u8, 4>* out);
|
||||
|
||||
struct TreeCache {
|
||||
tfrag3::TFragmentTreeKind kind;
|
||||
GLuint vertex_buffer = -1;
|
||||
GLuint vao;
|
||||
u32 vert_count = 0;
|
||||
const std::vector<tfrag3::Draw>* draws = nullptr;
|
||||
const std::vector<tfrag3::TimeOfDayColor>* colors = nullptr;
|
||||
const std::vector<tfrag3::VisNode>* vis = nullptr;
|
||||
|
||||
std::vector<u8> vis_temp;
|
||||
std::vector<u32> culled_indices;
|
||||
int num_vis_tree_roots = 0;
|
||||
int vis_tree_root = 0;
|
||||
int first_vis_leaf = 0;
|
||||
|
||||
void reset_stats() {
|
||||
rendered_this_frame = false;
|
||||
tris_this_frame = 0;
|
||||
draws_this_frame = 0;
|
||||
}
|
||||
bool rendered_this_frame = false;
|
||||
int tris_this_frame = 0;
|
||||
int draws_this_frame = 0;
|
||||
bool allowed = true;
|
||||
bool forced = false;
|
||||
bool cull_debug = false;
|
||||
};
|
||||
|
||||
std::string m_level_name;
|
||||
|
||||
std::vector<GLuint> m_textures;
|
||||
std::vector<TreeCache> m_cached_trees;
|
||||
GLuint m_time_of_day_texture = -1;
|
||||
bool m_has_time_of_day_texture = false;
|
||||
|
||||
std::vector<math::Vector<u8, 4>> m_color_result;
|
||||
|
||||
bool m_has_index_buffer = false;
|
||||
GLuint m_index_buffer = -1;
|
||||
|
||||
GLuint m_debug_vao = -1;
|
||||
GLuint m_debug_verts = -1;
|
||||
|
||||
// in theory could be up to 4096, I think, but we don't see that many...
|
||||
// should be easy to increase (will require a shader change too for indexing)
|
||||
static constexpr int TIME_OF_DAY_COLOR_COUNT = 2048;
|
||||
|
||||
static constexpr int DEBUG_TRI_COUNT = 4096;
|
||||
std::vector<DebugVertex> m_debug_vert_data;
|
||||
};
|
||||
@@ -471,40 +471,6 @@ void TFragment::exec_program_6_process_first(const Prog6Inputs& in,
|
||||
break;
|
||||
}
|
||||
}
|
||||
// while (m_next_block != TFragJumper::END_PROGRAM) {
|
||||
//// fmt::print("block {}\n", (int)m_next_block);
|
||||
// switch (m_next_block) {
|
||||
// case L128_PART0_X:
|
||||
// exec_jumper_L128<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L129_PART1_X:
|
||||
// exec_jumper_L129<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L0x6A1_PART0_Y:
|
||||
// exec_jumper_L6A1<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L130_PART1_Y:
|
||||
// exec_jumper_L130<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L0x6B0_PART0_Z:
|
||||
// exec_jumper_L6B0<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L131_PART1_Z:
|
||||
// exec_jumper_L131<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L0x6BF_PART0_W:
|
||||
// exec_jumper_L6BF<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L132_PART1_W:
|
||||
// exec_jumper_L132<DEBUG>(in, vars);
|
||||
// break;
|
||||
// case L122_KICK:
|
||||
// exec_jumper_L122<DEBUG>(in, vars, render_state, prof);
|
||||
// break;
|
||||
// default:
|
||||
// assert(false);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
template <bool DEBUG>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include "game/kernel/Ptr.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -30,6 +31,7 @@ extern LinkedFunctionTable gLinkedFunctionTable;
|
||||
struct Rng {
|
||||
Rng() { init(); }
|
||||
float R = 0.;
|
||||
std::mt19937 extra_random_generator;
|
||||
|
||||
u32 R_u32() {
|
||||
u32 result;
|
||||
@@ -60,6 +62,11 @@ struct Rng {
|
||||
u32 y = 1 & (r32 >> 22);
|
||||
r32 <<= 1;
|
||||
r32 = r32 ^ x ^ y;
|
||||
// we add a bit of extra randomness here. They XOR the random number generator with an
|
||||
// uninitialized register, and in our port this corresponds to some random value on the stack.
|
||||
// If we get unlucky this can end up being the current value in r32, and the random generator
|
||||
// will get stuck outputting 1 for a while.
|
||||
r32 ^= extra_random_generator();
|
||||
R = from23_bits(r32);
|
||||
}
|
||||
|
||||
|
||||
@@ -255,10 +255,11 @@
|
||||
)
|
||||
;; set the level.
|
||||
(set! (-> (scratchpad-object terrain-context) bsp lev-index) (-> s1-0 index))
|
||||
)
|
||||
|
||||
(set! (-> *tfrag-work* min-dist z) 4095996000.0)
|
||||
;; draw!
|
||||
(draw-drawable-tree-tfrag s2-0)
|
||||
(draw-drawable-tree-tfrag s2-0 s1-0)
|
||||
)
|
||||
;; remember closest.
|
||||
(set! (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 0)
|
||||
(-> *tfrag-work* min-dist z)
|
||||
@@ -281,9 +282,10 @@
|
||||
)
|
||||
)
|
||||
(set! (-> (scratchpad-object terrain-context) bsp lev-index) (-> s1-1 index))
|
||||
)
|
||||
|
||||
(set! (-> *tfrag-work* min-dist z) 4095996000.0)
|
||||
(draw-drawable-tree-trans-tfrag s2-1)
|
||||
(draw-drawable-tree-trans-tfrag s2-1 s1-1)
|
||||
)
|
||||
(set! (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(-> *tfrag-work* min-dist z)
|
||||
)
|
||||
@@ -306,9 +308,10 @@
|
||||
)
|
||||
)
|
||||
(set! (-> (scratchpad-object terrain-context) bsp lev-index) (-> s1-2 index))
|
||||
)
|
||||
|
||||
(set! (-> *tfrag-work* min-dist z) 4095996000.0)
|
||||
(draw-drawable-tree-dirt-tfrag s2-2)
|
||||
(draw-drawable-tree-dirt-tfrag s2-2 s1-2)
|
||||
)
|
||||
(set! (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(fmin (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(-> *tfrag-work* min-dist z)
|
||||
@@ -333,9 +336,10 @@
|
||||
)
|
||||
)
|
||||
(set! (-> (scratchpad-object terrain-context) bsp lev-index) (-> s1-3 index))
|
||||
)
|
||||
|
||||
(set! (-> *tfrag-work* min-dist z) 4095996000.0)
|
||||
(draw-drawable-tree-ice-tfrag s2-3)
|
||||
(draw-drawable-tree-ice-tfrag s2-3 s1-3)
|
||||
)
|
||||
(set! (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(fmin (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(-> *tfrag-work* min-dist z)
|
||||
@@ -361,9 +365,10 @@
|
||||
)
|
||||
)
|
||||
(set! (-> (scratchpad-object terrain-context) bsp lev-index) (-> s1-4 index))
|
||||
)
|
||||
|
||||
;;(format 0 "draw ~A~%" s2-4)
|
||||
(draw-drawable-tree-tfrag s2-4)
|
||||
(draw-drawable-tree-tfrag s2-4 s1-4)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -382,9 +387,10 @@
|
||||
)
|
||||
)
|
||||
(set! (-> (scratchpad-object terrain-context) bsp lev-index) (-> s1-5 index))
|
||||
)
|
||||
|
||||
(set! (-> *tfrag-work* min-dist z) 4095996000.0)
|
||||
(draw-drawable-tree-trans-tfrag s2-5)
|
||||
(draw-drawable-tree-trans-tfrag s2-5 s1-5)
|
||||
)
|
||||
(set! (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(fmin (-> *level* level (-> (scratchpad-object terrain-context) bsp lev-index) closest-object 3)
|
||||
(-> *tfrag-work* min-dist z)
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
(color-count uint8 :offset 57)
|
||||
(pad0 uint8 :offset 58)
|
||||
(pad1 uint8 :offset 59)
|
||||
(generic generic-tfragment :offset-assert 60)
|
||||
(generic generic-tfragment :offset-assert 60)
|
||||
(generic-u32 uint32 :offset 60)
|
||||
)
|
||||
:method-count-assert 18
|
||||
:size-assert #x40
|
||||
@@ -256,7 +257,8 @@
|
||||
|
||||
(define-extern *tfrag-work* tfrag-work)
|
||||
|
||||
(define-extern draw-drawable-tree-tfrag (function drawable-tree-tfrag none))
|
||||
(define-extern draw-drawable-tree-trans-tfrag (function drawable-tree-trans-tfrag none))
|
||||
(define-extern draw-drawable-tree-dirt-tfrag (function drawable-tree-dirt-tfrag none))
|
||||
(define-extern draw-drawable-tree-ice-tfrag (function drawable-tree-ice-tfrag none))
|
||||
(define-extern draw-drawable-tree-tfrag (function drawable-tree-tfrag level none))
|
||||
(define-extern draw-drawable-tree-trans-tfrag (function drawable-tree-trans-tfrag level none))
|
||||
(define-extern draw-drawable-tree-dirt-tfrag (function drawable-tree-dirt-tfrag level none))
|
||||
(define-extern draw-drawable-tree-ice-tfrag (function drawable-tree-ice-tfrag level none))
|
||||
(define-extern tfrag-init-buffer (function dma-buffer gs-test int level none))
|
||||
@@ -43,8 +43,8 @@
|
||||
;; functions.
|
||||
|
||||
|
||||
(defun draw-drawable-tree-tfrag ((arg0 drawable-tree-tfrag))
|
||||
"Draw the normal tfrag tree!"
|
||||
(defun draw-drawable-tree-tfrag ((arg0 drawable-tree-tfrag) (lev level))
|
||||
"Draw the normal tfrag tree! Added the lev argument for time-of-day integer times"
|
||||
(local-vars (r0-0 none) (a0-20 int) (a0-22 int) (a0-38 int) (a0-40 int) (sv-16 (pointer uint8)))
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
s1-0
|
||||
(new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) :aref #x26 :zte #x1 :ztst (gs-ztest greater-equal))
|
||||
0
|
||||
lev
|
||||
)
|
||||
|
||||
;; do the draw!
|
||||
@@ -197,7 +198,7 @@
|
||||
)
|
||||
|
||||
|
||||
(defun draw-drawable-tree-trans-tfrag ((arg0 drawable-tree-trans-tfrag))
|
||||
(defun draw-drawable-tree-trans-tfrag ((arg0 drawable-tree-trans-tfrag) (lev level))
|
||||
(local-vars
|
||||
(r0-0 none)
|
||||
(a0-18 int)
|
||||
@@ -248,6 +249,7 @@
|
||||
:ztst (gs-ztest greater-equal)
|
||||
)
|
||||
1
|
||||
lev
|
||||
)
|
||||
(reset! (-> *perf-stats* data 5))
|
||||
(draw-inline-array-tfrag sv-16 s5-1 s4-1 s2-0)
|
||||
@@ -261,7 +263,7 @@
|
||||
(set! (-> (the-as dma-packet v1-34) vif1) (new 'static 'vif-tag))
|
||||
(set! (-> s2-0 base) (&+ (the-as pointer v1-34) 16))
|
||||
)
|
||||
#|
|
||||
|
||||
(dma-bucket-insert-tag
|
||||
(-> *display* frames (-> *display* on-screen) frame bucket-group)
|
||||
(the-as
|
||||
@@ -273,7 +275,7 @@
|
||||
)
|
||||
s3-0
|
||||
(the-as (pointer dma-tag) a3-3)
|
||||
)|#
|
||||
)
|
||||
)
|
||||
)
|
||||
#| TODO
|
||||
@@ -364,7 +366,7 @@
|
||||
)
|
||||
|
||||
|
||||
(defun draw-drawable-tree-dirt-tfrag ((arg0 drawable-tree-dirt-tfrag))
|
||||
(defun draw-drawable-tree-dirt-tfrag ((arg0 drawable-tree-dirt-tfrag) (lev level))
|
||||
(local-vars
|
||||
(r0-0 none)
|
||||
(a0-18 int)
|
||||
@@ -421,7 +423,7 @@
|
||||
)
|
||||
(set! (-> *tfrag-work* wait-to-spr) (the-as uint 0))
|
||||
(set! (-> *tfrag-work* wait-from-spr) (the-as uint 0))
|
||||
(tfrag-init-buffer s2-0 (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) 1)
|
||||
(tfrag-init-buffer s2-0 (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal)) 1 lev)
|
||||
(reset! (-> *perf-stats* data 5))
|
||||
|
||||
(draw-inline-array-tfrag sv-16 s5-1 s4-1 s2-0)
|
||||
@@ -539,7 +541,7 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
(defun draw-drawable-tree-ice-tfrag ((arg0 drawable-tree-ice-tfrag))
|
||||
(defun draw-drawable-tree-ice-tfrag ((arg0 drawable-tree-ice-tfrag) (lev level))
|
||||
(local-vars
|
||||
(r0-0 none)
|
||||
(a0-18 int)
|
||||
@@ -591,6 +593,7 @@
|
||||
:ztst (gs-ztest greater-equal)
|
||||
)
|
||||
1
|
||||
lev
|
||||
)
|
||||
(reset! (-> *perf-stats* data 5))
|
||||
(draw-inline-array-tfrag sv-16 s5-1 s4-1 s2-0)
|
||||
|
||||
@@ -319,6 +319,30 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
(defun add-pc-tfrag3-data ((dma-buf dma-buffer) (lev level))
|
||||
"Add PC-port specific tfrag data"
|
||||
(let ((packet (the-as dma-packet (-> dma-buf base))))
|
||||
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 9))
|
||||
(set! (-> packet vif0) (new 'static 'vif-tag))
|
||||
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
|
||||
(set! (-> dma-buf base) (the pointer (&+ packet 16)))
|
||||
)
|
||||
|
||||
;; first 4 quadwords are planes, then itimes
|
||||
(let ((data-ptr (the-as (pointer uint128) (-> dma-buf base))))
|
||||
(set! (-> data-ptr 0) (-> *math-camera* plane 0 quad))
|
||||
(set! (-> data-ptr 1) (-> *math-camera* plane 1 quad))
|
||||
(set! (-> data-ptr 2) (-> *math-camera* plane 2 quad))
|
||||
(set! (-> data-ptr 3) (-> *math-camera* plane 3 quad))
|
||||
(set! (-> data-ptr 4) (-> lev mood itimes 0 quad))
|
||||
(set! (-> data-ptr 5) (-> lev mood itimes 1 quad))
|
||||
(set! (-> data-ptr 6) (-> lev mood itimes 2 quad))
|
||||
(set! (-> data-ptr 7) (-> lev mood itimes 3 quad))
|
||||
(charp<-string (the (pointer uint8) (&-> data-ptr 8)) (symbol->string (-> lev nickname)))
|
||||
)
|
||||
(&+! (-> dma-buf base) (* 16 9))
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TFRAG Stats
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -365,7 +389,7 @@
|
||||
;; buffer
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun tfrag-init-buffer ((arg0 dma-buffer) (arg1 gs-test) (arg2 int))
|
||||
(defun tfrag-init-buffer ((arg0 dma-buffer) (arg1 gs-test) (arg2 int) (lev level))
|
||||
(dma-buffer-add-vu-function arg0 tfrag-vu1-block 1)
|
||||
(let* ((v1-0 arg0)
|
||||
(a0-2 (the-as object (-> v1-0 base)))
|
||||
@@ -411,6 +435,9 @@
|
||||
(add-tfrag-mtx-0 arg0)
|
||||
(add-tfrag-mtx-1 arg0)
|
||||
(add-tfrag-data arg0 arg2)
|
||||
(#when PC_PORT
|
||||
(add-pc-tfrag3-data arg0 lev)
|
||||
)
|
||||
(let ((v1-3 (the-as object (-> arg0 base))))
|
||||
(set! (-> (the-as dma-packet v1-3) dma) (new 'static 'dma-tag :id (dma-tag-id cnt)))
|
||||
(set! (-> (the-as dma-packet v1-3) vif0) (new 'static 'vif-tag :cmd (vif-cmd base)))
|
||||
|
||||
@@ -45,6 +45,9 @@ class Compiler {
|
||||
void run_full_compiler_on_string_no_save(const std::string& src);
|
||||
void shutdown_target();
|
||||
void enable_throw_on_redefines() { m_throw_on_define_extern_redefinition = true; }
|
||||
void add_ignored_define_extern_symbol(const std::string& name) {
|
||||
m_allow_inconsistent_definition_symbols.insert(name);
|
||||
}
|
||||
Debugger& get_debugger() { return m_debugger; }
|
||||
listener::Listener& listener() { return m_listener; }
|
||||
void poke_target() { m_listener.send_poke(); }
|
||||
@@ -75,6 +78,7 @@ class Compiler {
|
||||
std::unordered_map<goos::HeapObject*, LambdaVal*> m_inlineable_functions;
|
||||
CompilerSettings m_settings;
|
||||
bool m_throw_on_define_extern_redefinition = false;
|
||||
std::unordered_set<std::string> m_allow_inconsistent_definition_symbols;
|
||||
SymbolInfoMap m_symbol_info;
|
||||
std::unique_ptr<ReplWrapper> m_repl;
|
||||
MakeSystem m_make;
|
||||
|
||||
@@ -80,15 +80,19 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec
|
||||
|
||||
auto existing_type = m_symbol_types.find(symbol_string(sym));
|
||||
if (existing_type != m_symbol_types.end() && existing_type->second != new_type) {
|
||||
if (m_throw_on_define_extern_redefinition) {
|
||||
throw_compiler_error(form,
|
||||
"define-extern would redefine the type of symbol {} from {} to {}.",
|
||||
symbol_string(sym), existing_type->second.print(), new_type.print());
|
||||
} else {
|
||||
print_compiler_warning(
|
||||
"[Warning] define-extern has redefined the type of symbol {}\npreviously: {}\nnow: {}\n",
|
||||
symbol_string(sym).c_str(), existing_type->second.print().c_str(),
|
||||
new_type.print().c_str());
|
||||
if (m_allow_inconsistent_definition_symbols.find(symbol_string(sym)) ==
|
||||
m_allow_inconsistent_definition_symbols.end()) {
|
||||
if (m_throw_on_define_extern_redefinition) {
|
||||
throw_compiler_error(form,
|
||||
"define-extern would redefine the type of symbol {} from {} to {}.",
|
||||
symbol_string(sym), existing_type->second.print(), new_type.print());
|
||||
} else {
|
||||
print_compiler_warning(
|
||||
"[Warning] define-extern has redefined the type of symbol {}\npreviously: {}\nnow: "
|
||||
"{}\n",
|
||||
symbol_string(sym).c_str(), existing_type->second.print().c_str(),
|
||||
new_type.print().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ void FormRegressionTest::TestData::add_string_at_label(const std::string& label_
|
||||
|
||||
// add string type tag:
|
||||
LinkedWord type_tag(0);
|
||||
type_tag.kind = LinkedWord::Kind::TYPE_PTR;
|
||||
type_tag.symbol_name = "string";
|
||||
type_tag.set_to_symbol(decompiler::LinkedWord::TYPE_PTR, "string");
|
||||
file.words_by_seg.at(1).push_back(type_tag);
|
||||
int string_start = 4 * int(file.words_by_seg.at(1).size());
|
||||
|
||||
|
||||
+1
@@ -75,6 +75,7 @@
|
||||
(pad0 uint8 :offset 58)
|
||||
(pad1 uint8 :offset 59)
|
||||
(generic generic-tfragment :offset-assert 60)
|
||||
(generic-u32 uint32 :offset 60)
|
||||
)
|
||||
:method-count-assert 18
|
||||
:size-assert #x40
|
||||
|
||||
@@ -16,7 +16,7 @@ std::vector<u32> get_test_data(const std::string& name) {
|
||||
|
||||
std::vector<u32> data;
|
||||
for (auto& w : parsed.words) {
|
||||
EXPECT_EQ(w.kind, LinkedWord::Kind::PLAIN_DATA);
|
||||
EXPECT_EQ(w.kind(), LinkedWord::Kind::PLAIN_DATA);
|
||||
data.push_back(w.data);
|
||||
}
|
||||
return data;
|
||||
|
||||
@@ -923,9 +923,18 @@ TEST_F(WithGameTests, Mips2C_CallGoal) {
|
||||
{"1 2 3 4 5 6 7 8\n12\n"});
|
||||
}
|
||||
|
||||
void add_expected_type_mismatches(Compiler& c) {
|
||||
c.add_ignored_define_extern_symbol("draw-drawable-tree-tfrag");
|
||||
c.add_ignored_define_extern_symbol("draw-drawable-tree-trans-tfrag");
|
||||
c.add_ignored_define_extern_symbol("draw-drawable-tree-dirt-tfrag");
|
||||
c.add_ignored_define_extern_symbol("draw-drawable-tree-ice-tfrag");
|
||||
c.add_ignored_define_extern_symbol("tfrag-init-buffer");
|
||||
}
|
||||
|
||||
TEST(TypeConsistency, MANUAL_TEST_TypeConsistencyWithBuildFirst) {
|
||||
Compiler compiler;
|
||||
compiler.enable_throw_on_redefines();
|
||||
add_expected_type_mismatches(compiler);
|
||||
compiler.run_test_no_load("test/goalc/source_templates/with_game/test-build-game.gc");
|
||||
compiler.run_test_no_load("decompiler/config/all-types.gc");
|
||||
}
|
||||
@@ -933,6 +942,7 @@ TEST(TypeConsistency, MANUAL_TEST_TypeConsistencyWithBuildFirst) {
|
||||
TEST(TypeConsistency, TypeConsistency) {
|
||||
Compiler compiler;
|
||||
compiler.enable_throw_on_redefines();
|
||||
add_expected_type_mismatches(compiler);
|
||||
compiler.run_test_no_load("decompiler/config/all-types.gc");
|
||||
compiler.run_test_no_load("test/goalc/source_templates/with_game/test-build-game.gc");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
|
||||
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)
|
||||
target_link_libraries(level_dump fmt common decomp)
|
||||
@@ -4,9 +4,8 @@
|
||||
#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 "decompiler/util/goal_data_reader.h"
|
||||
#include "decompiler/level_extractor/BspHeader.h"
|
||||
|
||||
#include "common/util/assert.h"
|
||||
|
||||
@@ -39,13 +38,13 @@ bool is_valid_bsp(const decompiler::LinkedObjectFile& file) {
|
||||
}
|
||||
|
||||
auto& first_word = file.words_by_seg.at(0).at(0);
|
||||
if (first_word.kind != decompiler::LinkedWord::TYPE_PTR) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user