[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; 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 { namespace util {
inline uint32_t get_arg_stack_ptr(PPCContext* ppc_context, uint8_t index) { inline uint32_t get_arg_stack_ptr(PPCContext* ppc_context, uint8_t index) {
return ((uint32_t)ppc_context->r[1]) + 0x54 + index * 8; 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) { if (index <= 7) {
return (uint8_t)ppc_context->r[3 + index]; return (uint8_t)ppc_context->r[3 + index];
} }
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8); const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_8(stack_address); return xe::load_and_swap<uint8_t>(
ppc_context->TranslateVirtual(stack_address));
} }
inline uint16_t get_arg_16(PPCContext* ppc_context, uint8_t index) { inline uint16_t get_arg_16(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) { if (index <= 7) {
return (uint16_t)ppc_context->r[3 + index]; return (uint16_t)ppc_context->r[3 + index];
} }
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8); const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_16(stack_address); return xe::load_and_swap<uint16_t>(
ppc_context->TranslateVirtual(stack_address));
} }
inline uint32_t get_arg_32(PPCContext* ppc_context, uint8_t index) { inline uint32_t get_arg_32(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) { if (index <= 7) {
return (uint32_t)ppc_context->r[3 + index]; return (uint32_t)ppc_context->r[3 + index];
} }
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8); const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_32(stack_address); return xe::load_and_swap<uint32_t>(
ppc_context->TranslateVirtual(stack_address));
} }
inline uint64_t get_arg_64(PPCContext* ppc_context, uint8_t index) { inline uint64_t get_arg_64(PPCContext* ppc_context, uint8_t index) {
if (index <= 7) { if (index <= 7) {
return ppc_context->r[3 + index]; return ppc_context->r[3 + index];
} }
uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8); const uint32_t stack_address = get_arg_stack_ptr(ppc_context, index - 8);
return SHIM_MEM_64(stack_address); return xe::load_and_swap<uint64_t>(
ppc_context->TranslateVirtual(stack_address));
} }
inline std::string_view TranslateAnsiString(const Memory* memory, inline std::string_view TranslateAnsiString(const Memory* memory,
@@ -128,15 +115,8 @@ inline std::u16string TranslateUnicodeString(
} }
} // namespace util } // 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_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 { namespace shim {
class Param { class Param {

View File

@@ -2,14 +2,14 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * 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. * * 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/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/xbox.h"
DEFINE_bool(log_string_format_kernel_calls, false, DEFINE_bool(log_string_format_kernel_calls, false,
"Log kernel calls with the kHighFrequency tag.", "Logging"); "Log kernel calls with the kHighFrequency tag.", "Logging");
@@ -18,56 +18,6 @@ namespace xe {
namespace kernel { namespace kernel {
namespace xboxkrnl { 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 // Making the assumption that the Xbox 360's implementation of the
// printf-functions matches what is described on MSDN's documentation for the // printf-functions matches what is described on MSDN's documentation for the
// Windows CRT: // Windows CRT:
@@ -503,9 +453,11 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
case 'n': { case 'n': {
auto pointer = (uint32_t)args.get32(); auto pointer = (uint32_t)args.get32();
if (flags & FF_IsShort) { 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 { } else {
SHIM_SET_MEM_32(pointer, (uint32_t)count); xe::store_and_swap<uint32_t>(
ppc_context->TranslateVirtual(pointer), (uint32_t)count);
} }
continue; 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.length = std::min((int32_t)strlen(nullstr), cap);
text.is_wide = false; text.is_wide = false;
} else { } else {
void* str = SHIM_MEM_ADDR(pointer); void* str = ppc_context->TranslateVirtual(pointer);
bool is_wide; bool is_wide;
if (flags & (FF_IsLong | FF_IsWide)) { if (flags & (FF_IsLong | FF_IsWide)) {
// "An ls, lS, ws or wS type specifier is synonymous with S in // "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; return count;
} }
class StackArgList : public ArgList { dword_result_t DbgPrint_entry(lpstring_t format, const ppc_context_t& ctx) {
public: if (!format) {
StackArgList(PPCContext* ppc_context, int32_t index) return X_STATUS_INVALID_PARAMETER;
: 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: StackArgList args(ctx, 1);
PPCContext* ppc_context; StringFormatData data(
int32_t index_; reinterpret_cast<const uint8_t*>(format.host_address()));
};
class ArrayArgList : public ArgList { const int32_t count = format_core(ctx, data, args, false);
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);
if (count <= 0) { if (count <= 0) {
SHIM_SET_RETURN_32(X_STATUS_SUCCESS); return X_STATUS_SUCCESS;
return;
} }
// trim whitespace from end of message // trim whitespace from end of message
auto str = data.str(); XELOGI("(DbgPrint) {}", string_util::rtrim(data.str()));
str.erase(std::find_if(str.rbegin(), str.rend(),
[](uint8_t c) { return !std::isspace(c); })
.base(),
str.end());
XELOGI("(DbgPrint) {}", str); return X_STATUS_SUCCESS;
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
} }
DECLARE_XBOXKRNL_EXPORT1(DbgPrint, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx // https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx
SHIM_CALL _snprintf_entry(PPCContext* ppc_context) { dword_result_t _snprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); lpstring_t format, const ppc_context_t& ctx) {
int32_t buffer_count = SHIM_GET_ARG_32(1); if (!buffer_ptr || buffer_count <= 0 || !format) {
uint32_t format_ptr = SHIM_GET_ARG_32(2); return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("_snprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr, buffer_count, XELOGD("_snprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr.value(),
format_ptr, buffer_count.value(), format.guest_address(), format.value());
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)));
} }
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { StackArgList args(ctx, 3);
SHIM_SET_RETURN_32(-1); StringFormatData data(
return; reinterpret_cast<const uint8_t*>(format.host_address()));
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr); auto* buffer = kernel_memory()->TranslateVirtual<char*>(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 3); int32_t count = format_core(ctx, data, args, false);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count < 0) { if (count < 0) {
if (buffer_count > 0) { if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe 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); std::memcpy(buffer, data.str().c_str(), buffer_count);
count = -1; // for return value 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 // https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
SHIM_CALL sprintf_entry(PPCContext* ppc_context) { dword_result_t sprintf_entry(dword_t buffer_ptr, lpstring_t format,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(1); if (!buffer_ptr || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("sprintf({:08X}, {:08X}({}), ...)", buffer_ptr, format_ptr, XELOGD("sprintf({:08X}, {:08X}({}), ...)", buffer_ptr.value(),
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr))); format.guest_address(), format.value());
} }
if (buffer_ptr == 0 || format_ptr == 0) { StackArgList args(ctx, 2);
SHIM_SET_RETURN_32(-1); StringFormatData data(
return; reinterpret_cast<const uint8_t*>(format.host_address()));
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr); auto* buffer = kernel_memory()->TranslateVirtual<char*>(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 2); int32_t count = format_core(ctx, data, args, false);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) { if (count <= 0) {
buffer[0] = '\0'; buffer[0] = '\0';
} else { } else {
std::memcpy(buffer, data.str().c_str(), count); std::memcpy(buffer, data.str().c_str(), count);
buffer[count] = '\0'; 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 // https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx
SHIM_CALL _snwprintf_entry(PPCContext* ppc_context) { dword_result_t _snwprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); lpu16string_t format,
int32_t buffer_count = SHIM_GET_ARG_32(1); const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(2); if (!buffer_ptr || buffer_count <= 0 || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("_snwprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr, buffer_count, XELOGD("_snwprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr.value(),
format_ptr, buffer_count.value(), format.guest_address(),
xe::to_utf8( xe::to_utf8(format.value()));
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))));
} }
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { StackArgList args(ctx, 3);
SHIM_SET_RETURN_32(-1); WideStringFormatData data(
return; reinterpret_cast<const uint16_t*>(format.host_address()));
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr); char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 3); int32_t count = format_core(ctx, data, args, true);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
if (count < 0) { if (count < 0) {
if (buffer_count > 0) { if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe buffer[0] = '\0'; // write a null, just to be safe
} }
} else if (count <= buffer_count) { } 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) { if (count < buffer_count) {
buffer[count] = '\0'; buffer[count] = '\0';
} }
} else { } 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 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 // https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
SHIM_CALL swprintf_entry(PPCContext* ppc_context) { dword_result_t swprintf_entry(dword_t buffer_ptr, lpu16string_t format,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(1); if (!buffer_ptr || !format) {
return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("swprintf({:08X}, {:08X}({}), ...)", buffer_ptr, format_ptr, XELOGD("swprintf({:08X}, {:08X}({}), ...)", buffer_ptr.value(),
xe::to_utf8( format.guest_address(), xe::to_utf8(format.value()));
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))));
} }
if (buffer_ptr == 0 || format_ptr == 0) { StackArgList args(ctx, 2);
SHIM_SET_RETURN_32(-1); WideStringFormatData data(
return; reinterpret_cast<const uint16_t*>(format.host_address()));
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr); char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
StackArgList args(ppc_context, 2); int32_t count = format_core(ctx, data, args, true);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) { if (count <= 0) {
buffer[0] = '\0'; buffer[0] = '\0';
} else { } 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'; 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 // https://msdn.microsoft.com/en-us/library/1kt27hek.aspx
SHIM_CALL _vsnprintf_entry(PPCContext* ppc_context) { dword_result_t _vsnprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); lpstring_t format, lpdword_t arg_ptr,
int32_t buffer_count = SHIM_GET_ARG_32(1); const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(2); if (!buffer_ptr || buffer_count <= 0 || !format) {
uint32_t arg_ptr = SHIM_GET_ARG_32(3); return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("_vsnprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr, XELOGD("_vsnprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr.value(),
buffer_count, format_ptr, buffer_count.value(), format.guest_address(), format.value(),
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)), arg_ptr); arg_ptr.value());
} }
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { auto buffer = kernel_memory()->TranslateVirtual<uint8_t*>(buffer_ptr);
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr); ArrayArgList args(ctx, arg_ptr.guest_address());
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr); StringFormatData data(
reinterpret_cast<const uint8_t*>(format.host_address()));
ArrayArgList args(ppc_context, arg_ptr); int32_t count = format_core(ctx, data, args, false);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count < 0) { if (count < 0) {
// Error. // Error.
if (buffer_count > 0) { if (buffer_count > 0) {
@@ -1029,37 +815,31 @@ SHIM_CALL _vsnprintf_entry(PPCContext* ppc_context) {
std::memcpy(buffer, data.str().c_str(), buffer_count); std::memcpy(buffer, data.str().c_str(), buffer_count);
count = -1; count = -1;
} }
SHIM_SET_RETURN_32(count); return count;
} }
DECLARE_XBOXKRNL_EXPORT1(_vsnprintf, kNone, kImplemented);
// https://msdn.microsoft.com/en-us/library/1kt27hek.aspx // https://msdn.microsoft.com/en-us/library/1kt27hek.aspx
SHIM_CALL _vsnwprintf_entry(PPCContext* ppc_context, dword_result_t _vsnwprintf_entry(dword_t buffer_ptr, dword_t buffer_count,
KernelState* kernel_state) { lpu16string_t format, lpdword_t arg_ptr,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); const ppc_context_t& ctx) {
int32_t buffer_count = SHIM_GET_ARG_32(1); if (!buffer_ptr || buffer_count <= 0 || !format) {
uint32_t format_ptr = SHIM_GET_ARG_32(2); return -1;
uint32_t arg_ptr = SHIM_GET_ARG_32(3); }
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("_vsnwprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr, XELOGD("_vsnwprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr.value(),
buffer_count, format_ptr, buffer_count.value(), format.guest_address(),
xe::to_utf8( xe::to_utf8(format.value()), arg_ptr.guest_address());
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
} }
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { ArrayArgList args(ctx, arg_ptr.guest_address());
SHIM_SET_RETURN_32(-1); WideStringFormatData data(
return; reinterpret_cast<const uint16_t*>(format.host_address()));
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr); char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr); int32_t count = format_core(ctx, data, args, true);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
if (count < 0) { if (count < 0) {
// Error. // Error.
if (buffer_count > 0) { if (buffer_count > 0) {
@@ -1067,125 +847,101 @@ SHIM_CALL _vsnwprintf_entry(PPCContext* ppc_context,
} }
} else if (count <= buffer_count) { } else if (count <= buffer_count) {
// Fit within the buffer. // 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) { if (count < buffer_count) {
buffer[count] = '\0'; buffer[count] = '\0';
} }
} else { } else {
// Overflowed buffer. We still return the count we would have written. // 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 // https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vsprintf_entry(PPCContext* ppc_context) { dword_result_t vsprintf_entry(dword_t buffer_ptr, lpstring_t format,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); lpdword_t arg_ptr, const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(1); if (!buffer_ptr || !format) {
uint32_t arg_ptr = SHIM_GET_ARG_32(2); return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("vsprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr, format_ptr, XELOGD("vsprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr.value(),
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)), arg_ptr); format.guest_address(), format.value(), arg_ptr.guest_address());
} }
if (buffer_ptr == 0 || format_ptr == 0) { ArrayArgList args(ctx, arg_ptr.guest_address());
SHIM_SET_RETURN_32(-1); StringFormatData data(
return; reinterpret_cast<const uint8_t*>(format.host_address()));
}
auto buffer = (uint8_t*)SHIM_MEM_ADDR(buffer_ptr); auto buffer = kernel_memory()->TranslateVirtual<uint8_t*>(buffer_ptr);
auto format = (const uint8_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr); int32_t count = format_core(ctx, data, args, false);
StringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, false);
if (count <= 0) { if (count <= 0) {
buffer[0] = '\0'; buffer[0] = '\0';
} else { } else {
std::memcpy(buffer, data.str().c_str(), count); std::memcpy(buffer, data.str().c_str(), count);
buffer[count] = '\0'; 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 // https://msdn.microsoft.com/en-us/library/w05tbk72.aspx
SHIM_CALL _vscwprintf_entry(PPCContext* ppc_context, dword_result_t _vscwprintf_entry(lpu16string_t format, lpdword_t arg_ptr,
KernelState* kernel_state) { const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(0); if (!format) {
uint32_t arg_ptr = SHIM_GET_ARG_32(1); return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("_vscwprintf({:08X}({}), {:08X})", format_ptr, XELOGD("_vscwprintf({:08X}({}), {:08X})", format.guest_address(),
xe::to_utf8( xe::to_utf8(format.value()), arg_ptr.guest_address());
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
} }
if (format_ptr == 0) { ArrayArgList args(ctx, arg_ptr.guest_address());
SHIM_SET_RETURN_32(-1); WideCountFormatData data(
return; reinterpret_cast<const uint16_t*>(format.host_address()));
}
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr); int32_t count = format_core(ctx, data, args, true);
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); 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 // https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vswprintf_entry(PPCContext* ppc_context) { dword_result_t vswprintf_entry(dword_t buffer_ptr, lpu16string_t format,
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); lpdword_t arg_ptr, const ppc_context_t& ctx) {
uint32_t format_ptr = SHIM_GET_ARG_32(1); if (!buffer_ptr || !format) {
uint32_t arg_ptr = SHIM_GET_ARG_32(2); return -1;
}
if (cvars::log_string_format_kernel_calls) { if (cvars::log_string_format_kernel_calls) {
XELOGD("vswprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr, format_ptr, XELOGD("vswprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr.value(),
xe::to_utf8( format.guest_address(), xe::to_utf8(format.value()),
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))), arg_ptr.guest_address());
arg_ptr);
} }
if (buffer_ptr == 0 || format_ptr == 0) { ArrayArgList args(ctx, arg_ptr.guest_address());
SHIM_SET_RETURN_32(-1); WideStringFormatData data(
return; reinterpret_cast<const uint16_t*>(format.host_address()));
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr); char16_t* buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr); int32_t count = format_core(ctx, data, args, true);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
if (count <= 0) { if (count <= 0) {
buffer[0] = '\0'; buffer[0] = '\0';
} else { } 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'; buffer[count] = '\0';
} }
SHIM_SET_RETURN_32(count); return count;
} }
#if 1 DECLARE_XBOXKRNL_EXPORT1(vswprintf, kNone, kImplemented);
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
} // namespace xboxkrnl } // namespace xboxkrnl
} // namespace kernel } // namespace kernel
} // namespace xe } // 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