diff --git a/Doc/FuncStats.xlsx b/Doc/FuncStats.xlsx index fb21803..032240b 100644 Binary files a/Doc/FuncStats.xlsx and b/Doc/FuncStats.xlsx differ diff --git a/SpaceCadetPinball/WaveMix.cpp b/SpaceCadetPinball/WaveMix.cpp index 8af1ff6..37cefeb 100644 --- a/SpaceCadetPinball/WaveMix.cpp +++ b/SpaceCadetPinball/WaveMix.cpp @@ -1,9 +1,81 @@ #include "pch.h" #include "WaveMix.h" +int WaveMix::initialized_flag; +char WaveMix::FileName[276]; +CHANNELNODE WaveMix::channel_nodes[MAXQUEUEDWAVES]; +CHANNELNODE* WaveMix::free_channel_nodes; +char WaveMix::volume_table[256 * 11]; +int WaveMix::debug_flag; +int WaveMix::cmixit_ptr; +HMODULE WaveMix::HModule; +PCMWAVEFORMAT WaveMix::gpFormat = {{1u, 1u, 11025u, 11025u, 1u}, 8u}; +char WaveMix::string_buffer[256] = "WaveMix V 2.3 by Angel M. Diaz, Jr. (c) Microsoft 1993-1995"; +GLOBALS* WaveMix::Globals; +int WaveMix::ShowDebugDialogs; + HANDLE WaveMix::Init() { - return (HANDLE)1; + return ConfigureInit(nullptr); +} + +HANDLE WaveMix::ConfigureInit(MIXCONFIG* lpConfig) +{ + MIXCONFIG mixConfig{}; + + memset(&mixConfig, 0, 0x1Cu); + unsigned int copySize = 30; + mixConfig.RegistryKey = nullptr; + mixConfig.wSize = 30; + if (lpConfig) + { + if (lpConfig->wSize < 30u) + copySize = lpConfig->wSize; + memcpy(&mixConfig, lpConfig, copySize); + } + if (initialized_flag || Startup(GetModuleHandleA(nullptr)) != 0) + { + bool showDebugDialogs; + if ((mixConfig.dwFlags & 0x100) != 0) + showDebugDialogs = mixConfig.ShowDebugDialogs != 0; + else + showDebugDialogs = GetPrivateProfileIntA("general", "ShowDebugDialogs", 0, FileName) != 0; + ShowDebugDialogs = showDebugDialogs; + if (!waveOutGetNumDevs()) + { + if (ShowDebugDialogs) + { + wsprintfA(string_buffer, "This system does not have a valid wave output device."); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); + } + return nullptr; + } + + if (GetPrivateProfileIntA("general", "ShowDevices", 0, FileName)) + ShowWaveOutDevices(); + auto globals = static_cast(LocalAlloc(0x40u, 0x1C0u)); + Globals = globals; + if (!globals) + return nullptr; + globals->CmixPtr = cmixit_ptr; + globals->wMagic2 = 21554; + globals->wMagic1 = 21554; + globals->unknown102 = 0; + globals->unknown5 = 0; + globals->unknown44 = 655370; + memset(globals->aChannel, 0xFFu, sizeof globals->aChannel); + memmove(&globals->PCM, &gpFormat, 0x10u); + if (!ReadConfigSettings(&mixConfig)) + { + Globals->wMagic2 = 0; + Globals->wMagic1 = 0; + LocalFree(Globals); + Globals = nullptr; + } + return Globals; + } + + return nullptr; } int WaveMix::CloseSession(HANDLE hMixSession) @@ -23,6 +95,52 @@ int WaveMix::CloseChannel(HANDLE hMixSession, int iChannel, unsigned dwFlags) int WaveMix::FlushChannel(HANDLE hMixSession, int iChannel, unsigned dwFlags) { + int channelId; + int lastChannel; + + int remixFlag = 0; + auto globals = SessionToGlobalDataPtr(hMixSession); + Globals = globals; + if (!globals) + return 5; + + if ((dwFlags & 1) != 0) + { + channelId = 0; + lastChannel = 16; + } + else + { + channelId = iChannel; + if (iChannel < 0 || iChannel >= 16) + return 11; + if (globals->aChannel[iChannel] == reinterpret_cast(-1)) + return 5; + lastChannel = iChannel + 1; + if (iChannel >= lastChannel) + return 0; + } + + for (auto index = channelId; index < lastChannel; index++) + { + auto curChannel = globals->aChannel[index]; + if (curChannel != reinterpret_cast(-1)) + { + globals->aChannel[index] = nullptr; + remixFlag |= curChannel != nullptr; + while (curChannel) + { + auto tmp = curChannel->next; + FreeChannelNode(curChannel); + curChannel = tmp; + } + } + } + + if (remixFlag && (dwFlags & 2) == 0 && globals->fActive) + { + globals->pfnRemix(MyWaveOutGetPosition(globals->hWaveOut, globals->fGoodGetPos), nullptr); + } return 0; } @@ -49,3 +167,285 @@ int WaveMix::Play(MIXPLAYPARAMS* lpMixPlayParams) { return 0; } + +GLOBALS* WaveMix::SessionToGlobalDataPtr(HANDLE hMixSession) +{ + auto globals = static_cast(hMixSession); + if (hMixSession && globals->wMagic1 == 21554 && globals->wMagic2 == 21554) + return globals; + MessageBeep(0xFFFFFFFF); + wsprintfA(string_buffer, "Invalid session handle 0x%04X passed to WaveMix API", hMixSession); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x30u); + return nullptr; +} + +int WaveMix::Startup(HMODULE hModule) +{ + WNDCLASSA WndClass; + + if (initialized_flag) + return 1; + if (!SetIniFileName(hModule)) + return 0; + InitVolumeTable(); + debug_flag = GetPrivateProfileIntA("general", "debug", 0, FileName); + cmixit_ptr = (int)cmixit; + HModule = hModule; + + WndClass.hCursor = LoadCursorA(nullptr, IDC_ARROW); + WndClass.hIcon = nullptr; + WndClass.lpszMenuName = nullptr; + WndClass.lpszClassName = "WavMix32"; + WndClass.hbrBackground = static_cast(GetStockObject(1)); + WndClass.hInstance = HModule; + WndClass.style = 0; + WndClass.lpfnWndProc = WndProc; + WndClass.cbWndExtra = 0; + WndClass.cbClsExtra = 0; + if (!RegisterClassA(&WndClass)) + return 0; + InitChannelNodes(); + initialized_flag = 1; + return 1; +} + +int WaveMix::SetIniFileName(HMODULE hModule) +{ + int result = GetModuleFileNameA(hModule, FileName, 0x104u); + if (result) + { + char* i; + for (i = &FileName[result]; *i != '\\'; --i) + { + } + *i = 0; + lstrcpyA(i, "\\WAVEMIX.INF"); + result = 1; + } + return result; +} + +void WaveMix::InitChannelNodes() +{ + CHANNELNODE* channelPtr = channel_nodes; + do + { + channelPtr->next = channelPtr + 1; + ++channelPtr; + } + while (channelPtr < &channel_nodes[MAXQUEUEDWAVES - 2]); + channel_nodes[MAXQUEUEDWAVES - 1].next = nullptr; + free_channel_nodes = channel_nodes; +} + +void WaveMix::InitVolumeTable() +{ + int index2 = 0; + int index3Sub = 0; + char* tablePtr = &volume_table[128]; + do + { + int index1 = -128; + int divSmth = index3Sub; + do + { + tablePtr[index1] = static_cast(divSmth / 10 + 128); + divSmth += index2; + ++index1; + } + while (index1 < 128); + ++index2; + index3Sub -= 128; + tablePtr += 256; + } + while (tablePtr <= &volume_table[2688]); +} + +void WaveMix::ShowWaveOutDevices() +{ + tagWAVEOUTCAPSA pwoc{}; + + auto deviceCount = waveOutGetNumDevs(); + if (deviceCount) + { + wsprintfA(string_buffer, "%d waveOut Devices have been detected on your system.", deviceCount); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); + for (auto uDeviceID = 0u; uDeviceID < deviceCount; ++uDeviceID) + { + if (!waveOutGetDevCapsA(uDeviceID, &pwoc, 0x34u) && RemoveInvalidIniNameCharacters(pwoc.szPname)) + wsprintfA( + string_buffer, + "Device %i: %s\n\tVersion %u.%u", + uDeviceID, + pwoc.szPname, + HIBYTE(pwoc.vDriverVersion), + LOBYTE(pwoc.vDriverVersion)); + else + wsprintfA(string_buffer, "waveOutGetDevCaps failed (err %u) for device %d", 1, uDeviceID); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); + } + } +} + +int WaveMix::RemoveInvalidIniNameCharacters(char* lpString) +{ + auto stringPtr = lpString; + if (!lpString || !*lpString) + return 0; + do + { + if (!isalnum(*stringPtr) && !isspace(*stringPtr)) + break; + ++stringPtr; + } + while (*stringPtr); + + do + *stringPtr-- = 0; + while (stringPtr >= lpString && isspace(*stringPtr)); + return lstrlenA(lpString); +} + +int WaveMix::ReadConfigSettings(MIXCONFIG* lpConfig) +{ + auto waveDeviceCount = waveOutGetNumDevs(); + if (!waveDeviceCount) + return 0; + if ((lpConfig->dwFlags & 0x400) != 0 && ReadRegistryForAppSpecificConfigs(lpConfig)) + { + Configure(Globals, nullptr, lpConfig, nullptr, 0); + return 1; + } + + return 1; +} + +int WaveMix::ReadRegistryForAppSpecificConfigs(MIXCONFIG* lpConfig) +{ + HKEY phkResult; + CHAR SubKey[52]; + + DWORD dwFlags = lpConfig->dwFlags; + if ((dwFlags & 0x400) == 0 || !lpConfig->RegistryKey) + return 0; + if ((dwFlags & 0x80u) == 0) + { + lpConfig->wDeviceID = Globals->wDeviceID; + } + else if (lpConfig->wDeviceID >= waveOutGetNumDevs()) + { + lpConfig->wDeviceID = 0; + } + wsprintfA(SubKey, "WaveMix\\Device%u", lpConfig->wDeviceID); + if (RegOpenKeyA(lpConfig->RegistryKey, SubKey, &phkResult)) + return 0; + if ((dwFlags & 1) == 0) + lpConfig->wChannels = ReadRegistryInt(phkResult, "Channels", 1); + if ((dwFlags & 2) == 0) + lpConfig->wSamplingRate = ReadRegistryInt(phkResult, "SamplesPerSec", 11); + if ((dwFlags & 4) == 0) + lpConfig->WaveBlocks = static_cast(ReadRegistryInt(phkResult, "WaveBlocks", 3)); + if ((dwFlags & 8) == 0) + lpConfig->WaveBlockLen = static_cast(ReadRegistryInt(phkResult, "WaveBlockLen", 0)); + lpConfig->CmixPtrDefaultFlag = 1; + if ((dwFlags & 0x20) == 0) + lpConfig->ResetMixDefaultFlag = static_cast(ReadRegistryInt(phkResult, "Remix", 1)) != 2; + if ((dwFlags & 0x40) == 0) + { + int defaultGoodWavePos = DefaultGoodWavePos(lpConfig->wDeviceID); + lpConfig->GoodWavePos = static_cast(ReadRegistryInt( + phkResult, "GoodWavePos", defaultGoodWavePos)) != 0; + } + if ((dwFlags & 0x100) == 0) + lpConfig->ShowDebugDialogs = static_cast(ReadRegistryInt(phkResult, "ShowDebugDialogs", 0)); + if ((dwFlags & 0x200) == 0) + { + int defaultPauseBlocks = DefaultPauseBlocks(static_cast(lpConfig->WaveBlocks)); + lpConfig->PauseBlocks = static_cast(ReadRegistryInt(phkResult, "PauseBlocks", defaultPauseBlocks)); + } + lpConfig->dwFlags = 1023; + waveOutGetDevCapsA(lpConfig->wDeviceID, &Globals->WaveoutCaps, 0x34u); + RegCloseKey(phkResult); + return 1; +} + +int WaveMix::ReadRegistryInt(HKEY hKey, LPCSTR lpSubKey, int defaultValue) +{ + int result; + LONG cbData = 10; + char Data[12]; + + if (!hKey || RegQueryValueA(hKey, lpSubKey, Data, &cbData)) + result = defaultValue; + else + result = atol(Data); + return result; +} + +int WaveMix::DefaultGoodWavePos(unsigned uDeviceID) +{ + int result; + struct tagWAVEOUTCAPSA pwoc{}; + + auto deviceCount = waveOutGetNumDevs(); + if (uDeviceID > deviceCount || (uDeviceID & 0x80000000) != 0 || + !deviceCount || waveOutGetDevCapsA(uDeviceID, &pwoc, 0x34u)) + { + result = 0; + } + else + { + result = (LOBYTE(pwoc.dwSupport) >> 5) & 1; + } + return result; +} + +#pragma warning (disable : 4996)/*Original uses GetVersion*/ +int WaveMix::DefaultPauseBlocks(int waveBlocks) +{ + int result; + if (GetVersion() < 0x80000000 || static_cast(GetVersion()) < 4u) + result = waveBlocks; + else + result = 0; + return result; +} +#pragma warning (default : 4996) + +int WaveMix::Configure(GLOBALS* hMixSession, HWND hWndParent, MIXCONFIG* lpConfig, int* flag1Ptr, int saveConfigFlag) +{ + return 1; +} + +unsigned WaveMix::MyWaveOutGetPosition(HWAVEOUT hwo, int fGoodGetPos) +{ + mmtime_tag pmmt{}; + + if (!fGoodGetPos) + return ((timeGetTime() - Globals->dwBaseTime) * Globals->PCM.wf.nAvgBytesPerSec / 0x3E8) & 0xFFFFFFF8; + pmmt.wType = 4; + waveOutGetPosition(hwo, &pmmt, 0xCu); + return Globals->pfnSampleAdjust(pmmt.u.ms, Globals->dwWaveOutPos); +} + +void WaveMix::FreeChannelNode(CHANNELNODE* channel) +{ + if (channel) + { + channel->next = free_channel_nodes; + free_channel_nodes = channel; + } +} + +short WaveMix::cmixit(BYTE* a1, char* a2, char* a3, int a4, unsigned short a5) +{ + return 0; +} + +LRESULT WaveMix::WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + if (Msg != 957 && Msg != 1024) + return DefWindowProcA(hWnd, Msg, wParam, lParam); + Pump(); + return 0; +} diff --git a/SpaceCadetPinball/WaveMix.h b/SpaceCadetPinball/WaveMix.h index 38f028f..c6b7e2b 100644 --- a/SpaceCadetPinball/WaveMix.h +++ b/SpaceCadetPinball/WaveMix.h @@ -8,6 +8,7 @@ #define WMIX_WAIT 0x08 #define MAXCHANNELS 16 +#define MAXQUEUEDWAVES 100 struct MIXWAVE { @@ -28,10 +29,131 @@ struct MIXPLAYPARAMS WORD wLoops; }; +struct CHANNELNODE +{ + CHANNELNODE* next; + MIXPLAYPARAMS PlayParams; + MIXWAVE* lpMixWave; + DWORD dwNumSamples; + DWORD dwStartPos; + DWORD dwEndPos; + char* lpPos; + char* lpEnd; + int Unknown0; + int Unknown1; +}; + +struct MIXCONFIG +{ + WORD wSize; + DWORD dwFlags; + WORD wChannels; + WORD wSamplingRate; + __int16 WaveBlocks; + __int16 WaveBlockLen; + __int16 CmixPtrDefaultFlag; + unsigned __int16 ResetMixDefaultFlag; + unsigned __int16 GoodWavePos; + unsigned __int16 wDeviceID; + __int16 PauseBlocks; + __int16 ShowDebugDialogs; + HKEY RegistryKey; +}; + +struct GLOBALS +{ + WORD wMagic1; + __int16 unknown0; + int unknown1; + int unknown2; + HWAVEOUT hWaveOut; + int fActive; + int unknown5; + unsigned int wDeviceID; + int unknown7; + int unknown8; + int unknown9; + int unknown10; + int unknown11; + int unknown12; + int unknown13; + int unknown14; + int unknown15; + int unknown16; + int unknown17; + int unknown18; + int unknown19; + int unknown20; + int unknown21; + int unknown22; + int unknown23; + int unknown24; + int unknown25; + int unknown26; + int unknown27; + int unknown28; + int unknown29; + int unknown30; + WAVEOUTCAPSA WaveoutCaps; + int unknown44; + int unknown45; + int unknown46; + int unknown47; + int unknown48; + int unknown49; + int unknown50; + int unknown51; + int unknown52; + int unknown53; + int unknown54; + int unknown55; + int unknown56; + int unknown57; + int unknown58; + int unknown59; + int unknown60; + CHANNELNODE* aChannel[16]; + int unknown77; + int unknown78; + int unknown79; + int unknown80; + int unknown81; + int unknown82; + int unknown83; + int unknown84; + int unknown85; + int unknown86; + int unknown87; + int unknown88; + int unknown89; + int unknown90; + int unknown91; + int unknown92; + int unknown93; + int unknown94; + PCMWAVEFORMAT PCM; + int WaveBlockLen; + int WaveBlockCount; + int PauseBlocks; + int unknown102; + int unknown103; + DWORD dwBaseTime; + int fGoodGetPos; + int dwWaveOutPos; + int CmixPtr; + void(__stdcall* pfnRemix)(DWORD, CHANNELNODE*); + int (__stdcall* pfnSampleAdjust)(int, int); + int unknown110; + __int16 wMagic2; + __int16 unknown112; +}; + + class WaveMix { public: static HANDLE Init(); + static HANDLE ConfigureInit(MIXCONFIG* lpConfig); static int CloseSession(HANDLE hMixSession); static int OpenChannel(HANDLE hMixSession, int iChannel, unsigned int dwFlags); static int CloseChannel(HANDLE hMixSession, int iChannel, unsigned int dwFlags); @@ -41,4 +163,36 @@ public: static int Activate(HANDLE hMixSession, bool fActivate); static void Pump(); static int Play(MIXPLAYPARAMS* lpMixPlayParams); + +private: + static GLOBALS* SessionToGlobalDataPtr(HANDLE hMixSession); + static int Startup(HMODULE hModule); + static int SetIniFileName(HMODULE hModule); + static void InitChannelNodes(); + static void InitVolumeTable(); + static void ShowWaveOutDevices(); + static int RemoveInvalidIniNameCharacters(char* lpString); + static int ReadConfigSettings(MIXCONFIG* lpConfig); + static int ReadRegistryForAppSpecificConfigs(MIXCONFIG* lpConfig); + static int ReadRegistryInt(HKEY hKey, LPCSTR lpSubKey, int defaultValue); + static int DefaultGoodWavePos(unsigned int uDeviceID); + static int DefaultPauseBlocks(int waveBlocks); + static int Configure(GLOBALS* hMixSession, HWND hWndParent, MIXCONFIG* lpConfig, int* flag1Ptr, int saveConfigFlag); + static unsigned MyWaveOutGetPosition(HWAVEOUT hwo, int fGoodGetPos); + static void FreeChannelNode(CHANNELNODE* channel); + static __int16 cmixit(BYTE* a1, char* a2, char* a3, int a4, unsigned __int16 a5); + static LRESULT __stdcall WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + + static int initialized_flag; + static char FileName[276]; + static CHANNELNODE channel_nodes[MAXQUEUEDWAVES]; + static CHANNELNODE* free_channel_nodes; + static char volume_table[256 * 11]; + static int debug_flag; + static int cmixit_ptr; + static HMODULE HModule; + static GLOBALS* Globals; + static PCMWAVEFORMAT gpFormat; + static int ShowDebugDialogs; + static char string_buffer[256]; };