Files
bigmak94andClaude Opus 5.5 4b395efe5c Add the Petari decompilation (SMGCommunity/Petari@0a5c660)
The sources of Petari (https://github.com/SMGCommunity/Petari), the
Super Mario Galaxy decompilation by the SMGCommunity, at commit
0a5c6608dc3b4c44a9aa68c2535d1793dc565f02, unchanged but for LF line
endings: LICENSE (as LICENSE-Petari.txt; CC0 1.0), include/, libs/ and
src/.  Left out: the game data Petari keeps from the original
executable, the error message archive (include/Game/System/
ErrorArchive.hpp) and two tables (src/Game/System/GalaxyID.bcsv and
StoryEvent.bcsv); GalaxyQuest's converter takes them from the player's
own disc.  The port's changes follow in the next commit, so
`git diff petari-base -- decomp` shows them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:59:38 +02:00

433 lines
13 KiB
C++

#pragma once
#include "Game/AudioLib/AudAudible.hpp"
#include "Game/Util/MathUtil.hpp"
#include <JSystem/JAudio2/JAISound.hpp>
#include <JSystem/JAudio2/JASSoundParams.hpp>
#include <JSystem/JAudio2/JAUAudible.hpp>
#include <JSystem/JAudio2/JAUSoundInfo.hpp>
#include <JSystem/JGeometry/TUtil.hpp>
#include <JSystem/JAudio2/JAIAudience.hpp>
#include <JSystem/JAudio2/JAUAudience.hpp>
#include <JSystem/JAudio2/JAUDoppler.hpp>
#include <JSystem/JGeometry/TMatrix.hpp>
class JAUDopplerAudibleRelPos;
class AudDopplerAudienceState {
public:
AudDopplerAudienceState() {
// TPos3f mtx;
// mtx.identity();
// resetMtx(mtx);
}
void resetMtx(const Mtx pMtx) NO_INLINE {
mMtx.set(pMtx);
TPos3f lookAt;
lookAt.setPositionFromLookAt(mMtx);
lookAt.getTrans(mPos);
mPrevPos.set(mPos);
mVelocity.zero();
}
void setMtx(const MtxPtr pMtx) {
mPrevPos.set(mPos);
mMtx.set(pMtx);
TPos3f lookAt;
lookAt.setPositionFromLookAt(mMtx);
lookAt.getTrans(mPos);
mVelocity.sub(mPos, mPrevPos);
}
const TVec3f& getPos() const {
return mPos;
}
void convertAbsToRel(const TVec3f& rPos, JAUDopplerAudibleRelPos* pRel) {
TVec3f diff;
diff.sub(rPos, mPos);
if (diff.x > -0.002f && diff.x < 0.002f) {
if (diff.x < 0.0f) {
diff.x = -0.002f;
} else {
diff.x = 0.002f;
}
}
if (diff.y > -0.002f && diff.y < 0.002f) {
if (diff.y < 0.0f) {
diff.y = -0.002f;
} else {
diff.y = 0.002f;
}
}
if (diff.z > -0.002f && diff.z < 0.002f) {
if (diff.z < 0.0f) {
diff.z = -0.002f;
} else {
diff.z = 0.002f;
}
}
PSMTXMultVecSR(mMtx, diff, pRel->mPos);
pRel->mDir.set(diff);
pRel->mDist = pRel->mDir.normalize();
}
const TVec3f& getVelocity() {
return mVelocity;
}
/* 0x00 */ TPos3f mMtx;
/* 0x30 */ TVec3f mVelocity;
/* 0x3C */ TVec3f mPos;
/* 0x48 */ TVec3f mPrevPos;
};
class AudAudience_withSetting : public JAIAudience {
public:
/// @brief Creates a new `AudAudience_withSetting`.
AudAudience_withSetting();
void updateSetting();
u32 calcDeltaPriority_(f32 dist, bool excludeFar, s32 distVolBit) const {
f32 farDist = distVolBit == 8 ? mSetting.mFarDistance / 2.0f : mSetting.mFarDistance * distVolBit;
if (mFarCamera) {
farDist *= 3.0f;
}
if (dist > farDist) {
if (excludeFar) {
return 0xFFFFFFFF;
}
return mSetting.mPriorityDownRange;
}
if (dist < mSetting.mNearDistance) {
return 0;
}
f32 priorityRate = distVolBit == 1 ? mPriorityRate : mSetting.mPriorityDownRange / (farDist - mSetting.mNearDistance);
return priorityRate * (dist - mSetting.mNearDistance);
}
f32 calcVolume_(f32 dist, s32 distVolBit) {
f32 farDist = distVolBit == 8 ? mSetting.mFarDistance / 2.0f : mSetting.mFarDistance * distVolBit;
if (mFarCamera) {
farDist *= 3.0f;
}
if (dist > farDist) {
return mSetting.mMinVolume;
}
if (dist < mSetting.mNearDistance) {
return 1.0f;
}
f32 volumeRate = distVolBit == 1 ? mVolumeRate : (mSetting.mMinVolume - 1.0f) / (farDist - mSetting.mNearDistance);
return MR::clamp(volumeRate * (dist - mSetting.mNearDistance) + 1.0f, 0.0f, 1.0f);
}
f32 calcDolby_(const TVec3f& rPos, s32 distVolBit) {
f32 z = rPos.z;
if (z > mSetting.mBehindDolbyZ) {
return mSetting.mMaxDolby;
}
if (z < mSetting.mFrontDolbyZ) {
return mSetting.mMinDolby;
}
if (z < 0.0f) {
return mSetting.mCenterDolby + mFrontDolbyRate * z;
} else {
return mSetting.mCenterDolby + mBehindDolbyRate * z;
}
}
f32 calcPan_(f32 dist, const TVec3f& rPos) {
f32 panRate = dist <= MR::epsilon() ? 0.0f : rPos.x / dist;
return panRate * mSetting.mPanAmplitude + 0.5f;
}
f32 calcDopplerPitch_(const TVec3f& rDir, const TVec3f& rVel1, const TVec3f& rVel2, f32 doppler) {
f32 vel1 = doppler * rDir.dot(rVel1);
f32 vel2 = doppler * rDir.dot(rVel2);
return (mSetting.mSonicSpeed + vel1) / (mSetting.mSonicSpeed + vel2);
}
f32 calcDopplerPitch_(f32 vel1, f32 vel2) {
return (mSetting.mSonicSpeed + vel1) / (mSetting.mSonicSpeed + vel2);
}
f32 calcPitch_(JAUDopplerAudibleChannel* pChannel, f32 pitch) {
if (pChannel->mPitch > 0.0f) {
if (pChannel->mPitch > pitch * mSetting.mPitchDeltaRatioRange) {
return pChannel->mPitch;
}
if (pitch > pChannel->mPitch * mSetting.mPitchDeltaRatioRange) {
return pChannel->mPitch;
}
pChannel->mPitch = pitch;
return pitch;
}
if (pitch > 0.0f) {
pChannel->mPitch = pitch;
return pitch;
}
return 1.0f;
}
f32 calcFxMix_(f32 dist) {
if (dist > mSetting.mFarDistance) {
return mSetting.mMaxFxMix;
}
if (dist < mSetting.mNearDistance) {
return mSetting.mMinFxMix;
}
return mSetting.mMinFxMix + mFxMixRate * (dist - mSetting.mNearDistance);
}
/* 0x04 */ JAUAudience3DSetting mSetting;
/* 0x3C */ f32 mFrontDolbyRate;
/* 0x40 */ f32 mBehindDolbyRate;
/* 0x44 */ f32 mVolumeRate;
/* 0x48 */ f32 mPriorityRate;
/* 0x4C */ f32 mFxMixRate;
/* 0x50 */ bool mFarCamera;
};
class AudAudience_withSetting_doppler : public AudAudience_withSetting {
public:
/// @brief Creates a new `AudAudience_withSetting_doppler`.
AudAudience_withSetting_doppler() : AudAudience_withSetting() {};
};
template < int MAX_CHANNELS, class Audible, class AudienceState, class Audience >
class AudAudience_ : public Audience {
public:
AudAudience_() : Audience(), mMaxChannels(MAX_CHANNELS) {
for (int i = 0; i < MAX_CHANNELS; i++) {
TPos3f mtx;
mtx.identity();
mStates[i].resetMtx(mtx);
}
};
virtual int getMaxChannels() {
return mMaxChannels;
}
virtual JAIAudible* newAudible(const TVec3f& rPos, JAISoundID soundID, const TVec3f* pPos, u32 mask) {
mask |= ~((1 << mMaxChannels) - 1);
if (mask == 0xFFFFFFFF) {
return nullptr;
}
Audible* audible = new Audible(rPos, pPos, mask);
if (audible == nullptr) {
return nullptr;
}
for (int i = 0; i < mMaxChannels; i++) {
JAUDopplerAudibleChannel* channel = audible->mChannels[i]; // TODO: inherited?
if (channel == nullptr) {
continue;
}
mStates[i].convertAbsToRel(audible->getPos(), &channel->mRelPos);
Audience::calcDeltaPriority_(channel->mRelPos.mDist, false, 1);
}
JAUSoundInfo* info = JAUSoundInfo::getInstance();
if (info != nullptr) {
audible->mAudibleParam = JAUAudibleParam(info->getAudibleSw(soundID), -1);
}
return audible;
}
virtual void deleteAudible(JAIAudible* pAudible) {
delete pAudible;
}
virtual u32 calcPriority(JAIAudible* pAudible) {
Audible* audible = (Audible*)pAudible;
if ((audible->getAudibleParam().get_BIT11()) == 0) {
for (int i = 0; i < mMaxChannels; i++) {
if (audible->mChannels[i] != nullptr) {
mStates[i].convertAbsToRel(audible->getPos(), &audible->mChannels[i]->mRelPos);
}
}
return 0;
}
u32 priority = -1;
u32 deltaPriority[MAX_CHANNELS];
for (int i = 0; i < mMaxChannels; i++) {
if (audible->mChannels[i] != nullptr) {
JAUDopplerAudibleChannel* channel = audible->mChannels[i];
u32 volDistBit = audible->getAudibleParam().getVolDistBit();
mStates[i].convertAbsToRel(audible->getPos(), &audible->mChannels[i]->mRelPos);
bool bit = audible->getAudibleParam().get_BIT8();
deltaPriority[i] = Audience::calcDeltaPriority_(channel->mRelPos.getDist(), bit, volDistBit);
if (deltaPriority[i] < priority) {
priority = deltaPriority[i];
}
}
}
return priority;
}
/*
s32 getVolDistBit(Audible* audible) {
JAUAudibleParam param = audible->getAudibleParam();
return param.getVolDistBit();
}
*/
virtual void mixChannelOut(const JASSoundParams& rParams, JAIAudible* pAudible, int channelNum) NO_INLINE {
Audible* audible = (Audible*)pAudible;
JAUDopplerAudibleChannel* channel = audible->getChannel(channelNum);
if (channel == nullptr) {
return;
}
JASSoundParams params;
if (audible->getAudibleParam().calcVolume()) {
params.mVolume = Audience::calcVolume_(channel->mRelPos.getDist(), audible->getVolDistBit());
f32 vol = params.mVolume;
if (!audible->mIsResetting) {
if (vol > audible->mVolume) {
if (vol - audible->mVolume >= 0.2f) {
vol = audible->mVolume + (vol - audible->mVolume) / 5.0f;
}
} else {
if (audible->mVolume - vol >= 0.2f) {
vol = vol + (audible->mVolume - vol) / 5.0f;
}
}
}
audible->mVolume = vol;
params.mVolume = vol;
} else {
params.mVolume = 1.0f;
}
if (audible->getAudibleParam().calcDolby()) {
params.mDolby = Audience::calcDolby_(channel->mRelPos.getPos(), audible->getVolDistBit());
f32 dolby = params.mDolby;
if (!audible->mIsResetting) {
if (dolby > audible->mDolby) {
if (dolby - audible->mDolby >= 0.2f) {
dolby = audible->mDolby + (dolby - audible->mDolby) / 5.0f;
}
} else {
if (audible->mDolby - dolby >= 0.2f) {
dolby = dolby + (audible->mDolby - dolby) / 5.0f;
}
}
}
audible->mDolby = dolby;
params.mDolby = dolby;
} else {
params.mDolby = 0.1f;
}
if (audible->getAudibleParam().calcPan()) {
if (mMaxChannels > 2) {
params.mPan = (channelNum & 1) != 0 ? 1.0f : 0.0f;
} else {
params.mPan = Audience::calcPan_(channel->mRelPos.getDist(), channel->mRelPos.getPos());
}
f32 pan = params.mPan;
if (!audible->mIsResetting) {
if (pan > audible->mPan) {
if (pan - audible->mPan >= 0.2f) {
pan = audible->mPan + (pan - audible->mPan) / 5.0f;
}
} else {
if (audible->mPan - pan >= 0.2f) {
pan = pan + (audible->mPan - pan) / 5.0f;
}
}
}
audible->mPan = pan;
params.mPan = pan;
} else {
params.mPan = 0.5f;
}
params.mPitch = calcPitch(channel, audible, &mStates[channelNum]);
params.mFxMix = calcFxMix(channel, audible);
audible->setOuterParams(rParams, params, channelNum);
audible->mIsResetting = false;
}
f32 calcPitch(JAUDopplerAudibleChannel* pChannel, Audible* pAudible, AudienceState* pState) {
JAUAudibleParam param = pAudible->getAudibleParam();
if (param.getDoppler() != 0) {
f32 doppler = param.getDopplerPower();
f32 vel1 = doppler * pChannel->getDir().dot(pState->getVelocity());
f32 vel2 = doppler * pChannel->getDir().dot(pAudible->getVelocity());
f32 pitch = Audience::calcDopplerPitch_(vel1, vel2);
return Audience::calcPitch_(pChannel, pitch);
} else {
return 1.0f;
}
}
f32 calcFxMix(JAUDopplerAudibleChannel* pChannel, Audible* pAudible) {
if (pAudible->getAudibleParam().calcFxMix()) {
return Audience::calcFxMix_(pChannel->mRelPos.getDist());
} else {
return 0.0f;
}
}
/* 0x054 */ int mMaxChannels;
/* 0x058 */ AudienceState mStates[MAX_CHANNELS];
/* 0x1A8 */ // Audible* mAudible;
};
template < int MAX_CHANNELS >
class AudAudience : public AudAudience_< MAX_CHANNELS, AudDopplerAudible< MAX_CHANNELS >, AudDopplerAudienceState, AudAudience_withSetting_doppler > {
public:
AudAudience()
: AudAudience_< MAX_CHANNELS, AudDopplerAudible< MAX_CHANNELS >, AudDopplerAudienceState, AudAudience_withSetting_doppler >() {
};
/* 0x1A8 */ // AudDopplerAudible< MAX_CHANNELS >* mAudible;
};