MPTrackWine.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. /*
  2. * MPTrackWine.cpp
  3. * ---------------
  4. * Purpose: OpenMPT Wine support functions.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #if MPT_COMPILER_MSVC
  11. #pragma warning(disable:4800) // 'T' : forcing value to bool 'true' or 'false' (performance warning)
  12. #endif // MPT_COMPILER_MSVC
  13. #include "MPTrackWine.h"
  14. #include "mpt/uuid/uuid.hpp"
  15. #include "Mptrack.h"
  16. #include "Mainfrm.h"
  17. #include "AboutDialog.h"
  18. #include "TrackerSettings.h"
  19. #include "../common/ComponentManager.h"
  20. #include "../common/mptFileIO.h"
  21. #include "../misc/mptOS.h"
  22. #include "mpt/crc/crc.hpp"
  23. #include "../common/FileReader.h"
  24. #include "../misc/mptWine.h"
  25. #include "MPTrackUtilWine.h"
  26. #include "wine/NativeSoundDevice.h"
  27. #include "openmpt/sounddevice/SoundDevice.hpp"
  28. #include "wine/NativeSoundDeviceMarshalling.h"
  29. #include "openmpt/sounddevice/SoundDeviceManager.hpp"
  30. #include <ios>
  31. #include <contrib/minizip/unzip.h>
  32. #include <contrib/minizip/iowin32.h>
  33. OPENMPT_NAMESPACE_BEGIN
  34. static mpt::ustring WineGetWindowTitle()
  35. {
  36. return U_("OpenMPT Wine integration");
  37. }
  38. static std::string WineGetWindowTitleUTF8()
  39. {
  40. return mpt::ToCharset(mpt::Charset::UTF8, WineGetWindowTitle());
  41. }
  42. static mpt::PathString WineGetSupportZipFilename()
  43. {
  44. return P_("openmpt-wine-support.zip");
  45. }
  46. static char SanitizeBuildIdChar(char c)
  47. {
  48. char result = c;
  49. if (c == '\0') result = '_';
  50. else if (c >= 'a' && c <= 'z') result = c;
  51. else if (c >= 'A' && c <= 'Z') result = c;
  52. else if (c >= '0' && c <= '9') result = c;
  53. else if (c == '!') result = c;
  54. else if (c == '+') result = c;
  55. else if (c == '-') result = c;
  56. else if (c == '.') result = c;
  57. else if (c == '~') result = c;
  58. else if (c == '_') result = c;
  59. else result = '_';
  60. return result;
  61. }
  62. static std::string SanitizeBuildID(std::string id)
  63. {
  64. for(auto & c : id)
  65. {
  66. c = SanitizeBuildIdChar(c);
  67. }
  68. return id;
  69. }
  70. namespace WineIntegration {
  71. static mpt::crc64_jones WineHashVersion(mpt::crc64_jones crc)
  72. {
  73. std::string s;
  74. s += mpt::ToCharset(mpt::Charset::UTF8, Build::GetVersionStringExtended());
  75. s += " ";
  76. s += mpt::ToCharset(mpt::Charset::UTF8, mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()));
  77. s += " ";
  78. s += mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().GetUrlWithRevision());
  79. s += " ";
  80. s += mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().GetStateString());
  81. crc(s.begin(), s.end());
  82. return crc;
  83. }
  84. static mpt::crc64_jones WineHashFile(mpt::crc64_jones crc, mpt::PathString filename)
  85. {
  86. InputFile file(filename, TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading);
  87. if(!file.IsValid())
  88. {
  89. return crc;
  90. }
  91. FileReader f = GetFileReader(file);
  92. FileReader::PinnedView view = f.ReadPinnedView();
  93. crc(view.begin(), view.end());
  94. return crc;
  95. }
  96. static mpt::crc64_jones WineHashSettings(mpt::crc64_jones crc)
  97. {
  98. std::string result;
  99. result += std::string() + "-c";
  100. result += std::string() + "-" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePulseAudio.Get());
  101. result += std::string() + "-" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePortAudio.Get());
  102. crc(result.begin(), result.end());
  103. return crc;
  104. }
  105. mpt::ustring WineGetSystemInfoString(mpt::OS::Wine::VersionContext & wineVersion)
  106. {
  107. mpt::ustring msg;
  108. msg += CAboutDlg::GetTabText(5);
  109. msg += U_("\n");
  110. msg += MPT_UFORMAT("OpenMPT detected Wine {} running on {}.\n")
  111. ( wineVersion.Version().AsString()
  112. , wineVersion.HostClass() == mpt::osinfo::osclass::Linux ? U_("Linux") : U_("unknown system")
  113. );
  114. return msg;
  115. }
  116. bool WineSetupIsSupported(mpt::OS::Wine::VersionContext & wineVersion)
  117. {
  118. bool supported = true;
  119. if(wineVersion.RawBuildID().empty()) supported = false;
  120. if(!TrackerSettings::Instance().WineSupportAllowUnknownHost)
  121. {
  122. if((wineVersion.HostClass() == mpt::osinfo::osclass::Linux) || ((wineVersion.HostClass() == mpt::osinfo::osclass::BSD) && wineVersion.RawHostSysName() == "FreeBSD"))
  123. {
  124. // ok
  125. } else
  126. {
  127. supported = false;
  128. }
  129. }
  130. if(!wineVersion.Version().IsValid()) supported = false;
  131. return supported;
  132. }
  133. bool WineSetupIsSupported(mpt::Wine::Context & wine)
  134. {
  135. bool supported = true;
  136. if(theApp.GetInstallPath().empty()) supported = false;
  137. if(wine.PathToPosix(theApp.GetInstallPath()).empty()) supported = false;
  138. if(wine.PathToPosix(theApp.GetConfigPath()).empty()) supported = false;
  139. if(wine.PathToWindows("/").empty()) supported = false;
  140. if(supported)
  141. {
  142. if(wine.HOME().empty()) supported = false;
  143. }
  144. if(supported)
  145. {
  146. if(wine.Uname_m() == "x86_64" && mpt::pointer_size != 8) supported = false;
  147. }
  148. return supported;
  149. }
  150. static std::map<std::string, std::vector<char> > UnzipToMap(mpt::PathString filename)
  151. {
  152. std::map<std::string, std::vector<char> > filetree;
  153. {
  154. zlib_filefunc64_def zipfilefuncs;
  155. MemsetZero(zipfilefuncs);
  156. fill_win32_filefunc64W(&zipfilefuncs);
  157. unzFile zipfile = unzOpen2_64(filename.ToWide().c_str(), &zipfilefuncs);
  158. if(!zipfile)
  159. {
  160. throw mpt::Wine::Exception("Archive is not a zip file");
  161. }
  162. for(int status = unzGoToFirstFile(zipfile); status == UNZ_OK; status = unzGoToNextFile(zipfile))
  163. {
  164. int openstatus = UNZ_OK;
  165. openstatus = unzOpenCurrentFile(zipfile);
  166. if(openstatus != UNZ_OK)
  167. {
  168. unzClose(zipfile);
  169. throw mpt::Wine::Exception("Archive is corrupted.");
  170. }
  171. unz_file_info info;
  172. MemsetZero(info);
  173. char name[1024];
  174. MemsetZero(name);
  175. openstatus = unzGetCurrentFileInfo(zipfile, &info, name, sizeof(name) - 1, nullptr, 0, nullptr, 0);
  176. if(openstatus != UNZ_OK)
  177. {
  178. unzCloseCurrentFile(zipfile);
  179. unzClose(zipfile);
  180. throw mpt::Wine::Exception("Archive is corrupted.");
  181. }
  182. std::vector<char> data(info.uncompressed_size);
  183. unzReadCurrentFile(zipfile, &data[0], info.uncompressed_size);
  184. unzCloseCurrentFile(zipfile);
  185. data = mpt::buffer_cast<std::vector<char>>(mpt::replace(mpt::buffer_cast<std::string>(data), std::string("\r\n"), std::string("\n")));
  186. filetree[mpt::replace(mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::CP437, name), std::string("\\"), std::string("/"))] = data;
  187. }
  188. unzClose(zipfile);
  189. }
  190. return filetree;
  191. }
  192. bool IsSupported()
  193. {
  194. return theApp.GetWine() ? true : false;
  195. }
  196. bool IsCompiled()
  197. {
  198. return !theApp.GetWineWrapperDllFilename().empty();
  199. }
  200. void Initialize()
  201. {
  202. if(!mpt::OS::Windows::IsWine())
  203. {
  204. return;
  205. }
  206. mpt::ustring lf = U_("\n");
  207. if(!TrackerSettings::Instance().WineSupportEnabled)
  208. {
  209. return;
  210. }
  211. mpt::OS::Wine::VersionContext wineVersion = *theApp.GetWineVersion();
  212. if(!WineSetupIsSupported(wineVersion))
  213. {
  214. mpt::ustring msg;
  215. msg += U_("OpenMPT does not support Wine integration on your current Wine setup.") + lf;
  216. Reporting::Notification(msg, WineGetWindowTitle());
  217. return;
  218. }
  219. try
  220. {
  221. mpt::Wine::Context wine = mpt::Wine::Context(wineVersion);
  222. if(!WineSetupIsSupported(wine))
  223. {
  224. mpt::ustring msg;
  225. msg += U_("OpenMPT does not support Wine integration on your current Wine setup.") + lf;
  226. Reporting::Notification(msg, WineGetWindowTitle());
  227. return;
  228. }
  229. theApp.SetWine(std::make_shared<mpt::Wine::Context>(wine));
  230. } catch(const std::exception & e)
  231. {
  232. mpt::ustring msg;
  233. msg += U_("OpenMPT was not able to determine Wine configuration details on your current Wine setup:") + lf;
  234. msg += mpt::get_exception_text<mpt::ustring>(e) + lf;
  235. msg += U_("OpenMPT native Wine Integration will not be available.") + lf;
  236. Reporting::Error(msg, WineGetWindowTitle());
  237. return;
  238. }
  239. mpt::Wine::Context wine = *theApp.GetWine();
  240. try
  241. {
  242. struct Paths
  243. {
  244. mpt::PathString AppData;
  245. mpt::PathString AppData_Wine;
  246. mpt::PathString AppData_Wine_WineVersion;
  247. mpt::PathString AppData_Wine_WineVersion_OpenMPTVersion;
  248. std::string Host_AppData;
  249. std::string Host_AppData_Wine;
  250. std::string Host_AppData_Wine_WineVersion;
  251. std::string Host_AppData_Wine_WineVersion_OpenMPTVersion;
  252. std::string Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion;
  253. static void CreatePath(mpt::PathString path)
  254. {
  255. if(path.IsDirectory())
  256. {
  257. return;
  258. }
  259. if(CreateDirectory(path.AsNative().c_str(), NULL) == 0)
  260. {
  261. throw mpt::Wine::Exception(std::string() + "Failed to create directory: " + path.ToUTF8());
  262. }
  263. }
  264. std::string GetOpenMPTVersion() const
  265. {
  266. std::string ver;
  267. ver += mpt::ToCharset(mpt::Charset::UTF8, Build::GetVersionStringPure() + U_("_") + mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()));
  268. mpt::crc64_jones crc;
  269. crc = WineHashVersion(crc);
  270. crc = WineHashFile(crc, theApp.GetInstallPath() + WineGetSupportZipFilename());
  271. crc = WineHashSettings(crc);
  272. ver += std::string("-") + mpt::afmt::hex0<16>(crc.result());
  273. return ver;
  274. }
  275. Paths(mpt::Wine::Context & wine)
  276. {
  277. AppData = theApp.GetConfigPath().WithoutTrailingSlash();
  278. AppData_Wine = AppData.WithTrailingSlash() + P_("Wine");
  279. AppData_Wine_WineVersion = AppData_Wine.WithTrailingSlash() + mpt::PathString::FromUTF8(SanitizeBuildID(wine.VersionContext().RawBuildID()));
  280. AppData_Wine_WineVersion_OpenMPTVersion = AppData_Wine_WineVersion.WithTrailingSlash() + mpt::PathString::FromUTF8(GetOpenMPTVersion());
  281. CreatePath(AppData);
  282. CreatePath(AppData_Wine);
  283. CreatePath(AppData_Wine_WineVersion);
  284. CreatePath(AppData_Wine_WineVersion_OpenMPTVersion);
  285. Host_AppData = wine.PathToPosixCanonical(AppData);
  286. Host_AppData_Wine = wine.PathToPosixCanonical(AppData_Wine);
  287. Host_AppData_Wine_WineVersion = wine.PathToPosixCanonical(AppData_Wine_WineVersion);
  288. Host_AppData_Wine_WineVersion_OpenMPTVersion = wine.PathToPosixCanonical(AppData_Wine_WineVersion_OpenMPTVersion);
  289. Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion = wine.XDG_DATA_HOME() + "/OpenMPT/Wine/" + SanitizeBuildID(wine.VersionContext().RawBuildID()) + "/" + GetOpenMPTVersion();
  290. }
  291. };
  292. const Paths paths(wine);
  293. const std::string nativeSearchPath = paths.Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion;
  294. if(!TrackerSettings::Instance().WineSupportAlwaysRecompile)
  295. {
  296. if((paths.AppData_Wine_WineVersion_OpenMPTVersion.WithTrailingSlash() + P_("success.txt")).IsFile())
  297. {
  298. theApp.SetWineWrapperDllFilename(paths.AppData_Wine_WineVersion_OpenMPTVersion.WithTrailingSlash() + P_("openmpt_wine_wrapper.dll"));
  299. return;
  300. }
  301. }
  302. if(TrackerSettings::Instance().WineSupportAskCompile)
  303. {
  304. mpt::ustring msg;
  305. msg += U_("OpenMPT Wine integration requires recompilation and will not work otherwise.\n");
  306. msg += U_("Recompile now?\n");
  307. if(Reporting::Confirm(msg, WineGetWindowTitle(), false, false) != cnfYes)
  308. {
  309. return;
  310. }
  311. }
  312. std::map<std::string, std::vector<char> > filetree;
  313. filetree = UnzipToMap(theApp.GetInstallPath() + WineGetSupportZipFilename());
  314. Util::Wine::Dialog dialog(WineGetWindowTitleUTF8(), TrackerSettings::Instance().WineSupportCompileVerbosity < 6);
  315. std::string script;
  316. script += std::string() + "#!/usr/bin/env sh" + "\n";
  317. script += std::string() + "\n";
  318. script += std::string() + "touch message.txt" + "\n";
  319. script += std::string() + "\n";
  320. script += dialog.Detect();
  321. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 6)
  322. {
  323. script += std::string() + "echo Working directory:" + "\n";
  324. script += std::string() + "pwd" + "\n";
  325. }
  326. script += std::string() + "\n";
  327. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 3)
  328. {
  329. script += std::string() + "echo " + WineGetWindowTitleUTF8() + "\n";
  330. } else
  331. {
  332. if(TrackerSettings::Instance().WineSupportCompileVerbosity == 2)
  333. {
  334. script += std::string() + "{" + "\n";
  335. script += std::string() + " echo 0" + "\n";
  336. script += std::string() + " echo 100" + "\n";
  337. 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";
  338. } else
  339. {
  340. script += std::string() + dialog.Status("Preparing OpenMPT Wine Integration.") + "\n";
  341. }
  342. }
  343. script += std::string() + "\n";
  344. script += std::string() + "printf \"#pragma once\\n\" >> common/svn_version.h" + "\n";
  345. script += std::string() + "printf \"#define OPENMPT_VERSION_URL \\\"" + mpt::ToCharset(mpt::Charset::ASCII, SourceInfo::Current().Url()) + "\\\"\\n\" >> common/svn_version.h" + "\n";
  346. script += std::string() + "printf \"#define OPENMPT_VERSION_DATE \\\"" + mpt::ToCharset(mpt::Charset::ASCII, SourceInfo::Current().Date()) + "\\\"\\n\" >> common/svn_version.h" + "\n";
  347. script += std::string() + "printf \"#define OPENMPT_VERSION_REVISION " + mpt::afmt::dec(SourceInfo::Current().Revision()) + "\\n\" >> common/svn_version.h" + "\n";
  348. script += std::string() + "printf \"#define OPENMPT_VERSION_DIRTY " + mpt::afmt::dec(SourceInfo::Current().IsDirty()) + "\\n\" >> common/svn_version.h" + "\n";
  349. script += std::string() + "printf \"#define OPENMPT_VERSION_MIXEDREVISIONS " + mpt::afmt::dec(SourceInfo::Current().HasMixedRevisions()) + "\\n\" >> common/svn_version.h" + "\n";
  350. script += std::string() + "printf \"#define OPENMPT_VERSION_IS_PACKAGE " + mpt::afmt::dec(SourceInfo::Current().IsPackage()) + "\\n\" >> common/svn_version.h" + "\n";
  351. script += std::string() + "\n";
  352. script += std::string() + "missing=" + "\n";
  353. script += std::string() + "\n";
  354. const std::string make = ((wineVersion.HostClass() == mpt::osinfo::osclass::BSD) ? "gmake" : "make");
  355. std::vector<std::string> commands;
  356. commands.push_back(make);
  357. commands.push_back("pkg-config");
  358. commands.push_back("cpp");
  359. commands.push_back("cc");
  360. commands.push_back("c++");
  361. commands.push_back("ld");
  362. commands.push_back("ccache");
  363. for(const auto &command : commands)
  364. {
  365. script += std::string() + "command -v " + command + " 2>/dev/null 1>/dev/null" + "\n";
  366. script += std::string() + "if [ \"$?\" -ne \"0\" ] ; then" + "\n";
  367. script += std::string() + " missing=\"$missing " + command + "\"" + "\n";
  368. script += std::string() + "fi" + "\n";
  369. }
  370. script += std::string() + "if [ \"x$missing\" = \"x\" ] ; then" + "\n";
  371. script += std::string() + " printf \"\"" + "\n";
  372. script += std::string() + "else" + "\n";
  373. #if 0
  374. if(!TrackerSettings::Instance().WineSupportSilentCompile >= 1)
  375. {
  376. script += std::string() + " " + dialog.YesNo("The following commands are missing:\\n\\n$missing\\n\\nDo you want OpenMPT to try installing those now?") + "\n";
  377. }
  378. script += std::string() + " if [ \"$?\" -ne \"0\" ] ; then" + "\n";
  379. script += std::string() + " exit 1" + "\n";
  380. script += std::string() + " fi" + "\n";
  381. #else
  382. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  383. {
  384. script += std::string() + " " + dialog.MessageBox("The following commands are missing:\\n\\n$missing\\n\\nPlease install them with your system package installer.") + "\n";
  385. }
  386. script += std::string() + " exit 1" + "\n";
  387. #endif
  388. script += std::string() + "fi" + "\n";
  389. script += std::string() + "\n";
  390. script += std::string() + "mkdir -p " + wine.EscapePosixShell(wine.XDG_DATA_HOME()) + "/OpenMPT/Wine" + "\n";
  391. script += std::string() + "mkdir -p " + wine.EscapePosixShell(wine.XDG_CACHE_HOME()) + "/OpenMPT/Wine" + "\n";
  392. script += std::string() + "mkdir -p " + wine.EscapePosixShell(wine.XDG_CONFIG_HOME()) + "/OpenMPT/Wine" + "\n";
  393. script += std::string() + "mkdir -p " + wine.EscapePosixShell(paths.Host_Native_OpenMPT_Wine_WineVersion_OpenMPTVersion) + "\n";
  394. script += std::string() + "\n";
  395. script += std::string() + "CCACHE_DIR=" + wine.EscapePosixShell(wine.XDG_CACHE_HOME()) + "/OpenMPT/Wine/ccache" + "\n";
  396. script += std::string() + "CCACHE_COMPRESS=1" + " \n";
  397. script += std::string() + "export CCACHE_DIR" + " \n";
  398. script += std::string() + "export CCACHE_COMPRESS" + " \n";
  399. script += std::string() + "\n";
  400. std::vector<std::string> winegcc;
  401. if constexpr(mpt::arch_bits == 32)
  402. { // 32bit winegcc probably cannot compile to 64bit
  403. winegcc.push_back("winegcc32-development");
  404. }
  405. MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64))
  406. {
  407. winegcc.push_back("winegcc64-development");
  408. }
  409. winegcc.push_back("winegcc-development");
  410. if(wineVersion.HostClass() != mpt::osinfo::osclass::BSD)
  411. { // avoid C++ compiler on *BSD because libc++ Win32 support tends to be missing there.
  412. if constexpr(mpt::arch_bits == 32)
  413. { // 32bit winegcc probably cannot compile to 64bit
  414. winegcc.push_back("wineg++32-development");
  415. }
  416. MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64))
  417. {
  418. winegcc.push_back("wineg++64-development");
  419. }
  420. winegcc.push_back("wineg++-development");
  421. }
  422. if constexpr(mpt::arch_bits == 32)
  423. { // 32bit winegcc probably cannot compile to 64bit
  424. winegcc.push_back("winegcc32");
  425. }
  426. MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64))
  427. {
  428. winegcc.push_back("winegcc64");
  429. }
  430. winegcc.push_back("winegcc");
  431. if(wineVersion.HostClass() != mpt::osinfo::osclass::BSD)
  432. { // avoid C++ compiler on *BSD because libc++ Win32 support tends to be missing there.
  433. if constexpr(mpt::arch_bits == 32)
  434. { // 32bit winegcc probably cannot compile to 64bit
  435. winegcc.push_back("wineg++32");
  436. }
  437. MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64))
  438. {
  439. winegcc.push_back("wineg++64");
  440. }
  441. winegcc.push_back("wineg++");
  442. }
  443. for(const auto &c : winegcc)
  444. {
  445. script += std::string() + "if command -v " + c + " 2>/dev/null 1>/dev/null ; then" + "\n";
  446. script += std::string() + " MPT_WINEGXX=" + c + "\n";
  447. script += std::string() + "fi" + "\n";
  448. }
  449. script += std::string() + "if [ -z $MPT_WINEGXX ] ; then" + "\n";
  450. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  451. {
  452. script += std::string() + " " + dialog.MessageBox("WineGCC not found.\\nPlease install it with your system package installer.") + "\n";
  453. }
  454. script += std::string() + " exit 1" + "\n";
  455. script += std::string() + "fi" + "\n";
  456. // Work-around for Debian 8, Wine 1.6.2
  457. MPT_MAYBE_CONSTANT_IF(TrackerSettings::Instance().WineSupportForeignOpenMPT || (mpt::arch_bits == 64))
  458. {
  459. script += std::string() + "if [ `$MPT_WINEGXX > /dev/null 2>&1 ; echo $?` -eq 127 ] ; then" + "\n";
  460. script += std::string() + " if command -v /usr/lib/x86_64-linux-gnu/wine/bin/winegcc 2>/dev/null 1>/dev/null ; then" + "\n";
  461. script += std::string() + " MPT_WINEGXX=/usr/lib/x86_64-linux-gnu/wine/bin/winegcc"+ "\n";
  462. script += std::string() + " PATH=/usr/lib/x86_64-linux-gnu/wine/bin:\"${PATH}\"" + "\n";
  463. script += std::string() + " export PATH" + "\n";
  464. script += std::string() + " fi" + "\n";
  465. script += std::string() + "fi" + "\n";
  466. }
  467. if constexpr(mpt::arch_bits == 32)
  468. {
  469. script += std::string() + "if [ `$MPT_WINEGXX > /dev/null 2>&1 ; echo $?` -eq 127 ] ; then" + "\n";
  470. script += std::string() + " if command -v /usr/lib/i386-linux-gnu/wine/bin/winegcc 2>/dev/null 1>/dev/null ; then" + "\n";
  471. script += std::string() + " MPT_WINEGXX=/usr/lib/i386-linux-gnu/wine/bin/winegcc" + "\n";
  472. script += std::string() + " PATH=/usr/lib/i386-linux-gnu/wine/bin:\"${PATH}\"" + "\n";
  473. script += std::string() + " export PATH" + "\n";
  474. script += std::string() + " fi" + "\n";
  475. script += std::string() + "fi" + "\n";
  476. }
  477. std::string features;
  478. if(TrackerSettings::Instance().WineSupportForeignOpenMPT)
  479. {
  480. features += std::string() + " " + "MPT_ARCH_BITS=" + mpt::afmt::dec(mpt::arch_bits);
  481. if constexpr(mpt::arch_bits == 64)
  482. {
  483. features += std::string() + " " + "MPT_TARGET=" + "x86_64-linux-gnu-";
  484. } else
  485. {
  486. features += std::string() + " " + "MPT_TARGET=" + "i686-linux-gnu-";
  487. }
  488. }
  489. features += std::string() + " " + "MPT_TRY_PORTAUDIO=" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePortAudio.Get());
  490. features += std::string() + " " + "MPT_TRY_PULSEAUDIO=" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnablePulseAudio.Get());
  491. features += std::string() + " " + "MPT_TRY_RTAUDIO=" + mpt::afmt::dec(TrackerSettings::Instance().WineSupportEnableRtAudio.Get());
  492. int makeverbosity = Clamp(TrackerSettings::Instance().WineSupportCompileVerbosity.Get(), 0, 6);
  493. if(TrackerSettings::Instance().WineSupportCompileVerbosity == 2)
  494. {
  495. script += std::string() + "{" + "\n";
  496. script += std::string() + " echo 0" + "\n";
  497. script += std::string() + " " + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast<unsigned int>(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";
  498. script += std::string() + " echo -n $? > stdexit.txt" + "\n";
  499. script += std::string() + " echo 100" + "\n";
  500. 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";
  501. script += std::string() + "MPT_EXITCODE=`cat stdexit.txt`" + "\n";
  502. script += std::string() + "if [ \"$MPT_EXITCODE\" -ne \"0\" ] ; then" + "\n";
  503. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  504. {
  505. script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n";
  506. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  507. }
  508. script += std::string() + " exit 1" + "\n";
  509. script += std::string() + "fi" + "\n";
  510. script += std::string() + "if [ -s stderr.txt ] ; then" + "\n";
  511. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  512. script += std::string() + "fi" + "\n";
  513. script += std::string() + "{" + "\n";
  514. script += std::string() + " echo 0" + "\n";
  515. script += std::string() + " " + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast<unsigned int>(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";
  516. script += std::string() + " echo -n $? > stdexit.txt" + "\n";
  517. script += std::string() + " echo 100" + "\n";
  518. 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";
  519. script += std::string() + "MPT_EXITCODE=`cat stdexit.txt`" + "\n";
  520. script += std::string() + "if [ \"$MPT_EXITCODE\" -ne \"0\" ] ; then" + "\n";
  521. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  522. {
  523. script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n";
  524. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  525. }
  526. script += std::string() + " exit 1" + "\n";
  527. script += std::string() + "fi" + "\n";
  528. script += std::string() + "if [ -s stderr.txt ] ; then" + "\n";
  529. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  530. script += std::string() + "fi" + "\n";
  531. } else
  532. {
  533. script += std::string() + "" + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast<unsigned int>(1))) + " -f build/wine/native_support.mk" + " V=" + mpt::afmt::dec(makeverbosity) + " " + features + " all" + "\n";
  534. script += std::string() + "if [ \"$?\" -ne \"0\" ] ; then" + "\n";
  535. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  536. {
  537. script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n";
  538. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  539. }
  540. script += std::string() + " exit 1" + "\n";
  541. script += std::string() + "fi" + "\n";
  542. script += std::string() + "if [ -s stderr.txt ] ; then" + "\n";
  543. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  544. script += std::string() + "fi" + "\n";
  545. script += std::string() + "" + make + " -j " + mpt::afmt::dec(std::max(std::thread::hardware_concurrency(), static_cast<unsigned int>(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";
  546. script += std::string() + "if [ \"$?\" -ne \"0\" ] ; then" + "\n";
  547. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  548. {
  549. script += std::string() + " " + dialog.MessageBox("OpenMPT Wine integration failed to compile.") + "\n";
  550. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  551. }
  552. script += std::string() + " exit 1" + "\n";
  553. script += std::string() + "fi" + "\n";
  554. script += std::string() + "if [ -s stderr.txt ] ; then" + "\n";
  555. script += std::string() + " " + dialog.TextBox("stderr.txt") + "\n";
  556. script += std::string() + "fi" + "\n";
  557. }
  558. script += std::string() + "\n";
  559. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 6)
  560. {
  561. script += std::string() + dialog.MessageBox("OpenMPT Wine integration compiled successfully.") + "\n";
  562. }
  563. script += std::string() + "\n";
  564. script += "exit 0" "\n";
  565. CMainFrame::GetMainFrame()->EnableWindow(FALSE);
  566. mpt::Wine::ExecResult result;
  567. try
  568. {
  569. FlagSet<mpt::Wine::ExecFlags> flags = mpt::Wine::ExecFlagNone;
  570. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 1)
  571. {
  572. flags = (mpt::Wine::ExecFlagProgressWindow | mpt::Wine::ExecFlagInteractive);
  573. } else if(TrackerSettings::Instance().WineSupportCompileVerbosity == 0)
  574. {
  575. flags = (mpt::Wine::ExecFlagProgressWindow | mpt::Wine::ExecFlagSilent);
  576. } else
  577. {
  578. flags = (mpt::Wine::ExecFlagSilent);
  579. }
  580. result = Util::Wine::ExecutePosixShellScript
  581. ( wine
  582. , script
  583. , flags
  584. , filetree
  585. , WineGetWindowTitleUTF8()
  586. , "Compiling Wine support ..."
  587. );
  588. } catch(const mpt::Wine::Exception & /* e */ )
  589. {
  590. CMainFrame::GetMainFrame()->EnableWindow(TRUE);
  591. throw;
  592. }
  593. CMainFrame::GetMainFrame()->EnableWindow(TRUE);
  594. if(result.exitcode != 0)
  595. {
  596. if(result.filetree["message.txt"].size() > 0)
  597. {
  598. throw mpt::Wine::Exception(std::string(result.filetree["message.txt"].begin(), result.filetree["message.txt"].end()));
  599. } else
  600. {
  601. throw mpt::Wine::Exception("Executing Wine integration build script failed.");
  602. }
  603. }
  604. {
  605. std::string fn = "libopenmpt_native_support.so";
  606. mpt::ofstream f(wine.PathToWindows(nativeSearchPath) + P_("\\") + mpt::PathString::FromUTF8(fn), std::ios::binary);
  607. f.write(&result.filetree[fn][0], result.filetree[fn].size());
  608. f.flush();
  609. if(!f)
  610. {
  611. throw mpt::Wine::Exception("Writing libopenmpt_native_support.so failed.");
  612. }
  613. }
  614. {
  615. std::string fn = "openmpt_wine_wrapper.dll";
  616. mpt::ofstream f(paths.AppData_Wine_WineVersion_OpenMPTVersion + P_("\\") + mpt::PathString::FromUTF8(fn), std::ios::binary);
  617. f.write(&result.filetree[fn][0], result.filetree[fn].size());
  618. f.flush();
  619. if(!f)
  620. {
  621. throw mpt::Wine::Exception("Writing openmpt_wine_wrapper.dll failed.");
  622. }
  623. }
  624. {
  625. std::string fn = "success.txt";
  626. mpt::ofstream f(paths.AppData_Wine_WineVersion_OpenMPTVersion + P_("\\") + mpt::PathString::FromUTF8(fn), std::ios::binary);
  627. f.imbue(std::locale::classic());
  628. f << std::string("1");
  629. f.flush();
  630. if(!f)
  631. {
  632. throw mpt::Wine::Exception("Writing success.txt failed.");
  633. }
  634. }
  635. theApp.SetWineWrapperDllFilename(paths.AppData_Wine_WineVersion_OpenMPTVersion + P_("\\") + P_("openmpt_wine_wrapper.dll"));
  636. } catch(const mpt::Wine::Exception &e)
  637. {
  638. Reporting::Error(U_("Setting up OpenMPT Wine integration failed: ") + mpt::get_exception_text<mpt::ustring>(e), WineGetWindowTitle());
  639. }
  640. }
  641. } // namespace WineIntegration
  642. std::string ComponentWineWrapper::result_as_string(char * str) const
  643. {
  644. std::string result = str;
  645. OpenMPT_Wine_Wrapper_String_Free(str);
  646. return result;
  647. }
  648. ComponentWineWrapper::ComponentWineWrapper()
  649. : ComponentLibrary(ComponentTypeBundled)
  650. {
  651. return;
  652. }
  653. bool ComponentWineWrapper::DoInitialize()
  654. {
  655. if(theApp.GetWineWrapperDllFilename().empty())
  656. {
  657. return false;
  658. }
  659. AddLibrary("WineWrapper", mpt::LibraryPath::FullPath(theApp.GetWineWrapperDllFilename()));
  660. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_Init);
  661. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_Fini);
  662. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_String_Free);
  663. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_EnumerateDevices);
  664. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Construct);
  665. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Destruct);
  666. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_SetMessageReceiver);
  667. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_SetCallback);
  668. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetDeviceInfo);
  669. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetDeviceCaps);
  670. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetDeviceDynamicCaps);
  671. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Init);
  672. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Open);
  673. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Close);
  674. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Start);
  675. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_Stop);
  676. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetRequestFlags);
  677. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsInited);
  678. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsOpen);
  679. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsAvailable);
  680. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsPlaying);
  681. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_IsPlayingSilence);
  682. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_StopAndAvoidPlayingSilence);
  683. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_EndPlayingSilence);
  684. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_OnIdle);
  685. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetSettings);
  686. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetActualSampleFormat);
  687. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetEffectiveBufferAttributes);
  688. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetTimeInfo);
  689. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetStreamPosition);
  690. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_DebugIsFragileDevice);
  691. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_DebugInRealtimeCallback);
  692. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_GetStatistics);
  693. MPT_COMPONENT_BIND("WineWrapper", OpenMPT_Wine_Wrapper_SoundDevice_OpenDriverSettings);
  694. if(HasBindFailed())
  695. {
  696. Reporting::Error("OpenMPT Wine integration failed loading.", WineGetWindowTitle());
  697. return false;
  698. }
  699. if(OpenMPT_Wine_Wrapper_Init() != 0)
  700. {
  701. Reporting::Error("OpenMPT Wine integration initialization failed.", WineGetWindowTitle());
  702. return false;
  703. }
  704. if(TrackerSettings::Instance().WineSupportCompileVerbosity >= 6)
  705. {
  706. Reporting::Notification(MPT_AFORMAT("OpenMPT Wine integration loaded successfully.")(), WineGetWindowTitle());
  707. }
  708. return true;
  709. }
  710. ComponentWineWrapper::~ComponentWineWrapper()
  711. {
  712. if(IsAvailable())
  713. {
  714. OpenMPT_Wine_Wrapper_Fini();
  715. }
  716. }
  717. namespace WineIntegration {
  718. void Load()
  719. {
  720. ReloadComponent<ComponentWineWrapper>();
  721. }
  722. } // namespace WineIntegration
  723. OPENMPT_NAMESPACE_END