Mptrack.cpp 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413
  1. /*
  2. * MPTrack.cpp
  3. * -----------
  4. * Purpose: OpenMPT core application class.
  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. #include "Mptrack.h"
  11. #include "Mainfrm.h"
  12. #include "IPCWindow.h"
  13. #include "InputHandler.h"
  14. #include "Childfrm.h"
  15. #include "Moddoc.h"
  16. #include "ModDocTemplate.h"
  17. #include "Globals.h"
  18. #include "../soundlib/Dlsbank.h"
  19. #include "../common/version.h"
  20. #include "../test/test.h"
  21. #include "UpdateCheck.h"
  22. #include "../common/mptStringBuffer.h"
  23. #include "ExceptionHandler.h"
  24. #include "CloseMainDialog.h"
  25. #include "PlugNotFoundDlg.h"
  26. #include "AboutDialog.h"
  27. #include "AutoSaver.h"
  28. #include "FileDialog.h"
  29. #include "Image.h"
  30. #include "BuildVariants.h"
  31. #include "../common/ComponentManager.h"
  32. #include "WelcomeDialog.h"
  33. #include "openmpt/sounddevice/SoundDeviceManager.hpp"
  34. #include "WineSoundDeviceStub.h"
  35. #include "../soundlib/plugins/PluginManager.h"
  36. #include "MPTrackWine.h"
  37. #include "MPTrackUtil.h"
  38. #if MPT_MSVC_AT_LEAST(2022, 2) && MPT_MSVC_BEFORE(2022, 3)
  39. // Work-around <https://developercommunity.visualstudio.com/t/warning-C4311-in-MFC-header-afxrecovery/10041328>,
  40. // see <https://developercommunity.visualstudio.com/t/Compiler-warnings-after-upgrading-to-17/10036311#T-N10061908>.
  41. template <class ARG_KEY>
  42. AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key);
  43. template <>
  44. AFX_INLINE UINT AFXAPI HashKey<CDocument*>(CDocument *key)
  45. {
  46. // (algorithm copied from STL hash in xfunctional)
  47. #pragma warning(suppress: 4302) // 'type cast' : truncation
  48. #pragma warning(suppress: 4311) // pointer truncation
  49. ldiv_t HashVal = ldiv((long)(CDocument*)key, 127773);
  50. HashVal.rem = 16807 * HashVal.rem - 2836 * HashVal.quot;
  51. if(HashVal.rem < 0)
  52. HashVal.rem += 2147483647;
  53. return ((UINT)HashVal.rem);
  54. }
  55. #endif
  56. #include <afxdatarecovery.h>
  57. // GDI+
  58. #include <atlbase.h>
  59. #define max(a, b) (((a) > (b)) ? (a) : (b))
  60. #define min(a, b) (((a) < (b)) ? (a) : (b))
  61. #if MPT_COMPILER_MSVC
  62. #pragma warning(push)
  63. #pragma warning(disable : 4458) // declaration of 'x' hides class member
  64. #endif
  65. #include <gdiplus.h>
  66. #if MPT_COMPILER_MSVC
  67. #pragma warning(pop)
  68. #endif
  69. #undef min
  70. #undef max
  71. #if MPT_COMPILER_MSVC
  72. #define _CRTDBG_MAP_ALLOC
  73. #include <stdlib.h>
  74. #include <crtdbg.h>
  75. #endif
  76. OPENMPT_NAMESPACE_BEGIN
  77. /////////////////////////////////////////////////////////////////////////////
  78. // The one and only CTrackApp object
  79. CTrackApp theApp;
  80. const TCHAR *szSpecialNoteNamesMPT[] = {_T("PCs"), _T("PC"), _T("~~ (Note Fade)"), _T("^^ (Note Cut)"), _T("== (Note Off)")};
  81. const TCHAR *szSpecialNoteShortDesc[] = {_T("Param Control (Smooth)"), _T("Param Control"), _T("Note Fade"), _T("Note Cut"), _T("Note Off")};
  82. // Make sure that special note arrays include string for every note.
  83. static_assert(NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1 == mpt::array_size<decltype(szSpecialNoteNamesMPT)>::size);
  84. static_assert(mpt::array_size<decltype(szSpecialNoteShortDesc)>::size == mpt::array_size<decltype(szSpecialNoteNamesMPT)>::size);
  85. const char *szHexChar = "0123456789ABCDEF";
  86. #ifdef MPT_WITH_ASIO
  87. class ComponentASIO
  88. : public ComponentBuiltin
  89. {
  90. MPT_DECLARE_COMPONENT_MEMBERS(ComponentASIO, "ASIO")
  91. public:
  92. ComponentASIO() = default;
  93. virtual ~ComponentASIO() = default;
  94. };
  95. #endif // MPT_WITH_ASIO
  96. #if defined(MPT_WITH_DIRECTSOUND)
  97. class ComponentDirectSound
  98. : public ComponentBuiltin
  99. {
  100. MPT_DECLARE_COMPONENT_MEMBERS(ComponentDirectSound, "DirectSound")
  101. public:
  102. ComponentDirectSound() = default;
  103. virtual ~ComponentDirectSound() = default;
  104. };
  105. #endif // MPT_WITH_DIRECTSOUND
  106. #if defined(MPT_WITH_PORTAUDIO)
  107. class ComponentPortAudio
  108. : public ComponentBuiltin
  109. {
  110. MPT_DECLARE_COMPONENT_MEMBERS(ComponentPortAudio, "PortAudio")
  111. public:
  112. ComponentPortAudio() = default;
  113. virtual ~ComponentPortAudio() = default;
  114. };
  115. #endif // MPT_WITH_PORTAUDIO
  116. #if defined(MPT_WITH_PULSEAUDIO)
  117. class ComponentPulseaudio
  118. : public ComponentBuiltin
  119. {
  120. MPT_DECLARE_COMPONENT_MEMBERS(ComponentPulseaudio, "Pulseaudio")
  121. public:
  122. ComponentPulseaudio() = default;
  123. virtual ~ComponentPulseaudio() = default;
  124. };
  125. #endif // MPT_WITH_PULSEAUDIO
  126. #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_WITH_PULSEAUDIOSIMPLE)
  127. class ComponentPulseaudioSimple
  128. : public ComponentBuiltin
  129. {
  130. MPT_DECLARE_COMPONENT_MEMBERS(ComponentPulseaudioSimple, "PulseaudioSimple")
  131. public:
  132. ComponentPulseaudioSimple() = default;
  133. virtual ~ComponentPulseaudioSimple() = default;
  134. };
  135. #endif // MPT_WITH_PULSEAUDIO && MPT_WITH_PULSEAUDIOSIMPLE
  136. #if defined(MPT_WITH_RTAUDIO)
  137. class ComponentRtAudio
  138. : public ComponentBuiltin
  139. {
  140. MPT_DECLARE_COMPONENT_MEMBERS(ComponentRtAudio, "RtAudio")
  141. public:
  142. ComponentRtAudio() = default;
  143. virtual ~ComponentRtAudio() = default;
  144. };
  145. #endif // MPT_WITH_RTAUDIO
  146. #if MPT_OS_WINDOWS
  147. class ComponentWaveOut
  148. : public ComponentBuiltin
  149. {
  150. MPT_DECLARE_COMPONENT_MEMBERS(ComponentWaveOut, "WaveOut")
  151. public:
  152. ComponentWaveOut() = default;
  153. virtual ~ComponentWaveOut() = default;
  154. };
  155. #endif // MPT_OS_WINDOWS
  156. struct AllSoundDeviceComponents
  157. {
  158. #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_ENABLE_PULSEAUDIO_FULL)
  159. ComponentHandle<ComponentPulseaudio> m_Pulseaudio;
  160. #endif // MPT_WITH_PULSEAUDIO && MPT_ENABLE_PULSEAUDIO_FULL
  161. #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_WITH_PULSEAUDIOSIMPLE)
  162. ComponentHandle<ComponentPulseaudioSimple> m_PulseaudioSimple;
  163. #endif // MPT_WITH_PULSEAUDIO && MPT_WITH_PULSEAUDIOSIMPLE
  164. #if MPT_OS_WINDOWS
  165. ComponentHandle<ComponentWaveOut> m_WaveOut;
  166. #endif // MPT_OS_WINDOWS
  167. #if defined(MPT_WITH_DIRECTSOUND)
  168. ComponentHandle<ComponentDirectSound> m_DirectSound;
  169. #endif // MPT_WITH_DIRECTSOUND
  170. #ifdef MPT_WITH_ASIO
  171. ComponentHandle<ComponentASIO> m_ASIO;
  172. #endif // MPT_WITH_ASIO
  173. #ifdef MPT_WITH_PORTAUDIO
  174. ComponentHandle<ComponentPortAudio> m_PortAudio;
  175. #endif // MPT_WITH_PORTAUDIO
  176. #ifdef MPT_WITH_RTAUDIO
  177. ComponentHandle<ComponentRtAudio> m_RtAudio;
  178. #endif // MPT_WITH_RTAUDIO
  179. operator SoundDevice::EnabledBackends() const
  180. {
  181. SoundDevice::EnabledBackends result;
  182. #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_ENABLE_PULSEAUDIO_FULL)
  183. result.Pulseaudio = IsComponentAvailable(m_PulseAudio);
  184. #endif // MPT_WITH_PULSEAUDIO && MPT_ENABLE_PULSEAUDIO_FULL
  185. #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_WITH_PULSEAUDIOSIMPLE)
  186. result.PulseaudioSimple = IsComponentAvailable(m_PulseAudioSimple);
  187. #endif // MPT_WITH_PULSEAUDIO && MPT_WITH_PULSEAUDIOSIMPLE
  188. #if MPT_OS_WINDOWS
  189. result.WaveOut = IsComponentAvailable(m_WaveOut);
  190. #endif // MPT_OS_WINDOWS
  191. #if defined(MPT_WITH_DIRECTSOUND)
  192. result.DirectSound = IsComponentAvailable(m_DirectSound);
  193. #endif // MPT_WITH_DIRECTSOUND
  194. #ifdef MPT_WITH_ASIO
  195. result.ASIO = IsComponentAvailable(m_ASIO);
  196. #endif // MPT_WITH_ASIO
  197. #ifdef MPT_WITH_PORTAUDIO
  198. result.PortAudio = IsComponentAvailable(m_PortAudio);
  199. #endif // MPT_WITH_PORTAUDIO
  200. #ifdef MPT_WITH_RTAUDIO
  201. result.RtAudio = IsComponentAvailable(m_RtAudio);
  202. #endif // MPT_WITH_RTAUDIO
  203. return result;
  204. }
  205. };
  206. void CTrackApp::OnFileCloseAll()
  207. {
  208. if(!(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_NOCLOSEDIALOG))
  209. {
  210. // Show modified documents window
  211. CloseMainDialog dlg;
  212. if(dlg.DoModal() != IDOK)
  213. {
  214. return;
  215. }
  216. }
  217. for(auto &doc : GetOpenDocuments())
  218. {
  219. doc->SafeFileClose();
  220. }
  221. }
  222. void CTrackApp::OnUpdateAnyDocsOpen(CCmdUI *cmd)
  223. {
  224. cmd->Enable(!GetModDocTemplate()->empty());
  225. }
  226. int CTrackApp::GetOpenDocumentCount() const
  227. {
  228. return static_cast<int>(GetModDocTemplate()->size());
  229. }
  230. // Retrieve a list of all open modules.
  231. std::vector<CModDoc *> CTrackApp::GetOpenDocuments() const
  232. {
  233. std::vector<CModDoc *> documents;
  234. if(auto *pDocTmpl = GetModDocTemplate())
  235. {
  236. POSITION pos = pDocTmpl->GetFirstDocPosition();
  237. CDocument *pDoc;
  238. while((pos != nullptr) && ((pDoc = pDocTmpl->GetNextDoc(pos)) != nullptr))
  239. {
  240. documents.push_back(dynamic_cast<CModDoc *>(pDoc));
  241. }
  242. }
  243. return documents;
  244. }
  245. /////////////////////////////////////////////////////////////////////////////
  246. // Command Line options
  247. class CMPTCommandLineInfo: public CCommandLineInfo
  248. {
  249. public:
  250. std::vector<mpt::PathString> m_fileNames;
  251. bool m_noDls = false, m_noPlugins = false, m_noAssembly = false, m_noSysCheck = false, m_noWine = false,
  252. m_portable = false, m_noCrashHandler = false, m_debugCrashHandler = false, m_sharedInstance = false;
  253. #ifdef ENABLE_TESTS
  254. bool m_noTests = false;
  255. #endif
  256. public:
  257. void ParseParam(LPCTSTR param, BOOL isFlag, BOOL isLast) override
  258. {
  259. if(isFlag)
  260. {
  261. if(!lstrcmpi(param, _T("nologo"))) { m_bShowSplash = FALSE; return; }
  262. if(!lstrcmpi(param, _T("nodls"))) { m_noDls = true; return; }
  263. if(!lstrcmpi(param, _T("noplugs"))) { m_noPlugins = true; return; }
  264. if(!lstrcmpi(param, _T("portable"))) { m_portable = true; return; }
  265. if(!lstrcmpi(param, _T("fullMemDump"))) { ExceptionHandler::fullMemDump = true; return; }
  266. if(!lstrcmpi(param, _T("noAssembly"))) { m_noAssembly = true; return; }
  267. if(!lstrcmpi(param, _T("noSysCheck"))) { m_noSysCheck = true; return; }
  268. if(!lstrcmpi(param, _T("noWine"))) { m_noWine = true; return; }
  269. if(!lstrcmpi(param, _T("noCrashHandler"))) { m_noCrashHandler = true; return; }
  270. if(!lstrcmpi(param, _T("DebugCrashHandler"))) { m_debugCrashHandler = true; return; }
  271. if(!lstrcmpi(param, _T("shared"))) { m_sharedInstance = true; return; }
  272. #ifdef ENABLE_TESTS
  273. if (!lstrcmpi(param, _T("noTests"))) { m_noTests = true; return; }
  274. #endif
  275. } else
  276. {
  277. m_fileNames.push_back(mpt::PathString::FromNative(param));
  278. if(m_nShellCommand == FileNew) m_nShellCommand = FileOpen;
  279. }
  280. CCommandLineInfo::ParseParam(param, isFlag, isLast);
  281. }
  282. };
  283. // Splash Screen
  284. static void StartSplashScreen();
  285. static void StopSplashScreen();
  286. static void TimeoutSplashScreen();
  287. /////////////////////////////////////////////////////////////////////////////
  288. // Midi Library
  289. MidiLibrary CTrackApp::midiLibrary;
  290. void CTrackApp::ImportMidiConfig(const mpt::PathString &filename, bool hideWarning)
  291. {
  292. if(filename.empty()) return;
  293. if(CDLSBank::IsDLSBank(filename))
  294. {
  295. ConfirmAnswer result = cnfYes;
  296. if(!hideWarning)
  297. {
  298. result = Reporting::Confirm("You are about to replace the current MIDI library:\n"
  299. "Do you want to replace only the missing instruments? (recommended)",
  300. "Warning", true);
  301. }
  302. if(result == cnfCancel) return;
  303. const bool replaceAll = (result == cnfNo);
  304. CDLSBank dlsbank;
  305. if (dlsbank.Open(filename))
  306. {
  307. for(uint32 ins = 0; ins < 256; ins++)
  308. {
  309. if(replaceAll || midiLibrary[ins].empty())
  310. {
  311. uint32 prog = (ins < 128) ? ins : 0xFF;
  312. uint32 key = (ins < 128) ? 0xFF : ins & 0x7F;
  313. uint32 bank = (ins < 128) ? 0 : F_INSTRUMENT_DRUMS;
  314. if (dlsbank.FindInstrument(ins >= 128, bank, prog, key))
  315. {
  316. midiLibrary[ins] = filename;
  317. }
  318. }
  319. }
  320. }
  321. return;
  322. }
  323. IniFileSettingsContainer file(filename);
  324. ImportMidiConfig(file, filename.GetPath());
  325. }
  326. static mpt::PathString GetUltraSoundPatchDir(SettingsContainer &file, const mpt::ustring &iniSection, const mpt::PathString &path, bool forgetSettings)
  327. {
  328. mpt::PathString patchDir = file.Read<mpt::PathString>(iniSection, U_("PatchDir"), {});
  329. if(forgetSettings)
  330. file.Forget(U_("Ultrasound"), U_("PatchDir"));
  331. if(patchDir.empty() || patchDir == P_(".\\"))
  332. patchDir = path;
  333. if(!patchDir.empty())
  334. patchDir.EnsureTrailingSlash();
  335. return patchDir;
  336. }
  337. void CTrackApp::ImportMidiConfig(SettingsContainer &file, const mpt::PathString &path, bool forgetSettings)
  338. {
  339. const mpt::PathString patchDir = GetUltraSoundPatchDir(file, U_("Ultrasound"), path, forgetSettings);
  340. for(uint32 prog = 0; prog < 256; prog++)
  341. {
  342. mpt::ustring key = MPT_UFORMAT("{}{}")((prog < 128) ? U_("Midi") : U_("Perc"), prog & 0x7F);
  343. mpt::PathString filename = file.Read<mpt::PathString>(U_("Midi Library"), key, mpt::PathString());
  344. // Check for ULTRASND.INI
  345. if(filename.empty())
  346. {
  347. mpt::ustring section = (prog < 128) ? UL_("Melodic Patches") : UL_("Drum Patches");
  348. key = mpt::ufmt::val(prog & 0x7f);
  349. filename = file.Read<mpt::PathString>(section, key, mpt::PathString());
  350. if(forgetSettings) file.Forget(section, key);
  351. if(filename.empty())
  352. {
  353. section = (prog < 128) ? UL_("Melodic Bank 0") : UL_("Drum Bank 0");
  354. filename = file.Read<mpt::PathString>(section, key, mpt::PathString());
  355. if(forgetSettings) file.Forget(section, key);
  356. }
  357. const mpt::PathString localPatchDir = GetUltraSoundPatchDir(file, section, patchDir, forgetSettings);
  358. if(!filename.empty())
  359. {
  360. filename = localPatchDir + filename + P_(".pat");
  361. }
  362. }
  363. if(!filename.empty())
  364. {
  365. filename = theApp.PathInstallRelativeToAbsolute(filename);
  366. midiLibrary[prog] = filename;
  367. }
  368. }
  369. }
  370. void CTrackApp::ExportMidiConfig(const mpt::PathString &filename)
  371. {
  372. if(filename.empty()) return;
  373. IniFileSettingsContainer file(filename);
  374. ExportMidiConfig(file);
  375. }
  376. void CTrackApp::ExportMidiConfig(SettingsContainer &file)
  377. {
  378. for(uint32 prog = 0; prog < 256; prog++) if (!midiLibrary[prog].empty())
  379. {
  380. mpt::PathString szFileName = midiLibrary[prog];
  381. if(!szFileName.empty())
  382. {
  383. if(theApp.IsPortableMode())
  384. szFileName = theApp.PathAbsoluteToInstallRelative(szFileName);
  385. mpt::ustring key = MPT_UFORMAT("{}{}")((prog < 128) ? U_("Midi") : U_("Perc"), prog & 0x7F);
  386. file.Write<mpt::PathString>(U_("Midi Library"), key, szFileName);
  387. }
  388. }
  389. }
  390. /////////////////////////////////////////////////////////////////////////////
  391. // DLS Banks support
  392. std::vector<std::unique_ptr<CDLSBank>> CTrackApp::gpDLSBanks;
  393. struct CompareLessPathStringNoCase
  394. {
  395. inline bool operator()(const mpt::PathString &l, const mpt::PathString &r) const
  396. {
  397. return mpt::PathString::CompareNoCase(l, r) < 0;
  398. }
  399. };
  400. std::future<std::vector<std::unique_ptr<CDLSBank>>> CTrackApp::LoadDefaultDLSBanks()
  401. {
  402. std::set<mpt::PathString, CompareLessPathStringNoCase> paths;
  403. uint32 numBanks = theApp.GetSettings().Read<uint32>(U_("DLS Banks"), U_("NumBanks"), 0);
  404. for(uint32 i = 0; i < numBanks; i++)
  405. {
  406. mpt::PathString path = theApp.GetSettings().Read<mpt::PathString>(U_("DLS Banks"), MPT_UFORMAT("Bank{}")(i + 1), mpt::PathString());
  407. paths.insert(theApp.PathInstallRelativeToAbsolute(path));
  408. }
  409. HKEY key;
  410. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\DirectMusic"), 0, KEY_READ, &key) == ERROR_SUCCESS)
  411. {
  412. DWORD dwRegType = REG_SZ;
  413. DWORD dwSize = 0;
  414. if(RegQueryValueEx(key, _T("GMFilePath"), NULL, &dwRegType, nullptr, &dwSize) == ERROR_SUCCESS && dwSize > 0)
  415. {
  416. std::vector<TCHAR> filenameT(dwSize / sizeof(TCHAR));
  417. if(RegQueryValueEx(key, _T("GMFilePath"), NULL, &dwRegType, reinterpret_cast<LPBYTE>(filenameT.data()), &dwSize) == ERROR_SUCCESS)
  418. {
  419. mpt::winstring filenamestr = ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<mpt::winstring>(filenameT.data(), dwSize);
  420. std::vector<TCHAR> filenameExpanded(::ExpandEnvironmentStrings(filenamestr.c_str(), nullptr, 0));
  421. ::ExpandEnvironmentStrings(filenamestr.c_str(), filenameExpanded.data(), static_cast<DWORD>(filenameExpanded.size()));
  422. auto filename = mpt::PathString::FromNative(filenameExpanded.data());
  423. ImportMidiConfig(filename, true);
  424. paths.insert(std::move(filename));
  425. }
  426. }
  427. RegCloseKey(key);
  428. }
  429. if(paths.empty())
  430. return {};
  431. return std::async(std::launch::async, [paths = std::move(paths)]()
  432. {
  433. std::vector<std::unique_ptr<CDLSBank>> banks;
  434. banks.reserve(paths.size());
  435. for(const auto &filename : paths)
  436. {
  437. if(filename.empty() || !CDLSBank::IsDLSBank(filename))
  438. continue;
  439. try
  440. {
  441. auto bank = std::make_unique<CDLSBank>();
  442. if(bank->Open(filename))
  443. {
  444. banks.push_back(std::move(bank));
  445. continue;
  446. }
  447. } catch(mpt::out_of_memory e)
  448. {
  449. mpt::delete_out_of_memory(e);
  450. } catch(const std::exception &)
  451. {
  452. }
  453. }
  454. // Avoid the overhead of future::wait_for(0) until future::is_ready is finally non-experimental
  455. theApp.m_scannedDlsBanksAvailable = true;
  456. return banks;
  457. });
  458. }
  459. void CTrackApp::SaveDefaultDLSBanks()
  460. {
  461. uint32 nBanks = 0;
  462. for(const auto &bank : gpDLSBanks)
  463. {
  464. if(!bank || bank->GetFileName().empty())
  465. continue;
  466. mpt::PathString path = bank->GetFileName();
  467. if(theApp.IsPortableMode())
  468. {
  469. path = theApp.PathAbsoluteToInstallRelative(path);
  470. }
  471. mpt::ustring key = MPT_UFORMAT("Bank{}")(nBanks + 1);
  472. theApp.GetSettings().Write<mpt::PathString>(U_("DLS Banks"), key, path);
  473. nBanks++;
  474. }
  475. theApp.GetSettings().Write<uint32>(U_("DLS Banks"), U_("NumBanks"), nBanks);
  476. }
  477. void CTrackApp::RemoveDLSBank(UINT nBank)
  478. {
  479. if(nBank < gpDLSBanks.size())
  480. gpDLSBanks[nBank] = nullptr;
  481. }
  482. bool CTrackApp::AddDLSBank(const mpt::PathString &filename)
  483. {
  484. if(filename.empty() || !CDLSBank::IsDLSBank(filename)) return false;
  485. // Check for dupes
  486. for(const auto &bank : gpDLSBanks)
  487. {
  488. if(bank && !mpt::PathString::CompareNoCase(filename, bank->GetFileName()))
  489. return true;
  490. }
  491. try
  492. {
  493. auto bank = std::make_unique<CDLSBank>();
  494. if(bank->Open(filename))
  495. {
  496. gpDLSBanks.push_back(std::move(bank));
  497. return true;
  498. }
  499. } catch(mpt::out_of_memory e)
  500. {
  501. mpt::delete_out_of_memory(e);
  502. } catch(const std::exception &)
  503. {
  504. }
  505. return false;
  506. }
  507. size_t CTrackApp::AddScannedDLSBanks()
  508. {
  509. if(!m_scannedDlsBanks.valid())
  510. return 0;
  511. size_t numAdded = 0;
  512. auto scannedBanks = m_scannedDlsBanks.get();
  513. gpDLSBanks.reserve(gpDLSBanks.size() + scannedBanks.size());
  514. const size_t existingBanks = gpDLSBanks.size();
  515. for(auto &bank : scannedBanks)
  516. {
  517. if(std::find_if(gpDLSBanks.begin(), gpDLSBanks.begin() + existingBanks, [&bank](const auto &other) { return other && *bank == *other; }) == gpDLSBanks.begin() + existingBanks)
  518. {
  519. gpDLSBanks.push_back(std::move(bank));
  520. numAdded++;
  521. }
  522. }
  523. return numAdded;
  524. }
  525. /////////////////////////////////////////////////////////////////////////////
  526. // CTrackApp
  527. MODTYPE CTrackApp::m_nDefaultDocType = MOD_TYPE_IT;
  528. BEGIN_MESSAGE_MAP(CTrackApp, CWinApp)
  529. //{{AFX_MSG_MAP(CTrackApp)
  530. ON_COMMAND(ID_FILE_NEW, &CTrackApp::OnFileNew)
  531. ON_COMMAND(ID_FILE_NEWMOD, &CTrackApp::OnFileNewMOD)
  532. ON_COMMAND(ID_FILE_NEWS3M, &CTrackApp::OnFileNewS3M)
  533. ON_COMMAND(ID_FILE_NEWXM, &CTrackApp::OnFileNewXM)
  534. ON_COMMAND(ID_FILE_NEWIT, &CTrackApp::OnFileNewIT)
  535. ON_COMMAND(ID_NEW_MPT, &CTrackApp::OnFileNewMPT)
  536. ON_COMMAND(ID_FILE_OPEN, &CTrackApp::OnFileOpen)
  537. ON_COMMAND(ID_FILE_CLOSEALL, &CTrackApp::OnFileCloseAll)
  538. ON_COMMAND(ID_APP_ABOUT, &CTrackApp::OnAppAbout)
  539. ON_UPDATE_COMMAND_UI(ID_FILE_CLOSEALL, &CTrackApp::OnUpdateAnyDocsOpen)
  540. //}}AFX_MSG_MAP
  541. END_MESSAGE_MAP()
  542. /////////////////////////////////////////////////////////////////////////////
  543. // CTrackApp construction
  544. CTrackApp::CTrackApp()
  545. {
  546. m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART | AFX_RESTART_MANAGER_REOPEN_PREVIOUS_FILES;
  547. }
  548. class OpenMPTDataRecoveryHandler
  549. : public CDataRecoveryHandler
  550. {
  551. public:
  552. OpenMPTDataRecoveryHandler(_In_ DWORD dwRestartManagerSupportFlags, _In_ int nAutosaveInterval)
  553. : CDataRecoveryHandler(dwRestartManagerSupportFlags, nAutosaveInterval)
  554. {
  555. return;
  556. }
  557. ~OpenMPTDataRecoveryHandler() override = default;
  558. BOOL SaveOpenDocumentList() override
  559. {
  560. BOOL bRet = TRUE; // return FALSE if document list non-empty and not saved
  561. POSITION posAutosave = m_mapDocNameToAutosaveName.GetStartPosition();
  562. if (posAutosave != NULL)
  563. {
  564. bRet = FALSE;
  565. // Save the open document list and associated autosave info to the registry
  566. IniFileSettingsBackend ini(theApp.GetConfigPath() + P_("restart.") + mpt::PathString::FromCString(GetRestartIdentifier()) + P_(".ini"));
  567. ini.ConvertToUnicode();
  568. int32 count = 0;
  569. while (posAutosave != NULL)
  570. {
  571. CString strDocument, strAutosave;
  572. m_mapDocNameToAutosaveName.GetNextAssoc(posAutosave, strDocument, strAutosave);
  573. ini.WriteSetting({ U_("RestartDocument"), mpt::ufmt::val(count) }, SettingValue(mpt::ToUnicode(strDocument)));
  574. ini.WriteSetting({ U_("RestartAutosave"), mpt::ufmt::val(count) }, SettingValue(mpt::ToUnicode(strAutosave)));
  575. count++;
  576. }
  577. ini.WriteSetting({ U_("Restart"), U_("Count") }, SettingValue(count));
  578. return TRUE;
  579. }
  580. return bRet;
  581. }
  582. BOOL ReadOpenDocumentList() override
  583. {
  584. BOOL bRet = FALSE; // return TRUE only if at least one document was found
  585. {
  586. IniFileSettingsBackend ini(theApp.GetConfigPath() + P_("restart.") + mpt::PathString::FromCString(GetRestartIdentifier()) + P_(".ini"));
  587. int32 count = ini.ReadSetting({ U_("Restart"), U_("Count") }, SettingValue(0));
  588. for(int32 index = 0; index < count; ++index)
  589. {
  590. mpt::ustring document = ini.ReadSetting({ U_("RestartDocument"), mpt::ufmt::val(index) }, SettingValue(U_("")));
  591. mpt::ustring autosave = ini.ReadSetting({ U_("RestartAutosave"), mpt::ufmt::val(index) }, SettingValue(U_("")));
  592. if(!document.empty())
  593. {
  594. m_mapDocNameToAutosaveName[mpt::ToCString(document)] = mpt::ToCString(autosave);
  595. bRet = TRUE;
  596. }
  597. }
  598. ini.RemoveSection(U_("Restart"));
  599. ini.RemoveSection(U_("RestartDocument"));
  600. ini.RemoveSection(U_("RestartAutosave"));
  601. }
  602. DeleteFile((theApp.GetConfigPath() + P_("restart.") + mpt::PathString::FromCString(GetRestartIdentifier()) + P_(".ini")).AsNative().c_str());
  603. return bRet;
  604. }
  605. };
  606. CDataRecoveryHandler *CTrackApp::GetDataRecoveryHandler()
  607. {
  608. static BOOL bTriedOnce = FALSE;
  609. // Since the application restart and application recovery are supported only on Windows
  610. // Vista and above, we don't need a recovery handler on Windows versions less than Vista.
  611. if (SupportsRestartManager() || SupportsApplicationRecovery())
  612. {
  613. if (!bTriedOnce && m_pDataRecoveryHandler == NULL)
  614. {
  615. m_pDataRecoveryHandler = new OpenMPTDataRecoveryHandler(m_dwRestartManagerSupportFlags, m_nAutosaveInterval);
  616. if (!m_pDataRecoveryHandler->Initialize())
  617. {
  618. delete m_pDataRecoveryHandler;
  619. m_pDataRecoveryHandler = NULL;
  620. }
  621. }
  622. }
  623. bTriedOnce = TRUE;
  624. return m_pDataRecoveryHandler;
  625. }
  626. void CTrackApp::AddToRecentFileList(LPCTSTR lpszPathName)
  627. {
  628. AddToRecentFileList(mpt::PathString::FromCString(lpszPathName));
  629. }
  630. void CTrackApp::AddToRecentFileList(const mpt::PathString &path)
  631. {
  632. RemoveMruItem(path);
  633. TrackerSettings::Instance().mruFiles.insert(TrackerSettings::Instance().mruFiles.begin(), path);
  634. if(TrackerSettings::Instance().mruFiles.size() > TrackerSettings::Instance().mruListLength)
  635. {
  636. TrackerSettings::Instance().mruFiles.resize(TrackerSettings::Instance().mruListLength);
  637. }
  638. CMainFrame::GetMainFrame()->UpdateMRUList();
  639. }
  640. void CTrackApp::RemoveMruItem(const size_t item)
  641. {
  642. if(item < TrackerSettings::Instance().mruFiles.size())
  643. {
  644. TrackerSettings::Instance().mruFiles.erase(TrackerSettings::Instance().mruFiles.begin() + item);
  645. CMainFrame::GetMainFrame()->UpdateMRUList();
  646. }
  647. }
  648. void CTrackApp::RemoveMruItem(const mpt::PathString &path)
  649. {
  650. auto &mruFiles = TrackerSettings::Instance().mruFiles;
  651. for(auto i = mruFiles.begin(); i != mruFiles.end(); i++)
  652. {
  653. if(!mpt::PathString::CompareNoCase(*i, path))
  654. {
  655. mruFiles.erase(i);
  656. break;
  657. }
  658. }
  659. }
  660. /////////////////////////////////////////////////////////////////////////////
  661. // CTrackApp initialization
  662. namespace Tracker
  663. {
  664. mpt::recursive_mutex_with_lock_count & GetGlobalMutexRef()
  665. {
  666. return theApp.GetGlobalMutexRef();
  667. }
  668. } // namespace Tracker
  669. class ComponentManagerSettings
  670. : public IComponentManagerSettings
  671. {
  672. private:
  673. TrackerSettings &conf;
  674. mpt::PathString configPath;
  675. public:
  676. ComponentManagerSettings(TrackerSettings &conf, const mpt::PathString &configPath)
  677. : conf(conf)
  678. , configPath(configPath)
  679. {
  680. return;
  681. }
  682. bool LoadOnStartup() const override
  683. {
  684. return conf.ComponentsLoadOnStartup;
  685. }
  686. bool KeepLoaded() const override
  687. {
  688. return conf.ComponentsKeepLoaded;
  689. }
  690. bool IsBlocked(const std::string &key) const override
  691. {
  692. return conf.IsComponentBlocked(key);
  693. }
  694. mpt::PathString Path() const override
  695. {
  696. if(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()).empty())
  697. {
  698. return mpt::PathString();
  699. }
  700. return configPath + P_("Components\\") + mpt::PathString::FromUnicode(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture())) + P_("\\");
  701. }
  702. };
  703. // Move a config file called fileName from the App's directory (or one of its sub directories specified by subDir) to
  704. // %APPDATA%. If specified, it will be renamed to newFileName. Existing files are never overwritten.
  705. // Returns true on success.
  706. bool CTrackApp::MoveConfigFile(const mpt::PathString &fileName, mpt::PathString subDir, mpt::PathString newFileName)
  707. {
  708. const mpt::PathString oldPath = GetInstallPath() + subDir + fileName;
  709. mpt::PathString newPath = GetConfigPath() + subDir;
  710. if(!newFileName.empty())
  711. newPath += newFileName;
  712. else
  713. newPath += fileName;
  714. if(!newPath.IsFile() && oldPath.IsFile())
  715. {
  716. return MoveFile(oldPath.AsNative().c_str(), newPath.AsNative().c_str()) != 0;
  717. }
  718. return false;
  719. }
  720. // Set up paths were configuration data is written to. Set overridePortable to true if application's own directory should always be used.
  721. void CTrackApp::SetupPaths(bool overridePortable)
  722. {
  723. // First, determine if the executable is installed in multi-arch mode or in the old standard mode.
  724. bool modeMultiArch = false;
  725. bool modeSourceProject = false;
  726. const mpt::PathString exePath = mpt::GetExecutablePath();
  727. auto exePathComponents = mpt::String::Split<mpt::ustring>(exePath.GetDir().WithoutTrailingSlash().ToUnicode(), P_("\\").ToUnicode());
  728. if(exePathComponents.size() >= 2)
  729. {
  730. if(exePathComponents[exePathComponents.size()-1] == mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))
  731. {
  732. if(exePathComponents[exePathComponents.size()-2] == U_("bin"))
  733. {
  734. modeMultiArch = true;
  735. }
  736. }
  737. }
  738. // Check if we are running from the source tree.
  739. if(!modeMultiArch && exePathComponents.size() >= 4)
  740. {
  741. if(exePathComponents[exePathComponents.size()-1] == mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))
  742. {
  743. if(exePathComponents[exePathComponents.size()-4] == U_("bin"))
  744. {
  745. modeSourceProject = true;
  746. }
  747. }
  748. }
  749. if(modeSourceProject)
  750. {
  751. m_InstallPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\") + P_("..\\") + P_("..\\"));
  752. m_InstallBinPath = mpt::GetAbsolutePath(exePath + P_("..\\"));
  753. m_InstallBinArchPath = exePath;
  754. m_InstallPkgPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\") + P_("..\\") + P_("..\\packageTemplate\\"));
  755. } else if(modeMultiArch)
  756. {
  757. m_InstallPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\"));
  758. m_InstallBinPath = mpt::GetAbsolutePath(exePath + P_("..\\"));
  759. m_InstallBinArchPath = exePath;
  760. m_InstallPkgPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\"));
  761. } else
  762. {
  763. m_InstallPath = exePath;
  764. m_InstallBinPath = exePath;
  765. m_InstallBinArchPath = exePath;
  766. m_InstallPkgPath = exePath;
  767. }
  768. // Determine paths, portable mode, first run. Do not yet update any state.
  769. mpt::PathString configPathPortable = (modeSourceProject ? exePath : m_InstallPath); // config path in portable mode
  770. mpt::PathString configPathUser; // config path in default non-portable mode
  771. {
  772. // Try to find a nice directory where we should store our settings (default: %APPDATA%)
  773. TCHAR dir[MAX_PATH] = { 0 };
  774. if((SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, dir) == S_OK)
  775. || (SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, dir) == S_OK))
  776. {
  777. // Store our app settings in %APPDATA% or "My Documents"
  778. configPathUser = mpt::PathString::FromNative(dir) + P_("\\OpenMPT\\");
  779. }
  780. }
  781. // Check if the user has configured portable mode.
  782. bool configInstallPortable = false;
  783. mpt::PathString portableFlagFilename = (configPathPortable + P_("OpenMPT.portable"));
  784. bool configPortableFlag = portableFlagFilename.IsFile();
  785. configInstallPortable = configInstallPortable || configPortableFlag;
  786. // before 1.29.00.13:
  787. configInstallPortable = configInstallPortable || (GetPrivateProfileInt(_T("Paths"), _T("UseAppDataDirectory"), 1, (configPathPortable + P_("mptrack.ini")).AsNative().c_str()) == 0);
  788. // convert to new style
  789. if(configInstallPortable && !configPortableFlag)
  790. {
  791. mpt::SafeOutputFile f(portableFlagFilename);
  792. }
  793. // Determine portable mode.
  794. bool portableMode = overridePortable || configInstallPortable || configPathUser.empty();
  795. // Update config dir
  796. m_ConfigPath = portableMode ? configPathPortable : configPathUser;
  797. // Set up default file locations
  798. m_szConfigFileName = m_ConfigPath + P_("mptrack.ini"); // config file
  799. m_szPluginCacheFileName = m_ConfigPath + P_("plugin.cache"); // plugin cache
  800. // Force use of custom ini file rather than windowsDir\executableName.ini
  801. if(m_pszProfileName)
  802. {
  803. free((void *)m_pszProfileName);
  804. }
  805. m_pszProfileName = _tcsdup(m_szConfigFileName.ToCString());
  806. m_bInstallerMode = !modeSourceProject && !portableMode;
  807. m_bPortableMode = portableMode;
  808. m_bSourceTreeMode = modeSourceProject;
  809. }
  810. void CTrackApp::CreatePaths()
  811. {
  812. // Create missing diretories
  813. if(!IsPortableMode())
  814. {
  815. if(!m_ConfigPath.IsDirectory())
  816. {
  817. CreateDirectory(m_ConfigPath.AsNative().c_str(), 0);
  818. }
  819. }
  820. if(!(GetConfigPath() + P_("Components")).IsDirectory())
  821. {
  822. CreateDirectory((GetConfigPath() + P_("Components")).AsNative().c_str(), 0);
  823. }
  824. if(!(GetConfigPath() + P_("Components\\") + mpt::PathString::FromUnicode(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))).IsDirectory())
  825. {
  826. CreateDirectory((GetConfigPath() + P_("Components\\") + mpt::PathString::FromUnicode(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))).AsNative().c_str(), 0);
  827. }
  828. // Handle updates from old versions.
  829. if(!IsPortableMode())
  830. {
  831. // Move the config files if they're still in the old place.
  832. MoveConfigFile(P_("mptrack.ini"));
  833. MoveConfigFile(P_("plugin.cache"));
  834. // Import old tunings
  835. const mpt::PathString oldTunings = GetInstallPath() + P_("tunings\\");
  836. if(oldTunings.IsDirectory())
  837. {
  838. const mpt::PathString searchPattern = oldTunings + P_("*.*");
  839. WIN32_FIND_DATA FindFileData;
  840. HANDLE hFind;
  841. hFind = FindFirstFile(searchPattern.AsNative().c_str(), &FindFileData);
  842. if(hFind != INVALID_HANDLE_VALUE)
  843. {
  844. do
  845. {
  846. MoveConfigFile(mpt::PathString::FromNative(FindFileData.cFileName), P_("tunings\\"));
  847. } while(FindNextFile(hFind, &FindFileData) != 0);
  848. }
  849. FindClose(hFind);
  850. RemoveDirectory(oldTunings.AsNative().c_str());
  851. }
  852. }
  853. }
  854. #if !defined(MPT_BUILD_RETRO)
  855. bool CTrackApp::CheckSystemSupport()
  856. {
  857. const mpt::ustring lf = U_("\n");
  858. const mpt::ustring url = Build::GetURL(Build::Url::Download);
  859. if(!BuildVariants::ProcessorCanRunCurrentBuild())
  860. {
  861. mpt::ustring text;
  862. text += U_("Your CPU is too old to run this variant of OpenMPT.") + lf;
  863. text += U_("OpenMPT will exit now.") + lf;
  864. Reporting::Error(text, "OpenMPT");
  865. return false;
  866. }
  867. if(BuildVariants::IsKnownSystem() && !BuildVariants::SystemCanRunCurrentBuild())
  868. {
  869. mpt::ustring text;
  870. text += U_("Your system does not meet the minimum requirements for this variant of OpenMPT.") + lf;
  871. if(mpt::OS::Windows::IsOriginal())
  872. {
  873. text += U_("OpenMPT will exit now.") + lf;
  874. }
  875. Reporting::Error(text, "OpenMPT");
  876. if(mpt::OS::Windows::IsOriginal())
  877. {
  878. return false;
  879. } else
  880. {
  881. return true; // may work though
  882. }
  883. }
  884. return true;
  885. }
  886. #endif // !MPT_BUILD_RETRO
  887. BOOL CTrackApp::InitInstanceEarly(CMPTCommandLineInfo &cmdInfo)
  888. {
  889. // The first step of InitInstance, always executed without any crash handler.
  890. #ifndef UNICODE
  891. if(MessageBox(NULL,
  892. _T("STOP!!!") _T("\n")
  893. _T("This is an ANSI (as opposed to a UNICODE) build of OpenMPT.") _T("\n")
  894. _T("\n")
  895. _T("ANSI builds are NOT SUPPORTED and WILL CAUSE CORRUPTION of the OpenMPT configuration and exhibit other unintended behaviour.") _T("\n")
  896. _T("\n")
  897. _T("Please use an official build of OpenMPT or compile 'OpenMPT.sln' instead of 'OpenMPT-ANSI.sln'.") _T("\n")
  898. _T("\n")
  899. _T("Continue starting OpenMPT anyway?") _T("\n"),
  900. _T("OpenMPT"), MB_ICONSTOP | MB_YESNO| MB_DEFBUTTON2)
  901. != IDYES)
  902. {
  903. ExitProcess(1);
  904. }
  905. #endif
  906. // Call the base class.
  907. // This is required for MFC RestartManager integration.
  908. if(!CWinApp::InitInstance())
  909. {
  910. return FALSE;
  911. }
  912. #if MPT_COMPILER_MSVC
  913. _CrtSetDebugFillThreshold(0); // Disable buffer filling in secure enhanced CRT functions.
  914. #endif
  915. // Avoid e.g. audio APIs trying to load wdmaud.drv from arbitrary working directory
  916. ::SetCurrentDirectory(mpt::GetExecutablePath().AsNative().c_str());
  917. // Initialize OLE MFC support
  918. BOOL oleinit = AfxOleInit();
  919. ASSERT(oleinit != FALSE); // no MPT_ASSERT here!
  920. // Parse command line for standard shell commands, DDE, file open
  921. ParseCommandLine(cmdInfo);
  922. // Set up paths to store configuration in
  923. SetupPaths(cmdInfo.m_portable);
  924. if(cmdInfo.m_sharedInstance && IPCWindow::SendToIPC(cmdInfo.m_fileNames))
  925. {
  926. ExitProcess(0);
  927. }
  928. // Initialize DocManager (for DDE)
  929. // requires mpt::PathString
  930. ASSERT(nullptr == m_pDocManager); // no MPT_ASSERT here!
  931. m_pDocManager = new CModDocManager();
  932. if(IsDebuggerPresent() && cmdInfo.m_debugCrashHandler)
  933. {
  934. ExceptionHandler::useAnyCrashHandler = true;
  935. ExceptionHandler::useImplicitFallbackSEH = false;
  936. ExceptionHandler::useExplicitSEH = true;
  937. ExceptionHandler::handleStdTerminate = true;
  938. ExceptionHandler::handleMfcExceptions = true;
  939. ExceptionHandler::debugExceptionHandler = true;
  940. } else if(IsDebuggerPresent() || cmdInfo.m_noCrashHandler)
  941. {
  942. ExceptionHandler::useAnyCrashHandler = false;
  943. ExceptionHandler::useImplicitFallbackSEH = false;
  944. ExceptionHandler::useExplicitSEH = false;
  945. ExceptionHandler::handleStdTerminate = false;
  946. ExceptionHandler::handleMfcExceptions = false;
  947. ExceptionHandler::debugExceptionHandler = false;
  948. } else
  949. {
  950. ExceptionHandler::useAnyCrashHandler = true;
  951. ExceptionHandler::useImplicitFallbackSEH = true;
  952. ExceptionHandler::useExplicitSEH = true;
  953. ExceptionHandler::handleStdTerminate = true;
  954. ExceptionHandler::handleMfcExceptions = true;
  955. ExceptionHandler::debugExceptionHandler = false;
  956. }
  957. return TRUE;
  958. }
  959. BOOL CTrackApp::InitInstanceImpl(CMPTCommandLineInfo &cmdInfo)
  960. {
  961. m_GuiThreadId = GetCurrentThreadId();
  962. mpt::log::Trace::SetThreadId(mpt::log::Trace::ThreadKindGUI, m_GuiThreadId);
  963. if(ExceptionHandler::useAnyCrashHandler)
  964. {
  965. ExceptionHandler::Register();
  966. }
  967. // Start loading
  968. BeginWaitCursor();
  969. MPT_LOG_GLOBAL(LogInformation, "", U_("OpenMPT Start"));
  970. // create the tracker-global random device
  971. m_RD = std::make_unique<mpt::random_device>();
  972. // make the device available to non-tracker-only code
  973. mpt::set_global_random_device(m_RD.get());
  974. // create and seed the traker-global best PRNG with the random device
  975. m_PRNG = std::make_unique<mpt::thread_safe_prng<mpt::default_prng> >(mpt::make_prng<mpt::default_prng>(RandomDevice()));
  976. // make the best PRNG available to non-tracker-only code
  977. mpt::set_global_prng(m_PRNG.get());
  978. // additionally, seed the C rand() PRNG, just in case any third party library calls rand()
  979. mpt::crand::reseed(RandomDevice());
  980. m_Gdiplus = std::make_unique<GdiplusRAII>();
  981. if(cmdInfo.m_noWine)
  982. {
  983. mpt::OS::Windows::PreventWineDetection();
  984. }
  985. #ifdef MPT_ENABLE_ARCH_INTRINSICS
  986. if(!cmdInfo.m_noAssembly)
  987. {
  988. CPU::EnableAvailableFeatures();
  989. }
  990. #endif // MPT_ENABLE_ARCH_INTRINSICS
  991. if(mpt::OS::Windows::IsWine())
  992. {
  993. SetWineVersion(std::make_shared<mpt::OS::Wine::VersionContext>());
  994. }
  995. // Create paths to store configuration in
  996. CreatePaths();
  997. m_pSettingsIniFile = new IniFileSettingsBackend(m_szConfigFileName);
  998. m_pSettings = new SettingsContainer(m_pSettingsIniFile);
  999. m_pDebugSettings = new DebugSettings(*m_pSettings);
  1000. m_pTrackerSettings = new TrackerSettings(*m_pSettings);
  1001. MPT_LOG_GLOBAL(LogInformation, "", U_("OpenMPT settings initialized."));
  1002. if(ExceptionHandler::useAnyCrashHandler)
  1003. {
  1004. ExceptionHandler::ConfigureSystemHandler();
  1005. }
  1006. if(TrackerSettings::Instance().MiscUseSingleInstance && IPCWindow::SendToIPC(cmdInfo.m_fileNames))
  1007. {
  1008. ExitProcess(0);
  1009. }
  1010. IPCWindow::Open(m_hInstance);
  1011. m_pSongSettingsIniFile = new IniFileSettingsBackend(GetConfigPath() + P_("SongSettings.ini"));
  1012. m_pSongSettings = new SettingsContainer(m_pSongSettingsIniFile);
  1013. m_pComponentManagerSettings = new ComponentManagerSettings(TrackerSettings::Instance(), GetConfigPath());
  1014. m_pPluginCache = new IniFileSettingsContainer(m_szPluginCacheFileName);
  1015. // Load standard INI file options (without MRU)
  1016. // requires SetupPaths+CreatePaths called
  1017. LoadStdProfileSettings(0);
  1018. // Set process priority class
  1019. #ifndef _DEBUG
  1020. SetPriorityClass(GetCurrentProcess(), TrackerSettings::Instance().MiscProcessPriorityClass);
  1021. #endif
  1022. // Dynamic DPI-awareness. Some users might want to disable DPI-awareness because of their DPI-unaware VST plugins.
  1023. bool setDPI = false;
  1024. // For Windows 10, Creators Update (1703) and newer
  1025. {
  1026. mpt::Library user32(mpt::LibraryPath::System(P_("user32")));
  1027. if (user32.IsValid())
  1028. {
  1029. enum MPT_DPI_AWARENESS_CONTEXT
  1030. {
  1031. MPT_DPI_AWARENESS_CONTEXT_UNAWARE = -1,
  1032. MPT_DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2,
  1033. MPT_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3,
  1034. MPT_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4,
  1035. MPT_DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5, // 1809 update and newer
  1036. };
  1037. using PSETPROCESSDPIAWARENESSCONTEXT = BOOL(WINAPI *)(HANDLE);
  1038. PSETPROCESSDPIAWARENESSCONTEXT SetProcessDpiAwarenessContext = nullptr;
  1039. if(user32.Bind(SetProcessDpiAwarenessContext, "SetProcessDpiAwarenessContext"))
  1040. {
  1041. if (TrackerSettings::Instance().highResUI)
  1042. {
  1043. setDPI = (SetProcessDpiAwarenessContext(HANDLE(MPT_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) == TRUE);
  1044. } else
  1045. {
  1046. if (SetProcessDpiAwarenessContext(HANDLE(MPT_DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) == TRUE)
  1047. setDPI = true;
  1048. else
  1049. setDPI = (SetProcessDpiAwarenessContext(HANDLE(MPT_DPI_AWARENESS_CONTEXT_UNAWARE)) == TRUE);
  1050. }
  1051. }
  1052. }
  1053. }
  1054. // For Windows 8.1 and newer
  1055. if(!setDPI)
  1056. {
  1057. mpt::Library shcore(mpt::LibraryPath::System(P_("SHCore")));
  1058. if(shcore.IsValid())
  1059. {
  1060. using PSETPROCESSDPIAWARENESS = HRESULT (WINAPI *)(int);
  1061. PSETPROCESSDPIAWARENESS SetProcessDPIAwareness = nullptr;
  1062. if(shcore.Bind(SetProcessDPIAwareness, "SetProcessDpiAwareness"))
  1063. {
  1064. setDPI = (SetProcessDPIAwareness(TrackerSettings::Instance().highResUI ? 2 : 0) == S_OK);
  1065. }
  1066. }
  1067. }
  1068. // For Vista and newer
  1069. if(!setDPI && TrackerSettings::Instance().highResUI)
  1070. {
  1071. mpt::Library user32(mpt::LibraryPath::System(P_("user32")));
  1072. if(user32.IsValid())
  1073. {
  1074. using PSETPROCESSDPIAWARE = BOOL (WINAPI *)();
  1075. PSETPROCESSDPIAWARE SetProcessDPIAware = nullptr;
  1076. if(user32.Bind(SetProcessDPIAware, "SetProcessDPIAware"))
  1077. {
  1078. SetProcessDPIAware();
  1079. }
  1080. }
  1081. }
  1082. // create main MDI Frame window
  1083. CMainFrame* pMainFrame = new CMainFrame();
  1084. if(!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;
  1085. m_pMainWnd = pMainFrame;
  1086. // Show splash screen
  1087. if(cmdInfo.m_bShowSplash && TrackerSettings::Instance().m_ShowSplashScreen)
  1088. {
  1089. StartSplashScreen();
  1090. }
  1091. // create component manager
  1092. ComponentManager::Init(*m_pComponentManagerSettings);
  1093. // load components
  1094. ComponentManager::Instance()->Startup();
  1095. // Wine Support
  1096. if(mpt::OS::Windows::IsWine())
  1097. {
  1098. WineIntegration::Initialize();
  1099. WineIntegration::Load();
  1100. }
  1101. // Register document templates
  1102. m_pModTemplate = new CModDocTemplate(
  1103. IDR_MODULETYPE,
  1104. RUNTIME_CLASS(CModDoc),
  1105. RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  1106. RUNTIME_CLASS(CModControlView));
  1107. AddDocTemplate(m_pModTemplate);
  1108. // Load Midi Library
  1109. ImportMidiConfig(theApp.GetSettings(), {}, true);
  1110. // Enable DDE Execute open
  1111. // requires m_pDocManager
  1112. EnableShellOpen();
  1113. // Enable drag/drop open
  1114. m_pMainWnd->DragAcceptFiles();
  1115. // Load sound APIs
  1116. // requires TrackerSettings
  1117. m_pAllSoundDeviceComponents = std::make_unique<AllSoundDeviceComponents>();
  1118. auto GetSysInfo = [&]()
  1119. {
  1120. if(mpt::OS::Windows::IsWine())
  1121. {
  1122. return SoundDevice::SysInfo(mpt::osinfo::get_class(), mpt::OS::Windows::Version::Current(), mpt::OS::Windows::IsWine(), GetWineVersion()->HostClass(), GetWineVersion()->Version());
  1123. }
  1124. return SoundDevice::SysInfo(mpt::osinfo::get_class(), mpt::OS::Windows::Version::Current(), mpt::OS::Windows::IsWine(), mpt::osinfo::osclass::Unknown, mpt::osinfo::windows::wine::version());
  1125. };
  1126. SoundDevice::SysInfo sysInfo = GetSysInfo();
  1127. SoundDevice::AppInfo appInfo;
  1128. appInfo.SetName(U_("OpenMPT"));
  1129. appInfo.SetHWND(*m_pMainWnd);
  1130. appInfo.BoostedThreadPriorityXP = TrackerSettings::Instance().SoundBoostedThreadPriority;
  1131. appInfo.BoostedThreadMMCSSClassVista = TrackerSettings::Instance().SoundBoostedThreadMMCSSClass;
  1132. appInfo.BoostedThreadRealtimePosix = TrackerSettings::Instance().SoundBoostedThreadRealtimePosix;
  1133. appInfo.BoostedThreadNicenessPosix = TrackerSettings::Instance().SoundBoostedThreadNicenessPosix;
  1134. appInfo.BoostedThreadRtprioPosix = TrackerSettings::Instance().SoundBoostedThreadRtprioPosix;
  1135. appInfo.MaskDriverCrashes = TrackerSettings::Instance().SoundMaskDriverCrashes;
  1136. appInfo.AllowDeferredProcessing = TrackerSettings::Instance().SoundAllowDeferredProcessing;
  1137. std::vector<std::shared_ptr<SoundDevice::IDevicesEnumerator>> deviceEnumerators = SoundDevice::Manager::GetEnabledEnumerators(*m_pAllSoundDeviceComponents);
  1138. deviceEnumerators.push_back(std::static_pointer_cast<SoundDevice::IDevicesEnumerator>(std::make_shared<SoundDevice::DevicesEnumerator<SoundDevice::SoundDeviceStub>>()));
  1139. m_pSoundDevicesManager = std::make_unique<SoundDevice::Manager>(m_GlobalLogger, sysInfo, appInfo, std::move(deviceEnumerators));
  1140. m_pTrackerSettings->MigrateOldSoundDeviceSettings(*m_pSoundDevicesManager);
  1141. // Set default note names
  1142. CSoundFile::SetDefaultNoteNames();
  1143. // Load DLS Banks
  1144. if (!cmdInfo.m_noDls)
  1145. m_scannedDlsBanks = LoadDefaultDLSBanks();
  1146. // Initialize Plugins
  1147. if (!cmdInfo.m_noPlugins) InitializeDXPlugins();
  1148. // Initialize CMainFrame
  1149. pMainFrame->Initialize();
  1150. InitCommonControls();
  1151. pMainFrame->m_InputHandler->UpdateMainMenu();
  1152. // Dispatch commands specified on the command line
  1153. if(cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew)
  1154. {
  1155. // When not asked to open any existing file,
  1156. // we do not want to open an empty new one on startup.
  1157. cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
  1158. }
  1159. bool shellSuccess = false;
  1160. if(cmdInfo.m_fileNames.empty())
  1161. {
  1162. shellSuccess = ProcessShellCommand(cmdInfo) != FALSE;
  1163. } else
  1164. {
  1165. cmdInfo.m_nShellCommand = CCommandLineInfo::FileOpen;
  1166. for(const auto &filename : cmdInfo.m_fileNames)
  1167. {
  1168. cmdInfo.m_strFileName = filename.ToCString();
  1169. shellSuccess |= ProcessShellCommand(cmdInfo) != FALSE;
  1170. }
  1171. }
  1172. if(!shellSuccess)
  1173. {
  1174. EndWaitCursor();
  1175. StopSplashScreen();
  1176. return FALSE;
  1177. }
  1178. pMainFrame->ShowWindow(m_nCmdShow);
  1179. pMainFrame->UpdateWindow();
  1180. EndWaitCursor();
  1181. // Perform startup tasks.
  1182. #if !defined(MPT_BUILD_RETRO)
  1183. // Check whether we are running the best build for the given system.
  1184. if(!cmdInfo.m_noSysCheck)
  1185. {
  1186. if(!CheckSystemSupport())
  1187. {
  1188. StopSplashScreen();
  1189. return FALSE;
  1190. }
  1191. }
  1192. #endif // !MPT_BUILD_RETRO
  1193. if(TrackerSettings::Instance().FirstRun)
  1194. {
  1195. // On high-DPI devices, automatically upscale pattern font
  1196. FontSetting font = TrackerSettings::Instance().patternFont;
  1197. font.size = Clamp(Util::GetDPIy(m_pMainWnd->m_hWnd) / 96 - 1, 0, 9);
  1198. TrackerSettings::Instance().patternFont = font;
  1199. new WelcomeDlg(m_pMainWnd);
  1200. } else
  1201. {
  1202. #if !defined(MPT_BUILD_RETRO)
  1203. bool deprecatedSoundDevice = GetSoundDevicesManager()->FindDeviceInfo(TrackerSettings::Instance().GetSoundDeviceIdentifier()).IsDeprecated();
  1204. bool showSettings = deprecatedSoundDevice && !TrackerSettings::Instance().m_SoundDeprecatedDeviceWarningShown && (Reporting::Confirm(
  1205. U_("You have currently selected a sound device which is deprecated. MME/WaveOut support will be removed in a future OpenMPT version.\n") +
  1206. U_("The recommended sound device type is WASAPI.\n") +
  1207. U_("Do you want to change your sound device settings now?"),
  1208. U_("OpenMPT - Deprecated sound device")
  1209. ) == cnfYes);
  1210. if(showSettings)
  1211. {
  1212. TrackerSettings::Instance().m_SoundDeprecatedDeviceWarningShown = true;
  1213. m_pMainWnd->PostMessage(WM_COMMAND, ID_VIEW_OPTIONS);
  1214. }
  1215. #endif // !MPT_BUILD_RETRO
  1216. }
  1217. #ifdef ENABLE_TESTS
  1218. if(!cmdInfo.m_noTests)
  1219. Test::DoTests();
  1220. #endif
  1221. if(TrackerSettings::Instance().m_SoundSettingsOpenDeviceAtStartup)
  1222. {
  1223. pMainFrame->InitPreview();
  1224. pMainFrame->PreparePreview(NOTE_NOTECUT, 0);
  1225. pMainFrame->PlayPreview();
  1226. }
  1227. if(!TrackerSettings::Instance().FirstRun)
  1228. {
  1229. #if defined(MPT_ENABLE_UPDATE)
  1230. if(CUpdateCheck::IsSuitableUpdateMoment())
  1231. {
  1232. CUpdateCheck::DoAutoUpdateCheck();
  1233. }
  1234. #endif // MPT_ENABLE_UPDATE
  1235. }
  1236. return TRUE;
  1237. }
  1238. BOOL CTrackApp::InitInstance()
  1239. {
  1240. CMPTCommandLineInfo cmdInfo;
  1241. if(!InitInstanceEarly(cmdInfo))
  1242. {
  1243. return FALSE;
  1244. }
  1245. return InitInstanceLate(cmdInfo);
  1246. }
  1247. BOOL CTrackApp::InitInstanceLate(CMPTCommandLineInfo &cmdInfo)
  1248. {
  1249. BOOL result = FALSE;
  1250. if(ExceptionHandler::useExplicitSEH)
  1251. {
  1252. // https://support.microsoft.com/en-us/kb/173652
  1253. __try
  1254. {
  1255. result = InitInstanceImpl(cmdInfo);
  1256. } __except(ExceptionHandler::ExceptionFilter(GetExceptionInformation()))
  1257. {
  1258. std::abort();
  1259. }
  1260. } else
  1261. {
  1262. result = InitInstanceImpl(cmdInfo);
  1263. }
  1264. return result;
  1265. }
  1266. int CTrackApp::Run()
  1267. {
  1268. int result = 255;
  1269. if(ExceptionHandler::useExplicitSEH)
  1270. {
  1271. // https://support.microsoft.com/en-us/kb/173652
  1272. __try
  1273. {
  1274. result = CWinApp::Run();
  1275. } __except(ExceptionHandler::ExceptionFilter(GetExceptionInformation()))
  1276. {
  1277. std::abort();
  1278. }
  1279. } else
  1280. {
  1281. result = CWinApp::Run();
  1282. }
  1283. return result;
  1284. }
  1285. LRESULT CTrackApp::ProcessWndProcException(CException * e, const MSG * pMsg)
  1286. {
  1287. if(ExceptionHandler::handleMfcExceptions)
  1288. {
  1289. LRESULT result = 0L; // as per documentation
  1290. if(pMsg)
  1291. {
  1292. if(pMsg->message == WM_COMMAND)
  1293. {
  1294. result = (LRESULT)TRUE; // as per documentation
  1295. }
  1296. }
  1297. if(dynamic_cast<CMemoryException*>(e))
  1298. {
  1299. e->ReportError();
  1300. //ExceptionHandler::UnhandledMFCException(e, pMsg);
  1301. } else
  1302. {
  1303. ExceptionHandler::UnhandledMFCException(e, pMsg);
  1304. }
  1305. return result;
  1306. } else
  1307. {
  1308. return CWinApp::ProcessWndProcException(e, pMsg);
  1309. }
  1310. }
  1311. int CTrackApp::ExitInstance()
  1312. {
  1313. int result = 0;
  1314. if(ExceptionHandler::useExplicitSEH)
  1315. {
  1316. // https://support.microsoft.com/en-us/kb/173652
  1317. __try
  1318. {
  1319. result = ExitInstanceImpl();
  1320. } __except(ExceptionHandler::ExceptionFilter(GetExceptionInformation()))
  1321. {
  1322. std::abort();
  1323. }
  1324. } else
  1325. {
  1326. result = ExitInstanceImpl();
  1327. }
  1328. return result;
  1329. }
  1330. int CTrackApp::ExitInstanceImpl()
  1331. {
  1332. IPCWindow::Close();
  1333. m_pSoundDevicesManager = nullptr;
  1334. m_pAllSoundDeviceComponents = nullptr;
  1335. ExportMidiConfig(theApp.GetSettings());
  1336. AddScannedDLSBanks();
  1337. SaveDefaultDLSBanks();
  1338. gpDLSBanks.clear();
  1339. // Uninitialize Plugins
  1340. UninitializeDXPlugins();
  1341. ComponentManager::Release();
  1342. delete m_pPluginCache;
  1343. m_pPluginCache = nullptr;
  1344. delete m_pComponentManagerSettings;
  1345. m_pComponentManagerSettings = nullptr;
  1346. delete m_pTrackerSettings;
  1347. m_pTrackerSettings = nullptr;
  1348. delete m_pDebugSettings;
  1349. m_pDebugSettings = nullptr;
  1350. delete m_pSettings;
  1351. m_pSettings = nullptr;
  1352. delete m_pSettingsIniFile;
  1353. m_pSettingsIniFile = nullptr;
  1354. delete m_pSongSettings;
  1355. m_pSongSettings = nullptr;
  1356. delete m_pSongSettingsIniFile;
  1357. m_pSongSettingsIniFile = nullptr;
  1358. if(mpt::OS::Windows::IsWine())
  1359. {
  1360. SetWineVersion(nullptr);
  1361. }
  1362. m_Gdiplus.reset();
  1363. mpt::set_global_prng(nullptr);
  1364. m_PRNG.reset();
  1365. mpt::set_global_random_device(nullptr);
  1366. m_RD.reset();
  1367. if(ExceptionHandler::useAnyCrashHandler)
  1368. {
  1369. ExceptionHandler::UnconfigureSystemHandler();
  1370. ExceptionHandler::Unregister();
  1371. }
  1372. return CWinApp::ExitInstance();
  1373. }
  1374. ////////////////////////////////////////////////////////////////////////////////
  1375. // App Messages
  1376. CModDoc *CTrackApp::NewDocument(MODTYPE newType)
  1377. {
  1378. // Build from template
  1379. if(newType == MOD_TYPE_NONE)
  1380. {
  1381. const mpt::PathString templateFile = TrackerSettings::Instance().defaultTemplateFile;
  1382. if(TrackerSettings::Instance().defaultNewFileAction == nfDefaultTemplate && !templateFile.empty())
  1383. {
  1384. // Template file can be either a filename inside one of the preset and user TemplateModules folders, or a full path.
  1385. const mpt::PathString dirs[] = { GetConfigPath() + P_("TemplateModules\\"), GetInstallPath() + P_("TemplateModules\\"), mpt::PathString() };
  1386. for(const auto &dir : dirs)
  1387. {
  1388. if((dir + templateFile).IsFile())
  1389. {
  1390. if(CModDoc *modDoc = static_cast<CModDoc *>(m_pModTemplate->OpenTemplateFile(dir + templateFile)))
  1391. {
  1392. return modDoc;
  1393. }
  1394. }
  1395. }
  1396. }
  1397. // Default module type
  1398. newType = TrackerSettings::Instance().defaultModType;
  1399. // Get active document to make the new module of the same type
  1400. CModDoc *pModDoc = CMainFrame::GetMainFrame()->GetActiveDoc();
  1401. if(pModDoc != nullptr && TrackerSettings::Instance().defaultNewFileAction == nfSameAsCurrent)
  1402. {
  1403. newType = pModDoc->GetSoundFile().GetBestSaveFormat();
  1404. }
  1405. }
  1406. SetDefaultDocType(newType);
  1407. return static_cast<CModDoc *>(m_pModTemplate->OpenDocumentFile(_T("")));
  1408. }
  1409. void CTrackApp::OpenModulesDialog(std::vector<mpt::PathString> &files, const mpt::PathString &overridePath)
  1410. {
  1411. files.clear();
  1412. static constexpr std::string_view commonExts[] = {"mod", "s3m", "xm", "it", "mptm", "mo3", "oxm", "nst", "stk", "m15", "pt36", "mid", "rmi", "smf", "wav", "mdz", "s3z", "xmz", "itz", "mdr"};
  1413. std::string exts, extsWithoutCommon;
  1414. for(const auto &ext : CSoundFile::GetSupportedExtensions(true))
  1415. {
  1416. const auto filter = std::string("*.") + ext + std::string(";");
  1417. exts += filter;
  1418. if(!mpt::contains(commonExts, ext))
  1419. extsWithoutCommon += filter;
  1420. }
  1421. static int nFilterIndex = 0;
  1422. FileDialog dlg = OpenFileDialog()
  1423. .AllowMultiSelect()
  1424. .ExtensionFilter("All Modules (*.mptm,*.mod,*.xm,*.s3m,*.it,...)|" + exts + ";mod.*"
  1425. "|"
  1426. "Compressed Modules (*.mdz,*.s3z,*.xmz,*.itz,*.mo3,*.oxm,...)|*.mdz;*.s3z;*.xmz;*.itz;*.mdr;*.zip;*.rar;*.lha;*.pma;*.lzs;*.gz;*.mo3;*.oxm"
  1427. "|"
  1428. "ProTracker Modules (*.mod,*.nst)|*.mod;mod.*;*.mdz;*.nst;*.m15;*.stk;*.pt36|"
  1429. "Scream Tracker Modules (*.s3m,*.stm)|*.s3m;*.stm;*.s3z;*.stx|"
  1430. "FastTracker Modules (*.xm)|*.xm;*.xmz|"
  1431. "Impulse Tracker Modules (*.it)|*.it;*.itz|"
  1432. "OpenMPT Modules (*.mptm)|*.mptm;*.mptmz|"
  1433. "Other Modules (*.mtm,*.okt,*.mdl,*.669,*.far,...)|" + extsWithoutCommon + "|"
  1434. "Wave Files (*.wav)|*.wav|"
  1435. "MIDI Files (*.mid,*.rmi)|*.mid;*.rmi;*.smf|"
  1436. "All Files (*.*)|*.*||")
  1437. .WorkingDirectory(overridePath.empty() ? TrackerSettings::Instance().PathSongs.GetWorkingDir() : overridePath)
  1438. .FilterIndex(&nFilterIndex);
  1439. if(!dlg.Show()) return;
  1440. if(overridePath.empty())
  1441. TrackerSettings::Instance().PathSongs.SetWorkingDir(dlg.GetWorkingDirectory());
  1442. files = dlg.GetFilenames();
  1443. }
  1444. void CTrackApp::OnFileOpen()
  1445. {
  1446. FileDialog::PathList files;
  1447. OpenModulesDialog(files);
  1448. for(const auto &file : files)
  1449. {
  1450. OpenDocumentFile(file.ToCString());
  1451. }
  1452. }
  1453. // App command to run the dialog
  1454. void CTrackApp::OnAppAbout()
  1455. {
  1456. if (CAboutDlg::instance) return;
  1457. CAboutDlg::instance = new CAboutDlg();
  1458. CAboutDlg::instance->Create(IDD_ABOUTBOX, m_pMainWnd);
  1459. }
  1460. /////////////////////////////////////////////////////////////////////////////
  1461. // Splash Screen
  1462. class CSplashScreen: public CDialog
  1463. {
  1464. protected:
  1465. std::unique_ptr<Gdiplus::Image> m_Image;
  1466. public:
  1467. ~CSplashScreen();
  1468. BOOL OnInitDialog() override;
  1469. void OnOK() override;
  1470. void OnCancel() override { OnOK(); }
  1471. void OnPaint();
  1472. BOOL OnEraseBkgnd(CDC *) { return TRUE; }
  1473. DECLARE_MESSAGE_MAP()
  1474. };
  1475. BEGIN_MESSAGE_MAP(CSplashScreen, CDialog)
  1476. ON_WM_PAINT()
  1477. ON_WM_ERASEBKGND()
  1478. END_MESSAGE_MAP()
  1479. static CSplashScreen *gpSplashScreen = NULL;
  1480. static DWORD64 gSplashScreenStartTime = 0;
  1481. CSplashScreen::~CSplashScreen()
  1482. {
  1483. gpSplashScreen = nullptr;
  1484. }
  1485. void CSplashScreen::OnPaint()
  1486. {
  1487. CPaintDC dc(this);
  1488. Gdiplus::Graphics gfx(dc);
  1489. CRect rect;
  1490. GetClientRect(&rect);
  1491. gfx.SetInterpolationMode(Gdiplus::InterpolationModeHighQuality);
  1492. gfx.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
  1493. gfx.DrawImage(m_Image.get(), 0, 0, rect.right, rect.bottom);
  1494. CDialog::OnPaint();
  1495. }
  1496. BOOL CSplashScreen::OnInitDialog()
  1497. {
  1498. CDialog::OnInitDialog();
  1499. try
  1500. {
  1501. m_Image = GDIP::LoadPixelImage(GetResource(MAKEINTRESOURCE(IDB_SPLASHNOFOLDFIN), _T("PNG")));
  1502. } catch(const bad_image &)
  1503. {
  1504. return FALSE;
  1505. }
  1506. CRect rect;
  1507. GetWindowRect(&rect);
  1508. const int width = Util::ScalePixels(m_Image->GetWidth(), m_hWnd) / 2;
  1509. const int height = Util::ScalePixels(m_Image->GetHeight(), m_hWnd) / 2;
  1510. SetWindowPos(nullptr,
  1511. rect.left - ((width - rect.Width()) / 2),
  1512. rect.top - ((height - rect.Height()) / 2),
  1513. width,
  1514. height,
  1515. SWP_NOZORDER | SWP_NOCOPYBITS);
  1516. return TRUE;
  1517. }
  1518. void CSplashScreen::OnOK()
  1519. {
  1520. StopSplashScreen();
  1521. }
  1522. static void StartSplashScreen()
  1523. {
  1524. if(!gpSplashScreen)
  1525. {
  1526. gpSplashScreen = new CSplashScreen();
  1527. gpSplashScreen->Create(IDD_SPLASHSCREEN, theApp.m_pMainWnd);
  1528. gpSplashScreen->ShowWindow(SW_SHOW);
  1529. gpSplashScreen->UpdateWindow();
  1530. gpSplashScreen->BeginWaitCursor();
  1531. gSplashScreenStartTime = Util::GetTickCount64();
  1532. }
  1533. }
  1534. static void StopSplashScreen()
  1535. {
  1536. if(gpSplashScreen)
  1537. {
  1538. gpSplashScreen->EndWaitCursor();
  1539. gpSplashScreen->DestroyWindow();
  1540. delete gpSplashScreen;
  1541. gpSplashScreen = nullptr;
  1542. }
  1543. }
  1544. static void TimeoutSplashScreen()
  1545. {
  1546. if(gpSplashScreen)
  1547. {
  1548. if(Util::GetTickCount64() - gSplashScreenStartTime > 100)
  1549. {
  1550. StopSplashScreen();
  1551. }
  1552. }
  1553. }
  1554. /////////////////////////////////////////////////////////////////////////////
  1555. // Idle-time processing
  1556. BOOL CTrackApp::OnIdle(LONG lCount)
  1557. {
  1558. BOOL b = CWinApp::OnIdle(lCount);
  1559. TimeoutSplashScreen();
  1560. if(CMainFrame::GetMainFrame())
  1561. {
  1562. CMainFrame::GetMainFrame()->IdleHandlerSounddevice();
  1563. if(m_scannedDlsBanksAvailable)
  1564. {
  1565. if(AddScannedDLSBanks())
  1566. CMainFrame::GetMainFrame()->RefreshDlsBanks();
  1567. }
  1568. }
  1569. // Call plugins idle routine for open editor
  1570. if (m_pPluginManager)
  1571. {
  1572. DWORD curTime = timeGetTime();
  1573. //rewbs.vstCompliance: call @ 50Hz
  1574. if (curTime - m_dwLastPluginIdleCall > 20 || curTime < m_dwLastPluginIdleCall)
  1575. {
  1576. m_pPluginManager->OnIdle();
  1577. m_dwLastPluginIdleCall = curTime;
  1578. }
  1579. }
  1580. return b;
  1581. }
  1582. /////////////////////////////////////////////////////////////////////////////
  1583. // DIB
  1584. RGBQUAD rgb2quad(COLORREF c)
  1585. {
  1586. RGBQUAD r;
  1587. r.rgbBlue = GetBValue(c);
  1588. r.rgbGreen = GetGValue(c);
  1589. r.rgbRed = GetRValue(c);
  1590. r.rgbReserved = 0;
  1591. return r;
  1592. }
  1593. void DibBlt(HDC hdc, int x, int y, int sizex, int sizey, int srcx, int srcy, MODPLUGDIB *lpdib)
  1594. {
  1595. if (!lpdib) return;
  1596. SetDIBitsToDevice( hdc,
  1597. x,
  1598. y,
  1599. sizex,
  1600. sizey,
  1601. srcx,
  1602. lpdib->bmiHeader.biHeight - srcy - sizey,
  1603. 0,
  1604. lpdib->bmiHeader.biHeight,
  1605. lpdib->lpDibBits,
  1606. (LPBITMAPINFO)lpdib,
  1607. DIB_RGB_COLORS);
  1608. }
  1609. MODPLUGDIB *LoadDib(LPCTSTR lpszName)
  1610. {
  1611. mpt::const_byte_span data = GetResource(lpszName, RT_BITMAP);
  1612. if(!data.data())
  1613. {
  1614. return nullptr;
  1615. }
  1616. LPBITMAPINFO p = (LPBITMAPINFO)data.data();
  1617. MODPLUGDIB *pmd = new MODPLUGDIB;
  1618. pmd->bmiHeader = p->bmiHeader;
  1619. for (int i=0; i<16; i++) pmd->bmiColors[i] = p->bmiColors[i];
  1620. LPBYTE lpDibBits = (LPBYTE)p;
  1621. lpDibBits += p->bmiHeader.biSize + 16 * sizeof(RGBQUAD);
  1622. pmd->lpDibBits = lpDibBits;
  1623. return pmd;
  1624. }
  1625. int DrawTextT(HDC hdc, const wchar_t *lpchText, int cchText, LPRECT lprc, UINT format)
  1626. {
  1627. return ::DrawTextW(hdc, lpchText, cchText, lprc, format);
  1628. }
  1629. int DrawTextT(HDC hdc, const char *lpchText, int cchText, LPRECT lprc, UINT format)
  1630. {
  1631. return ::DrawTextA(hdc, lpchText, cchText, lprc, format);
  1632. }
  1633. template<typename Tchar>
  1634. static void DrawButtonRectImpl(HDC hdc, CRect rect, const Tchar *lpszText, bool disabled, bool pushed, DWORD textFlags, uint32 topMargin)
  1635. {
  1636. int width = Util::ScalePixels(1, WindowFromDC(hdc));
  1637. if(width != 1)
  1638. {
  1639. // Draw "real" buttons in Hi-DPI mode
  1640. DrawFrameControl(hdc, rect, DFC_BUTTON, pushed ? (DFCS_PUSHED | DFCS_BUTTONPUSH) : DFCS_BUTTONPUSH);
  1641. } else
  1642. {
  1643. const auto colorHighlight = GetSysColor(COLOR_BTNHIGHLIGHT), colorShadow = GetSysColor(COLOR_BTNSHADOW);
  1644. auto oldpen = SelectPen(hdc, GetStockObject(DC_PEN));
  1645. ::SetDCPenColor(hdc, pushed ? colorShadow : colorHighlight);
  1646. ::FillRect(hdc, rect, GetSysColorBrush(COLOR_BTNFACE));
  1647. ::MoveToEx(hdc, rect.left, rect.bottom - 1, nullptr);
  1648. ::LineTo(hdc, rect.left, rect.top);
  1649. ::LineTo(hdc, rect.right - 1, rect.top);
  1650. ::SetDCPenColor(hdc, pushed ? colorHighlight : colorShadow);
  1651. ::LineTo(hdc, rect.right - 1, rect.bottom - 1);
  1652. ::LineTo(hdc, rect.left, rect.bottom - 1);
  1653. SelectPen(hdc, oldpen);
  1654. }
  1655. if(lpszText && lpszText[0])
  1656. {
  1657. rect.DeflateRect(width, width);
  1658. if(pushed)
  1659. {
  1660. rect.top += width;
  1661. rect.left += width;
  1662. }
  1663. ::SetTextColor(hdc, GetSysColor(disabled ? COLOR_GRAYTEXT : COLOR_BTNTEXT));
  1664. ::SetBkMode(hdc, TRANSPARENT);
  1665. rect.top += topMargin;
  1666. auto oldfont = SelectFont(hdc, CMainFrame::GetGUIFont());
  1667. DrawTextT(hdc, lpszText, -1, &rect, textFlags | DT_SINGLELINE | DT_NOPREFIX);
  1668. SelectFont(hdc, oldfont);
  1669. }
  1670. }
  1671. void DrawButtonRect(HDC hdc, const RECT *lpRect, LPCSTR lpszText, BOOL bDisabled, BOOL bPushed, DWORD dwFlags, uint32 topMargin)
  1672. {
  1673. DrawButtonRectImpl(hdc, *lpRect, lpszText, bDisabled, bPushed, dwFlags, topMargin);
  1674. }
  1675. void DrawButtonRect(HDC hdc, const RECT *lpRect, LPCWSTR lpszText, BOOL bDisabled, BOOL bPushed, DWORD dwFlags, uint32 topMargin)
  1676. {
  1677. DrawButtonRectImpl(hdc, *lpRect, lpszText, bDisabled, bPushed, dwFlags, topMargin);
  1678. }
  1679. //////////////////////////////////////////////////////////////////////////////////
  1680. // Misc functions
  1681. void ErrorBox(UINT nStringID, CWnd *parent)
  1682. {
  1683. CString str;
  1684. BOOL resourceLoaded = str.LoadString(nStringID);
  1685. if(!resourceLoaded)
  1686. {
  1687. str.Format(_T("Resource string %u not found."), nStringID);
  1688. }
  1689. MPT_ASSERT(resourceLoaded);
  1690. Reporting::CustomNotification(str, _T("Error!"), MB_OK | MB_ICONERROR, parent);
  1691. }
  1692. CString GetWindowTextString(const CWnd &wnd)
  1693. {
  1694. CString result;
  1695. wnd.GetWindowText(result);
  1696. return result;
  1697. }
  1698. mpt::ustring GetWindowTextUnicode(const CWnd &wnd)
  1699. {
  1700. return mpt::ToUnicode(GetWindowTextString(wnd));
  1701. }
  1702. ////////////////////////////////////////////////////////////////////////////////
  1703. // CFastBitmap 8-bit output / 4-bit input
  1704. // useful for lots of small blits with color mapping
  1705. // combined in one big blit
  1706. void CFastBitmap::Init(MODPLUGDIB *lpTextDib)
  1707. {
  1708. m_nBlendOffset = 0;
  1709. m_pTextDib = lpTextDib;
  1710. MemsetZero(m_Dib.bmiHeader);
  1711. m_nTextColor = 0;
  1712. m_nBkColor = 1;
  1713. m_Dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  1714. m_Dib.bmiHeader.biWidth = 0; // Set later
  1715. m_Dib.bmiHeader.biHeight = 0; // Ditto
  1716. m_Dib.bmiHeader.biPlanes = 1;
  1717. m_Dib.bmiHeader.biBitCount = 8;
  1718. m_Dib.bmiHeader.biCompression = BI_RGB;
  1719. m_Dib.bmiHeader.biSizeImage = 0;
  1720. m_Dib.bmiHeader.biXPelsPerMeter = 96;
  1721. m_Dib.bmiHeader.biYPelsPerMeter = 96;
  1722. m_Dib.bmiHeader.biClrUsed = 0;
  1723. m_Dib.bmiHeader.biClrImportant = 256; // MAX_MODPALETTECOLORS;
  1724. m_n4BitPalette[0] = (BYTE)m_nTextColor;
  1725. m_n4BitPalette[4] = MODCOLOR_SEPSHADOW;
  1726. m_n4BitPalette[12] = MODCOLOR_SEPFACE;
  1727. m_n4BitPalette[14] = MODCOLOR_SEPHILITE;
  1728. m_n4BitPalette[15] = (BYTE)m_nBkColor;
  1729. }
  1730. void CFastBitmap::Blit(HDC hdc, int x, int y, int cx, int cy)
  1731. {
  1732. SetDIBitsToDevice( hdc,
  1733. x,
  1734. y,
  1735. cx,
  1736. cy,
  1737. 0,
  1738. m_Dib.bmiHeader.biHeight - cy,
  1739. 0,
  1740. m_Dib.bmiHeader.biHeight,
  1741. &m_Dib.DibBits[0],
  1742. (LPBITMAPINFO)&m_Dib,
  1743. DIB_RGB_COLORS);
  1744. }
  1745. void CFastBitmap::SetColor(UINT nIndex, COLORREF cr)
  1746. {
  1747. if (nIndex < 256)
  1748. {
  1749. m_Dib.bmiColors[nIndex].rgbRed = GetRValue(cr);
  1750. m_Dib.bmiColors[nIndex].rgbGreen = GetGValue(cr);
  1751. m_Dib.bmiColors[nIndex].rgbBlue = GetBValue(cr);
  1752. }
  1753. }
  1754. void CFastBitmap::SetAllColors(UINT nBaseIndex, UINT nColors, COLORREF *pcr)
  1755. {
  1756. for (UINT i=0; i<nColors; i++)
  1757. {
  1758. SetColor(nBaseIndex+i, pcr[i]);
  1759. }
  1760. }
  1761. void CFastBitmap::SetBlendColor(COLORREF cr)
  1762. {
  1763. UINT r = GetRValue(cr);
  1764. UINT g = GetGValue(cr);
  1765. UINT b = GetBValue(cr);
  1766. for (UINT i=0; i<BLEND_OFFSET; i++)
  1767. {
  1768. UINT m = (m_Dib.bmiColors[i].rgbRed >> 2)
  1769. + (m_Dib.bmiColors[i].rgbGreen >> 1)
  1770. + (m_Dib.bmiColors[i].rgbBlue >> 2);
  1771. m_Dib.bmiColors[i|BLEND_OFFSET].rgbRed = static_cast<BYTE>((m + r)>>1);
  1772. m_Dib.bmiColors[i|BLEND_OFFSET].rgbGreen = static_cast<BYTE>((m + g)>>1);
  1773. m_Dib.bmiColors[i|BLEND_OFFSET].rgbBlue = static_cast<BYTE>((m + b)>>1);
  1774. }
  1775. }
  1776. // Monochrome 4-bit bitmap (0=text, !0 = back)
  1777. void CFastBitmap::TextBlt(int x, int y, int cx, int cy, int srcx, int srcy, MODPLUGDIB *lpdib)
  1778. {
  1779. const uint8 *psrc;
  1780. BYTE *pdest;
  1781. UINT x1, x2;
  1782. int srcwidth, srcinc;
  1783. m_n4BitPalette[0] = (BYTE)m_nTextColor;
  1784. m_n4BitPalette[15] = (BYTE)m_nBkColor;
  1785. if (x < 0)
  1786. {
  1787. cx += x;
  1788. x = 0;
  1789. }
  1790. if (y < 0)
  1791. {
  1792. cy += y;
  1793. y = 0;
  1794. }
  1795. if ((x >= m_Dib.bmiHeader.biWidth) || (y >= m_Dib.bmiHeader.biHeight)) return;
  1796. if (x+cx >= m_Dib.bmiHeader.biWidth) cx = m_Dib.bmiHeader.biWidth - x;
  1797. if (y+cy >= m_Dib.bmiHeader.biHeight) cy = m_Dib.bmiHeader.biHeight - y;
  1798. if (!lpdib) lpdib = m_pTextDib;
  1799. if ((cx <= 0) || (cy <= 0) || (!lpdib)) return;
  1800. srcwidth = (lpdib->bmiHeader.biWidth+1) >> 1;
  1801. srcinc = srcwidth;
  1802. if (((int)lpdib->bmiHeader.biHeight) > 0)
  1803. {
  1804. srcy = lpdib->bmiHeader.biHeight - 1 - srcy;
  1805. srcinc = -srcinc;
  1806. }
  1807. x1 = srcx & 1;
  1808. x2 = x1 + cx;
  1809. pdest = &m_Dib.DibBits[((m_Dib.bmiHeader.biHeight - 1 - y) << m_nXShiftFactor) + x];
  1810. psrc = lpdib->lpDibBits + (srcx >> 1) + (srcy * srcwidth);
  1811. for (int iy=0; iy<cy; iy++)
  1812. {
  1813. uint8 *p = pdest;
  1814. UINT ix = x1;
  1815. if (ix&1)
  1816. {
  1817. UINT b = psrc[ix >> 1];
  1818. *p++ = m_n4BitPalette[b & 0x0F]+m_nBlendOffset;
  1819. ix++;
  1820. }
  1821. while (ix+1 < x2)
  1822. {
  1823. UINT b = psrc[ix >> 1];
  1824. p[0] = m_n4BitPalette[b >> 4]+m_nBlendOffset;
  1825. p[1] = m_n4BitPalette[b & 0x0F]+m_nBlendOffset;
  1826. ix+=2;
  1827. p+=2;
  1828. }
  1829. if (x2&1)
  1830. {
  1831. UINT b = psrc[ix >> 1];
  1832. *p++ = m_n4BitPalette[b >> 4]+m_nBlendOffset;
  1833. }
  1834. pdest -= m_Dib.bmiHeader.biWidth;
  1835. psrc += srcinc;
  1836. }
  1837. }
  1838. void CFastBitmap::SetSize(int x, int y)
  1839. {
  1840. if(x > 4)
  1841. {
  1842. // Compute the required shift factor for obtaining a power-of-two bitmap width
  1843. m_nXShiftFactor = 1;
  1844. x--;
  1845. while(x >>= 1)
  1846. {
  1847. m_nXShiftFactor++;
  1848. }
  1849. } else
  1850. {
  1851. // Bitmaps rows are aligned to 4 bytes, so let this bitmap be exactly 4 pixels wide.
  1852. m_nXShiftFactor = 2;
  1853. }
  1854. x = (1 << m_nXShiftFactor);
  1855. if(m_Dib.DibBits.size() != static_cast<size_t>(y << m_nXShiftFactor)) m_Dib.DibBits.resize(y << m_nXShiftFactor);
  1856. m_Dib.bmiHeader.biWidth = x;
  1857. m_Dib.bmiHeader.biHeight = y;
  1858. }
  1859. ///////////////////////////////////////////////////////////////////////////////////
  1860. //
  1861. // DirectX Plugins
  1862. //
  1863. void CTrackApp::InitializeDXPlugins()
  1864. {
  1865. m_pPluginManager = new CVstPluginManager;
  1866. const size_t numPlugins = GetSettings().Read<int32>(U_("VST Plugins"), U_("NumPlugins"), 0);
  1867. bool maskCrashes = TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes;
  1868. std::vector<VSTPluginLib *> nonFoundPlugs;
  1869. const mpt::PathString failedPlugin = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), U_("FailedPlugin"), P_(""));
  1870. CDialog pluginScanDlg;
  1871. CWnd *textWnd = nullptr;
  1872. DWORD64 scanStart = Util::GetTickCount64();
  1873. // Read tags for built-in plugins
  1874. for(auto plug : *m_pPluginManager)
  1875. {
  1876. mpt::ustring key = MPT_UFORMAT("Plugin{}{}.Tags")(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2));
  1877. plug->tags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), key, mpt::ustring());
  1878. }
  1879. // Restructured plugin cache
  1880. if(TrackerSettings::Instance().PreviousSettingsVersion < MPT_V("1.27.00.15"))
  1881. {
  1882. DeleteFile(m_szPluginCacheFileName.AsNative().c_str());
  1883. GetPluginCache().ForgetAll();
  1884. }
  1885. m_pPluginManager->reserve(numPlugins);
  1886. auto plugIDFormat = MPT_UFORMAT("Plugin{}");
  1887. auto scanFormat = MPT_CFORMAT("Scanning Plugin {} / {}...\n{}");
  1888. auto tagFormat = MPT_UFORMAT("Plugin{}.Tags");
  1889. for(size_t plug = 0; plug < numPlugins; plug++)
  1890. {
  1891. mpt::PathString plugPath = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), plugIDFormat(plug), mpt::PathString());
  1892. if(!plugPath.empty())
  1893. {
  1894. plugPath = PathInstallRelativeToAbsolute(plugPath);
  1895. if(!pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 2000)
  1896. {
  1897. // If this is taking too long, show the user what they're waiting for.
  1898. pluginScanDlg.Create(IDD_SCANPLUGINS, gpSplashScreen);
  1899. pluginScanDlg.ShowWindow(SW_SHOW);
  1900. pluginScanDlg.CenterWindow(gpSplashScreen);
  1901. textWnd = pluginScanDlg.GetDlgItem(IDC_SCANTEXT);
  1902. } else if(pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 30)
  1903. {
  1904. textWnd->SetWindowText(scanFormat(plug + 1, numPlugins + 1, plugPath));
  1905. MSG msg;
  1906. while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1907. {
  1908. ::TranslateMessage(&msg);
  1909. ::DispatchMessage(&msg);
  1910. }
  1911. scanStart = Util::GetTickCount64();
  1912. }
  1913. if(plugPath == failedPlugin)
  1914. {
  1915. GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin"));
  1916. const CString text = _T("The following plugin has previously crashed OpenMPT during initialisation:\n\n") + failedPlugin.ToCString() + _T("\n\nDo you still want to load it?");
  1917. if(Reporting::Confirm(text, false, true, &pluginScanDlg) == cnfNo)
  1918. {
  1919. continue;
  1920. }
  1921. }
  1922. mpt::ustring plugTags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), tagFormat(plug), mpt::ustring());
  1923. bool plugFound = true;
  1924. VSTPluginLib *lib = m_pPluginManager->AddPlugin(plugPath, maskCrashes, plugTags, true, &plugFound);
  1925. if(!plugFound && lib != nullptr)
  1926. {
  1927. nonFoundPlugs.push_back(lib);
  1928. }
  1929. if(lib != nullptr && lib->libraryName == P_("MIDI Input Output") && lib->pluginId1 == PLUGMAGIC('V','s','t','P') && lib->pluginId2 == PLUGMAGIC('M','M','I','D'))
  1930. {
  1931. // This appears to be an old version of our MIDI I/O plugin, which is now built right into the main executable.
  1932. m_pPluginManager->RemovePlugin(lib);
  1933. }
  1934. }
  1935. }
  1936. GetPluginCache().Flush();
  1937. if(pluginScanDlg.m_hWnd)
  1938. {
  1939. pluginScanDlg.DestroyWindow();
  1940. }
  1941. if(!nonFoundPlugs.empty())
  1942. {
  1943. PlugNotFoundDialog(nonFoundPlugs, nullptr).DoModal();
  1944. }
  1945. }
  1946. void CTrackApp::UninitializeDXPlugins()
  1947. {
  1948. if(!m_pPluginManager) return;
  1949. #ifndef NO_PLUGINS
  1950. size_t plugIndex = 0;
  1951. for(auto plug : *m_pPluginManager)
  1952. {
  1953. if(!plug->isBuiltIn)
  1954. {
  1955. mpt::PathString plugPath = plug->dllPath;
  1956. if(theApp.IsPortableMode())
  1957. {
  1958. plugPath = PathAbsoluteToInstallRelative(plugPath);
  1959. }
  1960. theApp.GetSettings().Write<mpt::PathString>(U_("VST Plugins"), MPT_UFORMAT("Plugin{}")(plugIndex), plugPath);
  1961. theApp.GetSettings().Write(U_("VST Plugins"), MPT_UFORMAT("Plugin{}.Tags")(plugIndex), plug->tags);
  1962. plugIndex++;
  1963. } else
  1964. {
  1965. mpt::ustring key = MPT_UFORMAT("Plugin{}{}.Tags")(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2));
  1966. theApp.GetSettings().Write(U_("VST Plugins"), key, plug->tags);
  1967. }
  1968. }
  1969. theApp.GetSettings().Write(U_("VST Plugins"), U_("NumPlugins"), static_cast<uint32>(plugIndex));
  1970. #endif // NO_PLUGINS
  1971. delete m_pPluginManager;
  1972. m_pPluginManager = nullptr;
  1973. }
  1974. ///////////////////////////////////////////////////////////////////////////////////
  1975. // Internet-related functions
  1976. bool CTrackApp::OpenURL(const char *url)
  1977. {
  1978. if(!url) return false;
  1979. return OpenURL(mpt::PathString::FromUTF8(url));
  1980. }
  1981. bool CTrackApp::OpenURL(const std::string &url)
  1982. {
  1983. return OpenURL(mpt::PathString::FromUTF8(url));
  1984. }
  1985. bool CTrackApp::OpenURL(const CString &url)
  1986. {
  1987. return OpenURL(mpt::ToUnicode(url));
  1988. }
  1989. bool CTrackApp::OpenURL(const mpt::ustring &url)
  1990. {
  1991. return OpenURL(mpt::PathString::FromUnicode(url));
  1992. }
  1993. bool CTrackApp::OpenURL(const mpt::PathString &lpszURL)
  1994. {
  1995. if(!lpszURL.empty() && theApp.m_pMainWnd)
  1996. {
  1997. if(reinterpret_cast<INT_PTR>(ShellExecute(
  1998. theApp.m_pMainWnd->m_hWnd,
  1999. _T("open"),
  2000. lpszURL.AsNative().c_str(),
  2001. NULL,
  2002. NULL,
  2003. SW_SHOW)) >= 32)
  2004. {
  2005. return true;
  2006. }
  2007. }
  2008. return false;
  2009. }
  2010. CString CTrackApp::GetResamplingModeName(ResamplingMode mode, int length, bool addTaps)
  2011. {
  2012. CString result;
  2013. switch(mode)
  2014. {
  2015. case SRCMODE_NEAREST:
  2016. result = (length > 1) ? _T("No Interpolation") : _T("None") ;
  2017. break;
  2018. case SRCMODE_LINEAR:
  2019. result = _T("Linear");
  2020. break;
  2021. case SRCMODE_CUBIC:
  2022. result = _T("Cubic");
  2023. break;
  2024. case SRCMODE_SINC8:
  2025. result = _T("Sinc");
  2026. break;
  2027. case SRCMODE_SINC8LP:
  2028. result = _T("Sinc");
  2029. break;
  2030. default:
  2031. MPT_ASSERT_NOTREACHED();
  2032. break;
  2033. }
  2034. if(Resampling::HasAA(mode))
  2035. {
  2036. result += (length > 1) ? _T(" + Low-Pass") : _T(" + LP");
  2037. }
  2038. if(addTaps)
  2039. {
  2040. result += MPT_CFORMAT(" ({} tap{})")(Resampling::Length(mode), (Resampling::Length(mode) != 1) ? CString(_T("s")) : CString(_T("")));
  2041. }
  2042. return result;
  2043. }
  2044. mpt::ustring CTrackApp::GetFriendlyMIDIPortName(const mpt::ustring &deviceName, bool isInputPort, bool addDeviceName)
  2045. {
  2046. auto friendlyName = GetSettings().Read<mpt::ustring>(isInputPort ? U_("MIDI Input Ports") : U_("MIDI Output Ports"), deviceName, deviceName);
  2047. if(friendlyName.empty())
  2048. return deviceName;
  2049. else if(addDeviceName && friendlyName != deviceName)
  2050. return friendlyName + UL_(" (") + deviceName + UL_(")");
  2051. else
  2052. return friendlyName;
  2053. }
  2054. CString CTrackApp::GetFriendlyMIDIPortName(const CString &deviceName, bool isInputPort, bool addDeviceName)
  2055. {
  2056. return mpt::ToCString(GetFriendlyMIDIPortName(mpt::ToUnicode(deviceName), isInputPort, addDeviceName));
  2057. }
  2058. OPENMPT_NAMESPACE_END