Implemented extraction cgbin from elf64.

Implemented extraction ucode from cgbin.
fp_glsl:
 Fixed argument constant loading
 Fixed ADD, MUL, MAD instructions
 Minor fixes
This commit is contained in:
DH 2015-08-05 18:24:33 +03:00
parent c846f41e13
commit 50810f548a
14 changed files with 792 additions and 81 deletions

View File

@ -0,0 +1,102 @@
#pragma once
#include <endianness.h>
namespace cg
{
using namespace endianness;
typedef be<u32> CGprofile;
typedef be<s32> CGbool;
typedef be<u32> CGresource;
typedef be<u32> CGenum;
typedef be<u32> CGtype;
typedef be<u32> CgBinaryOffset;
typedef CgBinaryOffset CgBinaryEmbeddedConstantOffset;
typedef CgBinaryOffset CgBinaryFloatOffset;
typedef CgBinaryOffset CgBinaryStringOffset;
typedef CgBinaryOffset CgBinaryParameterOffset;
// fragment programs have their constants embedded in the microcode
struct CgBinaryEmbeddedConstant
{
be<u32> ucodeCount; // occurances
be<u32> ucodeOffset[1]; // offsets that need to be patched follow
};
// describe a binary program parameter (CgParameter is opaque)
struct CgBinaryParameter
{
CGtype type; // cgGetParameterType()
CGresource res; // cgGetParameterResource()
CGenum var; // cgGetParameterVariability()
be<s32> resIndex; // cgGetParameterResourceIndex()
CgBinaryStringOffset name; // cgGetParameterName()
CgBinaryFloatOffset defaultValue; // default constant value
CgBinaryEmbeddedConstantOffset embeddedConst; // embedded constant information
CgBinaryStringOffset semantic; // cgGetParameterSemantic()
CGenum direction; // cgGetParameterDirection()
be<s32> paramno; // 0..n: cgGetParameterIndex() -1: globals
CGbool isReferenced; // cgIsParameterReferenced()
CGbool isShared; // cgIsParameterShared()
};
// attributes needed for vshaders
struct CgBinaryVertexProgram
{
be<u32> instructionCount; // #instructions
be<u32> instructionSlot; // load address (indexed reads!)
be<u32> registerCount; // R registers count
be<u32> attributeInputMask; // attributes vs reads from
be<u32> attributeOutputMask; // attributes vs writes (uses SET_VERTEX_ATTRIB_OUTPUT_MASK bits)
be<u32> userClipMask; // user clip plane enables (for SET_USER_CLIP_PLANE_CONTROL)
};
typedef enum
{
CgBinaryPTTNone = 0,
CgBinaryPTT2x16 = 1,
CgBinaryPTT1x32 = 2
} CgBinaryPartialTexType;
// attributes needed for pshaders
struct CgBinaryFragmentProgram
{
be<u32> instructionCount; // #instructions
be<u32> attributeInputMask; // attributes fp reads (uses SET_VERTEX_ATTRIB_OUTPUT_MASK bits)
be<u32> partialTexType; // texid 0..15 use two bits each marking whether the texture format requires partial load: see CgBinaryPartialTexType
be<u16> texCoordsInputMask; // tex coords used by frag prog. (tex<n> is bit n)
be<u16> texCoords2D; // tex coords that are 2d (tex<n> is bit n)
be<u16> texCoordsCentroid; // tex coords that are centroid (tex<n> is bit n)
u8 registerCount; // R registers count
u8 outputFromH0; // final color from R0 or H0
u8 depthReplace; // fp generated z epth value
u8 pixelKill; // fp uses kill operations
};
struct CgBinaryProgram
{
// vertex/pixel shader identification (BE/LE as well)
CGprofile profile;
// binary revision (used to verify binary and driver structs match)
be<u32> binaryFormatRevision;
// total size of this struct including profile and totalSize field
be<u32> totalSize;
// parameter usually queried using cgGet[First/Next]LeafParameter
be<u32> parameterCount;
CgBinaryParameterOffset parameterArray;
// depending on profile points to a CgBinaryVertexProgram or CgBinaryFragmentProgram struct
CgBinaryOffset program;
// raw ucode data
be<u32> ucodeSize;
CgBinaryOffset ucode;
// variable length data follows
u8 data[1];
};
}

View File

@ -3,31 +3,158 @@
#include <iostream>
#include <fstream>
#include <functional>
#include "CgBinaryProgram.h"
#include "elf64.h"
#include <cctype>
template<typename DecompilerType>
int process(const std::string& path)
int process_ucode(const std::string& ipath, const std::string& opath)
{
if (auto &file_stream = std::ifstream{ path, std::ios::binary })
if (auto &ifile_stream = std::ifstream{ ipath, 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;
if (auto &ofile_stream = std::ofstream{ opath })
{
u32 buffer[512 * 4];
u32 size = ifile_stream.read((char*)buffer, sizeof(buffer)).gcount();
auto info = DecompilerType{ buffer, size }.decompile();
ofile_stream << info.text;
return 0;
}
return -4;
}
return -3;
}
const std::unordered_map<std::string, std::function<int(const std::string&)>> g_profiles =
int extract_ucode(const std::string& ipath, const std::string& opath)
{
{ "fp_glsl", process<rsx::fragment_program::glsl_decompiler> },
//{ "vp_glsl", process<rsx::vertex_program::glsl_decompiler> }
if (auto &ifile_stream = std::ifstream{ ipath, std::ios::binary | std::ios::ate })
{
std::size_t input_size = ifile_stream.tellg();
ifile_stream.seekg(0, ifile_stream.beg);
if (auto &ofile_stream = std::ofstream{ opath, std::ios::binary })
{
std::vector<u8> buffer(input_size);
ifile_stream.read((char*)buffer.data(), buffer.size());
cg::CgBinaryProgram* program = (cg::CgBinaryProgram*)buffer.data();
ofile_stream.write((char*)(buffer.data() + program->ucode), program->ucodeSize);
return 0;
}
return -4;
}
return -3;
}
int extract_objects_from_elf(const std::string& elf_path, const std::string& output_path, std::vector<std::string> objects_names)
{
if (std::ifstream& ifile = std::ifstream(elf_path, std::ios::binary))
{
using namespace endianness;
elf64::ehdr ehdr;
ifile.read((char*)&ehdr, sizeof(elf64::ehdr));
endian data_endian = ehdr.e_ident.data_encoding == elf::elf_encoding::big_endian ? endian::big : endian::little;
u64 symoffset = 0;
u64 symcount = 0;
u64 symstroffset = 0;
u64 symstrsize = 0;
std::vector<elf64::shdr> sections(unpack(ehdr.e_shnum, data_endian));
{
ifile.seekg(unpack(ehdr.e_shoff, data_endian));
ifile.read((char*)sections.data(), sections.size() * sizeof(elf64::shdr));
for (auto &section : sections)
{
if (unpack(section.sh_type, data_endian) == elf64::sh_type::symtab)
{
symoffset = unpack(section.sh_offset, data_endian);
symcount = unpack(section.sh_size, data_endian) / sizeof(elf64::sym);
auto &symstr = sections[unpack(section.sh_link, data_endian)];
symstroffset = unpack(symstr.sh_offset, data_endian);
symstrsize = unpack(symstr.sh_size, data_endian);
break;
}
}
}
if (symoffset == 0 || symcount == 0)
return -3;
std::vector<char> syms_names(symstrsize);
ifile.seekg(symstroffset);
ifile.read(syms_names.data(), symstrsize);
std::vector<elf64::sym> syms(symcount);
ifile.seekg(symoffset);
ifile.read((char*)syms.data(), symcount * sizeof(elf64::sym));
if (std::ofstream& ofile = std::ofstream(output_path, std::ios::binary))
{
auto find_symbol = [&](const std::string& obj_name)
{
for (auto &sym : syms)
{
std::string name = syms_names.data() + unpack(sym.st_name, data_endian);
if (obj_name == name)
{
u16 shndx_ = unpack(sym.st_shndx, data_endian);
if (shndx_ == (u16)elf64::shndx::abs)
{
return unpack(sym.st_value, data_endian);
}
else
{
return
unpack(sections[shndx_].sh_offset, data_endian) - unpack(sections[shndx_].sh_addr, data_endian)
+ unpack(sym.st_value, data_endian);
}
break;
}
}
throw std::runtime_error("symbol '" + obj_name + "' not found.");
};
std::vector<u64> symbols;
for (auto str : objects_names)
symbols.push_back(find_symbol(str));
assert(symbols.size() == 2);
std::vector<char> buffer(symbols[1] - symbols[0]);
ifile.seekg(symbols[0]);
ifile.read(buffer.data(), buffer.size());
ofile.write(buffer.data(), buffer.size());
}
}
return 0;
}
const std::unordered_map<std::string, std::function<int(const std::string&, const std::string&)>> g_profiles =
{
{ "fp_glsl", process_ucode<rsx::fragment_program::glsl_decompiler> },
//{ "vp_glsl", process_ucode<rsx::vertex_program::glsl_decompiler> },
{ "cgbin_extract_ucode", extract_ucode },
{ "elf_extract_fp_cgbin", [](const std::string& inp, const std::string& outp) { return extract_objects_from_elf(inp, outp, { "_binary_fp_shader_fpo_start", "_binary_fp_shader_fpo_end" }); } },
{ "elf_extract_vp_cgbin", [](const std::string& inp, const std::string& outp) { return extract_objects_from_elf(inp, outp, { "_binary_vp_shader_vpo_start", "_binary_vp_shader_vpo_end" }); } }
};
void help()
{
std::cout << "usage: [profile] <path to ucode>" << std::endl;
std::cout << "usage: [profile] <path to ucode file> <path to output file>" << std::endl;
std::cout << "supported profiles: ";
for (auto &profile : g_profiles)
{
@ -38,7 +165,7 @@ void help()
int main(int argc, char** argv)
{
if (argc != 3)
if (argc != 4)
{
help();
return -1;
@ -52,6 +179,14 @@ int main(int argc, char** argv)
return -2;
}
return found->second(argv[2]);
try
{
return found->second(argv[2], argv[3]);
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -5;
}
}

View File

@ -80,7 +80,7 @@
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>true</RunCodeAnalysis>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
@ -90,7 +90,7 @@
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
<IncludePath>$(SolutionDir)rsx_program_decompiler_lib\;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>true</RunCodeAnalysis>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
@ -100,7 +100,7 @@
<LibraryPath>$(SolutionDir)lib\;$(LibraryPath)</LibraryPath>
<IncludePath>$(SolutionDir)rsx_program_decompiler_lib\;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>true</RunCodeAnalysis>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
@ -110,7 +110,7 @@
<LibraryPath>$(SolutionDir)lib\;$(LibraryPath)</LibraryPath>
<IncludePath>$(SolutionDir)rsx_program_decompiler_lib\;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>true</RunCodeAnalysis>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -118,7 +118,7 @@
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnablePREfast>true</EnablePREfast>
<EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -132,7 +132,7 @@
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnablePREfast>true</EnablePREfast>
<EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -148,7 +148,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnablePREfast>true</EnablePREfast>
<EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -166,7 +166,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnablePREfast>true</EnablePREfast>
<EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View File

@ -1,22 +1,22 @@
<?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 &gt;out.glsl</LocalDebuggerCommandArguments>
<LocalDebuggerCommandArguments>-elf_extract_fp_cgbin rsx_fp_static_test2.ppu.elf cg.bin</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode &gt;out.glsl</LocalDebuggerCommandArguments>
<LocalDebuggerCommandArguments>-elf_extract_fp_cgbin rsx_fp_static_test2.ppu.elf cg.bin</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode &gt;out.glsl</LocalDebuggerCommandArguments>
<LocalDebuggerCommandArguments>-elf_extract_fp_cgbin rsx_fp_static_test2.ppu.elf cg.bin</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>-fp_glsl transform_program.ucode &gt;out.glsl</LocalDebuggerCommandArguments>
<LocalDebuggerCommandArguments>-elf_extract_fp_cgbin rsx_fp_static_test2.ppu.elf cg.bin</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerWorkingDirectory>$(SolutionDir)bin\</LocalDebuggerWorkingDirectory>
</PropertyGroup>

View File

@ -0,0 +1,176 @@
#pragma once
#include "endianness.h"
namespace elf
{
enum class elf_class : u8
{
elf32 = 1,
elf64 = 2
};
enum class elf_encoding : u8
{
little_endian = 1,
big_endian = 2
};
enum class osabi : u8
{
sysv = 0,
hpux = 1,
standalone = 255
};
struct alignas(16) ident_t
{
u8 magic[4];
elf_class file_class;
elf_encoding data_encoding;
u8 file_version;
osabi osabi;
u8 abi_version;
u8 pad[6];
u8 ident_size;
};
static_assert(sizeof(ident_t) == 16, "bad ident_t implementation");
}
namespace elf64
{
using endianness::ue;
using addr_t = ue<u64>;
using off_t = ue<u64>;
using half_t = ue<u16>;
using word_t = ue<u32>;
using sword_t = ue<s32>;
using xword_t = ue<u64>;
using sxword_t = ue<s64>;
enum class elf_type : half_t::type
{
none = 0,
rel = 1,
exec = 2,
dyn = 3,
core = 4,
loos = 0xfe00,
hios = 0xfeff,
loproc = 0xff00,
hiproc = 0xffff
};
enum class sh_type : word_t::type
{
null,
progbits,
symtab,
strtab,
rela,
hash,
dynamic,
note,
shlib,
dynsym,
loos = 0x6000'0000,
hios = 0x6fff'ffff,
loproc = 0x7000'0000,
hiproc = 0x7fff'ffff
};
enum class sh_flags : xword_t::type
{
write = 1,
alloc = 2,
execinstr = 4,
maskos = 0x0f00'0000,
maskproc = 0xf000'0000,
};
enum class shndx
{
undef = 0,
loproc = 0xff00,
hiproc = 0xff1f,
loos = 0xff20,
hioc = 0xff3f,
abs = 0xfff1,
common = 0xffff2
};
enum class st_binding : u8
{
local,
global,
weak,
};
static std::string to_string(st_binding value)
{
switch (value)
{
case st_binding::local: return "local";
case st_binding::global: return "global";
case st_binding::weak: return "weak";
}
return "unknown" + std::to_string((u8)value);
}
enum class st_type : u8
{
notype,
object,
func,
section,
file
};
struct ehdr
{
elf::ident_t e_ident;
ue<elf_type> e_type;
half_t e_machine;
word_t e_version;
addr_t e_entry;
off_t e_phoff;
off_t e_shoff;
word_t e_flags;
half_t e_ehsize;
half_t e_phentsize;
half_t e_phnum;
half_t e_shentsize;
half_t e_shnum;
half_t e_shstrndx;
};
struct shdr
{
word_t sh_name;
ue<sh_type> sh_type;
ue<sh_flags> sh_flags;
addr_t sh_addr;
off_t sh_offset;
xword_t sh_size;
word_t sh_link;
word_t sh_info;
xword_t sh_addralign;
xword_t sh_entsize;
};
struct sym
{
word_t st_name;
struct alignas(1)
{
st_binding binding : 4;
st_type type : 4;
} st_info;
u8 st_other;
half_t st_shndx;
addr_t st_value;
xword_t st_size;
};
}

View File

@ -0,0 +1,199 @@
#pragma once
#include "fmt.h"
namespace endianness
{
template<std::size_t size>
struct swap_impl;
template<> struct swap_impl<1>
{
template<typename T>
constexpr static T swap(T value)
{
return value;
}
};
template<> struct swap_impl<2>
{
template<typename T>
constexpr static T swap(T value)
{
return ((value >> 8) & 0xff) | ((value << 8) & 0xff00);
}
};
template<> struct swap_impl<4>
{
template<typename T>
constexpr static T swap(T value)
{
return
((value >> 24) & 0x000000ff) |
((value >> 8) & 0x0000ff00) |
((value << 8) & 0x00ff0000) |
((value << 24) & 0xff000000);
}
};
template<> struct swap_impl<8>
{
template<typename T>
constexpr static T swap(T value)
{
return
((value >> 56) & 0x00000000000000ff) |
((value >> 40) & 0x000000000000ff00) |
((value >> 24) & 0x0000000000ff0000) |
((value >> 8) & 0x00000000ff000000) |
((value << 8) & 0x000000ff00000000) |
((value << 24) & 0x0000ff0000000000) |
((value << 40) & 0x00ff000000000000) |
((value << 56) & 0xff00000000000000);
}
};
template<typename T>
constexpr T swap(T value)
{
return swap_impl<sizeof(T)>::swap(value);
}
template<std::size_t size>
struct simple_type;
template<> struct simple_type<1> { using type = u8; };
template<> struct simple_type<2> { using type = u16; };
template<> struct simple_type<4> { using type = u32; };
template<> struct simple_type<8> { using type = u64; };
template<std::size_t size>
using simple_type_t = typename simple_type<size>::type;
template<typename T>
using simple_type_for_t = typename simple_type<sizeof(T)>::type;
template<typename Type, typename EndiannessImpl>
class alignas(sizeof(Type)) endianness_t
{
public:
using type = std::remove_cv_t<Type>;
using storage_type = simple_type_for_t<type>;
private:
storage_type m_packed_value;
constexpr type unpack() const
{
return EndiannessImpl::unpack(m_packed_value);
}
void pack(type value)
{
m_packed_value = EndiannessImpl::pack<storage_type>(value);
}
public:
constexpr endianness_t(type value) : m_packed_value(EndiannessImpl::pack<storage_type>(value))
{
}
constexpr endianness_t() : m_packed_value{}
{
}
constexpr operator type() const
{
return EndiannessImpl::unpack(m_packed_value);
}
endianness_t& operator +=(const endianness_t& rhs) { pack(unpack() + rhs.unpack()); return *this; }
endianness_t& operator -=(const endianness_t& rhs) { pack(unpack() - rhs.unpack()); return *this; }
endianness_t& operator *=(const endianness_t& rhs) { pack(unpack() * rhs.unpack()); return *this; }
endianness_t& operator /=(const endianness_t& rhs) { pack(unpack() / rhs.unpack()); return *this; }
endianness_t& operator %=(const endianness_t& rhs) { pack(unpack() % rhs.unpack()); return *this; }
endianness_t& operator &=(const endianness_t& rhs) { pack(unpack() & rhs.unpack()); return *this; }
endianness_t& operator |=(const endianness_t& rhs) { pack(unpack() | rhs.unpack()); return *this; }
endianness_t& operator ^=(const endianness_t& rhs) { pack(unpack() ^ rhs.unpack()); return *this; }
endianness_t& operator >>=(int shift) { pack(unpack() >> shift); return *this; }
endianness_t& operator <<=(int shift) { pack(unpack() << shift); return *this; }
endianness_t& operator =(const type& rhs) { pack(rhs); return *this; }
};
struct swapped_endianness_impl
{
template<typename RT, typename T>
static constexpr RT pack(T value)
{
return swap(value);
}
template<typename T>
static constexpr T unpack(T value)
{
return swap(value);
}
};
struct native_endianness_impl
{
template<typename RT, typename T>
static constexpr RT pack(T value)
{
return value;
}
template<typename T>
static constexpr T unpack(T value)
{
return value;
}
};
enum class endian
{
little,
big,
unknown
};
static constexpr endian native_endianness =
(0x01234567 & 0xff) == 0x01 ? endian::big :
(0x01234567 & 0xff) == 0x67 ? endian::little : endian::unknown;
static_assert(native_endianness != endian::unknown, "unsupported endianness");
template<typename T> using be_t = endianness_t<T, std::conditional_t<native_endianness == endian::big, native_endianness_impl, swapped_endianness_impl>>;
template<typename T> using le_t = endianness_t<T, std::conditional_t<native_endianness == endian::little, native_endianness_impl, swapped_endianness_impl>>;
template<typename T> using ne_t = endianness_t<T, native_endianness_impl>;
template<typename T> using be = std::conditional_t<native_endianness == endian::big, T, be_t<T>>;
template<typename T> using le = std::conditional_t<native_endianness == endian::little, T, le_t<T>>;
template<typename T>
struct alignas(sizeof(T)) unknown_endian_t
{
using type = std::remove_cv_t<T>;
type packed_data;
};
template<typename T> constexpr T unpack(const unknown_endian_t<T>& data, endian endian)
{
return endian == native_endianness ? data.packed_data : (T)swap((simple_type_for_t<T>)data.packed_data);
}
template<typename T> constexpr unknown_endian_t<T> pack(T value, endian endian = native_endianness)
{
return{ endian == native_endianness ? value : (T)swap((simple_type_for_t<T>)value) };
}
template<typename T> void pack(unknown_endian_t<T>& resut, T value, endian endian = native_endianness)
{
resut = pack(value, endian);
}
template<typename T>
using ue = unknown_endian_t<T>;
}

View File

@ -76,7 +76,7 @@ namespace rsx
RET = 0x45 // Return
};
union OPDEST
union alignas(4) OPDEST
{
u32 HEX;
@ -100,7 +100,7 @@ namespace rsx
};
};
union SRC0
union alignas(4) SRC0
{
u32 HEX;
@ -127,7 +127,7 @@ namespace rsx
};
};
union SRC1
union alignas(4) SRC1
{
u32 HEX;
@ -165,7 +165,7 @@ namespace rsx
};
};
union SRC2
union alignas(4) SRC2
{
u32 HEX;

View File

@ -1,8 +0,0 @@
#include "rsx_fragment_program_decompiler.h"
namespace rsx
{
namespace fragment_program
{
}
}

View File

@ -2,6 +2,7 @@
#include "rsx_fragment_program.h"
#include "rsx_program_decompiler.h"
#include <unordered_set>
#include "endianness.h"
namespace rsx
{
@ -21,9 +22,9 @@ namespace rsx
struct cond {};
template<typename T> struct arg {};
union ucode_data
union alignas(16) ucode_data
{
struct
struct alignas(16)
{
OPDEST dst;
SRC0 src0;
@ -32,8 +33,22 @@ namespace rsx
};
u32 data[4];
ucode_data unpack()
{
ucode_data result;
result.data[0] = endianness::swap(data[0]); result.data[0] = (result.data[0] << 16) | (result.data[0] >> 16);
result.data[1] = endianness::swap(data[1]); result.data[1] = (result.data[1] << 16) | (result.data[1] >> 16);
result.data[2] = endianness::swap(data[2]); result.data[2] = (result.data[2] << 16) | (result.data[2] >> 16);
result.data[3] = endianness::swap(data[3]); result.data[3] = (result.data[3] << 16) | (result.data[3] >> 16);
return result;
}
};
static_assert(sizeof(ucode_data) == 4 * 4, "bad ucode_data");
template<typename decompiler_impl>
class decompiler;
@ -53,9 +68,9 @@ namespace rsx
};
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 MUL = instruction < opcode::MUL, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
using ADD = instruction < opcode::ADD, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 4>> >;
using MAD = instruction < opcode::MAD, H | C, dest<4>, arg<src<0, 4>>, arg<src<1, 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>> >;
@ -151,9 +166,9 @@ namespace rsx
{
ucode_data* ucode_ptr;
u32 ucode_size;
u32 ucode_index;
public:
int code_line_index = 0;
bool is_next_constant = false;
fragment_program::info info;
@ -212,23 +227,54 @@ namespace rsx
case 2: get_data_from(src2); break;
}
variable.name = is_fp16 ? "H" : "R";
bool need_declare = true;
switch (type)
{
case 0: //temporary register
variable.name = is_fp16 ? "H" : "R";
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;
if (0)
{
variable.index = dst.src_attr_reg_num;
variable.name = "fragment_input";
variable.array_size = 15;
}
else
{
static const std::string register_table[] =
{
"position",
"diff_color", "spec_color",
"fogc",
"tc0", "tc1", "tc2", "tc3", "tc4", "tc5", "tc6", "tc7", "tc8", "tc9",
"ssa"
};
variable.index = ~0;
variable.type = program_variable_type::input;
variable.name = register_table[dst.src_attr_reg_num];
}
}
break;
case 2: //constant
need_declare = false;
decompiler.is_next_constant = true;
variable.constant.type = program_constant_type::f32;
{
ucode_data constant = decompiler.ucode_ptr[decompiler.ucode_index + 1].unpack();
variable.constant.x.i32_value = constant.data[0];
variable.constant.y.i32_value = constant.data[1];
variable.constant.z.i32_value = constant.data[2];
variable.constant.w.i32_value = constant.data[3];
}
break;
}
@ -350,7 +396,7 @@ namespace rsx
void set_code_line(const std::string &code_line)
{
program_decompiler_core::builder.add_code_block(code_line_index, code_line);
program_decompiler_core::builder.add_code_block(ucode_index, code_line);
}
template<opcode id, u32 flags, int count>
@ -359,8 +405,10 @@ namespace rsx
set_code_line(decompiler_impl::set_dst<id, flags, count>(this, arg0, arg1, arg2));
}
void unknown_instruction()
void unknown_instruction(u32 opcode)
{
throw std::runtime_error("unimplemented instruction '" + instructions_names[opcode] + "' (" + std::to_string(opcode) + ") #"
+ std::to_string(ucode_index));
}
std::string function_begin(const std::string& name)
@ -391,29 +439,49 @@ namespace rsx
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)
for (ucode_index = 0; ucode_index < ucode_size; ++ucode_index)
{
ucode = ucode_ptr[i];
if (is_next_constant)
{
is_next_constant = false;
continue;
}
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);
ucode = ucode_ptr[ucode_index].unpack();
const u32 opcode = ucode.dst.opcode | (ucode.src1.opcode_is_branch << 6);
auto function = instructions[opcode];
//if (ucode_index == 20)
// break;
if (function)
if (opcode != 0)
{
function(*this);
code_line_index++;
}
else
{
unknown_instruction();
auto function = instructions[opcode];
try
{
if (function)
{
function(*this);
}
else
{
unknown_instruction(opcode);
}
}
catch (...)
{
std::exception_ptr ex_p = std::current_exception();
try
{
std::rethrow_exception(ex_p);
}
catch (const std::exception& ex)
{
throw std::out_of_range(ex.what());
}
}
}
if (ucode.dst.end)
@ -425,9 +493,8 @@ namespace rsx
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);
builder.add_code_block(ucode_index, function_end(), -1, 0);
builder.add_code_block(ucode_index, end);
info.text = builder.build();
return info;

View File

@ -1,6 +1,7 @@
#pragma once
#include "rsx_program_decompiler.h"
#include "rsx_fragment_program_decompiler.h"
#include <cassert>
namespace rsx
{
@ -35,7 +36,8 @@ namespace rsx
}
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";
(var.second.array_size ? ("[" + std::to_string(var.second.array_size + 1) + "]") :
(var.second.index != ~0 ? std::to_string(var.second.index) : std::string{})) + ";\n";
}
for (auto &func : dec->functions_set)
@ -56,7 +58,21 @@ namespace rsx
__forceinline static std::string variable_to_string(const program_variable& arg)
{
fmt::string result = arg;
fmt::string result;
if (arg.constant.type == program_constant_type::none)
{
result = arg;
}
else
{
assert(arg.constant.type == program_constant_type::f32);
result = fmt::format("vec4(%g, %g, %g, %g)",
arg.constant.x.f32_value,
arg.constant.y.f32_value,
arg.constant.z.f32_value,
arg.constant.w.f32_value);
}
if (arg.is_abs)
result = "abs(" + result + ")";
@ -181,11 +197,12 @@ namespace rsx
break;
case 1: //fp16, clamping
value += "clamp(" + value + ", -65536, 65536)";
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.");
value = "clamp(" + value + ", 0, 0)";
//throw std::runtime_error("fragment program decompiler: unimplemented precision.");
}
switch (dec->ucode.src1.scale)
@ -204,7 +221,7 @@ namespace rsx
if (dec->ucode.dst.saturate)
{
value += "clamp(" + value + ", 0, 1)";
value = "clamp(" + value + ", 0, 1)";
}
}

View File

@ -80,7 +80,7 @@ namespace rsx
if (array_size)
return name + "[" + std::to_string(index) + "]";
return name + std::to_string(index);
return index != ~0 ? name + std::to_string(index) : name;
}
std::string program_variable::append_dot_if_not_empty(const std::string& string) const
@ -90,7 +90,7 @@ namespace rsx
std::string program_variable::storage_name() const
{
return name + (array_size ? "[" + std::to_string(array_size + 1) + "]" : std::to_string(index));
return name + (array_size ? "[" + std::to_string(array_size + 1) + "]" : (index != ~0 ? std::to_string(index) : std::string{}));
}
std::string program_variable::to_string() const
@ -105,7 +105,7 @@ namespace rsx
bool program_variable::is_null() const
{
return name.empty();
return name.empty() && constant.type == program_constant_type::none;
}
program_variable::operator bool() const

View File

@ -29,6 +29,31 @@ namespace rsx
std::string to_string();
};
enum class program_constant_type
{
none,
f32,
f64,
i32,
i64
};
struct program_constant
{
program_constant_type type;
union swizzle_t
{
float f32_value;
double f64_value;
u32 i32_value;
u64 i64_value;
};
swizzle_t x, y, z, w;
};
struct program_variable
{
std::string name;
@ -40,6 +65,8 @@ namespace rsx
u32 array_size;
bool is_neg;
bool is_abs;
std::string initialization;
program_constant constant;
private:
std::string to_string_impl() const;

View File

@ -101,7 +101,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>true</RunCodeAnalysis>
<RunCodeAnalysis>false</RunCodeAnalysis>
<OutDir>$(SolutionDir)lib\</OutDir>
<IntDir>tmp\$(Platform)\$(Configuration)\</IntDir>
<TargetName>rsx_program_decompiler-$(Platform)-$(Configuration)</TargetName>
@ -141,7 +141,7 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<EnablePREfast>true</EnablePREfast>
<EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -222,7 +222,6 @@
</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>

View File

@ -36,9 +36,6 @@
<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>