feat: some default stub for now

This commit is contained in:
Ran-j
2025-04-14 02:34:51 -03:00
parent e5cef40032
commit aceb8b1535
2 changed files with 425 additions and 67 deletions
+13 -2
View File
@@ -45,10 +45,21 @@ namespace ps2recomp
bool shouldStubFunction(const std::string &name) const;
bool shouldSkipFunction(const std::string &name) const;
std::string generateRuntimeHeader();
std::string generateStubFunction(const Function& function);
bool generateFunctionHeader();
std::string generateStubFunction(const Function &function);
bool generateFunctionHeader();
bool writeToFile(const std::string &path, const std::string &content);
std::filesystem::path getOutputPath(const Function &function) const;
private:
std::string generatePrintfStub(const std::string &name);
std::string generateMemoryCopyStub(const std::string &name);
std::string generateFileIOStub(const std::string &name);
std::string generateStringStub(const std::string &name);
std::string generateMathStub(const std::string &name);
std::string generateTimeStub(const std::string &name);
std::string generateNetworkStub(const std::string &name);
std::string generateThreadStub(const std::string &name);
std::string generateDefaultStub(const std::string &name);
};
}
+412 -65
View File
@@ -85,8 +85,7 @@ namespace ps2recomp
std::cout << "Stubbing function: " << function.name << std::endl;
function.isStub = true;
function.isRecompiled = true; // we're generating code for it
// Generate stub implementation and store it
std::string stubCode = generateStubFunction(function);
m_generatedStubs[function.start] = stubCode;
@@ -193,13 +192,13 @@ namespace ps2recomp
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";
for (const auto &function : m_functions)
{
if (function.isRecompiled)
@@ -209,7 +208,7 @@ namespace ps2recomp
}
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());
@@ -288,7 +287,7 @@ namespace ps2recomp
ss << "#include \"ps2_runtime_macros.h\"\n";
ss << "#include \"ps2_runtime.h\"\n";
ss << "#include \"ps2_recompiled_functions.h\"\n\n";
ss << "#include \"ps2_recompiled_functions.h\"\n\n";
ss << "// STUB FUNCTION: " << function.name << "\n";
ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n";
@@ -302,76 +301,64 @@ namespace ps2recomp
}
else
{
// Default stub implementation based on common functions
if (function.name == "printf" || function.name == "fprintf" ||
function.name == "sprintf" || function.name == "snprintf")
const std::string &name = function.name;
if (name == "printf" || name == "fprintf" || name == "sprintf" ||
name == "snprintf" || name == "vprintf" || name == "vfprintf" ||
name == "vsprintf" || name == "vsnprintf")
{
ss << " // Format string is in $a0 (r4), args start at $a1 (r5)\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << function.name << " with format at 0x%08X\\n\", ctx->r[4]);\n";
ss << " #endif\n";
ss << " // Return success (number of characters, but we'll just say 1)\n";
ss << " ctx->r[2] = 1;\n";
ss << generatePrintfStub(name);
}
else if (function.name == "malloc" || function.name == "calloc" ||
function.name == "realloc" || function.name == "memalign")
else if (name == "fopen" || name == "fclose" || name == "fread" ||
name == "fwrite" || name == "fseek" || name == "ftell" ||
name == "rewind" || name == "fflush" || name == "ferror" ||
name == "clearerr" || name == "feof" || name == "open" ||
name == "close" || name == "read" || name == "write" ||
name == "lseek" || name == "stat" || name == "fstat")
{
ss << " // Memory allocation - Would call the runtime's allocation system\n";
ss << " uint32_t size = ctx->r[4]; // Size is in $a0\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << function.name << " size=%u\\n\", size);\n";
ss << " #endif\n";
ss << " // In a real implementation, call runtime->allocateMemory(size)\n";
ss << " ctx->r[2] = 0; // Return NULL for now - replace with actual allocation in real implementation\n";
ss << generateFileIOStub(name);
}
else if (function.name == "free")
else if (name == "strlen" || name == "strcpy" || name == "strncpy" ||
name == "strcat" || name == "strncat" || name == "strcmp" ||
name == "strncmp" || name == "strchr" || name == "strrchr" ||
name == "strstr" || name == "strtok" || name == "strtok_r")
{
ss << " // Free memory - Would call the runtime's free system\n";
ss << " uint32_t ptr = ctx->r[4]; // Pointer is in $a0\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: free(0x%08X)\\n\", ptr);\n";
ss << " #endif\n";
ss << " // In a real implementation, call runtime->freeMemory(ptr)\n";
ss << generateStringStub(name);
}
else if (function.name == "memcpy" || function.name == "memmove")
else if (name == "sin" || name == "cos" || name == "tan" ||
name == "asin" || name == "acos" || name == "atan" ||
name == "atan2" || name == "sinh" || name == "cosh" ||
name == "tanh" || name == "exp" || name == "log" ||
name == "log10" || name == "pow" || name == "sqrt" ||
name == "ceil" || name == "floor" || name == "fabs" ||
name == "fmod")
{
ss << " // Memory copy\n";
ss << " uint32_t dst = ctx->r[4]; // Destination in $a0\n";
ss << " uint32_t src = ctx->r[5]; // Source in $a1\n";
ss << " uint32_t size = ctx->r[6]; // Size in $a2\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << function.name << "(dst=0x%08X, src=0x%08X, size=%u)\\n\", dst, src, size);\n";
ss << " #endif\n";
ss << " // Only copy if within valid memory range\n";
ss << " if (dst < 0x2000000 && src < 0x2000000 && dst + size < 0x2000000 && src + size < 0x2000000) {\n";
ss << " memcpy(rdram + dst, rdram + src, size);\n";
ss << " }\n";
ss << " ctx->r[2] = dst; // Return destination pointer\n";
ss << generateMathStub(name);
}
else if (function.name == "memset")
else if (name == "time" || name == "clock" || name == "difftime" ||
name == "mktime" || name == "localtime" || name == "gmtime" ||
name == "asctime" || name == "ctime" || name == "strftime")
{
ss << " // Memory set\n";
ss << " uint32_t dst = ctx->r[4]; // Destination in $a0\n";
ss << " uint8_t value = (uint8_t)ctx->r[5]; // Value in $a1\n";
ss << " uint32_t size = ctx->r[6]; // Size in $a2\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: memset(dst=0x%08X, value=%u, size=%u)\\n\", dst, value, size);\n";
ss << " #endif\n";
ss << " // Only set if within valid memory range\n";
ss << " if (dst < 0x2000000 && dst + size < 0x2000000) {\n";
ss << " memset(rdram + dst, value, size);\n";
ss << " }\n";
ss << " ctx->r[2] = dst; // Return destination pointer\n";
ss << generateTimeStub(name);
}
else if (name == "socket" || name == "bind" || name == "listen" ||
name == "accept" || name == "connect" || name == "send" ||
name == "recv" || name == "sendto" || name == "recvfrom" ||
name == "gethostbyname" || name == "gethostbyaddr")
{
ss << generateNetworkStub(name);
}
else if (name == "pthread_create" || name == "pthread_join" ||
name == "pthread_exit" || name == "pthread_mutex_init" ||
name == "pthread_mutex_lock" || name == "pthread_mutex_unlock" ||
name == "pthread_cond_init" || name == "pthread_cond_wait" ||
name == "pthread_cond_signal")
{
ss << generateThreadStub(name);
}
else
{
// Generic stub for unknown functions
ss << " // Default stub implementation\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub function called: " << function.name << " at PC=0x%08X\\n\", ctx->pc);\n";
ss << " #endif\n";
ss << " // Default return value (0)\n";
ss << " ctx->r[2] = 0;\n";
ss << generateDefaultStub(name);
}
}
@@ -380,6 +367,29 @@ namespace ps2recomp
return ss.str();
}
std::string PS2Recompiler::generatePrintfStub(const std::string &name)
{
std::stringstream ss;
ss << " // Format string is in $a0 (r4), args start at $a1 (r5)\n";
ss << " #ifdef _DEBUG\n";
if (name == "fprintf" || name == "vfprintf")
{
ss << " printf(\"Stub called: " << name << " with file handle=0x%08X, format at 0x%08X\\n\", ctx->r[4], ctx->r[5]);\n";
}
else
{
ss << " printf(\"Stub called: " << name << " with format at 0x%08X\\n\", ctx->r[4]);\n";
}
ss << " #endif\n";
ss << " // Return success (number of characters, but we'll just say 1)\n";
ss << " ctx->r[2] = 1;\n";
return ss.str();
}
bool PS2Recompiler::writeToFile(const std::string &path, const std::string &content)
{
std::ofstream file(path);
@@ -416,4 +426,341 @@ namespace ps2recomp
return outputPath;
}
std::string PS2Recompiler::generateMemoryCopyStub(const std::string &name)
{
std::stringstream ss;
ss << " // Memory copy\n";
if (name == "bcopy")
{
ss << " uint32_t src = ctx->r[4]; // Source in $a0\n";
ss << " uint32_t dst = ctx->r[5]; // Destination in $a1\n";
}
else
{
ss << " uint32_t dst = ctx->r[4]; // Destination in $a0\n";
ss << " uint32_t src = ctx->r[5]; // Source in $a1\n";
}
ss << " uint32_t size = ctx->r[6]; // Size in $a2\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << name << "(dst=0x%08X, src=0x%08X, size=%u)\\n\", "
<< (name == "bcopy" ? "dst, src" : "dst, src") << ", size);\n";
ss << " #endif\n";
ss << " // Only copy if within valid memory range\n";
ss << " if (dst < 0x2000000 && src < 0x2000000 && dst + size < 0x2000000 && src + size < 0x2000000) {\n";
if (name == "memmove")
{
ss << " memmove(rdram + dst, rdram + src, size);\n";
}
else
{
ss << " memcpy(rdram + dst, rdram + src, size);\n";
}
ss << " }\n";
if (name == "bcopy")
{
ss << " // bcopy doesn't return a value\n";
}
else
{
ss << " ctx->r[2] = dst; // Return destination pointer\n";
}
return ss.str();
}
std::string PS2Recompiler::generateFileIOStub(const std::string &name)
{
std::stringstream ss;
ss << " // File I/O operation: " << name << "\n";
ss << " #ifdef _DEBUG\n";
if (name == "fopen" || name == "open")
{
ss << " uint32_t path_ptr = ctx->r[4]; // Path pointer in $a0\n";
ss << " uint32_t mode_ptr = ctx->r[5]; // Mode pointer in $a1\n";
ss << " printf(\"Stub called: " << name << "(path=0x%08X, mode=0x%08X)\\n\", path_ptr, mode_ptr);\n";
ss << " #endif\n";
ss << " // Return a fake file handle\n";
ss << " ctx->r[2] = 0x12345678; // Fake file handle\n";
}
else if (name == "fclose" || name == "close")
{
ss << " uint32_t handle = ctx->r[4]; // File handle in $a0\n";
ss << " printf(\"Stub called: " << name << "(handle=0x%08X)\\n\", handle);\n";
ss << " #endif\n";
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
else if (name == "fread" || name == "read")
{
ss << " uint32_t buffer = ctx->r[4]; // Buffer pointer in $a0\n";
ss << " uint32_t size = ctx->r[5]; // Size in $a1\n";
ss << " uint32_t count = ctx->r[6]; // Count in $a2 (for fread)\n";
ss << " uint32_t handle = ctx->r[7]; // File handle in $a3\n";
ss << " printf(\"Stub called: " << name << "(buffer=0x%08X, size=%u, count=%u, handle=0x%08X)\\n\", buffer, size, count, handle);\n";
ss << " #endif\n";
ss << " // Return number of bytes/items read\n";
ss << " ctx->r[2] = " << (name == "fread" ? "count" : "size") << "; // Pretend we read everything\n";
}
else if (name == "fwrite" || name == "write")
{
ss << " uint32_t buffer = ctx->r[4]; // Buffer pointer in $a0\n";
ss << " uint32_t size = ctx->r[5]; // Size in $a1\n";
ss << " uint32_t count = ctx->r[6]; // Count in $a2 (for fwrite)\n";
ss << " uint32_t handle = ctx->r[7]; // File handle in $a3\n";
ss << " printf(\"Stub called: " << name << "(buffer=0x%08X, size=%u, count=%u, handle=0x%08X)\\n\", buffer, size, count, handle);\n";
ss << " #endif\n";
ss << " // Return number of bytes/items written\n";
ss << " ctx->r[2] = " << (name == "fwrite" ? "count" : "size") << "; // Pretend we wrote everything\n";
}
else
{
ss << " printf(\"Stub called: " << name << "()\\n\");\n";
ss << " #endif\n";
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
return ss.str();
}
std::string PS2Recompiler::generateStringStub(const std::string &name)
{
std::stringstream ss;
ss << " // String operation: " << name << "\n";
ss << " #ifdef _DEBUG\n";
if (name == "strlen")
{
ss << " uint32_t str_ptr = ctx->r[4]; // String pointer in $a0\n";
ss << " printf(\"Stub called: strlen(str=0x%08X)\\n\", str_ptr);\n";
ss << " #endif\n";
ss << " // If string is in valid memory range, calculate real length\n";
ss << " if (str_ptr < 0x2000000) {\n";
ss << " const char* str = (const char*)(rdram + str_ptr);\n";
ss << " // Safely calculate strlen with bounds checking\n";
ss << " size_t len = 0;\n";
ss << " while (str_ptr + len < 0x2000000 && str[len] != '\\0') {\n";
ss << " len++;\n";
ss << " }\n";
ss << " ctx->r[2] = len;\n";
ss << " } else {\n";
ss << " ctx->r[2] = 0;\n";
ss << " }\n";
}
else if (name == "strcpy" || name == "strncpy")
{
ss << " uint32_t dst_ptr = ctx->r[4]; // Destination pointer in $a0\n";
ss << " uint32_t src_ptr = ctx->r[5]; // Source pointer in $a1\n";
if (name == "strncpy")
{
ss << " uint32_t max_len = ctx->r[6]; // Max length in $a2\n";
ss << " printf(\"Stub called: strncpy(dst=0x%08X, src=0x%08X, n=%u)\\n\", dst_ptr, src_ptr, max_len);\n";
}
else
{
ss << " printf(\"Stub called: strcpy(dst=0x%08X, src=0x%08X)\\n\", dst_ptr, src_ptr);\n";
}
ss << " #endif\n";
ss << " // Return destination pointer\n";
ss << " ctx->r[2] = dst_ptr;\n";
}
else if (name == "strcmp" || name == "strncmp")
{
ss << " uint32_t str1_ptr = ctx->r[4]; // String 1 pointer in $a0\n";
ss << " uint32_t str2_ptr = ctx->r[5]; // String 2 pointer in $a1\n";
if (name == "strncmp")
{
ss << " uint32_t max_len = ctx->r[6]; // Max length in $a2\n";
ss << " printf(\"Stub called: strncmp(s1=0x%08X, s2=0x%08X, n=%u)\\n\", str1_ptr, str2_ptr, max_len);\n";
}
else
{
ss << " printf(\"Stub called: strcmp(s1=0x%08X, s2=0x%08X)\\n\", str1_ptr, str2_ptr);\n";
}
ss << " #endif\n";
ss << " // Return comparison result (0 for equal)\n";
ss << " ctx->r[2] = 0;\n";
}
else
{
ss << " printf(\"Stub called: " << name << "()\\n\");\n";
ss << " #endif\n";
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
return ss.str();
}
std::string PS2Recompiler::generateMathStub(const std::string &name)
{
std::stringstream ss;
ss << " // Math operation: " << name << "\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << name << "()\\n\");\n";
ss << " #endif\n";
if (name == "sin" || name == "cos" || name == "tan" ||
name == "asin" || name == "acos" || name == "atan" ||
name == "atan2" || name == "sinh" || name == "cosh" ||
name == "tanh" || name == "exp" || name == "log" ||
name == "log10" || name == "pow" || name == "sqrt" ||
name == "ceil" || name == "floor" || name == "fabs" ||
name == "fmod")
{
ss << " // Simply delegate to host function\n";
ss << " float arg1 = *(float*)&ctx->r[4]; // First argument in $a0\n";
if (name == "atan2" || name == "pow" || name == "fmod")
{
ss << " float arg2 = *(float*)&ctx->r[5]; // Second argument in $a1\n";
ss << " float result = " << name << "f(arg1, arg2);\n";
}
else
{
ss << " float result = " << name << "f(arg1);\n";
}
ss << " *(float*)&ctx->r[2] = result; // Return result in $v0\n";
}
else
{
ss << " // Return 0.0f as default\n";
ss << " *(float*)&ctx->r[2] = 0.0f;\n";
}
return ss.str();
}
std::string PS2Recompiler::generateTimeStub(const std::string &name)
{
std::stringstream ss;
ss << " // Time operation: " << name << "\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << name << "()\\n\");\n";
ss << " #endif\n";
if (name == "time")
{
ss << " // Return current time value (seconds since epoch)\n";
ss << " ctx->r[2] = (uint32_t)time(NULL);\n";
}
else if (name == "clock")
{
ss << " // Return clock ticks\n";
ss << " ctx->r[2] = (uint32_t)clock();\n";
}
else
{
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
return ss.str();
}
// TODO give this more attention
std::string PS2Recompiler::generateNetworkStub(const std::string &name)
{
std::stringstream ss;
ss << " // Network operation: " << name << "\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << name << "()\\n\");\n";
ss << " #endif\n";
if (name == "socket")
{
ss << " // Return fake socket descriptor\n";
ss << " ctx->r[2] = 3; // Fake socket fd\n";
}
else if (name == "bind" || name == "listen" || name == "connect")
{
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
else if (name == "accept")
{
ss << " // Return fake client socket descriptor\n";
ss << " ctx->r[2] = 4; // Fake client socket fd\n";
}
else if (name == "send" || name == "sendto")
{
ss << " uint32_t size = ctx->r[6]; // Size in $a2\n";
ss << " // Return bytes sent (all of them)\n";
ss << " ctx->r[2] = size;\n";
}
else if (name == "recv" || name == "recvfrom")
{
ss << " // Return 0 (no data)\n";
ss << " ctx->r[2] = 0;\n";
}
else
{
ss << " // Return failure (-1)\n";
ss << " ctx->r[2] = (uint32_t)-1;\n";
}
return ss.str();
}
std::string PS2Recompiler::generateThreadStub(const std::string &name)
{
std::stringstream ss;
ss << " // Thread operation: " << name << "\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub called: " << name << "()\\n\");\n";
ss << " #endif\n";
if (name == "pthread_create")
{
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
ss << " // Set thread ID to 1 in the output parameter\n";
ss << " uint32_t thread_id_ptr = ctx->r[4]; // Thread ID pointer in $a0\n";
ss << " if (thread_id_ptr < 0x2000000) {\n";
ss << " *(uint32_t*)(rdram + thread_id_ptr) = 1;\n";
ss << " }\n";
}
else if (name.find("pthread_mutex_") != std::string::npos ||
name.find("pthread_cond_") != std::string::npos)
{
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
else
{
ss << " // Return success\n";
ss << " ctx->r[2] = 0;\n";
}
return ss.str();
}
std::string PS2Recompiler::generateDefaultStub(const std::string &name)
{
std::stringstream ss;
ss << " // Default stub implementation\n";
ss << " #ifdef _DEBUG\n";
ss << " printf(\"Stub function called: " << name << " at PC=0x%08X\\n\", ctx->pc);\n";
ss << " #endif\n";
ss << " // Default return value (0)\n";
ss << " ctx->r[2] = 0;\n";
return ss.str();
}
}