diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f0ec9263d..7211c00df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,6 +109,30 @@ jobs: symbols retention-days: 1 + compile-linux: + if: github.repository == 'IDI-Systems/acre2' && ! contains(github.event.head_commit.message, '[ci skip]') + runs-on: ubuntu-latest + steps: + - name: Checkout the source code + uses: actions/checkout@v2 + - name: Install Packages + run: | + sudo apt update + sudo DEBIAN_FRONTEND=noninteractive apt install -y libfaudio-dev libtbb-dev + - name: Build Extensions + run: | + mkdir -p extensions/build + cd extensions/build + cmake -DUSE_64BIT_BUILD=ON .. + make -j + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: acre2-extensions-linux + path: | + plugin + retention-days: 1 + publish-test: needs: [build, compile] if: github.event_name == 'workflow_dispatch' @@ -123,6 +147,7 @@ jobs: mv acre2-extensions-*/*.dll @acre2/ mv acre2-extensions-*/extras/*.exe @acre2/extras/ mkdir @acre2/plugin && cp -r acre2-extensions-*/plugin/* @acre2/plugin/ # mv can't merge directories + cp -r acre2-extensions-linux/mumble @acre2/plugin || true mkdir symbols && mv acre2-extensions-*/symbols/* . echo "::group::Archive build" zip -r acre2_${{ needs.build.outputs.VERSION }}${{ github.event.inputs.label }}.zip @acre2 @@ -171,6 +196,7 @@ jobs: mv acre2-extensions-*/*.dll @acre2/ mv acre2-extensions-*/extras/*.exe @acre2/extras/ mkdir @acre2/plugin && cp -r acre2-extensions-*/plugin/* @acre2/plugin/ # mv can't merge directories + cp -r acre2-extensions-linux/mumble @acre2/plugin || true mkdir symbols && mv acre2-extensions-*/symbols/* . echo "::group::Archive build" zip -r acre2_${{ needs.build.outputs.VERSION }}.zip @acre2 @@ -229,6 +255,7 @@ jobs: mv acre2-extensions-*/*.dll @acre2/ mv acre2-extensions-*/extras/*.exe @acre2/extras/ mkdir @acre2/plugin && cp -r acre2-extensions-*/plugin/* @acre2/plugin/ # mv can't merge directories + cp -r acre2-extensions-linux/mumble @acre2/plugin || true mkdir symbols && mv acre2-extensions-*/symbols/* . echo "::group::Archive build" zip -r acre2_${{ needs.build.outputs.VERSION }}.zip @acre2 diff --git a/addons/sys_core/initSettings.sqf b/addons/sys_core/initSettings.sqf index 8aa04a81c..981be8386 100644 --- a/addons/sys_core/initSettings.sqf +++ b/addons/sys_core/initSettings.sqf @@ -154,6 +154,18 @@ {[_this] call FUNC(setRevealToAI)} ] call CBA_fnc_addSetting; +if ("ACRE2Arma" callExtension "99" == "1") then { + // Wine Socket Port + [ + QGVAR(wineSocketPort), + "EDITBOX", + ["Wine Socket Port", "(Linux only) What port should ACRE2 try to connect to Mumble on?"], + "ACRE2 Wine", + "19141", + false + ] call CBA_fnc_addSetting; +}; + // Notification Settings - not yet implemented /*[ QGVAR(incomingTransmissionNotification), diff --git a/addons/sys_io/fnc_server.sqf b/addons/sys_io/fnc_server.sqf index 34a45fb43..217930d9c 100644 --- a/addons/sys_io/fnc_server.sqf +++ b/addons/sys_io/fnc_server.sqf @@ -23,7 +23,11 @@ DFUNC(connectionFnc) = { LOG("ATEEEMPTING TO OPEN PIPE!"); // acre_player sideChat "OPEN PIPE"; GVAR(pongTime) = diag_tickTime; - GVAR(pipeCode) = "ACRE2Arma" callExtension "0ts"; // Connect String + extensionParameter = "0"; + if (!isNil {EGVAR(sys_core,wineSocketPort)}) then { + extensionParameter = "0p" + EGVAR(sys_core,wineSocketPort); + }; + GVAR(pipeCode) = "ACRE2Arma" callExtension extensionParameter; // Connect String // acre_player sideChat format["RESULT: %1", GVAR(pipeCode)]; if (GVAR(pipeCode) != "1") then { if (time > 15) then { diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index 03d9dc0f2..b5e2e6556 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -9,11 +9,13 @@ option(USE_STATIC_LINKING "USE_STATIC_LINKING" ON) set(CMAKE_BUILD_TYPE "RelWithDebInfo") -find_package(DirectX) -if(DirectX_FOUND) - message("Found DirectX") -else() - message(FATAL_ERROR "DirectX NOT FOUND") +if (WIN32) + find_package(DirectX) + if(DirectX_FOUND) + message("Found DirectX") + else() + message(FATAL_ERROR "DirectX NOT FOUND") + endif() endif() if(USE_STATIC_LINKING) @@ -35,13 +37,21 @@ set(CMAKE_CL_64 ${USE_64BIT_BUILD}) if(USE_64BIT_BUILD) message("INFO: Building 64-bit projects") set(ACRE_ARCH "x64") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/win64/") - link_directories("${DirectX_ROOT_DIR}/lib/x64") + if(WIN32) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/win64/") + link_directories("${DirectX_ROOT_DIR}/lib/x64") + else() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/x86_64-linux-gnu/") + endif() else() message("INFO: Building 32-bit projects") set(ACRE_ARCH "x86") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/win32/") - link_directories("${DirectX_ROOT_DIR}/lib/x86") + if(WIN32) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/win32/") + link_directories("${DirectX_ROOT_DIR}/lib/x86") + else() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/i686-linux-gnu/") + endif() endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -65,12 +75,15 @@ include_directories(src/ACRE2Core) add_subdirectory(src/ACRE2Shared) add_subdirectory(src/ACRE2Core) -add_subdirectory(src/ACRE2Arma) -add_subdirectory(src/ACRE2Steam) - -add_subdirectory(src/ACRE2TS) add_subdirectory(src/ACRE2Mumble) -#Extras -add_subdirectory(src/Wav2B64) -#add_subdirectory(src/ACRE2DistortionTestPlugin) +if (WIN32) + add_subdirectory(src/ACRE2Arma) + add_subdirectory(src/ACRE2Steam) + + add_subdirectory(src/ACRE2TS) + + #Extras + add_subdirectory(src/Wav2B64) + #add_subdirectory(src/ACRE2DistortionTestPlugin) +endif() diff --git a/extensions/src/ACRE2Arma/arma2ts/CallExt_DllMain.cpp b/extensions/src/ACRE2Arma/arma2ts/CallExt_DllMain.cpp index d130d00c2..dbdfbb21a 100644 --- a/extensions/src/ACRE2Arma/arma2ts/CallExt_DllMain.cpp +++ b/extensions/src/ACRE2Arma/arma2ts/CallExt_DllMain.cpp @@ -9,23 +9,34 @@ #include #include "shlobj.h" #include "Shlwapi.h" -#include -#include +#include +#include #include #include +#include +#include +#include +#include "wine.h" + #pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "Ws2_32.lib") #define PIPE_COMMAND_OPEN 0 #define PIPE_COMMAND_CLOSE 1 #define PIPE_COMMAND_WRITE 2 #define PIPE_COMMAND_READ 3 #define PIPE_COMMAND_RESET 4 +#define COMMAND_IS_WINE 99 + +#define WINE_SOCKET_PORT "19141" #define FROM_PIPENAME_TS "\\\\.\\pipe\\acre_comm_pipe_fromTS" #define TO_PIPENAME_TS "\\\\.\\pipe\\acre_comm_pipe_toTS" +char isWine; +SOCKET wineSocket = INVALID_SOCKET; HANDLE writeHandle = INVALID_HANDLE_VALUE; HANDLE readHandle = INVALID_HANDLE_VALUE; @@ -34,10 +45,13 @@ BOOL writeConnected, readConnected; void ClosePipe(); +void CloseWineSocket(); +void __stdcall runCommandWine(char* output, int outputSize, int command, std::string params); +void __stdcall runCommand(char* output, int outputSize, int command, std::string params); extern "C" { __declspec (dllexport) void __stdcall RVExtensionVersion(char *output, int outputSize); - __declspec(dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function); + __declspec(dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function); }; void __stdcall RVExtensionVersion(char *output, int outputSize) { @@ -57,7 +71,7 @@ inline std::string get_cmdline() { inline std::string get_path(std::string filepath) { char drive[_MAX_DRIVE]; char dir [_MAX_DIR]; - + _splitpath( filepath.c_str(), drive, @@ -106,7 +120,7 @@ inline std::string find_mod_file(std::string filename) { std::string path = std::string(drive) + std::string(dir) + "\\" + filename; if (!PathFileExistsA(path.c_str())) { - // No mod path was set, it means they used the mod config. It *DOES* mean it relative to a folder in our path at least. + // No mod path was set, it means they used the mod config. It *DOES* mean it relative to a folder in our path at least. // So, we just search all the local folders WIN32_FIND_DATAA data; @@ -133,7 +147,7 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { size_t id_length = 1; std::string functionStr = std::string(function); - + if (functionStr.length() > 1) { if (isdigit(functionStr.substr(1, 1).c_str()[0])) { id_length = 2; @@ -148,22 +162,30 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) int command = atoi(id.c_str()); + if (isWine) { + runCommandWine(output, outputSize, command, params); + } else { + runCommand(output, outputSize, command, params); + } +} + +void __stdcall runCommand(char* output, int outputSize, int command, std::string params) { switch(command) { - + case PIPE_COMMAND_WRITE: { if (writeConnected) { if (params.length() > 0) { DWORD cbWritten; BOOL ret; //DEBUG("Writing [%s] to pipe [%d]\n", params.c_str(), hPipe); - - // Send a message to the pipe server. - ret = WriteFile( - writeHandle, // pipe handle - params.c_str(), // message - params.length(), // message length - &cbWritten, // bytes written - NULL); // not overlapped + + // Send a message to the pipe server. + ret = WriteFile( + writeHandle, // pipe handle + params.c_str(), // message + params.length(), // message length + &cbWritten, // bytes written + NULL); // not overlapped if (cbWritten != params.length()) { FlushFileBuffers(writeHandle); //printf("FLUSHING!\n"); @@ -235,28 +257,28 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) } std::string fromPipeName = FROM_PIPENAME_TS; std::string toPipeName = TO_PIPENAME_TS; - + if (readHandle != INVALID_HANDLE_VALUE) { CloseHandle(readHandle); readConnected = false; } if (!readConnected) { while (tries < 1) { - readHandle = CreateFileA( - fromPipeName.c_str(), // pipe name - GENERIC_READ | GENERIC_WRITE, - 0, // no sharing + readHandle = CreateFileA( + fromPipeName.c_str(), // pipe name + GENERIC_READ | GENERIC_WRITE, + 0, // no sharing NULL, // default security attributes - OPEN_EXISTING, // opens existing pipe - 0, // default attributes - NULL); // no template file + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file if (readHandle != INVALID_HANDLE_VALUE) { DWORD dwModeRead = PIPE_NOWAIT | PIPE_READMODE_MESSAGE; - ret = SetNamedPipeHandleState( - readHandle, // pipe handle - &dwModeRead, // new pipe mode - NULL, // don't set maximum bytes - NULL); // don't set maximum time + ret = SetNamedPipeHandleState( + readHandle, // pipe handle + &dwModeRead, // new pipe mode + NULL, // don't set maximum bytes + NULL); // don't set maximum time if ( ! ret) { //printf("READ PIPE MODE ERROR: %d\n", GetLastError()); sprintf(output, "Read SetNamedPipeHandleState WinErrCode: %d", GetLastError()); @@ -288,22 +310,22 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) if (!writeConnected) { tries = 0; while (tries < 1) { - writeHandle = CreateFileA( - toPipeName.c_str(), // pipe name + writeHandle = CreateFileA( + toPipeName.c_str(), // pipe name GENERIC_WRITE | GENERIC_READ, - 0, // no sharing + 0, // no sharing NULL, // default security attributes - OPEN_EXISTING, // opens existing pipe - 0, // default attributes - NULL); // no template file + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file if (writeHandle != INVALID_HANDLE_VALUE) { DWORD dwModeWrite = PIPE_READMODE_MESSAGE; - ret = SetNamedPipeHandleState( - writeHandle, // pipe handle - &dwModeWrite, // new pipe mode - NULL, // don't set maximum bytes - NULL); // don't set maximum time + ret = SetNamedPipeHandleState( + writeHandle, // pipe handle + &dwModeWrite, // new pipe mode + NULL, // don't set maximum bytes + NULL); // don't set maximum time if ( ! ret) { //printf("WRITE PIPE MODE ERROR: %d\n", GetLastError()); sprintf(output, "Write SetNamedPipeHandleState WinErrCode: %d", GetLastError()); @@ -345,6 +367,289 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) readHandle = INVALID_HANDLE_VALUE; return; } + case COMMAND_IS_WINE: { + strncpy(output,"0",outputSize); + return; + } + default: + return; + } +} + +void __stdcall runCommandWine(char* output, int outputSize, int command, std::string params) { + switch(command) { + case PIPE_COMMAND_WRITE: { + if (writeConnected) { + size_t size = params.length() + 4; + char* messageBuffer = (char*)calloc(size, sizeof(char)); + messageBuffer[0] = (char) (params.length() >> 24); + messageBuffer[1] = (char) (params.length() >> 16); + messageBuffer[2] = (char) (params.length() >> 8); + messageBuffer[3] = (char) (params.length()); + + strncpy(messageBuffer + 4, params.c_str(), params.length()); + + int written = 0; + while (written < size) { + int len = send( + wineSocket, + messageBuffer + written, + size - written, + 0 + ); + if (len == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + sprintf(output, "send failed with error: %d\n", WSAGetLastError()); + CloseWineSocket(); + return; + } + } else { + written += len; + } + + if (written < size) { + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(wineSocket, &socketSet); + + timeval timeout = { 0, 15000L }; + int iResult = select(1, NULL, &socketSet, NULL, NULL); + if (iResult == SOCKET_ERROR) { + sprintf(output, "select failed with error: %d\n", WSAGetLastError()); + CloseWineSocket(); + if (messageBuffer) { + free(messageBuffer); + } + return; + } + } + } + + if (messageBuffer) { + free(messageBuffer); + } + strncpy(output,"1",outputSize); + } else { + if (wineSocket != INVALID_SOCKET) { + CloseWineSocket(); + } + strncpy(output,"-1",outputSize); + } + + return; + } + + case PIPE_COMMAND_READ: { + if (readConnected) { + DWORD cbRead; + char lengthBuffer[4]; + constexpr size_t read_length = 4097U; + char value[read_length]; // Allocate on stack to delegate memory management to compiler, + // allows up to 4096 char string (+1 to ensure NUL terminated), like initial version + + cbRead = recv(wineSocket, lengthBuffer, 4, 0); + if (cbRead == SOCKET_ERROR) { + if (WSAGetLastError() == WSAEWOULDBLOCK) { + strncpy(output, "_JERR_NULL", outputSize); + } else { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + } + } else if (cbRead != 0) { + // If we receive some bytes, that means we're in the middle of a "message" + // + // Since there's no logic to stitch together a message from multiple calls into + // this command, there are two cases from here on out: + // Either we read the complete message, + // or the socket gets closed, possibly after a timeout + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(wineSocket, &socketSet); + // 15 ms timeout + timeval timeout = { 0, 15000L }; + + while (cbRead < 4) { + int iResult = select(1, &socketSet, NULL, NULL, &timeout); + if (iResult == 0 || iResult == SOCKET_ERROR) { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + return; + } + + int len = recv(wineSocket, lengthBuffer + cbRead, 4 - cbRead, 0); + if (len == 0) { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + return; + } + + cbRead += len; + }; + + DWORD messageLength = ((unsigned char) lengthBuffer[0] << 24) + + ((unsigned char) lengthBuffer[1] << 16) + + ((unsigned char) lengthBuffer[2] << 8) + + (unsigned char) lengthBuffer[3]; + if (messageLength > outputSize) { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + return; + } + + cbRead = 0; + while (cbRead < messageLength) { + int len = recv(wineSocket, value + cbRead, messageLength - cbRead, 0); + + if (len == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + return; + } + } else if (len == 0) { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + return; + } else { + cbRead += len; + } + + if (cbRead < messageLength) { + int iResult = select(1, &socketSet, NULL, NULL, &timeout); + if (iResult == 0 || iResult == SOCKET_ERROR) { + strncpy(output, "_JERR_FALSE", outputSize); + CloseWineSocket(); + return; + } + } + } + + if (cbRead >= (size_t)outputSize) { + // Prevent buffer overflow + cbRead = outputSize - 1U; + } + + // Ensure NUL terminated string (required by strncpy) + value[cbRead] = '\0'; + + strncpy(output, value, cbRead + 1); + } else { + CloseWineSocket(); + strncpy(output, "_JERR_FALSE", outputSize); + } + } else { + CloseWineSocket(); + strncpy(output, "_JERR_NOCONNECT", outputSize); + } + return; + } + + case PIPE_COMMAND_OPEN: { + if (readConnected) { + CloseWineSocket(); + } + wineSocket = INVALID_SOCKET; + + WSADATA wsaData; + struct addrinfo *result = NULL, *ptr = NULL, hints; + char recvbuf[4096]; + int iResult; + + iResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if (iResult != 0) { + sprintf(output, "WSA startup failed with error: %d", iResult); + return; + } + + ZeroMemory( &hints, sizeof(hints) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + const char * port; + if (params.length() > 0) { + port = params.c_str() + 1; + } else { + port = WINE_SOCKET_PORT; + } + + // Resolve the server address and port + iResult = getaddrinfo("localhost", port, &hints, &result); + if ( iResult != 0 ) { + sprintf(output, "getaddrinfo failed with error: %d\n", iResult); + WSACleanup(); + return; + } + + // Attempt to connect to an address until one succeeds + for(ptr = result ; ptr != NULL ; ptr = ptr -> ai_next) { + // Create a SOCKET for connecting to server + wineSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + if (wineSocket == INVALID_SOCKET) { + sprintf(output, "socket failed with error: %ld\n", WSAGetLastError()); + WSACleanup(); + return; + } + + u_long argp = 1; + iResult = ioctlsocket(wineSocket, FIONBIO, &argp); + if (iResult != NO_ERROR) { + sprintf(output, "ioctlsocket failed with error: %ld\n", iResult); + return; + } + + // Connect to server. + iResult = connect( wineSocket, ptr->ai_addr, (int)ptr->ai_addrlen); + if (iResult == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { + CloseWineSocket(); + continue; + } + + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(wineSocket, &socketSet); + + timeval timeout = { 0, 15000L }; + + iResult = select(1, NULL, &socketSet, NULL, &timeout); + if (iResult == SOCKET_ERROR) { + sprintf(output, "select failed with error: %d\n", WSAGetLastError()); + CloseWineSocket(); + WSACleanup(); + break; + } else if (iResult == 0) { + wineSocket = INVALID_SOCKET; + continue; + } + break; + } + + freeaddrinfo(result); + + + if (wineSocket == INVALID_SOCKET) { + WSACleanup(); + strncpy(output,"0",outputSize); + } else { + readConnected = true; + writeConnected = true; + strncpy(output,"1",outputSize); + } + + return; + } + + case PIPE_COMMAND_CLOSE: + case PIPE_COMMAND_RESET: { + CloseWineSocket(); + return; + } + + case COMMAND_IS_WINE: { + strncpy(output,"1",outputSize); + return; + } + default: return; } @@ -369,12 +674,23 @@ void ClosePipe() { readConnected = FALSE; } +void CloseWineSocket() { + if (wineSocket != INVALID_SOCKET) { + closesocket(wineSocket); + wineSocket = INVALID_SOCKET; + } + writeConnected = FALSE; + readConnected = FALSE; +} + void Init(void) { //g_Log = (Log *)new Log("ACRE2Arma.log"); //LOG("* Logging engine initialized."); writeConnected = FALSE; readConnected = FALSE; + + isWine = detectWine(); } void Cleanup(void) { diff --git a/extensions/src/ACRE2Arma/common/pbo/fileloader.hpp b/extensions/src/ACRE2Arma/common/pbo/fileloader.hpp index 1e013f0b5..190dd7aac 100644 --- a/extensions/src/ACRE2Arma/common/pbo/fileloader.hpp +++ b/extensions/src/ACRE2Arma/common/pbo/fileloader.hpp @@ -106,4 +106,4 @@ namespace acre { }; }; } -} \ No newline at end of file +} diff --git a/extensions/src/ACRE2Core/AcreDsp.cpp b/extensions/src/ACRE2Core/AcreDsp.cpp index 5c0b11c3c..0f56149b5 100644 --- a/extensions/src/ACRE2Core/AcreDsp.cpp +++ b/extensions/src/ACRE2Core/AcreDsp.cpp @@ -9,7 +9,7 @@ namespace Dsp { const static float c2 = (float)((int)(c1 / 3)) + 1; const static float c3 = 1.f / c1; - float random = ((float)rand() / (float)(RAND_MAX + 1)); + float random = ((float)rand() / ((float)RAND_MAX + 1)); float noise = (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3; return noise; @@ -26,4 +26,4 @@ namespace Dsp { } } } -} \ No newline at end of file +} diff --git a/extensions/src/ACRE2Core/AcreDsp.h b/extensions/src/ACRE2Core/AcreDsp.h index bccd879f0..73f46f0fb 100644 --- a/extensions/src/ACRE2Core/AcreDsp.h +++ b/extensions/src/ACRE2Core/AcreDsp.h @@ -1,5 +1,5 @@ #pragma once -#include "DspFilters\Dsp.h" +#include "DspFilters/Dsp.h" #define PINK_NOISE_NUM_STAGES 3 #define PI_2 1.57079632679489661923f diff --git a/extensions/src/ACRE2Core/AcreSettings.cpp b/extensions/src/ACRE2Core/AcreSettings.cpp index 090770e7f..d515cc517 100644 --- a/extensions/src/ACRE2Core/AcreSettings.cpp +++ b/extensions/src/ACRE2Core/AcreSettings.cpp @@ -16,6 +16,9 @@ acre::Result CAcreSettings::save(std::string filename) { iniFile << "premixGlobalVolume = " << this->m_PremixGlobalVolume << ";\n"; iniFile << "disableUnmuteClients = " << (this->m_DisableUnmuteClients ? "true" : "false") << ";\n"; iniFile << "disableChannelSwitch = " << (this->m_DisableChannelSwitch ? "true" : "false") << ";\n"; +#ifdef __linux__ + iniFile << "wineSocketPort = " << (this->m_WineSocketPort) << ";\n"; +#endif //LOG("Config Save: %f,%f", m_GlobalVolume, m_PremixGlobalVolume); iniFile.flush(); @@ -41,6 +44,9 @@ acre::Result CAcreSettings::load(std::string filename) { this->m_PremixGlobalVolume = (float)config.GetReal("acre2", "premixGlobalVolume", 1.0f); this->m_DisableUnmuteClients = config.GetBoolean("acre2", "disableUnmuteClients", false); this->m_DisableChannelSwitch = config.GetBoolean("acre2", "disableChannelSwitch", false); +#ifdef __linux__ + this->m_WineSocketPort = config.GetInteger("acre2", "wineSocketPort", 19141); +#endif //LOG("Config Load: %f,%f", m_GlobalVolume, m_PremixGlobalVolume); this->m_Path = filename; @@ -58,6 +64,9 @@ acre::Result CAcreSettings::load() { } CAcreSettings::CAcreSettings() : +#ifdef __linux__ + m_WineSocketPort(19141), +#endif m_GlobalVolume(1.0f), m_PremixGlobalVolume(1.0f), m_DisablePosition(false), @@ -71,7 +80,6 @@ CAcreSettings::CAcreSettings() : { // Set defaults! //LOG("Config Singleton Initialized"); - this->load(); } diff --git a/extensions/src/ACRE2Core/AcreSettings.h b/extensions/src/ACRE2Core/AcreSettings.h index 6cc84c8b7..a10a81a70 100644 --- a/extensions/src/ACRE2Core/AcreSettings.h +++ b/extensions/src/ACRE2Core/AcreSettings.h @@ -31,5 +31,9 @@ class CAcreSettings : DECLARE_MEMBER(bool, DisableChannelSwitch); DECLARE_MEMBER(bool, EnableAudioTest); +#ifdef __linux__ + DECLARE_MEMBER(uint16_t, WineSocketPort); +#endif + DECLARE_MEMBER(std::string, Path); }; diff --git a/extensions/src/ACRE2Core/CMakeLists.txt b/extensions/src/ACRE2Core/CMakeLists.txt index 3f7489a8e..5732364bb 100644 --- a/extensions/src/ACRE2Core/CMakeLists.txt +++ b/extensions/src/ACRE2Core/CMakeLists.txt @@ -8,5 +8,9 @@ file(GLOB SOURCES *.h *.hpp *.c *.cpp) file(GLOB DSP_SOURCES DspFilters/*.h DspFilters/*.hpp DspFilters/*.c DspFilters/*.cpp) add_library( ${ACRE_NAME} STATIC ${SOURCES} ${GLOBAL_SOURCES} ${DSP_SOURCES}) +if(NOT WIN32) + set_property(TARGET ${ACRE_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) + target_link_libraries(${ACRE_NAME} tbb) +endif() target_link_libraries( ${ACRE_NAME} ACRE2Shared) set_target_properties(${ACRE_NAME} PROPERTIES FOLDER ACRE2) diff --git a/extensions/src/ACRE2Core/Engine.cpp b/extensions/src/ACRE2Core/Engine.cpp index d8f329c75..019148960 100644 --- a/extensions/src/ACRE2Core/Engine.cpp +++ b/extensions/src/ACRE2Core/Engine.cpp @@ -29,21 +29,34 @@ #include "setSelectableVoiceCurve.h" #include "setSetting.h" #include "setChannelDetails.h" + +#ifdef WIN32 #include +#else +#include +#endif acre::Result CEngine::initialize(IClient *client, IServer *externalServer, std::string fromPipeName, std::string toPipeName) { if (!g_Log) { std::string acrePluginLog{"acre2_plugin.log"}; +#ifdef WIN32 std::array appDataPath{""}; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath.data()))) { acrePluginLog = std::string(appDataPath.data()) + "\\Arma 3\\" + acrePluginLog; } +#else + if (getenv("XDG_DATA_HOME")) { + acrePluginLog = std::string(getenv("XDG_DATA_HOME")) + "/" + acrePluginLog; + } else { + acrePluginLog = std::string(getenv("HOME")) + "/.local/share/" + acrePluginLog; + } +#endif g_Log = (Log *) new Log(acrePluginLog.c_str()); LOG("* Logging engine initialized."); } - LOG("Configuration Path: {%s\\acre2.ini}", client->getConfigFilePath().c_str()); - CAcreSettings::getInstance()->load(client->getConfigFilePath() + "\\acre2.ini"); + LOG("Configuration Path: {%s%sacre2.ini}", client->getConfigFilePath().c_str(), PATH_SEPARATOR); + CAcreSettings::getInstance()->load(client->getConfigFilePath() + PATH_SEPARATOR + "acre2.ini"); this->setClient(client); this->setExternalServer(externalServer); diff --git a/extensions/src/ACRE2Core/FilterGain.cpp b/extensions/src/ACRE2Core/FilterGain.cpp index 6b69add2e..a3cd610f5 100644 --- a/extensions/src/ACRE2Core/FilterGain.cpp +++ b/extensions/src/ACRE2Core/FilterGain.cpp @@ -28,4 +28,4 @@ CFilterGain::CFilterGain(void) CFilterGain::~CFilterGain(void) { -} \ No newline at end of file +} diff --git a/extensions/src/ACRE2Core/FilterPosition.cpp b/extensions/src/ACRE2Core/FilterPosition.cpp index 49db8551b..60562d09d 100644 --- a/extensions/src/ACRE2Core/FilterPosition.cpp +++ b/extensions/src/ACRE2Core/FilterPosition.cpp @@ -7,7 +7,9 @@ #include "Log.h" #include +#ifdef WIN32 #pragma comment(lib, "x3daudio.lib") +#endif #define MAX_FALLOFF_DISTANCE 75 #define MAX_FALLOFF_RANGE 150 @@ -28,7 +30,7 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann X3DAUDIO_VECTOR speaker_position; X3DAUDIO_VECTOR vector_listenerDirection; X3DAUDIO_VECTOR vector_speakerDirection; - + //LOCK(player); //LOCK(CEngine::getInstance()->getSelf()); float killCoef; @@ -49,7 +51,7 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann this->p_IsInitialized = TRUE; } - + if (CAcreSettings::getInstance()->getDisablePosition()) return acre::Result::ok; @@ -69,7 +71,7 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann Emitter.OrientFront = vector_speakerDirection; Emitter.OrientTop = this->getUpVector(vector_speakerDirection); - Emitter.Velocity = X3DAUDIO_VECTOR( 0, 0, 0 ); + Emitter.Velocity = X3DAUDIO_VECTOR(); Emitter.ChannelCount = 1; if (params->getParam("isWorld") == POSITIONAL_EFFECT_ISWORLD) { @@ -115,7 +117,7 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann Emitter.CurveDistanceScaler = 1.0f; Emitter.pVolumeCurve = (X3DAUDIO_DISTANCE_CURVE *)&distanceCurve; } - + Emitter.DopplerScaler = 1.0f; Emitter.ChannelRadius = 1.0f; @@ -127,8 +129,8 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann Emitter.pCone = &emitterCone; //Listener.pCone = &emitterCone; - - + + Emitter.InnerRadius = 2.0f; Emitter.InnerRadiusAngle = X3DAUDIO_PI/4.0f; @@ -140,10 +142,10 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann Listener.OrientFront = vector_listenerDirection; Listener.OrientTop = listener_topVec; Listener.Position = listener_position; - + //UNLOCK(CEngine::getInstance()->getSelf()); //UNLOCK(player); - + X3DAudioCalculate(this->p_X3DInstance, &Listener, &Emitter, X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_EMITTER_ANGLE, @@ -156,7 +158,7 @@ acre::Result CFilterPosition::process(short* samples, int sampleCount, int chann sprintf(mAppend, "%f, ", Matrix[i]); matrixVals.append(std::string(mAppend)); } - + TRACE("MATRIX: %s", matrixVals.c_str()); */ TRACE("matrix: c:[%d], %f, %f, %f", channels, Matrix[0], Matrix[1], (Matrix[0] + Matrix[1]));// +Matrix[2] + Matrix[3] + Matrix[4] + Matrix[5])); @@ -211,12 +213,16 @@ X3DAUDIO_VECTOR CFilterPosition::getUpVector(X3DAUDIO_VECTOR inVector) { CFilterPosition::CFilterPosition(void) { +#ifdef WIN32 CoInitializeEx(NULL, NULL); +#endif this->p_IsInitialized = FALSE; } CFilterPosition::~CFilterPosition(void) { +#ifdef WIN32 CoUninitialize(); +#endif } /* @@ -265,75 +271,75 @@ unsigned int CFilterPosition::getChannelMask(const unsigned int channelMask) { switch(channelMask) { case TS_SPEAKER_FRONT_LEFT: LOG("Found: %08x", SPEAKER_FRONT_LEFT); - returnValue = SPEAKER_FRONT_LEFT; + returnValue = SPEAKER_FRONT_LEFT; break; case TS_SPEAKER_FRONT_RIGHT: LOG("Found: %08x", SPEAKER_FRONT_RIGHT); - returnValue = SPEAKER_FRONT_RIGHT; + returnValue = SPEAKER_FRONT_RIGHT; break; - case TS_SPEAKER_FRONT_CENTER: + case TS_SPEAKER_FRONT_CENTER: LOG("Found: %08x", SPEAKER_FRONT_CENTER); - returnValue = SPEAKER_FRONT_CENTER; + returnValue = SPEAKER_FRONT_CENTER; break; - case TS_SPEAKER_LOW_FREQUENCY: + case TS_SPEAKER_LOW_FREQUENCY: LOG("Found: %08x", SPEAKER_LOW_FREQUENCY); - returnValue = SPEAKER_LOW_FREQUENCY; + returnValue = SPEAKER_LOW_FREQUENCY; break; - case TS_SPEAKER_BACK_LEFT: + case TS_SPEAKER_BACK_LEFT: LOG("Found: %08x", SPEAKER_BACK_LEFT); - returnValue = SPEAKER_BACK_LEFT; + returnValue = SPEAKER_BACK_LEFT; break; - case TS_SPEAKER_BACK_RIGHT: + case TS_SPEAKER_BACK_RIGHT: LOG("Found: %08x", SPEAKER_BACK_RIGHT); - returnValue = SPEAKER_BACK_RIGHT; + returnValue = SPEAKER_BACK_RIGHT; break; - case TS_SPEAKER_FRONT_LEFT_OF_CENTER: + case TS_SPEAKER_FRONT_LEFT_OF_CENTER: LOG("Found: %08x", SPEAKER_FRONT_LEFT_OF_CENTER); - returnValue = SPEAKER_FRONT_LEFT_OF_CENTER; + returnValue = SPEAKER_FRONT_LEFT_OF_CENTER; break; - case TS_SPEAKER_FRONT_RIGHT_OF_CENTER: + case TS_SPEAKER_FRONT_RIGHT_OF_CENTER: LOG("Found: %08x", SPEAKER_FRONT_RIGHT_OF_CENTER); - returnValue = SPEAKER_FRONT_RIGHT_OF_CENTER; + returnValue = SPEAKER_FRONT_RIGHT_OF_CENTER; break; - case TS_SPEAKER_BACK_CENTER: + case TS_SPEAKER_BACK_CENTER: LOG("Found: %08x", SPEAKER_BACK_CENTER); - returnValue = SPEAKER_BACK_CENTER; + returnValue = SPEAKER_BACK_CENTER; break; - case TS_SPEAKER_SIDE_LEFT: + case TS_SPEAKER_SIDE_LEFT: LOG("Found: %08x", SPEAKER_SIDE_LEFT); - returnValue = SPEAKER_SIDE_LEFT; + returnValue = SPEAKER_SIDE_LEFT; break; - case TS_SPEAKER_SIDE_RIGHT: + case TS_SPEAKER_SIDE_RIGHT: LOG("Found: %08x", SPEAKER_SIDE_RIGHT); - returnValue = SPEAKER_SIDE_RIGHT; + returnValue = SPEAKER_SIDE_RIGHT; break; - case TS_SPEAKER_TOP_CENTER: + case TS_SPEAKER_TOP_CENTER: LOG("Found: %08x", SPEAKER_TOP_CENTER); - returnValue = SPEAKER_TOP_CENTER; + returnValue = SPEAKER_TOP_CENTER; break; - case TS_SPEAKER_TOP_FRONT_LEFT: + case TS_SPEAKER_TOP_FRONT_LEFT: LOG("Found: %08x", SPEAKER_TOP_FRONT_LEFT); - returnValue = SPEAKER_TOP_FRONT_LEFT; + returnValue = SPEAKER_TOP_FRONT_LEFT; break; - case TS_SPEAKER_TOP_FRONT_CENTER: + case TS_SPEAKER_TOP_FRONT_CENTER: LOG("Found: %08x", SPEAKER_TOP_FRONT_CENTER); - returnValue = SPEAKER_TOP_FRONT_CENTER; + returnValue = SPEAKER_TOP_FRONT_CENTER; break; - case TS_SPEAKER_TOP_FRONT_RIGHT: + case TS_SPEAKER_TOP_FRONT_RIGHT: LOG("Found: %08x", SPEAKER_TOP_FRONT_RIGHT); - returnValue = SPEAKER_TOP_FRONT_RIGHT; + returnValue = SPEAKER_TOP_FRONT_RIGHT; break; - case TS_SPEAKER_TOP_BACK_LEFT: + case TS_SPEAKER_TOP_BACK_LEFT: LOG("Found: %08x", SPEAKER_TOP_BACK_LEFT); - returnValue = SPEAKER_TOP_BACK_LEFT; + returnValue = SPEAKER_TOP_BACK_LEFT; break; - case TS_SPEAKER_TOP_BACK_CENTER: + case TS_SPEAKER_TOP_BACK_CENTER: LOG("Found: %08x", SPEAKER_TOP_BACK_CENTER); - returnValue = SPEAKER_TOP_BACK_CENTER; + returnValue = SPEAKER_TOP_BACK_CENTER; break; - case TS_SPEAKER_TOP_BACK_RIGHT: + case TS_SPEAKER_TOP_BACK_RIGHT: LOG("Found: %08x", SPEAKER_TOP_BACK_RIGHT); - returnValue = SPEAKER_TOP_BACK_RIGHT; + returnValue = SPEAKER_TOP_BACK_RIGHT; break; } LOG("Mask Return: %08x", returnValue); diff --git a/extensions/src/ACRE2Core/FilterPosition.h b/extensions/src/ACRE2Core/FilterPosition.h index 2ee1cb1e9..cf4cd2086 100644 --- a/extensions/src/ACRE2Core/FilterPosition.h +++ b/extensions/src/ACRE2Core/FilterPosition.h @@ -5,7 +5,25 @@ #include "Macros.h" #include "SoundMixdownEffect.h" +#ifdef WIN32 #include +#else +#include +#define X3DAUDIO_CALCULATE_EMITTER_ANGLE F3DAUDIO_CALCULATE_EMITTER_ANGLE +#define X3DAUDIO_CALCULATE_MATRIX F3DAUDIO_CALCULATE_MATRIX +#define X3DAUDIO_CONE F3DAUDIO_CONE +#define X3DAUDIO_DISTANCE_CURVE F3DAUDIO_DISTANCE_CURVE +#define X3DAUDIO_DISTANCE_CURVE_POINT F3DAUDIO_DISTANCE_CURVE_POINT +#define X3DAUDIO_DSP_SETTINGS F3DAUDIO_DSP_SETTINGS +#define X3DAUDIO_EMITTER F3DAUDIO_EMITTER +#define X3DAUDIO_HANDLE F3DAUDIO_HANDLE +#define X3DAUDIO_LISTENER F3DAUDIO_LISTENER +#define X3DAUDIO_PI F3DAUDIO_PI +#define X3DAUDIO_VECTOR F3DAUDIO_VECTOR +#define X3DAudioCalculate F3DAudioCalculate +#define X3DAudioInitialize F3DAudioInitialize +#define X3DAUDIO_SPEED_OF_SOUND 343.5 +#endif class CFilterPosition { diff --git a/extensions/src/ACRE2Core/FilterVolume.cpp b/extensions/src/ACRE2Core/FilterVolume.cpp index 2f901367b..a38ab8980 100644 --- a/extensions/src/ACRE2Core/FilterVolume.cpp +++ b/extensions/src/ACRE2Core/FilterVolume.cpp @@ -40,4 +40,4 @@ CFilterVolume::CFilterVolume(void) CFilterVolume::~CFilterVolume(void) { -} \ No newline at end of file +} diff --git a/extensions/src/ACRE2Core/KeyHandlerEngine.cpp b/extensions/src/ACRE2Core/KeyHandlerEngine.cpp index 53c8af1b9..006d17e30 100644 --- a/extensions/src/ACRE2Core/KeyHandlerEngine.cpp +++ b/extensions/src/ACRE2Core/KeyHandlerEngine.cpp @@ -122,4 +122,4 @@ acre::Result CKeyHandlerEngine::clearKeybinds( void ) { this->unlock(); return acre::Result::ok; } -*/ \ No newline at end of file +*/ diff --git a/extensions/src/ACRE2Core/KeyHandlerEngine.h b/extensions/src/ACRE2Core/KeyHandlerEngine.h index f13c9b3a1..de6af59a2 100644 --- a/extensions/src/ACRE2Core/KeyHandlerEngine.h +++ b/extensions/src/ACRE2Core/KeyHandlerEngine.h @@ -46,4 +46,4 @@ class CKeyHandlerEngine : public CLockable { concurrency::concurrent_unordered_map m_keyMap; std::thread m_keyboardReadThread; }; -*/ \ No newline at end of file +*/ diff --git a/extensions/src/ACRE2Core/NamedPipeServer.cpp b/extensions/src/ACRE2Core/NamedPipeServer.cpp index 2da726fc6..bf50750bf 100644 --- a/extensions/src/ACRE2Core/NamedPipeServer.cpp +++ b/extensions/src/ACRE2Core/NamedPipeServer.cpp @@ -1,29 +1,62 @@ #include "NamedPipeServer.h" #include "TextMessage.h" -#include #include "Log.h" #include "Engine.h" - - +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#ifdef WIN32 +typedef clock_t walltime_t; +#else +typedef timespec walltime_t; +#endif CNamedPipeServer::CNamedPipeServer(std::string fromPipeName, std::string toPipeName) { this->setConnectedWrite(false); this->setConnectedRead(false); - this->setPipeHandleWrite(INVALID_HANDLE_VALUE); - this->setPipeHandleRead(INVALID_HANDLE_VALUE); this->setShuttingDown(false); +#ifdef WIN32 + this->setPipeHandleWrite(INVALID_HANDLE_VALUE); + this->setPipeHandleRead(INVALID_HANDLE_VALUE); this->setFromPipeName(fromPipeName); this->setToPipeName(toPipeName); - +#endif } CNamedPipeServer::~CNamedPipeServer( void ) { this->shutdown(); } +walltime_t getMonotime() { +#ifdef WIN32 + return clock(); +#else + walltime_t ret; + clock_gettime(CLOCK_MONOTONIC_RAW, &ret); + return ret; +#endif +} + +long diffMonotime(walltime_t current, walltime_t previous) { +#ifdef WIN32 + return ((current - previous) * 1000) / CLOCKS_PER_SEC; +#else + time_t s = (current.tv_sec - previous.tv_sec); + time_t ms = (time_t) ((current.tv_nsec - previous.tv_nsec) / 1000000); + return s * 1000 + ms; +#endif +} + acre::Result CNamedPipeServer::initialize() { +#ifdef WIN32 HANDLE writeHandle, readHandle; SECURITY_DESCRIPTOR sd; @@ -31,17 +64,19 @@ acre::Result CNamedPipeServer::initialize() { if (!SetSecurityDescriptorDacl(&sd, TRUE, nullptr, FALSE)) { LOG("SetSecurityDescriptorDacl Error : %u", GetLastError()); } if (!SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED)) { LOG("SetSecurityDescriptorControl Error : %u", GetLastError()); } SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), &sd, true }; +#endif // open our pipe handle, then kick up a thread to monitor it and add shit to our queue // this end LISTENS and CREATES the pipe LOG("Opening game pipe..."); bool tryAgain = true; +#ifdef WIN32 while (tryAgain) { writeHandle = CreateNamedPipeA( this->getFromPipeName().c_str(), // name of the pipe - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | // message-type pipe + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | // message-type pipe PIPE_READMODE_MESSAGE, // send data as message PIPE_UNLIMITED_INSTANCES, 4096, // no outbound buffer @@ -51,7 +86,7 @@ acre::Result CNamedPipeServer::initialize() { ); if (writeHandle == INVALID_HANDLE_VALUE) { char errstr[1024]; - + _snprintf_s(errstr, sizeof(errstr), "Conflicting game write pipe detected, could not create pipe!\nERROR CODE: %d", GetLastError()); int ret = MessageBoxA(NULL, errstr, "ACRE Error", MB_RETRYCANCEL | MB_ICONEXCLAMATION); if (ret != IDRETRY) { @@ -70,11 +105,11 @@ acre::Result CNamedPipeServer::initialize() { while (tryAgain) { readHandle = CreateNamedPipeA( this->getToPipeName().c_str(), // name of the pipe - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | // message-type pipe + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | // message-type pipe PIPE_NOWAIT | // Depricated but fuck it, it is simpler. PIPE_READMODE_MESSAGE, // send data as message - PIPE_UNLIMITED_INSTANCES, + PIPE_UNLIMITED_INSTANCES, 4096, // no outbound buffer 4096, // no inbound buffer 0, // use default wait time @@ -82,7 +117,7 @@ acre::Result CNamedPipeServer::initialize() { ); if (readHandle == INVALID_HANDLE_VALUE) { char errstr[1024]; - + _snprintf_s(errstr, sizeof(errstr), "Conflicting game read pipe detected, could not create pipe!\nERROR CODE: %d", GetLastError()); int ret = MessageBoxA(NULL, errstr, "ACRE Error", MB_RETRYCANCEL | MB_ICONEXCLAMATION); if (ret != IDRETRY) { @@ -93,7 +128,6 @@ acre::Result CNamedPipeServer::initialize() { tryAgain = false; } } - this->setPipeHandleRead(readHandle); this->setPipeHandleWrite(writeHandle); @@ -101,17 +135,49 @@ acre::Result CNamedPipeServer::initialize() { this->m_sendThread = std::thread(&CNamedPipeServer::sendLoop, this); this->m_readThread = std::thread(&CNamedPipeServer::readLoop, this); +#else + this->m_sockFD = socket(AF_INET, SOCK_STREAM, 0); + + const struct sockaddr_in listenAddr = { + .sin_family = AF_INET, + .sin_port = htons(acreListenPort), + .sin_addr = { + htonl(INADDR_LOOPBACK) + } + }; + + int param = 1; + setsockopt(this->m_sockFD, SOL_SOCKET, SO_REUSEADDR, ¶m, sizeof(int)); + int ret = bind(this->m_sockFD, (struct sockaddr *) &listenAddr, sizeof(listenAddr)); + + if(ret) { + LOG("Could not bind to port %d, error %d", acreListenPort, errno); + return acre::Result::error; + } + + LOG("Bound on port %d", acreListenPort); + + ret = listen(this->m_sockFD, 1); + if (ret) { + LOG("Could not listen on port %d, error %d", acreListenPort, errno); + return acre::Result::error; + } + + this->m_readThread = std::thread(&CNamedPipeServer::readLoop, this); + this->m_sendThread = std::thread(&CNamedPipeServer::sendLoop, this); +#endif + return acre::Result::ok; } acre::Result CNamedPipeServer::shutdown(void) { - HANDLE hPipe; - this->setShuttingDown(true); this->setConnectedWrite(false); this->setConnectedRead(false); - + +#ifdef WIN32 + HANDLE hPipe; //Wake the synchronous named pipe //Called from the same process but from a different thread hPipe = CreateFile(this->getToPipeName().c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); @@ -119,18 +185,24 @@ acre::Result CNamedPipeServer::shutdown(void) { DisconnectNamedPipe(hPipe); CloseHandle(hPipe); } +#else + ::shutdown(this->m_sockFD, SHUT_RDWR); + close(this->m_sockFD); +#endif // Read should initiate the full shutdown, so we wait for him to die first and we only wake him. if (this->m_readThread.joinable()) { this->m_readThread.join(); } +#ifdef WIN32 // Now we wake the write pipe just in case. hPipe = CreateFile(this->getFromPipeName().c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if (hPipe != INVALID_HANDLE_VALUE) { DisconnectNamedPipe(hPipe); CloseHandle(hPipe); } +#endif if (this->m_sendThread.joinable()) { this->m_sendThread.join(); @@ -142,11 +214,11 @@ acre::Result CNamedPipeServer::shutdown(void) { } acre::Result CNamedPipeServer::sendLoop() { +#ifdef WIN32 while (!this->getShuttingDown()) { - do { ConnectNamedPipe(this->m_PipeHandleWrite, NULL); - if (GetLastError() == ERROR_PIPE_CONNECTED) { + if (GetLastError() == ERROR_PIPE_CONNECTED) { LOG("Client write connected"); CEngine::getInstance()->getSoundEngine()->onClientGameConnected(); this->setConnectedWrite(true); @@ -157,13 +229,13 @@ acre::Result CNamedPipeServer::sendLoop() { } } while (!this->getConnectedWrite() && !this->getShuttingDown()); - clock_t lastTick = clock() / CLOCKS_PER_SEC; + walltime_t lastTick = getMonotime(); while (this->getConnectedWrite()) { if (this->getShuttingDown()) break; - clock_t tick = clock() / CLOCKS_PER_SEC; - if (tick - lastTick > (PIPE_TIMEOUT / 1000)) { + walltime_t tick = getMonotime(); + if (diffMonotime(tick, lastTick) > PIPE_TIMEOUT) { LOG("No send message for %d seconds, disconnecting", (PIPE_TIMEOUT / 1000)); this->setConnectedWrite(false); break; @@ -172,19 +244,19 @@ acre::Result CNamedPipeServer::sendLoop() { IMessage *msg = nullptr; if (this->m_sendQueue.try_pop(msg)) { if (msg != nullptr) { - lastTick = clock() / CLOCKS_PER_SEC; - const DWORD size = (DWORD)strlen((char *)msg->getData()) + 1; + lastTick = getMonotime(); + const DWORD size = (uint32_t)strlen((char *)msg->getData()) + 1; if (size > 3) { DWORD cbWritten = 0; // send it and free it //LOCK(this); this->lock(); - const bool ret = WriteFile( - this->m_PipeHandleWrite, // pipe handle - msg->getData(), // message - size, // message length - &cbWritten, // bytes written - NULL); // not overlapped + const bool ret = WriteFile( + this->m_PipeHandleWrite, // pipe handle + msg->getData(), // message + size, // message length + &cbWritten, // bytes written + NULL); // not overlapped this->unlock(); if (!ret) { @@ -207,9 +279,69 @@ acre::Result CNamedPipeServer::sendLoop() { TRACE("Sending thread terminating"); return acre::Result::ok; +#else + ssize_t bufferHead, len; + while (!this->getShuttingDown()) { + CEngine::getInstance()->getSoundEngine()->onClientGameConnected(); + while(!this->getConnectedWrite() && !this->getShuttingDown()) { + Sleep(1); + } + + walltime_t lastTick = getMonotime(); + while (this->getConnectedWrite()) { + if (this->getShuttingDown()) { + break; + } + + walltime_t tick = getMonotime(); + if (diffMonotime(tick, lastTick) > PIPE_TIMEOUT) { + LOG("No send message for %d seconds, disconnecting", (PIPE_TIMEOUT / 1000)); + this->setConnectedWrite(false); + break; + } + + IMessage *msg = nullptr; + if (this->m_sendQueue.try_pop(msg)) { + if (msg != nullptr) { + lastTick = getMonotime(); + const uint32_t msgSize = (uint32_t)strlen((char *)msg->getData()); + const uint32_t size = msgSize + 4; + char writeBuffer[size]; + strncpy(writeBuffer + 4, (char *)msg->getData(), strlen((char *) msg->getData())); + writeBuffer[0] = (char) (msgSize >> 24); + writeBuffer[1] = (char) (msgSize >> 16); + writeBuffer[2] = (char) (msgSize >> 8); + writeBuffer[3] = (char) (msgSize); + if (size > 3) { + //LOCK(this); + this->lock(); + bufferHead = 0; + while (bufferHead < size) { + len = write(this->m_clientFD, writeBuffer + bufferHead, size - bufferHead); + bufferHead += len; + if (len == -1) { + LOG("Error when writing to socket: %d", errno); + this->setConnectedWrite(false); + }; + }; + this->unlock(); + } + delete msg; + } + } + Sleep(1); + } + LOG("Write loop disconnected"); + Sleep(1); + } + TRACE("Sending thread terminating"); + + return acre::Result::ok; +#endif } acre::Result CNamedPipeServer::readLoop() { +#ifdef WIN32 DWORD cbRead; char *mBuffer = (char *)LocalAlloc(LMEM_FIXED, BUFSIZE); @@ -217,12 +349,12 @@ acre::Result CNamedPipeServer::readLoop() { LOG("LocalAlloc() failed: %d", GetLastError()); } /* - this->validTSServers.insert(std::string("enter a ts3 server id here")); + this->validTSServers.insert(std::string("enter a ts3 server id here")); */ while (!this->getShuttingDown()) { //this->checkServer(); bool ret = ConnectNamedPipe(this->m_PipeHandleRead, NULL); - if (GetLastError() == ERROR_PIPE_CONNECTED) { + if (GetLastError() == ERROR_PIPE_CONNECTED) { LOG("Client read connected"); CEngine::getInstance()->getClient()->updateShouldSwitchChannel(false); CEngine::getInstance()->getClient()->unMuteAll(); @@ -234,16 +366,16 @@ acre::Result CNamedPipeServer::readLoop() { continue; } - clock_t lastTick = clock() / CLOCKS_PER_SEC; + walltime_t lastTick = getMonotime(); while (this->getConnectedRead()) { //this->checkServer(); if (this->getShuttingDown()) { break; } - const clock_t tick = clock() / CLOCKS_PER_SEC; + walltime_t tick = getMonotime(); //LOG("[%d] - [%d] = [%d] vs. [%d]", tick, lastTick, (tick - lastTick),(PIPE_TIMEOUT / 1000)); - if (tick - lastTick > (PIPE_TIMEOUT / 1000)) { + if (diffMonotime(tick, lastTick) > PIPE_TIMEOUT) { LOG("No read message for %d seconds, disconnecting", (PIPE_TIMEOUT / 1000)); this->setConnectedWrite(false); this->setConnectedRead(false); @@ -274,12 +406,12 @@ acre::Result CNamedPipeServer::readLoop() { // Do not free msg, this is deleted inside runProcedure() CEngine::getInstance()->getRpcEngine()->runProcedure(this, msg); - lastTick = clock() / CLOCKS_PER_SEC; + lastTick = getMonotime(); //TRACE("tick [%d], [%s]",lastTick, msg->getData()); } // wait 1ms for new msg so we dont hog cpu cycles } while (!ret); - //ret = ConnectNamedPipe(this->getPipeHandle(), NULL); + //ret = ConnectNamedPipe(this->getPipeHandle(), NULL); Sleep(1); } // Kill the write pipe along with ourselves, because we master shutdown/startup @@ -293,28 +425,177 @@ acre::Result CNamedPipeServer::readLoop() { CEngine::getInstance()->getSoundEngine()->onClientGameDisconnected(); LOG("Client disconnected"); CEngine::getInstance()->getClient()->unMuteAll(); - + // Clear the send queue since client disconnected this->m_sendQueue.clear(); - - // send an event that we have disconnected + + // send an event that we have disconnected if (CEngine::getInstance()->getExternalServer()->getConnected()) { CEngine::getInstance()->getExternalServer()->sendMessage( - CTextMessage::formatNewMessage("ext_reset", + CTextMessage::formatNewMessage("ext_reset", "%d,", CEngine::getInstance()->getSelf()->getId() - ) + ) ); - } + } Sleep(1); } - + if (mBuffer) LocalFree(mBuffer); TRACE("Receiving thread terminating"); return acre::Result::ok; +#else + char *mBuffer = (char *)calloc(1, BUFSIZE); + if (mBuffer == nullptr) { + LOG("calloc failed: %d", errno); + } + + LOG("starting read loop"); + + ssize_t bufferHead; + ssize_t len; + unsigned char lengthBuffer[4]; + fd_set readfds; + + while (!this->getShuttingDown()) { + FD_ZERO(&readfds); + FD_SET(this->m_sockFD, &readfds); + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 50000 + }; + + if (select(this->m_sockFD + 1, &readfds, NULL, NULL, &tv) == 0) { + continue; + } + + this->m_clientFD = accept(this->m_sockFD, NULL, NULL); + if (this->m_clientFD != -1) { + LOG("Client connected"); + CEngine::getInstance()->getClient()->updateShouldSwitchChannel(false); + CEngine::getInstance()->getClient()->unMuteAll(); + CEngine::getInstance()->getSoundEngine()->onClientGameConnected(); + this->setConnectedRead(true); + this->setConnectedWrite(true); + } else { + this->setConnectedRead(false); + this->setConnectedWrite(false); + Sleep(1); + + continue; + } + walltime_t lastTick = getMonotime(); + while (this->getConnectedRead()) { + if (this->getShuttingDown()) { + break; + } + + walltime_t tick = getMonotime(); + if (diffMonotime(tick, lastTick) > PIPE_TIMEOUT) { + LOG("No read message for %d seconds, disconnecting", (PIPE_TIMEOUT / 1000)); + this->setConnectedWrite(false); + this->setConnectedRead(false); + break; + } + + //Run channel switch to server channel + if (CEngine::getInstance()->getClient()->shouldSwitchChannel()) { + CEngine::getInstance()->getClient()->moveToServerChannel(); + } + + // Read exactly four bytes for the message length + bufferHead = 0; + while(bufferHead < 4) { + len = read(this->m_clientFD, (&lengthBuffer) + bufferHead, 4 - bufferHead); + if (len == 0) { + this->setConnectedRead(false); + goto clientClose; + } else if (len == -1) { + LOG("Error when reading from socket: %d", errno); + this->setConnectedRead(false); + goto clientClose; + }; + bufferHead += len; + }; + uint32_t messageLength = (lengthBuffer[0] << 24) + (lengthBuffer[1] << 16) + (lengthBuffer[2] << 8) + lengthBuffer[3]; + + if (messageLength > BUFSIZE - 1) { + LOG("Received too-large message with size %d", messageLength); + this->setConnectedWrite(false); + this->setConnectedRead(false); + break; + } + + mBuffer[messageLength] = 0x00; + + bufferHead = 0; + while(bufferHead < messageLength) { + len = read(this->m_clientFD, (mBuffer) + bufferHead, messageLength - bufferHead); + if (len == 0) { + this->setConnectedRead(false); + goto clientClose; + } else if (len == -1) { + LOG("Error when reading from socket: %d", errno); + this->setConnectedRead(false); + goto clientClose; + }; + bufferHead += len; + }; + + // handle the packet and run it + mBuffer[messageLength] = 0x00; + //LOG("READ: %s", (char *)mBuffer); + IMessage *const msg = new CTextMessage((char *)mBuffer, messageLength); + TRACE("got and parsed message [%s]", msg->getData()); + if (msg != nullptr && msg->getProcedureName()) { + + // Do not free msg, this is deleted inside runProcedure() + CEngine::getInstance()->getRpcEngine()->runProcedure(this, msg); + + lastTick = getMonotime(); + //TRACE("tick [%d], [%s]",lastTick, msg->getData()); + } + // wait 1ms for new msg so we dont hog cpu cycles + Sleep(1); + } + +clientClose: + this->setConnectedWrite(false); + this->setConnectedRead(false); + close(this->m_clientFD); + + //Run channel switch to original channel + CEngine::getInstance()->getClient()->moveToPreviousChannel(); + CEngine::getInstance()->getSoundEngine()->onClientGameDisconnected(); + LOG("Client disconnected"); + CEngine::getInstance()->getClient()->unMuteAll(); + + // Clear the send queue since client disconnected + this->m_sendQueue.clear(); + + // send an event that we have disconnected + if (CEngine::getInstance()->getExternalServer()->getConnected()) { + CEngine::getInstance()->getExternalServer()->sendMessage( + CTextMessage::formatNewMessage("ext_reset", + "%d,", + CEngine::getInstance()->getSelf()->getId() + ) + ); + } + Sleep(1); + } + + if (mBuffer) { + free(mBuffer); + } + + TRACE("Receiving thread terminating"); + + return acre::Result::ok; +#endif } acre::Result CNamedPipeServer::sendMessage( IMessage *message ) { @@ -330,8 +611,11 @@ acre::Result CNamedPipeServer::sendMessage( IMessage *message ) { acre::Result CNamedPipeServer::checkServer( void ) { std::string uniqueId = CEngine::getInstance()->getClient()->getUniqueId(); if (uniqueId != "" && this->validTSServers.find(uniqueId) == this->validTSServers.end()) { +#ifdef WIN32 MessageBoxA(NULL, "This server is NOT registered for ACRE2 testing! Please remove the plugin! Teamspeak will now close.", "ACRE Error", MB_OK | MB_ICONEXCLAMATION); TerminateProcess(GetCurrentProcess(), 0); +#else +#endif } return acre::Result::ok; } diff --git a/extensions/src/ACRE2Core/NamedPipeServer.h b/extensions/src/ACRE2Core/NamedPipeServer.h index 65314ca91..07a35142e 100644 --- a/extensions/src/ACRE2Core/NamedPipeServer.h +++ b/extensions/src/ACRE2Core/NamedPipeServer.h @@ -4,8 +4,15 @@ #include "Types.h" #include "IMessage.h" #include "IServer.h" +#include "AcreSettings.h" +#ifdef WIN32 #include +#else +#include +namespace concurrency = tbb; +#endif + #include #include #include @@ -34,10 +41,12 @@ class CNamedPipeServer : public IServer, public CLockable { char *currentServerId; +#ifdef WIN32 DECLARE_MEMBER(HANDLE, PipeHandleRead); DECLARE_MEMBER(HANDLE, PipeHandleWrite); DECLARE_MEMBER(std::string, FromPipeName); DECLARE_MEMBER(std::string, ToPipeName); +#endif inline void setConnectedWrite(const bool value) { m_connectedWrite = value; } inline bool getConnectedWrite() const { return m_connectedWrite; } @@ -61,9 +70,19 @@ class CNamedPipeServer : public IServer, public CLockable { bool m_shuttingDown; private: - Concurrency::concurrent_queue m_sendQueue; + concurrency::concurrent_queue m_sendQueue; std::thread m_readThread; std::thread m_sendThread; - PSECURITY_ATTRIBUTES m_PipeSecurity; std::set validTSServers; +#ifdef WIN32 + PSECURITY_ATTRIBUTES m_PipeSecurity; +#else + int m_sockFD; + int m_clientFD; +#endif + +#ifdef __linux + uint16_t acreListenPort = CAcreSettings::getInstance()->getWineSocketPort(); +#endif + }; diff --git a/extensions/src/ACRE2Core/RpcEngine.cpp b/extensions/src/ACRE2Core/RpcEngine.cpp index 1c79b9bee..5c8af3217 100644 --- a/extensions/src/ACRE2Core/RpcEngine.cpp +++ b/extensions/src/ACRE2Core/RpcEngine.cpp @@ -67,7 +67,6 @@ acre::Result CRpcEngine::runProcedure(IServer *const serverInstance, IMessage *m } acre::Result CRpcEngine::runProcedure(IServer *const serverInstance, IMessage *msg, const bool entrant) { - if (msg == nullptr) { return acre::Result::error; } else if (msg->getProcedureName() == nullptr) { diff --git a/extensions/src/ACRE2Core/Self.h b/extensions/src/ACRE2Core/Self.h index 34c8f22a9..89455246b 100644 --- a/extensions/src/ACRE2Core/Self.h +++ b/extensions/src/ACRE2Core/Self.h @@ -18,4 +18,4 @@ class CSelf : public CPlayer { DECLARE_MEMBER(acre::CurveModel, CurveModel); DECLARE_MEMBER(BOOL, Speaking); DECLARE_MEMBER(int, CurrentLanguageId); -}; \ No newline at end of file +}; diff --git a/extensions/src/ACRE2Core/SoundMixdownEffect.h b/extensions/src/ACRE2Core/SoundMixdownEffect.h index b46d67bda..e51116afe 100644 --- a/extensions/src/ACRE2Core/SoundMixdownEffect.h +++ b/extensions/src/ACRE2Core/SoundMixdownEffect.h @@ -4,7 +4,13 @@ #include "Lockable.h" #include #include + +#ifdef WIN32 #include +#else +#include +namespace concurrency = tbb; +#endif class CSoundMixdownEffect : public CLockable { private: @@ -15,4 +21,4 @@ class CSoundMixdownEffect : public CLockable { virtual void process(short* samples, int sampleCount, int channels, const unsigned int speakerMask) = 0; void setParam(std::string paramName, float value) { paramMap[paramName] = value; }; float getParam(std::string paramName) { return paramMap[paramName]; }; -}; \ No newline at end of file +}; diff --git a/extensions/src/ACRE2Core/SoundMixer.h b/extensions/src/ACRE2Core/SoundMixer.h index 4e5df254c..a7d815972 100644 --- a/extensions/src/ACRE2Core/SoundMixer.h +++ b/extensions/src/ACRE2Core/SoundMixer.h @@ -6,7 +6,12 @@ #include "SoundMonoChannel.h" #include +#ifdef WIN32 #include +#else +#include +namespace concurrency = tbb; +#endif class CSoundMixer : public CLockable { private: @@ -21,4 +26,4 @@ class CSoundMixer : public CLockable { bool releaseChannel(CSoundChannelMono *releaseChannel); void mixDown(short* samples, int sampleCount, int channels, const unsigned int speakerMask); -}; \ No newline at end of file +}; diff --git a/extensions/src/ACRE2Core/SoundMonoEffect.h b/extensions/src/ACRE2Core/SoundMonoEffect.h index 881d066ae..eb092813e 100644 --- a/extensions/src/ACRE2Core/SoundMonoEffect.h +++ b/extensions/src/ACRE2Core/SoundMonoEffect.h @@ -4,7 +4,13 @@ #include "Lockable.h" #include #include + +#ifdef WIN32 #include +#else +#include +namespace concurrency = tbb; +#endif class CSoundMonoEffect : public CLockable { private: @@ -21,4 +27,4 @@ class CSoundMonoEffect : public CLockable { return 0.0f; } }; -}; \ No newline at end of file +}; diff --git a/extensions/src/ACRE2Core/SoundPlayback.cpp b/extensions/src/ACRE2Core/SoundPlayback.cpp index 9ddd09085..dbb043e9a 100644 --- a/extensions/src/ACRE2Core/SoundPlayback.cpp +++ b/extensions/src/ACRE2Core/SoundPlayback.cpp @@ -35,7 +35,7 @@ acre::Result CSoundPlayback::loadSound(std::string id) { std::string tempPath = CEngine::getInstance()->getClient()->getTempFilePath(); - tempPath += "\\"; + tempPath += PATH_SEPARATOR; tempPath += id; std::ofstream out(tempPath, std::ios::out | std::ios::binary); if (!out.is_open()) { @@ -52,7 +52,7 @@ acre::Result CSoundPlayback::loadSound(std::string id) { acre::Result CSoundPlayback::playSound(std::string id, acre::vec3_fp32_t position, acre::vec3_fp32_t direction, float volume, bool isWorld) { std::string tempPath = CEngine::getInstance()->getClient()->getTempFilePath(); - tempPath += "\\"; + tempPath += PATH_SEPARATOR; tempPath += id; CWave waveFile; if (waveFile.Load(tempPath)) { diff --git a/extensions/src/ACRE2Core/Wave.cpp b/extensions/src/ACRE2Core/Wave.cpp index 2c595dcd8..a60f3f0f2 100644 --- a/extensions/src/ACRE2Core/Wave.cpp +++ b/extensions/src/ACRE2Core/Wave.cpp @@ -1,6 +1,10 @@ +#ifdef WIN32 #include -#include #include +#else +#include +#endif +#include #include "Wave.h" #include "math.h" #include diff --git a/extensions/src/ACRE2Core/Wave.h b/extensions/src/ACRE2Core/Wave.h index 60a717477..3fc59ede3 100644 --- a/extensions/src/ACRE2Core/Wave.h +++ b/extensions/src/ACRE2Core/Wave.h @@ -1,12 +1,31 @@ #pragma once -#include +#include + +#ifdef WIN32 #include -#include -#include #include "Mmsystem.h" +#endif + +#include #include +#ifdef __linux__ +#define BYTE unsigned char +#define LPBYTE unsigned char* +#define SHORT short +#define HWAVEOUT void* +typedef struct wavehdr_tag { + char* lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD* dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct wavehdr_tag *lpNext; + DWORD* reserved; +} WAVEHDR, *LPWAVEHDR; +#endif typedef struct __WAVEDESCR { diff --git a/extensions/src/ACRE2Core/getClientID.h b/extensions/src/ACRE2Core/getClientID.h index b1f678c5a..f9c6d9bdf 100644 --- a/extensions/src/ACRE2Core/getClientID.h +++ b/extensions/src/ACRE2Core/getClientID.h @@ -5,7 +5,6 @@ #include "Macros.h" #include "Log.h" #include "IRpcFunction.h" -#include #include "IServer.h" #include "Engine.h" diff --git a/extensions/src/ACRE2Core/ping.h b/extensions/src/ACRE2Core/ping.h index ab53984a6..128d59762 100644 --- a/extensions/src/ACRE2Core/ping.h +++ b/extensions/src/ACRE2Core/ping.h @@ -10,8 +10,14 @@ volatile DWORD g_pingTime; RPC_FUNCTION(ping) { +#ifdef WIN32 g_pingTime = clock() / CLOCKS_PER_SEC; - vServer->sendMessage(CTextMessage::formatNewMessage("pong", "%f,", g_pingTime)); +#else + timespec t; + clock_gettime(CLOCK_MONOTONIC_RAW, &t); + g_pingTime = t.tv_sec; +#endif + vServer->sendMessage(CTextMessage::formatNewMessage("pong", "%f,", (float) g_pingTime)); return acre::Result::ok; } public: diff --git a/extensions/src/ACRE2Mumble/CMakeLists.txt b/extensions/src/ACRE2Mumble/CMakeLists.txt index 26a5ab283..8967ae86b 100644 --- a/extensions/src/ACRE2Mumble/CMakeLists.txt +++ b/extensions/src/ACRE2Mumble/CMakeLists.txt @@ -11,21 +11,41 @@ file(GLOB_RECURSE SOURCES *.h *.hpp *.c *.cpp *.asm mumble_includes/*) include_directories(mumble_includes) add_library( ${ACRE_NAME} MODULE ${SOURCES} ${GLOBAL_SOURCES}) -target_link_libraries(${ACRE_NAME} ACRE2Core ACRE2Shared x3daudio) -set_target_properties(${ACRE_NAME} PROPERTIES FOLDER ACRE2 LINK_FLAGS -SAFESEH:NO) +if(WIN32) + target_link_libraries(${ACRE_NAME} ACRE2Core ACRE2Shared x3daudio) + set_target_properties(${ACRE_NAME} PROPERTIES FOLDER ACRE2 LINK_FLAGS -SAFESEH:NO) +else() + target_link_libraries(${ACRE_NAME} ACRE2Core ACRE2Shared FAudio) +endif() target_compile_features(${ACRE_NAME} PRIVATE cxx_std_17) # Copy and rename -if(USE_64BIT_BUILD) - set(FINAL_DLL_NAME acre2_win64.dll) +if(WIN32) + if(USE_64BIT_BUILD) + set(FINAL_DLL_NAME acre2_win64.dll) + else() + set(FINAL_DLL_NAME acre2_win32.dll) + endif() else() - set(FINAL_DLL_NAME acre2_win32.dll) + if(USE_64BIT_BUILD) + set(FINAL_DLL_NAME acre2_x64.so) + else() + set(FINAL_DLL_NAME acre2_x86.so) + endif() endif() add_custom_command(TARGET ${ACRE_NAME} POST_BUILD # Copy DLL to plugins COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_SOURCE_DIR}/../plugin/mumble/${FINAL_DLL_NAME} # Copy PDB to symbols - COMMAND ${CMAKE_COMMAND} -E copy $/${ACRE_NAME}.pdb ${PROJECT_SOURCE_DIR}/../symbols/${ACRE_ARCH}/${ACRE_NAME}.pdb + COMMAND_EXPAND_LISTS ) + +if(WIN32) + add_custom_command(TARGET ${ACRE_NAME} POST_BUILD + # Copy PDB to symbols + COMMAND ${CMAKE_COMMAND} -E copy $/${ACRE_NAME}.pdb ${PROJECT_SOURCE_DIR}/../symbols/${ACRE_ARCH}/${ACRE_NAME}.pdb + COMMAND_EXPAND_LISTS + ) +endif() diff --git a/extensions/src/ACRE2Mumble/MumbleClient.cpp b/extensions/src/ACRE2Mumble/MumbleClient.cpp index d883b6076..85bef7a64 100644 --- a/extensions/src/ACRE2Mumble/MumbleClient.cpp +++ b/extensions/src/ACRE2Mumble/MumbleClient.cpp @@ -3,12 +3,18 @@ #include "Log.h" #include "MumbleClient.h" #include "MumbleFunctions.h" -#include "Shlwapi.h" #include "Types.h" #include "compat.h" + +#ifdef WIN32 +#include "Shlwapi.h" #include "shlobj.h" #pragma comment(lib, "Shlwapi.lib") +#else +#include +#include +#endif static constexpr std::int32_t invalid_mumble_channel = -1; constexpr char default_mumble_channel[] = "ACRE"; @@ -55,6 +61,11 @@ acre::Result CMumbleClient::stop() { } acre::Result CMumbleClient::start(const acre::id_t id_) { + #ifdef __linux__ + // Make sure floats get serialized with period decimal separators + setlocale(LC_NUMERIC, "C"); + #endif + CEngine::getInstance()->start(id_); this->setInputActive(false); this->setDirectFirst(false); @@ -218,15 +229,24 @@ std::string CMumbleClient::getUniqueId() { } std::string CMumbleClient::getConfigFilePath(void) { +#ifdef WIN32 std::string tempFolder = ".\\acre"; if (!PathFileExistsA(tempFolder.c_str()) && !CreateDirectoryA(tempFolder.c_str(), nullptr)) { LOG("ERROR: UNABLE TO CREATE TEMP DIR"); } return tempFolder; +#else + if (getenv("XDG_CONFIG_HOME")) { + return std::string(getenv("XDG_CONFIG_HOME")); + } else { + return std::string(getenv("HOME")) + "/.config"; + } +#endif } std::string CMumbleClient::getTempFilePath(void) { +#ifdef WIN32 char tempPath[MAX_PATH - 14]; GetTempPathA(sizeof(tempPath), tempPath); std::string tempFolder = std::string(tempPath); @@ -236,6 +256,14 @@ std::string CMumbleClient::getTempFilePath(void) { } return tempFolder; +#else + std::string dirname = std::string("/tmp/acre-") + getenv("USER"); + int result = mkdir(dirname.c_str(), 0755); + if (result == -1 && errno != EEXIST) { + LOG("ERROR: UNABLE TO CREATE TEMP DIR"); + } + return dirname; +#endif } acre::Result CMumbleClient::microphoneOpen(bool status_) { @@ -324,7 +352,7 @@ uint64_t CMumbleClient::findChannelByNames(std::vector details_) { } for (std::int32_t idx = 0U; idx < channelCount; idx++) { - channelId = *channelList + idx; + channelId = *(channelList + idx); const char *channelName = nullptr; if (mumAPI.getChannelName(pluginID, activeConnection, channelId, &channelName) == MUMBLE_STATUS_OK) { diff --git a/extensions/src/ACRE2Shared/CMakeLists.txt b/extensions/src/ACRE2Shared/CMakeLists.txt index 79007c67d..eccb92df9 100644 --- a/extensions/src/ACRE2Shared/CMakeLists.txt +++ b/extensions/src/ACRE2Shared/CMakeLists.txt @@ -9,3 +9,7 @@ file(GLOB SOURCES *.h *.hpp *.c *.cpp) add_library( ${ACRE_NAME} STATIC ${SOURCES} ${GLOBAL_SOURCES}) target_link_libraries( ${ACRE_NAME}) set_target_properties(${ACRE_NAME} PROPERTIES FOLDER ACRE2) + +if (NOT WIN32) +set_property(TARGET ${ACRE_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() diff --git a/extensions/src/ACRE2Shared/Log.cpp b/extensions/src/ACRE2Shared/Log.cpp index 617957ba8..9dd5070a1 100644 --- a/extensions/src/ACRE2Shared/Log.cpp +++ b/extensions/src/ACRE2Shared/Log.cpp @@ -4,6 +4,12 @@ #include #include +#ifdef __linux +#include +#include +#define strncat(x, y, z) strncat(x, y, z - 1) +#endif + Log *g_Log = nullptr; Log::Log(const char * const logFile) { @@ -23,7 +29,7 @@ Log::~Log(void) { logOutput.close(); } } -size_t Log::Write(const acre::LogLevel msgType, char *function, const uint32_t line, const char *format, ...) { +size_t Log::Write(const acre::LogLevel msgType, const char *function, const uint32_t line, const char *format, ...) { char buffer[4097], tbuffer[1024]; va_list va; @@ -72,9 +78,11 @@ size_t Log::Write(const acre::LogLevel msgType, char *function, const uint32_t l // test debug, print it too printf("%s", buffer); +#ifdef WIN32 if (msgType == acre::LogLevel::Error) { MessageBoxA(NULL, buffer, "CRITICAL ERROR", MB_OK); } +#endif return(ret); } @@ -87,7 +95,9 @@ size_t Log::PopMessage(const acre::LogLevel msgType, const char *format, ...) { int32_t ret = vsnprintf(buffer, 4096, format, va); va_end(va); +#ifdef WIN32 ret = MessageBoxA(NULL, buffer, "Log Message", MB_ICONINFORMATION | MB_OK); +#endif return(ret); } diff --git a/extensions/src/ACRE2Shared/Log.h b/extensions/src/ACRE2Shared/Log.h index 173e18f82..3ffb0c8c9 100644 --- a/extensions/src/ACRE2Shared/Log.h +++ b/extensions/src/ACRE2Shared/Log.h @@ -48,7 +48,7 @@ namespace acre { #ifdef _TRACE #define LOG(...) g_Log->Write(acre::LogLevel::Info, __FUNCTION__, __LINE__, __VA_ARGS__, NULL) #else - #define LOG(...) g_Log->Write(acre::LogLevel::Info, NULL, NULL, __VA_ARGS__, NULL) + #define LOG(...) g_Log->Write(acre::LogLevel::Info, NULL, 0, __VA_ARGS__, NULL) #endif #ifdef _TRACE @@ -67,7 +67,7 @@ class Log { Log(const char *const logFile); ~Log(void); - size_t Write(const acre::LogLevel msgType, char *function, const uint32_t line, const char *format, ...); + size_t Write(const acre::LogLevel msgType, const char *function, const uint32_t line, const char *format, ...); size_t PopMessage(const acre::LogLevel msgType, const char *format, ...); std::ofstream logOutput; diff --git a/extensions/src/ACRE2Shared/Macros.h b/extensions/src/ACRE2Shared/Macros.h index 308ebfef2..3d6c3ee34 100644 --- a/extensions/src/ACRE2Shared/Macros.h +++ b/extensions/src/ACRE2Shared/Macros.h @@ -34,7 +34,7 @@ #define DECLARE_MEMBER_SET(type, name) \ - virtual inline void set##name(##type value) { this->m_##name = value; } + virtual inline void set##name(type value) { this->m_##name = value; } #define DECLARE_MEMBER_GET(type, name) \ virtual inline type get##name() { return this->m_##name; } @@ -73,7 +73,7 @@ protected: \ #define DECLARE_INTERFACE_MEMBER_SET(type, name) \ - virtual void set##name(##type value) = 0; + virtual void set##name(type value) = 0; #define DECLARE_INTERFACE_MEMBER_GET(type, name) \ virtual type get##name() = 0; @@ -90,21 +90,21 @@ public: \ DECLARE_INTERFACE_MEMBER_GET(type, name) -#define RPC_FUNCTION(name) class name## : public IRpcFunction { \ +#define RPC_FUNCTION(name) class name : public IRpcFunction { \ public: \ - name##() : m_Name(STR(name)) {} \ - ~##name(){ } \ + name(){ this->m_Name = STR(name); } \ + ~name(){ } \ acre::Result call(IServer *vServer, IMessage *vMessage) #define CREATE_ITERATOR(type, name, from) \ type name = from; \ - type##::iterator iter_##name; + type::iterator iter_##name; #define DO_ITERATOR(type,name,from) \ CREATE_ITERATOR(type,name,from) \ - for (iter_##name = name##.begin(); \ - iter_##name != name##.end(); \ + for (iter_##name = name.begin(); \ + iter_##name != name.end(); \ iter_##name++ ) #define WAIT_IF_VALID(handle, wait) if (handle != INVALID_HANDLE_VALUE) { \ diff --git a/extensions/src/ACRE2Shared/TextMessage.cpp b/extensions/src/ACRE2Shared/TextMessage.cpp index 9382ffb41..eff486d49 100644 --- a/extensions/src/ACRE2Shared/TextMessage.cpp +++ b/extensions/src/ACRE2Shared/TextMessage.cpp @@ -27,7 +27,7 @@ acre::Result CTextMessage::parse(char *const value, const size_t len) { this->m_IsValid = false; return acre::Result::error; } - + this->m_DataPtr = (char *)LocalAlloc(0, length); memcpy(this->m_DataPtr, value, length); this->m_DataPtr[length-1] = 0x00; @@ -49,7 +49,7 @@ acre::Result CTextMessage::parse(char *const value, const size_t len) { // now parse parameters..if there are any if (pos == (this->m_Data->length() - 1)) { - this->m_IsValid = true; + this->m_IsValid = true; return acre::Result::ok; } @@ -64,7 +64,7 @@ acre::Result CTextMessage::parse(char *const value, const size_t len) { this->m_Parameters[x] = new std::string(t.substr(0, t.find(",")).c_str()); pParamCount += 1; } else if (t.find(",") == std::string::npos) { - this->m_IsValid = true; + this->m_IsValid = true; this->m_Parameters[x] = new std::string(t.substr(0, t.length())); pParamCount += 1; break; @@ -82,7 +82,7 @@ acre::Result CTextMessage::parse(char *const value, const size_t len) { this->m_ParameterCount = pParamCount; this->m_IsValid = true; - + //this->setLength((uint32_t)this->m_Data->size()); return acre::Result::ok; @@ -130,7 +130,7 @@ const unsigned char *const CTextMessage::getParameter(uint32_t index) const { } CTextMessage::~CTextMessage(void) { - + for (uint32_t x = 0; x < TEXTMESSAGE_MAX_PARAMETER_COUNT; x++) { if (this->m_Parameters[x]) { delete this->m_Parameters[x]; @@ -164,21 +164,21 @@ IMessage *CTextMessage::formatNewMessage(const char * const procedureName, const LOG("procedureName was null"); return nullptr; } - + char *finalBuffer = (char *)LocalAlloc(LPTR, TEXTMESSAGE_BUFSIZE); if (buffer == nullptr) { LOG("LocalAlloc() failed: %d", GetLastError()); return nullptr; } - + buffer[0] = 0x00; - _snprintf_s(finalBuffer, TEXTMESSAGE_BUFSIZE, TEXTMESSAGE_BUFSIZE-1, "%s:", procedureName); - + snprintf(finalBuffer, TEXTMESSAGE_BUFSIZE, "%s:", procedureName); + va_start(va, format); - vsprintf_s(buffer, sizeof(buffer), format, va); + vsprintf(buffer, format, va); va_end(va); - strcat_s(finalBuffer, TEXTMESSAGE_BUFSIZE, buffer); + strncat(finalBuffer, buffer, TEXTMESSAGE_BUFSIZE - strlen(finalBuffer)); CTextMessage *msg = new CTextMessage(finalBuffer, strlen(finalBuffer) + 1); @@ -195,33 +195,33 @@ IMessage *CTextMessage::formatNewMessage(const char * const procedureName, const IMessage *CTextMessage::createNewMessage(char *procedureName, ... ) { va_list va; - + if (!procedureName) { LOG("procedureName was null"); return nullptr; } - + char *buffer = (char *)LocalAlloc(LPTR, TEXTMESSAGE_BUFSIZE); if (buffer == nullptr) { LOG("LocalAlloc() failed: %d", GetLastError()); return nullptr; } - + buffer[0] = 0x00; - _snprintf_s(buffer, TEXTMESSAGE_BUFSIZE, TEXTMESSAGE_BUFSIZE - 1, "%s:", procedureName); - + snprintf(buffer, TEXTMESSAGE_BUFSIZE, "%s:", procedureName); + va_start(va, procedureName); char *ptr = va_arg( va, char * ); while (ptr != nullptr) { - strcat_s(buffer, TEXTMESSAGE_BUFSIZE, ptr); - strcat_s(buffer, TEXTMESSAGE_BUFSIZE, ","); - ptr = va_arg( va, char * ); + strncat(buffer, ptr, TEXTMESSAGE_BUFSIZE - strlen(buffer)); + strncat(buffer, ",", TEXTMESSAGE_BUFSIZE - strlen(buffer)); + ptr = va_arg( va, char * ); } va_end(va); buffer = (char *)LocalReAlloc(buffer, strlen(buffer) + 1, LMEM_MOVEABLE); CTextMessage *msg = new CTextMessage(buffer, strlen(buffer) + 1); - + if (!msg->isValid()) { LOG("ERR: msg was invalid"); delete msg; @@ -233,6 +233,6 @@ IMessage *CTextMessage::createNewMessage(char *procedureName, ... ) { return((IMessage *)msg); } -uint32_t CTextMessage::getParameterCount() const { +uint32_t CTextMessage::getParameterCount() const { return this->m_ParameterCount; } diff --git a/extensions/src/ACRE2Shared/_CONSTANTS.h b/extensions/src/ACRE2Shared/_CONSTANTS.h index cd8f2e9c6..87b07efd7 100644 --- a/extensions/src/ACRE2Shared/_CONSTANTS.h +++ b/extensions/src/ACRE2Shared/_CONSTANTS.h @@ -42,7 +42,7 @@ #define ACRE_COMMAND_KEYWORD "ACRE2" #define ACRE_VERSION QUOTE(ACRE_VERSION_MAJOR.ACRE_VERSION_MINOR.ACRE_VERSION_SUBMINOR.ACRE_VERSION_BUILD) -#define ACRE_VERSION_METADATA "Version: "QUOTE(ACRE_VERSION_MAJOR)"."QUOTE(ACRE_VERSION_MINOR)"."QUOTE(ACRE_VERSION_SUBMINOR)"."QUOTE(ACRE_VERSION_BUILD) +#define ACRE_VERSION_METADATA "Version: " QUOTE(ACRE_VERSION_MAJOR) "." QUOTE(ACRE_VERSION_MINOR) "." QUOTE(ACRE_VERSION_SUBMINOR) "." QUOTE(ACRE_VERSION_BUILD) /// warning disablers diff --git a/extensions/src/ACRE2Shared/compat.h b/extensions/src/ACRE2Shared/compat.h index 7ab7d6cac..52106fcac 100644 --- a/extensions/src/ACRE2Shared/compat.h +++ b/extensions/src/ACRE2Shared/compat.h @@ -10,13 +10,18 @@ #pragma warning(disable : 4366) -#define _WINSOCKAPI_ -#define NOMINMAX -#include -#include +#ifdef WIN32 + #define _WINSOCKAPI_ + #define NOMINMAX + #include + #include + #include +#endif + #include -#include #include +#include +#include #ifdef _ACRE_DEBUG_HEAP #define _CRTDBG_MAP_ALLOC @@ -30,14 +35,14 @@ #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "advapi32.lib") - #ifdef _WIN64 + #ifdef _WIN64 #pragma comment(lib, "..\\..\\ACRE2Shared\\bin\\ACRE2Shared_x64.lib") #else #pragma comment(lib, "..\\..\\ACRE2Shared\\bin\\ACRE2Shared_x86.lib") #endif #ifndef _NOCORE - #ifdef _WIN64 + #ifdef _WIN64 #pragma comment(lib, "..\\..\\ACRE2Core\\bin\\ACRE2Core_x64.lib") #else #pragma comment(lib, "..\\..\\ACRE2Core\\bin\\ACRE2Core_x86.lib") @@ -46,4 +51,31 @@ #endif */ //#include "Exception.h" -#include "_CONSTANTS.h" \ No newline at end of file +#include "_CONSTANTS.h" + +#ifdef WIN32 +#define PATH_SEPARATOR "\\" + +#else + +#include +#include +#include + +#define PATH_SEPARATOR "/" + +#define TRUE true +#define FALSE false +typedef int BOOL; +typedef uint32_t DWORD; + +#define MAXSHORT SHRT_MAX +#define MINSHORT SHRT_MIN + +#define LocalAlloc(x, y) calloc(1, y) +#define LocalFree free +#define LocalReAlloc(x, y, z) realloc(x, y) +#define GetLastError() errno +#define Sleep(x) usleep(x * 1000) + +#endif diff --git a/extensions/src/ACRE2Shared/ini.cpp b/extensions/src/ACRE2Shared/ini.cpp index be46aa9df..f2e0528e1 100644 --- a/extensions/src/ACRE2Shared/ini.cpp +++ b/extensions/src/ACRE2Shared/ini.cpp @@ -110,7 +110,7 @@ int ini_parse_file(FILE* file, end = find_char_or_comment(start + 1, ']'); if (*end == ']') { *end = '\0'; - strncpy_s(section, MAX_SECTION, start + 1, sizeof(section)); + strncpy(section, start + 1, sizeof(section)); *prev_name = '\0'; } else if (!error) { @@ -134,7 +134,7 @@ int ini_parse_file(FILE* file, rstrip(value); /* Valid name[=:]value pair found, call handler */ - strncpy_s(prev_name, MAX_NAME, name, sizeof(prev_name)); + strncpy(prev_name, name, sizeof(prev_name)); if (!handler(user, section, name, value) && !error) error = lineno; } @@ -164,7 +164,11 @@ int ini_parse(const char* filename, { FILE* file; int error; +#ifdef WIN32 fopen_s(&file, filename, "r"); +#else + file = fopen(filename, "r"); +#endif if (!file) return -1; error = ini_parse_file(file, handler, user); diff --git a/extensions/src/ACRE2Shared/wine.h b/extensions/src/ACRE2Shared/wine.h new file mode 100644 index 000000000..c42842843 --- /dev/null +++ b/extensions/src/ACRE2Shared/wine.h @@ -0,0 +1,15 @@ +#pragma once + +inline bool detectWine() { + FARPROC pwine_get_version; + HMODULE hntdll = GetModuleHandle("ntdll.dll"); + if(hntdll) { + if(GetProcAddress(hntdll, "wine_get_version")) { + return 1; + } else { + return 0; + } + } else { + return 0; + } +} diff --git a/extensions/src/ACRE2Steam/CallExt_DllMain.cpp b/extensions/src/ACRE2Steam/CallExt_DllMain.cpp index af0a6c6a7..75787ae66 100644 --- a/extensions/src/ACRE2Steam/CallExt_DllMain.cpp +++ b/extensions/src/ACRE2Steam/CallExt_DllMain.cpp @@ -14,6 +14,7 @@ #include "Shlwapi.h" #include "command_options.hpp" #include "mumble_plugin.hpp" +#include "mumble_linux_plugin.hpp" #include "shlobj.h" #include "ts3_plugin.hpp" @@ -74,6 +75,7 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { idi::acre::TS3Plugin ts3_plugin(skip_ts_plugin); idi::acre::MumblePlugin mumble_plugin(skip_mumble_plugin, mumble_path); + idi::acre::MumbleLinuxPlugin mumble_linux_plugin(skip_mumble_plugin, mumble_path); switch (command) { case SteamCommand::check: { @@ -116,8 +118,9 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { const bool ts3_locations_success = ts3_plugin.collect_plugin_locations(); const bool mumble_locations_success = mumble_plugin.collect_plugin_locations(); + const bool mumble_linux_locations_success = mumble_linux_plugin.collect_plugin_locations(); - if (!ts3_locations_success && !mumble_locations_success) { + if (!ts3_locations_success && !mumble_locations_success && !mumble_linux_locations_success) { const std::int32_t result = MessageBoxA(nullptr, "ACRE2 was unable to find a TeamSpeak 3 or a Mumble installation. If you do have an installation please copy the plugins " "yourself or reinstall TeamSpeak 3 or Mumble.\n\nIf you are sure you have TeamSpeak 3 and/or Mumble installed and wish to " @@ -140,9 +143,12 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { std::async(std::launch::async, [&]() { return ts3_plugin.handle_update_plugin(); }); std::future update_mumble = std::async(std::launch::async, [&]() { return mumble_plugin.handle_update_plugin(); }); + std::future update_mumble_linux = + std::async(std::launch::async, [&]() { return mumble_linux_plugin.handle_update_plugin(); }); const idi::acre::UpdateCode ts3_update_result = update_ts3.get(); const idi::acre::UpdateCode mumble_update_result = update_mumble.get(); + const idi::acre::UpdateCode mumble_linux_update_result = update_mumble_linux.get(); std::string error_msg; const bool update_ts3_ok = @@ -157,7 +163,13 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { error_msg = mumble_plugin.get_last_error_message(); } - if (!update_ts3_ok || !update_mumble_ok) { + const bool update_mumble_linux_ok = + (mumble_linux_update_result != idi::acre::UpdateCode::update_failed) && (mumble_linux_update_result != idi::acre::UpdateCode::other); + if (!update_mumble_linux_ok) { + error_msg = mumble_linux_plugin.get_last_error_message(); + } + + if (!update_ts3_ok || !update_mumble_ok || !update_mumble_linux_ok) { std::ostringstream oss; oss << "ACRE2 was unable to copy the Mumble/TeamSpeak 3 plugin. Please check if you have write access to the plugin " << "folder, close any instances of TeamSpeak 3 and/or Mumble and click \"Try Again\".\n\nIf you " @@ -179,7 +191,8 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { // Update was not necessary. if ((ts3_update_result == idi::acre::UpdateCode::update_not_necessary) && - (mumble_update_result == idi::acre::UpdateCode::update_not_necessary)) { // No update was copied etc. + (mumble_update_result == idi::acre::UpdateCode::update_not_necessary) && + (mumble_linux_update_result == idi::acre::UpdateCode::update_not_necessary)) { // No update was copied etc. strncpy(output, "[0]", outputSize); return; } @@ -200,24 +213,35 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { oss << "\n"; } - if (!mumble_plugin.get_updated_paths().empty()) { + if (!mumble_plugin.get_updated_paths().empty() || + !mumble_linux_plugin.get_updated_paths().empty()) { oss << "The Mumble plugins have been copied to the following location(s):\n"; - std::string arch_installed = ""; - if (mumble_plugin.get_arch_to_install() == idi::acre::Architecture::x32) { - arch_installed = " [32-bit only]"; - } else if (mumble_plugin.get_arch_to_install() == idi::acre::Architecture::x64) { - arch_installed = " [64-bit only]"; + if (!mumble_plugin.get_updated_paths().empty()) { + std::string arch_installed = ""; + if (mumble_plugin.get_arch_to_install() == idi::acre::Architecture::x32) { + arch_installed = " [32-bit only]"; + } else if (mumble_plugin.get_arch_to_install() == idi::acre::Architecture::x64) { + arch_installed = " [64-bit only]"; + } + + for (const auto &path : mumble_plugin.get_updated_paths()) { + oss << path << arch_installed << "\n"; + found_paths.append(path + "\n"); + } } - for (const auto &path : mumble_plugin.get_updated_paths()) { - oss << path << arch_installed << "\n"; - found_paths.append(path + "\n"); + if (!mumble_linux_plugin.get_updated_paths().empty()) { + for (const auto &path : mumble_linux_plugin.get_updated_paths()) { + oss << path << " [linux]" << "\n"; + found_paths.append(path + "\n"); + } } oss << "\n"; } + if (!ts3_plugin.get_removed_paths().empty()) { oss << "The TeamSpeak 3 plugin has been removed from the following location(s):\n"; for (const auto &path : ts3_plugin.get_removed_paths()) { @@ -227,10 +251,19 @@ void __stdcall RVExtension(char *output, int outputSize, const char *function) { oss << "\n"; } - if (!mumble_plugin.get_removed_paths().empty()) { + if (!mumble_plugin.get_removed_paths().empty() || + !mumble_linux_plugin.get_removed_paths().empty()) { oss << "The Mumble plugin has been removed from the following location(s):\n"; - for (const auto &path : mumble_plugin.get_removed_paths()) { - oss << path << "\n"; + if (!mumble_plugin.get_removed_paths().empty()) { + for (const auto &path : mumble_plugin.get_removed_paths()) { + oss << path << "\n"; + } + } + + if (!mumble_linux_plugin.get_removed_paths().empty()) { + for (const auto &path : mumble_linux_plugin.get_removed_paths()) { + oss << path << "\n"; + } } oss << "\n"; diff --git a/extensions/src/ACRE2Steam/mumble_linux_plugin.cpp b/extensions/src/ACRE2Steam/mumble_linux_plugin.cpp new file mode 100644 index 000000000..7545ce39b --- /dev/null +++ b/extensions/src/ACRE2Steam/mumble_linux_plugin.cpp @@ -0,0 +1,40 @@ +#include "mumble_linux_plugin.hpp" +#include "wine.h" + +#include + +using ::idi::acre::MumbleLinuxPlugin; + +bool MumbleLinuxPlugin::collect_plugin_locations() noexcept { + if (get_skip_plugin()) { + return true; + } + + if (!detectWine()) { + return true; + } + + // No 32-bit Linux plugin + set_arch_to_install(Architecture::x64); + + if (!mumble_path.empty()) { + check_plugin_locations(mumble_path); + } else { + std::string mumble_data_path("Mumble\\Mumble"); + std::string check_path; + if (getenv("XDG_DATA_HOME")) { + check_path = (std::filesystem::path("Z:") / getenv("XDG_DATA_HOME") / mumble_data_path).string(); + } else if (char* winehome = getenv("WINEHOMEDIR")) { + if (strstr(winehome, "\\??\\")) { + winehome = winehome + 4; + } + check_path = (std::filesystem::path(winehome) / ".local\\share" / mumble_data_path).string(); + } else { + return false; + } + check_plugin_locations(check_path); + } + + // No locations to copy to. + return !get_plugin_locations().empty(); +} diff --git a/extensions/src/ACRE2Steam/mumble_linux_plugin.hpp b/extensions/src/ACRE2Steam/mumble_linux_plugin.hpp new file mode 100644 index 000000000..2f65d75ea --- /dev/null +++ b/extensions/src/ACRE2Steam/mumble_linux_plugin.hpp @@ -0,0 +1,26 @@ +/** + * Mumble auto-plugin copy functionality for running under Wine. + */ +#pragma once +#include "voip_plugin.hpp" + +#include +#include + +namespace idi::acre { + class MumbleLinuxPlugin final : public VOIPPlugin { + public: + explicit MumbleLinuxPlugin(bool skip_plugin_, std::string mumble_path_ = "") noexcept + : VOIPPlugin(skip_plugin_, + "", + "", + find_mod_file("plugin\\mumble\\acre2_x64.so")), + mumble_path(std::move(mumble_path_)) {} + ~MumbleLinuxPlugin() noexcept final = default; + + bool collect_plugin_locations() noexcept final; + + private: + std::string mumble_path; + }; +} // namespace idi::acre diff --git a/extensions/src/ACRE2Steam/voip_plugin.cpp b/extensions/src/ACRE2Steam/voip_plugin.cpp index 7a279d560..971f51de6 100644 --- a/extensions/src/ACRE2Steam/voip_plugin.cpp +++ b/extensions/src/ACRE2Steam/voip_plugin.cpp @@ -194,10 +194,10 @@ idi::acre::UpdateCode VOIPPlugin::handle_update_plugin() noexcept { std::vector> plugin_paths_array; if (arch_to_install == Architecture::both || arch_to_install == Architecture::x32) { - plugin_paths_array.push_back(std::make_pair(plugin_folder / "acre2_win32.dll", x32_acre_plugin)); + plugin_paths_array.push_back(std::make_pair(plugin_folder / x32_acre_plugin.filename(), x32_acre_plugin)); } if (arch_to_install == Architecture::both || arch_to_install == Architecture::x64) { - plugin_paths_array.push_back(std::make_pair(plugin_folder / "acre2_win64.dll", x64_acre_plugin)); + plugin_paths_array.push_back(std::make_pair(plugin_folder / x64_acre_plugin.filename(), x64_acre_plugin)); } for (const auto &path : plugin_paths_array) { @@ -226,7 +226,7 @@ idi::acre::UpdateCode VOIPPlugin::handle_update_plugin() noexcept { continue; } - std::array plugin_paths_array = {plugin_folder / "acre2_win32.dll", plugin_folder / "acre2_win64.dll"}; + std::array plugin_paths_array = {plugin_folder / x32_acre_plugin.filename(), plugin_folder / x64_acre_plugin.filename()}; for (const auto &path : plugin_paths_array) { std::error_code err_code;