diff --git a/.github/scripts/releases/extract_build_linux.sh b/.github/scripts/releases/extract_build_linux.sh
index ea5a3ce33b..5f95e6531a 100755
--- a/.github/scripts/releases/extract_build_linux.sh
+++ b/.github/scripts/releases/extract_build_linux.sh
@@ -8,17 +8,21 @@ SOURCE=${3}
mkdir -p $DEST
-cp $BIN_SOURCE/game/gk $DEST
-cp $BIN_SOURCE/goalc/goalc $DEST
-cp $BIN_SOURCE/decompiler/extractor $DEST
+PREP_BIN="${PREP_BIN:-true}"
-strip $DEST/gk
-strip $DEST/goalc
-strip $DEST/extractor
+if [ "$PREP_BIN" = "true" ]; then
+ cp $BIN_SOURCE/game/gk $DEST
+ cp $BIN_SOURCE/goalc/goalc $DEST
+ cp $BIN_SOURCE/decompiler/extractor $DEST
-chmod +x $DEST/gk
-chmod +x $DEST/goalc
-chmod +x $DEST/extractor
+ strip $DEST/gk
+ strip $DEST/goalc
+ strip $DEST/extractor
+
+ chmod +x $DEST/gk
+ chmod +x $DEST/goalc
+ chmod +x $DEST/extractor
+fi
mkdir -p $DEST/data
mkdir -p $DEST/data/launcher/
diff --git a/.github/workflows/windows-build-clang.yaml b/.github/workflows/windows-build-clang.yaml
index d755a47e9f..7891494a2e 100644
--- a/.github/workflows/windows-build-clang.yaml
+++ b/.github/workflows/windows-build-clang.yaml
@@ -52,3 +52,10 @@ jobs:
GTEST_OUTPUT: "xml:opengoal-test-report.xml"
run: ./build/bin/goalc-test.exe --gtest_color=yes --gtest_brief=1 --gtest_filter="-*MANUAL_TEST*"
+ - name: Upload artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: opengoal-windows-${{ inputs.cachePrefix }}
+ if-no-files-found: error
+ path: ./build/bin
+
diff --git a/.gitignore b/.gitignore
index 2c014eb495..7bba4e3ac9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,4 @@ texture_replacements/*
svnrev.h
ci-artifacts/
out/build/
+__pycache__/
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 5bfcae9f77..6db58b557a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,9 +8,9 @@
"name": "Python",
"type": "python",
"request": "launch",
- "program": "${workspaceFolder}/scripts/initial_gsrc_creation/replicate_folder_structure.py",
+ "program": "${workspaceFolder}/scripts/gsrc/update-from-decomp.py",
"console": "integratedTerminal",
- "cwd": "${workspaceFolder}/scripts/initial_gsrc_creation"
+ "cwd": "${workspaceFolder}"
},
]
}
diff --git a/README.md b/README.md
index 40318178d4..c4f2b8296c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
-
+
@@ -11,12 +11,13 @@
-- [READ FIRST](#read-first)
- - [How to play the game](#how-to-play-the-game)
+- [Please read first](#please-read-first)
+ - [Quick Start](#quick-start)
- [Project Description](#project-description)
- [Current Status](#current-status)
- [Methodology](#methodology)
- [Setting up a Development Environment](#setting-up-a-development-environment)
+ - [Docker](#docker)
- [Linux](#linux)
- [Ubuntu (20.04)](#ubuntu-2004)
- [Arch](#arch)
@@ -39,21 +40,21 @@
- [`goal_src/`](#goal_src)
- [`game` runtime](#game-runtime)
-## READ FIRST
+## Please read first
-The github repositories are for development of the project and tracking active issues. Most of the information you will find here pertains to setting up the project for development purposes and is not relevant to a general user.
+Our repositories on GitHub are primarily for development of the project and tracking active issues. Most of the information you will find here pertains to setting up the project for development purposes and is not relevant to the end-user.
-There is a discord server where discussion occur https://discord.gg/VZbXMHXzWv
+For questions or additional information pertaining to the project, we have a Discord for discussion here: https://discord.gg/VZbXMHXzWv
-Additionally you can find further information and answers to **frequently asked questions** on the project's main website https://open-goal.github.io/
+Additionally, you can find further documentation and answers to **frequently asked questions** on the project's main website: https://opengoal.dev
-### How to play the game
+**Do not use this decompilation project without providing your own legally purchased copy of the game.**
-To just play the game, follow the steps in this video https://www.youtube.com/watch?v=p8I9NfuZOgE
+### Quick Start
-We don't save any assets from the game - you must use your own legitimately obtained copy of the game.
+If you just want to play the game, you can follow the steps in this video: https://www.youtube.com/watch?v=p8I9NfuZOgE
-> Note that this installation method is a temporary stop gap while we work on something more user-friendly.
+We do not distribute any assets from the game - you must use your own legitimately obtained PS2 copy of the game. We support every retail PAL, NTSC, and NTSC-J build, including Greatest Hits copies.
## Project Description
@@ -93,7 +94,31 @@ Our decompiler is designed specifically for processing the output of the origina
The remainder of this README is catered towards people interested in building the project from source, typically with the intention on contributing as a developer.
-If this does not sound like you and you just want to play the game, refer to the above section [How to play the game](#how-to-play-the-game)
+If this does not sound like you and you just want to play the game, refer to the above section [Quick Start](#quick-start)
+
+### Docker
+
+All three Linux systems are supported using Docker.
+
+Pick your supported prefered flavour of linux and build your chosen image
+
+```
+docker build -f docker/(Arch|Fedora|Ubuntu)/Dockerfile -t jak .
+```
+
+This will create an image with all required dependencies and already built.
+
+```
+docker run -v "$(pwd)"/build:/home/jak/jak-project/build -it jak bash
+```
+
+Note: If you the build directory you'll need to rerun the build command. Alteratively you can get the build via `docker cp`
+
+This will link your build folder to the images so can validate your build or test it on an external device.
+
+Docker images can be linked into your IDE (e.g. CLion) to help with codesniffing, static analysis, run tests and continuous build.
+
+Unfortently you'll still need task runner on your local machine to run the game or instead, manually run the game via the commands found in `Taskfile.yml`
### Linux
@@ -135,7 +160,7 @@ cmake -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_EXE_LINKER_FLAGS="-fuse
Install packages and init repository:
```sh
-sudo pacman -S cmake libpulse base-devel nasm python
+sudo pacman -S cmake libpulse base-devel nasm python libx11 libxrandr libxinerama libxcursor libxi
yay -S go-task
```
@@ -158,7 +183,7 @@ Run tests:
Install packages and init repository:
```sh
-sudo dnf install cmake lld clang nasm libX11-devel libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel pulseaudio-libs-devel
+sudo dnf install cmake python lld clang nasm libX11-devel libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel pulseaudio-libs-devel
sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
```
diff --git a/Taskfile.yml b/Taskfile.yml
index 6196e59ec1..2d0d4b4faf 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -36,12 +36,19 @@ tasks:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" ./iso_data ./decompiler_out --config-override "{\\\"decompile_code\\\": false}"'
boot-game:
- desc: "Boots the game, it will fail if it's not already booted!"
+ desc: "Boots the game, it will fail if it's not already compiled!"
preconditions:
- sh: test -f {{.GK_BIN_RELEASE_DIR}}/gk{{.EXE_FILE_EXTENSION}}
msg: "Couldn't locate runtime executable in '{{.GK_BIN_RELEASE_DIR}}/gk'"
cmds:
- "{{.GK_BIN_RELEASE_DIR}}/gk -boot -fakeiso -debug -v"
+ boot-game-retail:
+ desc: "Boots the game without debug mode, it will fail if it's not already compiled!"
+ preconditions:
+ - sh: test -f {{.GK_BIN_RELEASE_DIR}}/gk{{.EXE_FILE_EXTENSION}}
+ msg: "Couldn't locate runtime executable in '{{.GK_BIN_RELEASE_DIR}}/gk'"
+ cmds:
+ - "{{.GK_BIN_RELEASE_DIR}}/gk -boot -fakeiso -v"
run-game:
desc: "Start the game's runtime, to start the game itself the REPL is required"
preconditions:
@@ -78,17 +85,7 @@ tasks:
cmds:
- rm ./decompiler_out/**/*.asm
- rm ./decompiler_out/**/*disasm.gc
- # TODO - the below are broken, need to be updated or replaced
- # update-gsrc:
- # cmds:
- # - python ./scripts/next-decomp-file.py --files "{{.FILES}}"
- # - task: decomp
- # - task: find-label-types
- # - python ./scripts/update-goal-src.py --files "{{.FILES}}"
- # - task: type-test
- # - task: check-gsrc-file
# TOOLS
- # TODO - broken!
# analyze-ee-memory:
# cmds:
# - '{{.MEMDUMP_BIN_RELEASE_DIR}}/memory_dump_tool "{{.FILE}}" ./ > ee-analysis.log'
@@ -101,16 +98,6 @@ tasks:
offline-tests:
cmds:
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}}'
- # TODO - update or replace
- # add-reference-test:
- # cmds:
- # - task: decomp-file
- # - python ./scripts/add-reference-test.py --file "{{.FILES}}"
- # - task: offline-tests
- # add-reference-test-no-decomp:
- # cmds:
- # - python ./scripts/add-reference-test.py --file "{{.FILES}}"
- # - task: offline-tests
update-ref-tests:
cmds:
- cmd: python ./scripts/tasks/default-file-or-folder.py --path failures
diff --git a/common/type_system/TypeFieldLookup.cpp b/common/type_system/TypeFieldLookup.cpp
index cc013e71ec..5b690d75de 100644
--- a/common/type_system/TypeFieldLookup.cpp
+++ b/common/type_system/TypeFieldLookup.cpp
@@ -482,20 +482,6 @@ FieldReverseLookupOutput TypeSystem::reverse_field_lookup(
// just use the multi-lookup set to 1 and grab the first result.
auto multi_result = reverse_field_multi_lookup(input, 100);
- for (auto& result : multi_result.results) {
- // compute the score.
- result.total_score = 0;
- for (auto& tok : result.tokens) {
- result.total_score += tok.score();
- }
- }
-
- // use stable sort to make sure we break ties by being first in the order.
- std::stable_sort(multi_result.results.begin(), multi_result.results.end(),
- [](const FieldReverseLookupOutput& a, const FieldReverseLookupOutput& b) {
- return a.total_score > b.total_score;
- });
-
/*
if (multi_result.results.size() > 1) {
fmt::print("Multiple:\n");
@@ -532,6 +518,19 @@ FieldReverseMultiLookupOutput TypeSystem::reverse_field_multi_lookup(
try_reverse_lookup(input, *this, nullptr, &result, max_count);
if (!result.results.empty()) {
result.success = true;
+ for (auto& r : result.results) {
+ // compute the score.
+ r.total_score = 0;
+ for (auto& tok : r.tokens) {
+ r.total_score += tok.score();
+ }
+ }
+
+ // use stable sort to make sure we break ties by being first in the order.
+ std::stable_sort(result.results.begin(), result.results.end(),
+ [](const FieldReverseLookupOutput& a, const FieldReverseLookupOutput& b) {
+ return a.total_score > b.total_score;
+ });
}
return result;
}
diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp
index ffc8412baa..0234383710 100644
--- a/common/util/FileUtil.cpp
+++ b/common/util/FileUtil.cpp
@@ -33,8 +33,8 @@
#include
#include
#endif
+#include "common/log/log.h"
#include "common/util/Assert.h"
-#include
namespace file_util {
fs::path get_user_home_dir() {
@@ -66,14 +66,14 @@ fs::path get_user_config_dir() {
return config_base_path / "OpenGOAL";
}
-fs::path get_user_settings_dir() {
- // TODO - jak2
- return get_user_config_dir() / "jak1" / "settings";
+fs::path get_user_settings_dir(GameVersion game_version) {
+ auto game_version_name = game_version_names[game_version];
+ return get_user_config_dir() / game_version_name / "settings";
}
-fs::path get_user_memcard_dir() {
- // TODO - jak2
- return get_user_config_dir() / "jak1" / "saves";
+fs::path get_user_memcard_dir(GameVersion game_version) {
+ auto game_version_name = game_version_names[game_version];
+ return get_user_config_dir() / game_version_name / "saves";
}
struct {
diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h
index a830b4c185..99c7fa6e23 100644
--- a/common/util/FileUtil.h
+++ b/common/util/FileUtil.h
@@ -22,14 +22,15 @@
#include
#include "common/common_types.h"
+#include "common/versions.h"
namespace fs = ghc::filesystem;
namespace file_util {
fs::path get_user_home_dir();
fs::path get_user_config_dir();
-fs::path get_user_settings_dir();
-fs::path get_user_memcard_dir();
+fs::path get_user_settings_dir(GameVersion game_version);
+fs::path get_user_memcard_dir(GameVersion game_version);
fs::path get_jak_project_dir();
bool create_dir_if_needed(const fs::path& path);
diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt
index db9b96959a..0374470012 100644
--- a/decompiler/CMakeLists.txt
+++ b/decompiler/CMakeLists.txt
@@ -69,6 +69,9 @@ add_library(
ObjectFile/ObjectFileDB.cpp
ObjectFile/ObjectFileDB_IR2.cpp
+ types2/ForwardProp.cpp
+ types2/types2.cpp
+
util/config_parsers.cpp
util/data_decompile.cpp
util/DataParser.cpp
diff --git a/decompiler/Function/Warnings.h b/decompiler/Function/Warnings.h
index ad1a7bc4f1..92426b73d1 100644
--- a/decompiler/Function/Warnings.h
+++ b/decompiler/Function/Warnings.h
@@ -104,6 +104,5 @@ class DecompWarnings {
}
std::vector m_warnings;
- bool m_used_lq_sq = false;
};
} // namespace decompiler
diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp
index 72f19106df..d5d4b9a17e 100644
--- a/decompiler/IR2/AtomicOp.cpp
+++ b/decompiler/IR2/AtomicOp.cpp
@@ -142,16 +142,55 @@ SimpleAtom SimpleAtom::make_static_address(int static_label_id) {
return result;
}
+/*!
+ * Mark this atom as a float. It will be printed as a float.
+ * This can only be applied to an "integer" atom.
+ * This should be used carefully, as this doesn't handle casts/types - it just changes the
+ * representation, which will do the wrong thing unless the type system is aware of this
+ * too.
+ */
+void SimpleAtom::mark_as_float() {
+ ASSERT(is_int());
+ m_display_int_as_float = true;
+}
+
+bool SimpleAtom::is_integer_promoted_to_float() const {
+ return m_kind == Kind::INTEGER_CONSTANT && m_display_int_as_float;
+}
+
+float SimpleAtom::get_integer_promoted_to_float() const {
+ ASSERT(is_integer_promoted_to_float());
+ s32 as_s32 = get_int();
+ ASSERT(get_int() == (s64)as_s32);
+ float result;
+ memcpy(&result, &as_s32, 4);
+ return result;
+}
+
goos::Object SimpleAtom::to_form(const std::vector& labels, const Env& env) const {
switch (m_kind) {
case Kind::VARIABLE:
return m_variable.to_form(env);
case Kind::INTEGER_CONSTANT: {
- if (std::abs(m_int) > INT32_MAX) {
- u64 v = m_int;
- return pretty_print::to_symbol(fmt::format("#x{:x}", v));
+ if (m_display_int_as_float) {
+ float f;
+ s32 as_s32 = m_int;
+ ASSERT(((s64)as_s32) == m_int); // float should always be a sign extended 32-bit value.
+ memcpy(&f, &as_s32, 4);
+ if (f == f) {
+ return goos::Object::make_float(f);
+ } else {
+ // nan or weird
+ ASSERT(false); // let's abort on this for now, can remove if it actually comes up.
+ return pretty_print::to_symbol(fmt::format("(the-as float #x{:x})", m_int));
+ }
} else {
- return goos::Object::make_integer(m_int);
+ if (std::abs(m_int) > INT32_MAX) {
+ u64 v = m_int;
+ return pretty_print::to_symbol(fmt::format("#x{:x}", v));
+ } else {
+ return goos::Object::make_integer(m_int);
+ }
}
}
@@ -311,6 +350,8 @@ std::string get_simple_expression_op_name(SimpleExpression::Kind kind) {
return "vec3dot";
case SimpleExpression::Kind::VECTOR_4_DOT:
return "vec4dot";
+ case SimpleExpression::Kind::VECTOR_LENGTH:
+ return "veclength";
case SimpleExpression::Kind::SET_ON_LESS_THAN:
case SimpleExpression::Kind::SET_ON_LESS_THAN_IMM:
return "set-on-less-than";
@@ -378,6 +419,8 @@ int get_simple_expression_arg_count(SimpleExpression::Kind kind) {
case SimpleExpression::Kind::SET_ON_LESS_THAN:
case SimpleExpression::Kind::SET_ON_LESS_THAN_IMM:
return 2;
+ case SimpleExpression::Kind::VECTOR_LENGTH:
+ return 1;
default:
ASSERT(false);
return -1;
diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h
index 7b84193768..1cecc2e497 100644
--- a/decompiler/IR2/AtomicOp.h
+++ b/decompiler/IR2/AtomicOp.h
@@ -19,6 +19,11 @@ class FormElement;
class ConditionElement;
class FormPool;
class DecompilerTypeSystem;
+namespace types2 {
+struct Instruction;
+struct TypeState;
+struct TypePropExtras;
+} // namespace types2
/*!
* An atomic operation represents a single operation from the point of view of the IR2 system.
@@ -78,6 +83,12 @@ class AtomicOp {
TypeState propagate_types(const TypeState& input, const Env& env, DecompilerTypeSystem& dts);
+ virtual void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) = 0;
+
int op_id() const { return m_my_idx; }
const std::vector& read_regs() const { return m_read_regs; }
const std::vector& write_regs() const { return m_write_regs; }
@@ -176,12 +187,16 @@ class SimpleAtom {
ASSERT(is_sym_ptr() || is_sym_val());
return m_string;
}
+ void mark_as_float();
+ bool is_integer_promoted_to_float() const;
+ float get_integer_promoted_to_float() const;
private:
Kind m_kind = Kind::INVALID;
std::string m_string; // for symbol ptr and symbol val
s64 m_int = -1; // for integer constant and static address label id
RegisterAccess m_variable;
+ bool m_display_int_as_float = false;
};
/*!
@@ -240,6 +255,7 @@ class SimpleExpression {
SUBU_L32_S7, // use SUBU X, src0, s7 to check if lower 32-bits are s7.
VECTOR_3_DOT,
VECTOR_4_DOT,
+ VECTOR_LENGTH, // jak 2 only.
SET_ON_LESS_THAN,
SET_ON_LESS_THAN_IMM
};
@@ -250,6 +266,11 @@ class SimpleExpression {
ASSERT(idx < args());
return m_args[idx];
}
+
+ SimpleAtom& get_arg(int idx) {
+ ASSERT(idx < args());
+ return m_args[idx];
+ }
Kind kind() const { return m_kind; }
SimpleExpression() = default;
SimpleExpression(Kind kind, const SimpleAtom& arg0);
@@ -304,6 +325,11 @@ class SetVarOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
const RegisterAccess& dst() const { return m_dst; }
const SimpleExpression& src() const { return m_src; }
@@ -334,6 +360,11 @@ class AsmOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
const Instruction& instruction() const { return m_instr; }
const std::optional dst() const { return m_dst; }
@@ -437,6 +468,11 @@ class SetVarConditionOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
private:
@@ -463,6 +499,11 @@ class StoreOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
const SimpleExpression& addr() const { return m_addr; }
const SimpleAtom& value() const { return m_value; }
@@ -492,9 +533,13 @@ class LoadVarOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
TP_Type get_src_type(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) const;
void collect_vars(RegAccessSet& vars) const override;
-
const SimpleExpression& src() const { return m_src; }
Kind kind() const { return m_kind; }
int size() const { return m_size; }
@@ -580,6 +625,11 @@ class BranchOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
const IR2_BranchDelay& branch_delay() const { return m_branch_delay; }
const IR2_Condition& condition() const { return m_condition; }
@@ -616,6 +666,11 @@ class AsmBranchOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
bool is_likely() const { return m_likely; }
const IR2_Condition& condition() const { return m_condition; }
@@ -653,6 +708,11 @@ class SpecialOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
Kind kind() const { return m_kind; }
@@ -676,6 +736,11 @@ class CallOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
const std::vector& arg_vars() const { return m_arg_vars; }
RegisterAccess function_var() const { return m_function_var; }
@@ -717,6 +782,11 @@ class ConditionalMoveFalseOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
private:
@@ -748,6 +818,11 @@ class FunctionEndOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
void mark_function_as_no_return_value();
const RegisterAccess& return_var() const {
@@ -775,7 +850,13 @@ class StackSpillStoreOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
+ int offset() const { return m_offset; }
private:
SimpleAtom m_value;
@@ -798,7 +879,13 @@ class StackSpillLoadOp : public AtomicOp {
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
+ void propagate_types2(types2::Instruction& instr,
+ const Env& env,
+ types2::TypeState& input_types,
+ DecompilerTypeSystem& dts,
+ types2::TypePropExtras& extras) override;
void collect_vars(RegAccessSet& vars) const override;
+ int offset() const { return m_offset; }
private:
RegisterAccess m_dst;
diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp
index 327e8ada80..c843bfbdee 100644
--- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp
+++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp
@@ -243,6 +243,7 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
return TP_Type::make_from_ts("int");
case Kind::VECTOR_3_DOT:
case Kind::VECTOR_4_DOT:
+ case Kind::VECTOR_LENGTH:
return TP_Type::make_from_ts("float");
default:
throw std::runtime_error("Simple expression cannot get_type: " +
diff --git a/decompiler/IR2/Env.cpp b/decompiler/IR2/Env.cpp
index b520dbc952..011fccd69e 100644
--- a/decompiler/IR2/Env.cpp
+++ b/decompiler/IR2/Env.cpp
@@ -507,69 +507,73 @@ void Env::disable_use(const RegisterAccess& access) {
*/
void Env::set_stack_structure_hints(const std::vector& hints) {
for (auto& hint : hints) {
- StackStructureEntry entry;
- entry.hint = hint;
-
- switch (hint.container_type) {
- case StackStructureHint::ContainerType::NONE: {
- // parse the type spec.
- TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
- auto type_info = dts->ts.lookup_type(base_typespec);
- // just a plain object on the stack.
- if (!type_info->is_reference()) {
- throw std::runtime_error(
- fmt::format("Stack variable type {} is not a reference and cannot be stored directly "
- "on the stack. Use an array instead.",
- base_typespec.print()));
- }
- entry.ref_type = base_typespec;
- entry.size = type_info->get_size_in_memory();
- // sanity check the alignment
- if (align(entry.hint.stack_offset, type_info->get_in_memory_alignment()) !=
- entry.hint.stack_offset) {
- lg::error("Misaligned stack variable of type {} offset {} required align {}\n",
- entry.ref_type.print(), entry.hint.stack_offset,
- type_info->get_in_memory_alignment());
- }
- } break;
-
- case StackStructureHint::ContainerType::INLINE_ARRAY: {
- TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
- auto type_info = dts->ts.lookup_type(base_typespec);
- if (!type_info->is_reference()) {
- throw std::runtime_error(
- fmt::format("Stack inline-array element type {} is not a reference and cannot be "
- "stored in an inline-array. Use an array instead.",
- base_typespec.print()));
- }
-
- entry.ref_type = TypeSpec("inline-array", {TypeSpec(base_typespec)});
- entry.size = 1; // we assume that there is no constant propagation into this array and
- // make this only trigger in get_stack_type if we hit exactly.
- // sanity check the alignment
- if (align(entry.hint.stack_offset, type_info->get_in_memory_alignment()) !=
- entry.hint.stack_offset) {
- lg::error("Misaligned stack variable of type {} offset {} required align {}\n",
- entry.ref_type.print(), entry.hint.stack_offset,
- type_info->get_in_memory_alignment());
- }
- } break;
-
- case StackStructureHint::ContainerType::ARRAY: {
- TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
- entry.ref_type = TypeSpec("pointer", {TypeSpec(base_typespec)});
- entry.size = 1; // we assume that there is no constant propagation into this array and
- // make this only trigger in get_stack_type if we hit exactly.
- break;
- }
- default:
- ASSERT(false);
- }
-
- m_stack_structures.push_back(entry);
+ add_stack_structure_hint(hint);
}
}
+void Env::add_stack_structure_hint(const StackStructureHint& hint) {
+ StackStructureEntry entry;
+ entry.hint = hint;
+
+ switch (hint.container_type) {
+ case StackStructureHint::ContainerType::NONE: {
+ // parse the type spec.
+ TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
+ auto type_info = dts->ts.lookup_type(base_typespec);
+ // just a plain object on the stack.
+ if (!type_info->is_reference()) {
+ throw std::runtime_error(
+ fmt::format("Stack variable type {} is not a reference and cannot be stored directly "
+ "on the stack. Use an array instead.",
+ base_typespec.print()));
+ }
+ entry.ref_type = base_typespec;
+ entry.size = type_info->get_size_in_memory();
+ // sanity check the alignment
+ if (align(entry.hint.stack_offset, type_info->get_in_memory_alignment()) !=
+ entry.hint.stack_offset) {
+ lg::error("Misaligned stack variable of type {} offset {} required align {}\n",
+ entry.ref_type.print(), entry.hint.stack_offset,
+ type_info->get_in_memory_alignment());
+ }
+ } break;
+
+ case StackStructureHint::ContainerType::INLINE_ARRAY: {
+ TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
+ auto type_info = dts->ts.lookup_type(base_typespec);
+ if (!type_info->is_reference()) {
+ throw std::runtime_error(
+ fmt::format("Stack inline-array element type {} is not a reference and cannot be "
+ "stored in an inline-array. Use an array instead.",
+ base_typespec.print()));
+ }
+
+ entry.ref_type = TypeSpec("inline-array", {TypeSpec(base_typespec)});
+ entry.size = 1; // we assume that there is no constant propagation into this array and
+ // make this only trigger in get_stack_type if we hit exactly.
+ // sanity check the alignment
+ if (align(entry.hint.stack_offset, type_info->get_in_memory_alignment()) !=
+ entry.hint.stack_offset) {
+ lg::error("Misaligned stack variable of type {} offset {} required align {}\n",
+ entry.ref_type.print(), entry.hint.stack_offset,
+ type_info->get_in_memory_alignment());
+ }
+ } break;
+
+ case StackStructureHint::ContainerType::ARRAY: {
+ TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
+ entry.ref_type = TypeSpec("pointer", {TypeSpec(base_typespec)});
+ entry.size = 1; // we assume that there is no constant propagation into this array and
+ // make this only trigger in get_stack_type if we hit exactly.
+ break;
+ }
+ default:
+ ASSERT(false);
+ }
+
+ m_stack_structures.push_back(entry);
+}
+
std::optional Env::get_art_elt_name(int idx) const {
ASSERT(dts);
auto it = dts->art_group_info.find(art_group());
diff --git a/decompiler/IR2/Env.h b/decompiler/IR2/Env.h
index c577d44d06..6c5a6ef894 100644
--- a/decompiler/IR2/Env.h
+++ b/decompiler/IR2/Env.h
@@ -174,6 +174,7 @@ class Env {
}
void set_stack_structure_hints(const std::vector& hints);
+ void add_stack_structure_hint(const StackStructureHint& hint);
const std::vector& stack_structure_hints() const {
return m_stack_structures;
}
diff --git a/decompiler/IR2/ExpressionHelpers.cpp b/decompiler/IR2/ExpressionHelpers.cpp
index abbee45fca..b231cf114c 100644
--- a/decompiler/IR2/ExpressionHelpers.cpp
+++ b/decompiler/IR2/ExpressionHelpers.cpp
@@ -9,6 +9,20 @@
namespace decompiler {
+// needed for jak 2.
+std::optional try_get_const_float(const Form* form) {
+ auto* as_cfe = form->try_as_element();
+ if (as_cfe) {
+ return as_cfe->value();
+ }
+
+ auto atom = form_as_atom(form);
+ if (atom && atom->is_integer_promoted_to_float()) {
+ return atom->get_integer_promoted_to_float();
+ }
+ return {};
+}
+
FormElement* handle_get_property_value_float(const std::vector