Open mirh opened 2 years ago
Spoofing this "properly" turns out to be a bit more difficult than anticipated. Adding a unique user-specified name to the enumeration lists isn't that hard, nor would it be hard to open the default device when trying to open it. However, if the spoofed name is opened, having that device report back that spoofed name as its name is a bit more involved.
Another possibility would be an option to simply exclude the "OpenAL Soft on" part of the names, and return the full device list for basic and full enumeration. Essentially treating it as if each device on the system is provided by a hardware driver. This could be a problem if there's an actual hardware OpenAL driver also providing the device with a plain un-prefixed name too, though (and I wager if someone has an X-Fi or Audigy card, that would likely be the case).
Adding a unique user-specified name to the enumeration lists isn't that hard
?
Neither of my two ideas revolved around "adding" any name.
alcDefaultName
was to be replaced completely by whatever the solution would dictate.
nor would it be hard to open the default device when trying to open it. However, if the spoofed name is opened, having that device report back that spoofed name as its name is a bit more involved.
I'm getting lost here.
Another possibility would be an option to simply exclude the "OpenAL Soft on" part of the names, and return the full device list for basic and full enumeration. Essentially treating it as if each device on the system is provided by a hardware driver.
Yes, that's what I suggested in the first paragraph.
(and I wager if someone has an X-Fi or Audigy card, that would likely be the case).
Perhaps "dll names" precedence would dictate the winner. @Kappa971 Though I guess it would be certainly messy to distinguish the drivers (or could some unholy quirk just display one?). But anyway worst case scenario would have some instructions telling the users to just delete ct_oal or sens_oal.
Adding a unique user-specified name to the enumeration lists isn't that hard
? Neither of my two ideas revolved around "adding" any name.
alcDefaultName
was to be replaced completely by whatever the solution would dictate.
Right, but it's not that simple. alcDefaultName
is the basic/driver name. For general-purpose implementations, it's the implementation name, like OpenAL Soft, Rapture3D, or Generic Software. For hardware implementations, it's the name of the hardware device it's controlling (e.g. "Sound Blaster X-Fi" or whatever Windows calls it). As long as OpenAL Soft device names are still enumerated, whatever is used in place of alcDefaultName
still needs to include OpenAL Soft
in the list, and similar for the ENUMERATE_ALL list. It's basically spoofing a hardware driver in soft_oal.dll, where it's still normal OpenAL Soft, just with an added device name that it pretends is a hardware device.
The alternative would be to just pretend to be one or more hardware devices, and drop all references to being OpenAL Soft (in the device names, at least).
Yes, that's actually what I was suggesting in the first scenario.
In the second one instead alcDefaultName
would be free floating with whatever the users specifies.
E.g. this is what I had with just hex editing the second "OpenAL Soft" string that I found in the 1.22.2 dll.
All Available OpenAL Devices
Devices available with standard ALC_ENUMERATION_EXT
===================================================
OpenAL Spec/Ext Support
# Device Name Default 1.0 1.1 EFX
0 - OpenAL X-Fi X X X
1 - Generic Software X X
Devices available with ALC_ENUMERATE_ALL_EXT
============================================
OpenAL Spec/Ext Support
# Device Name Default 1.0 1.1 EFX
0 - OpenAL Soft on Speakers (X-Fi) (Realtek(R) Audio)
X X X
1 - OpenAL Soft on 27GL850 (NVIDIA High Definition Audio)
X X
2 - Generic Software on Speakers (X-Fi) (Realtek(R) Audio)
X X
3 - Generic Software on 27GL850 (NVIDIA High Definition Audio)
X X
Lmao, I just noticed that my little hack didn't affect the second query, yet for my intents and purpose it was good enough.
But this only happens if the dll is in SysWOW64. Otherwise the Generic Software on Speakers (X-Fi)
coming from wrap_oal there wins (even once I managed to get OpenAL X-Fi on Speakers (X-Fi)
too).
I have done some test in another discussion/issue.
If I remember correctly, the OpenAL router looks for "X-Fi" in both the OpenAL device name and the system device name. In my case it is Speakers (Creative SB X-Fi)
as Windows device name and SB X-Fi Audio [0001]
as OpenAL device name.
Another way is for the Windows device and the OpenAL device to have the exact same name, for example Speakers (Sound Blaster Z)
. In the case of the SB Z (and newer), it would "collide" with Host OpenAL which adopts the same solution. For Realtek cards I think there would be no problems.
If none of the above is valid, the router checks for Generic Hardware
. If OpenAL Soft is named "Generic Hardware" in ALC_ENUMERATION_EXT, it would be set as the default device, regardless of the Windows device name.
Would there be any problem with calling OpenAL Soft Generic Hardware
?
In the case of the SB Z (and newer), it would "collide" with Host OpenAL which adopts the same solution.
Yes, that's what we were wondering. But who wins eventually? And I imagine that you are getting two devices (with the same name) that you can hardly distinguish otherwise?
If none of the above is valid, the router checks for
Generic Hardware
It's not like they can check what doesn't exist...
Would there be any problem with calling OpenAL Soft like that?
This idea got me cringing. Then it got my laughing (thinking that for instance mass effect has an awful in-game toggle that can only force this name) And now I was kind puzzled (putting aside that well, it turns out something did indeed escape my tight logic). ... And while trying to come up with some counterpoint (e.g. could some lazy ass game interpret "Generic Hardware" reversely to mean directsound?) something even harder hit me.
The wrap_oal.dll entries weren't winning (in my last comment) because of some randomness.
The dlls whose folder is the one where the loaded OpenAL32.dll is from, are those on top of the world (at least in cases where partial matching is being used). And this is bad for games shipping their own router.
I don't know then if the Generic Hardware
couldn't have a different fate (its enumeration is somewhat of a special case of the special case), but if even that wouldn't play out I'm afraid that the only "drop once and forget" solution could be exact matching.
It's not like they can check what doesn't exist...
To know if the first two conditions are valid, I think the router actually checks if those names exist.
This idea got me cringing. Then it got my laughing (thinking that for instance mass effect has an awful in-game toggle that can only force this name) And now I was kind puzzled (putting aside that well, it turns out something did indeed escape my tight logic). ... And while trying to come up with some counterpoint (e.g. could some lazy ass game interpret "Generic Hardware" reversely to mean directsound?) something even harder hit me.
The wrap_oal.dll entries weren't winning (in my last comment) because of some randomness. The dlls whose folder is the one where the loaded OpenAL32.dll is from, are those on top of the world (at least in cases where partial matching is being used). And this is bad for games shipping their own router. I don't know then if the
Generic Hardware
couldn't have a different fate (its enumeration is somewhat of a special case of the special case), but if even that wouldn't play out I'm afraid that the only "drop once and forget" solution could be exact matching.
I can't give you a technical answer, but from what I understand, Creative's OpenAL router does certain checks:
If the name "Generic Hardware" causes no conflict, it would solve the problem. Otherwise uninstall Creative's OpenAL router and rename soft_oal.dll to OpenAL32.dll and paste it into System32/SysWOW64.
I guess "Generic Hardware" is the name of the device in wrap_oal.dll using DirectSound3D. It is probably something dating back to Windows XP and with Sound Blaster cards not supporting OpenAL. On modern versions of Windows it would probably never be detected (perhaps forcing it with ALchemy and removing ct_oal/sens_oal or any other driver, but it wouldn't make sense).
We have the source code of the router.. there isn't much to guess, only to be smart enough to comprehend it well.
And I just realized how big of a change a small difference in implementation between ALC_ENUMERATION_EXT
and ALC_ENUMERATE_ALL_EXT
can make.
Here you can see the former (notice how "playback device name" based entries will always strictly win over the generic renderers) https://github.com/kcat/openal-soft/blob/3bedb2b4e95e22353ccb0e976d5c320cd7572f10/Router/alc.cpp#L591-L620
And here's the later (set aside the first priority, and just focus on what that "all devices list" would mean for partial matching) https://github.com/kcat/openal-soft/blob/3bedb2b4e95e22353ccb0e976d5c320cd7572f10/Router/alc.cpp#L689-L706 Of course every "XX device driver on YY playback device" is always gonna match the YY playback device name! This includes openal-soft unsurprisingly, but this time "Generics" are in here too.
And the "Find Device" function is just a stupid for loop, that will break off on the first match.
This is what makes the "load order" count, insofar as you'll slyly get in front of others in the device list.
If in my previous EnumerateWin32 example, openal-soft had been 1 instead of 0 (something that can also happen in the same local folder if you just rename it to zoft_oal.dll
) it would still have been screwed.
So.. uh, no. The "Generic Hardware" idea wouldn't help the slightest for our last problem of ALC_ENUMERATE_ALL_EXT
partial matching. And there's nothing that gets checked before the OpenAL32.dll
folder (even if we somehow weaponized windows or something, some names their router BF2OpenAL.dll
or DefOpenAL32.dll
) so the only way out seems the exact one.
If not any if you don't want to tinker with the folders of every single game you install (full disclaimer: I don't really know of any one that employs this ~2007 extension as opposed to the old one, your opinion is welcome on whether we should even care then as a matter of principle or not)
Of course every "XX device driver on YY playback device" is always gonna match the YY playback device name! This includes openal-soft unsurprisingly, but this time "Generics" are in here too.
And the "Find Device" function is just a stupid for loop, that will break off on the first match. This is what makes the "load order" count, insofar as you'll slyly get in front of others in the device list. If in my previous EnumerateWin32 example, openal-soft had been 1 instead of 0 (something that can also happen in the same local folder if you just rename it to
zoft_oal.dll
) it would still have been screwed.So.. uh, no. The "Generic Hardware" idea wouldn't help the slightest for our last problem of
ALC_ENUMERATE_ALL_EXT
partial matching. And there's nothing that gets checked before theOpenAL32.dll
folder (even if we somehow weaponized windows or something, some names their routerBF2OpenAL.dll
orDefOpenAL32.dll
) so the only way out seems the exact one.If not any if you don't want to tinker with the folders of every single game you install (full disclaimer: I don't really know of any one that employs this ~2007 extension as opposed to the old one, your opinion is welcome on whether we should even care then as a matter of principle or not)
What is the point of this useless and long discussion? Neither is a programmer and arguing by making others seem to understand what you are talking about is ridiculous. What I wrote I tried, I understand "Generic Hardware" is just a text string that Creative's router accepts as the default device if nothing else is found. I only knew Unreal 2 to have an OpenAL router (is it a router? I don't know) which can still be disabled from the config file or renamed or deleted (so it uses OpenAL32.dll in the system folder like other games, I find it would be easier than changing the Windows device name from the registry). If you can find a game that has problems with "Generic Hardware", then the idea is to be discarded. @kcat could give us a more technical answer to this than we ever could, let's focus on the field tests.
I can notice that in ALC_ENUMERATE_ALL_EXT OpenAL Soft is already set as default device so I don't think there is anything to do here.
The point, which I already stated, is just for people not having to manage every single game.
You don't really need to touch the registry to change device name, but yes "Generic Hardware" could certainly avoid any extra bother once you assume Vista+ (my vagueness was just due to fact that I cannot hex edit such a long name, and thus I couldn't test it directly).
That would still still be achieved through the tentatively named ALSOFT_NAME
idea, so there wasn't really too much to discern here.
But if ALC_ENUMERATE_ALL_EXT
was to matter somewhere, then that still wouldn't be enough.
My results above were made from a clean folder, that didn't have the openal router together with the executable. So loading of just about every dll comes from the Windows folder, where "soft" comes alphabetically before "wrap".
But this lucky factor isn't there to save you from the precedence of folders order, and so system-only openal-soft gets behind the local-folder wrapper.
I don't know then if the
Generic Hardware
couldn't have a different fate
Well, well, well.. I may have unduly blamed the router beforehand, but while fishing for ALC_DEFAULT_ALL_DEVICES_SPECIFIER
quirks I found out discrimination can indeed happen on the side of applications.
https://github.com/OpenXRay/xray-16/blob/1144-december-2021-rc1/src/xrSound/OpenALDeviceList.cpp#L94
https://github.com/ioquake/ioq3/blob/main/code/client/snd_openal.c#L2573
In order to avoid bugs with DS3D, some engines forcefully switch you to Generic Software
if the alternative is "Generic Hardware".
A selected number of developers (I reckon those with new enough of an interest to look into openal after 2007) also did actually switch to using ALC_ENUMERATE_ALL_EXT
when detected.. but they didn't really re-check the logic they previously had, let alone re-thinking the implications of the difference between devices and endpoints.
By all means, this is only going to affect a minuscule fraction of applications (albeit this quick github search may have been quite the skewed sample) but nonetheless few is not zero.
https://github.com/OpenMW/openmw/blob/openmw-0.47.0/apps/openmw/mwsound/openal_output.cpp And now that I think to it, even those who did thoroughly take care of letting the user in charge.. Still sounds at the mercy of whatever the list order is, in order to pick up one to start with the first time (I'll grant this is really a trifle though)
But with all of this said then, not all is doom and gloom eventually.
https://github.com/kcat/openal-soft/blob/3bedb2b4e95e22353ccb0e976d5c320cd7572f10/Router/alc.cpp#L1860-L1861
The router itself is still relying on the default device from ALC_ENUMERATION_EXT
for alcOpenDevice(NULL)
calls.
And IMHO games fancy enough to have incorporated the newer extension should also be fancy enough to still have somebody somewhere supporting them somewhat (hell, some recent ones even outright decided to just come built-in with openal-soft and screw everything else).
So not all is lost for the ALSOFT_NAME
camp, which I think is the best shoot at trying to eat one's cake of 100% simple compatibility and have it too with "Enumerate All" features (just remember for the string to be a strict superset of "Generic Hardware", to avoid the consequences of XP-drivers hate).
But the tentatively named SOUNDBLASTER/XP/COMPATIBILITY__MODE
still sounds the safest bet for the time being.
So, after much pondering I think both proposal should be implemented.
Perhaps.. would it be possible for soft_oal to detect if it's getting called directly by some executable, or if instead OpenAL32.dll is involved? This way you could minimize the scope of the latest hack.
Of note that (of course, duh) older OpenAL routers would only have Audigy
available for partial matching.
https://github.com/rpavlik/openal-svn-mirror/blob/653d01b45fad8abccc41741b55318140f9a8a1da/win/Router/alc.cpp#L1858L1861 No wonder that sometimes even newer OG soundblaster users had to bang their heads.
Btw "Generic Hardware" may only be accepted if it's perfectly matching with the device name here (if it even works at all.. though again I cannot really be certain without actual testing). Meaning that we are kinda back to square one about any one solution being strictly better than the others.
EDIT: actually, I was reading a bit more into the S.T.A.L.K.E.R. source code, and they had an eax_unwanted property to blacklist all "Generic" devices from using eax
EDIT2: on top of that, if the default openal router device selection is Generic Hardware
they override it with Generic Software
(so that's kinda another blacklist too)
https://github.com/kcat/openal-soft/issues/1001#issuecomment-2308007137
Ok well, this starts to be really messy. Long story short some older games want AL_VENDOR
to start with "Creative" (and EAX3.0 to be reported as "present") in order to accept openal at all. On top of that, then:
waveOutMessage((HWAVEOUT)0xffffffff,0x2015,(DWORD_PTR)&UStack_c,(DWORD_PTR)auStack_10);
waveOutGetDevCapsA(UStack_c,&tStack_48,0x34);
if (*(int *)(iStack_14 + 0x28) == 0) {
pFVar2 = GetProcAddress(*(HMODULE *)(iStack_14 + 4),"alcGetString");
*(FARPROC *)(iStack_14 + 0x28) = pFVar2;
}
_Str1 = (char *)(**(code **)(iStack_14 + 0x28))(*(undefined4 *)(iStack_14 + 0xfc),0x1004);
if (*(int *)(iStack_14 + 0x24) == 0) {
pFVar2 = GetProcAddress(*(HMODULE *)(iStack_14 + 4),"alcIsExtensionPresent");
*(FARPROC *)(iStack_14 + 0x24) = pFVar2;
if (pFVar2 != (FARPROC)0x0) goto LAB_0094e67e;
}
else {
LAB_0094e67e:
cVar1 = (**(code **)(iStack_14 + 0x24))(*(undefined4 *)(iStack_14 + 0xfc),"ALC_ENUMERATION_EXT")
;
cStack_5 = '\x01' - (cVar1 != '\x01');
if (cStack_5 != '\0') {
while( true ) {
if (_Str1 == (char *)0x0) {
return 0;
}
iVar3 = _strncmp(_Str1,tStack_48.szPname,0xff);
if (iVar3 == 0) break;
do {
pcVar4 = _Str1;
pcVar5 = pcVar4 + 1;
_Str1 = pcVar5;
} while (*pcVar4 != '\0');
_Str1 = pcVar4;
if ((*pcVar4 == '\0') && (_Str1 = pcVar5, *pcVar5 == '\0')) {
return 0;
}
}
if (_Str1 == (char *)0x0) {
return 0;
}
goto LAB_0094e710;
}
}
if ((_Str1 == (char *)0x0) || (iVar3 = _strncmp(_Str1,tStack_48.szPname,0xff), iVar3 != 0)) {
return 0;
}
LAB_0094e710:
_strncpy(param_1,_Str1,0x103);
param_1[0x103] = '\0';
return 1;
}
Across as far as I could decompile from Just Cause (gamecoda 2) there are no further "generic" checks, there is no soundblaster or audigy partial matching - just presumably WAVEOUTCAPS.szPname==ALC_DEFAULT_DEVICE_SPECIFIER
.
EDIT: of note that while that's pretty much exactly what the openal router was doing once upon a time, and god willing directsound shouldn't return different names, MMSYSTEM enumeration has MAXPNAMELEN which is only 32 characters long (31 once accounted for the null termination). And in this sense, I think it could be safely assumed this limitation is carried by at least all the older openal 1.0 software
So, with that being the premise (an exact match being required), and with Windows not even allowing to rename audio devices enough (even if you tinker with HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render
AFAICT you still cannot drop the brackets from the hardware name)* evidently only changing the later part of the equation seems possible to make everything and everyone happy**.
And since you don't want switching between your monitor's speakers and your headphones to be a bother, it should be an automatic thing always tracking PKEY_Device_FriendlyName.
Conversely, I think this should really be kept as low key as possible (it's already a delicate quirk enough) by only involving the minimum amount of variables that we know to affect games. So I believe all the individual overrides should be removed (except perhaps the name override which, I don't know, it seems cute to have?).
Even because, honestly, it seems completely useless to be able to change the openal version.. And there's exactly only one special AL_VENDOR matching if any (btw Creative by OpenAL Community
could still be fine)
And then maybe this could be also just one additional name to ALC_DEVICE_SPECIFIER
, as opposed to replacing it completely?
Last but not least I don't know then if it's possible to detect at compile time if openal-soft is going to be used as a stand-alone library, or be statically linked in an application.. But regardless, to to be even more humble with this special mode it could be further guarded by checking at runtime whether we run from a dll.
The only question I still have now, was for a decent name for it. ALSOFT_COMPATIBILITY_MODE
? ALSOFT_CREATIVE
? ALSOFT_OLDBUTGOLD
? OPENAL_LEAN_AND_MEAN
?
* the openAL router actually seemed to be aware of this possible incongruence, but they completely forgot about it in the latest versions ** to be fair I also found a case, where "Generic Software" is ALWAYS forced but for probably the same reason this even happened (being a few linux-primary randos making an open indie game) this was eventually fixed
See https://github.com/kcat/openal-soft/issues/650#issuecomment-1186326473. In order to play nice with Creative's official OAL router (and yes, this seems the only loophole to avoid recurrent maintenance of either system or games folders) the device name is key. https://github.com/kcat/openal-soft/blob/b8d73a226ab4251f94a9096fa95a20fac58e826b/alc/alc.cpp#L919 The best way to this aim, if not any because it would be entirely addressable within openal-soft, would be for its name to always match whatever the current default device in windows is (as per K971's observations). But AFAICT this would break total mayhem in
ALC_ENUMERATE_ALL_EXT
querying. ...unless perhaps the same knob also enabled "soundblaster-like behaviour" (i.e. only care about a single device, don't report "XX on YY")?And so the next (and only other possible) solution would be to rely on the "partial name match" path. I'm not 100% sure on how exactly they filter the list of the available implementations depending on the name of the current device.. but anyway long story short, if both the device and the openal driver have "X-Fi" or "Audigy" in the name they get more priority than the godawful "Generic Software" fallback. And so, rather than just relying on some other kind of hardcoded strings, I thought that we could as well just use an environmental variable to arbitrarily change the name (so that you wouldn't have to include in the repo those names, or other awfully similar puns). Of course the user would also have to be instructed to rename their playback device, but this is another matter.
p.s. I had originally started this feature request to just propose the second idea, but throughout the writing of the premise (and of the logical implications that would entail there aren't other ways to make OpenAL32.dll happy) the first oddball emerged too. I'm not asking for both, or even one of them in particular.. but yeah. For the moment, after all this brainstorming I'd just like to receive a reality check if I haven't missed anything else. EDIT: and it's ironic that they were the ones warning against "second guessing"