Skip to content

Commit

Permalink
Merge pull request #19097 from Kethen/sysclib_sprintf
Browse files Browse the repository at this point in the history
implement sysclib_sprintf
  • Loading branch information
hrydgard committed May 1, 2024
2 parents edcf685 + fa0eaeb commit 59be4b0
Showing 1 changed file with 153 additions and 5 deletions.
158 changes: 153 additions & 5 deletions Core/HLE/sceKernelInterrupt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,14 +742,162 @@ static int sysclib_memcmp(u32 dst, u32 src, u32 size) {
}

static int sysclib_sprintf(u32 dst, u32 fmt) {
ERROR_LOG(SCEKERNEL, "Unimpl sysclib_sprintf(dest=%08x, src=%08x)", dst, fmt);
if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(fmt)) {
// TODO: Properly use the format string with more parameters.
return sprintf((char *)Memory::GetPointerUnchecked(dst), "%s", Memory::GetCharPointerUnchecked(fmt));
} else {
ERROR_LOG(SCEKERNEL, "Untested sysclib_sprintf(dst=%08x, fmt=%08x)", dst, fmt);
if (!Memory::IsValidAddress(dst) || !Memory::IsValidAddress(fmt)) {
// What to do? Crash, probably.
return 0;
}

DEBUG_LOG(SCEKERNEL, "sysclib_sprintf fmt: %s", Memory::GetCharPointerUnchecked(fmt));
DEBUG_LOG(SCEKERNEL, "sysclib_sprintf a0-a4, t0-t4: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
currentMIPS->r[MIPS_REG_A0],
currentMIPS->r[MIPS_REG_A1],
currentMIPS->r[MIPS_REG_A2],
currentMIPS->r[MIPS_REG_A3],
currentMIPS->r[MIPS_REG_T0],
currentMIPS->r[MIPS_REG_T1],
currentMIPS->r[MIPS_REG_T2],
currentMIPS->r[MIPS_REG_T3]
);

bool processing_specifier = false;
std::string specifier = "";
int bytes_to_read = 0;
int arg_idx = 0;
int fmt_len = 0;
std::string result = "";
for (const char *c = Memory::GetCharPointerUnchecked(fmt); *c != '\0'; c++) {
// in case we have a bad fmt string, try not to crash the whole emulator
fmt_len++;
if (fmt_len == 1024) {
ERROR_LOG(SCEKERNEL, "sysclib_sprintf fmt is longer than 1024");
return 0;
}

if (!processing_specifier) {
if (*c == '%') {
specifier = "%";
processing_specifier = true;
bytes_to_read = 0;
} else {
result.append(1, *c);
}
} else {
specifier.append(1, *c);

// going by https://cplusplus.com/reference/cstdio/printf/#compatibility
// no idea what the kernel module really supports as of writing this
switch (*c) {
case '%':
{
result.append(specifier);
processing_specifier = false;
break;
}
case 's':
{
// consume 4 bytes from arguments
u32 val = 0;
if (arg_idx <= 1) {
val = currentMIPS->r[MIPS_REG_A2 + arg_idx];
} else if(arg_idx <= 5) {
val = currentMIPS->r[MIPS_REG_T0 + arg_idx - 2];
} else {
int stack_idx = arg_idx - 6;
u32 stack_cur = currentMIPS->r[MIPS_REG_SP] + stack_idx * 4;

if (!Memory::IsValidAddress(stack_cur)) {
ERROR_LOG(SCEKERNEL, "sysclib_sprintf bad stack pointer %08x", stack_cur);
return 0;
}
val = Memory::Read_U32(stack_cur);
DEBUG_LOG(SCEKERNEL, "sysclib_sprintf fetching %08x from sp + %u", val, stack_idx * 4);
}
arg_idx++;

if (!Memory::IsValidAddress(val)) {
ERROR_LOG(SCEKERNEL, "sysclib_sprintf bad string reference %08x", val);
return 0;
}

const char *str = Memory::GetCharPointerUnchecked(val);
// limit the string length and hope that we don't crash on a bad string reference
char buf[1024] = {0};
strncpy(buf, str, sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
result.append(buf);
processing_specifier = false;
break;
}
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
case 'c':
case 'p':
case 'n':
{
u64 val = 0;
if (bytes_to_read == 0) {
bytes_to_read = 4;
}
int read_cnt = 0;
while (bytes_to_read != 0) {
u32 val_from_arg = 0;
if (arg_idx <= 1) {
val_from_arg = currentMIPS->r[MIPS_REG_A2 + arg_idx];
} else if (arg_idx <= 5) {
val_from_arg = currentMIPS->r[MIPS_REG_T0 + arg_idx - 2];
} else {
int stack_idx = arg_idx - 6;
u32 stack_cur = currentMIPS->r[MIPS_REG_SP] + stack_idx * 4;

if (!Memory::IsValidAddress(stack_cur)) {
ERROR_LOG(SCEKERNEL, "sysclib_sprintf bad stack pointer %08x", stack_cur);
return 0;
}
val_from_arg = Memory::Read_U32(stack_cur);
DEBUG_LOG(SCEKERNEL, "sysclib_sprintf fetching %08x from sp + %u", val_from_arg, stack_idx * 4);
}
arg_idx++;

val = val | ((u64)val_from_arg << (read_cnt * 32));

bytes_to_read = bytes_to_read - 4;
read_cnt++;
}
char buf[1024] = {0};
snprintf(buf, sizeof(buf), specifier.c_str(), val);
buf[sizeof(buf) - 1] = '\0';
result.append(buf);
processing_specifier = false;
break;
}
case 'h':
{
// allegrex calling convention is 4 bytes aligned
bytes_to_read = 4;
break;
}
case 'l':
{
bytes_to_read = bytes_to_read + 4;
break;
}
}
}
}

// if a small buffer was allocated by the program, we will likely crash
strcpy((char *)Memory::GetPointerUnchecked(dst), result.c_str());
return result.length();
}

static u32 sysclib_memset(u32 destAddr, int data, int size) {
Expand Down

0 comments on commit 59be4b0

Please sign in to comment.