diff --git a/src/xenia/kernel/xboxkrnl_strings.cc b/src/xenia/kernel/xboxkrnl_strings.cc index 017f7a7fc..caccdaf9c 100644 --- a/src/xenia/kernel/xboxkrnl_strings.cc +++ b/src/xenia/kernel/xboxkrnl_strings.cc @@ -26,6 +26,218 @@ namespace xe { namespace kernel { +// TODO: clean me up! +SHIM_CALL vsprintf_shim( + PPCContext* ppc_state, KernelState* state) { + + uint32_t buffer_ptr = SHIM_GET_ARG_32(0); + uint32_t format_ptr = SHIM_GET_ARG_32(1); + uint32_t arg_ptr = SHIM_GET_ARG_32(2); + + if (format_ptr == 0) { + SHIM_SET_RETURN_32(-1); + return; + } + + char* buffer = (char*)SHIM_MEM_ADDR(buffer_ptr); + const char* format = (const char*)SHIM_MEM_ADDR(format_ptr); + + int arg_index = 0; + + char* b = buffer; + for (; *format != '\0'; ++format) { + const char* start = format; + + if (*format != '%') { + *b++ = *format; + continue; + } + + ++format; + if (*format == '\0') { + break; + } + + if (*format == '%') { + *b++ = *format; + continue; + } + + const char* end; + end = format; + + // skip flags + while (*end == '-' || + *end == '+' || + *end == ' ' || + *end == '#' || + *end == '0') { + ++end; + } + + if (*end == '\0') { + break; + } + + int arg_extras = 0; + + // skip width + if (*end == '*') { + ++end; + arg_extras++; + } + else { + while (*end >= '0' && *end <= '9') { + ++end; + } + } + + if (*end == '\0') { + break; + } + + // skip precision + if (*end == '.') { + ++end; + + if (*end == '*') { + ++end; + ++arg_extras; + } + else { + while (*end >= '0' && *end <= '9') { + ++end; + } + } + } + + if (*end == '\0') { + break; + } + + // get length + int arg_size = 4; + + if (*end == 'h') { + ++end; + arg_size = 4; + if (*end == 'h') { + ++end; + } + } + else if (*end == 'l') { + ++end; + arg_size = 4; + if (*end == 'l') { + ++end; + arg_size = 8; + } + } + else if (*end == 'j') { + arg_size = 8; + ++end; + } + else if (*end == 'z') { + arg_size = 4; + ++end; + } + else if (*end == 't') { + arg_size = 8; + ++end; + } + else if (*end == 'L') { + arg_size = 8; + ++end; + } + + if (*end == '\0') { + break; + } + + if (*end == 'd' || + *end == 'i' || + *end == 'u' || + *end == 'o' || + *end == 'x' || + *end == 'X' || + *end == 'f' || + *end == 'F' || + *end == 'e' || + *end == 'E' || + *end == 'g' || + *end == 'G' || + *end == 'a' || + *end == 'A' || + *end == 'c') { + char local[512]; + local[0] = '\0'; + strncat(local, start, end + 1 - start); + + XEASSERT(arg_size == 8 || arg_size == 4); + if (arg_size == 8) { + if (arg_extras == 0) { + uint64_t value = SHIM_MEM_64(arg_ptr + (arg_index * 8)); // TODO: check if this is correct... + int result = sprintf(b, local, value); + b += result; + arg_index++; + } + else { + XEASSERT(false); + } + } + else if (arg_size == 4) { + if (arg_extras == 0) { + uint32_t value = (uint32_t)SHIM_MEM_64(arg_ptr + (arg_index * 8)); // TODO: check if this is correct... + int result = sprintf(b, local, value); + b += result; + arg_index++; + } + else { + XEASSERT(false); + } + } + } + else if (*end == 'n') + { + XEASSERT(arg_size == 4); + if (arg_extras == 0) { + uint32_t value = (uint32_t)SHIM_MEM_64(arg_ptr + (arg_index * 8)); // TODO: check if this is correct... + SHIM_SET_MEM_32(value, (uint32_t)((b - buffer) / sizeof(char))); + arg_index++; + } + else { + XEASSERT(false); + } + } + else if (*end == 's' || + *end == 'p') { + char local[512]; + local[0] = '\0'; + strncat(local, start, end + 1 - start); + + XEASSERT(arg_size == 4); + if (arg_extras == 0) { + uint32_t value = (uint32_t)SHIM_MEM_64(arg_ptr + (arg_index * 8)); // TODO: check if this is correct... + const void* pointer = (const void*)SHIM_MEM_ADDR(value); + int result = sprintf(b, local, pointer); + b += result; + arg_index++; + } + else { + XEASSERT(false); + } + } + else { + XEASSERT(false); + break; + } + format = end; + } + *b = '\0'; + SHIM_SET_RETURN_32((uint32_t)(b - buffer)); +} + + // TODO: clean me up! SHIM_CALL _vsnprintf_shim( PPCContext* ppc_state, KernelState* state) { @@ -504,6 +716,7 @@ SHIM_CALL _vswprintf_shim( void xe::kernel::xboxkrnl::RegisterStringExports( ExportResolver* export_resolver, KernelState* state) { + SHIM_SET_MAPPING("xboxkrnl.exe", vsprintf, state); SHIM_SET_MAPPING("xboxkrnl.exe", _vsnprintf, state); SHIM_SET_MAPPING("xboxkrnl.exe", _vswprintf, state); }