project(application)

################################ FILES ################################

######## Sources and headers ########

include (UnitTest)

set(CORE_SOURCES
    # LOGIC - Base classes and infrastructure
    BaseInstaller.h
    BaseInstaller.cpp
    BaseVersionList.h
    BaseVersionList.cpp
    InstanceList.h
    InstanceList.cpp
    InstanceTask.h
    InstanceTask.cpp
    LoggedProcess.h
    LoggedProcess.cpp
    Logging.h
    Logging.cpp
    MessageLevel.cpp
    MessageLevel.h
    BaseVersion.h
    BaseInstance.h
    BaseInstance.cpp
    NullInstance.h
    MMCZip.h
    MMCZip.cpp
    MMCStrings.h
    MMCStrings.cpp

    # The long-running halves of MMCZip, as tasks: an archive big enough to
    # need a progress bar is an archive big enough to need a way out, and
    # neither is possible from inside a single blocking call.
    archive/ExportToZipTask.h
    archive/ExportToZipTask.cpp
    archive/ExtractZipTask.h
    archive/ExtractZipTask.cpp

    # Basic instance manipulation tasks (derived from InstanceTask)
    InstanceCreationTask.h
    InstanceCreationTask.cpp
    InstanceCopyTask.h
    InstanceCopyTask.cpp
    InstanceImportTask.h
    InstanceImportTask.cpp

    # Use tracking separate from memory management
    Usable.h

    # Prefix tree where node names are strings between separators
    SeparatorPrefixTree.h

    # The checkable file tree the export dialogs are built around, and the
    # icon provider that keeps pointing it at an entire instance directory
    # from costing a per-file platform lookup.
    FileIgnoreProxy.h
    FileIgnoreProxy.cpp
    FastFileIconProvider.h
    FastFileIconProvider.cpp

    # String filters
    Filter.h
    Filter.cpp

    # JSON parsing helpers
    Json.h
    Json.cpp

    FileSystem.h
    FileSystem.cpp

    Exception.h

    # RW lock protected map
    RWStorage.h

    # A variable that has an implicit default value and keeps track of changes
    DefaultVariable.h

    # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
    QObjectPtr.h

    # Compression support
    GZip.h
    GZip.cpp

    # Command line parameter parsing
    Commandline.h
    Commandline.cpp

    # Version number string support
    Version.h
    Version.cpp

    # A Recursive file system watcher
    RecursiveFileSystemWatcher.h
    RecursiveFileSystemWatcher.cpp

    # Time
    MMCTime.h
    MMCTime.cpp
)

add_unit_test(FileSystem
    SOURCES FileSystem_test.cpp
    LIBS MeshMC_logic
    DATA testdata
    )

add_unit_test(GZip
    SOURCES GZip_test.cpp
    LIBS MeshMC_logic
    )

add_unit_test(MMCZip
    SOURCES MMCZip_test.cpp
    LIBS MeshMC_logic
    )

set(PATHMATCHER_SOURCES
    # Path matchers
    pathmatcher/FSTreeMatcher.h
    pathmatcher/IPathMatcher.h
    pathmatcher/MultiMatcher.h
    pathmatcher/RegexpMatcher.h
)

set(NET_SOURCES
    # network stuffs
    net/ByteArraySink.h
    net/ChecksumValidator.h
    net/Download.cpp
    net/Download.h
    net/FileSink.cpp
    net/FileSink.h
    net/HttpMetaCache.cpp
    net/HttpMetaCache.h
    net/MetaCacheSink.cpp
    net/MetaCacheSink.h
    net/NetAction.h
    net/NetJob.cpp
    net/NetJob.h
    # The one API question that does not fit in a URL, and so cannot be a
    # Net::Download. See net/JsonPost.h.
    net/JsonPost.cpp
    net/JsonPost.h
    net/PasteUpload.cpp
    net/PasteUpload.h
    net/Sink.h
    net/Validator.h
)

add_unit_test(FileSink
    SOURCES net/FileSink_test.cpp
    LIBS MeshMC_logic
    )

# Game launch logic
set(LAUNCH_SOURCES
    launch/steps/CheckJava.cpp
    launch/steps/CheckJava.h
    launch/steps/CreateBackup.cpp
    launch/steps/CreateBackup.h
    launch/steps/LookupServerAddress.cpp
    launch/steps/LookupServerAddress.h
    launch/steps/PostLaunchCommand.cpp
    launch/steps/PostLaunchCommand.h
    launch/steps/PreLaunchCommand.cpp
    launch/steps/PreLaunchCommand.h
    launch/steps/TextPrint.cpp
    launch/steps/TextPrint.h
    launch/steps/Update.cpp
    launch/steps/Update.h
    launch/LaunchStep.cpp
    launch/LaunchStep.h
    launch/LaunchTask.cpp
    launch/LaunchTask.h
    launch/LogModel.cpp
    launch/LogModel.h
)

# New updater system (replaces the old GoUpdate-based system)
set(UPDATE_SOURCES
    updater/UpdateChecker.h
    updater/UpdateChecker.cpp
)

add_unit_test(UpdateChecker
    SOURCES updater/UpdateChecker_test.cpp updater/UpdateChecker_test.h
    LIBS MeshMC_logic
    )

# Rarely used notifications
set(NOTIFICATIONS_SOURCES
    # Notifications - short warning messages
    notifications/NotificationChecker.h
    notifications/NotificationChecker.cpp
)

# Backend for the news bar... there's usually no news.
set(NEWS_SOURCES
    # News System
    news/NewsChecker.h
    news/NewsChecker.cpp
    news/NewsEntry.h
    news/NewsEntry.cpp
)

# Instance backup snapshots (was the BackupSystem .mmco plugin)
set(BACKUP_SOURCES
    backup/BackupManager.h
    backup/BackupManager.cpp
    backup/BackupTask.h
    backup/BackupTask.cpp
)

# Icon interface
set(ICONS_SOURCES
    # Icons System and related code
    icons/IconUtils.h
    icons/IconUtils.cpp
)

# Support for Minecraft instances and launch
set(MINECRAFT_SOURCES
    # Minecraft support
    minecraft/auth/AccountData.cpp
    minecraft/auth/AccountData.h
    minecraft/auth/AccountList.cpp
    minecraft/auth/AccountList.h
    minecraft/auth/AccountTask.cpp
    minecraft/auth/AccountTask.h
    minecraft/auth/AuthRequest.cpp
    minecraft/auth/AuthRequest.h
    minecraft/auth/AuthSession.cpp
    minecraft/auth/AuthSession.h
    minecraft/auth/AuthStep.cpp
    minecraft/auth/AuthStep.h
    minecraft/auth/MinecraftAccount.cpp
    minecraft/auth/MinecraftAccount.h
    minecraft/auth/Parsers.cpp
    minecraft/auth/Parsers.h
    minecraft/auth/flows/AuthFlow.cpp
    minecraft/auth/flows/AuthFlow.h
    minecraft/auth/flows/MSA.cpp
    minecraft/auth/flows/MSA.h

    minecraft/auth/steps/EntitlementsStep.cpp
    minecraft/auth/steps/EntitlementsStep.h
    minecraft/auth/steps/GetSkinStep.cpp
    minecraft/auth/steps/GetSkinStep.h
    minecraft/auth/steps/MeshMCLoginStep.cpp
    minecraft/auth/steps/MeshMCLoginStep.h
    minecraft/auth/steps/MinecraftProfileStep.cpp
    minecraft/auth/steps/MinecraftProfileStep.h
    minecraft/auth/steps/MSAStep.cpp
    minecraft/auth/steps/MSAStep.h
    minecraft/auth/steps/XboxAuthorizationStep.cpp
    minecraft/auth/steps/XboxAuthorizationStep.h
    minecraft/auth/steps/XboxProfileStep.cpp
    minecraft/auth/steps/XboxProfileStep.h
    minecraft/auth/steps/XboxUserStep.cpp
    minecraft/auth/steps/XboxUserStep.h
    minecraft/gameoptions/GameOptions.h
    minecraft/gameoptions/GameOptions.cpp

    minecraft/update/AssetUpdateTask.h
    minecraft/update/AssetUpdateTask.cpp
    minecraft/update/FMLLibrariesTask.cpp
    minecraft/update/FMLLibrariesTask.h
    minecraft/update/FoldersTask.cpp
    minecraft/update/FoldersTask.h
    minecraft/update/LibrariesTask.cpp
    minecraft/update/LibrariesTask.h

    minecraft/launch/ClaimAccount.cpp
    minecraft/launch/ClaimAccount.h
    minecraft/launch/CreateGameFolders.cpp
    minecraft/launch/CreateGameFolders.h
    minecraft/launch/ModMinecraftJar.cpp
    minecraft/launch/ModMinecraftJar.h
    minecraft/launch/DirectJavaLaunch.cpp
    minecraft/launch/DirectJavaLaunch.h
    minecraft/launch/ExtractNatives.cpp
    minecraft/launch/ExtractNatives.h
    minecraft/launch/MeshMCPartLaunch.cpp
    minecraft/launch/MeshMCPartLaunch.h
    minecraft/launch/MinecraftServerTarget.cpp
    minecraft/launch/MinecraftServerTarget.h
    minecraft/launch/PrintInstanceInfo.cpp
    minecraft/launch/PrintInstanceInfo.h
    minecraft/launch/ReconstructAssets.cpp
    minecraft/launch/ReconstructAssets.h
    minecraft/launch/ScanModFolders.cpp
    minecraft/launch/ScanModFolders.h
    minecraft/launch/VerifyJavaInstall.cpp
    minecraft/launch/VerifyJavaInstall.h

    minecraft/legacy/LegacyModList.h
    minecraft/legacy/LegacyModList.cpp
    minecraft/legacy/LegacyInstance.h
    minecraft/legacy/LegacyInstance.cpp
    minecraft/legacy/LegacyUpgradeTask.h
    minecraft/legacy/LegacyUpgradeTask.cpp

    minecraft/GradleSpecifier.h
    minecraft/MinecraftInstance.cpp
    minecraft/MinecraftInstance.h
    minecraft/LaunchProfile.cpp
    minecraft/LaunchProfile.h
    minecraft/Component.cpp
    minecraft/Component.h
    minecraft/PackProfile.cpp
    minecraft/PackProfile.h
    minecraft/ComponentUpdateTask.cpp
    minecraft/ComponentUpdateTask.h
    minecraft/MinecraftLoadAndCheck.h
    minecraft/MinecraftLoadAndCheck.cpp
    minecraft/MinecraftUpdate.h
    minecraft/MinecraftUpdate.cpp
    minecraft/MojangVersionFormat.cpp
    minecraft/MojangVersionFormat.h
    minecraft/Rule.cpp
    minecraft/Rule.h
    minecraft/OneSixVersionFormat.cpp
    minecraft/OneSixVersionFormat.h
    minecraft/OpSys.cpp
    minecraft/OpSys.h
    minecraft/ParseUtils.cpp
    minecraft/ParseUtils.h
    minecraft/ProfileUtils.cpp
    minecraft/ProfileUtils.h
    minecraft/ShortcutUtils.cpp
    minecraft/ShortcutUtils.h
    minecraft/Library.cpp
    minecraft/Library.h
    minecraft/MojangDownloadInfo.h
    minecraft/VersionFile.cpp
    minecraft/VersionFile.h
    minecraft/VersionFilterData.h
    minecraft/VersionFilterData.cpp
    minecraft/World.h
    minecraft/World.cpp
    minecraft/WorldList.h
    minecraft/WorldList.cpp

    minecraft/mod/Mod.h
    minecraft/mod/Mod.cpp
    minecraft/mod/ModDetails.h
    minecraft/mod/ModFolderModel.h
    minecraft/mod/ModFolderModel.cpp
    minecraft/mod/ModFolderLoadTask.h
    minecraft/mod/ModFolderLoadTask.cpp
    minecraft/mod/LocalModParseTask.h
    minecraft/mod/LocalModParseTask.cpp
    minecraft/mod/ModMetadataIndex.h
    minecraft/mod/ModMetadataIndex.cpp
    minecraft/mod/PackwizSidecar.h
    minecraft/mod/PackwizSidecar.cpp
    minecraft/mod/PackLayout.h
    minecraft/mod/PackLayout.cpp
    minecraft/mod/ResourcePackFolderModel.h
    minecraft/mod/ResourcePackFolderModel.cpp
    minecraft/mod/TexturePackFolderModel.h
    minecraft/mod/TexturePackFolderModel.cpp
    minecraft/mod/ShaderPackFolderModel.h
    minecraft/mod/ShaderPackFolderModel.cpp
    minecraft/mod/DataPackFolderModel.h
    minecraft/mod/DataPackFolderModel.cpp

    # Assets
    minecraft/AssetsUtils.h
    minecraft/AssetsUtils.cpp

    # Minecraft services
    minecraft/services/CapeChange.cpp
    minecraft/services/CapeChange.h
    minecraft/services/SkinUpload.cpp
    minecraft/services/SkinUpload.h
    minecraft/services/SkinDelete.cpp
    minecraft/services/SkinDelete.h

    mojang/PackageManifest.h
    mojang/PackageManifest.cpp
    )

add_unit_test(GradleSpecifier
    SOURCES minecraft/GradleSpecifier_test.cpp
    LIBS MeshMC_logic
    )

add_executable(PackageManifest
    mojang/PackageManifest_test.cpp
)
target_link_libraries(PackageManifest
    MeshMC_logic
    Qt6::Test
)
target_include_directories(PackageManifest
    PRIVATE ../cmake/UnitTest/
)
add_test(
    NAME PackageManifest
    COMMAND PackageManifest
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

add_unit_test(MojangVersionFormat
    SOURCES minecraft/MojangVersionFormat_test.cpp
    LIBS MeshMC_logic
    DATA minecraft/testdata
    )

add_unit_test(Library
    SOURCES minecraft/Library_test.cpp
    LIBS MeshMC_logic
    )

# FIXME: shares data with FileSystem test
add_unit_test(ModFolderModel
    SOURCES minecraft/mod/ModFolderModel_test.cpp
    DATA testdata
    LIBS MeshMC_logic
    )

add_unit_test(ParseUtils
    SOURCES minecraft/ParseUtils_test.cpp
    LIBS MeshMC_logic
    )

add_unit_test(World
    SOURCES minecraft/World_test.cpp
    LIBS MeshMC_logic
    )

# the screenshots feature
set(SCREENSHOTS_SOURCES
    screenshots/Screenshot.h
    screenshots/ImgurUpload.h
    screenshots/ImgurUpload.cpp
    screenshots/ImgurAlbumCreation.h
    screenshots/ImgurAlbumCreation.cpp
)

set(TASKS_SOURCES
    # Tasks
    tasks/Task.h
    tasks/Task.cpp
    tasks/ConcurrentTask.h
    tasks/ConcurrentTask.cpp
    tasks/SequentialTask.h
    tasks/SequentialTask.cpp
)

set(SETTINGS_SOURCES
    # Settings
    settings/INIFile.cpp
    settings/INIFile.h
    settings/INISettingsObject.cpp
    settings/INISettingsObject.h
    settings/OverrideSetting.cpp
    settings/OverrideSetting.h
    settings/PassthroughSetting.cpp
    settings/PassthroughSetting.h
    settings/Setting.cpp
    settings/Setting.h
    settings/SettingsObject.cpp
    settings/SettingsObject.h
)

add_unit_test(INIFile
    SOURCES settings/INIFile_test.cpp
    LIBS MeshMC_logic
    )

set(JAVA_SOURCES
    java/JavaChecker.h
    java/JavaChecker.cpp
    java/JavaCheckerJob.h
    java/JavaCheckerJob.cpp
    java/JavaInstall.h
    java/JavaInstall.cpp
    java/JavaInstallList.h
    java/JavaInstallList.cpp
    java/JavaUtils.h
    java/JavaUtils.cpp
    java/JavaVersion.h
    java/JavaVersion.cpp
)

if(NOT MeshMC_DISABLE_JAVA_DOWNLOADER)
    list(APPEND JAVA_SOURCES
        java/download/JavaRuntime.h
        java/download/JavaDownloadTask.h
        java/download/JavaDownloadTask.cpp
    )
endif()

add_unit_test(JavaVersion
    SOURCES java/JavaVersion_test.cpp
    LIBS MeshMC_logic
    )

set(TRANSLATIONS_SOURCES
    translations/TranslationsModel.h
    translations/TranslationsModel.cpp
    translations/POTranslator.h
    translations/POTranslator.cpp
)

set(TOOLS_SOURCES
    # Tools
    tools/BaseExternalTool.cpp
    tools/BaseExternalTool.h
    tools/BaseProfiler.cpp
    tools/BaseProfiler.h
    tools/JProfiler.cpp
    tools/JProfiler.h
    tools/JVisualVM.cpp
    tools/JVisualVM.h
    tools/MCEditTool.cpp
    tools/MCEditTool.h
)

set(META_SOURCES
    # Metadata sources
    meta/JsonFormat.cpp
    meta/JsonFormat.h
    meta/BaseEntity.cpp
    meta/BaseEntity.h
    meta/VersionList.cpp
    meta/VersionList.h
    meta/Version.cpp
    meta/Version.h
    meta/Index.cpp
    meta/Index.h
)

set(FTB_SOURCES
    modplatform/legacy_ftb/PackFetchTask.h
    modplatform/legacy_ftb/PackFetchTask.cpp
    modplatform/legacy_ftb/PackInstallTask.h
    modplatform/legacy_ftb/PackInstallTask.cpp
    modplatform/legacy_ftb/PrivatePackManager.h
    modplatform/legacy_ftb/PrivatePackManager.cpp

    modplatform/legacy_ftb/PackHelpers.h
)

set(FLAME_SOURCES
    # Flame
    modplatform/flame/FlamePackIndex.cpp
    modplatform/flame/FlamePackIndex.h
    modplatform/flame/PackManifest.h
    modplatform/flame/PackManifest.cpp
    modplatform/flame/FileResolvingTask.h
    modplatform/flame/FileResolvingTask.cpp
    modplatform/flame/FlameContentModel.h
    modplatform/flame/FlameContentModel.cpp
    modplatform/flame/FlameApi.h
    modplatform/flame/FlameApi.cpp
    # The file identifier CurseForge looks files up by, and the export
    # that has to compute it to name a mod instead of shipping it.
    modplatform/flame/FlameFingerprint.h
    modplatform/flame/FlameFingerprint.cpp
    modplatform/flame/FlamePackExportTask.h
    modplatform/flame/FlamePackExportTask.cpp
)

set(MODPACKSCH_SOURCES
    modplatform/modpacksch/FTBPackInstallTask.h
    modplatform/modpacksch/FTBPackInstallTask.cpp
    modplatform/modpacksch/FTBPackManifest.h
    modplatform/modpacksch/FTBPackManifest.cpp
)

set(TECHNIC_SOURCES
    modplatform/technic/SingleZipPackInstallTask.h
    modplatform/technic/SingleZipPackInstallTask.cpp
    modplatform/technic/SolderPackInstallTask.h
    modplatform/technic/SolderPackInstallTask.cpp
    modplatform/technic/TechnicPackProcessor.h
    modplatform/technic/TechnicPackProcessor.cpp
)

set(ATLAUNCHER_SOURCES
    modplatform/atlauncher/ATLPackIndex.cpp
    modplatform/atlauncher/ATLPackIndex.h
    modplatform/atlauncher/ATLPackInstallTask.cpp
    modplatform/atlauncher/ATLPackInstallTask.h
    modplatform/atlauncher/ATLPackManifest.cpp
    modplatform/atlauncher/ATLPackManifest.h
)

set(MODRINTH_SOURCES
    modplatform/modrinth/ModrinthPackIndex.cpp
    modplatform/modrinth/ModrinthPackIndex.h
    modplatform/modrinth/ModrinthPackManifest.cpp
    modplatform/modrinth/ModrinthPackManifest.h
    modplatform/modrinth/ModrinthContentModel.h
    modplatform/modrinth/ModrinthContentModel.cpp
    modplatform/modrinth/ModrinthApi.h
    modplatform/modrinth/ModrinthApi.cpp
    modplatform/modrinth/ModrinthPackExportTask.h
    modplatform/modrinth/ModrinthPackExportTask.cpp
)

set(CONTENT_DOWNLOAD_SOURCES
    modplatform/ContentApi.h
    modplatform/ManagedPackVersions.h
    modplatform/ManagedPackVersions.cpp
    modplatform/PackContents.h
    modplatform/PackContents.cpp
    modplatform/VersionPicker.h
    modplatform/VersionPicker.cpp
    modplatform/ContentProviderModel.h
    modplatform/ContentProviderModel.cpp
    modplatform/ContentListExport.h
    modplatform/ContentListExport.cpp
    modplatform/ContentType.h
    modplatform/ModDownloadTypes.h
    modplatform/DependencyResolver.h
    modplatform/DependencyResolver.cpp
    modplatform/ContentDownloadTask.h
    modplatform/ContentDownloadTask.cpp
    modplatform/ModInstallConflictAnalyzer.h
    modplatform/ModInstallConflictAnalyzer.cpp
    modplatform/ModUpdateCheckTask.h
    modplatform/ModUpdateCheckTask.cpp
)

add_unit_test(PackContents
    SOURCES modplatform/PackContents_test.cpp
    LIBS MeshMC_logic
    )

set(PLUGIN_SOURCES
    # MMCO Plugin System
    plugin/MMCOFormat.h
    plugin/PluginHooks.h
    plugin/PluginAPI.h
    plugin/PluginMetadata.h
    plugin/CoreSupersededPlugins.h
    plugin/PluginLoader.h
    plugin/PluginLoader.cpp
    plugin/PluginManager.h
    plugin/PluginManager.cpp
    plugin/PluginHookTask.h
    plugin/PluginHookTask.cpp
    plugin/PluginSignature.h
    plugin/PluginSignature.cpp
    plugin/PluginDependencyResolver.h
    plugin/PluginDependencyResolver.cpp
)

add_unit_test(Index
    SOURCES meta/Index_test.cpp
    LIBS MeshMC_logic
    )

################################ COMPILE ################################

# zlib, libarchive, cmark and toml++ are resolved in the top-level
# CMakeLists.txt -- from vcpkg under the presets, from the system package when
# configured without the vcpkg toolchain. nbt++ is a subtree under libraries/
# and is built here. Either way the imported target names below are the same.

set(LOGIC_SOURCES
    ${CORE_SOURCES}
    ${PATHMATCHER_SOURCES}
    ${NET_SOURCES}
    ${LAUNCH_SOURCES}
    ${UPDATE_SOURCES}
    ${NOTIFICATIONS_SOURCES}
    ${NEWS_SOURCES}
    ${BACKUP_SOURCES}
    ${MINECRAFT_SOURCES}
    ${SCREENSHOTS_SOURCES}
    ${TASKS_SOURCES}
    ${SETTINGS_SOURCES}
    ${JAVA_SOURCES}
    ${TRANSLATIONS_SOURCES}
    ${TOOLS_SOURCES}
    ${META_SOURCES}
    ${ICONS_SOURCES}
    ${FTB_SOURCES}
    ${FLAME_SOURCES}
    ${MODPACKSCH_SOURCES}
    ${TECHNIC_SOURCES}
    ${ATLAUNCHER_SOURCES}
    ${MODRINTH_SOURCES}
    ${CONTENT_DOWNLOAD_SOURCES}
    ${PLUGIN_SOURCES}
)

SET(MESHMC_SOURCES
    # Application base
    Application.h
    Application.cpp
    UpdateController.cpp
    UpdateController.h
    ApplicationMessage.h
    ApplicationMessage.cpp

    # GUI - general utilities
    DesktopServices.h
    DesktopServices.cpp
    VersionProxyModel.h
    VersionProxyModel.cpp
    HoeDown.h

    # Super secret!
    KonamiCode.h
    KonamiCode.cpp

    # Bundled resources
    resources/backgrounds/backgrounds.qrc
    resources/multimc/multimc.qrc
    resources/pe_dark/pe_dark.qrc
    resources/pe_light/pe_light.qrc
    resources/pe_colored/pe_colored.qrc
    resources/pe_blue/pe_blue.qrc
    resources/OSX/OSX.qrc
    resources/iOS/iOS.qrc
    resources/flat/flat.qrc
    resources/flat_white/flat_white.qrc
    resources/documents/documents.qrc
    resources/breeze_dark/breeze_dark.qrc
    resources/breeze_light/breeze_light.qrc
    ${CMAKE_BINARY_DIR}/${MeshMC_AppBinaryName}.qrc

    # Icons
    icons/MMCIcon.h
    icons/MMCIcon.cpp
    icons/IconList.h
    icons/IconList.cpp

    # GUI - windows
    ui/GuiUtil.h
    ui/GuiUtil.cpp
    ui/ColorCache.h
    ui/ColorCache.cpp
    ui/MainWindow.h
    ui/MainWindow.cpp
    ui/MacMenuBar.h
    ui/MacMenuBar.cpp
    ui/InstanceWindow.h
    ui/InstanceWindow.cpp

    # FIXME: maybe find a better home for this.
    SkinUtils.cpp
    SkinUtils.h

    # GUI - setup wizard
    ui/setupwizard/SetupWizard.h
    ui/setupwizard/SetupWizard.cpp
    ui/setupwizard/BaseWizardPage.h
    ui/setupwizard/JavaWizardPage.cpp
    ui/setupwizard/JavaWizardPage.h
    ui/setupwizard/LanguageWizardPage.cpp
    ui/setupwizard/LanguageWizardPage.h

    # GUI - themes
    ui/themes/FusionTheme.cpp
    ui/themes/FusionTheme.h
    ui/themes/BrightTheme.cpp
    ui/themes/BrightTheme.h
    ui/themes/GreenDarkTheme.cpp
    ui/themes/GreenDarkTheme.h
    ui/themes/GreenLightTheme.cpp
    ui/themes/GreenLightTheme.h
    ui/themes/CustomTheme.cpp
    ui/themes/CustomTheme.h
    ui/themes/DarkTheme.cpp
    ui/themes/DarkTheme.h
    ui/themes/ITheme.cpp
    ui/themes/ITheme.h
    ui/themes/SystemTheme.cpp
    ui/themes/SystemTheme.h
    ui/themes/ThemeManager.cpp
    ui/themes/ThemeManager.h
    ui/themes/CatPack.cpp
    ui/themes/CatPack.h

    # Processes
    LaunchController.h
    LaunchController.cpp

    # page provider for instances
    InstancePageProvider.h

    # Common java checking UI
    JavaCommon.h
    JavaCommon.cpp

    # GUI - paged dialog base
    ui/pages/BasePage.h
    ui/pages/BasePageContainer.h
    ui/pages/BasePageProvider.h

    # GUI - instance pages
    ui/pages/instance/BackupPage.cpp
    ui/pages/instance/BackupPage.h
    ui/pages/instance/GameOptionsPage.cpp
    ui/pages/instance/GameOptionsPage.h
    ui/pages/instance/VersionPage.cpp
    ui/pages/instance/VersionPage.h
    ui/pages/instance/TexturePackPage.h
    ui/pages/instance/ResourcePackPage.h
    ui/pages/instance/ShaderPackPage.h
    ui/pages/instance/DataPackPage.h
    ui/pages/instance/DataPackPage.cpp
    ui/pages/instance/ModFolderPage.cpp
    ui/pages/instance/ModFolderPage.h
    ui/pages/instance/NotesPage.cpp
    ui/pages/instance/NotesPage.h
    ui/pages/instance/LogPage.cpp
    ui/pages/instance/LogPage.h
    ui/pages/instance/InstanceSettingsPage.cpp
    ui/pages/instance/InstanceSettingsPage.h
    ui/pages/instance/ScreenshotsPage.cpp
    ui/pages/instance/ScreenshotsPage.h
    ui/pages/instance/OtherLogsPage.cpp
    ui/pages/instance/OtherLogsPage.h
    ui/pages/instance/ServersPage.cpp
    ui/pages/instance/ServersPage.h
    ui/pages/instance/LegacyUpgradePage.cpp
    ui/pages/instance/LegacyUpgradePage.h
    ui/pages/instance/ManagedPackPage.cpp
    ui/pages/instance/ManagedPackPage.h
    ui/pages/instance/WorldListPage.cpp
    ui/pages/instance/WorldListPage.h

    # GUI - global settings pages
    ui/pages/global/AccountListPage.cpp
    ui/pages/global/AccountListPage.h
    ui/pages/global/AppearancePage.cpp
    ui/pages/global/AppearancePage.h
    ui/pages/global/CustomCommandsPage.cpp
    ui/pages/global/CustomCommandsPage.h
    ui/pages/global/ExternalToolsPage.cpp
    ui/pages/global/ExternalToolsPage.h
    ui/pages/global/JavaPage.cpp
    ui/pages/global/JavaPage.h
    ui/pages/global/LanguagePage.cpp
    ui/pages/global/LanguagePage.h
    ui/pages/global/MinecraftPage.cpp
    ui/pages/global/MinecraftPage.h
    ui/pages/global/MeshMCPage.cpp
    ui/pages/global/MeshMCPage.h
    ui/pages/global/ProxyPage.cpp
    ui/pages/global/ProxyPage.h
    ui/pages/global/PasteEEPage.cpp
    ui/pages/global/PasteEEPage.h

    # GUI - platform pages
    ui/pages/modplatform/VanillaPage.cpp
    ui/pages/modplatform/VanillaPage.h

    ui/pages/modplatform/ContentProviderPage.cpp
    ui/pages/modplatform/ContentProviderPage.h

    ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
    ui/pages/modplatform/atlauncher/AtlFilterModel.h
    ui/pages/modplatform/atlauncher/AtlListModel.cpp
    ui/pages/modplatform/atlauncher/AtlListModel.h
    ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp
    ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h
    ui/pages/modplatform/atlauncher/AtlPage.cpp
    ui/pages/modplatform/atlauncher/AtlPage.h

    ui/pages/modplatform/ftb/FtbFilterModel.cpp
    ui/pages/modplatform/ftb/FtbFilterModel.h
    ui/pages/modplatform/ftb/FtbListModel.cpp
    ui/pages/modplatform/ftb/FtbListModel.h
    ui/pages/modplatform/ftb/FtbPage.cpp
    ui/pages/modplatform/ftb/FtbPage.h

    ui/pages/modplatform/legacy_ftb/Page.cpp
    ui/pages/modplatform/legacy_ftb/Page.h
    ui/pages/modplatform/legacy_ftb/ListModel.h
    ui/pages/modplatform/legacy_ftb/ListModel.cpp

    ui/pages/modplatform/flame/FlameModel.cpp
    ui/pages/modplatform/flame/FlameModel.h
    ui/pages/modplatform/flame/FlamePage.cpp
    ui/pages/modplatform/flame/FlamePage.h

    ui/pages/modplatform/modrinth/ModrinthModel.cpp
    ui/pages/modplatform/modrinth/ModrinthModel.h
    ui/pages/modplatform/modrinth/ModrinthPage.cpp
    ui/pages/modplatform/modrinth/ModrinthPage.h

    ui/pages/modplatform/technic/TechnicModel.cpp
    ui/pages/modplatform/technic/TechnicModel.h
    ui/pages/modplatform/technic/TechnicPage.cpp
    ui/pages/modplatform/technic/TechnicPage.h

    ui/pages/modplatform/ImportPage.cpp
    ui/pages/modplatform/ImportPage.h

    # GUI - dialogs
    ui/dialogs/AboutDialog.cpp
    ui/dialogs/AboutDialog.h
    ui/dialogs/BlockedModsDialog.cpp
    ui/dialogs/BlockedModsDialog.h
    ui/dialogs/ProfileSelectDialog.cpp
    ui/dialogs/ProfileSelectDialog.h
    ui/dialogs/ProfileSetupDialog.cpp
    ui/dialogs/ProfileSetupDialog.h
    ui/dialogs/CopyInstanceDialog.cpp
    ui/dialogs/CopyInstanceDialog.h
    ui/dialogs/CreateShortcutDialog.cpp
    ui/dialogs/CreateShortcutDialog.h
    ui/dialogs/CustomMessageBox.cpp
    ui/dialogs/CustomMessageBox.h
    ui/dialogs/EditAccountDialog.cpp
    ui/dialogs/EditAccountDialog.h
    ui/dialogs/ExportInstanceDialog.cpp
    ui/dialogs/ExportInstanceDialog.h
    ui/dialogs/ExportPackDialog.cpp
    ui/dialogs/ExportPackDialog.h
    ui/dialogs/IconPickerDialog.cpp
    ui/dialogs/IconPickerDialog.h
    ui/dialogs/InstallLoaderDialog.cpp
    ui/dialogs/InstallLoaderDialog.h
    ui/dialogs/MSALoginDialog.cpp
    ui/dialogs/MSALoginDialog.h
    ui/dialogs/NewComponentDialog.cpp
    ui/dialogs/NewComponentDialog.h
    ui/dialogs/NewInstanceDialog.cpp
    ui/dialogs/NewInstanceDialog.h
    ui/dialogs/NewsViewerDialog.cpp
    ui/dialogs/NewsViewerDialog.h
    ui/dialogs/NotificationDialog.cpp
    ui/dialogs/NotificationDialog.h
    ui/pagedialog/PageDialog.cpp
    ui/pagedialog/PageDialog.h
    ui/dialogs/PluginsDialog.cpp
    ui/dialogs/PluginsDialog.h
    ui/dialogs/ProgressDialog.cpp
    ui/dialogs/ProgressDialog.h
    ui/dialogs/UpdateDialog.cpp
    ui/dialogs/UpdateDialog.h
    ui/dialogs/VersionSelectDialog.cpp
    ui/dialogs/VersionSelectDialog.h
    ui/dialogs/SkinUploadDialog.cpp
    ui/dialogs/SkinUploadDialog.h
    ui/dialogs/DownloadContentDialog.cpp
    ui/dialogs/DownloadContentDialog.h
    ui/dialogs/DownloadSummaryDialog.cpp
    ui/dialogs/DownloadSummaryDialog.h
    ui/dialogs/ModUpdateDialog.cpp
    ui/dialogs/ModUpdateDialog.h
    ui/dialogs/ExportListDialog.cpp
    ui/dialogs/ExportListDialog.h
    ui/dialogs/MeshMCLogsDialog.cpp
    ui/dialogs/MeshMCLogsDialog.h
    ui/dialogs/UpdateProgressDialog.cpp
    ui/dialogs/UpdateProgressDialog.h
    ui/dialogs/UntrustedModsDialog.cpp
    ui/dialogs/UntrustedModsDialog.h


    # GUI - widgets
    ui/widgets/Common.cpp
    ui/widgets/Common.h
    ui/widgets/CustomCommands.cpp
    ui/widgets/CustomCommands.h
    ui/widgets/DropLabel.cpp
    ui/widgets/DropLabel.h
    ui/widgets/FocusLineEdit.cpp
    ui/widgets/FocusLineEdit.h
    ui/widgets/IconLabel.cpp
    ui/widgets/IconLabel.h
    ui/widgets/JavaSettingsWidget.cpp
    ui/widgets/JavaSettingsWidget.h
    ui/widgets/LabeledToolButton.cpp
    ui/widgets/LabeledToolButton.h
    ui/widgets/LanguageSelectionWidget.cpp
    ui/widgets/LanguageSelectionWidget.h
    ui/widgets/LineSeparator.cpp
    ui/widgets/LineSeparator.h
    ui/widgets/LogView.cpp
    ui/widgets/LogView.h
    ui/widgets/MCModInfoFrame.cpp
    ui/widgets/MCModInfoFrame.h
    ui/widgets/ModListView.cpp
    ui/widgets/ModListView.h
    ui/widgets/PageContainer.cpp
    ui/widgets/PageContainer.h
    ui/widgets/PageContainer_p.h
    ui/widgets/VersionListView.cpp
    ui/widgets/VersionListView.h
    ui/widgets/VersionSelectWidget.cpp
    ui/widgets/VersionSelectWidget.h
    ui/widgets/ProgressWidget.h
    ui/widgets/ProgressWidget.cpp
    ui/widgets/ProjectItemDelegate.h
    ui/widgets/ProjectItemDelegate.cpp
    ui/widgets/ProjectDescriptionPage.h
    ui/widgets/ProjectDescriptionPage.cpp
    ui/widgets/ContentFilterWidget.h
    ui/widgets/ContentFilterWidget.cpp
    ui/widgets/CheckComboBox.h
    ui/widgets/CheckComboBox.cpp
    ui/widgets/TaskStepProgressBar.h
    ui/widgets/TaskStepProgressBar.cpp
    ui/widgets/WideBar.h
    ui/widgets/WideBar.cpp

    # GUI - instance group view
    ui/instanceview/InstanceProxyModel.cpp
    ui/instanceview/InstanceProxyModel.h
    ui/instanceview/AccessibleInstanceView.cpp
    ui/instanceview/AccessibleInstanceView.h
    ui/instanceview/AccessibleInstanceView_p.h
    ui/instanceview/InstanceView.cpp
    ui/instanceview/InstanceView.h
    ui/instanceview/InstanceDelegate.cpp
    ui/instanceview/InstanceDelegate.h
    ui/instanceview/VisualGroup.cpp
    ui/instanceview/VisualGroup.h
)

qt6_wrap_ui(MESHMC_UI
    ui/pages/global/AccountListPage.ui
    ui/pages/global/AppearancePage.ui
    ui/pages/global/JavaPage.ui
    ui/pages/global/MeshMCPage.ui
    ui/pages/global/PasteEEPage.ui
    ui/pages/global/ProxyPage.ui
    ui/pages/global/MinecraftPage.ui
    ui/pages/global/ExternalToolsPage.ui
    ui/pages/instance/BackupPage.ui
    ui/pages/instance/ModFolderPage.ui
    ui/pages/instance/NotesPage.ui
    ui/pages/instance/LogPage.ui
    ui/pages/instance/ServersPage.ui
    ui/pages/instance/GameOptionsPage.ui
    ui/pages/instance/OtherLogsPage.ui
    ui/pages/instance/InstanceSettingsPage.ui
    ui/pages/instance/VersionPage.ui
    ui/pages/instance/WorldListPage.ui
    ui/pages/instance/LegacyUpgradePage.ui
    ui/pages/instance/ManagedPackPage.ui
    ui/pages/instance/ScreenshotsPage.ui
    ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
    ui/pages/modplatform/atlauncher/AtlPage.ui
    ui/pages/modplatform/VanillaPage.ui
    ui/pages/modplatform/ContentProviderPage.ui
    ui/pages/modplatform/flame/FlamePage.ui
    ui/pages/modplatform/modrinth/ModrinthPage.ui
    ui/pages/modplatform/legacy_ftb/Page.ui
    ui/pages/modplatform/ImportPage.ui
    ui/pages/modplatform/ftb/FtbPage.ui
    ui/pages/modplatform/technic/TechnicPage.ui
    ui/widgets/InstanceCardWidget.ui
    ui/widgets/CustomCommands.ui
    ui/widgets/MCModInfoFrame.ui
    ui/widgets/TaskStepProgressBar.ui
    ui/dialogs/CopyInstanceDialog.ui
    ui/dialogs/ProfileSetupDialog.ui
    ui/dialogs/ProgressDialog.ui
    ui/dialogs/NewInstanceDialog.ui
    ui/dialogs/NotificationDialog.ui
    ui/dialogs/UpdateDialog.ui
    ui/dialogs/MeshMCLogsDialog.ui
    ui/dialogs/PluginsDialog.ui
    ui/dialogs/NewComponentDialog.ui
    ui/dialogs/ProfileSelectDialog.ui
    ui/dialogs/SkinUploadDialog.ui
    ui/dialogs/CreateShortcutDialog.ui
    ui/dialogs/ExportInstanceDialog.ui
    ui/dialogs/ExportPackDialog.ui
    ui/dialogs/IconPickerDialog.ui
    ui/dialogs/MSALoginDialog.ui
    ui/dialogs/AboutDialog.ui
    ui/dialogs/EditAccountDialog.ui
    ui/dialogs/UntrustedModsDialog.ui
)

if(NOT MeshMC_DISABLE_JAVA_DOWNLOADER)
    list(APPEND MESHMC_SOURCES
        ui/dialogs/JavaDownloadDialog.cpp
        ui/dialogs/JavaDownloadDialog.h
    )
endif()

if (APPLE)
    set(MESHMC_SOURCES
        ${MESHMC_SOURCES}
        ui/themes/ThemeManager.mm
    )
endif()

qt6_add_resources(MESHMC_RESOURCES
    resources/backgrounds/backgrounds.qrc
    resources/multimc/multimc.qrc
    resources/pe_dark/pe_dark.qrc
    resources/pe_light/pe_light.qrc
    resources/pe_colored/pe_colored.qrc
    resources/pe_blue/pe_blue.qrc
    resources/OSX/OSX.qrc
    resources/iOS/iOS.qrc
    resources/flat/flat.qrc
    resources/flat_white/flat_white.qrc
    resources/documents/documents.qrc
    resources/breeze_dark/breeze_dark.qrc
    resources/breeze_light/breeze_light.qrc
    ${CMAKE_BINARY_DIR}/${MeshMC_AppBinaryName}.qrc
)

######## Windows resource files ########
if(WIN32)
    set(MESHMC_RCS ${CMAKE_BINARY_DIR}/${MeshMC_AppBinaryName}.rc)
endif()

# Add executable
add_library(MeshMC_logic STATIC ${LOGIC_SOURCES} ${MESHMC_SOURCES} ${MESHMC_UI} ${MESHMC_RESOURCES})

# Declared after MeshMC_logic because it links against it.
add_unit_test(CustomTheme
    SOURCES ui/themes/CustomTheme_test.cpp
    LIBS MeshMC_logic
    )

target_link_libraries(MeshMC_logic
    systeminfo
    classparser
    nbt++
    ZLIB::ZLIB
    tomlplusplus::tomlplusplus
    BuildConfig
    Katabasis
)

if(MeshMC_DISABLE_JAVA_DOWNLOADER)
    target_compile_definitions(MeshMC_logic PUBLIC MeshMC_DISABLE_JAVA_DOWNLOADER)
endif()

# Keep the function/line of every log message in *all* configurations. Qt only
# fills the QMessageLogContext in while QT_NO_DEBUG is absent, which CMake
# defines for every config except Debug - so without this, official builds log
# no source locations at all and the log is much harder to act on. PUBLIC
# because the message logger macros expand at every call site, including the
# ones in the application target.
# Cost, knowingly accepted: __FILE__ is baked into the binary for each
# translation unit that logs, so the binary grows and carries build paths.
target_compile_definitions(MeshMC_logic PUBLIC QT_MESSAGELOGCONTEXT)
target_link_libraries(MeshMC_logic
    Qt6::Core
    Qt6::Xml
    Qt6::Network
    Qt6::NetworkAuth
    Qt6::Concurrent
    Qt6::Gui
    LibArchive::LibArchive
)
target_link_libraries(MeshMC_logic
    iconfix
    cmark::cmark
    rainbow
    LocalPeer::LocalPeer
)

# Plugin system needs dlopen/dlsym on Unix
if(UNIX AND NOT APPLE)
    target_link_libraries(MeshMC_logic ${CMAKE_DL_LIBS})
endif()

# GpgME (C++ bindings) — MMCO plugin signature verification.
# Only linked when MeshMC_PLUGIN_SIGNATURES is ON (default off on MSVC,
# because upstream gpgme does not support MSVC). When disabled,
# PluginSignature.cpp compiles into a stub that reports every module
# as unsigned and never tries to include any gpgme++ headers.
if(MeshMC_PLUGIN_SIGNATURES)
    # Gpgmepp or GpgmeppStatic, depending on the linkage of the installation
    # that was found; resolved in the top-level CMakeLists.
    target_link_libraries(MeshMC_logic ${MeshMC_GpgmeppTarget})
    target_compile_definitions(MeshMC_logic PRIVATE MESHMC_PLUGIN_SIGNATURES=1)
else()
    target_compile_definitions(MeshMC_logic PRIVATE MESHMC_PLUGIN_SIGNATURES=0)
endif()

if(APPLE)
    set(CMAKE_MACOSX_RPATH 1)
    set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
endif()

add_executable(${MeshMC_Name} MACOSX_BUNDLE WIN32 main.cpp ${MESHMC_RCS})
target_link_libraries(${MeshMC_Name} MeshMC_logic)

# Export all symbols so dlopen()'ed .mmco plugins can resolve launcher symbols at runtime
set_target_properties(${MeshMC_Name} PROPERTIES ENABLE_EXPORTS ON)

if(MSVC)
    target_link_options(${MeshMC_Name} PRIVATE "/MANIFEST:NO")
endif()

if(DEFINED MeshMC_AppBinaryName)
    set_target_properties(${MeshMC_Name} PROPERTIES OUTPUT_NAME "${MeshMC_AppBinaryName}")
endif()
if(APPLE)
    set_target_properties(${MeshMC_Name} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/MacOSXBundleInfo.plist.in")
endif()
if(DEFINED MeshMC_BINARY_RPATH)
    SET_TARGET_PROPERTIES(${MeshMC_Name} PROPERTIES INSTALL_RPATH "${MeshMC_BINARY_RPATH}")
endif()

if(DEFINED MeshMC_APP_BINARY_DEFS)
    target_compile_definitions(${MeshMC_Name} PRIVATE ${MeshMC_APP_BINARY_DEFS})
    target_compile_definitions(MeshMC_logic PRIVATE ${MeshMC_APP_BINARY_DEFS})
endif()

if(APPLE)
    # CMake derives an app bundle's *directory* name from the target's
    # OUTPUT_NAME, and there is no property to decouple the two: with
    # OUTPUT_NAME "meshmc" the only thing install(TARGETS ... BUNDLE) can
    # produce is "meshmc.app" (RUNTIME DESTINATION is silently ignored for
    # bundle targets, and omitting BUNDLE DESTINATION is a hard error).
    #
    # Everything else, however, installs into "MeshMC.app": BINARY_DEST_DIR,
    # FRAMEWORK_DEST_DIR, RESOURCES_DEST_DIR, JARS_DEST_DIR, the MMCO_*
    # directories, the .icns/Assets.car rules in the top-level CMakeLists,
    # the qt_deploy_runtime_dependencies() call below and the macOS
    # packaging action. On a case-insensitive volume the two spellings
    # collapse into whichever directory is created first, so the mismatch
    # only shows up as odd casing; on a case-sensitive one the install
    # splits in half — a bundle without a binary next to a binary without
    # a bundle.
    #
    # Install the bundle into a staging directory (a name that cannot
    # collide with MeshMC.app under either casing rule) and merge it into
    # place. The staging hop is not cosmetic: install(TARGETS) is what
    # rewrites the build RPATH into INSTALL_RPATH via install_name_tool,
    # and it only does that for the copy it makes itself. Copying the
    # build-tree bundle directly would ship a binary still pointing at the
    # build directory.
    set(MeshMC_BundleStagingDir "_meshmc_bundle_staging")

    install(TARGETS ${MeshMC_Name}
        RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET
        BUNDLE DESTINATION "${MeshMC_BundleStagingDir}"
        LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
    )

    # Runs before the qt_deploy_runtime_dependencies() script registered
    # below, which expects the binary to already sit at
    # ${BINARY_DEST_DIR}/$<TARGET_FILE_NAME>.
    install(CODE "
        set(_meshmc_prefix \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\")
        set(_meshmc_staging \"\${_meshmc_prefix}/${MeshMC_BundleStagingDir}\")
        set(_meshmc_staged_bundle \"\${_meshmc_staging}/$<TARGET_BUNDLE_DIR_NAME:${MeshMC_Name}>\")
        set(_meshmc_bundle \"\${_meshmc_prefix}/${MeshMC_Name}.app\")
        if(NOT IS_DIRECTORY \"\${_meshmc_staged_bundle}\")
            message(FATAL_ERROR
                \"Staged app bundle '\${_meshmc_staged_bundle}' is missing; \"
                \"cannot assemble ${MeshMC_Name}.app\")
        endif()
        message(STATUS \"Merging \${_meshmc_staged_bundle} into \${_meshmc_bundle}\")
        file(COPY \"\${_meshmc_staged_bundle}/\" DESTINATION \"\${_meshmc_bundle}\")
        file(REMOVE_RECURSE \"\${_meshmc_staging}\")
    ")
else()
    install(TARGETS ${MeshMC_Name}
        RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET
        BUNDLE DESTINATION ${BUNDLE_DEST_DIR}
        LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
        RUNTIME DESTINATION ${BINARY_DEST_DIR}
    )
endif()

#### The bundle mess! ####
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
# NOTE: it seems that this absolutely has to be here, and nowhere else.
if(WIN32 OR (UNIX AND APPLE))
    if(WIN32)
        # Find windeployqt directly — Qt's qt_generate_deploy_script tries to find
        # windeployqt.debug.bat for Debug builds, which may not be available.
        get_target_property(_qt_qmake_loc Qt6::qmake IMPORTED_LOCATION)
        get_filename_component(_qt_bin_dir "${_qt_qmake_loc}" DIRECTORY)
        find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}")
        if(NOT WINDEPLOYQT_EXECUTABLE)
            message(FATAL_ERROR "windeployqt not found")
        endif()
    endif()

    if(UNIX AND APPLE)
        qt_generate_deploy_script(
            TARGET ${MeshMC_Name}
            OUTPUT_SCRIPT QT_DEPLOY_SCRIPT
            CONTENT "
            qt_deploy_runtime_dependencies(
                EXECUTABLE ${BINARY_DEST_DIR}/$<TARGET_FILE_NAME:${MeshMC_Name}>
                BIN_DIR ${BINARY_DEST_DIR}
                LIBEXEC_DIR ${BINARY_DEST_DIR}
                LIB_DIR ${LIBRARY_DEST_DIR}
                PLUGINS_DIR ${PLUGIN_DEST_DIR}
                NO_OVERWRITE
                NO_TRANSLATIONS
                NO_COMPILER_RUNTIME
            )"
        )
        install(
            SCRIPT ${QT_DEPLOY_SCRIPT}
        )
    endif()

    # Bundle our linked dependencies
    # Collect bin/ directories from CMAKE_PREFIX_PATH (both CMake variable and
    # environment variable) so RUNTIME_DEPENDENCY_SET can locate third-party
    # DLLs that live in a dependency prefix.
    #
    # nbt++/iconfix/rainbow are NOT handled here: they are our own targets in
    # this build tree, and the top-level CMakeLists.txt installs them directly
    # to the right place. Everything else under libraries/ links statically.
    set(_prefix_path_bins "")
    foreach(_prefix IN LISTS CMAKE_PREFIX_PATH)
        list(APPEND _prefix_path_bins "${_prefix}/bin")
    endforeach()
    if(DEFINED ENV{CMAKE_PREFIX_PATH})
        file(TO_CMAKE_PATH "$ENV{CMAKE_PREFIX_PATH}" _env_prefixes)
        foreach(_prefix IN LISTS _env_prefixes)
            list(APPEND _prefix_path_bins "${_prefix}/bin")
        endforeach()
    endif()
    list(REMOVE_DUPLICATES _prefix_path_bins)
    install(
        RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET
        DIRECTORIES
            ${CMAKE_SYSTEM_LIBRARY_PATH}
            ${QT_LIBS_DIR}
            ${QT_LIBEXECS_DIR}
            ${_prefix_path_bins}
        PRE_EXCLUDE_REGEXES
            "^(api-ms-win|ext-ms)-.*\\.dll$"
            "^azure.*\\.dll$"
            "^vcruntime.*\\.dll$"
        POST_EXCLUDE_REGEXES
            "system32"
        LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
        RUNTIME DESTINATION ${BINARY_DEST_DIR}
        FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR}
    )

    if(WIN32)
        # Deploy Qt plugins and DLLs using windeployqt.
        # install-qt-action and MSYS2 only provide release Qt, so always use --release
        # even when the app itself is built in Debug mode.
        install(CODE "
            execute_process(
                COMMAND \"${WINDEPLOYQT_EXECUTABLE}\"
                    --release
                    --no-opengl-sw
                    --no-quick-import
                    --no-system-d3d-compiler
                    --no-system-dxc-compiler
                    --skip-plugin-types generic,networkinformation
                    --no-translations
                    --no-compiler-runtime
                    --dir \"\${CMAKE_INSTALL_PREFIX}/${BINARY_DEST_DIR}\"
                    --libdir \"\${CMAKE_INSTALL_PREFIX}/${LIBRARY_DEST_DIR}\"
                    --plugindir \"\${CMAKE_INSTALL_PREFIX}/${PLUGIN_DEST_DIR}\"
                    \"\${CMAKE_INSTALL_PREFIX}/${BINARY_DEST_DIR}/$<TARGET_FILE_NAME:${MeshMC_Name}>\"
                RESULT_VARIABLE _deploy_result
            )
            if(NOT _deploy_result EQUAL 0)
                message(FATAL_ERROR \"windeployqt failed with exit code: \${_deploy_result}\")
            endif()
        ")
    endif()

    # FIXME: remove this crap once we stop using msys2
    if(MINGW)
        install(CODE "
            set(QT_PLUGINS_DIR \"${QT_PLUGINS_DIR}\")
            set(QT_LIBS_DIR \"${QT_LIBS_DIR}\")
            set(QT_LIBEXECS_DIR \"${QT_LIBEXECS_DIR}\")
            set(CMAKE_SYSTEM_LIBRARY_PATH \"${CMAKE_SYSTEM_LIBRARY_PATH}\")
            set(CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\")
            ")

        install(CODE [[
            file(GLOB QT_IMAGEFORMAT_DLLS "${QT_PLUGINS_DIR}/imageformats/*.dll")
            set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump)
            file(GET_RUNTIME_DEPENDENCIES
                RESOLVED_DEPENDENCIES_VAR imageformatdeps
                LIBRARIES ${QT_IMAGEFORMAT_DLLS}
                DIRECTORIES
                    ${CMAKE_SYSTEM_LIBRARY_PATH}
                    ${QT_PLUGINS_DIR}
                    ${QT_LIBS_DIR}
                    ${QT_LIBEXECS_DIR}
                PRE_EXCLUDE_REGEXES
                    "^(api-ms-win|ext-ms)-.*\\.dll$"
                    "^azure.*\\.dll$"
                    "^vcruntime.*\\.dll$"
                POST_EXCLUDE_REGEXES
                    "system32"
            )
            foreach(_lib ${imageformatdeps})
                file(INSTALL
                    DESTINATION ${CMAKE_INSTALL_PREFIX}
                    TYPE SHARED_LIBRARY
                    FOLLOW_SYMLINK_CHAIN
                    FILES ${_lib}
                )
            endforeach()
        ]])
    endif()

    # Add qt.conf - tell Qt where to find plugins relative to the executable
    install(
        CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \"[Paths]\nPlugins = ${PLUGIN_DEST_DIR}\n\")"
    )
    # Add qtlogging.ini as a config file
    install(
        FILES "qtlogging.ini"
        DESTINATION ${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}
    )
endif()

find_program(CLANG_FORMAT clang-format OPTIONAL)
if(CLANG_FORMAT)
    message(STATUS "Creating clang-format target")
    add_custom_target(
        clang-format
        COMMAND ${CLANG_FORMAT} -i --style=file:${CMAKE_SOURCE_DIR}/.clang-format ${LOGIC_SOURCES} ${MESHMC_SOURCES}
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    )
else()
    message(WARNING "Unable to find `clang-format`. Not creating custom target")
endif()

find_program(CLANG_TIDY clang-tidy OPTIONAL)
find_program(RUN_CLANG_TIDY NAMES run-clang-tidy run-clang-tidy.py OPTIONAL)
if(CLANG_TIDY AND RUN_CLANG_TIDY)
    message(STATUS "Creating clang-tidy target")
    add_custom_target(
        clang-tidy
        COMMAND ${RUN_CLANG_TIDY}
            -p ${PROJECT_BINARY_DIR}
            -clang-tidy-binary ${CLANG_TIDY}
            -config-file=${CMAKE_SOURCE_DIR}/.clang-tidy
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        USES_TERMINAL
    )
elseif(CLANG_TIDY)
    message(WARNING "Unable to find `run-clang-tidy`. Not creating custom target")
endif()
