Skip to content

feat(deps): Replace the Bink SDK and Miles SDK stubs with runtime loaders - #3272

Merged
xezon merged 3 commits into
TheSuperHackers:mainfrom
xezon:xezon/replace-miles-bink-stubs
Sep 12, 2026
Merged

feat(deps): Replace the Bink SDK and Miles SDK stubs with runtime loaders#3272
xezon merged 3 commits into
TheSuperHackers:mainfrom
xezon:xezon/replace-miles-bink-stubs

Conversation

@xezon

@xezon xezon commented Sep 9, 2026

Copy link
Copy Markdown

Merge with Rebase

This change has 3 commits:

  1. Removes the Bink SDK stub and replaces it with a Bink DLL runtime loader.
  2. Removes the Miles SDK stub and replaces it with a Miles DLL runtime loader.
  3. Refactor pass on mss.h correcting its primitive types. No ABI changes.

It uses the original Bink and Miles stubs from OmniBlade as the base and built around it.

The benefit of this approach is that

  • The Win32 game executable no longer has a strict runtime dependency on mss32.dll and binkw32.dll
  • The mss32.dll and binkw32.dll no longer need to sit next to the generals.exe but can also load successfully after our new command line argument -setCwd has set a custom work directory where these dll's can be found in

AI use

This change was mostly a one-shot generation with Claude Opus 5. I just cleaned up the comments and cleaned up a few minor things. But overall it looked very solid to begin with.

TODO

  • Add pull id to commit titles
  • Rename feat to refactor

@xezon xezon added this to the Code foundation build up milestone Sep 9, 2026
@xezon xezon added Build Anything related to building, compiling Refactor Edits the code with insignificant behavior changes, is never user facing System Is Systems related labels Sep 9, 2026
@xezon
xezon requested a review from OmniBlade September 9, 2026 10:26
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Runtime-load Bink and Miles DLLs after working-directory setup

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Runtime-loads Bink and Miles DLLs after command-line working-directory selection.
• Preserves existing SDK call sites through neutral-fallback forwarding functions.
• Replaces fetched stub libraries across game, engine, and tool build targets.
Diagram

graph TD
  CWD["Configured CWD"] --> Video["Video Player"] --> Bink["Bink Loader"] --> BinkDLL["binkw32.dll"]
  CWD --> Audio["Audio Managers"] --> Miles["Miles Loader"] --> MilesDLL["mss32.dll"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Windows delay-load imports
  • ➕ Retains direct SDK calls with substantially less forwarding code.
  • ➕ Lets symbol signatures remain linker-validated against an import library.
  • ➖ Requires delay-load failure hooks to preserve graceful missing-DLL behavior.
  • ➖ Missing exports need custom handling and may still terminate individual calls.
  • ➖ Behavior is tied more closely to the Microsoft linker and toolchain.
2. Generated forwarding tables
  • ➕ Reduces manual repetition across the large Miles API surface.
  • ➕ Can generate typedefs, export names, resets, and wrappers from one manifest.
  • ➕ Makes ABI additions and decorated-name reviews more systematic.
  • ➖ Introduces a generator and manifest that must be maintained.
  • ➖ Adds build or code-generation complexity for legacy toolchains.
  • ➖ Still requires careful validation of SDK signatures and neutral return values.

Recommendation: Explicit LoadLibrary/GetProcAddress forwarding is the best fit because it delays discovery until the configured working directory is active and preserves graceful degradation without changing call sites. The overall approach should remain, although generating the repetitive Miles bindings from a reviewed manifest would reduce long-term ABI drift if loader maintenance expands.

Files changed (22) +1831 / -13

Enhancement (9) +1433 / -0
MilesAudioManager.hTrack Miles runtime-loader state +2/-0

Track Miles runtime-loader state

• Adds a flag recording whether Miles was loaded successfully so device shutdown can release it.

Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h

BinkVideoPlayer.hTrack Bink runtime-loader state +2/-0

Track Bink runtime-loader state

• Adds instance state indicating whether the video player successfully loaded the Bink runtime.

Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h

MilesAudioManager.cppLoad Miles during audio device initialization +15/-0

Load Miles during audio device initialization

• Loads mss32.dll before Miles startup, logs failures, and unloads the runtime during device shutdown. Missing Miles now disables audio without creating a process-load dependency.

Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp

BinkVideoPlayer.cppLoad Bink during video-player initialization +18/-0

Load Bink during video-player initialization

• Loads binkw32.dll when the video player initializes, reports failures, and unloads it during deinitialization. This defers DLL discovery until the working directory is finalized.

Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp

WWAudio.cppManage Miles loading in WWAudio lifecycle +11/-0

Manage Miles loading in WWAudio lifecycle

• Loads the Miles DLL before starting the sound system, logs unavailable audio, and releases the loader from the WWAudio destructor.

Core/Libraries/Source/WWVegas/WWAudio/WWAudio.cpp

BinkLoader.cppImplement Bink DLL loading and API forwarding +250/-0

Implement Bink DLL loading and API forwarding

• Implements reference-counted Win32 loading, decorated export resolution, cleanup, and forwarding for the Bink API subset. Unavailable functions return neutral values compatible with the former stub behavior.

Dependencies/Bink/BinkLoader.cpp

BinkLoader.hDefine the Bink runtime-loader interface +52/-0

Define the Bink runtime-loader interface

• Declares load, unload, status, and error-reporting operations and documents lifecycle and thread-safety expectations.

Dependencies/Bink/BinkLoader.h

MilesLoader.cppImplement Miles DLL loading and API forwarding +1029/-0

Implement Miles DLL loading and API forwarding

• Implements reference-counted loading of mss32.dll, resolution of decorated AIL exports, and resource cleanup. The existing Miles API is forwarded when available and otherwise returns stub-compatible neutral values.

Dependencies/Miles/MilesLoader.cpp

MilesLoader.hDefine the Miles runtime-loader interface +54/-0

Define the Miles runtime-loader interface

• Declares runtime loading, unloading, status inspection, and error retrieval while documenting lifecycle and threading constraints.

Dependencies/Miles/MilesLoader.h

Other (13) +398 / -13
CMakeLists.txtReplace fetched SDK stubs with local loader targets +3/-2

Replace fetched SDK stubs with local loader targets

• Removes the conditional Bink and Miles stub includes. Adds the in-tree Bink and Miles dependency directories to the main build.

CMakeLists.txt

CMakeLists.txtLink game engine devices against runtime loaders +2/-2

Link game engine devices against runtime loaders

• Replaces the Bink and Miles stub dependencies with the new loader libraries for public game-engine-device consumers.

Core/GameEngineDevice/CMakeLists.txt

CMakeLists.txtLink WWCommon against the Miles loader +1/-1

Link WWCommon against the Miles loader

• Switches the shared WWVegas interface dependency from the Miles stub to the runtime loader.

Core/Libraries/Source/WWVegas/CMakeLists.txt

CMakeLists.txtAdd the static Bink loader target +20/-0

Add the static Bink loader target

• Defines the binkloader static library, exports its headers, and links its utility dependency.

Dependencies/Bink/CMakeLists.txt

bink.hVendor the engine-used Bink API subset +71/-0

Vendor the engine-used Bink API subset

• Defines the ABI-compatible Bink structures, constants, callback types, and function declarations required by the engine and loader.

Dependencies/Bink/bink.h

CMakeLists.txtAdd the static Miles loader target +21/-0

Add the static Miles loader target

• Defines the milesloader static library and publishes include paths for both loader and Miles API headers.

Dependencies/Miles/CMakeLists.txt

mss.hVendor the engine-used Miles API subset +272/-0

Vendor the engine-used Miles API subset

• Defines the cross-platform-compatible Miles handles, structures, constants, callbacks, and AIL declarations needed by existing audio code and the loader.

Dependencies/Miles/mss/mss.h

CMakeLists.txtUse the Miles loader in Generals WWCommon +1/-1

Use the Miles loader in Generals WWCommon

• Replaces the Miles stub dependency with milesloader for the Generals WWVegas interface target.

Generals/Code/Libraries/Source/WWVegas/CMakeLists.txt

CMakeLists.txtLink Generals with runtime media loaders +2/-2

Link Generals with runtime media loaders

• Switches the Generals executable from Bink and Miles stubs to the new runtime-loader libraries.

Generals/Code/Main/CMakeLists.txt

CMakeLists.txtLink Generals W3DView with the Miles loader +1/-1

Link Generals W3DView with the Miles loader

• Replaces the tool's Miles stub linkage with the runtime loader.

Generals/Code/Tools/W3DView/CMakeLists.txt

CMakeLists.txtUse the Miles loader in Zero Hour WWCommon +1/-1

Use the Miles loader in Zero Hour WWCommon

• Replaces the Miles stub dependency with milesloader for the Zero Hour WWVegas interface target.

GeneralsMD/Code/Libraries/Source/WWVegas/CMakeLists.txt

CMakeLists.txtLink Zero Hour with runtime media loaders +2/-2

Link Zero Hour with runtime media loaders

• Switches the Zero Hour executable from Bink and Miles stubs to the new runtime-loader libraries.

GeneralsMD/Code/Main/CMakeLists.txt

CMakeLists.txtLink Zero Hour W3DView with the Miles loader +1/-1

Link Zero Hour W3DView with the Miles loader

• Replaces the tool's Miles stub linkage with the runtime loader.

GeneralsMD/Code/Tools/W3DView/CMakeLists.txt

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Partial sound libraries crash startup ✓ Resolved 🐞 Bug ≡ Correctness
Description
AIL_get_DirectSound_info returns without initializing either output when its export is
unavailable. If the DLL otherwise loads and audio startup succeeds, provider selection tests and may
dereference an indeterminate DirectSound pointer, while Bink initialization can also return an
indeterminate driver pointer.
Code

Dependencies/Miles/MilesLoader.cpp[R932-935]

+void __stdcall AIL_get_DirectSound_info(HSAMPLE sample, AILLPDIRECTSOUND *lplpDS, AILLPDIRECTSOUNDBUFFER *lplpDSB)
+{
+	if (AIL_get_DirectSound_infoPtr != nullptr)
+		AIL_get_DirectSound_infoPtr(sample, lplpDS, lplpDSB);
Evidence
The wrapper is a no-op when unresolved. selectProvider declares its output without initialization
before testing and dereferencing it, and getHandleForBink similarly returns an untouched local.

Dependencies/Miles/MilesLoader.cpp[932-935]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1630-1636]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[2840-2842]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The fallback for a missing `AIL_get_DirectSound_info` export leaves caller-owned outputs uninitialized. Existing callers immediately inspect, dereference, or return those values.
## Issue Context
Missing Miles exports are explicitly accepted by the loader, so every fallback must provide deterministic outputs.
## Fix Focus Areas
- Dependencies/Miles/MilesLoader.cpp[932-935]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1630-1636]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[2840-2842]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Audio startup reads outside providers 🐞 Bug ≡ Correctness
Description
AIL_enumerate_3D_providers reports no providers when unresolved, but the loader still treats the
DLL as successfully loaded. When the remaining startup functions succeed, selectProvider receives
PROVIDER_ERROR and indexes m_provider3D with it before checking the provider count.
Code

Dependencies/Miles/MilesLoader.cpp[R886-889]

+int __stdcall AIL_enumerate_3D_providers(HPROENUM* next, HPROVIDER* dest, char** name)
+{
+	return AIL_enumerate_3D_providersPtr != nullptr ? AIL_enumerate_3D_providersPtr(next, dest, name) : 0;
+}
Evidence
The unresolved wrapper returns zero, leaving m_providerCount at zero. Both preferred-provider
lookups then return the error sentinel, which is used directly as an array index in
selectProvider.

Dependencies/Miles/MilesLoader.cpp[886-889]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1600-1609]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1671-1695]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[2712-2724]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A missing provider-enumeration export produces an empty provider list, after which startup indexes the list using the error sentinel. Validate provider indices or classify this export as required before continuing initialization.
## Issue Context
The loader intentionally permits missing exports, so startup must safely handle an empty list.
## Fix Focus Areas
- Dependencies/Miles/MilesLoader.cpp[886-889]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1600-1609]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1671-1695]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[2712-2724]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Audio metadata can become random ✓ Resolved 🐞 Bug ≡ Correctness
Description
AIL_WAV_info returns zero without initializing its AILSOUNDINFO output when the export is
missing. AudioFileCache::openFile ignores that result and reads the uninitialized channel and
format fields, so playback can enter arbitrary validation or decompression paths.
Code

Dependencies/Miles/MilesLoader.cpp[R799-802]

+int __stdcall AIL_WAV_info(const void* data, AILSOUNDINFO* info)
+{
+	return AIL_WAV_infoPtr != nullptr ? AIL_WAV_infoPtr(data, info) : 0;
+}
Evidence
The wrapper does not write info in its fallback. The caller creates an uninitialized structure,
ignores the status, and immediately uses its fields to select stereo validation and buffer-handling
branches.

Dependencies/Miles/MilesLoader.cpp[799-802]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[3029-3059]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The missing-export fallback for `AIL_WAV_info` leaves the output structure uninitialized, and its caller ignores the return value. Make the fallback and caller establish a deterministic failure path.
## Issue Context
The format field controls buffer ownership and ADPCM decompression, so arbitrary contents are unsafe.
## Fix Focus Areas
- Dependencies/Miles/MilesLoader.cpp[799-802]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[3029-3059]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View action required (1)
4. ADPCM playback can corrupt memory ✓ Resolved 🐞 Bug ≡ Correctness
Description
AIL_decompress_ADPCM returns zero without writing outdata or outsize when its export is
missing. The cache code ignores the result, deletes the valid source buffer, and stores the
indeterminate pointer and size for later playback and cleanup.
Code

Dependencies/Miles/MilesLoader.cpp[R927-930]

+int __stdcall AIL_decompress_ADPCM(const AILSOUNDINFO *info, void **outdata, unsigned long *outsize)
+{
+	return AIL_decompress_ADPCMPtr != nullptr ? AIL_decompress_ADPCMPtr(info, outdata, outsize) : 0;
+}
Evidence
The fallback writes neither output. The caller stores both uninitialized locals after freeing the
original file data, and cleanup later treats the stored pointer as a valid Miles allocation.

Dependencies/Miles/MilesLoader.cpp[927-930]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[3040-3049]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[3113-3119]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The missing ADPCM decompressor fallback leaves output parameters untouched. Ensure failure preserves valid ownership and prevents uninitialized pointers from entering the audio cache.
## Issue Context
The caller currently replaces and deletes the source buffer without checking decompression success.
## Fix Focus Areas
- Dependencies/Miles/MilesLoader.cpp[927-930]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[3040-3049]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[3113-3119]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Failed video loads never recover ✓ Resolved 🐞 Bug ☼ Reliability
Description
BinkVideoPlayer::init stores a failed BinkLoader::load() result as false, and deinit
therefore skips unload() even though load() increments its reference count before attempting
LoadLibraryA. After a missing-DLL failure, repeated initialization accumulates references, retains
the persistent Failed state, and prevents a later DLL-load retry in the same process.
Code

Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp[R138-142]

+		m_binkLoaded = BinkLoader::load();
+		if (!m_binkLoaded)
+		{
+			DEBUG_LOG(("Failed to load binkw32.dll (error %d). Videos will not play.", BinkLoader::getLastError()));
+		}
Evidence
BinkLoader::load() increments ReferenceCount before checking or setting Failed, including when
LoadLibraryA cannot find the DLL, while only unload() decrements the reference count and clears
the failure state. BinkVideoPlayer records the failed result as m_binkLoaded == false and gates
its sole unload() call on that value, proving that failed load attempts remain acquired and keep
the loader failed.

Dependencies/Bink/BinkLoader.cpp[139-167]
Dependencies/Bink/BinkLoader.cpp[170-183]
Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp[135-160]
Dependencies/Bink/BinkLoader.cpp[139-183]
Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp[135-161]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A failed Bink load increments the loader reference count, but `BinkVideoPlayer` does not pair the attempt with `BinkLoader::unload()`. This leaves the reference count and failed state active, preventing later recovery and DLL-load retries in the same process.
## Issue Context
`BinkLoader::load()` increments its reference count before attempting to load the DLL, so every acquisition—including a failed attempt—must be paired with `unload()`. Track whether loading was attempted separately from whether it succeeded and unload each attempted acquisition during deinitialization, or make the loader roll back failed acquisitions while preserving correct multi-user semantics.
## Fix Focus Areas
- Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp[135-160]
- Dependencies/Bink/BinkLoader.cpp[139-183]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Failed audio loads never recover ✓ Resolved 🐞 Bug ☼ Reliability
Description
MilesAudioManager::openDevice stores a failed MilesLoader::load() result as false, and
closeDevice() consequently skips unload() even though load() increments its reference count
before attempting LoadLibraryA. When the DLL is missing, Failed remains latched, later
initialization attempts return false without retrying the library load, and each attempt retains
another reference.
Code

Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[R1375-1379]

+	m_milesLoaded = MilesLoader::load();
+	if (!m_milesLoaded)
+	{
+		DEBUG_LOG(("Failed to load mss32.dll (error %d). Audio will be turned off.", MilesLoader::getLastError()));
+	}
Evidence
MilesLoader::load() increments its reference count before attempting to load the DLL and leaves
Failed set on failure, while unload() is the only operation that clears that state.
MilesAudioManager records the failed result as false and gates the balancing unload on that
success flag, proving that failed attempts are never cleaned up.

Dependencies/Miles/MilesLoader.cpp[455-482]
Dependencies/Miles/MilesLoader.cpp[486-499]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1374-1424]
Dependencies/Miles/MilesLoader.cpp[455-499]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A failed Miles load increments the loader reference count, but `MilesAudioManager` does not pair the attempt with `MilesLoader::unload()`. The retained reference keeps the loader marked `Failed`, preventing a later initialization attempt from retrying `LoadLibraryA` and leaking another reference on each attempt.
## Issue Context
`MilesLoader::load()` increments its reference count before attempting `LoadLibraryA`, while `MilesAudioManager::closeDevice()` bases cleanup on whether module loading succeeded. Other users such as `WWAudioClass` unload unconditionally, and the loader contract requires every load attempt to be paired. Record each load attempt separately and always release it during device teardown, or roll back the reference in the loader failure path without breaking multi-user lifetime management.
## Fix Focus Areas
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1374-1424]
- Dependencies/Miles/MilesLoader.cpp[455-499]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Missing exports use garbage audio pan ✓ Resolved 🐞 Bug ≡ Correctness
Description
AIL_sample_volume_pan and AIL_stream_volume_pan leave their output parameters untouched when
either export is absent. Volume adjustment passes an uninitialized pan local back into the
corresponding Miles setter, producing undefined panning values for active samples and streams.
Code

Dependencies/Miles/MilesLoader.cpp[R986-990]

+void __stdcall AIL_sample_volume_pan(HSAMPLE sample, float *volume, float *pan)
+{
+	if (AIL_sample_volume_panPtr != nullptr)
+		AIL_sample_volume_panPtr(sample, volume, pan);
+}
Evidence
Both wrappers are no-ops if unresolved. adjustPlayingVolume declares pan without initialization
and passes it to a setter immediately after either query.

Dependencies/Miles/MilesLoader.cpp[986-990]
Dependencies/Miles/MilesLoader.cpp[1020-1024]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1229-1245]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The volume/pan query fallbacks do not populate caller outputs. Supply deterministic defaults or require callers to initialize and validate their values before invoking setters.
## Issue Context
Both sample and stream paths reuse the queried pan immediately.
## Fix Focus Areas
- Dependencies/Miles/MilesLoader.cpp[986-990]
- Dependencies/Miles/MilesLoader.cpp[1020-1024]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[1229-1245]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View review recommended (1)
8. Stream lengths become random ✓ Resolved 🐞 Bug ≡ Correctness
Description
AIL_stream_ms_position does not initialize its output parameters when the export is unavailable.
getFileLengthMS passes an uninitialized local as the total-length output and returns its
indeterminate value to callers.
Code

Dependencies/Miles/MilesLoader.cpp[R636-640]

+void __stdcall AIL_stream_ms_position(HSTREAM sample, S32* total_milliseconds, S32* current_milliseconds)
+{
+	if (AIL_stream_ms_positionPtr != nullptr)
+		AIL_stream_ms_positionPtr(sample, total_milliseconds, current_milliseconds);
+}
Evidence
The wrapper only writes outputs when the function pointer exists. The file-length caller initializes
neither local before returning the requested total position.

Dependencies/Miles/MilesLoader.cpp[636-640]
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[2477-2482]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The missing stream-position fallback leaves length outputs uninitialized. Return deterministic zero values or make the caller initialize and validate the outputs.
## Issue Context
`getFileLengthMS` directly converts the untouched local into its return value.
## Fix Focus Areas
- Dependencies/Miles/MilesLoader.cpp[636-640]
- Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp[2477-2482]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

9. The sound API exposes a wrong type 🐞 Bug ⚙ Maintainability
Description
The new AIL_waveOutOpen declaration and forwarding typedef use LPHWAVEOUT*, although
LPHWAVEOUT is already a pointer to HWAVEOUT. Current callers pass null for this argument, but
any caller providing an output handle receives an incompatible extra pointer indirection.
Code

Dependencies/Miles/mss/mss.h[191]

+int __stdcall AIL_waveOutOpen(HDIGDRIVER* driver, LPHWAVEOUT* waveout, int id, LPWAVEFORMAT format);
Evidence
The header defines LPHWAVEOUT as HWAVEOUT* and then declares the API with LPHWAVEOUT*. The
loader repeats the same extra indirection, while current calls only avoid the mismatch by passing
null.

Dependencies/Miles/mss/mss.h[34-58]
Dependencies/Miles/mss/mss.h[191-191]
Dependencies/Miles/MilesLoader.cpp[51-51]
Core/Libraries/Source/WWVegas/WWAudio/WWAudio.cpp[245-269]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`AIL_waveOutOpen` adds an extra pointer level to its wave-output handle parameter. Align the public declaration, function-pointer typedef, and forwarding definition with the expected `LPHWAVEOUT` parameter.
## Issue Context
Existing in-repository calls pass null, so the mismatch becomes visible when a caller requests the handle.
## Fix Focus Areas
- Dependencies/Miles/mss/mss.h[34-58]
- Dependencies/Miles/mss/mss.h[191-191]
- Dependencies/Miles/MilesLoader.cpp[51-51]
- Dependencies/Miles/MilesLoader.cpp[608-610]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Comment thread Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Outdated
Comment thread Dependencies/Miles/MilesLoader.cpp
Comment thread Dependencies/Miles/MilesLoader.cpp Outdated
Comment thread Dependencies/Miles/MilesLoader.cpp Outdated
Comment thread Dependencies/Miles/MilesLoader.cpp Outdated
Comment thread Dependencies/Miles/MilesLoader.cpp Outdated
Comment thread Dependencies/Miles/MilesLoader.cpp
Comment thread Dependencies/Miles/mss/mss.h Outdated
@greptile-apps

greptile-apps Bot commented Sep 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the link-time Bink and Miles SDK stubs with optional runtime-loaded DLL adapters.

  • Adds reference-counted loaders and neutral fallback implementations for the Bink and Miles APIs.
  • Updates the game, WWAudio, and build targets to use the new loader libraries.
  • Allows audio and video DLL discovery after command-line working-directory setup while permitting startup when either DLL is absent.
  • Balances unsuccessful loader acquisitions during teardown, addressing the previous review finding.

Confidence Score: 5/5

The PR appears safe to merge; no outstanding correctness or repository-rule violations were identified.

The previously reported failed-load reference imbalance is fully addressed because each integration path now performs its matching unload even when DLL loading fails, and no accepted new findings remain.

Important Files Changed

Filename Overview
Dependencies/Bink/BinkLoader.cpp Implements reference-counted runtime loading and fallback forwarding for the Bink API.
Dependencies/Miles/MilesLoader.cpp Implements runtime export resolution and neutral fallbacks for the Miles API surface.
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Integrates Miles loading with audio-device startup and balances the acquisition during shutdown.
Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Loads Bink during video initialization and releases the loader reference during deinitialization.
Core/Libraries/Source/WWVegas/WWAudio/WWAudio.cpp Integrates the Miles runtime loader with the WWAudio object lifecycle.
CMakeLists.txt Replaces generated SDK-stub setup with the new Bink and Miles loader subdirectories.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    App[Game or WWAudio initialization] --> Loader{Runtime loader}
    Loader -->|LoadLibrary succeeds| DLL[Bink or Miles DLL]
    DLL --> API[Forward API calls]
    Loader -->|DLL unavailable| Fallback[Neutral fallback behavior]
    Fallback --> Disabled[Video or audio remains disabled]
    API --> Teardown[Paired unload during teardown]
    Disabled --> Teardown
Loading

Reviews (3): Last reviewed commit: "refactor(miles): Fix the written primiti..." | Re-trigger Greptile

Comment thread Dependencies/Bink/BinkLoader.cpp

@OmniBlade OmniBlade left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fairly straight forward, just one question on refcounting.

Comment thread Dependencies/Miles/MilesLoader.cpp
Comment thread Dependencies/Bink/BinkLoader.cpp
xezon and others added 3 commits September 12, 2026 10:24
…(#3272)

The Bink SDK stub was linked as an import library, so binkw32.dll had to be
resolvable while the process image was still loading, long before WinMain and
therefore long before the command line was parsed. That is why -setCwd could not
point a build at a retail installation: the working directory it selects is set
far too late to influence how the library is found.

BinkLoader loads binkw32.dll explicitly once BinkVideoPlayer is initialized, at
which point the working directory is final. The Bink functions declared in bink.h
are now ordinary functions that forward to the matching export of the loaded
module, so no call site changes. An unresolved function returns the same neutral
value the stub library returned, which means a missing binkw32.dll disables video
playback instead of preventing the game from starting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…er (#3272)

The Miles SDK stub was linked as an import library, so mss32.dll had to be
resolvable while the process image was still loading, long before WinMain and
therefore long before the command line was parsed. That is why -setCwd could not
point a build at a retail installation: the working directory it selects is set
far too late to influence how the library is found.

MilesLoader loads mss32.dll explicitly once the audio device is opened, at which
point the working directory is final. The Miles functions declared in mss/mss.h
are now ordinary functions that forward to the matching export of the loaded
module, so no call site changes. An unresolved function returns the same neutral
value the stub library returned, which means a missing mss32.dll turns audio off
instead of preventing the game from starting.

Nine declarations were dropped along the way, because the retail mss32.dll does
not export them and nothing has called them since they were replaced by their
volume_pan counterparts: AIL_sample_volume, AIL_set_sample_volume, AIL_sample_pan,
AIL_set_sample_pan and the four stream equivalents, plus AIL_open_stream_by_sample.
The MSS_auto_cleanup hook was dropped as well, because its atexit handler would
have called AIL_shutdown after the module was already freed. All 92 remaining
exports were verified to resolve against the retail mss32.dll.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xezon
xezon force-pushed the xezon/replace-miles-bink-stubs branch from 875e5e9 to c795f10 Compare September 12, 2026 08:26
@xezon
xezon merged commit fd5caf0 into TheSuperHackers:main Sep 12, 2026
23 checks passed
xezon added a commit that referenced this pull request Sep 12, 2026
…3272)

The Bink SDK stub was linked as an import library, so binkw32.dll had to be
resolvable while the process image was still loading, long before WinMain and
therefore long before the command line was parsed. That is why -setCwd could not
point a build at a retail installation: the working directory it selects is set
far too late to influence how the library is found.

BinkLoader loads binkw32.dll explicitly once BinkVideoPlayer is initialized, at
which point the working directory is final. The Bink functions declared in bink.h
are now ordinary functions that forward to the matching export of the loaded
module, so no call site changes. An unresolved function returns the same neutral
value the stub library returned, which means a missing binkw32.dll disables video
playback instead of preventing the game from starting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xezon added a commit that referenced this pull request Sep 12, 2026
…er (#3272)

The Miles SDK stub was linked as an import library, so mss32.dll had to be
resolvable while the process image was still loading, long before WinMain and
therefore long before the command line was parsed. That is why -setCwd could not
point a build at a retail installation: the working directory it selects is set
far too late to influence how the library is found.

MilesLoader loads mss32.dll explicitly once the audio device is opened, at which
point the working directory is final. The Miles functions declared in mss/mss.h
are now ordinary functions that forward to the matching export of the loaded
module, so no call site changes. An unresolved function returns the same neutral
value the stub library returned, which means a missing mss32.dll turns audio off
instead of preventing the game from starting.

Nine declarations were dropped along the way, because the retail mss32.dll does
not export them and nothing has called them since they were replaced by their
volume_pan counterparts: AIL_sample_volume, AIL_set_sample_volume, AIL_sample_pan,
AIL_set_sample_pan and the four stream equivalents, plus AIL_open_stream_by_sample.
The MSS_auto_cleanup hook was dropped as well, because its atexit handler would
have called AIL_shutdown after the module was already freed. All 92 remaining
exports were verified to resolve against the retail mss32.dll.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xezon
xezon deleted the xezon/replace-miles-bink-stubs branch September 12, 2026 08:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build Anything related to building, compiling Refactor Edits the code with insignificant behavior changes, is never user facing System Is Systems related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants