netcode-revival: answer Xam user shims for every slot + live flag = user 0
Some checks are pending
Lint & Format Check / Format Check (push) Waiting to run
Some checks are pending
Lint & Format Check / Format Check (push) Waiting to run
The game's 'Party Play' gate treats its internal live flag (0x83028851) as a USER INDEX, then checks XamUserGetSigninState(idx) and XamUserGetSigninInfo (idx). rexglue only answered user 0, so the predicate failed and the option stayed greyed out (no rexglue API is even reached on click). Now: - XamUserGetSigninState/GetSigninInfo/GetXUID/GetName/GetGamerTag/ GetMembershipTier accept any slot < 4 and return the single profile (signed-in, live, Gold). - ForceLiveFlag writes 0 (the profile's real slot) instead of 1, since the flag is a user index; slot 0 is safe for every index-into-live-array read.
This commit is contained in:
parent
73d8f36af2
commit
e09f224a6b
2 changed files with 38 additions and 32 deletions
|
|
@ -54,7 +54,6 @@ i32 XamUserGetXUID_entry(u32 user_index, u32 type_mask, mapped_u64 xuid_ptr) {
|
||||||
uint32_t result = X_E_NO_SUCH_USER;
|
uint32_t result = X_E_NO_SUCH_USER;
|
||||||
uint64_t xuid = 0;
|
uint64_t xuid = 0;
|
||||||
if (user_index < 4) {
|
if (user_index < 4) {
|
||||||
if (user_index == 0) {
|
|
||||||
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
||||||
if (user_profile->is_signed_in()) {
|
if (user_profile->is_signed_in()) {
|
||||||
auto type = user_profile->type() & type_mask;
|
auto type = user_profile->type() & type_mask;
|
||||||
|
|
@ -68,7 +67,6 @@ i32 XamUserGetXUID_entry(u32 user_index, u32 type_mask, mapped_u64 xuid_ptr) {
|
||||||
result = X_E_SUCCESS;
|
result = X_E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
result = X_E_INVALIDARG;
|
result = X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
@ -78,12 +76,13 @@ i32 XamUserGetXUID_entry(u32 user_index, u32 type_mask, mapped_u64 xuid_ptr) {
|
||||||
|
|
||||||
u32 XamUserGetSigninState_entry(u32 user_index) {
|
u32 XamUserGetSigninState_entry(u32 user_index) {
|
||||||
uint32_t signin_state = 0;
|
uint32_t signin_state = 0;
|
||||||
|
// netcode-revival: the party flow reads signin state for the "live user
|
||||||
|
// index" (the game's live flag is a user index, not a boolean). We model
|
||||||
|
// one profile, so answer every slot with the same signed-in state.
|
||||||
if (user_index < 4) {
|
if (user_index < 4) {
|
||||||
if (user_index == 0) {
|
|
||||||
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
||||||
signin_state = user_profile->signin_state();
|
signin_state = user_profile->signin_state();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (REXCVAR_GET(xnet_trace)) {
|
if (REXCVAR_GET(xnet_trace)) {
|
||||||
// Rate-limited: the frontend polls this frequently.
|
// Rate-limited: the frontend polls this frequently.
|
||||||
static double last_log = 0.0;
|
static double last_log = 0.0;
|
||||||
|
|
@ -114,7 +113,9 @@ i32 XamUserGetSigninInfo_entry(u32 user_index, u32 flags, ppc_ptr_t<X_USER_SIGNI
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(info, 0, sizeof(X_USER_SIGNIN_INFO));
|
std::memset(info, 0, sizeof(X_USER_SIGNIN_INFO));
|
||||||
if (user_index) {
|
// netcode-revival: answer every slot with the one profile (the game's
|
||||||
|
// "live user index" is not necessarily 0).
|
||||||
|
if (user_index >= 4) {
|
||||||
return X_E_NO_SUCH_USER;
|
return X_E_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,10 +139,6 @@ u32 XamUserGetName_entry(u32 user_index, mapped_string buffer, u32 buffer_len) {
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index) {
|
|
||||||
return X_E_NO_SUCH_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
||||||
if (!user_profile->is_signed_in()) {
|
if (!user_profile->is_signed_in()) {
|
||||||
if (buffer && buffer_len) {
|
if (buffer && buffer_len) {
|
||||||
|
|
@ -160,10 +157,6 @@ u32 XamUserGetGamerTag_entry(u32 user_index, mapped_wstring buffer, u32 buffer_l
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index) {
|
|
||||||
return X_E_NO_SUCH_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
const auto& user_profile = REX_KERNEL_STATE()->user_profile();
|
||||||
if (!user_profile->is_signed_in()) {
|
if (!user_profile->is_signed_in()) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
|
|
@ -506,9 +499,6 @@ u32 XamUserGetMembershipTier_entry(u32 user_index) {
|
||||||
if (user_index >= 4) {
|
if (user_index >= 4) {
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
if (user_index) {
|
|
||||||
return X_ERROR_NO_SUCH_USER;
|
|
||||||
}
|
|
||||||
if (!REX_KERNEL_STATE()->user_profile()->is_live_signed_in()) {
|
if (!REX_KERNEL_STATE()->user_profile()->is_live_signed_in()) {
|
||||||
return X_ERROR_NOT_LOGGED_ON;
|
return X_ERROR_NOT_LOGGED_ON;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,9 +162,25 @@ void XNetTransport::ForceLiveFlag() {
|
||||||
// 0x83030000 (recompiler's -2097348608) then lbz at -30639 =>
|
// 0x83030000 (recompiler's -2097348608) then lbz at -30639 =>
|
||||||
// 0x83028851. (Fragile: tied to this title's build; revival only.)
|
// 0x83028851. (Fragile: tied to this title's build; revival only.)
|
||||||
uint8_t* flag = REX_KERNEL_MEMORY()->TranslateVirtual<uint8_t*>(0x83028851);
|
uint8_t* flag = REX_KERNEL_MEMORY()->TranslateVirtual<uint8_t*>(0x83028851);
|
||||||
if (flag != nullptr && *flag == 0) {
|
// Rate-limited status so we can SEE whether the address maps and its value.
|
||||||
REXLOG_INFO("xnet trace: forcing game live flag 0x83028851 = 1");
|
static double last_log = 0.0;
|
||||||
*flag = 1;
|
const double now =
|
||||||
|
double(std::chrono::steady_clock::now().time_since_epoch().count()) / 1e9;
|
||||||
|
const bool rate = now - last_log >= 2.0;
|
||||||
|
if (rate) {
|
||||||
|
last_log = now;
|
||||||
|
REXLOG_INFO("xnet trace: live flag 0x83028851 {} value={}",
|
||||||
|
flag != nullptr ? "mapped" : "NOT MAPPED",
|
||||||
|
flag != nullptr ? int(*flag) : -1);
|
||||||
|
}
|
||||||
|
// The flag is the LIVE USER INDEX (not a boolean). The profile lives in
|
||||||
|
// slot 0, so force 0 (any slot answers after the shim fix, but 0 is the
|
||||||
|
// safest for index-into-live-array readers).
|
||||||
|
if (flag != nullptr && *flag != 0) {
|
||||||
|
if (!rate) {
|
||||||
|
REXLOG_INFO("xnet trace: forcing game live flag 0x83028851 = 0");
|
||||||
|
}
|
||||||
|
*flag = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue