mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
313 lines
10 KiB
C++
313 lines
10 KiB
C++
#include "ps2recomp/ps2_recompiler.h"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <filesystem>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace ps2recomp
|
|
{
|
|
|
|
PS2Recompiler::PS2Recompiler(const std::string &configPath)
|
|
: m_configManager(configPath)
|
|
{
|
|
}
|
|
|
|
bool PS2Recompiler::initialize()
|
|
{
|
|
try
|
|
{
|
|
m_config = m_configManager.loadConfig();
|
|
|
|
for (const auto &name : m_config.skipFunctions)
|
|
{
|
|
m_skipFunctions[name] = true;
|
|
}
|
|
|
|
m_elfParser = std::make_unique<ElfParser>(m_config.inputPath);
|
|
if (!m_elfParser->parse())
|
|
{
|
|
std::cerr << "Failed to parse ELF file: " << m_config.inputPath << std::endl;
|
|
return false;
|
|
}
|
|
|
|
m_functions = m_elfParser->extractFunctions();
|
|
m_symbols = m_elfParser->extractSymbols();
|
|
m_sections = m_elfParser->getSections();
|
|
m_relocations = m_elfParser->getRelocations();
|
|
|
|
std::cout << "Extracted " << m_functions.size() << " functions, "
|
|
<< m_symbols.size() << " symbols, "
|
|
<< m_sections.size() << " sections, "
|
|
<< m_relocations.size() << " relocations." << std::endl;
|
|
|
|
m_decoder = std::make_unique<R5900Decoder>();
|
|
m_codeGenerator = std::make_unique<CodeGenerator>(m_symbols);
|
|
|
|
fs::create_directories(m_config.outputPath);
|
|
|
|
return true;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error during initialization: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PS2Recompiler::recompile()
|
|
{
|
|
try
|
|
{
|
|
std::cout << "Recompiling " << m_functions.size() << " functions..." << std::endl;
|
|
|
|
std::string runtimeHeader = generateRuntimeHeader();
|
|
fs::path runtimeHeaderPath = fs::path(m_config.outputPath) / "ps2_runtime_macros.h";
|
|
|
|
writeToFile(runtimeHeaderPath.string(), runtimeHeader);
|
|
|
|
size_t processedCount = 0;
|
|
for (auto &function : m_functions)
|
|
{
|
|
std::cout << "processing function: " << function.name << std::endl;
|
|
|
|
if (shouldSkipFunction(function.name))
|
|
{
|
|
std::cout << "Skipping function: " << function.name << std::endl;
|
|
continue;
|
|
}
|
|
|
|
if (!decodeFunction(function))
|
|
{
|
|
std::cerr << "Failed to decode function: " << function.name << std::endl;
|
|
return false;
|
|
}
|
|
|
|
function.isRecompiled = true;
|
|
#if _DEBUG
|
|
processedCount++;
|
|
if (processedCount % 100 == 0)
|
|
{
|
|
std::cout << "Processed " << processedCount << " functions." << std::endl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::cout << "Recompilation completed successfully." << std::endl;
|
|
return true;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error during recompilation: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void PS2Recompiler::generateOutput()
|
|
{
|
|
try
|
|
{
|
|
generateFunctionHeader();
|
|
|
|
if (m_config.singleFileOutput)
|
|
{
|
|
std::stringstream combinedOutput;
|
|
|
|
combinedOutput << "#include \"ps2_runtime_macros.h\"\n";
|
|
combinedOutput << "#include \"ps2_runtime.h\"\n\n";
|
|
|
|
for (const auto &function : m_functions)
|
|
{
|
|
if (!function.isRecompiled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (function.isStub)
|
|
{
|
|
combinedOutput << m_generatedStubs[function.start] << "\n\n";
|
|
}
|
|
else
|
|
{
|
|
const auto &instructions = m_decodedFunctions[function.start];
|
|
std::string code = m_codeGenerator->generateFunction(function, instructions, false);
|
|
combinedOutput << code << "\n\n";
|
|
}
|
|
}
|
|
|
|
fs::path outputPath = fs::path(m_config.outputPath) / "recompiled.cpp";
|
|
writeToFile(outputPath.string(), combinedOutput.str());
|
|
std::cout << "Wrote recompiled to combined output to: " << outputPath << std::endl;
|
|
}
|
|
else
|
|
{
|
|
for (const auto &function : m_functions)
|
|
{
|
|
if (!function.isRecompiled || function.isStub)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::string code;
|
|
if (function.isStub)
|
|
{
|
|
code = m_generatedStubs[function.start];
|
|
}
|
|
else
|
|
{
|
|
const auto &instructions = m_decodedFunctions[function.start];
|
|
code = m_codeGenerator->generateFunction(function, instructions, true);
|
|
}
|
|
|
|
fs::path outputPath = getOutputPath(function);
|
|
fs::create_directories(outputPath.parent_path());
|
|
writeToFile(outputPath.string(), code);
|
|
}
|
|
|
|
std::cout << "Wrote individual function files to: " << m_config.outputPath << std::endl;
|
|
}
|
|
|
|
std::string registerFunctions = m_codeGenerator->generateFunctionRegistration(
|
|
m_functions, m_generatedStubs);
|
|
|
|
fs::path registerPath = fs::path(m_config.outputPath) / "register_functions.cpp";
|
|
writeToFile(registerPath.string(), registerFunctions);
|
|
std::cout << "Generated function registration file: " << registerPath << std::endl;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error during output generation: " << e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
bool PS2Recompiler::generateFunctionHeader()
|
|
{
|
|
try
|
|
{
|
|
std::stringstream ss;
|
|
|
|
ss << "#ifndef PS2_RECOMPILED_FUNCTIONS_H\n";
|
|
ss << "#define PS2_RECOMPILED_FUNCTIONS_H\n\n";
|
|
|
|
ss << "#include <cstdint>\n\n";
|
|
ss << "struct R5900Context;\n\n";
|
|
ss << "class PS2Runtime;\n\n";
|
|
|
|
for (const auto &function : m_functions)
|
|
{
|
|
if (function.isRecompiled)
|
|
{
|
|
ss << "void " << function.name << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime);\n";
|
|
}
|
|
}
|
|
|
|
ss << "\n#endif // PS2_RECOMPILED_FUNCTIONS_H\n";
|
|
|
|
fs::path headerPath = fs::path(m_config.outputPath) / "ps2_recompiled_functions.h";
|
|
writeToFile(headerPath.string(), ss.str());
|
|
|
|
std::cout << "Generated function header file: " << headerPath << std::endl;
|
|
return true;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error generating function header: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PS2Recompiler::decodeFunction(Function &function)
|
|
{
|
|
std::vector<Instruction> instructions;
|
|
|
|
uint32_t start = function.start;
|
|
uint32_t end = function.end;
|
|
|
|
for (uint32_t address = start; address < end; address += 4)
|
|
{
|
|
try
|
|
{
|
|
if (!m_elfParser->isValidAddress(address))
|
|
{
|
|
std::cerr << "Invalid address: 0x" << std::hex << address << std::dec
|
|
<< " in function: " << function.name << std::endl;
|
|
return false;
|
|
}
|
|
|
|
uint32_t rawInstruction = m_elfParser->readWord(address);
|
|
|
|
auto patchIt = m_config.patches.find(address);
|
|
if (patchIt != m_config.patches.end())
|
|
{
|
|
rawInstruction = std::stoul(patchIt->second, nullptr, 0);
|
|
std::cout << "Applied patch at 0x" << std::hex << address << std::dec << std::endl;
|
|
}
|
|
|
|
Instruction inst = m_decoder->decodeInstruction(address, rawInstruction);
|
|
|
|
instructions.push_back(inst);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error decoding instruction at 0x" << std::hex << address << std::dec
|
|
<< " in function: " << function.name << ": " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_decodedFunctions[function.start] = instructions;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PS2Recompiler::shouldSkipFunction(const std::string &name) const
|
|
{
|
|
return m_skipFunctions.find(name) != m_skipFunctions.end();
|
|
}
|
|
|
|
std::string PS2Recompiler::generateRuntimeHeader()
|
|
{
|
|
return m_codeGenerator->generateMacroHeader();
|
|
}
|
|
|
|
bool PS2Recompiler::writeToFile(const std::string &path, const std::string &content)
|
|
{
|
|
std::ofstream file(path);
|
|
if (!file)
|
|
{
|
|
std::cerr << "Failed to open file for writing: " << path << std::endl;
|
|
return false;
|
|
}
|
|
|
|
file << content;
|
|
file.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
std::filesystem::path PS2Recompiler::getOutputPath(const Function &function) const
|
|
{
|
|
std::string safeName = function.name;
|
|
|
|
std::replace_if(safeName.begin(), safeName.end(), [](char c)
|
|
{ return c == '/' || c == '\\' || c == ':' || c == '*' ||
|
|
c == '?' || c == '"' || c == '<' || c == '>' ||
|
|
c == '|' || c == '$'; }, '_');
|
|
|
|
if (safeName.empty())
|
|
{
|
|
std::stringstream ss;
|
|
ss << "func_" << std::hex << function.start;
|
|
safeName = ss.str();
|
|
}
|
|
|
|
std::filesystem::path outputPath = m_config.outputPath;
|
|
outputPath /= safeName + ".cpp";
|
|
|
|
return outputPath;
|
|
}
|
|
}
|