[XAM] Add XamShowCreateProfileUI & XamProfileCreate impl.

Seems to kinda work, xshell uses XamProfileCreate and says that it's successful at least.
Dash uses the ShowUI function, but doesn't seem to login to the created profile correctly for some reason... restarting with the toml configured for the new profile does seem to work though.
This commit is contained in:
emoose 2020-01-06 07:37:16 +00:00 committed by illusion
parent a2037b15bc
commit dcfc9321f8
4 changed files with 125 additions and 31 deletions

View File

@ -286,6 +286,53 @@ std::wstring UserProfile::ExtractProfile(const std::wstring& path) {
return package + L"\\"; return package + L"\\";
} }
bool UserProfile::Create(X_XAMACCOUNTINFO* account, bool generate_gamertag) {
// TODO: check for any existing profile with the same account->gamertag!
static int num_xuids = 0; // TODO: we really should save this between runs
// Create some unique IDs for the profile
xuid_offline_ = 0xE0000000BEEFCAFE + num_xuids;
account->xuid_online = 0x09000000BEEFCAFE + num_xuids;
if (generate_gamertag) {
swprintf_s(account->gamertag, L"XeniaUser%d", num_xuids);
}
num_xuids++;
profile_path_ = path(xuid_offline_);
profile_path_ = ExtractProfile(profile_path_);
memcpy(&account_, account, sizeof(X_XAMACCOUNTINFO));
if (!filesystem::PathExists(profile_path_)) {
// Profile path doesn't exist - create new profile!
if (!filesystem::CreateFolder(profile_path_)) {
XELOGE("UserProfile::Create: Failed to create profile for '%S' at %S!",
account->gamertag, path().c_str());
return false;
} else {
// Write out an account file
filesystem::CreateFile(
path() + L"Account"); // MappedMemory needs an existing file...
auto mmap_ = MappedMemory::Open(path() + L"Account",
MappedMemory::Mode::kReadWrite, 0,
sizeof(X_XAMACCOUNTINFO) + 0x18);
if (!mmap_) {
XELOGE("UserProfile::Create: Failed to create new Account at %S!",
path().c_str());
return false;
} else {
XELOGI("Writing Account file for '%S' to path %SAccount",
account->gamertag, path().c_str());
EncryptAccountFile(account, mmap_->data(), false);
mmap_->Close();
}
// Dash GPD will be updated below, after default settings are added
}
}
return true;
}
bool UserProfile::Login(uint64_t offline_xuid) { bool UserProfile::Login(uint64_t offline_xuid) {
xuid_offline_ = offline_xuid; xuid_offline_ = offline_xuid;
auto profile_path = path(xuid_offline_); auto profile_path = path(xuid_offline_);
@ -308,12 +355,9 @@ bool UserProfile::Login(uint64_t offline_xuid) {
"using temp. profile"); "using temp. profile");
memset(&account_, 0, sizeof(X_XAMACCOUNTINFO)); memset(&account_, 0, sizeof(X_XAMACCOUNTINFO));
// Create some unique IDs for the profile if (!Create(&account_, true)) {
static int num_xuids = 0; return false;
xuid_offline_ = 0xE0000000BEEFCAFE + num_xuids; }
account_.xuid_online = 0x09000000BEEFCAFE + num_xuids;
swprintf_s(account_.gamertag, L"XeniaUser%d", num_xuids);
num_xuids++;
profile_path = path(xuid_offline_); profile_path = path(xuid_offline_);
} }
@ -325,31 +369,7 @@ bool UserProfile::Login(uint64_t offline_xuid) {
return false; return false;
} }
if (!filesystem::PathExists(profile_path_)) { if (filesystem::PathExists(profile_path_)) {
// Profile path doesn't exist - create new profile!
if (!filesystem::CreateFolder(profile_path_)) {
XELOGE("UserProfile::Login: Failed to create profile for '%S' at %S!",
account_.gamertag, path().c_str());
} else {
// Write out an account file
filesystem::CreateFile(
path() + L"Account"); // MappedMemory needs an existing file...
auto mmap_ = MappedMemory::Open(path() + L"Account",
MappedMemory::Mode::kReadWrite, 0,
sizeof(X_XAMACCOUNTINFO) + 0x18);
if (!mmap_) {
XELOGE("UserProfile::Login: Failed to create new Account at %S!",
path().c_str());
} else {
XELOGI("Writing Account file for '%S' to path %SAccount",
account_.gamertag, path().c_str());
EncryptAccountFile(&account_, mmap_->data(), false);
mmap_->Close();
}
// Dash GPD will be updated below, after default settings are added
}
} else {
// Profile exists, load Account and any GPDs // Profile exists, load Account and any GPDs
auto mmap_ = auto mmap_ =
MappedMemory::Open(path() + L"Account", MappedMemory::Mode::kRead); MappedMemory::Open(path() + L"Account", MappedMemory::Mode::kRead);

View File

@ -216,6 +216,7 @@ class UserProfile {
// If no profiles are available, will use a Xenia-generated one // If no profiles are available, will use a Xenia-generated one
bool Login(uint64_t offline_xuid = 0); bool Login(uint64_t offline_xuid = 0);
void Logout(); void Logout();
bool Create(X_XAMACCOUNTINFO* account, bool generate_gamertag = false);
private: private:
// Extracts profile package if needed - returns path to extracted folder // Extracts profile package if needed - returns path to extracted folder

View File

@ -490,6 +490,54 @@ void XamShowDirtyDiscErrorUI(dword_t user_index) {
} }
DECLARE_XAM_EXPORT1(XamShowDirtyDiscErrorUI, kUI, kImplemented); DECLARE_XAM_EXPORT1(XamShowDirtyDiscErrorUI, kUI, kImplemented);
dword_result_t XamShowCreateProfileUI(dword_t user_index) {
auto user = kernel_state()->user_profile(user_index, true);
if (!user) {
return X_ERROR_ACCESS_DENIED; // xam returns this when errors?
}
// Broadcast XN_SYS_UI = true
kernel_state()->BroadcastNotification(0x9, true);
xe::threading::Fence fence;
std::wstring out_text;
++xam_dialogs_shown_;
auto display_window = kernel_state()->emulator()->display_window();
display_window->loop()->PostSynchronous([&]() {
// Create the dialog
(new KeyboardInputDialog(display_window, L"Profile Creation",
L"Choose a gamertag", L"", &out_text, 15))
->Then(&fence);
});
fence.Wait();
--xam_dialogs_shown_;
// Broadcast XN_SYS_UI = true
kernel_state()->BroadcastNotification(0x9, false);
X_XAMACCOUNTINFO account;
memset(&account, 0, sizeof(X_XAMACCOUNTINFO));
memcpy(account.gamertag, out_text.c_str(),
std::min((uint32_t)out_text.size(), 15u) * sizeof(wchar_t));
user->Logout();
user->Create(&account, false);
// TODO: the following does seem to trigger dash and make it try reloading the
// profile, but some reason it won't load properly until restart (no
// gamertag/gamerscore/games shown, etc)
// maybe need to set some notification for it or something?
user->Login(user->xuid_offline());
user->signin_state(1);
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamShowCreateProfileUI, kUI, kImplemented);
void RegisterUIExports(xe::cpu::ExportResolver* export_resolver, void RegisterUIExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {} KernelState* kernel_state) {}

View File

@ -881,6 +881,31 @@ dword_result_t XamUserGetIndexFromXUID(qword_t xuid, dword_t r4,
} }
DECLARE_XAM_EXPORT1(XamUserGetIndexFromXUID, kUserProfiles, kStub); DECLARE_XAM_EXPORT1(XamUserGetIndexFromXUID, kUserProfiles, kStub);
dword_result_t XamProfileCreate(dword_t flags, lpdword_t device_id,
qword_t xuid,
pointer_t<X_XAMACCOUNTINFO> account, dword_t r7,
dword_t r8, dword_t r9, dword_t r10) {
*device_id = 0xF00D0000;
X_XAMACCOUNTINFO swapped;
memcpy(&swapped, account, sizeof(X_XAMACCOUNTINFO));
xe::copy_and_swap<wchar_t>(swapped.gamertag, swapped.gamertag, 16);
if (xuid != 0) {
// Why is this param even included?
return X_E_INVALIDARG;
}
xam::UserProfile profile(kernel_state());
profile.Create(&swapped, false);
auto new_xuid = profile.xuid_offline();
// TODO: r10 seems to be some kind of output?
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamProfileCreate, kUserProfiles, kStub);
} // namespace xdbf } // namespace xdbf
} // namespace xam } // namespace xam
} // namespace kernel } // namespace kernel