Skip to content

reimplement: SHC_3BB0A8C1_0x0047AF50 100%#117

Merged
gynt merged 3 commits into
mainfrom
reimpl/SHC_3BB0A8C1_0x0047AF50
Jun 24, 2026
Merged

reimplement: SHC_3BB0A8C1_0x0047AF50 100%#117
gynt merged 3 commits into
mainfrom
reimpl/SHC_3BB0A8C1_0x0047AF50

Conversation

@TheRedDaemon

@TheRedDaemon TheRedDaemon commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Faulty register usuage related and scoped to a call. Out of ideas:
- Copied known functions inside file: No sharing issue.
- Outside of this, the structure seems to fit completely. The only other things are jmps extended by one byte due to the ECX move.

@TheRedDaemon TheRedDaemon added the help wanted Extra attention is needed label Jun 17, 2026
@gynt

gynt commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Claude's try: 61%

#include "../SoundSystem.func.hpp"

#include "OpenSHC/Audio/SFX/SFXState.func.hpp"
#include "OpenSHC/string-literals.hpp"

#include "OpenSHC/Globals/DAT_GameCore.hpp"
#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp"
#include "OpenSHC/Globals/DAT_SFXState.hpp"
#include "OpenSHC/Globals/DAT_SoundEffectsHelperData1.hpp"

namespace OpenSHC {
namespace Audio {
    namespace MSS {

        // FUNCTION: STRONGHOLDCRUSADER 0x0047AF50
        void SoundSystem::handleBattleEndMusicTransition()
        {
            if (DAT_GameCore::ptr->gameMode_2 == Game::GM_EDITOR) {
                return;
            }
            if (DAT_GameCore::ptr->gameMode_2 == Game::GM_SIEGE_THAT) {
                return;
            }
            if (DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field0_0x0 != 5) {
                return;
            }
            if (DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field6_0x18 != 0) {
                return;
            }

            int vol;

            if (DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.troopValueLevel == 0) {
                DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field6_0x18 = 1;
                unsigned int volumeLevel = (unsigned int)DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.volumeLevel + (unsigned int)-1;
                if (volumeLevel > 4) {
                    return;
                }
                MACRO_CALL_MEMBER(SoundSystem_Func::setSomeSoundTime, this)();
                if (DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.troopValueLevel == 0) {
                    DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field0_0x0 = 1;
                    goto do_play_victory_1;
                }
                DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field0_0x0 = 1;
            } else {
                DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field6_0x18 = 1;
            }

            vol = DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.volumeLevel;
            vol -= 1;
            if (vol != 0) {
                vol -= 1;
                if (vol != 0) {
                    return;
                }
                // vol == 2
                MACRO_CALL_MEMBER(SoundSystem_Func::setSomeSoundTime, this)();
                if (DAT_GameSynchronyState::ptr->DAT_CurrentGameMode == Game::GM_SOLITARY) {
                    int variation = DAT_SoundEffectsHelperData1::ptr->DAT_WinMusicVariation;
                    MACRO_CALL_MEMBER(SoundSystem_Func::setupVolumeAndSoundIDWithMultiplier, this)(
                        (eMusicIDs)(variation + DE::SHCDE::MUSIC_TUNE_WIN1), 100);
                    variation = DAT_SoundEffectsHelperData1::ptr->DAT_WinMusicVariation + 1;
                    DAT_SoundEffectsHelperData1::ptr->DAT_WinMusicVariation = variation;
                    if (variation > 2) {
                        DAT_SoundEffectsHelperData1::ptr->DAT_WinMusicVariation = 0;
                        goto do_play_victory_1;
                    }
                    // variation <= 2: fall through to tail2
                } else {
                    DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field0_0x0 = 1;
                }
            do_play_victory_2:
                MACRO_CALL_MEMBER(SFX::SFXState_Func::playVictoryMusic678, DAT_SFXState::ptr)();
                return;
            }
            // vol == 1
            if (DAT_GameSynchronyState::ptr->DAT_CurrentGameMode != Game::GM_SOLITARY) {
                MACRO_CALL_MEMBER(SoundSystem_Func::setSomeSoundTime, this)();
                DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.field0_0x0 = 1;
                return;
            }
            MACRO_CALL_MEMBER(SoundSystem_Func::stopMusicPlayback, this)();
            MACRO_CALL_MEMBER(SoundSystem_Func::setupVolumeAndSoundIDWithMultiplier, this)(
                DE::SHCDE::MUSIC_TUNE_BATTLE_L1D, 100);
        do_play_victory_1:
            MACRO_CALL_MEMBER(SFX::SFXState_Func::playVictoryMusic678, DAT_SFXState::ptr)();
        }

    }
}
}

@TheRedDaemon

TheRedDaemon commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

So, I am now out of ideas.
I also tested:

  • Intermediate variables for the compute.
  • Having a local variables for SEC_Section1079
  • Having a global variable const for the 100 volume
  • Adding implementations of the function used did not help

The code structure itself basically is equal to the logic displayed in assembly. I even think the switch is determined by the code. Tests also revealed that the same intitial value that should not be loaded in to ECX is happily loaded into EAX in cases it is not used for the call. But even in source the value is only used for the call.

Things that I could now think of but will not test for now. The first I consider more likely:

  • There is one usage of this funtion in another SoundSystem function. I am not sure, but if they are in the same file, could the user also apply pressure?
  • Unknown redundant temporaries and locals that got optimized aways but still added pressure.
  • The structure was actually very different, but the compiler optimized in to this one, the EAX mov being the only trace.
  • Certain non-fitting types that apply some pressure.
  • We might have our first case of compiler entropy, however, adding or moving file content did not change anything. It mostly tried to use EXC.
  • Yet another compiler version (TM). There were at least patches for redistributables, so maybe one could have a small patch on the MSVC also? Who knows...
  • Pipeline issues, although it seems rather unlikely with every 100% function.

For all intends an purposes, the logic is identical, so this one could just be an annoyance, but it would still be nice to figure out how this happenend in the first place.

It should stay draft at least until all other SoundSystem functions are done. Then we can test the theory with sharing a file with the one usage function. After that, we can discuss if we just add it with a lesser percentage.

@TheRedDaemon TheRedDaemon force-pushed the reimpl/SHC_3BB0A8C1_0x0047AF50 branch from 368e19d to 04c17cf Compare June 23, 2026 19:53
@TheRedDaemon TheRedDaemon added declaration/type change This requires changes to the generated/exported types or definitions. and removed help wanted Extra attention is needed labels Jun 23, 2026
@TheRedDaemon TheRedDaemon changed the title reimplement: SHC_3BB0A8C1_0x0047AF50 structure and register issue reimplement: SHC_3BB0A8C1_0x0047AF50 100% Jun 23, 2026
@TheRedDaemon

Copy link
Copy Markdown
Contributor Author

Managed to resolve this by changing

 if (DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.volumeLevel - 1U > 4) {

to

int const volumeLevel = DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.volumeLevel;
if (volumeLevel < 1 || volumeLevel > 5) {

It is hard to connect these and the position that used the other registers. It seems it just increased the pressure to use EAX.
Maybe it is related to the fact that at another position EAX was used to handle the switch based on DAT_SoundEffectsHelperData1::ptr->SEC_Section1079.volumeLevel? I do not know.

In general, in cases where the logic is sound, but the result is not fitting, one must try to find the boundaries the logic itself requires. Function calls or or variable usage or lifetime. Within these boundaries, certain structures can be moved, or local variables created and used. These can influence the compiler.

There is still the usage of a call requiring the char const *.

@gynt

gynt commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Wow! Nice solve!

@gynt

gynt commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Desired changes have been implemented upstream a1f7e6d

@TheRedDaemon TheRedDaemon force-pushed the reimpl/SHC_3BB0A8C1_0x0047AF50 branch from 04c17cf to 07d5c00 Compare June 24, 2026 19:54
@TheRedDaemon TheRedDaemon removed the declaration/type change This requires changes to the generated/exported types or definitions. label Jun 24, 2026
@TheRedDaemon TheRedDaemon marked this pull request as ready for review June 24, 2026 19:57
@gynt gynt merged commit f36800f into main Jun 24, 2026
2 checks passed
@gynt gynt deleted the reimpl/SHC_3BB0A8C1_0x0047AF50 branch June 24, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants