Files
Xenia-Canary/src/xenia/kernel/xboxkrnl_strings.cc
2015-06-16 23:16:18 -06:00

1040 lines
26 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <cstring>
#include <iomanip>
#include <sstream>
#include "xenia/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/objects/xthread.h"
#include "xenia/kernel/objects/xuser_module.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/util/xex2.h"
#include "xenia/kernel/xboxkrnl_private.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
enum FormatState {
FS_Invalid = 0,
FS_Unknown,
FS_Start,
FS_Flags,
FS_Width,
FS_PrecisionStart,
FS_Precision,
FS_Size,
FS_Type,
FS_End,
};
enum FormatFlags {
FF_LeftJustify = 1 << 0,
FF_AddLeadingZeros = 1 << 1,
FF_AddPositive = 1 << 2,
FF_AddPositiveAsSpace = 1 << 3,
FF_AddNegative = 1 << 4,
FF_AddPrefix = 1 << 5,
FF_IsShort = 1 << 6,
FF_IsLong = 1 << 7,
FF_IsLongLong = 1 << 8,
FF_IsWide = 1 << 9,
FF_IsSigned = 1 << 10,
FF_ForceLeadingZero = 1 << 11,
};
enum ArgumentSize {
AS_Default = 0,
AS_Short,
AS_Long,
AS_LongLong,
};
class FormatData {
public:
virtual uint16_t get() = 0;
virtual uint16_t peek(int32_t offset) = 0;
virtual void skip(int32_t count) = 0;
virtual bool put(uint16_t c) = 0;
};
class ArgList {
public:
virtual uint32_t get32() = 0;
virtual uint64_t get64() = 0;
};
// Making the assumption that the Xbox 360's implementation of the
// printf-functions matches what is described on MSDN's documentation for the
// Windows CRT:
//
// "Format Specification Syntax: printf and wprintf Functions"
// https://msdn.microsoft.com/en-us/library/56e442dc.aspx
std::string format_double(double value, int32_t precision, uint16_t c,
uint32_t flags) {
if (precision < 0) {
precision = 6;
} else if (precision == 0 && c == 'g') {
precision = 1;
}
std::ostringstream temp;
temp << std::setprecision(precision);
if (c == 'f') {
temp << std::fixed;
} else if (c == 'e' || c == 'E') {
temp << std::scientific;
} else if (c == 'a' || c == 'A') {
temp << std::hexfloat;
} else if (c == 'g' || c == 'G') {
temp << std::defaultfloat;
}
if (c == 'E' || c == 'G' || c == 'A') {
temp << std::uppercase;
}
if (flags & FF_AddPrefix) {
temp << std::showpoint;
}
temp << value;
return temp.str();
}
int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
const bool wide) {
int32_t count = 0;
char work[512];
wchar_t wwork[4];
struct {
const void* buffer;
int32_t length;
bool is_wide;
bool swap_wide;
} text;
struct {
char buffer[2];
int32_t length;
} prefix;
auto state = FS_Unknown;
uint32_t flags = 0;
int32_t width = 0;
int32_t precision = -1;
ArgumentSize size = AS_Default;
int32_t radix = 0;
const char* digits = nullptr;
text.buffer = nullptr;
text.is_wide = false;
text.swap_wide = true;
text.length = 0;
prefix.buffer[0] = '\0';
prefix.length = 0;
for (uint16_t c = data.get();; c = data.get()) {
if (state == FS_Unknown) {
if (!c) { // the end
return count;
} else if (c != '%') {
output:
if (!data.put(c)) {
return -1;
}
++count;
continue;
}
state = FS_Start;
c = data.get();
// fall through
}
// in any state, if c is \0, it's bad
if (!c) {
return -1;
}
restart:
switch (state) {
case FS_Start: {
if (c == '%') {
state = FS_Unknown;
goto output;
}
state = FS_Flags;
// reset to defaults
flags = 0;
width = 0;
precision = -1;
size = AS_Default;
radix = 0;
digits = nullptr;
text.buffer = nullptr;
text.is_wide = false;
text.swap_wide = true;
text.length = 0;
prefix.buffer[0] = '\0';
prefix.length = 0;
// fall through, don't need to goto restart
}
// https://msdn.microsoft.com/en-us/library/8aky45ct.aspx
case FS_Flags: {
if (c == '-') {
flags |= FF_LeftJustify;
continue;
} else if (c == '+') {
flags |= FF_AddPositive;
continue;
} else if (c == '0') {
flags |= FF_AddLeadingZeros;
continue;
} else if (c == ' ') {
flags |= FF_AddPositiveAsSpace;
continue;
} else if (c == '#') {
flags |= FF_AddPrefix;
continue;
}
state = FS_Width;
// fall through, don't need to goto restart
}
// https://msdn.microsoft.com/en-us/library/25366k66.aspx
case FS_Width: {
if (c == '*') {
width = (int32_t)args.get32();
if (width < 0) {
flags |= FF_LeftJustify;
width = -width;
}
state = FS_PrecisionStart;
continue;
} else if (c >= '0' && c <= '9') {
width *= 10;
width += c - '0';
continue;
}
state = FS_PrecisionStart;
// fall through, don't need to goto restart
}
// https://msdn.microsoft.com/en-us/library/0ecbz014.aspx
case FS_PrecisionStart: {
if (c == '.') {
state = FS_Precision;
precision = 0;
continue;
}
state = FS_Size;
goto restart;
}
// https://msdn.microsoft.com/en-us/library/0ecbz014.aspx
case FS_Precision: {
if (c == '*') {
precision = (int32_t)args.get32();
if (precision < 0) {
precision = -1;
}
state = FS_Size;
continue;
} else if (c >= '0' && c <= '9') {
precision *= 10;
precision += c - '0';
continue;
}
state = FS_Size;
// fall through
}
// https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx
case FS_Size: {
if (c == 'l') {
if (data.peek(0) == 'l') {
data.skip(1);
flags |= FF_IsLongLong;
} else {
flags |= FF_IsLong;
}
state = FS_Type;
continue;
} else if (c == 'h') {
flags |= FF_IsShort;
state = FS_Type;
continue;
} else if (c == 'w') {
flags |= FF_IsWide;
state = FS_Type;
continue;
} else if (c == 'I') {
if (data.peek(0) == '6' && data.peek(1) == '4') {
data.skip(2);
flags |= FF_IsLongLong;
state = FS_Type;
continue;
} else if (data.peek(0) == '3' && data.peek(1) == '2') {
data.skip(2);
state = FS_Type;
continue;
} else {
state = FS_Type;
continue;
}
}
// fall through
}
// https://msdn.microsoft.com/en-us/library/hf4y5e3w.aspx
case FS_Type: {
// wide character
switch (c) {
case 'C': {
if (!(flags & (FF_IsShort | FF_IsLong | FF_IsWide))) {
flags |= FF_IsWide;
}
// fall through
}
// character
case 'c': {
bool is_wide = ((flags & (FF_IsLong | FF_IsWide)) != 0) ^ wide;
auto value = args.get32();
if (!is_wide) {
work[0] = (uint8_t)value;
text.buffer = &work[0];
text.length = 1;
text.is_wide = false;
} else {
wwork[0] = (uint16_t)value;
text.buffer = &wwork[0];
text.length = 1;
text.is_wide = true;
text.swap_wide = false;
}
break;
}
// signed decimal integer
case 'd':
case 'i': {
flags |= FF_IsSigned;
digits = "0123456789";
radix = 10;
integer:
assert_not_null(digits);
assert_not_zero(radix);
int64_t value;
if (flags & FF_IsLongLong) {
value = (int64_t)args.get64();
} else if (flags & FF_IsLong) {
value = (int32_t)args.get32();
} else if (flags & FF_IsShort) {
value = (int16_t)args.get32();
} else {
value = (int32_t)args.get32();
}
if (precision >= 0) {
precision = std::min(precision, (int32_t)xe::countof(work));
} else {
precision = 1;
}
if ((flags & FF_IsSigned) && value < 0) {
value = -value;
flags |= FF_AddNegative;
}
if (!(flags & FF_IsLongLong)) {
value &= UINT32_MAX;
}
if (value == 0) {
prefix.length = 0;
}
char* end = &work[xe::countof(work) - 1];
char* start = end;
start[0] = '\0';
while (precision-- > 0 || value != 0) {
auto digit = (int32_t)(value % radix);
value /= radix;
assert_true(digit < strlen(digits));
*--start = digits[digit];
}
if ((flags & FF_ForceLeadingZero) &&
(start == end || *start != '0')) {
*--start = '0';
}
text.buffer = start;
text.length = (int32_t)(end - start);
text.is_wide = false;
break;
}
// unsigned octal integer
case 'o': {
digits = "01234567";
radix = 8;
if (flags & FF_AddPrefix) {
flags |= FF_ForceLeadingZero;
}
goto integer;
}
// unsigned decimal integer
case 'u': {
digits = "0123456789";
radix = 10;
goto integer;
}
// unsigned hexadecimal integer
case 'x':
case 'X': {
digits = c == 'x' ? "0123456789abcdef" : "0123456789ABCDEF";
radix = 16;
if (flags & FF_AddPrefix) {
prefix.buffer[0] = '0';
prefix.buffer[1] = c == 'x' ? 'x' : 'X';
prefix.length = 2;
}
goto integer;
}
// floating-point with exponent
case 'e':
case 'E': {
// fall through
}
// floating-point without exponent
case 'f': {
floatingpoint:
flags |= FF_IsSigned;
int64_t dummy = args.get64();
double value = *(double*)&dummy;
if (value < 0) {
value = -value;
flags |= FF_AddNegative;
}
auto s = format_double(value, precision, c, flags);
auto length = (int32_t)s.size();
assert_true(length < xe::countof(work));
auto start = &work[0];
auto end = &start[length];
std::memcpy(start, s.c_str(), length);
end[0] = '\0';
text.buffer = start;
text.length = (int32_t)(end - start);
text.is_wide = false;
break;
}
// floating-point with or without exponent
case 'g':
case 'G': {
goto floatingpoint;
}
// floating-point in hexadecimal
case 'a':
case 'A': {
goto floatingpoint;
}
// pointer to integer
case 'n': {
auto pointer = (uint32_t)args.get32();
if (flags & FF_IsShort) {
SHIM_SET_MEM_16(pointer, (uint16_t)count);
} else {
SHIM_SET_MEM_32(pointer, (uint32_t)count);
}
continue;
}
// pointer
case 'p': {
digits = "0123456789ABCDEF";
radix = 16;
precision = 8;
flags &= ~(FF_IsLongLong | FF_IsShort);
flags |= FF_IsLong;
goto integer;
}
// wide string
case 'S': {
if (!(flags & (FF_IsShort | FF_IsLong | FF_IsWide))) {
flags |= FF_IsWide;
}
// fall through
}
// string
case 's': {
uint32_t pointer = args.get32();
int32_t cap = precision < 0 ? INT32_MAX : precision;
if (pointer == 0) {
auto nullstr = "(null)";
text.buffer = nullstr;
text.length = std::min((int32_t)strlen(nullstr), cap);
text.is_wide = false;
} else {
void* str = SHIM_MEM_ADDR(pointer);
bool is_wide = ((flags & (FF_IsLong | FF_IsWide)) != 0) ^ wide;
int32_t length;
if (!is_wide) {
length = 0;
for (auto s = (const uint8_t*)str; cap > 0 && *s; ++s, cap--) {
length++;
}
} else {
length = 0;
for (auto s = (const uint16_t*)str; cap > 0 && *s; ++s, cap--) {
length++;
}
}
text.buffer = str;
text.length = length;
text.is_wide = is_wide;
}
break;
}
// ANSI_STRING / UNICODE_STRING
case 'Z': {
assert_always();
}
default: { assert_always(); }
}
}
}
if (flags & FF_IsSigned) {
if (flags & FF_AddNegative) {
prefix.buffer[0] = '-';
prefix.length = 1;
} else if (flags & FF_AddPositive) {
prefix.buffer[0] = '+';
prefix.length = 1;
} else if (flags & FF_AddPositiveAsSpace) {
prefix.buffer[0] = ' ';
prefix.length = 1;
}
}
int32_t padding = width - text.length - prefix.length;
if (!(flags & (FF_LeftJustify | FF_AddLeadingZeros)) && padding > 0) {
count += padding;
while (padding-- > 0) {
if (!data.put(' ')) {
return -1;
}
}
}
if (prefix.length > 0) {
int32_t remaining = prefix.length;
count += prefix.length;
auto b = &prefix.buffer[0];
while (remaining-- > 0) {
if (!data.put(*b++)) {
return -1;
}
}
}
if ((flags & FF_AddLeadingZeros) && !(flags & (FF_LeftJustify)) &&
padding > 0) {
count += padding;
while (padding-- > 0) {
if (!data.put('0')) {
return -1;
}
}
}
int32_t remaining = text.length;
if (!text.is_wide) {
// it's a const char*
auto b = (const uint8_t*)text.buffer;
while (remaining-- > 0) {
if (!data.put(*b++)) {
return -1;
}
}
} else {
// it's a const wchar_t*
auto b = (const uint16_t*)text.buffer;
if (text.swap_wide) {
while (remaining-- > 0) {
if (!data.put(xe::byte_swap(*b++))) {
return -1;
}
}
} else {
while (remaining-- > 0) {
if (!data.put(*b++)) {
return -1;
}
}
}
}
count += text.length;
// right padding
if ((flags & FF_LeftJustify) && padding > 0) {
count += padding;
while (padding-- > 0) {
if (!data.put(' ')) {
return -1;
}
}
}
state = FS_Unknown;
}
return count;
}
class StackArgList : public ArgList {
public:
StackArgList(PPCContext* ppc_context, int32_t index)
: ppc_context(ppc_context), index_(index) {}
uint32_t get32() { return (uint32_t)get64(); }
uint64_t get64() {
auto value = SHIM_GET_ARG_64(index_);
++index_;
return value;
}
private:
PPCContext* ppc_context;
int32_t index_;
};
class ArrayArgList : public ArgList {
public:
ArrayArgList(PPCContext* ppc_context, uint32_t arg_ptr)
: ppc_context(ppc_context), arg_ptr_(arg_ptr), index_(0) {}
uint32_t get32() { return (uint32_t)get64(); }
uint64_t get64() {
auto value = SHIM_MEM_64(arg_ptr_ + (8 * index_));
++index_;
return value;
}
private:
PPCContext* ppc_context;
uint32_t arg_ptr_;
int32_t index_;
};
class StringFormatData : public FormatData {
public:
StringFormatData(const uint8_t* input) : input_(input) {}
uint16_t get() {
uint16_t result = *input_;
if (result) {
input_++;
}
return result;
}
uint16_t peek(int32_t offset) { return input_[offset]; }
void skip(int32_t count) {
while (count-- > 0) {
if (!*input_) {
break;
}
}
}
bool put(uint16_t c) {
if (c >= 0x100) {
return false;
}
output_ << (char)c;
return true;
}
std::string str() const { return output_.str(); }
private:
const uint8_t* input_;
std::ostringstream output_;
};
class WideStringFormatData : public FormatData {
public:
WideStringFormatData(const uint16_t* input) : input_(input) {}
uint16_t get() {
uint16_t result = *input_;
if (result) {
input_++;
}
return xe::byte_swap(result);
}
uint16_t peek(int32_t offset) { return input_[offset]; }
void skip(int32_t count) {
while (count-- > 0) {
if (!*input_) {
break;
}
}
}
bool put(uint16_t c) {
output_ << (wchar_t)c;
return true;
}
std::wstring wstr() const { return output_.str(); }
private:
const uint16_t* input_;
std::wostringstream output_;
};
class WideCountFormatData : public FormatData {
public:
WideCountFormatData(const uint16_t* input) : input_(input), count_(0) {}
uint16_t get() {
uint16_t result = *input_;
if (result) {
input_++;
}
return xe::byte_swap(result);
}
uint16_t peek(int32_t offset) { return input_[offset]; }
void skip(int32_t count) {
while (count-- > 0) {
if (!*input_) {
break;
}
}
}
bool put(uint16_t c) {
++count_;
return true;
}
const int32_t count() const { return count_; }
private:
const uint16_t* input_;
int32_t count_;
};
SHIM_CALL DbgPrint_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(0);
if (!format_ptr) {
SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER);
return;
}
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 1);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) {
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
return;
}
XELOGD("(DbgPrint) %s", data.str().c_str());
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
}
// https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx
SHIM_CALL _snprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
int32_t buffer_count = SHIM_GET_ARG_32(1);
uint32_t format_ptr = SHIM_GET_ARG_32(2);
XELOGD("_snprintf(%08X, %i, %08X, ...)", buffer_ptr, buffer_count,
format_ptr);
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 3);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count < 0) {
if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe
}
} else if (count <= buffer_count) {
std::memcpy(buffer, data.str().c_str(), count);
if (count < buffer_count) {
buffer[count] = '\0';
}
} else {
std::memcpy(buffer, data.str().c_str(), buffer_count);
count = -1; // for return value
}
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
SHIM_CALL sprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
uint32_t format_ptr = SHIM_GET_ARG_32(1);
XELOGD("sprintf(%08X, %08X, ...)", buffer_ptr, format_ptr);
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 2);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) {
buffer[0] = '\0';
} else {
std::memcpy(buffer, data.str().c_str(), count);
buffer[count] = '\0';
}
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
SHIM_CALL swprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
uint32_t format_ptr = SHIM_GET_ARG_32(1);
XELOGD("swprintf(%08X, %08X, ...)", buffer_ptr, format_ptr);
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 2);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) {
buffer[0] = '\0';
} else {
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
buffer[count] = '\0';
}
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/1kt27hek.aspx
SHIM_CALL _vsnprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
int32_t buffer_count = SHIM_GET_ARG_32(1);
uint32_t format_ptr = SHIM_GET_ARG_32(2);
uint32_t arg_ptr = SHIM_GET_ARG_32(3);
XELOGD("_vsnprintf(%08X, %i, %08X, %08X)", buffer_ptr, buffer_count,
format_ptr, arg_ptr);
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count < 0) {
// Error.
if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe
}
} else if (count <= buffer_count) {
// Fit within the buffer.
std::memcpy(buffer, data.str().c_str(), count);
if (count < buffer_count) {
buffer[count] = '\0';
}
} else {
// Overflowed buffer. We still return the count we would have written.
std::memcpy(buffer, data.str().c_str(), buffer_count);
}
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vsprintf_shim(PPCContext* ppc_context, KernelState* kernel_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);
XELOGD("vsprintf(%08X, %08X, %08X)", buffer_ptr, format_ptr, arg_ptr);
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) {
buffer[0] = '\0';
} else {
std::memcpy(buffer, data.str().c_str(), count);
buffer[count] = '\0';
}
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/w05tbk72.aspx
SHIM_CALL _vscwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(0);
uint32_t arg_ptr = SHIM_GET_ARG_32(1);
XELOGD("_vscwprintf(%08X, %08X)", format_ptr, arg_ptr);
if (format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr);
WideCountFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
assert_true(count < 0 || data.count() == count);
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vswprintf_shim(PPCContext* ppc_context, KernelState* kernel_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);
XELOGD("vswprintf(%08X, %08X, %08X)", buffer_ptr, format_ptr, arg_ptr);
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
if (count <= 0) {
buffer[0] = '\0';
} else {
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
buffer[count] = '\0';
}
SHIM_SET_RETURN_32(count);
}
} // namespace kernel
} // namespace xe
void xe::kernel::xboxkrnl::RegisterStringExports(
xe::cpu::ExportResolver* export_resolver, KernelState* state) {
SHIM_SET_MAPPING("xboxkrnl.exe", DbgPrint, state);
SHIM_SET_MAPPING("xboxkrnl.exe", _snprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", sprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", swprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", _vsnprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", vsprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", _vscwprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", vswprintf, state);
}