mirror of
https://github.com/hedge-dev/XenonRecomp.git
synced 2026-01-21 20:05:58 +00:00
Rebrand to XenonRecomp.
This commit is contained in:
24
XenonRecomp/CMakeLists.txt
Normal file
24
XenonRecomp/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required (VERSION 3.8)
|
||||
|
||||
project("XenonRecomp")
|
||||
|
||||
add_executable(XenonRecomp
|
||||
"main.cpp"
|
||||
"recompiler.cpp"
|
||||
"test_recompiler.cpp"
|
||||
"recompiler_config.cpp")
|
||||
|
||||
target_precompile_headers(XenonRecomp PUBLIC "pch.h")
|
||||
|
||||
target_link_libraries(XenonRecomp PRIVATE
|
||||
LibXenonAnalyse
|
||||
XenonUtils
|
||||
fmt::fmt
|
||||
tomlplusplus::tomlplusplus
|
||||
xxHash::xxhash)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
target_compile_options(XenonRecomp PRIVATE -Wno-switch -Wno-unused-variable -Wno-null-arithmetic)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(XenonRecomp PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
42
XenonRecomp/main.cpp
Normal file
42
XenonRecomp/main.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "pch.h"
|
||||
#include "test_recompiler.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const char* path =
|
||||
#ifdef XENON_RECOMP_CONFIG_FILE_PATH
|
||||
XENON_RECOMP_CONFIG_FILE_PATH
|
||||
#else
|
||||
argv[1]
|
||||
#endif
|
||||
;
|
||||
|
||||
if (std::filesystem::is_regular_file(path))
|
||||
{
|
||||
Recompiler recompiler;
|
||||
recompiler.LoadConfig(path);
|
||||
recompiler.Analyse();
|
||||
|
||||
auto entry = recompiler.image.symbols.find(recompiler.image.entry_point);
|
||||
if (entry != recompiler.image.symbols.end())
|
||||
{
|
||||
entry->name = "_xstart";
|
||||
}
|
||||
|
||||
const char* headerFilePath =
|
||||
#ifdef XENON_RECOMP_HEADER_FILE_PATH
|
||||
XENON_RECOMP_HEADER_FILE_PATH
|
||||
#else
|
||||
argv[2]
|
||||
#endif
|
||||
;
|
||||
|
||||
recompiler.Recompile(headerFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
TestRecompiler::RecompileTests(path, argv[2]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
19
XenonRecomp/pch.h
Normal file
19
XenonRecomp/pch.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <charconv>
|
||||
#include <disasm.h>
|
||||
#include <file.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <function.h>
|
||||
#include <image.h>
|
||||
#include <toml++/toml.hpp>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <xbox.h>
|
||||
#include <xxhash.h>
|
||||
#include <fmt/core.h>
|
||||
#include <xmmintrin.h>
|
||||
2526
XenonRecomp/recompiler.cpp
Normal file
2526
XenonRecomp/recompiler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
70
XenonRecomp/recompiler.h
Normal file
70
XenonRecomp/recompiler.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "recompiler_config.h"
|
||||
|
||||
struct RecompilerLocalVariables
|
||||
{
|
||||
bool ctr{};
|
||||
bool xer{};
|
||||
bool reserved{};
|
||||
bool cr[8]{};
|
||||
bool r[32]{};
|
||||
bool f[32]{};
|
||||
bool v[128]{};
|
||||
bool env{};
|
||||
bool temp{};
|
||||
bool vTemp{};
|
||||
bool ea{};
|
||||
};
|
||||
|
||||
enum class CSRState
|
||||
{
|
||||
Unknown,
|
||||
FPU,
|
||||
VMX
|
||||
};
|
||||
|
||||
struct Recompiler
|
||||
{
|
||||
// Enforce In-order Execution of I/O constant for quick comparison
|
||||
static constexpr uint32_t c_eieio = 0xAC06007C;
|
||||
Image image;
|
||||
std::vector<Function> functions;
|
||||
std::string out;
|
||||
size_t cppFileIndex = 0;
|
||||
RecompilerConfig config;
|
||||
|
||||
void LoadConfig(const std::string_view& configFilePath);
|
||||
|
||||
template<class... Args>
|
||||
void print(fmt::format_string<Args...> fmt, Args&&... args)
|
||||
{
|
||||
fmt::vformat_to(std::back_inserter(out), fmt.get(), fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void println(fmt::format_string<Args...> fmt, Args&&... args)
|
||||
{
|
||||
fmt::vformat_to(std::back_inserter(out), fmt.get(), fmt::make_format_args(args...));
|
||||
out += '\n';
|
||||
}
|
||||
|
||||
void Analyse();
|
||||
|
||||
// TODO: make a RecompileArgs struct instead this is getting messy
|
||||
bool Recompile(
|
||||
const Function& fn,
|
||||
uint32_t base,
|
||||
const ppc_insn& insn,
|
||||
const uint32_t* data,
|
||||
std::unordered_map<uint32_t, RecompilerSwitchTable>::iterator& switchTable,
|
||||
RecompilerLocalVariables& localVariables,
|
||||
CSRState& csrState);
|
||||
|
||||
bool Recompile(const Function& fn);
|
||||
|
||||
void Recompile(const std::filesystem::path& headerFilePath);
|
||||
|
||||
void SaveCurrentOutData(const std::string_view& name = std::string_view());
|
||||
};
|
||||
124
XenonRecomp/recompiler_config.cpp
Normal file
124
XenonRecomp/recompiler_config.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "recompiler_config.h"
|
||||
|
||||
void RecompilerConfig::Load(const std::string_view& configFilePath)
|
||||
{
|
||||
directoryPath = configFilePath.substr(0, configFilePath.find_last_of("\\/") + 1);
|
||||
toml::table toml = toml::parse_file(configFilePath)
|
||||
#if !TOML_EXCEPTIONS
|
||||
.table()
|
||||
#endif
|
||||
;
|
||||
|
||||
if (auto mainPtr = toml["main"].as_table())
|
||||
{
|
||||
const auto& main = *mainPtr;
|
||||
filePath = main["file_path"].value_or<std::string>("");
|
||||
outDirectoryPath = main["out_directory_path"].value_or<std::string>("");
|
||||
switchTableFilePath = main["switch_table_file_path"].value_or<std::string>("");
|
||||
|
||||
skipLr = main["skip_lr"].value_or(false);
|
||||
skipMsr = main["skip_msr"].value_or(false);
|
||||
ctrAsLocalVariable = main["ctr_as_local"].value_or(false);
|
||||
xerAsLocalVariable = main["xer_as_local"].value_or(false);
|
||||
reservedRegisterAsLocalVariable = main["reserved_as_local"].value_or(false);
|
||||
crRegistersAsLocalVariables = main["cr_as_local"].value_or(false);
|
||||
nonArgumentRegistersAsLocalVariables = main["non_argument_as_local"].value_or(false);
|
||||
nonVolatileRegistersAsLocalVariables = main["non_volatile_as_local"].value_or(false);
|
||||
|
||||
restGpr14Address = main["restgprlr_14_address"].value_or(0u);
|
||||
saveGpr14Address = main["savegprlr_14_address"].value_or(0u);
|
||||
restFpr14Address = main["restfpr_14_address"].value_or(0u);
|
||||
saveFpr14Address = main["savefpr_14_address"].value_or(0u);
|
||||
restVmx14Address = main["restvmx_14_address"].value_or(0u);
|
||||
saveVmx14Address = main["savevmx_14_address"].value_or(0u);
|
||||
restVmx64Address = main["restvmx_64_address"].value_or(0u);
|
||||
saveVmx64Address = main["savevmx_64_address"].value_or(0u);
|
||||
longJmpAddress = main["longjmp_address"].value_or(0u);
|
||||
setJmpAddress = main["setjmp_address"].value_or(0u);
|
||||
|
||||
if (auto functionsArray = main["functions"].as_array())
|
||||
{
|
||||
for (auto& func : *functionsArray)
|
||||
{
|
||||
auto& funcTable = *func.as_table();
|
||||
uint32_t address = *funcTable["address"].value<uint32_t>();
|
||||
uint32_t size = *funcTable["size"].value<uint32_t>();
|
||||
functions.emplace(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto invalidArray = main["invalid_instructions"].as_array())
|
||||
{
|
||||
for (auto& instr : *invalidArray)
|
||||
{
|
||||
auto& instrTable = *instr.as_table();
|
||||
uint32_t data = *instrTable["data"].value<uint32_t>();
|
||||
uint32_t size = *instrTable["size"].value<uint32_t>();
|
||||
invalidInstructions.emplace(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!switchTableFilePath.empty())
|
||||
{
|
||||
toml::table switchToml = toml::parse_file(directoryPath + switchTableFilePath)
|
||||
#if !TOML_EXCEPTIONS
|
||||
.table()
|
||||
#endif
|
||||
;
|
||||
if (auto switchArray = switchToml["switch"].as_array())
|
||||
{
|
||||
for (auto& entry : *switchArray)
|
||||
{
|
||||
auto& table = *entry.as_table();
|
||||
RecompilerSwitchTable switchTable;
|
||||
switchTable.r = *table["r"].value<uint32_t>();
|
||||
for (auto& label : *table["labels"].as_array())
|
||||
{
|
||||
switchTable.labels.push_back(*label.value<uint32_t>());
|
||||
}
|
||||
switchTables.emplace(*table["base"].value<uint32_t>(), std::move(switchTable));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto midAsmHookArray = toml["midasm_hook"].as_array())
|
||||
{
|
||||
for (auto& entry : *midAsmHookArray)
|
||||
{
|
||||
auto& table = *entry.as_table();
|
||||
|
||||
RecompilerMidAsmHook midAsmHook;
|
||||
midAsmHook.name = *table["name"].value<std::string>();
|
||||
if (auto registerArray = table["registers"].as_array())
|
||||
{
|
||||
for (auto& reg : *registerArray)
|
||||
midAsmHook.registers.push_back(*reg.value<std::string>());
|
||||
}
|
||||
|
||||
midAsmHook.ret = table["return"].value_or(false);
|
||||
midAsmHook.returnOnTrue = table["return_on_true"].value_or(false);
|
||||
midAsmHook.returnOnFalse = table["return_on_false"].value_or(false);
|
||||
|
||||
midAsmHook.jumpAddress = table["jump_address"].value_or(0u);
|
||||
midAsmHook.jumpAddressOnTrue = table["jump_address_on_true"].value_or(0u);
|
||||
midAsmHook.jumpAddressOnFalse = table["jump_address_on_false"].value_or(0u);
|
||||
|
||||
if ((midAsmHook.ret && midAsmHook.jumpAddress != NULL) ||
|
||||
(midAsmHook.returnOnTrue && midAsmHook.jumpAddressOnTrue != NULL) ||
|
||||
(midAsmHook.returnOnFalse && midAsmHook.jumpAddressOnFalse != NULL))
|
||||
{
|
||||
fmt::println("{}: can't return and jump at the same time", midAsmHook.name);
|
||||
}
|
||||
|
||||
if ((midAsmHook.ret || midAsmHook.jumpAddress != NULL) &&
|
||||
(midAsmHook.returnOnFalse != NULL || midAsmHook.returnOnTrue != NULL ||
|
||||
midAsmHook.jumpAddressOnFalse != NULL || midAsmHook.jumpAddressOnTrue != NULL))
|
||||
{
|
||||
fmt::println("{}: can't mix direct and conditional return/jump", midAsmHook.name);
|
||||
}
|
||||
|
||||
midAsmHooks.emplace(*table["address"].value<uint32_t>(), std::move(midAsmHook));
|
||||
}
|
||||
}
|
||||
}
|
||||
53
XenonRecomp/recompiler_config.h
Normal file
53
XenonRecomp/recompiler_config.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
struct RecompilerSwitchTable
|
||||
{
|
||||
uint32_t r;
|
||||
std::vector<uint32_t> labels;
|
||||
};
|
||||
|
||||
struct RecompilerMidAsmHook
|
||||
{
|
||||
std::string name;
|
||||
std::vector<std::string> registers;
|
||||
|
||||
bool ret = false;
|
||||
bool returnOnTrue = false;
|
||||
bool returnOnFalse = false;
|
||||
|
||||
uint32_t jumpAddress = 0;
|
||||
uint32_t jumpAddressOnTrue = 0;
|
||||
uint32_t jumpAddressOnFalse = 0;
|
||||
};
|
||||
|
||||
struct RecompilerConfig
|
||||
{
|
||||
std::string directoryPath;
|
||||
std::string filePath;
|
||||
std::string outDirectoryPath;
|
||||
std::string switchTableFilePath;
|
||||
std::unordered_map<uint32_t, RecompilerSwitchTable> switchTables;
|
||||
bool skipLr = false;
|
||||
bool ctrAsLocalVariable = false;
|
||||
bool xerAsLocalVariable = false;
|
||||
bool reservedRegisterAsLocalVariable = false;
|
||||
bool skipMsr = false;
|
||||
bool crRegistersAsLocalVariables = false;
|
||||
bool nonArgumentRegistersAsLocalVariables = false;
|
||||
bool nonVolatileRegistersAsLocalVariables = false;
|
||||
uint32_t restGpr14Address = 0;
|
||||
uint32_t saveGpr14Address = 0;
|
||||
uint32_t restFpr14Address = 0;
|
||||
uint32_t saveFpr14Address = 0;
|
||||
uint32_t restVmx14Address = 0;
|
||||
uint32_t saveVmx14Address = 0;
|
||||
uint32_t restVmx64Address = 0;
|
||||
uint32_t saveVmx64Address = 0;
|
||||
uint32_t longJmpAddress = 0;
|
||||
uint32_t setJmpAddress = 0;
|
||||
std::unordered_map<uint32_t, uint32_t> functions;
|
||||
std::unordered_map<uint32_t, uint32_t> invalidInstructions;
|
||||
std::unordered_map<uint32_t, RecompilerMidAsmHook> midAsmHooks;
|
||||
|
||||
void Load(const std::string_view& configFilePath);
|
||||
};
|
||||
283
XenonRecomp/test_recompiler.cpp
Normal file
283
XenonRecomp/test_recompiler.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "test_recompiler.h"
|
||||
|
||||
void TestRecompiler::Analyse(const std::string_view& testName)
|
||||
{
|
||||
for (const auto& section : image.sections)
|
||||
{
|
||||
if (!(section.flags & SectionFlags_Code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
size_t base = section.base;
|
||||
uint8_t* data = section.data;
|
||||
uint8_t* dataEnd = section.data + section.size;
|
||||
|
||||
while (data < dataEnd)
|
||||
{
|
||||
if (*(uint32_t*)data == 0)
|
||||
{
|
||||
data += 4;
|
||||
base += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& fn = functions.emplace_back(Function::Analyze(data, dataEnd - data, base));
|
||||
image.symbols.emplace(fmt::format("{}_{:X}", testName, fn.base), fn.base, fn.size, Symbol_Function);
|
||||
|
||||
base += fn.size;
|
||||
data += fn.size;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(functions.begin(), functions.end(), [](auto& lhs, auto& rhs) { return lhs.base < rhs.base; });
|
||||
}
|
||||
|
||||
void TestRecompiler::RecompileTests(const char* srcDirectoryPath, const char* dstDirectoryPath)
|
||||
{
|
||||
std::map<std::string, std::unordered_set<size_t>> functions;
|
||||
|
||||
for (auto& file : std::filesystem::directory_iterator(srcDirectoryPath))
|
||||
{
|
||||
if (file.path().extension() == ".o")
|
||||
{
|
||||
const auto exeFile = LoadFile(file.path().string().c_str());
|
||||
|
||||
TestRecompiler recompiler;
|
||||
recompiler.config.outDirectoryPath = dstDirectoryPath;
|
||||
recompiler.image = Image::ParseImage(exeFile.data(), exeFile.size());
|
||||
|
||||
auto stem = file.path().stem().string();
|
||||
recompiler.Analyse(stem);
|
||||
|
||||
recompiler.println("#define PPC_CONFIG_H_INCLUDED");
|
||||
recompiler.println("#include <ppc_context.h>\n");
|
||||
recompiler.println("#define __debugbreak()\n");
|
||||
|
||||
for (auto& fn : recompiler.functions)
|
||||
{
|
||||
if (recompiler.Recompile(fn))
|
||||
{
|
||||
functions[stem].emplace(fn.base);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println("Function {:X} in {} has unimplemented instructions", fn.base, stem);
|
||||
}
|
||||
}
|
||||
stem += ".cpp";
|
||||
recompiler.SaveCurrentOutData(stem);
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> symbols;
|
||||
|
||||
for (auto& [fn, addr] : functions)
|
||||
{
|
||||
std::ifstream in(fmt::format("{}/{}.dis", srcDirectoryPath, fn));
|
||||
if (in.is_open())
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(in, line))
|
||||
{
|
||||
int spaceIndex = line.find(' ');
|
||||
int bracketIndex = line.find('>');
|
||||
if (spaceIndex != std::string::npos && bracketIndex != std::string::npos)
|
||||
{
|
||||
size_t address = ~0;
|
||||
std::from_chars(&line[0], &line[spaceIndex], address, 16);
|
||||
address &= 0xFFFFF;
|
||||
if (addr.find(address) != addr.end())
|
||||
symbols.emplace(line.substr(spaceIndex + 2, bracketIndex - spaceIndex - 2), fmt::format("{}_{:X}", fn, address));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println("Unable to locate disassembly file for {}", fn);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* file = fopen(fmt::format("{}/main.cpp", dstDirectoryPath).c_str(), "w");
|
||||
std::string main;
|
||||
|
||||
fmt::println(file, "#define PPC_CONFIG_H_INCLUDED");
|
||||
fmt::println(file, "#include <ppc_context.h>");
|
||||
fmt::println(file, "#include <Windows.h>");
|
||||
fmt::println(file, "#include <print>\n");
|
||||
fmt::println(file, "#define PPC_CHECK_VALUE_U(f, lhs, rhs) if (lhs != rhs) fmt::println(#f \" \" #lhs \" EXPECTED \" #rhs \" ACTUAL {{:X}}\", lhs)\n");
|
||||
fmt::println(file, "#define PPC_CHECK_VALUE_F(f, lhs, rhs) if (lhs != rhs) fmt::println(#f \" \" #lhs \" EXPECTED \" #rhs \" ACTUAL {{}}\", lhs)\n");
|
||||
|
||||
for (auto& [fn, addr] : functions)
|
||||
{
|
||||
std::ifstream in(fmt::format("{}/../{}.s", srcDirectoryPath, fn));
|
||||
if (in.is_open())
|
||||
{
|
||||
std::string str;
|
||||
auto getline = [&]()
|
||||
{
|
||||
if (std::getline(in, str))
|
||||
{
|
||||
str.erase(str.find_last_not_of(' ') + 1);
|
||||
str.erase(0, str.find_first_not_of(' '));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
while (getline())
|
||||
{
|
||||
if (!str.empty() && str[0] != '#')
|
||||
{
|
||||
int colonIndex = str.find(':');
|
||||
if (colonIndex != std::string::npos)
|
||||
{
|
||||
auto name = str.substr(0, colonIndex);
|
||||
auto symbol = symbols.find(name);
|
||||
if (symbol != symbols.end())
|
||||
{
|
||||
fmt::println(file, "PPC_FUNC({});\n", symbol->second);
|
||||
fmt::println(file, "void {}(uint8_t* base) {{", name);
|
||||
fmt::println(file, "\tPPCContext ctx{{}};");
|
||||
fmt::println(file, "\tctx.fpscr.loadFromHost();");
|
||||
|
||||
while (getline() && !str.empty() && str[0] == '#')
|
||||
{
|
||||
if (str.size() > 1 && str[1] == '_')
|
||||
{
|
||||
int registerInIndex = str.find("REGISTER_IN");
|
||||
if (registerInIndex != std::string::npos)
|
||||
{
|
||||
int spaceIndex = str.find(' ', registerInIndex);
|
||||
int secondSpaceIndex = str.find(' ', spaceIndex + 1);
|
||||
auto reg = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1);
|
||||
if (reg[0] == 'v')
|
||||
{
|
||||
int openingBracketIndex = str.find('[', secondSpaceIndex + 1);
|
||||
int commaIndex0 = str.find(',', openingBracketIndex + 1);
|
||||
int commaIndex1 = str.find(',', commaIndex0 + 1);
|
||||
int commaIndex2 = str.find(',', commaIndex1 + 1);
|
||||
int closingBracketIndex = str.find(']', commaIndex2 + 1);
|
||||
|
||||
fmt::println(file, "\tctx.{}.u32[3] = 0x{};", reg, str.substr(openingBracketIndex + 1, commaIndex0 - openingBracketIndex - 1));
|
||||
fmt::println(file, "\tctx.{}.u32[2] = 0x{};", reg, str.substr(commaIndex0 + 2, commaIndex1 - commaIndex0 - 2));
|
||||
fmt::println(file, "\tctx.{}.u32[1] = 0x{};", reg, str.substr(commaIndex1 + 2, commaIndex2 - commaIndex1 - 2));
|
||||
fmt::println(file, "\tctx.{}.u32[0] = 0x{};", reg, str.substr(commaIndex2 + 2, closingBracketIndex - commaIndex2 - 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println(file, "\tctx.{}.{}64 = {};",
|
||||
reg,
|
||||
str.find('.', secondSpaceIndex) != std::string::npos ? 'f' : 'u',
|
||||
str.substr(secondSpaceIndex + 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int memoryInIndex = str.find("MEMORY_IN");
|
||||
if (memoryInIndex != std::string::npos)
|
||||
{
|
||||
int spaceIndex = str.find(' ', memoryInIndex);
|
||||
int secondSpaceIndex = str.find(' ', spaceIndex + 1);
|
||||
auto address = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1);
|
||||
for (size_t i = secondSpaceIndex + 1, j = 0; i < str.size(); i++)
|
||||
{
|
||||
if (str[i] != ' ')
|
||||
{
|
||||
fmt::println(file, "\tbase[0x{} + 0x{:X}] = 0x{}{};", address, j, str[i], str[i + 1]);
|
||||
++i; // the loop adds another
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (getline() && (str.empty() || str[0] != '#'))
|
||||
;
|
||||
|
||||
fmt::println(file, "\t{}(ctx, base);", symbol->second);
|
||||
|
||||
do
|
||||
{
|
||||
if (str.size() > 1 && str[1] == '_')
|
||||
{
|
||||
int registerOutIndex = str.find("REGISTER_OUT");
|
||||
if (registerOutIndex != std::string::npos)
|
||||
{
|
||||
int spaceIndex = str.find(' ', registerOutIndex);
|
||||
int secondSpaceIndex = str.find(' ', spaceIndex + 1);
|
||||
auto reg = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1);
|
||||
if (reg[0] == 'c')
|
||||
continue; // TODO
|
||||
if (reg[0] == 'v')
|
||||
{
|
||||
int openingBracketIndex = str.find('[', secondSpaceIndex + 1);
|
||||
int commaIndex0 = str.find(',', openingBracketIndex + 1);
|
||||
int commaIndex1 = str.find(',', commaIndex0 + 1);
|
||||
int commaIndex2 = str.find(',', commaIndex1 + 1);
|
||||
int closingBracketIndex = str.find(']', commaIndex2 + 1);
|
||||
|
||||
fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[3], 0x{});", name, reg, str.substr(openingBracketIndex + 1, commaIndex0 - openingBracketIndex - 1));
|
||||
fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[2], 0x{});", name, reg, str.substr(commaIndex0 + 2, commaIndex1 - commaIndex0 - 2));
|
||||
fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[1], 0x{});", name, reg, str.substr(commaIndex1 + 2, commaIndex2 - commaIndex1 - 2));
|
||||
fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[0], 0x{});", name, reg, str.substr(commaIndex2 + 2, closingBracketIndex - commaIndex2 - 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println(file, "\tPPC_CHECK_VALUE_{}({}, ctx.{}.{}64, {});",
|
||||
str.find('.', secondSpaceIndex) != std::string::npos ? 'F' : 'U',
|
||||
name,
|
||||
reg,
|
||||
str.find('.', secondSpaceIndex) != std::string::npos ? 'f' : 'u',
|
||||
str.substr(secondSpaceIndex + 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int memoryOutIndex = str.find("MEMORY_OUT");
|
||||
if (memoryOutIndex != std::string::npos)
|
||||
{
|
||||
int spaceIndex = str.find(' ', memoryOutIndex);
|
||||
int secondSpaceIndex = str.find(' ', spaceIndex + 1);
|
||||
auto address = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1);
|
||||
for (size_t i = secondSpaceIndex + 1, j = 0; i < str.size(); i++)
|
||||
{
|
||||
if (str[i] != ' ')
|
||||
{
|
||||
fmt::println(file, "\tPPC_CHECK_VALUE_U({}, base[0x{} + 0x{:X}], 0x{}{});", name, address, j, str[i], str[i + 1]);
|
||||
++i; // the loop adds another
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (getline() && !str.empty() && str[0] == '#');
|
||||
|
||||
fmt::println(file, "}}\n");
|
||||
|
||||
fmt::format_to(std::back_inserter(main), "\t{}(base);\n", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println("Found no symbol for {}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println("Unable to locate source file for {}", fn);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::println(file, "int main() {{");
|
||||
fmt::println(file, "\tuint8_t* base = reinterpret_cast<uint8_t*>(VirtualAlloc(nullptr, 0x100000000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));");
|
||||
fwrite(main.data(), 1, main.size(), file);
|
||||
fmt::println(file, "\treturn 0;");
|
||||
fmt::println(file, "}}");
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
10
XenonRecomp/test_recompiler.h
Normal file
10
XenonRecomp/test_recompiler.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "recompiler.h"
|
||||
|
||||
struct TestRecompiler : Recompiler
|
||||
{
|
||||
void Analyse(const std::string_view& testName);
|
||||
void Reset();
|
||||
|
||||
static void RecompileTests(const char* srcDirectoryPath, const char* dstDirectoryPath);
|
||||
};
|
||||
Reference in New Issue
Block a user