[Kernel] Replace SHIM version of kernel strings with exports

- Split into header file for usage in other places
- Removed most of define SHIM Utils
This commit is contained in:
Gliniak
2026-03-27 00:06:09 +01:00
committed by Radosław Gliński
parent b575c68418
commit fdbaaaba21
3 changed files with 394 additions and 435 deletions

View File

@@ -28,23 +28,6 @@ namespace kernel {
using PPCContext = xe::cpu::ppc::PPCContext;
#define SHIM_CALL void
#define SHIM_SET_MAPPING(library_name, export_name, shim_data) \
export_resolver->SetFunctionMapping( \
library_name, ordinals::export_name, \
(xe::cpu::xe_kernel_export_shim_fn)export_name##_entry);
#define SHIM_MEM_ADDR(a) ((a) ? ppc_context->TranslateVirtual(a) : nullptr)
#define SHIM_MEM_8(a) xe::load_and_swap<uint8_t>(SHIM_MEM_ADDR(a))
#define SHIM_MEM_16(a) xe::load_and_swap<uint16_t>(SHIM_MEM_ADDR(a))
#define SHIM_MEM_32(a) xe::load_and_swap<uint32_t>(SHIM_MEM_ADDR(a))
#define SHIM_MEM_64(a) xe::load_and_swap<uint64_t>(SHIM_MEM_ADDR(a))
#define SHIM_SET_MEM_8(a, v) xe::store_and_swap<uint8_t>(SHIM_MEM_ADDR(a), v)
#define SHIM_SET_MEM_16(a, v) xe::store_and_swap<uint16_t>(SHIM_MEM_ADDR(a), v)
#define SHIM_SET_MEM_32(a, v) xe::store_and_swap<uint32_t>(SHIM_MEM_ADDR(a), v)
#define SHIM_SET_MEM_64(a, v) xe::store_and_swap<uint64_t>(SHIM_MEM_ADDR(a), v)
namespace util {
inline uint32_t get_arg_stack_ptr(PPCContext* ppc_context, uint8_t index) {
return ((uint32_t)ppc_context->r[1]) + 0x54 + index * 8;
@@ -54,32 +37,36 @@ inline uint8_t get_arg_8(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) {
return (uint8_t)ppc_context->r[3 + index];
}
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_8(stack_address);
const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return xe::load_and_swap<uint8_t>(
ppc_context->TranslateVirtual(stack_address));
}
inline uint16_t get_arg_16(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) {
return (uint16_t)ppc_context->r[3 + index];
}
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_16(stack_address);
const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return xe::load_and_swap<uint16_t>(
ppc_context->TranslateVirtual(stack_address));
}
inline uint32_t get_arg_32(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) {
return (uint32_t)ppc_context->r[3 + index];
}
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_32(stack_address);
const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return xe::load_and_swap<uint32_t>(
ppc_context->TranslateVirtual(stack_address));
}
inline uint64_t get_arg_64(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) {
return ppc_context->r[3 + index];
}
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_64(stack_address);
const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return xe::load_and_swap<uint64_t>(
ppc_context->TranslateVirtual(stack_address));
}
inline std::string_view TranslateAnsiString(const Memory* memory,
@@ -128,15 +115,8 @@ inline std::u16string TranslateUnicodeString(
}
} // namespace util
#define SHIM_GET_ARG_8(n) util::get_arg_8(ppc_context, n)
#define SHIM_GET_ARG_16(n) util::get_arg_16(ppc_context, n)
#define SHIM_GET_ARG_32(n) util::get_arg_32(ppc_context, n)
#define SHIM_GET_ARG_64(n) util::get_arg_64(ppc_context, n)
#define SHIM_SET_RETURN_32(v) ppc_context->r[3] = (uint64_t)((int32_t)v)
#define SHIM_STRUCT(type, address) \
reinterpret_cast<type*>(SHIM_MEM_ADDR(address))
namespace shim {
class Param {

View File

@@ -2,14 +2,14 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Copyright 2026 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xboxkrnl/xboxkrnl_strings.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/xbox.h"
DEFINE_bool(log_string_format_kernel_calls, false,
"Log kernel calls with the kHighFrequency tag.", "Logging");
@@ -18,56 +18,6 @@ namespace xe {
namespace kernel {
namespace xboxkrnl {
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,
FF_InvertWide = 1 << 12,
};
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:
@@ -503,9 +453,11 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
case 'n': {
auto pointer = (uint32_t)args.get32();
if (flags & FF_IsShort) {
SHIM_SET_MEM_16(pointer, (uint16_t)count);
xe::store_and_swap<uint16_t>(
ppc_context->TranslateVirtual(pointer), (uint16_t)count);
} else {
SHIM_SET_MEM_32(pointer, (uint32_t)count);
xe::store_and_swap<uint32_t>(
ppc_context->TranslateVirtual(pointer), (uint32_t)count);
}
continue;
}
@@ -537,7 +489,7 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
text.length = std::min((int32_t)strlen(nullstr), cap);
text.is_wide = false;
} else {
void* str = SHIM_MEM_ADDR(pointer);
void* str = ppc_context->TranslateVirtual(pointer);
bool is_wide;
if (flags & (FF_IsLong | FF_IsWide)) {
// "An ls, lS, ws or wS type specifier is synonymous with S in
@@ -673,201 +625,46 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
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;
dword_result_t DbgPrint_entry(lpstring_t format, const ppc_context_t& ctx) {
if (!format) {
return X_STATUS_INVALID_PARAMETER;
}
private:
PPCContext* ppc_context;
int32_t index_;
};
StackArgList args(ctx, 1);
StringFormatData data(
reinterpret_cast<const uint8_t*>(format.host_address()));
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 (!get()) {
break;
}
}
}
bool put(uint16_t c) {
if (c >= 0x100) {
return false;
}
output_.push_back(char(c));
return true;
}
const std::string& str() const { return output_; }
private:
const uint8_t* input_;
std::string 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 xe::byte_swap(input_[offset]); }
void skip(int32_t count) {
while (count-- > 0) {
if (!get()) {
break;
}
}
}
bool put(uint16_t c) {
output_.push_back(char16_t(c));
return true;
}
const std::u16string& wstr() const { return output_; }
private:
const uint16_t* input_;
std::u16string 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 xe::byte_swap(input_[offset]); }
void skip(int32_t count) {
while (count-- > 0) {
if (!get()) {
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_entry(PPCContext* ppc_context) {
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);
const int32_t count = format_core(ctx, data, args, false);
if (count <= 0) {
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
return;
return X_STATUS_SUCCESS;
}
// trim whitespace from end of message
auto str = data.str();
str.erase(std::find_if(str.rbegin(), str.rend(),
[](uint8_t c) { return !std::isspace(c); })
.base(),
str.end());
XELOGI("(DbgPrint) {}", string_util::rtrim(data.str()));
XELOGI("(DbgPrint) {}", str);
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
return X_STATUS_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT1(DbgPrint, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx
SHIM_CALL _snprintf_entry(PPCContext* ppc_context) {
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);
dword_result_t _snprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
lpstring_t format, const ppc_context_t& ctx) {
if (!buffer_ptr || buffer_count <= 0 || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("_snprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr, buffer_count,
format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)));
XELOGD("_snprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr.value(),
buffer_count.value(), format.guest_address(), format.value());
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
StackArgList args(ctx, 3);
StringFormatData data(
reinterpret_cast<const uint8_t*>(format.host_address()));
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
auto* buffer = kernel_memory()->TranslateVirtual<char*>(buffer_ptr);
StackArgList args(ppc_context, 3);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
int32_t count = format_core(ctx, data, args, false);
if (count < 0) {
if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe
@@ -881,138 +678,127 @@ SHIM_CALL _snprintf_entry(PPCContext* ppc_context) {
std::memcpy(buffer, data.str().c_str(), buffer_count);
count = -1; // for return value
}
SHIM_SET_RETURN_32(count);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(_snprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
SHIM_CALL sprintf_entry(PPCContext* ppc_context) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
uint32_t format_ptr = SHIM_GET_ARG_32(1);
dword_result_t sprintf_entry(dword_t buffer_ptr, lpstring_t format,
const ppc_context_t& ctx) {
if (!buffer_ptr || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("sprintf({:08X}, {:08X}({}), ...)", buffer_ptr, format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)));
XELOGD("sprintf({:08X}, {:08X}({}), ...)", buffer_ptr.value(),
format.guest_address(), format.value());
}
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
StackArgList args(ctx, 2);
StringFormatData data(
reinterpret_cast<const uint8_t*>(format.host_address()));
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
auto* buffer = kernel_memory()->TranslateVirtual<char*>(buffer_ptr);
StackArgList args(ppc_context, 2);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
int32_t count = format_core(ctx, 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);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(sprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx
SHIM_CALL _snwprintf_entry(PPCContext* ppc_context) {
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);
dword_result_t _snwprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
lpu16string_t format,
const ppc_context_t& ctx) {
if (!buffer_ptr || buffer_count <= 0 || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("_snwprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr, buffer_count,
format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))));
XELOGD("_snwprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr.value(),
buffer_count.value(), format.guest_address(),
xe::to_utf8(format.value()));
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
StackArgList args(ctx, 3);
WideStringFormatData data(
reinterpret_cast<const uint16_t*>(format.host_address()));
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
StackArgList args(ppc_context, 3);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
int32_t count = format_core(ctx, data, args, true);
if (count < 0) {
if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe
}
} else if (count <= buffer_count) {
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
xe::copy_and_swap(buffer, data.wstr().c_str(), count);
if (count < buffer_count) {
buffer[count] = '\0';
}
} else {
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), buffer_count);
xe::copy_and_swap(buffer, data.wstr().c_str(), buffer_count);
count = -1; // for return value
}
SHIM_SET_RETURN_32(count);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(_snwprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
SHIM_CALL swprintf_entry(PPCContext* ppc_context) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
uint32_t format_ptr = SHIM_GET_ARG_32(1);
dword_result_t swprintf_entry(dword_t buffer_ptr, lpu16string_t format,
const ppc_context_t& ctx) {
if (!buffer_ptr || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("swprintf({:08X}, {:08X}({}), ...)", buffer_ptr, format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))));
XELOGD("swprintf({:08X}, {:08X}({}), ...)", buffer_ptr.value(),
format.guest_address(), xe::to_utf8(format.value()));
}
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
StackArgList args(ctx, 2);
WideStringFormatData data(
reinterpret_cast<const uint16_t*>(format.host_address()));
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
StackArgList args(ppc_context, 2);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
int32_t count = format_core(ctx, data, args, true);
if (count <= 0) {
buffer[0] = '\0';
} else {
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
xe::copy_and_swap(buffer, data.wstr().c_str(), count);
buffer[count] = '\0';
}
SHIM_SET_RETURN_32(count);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(swprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/1kt27hek.aspx
SHIM_CALL _vsnprintf_entry(PPCContext* ppc_context) {
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);
dword_result_t _vsnprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
lpstring_t format, lpdword_t arg_ptr,
const ppc_context_t& ctx) {
if (!buffer_ptr || buffer_count <= 0 || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("_vsnprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr,
buffer_count, format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)), arg_ptr);
XELOGD("_vsnprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr.value(),
buffer_count.value(), format.guest_address(), format.value(),
arg_ptr.value());
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = kernel_memory()->TranslateVirtual<uint8_t*>(buffer_ptr);
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ctx, arg_ptr.guest_address());
StringFormatData data(
reinterpret_cast<const uint8_t*>(format.host_address()));
ArrayArgList args(ppc_context, arg_ptr);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
int32_t count = format_core(ctx, data, args, false);
if (count < 0) {
// Error.
if (buffer_count > 0) {
@@ -1029,37 +815,31 @@ SHIM_CALL _vsnprintf_entry(PPCContext* ppc_context) {
std::memcpy(buffer, data.str().c_str(), buffer_count);
count = -1;
}
SHIM_SET_RETURN_32(count);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(_vsnprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/1kt27hek.aspx
SHIM_CALL _vsnwprintf_entry(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);
dword_result_t _vsnwprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
lpu16string_t format, lpdword_t arg_ptr,
const ppc_context_t& ctx) {
if (!buffer_ptr || buffer_count <= 0 || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("_vsnwprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr,
buffer_count, format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
XELOGD("_vsnwprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr.value(),
buffer_count.value(), format.guest_address(),
xe::to_utf8(format.value()), arg_ptr.guest_address());
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
ArrayArgList args(ctx, arg_ptr.guest_address());
WideStringFormatData data(
reinterpret_cast<const uint16_t*>(format.host_address()));
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
ArrayArgList args(ppc_context, arg_ptr);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
int32_t count = format_core(ctx, data, args, true);
if (count < 0) {
// Error.
if (buffer_count > 0) {
@@ -1067,125 +847,101 @@ SHIM_CALL _vsnwprintf_entry(PPCContext* ppc_context,
}
} else if (count <= buffer_count) {
// Fit within the buffer.
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
xe::copy_and_swap(buffer, data.wstr().c_str(), count);
if (count < buffer_count) {
buffer[count] = '\0';
}
} else {
// Overflowed buffer. We still return the count we would have written.
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), buffer_count);
xe::copy_and_swap(buffer, data.wstr().c_str(), buffer_count);
}
SHIM_SET_RETURN_32(count);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(_vsnwprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vsprintf_entry(PPCContext* ppc_context) {
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);
dword_result_t vsprintf_entry(dword_t buffer_ptr, lpstring_t format,
lpdword_t arg_ptr, const ppc_context_t& ctx) {
if (!buffer_ptr || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("vsprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr, format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)), arg_ptr);
XELOGD("vsprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr.value(),
format.guest_address(), format.value(), arg_ptr.guest_address());
}
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
ArrayArgList args(ctx, arg_ptr.guest_address());
StringFormatData data(
reinterpret_cast<const uint8_t*>(format.host_address()));
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
auto buffer = kernel_memory()->TranslateVirtual<uint8_t*>(buffer_ptr);
ArrayArgList args(ppc_context, arg_ptr);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
int32_t count = format_core(ctx, 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);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(vsprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/w05tbk72.aspx
SHIM_CALL _vscwprintf_entry(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(0);
uint32_t arg_ptr = SHIM_GET_ARG_32(1);
dword_result_t _vscwprintf_entry(lpu16string_t format, lpdword_t arg_ptr,
const ppc_context_t& ctx) {
if (!format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("_vscwprintf({:08X}({}), {:08X})", format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
XELOGD("_vscwprintf({:08X}({}), {:08X})", format.guest_address(),
xe::to_utf8(format.value()), arg_ptr.guest_address());
}
if (format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
ArrayArgList args(ctx, arg_ptr.guest_address());
WideCountFormatData data(
reinterpret_cast<const uint16_t*>(format.host_address()));
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);
int32_t count = format_core(ctx, data, args, true);
assert_true(count < 0 || data.count() == count);
SHIM_SET_RETURN_32(count);
return count;
}
DECLARE_XBOXKRNL_EXPORT1(_vscwprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vswprintf_entry(PPCContext* ppc_context) {
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);
dword_result_t vswprintf_entry(dword_t buffer_ptr, lpu16string_t format,
lpdword_t arg_ptr, const ppc_context_t& ctx) {
if (!buffer_ptr || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) {
XELOGD("vswprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr, format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
XELOGD("vswprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr.value(),
format.guest_address(), xe::to_utf8(format.value()),
arg_ptr.guest_address());
}
if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
ArrayArgList args(ctx, arg_ptr.guest_address());
WideStringFormatData data(
reinterpret_cast<const uint16_t*>(format.host_address()));
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
ArrayArgList args(ppc_context, arg_ptr);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
int32_t count = format_core(ctx, data, args, true);
if (count <= 0) {
buffer[0] = '\0';
} else {
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
xe::copy_and_swap(buffer, data.wstr().c_str(), count);
buffer[count] = '\0';
}
SHIM_SET_RETURN_32(count);
return count;
}
#if 1
void 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", _snwprintf, 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);
SHIM_SET_MAPPING("xboxkrnl.exe", _vsnwprintf, state);
}
#endif
DECLARE_XBOXKRNL_EXPORT1(vswprintf, kNone, kImplemented);
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(String);

View File

@@ -0,0 +1,223 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2026 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XBOXKRNL_XBOXKRNL_STRINGS_H_
#define XENIA_KERNEL_XBOXKRNL_XBOXKRNL_STRINGS_H_
#include "xenia/kernel/util/shim_utils.h"
namespace xe {
namespace kernel {
namespace xboxkrnl {
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,
FF_InvertWide = 1 << 12,
};
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);
int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
const bool wide);
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() { return util::get_arg_64(ppc_context, index_++); }
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() {
return xe::load_and_swap<uint64_t>(
ppc_context->TranslateVirtual(arg_ptr_ + (8 * index_++)));
}
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 (!get()) {
break;
}
}
}
bool put(uint16_t c) {
if (c >= 0x100) {
return false;
}
output_.push_back(char(c));
return true;
}
const std::string& str() const { return output_; }
private:
const uint8_t* input_;
std::string 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 xe::byte_swap(input_[offset]); }
void skip(int32_t count) {
while (count-- > 0) {
if (!get()) {
break;
}
}
}
bool put(uint16_t c) {
output_.push_back(char16_t(c));
return true;
}
const std::u16string& wstr() const { return output_; }
private:
const uint16_t* input_;
std::u16string 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 xe::byte_swap(input_[offset]); }
void skip(int32_t count) {
while (count-- > 0) {
if (!get()) {
break;
}
}
}
bool put(uint16_t c) {
++count_;
return true;
}
const int32_t count() const { return count_; }
private:
const uint16_t* input_;
int32_t count_;
};
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif