FT SDK: Difference between revisions

From JFTSE Wiki
Jump to navigation Jump to search
Created page with "== Overview == This page documents the current reverse engineering progress for the Fantasy Tennis client-side SDK used by JFTSE tooling. The SDK maps known client structures, virtual functions, GUI objects, stage management, XML dialog loading, popup ownership and input routing into C++ wrappers that can be used from injected debugging or extension code. The current focus is the client GUI and StageManager layer. Confirmed areas include the fixed 24-stage owner model,..."
 
No edit summary
Line 2: Line 2:
This page documents the current reverse engineering progress for the Fantasy Tennis client-side SDK used by JFTSE tooling. The SDK maps known client structures, virtual functions, GUI objects, stage management, XML dialog loading, popup ownership and input routing into C++ wrappers that can be used from injected debugging or extension code.
This page documents the current reverse engineering progress for the Fantasy Tennis client-side SDK used by JFTSE tooling. The SDK maps known client structures, virtual functions, GUI objects, stage management, XML dialog loading, popup ownership and input routing into C++ wrappers that can be used from injected debugging or extension code.


The current focus is the client GUI and StageManager layer. Confirmed areas include the fixed 24-stage owner model, current-stage GUI attachment, embedded modal/input owner behavior, built-in popup factory behavior, owner lifecycle functions, ADU GUI object and control layouts, XML control binding, callback dispatch, StageManager-based input forwarding and EditBox text extraction.
The current focus is the client GUI layer around <code>StageManager</code>, <code>ScreenRoot</code>, <code>StageBase</code>, <code>GameDialogStage</code> and Adu GUI controls. Confirmed areas include the fixed 24-stage model, current-stage lookup, parent-chain/process/open-state behavior, built-in popup slot creation, XML dialog loading, command-ID binding, callback dispatch and Adu object state handling.


This page is the current reverse engineering handoff for the SDK. Names, offsets and layouts are updated as behavior is verified through disassembly, runtime tests and real client call sites.
This page is the current reverse engineering handoff for the SDK. Names, offsets and layouts are updated only after they are verified through disassembly, runtime dumps and real client call sites.


== Scope ==
== Scope ==
* StageManager state, input routing and owner lifecycle.
* <code>StageManager</code> state tracking, stage switching and current-stage access.
* Fixed stage owner list and current-stage owner access.
* Fixed 24-stage owner list and safe current-stage attachment.
* Safe custom GUI attachment to the current stage owner.
* <code>ScreenRoot</code> parent/child ownership, input routing, open state and processing state.
* Built-in popup owner factory and popup slot behavior.
* <code>StageBase</code> XML dialog ownership, child-owner lists, priority input child handling and GUI event dispatch.
* ADU GUI object, dialog and control layouts.
* <code>GameDialogStage</code> built-in popup slots, popup factory behavior, selected/active popup index and tray-popup related state.
* XML GUI binding through command IDs and control types.
* Game-dialog lifecycle hooks, including XML initialization, activation, update, back/default action and popup close paths.
* GUI callback dispatch, including callback user data and event parameters.
* Adu GUI object hierarchy, including active/update/visible flags, timers, child traversal and recursive processing.
* Confirmed edit-box text access through the wide-character text buffer.
* Adu dialog/control layouts, including object arrays, command IDs, control names, control types, state sets and basic runtime flags.
* Utility wrappers for dumping owners, dialogs, controls and popup state.
* XML GUI binding through <code>GuiBind</code> arrays, command IDs and control type IDs.
* Default GUI callback routing through <code>AduGuiDialog</code> callback/user-data fields.
* Confirmed event dispatch for buttons, radio buttons, edit boxes, context menus and related GUI events.
* Confirmed edit-box text access through the wide-character text pointer at the current <code>AduGuiEditBox</code> layout.
* Group-style control activation behavior used by ranking and popup XML, especially active/visible state synchronization.


== Stability Notes ==
== Stability Notes ==
Line 30: Line 34:
#include <cstdio>
#include <cstdio>
#include <cstring>
#include <cstring>
#include <cwchar>
#include <string>
#include <type_traits>
#include <type_traits>


Line 39: Line 45:
namespace FTSDK {
namespace FTSDK {


     struct GuiBind {
     namespace Structs {
         int32_t commandId;
         struct FTStringA {
        const char* controlName;
            uint32_t unknown_00;
        int32_t controlTypeId;
    };


    enum GuiEventType : int32_t {
            union {
        ButtonClick = 0,
                char inlineBuffer[16];
        ButtonOver = 1,
                char* heapPtr;
            };


        ComboBoxOpen = 2,
            uint32_t length;
        ComboBoxClose = 3,
            uint32_t capacity;
        ComboBoxSelectChange = 4,


        RadioButtonChange = 5,
            bool IsInline() const {
        CheckBoxChange = 6,
                return capacity < 16;
            }


        SliderValueChange = 7,
            bool IsValid() const {
if (length > capacity || length > 4096)
                    return false;


        EditBoxEnter = 8,
                if (capacity > 0xFFFFFFFEu)
        EditBoxChange = 9,
return false;
        EditBoxTAB = 10,
        EditBoxESC = 11,
        EditBoxCharLimit = 12,
        EditBoxKeyUp = 13,
        EditBoxKeyDown = 14,


        ListBoxDBClick = 15,
                if (!IsInline() && !heapPtr)
        ListBoxSelect = 16,
                    return false;
        ListBoxSelectEnd = 17,


        ContextMenuClick = 18,
                return true;
        ScrollBarPosChange = 19
            }
    };


    enum GuiControlType : int32_t {
            const char* Data() const {
        Static = 0,
                if (!IsValid())
        Button = 1,
                    return "";
        CheckBox = 2,
        RadioButton = 3,
        ComboBox = 4,
        Slider = 5,
        Gauge = 6,
        EditBox = 7,
        IMEEditBox = 8,
        ListBox = 9,
        ScrollBar = 10,
        ContextMenu = 11
    };


    using GuiCallbackFn = void(FTSDK_STDCALL*)(
                return IsInline() ? inlineBuffer : heapPtr;
        GuiEventType eventType,
            }
        int commandId,
        int param,
        void* userData
        );


    struct RectI {
            int32_t Length() const {
        int32_t left;
                return IsValid() ? static_cast<int32_t>(length) : 0;
        int32_t top;
            }
        int32_t right;
        int32_t bottom;
    };


    struct FTString {
            bool Equals(const char* text) const {
        uint32_t unknown_00;
                if (!text || !IsValid())
                    return false;


        union {
                const size_t textLen = std::strlen(text);
            char inlineBuffer[16];
                return textLen == length && std::memcmp(Data(), text, length) == 0;
             char* heapPtr;
             }
         };
         };


         uint32_t length;
         struct FTStringW {
        uint32_t capacity;
            uint32_t unknown_00;
 
            union {
                wchar_t inlineBuffer[8];
                wchar_t* heapPtr;
            };


        bool IsInline() const {
uint32_t length;
             return capacity <= 15;
             uint32_t capacity;
        }


        bool IsValid() const {
            bool IsInline() const {
            if (length > capacity || length >= 4096)
                 return capacity < 8;
                 return false;
}


             if (!IsInline() && !heapPtr)
             bool IsValid() const {
                return false;
if (length > capacity || length > 4096)
                    return false;


            return true;
                if (capacity > 0x7FFFFFFEu)
        }
                    return false;


        const char* Data() const {
                if (!IsInline() && !heapPtr)
            if (!IsValid())
                    return false;
                return "";


            return IsInline() ? inlineBuffer : heapPtr;
                return true;
        }
            }


        int32_t Length() const {
            const wchar_t* Data() const {
            return IsValid() ? static_cast<int32_t>(length) : 0;
                if (!IsValid())
        }
                    return L"";
                return IsInline() ? inlineBuffer : heapPtr;
}


        bool Equals(const char* text) const {
            int32_t Length() const {
            if (!text || !IsValid())
                return IsValid() ? static_cast<int32_t>(length) : 0;
                return false;
            }


             const size_t textLen = std::strlen(text);
             bool Equals(const wchar_t* text) const {
            return textLen == length && std::memcmp(Data(), text, length) == 0;
                if (!text || !IsValid())
        }
                    return false;
    };
                const size_t textLen = std::wcslen(text);
                return textLen == length && std::memcmp(Data(), text, length * sizeof(wchar_t)) == 0;
}
        };


    static_assert(sizeof(GuiBind) == 0x0C, "GuiBind must be 0x0C");
        template <typename T>
    static_assert(sizeof(RectI) == 0x10, "RectI must be 0x10");
        struct FTVector {
    static_assert(sizeof(FTString) == 0x1C, "FTString must be 0x1C");
            uint32_t unknown_00;
T* first;
T* last;
            T* end;


    struct AduGuiStateSet;
            int32_t Size() const {
                if (!first || !last || last < first)
                    return 0;


    template <typename T>
                return static_cast<int32_t>(last - first);
    struct FTContainerVector {
}
        void* containerBase;
        T* first;
        T* last;
        T* capacityEnd;


        int32_t size() const {
            int32_t Capacity() const {
            if (!first || !last || last < first)
                if (!first || !end || end < first)
                return 0;
                    return 0;


            return static_cast<int32_t>(last - first);
                return static_cast<int32_t>(end - first);
        }
            }


        int32_t capacity() const {
            bool IsEmpty() const {
            if (!first || !capacityEnd || capacityEnd < first)
return Size() == 0;
                return 0;
}


             return static_cast<int32_t>(capacityEnd - first);
             T& operator[](size_t index) {
        }
                return first[index];
            }


        bool empty() const {
            const T& operator[](size_t index) const {
            return size() == 0;
                return first[index];
        }
            }


        bool validIndex(std::size_t index) const {
            T* Begin() {
            return index < static_cast<std::size_t>(size());
                return first;
        }
}


        T& operator[](std::size_t index) {
            T* End() {
            return first[index];
                return last;
        }
            }


        const T& operator[](std::size_t index) const {
            const T* Begin() const {
            return first[index];
                return first;
        }
}


        T* begin() {
            const T* End() const {
            return first;
                return last;
         }
            }
         };


         T* end() {
         template <typename T>
             return last;
        struct FTList {
         }
             uint32_t unknown_00;
T* next;
uint32_t count;
         };


         const T* begin() const {
         struct GuiBind {
             return first;
            int32_t commandId;
         }
            const char* controlName;
            int32_t controlTypeId;
        };
       
        struct Rect {
             int32_t left;
            int32_t top;
            int32_t right;
            int32_t bottom;
         };


         const T* end() const {
         struct GuiRectStorage {
             return last;
            void* vtable;
         }
            int32_t left;
    };
            int32_t top;
            int32_t right;
             int32_t bottom;
         };


    static_assert(sizeof(FTContainerVector<void*>) == 0x10, "FTContainerVector must be 0x10");


    template <typename T>
        struct StageOwnedResource {
    struct FTVector3 {
            uint8_t unknown00[0x1C];
        T* first;
            void* object;
        T* last;
         };
         T* capacityEnd;


         int32_t size() const {
         struct StageOverlayVertex {
             if (!first || !last || last < first)
             float x;
                return 0;
            float y;
            float unknown08;
            float rhwOrOne;
            uint32_t color;
            float unknown14;
            float unknown18;
        };


             return static_cast<int32_t>(last - first);
        struct StageOverlayQuad {
        }
             StageOverlayVertex vertices[4];


        int32_t capacity() const {
            int32_t unknown70;
             if (!first || !capacityEnd || capacityEnd < first)
             int32_t unknown74;
                return 0;
            int32_t widthInt;
            int32_t heightInt;


             return static_cast<int32_t>(capacityEnd - first);
             float x;
        }
            float y;
            float width;
            float height;


        bool empty() const {
            float scaleX;
             return size() == 0;
             float alpha;
        }
            float rotationOrAngle;


        bool validIndex(std::size_t index) const {
            uint8_t unknown9C;
             return index < static_cast<std::size_t>(size());
             uint8_t unknown9D;
        }
            uint8_t visibleOrEnabled;
            uint8_t pad9F;


        T& operator[](std::size_t index) {
             uint8_t unknownA0[0x0C];
             return first[index];
         };
         }


         const T& operator[](std::size_t index) const {
         static_assert(sizeof(Rect) == 0x10);
            return first[index];
        static_assert(sizeof(GuiRectStorage) == 0x14);
         }
        static_assert(sizeof(StageOwnedResource) == 0x20);
        static_assert(sizeof(FTStringA) == 0x1C);
static_assert(sizeof(FTStringW) == 0x1C);
static_assert(sizeof(GuiBind) == 0x0C);
static_assert(sizeof(FTVector<void*>) == 0x10);
        static_assert(sizeof(StageOverlayQuad) == 0xAC);
        static_assert(sizeof(StageOverlayVertex) == 0x1C);
         static_assert(sizeof(FTList<void*>) == 0x0C);
    }


        T* begin() {
    enum GuiEventType : int32_t {
            return first;
        ButtonClick = 0,
         }
         ButtonOver = 1,


         T* end() {
         ComboBoxOpen = 2,
            return last;
        ComboBoxClose = 3,
         }
         ComboBoxSelectChange = 4,


         const T* begin() const {
         RadioButtonChange = 5,
            return first;
         CheckBoxChange = 6,
         }


         const T* end() const {
         SliderValueChange = 7,
            return last;
        }
    };


    static_assert(sizeof(FTVector3<void*>) == 0x0C, "FTVector3 must be 0x0C");
        EditBoxEnter = 8,
        EditBoxChange = 9,
        EditBoxTAB = 10,
        EditBoxESC = 11,
        EditBoxCharLimit = 12,
        EditBoxKeyUp = 13,
        EditBoxKeyDown = 14,


    template <typename T>
         ListBoxDBClick = 15,
    struct FTProcessingOwnerVector {
         ListBoxSelect = 16,
         void* unknown; // +0x00 / owner +0x1D8
         ListBoxSelectEnd = 17,
         T* first;      // +0x04 / owner +0x1DC
         T* last;      // +0x08 / owner +0x1E0


         int32_t size() const {
         ContextMenuClick = 18,
            if (!first || !last || last < first)
        ScrollBarPosChange = 19
                return 0;
    };
            return static_cast<int32_t>(last - first);
        }


        bool validIndex(std::size_t index) const {
    enum GuiControlType : int32_t {
            return index < static_cast<std::size_t>(size());
        Static = 0,
         }
        Button = 1,
        CheckBox = 2,
        RadioButton = 3,
        ComboBox = 4,
        Slider = 5,
        Gauge = 6,
        EditBox = 7,
        IMEEditBox = 8,
        ListBox = 9,
        ScrollBar = 10,
         ContextMenu = 11
    };


        T& operator[](std::size_t index) {
    using GuiCallbackFn = void(FTSDK_STDCALL*)(
            return first[index];
        GuiEventType eventType,
         }
        int commandId,
        int param,
        void* userData
         );


        const T& operator[](std::size_t index) const {
     struct AduGuiStateSet;
            return first[index];
        }
     };


     namespace Constants {
     namespace Constants {
         // Confirmed by StageManager InitStages / SwitchStateNow bounds.
         // Confirmed by StageManager InitStages / SwitchStateNow bounds.
         // Do not add a custom 25th stage; attach custom UI to an existing owner/modal.
         // Do not add a custom 25th stage; attach custom UI to an existing owner/modal
         constexpr int32_t StageOwnerCount = 24;
         constexpr int32_t StageOwnerCount = 24;
         // These are built-in popup owner slots at FTGameDialogOwner + 0x1E8.
         // These are built-in popup owner slots at FTGameDialogOwner + 0x1E8
         // Not proof that every stage uses all 10 slots.
         // Not proof that every stage uses all 10 slots
         constexpr int32_t KnownBuiltinPopupSlotCount = 10;
         constexpr int32_t KnownBuiltinPopupSlotCount = 10;
        constexpr uintptr_t BuiltinPopupOwnerSlotsOffset = 0x1E8;
        constexpr int32_t BuiltinPopupOwnerSlotBaseIndex = 122;
     }
     }


Line 310: Line 342:
         virtual AduGameObj* FTSDK_THISCALL Destroy(uint32_t flags) = 0;
         virtual AduGameObj* FTSDK_THISCALL Destroy(uint32_t flags) = 0;
         virtual bool FTSDK_THISCALL SetField08AndMark(int32_t value) = 0;
         virtual bool FTSDK_THISCALL SetField08AndMark(int32_t value) = 0;
         virtual int32_t FTSDK_THISCALL ResetElapsedRecursive() = 0;
         virtual int32_t FTSDK_THISCALL ResetTimerRecursive() = 0;
         virtual bool FTSDK_THISCALL SetActive(bool value) = 0;
         virtual bool FTSDK_THISCALL SetActive(bool value) = 0;
         virtual bool FTSDK_THISCALL SetUpdateBlocked(bool value) = 0;
         virtual bool FTSDK_THISCALL SetUpdateBlocked(bool value) = 0;
Line 404: Line 436:
         }
         }


         const FTString& Name() const {
         const Structs::FTStringA& Name() const {
             return *reinterpret_cast<const FTString*>(
             return *reinterpret_cast<const Structs::FTStringA*>(
                 reinterpret_cast<uintptr_t>(this) + 0x2C
                 reinterpret_cast<uintptr_t>(this) + 0x2C
                 );
                 );
Line 430: Line 462:
         }
         }


         const RectI& Rect() const {
         const Structs::Rect& Rect() const {
             return *reinterpret_cast<const RectI*>(
             return *reinterpret_cast<const Structs::Rect*>(
                 reinterpret_cast<uintptr_t>(this) + 0x60
                 reinterpret_cast<uintptr_t>(this) + 0x60
                 );
                 );
Line 576: Line 608:
     class StageManager;
     class StageManager;


     class FTGuiOwnerBase {
     class ScreenRoot {
     public:
     public:
         void* VTable() const {
         virtual void FTSDK_THISCALL Reserved00() = 0;
            return *reinterpret_cast<void* const*>(this);
        virtual void FTSDK_THISCALL Reserved04() = 0;
         }
        virtual void FTSDK_THISCALL Reserved08() = 0;
        virtual void FTSDK_THISCALL Reserved0C() = 0;
        virtual void FTSDK_THISCALL Reserved10() = 0;
        virtual void FTSDK_THISCALL Reserved14() = 0;
        virtual void FTSDK_THISCALL Reserved18() = 0;
        virtual void FTSDK_THISCALL Reserved1C() = 0;
         virtual void FTSDK_THISCALL Reserved20() = 0;
        virtual void FTSDK_THISCALL Reserved24() = 0;
        virtual void FTSDK_THISCALL Reserved28() = 0;


         int32_t StateIndex() const {
         virtual int FTSDK_THISCALL OnFocusEnter() = 0;
            return Read<int32_t>(0x04);
        virtual int FTSDK_THISCALL OnFocusLeave() = 0;
         }
        virtual void* FTSDK_THISCALL Destroy(uint32_t flags) = 0;
        virtual void FTSDK_THISCALL AttachRaw(ScreenRoot* parentOrManager, int32_t rectVtableOrTemp, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t id) = 0;
        virtual int FTSDK_THISCALL Shutdown() = 0;
        virtual int FTSDK_THISCALL Update() = 0;
        virtual int FTSDK_THISCALL OffsetRect(int32_t dx, int32_t dy) = 0;
        virtual void FTSDK_THISCALL CenterRectInParent() = 0;
         virtual ScreenRoot* FTSDK_THISCALL ActivateProcessing() = 0;
        virtual void FTSDK_THISCALL DeactivateProcessing() = 0;


         uintptr_t Address() const {
         virtual void FTSDK_THISCALL OnStageCommand(int commandId) = 0;
            return reinterpret_cast<uintptr_t>(this);
        virtual void FTSDK_THISCALL Reserved58() = 0;
         }
        virtual void FTSDK_THISCALL Reserved5C() = 0;
        virtual void FTSDK_THISCALL Reserved60() = 0;
        virtual void FTSDK_THISCALL Reserved64() = 0;
        virtual void FTSDK_THISCALL Reserved68() = 0;
        virtual void FTSDK_THISCALL Reserved6C() = 0;
         virtual void FTSDK_THISCALL Reserved70() = 0;
        virtual void FTSDK_THISCALL Reserved74() = 0;
        virtual void FTSDK_THISCALL Reserved78() = 0;
        virtual void FTSDK_THISCALL Reserved7C() = 0;
        virtual void FTSDK_THISCALL Reserved80() = 0;


         const char* OwnerName() const {
         virtual void FTSDK_THISCALL SetOpenFlag() = 0;
            return reinterpret_cast<const char*>(
        virtual void FTSDK_THISCALL ClearOpenFlag() = 0;
                reinterpret_cast<uintptr_t>(this) + 0x10
        virtual void FTSDK_STDCALL RectCleanupThunk(int32_t a1, uint8_t rectObj, int32_t a3, int32_t a4) = 0;
                );
        virtual void FTSDK_THISCALL HandleSystemEvent(int32_t eventType, void* data, int32_t param) = 0;
         }
         virtual void FTSDK_STDCALL RectCleanupThunk2(uint8_t rectObj, int32_t a2, int32_t a3) = 0;
        virtual bool FTSDK_THISCALL HandleInput(uint32_t msg, int32_t wParam, int32_t lParam) = 0;


        RectI OwnerRect() const {
    public:
            RectI r{};
         int32_t id;
            r.left = Left();
         float field08;
            r.top = Top();
         float field0C;
            r.right = Right();
            r.bottom = Bottom();
            return r;
        }
 
         int32_t Width() const {
            return Right() - Left();
         }
 
         int32_t Height() const {
            return Bottom() - Top();
        }


         int32_t Left() const {
         uint8_t unknown10[0x80];
            return Read<int32_t>(0x94);
        }


         int32_t Top() const {
         Structs::GuiRectStorage rect;
            return Read<int32_t>(0x98);
        }


         int32_t Right() const {
         Structs::FTVector<ScreenRoot*> children;
            return Read<int32_t>(0x9C);
         ScreenRoot* parent;
         }


         int32_t Bottom() const {
         int32_t parentChainEnabled;
            return Read<int32_t>(0xA0);
        int32_t processEnabled;
         }
        int32_t openFlag;
         int32_t rectOffsetEnabled;


         FTContainerVector<FTGuiOwnerBase*>& ActiveChildOwners() {
         uint8_t unknownC8[0x70];
            return *reinterpret_cast<FTContainerVector<FTGuiOwnerBase*>*>(
        char resourceName[0x80];
                reinterpret_cast<uintptr_t>(this) + 0xA4
        void* lazyResource;
                );
        uint8_t lazyResourceAllowed;
        }
        uint8_t pad1BD[3];
    };
static_assert(sizeof(ScreenRoot) == 0x1C0);


        const FTContainerVector<FTGuiOwnerBase*>& ActiveChildOwners() const {
    class ScreenInputRoot : public ScreenRoot {
            return *reinterpret_cast<const FTContainerVector<FTGuiOwnerBase*>*>(
    public:
                reinterpret_cast<uintptr_t>(this) + 0xA4
        uint8_t unknown1C0[0x0C];
                );
          
         }
         ScreenRoot* rootFocusOwner;
 
        ScreenRoot* inputFocusOwner;
         FTGuiOwnerBase* UnknownB0_ActiveChildCapacityEndAlias() const {
         ScreenRoot* previousFocusOwner;
            return Read<FTGuiOwnerBase*>(0xB0);
         ScreenRoot* activeFocusOwner;
         }
       
 
         uint8_t unknown1DC[0x08];
         FTGuiOwnerBase* ParentOwner() const {
            return Read<FTGuiOwnerBase*>(0xB4);
         }


         void* OwnerManager() const {
         int32_t cursorX;
            return ParentOwner();
int32_t cursorY;
        }


         bool IsParentChainEnabled() const {
         uint8_t inputState[0x26C];
            return Read<uint32_t>(0xB8) != 0;
        }


         bool IsProcessEnabled() const {
         ScreenRoot* hoverOrCapturedOwner;
            return Read<uint32_t>(0xBC) != 0;
        }


         bool IsRegisteredForProcessing() const {
         char resourceBasePath[0x104];
            return IsProcessEnabled();
    };
        }
static_assert(sizeof(ScreenInputRoot) == 0x560);


        bool IsOwnerActiveFlag() const {
    class StageBase : public ScreenRoot {
            return IsRegisteredForProcessing();
    public:
        }
virtual void FTSDK_THISCALL AttachOwnerWithDefaultRect(ScreenRoot* parent, int32_t id) = 0;
 
virtual int FTSDK_THISCALL InitDialogWithParentOwner(StageBase* parent) = 0;
        bool IsOwnerModalOrOpenFlag() const {
virtual bool FTSDK_THISCALL LoadXml(const char* xmlName, const Structs::GuiBind* binds, int32_t bindCount, GuiCallbackFn callback, bool reload) = 0;
            return Read<uint32_t>(0xC0) != 0;
virtual int FTSDK_THISCALL OnActivate() = 0;
         }
virtual int FTSDK_THISCALL OnDeactivate() = 0;
virtual int FTSDK_THISCALL OnUpdate() = 0;
virtual bool FTSDK_THISCALL ProcessChildOwners(float dt) = 0;
virtual int FTSDK_THISCALL HandleStagePacket(void* packet) = 0;
virtual int FTSDK_THISCALL HandleGuiEvent(GuiEventType eventType, int commandId, int param) = 0;
virtual int FTSDK_THISCALL OnCommandAction(int commandId) = 0;
virtual int FTSDK_THISCALL OnEditBoxEnter(int commandId) = 0;
virtual int FTSDK_THISCALL OnEditBoxChange(int commandId) = 0;
virtual int FTSDK_THISCALL OnEditBoxTab(int commandId) = 0;
virtual int FTSDK_THISCALL OnEditBoxEsc(int commandId) = 0;
virtual int FTSDK_THISCALL OnEditBoxKeyUp(int commandId) = 0;
virtual int FTSDK_THISCALL OnEditBoxKeyDown(int commandId) = 0;
        virtual int FTSDK_THISCALL OnContextMenuClick(int commandId, int selectedValue, AduGuiControl* contextMenuControl, int selectedData) = 0;
    public:
         StageBase* priorityInputChildOwner;
        uint8_t inputConsumedFlag;
        uint8_t padOrUnknown1C5[3];
        AduGuiDialog* xmlDialog;


        bool IsRectOffsetEnabled() const {
Structs::FTList<void> internalOwnerList;
            return Read<uint32_t>(0xC4) != 0;
         Structs::FTVector<StageBase*> processingChildOwners;
         }


    public:
         bool InputConsumedFlag() const {
         bool InputConsumedFlag() const {
             return Read<uint8_t>(0x1C4) != 0;
             return inputConsumedFlag != 0;
         }
         }


         AduGuiDialog* XmlDialog() const {
         AduGuiDialog* XmlDialog() const {
             return Read<AduGuiDialog*>(0x1C8);
             return xmlDialog;
         }
         }


         bool HasXmlDialog() const {
         bool HasXmlDialog() const {
             return XmlDialog() != nullptr;
             return xmlDialog != nullptr;
         }
         }


         FTProcessingOwnerVector<FTGuiOwnerBase*>& ProcessingChildOwners() {
         StageBase* PriorityInputChildOwner() const {
             return *reinterpret_cast<FTProcessingOwnerVector<FTGuiOwnerBase*>*>(
             return priorityInputChildOwner;
                reinterpret_cast<uintptr_t>(this) + 0x1D8
                );
         }
         }


         const FTProcessingOwnerVector<FTGuiOwnerBase*>& ProcessingChildOwners() const {
         Structs::FTVector<StageBase*>& ProcessingChildOwners() {
             return *reinterpret_cast<const FTProcessingOwnerVector<FTGuiOwnerBase*>*>(
             return processingChildOwners;
                reinterpret_cast<uintptr_t>(this) + 0x1D8
                );
         }
         }


         FTGuiOwnerBase* Unknown1D0_SelfOnEmbeddedModalInit() const {
         const Structs::FTVector<StageBase*>& ProcessingChildOwners() const {
             return Read<FTGuiOwnerBase*>(0x1D0);
             return processingChildOwners;
         }
         }


         AduGuiControl* FindControlByCommandId(int32_t commandId) const {
         void SetPriorityInputChildOwner(StageBase* child) {
             auto* xml = XmlDialog();
             priorityInputChildOwner = child;
            return xml ? xml->FindControlByCommandId(commandId) : nullptr;
}
        }


         AduGuiControl* FindControlByName(const char* name) const {
         void ClearPriorityInputChildOwnerIf(StageBase* child) {
             auto* xml = XmlDialog();
             if (PriorityInputChildOwner() == child)
            return xml ? xml->FindControlByName(name) : nullptr;
                SetPriorityInputChildOwner(nullptr);
        }
}
 
        void SetOwnerRect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
            Write<int32_t>(0x94, left);
            Write<int32_t>(0x98, top);
            Write<int32_t>(0x9C, right);
            Write<int32_t>(0xA0, bottom);
        }


        using DestroyOwnerFn = void* (FTSDK_THISCALL*)(
         using AddChildOwnerFn = int(FTSDK_THISCALL*)(StageBase* self, StageBase* child);
            FTGuiOwnerBase* self,
         void AddChildOwner(StageBase* child) {
            uint32_t flags
            );
 
        void* DestroyOwner(uint32_t flags) {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<DestroyOwnerFn>(vt[0x34 / 4]);
            return fn(this, flags);
        }
 
         using AddChildOwnerFn = int(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self,
            FTGuiOwnerBase* child
            );
 
         void AddChildOwner(FTGuiOwnerBase* child) {
             reinterpret_cast<AddChildOwnerFn>(0x005AA720)(this, child);
             reinterpret_cast<AddChildOwnerFn>(0x005AA720)(this, child);
         }
         }


         using BringChildOwnerToFrontFn = int(FTSDK_THISCALL*)(
         using BringChildOwnerToFrontFn = int(FTSDK_THISCALL*)(StageBase* self, StageBase* child);
            FTGuiOwnerBase* self,
         void BringChildOwnerToFront(StageBase* child) {
            FTGuiOwnerBase* child
            );
 
         void BringChildOwnerToFront(FTGuiOwnerBase* child) {
             reinterpret_cast<BringChildOwnerToFrontFn>(0x005AA780)(this, child);
             reinterpret_cast<BringChildOwnerToFrontFn>(0x005AA780)(this, child);
         }
         }


         using IsOwnerReachableFn = int(FTSDK_FASTCALL*)(
         using IsOwnerReachableFn = int(FTSDK_FASTCALL*)(StageBase* self);
            FTGuiOwnerBase* self
            );
 
         bool IsReachableThroughParentChain() const {
         bool IsReachableThroughParentChain() const {
             return reinterpret_cast<IsOwnerReachableFn>(0x005A99E0)(
             return reinterpret_cast<IsOwnerReachableFn>(0x005A99E0)(const_cast<StageBase*>(this)) != 0;
                const_cast<FTGuiOwnerBase*>(this)
                ) != 0;
         }
         }


        using ActivateOwnerProcessingFn = FTGuiOwnerBase * (FTSDK_THISCALL*)(
         using GetControlByCommandIdFn = AduGuiControl* (FTSDK_THISCALL*)(StageBase* self, int32_t commandId, int32_t expectedType);
            FTGuiOwnerBase* self
            );
 
        // vtable + 0x4C.
        FTGuiOwnerBase* ActivateOwnerProcessing() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<ActivateOwnerProcessingFn>(vt[0x4C / 4]);
            return fn(this);
        }
 
        using OnPostGuiLoadFn = void(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self
            );
 
        void OnPostGuiLoad() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<OnPostGuiLoadFn>(vt[0x50 / 4]);
            fn(this);
        }
 
        using SetOpenFlagFn = void(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self
            );
 
        void OnOwnerOpened() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<SetOpenFlagFn>(vt[0x84 / 4]);
            fn(this);
        }
 
        void OnOwnerClosed() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<SetOpenFlagFn>(vt[0x88 / 4]);
            fn(this);
        }
 
        using OpenOwnerFn = int(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self
            );
 
        int OpenOwner() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<OpenOwnerFn>(vt[0x2C / 4]);
            return fn(this);
        }
 
        int CloseOwner() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<OpenOwnerFn>(vt[0x30 / 4]);
            return fn(this);
        }
 
        using LoadGuiFn = bool(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self,
            const char* xmlName,
            const GuiBind* binds,
            int32_t bindCount,
            GuiCallbackFn callback,
            bool reload
            );
 
        bool LoadGui(
            const char* xmlName,
            const GuiBind* binds,
            int32_t bindCount,
            GuiCallbackFn callback = nullptr,
            bool reload = false
        ) {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<LoadGuiFn>(vt[0xA4 / 4]);
            return fn(this, xmlName, binds, bindCount, callback, reload);
        }
 
        using InitWithParentOwnerFn = void(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self,
            FTGuiOwnerBase* parentOwner
            );
 
        // Base implementation vfunc_1757376:
        //  - attaches this owner to parent with a zero rect
        //  - sets ParentOwner at +0xB4
        //  - adds this to parent->ActiveChildOwners
        //
        // Popup subclasses override this
        void InitWithParentOwner(FTGuiOwnerBase* parentOwner) {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<InitWithParentOwnerFn>(vt[0xA0 / 4]);
            fn(this, parentOwner);
        }
 
        using DispatchGuiEventFn = int(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self,
            GuiEventType eventType,
            int commandId,
            int param
            );
 
        int DispatchGuiEvent(GuiEventType eventType, int commandId, int param) {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<DispatchGuiEventFn>(vt[0xBC / 4]);
            return fn(this, eventType, commandId, param);
        }
 
        using SetOwnerRectCenteredInParentFn = void(FTSDK_THISCALL*)(
            FTGuiOwnerBase* self
            );
 
        void SetOwnerRectCenteredInParent() {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<SetOwnerRectCenteredInParentFn>(vt[0x48 / 4]);
            fn(this);
        }
 
         using GetControlByCommandIdFn = AduGuiControl * (FTSDK_THISCALL*)(
            FTGuiOwnerBase* self,
            int32_t commandId,
            int32_t expectedType
            );
 
         AduGuiControl* GetControlByCommandId(int32_t commandId, int32_t expectedType = -1) {
         AduGuiControl* GetControlByCommandId(int32_t commandId, int32_t expectedType = -1) {
             return reinterpret_cast<GetControlByCommandIdFn>(0x005AE7D0)(
             return reinterpret_cast<GetControlByCommandIdFn>(0x005AE7D0)(this, commandId, expectedType);
                this,
                commandId,
                expectedType
                );
         }
         }


         using GetControlInnerObjectFn = void* (FTSDK_THISCALL*)(
         using GetControlInnerObjectFn = void* (FTSDK_THISCALL*)(StageBase* self, int32_t commandId);
            FTGuiOwnerBase* self,
            int32_t commandId
            );
 
         void* GetControlInnerObject(int32_t commandId) {
         void* GetControlInnerObject(int32_t commandId) {
             return reinterpret_cast<GetControlInnerObjectFn>(0x005AE840)(
             return reinterpret_cast<GetControlInnerObjectFn>(0x005AE840)(this, commandId);
                this,
                commandId
                );
         }
         }
    };


         using HandleInputFn = bool(FTSDK_THISCALL*)(
    class GameDialogStage : public StageBase {
            FTGuiOwnerBase* self,
    public:
            uint32_t msg,
         virtual void FTSDK_THISCALL AttachOwnerAndCreateUserNotice(ScreenRoot* parent, int32_t id) = 0;
            int32_t wParam,
        virtual int FTSDK_THISCALL InitStageXml() = 0;
            int32_t lParam
        virtual int FTSDK_THISCALL SetSelectedOrActivePopupIndex(int32_t index) = 0;
            );
        virtual int FTSDK_THISCALL CanLeaveStageOrPopup(int32_t reason) = 0;
        virtual void FTSDK_THISCALL ReservedF0() = 0;
        virtual int FTSDK_THISCALL OnBackOrDefaultAction() = 0;


        // vtable + 0x98.
    public:
        //
         StageBase* builtinPopupOwners[10];
        // Base/game-dialog behavior vfunc_1758720:
        uint8_t xmlLoadedFlag;
         //  - delegates to focused/input owner if present
         uint8_t pad211[3];
        //  - handles IME/edit path if present
        //  - walks ProcessingChildOwners at +0x1D8
        //  - calls child->HandleInput(...) when child +0xBC is enabled
        bool HandleInput(uint32_t msg, int32_t wParam, int32_t lParam) {
            auto vt = *reinterpret_cast<void***>(this);
            auto fn = reinterpret_cast<HandleInputFn>(vt[0x98 / 4]);
            return fn(this, msg, wParam, lParam);
         }
 
        // +0x1C0 / this[112].
        //
        // Used by vt+0x98 HandleInput and vt+0xB4 update traversal before
        // falling back to the +0x1D8 processing child vector.
        //
        // If set and reachable/process-enabled, this owner receives input first.
        FTGuiOwnerBase* PriorityInputChildOwner() const {
            return Read<FTGuiOwnerBase*>(0x1C0);
        }
 
        void SetPriorityInputChildOwner(FTGuiOwnerBase* child) {
            Write<FTGuiOwnerBase*>(0x1C0, child);
        }
 
        void ClearPriorityInputChildOwnerIf(FTGuiOwnerBase* child) {
            if (PriorityInputChildOwner() == child)
                SetPriorityInputChildOwner(nullptr);
        }
 
        void SetParentChainEnabled(bool value) {
            Write<uint32_t>(0xB8, value ? 1u : 0u);
        }
 
        uint8_t ReadU8(uintptr_t offset) const {
            return Read<uint8_t>(offset);
        }
 
        uint32_t ReadU32(uintptr_t offset) const {
            return Read<uint32_t>(offset);
        }


         void* ReadPtr(uintptr_t offset) const {
         int32_t selectedOrActivePopupIndex;
            return Read<void*>(offset);
         void* trayPopupOwner;
        }
 
    protected:
        template <typename T>
        T Read(uintptr_t offset) const {
            return *reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(this) + offset);
        }
 
        template <typename T>
         void Write(uintptr_t offset, T value) {
            *reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(this) + offset) = value;
        }
    };


    class FTGameDialogOwner : public FTGuiOwnerBase {
     public:
     public:
        bool IsXmlLoadedGameFlag() const {
            return Read<uint8_t>(0x210) != 0;
        }
         bool IsXmlLoaded() const {
         bool IsXmlLoaded() const {
             return IsXmlLoadedGameFlag();
             return xmlLoadedFlag != 0;
         }
         }


         // Valid only for stage/current dialog owners that own the built-in popup table.
         StageBase* BuiltinPopupOwnerAt(int32_t index) const {
        // Do not call this on arbitrary popup owner instances.
             if (index < 0 || index >= 10)
        //
        // sub_498890 uses parentOwner[122 + index] as the built-in popup slot table.
        // Ranking popup constructor also writes to [122], proving that offset 0x1E8
        // is subclass-specific on popup objects and not universally a popup table.
        FTGameDialogOwner* StageBuiltinPopupOwnerAt(int32_t popupIndex) const {
             if (popupIndex < 0 || popupIndex >= Constants::KnownBuiltinPopupSlotCount)
                 return nullptr;
                 return nullptr;


             return Read<FTGameDialogOwner*>(
             return builtinPopupOwners[index];
                Constants::BuiltinPopupOwnerSlotsOffset + popupIndex * 4
            );
        }
 
        // Compatibility alias. Use only on stage/current dialog owners.
        FTGameDialogOwner* BuiltinPopupOwnerAt(int32_t popupIndex) const {
            return StageBuiltinPopupOwnerAt(popupIndex);
        }
 
        // Compatibility alias. Use only on stage/current dialog owners.
        FTGameDialogOwner* PopupOwnerAt(int32_t popupIndex) const {
            return StageBuiltinPopupOwnerAt(popupIndex);
        }
 
        AduGuiDialog* StageBuiltinPopupXmlDialogAt(int32_t popupIndex) const {
            auto* popup = StageBuiltinPopupOwnerAt(popupIndex);
            return popup ? popup->XmlDialog() : nullptr;
        }
 
        AduGuiDialog* BuiltinPopupXmlDialogAt(int32_t popupIndex) const {
            return StageBuiltinPopupXmlDialogAt(popupIndex);
        }
 
        AduGuiDialog* PopupXmlDialogAt(int32_t popupIndex) const {
            return StageBuiltinPopupXmlDialogAt(popupIndex);
        }
 
        bool HasStageBuiltinPopupOwner(int32_t popupIndex) const {
            return StageBuiltinPopupOwnerAt(popupIndex) != nullptr;
        }
 
        bool HasAnyStageBuiltinPopupOwner() const {
            for (int32_t i = 0; i < Constants::KnownBuiltinPopupSlotCount; ++i) {
                if (StageBuiltinPopupOwnerAt(i))
                    return true;
            }
 
            return false;
         }
         }
 
       
         using GetOrCreateBuiltinPopupOwnerFn = FTGameDialogOwner * (FTSDK_THISCALL*)(
         using GetOrCreateBuiltinPopupOwnerFn = StageBase * (FTSDK_THISCALL*)(StageBase* self, uint32_t popupIndex);
            FTGameDialogOwner* self,
         StageBase* GetOrCreateBuiltinPopupOwner(uint32_t popupIndex) {
            uint32_t popupIndex
            );
 
        // sub_498890 / 0x00498890.
        //
        // Valid for stage/current dialog owners.
        //
        // Creates one of the known built-in popup owner types if the slot is empty,
        // stores it at parentOwner[122 + popupIndex], then calls the popup owner's
        // vt+0xA0 init/attach method with this owner as parent.
         FTGameDialogOwner* GetOrCreateBuiltinPopupOwner(uint32_t popupIndex) {
             if (popupIndex >= Constants::KnownBuiltinPopupSlotCount)
             if (popupIndex >= Constants::KnownBuiltinPopupSlotCount)
                 return nullptr;
                 return nullptr;


             return reinterpret_cast<GetOrCreateBuiltinPopupOwnerFn>(0x00498890)(
             return reinterpret_cast<GetOrCreateBuiltinPopupOwnerFn>(0x00498890)(this, popupIndex);
                this,
                popupIndex
                );
         }
         }
     };
     };


     class StageManager {
     class StageManager : public ScreenInputRoot {
     public:
     public:
         virtual void FTSDK_THISCALL Reserved00() = 0;
         void* hwnd;
        virtual void FTSDK_THISCALL Reserved04() = 0;
         int32_t previousStageId;
        virtual void FTSDK_THISCALL Reserved08() = 0;
         int32_t pendingStageId;
        virtual void FTSDK_THISCALL Reserved0C() = 0;
         int32_t currentStageId;
        virtual void FTSDK_THISCALL Reserved10() = 0;
         int32_t transitionStageScratchId;
        virtual void FTSDK_THISCALL Reserved14() = 0;
         virtual void FTSDK_THISCALL Reserved18() = 0;
         virtual void FTSDK_THISCALL Reserved1C() = 0;
         virtual void FTSDK_THISCALL Reserved20() = 0;
         virtual void FTSDK_THISCALL Reserved24() = 0;
        virtual void FTSDK_THISCALL Reserved28() = 0;
 
        virtual int FTSDK_THISCALL OpenOwner() = 0;
        virtual int FTSDK_THISCALL CloseOwner() = 0;
 
        virtual void* FTSDK_THISCALL Destroy(uint32_t flags) = 0;


         virtual void FTSDK_THISCALL AttachOwnerRaw(
         GameDialogStage* currentStage;
            void* parentOrManager,
         uint8_t embeddedDialogStageStorage[0x21C];
            int32_t rectVtableOrTemp,
            int32_t left,
            int32_t top,
            int32_t right,
            int32_t bottom,
            int32_t stateIndex
         ) = 0;


         virtual int FTSDK_THISCALL ShutdownStages() = 0;
         Structs::FTVector<GameDialogStage*> stages;
        virtual int FTSDK_THISCALL UpdateStageManager() = 0;


         virtual int FTSDK_THISCALL OffsetOwnerRect(int32_t dx, int32_t dy) = 0;
         void* currentOverlayRaw;
         virtual void FTSDK_THISCALL CenterOwnerRectInParent() = 0;
         Structs::StageOverlayQuad overlayQuad;


         virtual StageManager* FTSDK_THISCALL RegisterProcessEnabled() = 0;
         float overlayTimer;
        virtual void FTSDK_THISCALL ResetProcessEnabled() = 0;
 
        virtual void FTSDK_THISCALL Reserved54() = 0;
        virtual void FTSDK_THISCALL Reserved58() = 0;
        virtual void FTSDK_THISCALL Reserved5C() = 0;
        virtual void FTSDK_THISCALL Reserved60() = 0;
        virtual void FTSDK_THISCALL Reserved64() = 0;
        virtual void FTSDK_THISCALL Reserved68() = 0;
        virtual void FTSDK_THISCALL Reserved6C() = 0;
        virtual void FTSDK_THISCALL Reserved70() = 0;
        virtual void FTSDK_THISCALL Reserved74() = 0;
        virtual void FTSDK_THISCALL Reserved78() = 0;
        virtual void FTSDK_THISCALL Reserved7C() = 0;
        virtual void FTSDK_THISCALL Reserved80() = 0;


         virtual void FTSDK_THISCALL OnOwnerOpened() = 0;
         Structs::StageOwnedResource ownedEffectResource;
        virtual void FTSDK_THISCALL OnOwnerClosed() = 0;


         virtual void FTSDK_STDCALL RectCleanupThunk(
         int32_t frameUpdating;
            int32_t a1,
            uint8_t rectObj,
            int32_t a3,
            int32_t a4
        ) = 0;


         virtual void FTSDK_THISCALL HandleSystemEvent(
         uint8_t unknown87C[0x10];
            int32_t eventType,
            void* data,
            int32_t param
        ) = 0;


         virtual void FTSDK_STDCALL RectCleanupThunk2(
         uint8_t delayedReconnectFlag;
            uint8_t rectObj,
            int32_t a2,
            int32_t a3
        ) = 0;


         virtual bool FTSDK_THISCALL HandleInput(
         uint8_t pad88D[3];
            uint32_t msg,
            int32_t wParam,
            int32_t lParam
        ) = 0;


    public:
         float periodicTimer;
         void* VTable() const {
         uint8_t hourlyPacketReadyFlag;
            return *reinterpret_cast<void* const*>(this);
         }


         void* Hwnd() const {
         uint8_t pad895[3];
            return Read<void*>(0x560);
        }


         int32_t PreviousState() const {
         float hourlyPacketTimer;
            return Read<int32_t>(0x564);
        int32_t unknown89C;
        }


         int32_t PendingState() const {
         GameDialogStage* EmbeddedDialogStage() {
             return Read<int32_t>(0x568);
             return reinterpret_cast<GameDialogStage*>(embeddedDialogStageStorage);
         }
         }


         int32_t CurrentState() const {
         const GameDialogStage* EmbeddedDialogStage() const {
             return Read<int32_t>(0x56C);
             return reinterpret_cast<const GameDialogStage*>(embeddedDialogStageStorage);
         }
         }


         int32_t TransitionStateScratch() const {
         GameDialogStage* CurrentStage() const {
             return Read<int32_t>(0x570);
             if (currentStage)
        }
                return currentStage;


        FTGameDialogOwner* CurrentStageOwner() const {
if (currentStageId < 0 || currentStageId >= stages.Size())
            return Read<FTGameDialogOwner*>(0x574);
return nullptr;
        }


        FTGameDialogOwner* CurrentDialogOwner() const {
return stages[currentStageId];
            return CurrentStageOwner();
         }
         }
 
          
         AduGuiDialog* CurrentDialog() const {
         bool IsValidStageIndex(uint32_t index) const {
            auto* owner = CurrentStageOwner();
             return index < Constants::StageOwnerCount;
            return owner ? owner->XmlDialog() : nullptr;
        }
 
         bool HasModalDialog() const {
            return Read<uint32_t>(0x634) != 0;
        }
 
        FTGuiOwnerBase* ModalDialogOwner() {
            return reinterpret_cast<FTGuiOwnerBase*>(
                reinterpret_cast<uintptr_t>(this) + 0x578
                );
        }
 
        const FTGuiOwnerBase* ModalDialogOwner() const {
            return reinterpret_cast<const FTGuiOwnerBase*>(
                reinterpret_cast<uintptr_t>(this) + 0x578
                );
        }
 
        FTContainerVector<FTGameDialogOwner*>& StageOwners() {
            return *reinterpret_cast<FTContainerVector<FTGameDialogOwner*>*>(
                reinterpret_cast<uintptr_t>(this) + 0x794
                );
        }
 
        const FTContainerVector<FTGameDialogOwner*>& StageOwners() const {
            return *reinterpret_cast<const FTContainerVector<FTGameDialogOwner*>*>(
                reinterpret_cast<uintptr_t>(this) + 0x794
                );
        }
 
        FTContainerVector<FTGameDialogOwner*>& DialogOwners() {
            return StageOwners();
        }
 
        const FTContainerVector<FTGameDialogOwner*>& DialogOwners() const {
            return StageOwners();
        }
 
        FTGameDialogOwner* StageOwnerAt(int32_t index) const {
            const auto& owners = StageOwners();
 
            if (index < 0 || !owners.validIndex(static_cast<std::size_t>(index)))
                return nullptr;
 
            return owners[static_cast<std::size_t>(index)];
        }
 
        FTGameDialogOwner* DialogOwnerAt(int32_t index) const {
             return StageOwnerAt(index);
        }
 
        void* CurrentOverlayRaw() const {
            return Read<void*>(0x7A4);
        }
 
        float OverlayTimer() const {
            return Read<float>(0x854);
        }
 
        bool IsFrameUpdating() const {
            return Read<uint32_t>(0x878) != 0;
        }
 
        uint8_t DelayedReconnectFlag() const {
            return Read<uint8_t>(0x88C);
        }
 
        float PeriodicTimer() const {
            return Read<float>(0x890);
        }
 
        uint8_t HourlyPacketReadyFlag() const {
            return Read<uint8_t>(0x894);
        }
 
        float HourlyPacketTimer() const {
            return Read<float>(0x898);
        }
 
        bool IsValidStageIndex(uint32_t state) const {
            return state < Constants::StageOwnerCount;
        }
 
        bool HasCurrentStageOwner() const {
            return CurrentStageOwner() != nullptr;
         }
         }


Line 1,288: Line 916:
         bool InitStages(void* hwnd, int startupParam) {
         bool InitStages(void* hwnd, int startupParam) {
             return reinterpret_cast<InitStagesFn>(0x004B0590)(this, hwnd, startupParam);
             return reinterpret_cast<InitStagesFn>(0x004B0590)(this, hwnd, startupParam);
        }
    private:
        template <typename T>
        T Read(uintptr_t offset) const {
            return *reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(this) + offset);
         }
         }
     };
     };


     using GetStageManagerFn = StageManager * (FTSDK_CDECL*)();
     using GetStageManagerFn = StageManager* (FTSDK_CDECL*)();
 
     inline StageManager* GetStageManager() {
     inline StageManager* GetStageManager() {
         return reinterpret_cast<GetStageManagerFn>(0x004B0520)();
         return reinterpret_cast<GetStageManagerFn>(0x004B0520)();
     }
     }


     using ConstructGuiOwnerBaseFn = FTGuiOwnerBase * (FTSDK_THISCALL*)(
     using ConstructGuiOwnerBaseFn = StageBase* (FTSDK_THISCALL*)(StageBase* self);
        FTGuiOwnerBase* self
     inline StageBase* ConstructStageBase(StageBase* memory) {
        );
 
     inline FTGuiOwnerBase* ConstructGuiOwnerBase(FTGuiOwnerBase* memory) {
         return reinterpret_cast<ConstructGuiOwnerBaseFn>(0x005AEC40)(memory);
         return reinterpret_cast<ConstructGuiOwnerBaseFn>(0x005AEC40)(memory);
     }
     }
      
      
     using ConstructGameDialogOwnerFn = FTGameDialogOwner * (FTSDK_THISCALL*)(
     using ConstructGameDialogStageFn = GameDialogStage* (FTSDK_THISCALL*)(GameDialogStage* self);
        FTGameDialogOwner* self
     inline GameDialogStage* ConstructGameDialogStage(GameDialogStage* memory) {
        );
         return reinterpret_cast<ConstructGameDialogStageFn>(0x004987C0)(memory);
 
     inline FTGameDialogOwner* ConstructGameDialogOwner(FTGameDialogOwner* memory) {
         return reinterpret_cast<ConstructGameDialogOwnerFn>(0x004987C0)(memory);
     }
     }
}
}

Revision as of 14:11, 13 May 2026

Overview

This page documents the current reverse engineering progress for the Fantasy Tennis client-side SDK used by JFTSE tooling. The SDK maps known client structures, virtual functions, GUI objects, stage management, XML dialog loading, popup ownership and input routing into C++ wrappers that can be used from injected debugging or extension code.

The current focus is the client GUI layer around StageManager, ScreenRoot, StageBase, GameDialogStage and Adu GUI controls. Confirmed areas include the fixed 24-stage model, current-stage lookup, parent-chain/process/open-state behavior, built-in popup slot creation, XML dialog loading, command-ID binding, callback dispatch and Adu object state handling.

This page is the current reverse engineering handoff for the SDK. Names, offsets and layouts are updated only after they are verified through disassembly, runtime dumps and real client call sites.

Scope

  • StageManager state tracking, stage switching and current-stage access.
  • Fixed 24-stage owner list and safe current-stage attachment.
  • ScreenRoot parent/child ownership, input routing, open state and processing state.
  • StageBase XML dialog ownership, child-owner lists, priority input child handling and GUI event dispatch.
  • GameDialogStage built-in popup slots, popup factory behavior, selected/active popup index and tray-popup related state.
  • Game-dialog lifecycle hooks, including XML initialization, activation, update, back/default action and popup close paths.
  • Adu GUI object hierarchy, including active/update/visible flags, timers, child traversal and recursive processing.
  • Adu dialog/control layouts, including object arrays, command IDs, control names, control types, state sets and basic runtime flags.
  • XML GUI binding through GuiBind arrays, command IDs and control type IDs.
  • Default GUI callback routing through AduGuiDialog callback/user-data fields.
  • Confirmed event dispatch for buttons, radio buttons, edit boxes, context menus and related GUI events.
  • Confirmed edit-box text access through the wide-character text pointer at the current AduGuiEditBox layout.
  • Group-style control activation behavior used by ranking and popup XML, especially active/visible state synchronization.

Stability Notes

Offsets and function addresses are version specific to the currently analyzed Fantasy Tennis client build. Names describe current understanding, not original source names. Any field marked unknown, maybe or conservative should be treated as subject to change until confirmed by call sites, runtime tests or additional disassembly.

Important caveat: custom GUI work should not add another stage. The client uses a fixed set of 24 stage owners. Custom UI should attach to the current stage owner, an existing popup path or the embedded modal/input owner where appropriate.

Current SDK Header

#pragma once

#include <cstdint>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <cwchar>
#include <string>
#include <type_traits>

#define FTSDK_THISCALL __thiscall
#define FTSDK_CDECL    __cdecl
#define FTSDK_STDCALL  __stdcall
#define FTSDK_FASTCALL __fastcall

namespace FTSDK {

    namespace Structs {
        struct FTStringA {
            uint32_t unknown_00;

            union {
                char inlineBuffer[16];
                char* heapPtr;
            };

            uint32_t length;
            uint32_t capacity;

            bool IsInline() const {
                return capacity < 16;
            }

            bool IsValid() const {
				if (length > capacity || length > 4096)
                    return false;

                if (capacity > 0xFFFFFFFEu)
					return false;

                if (!IsInline() && !heapPtr)
                    return false;

                return true;
            }

            const char* Data() const {
                if (!IsValid())
                    return "";

                return IsInline() ? inlineBuffer : heapPtr;
            }

            int32_t Length() const {
                return IsValid() ? static_cast<int32_t>(length) : 0;
            }

            bool Equals(const char* text) const {
                if (!text || !IsValid())
                    return false;

                const size_t textLen = std::strlen(text);
                return textLen == length && std::memcmp(Data(), text, length) == 0;
            }
        };

        struct FTStringW {
            uint32_t unknown_00;

            union {
                wchar_t inlineBuffer[8];
                wchar_t* heapPtr;
            };

			uint32_t length;
            uint32_t capacity;

            bool IsInline() const {
                return capacity < 8;
			}

            bool IsValid() const {
				if (length > capacity || length > 4096)
                    return false;

                if (capacity > 0x7FFFFFFEu)
                    return false;

                if (!IsInline() && !heapPtr)
                    return false;

                return true;
            }

            const wchar_t* Data() const {
                if (!IsValid())
                    return L"";
                return IsInline() ? inlineBuffer : heapPtr;
			}

            int32_t Length() const {
                return IsValid() ? static_cast<int32_t>(length) : 0;
            }

            bool Equals(const wchar_t* text) const {
                if (!text || !IsValid())
                    return false;
                const size_t textLen = std::wcslen(text);
                return textLen == length && std::memcmp(Data(), text, length * sizeof(wchar_t)) == 0;
			}
        };

        template <typename T>
        struct FTVector {
            uint32_t unknown_00;
			T* first;
			T* last;
            T* end;

            int32_t Size() const {
                if (!first || !last || last < first)
                    return 0;

                return static_cast<int32_t>(last - first);
			}

            int32_t Capacity() const {
                if (!first || !end || end < first)
                    return 0;

                return static_cast<int32_t>(end - first);
            }

            bool IsEmpty() const {
				return Size() == 0;
			}

            T& operator[](size_t index) {
                return first[index];
            }

            const T& operator[](size_t index) const {
                return first[index];
            }

            T* Begin() {
                return first;
			}

            T* End() {
                return last;
            }

            const T* Begin() const {
                return first;
			}

            const T* End() const {
                return last;
            }
        };

        template <typename T>
        struct FTList {
            uint32_t unknown_00;
			T* next;
			uint32_t count;
        };

        struct GuiBind {
            int32_t commandId;
            const char* controlName;
            int32_t controlTypeId;
        };
        
        struct Rect {
            int32_t left;
            int32_t top;
            int32_t right;
            int32_t bottom;
        };

        struct GuiRectStorage {
            void* vtable;
            int32_t left;
            int32_t top;
            int32_t right;
            int32_t bottom;
        };


        struct StageOwnedResource {
            uint8_t unknown00[0x1C];
            void* object;
        };

        struct StageOverlayVertex {
            float x;
            float y;
            float unknown08;
            float rhwOrOne;
            uint32_t color;
            float unknown14;
            float unknown18;
        };

        struct StageOverlayQuad {
            StageOverlayVertex vertices[4];

            int32_t unknown70;
            int32_t unknown74;
            int32_t widthInt;
            int32_t heightInt;

            float x;
            float y;
            float width;
            float height;

            float scaleX;
            float alpha;
            float rotationOrAngle;

            uint8_t unknown9C;
            uint8_t unknown9D;
            uint8_t visibleOrEnabled;
            uint8_t pad9F;

            uint8_t unknownA0[0x0C];
        };

        static_assert(sizeof(Rect) == 0x10);
        static_assert(sizeof(GuiRectStorage) == 0x14);
        static_assert(sizeof(StageOwnedResource) == 0x20);
        static_assert(sizeof(FTStringA) == 0x1C);
		static_assert(sizeof(FTStringW) == 0x1C);
		static_assert(sizeof(GuiBind) == 0x0C);
		static_assert(sizeof(FTVector<void*>) == 0x10);
        static_assert(sizeof(StageOverlayQuad) == 0xAC);
        static_assert(sizeof(StageOverlayVertex) == 0x1C);
        static_assert(sizeof(FTList<void*>) == 0x0C);
    }

    enum GuiEventType : int32_t {
        ButtonClick = 0,
        ButtonOver = 1,

        ComboBoxOpen = 2,
        ComboBoxClose = 3,
        ComboBoxSelectChange = 4,

        RadioButtonChange = 5,
        CheckBoxChange = 6,

        SliderValueChange = 7,

        EditBoxEnter = 8,
        EditBoxChange = 9,
        EditBoxTAB = 10,
        EditBoxESC = 11,
        EditBoxCharLimit = 12,
        EditBoxKeyUp = 13,
        EditBoxKeyDown = 14,

        ListBoxDBClick = 15,
        ListBoxSelect = 16,
        ListBoxSelectEnd = 17,

        ContextMenuClick = 18,
        ScrollBarPosChange = 19
    };

    enum GuiControlType : int32_t {
        Static = 0,
        Button = 1,
        CheckBox = 2,
        RadioButton = 3,
        ComboBox = 4,
        Slider = 5,
        Gauge = 6,
        EditBox = 7,
        IMEEditBox = 8,
        ListBox = 9,
        ScrollBar = 10,
        ContextMenu = 11
    };

    using GuiCallbackFn = void(FTSDK_STDCALL*)(
        GuiEventType eventType,
        int commandId,
        int param,
        void* userData
        );

    struct AduGuiStateSet;

    namespace Constants {
        // Confirmed by StageManager InitStages / SwitchStateNow bounds.
        // Do not add a custom 25th stage; attach custom UI to an existing owner/modal
        constexpr int32_t StageOwnerCount = 24;
        // These are built-in popup owner slots at FTGameDialogOwner + 0x1E8
        // Not proof that every stage uses all 10 slots
        constexpr int32_t KnownBuiltinPopupSlotCount = 10;
    }

    class AduBase {
    public:
        virtual AduBase* FTSDK_THISCALL Destroy(uint32_t flags) = 0;
    };

    class AduGameObj : public AduBase {
    public:
        virtual AduGameObj* FTSDK_THISCALL Destroy(uint32_t flags) = 0;
        virtual bool FTSDK_THISCALL SetField08AndMark(int32_t value) = 0;
        virtual int32_t FTSDK_THISCALL ResetTimerRecursive() = 0;
        virtual bool FTSDK_THISCALL SetActive(bool value) = 0;
        virtual bool FTSDK_THISCALL SetUpdateBlocked(bool value) = 0;
        virtual bool FTSDK_THISCALL SetVisible(bool value) = 0;
        virtual bool FTSDK_THISCALL UpdateRecursive(float dt) = 0;
        virtual int32_t FTSDK_THISCALL ProcessVisibleRecursive() = 0;

    public:
        void* VTable() const {
            return *reinterpret_cast<void* const*>(this);
        }

        int32_t Field04() const {
            return Read<int32_t>(0x04);
        }

        int32_t Field08() const {
            return Read<int32_t>(0x08);
        }

        AduGameObj* FirstChild() const {
            return Read<AduGameObj*>(0x0C);
        }

        AduGameObj* NextSibling() const {
            return Read<AduGameObj*>(0x10);
        }

        bool IsMarked() const {
            return ReadBool(0x14);
        }

        bool IsActive() const {
            return ReadBool(0x15);
        }

        bool IsUpdateBlocked() const {
            return ReadBool(0x16);
        }

        bool IsVisible() const {
            return ReadBool(0x17);
        }

        float ElapsedOrTime() const {
            return Read<float>(0x18);
        }

        float LastDelta() const {
            return Read<float>(0x1C);
        }

        template <typename Callback>
        void ForEachChild(Callback&& callback) {
            for (AduGameObj* child = FirstChild(); child; child = child->NextSibling())
                callback(child);
        }

        template <typename Callback>
        void ForEachChild(Callback&& callback) const {
            for (AduGameObj* child = FirstChild(); child; child = child->NextSibling())
                callback(child);
        }

    protected:
        template <typename T>
        T Read(uintptr_t offset) const {
            return *reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(this) + offset);
        }

        template <typename T>
        void Write(uintptr_t offset, T value) {
            *reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(this) + offset) = value;
        }

        bool ReadBool(uintptr_t offset) const {
            return Read<uint8_t>(offset) != 0;
        }
    };

    class AduGuiControl : public AduGameObj {
    public:
        AduGuiStateSet** StateSets() const {
            return Read<AduGuiStateSet**>(0x20);
        }

        int32_t StateCount() const {
            return Read<int32_t>(0x24);
        }

        int32_t StateCapacityMaybe() const {
            return Read<int32_t>(0x28);
        }

        const Structs::FTStringA& Name() const {
            return *reinterpret_cast<const Structs::FTStringA*>(
                reinterpret_cast<uintptr_t>(this) + 0x2C
                );
        }

        int32_t CommandId() const {
            return Read<int32_t>(0x48);
        }

        uint32_t Field4C() const {
            return Read<uint32_t>(0x4C);
        }

        int32_t ControlTypeId() const {
            return Read<int32_t>(0x50);
        }

        bool IsControlEnabledFlag() const {
            return Read<uint8_t>(0x5C) != 0;
        }

        bool IsHoverOrState3Flag() const {
            return Read<uint8_t>(0x5D) != 0;
        }

        const Structs::Rect& Rect() const {
            return *reinterpret_cast<const Structs::Rect*>(
                reinterpret_cast<uintptr_t>(this) + 0x60
                );
        }

        int32_t CurrentVisualState() const {
            return Read<int32_t>(0x70);
        }

        int32_t DefaultStateIndex() const {
            return Read<int32_t>(0x74);
        }

        bool IsDebugFlag() const {
            return Read<uint8_t>(0x79) != 0;
        }

        bool IsOverFlag() const {
            return Read<uint8_t>(0x7A) != 0;
        }

        bool IsPressedFlag() const {
            return Read<uint8_t>(0x7B) != 0;
        }

        int32_t X() const {
            return Read<int32_t>(0x80);
        }

        int32_t Y() const {
            return Read<int32_t>(0x84);
        }

        int32_t Width() const {
            return Read<int32_t>(0x88);
        }

        int32_t Height() const {
            return Read<int32_t>(0x8C);
        }

        float ControlTimer() const {
            return Read<float>(0x18);
        }
    };

    class AduGuiEditBox : public AduGuiControl {
    public:
        const wchar_t* TextW() const {
            const wchar_t* text = Read<const wchar_t*>(0x218);
            return text ? text : L"";
        }

        std::size_t TextLength() const {
            return std::wcslen(TextW());
        }

        uint32_t TextField21C() const {
            return Read<uint32_t>(0x21C);
        }

        void DumpText() const {
            std::printf(
                "[EditBox] this=%p textPtr=%p strlen=%u field21C=%u text=\"%ls\"\n",
                this,
                TextW(),
                static_cast<unsigned>(TextLength()),
                TextField21C(),
                TextW()
            );
        }
    };

    class AduGuiDialog : public AduGameObj {
    public:
        AduGameObj** ObjectArray() const {
            return Read<AduGameObj**>(0x8C);
        }

        int32_t ObjectCount() const {
            return Read<int32_t>(0x90);
        }

        AduGameObj* ObjectAt(int32_t index) const {
            auto arr = ObjectArray();

            if (!arr || index < 0 || index >= ObjectCount())
                return nullptr;

            return arr[index];
        }

        AduGuiControl* ControlAt(int32_t index) const {
            return reinterpret_cast<AduGuiControl*>(ObjectAt(index));
        }

        AduGuiControl* FindControlByCommandId(int32_t commandId) const {
            const int32_t count = ObjectCount();

            for (int32_t i = 0; i < count; ++i) {
                auto* control = ControlAt(i);
                if (!control)
                    continue;

                if (control->CommandId() == commandId)
                    return control;
            }

            return nullptr;
        }

        AduGuiControl* FindControlByName(const char* name) const {
            if (!name)
                return nullptr;

            const int32_t count = ObjectCount();

            for (int32_t i = 0; i < count; ++i) {
                auto* control = ControlAt(i);
                if (!control)
                    continue;

                if (control->Name().Equals(name))
                    return control;
            }

            return nullptr;
        }

        void SetCallback(GuiCallbackFn callback, void* userData) {
            Write<GuiCallbackFn>(27 * 4, callback);
            Write<void*>(28 * 4, userData);
        }

        GuiCallbackFn Callback() const {
            return Read<GuiCallbackFn>(27 * 4);
        }

        void* CallbackUserData() const {
            return Read<void*>(28 * 4);
        }
    };

    class StageManager;

    class ScreenRoot {
    public:
        virtual void FTSDK_THISCALL Reserved00() = 0;
        virtual void FTSDK_THISCALL Reserved04() = 0;
        virtual void FTSDK_THISCALL Reserved08() = 0;
        virtual void FTSDK_THISCALL Reserved0C() = 0;
        virtual void FTSDK_THISCALL Reserved10() = 0;
        virtual void FTSDK_THISCALL Reserved14() = 0;
        virtual void FTSDK_THISCALL Reserved18() = 0;
        virtual void FTSDK_THISCALL Reserved1C() = 0;
        virtual void FTSDK_THISCALL Reserved20() = 0;
        virtual void FTSDK_THISCALL Reserved24() = 0;
        virtual void FTSDK_THISCALL Reserved28() = 0;

        virtual int FTSDK_THISCALL OnFocusEnter() = 0;
        virtual int FTSDK_THISCALL OnFocusLeave() = 0;
        virtual void* FTSDK_THISCALL Destroy(uint32_t flags) = 0;
        virtual void FTSDK_THISCALL AttachRaw(ScreenRoot* parentOrManager, int32_t rectVtableOrTemp, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t id) = 0;
        virtual int FTSDK_THISCALL Shutdown() = 0;
        virtual int FTSDK_THISCALL Update() = 0;
        virtual int FTSDK_THISCALL OffsetRect(int32_t dx, int32_t dy) = 0;
        virtual void FTSDK_THISCALL CenterRectInParent() = 0;
        virtual ScreenRoot* FTSDK_THISCALL ActivateProcessing() = 0;
        virtual void FTSDK_THISCALL DeactivateProcessing() = 0;

        virtual void FTSDK_THISCALL OnStageCommand(int commandId) = 0;
        virtual void FTSDK_THISCALL Reserved58() = 0;
        virtual void FTSDK_THISCALL Reserved5C() = 0;
        virtual void FTSDK_THISCALL Reserved60() = 0;
        virtual void FTSDK_THISCALL Reserved64() = 0;
        virtual void FTSDK_THISCALL Reserved68() = 0;
        virtual void FTSDK_THISCALL Reserved6C() = 0;
        virtual void FTSDK_THISCALL Reserved70() = 0;
        virtual void FTSDK_THISCALL Reserved74() = 0;
        virtual void FTSDK_THISCALL Reserved78() = 0;
        virtual void FTSDK_THISCALL Reserved7C() = 0;
        virtual void FTSDK_THISCALL Reserved80() = 0;

        virtual void FTSDK_THISCALL SetOpenFlag() = 0;
        virtual void FTSDK_THISCALL ClearOpenFlag() = 0;
        virtual void FTSDK_STDCALL RectCleanupThunk(int32_t a1, uint8_t rectObj, int32_t a3, int32_t a4) = 0;
        virtual void FTSDK_THISCALL HandleSystemEvent(int32_t eventType, void* data, int32_t param) = 0;
        virtual void FTSDK_STDCALL RectCleanupThunk2(uint8_t rectObj, int32_t a2, int32_t a3) = 0;
        virtual bool FTSDK_THISCALL HandleInput(uint32_t msg, int32_t wParam, int32_t lParam) = 0;

    public:
        int32_t id;
        float field08;
        float field0C;

        uint8_t unknown10[0x80];

        Structs::GuiRectStorage rect;

        Structs::FTVector<ScreenRoot*> children;
        ScreenRoot* parent;

        int32_t parentChainEnabled;
        int32_t processEnabled;
        int32_t openFlag;
        int32_t rectOffsetEnabled;

        uint8_t unknownC8[0x70];
        char resourceName[0x80];
        void* lazyResource;
        uint8_t lazyResourceAllowed;
        uint8_t pad1BD[3];
    };
	static_assert(sizeof(ScreenRoot) == 0x1C0);

    class ScreenInputRoot : public ScreenRoot {
    public:
        uint8_t unknown1C0[0x0C];
        
        ScreenRoot* rootFocusOwner;
        ScreenRoot* inputFocusOwner;
        ScreenRoot* previousFocusOwner;
        ScreenRoot* activeFocusOwner;
        
        uint8_t unknown1DC[0x08];

        int32_t cursorX;
		int32_t cursorY;

        uint8_t inputState[0x26C];

        ScreenRoot* hoverOrCapturedOwner;

        char resourceBasePath[0x104];
    };
	static_assert(sizeof(ScreenInputRoot) == 0x560);

    class StageBase : public ScreenRoot {
    public:
		virtual void FTSDK_THISCALL AttachOwnerWithDefaultRect(ScreenRoot* parent, int32_t id) = 0;
		virtual int FTSDK_THISCALL InitDialogWithParentOwner(StageBase* parent) = 0;
		virtual bool FTSDK_THISCALL LoadXml(const char* xmlName, const Structs::GuiBind* binds, int32_t bindCount, GuiCallbackFn callback, bool reload) = 0;
		virtual int FTSDK_THISCALL OnActivate() = 0;
		virtual int FTSDK_THISCALL OnDeactivate() = 0;
		virtual int FTSDK_THISCALL OnUpdate() = 0;
		virtual bool FTSDK_THISCALL ProcessChildOwners(float dt) = 0;
		virtual int FTSDK_THISCALL HandleStagePacket(void* packet) = 0;
		virtual int FTSDK_THISCALL HandleGuiEvent(GuiEventType eventType, int commandId, int param) = 0;
		virtual int FTSDK_THISCALL OnCommandAction(int commandId) = 0;
		virtual int FTSDK_THISCALL OnEditBoxEnter(int commandId) = 0;
		virtual int FTSDK_THISCALL OnEditBoxChange(int commandId) = 0;
		virtual int FTSDK_THISCALL OnEditBoxTab(int commandId) = 0;
		virtual int FTSDK_THISCALL OnEditBoxEsc(int commandId) = 0;
		virtual int FTSDK_THISCALL OnEditBoxKeyUp(int commandId) = 0;
		virtual int FTSDK_THISCALL OnEditBoxKeyDown(int commandId) = 0;
        virtual int FTSDK_THISCALL OnContextMenuClick(int commandId, int selectedValue, AduGuiControl* contextMenuControl, int selectedData) = 0;
    public:
        StageBase* priorityInputChildOwner;
        uint8_t inputConsumedFlag;
        uint8_t padOrUnknown1C5[3];
        AduGuiDialog* xmlDialog;

		Structs::FTList<void> internalOwnerList;
        Structs::FTVector<StageBase*> processingChildOwners;

    public:
        bool InputConsumedFlag() const {
            return inputConsumedFlag != 0;
        }

        AduGuiDialog* XmlDialog() const {
            return xmlDialog;
        }

        bool HasXmlDialog() const {
            return xmlDialog != nullptr;
        }

        StageBase* PriorityInputChildOwner() const {
            return priorityInputChildOwner;
        }

        Structs::FTVector<StageBase*>& ProcessingChildOwners() {
            return processingChildOwners;
        }

        const Structs::FTVector<StageBase*>& ProcessingChildOwners() const {
            return processingChildOwners;
        }

        void SetPriorityInputChildOwner(StageBase* child) {
            priorityInputChildOwner = child;
		}

        void ClearPriorityInputChildOwnerIf(StageBase* child) {
            if (PriorityInputChildOwner() == child)
                SetPriorityInputChildOwner(nullptr);
		}

        using AddChildOwnerFn = int(FTSDK_THISCALL*)(StageBase* self, StageBase* child);
        void AddChildOwner(StageBase* child) {
            reinterpret_cast<AddChildOwnerFn>(0x005AA720)(this, child);
        }

        using BringChildOwnerToFrontFn = int(FTSDK_THISCALL*)(StageBase* self, StageBase* child);
        void BringChildOwnerToFront(StageBase* child) {
            reinterpret_cast<BringChildOwnerToFrontFn>(0x005AA780)(this, child);
        }

        using IsOwnerReachableFn = int(FTSDK_FASTCALL*)(StageBase* self);
        bool IsReachableThroughParentChain() const {
            return reinterpret_cast<IsOwnerReachableFn>(0x005A99E0)(const_cast<StageBase*>(this)) != 0;
        }

        using GetControlByCommandIdFn = AduGuiControl* (FTSDK_THISCALL*)(StageBase* self, int32_t commandId, int32_t expectedType);
        AduGuiControl* GetControlByCommandId(int32_t commandId, int32_t expectedType = -1) {
            return reinterpret_cast<GetControlByCommandIdFn>(0x005AE7D0)(this, commandId, expectedType);
        }

        using GetControlInnerObjectFn = void* (FTSDK_THISCALL*)(StageBase* self, int32_t commandId);
        void* GetControlInnerObject(int32_t commandId) {
            return reinterpret_cast<GetControlInnerObjectFn>(0x005AE840)(this, commandId);
        }
    };

    class GameDialogStage : public StageBase {
    public:
        virtual void FTSDK_THISCALL AttachOwnerAndCreateUserNotice(ScreenRoot* parent, int32_t id) = 0;
        virtual int FTSDK_THISCALL InitStageXml() = 0;
        virtual int FTSDK_THISCALL SetSelectedOrActivePopupIndex(int32_t index) = 0;
        virtual int FTSDK_THISCALL CanLeaveStageOrPopup(int32_t reason) = 0;
        virtual void FTSDK_THISCALL ReservedF0() = 0;
        virtual int FTSDK_THISCALL OnBackOrDefaultAction() = 0;

    public:
        StageBase* builtinPopupOwners[10];
        uint8_t xmlLoadedFlag;
        uint8_t pad211[3];

        int32_t selectedOrActivePopupIndex;
        void* trayPopupOwner;

    public:
        bool IsXmlLoaded() const {
            return xmlLoadedFlag != 0;
        }

        StageBase* BuiltinPopupOwnerAt(int32_t index) const {
            if (index < 0 || index >= 10)
                return nullptr;

            return builtinPopupOwners[index];
        }
        
        using GetOrCreateBuiltinPopupOwnerFn = StageBase * (FTSDK_THISCALL*)(StageBase* self, uint32_t popupIndex);
        StageBase* GetOrCreateBuiltinPopupOwner(uint32_t popupIndex) {
            if (popupIndex >= Constants::KnownBuiltinPopupSlotCount)
                return nullptr;

            return reinterpret_cast<GetOrCreateBuiltinPopupOwnerFn>(0x00498890)(this, popupIndex);
        }
    };

    class StageManager : public ScreenInputRoot {
    public:
        void* hwnd;
        int32_t previousStageId;
        int32_t pendingStageId;
        int32_t currentStageId;
        int32_t transitionStageScratchId;

        GameDialogStage* currentStage;
        uint8_t embeddedDialogStageStorage[0x21C];

        Structs::FTVector<GameDialogStage*> stages;

        void* currentOverlayRaw;
        Structs::StageOverlayQuad overlayQuad;

        float overlayTimer;

        Structs::StageOwnedResource ownedEffectResource;

        int32_t frameUpdating;

        uint8_t unknown87C[0x10];

        uint8_t delayedReconnectFlag;

        uint8_t pad88D[3];

        float periodicTimer;
        uint8_t hourlyPacketReadyFlag;

        uint8_t pad895[3];

        float hourlyPacketTimer;
        int32_t unknown89C;

        GameDialogStage* EmbeddedDialogStage() {
            return reinterpret_cast<GameDialogStage*>(embeddedDialogStageStorage);
        }

        const GameDialogStage* EmbeddedDialogStage() const {
            return reinterpret_cast<const GameDialogStage*>(embeddedDialogStageStorage);
        }

        GameDialogStage* CurrentStage() const {
            if (currentStage)
                return currentStage;

			if (currentStageId < 0 || currentStageId >= stages.Size())
				return nullptr;

			return stages[currentStageId];
        }
        
        bool IsValidStageIndex(uint32_t index) const {
            return index < Constants::StageOwnerCount;
        }

        using RequestStateFn = void(FTSDK_THISCALL*)(
            StageManager* self,
            uint32_t state
            );

        void RequestState(uint32_t state) {
            if (!IsValidStageIndex(state))
                return;

            reinterpret_cast<RequestStateFn>(0x004AE9A0)(this, state);
        }

        using SwitchStateNowFn = void(FTSDK_THISCALL*)(
            StageManager* self,
            uint32_t state
            );

        void SwitchStateNow(uint32_t state) {
            if (!IsValidStageIndex(state))
                return;

            reinterpret_cast<SwitchStateNowFn>(0x004AD7D0)(this, state);
        }

        using InitStagesFn = bool(FTSDK_THISCALL*)(
            StageManager* self,
            void* hwnd,
            int startupParam
            );

        bool InitStages(void* hwnd, int startupParam) {
            return reinterpret_cast<InitStagesFn>(0x004B0590)(this, hwnd, startupParam);
        }
    };

    using GetStageManagerFn = StageManager* (FTSDK_CDECL*)();
    inline StageManager* GetStageManager() {
        return reinterpret_cast<GetStageManagerFn>(0x004B0520)();
    }

    using ConstructGuiOwnerBaseFn = StageBase* (FTSDK_THISCALL*)(StageBase* self);
    inline StageBase* ConstructStageBase(StageBase* memory) {
        return reinterpret_cast<ConstructGuiOwnerBaseFn>(0x005AEC40)(memory);
    }
     
    using ConstructGameDialogStageFn = GameDialogStage* (FTSDK_THISCALL*)(GameDialogStage* self);
    inline GameDialogStage* ConstructGameDialogStage(GameDialogStage* memory) {
        return reinterpret_cast<ConstructGameDialogStageFn>(0x004987C0)(memory);
    }
}

Example Usage