mirror of
https://github.com/RPCS3/rsx_program_decompiler.git
synced 2026-01-31 01:25:19 +01:00
Implemented fragment program decompiler
This commit is contained in:
49
rsx_program_decompiler.sln
Normal file
49
rsx_program_decompiler.sln
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rsx_program_decompiler", "rsx_program_decompiler\rsx_program_decompiler.vcxproj", "{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rsx_program_decompiler_gui", "rsx_program_decompiler_gui\rsx_program_decompiler_gui.vcxproj", "{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE} = {F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Debug|x64.Build.0 = Debug|x64
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Release|ARM.Build.0 = Release|ARM
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Release|x64.ActiveCfg = Release|x64
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Release|x64.Build.0 = Release|x64
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F7AAD20D-BCFA-4B17-A178-BD8606B4E1FE}.Release|x86.Build.0 = Release|Win32
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Debug|x64.Build.0 = Debug|x64
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Debug|x86.Build.0 = Debug|Win32
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Release|x64.ActiveCfg = Release|x64
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Release|x64.Build.0 = Release|x64
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Release|x86.ActiveCfg = Release|Win32
|
||||
{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
129
rsx_program_decompiler/fmt.cpp
Normal file
129
rsx_program_decompiler/fmt.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "fmt.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to)
|
||||
{
|
||||
auto pos = src.find(from);
|
||||
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
|
||||
return (pos ? src.substr(0, pos) + to : to) + std::string(src.c_str() + pos + from.length());
|
||||
}
|
||||
|
||||
std::string replace_all(const std::string &src, const std::string& from, const std::string& to)
|
||||
{
|
||||
std::string target = src;
|
||||
for (auto pos = target.find(from); pos != std::string::npos; pos = target.find(from, pos + 1))
|
||||
{
|
||||
target = (pos ? target.substr(0, pos) + to : to) + std::string(target.c_str() + pos + from.length());
|
||||
pos += to.length();
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::size_t cursor_begin = 0;
|
||||
|
||||
for (std::size_t cursor_end = 0; cursor_end < source.length(); ++cursor_end)
|
||||
{
|
||||
for (auto &separator : separators)
|
||||
{
|
||||
if (strncmp(source.c_str() + cursor_end, separator.c_str(), separator.length()) == 0)
|
||||
{
|
||||
std::string candidate = source.substr(cursor_begin, cursor_end - cursor_begin);
|
||||
if (!is_skip_empty || !candidate.empty())
|
||||
result.push_back(candidate);
|
||||
|
||||
cursor_begin = cursor_end + separator.length();
|
||||
cursor_end = cursor_begin - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor_begin != source.length())
|
||||
{
|
||||
result.push_back(source.substr(cursor_begin));
|
||||
}
|
||||
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& source, const std::string& separator, bool is_skip_empty)
|
||||
{
|
||||
return split(source, { separator }, is_skip_empty);
|
||||
}
|
||||
|
||||
std::string tolower(std::string source)
|
||||
{
|
||||
std::transform(source.begin(), source.end(), source.begin(), ::tolower);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string toupper(std::string source)
|
||||
{
|
||||
std::transform(source.begin(), source.end(), source.begin(), ::toupper);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string escape(std::string source)
|
||||
{
|
||||
const std::pair<std::string, std::string> escape_list[] =
|
||||
{
|
||||
{ "\\", "\\\\" },
|
||||
{ "\a", "\\a" },
|
||||
{ "\b", "\\b" },
|
||||
{ "\f", "\\f" },
|
||||
{ "\n", "\\n\n" },
|
||||
{ "\r", "\\r" },
|
||||
{ "\t", "\\t" },
|
||||
{ "\v", "\\v" },
|
||||
};
|
||||
|
||||
source = replace_all(source, escape_list);
|
||||
|
||||
for (char c = 0; c < 32; c++)
|
||||
{
|
||||
if (c != '\n')
|
||||
source = replace_all(source, std::string(1, c), Format("\\x%02X", c));
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
std::vector<std::string> string::split(std::initializer_list<std::string> separators, bool is_skip_empty) const
|
||||
{
|
||||
return fmt::split(*this, separators, is_skip_empty);
|
||||
}
|
||||
|
||||
string& string::to_lower()
|
||||
{
|
||||
return *this = tolower(*this);
|
||||
}
|
||||
|
||||
string string::as_lower() const
|
||||
{
|
||||
return tolower(*this);
|
||||
}
|
||||
|
||||
string& string::to_upper()
|
||||
{
|
||||
return *this = toupper(*this);
|
||||
}
|
||||
|
||||
string string::as_upper() const
|
||||
{
|
||||
return toupper(*this);
|
||||
}
|
||||
}
|
||||
361
rsx_program_decompiler/fmt.h
Normal file
361
rsx_program_decompiler/fmt.h
Normal file
@@ -0,0 +1,361 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using s8 = std::int8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using s16 = std::int16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using s32 = std::int32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define snprintf _snprintf
|
||||
#define force_inline __forceinline
|
||||
#else
|
||||
#define force_inline inline
|
||||
#endif
|
||||
|
||||
#define safe_buffers
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
struct empty_t{};
|
||||
|
||||
//small wrapper used to deal with bitfields
|
||||
template<typename T>
|
||||
T by_value(T x) { return x; }
|
||||
|
||||
//wrapper to deal with advance sprintf formating options with automatic length finding
|
||||
template<typename... Args> std::string Format(const char* fmt, Args... parameters)
|
||||
{
|
||||
std::size_t length = 256;
|
||||
std::string str;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::vector<char> buffptr(length);
|
||||
#if !defined(_MSC_VER)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-security"
|
||||
std::size_t printlen = snprintf(buffptr.data(), length, fmt, std::forward<Args>(parameters)...);
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
std::size_t printlen = _snprintf_s(buffptr.data(), length, length - 1, fmt, std::forward<Args>(parameters)...);
|
||||
#endif
|
||||
if (printlen < length)
|
||||
{
|
||||
str = std::string(buffptr.data(), printlen);
|
||||
break;
|
||||
}
|
||||
length *= 2;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||
std::string replace_all(const std::string &src, const std::string& from, const std::string& to);
|
||||
|
||||
template<std::size_t list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::string>(&list)[list_size])
|
||||
{
|
||||
for (std::size_t pos = 0; pos < src.length(); ++pos)
|
||||
{
|
||||
for (std::size_t i = 0; i < list_size; ++i)
|
||||
{
|
||||
const std::size_t comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length)
|
||||
continue;
|
||||
|
||||
if (src.substr(pos, comp_length) == list[i].first)
|
||||
{
|
||||
src = (pos ? src.substr(0, pos) + list[i].second : list[i].second) + src.substr(pos + comp_length);
|
||||
pos += list[i].second.length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
template<std::size_t list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::function<std::string()>>(&list)[list_size])
|
||||
{
|
||||
for (std::size_t pos = 0; pos < src.length(); ++pos)
|
||||
{
|
||||
for (std::size_t i = 0; i < list_size; ++i)
|
||||
{
|
||||
const std::size_t comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length)
|
||||
continue;
|
||||
|
||||
if (src.substr(pos, comp_length) == list[i].first)
|
||||
{
|
||||
src = (pos ? src.substr(0, pos) + list[i].second() : list[i].second()) + src.substr(pos + comp_length);
|
||||
pos += list[i].second().length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
template<typename T, bool is_enum = std::is_enum<T>::value>
|
||||
struct unveil
|
||||
{
|
||||
typedef T result_type;
|
||||
|
||||
force_inline static result_type get_value(const T& arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct unveil<char*, false>
|
||||
{
|
||||
typedef const char* result_type;
|
||||
|
||||
force_inline static result_type get_value(const char* arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct unveil<const char[N], false>
|
||||
{
|
||||
typedef const char* result_type;
|
||||
|
||||
force_inline static result_type get_value(const char(&arg)[N])
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct unveil<std::string, false>
|
||||
{
|
||||
typedef const char* result_type;
|
||||
|
||||
force_inline static result_type get_value(const std::string& arg)
|
||||
{
|
||||
return arg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct unveil<T, true>
|
||||
{
|
||||
typedef typename std::underlying_type<T>::type result_type;
|
||||
|
||||
force_inline static result_type get_value(const T& arg)
|
||||
{
|
||||
return static_cast<result_type>(arg);
|
||||
}
|
||||
};
|
||||
/*
|
||||
template<typename T, typename T2>
|
||||
struct unveil<be_t<T, T2>, false>
|
||||
{
|
||||
typedef typename unveil<T>::result_type result_type;
|
||||
|
||||
force_inline static result_type get_value(const be_t<T, T2>& arg)
|
||||
{
|
||||
return unveil<T>::get_value(arg.value());
|
||||
}
|
||||
};
|
||||
*/
|
||||
template<typename T>
|
||||
force_inline typename unveil<T>::result_type do_unveil(const T& arg)
|
||||
{
|
||||
return unveil<T>::get_value(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
fmt::format(const char* fmt, args...)
|
||||
|
||||
Formatting function with special functionality:
|
||||
|
||||
std::string forced to .c_str()
|
||||
be_t<> forced to .value() (fmt::unveil reverts byte order automatically)
|
||||
|
||||
External specializations for fmt::unveil (can be found in another headers):
|
||||
vm::ps3::ptr (fmt::unveil) (vm_ptr.h) (with appropriate address type, using .addr() can be avoided)
|
||||
vm::ps3::bptr (fmt::unveil) (vm_ptr.h)
|
||||
vm::psv::ptr (fmt::unveil) (vm_ptr.h)
|
||||
vm::ps3::ref (fmt::unveil) (vm_ref.h)
|
||||
vm::ps3::bref (fmt::unveil) (vm_ref.h)
|
||||
vm::psv::ref (fmt::unveil) (vm_ref.h)
|
||||
|
||||
*/
|
||||
template<typename... Args> force_inline safe_buffers std::string format(const char* fmt, Args... args)
|
||||
{
|
||||
return Format(fmt, do_unveil(args)...);
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators = { " ", "\t" }, bool is_skip_empty = true);
|
||||
std::vector<std::string> split(const std::string& source, const std::string& separator, bool is_skip_empty = true);
|
||||
|
||||
template<typename T>
|
||||
std::string merge(const T& source, const std::string& separator)
|
||||
{
|
||||
if (!source.size())
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
auto it = source.begin();
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result += *it + separator;
|
||||
}
|
||||
|
||||
return result + source.back();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string merge(std::initializer_list<T> sources, const std::string& separator)
|
||||
{
|
||||
if (!sources.size())
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
bool first = true;
|
||||
|
||||
for (auto &v : sources)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
result = fmt::merge(v, separator);
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += separator + fmt::merge(v, separator);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string tolower(std::string source);
|
||||
std::string toupper(std::string source);
|
||||
std::string escape(std::string source);
|
||||
|
||||
template<typename T, bool use_std_to_string>
|
||||
struct to_string_impl
|
||||
{
|
||||
static std::string func(const T& value)
|
||||
{
|
||||
return value.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool use_std_to_string = std::is_arithmetic<T>::value>
|
||||
std::string to_string(const T& value)
|
||||
{
|
||||
return to_string_impl<std::remove_cv_t<T>, use_std_to_string>::func(value);
|
||||
}
|
||||
|
||||
class string : public std::string
|
||||
{
|
||||
public:
|
||||
//using std::string;
|
||||
string() = default;
|
||||
|
||||
template<typename T>
|
||||
string(const T& value) : std::string(to_string(value))
|
||||
{
|
||||
}
|
||||
|
||||
string(std::size_t count, char ch) : std::string(count, ch)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
string& operator = (const T& rhs)
|
||||
{
|
||||
std::string::operator=(to_string(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
string operator + (const T& rhs) const
|
||||
{
|
||||
return to_string(*this) + to_string(rhs);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
string& operator += (const T& rhs)
|
||||
{
|
||||
std::string::operator+=(to_string(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(std::initializer_list<std::string> separators = { " ", "\t" }, bool is_skip_empty = true) const;
|
||||
|
||||
string& to_lower();
|
||||
string as_lower() const;
|
||||
string& to_upper();
|
||||
string as_upper() const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct to_string_impl<T, true>
|
||||
{
|
||||
static std::string func(const T& value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<bool use_std_to_string>
|
||||
struct to_string_impl<std::string, use_std_to_string>
|
||||
{
|
||||
static const std::string& func(const std::string& value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<bool use_std_to_string>
|
||||
struct to_string_impl<string, use_std_to_string>
|
||||
{
|
||||
static const std::string& func(const string& value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<bool use_std_to_string>
|
||||
struct to_string_impl<char*, use_std_to_string>
|
||||
{
|
||||
static std::string func(const char* value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<bool use_std_to_string, std::size_t N>
|
||||
struct to_string_impl<char[N], use_std_to_string>
|
||||
{
|
||||
static std::string func(const char(&value)[N])
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
4
rsx_program_decompiler/rsx/rsx.h
Normal file
4
rsx_program_decompiler/rsx/rsx.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "rsx_glsl_fragment_program_decompiler.h"
|
||||
#include "rsx_glsl_vertex_program_decompiler.h"
|
||||
211
rsx_program_decompiler/rsx/rsx_fragment_program.h
Normal file
211
rsx_program_decompiler/rsx/rsx_fragment_program.h
Normal file
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
#include "fmt.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace fragment_program
|
||||
{
|
||||
enum class opcode
|
||||
{
|
||||
NOP = 0x00, // No-Operation
|
||||
MOV = 0x01, // Move
|
||||
MUL = 0x02, // Multiply
|
||||
ADD = 0x03, // Add
|
||||
MAD = 0x04, // Multiply-Add
|
||||
DP3 = 0x05, // 3-component Dot Product
|
||||
DP4 = 0x06, // 4-component Dot Product
|
||||
DST = 0x07, // Distance
|
||||
MIN = 0x08, // Minimum
|
||||
MAX = 0x09, // Maximum
|
||||
SLT = 0x0A, // Set-If-LessThan
|
||||
SGE = 0x0B, // Set-If-GreaterEqual
|
||||
SLE = 0x0C, // Set-If-LessEqual
|
||||
SGT = 0x0D, // Set-If-GreaterThan
|
||||
SNE = 0x0E, // Set-If-NotEqual
|
||||
SEQ = 0x0F, // Set-If-Equal
|
||||
FRC = 0x10, // Fraction (fract)
|
||||
FLR = 0x11, // Floor
|
||||
KIL = 0x12, // Kill fragment
|
||||
PK4 = 0x13, // Pack four signed 8-bit values
|
||||
UP4 = 0x14, // Unpack four signed 8-bit values
|
||||
DDX = 0x15, // Partial-derivative in x (Screen space derivative w.r.t. x)
|
||||
DDY = 0x16, // Partial-derivative in y (Screen space derivative w.r.t. y)
|
||||
TEX = 0x17, // Texture lookup
|
||||
TXP = 0x18, // Texture sample with projection (Projective texture lookup)
|
||||
TXD = 0x19, // Texture sample with partial differentiation (Texture lookup with derivatives)
|
||||
RCP = 0x1A, // Reciprocal
|
||||
RSQ = 0x1B, // Reciprocal Square Root
|
||||
EX2 = 0x1C, // Exponentiation base 2
|
||||
LG2 = 0x1D, // Log base 2
|
||||
LIT = 0x1E, // Lighting coefficients
|
||||
LRP = 0x1F, // Linear Interpolation
|
||||
STR = 0x20, // Set-If-True
|
||||
SFL = 0x21, // Set-If-False
|
||||
COS = 0x22, // Cosine
|
||||
SIN = 0x23, // Sine
|
||||
PK2 = 0x24, // Pack two 16-bit floats
|
||||
UP2 = 0x25, // Unpack two 16-bit floats
|
||||
POW = 0x26, // Power
|
||||
PKB = 0x27, // Pack bytes
|
||||
UPB = 0x28, // Unpack bytes
|
||||
PK16 = 0x29, // Pack 16 bits
|
||||
UP16 = 0x2A, // Unpack 16
|
||||
BEM = 0x2B, // Bump-environment map (a.k.a. 2D coordinate transform)
|
||||
PKG = 0x2C, // Pack with sRGB transformation
|
||||
UPG = 0x2D, // Unpack gamma
|
||||
DP2A = 0x2E, // 2-component dot product with scalar addition
|
||||
TXL = 0x2F, // Texture sample with explicit LOD
|
||||
TXB = 0x31, // Texture sample with bias
|
||||
TEXBEM = 0x33,
|
||||
TXPBEM = 0x34,
|
||||
BEMLUM = 0x35,
|
||||
REFL = 0x36, // Reflection vector
|
||||
TIMESWTEX = 0x37,
|
||||
DP2 = 0x38, // 2-component dot product
|
||||
NRM = 0x39, // Normalize
|
||||
DIV = 0x3A, // Division
|
||||
DIVSQ = 0x3B, // Divide by Square Root
|
||||
LIF = 0x3C, // Final part of LIT
|
||||
FENCT = 0x3D, // Fence T?
|
||||
FENCB = 0x3E, // Fence B?
|
||||
BRK = 0x40, // Break
|
||||
CAL = 0x41, // Subroutine call
|
||||
IFE = 0x42, // If
|
||||
LOOP = 0x43, // Loop
|
||||
REP = 0x44, // Repeat
|
||||
RET = 0x45 // Return
|
||||
};
|
||||
|
||||
union OPDEST
|
||||
{
|
||||
u32 HEX;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 end : 1; // Set to 1 if this is the last instruction
|
||||
u32 dest_reg : 6; // Destination register index
|
||||
u32 fp16 : 1; // Destination is a half register (H0 to H47)
|
||||
u32 set_cond : 1; // Condition Code Registers (CC0 and CC1) are updated
|
||||
u32 mask_x : 1;
|
||||
u32 mask_y : 1;
|
||||
u32 mask_z : 1;
|
||||
u32 mask_w : 1;
|
||||
u32 src_attr_reg_num : 4;
|
||||
u32 tex_num : 4;
|
||||
u32 exp_tex : 1; // _bx2
|
||||
u32 prec : 2;
|
||||
u32 opcode : 6;
|
||||
u32 no_dest : 1;
|
||||
u32 saturate : 1; // _sat
|
||||
};
|
||||
};
|
||||
|
||||
union SRC0
|
||||
{
|
||||
u32 HEX;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 reg_type : 2;
|
||||
u32 tmp_reg_index : 6;
|
||||
u32 fp16 : 1;
|
||||
u32 swizzle_x : 2;
|
||||
u32 swizzle_y : 2;
|
||||
u32 swizzle_z : 2;
|
||||
u32 swizzle_w : 2;
|
||||
u32 neg : 1;
|
||||
u32 exec_if_lt : 1;
|
||||
u32 exec_if_eq : 1;
|
||||
u32 exec_if_gr : 1;
|
||||
u32 cond_swizzle_x : 2;
|
||||
u32 cond_swizzle_y : 2;
|
||||
u32 cond_swizzle_z : 2;
|
||||
u32 cond_swizzle_w : 2;
|
||||
u32 abs : 1;
|
||||
u32 cond_mod_reg_index : 1;
|
||||
u32 cond_reg_index : 1;
|
||||
};
|
||||
};
|
||||
|
||||
union SRC1
|
||||
{
|
||||
u32 HEX;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 reg_type : 2;
|
||||
u32 tmp_reg_index : 6;
|
||||
u32 fp16 : 1;
|
||||
u32 swizzle_x : 2;
|
||||
u32 swizzle_y : 2;
|
||||
u32 swizzle_z : 2;
|
||||
u32 swizzle_w : 2;
|
||||
u32 neg : 1;
|
||||
u32 abs : 1;
|
||||
u32 input_mod_src0 : 3;
|
||||
u32: 6;
|
||||
u32 scale : 3;
|
||||
u32 opcode_is_branch : 1;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
u32 else_offset : 31;
|
||||
u32: 1;
|
||||
};
|
||||
|
||||
// LOOP, REP
|
||||
struct
|
||||
{
|
||||
u32: 2;
|
||||
u32 end_counter : 8; // End counter value for LOOP or rep count for REP
|
||||
u32 init_counter : 8; // Initial counter value for LOOP
|
||||
u32: 1;
|
||||
u32 increment : 8; // Increment value for LOOP
|
||||
};
|
||||
};
|
||||
|
||||
union SRC2
|
||||
{
|
||||
u32 HEX;
|
||||
|
||||
u32 end_offset;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 reg_type : 2;
|
||||
u32 tmp_reg_index : 6;
|
||||
u32 fp16 : 1;
|
||||
u32 swizzle_x : 2;
|
||||
u32 swizzle_y : 2;
|
||||
u32 swizzle_z : 2;
|
||||
u32 swizzle_w : 2;
|
||||
u32 neg : 1;
|
||||
u32 abs : 1;
|
||||
u32 addr_reg : 11;
|
||||
u32 use_index_reg : 1;
|
||||
u32 perspective_corr : 1;
|
||||
};
|
||||
};
|
||||
static const char* input_attr_regs[] =
|
||||
{
|
||||
"WPOS", "COL0", "COL1", "FOGC", "TEX0",
|
||||
"TEX1", "TEX2", "TEX3", "TEX4", "TEX5",
|
||||
"TEX6", "TEX7", "TEX8", "TEX9", "SSA"
|
||||
};
|
||||
|
||||
static const std::string instructions_names[] =
|
||||
{
|
||||
"NOP", "MOV", "MUL", "ADD", "MAD", "DP3", "DP4",
|
||||
"DST", "MIN", "MAX", "SLT", "SGE", "SLE", "SGT",
|
||||
"SNE", "SEQ", "FRC", "FLR", "KIL", "PK4", "UP4",
|
||||
"DDX", "DDY", "TEX", "TXP", "TXD", "RCP", "RSQ",
|
||||
"EX2", "LG2", "LIT", "LRP", "STR", "SFL", "COS",
|
||||
"SIN", "PK2", "UP2", "POW", "PKB", "UPB", "PK16",
|
||||
"UP16", "BEM", "PKG", "UPG", "DP2A", "TXL", "NULL",
|
||||
"TXB", "NULL", "TEXBEM", "TXPBEM", "BEMLUM", "REFL", "TIMESWTEX",
|
||||
"DP2", "NRM", "DIV", "DIVSQ", "LIF", "FENCT", "FENCB",
|
||||
"NULL", "BRK", "CAL", "IFE", "LOOP", "REP", "RET"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "rsx_fragment_program_decompiler.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace fragment_program
|
||||
{
|
||||
}
|
||||
}
|
||||
437
rsx_program_decompiler/rsx/rsx_fragment_program_decompiler.h
Normal file
437
rsx_program_decompiler/rsx/rsx_fragment_program_decompiler.h
Normal file
@@ -0,0 +1,437 @@
|
||||
#pragma once
|
||||
#include "rsx_fragment_program.h"
|
||||
#include "rsx_program_decompiler.h"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace fragment_program
|
||||
{
|
||||
enum suffix
|
||||
{
|
||||
suffix_none,
|
||||
H = 1 << 0,
|
||||
C = 1 << 1
|
||||
};
|
||||
|
||||
template<int size> struct dest {};
|
||||
template<int index, int size> struct src {};
|
||||
struct texture {};
|
||||
struct addr {};
|
||||
struct cond {};
|
||||
template<typename T> struct arg {};
|
||||
|
||||
union ucode_data
|
||||
{
|
||||
struct
|
||||
{
|
||||
OPDEST dst;
|
||||
SRC0 src0;
|
||||
SRC1 src1;
|
||||
SRC2 src2;
|
||||
};
|
||||
|
||||
u32 data[4];
|
||||
};
|
||||
|
||||
template<typename decompiler_impl>
|
||||
class decompiler;
|
||||
|
||||
template<opcode id, u32 flags, typename... Tuple>
|
||||
struct handle_instruction
|
||||
{
|
||||
};
|
||||
|
||||
template<opcode id, u32 flags, typename... Tuple>
|
||||
struct instruction
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
static void impl(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
handle_instruction < id, flags, Tuple... >::function(decompiler);
|
||||
}
|
||||
};
|
||||
|
||||
using MOV = instruction < opcode::MOV, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using MUL = instruction < opcode::MUL, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using ADD = instruction < opcode::ADD, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using MAD = instruction < opcode::MAD, H | C, dest<4>, arg<src<0, 4>>, arg<src<2, 4>> >;
|
||||
using DP3 = instruction < opcode::DP3, H | C, dest<3>, arg<src<0, 3>>, arg<src<1, 3>> >;
|
||||
using DP4 = instruction < opcode::DP4, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using DST = instruction < opcode::DST, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using MIN = instruction < opcode::MIN, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using MAX = instruction < opcode::MAX, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using SLT = instruction < opcode::SLT, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using SGE = instruction < opcode::SGE, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using SLE = instruction < opcode::SLE, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using SGT = instruction < opcode::SGT, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using SNE = instruction < opcode::SNE, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using SEQ = instruction < opcode::SEQ, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using FRC = instruction < opcode::FRC, H | C, dest<4>, arg<src<0, 1>> >;
|
||||
using FLR = instruction < opcode::FLR, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using KIL = instruction < opcode::KIL, suffix_none, arg<cond> >;
|
||||
using PK4 = instruction < opcode::PK4, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using UP4 = instruction < opcode::UP4, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using DDX = instruction < opcode::DDX, H | C, dest<2>, arg<src<0, 2> > >;
|
||||
using DDY = instruction < opcode::DDY, H | C, dest<2>, arg<src<0, 2> > >;
|
||||
using TEX = instruction < opcode::TEX, H | C, dest<4>, arg<src<0, 4>>, arg<texture> >;
|
||||
using TXP = instruction < opcode::TXP, H | C, dest<4>, arg<src<0, 4>>, arg<texture> >;
|
||||
using TXD = instruction < opcode::TXD, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using RCP = instruction < opcode::RCP, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using RSQ = instruction < opcode::RSQ, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using EX2 = instruction < opcode::EX2, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using LG2 = instruction < opcode::LG2, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using LIT = instruction < opcode::LIT, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using LRP = instruction < opcode::LRP, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using STR = instruction < opcode::STR, H | C, dest<4> >;
|
||||
using SFL = instruction < opcode::SFL, H | C, dest<4> >;
|
||||
using COS = instruction < opcode::COS, H | C, dest<4>, arg<src<0, 1> > >;
|
||||
using SIN = instruction < opcode::SIN, H | C, dest<4>, arg<src<0, 1> > >;
|
||||
using PK2 = instruction < opcode::PK2, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using UP2 = instruction < opcode::UP2, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using POW = instruction < opcode::POW, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using PKB = instruction < opcode::PKB, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using UPB = instruction < opcode::UPB, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using PK16 = instruction < opcode::PK16, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using UP16 = instruction < opcode::UP16, H | C, dest<4>, arg<src<0, 4> > >;
|
||||
using BEM = instruction < opcode::BEM, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using PKG = instruction < opcode::PKG, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using UPG = instruction < opcode::UPG, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using DP2A = instruction < opcode::DP2A, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using TXL = instruction < opcode::TXL, H | C, dest<4>, arg<src<0, 4>>, arg<texture> >;
|
||||
using TXB = instruction < opcode::TXB, H | C, dest<4>, arg<src<0, 4>>, arg<texture> >;
|
||||
using TEXBEM = instruction < opcode::TEXBEM, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using TXPBEM = instruction < opcode::TXPBEM, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using BEMLUM = instruction < opcode::BEMLUM, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using REFL = instruction < opcode::REFL, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using TIMESWTEX = instruction < opcode::TIMESWTEX, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using DP2 = instruction < opcode::DP2, H | C, dest<2>, arg<src<0, 2>>, arg<src<1, 2>> >;
|
||||
using NRM = instruction < opcode::NRM, H | C, dest<4>, arg<src<0, 4>> >;
|
||||
using DIV = instruction < opcode::DIV, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using DIVSQ = instruction < opcode::LOOP, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
|
||||
using LIF = instruction < opcode::LIF, H >;
|
||||
using FENCT = instruction < opcode::FENCT, suffix_none >;
|
||||
using FENCB = instruction < opcode::FENCB, suffix_none >;
|
||||
using BRK = instruction < opcode::BRK, suffix_none, arg<addr> >;
|
||||
using CAL = instruction < opcode::CAL, suffix_none, arg<addr> >;
|
||||
using IFE = instruction < opcode::IFE, H >;
|
||||
using LOOP = instruction < opcode::LOOP, H >;
|
||||
using REP = instruction < opcode::REP, H >;
|
||||
using RET = instruction < opcode::RET, H >;
|
||||
|
||||
template<typename decompiler_impl>
|
||||
using instruction_impl_func = void(*)(decompiler<decompiler_impl>&);
|
||||
|
||||
struct info : program_info
|
||||
{
|
||||
std::vector<u32> constant_offsets;
|
||||
};
|
||||
|
||||
template<opcode id, u32 flags, typename... args_type>
|
||||
struct handle_instruction < id, flags, arg<args_type>... >
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
__forceinline static void function(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<opcode id, u32 flags, int dest_count, typename... args_type>
|
||||
struct handle_instruction < id, flags, dest<dest_count>, arg<args_type>... >
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
__forceinline static void function(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
decompiler.set_dst<id, flags, dest_count>(decompiler.arg<args_type>()...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename decompiler_impl>
|
||||
class decompiler : public program_decompiler_core
|
||||
{
|
||||
ucode_data* ucode_ptr;
|
||||
u32 ucode_size;
|
||||
|
||||
public:
|
||||
int code_line_index = 0;
|
||||
bool is_next_constant = false;
|
||||
|
||||
fragment_program::info info;
|
||||
ucode_data ucode;
|
||||
u32 ctrl;
|
||||
|
||||
decompiler(void* ucode_ptr, u32 ucode_size, u32 ctrl = 0x40)
|
||||
: ucode_ptr((ucode_data*)ucode_ptr)
|
||||
, ucode_size(ucode_size)
|
||||
, ctrl(ctrl)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
struct expand_arg_t
|
||||
{
|
||||
};
|
||||
|
||||
template<int index, int count>
|
||||
struct expand_arg_t<src<index, count>>
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
__forceinline static program_variable impl(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
program_variable_type variable_type = program_variable_type::none;
|
||||
program_variable variable = {};
|
||||
|
||||
variable.size = count;
|
||||
|
||||
bool is_fp16 = false;
|
||||
int type;
|
||||
|
||||
const auto& dst = decompiler.ucode.dst;
|
||||
const auto& src0 = decompiler.ucode.src0;
|
||||
const auto& src1 = decompiler.ucode.src1;
|
||||
const auto& src2 = decompiler.ucode.src2;
|
||||
|
||||
u8 swizzle_x, swizzle_y, swizzle_z, swizzle_w;
|
||||
|
||||
auto get_data_from = [&](auto &src)
|
||||
{
|
||||
is_fp16 = src.fp16;
|
||||
type = src.reg_type;
|
||||
variable.index = src.tmp_reg_index;
|
||||
swizzle_x = src.swizzle_x;
|
||||
swizzle_y = src.swizzle_y;
|
||||
swizzle_z = src.swizzle_z;
|
||||
swizzle_w = src.swizzle_w;
|
||||
};
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: get_data_from(src0); break;
|
||||
case 1: get_data_from(src1); break;
|
||||
case 2: get_data_from(src2); break;
|
||||
}
|
||||
|
||||
variable.name = is_fp16 ? "H" : "R";
|
||||
bool need_declare = true;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0: //temporary register
|
||||
variable.type = program_variable_type::none;
|
||||
break;
|
||||
|
||||
case 1: //input register
|
||||
variable.index = dst.src_attr_reg_num;
|
||||
variable.type = program_variable_type::input;
|
||||
break;
|
||||
|
||||
case 2: //constant
|
||||
need_declare = false;
|
||||
decompiler.is_next_constant = true;
|
||||
break;
|
||||
}
|
||||
|
||||
static const std::string mask = "xyzw";
|
||||
|
||||
std::string swizzle;
|
||||
swizzle += mask[swizzle_x];
|
||||
swizzle += mask[swizzle_y];
|
||||
swizzle += mask[swizzle_z];
|
||||
swizzle += mask[swizzle_w];
|
||||
|
||||
variable.mask.add(swizzle);
|
||||
|
||||
if (need_declare)
|
||||
variable = decompiler.info.vars.add(variable);
|
||||
|
||||
return variable;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct expand_arg_t<cond>
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
__forceinline static program_variable impl(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
return decompiler.execution_condition();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct expand_arg_t<texture>
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
__forceinline static program_variable impl(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
program_variable result = {};
|
||||
result.name = "texture";
|
||||
result.index = decompiler.ucode.dst.tex_num;
|
||||
result.size = 1;
|
||||
return decompiler.info.vars.add(decompiler_impl::texture_variable(result));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct expand_arg_t<addr>
|
||||
{
|
||||
template<typename decompiler_impl>
|
||||
__forceinline static program_variable impl(decompiler<decompiler_impl>& decompiler)
|
||||
{
|
||||
return{ "label",{}, program_variable_type::none, 1, 0 };
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
std::unordered_set<std::string> functions_set;
|
||||
|
||||
template<u32 flags, int count>
|
||||
program_variable dst()
|
||||
{
|
||||
if (ucode.dst.no_dest)
|
||||
return{};
|
||||
|
||||
program_variable result = {};
|
||||
result.index = ucode.dst.dest_reg;
|
||||
result.name = ucode.dst.fp16 ? "H" : "R";
|
||||
result.size = count;
|
||||
|
||||
static const std::string mask = "xyzw";
|
||||
|
||||
std::string swizzle;
|
||||
if (ucode.dst.mask_x) swizzle += mask[0];
|
||||
if (ucode.dst.mask_y) swizzle += mask[1];
|
||||
if (ucode.dst.mask_z) swizzle += mask[2];
|
||||
if (ucode.dst.mask_w) swizzle += mask[3];
|
||||
|
||||
result.mask.add(swizzle);
|
||||
|
||||
return info.vars.add(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
program_variable arg()
|
||||
{
|
||||
return expand_arg_t<T>::impl(*this);
|
||||
}
|
||||
|
||||
program_variable update_condition()
|
||||
{
|
||||
program_variable result = {};
|
||||
|
||||
result.name = "CC";
|
||||
result.index = ucode.src0.cond_mod_reg_index;
|
||||
result.size = 4;
|
||||
|
||||
return info.vars.add(result);
|
||||
}
|
||||
|
||||
program_variable execution_condition()
|
||||
{
|
||||
program_variable result = {};
|
||||
|
||||
result.name = "CC";
|
||||
result.index = ucode.src0.cond_reg_index;
|
||||
result.size = 4;
|
||||
|
||||
static const std::string mask = "xyzw";
|
||||
|
||||
std::string swizzle;
|
||||
swizzle += mask[ucode.src0.cond_swizzle_x];
|
||||
swizzle += mask[ucode.src0.cond_swizzle_y];
|
||||
swizzle += mask[ucode.src0.cond_swizzle_z];
|
||||
swizzle += mask[ucode.src0.cond_swizzle_w];
|
||||
|
||||
result.mask.add(swizzle);
|
||||
|
||||
return info.vars.add(result);
|
||||
}
|
||||
|
||||
void set_code_line(const std::string &code_line)
|
||||
{
|
||||
program_decompiler_core::builder.add_code_block(code_line_index, code_line);
|
||||
}
|
||||
|
||||
template<opcode id, u32 flags, int count>
|
||||
void set_dst(const program_variable& arg0 = {}, const program_variable& arg1 = {}, const program_variable& arg2 = {})
|
||||
{
|
||||
set_code_line(decompiler_impl::set_dst<id, flags, count>(this, arg0, arg1, arg2));
|
||||
}
|
||||
|
||||
void unknown_instruction()
|
||||
{
|
||||
}
|
||||
|
||||
std::string function_begin(const std::string& name)
|
||||
{
|
||||
functions_set.insert(name);
|
||||
return decompiler_impl::function_begin(name);
|
||||
}
|
||||
|
||||
std::string function_end()
|
||||
{
|
||||
return decompiler_impl::function_end();
|
||||
}
|
||||
|
||||
public:
|
||||
fragment_program::info& decompile()
|
||||
{
|
||||
static const instruction_impl_func<decompiler_impl> instructions[0x80] =
|
||||
{
|
||||
nullptr, MOV::impl, MUL::impl, ADD::impl, MAD::impl, DP3::impl, DP4::impl,
|
||||
DST::impl, MIN::impl, MAX::impl, SLT::impl, SGE::impl, SLE::impl, SGT::impl,
|
||||
SNE::impl, SEQ::impl, FRC::impl, FLR::impl, KIL::impl, PK4::impl, UP4::impl,
|
||||
DDX::impl, DDY::impl, TEX::impl, TXP::impl, TXD::impl, RCP::impl, RSQ::impl,
|
||||
EX2::impl, LG2::impl, LIT::impl, LRP::impl, STR::impl, SFL::impl, COS::impl,
|
||||
SIN::impl, PK2::impl, UP2::impl, POW::impl, PKB::impl, UPB::impl, PK16::impl,
|
||||
UP16::impl, BEM::impl, PKG::impl, UPG::impl, DP2A::impl, TXL::impl, nullptr,
|
||||
TXB::impl, nullptr, TEXBEM::impl, TXPBEM::impl, BEMLUM::impl, REFL::impl, TIMESWTEX::impl,
|
||||
DP2::impl, NRM::impl, DIV::impl, DIVSQ::impl, LIF::impl, FENCT::impl, FENCB::impl,
|
||||
nullptr, BRK::impl, CAL::impl, IFE::impl, LOOP::impl, REP::impl, RET::impl
|
||||
};
|
||||
|
||||
code_line_index = 0;
|
||||
|
||||
for (u32 i = 0; i < ucode_size; ++i)
|
||||
{
|
||||
ucode = ucode_ptr[i];
|
||||
|
||||
ucode.data[0] = (ucode.data[0] << 16) | (ucode.data[0] >> 16);
|
||||
ucode.data[1] = (ucode.data[1] << 16) | (ucode.data[1] >> 16);
|
||||
ucode.data[2] = (ucode.data[2] << 16) | (ucode.data[2] >> 16);
|
||||
ucode.data[3] = (ucode.data[3] << 16) | (ucode.data[3] >> 16);
|
||||
|
||||
const u32 opcode = ucode.dst.opcode | (ucode.src1.opcode_is_branch << 6);
|
||||
|
||||
auto function = instructions[opcode];
|
||||
|
||||
if (function)
|
||||
{
|
||||
function(*this);
|
||||
code_line_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
unknown_instruction();
|
||||
}
|
||||
|
||||
if (ucode.dst.end)
|
||||
break;
|
||||
}
|
||||
|
||||
builder.add_code_block(0, function_begin("label0"), 0, 1, false);
|
||||
|
||||
std::string end = decompiler_impl::finalyze(this);
|
||||
|
||||
builder.add_code_block(0, decompiler_impl::get_header(this), 0, 0, false);
|
||||
builder.add_code_block(code_line_index, function_end(), -1, 0);
|
||||
|
||||
builder.add_code_block(code_line_index, end);
|
||||
|
||||
info.text = builder.build();
|
||||
return info;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
#pragma once
|
||||
#include "rsx_program_decompiler.h"
|
||||
#include "rsx_fragment_program_decompiler.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace fragment_program
|
||||
{
|
||||
struct glsl_decompiler_impl
|
||||
{
|
||||
using decompiler = fragment_program::decompiler < glsl_decompiler_impl >;
|
||||
|
||||
__forceinline static std::string get_header(decompiler* dec)
|
||||
{
|
||||
std::string result = "#version 420\n\n";
|
||||
for (auto &var : dec->info.vars)
|
||||
{
|
||||
switch (var.second.type)
|
||||
{
|
||||
case program_variable_type::input: result += "in "; break;
|
||||
case program_variable_type::output: result += "layout(location = " + std::to_string(var.second.index) + ") out "; break;
|
||||
case program_variable_type::constant: result += "uniform "; break;
|
||||
}
|
||||
|
||||
if (var.second.storage_type.empty())
|
||||
{
|
||||
if (var.second.size == 1)
|
||||
{
|
||||
var.second.storage_type = "float";
|
||||
}
|
||||
else
|
||||
{
|
||||
var.second.storage_type = "vec" + std::to_string(var.second.size);
|
||||
}
|
||||
}
|
||||
|
||||
result += var.second.storage_type + " " + var.second.name +
|
||||
(var.second.array_size ? ("[" + std::to_string(var.second.array_size + 1) + "]") : std::to_string(var.second.index)) + ";\n";
|
||||
}
|
||||
|
||||
for (auto &func : dec->functions_set)
|
||||
{
|
||||
result += "void " + func + "();\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
__forceinline static program_variable texture_variable(program_variable arg)
|
||||
{
|
||||
arg.storage_type = "sampler2D";
|
||||
arg.type = program_variable_type::constant;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
__forceinline static std::string variable_to_string(const program_variable& arg)
|
||||
{
|
||||
fmt::string result = arg;
|
||||
|
||||
if (arg.is_abs)
|
||||
result = "abs(" + result + ")";
|
||||
|
||||
if (arg.is_neg)
|
||||
result = "-" + result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
__forceinline static std::string function_begin(const std::string& name)
|
||||
{
|
||||
return "\nvoid " + name + "() {";
|
||||
}
|
||||
|
||||
__forceinline static std::string function_end()
|
||||
{
|
||||
return "}";
|
||||
}
|
||||
|
||||
template<opcode id, u32 flags, int count>
|
||||
__forceinline static std::string set_dst(decompiler* dec, const program_variable& arg0, const program_variable& arg1, const program_variable& arg2)
|
||||
{
|
||||
opcode _id = id;
|
||||
|
||||
static const char operators[] =
|
||||
{
|
||||
'?', '?', '*', '+', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?',
|
||||
'?', '?', '/', '?', '?', '?', '?',
|
||||
'?', '?', '?', '?', '?', '?', '?'
|
||||
};
|
||||
|
||||
static const std::string functions[] =
|
||||
{
|
||||
"?", "?", "?", "?", "fma", "dot", "dot",
|
||||
"distantion", "min", "max", "lessThan", "greaterThanEquals", "lessThanEqual", "greaterThan",
|
||||
"notEqual", "equal", "fract", "floor", "?", "pk4", "up4",
|
||||
"ddx", "ddy", "texture", "txp", "txd", "rcp", "rsq",
|
||||
"exp2", "log2", "lit", "lrp", "str", "sfl", "cos",
|
||||
"sin", "pk2", "up2", "pow", "pkb", "upb", "pk16",
|
||||
"up16", "bem" "pkg", "upg", "dpa2", "txl", "?",
|
||||
"txb", "?", "texbem", "txpbem", "bemlum", "refl", "timeswtex",
|
||||
"dot", "normalize", "?", "divsq", "lif", "fenct", "fencb",
|
||||
"?", "break", "cal", "ife", "loop", "rep", "return"
|
||||
};
|
||||
|
||||
fmt::string value;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case opcode::MOV:
|
||||
case opcode::MUL:
|
||||
case opcode::ADD:
|
||||
case opcode::DIV:
|
||||
//operators
|
||||
|
||||
if (!arg0.is_null())
|
||||
{
|
||||
value += variable_to_string(arg0);
|
||||
|
||||
if (!arg1.is_null())
|
||||
{
|
||||
value += " " + std::string(1, operators[(std::size_t)id]) + " " + variable_to_string(arg1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case opcode::DIVSQ:
|
||||
value += variable_to_string(arg0) + " / sqrt(" + variable_to_string(arg1) + ")";
|
||||
break;
|
||||
|
||||
case opcode::LIF:
|
||||
{
|
||||
program_variable arg0_y = arg0; arg0_y.mask.add("y");
|
||||
program_variable arg0_w = arg0; arg0_w.mask.add("w");
|
||||
std::string arg0_y_string = variable_to_string(arg0_y);
|
||||
std::string arg0_w_string = variable_to_string(arg0_w);
|
||||
|
||||
//vec4(1.0f, $0.y, ($0.y > 0 ? pow(2.0f, $0.w) : 0.0f), 1.0f)
|
||||
value += "vec4(1.0f, " + arg0_y_string + ", (" + arg0_y_string + " > 0.0f ? pow(2.0f, " + arg0_w_string + ") : 0.0f), 1.0f)";
|
||||
|
||||
//value += variable_to_string(arg0) + " / sqrt(" + variable_to_string(arg1) + ")";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//functions
|
||||
|
||||
value += functions[(std::size_t)id] + "(";
|
||||
|
||||
if (!arg0.is_null())
|
||||
{
|
||||
value += variable_to_string(arg0);
|
||||
|
||||
if (!arg1.is_null())
|
||||
{
|
||||
value += ", " + variable_to_string(arg1);
|
||||
|
||||
if (!arg2.is_null())
|
||||
{
|
||||
value += ", " + variable_to_string(arg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value += ")";
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & H)
|
||||
{
|
||||
switch (dec->ucode.dst.prec)
|
||||
{
|
||||
case 0: //fp32, do nothing
|
||||
break;
|
||||
|
||||
case 1: //fp16, clamping
|
||||
value += "clamp(" + value + ", -65536, 65536)";
|
||||
break;
|
||||
|
||||
case 2: //fixed point 12? let it be unimplemented, atm
|
||||
throw std::runtime_error("fragment program decompiler: unimplemented precision.");
|
||||
}
|
||||
|
||||
switch (dec->ucode.src1.scale)
|
||||
{
|
||||
case 0: break;
|
||||
case 1: value = "(" + value + " * 2.0)"; break;
|
||||
case 2: value = "(" + value + " * 4.0)"; break;
|
||||
case 3: value = "(" + value + " * 8.0)"; break;
|
||||
case 5: value = "(" + value + " / 2.0)"; break;
|
||||
case 6: value = "(" + value + " / 4.0)"; break;
|
||||
case 7: value = "(" + value + " / 8.0)"; break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("fragment program decompiler: unimplemented scale.");
|
||||
}
|
||||
|
||||
if (dec->ucode.dst.saturate)
|
||||
{
|
||||
value += "clamp(" + value + ", 0, 1)";
|
||||
}
|
||||
}
|
||||
|
||||
std::string result;
|
||||
program_variable dst = dec->dst<flags, count>();
|
||||
program_variable update_condition;
|
||||
|
||||
bool do_update_condition = false;
|
||||
if ((flags & C) && dec->ucode.dst.set_cond)
|
||||
{
|
||||
update_condition = dec->update_condition();
|
||||
do_update_condition = !update_condition.is_null();
|
||||
}
|
||||
|
||||
if (dec->ucode.src0.exec_if_eq && dec->ucode.src0.exec_if_gr && dec->ucode.src0.exec_if_lt)
|
||||
{
|
||||
std::string dst_string;
|
||||
|
||||
if (dst)
|
||||
{
|
||||
dst_string = dst.to_string();
|
||||
result += dst_string + " = " + value + ";\n";
|
||||
}
|
||||
|
||||
if (do_update_condition)
|
||||
{
|
||||
result += update_condition.to_string() + " = " + (dst ? dst_string : value) + ";\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
program_variable execution_condition = dec->execution_condition();
|
||||
mask_t update_mask;
|
||||
update_mask.add(dst.mask.to_string());
|
||||
update_mask.add(execution_condition.mask.to_string());
|
||||
update_mask.add(fmt::string("xyzw").substr(0, count));
|
||||
|
||||
fmt::string execution_condition_string = execution_condition;
|
||||
|
||||
std::string execution_condition_operation;
|
||||
|
||||
if (dec->ucode.src0.exec_if_gr && dec->ucode.src0.exec_if_eq)
|
||||
execution_condition_operation = ">=";
|
||||
else if (dec->ucode.src0.exec_if_lt && dec->ucode.src0.exec_if_eq)
|
||||
execution_condition_operation = "<=";
|
||||
else if (dec->ucode.src0.exec_if_gr && dec->ucode.src0.exec_if_lt)
|
||||
execution_condition_operation = "!=";
|
||||
else if (dec->ucode.src0.exec_if_gr)
|
||||
execution_condition_operation = ">";
|
||||
else if (dec->ucode.src0.exec_if_lt)
|
||||
execution_condition_operation = "<";
|
||||
else //if(dec->ucode.src0.exec_if_eq)
|
||||
execution_condition_operation = "==";
|
||||
|
||||
fmt::string update_mask_string = update_mask.to_string();
|
||||
if (update_mask_string.empty())
|
||||
update_mask_string = "xyzw";
|
||||
|
||||
std::string last_condition_group;
|
||||
std::string last_line;
|
||||
|
||||
for (char _mask : update_mask_string)
|
||||
{
|
||||
const std::string mask(1, _mask);
|
||||
const std::string dot_mask = "." + mask;
|
||||
|
||||
auto channel_execution_condition = execution_condition;
|
||||
std::string channel_execution_condition_mask = channel_execution_condition.mask.add(mask).to_string();
|
||||
|
||||
if (channel_execution_condition_mask != last_condition_group)
|
||||
{
|
||||
if (!last_condition_group.empty())
|
||||
{
|
||||
result += "}\n";
|
||||
}
|
||||
|
||||
result += "if (" + channel_execution_condition.to_string() + " " + execution_condition_operation + " 0.0f) {\n";
|
||||
last_condition_group = channel_execution_condition_mask;
|
||||
last_line.clear();
|
||||
}
|
||||
|
||||
std::string channel_dst_string;
|
||||
|
||||
if (dst)
|
||||
{
|
||||
auto channel_dst = dst;
|
||||
channel_dst.mask.add(mask);
|
||||
channel_dst_string = channel_dst.to_string();
|
||||
std::string line = "\t" + channel_dst_string + " = " + value + dot_mask + ";\n";
|
||||
if (last_line == line)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
result += line;
|
||||
last_line = line;
|
||||
}
|
||||
|
||||
if (do_update_condition)
|
||||
{
|
||||
std::string cond_value = (dst ? channel_dst_string : value) + dot_mask;
|
||||
|
||||
result += "\t" + update_condition.to_string() + dot_mask + " = " + cond_value + ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!last_condition_group.empty())
|
||||
result += "}\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string finalyze(decompiler *dec)
|
||||
{
|
||||
std::string result = "\nvoid main() {\n";
|
||||
result += "\tlabel0();\n";
|
||||
|
||||
struct destination_info
|
||||
{
|
||||
std::string name;
|
||||
bool is_need_declare;
|
||||
};
|
||||
|
||||
struct source_info
|
||||
{
|
||||
std::size_t r_register;
|
||||
std::size_t h_register;
|
||||
};
|
||||
|
||||
const source_info color_source_registers[] =
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ 2, 4 },
|
||||
{ 3, 6 },
|
||||
{ 4, 8 }
|
||||
};
|
||||
program_variable color_variable{};
|
||||
color_variable.type = program_variable_type::output;
|
||||
color_variable.size = 4;
|
||||
color_variable.name = "ocolor";
|
||||
|
||||
for (u32 i = 0; i < (u32)std::size(color_source_registers); ++i)
|
||||
{
|
||||
std::size_t color_register_index = dec->ctrl & 0x40 ? color_source_registers[i].r_register : color_source_registers[i].h_register;
|
||||
std::string color_register = (dec->ctrl & 0x40 ? "R" : "H") + std::to_string(color_register_index);
|
||||
if (dec->info.vars.exists(color_register))
|
||||
{
|
||||
color_variable.index = i;
|
||||
result += "\t" + dec->info.vars.add(color_variable).to_string() + " = " + color_register + ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (dec->ctrl & 0xe)
|
||||
{
|
||||
if (dec->ctrl & 0x40)
|
||||
{
|
||||
if (dec->info.vars.exists("R1"))
|
||||
{
|
||||
result += "\tgl_FragDepth = R1.z;\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dec->info.vars.exists("H2"))
|
||||
{
|
||||
result += "\tgl_FragDepth = H2.z;\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result + "}";
|
||||
}
|
||||
};
|
||||
|
||||
using glsl_decompiler = decompiler < glsl_decompiler_impl >;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "rsx_vertex_program_decompiler.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace vertex_program
|
||||
{
|
||||
using glsl_decompiler = decompiler;
|
||||
}
|
||||
}
|
||||
284
rsx_program_decompiler/rsx/rsx_program_decompiler.cpp
Normal file
284
rsx_program_decompiler/rsx/rsx_program_decompiler.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "rsx_program_decompiler.h"
|
||||
#include "fmt.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
mask_t& mask_t::add(const std::string& mask)
|
||||
{
|
||||
if (!mask.empty())
|
||||
swizzles.push_back(mask);
|
||||
return *this;
|
||||
}
|
||||
|
||||
mask_t& mask_t::symplify()
|
||||
{
|
||||
if (swizzles.size() < 2)
|
||||
return *this;
|
||||
|
||||
std::unordered_map<char, char> swizzle;
|
||||
|
||||
static std::unordered_map<int, char> pos_to_swizzle =
|
||||
{
|
||||
{ 0, 'x' },
|
||||
{ 1, 'y' },
|
||||
{ 2, 'z' },
|
||||
{ 3, 'w' }
|
||||
};
|
||||
|
||||
auto it = swizzles.begin();
|
||||
|
||||
const std::string& sw_front = *it;
|
||||
|
||||
for (auto &i : pos_to_swizzle)
|
||||
{
|
||||
swizzle[i.second] = sw_front.length() > i.first ? sw_front[i.first] : 0;
|
||||
}
|
||||
|
||||
for (++it; it != swizzles.end(); ++it)
|
||||
{
|
||||
std::unordered_map<char, char> new_swizzle;
|
||||
|
||||
for (auto &sw : pos_to_swizzle)
|
||||
{
|
||||
new_swizzle[sw.second] = swizzle[it->length() <= sw.first ? '\0' : (*it)[sw.first]];
|
||||
}
|
||||
|
||||
swizzle = new_swizzle;
|
||||
}
|
||||
|
||||
swizzles.clear();
|
||||
std::string new_swizzle;
|
||||
|
||||
for (auto &i : pos_to_swizzle)
|
||||
{
|
||||
if (swizzle[i.second] != '\0')
|
||||
new_swizzle += swizzle[i.second];
|
||||
}
|
||||
|
||||
swizzles.push_back(new_swizzle);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string mask_t::to_string_impl() const
|
||||
{
|
||||
return fmt::merge(swizzles, ".");
|
||||
}
|
||||
|
||||
std::string mask_t::to_string() const
|
||||
{
|
||||
return to_string_impl();
|
||||
}
|
||||
|
||||
std::string mask_t::to_string()
|
||||
{
|
||||
return symplify().to_string_impl();
|
||||
}
|
||||
|
||||
std::string program_variable::to_string_impl() const
|
||||
{
|
||||
if (array_size)
|
||||
return name + "[" + std::to_string(index) + "]";
|
||||
|
||||
return name + std::to_string(index);
|
||||
}
|
||||
|
||||
std::string program_variable::append_dot_if_not_empty(const std::string& string) const
|
||||
{
|
||||
return string.empty() ? std::string{} : "." + string;
|
||||
};
|
||||
|
||||
std::string program_variable::storage_name() const
|
||||
{
|
||||
return name + (array_size ? "[" + std::to_string(array_size + 1) + "]" : std::to_string(index));
|
||||
}
|
||||
|
||||
std::string program_variable::to_string() const
|
||||
{
|
||||
return to_string_impl() + append_dot_if_not_empty(mask.to_string());
|
||||
}
|
||||
|
||||
std::string program_variable::to_string()
|
||||
{
|
||||
return to_string_impl() + append_dot_if_not_empty(mask.to_string());
|
||||
}
|
||||
|
||||
bool program_variable::is_null() const
|
||||
{
|
||||
return name.empty();
|
||||
}
|
||||
|
||||
program_variable::operator bool() const
|
||||
{
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
program_variable& program_variables::add(const program_variable& var)
|
||||
{
|
||||
auto &new_var = m_data[var.storage_name()];
|
||||
new_var = var;
|
||||
|
||||
return new_var;
|
||||
}
|
||||
|
||||
const program_variable& program_variables::operator[](const std::string& name) const
|
||||
{
|
||||
auto found = m_data.find(name);
|
||||
if (found == m_data.end())
|
||||
throw std::logic_error("program_variables: program_variable '" + name + "' not found");
|
||||
|
||||
return found->second;
|
||||
}
|
||||
|
||||
program_variable& program_variables::operator[](const std::string& name)
|
||||
{
|
||||
auto found = m_data.find(name);
|
||||
if (found == m_data.end())
|
||||
throw std::logic_error("program_variables: program_variable '" + name + "' not found");
|
||||
|
||||
return found->second;
|
||||
}
|
||||
|
||||
bool program_variables::exists(const std::string& name) const
|
||||
{
|
||||
return m_data.find(name) != m_data.end();
|
||||
}
|
||||
|
||||
void program_variables::clear()
|
||||
{
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, program_variable>::iterator program_variables::begin()
|
||||
{
|
||||
return m_data.begin();
|
||||
}
|
||||
|
||||
std::unordered_map< std::string, program_variable>::iterator program_variables::end()
|
||||
{
|
||||
return m_data.end();
|
||||
}
|
||||
|
||||
void program_info::clear()
|
||||
{
|
||||
text.clear();
|
||||
vars.clear();
|
||||
}
|
||||
|
||||
void code_builder::add_code_block(size_t index, const std::string& lines, int tab_before, int tab_after, bool to_end)
|
||||
{
|
||||
auto& value = m_entries[index];
|
||||
std::list<code_line> code_lines;
|
||||
|
||||
const auto blocked_lines = fmt::split(lines, "\n", false);
|
||||
if (blocked_lines.size() == 1)
|
||||
{
|
||||
code_lines.push_back(code_line{ tab_before, tab_after, blocked_lines[0] });
|
||||
}
|
||||
else if (blocked_lines.size() > 1)
|
||||
{
|
||||
auto begin = blocked_lines.begin();
|
||||
auto end = blocked_lines.end() - 1;
|
||||
|
||||
code_lines.push_back(code_line{ tab_before, 0, *begin++ });
|
||||
while (begin != end)
|
||||
{
|
||||
code_lines.push_back(code_line{ 0, 0, *begin++ });
|
||||
}
|
||||
|
||||
code_lines.push_back(code_line{ 0, tab_after, blocked_lines.back() });
|
||||
}
|
||||
|
||||
value.code_lines.insert(to_end ? value.code_lines.end() : value.code_lines.begin(), code_lines.begin(), code_lines.end());
|
||||
}
|
||||
|
||||
void code_builder::branch(size_t from, size_t to, const std::string& condition)
|
||||
{
|
||||
if (from < to)
|
||||
{
|
||||
m_entries[from].branch_to.push_back({ to, condition });
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entries[to].branch_from.push_back({ from, condition });
|
||||
}
|
||||
}
|
||||
|
||||
void code_builder::build_branches()
|
||||
{
|
||||
std::vector<std::pair<size_t, size_t>> reserved_blocks;
|
||||
|
||||
auto is_reserved = [&](size_t begin, size_t end)
|
||||
{
|
||||
for (auto &block : reserved_blocks)
|
||||
{
|
||||
if (begin >= block.first && begin < block.second)
|
||||
{
|
||||
if (end < block.first || end > block.second)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for (auto &entry : m_entries)
|
||||
{
|
||||
for (auto &binfo : entry.second.branch_to)
|
||||
{
|
||||
if (is_reserved(entry.first, binfo.index))
|
||||
{
|
||||
throw std::runtime_error("unhandled branch");
|
||||
}
|
||||
|
||||
add_code_block(entry.first, "if (!" + binfo.condition + ")\n{", 0, 1, true);
|
||||
add_code_block(binfo.index, "}", -1, 0, false);
|
||||
|
||||
reserved_blocks.push_back(std::make_pair(entry.first, binfo.index));
|
||||
}
|
||||
|
||||
for (auto &binfo : entry.second.branch_from)
|
||||
{
|
||||
if (is_reserved(entry.first, binfo.index))
|
||||
{
|
||||
throw std::runtime_error("unhandled branch");
|
||||
}
|
||||
|
||||
add_code_block(entry.first, "do\n{", 0, 1, true);
|
||||
add_code_block(binfo.index, "}\nwhile (" + binfo.condition + ");", -1, 0, false);
|
||||
|
||||
reserved_blocks.push_back(std::make_pair(entry.first, binfo.index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string code_builder::build_code() const
|
||||
{
|
||||
std::string result;
|
||||
int tab_count = 0;
|
||||
|
||||
for (auto &entry : m_entries)
|
||||
{
|
||||
for (auto &line : entry.second.code_lines)
|
||||
{
|
||||
tab_count += line.tab_before;
|
||||
result.append(tab_count, '\t') += line.value + "\n";
|
||||
tab_count += line.tab_after;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string code_builder::build()
|
||||
{
|
||||
build_branches();
|
||||
return build_code();
|
||||
}
|
||||
|
||||
program_info& program_decompiler_core::decompile()
|
||||
{
|
||||
info.text = builder.build();
|
||||
return info;
|
||||
}
|
||||
}
|
||||
120
rsx_program_decompiler/rsx/rsx_program_decompiler.h
Normal file
120
rsx_program_decompiler/rsx/rsx_program_decompiler.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "fmt.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
enum class program_variable_type
|
||||
{
|
||||
none,
|
||||
input,
|
||||
output,
|
||||
constant
|
||||
};
|
||||
|
||||
struct mask_t
|
||||
{
|
||||
std::vector<std::string> swizzles;
|
||||
|
||||
mask_t& add(const std::string& mask);
|
||||
mask_t& symplify();
|
||||
|
||||
private:
|
||||
std::string to_string_impl() const;
|
||||
|
||||
public:
|
||||
std::string to_string() const;
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
struct program_variable
|
||||
{
|
||||
std::string name;
|
||||
mask_t mask;
|
||||
program_variable_type type;
|
||||
u32 index;
|
||||
u8 size;
|
||||
std::string storage_type;
|
||||
u32 array_size;
|
||||
bool is_neg;
|
||||
bool is_abs;
|
||||
|
||||
private:
|
||||
std::string to_string_impl() const;
|
||||
std::string append_dot_if_not_empty(const std::string& string) const;
|
||||
|
||||
public:
|
||||
std::string storage_name() const;
|
||||
std::string to_string() const;
|
||||
std::string to_string();
|
||||
bool is_null() const;
|
||||
explicit operator bool() const;
|
||||
};
|
||||
|
||||
class program_variables
|
||||
{
|
||||
std::unordered_map<std::string, program_variable> m_data;
|
||||
|
||||
public:
|
||||
program_variable& add(const program_variable& var);
|
||||
const program_variable& operator[](const std::string& name) const;
|
||||
program_variable& operator[](const std::string& name);
|
||||
bool exists(const std::string& name) const;
|
||||
|
||||
void clear();
|
||||
|
||||
std::unordered_map<std::string, program_variable>::iterator begin();
|
||||
std::unordered_map<std::string, program_variable>::iterator end();
|
||||
};
|
||||
|
||||
struct program_info
|
||||
{
|
||||
std::string text;
|
||||
program_variables vars;
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
class code_builder
|
||||
{
|
||||
struct code_line
|
||||
{
|
||||
int tab_before;
|
||||
int tab_after;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct branch_info
|
||||
{
|
||||
size_t index;
|
||||
std::string condition;
|
||||
};
|
||||
|
||||
struct entry
|
||||
{
|
||||
std::list<branch_info> branch_to;
|
||||
std::list<branch_info> branch_from;
|
||||
std::list<code_line> code_lines;
|
||||
};
|
||||
|
||||
std::map<size_t, entry> m_entries;
|
||||
size_t next_index = 0;
|
||||
|
||||
public:
|
||||
void add_code_block(size_t index, const std::string& lines, int tab_before = 0, int tab_after = 0, bool to_end = true);
|
||||
void branch(size_t from, size_t to, const std::string& condition = {});
|
||||
void build_branches();
|
||||
std::string build_code() const;
|
||||
std::string build();
|
||||
};
|
||||
|
||||
struct program_decompiler_core
|
||||
{
|
||||
code_builder builder;
|
||||
program_info info;
|
||||
|
||||
program_info& decompile();
|
||||
};
|
||||
}
|
||||
8
rsx_program_decompiler/rsx/rsx_vertex_program.h
Normal file
8
rsx_program_decompiler/rsx/rsx_vertex_program.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace vertex_program
|
||||
{
|
||||
}
|
||||
}
|
||||
13
rsx_program_decompiler/rsx/rsx_vertex_program_decompiler.cpp
Normal file
13
rsx_program_decompiler/rsx/rsx_vertex_program_decompiler.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "rsx_vertex_program_decompiler.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace vertex_program
|
||||
{
|
||||
void decompiler::test()
|
||||
{
|
||||
std::cout << __FUNCTION__ << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
rsx_program_decompiler/rsx/rsx_vertex_program_decompiler.h
Normal file
14
rsx_program_decompiler/rsx/rsx_vertex_program_decompiler.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "rsx_vertex_program.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace vertex_program
|
||||
{
|
||||
class decompiler
|
||||
{
|
||||
public:
|
||||
void test();
|
||||
};
|
||||
}
|
||||
}
|
||||
232
rsx_program_decompiler/rsx_program_decompiler.vcxproj
Normal file
232
rsx_program_decompiler/rsx_program_decompiler.vcxproj
Normal file
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{f7aad20d-bcfa-4b17-a178-bd8606b4e1fe}</ProjectGuid>
|
||||
<Keyword>StaticLibrary</Keyword>
|
||||
<ProjectName>rsx_program_decompiler</ProjectName>
|
||||
<RootNamespace>rsx_program_decompiler</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.10240.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<OutDir>$(SolutionDir)lib\</OutDir>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)lib\</OutDir>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)lib\</OutDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)lib\</OutDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)lib\</OutDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)lib\</OutDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="fmt.h" />
|
||||
<ClInclude Include="rsx\rsx.h" />
|
||||
<ClInclude Include="rsx\rsx_fragment_program.h" />
|
||||
<ClInclude Include="rsx\rsx_fragment_program_decompiler.h" />
|
||||
<ClInclude Include="rsx\rsx_glsl_fragment_program_decompiler.h" />
|
||||
<ClInclude Include="rsx\rsx_glsl_vertex_program_decompiler.h" />
|
||||
<ClInclude Include="rsx\rsx_program_decompiler.h" />
|
||||
<ClInclude Include="rsx\rsx_vertex_program.h" />
|
||||
<ClInclude Include="rsx\rsx_vertex_program_decompiler.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="fmt.cpp" />
|
||||
<ClCompile Include="rsx\rsx_fragment_program_decompiler.cpp" />
|
||||
<ClCompile Include="rsx\rsx_program_decompiler.cpp" />
|
||||
<ClCompile Include="rsx\rsx_vertex_program_decompiler.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="rsx">
|
||||
<UniqueIdentifier>{151c23cf-edbe-4ca4-9c6c-62d197083805}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="rsx\rsx.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_fragment_program.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_fragment_program_decompiler.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_glsl_fragment_program_decompiler.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_glsl_vertex_program_decompiler.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_vertex_program.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_vertex_program_decompiler.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rsx\rsx_program_decompiler.h">
|
||||
<Filter>rsx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="fmt.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="rsx\rsx_program_decompiler.cpp">
|
||||
<Filter>rsx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rsx\rsx_fragment_program_decompiler.cpp">
|
||||
<Filter>rsx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rsx\rsx_vertex_program_decompiler.cpp">
|
||||
<Filter>rsx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="fmt.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
57
rsx_program_decompiler_gui/rsx_program_decompiler_gui.cpp
Normal file
57
rsx_program_decompiler_gui/rsx_program_decompiler_gui.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <rsx/rsx.h>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
|
||||
template<typename DecompilerType>
|
||||
int process(const std::string& path)
|
||||
{
|
||||
if (auto &file_stream = std::ifstream{ path, std::ios::binary })
|
||||
{
|
||||
u32 buffer[512 * 4];
|
||||
u32 size = file_stream.read((char*)buffer, sizeof(buffer)).gcount();
|
||||
auto info = DecompilerType{ buffer, size }.decompile();
|
||||
std::cout << info.text;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -3;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, std::function<int(const std::string&)>> g_profiles =
|
||||
{
|
||||
{ "fp_glsl", process<rsx::fragment_program::glsl_decompiler> },
|
||||
//{ "vp_glsl", process<rsx::vertex_program::glsl_decompiler> }
|
||||
};
|
||||
|
||||
void help()
|
||||
{
|
||||
std::cout << "usage: [profile] <path to ucode>" << std::endl;
|
||||
std::cout << "supported profiles: ";
|
||||
for (auto &profile : g_profiles)
|
||||
{
|
||||
std::cout << profile.first << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto found = g_profiles.find(std::string(argv[1]).substr(1));
|
||||
|
||||
if (found == g_profiles.end())
|
||||
{
|
||||
help();
|
||||
return -2;
|
||||
}
|
||||
|
||||
return found->second(argv[2]);
|
||||
}
|
||||
|
||||
182
rsx_program_decompiler_gui/rsx_program_decompiler_gui.vcxproj
Normal file
182
rsx_program_decompiler_gui/rsx_program_decompiler_gui.vcxproj
Normal file
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{9DAF4DF3-0E31-4C55-B367-6992C35F89CE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>rsx_program_decompiler_gui</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.10240.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<LibraryPath>$(SolutionDir)lib\;$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>$(SolutionDir)rsx_program_decompiler\;$(IncludePath)</IncludePath>
|
||||
<OutDir>$(SolutionDir)bin\</OutDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<LibraryPath>$(SolutionDir)lib\;$(LibraryPath)</LibraryPath>
|
||||
<OutDir>$(SolutionDir)bin\</OutDir>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
<IncludePath>$(SolutionDir)rsx_program_decompiler\;$(IncludePath)</IncludePath>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)bin\</OutDir>
|
||||
<LibraryPath>$(SolutionDir)lib\;$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>$(SolutionDir)rsx_program_decompiler\;$(IncludePath)</IncludePath>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||
<OutDir>$(SolutionDir)bin\</OutDir>
|
||||
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
|
||||
<LibraryPath>$(SolutionDir)lib\;$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>$(SolutionDir)rsx_program_decompiler\;$(IncludePath)</IncludePath>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>rsx_program_decompiler-$(Platform)-$(Configuration).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>rsx_program_decompiler-$(Platform)-$(Configuration).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>rsx_program_decompiler-$(Platform)-$(Configuration).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnablePREfast>true</EnablePREfast>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>rsx_program_decompiler-$(Platform)-$(Configuration).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="rsx_program_decompiler_gui.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="rsx_program_decompiler_gui.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode >out.glsl</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode >out.glsl</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode >out.glsl</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode >out.glsl</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user