Merge remote-tracking branch 'upstream/master'

This commit is contained in:
ManDude
2021-01-09 04:53:31 +00:00
112 changed files with 3334 additions and 695 deletions
+22 -7
View File
@@ -18,9 +18,10 @@ jobs:
matrix:
os: [ubuntu-20.04]
config: [Debug] # TODO - Eventually we need to make a Release Config
compiler: [clang, gcc]
experimental: [false]
name: ${{ matrix.config }}
name: ${{ matrix.config }}-${{ matrix.compiler }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
# Set some sort of timeout in the event of run-away builds. We are limited on concurrent jobs so, get rid of them.
@@ -44,7 +45,6 @@ jobs:
key: submodules-${{ hashFiles('./.gitmodules') }}
path: |
./third-party/googletest
./third-party/spdlog
./third-party/zydis
./.git/modules/
@@ -53,6 +53,7 @@ jobs:
run: git submodule update --init --recursive --jobs 2
- name: Prepare Artifact Git Info
id: git-vars
shell: bash
run: |
echo "##[set-output name=branch;]${GITHUB_REF#refs/heads/}"
@@ -66,10 +67,9 @@ jobs:
echo "##[set-output name=short-sha;]$(git rev-parse --short "$GITHUB_SHA")"
fi
echo "##[set-output name=artifact-metadata;]${ARTIFACT_NAME}"
id: git-vars
- name: Get Package Dependencies
run: sudo apt install build-essential cmake ccache gcc g++ lcov make nasm
run: sudo apt install build-essential cmake ccache clang gcc g++ lcov make nasm
# # -- SETUP CCACHE - https://cristianadam.eu/20200113/speeding-up-c-plus-plus-github-actions-using-ccache/
# - name: Prepare ccache timestamp
@@ -87,9 +87,18 @@ jobs:
# restore-keys: |
# ${{ matrix.config }}-ccache-
- name: CMake Generation
- name: CMake Generation
# run: cmake -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -B build -DCODE_COVERAGE=ON
run: cmake -B build -DCODE_COVERAGE=ON
run: |
if [ "${{ matrix.compiler }}" == 'clang' ]; then
export CC=clang
export CXX=clang++
cmake -B build -DCODE_COVERAGE=ON -DASAN_BUILD=ON
else
export CC=gcc
export CXX=g++
cmake -B build -DCODE_COVERAGE=ON
fi
- name: Build Project
working-directory: ./build
@@ -106,9 +115,15 @@ jobs:
run: make -j4
- name: Run Tests
run: ./test_code_coverage.sh
run: |
if [ "${{ matrix.compiler }}" == 'clang' ]; then
./test.sh
else
./test_code_coverage.sh
fi
- name: Coveralls
if: ${{ matrix.compiler }} != 'clang'
uses: coverallsapp/github-action@master
continue-on-error: true # Sometimes Coveralls has intermittent problems, and codecoverage isn't critical to our success
with:
+2 -3
View File
@@ -17,7 +17,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-2019]
config: [Debug] # TODO - Eventually we need to make a Release Config
config: [Release]
experimental: [false]
name: ${{ matrix.config }}
@@ -44,7 +44,6 @@ jobs:
key: submodules-${{ hashFiles('./.gitmodules') }}
path: |
./third-party/googletest
./third-party/spdlog
./third-party/zydis
./.git/modules/
@@ -76,7 +75,7 @@ jobs:
run: |
call "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat"
cmake --version
cmake -B build -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" .
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles" .
- name: Build Project
working-directory: ./build
+1
View File
@@ -4,3 +4,4 @@ cmake-build-debug/*
build/*
decompiler_out/*
logs/*
log/*
-3
View File
@@ -1,9 +1,6 @@
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest.git
[submodule "third-party/spdlog"]
path = third-party/spdlog
url = https://github.com/gabime/spdlog.git
[submodule "third-party/zydis"]
path = third-party/zydis
url = https://github.com/zyantific/zydis.git
+3 -26
View File
@@ -44,7 +44,7 @@ IF (WIN32)
ENDIF ()
IF (ASAN_BUILD)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1")
message(STATUS "Doing ASAN build")
ENDIF ()
@@ -64,28 +64,8 @@ include_directories(./)
include_directories(SYSTEM third-party/inja)
# build spdlog as a shared library to improve compile times
# adding this as a SYSTEM include suppresses all the terrible warnings in spdlog
include_directories(SYSTEM third-party/spdlog/include)
# this makes spdlog generate a shared library that we can link against
set(SPDLOG_BUILD_SHARED ON CACHE BOOL "a" FORCE)
# this makes the spdlog includes not use the header only version, making compiling faster
add_definitions(-DSPDLOG_COMPILED_LIB)
# build goos
add_subdirectory(common/goos)
# build type_system library for compiler/decompiler
add_subdirectory(common/type_system)
# build common_util library
add_subdirectory(common/util)
# build cross platform socket library
add_subdirectory(common/cross_sockets)
# build cross platform debug library
add_subdirectory(common/cross_os_debug)
# build common library
add_subdirectory(common)
# build decompiler
add_subdirectory(decompiler)
@@ -108,9 +88,6 @@ add_subdirectory(third-party/minilzo)
# build format library
add_subdirectory(third-party/fmt)
# build spdlog library
add_subdirectory(third-party/spdlog)
# build zydis third party library for disassembling x86
option(ZYDIS_BUILD_TOOLS "" OFF)
option(ZYDIS_BUILD_EXAMPLES "" OFF)
+40 -38
View File
@@ -1,40 +1,42 @@
{
"configurations": [
{
"name": "Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"variables": [
{
"name": "INSTALL_GTEST",
"value": "True",
"type": "BOOL"
}
]
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"variables": [
{
"name": "INSTALL_GTEST",
"value": "True",
"type": "BOOL"
}
]
}
]
"configurations": [
{
"name": "Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"addressSanitizerEnabled": true,
"ctestCommandArgs": "",
"variables": [
{
"name": "INSTALL_GTEST",
"value": "True",
"type": "BOOL"
}
]
},
{
"name": "Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"addressSanitizerEnabled": true,
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": [
{
"name": "INSTALL_GTEST",
"value": "True",
"type": "BOOL"
}
]
}
]
}
+5 -2
View File
@@ -3,16 +3,20 @@
![Linux](https://github.com/water111/jak-project/workflows/Linux/badge.svg)
![Windows](https://github.com/water111/jak-project/workflows/Windows/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/water111/jak-project/badge.svg?branch=master)](https://coveralls.io/github/water111/jak-project?branch=master)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7c3cdc07523f43aca3433484ebc62ff9)](https://www.codacy.com/gh/water111/jak-project/dashboard?utm_source=github.com&utm_medium=referral&utm_content=xTVaser/jak-project&utm_campaign=Badge_Grade)
## Table of Contents
<!-- toc -->
- [Project Description](#project-description)
- [Table of Contents](#table-of-contents)
- [Project Description](#project-description)
- [Getting Started - Linux (Ubuntu)](#getting-started---linux-ubuntu)
- [Getting Started - Windows](#getting-started---windows)
- [Project Layout](#project-layout)
- [Directory Layout](#directory-layout)
- [More Documentation](#more-documentation)
- [ASan Build](#asan-build)
<!-- tocstop -->
## Project Description
@@ -161,7 +165,6 @@ The final component is the "runtime", located in `game`. This is the part of the
- `mman`: Windows library used to emulate `mmap` on Linux
- `run-clang-format`: Utility to check and enforce code formatting
- `run-clang-tidy`
- `spdlog`: Logging library
- `zydis`: x86-64 disassembler used in the OpenGOAL debugger
- `json`: A JSON library
- `linenoise`: Used for the REPL input. Support history and useful editing shortcuts.
+28
View File
@@ -0,0 +1,28 @@
add_library(common
SHARED
cross_os_debug/xdbg.cpp
cross_sockets/xsocket.cpp
goos/Interpreter.cpp
goos/Object.cpp
goos/ParseHelpers.cpp
goos/PrettyPrinter.cpp
goos/Reader.cpp
goos/TextDB.cpp
log/log.cpp
type_system/deftype.cpp
type_system/Type.cpp
type_system/TypeFieldLookup.cpp
type_system/TypeSpec.cpp
type_system/TypeSystem.cpp
util/DgoWriter.cpp
util/FileUtil.cpp
util/Timer.cpp
)
target_link_libraries(common fmt)
IF(WIN32)
target_link_libraries(common wsock32 ws2_32)
ELSE()
target_link_libraries(common stdc++fs)
ENDIF()
-4
View File
@@ -1,4 +0,0 @@
add_library(cross_os_debug SHARED
xdbg.cpp)
target_link_libraries(cross_os_debug fmt)
+21 -18
View File
@@ -92,32 +92,35 @@ bool attach_and_break(const ThreadID& tid) {
*/
bool check_stopped(const ThreadID& tid, SignalInfo* out) {
int status;
if (waitpid(tid.id, &status, WNOHANG) < 0) {
int rv = waitpid(tid.id, &status, WNOHANG);
if (rv < 0) {
printf("[Debugger] Failed to waitpid: %s.\n", strerror(errno));
// assert(false); // todo, temp because I think we should never hit this.
return false;
}
if (WIFSTOPPED(status)) {
auto sig = WSTOPSIG(status);
if (out) {
switch (sig) {
case SIGSEGV:
out->kind = SignalInfo::SEGFAULT;
break;
case SIGFPE:
out->kind = SignalInfo::MATH_EXCEPTION;
break;
case SIGTRAP:
out->kind = SignalInfo::BREAK;
break;
if (rv > 0) {
// status has actually changed
if (WIFSTOPPED(status)) {
auto sig = WSTOPSIG(status);
if (out) {
switch (sig) {
case SIGSEGV:
out->kind = SignalInfo::SEGFAULT;
break;
case SIGFPE:
out->kind = SignalInfo::MATH_EXCEPTION;
break;
case SIGTRAP:
out->kind = SignalInfo::BREAK;
break;
default:
out->kind = SignalInfo::UNKNOWN;
default:
out->kind = SignalInfo::UNKNOWN;
}
}
return true;
}
return true;
}
return false;
-12
View File
@@ -1,12 +0,0 @@
add_library(cross_sockets
SHARED
"xsocket.h"
"xsocket.cpp")
IF (WIN32)
# set stuff for windows
target_link_libraries(cross_sockets wsock32 ws2_32)
ELSE()
# set stuff for other systems
target_link_libraries(cross_sockets)
ENDIF()
-9
View File
@@ -1,9 +0,0 @@
IF (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2")
ELSE()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
ENDIF()
add_library(goos SHARED Object.cpp ParseHelpers.cpp TextDB.cpp Reader.cpp Interpreter.cpp PrettyPrinter.cpp ParseHelpers.cpp ParseHelpers.h)
target_link_libraries(goos common_util fmt)
+114
View File
@@ -0,0 +1,114 @@
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <mutex>
#include "third-party/fmt/color.h"
#include "log.h"
namespace lg {
struct Logger {
Logger() = default;
bool initialized = false;
FILE* fp = nullptr;
level stdout_log_level = level::trace;
level file_log_level = level::trace;
level flush_level = level::trace;
std::mutex mutex;
~Logger() {
// will run when program exits.
if (fp) {
fclose(fp);
}
}
};
Logger gLogger;
namespace internal {
const char* log_level_names[] = {"trace", "debug", "info", "warn", "error", "die"};
const fmt::color log_colors[] = {fmt::color::gray, fmt::color::turquoise, fmt::color::light_green,
fmt::color::yellow, fmt::color::red, fmt::color::hot_pink};
void log_message(level log_level, LogTime& now, const char* message) {
#ifdef __linux__
char date_time_buffer[128];
time_t now_seconds = now.tv.tv_sec;
auto now_milliseconds = now.tv.tv_usec / 1000;
strftime(date_time_buffer, 128, "%Y-%m-%d %H:%M:%S", localtime(&now_seconds));
std::string date_string = fmt::format("[{}:{:03d}]", date_time_buffer, now_milliseconds);
#else
char date_time_buffer[128];
strftime(date_time_buffer, 128, "%Y-%m-%d %H:%M:%S", localtime(&now.tim));
std::string date_string = fmt::format("[{}]", date_time_buffer);
#endif
{
std::lock_guard<std::mutex> lock(gLogger.mutex);
if (gLogger.fp && log_level >= gLogger.file_log_level) {
// log to file
std::string file_string =
fmt::format("{} [{}] {}\n", date_string, log_level_names[int(log_level)], message);
fwrite(file_string.c_str(), file_string.length(), 1, gLogger.fp);
if (log_level >= gLogger.flush_level) {
fflush(gLogger.fp);
}
}
if (log_level >= gLogger.stdout_log_level) {
fmt::print("{} [", date_string);
fmt::print(fg(log_colors[int(log_level)]), "{}", log_level_names[int(log_level)]);
fmt::print("] {}\n", message);
if (log_level >= gLogger.flush_level) {
fflush(stdout);
}
}
}
if (log_level == level::die) {
exit(-1);
}
}
} // namespace internal
void set_file(const std::string& filename) {
assert(!gLogger.fp);
gLogger.fp = fopen(filename.c_str(), "w");
assert(gLogger.fp);
}
void set_flush_level(level log_level) {
gLogger.flush_level = log_level;
}
void set_file_level(level log_level) {
gLogger.file_log_level = log_level;
}
void set_stdout_level(level log_level) {
gLogger.stdout_log_level = log_level;
}
void set_max_debug_levels() {
gLogger.flush_level = level::trace;
gLogger.stdout_log_level = level::trace;
gLogger.file_log_level = level::trace;
}
void initialize() {
assert(!gLogger.initialized);
gLogger.initialized = true;
}
void finish() {
{
std::lock_guard<std::mutex> lock(gLogger.mutex);
if (gLogger.fp) {
fclose(gLogger.fp);
gLogger.fp = nullptr;
}
}
}
} // namespace lg
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#include <ctime>
#ifdef __linux__
#include <sys/time.h>
#endif
#include <string>
#include "third-party/fmt/core.h"
namespace lg {
#ifdef __linux__
struct LogTime {
timeval tv;
};
#else
struct LogTime {
time_t tim;
};
#endif
// Logging API
enum class level { trace = 0, debug = 1, info = 2, warn = 3, error = 4, die = 5 };
namespace internal {
// log implementation stuff, not to be called by the user
void log_message(level log_level, LogTime& now, const char* message);
} // namespace internal
void set_file(const std::string& filename);
void set_flush_level(level log_level);
void set_file_level(level log_level);
void set_stdout_level(level log_level);
void set_max_debug_levels();
void initialize();
void finish();
template <typename... Args>
void log(level log_level, const std::string& format, Args&&... args) {
LogTime now;
#ifdef __linux__
gettimeofday(&now.tv, nullptr);
#else
now.tim = time(nullptr);
#endif
std::string formatted_message = fmt::format(format, std::forward<Args>(args)...);
internal::log_message(log_level, now, formatted_message.c_str());
}
template <typename... Args>
void trace(const std::string& format, Args&&... args) {
log(level::trace, format, std::forward<Args>(args)...);
}
template <typename... Args>
void debug(const std::string& format, Args&&... args) {
log(level::debug, format, std::forward<Args>(args)...);
}
template <typename... Args>
void info(const std::string& format, Args&&... args) {
log(level::info, format, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(const std::string& format, Args&&... args) {
log(level::warn, format, std::forward<Args>(args)...);
}
template <typename... Args>
void error(const std::string& format, Args&&... args) {
log(level::error, format, std::forward<Args>(args)...);
}
template <typename... Args>
void die(const std::string& format, Args&&... args) {
log(level::die, format, std::forward<Args>(args)...);
}
} // namespace lg
-9
View File
@@ -1,9 +0,0 @@
add_library(type_system
SHARED
TypeSystem.cpp
Type.cpp
TypeSpec.cpp
deftype.cpp
TypeFieldLookup.cpp)
target_link_libraries(type_system fmt goos)
-9
View File
@@ -1,9 +0,0 @@
add_library(common_util
SHARED
FileUtil.cpp
DgoWriter.cpp
Timer.cpp)
IF(UNIX)
target_link_libraries(common_util stdc++fs)
ENDIF()
+51 -30
View File
@@ -1,38 +1,59 @@
add_executable(decompiler
main.cpp
ObjectFile/ObjectFileDB.cpp
Disasm/Instruction.cpp
Disasm/InstructionDecode.cpp
Disasm/OpcodeInfo.cpp
Disasm/Register.cpp
ObjectFile/LinkedObjectFileCreation.cpp
ObjectFile/LinkedObjectFile.cpp
Function/Function.cpp
config.cpp
util/DecompilerTypeSystem.cpp
Function/BasicBlocks.cpp
Disasm/InstructionMatching.cpp
Function/CfgVtx.cpp
IR/BasicOpBuilder.cpp
IR/CfgBuilder.cpp
IR/IR.cpp
Function/TypeInspector.cpp
data/tpage.cpp
add_library(
decomp
SHARED
data/game_count.cpp
data/game_text.cpp
data/StrFileReader.cpp
data/game_count.cpp
Function/TypeAnalysis.cpp
IR/IR_TypeAnalysis.cpp
util/TP_Type.cpp
Function/RegUsage.cpp
data/tpage.cpp
Disasm/Instruction.cpp
Disasm/InstructionDecode.cpp
Disasm/InstructionMatching.cpp
Disasm/InstructionParser.cpp
Disasm/OpcodeInfo.cpp
Disasm/Register.cpp
Function/BasicBlocks.cpp
Function/CfgVtx.cpp
Function/ExpressionBuilder.cpp
Function/ExpressionStack.cpp
IR/IR_ExpressionStack.cpp)
Function/Function.cpp
Function/RegUsage.cpp
Function/TypeAnalysis.cpp
Function/TypeInspector.cpp
IR/BasicOpBuilder.cpp
IR/CfgBuilder.cpp
IR/IR.cpp
IR/IR_ExpressionStack.cpp
IR/IR_TypeAnalysis.cpp
IR2/AtomicOp.cpp
IR2/AtomicOpBuilder.cpp
IR2/Env.cpp
ObjectFile/LinkedObjectFile.cpp
ObjectFile/LinkedObjectFileCreation.cpp
ObjectFile/ObjectFileDB.cpp
util/DecompilerTypeSystem.cpp
util/TP_Type.cpp
config.cpp
)
target_link_libraries(decomp
minilzo
common
fmt
)
add_executable(decompiler
main.cpp
)
target_link_libraries(decompiler
goos
decomp
common
minilzo
common_util
type_system
spdlog
fmt)
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <string>
namespace decompiler {
/*!
* A label to a location in an object file.
* Doesn't have to be word aligned.
*/
struct DecompilerLabel {
std::string name;
int target_segment;
int offset; // in bytes
};
} // namespace decompiler
+53 -11
View File
@@ -8,17 +8,18 @@
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include <cassert>
namespace decompiler {
/*!
* Convert atom to a string for disassembly.
*/
std::string InstructionAtom::to_string(const LinkedObjectFile& file) const {
std::string InstructionAtom::to_string(const std::vector<DecompilerLabel>& labels) const {
switch (kind) {
case REGISTER:
return reg.to_string();
case IMM:
return std::to_string(imm);
case LABEL:
return file.get_label_name(label_id);
return labels.at(label_id).name;
case VU_ACC:
return "acc";
case VU_Q:
@@ -115,6 +116,25 @@ bool InstructionAtom::is_link_or_label() const {
return kind == IMM_SYM || kind == LABEL;
}
bool InstructionAtom::operator==(const InstructionAtom& other) const {
if (kind != other.kind) {
return false;
}
switch (kind) {
case REGISTER:
return reg == other.reg;
case IMM:
return imm == other.imm;
case LABEL:
return label_id == other.label_id;
case VU_ACC:
case VU_Q:
return true;
default:
assert(false);
}
}
/*!
* Convert just the name of the opcode to a string, omitting src/dst, but including
* suffixes (interlock, broadcasts and destination)
@@ -169,7 +189,7 @@ std::string Instruction::op_name_to_string() const {
/*!
* Convert entire instruction to a string.
*/
std::string Instruction::to_string(const LinkedObjectFile& file) const {
std::string Instruction::to_string(const std::vector<DecompilerLabel>& labels) const {
auto& info = gOpcodeInfo[(int)kind];
auto result = op_name_to_string();
@@ -178,33 +198,33 @@ std::string Instruction::to_string(const LinkedObjectFile& file) const {
assert(n_dst == 0);
assert(n_src == 3);
result += " ";
result += src[0].to_string(file);
result += src[0].to_string(labels);
result += ", ";
result += src[1].to_string(file);
result += src[1].to_string(labels);
result += "(";
result += src[2].to_string(file);
result += src[2].to_string(labels);
result += ")";
} else if (info.is_load) {
assert(n_dst == 1);
assert(n_src == 2);
result += " ";
result += dst[0].to_string(file);
result += dst[0].to_string(labels);
result += ", ";
result += src[0].to_string(file);
result += src[0].to_string(labels);
result += "(";
result += src[1].to_string(file);
result += src[1].to_string(labels);
result += ")";
} else {
// for instructions that aren't a store or load, the dest/sources are comma separated.
bool end_comma = false;
for (uint8_t i = 0; i < n_dst; i++) {
result += " " + dst[i].to_string(file) + ",";
result += " " + dst[i].to_string(labels) + ",";
end_comma = true;
}
for (uint8_t i = 0; i < n_src; i++) {
result += " " + src[i].to_string(file) + ",";
result += " " + src[i].to_string(labels) + ",";
end_comma = true;
}
@@ -312,3 +332,25 @@ int Instruction::get_label_target() const {
}
return result;
}
bool Instruction::operator==(const Instruction& other) const {
if (kind != other.kind || n_src != other.n_src || n_dst != other.n_dst ||
cop2_dest != other.cop2_dest || cop2_bc != other.cop2_bc || il != other.il) {
return false;
}
for (int i = 0; i < n_dst; i++) {
if (dst[i] != other.dst[i]) {
return false;
}
}
for (int i = 0; i < n_src; i++) {
if (src[i] != other.src[i]) {
return false;
}
}
return true;
}
} // namespace decompiler
+12 -5
View File
@@ -9,10 +9,12 @@
#ifndef NEXT_INSTRUCTION_H
#define NEXT_INSTRUCTION_H
#include <vector>
#include "OpcodeInfo.h"
#include "Register.h"
class LinkedObjectFile;
namespace decompiler {
struct DecompilerLabel;
constexpr int MAX_INSTRUCTION_SOURCE = 3;
constexpr int MAX_INTRUCTION_DEST = 1;
@@ -41,7 +43,7 @@ struct InstructionAtom {
int get_label() const;
std::string get_sym() const;
std::string to_string(const LinkedObjectFile& file) const;
std::string to_string(const std::vector<DecompilerLabel>& labels) const;
bool is_link_or_label() const;
bool is_reg() const { return kind == REGISTER; }
@@ -51,11 +53,13 @@ struct InstructionAtom {
bool is_reg(Register r) const { return kind == REGISTER && reg == r; }
bool operator==(const InstructionAtom& other) const;
bool operator!=(const InstructionAtom& other) const { return !((*this) == other); }
private:
int32_t imm;
int label_id;
Register reg;
std::string sym;
};
@@ -66,7 +70,7 @@ class Instruction {
InstructionKind kind = InstructionKind::UNKNOWN;
std::string op_name_to_string() const;
std::string to_string(const LinkedObjectFile& file) const;
std::string to_string(const std::vector<DecompilerLabel>& labels) const;
bool is_valid() const;
void add_src(InstructionAtom& a);
@@ -89,10 +93,13 @@ class Instruction {
int get_label_target() const;
bool operator==(const Instruction& other) const;
bool operator!=(const Instruction& other) const { return !((*this) == other); }
// extra fields for some COP2 instructions.
uint8_t cop2_dest = 0xff; // 0xff indicates "don't print dest"
uint8_t cop2_bc = 0xff; // 0xff indicates "don't print bc"
uint8_t il = 0xff; // 0xff indicates "don't print il"
};
} // namespace decompiler
#endif // NEXT_INSTRUCTION_H
+2
View File
@@ -8,6 +8,7 @@
#include <cassert>
#include "decompiler/ObjectFile/LinkedObjectFile.h"
namespace decompiler {
// utility class to extract fields of an opcode.
struct OpcodeFields {
OpcodeFields(uint32_t _data) : data(_data) {}
@@ -1171,3 +1172,4 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg
return i;
}
} // namespace decompiler
+2 -1
View File
@@ -11,9 +11,10 @@
#include "Instruction.h"
namespace decompiler {
class LinkedWord;
class LinkedObjectFile;
Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg_id, int word_id);
} // namespace decompiler
#endif // NEXT_INSTRUCTIONDECODE_H
@@ -6,6 +6,7 @@
#include <cassert>
#include "InstructionMatching.h"
namespace decompiler {
/*!
* Check if the given instruction stores a GPR with the specified parameters.
*/
@@ -348,3 +349,4 @@ bool is_always_branch(const Instruction& instr) {
return false;
}
} // namespace decompiler
+2 -1
View File
@@ -11,6 +11,7 @@
#include "Instruction.h"
#include "decompiler/util/MatchParam.h"
namespace decompiler {
bool is_no_link_gpr_store(const Instruction& instr,
MatchParam<int> size,
MatchParam<Register> src,
@@ -56,5 +57,5 @@ Register make_fpr(int fpr);
bool is_branch(const Instruction& instr, MatchParam<bool> likely);
bool is_always_branch(const Instruction& instr);
} // namespace decompiler
#endif // JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H
+307
View File
@@ -0,0 +1,307 @@
#include <cassert>
#include <algorithm>
#include <stdexcept>
#include "common/common_types.h"
#include "InstructionParser.h"
namespace decompiler {
InstructionParser::InstructionParser() {
init_opcode_info();
// we only support a subset of the total instructions. These are common used and don't have
// strange formatting.
int added = 0;
for (auto i : {InstructionKind::DADDIU, InstructionKind::ADDIU, InstructionKind::SLTI,
InstructionKind::SLTIU, InstructionKind::SB, InstructionKind::SH,
InstructionKind::SW, InstructionKind::SD, InstructionKind::SQ,
InstructionKind::LB, InstructionKind::LBU, InstructionKind::LH,
InstructionKind::LHU, InstructionKind::LW, InstructionKind::LWU,
InstructionKind::LD, InstructionKind::LQ, InstructionKind::LDR,
InstructionKind::LDL, InstructionKind::LWL, InstructionKind::LWR,
InstructionKind::DADDU, InstructionKind::SUBU, InstructionKind::ADDU,
InstructionKind::DSUBU, InstructionKind::MULT3, InstructionKind::MULTU3,
InstructionKind::AND, InstructionKind::OR, InstructionKind::NOR,
InstructionKind::XOR, InstructionKind::MOVN, InstructionKind::MOVZ,
InstructionKind::SLT, InstructionKind::SLTU, InstructionKind::SLL,
InstructionKind::SRA, InstructionKind::SRL, InstructionKind::DSLL,
InstructionKind::DSLL32, InstructionKind::DSRA, InstructionKind::DSRA32,
InstructionKind::DSRL, InstructionKind::DSRL32, InstructionKind::DSRAV,
InstructionKind::SLLV, InstructionKind::DSLLV, InstructionKind::DSRLV,
InstructionKind::DIV, InstructionKind::DIVU, InstructionKind::ORI,
InstructionKind::XORI, InstructionKind::ANDI, InstructionKind::LUI,
InstructionKind::JALR, InstructionKind::JR, InstructionKind::LWC1,
InstructionKind::SWC1, InstructionKind::ADDS, InstructionKind::SUBS,
InstructionKind::MULS, InstructionKind::DIVS, InstructionKind::MINS,
InstructionKind::MAXS, InstructionKind::MADDS, InstructionKind::MSUBS,
InstructionKind::RSQRTS, InstructionKind::ABSS, InstructionKind::NEGS,
InstructionKind::CVTSW, InstructionKind::CVTWS, InstructionKind::MOVS,
InstructionKind::SQRTS, InstructionKind::CLTS, InstructionKind::CLES,
InstructionKind::CEQS, InstructionKind::BC1F, InstructionKind::BC1T,
InstructionKind::BEQ, InstructionKind::BNE, InstructionKind::BEQL,
InstructionKind::BNEL, InstructionKind::BC1FL, InstructionKind::BC1TL,
InstructionKind::BLTZ, InstructionKind::BGEZ, InstructionKind::BLEZ,
InstructionKind::BGTZ, InstructionKind::BLTZL, InstructionKind::BGTZL,
InstructionKind::BGEZL}) {
auto& info = gOpcodeInfo[int(i)];
if (info.defined) {
m_opcode_name_lookup[info.name] = int(i);
added++;
}
}
assert(added == int(m_opcode_name_lookup.size()));
}
namespace {
std::string get_until_space(std::string& instr) {
assert(!instr.empty());
size_t i;
for (i = 0; i < instr.length(); i++) {
if (instr[i] == ' ') {
break;
}
}
auto name = instr.substr(0, i);
if (i == instr.length()) {
instr.clear();
} else {
instr = instr.substr(i + 1);
}
return name;
}
std::string get_comma_separated(std::string& instr) {
assert(!instr.empty());
auto arg = get_until_space(instr);
if (instr.empty()) {
assert(arg.back() != ',');
} else {
assert(arg.back() == ',');
arg.pop_back();
}
return arg;
}
std::string get_before_paren(std::string& instr) {
size_t i;
for (i = 0; i < instr.length(); i++) {
if (instr[i] == '(') {
auto result = instr.substr(0, i);
instr = instr.substr(i);
return result;
}
}
assert(false);
}
std::string get_in_paren(std::string& instr) {
assert(instr.length() > 2);
assert(instr.front() == '(');
size_t i;
for (i = 0; i < instr.length(); i++) {
if (instr[i] == ')') {
auto result = instr.substr(1, i - 1);
if (i == instr.length()) {
instr.clear();
} else {
instr = instr.substr(i + 1);
}
return result;
}
}
assert(false);
}
bool is_integer(const std::string& str) {
assert(!str.empty());
char* end;
std::strtol(str.c_str(), &end, 10);
return end == str.c_str() + str.length();
}
int parse_integer(const std::string& str) {
assert(!str.empty());
char* end;
int result = std::strtol(str.c_str(), &end, 10);
assert(end == str.c_str() + str.length());
return result;
}
std::vector<std::string> string_to_lines(const std::string& str) {
std::vector<std::string> result;
std::string::size_type i;
std::string::size_type start = 0;
while (true) {
i = str.find('\n', start);
if (i == std::string::npos) {
if (start < str.length()) {
result.push_back(str.substr(start));
}
return result;
} else {
result.push_back(str.substr(start, i - start));
start = i + 1;
}
}
}
} // namespace
Instruction InstructionParser::parse_single_instruction(
std::string str,
const std::vector<DecompilerLabel>& labels) {
auto name = get_until_space(str);
auto lookup = m_opcode_name_lookup.find(name);
if (lookup == m_opcode_name_lookup.end()) {
throw std::runtime_error("InstructionParser cannot handle opcode " + name);
}
Instruction instr;
instr.kind = InstructionKind(lookup->second);
auto& info = gOpcodeInfo[lookup->second];
for (u8 i = 0; i < info.step_count; i++) {
auto& step = info.steps[i];
switch (step.decode) {
case DecodeType::GPR: {
std::string gpr_name;
if ((info.is_store || info.is_load) && i == 2) {
gpr_name = get_in_paren(str);
} else {
gpr_name = get_comma_separated(str);
}
Register reg(gpr_name);
assert(reg.get_kind() == Reg::GPR);
InstructionAtom atom;
atom.set_reg(reg);
if (step.is_src) {
instr.add_src(atom);
} else {
instr.add_dst(atom);
}
} break;
case DecodeType::FPR: {
auto reg_name = get_comma_separated(str);
Register reg(reg_name);
assert(reg.get_kind() == Reg::FPR);
InstructionAtom atom;
atom.set_reg(reg);
if (step.is_src) {
instr.add_src(atom);
} else {
instr.add_dst(atom);
}
} break;
case DecodeType::IMM: {
InstructionAtom atom;
std::string atom_str;
if ((info.is_store || info.is_load) && i == 1) {
// number before paren
atom_str = get_before_paren(str);
} else {
atom_str = get_comma_separated(str);
}
if (is_integer(atom_str)) {
auto amt = parse_integer(atom_str);
atom.set_imm(amt);
} else {
atom.set_sym(atom_str);
}
if (step.is_src) {
instr.add_src(atom);
} else {
instr.add_dst(atom);
}
} break;
case DecodeType::BRANCH_TARGET: {
auto label = get_comma_separated(str);
auto f = std::find_if(labels.begin(), labels.end(),
[&](const DecompilerLabel& l) { return l.name == label; });
assert(f != labels.end());
auto idx = f - labels.begin();
InstructionAtom atom;
atom.set_label(idx);
if (step.is_src) {
instr.add_src(atom);
} else {
instr.add_dst(atom);
}
} break;
default:
assert(false);
}
}
assert(str.empty());
return instr;
}
ParsedProgram InstructionParser::parse_program(const std::string& str) {
ParsedProgram program;
auto lines = string_to_lines(str);
int byte_offset = 0;
// first pass
for (auto& line : lines) {
// strip off leading white space
size_t i;
for (i = 0; i < line.length(); i++) {
if (line[i] != ' ') {
line = line.substr(i);
break;
}
}
if (line.empty()) {
continue;
}
if (line.front() == 'L') {
if (line.back() == ':') {
line.pop_back();
} else {
assert(false);
}
DecompilerLabel label;
label.target_segment = 0;
label.offset = byte_offset;
label.name = line;
program.labels.push_back(label);
} else {
byte_offset += 4;
}
}
// second pass
for (auto& line : lines) {
if (!line.empty() && line.front() != 'L') {
program.instructions.push_back(parse_single_instruction(line, program.labels));
}
}
return program;
}
std::string ParsedProgram::print() {
std::string result;
int offset = 0;
for (auto& instr : instructions) {
for (auto& label : labels) {
if (label.offset == offset) {
result += label.name;
result += ":\n";
}
}
result += ' ';
result += ' ';
result += instr.to_string(labels);
result += '\n';
offset += 4;
}
return result;
}
} // namespace decompiler
+29
View File
@@ -0,0 +1,29 @@
/*!
* The InstructionParser converts a string like "daddu a0, s7, r0" into an Instruction.
* It is used to generate test sequences of instructions for decompiler algorithms.
*/
#pragma once
#include <string>
#include <unordered_map>
#include "Instruction.h"
#include "DecompilerLabel.h"
namespace decompiler {
struct ParsedProgram {
std::vector<DecompilerLabel> labels;
std::vector<Instruction> instructions;
std::string print();
};
class InstructionParser {
public:
InstructionParser();
Instruction parse_single_instruction(std::string str, const std::vector<DecompilerLabel>& labels);
ParsedProgram parse_program(const std::string& str);
private:
std::unordered_map<std::string, int> m_opcode_name_lookup;
};
} // namespace decompiler
+11 -1
View File
@@ -6,8 +6,13 @@
#include "OpcodeInfo.h"
#include <cassert>
namespace decompiler {
OpcodeInfo gOpcodeInfo[(uint32_t)InstructionKind::EE_OP_MAX];
namespace {
bool opcodes_initialized = false;
}
typedef InstructionKind IK;
typedef FieldType FT;
typedef DecodeType DT;
@@ -130,6 +135,9 @@ static OpcodeInfo& cd_dacc_svfs_svft(OpcodeInfo& info) {
}
void init_opcode_info() {
if (opcodes_initialized) {
return;
}
gOpcodeInfo[0].name = ";; ??????";
// RT, RS, SIMM
@@ -444,6 +452,7 @@ void init_opcode_info() {
// for the UNKNOWN op which shouldn't be valid.
total_count--;
assert(total_count == valid_count);
opcodes_initialized = true;
}
void OpcodeInfo::step(DecodeStep& s) {
@@ -501,4 +510,5 @@ OpcodeInfo& OpcodeInfo::dst_vf(FieldType field) {
OpcodeInfo& OpcodeInfo::dst_vi(FieldType field) {
return dst(field, DT::VI);
}
}
} // namespace decompiler
+3 -2
View File
@@ -10,6 +10,7 @@
#include <string>
namespace decompiler {
enum class InstructionKind {
UNKNOWN,
@@ -342,12 +343,12 @@ struct OpcodeInfo {
OpcodeInfo& dst_vf(FieldType field);
OpcodeInfo& dst_vi(FieldType field);
uint8_t step_count;
uint8_t step_count = 0;
DecodeStep steps[MAX_DECODE_STEPS];
};
extern OpcodeInfo gOpcodeInfo[(uint32_t)InstructionKind::EE_OP_MAX];
void init_opcode_info();
} // namespace decompiler
#endif // NEXT_OPCODEINFO_H
+20 -1
View File
@@ -7,6 +7,24 @@
#include <cassert>
#include <stdexcept>
namespace decompiler {
namespace Reg {
// register which may hold GOAL local variables
// clang-format off
const bool allowed_local_gprs[Reg::MAX_GPR] = {
false /*R0*/, false /*AT*/, true /*V0*/, true /*V1*/,
true /*A0*/, true /*A1*/, true /*A2*/, true /*A3*/,
true /*T0*/, true /*T1*/, true /*T2*/, true /*T3*/,
true /*T4*/, true /*T5*/, true /*T6*/, true /*T7*/,
true /*S0*/, true /*S1*/, true /*S2*/, true /*S3*/,
true /*S4*/, true /*S5*/, false /*S6*/, false /*S7*/,
true /*T8*/, true /*T9*/, false /*K0*/, false /*K1*/,
true /*GP*/, true /*SP*/, false /*FP*/, false /*RA*/
};
// clang-format on
} // namespace Reg
////////////////////////////
// Register Name Constants
////////////////////////////
@@ -233,4 +251,5 @@ bool Register::operator==(const Register& other) const {
bool Register::operator!=(const Register& other) const {
return id != other.id;
}
}
} // namespace decompiler
+5 -1
View File
@@ -11,6 +11,7 @@
#include <cstdint>
#include <string>
namespace decompiler {
// Namespace for register name constants
namespace Reg {
enum RegisterKind {
@@ -120,6 +121,9 @@ enum Vi {
CMSAR1 = 31,
MAX_COP2 = 32
};
const extern bool allowed_local_gprs[Reg::MAX_GPR];
} // namespace Reg
// Representation of a register. Uses a 32-bit integer internally.
@@ -148,5 +152,5 @@ class Register {
private:
uint16_t id = -1;
};
} // namespace decompiler
#endif // NEXT_REGISTER_H
+3 -1
View File
@@ -4,6 +4,7 @@
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/Disasm/InstructionMatching.h"
namespace decompiler {
/*!
* Find all basic blocks in a function.
* All delay slot instructions are grouped with the branch instruction.
@@ -48,4 +49,5 @@ std::vector<BasicBlock> find_blocks_in_function(const LinkedObjectFile& file,
}
return basic_blocks;
}
}
} // namespace decompiler
+2
View File
@@ -7,6 +7,7 @@
#include "decompiler/util/DecompilerTypeSystem.h"
#include "decompiler/util/TP_Type.h"
namespace decompiler {
class LinkedObjectFile;
class Function;
@@ -48,3 +49,4 @@ struct BlockTopologicalSort {
std::vector<BasicBlock> find_blocks_in_function(const LinkedObjectFile& file,
int seg,
const Function& func);
} // namespace decompiler
+2
View File
@@ -5,6 +5,7 @@
#include "CfgVtx.h"
#include "Function.h"
namespace decompiler {
/////////////////////////////////////////
/// CfgVtx
/////////////////////////////////////////
@@ -1912,3 +1913,4 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
return cfg;
}
} // namespace decompiler
+2 -1
View File
@@ -11,6 +11,7 @@ namespace goos {
class Object;
}
namespace decompiler {
/*!
* In v, find an item equal to old, and replace it with replace.
* Will throw an error is there is not exactly one thing equal to old.
@@ -351,5 +352,5 @@ class ControlFlowGraph {
class LinkedObjectFile;
class Function;
std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int seg, Function& func);
} // namespace decompiler
#endif // JAK_DISASSEMBLER_CFGVTX_H
+3 -1
View File
@@ -2,6 +2,7 @@
#include "decompiler/IR/IR.h"
#include "ExpressionStack.h"
namespace decompiler {
namespace {
bool expressionize_begin(IR_Begin* begin, LinkedObjectFile& file) {
ExpressionStack stack;
@@ -55,4 +56,5 @@ bool Function::build_expression(LinkedObjectFile& file) {
}
return true;
}
}
} // namespace decompiler
+3 -1
View File
@@ -1,6 +1,7 @@
#include "third-party/fmt/core.h"
#include "ExpressionStack.h"
namespace decompiler {
std::string ExpressionStack::StackEntry::print(LinkedObjectFile& file) {
return fmt::format("d: {} s: {} | {} <- {}", display, sequence_point,
destination.has_value() ? destination.value().to_charp() : "N/A",
@@ -107,4 +108,5 @@ ExpressionStack::StackEntry& ExpressionStack::get_display_stack_top() {
}
}
assert(false);
}
}
} // namespace decompiler
+3 -1
View File
@@ -6,6 +6,7 @@
#include "decompiler/Disasm/Register.h"
#include "decompiler/util/TP_Type.h"
namespace decompiler {
/*!
* An ExpressionStack is used to track partial expressions when rebuilding the tree structure of
* GOAL code. Linear sequences of operations are added onto the expression stack.
@@ -33,4 +34,5 @@ class ExpressionStack {
bool display_stack_empty();
StackEntry& get_display_stack_top();
};
};
} // namespace decompiler
+14 -12
View File
@@ -1,13 +1,14 @@
#include <cassert>
#include <vector>
#include "Function.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "TypeInspector.h"
#include "decompiler/IR/IR.h"
namespace decompiler {
namespace {
std::vector<Register> gpr_backups = {make_gpr(Reg::GP), make_gpr(Reg::S5), make_gpr(Reg::S4),
make_gpr(Reg::S3), make_gpr(Reg::S2), make_gpr(Reg::S1),
@@ -70,8 +71,8 @@ void Function::analyze_prologue(const LinkedObjectFile& file) {
// storing stack pointer on the stack is done by some ASM kernel functions
if (instr.kind == InstructionKind::SW && instr.get_src(0).get_reg() == make_gpr(Reg::SP)) {
printf("[Warning] %s Suspected ASM function based on this instruction in prologue: %s\n",
guessed_name.to_string().c_str(), instr.to_string(file).c_str());
warnings += ";; Flagged as ASM function because of " + instr.to_string(file) + "\n";
guessed_name.to_string().c_str(), instr.to_string(file.labels).c_str());
warnings += ";; Flagged as ASM function because of " + instr.to_string(file.labels) + "\n";
suspected_asm = true;
return;
}
@@ -92,9 +93,9 @@ 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)) {
spdlog::warn("{} Suspected ASM function based on this instruction in prologue: {}\n",
guessed_name.to_string(), instr.to_string(file));
warnings += ";; Flagged as ASM function because of " + instr.to_string(file) + "\n";
lg::warn("{} Suspected ASM function based on this instruction in prologue: {}\n",
guessed_name.to_string(), instr.to_string(file.labels));
warnings += ";; Flagged as ASM function because of " + instr.to_string(file.labels) + "\n";
suspected_asm = true;
return;
}
@@ -164,9 +165,9 @@ void Function::analyze_prologue(const LinkedObjectFile& file) {
suspected_asm = true;
printf("[Warning] %s Suspected asm function that isn't flagged due to stack store %s\n",
guessed_name.to_string().c_str(),
instructions.at(idx + i).to_string(file).c_str());
instructions.at(idx + i).to_string(file.labels).c_str());
warnings += ";; Suspected asm function due to stack store: " +
instructions.at(idx + i).to_string(file) + "\n";
instructions.at(idx + i).to_string(file.labels) + "\n";
return;
}
}
@@ -194,9 +195,9 @@ void Function::analyze_prologue(const LinkedObjectFile& file) {
suspected_asm = true;
printf("[Warning] %s Suspected asm function that isn't flagged due to stack store %s\n",
guessed_name.to_string().c_str(),
instructions.at(idx + i).to_string(file).c_str());
instructions.at(idx + i).to_string(file.labels).c_str());
warnings += ";; Suspected asm function due to stack store: " +
instructions.at(idx + i).to_string(file) + "\n";
instructions.at(idx + i).to_string(file.labels) + "\n";
return;
}
}
@@ -643,7 +644,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts)
// done!
// fmt::print("Got type {} parent {}\n", type_name, parent_type);
dts.add_type_parent(type_name, parent_type);
Label flag_label = file.labels.at(label_idx);
DecompilerLabel flag_label = file.labels.at(label_idx);
u64 word = file.read_data_word(flag_label);
flag_label.offset += 4;
u64 word2 = file.read_data_word(flag_label);
@@ -744,4 +745,5 @@ BlockTopologicalSort Function::bb_topo_sort() {
}
return result;
}
}
} // namespace decompiler
+2 -1
View File
@@ -15,6 +15,7 @@
#include "common/type_system/TypeSpec.h"
#include "decompiler/config.h"
namespace decompiler {
class DecompilerTypeSystem;
class IR_Atomic;
class IR;
@@ -158,5 +159,5 @@ class Function {
std::unordered_map<int, int> instruction_to_basic_op;
std::unordered_map<int, int> basic_op_to_instruction;
};
} // namespace decompiler
#endif // NEXT_FUNCTION_H
+3 -1
View File
@@ -1,6 +1,7 @@
#include "Function.h"
#include "decompiler/IR/IR.h"
namespace decompiler {
namespace {
bool in_set(RegSet& set, const Register& obj) {
return set.find(obj) != set.end();
@@ -170,4 +171,5 @@ void Function::run_reg_usage() {
}
}
}
}
}
} // namespace decompiler
+3 -1
View File
@@ -3,6 +3,7 @@
#include "third-party/fmt/core.h"
#include "decompiler/config.h"
namespace decompiler {
namespace {
TypeState construct_initial_typestate(const TypeSpec& f_ts) {
TypeState result;
@@ -132,4 +133,5 @@ bool Function::run_type_analysis(const TypeSpec& my_type,
}
return true;
}
}
} // namespace decompiler
+3 -1
View File
@@ -8,6 +8,7 @@
#include "common/type_system/deftype.h"
#include "decompiler/IR/IR.h"
namespace decompiler {
namespace {
struct FieldPrint {
char format = '\0';
@@ -843,4 +844,5 @@ std::string TypeInspectorResult::print_as_deftype() {
result.append(")\n");
return result;
}
}
} // namespace decompiler
+4 -1
View File
@@ -8,10 +8,12 @@
#include <vector>
#include "common/common_types.h"
class Field;
namespace decompiler {
class Function;
class DecompilerTypeSystem;
class LinkedObjectFile;
class Field;
struct TypeInspectorResult {
bool success = false;
@@ -34,3 +36,4 @@ TypeInspectorResult inspect_inspect_method(Function& inspect,
const std::string& type_name,
DecompilerTypeSystem& dts,
LinkedObjectFile& file);
} // namespace decompiler
+5 -2
View File
@@ -11,9 +11,11 @@
#include "decompiler/Function/Function.h"
#include "decompiler/Function/BasicBlocks.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/IR/IR.h"
#include "common/symbols.h"
namespace decompiler {
namespace {
///////////////////////////////
@@ -135,7 +137,7 @@ std::shared_ptr<IR_Atomic> to_asm_automatic(const std::string& str, Instruction&
}
if (instr.n_src >= 3) {
result->src1 = instr_atom_to_ir(instr.get_src(2), idx);
result->src2 = instr_atom_to_ir(instr.get_src(2), idx);
}
result->set_reg_info();
@@ -2520,7 +2522,7 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
// everything failed
if (!result) {
// temp hack for debug:
printf("Instruction -> BasicOp failed on %s\n", i.to_string(*file).c_str());
printf("Instruction -> BasicOp failed on %s\n", i.to_string(file->labels).c_str());
func->add_basic_op(std::make_shared<IR_Failed_Atomic>(), instr, instr + 1);
} else {
if (!func->contains_asm_ops && dynamic_cast<IR_AsmOp*>(result.get())) {
@@ -2536,3 +2538,4 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
}
}
}
} // namespace decompiler
+3 -1
View File
@@ -6,8 +6,10 @@
#pragma once
namespace decompiler {
class Function;
struct BasicBlock;
class LinkedObjectFile;
void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file);
void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file);
} // namespace decompiler
+2
View File
@@ -7,6 +7,7 @@
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/IR/IR.h"
namespace decompiler {
namespace {
std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx);
@@ -1278,3 +1279,4 @@ std::shared_ptr<IR> build_cfg_ir(Function& function,
return nullptr;
}
}
} // namespace decompiler
+3 -1
View File
@@ -2,9 +2,11 @@
#include <memory>
namespace decompiler {
class IR;
class Function;
class LinkedObjectFile;
class ControlFlowGraph;
std::shared_ptr<IR> build_cfg_ir(Function& function, ControlFlowGraph& cfg, LinkedObjectFile& file);
std::shared_ptr<IR> build_cfg_ir(Function& function, ControlFlowGraph& cfg, LinkedObjectFile& file);
} // namespace decompiler
+3 -1
View File
@@ -3,6 +3,7 @@
#include "common/goos/PrettyPrinter.h"
#include "third-party/fmt/core.h"
namespace decompiler {
// hack to print out reverse deref paths on loads to help with debugging load stuff.
bool enable_hack_load_path_print = false;
// hack to print (begin x) as x to make debug output easier to read.
@@ -1273,4 +1274,5 @@ goos::Object IR_Break::to_form(const LinkedObjectFile& file) const {
void IR_Break::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(return_code);
output->push_back(dead_code);
}
}
} // namespace decompiler
+6 -5
View File
@@ -11,14 +11,15 @@
#include "decompiler/util/DecompilerTypeSystem.h"
#include "decompiler/util/TP_Type.h"
class LinkedObjectFile;
class DecompilerTypeSystem;
class ExpressionStack;
namespace goos {
class Object;
}
namespace decompiler {
class LinkedObjectFile;
class DecompilerTypeSystem;
class ExpressionStack;
class IR {
public:
virtual goos::Object to_form(const LinkedObjectFile& file) const = 0;
@@ -765,5 +766,5 @@ class IR_Break : public virtual IR {
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
} // namespace decompiler
#endif // JAK_IR_H
+3 -1
View File
@@ -2,6 +2,7 @@
#include "IR.h"
#include "decompiler/Function/ExpressionStack.h"
namespace decompiler {
bool IR_Set_Atomic::expression_stack(ExpressionStack& stack, LinkedObjectFile& file) {
// first determine the type of the set.
switch (kind) {
@@ -448,4 +449,5 @@ bool IR_FloatMath1::update_from_stack(const std::unordered_set<Register, Registe
LinkedObjectFile& file) {
update_from_stack_helper(&arg, consume, stack, file);
return true;
}
}
} // namespace decompiler
+3 -1
View File
@@ -5,6 +5,7 @@
#include "decompiler/util/TP_Type.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
namespace decompiler {
namespace {
// bool is_plain_type(const TP_Type& type, const TypeSpec& ts) {
// return type.as_typespec() == ts;
@@ -945,4 +946,5 @@ TP_Type IR_CMoveF::get_expression_type(const TypeState& input,
(void)file;
(void)dts;
return TP_Type::make_from_typespec(TypeSpec("symbol"));
}
}
} // namespace decompiler
File diff suppressed because it is too large Load Diff
+544
View File
@@ -0,0 +1,544 @@
#pragma once
#include <string>
#include <optional>
#include <cassert>
#include "common/goos/Object.h"
#include "decompiler/Disasm/Register.h"
#include "decompiler/Disasm/Instruction.h"
#include "Env.h"
namespace decompiler {
class Expr;
/*!
* A "Variable" represents a register at a given instruction index.
* The register can either be a GOAL local variable or a GOAL register used in inline assembly.
* Because OpenGOAL's registers don't one-to-one map to GOAL registers, GOAL "inline assembly
* registers" will become OpenGOAL variables, and are treated similarly to variables in
* decompilation.
*
* In the earlier parts of decompilation, this just behaves like a register in all cases.
* But in later parts registers can be mapped to real local variables with types. A variable can
* look itself up in an environment to determine what "local variable" it is.
*
* Note: a variable is _not_ allowed to be R0, AT, S7, K0, K1, FP, or RA by default, as these
* can never hold normal GOAL locals. Inline assembly may use these, but you must set the allow_all
* flag to true in the constructor of Variable to indicate this is what you really want.
*
* Note: access to the process pointer (s6) is handled as a variable. As a result, you may always
* use s6 as a variable.
*/
class Variable {
public:
enum class Mode : u8 {
READ, // represents value of the variable at the beginning of the instruction
WRITE // represents value of the variable at the end of the instruction
};
Variable() = default;
Variable(Mode mode, Register reg, int atomic_idx, bool allow_all = false);
enum class Print {
AS_REG, // print as a PS2 register name
FULL, // print as a register name, plus an index, plus read or write
AS_VARIABLE, // print local variable name, error if impossible
AUTOMATIC, // print as variable, but if that's not possible print as reg.
};
std::string to_string(const Env* env, Print mode = Print::AUTOMATIC) const;
bool operator==(const Variable& other) const;
bool operator!=(const Variable& other) const;
const Register& reg() const { return m_reg; }
Mode mode() const { return m_mode; }
int idx() const { return m_atomic_idx; }
private:
Mode m_mode = Mode::READ; // do we represent a read or a write?
Register m_reg; // the EE register
int m_atomic_idx = -1; // the index in the function's list of AtomicOps
};
/*!
* An atomic operation represents a single operation from the point of view of the IR2 system.
* Each IR2 op is one or more instructions.
* Each function can be represented as a list of AtomicOps. These are stored in exactly the same
* order as the instructions appear.
*
* The AtomicOps use SimpleAtom and SimpleExpression. These are extremely limited versions of
* the full IR2 expression system, but are much easier to work with because they are less general
* and can't be nested infinitely. They also have features specific to the AtomicOp system that are
* not required for full expressions. The full expression system will later convert these into the
* more complicated expressions.
*
* The types of AtomicOp are:
* ConditionalMoveFalseOp
* CallOp
* SpecialOp
* BranchOp
* LoadVarOp
* StoreOp
* SetVarConditionOp
* AsmOp
* SetVarExprOp
* AsmOp
*/
class AtomicOp {
public:
explicit AtomicOp(int my_idx);
std::string to_string(const std::vector<DecompilerLabel>& labels, const Env* env);
virtual goos::Object to_form(const std::vector<DecompilerLabel>& labels,
const Env* env) const = 0;
virtual bool operator==(const AtomicOp& other) const = 0;
bool operator!=(const AtomicOp& other) const;
// determine if this is a (set! <var> thing) form. These will be handled differently in expression
// building.
virtual bool is_variable_set() const = 0;
// determine if this is a GOAL "sequence point".
// non-sequence point instructions may be out of order from the point of view of the expression
// stack.
virtual bool is_sequence_point() const = 0;
// get the variable being set by this operation. Only call this if is_variable_set returns true.
virtual Variable get_set_destination() const = 0;
// get the value of the variable being set, as an expression. Only call this if is_variable_set
// returns true.
virtual std::unique_ptr<Expr> get_set_source_as_expr() const = 0;
// convert me to an expression. If I'm a set!, this will produce a (set! x y), which may be
// undesirable when expression stacking.
virtual std::unique_ptr<Expr> get_as_expr() const = 0;
// figure out what registers are read and written in this AtomicOp and update read_regs,
// write_regs, and clobber_regs. It's expected that these have duplicates if a register appears
// in the original instructions multiple times. Ex: "and v0, v1, v1" would end up putting v1 in
// read twice.
virtual void update_register_info() = 0;
const std::vector<Register>& read_regs() { return m_read_regs; }
const std::vector<Register>& write_regs() { return m_write_regs; }
const std::vector<Register>& clobber_regs() { return m_clobber_regs; }
protected:
int m_my_idx = -1;
// the register values that are read (at the start of this op)
std::vector<Register> m_read_regs;
// the registers that have actual values written into them (at the end of this op)
std::vector<Register> m_write_regs;
// the registers which have junk written into them.
std::vector<Register> m_clobber_regs;
};
/*!
* The has a value. In some cases it can be set.
*/
class SimpleAtom {
public:
enum class Kind : u8 {
VARIABLE,
INTEGER_CONSTANT,
SYMBOL_PTR,
SYMBOL_VAL,
EMPTY_LIST,
STATIC_ADDRESS,
INVALID
};
SimpleAtom() = default;
static SimpleAtom make_var(const Variable& var);
static SimpleAtom make_sym_ptr(const std::string& name);
static SimpleAtom make_sym_val(const std::string& name);
static SimpleAtom make_empty_list();
static SimpleAtom make_int_constant(s64 value);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const;
bool is_var() const { return m_kind == Kind::VARIABLE; }
const Variable& var() const {
assert(is_var());
return m_variable;
}
bool is_int() const { return m_kind == Kind::INTEGER_CONSTANT; };
bool is_sym_ptr() const { return m_kind == Kind::SYMBOL_PTR; };
bool is_sym_val() const { return m_kind == Kind::SYMBOL_VAL; };
bool is_empty_list() const { return m_kind == Kind::EMPTY_LIST; };
bool is_static_addr() const { return m_kind == Kind::STATIC_ADDRESS; };
bool operator==(const SimpleAtom& other) const;
bool operator!=(const SimpleAtom& other) const { return !((*this) == other); }
void get_regs(std::vector<Register>* out) const;
private:
Kind m_kind = Kind::INVALID;
std::string m_string; // for symbol ptr and symbol val
s64 m_int = 0; // for integer constant and static address label id
Variable m_variable;
};
/*!
* A "simple expression" can be used within an AtomicOp.
* AtomicOps are often made up of very few instructions, so these expressions are quite simple and
* can't nest. There is an "operation" and some arguments. There are no side effects of a
* SimpleExpression. The side effects will be captured by the AtomicOp.
*
* Note - there is an expression kind called identity which takes one argument and uses that
* argument as an expression.
*/
class SimpleExpression {
public:
enum class Kind : u8 {
INVALID,
IDENTITY,
DIV_S,
MUL_S,
ADD_S,
SUB_S,
MIN_S,
MAX_S,
FLOAT_TO_INT,
INT_TO_FLOAT,
ABS_S,
NEG_S,
SQRT_S,
ADD,
SUB,
MUL_SIGNED,
DIV_SIGNED,
MOD_SIGNED,
DIV_UNSIGNED,
MOD_UNSIGNED,
OR,
AND,
NOR,
XOR,
LEFT_SHIFT,
RIGHT_SHIFT_ARITH,
RIGHT_SHIFT_LOGIC,
MUL_UNSIGNED,
NOT,
NEG
};
// how many arguments?
int args() const { return n_args; }
const SimpleAtom& get_arg(int idx) const {
assert(idx < args());
return m_args[idx];
}
Kind kind() const { return m_kind; }
SimpleExpression(Kind kind, const SimpleAtom& arg0);
SimpleExpression(Kind kind, const SimpleAtom& arg0, const SimpleAtom& arg1);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const;
bool operator==(const SimpleExpression& other) const;
bool is_identity() const { return m_kind == Kind::IDENTITY; }
void get_regs(std::vector<Register>* out) const;
private:
Kind m_kind = Kind::INVALID;
SimpleAtom m_args[2];
s8 n_args = -1;
};
/*!
* Set a variable equal to a Simple Expression
*/
class SetVarOp : public AtomicOp {
public:
SetVarOp(const Variable& dst, const SimpleExpression& src, int my_idx)
: AtomicOp(my_idx), m_dst(dst), m_src(src) {
assert(my_idx == dst.idx());
}
virtual goos::Object to_form(const std::vector<DecompilerLabel>& labels,
const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
Variable m_dst;
SimpleExpression m_src;
};
/*!
* An AsmOp represents a single inline assembly instruction. This is used when the BasicOpBuilder
* pass decides that an instruction could not have been generated from high-level GOAL code, and
* instead must be due to inline assembly.
*
* Each AsmOp stores the instruction it uses, as well as "Variable"s for each register used.
*/
class AsmOp : public AtomicOp {
public:
AsmOp(Instruction instr, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
Instruction m_instr;
std::optional<Variable> m_dst;
std::optional<Variable> m_src[3];
};
/*!
* A condition represents something that can generate a 0 or 1 based on a check or comparison.
* This can be used as a branch condition in BranchOp
* This can be used as a condition in an SetVarConditionOp, which sets a variable to a GOAL boolean.
* Sometimes a SetVarConditionOp gets spread across many many instructions, in which case it is
* not correctly detected here.
*/
class IR2_Condition {
public:
enum class Kind {
NOT_EQUAL,
EQUAL,
LESS_THAN_SIGNED,
GREATER_THAN_SIGNED,
LEQ_SIGNED,
GEQ_SIGNED,
GREATER_THAN_ZERO_SIGNED,
LEQ_ZERO_SIGNED,
LESS_THAN_ZERO,
GEQ_ZERO_SIGNED,
LESS_THAN_UNSIGNED,
GREATER_THAN_UNSIGNED,
LEQ_UNSIGNED,
GEQ_UNSIGNED,
ZERO,
NONZERO,
FALSE,
TRUTHY,
ALWAYS,
NEVER,
FLOAT_EQUAL,
FLOAT_NOT_EQUAL,
FLOAT_LESS_THAN,
FLOAT_GEQ,
FLOAT_LEQ,
FLOAT_GREATER_THAN,
INVALID
};
explicit IR2_Condition(Kind kind);
IR2_Condition(Kind kind, const Variable& src0);
IR2_Condition(Kind kind, const Variable& src0, const Variable& src1);
void invert();
bool operator==(const IR2_Condition& other) const;
bool operator!=(const IR2_Condition& other) const { return !((*this) == other); }
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const;
void get_regs(std::vector<Register>* out) const;
private:
Kind m_kind = Kind::INVALID;
Variable m_src[2];
};
/*!
* Set a variable to a GOAL boolean, based off of a condition.
*/
class SetVarConditionOp : public AtomicOp {
public:
SetVarConditionOp(Variable dst, IR2_Condition condition, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
Variable m_dst;
IR2_Condition m_condition;
};
/*!
* Store an Atom into a memory location.
* Note - this is _not_ considered a set! form because you are not setting the value of a
* register which can be expression-compacted.
*/
class StoreOp : public AtomicOp {
public:
StoreOp(SimpleExpression addr, SimpleAtom value, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
SimpleExpression m_addr;
SimpleAtom m_value;
};
/*!
* Load a value into a variable.
* This is treated as a set! form.
*/
class LoadVarOp : public AtomicOp {
public:
LoadVarOp(Variable dst, SimpleExpression src, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
Variable m_dst;
SimpleExpression m_src;
};
/*!
* This represents one of the possible instructions that can go in a branch delay slot.
* These will be "absorbed" into higher level structures, but for the purpose of printing AtomicOps,
* it will be nice to have these print like expressions.
*
* These are always part of the branch op.
*/
class IR2_BranchDelay {
public:
enum class Kind {
NOP,
SET_REG_FALSE,
SET_REG_TRUE,
SET_REG_REG,
SET_BINTEGER,
SET_PAIR,
DSLLV,
NEGATE
};
explicit IR2_BranchDelay(Kind kind);
IR2_BranchDelay(Kind kind, Variable var0);
IR2_BranchDelay(Kind kind, Variable var0, Variable var1);
IR2_BranchDelay(Kind kind, Variable var0, Variable var1, Variable var2);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const;
bool operator==(const IR2_BranchDelay& other) const;
void get_regs(std::vector<Register>* write, std::vector<Register>* read) const;
private:
std::optional<Variable> m_var[3];
Kind m_kind;
};
/*!
* This represents a combination of a condition + a branch + the branch delay slot.
* This is considered as a single operation.
*/
class BranchOp : public AtomicOp {
public:
BranchOp(bool likely,
IR2_Condition condition,
int label,
IR2_BranchDelay branch_delay,
int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
bool m_likely = false;
IR2_Condition m_condition;
int m_label = -1;
IR2_BranchDelay m_branch_delay;
};
/*!
* A "special" op has no arguments.
* NOP, BREAK, SUSPEND,
*/
class SpecialOp : public AtomicOp {
public:
enum class Kind {
NOP,
BREAK,
SUSPEND,
};
SpecialOp(Kind kind, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
Kind m_kind;
};
/*!
* Represents a function call.
* This has so many special cases and exceptions that it is separate from SpecialOp.
*/
class CallOp : public AtomicOp {
public:
CallOp(int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
};
/*!
* Unfortunately the original GOAL compiler does something weird when compiling (zero? x) or (not
* (zero? x)) when the result needs to be stored in a GOAL boolean (not in a branch condition). It
* first does a (set! result #t), then (possibly) a bunch of code to evaluate x, then does a
* conditional move (movn/movz). As a result, we can't recognize this as a Condition in the
* AtomicOp pass. Instead we'll recognize it as a (set! result #t) .... (cmove result flag) where
* flag is checked to be 0 or not. It's weird because all of the other similar cases get this
* right.
*
* Note - this isn't considered a variable set. It's "conditional set" so it needs to be
* handled separately. Unfortunately.
*/
class ConditionalMoveFalseOp : public AtomicOp {
public:
ConditionalMoveFalseOp(Variable dst, Variable src, bool on_zero, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_variable_set() const override;
bool is_sequence_point() const override;
Variable get_set_destination() const override;
std::unique_ptr<Expr> get_set_source_as_expr() const override;
std::unique_ptr<Expr> get_as_expr() const override;
void update_register_info() override;
private:
Variable m_dst, m_src;
bool m_on_zero;
};
} // namespace decompiler
+141
View File
@@ -0,0 +1,141 @@
#include "AtomicOpBuilder.h"
#include "common/log/log.h"
#include "decompiler/Function/BasicBlocks.h"
#include "decompiler/Function/Function.h"
namespace decompiler {
namespace {
Variable make_dst_var(Register reg, int idx) {
return Variable(Variable::Mode::WRITE, reg, idx);
}
Variable make_src_var(Register reg, int idx) {
return Variable(Variable::Mode::READ, reg, idx);
}
SimpleAtom make_src_atom(Register reg, int idx) {
return SimpleAtom::make_var(make_src_var(reg, idx));
}
/*!
* Convert a single instruction in the form instr dest_reg, src_reg, src_reg
* to an atomic op of (set! dst_reg (op src_reg src_reg))
* Like daddu a0, a1, a2
*/
void make_3reg_op(const Instruction& instr,
SimpleExpression::Kind kind,
int idx,
std::unique_ptr<AtomicOp>& result) {
auto dst = make_dst_var(instr.get_dst(0).get_reg(), idx);
auto src0 = make_src_atom(instr.get_src(0).get_reg(), idx);
auto src1 = make_src_atom(instr.get_src(1).get_reg(), idx);
result = std::make_unique<SetVarOp>(dst, SimpleExpression(kind, src0, src1), idx);
}
bool convert_and_1(const Instruction& i0, int idx, std::unique_ptr<AtomicOp>& result) {
// or reg, reg, reg:
make_3reg_op(i0, SimpleExpression::Kind::AND, idx, result);
return true;
}
bool convert_1(const Instruction& i0, int idx, std::unique_ptr<AtomicOp>& result) {
switch (i0.kind) {
case InstructionKind::AND:
return convert_and_1(i0, idx, result);
default:
return false;
}
}
} // namespace
/*!
* Convert an entire basic block and add the results to a FunctionAtomicOps
* @param block_id : the index of the block
* @param begin : the start of the instructions for the block
* @param end : the end of the instructions for the block
* @param container : the container to add to
*/
void convert_block_to_atomic_ops(int begin_idx,
std::vector<Instruction>::const_iterator begin,
std::vector<Instruction>::const_iterator end,
const std::vector<DecompilerLabel>& labels,
FunctionAtomicOps* container) {
container->block_id_to_first_atomic_op.push_back(container->ops.size());
for (auto& instr = begin; instr < end;) {
// how many instructions can we look at, at most?
int n_instr = end - instr;
// how many instructions did we use?
int length = 0;
// what is the index of the atomic op we would add
int op_idx = int(container->ops.size());
bool converted = false;
std::unique_ptr<AtomicOp> op;
if (n_instr >= 4) {
// try 4 instructions
}
if (!converted && n_instr >= 3) {
// try 3 instructions
}
if (!converted && n_instr >= 2) {
// try 2 instructions
}
if (!converted) {
// try 1 instruction
if (convert_1(*instr, op_idx, op)) {
converted = true;
length = 1;
}
}
if (!converted) {
// try assembly fallback.
}
if (!converted) {
// failed!
lg::die("Failed to convert instruction {} to an atomic op", instr->to_string(labels));
}
assert(converted && length && op);
// add mappings:
container->atomic_op_to_instruction[container->ops.size()] = begin_idx;
for (int i = 0; i < length; i++) {
container->instruction_to_basic_op[begin_idx + i] = container->ops.size();
}
// add
op->update_register_info();
container->ops.emplace_back(std::move(op));
instr += length;
}
container->block_id_to_end_atomic_op.push_back(container->ops.size());
}
FunctionAtomicOps convert_function_to_atomic_ops(const Function& func,
const std::vector<DecompilerLabel>& labels) {
FunctionAtomicOps result;
for (const auto& block : func.basic_blocks) {
// we should only consider the blocks which actually have instructions:
if (block.end_word > block.start_word) {
auto begin = func.instructions.begin() + block.start_word;
auto end = func.instructions.begin() + block.end_word;
convert_block_to_atomic_ops(block.start_word, begin, end, labels, &result);
} else {
result.block_id_to_first_atomic_op.push_back(-1);
result.block_id_to_end_atomic_op.push_back(-1);
}
}
assert(func.basic_blocks.size() == result.block_id_to_end_atomic_op.size());
assert(func.basic_blocks.size() == result.block_id_to_first_atomic_op.size());
return result;
}
} // namespace decompiler
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <vector>
#include "AtomicOp.h"
namespace decompiler {
class Function;
struct BasicBlock;
class LinkedObjectFile;
/*!
* A collection of Atomic Ops in a function
*/
struct FunctionAtomicOps {
// the actual ops, store in the correct order
std::vector<std::unique_ptr<AtomicOp>> ops;
// mappings from instructions to atomic ops and back
std::unordered_map<int, int> instruction_to_basic_op;
std::unordered_map<int, int> atomic_op_to_instruction;
// map from basic block to the index of the first op
std::vector<int> block_id_to_first_atomic_op;
// map from basic block to the index of the last op + 1
std::vector<int> block_id_to_end_atomic_op;
};
/*!
* Convert an entire basic block and add the results to a FunctionAtomicOps.
* Updates the mapping between blocks, instructions, and atomic ops as needed
* @param begin idx : the index of the first instruction for the block
* @param begin : the start of the instructions for the block
* @param end : the end of the instructions for the block
* @param labels : label names for the function, used for error prints on failed conversions
* @param container : the container to add to
*/
void convert_block_to_atomic_ops(int begin_idx,
std::vector<Instruction>::const_iterator begin,
std::vector<Instruction>::const_iterator end,
const std::vector<DecompilerLabel>& labels,
FunctionAtomicOps* container);
/*!
* Convert an entire function to AtomicOps
*/
FunctionAtomicOps convert_function_to_atomic_ops(const Function& func,
const std::vector<DecompilerLabel>& labels);
} // namespace decompiler
+10
View File
@@ -0,0 +1,10 @@
#include <stdexcept>
#include "Env.h"
namespace decompiler {
std::string Env::get_variable_name(Register reg, int atomic_idx) const {
(void)reg;
(void)atomic_idx;
throw std::runtime_error("Env::get_variable_name not yet implemented.");
}
} // namespace decompiler
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <string>
#include "decompiler/Disasm/Register.h"
namespace decompiler {
/*!
* An "environment" for a single function.
* This contains data for an entire function, like which registers are live when, the types of
* values in registers, and local variable names. This does not actually store IR itself, just
* shared data that all IR can look at. The concept is somewhat similar to Env in the compiler.
*/
class Env {
public:
bool has_local_vars() const { return m_has_local_vars; }
std::string get_variable_name(Register reg, int atomic_idx) const;
private:
bool m_has_local_vars = false;
};
} // namespace decompiler
+8
View File
@@ -0,0 +1,8 @@
#pragma once
namespace decompiler {
class IR2 {
public:
private:
};
} // namespace decompiler
+11 -9
View File
@@ -13,9 +13,10 @@
#include "decompiler/Disasm/InstructionDecode.h"
#include "decompiler/config.h"
#include "third-party/json.hpp"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
#include "common/goos/PrettyPrinter.h"
namespace decompiler {
/*!
* Set the number of segments in this object file.
* This can only be done once, and must be done before adding any words.
@@ -45,7 +46,7 @@ int LinkedObjectFile::get_label_id_for(int seg, int offset) {
if (kv == label_per_seg_by_offset.at(seg).end()) {
// create a new label
int id = labels.size();
Label label;
DecompilerLabel label;
label.target_segment = seg;
label.offset = offset;
label.name = "L" + std::to_string(id);
@@ -498,7 +499,7 @@ void LinkedObjectFile::process_fp_relative_links() {
} break;
default:
printf("unknown fp using op: %s\n", instr.to_string(*this).c_str());
printf("unknown fp using op: %s\n", instr.to_string(labels).c_str());
assert(false);
}
}
@@ -518,7 +519,7 @@ std::string LinkedObjectFile::to_asm_json(const std::string& obj_file_name) {
auto& func = functions_by_seg.at(seg).at(fi);
auto fname = func.guessed_name.to_string();
if (functions_seen.find(fname) != functions_seen.end()) {
spdlog::warn(
lg::warn(
"Function {} appears multiple times in the same object file {} - it cannot be uniquely "
"referenced from config",
func.guessed_name.to_string(), obj_file_name);
@@ -544,7 +545,7 @@ std::string LinkedObjectFile::to_asm_json(const std::string& obj_file_name) {
}
auto& instr = func.instructions.at(i);
op["id"] = i;
op["asm_op"] = instr.to_string(*this);
op["asm_op"] = instr.to_string(labels);
if (func.has_basic_ops() && func.instr_starts_basic_op(i)) {
op["basic_op"] = func.get_basic_op_at_instr(i)->print(*this);
@@ -608,7 +609,7 @@ std::string LinkedObjectFile::print_function_disassembly(Function& func,
}
auto& instr = func.instructions.at(i);
std::string line = " " + instr.to_string(*this);
std::string line = " " + instr.to_string(labels);
if (write_hex) {
if (line.length() < 60) {
@@ -1053,14 +1054,15 @@ goos::Object LinkedObjectFile::to_form_script_object(int seg,
return result;
}
u32 LinkedObjectFile::read_data_word(const Label& label) {
u32 LinkedObjectFile::read_data_word(const DecompilerLabel& label) {
assert(0 == (label.offset % 4));
auto& word = words_by_seg.at(label.target_segment).at(label.offset / 4);
assert(word.kind == LinkedWord::Kind::PLAIN_DATA);
return word.data;
}
std::string LinkedObjectFile::get_goal_string_by_label(const Label& label) const {
std::string LinkedObjectFile::get_goal_string_by_label(const DecompilerLabel& label) const {
assert(0 == (label.offset % 4));
return get_goal_string(label.target_segment, (label.offset / 4) - 1, false);
}
}
} // namespace decompiler
+6 -13
View File
@@ -14,19 +14,11 @@
#include <unordered_map>
#include <unordered_set>
#include "LinkedWord.h"
#include "decompiler/Disasm/DecompilerLabel.h"
#include "decompiler/Function/Function.h"
#include "common/common_types.h"
/*!
* A label to a location in this object file.
* Doesn't have to be word aligned.
*/
struct Label {
std::string name;
int target_segment;
int offset; // in bytes
};
namespace decompiler {
/*!
* An object file's data with linking information included.
*/
@@ -69,8 +61,8 @@ class LinkedObjectFile {
const std::string& extra_name);
std::string print_asm_function_disassembly(const std::string& my_name);
u32 read_data_word(const Label& label);
std::string get_goal_string_by_label(const Label& label) const;
u32 read_data_word(const DecompilerLabel& label);
std::string get_goal_string_by_label(const DecompilerLabel& label) const;
struct Stats {
uint32_t total_code_bytes = 0;
@@ -131,7 +123,7 @@ class LinkedObjectFile {
std::vector<std::vector<LinkedWord>> words_by_seg;
std::vector<uint32_t> offset_of_data_zone_by_seg;
std::vector<std::vector<Function>> functions_by_seg;
std::vector<Label> labels;
std::vector<DecompilerLabel> labels;
private:
goos::Object to_form_script(int seg, int word_idx, std::vector<bool>& seen);
@@ -142,5 +134,6 @@ class LinkedObjectFile {
std::vector<std::unordered_map<int, int>> label_per_seg_by_offset;
};
} // namespace decompiler
#endif // NEXT_LINKEDOBJECTFILE_H
@@ -11,6 +11,7 @@
#include "decompiler/util/DecompilerTypeSystem.h"
#include "common/link_types.h"
namespace decompiler {
// There are three link versions:
// V2 - not really in use anymore, but V4 will resue logic from it (and the game didn't rename the
// functions) V3 - optimized for code and small stuff. Supports segments (main, debug, top-level) V4
@@ -819,3 +820,4 @@ LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
return result;
}
} // namespace decompiler
@@ -11,9 +11,11 @@
#include "LinkedObjectFile.h"
namespace decompiler {
class DecompilerTypeSystem;
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
const std::string& name,
DecompilerTypeSystem& dts);
} // namespace decompiler
#endif // NEXT_LINKEDOBJECTFILECREATION_H
+2
View File
@@ -11,6 +11,7 @@
#include <cstdint>
#include <string>
namespace decompiler {
class LinkedWord {
public:
explicit LinkedWord(uint32_t _data) : data(_data) {}
@@ -31,5 +32,6 @@ class LinkedWord {
int label_id = -1;
std::string symbol_name;
};
} // namespace decompiler
#endif // JAK2_DISASSEMBLER_LINKEDWORD_H
+130 -129
View File
@@ -24,9 +24,10 @@
#include "decompiler/IR/BasicOpBuilder.h"
#include "decompiler/IR/CfgBuilder.h"
#include "decompiler/Function/TypeInspector.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
#include "third-party/json.hpp"
namespace decompiler {
namespace {
std::string strip_dgo_extension(const std::string& x) {
auto ext = x.substr(x.length() - 4, 4);
@@ -112,32 +113,32 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
const std::vector<std::string>& str_files) {
Timer timer;
spdlog::info("-Loading types...");
lg::info("-Loading types...");
dts.parse_type_defs({"decompiler", "config", "all-types.gc"});
if (!obj_file_name_map_file.empty()) {
spdlog::info("-Loading obj name map file...");
lg::info("-Loading obj name map file...");
load_map_file(file_util::read_text_file(file_util::get_file_path({obj_file_name_map_file})));
} else {
spdlog::warn(
lg::warn(
"Not using an obj name map file! The decompiler will automatically generate object file "
"names and write them to out/objs.txt. It is recommended to reuse this map file to get "
"consistent naming when doing a partial decompilation.");
}
spdlog::info("-Loading DGOs...");
lg::info("-Loading DGOs...");
for (auto& dgo : _dgos) {
get_objs_from_dgo(dgo);
}
spdlog::info("-Loading plain object files...");
lg::info("-Loading plain object files...");
for (auto& obj : object_files) {
auto data = file_util::read_binary_file(obj);
auto name = obj_filename_to_name(obj);
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO");
}
spdlog::info("-Loading streaming object files...");
lg::info("-Loading streaming object files...");
for (auto& obj : str_files) {
StrFileReader reader(obj);
// name from the file name
@@ -152,15 +153,15 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
}
}
spdlog::info("ObjectFileDB Initialized:");
spdlog::info("Total DGOs: {}", int(_dgos.size()));
spdlog::info("Total data: {} bytes", stats.total_dgo_bytes);
spdlog::info("Total objs: {}", stats.total_obj_files);
spdlog::info("Unique objs: {}", stats.unique_obj_files);
spdlog::info("Unique data: {} bytes", stats.unique_obj_bytes);
spdlog::info("Total {:.2f} ms ({:.3f} MB/sec, {:.2f} obj/sec)", timer.getMs(),
stats.total_dgo_bytes / ((1u << 20u) * timer.getSeconds()),
stats.total_obj_files / timer.getSeconds());
lg::info("ObjectFileDB Initialized:");
lg::info("Total DGOs: {}", int(_dgos.size()));
lg::info("Total data: {} bytes", stats.total_dgo_bytes);
lg::info("Total objs: {}", stats.total_obj_files);
lg::info("Unique objs: {}", stats.unique_obj_files);
lg::info("Unique data: {} bytes", stats.unique_obj_bytes);
lg::info("Total {:.2f} ms ({:.3f} MB/sec, {:.2f} obj/sec)", timer.getMs(),
stats.total_dgo_bytes / ((1u << 20u) * timer.getSeconds()),
stats.total_obj_files / timer.getSeconds());
}
void ObjectFileDB::load_map_file(const std::string& map_data) {
@@ -180,7 +181,7 @@ void ObjectFileDB::load_map_file(const std::string& map_data) {
for (auto& dgo : dgo_names) {
auto kv = dgo_obj_name_map[dgo].find(game_name_with_ag);
if (kv != dgo_obj_name_map[dgo].end()) {
spdlog::error("Object {} in dgo {} occurs more than one time.", game_name_with_ag, dgo);
lg::error("Object {} in dgo {} occurs more than one time.", game_name_with_ag, dgo);
assert(false);
}
dgo_obj_name_map[dgo][game_name_with_ag] = mapped_name;
@@ -317,7 +318,7 @@ void ObjectFileDB::get_objs_from_dgo(const std::string& filename) {
assert_string_empty_after(obj_header.name, 60);
if (std::string(obj_header.name).find("-ag") != std::string::npos) {
spdlog::error(
lg::error(
"Object file {} has \"-ag\" in its name. This will break any tools which use this to "
"detect an art group",
obj_header.name);
@@ -388,14 +389,13 @@ void ObjectFileDB::add_obj_from_dgo(const std::string& obj_name,
if (!dgo_obj_name_map.empty()) {
auto dgo_kv = dgo_obj_name_map.find(strip_dgo_extension(dgo_name));
if (dgo_kv == dgo_obj_name_map.end()) {
spdlog::error("Object {} is from DGO {}, but this DGO wasn't in the map.", obj_name,
dgo_name);
lg::error("Object {} is from DGO {}, but this DGO wasn't in the map.", obj_name, dgo_name);
assert(false);
}
auto name_kv = dgo_kv->second.find(obj_name);
if (name_kv == dgo_kv->second.end()) {
spdlog::error("Object {} from DGO {} wasn't found in the name map.", obj_name, dgo_name);
lg::error("Object {} from DGO {} wasn't found in the name map.", obj_name, dgo_name);
assert(false);
}
data.name_from_map = name_kv->second;
@@ -481,7 +481,7 @@ std::string ObjectFileDB::generate_obj_listing() {
* Process all of the linking data of all objects.
*/
void ObjectFileDB::process_link_data() {
spdlog::info("- Processing Link Data...");
lg::info("- Processing Link Data...");
Timer process_link_timer;
LinkedObjectFile::Stats combined_stats;
@@ -491,26 +491,26 @@ void ObjectFileDB::process_link_data() {
combined_stats.add(obj.linked_data.stats);
});
spdlog::info("Processed Link Data:");
spdlog::info(" Code {} bytes", combined_stats.total_code_bytes);
spdlog::info(" v2 Code {} bytes", combined_stats.total_v2_code_bytes);
spdlog::info(" v2 Link Data {} bytes", combined_stats.total_v2_link_bytes);
spdlog::info(" v2 Pointers {}", combined_stats.total_v2_pointers);
spdlog::info(" v2 Pointer Seeks {}", combined_stats.total_v2_pointer_seeks);
spdlog::info(" v2 Symbols {}", combined_stats.total_v2_symbol_count);
spdlog::info(" v2 Symbol Links {}", combined_stats.total_v2_symbol_links);
lg::info("Processed Link Data:");
lg::info(" Code {} bytes", combined_stats.total_code_bytes);
lg::info(" v2 Code {} bytes", combined_stats.total_v2_code_bytes);
lg::info(" v2 Link Data {} bytes", combined_stats.total_v2_link_bytes);
lg::info(" v2 Pointers {}", combined_stats.total_v2_pointers);
lg::info(" v2 Pointer Seeks {}", combined_stats.total_v2_pointer_seeks);
lg::info(" v2 Symbols {}", combined_stats.total_v2_symbol_count);
lg::info(" v2 Symbol Links {}", combined_stats.total_v2_symbol_links);
spdlog::info(" v3 Code {} bytes", combined_stats.v3_code_bytes);
spdlog::info(" v3 Link Data {} bytes", combined_stats.v3_link_bytes);
spdlog::info(" v3 Pointers {}", combined_stats.v3_pointers);
spdlog::info(" Split {}", combined_stats.v3_split_pointers);
spdlog::info(" Word {}", combined_stats.v3_word_pointers);
spdlog::info(" v3 Pointer Seeks {}", combined_stats.v3_pointer_seeks);
spdlog::info(" v3 Symbols {}", combined_stats.v3_symbol_count);
spdlog::info(" v3 Offset Symbol Links {}", combined_stats.v3_symbol_link_offset);
spdlog::info(" v3 Word Symbol Links {}", combined_stats.v3_symbol_link_word);
lg::info(" v3 Code {} bytes", combined_stats.v3_code_bytes);
lg::info(" v3 Link Data {} bytes", combined_stats.v3_link_bytes);
lg::info(" v3 Pointers {}", combined_stats.v3_pointers);
lg::info(" Split {}", combined_stats.v3_split_pointers);
lg::info(" Word {}", combined_stats.v3_word_pointers);
lg::info(" v3 Pointer Seeks {}", combined_stats.v3_pointer_seeks);
lg::info(" v3 Symbols {}", combined_stats.v3_symbol_count);
lg::info(" v3 Offset Symbol Links {}", combined_stats.v3_symbol_link_offset);
lg::info(" v3 Word Symbol Links {}", combined_stats.v3_symbol_link_word);
spdlog::info(" Total {} ms\n", process_link_timer.getMs());
lg::info(" Total {} ms\n", process_link_timer.getMs());
// printf("\n");
}
@@ -518,14 +518,14 @@ void ObjectFileDB::process_link_data() {
* Process all of the labels generated from linking and give them reasonable names.
*/
void ObjectFileDB::process_labels() {
spdlog::info("- Processing Labels...");
lg::info("- Processing Labels...");
Timer process_label_timer;
uint32_t total = 0;
for_each_obj([&](ObjectFileData& obj) { total += obj.linked_data.set_ordered_label_names(); });
spdlog::info("Processed Labels:");
spdlog::info(" Total {} labels", total);
spdlog::info(" Total {} ms", process_label_timer.getMs());
lg::info("Processed Labels:");
lg::info(" Total {} labels", total);
lg::info(" Total {} ms", process_label_timer.getMs());
// printf("\n");
}
@@ -534,9 +534,9 @@ void ObjectFileDB::process_labels() {
*/
void ObjectFileDB::write_object_file_words(const std::string& output_dir, bool dump_v3_only) {
if (dump_v3_only) {
spdlog::info("- Writing object file dumps (v3 only)...");
lg::info("- Writing object file dumps (v3 only)...");
} else {
spdlog::info("- Writing object file dumps (all)...");
lg::info("- Writing object file dumps (all)...");
}
Timer timer;
@@ -552,17 +552,17 @@ void ObjectFileDB::write_object_file_words(const std::string& output_dir, bool d
}
});
spdlog::info("Wrote object file dumps:");
spdlog::info(" Total {} files", total_files);
spdlog::info(" Total {:.3f} MB", total_bytes / ((float)(1u << 20u)));
spdlog::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(),
total_bytes / ((1u << 20u) * timer.getSeconds()));
lg::info("Wrote object file dumps:");
lg::info(" Total {} files", total_files);
lg::info(" Total {:.3f} MB", total_bytes / ((float)(1u << 20u)));
lg::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(),
total_bytes / ((1u << 20u) * timer.getSeconds()));
// printf("\n");
}
void ObjectFileDB::write_debug_type_analysis(const std::string& output_dir,
const std::string& suffix) {
spdlog::info("- Writing debug type analysis...");
lg::info("- Writing debug type analysis...");
Timer timer;
uint32_t total_bytes = 0, total_files = 0;
@@ -578,11 +578,11 @@ void ObjectFileDB::write_debug_type_analysis(const std::string& output_dir,
}
});
spdlog::info("Wrote functions dumps:");
spdlog::info(" Total {} files", total_files);
spdlog::info(" Total {} MB", total_bytes / ((float)(1u << 20u)));
spdlog::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(),
total_bytes / ((1u << 20u) * timer.getSeconds()));
lg::info("Wrote functions dumps:");
lg::info(" Total {} files", total_files);
lg::info(" Total {} MB", total_bytes / ((float)(1u << 20u)));
lg::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(),
total_bytes / ((1u << 20u) * timer.getSeconds()));
}
/*!
@@ -592,7 +592,7 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir,
bool disassemble_objects_without_functions,
bool write_json,
const std::string& file_suffix) {
spdlog::info("- Writing functions...");
lg::info("- Writing functions...");
Timer timer;
uint32_t total_bytes = 0, total_files = 0;
@@ -625,18 +625,18 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir,
file_util::write_text_file(file_util::combine_path(output_dir, "asm_functions.func"),
asm_functions);
spdlog::info("Wrote functions dumps:");
spdlog::info(" Total {} files", total_files);
spdlog::info(" Total {} MB", total_bytes / ((float)(1u << 20u)));
spdlog::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(),
total_bytes / ((1u << 20u) * timer.getSeconds()));
lg::info("Wrote functions dumps:");
lg::info(" Total {} files", total_files);
lg::info(" Total {} MB", total_bytes / ((float)(1u << 20u)));
lg::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(),
total_bytes / ((1u << 20u) * timer.getSeconds()));
}
/*!
* Find code/data zones, identify functions, and disassemble
*/
void ObjectFileDB::find_code() {
spdlog::info("- Finding code in object files...");
lg::info("- Finding code in object files...");
LinkedObjectFile::Stats combined_stats;
Timer timer;
@@ -649,28 +649,28 @@ void ObjectFileDB::find_code() {
if (get_config().game_version == 1 || obj.to_unique_name() != "effect-control-v0") {
obj.linked_data.process_fp_relative_links();
} else {
spdlog::warn("Skipping process_fp_relative_links in {}", obj.to_unique_name().c_str());
lg::warn("Skipping process_fp_relative_links in {}", obj.to_unique_name().c_str());
}
auto& obj_stats = obj.linked_data.stats;
if (obj_stats.code_bytes / 4 > obj_stats.decoded_ops) {
spdlog::warn("Failed to decode all in {} ({} / {})", obj.to_unique_name().c_str(),
obj_stats.decoded_ops, obj_stats.code_bytes / 4);
lg::warn("Failed to decode all in {} ({} / {})", obj.to_unique_name().c_str(),
obj_stats.decoded_ops, obj_stats.code_bytes / 4);
}
combined_stats.add(obj.linked_data.stats);
});
spdlog::info("Found code:");
spdlog::info(" Code {:.3f} MB", combined_stats.code_bytes / (float)(1 << 20));
spdlog::info(" Data {:.3f} MB", combined_stats.data_bytes / (float)(1 << 20));
spdlog::info(" Functions: {}", combined_stats.function_count);
spdlog::info(" fp uses resolved: {} / {} ({:.3f} %)", combined_stats.n_fp_reg_use_resolved,
combined_stats.n_fp_reg_use,
100.f * (float)combined_stats.n_fp_reg_use_resolved / combined_stats.n_fp_reg_use);
lg::info("Found code:");
lg::info(" Code {:.3f} MB", combined_stats.code_bytes / (float)(1 << 20));
lg::info(" Data {:.3f} MB", combined_stats.data_bytes / (float)(1 << 20));
lg::info(" Functions: {}", combined_stats.function_count);
lg::info(" fp uses resolved: {} / {} ({:.3f} %)", combined_stats.n_fp_reg_use_resolved,
combined_stats.n_fp_reg_use,
100.f * (float)combined_stats.n_fp_reg_use_resolved / combined_stats.n_fp_reg_use);
auto total_ops = combined_stats.code_bytes / 4;
spdlog::info(" Decoded {} / {} ({:.3f} %)", combined_stats.decoded_ops, total_ops,
100.f * (float)combined_stats.decoded_ops / total_ops);
spdlog::info(" Total {:.3f} ms", timer.getMs());
lg::info(" Decoded {} / {} ({:.3f} %)", combined_stats.decoded_ops, total_ops,
100.f * (float)combined_stats.decoded_ops / total_ops);
lg::info(" Total {:.3f} ms", timer.getMs());
// printf("\n");
}
@@ -679,7 +679,7 @@ void ObjectFileDB::find_code() {
* Doesn't change any state in ObjectFileDB.
*/
void ObjectFileDB::find_and_write_scripts(const std::string& output_dir) {
spdlog::info("- Finding scripts in object files...");
lg::info("- Finding scripts in object files...");
Timer timer;
std::string all_scripts;
@@ -696,12 +696,12 @@ void ObjectFileDB::find_and_write_scripts(const std::string& output_dir) {
auto file_name = file_util::combine_path(output_dir, "all_scripts.lisp");
file_util::write_text_file(file_name, all_scripts);
spdlog::info("Found scripts:");
spdlog::info(" Total {:.3f} ms\n", timer.getMs());
lg::info("Found scripts:");
lg::info(" Total {:.3f} ms\n", timer.getMs());
}
void ObjectFileDB::process_tpages() {
spdlog::info("- Finding textures in tpages...");
lg::info("- Finding textures in tpages...");
std::string tpage_string = "tpage-";
int total = 0, success = 0;
Timer timer;
@@ -712,12 +712,12 @@ void ObjectFileDB::process_tpages() {
success += statistics.successful_textures;
}
});
spdlog::info("Processed {} / {} textures {:.2f}% in {:.2f} ms", success, total,
100.f * float(success) / float(total), timer.getMs());
lg::info("Processed {} / {} textures {:.2f}% in {:.2f} ms", success, total,
100.f * float(success) / float(total), timer.getMs());
}
std::string ObjectFileDB::process_game_text() {
spdlog::info("- Finding game text...");
std::string ObjectFileDB::process_game_text_files() {
lg::info("- Finding game text...");
std::string text_string = "COMMON";
Timer timer;
int file_count = 0;
@@ -728,7 +728,7 @@ std::string ObjectFileDB::process_game_text() {
for_each_obj([&](ObjectFileData& data) {
if (data.name_in_dgo.substr(1) == text_string) {
file_count++;
auto statistics = ::process_game_text(data);
auto statistics = process_game_text(data);
string_count += statistics.total_text;
char_count += statistics.total_chars;
if (text_by_language_by_id.find(statistics.language) != text_by_language_by_id.end()) {
@@ -738,14 +738,14 @@ std::string ObjectFileDB::process_game_text() {
}
});
spdlog::info("Processed {} text files ({} strings, {} characters) in {:.2f} ms", file_count,
string_count, char_count, timer.getMs());
lg::info("Processed {} text files ({} strings, {} characters) in {:.2f} ms", file_count,
string_count, char_count, timer.getMs());
return write_game_text(text_by_language_by_id);
}
std::string ObjectFileDB::process_game_count() {
spdlog::info("- Finding game count file...");
std::string ObjectFileDB::process_game_count_file() {
lg::info("- Finding game count file...");
bool found = false;
std::string result;
@@ -753,12 +753,12 @@ std::string ObjectFileDB::process_game_count() {
if (data.name_in_dgo == "game-cnt") {
assert(!found);
found = true;
result = write_game_count(::process_game_count(data));
result = write_game_count(process_game_count(data));
}
});
if (!found) {
spdlog::warn("did not find game-cnt file");
lg::warn("did not find game-cnt file");
}
return result;
@@ -768,7 +768,7 @@ std::string ObjectFileDB::process_game_count() {
* This is the main decompiler routine which runs after we've identified functions.
*/
void ObjectFileDB::analyze_functions() {
spdlog::info("- Analyzing Functions...");
lg::info("- Analyzing Functions...");
Timer timer;
int total_functions = 0;
@@ -777,7 +777,7 @@ void ObjectFileDB::analyze_functions() {
// Step 1 - analyze the "top level" or "login" code for each object file.
// this will give us type definitions, method definitions, and function definitions...
spdlog::info(" - Processing top levels...");
lg::info(" - Processing top levels...");
timer.start();
for_each_obj([&](ObjectFileData& data) {
@@ -921,8 +921,8 @@ void ObjectFileDB::analyze_functions() {
if (func.cfg->is_fully_resolved()) {
resolved_cfg_functions++;
} else {
spdlog::warn("Function {} from {} failed cfg ir", func.guessed_name.to_string(),
data.to_unique_name());
lg::warn("Function {} from {} failed cfg ir", func.guessed_name.to_string(),
data.to_unique_name());
}
// type analysis
@@ -936,15 +936,15 @@ void ObjectFileDB::analyze_functions() {
auto kv = dts.symbol_types.find(func.guessed_name.function_name);
if (kv != dts.symbol_types.end() && kv->second.arg_count() >= 1) {
if (kv->second.base_type() != "function") {
spdlog::error("Found a function named {} but the symbol has type {}",
func.guessed_name.to_string(), kv->second.print());
lg::error("Found a function named {} but the symbol has type {}",
func.guessed_name.to_string(), kv->second.print());
assert(false);
}
// GOOD!
func.type = kv->second;
func.attempted_type_analysis = true;
attempted_type_analysis++;
// spdlog::info("Type Analysis on {} {}", func.guessed_name.to_string(),
// lg::info("Type Analysis on {} {}", func.guessed_name.to_string(),
// kv->second.print());
if (func.run_type_analysis(kv->second, dts, data.linked_data, hints)) {
successful_type_analysis++;
@@ -963,15 +963,15 @@ void ObjectFileDB::analyze_functions() {
dts.ts.lookup_method(func.guessed_name.type_name, func.guessed_name.method_id);
if (info.type.arg_count() >= 1) {
if (info.type.base_type() != "function") {
spdlog::error("Found a method named {} but the symbol has type {}",
func.guessed_name.to_string(), info.type.print());
lg::error("Found a method named {} but the symbol has type {}",
func.guessed_name.to_string(), info.type.print());
assert(false);
}
// GOOD!
func.type = info.type.substitute_for_method_call(func.guessed_name.type_name);
func.attempted_type_analysis = true;
attempted_type_analysis++;
// spdlog::info("Type Analysis on {} {}",
// lg::info("Type Analysis on {} {}",
// func.guessed_name.to_string(),
// func.type.print());
if (func.run_type_analysis(func.type, dts, data.linked_data, hints)) {
@@ -1061,29 +1061,29 @@ void ObjectFileDB::analyze_functions() {
// }
});
spdlog::info("Found {} functions ({} with no control flow)", total_functions,
total_trivial_cfg_functions);
spdlog::info("Named {}/{} functions ({:.3f}%)", total_named_functions, total_functions,
100.f * float(total_named_functions) / float(total_functions));
spdlog::info("Excluding {} asm functions", asm_funcs);
spdlog::info("Found {} basic blocks in {:.3f} ms", total_basic_blocks, timer.getMs());
spdlog::info(" {}/{} functions passed cfg analysis stage ({:.3f}%)", resolved_cfg_functions,
non_asm_funcs, 100.f * float(resolved_cfg_functions) / float(non_asm_funcs));
lg::info("Found {} functions ({} with no control flow)", total_functions,
total_trivial_cfg_functions);
lg::info("Named {}/{} functions ({:.3f}%)", total_named_functions, total_functions,
100.f * float(total_named_functions) / float(total_functions));
lg::info("Excluding {} asm functions", asm_funcs);
lg::info("Found {} basic blocks in {:.3f} ms", total_basic_blocks, timer.getMs());
lg::info(" {}/{} functions passed cfg analysis stage ({:.3f}%)", resolved_cfg_functions,
non_asm_funcs, 100.f * float(resolved_cfg_functions) / float(non_asm_funcs));
int successful_basic_ops = total_basic_ops - total_failed_basic_ops;
spdlog::info(" {}/{} basic ops converted successfully ({:.3f}%)", successful_basic_ops,
total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops));
spdlog::info(" {}/{} basic ops with reginfo ({:.3f}%)", total_reginfo_ops, total_basic_ops,
100.f * float(total_reginfo_ops) / float(total_basic_ops));
spdlog::info(" {}/{} cfgs converted to ir ({:.3f}%)", successful_cfg_irs, non_asm_funcs,
100.f * float(successful_cfg_irs) / float(non_asm_funcs));
spdlog::info(" {}/{} functions attempted type analysis ({:.2f}%)", attempted_type_analysis,
non_asm_funcs, 100.f * float(attempted_type_analysis) / float(non_asm_funcs));
spdlog::info(" {}/{} functions that attempted type analysis succeeded ({:.2f}%)",
successful_type_analysis, attempted_type_analysis,
100.f * float(successful_type_analysis) / float(attempted_type_analysis));
spdlog::info(" {}/{} functions passed type analysis ({:.2f}%)", successful_type_analysis,
non_asm_funcs, 100.f * float(successful_type_analysis) / float(non_asm_funcs));
spdlog::info(
lg::info(" {}/{} basic ops converted successfully ({:.3f}%)", successful_basic_ops,
total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops));
lg::info(" {}/{} basic ops with reginfo ({:.3f}%)", total_reginfo_ops, total_basic_ops,
100.f * float(total_reginfo_ops) / float(total_basic_ops));
lg::info(" {}/{} cfgs converted to ir ({:.3f}%)", successful_cfg_irs, non_asm_funcs,
100.f * float(successful_cfg_irs) / float(non_asm_funcs));
lg::info(" {}/{} functions attempted type analysis ({:.2f}%)", attempted_type_analysis,
non_asm_funcs, 100.f * float(attempted_type_analysis) / float(non_asm_funcs));
lg::info(" {}/{} functions that attempted type analysis succeeded ({:.2f}%)",
successful_type_analysis, attempted_type_analysis,
100.f * float(successful_type_analysis) / float(attempted_type_analysis));
lg::info(" {}/{} functions passed type analysis ({:.2f}%)", successful_type_analysis,
non_asm_funcs, 100.f * float(successful_type_analysis) / float(non_asm_funcs));
lg::info(
" {} functions were supposed to do type analysis but either failed or didn't know their "
"types.\n",
bad_type_analysis);
@@ -1097,7 +1097,7 @@ void ObjectFileDB::analyze_functions() {
}
void ObjectFileDB::analyze_expressions() {
spdlog::info("- Analyzing Expressions...");
lg::info("- Analyzing Expressions...");
Timer timer;
int attempts = 0;
int success = 0;
@@ -1107,7 +1107,7 @@ void ObjectFileDB::analyze_expressions() {
if (/*!had_failure &&*/ func.attempted_type_analysis) {
attempts++;
spdlog::info("Analyze {}", func.guessed_name.to_string());
lg::info("Analyze {}", func.guessed_name.to_string());
if (func.build_expression(data.linked_data)) {
success++;
} else {
@@ -1117,8 +1117,8 @@ void ObjectFileDB::analyze_expressions() {
}
});
spdlog::info(" {}/{} functions passed expression building ({:.2f}%)\n", success, attempts,
100.f * float(success) / float(attempts));
lg::info(" {}/{} functions passed expression building ({:.2f}%)\n", success, attempts,
100.f * float(success) / float(attempts));
}
void ObjectFileDB::dump_raw_objects(const std::string& output_dir) {
@@ -1126,4 +1126,5 @@ void ObjectFileDB::dump_raw_objects(const std::string& output_dir) {
auto dest = output_dir + "/" + data.to_unique_name();
file_util::write_binary_file(dest, data.data.data(), data.data.size());
});
}
}
} // namespace decompiler
+4 -2
View File
@@ -18,6 +18,7 @@
#include "decompiler/util/DecompilerTypeSystem.h"
#include "common/common_types.h"
namespace decompiler {
/*!
* A "record" which can be used to identify an object file.
*/
@@ -67,8 +68,8 @@ class ObjectFileDB {
void analyze_functions();
void process_tpages();
void analyze_expressions();
std::string process_game_count();
std::string process_game_text();
std::string process_game_count_file();
std::string process_game_text_files();
ObjectFileData& lookup_record(const ObjectFileRecord& rec);
DecompilerTypeSystem dts;
@@ -148,5 +149,6 @@ class ObjectFileDB {
uint32_t unique_obj_bytes = 0;
} stats;
};
} // namespace decompiler
#endif // JAK2_DISASSEMBLER_OBJECTFILEDB_H
+2
View File
@@ -2,6 +2,7 @@
#include "third-party/json.hpp"
#include "common/util/FileUtil.h"
namespace decompiler {
Config gConfig;
Config& get_config() {
@@ -101,3 +102,4 @@ void set_config(const std::string& path_to_config_file) {
}
}
}
} // namespace decompiler
+2
View File
@@ -9,6 +9,7 @@
#include <unordered_map>
#include "decompiler/Disasm/Register.h"
namespace decompiler {
struct TypeHint {
Register reg;
std::string type_name;
@@ -46,5 +47,6 @@ struct Config {
Config& get_config();
void set_config(const std::string& path_to_config_file);
} // namespace decompiler
#endif // JAK2_DISASSEMBLER_CONFIG_H
+3 -1
View File
@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "decompiler/ObjectFile/LinkedWord.h"
namespace decompiler {
class LinkedWordReader {
public:
explicit LinkedWordReader(const std::vector<LinkedWord>* words) : m_words(words) {}
@@ -37,4 +38,5 @@ class LinkedWordReader {
private:
const std::vector<LinkedWord>* m_words = nullptr;
u32 m_offset = 0;
};
};
} // namespace decompiler
+3 -1
View File
@@ -10,6 +10,7 @@
#include "game/common/str_rpc_types.h"
#include "StrFileReader.h"
namespace decompiler {
StrFileReader::StrFileReader(const std::string& file_path) {
auto data = file_util::read_binary_file(file_path);
assert(data.size() >= SECTOR_SIZE); // must have at least the header sector
@@ -178,4 +179,5 @@ std::string StrFileReader::get_full_name(const std::string& short_name) const {
assert(strcmp(iso_name_1, iso_name_2) == 0);
return result;
}
}
} // namespace decompiler
+2
View File
@@ -9,6 +9,7 @@
#include <vector>
#include "common/common_types.h"
namespace decompiler {
class StrFileReader {
public:
explicit StrFileReader(const std::string& file_path);
@@ -19,3 +20,4 @@ class StrFileReader {
private:
std::vector<std::vector<u8>> m_chunks;
};
} // namespace decompiler
+3 -1
View File
@@ -3,6 +3,7 @@
#include "game_count.h"
#include "LinkedWordReader.h"
namespace decompiler {
GameCountResult process_game_count(ObjectFileData& data) {
GameCountResult result;
auto& words = data.linked_data.words_by_seg.at(0);
@@ -37,4 +38,5 @@ std::string write_game_count(const GameCountResult& result) {
str += fmt::format("(:unknown-1 {} :unknown-2 {})\n", result.mystery_data[0],
result.mystery_data[1]);
return str;
}
}
} // namespace decompiler
+3 -1
View File
@@ -3,6 +3,7 @@
#include <vector>
#include "common/common_types.h"
namespace decompiler {
struct GameCountResult {
struct CountInfo {
s32 money_count;
@@ -15,4 +16,5 @@ struct GameCountResult {
struct ObjectFileData;
GameCountResult process_game_count(ObjectFileData& data);
std::string write_game_count(const GameCountResult& result);
std::string write_game_count(const GameCountResult& result);
} // namespace decompiler
+4 -2
View File
@@ -7,6 +7,7 @@
#include "decompiler/ObjectFile/ObjectFileDB.h"
#include "common/goos/Reader.h"
namespace decompiler {
namespace {
template <typename T>
T get_word(const LinkedWord& word) {
@@ -17,7 +18,7 @@ T get_word(const LinkedWord& word) {
return result;
}
Label get_label(ObjectFileData& data, const LinkedWord& word) {
DecompilerLabel get_label(ObjectFileData& data, const LinkedWord& word) {
assert(word.kind == LinkedWord::PTR);
return data.linked_data.labels.at(word.label_id);
}
@@ -159,4 +160,5 @@ std::string write_game_text(
}
return result;
}
}
} // namespace decompiler
+3 -1
View File
@@ -2,6 +2,7 @@
#include <string>
#include <unordered_map>
namespace decompiler {
struct ObjectFileData;
struct GameTextResult {
@@ -13,4 +14,5 @@ struct GameTextResult {
GameTextResult process_game_text(ObjectFileData& data);
std::string write_game_text(
const std::unordered_map<int, std::unordered_map<int, std::string>>& data);
const std::unordered_map<int, std::unordered_map<int, std::string>>& data);
} // namespace decompiler
+10 -8
View File
@@ -18,8 +18,9 @@
#include "tpage.h"
#include "common/versions.h"
#include "decompiler/ObjectFile/ObjectFileDB.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "third-party/fmt/core.h"
namespace decompiler {
namespace {
/*!
@@ -314,7 +315,7 @@ struct Texture {
u32 packed_info_words[9];
};
Label name_label;
DecompilerLabel name_label;
std::string name;
u32 size;
float uv_dist;
@@ -342,7 +343,7 @@ struct Texture {
* Unclear what the segments really are, maybe you could split up big tpages if needed?
*/
struct TexturePageSegment {
Label block_data_label;
DecompilerLabel block_data_label;
u32 size = 0xffffffff;
u32 dest = 0xffffffff;
std::string print_debug() const {
@@ -379,10 +380,10 @@ struct FileInfo {
* GOAL texture-page type.
*/
struct TexturePage {
Label info_label;
DecompilerLabel info_label;
FileInfo info;
Label name_label;
DecompilerLabel name_label;
std::string name;
u32 id = 0xffffffff;
@@ -392,7 +393,7 @@ struct TexturePage {
TexturePageSegment segments[3];
u32 pad[16] = {};
// data...
std::vector<Label> data;
std::vector<DecompilerLabel> data;
std::vector<Texture> textures;
std::string print_debug() const {
@@ -423,7 +424,7 @@ struct TexturePage {
* Convert a label to the offset (words) in the object segment.
* If basic is set, gives you a pointer to the beginning of the memory, if the thing is a basic.
*/
int label_to_word_offset(Label l, bool basic) {
int label_to_word_offset(DecompilerLabel l, bool basic) {
assert((l.offset & 3) == 0);
int result = l.offset / 4;
if (basic) {
@@ -441,7 +442,7 @@ bool is_type_tag(const LinkedWord& word, const std::string& type) {
return word.kind == LinkedWord::TYPE_PTR && word.symbol_name == type;
}
Label get_label(ObjectFileData& data, const LinkedWord& word) {
DecompilerLabel get_label(ObjectFileData& data, const LinkedWord& word) {
assert(word.kind == LinkedWord::PTR);
return data.linked_data.labels.at(word.label_id);
}
@@ -905,3 +906,4 @@ TPageResultStats process_tpage(ObjectFileData& data) {
}
return stats;
}
} // namespace decompiler
+3 -1
View File
@@ -1,5 +1,6 @@
#pragma once
namespace decompiler {
struct ObjectFileData;
struct TPageResultStats {
@@ -7,4 +8,5 @@ struct TPageResultStats {
int successful_textures = 0;
};
TPageResultStats process_tpage(ObjectFileData& data);
TPageResultStats process_tpage(ObjectFileData& data);
} // namespace decompiler
+10 -11
View File
@@ -2,18 +2,17 @@
#include <string>
#include <vector>
#include "ObjectFile/ObjectFileDB.h"
#include "common/log/log.h"
#include "config.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "third-party/spdlog/include/spdlog/sinks/basic_file_sink.h"
#include "common/util/FileUtil.h"
int main(int argc, char** argv) {
spdlog::info("Beginning disassembly. This may take a few minutes...");
spdlog::set_level(spdlog::level::debug);
// auto lu = spdlog::basic_logger_mt("GOAL Decompiler", "logs/decompiler.log");
// spdlog::set_default_logger(lu);
spdlog::flush_on(spdlog::level::info);
using namespace decompiler;
lg::set_file(file_util::get_file_path({"log/decompiler.txt"}));
lg::set_file_level(lg::level::info);
lg::set_stdout_level(lg::level::info);
lg::set_flush_level(lg::level::info);
lg::initialize();
file_util::init_crc();
init_opcode_info();
@@ -69,7 +68,7 @@ int main(int argc, char** argv) {
}
if (get_config().process_game_text) {
auto result = db.process_game_text();
auto result = db.process_game_text_files();
file_util::write_text_file(file_util::get_file_path({"assets", "game_text.txt"}), result);
}
@@ -78,7 +77,7 @@ int main(int argc, char** argv) {
}
if (get_config().process_game_count) {
auto result = db.process_game_count();
auto result = db.process_game_count_file();
file_util::write_text_file(file_util::get_file_path({"assets", "game_count.txt"}), result);
}
@@ -98,6 +97,6 @@ int main(int argc, char** argv) {
file_util::write_text_file(file_util::combine_path(out_folder, "all-syms.gc"),
db.dts.dump_symbol_types());
spdlog::info("Disassembly has completed successfully.");
lg::info("Disassembly has completed successfully.");
return 0;
}
+11 -9
View File
@@ -2,9 +2,10 @@
#include "common/goos/Reader.h"
#include "common/type_system/deftype.h"
#include "decompiler/Disasm/Register.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
#include "TP_Type.h"
namespace decompiler {
DecompilerTypeSystem::DecompilerTypeSystem() {
ts.add_builtin_types();
}
@@ -105,9 +106,9 @@ void DecompilerTypeSystem::add_type_flags(const std::string& name, u64 flags) {
auto kv = type_flags.find(name);
if (kv != type_flags.end()) {
if (kv->second != flags) {
spdlog::warn("duplicated type flags for {}, was 0x{:x}, now 0x{:x}", name.c_str(), kv->second,
flags);
spdlog::warn("duplicated type flags that are inconsistent!");
lg::warn("duplicated type flags for {}, was 0x{:x}, now 0x{:x}", name.c_str(), kv->second,
flags);
lg::warn("duplicated type flags that are inconsistent!");
}
}
type_flags[name] = flags;
@@ -117,8 +118,8 @@ void DecompilerTypeSystem::add_type_parent(const std::string& child, const std::
auto kv = type_parents.find(child);
if (kv != type_parents.end()) {
if (kv->second != parent) {
spdlog::warn("duplicated type parents for {} was {} now {}", child.c_str(),
kv->second.c_str(), parent.c_str());
lg::warn("duplicated type parents for {} was {} now {}", child.c_str(), kv->second.c_str(),
parent.c_str());
throw std::runtime_error("duplicated type parents that are inconsistent!");
}
}
@@ -151,8 +152,8 @@ void DecompilerTypeSystem::add_symbol(const std::string& name, const TypeSpec& t
} else {
if (ts.typecheck(type_spec, skv->second, "", false, false)) {
} else {
spdlog::warn("Attempting to redefine type of symbol {} from {} to {}\n", name,
skv->second.print(), type_spec.print());
lg::warn("Attempting to redefine type of symbol {} from {} to {}\n", name,
skv->second.print(), type_spec.print());
throw std::runtime_error("Type redefinition");
}
}
@@ -322,4 +323,5 @@ int DecompilerTypeSystem::get_format_arg_count(const TP_Type& type) {
} else {
return type.get_format_string_arg_count();
}
}
}
} // namespace decompiler
+3 -1
View File
@@ -5,7 +5,8 @@
#include "decompiler/Disasm/Register.h"
#include "common/goos/Reader.h"
struct TP_Type;
namespace decompiler {
class TP_Type;
struct TypeState;
class DecompilerTypeSystem {
@@ -54,5 +55,6 @@ class DecompilerTypeSystem {
private:
goos::Reader m_reader;
};
} // namespace decompiler
#endif // JAK_DECOMPILERTYPESYSTEM_H
+3 -1
View File
@@ -1,5 +1,6 @@
#pragma once
namespace decompiler {
template <typename T>
struct MatchParam {
MatchParam() { is_wildcard = true; }
@@ -15,4 +16,5 @@ struct MatchParam {
bool operator==(const T& other) const { return is_wildcard || (value == other); }
bool operator!=(const T& other) const { return !(*this == other); }
};
};
} // namespace decompiler
+2
View File
@@ -1,6 +1,7 @@
#include "TP_Type.h"
#include "third-party/fmt/core.h"
namespace decompiler {
std::string TypeState::print_gpr_masked(u32 mask) const {
std::string result;
for (int i = 0; i < 32; i++) {
@@ -117,3 +118,4 @@ TypeSpec TP_Type::typespec() const {
assert(false);
}
}
} // namespace decompiler
+3 -78
View File
@@ -5,83 +5,7 @@
#include "common/common_types.h"
#include "decompiler/Disasm/Register.h"
// struct TP_Type {
// enum Kind {
// OBJECT_OF_TYPE,
// TYPE_OBJECT,
// FALSE,
// NONE,
// PRODUCT,
// OBJ_PLUS_PRODUCT,
// PARTIAL_METHOD_TABLE_ACCESS, // type + method_number * 4
// METHOD_NEW_OF_OBJECT,
// STRING
// } kind = NONE;
// // in the case that we are type_object, just store the type name in a single arg ts.
// TypeSpec ts;
// int multiplier;
// std::string str_data;
//
// TP_Type() = default;
// explicit TP_Type(const TypeSpec& _ts) {
// kind = OBJECT_OF_TYPE;
// ts = _ts;
// }
//
// TP_Type simplify() const;
// std::string print() const;
//
// bool is_object_of_type() const { return kind == TYPE_OBJECT || ts == TypeSpec("type"); }
//
// TypeSpec as_typespec() const {
// switch (kind) {
// case OBJECT_OF_TYPE:
// return ts;
// case TYPE_OBJECT:
// return TypeSpec("type");
// case FALSE:
// return TypeSpec("symbol");
// case NONE:
// return TypeSpec("none");
// case PRODUCT:
// case METHOD_NEW_OF_OBJECT:
// return ts;
// default:
// assert(false);
// }
// }
//
// static TP_Type make_partial_method_table_access(TypeSpec ts) {
// TP_Type result;
// result.kind = PARTIAL_METHOD_TABLE_ACCESS;
// result.ts = std::move(ts);
// return result;
// }
//
// static TP_Type make_type_object(const std::string& name) {
// TP_Type result;
// result.kind = TYPE_OBJECT;
// result.ts = TypeSpec(name);
// return result;
// }
//
// static TP_Type make_string_object(const std::string& str) {
// TP_Type result;
// result.kind = STRING;
// result.ts = TypeSpec("string");
// result.str_data = str;
// return result;
// }
//
// static TP_Type make_none() {
// TP_Type result;
// result.kind = NONE;
// return result;
// }
//
// bool operator==(const TP_Type& other) const;
//};
namespace decompiler {
/*!
* A TP_Type is a specialized typespec used in the type propagation algorithm.
* It is basically a normal typespec plus some optional information.
@@ -267,4 +191,5 @@ struct TypeState {
assert(false);
}
}
};
};
} // namespace decompiler
+7 -14
View File
@@ -69,24 +69,17 @@ set(RUNTIME_SOURCE
overlord/ssound.cpp
overlord/stream.cpp)
# the runtime should be built without any static/dynamic libraries.
#add_executable(gk ${RUNTIME_SOURCE} main.cpp)
# we also build a runtime library for testing. This version is likely unable to call GOAL code correctly, but
# can be used to test other things.
# we build the runtime as a static library.
add_library(runtime ${RUNTIME_SOURCE})
add_executable(gk main.cpp)
target_link_libraries(runtime common fmt)
IF (WIN32)
# set stuff for windows
target_link_libraries(runtime mman cross_sockets common_util spdlog cross_os_debug)
target_link_libraries(gk cross_sockets mman common_util runtime spdlog cross_os_debug)
target_link_libraries(runtime mman)
ELSE()
# set stuff for other systems
target_link_libraries(runtime pthread cross_sockets common_util spdlog cross_os_debug)
target_link_libraries(gk cross_sockets pthread common_util runtime spdlog cross_os_debug)
target_link_libraries(runtime pthread)
ENDIF()
add_executable(gk main.cpp)
target_link_libraries(gk runtime)
+22 -2
View File
@@ -187,15 +187,25 @@ _call_goal_asm_win32:
push r13 ; 88
push r14 ; 96
push r15 ; 104
sub rsp, 16
movups [rsp], xmm6
sub rsp, 16
movups [rsp], xmm7
mov rdi, rcx ;; rdi is GOAL first argument, rcx is windows first argument
mov rsi, rdx ;; rsi is GOAL second argument, rdx is windows second argument
mov rdx, r8 ;; rdx is GOAL third argument, r8 is windows third argument
mov r13, r9 ;; r13 is GOAL fp, r9 is windows fourth argument
mov r15, [rsp + 152] ;; symbol table
mov r14, [rsp + 144] ;; offset
mov r15, [rsp + 184] ;; symbol table
mov r14, [rsp + 176] ;; offset
call r13
movups xmm7, [rsp]
add rsp, 16
movups xmm6, [rsp]
add rsp, 16
pop r15
pop r14
@@ -234,6 +244,11 @@ _call_goal_on_stack_asm_win32:
push r14 ; 96
push r15 ; 104
sub rsp, 16
movups [rsp], xmm6
sub rsp, 16
movups [rsp], xmm7
;; stack swap
mov rsi, rsp
mov rsp, rcx
@@ -249,6 +264,11 @@ _call_goal_on_stack_asm_win32:
pop rsi
mov rsp, rsi
movups xmm7, [rsp]
add rsp, 16
movups xmm6, [rsp]
add rsp, 16
pop r15
pop r14
pop r13
+2
View File
@@ -6,6 +6,8 @@
#include <cstring>
#include <chrono>
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include "common/common_types.h"
+5 -5
View File
@@ -17,7 +17,7 @@
#include "game/common/loader_rpc_types.h"
#include "game/common/play_rpc_types.h"
#include "game/common/str_rpc_types.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
using namespace ee;
@@ -192,8 +192,8 @@ void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8>
// file name
strcpy(sMsg[msgID].name, name);
spdlog::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset,
buffer2.offset, currentHeap.offset);
lg::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset,
buffer2.offset, currentHeap.offset);
// this RPC will return once we have loaded the first object file.
// but we call async, so we don't block here.
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_FNO, true, mess, sizeof(RPC_Dgo_Cmd), mess,
@@ -315,7 +315,7 @@ void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size)
* This does not use the mutli-threaded linker and will block until the entire file is done.e
*/
void load_and_link_dgo_from_c(const char* name, Ptr<kheapinfo> heap, u32 linkFlag, s32 bufferSize) {
spdlog::debug("[Load and Link DGO From C] {}", name);
lg::debug("[Load and Link DGO From C] {}", name);
u32 oldShowStall = sShowStallMsg;
// remember where the heap top point is so we can clear temporary allocations
@@ -364,7 +364,7 @@ void load_and_link_dgo_from_c(const char* name, Ptr<kheapinfo> heap, u32 linkFla
char objName[64];
strcpy(objName, (dgoObj + 4).cast<char>().c()); // name from dgo object header
spdlog::debug("[link and exec] {} {}", objName, lastObjectLoaded);
lg::debug("[link and exec] {} {}", objName, lastObjectLoaded);
link_and_exec(obj, objName, objSize, heap, linkFlag); // link now!
// inform IOP we are done
+22 -22
View File
@@ -25,7 +25,7 @@
#include "game/sce/libcdvd_ee.h"
#include "game/sce/stubs.h"
#include "common/symbols.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
using namespace ee;
/*!
@@ -148,13 +148,13 @@ void InitParms(int argc, const char* const* argv) {
* DONE, EXACT
*/
void InitCD() {
spdlog::info("Initializing CD drive. This may take a while...");
lg::info("Initializing CD drive. This may take a while...");
sceCdInit(SCECdINIT);
sceCdMmode(SCECdDVD);
while (sceCdDiskReady(0) == SCECdNotReady) {
spdlog::debug("Drive not ready... insert a disk!");
lg::debug("Drive not ready... insert a disk!");
}
spdlog::debug("Disk type {}\n", sceCdGetDiskType());
lg::debug("Disk type {}\n", sceCdGetDiskType());
}
/*!
@@ -172,22 +172,22 @@ void InitIOP() {
if (!reboot) {
// reboot with development IOP kernel
spdlog::debug("Rebooting IOP...");
lg::debug("Rebooting IOP...");
while (!sceSifRebootIop("host0:/usr/local/sce/iop/modules/ioprp221.img")) {
spdlog::debug("Failed, retrying");
lg::debug("Failed, retrying");
}
while (!sceSifSyncIop()) {
spdlog::debug("Syncing...");
lg::debug("Syncing...");
}
} else {
// reboot with IOP kernel off of the disk
// reboot with development IOP kernel
spdlog::debug("Rebooting IOP...");
lg::debug("Rebooting IOP...");
while (!sceSifRebootIop("cdrom0:\\DRIVERS\\IOPRP221.IMG;1")) {
spdlog::debug("Failed, retrying");
lg::debug("Failed, retrying");
}
while (!sceSifSyncIop()) {
spdlog::debug("Syncing...");
lg::debug("Syncing...");
}
}
@@ -238,7 +238,7 @@ void InitIOP() {
sceSifLoadModule("host0:/usr/home/src/989snd10/iop/989ERR.IRX", 0, nullptr);
spdlog::debug("Initializing CD library...");
lg::debug("Initializing CD library...");
auto rv = sceSifLoadModule("host0:binee/overlord.irx", cmd + len + 1 - overlord_boot_command,
overlord_boot_command);
if (rv < 0) {
@@ -270,7 +270,7 @@ void InitIOP() {
MsgErr("loading 989snd.irx failed\n");
}
spdlog::debug("Initializing CD library in ISO_CD mode...");
lg::debug("Initializing CD library in ISO_CD mode...");
auto rv = sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\OVERLORD.IRX;1",
cmd + len + 1 - overlord_boot_command, overlord_boot_command);
if (rv < 0) {
@@ -281,7 +281,7 @@ void InitIOP() {
if (rv < 0) {
MsgErr("MC driver init failed %d\n", rv);
} else {
spdlog::info("InitIOP OK");
lg::info("InitIOP OK");
}
}
@@ -302,8 +302,8 @@ int InitMachine() {
// initialize the global heap
u32 global_heap_size = GLOBAL_HEAP_END - HEAP_START;
float size_mb = ((float)global_heap_size) / (float)(1 << 20);
spdlog::info("gkernel: global heap 0x{:08x} to 0x{:08x} (size {:.3f} MB)", HEAP_START,
GLOBAL_HEAP_END, size_mb);
lg::info("gkernel: global heap 0x{:08x} to 0x{:08x} (size {:.3f} MB)", HEAP_START,
GLOBAL_HEAP_END, size_mb);
kinitheap(kglobalheap, Ptr<u8>(HEAP_START), global_heap_size);
// initialize the debug heap, if appropriate
@@ -312,8 +312,8 @@ int InitMachine() {
kinitheap(kdebugheap, Ptr<u8>(DEBUG_HEAP_START), debug_heap_size);
float debug_size_mb = ((float)debug_heap_size) / (float)(1 << 20);
float gap_size_mb = ((float)DEBUG_HEAP_START - GLOBAL_HEAP_END) / (float)(1 << 20);
spdlog::info("gkernel: debug heap 0x{:08x} to 0x{:08x} (size {:.3f} MB, gap {:.3f} MB)",
DEBUG_HEAP_START, debug_heap_end, debug_size_mb, gap_size_mb);
lg::info("gkernel: debug heap 0x{:08x} to 0x{:08x} (size {:.3f} MB, gap {:.3f} MB)",
DEBUG_HEAP_START, debug_heap_end, debug_size_mb, gap_size_mb);
} else {
// if no debug, we make the kheapinfo structure NULL so GOAL knows not to use it.
kdebugheap.offset = 0;
@@ -338,9 +338,9 @@ int InitMachine() {
InitGoalProto();
}
spdlog::info("InitSound");
lg::info("InitSound");
InitSound(); // do nothing!
spdlog::info("InitRPC");
lg::info("InitRPC");
InitRPC(); // connect to IOP
reset_output(); // reset output buffers
clear_print();
@@ -350,9 +350,9 @@ int InitMachine() {
return goal_status;
}
spdlog::info("InitListenerConnect");
lg::info("InitListenerConnect");
InitListenerConnect();
spdlog::info("InitCheckListener");
lg::info("InitCheckListener");
InitCheckListener();
Msg(6, "kernel: machine started\n");
return 0;
@@ -619,7 +619,7 @@ void InitMachineScheme() {
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE).cast<u32>()),
make_string_from_c("common"), kernel_packages->value);
spdlog::info("calling fake play~");
lg::info("calling fake play~");
call_goal_function_by_name("play");
}
}
+3 -3
View File
@@ -349,7 +349,7 @@ s32 cvt_float(float x, s32 precision, s32* lead_char, char* buff_start, char* bu
value = (char)rounder;
} else if (!(ru32 >> 31)) { // sign bit
value = 0;
assert(false); // not sure what happens here.
// assert(false); // not sure what happens here.
} else {
value = -1; // happens on NaN's
}
@@ -396,7 +396,7 @@ s32 cvt_float(float x, s32 precision, s32* lead_char, char* buff_start, char* bu
value = (char)next_int;
} else if (!(ru32 >> 0x1f)) {
value = 0;
assert(false); // not sure what happens here.
// assert(false); // not sure what happens here.
} else {
value = -1; // happens on NaN's
}
@@ -1085,4 +1085,4 @@ s32 format_impl(uint64_t* args) {
assert(false); // ??????
return 7;
}
}
+3 -5
View File
@@ -20,7 +20,7 @@
#include "common/symbols.h"
#include "common/versions.h"
#include "common/goal_constants.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
//! Controls link mode when EnableMethodSet = 0, MasterDebug = 1, DiskBoot = 0. Will enable a
//! warning message if EnableMethodSet = 1
@@ -1979,10 +1979,8 @@ s32 InitHeapAndSymbol() {
(kernel_version >> 3) & 0xffff);
return -1;
} else {
spdlog::info("Got correct kernel version {}.{}", kernel_version >> 0x13,
(kernel_version >> 3) & 0xffff);
// printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13,
// (kernel_version >> 3) & 0xffff);
lg::info("Got correct kernel version {}.{}", kernel_version >> 0x13,
(kernel_version >> 3) & 0xffff);
}
}
+11 -14
View File
@@ -6,23 +6,21 @@
#include <string>
#include "runtime.h"
#include "common/versions.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "third-party/spdlog/include/spdlog/sinks/basic_file_sink.h"
#include "third-party/spdlog/include/spdlog/sinks/stdout_color_sinks.h"
#include "common/log/log.h"
#include "common/util/FileUtil.h"
void setup_logging(bool verbose) {
spdlog::set_level(spdlog::level::debug);
lg::set_file(file_util::get_file_path({"log/game.txt"}));
if (verbose) {
auto game_logger = spdlog::stdout_color_mt("GOAL Runtime");
spdlog::set_default_logger(game_logger);
spdlog::flush_on(spdlog::level::info);
spdlog::info("Verbose logging enabled");
lg::set_file_level(lg::level::info);
lg::set_stdout_level(lg::level::info);
lg::set_flush_level(lg::level::info);
} else {
auto game_logger = spdlog::basic_logger_mt("GOAL Runtime", "logs/runtime.log");
spdlog::set_default_logger(game_logger);
spdlog::flush_on(spdlog::level::debug);
printf("OpenGOAL Runtime %d.%d\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
lg::set_file_level(lg::level::warn);
lg::set_stdout_level(lg::level::warn);
lg::set_flush_level(lg::level::warn);
}
lg::initialize();
}
int main(int argc, char** argv) {
@@ -38,8 +36,7 @@ int main(int argc, char** argv) {
while (true) {
// run the runtime in a loop so we can reset the game and have it restart cleanly
spdlog::info("OpenGOAL Runtime {}.{}", versions::GOAL_VERSION_MAJOR,
versions::GOAL_VERSION_MINOR);
lg::info("OpenGOAL Runtime {}.{}", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
if (exec_runtime(argc, argv) == 2) {
return 0;
+8 -8
View File
@@ -16,7 +16,7 @@
#include "isocommon.h"
#include "overlord.h"
#include "common/util/FileUtil.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
using namespace iop;
@@ -199,7 +199,7 @@ uint32_t FS_GetLength(FileRecord* fr) {
* This is an ISO FS API Function
*/
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
spdlog::debug("[OVERLORD] FS Open {}", fr->name);
lg::debug("[OVERLORD] FS Open {}", fr->name);
LoadStackEntry* selected = nullptr;
// find first unused spot on load stack.
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
@@ -213,7 +213,7 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
return selected;
}
}
spdlog::warn("[OVERLORD] Failed to FS Open {}", fr->name);
lg::warn("[OVERLORD] Failed to FS Open {}", fr->name);
ExitIOP();
return nullptr;
}
@@ -224,7 +224,7 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
* This is an ISO FS API Function
*/
LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
spdlog::debug("[OVERLORD] FS_OpenWad {}", fr->name);
lg::debug("[OVERLORD] FS_OpenWad {}", fr->name);
LoadStackEntry* selected = nullptr;
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
@@ -234,7 +234,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
return selected;
}
}
spdlog::warn("[OVERLORD] Failed to FS_OpenWad {}", fr->name);
lg::warn("[OVERLORD] Failed to FS_OpenWad {}", fr->name);
ExitIOP();
return nullptr;
}
@@ -244,7 +244,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
* This is an ISO FS API Function
*/
void FS_Close(LoadStackEntry* fd) {
spdlog::debug("[OVERLORD] FS_Close {}", fd->fr->name);
lg::debug("[OVERLORD] FS_Close {}", fd->fr->name);
// close the FD
fd->fr = nullptr;
@@ -263,7 +263,7 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
int32_t real_size = len;
if (len < 0) {
// not sure what this is about...
spdlog::warn("[OVERLORD ISO CD] Negative length warning!");
lg::warn("[OVERLORD ISO CD] Negative length warning!");
real_size = len + 0x7ff;
}
@@ -274,7 +274,7 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
const char* path = get_file_path(fd->fr);
FILE* fp = fopen(path, "rb");
if (!fp) {
spdlog::error("[OVERLORD] fake iso could not open the file \"{}\"", path);
lg::error("[OVERLORD] fake iso could not open the file \"{}\"", path);
}
assert(fp);
fseek(fp, 0, SEEK_END);
+3 -3
View File
@@ -4,7 +4,7 @@
* This is a huge mess
*/
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
#include <assert.h>
#include <cstring>
#include <cstdio>
@@ -496,8 +496,8 @@ u32 RunDGOStateMachine(IsoMessage* _cmd, IsoBufferHeader* buffer) {
// printf("[Overlord DGO] Got DGO file header for %s with %d objects\n",
// cmd->dgo_header.name,
// cmd->dgo_header.object_count); // added
spdlog::info("[Overlord DGO] Got DGO file header for {} with {} objects",
cmd->dgo_header.name, cmd->dgo_header.object_count);
lg::info("[Overlord DGO] Got DGO file header for {} with {} objects",
cmd->dgo_header.name, cmd->dgo_header.object_count);
cmd->bytes_processed = 0;
cmd->objects_loaded = 0;
if (cmd->dgo_header.object_count == 1) {
+4 -6
View File
@@ -1,6 +1,6 @@
#include "iso_api.h"
#include "game/sce/iop.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "common/log/log.h"
using namespace iop;
@@ -8,8 +8,7 @@ using namespace iop;
* Load a File to IOP memory (blocking)
*/
s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
// printf("[OVERLORD] LoadISOFileToIOP %s, %d/%d bytes\n", file->name, length, file->size);
spdlog::debug("[OVERLORD] LoadISOFileToIOP {}, {}/{} bytes", file->name, length, file->size);
lg::debug("[OVERLORD] LoadISOFileToIOP {}, {}/{} bytes", file->name, length, file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_IOP_CMD_ID;
cmd.messagebox_to_reply = 0;
@@ -31,8 +30,7 @@ s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
* Load a File to IOP memory (blocking)
*/
s32 LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
// printf("[OVERLORD] LoadISOFileToEE %s, %d/%d bytes\n", file->name, length, file->size);
spdlog::debug("[OVERLORD] LoadISOFileToEE {}, {}/{} bytes", file->name, length, file->size);
lg::debug("[OVERLORD] LoadISOFileToEE {}, {}/{} bytes", file->name, length, file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_EE_CMD_ID;
cmd.messagebox_to_reply = 0;
@@ -51,7 +49,7 @@ s32 LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
}
s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length, uint32_t offset) {
spdlog::debug("[OVERLORD] LoadISOFileChunkToEE {} : {} offset {}\n", file->name, length, offset);
lg::debug("[OVERLORD] LoadISOFileChunkToEE {} : {} offset {}\n", file->name, length, offset);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_EE_OFFSET_CMD_ID;
cmd.messagebox_to_reply = 0;
+21 -20
View File
@@ -11,6 +11,7 @@
#include "overlord.h"
#include "soundcommon.h"
#include "srpc.h"
#include "common/log/log.h"
// iso_cd is an implementation of the IsoFs API for loading files from a CD/DVD with an ISO and/or
// DUP filesystem.
@@ -231,7 +232,7 @@ u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) {
while (lsize > 0) {
// ISO low-level read
if (!ReadSectorsNow(lsector, 1, buffer)) {
spdlog::info("[OVERLORD ISO CD] Failed to read sector in ReadDirectory!");
lg::info("[OVERLORD ISO CD] Failed to read sector in ReadDirectory!");
return 0;
}
u8* lbuffer = buffer;
@@ -264,7 +265,7 @@ u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) {
}
} else {
if (sNumFiles == MAX_ISO_FILES) {
spdlog::info("[OVERLORD ISO CD] There are too many files on the disc!");
lg::info("[OVERLORD ISO CD] There are too many files on the disc!");
return 0;
}
@@ -283,7 +284,7 @@ u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) {
lsize -= 0x800;
}
} else {
spdlog::info("[OVERLORD ISO CD] ReadDirectory ran out of sector buffers!");
lg::info("[OVERLORD ISO CD] ReadDirectory ran out of sector buffers!");
}
return 1;
}
@@ -354,7 +355,7 @@ void LoadMusicTweaks(u8* buffer) {
FileRecord* fr = FS_FindIN(iso_name);
if (!fr || !ReadSectorsNow(fr->location, 1, buffer)) {
*(s32*)gMusicTweakInfo = 0;
spdlog::warn("[OVERLORD ISO CD] Failed to load music tweaks!");
lg::warn("[OVERLORD ISO CD] Failed to load music tweaks!");
} else {
memcpy(gMusicTweakInfo, buffer, MUSIC_TWEAK_SIZE);
}
@@ -373,7 +374,7 @@ void LoadDiscID() {
MakeISOName(iso_name, "DISK_ID.DIZ");
FileRecord* fr = FS_FindIN(iso_name);
if (!fr) {
spdlog::warn(
lg::warn(
"[OVERLORD ISO CD] LoadDiscID failed to find DISK_ID.DIZ, using sector 0x400 instead!");
CD_ID_SectorNum = 0x400;
} else {
@@ -385,7 +386,7 @@ void LoadDiscID() {
for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) {
CD_ID_SectorSum += CD_ID_Sector[i];
}
spdlog::info("[OVERLORD] DISK_ID.DIZ OK 0x{:x}\n", CD_ID_SectorSum);
lg::info("[OVERLORD] DISK_ID.DIZ OK 0x{:x}\n", CD_ID_SectorSum);
}
/*!
@@ -416,13 +417,13 @@ void SetRealSector() {
} else {
// it's a duplicated file, and duplicate read is enabled, so get the area 2 sector.
_real_sector = _sector + sAreaDiff;
spdlog::warn("[OVERLORD] Warning, adjusting real sector in SetRealSector");
lg::warn("[OVERLORD] Warning, adjusting real sector in SetRealSector");
}
// we suspect the game is pirated, load the wrong sector.
if (pirated) {
_real_sector += 3;
spdlog::warn("Pirated!");
lg::warn("Pirated!");
}
}
@@ -466,13 +467,13 @@ int FS_Init(u8* buffer) {
// read primary volume descriptor into buffer
if (!ReadSectorsNow(0x10, 1, sSecBuffer[0])) {
spdlog::warn("[OVERLORD ISO CD] Failed to read primary volume descriptor");
lg::warn("[OVERLORD ISO CD] Failed to read primary volume descriptor");
return 1;
}
// check volume descriptor identifier
if (memcmp(sSecBuffer[0] + 1, "CD001", 5)) {
spdlog::warn("[OVERLORD ISO CD] Got the wrong volume descriptor identifier");
lg::warn("[OVERLORD ISO CD] Got the wrong volume descriptor identifier");
char* cptr = (char*)sSecBuffer[0] + 1;
printf("%c%c%c%c%c\n", cptr[0], cptr[1], cptr[2], cptr[3], cptr[4]);
return 1;
@@ -482,7 +483,7 @@ int FS_Init(u8* buffer) {
uint32_t path_table_sector = ReadU32(sSecBuffer[0] + 0x8c);
if (!ReadSectorsNow(path_table_sector, 1, sSecBuffer[0])) {
spdlog::warn("[OVERLORD ISO CD] Failed to read path");
lg::warn("[OVERLORD ISO CD] Failed to read path");
return 1;
}
@@ -490,14 +491,14 @@ int FS_Init(u8* buffer) {
uint32_t path_table_extent = ReadU32(sSecBuffer[0] + 2);
if (!ReadSectorsNow(path_table_extent, 1, sSecBuffer[0])) {
spdlog::warn("[OVERLORD ISO CD] Failed to read path table extent");
lg::warn("[OVERLORD ISO CD] Failed to read path table extent");
}
// read root directory
add_files = true;
uint32_t dir_size = ReadU32(sSecBuffer[0] + 10);
if (!ReadDirectory(path_table_extent, dir_size, 0)) {
spdlog::warn("[OVERLORD ISO CD] Failed to ReadDirectory");
lg::warn("[OVERLORD ISO CD] Failed to ReadDirectory");
return 1;
}
@@ -520,7 +521,7 @@ int FS_Init(u8* buffer) {
}
return 0;
} else {
spdlog::warn("[OVERLORD ISO CD] Bad Media Type!");
lg::warn("[OVERLORD ISO CD] Bad Media Type!");
return 1;
}
}
@@ -567,7 +568,7 @@ FileRecord* FS_FindIN(const char* iso_name) {
}
// we didn't get 1 GB of files, you're a pirate.
spdlog::warn("Pirated!");
lg::warn("Pirated!");
}
}
@@ -585,7 +586,7 @@ uint32_t FS_GetLength(FileRecord* fr) {
* This is an ISO FS API Function
*/
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
spdlog::info("[OVERLORD] FS Open {}", fr->name);
lg::info("[OVERLORD] FS Open {}", fr->name);
LoadStackEntry* selected = nullptr;
// find first unused spot on load stack.
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
@@ -599,7 +600,7 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
return selected;
}
}
spdlog::warn("[OVERLORD ISO CD] Failed to FS_Open {}", fr->name);
lg::warn("[OVERLORD ISO CD] Failed to FS_Open {}", fr->name);
ExitIOP();
return nullptr;
}
@@ -620,7 +621,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
return selected;
}
}
spdlog::warn("[OVERLORD ISO CD] Failed to use FS_OpenWad {}", fr->name);
lg::warn("[OVERLORD ISO CD] Failed to use FS_OpenWad {}", fr->name);
ExitIOP();
return nullptr;
}
@@ -630,7 +631,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
* This is an ISO FS API Function
*/
void FS_Close(LoadStackEntry* fd) {
spdlog::info("[OVERLORD] FS Close {}", fd->fr->name);
lg::info("[OVERLORD] FS Close {}", fd->fr->name);
if (fd == sReadInfo) {
// the file is currently being read, so lets try to finish out the read, if possible.
int count = 0;
@@ -663,7 +664,7 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
int32_t real_size = len;
if (len < 0) {
// not sure what this is about...
spdlog::warn("[OVERLORD ISO CD] Negative length warning!");
lg::warn("[OVERLORD ISO CD] Negative length warning!");
real_size = len + 0x7ff;
}
_sectors = real_size >> 11;
+5 -4
View File
@@ -1,6 +1,7 @@
#include <cstring>
#include <cstdio>
#include <cassert>
#include "common/log/log.h"
#include "game/sce/iop.h"
#include "iso_queue.h"
#include "isocommon.h"
@@ -179,11 +180,11 @@ u32 QueueMessage(IsoMessage* cmd, int32_t priority, const char* name) {
gPriStack[priority].cmds[gPriStack[priority].n] = cmd;
gPriStack[priority].names[gPriStack[priority].n] = name;
gPriStack[priority].n++;
spdlog::debug("[OVERLORD] Queue {} ({}/{}), {}", priority, gPriStack[priority].n,
PRI_STACK_LENGTH, gPriStack[priority].names[gPriStack[priority].n - 1].c_str());
lg::debug("[OVERLORD] Queue {} ({}/{}), {}", priority, gPriStack[priority].n, PRI_STACK_LENGTH,
gPriStack[priority].names[gPriStack[priority].n - 1].c_str());
DisplayQueue();
} else {
spdlog::warn("[OVERLORD ISO QUEUE] Failed to queue!");
lg::warn("[OVERLORD ISO QUEUE] Failed to queue!");
cmd->status = CMD_STATUS_FAILED_TO_QUEUE;
ReturnMessage(cmd);
}
@@ -209,7 +210,7 @@ void UnqueueMessage(IsoMessage* cmd) {
}
}
}
spdlog::warn("[OVERLORD ISO QUEUE] Failed to unqueue!");
lg::warn("[OVERLORD ISO QUEUE] Failed to unqueue!");
found:
assert(gPriStack[pri].cmds[idx] == cmd);
+1
View File
@@ -5,6 +5,7 @@
*/
#include <cassert>
#include <cstring>
#include "stream.h"
#include "game/sce/iop.h"
#include "game/common/str_rpc_types.h"
+17 -16
View File
@@ -16,6 +16,7 @@
#include <cstring>
#include <thread>
#include "common/log/log.h"
#include "runtime.h"
#include "system/SystemThread.h"
#include "sce/libcdvd_ee.h"
@@ -72,21 +73,21 @@ void deci2_runner(SystemThreadInterface& iface) {
iface.initialization_complete();
// in our own thread, wait for the EE to register the first protocol driver
spdlog::debug("[DECI2] Waiting for EE to register protos");
lg::debug("[DECI2] Waiting for EE to register protos");
server.wait_for_protos_ready();
// then allow the server to accept connections
if (!server.init()) {
assert(false);
}
spdlog::debug("[DECI2] Waiting for listener...");
// spdlog::debug("[DECI2] Waiting for listener..."); --> disabled temporarily, some weird race
lg::debug("[DECI2] Waiting for listener...");
// lg::debug("[DECI2] Waiting for listener..."); --> disabled temporarily, some weird race
// condition?
bool saw_listener = false;
while (!iface.get_want_exit()) {
if (server.check_for_listener()) {
if (!saw_listener) {
spdlog::debug("[DECI2] Connected!");
lg::debug("[DECI2] Connected!");
}
saw_listener = true;
// we have a listener, run!
@@ -116,19 +117,19 @@ void ee_runner(SystemThreadInterface& iface) {
}
if (g_ee_main_mem == (u8*)(-1)) {
spdlog::debug("Failed to initialize main memory! {}", strerror(errno));
lg::debug("Failed to initialize main memory! {}", strerror(errno));
iface.initialization_complete();
return;
}
spdlog::debug("Main memory mapped at 0x{:016x}", (u64)(g_ee_main_mem));
spdlog::debug("Main memory size 0x{:x} bytes ({:.3f} MB)", EE_MAIN_MEM_SIZE,
(double)EE_MAIN_MEM_SIZE / (1 << 20));
lg::debug("Main memory mapped at 0x{:016x}", (u64)(g_ee_main_mem));
lg::debug("Main memory size 0x{:x} bytes ({:.3f} MB)", EE_MAIN_MEM_SIZE,
(double)EE_MAIN_MEM_SIZE / (1 << 20));
spdlog::debug("[EE] Initialization complete!");
lg::debug("[EE] Initialization complete!");
iface.initialization_complete();
spdlog::debug("[EE] Run!");
lg::debug("[EE] Run!");
memset((void*)g_ee_main_mem, 0, EE_MAIN_MEM_SIZE);
// prevent access to the first 1 MB of memory.
@@ -153,7 +154,7 @@ void ee_runner(SystemThreadInterface& iface) {
xdbg::allow_debugging();
goal_main(g_argc, g_argv);
spdlog::debug("[EE] Done!");
lg::debug("[EE] Done!");
// // kill the IOP todo
iop::LIBRARY_kill();
@@ -169,7 +170,7 @@ void ee_runner(SystemThreadInterface& iface) {
*/
void iop_runner(SystemThreadInterface& iface) {
IOP iop;
spdlog::debug("[IOP] Restart!");
lg::debug("[IOP] Restart!");
iop.reset_allocator();
ee::LIBRARY_sceSif_register(&iop);
iop::LIBRARY_register(&iop);
@@ -192,12 +193,12 @@ void iop_runner(SystemThreadInterface& iface) {
iface.initialization_complete();
spdlog::debug("[IOP] Wait for OVERLORD to start...");
lg::debug("[IOP] Wait for OVERLORD to start...");
iop.wait_for_overlord_start_cmd();
if (iop.status == IOP_OVERLORD_INIT) {
spdlog::debug("[IOP] Run!");
lg::debug("[IOP] Run!");
} else {
spdlog::debug("[IOP] Shutdown!");
lg::debug("[IOP] Shutdown!");
return;
}
@@ -259,6 +260,6 @@ u32 exec_runtime(int argc, char** argv) {
// join and exit
tm.join();
spdlog::info("GOAL Runtime Shutdown (code {})", MasterExit);
lg::info("GOAL Runtime Shutdown (code {})", MasterExit);
return MasterExit;
}
+2 -2
View File
@@ -6,6 +6,7 @@
#include <cassert>
#include <cstdio>
#include <cstring>
#include "common/log/log.h"
#include "deci2.h"
#include "game/system/Deci2Server.h"
@@ -69,8 +70,7 @@ s32 sceDeci2Open(u16 protocol, void* opt, void (*handler)(s32 event, s32 param,
drv.id = protocol_count + 1;
drv.active = true;
protocols[protocol_count++] = drv;
// printf("[DECI2] Add new protocol driver %d for 0x%x\n", drv.id, drv.protocol);
spdlog::info("[DECI2] Add new protocol driver {} for 0x{:x}", drv.id, drv.protocol);
lg::info("[DECI2] Add new protocol driver {} for 0x{:x}", drv.id, drv.protocol);
server->unlock();
if (protocol_count == 1) {
-1
View File
@@ -4,7 +4,6 @@
#define JAK1_IOP_H
#include "common/common_types.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#define SMEM_Low (0)
#define SMEM_High (1)
-1
View File
@@ -19,7 +19,6 @@
#include <condition_variable>
#include <functional>
#include "game/system/deci_common.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
class Deci2Server {
public:

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