diff --git a/src/xenia/kernel/xam/user_profile.cc b/src/xenia/kernel/xam/user_profile.cc index 2d0050438..42afb6c46 100644 --- a/src/xenia/kernel/xam/user_profile.cc +++ b/src/xenia/kernel/xam/user_profile.cc @@ -286,6 +286,53 @@ std::wstring UserProfile::ExtractProfile(const std::wstring& path) { 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) { xuid_offline_ = offline_xuid; auto profile_path = path(xuid_offline_); @@ -308,12 +355,9 @@ bool UserProfile::Login(uint64_t offline_xuid) { "using temp. profile"); memset(&account_, 0, sizeof(X_XAMACCOUNTINFO)); - // Create some unique IDs for the profile - static int num_xuids = 0; - xuid_offline_ = 0xE0000000BEEFCAFE + num_xuids; - account_.xuid_online = 0x09000000BEEFCAFE + num_xuids; - swprintf_s(account_.gamertag, L"XeniaUser%d", num_xuids); - num_xuids++; + if (!Create(&account_, true)) { + return false; + } profile_path = path(xuid_offline_); } @@ -325,31 +369,7 @@ bool UserProfile::Login(uint64_t offline_xuid) { return false; } - 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 { + if (filesystem::PathExists(profile_path_)) { // Profile exists, load Account and any GPDs auto mmap_ = MappedMemory::Open(path() + L"Account", MappedMemory::Mode::kRead); diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index 798988109..ad300d5fe 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -216,6 +216,7 @@ class UserProfile { // If no profiles are available, will use a Xenia-generated one bool Login(uint64_t offline_xuid = 0); void Logout(); + bool Create(X_XAMACCOUNTINFO* account, bool generate_gamertag = false); private: // Extracts profile package if needed - returns path to extracted folder diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index 507579145..72a542e2e 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -464,6 +464,54 @@ void XamShowDirtyDiscErrorUI(dword_t user_index) { } 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, KernelState* kernel_state) {} diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 99ab9a4ff..d82387e25 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -881,6 +881,31 @@ dword_result_t XamUserGetIndexFromXUID(qword_t xuid, dword_t r4, } DECLARE_XAM_EXPORT1(XamUserGetIndexFromXUID, kUserProfiles, kStub); +dword_result_t XamProfileCreate(dword_t flags, lpdword_t device_id, + qword_t xuid, + pointer_t 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(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 xam } // namespace kernel