cmake_minimum_required(VERSION 3.20)

project(PS2Runtime VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

option(PS2X_ENABLE_RUNNER_UNITY_BUILD "Build ps2EntryRunner with CMake unity build" ON)
set(PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE 32 CACHE STRING "Unity build batch size for ps2EntryRunner")
option(PS2X_ENABLE_RUNNER_PCH "Precompile the heavy runtime headers for ps2EntryRunner" ON)
option(PS2X_ENABLE_SCCACHE "Use sccache as compiler launcher when available" ON)

option(PS2X_ENABLE_RUNTIME_LOGS "Enable PS2 runtime logs" ON)
option(PS2X_ENABLE_AGRESSIVE_LOGS "Enable very verbose/agressive PS2 runtime logs" ON)
option(PS2X_ENABLE_IOP_RPC_TRACE "Log unhandled IOP/SIF RPC trace suggestions" ON)
option(PS2X_STRICT_RETURN_DIAGNOSTICS "Route generated JR $ra returns through runtime branch diagnostics" OFF)
option(PS2X_SHOW_WINDOWS_CONSOLE "Show a console window for ps2EntryRunner on Windows release builds" ON)
option(PS2X_ENABLE_DEBUG_UI "Build the desktop runtime debug UI" ON)

set(PS2X_GS_BACKEND "CPU" CACHE STRING "GS backend: CPU, or PARALLEL_GS (paraLLEl-GS on Vulkan, CPU fallback at run time)")
set_property(CACHE PS2X_GS_BACKEND PROPERTY STRINGS CPU PARALLEL_GS)
if(NOT PS2X_GS_BACKEND MATCHES "^(CPU|PARALLEL_GS)$")
    message(FATAL_ERROR "PS2X_GS_BACKEND must be CPU or PARALLEL_GS")
endif()
if(PS2X_GS_BACKEND STREQUAL "PARALLEL_GS" AND (ANDROID OR NOT (WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")))
    message(FATAL_ERROR "PARALLEL_GS is supported only on Windows and Linux desktop")
endif()

if(PS2X_ENABLE_SCCACHE)
    find_program(PS2X_SCCACHE_PROGRAM sccache)

    if(PS2X_SCCACHE_PROGRAM)
        if(CMAKE_C_COMPILER)
            set(CMAKE_C_COMPILER_LAUNCHER "${PS2X_SCCACHE_PROGRAM}")
        endif()

        set(CMAKE_CXX_COMPILER_LAUNCHER "${PS2X_SCCACHE_PROGRAM}")
        message(STATUS "Using sccache: ${PS2X_SCCACHE_PROGRAM}")
    else()
        message(STATUS "sccache not found; continuing without compiler launcher")
    endif()
endif()

include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ReleaseMode.cmake")

set(PS2X_IS_VITA OFF)

if((CMAKE_C_COMPILER MATCHES "arm-vita-eabi") OR
    (CMAKE_CXX_COMPILER MATCHES "arm-vita-eabi"))
    set(PS2X_IS_VITA ON)
endif()

set(PS2X_IS_ANDROID OFF)

if(ANDROID)
    set(PS2X_IS_ANDROID ON)
endif()

option(PS2X_VITA_CREATE_PACKAGE "Create Vita eboot/vpk targets" ON)
set(PS2X_VITA_APP_NAME "PS2 Retro X" CACHE STRING "Display name for the Vita bubble")
set(PS2X_VITA_TITLEID "RANJ00001" CACHE STRING "9-character Vita title id")
set(PS2X_VITA_VERSION "01.00" CACHE STRING "Vita app version")
set(PS2X_DEFAULT_BOOT_ELF "" CACHE STRING "Guest ELF path passed directly to main() when argv is unavailable")

if(PS2X_IS_VITA)
    if(NOT DEFINED ENV{VITASDK})
        message(FATAL_ERROR "VITASDK is not defined. Configure inside the VitaSDK environment or pass the Vita toolchain file.")
    endif()

    set(PS2X_VITASDK "$ENV{VITASDK}")

    if(NOT EXISTS "${PS2X_VITASDK}/share/vita.cmake")
        message(FATAL_ERROR "Could not find vita.cmake under ${PS2X_VITASDK}/share.")
    endif()

    include("${PS2X_VITASDK}/share/vita.cmake" REQUIRED)
else()
    include(FetchContent)
    set(FETCHCONTENT_QUIET FALSE)
    set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
    set(BUILD_GAMES OFF CACHE BOOL "" FORCE)

    if(PS2X_IS_ANDROID)
        set(PLATFORM "Android" CACHE STRING "" FORCE)
    endif()

    FetchContent_Declare(
        raylib
        GIT_REPOSITORY "https://github.com/raysan5/raylib.git"
        GIT_TAG "5.5" # we will migrate to 4.2.0 later, trust me it will be better
        GIT_PROGRESS TRUE
    )
    FetchContent_MakeAvailable(raylib)

    if(PS2X_ENABLE_DEBUG_UI AND NOT PS2X_IS_ANDROID)
        FetchContent_Declare(
            imgui
            GIT_REPOSITORY https://github.com/ocornut/imgui.git
            GIT_TAG "v1.92.7-docking"
            GIT_SHALLOW TRUE
        )
        FetchContent_GetProperties(imgui)

        if(NOT imgui_POPULATED)
            FetchContent_Populate(imgui)
        endif()

        set(PS2X_IMGUI_SOURCE_DIR "${imgui_SOURCE_DIR}")

        add_library(imgui STATIC
            "${PS2X_IMGUI_SOURCE_DIR}/imgui.cpp"
            "${PS2X_IMGUI_SOURCE_DIR}/imgui_draw.cpp"
            "${PS2X_IMGUI_SOURCE_DIR}/imgui_tables.cpp"
            "${PS2X_IMGUI_SOURCE_DIR}/imgui_widgets.cpp"
            "${PS2X_IMGUI_SOURCE_DIR}/imgui_demo.cpp"
        )
        target_include_directories(imgui PUBLIC "${PS2X_IMGUI_SOURCE_DIR}")

        FetchContent_Declare(
            rlImGui
            GIT_REPOSITORY https://github.com/raylib-extras/rlImGui.git
            GIT_TAG "Raylib_5_5"
            GIT_SHALLOW TRUE
        )
        FetchContent_GetProperties(rlImGui)

        if(NOT rlimgui_POPULATED)
            FetchContent_Populate(rlImGui)
        endif()

        set(PS2X_RLIMGUI_SOURCE_DIR "${rlimgui_SOURCE_DIR}")

        add_library(rlImGui STATIC
            "${PS2X_RLIMGUI_SOURCE_DIR}/rlImGui.cpp"
        )
        target_include_directories(rlImGui PUBLIC "${PS2X_RLIMGUI_SOURCE_DIR}")
        target_link_libraries(rlImGui PUBLIC raylib imgui)
    endif()
endif()

add_library(ps2_host_backend INTERFACE)

set(PS2X_DEFAULT_BOOT_ELF_DEFINE "")

if(PS2X_DEFAULT_BOOT_ELF)
    set(PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF}")
    string(REPLACE "\\" "\\\\" PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF_DEFINE}")
    string(REPLACE "\"" "\\\"" PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF_DEFINE}")
endif()

if(PS2X_IS_VITA)
    set(PS2X_VITA_PREFIX "${PS2X_VITASDK}/arm-vita-eabi")
    set(PS2X_VITA_EXTRA_INCLUDE_DIRS "" CACHE STRING "Extra include directories for Vita host dependencies")
    set(PS2X_VITA_LIBRARY_DIR "${PS2X_VITA_PREFIX}/lib")
    set(PS2X_VITA_REQUIRED_LIBRARY_NAMES
        raylib
        SDL2
        OpenSLES
        taihen_stub
        SceAppMgr_stub
        SceCtrl_stub
        SceGxm_stub
        SceCommonDialog_stub
        SceLibKernel_stub
        SceAudio_stub
        SceTouch_stub
        SceHid_stub
        SceMotion_stub
        SceSysmodule_stub
        SceIofilemgr_stub
        SceNetCtl_stub
        SceNet_stub
        SceDisplay_stub
        SceAppUtil_stub
        SceAudioIn_stub
        ScePower_stub
        SceProcessmgr_stub
        SceIme_stub
        libIMGEGL_stub_weak
        libgpu_es4_ext_stub_weak
        libGLESv2_stub_weak
        libGL_stub_weak
        libGLESv1_CM_stub_weak
    )

    set(PS2X_VITA_INCLUDE_DIRS
        "${PS2X_VITA_PREFIX}/include"
    )

    foreach(PS2X_VITA_OPTIONAL_INCLUDE_DIR IN ITEMS
        "${PS2X_VITA_PREFIX}/include/raylib"
        "${PS2X_VITA_PREFIX}/include/SDL2")
        if(EXISTS "${PS2X_VITA_OPTIONAL_INCLUDE_DIR}")
            list(APPEND PS2X_VITA_INCLUDE_DIRS "${PS2X_VITA_OPTIONAL_INCLUDE_DIR}")
        endif()
    endforeach()

    if(PS2X_VITA_EXTRA_INCLUDE_DIRS)
        list(APPEND PS2X_VITA_INCLUDE_DIRS ${PS2X_VITA_EXTRA_INCLUDE_DIRS})
    endif()

    if(NOT EXISTS "${PS2X_VITA_PREFIX}/include/SDL2/SDL.h")
        message(FATAL_ERROR
            "Missing SDL2 headers under ${PS2X_VITA_PREFIX}/include/SDL2. "
            "Quenom/raylib-5.5-vita requires SDL2 with PVR support installed into VitaSDK.")
    endif()

    if(NOT EXISTS "${PS2X_VITA_LIBRARY_DIR}")
        message(FATAL_ERROR "Missing Vita library directory: ${PS2X_VITA_LIBRARY_DIR}")
    endif()

    function(ps2x_find_vita_library out_var library_name)
        string(MAKE_C_IDENTIFIER "${library_name}" library_id)
        set(cache_var "PS2X_VITA_LIB_${library_id}")

        find_library(${cache_var}
            NAMES "${library_name}" "${library_name}.a" "lib${library_name}.a"
            PATHS "${PS2X_VITA_LIBRARY_DIR}"
            NO_DEFAULT_PATH
        )

        if(NOT ${cache_var})
            message(FATAL_ERROR
                "Could not find Vita library '${library_name}' under ${PS2X_VITA_LIBRARY_DIR}. "
                "Install Quenom/raylib-5.5-vita and its SDL2/PVR dependencies into VitaSDK first.")
        endif()

        set(${out_var} "${${cache_var}}" PARENT_SCOPE)
    endfunction()

    set(PS2X_VITA_RESOLVED_LIBRARIES "")

    foreach(PS2X_VITA_LIBRARY_NAME IN LISTS PS2X_VITA_REQUIRED_LIBRARY_NAMES)
        ps2x_find_vita_library(PS2X_VITA_LIBRARY_PATH "${PS2X_VITA_LIBRARY_NAME}")
        list(APPEND PS2X_VITA_RESOLVED_LIBRARIES "${PS2X_VITA_LIBRARY_PATH}")
    endforeach()

    target_compile_definitions(ps2_host_backend INTERFACE PLATFORM_VITA)
    target_include_directories(ps2_host_backend INTERFACE ${PS2X_VITA_INCLUDE_DIRS})
    target_link_libraries(ps2_host_backend INTERFACE
        ${PS2X_VITA_RESOLVED_LIBRARIES}
        m
        c
        pthread
    )
else()
    target_link_libraries(ps2_host_backend INTERFACE raylib)

    if(PS2X_IS_ANDROID)
        target_link_libraries(ps2_host_backend INTERFACE log android)
    endif()
endif()

if(PS2X_IS_ANDROID)
    set(PS2X_ENABLE_FFMPEG_DEFAULT OFF)
else()
    set(PS2X_ENABLE_FFMPEG_DEFAULT ON)
endif()

option(PS2X_ENABLE_FFMPEG "Enable FFmpeg-backed MPEG video decoding" ${PS2X_ENABLE_FFMPEG_DEFAULT})

if(NOT PS2X_ENABLE_FFMPEG)
    message(STATUS "FFmpeg disabled; MPEG video decode falls back to stub frames")
    add_library(ffmpeg INTERFACE)
elseif(WIN32)
    include(ExternalProject)

    set(FFMPEG_PREBUILT_TAG "n7.1-241205")
    set(FFMPEG_PREBUILT_URL
        "https://github.com/System233/ffmpeg-msvc-prebuilt/releases/download/${FFMPEG_PREBUILT_TAG}/ffmpeg-${FFMPEG_PREBUILT_TAG}-lgpl-amd64-shared.zip")

    set(FFMPEG_PREFIX_DIR "${CMAKE_BINARY_DIR}/ThirdParty/ffmpeg-prefix")
    set(FFMPEG_SOURCE_DIR "${FFMPEG_PREFIX_DIR}/src/ffmpeg_external")
    set(FFMPEG_INCLUDE_DIR "${FFMPEG_SOURCE_DIR}/include")
    set(FFMPEG_LIB_DIR "${FFMPEG_SOURCE_DIR}/bin")
    set(FFMPEG_BIN_DIR "${FFMPEG_SOURCE_DIR}/bin")

    ExternalProject_Add(ffmpeg_external
        URL "${FFMPEG_PREBUILT_URL}"
        DOWNLOAD_EXTRACT_TIMESTAMP TRUE
        PREFIX "${FFMPEG_PREFIX_DIR}"
        CONFIGURE_COMMAND ""
        BUILD_COMMAND ""
        INSTALL_COMMAND ""
        BUILD_BYPRODUCTS
        "${FFMPEG_LIB_DIR}/avcodec.lib"
        "${FFMPEG_LIB_DIR}/avformat.lib"
        "${FFMPEG_LIB_DIR}/avutil.lib"
        "${FFMPEG_LIB_DIR}/swresample.lib"
        "${FFMPEG_LIB_DIR}/swscale.lib"
        "${FFMPEG_SOURCE_DIR}/lib/SDL2.lib"
    )

    # Link against the import libraries shipped with the Windows DLLs.
    add_library(ffmpeg_avcodec STATIC IMPORTED GLOBAL)
    set_target_properties(ffmpeg_avcodec PROPERTIES
        IMPORTED_LOCATION "${FFMPEG_LIB_DIR}/avcodec.lib"
        INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}"
    )
    add_dependencies(ffmpeg_avcodec ffmpeg_external)

    add_library(ffmpeg_avformat STATIC IMPORTED GLOBAL)
    set_target_properties(ffmpeg_avformat PROPERTIES
        IMPORTED_LOCATION "${FFMPEG_LIB_DIR}/avformat.lib"
        INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}"
    )
    add_dependencies(ffmpeg_avformat ffmpeg_external)

    add_library(ffmpeg_avutil STATIC IMPORTED GLOBAL)
    set_target_properties(ffmpeg_avutil PROPERTIES
        IMPORTED_LOCATION "${FFMPEG_LIB_DIR}/avutil.lib"
        INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}"
    )
    add_dependencies(ffmpeg_avutil ffmpeg_external)

    add_library(ffmpeg_swresample STATIC IMPORTED GLOBAL)
    set_target_properties(ffmpeg_swresample PROPERTIES
        IMPORTED_LOCATION "${FFMPEG_LIB_DIR}/swresample.lib"
        INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}"
    )
    add_dependencies(ffmpeg_swresample ffmpeg_external)

    add_library(ffmpeg_swscale STATIC IMPORTED GLOBAL)
    set_target_properties(ffmpeg_swscale PROPERTIES
        IMPORTED_LOCATION "${FFMPEG_LIB_DIR}/swscale.lib"
        INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}"
    )
    add_dependencies(ffmpeg_swscale ffmpeg_external)

    # The package also ships SDL2 (FFmpeg's SDL output needs it); the pad reads game controllers with it.
    add_library(ffmpeg_sdl2 STATIC IMPORTED GLOBAL)
    set_target_properties(ffmpeg_sdl2 PROPERTIES
        IMPORTED_LOCATION "${FFMPEG_SOURCE_DIR}/lib/SDL2.lib"
        INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}"
        INTERFACE_COMPILE_DEFINITIONS PS2X_HAS_SDL2
    )
    add_dependencies(ffmpeg_sdl2 ffmpeg_external)

    add_library(ffmpeg INTERFACE)
    target_link_libraries(ffmpeg INTERFACE
        ffmpeg_sdl2
        ffmpeg_avcodec
        ffmpeg_avformat
        ffmpeg_avutil
        ffmpeg_swresample
        ffmpeg_swscale
        bcrypt
        secur32
        ws2_32
        user32
        advapi32
        ole32
        shell32
    )

    set(PS2X_FFMPEG_BIN_DIR "${FFMPEG_BIN_DIR}" CACHE INTERNAL "Directory containing FFmpeg runtime DLLs")
    set(PS2X_COPY_FFMPEG_DLLS_SCRIPT
        "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFfmpegDlls.cmake"
        CACHE INTERNAL
        "Script used to stage FFmpeg runtime DLLs beside executables")
else()
    find_package(PkgConfig REQUIRED)
    pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET GLOBAL
        libavcodec
        libavformat
        libavutil
        libswresample
        libswscale
    )

    add_library(ffmpeg INTERFACE)
    target_link_libraries(ffmpeg INTERFACE PkgConfig::FFMPEG)
    # Optional: game controllers through SDL2 when it is installed.
    pkg_check_modules(SDL2 QUIET IMPORTED_TARGET GLOBAL sdl2)
    if(SDL2_FOUND)
        target_link_libraries(ffmpeg INTERFACE PkgConfig::SDL2)
        target_compile_definitions(ffmpeg INTERFACE PS2X_HAS_SDL2)
    endif()
endif()

function(ps2x_stage_ffmpeg_runtime_dlls target_name)
    if(NOT WIN32)
        return()
    endif()

    if(NOT PS2X_FFMPEG_BIN_DIR OR NOT PS2X_COPY_FFMPEG_DLLS_SCRIPT)
        return()
    endif()

    if(TARGET ffmpeg_external)
        add_dependencies(${target_name} ffmpeg_external)
    endif()

    add_custom_command(
        TARGET ${target_name} POST_BUILD
        COMMAND "${CMAKE_COMMAND}"
        "-Dsource_dir=${PS2X_FFMPEG_BIN_DIR}"
        "-Ddest_dir=$<TARGET_FILE_DIR:${target_name}>"
        -P "${PS2X_COPY_FFMPEG_DLLS_SCRIPT}"
        VERBATIM
    )
endfunction()

add_library(ps2_runtime STATIC
    src/lib/game_overrides.cpp
    src/lib/gs/ps2_gif_arbiter.cpp
    src/lib/ps2_audio.cpp
    src/lib/ps2_audio_vag.cpp
    src/lib/gs/ps2_gs_memory.cpp
    src/lib/gs/gs_frontend.cpp
    src/lib/gs/gs_cpu_backend.cpp
    src/lib/ps2_iop_host.cpp
    src/lib/ps2_memory.cpp
    src/lib/transfer_buffers.cpp
    src/lib/ps2_ipu.cpp
    src/lib/ps2_pad.cpp
    src/lib/ps2_rom_device.cpp
    src/lib/ps2_vfs.cpp
    src/lib/ps2_runtime.cpp
    src/lib/ps2_save_state.cpp
    src/lib/host_audio.cpp
    src/lib/host_settings.cpp
    src/lib/car_lights.cpp
    src/lib/disc_patches.cpp
    src/lib/disc_read_ahead.cpp
    src/lib/settings_menu.cpp
    src/lib/video_skip.cpp
    src/lib/post_filter.cpp
    src/lib/shared_frames.cpp
    src/lib/gs_worker.cpp
    src/lib/host_sleep.cpp
    src/lib/ps2_vif1_interpreter.cpp
    src/lib/vu/ps2_vu1_core.cpp
    src/lib/vu/ps2_vu1_upper.cpp
    src/lib/vu/ps2_vu1_lower.cpp
    src/lib/vu/ps2_vu1_pipeline.cpp
    src/lib/vu/ps2_vu1_capture.cpp
    src/lib/vu/ps2_vu1_cache.cpp
    src/lib/vu/ps2_vu1_program.cpp
    src/lib/vu/ps2_vu1_native.cpp
    src/lib/games_database.cpp
)

if(PS2X_GS_BACKEND STREQUAL "PARALLEL_GS")
    message(STATUS "Using the paraLLEl-GS backend for PS2 graphics")
    include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ParallelGS.cmake")
endif()

# Keep the VU execution loop and its arithmetic helpers visible to the optimizer.
set_target_properties(ps2_runtime PROPERTIES UNITY_BUILD ON UNITY_BUILD_MODE GROUP)
set_source_files_properties(
    src/lib/vu/ps2_vu1_core.cpp
    src/lib/vu/ps2_vu1_upper.cpp
    src/lib/vu/ps2_vu1_lower.cpp
    PROPERTIES UNITY_GROUP vu1)

# Native VU1 programs (runtime/ps2_vu1_native.h) on x86-64: GCC and Clang compile only the
# translations for SSE4.1 and FMA3 (below), so the rest of the runtime keeps its floating point.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
    set(PS2X_VU_NATIVE_X86_64 ON)
    target_compile_definitions(ps2_runtime PRIVATE PS2X_VU_NATIVE_X86_64=1)
endif()

set(PS2X_VU_AOT_IMAGES "" CACHE STRING "Local VU code images or block profiles to compile; never distributed")
set(PS2X_VU_AOT_PAIR_IMAGES "" CACHE STRING "Additional local VU inputs to compile as reusable individual pairs")
set(PS2X_VU_AOT_LOOP_IMAGES "" CACHE STRING "Local VU code images to scan for bounded conditional loops")
option(PS2X_VU_AOT_DEBUG_INFO "Include expanded VU AOT debug scopes (slow to compile)" OFF)
set(PS2X_VU_AOT_SHARDS "32" CACHE STRING "Number of independent VU AOT compilation units")
set(PS2X_VU_AOT_COMPILE_JOBS "2" CACHE STRING "Maximum concurrent runtime compilations with VU AOT (Ninja)")
if(PS2X_VU_AOT_IMAGES OR PS2X_VU_AOT_PAIR_IMAGES OR PS2X_VU_AOT_LOOP_IMAGES)
    find_package(Python3 COMPONENTS Interpreter REQUIRED)
    if(NOT PS2X_VU_AOT_SHARDS MATCHES "^[1-9][0-9]*$")
        message(FATAL_ERROR "PS2X_VU_AOT_SHARDS must be a positive integer")
    endif()
    if(NOT PS2X_VU_AOT_COMPILE_JOBS MATCHES "^[1-9][0-9]*$")
        message(FATAL_ERROR "PS2X_VU_AOT_COMPILE_JOBS must be a positive integer")
    endif()
    # Static data-flow specializations otherwise exhaust memory in parallel builds.
    if(CMAKE_GENERATOR MATCHES "Ninja")
        set_property(GLOBAL APPEND PROPERTY JOB_POOLS ps2_vu_aot_compile=${PS2X_VU_AOT_COMPILE_JOBS})
        set_property(TARGET ps2_runtime PROPERTY JOB_POOL_COMPILE ps2_vu_aot_compile)
    endif()
    set(PS2X_VU_AOT_INCLUDE "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_aot_blocks.inc")
    set(PS2X_VU_AOT_EXTERN_INCLUDE "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_aot_blocks_extern.inc")
    set(PS2X_VU_AOT_SOURCES)
    math(EXPR PS2X_VU_AOT_LAST_SHARD "${PS2X_VU_AOT_SHARDS} - 1")
    foreach(SHARD RANGE ${PS2X_VU_AOT_LAST_SHARD})
        list(APPEND PS2X_VU_AOT_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_aot_blocks_${SHARD}.cpp")
    endforeach()
    add_custom_command(
        OUTPUT "${PS2X_VU_AOT_INCLUDE}" "${PS2X_VU_AOT_EXTERN_INCLUDE}" ${PS2X_VU_AOT_SOURCES}
        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../tools/compile_vu_blocks.py"
                --output "${PS2X_VU_AOT_INCLUDE}" --shards ${PS2X_VU_AOT_SHARDS}
                --pair-images ${PS2X_VU_AOT_PAIR_IMAGES}
                --loop-images ${PS2X_VU_AOT_LOOP_IMAGES}
                -- ${PS2X_VU_AOT_IMAGES}
        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../tools/compile_vu_blocks.py"
                ${PS2X_VU_AOT_IMAGES} ${PS2X_VU_AOT_PAIR_IMAGES} ${PS2X_VU_AOT_LOOP_IMAGES}
        VERBATIM)
    add_custom_target(ps2_vu_aot DEPENDS "${PS2X_VU_AOT_INCLUDE}" "${PS2X_VU_AOT_EXTERN_INCLUDE}" ${PS2X_VU_AOT_SOURCES})
    add_dependencies(ps2_runtime ps2_vu_aot)
    target_sources(ps2_runtime PRIVATE ${PS2X_VU_AOT_SOURCES})
    target_compile_definitions(ps2_runtime PRIVATE
        PS2X_VU_AOT_INCLUDE="${PS2X_VU_AOT_INCLUDE}"
        PS2X_VU_AOT_EXTERN_INCLUDE="${PS2X_VU_AOT_EXTERN_INCLUDE}")
    set_source_files_properties(${PS2X_VU_AOT_SOURCES} PROPERTIES
        SKIP_UNITY_BUILD_INCLUSION ON
        INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/vu")
    # Thousands of specialized inline scopes otherwise dominate compile time.
    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU" AND NOT PS2X_VU_AOT_DEBUG_INFO)
        set_source_files_properties(
            "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/ps2_runtime.dir/Unity/unity_vu1_cxx.cxx"
            ${PS2X_VU_AOT_SOURCES}
            PROPERTIES COMPILE_OPTIONS "-g0")
    endif()
endif()

# Whole VU1 routines, compiled from PS2_VU_PROGRAM_PROFILE recordings.
set(PS2X_VU_PROGRAM_PROFILES "" CACHE STRING "Local VU1 program profile directories to compile; never distributed")
set(PS2X_VU_PROGRAM_SHARDS "8" CACHE STRING "Number of compilation units for compiled VU1 routines")
set(PS2X_VU_PROGRAM_MAX_PAIRS "256" CACHE STRING
    "Pairs of a compiled VU1 routine built as one function; longer routines are split, as optimizers slow down sharply on long functions")
if(PS2X_VU_PROGRAM_PROFILES)
    find_package(Python3 COMPONENTS Interpreter REQUIRED)
    if(NOT PS2X_VU_PROGRAM_SHARDS MATCHES "^[1-9][0-9]*$")
        message(FATAL_ERROR "PS2X_VU_PROGRAM_SHARDS must be a positive integer")
    endif()
    if(NOT PS2X_VU_PROGRAM_MAX_PAIRS MATCHES "^[0-9]+$" OR PS2X_VU_PROGRAM_MAX_PAIRS LESS 128)
        message(FATAL_ERROR "PS2X_VU_PROGRAM_MAX_PAIRS must be at least 128, the longest block")
    endif()
    set(PS2X_VU_PROGRAM_INCLUDE "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_programs.inc")
    set(PS2X_VU_PROGRAM_EXTERN_INCLUDE "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_programs_extern.inc")
    set(PS2X_VU_PROGRAM_SOURCES)
    math(EXPR PS2X_VU_PROGRAM_LAST_SHARD "${PS2X_VU_PROGRAM_SHARDS} - 1")
    foreach(SHARD RANGE ${PS2X_VU_PROGRAM_LAST_SHARD})
        list(APPEND PS2X_VU_PROGRAM_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_programs_${SHARD}.cpp")
    endforeach()
    set(PS2X_VU_PROGRAM_LISTS)
    foreach(PROFILE ${PS2X_VU_PROGRAM_PROFILES})
        list(APPEND PS2X_VU_PROGRAM_LISTS "${PROFILE}/entries.txt")
    endforeach()
    # Native VU1 programs (runtime/ps2_vu1_native.h), translated from the same code images:
    # the ones the project's list names.
    set(PS2X_VU_NATIVE_LIST "${CMAKE_CURRENT_SOURCE_DIR}/../data/vu_native_programs.txt" CACHE FILEPATH
        "VU1 programs to translate to C++ (tools/translate_vu_programs.py); empty: none")
    # No routine for the starts those programs run: the MSVC build requires AVX2 (ReleaseMode.cmake),
    # so the translations always run there. Elsewhere a CPU without FMA3 runs the routines.
    set(PS2X_VU_PROGRAM_NATIVE_ARGS)
    if(MSVC AND PS2X_VU_NATIVE_X86_64 AND PS2X_VU_NATIVE_LIST)
        set(PS2X_VU_PROGRAM_NATIVE_ARGS --native-programs "${PS2X_VU_NATIVE_LIST}")
    endif()
    add_custom_command(
        OUTPUT "${PS2X_VU_PROGRAM_INCLUDE}" "${PS2X_VU_PROGRAM_EXTERN_INCLUDE}" ${PS2X_VU_PROGRAM_SOURCES}
        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../tools/compile_vu_programs.py"
                --output "${PS2X_VU_PROGRAM_INCLUDE}" --shards ${PS2X_VU_PROGRAM_SHARDS}
                --max-routine-pairs ${PS2X_VU_PROGRAM_MAX_PAIRS} ${PS2X_VU_PROGRAM_NATIVE_ARGS}
                ${PS2X_VU_PROGRAM_PROFILES}
        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../tools/compile_vu_programs.py" ${PS2X_VU_PROGRAM_LISTS}
                ${PS2X_VU_NATIVE_LIST}
        VERBATIM)
    if(PS2X_VU_NATIVE_LIST)
        # The table, then the translations in PS2X_VU_NATIVE_SHARDS files compiled in parallel. One
        # file's memory grows with the programs in it: 16 files keep the longest programs apart.
        set(PS2X_VU_NATIVE_SHARDS "16" CACHE STRING "Number of compilation units for the translated VU programs")
        if(NOT PS2X_VU_NATIVE_SHARDS MATCHES "^[1-9][0-9]*$")
            message(FATAL_ERROR "PS2X_VU_NATIVE_SHARDS must be a positive integer")
        endif()
        math(EXPR PS2X_VU_NATIVE_LAST_SHARD "${PS2X_VU_NATIVE_SHARDS} - 1")
        set(PS2X_VU_NATIVE_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_native_programs.cpp")
        set(PS2X_VU_NATIVE_SOURCES "${PS2X_VU_NATIVE_SOURCE}")
        foreach(SHARD RANGE ${PS2X_VU_NATIVE_LAST_SHARD})
            list(APPEND PS2X_VU_NATIVE_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_native_programs_${SHARD}.cpp")
        endforeach()
        add_custom_command(
            OUTPUT ${PS2X_VU_NATIVE_SOURCES}
            COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../tools/translate_vu_programs.py"
                    --programs "${PS2X_VU_NATIVE_LIST}" --output "${PS2X_VU_NATIVE_SOURCE}"
                    --shards ${PS2X_VU_NATIVE_SHARDS} ${PS2X_VU_PROGRAM_PROFILES}
            DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../tools/translate_vu_programs.py" "${PS2X_VU_NATIVE_LIST}"
                    ${PS2X_VU_PROGRAM_LISTS}
            VERBATIM)
        set_source_files_properties(src/lib/vu/ps2_vu1_native.cpp PROPERTIES COMPILE_DEFINITIONS PS2X_VU_NATIVE_PROGRAMS=1)
        # Exact VU arithmetic: no fast-math rewrites (x + 0 is not x when x is a denormal the
        # VU reads as zero) and the rounding mode ps2_vu_native::run sets. MSVC leaves a function
        # past its size limit unoptimized (C4883), one stack slot per value: GtIm3d's took 3 MiB.
        if(MSVC)
            set(PS2X_VU_NATIVE_FP_OPTIONS /fp:strict /d2OptimizeHugeFunctions)
        else()
            set(PS2X_VU_NATIVE_FP_OPTIONS -fno-fast-math -frounding-math -ffp-contract=off)
            if(PS2X_VU_NATIVE_X86_64)
                list(APPEND PS2X_VU_NATIVE_FP_OPTIONS -msse4.1 -mfma)
            endif()
        endif()
        target_sources(ps2_runtime PRIVATE ${PS2X_VU_NATIVE_SOURCES})
        set_source_files_properties(${PS2X_VU_NATIVE_SOURCES} PROPERTIES
            SKIP_UNITY_BUILD_INCLUSION ON COMPILE_OPTIONS "${PS2X_VU_NATIVE_FP_OPTIONS}")
        # MSVC takes up to 7.6 GB to compile one of these files; eight at once ran out of memory
        # (GitHub issue 11). At most one per 8 GB of memory, 4 GB left to the system.
        cmake_host_system_information(RESULT PS2X_MEMORY_MIB QUERY TOTAL_PHYSICAL_MEMORY)
        math(EXPR PS2X_VU_NATIVE_JOBS_FOR_MEMORY "(${PS2X_MEMORY_MIB} - 4096) / 8192")
        if(PS2X_VU_NATIVE_JOBS_FOR_MEMORY LESS 1)
            set(PS2X_VU_NATIVE_JOBS_FOR_MEMORY 1)
        endif()
        set(PS2X_VU_NATIVE_COMPILE_JOBS "${PS2X_VU_NATIVE_JOBS_FOR_MEMORY}" CACHE STRING
            "Translated VU programs compiled at once (default: one per 8 GB of memory)")
        if(NOT PS2X_VU_NATIVE_COMPILE_JOBS MATCHES "^[1-9][0-9]*$")
            message(FATAL_ERROR "PS2X_VU_NATIVE_COMPILE_JOBS must be a positive integer")
        endif()
        if(CMAKE_GENERATOR MATCHES "Ninja")
            # A source file's pool needs CMake 4.2; older versions ignore it.
            set_property(GLOBAL APPEND PROPERTY JOB_POOLS ps2_vu_native_compile=${PS2X_VU_NATIVE_COMPILE_JOBS})
            set_property(SOURCE ${PS2X_VU_NATIVE_SOURCES} PROPERTY JOB_POOL_COMPILE ps2_vu_native_compile)
        elseif(CMAKE_GENERATOR MATCHES "Visual Studio")
            # MSBuild compiles files whose settings differ in separate batches, one after another.
            foreach(SHARD RANGE ${PS2X_VU_NATIVE_LAST_SHARD})
                math(EXPR PS2X_VU_NATIVE_GROUP "${SHARD} / ${PS2X_VU_NATIVE_COMPILE_JOBS}")
                set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated/vu_native_programs_${SHARD}.cpp" APPEND PROPERTY
                    COMPILE_DEFINITIONS PS2X_VU_NATIVE_GROUP=${PS2X_VU_NATIVE_GROUP})
            endforeach()
        endif()
    endif()
    add_custom_target(ps2_vu_programs DEPENDS "${PS2X_VU_PROGRAM_INCLUDE}"
                      "${PS2X_VU_PROGRAM_EXTERN_INCLUDE}" ${PS2X_VU_PROGRAM_SOURCES} ${PS2X_VU_NATIVE_SOURCES})
    add_dependencies(ps2_runtime ps2_vu_programs)
    # Visual Studio's clang-cl (component "C++ Clang Compiler for Windows") makes code of the
    # routines that runs about 5% faster than MSVC's: GS worker time in a race, same GIF output.
    # Single precision without contraction to FMA, as MSVC compiles them. OFF: MSVC compiles them;
    # another clang-cl must be recent enough for this STL.
    if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_VERSION VERSION_LESS 3.21)
        get_filename_component(PS2X_MSVC_TOOLS_DIR "${CMAKE_CXX_COMPILER}/../../../../../.." ABSOLUTE)
        find_program(PS2X_VU_PROGRAM_CLANG_CL clang-cl PATHS "${PS2X_MSVC_TOOLS_DIR}/Llvm/x64/bin" NO_DEFAULT_PATH
            DOC "clang-cl for the compiled VU1 routines; OFF: MSVC compiles them")
    endif()
    if(MSVC AND PS2X_VU_PROGRAM_CLANG_CL)
        message(STATUS "Compiled VU1 routines: ${PS2X_VU_PROGRAM_CLANG_CL}")
        cmake_policy(PUSH)
        if(POLICY CMP0147)
            cmake_policy(SET CMP0147 NEW) # MSBuild compiles them in parallel
        endif()
        set(PS2X_VU_DEFINITIONS "$<REMOVE_DUPLICATES:$<TARGET_PROPERTY:ps2_runtime,COMPILE_DEFINITIONS>>")
        set(PS2X_VU_INCLUDES "$<REMOVE_DUPLICATES:$<TARGET_PROPERTY:ps2_runtime,INCLUDE_DIRECTORIES>>")
        foreach(SOURCE ${PS2X_VU_PROGRAM_SOURCES})
            get_filename_component(NAME "${SOURCE}" NAME_WE)
            set(OBJECT "${CMAKE_CURRENT_BINARY_DIR}/generated/clang-cl/$<CONFIG>/${NAME}.obj")
            add_custom_command(
                OUTPUT "${OBJECT}"
                COMMAND ${PS2X_VU_PROGRAM_CLANG_CL} /nologo /c /TP /std:c++20 /EHsc /W0 /Z7 /arch:AVX2
                        /fp:precise /clang:-ffp-contract=off /GS- /Gy /Gw /DWIN32 /D_WINDOWS
                        "$<IF:$<CONFIG:Debug>,/Od;/MDd;/D_DEBUG,/O2;/MD;/DNDEBUG>"
                        "$<$<BOOL:${PS2X_VU_DEFINITIONS}>:/D$<JOIN:${PS2X_VU_DEFINITIONS},;/D>>"
                        "/I${CMAKE_CURRENT_SOURCE_DIR}/src/lib/vu"
                        "$<$<BOOL:${PS2X_VU_INCLUDES}>:/I$<JOIN:${PS2X_VU_INCLUDES},;/I>>"
                        "/clang:-MD" "/clang:-MF${OBJECT}.d" "/clang:-MT${OBJECT}" "/Fo${OBJECT}" "${SOURCE}"
                DEPENDS "${SOURCE}"
                DEPFILE "${OBJECT}.d"
                COMMENT "Compiling ${NAME}.cpp with clang-cl"
                COMMAND_EXPAND_LISTS VERBATIM)
            # An .obj source goes into the library as it is.
            target_sources(ps2_runtime PRIVATE "${OBJECT}")
        endforeach()
        cmake_policy(POP)
    else()
        target_sources(ps2_runtime PRIVATE ${PS2X_VU_PROGRAM_SOURCES})
    endif()
    set_source_files_properties(src/lib/vu/ps2_vu1_program.cpp PROPERTIES COMPILE_DEFINITIONS
        "PS2X_VU_PROGRAM_INCLUDE=\"${PS2X_VU_PROGRAM_INCLUDE}\";PS2X_VU_PROGRAM_EXTERN_INCLUDE=\"${PS2X_VU_PROGRAM_EXTERN_INCLUDE}\"")
    set_source_files_properties(${PS2X_VU_PROGRAM_SOURCES} PROPERTIES
        SKIP_UNITY_BUILD_INCLUSION ON
        INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/vu")
    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
        set_source_files_properties(${PS2X_VU_PROGRAM_SOURCES} PROPERTIES COMPILE_OPTIONS "-g0")
    endif()
endif()

if(PS2X_ENABLE_RUNTIME_LOGS)
    target_compile_definitions(ps2_runtime PUBLIC
        PS2_RUNTIME_LOGS=1
    )
endif()

if(PS2X_ENABLE_AGRESSIVE_LOGS)
    target_compile_definitions(ps2_runtime PUBLIC
        AGRESSIVE_LOGS=1
        PS2_FUNCTION_LOG_TRACKER=1
    )
endif()

if(PS2X_ENABLE_IOP_RPC_TRACE)
    target_compile_definitions(ps2_runtime PUBLIC
        PS2X_ENABLE_IOP_RPC_TRACE=1
    )
else()
    target_compile_definitions(ps2_runtime PUBLIC
        PS2X_ENABLE_IOP_RPC_TRACE=0
    )
endif()

if(PS2X_STRICT_RETURN_DIAGNOSTICS)
    target_compile_definitions(ps2_runtime PUBLIC
        PS2X_STRICT_RETURN_DIAGNOSTICS=1
    )
endif()

target_compile_definitions(ps2_runtime PRIVATE
    PS2X_HAS_FFMPEG=$<BOOL:${PS2X_ENABLE_FFMPEG}>
)

file(GLOB_RECURSE KERNEL_SRC_FILES CONFIGURE_DEPENDS
    "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel/*.cpp"
)

target_sources(ps2_runtime PRIVATE
    ${KERNEL_SRC_FILES}
)

file(GLOB RUNNER_SRC_FILES CONFIGURE_DEPENDS
    "${CMAKE_CURRENT_SOURCE_DIR}/src/runner/*.cpp"
)

set(RUNNER_MAIN_CPP "${CMAKE_CURRENT_SOURCE_DIR}/src/runner/main.cpp")
set(ROOT_MAIN_CPP "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")

if(EXISTS "${RUNNER_MAIN_CPP}")
    list(APPEND RUNNER_SRC_FILES "${RUNNER_MAIN_CPP}")
elseif(EXISTS "${ROOT_MAIN_CPP}")
    list(APPEND RUNNER_SRC_FILES "${ROOT_MAIN_CPP}")
endif()

if(PS2X_IS_ANDROID)
    add_library(ps2EntryRunner SHARED
        ${RUNNER_SRC_FILES}
    )

    target_link_options(ps2EntryRunner PRIVATE "-Wl,--undefined=ANativeActivity_onCreate")
else()
    add_executable(ps2EntryRunner
        ${RUNNER_SRC_FILES}
    )
endif()

if(PS2X_ENABLE_RUNNER_UNITY_BUILD)
    set_target_properties(ps2EntryRunner PROPERTIES
        UNITY_BUILD ON
        UNITY_BUILD_BATCH_SIZE "${PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE}"
    )
endif()

if(PS2X_ENABLE_RUNNER_PCH)
    set(PS2X_RUNNER_PCH_HEADERS
        "${CMAKE_CURRENT_SOURCE_DIR}/include/ps2_runtime_macros.h"
        "${CMAKE_CURRENT_SOURCE_DIR}/include/ps2_runtime.h"
    )

    foreach(PS2X_RUNNER_GENERATED_HEADER IN ITEMS
        "${CMAKE_CURRENT_SOURCE_DIR}/include/ps2_recompiled_functions.h"
        "${CMAKE_CURRENT_SOURCE_DIR}/include/ps2_recompiled_stubs.h")
        if(EXISTS "${PS2X_RUNNER_GENERATED_HEADER}")
            list(APPEND PS2X_RUNNER_PCH_HEADERS "${PS2X_RUNNER_GENERATED_HEADER}")
        endif()
    endforeach()

    list(APPEND PS2X_RUNNER_PCH_HEADERS
        "${CMAKE_CURRENT_SOURCE_DIR}/include/ps2_syscalls.h"
        "${CMAKE_CURRENT_SOURCE_DIR}/include/ps2_stubs.h"
    )

    target_precompile_headers(ps2EntryRunner PRIVATE ${PS2X_RUNNER_PCH_HEADERS})
endif()

if(PS2X_DEFAULT_BOOT_ELF_DEFINE)
    target_compile_definitions(ps2EntryRunner PRIVATE
        PS2X_DEFAULT_BOOT_ELF="${PS2X_DEFAULT_BOOT_ELF_DEFINE}"
    )
endif()

if(MSVC)
    target_compile_options(ps2EntryRunner PRIVATE /FS)
endif()

target_include_directories(ps2_runtime PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/include
    ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel
)

target_link_libraries(ps2_runtime PUBLIC ps2_host_backend ffmpeg ps2_iop)
target_link_libraries(ps2EntryRunner
    PRIVATE
    ps2_runtime
)

if(PS2X_ENABLE_DEBUG_UI AND NOT PS2X_IS_VITA AND NOT PS2X_IS_ANDROID)
    target_sources(ps2EntryRunner PRIVATE
        src/lib/ps2_debug_panel.cpp
    )
    target_link_libraries(ps2EntryRunner PRIVATE rlImGui imgui)
    target_compile_definitions(ps2EntryRunner PRIVATE PS2X_ENABLE_DEBUG_UI=1)
endif()

ps2x_stage_ffmpeg_runtime_dlls(ps2EntryRunner)

if(PS2X_IS_VITA)
    set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1") 
    set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d ATTRIBUTE2=12")

    if(PS2X_VITA_CREATE_PACKAGE)
        set(PS2X_VITA_VPK_FILES "")

        foreach(PS2X_VITA_MODULE IN ITEMS
            libgpu_es4_ext.suprx
            libIMGEGL.suprx
            libGLESv2.suprx
            libpvrPSP2_WSEGL.suprx
            libGL.suprx)
            set(PS2X_VITA_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/vita/module/${PS2X_VITA_MODULE}")

            if(EXISTS "${PS2X_VITA_MODULE_PATH}")
                list(APPEND PS2X_VITA_VPK_FILES FILE "${PS2X_VITA_MODULE_PATH}" "module/${PS2X_VITA_MODULE}")
            else()
                message(WARNING
                    "Missing ${PS2X_VITA_MODULE_PATH}; the vpk will rely on ur0:data/external/ for it")
            endif()
        endforeach()

        vita_create_self(eboot.bin ps2EntryRunner UNSAFE)
        vita_create_vpk(ps2EntryRunner.vpk ${PS2X_VITA_TITLEID} eboot.bin
            VERSION ${PS2X_VITA_VERSION}
            NAME ${PS2X_VITA_APP_NAME}
            ${PS2X_VITA_VPK_FILES}
        )
    endif()
endif()

if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
    EnableFastReleaseMode(ps2_runtime)
    EnableFastReleaseMode(ps2EntryRunner)

    if(WIN32 AND NOT PS2X_SHOW_WINDOWS_CONSOLE)
        if(MSVC)
            target_link_options(ps2EntryRunner PRIVATE
                $<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/SUBSYSTEM:WINDOWS>
                $<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/ENTRY:mainCRTStartup>
            )
        elseif(MINGW)
            target_link_options(ps2EntryRunner PRIVATE
                $<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:-mwindows>
            )
        endif()
    endif()
endif()

# Work around WinAPI vs raylib symbol clash for CloseWindow on x64
if(MSVC)
    target_link_options(ps2EntryRunner PRIVATE "/FORCE:MULTIPLE")
endif()

# Vita: Work around for GCC 10's LTO plugin
if(PS2X_IS_VITA)
    target_link_options(ps2EntryRunner PRIVATE "-Wl,--allow-multiple-definition")
    target_sources(ps2EntryRunner PRIVATE src/lib/ps2_vita_runtime.cpp)
    set_source_files_properties(src/lib/ps2_vita_runtime.cpp PROPERTIES
        SKIP_UNITY_BUILD_INCLUSION TRUE
        COMPILE_OPTIONS "-fno-lto"
    )
endif()

if(PS2X_IS_ANDROID)
    target_sources(ps2EntryRunner PRIVATE src/lib/ps2_android_runtime.cpp)
endif()

install(TARGETS ps2_runtime ps2EntryRunner
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)

if(WIN32 AND PS2X_ENABLE_FFMPEG)
    install(DIRECTORY "${FFMPEG_BIN_DIR}/"
        DESTINATION bin
        FILES_MATCHING
        PATTERN "*.dll"
    )
endif()

install(DIRECTORY include/
    DESTINATION include
)
