diff --git a/src/xenia/kernel/xam/apps/xam_app.cc b/src/xenia/kernel/xam/apps/xam_app.cc index 650360a47..79582f0fc 100644 --- a/src/xenia/kernel/xam/apps/xam_app.cc +++ b/src/xenia/kernel/xam/apps/xam_app.cc @@ -15,10 +15,14 @@ #include "xenia/kernel/xam/xam_content_device.h" #include "xenia/kernel/xenumerator.h" -// Notes: -// - Messages ids that start with 0x00021xxx are UI calls -// - Messages ids that start with 0x00023xxx are used for the user profile -// - Messages ids that start with 0x0002Bxxx are used for the Kinect +/* Notes: + - Messages ids that start with 0x00021xxx are UI calls + - Messages ids that start with 0x00023xxx are used for the user profile + - Messages ids that start with 0x0002Bxxx are used by the Kinect device + usually for camera related functions + - Messages ids that start with 0x0002Cxxx are used by the XamNuiIdentity + functions +*/ namespace xe { namespace kernel { diff --git a/src/xenia/kernel/xam/xam_module.cc b/src/xenia/kernel/xam/xam_module.cc index f02f5ee64..3487a9c27 100644 --- a/src/xenia/kernel/xam/xam_module.cc +++ b/src/xenia/kernel/xam/xam_module.cc @@ -20,6 +20,7 @@ namespace kernel { namespace xam { std::atomic xam_dialogs_shown_ = {0}; +std::atomic xam_nui_dialogs_shown_ = {0}; // FixMe(RodoMa92): Same hack as main_init_posix.cc:40 // Force initialization before constructor calling, mimicking @@ -33,6 +34,7 @@ static std::vector xam_exports(4096); bool xeXamIsUIActive() { return xam_dialogs_shown_ > 0; } +bool xeXamIsNuiUIActive() { return xam_nui_dialogs_shown_ > 0; } XamModule::XamModule(Emulator* emulator, KernelState* kernel_state) : KernelModule(kernel_state, "xe:\\xam.xex"), loader_data_() { diff --git a/src/xenia/kernel/xam/xam_module.h b/src/xenia/kernel/xam/xam_module.h index b331d8163..cfb3b764c 100644 --- a/src/xenia/kernel/xam/xam_module.h +++ b/src/xenia/kernel/xam/xam_module.h @@ -22,6 +22,7 @@ namespace kernel { namespace xam { bool xeXamIsUIActive(); +bool xeXamIsNuiUIActive(); static constexpr std::string_view kXamModuleLoaderDataFileName = "launch_data.bin"; diff --git a/src/xenia/kernel/xam/xam_nui.cc b/src/xenia/kernel/xam/xam_nui.cc index 512eb043b..8d3b79563 100644 --- a/src/xenia/kernel/xam/xam_nui.cc +++ b/src/xenia/kernel/xam/xam_nui.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2022 Ben Vanik. All rights reserved. * + * Copyright 2025 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,13 +19,35 @@ #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" +DEFINE_bool(Allow_nui_initialization, false, + "Enable NUI initialization\n" + " Only set true when testing kinect games. Certain games may\n" + " require avatar implementation.", + "Kernel"); + namespace xe { namespace kernel { namespace xam { extern std::atomic xam_dialogs_shown_; +extern std::atomic xam_nui_dialogs_shown_; + +// https://web.cs.ucdavis.edu/~okreylos/ResDev/Kinect/MainPage.html struct X_NUI_DEVICE_STATUS { + /* Notes: + - for one side func of XamNuiGetDeviceStatus + - if some data addressis less than zero then unk1 = it + - else another func is called and its return can set unk1 = c0051200 or + some value involving DetroitDeviceRequest + - next PsCamDeviceRequest is called and if its return is less than zero + then X_NUI_DEVICE_STATUS = return of PsCamDeviceRequest + - else it equals an unknown local_1c + - finally McaDeviceRequest is called and if its return is less than zero + then unk2 = return of McaDeviceRequest + - else it equals an unknown local_14 + - status can be set to X_NUI_DEVICE_STATUS[3] | 0x44 or | 0x40 + */ xe::be unk0; xe::be unk1; xe::be unk2; @@ -35,23 +57,300 @@ struct X_NUI_DEVICE_STATUS { }; static_assert(sizeof(X_NUI_DEVICE_STATUS) == 24, "Size matters"); -void XamNuiGetDeviceStatus_entry(pointer_t status_ptr) { +// Get +dword_result_t XamNuiGetDeviceStatus_entry( + pointer_t status_ptr) { + /* Notes: + - it does return a value that is not always used + - returns values are X_ERROR_SUCCESS, 0xC0050006, and others + - 1) On func start *status_ptr = 0, status_ptr->unk1 = 0, status_ptr->unk2 + = 0, and status_ptr->status = 0 + - 2) calls XamXStudioRequest(6,&var <- = 0); + - if return is greater than -1 && var & 0x80000000 != 0 then set + status_ptr->unk1 = 0xC000009D, status_ptr->unk2 = 0xC000009D, and + status_ptr->status = status_ptr[3] = 0x20 + - lots of branching functions after + */ + status_ptr.Zero(); - status_ptr->status = 0; // Not connected. + status_ptr->status = cvars::Allow_nui_initialization; + return cvars::Allow_nui_initialization ? X_ERROR_SUCCESS : 0xC0050006; } DECLARE_XAM_EXPORT1(XamNuiGetDeviceStatus, kNone, kStub); +dword_result_t XamUserNuiGetUserIndex_entry(unknown_t unk, lpdword_t index) { + return X_E_NO_SUCH_USER; +} +DECLARE_XAM_EXPORT1(XamUserNuiGetUserIndex, kNone, kStub); + +dword_result_t XamUserNuiGetUserIndexForSignin_entry(lpdword_t index) { + for (uint32_t i = 0; i < XUserMaxUserCount; i++) { + auto profile = kernel_state()->xam_state()->GetUserProfile(i); + if (profile) { + *index = i; + return X_E_SUCCESS; + } + } + + return X_E_ACCESS_DENIED; +} +DECLARE_XAM_EXPORT1(XamUserNuiGetUserIndexForSignin, kNone, kImplemented); + +dword_result_t XamUserNuiGetUserIndexForBind_entry(lpdword_t index) { + return X_E_FAIL; +} +DECLARE_XAM_EXPORT1(XamUserNuiGetUserIndexForBind, kNone, kStub); + +dword_result_t XamNuiGetDepthCalibration_entry(lpdword_t unk1) { + /* Notes: + - Possible returns X_STATUS_NO_SUCH_FILE, and 0x10000000 + */ + return X_STATUS_NO_SUCH_FILE; +} +DECLARE_XAM_EXPORT1(XamNuiGetDepthCalibration, kNone, kStub); + +// Skeleton +qword_result_t XamNuiSkeletonGetBestSkeletonIndex_entry(int_t unk) { + return 0xffffffffffffffff; +} +DECLARE_XAM_EXPORT1(XamNuiSkeletonGetBestSkeletonIndex, kNone, kStub); + +/* XamNuiCamera Notes + - most require message calls to xam in 0x0002Bxxx area +*/ + +dword_result_t XamNuiCameraTiltGetStatus_entry(lpvoid_t unk) { + /* Notes: + - Used by XamNuiCameraElevationGetAngle, and XamNuiCameraSetFlags + - if it returns anything greater than -1 then both above functions continue + - Both funcs send in a param of *unk = 0x50 bytes to copy + - unk2 + - Ghidra decompile fails + */ + return X_E_FAIL; +} +DECLARE_XAM_EXPORT1(XamNuiCameraTiltGetStatus, kNone, kStub); + +dword_result_t XamNuiCameraElevationGetAngle_entry(lpqword_t unk1, + lpdword_t unk2) { + /* Notes: + - Xam 12611 does not show what unk1 is used for (Ghidra) + */ + uint32_t tilt_status[] = {0x58745373, 0x50}; // (XtSs)? & bytes to copy + X_STATUS result = XamNuiCameraTiltGetStatus_entry(tilt_status); + if (XSUCCEEDED(result)) { + // operation here + // *unk1 = output1 + // *unk2 = output2 + } + return result; +} +DECLARE_XAM_EXPORT1(XamNuiCameraElevationGetAngle, kNone, kStub); + +dword_result_t XamNuiCameraGetTiltControllerType_entry() { + /* Notes: + - undefined unk[8] + - undefined8 local_28; + - undefined8 local_20; + - undefined8 local_18; + - undefined4 local_10; + - local_20 = 0; + - local_18 = 0; + - local_10 = 0; + - local_28 = 0xf030000000000; + - calls DetroitDeviceRequest(unk) -> result + - returns (ulonglong)(LZCOUNT(result) << 0x20) >> 0x25 + */ + return X_E_FAIL; +} +DECLARE_XAM_EXPORT1(XamNuiCameraGetTiltControllerType, kNone, kStub); + +dword_result_t XamNuiCameraSetFlags_entry(qword_t unk1, dword_t unk2) { + /* Notes: + - if XamNuiCameraGetTiltControllerType returns 1 then operation is done + - else 0xffffffff8007048f + */ + X_STATUS result = X_E_DEVICE_NOT_CONNECTED; + int Controller_Type = XamNuiCameraGetTiltControllerType_entry(); + + if (Controller_Type == 1) { + uint32_t tilt_status[] = {0x58745373, 0x50}; // (XtSs)? & bytes to copy + result = XamNuiCameraTiltGetStatus_entry(tilt_status); + if (XSUCCEEDED(result)) { + // op here + // result = + } + } + return result; +} +DECLARE_XAM_EXPORT1(XamNuiCameraSetFlags, kNone, kStub); + +dword_result_t XamIsNuiUIActive_entry() { return xeXamIsNuiUIActive(); } +DECLARE_XAM_EXPORT1(XamIsNuiUIActive, kNone, kImplemented); + +dword_result_t XamNuiIsDeviceReady_entry() { + /* device_state Notes: + - used with XNotifyBroadcast(kXNotificationSystemNUIHardwareStatusChanged, + device_state) + - known values: + - 0x0001 + - 0x0004 + - 0x0040 + */ + uint16_t device_state = cvars::Allow_nui_initialization ? 1 : 0; + return device_state >> 1 & 1; +} +DECLARE_XAM_EXPORT1(XamNuiIsDeviceReady, kNone, kImplemented); + +dword_result_t XamIsNuiAutomationEnabled_entry(unknown_t unk1, unknown_t unk2) { + /* Notes: + - XamIsNuiAutomationEnabled = XamIsNatalPlaybackEnabled + - Always returns X_E_SUCCESS? Maybe check later versions + - Recieves param but never interacts with them + - No Operations + */ + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT2(XamIsNuiAutomationEnabled, kNone, kStub, kHighFrequency); + +dword_result_t XamIsNatalPlaybackEnabled_entry(unknown_t unk1, unknown_t unk2) { + /* Notes: + - XamIsNuiAutomationEnabled = XamIsNatalPlaybackEnabled + - Always returns X_E_SUCCESS? Maybe check later versions + - Recieves param but never interacts with them + - No Operations + */ + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT2(XamIsNatalPlaybackEnabled, kNone, kStub, kHighFrequency); + +dword_result_t XamNuiIsChatMicEnabled_entry() { + /* Notes: + - calls a second function with a param of uint local_20 [4]; + - Second function calls ExGetXConfigSetting(7,9,local_30,0x1c,local_40); + - Result is sent to *local_20[0] = ^ + - Once sent back to XamNuiIsChatMicEnabled it looks for byte that + correlates to NUI mic setting + - return uVar2 = (~(ulonglong)local_20[0] << 0x20) >> 0x23 & 1; + - unless the second function returns something -1 or less then + XamNuiIsChatMicEnabled 1 + */ + return false; +} +DECLARE_XAM_EXPORT1(XamNuiIsChatMicEnabled, kNone, kImplemented); + +/* HUD Notes: + - XamNuiHudGetEngagedTrackingID, XamNuiHudIsEnabled, + XamNuiHudSetEngagedTrackingID, XamNuiHudInterpretFrame, and + XamNuiHudGetEngagedEnrollmentIndex all utilize the same data address + - engaged_tracking_id set second param of XamShowNuiTroubleshooterUI +*/ +uint32_t nui_unknown_1 = 0; +uint32_t engaged_tracking_id = 0; +char nui_unknown_2 = '\0'; + +dword_result_t XamNuiHudSetEngagedTrackingID_entry(dword_t id) { + if (!id) { + return X_STATUS_SUCCESS; + } + + if (nui_unknown_1 != 0) { + engaged_tracking_id = id; + return X_STATUS_SUCCESS; + } + + return X_E_FAIL; +} +DECLARE_XAM_EXPORT1(XamNuiHudSetEngagedTrackingID, kNone, kImplemented); + +qword_result_t XamNuiHudGetEngagedTrackingID_entry() { + if (nui_unknown_1 != 0) { + return engaged_tracking_id; + } + + return X_STATUS_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamNuiHudGetEngagedTrackingID, kNone, kImplemented); + +dword_result_t XamNuiHudIsEnabled_entry() { + /* Notes: + - checks if XamNuiIsDeviceReady false, if nui_unknown_1 exists, and + nui_unknown_2 is equal to null terminated string + - only returns true if one check fails and allows for other NUI functions + to progress + */ + bool result = XamNuiIsDeviceReady_entry(); + if (nui_unknown_1 != 0 && nui_unknown_2 != '\0' && result) { + return true; + } + return false; +} +DECLARE_XAM_EXPORT1(XamNuiHudIsEnabled, kNone, kImplemented); + +uint32_t XeXamNuiHudCheck(dword_t unk1) { + uint32_t check = XamNuiHudIsEnabled_entry(); + if (check == 0) { + return X_ERROR_ACCESS_DENIED; + } + + check = XamNuiHudSetEngagedTrackingID_entry(unk1); + if (check != 0) { + return X_ERROR_FUNCTION_FAILED; + } + return X_STATUS_SUCCESS; +} + +dword_result_t XamNuiHudGetInitializeFlags_entry() { + /* HUD_Flags Notes: + - set by 0x2B003 + - set to 0 by unnamed func alongside version_id + - known values: + - 0x40000000 + - 0x200 + */ + return 0; +} +DECLARE_XAM_EXPORT1(XamNuiHudGetInitializeFlags, kNone, kImplemented); + +void XamNuiHudGetVersions_entry(lpqword_t unk1, lpqword_t unk2) { + /* version_id Notes: + - set by 0x2B003 + - set to 0 by unnamed func alongside HUD_Flags + */ + if (unk1) { + *unk1 = 0; + } + if (unk2) { + *unk2 = 0; + } +} +DECLARE_XAM_EXPORT1(XamNuiHudGetVersions, kNone, kImplemented); + // UI dword_result_t XamShowNuiTroubleshooterUI_entry(unknown_t unk1, unknown_t unk2, dword_t flag) { /* Notes: - unk1 is 0xFF - possibly user index? - unk2 appear to always be zero. - - First calls XamPackageManagerGetExperienceMode and checks if the return - is less than zero - - If less than zero then returns error message below - - else it checks if flag = 0x800000 if it does then call - XamNuiGetDeviceStatus. if not return error + - calls XamPackageManagerGetExperienceMode(&var) with var = 1 + - If returns less than zero or (var & 1) == 0 then get error message: + - if XamPackageManagerGetExperienceMode = 0 then call XamShowMessageBoxUI + - if XamShowMessageBoxUI returns 0x3e5 then XamShowNuiTroubleshooterUI + returns 0 + - else XamShowNuiTroubleshooterUI returns 0x65b and call another func + - else: + - call XamNuiHudSetEngagedTrackingID(unk2) and doesn't care aboot return + and set var2 = 2 + - checks if (flag & 0x800000) == 0 + - if true call XamNuiGetDeviceStatus. + - if XamNuiGetDeviceStatus != 0 set var2 = 3 + - else var2 = 4 + - XamAppRequestLoadEx(var2); + - if return = 0 then XamShowNuiTroubleshooterUI returns 5 + - else set buffer[8] and call + XMsgSystemProcessCall(0xfe,0x21028,buffer,0xc); + - XamNuiNatalCameraUpdateComplete calls + XamShowNuiTroubleshooterUI(0xff,0,0) if param = -0x7ff8fffe */ if (cvars::headless) { @@ -75,7 +374,7 @@ dword_result_t XamShowNuiTroubleshooterUI_entry(unknown_t unk1, unknown_t unk2, } } - return 0; + return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT1(XamShowNuiTroubleshooterUI, kNone, kStub); @@ -88,6 +387,86 @@ dword_result_t XamShowNuiHardwareRequiredUI_entry(unknown_t unk1) { } DECLARE_XAM_EXPORT1(XamShowNuiHardwareRequiredUI, kNone, kImplemented); +dword_result_t XamShowNuiGuideUI_entry(unknown_t unk1, unknown_t unk2) { + /* Notes: + - calls an unnamed function that checks XamNuiHudIsEnabled and + XamNuiHudSetEngagedTrackingID + - if XamNuiHudIsEnabled returns false then fuctions fails return + X_ERROR_ACCESS_DENIED + - else calls XamNuiHudSetEngagedTrackingID and if returns less than 0 then + returns X_ERROR_FUNCTION_FAILED + - else return X_ERROR_SUCCESS + - if return offunc is X_ERROR_SUCCESS then call up ui screen + - else return value of func + */ + + // decompiler error stops me from knowing which param gets used here + uint32_t result = XeXamNuiHudCheck(0); + if (!result) { + // operations here + // XMsgSystemProcessCall(0xfe,0x21030, undefined local_30[8] ,0xc); + } + return result; +} +DECLARE_XAM_EXPORT1(XamShowNuiGuideUI, kNone, kStub); + +/* XamNuiIdentity Notes: + - most require message calls to xam in 0x0002Cxxx area +*/ +uint64_t NUI_Session_Id = 0; + +qword_result_t XamNuiIdentityGetSessionId_entry() { + if (NUI_Session_Id == 0) { + // xboxkrnl::XeCryptRandom_entry(NUI_Session_Id, 8); + NUI_Session_Id = 0xDEADF00DDEADF00D; + } + return NUI_Session_Id; +} +DECLARE_XAM_EXPORT1(XamNuiIdentityGetSessionId, kNone, kImplemented); + +dword_result_t XamNuiIdentityEnrollForSignIn_entry(dword_t unk1, qword_t unk2, + qword_t unk3, dword_t unk4) { + /* Notes: + - Decompiler issues so double check + */ + if (XamNuiHudIsEnabled_entry() == false) { + return X_E_FAIL; + } + // buffer [2] + // buffer[0] = unk + // var = unk4 + // return func(0xfe,0x2c010,buffer,0xc,unk3); + return X_E_FAIL; +} +DECLARE_XAM_EXPORT1(XamNuiIdentityEnrollForSignIn, kNone, kStub); + +dword_result_t XamNuiIdentityAbort_entry(dword_t unk) { + if (XamNuiHudIsEnabled_entry() == false) { + return X_E_FAIL; + } + // buffer [4] + // buffer[0] = unk + // return func(0xfe,0x2c00e,buffer,4,0) + return X_E_FAIL; +} +DECLARE_XAM_EXPORT1(XamNuiIdentityAbort, kNone, kStub); + +// Other +dword_result_t XamUserNuiEnableBiometric_entry(dword_t user_index, + int_t enable) { + return X_E_INVALIDARG; +} +DECLARE_XAM_EXPORT1(XamUserNuiEnableBiometric, kNone, kStub); + +void XamNuiPlayerEngagementUpdate_entry(qword_t unk1, unknown_t unk2, + lpunknown_t unk3) { + /* Notes: + - Only calls a second function with the params unk3, 0, and 0x1c in that + order + */ +} +DECLARE_XAM_EXPORT1(XamNuiPlayerEngagementUpdate, kNone, kStub); + } // namespace xam } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xam/xam_private.h b/src/xenia/kernel/xam/xam_private.h index 9122bc21c..2f477b288 100644 --- a/src/xenia/kernel/xam/xam_private.h +++ b/src/xenia/kernel/xam/xam_private.h @@ -19,6 +19,7 @@ namespace kernel { namespace xam { bool xeXamIsUIActive(); +bool xeXamIsNuiUIActive(); xe::cpu::Export* RegisterExport_xam(xe::cpu::Export* export_entry); diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index c73017283..7a206d56d 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -108,6 +108,7 @@ typedef uint32_t X_RESULT; #define X_ERROR_DEVICE_NOT_CONNECTED X_RESULT_FROM_WIN32(0x0000048FL) #define X_ERROR_NOT_FOUND X_RESULT_FROM_WIN32(0x00000490L) #define X_ERROR_CANCELLED X_RESULT_FROM_WIN32(0x000004C7L) +#define X_ERROR_ABORTED X_RESULT_FROM_WIN32(0x000004D3L) #define X_ERROR_NOT_LOGGED_ON X_RESULT_FROM_WIN32(0x000004DDL) #define X_ERROR_NO_SUCH_USER X_RESULT_FROM_WIN32(0x00000525L) #define X_ERROR_FUNCTION_FAILED X_RESULT_FROM_WIN32(0x0000065BL) @@ -273,7 +274,7 @@ constexpr uint8_t XUserIndexAny = 0xFF; // https://github.com/ThirteenAG/Ultimate-ASI-Loader/blob/master/source/xlive/xliveless.h typedef uint32_t XNotificationID; enum : XNotificationID { - /* Notes: + /* XNotification Notes: - Notification Ids are split into three Sections: Area, Version, and Message Id. - Each Area has the potential to hold 65535 unique notifications as it @@ -292,6 +293,12 @@ enum : XNotificationID { kXNotifyAll = 0x000000EF, // XNotification System + /* System Notes: + - for some functions if XamIsNuiUIActive returns false then + XNotifyBroadcast(kXNotificationSystemNUIPause, unk data) is called + - XNotifyBroadcast(kXNotificationSystemNUIHardwareStatusChanged, + device_state) + */ kXNotificationSystemUI = 0x00000009, kXNotificationSystemSignInChanged = 0x0000000A, kXNotificationSystemStorageDevicesChanged = 0x0000000B, @@ -310,7 +317,7 @@ enum : XNotificationID { kXNotificationSystemNUIBindingChanged = 0x0006001D, kXNotificationSystemAudioLatencyChanged = 0x0008001E, kXNotificationSystemNUIChatBindingChanged = 0x0008001F, - kXNotificationSystemInputActivityChanged = 0x00009020, + kXNotificationSystemInputActivityChanged = 0x00090020, // XNotification Live kXNotificationLiveConnectionChanged = 0x02000001, @@ -331,9 +338,18 @@ enum : XNotificationID { kXNotificationCustomGamercard = 0x06010004, // XNotification Dvd ? - kXNotificationDvdDriveUnknown = 0x80000003, + /* Dvd Drive? Notes: + - after XamLoaderGetMediaInfoEx(media_type?, title_id?, unk) is used for + some funcs the first param is used with + XNotifyBroadcast(kXNotificationDvdDriveTrayStateChanged, media_type?) + - after XamLoaderGetMediaInfoEx(media_type?, title_id?, unk) is used for + some funcs the third param is used with + XNotifyBroadcast(kXNotificationDvdDriveUnknown2, unk) + */ + kXNotificationDvdDriveUnknown1 = 0x80000003, kXNotificationDvdDriveUnknownDashContext = 0x8000000C, kXNotificationDvdDriveTrayStateChanged = 0x8000000D, + kXNotificationDvdDriveUnknown2 = 0x80010014, // XNotification XMP kXNotificationXmpStateChanged = 0x0A000001,