#pragma once /* * UIScene_MSAuth — Microsoft Account Manager. * * Two views: * 1. Account List — shows saved accounts, select/remove/add. * 2. Device Code — device code flow for adding a new account. * * Native overlay (no SWF). */ #include "UIScene.h" #include "NativeUIRenderer.h" #include "../../../MCAuth/include/MCAuthManager.h" #include #include #include #include // Control IDs for UIScene_MSAuth focus list. // File-scope so static render helpers in the .cpp can reference them directly. namespace MSAuthUI { enum EControls { eBtn_Back = 0, eBtn_AddAccount = 1, eBtn_AddOffline = 2, eBtn_OfflineConfirm = 3, eBtn_ConfirmYes = 4, eBtn_ConfirmNo = 5, eBtn_OfflineTextBox = 6, eBtn_AddElyby = 7, eBtn_ElybyUsername = 8, eBtn_ElybyPassword = 9, eBtn_ElybySignIn = 10, eBtn_ElybyCancel = 11, eBtn_Elyby2FACode = 12, eBtn_Elyby2FASubmit = 13, eBtn_Elyby2FACancel = 14, eBtn_RemoveBase = 50, // 50 + index = remove button for account i eAccountBase = 100, // 100 + index = account row for account i eLink_URL = 200, }; } class UIScene_MSAuth : public UIScene { private: // View mode enum EView { eView_AccountList, eView_DeviceCode, eView_OfflineInput, eView_ElybyInput, eView_Elyby2FA }; EView m_view = eView_AccountList; // Shared flags survive scene destruction (prevent UAF from async callback) struct AuthFlags { std::atomic done { false }; std::atomic success{ false }; std::atomic need2FA{ false }; }; std::shared_ptr m_authFlags = std::make_shared(); int m_closeCountdown = 0; int m_spinnerTick = 0; NativeUI::NineSlice m_panel; NativeUI::NineSlice m_recessPanel; bool m_panelLoaded = false; NativeUI::FocusList m_focus; std::string m_cachedUri; // Cached account list (refreshed each tick) std::vector m_accounts; int m_activeIdx = -1; // Scroll offset for account list int m_scrollOffset = 0; void StartAddAccount(); void SwitchToAccountList(); void SwitchToDeviceCode(); void SwitchToOfflineInput(); void SwitchToElybyInput(); void SwitchToElyby2FA(); void ConfirmOfflineAccount(); void SubmitElybyLogin(); void SubmitElyby2FA(); // Offline username input state std::string m_offlineUsername; int m_offlineCursorBlink = 0; bool m_textInputActive = false; // true = text box has focus and is accepting keyboard input // Ely.by login state std::string m_elybyUsername; std::string m_elybyPassword; std::string m_elyby2FACode; // (2FA flag lives in m_authFlags->need2FA for async safety) int m_elybyActiveField = 0; // 0=username, 1=password, 2=2fa code // Target slot for splitscreen (0 = primary player, 1-3 = splitscreen). // When > 0, account selection binds to that slot instead of slot 0. int m_targetSlot = 0; // Input guard: ignore input for the first N ticks after opening to avoid // processing the button press that triggered the scene to open. int m_inputGuardTicks = 6; // Remove confirmation dialog (-1 = not showing, >=0 = account index pending removal) int m_pendingRemoveIdx = -1; std::string m_pendingRemoveUuid; // UUID captured at click time for stale-index safety public: // Shared buffer for virtual keyboard callback (public so the file-static // callback function can access it; prevents UAF if scene is destroyed // while the keyboard is open on a different UI group). struct PendingKeyboardResult { std::string value; std::atomic ready{false}; std::atomic valid{true}; }; // Skin texture cache entry (public so static render helpers can access it) struct SkinEntry { std::atomic textureId{-2}; // -2=not started, -1=downloading/failed, -3=file ready, >=0=texture ID std::string filePath; }; private: std::shared_ptr m_pendingKBResult; // Skin texture cache (key = UUID string) std::unordered_map> m_skinCache; void EnsureSkinLoaded(const std::string& uuid); public: UIScene_MSAuth(int iPad, void* initData, UILayer* parentLayer); ~UIScene_MSAuth(); virtual EUIScene getSceneType() override { return eUIScene_MSAuth; } virtual wstring getMoviePath() override { return L""; } virtual bool hidesLowerScenes() override { return true; } virtual bool blocksInput() override { return true; } virtual bool hasFocus(int iPad) override { return bHasFocus; } virtual bool needsReloaded() override { return false; } virtual void updateTooltips() override; virtual void tick() override; virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport) override; virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool& handled) override; virtual void handlePress(F64 controlId, F64 childId) override; #ifdef _WINDOWS64 virtual bool handleMouseClick(F32 x, F32 y) override; #endif };