Files
Xenia-Canary/src/xenia/kernel/xam/xam_info.cc

859 lines
31 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/clock.h"
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/string_util.h"
#include "xenia/config.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/title_id_utils.h"
#include "xenia/kernel/user_module.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_module.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_error.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_memory.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_modules.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
#include "xenia/kernel/xconfig.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/kernel/xthread.h"
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/window.h"
#include "xenia/ui/windowed_app_context.h"
#include "xenia/xbox.h"
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/fmt/include/fmt/xchar.h"
DEFINE_int32(avpack, 8,
"Video modes\n"
" 0 = PAL-60 Component (SD)\n"
" 1 = Unused\n"
" 2 = PAL-60 SCART\n"
" 3 = 480p Component (HD)\n"
" 4 = HDMI+A\n"
" 5 = PAL-60 Composite/S-Video\n"
" 6 = VGA\n"
" 7 = TV PAL-60\n"
" 8 = HDMI (default)",
"Video");
DEFINE_bool(staging_mode, 0,
"Enables preview mode in dashboards to render debug information.",
"Kernel");
namespace xe {
namespace kernel {
namespace xam {
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/km/wdm.h#L15539
typedef enum _MODE { KernelMode, UserMode, MaximumMode } MODE;
dword_result_t XamFeatureEnabled_entry(qword_t feature_bit) {
const std::bitset<36> feature(0x40ffffffff);
if (feature.test(feature_bit)) {
return 1;
}
return 0;
}
DECLARE_XAM_EXPORT1(XamFeatureEnabled, kNone, kImplemented);
dword_result_t XamGetStagingMode_entry() { return cvars::staging_mode; }
DECLARE_XAM_EXPORT1(XamGetStagingMode, kNone, kStub);
// Empty stub schema binary.
uint8_t schema_bin[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
0x00, 0x2C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
};
dword_result_t XamGetOnlineSchema_entry() {
static uint32_t schema_guest = 0;
if (!schema_guest) {
schema_guest =
kernel_state()->memory()->SystemHeapAlloc(8 + sizeof(schema_bin));
auto schema = kernel_state()->memory()->TranslateVirtual(schema_guest);
std::memcpy(schema + 8, schema_bin, sizeof(schema_bin));
xe::store_and_swap<uint32_t>(schema + 0, schema_guest + 8);
xe::store_and_swap<uint32_t>(schema + 4, sizeof(schema_bin));
}
// return pointer to the schema ptr/schema size struct
return schema_guest;
}
DECLARE_XAM_EXPORT1(XamGetOnlineSchema, kNone, kImplemented);
dword_result_t keXamBuildResourceLocator(uint64_t module,
const std::u16string& container,
const std::u16string& resource,
lpvoid_t buffer_ptr,
uint32_t buffer_count) {
std::u16string path;
if (!module) {
path = fmt::format(u"file://media:/{}.xzp#{}", container, resource);
XELOGD(
"XamBuildResourceLocator({0}) returning locator to local file {0}.xzp",
xe::to_utf8(container));
} else {
path = fmt::format(u"section://{:X},{}#{}", (uint32_t)module, container,
resource);
}
xe::string_util::copy_and_swap_truncating(buffer_ptr.as<char16_t*>(), path,
buffer_count);
return 0;
}
dword_result_t XamBuildResourceLocator_entry(qword_t module,
lpu16string_t container,
lpu16string_t resource,
lpvoid_t buffer_ptr,
dword_t buffer_count) {
return keXamBuildResourceLocator(module, container.value(), resource.value(),
buffer_ptr, buffer_count);
}
DECLARE_XAM_EXPORT1(XamBuildResourceLocator, kNone, kImplemented);
dword_result_t XamBuildGamercardResourceLocator_entry(lpu16string_t filename,
lpvoid_t buffer_ptr,
dword_t buffer_count) {
// On an actual xbox these funcs would return a locator to xam.xex resources,
// but for Xenia we can return a locator to the resources as local files. (big
// thanks to MS for letting XamBuildResourceLocator return local file
// locators!)
// If you're running an app that'll need them, make sure to extract xam.xex
// resources with xextool ("xextool -d . xam.xex") and add a .xzp extension.
const auto xam_module = kernel_state()->GetModule("xam", true);
return keXamBuildResourceLocator(xam_module ? xam_module->hmodule_ptr() : 0,
u"gamercrd", filename.value(), buffer_ptr,
buffer_count);
}
DECLARE_XAM_EXPORT1(XamBuildGamercardResourceLocator, kNone, kImplemented);
dword_result_t XamBuildSharedSystemResourceLocator_entry(lpu16string_t filename,
lpvoid_t buffer_ptr,
dword_t buffer_count) {
const auto xam_module = kernel_state()->GetModule("xam", true);
// see notes inside XamBuildGamercardResourceLocator above
return keXamBuildResourceLocator(xam_module ? xam_module->hmodule_ptr() : 0,
u"shrdres", filename.value(), buffer_ptr,
buffer_count);
}
DECLARE_XAM_EXPORT1(XamBuildSharedSystemResourceLocator, kNone, kImplemented);
dword_result_t XamBuildLegacySystemResourceLocator_entry(lpu16string_t filename,
lpvoid_t buffer_ptr,
dword_t buffer_count) {
return XamBuildSharedSystemResourceLocator_entry(filename, buffer_ptr,
buffer_count);
}
DECLARE_XAM_EXPORT1(XamBuildLegacySystemResourceLocator, kNone, kImplemented);
dword_result_t XamBuildXamResourceLocator_entry(lpu16string_t filename,
lpvoid_t buffer_ptr,
dword_t buffer_count) {
return keXamBuildResourceLocator(0, u"xam", filename.value(), buffer_ptr,
buffer_count);
}
DECLARE_XAM_EXPORT1(XamBuildXamResourceLocator, kNone, kImplemented);
dword_result_t XamGetCachedTitleName_entry(dword_t title_id,
dword_t title_name_address,
lpdword_t title_name_size_ptr) {
if (!title_name_address || !title_name_size_ptr) {
return X_ERROR_INVALID_PARAMETER;
}
assert_false(title_id != kernel_state()->title_id());
char16_t* title_name_ptr =
kernel_state()->memory()->TranslateVirtual<char16_t*>(title_name_address);
std::u16string title_name = xe::to_utf16(
kernel_state()->emulator()->game_info_database()->GetTitleName());
size_t title_name_size = string_util::size_in_bytes(title_name, true);
string_util::copy_and_swap_truncating(title_name_ptr, title_name,
title_name_size);
*title_name_size_ptr = static_cast<uint32_t>(title_name_size);
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamGetCachedTitleName, kNone, kImplemented);
dword_result_t XamGetSystemVersion_entry() {
// eh, just picking one. If we go too low we may break new games, but
// this value seems to be used for conditionally loading symbols and if
// we pretend to be old we have less to worry with implementing.
// 0x200A3200
// 0x20096B00
return 0;
}
DECLARE_XAM_EXPORT1(XamGetSystemVersion, kNone, kStub);
void XCustomRegisterDynamicActions_entry() {
// ???
}
DECLARE_XAM_EXPORT1(XCustomRegisterDynamicActions, kNone, kStub);
dword_result_t XGetAVPack_entry() {
// Value from
// https://github.com/Free60Project/libxenon/blob/920146f/libxenon/drivers/xenos/xenos_videomodes.h
// DWORD
// Not sure what the values are for this, but 6 is VGA.
// Other likely values are 3/4/8 for HDMI or something.
// Games seem to use this as a PAL check - if the result is not 3/4/6/8
// they explode with errors if not in PAL mode.
return (cvars::avpack);
}
DECLARE_XAM_EXPORT1(XGetAVPack, kNone, kStub);
dword_result_t XamGetCurrentTitleId_entry() {
return kernel_state()->emulator()->title_id();
}
DECLARE_XAM_EXPORT1(XamGetCurrentTitleId, kNone, kImplemented);
dword_result_t XamIsCurrentTitleDash_entry(const ppc_context_t& ctx) {
return ctx->kernel_state->title_id() == kDashboardID;
}
DECLARE_XAM_EXPORT1(XamIsCurrentTitleDash, kNone, kImplemented);
dword_result_t XamGetExecutionId_entry(lpdword_t info_ptr) {
auto module = kernel_state()->GetExecutableModule();
assert_not_null(module);
uint32_t guest_hdr_ptr;
X_STATUS result =
module->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &guest_hdr_ptr);
if (XFAILED(result)) {
return result;
}
*info_ptr = guest_hdr_ptr;
return X_STATUS_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamGetExecutionId, kNone, kImplemented);
dword_result_t XamLoaderSetLaunchData_entry(lpvoid_t data, dword_t size) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
loader_data.launch_data.resize(size);
std::memcpy(loader_data.launch_data.data(), data, size);
return 0;
}
DECLARE_XAM_EXPORT1(XamLoaderSetLaunchData, kNone, kSketchy);
dword_result_t XamLoaderGetLaunchDataSize_entry(lpdword_t size_ptr) {
if (!size_ptr) {
return X_ERROR_INVALID_PARAMETER;
}
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
if (loader_data.launch_data.empty()) {
*size_ptr = 0;
return X_ERROR_NOT_FOUND;
}
*size_ptr = uint32_t(xam->loader_data().launch_data.size());
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamLoaderGetLaunchDataSize, kNone, kSketchy);
dword_result_t XamLoaderGetLaunchData_entry(lpvoid_t buffer_ptr,
dword_t buffer_size) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
if (loader_data.launch_data.empty()) {
return X_ERROR_NOT_FOUND;
}
uint32_t copy_size =
std::min(uint32_t(loader_data.launch_data.size()), uint32_t(buffer_size));
std::memcpy(buffer_ptr, loader_data.launch_data.data(), copy_size);
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamLoaderGetLaunchData, kNone, kSketchy);
void XamLoaderLaunchTitle_entry(lpstring_t raw_name_ptr, dword_t flags) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
loader_data.launch_flags = flags;
std::string title;
std::string message;
// Translate the launch path to a full path.
if (raw_name_ptr && !raw_name_ptr.value().empty()) {
loader_data.launch_path = xe::path_to_utf8(raw_name_ptr.value());
xam->SaveLoaderData();
title = "Title was restarted";
message =
"Title closed with new launch data. \nPlease restart Xenia. "
"Game will be loaded automatically.";
} else {
title = "Title terminated";
message = "Game requested exit to dashboard.";
assert_always("Game requested exit to dashboard via XamLoaderLaunchTitle");
}
auto display_window = kernel_state()->emulator()->display_window();
auto imgui_drawer = kernel_state()->emulator()->imgui_drawer();
if (display_window && imgui_drawer) {
display_window->app_context().CallInUIThreadSynchronous(
[imgui_drawer, title, message]() {
auto dialog = xe::ui::ImGuiDialog::ShowMessageBox(
imgui_drawer, title.c_str(), message.c_str());
std::jthread([dialog]() {
while (!dialog->IsClosing()) {
std::this_thread::yield();
}
config::SaveConfig();
xe::FlushLog();
std::quick_exit(0);
}).detach();
});
}
// This function does not return.
kernel_state()->TerminateTitle();
}
DECLARE_XAM_EXPORT1(XamLoaderLaunchTitle, kNone, kSketchy);
void XamLoaderTerminateTitle_entry() {
std::string title = "Title terminated";
std::string message = "Game requested exit to dashboard.";
assert_always("Game requested exit to dashboard via XamLoaderTerminateTitle");
auto display_window = kernel_state()->emulator()->display_window();
auto imgui_drawer = kernel_state()->emulator()->imgui_drawer();
if (display_window && imgui_drawer) {
display_window->app_context().CallInUIThreadSynchronous(
[imgui_drawer, title, message]() {
auto dialog = xe::ui::ImGuiDialog::ShowMessageBox(
imgui_drawer, title.c_str(), message.c_str());
std::jthread([dialog]() {
while (!dialog->IsClosing()) {
std::this_thread::yield();
}
config::SaveConfig();
xe::FlushLog();
std::quick_exit(0);
}).detach();
});
}
// This function does not return.
kernel_state()->TerminateTitle();
}
DECLARE_XAM_EXPORT1(XamLoaderTerminateTitle, kNone, kSketchy);
uint32_t XamAllocImpl(uint32_t flags, uint32_t size,
xe::be<uint32_t>* out_ptr) {
if (flags & 0x00100000) { // HEAP_ZERO_memory used unless this flag
// do nothing!
// maybe we ought to fill it with nonzero garbage, but otherwise this is a
// flag we can safely ignore
}
// Allocate from the heap. Not sure why XAM does this specially, perhaps
// it keeps stuff in a separate heap?
// chrispy: there is a set of different heaps it uses, an array of them. the
// top 4 bits of the 32 bit flags seems to select the heap
uint32_t ptr = kernel_state()->memory()->SystemHeapAlloc(size);
*out_ptr = ptr;
return X_ERROR_SUCCESS;
}
dword_result_t XamAlloc_entry(dword_t flags, dword_t size, lpdword_t out_ptr) {
return XamAllocImpl(flags, size, out_ptr);
}
DECLARE_XAM_EXPORT1(XamAlloc, kMemory, kImplemented);
static const unsigned short XamPhysicalProtTable[4] = {
X_PAGE_READONLY, X_PAGE_READWRITE | X_PAGE_NOCACHE, X_PAGE_READWRITE,
X_PAGE_WRITECOMBINE | X_PAGE_READWRITE};
dword_result_t XamAllocEx_entry(dword_t phys_flags, dword_t flags, dword_t size,
lpdword_t out_ptr, const ppc_context_t& ctx) {
if ((flags & 0x40000000) == 0) {
return XamAllocImpl(flags, size, out_ptr);
}
uint32_t flags_remapped = phys_flags;
if ((phys_flags & 0xF000000) == 0) {
// setting default alignment
flags_remapped = 0xC000000 | phys_flags & 0xF0FFFFFF;
}
uint32_t result = xboxkrnl::xeMmAllocatePhysicalMemoryEx(
2, size, XamPhysicalProtTable[(flags_remapped >> 28) & 0b11], 0,
0xFFFFFFFF, 1 << ((flags_remapped >> 24) & 0xF));
if (result && (flags_remapped & 0x40000000) != 0) {
memset(ctx->TranslateVirtual<uint8_t*>(result), 0, size);
}
*out_ptr = result;
return result ? 0 : 0x8007000E;
}
DECLARE_XAM_EXPORT1(XamAllocEx, kMemory, kImplemented);
dword_result_t XamFree_entry(lpdword_t ptr) {
kernel_state()->memory()->SystemHeapFree(ptr.guest_address());
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamFree, kMemory, kImplemented);
dword_result_t XamQueryLiveHiveW_entry(lpu16string_t name, lpvoid_t out_buf,
dword_t out_size,
dword_t type /* guess */) {
return X_STATUS_INVALID_PARAMETER_1;
}
DECLARE_XAM_EXPORT1(XamQueryLiveHiveW, kNone, kStub);
// http://www.noxa.org/blog/2011/02/28/building-an-xbox-360-emulator-part-3-feasibilityos/
// http://www.noxa.org/blog/2011/08/13/building-an-xbox-360-emulator-part-5-xex-files/
dword_result_t RtlSleep_entry(dword_t dwMilliseconds, dword_t bAlertable) {
uint64_t delay{};
// Convert the delay time to 100-nanosecond intervals
delay = dwMilliseconds == -1 ? LLONG_MAX
: static_cast<int64_t>(-10000) * dwMilliseconds;
X_STATUS result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
&delay, nullptr);
// If the delay was interrupted by an APC, keep delaying the thread
while (bAlertable && result == X_STATUS_ALERTED) {
result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
&delay, nullptr);
}
return result == X_STATUS_SUCCESS ? X_STATUS_SUCCESS : X_STATUS_USER_APC;
}
DECLARE_XAM_EXPORT1(RtlSleep, kNone, kImplemented);
dword_result_t SleepEx_entry(dword_t dwMilliseconds, dword_t bAlertable) {
return RtlSleep_entry(dwMilliseconds, bAlertable);
}
DECLARE_XAM_EXPORT1(SleepEx, kNone, kImplemented);
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
void Sleep_entry(dword_t dwMilliseconds) {
RtlSleep_entry(dwMilliseconds, false);
}
DECLARE_XAM_EXPORT1(Sleep, kNone, kImplemented);
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount
dword_result_t GetTickCount_entry() { return Clock::QueryGuestUptimeMillis(); }
DECLARE_XAM_EXPORT1(GetTickCount, kNone, kImplemented);
dword_result_t RtlSetLastNTError_entry(dword_t error_code) {
const uint32_t result =
xe::kernel::xboxkrnl::xeRtlNtStatusToDosError(error_code);
XThread::SetLastError(result);
return result;
}
DECLARE_XAM_EXPORT1(RtlSetLastNTError, kNone, kImplemented);
dword_result_t RtlGetLastError_entry() { return XThread::GetLastError(); }
DECLARE_XAM_EXPORT1(RtlGetLastError, kNone, kImplemented);
dword_result_t XGetOverlappedExtendedError_entry(
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
if (!overlapped_ptr) {
return XThread::GetLastError();
}
if (overlapped_ptr->result == X_ERROR_IO_PENDING) {
return X_ERROR_IO_INCOMPLETE;
}
return static_cast<uint32_t>(overlapped_ptr->extended_error);
}
DECLARE_XAM_EXPORT1(XGetOverlappedExtendedError, kNone, kImplemented);
dword_result_t GetLastError_entry() { return RtlGetLastError_entry(); }
DECLARE_XAM_EXPORT1(GetLastError, kNone, kImplemented);
dword_result_t GetModuleHandleA_entry(lpstring_t module_name) {
xe::be<uint32_t> module_ptr = 0;
const X_STATUS error_code = xe::kernel::xboxkrnl::XexGetModuleHandle(
module_name.value(), &module_ptr);
if (XFAILED(error_code)) {
RtlSetLastNTError_entry(error_code);
return NULL;
}
return (uint32_t)module_ptr;
}
DECLARE_XAM_EXPORT1(GetModuleHandleA, kNone, kImplemented);
dword_result_t XapipCreateThread_entry(
lpdword_t thread_attributes, dword_t stack_size, lpvoid_t start_address,
lpvoid_t parameter, dword_t creation_flags, dword_t thread_processor,
lpdword_t thread_id) {
uint32_t flags = (creation_flags >> 2) & 1;
if (thread_processor != -1) {
flags |= 1 << thread_processor << 24;
}
xe::be<uint32_t> result = 0;
const X_STATUS error_code = xe::kernel::xboxkrnl::ExCreateThread(
&result, stack_size, thread_id, start_address, parameter, 0, flags);
if (XFAILED(error_code)) {
RtlSetLastNTError_entry(error_code);
return NULL;
}
return (uint32_t)result;
}
DECLARE_XAM_EXPORT1(XapipCreateThread, kNone, kImplemented);
dword_result_t CreateThread_entry(lpdword_t lpThreadAttributes,
dword_t dwStackSize, lpvoid_t lpStartAddress,
lpvoid_t lpParameter, dword_t dwCreationFlags,
lpdword_t lpThreadId) {
return XapipCreateThread_entry(lpThreadAttributes, dwStackSize,
lpStartAddress, lpParameter, dwCreationFlags,
-1, lpThreadId);
}
DECLARE_XAM_EXPORT1(CreateThread, kNone, kImplemented);
dword_result_t CloseHandle_entry(dword_t hObject) {
const X_STATUS error_code = xe::kernel::xboxkrnl::NtClose(hObject);
if (XFAILED(error_code)) {
RtlSetLastNTError_entry(error_code);
return false;
}
return true;
}
DECLARE_XAM_EXPORT1(CloseHandle, kNone, kImplemented);
dword_result_t ResumeThread_entry(dword_t hThread) {
uint32_t suspend_count;
const X_STATUS error_code =
xe::kernel::xboxkrnl::NtResumeThread(hThread, &suspend_count);
if (XFAILED(error_code)) {
RtlSetLastNTError_entry(error_code);
return -1;
}
return suspend_count;
}
DECLARE_XAM_EXPORT1(ResumeThread, kNone, kImplemented);
void ExitThread_entry(dword_t exit_code) {
xe::kernel::xboxkrnl::ExTerminateThread(exit_code);
}
DECLARE_XAM_EXPORT1(ExitThread, kNone, kImplemented);
dword_result_t GetCurrentThreadId_entry() {
return XThread::GetCurrentThread()->GetCurrentThreadId();
}
DECLARE_XAM_EXPORT1(GetCurrentThreadId, kNone, kImplemented);
qword_result_t XapiFormatTimeOut_entry(lpqword_t result,
dword_t dwMilliseconds) {
if (dwMilliseconds == -1) {
return 0;
}
*result = static_cast<int64_t>(-10000) * dwMilliseconds;
return result.host_address();
}
DECLARE_XAM_EXPORT1(XapiFormatTimeOut, kNone, kImplemented);
dword_result_t WaitForSingleObjectEx_entry(dword_t hHandle,
dword_t dwMilliseconds,
dword_t bAlertable) {
// TODO(Gliniak): Figure it out to be less janky.
uint64_t timeout;
uint64_t* timeout_ptr = reinterpret_cast<uint64_t*>(
static_cast<uint64_t>(XapiFormatTimeOut_entry(&timeout, dwMilliseconds)));
X_STATUS result =
xboxkrnl::NtWaitForSingleObjectEx(hHandle, 1, bAlertable, timeout_ptr);
while (bAlertable && result == X_STATUS_ALERTED) {
result =
xboxkrnl::NtWaitForSingleObjectEx(hHandle, 1, bAlertable, timeout_ptr);
}
RtlSetLastNTError_entry(result);
result = -1;
return result;
}
DECLARE_XAM_EXPORT1(WaitForSingleObjectEx, kNone, kImplemented);
dword_result_t WaitForSingleObject_entry(dword_t hHandle,
dword_t dwMilliseconds) {
return WaitForSingleObjectEx_entry(hHandle, dwMilliseconds, 0);
}
DECLARE_XAM_EXPORT1(WaitForSingleObject, kNone, kImplemented);
dword_result_t lstrlenW_entry(lpu16string_t string) {
// wcslen?
if (string) {
return (uint32_t)string.value().length();
}
return NULL;
}
DECLARE_XAM_EXPORT1(lstrlenW, kNone, kImplemented);
dword_result_t XGetAudioFlags_entry() {
if (cvars::avpack == 2) {
return 2;
}
const auto audio_flags = kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_AUDIO_FLAGS);
return audio_flags ? audio_flags : 0x10000 | 0x1;
}
DECLARE_XAM_EXPORT1(XGetAudioFlags, kNone, kImplemented);
/*
todo: this table should instead be pointed to by a member of kernel
state and initialized along with the process
*/
static int32_t XamRtlRandomTable[128] = {
1284227242, 1275210071, 573735546, 790525478, 2139871995, 1547161642,
179605362, 789336058, 688789844, 1801674531, 1563985344, 1957994488,
1364589140, 1645522239, 287218729, 606747145, 1972579041, 1085031214,
1425521274, 1482476501, 1844823847, 57989841, 1897051121, 1935655697,
1078145449, 1960408961, 1682526488, 842925246, 1500820517, 1214440339,
1647877149, 682003330, 261478967, 2052111302, 162531612, 583907252,
1336601894, 1715567821, 413027322, 484763869, 1383384898, 1004336348,
764733703, 854245398, 651377827, 1614895754, 838170752, 1757981266,
602609370, 1644491937, 926492609, 220523388, 115176313, 725345543,
261903793, 746137067, 920026266, 1123561945, 1580818891, 1708537768,
616249376, 1292428093, 562591055, 343818398, 1788223648, 1659004503,
2077806209, 299502267, 1604221776, 602162358, 630328440, 1606980848,
1580436667, 1078081533, 492894223, 839522115, 1979792683, 117609710,
1767777339, 1454471165, 1965331169, 1844237615, 308236825, 329068152,
412668190, 796845957, 1303643608, 436374069, 1677128483, 527237240,
813497703, 1060284298, 1770027372, 1177238915, 884357618, 1409082233,
1958367476, 448539723, 1592454029, 861567501, 963894560, 73586283,
362288127, 507921405, 113007714, 823518204, 152049171, 1202660629,
1326574676, 2025429265, 1035525444, 515967899, 1532298954, 2000478354,
1450960922, 1417001333, 2049760794, 1229272821, 879983540, 1993962763,
706699826, 776561741, 2111687655, 1343024091, 1637723038, 1220945662,
484061587, 1390067357};
/*
Follows xam exactly, the updates to the random table are probably racy.
*/
dword_result_t RtlRandom_entry(lpdword_t seed_out) {
int32_t table_seed_new = (0x7FFFFFED * *seed_out + 0x7FFFFFC3) % 0x7FFFFFFF;
*seed_out = table_seed_new;
uint32_t param_seed_new =
(0x7FFFFFED * table_seed_new + 0x7FFFFFC3) % 0x7FFFFFFFu;
*seed_out = param_seed_new;
int32_t* update_table_position = &XamRtlRandomTable[param_seed_new & 0x7F];
int32_t result = *update_table_position;
*update_table_position = table_seed_new;
return result;
}
DECLARE_XAM_EXPORT1(RtlRandom, kNone, kImplemented);
dword_result_t Refresh_entry() { return X_ERROR_SUCCESS; }
DECLARE_XAM_EXPORT1(Refresh, kNone, kStub);
dword_result_t XamIsSystemExperienceTitleId_entry(dword_t title_id) {
return IsSystemExperienceTitle(title_id);
}
DECLARE_XAM_EXPORT1(XamIsSystemExperienceTitleId, kNone, kImplemented);
dword_result_t XamIsSystemTitleId_entry(dword_t title_id) {
return IsSystemTitle(title_id);
}
DECLARE_XAM_EXPORT1(XamIsSystemTitleId, kNone, kImplemented);
dword_result_t XamIsXbox1TitleId_entry(dword_t title_id) {
return IsOriginalXboxTitle(title_id);
}
DECLARE_XAM_EXPORT1(XamIsXbox1TitleId, kNone, kImplemented);
dword_result_t XamIsChildAccountSignedIn_entry() {
for (uint32_t i = 0; i < XUserMaxUserCount; i++) {
const auto& profile = kernel_state()->xam_state()->GetUserProfile(i);
if (profile && profile->IsParentalControlled()) {
return true;
}
}
return false;
}
DECLARE_XAM_EXPORT1(XamIsChildAccountSignedIn, kNone, kImplemented);
void XamSetActiveDashAppInfo_entry(pointer_t<X_DASH_APP_INFO> dash_app) {
if (!dash_app) {
kernel_state()->xam_state()->dash_app_info_ = {};
return;
}
std::memcpy(&kernel_state()->xam_state()->dash_app_info_, dash_app,
sizeof(X_DASH_APP_INFO));
}
DECLARE_XAM_EXPORT1(XamSetActiveDashAppInfo, kNone, kImplemented);
void XamGetActiveDashAppInfo_entry(pointer_t<X_DASH_APP_INFO> dash_app) {
if (!dash_app) {
return;
}
std::memcpy(dash_app, &kernel_state()->xam_state()->dash_app_info_,
sizeof(X_DASH_APP_INFO));
}
DECLARE_XAM_EXPORT1(XamGetActiveDashAppInfo, kNone, kImplemented);
dword_result_t XamGetDashBackstackData_entry(
pointer_t<X_DASH_BACKSTACK_DATA> backstack_data, lpdword_t count) {
auto xam_state_ = kernel_state()->xam_state();
uint32_t total = xam_state_->dash_backstack_nodes_count_;
if (total) {
if (*count <= total) {
total = *count;
}
std::memcpy(backstack_data, &xam_state_->dash_backstack_data_,
sizeof(X_DASH_BACKSTACK_DATA) * total);
xam_state_->dash_backstack_nodes_count_ = 0;
}
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamGetDashBackstackData, kNone, kImplemented);
void XampWebInstrumentationSetProfileCounts_entry(dword_t live_profiles,
dword_t local_profiles,
dword_t adult_profiles,
dword_t child_profiles) {}
DECLARE_XAM_EXPORT1(XampWebInstrumentationSetProfileCounts, kUserProfiles,
kStub);
dword_result_t XamDoesOmniNeedConfiguration_entry() { return 0; }
DECLARE_XAM_EXPORT1(XamDoesOmniNeedConfiguration, kNone, kStub);
dword_result_t XamFirstRunExperienceShouldRun_entry() { return 0; }
DECLARE_XAM_EXPORT1(XamFirstRunExperienceShouldRun, kNone, kStub);
dword_result_t QueryPerformanceFrequency_entry(lpqword_t query) {
// Copied from KeQueryPerformanceFrequency
uint64_t result = Clock::guest_tick_frequency();
*query = static_cast<uint32_t>(result);
return 1;
}
DECLARE_XAM_EXPORT1(QueryPerformanceFrequency, kNone, kImplemented);
void GetSystemTimeAsFileTime_entry(lpqword_t time_ptr,
const ppc_context_t& ctx) {
if (time_ptr) {
// Copied from KeQuerySystemTime
// update the timestamp bundle to the time we queried.
// this is a race, but i don't of any sw that requires it, it just seems
// like we ought to keep it consistent with ketimestampbundle in case
// something uses this function, but also reads it directly
uint32_t ts_bundle = ctx->kernel_state->GetKeTimestampBundle();
uint64_t time = Clock::QueryGuestSystemTime();
// todo: cmpxchg?
ctx->TranslateVirtual<X_TIME_STAMP_BUNDLE*>(ts_bundle)->system_time =
xe::byte_swap(time);
*time_ptr = time;
}
}
DECLARE_XAM_EXPORT1(GetSystemTimeAsFileTime, kNone, kImplemented);
dword_result_t XamIsIptvEnabled_entry() {
const bool iptv_enabled =
kernel_state()->xconfig()->ReadSetting<uint32_t>(
X_CONFIG_CATEGORY::XCONFIG_USER_CATEGORY, XCONFIG_USER_RETAIL_FLAGS) &
X_RETAIL_FLAGS::IPTVEnabled;
return !iptv_enabled ? X_E_FAIL : X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamIsIptvEnabled, kNone, kImplemented);
dword_result_t XamIptvGetServiceName_entry(lpdword_t service_name_ptr) {
auto address = kernel_state()->xam_state()->GetIptvNameAddress();
auto buffer = kernel_state()->memory()->TranslateVirtual(address);
char16_t* data_ptr = reinterpret_cast<char16_t*>(buffer);
kernel_state()->xconfig()->ReadSetting(
X_CONFIG_CATEGORY::XCONFIG_IPTV_CATEGORY,
XCONFIG_IPTV_SERVICE_PROVIDER_NAME, data_ptr);
if (*data_ptr == u'\0') {
xe::string_util::copy_and_swap_truncating(data_ptr, u"Xenia TV", 9);
}
*service_name_ptr = address;
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamIptvGetServiceName, kNone, kImplemented);
dword_result_t XamGetDvrStorage_entry(lpdword_t dvr_storage,
lpdword_t used_dvr_storage,
lpdword_t hdd_unused_space) {
*dvr_storage = 0;
*used_dvr_storage = 0;
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamGetDvrStorage, kNone, kStub);
dword_result_t XamSetDvrStorage_entry(
dword_t dvr_storage_size, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamSetDvrStorage, kNone, kStub);
dword_result_t XamLookupCommonStringByIndex_entry(dword_t string_index) {
return 0;
}
DECLARE_XAM_EXPORT1(XamLookupCommonStringByIndex, kNone, kImplemented);
} // namespace xam
} // namespace kernel
} // namespace xe
DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Info);