mirror of
https://github.com/RPCS3/rsx_program_decompiler.git
synced 2025-04-12 10:32:55 +00:00
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:
parent
c846f41e13
commit
50810f548a
102
rsx_program_decompiler/CgBinaryProgram.h
Normal file
102
rsx_program_decompiler/CgBinaryProgram.h
Normal 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];
|
||||
};
|
||||
}
|
@ -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 §ion : 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 >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 >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 >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 >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>
|
||||
|
176
rsx_program_decompiler_lib/elf64.h
Normal file
176
rsx_program_decompiler_lib/elf64.h
Normal 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;
|
||||
};
|
||||
}
|
199
rsx_program_decompiler_lib/endianness.h
Normal file
199
rsx_program_decompiler_lib/endianness.h
Normal 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>;
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
#include "rsx_fragment_program_decompiler.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace fragment_program
|
||||
{
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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)";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user