mirror of
https://github.com/open-goal/jak-project
synced 2026-09-03 18:23:39 -04:00
[sparticle] 2d hud particles (#849)
* wip, taking a break to work on asm stuff first * the goal code for sparticle * mips2c the first sparticle asm function * temp * particle processing no longer crashing * temp * working texture cache for vi1 and hud textures * sprites * cleanup 1 * temp * temp * add zstd library * temp * working * tests * include fix * uncomment * better decomp of sparticle stuff, part 1 * update references
This commit is contained in:
@@ -24,6 +24,10 @@ savestate-out/
|
||||
failures/
|
||||
ee-results.json
|
||||
|
||||
# graphics debug
|
||||
debug_out/*
|
||||
gfx_dumps/*
|
||||
|
||||
# game stuff
|
||||
game_config/*
|
||||
imgui.ini
|
||||
|
||||
+2
-1
@@ -27,7 +27,8 @@ if(UNIX)
|
||||
-Woverloaded-virtual \
|
||||
-Wredundant-decls \
|
||||
-Wshadow \
|
||||
-Wsign-promo")
|
||||
-Wsign-promo \
|
||||
-fdiagnostics-color=always")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "/EHsc")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10000000")
|
||||
|
||||
@@ -78,6 +78,21 @@ class Vector {
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<T, Size>& operator+=(const Vector<T, Size>& other) {
|
||||
for (int i = 0; i < Size; i++) {
|
||||
m_data[i] += other[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector<T, Size> elementwise_multiply(const Vector<T, Size>& other) const {
|
||||
Vector<T, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = m_data[i] * other[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<T, Size> operator-(const Vector<T, Size>& other) const {
|
||||
Vector<T, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
@@ -114,6 +129,13 @@ class Vector {
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<T, Size>& operator*=(const T& val) {
|
||||
for (int i = 0; i < Size; i++) {
|
||||
m_data[i] *= val;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector<T, Size> cross(const Vector<T, Size>& other) const {
|
||||
static_assert(Size == 3, "Size for cross");
|
||||
Vector<T, Size> result = {y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(),
|
||||
@@ -150,6 +172,34 @@ class Vector {
|
||||
T m_data[Size];
|
||||
};
|
||||
|
||||
// column major
|
||||
template <typename T, int Rows, int Cols>
|
||||
struct Matrix {
|
||||
Matrix() = default;
|
||||
|
||||
static Matrix zero() {
|
||||
Matrix result;
|
||||
for (auto& x : result.m_data) {
|
||||
x = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const T& operator()(int r, int c) const { return m_data[c + r * Cols]; }
|
||||
T& operator()(int r, int c) { return m_data[r + c * Rows]; }
|
||||
|
||||
Vector<T, Rows> col(int c) const {
|
||||
Vector<T, Rows> result;
|
||||
for (int i = 0; i < Rows; i++) {
|
||||
result[i] = m_data[c * Rows + i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_data[Rows * Cols];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using Vector2 = Vector<T, 2>;
|
||||
|
||||
@@ -165,4 +215,6 @@ using Vector4f = Vector4<float>;
|
||||
using Vector2d = Vector2<double>;
|
||||
using Vector3d = Vector3<double>;
|
||||
using Vector4d = Vector4<double>;
|
||||
|
||||
using Matrix4f = Matrix<float, 4, 4>;
|
||||
} // namespace math
|
||||
|
||||
@@ -207,9 +207,12 @@ inline u32 rgba16_to_rgba32(u32 in) {
|
||||
u32 r = (in & 0b11111) * ratio;
|
||||
u32 g = ((in >> 5) & 0b11111) * ratio;
|
||||
u32 b = ((in >> 10) & 0b11111) * ratio;
|
||||
u32 a = (in & 0x8000) * 0x1FE00;
|
||||
|
||||
return a | (b << 16) | (g << 8) | r;
|
||||
// rgba has only 1 bit for a and how it gets converted depends on the value of ta1.
|
||||
// for now, it looks like they always use 0x80, so this is fine.
|
||||
u32 a = (in & 0x8000) ? 0x80 : 0;
|
||||
|
||||
return (a << 24) | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
// texture format enums
|
||||
|
||||
@@ -64,3 +64,23 @@ std::optional<int> get_power_of_two(T in) {
|
||||
|
||||
bool integer_fits(s64 in, int size, bool is_signed);
|
||||
u32 float_as_u32(float x);
|
||||
|
||||
template <typename T>
|
||||
T align16(T in) {
|
||||
return (in + 15) & (~T(15));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T align8(T in) {
|
||||
return (in + 7) & (~T(7));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T align4(T in) {
|
||||
return (in + 3) & (~T(3));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T align64(T in) {
|
||||
return (in + 63) & (~T(63));
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/util/assert.h"
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/*!
|
||||
* The Serializer is a tool to load or save data from a buffer.
|
||||
* It's currently used to save graphics dumps, but could also be used for savestates in the future.
|
||||
*
|
||||
* The serializer can be constructed in either a "save" or a "load" mode, for saving or loading.
|
||||
* When saving, it copies stuff into a buffer.
|
||||
* When loading, it copies stuff out of this buffer. This will only work if you do things in
|
||||
* exactly the same order. To make this easier, most of the commonly used functions can work for
|
||||
* either saving or loading, so you can have a method like
|
||||
*
|
||||
* void MyObject::serialize(Serializer& ser) {
|
||||
* ser.from_ptr(&m_foo);
|
||||
* ser.from_ptr(&m_bar);
|
||||
* }
|
||||
*
|
||||
* and it will work for either saving or loading.
|
||||
*
|
||||
* Methods that are named like "save_" can only be used in save mode.
|
||||
*
|
||||
* Methods like "from_ptr" can work in either save or load mode, and will work in either.
|
||||
* In general, you can only use most of these on POD, and saving more complicated data structures
|
||||
* requires special handling.
|
||||
*/
|
||||
class Serializer {
|
||||
public:
|
||||
/*!
|
||||
* Construct a serializer in writing mode. This saves data from the program into a buffer that can
|
||||
* later be accessed with get_save_result.
|
||||
*/
|
||||
Serializer() : m_writing(true) {
|
||||
const size_t initial_size = 32;
|
||||
m_data = (u8*)malloc(initial_size);
|
||||
m_size = initial_size;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Construct a serializer that reads from the given data.
|
||||
* The data is copied to an internal buffer managed by the serializer, there is no need to keep
|
||||
* the input data around.
|
||||
*/
|
||||
Serializer(const u8* data, size_t size) : m_size(size), m_writing(false) {
|
||||
m_data = (u8*)malloc(size);
|
||||
memcpy(m_data, data, size);
|
||||
}
|
||||
|
||||
// don't allow copying, assigning, or move constructing.
|
||||
Serializer(const Serializer& other) = delete;
|
||||
Serializer& operator=(const Serializer& other) = delete;
|
||||
Serializer(Serializer&& other) = delete;
|
||||
|
||||
// move assignment is supported.
|
||||
Serializer& operator=(Serializer&& other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
m_offset = other.m_offset;
|
||||
m_writing = other.m_writing;
|
||||
|
||||
other.m_data = nullptr;
|
||||
other.m_size = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Serializer() { free(m_data); }
|
||||
|
||||
/*!
|
||||
* Save or load the thing pointed to by ptr.
|
||||
* T must be POD.
|
||||
*/
|
||||
template <typename T>
|
||||
void from_ptr(T* ptr) {
|
||||
read_or_write(ptr, sizeof(T));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Save or load size bytes from ptr.
|
||||
*/
|
||||
void from_raw_data(void* ptr, size_t size) { read_or_write(ptr, size); }
|
||||
|
||||
/*!
|
||||
* Load a T. T should be POD.
|
||||
*/
|
||||
template <typename T>
|
||||
T load() {
|
||||
assert(!m_writing);
|
||||
T result;
|
||||
read_or_write(&result, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Save a T. T should be POD.
|
||||
*/
|
||||
template <typename T>
|
||||
void save(const T& thing) {
|
||||
assert(m_writing);
|
||||
read_or_write(const_cast<T*>(&thing), sizeof(T));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Save or load a string.
|
||||
*/
|
||||
void from_str(std::string* str) {
|
||||
// size, then data.
|
||||
if (is_loading()) {
|
||||
str->resize(load<size_t>());
|
||||
} else {
|
||||
save<size_t>(str->size());
|
||||
}
|
||||
from_raw_data(str->data(), str->size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Save a std::string. This is just so you can save a const string without needing to copy or do a
|
||||
* const_cast.
|
||||
*/
|
||||
void save_str(const std::string* str) {
|
||||
assert(is_saving());
|
||||
// safe, we're saving, so from_str will only read.
|
||||
from_str(const_cast<std::string*>(str));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Load a std::string and return it.
|
||||
*/
|
||||
std::string load_string() {
|
||||
assert(is_loading());
|
||||
std::string s;
|
||||
from_str(&s);
|
||||
return s;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Save or load a vector of POD. This won't work on vectors of vectors, for example.
|
||||
*/
|
||||
template <typename T>
|
||||
void from_pod_vector(std::vector<T>* vec) {
|
||||
if (is_saving()) {
|
||||
save<size_t>(vec->size());
|
||||
} else {
|
||||
vec->resize(load<size_t>());
|
||||
}
|
||||
from_raw_data(vec->data(), vec->size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Are we saving?
|
||||
*/
|
||||
bool is_saving() const { return m_writing; }
|
||||
|
||||
/*!
|
||||
* Are we loading?
|
||||
*/
|
||||
bool is_loading() const { return !m_writing; }
|
||||
|
||||
/*!
|
||||
* Reset a load back to the beginning.
|
||||
*/
|
||||
void reset_load() {
|
||||
assert(is_loading());
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the result of the save. This is a view of the buffer owned by the Serializer.
|
||||
*/
|
||||
std::pair<const u8*, size_t> get_save_result() {
|
||||
assert(m_writing);
|
||||
return {m_data, m_offset};
|
||||
}
|
||||
|
||||
/*!
|
||||
* Have we reached the end of the load?
|
||||
*/
|
||||
bool get_load_finished() const {
|
||||
assert(!m_writing);
|
||||
return m_offset == m_size;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Size of buffer, in bytes.
|
||||
*/
|
||||
size_t data_size() const { return m_size; }
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Main function to read and write the buffer.
|
||||
*/
|
||||
void read_or_write(void* data, size_t size) {
|
||||
if (m_writing) {
|
||||
// if we would overflow, just resize the buffer.
|
||||
if (m_offset + size > m_size) {
|
||||
m_data = (u8*)realloc(m_data, (m_offset + size) * 2);
|
||||
}
|
||||
memcpy(m_data + m_offset, data, size);
|
||||
} else {
|
||||
// if we would overflow, it's an error.
|
||||
assert(m_offset + size <= m_size);
|
||||
memcpy(data, m_data + m_offset, size);
|
||||
}
|
||||
m_offset += size;
|
||||
}
|
||||
|
||||
u8* m_data = nullptr;
|
||||
size_t m_size = 0;
|
||||
size_t m_offset = 0;
|
||||
bool m_writing = false;
|
||||
};
|
||||
@@ -6,12 +6,16 @@
|
||||
#include "common/util/assert.h"
|
||||
|
||||
namespace compression {
|
||||
|
||||
/*!
|
||||
* Compress data with zstd. There is an 8-byte header containing the decompressed data's size.
|
||||
*/
|
||||
std::vector<u8> compress_zstd(const void* data, size_t size) {
|
||||
auto max_compressed = ZSTD_compressBound(size);
|
||||
std::vector<u8> result(sizeof(size_t) + max_compressed);
|
||||
memcpy(result.data(), &size, sizeof(size_t));
|
||||
auto compressed_size = ZSTD_compress(result.data() + sizeof(size_t), max_compressed, data, size,
|
||||
ZSTD_CLEVEL_DEFAULT);
|
||||
auto compressed_size =
|
||||
ZSTD_compress(result.data() + sizeof(size_t), max_compressed, data, size, 1);
|
||||
if (ZSTD_isError(compressed_size)) {
|
||||
printf("ZSTD error: %s\n", ZSTD_getErrorName(compressed_size));
|
||||
assert(false);
|
||||
@@ -20,6 +24,10 @@ std::vector<u8> compress_zstd(const void* data, size_t size) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Decompress data with zstd. The first 8-bytes of the data should be a header containing the
|
||||
* decompressed data's size.
|
||||
*/
|
||||
std::vector<u8> decompress_zstd(const void* data, size_t size) {
|
||||
assert(size >= sizeof(size_t));
|
||||
size_t decompressed_size;
|
||||
@@ -37,4 +45,4 @@ std::vector<u8> decompress_zstd(const void* data, size_t size) {
|
||||
assert(decomp_size == decompressed_size);
|
||||
return result;
|
||||
}
|
||||
} // namespace compression
|
||||
} // namespace compression
|
||||
|
||||
@@ -62,6 +62,7 @@ add_library(
|
||||
util/data_decompile.cpp
|
||||
util/DataParser.cpp
|
||||
util/DecompilerTypeSystem.cpp
|
||||
util/sparticle_decompile.cpp
|
||||
util/TP_Type.cpp
|
||||
|
||||
VuDisasm/VuDisassembler.cpp
|
||||
|
||||
@@ -302,12 +302,13 @@ void init_opcode_info() {
|
||||
drd_srs_srt(def(IK::PCPYLD, "pcpyld").gpr128()); // Parallel Copy Lower Doubleword
|
||||
drd_srs_srt(def(IK::PMADDH, "pmaddh").gpr128()); // Parallel Multiply-Add Halfword
|
||||
drd_srs_srt(def(IK::PMULTH, "pmulth").gpr128()); // Parallel Multiply Halfword
|
||||
drd_srs_srt(def(IK::PEXEW, "pexew").gpr128()); // Parallel Exchange Even Word
|
||||
drd_srs_srt(def(IK::PINTEH, "pinteh").gpr128()); // Parallel Interleave Even Halfword
|
||||
drd_srs_srt(def(IK::PAND, "pand").gpr128()); // Parallel And
|
||||
drd_srs_srt(def(IK::POR, "por").gpr128()); // Parallel Or
|
||||
drd_srs_srt(def(IK::PNOR, "pnor").gpr128()); // Parallel Not Or
|
||||
|
||||
def(IK::PEXEW, "pexew").gpr128().dst_gpr(FT::RD).src_gpr(FT::RT); // Parallel Exchange Even Word
|
||||
|
||||
drd_srt_ssa(def(IK::PSLLW, "psllw").gpr128()); // Parallel Shift Left Logical Word
|
||||
drd_srt_ssa(def(IK::PSLLH, "psllh").gpr128()); // Parallel Shift Left Logical Halfword
|
||||
drd_srt_ssa(def(IK::PSRAW, "psraw").gpr128()); // Parallel Shift Right Arithmetic Word
|
||||
|
||||
@@ -59,6 +59,18 @@ void CfgVtx::replace_succ_and_check(CfgVtx* old_succ, CfgVtx* new_succ) {
|
||||
assert(replaced);
|
||||
}
|
||||
|
||||
void CfgVtx::remove_pred(CfgVtx* to_remove) {
|
||||
bool found = false;
|
||||
for (auto it = pred.begin(); it != pred.end(); it++) {
|
||||
if (*it == to_remove) {
|
||||
pred.erase(it);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Replace references to old_preds with a single new_pred.
|
||||
* Doesn't insert duplicates.
|
||||
@@ -477,7 +489,15 @@ bool ControlFlowGraph::is_while_loop(CfgVtx* b0, CfgVtx* b1, CfgVtx* b2) {
|
||||
if (!b0 || !b1 || !b2)
|
||||
return false;
|
||||
|
||||
bool debug = b0->to_string() == "Seq CONDNE104 ... Block 18100";
|
||||
|
||||
if (debug) {
|
||||
fmt::print("try while: {} | {} | {}\n", b0->to_string(), b1->to_string(), b2->to_string());
|
||||
}
|
||||
|
||||
if (b0->end_branch.asm_branch || b1->end_branch.asm_branch) {
|
||||
if (debug)
|
||||
fmt::print("reject 1 {} {}\n", b0->end_branch.asm_branch, b1->end_branch.asm_branch);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1101,10 +1121,19 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!b0->end_branch.asm_branch) {
|
||||
if (!b0->end_branch.asm_branch || !b0->end_branch.has_branch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (b1->succ_branch == b1) {
|
||||
// asm branch to yourself. just remove it.
|
||||
b1->succ_branch = nullptr;
|
||||
b1->end_branch.has_branch = false;
|
||||
b1->remove_pred(b1);
|
||||
replaced = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't want to combine two with an incoming edge in between.
|
||||
if (b1->pred.size() > 1) {
|
||||
return true;
|
||||
@@ -2094,6 +2123,10 @@ bool ControlFlowGraph::find_cond_n_else() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prev_condition->end_branch.asm_branch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// prev_body should fall through to end todo - this was wrong?
|
||||
if (prev_body->succ_ft != end_block) {
|
||||
printf("reject 7\n");
|
||||
|
||||
@@ -127,6 +127,8 @@ class CfgVtx {
|
||||
void replace_succ_and_check(CfgVtx* old_succ, CfgVtx* new_succ);
|
||||
void replace_preds_with_and_check(std::vector<CfgVtx*> old_preds, CfgVtx* new_pred);
|
||||
|
||||
void remove_pred(CfgVtx* to_remove);
|
||||
|
||||
std::string links_to_string();
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "TypeInspector.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
#include "decompiler/IR2/Form.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
|
||||
namespace decompiler {
|
||||
namespace {
|
||||
@@ -30,18 +31,6 @@ Register get_expected_fpr_backup(int n, int total) {
|
||||
return fpr_backups.at((total - 1) - n);
|
||||
}
|
||||
|
||||
uint32_t align16(uint32_t in) {
|
||||
return (in + 15) & (~15);
|
||||
}
|
||||
|
||||
uint32_t align8(uint32_t in) {
|
||||
return (in + 7) & (~7);
|
||||
}
|
||||
|
||||
uint32_t align4(uint32_t in) {
|
||||
return (in + 3) & (~3);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Function::Function(int _start_word, int _end_word) : start_word(_start_word), end_word(_end_word) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "decompiler/IR2/bitfields.h"
|
||||
#include "common/type_system/state.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
@@ -319,6 +320,7 @@ TP_Type get_stack_type_at_constant_offset(int offset,
|
||||
throw std::runtime_error(
|
||||
fmt::format("Failed to find a stack variable or structure at offset {}", offset));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
@@ -601,9 +603,16 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
|
||||
if (m_kind == Kind::ADD && tc(dts, TypeSpec("structure"), arg0_type) &&
|
||||
arg1_type.is_integer_constant()) {
|
||||
auto type_info = dts.ts.lookup_type(arg0_type.typespec());
|
||||
|
||||
// get next in memory, allow this as &+
|
||||
if ((u64)type_info->get_size_in_memory() == arg1_type.get_integer_constant()) {
|
||||
return TP_Type::make_from_ts(arg0_type.typespec());
|
||||
}
|
||||
|
||||
// also allow it, if 16-byte aligned stride.
|
||||
if ((u64)align16(type_info->get_size_in_memory()) == arg1_type.get_integer_constant()) {
|
||||
return TP_Type::make_from_ts(arg0_type.typespec());
|
||||
}
|
||||
}
|
||||
|
||||
if (tc(dts, TypeSpec("structure"), arg1_type) && !m_args[0].is_int() &&
|
||||
@@ -663,6 +672,11 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
|
||||
}
|
||||
}
|
||||
|
||||
// allow shifting stuff for setting bitfields
|
||||
if (m_kind == Kind::LEFT_SHIFT) {
|
||||
return TP_Type::make_from_ts("int");
|
||||
}
|
||||
|
||||
throw std::runtime_error(fmt::format("Cannot get_type_int2: {}, args {} and {}",
|
||||
to_form(env.file->labels, env).print(), arg0_type.print(),
|
||||
arg1_type.print()));
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "decompiler/config.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "common/link_types.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
|
||||
namespace decompiler {
|
||||
// There are three link versions:
|
||||
@@ -197,14 +198,6 @@ static uint32_t c_symlink3(LinkedObjectFile& f,
|
||||
return link_ptr + 1;
|
||||
}
|
||||
|
||||
static uint32_t align64(uint32_t in) {
|
||||
return (in + 63) & (~63);
|
||||
}
|
||||
|
||||
static uint32_t align16(uint32_t in) {
|
||||
return (in + 15) & (~15);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Process link data for a "V4" or "V2" object file.
|
||||
* In reality a V4 seems to be just a V2 object, but with the link data after the real data.
|
||||
|
||||
@@ -126,7 +126,7 @@ VuDisassembler::VuDisassembler() {
|
||||
add_op(VuInstrK::MUL, "mul").iemdt().dst_mask().dss_fd_fs_ft();
|
||||
add_op(VuInstrK::MULq, "mul").iemdt().dst_mask().dst_vfd().src_vfs().src_q().vft_zero();
|
||||
add_op(VuInstrK::SUB, "sub").iemdt().dst_mask().dss_fd_fs_ft();
|
||||
add_op(VuInstrK::MSUBbc, "msub").iemdt().dst_mask().dss_fd_fs_ft();
|
||||
add_op(VuInstrK::MSUBbc, "msub").iemdt().dst_mask().dss_fd_fs_ft().bc();
|
||||
add_op(VuInstrK::MADDA, "madda").iemdt().dst_mask().dst_acc().src_vfs().src_vft();
|
||||
add_op(VuInstrK::MULA, "mula").iemdt().dst_mask().dst_acc().src_vfs().src_vft();
|
||||
add_op(VuInstrK::MINIbc, "mini").iemdt().dst_mask().bc().dss_fd_fs_ft();
|
||||
|
||||
+297
-47
@@ -1,12 +1,31 @@
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "mips2c.h"
|
||||
|
||||
#include "common/util/print_float.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
/*!
|
||||
* Mips2C:
|
||||
* The mips2c analysis pass converts mips assembly into C code. It is a very literal translation.
|
||||
* This relies on the helper functions in mips2c_private.h header.
|
||||
*
|
||||
* We generate a "link" function and an "execute" function. The "link" function performs symbol
|
||||
* table lookups and saves the address of the slots in a cache structure used during runtime. It
|
||||
* also allocates a stub function on the GOAL heap that jumps to the C++ function in the proper way.
|
||||
*
|
||||
* The "link" function should be called by the linker when the appropriate object file is linked.
|
||||
* It should happen after GOAL linking, but before executing the top-level segment.
|
||||
* This _only_ allocates a function, but doesn't set the symbol.
|
||||
* You have to do that yourself, in the top level.
|
||||
* This order seems weird and annoying, but it makes sure that we get the order of allocations
|
||||
* right. It's likely that nothing depends on this, but better to be safe.
|
||||
*
|
||||
* The "execute" function is the function that should be called from GOAL.
|
||||
*/
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
//////////////////////
|
||||
@@ -45,6 +64,10 @@ Register make_vf(int idx) {
|
||||
return Register(Reg::VF, idx);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a GOAL symbol name to a valid C++ variable name.
|
||||
* dashes become underscores, and !/?/ * are dropped.
|
||||
*/
|
||||
std::string goal_to_c_name(const std::string& name) {
|
||||
std::string result;
|
||||
for (auto c : name) {
|
||||
@@ -61,6 +84,9 @@ std::string goal_to_c_name(const std::string& name) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a decompiler function name to a valid C++ variable name.
|
||||
*/
|
||||
std::string goal_to_c_function_name(const FunctionName& name) {
|
||||
switch (name.kind) {
|
||||
case FunctionName::FunctionKind::GLOBAL:
|
||||
@@ -70,11 +96,17 @@ std::string goal_to_c_function_name(const FunctionName& name) {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a decompiler register into the name of the register constant in mips2c_private.h
|
||||
*/
|
||||
const char* reg_to_name(const InstructionAtom& atom) {
|
||||
assert(atom.is_reg());
|
||||
return atom.get_reg().to_charp();
|
||||
}
|
||||
|
||||
/*!
|
||||
* A line of code in the mips2c function output. Just code + end of line comment.
|
||||
*/
|
||||
struct Mips2C_Line {
|
||||
std::string code;
|
||||
std::string comment;
|
||||
@@ -85,14 +117,32 @@ struct Mips2C_Line {
|
||||
: code(_code), comment(_comment) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Mips2C output. Contains the execute and link function.
|
||||
* This is built up in the order of MIPS instructions/labels/comments using the output_* functions.
|
||||
* The output is built by write_to_string.
|
||||
*/
|
||||
struct Mips2C_Output {
|
||||
/*!
|
||||
* Add a label at the current line.
|
||||
*/
|
||||
void output_label(int block_idx) { lines.push_back(fmt::format("\nblock_{}:", block_idx)); }
|
||||
|
||||
/*!
|
||||
* Add a full line comment at the current line. Includes "//" automatically
|
||||
*/
|
||||
void output_line_comment(const std::string& text) { lines.emplace_back("// " + text); }
|
||||
|
||||
/*!
|
||||
* Output code and comment for an instruction.
|
||||
*/
|
||||
void output_instr(const std::string& instr, const std::string& comment) {
|
||||
lines.emplace_back(instr, comment);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert the output to a string.
|
||||
*/
|
||||
std::string write_to_string(const FunctionName& goal_func_name) const {
|
||||
std::string name = goal_to_c_function_name(goal_func_name);
|
||||
std::string result = "//--------------------------MIPS2C---------------------\n";
|
||||
@@ -103,6 +153,7 @@ struct Mips2C_Output {
|
||||
result += "namespace Mips2C {\n";
|
||||
result += fmt::format("namespace {} {{\n", name);
|
||||
|
||||
// definition of the symbol cache.
|
||||
if (!symbol_cache.empty()) {
|
||||
result += "struct Cache {\n";
|
||||
for (auto& sym : symbol_cache) {
|
||||
@@ -111,9 +162,23 @@ struct Mips2C_Output {
|
||||
result += "} cache;\n\n";
|
||||
}
|
||||
|
||||
// definition of the function
|
||||
// the mips2c_call function will build and pass an ExecutionContext
|
||||
result += "u64 execute(void* ctxt) {\n";
|
||||
result += " auto* c = (ExecutionContext*)ctxt;\n";
|
||||
result += " bool bc = false;";
|
||||
|
||||
// the branch condition (for delay slots)
|
||||
result += " bool bc = false;\n";
|
||||
|
||||
// the function call address (for jalr delay slots)
|
||||
result += " u32 call_addr = 0;\n";
|
||||
|
||||
if (needs_cop1_bc) {
|
||||
// the cop1 branch flag (separate from delay slot bc).
|
||||
result += " bool cop1_bc = false;\n";
|
||||
}
|
||||
|
||||
// add all lines
|
||||
for (auto& line : lines) {
|
||||
result += " ";
|
||||
result += line.code;
|
||||
@@ -130,14 +195,17 @@ struct Mips2C_Output {
|
||||
result += '\n';
|
||||
}
|
||||
|
||||
// return!
|
||||
result += "end_of_function:\n return c->gprs[v0].du64[0];\n";
|
||||
result += "}\n\n";
|
||||
|
||||
// link function:
|
||||
result += "void link() {\n";
|
||||
// lookup all symbols
|
||||
for (auto& sym : symbol_cache) {
|
||||
result += fmt::format(" cache.{} = intern_from_c(\"{}\").c();\n", goal_to_c_name(sym), sym);
|
||||
}
|
||||
// this adds us to a table for lookup later, and also allocates our trampoline.
|
||||
result +=
|
||||
fmt::format(" gLinkedFunctionTable.reg(\"{}\", execute);\n", goal_func_name.to_string());
|
||||
result += "}\n\n";
|
||||
@@ -145,6 +213,7 @@ struct Mips2C_Output {
|
||||
result += fmt::format("}} // namespace {}\n", name);
|
||||
result += "} // namespace Mips2C\n";
|
||||
|
||||
// reminder to the user to add a callback to the link function in the linker.
|
||||
result +=
|
||||
fmt::format("// add {}::link to the link callback table for the object file.\n", name);
|
||||
result += "// FWD DEC:\n";
|
||||
@@ -152,24 +221,31 @@ struct Mips2C_Output {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Adds name to the symbol cache, if it's not there already.
|
||||
*/
|
||||
void require_symbol(const std::string& name) { symbol_cache.insert(name); }
|
||||
|
||||
std::vector<Mips2C_Line> lines;
|
||||
std::set<std::string> symbol_cache;
|
||||
bool needs_cop1_bc = false;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Basic block used for mips2c.
|
||||
*/
|
||||
struct M2C_Block {
|
||||
int idx = -1;
|
||||
int succ_branch = -1;
|
||||
int succ_ft = -1;
|
||||
std::vector<int> pred;
|
||||
int idx = -1; // block idx
|
||||
int succ_branch = -1; // block idx if we take the branch
|
||||
int succ_ft = -1; // block idx if we don't take the branch (or there is none)
|
||||
std::vector<int> pred; // block idx of predecessors
|
||||
|
||||
int start_instr = -1;
|
||||
int end_instr = -1;
|
||||
int start_instr = -1; // first instruction idx
|
||||
int end_instr = -1; // last instruction idx (not inclusive)
|
||||
|
||||
bool has_branch = false;
|
||||
bool branch_likely = false;
|
||||
bool branch_always = false;
|
||||
bool has_branch = false; // ends in a branch instruction?
|
||||
bool branch_likely = false; // that branch is likely branch?
|
||||
bool branch_always = false; // that branch is always taken?
|
||||
|
||||
bool has_pred(int pidx) const {
|
||||
for (auto p : pred) {
|
||||
@@ -181,6 +257,9 @@ struct M2C_Block {
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Make second_idx be a fallthrough of first_idx.
|
||||
*/
|
||||
void link_fall_through(int first_idx, int second_idx, std::vector<M2C_Block>& blocks) {
|
||||
auto& first = blocks.at(first_idx);
|
||||
auto& second = blocks.at(second_idx);
|
||||
@@ -198,6 +277,9 @@ void link_fall_through(int first_idx, int second_idx, std::vector<M2C_Block>& bl
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Make second_idx be the branch destination of first_idx.
|
||||
*/
|
||||
void link_branch(int first_idx, int second_idx, std::vector<M2C_Block>& blocks) {
|
||||
auto& first = blocks.at(first_idx);
|
||||
auto& second = blocks.at(second_idx);
|
||||
@@ -210,6 +292,9 @@ void link_branch(int first_idx, int second_idx, std::vector<M2C_Block>& blocks)
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Make second_idx be the fall through of a likely branch (after the delay slot)
|
||||
*/
|
||||
void link_fall_through_likely(int first_idx, int second_idx, std::vector<M2C_Block>& blocks) {
|
||||
auto& first = blocks.at(first_idx);
|
||||
auto& second = blocks.at(second_idx);
|
||||
@@ -236,7 +321,9 @@ void link_fall_through_likely(int first_idx, int second_idx, std::vector<M2C_Blo
|
||||
* Otherwise, has_branch is true, and the succ_branch is taken if the branch condition is true.
|
||||
* The succ_ft and succ_branch may be _anywhere_. succ_ft may not always be the next destination.
|
||||
*/
|
||||
std::vector<M2C_Block> setup_preds_and_succs(const Function& func, const LinkedObjectFile& file) {
|
||||
std::vector<M2C_Block> setup_preds_and_succs(const Function& func,
|
||||
const LinkedObjectFile& file,
|
||||
std::unordered_set<int>& likely_delay_slot_blocks) {
|
||||
// create m2c blocks
|
||||
std::vector<M2C_Block> blocks;
|
||||
blocks.resize(func.basic_blocks.size());
|
||||
@@ -249,7 +336,13 @@ std::vector<M2C_Block> setup_preds_and_succs(const Function& func, const LinkedO
|
||||
// set up succ / pred
|
||||
for (int i = 0; i < int(func.basic_blocks.size()); i++) {
|
||||
auto& b = func.basic_blocks[i];
|
||||
assert(!blocks.at(i).branch_always);
|
||||
if (blocks.at(i).branch_always) {
|
||||
// likely branch, already set up.
|
||||
assert(likely_delay_slot_blocks.count(i));
|
||||
continue;
|
||||
} else {
|
||||
assert(!likely_delay_slot_blocks.count(i));
|
||||
}
|
||||
bool not_last = (i + 1) < int(func.basic_blocks.size());
|
||||
|
||||
if (b.end_word == b.start_word) {
|
||||
@@ -306,7 +399,8 @@ std::vector<M2C_Block> setup_preds_and_succs(const Function& func, const LinkedO
|
||||
delay_block.branch_likely = false;
|
||||
delay_block.branch_always = true;
|
||||
delay_block.has_branch = true;
|
||||
// delay_block.kind = CfgVtx::DelaySlotKind::NO_DELAY;
|
||||
auto inserted = likely_delay_slot_blocks.insert(i + 1).second;
|
||||
assert(inserted);
|
||||
link_branch(i + 1, block_target, blocks);
|
||||
|
||||
} else {
|
||||
@@ -320,11 +414,9 @@ std::vector<M2C_Block> setup_preds_and_succs(const Function& func, const LinkedO
|
||||
int idx = b.end_word - 2;
|
||||
assert(idx >= b.start_word);
|
||||
auto& branch_candidate = func.instructions.at(idx);
|
||||
// auto& delay_slot_candidate = func.instructions.at(idx + 1);
|
||||
if (is_branch(branch_candidate, false)) {
|
||||
blocks.at(i).has_branch = true;
|
||||
blocks.at(i).branch_likely = false;
|
||||
// blocks.at(i).kind = get_delay_slot(delay_slot_candidate);
|
||||
bool branch_always = is_always_branch(branch_candidate);
|
||||
|
||||
// need to find block target
|
||||
@@ -337,10 +429,6 @@ std::vector<M2C_Block> setup_preds_and_succs(const Function& func, const LinkedO
|
||||
int offset = label.offset / 4 - func.start_word;
|
||||
assert(offset >= 0);
|
||||
|
||||
// the order here matters when there are zero size blocks. Unclear what the best answer
|
||||
// is.
|
||||
// i think in end it doesn't actually matter??
|
||||
// for (int j = 0; j < int(func.basic_blocks.size()); j++) {
|
||||
for (int j = int(func.basic_blocks.size()); j-- > 0;) {
|
||||
if (func.basic_blocks[j].start_word == offset) {
|
||||
block_target = j;
|
||||
@@ -373,6 +461,9 @@ std::vector<M2C_Block> setup_preds_and_succs(const Function& func, const LinkedO
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Does the given block require a label in front of it?
|
||||
*/
|
||||
bool block_requires_label(const Function* f,
|
||||
const std::vector<M2C_Block>& blocks,
|
||||
size_t block_idx) {
|
||||
@@ -388,26 +479,46 @@ bool block_requires_label(const Function* f,
|
||||
|
||||
if (block.pred.size() == 1 && block_idx > 0 && block.pred.front() == (int)block_idx - 1 &&
|
||||
blocks.at(block_idx - 1).succ_ft == (int)block_idx) {
|
||||
// the only way to get to this block is to fall through.
|
||||
// the only way to get to this block is to fall through, no need for a label.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// hack counter for total number of unknown instruction. TODO remove
|
||||
int g_unknown = 0;
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Complain about an unknown instruction.
|
||||
*/
|
||||
Mips2C_Line handle_unknown(const std::string& instr_str) {
|
||||
g_unknown++;
|
||||
lg::warn("mips2c unknown: {}", instr_str);
|
||||
return fmt::format("// Unknown instr: {}", instr_str);
|
||||
}
|
||||
|
||||
Mips2C_Line handle_generic_load(const Instruction& i0, const std::string& instr_str) {
|
||||
if (i0.get_src(1).is_reg(rsp())) {
|
||||
return handle_unknown(instr_str);
|
||||
} else {
|
||||
return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)),
|
||||
i0.get_src(0).get_imm(), reg_to_name(i0.get_src(1))),
|
||||
return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)),
|
||||
i0.get_src(0).get_imm(), reg_to_name(i0.get_src(1))),
|
||||
instr_str};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_lwc1(const Instruction& i0,
|
||||
const std::string& instr_str,
|
||||
const LinkedObjectFile* file) {
|
||||
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);
|
||||
float f;
|
||||
memcpy(&f, &word.data, 4);
|
||||
return {fmt::format("c->fprs[{}] = {};", reg_to_name(i0.get_dst(0)), float_to_string(f)),
|
||||
instr_str};
|
||||
} else {
|
||||
return handle_generic_load(i0, instr_str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,13 +539,9 @@ Mips2C_Line handle_lw(Mips2C_Output& out, const Instruction& i0, const std::stri
|
||||
Mips2C_Line handle_generic_store(Mips2C_Output& /*out*/,
|
||||
const Instruction& i0,
|
||||
const std::string& instr_str) {
|
||||
if (i0.get_src(2).is_reg(Register(Reg::GPR, Reg::SP))) {
|
||||
return handle_unknown(instr_str);
|
||||
} else {
|
||||
return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_src(0)),
|
||||
i0.get_src(1).get_imm(), reg_to_name(i0.get_src(2))),
|
||||
instr_str};
|
||||
}
|
||||
return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_src(0)),
|
||||
i0.get_src(1).get_imm(), reg_to_name(i0.get_src(2))),
|
||||
instr_str};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_generic_op2_u16(const Instruction& i0, const std::string& instr_str) {
|
||||
@@ -528,10 +635,12 @@ Mips2C_Line handle_generic_op2(const Instruction& i0,
|
||||
Mips2C_Line handle_or(const Instruction& i0, const std::string& instr_str) {
|
||||
if (is_gpr_3(i0, InstructionKind::OR, {}, rs7(), rr0())) {
|
||||
// set reg_dest to #f : or reg_dest, s7, r0
|
||||
return handle_unknown(instr_str);
|
||||
return {
|
||||
fmt::format("c->mov64({}, {});", reg_to_name(i0.get_dst(0)), reg_to_name(i0.get_src(0))),
|
||||
instr_str};
|
||||
} else if (is_gpr_3(i0, InstructionKind::OR, {}, rr0(), rr0())) {
|
||||
// set reg_dest to 0 : or reg_dest, r0, r0
|
||||
return handle_unknown(instr_str);
|
||||
return {fmt::format("c->gprs[{}].du64[0] = 0;", reg_to_name(i0.get_dst(0))), instr_str};
|
||||
} else if (is_gpr_3(i0, InstructionKind::OR, {}, {}, rr0())) {
|
||||
// set dst to src : or dst, src, r0
|
||||
return {
|
||||
@@ -589,6 +698,25 @@ Mips2C_Line handle_non_likely_branch_bc(const Instruction& i0, const std::string
|
||||
return {fmt::format("bc = ((s64){}) < 0;", reg64_or_zero(i0.get_src(0))), instr_str};
|
||||
case InstructionKind::BGTZ:
|
||||
return {fmt::format("bc = ((s64){}) > 0;", reg64_or_zero(i0.get_src(0))), instr_str};
|
||||
case InstructionKind::BGEZ:
|
||||
return {fmt::format("bc = ((s64){}) >= 0;", reg64_or_zero(i0.get_src(0))), instr_str};
|
||||
case InstructionKind::BLEZ:
|
||||
return {fmt::format("bc = ((s64){}) <= 0;", reg64_or_zero(i0.get_src(0))), instr_str};
|
||||
case InstructionKind::BC1F:
|
||||
return {fmt::format("bc = !cop1_bc;"), instr_str};
|
||||
default:
|
||||
return handle_unknown(instr_str);
|
||||
}
|
||||
}
|
||||
|
||||
Mips2C_Line handle_likely_branch_bc(const Instruction& i0, const std::string& instr_str) {
|
||||
switch (i0.kind) {
|
||||
case InstructionKind::BLTZL:
|
||||
return {fmt::format("((s64){}) < 0", reg64_or_zero(i0.get_src(0))), instr_str};
|
||||
case InstructionKind::BNEL:
|
||||
return {fmt::format("((s64){}) != ((s64){})", reg64_or_zero(i0.get_src(0)),
|
||||
reg64_or_zero(i0.get_src(1))),
|
||||
instr_str};
|
||||
default:
|
||||
return handle_unknown(instr_str);
|
||||
}
|
||||
@@ -601,6 +729,29 @@ Mips2C_Line handle_vdiv(const Instruction& i0, const std::string& instr_string)
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_vsqrt(const Instruction& i0, const std::string& instr_string) {
|
||||
return {fmt::format("c->vsqrt({}, BC::{});", reg_to_name(i0.get_src(0)),
|
||||
"xyzw"[i0.get_src(1).get_vf_field()]),
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_vrxor(const Instruction& i0, const std::string& instr_string) {
|
||||
return {fmt::format("c->vrxor({}, BC::{});", reg_to_name(i0.get_src(0)), i0.cop2_bc_to_char()),
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_vrget(const Instruction& i0, const std::string& instr_string) {
|
||||
return {fmt::format("c->vrget(DEST::{}, {});", dest_to_char(i0.cop2_dest),
|
||||
reg_to_name(i0.get_dst(0))),
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_vrnext(const Instruction& i0, const std::string& instr_string) {
|
||||
return {fmt::format("c->vrnext(DEST::{}, {});", dest_to_char(i0.cop2_dest),
|
||||
reg_to_name(i0.get_dst(0))),
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_por(const Instruction& i0, const std::string& instr_string) {
|
||||
if (is_gpr_3(i0, InstructionKind::POR, {}, {}, rr0())) {
|
||||
return {fmt::format("c->mov128_gpr_gpr({}, {});", reg_to_name(i0.get_dst(0)),
|
||||
@@ -611,10 +762,22 @@ Mips2C_Line handle_por(const Instruction& i0, const std::string& instr_string) {
|
||||
}
|
||||
}
|
||||
|
||||
Mips2C_Line handle_lui(const Instruction& i0, const std::string& instr_string) {
|
||||
return {fmt::format("c->lui({}, {});", reg_to_name(i0.get_dst(0)), i0.get_src(0).get_imm()),
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_clts(const Instruction& i0, const std::string& instr_string) {
|
||||
return {fmt::format("cop1_bc = c->fprs[{}] < c->fprs[{}];", reg_to_name(i0.get_src(0)),
|
||||
reg_to_name(i0.get_src(1))),
|
||||
instr_string};
|
||||
}
|
||||
|
||||
Mips2C_Line handle_normal_instr(Mips2C_Output& output,
|
||||
const Instruction& i0,
|
||||
const std::string& instr_str,
|
||||
int& unknown_count) {
|
||||
int& unknown_count,
|
||||
const LinkedObjectFile* file) {
|
||||
switch (i0.kind) {
|
||||
case InstructionKind::LW:
|
||||
return handle_lw(output, i0, instr_str);
|
||||
@@ -622,12 +785,24 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
|
||||
case InstructionKind::LWU:
|
||||
case InstructionKind::LQ:
|
||||
case InstructionKind::LQC2:
|
||||
case InstructionKind::LH:
|
||||
case InstructionKind::LHU:
|
||||
case InstructionKind::LD:
|
||||
return handle_generic_load(i0, instr_str);
|
||||
case InstructionKind::LWC1:
|
||||
return handle_lwc1(i0, instr_str, file);
|
||||
case InstructionKind::SQ:
|
||||
case InstructionKind::SQC2:
|
||||
case InstructionKind::SH:
|
||||
case InstructionKind::SD:
|
||||
case InstructionKind::SWC1:
|
||||
return handle_generic_store(output, i0, instr_str);
|
||||
case InstructionKind::VADD_BC:
|
||||
return handle_generic_op3_bc_mask(i0, instr_str, "vadd_bc");
|
||||
case InstructionKind::VMINI_BC:
|
||||
return handle_generic_op3_bc_mask(i0, instr_str, "vmini_bc");
|
||||
case InstructionKind::VMAX_BC:
|
||||
return handle_generic_op3_bc_mask(i0, instr_str, "vmax_bc");
|
||||
case InstructionKind::VSUB_BC:
|
||||
return handle_generic_op3_bc_mask(i0, instr_str, "vsub_bc");
|
||||
case InstructionKind::VMUL_BC:
|
||||
@@ -646,21 +821,43 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
|
||||
return handle_generic_op2_mask(i0, instr_str, "vmove");
|
||||
case InstructionKind::VITOF0:
|
||||
return handle_generic_op2_mask(i0, instr_str, "vitof0");
|
||||
case InstructionKind::VFTOI0:
|
||||
return handle_generic_op2_mask(i0, instr_str, "vftoi0");
|
||||
case InstructionKind::VFTOI4:
|
||||
return handle_generic_op2_mask(i0, instr_str, "vftoi4");
|
||||
case InstructionKind::VADDQ:
|
||||
return handle_generic_op2_mask(i0, instr_str, "vaddq");
|
||||
case InstructionKind::ANDI:
|
||||
case InstructionKind::ORI:
|
||||
case InstructionKind::SRA:
|
||||
case InstructionKind::DSLL:
|
||||
case InstructionKind::DSLL32:
|
||||
case InstructionKind::DSRA:
|
||||
case InstructionKind::DSRA32:
|
||||
return handle_generic_op2_u16(i0, instr_str);
|
||||
case InstructionKind::SLL:
|
||||
return handle_sll(i0, instr_str);
|
||||
case InstructionKind::DADDU:
|
||||
case InstructionKind::DSUBU:
|
||||
case InstructionKind::ADDU:
|
||||
case InstructionKind::PEXTLH:
|
||||
case InstructionKind::PEXTLB:
|
||||
case InstructionKind::MOVN:
|
||||
case InstructionKind::PEXTUW:
|
||||
case InstructionKind::PCPYUD:
|
||||
case InstructionKind::MOVZ:
|
||||
case InstructionKind::MULT3:
|
||||
case InstructionKind::PMINW:
|
||||
case InstructionKind::PMAXW:
|
||||
return handle_generic_op3(i0, instr_str, {});
|
||||
case InstructionKind::MULS:
|
||||
return handle_generic_op3(i0, instr_str, "muls");
|
||||
case InstructionKind::ADDS:
|
||||
return handle_generic_op3(i0, instr_str, "adds");
|
||||
case InstructionKind::SUBS:
|
||||
return handle_generic_op3(i0, instr_str, "subs");
|
||||
case InstructionKind::XOR:
|
||||
return handle_generic_op3(i0, instr_str, "xor_");
|
||||
case InstructionKind::AND:
|
||||
return handle_generic_op3(i0, instr_str, "and_"); // and isn't allowed in C++
|
||||
case InstructionKind::DADDIU:
|
||||
@@ -678,6 +875,14 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
|
||||
return handle_generic_op3_bc_mask(i0, instr_str, "vmadd_bc");
|
||||
case InstructionKind::VDIV:
|
||||
return handle_vdiv(i0, instr_str);
|
||||
case InstructionKind::VSQRT:
|
||||
return handle_vsqrt(i0, instr_str);
|
||||
case InstructionKind::VRXOR:
|
||||
return handle_vrxor(i0, instr_str);
|
||||
case InstructionKind::VRGET:
|
||||
return handle_vrget(i0, instr_str);
|
||||
case InstructionKind::VRNEXT:
|
||||
return handle_vrnext(i0, instr_str);
|
||||
case InstructionKind::POR:
|
||||
return handle_por(i0, instr_str);
|
||||
case InstructionKind::VMULQ:
|
||||
@@ -686,6 +891,21 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
|
||||
return {"// nop", instr_str};
|
||||
case InstructionKind::MFC1:
|
||||
return handle_generic_op2(i0, instr_str, "mfc1");
|
||||
case InstructionKind::MTC1:
|
||||
return handle_generic_op2(i0, instr_str, "mtc1");
|
||||
case InstructionKind::CVTWS:
|
||||
return handle_generic_op2(i0, instr_str, "cvtws");
|
||||
case InstructionKind::CVTSW:
|
||||
return handle_generic_op2(i0, instr_str, "cvtsw");
|
||||
case InstructionKind::PEXEW:
|
||||
return handle_generic_op2(i0, instr_str, "pexew");
|
||||
case InstructionKind::SQRTS:
|
||||
return handle_generic_op2(i0, instr_str, "sqrts");
|
||||
case InstructionKind::LUI:
|
||||
return handle_lui(i0, instr_str);
|
||||
case InstructionKind::CLTS:
|
||||
output.needs_cop1_bc = true;
|
||||
return handle_clts(i0, instr_str);
|
||||
default:
|
||||
unknown_count++;
|
||||
return handle_unknown(instr_str);
|
||||
@@ -697,17 +917,20 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
|
||||
void run_mips2c(Function* f) {
|
||||
g_unknown = 0;
|
||||
auto* file = f->ir2.env.file;
|
||||
auto blocks = setup_preds_and_succs(*f, *file);
|
||||
std::unordered_set<int> likely_delay_blocks;
|
||||
auto blocks = setup_preds_and_succs(*f, *file, likely_delay_blocks);
|
||||
Mips2C_Output output;
|
||||
int unknown_count = 0;
|
||||
|
||||
for (size_t block_idx = 0; block_idx < blocks.size(); block_idx++) {
|
||||
const auto& block = blocks[block_idx];
|
||||
// fmt::print("block {}: {} to {}\n", block_idx, block.start_instr, block.end_instr);
|
||||
|
||||
if (likely_delay_blocks.count(block_idx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block_requires_label(f, blocks, block_idx)) {
|
||||
output.output_label(block_idx);
|
||||
} else {
|
||||
// output.comment_line("block {}",)
|
||||
}
|
||||
|
||||
for (int i = block.start_instr; i < block.end_instr; i++) {
|
||||
@@ -717,7 +940,22 @@ void run_mips2c(Function* f) {
|
||||
|
||||
if (is_branch(instr, {})) {
|
||||
if (block.branch_likely) {
|
||||
output.lines.push_back(handle_unknown(instr_str));
|
||||
auto branch_line = handle_likely_branch_bc(instr, instr_str);
|
||||
output.lines.emplace_back(fmt::format("if ({}) {{", branch_line.code),
|
||||
branch_line.comment);
|
||||
// next block should be the delay slot
|
||||
assert((int)block_idx + 1 == block.succ_branch);
|
||||
auto& delay_block = blocks.at(block.succ_branch);
|
||||
assert(delay_block.end_instr - delay_block.start_instr == 1); // only 1 instr.
|
||||
auto& delay_instr = f->instructions.at(delay_block.start_instr);
|
||||
auto delay_instr_str = delay_instr.to_string(file->labels);
|
||||
auto delay_instr_line =
|
||||
handle_normal_instr(output, delay_instr, delay_instr_str, unknown_count, file);
|
||||
output.lines.emplace_back(fmt::format(" {}", delay_instr_line.code),
|
||||
delay_instr_line.comment);
|
||||
assert(delay_block.succ_ft == -1);
|
||||
output.lines.emplace_back(fmt::format(" goto block_{};", delay_block.succ_branch), "");
|
||||
output.lines.emplace_back("}", "");
|
||||
} else {
|
||||
if (is_always_branch(instr)) {
|
||||
// skip the branch ins.
|
||||
@@ -728,7 +966,7 @@ void run_mips2c(Function* f) {
|
||||
auto& delay_i = f->instructions.at(i);
|
||||
auto delay_i_str = delay_i.to_string(file->labels);
|
||||
output.lines.push_back(
|
||||
handle_normal_instr(output, delay_i, delay_i_str, unknown_count));
|
||||
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
|
||||
assert(i + 1 == block.end_instr);
|
||||
// then the goto
|
||||
output.lines.emplace_back(fmt::format("goto block_{};", block.succ_branch),
|
||||
@@ -742,7 +980,7 @@ void run_mips2c(Function* f) {
|
||||
auto& delay_i = f->instructions.at(i);
|
||||
auto delay_i_str = delay_i.to_string(file->labels);
|
||||
output.lines.push_back(
|
||||
handle_normal_instr(output, delay_i, delay_i_str, unknown_count));
|
||||
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
|
||||
assert(i + 1 == block.end_instr);
|
||||
// then the goto
|
||||
output.lines.emplace_back(fmt::format("if (bc) {{goto block_{};}}", block.succ_branch),
|
||||
@@ -757,15 +995,27 @@ void run_mips2c(Function* f) {
|
||||
i++;
|
||||
auto& delay_i = f->instructions.at(i);
|
||||
auto delay_i_str = delay_i.to_string(file->labels);
|
||||
output.lines.push_back(handle_normal_instr(output, delay_i, delay_i_str, unknown_count));
|
||||
assert(i + 1 == block.end_instr);
|
||||
output.lines.push_back(
|
||||
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
|
||||
|
||||
// then the goto
|
||||
output.lines.emplace_back(fmt::format("goto end_of_function;", block.succ_branch),
|
||||
"return\n");
|
||||
} else if (instr.kind == InstructionKind::JALR) {
|
||||
assert(instr.get_dst(0).is_reg(Register(Reg::GPR, Reg::RA)));
|
||||
assert(i < block.end_instr - 1);
|
||||
output.lines.emplace_back(
|
||||
fmt::format("call_addr = c->gprs[{}].du32[0];", reg_to_name(instr.get_src(0))),
|
||||
"function call:");
|
||||
i++;
|
||||
auto& delay_i = f->instructions.at(i);
|
||||
auto delay_i_str = delay_i.to_string(file->labels);
|
||||
output.lines.push_back(
|
||||
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
|
||||
output.lines.emplace_back("c->jalr(call_addr);", instr_str);
|
||||
} else {
|
||||
output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count));
|
||||
output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count, file));
|
||||
}
|
||||
// fmt::print("I: {}\n", instr_str);
|
||||
|
||||
assert(output.lines.size() > old_line_count);
|
||||
}
|
||||
|
||||
+244
-101
@@ -213,6 +213,7 @@
|
||||
(stmod 5) ;; set mode register
|
||||
(mskpath3 6) ;; set path 3 mask
|
||||
(mark 7) ;; set mark register
|
||||
(pc-port 8) ;; special tag for PC Port data.
|
||||
(flushe 16) ;; wait for end of microprogram
|
||||
(flush 17) ;; wait for end of microprogram and transfer (path1/path2)
|
||||
(flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3)
|
||||
@@ -547,7 +548,7 @@
|
||||
;; merc1 61
|
||||
;; generic1 62
|
||||
(depth-cue 64)
|
||||
(bucket-65 65)
|
||||
(pre-sprite-textures 65) ;; common
|
||||
(sprite 66)
|
||||
;; debug spheres? 67
|
||||
(debug-draw0 67)
|
||||
@@ -1019,7 +1020,7 @@
|
||||
(ogre-end #x600)
|
||||
(ogre-buzzer #x601)
|
||||
(ogre-boss #x603)
|
||||
|
||||
|
||||
(assistant-voicebox-intro-ogre-race #x605)
|
||||
|
||||
(sidekick-speech-hint-ogre-race #x61c)
|
||||
@@ -5091,7 +5092,7 @@
|
||||
(debug-print-entities (_type_ symbol type) none 13)
|
||||
(debug-draw-actors (_type_ symbol) none 14)
|
||||
(dummy-15 (_type_) object 15)
|
||||
(dummy-16 (_type_) int 16)
|
||||
(level-update (_type_) int 16)
|
||||
(level-get-target-inside (_type_) level 17)
|
||||
(alloc-levels! (_type_ symbol) int 18)
|
||||
(load-commands-set! (_type_ pair) pair 19)
|
||||
@@ -10106,6 +10107,7 @@
|
||||
:method-count-assert 10
|
||||
:size-assert #x68
|
||||
:flag-assert #xa00000068
|
||||
;; field handle is likely a value type
|
||||
(:methods
|
||||
(dummy-9 (_type_ attack-info) none 9)
|
||||
)
|
||||
@@ -11874,8 +11876,8 @@
|
||||
(texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
(texture-remap-table-len int32 :offset-assert 56)
|
||||
|
||||
(unk-data-1 pointer :offset-assert 60)
|
||||
(unk-data-1-len int32 :offset-assert 64)
|
||||
(texture-ids (pointer texture-id) :offset-assert 60)
|
||||
(texture-page-count int32 :offset-assert 64)
|
||||
|
||||
(unk-zero-0 basic :offset-assert 68)
|
||||
|
||||
@@ -13443,11 +13445,99 @@
|
||||
;; Containing DGOs - ['GAME', 'ENGINE']
|
||||
;; Version - 3
|
||||
|
||||
(defenum sp-field-id
|
||||
:type uint16
|
||||
|
||||
(misc-fields-start 0)
|
||||
(spt-texture 1)
|
||||
(spt-anim 2)
|
||||
(spt-anim-speed 3)
|
||||
(spt-birth-func 4)
|
||||
(spt-joint/refpoint 5)
|
||||
(spt-num 6)
|
||||
(spt-sound 7)
|
||||
(misc-fields-end 8)
|
||||
|
||||
(sprite-fields-start 9)
|
||||
(spt-x 10)
|
||||
(spt-y 11)
|
||||
(spt-z 12)
|
||||
(spt-scale-x 13)
|
||||
(spt-rot-x 14)
|
||||
(spt-rot-y 15)
|
||||
(spt-rot-z 16)
|
||||
(spt-scale-y 17)
|
||||
(spt-r 18)
|
||||
(spt-g 19)
|
||||
(spt-b 20)
|
||||
(spt-a 21)
|
||||
(sprite-fields-end 22)
|
||||
|
||||
(cpu-fields-start 23)
|
||||
(spt-omega 24)
|
||||
(spt-vel-x 25)
|
||||
(spt-vel-y 26)
|
||||
(spt-vel-z 27)
|
||||
(spt-scalevel-x 28)
|
||||
(spt-rotvel-x 29)
|
||||
(spt-rotvel-y 30)
|
||||
(spt-rotvel-z 31)
|
||||
(spt-scalevel-y 32)
|
||||
(spt-fade-r 33)
|
||||
(spt-fade-g 34)
|
||||
(spt-fade-b 35)
|
||||
(spt-fade-a 36)
|
||||
(spt-accel-x 37)
|
||||
(spt-accel-y 38)
|
||||
(spt-accel-z 39)
|
||||
(spt-dummy 40)
|
||||
(spt-quat-x 41)
|
||||
(spt-quat-y 42)
|
||||
(spt-quat-z 43)
|
||||
(spt-quad-w 44)
|
||||
(spt-friction 45)
|
||||
(spt-timer 46)
|
||||
(spt-flags 47)
|
||||
(spt-userdata 48)
|
||||
(spt-func 49)
|
||||
(spt-next-time 50)
|
||||
(spt-next-launcher 51)
|
||||
(cpu-fields-end 52)
|
||||
|
||||
(launch-fields-start 53)
|
||||
(spt-launchrot-x 54)
|
||||
(spt-launchrot-y 55)
|
||||
(spt-launchrot-z 56)
|
||||
(spt-launchrot-w 57)
|
||||
(spt-conerot-x 58)
|
||||
(spt-conerot-y 59)
|
||||
(spt-conerot-z 60)
|
||||
(spt-conerot-w 61)
|
||||
(spt-conerot-radius 62)
|
||||
(spt-rotate-y 63)
|
||||
(launch-fields-end 64)
|
||||
|
||||
(spt-scale 65)
|
||||
(spt-scalevel 66)
|
||||
(spt-end 67)
|
||||
)
|
||||
|
||||
(defenum sp-flag
|
||||
:type uint16
|
||||
(plain-v1 0) ;; just a plain signed integer. No random crap.
|
||||
(float-with-rand 1)
|
||||
(int-with-rand 2)
|
||||
(copy-from-other-field 3)
|
||||
(plain-v2 4)
|
||||
(from-pointer 5)
|
||||
(part-by-id 6)
|
||||
)
|
||||
|
||||
;; - Types
|
||||
|
||||
(deftype sp-field-init-spec (structure)
|
||||
((field uint16 :offset-assert 0)
|
||||
(flags uint16 :offset-assert 2)
|
||||
((field sp-field-id :offset-assert 0)
|
||||
(flags sp-flag :offset-assert 2)
|
||||
(initial-valuef float :offset-assert 4)
|
||||
(random-rangef float :offset-assert 8)
|
||||
(random-multf float :offset-assert 12)
|
||||
@@ -13476,11 +13566,20 @@
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
(defenum sp-group-item-flag
|
||||
:bitfield #t
|
||||
:type uint16
|
||||
(is-3d 0)
|
||||
(bit1 1)
|
||||
(start-dead 2)
|
||||
(launch-asap 3)
|
||||
)
|
||||
|
||||
(deftype sparticle-group-item (structure)
|
||||
((launcher uint32 :offset-assert 0)
|
||||
(fade-after meters :offset-assert 4)
|
||||
(falloff-to meters :offset-assert 8)
|
||||
(flags uint16 :offset-assert 12)
|
||||
(flags sp-group-item-flag :offset-assert 12)
|
||||
(period uint16 :offset-assert 14)
|
||||
(length uint16 :offset-assert 16)
|
||||
(offset uint16 :offset-assert 18)
|
||||
@@ -13492,13 +13591,23 @@
|
||||
:flag-assert #x90000001c
|
||||
)
|
||||
|
||||
(defenum sp-launch-state-flags
|
||||
:bitfield #t
|
||||
:type uint16
|
||||
(launcher-active 0) ;; active
|
||||
(particles-active 1) ;; wants to launch
|
||||
(bit2 2)
|
||||
)
|
||||
|
||||
(declare-type sparticle-cpuinfo structure)
|
||||
|
||||
(deftype sparticle-launch-state (structure)
|
||||
((group-item sparticle-group-item :offset-assert 0)
|
||||
(flags uint16 :offset-assert 4)
|
||||
(flags sp-launch-state-flags :offset-assert 4)
|
||||
(randomize uint16 :offset-assert 6)
|
||||
(origin vector :offset-assert 8)
|
||||
(sprite3d sprite-vec-data-3d :offset-assert 12)
|
||||
(sprite basic :offset-assert 16)
|
||||
(sprite sparticle-cpuinfo :offset-assert 16)
|
||||
(offset uint32 :offset-assert 20)
|
||||
(accum float :offset-assert 24)
|
||||
(spawn-time uint32 :offset-assert 28)
|
||||
@@ -13513,12 +13622,20 @@
|
||||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
(defenum sp-group-flag
|
||||
:bitfield #t
|
||||
:type uint16
|
||||
(use-local-clock 0)
|
||||
(always-draw 1)
|
||||
(screen-space 2)
|
||||
)
|
||||
|
||||
(deftype sparticle-launch-group (basic)
|
||||
((length int16 :offset-assert 4)
|
||||
(duration uint16 :offset-assert 6)
|
||||
(linger-duration uint16 :offset-assert 8)
|
||||
(flags uint16 :offset-assert 10)
|
||||
(name basic :offset-assert 12)
|
||||
(flags sp-group-flag :offset-assert 10)
|
||||
(name string :offset-assert 12)
|
||||
(launcher (inline-array sparticle-group-item) :offset-assert 16)
|
||||
(bounds sphere :inline :offset-assert 32)
|
||||
)
|
||||
@@ -13546,10 +13663,10 @@
|
||||
:flag-assert #xe00000040
|
||||
(:methods
|
||||
(initialize (_type_ sparticle-launch-group process) none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 (_type_ vector) none 11)
|
||||
(deactivate (_type_) none 12)
|
||||
(dummy-13 () none 13)
|
||||
(is-visible? (_type_ vector) symbol 10)
|
||||
(spawn (_type_ vector) object 11)
|
||||
(kill-and-free-particles (_type_) none 12)
|
||||
(kill-particles (_type_) none 13)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -13564,6 +13681,28 @@
|
||||
|
||||
;; - Types
|
||||
|
||||
(defenum sp-cpuinfo-flag
|
||||
:bitfield #t
|
||||
:type uint32
|
||||
(bit0 0)
|
||||
(bit2 2) ;; cleared after an aux has its func set to add-to-sprite-aux-lst
|
||||
(bit3 3)
|
||||
(ready-to-launch 6) ;; maybe just just death?
|
||||
(bit7 7)
|
||||
(aux-list 8) ;; prevents relaunch, adds to aux
|
||||
(bit9 9)
|
||||
(level0 10)
|
||||
(level1 11)
|
||||
(bit12 12) ;; required to relaunch
|
||||
(bit13 13)
|
||||
(bit14 14)
|
||||
(use-global-acc 16)
|
||||
(launch-along-z 17)
|
||||
(left-multiply-quat 18)
|
||||
(right-multiply-quat 19)
|
||||
(set-conerot 20)
|
||||
)
|
||||
|
||||
(deftype sparticle-cpuinfo (structure)
|
||||
((sprite sprite-vec-data-2d :offset-assert 0)
|
||||
(adgif adgif-shader :offset-assert 4)
|
||||
@@ -13580,7 +13719,7 @@
|
||||
(scalevely float :offset 44)
|
||||
(friction float :offset-assert 96)
|
||||
(timer int32 :offset-assert 100)
|
||||
(flags uint32 :offset-assert 104)
|
||||
(flags sp-cpuinfo-flag :offset-assert 104)
|
||||
(user-int32 int32 :offset-assert 108)
|
||||
(user-uint32 uint32 :offset 108)
|
||||
(user-float float :score 100 :offset 108)
|
||||
@@ -13617,8 +13756,8 @@
|
||||
|
||||
(deftype sparticle-system (basic)
|
||||
((blocks int32 2 :offset-assert 4)
|
||||
(length uint32 2 :offset-assert 12)
|
||||
(num-alloc uint32 2 :offset-assert 20)
|
||||
(length int32 2 :offset-assert 12)
|
||||
(num-alloc int32 2 :offset-assert 20)
|
||||
(is-3d basic :offset-assert 28)
|
||||
(flags uint32 :offset-assert 32)
|
||||
(alloc-table (pointer uint64) :offset-assert 36)
|
||||
@@ -15312,7 +15451,7 @@
|
||||
(define-extern sprite-add-2d-chunk (function sprite-array-2d int int dma-buffer int none))
|
||||
(define-extern sprite-setup-frame-data (function sprite-frame-data int none))
|
||||
(define-extern clear-sprite-aux-list (function none))
|
||||
(define-extern add-to-sprite-aux-list function) ;; it's a callback.
|
||||
(define-extern add-to-sprite-aux-list (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d none)) ;; it's a callback.
|
||||
(define-extern sprite-set-3d-quaternion! (function sprite-vec-data-3d quaternion quaternion))
|
||||
(define-extern sprite-get-3d-quaternion! (function quaternion sprite-vec-data-3d quaternion))
|
||||
(define-extern sprite-draw (function display none))
|
||||
@@ -16384,25 +16523,25 @@
|
||||
|
||||
;; - Types
|
||||
|
||||
; (deftype sparticle-birthinfo (structure)
|
||||
; ((sprite uint32 :offset-assert 0)
|
||||
; (anim int32 :offset-assert 4)
|
||||
; (anim-speed float :offset-assert 8)
|
||||
; (birth-func basic :offset-assert 12)
|
||||
; (joint-ppoint int32 :offset-assert 16)
|
||||
; (num-to-birth float :offset-assert 20)
|
||||
; (sound basic :offset-assert 24)
|
||||
; (dataf UNKNOWN 1 :offset-assert 0)
|
||||
; (data UNKNOWN 1 :offset-assert 0)
|
||||
; )
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x1c
|
||||
; :flag-assert #x90000001c
|
||||
; )
|
||||
(deftype sparticle-birthinfo (structure)
|
||||
((sprite uint32 :offset-assert 0)
|
||||
(anim int32 :offset-assert 4)
|
||||
(anim-speed float :offset-assert 8)
|
||||
(birth-func basic :offset-assert 12)
|
||||
(joint-ppoint int32 :offset-assert 16)
|
||||
(num-to-birth float :offset-assert 20)
|
||||
(sound basic :offset-assert 24)
|
||||
(dataf float 1 :offset 0)
|
||||
(data uint32 1 :offset 0)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x1c
|
||||
:flag-assert #x90000001c
|
||||
)
|
||||
|
||||
(deftype sp-queued-launch-particles (structure)
|
||||
((sp-system basic :offset-assert 0)
|
||||
(sp-launcher basic :offset-assert 4)
|
||||
((sp-system sparticle-system :offset-assert 0)
|
||||
(sp-launcher sparticle-launcher :offset-assert 4)
|
||||
(pos vector :inline :offset-assert 16)
|
||||
)
|
||||
:method-count-assert 9
|
||||
@@ -16410,66 +16549,70 @@
|
||||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
; (deftype sp-launch-queue (basic)
|
||||
; ((in-use int32 :offset-assert 4)
|
||||
; (queue UNKNOWN 32 :offset-assert 16)
|
||||
; )
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x410
|
||||
; :flag-assert #x900000410
|
||||
; )
|
||||
(deftype sp-launch-queue (basic)
|
||||
((in-use int32 :offset-assert 4)
|
||||
(queue sp-queued-launch-particles 32 :inline :offset-assert 16)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x410
|
||||
:flag-assert #x900000410
|
||||
)
|
||||
|
||||
; (deftype particle-adgif-cache (basic)
|
||||
; ((used int32 :offset-assert 4)
|
||||
; (last uint16 :offset-assert 8)
|
||||
; (lastgif adgif-shader :offset-assert 12)
|
||||
; (tidhash UNKNOWN 80 :offset-assert 16)
|
||||
; (spadgif UNKNOWN 80 :offset-assert 176)
|
||||
; )
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x19b0
|
||||
; :flag-assert #x9000019b0
|
||||
; )
|
||||
(deftype particle-adgif-cache (basic)
|
||||
((used int32 :offset-assert 4)
|
||||
(last uint16 :offset-assert 8)
|
||||
(lastgif adgif-shader :offset-assert 12)
|
||||
(tidhash uint16 80 :offset-assert 16)
|
||||
(spadgif adgif-shader 80 :inline :offset-assert 176)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x19b0
|
||||
:flag-assert #x9000019b0
|
||||
)
|
||||
|
||||
;; - Functions
|
||||
|
||||
(define-extern sphere-in-view-frustum? (function vector symbol))
|
||||
(define-extern sphere-in-view-frustum? (function sphere symbol))
|
||||
(define-extern kill-all-particles-with-key (function sparticle-launch-control none))
|
||||
(define-extern sp-relaunch-setup-fields function)
|
||||
(define-extern sp-init-fields! function)
|
||||
(define-extern sp-launch-particles-var (function sparticle-system sparticle-launcher vector symbol symbol float none)) ;; asm - ret not confirmed
|
||||
(define-extern sp-get-particle function)
|
||||
(define-extern particle-adgif function)
|
||||
|
||||
(define-extern sp-relaunch-setup-fields (function object sparticle-launcher sparticle-cpuinfo sprite-vec-data-3d none))
|
||||
|
||||
;; data, field-inits, start field idx, end field idx, bool?
|
||||
(define-extern sp-init-fields! (function object (inline-array sp-field-init-spec) sp-field-id sp-field-id symbol object))
|
||||
|
||||
(define-extern sp-launch-particles-var (function sparticle-system sparticle-launcher vector sparticle-launch-state sparticle-launch-control float none)) ;; asm - ret not confirmed
|
||||
(define-extern sp-get-particle (function sparticle-system int sparticle-launch-state sparticle-cpuinfo))
|
||||
(define-extern particle-adgif (function adgif-shader texture-id none))
|
||||
(define-extern lookup-part-group-by-name (function string basic))
|
||||
(define-extern lookup-part-group-pointer-by-name function)
|
||||
(define-extern lookup-part-group-pointer-by-name (function string (pointer sparticle-launch-group)))
|
||||
(define-extern unlink-part-group-by-heap (function kheap int))
|
||||
(define-extern particle-setup-adgif function)
|
||||
(define-extern sp-queue-launch function)
|
||||
(define-extern sp-adjust-launch function)
|
||||
(define-extern sp-euler-convert function)
|
||||
(define-extern sp-rotate-system function)
|
||||
(define-extern sp-launch-particles-death function)
|
||||
(define-extern sp-clear-queue function)
|
||||
(define-extern sp-relaunch-particle-2d function)
|
||||
(define-extern sp-relaunch-particle-3d function)
|
||||
(define-extern sparticle-track-root function)
|
||||
(define-extern sparticle-track-root-prim function)
|
||||
(define-extern birth-func-copy-rot-color function)
|
||||
(define-extern birth-func-copy2-rot-color function)
|
||||
(define-extern birth-func-copy-omega-to-z function)
|
||||
(define-extern birth-func-random-next-time function)
|
||||
(define-extern particle-setup-adgif (function adgif-shader texture-id none))
|
||||
(define-extern sp-queue-launch (function sparticle-system sparticle-launcher vector int))
|
||||
(define-extern sp-adjust-launch (function sparticle-launchinfo sparticle-cpuinfo (inline-array sp-field-init-spec) none))
|
||||
(define-extern sp-euler-convert (function sparticle-launchinfo sparticle-cpuinfo none))
|
||||
(define-extern sp-rotate-system (function sparticle-launchinfo sparticle-cpuinfo transformq none))
|
||||
(define-extern sp-launch-particles-death (function sparticle-system sparticle-launcher sparticle-launchinfo none))
|
||||
(define-extern sp-clear-queue (function none))
|
||||
(define-extern sp-relaunch-particle-2d (function object sparticle-launcher sparticle-cpuinfo sprite-vec-data-3d none))
|
||||
(define-extern sp-relaunch-particle-3d (function object sparticle-launcher sparticle-cpuinfo sprite-vec-data-3d none))
|
||||
(define-extern sparticle-track-root (function object sparticle-cpuinfo vector none))
|
||||
(define-extern sparticle-track-root-prim (function object sparticle-cpuinfo vector none))
|
||||
(define-extern birth-func-copy-rot-color (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none))
|
||||
(define-extern birth-func-copy2-rot-color (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none))
|
||||
(define-extern birth-func-copy-omega-to-z (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none))
|
||||
(define-extern birth-func-random-next-time (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none))
|
||||
|
||||
;; - Unknowns
|
||||
|
||||
;;(define-extern *global-toggle* object) ;; unknown type
|
||||
(define-extern *global-toggle* int) ;; unknown type
|
||||
(define-extern *part-id-table* (array sparticle-launcher))
|
||||
(define-extern *particle-300hz-timer* int)
|
||||
;;(define-extern *sp-launch-queue* object) ;; unknown type
|
||||
;;(define-extern *death-adgif* object) ;; unknown type
|
||||
(define-extern *sp-launch-queue* sp-launch-queue)
|
||||
(define-extern *death-adgif* adgif-shader) ;; guess
|
||||
(define-extern *part-group-id-table* (array sparticle-launch-group))
|
||||
;;(define-extern *sp-launcher-lock* object) ;; unknown type
|
||||
(define-extern *sp-launcher-lock* symbol)
|
||||
(define-extern *sp-launcher-enable* symbol)
|
||||
;;(define-extern *particle-adgif-cache* object) ;; unknown type
|
||||
(define-extern *particle-adgif-cache* particle-adgif-cache)
|
||||
|
||||
|
||||
;; ----------------------
|
||||
@@ -16480,31 +16623,31 @@
|
||||
|
||||
;; - Functions
|
||||
|
||||
(define-extern all-particles-60-to-50 function)
|
||||
(define-extern all-particles-50-to-60 function)
|
||||
(define-extern sp-process-particle-system function)
|
||||
(define-extern forall-particles-runner function)
|
||||
(define-extern sparticle-60-to-50 function)
|
||||
(define-extern sparticle-50-to-60 function)
|
||||
(define-extern forall-particles function)
|
||||
(define-extern all-particles-60-to-50 (function none))
|
||||
(define-extern all-particles-50-to-60 (function none))
|
||||
(define-extern sp-process-particle-system (function sparticle-system int sprite-array-2d none))
|
||||
(define-extern forall-particles-runner (function (function sparticle-system sparticle-cpuinfo pointer none) sparticle-system none))
|
||||
(define-extern sparticle-60-to-50 (function sparticle-system sparticle-cpuinfo pointer none))
|
||||
(define-extern sparticle-50-to-60 (function sparticle-system sparticle-cpuinfo pointer none))
|
||||
(define-extern forall-particles (function function symbol symbol none))
|
||||
(define-extern sparticle-kill-it-level0 (function sparticle-system sparticle-cpuinfo none))
|
||||
(define-extern sparticle-kill-it-level1 (function sparticle-system sparticle-cpuinfo none))
|
||||
(define-extern forall-particles-with-key (function sparticle-launch-control (function sparticle-system sparticle-cpuinfo none) symbol symbol none))
|
||||
(define-extern sparticle-kill-it (function sparticle-system sparticle-cpuinfo none))
|
||||
(define-extern forall-particles-with-key-runner (function sparticle-launch-control (function sparticle-system sparticle-cpuinfo none) sparticle-system none))
|
||||
(define-extern sp-get-approx-alloc-size function)
|
||||
(define-extern sp-process-block function)
|
||||
(define-extern sp-copy-to-spr function)
|
||||
(define-extern sp-process-block-3d function)
|
||||
(define-extern sp-process-block-2d function)
|
||||
(define-extern sp-copy-from-spr function)
|
||||
(define-extern sp-free-particle function)
|
||||
(define-extern sp-particle-copy! function)
|
||||
(define-extern sp-get-block-size function)
|
||||
(define-extern sp-get-approx-alloc-size (function sparticle-system int int))
|
||||
(define-extern sp-process-block (function sparticle-system int sprite-array-2d int none))
|
||||
(define-extern sp-copy-to-spr (function int pointer int none))
|
||||
(define-extern sp-process-block-3d (function sparticle-system int int int int symbol none))
|
||||
(define-extern sp-process-block-2d (function sparticle-system int int int int symbol none))
|
||||
(define-extern sp-copy-from-spr (function int pointer int none))
|
||||
(define-extern sp-free-particle (function sparticle-system int sparticle-cpuinfo sprite-vec-data-2d none))
|
||||
(define-extern sp-particle-copy! (function sparticle-cpuinfo sparticle-cpuinfo none))
|
||||
(define-extern sp-get-block-size (function sparticle-system int int))
|
||||
(define-extern sp-kill-particle (function sparticle-system sparticle-cpuinfo none))
|
||||
(define-extern sp-orbiter function)
|
||||
(define-extern sp-orbiter (function sparticle-system sparticle-cpuinfo vector none))
|
||||
(define-extern memcpy function)
|
||||
(define-extern kill-all-particles-in-level (function int))
|
||||
(define-extern kill-all-particles-in-level (function level int))
|
||||
(define-extern set-particle-frame-time (function int none))
|
||||
(define-extern process-particles (function none))
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@
|
||||
"cspace<-parented-transformq-joint!",
|
||||
|
||||
// sprite
|
||||
"add-to-sprite-aux-list", // fine, but don't know types yet.
|
||||
|
||||
// merc-blend-shape
|
||||
"setup-blerc-chains-for-one-fragment", // F: asm branching
|
||||
@@ -211,16 +210,12 @@
|
||||
"draw-inline-array-instance-tie",
|
||||
|
||||
// sparticle-launcher
|
||||
"(method 11 sparticle-launch-control)", // BUG: cfg ir
|
||||
"sp-launch-particles-var",
|
||||
"particle-adgif",
|
||||
"sp-init-fields!",
|
||||
|
||||
// sparticle
|
||||
"memcpy",
|
||||
"sp-process-block-3d",
|
||||
"sp-process-block-2d",
|
||||
"sp-get-particle",
|
||||
|
||||
// mood BUG
|
||||
"update-mood-lava", // BUG:
|
||||
@@ -503,7 +498,10 @@
|
||||
"unpack-comp-huf":[2, 4, 5, 6, 7, 8, 9],
|
||||
"blerc-execute":[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33],
|
||||
"(method 11 fact-info-target)":[42],
|
||||
"(code format-card auto-save)":[3, 4, 5, 6, 7, 8]
|
||||
"(code format-card auto-save)":[3, 4, 5, 6, 7, 8],
|
||||
"particle-adgif":[0, 1, 2, 3, 4, 5, 7],
|
||||
"sp-launch-particles-var":[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66],
|
||||
"(method 11 sparticle-launch-control)": [ 27, 28, 35, 46, 48, 49, 77]
|
||||
},
|
||||
|
||||
// Sometimes the game might use format strings that are fetched dynamically,
|
||||
@@ -542,7 +540,11 @@
|
||||
},
|
||||
|
||||
"mips2c_functions_by_name":[
|
||||
"draw-string"
|
||||
"sp-init-fields!",
|
||||
"particle-adgif",
|
||||
"sp-launch-particles-var",
|
||||
"sp-process-block-2d",
|
||||
"sp-process-block-3d"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -611,6 +611,9 @@
|
||||
"hint-control": [
|
||||
["L45", "(array task-hint-control-group)"]
|
||||
],
|
||||
"sparticle-launcher": [
|
||||
["L192", "adgif-shader"]
|
||||
],
|
||||
|
||||
// please do not add things after this entry! git is dumb.
|
||||
"object-file-that-doesnt-actually-exist-and-i-just-put-this-here-to-prevent-merge-conflicts-with-this-file": []
|
||||
|
||||
@@ -1892,5 +1892,60 @@
|
||||
"ambient-type-sound": [[32, "sound-spec"]],
|
||||
"ambient-type-sound-loop": [[16, "sound-spec"]],
|
||||
|
||||
"sp-relaunch-particle-3d": [
|
||||
[16, "quaternion"],
|
||||
[32, "vector"],
|
||||
[48, "quaternion"]
|
||||
],
|
||||
|
||||
"sp-adjust-launch": [
|
||||
[16, "sparticle-launchinfo"],
|
||||
[64, "matrix"],
|
||||
[128, "vector"],
|
||||
[144, "matrix"]
|
||||
],
|
||||
|
||||
"(method 10 sparticle-launch-control)":[
|
||||
[16, "vector"]
|
||||
],
|
||||
|
||||
"sparticle-50-to-60":[
|
||||
[16, "quaternion"]
|
||||
],
|
||||
|
||||
|
||||
"sparticle-60-to-50":[
|
||||
[16, "quaternion"]
|
||||
],
|
||||
|
||||
"sp-orbiter":[
|
||||
[16, "vector"],
|
||||
[32, "vector"],
|
||||
[48, "matrix"]
|
||||
],
|
||||
|
||||
"sp-euler-convert":[
|
||||
[16, "vector"],
|
||||
[32, "quaternion"]
|
||||
],
|
||||
|
||||
"sp-rotate-system": [
|
||||
[16, "matrix"],
|
||||
[80, "quaternion"]
|
||||
],
|
||||
|
||||
"sp-launch-particles-death": [
|
||||
[16, "sprite-vec-data-2d"] // TODO this is probably wrong.
|
||||
],
|
||||
|
||||
"birth-func-copy-rot-color": [
|
||||
[16, "vector"]
|
||||
],
|
||||
|
||||
"birth-func-copy2-rot-color": [
|
||||
[16, "vector"],
|
||||
[32, "vector"]
|
||||
],
|
||||
|
||||
"placeholder-do-not-add-below!": []
|
||||
}
|
||||
|
||||
@@ -3178,6 +3178,34 @@
|
||||
["_stack_", 112, "res-tag"],
|
||||
[57, "v0", "symbol"]
|
||||
],
|
||||
"forall-particles-runner": [
|
||||
[[19,28], "s4", "sparticle-cpuinfo"],
|
||||
[34, "s4", "pointer"],
|
||||
[35, "s3", "pointer"]
|
||||
],
|
||||
|
||||
"(method 2 sparticle-cpuinfo)": [
|
||||
[14, "f0", "float"]
|
||||
],
|
||||
|
||||
"sp-kill-particle": [
|
||||
[7, "a1", "uint"],
|
||||
[7, "v1", "uint"]
|
||||
],
|
||||
|
||||
"sparticle-track-root":[
|
||||
[2, "v1", "process-drawable"]
|
||||
],
|
||||
|
||||
"sparticle-track-root-prim":[
|
||||
[2, "v1", "process-drawable"],
|
||||
[3, "v1", "collide-shape"]
|
||||
],
|
||||
|
||||
"sp-orbiter":[
|
||||
[[73, 82], "v1", "sprite-vec-data-2d"]
|
||||
],
|
||||
|
||||
|
||||
"placeholder-do-not-add-below": []
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "game_text.h"
|
||||
#include "decompiler/ObjectFile/ObjectFileDB.h"
|
||||
#include "common/goos/Reader.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
|
||||
namespace decompiler {
|
||||
namespace {
|
||||
@@ -23,10 +24,6 @@ DecompilerLabel get_label(ObjectFileData& data, const LinkedWord& word) {
|
||||
return data.linked_data.labels.at(word.label_id);
|
||||
}
|
||||
|
||||
int align16(int in) {
|
||||
return (in + 15) & ~15;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
@@ -76,7 +73,7 @@ GameTextResult process_game_text(ObjectFileData& data) {
|
||||
assert(group_name == "common");
|
||||
// remember that we read these bytes
|
||||
auto group_start = (group_label.offset / 4) - 1;
|
||||
for (int j = 0; j < align16(8 + 1 + group_name.length()) / 4; j++) {
|
||||
for (int j = 0; j < align16(8 + 1 + (int)group_name.length()) / 4; j++) {
|
||||
read_words.at(group_start + j)++;
|
||||
}
|
||||
|
||||
@@ -106,7 +103,7 @@ GameTextResult process_game_text(ObjectFileData& data) {
|
||||
// remember what we read (-1 for the type tag)
|
||||
auto string_start = (text_label.offset / 4) - 1;
|
||||
// 8 for type tag and length fields, 1 for null char.
|
||||
for (int j = 0; j < align16(8 + 1 + text.length()) / 4; j++) {
|
||||
for (int j = 0; j < align16(8 + 1 + (int)text.length()) / 4; j++) {
|
||||
read_words.at(string_start + j)++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "decompiler/IR2/Form.h"
|
||||
#include "decompiler/analysis/final_output.h"
|
||||
#include "decompiler/util/sparticle_decompile.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
@@ -186,7 +187,7 @@ goos::Object decompile_at_label(const TypeSpec& type,
|
||||
}
|
||||
|
||||
if (ts.tc(TypeSpec("structure"), type)) {
|
||||
return decompile_structure(type, label, labels, words, ts, file);
|
||||
return decompile_structure(type, label, labels, words, ts, file, true);
|
||||
}
|
||||
|
||||
if (type == TypeSpec("pair")) {
|
||||
@@ -473,7 +474,15 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file) {
|
||||
const LinkedObjectFile* file,
|
||||
bool use_fancy_macros) {
|
||||
if (use_fancy_macros && type == TypeSpec("sp-field-init-spec")) {
|
||||
return decompile_sparticle_field_init(type, label, labels, words, ts, file);
|
||||
}
|
||||
|
||||
if (use_fancy_macros && type == TypeSpec("sparticle-group-item")) {
|
||||
return decompile_sparticle_group_item(type, label, labels, words, ts, file);
|
||||
}
|
||||
// first step, get type info and words
|
||||
TypeSpec actual_type = type;
|
||||
auto uncast_type_info = ts.lookup_type(actual_type);
|
||||
@@ -823,7 +832,6 @@ goos::Object decompile_structure(const TypeSpec& type,
|
||||
return pretty_print::build_list(result_def);
|
||||
}
|
||||
|
||||
namespace {
|
||||
goos::Object bitfield_defs_print(const TypeSpec& type,
|
||||
const std::vector<BitFieldConstantDef>& defs) {
|
||||
std::vector<goos::Object> result;
|
||||
@@ -843,8 +851,6 @@ goos::Object bitfield_defs_print(const TypeSpec& type,
|
||||
return pretty_print::build_list(result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
goos::Object decompile_value(const TypeSpec& type,
|
||||
const std::vector<u8>& bytes,
|
||||
const TypeSystem& ts) {
|
||||
|
||||
@@ -39,7 +39,8 @@ goos::Object decompile_structure(const TypeSpec& actual_type,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file);
|
||||
const LinkedObjectFile* file,
|
||||
bool use_fancy_macros);
|
||||
goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
@@ -105,5 +106,6 @@ std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
|
||||
const TypeSystem& ts,
|
||||
u64 value);
|
||||
std::string decompile_int_enum_from_int(const TypeSpec& type, const TypeSystem& ts, u64 value);
|
||||
|
||||
goos::Object bitfield_defs_print(const TypeSpec& type,
|
||||
const std::vector<BitFieldConstantDef>& defs);
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -0,0 +1,622 @@
|
||||
#include "sparticle_decompile.h"
|
||||
#include "decompiler/util/data_decompile.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/util/print_float.h"
|
||||
|
||||
namespace decompiler {
|
||||
// sparticle fields.
|
||||
// should match the enum in the game.
|
||||
enum class FieldId {
|
||||
MISC_FIELDS_START = 0,
|
||||
SPT_TEXTURE = 1,
|
||||
SPT_ANIM = 2,
|
||||
SPT_ANIM_SPEED = 3,
|
||||
SPT_BIRTH_FUNC = 4,
|
||||
SPT_JOINT_REFPOINT = 5,
|
||||
SPT_NUM = 6,
|
||||
SPT_SOUND = 7,
|
||||
MISC_FIELDS_END = 8,
|
||||
SPRITE_FIELDS_START = 9,
|
||||
SPT_X = 10,
|
||||
SPT_Y = 11,
|
||||
SPT_Z = 12,
|
||||
SPT_SCALE_X = 13,
|
||||
SPT_ROT_X = 14,
|
||||
SPT_ROT_Y = 15,
|
||||
SPT_ROT_Z = 16,
|
||||
SPT_SCALE_Y = 17,
|
||||
SPT_R = 18,
|
||||
SPT_G = 19,
|
||||
SPT_B = 20,
|
||||
SPT_A = 21,
|
||||
SPRITE_FIELDS_END = 22,
|
||||
CPU_FIELDS_START = 23,
|
||||
SPT_OMEGA = 24,
|
||||
SPT_VEL_X = 25,
|
||||
SPT_VEL_Y = 26,
|
||||
SPT_VEL_Z = 27,
|
||||
SPT_SCALEVEL_X = 28,
|
||||
SPT_ROTVEL_X = 29,
|
||||
SPT_ROTVEL_Y = 30,
|
||||
SPT_ROTVEL_Z = 31,
|
||||
SPT_SCALEVEL_Y = 32,
|
||||
SPT_FADE_R = 33,
|
||||
SPT_FADE_G = 34,
|
||||
SPT_FADE_B = 35,
|
||||
SPT_FADE_A = 36,
|
||||
SPT_ACCEL_X = 37,
|
||||
SPT_ACCEL_Y = 38,
|
||||
SPT_ACCEL_Z = 39,
|
||||
SPT_DUMMY = 40,
|
||||
SPT_QUAT_X = 41,
|
||||
SPT_QUAT_Y = 42,
|
||||
SPT_QUAT_Z = 43,
|
||||
SPT_QUAD_W = 44,
|
||||
SPT_FRICTION = 45,
|
||||
SPT_TIMER = 46,
|
||||
SPT_FLAGS = 47,
|
||||
SPT_USERDATA = 48,
|
||||
SPT_FUNC = 49,
|
||||
SPT_NEXT_TIME = 50,
|
||||
SPT_NEXT_LAUNCHER = 51,
|
||||
CPU_FIELDS_END = 52,
|
||||
LAUNCH_FIELDS_START = 53,
|
||||
SPT_LAUNCHROT_X = 54,
|
||||
SPT_LAUNCHROT_Y = 55,
|
||||
SPT_LAUNCHROT_Z = 56,
|
||||
SPT_LAUNCHROT_W = 57,
|
||||
SPT_CONEROT_X = 58,
|
||||
SPT_CONEROT_Y = 59,
|
||||
SPT_CONEROT_Z = 60,
|
||||
SPT_CONEROT_W = 61,
|
||||
SPT_CONEROT_RADIUS = 62,
|
||||
SPT_ROTATE_Y = 63,
|
||||
LAUNCH_FIELDS_END = 64,
|
||||
SPT_SCALE = 65,
|
||||
SPT_SCALEVEL = 66,
|
||||
SPT_END = 67,
|
||||
};
|
||||
|
||||
// flag vals:
|
||||
// 0: timer, flags, end
|
||||
// 1: texture, float, random-rangef
|
||||
// 3: integer
|
||||
// 6: next launcher
|
||||
|
||||
// flag bits
|
||||
// 2: number is an integer
|
||||
// 4: launcher index
|
||||
|
||||
enum class FieldKind {
|
||||
FLOAT,
|
||||
TEXTURE_ID,
|
||||
FLOAT_WITH_RAND,
|
||||
METER_WITH_RAND,
|
||||
DEGREES_WITH_RAND,
|
||||
// INT_WITH_RAND,
|
||||
PLAIN_INT,
|
||||
PLAIN_INT_WITH_RANDS,
|
||||
CPUINFO_FLAGS,
|
||||
END_FLAG,
|
||||
LAUNCHER_BY_ID,
|
||||
NO_FANCY_DECOMP,
|
||||
FUNCTION,
|
||||
USERDATA,
|
||||
INVALID
|
||||
};
|
||||
|
||||
struct SparticleFieldDecomp {
|
||||
bool known = false; // error if we try to decomp one that isn't known
|
||||
FieldKind kind = FieldKind::INVALID;
|
||||
};
|
||||
|
||||
const SparticleFieldDecomp field_kinds[68] = {
|
||||
{false}, // MISC_FIELDS_START = 0
|
||||
{true, FieldKind::TEXTURE_ID}, // SPT_TEXTURE = 1
|
||||
{false}, // SPT_ANIM = 2
|
||||
{false}, // SPT_ANIM_SPEED = 3
|
||||
{true, FieldKind::FUNCTION}, // SPT_BIRTH_FUNC = 4
|
||||
{false}, // SPT_JOINT/REFPOINT = 5
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_NUM = 6
|
||||
{false}, // SPT_SOUND = 7
|
||||
{false}, // MISC_FIELDS_END = 8
|
||||
{false}, // SPRITE_FIELDS_START = 9
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_X = 10
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_Y = 11
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_Z = 12
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_SCALE_X = 13
|
||||
{true, FieldKind::PLAIN_INT}, // SPT_ROT_X = 14
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_ROT_Y = 15
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_ROT_Z = 16
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_SCALE_Y = 17
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_R = 18
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_G = 19
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_B = 20
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_A = 21
|
||||
{false}, // SPRITE_FIELDS_END = 22
|
||||
{false}, // CPU_FIELDS_START = 23
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_OMEGA = 24
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_VEL_X = 25 (likely m/s)
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_VEL_Y = 26
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_VEL_Z = 27
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_SCALEVEL_X = 28
|
||||
{false}, // SPT_ROTVEL_X = 29
|
||||
{false}, // SPT_ROTVEL_Y = 30
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_ROTVEL_Z = 31
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_SCALEVEL_Y = 32
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_FADE_R = 33
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_FADE_G = 34
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_FADE_B = 35
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_FADE_A = 36
|
||||
{false}, // SPT_ACCEL_X = 37
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_ACCEL_Y = 38
|
||||
{false}, // SPT_ACCEL_Z = 39
|
||||
{false}, // SPT_DUMMY = 40
|
||||
{false}, // SPT_QUAT_X = 41
|
||||
{false}, // SPT_QUAT_Y = 42
|
||||
{false}, // SPT_QUAT_Z = 43
|
||||
{false}, // SPT_QUAD_W = 44
|
||||
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_FRICTION = 45
|
||||
{true, FieldKind::PLAIN_INT_WITH_RANDS}, // SPT_TIMER = 46
|
||||
{true, FieldKind::CPUINFO_FLAGS}, // SPT_FLAGS = 47
|
||||
{true, FieldKind::USERDATA}, // SPT_USERDATA = 48
|
||||
{true, FieldKind::FUNCTION}, // SPT_FUNC = 49
|
||||
{true, FieldKind::PLAIN_INT_WITH_RANDS}, // SPT_NEXT_TIME = 50
|
||||
{true, FieldKind::LAUNCHER_BY_ID}, // SPT_NEXT_LAUNCHER = 51
|
||||
{false}, // CPU_FIELDS_END = 52
|
||||
{false}, // LAUNCH_FIELDS_START = 53
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_LAUNCHROT_X = 54
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_LAUNCHROT_Y = 55
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_LAUNCHROT_Z = 56
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_LAUNCHROT_W = 57
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_CONEROT_X = 58
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_CONEROT_Y = 59
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_CONEROT_Z = 60
|
||||
{false}, // SPT_CONEROT_W = 61
|
||||
{true, FieldKind::METER_WITH_RAND}, // SPT_CONEROT_RADIUS = 62
|
||||
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_ROTATE_Y = 63
|
||||
{false}, // LAUNCH_FIELDS_END = 64
|
||||
{false}, // SPT_SCALE = 65
|
||||
{false}, // SPT_SCALEVEL = 66
|
||||
{true, FieldKind::END_FLAG}, // SPT_END = 67
|
||||
|
||||
};
|
||||
|
||||
std::string make_flags_str(const std::vector<std::string>& flags) {
|
||||
if (flags.empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string result = " :flags (";
|
||||
for (auto& x : flags) {
|
||||
result += x;
|
||||
result += ' ';
|
||||
}
|
||||
result.pop_back();
|
||||
result += ')';
|
||||
return result;
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_tex_field_init(const std::vector<LinkedWord>& words,
|
||||
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(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 0);
|
||||
assert(flag_name == "plain-v1");
|
||||
|
||||
auto tex_id_type = TypeSpec("texture-id");
|
||||
auto tex_id_str = bitfield_defs_print(
|
||||
tex_id_type, decompile_bitfield_from_int(tex_id_type, ts, words.at(1).data));
|
||||
|
||||
return pretty_print::to_symbol(fmt::format("(sp-tex {} {})", field_name, tex_id_str.print()));
|
||||
}
|
||||
|
||||
float word_as_float(const LinkedWord& w) {
|
||||
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);
|
||||
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(2).data == 0);
|
||||
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));
|
||||
}
|
||||
|
||||
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).data == 0);
|
||||
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).data == 0);
|
||||
assert(flag_name == "plain-v1");
|
||||
assert(field_name == "spt-end");
|
||||
return pretty_print::to_symbol("(sp-end)");
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_int_with_rand_to_float(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(flag_name == "int-with-rand");
|
||||
|
||||
return pretty_print::to_symbol(fmt::format("(sp-rnd-int {} {} {} {})", field_name,
|
||||
word_as_s32(words.at(1)), word_as_s32(words.at(2)),
|
||||
float_to_string(word_as_float(words.at(3)))));
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_float_with_rand_init(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
if (flag_name == "int-with-rand") {
|
||||
return decompile_sparticle_int_with_rand_to_float(words, field_name, flag_name);
|
||||
}
|
||||
assert(flag_name == "float-with-rand");
|
||||
|
||||
float range = word_as_float(words.at(2));
|
||||
float mult = word_as_float(words.at(3));
|
||||
|
||||
if (range == 0.f && mult == 1.f) {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-flt {} {})", field_name, float_to_string(word_as_float(words.at(1)))));
|
||||
} else {
|
||||
return pretty_print::to_symbol(fmt::format("(sp-rnd-flt {} {} {} {})", field_name,
|
||||
float_to_string(word_as_float(words.at(1))),
|
||||
float_to_string(range), float_to_string(mult)));
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_userdata(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name,
|
||||
const goos::Object& original) {
|
||||
if (flag_name == "int-with-rand" || flag_name == "float-with-rand") {
|
||||
return decompile_sparticle_float_with_rand_init(words, field_name, flag_name);
|
||||
} else {
|
||||
return original;
|
||||
}
|
||||
}
|
||||
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(2).data == 0);
|
||||
assert(words.at(3).kind == LinkedWord::PLAIN_DATA);
|
||||
assert(words.at(3).data == 1);
|
||||
assert(flag_name == "plain-v1");
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-int {} {})", field_name, word_as_s32(words.at(1))));
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_int_with_rand_init(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
assert(flag_name == "plain-v1");
|
||||
if (word_as_s32(words.at(2)) == 0 && word_as_s32(words.at(3)) == 1) {
|
||||
return decompile_sparticle_int_init(words, field_name, flag_name);
|
||||
}
|
||||
return pretty_print::to_symbol(fmt::format("(sp-int-plain-rnd {} {} {} {})", field_name,
|
||||
word_as_s32(words.at(1)), word_as_s32(words.at(2)),
|
||||
word_as_s32(words.at(3))));
|
||||
}
|
||||
|
||||
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(2).data == 0);
|
||||
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(
|
||||
fmt::format("(sp-launcher-by-id {} {})", field_name, word_as_s32(words.at(1))));
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_flags(const std::vector<LinkedWord>& words,
|
||||
const TypeSystem& ts,
|
||||
const std::string& field_name,
|
||||
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).data == 1);
|
||||
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)));
|
||||
std::string result = "(sp-cpuinfo-flags";
|
||||
for (const auto& def : flag_def) {
|
||||
result += ' ';
|
||||
result += def;
|
||||
}
|
||||
result += ')';
|
||||
return pretty_print::to_symbol(result);
|
||||
}
|
||||
|
||||
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(2).data == 0);
|
||||
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(
|
||||
fmt::format("(sp-copy-from-other {} {})", field_name, word_as_s32(words.at(1))));
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_float_meters_with_rand_init(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
if (flag_name == "int-with-rand") {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-rnd-int-flt {} (meters {}) {} {})", field_name,
|
||||
float_to_string(word_as_float(words.at(1)) / METER_LENGTH),
|
||||
word_as_s32(words.at(2)), float_to_string(word_as_float(words.at(3)))));
|
||||
}
|
||||
assert(flag_name == "float-with-rand");
|
||||
|
||||
float range = word_as_float(words.at(2));
|
||||
float mult = word_as_float(words.at(3));
|
||||
|
||||
if (range == 0.f && mult == 1.f) {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-flt {} (meters {}))", field_name,
|
||||
float_to_string(word_as_float(words.at(1)) / METER_LENGTH)));
|
||||
} else {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-rnd-flt {} (meters {}) (meters {}) {})", field_name,
|
||||
float_to_string(word_as_float(words.at(1)) / METER_LENGTH),
|
||||
float_to_string(word_as_float(words.at(2)) / METER_LENGTH),
|
||||
float_to_string(word_as_float(words.at(3)))));
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_float_degrees_with_rand_init(const std::vector<LinkedWord>& words,
|
||||
const std::string& field_name,
|
||||
const std::string& flag_name) {
|
||||
if (flag_name == "int-with-rand") {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-rnd-int-flt {} (degrees {}) {} {})", field_name,
|
||||
float_to_string(word_as_float(words.at(1)) / DEGREES_LENGTH),
|
||||
word_as_s32(words.at(2)), float_to_string(word_as_float(words.at(3)))));
|
||||
}
|
||||
assert(flag_name == "float-with-rand");
|
||||
float range = word_as_float(words.at(2));
|
||||
float mult = word_as_float(words.at(3));
|
||||
if (range == 0.f && mult == 1.f) {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-flt {} (degrees {}))", field_name,
|
||||
float_to_string(word_as_float(words.at(1)) / DEGREES_LENGTH)));
|
||||
} else {
|
||||
return pretty_print::to_symbol(
|
||||
fmt::format("(sp-rnd-flt {} (degrees {}) (degrees {}) {})", field_name,
|
||||
float_to_string(word_as_float(words.at(1)) / DEGREES_LENGTH),
|
||||
float_to_string(word_as_float(words.at(2)) / DEGREES_LENGTH),
|
||||
float_to_string(word_as_float(words.at(3)))));
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_group_item(const TypeSpec& type,
|
||||
const DecompilerLabel& label,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file) {
|
||||
auto normal = decompile_structure(type, label, labels, words, ts, file, false);
|
||||
fmt::print("Doing: {}\n", normal.print());
|
||||
auto uncast_type_info = ts.lookup_type(type);
|
||||
auto type_info = dynamic_cast<StructureType*>(uncast_type_info);
|
||||
if (!type_info) {
|
||||
throw std::runtime_error(fmt::format("Type {} wasn't a structure type.", type.print()));
|
||||
}
|
||||
assert(type_info->get_size_in_memory() == 0x1c);
|
||||
|
||||
// get words for real
|
||||
auto offset_location = label.offset - type_info->get_offset();
|
||||
int word_count = (type_info->get_size_in_memory() + 3) / 4;
|
||||
std::vector<LinkedWord> obj_words;
|
||||
obj_words.insert(obj_words.begin(),
|
||||
words.at(label.target_segment).begin() + (offset_location / 4),
|
||||
words.at(label.target_segment).begin() + (offset_location / 4) + word_count);
|
||||
|
||||
// 0 launcher
|
||||
// 4 fade-after (meters)
|
||||
// 8 falloff-to (meters)
|
||||
// flags, period
|
||||
// length, offset
|
||||
// hour-mask
|
||||
// binding
|
||||
|
||||
s32 launcher = word_as_s32(obj_words.at(0));
|
||||
float fade_after_meters = word_as_float(obj_words.at(1)) / METER_LENGTH;
|
||||
float falloff_to_meters = word_as_float(obj_words.at(2)) / METER_LENGTH;
|
||||
u32 fp = word_as_s32(obj_words.at(3));
|
||||
u16 flags = fp & 0xffff;
|
||||
u16 period = fp >> 16;
|
||||
u32 lo = word_as_s32(obj_words.at(4));
|
||||
u16 length = lo & 0xffff;
|
||||
u16 offset = lo >> 16;
|
||||
u32 hour_mask = word_as_s32(obj_words.at(5));
|
||||
u32 binding = word_as_s32(obj_words.at(6));
|
||||
|
||||
std::string result =
|
||||
fmt::format("(sp-item {}", launcher); // use decimal, so it matches array idx
|
||||
|
||||
if (fade_after_meters != 0.0) {
|
||||
result += fmt::format(" :fade-after (meters {})", float_to_string(fade_after_meters));
|
||||
}
|
||||
|
||||
if (falloff_to_meters != 0.0) {
|
||||
result += fmt::format(" :falloff-to (meters {})", float_to_string(falloff_to_meters));
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
auto things = decompile_bitfield_enum_from_int(TypeSpec("sp-group-item-flag"), ts, flags);
|
||||
result += " :flags (";
|
||||
for (auto& thing : things) {
|
||||
result += thing;
|
||||
result += ' ';
|
||||
}
|
||||
result.pop_back();
|
||||
result += ')';
|
||||
}
|
||||
|
||||
if (period) {
|
||||
result += fmt::format(" :period {}", period);
|
||||
}
|
||||
|
||||
if (length) {
|
||||
result += fmt::format(" :length {}", length);
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
result += fmt::format(" :offset {}", offset);
|
||||
}
|
||||
|
||||
if (hour_mask) {
|
||||
result += fmt::format(" :hour-mask #b{:b}", hour_mask);
|
||||
}
|
||||
|
||||
if (binding) {
|
||||
result += fmt::format(" :binding {}", binding);
|
||||
}
|
||||
|
||||
result += ')';
|
||||
fmt::print("Result: {}\n", result);
|
||||
return pretty_print::to_symbol(result);
|
||||
}
|
||||
|
||||
goos::Object decompile_sparticle_field_init(const TypeSpec& type,
|
||||
const DecompilerLabel& label,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file) {
|
||||
auto normal = decompile_structure(type, label, labels, words, ts, file, false);
|
||||
fmt::print("Doing: {}\n", normal.print());
|
||||
auto uncast_type_info = ts.lookup_type(type);
|
||||
auto type_info = dynamic_cast<StructureType*>(uncast_type_info);
|
||||
if (!type_info) {
|
||||
throw std::runtime_error(fmt::format("Type {} wasn't a structure type.", type.print()));
|
||||
}
|
||||
assert(type_info->get_size_in_memory() == 16);
|
||||
|
||||
// get words for real
|
||||
auto offset_location = label.offset - type_info->get_offset();
|
||||
int word_count = (type_info->get_size_in_memory() + 3) / 4;
|
||||
std::vector<LinkedWord> obj_words;
|
||||
obj_words.insert(obj_words.begin(),
|
||||
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);
|
||||
u16 field_id = obj_words.at(0).data & 0xffff;
|
||||
u16 flags = obj_words.at(0).data >> 16;
|
||||
|
||||
assert(field_id <= (u32)FieldId::SPT_END);
|
||||
auto field_name = decompile_int_enum_from_int(TypeSpec("sp-field-id"), ts, field_id);
|
||||
const auto& field_info = field_kinds[field_id];
|
||||
if (!field_info.known) {
|
||||
throw std::runtime_error("Unknown sparticle field: " + field_name);
|
||||
}
|
||||
|
||||
auto flag_name = decompile_int_enum_from_int(TypeSpec("sp-flag"), ts, flags);
|
||||
goos::Object result;
|
||||
|
||||
if (flag_name == "copy-from-other-field") {
|
||||
result = decompile_sparticle_from_other(obj_words, field_name, flag_name);
|
||||
} else {
|
||||
switch (field_info.kind) {
|
||||
case FieldKind::TEXTURE_ID:
|
||||
result = decompile_sparticle_tex_field_init(obj_words, ts, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::FLOAT_WITH_RAND:
|
||||
result = decompile_sparticle_float_with_rand_init(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::METER_WITH_RAND:
|
||||
result = decompile_sparticle_float_meters_with_rand_init(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::DEGREES_WITH_RAND:
|
||||
result = decompile_sparticle_float_degrees_with_rand_init(obj_words, field_name, flag_name);
|
||||
break;
|
||||
// case FieldKind::INT_WITH_RAND:
|
||||
// result = decompile_sparticle_int_with_rand_init(obj_words, field_name, flag_name);
|
||||
// break;
|
||||
case FieldKind::PLAIN_INT_WITH_RANDS:
|
||||
result = decompile_sparticle_int_with_rand_init(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::PLAIN_INT:
|
||||
result = decompile_sparticle_int_init(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::CPUINFO_FLAGS:
|
||||
result = decompile_sparticle_flags(obj_words, ts, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::END_FLAG:
|
||||
result = decompile_sparticle_end(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::LAUNCHER_BY_ID:
|
||||
result = decompile_sparticle_launcher_by_id(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::NO_FANCY_DECOMP:
|
||||
result = normal;
|
||||
break;
|
||||
case FieldKind::FUNCTION:
|
||||
result = decompile_sparticle_func(obj_words, field_name, flag_name);
|
||||
break;
|
||||
case FieldKind::USERDATA:
|
||||
result = decompile_sparticle_userdata(obj_words, field_name, flag_name, normal);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("Result: {}\n\n", result.print());
|
||||
return result;
|
||||
}
|
||||
} // namespace decompiler
|
||||
|
||||
/*
|
||||
(deftype sp-field-init-spec (structure)
|
||||
((field sp-field-id :offset-assert 0)
|
||||
(flags sp-flag :offset-assert 2)
|
||||
(initial-valuef float :offset-assert 4)
|
||||
(random-rangef float :offset-assert 8)
|
||||
(random-multf float :offset-assert 12)
|
||||
(initial-value int32 :offset 4)
|
||||
(random-range int32 :offset 8)
|
||||
(random-mult int32 :offset 12)
|
||||
(sym symbol :offset 4) ;; moved
|
||||
(func function :offset 4)
|
||||
(tex uint32 :offset 4)
|
||||
(pntr pointer :offset 4)
|
||||
;; gap
|
||||
(sound basic :offset 4)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x10
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
*/
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/goos/Object.h"
|
||||
#include "common/type_system/TypeSpec.h"
|
||||
#include "decompiler/Disasm/DecompilerLabel.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
namespace decompiler {
|
||||
goos::Object decompile_sparticle_field_init(const TypeSpec& type,
|
||||
const DecompilerLabel& label,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file);
|
||||
goos::Object decompile_sparticle_group_item(const TypeSpec& type,
|
||||
const DecompilerLabel& label,
|
||||
const std::vector<DecompilerLabel>& labels,
|
||||
const std::vector<std::vector<LinkedWord>>& words,
|
||||
const TypeSystem& ts,
|
||||
const LinkedObjectFile* file);
|
||||
} // namespace decompiler
|
||||
@@ -203,4 +203,5 @@
|
||||
- It is now a warning to redefine a constant.
|
||||
- Fix a bug where the size of static boxed arrays was only `length` and not `allocated-length`
|
||||
- It is now possible to call a method on a forward declared type. The forward declared type must be a basic.
|
||||
- Using `->` on a plain `pointer` or `inline-array` now generates an error instead of crashing the compiler
|
||||
- Using `->` on a plain `pointer` or `inline-array` now generates an error instead of crashing the compiler
|
||||
- It is now possible to use a macro to provide a static inline array element definition
|
||||
@@ -0,0 +1,326 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; .function sp-init-fields!
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;BAD PROLOGUE
|
||||
;; Warnings:
|
||||
;; INFO: Flagged as asm by config
|
||||
;; INFO: Assembly Function
|
||||
|
||||
# inputs: a0 output-data (multiple types)
|
||||
# a1 init-spec-array
|
||||
# a2 start-field-idx
|
||||
# a3 end-field-idx (not inclusive, not that it matters?)
|
||||
# t0 usually #f.
|
||||
|
||||
|
||||
## Initialize
|
||||
B0:
|
||||
L155:
|
||||
or v1, a0, r0
|
||||
or v1, a2, r0
|
||||
or v1, a3, r0
|
||||
or v1, t0, r0
|
||||
sll r0, r0, 0
|
||||
daddiu a2, a2, 1 # actual first field to care about.
|
||||
or v0, a1, r0 # v0 = current field?
|
||||
|
||||
## Loop to advance our init spec until we hit a field >= our minimum
|
||||
B1:
|
||||
L156:
|
||||
lh a1, 0(v0) # a1 = field-id
|
||||
sll r0, r0, 0
|
||||
dsubu a1, a1, a2 #
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
bltzl a1, L156
|
||||
B2:
|
||||
daddiu v0, v0, 16
|
||||
|
||||
## If our range is size 0 (or negative), exit early, we have no fields to initialize
|
||||
B3:
|
||||
dsubu a1, a2, a3
|
||||
sll r0, r0, 0
|
||||
bgez a1, L169
|
||||
sll r0, r0, 0
|
||||
|
||||
|
||||
# LOOP TOP:
|
||||
# v0 = init-spec, a2 = field to init,
|
||||
B4:
|
||||
L157:
|
||||
lh a1, 0(v0)
|
||||
sll r0, r0, 0
|
||||
bne a1, a2, L167 # jump to end if field doesn't match spec.
|
||||
vrget.xyzw vf1
|
||||
|
||||
B5:
|
||||
vsqrt Q, vf1.x
|
||||
lh a1, 2(v0) # a1 = flags
|
||||
vaddq.x vf2, vf0, Q # more random?
|
||||
lw t2, 8(v0) # t2 = random-range (float or int possible here)
|
||||
addiu v1, r0, 7
|
||||
beq a2, v1, L159 # spt-sound
|
||||
addiu t1, r0, 1
|
||||
|
||||
B6:
|
||||
beq a1, t1, L160 # flags = 1.
|
||||
addiu t1, r0, 2
|
||||
|
||||
B7:
|
||||
beq a1, t1, L162 # flags = 2.
|
||||
addiu t1, r0, 3
|
||||
|
||||
B8:
|
||||
beq a1, t1, L163 # flags = 3
|
||||
addiu t1, r0, 5
|
||||
|
||||
B9:
|
||||
beq a1, t1, L164 # flags = 5
|
||||
addiu t1, r0, 6
|
||||
|
||||
B10:
|
||||
beq a1, t1, L165 # flags = 6
|
||||
addiu t1, r0, 4
|
||||
|
||||
B11:
|
||||
beq a1, t1, L166 # flags = 4
|
||||
sll r0, r0, 0
|
||||
|
||||
B12:
|
||||
beq t2, r0, L158 # flags = 0
|
||||
sll r0, r0, 0
|
||||
|
||||
B13:
|
||||
vrxorw vf2 # other flags
|
||||
lw t1, 12(v0)
|
||||
vrnext.xyzw vf1
|
||||
lw t3, 4(v0)
|
||||
vsubw.xyzw vf1, vf1, vf0
|
||||
sll r0, r0, 0
|
||||
qmtc2.i vf2, t2
|
||||
sll r0, r0, 0
|
||||
vitof0.xyzw vf2, vf2
|
||||
sll r0, r0, 0
|
||||
vmul.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
vftoi0.xyzw vf1, vf1
|
||||
sll r0, r0, 0
|
||||
qmfc2.i t2, vf1
|
||||
sll r0, r0, 0
|
||||
mult3 t2, t2, t1
|
||||
sll r0, r0, 0
|
||||
daddu t2, t2, t3
|
||||
daddiu a2, a2, 1
|
||||
sw t2, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B14:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B15:
|
||||
L158: # flags = 0
|
||||
lw t3, 4(v0) ## int32
|
||||
daddiu a2, a2, 1
|
||||
sw t3, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B16:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B17:
|
||||
L159: ## special case for sound.
|
||||
lw t3, 4(v0)
|
||||
daddiu a2, a2, 1
|
||||
sw t3, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B18:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B19:
|
||||
L160: ## flags = 1
|
||||
beq t2, r0, L161 # skip if range = 0
|
||||
vrxorw vf2
|
||||
|
||||
B20:
|
||||
vrnext.xyzw vf1
|
||||
lw t1, 12(v0) ## t1 = multiplier
|
||||
vsubw.xyzw vf1, vf1, vf0
|
||||
lw t3, 4(v0) ## t3 = initial-value
|
||||
qmtc2.i vf2, t2
|
||||
sll r0, r0, 0
|
||||
vmul.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
qmtc2.i vf2, t1
|
||||
sll r0, r0, 0
|
||||
vmul.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
qmtc2.i vf2, t3
|
||||
sll r0, r0, 0
|
||||
vadd.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
qmfc2.i t2, vf1
|
||||
daddiu a2, a2, 1
|
||||
sw t2, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B21:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B22:
|
||||
L161:
|
||||
lw t3, 4(v0)
|
||||
daddiu a2, a2, 1
|
||||
sw t3, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B23:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B24:
|
||||
L162:
|
||||
beq t2, r0, L161 ## flags = 2
|
||||
vrxorw vf2
|
||||
|
||||
B25:
|
||||
vrnext.xyzw vf1
|
||||
lw t1, 12(v0)
|
||||
vsubw.xyzw vf1, vf1, vf0
|
||||
daddiu t2, t2, 1
|
||||
qmtc2.i vf2, t2
|
||||
lw t3, 4(v0)
|
||||
vitof0.xyzw vf2, vf2
|
||||
sll r0, r0, 0
|
||||
vmul.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
vftoi0.xyzw vf1, vf1
|
||||
sll r0, r0, 0
|
||||
vitof0.xyzw vf1, vf1
|
||||
sll r0, r0, 0
|
||||
qmtc2.i vf2, t1
|
||||
sll r0, r0, 0
|
||||
vmul.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
qmtc2.i vf2, t3
|
||||
sll r0, r0, 0
|
||||
vadd.xyzw vf1, vf1, vf2
|
||||
sll r0, r0, 0
|
||||
qmfc2.i t2, vf1
|
||||
daddiu a2, a2, 1
|
||||
sw t2, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B26:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B27:
|
||||
L163: ## flags = 3
|
||||
lw t1, 4(v0) # t1 = init-val
|
||||
sll r0, r0, 0
|
||||
dsll t1, t1, 2 # val * 4
|
||||
sll r0, r0, 0
|
||||
daddu t1, t1, a0 # t1 = dest + val*4
|
||||
sll r0, r0, 0
|
||||
lw t3, 0(t1)
|
||||
daddiu a2, a2, 1
|
||||
sw t3, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B28:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B29:
|
||||
L164: ## flags = 5
|
||||
lw t1, 4(v0)
|
||||
sll r0, r0, 0
|
||||
lw t3, 0(t1)
|
||||
daddiu a2, a2, 1
|
||||
sw t3, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B30:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B31:
|
||||
L165:
|
||||
lw t1, *part-id-table*(s7) ## flags = 6
|
||||
sll r0, r0, 0
|
||||
lw t3, 4(v0)
|
||||
sll r0, r0, 0
|
||||
dsll t3, t3, 2
|
||||
daddiu t1, t1, 12
|
||||
daddu t3, t3, t1
|
||||
sll r0, r0, 0
|
||||
lw t2, 0(t3)
|
||||
daddiu a2, a2, 1
|
||||
sw t2, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B32:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B33:
|
||||
L166:
|
||||
lw t3, 4(v0) ## flags = 4
|
||||
daddiu a2, a2, 1
|
||||
sw t3, 0(a0)
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
daddiu v0, v0, 16
|
||||
|
||||
B34:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
B35:
|
||||
L167:
|
||||
bnel t0, s7, L168
|
||||
B36:
|
||||
sw r0, 0(a0)
|
||||
|
||||
B37:
|
||||
L168:
|
||||
daddiu a2, a2, 1
|
||||
daddiu a0, a0, 4
|
||||
bne a2, a3, L157
|
||||
sll r0, r0, 0
|
||||
|
||||
B38:
|
||||
L169:
|
||||
jr ra
|
||||
sll r0, r0, 0
|
||||
|
||||
jr ra
|
||||
daddu sp, sp, r0
|
||||
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,8 @@ set(RUNTIME_SOURCE
|
||||
kernel/ksound.cpp
|
||||
mips2c/mips2c_table.cpp
|
||||
mips2c/functions/draw_string.cpp
|
||||
mips2c/functions/sparticle.cpp
|
||||
mips2c/functions/sparticle_launcher.cpp
|
||||
mips2c/functions/test_func.cpp
|
||||
overlord/dma.cpp
|
||||
overlord/fake_iso.cpp
|
||||
@@ -82,9 +84,12 @@ set(RUNTIME_SOURCE
|
||||
graphics/dma/dma_copy.cpp
|
||||
graphics/dma/gs.cpp
|
||||
graphics/opengl_renderer/BucketRenderer.cpp
|
||||
graphics/opengl_renderer/debug_gui.cpp
|
||||
graphics/opengl_renderer/DirectRenderer.cpp
|
||||
graphics/opengl_renderer/OpenGLRenderer.cpp
|
||||
graphics/opengl_renderer/Shader.cpp
|
||||
graphics/opengl_renderer/SpriteRenderer.cpp
|
||||
graphics/opengl_renderer/TextureUploadHandler.cpp
|
||||
graphics/texture/TextureConverter.cpp
|
||||
graphics/texture/TexturePool.cpp
|
||||
graphics/pipelines/opengl.cpp
|
||||
|
||||
@@ -10,6 +10,10 @@ TWEAKVAL.MUS resources/TWEAKVAL.MUS
|
||||
VAGDIR.AYB resources/VAGDIR.AYB
|
||||
SCREEN1.USA resources/SCREEN1.USA
|
||||
0COMMON.TXT out/iso/0COMMON.TXT
|
||||
1COMMON.TXT out/iso/1COMMON.TXT
|
||||
2COMMON.TXT out/iso/2COMMON.TXT
|
||||
3COMMON.TXT out/iso/3COMMON.TXT
|
||||
4COMMON.TXT out/iso/4COMMON.TXT
|
||||
5COMMON.TXT out/iso/5COMMON.TXT
|
||||
0TEST.TXT out/iso/0TEST.TXT
|
||||
VI1.DGO out/iso/VI1.DGO
|
||||
|
||||
@@ -21,9 +21,10 @@ std::string VifCode::print() {
|
||||
case Kind::NOP:
|
||||
result = "NOP";
|
||||
break;
|
||||
case Kind::STCYCL:
|
||||
result = "STCYCL";
|
||||
break;
|
||||
case Kind::STCYCL: {
|
||||
VifCodeStcycl stcycl(immediate);
|
||||
result = fmt::format("STCYCL cl: {} wl: {}", stcycl.cl, stcycl.wl);
|
||||
} break;
|
||||
case Kind::OFFSET:
|
||||
result = "OFFSET";
|
||||
break;
|
||||
@@ -48,7 +49,6 @@ std::string VifCode::print() {
|
||||
case Kind::FLUSH:
|
||||
result = "FLUSH";
|
||||
break;
|
||||
|
||||
case Kind::FLUSHA:
|
||||
result = "FLUSHA";
|
||||
break;
|
||||
@@ -81,7 +81,8 @@ std::string VifCode::print() {
|
||||
break;
|
||||
default:
|
||||
fmt::print("Unhandled vif code {}", (int)kind);
|
||||
assert(false);
|
||||
result = "???";
|
||||
// assert(false);
|
||||
break;
|
||||
}
|
||||
// TODO: the rest of the VIF code.
|
||||
|
||||
+34
-1
@@ -10,6 +10,15 @@
|
||||
#include "common/util/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
struct DmaStats {
|
||||
double sync_time_ms = 0;
|
||||
int num_tags = 0;
|
||||
int num_data_bytes = 0;
|
||||
int num_chunks = 0;
|
||||
int num_copied_bytes = 0;
|
||||
int num_fixups = 0;
|
||||
};
|
||||
|
||||
struct DmaTag {
|
||||
enum class Kind : u8 {
|
||||
REFE = 0,
|
||||
@@ -45,6 +54,7 @@ struct VifCode {
|
||||
BASE = 0b11,
|
||||
ITOP = 0b100,
|
||||
STMOD = 0b101,
|
||||
PC_PORT = 0b1000, // not a valid PS2 VIF code, but we use this to signal PC-PORT specific stuff
|
||||
MSK3PATH = 0b110,
|
||||
MARK = 0b111,
|
||||
FLUSHE = 0b10000,
|
||||
@@ -59,7 +69,8 @@ struct VifCode {
|
||||
MPG = 0b1001010,
|
||||
DIRECT = 0b1010000,
|
||||
DIRECTHL = 0b1010001,
|
||||
UNPACK_MASK = 0b1100000 // unpack is a bunch of commands.
|
||||
UNPACK_MASK = 0b1100000, // unpack is a bunch of commands.
|
||||
UNPACK_V4_32 = 0b1101100,
|
||||
};
|
||||
|
||||
VifCode(u32 value) {
|
||||
@@ -76,3 +87,25 @@ struct VifCode {
|
||||
|
||||
std::string print();
|
||||
};
|
||||
|
||||
struct VifCodeStcycl {
|
||||
explicit VifCodeStcycl(const VifCode& code) {
|
||||
cl = code.immediate & 0xff;
|
||||
wl = (code.immediate >> 8);
|
||||
}
|
||||
|
||||
u16 cl;
|
||||
u16 wl;
|
||||
};
|
||||
|
||||
struct VifCodeUnpack {
|
||||
explicit VifCodeUnpack(const VifCode& code) {
|
||||
addr_qw = code.immediate & 0b1111111111;
|
||||
is_unsigned = (code.immediate & (1 << 14));
|
||||
use_tops_flag = (code.immediate & (1 << 15));
|
||||
}
|
||||
|
||||
u16 addr_qw;
|
||||
bool is_unsigned; // only care for 8/16 bit data.
|
||||
bool use_tops_flag; // uses double buffering
|
||||
};
|
||||
@@ -28,6 +28,9 @@ struct DmaTransfer {
|
||||
|
||||
u32 vif0() const { return transferred_tag & 0xffffffff; }
|
||||
u32 vif1() const { return (transferred_tag >> 32) & 0xffffffff; }
|
||||
|
||||
VifCode vifcode0() const { return VifCode(vif0()); }
|
||||
VifCode vifcode1() const { return VifCode(vif1()); }
|
||||
};
|
||||
|
||||
class DmaFollower {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
|
||||
#include "common/goal_constants.h"
|
||||
#include "game/graphics/dma/dma_chain_read.h"
|
||||
#include "dma_copy.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
/*!
|
||||
* Convert a DMA chain to an array of bytes that can be directly fed to VIF.
|
||||
@@ -27,6 +29,11 @@ std::vector<u8> flatten_dma(const DmaFollower& in) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void FixedChunkDmaCopier::serialize_last_result(Serializer& serializer) {
|
||||
serializer.from_ptr(&m_result.start_offset);
|
||||
serializer.from_pod_vector(&m_result.data);
|
||||
}
|
||||
|
||||
FixedChunkDmaCopier::FixedChunkDmaCopier(u32 main_memory_size)
|
||||
: m_main_memory_size(main_memory_size), m_chunk_count(main_memory_size / chunk_size) {
|
||||
assert(chunk_size * m_chunk_count == m_main_memory_size); // make sure the memory size is valid.
|
||||
@@ -35,15 +42,20 @@ FixedChunkDmaCopier::FixedChunkDmaCopier(u32 main_memory_size)
|
||||
}
|
||||
|
||||
const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool verify) {
|
||||
Timer timer;
|
||||
m_input_offset = offset;
|
||||
m_input_data = memory;
|
||||
std::fill(m_chunk_mask.begin(), m_chunk_mask.end(), false);
|
||||
m_fixups.clear();
|
||||
m_result.data.clear();
|
||||
m_result.stats = DmaStats();
|
||||
m_result.start_offset = 0;
|
||||
|
||||
DmaFollower dma(memory, offset);
|
||||
while (!dma.ended()) {
|
||||
auto tag_offset = dma.current_tag_offset();
|
||||
auto tag = dma.current_tag();
|
||||
m_result.stats.num_tags++;
|
||||
|
||||
// first, make sure we get this tag:
|
||||
u32 tag_chunk_idx = tag_offset / chunk_size;
|
||||
@@ -51,6 +63,7 @@ const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool ver
|
||||
m_chunk_mask.at(tag_chunk_idx) = true;
|
||||
|
||||
if (tag.addr) {
|
||||
assert(tag.addr > EE_MAIN_MEM_LOW_PROTECT);
|
||||
u32 addr_chunk_idx = tag.addr / chunk_size;
|
||||
u32 addr_offset_in_chunk = tag.addr % chunk_size;
|
||||
// next, make sure that we get the address (if applicable)
|
||||
@@ -66,6 +79,7 @@ const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool ver
|
||||
|
||||
auto transfer = dma.read_and_advance();
|
||||
if (transfer.size_bytes) {
|
||||
m_result.stats.num_data_bytes += transfer.size_bytes;
|
||||
u32 initial_chunk = transfer.data_offset / chunk_size;
|
||||
m_chunk_mask.at(initial_chunk) = true;
|
||||
s32 bytes_remaining = transfer.size_bytes;
|
||||
@@ -89,6 +103,9 @@ const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool ver
|
||||
}
|
||||
|
||||
m_result.data.resize(current_out_chunk * chunk_size);
|
||||
m_result.stats.num_chunks = current_out_chunk;
|
||||
m_result.stats.num_copied_bytes = m_result.data.size();
|
||||
m_result.stats.num_fixups = m_fixups.size();
|
||||
|
||||
// copy
|
||||
for (u32 chunk_idx = 0; chunk_idx < m_chunk_mask.size(); chunk_idx++) {
|
||||
@@ -119,5 +136,6 @@ const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool ver
|
||||
}
|
||||
}
|
||||
|
||||
m_result.stats.sync_time_ms = timer.getMs();
|
||||
return m_result;
|
||||
}
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "game/graphics/dma/dma_chain_read.h"
|
||||
#include "common/util/Serializer.h"
|
||||
|
||||
struct DmaData {
|
||||
u32 start_offset = 0;
|
||||
std::vector<u8> data;
|
||||
DmaStats stats;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -21,8 +23,14 @@ class FixedChunkDmaCopier {
|
||||
FixedChunkDmaCopier(u32 main_memory_size);
|
||||
|
||||
const DmaData& run(const void* memory, u32 offset, bool verify = false);
|
||||
|
||||
void serialize_last_result(Serializer& serializer);
|
||||
|
||||
const DmaData& get_last_result() const { return m_result; }
|
||||
|
||||
const void* get_last_input_data() const { return m_input_data; }
|
||||
u32 get_last_input_offset() const { return m_input_offset; }
|
||||
|
||||
private:
|
||||
struct Fixup {
|
||||
u32 source_chunk;
|
||||
@@ -36,6 +44,9 @@ class FixedChunkDmaCopier {
|
||||
u32 m_chunk_count = 0;
|
||||
std::vector<u32> m_chunk_mask;
|
||||
DmaData m_result;
|
||||
|
||||
u32 m_input_offset = 0;
|
||||
const void* m_input_data = nullptr;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@@ -348,4 +348,8 @@ std::string GsTex0::print() const {
|
||||
return fmt::format(
|
||||
"tbp0: {} tbw: {} psm: {} tw: {} th: {} tcc: {} tfx: {} cbp: {} cpsm: {} csm: {}\n", tbp0(),
|
||||
tbw(), psm(), tw(), th(), tcc(), tfx(), cbp(), cpsm(), csm());
|
||||
}
|
||||
|
||||
std::string GsPrim::print() const {
|
||||
return fmt::format("0x{:x}, kind {}\n", data, kind());
|
||||
}
|
||||
@@ -54,10 +54,15 @@ struct GifTag {
|
||||
std::string print() const;
|
||||
|
||||
GifTag(const u8* ptr) { memcpy(data, ptr, 16); }
|
||||
GifTag() = default;
|
||||
|
||||
u64 data[2];
|
||||
};
|
||||
|
||||
struct AdGif {
|
||||
GifTag giftag[5];
|
||||
};
|
||||
|
||||
std::string reg_descriptor_name(GifTag::RegisterDescriptor reg);
|
||||
|
||||
enum class GsRegisterAddress : u8 {
|
||||
@@ -247,6 +252,8 @@ struct GsPrim {
|
||||
|
||||
bool operator==(const GsPrim& other) const { return data == other.data; }
|
||||
bool operator!=(const GsPrim& other) const { return data != other.data; }
|
||||
|
||||
std::string print() const;
|
||||
};
|
||||
|
||||
struct GsTex0 {
|
||||
|
||||
@@ -12,7 +12,13 @@
|
||||
enum class BucketId {
|
||||
BUCKET0 = 0,
|
||||
BUCKET1 = 1,
|
||||
TFRAG_TEX_LEVEL0 = 5,
|
||||
SHRUB_TEX_LEVEL0 = 19,
|
||||
ALPHA_TEX_LEVEL0 = 31,
|
||||
PRIS_TEX_LEVEL0 = 48,
|
||||
WATER_TEX_LEVEL0 = 57,
|
||||
// ...
|
||||
PRE_SPRITE_TEX = 65, // maybe it's just common textures?
|
||||
SPRITE = 66,
|
||||
DEBUG_DRAW_0 = 67,
|
||||
DEBUG_DRAW_1 = 68,
|
||||
@@ -31,6 +37,10 @@ struct SharedRenderState {
|
||||
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.
|
||||
|
||||
void* ee_main_memory = nullptr;
|
||||
u32 offset_of_s7;
|
||||
bool dump_playback = false;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -44,7 +54,8 @@ class BucketRenderer {
|
||||
virtual ~BucketRenderer() = default;
|
||||
bool& enabled() { return m_enabled; }
|
||||
virtual bool empty() const { return false; }
|
||||
virtual void draw_debug_window() {}
|
||||
virtual void draw_debug_window() = 0;
|
||||
virtual void serialize(Serializer&) {}
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
@@ -60,4 +71,5 @@ class EmptyBucketRenderer : public BucketRenderer {
|
||||
EmptyBucketRenderer(const std::string& name, BucketId my_id);
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state) override;
|
||||
bool empty() const override { return true; }
|
||||
void draw_debug_window() override {}
|
||||
};
|
||||
@@ -5,8 +5,8 @@
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
DirectRenderer::DirectRenderer(const std::string& name, BucketId my_id, int batch_size)
|
||||
: BucketRenderer(name, my_id), m_prim_buffer(batch_size) {
|
||||
DirectRenderer::DirectRenderer(const std::string& name, BucketId my_id, int batch_size, Mode mode)
|
||||
: BucketRenderer(name, my_id), m_prim_buffer(batch_size), m_mode(mode) {
|
||||
glGenBuffers(1, &m_ogl.vertex_buffer);
|
||||
glGenBuffers(1, &m_ogl.color_buffer);
|
||||
glGenBuffers(1, &m_ogl.st_buffer);
|
||||
@@ -35,9 +35,6 @@ DirectRenderer::~DirectRenderer() {
|
||||
* Render from a DMA bucket.
|
||||
*/
|
||||
void DirectRenderer::render(DmaFollower& dma, SharedRenderState* render_state) {
|
||||
m_triangles = 0;
|
||||
m_draw_calls = 0;
|
||||
|
||||
// if we're rendering from a bucket, we should start off we a totally reset state:
|
||||
reset_state();
|
||||
setup_common_state(render_state);
|
||||
@@ -66,11 +63,32 @@ void DirectRenderer::draw_debug_window() {
|
||||
ImGui::Checkbox("Wireframe", &m_debug_state.wireframe);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("No-texture", &m_debug_state.disable_texture);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("red", &m_debug_state.red);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("always", &m_debug_state.always_draw);
|
||||
|
||||
if (m_mode == Mode::SPRITE_CPU) {
|
||||
ImGui::Checkbox("draw1", &m_sprite_mode.do_first_draw);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("draw2", &m_sprite_mode.do_second_draw);
|
||||
}
|
||||
|
||||
ImGui::Text("Triangles: %d", m_triangles);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Draws: %d", m_draw_calls);
|
||||
}
|
||||
|
||||
float u32_to_float(u32 in) {
|
||||
double x = (double)in / UINT32_MAX;
|
||||
return x;
|
||||
}
|
||||
|
||||
float u32_to_sc(u32 in) {
|
||||
float flt = u32_to_float(in);
|
||||
return (flt - 0.5) * 16.0;
|
||||
}
|
||||
|
||||
void DirectRenderer::flush_pending(SharedRenderState* render_state) {
|
||||
if (m_prim_buffer.vert_count == 0) {
|
||||
return;
|
||||
@@ -92,6 +110,11 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state) {
|
||||
m_test_state_needs_gl_update = false;
|
||||
}
|
||||
|
||||
if (m_texture_state.needs_gl_update) {
|
||||
update_gl_texture(render_state);
|
||||
m_texture_state.needs_gl_update = false;
|
||||
}
|
||||
|
||||
if (m_debug_state.wireframe) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
} else {
|
||||
@@ -103,9 +126,16 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state) {
|
||||
render_state->shaders[ShaderId::DIRECT_BASIC].activate();
|
||||
}
|
||||
|
||||
if (m_debug_state.red) {
|
||||
render_state->shaders[ShaderId::DEBUG_RED].activate();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
// hacks
|
||||
// glEnable(GL_DEPTH_TEST);
|
||||
// glDepthFunc(GL_ALWAYS);
|
||||
if (m_debug_state.always_draw) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
}
|
||||
|
||||
GLuint vao;
|
||||
glGenVertexArrays(1, &vao);
|
||||
@@ -157,10 +187,30 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
// assert(false);
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
|
||||
|
||||
int draw_count = 0;
|
||||
if (m_mode == Mode::SPRITE_CPU) {
|
||||
assert(m_texture_state.tcc);
|
||||
assert(m_prim_gl_state.texture_enable);
|
||||
if (m_sprite_mode.do_first_draw) {
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
|
||||
draw_count++;
|
||||
}
|
||||
if (m_sprite_mode.do_second_draw) {
|
||||
render_state->shaders[ShaderId::SPRITE_CPU_AFAIL].activate();
|
||||
glDepthMask(GL_FALSE);
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
|
||||
glDepthMask(GL_TRUE);
|
||||
draw_count++;
|
||||
}
|
||||
} else {
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
|
||||
draw_count++;
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
m_triangles += m_prim_buffer.vert_count / 3;
|
||||
m_draw_calls++;
|
||||
m_triangles += draw_count * (m_prim_buffer.vert_count / 3);
|
||||
m_draw_calls += draw_count;
|
||||
m_prim_buffer.vert_count = 0;
|
||||
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
@@ -170,7 +220,15 @@ void DirectRenderer::update_gl_prim(SharedRenderState* render_state) {
|
||||
// currently gouraud is handled in setup.
|
||||
const auto& state = m_prim_gl_state;
|
||||
if (state.texture_enable) {
|
||||
render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].activate();
|
||||
if (m_texture_state.tcc) {
|
||||
if (m_mode == Mode::SPRITE_CPU) {
|
||||
render_state->shaders[ShaderId::SPRITE_CPU].activate();
|
||||
} else {
|
||||
render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].activate();
|
||||
}
|
||||
} else {
|
||||
render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED_TCC0].activate();
|
||||
}
|
||||
update_gl_texture(render_state);
|
||||
} else {
|
||||
render_state->shaders[ShaderId::DIRECT_BASIC].activate();
|
||||
@@ -209,9 +267,9 @@ void DirectRenderer::update_gl_texture(SharedRenderState* render_state) {
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex->gpu_texture);
|
||||
// TODO these wrappings are probably wrong.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
// Note: CLAMP and CLAMP_TO_EDGE are different...
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glUniform1i(
|
||||
@@ -227,14 +285,24 @@ void DirectRenderer::update_gl_blend() {
|
||||
glEnable(GL_BLEND);
|
||||
// s, d
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else if (state.a == GsAlpha::BlendMode::SOURCE &&
|
||||
state.b == GsAlpha::BlendMode::ZERO_OR_FIXED &&
|
||||
state.c == GsAlpha::BlendMode::SOURCE && state.d == GsAlpha::BlendMode::DEST) {
|
||||
// (Cs - 0) * As + Cd
|
||||
// Cs * As + (1) * CD
|
||||
glEnable(GL_BLEND);
|
||||
// s, d
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
} else {
|
||||
fmt::print("unsupported blend\n");
|
||||
lg::error("unsupported blend: a {} b {} c {} d {}\n", (int)state.a, (int)state.b, (int)state.c,
|
||||
(int)state.d);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectRenderer::update_gl_test() {
|
||||
const auto& state = m_test_state;
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
if (state.zte) {
|
||||
switch (state.ztst) {
|
||||
case GsTest::ZTest::NEVER:
|
||||
@@ -263,6 +331,12 @@ void DirectRenderer::update_gl_test() {
|
||||
if (state.alpha_test_enable) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (state.depth_writes) {
|
||||
glDepthMask(GL_TRUE);
|
||||
} else {
|
||||
glDepthMask(GL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectRenderer::setup_common_state(SharedRenderState* /*render_state*/) {
|
||||
@@ -345,9 +419,10 @@ void DirectRenderer::render_gif(const u8* data, u32 size, SharedRenderState* ren
|
||||
|
||||
u32 offset = 0;
|
||||
while (!eop) {
|
||||
assert(offset < size);
|
||||
GifTag tag(data + offset);
|
||||
offset += 16;
|
||||
// fmt::print("Tag: {}\n", tag.print());
|
||||
// fmt::print("Tag at offset {}: {}\n", offset, tag.print());
|
||||
|
||||
// unpack registers.
|
||||
// faster to do it once outside of the nloop loop.
|
||||
@@ -377,6 +452,12 @@ void DirectRenderer::render_gif(const u8* data, u32 size, SharedRenderState* ren
|
||||
case GifTag::RegisterDescriptor::XYZF2:
|
||||
handle_xyzf2_packed(data + offset, render_state);
|
||||
break;
|
||||
case GifTag::RegisterDescriptor::PRIM:
|
||||
handle_prim_packed(data + offset, render_state);
|
||||
break;
|
||||
case GifTag::RegisterDescriptor::TEX0_1:
|
||||
handle_tex0_1_packed(data + offset, render_state);
|
||||
break;
|
||||
default:
|
||||
fmt::print("Register {} is not supported in packed mode yet\n",
|
||||
reg_descriptor_name(reg_desc[reg]));
|
||||
@@ -430,7 +511,7 @@ void DirectRenderer::handle_ad(const u8* data, SharedRenderState* render_state)
|
||||
|
||||
switch (addr) {
|
||||
case GsRegisterAddress::ZBUF_1:
|
||||
handle_zbuf1(value);
|
||||
handle_zbuf1(value, render_state);
|
||||
break;
|
||||
case GsRegisterAddress::TEST_1:
|
||||
handle_test1(value, render_state);
|
||||
@@ -466,6 +547,9 @@ void DirectRenderer::handle_ad(const u8* data, SharedRenderState* render_state)
|
||||
case GsRegisterAddress::TEX0_1:
|
||||
handle_tex0_1(value, render_state);
|
||||
break;
|
||||
case GsRegisterAddress::MIPTBP1_1:
|
||||
// TODO this has the address of different mip levels.
|
||||
break;
|
||||
default:
|
||||
fmt::print("Address {} is not supported\n", register_address_name(addr));
|
||||
assert(false);
|
||||
@@ -475,19 +559,26 @@ void DirectRenderer::handle_ad(const u8* data, SharedRenderState* render_state)
|
||||
void DirectRenderer::handle_tex1_1(u64 val) {
|
||||
GsTex1 reg(val);
|
||||
// for now, we aren't going to handle mipmapping. I don't think it's used with direct.
|
||||
assert(reg.mxl() == 0);
|
||||
// assert(reg.mxl() == 0);
|
||||
// if that's true, we can ignore LCM, MTBA, L, K
|
||||
|
||||
// MMAG/MMIN specify texture filtering. For now, assume always linear
|
||||
assert(reg.mmag() == true);
|
||||
assert(reg.mmin() == 1);
|
||||
if (!(reg.mmin() == 1 || reg.mmin() == 4)) { // with mipmap off, both of these are linear
|
||||
// lg::error("unsupported mmin");
|
||||
}
|
||||
|
||||
// fmt::print("{}\n", reg.print());
|
||||
//
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_tex0_1_packed(const u8* data, SharedRenderState* render_state) {
|
||||
u64 val;
|
||||
memcpy(&val, data, sizeof(u64));
|
||||
handle_tex0_1(val, render_state);
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_tex0_1(u64 val, SharedRenderState* render_state) {
|
||||
GsTex0 reg(val);
|
||||
// fmt::print("{}\n", reg.print());
|
||||
|
||||
// update tbp
|
||||
if (m_texture_state.current_register != reg) {
|
||||
@@ -496,6 +587,10 @@ void DirectRenderer::handle_tex0_1(u64 val, SharedRenderState* render_state) {
|
||||
m_texture_state.using_mt4hh = reg.psm() == GsTex0::PSM::PSMT4HH;
|
||||
m_prim_gl_state_needs_gl_update = true;
|
||||
m_texture_state.current_register = reg;
|
||||
if (m_texture_state.tcc != reg.tcc()) {
|
||||
m_texture_state.needs_gl_update = true;
|
||||
}
|
||||
m_texture_state.tcc = reg.tcc();
|
||||
}
|
||||
|
||||
// tbw: assume they got it right
|
||||
@@ -503,8 +598,6 @@ void DirectRenderer::handle_tex0_1(u64 val, SharedRenderState* render_state) {
|
||||
// tw: assume they got it right
|
||||
// th: assume they got it right
|
||||
|
||||
// these mean that the texture is multiplied, and uses the alpha from the clut.
|
||||
assert(reg.tcc() == 1);
|
||||
assert(reg.tfx() == GsTex0::TextureFunction::MODULATE);
|
||||
|
||||
// cbp: assume they got it right
|
||||
@@ -518,7 +611,7 @@ void DirectRenderer::handle_texa(u64 val) {
|
||||
// rgba16 isn't used so this doesn't matter?
|
||||
// but they use sane defaults anyway
|
||||
assert(reg.ta0() == 0);
|
||||
assert(reg.ta1() == 0x80);
|
||||
assert(reg.ta1() == 0x80); // note: check rgba16_to_rgba32 if this changes.
|
||||
|
||||
assert(reg.aem() == false);
|
||||
}
|
||||
@@ -537,11 +630,6 @@ void DirectRenderer::handle_rgbaq_packed(const u8* data) {
|
||||
m_prim_building.rgba_reg[3] = data[12];
|
||||
}
|
||||
|
||||
float u32_to_float(u32 in) {
|
||||
double x = (double)in / UINT32_MAX;
|
||||
return x * 2 - 1;
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_xyzf2_packed(const u8* data, SharedRenderState* render_state) {
|
||||
u32 x, y;
|
||||
memcpy(&x, data, 4);
|
||||
@@ -550,6 +638,7 @@ void DirectRenderer::handle_xyzf2_packed(const u8* data, SharedRenderState* rend
|
||||
u64 upper;
|
||||
memcpy(&upper, data + 8, 8);
|
||||
u32 z = (upper >> 4) & 0xffffff;
|
||||
|
||||
u8 f = (upper >> 36);
|
||||
bool adc = upper & (1ull << 47);
|
||||
assert(!adc);
|
||||
@@ -557,21 +646,28 @@ void DirectRenderer::handle_xyzf2_packed(const u8* data, SharedRenderState* rend
|
||||
handle_xyzf2_common(x, y, z, f, render_state);
|
||||
}
|
||||
|
||||
void debug_print_vtx(const math::Vector<u32, 3>& vtx) {
|
||||
fmt::print("{} {}\n", u32_to_float(vtx.x()), u32_to_float(vtx.y()));
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_zbuf1(u64 val) {
|
||||
void DirectRenderer::handle_zbuf1(u64 val, SharedRenderState* render_state) {
|
||||
// note: we can basically ignore this. There's a single z buffer that's always configured the same
|
||||
// way - 24-bit, at offset 448.
|
||||
GsZbuf x(val);
|
||||
assert(x.zmsk()); // note: not sure if this ever changes or not.
|
||||
assert(x.psm() == TextureFormat::PSMZ24);
|
||||
assert(x.zbp() == 448);
|
||||
|
||||
bool write = !x.zmsk();
|
||||
// assert(write);
|
||||
|
||||
if (write != m_test_state.depth_writes) {
|
||||
flush_pending(render_state);
|
||||
m_test_state_needs_gl_update = true;
|
||||
m_test_state.depth_writes = !write;
|
||||
}
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_test1(u64 val, SharedRenderState* render_state) {
|
||||
GsTest reg(val);
|
||||
assert(!reg.alpha_test_enable());
|
||||
assert(!reg.date());
|
||||
assert(!(val & 1));
|
||||
if (m_test_state.current_register != reg) {
|
||||
flush_pending(render_state);
|
||||
m_test_state.from_register(reg);
|
||||
@@ -596,6 +692,12 @@ void DirectRenderer::handle_clamp1(u64 val) {
|
||||
assert(val == 0b101); // clamp s and t.
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_prim_packed(const u8* data, SharedRenderState* render_state) {
|
||||
u64 val;
|
||||
memcpy(&val, data, sizeof(u64));
|
||||
handle_prim(val, render_state);
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_prim(u64 val, SharedRenderState* render_state) {
|
||||
if (m_prim_building.tri_strip_startup) {
|
||||
m_prim_building.tri_strip_startup = 0;
|
||||
@@ -629,15 +731,16 @@ void DirectRenderer::handle_xyzf2_common(u32 x,
|
||||
u32 z,
|
||||
u8 f,
|
||||
SharedRenderState* render_state) {
|
||||
assert(z < (1 << 24));
|
||||
(void)f; // TODO: do something with this.
|
||||
if (m_prim_buffer.is_full()) {
|
||||
flush_pending(render_state);
|
||||
}
|
||||
// assert(f == 0);
|
||||
|
||||
// assert(f == 0);
|
||||
m_prim_building.building_st.at(m_prim_building.building_idx) = m_prim_building.st_reg;
|
||||
m_prim_building.building_rgba.at(m_prim_building.building_idx) = m_prim_building.rgba_reg;
|
||||
m_prim_building.building_vert.at(m_prim_building.building_idx) = {x << 16, y << 16, z};
|
||||
m_prim_building.building_vert.at(m_prim_building.building_idx) = {x << 16, y << 16, z << 8};
|
||||
m_prim_building.building_idx++;
|
||||
|
||||
switch (m_prim_building.kind) {
|
||||
@@ -694,6 +797,22 @@ void DirectRenderer::handle_xyzf2_common(u32 x,
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GsPrim::Kind::TRI_FAN: {
|
||||
if (m_prim_building.tri_strip_startup < 2) {
|
||||
m_prim_building.tri_strip_startup++;
|
||||
} else {
|
||||
if (m_prim_building.building_idx == 2) {
|
||||
// nothing.
|
||||
} else if (m_prim_building.building_idx == 3) {
|
||||
m_prim_building.building_idx = 1;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
m_prim_buffer.push(m_prim_building.building_rgba[i], m_prim_building.building_vert[i],
|
||||
m_prim_building.building_st[i]);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case GsPrim::Kind::LINE: {
|
||||
if (m_prim_building.building_idx == 2) {
|
||||
math::Vector<double, 3> pt0 = m_prim_building.building_vert[0].cast<double>();
|
||||
@@ -729,11 +848,9 @@ void DirectRenderer::handle_xyzf2_common(u32 x,
|
||||
}
|
||||
|
||||
void DirectRenderer::handle_xyzf2(u64 val, SharedRenderState* render_state) {
|
||||
// m_prim_buffer.rgba_u8[m_prim_buffer.vert_count] = m_prim_building.rgba;
|
||||
|
||||
u32 x = val & 0xffff;
|
||||
u32 y = (val >> 16) & 0xffff;
|
||||
u32 z = (val >> 32) & 0xfffff;
|
||||
u32 z = (val >> 32) & 0xffffff;
|
||||
u32 f = (val >> 56) & 0xff;
|
||||
|
||||
handle_xyzf2_common(x, y, z, f, render_state);
|
||||
@@ -752,6 +869,9 @@ void DirectRenderer::reset_state() {
|
||||
m_texture_state = TextureState();
|
||||
|
||||
m_prim_building = PrimBuildState();
|
||||
|
||||
m_triangles = 0;
|
||||
m_draw_calls = 0;
|
||||
}
|
||||
|
||||
void DirectRenderer::TestState::from_register(GsTest reg) {
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
*/
|
||||
class DirectRenderer : public BucketRenderer {
|
||||
public:
|
||||
DirectRenderer(const std::string& name, BucketId my_id, int batch_size);
|
||||
// specializations of direct renderer to handle certain outputs.
|
||||
enum class Mode {
|
||||
NORMAL, // use for general debug drawing, font.
|
||||
SPRITE_CPU // use for sprites (does the appropriate alpha test)
|
||||
};
|
||||
DirectRenderer(const std::string& name, BucketId my_id, int batch_size, Mode mode);
|
||||
~DirectRenderer();
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state) override;
|
||||
|
||||
@@ -46,19 +51,23 @@ class DirectRenderer : public BucketRenderer {
|
||||
*/
|
||||
void flush_pending(SharedRenderState* render_state);
|
||||
|
||||
void draw_debug_window() override;
|
||||
|
||||
private:
|
||||
void handle_ad(const u8* data, SharedRenderState* render_state);
|
||||
void handle_zbuf1(u64 val);
|
||||
void handle_zbuf1(u64 val, SharedRenderState* render_state);
|
||||
void handle_test1(u64 val, SharedRenderState* render_state);
|
||||
void handle_alpha1(u64 val, SharedRenderState* render_state);
|
||||
void handle_pabe(u64 val);
|
||||
void handle_clamp1(u64 val);
|
||||
void handle_prim(u64 val, SharedRenderState* render_state);
|
||||
void handle_prim_packed(const u8* data, SharedRenderState* render_state);
|
||||
void handle_rgbaq(u64 val);
|
||||
void handle_xyzf2(u64 val, SharedRenderState* render_state);
|
||||
void handle_st_packed(const u8* data);
|
||||
void handle_rgbaq_packed(const u8* data);
|
||||
void handle_xyzf2_packed(const u8* data, SharedRenderState* render_state);
|
||||
void handle_tex0_1_packed(const u8* data, SharedRenderState* render_state);
|
||||
void handle_tex0_1(u64 val, SharedRenderState* render_state);
|
||||
void handle_tex1_1(u64 val);
|
||||
void handle_texa(u64 val);
|
||||
@@ -70,8 +79,6 @@ class DirectRenderer : public BucketRenderer {
|
||||
void update_gl_test();
|
||||
void update_gl_texture(SharedRenderState* render_state);
|
||||
|
||||
void draw_debug_window() override;
|
||||
|
||||
struct TestState {
|
||||
void from_register(GsTest reg);
|
||||
|
||||
@@ -86,6 +93,8 @@ class DirectRenderer : public BucketRenderer {
|
||||
bool zte = true;
|
||||
GsTest::ZTest ztst = GsTest::ZTest::GEQUAL;
|
||||
|
||||
bool depth_writes = true;
|
||||
|
||||
} m_test_state;
|
||||
|
||||
struct BlendState {
|
||||
@@ -120,6 +129,8 @@ class DirectRenderer : public BucketRenderer {
|
||||
GsTex0 current_register;
|
||||
u32 texture_base_ptr = 0;
|
||||
bool using_mt4hh = false;
|
||||
bool tcc = false;
|
||||
bool needs_gl_update = true;
|
||||
} m_texture_state;
|
||||
|
||||
// state set through the prim/rgbaq register that doesn't require changing GL stuff
|
||||
@@ -163,6 +174,8 @@ class DirectRenderer : public BucketRenderer {
|
||||
struct {
|
||||
bool disable_texture = false;
|
||||
bool wireframe = false;
|
||||
bool red = false;
|
||||
bool always_draw = false;
|
||||
} m_debug_state;
|
||||
|
||||
int m_triangles = 0;
|
||||
@@ -171,4 +184,11 @@ class DirectRenderer : public BucketRenderer {
|
||||
bool m_prim_gl_state_needs_gl_update = true;
|
||||
bool m_test_state_needs_gl_update = true;
|
||||
bool m_blend_state_needs_gl_update = true;
|
||||
|
||||
struct SpriteMode {
|
||||
bool do_first_draw = true;
|
||||
bool do_second_draw = true;
|
||||
} m_sprite_mode;
|
||||
|
||||
Mode m_mode;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "common/log/log.h"
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "game/graphics/opengl_renderer/DirectRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/SpriteRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/TextureUploadHandler.h"
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
// for the vif callback
|
||||
@@ -51,13 +53,18 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool)
|
||||
* Construct bucket renderers. We can specify different renderers for different buckets
|
||||
*/
|
||||
void OpenGLRenderer::init_bucket_renderers() {
|
||||
// For example, set up bucket 0:
|
||||
init_bucket_renderer<EmptyBucketRenderer>("bucket0", BucketId::BUCKET0);
|
||||
|
||||
// TODO what the heck is drawing to debug-draw-0 on init?
|
||||
init_bucket_renderer<DirectRenderer>("sprite", BucketId::SPRITE, 102);
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-0", BucketId::DEBUG_DRAW_0, 102);
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-1", BucketId::DEBUG_DRAW_1, 102);
|
||||
init_bucket_renderer<TextureUploadHandler>("tfrag-tex-0", BucketId::TFRAG_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("shrub-tex-0", BucketId::SHRUB_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("alpha-tex-0", BucketId::ALPHA_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("pris-tex-0", BucketId::PRIS_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("water-tex-0", BucketId::WATER_TEX_LEVEL0);
|
||||
init_bucket_renderer<TextureUploadHandler>("pre-sprite-tex", BucketId::PRE_SPRITE_TEX);
|
||||
init_bucket_renderer<SpriteRenderer>("sprite", BucketId::SPRITE);
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-0", BucketId::DEBUG_DRAW_0, 102,
|
||||
DirectRenderer::Mode::NORMAL);
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-1", BucketId::DEBUG_DRAW_1, 102,
|
||||
DirectRenderer::Mode::NORMAL);
|
||||
|
||||
// for now, for any unset renderers, just set them to an EmptyBucketRenderer.
|
||||
for (size_t i = 0; i < m_bucket_renderers.size(); i++) {
|
||||
@@ -70,15 +77,34 @@ void OpenGLRenderer::init_bucket_renderers() {
|
||||
/*!
|
||||
* Main render function. This is called from the gfx loop with the chain passed from the game.
|
||||
*/
|
||||
void OpenGLRenderer::render(DmaFollower dma, int window_width_px, int window_height_px) {
|
||||
void OpenGLRenderer::render(DmaFollower dma,
|
||||
int window_width_px,
|
||||
int window_height_px,
|
||||
bool draw_debug_window,
|
||||
bool dump_playback) {
|
||||
m_render_state.dump_playback = dump_playback;
|
||||
m_render_state.ee_main_memory = dump_playback ? nullptr : g_ee_main_mem;
|
||||
m_render_state.offset_of_s7 = offset_of_s7();
|
||||
setup_frame(window_width_px, window_height_px);
|
||||
m_render_state.texture_pool->remove_garbage_textures();
|
||||
// draw_test_triangle();
|
||||
// render the buckets!
|
||||
dispatch_buckets(dma);
|
||||
|
||||
draw_renderer_selection_window();
|
||||
// add a profile bar for the imgui stuff
|
||||
vif_interrupt_callback();
|
||||
if (draw_debug_window) {
|
||||
draw_renderer_selection_window();
|
||||
// add a profile bar for the imgui stuff
|
||||
if (!m_render_state.dump_playback) {
|
||||
vif_interrupt_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLRenderer::serialize(Serializer& ser) {
|
||||
m_render_state.texture_pool->serialize(ser);
|
||||
for (auto& renderer : m_bucket_renderers) {
|
||||
renderer->serialize(ser);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLRenderer::draw_renderer_selection_window() {
|
||||
@@ -87,15 +113,17 @@ void OpenGLRenderer::draw_renderer_selection_window() {
|
||||
auto renderer = m_bucket_renderers[i].get();
|
||||
if (renderer && !renderer->empty()) {
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::CollapsingHeader(renderer->name_and_id().c_str())) {
|
||||
if (ImGui::TreeNode(renderer->name_and_id().c_str())) {
|
||||
ImGui::Checkbox("Enable", &renderer->enabled());
|
||||
renderer->draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Texture Pool")) {
|
||||
if (ImGui::TreeNode("Texture Pool")) {
|
||||
m_render_state.texture_pool->draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -105,8 +133,10 @@ void OpenGLRenderer::draw_renderer_selection_window() {
|
||||
*/
|
||||
void OpenGLRenderer::setup_frame(int window_width_px, int window_height_px) {
|
||||
glViewport(0, 0, window_width_px, window_height_px);
|
||||
glClearColor(0.5, 0.5, 0.5, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClearColor(0.5, 0.5, 0.5, 1.0);
|
||||
glClearDepth(0.0);
|
||||
glDepthMask(GL_TRUE);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
@@ -144,12 +174,14 @@ void OpenGLRenderer::dispatch_buckets(DmaFollower dma) {
|
||||
// loop over the buckets!
|
||||
for (int bucket_id = 0; bucket_id < (int)BucketId::MAX_BUCKETS; bucket_id++) {
|
||||
auto& renderer = m_bucket_renderers[bucket_id];
|
||||
// fmt::print("render bucket {} with {}\n", bucket_id, renderer->name_and_id());
|
||||
renderer->render(dma, &m_render_state);
|
||||
// should have ended at the start of the next chain
|
||||
assert(dma.current_tag_offset() == m_render_state.next_bucket);
|
||||
m_render_state.next_bucket += 16;
|
||||
vif_interrupt_callback();
|
||||
|
||||
if (!m_render_state.dump_playback) {
|
||||
vif_interrupt_callback();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO ending data.
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
class OpenGLRenderer {
|
||||
public:
|
||||
OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool);
|
||||
void render(DmaFollower dma, int window_width_px, int window_height_px);
|
||||
void render(DmaFollower dma,
|
||||
int window_width_px,
|
||||
int window_height_px,
|
||||
bool draw_debug_window,
|
||||
bool dump_playback);
|
||||
void serialize(Serializer& ser);
|
||||
|
||||
private:
|
||||
void setup_frame(int window_width_px, int window_height_px);
|
||||
@@ -18,7 +23,6 @@ class OpenGLRenderer {
|
||||
void dispatch_buckets(DmaFollower dma);
|
||||
void init_bucket_renderers();
|
||||
void draw_renderer_selection_window();
|
||||
void draw_texture_window();
|
||||
|
||||
template <typename T, class... Args>
|
||||
void init_bucket_renderer(const std::string& name, BucketId id, Args&&... args) {
|
||||
|
||||
@@ -66,5 +66,9 @@ void Shader::activate() {
|
||||
ShaderLibrary::ShaderLibrary() {
|
||||
at(ShaderId::TEST_SHADER) = {"test_shader"};
|
||||
at(ShaderId::DIRECT_BASIC) = {"direct_basic"};
|
||||
at(ShaderId::DIRECT_BASIC_TEXTURED_TCC0) = {"direct_basic_textured_tcc0"};
|
||||
at(ShaderId::DIRECT_BASIC_TEXTURED) = {"direct_basic_textured"};
|
||||
at(ShaderId::DEBUG_RED) = {"debug_red"};
|
||||
at(ShaderId::SPRITE_CPU) = {"sprite_cpu"};
|
||||
at(ShaderId::SPRITE_CPU_AFAIL) = {"sprite_cpu_afail"};
|
||||
}
|
||||
@@ -21,7 +21,16 @@ class Shader {
|
||||
};
|
||||
|
||||
// note: update the constructor in Shader.cpp
|
||||
enum class ShaderId { TEST_SHADER = 0, DIRECT_BASIC = 1, DIRECT_BASIC_TEXTURED = 2, MAX_SHADERS };
|
||||
enum class ShaderId {
|
||||
TEST_SHADER = 0,
|
||||
DIRECT_BASIC = 1,
|
||||
DIRECT_BASIC_TEXTURED = 2,
|
||||
DIRECT_BASIC_TEXTURED_TCC0 = 3,
|
||||
DEBUG_RED = 4,
|
||||
SPRITE_CPU = 5,
|
||||
SPRITE_CPU_AFAIL = 6,
|
||||
MAX_SHADERS
|
||||
};
|
||||
|
||||
class ShaderLibrary {
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,769 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/imgui/imgui.h"
|
||||
#include "SpriteRenderer.h"
|
||||
|
||||
namespace {
|
||||
/*!
|
||||
* Make sure that the DMA Transfer is a VIF unpack (copy data to VIF memory) with the given
|
||||
* setup. This is for a transfer with STCYCL followed by UNPACK.
|
||||
*/
|
||||
bool verify_unpack_with_stcycl(const DmaTransfer& transfer,
|
||||
VifCode::Kind unpack_kind,
|
||||
u16 cl,
|
||||
u16 wl,
|
||||
u32 qwc,
|
||||
u32 addr,
|
||||
bool usn,
|
||||
bool flg) {
|
||||
if (transfer.size_bytes != qwc * 16) {
|
||||
fmt::print("verify_unpack: bad size {} vs {}\n", transfer.size_bytes, qwc * 16);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transfer.vifcode0().kind != VifCode::Kind::STCYCL) {
|
||||
fmt::print("verify_unpack: bad vifcode 0\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transfer.vifcode1().kind != unpack_kind) {
|
||||
fmt::print("verify_unpack: bad vifcode 1\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
VifCodeStcycl stcycl(transfer.vifcode0());
|
||||
VifCodeUnpack unpack(transfer.vifcode1());
|
||||
|
||||
if (stcycl.cl != cl || stcycl.wl != wl) {
|
||||
fmt::print("verify_unpack: bad cl/wl {}/{} vs {}/{}\n", stcycl.cl, stcycl.wl, cl, wl);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unpack.addr_qw != addr || unpack.use_tops_flag != flg || unpack.is_unsigned != usn) {
|
||||
fmt::print("verify_unpack: bad unpack {}/{}/{} vs {}/{}/{}", unpack.addr_qw,
|
||||
unpack.use_tops_flag, unpack.is_unsigned, addr, flg, usn);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transfer.vifcode1().num != qwc) {
|
||||
fmt::print("verify_unpack: bad num {} vs {}\n", transfer.vifcode1().num, qwc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Make sure that the DMA transfer is a VIF unpack with the given setup.
|
||||
* This is for when there's just an UNPACK.
|
||||
*/
|
||||
bool verify_unpack_no_stcycl(const DmaTransfer& transfer,
|
||||
VifCode::Kind unpack_kind,
|
||||
u32 qwc,
|
||||
u32 addr,
|
||||
bool usn,
|
||||
bool flg) {
|
||||
if (transfer.size_bytes != qwc * 16) {
|
||||
fmt::print("verify_unpack: bad size {} vs {}\n", transfer.size_bytes, qwc * 16);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transfer.vifcode0().kind != VifCode::Kind::NOP) {
|
||||
fmt::print("verify_unpack: bad vifcode 0\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transfer.vifcode1().kind != unpack_kind) {
|
||||
fmt::print("verify_unpack: bad vifcode 1\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
VifCodeUnpack unpack(transfer.vifcode1());
|
||||
|
||||
if (unpack.addr_qw != addr || unpack.use_tops_flag != flg || unpack.is_unsigned != usn) {
|
||||
fmt::print("verify_unpack: bad unpack {}/{}/{} vs {}/{}/{}", unpack.addr_qw,
|
||||
unpack.use_tops_flag, unpack.is_unsigned, addr, flg, usn);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transfer.vifcode1().num != qwc) {
|
||||
fmt::print("verify_unpack: bad num {} vs {}\n", transfer.vifcode1().num, qwc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Verify the DMA transfer is a VIF unpack (with no STCYCL tag).
|
||||
* Then, unpack the data to dst.
|
||||
*/
|
||||
void unpack_to_no_stcycl(void* dst,
|
||||
const DmaTransfer& transfer,
|
||||
VifCode::Kind unpack_kind,
|
||||
u32 size_bytes,
|
||||
u32 addr,
|
||||
bool usn,
|
||||
bool flg) {
|
||||
bool ok = verify_unpack_no_stcycl(transfer, unpack_kind, size_bytes / 16, addr, usn, flg);
|
||||
assert(ok);
|
||||
assert((size_bytes & 0xf) == 0);
|
||||
memcpy(dst, transfer.data, size_bytes);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Does the next DMA transfer look like it could be the start of a 2D group?
|
||||
*/
|
||||
bool looks_like_2d_chunk_start(const DmaFollower& dma) {
|
||||
return dma.current_tag().qwc == 1 && dma.current_tag().kind == DmaTag::Kind::CNT;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Read the header. Asserts if it's bad.
|
||||
* Returns the number of sprites.
|
||||
* Advances 1 dma transfer
|
||||
*/
|
||||
u32 process_sprite_chunk_header(DmaFollower& dma) {
|
||||
auto transfer = dma.read_and_advance();
|
||||
// note that flg = true, this should use double buffering
|
||||
bool ok = verify_unpack_with_stcycl(transfer, VifCode::Kind::UNPACK_V4_32, 4, 4, 1,
|
||||
SpriteDataMem::Header, false, true);
|
||||
assert(ok);
|
||||
u32 header[4];
|
||||
memcpy(header, transfer.data, 16);
|
||||
assert(header[0] <= SpriteRenderer::SPRITES_PER_CHUNK);
|
||||
return header[0];
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SpriteRenderer::SpriteRenderer(const std::string& name, BucketId my_id)
|
||||
: BucketRenderer(name, my_id),
|
||||
m_sprite_renderer(fmt::format("{}.sprites", name),
|
||||
my_id,
|
||||
100,
|
||||
DirectRenderer::Mode::SPRITE_CPU),
|
||||
m_direct_renderer(fmt::format("{}.direct", name), my_id, 100, DirectRenderer::Mode::NORMAL) {}
|
||||
|
||||
/*!
|
||||
* Run the sprite distorter. Currently nothing uses sprite-distorter so this just skips through
|
||||
* the table upload stuff that runs every frame, even if there are no sprites.
|
||||
*/
|
||||
void SpriteRenderer::render_distorter(DmaFollower& dma, SharedRenderState* render_state) {
|
||||
// Next thing should be the sprite-distorter setup
|
||||
m_direct_renderer.reset_state();
|
||||
while (dma.current_tag().qwc != 7) {
|
||||
auto direct_data = dma.read_and_advance();
|
||||
m_direct_renderer.render_vif(direct_data.vif0(), direct_data.vif1(), direct_data.data,
|
||||
direct_data.size_bytes, render_state);
|
||||
}
|
||||
m_direct_renderer.flush_pending(render_state);
|
||||
auto sprite_distorter_direct_setup = dma.read_and_advance();
|
||||
assert(sprite_distorter_direct_setup.vifcode0().kind == VifCode::Kind::NOP);
|
||||
assert(sprite_distorter_direct_setup.vifcode1().kind == VifCode::Kind::DIRECT);
|
||||
assert(sprite_distorter_direct_setup.vifcode1().immediate == 7);
|
||||
memcpy(m_sprite_distorter_setup, sprite_distorter_direct_setup.data, 7 * 16);
|
||||
|
||||
// Next thing should be the sprite-distorter tables
|
||||
auto sprite_distorter_tables = dma.read_and_advance();
|
||||
assert(sprite_distorter_tables.size_bytes == 0x8b * 16);
|
||||
assert(sprite_distorter_tables.vifcode0().kind == VifCode::Kind::STCYCL);
|
||||
VifCodeStcycl distorter_table_transfer(sprite_distorter_tables.vifcode0());
|
||||
assert(distorter_table_transfer.cl == 4);
|
||||
assert(distorter_table_transfer.wl == 4);
|
||||
// TODO: check unpack cmd (vif1)
|
||||
|
||||
// TODO: do something with the table
|
||||
|
||||
// next would be the program, but we don't have it.
|
||||
|
||||
// TODO: next is the sprite-distorter (currently not used)
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handle DMA data that does the per-frame setup.
|
||||
* This should get the dma chain immediately after the call to sprite-draw-distorters.
|
||||
* It ends right before the sprite-add-matrix-data for the 3d's
|
||||
*/
|
||||
void SpriteRenderer::handle_sprite_frame_setup(DmaFollower& dma) {
|
||||
// first is some direct data
|
||||
auto direct_data = dma.read_and_advance();
|
||||
assert(direct_data.size_bytes == 3 * 16);
|
||||
memcpy(m_sprite_direct_setup, direct_data.data, 3 * 16);
|
||||
|
||||
// next would be the program, but it's 0 size on the PC and isn't sent.
|
||||
|
||||
// next is the "frame data"
|
||||
auto frame_data = dma.read_and_advance();
|
||||
assert(frame_data.size_bytes == (int)sizeof(SpriteFrameData)); // very cool
|
||||
assert(frame_data.vifcode0().kind == VifCode::Kind::STCYCL);
|
||||
VifCodeStcycl frame_data_stcycl(frame_data.vifcode0());
|
||||
assert(frame_data_stcycl.cl == 4);
|
||||
assert(frame_data_stcycl.wl == 4);
|
||||
assert(frame_data.vifcode1().kind == VifCode::Kind::UNPACK_V4_32);
|
||||
VifCodeUnpack frame_data_unpack(frame_data.vifcode1());
|
||||
assert(frame_data_unpack.addr_qw == SpriteDataMem::FrameData);
|
||||
assert(frame_data_unpack.use_tops_flag == false);
|
||||
memcpy(&m_frame_data, frame_data.data, sizeof(SpriteFrameData));
|
||||
|
||||
// next, a MSCALF.
|
||||
auto mscalf = dma.read_and_advance();
|
||||
assert(mscalf.size_bytes == 0);
|
||||
assert(mscalf.vifcode0().kind == VifCode::Kind::MSCALF);
|
||||
assert(mscalf.vifcode0().immediate == SpriteProgMem::Init);
|
||||
assert(mscalf.vifcode1().kind == VifCode::Kind::FLUSHE);
|
||||
|
||||
// next base and offset
|
||||
auto base_offset = dma.read_and_advance();
|
||||
assert(base_offset.size_bytes == 0);
|
||||
assert(base_offset.vifcode0().kind == VifCode::Kind::BASE);
|
||||
assert(base_offset.vifcode0().immediate == SpriteDataMem::Buffer0);
|
||||
assert(base_offset.vifcode1().kind == VifCode::Kind::OFFSET);
|
||||
assert(base_offset.vifcode1().immediate == SpriteDataMem::Buffer1);
|
||||
}
|
||||
|
||||
void SpriteRenderer::render_3d(DmaFollower& dma) {
|
||||
// one time matrix data
|
||||
auto matrix_data = dma.read_and_advance();
|
||||
assert(matrix_data.size_bytes == sizeof(Sprite3DMatrixData));
|
||||
|
||||
bool unpack_ok = verify_unpack_with_stcycl(matrix_data, VifCode::Kind::UNPACK_V4_32, 4, 4, 5,
|
||||
SpriteDataMem::Matrix, false, false);
|
||||
assert(unpack_ok);
|
||||
static_assert(sizeof(m_3d_matrix_data) == 5 * 16);
|
||||
memcpy(&m_3d_matrix_data, matrix_data.data, sizeof(m_3d_matrix_data));
|
||||
// TODO
|
||||
}
|
||||
|
||||
void SpriteRenderer::render_2d_group0(DmaFollower& dma) {
|
||||
(void)dma;
|
||||
// TODO
|
||||
}
|
||||
|
||||
void SpriteRenderer::render_fake_shadow(DmaFollower& dma) {
|
||||
// TODO
|
||||
// nop + flushe
|
||||
auto nop_flushe = dma.read_and_advance();
|
||||
assert(nop_flushe.vifcode0().kind == VifCode::Kind::NOP);
|
||||
assert(nop_flushe.vifcode1().kind == VifCode::Kind::FLUSHE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handle DMA data for group1 2d's (HUD)
|
||||
*/
|
||||
void SpriteRenderer::render_2d_group1(DmaFollower& dma, SharedRenderState* render_state) {
|
||||
// one time matrix data upload
|
||||
auto mat_upload = dma.read_and_advance();
|
||||
bool mat_ok = verify_unpack_with_stcycl(mat_upload, VifCode::Kind::UNPACK_V4_32, 4, 4, 80,
|
||||
SpriteDataMem::Matrix, false, false);
|
||||
assert(mat_ok);
|
||||
assert(mat_upload.size_bytes == sizeof(m_hud_matrix_data));
|
||||
memcpy(&m_hud_matrix_data, mat_upload.data, sizeof(m_hud_matrix_data));
|
||||
|
||||
// loop through chunks.
|
||||
while (looks_like_2d_chunk_start(dma)) {
|
||||
m_debug_stats.blocks_2d_grp1++;
|
||||
// 4 packets per chunk
|
||||
|
||||
// first is the header
|
||||
u32 sprite_count = process_sprite_chunk_header(dma);
|
||||
m_debug_stats.count_2d_grp1 += sprite_count;
|
||||
|
||||
// second is the vector data
|
||||
u32 expected_vec_size = sizeof(SpriteVecData2d) * sprite_count;
|
||||
auto vec_data = dma.read_and_advance();
|
||||
assert(expected_vec_size <= sizeof(m_vec_data_2d));
|
||||
unpack_to_no_stcycl(&m_vec_data_2d, vec_data, VifCode::Kind::UNPACK_V4_32, expected_vec_size,
|
||||
SpriteDataMem::Vector, false, true);
|
||||
|
||||
// third is the adgif data
|
||||
u32 expected_adgif_size = sizeof(AdGif) * sprite_count;
|
||||
auto adgif_data = dma.read_and_advance();
|
||||
assert(expected_adgif_size <= sizeof(m_adgif));
|
||||
unpack_to_no_stcycl(&m_adgif, adgif_data, VifCode::Kind::UNPACK_V4_32, expected_adgif_size,
|
||||
SpriteDataMem::Adgif, false, true);
|
||||
|
||||
// fourth is the actual run!!!!!
|
||||
auto run = dma.read_and_advance();
|
||||
assert(run.vifcode0().kind == VifCode::Kind::NOP);
|
||||
assert(run.vifcode1().kind == VifCode::Kind::MSCAL);
|
||||
assert(run.vifcode1().immediate == SpriteProgMem::Sprites2dHud);
|
||||
if (m_enabled) {
|
||||
do_2d_group1_block_cpu(sprite_count, render_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteRenderer::render(DmaFollower& dma, SharedRenderState* render_state) {
|
||||
m_debug_stats = {};
|
||||
// First thing should be a NEXT with two nops. this is a jump from buckets to sprite data
|
||||
auto data0 = dma.read_and_advance();
|
||||
assert(data0.vif1() == 0);
|
||||
assert(data0.vif0() == 0);
|
||||
assert(data0.size_bytes == 0);
|
||||
|
||||
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
|
||||
// sprite renderer didn't run, let's just get out of here.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
dma.read_and_advance();
|
||||
}
|
||||
assert(dma.current_tag_offset() == render_state->next_bucket);
|
||||
return;
|
||||
}
|
||||
|
||||
// First is the distorter
|
||||
render_distorter(dma, render_state);
|
||||
|
||||
// next, sprite frame setup.
|
||||
handle_sprite_frame_setup(dma);
|
||||
|
||||
// 3d sprites
|
||||
render_3d(dma);
|
||||
|
||||
// 2d draw
|
||||
render_2d_group0(dma);
|
||||
|
||||
// shadow draw
|
||||
render_fake_shadow(dma);
|
||||
|
||||
// 2d draw (HUD)
|
||||
m_sprite_renderer.reset_state();
|
||||
render_2d_group1(dma, render_state);
|
||||
m_sprite_renderer.flush_pending(render_state);
|
||||
|
||||
// TODO finish this up.
|
||||
// fmt::print("next bucket is 0x{}\n", render_state->next_bucket);
|
||||
while (dma.current_tag_offset() != render_state->next_bucket) {
|
||||
// auto tag = dma.current_tag();
|
||||
// fmt::print("@ 0x{:x} tag: {}", dma.current_tag_offset(), tag.print());
|
||||
auto data = dma.read_and_advance();
|
||||
VifCode code(data.vif0());
|
||||
// fmt::print(" vif: {}\n", code.print());
|
||||
if (code.kind == VifCode::Kind::NOP) {
|
||||
// fmt::print(" vif: {}\n", VifCode(data.vif1()).print());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteRenderer::draw_debug_window() {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("2D Group 1 (HUD) blocks: %d sprites: %d", m_debug_stats.blocks_2d_grp1,
|
||||
m_debug_stats.count_2d_grp1);
|
||||
ImGui::Checkbox("Extra Debug", &m_extra_debug);
|
||||
if (ImGui::TreeNode("direct")) {
|
||||
m_sprite_renderer.draw_debug_window();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Render (for real)
|
||||
|
||||
namespace {
|
||||
Vector4f matrix_transform(const Matrix4f& mat, const Vector4f& pt) {
|
||||
// mulaw.xyzw ACC, vf28, vf00
|
||||
// maddax.xyzw ACC, vf25, vf01
|
||||
// madday.xyzw ACC, vf26, vf01
|
||||
// maddz.xyzw vf02, vf27, vf01
|
||||
return mat.col(3) + (mat.col(0) * pt[0]) + (mat.col(1) * pt[1]) + (mat.col(2) * pt[2]);
|
||||
}
|
||||
|
||||
bool clip_xyz_plus_minus(const Vector4f& pt) {
|
||||
float pw = std::abs(pt.w());
|
||||
float mw = -pw;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (pt[i] > pw) {
|
||||
return true;
|
||||
}
|
||||
if (pt[i] < mw) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void imgui_vec(const Vector4f& vec, const char* name = nullptr, int indent = 0) {
|
||||
std::string spacing(indent, ' ');
|
||||
if (name) {
|
||||
ImGui::Text("%s%s: %f, %f, %f, %f", spacing.c_str(), name, vec.x(), vec.y(), vec.z(), vec.w());
|
||||
} else {
|
||||
ImGui::Text("%s%f, %f, %f, %f", spacing.c_str(), vec.x(), vec.y(), vec.z(), vec.w());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Render the sprites!
|
||||
* This is a somewhat inefficient way to do it:
|
||||
* The VU program is (poorly) translated to C, then the gs packet is sent to a DirectRenderer.
|
||||
* In the future we should make a sprite-specific renderer which would have some benefits:
|
||||
* - do this math on the GPU
|
||||
* - special case the primitive buffer stuff
|
||||
*/
|
||||
void SpriteRenderer::do_2d_group1_block_cpu(u32 count, SharedRenderState* render_state) {
|
||||
if (m_extra_debug) {
|
||||
ImGui::Begin("Sprite Extra Debug");
|
||||
}
|
||||
|
||||
// set up double buffering
|
||||
// xtop vi02 | nop
|
||||
// nop | nop
|
||||
// load sprite count from header
|
||||
// vi04 = count
|
||||
// ilwr.x vi04, vi02 | nop
|
||||
// vi02 = m_vec_data_2d
|
||||
// iaddi vi02, vi02, 0x1 | nop
|
||||
// vi03 = m_adgif
|
||||
// iaddiu vi03, vi02, 0x90 | nop
|
||||
|
||||
// this VU program uses "software pipelining"
|
||||
// it's a little bit tricky to use software pipelining in a case like
|
||||
// this where sometimes you want to reject a sprite entirely and jump ahead
|
||||
// so sometimes they reset back to L7 on rejection.
|
||||
|
||||
// The approach in this translation is to assume we loop back to L7 every time
|
||||
// and not worry about the pipeline stuff that shows up in L8 and on.
|
||||
// you can enter from L7 at anytime, they are not assumed to only run on the first go.
|
||||
// (though if their implementation has bugs we will not replicate them correctly...)
|
||||
|
||||
Matrix4f camera_matrix = m_hud_matrix_data.matrix; // vf25, vf26, vf27, vf28
|
||||
|
||||
for (u32 sprite_idx = 0; sprite_idx < count; sprite_idx++) {
|
||||
if (m_extra_debug) {
|
||||
ImGui::Text("Sprite: %d", sprite_idx);
|
||||
}
|
||||
SpriteHud2DPacket packet;
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
// L7 (prologue, and early abort)
|
||||
// ilw.y vi08, 1(vi02) | nop vi08 = matrix
|
||||
u32 offset_selector = m_vec_data_2d[sprite_idx].matrix();
|
||||
|
||||
// moved this out of the loop.
|
||||
// lq.xyzw vf25, 900(vi00) | nop vf25 = cam_mat
|
||||
// lq.xyzw vf26, 901(vi00) | nop
|
||||
// lq.xyzw vf27, 902(vi00) | nop
|
||||
// lq.xyzw vf28, 903(vi00) | nop
|
||||
// lq.xyzw vf30, 904(vi08) | nop vf30 = hvdf_offset
|
||||
// vf30
|
||||
Vector4f hvdf_offset = offset_selector == 0 ? m_hud_matrix_data.hvdf_offset
|
||||
: m_hud_matrix_data.user_hvdf[offset_selector - 1];
|
||||
|
||||
// lqi.xyzw vf01, vi02 | nop
|
||||
Vector4f pos_vf01 = m_vec_data_2d[sprite_idx].xyz_sx;
|
||||
// lqi.xyzw vf05, vi02 | nop
|
||||
Vector4f flags_vf05 = m_vec_data_2d[sprite_idx].flag_rot_sy;
|
||||
// lqi.xyzw vf11, vi02 | nop
|
||||
Vector4f color_vf11 = m_vec_data_2d[sprite_idx].rgba;
|
||||
|
||||
// multiplications from the right column
|
||||
Vector4f transformed_pos_vf02 = matrix_transform(camera_matrix, pos_vf01);
|
||||
|
||||
Vector4f scales_vf01 = pos_vf01; // now used for something else.
|
||||
// lq.xyzw vf12, 1020(vi00) | mulaw.xyzw ACC, vf28, vf00
|
||||
// vf12 is fog consts
|
||||
Vector4f fog_consts_vf12(m_frame_data.fog_min, m_frame_data.fog_max, m_frame_data.max_scale,
|
||||
m_frame_data.bonus);
|
||||
// ilw.y vi08, 1(vi02) | maddax.xyzw ACC, vf25, vf01
|
||||
// load offset selector for the next round.
|
||||
// nop | madday.xyzw ACC, vf26, vf01
|
||||
// nop | maddz.xyzw vf02, vf27, vf01
|
||||
|
||||
// move.w vf05, vf00 | addw.z vf01, vf00, vf05
|
||||
// scales_vf01.z = sy
|
||||
scales_vf01.z() = flags_vf05.w(); // start building the scale vector
|
||||
flags_vf05.w() = 1.f; // what are we building in flags right now??
|
||||
|
||||
// nop | nop
|
||||
// div Q, vf31.x, vf02.w | muly.z vf05, vf05, vf31
|
||||
float Q = m_frame_data.pfog0 / transformed_pos_vf02.w();
|
||||
flags_vf05.z() *= m_frame_data.deg_to_rad;
|
||||
// nop | mul.xyzw vf03, vf02, vf29
|
||||
Vector4f scaled_pos_vf03 = transformed_pos_vf02.elementwise_multiply(m_frame_data.hmge_scale);
|
||||
// nop | nop
|
||||
// nop | nop
|
||||
// nop | mulz.z vf04, vf05, vf05 (ts)
|
||||
// fmt::print("rot is {} degrees\n", flags_vf05.z() * 360.0 / (2.0 * M_PI));
|
||||
|
||||
// the load is for rotation stuff,
|
||||
// lq.xyzw vf14, 1001(vi00) | clipw.xyz vf03, vf03 (used for fcand)
|
||||
// iaddi vi06, vi00, 0x1 | adda.xyzw ACC, vf11, vf11 (used for fmand)
|
||||
|
||||
// upcoming fcand with 0x3f, that checks all of them.
|
||||
bool fcand_result = clip_xyz_plus_minus(scaled_pos_vf03);
|
||||
bool fmand_result = color_vf11.w() == 0; // (really w+w, but I don't think it matters?)
|
||||
|
||||
// L8:
|
||||
// xgkick double buffer setup
|
||||
// ior vi05, vi15, vi00 | mul.zw vf01, vf01, Q
|
||||
scales_vf01.z() *= Q; // sy
|
||||
scales_vf01.w() *= Q; // sx
|
||||
|
||||
// lq.xyzw vf06, 998(vi00) | mulz.xyzw vf15, vf05, vf04 (ts)
|
||||
auto adgif_vf06 = m_frame_data.adgif_giftag;
|
||||
|
||||
// lq.xyzw vf14, 1002(vi00) ts| mula.xyzw ACC, vf05, vf14 (ts)
|
||||
|
||||
// fmand vi01, vi06 | mul.xyz vf02, vf02, Q
|
||||
transformed_pos_vf02.x() *= Q;
|
||||
transformed_pos_vf02.y() *= Q;
|
||||
transformed_pos_vf02.z() *= Q;
|
||||
|
||||
// if (m_extra_debug) {
|
||||
// imgui_vec(transformed_pos_vf02, "scaled xf");
|
||||
// }
|
||||
|
||||
// ibne vi00, vi01, L10 | addz.x vf01, vf00, vf01
|
||||
scales_vf01.x() = scales_vf01.z(); // = sy
|
||||
if (fmand_result) {
|
||||
if (m_extra_debug) {
|
||||
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fmand reject");
|
||||
ImGui::Separator();
|
||||
}
|
||||
continue; // reject!
|
||||
}
|
||||
|
||||
// lqi.xyzw vf07, vi03 | mulz.xyzw vf16, vf15, vf04 (ts)
|
||||
// vf07 is first use adgif
|
||||
|
||||
// lq.xyzw vf14, 1003(vi00) | madda.xyzw ACC, vf15, vf14 (ts both)
|
||||
|
||||
// lqi.xyzw vf08, vi03 | add.xyzw vf10, vf02, vf30
|
||||
// vf08 is second user adgif
|
||||
Vector4f offset_pos_vf10 = transformed_pos_vf02 + hvdf_offset;
|
||||
// if (m_extra_debug) {
|
||||
// ImGui::Text("sel %d", offset_selector);
|
||||
// //ImGui::Text("hvdf off z: %f tf/w z: %f", hvdf_offset.z(), transformed_pos_vf02.z());
|
||||
// imgui_vec(hvdf_offset, "hvdf");
|
||||
// imgui_vec(transformed_pos_vf02, "tf'd");
|
||||
// }
|
||||
|
||||
// lqi.xyzw vf09, vi03 | mulw.x vf01, vf01, vf01
|
||||
// vf09 is third user adgif
|
||||
scales_vf01.x() *= scales_vf01.w(); // x = sx * sy
|
||||
|
||||
// sqi.xyzw vf06, vi05 | mulz.xyzw vf15, vf16, vf04 (ts)
|
||||
// FIRST ADGIF IS adgif_vf06
|
||||
packet.adgif_giftag = adgif_vf06;
|
||||
|
||||
// lq.xyzw vf14, 1004(vi00) | madda.xyzw ACC, vf16, vf14 (ts both)
|
||||
|
||||
// sqi.xyzw vf07, vi05 | maxx.w vf10, vf10, vf12
|
||||
// SECOND ADGIF is first user
|
||||
// just do all 5 now.
|
||||
packet.user_adgif = m_adgif[sprite_idx];
|
||||
|
||||
offset_pos_vf10.w() = std::max(offset_pos_vf10.w(), m_frame_data.fog_max);
|
||||
|
||||
// sqi.xyzw vf08, vi05 | maxz.zw vf01, vf01, vf31
|
||||
// THIRD ADGIF is second user
|
||||
scales_vf01.z() = std::max(scales_vf01.z(), m_frame_data.min_scale);
|
||||
scales_vf01.w() = std::max(scales_vf01.w(), m_frame_data.min_scale);
|
||||
|
||||
// sqi.xyzw vf09, vi05 | mulz.xyzw vf16, vf15, vf04 (ts)
|
||||
// FOURTH ADGIF is third user
|
||||
|
||||
// lq.xyzw vf14, 1005(vi00) | madda.xyzw ACC, vf15, vf14 (ts both)
|
||||
|
||||
// lqi.xyzw vf06, vi03 | mulw.x vf01, vf01, vf31
|
||||
// vf06 is fourth user adgif
|
||||
scales_vf01.x() *= m_frame_data.inv_area; // x = sx * sy * inv_area (area ratio)
|
||||
|
||||
// lqi.xyzw vf07, vi03 | miniy.w vf10, vf10, vf12
|
||||
// vf07 is fifth user adgif
|
||||
offset_pos_vf10.w() = std::min(offset_pos_vf10.w(), m_frame_data.fog_min);
|
||||
|
||||
// lq.xyzw vf08, 1000(vi00) | nop
|
||||
// vf08 is 2d giftag 2
|
||||
|
||||
// ilw.x vi07, -2(vi02) | madd.xyzw vf05, vf16, vf14
|
||||
auto flag_vi07 = m_vec_data_2d[sprite_idx].flag();
|
||||
Vector4f vf05_sincos(0, 0, std::sin(flags_vf05.z()), std::cos(flags_vf05.z()));
|
||||
|
||||
// lq.xyzw vf30, 904(vi08) | nop
|
||||
// pipline
|
||||
|
||||
// lqi.xyzw vf23, vi02 | miniw.x vf01, vf01, vf00
|
||||
// pipeline
|
||||
scales_vf01.x() = std::min(scales_vf01.x(), 1.f);
|
||||
|
||||
// lqi.xyzw vf24, vi02 | mulx.w vf11, vf11, vf01
|
||||
// pipeline
|
||||
color_vf11.w() *= scales_vf01.x(); // is this right? doesn't this stall??
|
||||
|
||||
// fcand vi01, 0x3f | mulaw.xyzw ACC, vf28, vf00
|
||||
// already computed pipeline
|
||||
|
||||
// lq.xyzw vf17, 1006(vi00) | maddax.xyzw ACC, vf25, vf23 (pipeline)
|
||||
Vector4f basis_x_vf17 = m_frame_data.basis_x;
|
||||
|
||||
// lq.xyzw vf18, 1007(vi00) | madday.xyzw ACC, vf26, vf23 (pipeline)
|
||||
Vector4f basis_y_vf18 = m_frame_data.basis_y;
|
||||
|
||||
assert(flag_vi07 == 0);
|
||||
Vector4f* xy_array = m_frame_data.xy_array + flag_vi07;
|
||||
// lq.xyzw vf19, 980(vi07) | ftoi0.xyzw vf11, vf11
|
||||
Vector4f xy0_vf19 = xy_array[0];
|
||||
math::Vector<s32, 4> color_integer_vf11 = color_vf11.cast<s32>();
|
||||
|
||||
// lq.xyzw vf20, 981(vi07) | maddz.xyzw vf02, vf27, vf23 (pipeline)
|
||||
Vector4f xy1_vf20 = xy_array[1];
|
||||
|
||||
// lq.xyzw vf21, 982(vi07) | mulaw.xyzw ACC, vf17, vf05
|
||||
Vector4f xy2_vf21 = xy_array[2];
|
||||
Vector4f acc = basis_x_vf17 * vf05_sincos.w();
|
||||
|
||||
// lq.xyzw vf22, 983(vi07) | msubz.xyzw vf12, vf18, vf05
|
||||
Vector4f xy3_vf22 = xy_array[3];
|
||||
Vector4f vf12_rotated = acc - (basis_y_vf18 * vf05_sincos.z());
|
||||
// sq.xyzw vf11, 3(vi05) | mulaz.xyzw ACC, vf17, vf05
|
||||
// EIGHTH is color integer
|
||||
packet.color = color_integer_vf11;
|
||||
|
||||
acc = basis_x_vf17 * vf05_sincos.z();
|
||||
|
||||
// lqi.xyzw vf11, vi02 | maddw.xyzw vf13, vf18, vf05
|
||||
// (pipeline)
|
||||
Vector4f vf13_rotated_trans = acc + basis_y_vf18 * vf05_sincos.w();
|
||||
|
||||
// move.w vf24, vf00 | addw.z vf23, vf00, vf24 (pipeline both)
|
||||
|
||||
// div Q, vf31.x, vf02.w | mulw.xyzw vf12, vf12, vf01
|
||||
// (pipeline)
|
||||
vf12_rotated *= scales_vf01.w();
|
||||
|
||||
// ibne vi00, vi01, L9 | muly.z vf24, vf24, vf31 (pipeline)
|
||||
if (fcand_result) {
|
||||
if (m_extra_debug) {
|
||||
ImGui::TextColored(ImVec4(0.8, 0.2, 0.2, 1.0), "fcand reject");
|
||||
ImGui::Separator();
|
||||
}
|
||||
continue; // reject (could move earlier)
|
||||
}
|
||||
|
||||
// ilw.y vi08, 1(vi02) | mulz.xyzw vf13, vf13, vf01
|
||||
// (pipeline)
|
||||
vf13_rotated_trans *= scales_vf01.z();
|
||||
|
||||
// LEFT OFF HERE!
|
||||
|
||||
// sqi.xyzw vf06, vi05 | mul.xyzw vf03, vf02, vf29
|
||||
// FIFTH is fourth user
|
||||
|
||||
// sqi.xyzw vf07, vi05 | mulaw.xyzw ACC, vf10, vf00
|
||||
// SIXTH is fifth user
|
||||
acc = offset_pos_vf10;
|
||||
|
||||
// sqi.xyzw vf08, vi05 | maddax.xyzw ACC, vf12, vf19
|
||||
// SEVENTH is giftag2
|
||||
packet.sprite_giftag = m_frame_data.sprite_2d_giftag2;
|
||||
acc += vf12_rotated * xy0_vf19.x();
|
||||
|
||||
// lq.xyzw vf06, 988(vi00) | maddy.xyzw vf19, vf13, vf19
|
||||
Vector4f st0_vf06 = m_frame_data.st_array[0];
|
||||
xy0_vf19 = acc + vf13_rotated_trans * xy0_vf19.y();
|
||||
|
||||
// lq.xyzw vf07, 989(vi00) | mulaw.xyzw ACC, vf10, vf00
|
||||
Vector4f st1_vf07 = m_frame_data.st_array[1];
|
||||
acc = offset_pos_vf10;
|
||||
|
||||
// lq.xyzw vf08, 990(vi00) | maddax.xyzw ACC, vf12, vf20
|
||||
Vector4f st2_vf08 = m_frame_data.st_array[2];
|
||||
acc += vf12_rotated * xy1_vf20.x();
|
||||
|
||||
// lq.xyzw vf09, 991(vi00) | maddy.xyzw vf20, vf13, vf20
|
||||
Vector4f st3_vf09 = m_frame_data.st_array[3];
|
||||
xy1_vf20 = acc + vf13_rotated_trans * xy1_vf20.y();
|
||||
|
||||
// sq.xyzw vf06, 1(vi05) | mulaw.xyzw ACC, vf10, vf00
|
||||
// NINTH is st0
|
||||
packet.st0 = st0_vf06;
|
||||
acc = offset_pos_vf10;
|
||||
|
||||
// sq.xyzw vf07, 3(vi05) | maddax.xyzw ACC, vf12, vf21
|
||||
// ELEVEN is st1
|
||||
packet.st1 = st1_vf07;
|
||||
acc += vf12_rotated * xy2_vf21.x();
|
||||
|
||||
// sq.xyzw vf08, 5(vi05) | maddy.xyzw vf21, vf13, vf21
|
||||
// THIRTEEN is st2
|
||||
packet.st2 = st2_vf08;
|
||||
xy2_vf21 = acc + vf13_rotated_trans * xy2_vf21.y();
|
||||
|
||||
// sq.xyzw vf09, 7(vi05) | mulaw.xyzw ACC, vf10, vf00
|
||||
// FIFTEEN is st3
|
||||
packet.st3 = st3_vf09;
|
||||
acc = offset_pos_vf10;
|
||||
|
||||
// nop | maddax.xyzw ACC, vf12, vf22
|
||||
acc += vf12_rotated * xy3_vf22.x();
|
||||
|
||||
// nop | maddy.xyzw vf22, vf13, vf22
|
||||
xy3_vf22 = acc + vf13_rotated_trans * xy3_vf22.y();
|
||||
|
||||
// lq.xyzw vf12, 1020(vi00) | ftoi4.xyzw vf19, vf19
|
||||
// (pipeline)
|
||||
auto xy0_vf19_int = (xy0_vf19 * 16.f).cast<s32>();
|
||||
|
||||
// lq.xyzw vf14, 1001(vi00) | ftoi4.xyzw vf20, vf20
|
||||
// (pipeline)
|
||||
auto xy1_vf20_int = (xy1_vf20 * 16.f).cast<s32>();
|
||||
|
||||
// move.xyzw vf05, vf24 | ftoi4.xyzw vf21, vf21
|
||||
// (pipeline)
|
||||
auto xy2_vf21_int = (xy2_vf21 * 16.f).cast<s32>();
|
||||
|
||||
// move.xyzw vf01, vf23 | ftoi4.xyzw vf22, vf22
|
||||
// (pipeline)
|
||||
auto xy3_vf22_int = (xy3_vf22 * 16.f).cast<s32>();
|
||||
|
||||
if (m_extra_debug) {
|
||||
u32 zi = xy3_vf22_int.z() >> 4;
|
||||
ImGui::Text("z (int): 0x%08x %s", zi, zi >= (1 << 24) ? "bad" : "");
|
||||
ImGui::Text("z (flt): %f", (double)(((u32)zi) << 8) / UINT32_MAX);
|
||||
}
|
||||
|
||||
// sq.xyzw vf19, 2(vi05) | mulz.z vf04, vf24, vf24 (pipeline)
|
||||
// TENTH is xy0int
|
||||
packet.xy0 = xy0_vf19_int;
|
||||
// sq.xyzw vf20, 4(vi05) | clipw.xyz vf03, vf03 (pipeline)
|
||||
// TWELVE is xy1int
|
||||
packet.xy1 = xy1_vf20_int;
|
||||
// sq.xyzw vf21, 6(vi05) | nop
|
||||
// FOURTEEN is xy2int
|
||||
packet.xy2 = xy2_vf21_int;
|
||||
// sq.xyzw vf22, 8(vi05) | nop
|
||||
// SIXTEEN is xy3int
|
||||
packet.xy3 = xy3_vf22_int;
|
||||
|
||||
m_sprite_renderer.render_gif((const u8*)&packet, sizeof(packet), render_state);
|
||||
if (m_extra_debug) {
|
||||
imgui_vec(vf12_rotated, "vf12", 2);
|
||||
imgui_vec(vf13_rotated_trans, "vf13", 2);
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// xgkick vi15 | nop
|
||||
// iaddi vi04, vi04, -0x1 | nop
|
||||
// iaddiu vi01, vi00, 0x672 | nop
|
||||
// ibne vi00, vi04, L8 | nop
|
||||
// isub vi15, vi01, vi15 | adda.xyzw ACC, vf11, vf11
|
||||
// nop | nop :e
|
||||
// nop | nop
|
||||
// L9:
|
||||
// iaddi vi04, vi04, -0x1 | nop
|
||||
// iaddi vi02, vi02, -0x3 | nop
|
||||
// ibne vi00, vi04, L7 | nop
|
||||
// nop | nop
|
||||
// nop | nop :e
|
||||
// nop | nop
|
||||
// L10:
|
||||
// iaddi vi04, vi04, -0x1 | nop
|
||||
// iaddi vi03, vi03, 0x4 | nop
|
||||
// ibne vi00, vi04, L7 | nop
|
||||
// nop | nop
|
||||
// nop | nop :e
|
||||
// nop | nop
|
||||
}
|
||||
|
||||
if (m_extra_debug) {
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/DirectRenderer.h"
|
||||
#include "game/graphics/dma/gs.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
using math::Matrix4f;
|
||||
using math::Vector4f;
|
||||
|
||||
/*!
|
||||
* GOAL sprite-frame-data, all the data that's uploaded once per frame for the sprite system.
|
||||
*/
|
||||
struct SpriteFrameData {
|
||||
Vector4f xy_array[8];
|
||||
Vector4f st_array[4];
|
||||
Vector4f xyz_array[4];
|
||||
Vector4f hmge_scale;
|
||||
float pfog0;
|
||||
float deg_to_rad;
|
||||
float min_scale;
|
||||
float inv_area;
|
||||
GifTag adgif_giftag;
|
||||
GifTag sprite_2d_giftag;
|
||||
GifTag sprite_2d_giftag2;
|
||||
Vector4f sincos[5];
|
||||
Vector4f basis_x;
|
||||
Vector4f basis_y;
|
||||
GifTag sprite_3d_giftag;
|
||||
AdGif screen_shader;
|
||||
GifTag clipped_giftag;
|
||||
Vector4f inv_hmge_scale;
|
||||
Vector4f stq_offset;
|
||||
Vector4f stq_scale;
|
||||
Vector4f rgba_plain;
|
||||
GifTag warp_giftag;
|
||||
float fog_min;
|
||||
float fog_max;
|
||||
float max_scale;
|
||||
float bonus;
|
||||
};
|
||||
|
||||
/*!
|
||||
* "Matrix Data" for 3D sprites. This is shared for all 3D sprites
|
||||
*/
|
||||
struct Sprite3DMatrixData {
|
||||
Matrix4f camera;
|
||||
Vector4f hvdf_offset;
|
||||
};
|
||||
|
||||
/*!
|
||||
* "Matrix Data" for 2D screen space sprites. These are shared for all 2D HUD sprites
|
||||
*/
|
||||
struct SpriteHudMatrixData {
|
||||
Matrix4f matrix;
|
||||
|
||||
// the "matrix" field is an index into these 76 quadwords
|
||||
Vector4f hvdf_offset;
|
||||
Vector4f user_hvdf[75];
|
||||
};
|
||||
|
||||
/*!
|
||||
* The "vector data" (sprite-vec-data-2d). Each sprite has its own vector data.
|
||||
*/
|
||||
struct SpriteVecData2d {
|
||||
Vector4f xyz_sx; // position + x scale
|
||||
Vector4f flag_rot_sy; // flags, rotation, and scale y
|
||||
Vector4f rgba; // color
|
||||
|
||||
float sx() const { return xyz_sx.w(); }
|
||||
|
||||
// for HUD, this is the hvdf offset index
|
||||
s32 flag() {
|
||||
s32 result;
|
||||
memcpy(&result, &flag_rot_sy.x(), sizeof(s32));
|
||||
return result;
|
||||
}
|
||||
|
||||
// unused for HUD
|
||||
s32 matrix() {
|
||||
s32 result;
|
||||
memcpy(&result, &flag_rot_sy.y(), sizeof(s32));
|
||||
return result;
|
||||
}
|
||||
|
||||
// rotation in degrees
|
||||
float rot() const { return flag_rot_sy.z(); }
|
||||
|
||||
// scale y.
|
||||
float sy() const { return flag_rot_sy.w(); }
|
||||
};
|
||||
|
||||
/*!
|
||||
* The layout of VU1 data memory, in quadword addresses
|
||||
* The lower 800 qw's hold two buffers for double buffering drawing/loading.
|
||||
*/
|
||||
enum SpriteDataMem {
|
||||
// these three can have an offset of 0 or 400 depending on which buffer
|
||||
Header = 0, // number of sprites (updated per chunk)
|
||||
Vector = 1, // vector data (updated per chunk)
|
||||
Adgif = 145, // adgifs (updated per chunk)
|
||||
|
||||
// offset of first buffer
|
||||
Buffer0 = 0,
|
||||
// offset of second buffer
|
||||
Buffer1 = 400,
|
||||
|
||||
GiftagBuilding = 800, // used to store gs packets for xgkicking
|
||||
// matrix data (different depending on group)
|
||||
Matrix = 900,
|
||||
// frame data (same for the whole frame)
|
||||
FrameData = 980
|
||||
};
|
||||
|
||||
/*!
|
||||
* The GS packet built by the sprite renderer.
|
||||
*/
|
||||
struct SpriteHud2DPacket {
|
||||
GifTag adgif_giftag; // starts the adgif shader. 0
|
||||
AdGif user_adgif; // the adgif shader 16
|
||||
GifTag sprite_giftag; // 96
|
||||
math::Vector<s32, 4> color;
|
||||
Vector4f st0;
|
||||
math::Vector<s32, 4> xy0;
|
||||
Vector4f st1;
|
||||
math::Vector<s32, 4> xy1;
|
||||
Vector4f st2;
|
||||
math::Vector<s32, 4> xy2;
|
||||
Vector4f st3;
|
||||
math::Vector<s32, 4> xy3;
|
||||
};
|
||||
|
||||
/*!
|
||||
* The layout of VU1 code memory
|
||||
*/
|
||||
enum SpriteProgMem {
|
||||
Init = 0, // the sprite initialization program. runs once per frame.
|
||||
Sprites2dGrp0 = 3, // world space 2d sprites
|
||||
Sprites2dHud = 109, // hud sprites
|
||||
Sprites3d = 211 // 3d sprites
|
||||
};
|
||||
|
||||
static_assert(offsetof(SpriteFrameData, hmge_scale) == 256);
|
||||
static_assert(sizeof(SpriteFrameData) == 0x290, "SpriteFrameData size");
|
||||
|
||||
class SpriteRenderer : public BucketRenderer {
|
||||
public:
|
||||
SpriteRenderer(const std::string& name, BucketId my_id);
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state) override;
|
||||
void draw_debug_window() override;
|
||||
static constexpr int SPRITES_PER_CHUNK = 48;
|
||||
|
||||
private:
|
||||
void render_distorter(DmaFollower& dma, SharedRenderState* render_state);
|
||||
void handle_sprite_frame_setup(DmaFollower& dma);
|
||||
void render_3d(DmaFollower& dma);
|
||||
void render_2d_group0(DmaFollower& dma);
|
||||
void render_fake_shadow(DmaFollower& dma);
|
||||
void render_2d_group1(DmaFollower& dma, SharedRenderState* render_state);
|
||||
void do_2d_group1_block_cpu(u32 count, SharedRenderState* render_state);
|
||||
|
||||
u8 m_sprite_distorter_setup[7 * 16]; // direct data
|
||||
u8 m_sprite_direct_setup[3 * 16];
|
||||
SpriteFrameData m_frame_data; // qwa: 980
|
||||
Sprite3DMatrixData m_3d_matrix_data;
|
||||
SpriteHudMatrixData m_hud_matrix_data;
|
||||
|
||||
SpriteVecData2d m_vec_data_2d[SPRITES_PER_CHUNK];
|
||||
AdGif m_adgif[SPRITES_PER_CHUNK];
|
||||
|
||||
struct DebugStats {
|
||||
int blocks_2d_grp1 = 0;
|
||||
int count_2d_grp1 = 0;
|
||||
} m_debug_stats;
|
||||
|
||||
bool m_extra_debug = false;
|
||||
|
||||
DirectRenderer m_sprite_renderer;
|
||||
DirectRenderer m_direct_renderer;
|
||||
};
|
||||
@@ -0,0 +1,249 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
#include "TextureUploadHandler.h"
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
|
||||
TextureUploadHandler::TextureUploadHandler(const std::string& name, BucketId my_id)
|
||||
: BucketRenderer(name, my_id) {}
|
||||
|
||||
void TextureUploadHandler::render(DmaFollower& dma, SharedRenderState* render_state) {
|
||||
m_stats = {};
|
||||
|
||||
// this is the data we get from the PC Port modification.
|
||||
struct TextureUpload {
|
||||
u64 page;
|
||||
s64 mode;
|
||||
};
|
||||
|
||||
std::vector<TextureUpload> uploads;
|
||||
|
||||
// loop through all data, grabbing buckets
|
||||
while (dma.current_tag_offset() != render_state->next_bucket) {
|
||||
auto dma_tag = dma.current_tag();
|
||||
auto data = dma.read_and_advance();
|
||||
if (data.size_bytes == 0 && data.vif0() == 0 && data.vif1() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.size_bytes == 16 && data.vifcode0().kind == VifCode::Kind::PC_PORT &&
|
||||
data.vif1() == 3) {
|
||||
TextureUpload upload_data;
|
||||
memcpy(&upload_data, data.data, sizeof(upload_data));
|
||||
uploads.push_back(upload_data);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dma_tag.kind == DmaTag::Kind::CALL) {
|
||||
dma.read_and_advance(); // call
|
||||
dma.read_and_advance(); // cnt
|
||||
dma.read_and_advance(); // ret
|
||||
// on next
|
||||
assert(dma.current_tag_offset() == render_state->next_bucket);
|
||||
}
|
||||
}
|
||||
|
||||
// if we're replaying a graphics dump, don't try to read ee memory
|
||||
// TODO, we might still want to grab stuff from the cache
|
||||
if (render_state->dump_playback) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: we don't actually copy the textures in the dma chain copying because they aren't
|
||||
// reference by DMA tag. So there's the potential for race conditions if the game gets messed
|
||||
// up and corrupts the texture memory.
|
||||
const u8* ee_mem = (const u8*)render_state->ee_main_memory;
|
||||
|
||||
// The logic here is a bit confusing. It works around an issue where higher LODs are uploaded
|
||||
// before their CLUT in some cases.
|
||||
if (uploads.size() == 2 && uploads[0].mode == 2 && uploads[1].mode == -2 &&
|
||||
uploads[0].page == uploads[1].page) {
|
||||
bool has_segment[3] = {true, true, true};
|
||||
if (!try_to_populate_from_cache(uploads[0].page, has_segment, render_state)) {
|
||||
// couldn't find this texture in cache, need to convert it
|
||||
populate_cache(render_state->texture_pool->convert_textures(
|
||||
ee_mem + uploads[0].page, -2, ee_mem, render_state->offset_of_s7),
|
||||
render_state);
|
||||
populate_cache(render_state->texture_pool->convert_textures(
|
||||
ee_mem + uploads[0].page, 2, ee_mem, render_state->offset_of_s7),
|
||||
render_state);
|
||||
// after conversion, we should be able to populate the texture pool.
|
||||
bool ok = try_to_populate_from_cache(uploads[0].page, has_segment, render_state);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
} else if (uploads.size() == 1 && uploads[0].mode == -1) {
|
||||
// look at the texture page and determine if we have it in cache.
|
||||
bool has_segment[3] = {true, true, true};
|
||||
if (!try_to_populate_from_cache(uploads[0].page, has_segment, render_state)) {
|
||||
populate_cache(render_state->texture_pool->convert_textures(
|
||||
ee_mem + uploads[0].page, -1, ee_mem, render_state->offset_of_s7),
|
||||
render_state);
|
||||
bool ok = try_to_populate_from_cache(uploads[0].page, has_segment, render_state);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
} else if (uploads.size() == 1 && uploads[0].mode == -2) {
|
||||
bool has_segment[3] = {true, true, true};
|
||||
if (!try_to_populate_from_cache(uploads[0].page, has_segment, render_state)) {
|
||||
populate_cache(render_state->texture_pool->convert_textures(
|
||||
ee_mem + uploads[0].page, -2, ee_mem, render_state->offset_of_s7),
|
||||
render_state);
|
||||
bool ok = try_to_populate_from_cache(uploads[0].page, has_segment, render_state);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
} else if (uploads.empty()) {
|
||||
// do nothing.
|
||||
} else {
|
||||
fmt::print("unhandled upload sequence in {}:\n", m_name);
|
||||
for (auto& upload : uploads) {
|
||||
fmt::print(" page: 0x{:x} mode: {}\n", upload.page, upload.mode);
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureUploadHandler::draw_debug_window() {
|
||||
ImGui::Text("Textures this frame: %d", m_stats.textures_provided);
|
||||
ImGui::Text("Textures converted: %d", m_stats.textures_converted);
|
||||
ImGui::Text("Textures replaced: %d", m_stats.textures_evicted);
|
||||
}
|
||||
|
||||
namespace {
|
||||
const char* goal_string(u32 ptr, const u8* memory_base) {
|
||||
if (ptr == 0) {
|
||||
assert(false);
|
||||
}
|
||||
return (const char*)(memory_base + ptr + 4);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Try to set an entry in the texture pool from a cached texture for the given page (GOAL pointer).
|
||||
*/
|
||||
bool TextureUploadHandler::try_to_populate_from_cache(u64 page,
|
||||
const bool with_seg[3],
|
||||
SharedRenderState* render_state) {
|
||||
auto old_tex_provided = m_stats.textures_provided;
|
||||
const u8* ee_mem = (const u8*)render_state->ee_main_memory;
|
||||
auto tpage = ee_mem + page;
|
||||
GoalTexturePage texture_page;
|
||||
memcpy(&texture_page, tpage, sizeof(GoalTexturePage));
|
||||
|
||||
// loop over all textures in the page
|
||||
for (int tex_idx = 0; tex_idx < texture_page.length; tex_idx++) {
|
||||
// we might have some invalid textures, for whatever reason. The PS2 side checks for this.
|
||||
GoalTexture tex;
|
||||
if (texture_page.try_copy_texture_description(&tex, tex_idx, ee_mem, tpage,
|
||||
render_state->offset_of_s7)) {
|
||||
// loop over all mip levels of this texture
|
||||
for (int mip_idx = 0; mip_idx < tex.num_mips; mip_idx++) {
|
||||
// only grab mip levels that we requested (we don't want to overwrite vram that the engine
|
||||
// expects us to not touch)
|
||||
if (with_seg[tex.segment_of_mip(mip_idx)]) {
|
||||
m_stats.textures_provided++;
|
||||
|
||||
// lookup the texture by name!
|
||||
auto it = m_tex_cache.find(goal_string(tex.name_ptr, ee_mem));
|
||||
if (it == m_tex_cache.end() || !it->second.at(mip_idx)) {
|
||||
// failed to find it, reject the entire page load
|
||||
m_stats.textures_provided = old_tex_provided;
|
||||
return false;
|
||||
} else {
|
||||
// found it! Set it in the pool (just setting a pointer)
|
||||
render_state->texture_pool->set_texture(tex.dest[mip_idx], it->second.at(mip_idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Cache the given textures and set in pool
|
||||
*/
|
||||
void TextureUploadHandler::populate_cache(
|
||||
const std::vector<std::shared_ptr<TextureRecord>>& textures,
|
||||
SharedRenderState* render_state) {
|
||||
for (auto& tex : textures) {
|
||||
// disable automatic GC of these textures. We need this - even if the texture becomes evicted
|
||||
// from PS2 VRAM, we want to hold on to the conversion. Now this cache will be responsible for
|
||||
// managing this texture.
|
||||
tex->do_gc = false;
|
||||
m_stats.textures_provided++;
|
||||
m_stats.textures_converted++;
|
||||
// put in pool too
|
||||
render_state->texture_pool->set_texture(tex->dest, tex);
|
||||
auto it = m_tex_cache.find(tex->name);
|
||||
if (it != m_tex_cache.end()) {
|
||||
if (it->second.at(tex->mip_level)) {
|
||||
// replacing an existing, don't forget to kill the original.
|
||||
m_stats.textures_evicted++;
|
||||
render_state->texture_pool->discard(it->second.at(tex->mip_level));
|
||||
}
|
||||
it->second.at(tex->mip_level) = tex;
|
||||
} else {
|
||||
std::vector<std::shared_ptr<TextureRecord>> recs(7); // max mip
|
||||
recs.at(tex->mip_level) = tex;
|
||||
m_tex_cache.insert({tex->name, std::move(recs)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Unload any cached textures from GPU.
|
||||
* Remove all textures from this cache.
|
||||
* Set do_gc on all textures, as they may be in use in the pool and we may need them.
|
||||
*
|
||||
* Effectively, this will require all textures to re-converted and re-uploaded next time they are
|
||||
* uploaded from the game.
|
||||
*/
|
||||
void TextureUploadHandler::evict_all() {
|
||||
for (auto& e : m_tex_cache) {
|
||||
for (auto& x : e.second) {
|
||||
if (x) {
|
||||
if (x->on_gpu) {
|
||||
x->unload_from_gpu();
|
||||
}
|
||||
x->do_gc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_tex_cache = {};
|
||||
}
|
||||
|
||||
void TextureUploadHandler::serialize(Serializer& ser) {
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(m_tex_cache.size());
|
||||
for (auto& entry : m_tex_cache) {
|
||||
ser.save_str(&entry.first);
|
||||
ser.save<size_t>(entry.second.size());
|
||||
for (auto& x : entry.second) {
|
||||
if (x) {
|
||||
ser.save<u8>(1);
|
||||
x->serialize(ser);
|
||||
} else {
|
||||
ser.save<u8>(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
evict_all();
|
||||
auto size = ser.load<size_t>();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
auto str = ser.load_string();
|
||||
std::vector<std::shared_ptr<TextureRecord>> recs(ser.load<size_t>());
|
||||
for (auto& x : recs) {
|
||||
if (ser.load<u8>()) {
|
||||
x = std::make_shared<TextureRecord>();
|
||||
x->serialize(ser);
|
||||
x->on_gpu = false;
|
||||
}
|
||||
}
|
||||
m_tex_cache.insert({str, std::move(recs)});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/texture/TexturePool.h"
|
||||
|
||||
/*!
|
||||
* The TextureUploadHandler receives textures uploads in the DMA chain and updates the TexturePool.
|
||||
* It will attempt to cache textures when possible as converting and uploading them to the GPU is
|
||||
* pretty expensive.
|
||||
*
|
||||
* Note that the PC Port sends a somewhat simplified texture upload message and this can't handle
|
||||
* any arbitrary PS2 texture transfer. We rely on the texture metadata in GOAL to simplify this.
|
||||
*/
|
||||
class TextureUploadHandler : public BucketRenderer {
|
||||
public:
|
||||
TextureUploadHandler(const std::string& name, BucketId my_id);
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state) override;
|
||||
void draw_debug_window() override;
|
||||
void serialize(Serializer& ser) override;
|
||||
|
||||
private:
|
||||
void evict_all();
|
||||
bool try_to_populate_from_cache(u64 page,
|
||||
const bool with_seg[3],
|
||||
SharedRenderState* render_state);
|
||||
void populate_cache(const std::vector<std::shared_ptr<TextureRecord>>& textures,
|
||||
SharedRenderState* render_state);
|
||||
std::unordered_map<std::string, std::vector<std::shared_ptr<TextureRecord>>> m_tex_cache;
|
||||
|
||||
struct {
|
||||
u32 textures_provided = 0;
|
||||
u32 textures_converted = 0;
|
||||
u32 textures_evicted = 0;
|
||||
|
||||
} m_stats;
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
#include "debug_gui.h"
|
||||
#include <algorithm>
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
void FrameTimeRecorder::finish_frame() {
|
||||
m_frame_times[m_idx++] = m_timer.getMs();
|
||||
if (m_idx == SIZE) {
|
||||
m_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTimeRecorder::start_frame() {
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
void FrameTimeRecorder::draw_window(const DmaStats& dma_stats) {
|
||||
auto* p_open = &m_open;
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
|
||||
|
||||
const float PAD = 10.0f;
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
|
||||
ImVec2 work_size = viewport->WorkSize;
|
||||
ImVec2 window_pos, window_pos_pivot;
|
||||
window_pos.x = (work_pos.x + work_size.x - PAD);
|
||||
window_pos.y = (work_pos.y + work_size.y - PAD);
|
||||
window_pos_pivot.x = 1.0f;
|
||||
window_pos_pivot.y = 1.0f;
|
||||
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(0.35f); // 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);
|
||||
float worst = 0, total = 0;
|
||||
for (auto x : m_frame_times) {
|
||||
worst = std::max(x, worst);
|
||||
total += x;
|
||||
}
|
||||
if (total / SIZE > 17.) {
|
||||
ImGui::TextColored(ImVec4(1.0, 0.3, 0.3, 1.0), "avg: %.1f", total / SIZE);
|
||||
} else {
|
||||
ImGui::Text("avg: %.1f", total / SIZE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (worst > 17.) {
|
||||
ImGui::TextColored(ImVec4(1.0, 0.3, 0.3, 1.0), "worst: %.1f", worst);
|
||||
} else {
|
||||
ImGui::Text("worst: %.1f", worst);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PlotLines(
|
||||
"0-20ms",
|
||||
[](void* data, int idx) {
|
||||
auto* me = (FrameTimeRecorder*)data;
|
||||
return me->m_frame_times[(me->m_idx + idx) % SIZE];
|
||||
},
|
||||
(void*)this, SIZE, 0, nullptr, 0, 20., ImVec2(300, 40));
|
||||
|
||||
ImGui::Checkbox("Run", &m_play);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Single Frame Advance")) {
|
||||
m_single_frame = true;
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void OpenGlDebugGui::start_frame() {
|
||||
m_frame_timer.start_frame();
|
||||
}
|
||||
|
||||
void OpenGlDebugGui::finish_frame() {
|
||||
m_frame_timer.finish_frame();
|
||||
}
|
||||
|
||||
void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("Windows")) {
|
||||
ImGui::MenuItem("Frame Time Plot", nullptr, &m_draw_frame_time);
|
||||
ImGui::MenuItem("Render Debug", nullptr, &m_draw_debug);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Gfx Dump")) {
|
||||
ImGui::MenuItem("Dump Next Frame!", nullptr, &m_want_save);
|
||||
bool old_replay = m_want_replay;
|
||||
ImGui::MenuItem("Load Saved Dump", nullptr, &m_want_replay);
|
||||
if (!old_replay && m_want_replay) {
|
||||
m_want_dump_load = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputText("Filename", m_dump_save_name, 12);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
|
||||
if (m_draw_frame_time) {
|
||||
m_frame_timer.draw_window(dma_stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file debug_gui.h
|
||||
* The debug menu-bar and frame timing window
|
||||
*/
|
||||
|
||||
#include "common/util/Timer.h"
|
||||
#include "game/graphics/dma/dma.h"
|
||||
|
||||
class FrameTimeRecorder {
|
||||
public:
|
||||
static constexpr int SIZE = 60 * 5;
|
||||
|
||||
void finish_frame();
|
||||
void start_frame();
|
||||
void draw_window(const DmaStats& dma_stats);
|
||||
bool should_advance_frame() {
|
||||
if (m_single_frame) {
|
||||
m_single_frame = false;
|
||||
return true;
|
||||
}
|
||||
return m_play;
|
||||
}
|
||||
|
||||
private:
|
||||
float m_frame_times[SIZE] = {0};
|
||||
int m_idx = 0;
|
||||
Timer m_timer;
|
||||
bool m_open = true;
|
||||
|
||||
bool m_play = true;
|
||||
bool m_single_frame = false;
|
||||
};
|
||||
|
||||
class OpenGlDebugGui {
|
||||
public:
|
||||
void start_frame();
|
||||
void finish_frame();
|
||||
void draw(const DmaStats& dma_stats);
|
||||
bool should_draw_render_debug() const { return m_draw_debug; }
|
||||
bool& want_save() { return m_want_save; }
|
||||
bool& want_dump_replay() { return m_want_replay; }
|
||||
bool& want_dump_load() { return m_want_dump_load; }
|
||||
const char* dump_name() const { return m_dump_save_name; }
|
||||
|
||||
bool should_advance_frame() { return m_frame_timer.should_advance_frame(); }
|
||||
|
||||
private:
|
||||
FrameTimeRecorder m_frame_timer;
|
||||
bool m_draw_frame_time = false;
|
||||
bool m_draw_debug = false;
|
||||
bool m_want_save = false;
|
||||
bool m_want_replay = false;
|
||||
bool m_want_dump_load = false;
|
||||
char m_dump_save_name[256] = "dump.bin";
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// Debug shader for drawing things in red. Uses the same conventions as direct_basic, see there for more details
|
||||
|
||||
#version 330 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
in vec4 fragment_color;
|
||||
|
||||
void main() {
|
||||
color = fragment_color;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Debug shader for drawing things in red. Uses the same conventions as direct_basic, see there for more details
|
||||
|
||||
#version 330 core
|
||||
|
||||
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);
|
||||
fragment_color = vec4(1.0, 0, 0, 0.7);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// Shader for the DirectRenderer. Inputs are RGBA + position.
|
||||
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 position_in;
|
||||
@@ -6,6 +8,7 @@ layout (location = 1) in vec4 rgba_in;
|
||||
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);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w + 0.5);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
in vec4 fragment_color;
|
||||
in vec2 tex_coord;
|
||||
uniform sampler2D tex_T0;
|
||||
|
||||
void main() {
|
||||
vec4 T0 = texture(tex_T0, tex_coord);
|
||||
T0.w = 1.0;
|
||||
color = fragment_color * T0 * 2.0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 position_in;
|
||||
layout (location = 1) in vec4 rgba_in;
|
||||
layout (location = 2) in vec2 tex_coord_in;
|
||||
|
||||
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);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.a * 2);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
in vec4 fragment_color;
|
||||
in vec2 tex_coord;
|
||||
uniform sampler2D tex_T0;
|
||||
|
||||
void main() {
|
||||
vec4 T0 = texture(tex_T0, tex_coord);
|
||||
vec4 tex_color = fragment_color * T0 * 2.0;
|
||||
if (tex_color.a <= 38./255.) {
|
||||
discard;
|
||||
}
|
||||
color = tex_color;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 position_in;
|
||||
layout (location = 1) in vec4 rgba_in;
|
||||
layout (location = 2) in vec2 tex_coord_in;
|
||||
|
||||
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);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
in vec4 fragment_color;
|
||||
in vec2 tex_coord;
|
||||
uniform sampler2D tex_T0;
|
||||
|
||||
void main() {
|
||||
vec4 T0 = texture(tex_T0, tex_coord);
|
||||
vec4 tex_color = fragment_color * T0 * 2.0;
|
||||
if (tex_color.a > 38./255.) {
|
||||
discard;
|
||||
}
|
||||
color = tex_color;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 position_in;
|
||||
layout (location = 1) in vec4 rgba_in;
|
||||
layout (location = 2) in vec2 tex_coord_in;
|
||||
|
||||
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);
|
||||
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
|
||||
tex_coord = tex_coord_in;
|
||||
}
|
||||
@@ -22,6 +22,10 @@
|
||||
#include "common/log/log.h"
|
||||
#include "common/goal_constants.h"
|
||||
#include "game/runtime.h"
|
||||
#include "common/util/Timer.h"
|
||||
#include "game/graphics/opengl_renderer/debug_gui.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/compress.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -34,6 +38,7 @@ struct GraphicsData {
|
||||
std::mutex dma_mutex;
|
||||
std::condition_variable dma_cv;
|
||||
u64 frame_idx = 0;
|
||||
u64 frame_idx_of_input_data = 0;
|
||||
bool has_data_to_render = false;
|
||||
FixedChunkDmaCopier dma_copier;
|
||||
|
||||
@@ -43,6 +48,15 @@ struct GraphicsData {
|
||||
// temporary opengl renderer
|
||||
OpenGLRenderer ogl_renderer;
|
||||
|
||||
OpenGlDebugGui debug_gui;
|
||||
|
||||
Serializer loaded_dump;
|
||||
|
||||
void serialize(Serializer& ser) {
|
||||
dma_copier.serialize_last_result(ser);
|
||||
ogl_renderer.serialize(ser);
|
||||
}
|
||||
|
||||
GraphicsData()
|
||||
: dma_copier(EE_MAIN_MEM_SIZE),
|
||||
texture_pool(std::make_shared<TexturePool>()),
|
||||
@@ -137,6 +151,7 @@ static std::shared_ptr<GfxDisplay> gl_make_main_display(int width,
|
||||
glfwSwapInterval(settings.vsync);
|
||||
|
||||
SetDisplayCallbacks(window);
|
||||
Pad::initialize();
|
||||
|
||||
if (HasError()) {
|
||||
lg::error("gl_make_main_display error");
|
||||
@@ -175,18 +190,26 @@ static void gl_kill_display(GfxDisplay* display) {
|
||||
glfwDestroyWindow(display->window_glfw);
|
||||
}
|
||||
|
||||
static void gl_render_display(GfxDisplay* display) {
|
||||
GLFWwindow* window = display->window_glfw;
|
||||
void make_gfx_dump() {
|
||||
Timer ser_timer;
|
||||
Serializer ser;
|
||||
|
||||
// poll events
|
||||
glfwPollEvents();
|
||||
glfwMakeContextCurrent(window);
|
||||
// save the dma chain and renderer state
|
||||
g_gfx_data->serialize(ser);
|
||||
auto result = ser.get_save_result();
|
||||
Timer compression_timer;
|
||||
auto compressed = compression::compress_zstd(result.first, result.second);
|
||||
lg::info("Serialized graphics state in {:.1f} ms, {:.3f} MB, compressed {:.3f} MB {:.1f} ms",
|
||||
ser_timer.getMs(), ((double)result.second) / (1 << 20),
|
||||
((double)compressed.size() / (1 << 20)), compression_timer.getMs());
|
||||
|
||||
// imgui start of frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
file_util::create_dir_if_needed(file_util::get_file_path({"gfx_dumps"}));
|
||||
file_util::write_binary_file(
|
||||
file_util::get_file_path({"gfx_dumps", g_gfx_data->debug_gui.dump_name()}), compressed.data(),
|
||||
compressed.size());
|
||||
}
|
||||
|
||||
void render_game_frame(int width, int height) {
|
||||
// wait for a copied chain.
|
||||
bool got_chain = false;
|
||||
{
|
||||
@@ -199,11 +222,21 @@ static void gl_render_display(GfxDisplay* display) {
|
||||
|
||||
// render that chain.
|
||||
if (got_chain) {
|
||||
// g_gfx_data->ogl_renderer.render(DmaFollower(g_gfx_data->dma_copier.get_last_input_data(),
|
||||
// g_gfx_data->dma_copier.get_last_input_offset()),
|
||||
// width, height);
|
||||
|
||||
// we want to serialize before rendering
|
||||
if (g_gfx_data->debug_gui.want_save()) {
|
||||
make_gfx_dump();
|
||||
g_gfx_data->debug_gui.want_save() = false;
|
||||
}
|
||||
|
||||
auto& chain = g_gfx_data->dma_copier.get_last_result();
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
g_gfx_data->frame_idx_of_input_data = g_gfx_data->frame_idx;
|
||||
g_gfx_data->ogl_renderer.render(DmaFollower(chain.data.data(), chain.start_offset), width,
|
||||
height);
|
||||
height, g_gfx_data->debug_gui.should_draw_render_debug(),
|
||||
false);
|
||||
}
|
||||
|
||||
// before vsync, mark the chain as rendered.
|
||||
@@ -214,16 +247,65 @@ static void gl_render_display(GfxDisplay* display) {
|
||||
g_gfx_data->has_data_to_render = false;
|
||||
g_gfx_data->sync_cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void render_dump_frame(int width, int height) {
|
||||
Timer deser_timer;
|
||||
if (g_gfx_data->debug_gui.want_dump_load()) {
|
||||
auto data = file_util::read_binary_file(
|
||||
file_util::get_file_path({"gfx_dumps", g_gfx_data->debug_gui.dump_name()}));
|
||||
auto decompressed = compression::decompress_zstd(data.data(), data.size());
|
||||
g_gfx_data->loaded_dump = Serializer(decompressed.data(), decompressed.size());
|
||||
}
|
||||
|
||||
g_gfx_data->loaded_dump.reset_load();
|
||||
g_gfx_data->serialize(g_gfx_data->loaded_dump);
|
||||
|
||||
if (g_gfx_data->debug_gui.want_dump_load()) {
|
||||
lg::info("Loaded and deserialized graphics state in {:.1f} ms, {:.3f} MB", deser_timer.getMs(),
|
||||
((double)g_gfx_data->loaded_dump.data_size()) / (1 << 20));
|
||||
}
|
||||
g_gfx_data->debug_gui.want_dump_load() = false;
|
||||
|
||||
auto& chain = g_gfx_data->dma_copier.get_last_result();
|
||||
g_gfx_data->ogl_renderer.render(DmaFollower(chain.data.data(), chain.start_offset), width, height,
|
||||
g_gfx_data->debug_gui.should_draw_render_debug(), true);
|
||||
}
|
||||
|
||||
static void gl_render_display(GfxDisplay* display) {
|
||||
GLFWwindow* window = display->window_glfw;
|
||||
|
||||
// poll events
|
||||
glfwPollEvents();
|
||||
glfwMakeContextCurrent(window);
|
||||
Pad::update_gamepads();
|
||||
|
||||
// imgui start of frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
|
||||
if (g_gfx_data->debug_gui.want_dump_replay()) {
|
||||
render_dump_frame(width, height);
|
||||
} else if (g_gfx_data->debug_gui.should_advance_frame()) {
|
||||
render_game_frame(width, height);
|
||||
}
|
||||
|
||||
// render imgui
|
||||
g_gfx_data->debug_gui.draw(g_gfx_data->dma_copier.get_last_result().stats);
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
// actual vsync
|
||||
g_gfx_data->debug_gui.finish_frame();
|
||||
glfwSwapBuffers(window);
|
||||
g_gfx_data->debug_gui.start_frame();
|
||||
|
||||
// toggle even odd and wake up engine waiting on vsync.
|
||||
{
|
||||
if (!g_gfx_data->debug_gui.want_dump_replay()) {
|
||||
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
|
||||
g_gfx_data->frame_idx++;
|
||||
|
||||
@@ -247,7 +329,7 @@ u32 gl_vsync() {
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
|
||||
auto init_frame = g_gfx_data->frame_idx;
|
||||
auto init_frame = g_gfx_data->frame_idx_of_input_data;
|
||||
g_gfx_data->sync_cv.wait(lock, [=] { return g_gfx_data->frame_idx > init_frame; });
|
||||
|
||||
return g_gfx_data->frame_idx & 1;
|
||||
@@ -290,9 +372,7 @@ void gl_send_chain(const void* data, u32 offset) {
|
||||
// The renderers should just operate on DMA chains, so eliminating this step in the future may
|
||||
// be easy.
|
||||
|
||||
// Timer copy_timer;
|
||||
g_gfx_data->dma_copier.run(data, offset);
|
||||
// fmt::print("copy took {:.3f}ms\n", copy_timer.getMs());
|
||||
|
||||
g_gfx_data->has_data_to_render = true;
|
||||
g_gfx_data->dma_cv.notify_all();
|
||||
@@ -300,7 +380,8 @@ void gl_send_chain(const void* data, u32 offset) {
|
||||
}
|
||||
|
||||
void gl_texture_upload_now(const u8* tpage, int mode, u32 s7_ptr) {
|
||||
if (g_gfx_data) {
|
||||
// block
|
||||
if (g_gfx_data && !g_gfx_data->debug_gui.want_dump_replay()) {
|
||||
// just pass it to the texture pool.
|
||||
// the texture pool will take care of locking.
|
||||
// we don't want to lock here for the entire duration of the conversion.
|
||||
@@ -309,7 +390,7 @@ void gl_texture_upload_now(const u8* tpage, int mode, u32 s7_ptr) {
|
||||
}
|
||||
|
||||
void gl_texture_relocate(u32 destination, u32 source, u32 format) {
|
||||
if (g_gfx_data) {
|
||||
if (g_gfx_data && !g_gfx_data->debug_gui.want_dump_replay()) {
|
||||
g_gfx_data->texture_pool->relocate(destination, source, format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,4 +174,8 @@ void TextureConverter::download_rgba8888(u8* result,
|
||||
}
|
||||
|
||||
assert(out_offset == expected_size_bytes);
|
||||
}
|
||||
|
||||
void TextureConverter::serialize(Serializer& ser) {
|
||||
ser.from_pod_vector(&m_vram);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/Serializer.h"
|
||||
|
||||
class TextureConverter {
|
||||
public:
|
||||
@@ -17,6 +18,7 @@ class TextureConverter {
|
||||
u32 clut_psm,
|
||||
u32 clut_vram_addr,
|
||||
u32 expected_size_bytes);
|
||||
void serialize(Serializer& ser);
|
||||
|
||||
private:
|
||||
std::vector<u8> m_vram;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <regex>
|
||||
|
||||
#include "TexturePool.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
@@ -14,8 +16,9 @@
|
||||
// this simply converts the PS2 format textures loaded by the game, then puts them into the PC
|
||||
// port texture pool.
|
||||
|
||||
constexpr bool dump_textures_to_file = false;
|
||||
// constexpr bool dump_textures_to_file = false;
|
||||
|
||||
namespace {
|
||||
const char empty_string[] = "";
|
||||
const char* goal_string(u32 ptr, const u8* memory_base) {
|
||||
if (ptr == 0) {
|
||||
@@ -24,71 +27,237 @@ const char* goal_string(u32 ptr, const u8* memory_base) {
|
||||
return (const char*)(memory_base + ptr + 4);
|
||||
}
|
||||
|
||||
struct GoalTexture {
|
||||
s16 w;
|
||||
s16 h;
|
||||
u8 num_mips;
|
||||
u8 tex1_control;
|
||||
u8 psm;
|
||||
u8 mip_shift;
|
||||
u16 clutpsm;
|
||||
u16 dest[7];
|
||||
u16 clut_dest;
|
||||
u8 width[7];
|
||||
u32 name_ptr;
|
||||
u32 size;
|
||||
float uv_dist;
|
||||
u32 masks[3];
|
||||
} // namespace
|
||||
|
||||
s32 segment_of_mip(s32 mip) const {
|
||||
if (2 >= num_mips) {
|
||||
return num_mips - mip - 1;
|
||||
std::string GoalTexturePage::print() const {
|
||||
return fmt::format("Tpage id {} textures {} seg0 {} {} seg1 {} {} seg2 {} {}\n", id, length,
|
||||
segment[0].size, segment[0].dest, segment[1].size, segment[1].dest,
|
||||
segment[2].size, segment[2].dest);
|
||||
}
|
||||
|
||||
void TextureRecord::serialize(Serializer& ser) {
|
||||
ser.from_str(&page_name);
|
||||
ser.from_str(&name);
|
||||
ser.from_ptr(&mip_level);
|
||||
ser.from_ptr(&psm);
|
||||
ser.from_ptr(&cpsm);
|
||||
ser.from_ptr(&w);
|
||||
ser.from_ptr(&h);
|
||||
ser.from_ptr(&data_segment);
|
||||
ser.from_ptr(&on_gpu);
|
||||
ser.from_ptr(&do_gc);
|
||||
ser.from_ptr(&gpu_texture);
|
||||
ser.from_ptr(&dest);
|
||||
ser.from_pod_vector(&data);
|
||||
ser.from_ptr(&min_a_zero);
|
||||
ser.from_ptr(&max_a_zero);
|
||||
ser.from_ptr(&min_a_nonzero);
|
||||
ser.from_ptr(&max_a_nonzero);
|
||||
}
|
||||
|
||||
void TextureData::serialize(Serializer& ser) {
|
||||
if (ser.is_saving()) {
|
||||
if (normal_texture) {
|
||||
ser.save<u8>(1); // has it.
|
||||
normal_texture->serialize(ser);
|
||||
} else {
|
||||
return std::max(0, 2 - mip);
|
||||
ser.save<u8>(0);
|
||||
}
|
||||
|
||||
if (mt4hh_texture) {
|
||||
ser.save<u8>(1); // has it.
|
||||
mt4hh_texture->serialize(ser);
|
||||
} else {
|
||||
ser.save<u8>(0);
|
||||
}
|
||||
} else {
|
||||
u8 has_normal = ser.load<u8>();
|
||||
if (has_normal) {
|
||||
normal_texture = std::make_shared<TextureRecord>();
|
||||
normal_texture->serialize(ser);
|
||||
// after deserializing, nothing is on the GPU
|
||||
normal_texture->on_gpu = false;
|
||||
// there will be a duplicate copy of this texture in the bucket, we want this one to be gc'd
|
||||
normal_texture->do_gc = true;
|
||||
} else {
|
||||
normal_texture.reset();
|
||||
}
|
||||
|
||||
u8 has_mt4 = ser.load<u8>();
|
||||
if (has_mt4) {
|
||||
mt4hh_texture = std::make_shared<TextureRecord>();
|
||||
mt4hh_texture->serialize(ser);
|
||||
mt4hh_texture->on_gpu = false;
|
||||
mt4hh_texture->do_gc = true;
|
||||
} else {
|
||||
mt4hh_texture.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static_assert(sizeof(GoalTexture) == 60, "GoalTexture size");
|
||||
static_assert(offsetof(GoalTexture, clutpsm) == 8);
|
||||
static_assert(offsetof(GoalTexture, clut_dest) == 24);
|
||||
void TexturePool::serialize(Serializer& ser) {
|
||||
m_tex_converter.serialize(ser);
|
||||
|
||||
struct GoalTexturePage {
|
||||
struct Seg {
|
||||
u32 block_data_ptr;
|
||||
u32 size;
|
||||
u32 dest;
|
||||
};
|
||||
u32 file_info_ptr;
|
||||
u32 name_ptr;
|
||||
u32 id;
|
||||
s32 length; // texture count
|
||||
u32 mip0_size;
|
||||
u32 size;
|
||||
Seg segment[3];
|
||||
u32 pad[16];
|
||||
// start of array.
|
||||
|
||||
std::string print() const {
|
||||
return fmt::format("Tpage id {} textures {} seg0 {} {} seg1 {} {} seg2 {} {}\n", id, length,
|
||||
segment[0].size, segment[0].dest, segment[1].size, segment[1].dest,
|
||||
segment[2].size, segment[2].dest);
|
||||
if (ser.is_loading()) {
|
||||
remove_garbage_textures();
|
||||
unload_all_textures();
|
||||
}
|
||||
for (auto& tex : m_textures) {
|
||||
tex.serialize(ser);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_copy_texture_description(GoalTexture* dest,
|
||||
int idx,
|
||||
const u8* memory_base,
|
||||
const u8* tpage,
|
||||
u32 s7_ptr) {
|
||||
u32 ptr;
|
||||
memcpy(&ptr, tpage + sizeof(GoalTexturePage) + 4 * idx, 4);
|
||||
if (ptr == s7_ptr) {
|
||||
return false;
|
||||
void TexturePool::unload_all_textures() {
|
||||
for (auto& tex : m_textures) {
|
||||
if (tex.normal_texture && tex.normal_texture->on_gpu) {
|
||||
tex.normal_texture->unload_from_gpu();
|
||||
}
|
||||
|
||||
if (tex.mt4hh_texture && tex.mt4hh_texture->on_gpu) {
|
||||
tex.mt4hh_texture->unload_from_gpu();
|
||||
}
|
||||
memcpy(dest, memory_base + ptr, sizeof(GoalTexture));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void TextureRecord::unload_from_gpu() {
|
||||
assert(on_gpu);
|
||||
GLuint tex_id = gpu_texture;
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||||
glDeleteTextures(1, &tex_id);
|
||||
on_gpu = false;
|
||||
gpu_texture = -1;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<TextureRecord>> TexturePool::convert_textures(const u8* tpage,
|
||||
int mode,
|
||||
const u8* memory_base,
|
||||
u32 s7_ptr) {
|
||||
Timer timer;
|
||||
std::vector<std::shared_ptr<TextureRecord>> result;
|
||||
|
||||
bool dump_textures_to_file = false;
|
||||
// extract the texture-page object. This is just a description of the page data.
|
||||
GoalTexturePage texture_page;
|
||||
memcpy(&texture_page, tpage, sizeof(GoalTexturePage));
|
||||
|
||||
bool has_segment[3] = {true, true, true};
|
||||
|
||||
u32 sizes[3] = {texture_page.segment[0].size, texture_page.segment[1].size,
|
||||
texture_page.segment[2].size};
|
||||
if (mode == -1) {
|
||||
// I don't really understand what's going on here with the size.
|
||||
// the sizes given aren't the actual sizes in memory, so if you just use that, you get the
|
||||
// wrong answer. I solved this in the decompiler by using the size of the actual data, but we
|
||||
// don't really have that here.
|
||||
u32 size = ((sizes[0] + sizes[1] + sizes[2] + 255) / 256) * 256;
|
||||
|
||||
m_tex_converter.upload(memory_base + texture_page.segment[0].block_data_ptr,
|
||||
texture_page.segment[0].dest, size);
|
||||
|
||||
} else if (mode == 2) {
|
||||
// dump_textures_to_file = true;
|
||||
has_segment[0] = false;
|
||||
has_segment[1] = false;
|
||||
u32 size = ((sizes[2] + 255) / 256) * 256;
|
||||
|
||||
// dest is in 4-byte vram words
|
||||
m_tex_converter.upload(memory_base + texture_page.segment[2].block_data_ptr,
|
||||
texture_page.segment[2].dest, size);
|
||||
} else if (mode == -2) {
|
||||
has_segment[2] = false;
|
||||
// I don't really understand what's going on here with the size.
|
||||
// the selector texture the hud page will be missing the clut unless I make this bigger.
|
||||
u32 size = ((sizes[0] + sizes[1] + 2047) / 256) * 256;
|
||||
m_tex_converter.upload(memory_base + texture_page.segment[0].block_data_ptr,
|
||||
texture_page.segment[0].dest, size);
|
||||
} else {
|
||||
// no reason to skip this, other than
|
||||
lg::error("TexturePool skipping upload now with mode {}.", mode);
|
||||
return {};
|
||||
}
|
||||
|
||||
// loop over all texture in the tpage and download them.
|
||||
for (int tex_idx = 0; tex_idx < texture_page.length; tex_idx++) {
|
||||
GoalTexture tex;
|
||||
if (texture_page.try_copy_texture_description(&tex, tex_idx, memory_base, tpage, s7_ptr)) {
|
||||
// each texture may have multiple mip levels.
|
||||
for (int mip_idx = 0; mip_idx < tex.num_mips; mip_idx++) {
|
||||
if (has_segment[tex.segment_of_mip(mip_idx)]) {
|
||||
u32 ww = tex.w >> mip_idx;
|
||||
u32 hh = tex.h >> mip_idx;
|
||||
u32 size_bytes = ww * hh * 4;
|
||||
|
||||
auto texture_record = std::make_shared<TextureRecord>();
|
||||
texture_record->page_name = goal_string(texture_page.name_ptr, memory_base);
|
||||
texture_record->name = goal_string(tex.name_ptr, memory_base);
|
||||
texture_record->mip_level = mip_idx;
|
||||
texture_record->w = ww;
|
||||
texture_record->h = hh;
|
||||
texture_record->data_segment = tex.segment_of_mip(mip_idx);
|
||||
texture_record->data.resize(size_bytes);
|
||||
texture_record->psm = tex.psm;
|
||||
texture_record->cpsm = tex.clutpsm;
|
||||
texture_record->dest = tex.dest[mip_idx];
|
||||
|
||||
m_tex_converter.download_rgba8888(texture_record->data.data(), tex.dest[mip_idx],
|
||||
tex.width[mip_idx], ww, hh, tex.psm, tex.clutpsm,
|
||||
tex.clut_dest, size_bytes);
|
||||
|
||||
u8 max_a_zero = 0;
|
||||
u8 min_a_zero = 255;
|
||||
u8 max_a_nonzero = 0;
|
||||
u8 min_a_nonzero = 255;
|
||||
for (u32 i = 0; i < ww * hh; i++) {
|
||||
u8 r = texture_record->data[i * 4 + 0];
|
||||
u8 g = texture_record->data[i * 4 + 1];
|
||||
u8 b = texture_record->data[i * 4 + 2];
|
||||
u8 a = texture_record->data[i * 4 + 3];
|
||||
if (r || g || b) {
|
||||
max_a_nonzero = std::max(max_a_nonzero, a);
|
||||
min_a_nonzero = std::min(min_a_nonzero, a);
|
||||
} else {
|
||||
max_a_zero = std::max(max_a_zero, a);
|
||||
min_a_zero = std::min(min_a_zero, a);
|
||||
}
|
||||
}
|
||||
texture_record->max_a_zero = max_a_zero;
|
||||
texture_record->min_a_zero = min_a_zero;
|
||||
texture_record->max_a_nonzero = max_a_nonzero;
|
||||
texture_record->min_a_nonzero = min_a_nonzero;
|
||||
|
||||
if (texture_record->name == "selector" || texture_record->name == "next") {
|
||||
fmt::print("{}: {} {} {} {}\n", texture_record->name, tex.psm, tex.clutpsm,
|
||||
tex.clut_dest * 256 / 4,
|
||||
texture_page.segment[0].dest + ((sizes[0] + sizes[1] + 255) / 256) * 256);
|
||||
}
|
||||
|
||||
fmt::print("TEX: {} nz ({}, {}) z ({}, {}0\n", texture_record->name,
|
||||
texture_record->min_a_nonzero, texture_record->max_a_nonzero,
|
||||
texture_record->min_a_zero, texture_record->max_a_zero);
|
||||
|
||||
// Debug output.
|
||||
if (dump_textures_to_file) {
|
||||
const char* tpage_name = goal_string(texture_page.name_ptr, memory_base);
|
||||
const char* tex_name = goal_string(tex.name_ptr, memory_base);
|
||||
file_util::create_dir_if_needed(
|
||||
file_util::get_file_path({"debug_out", "textures", tpage_name}));
|
||||
file_util::write_rgba_png(
|
||||
fmt::format(
|
||||
file_util::get_file_path({"debug_out", "textures", tpage_name, "{}-{}-{}.png"}),
|
||||
tex_idx, tex_name, mip_idx),
|
||||
texture_record->data.data(), ww, hh);
|
||||
}
|
||||
result.push_back(std::move(texture_record));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// texture was #f, skip it.
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("upload now took {:.2f} ms\n", timer.getMs());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handle a GOAL texture-page object being uploaded to VRAM.
|
||||
@@ -104,93 +273,31 @@ struct GoalTexturePage {
|
||||
* multiple frames.
|
||||
*/
|
||||
void TexturePool::handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr) {
|
||||
Timer timer;
|
||||
|
||||
// extract the texture-page object. This is just a description of the page data.
|
||||
GoalTexturePage texture_page;
|
||||
memcpy(&texture_page, tpage, sizeof(GoalTexturePage));
|
||||
|
||||
u32 sizes[3] = {texture_page.segment[0].size, texture_page.segment[1].size,
|
||||
texture_page.segment[2].size};
|
||||
if (mode == -1) {
|
||||
// I don't really understand what's going on here with the size.
|
||||
// the sizes given aren't the actual sizes in memory, so if you just use that, you get the
|
||||
// wrong answer. I solved this in the decompiler by using the size of the actual data, but we
|
||||
// don't really have that here.
|
||||
u32 size = ((sizes[0] + sizes[1] + sizes[2] + 255) / 256) * 256;
|
||||
m_tex_converter.upload(memory_base + texture_page.segment[0].block_data_ptr,
|
||||
texture_page.segment[0].dest, size);
|
||||
|
||||
} else {
|
||||
// no reason to skip this, other than
|
||||
lg::error("TexturePool skipping upload now with mode {}.", mode);
|
||||
return;
|
||||
auto textures = convert_textures(tpage, mode, memory_base, s7_ptr);
|
||||
for (auto& tex : textures) {
|
||||
set_texture(tex->dest, tex);
|
||||
}
|
||||
|
||||
// loop over all texture in the tpage and download them.
|
||||
for (int tex_idx = 0; tex_idx < texture_page.length; tex_idx++) {
|
||||
GoalTexture tex;
|
||||
if (texture_page.try_copy_texture_description(&tex, tex_idx, memory_base, tpage, s7_ptr)) {
|
||||
// each texture may have multiple mip levels.
|
||||
for (int mip_idx = 0; mip_idx < tex.num_mips; mip_idx++) {
|
||||
u32 ww = tex.w >> mip_idx;
|
||||
u32 hh = tex.h >> mip_idx;
|
||||
u32 size_bytes = ww * hh * 4;
|
||||
|
||||
auto texture_record = std::make_unique<TextureRecord>();
|
||||
texture_record->page_name = goal_string(texture_page.name_ptr, memory_base);
|
||||
texture_record->name = goal_string(tex.name_ptr, memory_base);
|
||||
texture_record->mip_level = mip_idx;
|
||||
texture_record->w = ww;
|
||||
texture_record->h = hh;
|
||||
texture_record->data_segment = tex.segment_of_mip(mip_idx);
|
||||
texture_record->data.resize(size_bytes);
|
||||
|
||||
m_tex_converter.download_rgba8888(texture_record->data.data(), tex.dest[mip_idx],
|
||||
tex.width[mip_idx], ww, hh, tex.psm, tex.clutpsm,
|
||||
tex.clut_dest, size_bytes);
|
||||
|
||||
// Debug output.
|
||||
if (dump_textures_to_file) {
|
||||
const char* tpage_name = goal_string(texture_page.name_ptr, memory_base);
|
||||
const char* tex_name = goal_string(tex.name_ptr, memory_base);
|
||||
file_util::create_dir_if_needed(
|
||||
file_util::get_file_path({"debug_out", "textures", tpage_name}));
|
||||
file_util::write_rgba_png(
|
||||
fmt::format(
|
||||
file_util::get_file_path({"debug_out", "textures", tpage_name, "{}-{}-{}.png"}),
|
||||
tex_idx, tex_name, mip_idx),
|
||||
texture_record->data.data(), ww, hh);
|
||||
}
|
||||
if (tex.psm == 44) {
|
||||
set_mt4hh_texture(tex.dest[mip_idx], std::move(texture_record));
|
||||
} else {
|
||||
set_texture(tex.dest[mip_idx], std::move(texture_record));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// texture was #f, skip it.
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("upload now took {:.2f} ms\n", timer.getMs());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Store a texture in the pool. Location is specified like TBP.
|
||||
*/
|
||||
void TexturePool::set_texture(u32 location, std::unique_ptr<TextureRecord>&& record) {
|
||||
if (m_textures.at(location).normal_texture) {
|
||||
m_garbage_textures.push_back(std::move(m_textures[location].normal_texture));
|
||||
void TexturePool::set_texture(u32 location, std::shared_ptr<TextureRecord> record) {
|
||||
if (record->psm == 44) {
|
||||
if (m_textures.at(location).mt4hh_texture) {
|
||||
if (record->do_gc && m_textures.at(location).mt4hh_texture != record) {
|
||||
m_garbage_textures.push_back(std::move(m_textures[location].mt4hh_texture));
|
||||
}
|
||||
}
|
||||
m_textures[location].mt4hh_texture = std::move(record);
|
||||
} else {
|
||||
if (m_textures.at(location).normal_texture) {
|
||||
if (record->do_gc && m_textures.at(location).normal_texture != record) {
|
||||
m_garbage_textures.push_back(std::move(m_textures[location].normal_texture));
|
||||
}
|
||||
}
|
||||
m_textures[location].normal_texture = std::move(record);
|
||||
}
|
||||
m_textures[location].normal_texture = std::move(record);
|
||||
}
|
||||
|
||||
void TexturePool::set_mt4hh_texture(u32 location, std::unique_ptr<TextureRecord>&& record) {
|
||||
if (m_textures.at(location).mt4hh_texture) {
|
||||
m_garbage_textures.push_back(std::move(m_textures[location].mt4hh_texture));
|
||||
}
|
||||
m_textures[location].mt4hh_texture = std::move(record);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -210,14 +317,23 @@ void TexturePool::draw_debug_window() {
|
||||
int id = 0;
|
||||
int total_vram_bytes = 0;
|
||||
int total_textures = 0;
|
||||
int total_displayed_textures = 0;
|
||||
int total_uploaded_textures = 0;
|
||||
ImGui::Text("GC %d on GPU %d", m_most_recent_gc_count, m_most_recent_gc_count_gpu);
|
||||
ImGui::InputText("texture search", m_regex_input, sizeof(m_regex_input));
|
||||
std::regex regex(m_regex_input[0] ? m_regex_input : ".*");
|
||||
|
||||
for (auto& record : m_textures) {
|
||||
if (record.normal_texture) {
|
||||
ImGui::PushID(id++);
|
||||
auto& tex = *record.normal_texture;
|
||||
draw_debug_for_tex(tex.name, tex);
|
||||
ImGui::PopID();
|
||||
total_textures++;
|
||||
auto& tex = *record.normal_texture;
|
||||
if (std::regex_search(tex.name, regex)) {
|
||||
ImGui::PushID(id++);
|
||||
draw_debug_for_tex(tex.name, tex);
|
||||
ImGui::PopID();
|
||||
total_displayed_textures++;
|
||||
}
|
||||
|
||||
if (tex.on_gpu) {
|
||||
total_vram_bytes += tex.w * tex.h * 4; // todo, if we support other formats
|
||||
total_uploaded_textures++;
|
||||
@@ -225,25 +341,33 @@ void TexturePool::draw_debug_window() {
|
||||
}
|
||||
|
||||
if (record.mt4hh_texture) {
|
||||
ImGui::PushID(id++);
|
||||
auto& tex = *record.mt4hh_texture;
|
||||
draw_debug_for_tex(tex.name, tex);
|
||||
ImGui::PopID();
|
||||
total_textures++;
|
||||
auto& tex = *record.mt4hh_texture;
|
||||
if (std::regex_search(tex.name, regex)) {
|
||||
ImGui::PushID(id++);
|
||||
draw_debug_for_tex(tex.name, tex);
|
||||
ImGui::PopID();
|
||||
total_displayed_textures++;
|
||||
}
|
||||
|
||||
if (tex.on_gpu) {
|
||||
total_vram_bytes += tex.w * tex.h * 4; // todo, if we support other formats
|
||||
total_uploaded_textures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Text("Total Textures: %d Uploaded: %d VRAM: %.3f MB", total_textures,
|
||||
total_uploaded_textures, (float)total_vram_bytes / (1024 * 1024));
|
||||
ImGui::Text("Total Textures: %d Uploaded: %d Shown: %d VRAM: %.3f MB", total_textures,
|
||||
total_uploaded_textures, total_displayed_textures,
|
||||
(float)total_vram_bytes / (1024 * 1024));
|
||||
}
|
||||
|
||||
void TexturePool::draw_debug_for_tex(const std::string& name, TextureRecord& tex) {
|
||||
if (ImGui::CollapsingHeader(name.c_str())) {
|
||||
ImGui::Text("Page: %s Size: %d x %d mip %d On GPU? %d", tex.page_name.c_str(), tex.w, tex.h,
|
||||
tex.mip_level, tex.on_gpu);
|
||||
if (tex.on_gpu) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.3, 0.8, 0.3, 1.0));
|
||||
}
|
||||
if (ImGui::TreeNode(name.c_str())) {
|
||||
ImGui::Text("P: %s sz: %d x %d mip %d GPU? %d psm %d cpsm %d", tex.page_name.c_str(), tex.w,
|
||||
tex.h, tex.mip_level, tex.on_gpu, tex.psm, tex.cpsm);
|
||||
if (tex.on_gpu) {
|
||||
ImGui::Image((void*)tex.gpu_texture, ImVec2(tex.w, tex.h));
|
||||
} else {
|
||||
@@ -251,6 +375,11 @@ void TexturePool::draw_debug_for_tex(const std::string& name, TextureRecord& tex
|
||||
upload_to_gpu(&tex);
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
ImGui::Separator();
|
||||
}
|
||||
if (tex.on_gpu) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,10 +396,28 @@ void TexturePool::upload_to_gpu(TextureRecord* tex) {
|
||||
// we have to set these, imgui won't do it automatically
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex->gpu_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
tex->on_gpu = true;
|
||||
}
|
||||
|
||||
void TexturePool::remove_garbage_textures() {
|
||||
m_most_recent_gc_count = m_garbage_textures.size();
|
||||
m_most_recent_gc_count_gpu = 0;
|
||||
|
||||
for (auto& t : m_garbage_textures) {
|
||||
if (t->on_gpu) {
|
||||
m_most_recent_gc_count_gpu++;
|
||||
t->unload_from_gpu();
|
||||
}
|
||||
}
|
||||
m_garbage_textures.clear();
|
||||
}
|
||||
|
||||
void TexturePool::discard(std::shared_ptr<TextureRecord> tex) {
|
||||
assert(!tex->do_gc);
|
||||
m_garbage_textures.push_back(tex);
|
||||
}
|
||||
@@ -5,28 +5,108 @@
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "game/graphics/texture/TextureConverter.h"
|
||||
#include "common/util/Serializer.h"
|
||||
|
||||
struct TextureRecord {
|
||||
std::string page_name;
|
||||
std::string name;
|
||||
u8 mip_level;
|
||||
u8 psm = -1;
|
||||
u8 cpsm = -1;
|
||||
u16 w, h;
|
||||
std::vector<u8> data;
|
||||
u8 data_segment;
|
||||
u64 gpu_texture = 0;
|
||||
bool on_gpu = false;
|
||||
bool do_gc = true;
|
||||
std::vector<u8> data;
|
||||
u64 gpu_texture = 0;
|
||||
u32 dest = -1;
|
||||
|
||||
u8 min_a_zero, max_a_zero, min_a_nonzero, max_a_nonzero;
|
||||
|
||||
void unload_from_gpu();
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
struct TextureData {
|
||||
std::unique_ptr<TextureRecord> normal_texture;
|
||||
std::unique_ptr<TextureRecord> mt4hh_texture;
|
||||
std::shared_ptr<TextureRecord> normal_texture;
|
||||
std::shared_ptr<TextureRecord> mt4hh_texture;
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
struct GoalTexture {
|
||||
s16 w;
|
||||
s16 h;
|
||||
u8 num_mips;
|
||||
u8 tex1_control;
|
||||
u8 psm;
|
||||
u8 mip_shift;
|
||||
u16 clutpsm;
|
||||
u16 dest[7];
|
||||
u16 clut_dest;
|
||||
u8 width[7];
|
||||
u32 name_ptr;
|
||||
u32 size;
|
||||
float uv_dist;
|
||||
u32 masks[3];
|
||||
|
||||
s32 segment_of_mip(s32 mip) const {
|
||||
if (2 >= num_mips) {
|
||||
return num_mips - mip - 1;
|
||||
} else {
|
||||
return std::max(0, 2 - mip);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(GoalTexture) == 60, "GoalTexture size");
|
||||
static_assert(offsetof(GoalTexture, clutpsm) == 8);
|
||||
static_assert(offsetof(GoalTexture, clut_dest) == 24);
|
||||
|
||||
struct GoalTexturePage {
|
||||
struct Seg {
|
||||
u32 block_data_ptr;
|
||||
u32 size;
|
||||
u32 dest;
|
||||
};
|
||||
u32 file_info_ptr;
|
||||
u32 name_ptr;
|
||||
u32 id;
|
||||
s32 length; // texture count
|
||||
u32 mip0_size;
|
||||
u32 size;
|
||||
Seg segment[3];
|
||||
u32 pad[16];
|
||||
// start of array.
|
||||
|
||||
std::string print() const;
|
||||
|
||||
bool try_copy_texture_description(GoalTexture* dest,
|
||||
int idx,
|
||||
const u8* memory_base,
|
||||
const u8* tpage,
|
||||
u32 s7_ptr) {
|
||||
u32 ptr;
|
||||
memcpy(&ptr, tpage + sizeof(GoalTexturePage) + 4 * idx, 4);
|
||||
if (ptr == s7_ptr) {
|
||||
return false;
|
||||
}
|
||||
memcpy(dest, memory_base + ptr, sizeof(GoalTexture));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class TexturePool {
|
||||
public:
|
||||
void handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr);
|
||||
void set_texture(u32 location, std::unique_ptr<TextureRecord>&& record);
|
||||
void set_mt4hh_texture(u32 location, std::unique_ptr<TextureRecord>&& record);
|
||||
|
||||
std::vector<std::shared_ptr<TextureRecord>> convert_textures(const u8* tpage,
|
||||
int mode,
|
||||
const u8* memory_base,
|
||||
u32 s7_ptr);
|
||||
|
||||
void set_texture(u32 location, std::shared_ptr<TextureRecord> record);
|
||||
void draw_debug_window();
|
||||
TextureRecord* lookup(u32 location) {
|
||||
if (m_textures.at(location).normal_texture) {
|
||||
@@ -48,7 +128,13 @@ class TexturePool {
|
||||
|
||||
void relocate(u32 destination, u32 source, u32 format);
|
||||
|
||||
void remove_garbage_textures();
|
||||
void discard(std::shared_ptr<TextureRecord> tex);
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
|
||||
private:
|
||||
void unload_all_textures();
|
||||
void draw_debug_for_tex(const std::string& name, TextureRecord& tex);
|
||||
TextureConverter m_tex_converter;
|
||||
|
||||
@@ -57,5 +143,10 @@ class TexturePool {
|
||||
|
||||
// textures that the game overwrote, but may be still allocated on the GPU.
|
||||
// TODO: free these periodically.
|
||||
std::vector<std::unique_ptr<TextureRecord>> m_garbage_textures;
|
||||
std::vector<std::shared_ptr<TextureRecord>> m_garbage_textures;
|
||||
|
||||
char m_regex_input[256] = "";
|
||||
|
||||
int m_most_recent_gc_count = 0;
|
||||
int m_most_recent_gc_count_gpu = 0;
|
||||
};
|
||||
|
||||
+121
-2
@@ -137,7 +137,12 @@ _mips2c_call_linux:
|
||||
mov [rsp + 144], r9 ;; arg5
|
||||
mov [rsp + 160], r10 ;; arg6
|
||||
mov [rsp + 176], r11 ;; arg7
|
||||
mov [rsp + 464], rsp ;; mip2c code's MIPS stack
|
||||
mov [rsp + 352], r13 ;; s6 (pp)
|
||||
mov [rsp + 368], r14 ;; s7 (st)
|
||||
|
||||
mov rdi, rsp
|
||||
sub rdi, r15
|
||||
mov [rsp + 464], rdi ;; mip2c code's MIPS stack
|
||||
|
||||
mov rdi, rsp
|
||||
|
||||
@@ -205,7 +210,12 @@ _mips2c_call_windows:
|
||||
mov [rsp + 144], r9 ;; arg5
|
||||
mov [rsp + 160], r10 ;; arg6
|
||||
mov [rsp + 176], r11 ;; arg7
|
||||
mov [rsp + 464], rsp ;; mip2c code's MIPS stack
|
||||
mov [rsp + 352], r13 ;; s6 (pp)
|
||||
mov [rsp + 368], r14 ;; s7 (st)
|
||||
|
||||
mov rdi, rsp
|
||||
sub rdi, r15
|
||||
mov [rsp + 464], rdi ;; mip2c code's MIPS stack
|
||||
|
||||
mov rcx, rsp
|
||||
|
||||
@@ -343,6 +353,47 @@ _call_goal_asm_linux:
|
||||
pop r13
|
||||
ret
|
||||
|
||||
global _call_goal8_asm_linux
|
||||
|
||||
_call_goal8_asm_linux:
|
||||
;; x86 saved registers we need to modify for GOAL should be saved
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
;; RDI - first arg (func)
|
||||
;; RSI - second arg (arg array)
|
||||
;; RDX - third arg (0)
|
||||
;; RCX - pp (goes in r13)
|
||||
;; R8 - st (goes in r14)
|
||||
;; R9 - off (goes in r15)
|
||||
|
||||
;; set GOAL function pointer
|
||||
mov r13, rcx
|
||||
;; st
|
||||
mov r14, r8
|
||||
;; offset
|
||||
mov r15, r9
|
||||
;; move function to temp
|
||||
mov rax, rdi
|
||||
;; extract arguments
|
||||
mov rdi, [rsi + 0] ;; 0
|
||||
mov rdx, [rsi + 16] ;; 2
|
||||
mov rcx, [rsi + 24] ;; 3
|
||||
mov r8, [rsi + 32] ;; 4
|
||||
mov r9, [rsi + 40] ;; 5
|
||||
mov r10, [rsi + 48] ;; 6
|
||||
mov r11, [rsi + 56] ;; 7
|
||||
mov rsi, [rsi + 8] ;; 1 (do this last)
|
||||
;; call GOAL by function pointer
|
||||
call rax
|
||||
|
||||
;; retore x86 registers.
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
ret
|
||||
|
||||
;; Call goal, but switch stacks.
|
||||
global _call_goal_on_stack_asm_linux
|
||||
|
||||
@@ -448,6 +499,74 @@ _call_goal_asm_win32:
|
||||
|
||||
ret
|
||||
|
||||
global _call_goal8_asm_win32
|
||||
|
||||
_call_goal8_asm_win32:
|
||||
push rdx ; 8
|
||||
push rbx ; 16
|
||||
push rbp ; 24
|
||||
push rsi ; 32
|
||||
push rdi ; 40
|
||||
push r8 ; 48
|
||||
push r9 ; 56
|
||||
push r10 ; 64
|
||||
push r11 ; 72
|
||||
push r12 ; 80
|
||||
push r13 ; 88
|
||||
push r14 ; 96
|
||||
push r15 ; 104
|
||||
|
||||
sub rsp, 16
|
||||
movups [rsp], xmm6
|
||||
sub rsp, 16
|
||||
movups [rsp], xmm7
|
||||
|
||||
;; mov rdi, rcx ;; rdi is GOAL first argument, rcx is windows first argument
|
||||
;; mov rsi, rdx ;; rsi is GOAL second argument, rdx is windows second argument
|
||||
;; mov rdx, r8 ;; rdx is GOAL third argument, r8 is windows third argument
|
||||
;; mov r13, r9 ;; r13 is GOAL fp, r9 is windows fourth argument
|
||||
;; mov r15, [rsp + 184] ;; symbol table
|
||||
;; mov r14, [rsp + 176] ;; offset
|
||||
|
||||
;; call r13
|
||||
|
||||
mov r13, r9 ;; pp
|
||||
mov r15, [rsp + 184] ;; symbol table
|
||||
mov r14, [rsp + 176] ;; offset
|
||||
mov rax, rcx ;; func temp
|
||||
mov rsi, rdx ;; arg table
|
||||
mov rdi, [rsi + 0] ;; 0
|
||||
mov rdx, [rsi + 16] ;; 2
|
||||
mov rcx, [rsi + 24] ;; 3
|
||||
mov r8, [rsi + 32] ;; 4
|
||||
mov r9, [rsi + 40] ;; 5
|
||||
mov r10, [rsi + 48] ;; 6
|
||||
mov r11, [rsi + 56] ;; 7
|
||||
mov rsi, [rsi + 8] ;; 1 (do this last)
|
||||
;; call GOAL by function pointer
|
||||
call rax
|
||||
|
||||
movups xmm7, [rsp]
|
||||
add rsp, 16
|
||||
movups xmm6, [rsp]
|
||||
add rsp, 16
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
pop rdx
|
||||
|
||||
ret
|
||||
|
||||
global _call_goal_on_stack_asm_win32
|
||||
|
||||
_call_goal_on_stack_asm_win32:
|
||||
|
||||
@@ -748,6 +748,13 @@ void vif_interrupt_callback() {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Added in PC port.
|
||||
*/
|
||||
u32 offset_of_s7() {
|
||||
return s7.offset;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Final initialization of the system after the kernel is loaded.
|
||||
* This is called from InitHeapAndSymbol at the very end.
|
||||
|
||||
@@ -128,3 +128,4 @@ struct FileStream {
|
||||
// static_assert(offsetof(CpadInfo, new_pad) == 76, "cpad type offset");
|
||||
|
||||
void vif_interrupt_callback();
|
||||
u32 offset_of_s7();
|
||||
@@ -0,0 +1,689 @@
|
||||
// clang-format off
|
||||
//--------------------------MIPS2C---------------------
|
||||
#include "game/mips2c/mips2c_private.h"
|
||||
#include "game/kernel/kscheme.h"
|
||||
namespace Mips2C {
|
||||
namespace sp_process_block_3d {
|
||||
struct Cache {
|
||||
void* sp_frame_time; // *sp-frame-time*
|
||||
void* quaternion; // quaternion*!
|
||||
void* sp_free_particle; // sp-free-particle
|
||||
void* sp_relaunch_particle_3d; // sp-relaunch-particle-3d
|
||||
} cache;
|
||||
|
||||
u64 execute(void* ctxt) {
|
||||
auto* c = (ExecutionContext*)ctxt;
|
||||
bool bc = false;
|
||||
u32 call_addr = 0;
|
||||
bool cop1_bc = false;
|
||||
c->daddiu(sp, sp, -160); // daddiu sp, sp, -160
|
||||
c->sd(ra, 0, sp); // sd ra, 0(sp)
|
||||
c->sd(fp, 8, sp); // sd fp, 8(sp)
|
||||
c->mov64(fp, t9); // or fp, t9, r0
|
||||
c->sq(s0, 48, sp); // sq s0, 48(sp)
|
||||
c->sq(s1, 64, sp); // sq s1, 64(sp)
|
||||
c->sq(s2, 80, sp); // sq s2, 80(sp)
|
||||
c->sq(s3, 96, sp); // sq s3, 96(sp)
|
||||
c->sq(s4, 112, sp); // sq s4, 112(sp)
|
||||
c->sq(s5, 128, sp); // sq s5, 128(sp)
|
||||
c->sq(gp, 144, sp); // sq gp, 144(sp)
|
||||
c->mov64(gp, a0); // or gp, a0, r0
|
||||
c->mov64(s5, a1); // or s5, a1, r0
|
||||
c->mov64(s4, a2); // or s4, a2, r0
|
||||
c->mov64(s0, a3); // or s0, a3, r0
|
||||
c->mov64(s3, t0); // or s3, t0, r0
|
||||
c->mov64(s2, t1); // or s2, t1, r0
|
||||
c->daddiu(s1, sp, 16); // daddiu s1, sp, 16
|
||||
c->sq(r0, 0, s1); // sq r0, 0(s1)
|
||||
c->load_symbol(v1, cache.sp_frame_time); // lw v1, *sp-frame-time*(s7)
|
||||
c->lqc2(vf16, 0, v1); // lqc2 vf16, 0(v1)
|
||||
c->mov128_gpr_vf(v1, vf16); // qmfc2.i v1, vf16
|
||||
c->andi(v1, v1, 255); // andi v1, v1, 255
|
||||
c->sq(v1, 32, sp); // sq v1, 32(sp)
|
||||
// nop // sll r0, r0, 0
|
||||
|
||||
block_1:
|
||||
c->lw(v1, 128, s5); // lw v1, 128(s5)
|
||||
bc = c->sgpr64(v1) == c->sgpr64(s7); // beq v1, s7, L83
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_34;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(s2) == c->sgpr64(s7); // beq s2, s7, L71
|
||||
c->lw(v1, 104, s5); // lw v1, 104(s5)
|
||||
if (bc) {goto block_8;} // branch non-likely
|
||||
|
||||
c->andi(v1, v1, 8192); // andi v1, v1, 8192
|
||||
bc = c->sgpr64(v1) != 0; // bne v1, r0, L71
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_8;} // branch non-likely
|
||||
|
||||
c->lw(v1, 100, s5); // lw v1, 100(s5)
|
||||
c->addiu(a0, r0, -1); // addiu a0, r0, -1
|
||||
bc = c->sgpr64(v1) == c->sgpr64(a0); // beq v1, a0, L70
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_6;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L82
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_33;} // branch non-likely
|
||||
|
||||
|
||||
block_6:
|
||||
c->lw(a0, 104, s5); // lw a0, 104(s5)
|
||||
c->andi(v1, a0, 64); // andi v1, a0, 64
|
||||
c->xor_(a0, a0, v1); // xor a0, a0, v1
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L83
|
||||
c->sw(a0, 104, s5); // sw a0, 104(s5)
|
||||
if (bc) {goto block_34;} // branch non-likely
|
||||
|
||||
c->lw(v1, 124, s5); // lw v1, 124(s5)
|
||||
//beq r0, r0, L83 // beq r0, r0, L83
|
||||
c->sw(v1, 44, s4); // sw v1, 44(s4)
|
||||
goto block_34; // branch always
|
||||
|
||||
|
||||
block_8:
|
||||
c->lw(v1, 100, s5); // lw v1, 100(s5)
|
||||
c->addiu(a0, r0, -1); // addiu a0, r0, -1
|
||||
bc = c->sgpr64(v1) == c->sgpr64(a0); // beq v1, a0, L72
|
||||
c->lq(a0, 32, sp); // lq a0, 32(sp)
|
||||
if (bc) {goto block_11;} // branch non-likely
|
||||
|
||||
c->dsubu(a0, v1, a0); // dsubu a0, v1, a0
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L82
|
||||
c->pmaxw(v1, a0, r0); // pmaxw v1, a0, r0
|
||||
if (bc) {goto block_33;} // branch non-likely
|
||||
|
||||
c->sw(v1, 100, s5); // sw v1, 100(s5)
|
||||
|
||||
block_11:
|
||||
c->lw(a0, 104, s5); // lw a0, 104(s5)
|
||||
c->andi(v1, a0, 64); // andi v1, a0, 64
|
||||
c->xor_(a0, a0, v1); // xor a0, a0, v1
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L73
|
||||
c->sw(a0, 104, s5); // sw a0, 104(s5)
|
||||
if (bc) {goto block_13;} // branch non-likely
|
||||
|
||||
c->lw(v1, 124, s5); // lw v1, 124(s5)
|
||||
c->sw(v1, 44, s4); // sw v1, 44(s4)
|
||||
|
||||
block_13:
|
||||
c->lw(t9, 112, s5); // lw t9, 112(s5)
|
||||
bc = c->sgpr64(t9) == 0; // beq t9, r0, L74
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_15;} // branch non-likely
|
||||
|
||||
c->daddiu(sp, sp, -96); // daddiu sp, sp, -96
|
||||
c->sq(gp, 0, sp); // sq gp, 0(sp)
|
||||
c->sq(s5, 16, sp); // sq s5, 16(sp)
|
||||
c->sq(s4, 32, sp); // sq s4, 32(sp)
|
||||
c->sq(s0, 48, sp); // sq s0, 48(sp)
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a1, s5); // or a1, s5, r0
|
||||
c->mov64(a2, s4); // or a2, s4, r0
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sq(s2, 80, sp); // sq s2, 80(sp)
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
c->lq(gp, 0, sp); // lq gp, 0(sp)
|
||||
c->lq(s5, 16, sp); // lq s5, 16(sp)
|
||||
c->lq(s4, 32, sp); // lq s4, 32(sp)
|
||||
c->lq(s0, 48, sp); // lq s0, 48(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->lq(s2, 80, sp); // lq s2, 80(sp)
|
||||
c->daddiu(sp, sp, 96); // daddiu sp, sp, 96
|
||||
|
||||
block_15:
|
||||
c->lw(a1, 120, s5); // lw a1, 120(s5)
|
||||
c->lw(v1, 116, s5); // lw v1, 116(s5)
|
||||
bc = c->sgpr64(a1) == 0; // beq a1, r0, L75
|
||||
c->lq(a0, 32, sp); // lq a0, 32(sp)
|
||||
if (bc) {goto block_18;} // branch non-likely
|
||||
|
||||
c->dsubu(v1, v1, a0); // dsubu v1, v1, a0
|
||||
bc = ((s64)c->sgpr64(v1)) >= 0; // bgez v1, L75
|
||||
c->sw(v1, 116, s5); // sw v1, 116(s5)
|
||||
if (bc) {goto block_18;} // branch non-likely
|
||||
|
||||
c->daddiu(sp, sp, -96); // daddiu sp, sp, -96
|
||||
c->sq(gp, 0, sp); // sq gp, 0(sp)
|
||||
c->sq(s5, 16, sp); // sq s5, 16(sp)
|
||||
c->sq(s4, 32, sp); // sq s4, 32(sp)
|
||||
c->sq(s0, 48, sp); // sq s0, 48(sp)
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->sq(s2, 80, sp); // sq s2, 80(sp)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a3, s4); // or a3, s4, r0
|
||||
c->mov64(a2, s5); // or a2, s5, r0
|
||||
c->load_symbol(t9, cache.sp_relaunch_particle_3d);// lw t9, sp-relaunch-particle-3d(s7)
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
c->lq(gp, 0, sp); // lq gp, 0(sp)
|
||||
c->lq(s5, 16, sp); // lq s5, 16(sp)
|
||||
c->lq(s4, 32, sp); // lq s4, 32(sp)
|
||||
c->lq(s0, 48, sp); // lq s0, 48(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->lq(s2, 80, sp); // lq s2, 80(sp)
|
||||
c->daddiu(sp, sp, 96); // daddiu sp, sp, 96
|
||||
|
||||
block_18:
|
||||
c->lqc2(vf8, 0, s4); // lqc2 vf8, 0(s4)
|
||||
c->lqc2(vf9, 16, s4); // lqc2 vf9, 16(s4)
|
||||
c->lqc2(vf10, 32, s4); // lqc2 vf10, 32(s4)
|
||||
c->lqc2(vf11, 16, s5); // lqc2 vf11, 16(s5)
|
||||
c->lqc2(vf12, 32, s5); // lqc2 vf12, 32(s5)
|
||||
c->lqc2(vf13, 48, s5); // lqc2 vf13, 48(s5)
|
||||
c->lqc2(vf14, 64, s5); // lqc2 vf14, 64(s5)
|
||||
c->lwc1(f0, 96, s5); // lwc1 f0, 96(s5)
|
||||
c->mfc1(v1, f0); // mfc1 v1, f0
|
||||
c->vmul_bc(DEST::xyzw, BC::z, vf14, vf14, vf16); // vmulz.xyzw vf14, vf14, vf16
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L76
|
||||
c->vadd(DEST::xyz, vf11, vf11, vf14); // vadd.xyz vf11, vf11, vf14
|
||||
if (bc) {goto block_20;} // branch non-likely
|
||||
|
||||
c->mov128_vf_gpr(vf15, v1); // qmtc2.i vf15, v1
|
||||
c->vsub_bc(DEST::w, BC::x, vf15, vf0, vf15); // vsubx.w vf15, vf0, vf15
|
||||
c->vmul_bc(DEST::xyzw, BC::w, vf15, vf15, vf16); // vmulw.xyzw vf15, vf15, vf16
|
||||
c->vsub_bc(DEST::w, BC::w, vf15, vf0, vf15); // vsubw.w vf15, vf0, vf15
|
||||
c->vmul_bc(DEST::xyz, BC::w, vf11, vf11, vf15); // vmulw.xyz vf11, vf11, vf15
|
||||
|
||||
block_20:
|
||||
c->vmul_bc(DEST::xyzw, BC::y, vf17, vf11, vf16); // vmuly.xyzw vf17, vf11, vf16
|
||||
c->vmul_bc(DEST::xyzw, BC::y, vf18, vf12, vf16); // vmuly.xyzw vf18, vf12, vf16
|
||||
c->vmul_bc(DEST::xyzw, BC::y, vf19, vf13, vf16); // vmuly.xyzw vf19, vf13, vf16
|
||||
c->vadd(DEST::xyzw, vf8, vf8, vf17); // vadd.xyzw vf8, vf8, vf17
|
||||
c->vadd_bc(DEST::w, BC::w, vf9, vf9, vf18); // vaddw.w vf9, vf9, vf18
|
||||
c->vadd(DEST::xyzw, vf10, vf10, vf19); // vadd.xyzw vf10, vf10, vf19
|
||||
c->vmax_bc(DEST::xyzw, BC::x, vf10, vf10, vf0); // vmaxx.xyzw vf10, vf10, vf0
|
||||
c->sqc2(vf11, 16, s5); // sqc2 vf11, 16(s5)
|
||||
c->sqc2(vf8, 0, s4); // sqc2 vf8, 0(s4)
|
||||
c->sqc2(vf9, 16, s4); // sqc2 vf9, 16(s4)
|
||||
c->sqc2(vf10, 32, s4); // sqc2 vf10, 32(s4)
|
||||
c->mov64(v1, s1); // or v1, s1, r0
|
||||
c->mov64(a0, s4); // or a0, s4, r0
|
||||
c->lwc1(f0, 16, a0); // lwc1 f0, 16(a0)
|
||||
c->lwc1(f1, 20, a0); // lwc1 f1, 20(a0)
|
||||
c->lwc1(f3, 24, a0); // lwc1 f3, 24(a0)
|
||||
c->swc1(f0, 0, v1); // swc1 f0, 0(v1)
|
||||
c->swc1(f1, 4, v1); // swc1 f1, 4(v1)
|
||||
c->swc1(f3, 8, v1); // swc1 f3, 8(v1)
|
||||
c->fprs[f2] = 1.0; // lwc1 f2, L157(fp)
|
||||
c->muls(f3, f3, f3); // mul.s f3, f3, f3
|
||||
c->subs(f2, f2, f3); // sub.s f2, f2, f3
|
||||
c->muls(f1, f1, f1); // mul.s f1, f1, f1
|
||||
c->subs(f1, f2, f1); // sub.s f1, f2, f1
|
||||
c->muls(f0, f0, f0); // mul.s f0, f0, f0
|
||||
c->subs(f0, f1, f0); // sub.s f0, f1, f0
|
||||
c->sqrts(f0, f0); // sqrt.s f0, f0
|
||||
c->swc1(f0, 12, v1); // swc1 f0, 12(v1)
|
||||
c->mfc1(a0, f0); // mfc1 a0, f0
|
||||
c->load_symbol(v1, cache.sp_frame_time); // lw v1, *sp-frame-time*(s7)
|
||||
c->lwc1(f0, 0, v1); // lwc1 f0, 0(v1)
|
||||
c->mfc1(v1, f0); // mfc1 v1, f0
|
||||
c->andi(v1, v1, 255); // andi v1, v1, 255
|
||||
c->daddiu(v1, v1, -10); // daddiu v1, v1, -10
|
||||
bc = ((s64)c->sgpr64(v1)) < 0; // bltz v1, L77
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_22;} // branch non-likely
|
||||
|
||||
c->load_symbol(t9, cache.quaternion); // lw t9, quaternion*!(s7)
|
||||
c->mov64(a0, s1); // or a0, s1, r0
|
||||
c->mov64(a1, s1); // or a1, s1, r0
|
||||
c->daddiu(a2, s5, 80); // daddiu a2, s5, 80
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
|
||||
block_22:
|
||||
c->load_symbol(t9, cache.quaternion); // lw t9, quaternion*!(s7)
|
||||
c->mov64(a0, s1); // or a0, s1, r0
|
||||
c->mov64(a1, s1); // or a1, s1, r0
|
||||
c->daddiu(a2, s5, 80); // daddiu a2, s5, 80
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
c->mov64(a0, s4); // or a0, s4, r0
|
||||
c->mov64(v1, s1); // or v1, s1, r0
|
||||
c->lwc1(f0, 12, v1); // lwc1 f0, 12(v1)
|
||||
c->mtc1(f1, r0); // mtc1 f1, r0
|
||||
cop1_bc = c->fprs[f0] < c->fprs[f1]; // c.lt.s f0, f1
|
||||
bc = !cop1_bc; // bc1f L78
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_24;} // branch non-likely
|
||||
|
||||
c->lqc2(vf1, 16, a0); // lqc2 vf1, 16(a0)
|
||||
c->lqc2(vf2, 0, v1); // lqc2 vf2, 0(v1)
|
||||
c->vsub(DEST::xyz, vf1, vf0, vf2); // vsub.xyz vf1, vf0, vf2
|
||||
c->sqc2(vf1, 16, a0); // sqc2 vf1, 16(a0)
|
||||
c->mov128_gpr_vf(a0, vf1); // qmfc2.i a0, vf1
|
||||
//beq r0, r0, L79 // beq r0, r0, L79
|
||||
// nop // sll r0, r0, 0
|
||||
goto block_25; // branch always
|
||||
|
||||
|
||||
block_24:
|
||||
c->lqc2(vf1, 16, a0); // lqc2 vf1, 16(a0)
|
||||
c->lqc2(vf2, 0, v1); // lqc2 vf2, 0(v1)
|
||||
c->vadd(DEST::xyz, vf1, vf0, vf2); // vadd.xyz vf1, vf0, vf2
|
||||
c->sqc2(vf1, 16, a0); // sqc2 vf1, 16(a0)
|
||||
c->mov128_gpr_vf(a0, vf1); // qmfc2.i a0, vf1
|
||||
|
||||
block_25:
|
||||
c->mov128_gpr_vf(v1, vf10); // qmfc2.i v1, vf10
|
||||
c->lw(a0, 104, s5); // lw a0, 104(s5)
|
||||
c->andi(a1, a0, 2); // andi a1, a0, 2
|
||||
bc = c->sgpr64(a1) == 0; // beq a1, r0, L80
|
||||
c->andi(a1, a0, 4); // andi a1, a0, 4
|
||||
if (bc) {goto block_28;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(v1) != 0; // bne v1, r0, L80
|
||||
c->pextuw(a2, v1, r0); // pextuw a2, v1, r0
|
||||
if (bc) {goto block_28;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(a2) == 0; // beq a2, r0, L82
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_33;} // branch non-likely
|
||||
|
||||
|
||||
block_28:
|
||||
bc = c->sgpr64(a1) == 0; // beq a1, r0, L81
|
||||
c->andi(a0, a0, 1); // andi a0, a0, 1
|
||||
if (bc) {goto block_30;} // branch non-likely
|
||||
|
||||
c->pcpyud(v1, v1, r0); // pcpyud v1, v1, r0
|
||||
c->pexew(v1, v1); // pexew v1, v1
|
||||
bc = ((s64)c->sgpr64(v1)) <= 0; // blez v1, L82
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_33;} // branch non-likely
|
||||
|
||||
|
||||
block_30:
|
||||
bc = c->sgpr64(a0) == 0; // beq a0, r0, L83
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_34;} // branch non-likely
|
||||
|
||||
c->mov128_gpr_vf(v1, vf8); // qmfc2.i v1, vf8
|
||||
c->pcpyud(v1, v1, r0); // pcpyud v1, v1, r0
|
||||
c->pexew(v1, v1); // pexew v1, v1
|
||||
bc = ((s64)c->sgpr64(v1)) < 0; // bltz v1, L82
|
||||
c->mov128_gpr_vf(v1, vf9); // qmfc2.i v1, vf9
|
||||
if (bc) {goto block_33;} // branch non-likely
|
||||
|
||||
c->pcpyud(v1, v1, r0); // pcpyud v1, v1, r0
|
||||
c->pexew(v1, v1); // pexew v1, v1
|
||||
bc = ((s64)c->sgpr64(v1)) >= 0; // bgez v1, L83
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_34;} // branch non-likely
|
||||
|
||||
|
||||
block_33:
|
||||
c->load_symbol(t9, cache.sp_free_particle); // lw t9, sp-free-particle(s7)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a1, s0); // or a1, s0, r0
|
||||
c->mov64(a2, s5); // or a2, s5, r0
|
||||
c->mov64(a3, s4); // or a3, s4, r0
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
|
||||
block_34:
|
||||
c->daddiu(s3, s3, -1); // daddiu s3, s3, -1
|
||||
c->daddiu(s5, s5, 144); // daddiu s5, s5, 144
|
||||
c->daddiu(s4, s4, 48); // daddiu s4, s4, 48
|
||||
bc = c->sgpr64(s3) != 0; // bne s3, r0, L69
|
||||
c->daddiu(s0, s0, 1); // daddiu s0, s0, 1
|
||||
if (bc) {goto block_1;} // branch non-likely
|
||||
|
||||
c->mov64(v0, s0); // or v0, s0, r0
|
||||
c->ld(ra, 0, sp); // ld ra, 0(sp)
|
||||
c->ld(fp, 8, sp); // ld fp, 8(sp)
|
||||
c->lq(gp, 144, sp); // lq gp, 144(sp)
|
||||
c->lq(s5, 128, sp); // lq s5, 128(sp)
|
||||
c->lq(s4, 112, sp); // lq s4, 112(sp)
|
||||
c->lq(s3, 96, sp); // lq s3, 96(sp)
|
||||
c->lq(s2, 80, sp); // lq s2, 80(sp)
|
||||
c->lq(s1, 64, sp); // lq s1, 64(sp)
|
||||
c->lq(s0, 48, sp); // lq s0, 48(sp)
|
||||
//jr ra // jr ra
|
||||
c->daddiu(sp, sp, 160); // daddiu sp, sp, 160
|
||||
goto end_of_function; // return
|
||||
|
||||
// nop // sll r0, r0, 0
|
||||
// nop // sll r0, r0, 0
|
||||
// nop // sll r0, r0, 0
|
||||
end_of_function:
|
||||
return c->gprs[v0].du64[0];
|
||||
}
|
||||
|
||||
void link() {
|
||||
cache.sp_frame_time = intern_from_c("*sp-frame-time*").c();
|
||||
cache.quaternion = intern_from_c("quaternion*!").c();
|
||||
cache.sp_free_particle = intern_from_c("sp-free-particle").c();
|
||||
cache.sp_relaunch_particle_3d = intern_from_c("sp-relaunch-particle-3d").c();
|
||||
gLinkedFunctionTable.reg("sp-process-block-3d", execute, 256);
|
||||
}
|
||||
|
||||
} // namespace sp_process_block_3d
|
||||
} // namespace Mips2C
|
||||
|
||||
//--------------------------MIPS2C---------------------
|
||||
#include "game/mips2c/mips2c_private.h"
|
||||
#include "game/kernel/kscheme.h"
|
||||
namespace Mips2C {
|
||||
namespace sp_process_block_2d {
|
||||
struct Cache {
|
||||
void* sp_frame_time; // *sp-frame-time*
|
||||
void* sp_free_particle; // sp-free-particle
|
||||
void* sp_orbiter; // sp-orbiter
|
||||
void* sp_relaunch_particle_2d; // sp-relaunch-particle-2d
|
||||
} cache;
|
||||
|
||||
u64 execute(void* ctxt) {
|
||||
auto* c = (ExecutionContext*)ctxt;
|
||||
bool bc = false;
|
||||
u32 call_addr = 0;
|
||||
c->daddiu(sp, sp, -128); // daddiu sp, sp, -128
|
||||
c->sd(ra, 0, sp); // sd ra, 0(sp)
|
||||
c->sq(s0, 16, sp); // sq s0, 16(sp)
|
||||
c->sq(s1, 32, sp); // sq s1, 32(sp)
|
||||
c->sq(s2, 48, sp); // sq s2, 48(sp)
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->sq(s4, 80, sp); // sq s4, 80(sp)
|
||||
c->sq(s5, 96, sp); // sq s5, 96(sp)
|
||||
c->sq(gp, 112, sp); // sq gp, 112(sp)
|
||||
c->mov64(gp, a0); // or gp, a0, r0
|
||||
c->mov64(s5, a1); // or s5, a1, r0
|
||||
c->mov64(s4, a2); // or s4, a2, r0
|
||||
c->mov64(s1, a3); // or s1, a3, r0
|
||||
c->mov64(s3, t0); // or s3, t0, r0
|
||||
c->mov64(s2, t1); // or s2, t1, r0
|
||||
c->load_symbol(v1, cache.sp_frame_time); // lw v1, *sp-frame-time*(s7)
|
||||
c->lqc2(vf9, 0, v1); // lqc2 vf9, 0(v1)
|
||||
c->mov128_gpr_vf(v1, vf9); // qmfc2.i v1, vf9
|
||||
c->andi(s0, v1, 255); // andi s0, v1, 255
|
||||
|
||||
block_1:
|
||||
c->lw(v1, 128, s5); // lw v1, 128(s5)
|
||||
bc = c->sgpr64(v1) == c->sgpr64(s7); // beq v1, s7, L97
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_31;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(s2) == c->sgpr64(s7); // beq s2, s7, L87
|
||||
c->lw(v1, 104, s5); // lw v1, 104(s5)
|
||||
if (bc) {goto block_8;} // branch non-likely
|
||||
|
||||
c->andi(v1, v1, 8192); // andi v1, v1, 8192
|
||||
bc = c->sgpr64(v1) != 0; // bne v1, r0, L87
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_8;} // branch non-likely
|
||||
|
||||
c->lw(v1, 100, s5); // lw v1, 100(s5)
|
||||
c->addiu(a0, r0, -1); // addiu a0, r0, -1
|
||||
bc = c->sgpr64(v1) == c->sgpr64(a0); // beq v1, a0, L86
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_6;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L96
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_30;} // branch non-likely
|
||||
|
||||
|
||||
block_6:
|
||||
c->lw(a0, 104, s5); // lw a0, 104(s5)
|
||||
c->andi(v1, a0, 64); // andi v1, a0, 64
|
||||
c->xor_(a0, a0, v1); // xor a0, a0, v1
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L97
|
||||
c->sw(a0, 104, s5); // sw a0, 104(s5)
|
||||
if (bc) {goto block_31;} // branch non-likely
|
||||
|
||||
c->lw(v1, 124, s5); // lw v1, 124(s5)
|
||||
//beq r0, r0, L97 // beq r0, r0, L97
|
||||
c->sw(v1, 44, s4); // sw v1, 44(s4)
|
||||
goto block_31; // branch always
|
||||
|
||||
|
||||
block_8:
|
||||
c->lw(v1, 100, s5); // lw v1, 100(s5)
|
||||
c->addiu(a0, r0, -1); // addiu a0, r0, -1
|
||||
bc = c->sgpr64(v1) == c->sgpr64(a0); // beq v1, a0, L88
|
||||
c->dsubu(a0, v1, s0); // dsubu a0, v1, s0
|
||||
if (bc) {goto block_11;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L96
|
||||
c->pmaxw(v1, a0, r0); // pmaxw v1, a0, r0
|
||||
if (bc) {goto block_30;} // branch non-likely
|
||||
|
||||
c->sw(v1, 100, s5); // sw v1, 100(s5)
|
||||
|
||||
block_11:
|
||||
c->lw(a0, 104, s5); // lw a0, 104(s5)
|
||||
c->andi(v1, a0, 64); // andi v1, a0, 64
|
||||
c->xor_(a0, a0, v1); // xor a0, a0, v1
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L89
|
||||
c->sw(a0, 104, s5); // sw a0, 104(s5)
|
||||
if (bc) {goto block_13;} // branch non-likely
|
||||
|
||||
c->lw(v1, 124, s5); // lw v1, 124(s5)
|
||||
c->sw(v1, 44, s4); // sw v1, 44(s4)
|
||||
|
||||
block_13:
|
||||
c->lw(t9, 112, s5); // lw t9, 112(s5)
|
||||
bc = c->sgpr64(t9) == 0; // beq t9, r0, L90
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_15;} // branch non-likely
|
||||
|
||||
c->daddiu(sp, sp, -80); // daddiu sp, sp, -80
|
||||
c->sq(gp, 0, sp); // sq gp, 0(sp)
|
||||
c->sq(s5, 16, sp); // sq s5, 16(sp)
|
||||
c->sq(s4, 32, sp); // sq s4, 32(sp)
|
||||
c->sq(s1, 48, sp); // sq s1, 48(sp)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a1, s5); // or a1, s5, r0
|
||||
c->mov64(a2, s4); // or a2, s4, r0
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
c->lq(gp, 0, sp); // lq gp, 0(sp)
|
||||
c->lq(s5, 16, sp); // lq s5, 16(sp)
|
||||
c->lq(s4, 32, sp); // lq s4, 32(sp)
|
||||
c->lq(s1, 48, sp); // lq s1, 48(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->daddiu(sp, sp, 80); // daddiu sp, sp, 80
|
||||
|
||||
block_15:
|
||||
c->lw(a1, 120, s5); // lw a1, 120(s5)
|
||||
c->lw(v1, 116, s5); // lw v1, 116(s5)
|
||||
bc = c->sgpr64(a1) == 0; // beq a1, r0, L91
|
||||
c->dsubu(a0, v1, s0); // dsubu a0, v1, s0
|
||||
if (bc) {goto block_18;} // branch non-likely
|
||||
|
||||
c->daddiu(v1, a0, -1); // daddiu v1, a0, -1
|
||||
bc = ((s64)c->sgpr64(v1)) >= 0; // bgez v1, L91
|
||||
c->sw(a0, 116, s5); // sw a0, 116(s5)
|
||||
if (bc) {goto block_18;} // branch non-likely
|
||||
|
||||
c->daddiu(sp, sp, -96); // daddiu sp, sp, -96
|
||||
c->sq(gp, 0, sp); // sq gp, 0(sp)
|
||||
c->sq(s5, 16, sp); // sq s5, 16(sp)
|
||||
c->sq(s4, 32, sp); // sq s4, 32(sp)
|
||||
c->sq(s1, 48, sp); // sq s1, 48(sp)
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->sq(s2, 80, sp); // sq s2, 80(sp)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a3, s4); // or a3, s4, r0
|
||||
c->mov64(a2, s5); // or a2, s5, r0
|
||||
c->load_symbol(t9, cache.sp_relaunch_particle_2d);// lw t9, sp-relaunch-particle-2d(s7)
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
c->lq(gp, 0, sp); // lq gp, 0(sp)
|
||||
c->lq(s5, 16, sp); // lq s5, 16(sp)
|
||||
c->lq(s4, 32, sp); // lq s4, 32(sp)
|
||||
c->lq(s1, 48, sp); // lq s1, 48(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->lq(s2, 80, sp); // lq s2, 80(sp)
|
||||
c->daddiu(sp, sp, 96); // daddiu sp, sp, 96
|
||||
|
||||
block_18:
|
||||
c->lqc2(vf1, 0, s4); // lqc2 vf1, 0(s4)
|
||||
c->lqc2(vf2, 16, s4); // lqc2 vf2, 16(s4)
|
||||
c->lqc2(vf3, 32, s4); // lqc2 vf3, 32(s4)
|
||||
c->lqc2(vf4, 16, s5); // lqc2 vf4, 16(s5)
|
||||
c->lqc2(vf5, 32, s5); // lqc2 vf5, 32(s5)
|
||||
c->lqc2(vf6, 48, s5); // lqc2 vf6, 48(s5)
|
||||
c->lqc2(vf7, 64, s5); // lqc2 vf7, 64(s5)
|
||||
c->lwc1(f0, 96, s5); // lwc1 f0, 96(s5)
|
||||
c->mfc1(v1, f0); // mfc1 v1, f0
|
||||
c->vmul_bc(DEST::xyzw, BC::z, vf7, vf7, vf9); // vmulz.xyzw vf7, vf7, vf9
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L92
|
||||
c->vadd(DEST::xyz, vf4, vf4, vf7); // vadd.xyz vf4, vf4, vf7
|
||||
if (bc) {goto block_20;} // branch non-likely
|
||||
|
||||
c->mov128_vf_gpr(vf8, v1); // qmtc2.i vf8, v1
|
||||
c->vsub_bc(DEST::w, BC::x, vf8, vf0, vf8); // vsubx.w vf8, vf0, vf8
|
||||
c->vmul_bc(DEST::xyzw, BC::w, vf8, vf8, vf9); // vmulw.xyzw vf8, vf8, vf9
|
||||
c->vsub_bc(DEST::w, BC::w, vf8, vf0, vf8); // vsubw.w vf8, vf0, vf8
|
||||
c->vmul_bc(DEST::xyz, BC::w, vf4, vf4, vf8); // vmulw.xyz vf4, vf4, vf8
|
||||
|
||||
block_20:
|
||||
c->vmul_bc(DEST::xyzw, BC::y, vf10, vf4, vf9); // vmuly.xyzw vf10, vf4, vf9
|
||||
c->vmul_bc(DEST::xyzw, BC::y, vf11, vf5, vf9); // vmuly.xyzw vf11, vf5, vf9
|
||||
c->vmul_bc(DEST::xyzw, BC::y, vf12, vf6, vf9); // vmuly.xyzw vf12, vf6, vf9
|
||||
c->vadd(DEST::xyzw, vf1, vf1, vf10); // vadd.xyzw vf1, vf1, vf10
|
||||
c->vadd(DEST::zw, vf2, vf2, vf11); // vadd.zw vf2, vf2, vf11
|
||||
c->vadd(DEST::xyzw, vf3, vf3, vf12); // vadd.xyzw vf3, vf3, vf12
|
||||
c->vmax_bc(DEST::xyzw, BC::x, vf3, vf3, vf0); // vmaxx.xyzw vf3, vf3, vf0
|
||||
c->sqc2(vf4, 16, s5); // sqc2 vf4, 16(s5)
|
||||
c->sqc2(vf1, 0, s4); // sqc2 vf1, 0(s4)
|
||||
c->sqc2(vf2, 16, s4); // sqc2 vf2, 16(s4)
|
||||
c->sqc2(vf3, 32, s4); // sqc2 vf3, 32(s4)
|
||||
c->lwc1(f0, 24, s4); // lwc1 f0, 24(s4)
|
||||
c->cvtws(f0, f0); // cvt.w.s f0, f0
|
||||
c->mfc1(v1, f0); // mfc1 v1, f0
|
||||
c->dsll32(v1, v1, 16); // dsll32 v1, v1, 16
|
||||
c->dsra32(v1, v1, 16); // dsra32 v1, v1, 16
|
||||
c->mtc1(f0, v1); // mtc1 f0, v1
|
||||
c->cvtsw(f0, f0); // cvt.s.w f0, f0
|
||||
c->swc1(f0, 24, s4); // swc1 f0, 24(s4)
|
||||
c->lw(v1, 104, s5); // lw v1, 104(s5)
|
||||
c->andi(v1, v1, 128); // andi v1, v1, 128
|
||||
bc = c->sgpr64(v1) == 0; // beq v1, r0, L93
|
||||
c->load_symbol(t9, cache.sp_orbiter); // lw t9, sp-orbiter(s7)
|
||||
if (bc) {goto block_22;} // branch non-likely
|
||||
|
||||
c->daddiu(sp, sp, -96); // daddiu sp, sp, -96
|
||||
c->sq(gp, 0, sp); // sq gp, 0(sp)
|
||||
c->sq(s5, 16, sp); // sq s5, 16(sp)
|
||||
c->sq(s4, 32, sp); // sq s4, 32(sp)
|
||||
c->sq(s1, 48, sp); // sq s1, 48(sp)
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a1, s5); // or a1, s5, r0
|
||||
c->mov64(a2, s4); // or a2, s4, r0
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sq(s2, 80, sp); // sq s2, 80(sp)
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
c->lq(gp, 0, sp); // lq gp, 0(sp)
|
||||
c->lq(s5, 16, sp); // lq s5, 16(sp)
|
||||
c->lq(s4, 32, sp); // lq s4, 32(sp)
|
||||
c->lq(s1, 48, sp); // lq s1, 48(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->lq(s2, 80, sp); // lq s2, 80(sp)
|
||||
c->daddiu(sp, sp, 96); // daddiu sp, sp, 96
|
||||
|
||||
block_22:
|
||||
c->lq(v1, 32, s4); // lq v1, 32(s4)
|
||||
c->lw(a0, 104, s5); // lw a0, 104(s5)
|
||||
c->andi(a1, a0, 2); // andi a1, a0, 2
|
||||
bc = c->sgpr64(a1) == 0; // beq a1, r0, L94
|
||||
c->andi(a1, a0, 4); // andi a1, a0, 4
|
||||
if (bc) {goto block_25;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(v1) != 0; // bne v1, r0, L94
|
||||
c->pextuw(t4, v1, r0); // pextuw t4, v1, r0
|
||||
if (bc) {goto block_25;} // branch non-likely
|
||||
|
||||
bc = c->sgpr64(t4) == 0; // beq t4, r0, L96
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_30;} // branch non-likely
|
||||
|
||||
|
||||
block_25:
|
||||
bc = c->sgpr64(a1) == 0; // beq a1, r0, L95
|
||||
c->andi(a0, a0, 1); // andi a0, a0, 1
|
||||
if (bc) {goto block_27;} // branch non-likely
|
||||
|
||||
c->pcpyud(t4, v1, r0); // pcpyud t4, v1, r0
|
||||
c->pexew(t4, t4); // pexew t4, r0, t4
|
||||
bc = ((s64)c->sgpr64(t4)) <= 0; // blez t4, L96
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_30;} // branch non-likely
|
||||
|
||||
|
||||
block_27:
|
||||
bc = c->sgpr64(a0) == 0; // beq a0, r0, L97
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_31;} // branch non-likely
|
||||
|
||||
c->mov128_gpr_vf(v1, vf1); // qmfc2.i v1, vf1
|
||||
c->pcpyud(v1, v1, r0); // pcpyud v1, v1, r0
|
||||
c->pexew(v1, v1); // pexew v1, r0, v1
|
||||
bc = ((s64)c->sgpr64(v1)) < 0; // bltz v1, L96
|
||||
c->mov128_gpr_vf(v1, vf2); // qmfc2.i v1, vf2
|
||||
if (bc) {goto block_30;} // branch non-likely
|
||||
|
||||
c->pcpyud(v1, v1, r0); // pcpyud v1, v1, r0
|
||||
c->pexew(v1, v1); // pexew v1, r0, v1
|
||||
bc = ((s64)c->sgpr64(v1)) >= 0; // bgez v1, L97
|
||||
// nop // sll r0, r0, 0
|
||||
if (bc) {goto block_31;} // branch non-likely
|
||||
|
||||
block_30:
|
||||
c->load_symbol(t9, cache.sp_free_particle); // lw t9, sp-free-particle(s7)
|
||||
c->mov64(a0, gp); // or a0, gp, r0
|
||||
c->mov64(a1, s1); // or a1, s1, r0
|
||||
c->mov64(a2, s5); // or a2, s5, r0
|
||||
c->mov64(a3, s4); // or a3, s4, r0
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
|
||||
block_31:
|
||||
c->daddiu(s3, s3, -1); // daddiu s3, s3, -1
|
||||
c->daddiu(s5, s5, 144); // daddiu s5, s5, 144
|
||||
c->daddiu(s4, s4, 48); // daddiu s4, s4, 48
|
||||
bc = c->sgpr64(s3) != 0; // bne s3, r0, L85
|
||||
c->daddiu(s1, s1, 1); // daddiu s1, s1, 1
|
||||
if (bc) {goto block_1;} // branch non-likely
|
||||
|
||||
c->mov64(v0, s1); // or v0, s1, r0
|
||||
c->ld(ra, 0, sp); // ld ra, 0(sp)
|
||||
c->lq(gp, 112, sp); // lq gp, 112(sp)
|
||||
c->lq(s5, 96, sp); // lq s5, 96(sp)
|
||||
c->lq(s4, 80, sp); // lq s4, 80(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->lq(s2, 48, sp); // lq s2, 48(sp)
|
||||
c->lq(s1, 32, sp); // lq s1, 32(sp)
|
||||
c->lq(s0, 16, sp); // lq s0, 16(sp)
|
||||
//jr ra // jr ra
|
||||
c->daddiu(sp, sp, 128); // daddiu sp, sp, 128
|
||||
goto end_of_function; // return
|
||||
|
||||
// nop // sll r0, r0, 0
|
||||
// nop // sll r0, r0, 0
|
||||
// nop // sll r0, r0, 0
|
||||
end_of_function:
|
||||
return c->gprs[v0].du64[0];
|
||||
}
|
||||
|
||||
void link() {
|
||||
cache.sp_frame_time = intern_from_c("*sp-frame-time*").c();
|
||||
cache.sp_free_particle = intern_from_c("sp-free-particle").c();
|
||||
cache.sp_orbiter = intern_from_c("sp-orbiter").c();
|
||||
cache.sp_relaunch_particle_2d = intern_from_c("sp-relaunch-particle-2d").c();
|
||||
gLinkedFunctionTable.reg("sp-process-block-2d", execute, 256);
|
||||
}
|
||||
|
||||
} // namespace sp_process_block_2d
|
||||
} // namespace Mips2C
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,4 +15,54 @@ u64 execute(void* ctxt) {
|
||||
return 0;
|
||||
}
|
||||
} // namespace test_func
|
||||
} // namespace Mips2C
|
||||
} // namespace Mips2C
|
||||
|
||||
//--------------------------MIPS2C---------------------
|
||||
#include "game/mips2c/mips2c_private.h"
|
||||
#include "game/kernel/kscheme.h"
|
||||
#include "game/kernel/kprint.h"
|
||||
namespace Mips2C {
|
||||
namespace goal_call_test {
|
||||
struct Cache {
|
||||
void* goal_check_function;
|
||||
} cache;
|
||||
|
||||
u64 execute(void* ctxt) {
|
||||
auto* c = (ExecutionContext*)ctxt;
|
||||
u32 call_addr = 0;
|
||||
c->daddiu(sp, sp, -128); // daddiu sp, sp, -128
|
||||
c->sd(ra, 0, sp); // sd ra, 0(sp)
|
||||
c->sq(s0, 16, sp); // sq s0, 16(sp)
|
||||
c->sq(s1, 32, sp); // sq s1, 32(sp)
|
||||
c->sq(s2, 48, sp); // sq s2, 48(sp)
|
||||
c->sq(s3, 64, sp); // sq s3, 64(sp)
|
||||
c->sq(s4, 80, sp); // sq s4, 80(sp)
|
||||
c->sq(s5, 96, sp); // sq s5, 96(sp)
|
||||
c->sq(gp, 112, sp); // sq gp, 112(sp)
|
||||
|
||||
c->load_symbol(t9, cache.goal_check_function);
|
||||
call_addr = c->gprs[t9].du32[0]; // function call:
|
||||
c->sll(v0, ra, 0); // sll v0, ra, 0
|
||||
c->jalr(call_addr); // jalr ra, t9
|
||||
|
||||
c->ld(ra, 0, sp); // ld ra, 0(sp)
|
||||
c->lq(gp, 112, sp); // lq gp, 112(sp)
|
||||
c->lq(s5, 96, sp); // lq s5, 96(sp)
|
||||
c->lq(s4, 80, sp); // lq s4, 80(sp)
|
||||
c->lq(s3, 64, sp); // lq s3, 64(sp)
|
||||
c->lq(s2, 48, sp); // lq s2, 48(sp)
|
||||
c->lq(s1, 32, sp); // lq s1, 32(sp)
|
||||
c->lq(s0, 16, sp); // lq s0, 16(sp)
|
||||
c->daddiu(sp, sp, 128); // daddiu sp, sp, 128
|
||||
goto end_of_function; // return
|
||||
|
||||
end_of_function:
|
||||
return c->gprs[v0].du64[0];
|
||||
}
|
||||
|
||||
void link() {
|
||||
cache.goal_check_function = intern_from_c("goal_check_function").c();
|
||||
}
|
||||
|
||||
} // namespace goal_call_test
|
||||
} // namespace Mips2C
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "game/mips2c/mips2c_table.h"
|
||||
#include "common/util/assert.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
// This file contains utility functions for code generated by the mips2c pass.
|
||||
// This is only useful for
|
||||
|
||||
extern u8* g_ee_main_mem;
|
||||
|
||||
extern "C" {
|
||||
u64 _call_goal8_asm_linux(void* func, u64* arg_array, u64 zero, u64 pp, u64 st, void* off);
|
||||
u64 _call_goal8_asm_win32(void* func, u64* arg_array, u64 zero, u64 pp, u64 st, void* off);
|
||||
}
|
||||
|
||||
namespace Mips2C {
|
||||
|
||||
// nicknames for GPRs
|
||||
@@ -198,12 +206,28 @@ struct ExecutionContext {
|
||||
memcpy(&vfs[vf], g_ee_main_mem + gpr_src(gpr).du32[0] + offset, 16);
|
||||
}
|
||||
|
||||
void lwc1(int dst, int offset, int gpr) {
|
||||
memcpy(&fprs[dst], g_ee_main_mem + gpr_src(gpr).du32[0] + offset, 4);
|
||||
}
|
||||
|
||||
void lw(int dst, int offset, int src) {
|
||||
s32 val;
|
||||
memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 4);
|
||||
gprs[dst].ds64[0] = val;
|
||||
}
|
||||
|
||||
void lh(int dst, int offset, int src) {
|
||||
s16 val;
|
||||
memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 2);
|
||||
gprs[dst].ds64[0] = val;
|
||||
}
|
||||
|
||||
void lhu(int dst, int offset, int src) {
|
||||
u16 val;
|
||||
memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 2);
|
||||
gprs[dst].du64[0] = val;
|
||||
}
|
||||
|
||||
void lwu(int dst, int offset, int src) {
|
||||
u32 val;
|
||||
memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 4);
|
||||
@@ -214,11 +238,38 @@ struct ExecutionContext {
|
||||
memcpy(&gprs[dst].du64[0], g_ee_main_mem + gpr_addr(src) + offset, 16);
|
||||
}
|
||||
|
||||
void ld(int dst, int offset, int src) {
|
||||
memcpy(&gprs[dst].du64[0], g_ee_main_mem + gpr_addr(src) + offset, 8);
|
||||
}
|
||||
|
||||
void sw(int src, int offset, int addr) {
|
||||
auto s = gpr_src(src);
|
||||
memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 4);
|
||||
}
|
||||
|
||||
void jalr(u32 addr) {
|
||||
// u64 _call_goal8_asm_linux(u64 func, u64* arg_array, u64 zero, u64 pp, u64 st, u64 off);
|
||||
u64 args[8] = {gprs[a0].du64[0], gprs[a1].du64[0], gprs[a2].du64[0], gprs[a3].du64[0],
|
||||
gprs[t0].du64[0], gprs[t1].du64[0], gprs[t2].du64[0], gprs[t3].du64[0]};
|
||||
#ifdef __linux__
|
||||
gprs[v0].du64[0] = _call_goal8_asm_linux(g_ee_main_mem + addr, args, 0, gprs[s6].du64[0],
|
||||
gprs[s7].du64[0], g_ee_main_mem);
|
||||
#elif _WIN32
|
||||
gprs[v0].du64[0] = _call_goal8_asm_win32(g_ee_main_mem + addr, args, 0, gprs[s6].du64[0],
|
||||
gprs[s7].du64[0], g_ee_main_mem);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sh(int src, int offset, int addr) {
|
||||
auto s = gpr_src(src);
|
||||
memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 2);
|
||||
}
|
||||
|
||||
void sd(int src, int offset, int addr) {
|
||||
auto s = gpr_src(src);
|
||||
memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 8);
|
||||
}
|
||||
|
||||
void sq(int src, int offset, int addr) {
|
||||
auto s = gpr_src(src);
|
||||
memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 16);
|
||||
@@ -226,9 +277,14 @@ struct ExecutionContext {
|
||||
|
||||
void sqc2(int src, int offset, int addr) {
|
||||
auto s = vf_src(src);
|
||||
assert(((gpr_addr(addr) + offset) & 0xf) == 0);
|
||||
memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 16);
|
||||
}
|
||||
|
||||
void swc1(int src, int offset, int addr) {
|
||||
memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &fprs[src], 4);
|
||||
}
|
||||
|
||||
void vadd_bc(DEST mask, BC bc, int dest, int src0, int src1) {
|
||||
auto s0 = vf_src(src0);
|
||||
auto s1 = vf_src(src1);
|
||||
@@ -240,6 +296,52 @@ struct ExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
void vmini_bc(DEST mask, BC bc, int dest, int src0, int src1) {
|
||||
auto s0 = vf_src(src0);
|
||||
auto s1 = vf_src(src1);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((u64)mask & (1 << i)) {
|
||||
vfs[dest].f[i] = std::min(s0.f[i], s1.f[(int)bc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vmax_bc(DEST mask, BC bc, int dest, int src0, int src1) {
|
||||
auto s0 = vf_src(src0);
|
||||
auto s1 = vf_src(src1);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((u64)mask & (1 << i)) {
|
||||
vfs[dest].f[i] = std::max(s0.f[i], s1.f[(int)bc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pextuw(int dst, int src0, int src1) {
|
||||
auto s0 = gpr_src(src0);
|
||||
auto s1 = gpr_src(src1);
|
||||
gprs[dst].du32[0] = s1.du32[2];
|
||||
gprs[dst].du32[1] = s0.du32[2];
|
||||
gprs[dst].du32[2] = s1.du32[3];
|
||||
gprs[dst].du32[3] = s0.du32[3];
|
||||
}
|
||||
|
||||
void pcpyud(int dst, int src0, int src1) {
|
||||
auto s0 = gpr_src(src0);
|
||||
auto s1 = gpr_src(src1);
|
||||
gprs[dst].du64[0] = s0.du64[1];
|
||||
gprs[dst].du64[1] = s1.du64[1];
|
||||
}
|
||||
|
||||
void pexew(int dst, int src) {
|
||||
auto s = gpr_src(src);
|
||||
gprs[dst].du32[0] = s.du32[2];
|
||||
gprs[dst].du32[1] = s.du32[1];
|
||||
gprs[dst].du32[2] = s.du32[0];
|
||||
gprs[dst].du32[3] = s.du32[3];
|
||||
}
|
||||
|
||||
void vsub_bc(DEST mask, BC bc, int dest, int src0, int src1) {
|
||||
auto s0 = vf_src(src0);
|
||||
auto s1 = vf_src(src1);
|
||||
@@ -332,6 +434,10 @@ struct ExecutionContext {
|
||||
Q = vf_src(src0).f[(int)bc0] / vf_src(src1).f[(int)bc1];
|
||||
}
|
||||
|
||||
void vsqrt(int src, BC bc) { Q = std::sqrt(std::abs(vf_src(src).f[(int)bc])); }
|
||||
|
||||
void sqrts(int src, int dst) { fprs[dst] = std::sqrt(std::abs(fprs[src])); }
|
||||
|
||||
void vmulq(DEST mask, int dst, int src) {
|
||||
auto s0 = vf_src(src);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -341,6 +447,36 @@ struct ExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
void vrget(DEST mask, int dst) {
|
||||
float r = gRng.R;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((u64)mask & (1 << i)) {
|
||||
vfs[dst].f[i] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vrxor(int src, BC bc) { gRng.rxor(vf_src(src).du32[(int)bc]); }
|
||||
|
||||
void vaddq(DEST mask, int dst, int src) {
|
||||
auto s = vf_src(src);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((u64)mask & (1 << i)) {
|
||||
vfs[dst].f[i] = s.f[i] + Q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vrnext(DEST mask, int dst) {
|
||||
gRng.advance();
|
||||
float r = gRng.R;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((u64)mask & (1 << i)) {
|
||||
vfs[dst].f[i] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mov64(int dest, int src) { gprs[dest].ds64[0] = gpr_src(src).du64[0]; }
|
||||
|
||||
void vmove(DEST mask, int dest, int src) {
|
||||
@@ -358,7 +494,11 @@ struct ExecutionContext {
|
||||
gprs[dst].ds64[0] = value_signed;
|
||||
}
|
||||
|
||||
void dsra(int dst, int src, int sa) { gprs[dst].ds64[0] = gpr_src(src).ds64[0] >> sa; }
|
||||
void dsra32(int dst, int src, int sa) { gprs[dst].ds64[0] = gpr_src(src).ds64[0] >> (32 + sa); }
|
||||
void sra(int dst, int src, int sa) { gprs[dst].ds64[0] = gpr_src(src).ds32[0] >> sa; }
|
||||
void dsll(int dst, int src0, int sa) { gprs[dst].du64[0] = gpr_src(src0).du64[0] << sa; }
|
||||
void dsll32(int dst, int src0, int sa) { gprs[dst].du64[0] = gpr_src(src0).du64[0] << (32 + sa); }
|
||||
|
||||
void daddu(int dst, int src0, int src1) { gprs[dst].du64[0] = sgpr64(src0) + sgpr64(src1); }
|
||||
void daddiu(int dst, int src0, s64 imm) { gprs[dst].du64[0] = sgpr64(src0) + imm; }
|
||||
@@ -367,11 +507,19 @@ struct ExecutionContext {
|
||||
gprs[dst].ds64[0] = temp;
|
||||
}
|
||||
|
||||
void lui(int dst, u32 src) {
|
||||
s32 val = (src << 16);
|
||||
gprs[dst].ds64[0] = val;
|
||||
}
|
||||
|
||||
void addu(int dst, int src0, int src1) {
|
||||
s32 temp = sgpr64(src0) + sgpr64(src1);
|
||||
gprs[dst].ds64[0] = temp;
|
||||
}
|
||||
|
||||
void dsubu(int dst, int src0, int src1) { gprs[dst].du64[0] = sgpr64(src0) - sgpr64(src1); }
|
||||
void xor_(int dst, int src0, int src1) { gprs[dst].du64[0] = sgpr64(src0) ^ sgpr64(src1); }
|
||||
|
||||
void movz(int dst, int src0, int src1) {
|
||||
if (sgpr64(src1) == 0) {
|
||||
gprs[dst].du64[0] = sgpr64(src0);
|
||||
@@ -384,6 +532,12 @@ struct ExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
void mult3(int dst, int src0, int src1) {
|
||||
u32 result = gpr_src(src0).du32[0] * gpr_src(src1).du32[0];
|
||||
s32 sresult = result;
|
||||
gprs[dst].ds64[0] = sresult;
|
||||
}
|
||||
|
||||
void andi(int dest, int src, u64 imm) { gprs[dest].du64[0] = gpr_src(src).du64[0] & imm; }
|
||||
void ori(int dest, int src, u64 imm) { gprs[dest].du64[0] = gpr_src(src).du64[0] | imm; }
|
||||
void and_(int dest, int src0, int src1) {
|
||||
@@ -431,6 +585,22 @@ struct ExecutionContext {
|
||||
gprs[dst].du64[1] = s0.du64[1] | s1.du64[1];
|
||||
}
|
||||
|
||||
void pmaxw(int dst, int src0, int src1) {
|
||||
auto s0 = gpr_src(src0);
|
||||
auto s1 = gpr_src(src1);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gprs[dst].ds32[i] = std::max(s0.ds32[i], s1.ds32[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void pminw(int dst, int src0, int src1) {
|
||||
auto s0 = gpr_src(src0);
|
||||
auto s1 = gpr_src(src1);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gprs[dst].ds32[i] = std::min(s0.ds32[i], s1.ds32[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void mov128_vf_gpr(int dst, int src) { vfs[dst] = gpr_src(src); }
|
||||
void mov128_gpr_vf(int dst, int src) { gprs[dst] = vf_src(src); }
|
||||
void mov128_gpr_gpr(int dst, int src) { gprs[dst] = gpr_src(src); }
|
||||
@@ -453,11 +623,42 @@ struct ExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
void vftoi0(DEST mask, int dst, int src) {
|
||||
auto s = vf_src(src);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((u64)mask & (1 << i)) {
|
||||
vfs[dst].ds32[i] = s.f[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mfc1(int dst, int src) {
|
||||
s32 val;
|
||||
memcpy(&val, &fprs[src], 4);
|
||||
gprs[dst].ds64[0] = val;
|
||||
}
|
||||
|
||||
void mtc1(int dst, int src) {
|
||||
u32 val = gpr_src(src).du32[0];
|
||||
memcpy(&fprs[dst], &val, 4);
|
||||
}
|
||||
|
||||
void muls(int dst, int src0, int src1) { fprs[dst] = fprs[src0] * fprs[src1]; }
|
||||
void adds(int dst, int src0, int src1) { fprs[dst] = fprs[src0] + fprs[src1]; }
|
||||
void subs(int dst, int src0, int src1) { fprs[dst] = fprs[src0] - fprs[src1]; }
|
||||
|
||||
void cvtws(int dst, int src) {
|
||||
// float to int
|
||||
s32 value = fprs[src];
|
||||
memcpy(&fprs[dst], &value, 4);
|
||||
}
|
||||
|
||||
void cvtsw(int dst, int src) {
|
||||
// int to float
|
||||
s32 value;
|
||||
memcpy(&value, &fprs[src], 4);
|
||||
fprs[dst] = value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Mips2C
|
||||
@@ -16,9 +16,33 @@ namespace draw_string {
|
||||
extern void link();
|
||||
}
|
||||
|
||||
namespace sp_init_fields {
|
||||
extern void link();
|
||||
}
|
||||
|
||||
namespace particle_adgif {
|
||||
extern void link();
|
||||
}
|
||||
|
||||
namespace sp_launch_particles_var {
|
||||
extern void link();
|
||||
}
|
||||
|
||||
namespace sp_process_block_3d {
|
||||
extern void link();
|
||||
}
|
||||
|
||||
namespace sp_process_block_2d {
|
||||
extern void link();
|
||||
}
|
||||
|
||||
LinkedFunctionTable gLinkedFunctionTable;
|
||||
Rng gRng;
|
||||
std::unordered_map<std::string, std::vector<void (*)()>> gMips2CLinkCallbacks = {
|
||||
{"font", {draw_string::link}}};
|
||||
{"font", {draw_string::link}},
|
||||
{"sparticle-launcher",
|
||||
{sp_init_fields::link, particle_adgif::link, sp_launch_particles_var::link}},
|
||||
{"sparticle", {sp_process_block_3d::link, sp_process_block_2d::link}}};
|
||||
|
||||
void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 stack_size) {
|
||||
const auto& it = m_executes.insert({name, {exec, Ptr<u8>()}});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include "game/kernel/Ptr.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -26,4 +27,44 @@ class LinkedFunctionTable {
|
||||
extern std::unordered_map<std::string, std::vector<void (*)()>> gMips2CLinkCallbacks;
|
||||
extern LinkedFunctionTable gLinkedFunctionTable;
|
||||
|
||||
struct Rng {
|
||||
Rng() { init(); }
|
||||
float R = 0.;
|
||||
|
||||
u32 R_u32() {
|
||||
u32 result;
|
||||
memcpy(&result, &R, 4);
|
||||
return result;
|
||||
}
|
||||
|
||||
float from23_bits(float in) {
|
||||
u32 val;
|
||||
memcpy(&val, &in, 4);
|
||||
val = 0x3F800000 | (0x0007FFFFF & val);
|
||||
memcpy(&in, &val, 4);
|
||||
return in;
|
||||
}
|
||||
|
||||
float from23_bits(u32 val) {
|
||||
val = 0x3F800000 | (0x0007FFFFF & val);
|
||||
float out;
|
||||
memcpy(&out, &val, 4);
|
||||
return out;
|
||||
}
|
||||
|
||||
void init(float rinit = 1.418091058731079f) { R = from23_bits(rinit); }
|
||||
|
||||
void advance() {
|
||||
u32 r32 = R_u32();
|
||||
u32 x = 1 & (r32 >> 4);
|
||||
u32 y = 1 & (r32 >> 22);
|
||||
r32 <<= 1;
|
||||
r32 = r32 ^ x ^ y;
|
||||
R = from23_bits(r32);
|
||||
}
|
||||
|
||||
void rxor(u32 in) { R = from23_bits(in ^ R_u32()); }
|
||||
};
|
||||
|
||||
extern Rng gRng;
|
||||
} // namespace Mips2C
|
||||
+7
-7
@@ -253,12 +253,12 @@ void dmac_runner(SystemThreadInterface& iface) {
|
||||
iface.initialization_complete();
|
||||
|
||||
while (!iface.get_want_exit() && !VM::vm_want_exit()) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (VM::dmac_ch[i]->chcr.str) {
|
||||
// lg::info("DMA detected on channel {}, clearing", i);
|
||||
VM::dmac_ch[i]->chcr.str = 0;
|
||||
}
|
||||
}
|
||||
// for (int i = 0; i < 10; ++i) {
|
||||
// if (VM::dmac_ch[i]->chcr.str) {
|
||||
// // lg::info("DMA detected on channel {}, clearing", i);
|
||||
// VM::dmac_ch[i]->chcr.str = 0;
|
||||
// }
|
||||
// }
|
||||
// avoid running the DMAC on full blast (this does not sync to its clockrate)
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(50));
|
||||
}
|
||||
@@ -318,7 +318,7 @@ u32 exec_runtime(int argc, char** argv) {
|
||||
// TODO also sync this up with how the game actually renders things (this is just a placeholder)
|
||||
if (enable_display) {
|
||||
Gfx::Init();
|
||||
Gfx::Loop([&tm]() { return !tm.all_threads_exiting(); });
|
||||
Gfx::Loop([]() { return !MasterExit; });
|
||||
Gfx::Exit();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ static const Pad::Button libpad_PadPressureButtons[] = {
|
||||
// returns buffer size (32) or 0 on error.
|
||||
int scePadRead(int port, int /*slot*/, u8* rdata) {
|
||||
auto cpad = (CPadInfo*)(rdata);
|
||||
Gfx::poll_events();
|
||||
// Gfx::poll_events();
|
||||
|
||||
cpad->valid = 0; // success
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace Pad {
|
||||
std::unordered_map<int, int> g_key_status;
|
||||
std::unordered_map<int, int> g_buffered_key_status;
|
||||
|
||||
bool g_gamepad_buttons[(int)Button::Max] = {0};
|
||||
|
||||
// input mode for controller mapping
|
||||
InputModeStatus input_mode = InputModeStatus::Disabled;
|
||||
u64 input_mode_pad = 0;
|
||||
@@ -101,6 +103,10 @@ int IsPressed(MappingInfo& mapping, Button button, int pad = 0) {
|
||||
if (CheckPadIdx(pad) == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_gamepad_buttons[(int)button]) {
|
||||
return 1;
|
||||
}
|
||||
auto key = mapping.pad_mapping[pad][(int)button];
|
||||
if (key == -1)
|
||||
return 0;
|
||||
@@ -175,4 +181,62 @@ u64 input_mode_get_index() {
|
||||
return input_mode_index;
|
||||
}
|
||||
|
||||
/*
|
||||
********************************
|
||||
* Gamepad Support
|
||||
********************************
|
||||
*/
|
||||
|
||||
struct GamepadState {
|
||||
int gamepad_idx = -1;
|
||||
} g_gamepads;
|
||||
|
||||
void initialize() {
|
||||
for (int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_LAST; i++) {
|
||||
if (glfwJoystickPresent(i) && glfwJoystickIsGamepad(i)) {
|
||||
g_gamepads.gamepad_idx = i;
|
||||
lg::info("Using joystick {}: {}, {}", i, glfwGetJoystickName(i), glfwGetGamepadName(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (g_gamepads.gamepad_idx == -1) {
|
||||
lg::info("No joysticks found.");
|
||||
}
|
||||
}
|
||||
|
||||
void update_gamepads() {
|
||||
if (g_gamepads.gamepad_idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!glfwJoystickPresent(g_gamepads.gamepad_idx)) {
|
||||
g_gamepads.gamepad_idx = -1;
|
||||
lg::info("Gamepad has been disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
GLFWgamepadstate state;
|
||||
glfwGetGamepadState(g_gamepads.gamepad_idx, &state);
|
||||
|
||||
constexpr std::pair<Button, int> gamepad_map[] = {
|
||||
{Button::Select, GLFW_GAMEPAD_BUTTON_BACK},
|
||||
{Button::L3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB},
|
||||
{Button::R3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB},
|
||||
{Button::Start, GLFW_GAMEPAD_BUTTON_START},
|
||||
{Button::Up, GLFW_GAMEPAD_BUTTON_DPAD_UP},
|
||||
{Button::Right, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT},
|
||||
{Button::Down, GLFW_GAMEPAD_BUTTON_DPAD_DOWN},
|
||||
{Button::Left, GLFW_GAMEPAD_BUTTON_DPAD_LEFT},
|
||||
{Button::L1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER},
|
||||
{Button::R1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER},
|
||||
{Button::Triangle, GLFW_GAMEPAD_BUTTON_TRIANGLE},
|
||||
{Button::Circle, GLFW_GAMEPAD_BUTTON_CIRCLE},
|
||||
{Button::X, GLFW_GAMEPAD_BUTTON_CROSS},
|
||||
{Button::Square, GLFW_GAMEPAD_BUTTON_SQUARE}};
|
||||
|
||||
for (const auto& [button, idx] : gamepad_map) {
|
||||
g_gamepad_buttons[(int)button] = state.buttons[idx];
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace Pad
|
||||
|
||||
@@ -82,4 +82,7 @@ u64 input_mode_get();
|
||||
u64 input_mode_get_key();
|
||||
u64 input_mode_get_index();
|
||||
|
||||
void initialize();
|
||||
void update_gamepads();
|
||||
|
||||
} // namespace Pad
|
||||
|
||||
+487
-2008
File diff suppressed because it is too large
Load Diff
@@ -190,6 +190,15 @@
|
||||
(set! (-> math-cam guard z) 1.0)
|
||||
(set! (-> math-cam guard w) 1.0)
|
||||
(set! (-> math-cam isometric data 14) (- 16777215.0 hvdf-z))
|
||||
;; PC HACK!
|
||||
;; for whatever reason, the font render ends up computing a depth #x1000000 instead of
|
||||
;; #xffffffff, which overflows the 24-bit z buffer.
|
||||
;; cheating this by 1 bit seems to fix it.
|
||||
(#when PC_PORT
|
||||
;; #x4b002032 -> #x4b002031
|
||||
(-! (-> math-cam isometric data 14) 1.)
|
||||
)
|
||||
|
||||
)
|
||||
(set! (-> math-cam isometric data 15) fog-at-near-plane)
|
||||
|
||||
@@ -277,6 +286,8 @@
|
||||
math-cam
|
||||
)
|
||||
|
||||
|
||||
|
||||
(defmethod new math-camera ((allocation symbol) (type-to-make type))
|
||||
(let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
|
||||
(set! (-> gp-0 d) 1024.0)
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
;; merc1 61
|
||||
;; generic1 62
|
||||
(depth-cue 64)
|
||||
(bucket-65 65)
|
||||
(pre-sprite-textures 65) ;; always common
|
||||
(sprite 66)
|
||||
;; debug spheres? 67
|
||||
(debug-draw0 67)
|
||||
@@ -341,6 +341,7 @@
|
||||
(stmod 5) ;; set mode register
|
||||
(mskpath3 6) ;; set path 3 mask
|
||||
(mark 7) ;; set mark register
|
||||
(pc-port 8) ;; special tag for PC Port data.
|
||||
(flushe 16) ;; wait for end of microprogram
|
||||
(flush 17) ;; wait for end of microprogram and transfer (path1/path2)
|
||||
(flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3)
|
||||
|
||||
@@ -40,3 +40,4 @@
|
||||
(declare-type process-drawable process)
|
||||
(define-extern process-drawable-art-error (state string process-drawable))
|
||||
(define-extern foreground-engine-execute (function engine display-frame int int none))
|
||||
(define-extern sphere-in-view-frustum? (function sphere symbol))
|
||||
|
||||
@@ -5,6 +5,38 @@
|
||||
;; name in dgo: drawable
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
(defun sphere-in-view-frustum? ((arg0 sphere))
|
||||
(local-vars (r0-0 uint128) (v1-1 uint128) (v1-2 uint128) (v1-3 uint128))
|
||||
(rlet ((acc :class vf)
|
||||
(vf0 :class vf)
|
||||
(vf1 :class vf)
|
||||
(vf2 :class vf)
|
||||
(vf3 :class vf)
|
||||
(vf4 :class vf)
|
||||
(vf5 :class vf)
|
||||
(vf6 :class vf)
|
||||
)
|
||||
(init-vf0-vector)
|
||||
(set! r0-0 (the uint128 0))
|
||||
(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))
|
||||
)
|
||||
)
|
||||
|
||||
(defun real-main-draw-hook ()
|
||||
(when *slow-frame-rate*
|
||||
(dotimes (v1-2 #xc3500)
|
||||
@@ -17,10 +49,111 @@
|
||||
)
|
||||
)
|
||||
"Function to be executed to set up for engine dma"
|
||||
(set! *vu1-enable-user* *vu1-enable-user-menu*)
|
||||
(set! *texture-enable-user* *texture-enable-user-menu*)
|
||||
;; todo debug memory
|
||||
;; todo shrub matrix
|
||||
;; todo generic init
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; texture uploads
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; tfrag
|
||||
|
||||
(when (logtest? *texture-enable-user* 1)
|
||||
(dotimes (gp-1 (-> *level* length))
|
||||
(let ((a1-2 (-> *level* level gp-1)))
|
||||
(if (= (-> a1-2 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-2 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; pris
|
||||
|
||||
(when (logtest? *texture-enable-user* 2)
|
||||
(dotimes (gp-2 (-> *level* length))
|
||||
(let ((a1-3 (-> *level* level gp-2)))
|
||||
(if (= (-> a1-3 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-3 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; shrub
|
||||
|
||||
(when (logtest? *texture-enable-user* 4)
|
||||
(dotimes (gp-3 (-> *level* length))
|
||||
(let ((a1-4 (-> *level* level gp-3)))
|
||||
(if (= (-> a1-4 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-4 2)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; alpha and common.
|
||||
(when (logtest? *texture-enable-user* 8)
|
||||
(let ((uploaded-common #f))
|
||||
(dotimes (gp-4 (-> *level* length))
|
||||
(let ((a1-5 (-> *level* level gp-4)))
|
||||
(when (= (-> a1-5 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-5 3)
|
||||
(when (not uploaded-common)
|
||||
(upload-one-common! *texture-pool* (-> *level* level0))
|
||||
(set! uploaded-common #t)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(when (not uploaded-common)
|
||||
(upload-one-common! *texture-pool* (-> *level* level0))
|
||||
#t
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; water.
|
||||
(when (logtest? *texture-enable-user* 16)
|
||||
(dotimes (gp-5 (-> *level* length))
|
||||
(let ((a1-8 (-> *level* level gp-5)))
|
||||
(if (= (-> a1-8 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-8 4)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; texture common
|
||||
;; sky
|
||||
;; tod update
|
||||
;; closest
|
||||
;; ocean
|
||||
;; merc
|
||||
;; init bg
|
||||
;; exec bg
|
||||
;; finish bg
|
||||
;; stats
|
||||
;; fg engine
|
||||
;; bones
|
||||
;; gmerc
|
||||
;; shadow
|
||||
;; eyes
|
||||
(when (logtest? #x10000 *vu1-enable-user*)
|
||||
(swap-fake-shadow-buffers)
|
||||
(sprite-draw *display*)
|
||||
)
|
||||
;; lots more in this function.
|
||||
(when *debug-segment*
|
||||
(debug-draw-actors *level* *display-actor-marks*)
|
||||
;; collide-shape-debug
|
||||
)
|
||||
;; boundaries
|
||||
;; method15 level
|
||||
;; collide stats
|
||||
)
|
||||
|
||||
(defun main-draw-hook ()
|
||||
@@ -542,5 +675,4 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
(define-extern sphere-in-view-frustum? (function vector symbol))
|
||||
|
||||
|
||||
@@ -26,3 +26,22 @@
|
||||
(define-extern anim-loop (function symbol))
|
||||
;; TODO - for bouncer
|
||||
(define-extern ja-min? (function int symbol))
|
||||
|
||||
|
||||
(defun vector<-cspace! ((arg0 vector) (arg1 cspace))
|
||||
(rlet ((Q :class vf)
|
||||
(vf0 :class vf)
|
||||
(vf2 :class vf)
|
||||
)
|
||||
(init-vf0-vector)
|
||||
(.lvf vf2 (&-> (-> arg1 bone) transform vector 3 quad))
|
||||
(.div.vf Q vf0 vf2 :fsf #b11 :ftf #b11)
|
||||
(.wait.vf)
|
||||
(.mul.vf vf2 vf2 Q :mask #b111)
|
||||
(.nop.vf)
|
||||
(.nop.vf)
|
||||
(.mov.vf vf2 vf0 :mask #b1000)
|
||||
(.svf (&-> arg0 quad) vf2)
|
||||
arg0
|
||||
)
|
||||
)
|
||||
@@ -1613,82 +1613,36 @@
|
||||
;; used for the flashing auto-save icon, I think.
|
||||
|
||||
(set! (-> *part-group-id-table* 656)
|
||||
(new 'static 'sparticle-launch-group
|
||||
:length 1
|
||||
:duration #xbb8
|
||||
:linger-duration #x5dc
|
||||
:flags #x4
|
||||
:name "group-part-save-icon"
|
||||
:launcher
|
||||
(new 'static 'inline-array sparticle-group-item 1
|
||||
(new 'static 'sparticle-group-item :launcher #xa66)
|
||||
)
|
||||
:bounds (new 'static 'sphere :w 409600.0)
|
||||
)
|
||||
)
|
||||
(new 'static 'sparticle-launch-group
|
||||
:length 1
|
||||
:duration #xbb8
|
||||
:linger-duration #x5dc
|
||||
:flags (sp-group-flag screen-space)
|
||||
:name "group-part-save-icon"
|
||||
:launcher
|
||||
(new 'static 'inline-array sparticle-group-item 1 (sp-item 2662))
|
||||
:bounds (new 'static 'sphere :w 409600.0)
|
||||
)
|
||||
)
|
||||
|
||||
;; todo floats/ints for these values.
|
||||
(set! (-> *part-id-table* 2662)
|
||||
(new 'static 'sparticle-launcher
|
||||
:init-specs
|
||||
(new 'static 'inline-array sp-field-init-spec 11
|
||||
(new 'static 'sp-field-init-spec :field #x1 :initial-value #x1cf06b00)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x6
|
||||
:flags #x1
|
||||
:initial-value #x3f800000
|
||||
:random-mult #x3f800000
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #xd
|
||||
:flags #x1
|
||||
:initial-value #x45c00000
|
||||
:random-mult #x3f800000
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x11
|
||||
:flags #x3
|
||||
:initial-value -4
|
||||
:random-mult 1
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x12
|
||||
:flags #x1
|
||||
:initial-value #x43000000
|
||||
:random-mult #x3f800000
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x13
|
||||
:flags #x1
|
||||
:initial-value #x43000000
|
||||
:random-mult #x3f800000
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x14
|
||||
:flags #x1
|
||||
:initial-value #x43000000
|
||||
:random-mult #x3f800000
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x15
|
||||
:flags #x1
|
||||
:initial-value #x43000000
|
||||
:random-mult #x3f800000
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x2e
|
||||
:initial-value 5
|
||||
:random-mult 1
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x2f
|
||||
:initial-value #x2204
|
||||
:random-mult 1
|
||||
)
|
||||
(new 'static 'sp-field-init-spec :field #x43)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new 'static 'sparticle-launcher
|
||||
:init-specs
|
||||
(new 'static 'inline-array sp-field-init-spec 11
|
||||
(sp-tex spt-texture (new 'static 'texture-id :index #x6b :page #x1cf))
|
||||
(sp-flt spt-num 1.0)
|
||||
(sp-flt spt-scale-x (meters 1.5))
|
||||
(sp-copy-from-other spt-scale-y -4)
|
||||
(sp-flt spt-r 128.0)
|
||||
(sp-flt spt-g 128.0)
|
||||
(sp-flt spt-b 128.0)
|
||||
(sp-flt spt-a 128.0)
|
||||
(sp-int spt-timer 5)
|
||||
(sp-cpuinfo-flags bit2 bit9 bit13)
|
||||
(sp-end)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; auto-save process
|
||||
@@ -1731,7 +1685,7 @@
|
||||
"Deactivate the auto-save process."
|
||||
;; kill the particles
|
||||
(if (nonzero? (-> obj part))
|
||||
(deactivate (-> obj part))
|
||||
(kill-and-free-particles (-> obj part))
|
||||
)
|
||||
;; and do a normal deactivate.
|
||||
((method-of-type process deactivate) obj)
|
||||
@@ -1870,7 +1824,7 @@
|
||||
(set! (-> gp-2 vector4w w) (the-as int (-> *math-camera* hvdf-off w)))
|
||||
)
|
||||
)
|
||||
(dummy-11 (-> self part) *zero-vector*)
|
||||
(spawn (-> self part) *zero-vector*)
|
||||
)
|
||||
)
|
||||
(none)
|
||||
|
||||
@@ -684,15 +684,22 @@
|
||||
;; console buffers
|
||||
(set! *stdcon* (clear *stdcon0*))
|
||||
|
||||
;; here it is:
|
||||
;; <--------------------------- SWAP DISPLAY!
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(swap-display disp)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; teleport stuff
|
||||
;; perf stats
|
||||
|
||||
(process-particles)
|
||||
|
||||
|
||||
;; swap display
|
||||
;; particles
|
||||
;; vif0 collid
|
||||
;; swap sound
|
||||
;; str play
|
||||
;; level update
|
||||
(level-update *level*) ;; also updates settings.
|
||||
;; run mc
|
||||
;; auto save check
|
||||
;; suspend
|
||||
|
||||
+372
-1804
File diff suppressed because it is too large
Load Diff
@@ -224,6 +224,7 @@
|
||||
((= z 0.0)
|
||||
(let ((v1-4 obj))
|
||||
(set! (-> v1-4 origin z) (-> *math-camera* isometric vector 3 z))
|
||||
;;(format #t "fc: ~F~%" (-> v1-4 origin z))
|
||||
)
|
||||
)
|
||||
(else
|
||||
|
||||
@@ -43,3 +43,5 @@
|
||||
|
||||
;; definition for symbol *fake-shadow-buffer*, type fake-shadow-buffer
|
||||
(define *fake-shadow-buffer* *fake-shadow-buffer-1*)
|
||||
|
||||
(define-extern swap-fake-shadow-buffers (function none))
|
||||
@@ -234,125 +234,29 @@
|
||||
(new 'static 'sparticle-launcher
|
||||
:init-specs
|
||||
(new 'static 'inline-array sp-field-init-spec 20
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x1
|
||||
:initial-valuef (the-as float #x200000)
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x6
|
||||
:flags #x1
|
||||
:initial-valuef 8.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #xd
|
||||
:flags #x1
|
||||
:initial-valuef 1228.8
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x10
|
||||
:flags #x1
|
||||
:initial-valuef -32768.0
|
||||
:random-rangef 65536.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x11
|
||||
:flags #x3
|
||||
:initial-valuef (the-as float #xfffffffc)
|
||||
:random-multf (the-as float #x1)
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x12
|
||||
:flags #x1
|
||||
:initial-valuef 90.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x13
|
||||
:flags #x1
|
||||
:initial-valuef 90.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x14
|
||||
:flags #x1
|
||||
:initial-valuef 90.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x15
|
||||
:flags #x1
|
||||
:initial-valuef 20.0
|
||||
:random-rangef 20.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x1a
|
||||
:flags #x1
|
||||
:initial-valuef 8.192
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x1c
|
||||
:flags #x1
|
||||
:initial-valuef 17.066668
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x20
|
||||
:flags #x3
|
||||
:initial-valuef (the-as float #xfffffffc)
|
||||
:random-multf (the-as float #x1)
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x24
|
||||
:flags #x1
|
||||
:initial-valuef -0.3
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x2e
|
||||
:initial-valuef (the-as float #x138c)
|
||||
:random-multf (the-as float #x1)
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x2f
|
||||
:initial-valuef (the-as float #xc)
|
||||
:random-multf (the-as float #x1)
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x36
|
||||
:flags #x1
|
||||
:initial-valuef -3640.889
|
||||
:random-rangef 2730.6667
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x3a
|
||||
:flags #x1
|
||||
:initial-valuef 16384.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x3b
|
||||
:flags #x1
|
||||
:initial-valuef -32768.0
|
||||
:random-rangef 65536.0
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec
|
||||
:field #x3e
|
||||
:flags #x1
|
||||
:initial-valuef 819.2
|
||||
:random-multf 1.0
|
||||
)
|
||||
(new 'static 'sp-field-init-spec :field #x43)
|
||||
(sp-tex spt-texture (new 'static 'texture-id :page #x2))
|
||||
(sp-flt spt-num 8.0)
|
||||
(sp-flt spt-scale-x (meters 0.3))
|
||||
(sp-rnd-flt spt-rot-z (degrees -180.0) (degrees 360.0) 1.0)
|
||||
(sp-copy-from-other spt-scale-y -4)
|
||||
(sp-flt spt-r 90.0)
|
||||
(sp-flt spt-g 90.0)
|
||||
(sp-flt spt-b 90.0)
|
||||
(sp-rnd-flt spt-a 20.0 20.0 1.0)
|
||||
(sp-flt spt-vel-y (meters 0.002))
|
||||
(sp-flt spt-scalevel-x (meters 0.004166667))
|
||||
(sp-copy-from-other spt-scalevel-y -4)
|
||||
(sp-flt spt-fade-a -0.3)
|
||||
(sp-int spt-timer 5004)
|
||||
(sp-cpuinfo-flags bit2 bit3)
|
||||
(sp-rnd-flt spt-launchrot-x (degrees -20.0) (degrees 15.0) 1.0)
|
||||
(sp-flt spt-conerot-x (degrees 90.0))
|
||||
(sp-rnd-flt spt-conerot-y (degrees -180.0) (degrees 360.0) 1.0)
|
||||
(sp-flt spt-conerot-radius (meters 0.2))
|
||||
(sp-end)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -107,12 +107,13 @@
|
||||
)
|
||||
|
||||
|
||||
;; TODO
|
||||
(define sprite-distort-vu1-block (the-as vu-function 0))
|
||||
;; we need to at least put something here so dma-buffer-add-vu-function doesn't crash.
|
||||
(define sprite-distort-vu1-block (new 'static 'vu-function))
|
||||
|
||||
|
||||
(defun sprite-init-distorter ((arg0 dma-buffer) (arg1 uint))
|
||||
"Set up DMA for setting up the sprite-distorter renderer"
|
||||
;;(format #t "distorter: ~d~%" (-> *sprite-aux-list* entry))
|
||||
(let* ((v1-0 arg0)
|
||||
(a2-0 (the-as object (-> v1-0 base)))
|
||||
)
|
||||
|
||||
@@ -98,18 +98,15 @@
|
||||
)
|
||||
)
|
||||
|
||||
#|
|
||||
;; until we figure out the entry type, we can't have this function.
|
||||
(defmethod inspect sprite-aux-list ((obj sprite-aux-list))
|
||||
(format #t "[~X] sprite-aux-list:~%" obj)
|
||||
(format #t "~Tnum-entries: ~D~%" (-> obj num-entries))
|
||||
(format #t "~Tentry: ~D~%" (-> obj entry))
|
||||
(dotimes (s5-0 (-> obj entry))
|
||||
(format #t "~T~D : ~X~%" s5-0 (l.wu (+ (+ (* s5-0 4) (the-as int obj)) 8)))
|
||||
(format #t "~T~D : ~X~%" s5-0 (-> obj data s5-0))
|
||||
)
|
||||
(the-as sprite-aux-list #f)
|
||||
)
|
||||
|#
|
||||
|
||||
(define *sprite-aux-list* (new 'global 'sprite-aux-list 256))
|
||||
|
||||
@@ -119,7 +116,17 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
;; TODO function add-to-sprite-aux-list
|
||||
(defun add-to-sprite-aux-list ((arg0 sparticle-system) (arg1 sparticle-cpuinfo) (arg2 sprite-vec-data-3d))
|
||||
(let ((v1-0 *sprite-aux-list*))
|
||||
(when (< (-> v1-0 entry) (-> v1-0 num-entries))
|
||||
(set! (-> v1-0 data (-> v1-0 entry)) (-> arg1 sprite))
|
||||
(+! (-> v1-0 entry) 1)
|
||||
)
|
||||
)
|
||||
(set! (-> arg2 r-g-b-a w) 0.0)
|
||||
0
|
||||
(none)
|
||||
)
|
||||
|
||||
;; The sprite-frame-data is data transferred to VU1 and remains there for all chunks of sprites.
|
||||
(deftype sprite-frame-data (structure)
|
||||
@@ -372,7 +379,8 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
;;(define sprite-vu1-block (the-as vu-function L58))
|
||||
;; we need to at least put something here so dma-buffer-add-vu-function doesn't crash.
|
||||
(define sprite-vu1-block (new 'static 'vu-function))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;
|
||||
;; sprite-arrays
|
||||
@@ -569,6 +577,7 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
#|
|
||||
(defun sprite-add-frame-data ((dma-buff dma-buffer) (tbp-offset uint))
|
||||
"Upload the frame data."
|
||||
(let ((s5-0 41)) ;; qwc of frame data.
|
||||
@@ -587,6 +596,29 @@
|
||||
)
|
||||
(none)
|
||||
)
|
||||
|#
|
||||
|
||||
(defun sprite-add-frame-data ((dma-buff dma-buffer) (tbp-offset uint))
|
||||
(let ((s5-0 41))
|
||||
(let* ((v1-0 dma-buff)
|
||||
(pkt (the-as dma-packet (-> v1-0 base)))
|
||||
)
|
||||
(set! (-> pkt dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc s5-0))
|
||||
(set! (-> pkt vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)))
|
||||
(set!
|
||||
(-> pkt vif1)
|
||||
(new 'static 'vif-tag :imm #x3d4 :cmd (vif-cmd unpack-v4-32) :num s5-0)
|
||||
)
|
||||
(set! (-> v1-0 base) (&+ (the-as pointer pkt) 16))
|
||||
)
|
||||
(sprite-setup-frame-data
|
||||
(the-as sprite-frame-data (-> dma-buff base))
|
||||
(the-as int tbp-offset)
|
||||
)
|
||||
(&+! (-> dma-buff base) (* s5-0 16))
|
||||
)
|
||||
(none)
|
||||
)
|
||||
|
||||
(defun sprite-add-2d-chunk ((sprites sprite-array-2d) (start-sprite-idx int) (num-sprites int) (dma-buff dma-buffer) (mscal-addr int))
|
||||
"Upload sprite data from elements in the array."
|
||||
@@ -612,6 +644,18 @@
|
||||
:imm (new 'static 'vif-unpack-imm :flg 1 :addr 1))
|
||||
)
|
||||
)
|
||||
|
||||
; (dotimes (i num-sprites)
|
||||
; (let ((spidx (+ i start-sprite-idx)))
|
||||
; (when (or (= spidx (-> sprites num-sprites 0)) (= spidx (+ 1 (-> sprites num-sprites 0))))
|
||||
; (let ((data (the sprite-vec-data-2d (&+ (-> sprites vec-data) (* 48 (+ i start-sprite-idx))))))
|
||||
; (format #t "spidx: ~d~%")
|
||||
; (inspect data)
|
||||
; (inspect (-> data r-g-b-a))
|
||||
; )
|
||||
; )
|
||||
; )
|
||||
; )
|
||||
|
||||
;; third packet is adgif data (5 qw/sprite)
|
||||
(let ((qwc-pkt3 (* 5 num-sprites)))
|
||||
@@ -643,6 +687,8 @@
|
||||
;; and use the other mpg.
|
||||
(set! mscal-addr 109)
|
||||
)
|
||||
|
||||
;;(format #t "group ~d 2D ~d~%" group-idx (-> sprites num-valid group-idx))
|
||||
|
||||
;; loop over chunks
|
||||
(let ((remaining-sprites (-> sprites num-valid group-idx)))
|
||||
@@ -815,6 +861,7 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
|
||||
(defun sprite-draw ((disp display))
|
||||
"Main sprite draw function."
|
||||
;; start of our DMA for all the sprite data
|
||||
@@ -896,6 +943,8 @@
|
||||
(none)
|
||||
)
|
||||
|
||||
|
||||
|
||||
(defun sprite-allocate-user-hvdf ()
|
||||
"Allocate an HVDF entry. Returns the index. Or 0 if it fails"
|
||||
(dotimes (v1-0 76)
|
||||
|
||||
@@ -627,6 +627,7 @@
|
||||
;; allocate the common and near segments
|
||||
(allocate-segment! obj (-> obj segment-common) COMMON_SEGMENT_WORDS) ;; ~0.5 MB
|
||||
(allocate-segment! obj (-> obj segment-near) NEAR_SEGMENT_WORDS) ;; ~1.6 MB.
|
||||
(format #t "near segment is at ~D to ~d~%" (-> obj segment-near dest) (+ (-> obj segment-near dest) (-> obj segment-near size)))
|
||||
|
||||
;; Allocate the random crap
|
||||
(set! *sky-base-vram-word* (allocate-vram-words! obj SPECIAL_VRAM_WORDS))
|
||||
@@ -799,6 +800,8 @@
|
||||
(first-chunk-idx-to-upload int)
|
||||
(tex-id uint)
|
||||
)
|
||||
|
||||
|
||||
(let ((total-upload-size 0))
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> (current-frame) global-buf)) ;; the global DMA buffer
|
||||
bucket-idx)
|
||||
@@ -843,7 +846,7 @@
|
||||
(dotimes (upload-chunk-idx (the-as int chunk-count))
|
||||
;; the destination of the chunk.
|
||||
(let ((current-dest-chunk
|
||||
(+ tex-dest-base-chunk (the-as uint upload-chunk-idx))
|
||||
(+ tex-dest-base-chunk (the-as uint upload-chunk-idx))
|
||||
)
|
||||
)
|
||||
;; now we see if we can get away with not uploading the chunk.
|
||||
@@ -860,42 +863,57 @@
|
||||
((= (-> pool ids current-dest-chunk) tex-id)
|
||||
;; and the run ends, we found a chunk that's already loaded.
|
||||
;; so we upload the run:
|
||||
(upload-vram-data dma-buf
|
||||
(the int (shl (+ tex-dest-base-chunk (the-as uint first-chunk-idx-to-upload)) 6))
|
||||
(&+ tex-data (shl first-chunk-idx-to-upload 14))
|
||||
(shl chunks-to-upload-count 5)
|
||||
)
|
||||
(#when (not PC_PORT)
|
||||
(upload-vram-data dma-buf
|
||||
(the int (shl (+ tex-dest-base-chunk (the-as uint first-chunk-idx-to-upload)) 6))
|
||||
(&+ tex-data (shl first-chunk-idx-to-upload 14))
|
||||
(shl chunks-to-upload-count 5)
|
||||
)
|
||||
)
|
||||
|
||||
(+! total-upload-size chunks-to-upload-count)
|
||||
;; reset
|
||||
(set! chunks-to-upload-count 0)
|
||||
)
|
||||
(else
|
||||
;; the run continues!
|
||||
(set! (-> pool ids current-dest-chunk) tex-id)
|
||||
(set! chunks-to-upload-count (+ chunks-to-upload-count 1))
|
||||
)
|
||||
;; the run continues!
|
||||
(set! (-> pool ids current-dest-chunk) tex-id)
|
||||
(set! chunks-to-upload-count (+ chunks-to-upload-count 1))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; if we finished with a run of "needs upload", set up the upload.
|
||||
(when (nonzero? chunks-to-upload-count)
|
||||
(upload-vram-data
|
||||
dma-buf
|
||||
(the int (shl (+ tex-dest-base-chunk (the-as uint first-chunk-idx-to-upload)) 6))
|
||||
(&+ tex-data (shl first-chunk-idx-to-upload 14))
|
||||
(shl chunks-to-upload-count 5)
|
||||
)
|
||||
(#when (not PC_PORT)
|
||||
(upload-vram-data
|
||||
dma-buf
|
||||
(the int (shl (+ tex-dest-base-chunk (the-as uint first-chunk-idx-to-upload)) 6))
|
||||
(&+ tex-data (shl first-chunk-idx-to-upload 14))
|
||||
(shl chunks-to-upload-count 5)
|
||||
)
|
||||
)
|
||||
(+! total-upload-size chunks-to-upload-count)
|
||||
)
|
||||
|
||||
;; do a texflush
|
||||
|
||||
;; send gif (a+d)
|
||||
(dma-buffer-add-gs-set dma-buf
|
||||
(texflush 1) ;; texflush
|
||||
)
|
||||
(#when (not PC_PORT)
|
||||
(dma-buffer-add-gs-set dma-buf
|
||||
(texflush 1) ;; texflush
|
||||
)
|
||||
)
|
||||
|
||||
;; in PC PORT we just skip all that stuff and just send a pointer to the texture page and the mode.
|
||||
(#when PC_PORT
|
||||
(dma-buffer-add-cnt-vif2 dma-buf 1 (new 'static 'vif-tag :cmd (vif-cmd pc-port)) (the-as vif-tag 3))
|
||||
(dma-buffer-add-uint64 dma-buf page)
|
||||
(dma-buffer-add-uint64 dma-buf mode)
|
||||
)
|
||||
|
||||
)
|
||||
(shl total-upload-size 14)
|
||||
)
|
||||
@@ -1580,7 +1598,7 @@
|
||||
(and (nonzero? a2-0) (logtest? (-> obj common-page-mask) (ash 1 v1-0))) ;; in the mask.
|
||||
)
|
||||
;; upload it!
|
||||
(upload-vram-pages obj (-> obj segment-common) a2-0 -2 (bucket-id bucket-65))
|
||||
(upload-vram-pages obj (-> obj segment-common) a2-0 -2 (bucket-id pre-sprite-textures))
|
||||
(return #f)
|
||||
)
|
||||
)
|
||||
@@ -2080,6 +2098,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
;; (format #t "relocate dests: ~A seg ~D from ~D to ~D~%" obj seg-id (-> obj segment seg-id dest) new-dest)
|
||||
(set! (-> obj segment seg-id dest) (the-as uint new-dest))
|
||||
)
|
||||
)
|
||||
@@ -2227,7 +2246,9 @@
|
||||
"Look up a texture by ID, loading it from debug network if its not loaded.
|
||||
Default allocates if it has to load, so it will permanently use VRAM"
|
||||
(let ((v1-0 (texture-page-login arg0 texture-page-default-allocate loading-level)))
|
||||
(if (and v1-0 (< (-> arg0 index) (the-as uint (-> v1-0 page length))))
|
||||
(when (and v1-0 (< (-> arg0 index) (the-as uint (-> v1-0 page length))))
|
||||
;;(format #t "texture:~%")
|
||||
;;(inspect (-> v1-0 page data (-> arg0 index)))
|
||||
(-> v1-0 page data (-> arg0 index))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
;; name in dgo: time-of-day
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
(define *time-of-day-proc* (the (pointer time-of-day-proc) #f))
|
||||
@@ -43,8 +43,8 @@
|
||||
(pat-length int32 :offset-assert 48)
|
||||
(texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
(texture-remap-table-len int32 :offset-assert 56)
|
||||
(unk-data-1 pointer :offset-assert 60)
|
||||
(unk-data-1-len int32 :offset-assert 64)
|
||||
(texture-ids (pointer texture-id) :offset-assert 60)
|
||||
(texture-page-count int32 :offset-assert 64)
|
||||
(unk-zero-0 basic :offset-assert 68)
|
||||
(name symbol :offset-assert 72)
|
||||
(nickname symbol :offset-assert 76)
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
(set! (-> mem-use length) (max 58 (-> mem-use length)))
|
||||
(set! (-> mem-use data 57 name) "bsp-misc")
|
||||
(+! (-> mem-use data 57 count) 1)
|
||||
(let ((v1-56 (* (-> obj unk-data-1-len) 4)))
|
||||
(let ((v1-56 (* (-> obj texture-page-count) 4)))
|
||||
(+! (-> mem-use data 57 used) v1-56)
|
||||
(+! (-> mem-use data 57 total) (logand -16 (+ v1-56 15)))
|
||||
)
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
(debug-print-entities (_type_ symbol type) none 13)
|
||||
(debug-draw-actors (_type_ symbol) none 14)
|
||||
(dummy-15 (_type_) object 15)
|
||||
(dummy-16 (_type_) int 16)
|
||||
(level-update (_type_) int 16)
|
||||
(level-get-target-inside (_type_) level 17)
|
||||
(alloc-levels! (_type_ symbol) int 18)
|
||||
(load-commands-set! (_type_ pair) pair 19)
|
||||
|
||||
@@ -488,12 +488,12 @@
|
||||
(cond
|
||||
((-> obj bsp)
|
||||
(set! (-> *level* log-in-level-bsp) (-> obj bsp))
|
||||
;; TODO
|
||||
;;(login-level-textures *texture-pool* obj (-> obj bsp unk-data-1-len) (the-as (pointer texture-id) (-> obj bsp unk-data-1)))
|
||||
(login-level-textures *texture-pool* obj (-> obj bsp texture-page-count) (-> obj bsp texture-ids))
|
||||
(let ((bsp (-> obj bsp)))
|
||||
(when (nonzero? (-> bsp adgifs))
|
||||
(let ((adgifs (-> bsp adgifs)))
|
||||
(dotimes (i (-> adgifs length))
|
||||
;; TODO
|
||||
;;(adgif-shader-login-no-remap (-> adgifs data i)) TODO texture.gc
|
||||
)
|
||||
)
|
||||
@@ -1549,6 +1549,12 @@
|
||||
|
||||
;; method 16 level-group (debug text stuff)
|
||||
|
||||
(defmethod level-update level-group ((obj level-group))
|
||||
;; todo lots of stuff
|
||||
(update-per-frame-settings! *setting-control*)
|
||||
0
|
||||
)
|
||||
|
||||
(defun-debug show-level ((level-name symbol))
|
||||
(set! (-> *setting-control* default border-mode) #t)
|
||||
(load-state-want-levels (-> (level-get-target-inside *level*) name) level-name)
|
||||
|
||||
@@ -17,6 +17,28 @@
|
||||
|
||||
(define *sp-60-hz* #t)
|
||||
|
||||
(defenum sp-cpuinfo-flag
|
||||
:bitfield #t
|
||||
:type uint32
|
||||
(bit0 0)
|
||||
(bit2 2) ;; cleared after an aux has its func set to add-to-sprite-aux-lst
|
||||
(bit3 3)
|
||||
(ready-to-launch 6) ;; maybe just just death?
|
||||
(bit7 7)
|
||||
(aux-list 8) ;; prevents relaunch, adds to aux
|
||||
(bit9 9)
|
||||
(level0 10)
|
||||
(level1 11)
|
||||
(bit12 12) ;; required to relaunch
|
||||
(bit13 13)
|
||||
(bit14 14)
|
||||
(use-global-acc 16)
|
||||
(launch-along-z 17)
|
||||
(left-multiply-quat 18)
|
||||
(right-multiply-quat 19)
|
||||
(set-conerot 20)
|
||||
)
|
||||
|
||||
(deftype sparticle-cpuinfo (structure)
|
||||
((sprite sprite-vec-data-2d :offset-assert 0)
|
||||
(adgif adgif-shader :offset-assert 4)
|
||||
@@ -33,7 +55,7 @@
|
||||
(scalevely float :offset 44)
|
||||
(friction float :offset-assert 96)
|
||||
(timer int32 :offset-assert 100)
|
||||
(flags uint32 :offset-assert 104)
|
||||
(flags sp-cpuinfo-flag :offset-assert 104)
|
||||
(user-int32 int32 :offset-assert 108)
|
||||
(user-uint32 uint32 :offset 108)
|
||||
(user-float float :score 100 :offset 108)
|
||||
@@ -53,8 +75,10 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x8c
|
||||
:flag-assert #x90000008c
|
||||
;; field key is a basic loaded with a signed load
|
||||
)
|
||||
|
||||
|
||||
(deftype sparticle-launchinfo (structure)
|
||||
((launchrot vector :inline :offset-assert 0)
|
||||
(conerot vector :inline :offset-assert 16)
|
||||
@@ -69,8 +93,8 @@
|
||||
|
||||
(deftype sparticle-system (basic)
|
||||
((blocks int32 2 :offset-assert 4)
|
||||
(length uint32 2 :offset-assert 12)
|
||||
(num-alloc uint32 2 :offset-assert 20)
|
||||
(length int32 2 :offset-assert 12)
|
||||
(num-alloc int32 2 :offset-assert 20)
|
||||
(is-3d basic :offset-assert 28)
|
||||
(flags uint32 :offset-assert 32)
|
||||
(alloc-table (pointer uint64) :offset-assert 36)
|
||||
@@ -98,3 +122,4 @@
|
||||
|
||||
(defun-extern kill-all-particles-with-key sparticle-launch-control none)
|
||||
|
||||
(define-extern sp-get-particle (function sparticle-system int sparticle-launch-state sparticle-cpuinfo))
|
||||
@@ -5,9 +5,139 @@
|
||||
;; name in dgo: sparticle-launcher-h
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
|
||||
;; The "sparticle" system is the particle system.
|
||||
;; Features
|
||||
;; - Support for 2D particles (autosave icon, progress menu graphics)
|
||||
;; - Support for 3D particles (many of the effects)
|
||||
;; - Uses the "sprite" renderer to draw particles
|
||||
|
||||
;; The "sparticle-launcher" code is the framework for describing particle effects
|
||||
;; The "sparticle" code is the system that runs particles
|
||||
;; Note that neither of these link particles to the process system. See part-tracker for that.
|
||||
|
||||
;; The higheset level class here is sparticle-launch-control.
|
||||
;; Each instance of a particle effect must have one of these.
|
||||
;; For example, there would be one of these per eco-vent.
|
||||
;; These store some state (a sparticle-launch-state) and a reference to a sparticle-launch-group
|
||||
;; Multiple launch-controls can refer to the same launch-group.
|
||||
|
||||
|
||||
;; A sparticle-launch-group is a description of a particle effect.
|
||||
;; It can contain multiple types of particles.
|
||||
;; The `*part-group-id-table*` array stores a reference to every launch-group, indexed by group id.
|
||||
;; Each launch-group is just a list of sparticle-launchers, stored as an index
|
||||
|
||||
;; A launcher is a single particle effect.
|
||||
;; The `*part-id-table*` has references to all particle effects.
|
||||
;; It contains a list of "field-init-specs". When the particle effect starts, the system
|
||||
;; iterates through this list and sets parameters about particles.
|
||||
|
||||
;; There are five types of fields:
|
||||
;; misc fields
|
||||
;; sprite fields
|
||||
;; cpu fields
|
||||
;; launch fields
|
||||
;; weird fields
|
||||
|
||||
;; The built-in parameters can be used for many simple effects, but sometimes it is not enough.
|
||||
;; You can provide a callback function to update the particle's state if needed.
|
||||
|
||||
;; These are the user-settable state variables for each particle effect
|
||||
(defenum sp-field-id
|
||||
:type uint16
|
||||
|
||||
(misc-fields-start 0)
|
||||
(spt-texture 1)
|
||||
(spt-anim 2)
|
||||
(spt-anim-speed 3)
|
||||
(spt-birth-func 4)
|
||||
(spt-joint/refpoint 5)
|
||||
(spt-num 6)
|
||||
(spt-sound 7)
|
||||
(misc-fields-end 8)
|
||||
|
||||
(sprite-fields-start 9)
|
||||
(spt-x 10)
|
||||
(spt-y 11)
|
||||
(spt-z 12)
|
||||
(spt-scale-x 13)
|
||||
(spt-rot-x 14)
|
||||
(spt-rot-y 15)
|
||||
(spt-rot-z 16)
|
||||
(spt-scale-y 17)
|
||||
(spt-r 18)
|
||||
(spt-g 19)
|
||||
(spt-b 20)
|
||||
(spt-a 21)
|
||||
(sprite-fields-end 22)
|
||||
|
||||
(cpu-fields-start 23)
|
||||
(spt-omega 24)
|
||||
(spt-vel-x 25)
|
||||
(spt-vel-y 26)
|
||||
(spt-vel-z 27)
|
||||
(spt-scalevel-x 28)
|
||||
(spt-rotvel-x 29)
|
||||
(spt-rotvel-y 30)
|
||||
(spt-rotvel-z 31)
|
||||
(spt-scalevel-y 32)
|
||||
(spt-fade-r 33)
|
||||
(spt-fade-g 34)
|
||||
(spt-fade-b 35)
|
||||
(spt-fade-a 36)
|
||||
(spt-accel-x 37)
|
||||
(spt-accel-y 38)
|
||||
(spt-accel-z 39)
|
||||
(spt-dummy 40)
|
||||
(spt-quat-x 41)
|
||||
(spt-quat-y 42)
|
||||
(spt-quat-z 43)
|
||||
(spt-quad-w 44)
|
||||
(spt-friction 45)
|
||||
(spt-timer 46)
|
||||
(spt-flags 47)
|
||||
(spt-userdata 48)
|
||||
(spt-func 49)
|
||||
(spt-next-time 50)
|
||||
(spt-next-launcher 51)
|
||||
(cpu-fields-end 52)
|
||||
|
||||
(launch-fields-start 53)
|
||||
(spt-launchrot-x 54)
|
||||
(spt-launchrot-y 55)
|
||||
(spt-launchrot-z 56)
|
||||
(spt-launchrot-w 57)
|
||||
(spt-conerot-x 58)
|
||||
(spt-conerot-y 59)
|
||||
(spt-conerot-z 60)
|
||||
(spt-conerot-w 61)
|
||||
(spt-conerot-radius 62)
|
||||
(spt-rotate-y 63)
|
||||
(launch-fields-end 64)
|
||||
|
||||
(spt-scale 65)
|
||||
(spt-scalevel 66)
|
||||
(spt-end 67)
|
||||
)
|
||||
|
||||
(defenum sp-flag
|
||||
:type uint16
|
||||
(plain-v1 0) ;; just a plain signed integer. No random crap.
|
||||
(float-with-rand 1)
|
||||
(int-with-rand 2)
|
||||
(copy-from-other-field 3)
|
||||
(plain-v2 4)
|
||||
(from-pointer 5)
|
||||
(part-by-id 6)
|
||||
)
|
||||
|
||||
;; This describes the initial value and some more info for a single field
|
||||
;; Note that there are overlays here and some values only make sense in some
|
||||
;; cases.
|
||||
(deftype sp-field-init-spec (structure)
|
||||
((field uint16 :offset-assert 0)
|
||||
(flags uint16 :offset-assert 2)
|
||||
((field sp-field-id :offset-assert 0)
|
||||
(flags sp-flag :offset-assert 2)
|
||||
(initial-valuef float :offset-assert 4)
|
||||
(random-rangef float :offset-assert 8)
|
||||
(random-multf float :offset-assert 12)
|
||||
@@ -26,6 +156,112 @@
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
;; sparticle field macros
|
||||
|
||||
(defmacro sp-tex (field-name tex-id)
|
||||
`(new 'static 'sp-field-init-spec :field (sp-field-id ,field-name) :tex ,tex-id)
|
||||
)
|
||||
|
||||
(defmacro sp-rnd-flt (field-name val range mult)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-valuef ,val
|
||||
:random-rangef ,range
|
||||
:random-multf ,mult
|
||||
:flags (sp-flag float-with-rand)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-flt (field-name val)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-valuef ,val
|
||||
:random-rangef 0.0
|
||||
:random-multf 1.0
|
||||
:flags (sp-flag float-with-rand)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-int (field-name val)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-value ,val
|
||||
:random-range 0
|
||||
:random-mult 1
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-int-plain-rnd (field-name val range mult)
|
||||
"For when we use plain integer, but set the randoms."
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-value ,val
|
||||
:random-range ,range
|
||||
:random-mult ,mult
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-rnd-int (field-name val range mult)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-value ,val
|
||||
:random-range ,range
|
||||
:random-multf ,mult
|
||||
:flags (sp-flag int-with-rand)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
(defmacro sp-rnd-int-flt (field-name val range mult)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-valuef ,val
|
||||
:random-range ,range
|
||||
:random-multf ,mult
|
||||
:flags (sp-flag int-with-rand)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-cpuinfo-flags (&rest flags)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id spt-flags)
|
||||
:initial-value (sp-cpuinfo-flag ,@flags)
|
||||
:random-mult 1
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-launcher-by-id (field-name val)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-value ,val
|
||||
:flags (sp-flag part-by-id)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-func (field-name val)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:sym ,val
|
||||
:flags (sp-flag from-pointer)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-end ()
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id spt-end)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro sp-copy-from-other (field-name offset)
|
||||
`(new 'static 'sp-field-init-spec
|
||||
:field (sp-field-id ,field-name)
|
||||
:initial-value ,offset
|
||||
:random-mult 1
|
||||
:flags (sp-flag copy-from-other-field)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
(deftype sparticle-launcher (basic)
|
||||
((birthaccum float :offset-assert 4)
|
||||
(soundaccum float :offset-assert 8)
|
||||
@@ -36,11 +272,20 @@
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
(defenum sp-group-item-flag
|
||||
:bitfield #t
|
||||
:type uint16
|
||||
(is-3d 0)
|
||||
(bit1 1)
|
||||
(start-dead 2)
|
||||
(launch-asap 3)
|
||||
)
|
||||
|
||||
(deftype sparticle-group-item (structure)
|
||||
((launcher uint32 :offset-assert 0)
|
||||
(fade-after meters :offset-assert 4)
|
||||
(falloff-to meters :offset-assert 8)
|
||||
(flags uint16 :offset-assert 12)
|
||||
(flags sp-group-item-flag :offset-assert 12)
|
||||
(period uint16 :offset-assert 14)
|
||||
(length uint16 :offset-assert 16)
|
||||
(offset uint16 :offset-assert 18)
|
||||
@@ -52,13 +297,46 @@
|
||||
:flag-assert #x90000001c
|
||||
)
|
||||
|
||||
(defmacro sp-item (launcher
|
||||
&key (fade-after 0.0)
|
||||
&key (falloff-to 0.0)
|
||||
&key (flags ())
|
||||
&key (period 0)
|
||||
&key (length 0)
|
||||
&key (offset 0)
|
||||
&key (hour-mask 0)
|
||||
&key (binding 0)
|
||||
)
|
||||
`(new 'static 'sparticle-group-item
|
||||
:launcher ,launcher
|
||||
:fade-after ,fade-after
|
||||
:falloff-to ,falloff-to
|
||||
:flags (sp-group-item-flag ,@flags)
|
||||
:period ,period
|
||||
:length ,length
|
||||
:offset ,offset
|
||||
:hour-mask ,hour-mask
|
||||
:binding ,binding
|
||||
)
|
||||
)
|
||||
|
||||
(defenum sp-launch-state-flags
|
||||
:bitfield #t
|
||||
:type uint16
|
||||
(launcher-active 0) ;; active
|
||||
(particles-active 1) ;; wants to launch
|
||||
(bit2 2)
|
||||
)
|
||||
|
||||
(declare-type sparticle-cpuinfo structure)
|
||||
|
||||
(deftype sparticle-launch-state (structure)
|
||||
((group-item sparticle-group-item :offset-assert 0)
|
||||
(flags uint16 :offset-assert 4)
|
||||
(flags sp-launch-state-flags :offset-assert 4)
|
||||
(randomize uint16 :offset-assert 6)
|
||||
(origin vector :offset-assert 8)
|
||||
(sprite3d sprite-vec-data-3d :offset-assert 12)
|
||||
(sprite basic :offset-assert 16)
|
||||
(sprite sparticle-cpuinfo :offset-assert 16)
|
||||
(offset uint32 :offset-assert 20)
|
||||
(accum float :offset-assert 24)
|
||||
(spawn-time uint32 :offset-assert 28)
|
||||
@@ -73,12 +351,20 @@
|
||||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
(defenum sp-group-flag
|
||||
:bitfield #t
|
||||
:type uint16
|
||||
(use-local-clock 0)
|
||||
(always-draw 1)
|
||||
(screen-space 2)
|
||||
)
|
||||
|
||||
(deftype sparticle-launch-group (basic)
|
||||
((length int16 :offset-assert 4)
|
||||
(duration uint16 :offset-assert 6)
|
||||
(linger-duration uint16 :offset-assert 8)
|
||||
(flags uint16 :offset-assert 10)
|
||||
(name basic :offset-assert 12)
|
||||
(flags sp-group-flag :offset-assert 10)
|
||||
(name string :offset-assert 12)
|
||||
(launcher (inline-array sparticle-group-item) :offset-assert 16)
|
||||
(bounds sphere :inline :offset-assert 32)
|
||||
)
|
||||
@@ -106,10 +392,11 @@
|
||||
:flag-assert #xe00000040
|
||||
(:methods
|
||||
(initialize (_type_ sparticle-launch-group process) none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 (_type_ vector) none 11)
|
||||
(deactivate (_type_) none 12)
|
||||
(dummy-13 () none 13)
|
||||
(is-visible? (_type_ vector) symbol 10)
|
||||
(spawn (_type_ vector) object 11)
|
||||
(kill-and-free-particles (_type_) none 12)
|
||||
(kill-particles (_type_) none 13)
|
||||
)
|
||||
)
|
||||
(set! (-> sparticle-launch-control heap-base) (the-as uint 32))
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user