1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413 |
- /*
- * MPTrack.cpp
- * -----------
- * Purpose: OpenMPT core application class.
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Mptrack.h"
- #include "Mainfrm.h"
- #include "IPCWindow.h"
- #include "InputHandler.h"
- #include "Childfrm.h"
- #include "Moddoc.h"
- #include "ModDocTemplate.h"
- #include "Globals.h"
- #include "../soundlib/Dlsbank.h"
- #include "../common/version.h"
- #include "../test/test.h"
- #include "UpdateCheck.h"
- #include "../common/mptStringBuffer.h"
- #include "ExceptionHandler.h"
- #include "CloseMainDialog.h"
- #include "PlugNotFoundDlg.h"
- #include "AboutDialog.h"
- #include "AutoSaver.h"
- #include "FileDialog.h"
- #include "Image.h"
- #include "BuildVariants.h"
- #include "../common/ComponentManager.h"
- #include "WelcomeDialog.h"
- #include "openmpt/sounddevice/SoundDeviceManager.hpp"
- #include "WineSoundDeviceStub.h"
- #include "../soundlib/plugins/PluginManager.h"
- #include "MPTrackWine.h"
- #include "MPTrackUtil.h"
- #if MPT_MSVC_AT_LEAST(2022, 2) && MPT_MSVC_BEFORE(2022, 3)
- // Work-around <https://developercommunity.visualstudio.com/t/warning-C4311-in-MFC-header-afxrecovery/10041328>,
- // see <https://developercommunity.visualstudio.com/t/Compiler-warnings-after-upgrading-to-17/10036311#T-N10061908>.
- template <class ARG_KEY>
- AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key);
- template <>
- AFX_INLINE UINT AFXAPI HashKey<CDocument*>(CDocument *key)
- {
- // (algorithm copied from STL hash in xfunctional)
- #pragma warning(suppress: 4302) // 'type cast' : truncation
- #pragma warning(suppress: 4311) // pointer truncation
- ldiv_t HashVal = ldiv((long)(CDocument*)key, 127773);
- HashVal.rem = 16807 * HashVal.rem - 2836 * HashVal.quot;
- if(HashVal.rem < 0)
- HashVal.rem += 2147483647;
- return ((UINT)HashVal.rem);
- }
- #endif
- #include <afxdatarecovery.h>
- // GDI+
- #include <atlbase.h>
- #define max(a, b) (((a) > (b)) ? (a) : (b))
- #define min(a, b) (((a) < (b)) ? (a) : (b))
- #if MPT_COMPILER_MSVC
- #pragma warning(push)
- #pragma warning(disable : 4458) // declaration of 'x' hides class member
- #endif
- #include <gdiplus.h>
- #if MPT_COMPILER_MSVC
- #pragma warning(pop)
- #endif
- #undef min
- #undef max
- #if MPT_COMPILER_MSVC
- #define _CRTDBG_MAP_ALLOC
- #include <stdlib.h>
- #include <crtdbg.h>
- #endif
- OPENMPT_NAMESPACE_BEGIN
- /////////////////////////////////////////////////////////////////////////////
- // The one and only CTrackApp object
- CTrackApp theApp;
- const TCHAR *szSpecialNoteNamesMPT[] = {_T("PCs"), _T("PC"), _T("~~ (Note Fade)"), _T("^^ (Note Cut)"), _T("== (Note Off)")};
- const TCHAR *szSpecialNoteShortDesc[] = {_T("Param Control (Smooth)"), _T("Param Control"), _T("Note Fade"), _T("Note Cut"), _T("Note Off")};
- // Make sure that special note arrays include string for every note.
- static_assert(NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1 == mpt::array_size<decltype(szSpecialNoteNamesMPT)>::size);
- static_assert(mpt::array_size<decltype(szSpecialNoteShortDesc)>::size == mpt::array_size<decltype(szSpecialNoteNamesMPT)>::size);
- const char *szHexChar = "0123456789ABCDEF";
- #ifdef MPT_WITH_ASIO
- class ComponentASIO
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentASIO, "ASIO")
- public:
- ComponentASIO() = default;
- virtual ~ComponentASIO() = default;
- };
- #endif // MPT_WITH_ASIO
- #if defined(MPT_WITH_DIRECTSOUND)
- class ComponentDirectSound
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentDirectSound, "DirectSound")
- public:
- ComponentDirectSound() = default;
- virtual ~ComponentDirectSound() = default;
- };
- #endif // MPT_WITH_DIRECTSOUND
- #if defined(MPT_WITH_PORTAUDIO)
- class ComponentPortAudio
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentPortAudio, "PortAudio")
- public:
- ComponentPortAudio() = default;
- virtual ~ComponentPortAudio() = default;
- };
- #endif // MPT_WITH_PORTAUDIO
- #if defined(MPT_WITH_PULSEAUDIO)
- class ComponentPulseaudio
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentPulseaudio, "Pulseaudio")
- public:
- ComponentPulseaudio() = default;
- virtual ~ComponentPulseaudio() = default;
- };
- #endif // MPT_WITH_PULSEAUDIO
- #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_WITH_PULSEAUDIOSIMPLE)
- class ComponentPulseaudioSimple
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentPulseaudioSimple, "PulseaudioSimple")
- public:
- ComponentPulseaudioSimple() = default;
- virtual ~ComponentPulseaudioSimple() = default;
- };
- #endif // MPT_WITH_PULSEAUDIO && MPT_WITH_PULSEAUDIOSIMPLE
- #if defined(MPT_WITH_RTAUDIO)
- class ComponentRtAudio
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentRtAudio, "RtAudio")
- public:
- ComponentRtAudio() = default;
- virtual ~ComponentRtAudio() = default;
- };
- #endif // MPT_WITH_RTAUDIO
- #if MPT_OS_WINDOWS
- class ComponentWaveOut
- : public ComponentBuiltin
- {
- MPT_DECLARE_COMPONENT_MEMBERS(ComponentWaveOut, "WaveOut")
- public:
- ComponentWaveOut() = default;
- virtual ~ComponentWaveOut() = default;
- };
- #endif // MPT_OS_WINDOWS
- struct AllSoundDeviceComponents
- {
- #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_ENABLE_PULSEAUDIO_FULL)
- ComponentHandle<ComponentPulseaudio> m_Pulseaudio;
- #endif // MPT_WITH_PULSEAUDIO && MPT_ENABLE_PULSEAUDIO_FULL
- #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_WITH_PULSEAUDIOSIMPLE)
- ComponentHandle<ComponentPulseaudioSimple> m_PulseaudioSimple;
- #endif // MPT_WITH_PULSEAUDIO && MPT_WITH_PULSEAUDIOSIMPLE
- #if MPT_OS_WINDOWS
- ComponentHandle<ComponentWaveOut> m_WaveOut;
- #endif // MPT_OS_WINDOWS
- #if defined(MPT_WITH_DIRECTSOUND)
- ComponentHandle<ComponentDirectSound> m_DirectSound;
- #endif // MPT_WITH_DIRECTSOUND
- #ifdef MPT_WITH_ASIO
- ComponentHandle<ComponentASIO> m_ASIO;
- #endif // MPT_WITH_ASIO
- #ifdef MPT_WITH_PORTAUDIO
- ComponentHandle<ComponentPortAudio> m_PortAudio;
- #endif // MPT_WITH_PORTAUDIO
- #ifdef MPT_WITH_RTAUDIO
- ComponentHandle<ComponentRtAudio> m_RtAudio;
- #endif // MPT_WITH_RTAUDIO
- operator SoundDevice::EnabledBackends() const
- {
- SoundDevice::EnabledBackends result;
- #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_ENABLE_PULSEAUDIO_FULL)
- result.Pulseaudio = IsComponentAvailable(m_PulseAudio);
- #endif // MPT_WITH_PULSEAUDIO && MPT_ENABLE_PULSEAUDIO_FULL
- #if defined(MPT_WITH_PULSEAUDIO) && defined(MPT_WITH_PULSEAUDIOSIMPLE)
- result.PulseaudioSimple = IsComponentAvailable(m_PulseAudioSimple);
- #endif // MPT_WITH_PULSEAUDIO && MPT_WITH_PULSEAUDIOSIMPLE
- #if MPT_OS_WINDOWS
- result.WaveOut = IsComponentAvailable(m_WaveOut);
- #endif // MPT_OS_WINDOWS
- #if defined(MPT_WITH_DIRECTSOUND)
- result.DirectSound = IsComponentAvailable(m_DirectSound);
- #endif // MPT_WITH_DIRECTSOUND
- #ifdef MPT_WITH_ASIO
- result.ASIO = IsComponentAvailable(m_ASIO);
- #endif // MPT_WITH_ASIO
- #ifdef MPT_WITH_PORTAUDIO
- result.PortAudio = IsComponentAvailable(m_PortAudio);
- #endif // MPT_WITH_PORTAUDIO
- #ifdef MPT_WITH_RTAUDIO
- result.RtAudio = IsComponentAvailable(m_RtAudio);
- #endif // MPT_WITH_RTAUDIO
- return result;
- }
- };
- void CTrackApp::OnFileCloseAll()
- {
- if(!(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_NOCLOSEDIALOG))
- {
- // Show modified documents window
- CloseMainDialog dlg;
- if(dlg.DoModal() != IDOK)
- {
- return;
- }
- }
- for(auto &doc : GetOpenDocuments())
- {
- doc->SafeFileClose();
- }
- }
- void CTrackApp::OnUpdateAnyDocsOpen(CCmdUI *cmd)
- {
- cmd->Enable(!GetModDocTemplate()->empty());
- }
- int CTrackApp::GetOpenDocumentCount() const
- {
- return static_cast<int>(GetModDocTemplate()->size());
- }
- // Retrieve a list of all open modules.
- std::vector<CModDoc *> CTrackApp::GetOpenDocuments() const
- {
- std::vector<CModDoc *> documents;
- if(auto *pDocTmpl = GetModDocTemplate())
- {
- POSITION pos = pDocTmpl->GetFirstDocPosition();
- CDocument *pDoc;
- while((pos != nullptr) && ((pDoc = pDocTmpl->GetNextDoc(pos)) != nullptr))
- {
- documents.push_back(dynamic_cast<CModDoc *>(pDoc));
- }
- }
- return documents;
- }
- /////////////////////////////////////////////////////////////////////////////
- // Command Line options
- class CMPTCommandLineInfo: public CCommandLineInfo
- {
- public:
- std::vector<mpt::PathString> m_fileNames;
- bool m_noDls = false, m_noPlugins = false, m_noAssembly = false, m_noSysCheck = false, m_noWine = false,
- m_portable = false, m_noCrashHandler = false, m_debugCrashHandler = false, m_sharedInstance = false;
- #ifdef ENABLE_TESTS
- bool m_noTests = false;
- #endif
- public:
- void ParseParam(LPCTSTR param, BOOL isFlag, BOOL isLast) override
- {
- if(isFlag)
- {
- if(!lstrcmpi(param, _T("nologo"))) { m_bShowSplash = FALSE; return; }
- if(!lstrcmpi(param, _T("nodls"))) { m_noDls = true; return; }
- if(!lstrcmpi(param, _T("noplugs"))) { m_noPlugins = true; return; }
- if(!lstrcmpi(param, _T("portable"))) { m_portable = true; return; }
- if(!lstrcmpi(param, _T("fullMemDump"))) { ExceptionHandler::fullMemDump = true; return; }
- if(!lstrcmpi(param, _T("noAssembly"))) { m_noAssembly = true; return; }
- if(!lstrcmpi(param, _T("noSysCheck"))) { m_noSysCheck = true; return; }
- if(!lstrcmpi(param, _T("noWine"))) { m_noWine = true; return; }
- if(!lstrcmpi(param, _T("noCrashHandler"))) { m_noCrashHandler = true; return; }
- if(!lstrcmpi(param, _T("DebugCrashHandler"))) { m_debugCrashHandler = true; return; }
- if(!lstrcmpi(param, _T("shared"))) { m_sharedInstance = true; return; }
- #ifdef ENABLE_TESTS
- if (!lstrcmpi(param, _T("noTests"))) { m_noTests = true; return; }
- #endif
- } else
- {
- m_fileNames.push_back(mpt::PathString::FromNative(param));
- if(m_nShellCommand == FileNew) m_nShellCommand = FileOpen;
- }
- CCommandLineInfo::ParseParam(param, isFlag, isLast);
- }
- };
- // Splash Screen
- static void StartSplashScreen();
- static void StopSplashScreen();
- static void TimeoutSplashScreen();
- /////////////////////////////////////////////////////////////////////////////
- // Midi Library
- MidiLibrary CTrackApp::midiLibrary;
- void CTrackApp::ImportMidiConfig(const mpt::PathString &filename, bool hideWarning)
- {
- if(filename.empty()) return;
- if(CDLSBank::IsDLSBank(filename))
- {
- ConfirmAnswer result = cnfYes;
- if(!hideWarning)
- {
- result = Reporting::Confirm("You are about to replace the current MIDI library:\n"
- "Do you want to replace only the missing instruments? (recommended)",
- "Warning", true);
- }
- if(result == cnfCancel) return;
- const bool replaceAll = (result == cnfNo);
- CDLSBank dlsbank;
- if (dlsbank.Open(filename))
- {
- for(uint32 ins = 0; ins < 256; ins++)
- {
- if(replaceAll || midiLibrary[ins].empty())
- {
- uint32 prog = (ins < 128) ? ins : 0xFF;
- uint32 key = (ins < 128) ? 0xFF : ins & 0x7F;
- uint32 bank = (ins < 128) ? 0 : F_INSTRUMENT_DRUMS;
- if (dlsbank.FindInstrument(ins >= 128, bank, prog, key))
- {
- midiLibrary[ins] = filename;
- }
- }
- }
- }
- return;
- }
- IniFileSettingsContainer file(filename);
- ImportMidiConfig(file, filename.GetPath());
- }
- static mpt::PathString GetUltraSoundPatchDir(SettingsContainer &file, const mpt::ustring &iniSection, const mpt::PathString &path, bool forgetSettings)
- {
- mpt::PathString patchDir = file.Read<mpt::PathString>(iniSection, U_("PatchDir"), {});
- if(forgetSettings)
- file.Forget(U_("Ultrasound"), U_("PatchDir"));
- if(patchDir.empty() || patchDir == P_(".\\"))
- patchDir = path;
- if(!patchDir.empty())
- patchDir.EnsureTrailingSlash();
- return patchDir;
- }
- void CTrackApp::ImportMidiConfig(SettingsContainer &file, const mpt::PathString &path, bool forgetSettings)
- {
- const mpt::PathString patchDir = GetUltraSoundPatchDir(file, U_("Ultrasound"), path, forgetSettings);
- for(uint32 prog = 0; prog < 256; prog++)
- {
- mpt::ustring key = MPT_UFORMAT("{}{}")((prog < 128) ? U_("Midi") : U_("Perc"), prog & 0x7F);
- mpt::PathString filename = file.Read<mpt::PathString>(U_("Midi Library"), key, mpt::PathString());
- // Check for ULTRASND.INI
- if(filename.empty())
- {
- mpt::ustring section = (prog < 128) ? UL_("Melodic Patches") : UL_("Drum Patches");
- key = mpt::ufmt::val(prog & 0x7f);
- filename = file.Read<mpt::PathString>(section, key, mpt::PathString());
- if(forgetSettings) file.Forget(section, key);
- if(filename.empty())
- {
- section = (prog < 128) ? UL_("Melodic Bank 0") : UL_("Drum Bank 0");
- filename = file.Read<mpt::PathString>(section, key, mpt::PathString());
- if(forgetSettings) file.Forget(section, key);
- }
- const mpt::PathString localPatchDir = GetUltraSoundPatchDir(file, section, patchDir, forgetSettings);
- if(!filename.empty())
- {
- filename = localPatchDir + filename + P_(".pat");
- }
- }
- if(!filename.empty())
- {
- filename = theApp.PathInstallRelativeToAbsolute(filename);
- midiLibrary[prog] = filename;
- }
- }
- }
- void CTrackApp::ExportMidiConfig(const mpt::PathString &filename)
- {
- if(filename.empty()) return;
- IniFileSettingsContainer file(filename);
- ExportMidiConfig(file);
- }
- void CTrackApp::ExportMidiConfig(SettingsContainer &file)
- {
- for(uint32 prog = 0; prog < 256; prog++) if (!midiLibrary[prog].empty())
- {
- mpt::PathString szFileName = midiLibrary[prog];
- if(!szFileName.empty())
- {
- if(theApp.IsPortableMode())
- szFileName = theApp.PathAbsoluteToInstallRelative(szFileName);
- mpt::ustring key = MPT_UFORMAT("{}{}")((prog < 128) ? U_("Midi") : U_("Perc"), prog & 0x7F);
- file.Write<mpt::PathString>(U_("Midi Library"), key, szFileName);
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // DLS Banks support
- std::vector<std::unique_ptr<CDLSBank>> CTrackApp::gpDLSBanks;
- struct CompareLessPathStringNoCase
- {
- inline bool operator()(const mpt::PathString &l, const mpt::PathString &r) const
- {
- return mpt::PathString::CompareNoCase(l, r) < 0;
- }
- };
- std::future<std::vector<std::unique_ptr<CDLSBank>>> CTrackApp::LoadDefaultDLSBanks()
- {
- std::set<mpt::PathString, CompareLessPathStringNoCase> paths;
- uint32 numBanks = theApp.GetSettings().Read<uint32>(U_("DLS Banks"), U_("NumBanks"), 0);
- for(uint32 i = 0; i < numBanks; i++)
- {
- mpt::PathString path = theApp.GetSettings().Read<mpt::PathString>(U_("DLS Banks"), MPT_UFORMAT("Bank{}")(i + 1), mpt::PathString());
- paths.insert(theApp.PathInstallRelativeToAbsolute(path));
- }
- HKEY key;
- if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\DirectMusic"), 0, KEY_READ, &key) == ERROR_SUCCESS)
- {
- DWORD dwRegType = REG_SZ;
- DWORD dwSize = 0;
- if(RegQueryValueEx(key, _T("GMFilePath"), NULL, &dwRegType, nullptr, &dwSize) == ERROR_SUCCESS && dwSize > 0)
- {
- std::vector<TCHAR> filenameT(dwSize / sizeof(TCHAR));
- if(RegQueryValueEx(key, _T("GMFilePath"), NULL, &dwRegType, reinterpret_cast<LPBYTE>(filenameT.data()), &dwSize) == ERROR_SUCCESS)
- {
- mpt::winstring filenamestr = ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<mpt::winstring>(filenameT.data(), dwSize);
- std::vector<TCHAR> filenameExpanded(::ExpandEnvironmentStrings(filenamestr.c_str(), nullptr, 0));
- ::ExpandEnvironmentStrings(filenamestr.c_str(), filenameExpanded.data(), static_cast<DWORD>(filenameExpanded.size()));
- auto filename = mpt::PathString::FromNative(filenameExpanded.data());
- ImportMidiConfig(filename, true);
- paths.insert(std::move(filename));
- }
- }
- RegCloseKey(key);
- }
- if(paths.empty())
- return {};
- return std::async(std::launch::async, [paths = std::move(paths)]()
- {
- std::vector<std::unique_ptr<CDLSBank>> banks;
- banks.reserve(paths.size());
- for(const auto &filename : paths)
- {
- if(filename.empty() || !CDLSBank::IsDLSBank(filename))
- continue;
- try
- {
- auto bank = std::make_unique<CDLSBank>();
- if(bank->Open(filename))
- {
- banks.push_back(std::move(bank));
- continue;
- }
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- } catch(const std::exception &)
- {
- }
- }
- // Avoid the overhead of future::wait_for(0) until future::is_ready is finally non-experimental
- theApp.m_scannedDlsBanksAvailable = true;
- return banks;
- });
- }
- void CTrackApp::SaveDefaultDLSBanks()
- {
- uint32 nBanks = 0;
- for(const auto &bank : gpDLSBanks)
- {
- if(!bank || bank->GetFileName().empty())
- continue;
- mpt::PathString path = bank->GetFileName();
- if(theApp.IsPortableMode())
- {
- path = theApp.PathAbsoluteToInstallRelative(path);
- }
- mpt::ustring key = MPT_UFORMAT("Bank{}")(nBanks + 1);
- theApp.GetSettings().Write<mpt::PathString>(U_("DLS Banks"), key, path);
- nBanks++;
- }
- theApp.GetSettings().Write<uint32>(U_("DLS Banks"), U_("NumBanks"), nBanks);
- }
- void CTrackApp::RemoveDLSBank(UINT nBank)
- {
- if(nBank < gpDLSBanks.size())
- gpDLSBanks[nBank] = nullptr;
- }
- bool CTrackApp::AddDLSBank(const mpt::PathString &filename)
- {
- if(filename.empty() || !CDLSBank::IsDLSBank(filename)) return false;
- // Check for dupes
- for(const auto &bank : gpDLSBanks)
- {
- if(bank && !mpt::PathString::CompareNoCase(filename, bank->GetFileName()))
- return true;
- }
- try
- {
- auto bank = std::make_unique<CDLSBank>();
- if(bank->Open(filename))
- {
- gpDLSBanks.push_back(std::move(bank));
- return true;
- }
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- } catch(const std::exception &)
- {
- }
- return false;
- }
- size_t CTrackApp::AddScannedDLSBanks()
- {
- if(!m_scannedDlsBanks.valid())
- return 0;
- size_t numAdded = 0;
- auto scannedBanks = m_scannedDlsBanks.get();
- gpDLSBanks.reserve(gpDLSBanks.size() + scannedBanks.size());
- const size_t existingBanks = gpDLSBanks.size();
- for(auto &bank : scannedBanks)
- {
- if(std::find_if(gpDLSBanks.begin(), gpDLSBanks.begin() + existingBanks, [&bank](const auto &other) { return other && *bank == *other; }) == gpDLSBanks.begin() + existingBanks)
- {
- gpDLSBanks.push_back(std::move(bank));
- numAdded++;
- }
- }
- return numAdded;
- }
- /////////////////////////////////////////////////////////////////////////////
- // CTrackApp
- MODTYPE CTrackApp::m_nDefaultDocType = MOD_TYPE_IT;
- BEGIN_MESSAGE_MAP(CTrackApp, CWinApp)
- //{{AFX_MSG_MAP(CTrackApp)
- ON_COMMAND(ID_FILE_NEW, &CTrackApp::OnFileNew)
- ON_COMMAND(ID_FILE_NEWMOD, &CTrackApp::OnFileNewMOD)
- ON_COMMAND(ID_FILE_NEWS3M, &CTrackApp::OnFileNewS3M)
- ON_COMMAND(ID_FILE_NEWXM, &CTrackApp::OnFileNewXM)
- ON_COMMAND(ID_FILE_NEWIT, &CTrackApp::OnFileNewIT)
- ON_COMMAND(ID_NEW_MPT, &CTrackApp::OnFileNewMPT)
- ON_COMMAND(ID_FILE_OPEN, &CTrackApp::OnFileOpen)
- ON_COMMAND(ID_FILE_CLOSEALL, &CTrackApp::OnFileCloseAll)
- ON_COMMAND(ID_APP_ABOUT, &CTrackApp::OnAppAbout)
- ON_UPDATE_COMMAND_UI(ID_FILE_CLOSEALL, &CTrackApp::OnUpdateAnyDocsOpen)
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- /////////////////////////////////////////////////////////////////////////////
- // CTrackApp construction
- CTrackApp::CTrackApp()
- {
- m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART | AFX_RESTART_MANAGER_REOPEN_PREVIOUS_FILES;
- }
- class OpenMPTDataRecoveryHandler
- : public CDataRecoveryHandler
- {
- public:
- OpenMPTDataRecoveryHandler(_In_ DWORD dwRestartManagerSupportFlags, _In_ int nAutosaveInterval)
- : CDataRecoveryHandler(dwRestartManagerSupportFlags, nAutosaveInterval)
- {
- return;
- }
- ~OpenMPTDataRecoveryHandler() override = default;
- BOOL SaveOpenDocumentList() override
- {
- BOOL bRet = TRUE; // return FALSE if document list non-empty and not saved
- POSITION posAutosave = m_mapDocNameToAutosaveName.GetStartPosition();
- if (posAutosave != NULL)
- {
- bRet = FALSE;
- // Save the open document list and associated autosave info to the registry
- IniFileSettingsBackend ini(theApp.GetConfigPath() + P_("restart.") + mpt::PathString::FromCString(GetRestartIdentifier()) + P_(".ini"));
- ini.ConvertToUnicode();
- int32 count = 0;
- while (posAutosave != NULL)
- {
- CString strDocument, strAutosave;
- m_mapDocNameToAutosaveName.GetNextAssoc(posAutosave, strDocument, strAutosave);
- ini.WriteSetting({ U_("RestartDocument"), mpt::ufmt::val(count) }, SettingValue(mpt::ToUnicode(strDocument)));
- ini.WriteSetting({ U_("RestartAutosave"), mpt::ufmt::val(count) }, SettingValue(mpt::ToUnicode(strAutosave)));
- count++;
- }
- ini.WriteSetting({ U_("Restart"), U_("Count") }, SettingValue(count));
- return TRUE;
- }
- return bRet;
- }
- BOOL ReadOpenDocumentList() override
- {
- BOOL bRet = FALSE; // return TRUE only if at least one document was found
- {
- IniFileSettingsBackend ini(theApp.GetConfigPath() + P_("restart.") + mpt::PathString::FromCString(GetRestartIdentifier()) + P_(".ini"));
- int32 count = ini.ReadSetting({ U_("Restart"), U_("Count") }, SettingValue(0));
- for(int32 index = 0; index < count; ++index)
- {
- mpt::ustring document = ini.ReadSetting({ U_("RestartDocument"), mpt::ufmt::val(index) }, SettingValue(U_("")));
- mpt::ustring autosave = ini.ReadSetting({ U_("RestartAutosave"), mpt::ufmt::val(index) }, SettingValue(U_("")));
- if(!document.empty())
- {
- m_mapDocNameToAutosaveName[mpt::ToCString(document)] = mpt::ToCString(autosave);
- bRet = TRUE;
- }
- }
- ini.RemoveSection(U_("Restart"));
- ini.RemoveSection(U_("RestartDocument"));
- ini.RemoveSection(U_("RestartAutosave"));
- }
- DeleteFile((theApp.GetConfigPath() + P_("restart.") + mpt::PathString::FromCString(GetRestartIdentifier()) + P_(".ini")).AsNative().c_str());
- return bRet;
- }
- };
- CDataRecoveryHandler *CTrackApp::GetDataRecoveryHandler()
- {
- static BOOL bTriedOnce = FALSE;
- // Since the application restart and application recovery are supported only on Windows
- // Vista and above, we don't need a recovery handler on Windows versions less than Vista.
- if (SupportsRestartManager() || SupportsApplicationRecovery())
- {
- if (!bTriedOnce && m_pDataRecoveryHandler == NULL)
- {
- m_pDataRecoveryHandler = new OpenMPTDataRecoveryHandler(m_dwRestartManagerSupportFlags, m_nAutosaveInterval);
- if (!m_pDataRecoveryHandler->Initialize())
- {
- delete m_pDataRecoveryHandler;
- m_pDataRecoveryHandler = NULL;
- }
- }
- }
- bTriedOnce = TRUE;
- return m_pDataRecoveryHandler;
- }
- void CTrackApp::AddToRecentFileList(LPCTSTR lpszPathName)
- {
- AddToRecentFileList(mpt::PathString::FromCString(lpszPathName));
- }
- void CTrackApp::AddToRecentFileList(const mpt::PathString &path)
- {
- RemoveMruItem(path);
- TrackerSettings::Instance().mruFiles.insert(TrackerSettings::Instance().mruFiles.begin(), path);
- if(TrackerSettings::Instance().mruFiles.size() > TrackerSettings::Instance().mruListLength)
- {
- TrackerSettings::Instance().mruFiles.resize(TrackerSettings::Instance().mruListLength);
- }
- CMainFrame::GetMainFrame()->UpdateMRUList();
- }
- void CTrackApp::RemoveMruItem(const size_t item)
- {
- if(item < TrackerSettings::Instance().mruFiles.size())
- {
- TrackerSettings::Instance().mruFiles.erase(TrackerSettings::Instance().mruFiles.begin() + item);
- CMainFrame::GetMainFrame()->UpdateMRUList();
- }
- }
- void CTrackApp::RemoveMruItem(const mpt::PathString &path)
- {
- auto &mruFiles = TrackerSettings::Instance().mruFiles;
- for(auto i = mruFiles.begin(); i != mruFiles.end(); i++)
- {
- if(!mpt::PathString::CompareNoCase(*i, path))
- {
- mruFiles.erase(i);
- break;
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // CTrackApp initialization
- namespace Tracker
- {
- mpt::recursive_mutex_with_lock_count & GetGlobalMutexRef()
- {
- return theApp.GetGlobalMutexRef();
- }
- } // namespace Tracker
- class ComponentManagerSettings
- : public IComponentManagerSettings
- {
- private:
- TrackerSettings &conf;
- mpt::PathString configPath;
- public:
- ComponentManagerSettings(TrackerSettings &conf, const mpt::PathString &configPath)
- : conf(conf)
- , configPath(configPath)
- {
- return;
- }
- bool LoadOnStartup() const override
- {
- return conf.ComponentsLoadOnStartup;
- }
- bool KeepLoaded() const override
- {
- return conf.ComponentsKeepLoaded;
- }
- bool IsBlocked(const std::string &key) const override
- {
- return conf.IsComponentBlocked(key);
- }
- mpt::PathString Path() const override
- {
- if(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()).empty())
- {
- return mpt::PathString();
- }
- return configPath + P_("Components\\") + mpt::PathString::FromUnicode(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture())) + P_("\\");
- }
- };
- // Move a config file called fileName from the App's directory (or one of its sub directories specified by subDir) to
- // %APPDATA%. If specified, it will be renamed to newFileName. Existing files are never overwritten.
- // Returns true on success.
- bool CTrackApp::MoveConfigFile(const mpt::PathString &fileName, mpt::PathString subDir, mpt::PathString newFileName)
- {
- const mpt::PathString oldPath = GetInstallPath() + subDir + fileName;
- mpt::PathString newPath = GetConfigPath() + subDir;
- if(!newFileName.empty())
- newPath += newFileName;
- else
- newPath += fileName;
- if(!newPath.IsFile() && oldPath.IsFile())
- {
- return MoveFile(oldPath.AsNative().c_str(), newPath.AsNative().c_str()) != 0;
- }
- return false;
- }
- // Set up paths were configuration data is written to. Set overridePortable to true if application's own directory should always be used.
- void CTrackApp::SetupPaths(bool overridePortable)
- {
- // First, determine if the executable is installed in multi-arch mode or in the old standard mode.
- bool modeMultiArch = false;
- bool modeSourceProject = false;
- const mpt::PathString exePath = mpt::GetExecutablePath();
- auto exePathComponents = mpt::String::Split<mpt::ustring>(exePath.GetDir().WithoutTrailingSlash().ToUnicode(), P_("\\").ToUnicode());
- if(exePathComponents.size() >= 2)
- {
- if(exePathComponents[exePathComponents.size()-1] == mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))
- {
- if(exePathComponents[exePathComponents.size()-2] == U_("bin"))
- {
- modeMultiArch = true;
- }
- }
- }
- // Check if we are running from the source tree.
- if(!modeMultiArch && exePathComponents.size() >= 4)
- {
- if(exePathComponents[exePathComponents.size()-1] == mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))
- {
- if(exePathComponents[exePathComponents.size()-4] == U_("bin"))
- {
- modeSourceProject = true;
- }
- }
- }
- if(modeSourceProject)
- {
- m_InstallPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\") + P_("..\\") + P_("..\\"));
- m_InstallBinPath = mpt::GetAbsolutePath(exePath + P_("..\\"));
- m_InstallBinArchPath = exePath;
- m_InstallPkgPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\") + P_("..\\") + P_("..\\packageTemplate\\"));
- } else if(modeMultiArch)
- {
- m_InstallPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\"));
- m_InstallBinPath = mpt::GetAbsolutePath(exePath + P_("..\\"));
- m_InstallBinArchPath = exePath;
- m_InstallPkgPath = mpt::GetAbsolutePath(exePath + P_("..\\") + P_("..\\"));
- } else
- {
- m_InstallPath = exePath;
- m_InstallBinPath = exePath;
- m_InstallBinArchPath = exePath;
- m_InstallPkgPath = exePath;
- }
- // Determine paths, portable mode, first run. Do not yet update any state.
- mpt::PathString configPathPortable = (modeSourceProject ? exePath : m_InstallPath); // config path in portable mode
- mpt::PathString configPathUser; // config path in default non-portable mode
- {
- // Try to find a nice directory where we should store our settings (default: %APPDATA%)
- TCHAR dir[MAX_PATH] = { 0 };
- if((SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, dir) == S_OK)
- || (SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, dir) == S_OK))
- {
- // Store our app settings in %APPDATA% or "My Documents"
- configPathUser = mpt::PathString::FromNative(dir) + P_("\\OpenMPT\\");
- }
- }
- // Check if the user has configured portable mode.
- bool configInstallPortable = false;
- mpt::PathString portableFlagFilename = (configPathPortable + P_("OpenMPT.portable"));
- bool configPortableFlag = portableFlagFilename.IsFile();
- configInstallPortable = configInstallPortable || configPortableFlag;
- // before 1.29.00.13:
- configInstallPortable = configInstallPortable || (GetPrivateProfileInt(_T("Paths"), _T("UseAppDataDirectory"), 1, (configPathPortable + P_("mptrack.ini")).AsNative().c_str()) == 0);
- // convert to new style
- if(configInstallPortable && !configPortableFlag)
- {
- mpt::SafeOutputFile f(portableFlagFilename);
- }
- // Determine portable mode.
- bool portableMode = overridePortable || configInstallPortable || configPathUser.empty();
- // Update config dir
- m_ConfigPath = portableMode ? configPathPortable : configPathUser;
- // Set up default file locations
- m_szConfigFileName = m_ConfigPath + P_("mptrack.ini"); // config file
- m_szPluginCacheFileName = m_ConfigPath + P_("plugin.cache"); // plugin cache
- // Force use of custom ini file rather than windowsDir\executableName.ini
- if(m_pszProfileName)
- {
- free((void *)m_pszProfileName);
- }
- m_pszProfileName = _tcsdup(m_szConfigFileName.ToCString());
- m_bInstallerMode = !modeSourceProject && !portableMode;
- m_bPortableMode = portableMode;
- m_bSourceTreeMode = modeSourceProject;
- }
- void CTrackApp::CreatePaths()
- {
- // Create missing diretories
- if(!IsPortableMode())
- {
- if(!m_ConfigPath.IsDirectory())
- {
- CreateDirectory(m_ConfigPath.AsNative().c_str(), 0);
- }
- }
- if(!(GetConfigPath() + P_("Components")).IsDirectory())
- {
- CreateDirectory((GetConfigPath() + P_("Components")).AsNative().c_str(), 0);
- }
- if(!(GetConfigPath() + P_("Components\\") + mpt::PathString::FromUnicode(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))).IsDirectory())
- {
- CreateDirectory((GetConfigPath() + P_("Components\\") + mpt::PathString::FromUnicode(mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()))).AsNative().c_str(), 0);
- }
- // Handle updates from old versions.
- if(!IsPortableMode())
- {
- // Move the config files if they're still in the old place.
- MoveConfigFile(P_("mptrack.ini"));
- MoveConfigFile(P_("plugin.cache"));
-
- // Import old tunings
- const mpt::PathString oldTunings = GetInstallPath() + P_("tunings\\");
- if(oldTunings.IsDirectory())
- {
- const mpt::PathString searchPattern = oldTunings + P_("*.*");
- WIN32_FIND_DATA FindFileData;
- HANDLE hFind;
- hFind = FindFirstFile(searchPattern.AsNative().c_str(), &FindFileData);
- if(hFind != INVALID_HANDLE_VALUE)
- {
- do
- {
- MoveConfigFile(mpt::PathString::FromNative(FindFileData.cFileName), P_("tunings\\"));
- } while(FindNextFile(hFind, &FindFileData) != 0);
- }
- FindClose(hFind);
- RemoveDirectory(oldTunings.AsNative().c_str());
- }
- }
- }
- #if !defined(MPT_BUILD_RETRO)
- bool CTrackApp::CheckSystemSupport()
- {
- const mpt::ustring lf = U_("\n");
- const mpt::ustring url = Build::GetURL(Build::Url::Download);
- if(!BuildVariants::ProcessorCanRunCurrentBuild())
- {
- mpt::ustring text;
- text += U_("Your CPU is too old to run this variant of OpenMPT.") + lf;
- text += U_("OpenMPT will exit now.") + lf;
- Reporting::Error(text, "OpenMPT");
- return false;
- }
- if(BuildVariants::IsKnownSystem() && !BuildVariants::SystemCanRunCurrentBuild())
- {
- mpt::ustring text;
- text += U_("Your system does not meet the minimum requirements for this variant of OpenMPT.") + lf;
- if(mpt::OS::Windows::IsOriginal())
- {
- text += U_("OpenMPT will exit now.") + lf;
- }
- Reporting::Error(text, "OpenMPT");
- if(mpt::OS::Windows::IsOriginal())
- {
- return false;
- } else
- {
- return true; // may work though
- }
- }
- return true;
- }
- #endif // !MPT_BUILD_RETRO
- BOOL CTrackApp::InitInstanceEarly(CMPTCommandLineInfo &cmdInfo)
- {
- // The first step of InitInstance, always executed without any crash handler.
- #ifndef UNICODE
- if(MessageBox(NULL,
- _T("STOP!!!") _T("\n")
- _T("This is an ANSI (as opposed to a UNICODE) build of OpenMPT.") _T("\n")
- _T("\n")
- _T("ANSI builds are NOT SUPPORTED and WILL CAUSE CORRUPTION of the OpenMPT configuration and exhibit other unintended behaviour.") _T("\n")
- _T("\n")
- _T("Please use an official build of OpenMPT or compile 'OpenMPT.sln' instead of 'OpenMPT-ANSI.sln'.") _T("\n")
- _T("\n")
- _T("Continue starting OpenMPT anyway?") _T("\n"),
- _T("OpenMPT"), MB_ICONSTOP | MB_YESNO| MB_DEFBUTTON2)
- != IDYES)
- {
- ExitProcess(1);
- }
- #endif
- // Call the base class.
- // This is required for MFC RestartManager integration.
- if(!CWinApp::InitInstance())
- {
- return FALSE;
- }
- #if MPT_COMPILER_MSVC
- _CrtSetDebugFillThreshold(0); // Disable buffer filling in secure enhanced CRT functions.
- #endif
- // Avoid e.g. audio APIs trying to load wdmaud.drv from arbitrary working directory
- ::SetCurrentDirectory(mpt::GetExecutablePath().AsNative().c_str());
- // Initialize OLE MFC support
- BOOL oleinit = AfxOleInit();
- ASSERT(oleinit != FALSE); // no MPT_ASSERT here!
- // Parse command line for standard shell commands, DDE, file open
- ParseCommandLine(cmdInfo);
- // Set up paths to store configuration in
- SetupPaths(cmdInfo.m_portable);
- if(cmdInfo.m_sharedInstance && IPCWindow::SendToIPC(cmdInfo.m_fileNames))
- {
- ExitProcess(0);
- }
- // Initialize DocManager (for DDE)
- // requires mpt::PathString
- ASSERT(nullptr == m_pDocManager); // no MPT_ASSERT here!
- m_pDocManager = new CModDocManager();
- if(IsDebuggerPresent() && cmdInfo.m_debugCrashHandler)
- {
- ExceptionHandler::useAnyCrashHandler = true;
- ExceptionHandler::useImplicitFallbackSEH = false;
- ExceptionHandler::useExplicitSEH = true;
- ExceptionHandler::handleStdTerminate = true;
- ExceptionHandler::handleMfcExceptions = true;
- ExceptionHandler::debugExceptionHandler = true;
- } else if(IsDebuggerPresent() || cmdInfo.m_noCrashHandler)
- {
- ExceptionHandler::useAnyCrashHandler = false;
- ExceptionHandler::useImplicitFallbackSEH = false;
- ExceptionHandler::useExplicitSEH = false;
- ExceptionHandler::handleStdTerminate = false;
- ExceptionHandler::handleMfcExceptions = false;
- ExceptionHandler::debugExceptionHandler = false;
- } else
- {
- ExceptionHandler::useAnyCrashHandler = true;
- ExceptionHandler::useImplicitFallbackSEH = true;
- ExceptionHandler::useExplicitSEH = true;
- ExceptionHandler::handleStdTerminate = true;
- ExceptionHandler::handleMfcExceptions = true;
- ExceptionHandler::debugExceptionHandler = false;
- }
- return TRUE;
- }
- BOOL CTrackApp::InitInstanceImpl(CMPTCommandLineInfo &cmdInfo)
- {
- m_GuiThreadId = GetCurrentThreadId();
- mpt::log::Trace::SetThreadId(mpt::log::Trace::ThreadKindGUI, m_GuiThreadId);
- if(ExceptionHandler::useAnyCrashHandler)
- {
- ExceptionHandler::Register();
- }
- // Start loading
- BeginWaitCursor();
- MPT_LOG_GLOBAL(LogInformation, "", U_("OpenMPT Start"));
- // create the tracker-global random device
- m_RD = std::make_unique<mpt::random_device>();
- // make the device available to non-tracker-only code
- mpt::set_global_random_device(m_RD.get());
- // create and seed the traker-global best PRNG with the random device
- m_PRNG = std::make_unique<mpt::thread_safe_prng<mpt::default_prng> >(mpt::make_prng<mpt::default_prng>(RandomDevice()));
- // make the best PRNG available to non-tracker-only code
- mpt::set_global_prng(m_PRNG.get());
- // additionally, seed the C rand() PRNG, just in case any third party library calls rand()
- mpt::crand::reseed(RandomDevice());
- m_Gdiplus = std::make_unique<GdiplusRAII>();
- if(cmdInfo.m_noWine)
- {
- mpt::OS::Windows::PreventWineDetection();
- }
- #ifdef MPT_ENABLE_ARCH_INTRINSICS
- if(!cmdInfo.m_noAssembly)
- {
- CPU::EnableAvailableFeatures();
- }
- #endif // MPT_ENABLE_ARCH_INTRINSICS
- if(mpt::OS::Windows::IsWine())
- {
- SetWineVersion(std::make_shared<mpt::OS::Wine::VersionContext>());
- }
- // Create paths to store configuration in
- CreatePaths();
- m_pSettingsIniFile = new IniFileSettingsBackend(m_szConfigFileName);
- m_pSettings = new SettingsContainer(m_pSettingsIniFile);
- m_pDebugSettings = new DebugSettings(*m_pSettings);
- m_pTrackerSettings = new TrackerSettings(*m_pSettings);
- MPT_LOG_GLOBAL(LogInformation, "", U_("OpenMPT settings initialized."));
- if(ExceptionHandler::useAnyCrashHandler)
- {
- ExceptionHandler::ConfigureSystemHandler();
- }
- if(TrackerSettings::Instance().MiscUseSingleInstance && IPCWindow::SendToIPC(cmdInfo.m_fileNames))
- {
- ExitProcess(0);
- }
- IPCWindow::Open(m_hInstance);
- m_pSongSettingsIniFile = new IniFileSettingsBackend(GetConfigPath() + P_("SongSettings.ini"));
- m_pSongSettings = new SettingsContainer(m_pSongSettingsIniFile);
- m_pComponentManagerSettings = new ComponentManagerSettings(TrackerSettings::Instance(), GetConfigPath());
- m_pPluginCache = new IniFileSettingsContainer(m_szPluginCacheFileName);
- // Load standard INI file options (without MRU)
- // requires SetupPaths+CreatePaths called
- LoadStdProfileSettings(0);
- // Set process priority class
- #ifndef _DEBUG
- SetPriorityClass(GetCurrentProcess(), TrackerSettings::Instance().MiscProcessPriorityClass);
- #endif
- // Dynamic DPI-awareness. Some users might want to disable DPI-awareness because of their DPI-unaware VST plugins.
- bool setDPI = false;
- // For Windows 10, Creators Update (1703) and newer
- {
- mpt::Library user32(mpt::LibraryPath::System(P_("user32")));
- if (user32.IsValid())
- {
- enum MPT_DPI_AWARENESS_CONTEXT
- {
- MPT_DPI_AWARENESS_CONTEXT_UNAWARE = -1,
- MPT_DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2,
- MPT_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3,
- MPT_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4,
- MPT_DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5, // 1809 update and newer
- };
- using PSETPROCESSDPIAWARENESSCONTEXT = BOOL(WINAPI *)(HANDLE);
- PSETPROCESSDPIAWARENESSCONTEXT SetProcessDpiAwarenessContext = nullptr;
- if(user32.Bind(SetProcessDpiAwarenessContext, "SetProcessDpiAwarenessContext"))
- {
- if (TrackerSettings::Instance().highResUI)
- {
- setDPI = (SetProcessDpiAwarenessContext(HANDLE(MPT_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) == TRUE);
- } else
- {
- if (SetProcessDpiAwarenessContext(HANDLE(MPT_DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) == TRUE)
- setDPI = true;
- else
- setDPI = (SetProcessDpiAwarenessContext(HANDLE(MPT_DPI_AWARENESS_CONTEXT_UNAWARE)) == TRUE);
- }
- }
- }
- }
- // For Windows 8.1 and newer
- if(!setDPI)
- {
- mpt::Library shcore(mpt::LibraryPath::System(P_("SHCore")));
- if(shcore.IsValid())
- {
- using PSETPROCESSDPIAWARENESS = HRESULT (WINAPI *)(int);
- PSETPROCESSDPIAWARENESS SetProcessDPIAwareness = nullptr;
- if(shcore.Bind(SetProcessDPIAwareness, "SetProcessDpiAwareness"))
- {
- setDPI = (SetProcessDPIAwareness(TrackerSettings::Instance().highResUI ? 2 : 0) == S_OK);
- }
- }
- }
- // For Vista and newer
- if(!setDPI && TrackerSettings::Instance().highResUI)
- {
- mpt::Library user32(mpt::LibraryPath::System(P_("user32")));
- if(user32.IsValid())
- {
- using PSETPROCESSDPIAWARE = BOOL (WINAPI *)();
- PSETPROCESSDPIAWARE SetProcessDPIAware = nullptr;
- if(user32.Bind(SetProcessDPIAware, "SetProcessDPIAware"))
- {
- SetProcessDPIAware();
- }
- }
- }
- // create main MDI Frame window
- CMainFrame* pMainFrame = new CMainFrame();
- if(!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;
- m_pMainWnd = pMainFrame;
- // Show splash screen
- if(cmdInfo.m_bShowSplash && TrackerSettings::Instance().m_ShowSplashScreen)
- {
- StartSplashScreen();
- }
- // create component manager
- ComponentManager::Init(*m_pComponentManagerSettings);
- // load components
- ComponentManager::Instance()->Startup();
- // Wine Support
- if(mpt::OS::Windows::IsWine())
- {
- WineIntegration::Initialize();
- WineIntegration::Load();
- }
- // Register document templates
- m_pModTemplate = new CModDocTemplate(
- IDR_MODULETYPE,
- RUNTIME_CLASS(CModDoc),
- RUNTIME_CLASS(CChildFrame), // custom MDI child frame
- RUNTIME_CLASS(CModControlView));
- AddDocTemplate(m_pModTemplate);
- // Load Midi Library
- ImportMidiConfig(theApp.GetSettings(), {}, true);
- // Enable DDE Execute open
- // requires m_pDocManager
- EnableShellOpen();
- // Enable drag/drop open
- m_pMainWnd->DragAcceptFiles();
- // Load sound APIs
- // requires TrackerSettings
- m_pAllSoundDeviceComponents = std::make_unique<AllSoundDeviceComponents>();
- auto GetSysInfo = [&]()
- {
- if(mpt::OS::Windows::IsWine())
- {
- return SoundDevice::SysInfo(mpt::osinfo::get_class(), mpt::OS::Windows::Version::Current(), mpt::OS::Windows::IsWine(), GetWineVersion()->HostClass(), GetWineVersion()->Version());
- }
- 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());
- };
- SoundDevice::SysInfo sysInfo = GetSysInfo();
- SoundDevice::AppInfo appInfo;
- appInfo.SetName(U_("OpenMPT"));
- appInfo.SetHWND(*m_pMainWnd);
- appInfo.BoostedThreadPriorityXP = TrackerSettings::Instance().SoundBoostedThreadPriority;
- appInfo.BoostedThreadMMCSSClassVista = TrackerSettings::Instance().SoundBoostedThreadMMCSSClass;
- appInfo.BoostedThreadRealtimePosix = TrackerSettings::Instance().SoundBoostedThreadRealtimePosix;
- appInfo.BoostedThreadNicenessPosix = TrackerSettings::Instance().SoundBoostedThreadNicenessPosix;
- appInfo.BoostedThreadRtprioPosix = TrackerSettings::Instance().SoundBoostedThreadRtprioPosix;
- appInfo.MaskDriverCrashes = TrackerSettings::Instance().SoundMaskDriverCrashes;
- appInfo.AllowDeferredProcessing = TrackerSettings::Instance().SoundAllowDeferredProcessing;
- std::vector<std::shared_ptr<SoundDevice::IDevicesEnumerator>> deviceEnumerators = SoundDevice::Manager::GetEnabledEnumerators(*m_pAllSoundDeviceComponents);
- deviceEnumerators.push_back(std::static_pointer_cast<SoundDevice::IDevicesEnumerator>(std::make_shared<SoundDevice::DevicesEnumerator<SoundDevice::SoundDeviceStub>>()));
- m_pSoundDevicesManager = std::make_unique<SoundDevice::Manager>(m_GlobalLogger, sysInfo, appInfo, std::move(deviceEnumerators));
- m_pTrackerSettings->MigrateOldSoundDeviceSettings(*m_pSoundDevicesManager);
- // Set default note names
- CSoundFile::SetDefaultNoteNames();
- // Load DLS Banks
- if (!cmdInfo.m_noDls)
- m_scannedDlsBanks = LoadDefaultDLSBanks();
- // Initialize Plugins
- if (!cmdInfo.m_noPlugins) InitializeDXPlugins();
- // Initialize CMainFrame
- pMainFrame->Initialize();
- InitCommonControls();
- pMainFrame->m_InputHandler->UpdateMainMenu();
- // Dispatch commands specified on the command line
- if(cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew)
- {
- // When not asked to open any existing file,
- // we do not want to open an empty new one on startup.
- cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
- }
- bool shellSuccess = false;
- if(cmdInfo.m_fileNames.empty())
- {
- shellSuccess = ProcessShellCommand(cmdInfo) != FALSE;
- } else
- {
- cmdInfo.m_nShellCommand = CCommandLineInfo::FileOpen;
- for(const auto &filename : cmdInfo.m_fileNames)
- {
- cmdInfo.m_strFileName = filename.ToCString();
- shellSuccess |= ProcessShellCommand(cmdInfo) != FALSE;
- }
- }
- if(!shellSuccess)
- {
- EndWaitCursor();
- StopSplashScreen();
- return FALSE;
- }
- pMainFrame->ShowWindow(m_nCmdShow);
- pMainFrame->UpdateWindow();
- EndWaitCursor();
- // Perform startup tasks.
- #if !defined(MPT_BUILD_RETRO)
- // Check whether we are running the best build for the given system.
- if(!cmdInfo.m_noSysCheck)
- {
- if(!CheckSystemSupport())
- {
- StopSplashScreen();
- return FALSE;
- }
- }
- #endif // !MPT_BUILD_RETRO
- if(TrackerSettings::Instance().FirstRun)
- {
- // On high-DPI devices, automatically upscale pattern font
- FontSetting font = TrackerSettings::Instance().patternFont;
- font.size = Clamp(Util::GetDPIy(m_pMainWnd->m_hWnd) / 96 - 1, 0, 9);
- TrackerSettings::Instance().patternFont = font;
- new WelcomeDlg(m_pMainWnd);
- } else
- {
- #if !defined(MPT_BUILD_RETRO)
- bool deprecatedSoundDevice = GetSoundDevicesManager()->FindDeviceInfo(TrackerSettings::Instance().GetSoundDeviceIdentifier()).IsDeprecated();
- bool showSettings = deprecatedSoundDevice && !TrackerSettings::Instance().m_SoundDeprecatedDeviceWarningShown && (Reporting::Confirm(
- U_("You have currently selected a sound device which is deprecated. MME/WaveOut support will be removed in a future OpenMPT version.\n") +
- U_("The recommended sound device type is WASAPI.\n") +
- U_("Do you want to change your sound device settings now?"),
- U_("OpenMPT - Deprecated sound device")
- ) == cnfYes);
- if(showSettings)
- {
- TrackerSettings::Instance().m_SoundDeprecatedDeviceWarningShown = true;
- m_pMainWnd->PostMessage(WM_COMMAND, ID_VIEW_OPTIONS);
- }
- #endif // !MPT_BUILD_RETRO
- }
- #ifdef ENABLE_TESTS
- if(!cmdInfo.m_noTests)
- Test::DoTests();
- #endif
- if(TrackerSettings::Instance().m_SoundSettingsOpenDeviceAtStartup)
- {
- pMainFrame->InitPreview();
- pMainFrame->PreparePreview(NOTE_NOTECUT, 0);
- pMainFrame->PlayPreview();
- }
- if(!TrackerSettings::Instance().FirstRun)
- {
- #if defined(MPT_ENABLE_UPDATE)
- if(CUpdateCheck::IsSuitableUpdateMoment())
- {
- CUpdateCheck::DoAutoUpdateCheck();
- }
- #endif // MPT_ENABLE_UPDATE
- }
- return TRUE;
- }
- BOOL CTrackApp::InitInstance()
- {
- CMPTCommandLineInfo cmdInfo;
- if(!InitInstanceEarly(cmdInfo))
- {
- return FALSE;
- }
- return InitInstanceLate(cmdInfo);
- }
- BOOL CTrackApp::InitInstanceLate(CMPTCommandLineInfo &cmdInfo)
- {
- BOOL result = FALSE;
- if(ExceptionHandler::useExplicitSEH)
- {
- // https://support.microsoft.com/en-us/kb/173652
- __try
- {
- result = InitInstanceImpl(cmdInfo);
- } __except(ExceptionHandler::ExceptionFilter(GetExceptionInformation()))
- {
- std::abort();
- }
- } else
- {
- result = InitInstanceImpl(cmdInfo);
- }
- return result;
- }
- int CTrackApp::Run()
- {
- int result = 255;
- if(ExceptionHandler::useExplicitSEH)
- {
- // https://support.microsoft.com/en-us/kb/173652
- __try
- {
- result = CWinApp::Run();
- } __except(ExceptionHandler::ExceptionFilter(GetExceptionInformation()))
- {
- std::abort();
- }
- } else
- {
- result = CWinApp::Run();
- }
- return result;
- }
- LRESULT CTrackApp::ProcessWndProcException(CException * e, const MSG * pMsg)
- {
- if(ExceptionHandler::handleMfcExceptions)
- {
- LRESULT result = 0L; // as per documentation
- if(pMsg)
- {
- if(pMsg->message == WM_COMMAND)
- {
- result = (LRESULT)TRUE; // as per documentation
- }
- }
- if(dynamic_cast<CMemoryException*>(e))
- {
- e->ReportError();
- //ExceptionHandler::UnhandledMFCException(e, pMsg);
- } else
- {
- ExceptionHandler::UnhandledMFCException(e, pMsg);
- }
- return result;
- } else
- {
- return CWinApp::ProcessWndProcException(e, pMsg);
- }
- }
- int CTrackApp::ExitInstance()
- {
- int result = 0;
- if(ExceptionHandler::useExplicitSEH)
- {
- // https://support.microsoft.com/en-us/kb/173652
- __try
- {
- result = ExitInstanceImpl();
- } __except(ExceptionHandler::ExceptionFilter(GetExceptionInformation()))
- {
- std::abort();
- }
- } else
- {
- result = ExitInstanceImpl();
- }
- return result;
- }
- int CTrackApp::ExitInstanceImpl()
- {
- IPCWindow::Close();
- m_pSoundDevicesManager = nullptr;
- m_pAllSoundDeviceComponents = nullptr;
- ExportMidiConfig(theApp.GetSettings());
- AddScannedDLSBanks();
- SaveDefaultDLSBanks();
- gpDLSBanks.clear();
- // Uninitialize Plugins
- UninitializeDXPlugins();
- ComponentManager::Release();
- delete m_pPluginCache;
- m_pPluginCache = nullptr;
- delete m_pComponentManagerSettings;
- m_pComponentManagerSettings = nullptr;
- delete m_pTrackerSettings;
- m_pTrackerSettings = nullptr;
- delete m_pDebugSettings;
- m_pDebugSettings = nullptr;
- delete m_pSettings;
- m_pSettings = nullptr;
- delete m_pSettingsIniFile;
- m_pSettingsIniFile = nullptr;
- delete m_pSongSettings;
- m_pSongSettings = nullptr;
- delete m_pSongSettingsIniFile;
- m_pSongSettingsIniFile = nullptr;
- if(mpt::OS::Windows::IsWine())
- {
- SetWineVersion(nullptr);
- }
- m_Gdiplus.reset();
- mpt::set_global_prng(nullptr);
- m_PRNG.reset();
- mpt::set_global_random_device(nullptr);
- m_RD.reset();
- if(ExceptionHandler::useAnyCrashHandler)
- {
- ExceptionHandler::UnconfigureSystemHandler();
- ExceptionHandler::Unregister();
- }
- return CWinApp::ExitInstance();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // App Messages
- CModDoc *CTrackApp::NewDocument(MODTYPE newType)
- {
- // Build from template
- if(newType == MOD_TYPE_NONE)
- {
- const mpt::PathString templateFile = TrackerSettings::Instance().defaultTemplateFile;
- if(TrackerSettings::Instance().defaultNewFileAction == nfDefaultTemplate && !templateFile.empty())
- {
- // Template file can be either a filename inside one of the preset and user TemplateModules folders, or a full path.
- const mpt::PathString dirs[] = { GetConfigPath() + P_("TemplateModules\\"), GetInstallPath() + P_("TemplateModules\\"), mpt::PathString() };
- for(const auto &dir : dirs)
- {
- if((dir + templateFile).IsFile())
- {
- if(CModDoc *modDoc = static_cast<CModDoc *>(m_pModTemplate->OpenTemplateFile(dir + templateFile)))
- {
- return modDoc;
- }
- }
- }
- }
- // Default module type
- newType = TrackerSettings::Instance().defaultModType;
- // Get active document to make the new module of the same type
- CModDoc *pModDoc = CMainFrame::GetMainFrame()->GetActiveDoc();
- if(pModDoc != nullptr && TrackerSettings::Instance().defaultNewFileAction == nfSameAsCurrent)
- {
- newType = pModDoc->GetSoundFile().GetBestSaveFormat();
- }
- }
- SetDefaultDocType(newType);
- return static_cast<CModDoc *>(m_pModTemplate->OpenDocumentFile(_T("")));
- }
- void CTrackApp::OpenModulesDialog(std::vector<mpt::PathString> &files, const mpt::PathString &overridePath)
- {
- files.clear();
- 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"};
- std::string exts, extsWithoutCommon;
- for(const auto &ext : CSoundFile::GetSupportedExtensions(true))
- {
- const auto filter = std::string("*.") + ext + std::string(";");
- exts += filter;
- if(!mpt::contains(commonExts, ext))
- extsWithoutCommon += filter;
- }
- static int nFilterIndex = 0;
- FileDialog dlg = OpenFileDialog()
- .AllowMultiSelect()
- .ExtensionFilter("All Modules (*.mptm,*.mod,*.xm,*.s3m,*.it,...)|" + exts + ";mod.*"
- "|"
- "Compressed Modules (*.mdz,*.s3z,*.xmz,*.itz,*.mo3,*.oxm,...)|*.mdz;*.s3z;*.xmz;*.itz;*.mdr;*.zip;*.rar;*.lha;*.pma;*.lzs;*.gz;*.mo3;*.oxm"
- "|"
- "ProTracker Modules (*.mod,*.nst)|*.mod;mod.*;*.mdz;*.nst;*.m15;*.stk;*.pt36|"
- "Scream Tracker Modules (*.s3m,*.stm)|*.s3m;*.stm;*.s3z;*.stx|"
- "FastTracker Modules (*.xm)|*.xm;*.xmz|"
- "Impulse Tracker Modules (*.it)|*.it;*.itz|"
- "OpenMPT Modules (*.mptm)|*.mptm;*.mptmz|"
- "Other Modules (*.mtm,*.okt,*.mdl,*.669,*.far,...)|" + extsWithoutCommon + "|"
- "Wave Files (*.wav)|*.wav|"
- "MIDI Files (*.mid,*.rmi)|*.mid;*.rmi;*.smf|"
- "All Files (*.*)|*.*||")
- .WorkingDirectory(overridePath.empty() ? TrackerSettings::Instance().PathSongs.GetWorkingDir() : overridePath)
- .FilterIndex(&nFilterIndex);
- if(!dlg.Show()) return;
- if(overridePath.empty())
- TrackerSettings::Instance().PathSongs.SetWorkingDir(dlg.GetWorkingDirectory());
- files = dlg.GetFilenames();
- }
- void CTrackApp::OnFileOpen()
- {
- FileDialog::PathList files;
- OpenModulesDialog(files);
- for(const auto &file : files)
- {
- OpenDocumentFile(file.ToCString());
- }
- }
- // App command to run the dialog
- void CTrackApp::OnAppAbout()
- {
- if (CAboutDlg::instance) return;
- CAboutDlg::instance = new CAboutDlg();
- CAboutDlg::instance->Create(IDD_ABOUTBOX, m_pMainWnd);
- }
- /////////////////////////////////////////////////////////////////////////////
- // Splash Screen
- class CSplashScreen: public CDialog
- {
- protected:
- std::unique_ptr<Gdiplus::Image> m_Image;
- public:
- ~CSplashScreen();
- BOOL OnInitDialog() override;
- void OnOK() override;
- void OnCancel() override { OnOK(); }
- void OnPaint();
- BOOL OnEraseBkgnd(CDC *) { return TRUE; }
- DECLARE_MESSAGE_MAP()
- };
- BEGIN_MESSAGE_MAP(CSplashScreen, CDialog)
- ON_WM_PAINT()
- ON_WM_ERASEBKGND()
- END_MESSAGE_MAP()
- static CSplashScreen *gpSplashScreen = NULL;
- static DWORD64 gSplashScreenStartTime = 0;
- CSplashScreen::~CSplashScreen()
- {
- gpSplashScreen = nullptr;
- }
- void CSplashScreen::OnPaint()
- {
- CPaintDC dc(this);
- Gdiplus::Graphics gfx(dc);
- CRect rect;
- GetClientRect(&rect);
- gfx.SetInterpolationMode(Gdiplus::InterpolationModeHighQuality);
- gfx.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
- gfx.DrawImage(m_Image.get(), 0, 0, rect.right, rect.bottom);
- CDialog::OnPaint();
- }
- BOOL CSplashScreen::OnInitDialog()
- {
- CDialog::OnInitDialog();
- try
- {
- m_Image = GDIP::LoadPixelImage(GetResource(MAKEINTRESOURCE(IDB_SPLASHNOFOLDFIN), _T("PNG")));
- } catch(const bad_image &)
- {
- return FALSE;
- }
- CRect rect;
- GetWindowRect(&rect);
- const int width = Util::ScalePixels(m_Image->GetWidth(), m_hWnd) / 2;
- const int height = Util::ScalePixels(m_Image->GetHeight(), m_hWnd) / 2;
- SetWindowPos(nullptr,
- rect.left - ((width - rect.Width()) / 2),
- rect.top - ((height - rect.Height()) / 2),
- width,
- height,
- SWP_NOZORDER | SWP_NOCOPYBITS);
- return TRUE;
- }
- void CSplashScreen::OnOK()
- {
- StopSplashScreen();
- }
- static void StartSplashScreen()
- {
- if(!gpSplashScreen)
- {
- gpSplashScreen = new CSplashScreen();
- gpSplashScreen->Create(IDD_SPLASHSCREEN, theApp.m_pMainWnd);
- gpSplashScreen->ShowWindow(SW_SHOW);
- gpSplashScreen->UpdateWindow();
- gpSplashScreen->BeginWaitCursor();
- gSplashScreenStartTime = Util::GetTickCount64();
- }
- }
- static void StopSplashScreen()
- {
- if(gpSplashScreen)
- {
- gpSplashScreen->EndWaitCursor();
- gpSplashScreen->DestroyWindow();
- delete gpSplashScreen;
- gpSplashScreen = nullptr;
- }
- }
- static void TimeoutSplashScreen()
- {
- if(gpSplashScreen)
- {
- if(Util::GetTickCount64() - gSplashScreenStartTime > 100)
- {
- StopSplashScreen();
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // Idle-time processing
- BOOL CTrackApp::OnIdle(LONG lCount)
- {
- BOOL b = CWinApp::OnIdle(lCount);
- TimeoutSplashScreen();
- if(CMainFrame::GetMainFrame())
- {
- CMainFrame::GetMainFrame()->IdleHandlerSounddevice();
- if(m_scannedDlsBanksAvailable)
- {
- if(AddScannedDLSBanks())
- CMainFrame::GetMainFrame()->RefreshDlsBanks();
- }
- }
- // Call plugins idle routine for open editor
- if (m_pPluginManager)
- {
- DWORD curTime = timeGetTime();
- //rewbs.vstCompliance: call @ 50Hz
- if (curTime - m_dwLastPluginIdleCall > 20 || curTime < m_dwLastPluginIdleCall)
- {
- m_pPluginManager->OnIdle();
- m_dwLastPluginIdleCall = curTime;
- }
- }
- return b;
- }
- /////////////////////////////////////////////////////////////////////////////
- // DIB
- RGBQUAD rgb2quad(COLORREF c)
- {
- RGBQUAD r;
- r.rgbBlue = GetBValue(c);
- r.rgbGreen = GetGValue(c);
- r.rgbRed = GetRValue(c);
- r.rgbReserved = 0;
- return r;
- }
- void DibBlt(HDC hdc, int x, int y, int sizex, int sizey, int srcx, int srcy, MODPLUGDIB *lpdib)
- {
- if (!lpdib) return;
- SetDIBitsToDevice( hdc,
- x,
- y,
- sizex,
- sizey,
- srcx,
- lpdib->bmiHeader.biHeight - srcy - sizey,
- 0,
- lpdib->bmiHeader.biHeight,
- lpdib->lpDibBits,
- (LPBITMAPINFO)lpdib,
- DIB_RGB_COLORS);
- }
- MODPLUGDIB *LoadDib(LPCTSTR lpszName)
- {
- mpt::const_byte_span data = GetResource(lpszName, RT_BITMAP);
- if(!data.data())
- {
- return nullptr;
- }
- LPBITMAPINFO p = (LPBITMAPINFO)data.data();
- MODPLUGDIB *pmd = new MODPLUGDIB;
- pmd->bmiHeader = p->bmiHeader;
- for (int i=0; i<16; i++) pmd->bmiColors[i] = p->bmiColors[i];
- LPBYTE lpDibBits = (LPBYTE)p;
- lpDibBits += p->bmiHeader.biSize + 16 * sizeof(RGBQUAD);
- pmd->lpDibBits = lpDibBits;
- return pmd;
- }
- int DrawTextT(HDC hdc, const wchar_t *lpchText, int cchText, LPRECT lprc, UINT format)
- {
- return ::DrawTextW(hdc, lpchText, cchText, lprc, format);
- }
- int DrawTextT(HDC hdc, const char *lpchText, int cchText, LPRECT lprc, UINT format)
- {
- return ::DrawTextA(hdc, lpchText, cchText, lprc, format);
- }
- template<typename Tchar>
- static void DrawButtonRectImpl(HDC hdc, CRect rect, const Tchar *lpszText, bool disabled, bool pushed, DWORD textFlags, uint32 topMargin)
- {
- int width = Util::ScalePixels(1, WindowFromDC(hdc));
- if(width != 1)
- {
- // Draw "real" buttons in Hi-DPI mode
- DrawFrameControl(hdc, rect, DFC_BUTTON, pushed ? (DFCS_PUSHED | DFCS_BUTTONPUSH) : DFCS_BUTTONPUSH);
- } else
- {
- const auto colorHighlight = GetSysColor(COLOR_BTNHIGHLIGHT), colorShadow = GetSysColor(COLOR_BTNSHADOW);
- auto oldpen = SelectPen(hdc, GetStockObject(DC_PEN));
- ::SetDCPenColor(hdc, pushed ? colorShadow : colorHighlight);
- ::FillRect(hdc, rect, GetSysColorBrush(COLOR_BTNFACE));
- ::MoveToEx(hdc, rect.left, rect.bottom - 1, nullptr);
- ::LineTo(hdc, rect.left, rect.top);
- ::LineTo(hdc, rect.right - 1, rect.top);
- ::SetDCPenColor(hdc, pushed ? colorHighlight : colorShadow);
- ::LineTo(hdc, rect.right - 1, rect.bottom - 1);
- ::LineTo(hdc, rect.left, rect.bottom - 1);
- SelectPen(hdc, oldpen);
- }
-
- if(lpszText && lpszText[0])
- {
- rect.DeflateRect(width, width);
- if(pushed)
- {
- rect.top += width;
- rect.left += width;
- }
- ::SetTextColor(hdc, GetSysColor(disabled ? COLOR_GRAYTEXT : COLOR_BTNTEXT));
- ::SetBkMode(hdc, TRANSPARENT);
- rect.top += topMargin;
- auto oldfont = SelectFont(hdc, CMainFrame::GetGUIFont());
- DrawTextT(hdc, lpszText, -1, &rect, textFlags | DT_SINGLELINE | DT_NOPREFIX);
- SelectFont(hdc, oldfont);
- }
- }
- void DrawButtonRect(HDC hdc, const RECT *lpRect, LPCSTR lpszText, BOOL bDisabled, BOOL bPushed, DWORD dwFlags, uint32 topMargin)
- {
- DrawButtonRectImpl(hdc, *lpRect, lpszText, bDisabled, bPushed, dwFlags, topMargin);
- }
- void DrawButtonRect(HDC hdc, const RECT *lpRect, LPCWSTR lpszText, BOOL bDisabled, BOOL bPushed, DWORD dwFlags, uint32 topMargin)
- {
- DrawButtonRectImpl(hdc, *lpRect, lpszText, bDisabled, bPushed, dwFlags, topMargin);
- }
- //////////////////////////////////////////////////////////////////////////////////
- // Misc functions
- void ErrorBox(UINT nStringID, CWnd *parent)
- {
- CString str;
- BOOL resourceLoaded = str.LoadString(nStringID);
- if(!resourceLoaded)
- {
- str.Format(_T("Resource string %u not found."), nStringID);
- }
- MPT_ASSERT(resourceLoaded);
- Reporting::CustomNotification(str, _T("Error!"), MB_OK | MB_ICONERROR, parent);
- }
- CString GetWindowTextString(const CWnd &wnd)
- {
- CString result;
- wnd.GetWindowText(result);
- return result;
- }
- mpt::ustring GetWindowTextUnicode(const CWnd &wnd)
- {
- return mpt::ToUnicode(GetWindowTextString(wnd));
- }
- ////////////////////////////////////////////////////////////////////////////////
- // CFastBitmap 8-bit output / 4-bit input
- // useful for lots of small blits with color mapping
- // combined in one big blit
- void CFastBitmap::Init(MODPLUGDIB *lpTextDib)
- {
- m_nBlendOffset = 0;
- m_pTextDib = lpTextDib;
- MemsetZero(m_Dib.bmiHeader);
- m_nTextColor = 0;
- m_nBkColor = 1;
- m_Dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- m_Dib.bmiHeader.biWidth = 0; // Set later
- m_Dib.bmiHeader.biHeight = 0; // Ditto
- m_Dib.bmiHeader.biPlanes = 1;
- m_Dib.bmiHeader.biBitCount = 8;
- m_Dib.bmiHeader.biCompression = BI_RGB;
- m_Dib.bmiHeader.biSizeImage = 0;
- m_Dib.bmiHeader.biXPelsPerMeter = 96;
- m_Dib.bmiHeader.biYPelsPerMeter = 96;
- m_Dib.bmiHeader.biClrUsed = 0;
- m_Dib.bmiHeader.biClrImportant = 256; // MAX_MODPALETTECOLORS;
- m_n4BitPalette[0] = (BYTE)m_nTextColor;
- m_n4BitPalette[4] = MODCOLOR_SEPSHADOW;
- m_n4BitPalette[12] = MODCOLOR_SEPFACE;
- m_n4BitPalette[14] = MODCOLOR_SEPHILITE;
- m_n4BitPalette[15] = (BYTE)m_nBkColor;
- }
- void CFastBitmap::Blit(HDC hdc, int x, int y, int cx, int cy)
- {
- SetDIBitsToDevice( hdc,
- x,
- y,
- cx,
- cy,
- 0,
- m_Dib.bmiHeader.biHeight - cy,
- 0,
- m_Dib.bmiHeader.biHeight,
- &m_Dib.DibBits[0],
- (LPBITMAPINFO)&m_Dib,
- DIB_RGB_COLORS);
- }
- void CFastBitmap::SetColor(UINT nIndex, COLORREF cr)
- {
- if (nIndex < 256)
- {
- m_Dib.bmiColors[nIndex].rgbRed = GetRValue(cr);
- m_Dib.bmiColors[nIndex].rgbGreen = GetGValue(cr);
- m_Dib.bmiColors[nIndex].rgbBlue = GetBValue(cr);
- }
- }
- void CFastBitmap::SetAllColors(UINT nBaseIndex, UINT nColors, COLORREF *pcr)
- {
- for (UINT i=0; i<nColors; i++)
- {
- SetColor(nBaseIndex+i, pcr[i]);
- }
- }
- void CFastBitmap::SetBlendColor(COLORREF cr)
- {
- UINT r = GetRValue(cr);
- UINT g = GetGValue(cr);
- UINT b = GetBValue(cr);
- for (UINT i=0; i<BLEND_OFFSET; i++)
- {
- UINT m = (m_Dib.bmiColors[i].rgbRed >> 2)
- + (m_Dib.bmiColors[i].rgbGreen >> 1)
- + (m_Dib.bmiColors[i].rgbBlue >> 2);
- m_Dib.bmiColors[i|BLEND_OFFSET].rgbRed = static_cast<BYTE>((m + r)>>1);
- m_Dib.bmiColors[i|BLEND_OFFSET].rgbGreen = static_cast<BYTE>((m + g)>>1);
- m_Dib.bmiColors[i|BLEND_OFFSET].rgbBlue = static_cast<BYTE>((m + b)>>1);
- }
- }
- // Monochrome 4-bit bitmap (0=text, !0 = back)
- void CFastBitmap::TextBlt(int x, int y, int cx, int cy, int srcx, int srcy, MODPLUGDIB *lpdib)
- {
- const uint8 *psrc;
- BYTE *pdest;
- UINT x1, x2;
- int srcwidth, srcinc;
- m_n4BitPalette[0] = (BYTE)m_nTextColor;
- m_n4BitPalette[15] = (BYTE)m_nBkColor;
- if (x < 0)
- {
- cx += x;
- x = 0;
- }
- if (y < 0)
- {
- cy += y;
- y = 0;
- }
- if ((x >= m_Dib.bmiHeader.biWidth) || (y >= m_Dib.bmiHeader.biHeight)) return;
- if (x+cx >= m_Dib.bmiHeader.biWidth) cx = m_Dib.bmiHeader.biWidth - x;
- if (y+cy >= m_Dib.bmiHeader.biHeight) cy = m_Dib.bmiHeader.biHeight - y;
- if (!lpdib) lpdib = m_pTextDib;
- if ((cx <= 0) || (cy <= 0) || (!lpdib)) return;
- srcwidth = (lpdib->bmiHeader.biWidth+1) >> 1;
- srcinc = srcwidth;
- if (((int)lpdib->bmiHeader.biHeight) > 0)
- {
- srcy = lpdib->bmiHeader.biHeight - 1 - srcy;
- srcinc = -srcinc;
- }
- x1 = srcx & 1;
- x2 = x1 + cx;
- pdest = &m_Dib.DibBits[((m_Dib.bmiHeader.biHeight - 1 - y) << m_nXShiftFactor) + x];
- psrc = lpdib->lpDibBits + (srcx >> 1) + (srcy * srcwidth);
- for (int iy=0; iy<cy; iy++)
- {
- uint8 *p = pdest;
- UINT ix = x1;
- if (ix&1)
- {
- UINT b = psrc[ix >> 1];
- *p++ = m_n4BitPalette[b & 0x0F]+m_nBlendOffset;
- ix++;
- }
- while (ix+1 < x2)
- {
- UINT b = psrc[ix >> 1];
- p[0] = m_n4BitPalette[b >> 4]+m_nBlendOffset;
- p[1] = m_n4BitPalette[b & 0x0F]+m_nBlendOffset;
- ix+=2;
- p+=2;
- }
- if (x2&1)
- {
- UINT b = psrc[ix >> 1];
- *p++ = m_n4BitPalette[b >> 4]+m_nBlendOffset;
- }
- pdest -= m_Dib.bmiHeader.biWidth;
- psrc += srcinc;
- }
- }
- void CFastBitmap::SetSize(int x, int y)
- {
- if(x > 4)
- {
- // Compute the required shift factor for obtaining a power-of-two bitmap width
- m_nXShiftFactor = 1;
- x--;
- while(x >>= 1)
- {
- m_nXShiftFactor++;
- }
- } else
- {
- // Bitmaps rows are aligned to 4 bytes, so let this bitmap be exactly 4 pixels wide.
- m_nXShiftFactor = 2;
- }
- x = (1 << m_nXShiftFactor);
- if(m_Dib.DibBits.size() != static_cast<size_t>(y << m_nXShiftFactor)) m_Dib.DibBits.resize(y << m_nXShiftFactor);
- m_Dib.bmiHeader.biWidth = x;
- m_Dib.bmiHeader.biHeight = y;
- }
- ///////////////////////////////////////////////////////////////////////////////////
- //
- // DirectX Plugins
- //
- void CTrackApp::InitializeDXPlugins()
- {
- m_pPluginManager = new CVstPluginManager;
- const size_t numPlugins = GetSettings().Read<int32>(U_("VST Plugins"), U_("NumPlugins"), 0);
- bool maskCrashes = TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes;
- std::vector<VSTPluginLib *> nonFoundPlugs;
- const mpt::PathString failedPlugin = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), U_("FailedPlugin"), P_(""));
- CDialog pluginScanDlg;
- CWnd *textWnd = nullptr;
- DWORD64 scanStart = Util::GetTickCount64();
- // Read tags for built-in plugins
- for(auto plug : *m_pPluginManager)
- {
- mpt::ustring key = MPT_UFORMAT("Plugin{}{}.Tags")(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2));
- plug->tags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), key, mpt::ustring());
- }
- // Restructured plugin cache
- if(TrackerSettings::Instance().PreviousSettingsVersion < MPT_V("1.27.00.15"))
- {
- DeleteFile(m_szPluginCacheFileName.AsNative().c_str());
- GetPluginCache().ForgetAll();
- }
- m_pPluginManager->reserve(numPlugins);
- auto plugIDFormat = MPT_UFORMAT("Plugin{}");
- auto scanFormat = MPT_CFORMAT("Scanning Plugin {} / {}...\n{}");
- auto tagFormat = MPT_UFORMAT("Plugin{}.Tags");
- for(size_t plug = 0; plug < numPlugins; plug++)
- {
- mpt::PathString plugPath = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), plugIDFormat(plug), mpt::PathString());
- if(!plugPath.empty())
- {
- plugPath = PathInstallRelativeToAbsolute(plugPath);
- if(!pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 2000)
- {
- // If this is taking too long, show the user what they're waiting for.
- pluginScanDlg.Create(IDD_SCANPLUGINS, gpSplashScreen);
- pluginScanDlg.ShowWindow(SW_SHOW);
- pluginScanDlg.CenterWindow(gpSplashScreen);
- textWnd = pluginScanDlg.GetDlgItem(IDC_SCANTEXT);
- } else if(pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 30)
- {
- textWnd->SetWindowText(scanFormat(plug + 1, numPlugins + 1, plugPath));
- MSG msg;
- while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- {
- ::TranslateMessage(&msg);
- ::DispatchMessage(&msg);
- }
- scanStart = Util::GetTickCount64();
- }
- if(plugPath == failedPlugin)
- {
- GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin"));
- 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?");
- if(Reporting::Confirm(text, false, true, &pluginScanDlg) == cnfNo)
- {
- continue;
- }
- }
- mpt::ustring plugTags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), tagFormat(plug), mpt::ustring());
- bool plugFound = true;
- VSTPluginLib *lib = m_pPluginManager->AddPlugin(plugPath, maskCrashes, plugTags, true, &plugFound);
- if(!plugFound && lib != nullptr)
- {
- nonFoundPlugs.push_back(lib);
- }
- if(lib != nullptr && lib->libraryName == P_("MIDI Input Output") && lib->pluginId1 == PLUGMAGIC('V','s','t','P') && lib->pluginId2 == PLUGMAGIC('M','M','I','D'))
- {
- // This appears to be an old version of our MIDI I/O plugin, which is now built right into the main executable.
- m_pPluginManager->RemovePlugin(lib);
- }
- }
- }
- GetPluginCache().Flush();
- if(pluginScanDlg.m_hWnd)
- {
- pluginScanDlg.DestroyWindow();
- }
- if(!nonFoundPlugs.empty())
- {
- PlugNotFoundDialog(nonFoundPlugs, nullptr).DoModal();
- }
- }
- void CTrackApp::UninitializeDXPlugins()
- {
- if(!m_pPluginManager) return;
- #ifndef NO_PLUGINS
- size_t plugIndex = 0;
- for(auto plug : *m_pPluginManager)
- {
- if(!plug->isBuiltIn)
- {
- mpt::PathString plugPath = plug->dllPath;
- if(theApp.IsPortableMode())
- {
- plugPath = PathAbsoluteToInstallRelative(plugPath);
- }
- theApp.GetSettings().Write<mpt::PathString>(U_("VST Plugins"), MPT_UFORMAT("Plugin{}")(plugIndex), plugPath);
- theApp.GetSettings().Write(U_("VST Plugins"), MPT_UFORMAT("Plugin{}.Tags")(plugIndex), plug->tags);
- plugIndex++;
- } else
- {
- mpt::ustring key = MPT_UFORMAT("Plugin{}{}.Tags")(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2));
- theApp.GetSettings().Write(U_("VST Plugins"), key, plug->tags);
- }
- }
- theApp.GetSettings().Write(U_("VST Plugins"), U_("NumPlugins"), static_cast<uint32>(plugIndex));
- #endif // NO_PLUGINS
- delete m_pPluginManager;
- m_pPluginManager = nullptr;
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Internet-related functions
- bool CTrackApp::OpenURL(const char *url)
- {
- if(!url) return false;
- return OpenURL(mpt::PathString::FromUTF8(url));
- }
- bool CTrackApp::OpenURL(const std::string &url)
- {
- return OpenURL(mpt::PathString::FromUTF8(url));
- }
- bool CTrackApp::OpenURL(const CString &url)
- {
- return OpenURL(mpt::ToUnicode(url));
- }
- bool CTrackApp::OpenURL(const mpt::ustring &url)
- {
- return OpenURL(mpt::PathString::FromUnicode(url));
- }
- bool CTrackApp::OpenURL(const mpt::PathString &lpszURL)
- {
- if(!lpszURL.empty() && theApp.m_pMainWnd)
- {
- if(reinterpret_cast<INT_PTR>(ShellExecute(
- theApp.m_pMainWnd->m_hWnd,
- _T("open"),
- lpszURL.AsNative().c_str(),
- NULL,
- NULL,
- SW_SHOW)) >= 32)
- {
- return true;
- }
- }
- return false;
- }
- CString CTrackApp::GetResamplingModeName(ResamplingMode mode, int length, bool addTaps)
- {
- CString result;
- switch(mode)
- {
- case SRCMODE_NEAREST:
- result = (length > 1) ? _T("No Interpolation") : _T("None") ;
- break;
- case SRCMODE_LINEAR:
- result = _T("Linear");
- break;
- case SRCMODE_CUBIC:
- result = _T("Cubic");
- break;
- case SRCMODE_SINC8:
- result = _T("Sinc");
- break;
- case SRCMODE_SINC8LP:
- result = _T("Sinc");
- break;
- default:
- MPT_ASSERT_NOTREACHED();
- break;
- }
- if(Resampling::HasAA(mode))
- {
- result += (length > 1) ? _T(" + Low-Pass") : _T(" + LP");
- }
- if(addTaps)
- {
- result += MPT_CFORMAT(" ({} tap{})")(Resampling::Length(mode), (Resampling::Length(mode) != 1) ? CString(_T("s")) : CString(_T("")));
- }
- return result;
- }
- mpt::ustring CTrackApp::GetFriendlyMIDIPortName(const mpt::ustring &deviceName, bool isInputPort, bool addDeviceName)
- {
- auto friendlyName = GetSettings().Read<mpt::ustring>(isInputPort ? U_("MIDI Input Ports") : U_("MIDI Output Ports"), deviceName, deviceName);
- if(friendlyName.empty())
- return deviceName;
- else if(addDeviceName && friendlyName != deviceName)
- return friendlyName + UL_(" (") + deviceName + UL_(")");
- else
- return friendlyName;
- }
- CString CTrackApp::GetFriendlyMIDIPortName(const CString &deviceName, bool isInputPort, bool addDeviceName)
- {
- return mpt::ToCString(GetFriendlyMIDIPortName(mpt::ToUnicode(deviceName), isInputPort, addDeviceName));
- }
- OPENMPT_NAMESPACE_END
|