/* * MPTrackWine.cpp * --------------- * Purpose: OpenMPT Wine support functions. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #if MPT_COMPILER_MSVC #pragma warning(disable:4800) // 'T' : forcing value to bool 'true' or 'false' (performance warning) #endif // MPT_COMPILER_MSVC #include "MPTrackWine.h" #include "mpt/uuid/uuid.hpp" #include "Mptrack.h" #include "Mainfrm.h" #include "AboutDialog.h" #include "TrackerSettings.h" #include "../common/ComponentManager.h" #include "../common/mptFileIO.h" #include "../misc/mptOS.h" #include "mpt/crc/crc.hpp" #include "../common/FileReader.h" #include "../misc/mptWine.h" #include "MPTrackUtilWine.h" #include "wine/NativeSoundDevice.h" #include "openmpt/sounddevice/SoundDevice.hpp" #include "wine/NativeSoundDeviceMarshalling.h" #include "openmpt/sounddevice/SoundDeviceManager.hpp" #include #include #include OPENMPT_NAMESPACE_BEGIN static mpt::ustring WineGetWindowTitle() { return U_("OpenMPT Wine integration"); } static std::string WineGetWindowTitleUTF8() { return mpt::ToCharset(mpt::Charset::UTF8, WineGetWindowTitle()); } static mpt::PathString WineGetSupportZipFilename() { return P_("openmpt-wine-support.zip"); } static char SanitizeBuildIdChar(char c) { char result = c; if (c == '\0') result = '_'; else if (c >= 'a' && c <= 'z') result = c; else if (c >= 'A' && c <= 'Z') result = c; else if (c >= '0' && c <= '9') result = c; else if (c == '!') result = c; else if (c == '+') result = c; else if (c == '-') result = c; else if (c == '.') result = c; else if (c == '~') result = c; else if (c == '_') result = c; else result = '_'; return result; } static std::string SanitizeBuildID(std::string id) { for(auto & c : id) { c = SanitizeBuildIdChar(c); } return id; } namespace WineIntegration { static mpt::crc64_jones WineHashVersion(mpt::crc64_jones crc) { std::string s; s += mpt::ToCharset(mpt::Charset::UTF8, Build::GetVersionStringExtended()); s += " "; s += mpt::ToCharset(mpt::Charset::UTF8, mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture())); s += " "; s += mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().GetUrlWithRevision()); s += " "; s += mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().GetStateString()); crc(s.begin(), s.end()); return crc; } static mpt::crc64_jones WineHashFile(mpt::crc64_jones crc, mpt::PathString filename) { InputFile file(filename, TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading); if(!file.IsValid()) { return crc; } FileReader f = GetFileReader(file); FileReader::PinnedView view = f.ReadPinnedView(); crc(view.begin(), view.end()); return crc; } static mpt::crc64_jones WineHashSettings(mpt::crc64_jones crc) { std::string result; result += std::string() + "-c"; result += std::string() + "-" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePulseAudio.Get()); result += std::string() + "-" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePortAudio.Get()); crc(result.begin(), result.end()); return crc; } mpt::ustring WineGetSystemInfoString(mpt::OS::Wine::VersionContext & wineVersion) { mpt::ustring msg; msg += CAboutDlg::GetTabText(5); msg += U_("\n"); msg += MPT_UFORMAT("OpenMPT detected Wine {} running on {}.\n") ( wineVersion.Version().AsString() , wineVersion.HostClass() == mpt::osinfo::osclass::Linux ? U_("Linux") : U_("unknown system") ); return msg; } bool WineSetupIsSupported(mpt::OS::Wine::VersionContext & wineVersion) { bool supported = true; if(wineVersion.RawBuildID().empty()) supported = false; if(!TrackerSettings::Instance().WineSupportAllowUnknownHost) { if((wineVersion.HostClass() == mpt::osinfo::osclass::Linux) || ((wineVersion.HostClass() == mpt::osinfo::osclass::BSD) && wineVersion.RawHostSysName() == "FreeBSD")) { // ok } else { supported = false; } } if(!wineVersion.Version().IsValid()) supported = false; return supported; } bool WineSetupIsSupported(mpt::Wine::Context & wine) { bool supported = true; if(theApp.GetInstallPath().empty()) supported = false; if(wine.PathToPosix(theApp.GetInstallPath()).empty()) supported = false; if(wine.PathToPosix(theApp.GetConfigPath()).empty()) supported = false; if(wine.PathToWindows("/").empty()) supported = false; if(supported) { if(wine.HOME().empty()) supported = false; } if(supported) { if(wine.Uname_m() == "x86_64" && mpt::pointer_size != 8) supported = false; } return supported; } static std::map > UnzipToMap(mpt::PathString filename) { std::map > filetree; { zlib_filefunc64_def zipfilefuncs; MemsetZero(zipfilefuncs); fill_win32_filefunc64W(&zipfilefuncs); unzFile zipfile = unzOpen2_64(filename.ToWide().c_str(), &zipfilefuncs); if(!zipfile) { throw mpt::Wine::Exception("Archive is not a zip file"); } for(int status = unzGoToFirstFile(zipfile); status == UNZ_OK; status = unzGoToNextFile(zipfile)) { int openstatus = UNZ_OK; openstatus = unzOpenCurrentFile(zipfile); if(openstatus != UNZ_OK) { unzClose(zipfile); throw mpt::Wine::Exception("Archive is corrupted."); } unz_file_info info; MemsetZero(info); char name[1024]; MemsetZero(name); openstatus = unzGetCurrentFileInfo(zipfile, &info, name, sizeof(name) - 1, nullptr, 0, nullptr, 0); if(openstatus != UNZ_OK) { unzCloseCurrentFile(zipfile); unzClose(zipfile); throw mpt::Wine::Exception("Archive is corrupted."); } std::vector data(info.uncompressed_size); unzReadCurrentFile(zipfile, &data[0], info.uncompressed_size); unzCloseCurrentFile(zipfile); data = mpt::buffer_cast>(mpt::replace(mpt::buffer_cast(data), std::string("\r\n"), std::string("\n"))); filetree[mpt::replace(mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::CP437, name), std::string("\\"), std::string("/"))] = data; } unzClose(zipfile); } return filetree; } bool IsSupported() { return theApp.GetWine() ? true : false; } bool IsCompiled() { return !theApp.GetWineWrapperDllFilename().empty(); } void Initialize() { if(!mpt::OS::Windows::IsWine()) { return; } mpt::ustring lf = U_("\n"); if(!TrackerSettings::Instance().WineSupportEnabled) { return; } mpt::OS::Wine::VersionContext wineVersion = *theApp.GetWineVersion(); if(!WineSetupIsSupported(wineVersion)) { mpt::ustring msg; msg += U_("OpenMPT does not support Wine integration on your current Wine setup.") + lf; Reporting::Notification(msg, WineGetWindowTitle()); return; } try { mpt::Wine::Context wine = mpt::Wine::Context(wineVersion); if(!WineSetupIsSupported(wine)) { mpt::ustring msg; msg += U_("OpenMPT does not support Wine integration on your current Wine setup.") + lf; Reporting::Notification(msg, WineGetWindowTitle()); return; } theApp.SetWine(std::make_shared(wine)); } catch(const std::exception & e) { mpt::ustring msg; msg += U_("OpenMPT was not able to determine Wine configuration details on your current Wine setup:") + lf; msg += mpt::get_exception_text(e) + lf; msg += U_("OpenMPT native Wine Integration will not be available.") + lf; Reporting::Error(msg, WineGetWindowTitle()); return; } mpt::Wine::Context wine = *theApp.GetWine(); try { struct Paths { mpt::PathString AppData; mpt::PathString AppData_Wine; mpt::PathString AppData_Wine_WineVersion; mpt::PathString AppData_Wine_WineVersion_OpenMPTVersion; std::string Host_AppData; std::string Host_AppData_Wine; std::string Host_AppData_Wine_WineVersion; std::string Host_AppData_Wine_WineVersion_OpenMPTVersion; std::string Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion; static void CreatePath(mpt::PathString path) { if(path.IsDirectory()) { return; } if(CreateDirectory(path.AsNative().c_str(), NULL) == 0) { throw mpt::Wine::Exception(std::string() + "Failed to create directory: " + path.ToUTF8()); } } std::string GetOpenMPTVersion() const { std::string ver; ver += mpt::ToCharset(mpt::Charset::UTF8, Build::GetVersionStringPure() + U_("_") + mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture())); mpt::crc64_jones crc; crc = WineHashVersion(crc); crc = WineHashFile(crc, theApp.GetInstallPath() + WineGetSupportZipFilename()); crc = WineHashSettings(crc); ver += std::string("-") + mpt::afmt::hex0<16>(crc.result()); return ver; } Paths(mpt::Wine::Context & wine) { AppData = theApp.GetConfigPath().WithoutTrailingSlash(); AppData_Wine = AppData.WithTrailingSlash() + P_("Wine"); AppData_Wine_WineVersion = AppData_Wine.WithTrailingSlash() + mpt::PathString::FromUTF8(SanitizeBuildID(wine.VersionContext().RawBuildID())); AppData_Wine_WineVersion_OpenMPTVersion = AppData_Wine_WineVersion.WithTrailingSlash() + mpt::PathString::FromUTF8(GetOpenMPTVersion()); CreatePath(AppData); CreatePath(AppData_Wine); CreatePath(AppData_Wine_WineVersion); CreatePath(AppData_Wine_WineVersion_OpenMPTVersion); Host_AppData = wine.PathToPosixCanonical(AppData); Host_AppData_Wine = wine.PathToPosixCanonical(AppData_Wine); Host_AppData_Wine_WineVersion = wine.PathToPosixCanonical(AppData_Wine_WineVersion); Host_AppData_Wine_WineVersion_OpenMPTVersion = wine.PathToPosixCanonical(AppData_Wine_WineVersion_OpenMPTVersion); Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion = wine.XDG_DATA_HOME() + "/OpenMPT/Wine/" + SanitizeBuildID(wine.VersionContext().RawBuildID()) + "/" + GetOpenMPTVersion(); } }; const Paths paths(wine); const std::string nativeSearchPath = paths.Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion; if(!TrackerSettings::Instance().WineSupportAlwaysRecompile) { if((paths.AppData_Wine_WineVersion_OpenMPTVersion.WithTrailingSlash() + P_("success.txt")).IsFile()) { theApp.SetWineWrapperDllFilename(paths.AppData_Wine_WineVersion_OpenMPTVersion.WithTrailingSlash() + P_("openmpt_wine_wrapper.dll")); return; } } if(TrackerSettings::Instance().WineSupportAskCompile) { mpt::ustring msg; msg += U_("OpenMPT Wine integration requires recompilation and will not work otherwise.\n"); msg += U_("Recompile now?\n"); if(Reporting::Confirm(msg, WineGetWindowTitle(), false, false) != cnfYes) { return; } } std::map > filetree; filetree = UnzipToMap(theApp.GetInstallPath() + WineGetSupportZipFilename()); Util::Wine::Dialog dialog(WineGetWindowTitleUTF8(), TrackerSettings::Instance().WineSupportCompileVerbosity < 6); std::string script; script += std::string() + "#!/usr/bin/env sh" + "\n"; script += std::string() + "\n"; script += std::string() + "touch message.txt" + "\n"; script += std::string() + "\n"; script += dialog.Detect(); if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 6) { script += std::string() + "echo Working directory:" + "\n"; script += std::string() + "pwd" + "\n"; } script += std::string() + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 3) { script += std::string() + "echo " + WineGetWindowTitleUTF8() + "\n"; } else { if(TrackerSettings::Instance().WineSupportCompileVerbosity == 2) { script += std::string() + "{" + "\n"; script += std::string() + " echo 0" + "\n"; script += std::string() + " echo 100" + "\n"; script += std::string() + "} | " + dialog.Progress("[>>] Prepare OpenMPT Wine Integration\\n[ ] Compile native support\\n[ ] Compile Wine wrapper\\n\\n[1/3] Preparing OpenMPT Wine Integration ...") + "\n"; } else { script += std::string() + dialog.Status("Preparing OpenMPT Wine Integration.") + "\n"; } } script += std::string() + "\n"; script += std::string() + "printf \"#pragma once\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "printf \"#define OPENMPT_VERSION_URL \\\"" + mpt::ToCharset(mpt::Charset::ASCII, SourceInfo::Current().Url()) + "\\\"\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "printf \"#define OPENMPT_VERSION_DATE \\\"" + mpt::ToCharset(mpt::Charset::ASCII, SourceInfo::Current().Date()) + "\\\"\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "printf \"#define OPENMPT_VERSION_REVISION " + mpt::afmt::dec(SourceInfo::Current().Revision()) + "\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "printf \"#define OPENMPT_VERSION_DIRTY " + mpt::afmt::dec(SourceInfo::Current().IsDirty()) + "\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "printf \"#define OPENMPT_VERSION_MIXEDREVISIONS " + mpt::afmt::dec(SourceInfo::Current().HasMixedRevisions()) + "\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "printf \"#define OPENMPT_VERSION_IS_PACKAGE " + mpt::afmt::dec(SourceInfo::Current().IsPackage()) + "\\n\" >> common/svn_version.h" + "\n"; script += std::string() + "\n"; script += std::string() + "missing=" + "\n"; script += std::string() + "\n"; const std::string make = ((wineVersion.HostClass() == mpt::osinfo::osclass::BSD) ? "gmake" : "make"); std::vector commands; commands.push_back(make); commands.push_back("pkg-config"); commands.push_back("cpp"); commands.push_back("cc"); commands.push_back("c++"); commands.push_back("ld"); commands.push_back("ccache"); for(const auto &command : commands) { script += std::string() + "command -v " + command + " 2>/dev/null 1>/dev/null" + "\n"; script += std::string() + "if [ \"$?\" -ne \"0\" ] ; then" + "\n"; script += std::string() + " missing=\"$missing " + command + "\"" + "\n"; script += std::string() + "fi" + "\n"; } script += std::string() + "if [ \"x$missing\" = \"x\" ] ; then" + "\n"; script += std::string() + " printf \"\"" + "\n"; script += std::string() + "else" + "\n"; #if 0 if(!TrackerSettings::Instance().WineSupportSilentCompile >= 1) { script += std::string() + " " + dialog.YesNo("The following commands are missing:\\n\\n$missing\\n\\nDo you want OpenMPT to try installing those now?") + "\n"; } script += std::string() + " if [ \"$?\" -ne \"0\" ] ; then" + "\n"; script += std::string() + " exit 1" + "\n"; script += std::string() + " fi" + "\n"; #else if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { script += std::string() + " " + dialog.MessageBox("The following commands are missing:\\n\\n$missing\\n\\nPlease install them with your system package installer.") + "\n"; } script += std::string() + " exit 1" + "\n"; #endif script += std::string() + "fi" + "\n"; script += std::string() + "\n"; script += std::string() + "mkdir -p " + wine.EscapePosixShell(wine.XDG_DATA_HOME()) + "/OpenMPT/Wine" + "\n"; script += std::string() + "mkdir -p " + wine.EscapePosixShell(wine.XDG_CACHE_HOME()) + "/OpenMPT/Wine" + "\n"; script += std::string() + "mkdir -p " + wine.EscapePosixShell(wine.XDG_CONFIG_HOME()) + "/OpenMPT/Wine" + "\n"; script += std::string() + "mkdir -p " + wine.EscapePosixShell(paths.Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion) + "\n"; script += std::string() + "\n"; script += std::string() + "CCACHE_DIR=" + wine.EscapePosixShell(wine.XDG_CACHE_HOME()) + "/OpenMPT/Wine/ccache" + "\n"; script += std::string() + "CCACHE_COMPRESS=1" + " \n"; script += std::string() + "export CCACHE_DIR" + " \n"; script += std::string() + "export CCACHE_COMPRESS" + " \n"; script += std::string() + "\n"; std::vector winegcc; if constexpr(mpt::arch_bits == 32) { // 32bit winegcc probably cannot compile to 64bit winegcc.push_back("winegcc32-development"); } MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64)) { winegcc.push_back("winegcc64-development"); } winegcc.push_back("winegcc-development"); if(wineVersion.HostClass() != mpt::osinfo::osclass::BSD) { // avoid C++ compiler on *BSD because libc++ Win32 support tends to be missing there. if constexpr(mpt::arch_bits == 32) { // 32bit winegcc probably cannot compile to 64bit winegcc.push_back("wineg++32-development"); } MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64)) { winegcc.push_back("wineg++64-development"); } winegcc.push_back("wineg++-development"); } if constexpr(mpt::arch_bits == 32) { // 32bit winegcc probably cannot compile to 64bit winegcc.push_back("winegcc32"); } MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64)) { winegcc.push_back("winegcc64"); } winegcc.push_back("winegcc"); if(wineVersion.HostClass() != mpt::osinfo::osclass::BSD) { // avoid C++ compiler on *BSD because libc++ Win32 support tends to be missing there. if constexpr(mpt::arch_bits == 32) { // 32bit winegcc probably cannot compile to 64bit winegcc.push_back("wineg++32"); } MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64)) { winegcc.push_back("wineg++64"); } winegcc.push_back("wineg++"); } for(const auto &c : winegcc) { script += std::string() + "if command -v " + c + " 2>/dev/null 1>/dev/null ; then" + "\n"; script += std::string() + " MPT_WINEGXX=" + c + "\n"; script += std::string() + "fi" + "\n"; } script += std::string() + "if [ -z $MPT_WINEGXX ] ; then" + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { script += std::string() + " " + dialog.MessageBox("WineGCC not found.\\nPlease install it with your system package installer.") + "\n"; } script += std::string() + " exit 1" + "\n"; script += std::string() + "fi" + "\n"; // Work-around for Debian 8, Wine 1.6.2 MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64)) { script += std::string() + "if [ `$MPT_WINEGXX > /dev/null 2>&1 ; echo $?` -eq 127 ] ; then" + "\n"; script += std::string() + " if command -v /usr/lib/x86_64-linux-gnu/wine/bin/winegcc 2>/dev/null 1>/dev/null ; then" + "\n"; script += std::string() + " MPT_WINEGXX=/usr/lib/x86_64-linux-gnu/wine/bin/winegcc"+ "\n"; script += std::string() + " PATH=/usr/lib/x86_64-linux-gnu/wine/bin:\"${PATH}\"" + "\n"; script += std::string() + " export PATH" + "\n"; script += std::string() + " fi" + "\n"; script += std::string() + "fi" + "\n"; } if constexpr(mpt::arch_bits == 32) { script += std::string() + "if [ `$MPT_WINEGXX > /dev/null 2>&1 ; echo $?` -eq 127 ] ; then" + "\n"; script += std::string() + " if command -v /usr/lib/i386-linux-gnu/wine/bin/winegcc 2>/dev/null 1>/dev/null ; then" + "\n"; script += std::string() + " MPT_WINEGXX=/usr/lib/i386-linux-gnu/wine/bin/winegcc" + "\n"; script += std::string() + " PATH=/usr/lib/i386-linux-gnu/wine/bin:\"${PATH}\"" + "\n"; script += std::string() + " export PATH" + "\n"; script += std::string() + " fi" + "\n"; script += std::string() + "fi" + "\n"; } std::string features; if(TrackerSettings::Instance().WineSupportForeignOpenMPT) { features += std::string() + " " + "MPT_ARCH_BITS=" + mpt::afmt::dec(mpt::arch_bits); if constexpr(mpt::arch_bits == 64) { features += std::string() + " " + "MPT_TARGET=" + "x86_64-linux-gnu-"; } else { features += std::string() + " " + "MPT_TARGET=" + "i686-linux-gnu-"; } } features += std::string() + " " + "MPT_TRY_PORTAUDIO=" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePortAudio.Get()); features += std::string() + " " + "MPT_TRY_PULSEAUDIO=" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePulseAudio.Get()); features += std::string() + " " + "MPT_TRY_RTAUDIO=" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnableRtAudio.Get()); int makeverbosity = Clamp(TrackerSettings::Instance().WineSupportCompileVerbosity.Get(), 0, 6); if(TrackerSettings::Instance().WineSupportCompileVerbosity == 2) { script += std::string() + "{" + "\n"; script += std::string() + " echo 0" + "\n"; script += std::string() + " " + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast(1))) + " -f build/wine/native_support.mk" + " V=" + mpt::afmt::dec(makeverbosity) + " " + features + " all MPT_PROGRESS_FILE=\"&4\" 4>&1 1>stdout.txt 2>stderr.txt" + "\n"; script += std::string() + " echo -n $? > stdexit.txt" + "\n"; script += std::string() + " echo 100" + "\n"; script += std::string() + "} | " + dialog.Progress("[OK] Prepare OpenMPT Wine Integration\\n[>>] Compile native support\\n[ ] Compile Wine wrapper\\n\\n[2/3] Compiling native support ...") + "\n"; script += std::string() + "MPT_EXITCODE=`cat stdexit.txt`" + "\n"; script += std::string() + "if [ \"$MPT_EXITCODE\" -ne \"0\" ] ; then" + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; } script += std::string() + " exit 1" + "\n"; script += std::string() + "fi" + "\n"; script += std::string() + "if [ -s stderr.txt ] ; then" + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; script += std::string() + "fi" + "\n"; script += std::string() + "{" + "\n"; script += std::string() + " echo 0" + "\n"; script += std::string() + " " + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast(1))) + " -f build/wine/wine_wrapper.mk" + " V=" + mpt::afmt::dec(makeverbosity) + " WINEGXX=$MPT_WINEGXX " + "MPT_WINEGCC_LANG=" + ((wineVersion.HostClass() == mpt::osinfo::osclass::BSD) ? "C" : "CPLUSPLUS") + " MPT_WINE_SEARCHPATH=" + wine.EscapePosixShell(nativeSearchPath) + " all MPT_PROGRESS_FILE=\"&4\" 4>&1 1>stdout.txt 2>stderr.txt" + "\n"; script += std::string() + " echo -n $? > stdexit.txt" + "\n"; script += std::string() + " echo 100" + "\n"; script += std::string() + "} | " + dialog.Progress("[OK] Prepare OpenMPT Wine Integration\\n[OK] Compile native support\\n[>>] Compile Wine wrapper\\n\\n[3/3] Compiling Wine wrapper ...") + "\n"; script += std::string() + "MPT_EXITCODE=`cat stdexit.txt`" + "\n"; script += std::string() + "if [ \"$MPT_EXITCODE\" -ne \"0\" ] ; then" + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; } script += std::string() + " exit 1" + "\n"; script += std::string() + "fi" + "\n"; script += std::string() + "if [ -s stderr.txt ] ; then" + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; script += std::string() + "fi" + "\n"; } else { script += std::string() + "" + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast(1))) + " -f build/wine/native_support.mk" + " V=" + mpt::afmt::dec(makeverbosity) + " " + features + " all" + "\n"; script += std::string() + "if [ \"$?\" -ne \"0\" ] ; then" + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; } script += std::string() + " exit 1" + "\n"; script += std::string() + "fi" + "\n"; script += std::string() + "if [ -s stderr.txt ] ; then" + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; script += std::string() + "fi" + "\n"; script += std::string() + "" + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast(1))) + " -f build/wine/wine_wrapper.mk" + " V=" + mpt::afmt::dec(makeverbosity) + " WINEGXX=$MPT_WINEGXX " + "MPT_WINEGCC_LANG=" + ((wineVersion.HostClass() == mpt::osinfo::osclass::BSD) ? "C" : "CPLUSPLUS") + " MPT_WINE_SEARCHPATH=" + wine.EscapePosixShell(nativeSearchPath) + " all" + "\n"; script += std::string() + "if [ \"$?\" -ne \"0\" ] ; then" + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; } script += std::string() + " exit 1" + "\n"; script += std::string() + "fi" + "\n"; script += std::string() + "if [ -s stderr.txt ] ; then" + "\n"; script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n"; script += std::string() + "fi" + "\n"; } script += std::string() + "\n"; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 6) { script += std::string() + dialog.MessageBox("OpenMPT Wine integration compiled successfully.") + "\n"; } script += std::string() + "\n"; script += "exit 0" "\n"; CMainFrame::GetMainFrame()->EnableWindow(FALSE); mpt::Wine::ExecResult result; try { FlagSet flags = mpt::Wine::ExecFlagNone; if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1) { flags = (mpt::Wine::ExecFlagProgressWindow | mpt::Wine::ExecFlagInteractive); } else if(TrackerSettings::Instance().WineSupportCompileVerbosity == 0) { flags = (mpt::Wine::ExecFlagProgressWindow | mpt::Wine::ExecFlagSilent); } else { flags = (mpt::Wine::ExecFlagSilent); } result = Util::Wine::ExecutePosixShellScript ( wine , script , flags , filetree , WineGetWindowTitleUTF8() , "Compiling Wine support ..." ); } catch(const mpt::Wine::Exception & /* e */ ) { CMainFrame::GetMainFrame()->EnableWindow(TRUE); throw; } CMainFrame::GetMainFrame()->EnableWindow(TRUE); if(result.exitcode != 0) { if(result.filetree["message.txt"].size() > 0) { throw mpt::Wine::Exception(std::string(result.filetree["message.txt"].begin(), result.filetree["message.txt"].end())); } else { throw mpt::Wine::Exception("Executing Wine integration build script failed."); } } { std::string fn = "libopenmpt_native_support.so"; mpt::ofstream f(wine.PathToWindows(nativeSearchPath) + P_("\\") + mpt::PathString::FromUTF8(fn), std::ios::binary); f.write(&result.filetree[fn][0], result.filetree[fn].size()); f.flush(); if(!f) { throw mpt::Wine::Exception("Writing libopenmpt_native_support.so failed."); } } { std::string fn = "openmpt_wine_wrapper.dll"; mpt::ofstream f(paths.AppData_Wine_WineVersion_OpenMPTVersion + P_("\\") + mpt::PathString::FromUTF8(fn), std::ios::binary); f.write(&result.filetree[fn][0], result.filetree[fn].size()); f.flush(); if(!f) { throw mpt::Wine::Exception("Writing openmpt_wine_wrapper.dll failed."); } } { std::string fn = "success.txt"; mpt::ofstream f(paths.AppData_Wine_WineVersion_OpenMPTVersion + P_("\\") + mpt::PathString::FromUTF8(fn), std::ios::binary); f.imbue(std::locale::classic()); f << std::string("1"); f.flush(); if(!f) { throw mpt::Wine::Exception("Writing success.txt failed."); } } theApp.SetWineWrapperDllFilename(paths.AppData_Wine_WineVersion_OpenMPTVersion + P_("\\") + P_("openmpt_wine_wrapper.dll")); } catch(const mpt::Wine::Exception &e) { Reporting::Error(U_("Setting up OpenMPT Wine integration failed: ") + mpt::get_exception_text(e), WineGetWindowTitle()); } } } // namespace WineIntegration std::string ComponentWineWrapper::result_as_string(char * str) const { std::string result = str; OpenMPT_Wine_Wrapper_String_Free(str); return result; } ComponentWineWrapper::ComponentWineWrapper() : ComponentLibrary(ComponentTypeBundled) { return; } bool ComponentWineWrapper::DoInitialize() { if(theApp.GetWineWrapperDllFilename().empty()) { return false; } AddLibrary("WineWrapper", mpt::LibraryPath::FullPath(theApp.GetWineWrapperDllFilename())); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_Init); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_Fini); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_String_Free); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_EnumerateDevices); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Construct); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Destruct); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_SetMessageReceiver); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_SetCallback); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetDeviceInfo); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetDeviceCaps); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetDeviceDynamicCaps); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Init); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Open); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Close); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Start); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Stop); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetRequestFlags); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsInited); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsOpen); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsAvailable); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsPlaying); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsPlayingSilence); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_StopAndAvoidPlayingSilence); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_EndPlayingSilence); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_OnIdle); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetSettings); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetActualSampleFormat); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetEffectiveBufferAttributes); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetTimeInfo); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetStreamPosition); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_DebugIsFragileDevice); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_DebugInRealtimeCallback); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetStatistics); MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_OpenDriverSettings); if(HasBindFailed()) { Reporting::Error("OpenMPT Wine integration failed loading.", WineGetWindowTitle()); return false; } if(OpenMPT_Wine_Wrapper_Init() != 0) { Reporting::Error("OpenMPT Wine integration initialization failed.", WineGetWindowTitle()); return false; } if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 6) { Reporting::Notification(MPT_AFORMAT("OpenMPT Wine integration loaded successfully.")(), WineGetWindowTitle()); } return true; } ComponentWineWrapper::~ComponentWineWrapper() { if(IsAvailable()) { OpenMPT_Wine_Wrapper_Fini(); } } namespace WineIntegration { void Load() { ReloadComponent(); } } // namespace WineIntegration OPENMPT_NAMESPACE_END