diff --git a/src/xenia/kernel/util/shim_utils.h b/src/xenia/kernel/util/shim_utils.h index 0fa254157..0bb1786ea 100644 --- a/src/xenia/kernel/util/shim_utils.h +++ b/src/xenia/kernel/util/shim_utils.h @@ -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(SHIM_MEM_ADDR(a)) -#define SHIM_MEM_16(a) xe::load_and_swap(SHIM_MEM_ADDR(a)) -#define SHIM_MEM_32(a) xe::load_and_swap(SHIM_MEM_ADDR(a)) -#define SHIM_MEM_64(a) xe::load_and_swap(SHIM_MEM_ADDR(a)) -#define SHIM_SET_MEM_8(a, v) xe::store_and_swap(SHIM_MEM_ADDR(a), v) -#define SHIM_SET_MEM_16(a, v) xe::store_and_swap(SHIM_MEM_ADDR(a), v) -#define SHIM_SET_MEM_32(a, v) xe::store_and_swap(SHIM_MEM_ADDR(a), v) -#define SHIM_SET_MEM_64(a, v) xe::store_and_swap(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( + 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( + 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( + 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( + 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(SHIM_MEM_ADDR(address)) - namespace shim { class Param { diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc index 4e5c9c8f3..39875ea3d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc @@ -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( + ppc_context->TranslateVirtual(pointer), (uint16_t)count); } else { - SHIM_SET_MEM_32(pointer, (uint32_t)count); + xe::store_and_swap( + 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.h b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.h new file mode 100644 index 000000000..e496fa161 --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.h @@ -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( + 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