diff --git a/data/uking_functions.csv b/data/uking_functions.csv index 9a041f9a..690f660a 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -74000,7 +74000,7 @@ Address,Quality,Size,Name 0x0000007100d4e310,m,000028,_ZN4ksys3map10ObjectLink14sub_7100D4E310ENS0_14MapLinkDefTypeE 0x0000007100d4e32c,O,000024,_ZN4ksys3map10ObjectLink27isPlacementLODOrForSaleLinkENS0_14MapLinkDefTypeE 0x0000007100d4e344,O,000044,_ZN4ksys3map14ObjectLinkDataC1Ev -0x0000007100d4e370,O,000136,_ZN4ksys3map14ObjectLinkData12deleteArraysEv +0x0000007100d4e370,m,000136,_ZN4ksys3map14ObjectLinkData12deleteArraysEv 0x0000007100d4e3f8,U,000556,PlacementLinkData::allocLinks 0x0000007100d4e624,O,000212,_ZN4ksys3map14ObjectLinkData16allocLinksToSelfEiPN4sead4HeapE 0x0000007100d4e6f8,U,000292,PlacementLinkData::dtor @@ -74175,71 +74175,71 @@ Address,Quality,Size,Name 0x0000007100d5af44,O,000096,_ZNK4ksys3act2ai7Queries18updateForPreDeleteEv 0x0000007100d5afa4,O,000068,_ZNK4ksys3act2ai7Queries11onPreDeleteEv 0x0000007100d5afe8,O,000304,_ZNK4ksys3act2ai7Queries8getQueryERKN4sead14SafeStringBaseIcEE -0x0000007100d5b118,U,000004,nullsub_3774 -0x0000007100d5b11c,U,000004,j__ZdlPv_850 -0x0000007100d5b120,U,000008, -0x0000007100d5b128,U,000008, -0x0000007100d5b130,U,000008, -0x0000007100d5b138,U,000008, -0x0000007100d5b140,U,000008, -0x0000007100d5b148,U,000196, -0x0000007100d5b20c,U,000052, -0x0000007100d5b240,U,000160, -0x0000007100d5b2e0,U,000156, -0x0000007100d5b37c,U,000380, -0x0000007100d5b4f8,U,000008, -0x0000007100d5b500,U,000052, -0x0000007100d5b534,U,000052, -0x0000007100d5b568,U,000032, -0x0000007100d5b588,U,000032, -0x0000007100d5b5a8,U,000032, -0x0000007100d5b5c8,U,000052, -0x0000007100d5b5fc,U,000044, -0x0000007100d5b628,U,000008, +0x0000007100d5b118,O,000004,_ZN4ksys3map9RailPointD1Ev +0x0000007100d5b11c,O,000004,_ZN4ksys3map9RailPointD0Ev +0x0000007100d5b120,O,000008,_ZNK4ksys3map9RailPoint7getIterEv +0x0000007100d5b128,O,000008,_ZNK4ksys3map9RailPoint9getRotateEv +0x0000007100d5b130,O,000008,_ZNK4ksys3map9RailPoint12getTranslateEv +0x0000007100d5b138,O,000008,_ZNK4ksys3map9RailPoint15getPrevDistanceEv +0x0000007100d5b140,O,000008,_ZNK4ksys3map9RailPoint15getNextDistanceEv +0x0000007100d5b148,O,000196,_ZN4ksys3map9RailPoint5parseEPNS0_9MubinIterEPN4sead4HeapE +0x0000007100d5b20c,m,000052,_ZN4ksys3map4RailC1Ev +0x0000007100d5b240,m,000160,_ZN4ksys3map4RailD1Ev +0x0000007100d5b2e0,m,000156,_ZN4ksys3map4RailD0Ev +0x0000007100d5b37c,O,000380,_ZN4ksys3map4Rail4initEPNS0_9MubinIterEPN4sead4HeapE +0x0000007100d5b4f8,O,000008,_ZNK4ksys3map4Rail12getNumPointsEv +0x0000007100d5b500,O,000052,_ZNK4ksys3map4Rail8getPointEi +0x0000007100d5b534,O,000052,_ZN4ksys3map4Rail8getPointEi +0x0000007100d5b568,O,000032,_ZNK4ksys3map4Rail11getPointSRTEi +0x0000007100d5b588,O,000032,_ZNK4ksys3map4Rail14getPointRotateEi +0x0000007100d5b5a8,O,000032,_ZNK4ksys3map4Rail17getPointTranslateEi +0x0000007100d5b5c8,O,000052,_ZNK4ksys3map4Rail15getControlPointEii +0x0000007100d5b5fc,O,000044,_ZNK4ksys3map4Rail13calcTranslateEf +0x0000007100d5b628,O,000008,_ZNK4ksys3map4Rail13calcTranslateEPN4sead7Vector3IfEEf 0x0000007100d5b630,U,001356, -0x0000007100d5bb7c,U,000012, -0x0000007100d5bb88,U,000012, -0x0000007100d5bb94,U,000008, -0x0000007100d5bb9c,U,000008, -0x0000007100d5bba4,U,000168, -0x0000007100d5bc4c,U,000348, +0x0000007100d5bb7c,O,000012,_ZNK4ksys3map4Rail8isClosedEv +0x0000007100d5bb88,O,000012,_ZNK4ksys3map4Rail8isBezierEv +0x0000007100d5bb94,O,000008,_ZNK4ksys3map4Rail9getHashIdEv +0x0000007100d5bb9c,O,000008,_ZNK4ksys3map4Rail13getUniqueNameEv +0x0000007100d5bba4,m,000168,_ZN4ksys3map4Rail10allocPointEPN4sead4HeapE +0x0000007100d5bc4c,O,000348,_ZN4ksys3map4Rail5parseEPNS0_9MubinIterE 0x0000007100d5bda8,U,000004,j__ZdlPv_851 -0x0000007100d5bdac,U,000228, -0x0000007100d5be90,U,000096, -0x0000007100d5bef0,U,000156, -0x0000007100d5bf8c,U,000188, +0x0000007100d5bdac,O,000228,_ZN4ksys3map14RailGuidePoint5parseEPNS0_9MubinIterEPN4sead4HeapE +0x0000007100d5be90,U,000096,_ZN4ksys3map9RailGuideC1Ev +0x0000007100d5bef0,U,000156,_ZN4ksys3map9RailGuideD1Ev +0x0000007100d5bf8c,U,000188,_ZN4ksys3map9RailGuideD0Ev 0x0000007100d5c048,U,000004,j__ZdlPv_852 -0x0000007100d5c04c,U,000260, +0x0000007100d5c04c,O,000260,_ZN4ksys3map20RailRemainGuidePoint5parseEPNS0_9MubinIterEPN4sead4HeapE 0x0000007100d5c150,U,000096, 0x0000007100d5c1b0,U,000156, 0x0000007100d5c24c,U,000188, -0x0000007100d5c308,U,000076, -0x0000007100d5c354,U,000076, -0x0000007100d5c3a0,U,000008, -0x0000007100d5c3a8,U,000008, +0x0000007100d5c308,m,000076,_ZN4ksys3map20RailConnectablePointD1Ev +0x0000007100d5c354,m,000076,_ZN4ksys3map20RailConnectablePointD0Ev +0x0000007100d5c3a0,O,000008,_ZNK4ksys3map20RailConnectablePoint15getJunctionRailEv +0x0000007100d5c3a8,O,000008,_ZNK4ksys3map20RailConnectablePoint16getJunctionPointEv 0x0000007100d5c3b0,U,000428, -0x0000007100d5c55c,U,000196, +0x0000007100d5c55c,O,000196,_ZN4ksys3map20RailConnectablePoint5parseEPNS0_9MubinIterEPN4sead4HeapE 0x0000007100d5c620,U,000096, 0x0000007100d5c680,U,000156, 0x0000007100d5c71c,U,000192, -0x0000007100d5c7dc,U,000076, -0x0000007100d5c828,U,000008, -0x0000007100d5c830,U,000252, -0x0000007100d5c92c,U,000116, -0x0000007100d5c9a0,U,000012, -0x0000007100d5c9ac,U,000012, -0x0000007100d5c9b8,U,000012, -0x0000007100d5c9c4,U,000008, -0x0000007100d5c9cc,U,000032, +0x0000007100d5c7dc,m,000076,_ZN4ksys3map14RailRoutePointD0Ev +0x0000007100d5c828,O,000008,_ZNK4ksys3map14RailRoutePoint17getCheckPointNameEv +0x0000007100d5c830,O,000252,_ZN4ksys3map14RailRoutePoint5parseEPNS0_9MubinIterEPN4sead4HeapE +0x0000007100d5c92c,m,000116,_ZN4ksys3map9RailRouteC1Ev +0x0000007100d5c9a0,O,000012,_ZNK4ksys3map9RailRoute15isRenderEnabledEv +0x0000007100d5c9ac,O,000012,_ZNK4ksys3map9RailRoute10isWalkableEv +0x0000007100d5c9b8,O,000012,_ZNK4ksys3map9RailRoute19isHorseTraceEnabledEv +0x0000007100d5c9c4,O,000008,_ZNK4ksys3map9RailRoute10getRouteIdEv +0x0000007100d5c9cc,O,000032,_ZNK4ksys3map9RailRoute17getCheckPointNameEi 0x0000007100d5c9ec,U,000196, 0x0000007100d5cab0,U,000156, -0x0000007100d5cb4c,U,000384, -0x0000007100d5cccc,U,000004,nullsub_3775 -0x0000007100d5ccd0,U,000008, -0x0000007100d5ccd8,U,000008, -0x0000007100d5cce0,U,000004,nullsub_3776 -0x0000007100d5cce4,U,000008, -0x0000007100d5ccec,U,000008, +0x0000007100d5cb4c,m,000384,_ZN4ksys3map9RailRoute5parseEPNS0_9MubinIterE +0x0000007100d5cccc,O,000004,_ZN4ksys3map4Rail4x_38Ev +0x0000007100d5ccd0,O,000008,_ZN4ksys3map4Rail4x_18Ev +0x0000007100d5ccd8,O,000008,_ZN4ksys3map4Rail4x_20Ev +0x0000007100d5cce0,O,000004,_ZN4ksys3map15RailConnectable4x_38Ev +0x0000007100d5cce4,O,000008,_ZN4ksys3map15RailConnectable4x_18Ev +0x0000007100d5ccec,O,000008,_ZN4ksys3map15RailConnectable4x_20Ev 0x0000007100d5ccf4,U,000152, 0x0000007100d5cd8c,U,000056, 0x0000007100d5cdc4,U,000092, diff --git a/src/KingSystem/Map/mapRail.cpp b/src/KingSystem/Map/mapRail.cpp index 939707d6..2ddf960c 100644 --- a/src/KingSystem/Map/mapRail.cpp +++ b/src/KingSystem/Map/mapRail.cpp @@ -1 +1,299 @@ #include "KingSystem/Map/mapRail.h" +#include +#include "KingSystem/Utils/SafeDelete.h" + +namespace ksys::map { + +RailPoint::~RailPoint() = default; + +const MubinIter& RailPoint::getIter() const { + return mIter; +} + +const sead::Vector3f& RailPoint::getRotate() const { + return mSRT.rotate; +} + +const sead::Vector3f& RailPoint::getTranslate() const { + return mSRT.translate; +} + +f32 RailPoint::getPrevDistance() const { + return mPrevDistance; +} + +f32 RailPoint::getNextDistance() const { + return mNextDistance; +} + +bool RailPoint::parse(MubinIter* iter, sead::Heap* heap) { + iter->getScale(&mSRT.scale); + iter->getRotate(&mSRT.rotate); + + bool success = true; + success &= iter->tryGetFloatArrayByKey(mSRT.translate.e.data(), "Translate"); + success &= iter->tryGetParamFloatByKey(&mPrevDistance, "PrevDistance"); + success &= iter->tryGetParamFloatByKey(&mNextDistance, "NextDistance"); + iter->tryGetControlPoints(&mCtrlPoints); + + return success; +} + +Rail::Rail() = default; + +Rail::~Rail() { + mRailPoints.freeBuffer(); +} + +void Rail::init(MubinIter* iter, sead::Heap* heap) { + parse(iter); + + MubinIter result; + if (iter->tryGetParamIterByKey(&result, "RailPoints")) { + s32 size = result.getSize(); + mRailPoints.allocBufferAssert(size, heap); + for (int i = 0; i < size; ++i) { + MubinIter node_iter; + if (result.tryGetIterByIndex(&node_iter, i)) { + RailPoint* point = allocPointIdx(i, heap); + point->mIter = node_iter; + point->parse(&node_iter, heap); + } + } + } + + x_38(); +} + +RailPoint* Rail::allocPointIdx(s32 idx, sead::Heap* heap) { + mRailPoints[idx] = allocPoint(heap); + return mRailPoints[idx]; +} + +s32 Rail::getNumPoints() const { + return mRailPoints.size(); +} + +const RailPoint* Rail::getPoint(s32 idx) const { + if (idx < 0 || idx >= mRailPoints.size()) + return nullptr; + return mRailPoints[idx]; +} + +RailPoint* Rail::getPoint(s32 idx) { + if (idx < 0 || idx >= mRailPoints.size()) + return nullptr; + return mRailPoints[idx]; +} + +const SRT& Rail::getPointSRT(s32 idx) const { + return mRailPoints[idx]->mSRT; +} + +const sead::Vector3f& Rail::getPointRotate(s32 idx) const { + return mRailPoints[idx]->mSRT.rotate; +} + +const sead::Vector3f& Rail::getPointTranslate(s32 idx) const { + return mRailPoints[idx]->mSRT.translate; +} + +const sead::Vector3f& Rail::getControlPoint(s32 rail_idx, s32 pt_idx) const { + return mRailPoints[rail_idx]->mCtrlPoints[pt_idx]; +} + +sead::Vector3f Rail::calcTranslate(float progress) const { + sead::Vector3f pos; + calcTranslateRotate(&pos, nullptr, progress); + return pos; +} + +void Rail::calcTranslate(sead::Vector3f* pos_out, float progress) const { + calcTranslateRotate(pos_out, nullptr, progress); +} + +bool Rail::isClosed() const { + return mFlags.isOn(Flag::Closed); +} + +bool Rail::isBezier() const { + return mFlags.isOn(Flag::Bezier); +} + +const u32& Rail::getHashId() const { + return mHashId; +} + +const char* Rail::getUniqueName() const { + return mUniqueName; +} + +RailPoint* Rail::allocPoint(sead::Heap* heap) { + return new (heap) RailPoint; +} + +bool Rail::parse(MubinIter* iter) { + bool success = true; + + success &= iter->tryGetParamUIntByKey(&mHashId, "HashId"); + success &= iter->tryGetParamStringByKey(&mUniqueName, "UniqueName"); + + bool closed = false; + if (!iter->tryGetParamBoolByKey(&closed, "Closed")) { + success = false; + } else if (closed) { + mFlags.set(Flag::Closed); + } else { + mFlags.reset(Flag::Closed); + } + + const char* type = nullptr; + if (!iter->tryGetParamStringByKey(&type, "RailType")) { + success = false; + } else { + mFlags.change(Flag::Bezier, sead::SafeString(type) == "Bezier"); + } + + return success; +} + +bool RailGuidePoint::parse(MubinIter* iter, sead::Heap* heap) { + bool success = true; + success &= RailPoint::parse(iter, heap); + success &= iter->tryGetParamFloatByKey(&mWaitFrame, "WaitFrame"); + return success; +} + +RailGuide::RailGuide() = default; + +RailGuide::~RailGuide() = default; + +bool RailRemainGuidePoint::parse(MubinIter* iter, sead::Heap* heap) { + bool success = true; + success &= RailGuidePoint::parse(iter, heap); + success &= iter->tryGetParamFloatByKey(&mMoveSpeed, "MoveSpeed"); + return success; +} + +RailRemainGuide::RailRemainGuide() = default; + +RailRemainGuide::~RailRemainGuide() = default; + +RailPoint* RailRemainGuide::allocPoint(sead::Heap* heap) { + return new (heap) RailRemainGuidePoint; +} + +RailConnectablePoint::RailConnectablePoint() = default; + +RailConnectablePoint::~RailConnectablePoint() { + sead::HeapMgr::instance()->getCurrentHeap(); + + if (mJunctionPoint) + util::safeDeleteArray(mJunctionPoint); +} + +Rail* RailConnectablePoint::getJunctionRail() const { + return mJunctionRail; +} + +RailPoint* RailConnectablePoint::getJunctionPoint() const { + return mJunctionPoint; +} + +bool RailConnectablePoint::parse(MubinIter* iter, sead::Heap* heap) { + return RailPoint::parse(iter, heap); +} + +RailRoutePoint::RailRoutePoint() = default; + +RailRoutePoint::~RailRoutePoint() = default; + +const char* RailRoutePoint::getCheckPointName() const { + return mCheckPointName; +} + +bool RailRoutePoint::parse(MubinIter* iter, sead::Heap* heap) { + bool success = true; + success &= RailConnectablePoint::parse(iter, heap); + iter->tryGetParamStringByKey(&mEntryPointName, "EntryPointName"); + iter->tryGetParamStringByKey(&mCheckPointName, "CheckPointName"); + return success; +} + +RailRoute::RailRoute() { + mFlags.set(Flag::AutoPlacementEnabled); + mFlags.set(Flag::EnableHorseTrace); + mFlags.set(Flag::Walkable); +} + +RailRoute::~RailRoute() = default; + +bool RailRoute::isRenderEnabled() const { + return mFlags.isOn(Flag::RenderEnabled); +} + +bool RailRoute::isAutoPlacementEnabled() const { + return mFlags.isOn(Flag::AutoPlacementEnabled); +} + +bool RailRoute::isWalkable() const { + return mFlags.isOn(Flag::Walkable); +} + +bool RailRoute::isHorseTraceEnabled() const { + return mFlags.isOn(Flag::EnableHorseTrace); +} + +const char* RailRoute::getRouteId() const { + return mRouteId; +} + +const char* RailRoute::getCheckPointName(s32 idx) const { + return static_cast(mRailPoints[idx])->mCheckPointName; +} + +bool RailRoute::parse(MubinIter* iter) { + int success = Rail::parse(iter); + + bool result = false; + if (iter->tryGetParamBoolByKey(&result, "RenderEnabled")) { + if (result) { + mFlags.set(Flag::RenderEnabled); + } else { + mFlags.reset(Flag::RenderEnabled); + } + } + + if (iter->tryGetParamBoolByKey(&result, "AutoPlacementEnabled")) { + if (result) { + mFlags.set(Flag::AutoPlacementEnabled); + } else { + mFlags.reset(Flag::AutoPlacementEnabled); + } + } else { + success = false; + } + + if (iter->tryGetParamBoolByKey(&result, "IsWalkable")) { + if (result) { + mFlags.set(Flag::Walkable); + } else { + mFlags.reset(Flag::Walkable); + } + } else { + success = false; + } + + if (iter->tryGetParamBoolByKey(&result, "IsEnableHorseTrace")) { + if (result) { + mFlags.set(Flag::EnableHorseTrace); + } else { + mFlags.reset(Flag::EnableHorseTrace); + } + } + + success &= iter->tryGetParamStringByKey(&mRouteId, "RouteId"); + return success; +} + +} // namespace ksys::map diff --git a/src/KingSystem/Map/mapRail.h b/src/KingSystem/Map/mapRail.h index dbbfca39..838b9039 100644 --- a/src/KingSystem/Map/mapRail.h +++ b/src/KingSystem/Map/mapRail.h @@ -1,7 +1,191 @@ #pragma once +#include +#include +#include "KingSystem/Map/mapMubinIter.h" + namespace ksys::map { -class Rail {}; +class Placement18; + +class RailPoint { + friend class Rail; + +public: + RailPoint() { + mSRT.scale = sead::Vector3f::ones; + mSRT.rotate = sead::Vector3f::zero; + mSRT.translate = sead::Vector3f::zero; + mCtrlPoints[0] = sead::Vector3f::zero; + mCtrlPoints[1] = sead::Vector3f::zero; + mPrevDistance = 0.0; + mNextDistance = 0.0; + } + virtual ~RailPoint(); + virtual bool parse(MubinIter* iter, sead::Heap* heap); + + const MubinIter& getIter() const; + const sead::Vector3f& getRotate() const; + const sead::Vector3f& getTranslate() const; + f32 getPrevDistance() const; + f32 getNextDistance() const; + +protected: + SRT mSRT; + ControlPoints mCtrlPoints; + float mPrevDistance; + float mNextDistance; + MubinIter mIter; +}; + +class Rail { +public: + Rail(); + virtual ~Rail(); + virtual void init(MubinIter* iter, sead::Heap* heap); + virtual s32 x_18() { return 0; } + virtual s32 x_20() { return 0; } + virtual RailPoint* allocPoint(sead::Heap* heap); + virtual bool parse(MubinIter* iter); + virtual void x_38() {} + + RailPoint* allocPointIdx(s32 idx, sead::Heap* heap); + s32 getNumPoints() const; + const RailPoint* getPoint(s32 idx) const; + RailPoint* getPoint(s32 idx); + + const SRT& getPointSRT(s32 idx) const; + const sead::Vector3f& getPointRotate(s32 idx) const; + const sead::Vector3f& getPointTranslate(s32 idx) const; + const sead::Vector3f& getControlPoint(s32 rail_idx, s32 pt_idx) const; + + sead::Vector3f calcTranslate(float progress) const; + void calcTranslate(sead::Vector3f* pos_out, float progress) const; + void calcTranslateRotate(sead::Vector3f* pos_out, sead::Vector3f* rot_out, + float progress) const; + bool isClosed() const; + bool isBezier() const; + const u32& getHashId() const; + const char* getUniqueName() const; + + enum class Flag : u32 { + Closed = 1 << 0, + Bezier = 1 << 1, + RenderEnabled = 1 << 2, + AutoPlacementEnabled = 1 << 3, + Walkable = 1 << 4, + EnableHorseTrace = 1 << 5, + }; + +protected: + static constexpr u32 sHashBase = 0; + + sead::TypedBitFlag mFlags{}; + u32 mHashId = sHashBase; + const char* mUniqueName{}; + sead::Buffer mRailPoints{}; + MubinIter mIter{}; +}; + +class RailGuidePoint : public RailPoint { + friend class RailGuide; + +public: + RailGuidePoint(); + ~RailGuidePoint() override; + + bool parse(MubinIter* iter, sead::Heap* heap) override; + +protected: + float mWaitFrame; +}; + +class RailRemainGuidePoint : public RailGuidePoint { + friend class RailRemainGuide; + +public: + RailRemainGuidePoint(); + ~RailRemainGuidePoint() override; + + bool parse(MubinIter* iter, sead::Heap* heap) override; + +protected: + float mMoveSpeed; +}; + +class RailGuide : public Rail { +public: + RailGuide(); + ~RailGuide() override; +}; + +class RailRemainGuide : public RailGuide { +public: + RailRemainGuide(); + ~RailRemainGuide() override; + + RailPoint* allocPoint(sead::Heap* heap) override; +}; + +class RailConnectablePoint : public RailPoint { + friend class RailConnectable; + +public: + RailConnectablePoint(); + ~RailConnectablePoint() override; + + bool parse(MubinIter* iter, sead::Heap* heap) override; + + Rail* getJunctionRail() const; + RailPoint* getJunctionPoint() const; + void parseJunctions(Placement18 p18, s32 idx, u32 hash, sead::Heap* heap); + +protected: + Rail* mJunctionRail; + RailPoint* mJunctionPoint; +}; + +class RailConnectable : public Rail { +public: + RailConnectable(); + ~RailConnectable() override; + + s32 x_18() override { return 1; } + s32 x_20() override { return 1; } + void x_38() override {} +}; + +class RailRoutePoint : public RailConnectablePoint { + friend class RailRoute; + +public: + RailRoutePoint(); + ~RailRoutePoint() override; + + bool parse(MubinIter* iter, sead::Heap* heap) override; + const char* getCheckPointName() const; + +protected: + const char* mEntryPointName; + const char* mCheckPointName; +}; + +class RailRoute : public RailConnectable { +public: + RailRoute(); + ~RailRoute() override; + + bool parse(MubinIter* iter) override; + + bool isRenderEnabled() const; + bool isAutoPlacementEnabled() const; + bool isWalkable() const; + bool isHorseTraceEnabled() const; + const char* getRouteId() const; + const char* getCheckPointName(s32 idx) const; + +protected: + const char* mRouteId; +}; } // namespace ksys::map diff --git a/src/KingSystem/System/CMakeLists.txt b/src/KingSystem/System/CMakeLists.txt index 1a53e941..4f907b81 100644 --- a/src/KingSystem/System/CMakeLists.txt +++ b/src/KingSystem/System/CMakeLists.txt @@ -62,4 +62,4 @@ target_sources(uking PRIVATE VFR.h VFRValue.cpp VFRValue.h - ) +)