From b29838e9d84eb55ab73782893aa16f7553bf1ab9 Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Sat, 11 Jul 2026 06:03:34 +0000 Subject: [PATCH 1/8] SMO matching and cleanup changes --- include/common/aglResBinaryShaderArchive.h | 20 +- include/common/aglResBinaryShaderProgram.h | 7 +- include/common/aglResCommon.h | 4 +- include/common/aglResShaderBinary.h | 41 +- include/common/aglResShaderMacro.h | 2 +- include/common/aglResShaderProgram.h | 2 +- include/common/aglResShaderSource.h | 2 +- include/common/aglResShaderVariation.h | 2 +- include/detail/aglGPUMemBlockMgr.h | 1 + include/driver/aglNVNMgr.h | 18 +- include/utility/aglParameterCurve.hpp | 24 ++ src/detail/aglGPUMemBlockMgr.cpp | 42 +- src/driver/aglNVNMgr.cpp | 18 +- src/utility/aglAtomicPtrArray.cpp | 88 ++++- src/utility/aglParameter.cpp | 55 ++- src/utility/aglParameterCurve.cpp | 8 + src/utility/aglParameterList.cpp | 54 ++- src/utility/aglParameterObj.cpp | 28 +- src/utility/aglResParameter.cpp | 81 +++- src/utility/common/aglResShaderArchive.cpp | 425 +++++++++++++++------ 20 files changed, 691 insertions(+), 231 deletions(-) create mode 100644 src/utility/aglParameterCurve.cpp diff --git a/include/common/aglResBinaryShaderArchive.h b/include/common/aglResBinaryShaderArchive.h index 0bd6e7a..41698e0 100644 --- a/include/common/aglResBinaryShaderArchive.h +++ b/include/common/aglResBinaryShaderArchive.h @@ -3,6 +3,7 @@ #include "common/aglResBinaryShaderProgram.h" #include "common/aglResShaderBinary.h" #include "common/aglResShaderProgram.h" +#include namespace agl { @@ -14,8 +15,12 @@ struct ResBinaryShaderArchiveData { u32 mVersion; u32 mFileSize; u32 mEndian; - u32 mResolved; + u32 mUnknown10; u32 mNameLen; + u32 mMemoryPoolSize; + u32 mMemoryPoolOffset; + NVNmemoryPool mMemoryPool; + NVNbuffer mBuffer; // char mName[]; public: @@ -24,14 +29,14 @@ struct ResBinaryShaderArchiveData { static const char* getExtension(); private: - static const u32 cVersion = 8; + static const u32 cVersion = 9; static const u32 cSignature = 0x53484142; // SHAB static const u32 cEndianCheckBit = 0x01000001; friend class ResCommon; friend class ResBinaryShaderArchive; }; -static_assert(sizeof(ResBinaryShaderArchiveData) == 0x18, +static_assert(sizeof(ResBinaryShaderArchiveData) == 0x150, "agl::ResBinaryShaderArchiveData size mismatch"); class ResBinaryShaderArchive : public ResCommon { @@ -47,19 +52,24 @@ class ResBinaryShaderArchive : public ResCommon { ResShaderBinaryArray getResShaderBinaryArray() const { const DataType* const data = ptr(); - return (const ResShaderBinaryArrayData*)((uintptr_t)(data + 1) + data->mNameLen); + uintptr_t offset = data->mNameLen; + offset += (uintptr_t)data; + return (const ResShaderBinaryArrayData*)(offset + sizeof(DataType)); } s32 getResShaderBinaryNum() const { return getResShaderBinaryArray().getNum(); } ResBinaryShaderProgramArray getResBinaryShaderProgramArray() const { const ResShaderBinaryArrayData* const data = getResShaderBinaryArray().ptr(); - return (const ResBinaryShaderProgramArrayData*)((uintptr_t)data + data->mSize); + return (const ResBinaryShaderProgramArrayData*)((uintptr_t)data + (u32)data->mSize); } s32 getResBinaryShaderProgramNum() const { return getResBinaryShaderProgramArray().getNum(); } bool setUp(bool le_resolve_pointers); + +private: + void createMemoryPoolBuffer_(); }; } // namespace agl diff --git a/include/common/aglResBinaryShaderProgram.h b/include/common/aglResBinaryShaderProgram.h index 95142f9..155effc 100644 --- a/include/common/aglResBinaryShaderProgram.h +++ b/include/common/aglResBinaryShaderProgram.h @@ -6,7 +6,7 @@ namespace agl { struct ResBinaryShaderProgramData { - u32 mSize; + s32 mSize; u32 mNameLen; u32 mKind; u32 mBaseIndex; @@ -23,7 +23,10 @@ class ResBinaryShaderProgram : public ResCommon { ResShaderVariationArray getResShaderVariationArray() const { const DataType* const data = ptr(); - return (const ResShaderVariationArrayData*)((uintptr_t)(data + 1) + data->mNameLen); + const u8* address = (const u8*)data; + address += sizeof(DataType); + address += data->mNameLen; + return (const ResShaderVariationArrayData*)address; } ResShaderVariationArray getResShaderVariationDefaultArray() const { diff --git a/include/common/aglResCommon.h b/include/common/aglResCommon.h index db574fd..8110ca8 100644 --- a/include/common/aglResCommon.h +++ b/include/common/aglResCommon.h @@ -109,7 +109,9 @@ class ResArray : public ResCommon> { iterator& operator++() { ++mIndex; - mElem = (ElemDataType*)((uintptr_t)mElem + Type(mElem).ref().mSize); + u8* elem = (u8*)mElem; + elem += Type(mElem).ref().mSize; + mElem = (ElemDataType*)elem; return *this; } diff --git a/include/common/aglResShaderBinary.h b/include/common/aglResShaderBinary.h index 23b0b89..1520c4b 100644 --- a/include/common/aglResShaderBinary.h +++ b/include/common/aglResShaderBinary.h @@ -5,13 +5,48 @@ namespace agl { +#pragma pack(push, 4) +template +union ResShaderBinaryPtr { + s32 mOffset; + T* mPtr; +}; + +template +struct ResShaderBinaryPtrEntry { + ResShaderBinaryPtr mValue; + u8 mPadding[Size - sizeof(mValue)]; +}; + +using ResShaderBinaryPtrEntry16 = ResShaderBinaryPtrEntry<0x10>; +using ResShaderBinaryPtrEntry12 = ResShaderBinaryPtrEntry<0xC>; + +struct ResShaderBinaryNvnData { + u8 mUnknown00[0xC]; + ResShaderBinaryPtr mData; + u32 mEntry16ANum; + ResShaderBinaryPtr mEntry16A; + u32 mEntry16BNum; + ResShaderBinaryPtr mEntry16B; + u32 mEntry12ANum; + ResShaderBinaryPtr mEntry12A; + u32 mEntry12BNum; + ResShaderBinaryPtr mEntry12B; + u32 mEntry12CNum; + ResShaderBinaryPtr mEntry12C; + u32 mEntry12DNum; + ResShaderBinaryPtr mEntry12D; +}; +#pragma pack(pop) + +static_assert(sizeof(ResShaderBinaryNvnData) == 0x5C); + struct ResShaderBinaryData { - u32 mSize; + s32 mSize; u32 mShaderType; s32 mDataOffset; // Relative to end of struct u32 mDataSize; }; -static_assert(sizeof(ResShaderBinaryData) == 0x10, "agl::ResShaderBinaryData size mismatch"); class ResShaderBinary : public ResCommon { public: @@ -25,12 +60,12 @@ class ResShaderBinary : public ResCommon { } void modifyBinaryEndian(); + void resolvePtr(const void* base); void setUp(); }; using ResShaderBinaryArray = ResArray; using ResShaderBinaryArrayData = ResShaderBinaryArray::DataType; -static_assert(sizeof(ResShaderBinaryArrayData) == 8, "agl::ResShaderBinaryArrayData size mismatch"); } // namespace agl diff --git a/include/common/aglResShaderMacro.h b/include/common/aglResShaderMacro.h index 91e2bbd..910a30a 100644 --- a/include/common/aglResShaderMacro.h +++ b/include/common/aglResShaderMacro.h @@ -5,7 +5,7 @@ namespace agl { struct ResShaderMacroData { - u32 mSize; + s32 mSize; u32 mNameLen; u32 mValueLen; // char mName[]; diff --git a/include/common/aglResShaderProgram.h b/include/common/aglResShaderProgram.h index fddfa34..d3daeeb 100644 --- a/include/common/aglResShaderProgram.h +++ b/include/common/aglResShaderProgram.h @@ -7,7 +7,7 @@ namespace agl { struct ResShaderProgramData { - u32 mSize; + s32 mSize; u32 mNameLen; s32 mSourceIndex[cShaderType_Num]; // char mName[]; diff --git a/include/common/aglResShaderSource.h b/include/common/aglResShaderSource.h index fdd6604..e75a200 100644 --- a/include/common/aglResShaderSource.h +++ b/include/common/aglResShaderSource.h @@ -5,7 +5,7 @@ namespace agl { struct ResShaderSourceData { - u32 mSize; + s32 mSize; u32 mNameLen; u32 mTextLen; // Text Length u32 _c; // Text Length... 2 diff --git a/include/common/aglResShaderVariation.h b/include/common/aglResShaderVariation.h index fe16be0..952aa64 100644 --- a/include/common/aglResShaderVariation.h +++ b/include/common/aglResShaderVariation.h @@ -5,7 +5,7 @@ namespace agl { struct ResShaderVariationData { - u32 mSize; + s32 mSize; u32 mNameLen; s32 mValueNum; u32 mIDLen; diff --git a/include/detail/aglGPUMemBlockMgr.h b/include/detail/aglGPUMemBlockMgr.h index 3553ad1..8a4d23e 100644 --- a/include/detail/aglGPUMemBlockMgr.h +++ b/include/detail/aglGPUMemBlockMgr.h @@ -57,6 +57,7 @@ class GPUMemBlockMgrHeapEx : public sead::hostio::Node, public sead::IDisposer { ~GPUMemBlockMgrHeapEx() override; void finalize(); + sead::Heap* getDisposerHeap() const { return getDisposerHeap_(); } private: s32 mAllowSharing; diff --git a/include/driver/aglNVNMgr.h b/include/driver/aglNVNMgr.h index 7e2a80a..2cfadcc 100644 --- a/include/driver/aglNVNMgr.h +++ b/include/driver/aglNVNMgr.h @@ -2,21 +2,31 @@ #include #include +#include #include "driver/aglGraphicsDriverMgr.h" namespace agl::driver { class NVNMgr : public GraphicsDriverMgr { - // TODO: This is wrong and should actually touch the GraphicsDriverMgr implementation - SEAD_SINGLETON_DISPOSER(NVNMgr) public: + static NVNMgr* instance() { return (NVNMgr*)GraphicsDriverMgr::instance(); } + static NVNMgr* createInstance(sead::Heap* heap); + + NVNdevice* getDevice() const { return mDevice; } + NVNMgr(); ~NVNMgr() override; + NVNMgr(const NVNMgr&) = delete; + NVNMgr& operator=(const NVNMgr&) = delete; + NVNMgr(NVNMgr&&) = delete; + NVNMgr& operator=(NVNMgr&&) = delete; + private: + NVNdevice* mDevice; + u8 mUnknown[0x470]; }; -// TODO: need sead::Graphics reversing... -// static_assert(sizeof(NVNMgr) == 0x548); +static_assert(sizeof(NVNMgr) == 0x4B0); } // namespace agl::driver diff --git a/include/utility/aglParameterCurve.hpp b/include/utility/aglParameterCurve.hpp index b6a2919..8daa409 100644 --- a/include/utility/aglParameterCurve.hpp +++ b/include/utility/aglParameterCurve.hpp @@ -27,6 +27,26 @@ inline void ParameterCurve<4>::reset() { } } +template <> +__attribute__((noinline, used)) inline void ParameterCurve<3>::reset() { + static f32 s_initialize[9] = {0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5}; + + sead::MemUtil::copy(mCurveData[0].f, s_initialize, sizeof(s_initialize)); + for (u32 j = 9; j < cUnitCurveParamNum; ++j) + mCurveData[0].f[j] = 1.0; + mCurves[0].setData(&mCurveData[0], sead::hostio::CurveType::Hermit2D, cUnitCurveParamNum, 9); + + sead::MemUtil::copy(mCurveData[1].f, s_initialize, sizeof(s_initialize)); + for (u32 j = 9; j < cUnitCurveParamNum; ++j) + mCurveData[1].f[j] = 1.0; + mCurves[1].setData(&mCurveData[1], sead::hostio::CurveType::Hermit2D, cUnitCurveParamNum, 9); + + sead::MemUtil::copy(mCurveData[2].f, s_initialize, sizeof(s_initialize)); + for (u32 j = 9; j < cUnitCurveParamNum; ++j) + mCurveData[2].f[j] = 1.0; + mCurves[2].setData(&mCurveData[2], sead::hostio::CurveType::Hermit2D, cUnitCurveParamNum, 9); +} + template inline void ParameterCurve::reset() { static f32 s_initialize[9] = {0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5}; @@ -89,6 +109,7 @@ inline ParameterBase* ParameterCurve::clone(sead::Heap* heap, IParameterObj* } template +// NON_MATCHING: N=2/3/4 behavior and baseline sizes are correct, but retail unrolls pointer/metadata stores in a different schedule. Pointer-first ordering shrinks the functions by hoisting a common store, while setData/setFloats expands them. Next hypothesis is an explicit per-instantiation assignment order that avoids both common-store hoisting and helper write-back. inline void ParameterCurve::postApplyResource_(const void*, size_t size) { if (this->size() == size) { for (u32 i = 0; i < N; ++i) { @@ -105,4 +126,7 @@ inline void ParameterCurve::postApplyResource_(const void*, size_t size) { } } +extern template bool ParameterCurve<1>::copy(const ParameterBase&); +extern template ParameterBase* ParameterCurve<1>::clone(sead::Heap*, IParameterObj*) const; + } // namespace agl::utl diff --git a/src/detail/aglGPUMemBlockMgr.cpp b/src/detail/aglGPUMemBlockMgr.cpp index 1b39a9c..0ad1738 100644 --- a/src/detail/aglGPUMemBlockMgr.cpp +++ b/src/detail/aglGPUMemBlockMgr.cpp @@ -38,18 +38,46 @@ GPUMemBlockMgrHeapEx* GPUMemBlockMgr::findGPUMemBlockMgrHeapEx_(sead::Heap* p_he int* p_outIndex) { SEAD_ASSERT(p_heap != nullptr); - if (mMngrHeaps.isEmpty()) { + const int size = mMngrHeaps.size(); + if (size == 0) return nullptr; + + int index; + GPUMemBlockMgrHeapEx* result; + int right = size - 1; + if (right < 0) { + result = nullptr; + } else { + auto** managers = mMngrHeaps.data(); + int left = 0; + while (true) { + index = right - ~left - 1; + index /= 2; + auto* manager = managers[index]; + const intptr_t difference = + reinterpret_cast(manager->getDisposerHeap()) - + reinterpret_cast(p_heap); + if (difference == 0) { + result = u32(size) > u32(index) ? manager : nullptr; + break; + } + left = difference >= 0 ? left : index + 1; + right = difference < 0 ? right : index - 1; + if (left > right) { + result = nullptr; + break; + } + } } - // TODO - return nullptr; + if (p_outIndex) + *p_outIndex = index; + return result; } -GPUMemBlockMgrHeapEx::GPUMemBlockMgrHeapEx(sead::Heap* p_heap) { - mAllowSharing = 1; - m08 = nullptr; - m10 = nullptr; +GPUMemBlockMgrHeapEx::GPUMemBlockMgrHeapEx(sead::Heap* p_heap) + : sead::IDisposer(p_heap, sead::IDisposer::HeapNullOption::AlwaysUseSpecifiedHeap), + mAllowSharing(1), m08(nullptr), m10(nullptr) { } } // namespace agl::detail diff --git a/src/driver/aglNVNMgr.cpp b/src/driver/aglNVNMgr.cpp index 85f0f11..24ec68f 100644 --- a/src/driver/aglNVNMgr.cpp +++ b/src/driver/aglNVNMgr.cpp @@ -1,6 +1,20 @@ #include "driver/aglNVNMgr.h" namespace agl::driver { -// TODO: This is wrong and should actually touch the GraphicsDriverMgr implementation -SEAD_SINGLETON_DISPOSER_IMPL(NVNMgr) + +NVNMgr* NVNMgr::createInstance(sead::Heap* heap) +{ + NVNMgr* instance = (NVNMgr*)GraphicsDriverMgr::sInstance; + if (!instance) + { + u8* buffer = new (heap, alignof(NVNMgr)) u8[sizeof(NVNMgr)]; + instance = (NVNMgr*)buffer; + GraphicsDriverMgr::SingletonDisposer_::sStaticDisposer = + new (&instance->mSingletonDisposerBuf_) GraphicsDriverMgr::SingletonDisposer_(heap); + instance = new (instance) NVNMgr; + GraphicsDriverMgr::sInstance = instance; + } + return instance; +} + } // namespace agl::driver diff --git a/src/utility/aglAtomicPtrArray.cpp b/src/utility/aglAtomicPtrArray.cpp index 09c4ec9..711c5a0 100644 --- a/src/utility/aglAtomicPtrArray.cpp +++ b/src/utility/aglAtomicPtrArray.cpp @@ -63,22 +63,98 @@ void AtomicPtrArrayImpl::erase(s32 pos, s32 count) { mPtrNum = ptr_num - count; } -// NON_MATCHING: semantically equivalent (Fisher–Yates) void AtomicPtrArrayImpl::shuffle(sead::Random* random) { SEAD_ASSERT(random); for (s32 i = mPtrNum - 1; i > 0; --i) swap(i, random->getS32Range(0, i + 1)); } -// NON_MATCHING: Nintendo implemented a sorting algorithm manually void AtomicPtrArrayImpl::sort(CompareCallbackImpl cmp) { - std::sort(mPtrs, mPtrs + mPtrNum, [cmp](void* a, void* b) { return cmp(a, b) < 0; }); + void** ptrs = mPtrs; + if (mPtrNum < 2) + return; + + s32 begin = 0; + s32 end = mPtrNum - 1; + do { + s32 last = begin; + for (s32 i = begin; i < end; ++i) { + if (cmp(ptrs[i], ptrs[i + 1]) > 0) { + void* tmp = ptrs[i + 1]; + ptrs[i + 1] = ptrs[i]; + ptrs[i] = tmp; + last = i; + } + } + + end = last; + if (begin == end) + break; + + last = end; + for (s32 i = end; i > begin; --i) { + if (cmp(ptrs[i], ptrs[i - 1]) < 0) { + void* tmp = ptrs[i - 1]; + ptrs[i - 1] = ptrs[i]; + ptrs[i] = tmp; + last = i; + } + } + begin = last; + } while (begin != end); } -// NON_MATCHING: Nintendo implemented heap sort manually void AtomicPtrArrayImpl::heapSort(CompareCallbackImpl cmp) { - std::make_heap(mPtrs, mPtrs + mPtrNum); - std::sort_heap(mPtrs, mPtrs + mPtrNum, [cmp](void* a, void* b) { return cmp(a, b) < 0; }); + s32 size = mPtrNum; + if (size < 2) + return; + + void** ptrs = mPtrs; + s32 parent = size / 2; + do { + void* value = ptrs[parent - 1]; + s32 hole = parent; + s32 child = parent * 2; + + while (child <= size) { + s32 selected = child; + if (child < size && cmp(ptrs[child - 1], ptrs[child]) < 0) + ++selected; + + if (cmp(value, ptrs[selected - 1]) >= 0) + break; + + ptrs[hole - 1] = ptrs[selected - 1]; + hole = selected; + child = selected * 2; + } + + ptrs[hole - 1] = value; + } while (parent-- > 1); + + while (size >= 2) { + const s32 last = size - 1; + void* value = ptrs[last]; + ptrs[last] = ptrs[0]; + + s32 hole = 1; + s32 child = 2; + while (child < size) { + s32 selected = child; + if (child < last && cmp(ptrs[child - 1], ptrs[child]) < 0) + ++selected; + + if (cmp(value, ptrs[selected - 1]) >= 0) + break; + + ptrs[hole - 1] = ptrs[selected - 1]; + hole = selected; + child = selected * 2; + } + + ptrs[hole - 1] = value; + size = last; + } } } // namespace agl::detail diff --git a/src/utility/aglParameter.cpp b/src/utility/aglParameter.cpp index c933573..4cb417c 100644 --- a/src/utility/aglParameter.cpp +++ b/src/utility/aglParameter.cpp @@ -101,43 +101,30 @@ const char* ParameterBase::getParameterTypeName(ParameterType type) { return sParameterTypeNames[u32(type)]; } -// NON_MATCHING: Clang emits a switch... bool ParameterBase::isSafeType(ParameterType type) const { if (getParameterType() == type) return true; - - constexpr std::pair pairs[] = { - {ParameterType::String64, ParameterType::String32}, - {ParameterType::String32, ParameterType::String64}, - {ParameterType::String256, ParameterType::String32}, - {ParameterType::String256, ParameterType::String64}, - {ParameterType::String32, ParameterType::String256}, - {ParameterType::String64, ParameterType::String256}, - }; - - for (const auto pair : pairs) { - if (type == pair.first && getParameterType() == pair.second) - return true; - } - + if (getParameterType() == ParameterType::String32 && type == ParameterType::String64) + return true; + if (getParameterType() == ParameterType::String64 && type == ParameterType::String32) + return true; + if (getParameterType() == ParameterType::String32 && type == ParameterType::String256) + return true; + if (getParameterType() == ParameterType::String64 && type == ParameterType::String256) + return true; + if (getParameterType() == ParameterType::String256 && type == ParameterType::String32) + return true; + if (getParameterType() == ParameterType::String256 && type == ParameterType::String64) + return true; if (getParameterType() == ParameterType::StringRef && (type == ParameterType::String32 || type == ParameterType::String64 || - type == ParameterType::String256)) { + type == ParameterType::String256)) return true; - } - return false; } -bool ParameterBase::verifyType(ParameterType type) const { - if (isSafeType(type)) - return true; - - sead::BufferingPrintFormatter ss; - ss << "!!! AGL ERROR !!! Instance ParameterType = %s Resource ParameterType = %s\n" - << sParameterTypeNames[u32(getParameterType())] << sParameterTypeNames[u32(type)] - << sead::flush; - return false; +bool ParameterBase::verifyType(ParameterType) const { + return true; } bool ParameterBase::copy(const ParameterBase& other) { @@ -150,17 +137,19 @@ bool ParameterBase::copy(const ParameterBase& other) { void ParameterBase::copyUnsafe(const ParameterBase& other) { if (other.getParameterType() == ParameterType::StringRef) { - auto* source = static_cast(other.typePtr()); - auto* dest = static_cast(typePtr()); - *dest = *source; + static_cast(typePtr())->operator=( + *static_cast(other.typePtr())); return; } auto* dest = ptrT(); auto* src = other.ptrT(); const s32 n = size(); - for (s32 i = 0; i < n; ++i) - *dest++ = *src++; + for (s32 i = 0; i < n; ++i) { + *dest = *src; + ++dest; + ++src; + } } template <> diff --git a/src/utility/aglParameterCurve.cpp b/src/utility/aglParameterCurve.cpp new file mode 100644 index 0000000..1c7a070 --- /dev/null +++ b/src/utility/aglParameterCurve.cpp @@ -0,0 +1,8 @@ +#include "utility/aglParameter.h" + +namespace agl::utl { + +template bool ParameterCurve<1>::copy(const ParameterBase&); +template ParameterBase* ParameterCurve<1>::clone(sead::Heap*, IParameterObj*) const; + +} // namespace agl::utl diff --git a/src/utility/aglParameterList.cpp b/src/utility/aglParameterList.cpp index 3142550..e57f7e6 100644 --- a/src/utility/aglParameterList.cpp +++ b/src/utility/aglParameterList.cpp @@ -27,7 +27,10 @@ void IParameterList::addList(IParameterList* child, const sead::SafeString& name SEAD_ASSERT(child != nullptr); child->setParameterListName_(name); - (!mpChildListTail ? mpChildListHead : mpChildListTail->mNext) = child; + if (mpChildListTail) + mpChildListTail->mNext = child; + else + mpChildListHead = child; mpChildListTail = child; child->mParent = this; } @@ -41,7 +44,10 @@ void IParameterList::addObj(IParameterObj* child, const sead::SafeString& name) #endif child->mNameHash = ParameterBase::calcHash(name); - (!mpChildObjTail ? mpChildObjHead : mpChildObjTail->mNext) = child; + if (mpChildObjTail) + mpChildObjTail->mNext = child; + else + mpChildObjHead = child; mpChildObjTail = child; } @@ -81,11 +87,12 @@ void IParameterList::removeList(IParameterList* child) { return; } - (prev ? prev->mNext : mpChildListHead) = child->mNext; - if (!child->mNext) { - SEAD_ASSERT(mpChildListTail == child); + if (prev) + prev->mNext = child->mNext; + else + mpChildListHead = child->mNext; + if (!child->mNext) mpChildListTail = prev; - } child->mNext = nullptr; } @@ -105,11 +112,12 @@ void IParameterList::removeObj(IParameterObj* child) { return; } - (prev ? prev->mNext : mpChildObjHead) = child->mNext; - if (!child->mNext) { - SEAD_ASSERT(mpChildObjTail == child); + if (prev) + prev->mNext = child->mNext; + else + mpChildObjHead = child->mNext; + if (!child->mNext) mpChildObjTail = prev; - } child->mNext = nullptr; } @@ -183,34 +191,24 @@ bool IParameterList::verifyObj() const { return ret; } +// NON_MATCHING: Retail shares the return epilogue with the adjacent verification function. bool IParameterList::verifyList(IParameterList* p_check, IParameterList* other) const { SEAD_ASSERT(p_check != nullptr); - auto* list = other; bool ok = true; - while (list) { - if (p_check->getNameHash() == list->getNameHash()) { - sead::BufferingPrintFormatter ss; - ss << "Same hash code at [%s] and [%s]. Please change.\n" - << p_check->getName().cstr() << list->getName().cstr() << sead::flush; - ok = false; - } - list = list->mNext; + while (other) { + ok &= p_check->getNameHash() != other->getNameHash(); + other = other->mNext; } return ok; } +// NON_MATCHING: Retail shares the return epilogue with the adjacent verification function. bool IParameterList::verifyObj(IParameterObj* p_check, IParameterObj* other) const { SEAD_ASSERT(p_check != nullptr); - auto* list = other; bool ok = true; - while (list) { - if (p_check->getNameHash() == list->getNameHash()) { - sead::BufferingPrintFormatter ss; - ss << "Same hash code at [%s] and [%s]. Please change.\n" - << p_check->getName().cstr() << list->getName().cstr() << sead::flush; - ok = false; - } - list = list->mNext; + while (other) { + ok &= p_check->getNameHash() != other->getNameHash(); + other = other->mNext; } return ok; } diff --git a/src/utility/aglParameterObj.cpp b/src/utility/aglParameterObj.cpp index d93fc22..babb552 100644 --- a/src/utility/aglParameterObj.cpp +++ b/src/utility/aglParameterObj.cpp @@ -10,15 +10,13 @@ IParameterObj::IParameterObj() = default; void IParameterObj::pushBackListNode(ParameterBase* p_node) { SEAD_ASSERT(p_node != nullptr); - ParameterBase** ptr; - if (mParamListTail) { - ptr = &mParamListTail->mNext; - } else { - ptr = &mParamListHead; + if (mParamListTail) + mParamListTail->mNext = p_node; + else { + mParamListHead = p_node; mParamListTail = p_node; } - *ptr = p_node; mParamListTail = p_node; ++mParamListSize; } @@ -87,18 +85,16 @@ bool IParameterObj::verify() const { return ret; } +// NON_MATCHING: Retail shares the return epilogue with the adjacent verification function. bool IParameterObj::verify(ParameterBase* p_check, ParameterBase* other) const { SEAD_ASSERT(p_check != nullptr); - auto* param = other; bool ok = true; - while (param) { - if (p_check->getNameHash() == param->getNameHash()) { - sead::BufferingPrintFormatter ss; - ss << "Same hash code at [%s] and [%s]. Please change.\n" - << p_check->getName().cstr() << param->getName().cstr() << sead::flush; - ok = false; - } - param = param->mNext; + if (other) { + const u32 hash = p_check->getNameHash(); + do { + ok &= hash != other->getNameHash(); + other = other->mNext; + } while (other); } return ok; } @@ -181,6 +177,8 @@ void IParameterObj::copyLerp(ParameterBase* first, ParameterBase* last, postCopy_(); } +// NON_MATCHING: Retail tests mParamListHead before the source-list pointer. +// Next hypothesis: alter local lifetimes to recover the X20/X21 assignment without changing the CFG. void IParameterObj::copyLerp(const IParameterObj& obj1, const IParameterObj& obj2, f32 t) { if (!preCopy_()) return; diff --git a/src/utility/aglResParameter.cpp b/src/utility/aglResParameter.cpp index 3afc4f4..a9af506 100644 --- a/src/utility/aglResParameter.cpp +++ b/src/utility/aglResParameter.cpp @@ -1,7 +1,12 @@ #include "utility/aglResParameter.h" +#include #include +#include +#include #include #include +#include +#include "detail/aglPrivateResource.h" #include "utility/aglParameter.h" namespace agl::utl { @@ -70,27 +75,77 @@ s32 ResParameterList::searchObjIndex(u32 obj_hash) const { return -1; } -// NON_MATCHING: partial implementation (unused conversion code is unimplemented) +// NON_MATCHING: Retail tests the endian flag with TBZ and lays out the early-return block oppositely. +// Next hypothesis: recover the original flag/helper boundary without branch hints. ResParameterArchive::ResParameterArchive(const void* p_data) { - mpData = static_cast(const_cast(p_data)); - if (!p_data) + mpData = (ResParameterArchiveData*)p_data; + if (!mpData) return; - SEAD_ASSERT(sead::PtrUtil::isAlignedN(p_data, 4)); - if (mpData->flags.isOff(ResParameterArchiveFlag::LittleEndian)) - ModifyEndianU32(false, const_cast(mpData), + const u32 initial_flags = mpData->flags.getDirect(); + bool utf8; + if ((initial_flags & u32(ResParameterArchiveFlag::LittleEndian)) == 0) { + ModifyEndianU32(false, (ResParameterArchiveData*)mpData, sizeof(ResParameterArchiveData)); + utf8 = mpData->flags.isOn(ResParameterArchiveFlag::Utf8); + } else { + if ((initial_flags & u32(ResParameterArchiveFlag::Utf8)) != 0) + return; + utf8 = false; + } - verify(); + const size_t list_size = sizeof(ResParameterListData) * mpData->num_lists; + const size_t object_size = sizeof(ResParameterObjData) * mpData->num_objects; - if (mpData->flags.isOn(ResParameterArchiveFlag::LittleEndian) && - mpData->flags.isOn(ResParameterArchiveFlag::Utf8)) { - // Nothing else to do. - return; + u8* const parameter_io = ptrBytes() + sizeof(ResParameterArchiveData) + mpData->offset_to_pio; + char* string = (char*)(parameter_io + list_size); + string += object_size; + const size_t parameter_size = sizeof(ResParameterData) * mpData->num_parameters; + string += parameter_size; + string += mpData->data_section_size; + char* const string_end = string + mpData->string_section_size; + + if ((initial_flags & u32(ResParameterArchiveFlag::LittleEndian)) == 0) { + const size_t parameter_io_size = + list_size + object_size + parameter_size + mpData->data_section_size; + if (parameter_io_size != 0) + ModifyEndianU32(false, parameter_io, parameter_io_size); + + const u32 unknown_size = mpData->unk_section_size; + for (u32 offset = 0; offset < unknown_size; + offset += *(u32*)(string_end + offset)) { + ModifyEndianU32(false, string_end + offset, sizeof(u32)); + } + + ((ResParameterArchiveData*)mpData)->flags.set( + ResParameterArchiveFlag::LittleEndian); } - // FIXME: implement endianness and string encoding conversion (requires PrivateResource) - SEAD_ASSERT_MSG(false, "endianness and string conversion is unimplemented"); + if (!utf8 && mpData->string_section_size != 0) { + while (string < string_end) { + const s32 source_length = sead::SafeString(string).calcLength(); + if (source_length > 0) { + sead::Heap* const heap = detail::PrivateResource::instance()->getWorkHeap(); + const s32 source_capacity = source_length + 1; + char16* const utf16 = new (heap, 8) char16[source_capacity]; + const s32 converted_capacity = 2 * source_capacity; + char* const converted = new (heap, 8) char[converted_capacity]; + + sead::StringUtil::convertSjisToUtf16(utf16, source_capacity, string, -1); + sead::StringUtil::convertUtf16ToUtf8(converted, converted_capacity, utf16, -1); + + const s32 converted_length = sead::SafeString(converted).calcLength(); + const s32 copy_size = sead::Mathi::min(converted_length + 1, source_capacity); + std::memcpy(string, converted, copy_size); + heap->free(utf16); + heap->free(converted); + } + + string += (source_length + 4) & ~3; + } + + ((ResParameterArchiveData*)mpData)->flags.set(ResParameterArchiveFlag::Utf8); + } } } // namespace agl::utl diff --git a/src/utility/common/aglResShaderArchive.cpp b/src/utility/common/aglResShaderArchive.cpp index 93a9cb4..a4bff20 100644 --- a/src/utility/common/aglResShaderArchive.cpp +++ b/src/utility/common/aglResShaderArchive.cpp @@ -5,13 +5,15 @@ #include "common/aglResShaderSymbol.h" #include "common/aglResShaderVariation.h" #include "common/aglShader.h" +#include "driver/aglNVNMgr.h" +#include #ifdef cafe #include #endif // cafe static inline void swap32(void* ptr, size_t size) { - u32* ptr_u32 = static_cast(ptr); + u32* ptr_u32 = (u32*)(ptr); u32 count = size / sizeof(u32); for (u32 i = 0; i < count; i++) { @@ -55,10 +57,143 @@ void* modifyBinaryPtr(void* base_ptr, void* ptr) { #endif // cafe +struct ResBinaryShaderSymbolData { + s32 mSize; + u32 mUnknown04; + u32 mNameLen; +}; + +struct ResBinaryShaderDefaultValueData { + s32 mSize; + u32 mUnknown04; + u32 mUnknown08; +}; + +using ResBinaryShaderSymbolArrayData = agl::ResArrayData; +using ResBinaryShaderDefaultValueArrayData = + agl::ResArrayData; + +void modifyEndianResBinarySymbolArray(ResBinaryShaderSymbolArrayData* symbol_array) { + agl::ModifyEndianU32(false, symbol_array, sizeof(*symbol_array)); + + ResBinaryShaderSymbolData* symbol = (ResBinaryShaderSymbolData*)(symbol_array + 1); + for (u32 num = symbol_array->mNum; num != 0; --num) { + agl::ModifyEndianU32(false, symbol, sizeof(*symbol)); + symbol = (ResBinaryShaderSymbolData*)( + (u8*)(symbol) + symbol->mSize); + } + + symbol = (ResBinaryShaderSymbolData*)(symbol_array + 1); + const u32 symbol_num = symbol_array->mNum; + for (u32 i = 0; i != symbol_num; + ++i, symbol = (ResBinaryShaderSymbolData*)( + (u8*)(symbol) + symbol->mSize)) { + ResBinaryShaderDefaultValueArrayData* default_array = + (ResBinaryShaderDefaultValueArrayData*)( + (u8*)(symbol + 1) + symbol->mNameLen); + agl::ModifyEndianU32(false, default_array, sizeof(*default_array)); + + ResBinaryShaderDefaultValueData* value = + (ResBinaryShaderDefaultValueData*)(default_array + 1); + for (u32 num = default_array->mNum; num != 0; --num) { + agl::ModifyEndianU32(false, value, sizeof(*value)); + value = (ResBinaryShaderDefaultValueData*)( + (u8*)(value) + value->mSize); + } + + } +} + +void modifyEndianResShaderArchiveSymbolArray(ResBinaryShaderSymbolArrayData* symbol_array) { + agl::ModifyEndianU32(false, symbol_array, sizeof(*symbol_array)); + + ResBinaryShaderSymbolData* symbol = (ResBinaryShaderSymbolData*)(symbol_array + 1); + for (u32 num = symbol_array->mNum; num != 0; --num) { + agl::ModifyEndianU32(false, symbol, sizeof(*symbol)); + symbol = (ResBinaryShaderSymbolData*)( + (u8*)(symbol) + symbol->mSize); + } + + symbol = (ResBinaryShaderSymbolData*)(symbol_array + 1); + const u32 symbol_num = symbol_array->mNum; + for (u32 i = 0; i != symbol_num; + ++i, symbol = (ResBinaryShaderSymbolData*)( + (u8*)(symbol) + symbol->mSize)) { + ResBinaryShaderDefaultValueArrayData* default_array = + (ResBinaryShaderDefaultValueArrayData*)( + (u8*)(symbol + 1) + symbol->mNameLen); + agl::ModifyEndianU32(false, default_array, sizeof(*default_array)); + + ResBinaryShaderDefaultValueData* value = + (ResBinaryShaderDefaultValueData*)(default_array + 1); + for (u32 num = default_array->mNum; num != 0; --num) { + agl::ModifyEndianU32(false, value, sizeof(*value)); + value = (ResBinaryShaderDefaultValueData*)( + (u8*)(value) + value->mSize); + } + + } +} + +template +void resolveShaderBinaryArray(agl::ResShaderBinaryPtr& relative, s32 count, + u8* binary_base, const void* pointer_base) { + if (!relative.mPtr) { + relative.mPtr = nullptr; + return; + } + + const u8* base = binary_base; + if (pointer_base) + base = (const u8*)(pointer_base); + + Entry* entries = (Entry*)(binary_base + relative.mOffset); + for (s32 i = 0; i < count; ++i) + entries[i].mValue.mPtr = base + entries[i].mValue.mOffset; + + relative.mPtr = entries; +} + } // namespace namespace agl { +void ResShaderBinary::resolvePtr(const void* pointer_base) { + ResShaderBinaryData* data = ptr(); + u8* binary_base = (u8*)(data + 1); + ResShaderBinaryNvnData* nvn_data = (ResShaderBinaryNvnData*)(binary_base); + + nvn_data->mData.mPtr = binary_base + nvn_data->mData.mOffset; + resolveShaderBinaryArray(nvn_data->mEntry16A, nvn_data->mEntry16ANum, binary_base, + pointer_base); + resolveShaderBinaryArray(nvn_data->mEntry16B, nvn_data->mEntry16BNum, binary_base, + pointer_base); + resolveShaderBinaryArray(nvn_data->mEntry12A, nvn_data->mEntry12ANum, binary_base, + pointer_base); + resolveShaderBinaryArray(nvn_data->mEntry12B, nvn_data->mEntry12BNum, binary_base, + pointer_base); + resolveShaderBinaryArray(nvn_data->mEntry12C, nvn_data->mEntry12CNum, binary_base, + pointer_base); + + ResShaderBinaryPtr& relative = nvn_data->mEntry12D; + if (!relative.mPtr) { + relative.mPtr = nullptr; + return; + } + + const s32 count = (s32)(nvn_data->mEntry12DNum); + const u8* base = binary_base; + if (pointer_base) + base = (const u8*)(pointer_base); + + ResShaderBinaryPtrEntry12* entries = + (ResShaderBinaryPtrEntry12*)(binary_base + relative.mOffset); + for (s32 i = 0; i < count; ++i) + entries[i].mValue.mPtr = base + entries[i].mValue.mOffset; + + relative.mPtr = entries; +} + void ResShaderBinary::modifyBinaryEndian() { size_t size = 0; void* data = nullptr; @@ -66,7 +201,7 @@ void ResShaderBinary::modifyBinaryEndian() { #ifdef cafe switch (getShaderType()) { case cShaderType_Vertex: { - GX2VertexShader* vertex_shader = static_cast(getData()); + GX2VertexShader* vertex_shader = (GX2VertexShader*)(getData()); swap32(vertex_shader, sizeof(GX2VertexShader)); size += vertex_shader->numUniformBlocks * sizeof(GX2UniformBlock) + @@ -78,7 +213,7 @@ void ResShaderBinary::modifyBinaryEndian() { data = vertex_shader + 1; } break; case cShaderType_Fragment: { - GX2PixelShader* pixel_shader = static_cast(getData()); + GX2PixelShader* pixel_shader = (GX2PixelShader*)(getData()); swap32(pixel_shader, sizeof(GX2PixelShader)); size += pixel_shader->numUniformBlocks * sizeof(GX2UniformBlock) + @@ -89,7 +224,7 @@ void ResShaderBinary::modifyBinaryEndian() { data = pixel_shader + 1; } break; case cShaderType_Geometry: { - GX2GeometryShader* geometry_shader = static_cast(getData()); + GX2GeometryShader* geometry_shader = (GX2GeometryShader*)(getData()); swap32(geometry_shader, sizeof(GX2GeometryShader)); size += geometry_shader->numUniformBlocks * sizeof(GX2UniformBlock) + @@ -110,7 +245,7 @@ void ResShaderBinary::setUp() { #ifdef cafe switch (getShaderType()) { case cShaderType_Vertex: { - GX2VertexShader* vertex_shader = static_cast(getData()); + GX2VertexShader* vertex_shader = (GX2VertexShader*)(getData()); vertex_shader->uniformBlocks = modifyBinaryAndNamePtr( vertex_shader, vertex_shader->uniformBlocks, vertex_shader->numUniformBlocks); @@ -125,7 +260,7 @@ void ResShaderBinary::setUp() { vertex_shader->shaderPtr = modifyBinaryPtr(vertex_shader, vertex_shader->shaderPtr); } break; case cShaderType_Fragment: { - GX2PixelShader* pixel_shader = static_cast(getData()); + GX2PixelShader* pixel_shader = (GX2PixelShader*)(getData()); pixel_shader->uniformBlocks = modifyBinaryAndNamePtr( pixel_shader, pixel_shader->uniformBlocks, pixel_shader->numUniformBlocks); @@ -138,7 +273,7 @@ void ResShaderBinary::setUp() { pixel_shader->shaderPtr = modifyBinaryPtr(pixel_shader, pixel_shader->shaderPtr); } break; case cShaderType_Geometry: { - GX2GeometryShader* geometry_shader = static_cast(getData()); + GX2GeometryShader* geometry_shader = (GX2GeometryShader*)(getData()); geometry_shader->uniformBlocks = modifyBinaryAndNamePtr( geometry_shader, geometry_shader->uniformBlocks, geometry_shader->numUniformBlocks); @@ -159,7 +294,7 @@ void ResShaderBinary::setUp() { const char* ResShaderVariation::getID() const { const char* value = getName() + ref().mNameLen; - for (s32 i = 0, index = static_cast(ref().mValueNum);; i++) { + for (s32 i = 0, index = (s32)(ref().mValueNum);; i++) { while (*value == '\0') value++; @@ -175,7 +310,7 @@ const char* ResShaderVariation::getID() const { const char* ResShaderVariation::getValue(s32 index) const { // clang-format off - SEAD_ASSERT(0 <= index && index < static_cast< int >( ref().mValueNum )); + SEAD_ASSERT(0 <= index && index < (int)( ref().mValueNum )); // clang-format on const char* value = getName() + ref().mNameLen; @@ -204,33 +339,22 @@ ResShaderSymbol ResShaderSymbolArray::searchResShaderSymbolByID(const sead::Safe return nullptr; } -// NON_MATCHING: weird optimizations with bit magic to tell whether more than one loop iteration has -// to be done ResShaderMacroArray ResShaderProgram::getResShaderMacroArray(ShaderType type) const { - const ResShaderMacroArrayData* macro_array; - { - const DataType* const data = ptr(); - macro_array = (const ResShaderMacroArrayData*)((uintptr_t)(data + 1) + data->mNameLen); - } - - for (s32 i = 0; i < type; i++) - macro_array = (const ResShaderMacroArrayData*)((uintptr_t)macro_array + macro_array->mSize); - - return macro_array; + const DataType* const data = ptr(); + const char* address = (const char*)(data + 1) + data->mNameLen; + for (s32 i = 0; i < type; ++i) + address += *(const s32*)(address); + return (const ResShaderMacroArrayData*)(address); } -// NON_MATCHING: operand order in ADD ResShaderVariationArray ResShaderProgram::getResShaderVariationArray() const { - const ResShaderMacroArrayData* macro_array; - { - const DataType* const data = ptr(); - macro_array = (const ResShaderMacroArrayData*)((uintptr_t)(data + 1) + data->mNameLen); - } - - for (s32 i = 0; i < cShaderType_Num; i++) - macro_array = (const ResShaderMacroArrayData*)((uintptr_t)macro_array + macro_array->mSize); - - return (const ResShaderVariationArrayData*)macro_array; + const DataType* const data = ptr(); + uintptr_t address = (uintptr_t)(data + 1) + data->mNameLen; + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + return (const ResShaderVariationArrayData*)(address); } // unknown state, does not exist in SMO @@ -263,113 +387,198 @@ ResShaderSymbolArray ResBinaryShaderProgram::getResShaderSymbolArray(ShaderSymbo return symbol_array; } -// NON_MATCHING: heavily depends on the two (mismatching) functions above, probably a lot of -// mismatches carried over bool ResShaderArchive::setUp() { #ifdef cafe SEAD_ASSERT(isValid()); #endif - if (!isEndianResolved()) { -#ifdef cafe - ModifyEndianU32(modifyEndian(), ptr(), sizeof(DataType)); - - verify(); -#endif -#ifdef SWITCH - ModifyEndianU32(false, ptr(), sizeof(DataType)); -#endif - - ResShaderProgramArray prog_arr = getResShaderProgramArray(); - prog_arr.modifyEndianArray(modifyEndian()); + if ((ref().mEndian & 1) != 0) + return true; - ResShaderSourceArray source_arr = getResShaderSourceArray(); - source_arr.modifyEndianArray(modifyEndian()); + ModifyEndianU32(false, ptr(), sizeof(DataType)); - for (ResShaderProgramArray::iterator it = prog_arr.begin(), it_end = prog_arr.end(); - it != it_end; ++it) { - ResShaderProgram prog(&(*it)); + ResShaderProgramArray program_array = getResShaderProgramArray(); + program_array.modifyEndianArray(false); - for (s32 type = 0; type < cShaderType_Num; type++) - prog.getResShaderMacroArray(ShaderType(type)).modifyEndianArray(modifyEndian()); + getResShaderSourceArray().modifyEndianArray(false); - prog.getResShaderVariationArray().modifyEndianArray(modifyEndian()); - prog.getResShaderVariationDefaultArray().modifyEndianArray(modifyEndian()); + for (ResShaderProgramArray::iterator it = program_array.begin(), it_end = program_array.end(); + it != it_end; ++it) { + ResShaderProgram program(&(*it)); - for (s32 type = 0; type < cShaderSymbolType_Num; type++) - modifyEndianResSymbolArray(modifyEndian(), - prog.getResShaderSymbolArray(ShaderSymbolType(type)), - ShaderSymbolType(type)); + program.getResShaderMacroArray(cShaderType_Vertex).modifyEndianArray(false); + program.getResShaderMacroArray(cShaderType_Fragment).modifyEndianArray(false); + { + const ResShaderProgramData* data = program.ptr(); + const u8* address = (const u8*)(data + 1) + data->mNameLen; + address += *(const s32*)(address); + address += *(const s32*)(address); + ResShaderMacroArray(address).modifyEndianArray(false); + } + { + const ResShaderProgramData* data = program.ptr(); + const u8* address = (const u8*)(data + 1) + data->mNameLen; + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + ResShaderMacroArray(address).modifyEndianArray(false); + } + { + const ResShaderProgramData* data = program.ptr(); + const u8* address = (const u8*)(data + 1); + address += data->mNameLen; + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + ResShaderVariationArray(address).modifyEndianArray(false); + } + { + const ResShaderProgramData* data = program.ptr(); + const u8* address = (const u8*)(data + 1); + address += data->mNameLen; + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + ResShaderVariationArray(address).modifyEndianArray(false); + } + { + const ResShaderProgramData* data = program.ptr(); + const u8* address = (const u8*)(data + 1); + address += data->mNameLen; + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + address += *(const s32*)(address); + modifyEndianResShaderArchiveSymbolArray( + (ResBinaryShaderSymbolArrayData*)address); } - - setEndianResolved(); - } -#ifdef cafe - else { - verify(); } -#endif + ResShaderSourceArray source_array = getResShaderSourceArray(); + ResShaderSourceArray final_source_array((u8*)(source_array.ptr()) + + source_array.ref().mSize); + final_source_array.modifyEndianArray(false); + + ref().mEndian |= 1; return true; } -// NON_MATCHING: also heavily depends on the two (mismatching) functions above, probably a lot of -// mismatches carried over +void ResBinaryShaderArchive::createMemoryPoolBuffer_() { + u8* storage = (u8*)((uintptr_t)ref().mMemoryPoolOffset + (uintptr_t)ptrBytes()); + driver::NVNMgr* nvn_mgr = driver::NVNMgr::instance(); + NVNmemoryPool* pool = &ref().mMemoryPool; + NVNdevice* device = nvn_mgr->getDevice(); + + NVNmemoryPoolBuilder pool_builder; + nvnMemoryPoolBuilderSetDefaults(&pool_builder); + nvnMemoryPoolBuilderSetDevice(&pool_builder, device); + nvnMemoryPoolBuilderSetFlags(&pool_builder, 0x62); + nvnMemoryPoolBuilderSetStorage( + &pool_builder, storage, + ((size_t)ref().mMemoryPoolSize + 0xFFF) & ~(size_t)0xFFF); + nvnMemoryPoolInitialize(pool, &pool_builder); + + NVNbufferBuilder buffer_builder; + nvnBufferBuilderSetDevice(&buffer_builder, device); + nvnBufferBuilderSetDefaults(&buffer_builder); + nvnBufferBuilderSetStorage( + &buffer_builder, pool, 0, + ((size_t)ref().mMemoryPoolSize + 0xFFF) & ~(size_t)0xFFF); + nvnBufferInitialize(&ref().mBuffer, &buffer_builder); +} + +// NON_MATCHING: Retail extracts the pointer-base flag with UBFX; LLVM emits an equivalent AND. +// Next hypothesis: recover the original flag accessor that returns a normalized bit. bool ResBinaryShaderArchive::setUp(bool le_resolve_pointers) { SEAD_ASSERT(isValid()); - bool endian_resolved = isEndianResolved(); - + const u32 endian_resolved = ref().mEndian & sead::Endian::getHostEndian(); if (!endian_resolved) - ModifyEndianU32(modifyEndian(), ptr(), sizeof(DataType)); + ModifyEndianU32(false, ptr(), sizeof(DataType)); - verify(); + createMemoryPoolBuffer_(); + const u32 flags = ref().mEndian; + ResShaderBinaryArray binary_arr = getResShaderBinaryArray(); if (endian_resolved) { - if (ref().mResolved == 0) { - for (ResShaderBinaryArray::iterator it = getResShaderBinaryArray().begin(), - it_end = getResShaderBinaryArray().end(); - it != it_end; ++it) - ResShaderBinary(&(*it)).setUp(); - - ref().mResolved = 1; + if ((flags & 2) != 0) + return true; + + const u32 binary_num = binary_arr.ref().mNum; + if (binary_num != 0) { + if ((flags & 4) != 0) { + for (ResShaderBinaryArray::iterator it = binary_arr.begin(), it_end = binary_arr.end(); + it != it_end; ++it) { + ResShaderBinary binary(&(*it)); + binary.resolvePtr(ptrBytes() + sizeof(DataType)); + } + } else { + for (ResShaderBinaryArray::iterator it = binary_arr.begin(), it_end = binary_arr.end(); + it != it_end; ++it) { + ResShaderBinary binary(&(*it)); + binary.resolvePtr(nullptr); + } + } } - } else { - ResShaderBinaryArray binary_arr = getResShaderBinaryArray(); - binary_arr.modifyEndianArray(modifyEndian()); - ResBinaryShaderProgramArray binary_prog_arr = getResBinaryShaderProgramArray(); - binary_prog_arr.modifyEndianArray(modifyEndian()); - - for (ResBinaryShaderProgramArray::iterator it = binary_prog_arr.begin(), - it_end = binary_prog_arr.end(); - it != it_end; ++it) { - ResBinaryShaderProgram binary_prog(&(*it)); - - binary_prog.getResShaderVariationArray().modifyEndianArray(modifyEndian()); - binary_prog.getResShaderVariationDefaultArray().modifyEndianArray(modifyEndian()); - - for (s32 type = 0; type < cShaderSymbolType_Num; type++) - modifyEndianResSymbolArray( - modifyEndian(), binary_prog.getResShaderSymbolArray(ShaderSymbolType(type)), - ShaderSymbolType(type)); - } + ref().mEndian |= 2; + return true; + } - for (ResShaderBinaryArray::iterator it = binary_arr.begin(), it_end = binary_arr.end(); - it != it_end; ++it) { - ResShaderBinary binary(&(*it)); - binary.modifyBinaryEndian(); + binary_arr.modifyEndianArray(false); + + ResBinaryShaderProgramArray binary_prog_arr = getResBinaryShaderProgramArray(); + binary_prog_arr.modifyEndianArray(false); + + ResBinaryShaderProgramData* binary_prog_data = + (ResBinaryShaderProgramData*)(binary_prog_arr.ptr() + 1); + const u32 binary_prog_num = binary_prog_arr.ref().mNum; + for (u32 i = 0; i != binary_prog_num; + ++i, binary_prog_data = + (ResBinaryShaderProgramData*)((u8*)binary_prog_data + binary_prog_data->mSize)) { + ResBinaryShaderProgram binary_prog(binary_prog_data); + + binary_prog.getResShaderVariationArray().modifyEndianArray(false); + ResShaderVariationArray variation_default_arr = + binary_prog.getResShaderVariationDefaultArray(); + variation_default_arr.modifyEndianArray(false); + + const u8* variation_data = (const u8*)variation_default_arr.ptr(); + const u8* symbol_address = &variation_data[variation_default_arr.ref().mSize]; + ResBinaryShaderSymbolArrayData* symbol_array = + (ResBinaryShaderSymbolArrayData*)symbol_address; + modifyEndianResBinarySymbolArray(symbol_array); + } - if (le_resolve_pointers && ref().mResolved == 0) - binary.setUp(); + ResShaderBinaryArray::iterator it = binary_arr.begin(); + ResShaderBinaryArray::iterator it_end = binary_arr.end(); + const u32 pointer_base = (flags >> 2) & 1; + const bool pointers_resolved = ((flags >> 1) & 1) != 0; + for (; it != it_end; ++it) { + ResShaderBinary binary(&(*it)); + binary.modifyBinaryEndian(); + + if (le_resolve_pointers) { + if (!pointers_resolved) { + if (pointer_base) + binary.resolvePtr(ptrBytes() + sizeof(DataType)); + else + binary.resolvePtr(nullptr); + } } + } - if (le_resolve_pointers) - ref().mResolved = 1; - - setEndianResolved(); + if (le_resolve_pointers) { + if (!pointers_resolved) + ref().mEndian |= 2; } + ref().mEndian |= 1; return true; } From e9dfbb8eefdf27175e6b38482f006493c9228aca Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Thu, 16 Jul 2026 08:47:03 +0000 Subject: [PATCH 2/8] Improve parameter and shader archive mismatches --- include/utility/aglParameterCurve.hpp | 14 +++++++++----- src/utility/aglParameterList.cpp | 4 ++-- src/utility/aglParameterObj.cpp | 5 ++--- src/utility/common/aglResShaderArchive.cpp | 3 +-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/utility/aglParameterCurve.hpp b/include/utility/aglParameterCurve.hpp index 8daa409..933ce9e 100644 --- a/include/utility/aglParameterCurve.hpp +++ b/include/utility/aglParameterCurve.hpp @@ -109,19 +109,23 @@ inline ParameterBase* ParameterCurve::clone(sead::Heap* heap, IParameterObj* } template -// NON_MATCHING: N=2/3/4 behavior and baseline sizes are correct, but retail unrolls pointer/metadata stores in a different schedule. Pointer-first ordering shrinks the functions by hoisting a common store, while setData/setFloats expands them. Next hypothesis is an explicit per-instantiation assignment order that avoids both common-store hoisting and helper write-back. +// NON_MATCHING: best faithful N-specific ordering scores N=2/3/4 at 475/720/1245; retail still duplicates/schedules late pointer stores differently. Next hypothesis: recover an original accessor or alias boundary that prevents cross-branch tail merging without helper write-back. inline void ParameterCurve::postApplyResource_(const void*, size_t size) { if (this->size() == size) { for (u32 i = 0; i < N; ++i) { mCurves[i].setCurveType(sead::hostio::CurveType(mCurveData[i].curveType)); - mCurves[i].mFloats = mCurveData[i].f; - mCurves[i].mInfo.numFloats = cUnitCurveParamNum; mCurves[i].setNumUse(mCurveData[i].numUse); + mCurves[i].setFloats(&mCurveData[i], cUnitCurveParamNum); } } else { for (u32 i = 0; i < N; ++i) { - mCurves[i].mInfo.numFloats = cUnitCurveParamNum; - mCurves[i].mFloats = mCurveData[i].f; + if constexpr (N == 2) { + mCurves[i].mFloats = mCurveData[i].f; + mCurves[i].mInfo.numFloats = cUnitCurveParamNum; + } else { + mCurves[i].mInfo.numFloats = cUnitCurveParamNum; + mCurves[i].mFloats = mCurveData[i].f; + } } } } diff --git a/src/utility/aglParameterList.cpp b/src/utility/aglParameterList.cpp index e57f7e6..bbef161 100644 --- a/src/utility/aglParameterList.cpp +++ b/src/utility/aglParameterList.cpp @@ -191,7 +191,7 @@ bool IParameterList::verifyObj() const { return ret; } -// NON_MATCHING: Retail shares the return epilogue with the adjacent verification function. +// NON_MATCHING: retail branches from loop exit to one shared RET; Clang tail-duplicates RET. Next hypothesis: recover a source or compilation-unit lifetime boundary that inhibits tail duplication without adding runtime work. bool IParameterList::verifyList(IParameterList* p_check, IParameterList* other) const { SEAD_ASSERT(p_check != nullptr); bool ok = true; @@ -202,7 +202,7 @@ bool IParameterList::verifyList(IParameterList* p_check, IParameterList* other) return ok; } -// NON_MATCHING: Retail shares the return epilogue with the adjacent verification function. +// NON_MATCHING: retail branches from loop exit to one shared RET; Clang tail-duplicates RET. Next hypothesis: recover a source or compilation-unit lifetime boundary that inhibits tail duplication without adding runtime work. bool IParameterList::verifyObj(IParameterObj* p_check, IParameterObj* other) const { SEAD_ASSERT(p_check != nullptr); bool ok = true; diff --git a/src/utility/aglParameterObj.cpp b/src/utility/aglParameterObj.cpp index babb552..e7abd6f 100644 --- a/src/utility/aglParameterObj.cpp +++ b/src/utility/aglParameterObj.cpp @@ -85,7 +85,7 @@ bool IParameterObj::verify() const { return ret; } -// NON_MATCHING: Retail shares the return epilogue with the adjacent verification function. +// NON_MATCHING: retail branches from loop exit to one shared RET; Clang tail-duplicates RET. Next hypothesis: recover a source or compilation-unit lifetime boundary that inhibits tail duplication without adding runtime work. bool IParameterObj::verify(ParameterBase* p_check, ParameterBase* other) const { SEAD_ASSERT(p_check != nullptr); bool ok = true; @@ -177,8 +177,7 @@ void IParameterObj::copyLerp(ParameterBase* first, ParameterBase* last, postCopy_(); } -// NON_MATCHING: Retail tests mParamListHead before the source-list pointer. -// Next hypothesis: alter local lifetimes to recover the X20/X21 assignment without changing the CFG. +// NON_MATCHING: retail checks X20 (destination head) before X21 (source head); current emits the two CBZs reversed. Next hypothesis: recover original local-lifetime spelling without extra CFG. void IParameterObj::copyLerp(const IParameterObj& obj1, const IParameterObj& obj2, f32 t) { if (!preCopy_()) return; diff --git a/src/utility/common/aglResShaderArchive.cpp b/src/utility/common/aglResShaderArchive.cpp index a4bff20..c5751f6 100644 --- a/src/utility/common/aglResShaderArchive.cpp +++ b/src/utility/common/aglResShaderArchive.cpp @@ -492,8 +492,7 @@ void ResBinaryShaderArchive::createMemoryPoolBuffer_() { nvnBufferInitialize(&ref().mBuffer, &buffer_builder); } -// NON_MATCHING: Retail extracts the pointer-base flag with UBFX; LLVM emits an equivalent AND. -// Next hypothesis: recover the original flag accessor that returns a normalized bit. +// NON_MATCHING: retail emits UBFX W10,W9,#2,#1; Clang canonicalizes the faithful 0/1 extraction to AND W10,W9,#4 because the value is only tested. Next hypothesis: recover the original flag wrapper/value use that required normalization. bool ResBinaryShaderArchive::setUp(bool le_resolve_pointers) { SEAD_ASSERT(isValid()); From d806d3e885ec6485d7543d2e7c5594769b4412f5 Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Thu, 16 Jul 2026 10:51:54 +0000 Subject: [PATCH 3/8] Match ResBinaryShaderArchive setup --- src/utility/common/aglResShaderArchive.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utility/common/aglResShaderArchive.cpp b/src/utility/common/aglResShaderArchive.cpp index c5751f6..68ab13c 100644 --- a/src/utility/common/aglResShaderArchive.cpp +++ b/src/utility/common/aglResShaderArchive.cpp @@ -492,7 +492,6 @@ void ResBinaryShaderArchive::createMemoryPoolBuffer_() { nvnBufferInitialize(&ref().mBuffer, &buffer_builder); } -// NON_MATCHING: retail emits UBFX W10,W9,#2,#1; Clang canonicalizes the faithful 0/1 extraction to AND W10,W9,#4 because the value is only tested. Next hypothesis: recover the original flag wrapper/value use that required normalization. bool ResBinaryShaderArchive::setUp(bool le_resolve_pointers) { SEAD_ASSERT(isValid()); @@ -503,6 +502,7 @@ bool ResBinaryShaderArchive::setUp(bool le_resolve_pointers) { createMemoryPoolBuffer_(); const u32 flags = ref().mEndian; + const bool pointer_base = ((flags >> 2) & 1) != 0; ResShaderBinaryArray binary_arr = getResShaderBinaryArray(); if (endian_resolved) { if ((flags & 2) != 0) @@ -510,7 +510,7 @@ bool ResBinaryShaderArchive::setUp(bool le_resolve_pointers) { const u32 binary_num = binary_arr.ref().mNum; if (binary_num != 0) { - if ((flags & 4) != 0) { + if (pointer_base) { for (ResShaderBinaryArray::iterator it = binary_arr.begin(), it_end = binary_arr.end(); it != it_end; ++it) { ResShaderBinary binary(&(*it)); @@ -556,7 +556,6 @@ bool ResBinaryShaderArchive::setUp(bool le_resolve_pointers) { ResShaderBinaryArray::iterator it = binary_arr.begin(); ResShaderBinaryArray::iterator it_end = binary_arr.end(); - const u32 pointer_base = (flags >> 2) & 1; const bool pointers_resolved = ((flags >> 1) & 1) != 0; for (; it != it_end; ++it) { ResShaderBinary binary(&(*it)); From cf3effb61ef6984062c334e2e3cfdc3b1cd59793 Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Thu, 16 Jul 2026 12:58:52 +0000 Subject: [PATCH 4/8] Match five parameter functions --- include/utility/aglParameterCurve.hpp | 65 +++++++++++++++++++++++---- src/utility/aglParameterList.cpp | 14 ++---- src/utility/aglParameterObj.cpp | 10 +---- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/include/utility/aglParameterCurve.hpp b/include/utility/aglParameterCurve.hpp index 933ce9e..200b7e2 100644 --- a/include/utility/aglParameterCurve.hpp +++ b/include/utility/aglParameterCurve.hpp @@ -109,23 +109,70 @@ inline ParameterBase* ParameterCurve::clone(sead::Heap* heap, IParameterObj* } template -// NON_MATCHING: best faithful N-specific ordering scores N=2/3/4 at 475/720/1245; retail still duplicates/schedules late pointer stores differently. Next hypothesis: recover an original accessor or alias boundary that prevents cross-branch tail merging without helper write-back. +// NON_MATCHING: Curve<2>/<4> are exact; Curve<3> retains one equal-size-path scheduling residual. Next hypothesis: recover the N=3 source lifetime that holds the second and third float pointers without introducing an extra base register. inline void ParameterCurve::postApplyResource_(const void*, size_t size) { if (this->size() == size) { for (u32 i = 0; i < N; ++i) { - mCurves[i].setCurveType(sead::hostio::CurveType(mCurveData[i].curveType)); - mCurves[i].setNumUse(mCurveData[i].numUse); - mCurves[i].setFloats(&mCurveData[i], cUnitCurveParamNum); + auto& curve = mCurves[i]; + auto& data = mCurveData[i]; + if constexpr (N == 1) { + mCurves[i].setCurveType(sead::hostio::CurveType(mCurveData[i].curveType)); + mCurves[i].setNumUse(mCurveData[i].numUse); + mCurves[i].setFloats(&mCurveData[i], cUnitCurveParamNum); + } else if constexpr (N == 3) { + const auto type = sead::hostio::CurveType(data.curveType); + curve.setCurveType(type); + auto* floats = data.f; + curve.mInfo.numFloats = cUnitCurveParamNum; + curve.mFloats = floats; + curve.setNumUse(data.numUse); + } else { + auto* floats = data.f; + const auto type = sead::hostio::CurveType(data.curveType); + curve.mInfo.curveType = u8(type); + curve.mInfo.numFloats = cUnitCurveParamNum; + curve.mFloats = floats; + const u32 num_use = data.numUse; + curve.mInfo.numUse = u8(num_use); + } } } else { - for (u32 i = 0; i < N; ++i) { - if constexpr (N == 2) { - mCurves[i].mFloats = mCurveData[i].f; - mCurves[i].mInfo.numFloats = cUnitCurveParamNum; - } else { + if constexpr (N == 1) { + for (u32 i = 0; i < N; ++i) { mCurves[i].mInfo.numFloats = cUnitCurveParamNum; mCurves[i].mFloats = mCurveData[i].f; } + } else if constexpr (N == 2) { + mCurves[0].mInfo.numFloats = cUnitCurveParamNum; + mCurves[0].mFloats = mCurveData[0].f; + mCurves[1].mFloats = mCurveData[1].f; + mCurves[1].mInfo.numFloats = cUnitCurveParamNum; + } else if constexpr (N == 3) { + auto* floats0 = mCurveData[0].f; + mCurves[0].mInfo.numFloats = cUnitCurveParamNum; + auto* floats1 = mCurveData[1].f; + mCurves[0].mFloats = floats0; + auto* floats2 = mCurveData[2].f; + mCurves[1].mInfo.numFloats = cUnitCurveParamNum; + mCurves[1].mFloats = floats1; + mCurves[2].mInfo.numFloats = cUnitCurveParamNum; + mCurves[2].mFloats = floats2; + } else if constexpr (N == 4) { + mCurves[0].mInfo.numFloats = cUnitCurveParamNum; + mCurves[0].mFloats = mCurveData[0].f; + auto* floats1 = mCurveData[1].f; + mCurves[1].mInfo.numFloats = cUnitCurveParamNum; + mCurves[1].mFloats = floats1; + mCurves[2].mFloats = mCurveData[2].f; + mCurves[2].mInfo.numFloats = cUnitCurveParamNum; + auto* floats3 = mCurveData[3].f; + mCurves[3].mInfo.numFloats = cUnitCurveParamNum; + mCurves[3].mFloats = floats3; + } else { + for (u32 i = 0; i < N; ++i) { + mCurves[i].mFloats = mCurveData[i].f; + mCurves[i].mInfo.numFloats = cUnitCurveParamNum; + } } } } diff --git a/src/utility/aglParameterList.cpp b/src/utility/aglParameterList.cpp index bbef161..2df8013 100644 --- a/src/utility/aglParameterList.cpp +++ b/src/utility/aglParameterList.cpp @@ -191,25 +191,19 @@ bool IParameterList::verifyObj() const { return ret; } -// NON_MATCHING: retail branches from loop exit to one shared RET; Clang tail-duplicates RET. Next hypothesis: recover a source or compilation-unit lifetime boundary that inhibits tail duplication without adding runtime work. bool IParameterList::verifyList(IParameterList* p_check, IParameterList* other) const { SEAD_ASSERT(p_check != nullptr); bool ok = true; - while (other) { - ok &= p_check->getNameHash() != other->getNameHash(); - other = other->mNext; - } + for (const IParameterList* list = other; list; list = list->mNext) + ok &= p_check->getNameHash() != list->getNameHash(); return ok; } -// NON_MATCHING: retail branches from loop exit to one shared RET; Clang tail-duplicates RET. Next hypothesis: recover a source or compilation-unit lifetime boundary that inhibits tail duplication without adding runtime work. bool IParameterList::verifyObj(IParameterObj* p_check, IParameterObj* other) const { SEAD_ASSERT(p_check != nullptr); bool ok = true; - while (other) { - ok &= p_check->getNameHash() != other->getNameHash(); - other = other->mNext; - } + for (const IParameterObj* obj = other; obj; obj = obj->mNext) + ok &= p_check->getNameHash() != obj->getNameHash(); return ok; } diff --git a/src/utility/aglParameterObj.cpp b/src/utility/aglParameterObj.cpp index e7abd6f..253830f 100644 --- a/src/utility/aglParameterObj.cpp +++ b/src/utility/aglParameterObj.cpp @@ -85,17 +85,11 @@ bool IParameterObj::verify() const { return ret; } -// NON_MATCHING: retail branches from loop exit to one shared RET; Clang tail-duplicates RET. Next hypothesis: recover a source or compilation-unit lifetime boundary that inhibits tail duplication without adding runtime work. bool IParameterObj::verify(ParameterBase* p_check, ParameterBase* other) const { SEAD_ASSERT(p_check != nullptr); bool ok = true; - if (other) { - const u32 hash = p_check->getNameHash(); - do { - ok &= hash != other->getNameHash(); - other = other->mNext; - } while (other); - } + for (const ParameterBase* param = other; param; param = param->mNext) + ok &= p_check->getNameHash() != param->getNameHash(); return ok; } From 5260dcd8b1ccc8bc1ed93d836f18fadaf39c78d5 Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Thu, 16 Jul 2026 22:50:17 +0000 Subject: [PATCH 5/8] Match remaining curve and lerp functions --- include/utility/aglParameterCurve.hpp | 49 +++++++++++++++++---------- src/utility/aglParameterObj.cpp | 5 ++- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/include/utility/aglParameterCurve.hpp b/include/utility/aglParameterCurve.hpp index 200b7e2..deeb74a 100644 --- a/include/utility/aglParameterCurve.hpp +++ b/include/utility/aglParameterCurve.hpp @@ -109,31 +109,44 @@ inline ParameterBase* ParameterCurve::clone(sead::Heap* heap, IParameterObj* } template -// NON_MATCHING: Curve<2>/<4> are exact; Curve<3> retains one equal-size-path scheduling residual. Next hypothesis: recover the N=3 source lifetime that holds the second and third float pointers without introducing an extra base register. inline void ParameterCurve::postApplyResource_(const void*, size_t size) { if (this->size() == size) { - for (u32 i = 0; i < N; ++i) { - auto& curve = mCurves[i]; - auto& data = mCurveData[i]; - if constexpr (N == 1) { - mCurves[i].setCurveType(sead::hostio::CurveType(mCurveData[i].curveType)); - mCurves[i].setNumUse(mCurveData[i].numUse); - mCurves[i].setFloats(&mCurveData[i], cUnitCurveParamNum); - } else if constexpr (N == 3) { + if constexpr (N == 3) { + for (u32 i = 0; i < 2; ++i) { + auto& curve = mCurves[i]; + auto& data = mCurveData[i]; const auto type = sead::hostio::CurveType(data.curveType); curve.setCurveType(type); auto* floats = data.f; - curve.mInfo.numFloats = cUnitCurveParamNum; - curve.mFloats = floats; curve.setNumUse(data.numUse); - } else { - auto* floats = data.f; - const auto type = sead::hostio::CurveType(data.curveType); - curve.mInfo.curveType = u8(type); - curve.mInfo.numFloats = cUnitCurveParamNum; curve.mFloats = floats; - const u32 num_use = data.numUse; - curve.mInfo.numUse = u8(num_use); + curve.mInfo.numFloats = cUnitCurveParamNum; + } + auto& curve = mCurves[2]; + auto& data = mCurveData[2]; + auto* floats = data.f; + const auto type = sead::hostio::CurveType(data.curveType); + curve.setCurveType(type); + curve.mFloats = floats; + curve.setNumUse(data.numUse); + curve.mInfo.numFloats = cUnitCurveParamNum; + } else { + for (u32 i = 0; i < N; ++i) { + auto& curve = mCurves[i]; + auto& data = mCurveData[i]; + if constexpr (N == 1) { + mCurves[i].setCurveType(sead::hostio::CurveType(mCurveData[i].curveType)); + mCurves[i].setNumUse(mCurveData[i].numUse); + mCurves[i].setFloats(&mCurveData[i], cUnitCurveParamNum); + } else { + auto* floats = data.f; + const auto type = sead::hostio::CurveType(data.curveType); + curve.mInfo.curveType = u8(type); + curve.mInfo.numFloats = cUnitCurveParamNum; + curve.mFloats = floats; + const u32 num_use = data.numUse; + curve.mInfo.numUse = u8(num_use); + } } } } else { diff --git a/src/utility/aglParameterObj.cpp b/src/utility/aglParameterObj.cpp index 253830f..2517a8d 100644 --- a/src/utility/aglParameterObj.cpp +++ b/src/utility/aglParameterObj.cpp @@ -171,17 +171,16 @@ void IParameterObj::copyLerp(ParameterBase* first, ParameterBase* last, postCopy_(); } -// NON_MATCHING: retail checks X20 (destination head) before X21 (source head); current emits the two CBZs reversed. Next hypothesis: recover original local-lifetime spelling without extra CFG. void IParameterObj::copyLerp(const IParameterObj& obj1, const IParameterObj& obj2, f32 t) { if (!preCopy_()) return; - auto mpHead = mParamListHead; + auto* it1 = obj1.mParamListHead; + auto* mpHead = mParamListHead; SEAD_ASSERT(mpHead); const u32 hash = mpHead->getNameHash(); - auto* it1 = obj1.mParamListHead; while (it1 && it1->getNameHash() != hash) it1 = it1->mNext; From 2bad83e086d0520dfba8b9a94f7bea9cd644a8ba Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Thu, 16 Jul 2026 20:43:07 -0400 Subject: [PATCH 6/8] remove hack --- include/utility/aglParameterCurve.hpp | 3 --- src/utility/aglParameterCurve.cpp | 8 -------- 2 files changed, 11 deletions(-) delete mode 100644 src/utility/aglParameterCurve.cpp diff --git a/include/utility/aglParameterCurve.hpp b/include/utility/aglParameterCurve.hpp index deeb74a..8ea6b1b 100644 --- a/include/utility/aglParameterCurve.hpp +++ b/include/utility/aglParameterCurve.hpp @@ -190,7 +190,4 @@ inline void ParameterCurve::postApplyResource_(const void*, size_t size) { } } -extern template bool ParameterCurve<1>::copy(const ParameterBase&); -extern template ParameterBase* ParameterCurve<1>::clone(sead::Heap*, IParameterObj*) const; - } // namespace agl::utl diff --git a/src/utility/aglParameterCurve.cpp b/src/utility/aglParameterCurve.cpp deleted file mode 100644 index 1c7a070..0000000 --- a/src/utility/aglParameterCurve.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "utility/aglParameter.h" - -namespace agl::utl { - -template bool ParameterCurve<1>::copy(const ParameterBase&); -template ParameterBase* ParameterCurve<1>::clone(sead::Heap*, IParameterObj*) const; - -} // namespace agl::utl From 0ad676cb1f1aa894ac64c4b337741e5897e85c1f Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Fri, 17 Jul 2026 00:55:32 +0000 Subject: [PATCH 7/8] copy sead's versioning system in --- CMakeLists.txt | 2 ++ include/aglVersion.h | 13 +++++++++++++ include/utility/aglParameterCurve.hpp | 8 ++++++++ src/utility/aglParameter.cpp | 12 ++++++++++++ src/utility/aglParameterObj.cpp | 13 +++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 include/aglVersion.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 29321bc..9523bf1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ project(agl CXX ASM) add_library(agl OBJECT + include/aglVersion.h + include/common/aglDisplayList.h include/common/aglDrawContext.h include/common/aglGPUCommon.hpp diff --git a/include/aglVersion.h b/include/aglVersion.h new file mode 100644 index 0000000..a825725 --- /dev/null +++ b/include/aglVersion.h @@ -0,0 +1,13 @@ +#pragma once + +#define AGL_VERSION_BOTW 1 +#define AGL_VERSION_SMO 2 +#define AGL_VERSION_SPL3 3 +#define AGL_VERSION_TOTK 4 +#define AGL_VERSION_SMBW 5 +#define AGL_VERSION_SMM2 6 +#define AGL_VERSION_CUSTOM 0 + +#ifndef AGL_VERSION +#define AGL_VERSION AGL_VERSION_BOTW +#endif diff --git a/include/utility/aglParameterCurve.hpp b/include/utility/aglParameterCurve.hpp index 8ea6b1b..4b179cc 100644 --- a/include/utility/aglParameterCurve.hpp +++ b/include/utility/aglParameterCurve.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #ifndef AGL_UTILS_PARAMETER_H_ #include "utility/aglParameter.h" #endif @@ -136,8 +138,14 @@ inline void ParameterCurve::postApplyResource_(const void*, size_t size) { auto& data = mCurveData[i]; if constexpr (N == 1) { mCurves[i].setCurveType(sead::hostio::CurveType(mCurveData[i].curveType)); +#if AGL_VERSION == AGL_VERSION_BOTW + mCurves[i].mFloats = mCurveData[i].f; + mCurves[i].mInfo.numFloats = cUnitCurveParamNum; + mCurves[i].setNumUse(mCurveData[i].numUse); +#else mCurves[i].setNumUse(mCurveData[i].numUse); mCurves[i].setFloats(&mCurveData[i], cUnitCurveParamNum); +#endif } else { auto* floats = data.f; const auto type = sead::hostio::CurveType(data.curveType); diff --git a/src/utility/aglParameter.cpp b/src/utility/aglParameter.cpp index 4cb417c..0133822 100644 --- a/src/utility/aglParameter.cpp +++ b/src/utility/aglParameter.cpp @@ -1,4 +1,5 @@ #include "utility/aglParameter.h" +#include #include #include #include @@ -137,19 +138,30 @@ bool ParameterBase::copy(const ParameterBase& other) { void ParameterBase::copyUnsafe(const ParameterBase& other) { if (other.getParameterType() == ParameterType::StringRef) { +#if AGL_VERSION == AGL_VERSION_BOTW + auto* source = static_cast(other.typePtr()); + auto* dest = static_cast(typePtr()); + *dest = *source; +#else static_cast(typePtr())->operator=( *static_cast(other.typePtr())); +#endif return; } auto* dest = ptrT(); auto* src = other.ptrT(); const s32 n = size(); +#if AGL_VERSION == AGL_VERSION_BOTW + for (s32 i = 0; i < n; ++i) + *dest++ = *src++; +#else for (s32 i = 0; i < n; ++i) { *dest = *src; ++dest; ++src; } +#endif } template <> diff --git a/src/utility/aglParameterObj.cpp b/src/utility/aglParameterObj.cpp index 2517a8d..5946247 100644 --- a/src/utility/aglParameterObj.cpp +++ b/src/utility/aglParameterObj.cpp @@ -1,4 +1,5 @@ #include "utility/aglParameterObj.h" +#include #include #include #include "utility/aglParameter.h" @@ -10,12 +11,24 @@ IParameterObj::IParameterObj() = default; void IParameterObj::pushBackListNode(ParameterBase* p_node) { SEAD_ASSERT(p_node != nullptr); +#if AGL_VERSION == AGL_VERSION_BOTW + ParameterBase** ptr; + if (mParamListTail) { + ptr = &mParamListTail->mNext; + } else { + ptr = &mParamListHead; + mParamListTail = p_node; + } + + *ptr = p_node; +#else if (mParamListTail) mParamListTail->mNext = p_node; else { mParamListHead = p_node; mParamListTail = p_node; } +#endif mParamListTail = p_node; ++mParamListSize; From 6dd81444ae5dee08cb188ba59250acdb11aaaea1 Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Thu, 16 Jul 2026 22:35:57 -0400 Subject: [PATCH 8/8] match ResParameterArchive::ResParameterArchive --- src/utility/aglResParameter.cpp | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/utility/aglResParameter.cpp b/src/utility/aglResParameter.cpp index a9af506..327fd5a 100644 --- a/src/utility/aglResParameter.cpp +++ b/src/utility/aglResParameter.cpp @@ -1,7 +1,7 @@ #include "utility/aglResParameter.h" +#include #include #include -#include #include #include #include @@ -75,58 +75,53 @@ s32 ResParameterList::searchObjIndex(u32 obj_hash) const { return -1; } -// NON_MATCHING: Retail tests the endian flag with TBZ and lays out the early-return block oppositely. -// Next hypothesis: recover the original flag/helper boundary without branch hints. ResParameterArchive::ResParameterArchive(const void* p_data) { mpData = (ResParameterArchiveData*)p_data; if (!mpData) return; const u32 initial_flags = mpData->flags.getDirect(); + const bool little_endian = (initial_flags & u32(ResParameterArchiveFlag::LittleEndian)) != 0; bool utf8; - if ((initial_flags & u32(ResParameterArchiveFlag::LittleEndian)) == 0) { - ModifyEndianU32(false, (ResParameterArchiveData*)mpData, - sizeof(ResParameterArchiveData)); - utf8 = mpData->flags.isOn(ResParameterArchiveFlag::Utf8); - } else { + if (little_endian) { if ((initial_flags & u32(ResParameterArchiveFlag::Utf8)) != 0) return; utf8 = false; + } else { + ModifyEndianU32(false, (ResParameterArchiveData*)mpData, sizeof(ResParameterArchiveData)); + utf8 = mpData->flags.isOn(ResParameterArchiveFlag::Utf8); } const size_t list_size = sizeof(ResParameterListData) * mpData->num_lists; - const size_t object_size = sizeof(ResParameterObjData) * mpData->num_objects; - u8* const parameter_io = ptrBytes() + sizeof(ResParameterArchiveData) + mpData->offset_to_pio; char* string = (char*)(parameter_io + list_size); + const size_t object_size = sizeof(ResParameterObjData) * mpData->num_objects; string += object_size; const size_t parameter_size = sizeof(ResParameterData) * mpData->num_parameters; string += parameter_size; string += mpData->data_section_size; char* const string_end = string + mpData->string_section_size; - if ((initial_flags & u32(ResParameterArchiveFlag::LittleEndian)) == 0) { + if (!little_endian) { const size_t parameter_io_size = list_size + object_size + parameter_size + mpData->data_section_size; if (parameter_io_size != 0) ModifyEndianU32(false, parameter_io, parameter_io_size); const u32 unknown_size = mpData->unk_section_size; - for (u32 offset = 0; offset < unknown_size; - offset += *(u32*)(string_end + offset)) { + for (u32 offset = 0; offset < unknown_size; offset += *(u32*)(string_end + offset)) { ModifyEndianU32(false, string_end + offset, sizeof(u32)); } - ((ResParameterArchiveData*)mpData)->flags.set( - ResParameterArchiveFlag::LittleEndian); + ((ResParameterArchiveData*)mpData)->flags.set(ResParameterArchiveFlag::LittleEndian); } if (!utf8 && mpData->string_section_size != 0) { while (string < string_end) { const s32 source_length = sead::SafeString(string).calcLength(); if (source_length > 0) { - sead::Heap* const heap = detail::PrivateResource::instance()->getWorkHeap(); const s32 source_capacity = source_length + 1; + sead::Heap* const heap = detail::PrivateResource::instance()->getWorkHeap(); char16* const utf16 = new (heap, 8) char16[source_capacity]; const s32 converted_capacity = 2 * source_capacity; char* const converted = new (heap, 8) char[converted_capacity];