#pragma once #include #include #include #include #include #include #include #include #include #include "KingSystem/GameData/gdtTriggerParam.h" #include "KingSystem/Resource/resHandle.h" #include "KingSystem/System/KingEditor.h" #include "KingSystem/Utils/Byaml/Byaml.h" #include "KingSystem/Utils/Types.h" namespace sead { class Framework; class MethodTreeMgr; } // namespace sead namespace ksys::gdt { namespace detail { template struct SetterTraits { static constexpr bool isVectorType() { return std::is_same_v || std::is_same_v || std::is_same_v; } static constexpr bool isStringType() { return std::is_same_v; } static constexpr bool isInlineType() { return std::is_same_v || std::is_same_v || std::is_same_v || isStringType(); } using ArgType = std::conditional_t; using WrapperArgType = std::conditional_t; using NoCheckForceArgType = T; static ArgType convertValue(WrapperArgType v) { if constexpr (isStringType()) return v.cstr(); else return v; } }; } // namespace detail enum class FlagHandle : u32 {}; constexpr FlagHandle InvalidHandle = FlagHandle(-1); class TriggerParamRef { public: TriggerParamRef(TriggerParam** param_1, TriggerParam** param, bool check_permissions, bool propagate_param_1_changes, bool change_only_once) : mParam1(param_1), mParam(param), mCheckPermissions(check_permissions), mPropagateParam1Changes(propagate_param_1_changes), mChangeOnlyOnce(change_only_once) {} virtual ~TriggerParamRef() = default; class Proxy { public: TriggerParam* getBuffer() const { return mUseParam1 ? *mRef.mParam1 : *mRef.mParam; } TriggerParam* getBuffer0() const { return *mRef.mParam; } TriggerParam* getBuffer1() const { return *mRef.mParam1; } // region Value getters #define PROXY_GET_SET_IMPL_(GET_NAME, SET_NAME, TYPE) \ bool GET_NAME(TYPE* value, s32 index) const { \ return getBuffer()->GET_NAME(value, index, mRef.mCheckPermissions); \ } \ bool GET_NAME(TYPE* value, s32 array_index, s32 index) const { \ return getBuffer()->GET_NAME(value, array_index, index, mRef.mCheckPermissions); \ } \ bool GET_NAME(TYPE* value, const sead::SafeString& name) const { \ return getBuffer()->GET_NAME(value, name, mRef.mCheckPermissions); \ } \ bool GET_NAME(TYPE* value, const sead::SafeString& array_name, s32 index) const { \ return getBuffer()->GET_NAME(value, array_name, index, mRef.mCheckPermissions); \ } \ \ bool SET_NAME(TYPE const& value, s32 idx, bool bypass_one_trigger_check = false) { \ if (mRef.mChangeOnlyOnce) \ return false; \ if (!getBuffer1()->SET_NAME(value, idx, mRef.mCheckPermissions, bypass_one_trigger_check)) \ return false; \ if (mRef.mPropagateParam1Changes) \ getBuffer0()->SET_NAME(value, idx, mRef.mCheckPermissions, bypass_one_trigger_check); \ return true; \ } \ bool SET_NAME(TYPE const& value, s32 array_idx, s32 idx, \ bool bypass_one_trigger_check = false) { \ if (mRef.mChangeOnlyOnce) \ return false; \ if (!getBuffer1()->SET_NAME(value, array_idx, idx, mRef.mCheckPermissions, \ bypass_one_trigger_check)) \ return false; \ if (mRef.mPropagateParam1Changes) \ getBuffer0()->SET_NAME(value, array_idx, idx, mRef.mCheckPermissions, \ bypass_one_trigger_check); \ return true; \ } \ bool SET_NAME(TYPE const& value, const sead::SafeString& name, \ bool bypass_one_trigger_check = false) { \ if (mRef.mChangeOnlyOnce) \ return false; \ if (!getBuffer1()->SET_NAME(value, name, mRef.mCheckPermissions, true, \ bypass_one_trigger_check)) \ return false; \ if (mRef.mPropagateParam1Changes) \ getBuffer0()->SET_NAME(value, name, mRef.mCheckPermissions, true, \ bypass_one_trigger_check); \ return true; \ } \ bool SET_NAME(TYPE const& value, const sead::SafeString& name, s32 idx, \ bool bypass_one_trigger_check = false) { \ if (mRef.mChangeOnlyOnce) \ return false; \ if (!getBuffer1()->SET_NAME(value, name, idx, mRef.mCheckPermissions, true, \ bypass_one_trigger_check)) \ return false; \ if (mRef.mPropagateParam1Changes) \ getBuffer0()->SET_NAME(value, name, idx, mRef.mCheckPermissions, true, \ bypass_one_trigger_check); \ return true; \ } PROXY_GET_SET_IMPL_(getBool, setBool, bool) PROXY_GET_SET_IMPL_(getS32, setS32, s32) PROXY_GET_SET_IMPL_(getF32, setF32, f32) PROXY_GET_SET_IMPL_(getStr, setStr, char const*) PROXY_GET_SET_IMPL_(getStr64, setStr64, char const*) PROXY_GET_SET_IMPL_(getStr256, setStr256, char const*) PROXY_GET_SET_IMPL_(getVec2f, setVec2f, sead::Vector2f) PROXY_GET_SET_IMPL_(getVec3f, setVec3f, sead::Vector3f) PROXY_GET_SET_IMPL_(getVec4f, setVec4f, sead::Vector4f) #undef PROXY_GET_SET_IMPL_ #define PROXY_RESET_IMPL_(NAME) \ bool NAME(s32 idx) { return getBuffer1()->NAME(idx, mRef.mCheckPermissions); } \ bool NAME(s32 idx, s32 sub_idx) { \ return getBuffer1()->NAME(idx, sub_idx, mRef.mCheckPermissions); \ } \ bool NAME(const sead::SafeString& name) { \ return getBuffer1()->NAME(name, mRef.mCheckPermissions); \ } \ bool NAME(const sead::SafeString& name, s32 sub_idx) { \ return getBuffer1()->NAME(name, sub_idx, mRef.mCheckPermissions); \ } PROXY_RESET_IMPL_(resetBool) PROXY_RESET_IMPL_(resetS32) PROXY_RESET_IMPL_(resetF32) PROXY_RESET_IMPL_(resetStr) PROXY_RESET_IMPL_(resetStr64) PROXY_RESET_IMPL_(resetStr256) PROXY_RESET_IMPL_(resetVec2f) PROXY_RESET_IMPL_(resetVec3f) PROXY_RESET_IMPL_(resetVec4f) #undef PROXY_RESET_IMPL_ private: friend class TriggerParamRef; Proxy(const TriggerParamRef& ref, bool param1) : mUseParam1(param1), mRef(ref) {} bool mUseParam1; const TriggerParamRef& mRef; }; Proxy get() const { return Proxy(*this, false); } Proxy get1() const { return Proxy(*this, true); } void setBuffers(TriggerParam** param1, TriggerParam** param) { mParam1 = param1; mParam = param; } bool shouldCheckPermissions() const { return mCheckPermissions; } bool shouldPropagateParam1Changes() const { return mPropagateParam1Changes; } bool shouldChangeOnlyOnce() const { return mChangeOnlyOnce; } void setCheckPermissions(bool on) { mCheckPermissions = on; } void setPropagateParam1Changes(bool on) { mPropagateParam1Changes = on; } void setChangeOnlyOnce(bool on) { mChangeOnlyOnce = on; } private: TriggerParam** mParam1; TriggerParam** mParam; bool mCheckPermissions; bool mPropagateParam1Changes; bool mChangeOnlyOnce; }; KSYS_CHECK_SIZE_NX150(TriggerParamRef, 0x20); class IManager { public: virtual ~IManager() = 0; }; inline IManager::~IManager() = default; class Manager : public IManager, public KingEditorComponent { SEAD_SINGLETON_DISPOSER(Manager) Manager(); ~Manager() override; const char* getName() const override { return "GameData"; } void syncData(const char* data) override; public: struct ResetEvent { virtual ~ResetEvent() = default; TriggerParam* param = nullptr; }; struct ReinitEvent { virtual ~ReinitEvent() = default; }; using ResetSignal = sead::DelegateEvent; using ReinitSignal = sead::DelegateEvent; sead::Heap* getGameDataHeap() const { return mGameDataHeap; } sead::Heap* getSaveAreaHeap() const { return mSaveAreaHeap; } sead::Heap* getGameDataComHeap() const { return mGameDataComHeap; } TriggerParamRef& getParam() { return mParam; } TriggerParamRef& getParamBypassPerm() { return mParamBypassPerm; } #define GDT_GET_HANDLE_(NAME, GET_IDX_NAME) \ FlagHandle NAME(const sead::SafeString& name) const { \ const auto prefix = mCurrentFlagHandlePrefix; \ const auto hash = sead::HashCRC32::calcStringHash(name); \ return makeFlagHandle(prefix, mParam.get1().getBuffer()->GET_IDX_NAME(hash)); \ } GDT_GET_HANDLE_(getBoolHandle, TriggerParam::getBoolIdx) GDT_GET_HANDLE_(getS32Handle, TriggerParam::getS32Idx) GDT_GET_HANDLE_(getF32Handle, TriggerParam::getF32Idx) GDT_GET_HANDLE_(getStrHandle, TriggerParam::getStrIdx) GDT_GET_HANDLE_(getStr64Handle, TriggerParam::getStr64Idx) GDT_GET_HANDLE_(getStr256Handle, TriggerParam::getStr256Idx) GDT_GET_HANDLE_(getVec2fHandle, TriggerParam::getVec2fIdx) GDT_GET_HANDLE_(getVec3fHandle, TriggerParam::getVec3fIdx) GDT_GET_HANDLE_(getVec4fHandle, TriggerParam::getVec4fIdx) GDT_GET_HANDLE_(getBoolArrayHandle, TriggerParam::getBoolArrayIdx) GDT_GET_HANDLE_(getS32ArrayHandle, TriggerParam::getS32ArrayIdx) GDT_GET_HANDLE_(getF32ArrayHandle, TriggerParam::getF32ArrayIdx) GDT_GET_HANDLE_(getStrArrayHandle, TriggerParam::getStrArrayIdx) GDT_GET_HANDLE_(getStr64ArrayHandle, TriggerParam::getStr64ArrayIdx) GDT_GET_HANDLE_(getStr256ArrayHandle, TriggerParam::getStr256ArrayIdx) GDT_GET_HANDLE_(getVec2fArrayHandle, TriggerParam::getVec2fArrayIdx) GDT_GET_HANDLE_(getVec3fArrayHandle, TriggerParam::getVec3fArrayIdx) GDT_GET_HANDLE_(getVec4fArrayHandle, TriggerParam::getVec4fArrayIdx) #undef GDT_GET_HANDLE_ #define GDT_GET_(NAME, T) \ bool NAME(FlagHandle handle, T* value, bool debug = false) { \ return unwrapHandle(handle, debug, [&](u32 idx, TriggerParamRef& ref) { \ return ref.get().NAME(value, idx); \ }); \ } \ bool NAME(FlagHandle handle, T* value, s32 sub_idx, bool debug = false) { \ return unwrapHandle(handle, debug, [&](u32 idx, TriggerParamRef& ref) { \ return ref.get().NAME(value, idx, sub_idx); \ }); \ } GDT_GET_(getBool, bool) GDT_GET_(getS32, s32) GDT_GET_(getF32, f32) GDT_GET_(getStr, char const*) GDT_GET_(getStr64, char const*) GDT_GET_(getStr256, char const*) GDT_GET_(getVec2f, sead::Vector2f) GDT_GET_(getVec3f, sead::Vector3f) GDT_GET_(getVec4f, sead::Vector4f) #undef GDT_GET_ #define GDT_SET_(NAME, TRAITS) \ /* Setters (by handle) */ \ KSYS_ALWAYS_INLINE bool NAME(TRAITS::ArgType value, FlagHandle handle, bool debug, \ bool force) { \ if (mBitFlags.isOn(BitFlag::_40000)) \ return false; \ return unwrapHandle(handle, debug, [&](u32 idx, TriggerParamRef& ref) { \ return ref.get().NAME(value, idx, force); \ }); \ } \ bool NAME(TRAITS::ArgType value, FlagHandle handle) { \ return NAME(value, handle, false, false); \ } \ bool NAME##NoCheck(TRAITS::ArgType value, FlagHandle handle) { \ return NAME(value, handle, true, false); \ } \ bool NAME##NoCheckForce(TRAITS::NoCheckForceArgType value, FlagHandle handle) { \ return NAME(value, handle, true, true); \ } \ /* Setters for arrays (by handle) */ \ KSYS_ALWAYS_INLINE bool NAME(TRAITS::ArgType value, FlagHandle handle, bool debug, bool force, \ s32 sub_idx) { \ if (mBitFlags.isOn(BitFlag::_40000)) \ return false; \ return unwrapHandle(handle, debug, [&](u32 idx, TriggerParamRef& ref) { \ return ref.get().NAME(value, idx, sub_idx, force); \ }); \ } \ bool NAME(TRAITS::ArgType value, FlagHandle handle, s32 sub_idx) { \ return NAME(value, handle, false, false, sub_idx); \ } \ bool NAME##NoCheck(TRAITS::ArgType value, FlagHandle handle, s32 sub_idx) { \ return NAME(value, handle, true, false, sub_idx); \ } \ bool NAME##NoCheckForce(TRAITS::NoCheckForceArgType value, FlagHandle handle, s32 sub_idx) { \ return NAME(value, handle, true, true, sub_idx); \ } \ /* Setters (by name) */ \ KSYS_ALWAYS_INLINE bool NAME(TRAITS::ArgType value, const sead::SafeString& name, bool debug, \ bool force) { \ if (mBitFlags.isOn(BitFlag::_40000)) \ return false; \ auto& ref = debug ? getParamBypassPerm() : getParam(); \ return ref.get().NAME(value, name, force); \ } \ [[gnu::noinline]] bool NAME(TRAITS::ArgType value, const sead::SafeString& name) { \ return NAME(value, name, false, false); \ } \ [[gnu::noinline]] bool NAME##NoCheck(TRAITS::ArgType value, const sead::SafeString& name) { \ return NAME(value, name, true, false); \ } \ [[gnu::noinline]] bool NAME##NoCheckForce(TRAITS::NoCheckForceArgType value, \ const sead::SafeString& name) { \ return NAME(value, name, true, true); \ } \ /* Setters for arrays (by name) */ \ KSYS_ALWAYS_INLINE bool NAME(TRAITS::ArgType value, const sead::SafeString& name, bool debug, \ bool force, s32 sub_idx) { \ if (mBitFlags.isOn(BitFlag::_40000)) \ return false; \ auto& ref = debug ? getParamBypassPerm() : getParam(); \ return ref.get().NAME(value, name, sub_idx, force); \ } \ [[gnu::noinline]] bool NAME(TRAITS::ArgType value, const sead::SafeString& name, \ s32 sub_idx) { \ return NAME(value, name, false, false, sub_idx); \ } \ [[gnu::noinline]] bool NAME##NoCheck(TRAITS::ArgType value, const sead::SafeString& name, \ s32 sub_idx) { \ return NAME(value, name, true, false, sub_idx); \ } \ [[gnu::noinline]] bool NAME##NoCheckForce(TRAITS::NoCheckForceArgType value, \ const sead::SafeString& name, s32 sub_idx) { \ return NAME(value, name, true, true, sub_idx); \ } \ \ bool NAME(TRAITS::WrapperArgType value, FlagHandle handle, bool debug) { \ if (debug) { \ onChangedByDebug(); \ return NAME##NoCheckForce(TRAITS::convertValue(value), handle); \ } \ return NAME(TRAITS::convertValue(value), handle); \ } \ bool NAME(TRAITS::WrapperArgType value, const sead::SafeString& name, bool debug) { \ if (debug) { \ onChangedByDebug(); \ return NAME##NoCheckForce(TRAITS::convertValue(value), name); \ } \ return NAME(TRAITS::convertValue(value), name); \ } \ inline bool NAME##Special(TRAITS::WrapperArgType value, const sead::SafeString& name, \ bool debug, bool force = false) { \ if (debug) { \ onChangedByDebug(); \ auto& ref = debug ? getParamBypassPerm() : getParam(); \ return ref.get().NAME(TRAITS::convertValue(value), name, force); \ } \ return NAME(TRAITS::convertValue(value), name); \ } \ \ bool NAME(TRAITS::WrapperArgType value, FlagHandle handle, bool debug, s32 sub_idx) { \ if (debug) { \ onChangedByDebug(); \ return NAME##NoCheckForce(TRAITS::convertValue(value), handle, sub_idx); \ } \ return NAME(TRAITS::convertValue(value), handle, sub_idx); \ } \ bool NAME(TRAITS::WrapperArgType value, const sead::SafeString& name, bool debug, \ s32 sub_idx) { \ if (debug) { \ onChangedByDebug(); \ return NAME##NoCheckForce(TRAITS::convertValue(value), name, sub_idx); \ } \ return NAME(TRAITS::convertValue(value), name, sub_idx); \ } GDT_SET_(setBool, detail::SetterTraits) GDT_SET_(setS32, detail::SetterTraits) GDT_SET_(setF32, detail::SetterTraits) GDT_SET_(setStr, detail::SetterTraits) GDT_SET_(setStr64, detail::SetterTraits) GDT_SET_(setStr256, detail::SetterTraits) GDT_SET_(setVec2f, detail::SetterTraits) GDT_SET_(setVec3f, detail::SetterTraits) GDT_SET_(setVec4f, detail::SetterTraits) #undef GDT_SET_ #define GDT_RESET_(NAME) \ KSYS_ALWAYS_INLINE bool NAME##_(FlagHandle handle, bool debug) { \ if (mBitFlags.isOn(BitFlag::_40000)) \ return false; \ return unwrapHandle( \ handle, debug, [&](u32 idx, TriggerParamRef& ref) { return ref.get().NAME(idx); }); \ } \ inline bool NAME(FlagHandle handle) { return NAME##_(handle, false); } \ inline bool NAME##NoCheck(FlagHandle handle) { return NAME##_(handle, true); } \ \ KSYS_ALWAYS_INLINE bool NAME##_(FlagHandle handle, bool debug, s32 sub_idx) { \ if (mBitFlags.isOn(BitFlag::_40000)) \ return false; \ return unwrapHandle(handle, debug, [&](u32 idx, TriggerParamRef& ref) { \ return ref.get().NAME(idx, sub_idx); \ }); \ } \ inline bool NAME(FlagHandle handle, s32 sub_idx) { return NAME##_(handle, false, sub_idx); } \ inline bool NAME##NoCheck(FlagHandle handle, s32 sub_idx) { \ return NAME##_(handle, true, sub_idx); \ } \ \ inline bool NAME(FlagHandle handle, bool debug) { \ if (debug) { \ onChangedByDebug(); \ return NAME##NoCheck(handle); \ } \ return NAME(handle); \ } \ inline bool NAME(FlagHandle handle, bool debug, s32 sub_idx) { \ if (debug) { \ onChangedByDebug(); \ return NAME##NoCheck(handle, sub_idx); \ } \ return NAME(handle, sub_idx); \ } GDT_RESET_(resetBool) GDT_RESET_(resetS32) GDT_RESET_(resetF32) GDT_RESET_(resetStr) GDT_RESET_(resetStr64) GDT_RESET_(resetStr256) GDT_RESET_(resetVec2f) GDT_RESET_(resetVec3f) GDT_RESET_(resetVec4f) #undef GDT_RESET_ void increaseS32CommonFlag(s32 value, const sead::SafeString& name, s32 sub_idx, bool debug) { if (!mIncreaseLogger) return; mIncreaseLogger->addRecord(value, name, sub_idx, debug); if (debug) onChangedByDebug(); } void init(sead::Heap* heap, sead::Framework* framework); void addReinitCallback(ReinitSignal::Slot& slot); void removeReinitCallback(ReinitSignal::Slot& slot); private: enum class BitFlag { _1 = 0x1, _2 = 0x2, _4 = 0x4, _8 = 0x8, _10 = 0x10, _20 = 0x20, _40 = 0x40, _80 = 0x80, _100 = 0x100, _200 = 0x200, _400 = 0x400, _800 = 0x800, _1000 = 0x1000, _2000 = 0x2000, _4000 = 0x4000, _8000 = 0x8000, _10000 = 0x10000, _20000 = 0x20000, _40000 = 0x40000, }; enum class ResetFlag { }; struct MethodTreeNode { virtual ~MethodTreeNode() = default; sead::MethodTreeNode node{nullptr}; sead::MethodTreeMgr* method_tree_mgr = nullptr; }; struct IncreaseLogger { struct Record { bool debug = false; u32 name_hash = 0; s32 sub_idx = -1; s32 value = 0; }; KSYS_CHECK_SIZE_NX150(Record, 0x10); void addRecord(s32 value, const sead::SafeString& name, s32 sub_idx, bool debug); u64 _0 = 0; sead::SafeArray, 3> ring_buffers[2]; sead::SafeArray arrays[2]{}; }; static FlagHandle makeFlagHandle(u32 prefix, s32 idx) { return FlagHandle(idx | (prefix << 24)); } /// Extracts a flag index out of a FlagHandle and passes it to the specified callable. /// fn must be callable with a u32 + TriggerParamRef& template KSYS_ALWAYS_INLINE bool unwrapHandle(FlagHandle handle, const Fn& fn) { const u32 idx = static_cast(handle); auto& ref = BypassPerm ? getParamBypassPerm() : getParam(); const auto check = [&] { return !Write || !ref.shouldChangeOnlyOnce(); }; if (mBitFlags.isOff(BitFlag::_8000) && handle != InvalidHandle) return check() && fn(idx, ref); return idx >> 24 == mCurrentFlagHandlePrefix && check() && fn(idx & 0xFFFFFF, ref); } /// Extracts a flag index out of a FlagHandle and passes it to the specified callable. /// fn must be callable with a u32 + TriggerParamRef& template KSYS_ALWAYS_INLINE bool unwrapHandle(FlagHandle handle, bool debug, const Fn& fn) { return debug ? unwrapHandle(handle, fn) : unwrapHandle(handle, fn); } void onChangedByDebug() { setBool(true, "IsChangedByDebug"); mBitFlags.set(BitFlag::_800); } void loadGameData(const sead::SafeString& path); void loadShopGameDataInfo(const sead::SafeString& path); void unloadResources(); sead::Heap* mGameDataHeap = nullptr; sead::Heap* mSaveAreaHeap = nullptr; sead::Heap* mGameDataComHeap = nullptr; res::Handle mGameDataArcHandle; sead::SafeArray mBgdataHandles; sead::ArchiveFileDevice mGameDataArc{nullptr}; res::Handle mShopGameDataInfoHandle; al::ByamlIter mShopAreaInfoValues; const u8* mShopAreaInfoHashes = nullptr; al::ByamlIter mShopSoldOutInfoValues; const u8* mShopSoldOutInfoHashes = nullptr; TriggerParamRef mParamBypassPerm{&mFlagBuffer1, &mFlagBuffer, false, false, false}; TriggerParamRef mParam{&mFlagBuffer1, &mFlagBuffer, true, false, false}; IncreaseLogger* mIncreaseLogger = nullptr; TriggerParam* mFlagBuffer1; TriggerParam* mFlagBuffer; TriggerParam* mRetryBuffer; TriggerParam* mGimmickResetBuffer; sead::TypedBitFlag mBitFlags; sead::TypedBitFlag mResetFlags; u32 _c20 = 0; u32 mNumFlagsToReset = 0; u32 mCurrentFlagHandlePrefix = 0; ReinitSignal mReinitSignal; ResetSignal mResetSignal; MethodTreeNode mMethodTreeNode; void* mDelegate1 = nullptr; // FIXME: figure out what this is sead::IDelegateR* mGetMapNameDelegate = nullptr; sead::FixedSafeString<64> mStr; // TODO: rename u32 mTrackerBlockSaveNumberFlagCrc32 = 0; u32 mSyncStep = 0; sead::Mutex mMutex; }; KSYS_CHECK_SIZE_NX150(Manager, 0xdc8); } // namespace ksys::gdt