diff --git a/ps2xAnalyzer/src/elf_analyzer.cpp b/ps2xAnalyzer/src/elf_analyzer.cpp index 0188bae..4f9683d 100644 --- a/ps2xAnalyzer/src/elf_analyzer.cpp +++ b/ps2xAnalyzer/src/elf_analyzer.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include namespace fs = std::filesystem; @@ -18,6 +20,8 @@ namespace ps2recomp { static bool hasPs2ApiPrefix(const std::string &name); static bool isDoNotSkipOrStub(const std::string &name); + static uint32_t decodeAbsoluteJumpTarget(uint32_t instructionAddress, uint32_t targetField); + static bool tryReadWord(const ElfParser *parser, uint32_t address, uint32_t &outWord); ElfAnalyzer::ElfAnalyzer(const std::string &elfPath) : m_elfPath(elfPath) @@ -95,11 +99,17 @@ namespace ps2recomp fs::path outputPathObj(outputPath); fs::path outputDir = outputPathObj.parent_path(); - std::string outputDirStr = outputDir.string() + "/output/"; - - if (!fs::exists(outputDir / "output")) + if (outputDir.empty()) { - fs::create_directory(outputDir / "output"); + outputDir = "."; + } + + const fs::path generatedOutputDir = outputDir / "output"; + std::string outputDirStr = generatedOutputDir.generic_string() + "/"; + + if (!fs::exists(generatedOutputDir)) + { + fs::create_directories(generatedOutputDir); } file << "# PS2Recomp configuration for: " << elfFileName << "\n"; @@ -244,13 +254,23 @@ namespace ps2recomp void ElfAnalyzer::analyzeEntryPoint() { + const uint32_t entryAddress = m_elfParser->getEntryPoint(); auto it = std::find_if(m_functions.begin(), m_functions.end(), - [](const Function &f) - { return f.name == "entry" || f.name == "_start"; }); + [entryAddress](const Function &f) + { return f.start == entryAddress; }); + + if (it == m_functions.end()) + { + it = std::find_if(m_functions.begin(), m_functions.end(), + [entryAddress](const Function &f) + { return f.start <= entryAddress && entryAddress < f.end; }); + } if (it != m_functions.end()) { - std::cout << "Found entry point: " << it->name << " at 0x" << std::hex << it->start << std::dec << std::endl; + std::cout << "Found entry point from ELF header: 0x" << std::hex << entryAddress + << " in function " << it->name << " (starts at 0x" << it->start << ")" + << std::dec << std::endl; m_skipFunctions.insert(it->name); @@ -260,7 +280,7 @@ namespace ps2recomp { if (inst.opcode == OPCODE_JAL) { - uint32_t target = (inst.address & 0xF0000000) | (inst.target << 2); + uint32_t target = decodeAbsoluteJumpTarget(inst.address, inst.target); for (const auto &func : m_functions) { @@ -281,7 +301,8 @@ namespace ps2recomp } else { - std::cout << "Entry point not found" << std::endl; + std::cout << "Entry point 0x" << std::hex << entryAddress + << " not mapped to an extracted function" << std::dec << std::endl; for (const auto &func : m_functions) { @@ -411,7 +432,11 @@ namespace ps2recomp for (int i = 1; i <= 5 && static_cast(inst.address) - i * 4 >= static_cast(func.start); i++) { uint32_t prevAddr = inst.address - i * 4; - uint32_t prevInst = m_elfParser->readWord(prevAddr); + uint32_t prevInst = 0; + if (!tryReadWord(m_elfParser.get(), prevAddr, prevInst)) + { + continue; + } // Check if it's a LUI instruction for the same register if (OPCODE(prevInst) == OPCODE_LUI && RT(prevInst) == inst.rs) @@ -554,7 +579,7 @@ namespace ps2recomp if (nextInst.opcode == OPCODE_J || nextInst.opcode == OPCODE_JAL) { - uint32_t jumpTarget = (nextInst.address & 0xF0000000) | (nextInst.target << 2); + uint32_t jumpTarget = decodeAbsoluteJumpTarget(nextInst.address, nextInst.target); for (const auto §ion : m_sections) { @@ -576,7 +601,11 @@ namespace ps2recomp for (int j = 1; j <= 5 && static_cast(inst.address) - j * 4 >= static_cast(func.start); j++) { uint32_t prevAddr = inst.address - j * 4; - uint32_t prevInst = m_elfParser->readWord(prevAddr); + uint32_t prevInst = 0; + if (!tryReadWord(m_elfParser.get(), prevAddr, prevInst)) + { + continue; + } if (OPCODE(prevInst) == OPCODE_LUI && RT(prevInst) == inst.rs) { @@ -669,7 +698,7 @@ namespace ps2recomp if (inst.opcode == OPCODE_JAL) { - targetAddr = (inst.address & 0xF0000000) | (inst.target << 2); + targetAddr = decodeAbsoluteJumpTarget(inst.address, inst.target); } else { @@ -766,10 +795,9 @@ namespace ps2recomp { uint32_t entryAddr = baseAddr + (e * 4); - if (m_elfParser->isValidAddress(entryAddr)) + uint32_t targetAddr = 0; + if (tryReadWord(m_elfParser.get(), entryAddr, targetAddr)) { - uint32_t targetAddr = m_elfParser->readWord(entryAddr); - JumpTableEntry entry; entry.index = e; entry.target = targetAddr; @@ -876,7 +904,7 @@ namespace ps2recomp for (const auto &func : m_functions) { - if (eligible.contains(func.name)) + if (!eligible.contains(func.name)) { continue; } @@ -893,7 +921,7 @@ namespace ps2recomp for (const auto &call : itCalls->second) { // non-eligible nodes to graph. - if (eligible.contains(call.calleeName)) + if (!eligible.contains(call.calleeName)) { continue; } @@ -932,7 +960,7 @@ namespace ps2recomp { for (const auto &w : it->second) { - if (index.contains(w)) + if (!index.contains(w)) { strongconnect(w); lowlink[v] = std::min(lowlink[v], lowlink[w]); @@ -966,7 +994,7 @@ namespace ps2recomp for (const auto &name : eligible) { - if (index.contains(name)) + if (!index.contains(name)) { strongconnect(name); } @@ -1442,9 +1470,10 @@ namespace ps2recomp if (inst.isBranch || inst.isJump) { - if (i + 1 < instructions.size()) + size_t fallthroughIndex = i + (inst.hasDelaySlot ? 2 : 1); + if (fallthroughIndex < instructions.size()) { - leaders.insert(instructions[i + 1].address); + leaders.insert(instructions[fallthroughIndex].address); } if (inst.isBranch) @@ -1457,7 +1486,7 @@ namespace ps2recomp // Jump target for J/JAL if ((inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL) && !inst.isCall) { - uint32_t target = (inst.address & 0xF0000000) | (inst.target << 2); + uint32_t target = decodeAbsoluteJumpTarget(inst.address, inst.target); leaders.insert(target); } } @@ -1496,12 +1525,29 @@ namespace ps2recomp for (auto &[addr, node] : cfg) { - const auto &lastInst = node.instructions.back(); - - if (lastInst.isBranch) + if (node.instructions.empty()) { - int32_t offset = static_cast(lastInst.immediate) << 2; - uint32_t targetAddr = lastInst.address + 4 + offset; + continue; + } + + const auto &lastInst = node.instructions.back(); + const Instruction *terminator = &lastInst; + + if (node.instructions.size() >= 2) + { + const auto &candidate = node.instructions[node.instructions.size() - 2]; + if (candidate.hasDelaySlot && + (candidate.isBranch || candidate.isJump) && + candidate.address + 4 == lastInst.address) + { + terminator = &candidate; + } + } + + if (terminator->isBranch) + { + int32_t offset = static_cast(terminator->immediate) << 2; + uint32_t targetAddr = terminator->address + 4 + offset; if (cfg.contains(targetAddr)) { @@ -1509,16 +1555,17 @@ namespace ps2recomp cfg[targetAddr].predecessors.push_back(addr); } - bool likelyBranch = (lastInst.opcode == OPCODE_BEQL || - lastInst.opcode == OPCODE_BNEL || - lastInst.opcode == OPCODE_BLEZL || - lastInst.opcode == OPCODE_BGTZL); + bool likelyBranch = (terminator->opcode == OPCODE_BEQL || + terminator->opcode == OPCODE_BNEL || + terminator->opcode == OPCODE_BLEZL || + terminator->opcode == OPCODE_BGTZL); if (!likelyBranch) { - if (lastInst.address + 8 <= function.end) + const uint32_t step = terminator->hasDelaySlot ? 8 : 4; + if (terminator->address + step <= function.end) { - uint32_t nextAddr = lastInst.address + 8; // Skip delay slot + uint32_t nextAddr = terminator->address + step; for (const auto &[blockAddr, blockNode] : cfg) { @@ -1533,12 +1580,12 @@ namespace ps2recomp } } } - else if (lastInst.isJump) + else if (terminator->isJump) { - if (lastInst.opcode == OPCODE_J || lastInst.opcode == OPCODE_JAL) + if (terminator->opcode == OPCODE_J || terminator->opcode == OPCODE_JAL) { // Direct jump - uint32_t targetAddr = (lastInst.address & 0xF0000000) | (lastInst.target << 2); + uint32_t targetAddr = decodeAbsoluteJumpTarget(terminator->address, terminator->target); // Only add successor if it's within this function if (targetAddr >= function.start && targetAddr < function.end && @@ -1644,8 +1691,7 @@ namespace ps2recomp return systemFuncs.contains(name) || name.find("__") == 0 || - name.find("_Z") == 0 || // C++ mangled names - name.find(".") == 0; // .text.* or .plt.* symbols + name.find(".") == 0; // .text.* or .plt.* symbols } bool ElfAnalyzer::isLibraryFunction(const std::string &name) const @@ -1675,13 +1721,12 @@ namespace ps2recomp for (uint32_t addr = function.start; addr < function.end; addr += 4) { - if (!m_elfParser->isValidAddress(addr)) + uint32_t rawInstruction = 0; + if (!tryReadWord(m_elfParser.get(), addr, rawInstruction)) { continue; } - uint32_t rawInstruction = m_elfParser->readWord(addr); - try { Instruction inst = m_decoder->decodeInstruction(addr, rawInstruction); @@ -1880,8 +1925,8 @@ namespace ps2recomp } } - // Consider it loop-heavy if it has more than 3 loops - return loopCount > 3; + // Consider it loop-heavy if it has more than 5 loops + return loopCount > 5; } uint32_t ElfAnalyzer::getSuccessor(const Instruction &inst, uint32_t currentAddr) @@ -1894,9 +1939,42 @@ namespace ps2recomp if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL) { - return (currentAddr & 0xF0000000) | (inst.target << 2); + return decodeAbsoluteJumpTarget(currentAddr, inst.target); } return currentAddr + 4; } + + static uint32_t decodeAbsoluteJumpTarget(uint32_t instructionAddress, uint32_t targetField) + { + return ((instructionAddress + 4) & 0xF0000000u) | (targetField << 2); + } + + static bool tryReadWord(const ElfParser *parser, uint32_t address, uint32_t &outWord) + { + if (parser == nullptr) + { + return false; + } + + if (address > (std::numeric_limits::max() - 3)) + { + return false; + } + + if (!parser->isValidAddress(address) || !parser->isValidAddress(address + 3)) + { + return false; + } + + try + { + outWord = parser->readWord(address); + return true; + } + catch (const std::exception &) + { + return false; + } + } } diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index a912f88..1e740d0 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -28,11 +28,48 @@ namespace ps2recomp namespace ps2recomp { + static uint32_t buildAbsoluteJumpTarget(uint32_t address, uint32_t target) + { + return ((address + 4) & 0xF0000000u) | (target << 2); + } + + static std::string sanitizeIdentifierBody(const std::string &name) + { + std::string sanitized; + sanitized.reserve(name.size() + 1); + + for (char c : name) + { + const unsigned char uc = static_cast(c); + if (std::isalnum(uc) || c == '_') + { + sanitized.push_back(c); + } + else + { + sanitized.push_back('_'); + } + } + + if (sanitized.empty()) + { + return sanitized; + } + + const unsigned char first = static_cast(sanitized.front()); + if (!(std::isalpha(first) || sanitized.front() == '_')) + { + sanitized.insert(sanitized.begin(), '_'); + } + + return sanitized; + } + static bool isReservedCxxIdentifier(const std::string &name) { if (name.size() >= 2 && name[0] == '_' && name[1] == '_') return true; - if (!name.empty() && name[0] == '_' && std::isupper(static_cast(name[1]))) + if (name.size() >= 2 && name[0] == '_' && std::isupper(static_cast(name[1]))) return true; return false; } @@ -79,9 +116,9 @@ namespace ps2recomp std::string CodeGenerator::sanitizeFunctionName(const std::string &name) const { - std::string sanitized = name; - - std::replace(sanitized.begin(), sanitized.end(), '.', '_'); + std::string sanitized = sanitizeIdentifierBody(name); + if (sanitized.empty()) + return sanitized; // ugly but will do for now if (sanitized == "main") @@ -117,7 +154,7 @@ namespace ps2recomp { ss << " " << delaySlotCode << "\n"; } - uint32_t target = (branchInst.address & 0xF0000000) | (branchInst.target << 2); + uint32_t target = buildAbsoluteJumpTarget(branchInst.address, branchInst.target); std::string funcName = getFunctionName(target); if (!funcName.empty()) { @@ -330,7 +367,7 @@ namespace ps2recomp } else if (isStaticJump) { - uint32_t target = (inst.address & 0xF0000000) | (inst.target << 2); + uint32_t target = buildAbsoluteJumpTarget(inst.address, inst.target); if (target >= function.start && target < function.end) { targets.insert(target); @@ -541,9 +578,9 @@ namespace ps2recomp "SET_GPR_S64(ctx, {}, (int64_t)GPR_S64(ctx, {}) + (int64_t){});", inst.rt, inst.rs, inst.simmediate); case OPCODE_J: - return fmt::format("// JAL 0x{:X} - Handled by branch logic", (inst.address & 0xF0000000) | (inst.target << 2)); + return fmt::format("// J 0x{:X} - Handled by branch logic", buildAbsoluteJumpTarget(inst.address, inst.target)); case OPCODE_JAL: - return fmt::format("// JAL 0x{:X} - Handled by branch logic", (inst.address & 0xF0000000) | (inst.target << 2)); + return fmt::format("// JAL 0x{:X} - Handled by branch logic", buildAbsoluteJumpTarget(inst.address, inst.target)); case OPCODE_BEQ: case OPCODE_BNE: case OPCODE_BLEZ: @@ -2329,8 +2366,15 @@ namespace ps2recomp ss << " const uint32_t bss_start = 0x" << std::hex << m_bootstrapInfo.bssStart << ";\n"; ss << " const uint32_t bss_end = 0x" << std::hex << m_bootstrapInfo.bssEnd << ";\n"; ss << " __m128i zero = _mm_setzero_si128();\n"; - ss << " for (uint32_t addr = bss_start; addr < bss_end; addr += 16) {\n"; + ss << " uint32_t addr = bss_start;\n"; + ss << " for (; (bss_end - addr) >= 16; addr += 16) {\n"; ss << " WRITE128(addr, zero);\n"; + ss << " }\n"; + ss << " for (; (bss_end - addr) >= 4; addr += 4) {\n"; + ss << " WRITE32(addr, 0);\n"; + ss << " }\n"; + ss << " for (; addr < bss_end; ++addr) {\n"; + ss << " WRITE8(addr, 0);\n"; ss << " }\n\n"; } if (m_bootstrapInfo.gp != 0) diff --git a/ps2xRecomp/src/lib/config_manager.cpp b/ps2xRecomp/src/lib/config_manager.cpp index b5d0db4..b1a7f2b 100644 --- a/ps2xRecomp/src/lib/config_manager.cpp +++ b/ps2xRecomp/src/lib/config_manager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace ps2recomp { @@ -22,14 +23,30 @@ namespace ps2recomp { std::cout << "Parsing toml file: " << m_configPath << std::endl; auto data = toml::parse(m_configPath); + const auto &general = toml::find(data, "general"); - config.inputPath = toml::find(data, "general", "input"); - config.ghidraMapPath = toml::find(data, "general", "ghidra_output"); - config.outputPath = toml::find(data, "general", "output"); - config.singleFileOutput = toml::find(data, "general", "single_file_output"); - config.stubImplementations = toml::find>(data, "general", "stubs"); + config.inputPath = toml::find(general, "input"); + config.ghidraMapPath = toml::find_or(general, "ghidra_output", ""); + config.outputPath = toml::find(general, "output"); + config.singleFileOutput = toml::find_or(general, "single_file_output", false); - config.skipFunctions = toml::find>(data, "general", "skip"); + if (general.contains("stubs") && general.at("stubs").is_array()) + { + config.stubImplementations = toml::find>(general, "stubs"); + } + else if (data.contains("stubs") && data.at("stubs").is_array()) + { + config.stubImplementations = toml::find>(data, "stubs"); + } + + if (general.contains("skip") && general.at("skip").is_array()) + { + config.skipFunctions = toml::find>(general, "skip"); + } + else if (data.contains("skip") && data.at("skip").is_array()) + { + config.skipFunctions = toml::find>(data, "skip"); + } if (data.contains("patches") && data.at("patches").is_table()) { @@ -42,9 +59,33 @@ namespace ps2recomp { if (patch.contains("address") && patch.contains("value")) { - uint32_t address = std::stoul(toml::find(patch, "address"), nullptr, 0); - std::string value = toml::find(patch, "value"); - config.patches[address] = value; + uint32_t address = 0; + const auto &addressValue = patch.at("address"); + if (addressValue.is_string()) + { + address = std::stoul(toml::find(patch, "address"), nullptr, 0); + } + else if (addressValue.is_integer()) + { + address = static_cast(toml::find(patch, "address")); + } + else + { + continue; + } + + const auto &valueField = patch.at("value"); + if (valueField.is_string()) + { + config.patches[address] = toml::find(patch, "value"); + } + else if (valueField.is_integer()) + { + std::ostringstream valueStream; + valueStream << "0x" << std::hex + << static_cast(toml::find(patch, "value")); + config.patches[address] = valueStream.str(); + } } } } @@ -65,34 +106,28 @@ namespace ps2recomp toml::table general; general["input"] = config.inputPath; + general["ghidra_output"] = config.ghidraMapPath; general["output"] = config.outputPath; general["single_file_output"] = config.singleFileOutput; + general["skip"] = config.skipFunctions; + general["stubs"] = config.stubImplementations; data["general"] = general; - toml::array skips; - for (const auto &skip : config.skipFunctions) - { - skips.push_back(skip); - } - data["skip"] = skips; - toml::table patches; toml::array instPatches; for (const auto &[addr, value] : config.patches) { + std::ostringstream addrStream; + addrStream << "0x" << std::hex << addr; + toml::table p; - p["address"] = "0x" + std::to_string(addr); + p["address"] = addrStream.str(); p["value"] = value; instPatches.push_back(p); } patches["instructions"] = instPatches; data["patches"] = patches; - if (!config.stubImplementations.empty()) - { - data["stubs"] = config.stubImplementations; - } - std::ofstream file(m_configPath); if (!file) { @@ -102,4 +137,4 @@ namespace ps2recomp file << data; } -} // namespace ps2recomp \ No newline at end of file +} // namespace ps2recomp diff --git a/ps2xRecomp/src/lib/elf_parser.cpp b/ps2xRecomp/src/lib/elf_parser.cpp index a2cd126..35cd9b8 100644 --- a/ps2xRecomp/src/lib/elf_parser.cpp +++ b/ps2xRecomp/src/lib/elf_parser.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace { @@ -613,13 +614,22 @@ namespace ps2recomp { for (const auto §ion : m_sections) { - if (address >= section.address && address < (section.address + section.size)) + if (address < section.address || section.size < sizeof(uint32_t)) { - if (section.data) - { - uint32_t offset = address - section.address; - return *reinterpret_cast(section.data + offset); - } + continue; + } + + const uint32_t offset = address - section.address; + if (offset > section.size - static_cast(sizeof(uint32_t))) + { + continue; + } + + if (section.data) + { + uint32_t word = 0; + std::memcpy(&word, section.data + offset, sizeof(word)); + return word; } } diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index d420510..0a5a5fb 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -28,6 +28,56 @@ namespace ps2recomp Stub }; + uint32_t decodeAbsoluteJumpTarget(uint32_t address, uint32_t target) + { + return ((address + 4) & 0xF0000000u) | (target << 2); + } + + bool isReservedCxxIdentifier(const std::string &name) + { + if (name.size() >= 2 && name[0] == '_' && name[1] == '_') + { + return true; + } + if (name.size() >= 2 && name[0] == '_' && std::isupper(static_cast(name[1]))) + { + return true; + } + return false; + } + + std::string sanitizeIdentifierBody(const std::string &name) + { + std::string sanitized; + sanitized.reserve(name.size() + 1); + + for (char c : name) + { + const unsigned char uc = static_cast(c); + if (std::isalnum(uc) || c == '_') + { + sanitized.push_back(c); + } + else + { + sanitized.push_back('_'); + } + } + + if (sanitized.empty()) + { + return sanitized; + } + + const unsigned char first = static_cast(sanitized.front()); + if (!(std::isalpha(first) || sanitized.front() == '_')) + { + sanitized.insert(sanitized.begin(), '_'); + } + + return sanitized; + } + StubTarget resolveStubTarget(const std::string &name) { if (ps2_runtime_calls::isSyscallName(name)) @@ -167,6 +217,7 @@ namespace ps2recomp std::cout << "Recompiling " << m_functions.size() << " functions..." << std::endl; size_t processedCount = 0; + size_t failedCount = 0; for (auto &function : m_functions) { std::cout << "processing function: " << function.name << std::endl; @@ -186,8 +237,9 @@ namespace ps2recomp if (!decodeFunction(function)) { - std::cerr << "Failed to decode function: " << function.name << std::endl; - return false; + ++failedCount; + std::cerr << "Skipping function due decode failure: " << function.name << std::endl; + continue; } function.isRecompiled = true; @@ -202,6 +254,11 @@ namespace ps2recomp discoverAdditionalEntryPoints(); + if (failedCount > 0) + { + std::cerr << "Recompile completed with " << failedCount << " function(s) skipped due decode issues." << std::endl; + } + std::cout << "Recompilation completed successfully." << std::endl; return true; } @@ -394,12 +451,12 @@ namespace ps2recomp stubFile << "#include \"ps2_runtime.h\"\n"; stubFile << "#include \"ps2_syscalls.h\"\n"; stubFile << "#include \"ps2_stubs.h\"\n\n"; - stubFile << m_generatedStubs[function.start] << "\n"; + stubFile << m_generatedStubs.at(function.start) << "\n"; code = stubFile.str(); } else { - const auto &instructions = m_decodedFunctions[function.start]; + const auto &instructions = m_decodedFunctions.at(function.start); code = m_codeGenerator->generateFunction(function, instructions, true); } } @@ -453,7 +510,7 @@ namespace ps2recomp for (const auto &funcName : stubNames) { - ss << "void " << funcName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime);\n"; + ss << "void " << sanitizeFunctionName(funcName) << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime);\n"; } // ss << "\n} // namespace stubs\n"; @@ -530,7 +587,7 @@ namespace ps2recomp { if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL) { - return (inst.address & 0xF0000000) | (inst.target << 2); + return decodeAbsoluteJumpTarget(inst.address, inst.target); } if (inst.opcode == OPCODE_SPECIAL && @@ -651,6 +708,7 @@ namespace ps2recomp bool PS2Recompiler::decodeFunction(Function &function) { std::vector instructions; + bool truncated = false; uint32_t start = function.start; uint32_t end = function.end; @@ -662,8 +720,10 @@ namespace ps2recomp if (!m_elfParser->isValidAddress(address)) { std::cerr << "Invalid address: 0x" << std::hex << address << std::dec - << " in function: " << function.name << std::endl; - return false; + << " in function: " << function.name + << " (truncating decode)" << std::endl; + truncated = true; + break; } uint32_t rawInstruction = m_elfParser->readWord(address); @@ -671,8 +731,17 @@ namespace ps2recomp 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; + try + { + rawInstruction = std::stoul(patchIt->second, nullptr, 0); + std::cout << "Applied patch at 0x" << std::hex << address << std::dec << std::endl; + } + catch (const std::exception &e) + { + std::cerr << "Invalid patch value at 0x" << std::hex << address << std::dec + << " (" << patchIt->second << "): " << e.what() + << ". Using original instruction." << std::endl; + } } Instruction inst = m_decoder->decodeInstruction(address, rawInstruction); @@ -682,12 +751,26 @@ namespace ps2recomp 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; + << " in function: " << function.name << ": " << e.what() + << " (truncating decode)" << std::endl; + truncated = true; + break; } } - m_decodedFunctions[function.start] = instructions; + if (instructions.empty()) + { + std::cerr << "No decodable instructions found for function: " << function.name + << " (0x" << std::hex << function.start << ")" << std::dec << std::endl; + return false; + } + + if (truncated) + { + function.end = instructions.back().address + 4; + } + + m_decodedFunctions.insert_or_assign(function.start, std::move(instructions)); return true; } @@ -723,7 +806,16 @@ namespace ps2recomp std::filesystem::path PS2Recompiler::getOutputPath(const Function &function) const { - std::string safeName = function.name; + std::string safeName; + auto renameIt = m_functionRenames.find(function.start); + if (renameIt != m_functionRenames.end() && !renameIt->second.empty()) + { + safeName = renameIt->second; + } + else + { + safeName = sanitizeFunctionName(function.name); + } std::replace_if(safeName.begin(), safeName.end(), [](char c) { return c == '/' || c == '\\' || c == ':' || c == '*' || @@ -737,6 +829,15 @@ namespace ps2recomp safeName = ss.str(); } + std::stringstream suffix; + suffix << "_0x" << std::hex << function.start; + const std::string suffixText = suffix.str(); + if (safeName.size() < suffixText.size() || + safeName.compare(safeName.size() - suffixText.size(), suffixText.size(), suffixText) != 0) + { + safeName += suffixText; + } + std::filesystem::path outputPath = m_config.outputPath; outputPath /= safeName + ".cpp"; @@ -745,23 +846,18 @@ namespace ps2recomp std::string PS2Recompiler::sanitizeFunctionName(const std::string &name) const { - std::string sanitized = name; - std::replace(sanitized.begin(), sanitized.end(), '.', '_'); + std::string sanitized = sanitizeIdentifierBody(name); + if (sanitized.empty()) + { + return sanitized; + } if (sanitized == "main") { return "ps2_main"; } - if (ps2recomp::kKeywords.contains(sanitized)) - { - return "ps2_" + sanitized; - } - - if (sanitized.size() >= 2 && - sanitized[0] == '_' && - (sanitized[1] == '_' || - std::isupper(static_cast(sanitized[1])))) + if (ps2recomp::kKeywords.contains(sanitized) || isReservedCxxIdentifier(sanitized)) { return "ps2_" + sanitized; }