Merge remote-tracking branch 'water111/master' into shrubbery

This commit is contained in:
Tyler Wilding
2022-01-23 19:39:29 -05:00
1539 changed files with 259431 additions and 225948 deletions
@@ -1,7 +1,6 @@
import glob
src_files = glob.glob("./goal_src/**/*.g[cs]", recursive=True)
data_files = glob.glob("./goal_src/**/*.gd", recursive=True)
# Find how many of each have been started
@@ -20,12 +19,6 @@ for f in src_files:
else:
src_files_started = src_files_started + 1
for f in data_files:
with open(f, "r") as temp_file:
line_count = len(temp_file.readlines())
if line_count > 7:
data_files_started = data_files_started + 1
import json
with open('./docs/gh-pages-proj/src/config/progress.json', 'r+', encoding='utf-8') as f:
data = {
@@ -33,9 +26,7 @@ with open('./docs/gh-pages-proj/src/config/progress.json', 'r+', encoding='utf-8
'fileProgress': {
'src_files_total': len(src_files),
'src_files_finished': src_files_finished,
'src_files_started': src_files_started,
'data_files_total': len(data_files),
'data_files_started': data_files_started
'src_files_started': src_files_started
}
}
}
+63
View File
@@ -0,0 +1,63 @@
import glob
import os
from pathlib import Path
import json
galleryLinks = {
'jak1': {
'name': "Jak 1",
'media': [],
},
'jak2': {
'name': "Jak 2",
'media': [],
},
'jak3': {
'name': "Jak 3",
'media': [],
},
'jakx': {
'name': "Jak X",
'media': [],
},
'misc': {
'name': "Miscellaneous",
'media': [],
}
}
def get_links(key, folder_to_search):
if os.path.isdir(folder_to_search):
files = glob.glob(folder_to_search + "/*.png", recursive=True)
files.extend(glob.glob(folder_to_search + "/*.jpg", recursive=True))
files.extend(glob.glob(folder_to_search + "/*.jpeg", recursive=True))
for f in files:
galleryLinks[key]["media"].append({
'fileName': os.path.basename(f),
'timestamp': Path(f).stem.split("_")[1],
'caption': Path(f).stem.split("_")[0].replace("-", " ").title(),
'video': False
})
# get videos potentially
if os.path.exists("{}/videos.json".format(folder_to_search)):
with open("{}/videos.json".format(folder_to_search), 'r') as f:
data = json.load(f)
for video in data:
galleryLinks[key]["media"].append({
'link': video["link"].replace("watch?v=", "embed/"),
'timestamp': video["timestamp"],
'video': True
})
# sort by timestamp
galleryLinks[key]["media"].sort(key=lambda x: x["timestamp"], reverse=True)
get_links('jak1', './docs/gh-pages-proj/src/assets/gallery/jak1')
get_links('jak2', './docs/gh-pages-proj/src/assets/gallery/jak2')
get_links('jak3', './docs/gh-pages-proj/src/assets/gallery/jak3')
get_links('jakx', './docs/gh-pages-proj/src/assets/gallery/jakx')
get_links('misc', './docs/gh-pages-proj/src/assets/gallery/misc')
with open('./docs/gh-pages-proj/src/config/gallery.json', 'r+', encoding='utf-8') as f:
f.seek(0)
json.dump(galleryLinks, f, ensure_ascii=False, indent=2)
f.truncate()
+4 -1
View File
@@ -21,7 +21,10 @@ jobs:
uses: actions/checkout@v2
- name: Update Line Count
run: python ./scripts/update-file-progress.py
run: python ./.github/scripts/update-file-progress.py
- name: Update Gallery Links
run: python ./.github/scripts/update-gallery.py
- name: Update Site
run: |
+1 -1
View File
@@ -48,7 +48,7 @@ jobs:
run: git submodule update --init --recursive -j 2
- name: Install Dependencies
run: choco install nasm
run: Choco-Install -PackageName nasm
- name: Setup Buildcache
uses: mikehardy/buildcache-action@v1.2.2
+3
View File
@@ -31,3 +31,6 @@ gfx_dumps/*
# game stuff
game_config/*
imgui.ini
# website stuff
node_modules/
+32 -3
View File
@@ -48,13 +48,31 @@
}
]
},
"(lwu .., 4\\(..\\).*)(dma-buffer)": {
"filterFileRegex": ".*ir2\\.asm",
"decorations": [
{},
{
"overviewRulerColor": "transparent",
"color": "red"
}
]
},
"(\\d+\\(sp\\))": {
"filterFileRegex": ".*ir2\\.asm",
"decorations": [
{
"overviewRulerColor": "transparent",
"color": "#00ff08"
}
]
},
"(sp, -?\\d+ )": {
"filterFileRegex": ".*ir2\\.asm",
"decorations": [
{
"overviewRulerColor": "transparent",
"color": "#00ff08",
"fontWeight": "bold"
"color": "#00ff08"
}
]
},
@@ -272,7 +290,18 @@
}
]
},
"(WARN:.*)": {
"(WARN: Unsupported.*)": {
"filterFileRegex": ".*ir2\\.asm",
"decorations": [
{
"overviewRulerColor": "#ea00ff",
"color": "#ea00ff",
"fontWeight": "bold",
"filterFileRegex": ".*ir2\\.asm"
}
]
},
"(WARN: (?!Unsupported).*)": {
"filterFileRegex": ".*ir2\\.asm",
"decorations": [
{
+21 -15
View File
@@ -9,6 +9,7 @@
<a href="https://coveralls.io/github/water111/jak-project?branch=master" rel="nofollow"><img src="https://coveralls.io/repos/github/water111/jak-project/badge.svg?branch=master" alt="Coverage Status" style="max-width:100%;"></a>
<a href="https://www.codacy.com/gh/water111/jak-project/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=xTVaser/jak-project&amp;utm_campaign=Badge_Grade" rel="nofollow"><img src="https://app.codacy.com/project/badge/Grade/7c3cdc07523f43aca3433484ebc62ff9" alt="Codacy Badge" style="max-width:100%;"></a>
<a href="https://discord.gg/E7yFpd6w9G"><img src="https://img.shields.io/discord/756287461377703987" alt="Discord"></a>
<a href="https://makeapullrequest.com"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square" alt=PRs Welcome></a>
</p>
## Table of Contents
@@ -17,15 +18,20 @@
- [Table of Contents](#table-of-contents)
- [Project Description](#project-description)
- [Current Status](#current-status)
- [What's Next](#whats-next)
- [Getting Started - Linux (Ubuntu)](#getting-started---linux-ubuntu)
- [Getting Started - Linux (Arch)](#getting-started---linux-arch)
- [Getting Started - Nixpkgs](#getting-started---nixpkgs)
- [Getting Started - Windows](#getting-started---windows)
- [Building and Running the Game](#building-and-running-the-game)
- [Extract Assets](#extract-assets)
- [Build Game](#build-game)
- [Run Game](#run-game)
- [Project Layout](#project-layout)
- [Directory Layout](#directory-layout)
- [More Documentation](#more-documentation)
- [ASan Build](#asan-build)
- [On Windows / Visual Studio](#on-windows--visual-studio)
- [On Windows / Visual Studio](#on-windows--visual-studio)
<!-- tocstop -->
## Project Description
@@ -44,6 +50,8 @@ Our objectives are:
We support both Linux and Windows on x86-64.
We have a Discord server where we discuss development. https://discord.gg/BVEHQmm8
## Current Status
So far, we've decompiled around 341,233 lines of GOAL code, out of an estimated 500,000 total lines and we've started work on an OpenGL renderer. Currently, the main display process (`*dproc*`) runs and sends data to our renderer. We can load textures, text files, and level files. Using keyboard controls, we can open the debug menu and turn on some simple debug visualizations.
@@ -138,14 +146,16 @@ nix-build -A packages.x86_64-linux.jak-asan # package with Clang ASan build
## Getting Started - Windows
Install Visual Studio 2019 and get the C++ and CMake tools via the Visual Studio Installer
Install Visual Studio 2022 and get the `Desktop development with C++` workload during the installation process.
> if you already have visual studio and don't have this installed - open your `Visual Studio Installer` and modify the installation
On Windows, it's recommended to get Scoop to use as a package manager, making the follow steps _much_ easier. Follow the steps on the bottom of the homepage here https://scoop.sh/
Once Scoop is installed, run the following command:
```ps1
scoop install llvm nasm
```sh
scoop install git llvm nasm
```
Initialize the repository's third-party dependencies:
@@ -154,17 +164,13 @@ Initialize the repository's third-party dependencies:
git submodule update --init --recursive
```
Open the project as a CMake project, browse for the root level `CMakeLists.txt`:
Open the project as a CMake project.
![](./docs/markdown/imgs/open-cmake-vs.png)
![](./docs/markdown/imgs/windows/open-project.png)
In the toolbar, you should be able to select an individual component to compile, or combine within the root CMakeLists.txt. In the future we will pre-define configurations to make this easier.
Then build the entire project
![](./docs/markdown/imgs/cmake-build-vs.png)
You may also wish to view the files that pertain to each CMake target, rather than the project as it is normally:
![](./docs/markdown/imgs/cmake-target-view.png)
![](./docs/markdown/imgs/windows/build-all.png)
## Building and Running the Game
@@ -200,7 +206,7 @@ gc> (test-play)
(play :use-vis #t :init-game #f) has been called!
0 #x0 0.0000 0
gc>
gc>
```
Then, in the graphics window, you can use the period key to bring up the debug menu. Controllers also work, using the same mapping as the original game.
+1 -1
View File
@@ -9,7 +9,7 @@ tasks:
ignore_error: true
run-game:
cmds:
- ./out/build/Release/bin/gk.exe -fakeiso -debug
- ./out/build/Release/bin/gk.exe -fakeiso -debug -v
run-game-headless:
cmds:
- ./out/build/Release/bin/gk.exe -fakeiso -debug -nodisplay
+32
View File
@@ -11,6 +11,20 @@ void StripDraw::serialize(Serializer& ser) {
ser.from_ptr(&num_triangles);
}
void InstancedStripDraw::serialize(Serializer& ser) {
ser.from_ptr(&mode);
ser.from_ptr(&tree_tex_id);
ser.from_pod_vector(&vertex_index_stream);
ser.from_pod_vector(&instance_groups);
ser.from_ptr(&num_triangles);
}
void TieWindInstance::serialize(Serializer& ser) {
ser.from_ptr(&matrix);
ser.from_ptr(&wind_idx);
ser.from_ptr(&stiffness);
}
void TfragTree::serialize(Serializer& ser) {
ser.from_ptr(&kind);
@@ -38,6 +52,24 @@ void TieTree::serialize(Serializer& ser) {
draw.serialize(ser);
}
if (ser.is_saving()) {
ser.save<size_t>(instanced_wind_draws.size());
} else {
instanced_wind_draws.resize(ser.load<size_t>());
}
for (auto& draw : instanced_wind_draws) {
draw.serialize(ser);
}
if (ser.is_saving()) {
ser.save<size_t>(instance_info.size());
} else {
instance_info.resize(ser.load<size_t>());
}
for (auto& inst : instance_info) {
inst.serialize(ser);
}
ser.from_pod_vector(&vertices);
ser.from_pod_vector(&colors);
bvh.serialize(ser);
+38 -7
View File
@@ -1,16 +1,17 @@
#pragma once
// Data format for the tfrag3 renderer.
#include <array>
#include "common/common_types.h"
#include "common/util/assert.h"
#include "common/dma/gs.h"
#include "common/util/Serializer.h"
#include "common/math/Vector.h"
#include "common/util/assert.h"
namespace tfrag3 {
constexpr int TFRAG3_VERSION = 7;
constexpr int TFRAG3_VERSION = 9;
// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
@@ -41,8 +42,8 @@ struct StripDraw {
// to do culling, the above vertex stream is grouped.
// by following the visgroups and checking the visibility, you can leave out invisible vertices.
struct VisGroup {
u32 num = 0; // number of vertex indices in this group
u32 vis_idx = 0; // the visibility group they belong to
u32 num = 0; // number of vertex indices in this group
u32 vis_idx_in_pc_bvh = 0; // the visibility group they belong to (in BVH)
};
std::vector<VisGroup> vis_groups;
@@ -51,12 +52,34 @@ struct StripDraw {
void serialize(Serializer& ser);
};
struct InstancedStripDraw {
DrawMode mode; // the OpenGL draw settings.
u32 tree_tex_id = 0; // the texture that should be bound for the draw
// the list of vertices in the draw. This includes the restart code of UINT32_MAX that OpenGL
// will use to start a new strip.
std::vector<u32> vertex_index_stream;
// the vertex stream above is segmented by instance.
struct InstanceGroup {
u32 num = 0; // number of vertex indices in this group
u32 instance_idx = 0; // the instance they belong to
u32 vis_idx = 0;
};
std::vector<InstanceGroup> instance_groups;
// for debug counting.
u32 num_triangles = 0;
void serialize(Serializer& ser);
};
// node in the BVH.
struct VisNode {
math::Vector<float, 4> bsphere; // the bounding sphere, in meters (4096 = 1 game meter). w = rad
u16 child_id = 0xffff; // the ID of our first child.
u8 num_kids = 0xff; // number of children. The children are consecutive in memory
u8 flags = 0; // flags. If 1, we have a DrawVisNode child, otherwise a leaf.
u16 my_id = 0xffff;
u8 num_kids = 0xff; // number of children. The children are consecutive in memory
u8 flags = 0; // flags. If 1, we have a DrawVisNode child, otherwise a leaf.
};
// The leaf nodes don't actually exist in the vector of VisNodes, but instead they are ID's used
@@ -114,6 +137,13 @@ struct TfragTree {
void serialize(Serializer& ser);
};
struct TieWindInstance {
std::array<math::Vector4f, 4> matrix;
u16 wind_idx;
float stiffness;
void serialize(Serializer& ser);
};
// A shrub model
struct ShrubTree {
BVH bvh;
@@ -132,7 +162,8 @@ struct TieTree {
std::vector<PreloadedVertex> vertices; // mesh vertices
std::vector<TimeOfDayColor> colors; // vertex colors (pre-interpolation)
// TODO wind stuff
std::vector<InstancedStripDraw> instanced_wind_draws;
std::vector<TieWindInstance> instance_info;
void serialize(Serializer& ser);
};
+2 -2
View File
@@ -7,8 +7,8 @@
#include <string>
#include <cstring>
#include "common/util/assert.h"
#include "common/common_types.h"
#include "common/util/assert.h"
struct DmaStats {
double sync_time_ms = 0;
@@ -117,4 +117,4 @@ struct VifCodeUnpack {
u16 addr_qw;
bool is_unsigned; // only care for 8/16 bit data.
bool use_tops_flag; // uses double buffering
};
};
+8 -1
View File
@@ -339,7 +339,7 @@ struct AdGifData {
u64 tex1_addr;
u64 mip_data;
u64 mip_addr;
u64 clamp_data;
u64 clamp_data; // can also be zbuf!!
u64 clamp_addr;
u64 alpha_data;
u64 alpha_addr;
@@ -366,6 +366,13 @@ class DrawMode {
bool get_depth_write_enable() const { return m_val & 0b1; }
void enable_depth_write() { m_val = m_val | 0b1; }
void disable_depth_write() { m_val = m_val & ~(0b1); }
void set_depth_write_enable(bool x) {
if (x) {
enable_depth_write();
} else {
disable_depth_write();
}
}
GsTest::ZTest get_depth_test() const { return (GsTest::ZTest)((m_val >> 1) & 0b11); }
void set_depth_test(GsTest::ZTest dt) { m_val = (m_val & ~(0b110)) | ((u32)(dt) << 1); }
+1 -1
View File
@@ -7,7 +7,7 @@
#include "Interpreter.h"
#include "ParseHelpers.h"
#include "common/util/FileUtil.h"
#include <third-party/fmt/core.h>
#include "third-party/fmt/core.h"
namespace goos {
Interpreter::Interpreter(const std::string& username) {
+1 -1
View File
@@ -41,7 +41,6 @@
*/
#include <string>
#include "common/util/assert.h"
#include <memory>
#include <unordered_map>
#include <unordered_set>
@@ -50,6 +49,7 @@
#include <stdexcept>
#include <map>
#include "common/common_types.h"
#include "common/util/assert.h"
namespace goos {
+1 -1
View File
@@ -4,7 +4,6 @@
* It is not very good, but significantly better than putting everything on one line
*/
#include "common/util/assert.h"
#include <stdexcept>
#include <utility>
#include <cstring>
@@ -14,6 +13,7 @@
#include "common/log/log.h"
#include "common/goos/PrettyPrinter2.h"
#include "common/util/assert.h"
namespace pretty_print {
+29 -10
View File
@@ -1,7 +1,7 @@
#include "PrettyPrinter2.h"
#include "common/common_types.h"
#include "common/util/assert.h"
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
namespace pretty_print {
@@ -11,9 +11,6 @@ namespace v2 {
// The previous issues we had with stack overflow only happened when there was a stack frame per
// element in a list.
// TODO: there's a different style of splitting that we should do for forms like:
// set!, and, or, <, >, +... where we try leaving operator + one other.
// The main node type.
// unlike v1, this nests lists.
// these have pointers to parents, so generally not safe to copy.
@@ -71,6 +68,19 @@ struct Node {
return false;
}
std::string debug_to_string() const {
switch (kind) {
case Kind::ATOM:
return fmt::format("[atom {}]", atom_str);
case Kind::LIST:
return "[list]";
case Kind::IMPROPER_LIST:
return "[improper list]";
default:
assert(false);
}
}
// how wide is this text? not including the indentation of this subtree.
u32 text_len = 0;
@@ -193,7 +203,8 @@ void break_list(Node* node) {
node->top_line_count = 1;
const std::unordered_set<std::string> sameline_splitters = {
"if", "<", ">", "<=", ">=", "set!", "=", "!=", "+", "-", "*", "/", "the", "->"};
"if", "<", ">", "<=", ">=", "set!", "=", "!=", "+", "-", "*",
"/", "the", "->", "and", "or", "logand", "logior", "logxor", "+!", "*!", "logtest?"};
if (node->child_nodes.at(0).kind == Node::Kind::LIST) {
// ((foo
@@ -208,7 +219,7 @@ void break_list(Node* node) {
// things with 4 things in the top line: (defmethod <method> <type> <args>
node->top_line_count = 4;
} else if (name == "until" || name == "while" || name == "dotimes" || name == "countdown" ||
name == "when" || name == "behavior" || name == "lambda") {
name == "when" || name == "behavior" || name == "lambda" || name == "defpart") {
node->top_line_count = 2;
} else if (name == "let" || name == "let*" || name == "rlet") {
// special case for things like let.
@@ -253,9 +264,9 @@ void break_list(Node* node) {
void insert_required_breaks(const std::vector<Node*>& bfs_order) {
const std::unordered_set<std::string> always_break = {
"when", "defun-debug", "countdown", "case", "defun", "defmethod",
"let", "until", "while", "if", "dotimes", "cond",
"else", "defbehavior", "with-pp", "rlet", "defstate"};
"when", "defun-debug", "countdown", "case", "defun", "defmethod", "let",
"until", "while", "if", "dotimes", "cond", "else", "defbehavior",
"with-pp", "rlet", "defstate", "behavior", "defpart"};
for (auto node : bfs_order) {
if (!node->break_list && node->kind == Node::Kind::LIST &&
node->child_nodes.at(0).kind == Node::Kind::ATOM) {
@@ -328,18 +339,26 @@ void append_node_to_string(const Node* node,
int listing_indent = next_indent_level + node->quoted + node->sub_elt_indent;
int extra_indent = 0;
int old_indent = listing_indent;
if (node->top_line_count) {
listing_indent -= node->sub_elt_indent;
listing_indent += node->child_nodes.front().kind == Node::Kind::LIST ? 1 : 2;
}
for (; node_idx < node->top_line_count; node_idx++) {
size_t s0 = str.length();
if (node->kind == Node::Kind::IMPROPER_LIST &&
&node->child_nodes.at(node_idx) == &node->child_nodes.back()) {
str.append(". ");
}
// so, if these need to break, they should have a bigger indent.
append_node_to_string(&node->child_nodes.at(node_idx), str, 0,
listing_indent + extra_indent);
// extra_indent += (str.length() - s0);
extra_indent = compute_extra_offset(str, s0, extra_indent);
str.push_back(' ');
}
if (node->top_line_count) {
listing_indent = old_indent;
}
if (node->top_line_count > 0) {
str.pop_back();
}
+4 -2
View File
@@ -217,9 +217,11 @@ std::optional<Object> Reader::read_from_stdin(const std::string& prompt, ReplWra
/*!
* Read a string.
*/
Object Reader::read_from_string(const std::string& str, bool add_top_level) {
Object Reader::read_from_string(const std::string& str,
bool add_top_level,
const std::optional<std::string>& string_name) {
// create text fragment and add to the DB
auto textFrag = std::make_shared<ProgramString>(str);
auto textFrag = std::make_shared<ProgramString>(str, string_name.value_or("Program string"));
db.insert(textFrag);
// perform read
+5 -2
View File
@@ -12,7 +12,6 @@
*/
#include <memory>
#include "common/util/assert.h"
#include <utility>
#include <unordered_map>
#include <optional>
@@ -22,6 +21,8 @@
#include "ReplUtils.h"
#include "common/util/assert.h"
namespace goos {
/*!
@@ -71,7 +72,9 @@ struct Token {
class Reader {
public:
Reader();
Object read_from_string(const std::string& str, bool add_top_level = true);
Object read_from_string(const std::string& str,
bool add_top_level = true,
const std::optional<std::string>& string_name = {});
std::optional<Object> read_from_stdin(const std::string& prompt, ReplWrapper& repl);
Object read_from_file(const std::vector<std::string>& file_path, bool check_encoding = false);
bool check_string_is_valid(const std::string& str) const;
+7 -2
View File
@@ -65,9 +65,14 @@ class ReplText : public SourceText {
*/
class ProgramString : public SourceText {
public:
explicit ProgramString(const std::string& text_) : SourceText(text_) {}
std::string get_description() override { return "Program string"; }
explicit ProgramString(const std::string& text_,
const std::string& string_name = "Program string")
: SourceText(text_), m_string_name(string_name) {}
std::string get_description() override { return m_string_name; }
~ProgramString() = default;
private:
std::string m_string_name;
};
/*!
+1 -1
View File
@@ -1,12 +1,12 @@
#include <cstdio>
#include <cstdlib>
#include "common/util/assert.h"
#include <mutex>
#include "third-party/fmt/color.h"
#include "log.h"
#ifdef _WIN32 // see lg::initialize
#include <Windows.h>
#endif
#include "common/util/assert.h"
namespace lg {
struct Logger {
+14 -7
View File
@@ -4,9 +4,9 @@
*/
#include <stdexcept>
#include "common/util/assert.h"
#include <third-party/fmt/core.h>
#include "third-party/fmt/core.h"
#include "Type.h"
#include "common/util/assert.h"
namespace {
std::string reg_kind_to_string(RegClass kind) {
@@ -696,9 +696,9 @@ StructureType::StructureType(std::string parent,
std::string StructureType::print() const {
std::string result = fmt::format(
"[StructureType] {}\n parent: {}\n boxed: {}\n dynamic: {}\n size: {}\n pack: {}\n misalign: "
"{}\n heap-base: {}\n fields:\n",
m_name, m_parent, m_is_boxed, m_dynamic, m_size_in_mem, m_pack, m_allow_misalign,
m_heap_base);
"{}\n heap-base: {}\n stack-singleton: {}\n fields:\n",
m_name, m_parent, m_is_boxed, m_dynamic, m_size_in_mem, m_pack, m_allow_misalign, m_heap_base,
m_always_stack_singleton);
for (auto& x : m_fields) {
result += " " + x.print() + "\n";
}
@@ -727,7 +727,8 @@ bool StructureType::operator==(const Type& other) const {
m_pack == p_other->m_pack &&
m_allow_misalign == p_other->m_allow_misalign &&
m_offset == p_other->m_offset &&
m_idx_of_first_unique_field == p_other->m_idx_of_first_unique_field;
m_idx_of_first_unique_field == p_other->m_idx_of_first_unique_field &&
m_always_stack_singleton == p_other->m_always_stack_singleton;
// clang-format on
}
@@ -773,6 +774,11 @@ std::string StructureType::diff_structure_common(const StructureType& other) con
result += fmt::format("allow_misalign: {} vs. {}\n", m_allow_misalign, other.m_allow_misalign);
}
if (m_always_stack_singleton != other.m_always_stack_singleton) {
result += fmt::format("always_stack_singleton: {} vs. {}\n", m_always_stack_singleton,
other.m_always_stack_singleton);
}
if (m_offset != other.m_offset) {
result += fmt::format("offset: {} vs. {}\n", m_offset, other.m_offset);
}
@@ -906,7 +912,8 @@ bool BasicType::operator==(const Type& other) const {
m_allow_misalign == p_other->m_allow_misalign &&
m_offset == p_other->m_offset &&
m_idx_of_first_unique_field == p_other->m_idx_of_first_unique_field &&
m_final == p_other->m_final;
m_final == p_other->m_final &&
m_always_stack_singleton == p_other->m_always_stack_singleton;
// clang-format on
}
+4 -1
View File
@@ -7,10 +7,10 @@
#include <string>
#include <map>
#include "common/util/assert.h"
#include <unordered_map>
#include "common/goal_constants.h"
#include "TypeSpec.h"
#include "common/util/assert.h"
class TypeSystem;
@@ -275,9 +275,11 @@ class StructureType : public ReferenceType {
bool is_dynamic() const { return m_dynamic; }
~StructureType() = default;
void set_pack(bool pack) { m_pack = pack; }
void set_always_stack_singleton() { m_always_stack_singleton = true; }
void set_heap_base(int hb) { m_heap_base = hb; }
bool is_packed() const { return m_pack; }
bool is_allowed_misalign() const { return m_allow_misalign; };
bool is_always_stack_singleton() const { return m_always_stack_singleton; }
void set_allow_misalign(bool misalign) { m_allow_misalign = misalign; }
void set_gen_inspect(bool gen_inspect) { m_generate_inspect = gen_inspect; }
@@ -300,6 +302,7 @@ class StructureType : public ReferenceType {
bool m_pack = false;
bool m_allow_misalign = false;
int m_offset = 0;
bool m_always_stack_singleton = false;
size_t m_idx_of_first_unique_field = 0;
};
+1 -1
View File
@@ -8,8 +8,8 @@
#include <vector>
#include <string>
#include <optional>
#include "common/util/assert.h"
#include "common/util/SmallVector.h"
#include "common/util/assert.h"
/*!
* A :name value modifier to apply to a type.
+7 -4
View File
@@ -5,12 +5,12 @@
* access types, and reverse type lookups.
*/
#include "common/util/assert.h"
#include "third-party/fmt/core.h"
#include "third-party/fmt/color.h"
#include <stdexcept>
#include <third-party/fmt/core.h>
#include "TypeSystem.h"
#include "common/util/math_util.h"
#include "third-party/fmt/color.h"
#include "common/util/assert.h"
namespace {
template <typename... Args>
@@ -1615,7 +1615,10 @@ std::string TypeSystem::generate_deftype_footer(const Type* type) const {
result.append(" :pack-me\n");
}
if (as_structure->is_allowed_misalign()) {
result.append(" :allow-misaligned");
result.append(" :allow-misaligned\n");
}
if (as_structure->is_always_stack_singleton()) {
result.append(" :always-stack-singleton\n");
}
}
+13
View File
@@ -277,6 +277,7 @@ struct StructureDefResult {
bool pack_me = false;
bool allow_misaligned = false;
bool final = false;
bool always_stack_singleton = false;
};
StructureDefResult parse_structure_def(StructureType* type,
@@ -347,6 +348,8 @@ StructureDefResult parse_structure_def(StructureType* type,
result.allow_misaligned = true;
} else if (opt_name == ":final") {
result.final = true;
} else if (opt_name == ":always-stack-singleton") {
result.always_stack_singleton = true;
} else {
throw std::runtime_error("Invalid option in field specification: " + opt_name);
}
@@ -572,6 +575,13 @@ DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
name);
throw std::runtime_error("invalid pack option on basic");
}
if (sr.always_stack_singleton) {
fmt::print(
"[TypeSystem] :always-stack-singleton was set on {}, which is a basic and cannot "
"be a stack singleton\n",
name);
throw std::runtime_error("invalid stack singleton option on basic");
}
new_type->set_heap_base(result.flags.heap_base);
if (sr.final) {
new_type->set_final();
@@ -592,6 +602,9 @@ DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
if (sr.allow_misaligned) {
new_type->set_allow_misalign(true);
}
if (sr.always_stack_singleton) {
new_type->set_always_stack_singleton();
}
if (sr.final) {
throw std::runtime_error(
fmt::format("[TypeSystem] :final option cannot be used on structure type {}", name));
+2 -2
View File
@@ -7,9 +7,9 @@
#include <cstdint>
#include <cstring>
#include "common/util/assert.h"
#include "common/common_types.h"
#include <vector>
#include "common/common_types.h"
#include "common/util/assert.h"
class BinaryReader {
public:
+1 -1
View File
@@ -5,11 +5,11 @@
* Write raw data like a stream.
*/
#include "common/util/assert.h"
#include <stdexcept>
#include <vector>
#include <cstdint>
#include <cstring>
#include "common/util/assert.h"
struct BinaryWriterRef {
size_t offset;
+2 -2
View File
@@ -1,9 +1,9 @@
#pragma once
#include <optional>
#include "common/util/assert.h"
#include "common/util/Range.h"
#include "common/common_types.h"
#include "common/util/assert.h"
constexpr int BITS_PER_BYTE = 8;
template <typename T>
@@ -93,4 +93,4 @@ inline u32 count_leading_zeros_u32(u32 in) {
_BitScanReverse(&result, in);
return result;
#endif
}
}
+1 -1
View File
@@ -9,7 +9,6 @@
#include <cstdio> /* defines FILENAME_MAX */
#include <fstream>
#include <sstream>
#include "common/util/assert.h"
#include <cstdlib>
#include "common/util/BinaryReader.h"
#include "BinaryWriter.h"
@@ -24,6 +23,7 @@
#include <unistd.h>
#include <cstring>
#endif
#include "common/util/assert.h"
namespace file_util {
std::filesystem::path get_user_home_dir() {
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "common/util/Timer.h"
class FrameLimiter {
public:
void run(double target_fps, bool experimental_accurate_lag, double engine_time) {
double target_seconds;
if (experimental_accurate_lag) {
target_seconds = round_to_nearest_60fps(engine_time);
} else {
target_seconds = 1.f / target_fps;
}
double remaining_time = target_seconds - m_timer.getSeconds();
while (remaining_time > 0) {
if (remaining_time > 0.003) {
std::this_thread::sleep_for(std::chrono::microseconds(int(remaining_time * 1e6 * 0.5)));
}
remaining_time = target_seconds - m_timer.getSeconds();
}
m_timer.start();
}
private:
double round_to_nearest_60fps(double current) {
double one_frame = 1.f / 60.f;
int frames_missed = (current / one_frame); // rounds down
if (frames_missed > 4) {
frames_missed = 4;
}
return (frames_missed + 1) * one_frame;
}
Timer m_timer;
};
+2 -2
View File
@@ -1,9 +1,9 @@
#pragma once
#include "common/util/assert.h"
#include <cstring>
#include <string>
#include <vector>
#include "common/util/assert.h"
/*!
* The Serializer is a tool to load or save data from a buffer.
@@ -216,4 +216,4 @@ class Serializer {
size_t m_size = 0;
size_t m_offset = 0;
bool m_writing = false;
};
};
+1 -1
View File
@@ -1,11 +1,11 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <iterator>
#include <new>
#include <type_traits>
#include <utility>
#include "assert.h"
namespace cu {
// This might seem stupid, but compiling an empty file with #include <algorithm> takes 0.5 seconds.
+1 -6
View File
@@ -1,11 +1,8 @@
#pragma once
#ifndef JAK_V2_TIMER_H
#define JAK_V2_TIMER_H
#include "common/util/assert.h"
#include <cstdint>
#include <ctime>
#include "common/util/assert.h"
/*!
* Timer for measuring time elapsed with clock_monotonic
@@ -45,5 +42,3 @@ class Timer {
struct timespec _startTime = {};
};
#endif // JAK_V2_TIMER_H
+2 -2
View File
@@ -1,8 +1,8 @@
#pragma once
#include "common/util/assert.h"
#include <vector>
#include <string>
#include "common/util/assert.h"
/*!
* A simple prefix tree. It works similarly to a map, but also supports fast lookups by prefix with
@@ -182,4 +182,4 @@ T* Trie<T>::operator[](const std::string& str) {
template <typename T>
std::vector<T*> Trie<T>::lookup_prefix(const std::string& str) const {
return m_root.lookup_prefix(str.c_str());
}
}
+3 -8
View File
@@ -1,24 +1,19 @@
#pragma once
/*!
* @file assert.h
* Wrapper around <cassert>.
* Make sure this file is always the last one included.
*/
#if defined NDEBUG && defined _WIN32
#pragma push_macro("NDEBUG")
#if defined NDEBUG
#undef NDEBUG
#undef assert
#include <cassert>
#pragma pop_macro("NDEBUG")
#define NDEBUG 1
#else
#include <cassert>
#endif
#define ASSERT assert
+2 -2
View File
@@ -1,8 +1,8 @@
#include "common/util/assert.h"
#include <cstring>
#include "dgo_util.h"
#include "common/versions.h"
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
/*!
* Assert false if the char[] has non-null data after the null terminated string.
@@ -50,4 +50,4 @@ std::string get_object_file_name(const std::string& original_name, u8* data, int
}
return original_name;
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
#include "common/log/log.h"
#include "common/util/assert.h"
#include "json_util.h"
#include "common/util/assert.h"
/*!
* Strip out // and / * comments
@@ -107,4 +107,4 @@ Range<int> parse_json_optional_integer_range(const nlohmann::json& json) {
} else {
throw std::runtime_error("Invalid json as input to parse_json_optional_integer_range");
}
}
}
+9 -1
View File
@@ -1,7 +1,7 @@
#include <cmath>
#include "third-party/fmt/core.h"
#include "common/common_types.h"
#include "common/goal_constants.h"
#include "third-party/dragonbox.h"
#include "print_float.h"
#include "common/util/assert.h"
@@ -19,6 +19,14 @@ std::string float_to_string(float value, bool append_trailing_decimal) {
return {buff};
}
/*!
* Wrapper around float_to_string, for printing meters. Unlike float_to_string, it does not append
* decimals by default.
*/
std::string meters_to_string(float value, bool append_trailing_decimal) {
return float_to_string(value / METER_LENGTH, append_trailing_decimal);
}
int float_to_cstr(float value, char* buffer, bool append_trailing_decimal) {
assert(std::isfinite(value));
// dragonbox gives us:
+2 -1
View File
@@ -3,4 +3,5 @@
#include <string>
std::string float_to_string(float value, bool append_trailing_decimal = true);
int float_to_cstr(float value, char* buffer, bool append_trailing_decimal = true);
std::string meters_to_string(float value, bool append_trailing_decimal = false);
int float_to_cstr(float value, char* buffer, bool append_trailing_decimal = true);
+1
View File
@@ -6,6 +6,7 @@ add_library(
analysis/cfg_builder.cpp
analysis/expression_build.cpp
analysis/final_output.cpp
analysis/find_defpartgroup.cpp
analysis/find_defstates.cpp
analysis/find_skelgroups.cpp
analysis/inline_asm_rewrite.cpp
+1 -1
View File
@@ -5,8 +5,8 @@
*/
#include "InstructionDecode.h"
#include "common/util/assert.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "common/util/assert.h"
namespace decompiler {
// utility class to extract fields of an opcode.
+1 -1
View File
@@ -3,8 +3,8 @@
* Utilities for checking if an instruction matches some criteria.
*/
#include "common/util/assert.h"
#include "InstructionMatching.h"
#include "common/util/assert.h"
namespace decompiler {
/*!
+2 -2
View File
@@ -1,9 +1,9 @@
#include "common/util/assert.h"
#include <algorithm>
#include <stdexcept>
#include <optional>
#include "common/common_types.h"
#include "InstructionParser.h"
#include "common/util/assert.h"
namespace decompiler {
InstructionParser::InstructionParser() {
@@ -487,4 +487,4 @@ std::string ParsedProgram::print() {
}
return result;
}
} // namespace decompiler
} // namespace decompiler
+1 -1
View File
@@ -4,9 +4,9 @@
*/
#include "Register.h"
#include "common/util/assert.h"
#include <stdexcept>
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
namespace decompiler {
namespace Reg {
+1 -1
View File
@@ -6,8 +6,8 @@
*/
#include <cstdint>
#include "common/util/assert.h"
#include <string>
#include "common/util/assert.h"
namespace decompiler {
// Namespace for register name constants
+1 -1
View File
@@ -1,8 +1,8 @@
#include <algorithm>
#include "common/util/assert.h"
#include "BasicBlocks.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "common/util/assert.h"
namespace decompiler {
/*!
+38 -1
View File
@@ -1,9 +1,9 @@
#include "common/util/assert.h"
#include "common/goos/PrettyPrinter.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "CfgVtx.h"
#include "Function.h"
#include "common/util/assert.h"
namespace decompiler {
/////////////////////////////////////////
@@ -1247,6 +1247,43 @@ bool ControlFlowGraph::clean_up_asm_branches() {
old_seq->parent_claim(seq);
bds->parent_claim(seq);
return false;
} else if (b0_seq && !b1_seq) {
replaced = true;
m_blocks.at(bds->succ_branch->get_first_block_id())->needs_label = true;
auto* seq = dynamic_cast<SequenceVtx*>(b0);
assert(seq);
if (b0->succ_branch) {
b0->succ_branch->replace_preds_with_and_check({b0}, nullptr);
}
if (bds->succ_branch) {
// likely delay slots "branch" in this graph.
bds->succ_branch->replace_preds_with_and_check({bds}, nullptr);
}
seq->seq.push_back(bds);
seq->seq.push_back(b1);
for (auto* x : b1->succs()) {
// printf("fix preds of %s\n", x->to_string().c_str());
x->replace_pred_and_check(b1, seq);
}
seq->succ_branch = b1->succ_branch;
seq->succ_ft = b1->succ_ft;
seq->end_branch = b1->end_branch;
seq->next = b1->next;
if (seq->next) {
seq->next->prev = seq;
}
// todo - proper trash?
b1->parent_claim(seq);
bds->parent_claim(seq);
return false;
}
-4
View File
@@ -1,8 +1,5 @@
#pragma once
#ifndef JAK_DISASSEMBLER_CFGVTX_H
#define JAK_DISASSEMBLER_CFGVTX_H
#include <string>
#include <vector>
#include "common/util/assert.h"
@@ -394,4 +391,3 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file,
const CondWithElseLengthHack& cond_with_else_hack,
const std::unordered_set<int>& blocks_ending_in_asm_br);
} // namespace decompiler
#endif // JAK_DISASSEMBLER_CFGVTX_H
+4 -3
View File
@@ -1,4 +1,3 @@
#include "common/util/assert.h"
#include <vector>
#include "Function.h"
#include "common/log/log.h"
@@ -9,6 +8,7 @@
#include "decompiler/IR/IR.h"
#include "decompiler/IR2/Form.h"
#include "common/util/BitUtils.h"
#include "common/util/assert.h"
namespace decompiler {
namespace {
@@ -88,7 +88,8 @@ void Function::analyze_prologue(const LinkedObjectFile& file) {
// storing s7 on the stack is done by interrupt handlers, which we probably don't want to
// support
if (instr.kind == InstructionKind::SD && instr.get_src(0).get_reg() == make_gpr(Reg::S7)) {
if (instr.kind == InstructionKind::SD && instr.get_src(0).get_reg() == make_gpr(Reg::S7) &&
instr.get_src(2).get_reg() == make_gpr(Reg::SP)) {
lg::warn(
"Function {} was flagged as asm due to this instruction: {}. Consider flagging as asm "
"in config!",
@@ -765,4 +766,4 @@ BlockTopologicalSort Function::bb_topo_sort() {
std::string Function::name() const {
return guessed_name.to_string();
}
} // namespace decompiler
} // namespace decompiler
+2 -3
View File
@@ -2,9 +2,8 @@
#include <string>
#include <vector>
#include <stdexcept>
#include "common/util/assert.h"
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
namespace decompiler {
class DecompWarnings {
@@ -110,4 +109,4 @@ class DecompWarnings {
std::vector<Warning> m_warnings;
bool m_used_lq_sq = false;
};
} // namespace decompiler
} // namespace decompiler
+3 -1
View File
@@ -1,7 +1,8 @@
#pragma once
#ifndef JAK_IR_H
#define JAK_IR_H
#include "common/util/assert.h"
#include <utility>
#include <memory>
#include <unordered_map>
@@ -10,6 +11,7 @@
#include "common/type_system/TypeSpec.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "decompiler/util/TP_Type.h"
#include "common/util/assert.h"
namespace goos {
class Object;
+11 -2
View File
@@ -1,4 +1,3 @@
#include "common/util/assert.h"
#include <utility>
#include <stdexcept>
#include "common/goal_constants.h"
@@ -8,6 +7,7 @@
#include "AtomicOp.h"
#include "OpenGoalMapping.h"
#include "Form.h"
#include "common/util/assert.h"
namespace decompiler {
/////////////////////////////
@@ -306,6 +306,9 @@ std::string get_simple_expression_op_name(SimpleExpression::Kind kind) {
return "vec3dot";
case SimpleExpression::Kind::VECTOR_4_DOT:
return "vec4dot";
case SimpleExpression::Kind::SET_ON_LESS_THAN:
case SimpleExpression::Kind::SET_ON_LESS_THAN_IMM:
return "set-on-less-than";
default:
assert(false);
return {};
@@ -367,6 +370,9 @@ int get_simple_expression_arg_count(SimpleExpression::Kind kind) {
case SimpleExpression::Kind::VECTOR_3_DOT:
case SimpleExpression::Kind::VECTOR_4_DOT:
return 2;
case SimpleExpression::Kind::SET_ON_LESS_THAN:
case SimpleExpression::Kind::SET_ON_LESS_THAN_IMM:
return 2;
default:
assert(false);
return -1;
@@ -512,7 +518,10 @@ AsmOp::AsmOp(Instruction instr, int my_idx) : AtomicOp(my_idx), m_instr(std::mov
if (src.is_reg()) {
auto reg = src.get_reg();
if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR || reg.get_kind() == Reg::VF) {
m_src[i] = RegisterAccess(AccessMode::READ, reg, my_idx, true);
if (reg != Register(Reg::GPR, Reg::R0) ||
(m_instr.kind == InstructionKind::PCPYUD || m_instr.kind == InstructionKind::PEXTUW)) {
m_src[i] = RegisterAccess(AccessMode::READ, reg, my_idx, true);
}
}
}
}
+4 -2
View File
@@ -2,13 +2,13 @@
#include <string>
#include <optional>
#include "common/util/assert.h"
#include <utility>
#include "common/goos/Object.h"
#include "decompiler/Disasm/Register.h"
#include "decompiler/Disasm/Instruction.h"
#include "decompiler/IR2/IR2_common.h"
#include "Env.h"
#include "common/util/assert.h"
namespace decompiler {
class FormElement;
@@ -235,7 +235,9 @@ class SimpleExpression {
VECTOR_CROSS,
SUBU_L32_S7, // use SUBU X, src0, s7 to check if lower 32-bits are s7.
VECTOR_3_DOT,
VECTOR_4_DOT
VECTOR_4_DOT,
SET_ON_LESS_THAN,
SET_ON_LESS_THAN_IMM
};
// how many arguments?
+7 -5
View File
@@ -437,7 +437,8 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
return TP_Type::make_from_ts(TypeSpec("int"));
}
if (arg0_type.is_product_with(4) && tc(dts, TypeSpec("type"), arg1_type)) {
if (arg0_type.is_product_with(4) && tc(dts, TypeSpec("type"), arg1_type) &&
env.func->name() != "overrides-parent-method?") {
// dynamic access into the method array with shift, add, offset-load
// no need to track the type because we don't know the method index anyway.
return TP_Type::make_partial_dyanmic_vtable_access();
@@ -621,12 +622,12 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
return TP_Type::make_from_ts(arg0_type.typespec());
}
if (m_kind == Kind::ADD && tc(dts, TypeSpec("structure"), arg0_type) &&
if ((m_kind == Kind::ADD || m_kind == Kind::SUB) && 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()) {
// get next in memory, allow this as &+/&-
if ((s64)type_info->get_size_in_memory() == std::abs((s64)arg1_type.get_integer_constant())) {
return TP_Type::make_from_ts(arg0_type.typespec());
}
@@ -827,7 +828,8 @@ TypeState AsmOp::propagate_types_internal(const TypeState& input,
}
// sllv out, in, r0
if (m_instr.kind == InstructionKind::SLLV && m_src[1]->reg() == Register(Reg::GPR, Reg::R0)) {
if (m_instr.kind == InstructionKind::SLLV &&
instruction().src[1].is_reg(Register(Reg::GPR, Reg::R0))) {
auto type = dts.ts.lookup_type(result.get(m_src[0]->reg()).typespec());
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
if (as_bitfield) {
+1 -1
View File
@@ -1,7 +1,7 @@
#include <stdexcept>
#include <unordered_set>
#include <algorithm>
#include <decompiler/util/DecompilerTypeSystem.h>
#include "decompiler/util/DecompilerTypeSystem.h"
#include "Env.h"
#include "Form.h"
#include "decompiler/analysis/atomic_op_builder.h"
+3 -3
View File
@@ -2,14 +2,14 @@
#include <string>
#include <vector>
#include "common/util/assert.h"
#include <common/goos/Object.h>
#include "common/goos/Object.h"
#include "decompiler/util/TP_Type.h"
#include "decompiler/util/StackSpillMap.h"
#include "decompiler/Disasm/Register.h"
#include "decompiler/IR2/IR2_common.h"
#include "decompiler/analysis/reg_usage.h"
#include "decompiler/config.h"
#include "common/util/assert.h"
namespace decompiler {
class LinkedObjectFile;
@@ -234,4 +234,4 @@ class Env {
StackSpillMap m_stack_spill_map;
};
} // namespace decompiler
} // namespace decompiler
+151 -10
View File
@@ -7,6 +7,7 @@
#include "common/type_system/TypeSystem.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "decompiler/util/data_decompile.h"
#include "decompiler/util/sparticle_decompile.h"
#include "common/util/print_float.h"
namespace decompiler {
@@ -2985,21 +2986,18 @@ goos::Object DefskelgroupElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> lod_forms;
for (const auto& e : m_info.lods) {
auto f_dist = pretty_print::to_symbol(fmt::format(
"(meters {})", float_to_string(e.lod_dist->to_form(env).as_float() / METER_LENGTH, false)));
auto f_dist = pretty_print::to_symbol(
fmt::format("(meters {})", meters_to_string(e.lod_dist->to_form(env).as_float())));
lod_forms.push_back(pretty_print::build_list(e.mgeo->to_form(env), f_dist));
}
forms.push_back(pretty_print::build_list(lod_forms));
forms.push_back(pretty_print::to_symbol(fmt::format(
":bounds (static-spherem {} {} {} {})", meters_to_string(m_static_info.bounds.x()),
meters_to_string(m_static_info.bounds.y()), meters_to_string(m_static_info.bounds.z()),
meters_to_string(m_static_info.bounds.w()))));
forms.push_back(pretty_print::to_symbol(
fmt::format(":bounds (static-spherem {} {} {} {})",
float_to_string(m_static_info.bounds.x() / METER_LENGTH, false),
float_to_string(m_static_info.bounds.y() / METER_LENGTH, false),
float_to_string(m_static_info.bounds.z() / METER_LENGTH, false),
float_to_string(m_static_info.bounds.w() / METER_LENGTH, false))));
forms.push_back(pretty_print::to_symbol(
fmt::format(":longest-edge (meters {})",
float_to_string(m_static_info.longest_edge / METER_LENGTH, false))));
fmt::format(":longest-edge (meters {})", meters_to_string(m_static_info.longest_edge))));
if (m_static_info.shadow != 0) {
forms.push_back(pretty_print::to_symbol(fmt::format(":shadow {}", m_static_info.shadow)));
@@ -3018,6 +3016,149 @@ goos::Object DefskelgroupElement::to_form_internal(const Env& env) const {
return pretty_print::build_list(forms);
}
////////////////////////////////
// DefpartgroupElement
////////////////////////////////
DefpartgroupElement::DefpartgroupElement(const StaticInfo& data, int group_id)
: m_static_info(data), m_group_id(group_id) {}
void DefpartgroupElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}
void DefpartgroupElement::apply_form(const std::function<void(Form*)>&) {}
void DefpartgroupElement::collect_vars(RegAccessSet&, bool) const {}
void DefpartgroupElement::get_modified_regs(RegSet&) const {}
goos::Object DefpartgroupElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol(fmt::format("defpartgroup {}", name())));
forms.push_back(pretty_print::to_symbol(fmt::format(":id {}", m_group_id)));
if (m_static_info.duration != 3000) {
forms.push_back(pretty_print::to_symbol(fmt::format(":duration {}", m_static_info.duration)));
}
if (m_static_info.linger != 1500) {
forms.push_back(
pretty_print::to_symbol(fmt::format(":linger-duration {}", m_static_info.linger)));
}
if (m_static_info.flags != 0) {
auto things = decompile_bitfield_enum_from_int(TypeSpec("sp-group-flag"), env.dts->ts,
m_static_info.flags);
std::string result = ":flags (";
for (auto& thing : things) {
result += thing;
result += ' ';
}
result.pop_back();
result += ')';
forms.push_back(pretty_print::to_symbol(result));
}
forms.push_back(pretty_print::to_symbol(fmt::format(
":bounds (static-bspherem {} {} {} {})", meters_to_string(m_static_info.bounds.x()),
meters_to_string(m_static_info.bounds.y()), meters_to_string(m_static_info.bounds.z()),
meters_to_string(m_static_info.bounds.w()))));
std::vector<goos::Object> item_forms;
for (const auto& e : m_static_info.elts) {
s32 launcher = e.part_id;
u16 flags = e.flags;
u16 period = e.period;
u16 length = e.length;
u16 offset = e.offset;
u32 hour_mask = e.hour_mask;
u32 binding = e.binding;
std::string result =
fmt::format("(sp-item {}", launcher); // use decimal, so it matches array idx
if (e.fade != 0.0) {
result += fmt::format(" :fade-after (meters {})", meters_to_string(e.fade));
}
if (e.falloff != 0.0) {
result += fmt::format(" :falloff-to (meters {})", meters_to_string(e.falloff));
}
if (flags) {
auto things =
decompile_bitfield_enum_from_int(TypeSpec("sp-group-item-flag"), env.dts->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 += ')';
item_forms.push_back(pretty_print::to_symbol(result));
}
if (!item_forms.empty()) {
forms.push_back(pretty_print::to_symbol(":parts"));
forms.push_back(pretty_print::build_list(item_forms));
}
return pretty_print::build_list(forms);
}
////////////////////////////////
// DefpartElement
////////////////////////////////
DefpartElement::DefpartElement(const StaticInfo& data, int id) : m_static_info(data), m_id(id) {}
void DefpartElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}
void DefpartElement::apply_form(const std::function<void(Form*)>&) {}
void DefpartElement::collect_vars(RegAccessSet&, bool) const {}
void DefpartElement::get_modified_regs(RegSet&) const {}
goos::Object DefpartElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("defpart"));
forms.push_back(pretty_print::to_symbol(fmt::format("{}", m_id)));
std::vector<goos::Object> item_forms;
for (const auto& e : m_static_info.fields) {
if (e.field_id == 67) {
// sp-end
break;
}
item_forms.push_back(decompile_sparticle_field_init(e, env.dts->ts));
}
if (!item_forms.empty()) {
forms.push_back(pretty_print::to_symbol(":init-specs"));
forms.push_back(pretty_print::build_list(item_forms));
}
return pretty_print::build_list(forms);
}
////////////////////////////////
// ResLumpMacroElement
////////////////////////////////
+76
View File
@@ -12,6 +12,7 @@
#include "common/type_system/state.h"
#include "decompiler/IR2/LabelDB.h"
#include "common/math/Vector.h"
#include "decompiler/ObjectFile/LinkedWord.h"
namespace decompiler {
class Form;
@@ -571,6 +572,10 @@ class ConditionElement : public FormElement {
FormPool& pool,
const std::vector<Form*>& source_forms,
const std::vector<TypeSpec>& types);
FormElement* make_geq_zero_unsigned_check_generic(const Env& env,
FormPool& pool,
const std::vector<Form*>& source_forms,
const std::vector<TypeSpec>& types);
FormElement* make_geq_zero_signed_check_generic(const Env& env,
FormPool& pool,
const std::vector<Form*>& source_forms,
@@ -1674,6 +1679,77 @@ class DefskelgroupElement : public FormElement {
Info m_info;
};
class DefpartgroupElement : public FormElement {
public:
struct StaticInfo {
u16 duration;
u16 linger;
u16 flags;
std::string name;
math::Vector4f bounds;
struct PartGroupItem {
u32 part_id;
float fade;
float falloff;
u16 flags;
u16 period;
u16 length;
u16 offset;
u32 hour_mask;
u32 binding;
};
std::vector<PartGroupItem> elts;
};
DefpartgroupElement(const StaticInfo& data, int group_id);
goos::Object to_form_internal(const Env& env) const override;
void apply(const std::function<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& f) override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
void get_modified_regs(RegSet& regs) const override;
const std::string& name() const { return m_static_info.name; }
private:
StaticInfo m_static_info;
int m_group_id;
};
class DefpartElement : public FormElement {
public:
struct StaticInfo {
struct PartField {
u16 field_id;
u16 flags;
std::vector<LinkedWord> data;
goos::Object sound_spec;
};
std::vector<PartField> fields;
};
DefpartElement(const StaticInfo& data, int id);
goos::Object to_form_internal(const Env& env) const override;
void apply(const std::function<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& f) override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
void get_modified_regs(RegSet& regs) const override;
private:
StaticInfo m_static_info;
int m_id;
};
class ResLumpMacroElement : public FormElement {
public:
enum class Kind { DATA, STRUCT, VALUE, INVALID };
+96 -56
View File
@@ -1065,7 +1065,8 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
result->push_back(pool.alloc_element<DerefElement>(args.at(1), rd_ok.addr_of, tokens));
return;
} else {
lg::error("Bad is {}\n", args.at(0)->to_string(env));
// TODO - output error to IR
lg::error("Bad {} at OP: {}\n", args.at(0)->to_string(env), m_my_idx);
throw std::runtime_error("Failed to match product_with_constant inline array access 2.");
}
}
@@ -3583,66 +3584,59 @@ FormElement* sc_to_handle_get_proc(ShortCircuitElement* elt,
void ShortCircuitElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
mark_popped();
if (!used_as_value.value_or(false)) {
throw std::runtime_error(
"ShortCircuitElement::push_to_stack not implemented for result not used case.");
if (already_rewritten) {
stack.push_form_element(this, true);
} else {
if (already_rewritten) {
stack.push_form_element(this, true);
return;
}
return;
}
// the first condition is special
auto first_condition = entries.front().condition;
// lets evaluate in on the parent stack...
for (auto x : first_condition->elts()) {
x->push_to_stack(env, pool, stack);
}
// the first condition is special
auto first_condition = entries.front().condition;
// lets evaluate in on the parent stack...
for (auto x : first_condition->elts()) {
x->push_to_stack(env, pool, stack);
}
for (int i = 0; i < int(entries.size()); i++) {
auto& entry = entries.at(i);
if (entry.condition == first_condition) {
entry.condition->clear();
entry.condition->push_back(stack.pop_back(pool));
for (int i = 0; i < int(entries.size()); i++) {
auto& entry = entries.at(i);
if (entry.condition == first_condition) {
entry.condition->clear();
entry.condition->push_back(stack.pop_back(pool));
} else {
FormStack temp_stack(false);
for (auto& elt : entry.condition->elts()) {
elt->push_to_stack(env, pool, temp_stack);
}
std::vector<FormElement*> new_entries;
if (i == int(entries.size()) - 1) {
new_entries = rewrite_to_get_var(temp_stack, pool, final_result, env);
} else {
FormStack temp_stack(false);
for (auto& elt : entry.condition->elts()) {
elt->push_to_stack(env, pool, temp_stack);
}
new_entries = temp_stack.rewrite(pool, env);
}
std::vector<FormElement*> new_entries;
if (i == int(entries.size()) - 1) {
new_entries = rewrite_to_get_var(temp_stack, pool, final_result, env);
} else {
new_entries = temp_stack.rewrite(pool, env);
}
entry.condition->clear();
for (auto e : new_entries) {
if (dynamic_cast<EmptyElement*>(e)) {
continue;
}
entry.condition->push_back(e);
}
if (entry.condition->elts().empty()) {
entry.condition->push_back(pool.alloc_element<EmptyElement>());
entry.condition->clear();
for (auto e : new_entries) {
if (dynamic_cast<EmptyElement*>(e)) {
continue;
}
entry.condition->push_back(e);
}
if (entry.condition->elts().empty()) {
entry.condition->push_back(pool.alloc_element<EmptyElement>());
}
}
FormElement* to_push = this;
auto as_handle_get = sc_to_handle_get_proc(this, env, pool, stack);
if (as_handle_get) {
to_push = as_handle_get;
}
assert(used_as_value.has_value());
stack.push_value_to_reg(final_result, pool.alloc_single_form(nullptr, to_push), true,
env.get_variable_type(final_result, false));
already_rewritten = true;
}
FormElement* to_push = this;
auto as_handle_get = sc_to_handle_get_proc(this, env, pool, stack);
if (as_handle_get) {
to_push = as_handle_get;
}
assert(used_as_value.has_value());
stack.push_value_to_reg(final_result, pool.alloc_single_form(nullptr, to_push), true,
env.get_variable_type(final_result, false));
already_rewritten = true;
}
void ShortCircuitElement::update_from_stack(const Env& env,
@@ -4029,6 +4023,31 @@ FormElement* ConditionElement::make_geq_zero_signed_check_generic(
}
}
FormElement* ConditionElement::make_geq_zero_unsigned_check_generic(
const Env& env,
FormPool& pool,
const std::vector<Form*>& source_forms,
const std::vector<TypeSpec>& types) {
assert(source_forms.size() == 1);
// (>= (shl (the-as int iter) 62) 0) -> (not (pair? iter))
// match (shl [(the-as int [x]) | [x]] 62)
auto shift_match =
match(Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL),
{
Matcher::match_or({Matcher::cast("uint", Matcher::any(0)),
Matcher::any(0)}), // the val
Matcher::integer(62) // get the bit in the highest position.
}),
source_forms.at(0));
auto casted = make_casts_if_needed(source_forms, types, TypeSpec("uint"), pool, env);
auto zero =
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, SimpleAtom::make_int_constant(0));
casted.push_back(zero);
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::GEQ),
casted);
}
FormElement* ConditionElement::make_generic(const Env& env,
FormPool& pool,
const std::vector<Form*>& source_forms,
@@ -4099,6 +4118,10 @@ FormElement* ConditionElement::make_generic(const Env& env,
return make_geq_zero_signed_check_generic(env, pool, source_forms, types);
}
case IR2_Condition::Kind::GEQ_ZERO_UNSIGNED: {
return make_geq_zero_unsigned_check_generic(env, pool, source_forms, types);
}
case IR2_Condition::Kind::GREATER_THAN_ZERO_UNSIGNED: {
auto casted = make_casts_if_needed(source_forms, types, TypeSpec("uint"), pool, env);
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(
@@ -4359,13 +4382,10 @@ void push_asm_sllv_to_stack(const AsmOp* op,
auto dst = op->dst();
assert(dst.has_value());
auto sav = op->src(1);
assert(sav.has_value());
auto arg0_type = env.get_variable_type(*var, true);
auto type_info = env.dts->ts.lookup_type(arg0_type);
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
if (sav->reg() == Register(Reg::GPR, Reg::R0)) {
if (op->instruction().src[1].is_reg(Register(Reg::GPR, Reg::R0))) {
if (bitfield_info) {
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
@@ -4881,6 +4901,8 @@ void ArrayFieldAccess::update_with_val(Form* new_val,
{reg0_matcher, Matcher::integer(m_expected_stride)});
mult_matcher = Matcher::match_or({Matcher::cast("uint", mult_matcher), mult_matcher});
auto matcher = Matcher::fixed_op(FixedOperatorKind::ADDITION, {mult_matcher, reg1_matcher});
matcher = Matcher::match_or({matcher, Matcher::fixed_op(FixedOperatorKind::ADDITION_PTR,
{reg1_matcher, mult_matcher})});
auto match_result = match(matcher, new_val);
Form* idx = nullptr;
Form* base = nullptr;
@@ -5355,6 +5377,24 @@ void DefskelgroupElement::update_from_stack(const Env&,
result->push_back(this);
}
void DefpartgroupElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
std::vector<FormElement*>* result,
bool) {
mark_popped();
result->push_back(this);
}
void DefpartElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
std::vector<FormElement*>* result,
bool) {
mark_popped();
result->push_back(this);
}
void ResLumpMacroElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
+39 -17
View File
@@ -26,7 +26,7 @@ const std::map<InstructionKind, OpenGOALAsm::Function> MIPS_ASM_TO_OPEN_GOAL_FUN
{InstructionKind::PAND, {".pand", {}}},
// Parallel Pack
{InstructionKind::PPACH, {".ppach", {}}},
{InstructionKind::PPACH, {".ppach", {MOD::QWORD_CAST}}},
// Parallel Compares
{InstructionKind::PCEQB, {".pceqb", {}}},
@@ -50,6 +50,11 @@ const std::map<InstructionKind, OpenGOALAsm::Function> MIPS_ASM_TO_OPEN_GOAL_FUN
// lots of implicit logic in OpenGOAL depending on argument types!
{InstructionKind::MFC1, {".mov", {}}},
{InstructionKind::MOVN, {"move-if-not-zero", {}}}, // s7 special case is handled elsewhere
{InstructionKind::SLT, {"set-on-less-than", {}}},
{InstructionKind::SLTI, {"set-on-less-than", {}}},
{InstructionKind::SRA, {"shift-arith-right-32", {}}},
// ---- COP2 -----
// TODO - VMOVE supports dest, but OpenGOAL does NOT yet!
{InstructionKind::VMOVE, {".mov.vf", {MOD::DEST_MASK}}},
@@ -104,9 +109,8 @@ const std::map<InstructionKind, OpenGOALAsm::Function> MIPS_ASM_TO_OPEN_GOAL_FUN
{InstructionKind::VABS, {".abs.vf", {MOD::DEST_MASK}}},
// Outer-product
// NOTE - currently it's assumed these groups of instructions will be replaced with 1
{InstructionKind::VOPMULA, {"TODO.VOPMULA.vf", {}}},
{InstructionKind::VOPMSUB, {".outer.product.vf", {MOD::SWAP_FIRST_TWO_SOURCE_ARGS}}},
{InstructionKind::VOPMULA, {".outer.product.a.vf", {}}},
{InstructionKind::VOPMSUB, {".outer.product.b.vf", {MOD::ACC_THIRD_SRC_ARG}}},
// Division
{InstructionKind::VDIV, {".div.vf", {MOD::FTF, MOD::FSF}}},
@@ -152,11 +156,11 @@ bool OpenGOALAsm::Function::allows_modifier(InstructionModifiers mod) {
return std::find(modifiers.begin(), modifiers.end(), mod) != modifiers.end();
}
OpenGOALAsm::OpenGOALAsm(Instruction _instr) : instr(_instr) {
if (MIPS_ASM_TO_OPEN_GOAL_FUNCS.count(instr.kind) == 0) {
OpenGOALAsm::OpenGOALAsm(Instruction _instr) : m_instr(_instr) {
if (MIPS_ASM_TO_OPEN_GOAL_FUNCS.count(m_instr.kind) == 0) {
valid = false;
} else {
func = MIPS_ASM_TO_OPEN_GOAL_FUNCS.at(instr.kind);
func = MIPS_ASM_TO_OPEN_GOAL_FUNCS.at(m_instr.kind);
if (func.funcTemplate.rfind("TODO", 0) == 0) {
todo = true;
}
@@ -170,11 +174,11 @@ OpenGOALAsm::OpenGOALAsm(Instruction _instr) : instr(_instr) {
OpenGOALAsm::OpenGOALAsm(Instruction _instr,
std::optional<RegisterAccess> _dst,
const std::vector<std::optional<RegisterAccess>>& _src)
: instr(_instr), m_dst(_dst), m_src(_src) {
if (MIPS_ASM_TO_OPEN_GOAL_FUNCS.count(instr.kind) == 0) {
: m_instr(_instr), m_dst(_dst), m_src(_src) {
if (MIPS_ASM_TO_OPEN_GOAL_FUNCS.count(m_instr.kind) == 0) {
valid = false;
} else {
func = MIPS_ASM_TO_OPEN_GOAL_FUNCS.at(instr.kind);
func = MIPS_ASM_TO_OPEN_GOAL_FUNCS.at(m_instr.kind);
if (func.funcTemplate.rfind("TODO", 0) == 0) {
todo = true;
}
@@ -189,8 +193,8 @@ std::string OpenGOALAsm::full_function_name() {
std::string func_name = func.funcTemplate;
// OpenGOAL uses the function name for broadcast specification
if (func.allows_modifier(MOD::BROADCAST)) {
if (instr.cop2_bc != 0xff) {
std::string bc = std::string(1, instr.cop2_bc_to_char());
if (m_instr.cop2_bc != 0xff) {
std::string bc = std::string(1, m_instr.cop2_bc_to_char());
func_name = fmt::format(func_name, bc);
}
}
@@ -203,9 +207,9 @@ std::vector<goos::Object> OpenGOALAsm::get_args(const std::vector<DecompilerLabe
std::vector<goos::Object> named_args;
bool got_fsf = false;
for (int i = 0; i < instr.n_src; i++) {
for (int i = 0; i < m_instr.n_src; i++) {
auto v = m_src.at(i);
InstructionAtom atom = instr.get_src(i);
InstructionAtom atom = m_instr.get_src(i);
if (v.has_value()) {
// Normal register / constant args
@@ -236,7 +240,19 @@ std::vector<goos::Object> OpenGOALAsm::get_args(const std::vector<DecompilerLabe
named_args.push_back(pretty_print::to_symbol(fmt::format(":offset {}", atom.get_imm())));
}
} else {
args.push_back(pretty_print::to_symbol(atom.to_string(labels)));
// if it's r0, replace it with a `0`
// unless it is pextuw or pcpyud
if (atom.is_reg() && atom.get_reg().get_kind() == decompiler::Reg::RegisterKind::GPR &&
atom.get_reg().reg_id() == decompiler::Reg::R0 &&
m_instr.kind != InstructionKind::PEXTUW && m_instr.kind != InstructionKind::PCPYUD) {
if (func.allows_modifier(MOD::QWORD_CAST)) {
args.push_back(pretty_print::to_symbol("(the-as uint128 0)"));
} else {
args.push_back(pretty_print::to_symbol("0"));
}
} else {
args.push_back(pretty_print::to_symbol(atom.to_string(labels)));
}
}
}
@@ -246,9 +262,10 @@ std::vector<goos::Object> OpenGOALAsm::get_args(const std::vector<DecompilerLabe
}
// Handle destination masks
if (func.allows_modifier(MOD::DEST_MASK) && instr.cop2_dest != 0xff && instr.cop2_dest != 15) {
if (func.allows_modifier(MOD::DEST_MASK) && m_instr.cop2_dest != 0xff &&
m_instr.cop2_dest != 15) {
named_args.push_back(
pretty_print::to_symbol(fmt::format(":mask #b{:b}", instr.cop2_dest_mask_intel())));
pretty_print::to_symbol(fmt::format(":mask #b{:b}", m_instr.cop2_dest_mask_intel())));
}
// Some functions are configured, or its easiest to swap the source args
@@ -257,6 +274,11 @@ std::vector<goos::Object> OpenGOALAsm::get_args(const std::vector<DecompilerLabe
std::swap(args.at(0), args.at(1));
}
if (m_instr.kind == InstructionKind::MOVZ || m_instr.kind == InstructionKind::MOVN) {
RegisterAccess ra(AccessMode::READ, m_dst->reg(), m_dst->idx());
args.push_back(ra.to_form(env));
}
args.insert(args.end(), named_args.begin(), named_args.end());
return args;
}
+3 -2
View File
@@ -2,7 +2,6 @@
#include <string>
#include <optional>
#include "common/util/assert.h"
#include <utility>
#include <map>
#include "common/goos/Object.h"
@@ -11,6 +10,7 @@
#include "decompiler/IR2/IR2_common.h"
#include "Env.h"
#include "AtomicOp.h"
#include "common/util/assert.h"
namespace decompiler {
@@ -23,6 +23,7 @@ struct OpenGOALAsm {
OFFSET,
SWAP_FIRST_TWO_SOURCE_ARGS,
ACC_THIRD_SRC_ARG,
QWORD_CAST,
SKIP_IT
};
@@ -42,7 +43,7 @@ struct OpenGOALAsm {
bool valid = true;
bool todo = false;
bool skip = false;
Instruction instr;
Instruction m_instr;
std::optional<RegisterAccess> m_dst;
std::vector<std::optional<RegisterAccess>> m_src;
OpenGOALAsm::Function func;
+1 -2
View File
@@ -1,10 +1,9 @@
#pragma once
#include "common/util/assert.h"
#include "common/common_types.h"
#include "decompiler/IR2/Form.h"
#include "decompiler/util/data_decompile.h"
#include "common/util/assert.h"
namespace decompiler {
struct BitfieldManip {
+1 -1
View File
@@ -4,7 +4,6 @@
*/
#include <algorithm>
#include "common/util/assert.h"
#include <cstring>
#include <numeric>
#include "decompiler/IR/IR.h"
@@ -15,6 +14,7 @@
#include "third-party/json.hpp"
#include "common/log/log.h"
#include "common/goos/PrettyPrinter.h"
#include "common/util/assert.h"
namespace decompiler {
/*!
@@ -4,13 +4,13 @@
* This implements a decoder for the GOAL linking format.
*/
#include "common/util/assert.h"
#include <cstring>
#include "LinkedObjectFileCreation.h"
#include "decompiler/config.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "common/link_types.h"
#include "common/util/BitUtils.h"
#include "common/util/assert.h"
namespace decompiler {
// There are three link versions:
+1 -1
View File
@@ -8,8 +8,8 @@
#include <cstdint>
#include <string>
#include <cstring>
#include "common/util/assert.h"
#include "common/common_types.h"
#include "common/util/assert.h"
namespace decompiler {
class LinkedWord {
+13 -11
View File
@@ -376,7 +376,7 @@ std::string pad_string(const std::string& in, size_t length) {
}
} // namespace
std::string ObjectFileDB::generate_obj_listing() {
std::string ObjectFileDB::generate_obj_listing(const std::unordered_set<std::string>& merged_objs) {
std::string result = "[";
std::set<std::string> all_unique_names;
int unique_count = 0;
@@ -391,23 +391,25 @@ std::string ObjectFileDB::generate_obj_listing() {
dgos.pop_back();
dgos.pop_back();
dgos += "]";
result += "[\"" + pad_string(x.to_unique_name() + "\", ", 50) + "\"" +
auto name = x.to_unique_name();
result += "[\"" + pad_string(name + "\", ", 50) + "\"" +
pad_string(x.name_in_dgo + "\", ", 50) + std::to_string(x.obj_version) + ", " +
dgos + ", \"\"],\n";
unique_count++;
if (all_unique_names.find(x.to_unique_name()) != all_unique_names.end()) {
lg::error("Object file {} appears multiple times with the same name.", x.to_unique_name());
if (all_unique_names.find(name) != all_unique_names.end() &&
merged_objs.find(name) == merged_objs.end()) {
lg::error("Object file {} appears multiple times with the same name.", name);
}
all_unique_names.insert(x.to_unique_name());
}
// this check is extremely important. It makes sure we don't have any repeat names. This could
// be caused by two files with the same name, in the same DGOs, but different data.
if (int(all_unique_names.size()) != unique_count) {
lg::error("Object files are not named properly, data will be lost!");
all_unique_names.insert(name);
}
}
// this check is extremely important. It makes sure we don't have any repeat names. This could
// be caused by two files with the same name, in the same DGOs, but different data.
if (int(all_unique_names.size()) != unique_count) {
lg::error("Object files are not named properly, data will be lost!");
}
if (result.length() >= 2) {
if (unique_count > 0) {
result.pop_back(); // kill last new line
result.pop_back(); // kill last comma
}
+3 -3
View File
@@ -7,7 +7,6 @@
* (there may be different object files with the same name sometimes)
*/
#include "common/util/assert.h"
#include <string>
#include <unordered_map>
#include <vector>
@@ -16,6 +15,7 @@
#include "common/common_types.h"
#include "decompiler/data/TextureDB.h"
#include "decompiler/analysis/symbol_def_map.h"
#include "common/util/assert.h"
namespace decompiler {
/*!
@@ -54,7 +54,7 @@ class ObjectFileDB {
const std::vector<std::string>& str_files,
const Config& config);
std::string generate_dgo_listing();
std::string generate_obj_listing();
std::string generate_obj_listing(const std::unordered_set<std::string>& merged_objs);
void process_link_data(const Config& config);
void process_labels();
void find_code(const Config& config);
@@ -81,7 +81,7 @@ class ObjectFileDB {
void ir2_register_usage_pass(int seg, ObjectFileData& data);
void ir2_variable_pass(int seg, ObjectFileData& data);
void ir2_cfg_build_pass(int seg, ObjectFileData& data);
void ir2_store_current_forms(int seg);
// void ir2_store_current_forms(int seg);
void ir2_build_expressions(int seg, const Config& config, ObjectFileData& data);
void ir2_insert_lets(int seg, ObjectFileData& data);
void ir2_rewrite_inline_asm_instructions(int seg, ObjectFileData& data);
+10 -2
View File
@@ -23,6 +23,7 @@
#include "decompiler/analysis/static_refs.h"
#include "decompiler/analysis/symbol_def_map.h"
#include "decompiler/analysis/find_skelgroups.h"
#include "decompiler/analysis/find_defpartgroup.h"
#include "common/goos/PrettyPrinter.h"
#include "decompiler/IR2/Form.h"
#include "decompiler/analysis/mips2c.h"
@@ -60,14 +61,21 @@ void ObjectFileDB::analyze_functions_ir2(
ir2_do_segment_analysis_phase2(TOP_LEVEL_SEGMENT, config, data);
try {
if (data.linked_data.functions_by_seg.size() == 3) {
run_defstate(data.linked_data.functions_by_seg.at(2).front(), skip_states);
run_defpartgroup(data.linked_data.functions_by_seg.at(TOP_LEVEL_SEGMENT).front());
}
} catch (const std::exception& e) {
lg::error("Failed to find defpartgroups: {}", e.what());
}
try {
if (data.linked_data.functions_by_seg.size() == 3) {
run_defstate(data.linked_data.functions_by_seg.at(TOP_LEVEL_SEGMENT).front(), skip_states);
}
} catch (const std::exception& e) {
lg::error("Failed to find defstates: {}", e.what());
}
try {
if (data.linked_data.functions_by_seg.size() == 3) {
run_defskelgroups(data.linked_data.functions_by_seg.at(2).front());
run_defskelgroups(data.linked_data.functions_by_seg.at(TOP_LEVEL_SEGMENT).front());
}
} catch (const std::exception& e) {
lg::error("Failed to find defskelgroups: {}", e.what());
+2 -2
View File
@@ -3,8 +3,8 @@
#include "VuDisassembler.h"
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
#include "common/util/print_float.h"
#include "common/util/assert.h"
namespace decompiler {
@@ -733,4 +733,4 @@ void VuDisassembler::name_labels() {
m_label_names.at(label_idx) = fmt::format("L{}", idx++);
}
}
} // namespace decompiler
} // namespace decompiler
+2 -3
View File
@@ -1,7 +1,6 @@
#include "common/util/assert.h"
#include "third-party/fmt/core.h"
#include "VuInstruction.h"
#include "common/util/assert.h"
namespace decompiler {
VuInstructionAtom VuInstructionAtom::make_vf(int idx) {
@@ -89,4 +88,4 @@ VuInstruction VuInstruction::make_fp_constant(u32 value) {
result.kind = VuInstrK::FP_CONSTANT;
return result;
}
} // namespace decompiler
} // namespace decompiler
+2 -2
View File
@@ -387,7 +387,7 @@ IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx) {
return IR2_BranchDelay(IR2_BranchDelay::Kind::NOP);
} else if (is_gpr_3(i0, InstructionKind::OR, {}, rs7(), rr0())) {
return IR2_BranchDelay(IR2_BranchDelay::Kind::SET_REG_FALSE, make_dst_var(i0, idx));
} else if (is_gpr_3(i0, InstructionKind::OR, {}, {}, rr0())) {
} else if (is_gpr_3(i0, InstructionKind::OR, {}, {}, rr0()) && !i0.get_src(0).is_reg(rr0())) {
return IR2_BranchDelay(IR2_BranchDelay::Kind::SET_REG_REG, make_dst_var(i0, idx),
make_src_var(i0.get_src(0).get_reg(), idx));
} else if (i0.kind == InstructionKind::DADDIU && i0.get_src(0).is_reg(rs7()) &&
@@ -2201,4 +2201,4 @@ FunctionAtomicOps convert_function_to_atomic_ops(
assert(func.basic_blocks.size() == result.block_id_to_first_atomic_op.size());
return result;
}
} // namespace decompiler
} // namespace decompiler
+227
View File
@@ -0,0 +1,227 @@
#include "find_defpartgroup.h"
#include "common/goos/PrettyPrinter.h"
#include "decompiler/IR2/Form.h"
#include "decompiler/IR2/GenericElementMatcher.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/util/data_decompile.h"
namespace decompiler {
namespace {
const goos::Object& car(const goos::Object* x) {
return x->as_pair()->car;
}
const goos::Object* cdr(const goos::Object* x) {
return &x->as_pair()->cdr;
}
void read_static_group_data(DecompiledDataElement* src,
const Env& env,
DefpartgroupElement::StaticInfo& group) {
auto lab = src->label();
// looks like:
/*
.type sparticle-launch-group
L81:
.word 0xbb80042
.word 0x405dc
.word L83
.word L82
.word 0x0
.word 0x0
.word 0x0
.word 0x0
.word 0x0
.word 0x0
.word 0x47800000
L82:
*/
int start_word_idx = (lab.offset / 4) - 1;
auto& words = env.file->words_by_seg.at(lab.target_segment);
auto& first_word = words.at(start_word_idx);
if (first_word.kind() != LinkedWord::TYPE_PTR ||
first_word.symbol_name() != "sparticle-launch-group") {
env.func->warnings.warn_and_throw(
"Reference to sparticle-launch-group bad: invalid type pointer");
}
auto& word_1 = words.at(start_word_idx + 1);
s16 len = word_1.data & 0xffff;
group.duration = (word_1.data >> 16) & 0xffff;
auto& word_2 = words.at(start_word_idx + 2);
group.linger = word_2.data & 0xffff;
group.flags = (word_2.data >> 16) & 0xffff;
auto& string_word = words.at(start_word_idx + 3);
if (string_word.kind() != LinkedWord::PTR) {
env.func->warnings.warn_and_throw(
"Reference to sparticle-launch-group bad: invalid name label");
}
group.name = env.file->get_goal_string_by_label(
env.file->get_label_by_name(env.file->get_label_name(string_word.label_id())));
auto& array_word = words.at(start_word_idx + 4);
if (array_word.kind() != LinkedWord::PTR) {
env.func->warnings.warn_and_throw(
"Reference to sparticle-launch-group bad: invalid array label");
}
auto& array_lab = env.file->get_label_by_name(env.file->get_label_name(array_word.label_id()));
auto& array_words = env.file->words_by_seg.at(array_lab.target_segment);
int array_start_word_idx = array_lab.offset / 4;
group.elts.clear();
for (int i = 0; i < len; ++i) {
int item_idx = i * 8 + array_start_word_idx;
auto& item = group.elts.emplace_back();
item.part_id = array_words.at(item_idx + 0).data;
item.fade = *reinterpret_cast<float*>(&array_words.at(item_idx + 1).data);
item.falloff = *reinterpret_cast<float*>(&array_words.at(item_idx + 2).data);
item.flags = array_words.at(item_idx + 3).data & 0xffff;
item.period = (array_words.at(item_idx + 3).data >> 16) & 0xffff;
item.length = array_words.at(item_idx + 4).data & 0xffff;
item.offset = (array_words.at(item_idx + 4).data >> 16) & 0xffff;
item.hour_mask = array_words.at(item_idx + 5).data;
item.binding = array_words.at(item_idx + 6).data;
}
for (int i = 0; i < 4; i++) {
auto& word = words.at(start_word_idx + 8 + i);
if (word.kind() != LinkedWord::PLAIN_DATA) {
env.func->warnings.warn_and_throw("Reference to sparticle-launch-group bad: invalid bounds");
}
group.bounds[i] = *reinterpret_cast<float*>(&word.data);
}
}
void read_static_part_data(DecompiledDataElement* src,
const Env& env,
DefpartElement::StaticInfo& part) {
auto lab = src->label();
// looks like:
/*
.type sparticle-launcher
L79:
.word 0x0
.word 0x0
.word L80
L80:
.word 0x1
.word 0x201200
.word 0x0
.word 0x0
.word 0x10006
.word 0x3dcccccd
.word 0x0
.word 0x3f800000
*/
int start_word_idx = (lab.offset / 4) - 1;
auto& words = env.file->words_by_seg.at(lab.target_segment);
auto& first_word = words.at(start_word_idx);
if (first_word.kind() != LinkedWord::TYPE_PTR ||
first_word.symbol_name() != "sparticle-launcher") {
env.func->warnings.warn_and_throw("Reference to sparticle-launcher bad: invalid type pointer");
}
auto& empty1 = words.at(start_word_idx + 1);
auto& empty2 = words.at(start_word_idx + 2);
if (empty1.kind() != LinkedWord::PLAIN_DATA || empty1.data != 0 ||
empty2.kind() != LinkedWord::PLAIN_DATA || empty2.data != 0) {
env.func->warnings.warn_and_throw("Reference to sparticle-launcher bad: accums not empty");
}
auto& array_word = words.at(start_word_idx + 3);
if (array_word.kind() != LinkedWord::PTR) {
env.func->warnings.warn_and_throw("Reference to sparticle-launcher bad: invalid array label");
}
auto& array_lab = env.file->get_label_by_name(env.file->get_label_name(array_word.label_id()));
auto& array_words = env.file->words_by_seg.at(array_lab.target_segment);
int array_start_word_idx = array_lab.offset / 4;
part.fields.clear();
src->do_decomp(env, env.file);
auto obj = src->to_form(env);
obj = car(cdr(cdr(&obj)));
auto cur_field = cdr(&obj);
for (int i = 0; true; ++i) {
int field_idx = i * 4 + array_start_word_idx;
auto& item = part.fields.emplace_back();
item.field_id = array_words.at(field_idx + 0).data & 0xffff;
item.flags = (array_words.at(field_idx + 0).data >> 16) & 0xffff;
item.data.push_back(array_words.at(field_idx + 0));
item.data.push_back(array_words.at(field_idx + 1));
item.data.push_back(array_words.at(field_idx + 2));
item.data.push_back(array_words.at(field_idx + 3));
if (item.field_id == 7) {
auto& fld = car(cur_field);
item.sound_spec = cdr(cdr(cdr(cdr(&fld))))->as_pair()->car;
}
if (item.field_id == 67) {
// sp-end
break;
}
cur_field = cdr(cur_field);
}
}
} // namespace
void run_defpartgroup(Function& top_level_func) {
auto& env = top_level_func.ir2.env;
auto& pool = *top_level_func.ir2.form_pool;
if (!top_level_func.ir2.top_form) {
return;
}
top_level_func.ir2.top_form->apply_form([&](Form* form) {
for (auto& fe : form->elts()) {
auto as_set = dynamic_cast<SetFormFormElement*>(fe);
if (as_set) {
/* Looks something like this:
(set! (-> *part-group-id-table* 188) (new 'static 'sparticle-launch-group
*/
if (as_set->dst()->elts().size() != 1) {
continue;
}
auto dest = dynamic_cast<DerefElement*>(as_set->dst()->elts().at(0));
if (!dest)
continue;
if (dest->tokens().size() != 1)
continue;
if (dest->tokens().at(0).kind() != DerefToken::Kind::INTEGER_CONSTANT)
continue;
if (dest->base()->elts().size() != 1)
continue;
auto dest_base = dynamic_cast<SimpleExpressionElement*>(dest->base()->elts().at(0));
if (!dest_base || !dest_base->expr().is_identity() || dest_base->expr().args() < 1)
continue;
auto src = dynamic_cast<DecompiledDataElement*>(as_set->src()->elts().at(0));
if (!src)
continue;
auto& sym = dest_base->expr().get_arg(0);
if (!sym.is_sym_val())
continue;
int id = dest->tokens().at(0).int_constant();
if (sym.get_str() == "*part-group-id-table*") {
DefpartgroupElement::StaticInfo group;
read_static_group_data(src, env, group);
auto rewritten = pool.alloc_element<DefpartgroupElement>(group, id);
if (rewritten) {
fe = rewritten;
}
} else if (sym.get_str() == "*part-id-table*") {
DefpartElement::StaticInfo part;
read_static_part_data(src, env, part);
auto rewritten = pool.alloc_element<DefpartElement>(part, id);
if (rewritten) {
fe = rewritten;
}
}
}
}
});
}
} // namespace decompiler
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "decompiler/Function/Function.h"
namespace decompiler {
void run_defpartgroup(Function& top_level_func);
}
+1 -1
View File
@@ -42,7 +42,7 @@ bool rewrite_inline_asm_instructions(Form* top_level_form,
/*lg::warn("[ASM Re-Write] - Unsupported inline assembly instruction kind - [{}]",
asmOp.instr.kind);*/
f.warnings.general_warning("Unsupported inline assembly instruction kind - [{}]",
asmOp.instr.to_string(f.ir2.env.file->labels));
asmOp.m_instr.to_string(f.ir2.env.file->labels));
new_entries.push_back(entry);
continue;
} else if (asmOp.skip) {
+61
View File
@@ -2,6 +2,7 @@
#include "mips2c.h"
#include "common/symbols.h"
#include "common/util/print_float.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/Function/Function.h"
@@ -91,6 +92,8 @@ std::string goal_to_c_function_name(const FunctionName& name) {
switch (name.kind) {
case FunctionName::FunctionKind::GLOBAL:
return goal_to_c_name(name.function_name);
case FunctionName::FunctionKind::METHOD:
return fmt::format("method_{}_{}", name.method_id, goal_to_c_name(name.type_name));
default:
assert(false);
}
@@ -568,8 +571,29 @@ Mips2C_Line handle_generic_op2_u16(const Instruction& i0, const std::string& ins
instr_str};
}
Mips2C_Line handle_daddiu(Mips2C_Output& out, const Instruction& i0, const std::string& instr_str) {
if (i0.get_src(1).is_label()) {
return {instr_str, instr_str};
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym("#t")) {
return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)),
reg_to_name(i0.get_src(0)), FIX_SYM_TRUE),
instr_str};
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym()) {
out.require_symbol(i0.get_src(1).get_sym());
return {fmt::format("c->load_symbol_addr({}, cache.{});", reg_to_name(i0.get_dst(0)),
goal_to_c_name(i0.get_src(1).get_sym())),
instr_str};
} else {
return handle_generic_op2_u16(i0, instr_str);
}
}
Mips2C_Line handle_sw(Mips2C_Output& out, const Instruction& i0, const std::string& instr_str) {
if (i0.get_src(1).is_sym() && i0.get_src(2).is_reg(rs7())) {
out.require_symbol(i0.get_src(1).get_sym());
return {fmt::format("c->store_symbol({}, cache.{});", reg_to_name(i0.get_src(0)),
goal_to_c_name(i0.get_src(1).get_sym())),
instr_str};
return handle_unknown(instr_str);
// auto name = i0.get_src(1).get_sym();
// // store into symbol table!
@@ -745,12 +769,22 @@ Mips2C_Line handle_likely_branch_bc(const Instruction& i0, const std::string& in
switch (i0.kind) {
case InstructionKind::BLTZL:
return {fmt::format("((s64){}) < 0", reg64_or_zero(i0.get_src(0))), instr_str};
case InstructionKind::BGEZL:
return {fmt::format("((s64){}) >= 0", reg64_or_zero(i0.get_src(0))), instr_str};
case InstructionKind::BGTZL:
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};
case InstructionKind::BEQL:
return {fmt::format("((s64){}) == ((s64){})", reg64_or_zero(i0.get_src(0)),
reg64_or_zero(i0.get_src(1))),
instr_str};
case InstructionKind::BC1TL:
return {"cop1_bc", instr_str};
case InstructionKind::BC1FL:
return {"!cop1_bc", instr_str};
default:
return handle_unknown(instr_str);
}
@@ -826,6 +860,18 @@ Mips2C_Line handle_clts(const Instruction& i0, const std::string& instr_string)
instr_string};
}
Mips2C_Line handle_cles(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_ceqs(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_pmfhl_lh(const Instruction& i0, const std::string& instr_string) {
return {fmt::format("c->pmfhl_lh({});", reg_to_name(i0.get_dst(0))), instr_string};
}
@@ -884,12 +930,16 @@ 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::VITOF12:
return handle_generic_op2_mask(i0, instr_str, "vitof12");
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::VFTOI12:
return handle_generic_op2_mask(i0, instr_str, "vftoi12");
case InstructionKind::VABS:
return handle_generic_op2_mask(i0, instr_str, "vabs");
case InstructionKind::VADDQ:
return handle_generic_op2_mask(i0, instr_str, "vaddq");
case InstructionKind::ANDI:
@@ -915,7 +965,9 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
case InstructionKind::PEXTLB:
case InstructionKind::MOVN:
case InstructionKind::PEXTUW:
case InstructionKind::PEXTLW:
case InstructionKind::PCPYUD:
case InstructionKind::PCPYLD:
case InstructionKind::PPACH:
case InstructionKind::PINTEH:
case InstructionKind::PCGTW:
@@ -946,6 +998,7 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
case InstructionKind::AND:
return handle_generic_op3(i0, instr_str, "and_"); // and isn't allowed in C++
case InstructionKind::DADDIU:
return handle_daddiu(output, i0, instr_str);
case InstructionKind::ADDIU:
return handle_generic_op2_u16(i0, instr_str);
case InstructionKind::QMTC2:
@@ -986,6 +1039,8 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
return handle_generic_op2(i0, instr_str, "mtc1");
case InstructionKind::NEGS:
return handle_generic_op2(i0, instr_str, "negs");
case InstructionKind::MOVS:
return handle_generic_op2(i0, instr_str, "movs");
case InstructionKind::CVTWS:
return handle_generic_op2(i0, instr_str, "cvtws");
case InstructionKind::CVTSW:
@@ -1001,6 +1056,12 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
case InstructionKind::CLTS:
output.needs_cop1_bc = true;
return handle_clts(i0, instr_str);
case InstructionKind::CLES:
output.needs_cop1_bc = true;
return handle_cles(i0, instr_str);
case InstructionKind::CEQS:
output.needs_cop1_bc = true;
return handle_ceqs(i0, instr_str);
case InstructionKind::VWAITQ:
return handle_plain_op(i0, instr_str, "vwaitq");
case InstructionKind::VOPMULA:
+2 -1
View File
@@ -20,11 +20,12 @@
#include <unordered_map>
#include <vector>
#include "common/util/assert.h"
#include "decompiler/Disasm/Register.h"
#include "decompiler/IR2/IR2_common.h"
#include "decompiler/util/TP_Type.h"
#include "common/util/assert.h"
namespace decompiler {
class Function;
+6
View File
@@ -202,6 +202,12 @@ Config read_config_file(const std::string& path_to_config_file) {
config.bad_format_strings =
hacks_json.at("bad_format_strings").get<std::unordered_map<std::string, int>>();
auto merged = hacks_json.at("expected_merged_objs").get<std::vector<std::string>>();
for (const auto& x : merged) {
config.merged_objects.insert(x);
}
config.levels_to_extract = cfg.at("levels_to_extract").get<std::vector<std::string>>();
return config;
}
+1
View File
@@ -115,6 +115,7 @@ struct Config {
std::unordered_set<std::string> allowed_objects;
std::unordered_set<std::string> banned_objects;
std::unordered_set<std::string> merged_objects;
std::unordered_map<std::string, std::unordered_map<int, std::vector<RegisterTypeCast>>>
register_type_casts_by_function_by_atomic_op_idx;
std::unordered_map<std::string, std::unordered_map<int, StackTypeCast>>
File diff suppressed because it is too large Load Diff
@@ -233,35 +233,26 @@
"collide-probe-node", // CFG
// collide-edge-grab
"(method 13 collide-edge-work)", // CFG
"(method 17 collide-edge-work)", // CFG
// CFG
// CFG
"(method 15 collide-edge-work)", // CFG
"(method 16 collide-edge-work)", // CFG
"(method 9 edge-grab-info)", // CFG
"(method 18 collide-edge-work)", // CFG
"(method 10 collide-edge-hold-list)", // CFG
// CFG
// CFG
// CFG
// CFG
// collide-shape
"(method 15 collide-shape-prim-mesh)", // CFG
"(method 15 collide-shape-prim-sphere)", // CFG
"(method 16 collide-shape-prim)", // CFG
"(method 15 collide-shape-prim-group)", // CFG
"(method 18 collide-shape-prim-sphere)",
"(method 23 collide-shape-prim-sphere)", // CFG
"(method 23 collide-shape-prim-mesh)", // BUG - crash in variable pass
"(method 24 collide-shape-prim)", // CFG
"(method 23 collide-shape-prim-group)", // CFG
"(method 42 collide-shape)", // CFG
"(method 12 collide-mesh)",
// process-drawable BUG
"cspace-inspect-tree",
"(method 19 process-drawable)",
//"(method 19 process-drawable)",
// ambient
"ambient-inspect",
// target BUG
"target-falling-anim-trans", // CFG resolution
//"target-falling-anim-trans", // CFG resolution
// target2 BUG
"look-for-points-of-interest", // Failed to split nested sc - looks like dead code to me
@@ -269,29 +260,20 @@
// collide-cache
"(method 10 collide-puss-work)", // CFG
"(method 9 collide-puss-work)", // decompiler crash
"(method 19 collide-cache)", // decompiler crash
"(method 10 collide-cache-prim)", // CFG
"(method 9 collide-cache-prim)", // CFG
"(method 30 collide-cache)", // unsupported asm - c.le.s
"(method 13 collide-shape-prim-group)", // CFG
"(method 13 collide-shape-prim-mesh)", // CFG
"(method 14 collide-shape-prim-group)", // CFG
"(method 14 collide-shape-prim-mesh)", // CFG
"(method 12 collide-shape-prim-group)", // CFG
"(method 12 collide-shape-prim-mesh)", // CFG
"(method 27 collide-cache)", // CFG
"(method 14 collide-cache)", // CFG
"(method 28 collide-cache)", // CFG
"(method 26 collide-cache)", // CFG
"(method 21 collide-cache)", // CFG
// decompiler crash
// CFG
// CFG
// CFG
// "(method 14 collide-cache)", // CFG
// CFG
// "(method 26 collide-cache)", // CFG
//"(method 21 collide-cache)", // CFG
"(method 32 collide-cache)", // CFG
// memory-usage BUG
//"(method 14 level)",
// navigate BUG
"(method 32 nav-control)",
// ocean
"draw-large-polygon-ocean", // CFG
@@ -305,10 +287,7 @@
// all unchecked and in level DGO code
"(anon-function 21 plant-boss)", // CFG
"target-flut-falling-anim-trans", // CFG failure
"(anon-function 2 target-tube)",
"(anon-function 5 orbit-plat)", // CFG
"(anon-function 2 ogreboss)"
"(anon-function 5 orbit-plat)" // CFG
],
// these functions use pairs and the decompiler
@@ -394,7 +373,8 @@
"ERROR: <asg> ~A in spool anim loop for ~A ~D, but not loaded.~": 3,
"~0k~5d/~d ~6d/~d ~6d/~d ": 6,
"~0k~s~%": 1,
"money ~A was killed in pickup~%": 0
"money ~A was killed in pickup~%": 0,
" id address name aid tsk lev status x y z address name state heap flags~%": 3
},
"blocks_ending_in_asm_branch": {
@@ -478,7 +458,48 @@
"birth-pickup-at-point": [0],
"draw-bones": [0, 1, 2, 8, 81],
"draw-bones-hud": [7, 8],
"(method 16 drawable-tree)": [7, 9, 10]
"(method 16 drawable-tree)": [7, 9, 10],
"(method 21 collide-cache)" : [3, 5, 19, 20,24,25,28,29],
"(method 14 collide-cache)" : [0, 1,2, 3, 4, 5],
"(method 9 collide-mesh-cache)" : [0, 1, 2, 5],
"(method 42 collide-shape)" : [0, 1, 2, 3, 4, 7],
"(method 23 collide-shape-prim-group)" : [1, 2, 3, 4, 5],
"(method 23 collide-shape-prim-mesh)" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
"(method 23 collide-shape-prim-sphere)" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
"(method 18 collide-shape-prim-sphere)" : [1, 3,4, 5, 7],
"(method 45 collide-shape)" : [0, 16, 42, 67, 92],
"(method 24 collide-shape-prim)" : [2, 3, 4, 5, 1],
"(method 20 collide-shape-prim-group)" : [11],
"(method 28 collide-shape-prim-mesh)" : [10],
"(method 40 collide-shape)": [0, 2,5,6,7, 11,12, 28, 43, 58, 63],
"(method 15 collide-shape-prim-group)" : [1, 2, 3, 4, 5, 6],
"(method 16 collide-shape-prim)" : [1, 2, 3, 4, 5, 6],
"(method 15 collide-shape-prim-sphere)" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
"(method 15 collide-shape-prim-mesh)" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 29],
"(method 35 collide-shape)" : [1],
"(method 22 collide-cache)":[2, 3, 4, 13, 14, 15, 23, 24, 25, 33, 34, 35],
"(method 12 collide-shape-prim-sphere)" : [0, 1],
"(method 12 collide-shape-prim-group)" : [1, 2, 3, 4],
"(method 24 collide-cache)" : [2, 3, 4, 13, 14, 15, 23, 24, 25, 33, 34, 35],
"(method 14 collide-shape-prim-sphere)" : [0, 1, 2, 3],
"(method 14 collide-shape-prim-group)" : [0, 1, 2, 3, 4],
"(method 23 collide-cache)" : [2, 3, 4, 13, 14, 15, 23, 24, 25, 33, 34, 35],
"(method 13 collide-shape-prim-sphere)" : [0, 1, 2],
"(method 13 collide-shape-prim-group)" : [0, 1, 2,3,4],
"(method 19 collide-cache)" : [0, 1, 3, 4, 5, 18, 19],
"(method 10 collide-mesh)" : [1, 2, 4, 5],
"target-falling-anim-trans" : [5, 6],
"(method 19 process-drawable)" : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
"(anon-function 9 racer)" : [75],
"(method 13 collide-edge-work)" : [0, 2],
"(method 17 collide-edge-work)" : [0, 1, 2, 3, 4],
"(method 9 edge-grab-info)" : [15, 16, 18, 19, 21, 22, 24],
// "(method 18 collide-edge-work)" : [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24],
"target-falling-anim-trans" : [5, 6],
"(method 27 nav-mesh)" : [8],
"(method 32 nav-control)": [12, 21, 32],
"start-collect-nav": [0],
"end-collect-nav": [0]
},
// Sometimes the game might use format strings that are fetched dynamically,
@@ -526,13 +547,73 @@
"time-of-day-interp-colors-scratch",
"normalize-frame-quaternions",
"collide-do-primitives",
"moving-sphere-triangle-intersect"
"moving-sphere-triangle-intersect",
"(method 12 collide-mesh)",
"(method 11 collide-mesh)",
"collide-probe-node",
"collide-probe-instance-tie",
"(method 32 collide-cache)",
"(method 28 collide-cache)",
"(method 26 collide-cache)",
"(method 27 collide-cache)",
"(method 29 collide-cache)",
"(method 12 collide-shape-prim-mesh)",
"(method 14 collide-shape-prim-mesh)",
"(method 13 collide-shape-prim-mesh)",
"(method 30 collide-cache)",
"(method 9 collide-cache-prim)",
"(method 10 collide-cache-prim)",
"(method 9 collide-puss-work)",
"(method 10 collide-puss-work)",
// these could easily be goal, but probably faster/easier this way.
"(method 14 collide-mesh)",
"(method 15 collide-mesh)",
"(method 16 collide-edge-work)",
"(method 10 collide-edge-hold-list)",
"(method 15 collide-edge-work)",
"(method 18 collide-edge-work)"
],
// there are some missing textures. I don't know what the game actually does here.
// the format for entries is [level, tpage, index]
"missing_textures": [
["finalboss", 1419, 3]
],
// some object files have garbage pad data at the end which makes the decompiler
// assume they must be different files, such as the art group for orb-cache-top.
// this just suppresses a message.
"expected_merged_objs": [
"orb-cache-top-ag",
"ecovalve-ag",
"barrel-ag",
"sack-ag",
"sharkey-ag",
"warp-gate-switch-ag",
"baby-spider-ag",
"cavetrapdoor-ag",
"spider-egg-ag",
"darkvine-ag",
"jng-iris-door-ag",
"eichar-fish+0-ag",
"launcherdoor-ag",
"plat-eco-ag",
"eichar-tube+0-ag",
"eichar-pole+0-ag",
"crate-darkeco-cluster-ag",
"ef-plane-ag",
"racer-ag",
"flut-saddle-ag",
"shover-ag",
"steam-cap-ag",
"sunkencam-ag",
"swampcam-ag",
"pontoonfive-ag",
"oracle-ag",
"village-cam-ag",
"plat-ag"
]
}
@@ -1609,11 +1609,17 @@
"rolling-lightning-mole": [
["L181", "vector"],
["L185", "rgba", true],
["L186", "rgba", true],
["L187", "rgba", true],
["L189", "rgba", true],
["L195", "vector"]
["L185", "vector4w"],
["L186", "vector4w"],
["L187", "vector4w"],
["L189", "vector4w"],
["L195", "vector"],
["L201", "vector4w"],
["L202", "vector4w"],
["L203", "vector4w"],
["L204", "vector4w"],
["L205", "vector4w"],
["L206", "vector4w"]
],
"rolling-robber": [
@@ -1624,7 +1630,95 @@
],
"ogreboss": [
["L525", "uint64", true],
["L359", "attack-info"],
["L367", "attack-info"],
["L370", "attack-info"],
["L371", "attack-info"],
["L384", "(pointer float)", 1],
["L384", "(pointer float)", 1],
["L385", "(pointer float)", 1],
["L386", "(pointer float)", 1],
["L387", "(pointer float)", 1],
["L388", "(pointer float)", 1],
["L389", "(pointer float)", 1],
["L390", "float", true],
["L391", "float", true],
["L392", "float", true],
["L393", "float", true],
["L394", "float", true],
["L395", "float", true],
["L396", "float", true],
["L397", "float", true],
["L398", "float", true],
["L399", "float", true],
["L400", "float", true],
["L401", "float", true],
["L402", "float", true],
["L403", "float", true],
["L404", "float", true],
["L405", "float", true],
["L406", "float", true],
["L407", "float", true],
["L408", "float", true],
["L409", "float", true],
["L410", "float", true],
["L411", "float", true],
["L412", "float", true],
["L413", "float", true],
["L414", "float", true],
["L415", "float", true],
["L416", "float", true],
["L417", "float", true],
["L418", "float", true],
["L419", "float", true],
["L420", "float", true],
["L421", "float", true],
["L422", "float", true],
["L423", "float", true],
["L424", "float", true],
["L425", "float", true],
["L426", "float", true],
["L427", "float", true],
["L428", "float", true],
["L429", "float", true],
["L430", "float", true],
["L431", "float", true],
["L432", "float", true],
["L433", "float", true],
["L434", "float", true],
["L435", "float", true],
["L436", "float", true],
["L437", "float", true],
["L438", "float", true],
["L439", "float", true],
["L440", "float", true],
["L441", "float", true],
["L442", "float", true],
["L443", "float", true],
["L444", "float", true],
["L445", "float", true],
["L446", "float", true],
["L447", "float", true],
["L448", "float", true],
["L449", "float", true],
["L450", "float", true],
["L451", "float", true],
["L452", "float", true],
["L453", "float", true],
["L454", "float", true],
["L455", "float", true],
["L456", "float", true],
["L457", "float", true],
["L458", "float", true],
["L459", "float", true],
["L460", "(pointer float)", 1],
["L461", "rgba", true],
["L462", "uint64", true],
["L463", "uint64", true],
["L464", "uint64", true],
["L465", "uint64", true],
["L466", "uint64", true],
["L525", "float", true],
["L526", "uint64", true],
["L527", "uint64", true],
["L528", "uint64", true],
@@ -1632,11 +1726,6 @@
["L530", "uint64", true]
],
"mother-spider": [
["L301", "(inline-array mother-spider-leg-info)", 8],
["L302", "(inline-array mother-spider-thread)", 9]
],
"target-flut": [
["L399", "attack-info"],
["L406", "float", true],
@@ -1674,8 +1763,8 @@
["L438", "float", true],
["L439", "float", true],
["L440", "float", true],
["L441", "float", true],
["L442", "float", true],
["L441", "(pointer float)", 1],
["L442", "(pointer float)", 1],
["L443", "float", true],
["L444", "float", true],
["L445", "float", true],
@@ -1850,7 +1939,7 @@
["L414", "attack-info"],
["L415", "attack-info"],
["L416", "attack-info"],
["L417", "attack-info"],
["L417", "vector"],
["L418", "attack-info"],
["L419", "attack-info"],
["L420", "vector"],
@@ -1878,7 +1967,8 @@
],
"collide-cache": [
["L304", "vector"]
["L304", "vector"],
["L303", "vector4w"]
],
"load-boundary-data": [
@@ -1996,6 +2086,31 @@
["L38", "vector"]
],
"depth-cue": [
["L17", "depth-cue-work"]
],
"navigate": [
["L402", "vector"],
["L403", "vector"],
["L404", "vector"],
["L405", "vector"],
["L406", "vector"],
["L422", "vector"],
["L419", "(pointer uint8)", 4],
["L425", "float", true],
["L426", "float", true],
["L429", "float", true],
["L430", "float", true],
["L436", "float", true],
["L438", "float", true],
["L444", "float", true],
["L442", "float", true],
["L520", "vector"],
["L522", "float", true], // TODO - meters
["L523", "float", true] // TODO - meters
],
"shrubbery": [
["L133", "vu-function"]
],
@@ -696,7 +696,7 @@
[48, "collide-tri-result"]
],
"(method 20 collide-cache)": [[16, "vector"]],
"(method 20 collide-cache)": [[16, "collide-puyp-work"]],
"(method 12 wobbler)": [[16, "vector"]],
@@ -1802,7 +1802,7 @@
[160, "vector"]
],
"(method 18 collide-cache)": [[16, "collide-cache-prim"]],
"(method 18 collide-cache)": [[16, "collide-puls-work"]],
"kill-current-level-hint": [[16, "event-message-block"]],
"(exit level-hint-sidekick)": [[16, "event-message-block"]],
@@ -4139,7 +4139,7 @@
],
"(method 9 touching-list)": [
[16, "touching-shapes-entry"]
[16, "add-prims-touching-work"]
],
"(method 11 touching-prims-entry)": [
@@ -4147,7 +4147,7 @@
],
"(method 35 collide-shape)": [
[16, "pull-rider-info"],
[16, "collide-overlap-result"],
[128, "matrix"],
[192, "event-message-block"],
[272, "collide-overlap-result"],
@@ -4196,11 +4196,11 @@
"(method 59 collide-shape-moving)": [
[16, "collide-tri-result"],
[112, "touching-shapes-entry"]
[112, "overlaps-others-params"]
],
"(method 58 collide-shape-moving)": [
[16, "touching-shapes-entry"]
[16, "overlaps-others-params"]
],
"simple-collision-reaction": [
@@ -4256,16 +4256,16 @@
],
"(method 45 collide-shape)": [
[16, "collide-work"],
[16, "collide-overlap-result"],
[128, "vector"],
[144, "vector"],
[160, "collide-work"],
[160, "collide-overlap-result"],
[272, "vector"],
[288, "vector"],
[304, "collide-work"],
[304, "collide-overlap-result"],
[416, "vector"],
[432, "vector"],
[448, "collide-work"],
[448, "collide-overlap-result"],
[560, "vector"],
[576, "vector"]
],
@@ -4366,7 +4366,7 @@
"target-collision-reaction": [
[16, "vector"],
[32, "vector"],
[48, "vector"],
[48, "matrix"],
[112, "vector"],
[128, "vector"],
[144, "vector"],
@@ -4415,12 +4415,12 @@
"find-ground-point": [
[16, "vector"],
[32, "collide-mesh-cache-tri"],
[32, "collide-tri-result"],
[128, "bounding-box"],
[160, "vector"]
],
"(method 19 collide-shape-prim-sphere)": [[16, "collide-mesh-cache-tri"]],
"(method 19 collide-shape-prim-sphere)": [[16, "collide-tri-result"]],
"(method 37 collide-shape)": [[16, "vector"]],
"starts": [
@@ -6017,5 +6017,341 @@
[16, "event-message-block"]
],
"(trans joint-exploder-shatter)": [
[16, "bounding-box"]
],
"(method 22 joint-exploder)": [
[16, "vector"],
[32, "collide-tri-result"]
],
"(enter target-tube-hit)": [
[16, "event-message-block"]
],
"(event slide-control-ride slide-control)": [
[16, "event-message-block"]
],
"(code target-tube-death)": [
[16, "vector"]
],
"(code target-tube-hit)": [
[16, "vector"],
[32, "vector"],
[48, "vector"],
[64, "vector"],
[80, "vector"]
],
"find-target-point": [
[16, "vector"],
[32, "vector"],
[48, "vector"]
],
"(trans slide-control-watch slide-control)": [
[16, "vector"],
[32, "event-message-block"]
],
"distance-from-tangent": [
[16, "vector"],
[32, "vector"],
[48, "vector"]
],
"(method 14 collide-cache)" : [
[16, "bounding-box"]
],
"(method 23 collide-shape-prim-mesh)" :[
[16, "collide-tri-result"]
],
"(method 23 collide-shape-prim-sphere)" : [
[16, "vector"],
[32, "vector"],
[48, "collide-tri-result"],
[144, "vector"],
[160, "vector"]
],
"(method 18 collide-shape-prim-sphere)" : [
[16, "collide-tri-result"]
],
"target-collision-low-coverage" : [
[64, "collide-tri-result"],
[176, "vector"],
[192, "vector"],
[224, "vector"],
[240, "vector"],
[256, "vector"],
[288, "vector"]
],
"(code ogreboss-missile-idle)": [
[16, "vector"],
[32, "vector"],
[48, "collide-tri-result"]
],
"(code ogreboss-missile-seek)": [
[16, "vector"],
[32, "vector"],
[48, "collide-tri-result"]
],
"ogreboss-rock-explosion-effect": [
[16, "event-message-block"]
],
"(code ogreboss-dead)": [
[16, "event-message-block"],
[96, "vector"]
],
"ogreboss-shoot-boulder": [
[16, "ogreboss-missile-init-data"],
[48, "vector"]
],
"(code ogreboss-die)": [
[16, "event-message-block"]
],
"(code ogreboss-stage3-hit)": [
[16, "event-message-block"]
],
"ogreboss-trigger-steps": [
[16, "event-message-block"]
],
"(code ogreboss-stage3-throw)": [
[16, "event-message-block"]
],
"ogreboss-attack-event-handler": [
[16, "event-message-block"]
],
"(exit ogreboss-stage3-shuffle)": [
[16, "event-message-block"]
],
"(trans ogreboss-stage3-shuffle)": [
[16, "event-message-block"]
],
"ogreboss-submerge": [
[16, "event-message-block"]
],
"ogreboss-post": [
[16, "event-message-block"]
],
"ogreboss-emerge": [
[16, "event-message-block"]
],
"(code ogreboss-intro)": [
[16, "event-message-block"]
],
"ogreboss-bounce-boulder-event-handler": [
[16, "event-message-block"]
],
"(enter ogreboss-idle)": [
[16, "event-message-block"]
],
"ogreboss-super-boulder-event-handler": [
[16, "event-message-block"]
],
"(code ogreboss-super-boulder-roll)": [
[16, "event-message-block"]
],
"(code ogreboss-missile-impact)": [
[16, "overlaps-others-params"]
],
"(event ogreboss-missile-impact)": [
[16, "event-message-block"],
[96, "vector"],
[112, "vector"],
[128, "vector"],
[224, "vector"]
],
"(method 18 collide-edge-work)": [
[16, "pbhp-stack-vars"]
],
"(method 9 edge-grab-info)": [
[16, "collide-using-spheres-params"],
[48, "event-message-block"]
],
"(method 20 nav-mesh)": [
[16, "vector"],
[32, "vector"]
],
"nav-ray-test": [
[16, "vector"],
[32, "vector"]
],
"circle-tangent-directions": [
[16, "vector"],
[32, "vector"],
[48, "vector"],
[64, "vector"]
],
"(method 18 nav-control)": [
[16, "vector"]
],
"(method 9 nav-control)": [
[16, "vector"],
[32, "vector"],
[48, "vector"],
[64, "vector"],
[80, "vector"],
[96, "vector"],
[112, "vector"],
[128, "vector"],
[144, "vector"],
[160, "vector"],
[176, "vector"]
],
"(method 11 nav-control)": [
[16, "vector"],
[32, "nav-gap-info"],
[64, "event-message-block"]
],
"(method 12 nav-control)": [
[16, "vector"]
],
"(method 13 nav-mesh)": [
[16, "vector"]
],
"(method 18 nav-mesh)": [
[16, "vector"]
],
"(method 24 nav-control)": [
[16, "vector"]
],
"(method 24 nav-mesh)": [
[16, "vector"],
[32, "vector"]
],
"(method 22 nav-control)": [
[16, "vector"]
],
"(method 17 nav-control)": [
[16, "vector"]
],
"(method 19 nav-control)": [
[16, "vector"],
[32, "vector"]
],
"(method 17 nav-mesh)": [
[16, "vector"],
[32, "vector"],
[48, ["array", "int8", 256]]
],
"(method 25 nav-mesh)": [
[16, "vector"],
[32, "matrix"]
],
"debug-nav-validate-current-poly": [
[16, "vector"]
],
"(method 13 nav-control)": [
[16, "vector"],
[32, "vector"],
[48, "vector"],
[64, "nav-route-portal"], // nav-poly | nav-route-portal | nav-route-portal
[112, "vector"],
[128, "clip-travel-vector-to-mesh-return-info"],
[288, "vector"]
],
"(method 28 nav-mesh)": [
[16, ["inline-array", "nav-vertex", 3]]
],
"choose-travel-portal-vertex": [
[16, "nav-route-portal"]
],
"(method 33 nav-control)": [
[16, "event-message-block"]
],
"(method 27 nav-control)": [
[16, "nav-ray"]
],
"(method 16 nav-mesh)": [
[16, "nav-ray"]
],
"(method 29 nav-mesh)": [
[16, ["inline-array", "nav-vertex", 6]]
],
"find-closest-circle-ray-intersection": [
[16, "vector"]
],
"(method 20 nav-control)": [
[16, "vector"]
],
"(method 21 nav-control)": [
[16, "vector"]
],
"(method 23 nav-control)": [
[16, "vector"],
[32, "vector"],
[48, "vector"]
],
"(method 16 nav-control)": [
[16, "vector"]
],
"(method 25 nav-control)": [
[16, "vector"]
],
"(method 28 nav-control)": [
[16, "vector"]
],
"(method 35 nav-control)": [
[16, "vector"]
],
"(method 15 nav-mesh)": [
[16, "nav-ray"]
],
"nav-ray-test-local?": [
[16, "nav-ray"]
],
"find-adjacent-bounds": [
[16, "vector"]
],
"(method 32 nav-control)": [
[16, "nav-control-cfs-work"]
],
"placeholder-do-not-add-below!": []
}
@@ -811,30 +811,10 @@
[[115, 154], "s3", "continue-point"]
],
"(method 20 level)": [[[43, 45], "s3", "ramdisk-rpc-fill"]],
"(anon-function 29 process-drawable)": [[[0, 999], "s6", "process-drawable"]],
"ja-done?": [[[0, 999], "s6", "process-drawable"]],
"ja-min?": [[[0, 999], "s6", "process-drawable"]],
"ja-max?": [[[0, 999], "s6", "process-drawable"]],
"ja-num-frames": [[[0, 999], "s6", "process-drawable"]],
"ja-frame-num": [[[0, 999], "s6", "process-drawable"]],
"ja-aframe-num": [[[0, 999], "s6", "process-drawable"]],
"ja-aframe": [[[0, 999], "s6", "process-drawable"]],
"ja-step": [[[0, 999], "s6", "process-drawable"]],
"ja-channel-set!": [[[0, 999], "s6", "process-drawable"]],
"ja-channel-push!": [[[0, 999], "s6", "process-drawable"]],
"ja-group-size": [[[0, 999], "s6", "process-drawable"]],
"ja-eval": [[[0, 999], "s6", "process-drawable"]],
"ja-blend-eval": [[[0, 999], "s6", "process-drawable"]],
"ja-post": [
[[0, 999], "s6", "process-drawable"],
[54, "a1", "process"]
],
"transform-post": [[[0, 999], "s6", "process-drawable"]],
"rider-trans": [[[0, 999], "s6", "process-drawable"]],
"rider-post": [[[0, 999], "s6", "process-drawable"]],
"pusher-post": [[[0, 999], "s6", "process-drawable"]],
"process-drawable-delay-player": [[[0, 999], "s6", "process-drawable"]],
"upload-generic-shrub": [
[[3, 13], "t0", "dma-packet"],
@@ -1494,45 +1474,34 @@
],
"cam-standard-event-handler": [
[[0, 999], "s6", "camera-slave"],
[[16, 30], "s5", "state"],
[41, "a0", "vector"],
[[5, 8], "t9", "(function object)"],
[[19, 22], "t9", "(function object)"],
[[30, 32], "t9", "(function object)"]
],
"cam-curve-pos": [[[0, 224], "s6", "camera-slave"]],
"cam-combiner-init": [[[0, 999], "s6", "camera-combiner"]],
"(code cam-combiner-active)": [[[0, 999], "s6", "camera-combiner"]],
"(event cam-combiner-active)": [
[10, "a0", "vector"],
[[0, 20], "s6", "camera-slave"],
[[20, 231], "s6", "camera-combiner"],
[[99, 127], "gp", "camera-slave"],
[[187, 231], "gp", "camera-slave"]
],
"cam-master-init": [[[0, 999], "s6", "camera-master"]],
"cam-curve-setup": [[[0, 82], "s6", "camera-slave"]],
"(method 15 tracking-spline)": [
[[57, 59], "a2", "vector"],
[[57, 59], "a3", "vector"]
],
"(method 16 tracking-spline)": [
[[40, 42], "a0", "vector"],
[[40, 42], "a1", "vector"]
],
"cam-slave-init-vars": [[[0, 999], "s6", "camera-slave"]],
"cam-slave-get-vector-with-offset": [[[52, 65], "s3", "vector"]],
"cam-slave-go": [[[3, 6], "t9", "(function object)"]],
"cam-slave-init": [
[[0, 999], "s6", "camera-slave"],
[[47, 50], "t9", "(function object object)"],
[[54, 58], "t9", "(function object object)"]
],
@@ -2314,6 +2283,8 @@
"(method 14 level-group)": [
[[54, 164], "s1", "process-drawable"],
[[107, 127], "s0", "(pointer int32)"],
[[153, 162], "v0", "symbol"],
[[319, 342], "s0", "process-drawable"],
[368, "v1", "(pointer process-drawable)"],
[[384, 494], "s5", "process-drawable"]
@@ -2327,8 +2298,6 @@
"(method 27 entity-ambient)": [[[15, 250], "s5", "symbol"]],
"cam-master-effect": [[[0, 999], "s6", "camera-master"]],
"birth-func-vector-orient": [[[7, 24], "s3", "sprite-vec-data-2d"]],
"process-drawable-burn-effect": [
@@ -4002,23 +3971,26 @@
"(method 32 sage-finalboss)": [
[[241, 245], "v1", "manipy"],
[[309, 313], "v1", "manipy"]
[[309, 313], "v1", "manipy"],
[313, "v1", "silodoor"]
],
"(trans play-anim sage-finalboss)": [
[[179, 183], "a0", "manipy"],
[[216, 220], "a0", "manipy"],
[[295, 299], "a1", "manipy"],
[[334, 338], "a1", "manipy"],
[[371, 375], "a1", "manipy"],
[391, "v0", "final-door"],
[396, "v0", "final-door"]
],
"(method 7 sage-finalboss)": [
[2, "v1", "(inline-array sage-finalboss-particle)"],
[6, "v1", "(inline-array sage-finalboss-particle)"],
[10, "v1", "(inline-array sage-finalboss-particle)"]
[186, "v1", "process-drawable"],
[223, "v1", "process-drawable"],
[300, "v1", "process-drawable"],
[339, "v1", "process-drawable"],
[376, "v1", "process-drawable"],
[399, "gp", "final-door"],
[401, "a0", "final-door"]
// [[179, 183], "a0", "manipy"],
// [[182, 187], "v1", "manipy"],
// [[216, 220], "a0", "manipy"],
// [[295, 299], "a1", "manipy"],
// [[334, 338], "a1", "manipy"],
// [[371, 375], "a1", "manipy"],
// [391, "v0", "final-door"],
// [396, "v0", "final-door"]
],
"(trans fisher-done)": [[[41, 46], "v1", "dma-packet"]],
@@ -4222,10 +4194,6 @@
[63, "t9", "(function cspace basic basic int)"]
],
"process-grab?": [
[18, "s6", "camera-tracker"]
],
"joint-control-copy!": [
[8, "a0", "pointer"],
[8, "a2", "pointer"]
@@ -5655,19 +5623,19 @@
],
"(method 14 touching-list)": [
[5, "s5", "touching-shapes-entry"],
[10, "s5", "touching-shapes-entry"]
[[0,11], "s5", "touching-shapes-entry"]
],
"(method 13 touching-list)": [
[5, "v0", "touching-shapes-entry"],
[10, "v0", "touching-shapes-entry"],
[17, "v0", "touching-shapes-entry"],
[26, "v0", "touching-shapes-entry"],
[46, "v0", "touching-shapes-entry"],
[47, "v0", "touching-shapes-entry"],
[48, "v0", "touching-shapes-entry"],
[50, "v0", "touching-shapes-entry"]
[[0, 51], "v0", "touching-shapes-entry"]
// [5, "v0", "touching-shapes-entry"],
// [10, "v0", "touching-shapes-entry"],
// [17, "v0", "touching-shapes-entry"],
// [26, "v0", "touching-shapes-entry"],
// [46, "v0", "touching-shapes-entry"],
// [47, "v0", "touching-shapes-entry"],
// [48, "v0", "touching-shapes-entry"],
// [50, "v0", "touching-shapes-entry"]
],
"(method 11 touching-list)": [
@@ -5729,17 +5697,15 @@
],
"(method 28 collide-shape-prim-mesh)": [
[27, "s4", "collide-shape-prim-group"]
[[22,45], "s4", "(array collide-mesh)"]
],
"(method 53 collide-shape)": [
[26, "a1", "collide-shape-prim-group"],
[36, "v1", "collide-shape-prim-group"]
[[24, 40], "v1", "collide-shape-prim-group"]
],
"(method 54 collide-shape)": [
[22, "a1", "collide-shape-prim-group"],
[29, "v1", "collide-shape-prim-group"]
[[18, 33], "v1", "collide-shape-prim-group"]
],
"(method 45 collide-shape)": [
@@ -5772,16 +5738,14 @@
],
"(method 9 collide-edge-work)": [
[10, "s3", "collide-edge-edge"],
[16, "s4", "collide-edge-hold-item"],
[46, "s4", "(inline-array collide-edge-hold-item)"],
[48, "s3", "(inline-array collide-edge-edge)"]
[[5, 52], "s3", "collide-edge-edge"],
[[5, 52], "s4", "collide-edge-hold-item"]
],
"(method 19 collide-edge-work)": [
[150, "a1", "int"],
[150, "v1", "int"],
[[149, 162], "a0", "collide-shape-prim-group"]
[150, "v1", "int"]
//[[149, 162], "a0", "collide-shape-prim-group"]
],
"collide-probe-make-list": [
@@ -5829,16 +5793,13 @@
],
"(method 9 collide-cache)": [
[5, "gp", "collide-cache-tri"],
[19, "gp", "collide-cache-tri"],
[20, "gp", "collide-cache-tri"],
[21, "gp", "collide-cache-tri"],
[23, "gp", "(inline-array collide-cache-tri)"],
[33, "gp", "collide-cache-prim"],
[[1,29], "gp", "collide-cache-tri"],
[[29,56], "gp", "collide-cache-prim"],
[35, "gp", "collide-cache-prim"],
[50, "gp", "collide-cache-prim"],
[51, "gp", "collide-cache-prim"],
[55, "gp", "(inline-array collide-cache-prim)"]
[55, "gp", "collide-cache-prim"],
[36, "v1", "collide-shape-prim-sphere"]
],
"(method 9 collide-mesh)": [
@@ -5847,7 +5808,8 @@
],
"(method 22 collide-shape-prim-mesh)": [
[10, "s4", "collide-shape-prim-group"]
[10, "s4", "collide-shape-prim-group"],
[41, "s4", "collide-shape-prim-group"]
],
"(method 44 collide-shape)": [
@@ -7393,6 +7355,473 @@
[31, "s4", "energyarm"]
],
"(method 0 joint-exploder-tuning)": [
[[6, 50], "v0", "joint-exploder-tuning"]
],
"(method 23 joint-exploder)": [
[[12, 102], "s3", "joint-exploder-joint"],
[[144, 146], "v1", "joint-exploder-list"],
[148, "v1", "matrix"],
[152, "v1", "matrix"]
],
"(method 20 joint-exploder)": [
[[8, 10], "a3", "joint-exploder-joint"],
[15, "v1", "joint-exploder-joint"]
],
"(method 27 joint-exploder)": [
[41, "s0", "joint-exploder-joint"],
[90, "s0", "joint-exploder-joint"],
[139, "s0", "joint-exploder-joint"]
],
"(method 25 joint-exploder)": [
[[16, 54], "s2", "joint-exploder-joint"]
],
"(method 22 joint-exploder)": [
[[18, 78], "s5", "joint-exploder-joint"]
],
"joint-exploder-joint-callback": [
[[10, 18], "v1", "joint-exploder-joint"]
],
"(method 24 joint-exploder)": [
[[12, 19], "v1", "joint-exploder-joint"]
],
"(method 26 joint-exploder)": [
[[5, 8], "a2", "joint-exploder-joint"],
[18, "v1", "joint-exploder-joint"],
[28, "v1", "joint-exploder-joint"]
],
"racer-effects": [
[739, "v0", "sound-rpc-set-param"]
],
"(code target-tube)": [
[31, "v1", "art-joint-anim"]
],
"(event slide-control-ride slide-control)": [
[24, "gp", "process-drawable"],
[31, "v1", "vector"],
[35, "v1", "vector"],
[39, "v1", "vector"]
],
"(code target-tube-start)": [
[110, "v1", "float"]
],
"depth-cue-set-stencil": [
[[1, 7], "t1", "dma-packet"],
[[10, 16], "t1", "gs-gif-tag"],
[27, "t1", "(pointer gs-xy-offset)"],
[29, "t1", "(pointer gs-reg64)"],
[34, "t1", "(pointer gs-frame)"],
[36, "t1", "(pointer gs-reg64)"],
[38, "t1", "(pointer gs-test)"],
[40, "t1", "(pointer gs-reg64)"],
[[43, 53], "a3", "(inline-array vector4w)"],
[[52, 80], "v0", "(inline-array vector4w)"]
],
"depth-cue-draw-front": [
[[26, 32], "t6", "dma-packet"],
[[33, 41], "t6", "gs-gif-tag"],
[49, "t6", "(pointer gs-xy-offset)"],
[51, "t6", "(pointer gs-reg64)"],
[56, "t6", "(pointer gs-frame)"],
[58, "t6", "(pointer gs-reg64)"],
[64, "t6", "(pointer gs-tex0)"],
[66, "t6", "(pointer gs-reg64)"],
[68, "t6", "(pointer gs-test)"],
[70, "t6", "(pointer gs-reg64)"],
[71, "t6", "(pointer gs-alpha)"],
[73, "t6", "(pointer gs-reg64)"],
[[76, 109], "t5", "(inline-array vector4w)"],
[112, "t5", "depth-cue-work"],
[[115, 121], "t6", "dma-packet"],
[[122, 130], "t6", "gs-gif-tag"],
[137, "t6", "(pointer gs-xy-offset)"],
[139, "t6", "(pointer gs-reg64)"],
[144, "t6", "(pointer gs-frame)"],
[146, "t6", "(pointer gs-reg64)"],
[148, "t6", "(pointer gs-texa)"],
[150, "t6", "(pointer gs-reg64)"],
[156, "t6", "(pointer gs-tex0)"],
[158, "t6", "(pointer gs-reg64)"],
[160, "t6", "(pointer gs-alpha)"],
[162, "t6", "(pointer gs-reg64)"],
[[165, 190], "t5", "(inline-array vector4w)"],
[[191, 201], "t5", "vector4w"],
[201, "t5", "depth-cue-work"]
],
"depth-cue-draw-depth": [
[[26, 32], "t6", "dma-packet"],
[[33, 41], "t6", "gs-gif-tag"],
[49, "t6", "(pointer gs-xy-offset)"],
[51, "t6", "(pointer gs-reg64)"],
[56, "t6", "(pointer gs-frame)"],
[58, "t6", "(pointer gs-reg64)"],
[64, "t6", "(pointer gs-tex0)"],
[66, "t6", "(pointer gs-reg64)"],
[68, "t6", "(pointer gs-test)"],
[70, "t6", "(pointer gs-reg64)"],
[[73, 106], "t5", "(inline-array vector4w)"],
[109, "t5", "depth-cue-work"],
[[112, 118], "t6", "dma-packet"],
[[121, 127], "t6", "gs-gif-tag"],
[134, "t6", "(pointer gs-xy-offset)"],
[136, "t6", "(pointer gs-reg64)"],
[141, "t6", "(pointer gs-frame)"],
[143, "t6", "(pointer gs-reg64)"],
[149, "t6", "(pointer gs-tex0)"],
[151, "t6", "(pointer gs-reg64)"],
[153, "t6", "(pointer gs-test)"],
[155, "t6", "(pointer gs-reg64)"],
[[158, 183], "t5", "(inline-array vector4w)"],
[[184, 193], "t5", "vector4w"],
[194, "t5", "depth-cue-work"]
],
"depth-cue": [
[[22, 28], "a2", "dma-packet"],
[[31, 37], "a2", "gs-gif-tag"],
[42, "a2", "(pointer gs-test)"],
[44, "a2", "(pointer gs-reg64)"],
[46, "a2", "(pointer gs-zbuf)"],
[48, "a2", "(pointer gs-reg64)"],
[50, "a2", "(pointer gs-reg64)"],
[52, "a2", "(pointer gs-reg64)"],
[53, "a2", "(pointer gs-tex1)"],
[55, "a2", "(pointer gs-reg64)"],
[62, "a2", "(pointer gs-clamp)"],
[64, "a2", "(pointer gs-reg64)"],
[66, "a2", "(pointer gs-reg64)"],
[68, "a2", "(pointer gs-reg64)"],
[[94, 100], "a0", "dma-packet"],
[[103, 109], "a0", "gs-gif-tag"],
[120, "a0", "(pointer gs-xy-offset)"],
[122, "a0", "(pointer gs-reg64)"],
[127, "a0", "(pointer gs-frame)"],
[129, "a0", "(pointer gs-reg64)"],
[[133, 138], "v1", "dma-packet"]
],
"collide-probe-collide-fragment-tree-make-list": [
[5, "v1", "drawable-inline-array-node"]
],
"collide-probe-instance-tie-tree-make-list": [
[[5,7], "v1", "drawable-inline-array-node"],
[[18,20], "v1", "drawable-inline-array-instance-tie"]
],
"collide-upload-vu0": [
[16, "a0", "dma-packet"],
[17, "a0", "(pointer uint64)"]
],
"collide-probe-make-list": [
[[20, 22], "v1", "drawable-inline-array-node"],
[[31, 33], "v1", "drawable-inline-array-instance-tie"],
[[47, 49], "v1", "drawable-inline-array-node"]
],
"(method 21 collide-cache)": [
[114, "a0", "(pointer int32)"],
[156, "t0", "(pointer int32)"],
[190, "v1", "(pointer int32)"],
[147, "v1", "collide-list-item"],
[148, "v1", "collide-list-item"],
[[91, 95], "v1", "dma-packet"],
[[112, 141], "v1", "dma-bank-spr"],
[[154, 188], "a2", "dma-bank-spr"],
[[217, 227], "s3", "collide-list-item"]
],
"(method 23 collide-shape-prim-sphere)": [
[[74,114], "s4", "collide-shape-prim-mesh"]
],
"(method 13 collide-mesh)" : [
[[0, 60], "a3", "(inline-array vector)"],
[[61, 123], "v1", "collide-mesh-tri"]
],
"(method 20 collide-shape-prim-group)": [
[5, "gp", "pointer"],
[6, "v1", "(pointer collide-shape-prim)"],
[[7,14], "a0", "collide-shape-prim"],
[32, "gp", "pointer"],
[33, "v1", "(pointer collide-shape-prim)"],
[[34,40], "a0", "collide-shape-prim"],
[[40, 46], "a0", "collide-shape-prim-group"]
],
"(method 29 collide-shape-prim-group)" : [
[5, "gp", "pointer"],
[6, "v1", "(pointer collide-shape-prim)"],
[[13, 19], "a0", "collide-shape-prim-group"]
],
"(method 40 collide-shape)" : [
[21, "a0", "connection"],
[[22,40], "a0", "collide-shape-moving"],
[85, "a0", "connection"],
[[86, 104], "a0", "collide-shape-moving"],
[147, "a0", "connection"],
[[148, 166], "a0", "collide-shape-moving"],
[209, "a0", "connection"],
[[210, 228], "a0", "collide-shape-moving"]
],
"(method 15 collide-shape-prim-sphere)" : [
[[16, 55], "gp", "collide-shape-prim-mesh"]
],
"(method 25 collide-cache)" : [
[[83, 104], "a2", "(inline-array collide-cache-tri)"]
],
"(method 22 collide-cache)" : [
[14, "v1", "connection"],
[[15,31], "v1", "collide-shape"],
[74, "v1", "connection"],
[[75, 91], "v1", "collide-shape"],
[130, "v1", "connection"],
[[131, 148], "v1", "collide-shape"],
[187, "v1", "connection"],
[[188, 205], "v1", "collide-shape"]
],
"(method 12 collide-shape-prim-sphere)" : [
[[13, 23], "t0", "collide-cache-prim"]
],
"(method 24 collide-cache)" : [
[14, "v1", "connection"],
[[15,31], "v1", "collide-shape"],
[74, "v1", "connection"],
[[75, 91], "v1", "collide-shape"],
[130, "v1", "connection"],
[[131, 148], "v1", "collide-shape"],
[187, "v1", "connection"],
[[188, 205], "v1", "collide-shape"]
],
"(method 14 collide-shape-prim-sphere)" : [
[[11, 23], "t0", "collide-cache-prim"]
],
"(method 23 collide-cache)" : [
[20, "v1", "connection"],
[[21,43], "v1", "collide-shape"],
[86, "v1", "connection"],
[[87,109], "v1", "collide-shape"],
[148, "v1", "connection"],
[[149, 174], "v1", "collide-shape"],
[211, "v1", "connection"],
[[212, 235], "v1", "collide-shape"]
],
"(method 13 collide-shape-prim-sphere)" : [
[[11, 23], "t0", "collide-cache-prim"]
],
"(method 31 collide-cache)" : [
[22, "v1", "collide-shape-prim-sphere"]
],
"(method 19 collide-cache)" : [
[[52, 94], "s4", "collide-cache-prim"],
[[1, 100], "s5", "collide-puss-work"]
],
"ogreboss-rock-explosion-effect": [
[83, "v1", "manipy"]
],
"ogreboss-missile-scale-explosion": [
[11, "gp", "process-drawable"],
[22, "gp", "process-drawable"]
],
"(event ogreboss-missile-impact)": [
[76, "t1", "target"]
],
"(code ogreboss-super-boulder-throw)": [
[16, "v1", "art-joint-anim"]
],
"ogreboss-emerge": [
[47, "v1", "art-joint-anim"]
],
"(code ogreboss-die)": [
[35, "v1", "art-joint-anim"]
],
"ogreboss-super-boulder-play-hit-anim": [
[15, "v1", "art-joint-anim"]
],
"(code ogreboss-stage3-hit)": [
[47, "v1", "art-joint-anim"]
],
"(code ogreboss-stage3-throw)": [
[33, "v1", "art-joint-anim"],
[89, "v1", "art-joint-anim"]
],
"ogreboss-shoot-boulder": [
[41, "a1", "process-drawable"]
],
"(method 7 ogreboss-super-boulder)": [
[14, "t9", "(function process-drawable int process-drawable)"]
],
"ogreboss-bounce-boulder-init-by-other": [
[112, "v1", "float"]
],
"(code ogreboss-stage3-shuffle)": [
[33, "v1", "art-joint-anim"],
[121, "v1", "art-joint-anim"],
[177, "v1", "art-joint-anim"],
[247, "v1", "art-joint-anim"],
[299, "v1", "art-joint-anim"],
[355, "v1", "art-joint-anim"],
[425, "v1", "art-joint-anim"]
],
"(code ogreboss-stage2)": [
[26, "v1", "art-joint-anim"],
[104, "v1", "art-joint-anim"],
[158, "v1", "art-joint-anim"]
],
"ogreboss-update-super-boulder": [
[12, "a1", "ogreboss-super-boulder"]
],
"(trans ogreboss-stage3-shuffle)": [
[13, "v1", "ogreboss-super-boulder"]
],
"(code ogreboss-stage1)": [
[36, "v1", "art-joint-anim"],
[93, "v1", "art-joint-anim"],
[172, "v1", "art-joint-anim"],
[273, "v1", "art-joint-anim"],
[329, "v1", "art-joint-anim"],
[386, "v1", "art-joint-anim"]
],
"(code ogreboss-bounce-boulder-idle)": [
[81, "v1", "art-joint-anim"]
],
"ogreboss-idle-loop": [
[145, "v1", "art-joint-anim"],
[206, "v1", "art-joint-anim"],
[261, "v1", "art-joint-anim"]
],
"(code ogreboss-super-boulder-roll)": [
[123, "v1", "art-joint-anim"]
],
"(code ogreboss-intro)": [
[92, "v1", "art-joint-anim"]
],
"ogreboss-submerge": [
[130, "v1", "art-joint-anim"]
],
"ogreboss-pick-target": [
[31, "s3", "process-drawable"]
],
"(method 29 progress)": [
[290, "a0", "(pointer symbol)"],
[299, "v1", "(pointer symbol)"],
[308, "a0", "(pointer symbol)"],
[317, "v1", "(pointer symbol)"],
[326, "a0", "(pointer symbol)"],
[589, "a0", "(pointer symbol)"],
[599, "v1", "(pointer symbol)"],
[608, "a1", "(pointer symbol)"],
[617, "v1", "(pointer symbol)"],
[626, "a1", "(pointer symbol)"],
[894, "a0", "(pointer symbol)"],
[921, "a0", "(pointer symbol)"]
],
"(method 9 edge-grab-info)": [
[23, "a0", "int"],
[[24,31], "s5", "collide-shape-prim"],
[29, "a0", "process-drawable"],
[156, "s5", "collide-shape-prim"]
],
"circle-triangle-intersection-proc?": [
[[113, 134], "v1", "vector"]
],
"(method 28 nav-control)": [
[170, "v1", "connection"],
[[170, 245], "s0", "collide-shape"]
],
"(method 29 nav-mesh)": [
[38, "v1", "int"],
[40, "v1", "int"],
[41, "v1", "int"],
[64, "f1", "float"],
[63, "v1", "float"]
],
"nav-mesh-update-route-table": [
[19, "a3", "(pointer uint8)"],
[24, "a0", "(pointer uint8)"]
],
"nav-mesh-lookup-route": [
[6, "a0", "(pointer uint8)"]
],
"(method 11 nav-mesh)": [
[12, "a2", "(pointer uint8)"]
],
"(method 12 nav-mesh)": [
[13, "a2", "(pointer uint8)"]
],
"recursive-inside-poly": [
[16, "a0", "(pointer nav-node)"],
[29, "v1", "(pointer nav-node)"]
],
"entity-nav-login": [
["_stack_", 16, "res-tag"]
],
"(method 18 nav-mesh)": [
[34, "v1", "nav-poly"]
],
"test-func": [
[7, "f1", "float"]
],
@@ -3355,7 +3355,7 @@
"(method 18 bsp-header)": {
"vars": {
"a2-0": "existing-actor-count",
"a2-0": "actor-count",
"s4-0": "birth-idx",
"a0-4": "idx-to-birth",
"v1-25": "actor-to-birth",
@@ -3530,7 +3530,9 @@
"(method 14 level-group)": {
"vars": {
"s1-1": ["s1-1", "process-drawable"]
"s0-1": ["s0-1", "(pointer int32)"],
"s1-1": ["s1-1", "process-drawable"],
"v0-10": ["v0-10", "symbol"]
}
},
@@ -3607,12 +3609,6 @@
}
},
"(method 7 sage-finalboss)": {
"vars": {
"v1-0": ["v1-0", "(inline-array sage-finalboss-particle)"]
}
},
"(code robotboss-white-eco-movie)": {
"vars": {
"gp-1": ["gp-1", "handle"]
@@ -3936,5 +3932,45 @@
}
},
"(method 13 touching-list)": {
"vars": {
"v0-0": ["v0-0", "touching-shapes-entry"]
}
},
"(method 11 touching-list)": {
"vars": {
"s5-0": ["s5-0", "touching-shapes-entry"]
}
},
"recursive-inside-poly": {
"vars": {
"a1-2": ["a1-2", "nav-node"]
}
},
"vu-point-triangle-intersection?": {
"vars": {
"v1-0": ["v1-0", "int"],
"a0-1": ["a0-1", "int"],
"a1-1": ["a1-1", "int"]
}
},
"point-inside-poly?": {
"vars": {
"v1-6": ["v1-6", "int"],
"a0-3": ["a0-3", "int"],
"a1-6": ["a1-6", "int"]
}
},
"(method 29 nav-mesh)": {
"vars": {
"v1-10": ["v1-10", "int"]
}
},
"aaaaaaaaaaaaaaaaaaaaaaa": {}
}
+2 -2
View File
@@ -1,11 +1,11 @@
#pragma once
#include "common/util/assert.h"
#include <cstring>
#include <vector>
#include <string>
#include <stdexcept>
#include "common/common_types.h"
#include "decompiler/ObjectFile/LinkedWord.h"
#include "common/util/assert.h"
namespace decompiler {
class LinkedWordReader {
@@ -41,4 +41,4 @@ class LinkedWordReader {
const std::vector<LinkedWord>* m_words = nullptr;
u32 m_offset = 0;
};
} // namespace decompiler
} // namespace decompiler
+2 -2
View File
@@ -3,12 +3,12 @@
* Utility class to read a .STR file and extract the full file name.
*/
#include "common/util/assert.h"
#include <cstring>
#include "common/util/FileUtil.h"
#include "game/common/overlord_common.h"
#include "game/common/str_rpc_types.h"
#include "StrFileReader.h"
#include "common/util/assert.h"
namespace decompiler {
StrFileReader::StrFileReader(const std::string& file_path) {
@@ -180,4 +180,4 @@ std::string StrFileReader::get_full_name(const std::string& short_name) const {
return result;
}
} // namespace decompiler
} // namespace decompiler
+2 -2
View File
@@ -1,7 +1,7 @@
#include "TextureDB.h"
#include "common/util/assert.h"
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
namespace decompiler {
@@ -37,4 +37,4 @@ void TextureDB::add_texture(u32 tpage,
}
}
} // namespace decompiler
} // namespace decompiler
+1 -1
View File
@@ -107,7 +107,7 @@ GameTextResult process_game_text(ObjectFileData& data, GameTextVersion version)
auto string_start = (text_label.offset / 4) - 1;
// 8 for type tag and length fields, 1 for null char.
for (int j = 0, m = align16(8 + 1 + (int)text.length()) / 4;
j < m && string_start + j < read_words.size(); j++) {
j < m && string_start + j < (int)read_words.size(); j++) {
read_words.at(string_start + j)++;
}
}
+2 -2
View File
@@ -14,7 +14,7 @@
* check duplicate names
*/
#include <common/util/FileUtil.h>
#include "common/util/FileUtil.h"
#include "tpage.h"
#include "common/versions.h"
#include "decompiler/ObjectFile/ObjectFileDB.h"
@@ -703,4 +703,4 @@ TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
}
return stats;
}
} // namespace decompiler
} // namespace decompiler
+1 -1
View File
@@ -1041,7 +1041,7 @@ void ProxyPrototypeArrayTie::read_from_file(TypedRef ref,
prototype_array_tie.read_from_file(
get_and_check_ref_to_basic(ref, "prototype-array-tie", "prototype-array-tie", dts), dts,
stats);
// TODO wind
wind_vectors = deref_label(get_field_ref(ref, "wind-vectors", dts));
}
std::string ProxyPrototypeArrayTie::print(const PrintSettings& settings, int indent) const {
+1 -1
View File
@@ -352,7 +352,7 @@ struct ProxyPrototypeArrayTie {
std::string print(const PrintSettings& settings, int indent) const;
PrototypeArrayTie prototype_array_tie;
// todo wind vectors.
Ref wind_vectors;
};
struct DrawableTreeInstanceTie : public DrawableTree {
+6 -1
View File
@@ -4,6 +4,7 @@
#include "decompiler/level_extractor/BspHeader.h"
#include "decompiler/level_extractor/extract_tfrag.h"
#include "decompiler/level_extractor/extract_tie.h"
#include "common/util/compress.h"
#include "common/util/FileUtil.h"
namespace decompiler {
@@ -107,8 +108,12 @@ void extract_from_level(ObjectFileDB& db,
Serializer ser;
tfrag_level.serialize(ser);
auto compressed =
compression::compress_zstd(ser.get_save_result().first, ser.get_save_result().second);
fmt::print("compressed: {} -> {} ({:.2f}%)\n", ser.get_save_result().second, compressed.size(),
100.f * compressed.size() / ser.get_save_result().second);
file_util::write_binary_file(file_util::get_file_path({fmt::format(
"assets/{}.fr3", dgo_name.substr(0, dgo_name.length() - 4))}),
ser.get_save_result().first, ser.get_save_result().second);
compressed.data(), compressed.size());
}
} // namespace decompiler
+21 -6
View File
@@ -1,10 +1,10 @@
#include "extract_tfrag.h"
#include "common/dma/dma.h"
#include "common/util/assert.h"
#include "decompiler/util/Error.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "common/util/FileUtil.h"
#include "common/dma/gs.h"
#include "common/util/assert.h"
namespace decompiler {
namespace {
@@ -117,6 +117,7 @@ VisNodeTree extract_vis_data(const level_tools::DrawableTreeTfrag* tree, u16 fir
}
vis.num_kids = elt.child_count;
vis.flags = elt.flags;
vis.my_id = elt.id;
assert(vis.flags == expecting_leaves ? 0 : 1);
assert(vis.num_kids > 0);
assert(vis.num_kids <= 8);
@@ -2036,8 +2037,8 @@ void make_tfrag3_data(std::map<u32, std::vector<GroupedDraw>>& draws,
for (auto& strip : draw.strips) {
tfrag3::StripDraw::VisGroup vgroup;
vgroup.vis_idx = strip.tfrag_id; // associate with the tfrag for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
vgroup.vis_idx_in_pc_bvh = strip.tfrag_id; // associate with the tfrag for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
tdraw.num_triangles += strip.verts.size() - 2;
for (auto& vert : strip.verts) {
@@ -2114,6 +2115,19 @@ void extract_time_of_day(const level_tools::DrawableTreeTfrag* tree, tfrag3::Tfr
}
}
void merge_groups(std::vector<tfrag3::StripDraw::VisGroup>& grps) {
std::vector<tfrag3::StripDraw::VisGroup> result;
result.push_back(grps.at(0));
for (size_t i = 1; i < grps.size(); i++) {
if (grps[i].vis_idx_in_pc_bvh == result.back().vis_idx_in_pc_bvh) {
result.back().num += grps[i].num;
} else {
result.push_back(grps[i]);
}
}
std::swap(result, grps);
}
} // namespace
void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
@@ -2183,13 +2197,14 @@ void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
for (auto& draw : this_tree.draws) {
for (auto& str : draw.vis_groups) {
auto it = tfrag_parents.find(str.vis_idx);
auto it = tfrag_parents.find(str.vis_idx_in_pc_bvh);
if (it == tfrag_parents.end()) {
str.vis_idx = UINT32_MAX;
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx = it->second;
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
}
out.tfrag_trees.push_back(this_tree);
}
@@ -20,15 +20,6 @@ struct VisNodeTree {
bool only_children = false;
};
// The final result
struct ExtractedTFragmentTree {
// TFragmentKind kind = TFragmentKind::INVALID;
VisNodeTree vis_nodes;
u16 num_tfrags = 0;
u16 tfrag_base_idx = 0;
};
// will pool textures with others already in out.
void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
const std::string& debug_name,
+412 -125
View File
@@ -126,6 +126,7 @@ void extract_vis_data(const level_tools::DrawableTreeInstanceTie* tree,
}
vis.num_kids = elt.child_count;
vis.flags = elt.flags;
vis.my_id = elt.id;
assert(vis.flags == expecting_leaves ? 0 : 1);
assert(vis.num_kids > 0);
assert(vis.num_kids <= 8);
@@ -167,32 +168,28 @@ void extract_vis_data(const level_tools::DrawableTreeInstanceTie* tree,
}
}
// Each TIE prototype is broken up into "fragments". These "fragments" have some maximum size based
// on the VU memory limit, so an instance may have multiple fragments, depending on how many
// vertices are in the model.
// Each instance has different set of time of day colors per fragment in the prototype.
// this type contains the indicies of these colors.
// For the PC port we combine all colors into a single "big palette".
// this stores the indices as indices into the original game's per fragment palette.
// and an offset for where this palette is located in the big palette.
struct TieInstanceFragInfo {
// the color index table uploaded to VU.
// this contains indices into the shared palette.
std::vector<u8> color_indices;
// in the PC port format, we upload a single giant time of day color. this points to the offset
// of the colors from this frag instance.
u16 color_index_offset_in_big_palette = -1;
math::Vector<u32, 4> lq_colors_ui(u32 qw) const {
// note: this includes the unpack
assert(qw >= 204);
qw -= 204;
qw *= 4;
assert(qw + 4 <= color_indices.size());
math::Vector<u32, 4> result;
for (int i = 0; i < 4; i++) {
result[i] = color_indices.at(qw + i);
}
return result;
}
};
// Each TIE instance has one of these. This is reorganized/unpacked data from the instance-tie type.
struct TieInstanceInfo {
// The index of the prototype (the geometry) that is used by this instance
// note: we're going to trust that this lines up with bucket.
// if this assumption is wrong, we'll be drawing with the wrong model and it will be super
// obvious.
u16 prototype_idx = 0;
// our bsphere's index in the BVH tree
@@ -202,33 +199,44 @@ struct TieInstanceInfo {
// actually cull using the tree)
math::Vector4f bsphere;
// the transformation matrix, unpacked from the weird TIE format.
// this can be used to transform points directly to world-space points that work
// with the normal math camera stuff.
std::array<math::Vector4f, 4> mat;
// this value is stashed inside the above matrix. It tells which "wind" we should use
// we just need to pass this along to the C++ rendering code.
u16 wind_index = 0;
float unknown_wind_related_value = 0.f; // w of the first mat vec.
std::vector<TieInstanceFragInfo> frags; // per-instance per-fragment info
std::vector<TieInstanceFragInfo> frags; // per-instance per-fragment info (just colors)
};
// The 5 qw of adgif data contains draw settings, and they also snuck in some extra data.
struct AdgifInfo {
u32 first_w;
u32 second_w;
u32 third_w;
u32 combo_tex;
u64 alpha_val;
u64 clamp_val;
// secret stuff they snuck in
u32 first_w; // VU memory offset
u32 second_w; // some size
u32 third_w; // unused, at least for not-near TIE
// the draw settings we care about:
u32 combo_tex; // PC texture ID
u64 alpha_val; // alpha blend settings
u64 clamp_val; // texture clamp settings
};
// When the prototype is uploaded, it places a bunch of strgif tags in VU memory.
// we'll need to remember where these are.
struct StrGifInfo {
u16 address;
u16 nloop;
u16 mode; // not yet fully understood, but can allow the use of other templates.
bool eop;
u16 address; // vu memory address
u16 nloop; // the nloop field of this strgif (how much to send)
u16 mode; // not yet fully understood, but can allow the use of other templates
bool eop; // end of packet flag
};
// data per vertex in a tie prototype
struct TieProtoVertex {
math::Vector<float, 3> pos;
math::Vector<float, 3> tex;
math::Vector<float, 3> pos; // position
math::Vector<float, 3> tex; // texture coordinate
// NOTE: this is a double lookup.
// first you look up the index in the _instance_ color table
@@ -236,26 +244,34 @@ struct TieProtoVertex {
u32 color_index_index;
};
// a tie fragment is made up of strips. Each strip has a single adgif info, and vertices
// the vertices make up a triangle strip
struct TieStrip {
AdgifInfo adgif;
std::vector<TieProtoVertex> verts;
};
// the tie fragment
// this is a per-prototype (all instances share the same TieFrags)
struct TieFrag {
bool has_magic_tex0_bit = false;
std::vector<AdgifInfo> adgifs;
bool has_magic_tex0_bit = false; // use decal mode (todo)
std::vector<AdgifInfo>
adgifs; // the adgifs that come with this tiefrag (different strips can hve different)
std::vector<u8> other_gif_data;
std::vector<u8> points_data;
std::vector<u32> point_sizes;
std::vector<u8> other_gif_data; // data sent from EE asm code, sizes/offsets/metadata
std::vector<u8> points_data; // data sent from EE asm code, actual vertex data
// number of "dverts" expected from game's metadata. we check our extraction from this.
u32 expected_dverts = 0;
// all the strips in this fragment
std::vector<TieStrip> strips;
// this contains vertices, key is the start of the actual xyzf/st/rgbaq data for it.
// this contains vertices, key is the address of the actual xyzf/st/rgbaq data in VU1 memory
// after the prototype program runs
std::unordered_map<u32, TieProtoVertex> vertex_by_dest_addr;
// simulate a load in the points data (using vu mem addr)
math::Vector<float, 4> lq_points(u32 qw) const {
assert(qw >= 50);
qw -= 50;
@@ -265,6 +281,8 @@ struct TieFrag {
return result;
}
// simulate a load from points, but don't die if we load past the end
// this can happen when pipelining.
math::Vector<float, 4> lq_points_allow_past_end(u32 qw) const {
assert(qw >= 50);
qw -= 50;
@@ -277,6 +295,8 @@ struct TieFrag {
}
}
// store data into points. annoyingly the points have to be unpacked
// and they are modified in place.
void sq_points(u32 qw, const math::Vector4f& data) {
assert(qw >= 50);
qw -= 50;
@@ -284,6 +304,7 @@ struct TieFrag {
memcpy(points_data.data() + (qw * 16), data.data(), 16);
}
// do a ilw from the other gif data.
u16 ilw_other_gif(u32 qw, u32 offset) const {
// unpacked with v8.
int qwi = qw;
@@ -292,6 +313,7 @@ struct TieFrag {
return other_gif_data.at(qwi * 4 + offset);
}
// reg values from the prototype program that are used by the instance program.
struct ProgramInfo {
std::vector<u16> adgif_offset_in_gif_buf_qw;
std::vector<StrGifInfo> str_gifs;
@@ -310,16 +332,21 @@ struct TieFrag {
} prog_info;
};
// main instance type
// unlike the GOAL type, we store all instances info in here too.
struct TieProtoInfo {
std::string name;
std::vector<TieInstanceInfo> instances;
bool uses_generic = false;
float stiffness = 0;
float stiffness = 0; // wind
u32 generic_flag;
std::vector<tfrag3::TimeOfDayColor> time_of_day_colors;
std::vector<TieFrag> frags;
std::vector<tfrag3::TimeOfDayColor> time_of_day_colors; // c++ type for time of day data
std::vector<TieFrag> frags; // the fragments of the prototype
};
/*!
* Convert TIE packed matrix to normal one. this was figured out from the EE asm.
*/
std::array<math::Vector4f, 4> extract_tie_matrix(const u16* data) {
std::array<math::Vector4f, 4> result;
for (int i = 0; i < 4; i++) {
@@ -341,27 +368,52 @@ std::array<math::Vector4f, 4> extract_tie_matrix(const u16* data) {
return result;
}
// geometry we use (todo, should really look at this)
constexpr int GEOM_IDX = 1; // todo 0 or 1??
/*!
* Confirm that the initial value of all wind vectors is 0.
* If this is true, we don't have to actually save them to the fr3 file, we can just create
* a bunch of 0 vectors in the TIE setup.
*/
void check_wind_vectors_zero(const std::vector<TieProtoInfo>& protos, Ref wind_ref) {
u16 max_wind = 0;
for (auto& proto : protos) {
for (auto& inst : proto.instances) {
max_wind = std::max(inst.wind_index, max_wind);
}
}
u32 wind_words = max_wind;
wind_words *= 4;
for (size_t i = 0; i < wind_words; i++) {
auto& word = wind_ref.data->words_by_seg.at(wind_ref.seg).at(wind_ref.byte_offset / 4 + i);
assert(word.kind() == LinkedWord::PLAIN_DATA);
assert(word.data == 0);
}
}
// get per-instance info from the level data
std::vector<TieProtoInfo> collect_instance_info(
const level_tools::DrawableInlineArrayInstanceTie* instances,
const std::vector<level_tools::PrototypeBucketTie>* protos) {
std::vector<TieProtoInfo> result;
// loop over instances in level
for (auto& instance : instances->instances) {
// copy basic data.
TieInstanceInfo info;
info.prototype_idx = instance.bucket_index;
info.vis_id = instance.id;
for (int i = 0; i < 4; i++) {
info.bsphere[i] = instance.bsphere.data[i];
}
// from ee asm
info.mat = extract_tie_matrix(instance.origin.data);
info.mat[3][0] += info.bsphere[0];
info.mat[3][1] += info.bsphere[1];
info.mat[3][2] += info.bsphere[2];
info.wind_index = instance.wind_index;
// there's a value stashed here that we can get rid of
// it is related to wind.
info.unknown_wind_related_value = info.mat[0][3];
info.mat[0][3] = 0.f;
// each fragment has its own color data (3 dmatags)
@@ -374,10 +426,15 @@ std::vector<TieProtoInfo> collect_instance_info(
// and this is only the indices.... there's yet another lookup on the VU
auto& proto = protos->at(info.prototype_idx);
u32 offset_bytes = proto.base_qw[GEOM_IDX] * 16;
// loop over frags. this is only the per-instance info so only colors indices. We know the
// location/layout of the color data from the EE asm code.
for (int frag_idx = 0; frag_idx < proto.frag_count[GEOM_IDX]; frag_idx++) {
TieInstanceFragInfo frag_info;
// read the number of quadwords
u32 num_color_qwc = proto.color_index_qwc.at(proto.index_start[GEOM_IDX] + frag_idx);
// loop over 4-byte words
for (u32 i = 0; i < num_color_qwc * 4; i++) {
// loop over bytes in word
for (u32 j = 0; j < 4; j++) {
frag_info.color_indices.push_back(
instance.color_indices.data->words_by_seg.at(instance.color_indices.seg)
@@ -387,6 +444,7 @@ std::vector<TieProtoInfo> collect_instance_info(
}
info.frags.push_back(std::move(frag_info));
assert(info.frags.back().color_indices.size() > 0);
offset_bytes += num_color_qwc * 16;
}
@@ -399,6 +457,10 @@ std::vector<TieProtoInfo> collect_instance_info(
return result;
}
/*!
* adgif shader texture id's can be "remapped". I think it allows textures to be shared.
* So far we haven't seen this feature used, but we do have the texture map and we check it here.
*/
u32 remap_texture(u32 original, const std::vector<level_tools::TextureRemap>& map) {
auto masked = original & 0xffffff00;
for (auto& t : map) {
@@ -411,6 +473,9 @@ u32 remap_texture(u32 original, const std::vector<level_tools::TextureRemap>& ma
return original;
}
/*!
* Update per-proto information.
*/
void update_proto_info(std::vector<TieProtoInfo>* out,
const std::vector<level_tools::TextureRemap>& map,
const TextureDB& tdb,
@@ -419,12 +484,18 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
for (size_t i = 0; i < protos.size(); i++) {
const auto& proto = protos[i];
auto& info = out->at(i);
// the flags can either be 0 or 2.
assert(proto.flags == 0 || proto.flags == 2);
// flag of 2 means it should use the generic renderer (determined from EE asm)
// for now, we ignore this and use TIE on everything.
info.uses_generic = (proto.flags == 2);
// for debug, remember the name
info.name = proto.name;
// wind "stiffness" nonzero value means it has the wind effect
info.stiffness = proto.stiffness;
info.generic_flag = proto.flags & 2;
// the actual colors (rgba) used by time of day interpolation
// there are "height" colors. Each color is actually 8 colors that are interpolated.
info.time_of_day_colors.resize(proto.time_of_day.height);
for (int k = 0; k < (int)proto.time_of_day.height; k++) {
for (int j = 0; j < 8; j++) {
@@ -432,20 +503,41 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
}
}
// loop over fragments in the proto. This is the actual mesh data data and drawing settings
for (int frag_idx = 0; frag_idx < proto.frag_count[GEOM_IDX]; frag_idx++) {
TieFrag frag_info;
// loop over adgif shaders
for (int tex_idx = 0;
tex_idx < proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).tex_count / 5; tex_idx++) {
// this adgif shader data is modified in the real game by the login methods.
// all TIE things have pretty normal adgif shaders
// all the useful adgif data will be saved into this AdgifInfo
AdgifInfo adgif;
// pointer to the level data
auto& gif_data = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].gif_data;
// address for the first adgif shader qw.
u8 ra_tex0 = gif_data.at(16 * (tex_idx * 5 + 0) + 8);
// data for the first adgif shader qw.
u64 ra_tex0_val;
memcpy(&ra_tex0_val, &gif_data.at(16 * (tex_idx * 5 + 0)), 8);
// always expecting TEX0_1
assert(ra_tex0 == (u8)GsRegisterAddress::TEX0_1);
// the value is overwritten by the login function. We don't care about this value, it's
// specific to the PS2's texture system.
assert(ra_tex0_val == 0 || ra_tex0_val == 0x800000000); // note: decal
// the original value is a flag. this means to use decal texture mode (todo)
frag_info.has_magic_tex0_bit = ra_tex0_val == 0x800000000;
// there's also a hidden value in the unused bits of the a+d data. it'll be used by the
// VU program.
memcpy(&adgif.first_w, &gif_data.at(16 * (tex_idx * 5 + 0) + 12), 4);
// Second adgif. Similar to the first, except the original data value is a texture ID.
u8 ra_tex1 = gif_data.at(16 * (tex_idx * 5 + 1) + 8);
u64 ra_tex1_val;
memcpy(&ra_tex1_val, &gif_data.at(16 * (tex_idx * 5 + 1)), 8);
@@ -453,34 +545,41 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
assert(ra_tex1_val == 0x120); // some flag
u32 original_tex;
memcpy(&original_tex, &gif_data.at(16 * (tex_idx * 5 + 1) + 8), 4);
// try remapping it
u32 new_tex = remap_texture(original_tex, map);
if (original_tex != new_tex) {
fmt::print("map from 0x{:x} to 0x{:x}\n", original_tex, new_tex);
}
// texture the texture page/texture index, and convert to a PC port texture ID
u32 tpage = new_tex >> 20;
u32 tidx = (new_tex >> 8) & 0b1111'1111'1111;
u32 tex_combo = (((u32)tpage) << 16) | tidx;
// look up the texture to make sure it's valid
auto tex = tdb.textures.find(tex_combo);
assert(tex != tdb.textures.end());
// remember the texture id
adgif.combo_tex = tex_combo;
// and the hidden value in the unused a+d
memcpy(&adgif.second_w, &gif_data.at(16 * (tex_idx * 5 + 1) + 12), 4);
// todo: figure out if this matters
if (ra_tex0_val == 0x800000000) {
fmt::print("texture {} in {} has weird tex setting\n", tex->second.name, proto.name);
}
// mipmap settings. we ignore, but get the hidden value
u8 ra_mip = gif_data.at(16 * (tex_idx * 5 + 2) + 8);
assert(ra_mip == (u8)GsRegisterAddress::MIPTBP1_1);
memcpy(&adgif.third_w, &gif_data.at(16 * (tex_idx * 5 + 2) + 12), 4);
// who cares about the value
// clamp settings. we care about these. no hidden value.
u8 ra_clamp = gif_data.at(16 * (tex_idx * 5 + 3) + 8);
assert(ra_clamp == (u8)GsRegisterAddress::CLAMP_1);
u64 clamp;
memcpy(&clamp, &gif_data.at(16 * (tex_idx * 5 + 3)), 8);
adgif.clamp_val = clamp;
// alpha settings. we care about these, but no hidden value
u8 ra_alpha = gif_data.at(16 * (tex_idx * 5 + 4) + 8);
assert(ra_alpha == (u8)GsRegisterAddress::ALPHA_1);
u64 alpha;
@@ -488,7 +587,12 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
adgif.alpha_val = alpha;
frag_info.adgifs.push_back(adgif);
}
// they store a vertex count. we later use this to sanity check out mesh extraction
frag_info.expected_dverts = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].num_dverts;
// each frag also has "other" data. This is some index data that the VU program uses.
// it comes in gif_data, after tex_qwc (determined from EE program)
int tex_qwc = proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).tex_count;
int other_qwc = proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).gif_count;
frag_info.other_gif_data.resize(16 * other_qwc);
@@ -496,6 +600,8 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
proto.geometry[GEOM_IDX].tie_fragments[frag_idx].gif_data.data() + (16 * tex_qwc),
16 * other_qwc);
// each frag's "point" data. These are stored as int16's, but get unpacked to 32-bit ints by
// the VIF. (determined from EE program)
const auto& pr = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].point_ref;
int in_qw = pr.size() / 16;
int out_qw = in_qw * 2;
@@ -508,16 +614,15 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
}
}
// just for debug
for (int g = 0; g < 4; g++) {
frag_info.point_sizes.push_back(proto.geometry[g].tie_fragments[frag_idx].point_ref.size());
}
info.frags.push_back(std::move(frag_info));
}
}
}
// List of dma tags from the EE code.
// upload-palette/upload-model happen per prototype.
// (palette may happen per prototype, model per geometry, but we only use 1 geom)
// upload-palette-0: just a flusha
// no data
@@ -538,9 +643,12 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
// points
// upload-model-3
// mscal 6
// mscal 6 <- this runs a VU program that unpacks the model data
// call the models!
// These upload-color's happen per instance. They only happen after the upload-palette/model's
// happen for the given model.
// upload-color-0
// 6 qw of matrix plus flag stuff
// to 198 (relative to TOP)
@@ -549,9 +657,13 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
// to 204 unsigned (relative to TOP)
// upload-color-2/ret
// mscal 0
// mscal 0 <- this runs a VU program that generates GS data to draw the instance.
// MEMORY MAP of TIE
// these are quadword addresses.
// some things are double/triple buffered.
// we ignore this for the most part and by convention use the lower address.
// 0 gif tags
// extra gifs
// 32 model
@@ -574,6 +686,9 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
// 973 atest-tra
// 974 atest-def
// the vu program emulation will fill out the vertex positions/draw settings for each instance.
// helper functions for the vu programs
math::Vector4f itof0(const math::Vector4f& vec) {
math::Vector4f result;
for (int i = 0; i < 4; i++) {
@@ -1561,38 +1676,49 @@ void emulate_tie_instance_program(std::vector<TieProtoInfo>& protos) {
}
}
// makes per-prototype meshes
// the final step of the VU program emulation is the "xgkick" instruction.
// there is a signal xgkick per fragment and it goes through the entire gif buf, hitting
// strgifs and adgifs. We look at the memory map for each frag and figure out which strips
// go with which adgifs, then copy vertices
void emulate_kicks(std::vector<TieProtoInfo>& protos) {
for (auto& proto : protos) {
for (auto& frag : proto.frags) {
// we iterate over both adgifs/stgifs. sometimes you can have multiple strgifs that use the
// same adgif. But we never expect to see multiple adgifs in a row.
auto adgif_it = frag.prog_info.adgif_offset_in_gif_buf_qw.begin();
auto adgif_end = frag.prog_info.adgif_offset_in_gif_buf_qw.end();
auto str_it = frag.prog_info.str_gifs.begin();
auto str_end = frag.prog_info.str_gifs.end();
// but, we should always start with an adgif (otherwise we'd use the draw settings from
// the last model, which we don't know)
assert(frag.prog_info.adgif_offset_in_gif_buf_qw.at(0) == 0);
// and we expect that the VU program placed all adgifs somewhere
assert(frag.prog_info.adgif_offset_in_gif_buf_qw.size() == frag.adgifs.size());
const AdgifInfo* adgif_info = nullptr;
int expected_next_tag = 0;
// loop over strgifs
while (str_it != str_end) {
// try advance adgif
// try to see if we got a adgif here
if (adgif_it != adgif_end && (*adgif_it) == expected_next_tag) {
// yep
int idx = adgif_it - frag.prog_info.adgif_offset_in_gif_buf_qw.begin();
adgif_info = &frag.adgifs.at(idx);
// fmt::print("using adgif {}\n", *adgif_it);
// the next strgif should come 6 qw's after
expected_next_tag += 6;
adgif_it++;
}
assert(adgif_info);
// fmt::print("strip: {}\n", str_it->address);
// make sure the next str is where we expect
assert(expected_next_tag == str_it->address);
// the next tag (either str/adgif) should be located at the end of this tag's data.
expected_next_tag += 3 * str_it->nloop + 1;
// here we have the right str and adgif.
// kinda stupid, but we have to guess the base address of the gifbuf
// kinda stupid, but we have to guess the base address of the gifbuf we're using.
// 286 gifbuf
// 470 gifbuf again
// 654 ??
@@ -1605,12 +1731,15 @@ void emulate_kicks(std::vector<TieProtoInfo>& protos) {
base_address = 470;
}
// now, vertices!
// now, we can add the vertices!
frag.strips.emplace_back();
auto& strip = frag.strips.back();
strip.adgif = *adgif_info;
// loop over all the vertices the strgif says we'll have
for (int vtx = 0; vtx < str_it->nloop; vtx++) {
// compute the address of this vertex (stored after the strgif)
u32 vtx_addr = str_it->address + 1 + (3 * vtx) + base_address;
// and grab it from the vertex map we made earlier.
strip.verts.push_back(frag.vertex_by_dest_addr.at(vtx_addr));
}
@@ -1622,6 +1751,11 @@ void emulate_kicks(std::vector<TieProtoInfo>& protos) {
}
}
// from here on, we are mostly converting the "info" formats to the C++ renderer format (tfrag3)
/*!
* Just used to debug, save a proto as an .obj mesh file.
*/
std::string debug_dump_proto_to_obj(const TieProtoInfo& proto) {
std::vector<math::Vector<float, 3>> verts;
std::vector<math::Vector<float, 2>> tcs;
@@ -1678,16 +1812,12 @@ std::string debug_dump_proto_to_obj(const TieProtoInfo& proto) {
return result;
}
/*!
* Transform a point in a prototype to the actual point location in the game world.
*/
math::Vector<float, 3> transform_tie(const std::array<math::Vector4f, 4> mat,
const math::Vector3f& pt) {
auto temp = mat[0] * pt.x() + mat[1] * pt.y() + mat[2] * pt.z() + mat[3];
// math::Vector4f temp;
// temp.x() = pt.x();
// temp.y() = pt.y();
// temp.z() = pt.z();
// temp += mat[3];
math::Vector3f result;
result.x() = temp.x();
result.y() = temp.y();
@@ -1695,6 +1825,10 @@ math::Vector<float, 3> transform_tie(const std::array<math::Vector4f, 4> mat,
return result;
}
/*!
* Dump the entire tie tree to an obj. Used to debug the transform_tie function. If we get this
* right, it should fit in with .obj's produced from the tfrag debug.
*/
std::string dump_full_to_obj(const std::vector<TieProtoInfo>& protos) {
std::vector<math::Vector<float, 3>> verts;
std::vector<math::Vector<float, 2>> tcs;
@@ -1774,6 +1908,7 @@ struct BigPalette {
std::vector<tfrag3::TimeOfDayColor> colors;
};
// combine all individual time of day palettes into one giant one.
BigPalette make_big_palette(std::vector<TieProtoInfo>& protos) {
BigPalette result;
@@ -1800,6 +1935,9 @@ BigPalette make_big_palette(std::vector<TieProtoInfo>& protos) {
return result;
}
/*!
* Given a current draw mode, update the alpha settings from a gs-alpha register value.
*/
void update_mode_from_alpha1(u64 val, DrawMode& mode) {
GsAlpha reg(val);
if (reg.a_mode() == GsAlpha::BlendMode::SOURCE && reg.b_mode() == GsAlpha::BlendMode::DEST &&
@@ -1837,13 +1975,19 @@ void update_mode_from_alpha1(u64 val, DrawMode& mode) {
fmt::print("unsupported blend: a {} b {} c {} d {}\n", (int)reg.a_mode(), (int)reg.b_mode(),
(int)reg.c_mode(), (int)reg.d_mode());
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
// assert(false);
assert(false);
}
}
/*!
* Convert adgif info into a C++ renderer DrawMode.
*/
DrawMode process_draw_mode(const AdgifInfo& info, bool use_atest, bool use_decal) {
DrawMode mode;
// some of these are set up once as part of tie initialization
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
// the atest giftag is set up at the end of the VU program.
if (use_atest) {
mode.enable_at();
mode.set_aref(0x26);
@@ -1855,13 +1999,17 @@ DrawMode process_draw_mode(const AdgifInfo& info, bool use_atest, bool use_decal
if (use_decal) {
mode.enable_decal();
}
// set up once.
mode.enable_depth_write();
mode.enable_zt(); // :zte #x1
mode.set_depth_test(GsTest::ZTest::GEQUAL); // :ztst (gs-ztest greater-equal))
mode.disable_ab();
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
// the alpha matters
update_mode_from_alpha1(info.alpha_val, mode);
// the clamp matters
if (!(info.clamp_val == 0b101 || info.clamp_val == 0 || info.clamp_val == 1 ||
info.clamp_val == 0b100)) {
fmt::print("clamp: 0x{:x}\n", info.clamp_val);
@@ -1874,27 +2022,49 @@ DrawMode process_draw_mode(const AdgifInfo& info, bool use_atest, bool use_decal
return mode;
}
// we need the lev to pool textures with tfrag.
/*!
* Convert TieProtoInfo's to C++ renderer format
*/
void add_vertices_and_static_draw(tfrag3::TieTree& tree,
tfrag3::Level& lev,
const TextureDB& tdb,
const std::vector<TieProtoInfo>& protos) {
// our current approach for static draws is just to flatten to giant mesh.
std::unordered_map<u32, std::vector<u32>> draws_by_tex;
std::unordered_map<u32, u32> interp_hack_colors;
// our current approach for static draws is just to flatten to giant mesh, except for wind stuff.
// this map sorts these two types of draws by texture.
std::unordered_map<u32, std::vector<u32>> static_draws_by_tex;
std::unordered_map<u32, std::vector<u32>> wind_draws_by_tex;
// loop over all prototypes
for (auto& proto : protos) {
// bool using_wind = true; // hack, for testing
bool using_wind = proto.stiffness != 0.f;
// loop over instances of the prototypes
for (auto& inst : proto.instances) {
// if we're using wind, we use the instanced renderer, which requires some extra info
// and we should remember which instance ID we are.
// Note: this is different from the game's instance index - we don't draw everything instanced
// so the non-instanced models don't get a C++ renderer instance ID
u32 wind_instance_idx = tree.instance_info.size();
if (using_wind) {
tfrag3::TieWindInstance wind_instance_info;
wind_instance_info.wind_idx = inst.wind_index; // which wind value to apply in the table
wind_instance_info.stiffness = proto.stiffness; // wind stiffness (how much we move)
wind_instance_info.matrix = inst.mat; // instance transformation matrix.
tree.instance_info.push_back(wind_instance_info);
}
// loop over fragments of the prototype
for (size_t frag_idx = 0; frag_idx < proto.frags.size(); frag_idx++) {
auto& frag = proto.frags[frag_idx];
auto& ifrag = inst.frags.at(frag_idx);
auto& frag = proto.frags[frag_idx]; // shared info for all instances of this frag
auto& ifrag = inst.frags.at(frag_idx); // color info for this instance of the frag
// loop over triangle strips within the fragment
for (auto& strip : frag.strips) {
// what texture are we using?
u32 combo_tex = strip.adgif.combo_tex;
// try looking it up in the existing textures
// try looking it up in the existing textures that we have in the C++ renderer data.
// (this is shared with tfrag)
u32 idx_in_lev_data = UINT32_MAX;
for (u32 i = 0; i < lev.textures.size(); i++) {
if (lev.textures[i].combo_id == combo_tex) {
@@ -1904,10 +2074,10 @@ void add_vertices_and_static_draw(tfrag3::TieTree& tree,
}
if (idx_in_lev_data == UINT32_MAX) {
// didn't find it, have to add a new one
// didn't find it, have to add a new one texture.
auto tex_it = tdb.textures.find(combo_tex);
if (tex_it == tdb.textures.end()) {
bool ok_to_miss = false; // TODO
bool ok_to_miss = false; // for TIE, there's no missing textures.
if (ok_to_miss) {
// we're missing a texture, just use the first one.
tex_it = tdb.textures.begin();
@@ -1922,6 +2092,7 @@ void add_vertices_and_static_draw(tfrag3::TieTree& tree,
assert(false);
}
}
// add a new texture to the level data
idx_in_lev_data = lev.textures.size();
lev.textures.emplace_back();
auto& new_tex = lev.textures.back();
@@ -1937,68 +2108,162 @@ void add_vertices_and_static_draw(tfrag3::TieTree& tree,
DrawMode mode =
process_draw_mode(strip.adgif, frag.prog_info.misc_x == 0, frag.has_magic_tex0_bit);
// okay, we now have a texture and draw mode, let's see if we can add to an existing...
auto existing_draws_in_tex = draws_by_tex.find(idx_in_lev_data);
tfrag3::StripDraw* draw_to_add_to = nullptr;
if (existing_draws_in_tex != draws_by_tex.end()) {
for (auto idx : existing_draws_in_tex->second) {
if (tree.static_draws.at(idx).mode == mode) {
draw_to_add_to = &tree.static_draws[idx];
if (using_wind) {
// okay, we now have a texture and draw mode, let's see if we can add to an existing...
auto existing_draws_in_tex = wind_draws_by_tex.find(idx_in_lev_data);
tfrag3::InstancedStripDraw* draw_to_add_to = nullptr;
if (existing_draws_in_tex != wind_draws_by_tex.end()) {
for (auto idx : existing_draws_in_tex->second) {
if (tree.instanced_wind_draws.at(idx).mode == mode) {
draw_to_add_to = &tree.instanced_wind_draws[idx];
}
}
}
}
if (!draw_to_add_to) {
// nope, need to create a new draw
tree.static_draws.emplace_back();
draws_by_tex[idx_in_lev_data].push_back(tree.static_draws.size() - 1);
draw_to_add_to = &tree.static_draws.back();
draw_to_add_to->mode = mode;
draw_to_add_to->tree_tex_id = idx_in_lev_data;
}
// now we have a draw, time to add vertices
tfrag3::StripDraw::VisGroup vgroup;
vgroup.vis_idx = inst.vis_id; // associate with the tfrag for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
draw_to_add_to->num_triangles += strip.verts.size() - 2;
for (auto& vert : strip.verts) {
tfrag3::PreloadedVertex vtx;
// todo fields
auto tf = transform_tie(inst.mat, vert.pos);
vtx.x = tf.x();
vtx.y = tf.y();
vtx.z = tf.z();
vtx.s = vert.tex.x();
vtx.t = vert.tex.y();
vtx.q = vert.tex.z();
// if this is true, we can remove a divide in the shader
assert(vtx.q == 1.f);
if (vert.color_index_index == UINT32_MAX) {
vtx.color_index = 0;
} else {
vtx.color_index = ifrag.color_indices.at(vert.color_index_index);
assert(vert.color_index_index < ifrag.color_indices.size());
vtx.color_index += ifrag.color_index_offset_in_big_palette;
if (!draw_to_add_to) {
// nope no existing draw for these settings, need to create a new draw
tree.instanced_wind_draws.emplace_back();
wind_draws_by_tex[idx_in_lev_data].push_back(tree.instanced_wind_draws.size() - 1);
draw_to_add_to = &tree.instanced_wind_draws.back();
draw_to_add_to->mode = mode;
draw_to_add_to->tree_tex_id = idx_in_lev_data;
}
size_t vert_idx = tree.vertices.size();
tree.vertices.push_back(vtx);
draw_to_add_to->vertex_index_stream.push_back(vert_idx);
// now we have a draw, time to add vertices. We make a vertex "group" which is a group
// of vertices that the renderer can decide to not draw based on visibility data.
tfrag3::InstancedStripDraw::InstanceGroup igroup;
// needs to be associated with this instance.
igroup.vis_idx = inst.vis_id; // associate with the instance for culling
// number of vertices. The +1 is for the primitive restart index, which tells opengl
// that the triangle strip is done.
igroup.num = strip.verts.size() + 1;
// groups for instances also need the instance idx to grab the appropriate wind/matrix
// data.
igroup.instance_idx = wind_instance_idx;
draw_to_add_to->num_triangles += strip.verts.size() - 2;
// note: this is a bit wasteful to duplicate the xyz/stq.
for (auto& vert : strip.verts) {
tfrag3::PreloadedVertex vtx;
vtx.x = vert.pos.x();
vtx.y = vert.pos.y();
vtx.z = vert.pos.z();
vtx.s = vert.tex.x();
vtx.t = vert.tex.y();
vtx.q = vert.tex.z();
// if this is true, we can remove a divide in the shader
assert(vtx.q == 1.f);
if (vert.color_index_index == UINT32_MAX) {
vtx.color_index = 0;
} else {
vtx.color_index = ifrag.color_indices.at(vert.color_index_index);
assert(vert.color_index_index < ifrag.color_indices.size());
vtx.color_index += ifrag.color_index_offset_in_big_palette;
}
size_t vert_idx = tree.vertices.size();
tree.vertices.push_back(vtx);
draw_to_add_to->vertex_index_stream.push_back(vert_idx);
}
// the primitive restart index
draw_to_add_to->vertex_index_stream.push_back(UINT32_MAX);
draw_to_add_to->instance_groups.push_back(igroup);
} else {
// okay, we now have a texture and draw mode, let's see if we can add to an existing...
auto existing_draws_in_tex = static_draws_by_tex.find(idx_in_lev_data);
tfrag3::StripDraw* draw_to_add_to = nullptr;
if (existing_draws_in_tex != static_draws_by_tex.end()) {
for (auto idx : existing_draws_in_tex->second) {
if (tree.static_draws.at(idx).mode == mode) {
draw_to_add_to = &tree.static_draws[idx];
}
}
}
if (!draw_to_add_to) {
// nope, need to create a new draw
tree.static_draws.emplace_back();
static_draws_by_tex[idx_in_lev_data].push_back(tree.static_draws.size() - 1);
draw_to_add_to = &tree.static_draws.back();
draw_to_add_to->mode = mode;
draw_to_add_to->tree_tex_id = idx_in_lev_data;
}
// now we have a draw, time to add vertices
tfrag3::StripDraw::VisGroup vgroup;
vgroup.vis_idx_in_pc_bvh = inst.vis_id; // associate with the instance for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
draw_to_add_to->num_triangles += strip.verts.size() - 2;
for (auto& vert : strip.verts) {
tfrag3::PreloadedVertex vtx;
// todo fields
auto tf = transform_tie(inst.mat, vert.pos);
vtx.x = tf.x();
vtx.y = tf.y();
vtx.z = tf.z();
vtx.s = vert.tex.x();
vtx.t = vert.tex.y();
vtx.q = vert.tex.z();
// if this is true, we can remove a divide in the shader
assert(vtx.q == 1.f);
if (vert.color_index_index == UINT32_MAX) {
vtx.color_index = 0;
} else {
vtx.color_index = ifrag.color_indices.at(vert.color_index_index);
assert(vert.color_index_index < ifrag.color_indices.size());
vtx.color_index += ifrag.color_index_offset_in_big_palette;
}
size_t vert_idx = tree.vertices.size();
tree.vertices.push_back(vtx);
draw_to_add_to->vertex_index_stream.push_back(vert_idx);
}
draw_to_add_to->vertex_index_stream.push_back(UINT32_MAX);
draw_to_add_to->vis_groups.push_back(vgroup);
}
draw_to_add_to->vertex_index_stream.push_back(UINT32_MAX);
draw_to_add_to->vis_groups.push_back(vgroup);
}
}
}
}
// sort draws by texture. no idea if this really matters, but will reduce the number of
// times the renderer changes textures. it at least makes the rendererdoc debugging easier.
std::stable_sort(tree.static_draws.begin(), tree.static_draws.end(),
[](const tfrag3::StripDraw& a, const tfrag3::StripDraw& b) {
return a.tree_tex_id < b.tree_tex_id;
});
}
/*!
* The groups are created per-fragment, but usually you have a few fragments per instance, so there
* are often consecutive groups that can be merged.
*/
void merge_groups(std::vector<tfrag3::InstancedStripDraw::InstanceGroup>& grps) {
std::vector<tfrag3::InstancedStripDraw::InstanceGroup> result;
result.push_back(grps.at(0));
for (size_t i = 1; i < grps.size(); i++) {
if (grps[i].vis_idx == result.back().vis_idx &&
grps[i].instance_idx == result.back().instance_idx) {
result.back().num += grps[i].num;
} else {
result.push_back(grps[i]);
}
}
std::swap(result, grps);
}
void merge_groups(std::vector<tfrag3::StripDraw::VisGroup>& grps) {
std::vector<tfrag3::StripDraw::VisGroup> result;
result.push_back(grps.at(0));
for (size_t i = 1; i < grps.size(); i++) {
if (grps[i].vis_idx_in_pc_bvh == result.back().vis_idx_in_pc_bvh) {
result.back().num += grps[i].num;
} else {
result.push_back(grps[i]);
}
}
std::swap(result, grps);
}
void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
const std::string& debug_name,
const std::vector<level_tools::TextureRemap>& tex_map,
@@ -2028,7 +2293,9 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
// parent of between 1 and 8 instances.
extract_vis_data(tree, as_instance_array->instances.front().id, this_tree);
// map of instance ID to its parent. We'll need this later.
// we use the index of the instance in the instance list as its index. But this is different
// from its visibility index. This map goes from instance index to the parent node in the vis
// tree. later, we can use this to remap from instance idx to the visiblity node index.
std::unordered_map<int, int> instance_parents;
for (size_t node_idx = 0; node_idx < this_tree.bvh.vis_nodes.size(); node_idx++) {
const auto& node = this_tree.bvh.vis_nodes[node_idx];
@@ -2039,31 +2306,49 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
}
}
// convert level format data to a nicer format
auto info = collect_instance_info(as_instance_array, &tree->prototypes.prototype_array_tie.data);
update_proto_info(&info, tex_map, tex_db, tree->prototypes.prototype_array_tie.data);
// debug_print_info(info);
check_wind_vectors_zero(info, tree->prototypes.wind_vectors);
// determine draws from VU program
emulate_tie_prototype_program(info);
emulate_tie_instance_program(info);
emulate_kicks(info);
// debug save to .obj
if (dump_level) {
auto dir = file_util::get_file_path({fmt::format("debug_out/tie-{}/", debug_name)});
file_util::create_dir_if_needed(dir);
for (auto& proto : info) {
auto data = debug_dump_proto_to_obj(proto);
file_util::write_text_file(fmt::format("{}/{}.obj", dir, proto.name), data);
// file_util::create_dir_if_needed()
}
auto full = dump_full_to_obj(info);
file_util::write_text_file(fmt::format("{}/ALL.obj", dir), full);
}
// create time of day data.
auto full_palette = make_big_palette(info);
// create draws
add_vertices_and_static_draw(this_tree, out, tex_db, info);
// remap vis indices and merge
for (auto& draw : this_tree.static_draws) {
for (auto& str : draw.vis_groups) {
auto it = instance_parents.find(str.vis_idx_in_pc_bvh);
if (it == instance_parents.end()) {
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
}
for (auto& draw : this_tree.instanced_wind_draws) {
for (auto& str : draw.instance_groups) {
auto it = instance_parents.find(str.vis_idx);
if (it == instance_parents.end()) {
str.vis_idx = UINT32_MAX;
@@ -2071,6 +2356,8 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
str.vis_idx = it->second;
}
}
merge_groups(draw.instance_groups);
}
this_tree.colors = full_palette.colors;
+1 -1
View File
@@ -74,7 +74,7 @@ int main(int argc, char** argv) {
db.generate_dgo_listing());
// write out object file map (used for future decompilations, if desired)
file_util::write_text_file(file_util::combine_path(out_folder, "obj.txt"),
db.generate_obj_listing());
db.generate_obj_listing(config.merged_objects));
// dump raw objs
if (config.dump_objs) {
+1 -1
View File
@@ -1,7 +1,7 @@
#include <stdexcept>
#include "common/util/assert.h"
#include "DataParser.h"
#include "third-party/fmt/core.h"
#include "common/util/assert.h"
/*
* Allowable lines:
+1 -1
View File
@@ -1,11 +1,11 @@
#pragma once
#include <string>
#include "common/util/assert.h"
#include <stdexcept>
#include "common/log/log.h"
#include "common/type_system/TypeSpec.h"
#include "common/common_types.h"
#include "decompiler/Disasm/Register.h"
#include "common/util/assert.h"
namespace decompiler {
/*!
+48 -2
View File
@@ -336,8 +336,9 @@ goos::Object decomp_ref_to_inline_array_guess_size(
// verify the stride matches the type system
auto elt_type_info = ts.lookup_type(array_elt_type);
assert(stride == align(elt_type_info->get_size_in_memory(),
elt_type_info->get_inline_array_stride_alignment()));
int ye = align(elt_type_info->get_size_in_memory(),
elt_type_info->get_inline_array_stride_alignment());
assert(stride == ye);
// the input is the location of the data field.
// we expect that to be a label:
@@ -450,6 +451,39 @@ goos::Object sp_field_init_spec_decompile(const std::vector<LinkedWord>& words,
file, TypeSpec("sp-field-init-spec"), 16);
}
goos::Object nav_mesh_vertex_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("nav-vertex"), 16);
}
goos::Object nav_mesh_poly_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("nav-poly"), 8);
}
goos::Object nav_mesh_route_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("vector4ub"), 4);
}
goos::Object sp_launch_grp_launcher_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
@@ -676,6 +710,18 @@ goos::Object decompile_structure(const TypeSpec& type,
field_defs_out.emplace_back(
field.name(), sp_field_init_spec_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file));
} else if (field.name() == "vertex" && type.print() == "nav-mesh") {
field_defs_out.emplace_back(
field.name(), nav_mesh_vertex_arr_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file));
} else if (field.name() == "poly" && type.print() == "nav-mesh") {
field_defs_out.emplace_back(
field.name(), nav_mesh_poly_arr_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file));
} else if (field.name() == "route" && type.print() == "nav-mesh") {
field_defs_out.emplace_back(
field.name(), nav_mesh_route_arr_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file));
} else if (field.name() == "launcher" && type.print() == "sparticle-launch-group") {
field_defs_out.emplace_back(field.name(), sp_launch_grp_launcher_decompile(
obj_words, labels, label.target_segment,
+114 -20
View File
@@ -104,6 +104,7 @@ enum class FieldKind {
FUNCTION,
USERDATA,
ROT_X,
SOUND_SPEC,
INVALID
};
@@ -120,12 +121,12 @@ const SparticleFieldDecomp field_kinds[68] = {
{true, FieldKind::FUNCTION}, // SPT_BIRTH_FUNC = 4
{false}, // SPT_JOINT/REFPOINT = 5
{true, FieldKind::FLOAT_WITH_RAND}, // SPT_NUM = 6
{true, FieldKind::NO_FANCY_DECOMP}, // SPT_SOUND = 7
{true, FieldKind::SOUND_SPEC}, // 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_Z = 12
{true, FieldKind::METER_WITH_RAND}, // SPT_SCALE_X = 13
{true, FieldKind::ROT_X}, // SPT_ROT_X = 14
{true, FieldKind::DEGREES_WITH_RAND}, // SPT_ROT_Y = 15
@@ -386,10 +387,10 @@ goos::Object decompile_sparticle_float_meters_with_rand_init(const std::vector<L
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)))));
return pretty_print::to_symbol(fmt::format("(sp-rnd-int-flt {} (meters {}) {} {})", field_name,
meters_to_string(word_as_float(words.at(1))),
word_as_s32(words.at(2)),
float_to_string(word_as_float(words.at(3)))));
}
assert(flag_name == "float-with-rand");
@@ -397,15 +398,13 @@ goos::Object decompile_sparticle_float_meters_with_rand_init(const std::vector<L
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)));
return pretty_print::to_symbol(fmt::format("(sp-flt {} (meters {}))", field_name,
meters_to_string(word_as_float(words.at(1)))));
} 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)))));
return pretty_print::to_symbol(fmt::format(
"(sp-rnd-flt {} (meters {}) (meters {}) {})", field_name,
meters_to_string(word_as_float(words.at(1))), meters_to_string(word_as_float(words.at(2))),
float_to_string(word_as_float(words.at(3)))));
}
}
@@ -434,6 +433,15 @@ goos::Object decompile_sparticle_float_degrees_with_rand_init(const std::vector<
}
}
goos::Object decompile_sparticle_sound_spec(const std::vector<LinkedWord>& /*words*/,
const std::string& field_name,
const std::string& flag_name,
const goos::Object& original) {
assert(field_name == "spt-sound");
assert(flag_name == "plain-v2");
return pretty_print::build_list("sp-sound", original);
}
goos::Object decompile_sparticle_group_item(const TypeSpec& type,
const DecompilerLabel& label,
const std::vector<DecompilerLabel>& labels,
@@ -466,8 +474,8 @@ goos::Object decompile_sparticle_group_item(const TypeSpec& type,
// 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;
float fade_after = word_as_float(obj_words.at(1));
float falloff_to = word_as_float(obj_words.at(2));
u32 fp = word_as_s32(obj_words.at(3));
u16 flags = fp & 0xffff;
u16 period = fp >> 16;
@@ -480,12 +488,12 @@ goos::Object decompile_sparticle_group_item(const TypeSpec& type,
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 (fade_after != 0.0) {
result += fmt::format(" :fade-after (meters {})", meters_to_string(fade_after));
}
if (falloff_to_meters != 0.0) {
result += fmt::format(" :falloff-to (meters {})", float_to_string(falloff_to_meters));
if (falloff_to != 0.0) {
result += fmt::format(" :falloff-to (meters {})", meters_to_string(falloff_to));
}
if (flags) {
@@ -595,6 +603,7 @@ goos::Object decompile_sparticle_field_init(const TypeSpec& type,
case FieldKind::LAUNCHER_BY_ID:
result = decompile_sparticle_launcher_by_id(obj_words, field_name, flag_name);
break;
case FieldKind::SOUND_SPEC:
case FieldKind::NO_FANCY_DECOMP:
result = normal;
break;
@@ -615,6 +624,91 @@ goos::Object decompile_sparticle_field_init(const TypeSpec& type,
// fmt::print("Result: {}\n\n", result.print());
return result;
}
goos::Object decompile_sparticle_userdata_assert(const std::vector<LinkedWord>& words,
const std::string& field_name,
const std::string& flag_name) {
if (flag_name == "int-with-rand" || flag_name == "float-with-rand") {
return decompile_sparticle_float_with_rand_init(words, field_name, flag_name);
} else {
assert(false);
}
}
goos::Object decompile_sparticle_field_init(const DefpartElement::StaticInfo::PartField& field,
const TypeSystem& ts) {
auto field_id = field.field_id;
auto flags = field.flags;
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(field.data, field_name, flag_name);
} else {
switch (field_info.kind) {
case FieldKind::TEXTURE_ID:
result = decompile_sparticle_tex_field_init(field.data, ts, field_name, flag_name);
break;
case FieldKind::FLOAT_WITH_RAND:
result = decompile_sparticle_float_with_rand_init(field.data, field_name, flag_name);
break;
case FieldKind::METER_WITH_RAND:
result = decompile_sparticle_float_meters_with_rand_init(field.data, field_name, flag_name);
break;
case FieldKind::DEGREES_WITH_RAND:
result =
decompile_sparticle_float_degrees_with_rand_init(field.data, field_name, flag_name);
break;
// case FieldKind::INT_WITH_RAND:
// result = decompile_sparticle_int_with_rand_init(field.data, field_name,
// flag_name); break;
case FieldKind::PLAIN_INT_WITH_RANDS:
result = decompile_sparticle_int_with_rand_init(field.data, field_name, flag_name);
break;
case FieldKind::PLAIN_INT:
result = decompile_sparticle_int_init(field.data, field_name, flag_name);
break;
case FieldKind::CPUINFO_FLAGS:
result = decompile_sparticle_flags(field.data, ts, field_name, flag_name);
break;
case FieldKind::END_FLAG:
result = decompile_sparticle_end(field.data, field_name, flag_name);
break;
case FieldKind::LAUNCHER_BY_ID:
result = decompile_sparticle_launcher_by_id(field.data, field_name, flag_name);
break;
case FieldKind::NO_FANCY_DECOMP:
assert(false);
break;
case FieldKind::FUNCTION:
result = decompile_sparticle_func(field.data, field_name, flag_name);
break;
case FieldKind::USERDATA:
result = decompile_sparticle_userdata_assert(field.data, field_name, flag_name);
break;
case FieldKind::ROT_X:
result = decompile_sparticle_rot_x(field.data, field_name, flag_name);
break;
case FieldKind::SOUND_SPEC:
result =
decompile_sparticle_sound_spec(field.data, field_name, flag_name, field.sound_spec);
break;
default:
assert(false);
}
}
// fmt::print("Result: {}\n\n", result.print());
return result;
}
} // namespace decompiler
/*

Some files were not shown because too many files have changed in this diff Show More